Inital import.

Initial fork from f2d678e6e89b6508147086610e985d4e8416e867 (1.0.2 beta).

(This change contains substantial changes from the original and
effectively starts a new history.)
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
new file mode 100644
index 0000000..8fb78f1
--- /dev/null
+++ b/crypto/CMakeLists.txt
@@ -0,0 +1,159 @@
+include_directories(. ../include)
+
+if (UNIX)
+	set(PERLASM_STYLE elf)
+	set(ASM_EXT S)
+	enable_language(ASM)
+else()
+	if (CMAKE_CL_64)
+		message("Using masm")
+		set(PERLASM_STYLE masm)
+	else()
+		message("Using win32n")
+		set(PERLASM_STYLE win32n)
+	endif()
+	set(ASM_EXT asm)
+	enable_language(ASM_MASM)
+endif()
+
+function(perlasm dest src)
+	add_custom_command(
+		OUTPUT ${dest}
+		COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/${src} ${PERLASM_STYLE} ${ARGN} > ${dest}
+		DEPENDS
+		${src}
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86_64-xlate.pl
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86gas.pl
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86asm.pl
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86asm.pl
+		WORKING_DIRECTORY .
+	)
+endfunction()
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		CRYPTO_ARCH_SOURCES
+
+		cpu-x86_64-asm.${ASM_EXT}
+		cpu-intel.c
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		CRYPTO_ARCH_SOURCES
+
+		cpu-x86-asm.${ASM_EXT}
+		cpu-intel.c
+	)
+endif()
+
+if (${ARCH} STREQUAL "arm")
+	set(
+		CRYPTO_ARCH_SOURCES
+
+		cpu-x86-asm.${ASM_EXT}
+		cpu-arm.c
+	)
+endif()
+
+# Level 0.1 - depends on nothing outside this set.
+add_subdirectory(stack)
+add_subdirectory(lhash)
+add_subdirectory(err)
+add_subdirectory(buf)
+add_subdirectory(comp)
+add_subdirectory(base64)
+add_subdirectory(bytestring)
+
+# Level 0.2 - depends on nothing but itself
+add_subdirectory(sha)
+add_subdirectory(md5)
+add_subdirectory(modes)
+add_subdirectory(aes)
+add_subdirectory(des)
+add_subdirectory(rc4)
+add_subdirectory(conf)
+
+# Level 1, depends only on 0.*
+add_subdirectory(digest)
+add_subdirectory(cipher)
+add_subdirectory(rand)
+add_subdirectory(bio)
+add_subdirectory(bn)
+add_subdirectory(obj)
+add_subdirectory(asn1)
+
+# Level 2
+add_subdirectory(engine)
+add_subdirectory(dh)
+add_subdirectory(dsa)
+add_subdirectory(rsa)
+add_subdirectory(ec)
+add_subdirectory(ecdh)
+add_subdirectory(ecdsa)
+add_subdirectory(hmac)
+
+# Level 3
+add_subdirectory(evp)
+add_subdirectory(pem)
+add_subdirectory(x509)
+add_subdirectory(x509v3)
+
+# Level 4
+add_subdirectory(pkcs8)
+
+add_library(
+	crypto
+	STATIC
+
+	crypto_error.c
+	mem.c
+	mem_clear.c
+	thread.c
+	ex_data.c
+	ex_data_impl.c
+	time_support.c
+	directory_posix.c
+	directory_win.c
+
+	${CRYPTO_ARCH_SOURCES}
+
+	$<TARGET_OBJECTS:stack>
+	$<TARGET_OBJECTS:lhash>
+	$<TARGET_OBJECTS:err>
+	$<TARGET_OBJECTS:comp>
+	$<TARGET_OBJECTS:base64>
+	$<TARGET_OBJECTS:bytestring>
+	$<TARGET_OBJECTS:sha>
+	$<TARGET_OBJECTS:md5>
+	$<TARGET_OBJECTS:digest>
+	$<TARGET_OBJECTS:cipher>
+	$<TARGET_OBJECTS:modes>
+	$<TARGET_OBJECTS:aes>
+	$<TARGET_OBJECTS:des>
+	$<TARGET_OBJECTS:rc4>
+	$<TARGET_OBJECTS:conf>
+	$<TARGET_OBJECTS:buf>
+	$<TARGET_OBJECTS:bn>
+	$<TARGET_OBJECTS:bio>
+	$<TARGET_OBJECTS:rand>
+	$<TARGET_OBJECTS:obj>
+	$<TARGET_OBJECTS:asn1>
+	$<TARGET_OBJECTS:engine>
+	$<TARGET_OBJECTS:dh>
+	$<TARGET_OBJECTS:dsa>
+	$<TARGET_OBJECTS:rsa>
+	$<TARGET_OBJECTS:ec>
+	$<TARGET_OBJECTS:ecdh>
+	$<TARGET_OBJECTS:ecdsa>
+	$<TARGET_OBJECTS:hmac>
+	$<TARGET_OBJECTS:evp>
+	$<TARGET_OBJECTS:pem>
+	$<TARGET_OBJECTS:x509>
+	$<TARGET_OBJECTS:x509v3>
+	$<TARGET_OBJECTS:pkcs8>
+)
+
+perlasm(cpu-x86_64-asm.${ASM_EXT} cpu-x86_64-asm.pl)
+perlasm(cpu-x86-asm.${ASM_EXT} cpu-x86-asm.pl)
diff --git a/crypto/aes/CMakeLists.txt b/crypto/aes/CMakeLists.txt
new file mode 100644
index 0000000..6545809
--- /dev/null
+++ b/crypto/aes/CMakeLists.txt
@@ -0,0 +1,52 @@
+include_directories(. .. ../../include)
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		AES_ARCH_SOURCES
+
+		aes-x86_64.${ASM_EXT}
+		aesni-x86_64.${ASM_EXT}
+		bsaes-x86_64.${ASM_EXT}
+		vpaes-x86_64.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		AES_ARCH_SOURCES
+
+		aes-586.${ASM_EXT}
+		vpaes-x86.${ASM_EXT}
+		aesni-x86.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "arm")
+	set(
+		AES_ARCH_SOURCES
+
+		aes-armv4.${ASM_EXT}
+		bsaes-armv7.${ASM_EXT}
+	)
+endif()
+
+add_library(
+	aes
+
+	OBJECT
+
+	aes.c
+	mode_wrappers.c
+
+	${AES_ARCH_SOURCES}
+)
+
+perlasm(aes-x86_64.${ASM_EXT} asm/aes-x86_64.pl)
+perlasm(aesni-x86_64.${ASM_EXT} asm/aesni-x86_64.pl)
+perlasm(bsaes-x86_64.${ASM_EXT} asm/bsaes-x86_64.pl)
+perlasm(vpaes-x86_64.${ASM_EXT} asm/vpaes-x86_64.pl)
+perlasm(aes-586.${ASM_EXT} asm/aes-586.pl)
+perlasm(vpaes-x86.${ASM_EXT} asm/vpaes-x86.pl)
+perlasm(aesni-x86.${ASM_EXT} asm/aesni-x86.pl)
+perlasm(aes-armv4.${ASM_EXT} asm/aes-armv4.pl)
+perlasm(bsaes-armv7.${ASM_EXT} asm/bsaes-armv7.pl)
diff --git a/crypto/aes/aes.c b/crypto/aes/aes.c
new file mode 100644
index 0000000..b085787
--- /dev/null
+++ b/crypto/aes/aes.c
@@ -0,0 +1,1077 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <openssl/aes.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+#if defined(OPENSSL_NO_ASM) || \
+    (!defined(OPENSSL_X86) && !defined(OPENSSL_X86_64) && !defined(OPENSSL_ARM))
+
+/* Te0[x] = S [x].[02, 01, 01, 03];
+ * Te1[x] = S [x].[03, 02, 01, 01];
+ * Te2[x] = S [x].[01, 03, 02, 01];
+ * Te3[x] = S [x].[01, 01, 03, 02];
+ *
+ * Td0[x] = Si[x].[0e, 09, 0d, 0b];
+ * Td1[x] = Si[x].[0b, 0e, 09, 0d];
+ * Td2[x] = Si[x].[0d, 0b, 0e, 09];
+ * Td3[x] = Si[x].[09, 0d, 0b, 0e];
+ * Td4[x] = Si[x].[01]; */
+
+static const uint32_t Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU,
+    0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U,
+    0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U,
+    0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU,
+    0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U,
+    0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU,
+    0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U,
+    0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U,
+    0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU,
+    0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU,
+    0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U,
+    0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U,
+    0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U,
+    0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U,
+    0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U,
+    0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU,
+    0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U,
+    0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U,
+    0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U,
+    0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U,
+    0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU,
+    0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U,
+    0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU,
+    0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U,
+    0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU,
+    0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU,
+    0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U,
+    0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U,
+    0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU,
+    0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU,
+    0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU,
+    0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U,
+    0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U,
+    0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U,
+    0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U,
+    0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U,
+    0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU,
+    0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U,
+    0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U,
+    0x2c16163aU, };
+
+static const uint32_t Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U,
+    0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U,
+    0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU,
+    0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU,
+    0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U,
+    0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U,
+    0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U,
+    0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U,
+    0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U,
+    0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U,
+    0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU,
+    0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U,
+    0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U,
+    0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU,
+    0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U,
+    0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U,
+    0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU,
+    0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U,
+    0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U,
+    0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU,
+    0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U,
+    0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U,
+    0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU,
+    0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U,
+    0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U,
+    0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U,
+    0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U,
+    0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU,
+    0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU,
+    0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U,
+    0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU,
+    0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU,
+    0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U,
+    0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U,
+    0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U,
+    0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U,
+    0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU,
+    0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U,
+    0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU,
+    0x3a2c1616U, };
+
+static const uint32_t Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U,
+    0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U,
+    0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU,
+    0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU,
+    0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U,
+    0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U,
+    0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U,
+    0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U,
+    0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U,
+    0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U,
+    0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU,
+    0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U,
+    0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U,
+    0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU,
+    0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U,
+    0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U,
+    0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU,
+    0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U,
+    0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U,
+    0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU,
+    0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U,
+    0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U,
+    0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU,
+    0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U,
+    0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U,
+    0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U,
+    0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U,
+    0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU,
+    0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU,
+    0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U,
+    0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU,
+    0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU,
+    0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U,
+    0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U,
+    0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U,
+    0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U,
+    0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU,
+    0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U,
+    0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU,
+    0x163a2c16U, };
+
+static const uint32_t Te3[256] = {
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU,
+    0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U,
+    0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU,
+    0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U,
+    0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U,
+    0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU,
+    0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U,
+    0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U,
+    0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU,
+    0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U,
+    0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU,
+    0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U,
+    0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U,
+    0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU,
+    0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U,
+    0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU,
+    0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U,
+    0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U,
+    0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U,
+    0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U,
+    0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U,
+    0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U,
+    0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U,
+    0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU,
+    0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U,
+    0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU,
+    0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U,
+    0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U,
+    0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U,
+    0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU,
+    0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U,
+    0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU,
+    0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U,
+    0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U,
+    0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U,
+    0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U,
+    0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U,
+    0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U,
+    0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU,
+    0x16163a2cU, };
+
+static const uint32_t Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU,
+    0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U,
+    0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U,
+    0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U,
+    0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U,
+    0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU,
+    0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U,
+    0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U,
+    0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U,
+    0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU,
+    0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U,
+    0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U,
+    0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U,
+    0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U,
+    0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U,
+    0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU,
+    0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U,
+    0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U,
+    0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU,
+    0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU,
+    0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U,
+    0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU,
+    0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U,
+    0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU,
+    0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU,
+    0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU,
+    0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU,
+    0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U,
+    0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU,
+    0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U,
+    0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU,
+    0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU,
+    0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U,
+    0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU,
+    0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U,
+    0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U,
+    0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U,
+    0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU,
+    0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U,
+    0xd0b85742U, };
+
+static const uint32_t Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU,
+    0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU,
+    0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U,
+    0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU,
+    0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U,
+    0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU,
+    0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U,
+    0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU,
+    0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU,
+    0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU,
+    0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU,
+    0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U,
+    0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU,
+    0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U,
+    0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU,
+    0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU,
+    0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U,
+    0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U,
+    0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU,
+    0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U,
+    0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU,
+    0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U,
+    0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U,
+    0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U,
+    0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U,
+    0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U,
+    0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U,
+    0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U,
+    0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U,
+    0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U,
+    0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U,
+    0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U,
+    0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU,
+    0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U,
+    0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U,
+    0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU,
+    0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U,
+    0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U,
+    0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU,
+    0x42d0b857U, };
+
+static const uint32_t Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU,
+    0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U,
+    0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U,
+    0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU,
+    0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U,
+    0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U,
+    0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U,
+    0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U,
+    0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU,
+    0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU,
+    0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U,
+    0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU,
+    0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U,
+    0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU,
+    0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U,
+    0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU,
+    0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU,
+    0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU,
+    0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U,
+    0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U,
+    0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U,
+    0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU,
+    0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U,
+    0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U,
+    0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U,
+    0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U,
+    0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U,
+    0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U,
+    0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU,
+    0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU,
+    0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U,
+    0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU,
+    0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU,
+    0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U,
+    0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U,
+    0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U,
+    0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU,
+    0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU,
+    0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU,
+    0x5742d0b8U, };
+
+static const uint32_t Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU,
+    0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU,
+    0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U,
+    0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U,
+    0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U,
+    0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U,
+    0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U,
+    0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU,
+    0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U,
+    0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU,
+    0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U,
+    0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U,
+    0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U,
+    0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U,
+    0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U,
+    0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU,
+    0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U,
+    0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U,
+    0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU,
+    0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU,
+    0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U,
+    0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U,
+    0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU,
+    0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U,
+    0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U,
+    0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U,
+    0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU,
+    0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU,
+    0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU,
+    0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU,
+    0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U,
+    0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U,
+    0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U,
+    0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU,
+    0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U,
+    0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U,
+    0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U,
+    0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U,
+    0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U,
+    0xb85742d0U, };
+
+static const uint8_t Td4[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U,
+    0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU,
+    0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, 0x54U,
+    0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU,
+    0x42U, 0xfaU, 0xc3U, 0x4eU, 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U,
+    0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, 0x72U, 0xf8U,
+    0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU,
+    0x65U, 0xb6U, 0x92U, 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, 0x90U, 0xd8U, 0xabU,
+    0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U,
+    0x45U, 0x06U, 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U,
+    0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, 0x3aU, 0x91U, 0x11U, 0x41U,
+    0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U,
+    0x73U, 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U,
+    0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU,
+    0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U,
+    0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U,
+    0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, 0x60U,
+    0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU,
+    0x93U, 0xc9U, 0x9cU, 0xefU, 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U,
+    0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, 0x17U, 0x2bU,
+    0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U,
+    0x21U, 0x0cU, 0x7dU, };
+
+static const uint8_t Te4[256] = {
+    0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U, 0x30U, 0x01U, 0x67U,
+    0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U, 0xcaU, 0x82U, 0xc9U, 0x7dU, 0xfaU, 0x59U,
+    0x47U, 0xf0U, 0xadU, 0xd4U, 0xa2U, 0xafU, 0x9cU, 0xa4U, 0x72U, 0xc0U, 0xb7U,
+    0xfdU, 0x93U, 0x26U, 0x36U, 0x3fU, 0xf7U, 0xccU, 0x34U, 0xa5U, 0xe5U, 0xf1U,
+    0x71U, 0xd8U, 0x31U, 0x15U, 0x04U, 0xc7U, 0x23U, 0xc3U, 0x18U, 0x96U, 0x05U,
+    0x9aU, 0x07U, 0x12U, 0x80U, 0xe2U, 0xebU, 0x27U, 0xb2U, 0x75U, 0x09U, 0x83U,
+    0x2cU, 0x1aU, 0x1bU, 0x6eU, 0x5aU, 0xa0U, 0x52U, 0x3bU, 0xd6U, 0xb3U, 0x29U,
+    0xe3U, 0x2fU, 0x84U, 0x53U, 0xd1U, 0x00U, 0xedU, 0x20U, 0xfcU, 0xb1U, 0x5bU,
+    0x6aU, 0xcbU, 0xbeU, 0x39U, 0x4aU, 0x4cU, 0x58U, 0xcfU, 0xd0U, 0xefU, 0xaaU,
+    0xfbU, 0x43U, 0x4dU, 0x33U, 0x85U, 0x45U, 0xf9U, 0x02U, 0x7fU, 0x50U, 0x3cU,
+    0x9fU, 0xa8U, 0x51U, 0xa3U, 0x40U, 0x8fU, 0x92U, 0x9dU, 0x38U, 0xf5U, 0xbcU,
+    0xb6U, 0xdaU, 0x21U, 0x10U, 0xffU, 0xf3U, 0xd2U, 0xcdU, 0x0cU, 0x13U, 0xecU,
+    0x5fU, 0x97U, 0x44U, 0x17U, 0xc4U, 0xa7U, 0x7eU, 0x3dU, 0x64U, 0x5dU, 0x19U,
+    0x73U, 0x60U, 0x81U, 0x4fU, 0xdcU, 0x22U, 0x2aU, 0x90U, 0x88U, 0x46U, 0xeeU,
+    0xb8U, 0x14U, 0xdeU, 0x5eU, 0x0bU, 0xdbU, 0xe0U, 0x32U, 0x3aU, 0x0aU, 0x49U,
+    0x06U, 0x24U, 0x5cU, 0xc2U, 0xd3U, 0xacU, 0x62U, 0x91U, 0x95U, 0xe4U, 0x79U,
+    0xe7U, 0xc8U, 0x37U, 0x6dU, 0x8dU, 0xd5U, 0x4eU, 0xa9U, 0x6cU, 0x56U, 0xf4U,
+    0xeaU, 0x65U, 0x7aU, 0xaeU, 0x08U, 0xbaU, 0x78U, 0x25U, 0x2eU, 0x1cU, 0xa6U,
+    0xb4U, 0xc6U, 0xe8U, 0xddU, 0x74U, 0x1fU, 0x4bU, 0xbdU, 0x8bU, 0x8aU, 0x70U,
+    0x3eU, 0xb5U, 0x66U, 0x48U, 0x03U, 0xf6U, 0x0eU, 0x61U, 0x35U, 0x57U, 0xb9U,
+    0x86U, 0xc1U, 0x1dU, 0x9eU, 0xe1U, 0xf8U, 0x98U, 0x11U, 0x69U, 0xd9U, 0x8eU,
+    0x94U, 0x9bU, 0x1eU, 0x87U, 0xe9U, 0xceU, 0x55U, 0x28U, 0xdfU, 0x8cU, 0xa1U,
+    0x89U, 0x0dU, 0xbfU, 0xe6U, 0x42U, 0x68U, 0x41U, 0x99U, 0x2dU, 0x0fU, 0xb0U,
+    0x54U, 0xbbU, 0x16U};
+
+static const uint32_t rcon[] = {
+    0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+    0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
+    /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+int AES_set_encrypt_key(const uint8_t *key, const unsigned bits, AES_KEY *aeskey) {
+  uint32_t *rk;
+  int i = 0;
+  uint32_t temp;
+
+  if (!key || !aeskey) {
+    return -1;
+  }
+
+  switch (bits) {
+    case 128:
+      aeskey->rounds = 10;
+      break;
+    case 192:
+      aeskey->rounds = 12;
+      break;
+    case 256:
+      aeskey->rounds = 14;
+      break;
+    default:
+      return -2;
+  }
+
+  rk = aeskey->rd_key;
+
+  rk[0] = GETU32(key);
+  rk[1] = GETU32(key + 4);
+  rk[2] = GETU32(key + 8);
+  rk[3] = GETU32(key + 12);
+  if (bits == 128) {
+    while (1) {
+      temp = rk[3];
+      rk[4] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+              (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+              (Te0[(temp) & 0xff] & 0x0000ff00) ^
+              (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+      rk[5] = rk[1] ^ rk[4];
+      rk[6] = rk[2] ^ rk[5];
+      rk[7] = rk[3] ^ rk[6];
+      if (++i == 10) {
+        return 0;
+      }
+      rk += 4;
+    }
+  }
+  rk[4] = GETU32(key + 16);
+  rk[5] = GETU32(key + 20);
+  if (bits == 192) {
+    while (1) {
+      temp = rk[5];
+      rk[6] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+              (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+              (Te0[(temp) & 0xff] & 0x0000ff00) ^
+              (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+      rk[7] = rk[1] ^ rk[6];
+      rk[8] = rk[2] ^ rk[7];
+      rk[9] = rk[3] ^ rk[8];
+      if (++i == 8) {
+        return 0;
+      }
+      rk[10] = rk[4] ^ rk[9];
+      rk[11] = rk[5] ^ rk[10];
+      rk += 6;
+    }
+  }
+  rk[6] = GETU32(key + 24);
+  rk[7] = GETU32(key + 28);
+  if (bits == 256) {
+    while (1) {
+      temp = rk[7];
+      rk[8] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+              (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+              (Te0[(temp) & 0xff] & 0x0000ff00) ^
+              (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+      rk[9] = rk[1] ^ rk[8];
+      rk[10] = rk[2] ^ rk[9];
+      rk[11] = rk[3] ^ rk[10];
+      if (++i == 7) {
+        return 0;
+      }
+      temp = rk[11];
+      rk[12] = rk[4] ^ (Te2[(temp >> 24)] & 0xff000000) ^
+               (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^
+               (Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^
+               (Te1[(temp) & 0xff] & 0x000000ff);
+      rk[13] = rk[5] ^ rk[12];
+      rk[14] = rk[6] ^ rk[13];
+      rk[15] = rk[7] ^ rk[14];
+
+      rk += 8;
+    }
+  }
+  return 0;
+}
+
+int AES_set_decrypt_key(const unsigned char *key, const unsigned bits,
+                        AES_KEY *aeskey) {
+  uint32_t *rk;
+  int i, j, status;
+  uint32_t temp;
+
+  /* first, start with an encryption schedule */
+  status = AES_set_encrypt_key(key, bits, aeskey);
+  if (status < 0) {
+    return status;
+  }
+
+  rk = aeskey->rd_key;
+
+  /* invert the order of the round keys: */
+  for (i = 0, j = 4 * aeskey->rounds; i < j; i += 4, j -= 4) {
+    temp = rk[i];
+    rk[i] = rk[j];
+    rk[j] = temp;
+    temp = rk[i + 1];
+    rk[i + 1] = rk[j + 1];
+    rk[j + 1] = temp;
+    temp = rk[i + 2];
+    rk[i + 2] = rk[j + 2];
+    rk[j + 2] = temp;
+    temp = rk[i + 3];
+    rk[i + 3] = rk[j + 3];
+    rk[j + 3] = temp;
+  }
+  /* apply the inverse MixColumn transform to all round keys but the first and
+   * the last: */
+  for (i = 1; i < aeskey->rounds; i++) {
+    rk += 4;
+    rk[0] =
+        Td0[Te1[(rk[0] >> 24)] & 0xff] ^ Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[0] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[0]) & 0xff] & 0xff];
+    rk[1] =
+        Td0[Te1[(rk[1] >> 24)] & 0xff] ^ Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[1] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[1]) & 0xff] & 0xff];
+    rk[2] =
+        Td0[Te1[(rk[2] >> 24)] & 0xff] ^ Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[2] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[2]) & 0xff] & 0xff];
+    rk[3] =
+        Td0[Te1[(rk[3] >> 24)] & 0xff] ^ Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[3] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[3]) & 0xff] & 0xff];
+  }
+  return 0;
+}
+
+void AES_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  const uint32_t *rk;
+  uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+  int r;
+#endif /* ?FULL_UNROLL */
+
+  assert(in && out && key);
+  rk = key->rd_key;
+
+  /* map byte array block to cipher state
+   * and add initial round key: */
+  s0 = GETU32(in) ^ rk[0];
+  s1 = GETU32(in + 4) ^ rk[1];
+  s2 = GETU32(in + 8) ^ rk[2];
+  s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+  /* round 1: */
+  t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+       Te3[s3 & 0xff] ^ rk[4];
+  t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+       Te3[s0 & 0xff] ^ rk[5];
+  t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+       Te3[s1 & 0xff] ^ rk[6];
+  t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+       Te3[s2 & 0xff] ^ rk[7];
+  /* round 2: */
+  s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+       Te3[t3 & 0xff] ^ rk[8];
+  s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+       Te3[t0 & 0xff] ^ rk[9];
+  s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+       Te3[t1 & 0xff] ^ rk[10];
+  s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+       Te3[t2 & 0xff] ^ rk[11];
+  /* round 3: */
+  t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+       Te3[s3 & 0xff] ^ rk[12];
+  t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+       Te3[s0 & 0xff] ^ rk[13];
+  t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+       Te3[s1 & 0xff] ^ rk[14];
+  t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+       Te3[s2 & 0xff] ^ rk[15];
+  /* round 4: */
+  s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+       Te3[t3 & 0xff] ^ rk[16];
+  s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+       Te3[t0 & 0xff] ^ rk[17];
+  s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+       Te3[t1 & 0xff] ^ rk[18];
+  s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+       Te3[t2 & 0xff] ^ rk[19];
+  /* round 5: */
+  t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+       Te3[s3 & 0xff] ^ rk[20];
+  t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+       Te3[s0 & 0xff] ^ rk[21];
+  t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+       Te3[s1 & 0xff] ^ rk[22];
+  t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+       Te3[s2 & 0xff] ^ rk[23];
+  /* round 6: */
+  s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+       Te3[t3 & 0xff] ^ rk[24];
+  s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+       Te3[t0 & 0xff] ^ rk[25];
+  s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+       Te3[t1 & 0xff] ^ rk[26];
+  s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+       Te3[t2 & 0xff] ^ rk[27];
+  /* round 7: */
+  t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+       Te3[s3 & 0xff] ^ rk[28];
+  t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+       Te3[s0 & 0xff] ^ rk[29];
+  t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+       Te3[s1 & 0xff] ^ rk[30];
+  t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+       Te3[s2 & 0xff] ^ rk[31];
+  /* round 8: */
+  s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+       Te3[t3 & 0xff] ^ rk[32];
+  s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+       Te3[t0 & 0xff] ^ rk[33];
+  s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+       Te3[t1 & 0xff] ^ rk[34];
+  s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+       Te3[t2 & 0xff] ^ rk[35];
+  /* round 9: */
+  t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+       Te3[s3 & 0xff] ^ rk[36];
+  t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+       Te3[s0 & 0xff] ^ rk[37];
+  t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+       Te3[s1 & 0xff] ^ rk[38];
+  t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+       Te3[s2 & 0xff] ^ rk[39];
+  if (key->rounds > 10) {
+    /* round 10: */
+    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+         Te3[t3 & 0xff] ^ rk[40];
+    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+         Te3[t0 & 0xff] ^ rk[41];
+    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+         Te3[t1 & 0xff] ^ rk[42];
+    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+         Te3[t2 & 0xff] ^ rk[43];
+    /* round 11: */
+    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+         Te3[s3 & 0xff] ^ rk[44];
+    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+         Te3[s0 & 0xff] ^ rk[45];
+    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+         Te3[s1 & 0xff] ^ rk[46];
+    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+         Te3[s2 & 0xff] ^ rk[47];
+    if (key->rounds > 12) {
+      /* round 12: */
+      s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+           Te3[t3 & 0xff] ^ rk[48];
+      s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+           Te3[t0 & 0xff] ^ rk[49];
+      s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+           Te3[t1 & 0xff] ^ rk[50];
+      s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+           Te3[t2 & 0xff] ^ rk[51];
+      /* round 13: */
+      t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+           Te3[s3 & 0xff] ^ rk[52];
+      t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+           Te3[s0 & 0xff] ^ rk[53];
+      t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+           Te3[s1 & 0xff] ^ rk[54];
+      t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+           Te3[s2 & 0xff] ^ rk[55];
+    }
+  }
+  rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+  /*
+   * Nr - 1 full rounds:
+   */
+  r = key->rounds >> 1;
+  for (;;) {
+    t0 = Te0[(s0 >> 24)] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+         Te3[(s3) & 0xff] ^ rk[4];
+    t1 = Te0[(s1 >> 24)] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+         Te3[(s0) & 0xff] ^ rk[5];
+    t2 = Te0[(s2 >> 24)] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+         Te3[(s1) & 0xff] ^ rk[6];
+    t3 = Te0[(s3 >> 24)] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+         Te3[(s2) & 0xff] ^ rk[7];
+
+    rk += 8;
+    if (--r == 0) {
+      break;
+    }
+
+    s0 = Te0[(t0 >> 24)] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+         Te3[(t3) & 0xff] ^ rk[0];
+    s1 = Te0[(t1 >> 24)] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+         Te3[(t0) & 0xff] ^ rk[1];
+    s2 = Te0[(t2 >> 24)] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+         Te3[(t1) & 0xff] ^ rk[2];
+    s3 = Te0[(t3 >> 24)] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+         Te3[(t2) & 0xff] ^ rk[3];
+  }
+#endif /* ?FULL_UNROLL */
+  /*  apply last round and map cipher state to byte array block: */
+  s0 = (Te2[(t0 >> 24)] & 0xff000000) ^ (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t3) & 0xff] & 0x000000ff) ^
+       rk[0];
+  PUTU32(out, s0);
+  s1 = (Te2[(t1 >> 24)] & 0xff000000) ^ (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t0) & 0xff] & 0x000000ff) ^
+       rk[1];
+  PUTU32(out + 4, s1);
+  s2 = (Te2[(t2 >> 24)] & 0xff000000) ^ (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t1) & 0xff] & 0x000000ff) ^
+       rk[2];
+  PUTU32(out + 8, s2);
+  s3 = (Te2[(t3 >> 24)] & 0xff000000) ^ (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t2) & 0xff] & 0x000000ff) ^
+       rk[3];
+  PUTU32(out + 12, s3);
+}
+
+void AES_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  const uint32_t *rk;
+  uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+  int r;
+#endif /* ?FULL_UNROLL */
+
+  assert(in && out && key);
+  rk = key->rd_key;
+
+  /* map byte array block to cipher state
+   * and add initial round key: */
+  s0 = GETU32(in) ^ rk[0];
+  s1 = GETU32(in + 4) ^ rk[1];
+  s2 = GETU32(in + 8) ^ rk[2];
+  s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+  /* round 1: */
+  t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+       Td3[s1 & 0xff] ^ rk[4];
+  t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+       Td3[s2 & 0xff] ^ rk[5];
+  t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+       Td3[s3 & 0xff] ^ rk[6];
+  t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+       Td3[s0 & 0xff] ^ rk[7];
+  /* round 2: */
+  s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+       Td3[t1 & 0xff] ^ rk[8];
+  s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+       Td3[t2 & 0xff] ^ rk[9];
+  s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+       Td3[t3 & 0xff] ^ rk[10];
+  s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+       Td3[t0 & 0xff] ^ rk[11];
+  /* round 3: */
+  t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+       Td3[s1 & 0xff] ^ rk[12];
+  t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+       Td3[s2 & 0xff] ^ rk[13];
+  t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+       Td3[s3 & 0xff] ^ rk[14];
+  t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+       Td3[s0 & 0xff] ^ rk[15];
+  /* round 4: */
+  s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+       Td3[t1 & 0xff] ^ rk[16];
+  s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+       Td3[t2 & 0xff] ^ rk[17];
+  s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+       Td3[t3 & 0xff] ^ rk[18];
+  s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+       Td3[t0 & 0xff] ^ rk[19];
+  /* round 5: */
+  t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+       Td3[s1 & 0xff] ^ rk[20];
+  t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+       Td3[s2 & 0xff] ^ rk[21];
+  t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+       Td3[s3 & 0xff] ^ rk[22];
+  t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+       Td3[s0 & 0xff] ^ rk[23];
+  /* round 6: */
+  s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+       Td3[t1 & 0xff] ^ rk[24];
+  s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+       Td3[t2 & 0xff] ^ rk[25];
+  s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+       Td3[t3 & 0xff] ^ rk[26];
+  s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+       Td3[t0 & 0xff] ^ rk[27];
+  /* round 7: */
+  t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+       Td3[s1 & 0xff] ^ rk[28];
+  t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+       Td3[s2 & 0xff] ^ rk[29];
+  t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+       Td3[s3 & 0xff] ^ rk[30];
+  t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+       Td3[s0 & 0xff] ^ rk[31];
+  /* round 8: */
+  s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+       Td3[t1 & 0xff] ^ rk[32];
+  s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+       Td3[t2 & 0xff] ^ rk[33];
+  s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+       Td3[t3 & 0xff] ^ rk[34];
+  s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+       Td3[t0 & 0xff] ^ rk[35];
+  /* round 9: */
+  t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+       Td3[s1 & 0xff] ^ rk[36];
+  t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+       Td3[s2 & 0xff] ^ rk[37];
+  t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+       Td3[s3 & 0xff] ^ rk[38];
+  t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+       Td3[s0 & 0xff] ^ rk[39];
+  if (key->rounds > 10) {
+    /* round 10: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+         Td3[t1 & 0xff] ^ rk[40];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+         Td3[t2 & 0xff] ^ rk[41];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+         Td3[t3 & 0xff] ^ rk[42];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+         Td3[t0 & 0xff] ^ rk[43];
+    /* round 11: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+         Td3[s1 & 0xff] ^ rk[44];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+         Td3[s2 & 0xff] ^ rk[45];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+         Td3[s3 & 0xff] ^ rk[46];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+         Td3[s0 & 0xff] ^ rk[47];
+    if (key->rounds > 12) {
+      /* round 12: */
+      s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+           Td3[t1 & 0xff] ^ rk[48];
+      s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+           Td3[t2 & 0xff] ^ rk[49];
+      s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+           Td3[t3 & 0xff] ^ rk[50];
+      s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+           Td3[t0 & 0xff] ^ rk[51];
+      /* round 13: */
+      t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+           Td3[s1 & 0xff] ^ rk[52];
+      t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+           Td3[s2 & 0xff] ^ rk[53];
+      t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+           Td3[s3 & 0xff] ^ rk[54];
+      t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+           Td3[s0 & 0xff] ^ rk[55];
+    }
+  }
+  rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+  /*
+   * Nr - 1 full rounds:
+   */
+  r = key->rounds >> 1;
+  for (;;) {
+    t0 = Td0[(s0 >> 24)] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+         Td3[(s1) & 0xff] ^ rk[4];
+    t1 = Td0[(s1 >> 24)] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+         Td3[(s2) & 0xff] ^ rk[5];
+    t2 = Td0[(s2 >> 24)] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+         Td3[(s3) & 0xff] ^ rk[6];
+    t3 = Td0[(s3 >> 24)] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+         Td3[(s0) & 0xff] ^ rk[7];
+
+    rk += 8;
+    if (--r == 0) {
+      break;
+    }
+
+    s0 = Td0[(t0 >> 24)] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+         Td3[(t1) & 0xff] ^ rk[0];
+    s1 = Td0[(t1 >> 24)] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+         Td3[(t2) & 0xff] ^ rk[1];
+    s2 = Td0[(t2 >> 24)] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+         Td3[(t3) & 0xff] ^ rk[2];
+    s3 = Td0[(t3 >> 24)] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+         Td3[(t0) & 0xff] ^ rk[3];
+  }
+#endif /* ?FULL_UNROLL */
+  /* apply last round and
+   * map cipher state to byte array block: */
+  s0 = (Td4[(t0 >> 24)] << 24) ^ (Td4[(t3 >> 16) & 0xff] << 16) ^
+       (Td4[(t2 >> 8) & 0xff] << 8) ^ (Td4[(t1) & 0xff]) ^ rk[0];
+  PUTU32(out, s0);
+  s1 = (Td4[(t1 >> 24)] << 24) ^ (Td4[(t0 >> 16) & 0xff] << 16) ^
+       (Td4[(t3 >> 8) & 0xff] << 8) ^ (Td4[(t2) & 0xff]) ^ rk[1];
+  PUTU32(out + 4, s1);
+  s2 = (Td4[(t2 >> 24)] << 24) ^ (Td4[(t1 >> 16) & 0xff] << 16) ^
+       (Td4[(t0 >> 8) & 0xff] << 8) ^ (Td4[(t3) & 0xff]) ^ rk[2];
+  PUTU32(out + 8, s2);
+  s3 = (Td4[(t3 >> 24)] << 24) ^ (Td4[(t2 >> 16) & 0xff] << 16) ^
+       (Td4[(t1 >> 8) & 0xff] << 8) ^ (Td4[(t0) & 0xff]) ^ rk[3];
+  PUTU32(out + 12, s3);
+}
+
+#endif
diff --git a/crypto/aes/aes.h b/crypto/aes/aes.h
new file mode 100644
index 0000000..8156964
--- /dev/null
+++ b/crypto/aes/aes.h
@@ -0,0 +1,138 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#ifndef OPENSSL_HEADER_AES_H
+#define OPENSSL_HEADER_AES_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Raw AES functions. */
+
+
+#define AES_ENCRYPT 1
+#define AES_DECRYPT 0
+
+/* AES_MAXNR is the maximum number of AES rounds. */
+#define AES_MAXNR 14
+
+#define AES_BLOCK_SIZE 16
+
+/* aes_key_st should be an opaque type, but EVP requires that the size be
+ * known. */
+struct aes_key_st {
+  uint32_t rd_key[4 * (AES_MAXNR + 1)];
+  unsigned rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+/* AES_set_encrypt_key configures |aeskey| to encrypt with the |bits|-bit key,
+ * |key|.
+ *
+ * WARNING: unlike other OpenSSL functions, this returns zero on success and a
+ * negative number on error. */
+int AES_set_encrypt_key(const uint8_t *key, unsigned bits, AES_KEY *aeskey);
+
+/* AES_set_decrypt_key configures |aeskey| to decrypt with the |bits|-bit key,
+ * |key|.
+ *
+ * WARNING: unlike other OpenSSL functions, this returns zero on success and a
+ * negative number on error. */
+int AES_set_decrypt_key(const uint8_t *key, unsigned bits, AES_KEY *aeskey);
+
+/* AES_encrypt encrypts a single block from |in| to |out| with |key|. The |in|
+ * and |out| pointers may overlap. */
+void AES_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+
+/* AES_decrypt decrypts a single block from |in| to |out| with |key|. The |in|
+ * and |out| pointers may overlap. */
+void AES_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+
+
+/* Block cipher modes. */
+
+/* AES_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode) |len|
+ * bytes from |in| to |out|. The |num| parameter must be set to zero on the
+ * first call and |ivec| will be incremented. */
+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);
+
+/* AES_ecb_encrypt encrypts (or decrypts, if |enc| == |AES_DECRYPT|) a single,
+ * 16 byte block from |in| to |out|. */
+void AES_ecb_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key,
+                     const int enc);
+
+/* AES_cbc_encrypt encrypts (or decrypts, if |enc| == |AES_DECRYPT|) |len|
+ * bytes from |in| to |out|. The length must be a multiple of the block size. */
+void AES_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                     const AES_KEY *key, uint8_t *ivec, const int enc);
+
+/* AES_ofb128_encrypt encrypts (or decrypts, it's the same in CTR mode) |len|
+ * bytes from |in| to |out|. The |num| parameter must be set to zero on the
+ * first call. */
+void AES_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                        const AES_KEY *key, uint8_t *ivec, int *num);
+
+/* AES_cfb128_encrypt encrypts (or decrypts, if |enc| == |AES_DECRYPT|) |len|
+ * bytes from |in| to |out|. The |num| parameter must be set to zero on the
+ * first call. */
+void AES_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                        const AES_KEY *key, uint8_t *ivec, int *num,
+                        int enc);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_AES_H */
diff --git a/crypto/aes/asm/aes-586.pl b/crypto/aes/asm/aes-586.pl
new file mode 100755
index 0000000..1c1e23e
--- /dev/null
+++ b/crypto/aes/asm/aes-586.pl
@@ -0,0 +1,2987 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Version 4.3.
+#
+# You might fail to appreciate this module performance from the first
+# try. If compared to "vanilla" linux-ia32-icc target, i.e. considered
+# to be *the* best Intel C compiler without -KPIC, performance appears
+# to be virtually identical... But try to re-configure with shared
+# library support... Aha! Intel compiler "suddenly" lags behind by 30%
+# [on P4, more on others]:-) And if compared to position-independent
+# code generated by GNU C, this code performs *more* than *twice* as
+# fast! Yes, all this buzz about PIC means that unlike other hand-
+# coded implementations, this one was explicitly designed to be safe
+# to use even in shared library context... This also means that this
+# code isn't necessarily absolutely fastest "ever," because in order
+# to achieve position independence an extra register has to be
+# off-loaded to stack, which affects the benchmark result.
+#
+# Special note about instruction choice. Do you recall RC4_INT code
+# performing poorly on P4? It might be the time to figure out why.
+# RC4_INT code implies effective address calculations in base+offset*4
+# form. Trouble is that it seems that offset scaling turned to be
+# critical path... At least eliminating scaling resulted in 2.8x RC4
+# performance improvement [as you might recall]. As AES code is hungry
+# for scaling too, I [try to] avoid the latter by favoring off-by-2
+# shifts and masking the result with 0xFF<<2 instead of "boring" 0xFF.
+#
+# As was shown by Dean Gaudet <dean@arctic.org>, the above note turned
+# void. Performance improvement with off-by-2 shifts was observed on
+# intermediate implementation, which was spilling yet another register
+# to stack... Final offset*4 code below runs just a tad faster on P4,
+# but exhibits up to 10% improvement on other cores.
+#
+# Second version is "monolithic" replacement for aes_core.c, which in
+# addition to AES_[de|en]crypt implements AES_set_[de|en]cryption_key.
+# This made it possible to implement little-endian variant of the
+# algorithm without modifying the base C code. Motivating factor for
+# the undertaken effort was that it appeared that in tight IA-32
+# register window little-endian flavor could achieve slightly higher
+# Instruction Level Parallelism, and it indeed resulted in up to 15%
+# better performance on most recent µ-archs...
+#
+# Third version adds AES_cbc_encrypt implementation, which resulted in
+# up to 40% performance imrovement of CBC benchmark results. 40% was
+# observed on P4 core, where "overall" imrovement coefficient, i.e. if
+# compared to PIC generated by GCC and in CBC mode, was observed to be
+# as large as 4x:-) CBC performance is virtually identical to ECB now
+# and on some platforms even better, e.g. 17.6 "small" cycles/byte on
+# Opteron, because certain function prologues and epilogues are
+# effectively taken out of the loop...
+#
+# Version 3.2 implements compressed tables and prefetch of these tables
+# in CBC[!] mode. Former means that 3/4 of table references are now
+# misaligned, which unfortunately has negative impact on elder IA-32
+# implementations, Pentium suffered 30% penalty, PIII - 10%.
+#
+# Version 3.3 avoids L1 cache aliasing between stack frame and
+# S-boxes, and 3.4 - L1 cache aliasing even between key schedule. The
+# latter is achieved by copying the key schedule to controlled place in
+# stack. This unfortunately has rather strong impact on small block CBC
+# performance, ~2x deterioration on 16-byte block if compared to 3.3.
+#
+# Version 3.5 checks if there is L1 cache aliasing between user-supplied
+# key schedule and S-boxes and abstains from copying the former if
+# there is no. This allows end-user to consciously retain small block
+# performance by aligning key schedule in specific manner.
+#
+# Version 3.6 compresses Td4 to 256 bytes and prefetches it in ECB.
+#
+# Current ECB performance numbers for 128-bit key in CPU cycles per
+# processed byte [measure commonly used by AES benchmarkers] are:
+#
+#		small footprint		fully unrolled
+# P4		24			22
+# AMD K8	20			19
+# PIII		25			23
+# Pentium	81			78
+#
+# Version 3.7 reimplements outer rounds as "compact." Meaning that
+# first and last rounds reference compact 256 bytes S-box. This means
+# that first round consumes a lot more CPU cycles and that encrypt
+# and decrypt performance becomes asymmetric. Encrypt performance
+# drops by 10-12%, while decrypt - by 20-25%:-( 256 bytes S-box is
+# aggressively pre-fetched.
+#
+# Version 4.0 effectively rolls back to 3.6 and instead implements
+# additional set of functions, _[x86|sse]_AES_[en|de]crypt_compact,
+# which use exclusively 256 byte S-box. These functions are to be
+# called in modes not concealing plain text, such as ECB, or when
+# we're asked to process smaller amount of data [or unconditionally
+# on hyper-threading CPU]. Currently it's called unconditionally from
+# AES_[en|de]crypt, which affects all modes, but CBC. CBC routine
+# still needs to be modified to switch between slower and faster
+# mode when appropriate... But in either case benchmark landscape
+# changes dramatically and below numbers are CPU cycles per processed
+# byte for 128-bit key.
+#
+#		ECB encrypt	ECB decrypt	CBC large chunk
+# P4		52[54]		83[95]		23
+# AMD K8	46[41]		66[70]		18
+# PIII		41[50]		60[77]		24
+# Core 2	31[36]		45[64]		18.5
+# Atom		76[100]		96[138]		60
+# Pentium	115		150		77
+#
+# Version 4.1 switches to compact S-box even in key schedule setup.
+#
+# Version 4.2 prefetches compact S-box in every SSE round or in other
+# words every cache-line is *guaranteed* to be accessed within ~50
+# cycles window. Why just SSE? Because it's needed on hyper-threading
+# CPU! Which is also why it's prefetched with 64 byte stride. Best
+# part is that it has no negative effect on performance:-)  
+#
+# Version 4.3 implements switch between compact and non-compact block
+# functions in AES_cbc_encrypt depending on how much data was asked
+# to be processed in one stroke.
+#
+######################################################################
+# Timing attacks are classified in two classes: synchronous when
+# attacker consciously initiates cryptographic operation and collects
+# timing data of various character afterwards, and asynchronous when
+# malicious code is executed on same CPU simultaneously with AES,
+# instruments itself and performs statistical analysis of this data.
+#
+# As far as synchronous attacks go the root to the AES timing
+# vulnerability is twofold. Firstly, of 256 S-box elements at most 160
+# are referred to in single 128-bit block operation. Well, in C
+# implementation with 4 distinct tables it's actually as little as 40
+# references per 256 elements table, but anyway... Secondly, even
+# though S-box elements are clustered into smaller amount of cache-
+# lines, smaller than 160 and even 40, it turned out that for certain
+# plain-text pattern[s] or simply put chosen plain-text and given key
+# few cache-lines remain unaccessed during block operation. Now, if
+# attacker can figure out this access pattern, he can deduct the key
+# [or at least part of it]. The natural way to mitigate this kind of
+# attacks is to minimize the amount of cache-lines in S-box and/or
+# prefetch them to ensure that every one is accessed for more uniform
+# timing. But note that *if* plain-text was concealed in such way that
+# input to block function is distributed *uniformly*, then attack
+# wouldn't apply. Now note that some encryption modes, most notably
+# CBC, do mask the plain-text in this exact way [secure cipher output
+# is distributed uniformly]. Yes, one still might find input that
+# would reveal the information about given key, but if amount of
+# candidate inputs to be tried is larger than amount of possible key
+# combinations then attack becomes infeasible. This is why revised
+# AES_cbc_encrypt "dares" to switch to larger S-box when larger chunk
+# of data is to be processed in one stroke. The current size limit of
+# 512 bytes is chosen to provide same [diminishigly low] probability
+# for cache-line to remain untouched in large chunk operation with
+# large S-box as for single block operation with compact S-box and
+# surely needs more careful consideration...
+#
+# As for asynchronous attacks. There are two flavours: attacker code
+# being interleaved with AES on hyper-threading CPU at *instruction*
+# level, and two processes time sharing single core. As for latter.
+# Two vectors. 1. Given that attacker process has higher priority,
+# yield execution to process performing AES just before timer fires
+# off the scheduler, immediately regain control of CPU and analyze the
+# cache state. For this attack to be efficient attacker would have to
+# effectively slow down the operation by several *orders* of magnitute,
+# by ratio of time slice to duration of handful of AES rounds, which
+# unlikely to remain unnoticed. Not to mention that this also means
+# that he would spend correspondigly more time to collect enough
+# statistical data to mount the attack. It's probably appropriate to
+# say that if adeversary reckons that this attack is beneficial and
+# risks to be noticed, you probably have larger problems having him
+# mere opportunity. In other words suggested code design expects you
+# to preclude/mitigate this attack by overall system security design.
+# 2. Attacker manages to make his code interrupt driven. In order for
+# this kind of attack to be feasible, interrupt rate has to be high
+# enough, again comparable to duration of handful of AES rounds. But
+# is there interrupt source of such rate? Hardly, not even 1Gbps NIC
+# generates interrupts at such raging rate...
+#
+# And now back to the former, hyper-threading CPU or more specifically
+# Intel P4. Recall that asynchronous attack implies that malicious
+# code instruments itself. And naturally instrumentation granularity
+# has be noticeably lower than duration of codepath accessing S-box.
+# Given that all cache-lines are accessed during that time that is.
+# Current implementation accesses *all* cache-lines within ~50 cycles
+# window, which is actually *less* than RDTSC latency on Intel P4!
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"aes-586.pl",$x86only = $ARGV[$#ARGV] eq "386");
+&static_label("AES_Te");
+&static_label("AES_Td");
+
+$s0="eax";
+$s1="ebx";
+$s2="ecx";
+$s3="edx";
+$key="edi";
+$acc="esi";
+$tbl="ebp";
+
+# stack frame layout in _[x86|sse]_AES_* routines, frame is allocated
+# by caller
+$__ra=&DWP(0,"esp");	# return address
+$__s0=&DWP(4,"esp");	# s0 backing store
+$__s1=&DWP(8,"esp");	# s1 backing store
+$__s2=&DWP(12,"esp");	# s2 backing store
+$__s3=&DWP(16,"esp");	# s3 backing store
+$__key=&DWP(20,"esp");	# pointer to key schedule
+$__end=&DWP(24,"esp");	# pointer to end of key schedule
+$__tbl=&DWP(28,"esp");	# %ebp backing store
+
+# stack frame layout in AES_[en|crypt] routines, which differs from
+# above by 4 and overlaps by %ebp backing store
+$_tbl=&DWP(24,"esp");
+$_esp=&DWP(28,"esp");
+
+sub _data_word() { my $i; while(defined($i=shift)) { &data_word($i,$i); } }
+
+$speed_limit=512;	# chunks smaller than $speed_limit are
+			# processed with compact routine in CBC mode
+$small_footprint=1;	# $small_footprint=1 code is ~5% slower [on
+			# recent µ-archs], but ~5 times smaller!
+			# I favor compact code to minimize cache
+			# contention and in hope to "collect" 5% back
+			# in real-life applications...
+
+$vertical_spin=0;	# shift "verticaly" defaults to 0, because of
+			# its proof-of-concept status...
+# Note that there is no decvert(), as well as last encryption round is
+# performed with "horizontal" shifts. This is because this "vertical"
+# implementation [one which groups shifts on a given $s[i] to form a
+# "column," unlike "horizontal" one, which groups shifts on different
+# $s[i] to form a "row"] is work in progress. It was observed to run
+# few percents faster on Intel cores, but not AMD. On AMD K8 core it's
+# whole 12% slower:-( So we face a trade-off... Shall it be resolved
+# some day? Till then the code is considered experimental and by
+# default remains dormant...
+
+sub encvert()
+{ my ($te,@s) = @_;
+  my ($v0,$v1) = ($acc,$key);
+
+	&mov	($v0,$s[3]);				# copy s3
+	&mov	(&DWP(4,"esp"),$s[2]);			# save s2
+	&mov	($v1,$s[0]);				# copy s0
+	&mov	(&DWP(8,"esp"),$s[1]);			# save s1
+
+	&movz	($s[2],&HB($s[0]));
+	&and	($s[0],0xFF);
+	&mov	($s[0],&DWP(0,$te,$s[0],8));		# s0>>0
+	&shr	($v1,16);
+	&mov	($s[3],&DWP(3,$te,$s[2],8));		# s0>>8
+	&movz	($s[1],&HB($v1));
+	&and	($v1,0xFF);
+	&mov	($s[2],&DWP(2,$te,$v1,8));		# s0>>16
+	 &mov	($v1,$v0);
+	&mov	($s[1],&DWP(1,$te,$s[1],8));		# s0>>24
+
+	&and	($v0,0xFF);
+	&xor	($s[3],&DWP(0,$te,$v0,8));		# s3>>0
+	&movz	($v0,&HB($v1));
+	&shr	($v1,16);
+	&xor	($s[2],&DWP(3,$te,$v0,8));		# s3>>8
+	&movz	($v0,&HB($v1));
+	&and	($v1,0xFF);
+	&xor	($s[1],&DWP(2,$te,$v1,8));		# s3>>16
+	 &mov	($v1,&DWP(4,"esp"));			# restore s2
+	&xor	($s[0],&DWP(1,$te,$v0,8));		# s3>>24
+
+	&mov	($v0,$v1);
+	&and	($v1,0xFF);
+	&xor	($s[2],&DWP(0,$te,$v1,8));		# s2>>0
+	&movz	($v1,&HB($v0));
+	&shr	($v0,16);
+	&xor	($s[1],&DWP(3,$te,$v1,8));		# s2>>8
+	&movz	($v1,&HB($v0));
+	&and	($v0,0xFF);
+	&xor	($s[0],&DWP(2,$te,$v0,8));		# s2>>16
+	 &mov	($v0,&DWP(8,"esp"));			# restore s1
+	&xor	($s[3],&DWP(1,$te,$v1,8));		# s2>>24
+
+	&mov	($v1,$v0);
+	&and	($v0,0xFF);
+	&xor	($s[1],&DWP(0,$te,$v0,8));		# s1>>0
+	&movz	($v0,&HB($v1));
+	&shr	($v1,16);
+	&xor	($s[0],&DWP(3,$te,$v0,8));		# s1>>8
+	&movz	($v0,&HB($v1));
+	&and	($v1,0xFF);
+	&xor	($s[3],&DWP(2,$te,$v1,8));		# s1>>16
+	 &mov	($key,$__key);				# reincarnate v1 as key
+	&xor	($s[2],&DWP(1,$te,$v0,8));		# s1>>24
+}
+
+# Another experimental routine, which features "horizontal spin," but
+# eliminates one reference to stack. Strangely enough runs slower...
+sub enchoriz()
+{ my ($v0,$v1) = ($key,$acc);
+
+	&movz	($v0,&LB($s0));			#  3, 2, 1, 0*
+	&rotr	($s2,8);			#  8,11,10, 9
+	&mov	($v1,&DWP(0,$te,$v0,8));	#  0
+	&movz	($v0,&HB($s1));			#  7, 6, 5*, 4
+	&rotr	($s3,16);			# 13,12,15,14
+	&xor	($v1,&DWP(3,$te,$v0,8));	#  5
+	&movz	($v0,&HB($s2));			#  8,11,10*, 9
+	&rotr	($s0,16);			#  1, 0, 3, 2
+	&xor	($v1,&DWP(2,$te,$v0,8));	# 10
+	&movz	($v0,&HB($s3));			# 13,12,15*,14
+	&xor	($v1,&DWP(1,$te,$v0,8));	# 15, t[0] collected
+	&mov	($__s0,$v1);			# t[0] saved
+
+	&movz	($v0,&LB($s1));			#  7, 6, 5, 4*
+	&shr	($s1,16);			#  -, -, 7, 6
+	&mov	($v1,&DWP(0,$te,$v0,8));	#  4
+	&movz	($v0,&LB($s3));			# 13,12,15,14*
+	&xor	($v1,&DWP(2,$te,$v0,8));	# 14
+	&movz	($v0,&HB($s0));			#  1, 0, 3*, 2
+	&and	($s3,0xffff0000);		# 13,12, -, -
+	&xor	($v1,&DWP(1,$te,$v0,8));	#  3
+	&movz	($v0,&LB($s2));			#  8,11,10, 9*
+	&or	($s3,$s1);			# 13,12, 7, 6
+	&xor	($v1,&DWP(3,$te,$v0,8));	#  9, t[1] collected
+	&mov	($s1,$v1);			#  s[1]=t[1]
+
+	&movz	($v0,&LB($s0));			#  1, 0, 3, 2*
+	&shr	($s2,16);			#  -, -, 8,11
+	&mov	($v1,&DWP(2,$te,$v0,8));	#  2
+	&movz	($v0,&HB($s3));			# 13,12, 7*, 6
+	&xor	($v1,&DWP(1,$te,$v0,8));	#  7
+	&movz	($v0,&HB($s2));			#  -, -, 8*,11
+	&xor	($v1,&DWP(0,$te,$v0,8));	#  8
+	&mov	($v0,$s3);
+	&shr	($v0,24);			# 13
+	&xor	($v1,&DWP(3,$te,$v0,8));	# 13, t[2] collected
+
+	&movz	($v0,&LB($s2));			#  -, -, 8,11*
+	&shr	($s0,24);			#  1*
+	&mov	($s2,&DWP(1,$te,$v0,8));	# 11
+	&xor	($s2,&DWP(3,$te,$s0,8));	#  1
+	&mov	($s0,$__s0);			# s[0]=t[0]
+	&movz	($v0,&LB($s3));			# 13,12, 7, 6*
+	&shr	($s3,16);			#   ,  ,13,12
+	&xor	($s2,&DWP(2,$te,$v0,8));	#  6
+	&mov	($key,$__key);			# reincarnate v0 as key
+	&and	($s3,0xff);			#   ,  ,13,12*
+	&mov	($s3,&DWP(0,$te,$s3,8));	# 12
+	&xor	($s3,$s2);			# s[2]=t[3] collected
+	&mov	($s2,$v1);			# s[2]=t[2]
+}
+
+# More experimental code... SSE one... Even though this one eliminates
+# *all* references to stack, it's not faster...
+sub sse_encbody()
+{
+	&movz	($acc,&LB("eax"));		#  0
+	&mov	("ecx",&DWP(0,$tbl,$acc,8));	#  0
+	&pshufw	("mm2","mm0",0x0d);		#  7, 6, 3, 2
+	&movz	("edx",&HB("eax"));		#  1
+	&mov	("edx",&DWP(3,$tbl,"edx",8));	#  1
+	&shr	("eax",16);			#  5, 4
+
+	&movz	($acc,&LB("ebx"));		# 10
+	&xor	("ecx",&DWP(2,$tbl,$acc,8));	# 10
+	&pshufw	("mm6","mm4",0x08);		# 13,12, 9, 8
+	&movz	($acc,&HB("ebx"));		# 11
+	&xor	("edx",&DWP(1,$tbl,$acc,8));	# 11
+	&shr	("ebx",16);			# 15,14
+
+	&movz	($acc,&HB("eax"));		#  5
+	&xor	("ecx",&DWP(3,$tbl,$acc,8));	#  5
+	&movq	("mm3",QWP(16,$key));
+	&movz	($acc,&HB("ebx"));		# 15
+	&xor	("ecx",&DWP(1,$tbl,$acc,8));	# 15
+	&movd	("mm0","ecx");			# t[0] collected
+
+	&movz	($acc,&LB("eax"));		#  4
+	&mov	("ecx",&DWP(0,$tbl,$acc,8));	#  4
+	&movd	("eax","mm2");			#  7, 6, 3, 2
+	&movz	($acc,&LB("ebx"));		# 14
+	&xor	("ecx",&DWP(2,$tbl,$acc,8));	# 14
+	&movd	("ebx","mm6");			# 13,12, 9, 8
+
+	&movz	($acc,&HB("eax"));		#  3
+	&xor	("ecx",&DWP(1,$tbl,$acc,8));	#  3
+	&movz	($acc,&HB("ebx"));		#  9
+	&xor	("ecx",&DWP(3,$tbl,$acc,8));	#  9
+	&movd	("mm1","ecx");			# t[1] collected
+
+	&movz	($acc,&LB("eax"));		#  2
+	&mov	("ecx",&DWP(2,$tbl,$acc,8));	#  2
+	&shr	("eax",16);			#  7, 6
+	&punpckldq	("mm0","mm1");		# t[0,1] collected
+	&movz	($acc,&LB("ebx"));		#  8
+	&xor	("ecx",&DWP(0,$tbl,$acc,8));	#  8
+	&shr	("ebx",16);			# 13,12
+
+	&movz	($acc,&HB("eax"));		#  7
+	&xor	("ecx",&DWP(1,$tbl,$acc,8));	#  7
+	&pxor	("mm0","mm3");
+	&movz	("eax",&LB("eax"));		#  6
+	&xor	("edx",&DWP(2,$tbl,"eax",8));	#  6
+	&pshufw	("mm1","mm0",0x08);		#  5, 4, 1, 0
+	&movz	($acc,&HB("ebx"));		# 13
+	&xor	("ecx",&DWP(3,$tbl,$acc,8));	# 13
+	&xor	("ecx",&DWP(24,$key));		# t[2]
+	&movd	("mm4","ecx");			# t[2] collected
+	&movz	("ebx",&LB("ebx"));		# 12
+	&xor	("edx",&DWP(0,$tbl,"ebx",8));	# 12
+	&shr	("ecx",16);
+	&movd	("eax","mm1");			#  5, 4, 1, 0
+	&mov	("ebx",&DWP(28,$key));		# t[3]
+	&xor	("ebx","edx");
+	&movd	("mm5","ebx");			# t[3] collected
+	&and	("ebx",0xffff0000);
+	&or	("ebx","ecx");
+
+	&punpckldq	("mm4","mm5");		# t[2,3] collected
+}
+
+######################################################################
+# "Compact" block function
+######################################################################
+
+sub enccompact()
+{ my $Fn = \&mov;
+  while ($#_>5) { pop(@_); $Fn=sub{}; }
+  my ($i,$te,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# $Fn is used in first compact round and its purpose is to
+	# void restoration of some values from stack, so that after
+	# 4xenccompact with extra argument $key value is left there...
+	if ($i==3)  {	&$Fn	($key,$__key);			}##%edx
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+	if ($i==1)  {	&shr	($s[0],16);			}#%ebx[1]
+	if ($i==2)  {	&shr	($s[0],24);			}#%ecx[2]
+			&movz	($out,&BP(-128,$te,$out,1));
+
+	if ($i==3)  {	$tmp=$s[1];				}##%eax
+			&movz	($tmp,&HB($s[1]));
+			&movz	($tmp,&BP(-128,$te,$tmp,1));
+			&shl	($tmp,8);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$__s0);		}##%ebx
+	else        {	&mov	($tmp,$s[2]);
+			&shr	($tmp,16);			}
+	if ($i==2)  {	&and	($s[1],0xFF);			}#%edx[2]
+			&and	($tmp,0xFF);
+			&movz	($tmp,&BP(-128,$te,$tmp,1));
+			&shl	($tmp,16);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}##%ecx
+	elsif($i==2){	&movz	($tmp,&HB($s[3]));		}#%ebx[2]
+	else        {	&mov	($tmp,$s[3]);
+			&shr	($tmp,24);			}
+			&movz	($tmp,&BP(-128,$te,$tmp,1));
+			&shl	($tmp,24);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$acc);			}
+	&comment();
+}
+
+sub enctransform()
+{ my @s = ($s0,$s1,$s2,$s3);
+  my $i = shift;
+  my $tmp = $tbl;
+  my $r2  = $key ;
+
+	&and	($tmp,$s[$i]);
+	&lea	($r2,&DWP(0,$s[$i],$s[$i]));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&and	($r2,0xfefefefe);
+	&sub	($acc,$tmp);
+	&mov	($tmp,$s[$i]);
+	&and	($acc,0x1b1b1b1b);
+	&rotr	($tmp,16);
+	&xor	($acc,$r2);	# r2
+	&mov	($r2,$s[$i]);
+
+	&xor	($s[$i],$acc);	# r0 ^ r2
+	&rotr	($r2,16+8);
+	&xor	($acc,$tmp);
+	&rotl	($s[$i],24);
+	&xor	($acc,$r2);
+	&mov	($tmp,0x80808080)	if ($i!=1);
+	&xor	($s[$i],$acc);	# ROTATE(r2^r0,24) ^ r2
+}
+
+&function_begin_B("_x86_AES_encrypt_compact");
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	# prefetch Te4
+	&mov	($key,&DWP(0-128,$tbl));
+	&mov	($acc,&DWP(32-128,$tbl));
+	&mov	($key,&DWP(64-128,$tbl));
+	&mov	($acc,&DWP(96-128,$tbl));
+	&mov	($key,&DWP(128-128,$tbl));
+	&mov	($acc,&DWP(160-128,$tbl));
+	&mov	($key,&DWP(192-128,$tbl));
+	&mov	($acc,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+
+		&enccompact(0,$tbl,$s0,$s1,$s2,$s3,1);
+		&enccompact(1,$tbl,$s1,$s2,$s3,$s0,1);
+		&enccompact(2,$tbl,$s2,$s3,$s0,$s1,1);
+		&enccompact(3,$tbl,$s3,$s0,$s1,$s2,1);
+		&mov	($tbl,0x80808080);
+		&enctransform(2);
+		&enctransform(3);
+		&enctransform(0);
+		&enctransform(1);
+		&mov 	($key,$__key);
+		&mov	($tbl,$__tbl);
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+	&cmp	($key,$__end);
+	&mov	($__key,$key);
+	&jb	(&label("loop"));
+
+	&enccompact(0,$tbl,$s0,$s1,$s2,$s3);
+	&enccompact(1,$tbl,$s1,$s2,$s3,$s0);
+	&enccompact(2,$tbl,$s2,$s3,$s0,$s1);
+	&enccompact(3,$tbl,$s3,$s0,$s1,$s2);
+
+	&xor	($s0,&DWP(16,$key));
+	&xor	($s1,&DWP(20,$key));
+	&xor	($s2,&DWP(24,$key));
+	&xor	($s3,&DWP(28,$key));
+
+	&ret	();
+&function_end_B("_x86_AES_encrypt_compact");
+
+######################################################################
+# "Compact" SSE block function.
+######################################################################
+#
+# Performance is not actually extraordinary in comparison to pure
+# x86 code. In particular encrypt performance is virtually the same.
+# Decrypt performance on the other hand is 15-20% better on newer
+# µ-archs [but we're thankful for *any* improvement here], and ~50%
+# better on PIII:-) And additionally on the pros side this code
+# eliminates redundant references to stack and thus relieves/
+# minimizes the pressure on the memory bus.
+#
+# MMX register layout                           lsb
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# |          mm4          |          mm0          |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# |     s3    |     s2    |     s1    |     s0    |    
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+#
+# Indexes translate as s[N/4]>>(8*(N%4)), e.g. 5 means s1>>8.
+# In this terms encryption and decryption "compact" permutation
+# matrices can be depicted as following:
+#
+# encryption              lsb	# decryption              lsb
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t0 || 15 | 10 |  5 |  0 |	# | t0 ||  7 | 10 | 13 |  0 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t1 ||  3 | 14 |  9 |  4 |	# | t1 || 11 | 14 |  1 |  4 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t2 ||  7 |  2 | 13 |  8 |	# | t2 || 15 |  2 |  5 |  8 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t3 || 11 |  6 |  1 | 12 |	# | t3 ||  3 |  6 |  9 | 12 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+#
+######################################################################
+# Why not xmm registers? Short answer. It was actually tested and
+# was not any faster, but *contrary*, most notably on Intel CPUs.
+# Longer answer. Main advantage of using mm registers is that movd
+# latency is lower, especially on Intel P4. While arithmetic
+# instructions are twice as many, they can be scheduled every cycle
+# and not every second one when they are operating on xmm register,
+# so that "arithmetic throughput" remains virtually the same. And
+# finally the code can be executed even on elder SSE-only CPUs:-)
+
+sub sse_enccompact()
+{
+	&pshufw	("mm1","mm0",0x08);		#  5, 4, 1, 0
+	&pshufw	("mm5","mm4",0x0d);		# 15,14,11,10
+	&movd	("eax","mm1");			#  5, 4, 1, 0
+	&movd	("ebx","mm5");			# 15,14,11,10
+	&mov	($__key,$key);
+
+	&movz	($acc,&LB("eax"));		#  0
+	&movz	("edx",&HB("eax"));		#  1
+	&pshufw	("mm2","mm0",0x0d);		#  7, 6, 3, 2
+	&movz	("ecx",&BP(-128,$tbl,$acc,1));	#  0
+	&movz	($key,&LB("ebx"));		# 10
+	&movz	("edx",&BP(-128,$tbl,"edx",1));	#  1
+	&shr	("eax",16);			#  5, 4
+	&shl	("edx",8);			#  1
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 10
+	&movz	($key,&HB("ebx"));		# 11
+	&shl	($acc,16);			# 10
+	&pshufw	("mm6","mm4",0x08);		# 13,12, 9, 8
+	&or	("ecx",$acc);			# 10
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 11
+	&movz	($key,&HB("eax"));		#  5
+	&shl	($acc,24);			# 11
+	&shr	("ebx",16);			# 15,14
+	&or	("edx",$acc);			# 11
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  5
+	&movz	($key,&HB("ebx"));		# 15
+	&shl	($acc,8);			#  5
+	&or	("ecx",$acc);			#  5
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 15
+	&movz	($key,&LB("eax"));		#  4
+	&shl	($acc,24);			# 15
+	&or	("ecx",$acc);			# 15
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  4
+	&movz	($key,&LB("ebx"));		# 14
+	&movd	("eax","mm2");			#  7, 6, 3, 2
+	&movd	("mm0","ecx");			# t[0] collected
+	&movz	("ecx",&BP(-128,$tbl,$key,1));	# 14
+	&movz	($key,&HB("eax"));		#  3
+	&shl	("ecx",16);			# 14
+	&movd	("ebx","mm6");			# 13,12, 9, 8
+	&or	("ecx",$acc);			# 14
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  3
+	&movz	($key,&HB("ebx"));		#  9
+	&shl	($acc,24);			#  3
+	&or	("ecx",$acc);			#  3
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  9
+	&movz	($key,&LB("ebx"));		#  8
+	&shl	($acc,8);			#  9
+	&shr	("ebx",16);			# 13,12
+	&or	("ecx",$acc);			#  9
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  8
+	&movz	($key,&LB("eax"));		#  2
+	&shr	("eax",16);			#  7, 6
+	&movd	("mm1","ecx");			# t[1] collected
+	&movz	("ecx",&BP(-128,$tbl,$key,1));	#  2
+	&movz	($key,&HB("eax"));		#  7
+	&shl	("ecx",16);			#  2
+	&and	("eax",0xff);			#  6
+	&or	("ecx",$acc);			#  2
+
+	&punpckldq	("mm0","mm1");		# t[0,1] collected
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  7
+	&movz	($key,&HB("ebx"));		# 13
+	&shl	($acc,24);			#  7
+	&and	("ebx",0xff);			# 12
+	&movz	("eax",&BP(-128,$tbl,"eax",1));	#  6
+	&or	("ecx",$acc);			#  7
+	&shl	("eax",16);			#  6
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 13
+	&or	("edx","eax");			#  6
+	&shl	($acc,8);			# 13
+	&movz	("ebx",&BP(-128,$tbl,"ebx",1));	# 12
+	&or	("ecx",$acc);			# 13
+	&or	("edx","ebx");			# 12
+	&mov	($key,$__key);
+	&movd	("mm4","ecx");			# t[2] collected
+	&movd	("mm5","edx");			# t[3] collected
+
+	&punpckldq	("mm4","mm5");		# t[2,3] collected
+}
+
+					if (!$x86only) {
+&function_begin_B("_sse_AES_encrypt_compact");
+	&pxor	("mm0",&QWP(0,$key));	#  7, 6, 5, 4, 3, 2, 1, 0
+	&pxor	("mm4",&QWP(8,$key));	# 15,14,13,12,11,10, 9, 8
+
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	&mov	($s0,0x1b1b1b1b);		# magic constant
+	&mov	(&DWP(8,"esp"),$s0);
+	&mov	(&DWP(12,"esp"),$s0);
+
+	# prefetch Te4
+	&mov	($s0,&DWP(0-128,$tbl));
+	&mov	($s1,&DWP(32-128,$tbl));
+	&mov	($s2,&DWP(64-128,$tbl));
+	&mov	($s3,&DWP(96-128,$tbl));
+	&mov	($s0,&DWP(128-128,$tbl));
+	&mov	($s1,&DWP(160-128,$tbl));
+	&mov	($s2,&DWP(192-128,$tbl));
+	&mov	($s3,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+		&sse_enccompact();
+		&add	($key,16);
+		&cmp	($key,$__end);
+		&ja	(&label("out"));
+
+		&movq	("mm2",&QWP(8,"esp"));
+		&pxor	("mm3","mm3");		&pxor	("mm7","mm7");
+		&movq	("mm1","mm0");		&movq	("mm5","mm4");	# r0
+		&pcmpgtb("mm3","mm0");		&pcmpgtb("mm7","mm4");
+		&pand	("mm3","mm2");		&pand	("mm7","mm2");
+		&pshufw	("mm2","mm0",0xb1);	&pshufw	("mm6","mm4",0xb1);# ROTATE(r0,16)
+		&paddb	("mm0","mm0");		&paddb	("mm4","mm4");
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# = r2
+		&pshufw	("mm3","mm2",0xb1);	&pshufw	("mm7","mm6",0xb1);# r0
+		&pxor	("mm1","mm0");		&pxor	("mm5","mm4");	# r0^r2
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= ROTATE(r0,16)
+
+		&movq	("mm2","mm3");		&movq	("mm6","mm7");
+		&pslld	("mm3",8);		&pslld	("mm7",8);
+		&psrld	("mm2",24);		&psrld	("mm6",24);
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= r0<<8
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= r0>>24
+
+		&movq	("mm3","mm1");		&movq	("mm7","mm5");
+		&movq	("mm2",&QWP(0,$key));	&movq	("mm6",&QWP(8,$key));
+		&psrld	("mm1",8);		&psrld	("mm5",8);
+		&mov	($s0,&DWP(0-128,$tbl));
+		&pslld	("mm3",24);		&pslld	("mm7",24);
+		&mov	($s1,&DWP(64-128,$tbl));
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= (r2^r0)<<8
+		&mov	($s2,&DWP(128-128,$tbl));
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= (r2^r0)>>24
+		&mov	($s3,&DWP(192-128,$tbl));
+
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");
+	&jmp	(&label("loop"));
+
+	&set_label("out",16);
+	&pxor	("mm0",&QWP(0,$key));
+	&pxor	("mm4",&QWP(8,$key));
+
+	&ret	();
+&function_end_B("_sse_AES_encrypt_compact");
+					}
+
+######################################################################
+# Vanilla block function.
+######################################################################
+
+sub encstep()
+{ my ($i,$te,@s) = @_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# lines marked with #%e?x[i] denote "reordered" instructions...
+	if ($i==3)  {	&mov	($key,$__key);			}##%edx
+	else        {	&mov	($out,$s[0]);
+			&and	($out,0xFF);			}
+	if ($i==1)  {	&shr	($s[0],16);			}#%ebx[1]
+	if ($i==2)  {	&shr	($s[0],24);			}#%ecx[2]
+			&mov	($out,&DWP(0,$te,$out,8));
+
+	if ($i==3)  {	$tmp=$s[1];				}##%eax
+			&movz	($tmp,&HB($s[1]));
+			&xor	($out,&DWP(3,$te,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$__s0);		}##%ebx
+	else        {	&mov	($tmp,$s[2]);
+			&shr	($tmp,16);			}
+	if ($i==2)  {	&and	($s[1],0xFF);			}#%edx[2]
+			&and	($tmp,0xFF);
+			&xor	($out,&DWP(2,$te,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}##%ecx
+	elsif($i==2){	&movz	($tmp,&HB($s[3]));		}#%ebx[2]
+	else        {	&mov	($tmp,$s[3]); 
+			&shr	($tmp,24)			}
+			&xor	($out,&DWP(1,$te,$tmp,8));
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$acc);			}
+			&comment();
+}
+
+sub enclast()
+{ my ($i,$te,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	if ($i==3)  {	&mov	($key,$__key);			}##%edx
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+	if ($i==1)  {	&shr	($s[0],16);			}#%ebx[1]
+	if ($i==2)  {	&shr	($s[0],24);			}#%ecx[2]
+			&mov	($out,&DWP(2,$te,$out,8));
+			&and	($out,0x000000ff);
+
+	if ($i==3)  {	$tmp=$s[1];				}##%eax
+			&movz	($tmp,&HB($s[1]));
+			&mov	($tmp,&DWP(0,$te,$tmp,8));
+			&and	($tmp,0x0000ff00);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$__s0);		}##%ebx
+	else        {	&mov	($tmp,$s[2]);
+			&shr	($tmp,16);			}
+	if ($i==2)  {	&and	($s[1],0xFF);			}#%edx[2]
+			&and	($tmp,0xFF);
+			&mov	($tmp,&DWP(0,$te,$tmp,8));
+			&and	($tmp,0x00ff0000);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}##%ecx
+	elsif($i==2){	&movz	($tmp,&HB($s[3]));		}#%ebx[2]
+	else        {	&mov	($tmp,$s[3]);
+			&shr	($tmp,24);			}
+			&mov	($tmp,&DWP(2,$te,$tmp,8));
+			&and	($tmp,0xff000000);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$acc);			}
+}
+
+&function_begin_B("_x86_AES_encrypt");
+	if ($vertical_spin) {
+		# I need high parts of volatile registers to be accessible...
+		&exch	($s1="edi",$key="ebx");
+		&mov	($s2="esi",$acc="ecx");
+	}
+
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+
+	if ($small_footprint) {
+	    &lea	($acc,&DWP(-2,$acc,$acc));
+	    &lea	($acc,&DWP(0,$key,$acc,8));
+	    &mov	($__end,$acc);		# end of key schedule
+
+	    &set_label("loop",16);
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+	    &cmp	($key,$__end);
+	    &mov	($__key,$key);
+	    &jb		(&label("loop"));
+	}
+	else {
+	    &cmp	($acc,10);
+	    &jle	(&label("10rounds"));
+	    &cmp	($acc,12);
+	    &jle	(&label("12rounds"));
+
+	&set_label("14rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("12rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("10rounds",4);
+	    for ($i=1;$i<10;$i++) {
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	}
+
+	if ($vertical_spin) {
+	    # "reincarnate" some registers for "horizontal" spin...
+	    &mov	($s1="ebx",$key="edi");
+	    &mov	($s2="ecx",$acc="esi");
+	}
+	&enclast(0,$tbl,$s0,$s1,$s2,$s3);
+	&enclast(1,$tbl,$s1,$s2,$s3,$s0);
+	&enclast(2,$tbl,$s2,$s3,$s0,$s1);
+	&enclast(3,$tbl,$s3,$s0,$s1,$s2);
+
+	&add	($key,$small_footprint?16:160);
+	&xor	($s0,&DWP(0,$key));
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&ret	();
+
+&set_label("AES_Te",64);	# Yes! I keep it in the code segment!
+	&_data_word(0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6);
+	&_data_word(0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591);
+	&_data_word(0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56);
+	&_data_word(0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec);
+	&_data_word(0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa);
+	&_data_word(0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb);
+	&_data_word(0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45);
+	&_data_word(0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b);
+	&_data_word(0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c);
+	&_data_word(0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83);
+	&_data_word(0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9);
+	&_data_word(0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a);
+	&_data_word(0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d);
+	&_data_word(0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f);
+	&_data_word(0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df);
+	&_data_word(0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea);
+	&_data_word(0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34);
+	&_data_word(0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b);
+	&_data_word(0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d);
+	&_data_word(0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413);
+	&_data_word(0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1);
+	&_data_word(0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6);
+	&_data_word(0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972);
+	&_data_word(0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85);
+	&_data_word(0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed);
+	&_data_word(0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511);
+	&_data_word(0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe);
+	&_data_word(0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b);
+	&_data_word(0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05);
+	&_data_word(0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1);
+	&_data_word(0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142);
+	&_data_word(0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf);
+	&_data_word(0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3);
+	&_data_word(0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e);
+	&_data_word(0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a);
+	&_data_word(0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6);
+	&_data_word(0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3);
+	&_data_word(0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b);
+	&_data_word(0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428);
+	&_data_word(0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad);
+	&_data_word(0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14);
+	&_data_word(0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8);
+	&_data_word(0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4);
+	&_data_word(0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2);
+	&_data_word(0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda);
+	&_data_word(0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949);
+	&_data_word(0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf);
+	&_data_word(0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810);
+	&_data_word(0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c);
+	&_data_word(0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697);
+	&_data_word(0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e);
+	&_data_word(0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f);
+	&_data_word(0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc);
+	&_data_word(0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c);
+	&_data_word(0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969);
+	&_data_word(0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27);
+	&_data_word(0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122);
+	&_data_word(0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433);
+	&_data_word(0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9);
+	&_data_word(0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5);
+	&_data_word(0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a);
+	&_data_word(0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0);
+	&_data_word(0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e);
+	&_data_word(0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c);
+
+#Te4	# four copies of Te4 to choose from to avoid L1 aliasing
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+#rcon:
+	&data_word(0x00000001, 0x00000002, 0x00000004, 0x00000008);
+	&data_word(0x00000010, 0x00000020, 0x00000040, 0x00000080);
+	&data_word(0x0000001b, 0x00000036, 0x00000000, 0x00000000);
+	&data_word(0x00000000, 0x00000000, 0x00000000, 0x00000000);
+&function_end_B("_x86_AES_encrypt");
+
+# void AES_encrypt (const void *inp,void *out,const AES_KEY *key);
+&function_begin("AES_encrypt");
+	&mov	($acc,&wparam(0));		# load inp
+	&mov	($key,&wparam(2));		# load key
+
+	&mov	($s0,"esp");
+	&sub	("esp",36);
+	&and	("esp",-64);			# align to cache-line
+
+	# place stack frame just "above" the key schedule
+	&lea	($s1,&DWP(-64-63,$key));
+	&sub	($s1,"esp");
+	&neg	($s1);
+	&and	($s1,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp",$s1);
+	&add	("esp",4);	# 4 is reserved for caller's return address
+	&mov	($_esp,$s0);			# save stack pointer
+
+	&call   (&label("pic_point"));          # make it PIC!
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&picmeup($s0,"OPENSSL_ia32cap_P",$tbl,&label("pic_point")) if (!$x86only);
+	&lea    ($tbl,&DWP(&label("AES_Te")."-".&label("pic_point"),$tbl));
+
+	# pick Te4 copy which can't "overlap" with stack frame or key schedule
+	&lea	($s1,&DWP(768-4,"esp"));
+	&sub	($s1,$tbl);
+	&and	($s1,0x300);
+	&lea	($tbl,&DWP(2048+128,$tbl,$s1));
+
+					if (!$x86only) {
+	&bt	(&DWP(0,$s0),25);	# check for SSE bit
+	&jnc	(&label("x86"));
+
+	&movq	("mm0",&QWP(0,$acc));
+	&movq	("mm4",&QWP(8,$acc));
+	&call	("_sse_AES_encrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&movq	(&QWP(0,$acc),"mm0");		# write output data
+	&movq	(&QWP(8,$acc),"mm4");
+	&emms	();
+	&function_end_A();
+					}
+	&set_label("x86",16);
+	&mov	($_tbl,$tbl);
+	&mov	($s0,&DWP(0,$acc));		# load input data
+	&mov	($s1,&DWP(4,$acc));
+	&mov	($s2,&DWP(8,$acc));
+	&mov	($s3,&DWP(12,$acc));
+	&call	("_x86_AES_encrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&mov	(&DWP(0,$acc),$s0);		# write output data
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+&function_end("AES_encrypt");
+
+#--------------------------------------------------------------------#
+
+######################################################################
+# "Compact" block function
+######################################################################
+
+sub deccompact()
+{ my $Fn = \&mov;
+  while ($#_>5) { pop(@_); $Fn=sub{}; }
+  my ($i,$td,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# $Fn is used in first compact round and its purpose is to
+	# void restoration of some values from stack, so that after
+	# 4xdeccompact with extra argument $key, $s0 and $s1 values
+	# are left there...
+	if($i==3)   {	&$Fn	($key,$__key);			}
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+			&movz	($out,&BP(-128,$td,$out,1));
+
+	if ($i==3)  {	$tmp=$s[1];				}
+			&movz	($tmp,&HB($s[1]));
+			&movz	($tmp,&BP(-128,$td,$tmp,1));
+			&shl	($tmp,8);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$acc);		}
+	else        {	mov	($tmp,$s[2]);			}
+			&shr	($tmp,16);
+			&and	($tmp,0xFF);
+			&movz	($tmp,&BP(-128,$td,$tmp,1));
+			&shl	($tmp,16);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &$Fn ($s[2],$__s1);		}
+	else        {	&mov	($tmp,$s[3]);			}
+			&shr	($tmp,24);
+			&movz	($tmp,&BP(-128,$td,$tmp,1));
+			&shl	($tmp,24);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&$Fn	($s[3],$__s0);			}
+}
+
+# must be called with 2,3,0,1 as argument sequence!!!
+sub dectransform()
+{ my @s = ($s0,$s1,$s2,$s3);
+  my $i = shift;
+  my $tmp = $key;
+  my $tp2 = @s[($i+2)%4]; $tp2 = @s[2] if ($i==1);
+  my $tp4 = @s[($i+3)%4]; $tp4 = @s[3] if ($i==1);
+  my $tp8 = $tbl;
+
+	&mov	($tmp,0x80808080);
+	&and	($tmp,$s[$i]);
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&lea	($tp2,&DWP(0,$s[$i],$s[$i]));
+	&sub	($acc,$tmp);
+	&and	($tp2,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	&xor	($tp2,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp2);
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&lea	($tp4,&DWP(0,$tp2,$tp2));
+	&sub	($acc,$tmp);
+	&and	($tp4,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &xor	($tp2,$s[$i]);	# tp2^tp1
+	&xor	($tp4,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp4);
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&lea	($tp8,&DWP(0,$tp4,$tp4));
+	&sub	($acc,$tmp);
+	&and	($tp8,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &xor	($tp4,$s[$i]);	# tp4^tp1
+	 &rotl	($s[$i],8);	# = ROTATE(tp1,8)
+	&xor	($tp8,$acc);
+
+	&xor	($s[$i],$tp2);
+	&xor	($tp2,$tp8);
+	&xor	($s[$i],$tp4);
+	&xor	($tp4,$tp8);
+	&rotl	($tp2,24);
+	&xor	($s[$i],$tp8);	# ^= tp8^(tp4^tp1)^(tp2^tp1)
+	&rotl	($tp4,16);
+	&xor	($s[$i],$tp2);	# ^= ROTATE(tp8^tp2^tp1,24)
+	&rotl	($tp8,8);
+	&xor	($s[$i],$tp4);	# ^= ROTATE(tp8^tp4^tp1,16)
+	 &mov	($s[0],$__s0)			if($i==2); #prefetch $s0
+	 &mov	($s[1],$__s1)			if($i==3); #prefetch $s1
+	 &mov	($s[2],$__s2)			if($i==1);
+	&xor	($s[$i],$tp8);	# ^= ROTATE(tp8,8)
+
+	&mov	($s[3],$__s3)			if($i==1);
+	&mov	(&DWP(4+4*$i,"esp"),$s[$i])	if($i>=2);
+}
+
+&function_begin_B("_x86_AES_decrypt_compact");
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	# prefetch Td4
+	&mov	($key,&DWP(0-128,$tbl));
+	&mov	($acc,&DWP(32-128,$tbl));
+	&mov	($key,&DWP(64-128,$tbl));
+	&mov	($acc,&DWP(96-128,$tbl));
+	&mov	($key,&DWP(128-128,$tbl));
+	&mov	($acc,&DWP(160-128,$tbl));
+	&mov	($key,&DWP(192-128,$tbl));
+	&mov	($acc,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+
+		&deccompact(0,$tbl,$s0,$s3,$s2,$s1,1);
+		&deccompact(1,$tbl,$s1,$s0,$s3,$s2,1);
+		&deccompact(2,$tbl,$s2,$s1,$s0,$s3,1);
+		&deccompact(3,$tbl,$s3,$s2,$s1,$s0,1);
+		&dectransform(2);
+		&dectransform(3);
+		&dectransform(0);
+		&dectransform(1);
+		&mov 	($key,$__key);
+		&mov	($tbl,$__tbl);
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+	&cmp	($key,$__end);
+	&mov	($__key,$key);
+	&jb	(&label("loop"));
+
+	&deccompact(0,$tbl,$s0,$s3,$s2,$s1);
+	&deccompact(1,$tbl,$s1,$s0,$s3,$s2);
+	&deccompact(2,$tbl,$s2,$s1,$s0,$s3);
+	&deccompact(3,$tbl,$s3,$s2,$s1,$s0);
+
+	&xor	($s0,&DWP(16,$key));
+	&xor	($s1,&DWP(20,$key));
+	&xor	($s2,&DWP(24,$key));
+	&xor	($s3,&DWP(28,$key));
+
+	&ret	();
+&function_end_B("_x86_AES_decrypt_compact");
+
+######################################################################
+# "Compact" SSE block function.
+######################################################################
+
+sub sse_deccompact()
+{
+	&pshufw	("mm1","mm0",0x0c);		#  7, 6, 1, 0
+	&pshufw	("mm5","mm4",0x09);		# 13,12,11,10
+	&movd	("eax","mm1");			#  7, 6, 1, 0
+	&movd	("ebx","mm5");			# 13,12,11,10
+	&mov	($__key,$key);
+
+	&movz	($acc,&LB("eax"));		#  0
+	&movz	("edx",&HB("eax"));		#  1
+	&pshufw	("mm2","mm0",0x06);		#  3, 2, 5, 4
+	&movz	("ecx",&BP(-128,$tbl,$acc,1));	#  0
+	&movz	($key,&LB("ebx"));		# 10
+	&movz	("edx",&BP(-128,$tbl,"edx",1));	#  1
+	&shr	("eax",16);			#  7, 6
+	&shl	("edx",8);			#  1
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 10
+	&movz	($key,&HB("ebx"));		# 11
+	&shl	($acc,16);			# 10
+	&pshufw	("mm6","mm4",0x03);		# 9, 8,15,14
+	&or	("ecx",$acc);			# 10
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 11
+	&movz	($key,&HB("eax"));		#  7
+	&shl	($acc,24);			# 11
+	&shr	("ebx",16);			# 13,12
+	&or	("edx",$acc);			# 11
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  7
+	&movz	($key,&HB("ebx"));		# 13
+	&shl	($acc,24);			#  7
+	&or	("ecx",$acc);			#  7
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 13
+	&movz	($key,&LB("eax"));		#  6
+	&shl	($acc,8);			# 13
+	&movd	("eax","mm2");			#  3, 2, 5, 4
+	&or	("ecx",$acc);			# 13
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  6
+	&movz	($key,&LB("ebx"));		# 12
+	&shl	($acc,16);			#  6
+	&movd	("ebx","mm6");			#  9, 8,15,14
+	&movd	("mm0","ecx");			# t[0] collected
+	&movz	("ecx",&BP(-128,$tbl,$key,1));	# 12
+	&movz	($key,&LB("eax"));		#  4
+	&or	("ecx",$acc);			# 12
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  4
+	&movz	($key,&LB("ebx"));		# 14
+	&or	("edx",$acc);			#  4
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 14
+	&movz	($key,&HB("eax"));		#  5
+	&shl	($acc,16);			# 14
+	&shr	("eax",16);			#  3, 2
+	&or	("edx",$acc);			# 14
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  5
+	&movz	($key,&HB("ebx"));		# 15
+	&shr	("ebx",16);			#  9, 8
+	&shl	($acc,8);			#  5
+	&movd	("mm1","edx");			# t[1] collected
+	&movz	("edx",&BP(-128,$tbl,$key,1));	# 15
+	&movz	($key,&HB("ebx"));		#  9
+	&shl	("edx",24);			# 15
+	&and	("ebx",0xff);			#  8
+	&or	("edx",$acc);			# 15
+
+	&punpckldq	("mm0","mm1");		# t[0,1] collected
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  9
+	&movz	($key,&LB("eax"));		#  2
+	&shl	($acc,8);			#  9
+	&movz	("eax",&HB("eax"));		#  3
+	&movz	("ebx",&BP(-128,$tbl,"ebx",1));	#  8
+	&or	("ecx",$acc);			#  9
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  2
+	&or	("edx","ebx");			#  8
+	&shl	($acc,16);			#  2
+	&movz	("eax",&BP(-128,$tbl,"eax",1));	#  3
+	&or	("edx",$acc);			#  2
+	&shl	("eax",24);			#  3
+	&or	("ecx","eax");			#  3
+	&mov	($key,$__key);
+	&movd	("mm4","edx");			# t[2] collected
+	&movd	("mm5","ecx");			# t[3] collected
+
+	&punpckldq	("mm4","mm5");		# t[2,3] collected
+}
+
+					if (!$x86only) {
+&function_begin_B("_sse_AES_decrypt_compact");
+	&pxor	("mm0",&QWP(0,$key));	#  7, 6, 5, 4, 3, 2, 1, 0
+	&pxor	("mm4",&QWP(8,$key));	# 15,14,13,12,11,10, 9, 8
+
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	&mov	($s0,0x1b1b1b1b);		# magic constant
+	&mov	(&DWP(8,"esp"),$s0);
+	&mov	(&DWP(12,"esp"),$s0);
+
+	# prefetch Td4
+	&mov	($s0,&DWP(0-128,$tbl));
+	&mov	($s1,&DWP(32-128,$tbl));
+	&mov	($s2,&DWP(64-128,$tbl));
+	&mov	($s3,&DWP(96-128,$tbl));
+	&mov	($s0,&DWP(128-128,$tbl));
+	&mov	($s1,&DWP(160-128,$tbl));
+	&mov	($s2,&DWP(192-128,$tbl));
+	&mov	($s3,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+		&sse_deccompact();
+		&add	($key,16);
+		&cmp	($key,$__end);
+		&ja	(&label("out"));
+
+		# ROTATE(x^y,N) == ROTATE(x,N)^ROTATE(y,N)
+		&movq	("mm3","mm0");		&movq	("mm7","mm4");
+		&movq	("mm2","mm0",1);	&movq	("mm6","mm4",1);
+		&movq	("mm1","mm0");		&movq	("mm5","mm4");
+		&pshufw	("mm0","mm0",0xb1);	&pshufw	("mm4","mm4",0xb1);# = ROTATE(tp0,16)
+		&pslld	("mm2",8);		&pslld	("mm6",8);
+		&psrld	("mm3",8);		&psrld	("mm7",8);
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= tp0<<8
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp0>>8
+		&pslld	("mm2",16);		&pslld	("mm6",16);
+		&psrld	("mm3",16);		&psrld	("mm7",16);
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= tp0<<24
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp0>>24
+
+		&movq	("mm3",&QWP(8,"esp"));
+		&pxor	("mm2","mm2");		&pxor	("mm6","mm6");
+		&pcmpgtb("mm2","mm1");		&pcmpgtb("mm6","mm5");
+		&pand	("mm2","mm3");		&pand	("mm6","mm3");
+		&paddb	("mm1","mm1");		&paddb	("mm5","mm5");
+		&pxor	("mm1","mm2");		&pxor	("mm5","mm6");	# tp2
+		&movq	("mm3","mm1");		&movq	("mm7","mm5");
+		&movq	("mm2","mm1");		&movq	("mm6","mm5");
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp2
+		&pslld	("mm3",24);		&pslld	("mm7",24);
+		&psrld	("mm2",8);		&psrld	("mm6",8);
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp2<<24
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= tp2>>8
+
+		&movq	("mm2",&QWP(8,"esp"));
+		&pxor	("mm3","mm3");		&pxor	("mm7","mm7");
+		&pcmpgtb("mm3","mm1");		&pcmpgtb("mm7","mm5");
+		&pand	("mm3","mm2");		&pand	("mm7","mm2");
+		&paddb	("mm1","mm1");		&paddb	("mm5","mm5");
+		&pxor	("mm1","mm3");		&pxor	("mm5","mm7");	# tp4
+		&pshufw	("mm3","mm1",0xb1);	&pshufw	("mm7","mm5",0xb1);
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp4
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= ROTATE(tp4,16)	
+
+		&pxor	("mm3","mm3");		&pxor	("mm7","mm7");
+		&pcmpgtb("mm3","mm1");		&pcmpgtb("mm7","mm5");
+		&pand	("mm3","mm2");		&pand	("mm7","mm2");
+		&paddb	("mm1","mm1");		&paddb	("mm5","mm5");
+		&pxor	("mm1","mm3");		&pxor	("mm5","mm7");	# tp8
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp8
+		&movq	("mm3","mm1");		&movq	("mm7","mm5");
+		&pshufw	("mm2","mm1",0xb1);	&pshufw	("mm6","mm5",0xb1);
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= ROTATE(tp8,16)
+		&pslld	("mm1",8);		&pslld	("mm5",8);
+		&psrld	("mm3",8);		&psrld	("mm7",8);
+		&movq	("mm2",&QWP(0,$key));	&movq	("mm6",&QWP(8,$key));
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp8<<8
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp8>>8
+		&mov	($s0,&DWP(0-128,$tbl));
+		&pslld	("mm1",16);		&pslld	("mm5",16);
+		&mov	($s1,&DWP(64-128,$tbl));
+		&psrld	("mm3",16);		&psrld	("mm7",16);
+		&mov	($s2,&DWP(128-128,$tbl));
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp8<<24
+		&mov	($s3,&DWP(192-128,$tbl));
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp8>>24
+
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");
+	&jmp	(&label("loop"));
+
+	&set_label("out",16);
+	&pxor	("mm0",&QWP(0,$key));
+	&pxor	("mm4",&QWP(8,$key));
+
+	&ret	();
+&function_end_B("_sse_AES_decrypt_compact");
+					}
+
+######################################################################
+# Vanilla block function.
+######################################################################
+
+sub decstep()
+{ my ($i,$td,@s) = @_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# no instructions are reordered, as performance appears
+	# optimal... or rather that all attempts to reorder didn't
+	# result in better performance [which by the way is not a
+	# bit lower than ecryption].
+	if($i==3)   {	&mov	($key,$__key);			}
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+			&mov	($out,&DWP(0,$td,$out,8));
+
+	if ($i==3)  {	$tmp=$s[1];				}
+			&movz	($tmp,&HB($s[1]));
+			&xor	($out,&DWP(3,$td,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$acc);		}
+	else        {	&mov	($tmp,$s[2]);			}
+			&shr	($tmp,16);
+			&and	($tmp,0xFF);
+			&xor	($out,&DWP(2,$td,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}
+	else        {	&mov	($tmp,$s[3]);			}
+			&shr	($tmp,24);
+			&xor	($out,&DWP(1,$td,$tmp,8));
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$__s0);			}
+			&comment();
+}
+
+sub declast()
+{ my ($i,$td,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	if($i==0)   {	&lea	($td,&DWP(2048+128,$td));
+			&mov	($tmp,&DWP(0-128,$td));
+			&mov	($acc,&DWP(32-128,$td));
+			&mov	($tmp,&DWP(64-128,$td));
+			&mov	($acc,&DWP(96-128,$td));
+			&mov	($tmp,&DWP(128-128,$td));
+			&mov	($acc,&DWP(160-128,$td));
+			&mov	($tmp,&DWP(192-128,$td));
+			&mov	($acc,&DWP(224-128,$td));
+			&lea	($td,&DWP(-128,$td));		}
+	if($i==3)   {	&mov	($key,$__key);			}
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+			&movz	($out,&BP(0,$td,$out,1));
+
+	if ($i==3)  {	$tmp=$s[1];				}
+			&movz	($tmp,&HB($s[1]));
+			&movz	($tmp,&BP(0,$td,$tmp,1));
+			&shl	($tmp,8);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$acc);		}
+	else        {	mov	($tmp,$s[2]);			}
+			&shr	($tmp,16);
+			&and	($tmp,0xFF);
+			&movz	($tmp,&BP(0,$td,$tmp,1));
+			&shl	($tmp,16);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}
+	else        {	&mov	($tmp,$s[3]);			}
+			&shr	($tmp,24);
+			&movz	($tmp,&BP(0,$td,$tmp,1));
+			&shl	($tmp,24);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$__s0);
+			&lea	($td,&DWP(-2048,$td));		}
+}
+
+&function_begin_B("_x86_AES_decrypt");
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+
+	if ($small_footprint) {
+	    &lea	($acc,&DWP(-2,$acc,$acc));
+	    &lea	($acc,&DWP(0,$key,$acc,8));
+	    &mov	($__end,$acc);		# end of key schedule
+	    &set_label("loop",16);
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+	    &cmp	($key,$__end);
+	    &mov	($__key,$key);
+	    &jb		(&label("loop"));
+	}
+	else {
+	    &cmp	($acc,10);
+	    &jle	(&label("10rounds"));
+	    &cmp	($acc,12);
+	    &jle	(&label("12rounds"));
+
+	&set_label("14rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("12rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("10rounds",4);
+	    for ($i=1;$i<10;$i++) {
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	}
+
+	&declast(0,$tbl,$s0,$s3,$s2,$s1);
+	&declast(1,$tbl,$s1,$s0,$s3,$s2);
+	&declast(2,$tbl,$s2,$s1,$s0,$s3);
+	&declast(3,$tbl,$s3,$s2,$s1,$s0);
+
+	&add	($key,$small_footprint?16:160);
+	&xor	($s0,&DWP(0,$key));
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&ret	();
+
+&set_label("AES_Td",64);	# Yes! I keep it in the code segment!
+	&_data_word(0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a);
+	&_data_word(0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b);
+	&_data_word(0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5);
+	&_data_word(0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5);
+	&_data_word(0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d);
+	&_data_word(0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b);
+	&_data_word(0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295);
+	&_data_word(0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e);
+	&_data_word(0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927);
+	&_data_word(0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d);
+	&_data_word(0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362);
+	&_data_word(0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9);
+	&_data_word(0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52);
+	&_data_word(0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566);
+	&_data_word(0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3);
+	&_data_word(0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed);
+	&_data_word(0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e);
+	&_data_word(0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4);
+	&_data_word(0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4);
+	&_data_word(0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd);
+	&_data_word(0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d);
+	&_data_word(0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060);
+	&_data_word(0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967);
+	&_data_word(0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879);
+	&_data_word(0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000);
+	&_data_word(0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c);
+	&_data_word(0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36);
+	&_data_word(0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624);
+	&_data_word(0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b);
+	&_data_word(0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c);
+	&_data_word(0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12);
+	&_data_word(0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14);
+	&_data_word(0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3);
+	&_data_word(0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b);
+	&_data_word(0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8);
+	&_data_word(0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684);
+	&_data_word(0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7);
+	&_data_word(0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177);
+	&_data_word(0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947);
+	&_data_word(0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322);
+	&_data_word(0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498);
+	&_data_word(0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f);
+	&_data_word(0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54);
+	&_data_word(0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382);
+	&_data_word(0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf);
+	&_data_word(0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb);
+	&_data_word(0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83);
+	&_data_word(0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef);
+	&_data_word(0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029);
+	&_data_word(0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235);
+	&_data_word(0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733);
+	&_data_word(0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117);
+	&_data_word(0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4);
+	&_data_word(0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546);
+	&_data_word(0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb);
+	&_data_word(0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d);
+	&_data_word(0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb);
+	&_data_word(0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a);
+	&_data_word(0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773);
+	&_data_word(0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478);
+	&_data_word(0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2);
+	&_data_word(0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff);
+	&_data_word(0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664);
+	&_data_word(0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0);
+
+#Td4:	# four copies of Td4 to choose from to avoid L1 aliasing
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+&function_end_B("_x86_AES_decrypt");
+
+# void AES_decrypt (const void *inp,void *out,const AES_KEY *key);
+&function_begin("AES_decrypt");
+	&mov	($acc,&wparam(0));		# load inp
+	&mov	($key,&wparam(2));		# load key
+
+	&mov	($s0,"esp");
+	&sub	("esp",36);
+	&and	("esp",-64);			# align to cache-line
+
+	# place stack frame just "above" the key schedule
+	&lea	($s1,&DWP(-64-63,$key));
+	&sub	($s1,"esp");
+	&neg	($s1);
+	&and	($s1,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp",$s1);
+	&add	("esp",4);	# 4 is reserved for caller's return address
+	&mov	($_esp,$s0);	# save stack pointer
+
+	&call   (&label("pic_point"));          # make it PIC!
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&picmeup($s0,"OPENSSL_ia32cap_P",$tbl,&label("pic_point")) if(!$x86only);
+	&lea    ($tbl,&DWP(&label("AES_Td")."-".&label("pic_point"),$tbl));
+
+	# pick Td4 copy which can't "overlap" with stack frame or key schedule
+	&lea	($s1,&DWP(768-4,"esp"));
+	&sub	($s1,$tbl);
+	&and	($s1,0x300);
+	&lea	($tbl,&DWP(2048+128,$tbl,$s1));
+
+					if (!$x86only) {
+	&bt	(&DWP(0,$s0),25);	# check for SSE bit
+	&jnc	(&label("x86"));
+
+	&movq	("mm0",&QWP(0,$acc));
+	&movq	("mm4",&QWP(8,$acc));
+	&call	("_sse_AES_decrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&movq	(&QWP(0,$acc),"mm0");		# write output data
+	&movq	(&QWP(8,$acc),"mm4");
+	&emms	();
+	&function_end_A();
+					}
+	&set_label("x86",16);
+	&mov	($_tbl,$tbl);
+	&mov	($s0,&DWP(0,$acc));		# load input data
+	&mov	($s1,&DWP(4,$acc));
+	&mov	($s2,&DWP(8,$acc));
+	&mov	($s3,&DWP(12,$acc));
+	&call	("_x86_AES_decrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&mov	(&DWP(0,$acc),$s0);		# write output data
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+&function_end("AES_decrypt");
+
+# void AES_cbc_encrypt (const void char *inp, unsigned char *out,
+#			size_t length, const AES_KEY *key,
+#			unsigned char *ivp,const int enc);
+{
+# stack frame layout
+#             -4(%esp)		# return address	 0(%esp)
+#              0(%esp)		# s0 backing store	 4(%esp)	
+#              4(%esp)		# s1 backing store	 8(%esp)
+#              8(%esp)		# s2 backing store	12(%esp)
+#             12(%esp)		# s3 backing store	16(%esp)
+#             16(%esp)		# key backup		20(%esp)
+#             20(%esp)		# end of key schedule	24(%esp)
+#             24(%esp)		# %ebp backup		28(%esp)
+#             28(%esp)		# %esp backup
+my $_inp=&DWP(32,"esp");	# copy of wparam(0)
+my $_out=&DWP(36,"esp");	# copy of wparam(1)
+my $_len=&DWP(40,"esp");	# copy of wparam(2)
+my $_key=&DWP(44,"esp");	# copy of wparam(3)
+my $_ivp=&DWP(48,"esp");	# copy of wparam(4)
+my $_tmp=&DWP(52,"esp");	# volatile variable
+#
+my $ivec=&DWP(60,"esp");	# ivec[16]
+my $aes_key=&DWP(76,"esp");	# copy of aes_key
+my $mark=&DWP(76+240,"esp");	# copy of aes_key->rounds
+
+&function_begin("AES_cbc_encrypt");
+	&mov	($s2 eq "ecx"? $s2 : "",&wparam(2));	# load len
+	&cmp	($s2,0);
+	&je	(&label("drop_out"));
+
+	&call   (&label("pic_point"));		# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&picmeup($s0,"OPENSSL_ia32cap_P",$tbl,&label("pic_point")) if(!$x86only);
+
+	&cmp	(&wparam(5),0);
+	&lea    ($tbl,&DWP(&label("AES_Te")."-".&label("pic_point"),$tbl));
+	&jne	(&label("picked_te"));
+	&lea	($tbl,&DWP(&label("AES_Td")."-".&label("AES_Te"),$tbl));
+	&set_label("picked_te");
+
+	# one can argue if this is required
+	&pushf	();
+	&cld	();
+
+	&cmp	($s2,$speed_limit);
+	&jb	(&label("slow_way"));
+	&test	($s2,15);
+	&jnz	(&label("slow_way"));
+					if (!$x86only) {
+	&bt	(&DWP(0,$s0),28);	# check for hyper-threading bit
+	&jc	(&label("slow_way"));
+					}
+	# pre-allocate aligned stack frame...
+	&lea	($acc,&DWP(-80-244,"esp"));
+	&and	($acc,-64);
+
+	# ... and make sure it doesn't alias with $tbl modulo 4096
+	&mov	($s0,$tbl);
+	&lea	($s1,&DWP(2048+256,$tbl));
+	&mov	($s3,$acc);
+	&and	($s0,0xfff);		# s = %ebp&0xfff
+	&and	($s1,0xfff);		# e = (%ebp+2048+256)&0xfff
+	&and	($s3,0xfff);		# p = %esp&0xfff
+
+	&cmp	($s3,$s1);		# if (p>=e) %esp =- (p-e);
+	&jb	(&label("tbl_break_out"));
+	&sub	($s3,$s1);
+	&sub	($acc,$s3);
+	&jmp	(&label("tbl_ok"));
+	&set_label("tbl_break_out",4);	# else %esp -= (p-s)&0xfff + framesz;
+	&sub	($s3,$s0);
+	&and	($s3,0xfff);
+	&add	($s3,384);
+	&sub	($acc,$s3);
+	&set_label("tbl_ok",4);
+
+	&lea	($s3,&wparam(0));	# obtain pointer to parameter block
+	&exch	("esp",$acc);		# allocate stack frame
+	&add	("esp",4);		# reserve for return address!
+	&mov	($_tbl,$tbl);		# save %ebp
+	&mov	($_esp,$acc);		# save %esp
+
+	&mov	($s0,&DWP(0,$s3));	# load inp
+	&mov	($s1,&DWP(4,$s3));	# load out
+	#&mov	($s2,&DWP(8,$s3));	# load len
+	&mov	($key,&DWP(12,$s3));	# load key
+	&mov	($acc,&DWP(16,$s3));	# load ivp
+	&mov	($s3,&DWP(20,$s3));	# load enc flag
+
+	&mov	($_inp,$s0);		# save copy of inp
+	&mov	($_out,$s1);		# save copy of out
+	&mov	($_len,$s2);		# save copy of len
+	&mov	($_key,$key);		# save copy of key
+	&mov	($_ivp,$acc);		# save copy of ivp
+
+	&mov	($mark,0);		# copy of aes_key->rounds = 0;
+	# do we copy key schedule to stack?
+	&mov	($s1 eq "ebx" ? $s1 : "",$key);
+	&mov	($s2 eq "ecx" ? $s2 : "",244/4);
+	&sub	($s1,$tbl);
+	&mov	("esi",$key);
+	&and	($s1,0xfff);
+	&lea	("edi",$aes_key);
+	&cmp	($s1,2048+256);
+	&jb	(&label("do_copy"));
+	&cmp	($s1,4096-244);
+	&jb	(&label("skip_copy"));
+	&set_label("do_copy",4);
+		&mov	($_key,"edi");
+		&data_word(0xA5F3F689);	# rep movsd
+	&set_label("skip_copy");
+
+	&mov	($key,16);
+	&set_label("prefetch_tbl",4);
+		&mov	($s0,&DWP(0,$tbl));
+		&mov	($s1,&DWP(32,$tbl));
+		&mov	($s2,&DWP(64,$tbl));
+		&mov	($acc,&DWP(96,$tbl));
+		&lea	($tbl,&DWP(128,$tbl));
+		&sub	($key,1);
+	&jnz	(&label("prefetch_tbl"));
+	&sub	($tbl,2048);
+
+	&mov	($acc,$_inp);
+	&mov	($key,$_ivp);
+
+	&cmp	($s3,0);
+	&je	(&label("fast_decrypt"));
+
+#----------------------------- ENCRYPT -----------------------------#
+	&mov	($s0,&DWP(0,$key));		# load iv
+	&mov	($s1,&DWP(4,$key));
+
+	&set_label("fast_enc_loop",16);
+		&mov	($s2,&DWP(8,$key));
+		&mov	($s3,&DWP(12,$key));
+
+		&xor	($s0,&DWP(0,$acc));	# xor input data
+		&xor	($s1,&DWP(4,$acc));
+		&xor	($s2,&DWP(8,$acc));
+		&xor	($s3,&DWP(12,$acc));
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_encrypt");
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+
+		&mov	(&DWP(0,$key),$s0);	# save output data
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($s2,$_len);		# load len
+		&mov	($_inp,$acc);		# save inp
+		&lea	($s3,&DWP(16,$key));	# advance out
+		&mov	($_out,$s3);		# save out
+		&sub	($s2,16);		# decrease len
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("fast_enc_loop"));
+	&mov	($acc,$_ivp);		# load ivp
+	&mov	($s2,&DWP(8,$key));	# restore last 2 dwords
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$acc),$s0);	# save ivec
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+
+	&cmp	($mark,0);		# was the key schedule copied?
+	&mov	("edi",$_key);
+	&je	(&label("skip_ezero"));
+	# zero copy of key schedule
+	&mov	("ecx",240/4);
+	&xor	("eax","eax");
+	&align	(4);
+	&data_word(0xABF3F689);		# rep stosd
+	&set_label("skip_ezero");
+	&mov	("esp",$_esp);
+	&popf	();
+    &set_label("drop_out");
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+#----------------------------- DECRYPT -----------------------------#
+&set_label("fast_decrypt",16);
+
+	&cmp	($acc,$_out);
+	&je	(&label("fast_dec_in_place"));	# in-place processing...
+
+	&mov	($_tmp,$key);
+
+	&align	(4);
+	&set_label("fast_dec_loop",16);
+		&mov	($s0,&DWP(0,$acc));	# read input
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_decrypt");
+
+		&mov	($key,$_tmp);		# load ivp
+		&mov	($acc,$_len);		# load len
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&mov	($key,$_out);		# load out
+		&mov	($acc,$_inp);		# load inp
+
+		&mov	(&DWP(0,$key),$s0);	# write output
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($s2,$_len);		# load len
+		&mov	($_tmp,$acc);		# save ivp
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&lea	($key,&DWP(16,$key));	# advance out
+		&mov	($_out,$key);		# save out
+		&sub	($s2,16);		# decrease len
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("fast_dec_loop"));
+	&mov	($key,$_tmp);		# load temp ivp
+	&mov	($acc,$_ivp);		# load user ivp
+	&mov	($s0,&DWP(0,$key));	# load iv
+	&mov	($s1,&DWP(4,$key));
+	&mov	($s2,&DWP(8,$key));
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$acc),$s0);	# copy back to user
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+	&jmp	(&label("fast_dec_out"));
+
+    &set_label("fast_dec_in_place",16);
+	&set_label("fast_dec_in_place_loop");
+		&mov	($s0,&DWP(0,$acc));	# read input
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&lea	($key,$ivec);
+		&mov	(&DWP(0,$key),$s0);	# copy to temp
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_decrypt");
+
+		&mov	($key,$_ivp);		# load ivp
+		&mov	($acc,$_out);		# load out
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&mov	(&DWP(0,$acc),$s0);	# write output
+		&mov	(&DWP(4,$acc),$s1);
+		&mov	(&DWP(8,$acc),$s2);
+		&mov	(&DWP(12,$acc),$s3);
+
+		&lea	($acc,&DWP(16,$acc));	# advance out
+		&mov	($_out,$acc);		# save out
+
+		&lea	($acc,$ivec);
+		&mov	($s0,&DWP(0,$acc));	# read temp
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&mov	(&DWP(0,$key),$s0);	# copy iv
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($s2,$_len);		# load len
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&sub	($s2,16);		# decrease len
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("fast_dec_in_place_loop"));
+
+    &set_label("fast_dec_out",4);
+	&cmp	($mark,0);		# was the key schedule copied?
+	&mov	("edi",$_key);
+	&je	(&label("skip_dzero"));
+	# zero copy of key schedule
+	&mov	("ecx",240/4);
+	&xor	("eax","eax");
+	&align	(4);
+	&data_word(0xABF3F689);		# rep stosd
+	&set_label("skip_dzero");
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+#--------------------------- SLOW ROUTINE ---------------------------#
+&set_label("slow_way",16);
+
+	&mov	($s0,&DWP(0,$s0)) if (!$x86only);# load OPENSSL_ia32cap
+	&mov	($key,&wparam(3));	# load key
+
+	# pre-allocate aligned stack frame...
+	&lea	($acc,&DWP(-80,"esp"));
+	&and	($acc,-64);
+
+	# ... and make sure it doesn't alias with $key modulo 1024
+	&lea	($s1,&DWP(-80-63,$key));
+	&sub	($s1,$acc);
+	&neg	($s1);
+	&and	($s1,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	($acc,$s1);
+
+	# pick S-box copy which can't overlap with stack frame or $key
+	&lea	($s1,&DWP(768,$acc));
+	&sub	($s1,$tbl);
+	&and	($s1,0x300);
+	&lea	($tbl,&DWP(2048+128,$tbl,$s1));
+
+	&lea	($s3,&wparam(0));	# pointer to parameter block
+
+	&exch	("esp",$acc);
+	&add	("esp",4);		# reserve for return address!
+	&mov	($_tbl,$tbl);		# save %ebp
+	&mov	($_esp,$acc);		# save %esp
+	&mov	($_tmp,$s0);		# save OPENSSL_ia32cap
+
+	&mov	($s0,&DWP(0,$s3));	# load inp
+	&mov	($s1,&DWP(4,$s3));	# load out
+	#&mov	($s2,&DWP(8,$s3));	# load len
+	#&mov	($key,&DWP(12,$s3));	# load key
+	&mov	($acc,&DWP(16,$s3));	# load ivp
+	&mov	($s3,&DWP(20,$s3));	# load enc flag
+
+	&mov	($_inp,$s0);		# save copy of inp
+	&mov	($_out,$s1);		# save copy of out
+	&mov	($_len,$s2);		# save copy of len
+	&mov	($_key,$key);		# save copy of key
+	&mov	($_ivp,$acc);		# save copy of ivp
+
+	&mov	($key,$acc);
+	&mov	($acc,$s0);
+
+	&cmp	($s3,0);
+	&je	(&label("slow_decrypt"));
+
+#--------------------------- SLOW ENCRYPT ---------------------------#
+	&cmp	($s2,16);
+	&mov	($s3,$s1);
+	&jb	(&label("slow_enc_tail"));
+
+					if (!$x86only) {
+	&bt	($_tmp,25);		# check for SSE bit
+	&jnc	(&label("slow_enc_x86"));
+
+	&movq	("mm0",&QWP(0,$key));	# load iv
+	&movq	("mm4",&QWP(8,$key));
+
+	&set_label("slow_enc_loop_sse",16);
+		&pxor	("mm0",&QWP(0,$acc));	# xor input data
+		&pxor	("mm4",&QWP(8,$acc));
+
+		&mov	($key,$_key);
+		&call	("_sse_AES_encrypt_compact");
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+		&mov	($s2,$_len);		# load len
+
+		&movq	(&QWP(0,$key),"mm0");	# save output data
+		&movq	(&QWP(8,$key),"mm4");
+
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&lea	($s3,&DWP(16,$key));	# advance out
+		&mov	($_out,$s3);		# save out
+		&sub	($s2,16);		# decrease len
+		&cmp	($s2,16);
+		&mov	($_len,$s2);		# save len
+	&jae	(&label("slow_enc_loop_sse"));
+	&test	($s2,15);
+	&jnz	(&label("slow_enc_tail"));
+	&mov	($acc,$_ivp);		# load ivp
+	&movq	(&QWP(0,$acc),"mm0");	# save ivec
+	&movq	(&QWP(8,$acc),"mm4");
+	&emms	();
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+					}
+    &set_label("slow_enc_x86",16);
+	&mov	($s0,&DWP(0,$key));	# load iv
+	&mov	($s1,&DWP(4,$key));
+
+	&set_label("slow_enc_loop_x86",4);
+		&mov	($s2,&DWP(8,$key));
+		&mov	($s3,&DWP(12,$key));
+
+		&xor	($s0,&DWP(0,$acc));	# xor input data
+		&xor	($s1,&DWP(4,$acc));
+		&xor	($s2,&DWP(8,$acc));
+		&xor	($s3,&DWP(12,$acc));
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_encrypt_compact");
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+
+		&mov	(&DWP(0,$key),$s0);	# save output data
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($s2,$_len);		# load len
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&lea	($s3,&DWP(16,$key));	# advance out
+		&mov	($_out,$s3);		# save out
+		&sub	($s2,16);		# decrease len
+		&cmp	($s2,16);
+		&mov	($_len,$s2);		# save len
+	&jae	(&label("slow_enc_loop_x86"));
+	&test	($s2,15);
+	&jnz	(&label("slow_enc_tail"));
+	&mov	($acc,$_ivp);		# load ivp
+	&mov	($s2,&DWP(8,$key));	# restore last dwords
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$acc),$s0);	# save ivec
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("slow_enc_tail",16);
+	&emms	()	if (!$x86only);
+	&mov	($key eq "edi"? $key:"",$s3);	# load out to edi
+	&mov	($s1,16);
+	&sub	($s1,$s2);
+	&cmp	($key,$acc eq "esi"? $acc:"");	# compare with inp
+	&je	(&label("enc_in_place"));
+	&align	(4);
+	&data_word(0xA4F3F689);	# rep movsb	# copy input
+	&jmp	(&label("enc_skip_in_place"));
+    &set_label("enc_in_place");
+	&lea	($key,&DWP(0,$key,$s2));
+    &set_label("enc_skip_in_place");
+	&mov	($s2,$s1);
+	&xor	($s0,$s0);
+	&align	(4);
+	&data_word(0xAAF3F689);	# rep stosb	# zero tail
+
+	&mov	($key,$_ivp);			# restore ivp
+	&mov	($acc,$s3);			# output as input
+	&mov	($s0,&DWP(0,$key));
+	&mov	($s1,&DWP(4,$key));
+	&mov	($_len,16);			# len=16
+	&jmp	(&label("slow_enc_loop_x86"));	# one more spin...
+
+#--------------------------- SLOW DECRYPT ---------------------------#
+&set_label("slow_decrypt",16);
+					if (!$x86only) {
+	&bt	($_tmp,25);		# check for SSE bit
+	&jnc	(&label("slow_dec_loop_x86"));
+
+	&set_label("slow_dec_loop_sse",4);
+		&movq	("mm0",&QWP(0,$acc));	# read input
+		&movq	("mm4",&QWP(8,$acc));
+
+		&mov	($key,$_key);
+		&call	("_sse_AES_decrypt_compact");
+
+		&mov	($acc,$_inp);		# load inp
+		&lea	($s0,$ivec);
+		&mov	($s1,$_out);		# load out
+		&mov	($s2,$_len);		# load len
+		&mov	($key,$_ivp);		# load ivp
+
+		&movq	("mm1",&QWP(0,$acc));	# re-read input
+		&movq	("mm5",&QWP(8,$acc));
+
+		&pxor	("mm0",&QWP(0,$key));	# xor iv
+		&pxor	("mm4",&QWP(8,$key));
+
+		&movq	(&QWP(0,$key),"mm1");	# copy input to iv
+		&movq	(&QWP(8,$key),"mm5");
+
+		&sub	($s2,16);		# decrease len
+		&jc	(&label("slow_dec_partial_sse"));
+
+		&movq	(&QWP(0,$s1),"mm0");	# write output
+		&movq	(&QWP(8,$s1),"mm4");
+
+		&lea	($s1,&DWP(16,$s1));	# advance out
+		&mov	($_out,$s1);		# save out
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("slow_dec_loop_sse"));
+	&emms	();
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("slow_dec_partial_sse",16);
+	&movq	(&QWP(0,$s0),"mm0");	# save output to temp
+	&movq	(&QWP(8,$s0),"mm4");
+	&emms	();
+
+	&add	($s2 eq "ecx" ? "ecx":"",16);
+	&mov	("edi",$s1);		# out
+	&mov	("esi",$s0);		# temp
+	&align	(4);
+	&data_word(0xA4F3F689);		# rep movsb # copy partial output
+
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+					}
+	&set_label("slow_dec_loop_x86",16);
+		&mov	($s0,&DWP(0,$acc));	# read input
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&lea	($key,$ivec);
+		&mov	(&DWP(0,$key),$s0);	# copy to temp
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_decrypt_compact");
+
+		&mov	($key,$_ivp);		# load ivp
+		&mov	($acc,$_len);		# load len
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&sub	($acc,16);
+		&jc	(&label("slow_dec_partial_x86"));
+
+		&mov	($_len,$acc);		# save len
+		&mov	($acc,$_out);		# load out
+
+		&mov	(&DWP(0,$acc),$s0);	# write output
+		&mov	(&DWP(4,$acc),$s1);
+		&mov	(&DWP(8,$acc),$s2);
+		&mov	(&DWP(12,$acc),$s3);
+
+		&lea	($acc,&DWP(16,$acc));	# advance out
+		&mov	($_out,$acc);		# save out
+
+		&lea	($acc,$ivec);
+		&mov	($s0,&DWP(0,$acc));	# read temp
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&mov	(&DWP(0,$key),$s0);	# copy it to iv
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($acc,$_inp);		# load inp
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+	&jnz	(&label("slow_dec_loop_x86"));
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("slow_dec_partial_x86",16);
+	&lea	($acc,$ivec);
+	&mov	(&DWP(0,$acc),$s0);	# save output to temp
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+
+	&mov	($acc,$_inp);
+	&mov	($s0,&DWP(0,$acc));	# re-read input
+	&mov	($s1,&DWP(4,$acc));
+	&mov	($s2,&DWP(8,$acc));
+	&mov	($s3,&DWP(12,$acc));
+
+	&mov	(&DWP(0,$key),$s0);	# copy it to iv
+	&mov	(&DWP(4,$key),$s1);
+	&mov	(&DWP(8,$key),$s2);
+	&mov	(&DWP(12,$key),$s3);
+
+	&mov	("ecx",$_len);
+	&mov	("edi",$_out);
+	&lea	("esi",$ivec);
+	&align	(4);
+	&data_word(0xA4F3F689);		# rep movsb # copy partial output
+
+	&mov	("esp",$_esp);
+	&popf	();
+&function_end("AES_cbc_encrypt");
+}
+
+#------------------------------------------------------------------#
+
+sub enckey()
+{
+	&movz	("esi",&LB("edx"));		# rk[i]>>0
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&movz	("esi",&HB("edx"));		# rk[i]>>8
+	&shl	("ebx",24);
+	&xor	("eax","ebx");
+
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&shr	("edx",16);
+	&movz	("esi",&LB("edx"));		# rk[i]>>16
+	&xor	("eax","ebx");
+
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&movz	("esi",&HB("edx"));		# rk[i]>>24
+	&shl	("ebx",8);
+	&xor	("eax","ebx");
+
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&shl	("ebx",16);
+	&xor	("eax","ebx");
+
+	&xor	("eax",&DWP(1024-128,$tbl,"ecx",4));	# rcon
+}
+
+&function_begin("_x86_AES_set_encrypt_key");
+	&mov	("esi",&wparam(1));		# user supplied key
+	&mov	("edi",&wparam(3));		# private key schedule
+
+	&test	("esi",-1);
+	&jz	(&label("badpointer"));
+	&test	("edi",-1);
+	&jz	(&label("badpointer"));
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&lea	($tbl,&DWP(&label("AES_Te")."-".&label("pic_point"),$tbl));
+	&lea	($tbl,&DWP(2048+128,$tbl));
+
+	# prefetch Te4
+	&mov	("eax",&DWP(0-128,$tbl));
+	&mov	("ebx",&DWP(32-128,$tbl));
+	&mov	("ecx",&DWP(64-128,$tbl));
+	&mov	("edx",&DWP(96-128,$tbl));
+	&mov	("eax",&DWP(128-128,$tbl));
+	&mov	("ebx",&DWP(160-128,$tbl));
+	&mov	("ecx",&DWP(192-128,$tbl));
+	&mov	("edx",&DWP(224-128,$tbl));
+
+	&mov	("ecx",&wparam(2));		# number of bits in key
+	&cmp	("ecx",128);
+	&je	(&label("10rounds"));
+	&cmp	("ecx",192);
+	&je	(&label("12rounds"));
+	&cmp	("ecx",256);
+	&je	(&label("14rounds"));
+	&mov	("eax",-2);			# invalid number of bits
+	&jmp	(&label("exit"));
+
+    &set_label("10rounds");
+	&mov	("eax",&DWP(0,"esi"));		# copy first 4 dwords
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+
+	&xor	("ecx","ecx");
+	&jmp	(&label("10shortcut"));
+
+	&align	(4);
+	&set_label("10loop");
+		&mov	("eax",&DWP(0,"edi"));		# rk[0]
+		&mov	("edx",&DWP(12,"edi"));		# rk[3]
+	&set_label("10shortcut");
+		&enckey	();
+
+		&mov	(&DWP(16,"edi"),"eax");		# rk[4]
+		&xor	("eax",&DWP(4,"edi"));
+		&mov	(&DWP(20,"edi"),"eax");		# rk[5]
+		&xor	("eax",&DWP(8,"edi"));
+		&mov	(&DWP(24,"edi"),"eax");		# rk[6]
+		&xor	("eax",&DWP(12,"edi"));
+		&mov	(&DWP(28,"edi"),"eax");		# rk[7]
+		&inc	("ecx");
+		&add	("edi",16);
+		&cmp	("ecx",10);
+	&jl	(&label("10loop"));
+
+	&mov	(&DWP(80,"edi"),10);		# setup number of rounds
+	&xor	("eax","eax");
+	&jmp	(&label("exit"));
+		
+    &set_label("12rounds");
+	&mov	("eax",&DWP(0,"esi"));		# copy first 6 dwords
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+	&mov	("ecx",&DWP(16,"esi"));
+	&mov	("edx",&DWP(20,"esi"));
+	&mov	(&DWP(16,"edi"),"ecx");
+	&mov	(&DWP(20,"edi"),"edx");
+
+	&xor	("ecx","ecx");
+	&jmp	(&label("12shortcut"));
+
+	&align	(4);
+	&set_label("12loop");
+		&mov	("eax",&DWP(0,"edi"));		# rk[0]
+		&mov	("edx",&DWP(20,"edi"));		# rk[5]
+	&set_label("12shortcut");
+		&enckey	();
+
+		&mov	(&DWP(24,"edi"),"eax");		# rk[6]
+		&xor	("eax",&DWP(4,"edi"));
+		&mov	(&DWP(28,"edi"),"eax");		# rk[7]
+		&xor	("eax",&DWP(8,"edi"));
+		&mov	(&DWP(32,"edi"),"eax");		# rk[8]
+		&xor	("eax",&DWP(12,"edi"));
+		&mov	(&DWP(36,"edi"),"eax");		# rk[9]
+
+		&cmp	("ecx",7);
+		&je	(&label("12break"));
+		&inc	("ecx");
+
+		&xor	("eax",&DWP(16,"edi"));
+		&mov	(&DWP(40,"edi"),"eax");		# rk[10]
+		&xor	("eax",&DWP(20,"edi"));
+		&mov	(&DWP(44,"edi"),"eax");		# rk[11]
+
+		&add	("edi",24);
+	&jmp	(&label("12loop"));
+
+	&set_label("12break");
+	&mov	(&DWP(72,"edi"),12);		# setup number of rounds
+	&xor	("eax","eax");
+	&jmp	(&label("exit"));
+
+    &set_label("14rounds");
+	&mov	("eax",&DWP(0,"esi"));		# copy first 8 dwords
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+	&mov	("eax",&DWP(16,"esi"));
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("edx",&DWP(28,"esi"));
+	&mov	(&DWP(16,"edi"),"eax");
+	&mov	(&DWP(20,"edi"),"ebx");
+	&mov	(&DWP(24,"edi"),"ecx");
+	&mov	(&DWP(28,"edi"),"edx");
+
+	&xor	("ecx","ecx");
+	&jmp	(&label("14shortcut"));
+
+	&align	(4);
+	&set_label("14loop");
+		&mov	("edx",&DWP(28,"edi"));		# rk[7]
+	&set_label("14shortcut");
+		&mov	("eax",&DWP(0,"edi"));		# rk[0]
+
+		&enckey	();
+
+		&mov	(&DWP(32,"edi"),"eax");		# rk[8]
+		&xor	("eax",&DWP(4,"edi"));
+		&mov	(&DWP(36,"edi"),"eax");		# rk[9]
+		&xor	("eax",&DWP(8,"edi"));
+		&mov	(&DWP(40,"edi"),"eax");		# rk[10]
+		&xor	("eax",&DWP(12,"edi"));
+		&mov	(&DWP(44,"edi"),"eax");		# rk[11]
+
+		&cmp	("ecx",6);
+		&je	(&label("14break"));
+		&inc	("ecx");
+
+		&mov	("edx","eax");
+		&mov	("eax",&DWP(16,"edi"));		# rk[4]
+		&movz	("esi",&LB("edx"));		# rk[11]>>0
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&movz	("esi",&HB("edx"));		# rk[11]>>8
+		&xor	("eax","ebx");
+
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&shr	("edx",16);
+		&shl	("ebx",8);
+		&movz	("esi",&LB("edx"));		# rk[11]>>16
+		&xor	("eax","ebx");
+
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&movz	("esi",&HB("edx"));		# rk[11]>>24
+		&shl	("ebx",16);
+		&xor	("eax","ebx");
+
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&shl	("ebx",24);
+		&xor	("eax","ebx");
+
+		&mov	(&DWP(48,"edi"),"eax");		# rk[12]
+		&xor	("eax",&DWP(20,"edi"));
+		&mov	(&DWP(52,"edi"),"eax");		# rk[13]
+		&xor	("eax",&DWP(24,"edi"));
+		&mov	(&DWP(56,"edi"),"eax");		# rk[14]
+		&xor	("eax",&DWP(28,"edi"));
+		&mov	(&DWP(60,"edi"),"eax");		# rk[15]
+
+		&add	("edi",32);
+	&jmp	(&label("14loop"));
+
+	&set_label("14break");
+	&mov	(&DWP(48,"edi"),14);		# setup number of rounds
+	&xor	("eax","eax");
+	&jmp	(&label("exit"));
+
+    &set_label("badpointer");
+	&mov	("eax",-1);
+    &set_label("exit");
+&function_end("_x86_AES_set_encrypt_key");
+
+# int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+#                        AES_KEY *key)
+&function_begin_B("AES_set_encrypt_key");
+	&call	("_x86_AES_set_encrypt_key");
+	&ret	();
+&function_end_B("AES_set_encrypt_key");
+
+sub deckey()
+{ my ($i,$key,$tp1,$tp2,$tp4,$tp8) = @_;
+  my $tmp = $tbl;
+
+	&mov	($tmp,0x80808080);
+	&and	($tmp,$tp1);
+	&lea	($tp2,&DWP(0,$tp1,$tp1));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&sub	($acc,$tmp);
+	&and	($tp2,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	&xor	($tp2,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp2);
+	&lea	($tp4,&DWP(0,$tp2,$tp2));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&sub	($acc,$tmp);
+	&and	($tp4,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &xor	($tp2,$tp1);	# tp2^tp1
+	&xor	($tp4,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp4);
+	&lea	($tp8,&DWP(0,$tp4,$tp4));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	 &xor	($tp4,$tp1);	# tp4^tp1
+	&sub	($acc,$tmp);
+	&and	($tp8,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &rotl	($tp1,8);	# = ROTATE(tp1,8)
+	&xor	($tp8,$acc);
+
+	&mov	($tmp,&DWP(4*($i+1),$key));	# modulo-scheduled load
+
+	&xor	($tp1,$tp2);
+	&xor	($tp2,$tp8);
+	&xor	($tp1,$tp4);
+	&rotl	($tp2,24);
+	&xor	($tp4,$tp8);
+	&xor	($tp1,$tp8);	# ^= tp8^(tp4^tp1)^(tp2^tp1)
+	&rotl	($tp4,16);
+	&xor	($tp1,$tp2);	# ^= ROTATE(tp8^tp2^tp1,24)
+	&rotl	($tp8,8);
+	&xor	($tp1,$tp4);	# ^= ROTATE(tp8^tp4^tp1,16)
+	&mov	($tp2,$tmp);
+	&xor	($tp1,$tp8);	# ^= ROTATE(tp8,8)
+
+	&mov	(&DWP(4*$i,$key),$tp1);
+}
+
+# int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+#                        AES_KEY *key)
+&function_begin_B("AES_set_decrypt_key");
+	&call	("_x86_AES_set_encrypt_key");
+	&cmp	("eax",0);
+	&je	(&label("proceed"));
+	&ret	();
+
+    &set_label("proceed");
+	&push	("ebp");
+	&push	("ebx");
+	&push	("esi");
+	&push	("edi");
+
+	&mov	("esi",&wparam(2));
+	&mov	("ecx",&DWP(240,"esi"));	# pull number of rounds
+	&lea	("ecx",&DWP(0,"","ecx",4));
+	&lea	("edi",&DWP(0,"esi","ecx",4));	# pointer to last chunk
+
+	&set_label("invert",4);			# invert order of chunks
+		&mov	("eax",&DWP(0,"esi"));
+		&mov	("ebx",&DWP(4,"esi"));
+		&mov	("ecx",&DWP(0,"edi"));
+		&mov	("edx",&DWP(4,"edi"));
+		&mov	(&DWP(0,"edi"),"eax");
+		&mov	(&DWP(4,"edi"),"ebx");
+		&mov	(&DWP(0,"esi"),"ecx");
+		&mov	(&DWP(4,"esi"),"edx");
+		&mov	("eax",&DWP(8,"esi"));
+		&mov	("ebx",&DWP(12,"esi"));
+		&mov	("ecx",&DWP(8,"edi"));
+		&mov	("edx",&DWP(12,"edi"));
+		&mov	(&DWP(8,"edi"),"eax");
+		&mov	(&DWP(12,"edi"),"ebx");
+		&mov	(&DWP(8,"esi"),"ecx");
+		&mov	(&DWP(12,"esi"),"edx");
+		&add	("esi",16);
+		&sub	("edi",16);
+		&cmp	("esi","edi");
+	&jne	(&label("invert"));
+
+	&mov	($key,&wparam(2));
+	&mov	($acc,&DWP(240,$key));		# pull number of rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	(&wparam(2),$acc);
+
+	&mov	($s0,&DWP(16,$key));		# modulo-scheduled load
+	&set_label("permute",4);		# permute the key schedule
+		&add	($key,16);
+		&deckey	(0,$key,$s0,$s1,$s2,$s3);
+		&deckey	(1,$key,$s1,$s2,$s3,$s0);
+		&deckey	(2,$key,$s2,$s3,$s0,$s1);
+		&deckey	(3,$key,$s3,$s0,$s1,$s2);
+		&cmp	($key,&wparam(2));
+	&jb	(&label("permute"));
+
+	&xor	("eax","eax");			# return success
+&function_end("AES_set_decrypt_key");
+&asciz("AES for x86, CRYPTOGAMS by <appro\@openssl.org>");
+
+&asm_finish();
diff --git a/crypto/aes/asm/aes-armv4.pl b/crypto/aes/asm/aes-armv4.pl
new file mode 100644
index 0000000..38a3f6a
--- /dev/null
+++ b/crypto/aes/asm/aes-armv4.pl
@@ -0,0 +1,1219 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# AES for ARMv4
+
+# January 2007.
+#
+# Code uses single 1K S-box and is >2 times faster than code generated
+# by gcc-3.4.1. This is thanks to unique feature of ARMv4 ISA, which
+# allows to merge logical or arithmetic operation with shift or rotate
+# in one instruction and emit combined result every cycle. The module
+# is endian-neutral. The performance is ~42 cycles/byte for 128-bit
+# key [on single-issue Xscale PXA250 core].
+
+# May 2007.
+#
+# AES_set_[en|de]crypt_key is added.
+
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 12% improvement on
+# Cortex A8 core and ~25 cycles per byte processed with 128-bit key.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 16%
+# improvement on Cortex A8 core and ~21.5 cycles per byte.
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$s0="r0";
+$s1="r1";
+$s2="r2";
+$s3="r3";
+$t1="r4";
+$t2="r5";
+$t3="r6";
+$i1="r7";
+$i2="r8";
+$i3="r9";
+
+$tbl="r10";
+$key="r11";
+$rounds="r12";
+
+$code=<<___;
+#if defined(__arm__)
+#ifndef __KERNEL__
+# include "arm_arch.h"
+#else
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+#endif
+
+.text
+#if __ARM_ARCH__<7
+.code	32
+#else
+.syntax	unified
+# ifdef __thumb2__
+.thumb
+# else
+.code	32
+# endif
+#endif
+
+.type	AES_Te,%object
+.align	5
+AES_Te:
+.word	0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d
+.word	0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554
+.word	0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d
+.word	0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a
+.word	0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87
+.word	0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b
+.word	0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea
+.word	0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b
+.word	0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a
+.word	0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f
+.word	0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108
+.word	0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f
+.word	0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e
+.word	0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5
+.word	0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d
+.word	0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f
+.word	0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e
+.word	0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb
+.word	0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce
+.word	0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497
+.word	0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c
+.word	0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed
+.word	0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b
+.word	0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a
+.word	0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16
+.word	0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594
+.word	0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81
+.word	0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3
+.word	0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a
+.word	0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504
+.word	0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163
+.word	0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d
+.word	0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f
+.word	0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739
+.word	0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47
+.word	0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395
+.word	0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f
+.word	0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883
+.word	0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c
+.word	0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76
+.word	0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e
+.word	0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4
+.word	0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6
+.word	0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b
+.word	0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7
+.word	0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0
+.word	0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25
+.word	0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818
+.word	0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72
+.word	0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651
+.word	0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21
+.word	0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85
+.word	0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa
+.word	0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12
+.word	0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0
+.word	0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9
+.word	0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133
+.word	0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7
+.word	0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920
+.word	0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a
+.word	0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17
+.word	0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8
+.word	0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11
+.word	0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a
+@ Te4[256]
+.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+@ rcon[]
+.word	0x01000000, 0x02000000, 0x04000000, 0x08000000
+.word	0x10000000, 0x20000000, 0x40000000, 0x80000000
+.word	0x1B000000, 0x36000000, 0, 0, 0, 0, 0, 0
+.size	AES_Te,.-AES_Te
+
+@ void AES_encrypt(const unsigned char *in, unsigned char *out,
+@ 		 const AES_KEY *key) {
+.global AES_encrypt
+.type   AES_encrypt,%function
+.align	5
+AES_encrypt:
+#if __ARM_ARCH__<7
+	sub	r3,pc,#8		@ AES_encrypt
+#else
+	adr	r3,AES_encrypt
+#endif
+	stmdb   sp!,{r1,r4-r12,lr}
+	mov	$rounds,r0		@ inp
+	mov	$key,r2
+	sub	$tbl,r3,#AES_encrypt-AES_Te	@ Te
+#if __ARM_ARCH__<7
+	ldrb	$s0,[$rounds,#3]	@ load input data in endian-neutral
+	ldrb	$t1,[$rounds,#2]	@ manner...
+	ldrb	$t2,[$rounds,#1]
+	ldrb	$t3,[$rounds,#0]
+	orr	$s0,$s0,$t1,lsl#8
+	ldrb	$s1,[$rounds,#7]
+	orr	$s0,$s0,$t2,lsl#16
+	ldrb	$t1,[$rounds,#6]
+	orr	$s0,$s0,$t3,lsl#24
+	ldrb	$t2,[$rounds,#5]
+	ldrb	$t3,[$rounds,#4]
+	orr	$s1,$s1,$t1,lsl#8
+	ldrb	$s2,[$rounds,#11]
+	orr	$s1,$s1,$t2,lsl#16
+	ldrb	$t1,[$rounds,#10]
+	orr	$s1,$s1,$t3,lsl#24
+	ldrb	$t2,[$rounds,#9]
+	ldrb	$t3,[$rounds,#8]
+	orr	$s2,$s2,$t1,lsl#8
+	ldrb	$s3,[$rounds,#15]
+	orr	$s2,$s2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#14]
+	orr	$s2,$s2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#13]
+	ldrb	$t3,[$rounds,#12]
+	orr	$s3,$s3,$t1,lsl#8
+	orr	$s3,$s3,$t2,lsl#16
+	orr	$s3,$s3,$t3,lsl#24
+#else
+	ldr	$s0,[$rounds,#0]
+	ldr	$s1,[$rounds,#4]
+	ldr	$s2,[$rounds,#8]
+	ldr	$s3,[$rounds,#12]
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+#endif
+	bl	_armv4_AES_encrypt
+
+	ldr	$rounds,[sp],#4		@ pop out
+#if __ARM_ARCH__>=7
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+	str	$s0,[$rounds,#0]
+	str	$s1,[$rounds,#4]
+	str	$s2,[$rounds,#8]
+	str	$s3,[$rounds,#12]
+#else
+	mov	$t1,$s0,lsr#24		@ write output in endian-neutral
+	mov	$t2,$s0,lsr#16		@ manner...
+	mov	$t3,$s0,lsr#8
+	strb	$t1,[$rounds,#0]
+	strb	$t2,[$rounds,#1]
+	mov	$t1,$s1,lsr#24
+	strb	$t3,[$rounds,#2]
+	mov	$t2,$s1,lsr#16
+	strb	$s0,[$rounds,#3]
+	mov	$t3,$s1,lsr#8
+	strb	$t1,[$rounds,#4]
+	strb	$t2,[$rounds,#5]
+	mov	$t1,$s2,lsr#24
+	strb	$t3,[$rounds,#6]
+	mov	$t2,$s2,lsr#16
+	strb	$s1,[$rounds,#7]
+	mov	$t3,$s2,lsr#8
+	strb	$t1,[$rounds,#8]
+	strb	$t2,[$rounds,#9]
+	mov	$t1,$s3,lsr#24
+	strb	$t3,[$rounds,#10]
+	mov	$t2,$s3,lsr#16
+	strb	$s2,[$rounds,#11]
+	mov	$t3,$s3,lsr#8
+	strb	$t1,[$rounds,#12]
+	strb	$t2,[$rounds,#13]
+	strb	$t3,[$rounds,#14]
+	strb	$s3,[$rounds,#15]
+#endif
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia   sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_encrypt,.-AES_encrypt
+
+.type   _armv4_AES_encrypt,%function
+.align	2
+_armv4_AES_encrypt:
+	str	lr,[sp,#-4]!		@ push lr
+	ldmia	$key!,{$t1-$i1}
+	eor	$s0,$s0,$t1
+	ldr	$rounds,[$key,#240-16]
+	eor	$s1,$s1,$t2
+	eor	$s2,$s2,$t3
+	eor	$s3,$s3,$i1
+	sub	$rounds,$rounds,#1
+	mov	lr,#255
+
+	and	$i1,lr,$s0
+	and	$i2,lr,$s0,lsr#8
+	and	$i3,lr,$s0,lsr#16
+	mov	$s0,$s0,lsr#24
+.Lenc_loop:
+	ldr	$t1,[$tbl,$i1,lsl#2]	@ Te3[s0>>0]
+	and	$i1,lr,$s1,lsr#16	@ i0
+	ldr	$t2,[$tbl,$i2,lsl#2]	@ Te2[s0>>8]
+	and	$i2,lr,$s1
+	ldr	$t3,[$tbl,$i3,lsl#2]	@ Te1[s0>>16]
+	and	$i3,lr,$s1,lsr#8
+	ldr	$s0,[$tbl,$s0,lsl#2]	@ Te0[s0>>24]
+	mov	$s1,$s1,lsr#24
+
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Te1[s1>>16]
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Te3[s1>>0]
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Te2[s1>>8]
+	eor	$s0,$s0,$i1,ror#8
+	ldr	$s1,[$tbl,$s1,lsl#2]	@ Te0[s1>>24]
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$t2,$i2,ror#8
+	and	$i2,lr,$s2,lsr#16	@ i1
+	eor	$t3,$t3,$i3,ror#8
+	and	$i3,lr,$s2
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Te2[s2>>8]
+	eor	$s1,$s1,$t1,ror#24
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Te1[s2>>16]
+	mov	$s2,$s2,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Te3[s2>>0]
+	eor	$s0,$s0,$i1,ror#16
+	ldr	$s2,[$tbl,$s2,lsl#2]	@ Te0[s2>>24]
+	and	$i1,lr,$s3		@ i0
+	eor	$s1,$s1,$i2,ror#8
+	and	$i2,lr,$s3,lsr#8	@ i1
+	eor	$t3,$t3,$i3,ror#16
+	and	$i3,lr,$s3,lsr#16	@ i2
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Te3[s3>>0]
+	eor	$s2,$s2,$t2,ror#16
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Te2[s3>>8]
+	mov	$s3,$s3,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Te1[s3>>16]
+	eor	$s0,$s0,$i1,ror#24
+	ldr	$i1,[$key],#16
+	eor	$s1,$s1,$i2,ror#16
+	ldr	$s3,[$tbl,$s3,lsl#2]	@ Te0[s3>>24]
+	eor	$s2,$s2,$i3,ror#8
+	ldr	$t1,[$key,#-12]
+	eor	$s3,$s3,$t3,ror#8
+
+	ldr	$t2,[$key,#-8]
+	eor	$s0,$s0,$i1
+	ldr	$t3,[$key,#-4]
+	and	$i1,lr,$s0
+	eor	$s1,$s1,$t1
+	and	$i2,lr,$s0,lsr#8
+	eor	$s2,$s2,$t2
+	and	$i3,lr,$s0,lsr#16
+	eor	$s3,$s3,$t3
+	mov	$s0,$s0,lsr#24
+
+	subs	$rounds,$rounds,#1
+	bne	.Lenc_loop
+
+	add	$tbl,$tbl,#2
+
+	ldrb	$t1,[$tbl,$i1,lsl#2]	@ Te4[s0>>0]
+	and	$i1,lr,$s1,lsr#16	@ i0
+	ldrb	$t2,[$tbl,$i2,lsl#2]	@ Te4[s0>>8]
+	and	$i2,lr,$s1
+	ldrb	$t3,[$tbl,$i3,lsl#2]	@ Te4[s0>>16]
+	and	$i3,lr,$s1,lsr#8
+	ldrb	$s0,[$tbl,$s0,lsl#2]	@ Te4[s0>>24]
+	mov	$s1,$s1,lsr#24
+
+	ldrb	$i1,[$tbl,$i1,lsl#2]	@ Te4[s1>>16]
+	ldrb	$i2,[$tbl,$i2,lsl#2]	@ Te4[s1>>0]
+	ldrb	$i3,[$tbl,$i3,lsl#2]	@ Te4[s1>>8]
+	eor	$s0,$i1,$s0,lsl#8
+	ldrb	$s1,[$tbl,$s1,lsl#2]	@ Te4[s1>>24]
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$i2,$t2,lsl#8
+	and	$i2,lr,$s2,lsr#16	@ i1
+	eor	$t3,$i3,$t3,lsl#8
+	and	$i3,lr,$s2
+	ldrb	$i1,[$tbl,$i1,lsl#2]	@ Te4[s2>>8]
+	eor	$s1,$t1,$s1,lsl#24
+	ldrb	$i2,[$tbl,$i2,lsl#2]	@ Te4[s2>>16]
+	mov	$s2,$s2,lsr#24
+
+	ldrb	$i3,[$tbl,$i3,lsl#2]	@ Te4[s2>>0]
+	eor	$s0,$i1,$s0,lsl#8
+	ldrb	$s2,[$tbl,$s2,lsl#2]	@ Te4[s2>>24]
+	and	$i1,lr,$s3		@ i0
+	eor	$s1,$s1,$i2,lsl#16
+	and	$i2,lr,$s3,lsr#8	@ i1
+	eor	$t3,$i3,$t3,lsl#8
+	and	$i3,lr,$s3,lsr#16	@ i2
+	ldrb	$i1,[$tbl,$i1,lsl#2]	@ Te4[s3>>0]
+	eor	$s2,$t2,$s2,lsl#24
+	ldrb	$i2,[$tbl,$i2,lsl#2]	@ Te4[s3>>8]
+	mov	$s3,$s3,lsr#24
+
+	ldrb	$i3,[$tbl,$i3,lsl#2]	@ Te4[s3>>16]
+	eor	$s0,$i1,$s0,lsl#8
+	ldr	$i1,[$key,#0]
+	ldrb	$s3,[$tbl,$s3,lsl#2]	@ Te4[s3>>24]
+	eor	$s1,$s1,$i2,lsl#8
+	ldr	$t1,[$key,#4]
+	eor	$s2,$s2,$i3,lsl#16
+	ldr	$t2,[$key,#8]
+	eor	$s3,$t3,$s3,lsl#24
+	ldr	$t3,[$key,#12]
+
+	eor	$s0,$s0,$i1
+	eor	$s1,$s1,$t1
+	eor	$s2,$s2,$t2
+	eor	$s3,$s3,$t3
+
+	sub	$tbl,$tbl,#2
+	ldr	pc,[sp],#4		@ pop and return
+.size	_armv4_AES_encrypt,.-_armv4_AES_encrypt
+
+.global AES_set_encrypt_key
+.type   AES_set_encrypt_key,%function
+.align	5
+AES_set_encrypt_key:
+_armv4_AES_set_encrypt_key:
+#if __ARM_ARCH__<7
+	sub	r3,pc,#8		@ AES_set_encrypt_key
+#else
+	adr	r3,AES_set_encrypt_key
+#endif
+	teq	r0,#0
+#if __ARM_ARCH__>=7
+	itt	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	moveq	r0,#-1
+	beq	.Labrt
+	teq	r2,#0
+#if __ARM_ARCH__>=7
+	itt	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	moveq	r0,#-1
+	beq	.Labrt
+
+	teq	r1,#128
+	beq	.Lok
+	teq	r1,#192
+	beq	.Lok
+	teq	r1,#256
+#if __ARM_ARCH__>=7
+	itt	ne			@ Thumb2 thing, sanity check in ARM
+#endif
+	movne	r0,#-1
+	bne	.Labrt
+
+.Lok:	stmdb   sp!,{r4-r12,lr}
+	sub	$tbl,r3,#_armv4_AES_set_encrypt_key-AES_Te-1024	@ Te4
+
+	mov	$rounds,r0		@ inp
+	mov	lr,r1			@ bits
+	mov	$key,r2			@ key
+
+#if __ARM_ARCH__<7
+	ldrb	$s0,[$rounds,#3]	@ load input data in endian-neutral
+	ldrb	$t1,[$rounds,#2]	@ manner...
+	ldrb	$t2,[$rounds,#1]
+	ldrb	$t3,[$rounds,#0]
+	orr	$s0,$s0,$t1,lsl#8
+	ldrb	$s1,[$rounds,#7]
+	orr	$s0,$s0,$t2,lsl#16
+	ldrb	$t1,[$rounds,#6]
+	orr	$s0,$s0,$t3,lsl#24
+	ldrb	$t2,[$rounds,#5]
+	ldrb	$t3,[$rounds,#4]
+	orr	$s1,$s1,$t1,lsl#8
+	ldrb	$s2,[$rounds,#11]
+	orr	$s1,$s1,$t2,lsl#16
+	ldrb	$t1,[$rounds,#10]
+	orr	$s1,$s1,$t3,lsl#24
+	ldrb	$t2,[$rounds,#9]
+	ldrb	$t3,[$rounds,#8]
+	orr	$s2,$s2,$t1,lsl#8
+	ldrb	$s3,[$rounds,#15]
+	orr	$s2,$s2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#14]
+	orr	$s2,$s2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#13]
+	ldrb	$t3,[$rounds,#12]
+	orr	$s3,$s3,$t1,lsl#8
+	str	$s0,[$key],#16
+	orr	$s3,$s3,$t2,lsl#16
+	str	$s1,[$key,#-12]
+	orr	$s3,$s3,$t3,lsl#24
+	str	$s2,[$key,#-8]
+	str	$s3,[$key,#-4]
+#else
+	ldr	$s0,[$rounds,#0]
+	ldr	$s1,[$rounds,#4]
+	ldr	$s2,[$rounds,#8]
+	ldr	$s3,[$rounds,#12]
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+	str	$s0,[$key],#16
+	str	$s1,[$key,#-12]
+	str	$s2,[$key,#-8]
+	str	$s3,[$key,#-4]
+#endif
+
+	teq	lr,#128
+	bne	.Lnot128
+	mov	$rounds,#10
+	str	$rounds,[$key,#240-16]
+	add	$t3,$tbl,#256			@ rcon
+	mov	lr,#255
+
+.L128_loop:
+	and	$t2,lr,$s3,lsr#24
+	and	$i1,lr,$s3,lsr#16
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$s3,lsr#8
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$s3
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#24
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$t3],#4			@ rcon[i++]
+	orr	$t2,$t2,$i3,lsl#8
+	eor	$t2,$t2,$t1
+	eor	$s0,$s0,$t2			@ rk[4]=rk[0]^...
+	eor	$s1,$s1,$s0			@ rk[5]=rk[1]^rk[4]
+	str	$s0,[$key],#16
+	eor	$s2,$s2,$s1			@ rk[6]=rk[2]^rk[5]
+	str	$s1,[$key,#-12]
+	eor	$s3,$s3,$s2			@ rk[7]=rk[3]^rk[6]
+	str	$s2,[$key,#-8]
+	subs	$rounds,$rounds,#1
+	str	$s3,[$key,#-4]
+	bne	.L128_loop
+	sub	r2,$key,#176
+	b	.Ldone
+
+.Lnot128:
+#if __ARM_ARCH__<7
+	ldrb	$i2,[$rounds,#19]
+	ldrb	$t1,[$rounds,#18]
+	ldrb	$t2,[$rounds,#17]
+	ldrb	$t3,[$rounds,#16]
+	orr	$i2,$i2,$t1,lsl#8
+	ldrb	$i3,[$rounds,#23]
+	orr	$i2,$i2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#22]
+	orr	$i2,$i2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#21]
+	ldrb	$t3,[$rounds,#20]
+	orr	$i3,$i3,$t1,lsl#8
+	orr	$i3,$i3,$t2,lsl#16
+	str	$i2,[$key],#8
+	orr	$i3,$i3,$t3,lsl#24
+	str	$i3,[$key,#-4]
+#else
+	ldr	$i2,[$rounds,#16]
+	ldr	$i3,[$rounds,#20]
+#ifdef __ARMEL__
+	rev	$i2,$i2
+	rev	$i3,$i3
+#endif
+	str	$i2,[$key],#8
+	str	$i3,[$key,#-4]
+#endif
+
+	teq	lr,#192
+	bne	.Lnot192
+	mov	$rounds,#12
+	str	$rounds,[$key,#240-24]
+	add	$t3,$tbl,#256			@ rcon
+	mov	lr,#255
+	mov	$rounds,#8
+
+.L192_loop:
+	and	$t2,lr,$i3,lsr#24
+	and	$i1,lr,$i3,lsr#16
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$i3,lsr#8
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$i3
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#24
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$t3],#4			@ rcon[i++]
+	orr	$t2,$t2,$i3,lsl#8
+	eor	$i3,$t2,$t1
+	eor	$s0,$s0,$i3			@ rk[6]=rk[0]^...
+	eor	$s1,$s1,$s0			@ rk[7]=rk[1]^rk[6]
+	str	$s0,[$key],#24
+	eor	$s2,$s2,$s1			@ rk[8]=rk[2]^rk[7]
+	str	$s1,[$key,#-20]
+	eor	$s3,$s3,$s2			@ rk[9]=rk[3]^rk[8]
+	str	$s2,[$key,#-16]
+	subs	$rounds,$rounds,#1
+	str	$s3,[$key,#-12]
+#if __ARM_ARCH__>=7
+	itt	eq				@ Thumb2 thing, sanity check in ARM
+#endif
+	subeq	r2,$key,#216
+	beq	.Ldone
+
+	ldr	$i1,[$key,#-32]
+	ldr	$i2,[$key,#-28]
+	eor	$i1,$i1,$s3			@ rk[10]=rk[4]^rk[9]
+	eor	$i3,$i2,$i1			@ rk[11]=rk[5]^rk[10]
+	str	$i1,[$key,#-8]
+	str	$i3,[$key,#-4]
+	b	.L192_loop
+
+.Lnot192:
+#if __ARM_ARCH__<7
+	ldrb	$i2,[$rounds,#27]
+	ldrb	$t1,[$rounds,#26]
+	ldrb	$t2,[$rounds,#25]
+	ldrb	$t3,[$rounds,#24]
+	orr	$i2,$i2,$t1,lsl#8
+	ldrb	$i3,[$rounds,#31]
+	orr	$i2,$i2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#30]
+	orr	$i2,$i2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#29]
+	ldrb	$t3,[$rounds,#28]
+	orr	$i3,$i3,$t1,lsl#8
+	orr	$i3,$i3,$t2,lsl#16
+	str	$i2,[$key],#8
+	orr	$i3,$i3,$t3,lsl#24
+	str	$i3,[$key,#-4]
+#else
+	ldr	$i2,[$rounds,#24]
+	ldr	$i3,[$rounds,#28]
+#ifdef __ARMEL__
+	rev	$i2,$i2
+	rev	$i3,$i3
+#endif
+	str	$i2,[$key],#8
+	str	$i3,[$key,#-4]
+#endif
+
+	mov	$rounds,#14
+	str	$rounds,[$key,#240-32]
+	add	$t3,$tbl,#256			@ rcon
+	mov	lr,#255
+	mov	$rounds,#7
+
+.L256_loop:
+	and	$t2,lr,$i3,lsr#24
+	and	$i1,lr,$i3,lsr#16
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$i3,lsr#8
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$i3
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#24
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$t3],#4			@ rcon[i++]
+	orr	$t2,$t2,$i3,lsl#8
+	eor	$i3,$t2,$t1
+	eor	$s0,$s0,$i3			@ rk[8]=rk[0]^...
+	eor	$s1,$s1,$s0			@ rk[9]=rk[1]^rk[8]
+	str	$s0,[$key],#32
+	eor	$s2,$s2,$s1			@ rk[10]=rk[2]^rk[9]
+	str	$s1,[$key,#-28]
+	eor	$s3,$s3,$s2			@ rk[11]=rk[3]^rk[10]
+	str	$s2,[$key,#-24]
+	subs	$rounds,$rounds,#1
+	str	$s3,[$key,#-20]
+#if __ARM_ARCH__>=7
+	itt	eq				@ Thumb2 thing, sanity check in ARM
+#endif
+	subeq	r2,$key,#256
+	beq	.Ldone
+
+	and	$t2,lr,$s3
+	and	$i1,lr,$s3,lsr#8
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$s3,lsr#16
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$s3,lsr#24
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#8
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$key,#-48]
+	orr	$t2,$t2,$i3,lsl#24
+
+	ldr	$i1,[$key,#-44]
+	ldr	$i2,[$key,#-40]
+	eor	$t1,$t1,$t2			@ rk[12]=rk[4]^...
+	ldr	$i3,[$key,#-36]
+	eor	$i1,$i1,$t1			@ rk[13]=rk[5]^rk[12]
+	str	$t1,[$key,#-16]
+	eor	$i2,$i2,$i1			@ rk[14]=rk[6]^rk[13]
+	str	$i1,[$key,#-12]
+	eor	$i3,$i3,$i2			@ rk[15]=rk[7]^rk[14]
+	str	$i2,[$key,#-8]
+	str	$i3,[$key,#-4]
+	b	.L256_loop
+
+.align	2
+.Ldone:	mov	r0,#0
+	ldmia   sp!,{r4-r12,lr}
+.Labrt:
+#if defined(__thumb2__) && __ARM_ARCH__>=7
+	.short	0x4770			@ bx lr in Thumb2 encoding
+#else
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_set_encrypt_key,.-AES_set_encrypt_key
+
+.global AES_set_decrypt_key
+.type   AES_set_decrypt_key,%function
+.align	5
+AES_set_decrypt_key:
+	str	lr,[sp,#-4]!            @ push lr
+	bl	_armv4_AES_set_encrypt_key
+	teq	r0,#0
+	ldr	lr,[sp],#4              @ pop lr
+	bne	.Labrt
+
+	mov	r0,r2			@ AES_set_encrypt_key preserves r2,
+	mov	r1,r2			@ which is AES_KEY *key
+	b	_armv4_AES_set_enc2dec_key
+.size	AES_set_decrypt_key,.-AES_set_decrypt_key
+
+@ void AES_set_enc2dec_key(const AES_KEY *inp,AES_KEY *out)
+.global	AES_set_enc2dec_key
+.type	AES_set_enc2dec_key,%function
+.align	5
+AES_set_enc2dec_key:
+_armv4_AES_set_enc2dec_key:
+	stmdb   sp!,{r4-r12,lr}
+
+	ldr	$rounds,[r0,#240]
+	mov	$i1,r0			@ input
+	add	$i2,r0,$rounds,lsl#4
+	mov	$key,r1			@ ouput
+	add	$tbl,r1,$rounds,lsl#4
+	str	$rounds,[r1,#240]
+
+.Linv:	ldr	$s0,[$i1],#16
+	ldr	$s1,[$i1,#-12]
+	ldr	$s2,[$i1,#-8]
+	ldr	$s3,[$i1,#-4]
+	ldr	$t1,[$i2],#-16
+	ldr	$t2,[$i2,#16+4]
+	ldr	$t3,[$i2,#16+8]
+	ldr	$i3,[$i2,#16+12]
+	str	$s0,[$tbl],#-16
+	str	$s1,[$tbl,#16+4]
+	str	$s2,[$tbl,#16+8]
+	str	$s3,[$tbl,#16+12]
+	str	$t1,[$key],#16
+	str	$t2,[$key,#-12]
+	str	$t3,[$key,#-8]
+	str	$i3,[$key,#-4]
+	teq	$i1,$i2
+	bne	.Linv
+
+	ldr	$s0,[$i1]
+	ldr	$s1,[$i1,#4]
+	ldr	$s2,[$i1,#8]
+	ldr	$s3,[$i1,#12]
+	str	$s0,[$key]
+	str	$s1,[$key,#4]
+	str	$s2,[$key,#8]
+	str	$s3,[$key,#12]
+	sub	$key,$key,$rounds,lsl#3
+___
+$mask80=$i1;
+$mask1b=$i2;
+$mask7f=$i3;
+$code.=<<___;
+	ldr	$s0,[$key,#16]!		@ prefetch tp1
+	mov	$mask80,#0x80
+	mov	$mask1b,#0x1b
+	orr	$mask80,$mask80,#0x8000
+	orr	$mask1b,$mask1b,#0x1b00
+	orr	$mask80,$mask80,$mask80,lsl#16
+	orr	$mask1b,$mask1b,$mask1b,lsl#16
+	sub	$rounds,$rounds,#1
+	mvn	$mask7f,$mask80
+	mov	$rounds,$rounds,lsl#2	@ (rounds-1)*4
+
+.Lmix:	and	$t1,$s0,$mask80
+	and	$s1,$s0,$mask7f
+	sub	$t1,$t1,$t1,lsr#7
+	and	$t1,$t1,$mask1b
+	eor	$s1,$t1,$s1,lsl#1	@ tp2
+
+	and	$t1,$s1,$mask80
+	and	$s2,$s1,$mask7f
+	sub	$t1,$t1,$t1,lsr#7
+	and	$t1,$t1,$mask1b
+	eor	$s2,$t1,$s2,lsl#1	@ tp4
+
+	and	$t1,$s2,$mask80
+	and	$s3,$s2,$mask7f
+	sub	$t1,$t1,$t1,lsr#7
+	and	$t1,$t1,$mask1b
+	eor	$s3,$t1,$s3,lsl#1	@ tp8
+
+	eor	$t1,$s1,$s2
+	eor	$t2,$s0,$s3		@ tp9
+	eor	$t1,$t1,$s3		@ tpe
+	eor	$t1,$t1,$s1,ror#24
+	eor	$t1,$t1,$t2,ror#24	@ ^= ROTATE(tpb=tp9^tp2,8)
+	eor	$t1,$t1,$s2,ror#16
+	eor	$t1,$t1,$t2,ror#16	@ ^= ROTATE(tpd=tp9^tp4,16)
+	eor	$t1,$t1,$t2,ror#8	@ ^= ROTATE(tp9,24)
+
+	ldr	$s0,[$key,#4]		@ prefetch tp1
+	str	$t1,[$key],#4
+	subs	$rounds,$rounds,#1
+	bne	.Lmix
+
+	mov	r0,#0
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia   sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_set_enc2dec_key,.-AES_set_enc2dec_key
+
+.type	AES_Td,%object
+.align	5
+AES_Td:
+.word	0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96
+.word	0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393
+.word	0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25
+.word	0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f
+.word	0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1
+.word	0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6
+.word	0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da
+.word	0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844
+.word	0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd
+.word	0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4
+.word	0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45
+.word	0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94
+.word	0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7
+.word	0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a
+.word	0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5
+.word	0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c
+.word	0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1
+.word	0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a
+.word	0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75
+.word	0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051
+.word	0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46
+.word	0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff
+.word	0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77
+.word	0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb
+.word	0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000
+.word	0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e
+.word	0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927
+.word	0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a
+.word	0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e
+.word	0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16
+.word	0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d
+.word	0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8
+.word	0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd
+.word	0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34
+.word	0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163
+.word	0xd731dcca, 0x42638510, 0x13972240, 0x84c61120
+.word	0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d
+.word	0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0
+.word	0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422
+.word	0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef
+.word	0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36
+.word	0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4
+.word	0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662
+.word	0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5
+.word	0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3
+.word	0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b
+.word	0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8
+.word	0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6
+.word	0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6
+.word	0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0
+.word	0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815
+.word	0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f
+.word	0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df
+.word	0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f
+.word	0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e
+.word	0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713
+.word	0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89
+.word	0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c
+.word	0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf
+.word	0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86
+.word	0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f
+.word	0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541
+.word	0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190
+.word	0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742
+@ Td4[256]
+.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+.size	AES_Td,.-AES_Td
+
+@ void AES_decrypt(const unsigned char *in, unsigned char *out,
+@ 		 const AES_KEY *key) {
+.global AES_decrypt
+.type   AES_decrypt,%function
+.align	5
+AES_decrypt:
+#if __ARM_ARCH__<7
+	sub	r3,pc,#8		@ AES_decrypt
+#else
+	adr	r3,AES_decrypt
+#endif
+	stmdb   sp!,{r1,r4-r12,lr}
+	mov	$rounds,r0		@ inp
+	mov	$key,r2
+	sub	$tbl,r3,#AES_decrypt-AES_Td		@ Td
+#if __ARM_ARCH__<7
+	ldrb	$s0,[$rounds,#3]	@ load input data in endian-neutral
+	ldrb	$t1,[$rounds,#2]	@ manner...
+	ldrb	$t2,[$rounds,#1]
+	ldrb	$t3,[$rounds,#0]
+	orr	$s0,$s0,$t1,lsl#8
+	ldrb	$s1,[$rounds,#7]
+	orr	$s0,$s0,$t2,lsl#16
+	ldrb	$t1,[$rounds,#6]
+	orr	$s0,$s0,$t3,lsl#24
+	ldrb	$t2,[$rounds,#5]
+	ldrb	$t3,[$rounds,#4]
+	orr	$s1,$s1,$t1,lsl#8
+	ldrb	$s2,[$rounds,#11]
+	orr	$s1,$s1,$t2,lsl#16
+	ldrb	$t1,[$rounds,#10]
+	orr	$s1,$s1,$t3,lsl#24
+	ldrb	$t2,[$rounds,#9]
+	ldrb	$t3,[$rounds,#8]
+	orr	$s2,$s2,$t1,lsl#8
+	ldrb	$s3,[$rounds,#15]
+	orr	$s2,$s2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#14]
+	orr	$s2,$s2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#13]
+	ldrb	$t3,[$rounds,#12]
+	orr	$s3,$s3,$t1,lsl#8
+	orr	$s3,$s3,$t2,lsl#16
+	orr	$s3,$s3,$t3,lsl#24
+#else
+	ldr	$s0,[$rounds,#0]
+	ldr	$s1,[$rounds,#4]
+	ldr	$s2,[$rounds,#8]
+	ldr	$s3,[$rounds,#12]
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+#endif
+	bl	_armv4_AES_decrypt
+
+	ldr	$rounds,[sp],#4		@ pop out
+#if __ARM_ARCH__>=7
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+	str	$s0,[$rounds,#0]
+	str	$s1,[$rounds,#4]
+	str	$s2,[$rounds,#8]
+	str	$s3,[$rounds,#12]
+#else
+	mov	$t1,$s0,lsr#24		@ write output in endian-neutral
+	mov	$t2,$s0,lsr#16		@ manner...
+	mov	$t3,$s0,lsr#8
+	strb	$t1,[$rounds,#0]
+	strb	$t2,[$rounds,#1]
+	mov	$t1,$s1,lsr#24
+	strb	$t3,[$rounds,#2]
+	mov	$t2,$s1,lsr#16
+	strb	$s0,[$rounds,#3]
+	mov	$t3,$s1,lsr#8
+	strb	$t1,[$rounds,#4]
+	strb	$t2,[$rounds,#5]
+	mov	$t1,$s2,lsr#24
+	strb	$t3,[$rounds,#6]
+	mov	$t2,$s2,lsr#16
+	strb	$s1,[$rounds,#7]
+	mov	$t3,$s2,lsr#8
+	strb	$t1,[$rounds,#8]
+	strb	$t2,[$rounds,#9]
+	mov	$t1,$s3,lsr#24
+	strb	$t3,[$rounds,#10]
+	mov	$t2,$s3,lsr#16
+	strb	$s2,[$rounds,#11]
+	mov	$t3,$s3,lsr#8
+	strb	$t1,[$rounds,#12]
+	strb	$t2,[$rounds,#13]
+	strb	$t3,[$rounds,#14]
+	strb	$s3,[$rounds,#15]
+#endif
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia   sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_decrypt,.-AES_decrypt
+
+.type   _armv4_AES_decrypt,%function
+.align	2
+_armv4_AES_decrypt:
+	str	lr,[sp,#-4]!		@ push lr
+	ldmia	$key!,{$t1-$i1}
+	eor	$s0,$s0,$t1
+	ldr	$rounds,[$key,#240-16]
+	eor	$s1,$s1,$t2
+	eor	$s2,$s2,$t3
+	eor	$s3,$s3,$i1
+	sub	$rounds,$rounds,#1
+	mov	lr,#255
+
+	and	$i1,lr,$s0,lsr#16
+	and	$i2,lr,$s0,lsr#8
+	and	$i3,lr,$s0
+	mov	$s0,$s0,lsr#24
+.Ldec_loop:
+	ldr	$t1,[$tbl,$i1,lsl#2]	@ Td1[s0>>16]
+	and	$i1,lr,$s1		@ i0
+	ldr	$t2,[$tbl,$i2,lsl#2]	@ Td2[s0>>8]
+	and	$i2,lr,$s1,lsr#16
+	ldr	$t3,[$tbl,$i3,lsl#2]	@ Td3[s0>>0]
+	and	$i3,lr,$s1,lsr#8
+	ldr	$s0,[$tbl,$s0,lsl#2]	@ Td0[s0>>24]
+	mov	$s1,$s1,lsr#24
+
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Td3[s1>>0]
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Td1[s1>>16]
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Td2[s1>>8]
+	eor	$s0,$s0,$i1,ror#24
+	ldr	$s1,[$tbl,$s1,lsl#2]	@ Td0[s1>>24]
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$i2,$t2,ror#8
+	and	$i2,lr,$s2		@ i1
+	eor	$t3,$i3,$t3,ror#8
+	and	$i3,lr,$s2,lsr#16
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Td2[s2>>8]
+	eor	$s1,$s1,$t1,ror#8
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Td3[s2>>0]
+	mov	$s2,$s2,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Td1[s2>>16]
+	eor	$s0,$s0,$i1,ror#16
+	ldr	$s2,[$tbl,$s2,lsl#2]	@ Td0[s2>>24]
+	and	$i1,lr,$s3,lsr#16	@ i0
+	eor	$s1,$s1,$i2,ror#24
+	and	$i2,lr,$s3,lsr#8	@ i1
+	eor	$t3,$i3,$t3,ror#8
+	and	$i3,lr,$s3		@ i2
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Td1[s3>>16]
+	eor	$s2,$s2,$t2,ror#8
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Td2[s3>>8]
+	mov	$s3,$s3,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Td3[s3>>0]
+	eor	$s0,$s0,$i1,ror#8
+	ldr	$i1,[$key],#16
+	eor	$s1,$s1,$i2,ror#16
+	ldr	$s3,[$tbl,$s3,lsl#2]	@ Td0[s3>>24]
+	eor	$s2,$s2,$i3,ror#24
+
+	ldr	$t1,[$key,#-12]
+	eor	$s0,$s0,$i1
+	ldr	$t2,[$key,#-8]
+	eor	$s3,$s3,$t3,ror#8
+	ldr	$t3,[$key,#-4]
+	and	$i1,lr,$s0,lsr#16
+	eor	$s1,$s1,$t1
+	and	$i2,lr,$s0,lsr#8
+	eor	$s2,$s2,$t2
+	and	$i3,lr,$s0
+	eor	$s3,$s3,$t3
+	mov	$s0,$s0,lsr#24
+
+	subs	$rounds,$rounds,#1
+	bne	.Ldec_loop
+
+	add	$tbl,$tbl,#1024
+
+	ldr	$t2,[$tbl,#0]		@ prefetch Td4
+	ldr	$t3,[$tbl,#32]
+	ldr	$t1,[$tbl,#64]
+	ldr	$t2,[$tbl,#96]
+	ldr	$t3,[$tbl,#128]
+	ldr	$t1,[$tbl,#160]
+	ldr	$t2,[$tbl,#192]
+	ldr	$t3,[$tbl,#224]
+
+	ldrb	$s0,[$tbl,$s0]		@ Td4[s0>>24]
+	ldrb	$t1,[$tbl,$i1]		@ Td4[s0>>16]
+	and	$i1,lr,$s1		@ i0
+	ldrb	$t2,[$tbl,$i2]		@ Td4[s0>>8]
+	and	$i2,lr,$s1,lsr#16
+	ldrb	$t3,[$tbl,$i3]		@ Td4[s0>>0]
+	and	$i3,lr,$s1,lsr#8
+
+	add	$s1,$tbl,$s1,lsr#24
+	ldrb	$i1,[$tbl,$i1]		@ Td4[s1>>0]
+	ldrb	$s1,[$s1]		@ Td4[s1>>24]
+	ldrb	$i2,[$tbl,$i2]		@ Td4[s1>>16]
+	eor	$s0,$i1,$s0,lsl#24
+	ldrb	$i3,[$tbl,$i3]		@ Td4[s1>>8]
+	eor	$s1,$t1,$s1,lsl#8
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$t2,$i2,lsl#8
+	and	$i2,lr,$s2		@ i1
+	ldrb	$i1,[$tbl,$i1]		@ Td4[s2>>8]
+	eor	$t3,$t3,$i3,lsl#8
+	ldrb	$i2,[$tbl,$i2]		@ Td4[s2>>0]
+	and	$i3,lr,$s2,lsr#16
+
+	add	$s2,$tbl,$s2,lsr#24
+	ldrb	$s2,[$s2]		@ Td4[s2>>24]
+	eor	$s0,$s0,$i1,lsl#8
+	ldrb	$i3,[$tbl,$i3]		@ Td4[s2>>16]
+	eor	$s1,$i2,$s1,lsl#16
+	and	$i1,lr,$s3,lsr#16	@ i0
+	eor	$s2,$t2,$s2,lsl#16
+	and	$i2,lr,$s3,lsr#8	@ i1
+	ldrb	$i1,[$tbl,$i1]		@ Td4[s3>>16]
+	eor	$t3,$t3,$i3,lsl#16
+	ldrb	$i2,[$tbl,$i2]		@ Td4[s3>>8]
+	and	$i3,lr,$s3		@ i2
+
+	add	$s3,$tbl,$s3,lsr#24
+	ldrb	$i3,[$tbl,$i3]		@ Td4[s3>>0]
+	ldrb	$s3,[$s3]		@ Td4[s3>>24]
+	eor	$s0,$s0,$i1,lsl#16
+	ldr	$i1,[$key,#0]
+	eor	$s1,$s1,$i2,lsl#8
+	ldr	$t1,[$key,#4]
+	eor	$s2,$i3,$s2,lsl#8
+	ldr	$t2,[$key,#8]
+	eor	$s3,$t3,$s3,lsl#24
+	ldr	$t3,[$key,#12]
+
+	eor	$s0,$s0,$i1
+	eor	$s1,$s1,$t1
+	eor	$s2,$s2,$t2
+	eor	$s3,$s3,$t3
+
+	sub	$tbl,$tbl,#1024
+	ldr	pc,[sp],#4		@ pop and return
+.size	_armv4_AES_decrypt,.-_armv4_AES_decrypt
+.asciz	"AES for ARMv4, CRYPTOGAMS by <appro\@openssl.org>"
+.align	2
+
+#endif
+___
+
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+
+open SELF,$0;
+while(<SELF>) {
+	next if (/^#!/);
+	last if (!s/^#/@/ and !/^$/);
+	print;
+}
+close SELF;
+
+print $code;
+close STDOUT;	# enforce flush
diff --git a/crypto/aes/asm/aes-x86_64.pl b/crypto/aes/asm/aes-x86_64.pl
new file mode 100644
index 0000000..e2a1246
--- /dev/null
+++ b/crypto/aes/asm/aes-x86_64.pl
@@ -0,0 +1,2811 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Version 2.1.
+#
+# aes-*-cbc benchmarks are improved by >70% [compared to gcc 3.3.2 on
+# Opteron 240 CPU] plus all the bells-n-whistles from 32-bit version
+# [you'll notice a lot of resemblance], such as compressed S-boxes
+# in little-endian byte order, prefetch of these tables in CBC mode,
+# as well as avoiding L1 cache aliasing between stack frame and key
+# schedule and already mentioned tables, compressed Td4...
+#
+# Performance in number of cycles per processed byte for 128-bit key:
+#
+#		ECB encrypt	ECB decrypt	CBC large chunk
+# AMD64		33		43		13.0
+# EM64T		38		56		18.6(*)
+# Core 2	30		42		14.5(*)
+# Atom		65		86		32.1(*)
+#
+# (*) with hyper-threading off
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$verticalspin=1;	# unlike 32-bit version $verticalspin performs
+			# ~15% better on both AMD and Intel cores
+$speed_limit=512;	# see aes-586.pl for details
+
+$code=".text\n";
+
+$s0="%eax";
+$s1="%ebx";
+$s2="%ecx";
+$s3="%edx";
+$acc0="%esi";	$mask80="%rsi";
+$acc1="%edi";	$maskfe="%rdi";
+$acc2="%ebp";	$mask1b="%rbp";
+$inp="%r8";
+$out="%r9";
+$t0="%r10d";
+$t1="%r11d";
+$t2="%r12d";
+$rnds="%r13d";
+$sbox="%r14";
+$key="%r15";
+
+sub hi() { my $r=shift;	$r =~ s/%[er]([a-d])x/%\1h/;	$r; }
+sub lo() { my $r=shift;	$r =~ s/%[er]([a-d])x/%\1l/;
+			$r =~ s/%[er]([sd]i)/%\1l/;
+			$r =~ s/%(r[0-9]+)[d]?/%\1b/;	$r; }
+sub LO() { my $r=shift; $r =~ s/%r([a-z]+)/%e\1/;
+			$r =~ s/%r([0-9]+)/%r\1d/;	$r; }
+sub _data_word()
+{ my $i;
+    while(defined($i=shift)) { $code.=sprintf".long\t0x%08x,0x%08x\n",$i,$i; }
+}
+sub data_word()
+{ my $i;
+  my $last=pop(@_);
+    $code.=".long\t";
+    while(defined($i=shift)) { $code.=sprintf"0x%08x,",$i; }
+    $code.=sprintf"0x%08x\n",$last;
+}
+
+sub data_byte()
+{ my $i;
+  my $last=pop(@_);
+    $code.=".byte\t";
+    while(defined($i=shift)) { $code.=sprintf"0x%02x,",$i&0xff; }
+    $code.=sprintf"0x%02x\n",$last&0xff;
+}
+
+sub encvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	# favor 3-way issue Opteron pipeline...
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	mov	0($sbox,$acc0,8),$t0
+	mov	0($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	movzb	`&lo("$s3")`,$acc2
+	xor	3($sbox,$acc0,8),$t0
+	xor	3($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t3
+
+	movzb	`&hi("$s3")`,$acc0
+	shr	\$16,$s2
+	movzb	`&hi("$s0")`,$acc2
+	xor	3($sbox,$acc0,8),$t2
+	shr	\$16,$s3
+	xor	3($sbox,$acc2,8),$t3
+
+	shr	\$16,$s1
+	lea	16($key),$key
+	shr	\$16,$s0
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	xor	2($sbox,$acc0,8),$t0
+	xor	2($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	movzb	`&lo("$s1")`,$acc2
+	xor	1($sbox,$acc0,8),$t0
+	xor	1($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t3
+
+	mov	12($key),$s3
+	movzb	`&hi("$s1")`,$acc1
+	movzb	`&hi("$s2")`,$acc2
+	mov	0($key),$s0
+	xor	1($sbox,$acc1,8),$t2
+	xor	1($sbox,$acc2,8),$t3
+
+	mov	4($key),$s1
+	mov	8($key),$s2
+	xor	$t0,$s0
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub enclastvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	movzb	2($sbox,$acc0,8),$t0
+	movzb	2($sbox,$acc1,8),$t1
+	movzb	2($sbox,$acc2,8),$t2
+
+	movzb	`&lo("$s3")`,$acc0
+	movzb	`&hi("$s1")`,$acc1
+	movzb	`&hi("$s2")`,$acc2
+	movzb	2($sbox,$acc0,8),$t3
+	mov	0($sbox,$acc1,8),$acc1	#$t0
+	mov	0($sbox,$acc2,8),$acc2	#$t1
+
+	and	\$0x0000ff00,$acc1
+	and	\$0x0000ff00,$acc2
+
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+	shr	\$16,$s2
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	shr	\$16,$s3
+	mov	0($sbox,$acc0,8),$acc0	#$t2
+	mov	0($sbox,$acc1,8),$acc1	#$t3
+
+	and	\$0x0000ff00,$acc0
+	and	\$0x0000ff00,$acc1
+	shr	\$16,$s1
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+	shr	\$16,$s0
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	mov	0($sbox,$acc0,8),$acc0	#$t0
+	mov	0($sbox,$acc1,8),$acc1	#$t1
+	mov	0($sbox,$acc2,8),$acc2	#$t2
+
+	and	\$0x00ff0000,$acc0
+	and	\$0x00ff0000,$acc1
+	and	\$0x00ff0000,$acc2
+
+	xor	$acc0,$t0
+	xor	$acc1,$t1
+	xor	$acc2,$t2
+
+	movzb	`&lo("$s1")`,$acc0
+	movzb	`&hi("$s3")`,$acc1
+	movzb	`&hi("$s0")`,$acc2
+	mov	0($sbox,$acc0,8),$acc0	#$t3
+	mov	2($sbox,$acc1,8),$acc1	#$t0
+	mov	2($sbox,$acc2,8),$acc2	#$t1
+
+	and	\$0x00ff0000,$acc0
+	and	\$0xff000000,$acc1
+	and	\$0xff000000,$acc2
+
+	xor	$acc0,$t3
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	mov	16+12($key),$s3
+	mov	2($sbox,$acc0,8),$acc0	#$t2
+	mov	2($sbox,$acc1,8),$acc1	#$t3
+	mov	16+0($key),$s0
+
+	and	\$0xff000000,$acc0
+	and	\$0xff000000,$acc1
+
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+
+	mov	16+4($key),$s1
+	mov	16+8($key),$s2
+	xor	$t0,$s0
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub encstep()
+{ my ($i,@s) = @_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	if ($i==3) {
+		$tmp0=$s[1];
+		$tmp1=$s[2];
+		$tmp2=$s[3];
+	}
+	$code.="	movzb	".&lo($s[0]).",$out\n";
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+	$code.="	lea	16($key),$key\n"	if ($i==0);
+
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	mov	0($sbox,$out,8),$out\n";
+
+	$code.="	shr	\$16,$tmp1\n";
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+	$code.="	xor	3($sbox,$tmp0,8),$out\n";
+
+	$code.="	movzb	".&lo($tmp1).",$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+	$code.="	xor	4*$i($key),$out\n";
+
+	$code.="	xor	2($sbox,$tmp1,8),$out\n";
+	$code.="	xor	1($sbox,$tmp2,8),$out\n";
+
+	$code.="	mov	$t0,$s[1]\n"		if ($i==3);
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	mov	$t2,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+sub enclast()
+{ my ($i,@s)=@_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	if ($i==3) {
+		$tmp0=$s[1];
+		$tmp1=$s[2];
+		$tmp2=$s[3];
+	}
+	$code.="	movzb	".&lo($s[0]).",$out\n";
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+
+	$code.="	mov	2($sbox,$out,8),$out\n";
+	$code.="	shr	\$16,$tmp1\n";
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+
+	$code.="	and	\$0x000000ff,$out\n";
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	movzb	".&lo($tmp1).",$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+
+	$code.="	mov	0($sbox,$tmp0,8),$tmp0\n";
+	$code.="	mov	0($sbox,$tmp1,8),$tmp1\n";
+	$code.="	mov	2($sbox,$tmp2,8),$tmp2\n";
+
+	$code.="	and	\$0x0000ff00,$tmp0\n";
+	$code.="	and	\$0x00ff0000,$tmp1\n";
+	$code.="	and	\$0xff000000,$tmp2\n";
+
+	$code.="	xor	$tmp0,$out\n";
+	$code.="	mov	$t0,$s[1]\n"		if ($i==3);
+	$code.="	xor	$tmp1,$out\n";
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	xor	$tmp2,$out\n";
+	$code.="	mov	$t2,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+$code.=<<___;
+.type	_x86_64_AES_encrypt,\@abi-omnipotent
+.align	16
+_x86_64_AES_encrypt:
+	xor	0($key),$s0			# xor with key
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+
+	mov	240($key),$rnds			# load key->rounds
+	sub	\$1,$rnds
+	jmp	.Lenc_loop
+.align	16
+.Lenc_loop:
+___
+	if ($verticalspin) { &encvert(); }
+	else {	&encstep(0,$s0,$s1,$s2,$s3);
+		&encstep(1,$s1,$s2,$s3,$s0);
+		&encstep(2,$s2,$s3,$s0,$s1);
+		&encstep(3,$s3,$s0,$s1,$s2);
+	}
+$code.=<<___;
+	sub	\$1,$rnds
+	jnz	.Lenc_loop
+___
+	if ($verticalspin) { &enclastvert(); }
+	else {	&enclast(0,$s0,$s1,$s2,$s3);
+		&enclast(1,$s1,$s2,$s3,$s0);
+		&enclast(2,$s2,$s3,$s0,$s1);
+		&enclast(3,$s3,$s0,$s1,$s2);
+		$code.=<<___;
+		xor	16+0($key),$s0		# xor with key
+		xor	16+4($key),$s1
+		xor	16+8($key),$s2
+		xor	16+12($key),$s3
+___
+	}
+$code.=<<___;
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_encrypt,.-_x86_64_AES_encrypt
+___
+
+# it's possible to implement this by shifting tN by 8, filling least
+# significant byte with byte load and finally bswap-ing at the end,
+# but such partial register load kills Core 2...
+sub enccompactvert()
+{ my ($t3,$t4,$t5)=("%r8d","%r9d","%r13d");
+
+$code.=<<___;
+	movzb	`&lo("$s0")`,$t0
+	movzb	`&lo("$s1")`,$t1
+	movzb	`&lo("$s2")`,$t2
+	movzb	`&lo("$s3")`,$t3
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	shr	\$16,$s2
+	movzb	`&hi("$s3")`,$acc2
+	movzb	($sbox,$t0,1),$t0
+	movzb	($sbox,$t1,1),$t1
+	movzb	($sbox,$t2,1),$t2
+	movzb	($sbox,$t3,1),$t3
+
+	movzb	($sbox,$acc0,1),$t4	#$t0
+	movzb	`&hi("$s0")`,$acc0
+	movzb	($sbox,$acc1,1),$t5	#$t1
+	movzb	`&lo("$s2")`,$acc1
+	movzb	($sbox,$acc2,1),$acc2	#$t2
+	movzb	($sbox,$acc0,1),$acc0	#$t3
+
+	shl	\$8,$t4
+	shr	\$16,$s3
+	shl	\$8,$t5
+	xor	$t4,$t0
+	shr	\$16,$s0
+	movzb	`&lo("$s3")`,$t4
+	shr	\$16,$s1
+	xor	$t5,$t1
+	shl	\$8,$acc2
+	movzb	`&lo("$s0")`,$t5
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	xor	$acc2,$t2
+
+	shl	\$8,$acc0
+	movzb	`&lo("$s1")`,$acc2
+	shl	\$16,$acc1
+	xor	$acc0,$t3
+	movzb	($sbox,$t4,1),$t4	#$t1
+	movzb	`&hi("$s3")`,$acc0
+	movzb	($sbox,$t5,1),$t5	#$t2
+	xor	$acc1,$t0
+
+	shr	\$8,$s2
+	movzb	`&hi("$s0")`,$acc1
+	shl	\$16,$t4
+	shr	\$8,$s1
+	shl	\$16,$t5
+	xor	$t4,$t1
+	movzb	($sbox,$acc2,1),$acc2	#$t3
+	movzb	($sbox,$acc0,1),$acc0	#$t0
+	movzb	($sbox,$acc1,1),$acc1	#$t1
+	movzb	($sbox,$s2,1),$s3	#$t3
+	movzb	($sbox,$s1,1),$s2	#$t2
+
+	shl	\$16,$acc2
+	xor	$t5,$t2
+	shl	\$24,$acc0
+	xor	$acc2,$t3
+	shl	\$24,$acc1
+	xor	$acc0,$t0
+	shl	\$24,$s3
+	xor	$acc1,$t1
+	shl	\$24,$s2
+	mov	$t0,$s0
+	mov	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub enctransform_ref()
+{ my $sn = shift;
+  my ($acc,$r2,$tmp)=("%r8d","%r9d","%r13d");
+
+$code.=<<___;
+	mov	$sn,$acc
+	and	\$0x80808080,$acc
+	mov	$acc,$tmp
+	shr	\$7,$tmp
+	lea	($sn,$sn),$r2
+	sub	$tmp,$acc
+	and	\$0xfefefefe,$r2
+	and	\$0x1b1b1b1b,$acc
+	mov	$sn,$tmp
+	xor	$acc,$r2
+
+	xor	$r2,$sn
+	rol	\$24,$sn
+	xor	$r2,$sn
+	ror	\$16,$tmp
+	xor	$tmp,$sn
+	ror	\$8,$tmp
+	xor	$tmp,$sn
+___
+}
+
+# unlike decrypt case it does not pay off to parallelize enctransform
+sub enctransform()
+{ my ($t3,$r20,$r21)=($acc2,"%r8d","%r9d");
+
+$code.=<<___;
+	mov	\$0x80808080,$t0
+	mov	\$0x80808080,$t1
+	and	$s0,$t0
+	and	$s1,$t1
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	shr	\$7,$t0
+	lea	($s0,$s0),$r20
+	shr	\$7,$t1
+	lea	($s1,$s1),$r21
+	sub	$t0,$acc0
+	sub	$t1,$acc1
+	and	\$0xfefefefe,$r20
+	and	\$0xfefefefe,$r21
+	and	\$0x1b1b1b1b,$acc0
+	and	\$0x1b1b1b1b,$acc1
+	mov	$s0,$t0
+	mov	$s1,$t1
+	xor	$acc0,$r20
+	xor	$acc1,$r21
+
+	xor	$r20,$s0
+	xor	$r21,$s1
+	 mov	\$0x80808080,$t2
+	rol	\$24,$s0
+	 mov	\$0x80808080,$t3
+	rol	\$24,$s1
+	 and	$s2,$t2
+	 and	$s3,$t3
+	xor	$r20,$s0
+	xor	$r21,$s1
+	 mov	$t2,$acc0
+	ror	\$16,$t0
+	 mov	$t3,$acc1
+	ror	\$16,$t1
+	 lea	($s2,$s2),$r20
+	 shr	\$7,$t2
+	xor	$t0,$s0
+	 shr	\$7,$t3
+	xor	$t1,$s1
+	ror	\$8,$t0
+	 lea	($s3,$s3),$r21
+	ror	\$8,$t1
+	 sub	$t2,$acc0
+	 sub	$t3,$acc1
+	xor	$t0,$s0
+	xor	$t1,$s1
+
+	and	\$0xfefefefe,$r20
+	and	\$0xfefefefe,$r21
+	and	\$0x1b1b1b1b,$acc0
+	and	\$0x1b1b1b1b,$acc1
+	mov	$s2,$t2
+	mov	$s3,$t3
+	xor	$acc0,$r20
+	xor	$acc1,$r21
+
+	ror	\$16,$t2
+	xor	$r20,$s2
+	ror	\$16,$t3
+	xor	$r21,$s3
+	rol	\$24,$s2
+	mov	0($sbox),$acc0			# prefetch Te4
+	rol	\$24,$s3
+	xor	$r20,$s2
+	mov	64($sbox),$acc1
+	xor	$r21,$s3
+	mov	128($sbox),$r20
+	xor	$t2,$s2
+	ror	\$8,$t2
+	xor	$t3,$s3
+	ror	\$8,$t3
+	xor	$t2,$s2
+	mov	192($sbox),$r21
+	xor	$t3,$s3
+___
+}
+
+$code.=<<___;
+.type	_x86_64_AES_encrypt_compact,\@abi-omnipotent
+.align	16
+_x86_64_AES_encrypt_compact:
+	lea	128($sbox),$inp			# size optimization
+	mov	0-128($inp),$acc1		# prefetch Te4
+	mov	32-128($inp),$acc2
+	mov	64-128($inp),$t0
+	mov	96-128($inp),$t1
+	mov	128-128($inp),$acc1
+	mov	160-128($inp),$acc2
+	mov	192-128($inp),$t0
+	mov	224-128($inp),$t1
+	jmp	.Lenc_loop_compact
+.align	16
+.Lenc_loop_compact:
+		xor	0($key),$s0		# xor with key
+		xor	4($key),$s1
+		xor	8($key),$s2
+		xor	12($key),$s3
+		lea	16($key),$key
+___
+		&enccompactvert();
+$code.=<<___;
+		cmp	16(%rsp),$key
+		je	.Lenc_compact_done
+___
+		&enctransform();
+$code.=<<___;
+	jmp	.Lenc_loop_compact
+.align	16
+.Lenc_compact_done:
+	xor	0($key),$s0
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_encrypt_compact,.-_x86_64_AES_encrypt_compact
+___
+
+# void AES_encrypt (const void *inp,void *out,const AES_KEY *key);
+$code.=<<___;
+.globl	AES_encrypt
+.type	AES_encrypt,\@function,3
+.align	16
+.globl	asm_AES_encrypt
+.hidden	asm_AES_encrypt
+asm_AES_encrypt:
+AES_encrypt:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	# allocate frame "above" key schedule
+	mov	%rsp,%r10
+	lea	-63(%rdx),%rcx	# %rdx is key argument
+	and	\$-64,%rsp
+	sub	%rsp,%rcx
+	neg	%rcx
+	and	\$0x3c0,%rcx
+	sub	%rcx,%rsp
+	sub	\$32,%rsp
+
+	mov	%rsi,16(%rsp)	# save out
+	mov	%r10,24(%rsp)	# save real stack pointer
+.Lenc_prologue:
+
+	mov	%rdx,$key
+	mov	240($key),$rnds	# load rounds
+
+	mov	0(%rdi),$s0	# load input vector
+	mov	4(%rdi),$s1
+	mov	8(%rdi),$s2
+	mov	12(%rdi),$s3
+
+	shl	\$4,$rnds
+	lea	($key,$rnds),%rbp
+	mov	$key,(%rsp)	# key schedule
+	mov	%rbp,8(%rsp)	# end of key schedule
+
+	# pick Te4 copy which can't "overlap" with stack frame or key schedule
+	lea	.LAES_Te+2048(%rip),$sbox
+	lea	768(%rsp),%rbp
+	sub	$sbox,%rbp
+	and	\$0x300,%rbp
+	lea	($sbox,%rbp),$sbox
+
+	call	_x86_64_AES_encrypt_compact
+
+	mov	16(%rsp),$out	# restore out
+	mov	24(%rsp),%rsi	# restore saved stack pointer
+	mov	$s0,0($out)	# write output vector
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lenc_epilogue:
+	ret
+.size	AES_encrypt,.-AES_encrypt
+___
+
+#------------------------------------------------------------------#
+
+sub decvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	# favor 3-way issue Opteron pipeline...
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	mov	0($sbox,$acc0,8),$t0
+	mov	0($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	movzb	`&lo("$s3")`,$acc2
+	xor	3($sbox,$acc0,8),$t0
+	xor	3($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t3
+
+	movzb	`&hi("$s1")`,$acc0
+	shr	\$16,$s0
+	movzb	`&hi("$s2")`,$acc2
+	xor	3($sbox,$acc0,8),$t2
+	shr	\$16,$s3
+	xor	3($sbox,$acc2,8),$t3
+
+	shr	\$16,$s1
+	lea	16($key),$key
+	shr	\$16,$s2
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	xor	2($sbox,$acc0,8),$t0
+	xor	2($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	movzb	`&lo("$s1")`,$acc2
+	xor	1($sbox,$acc0,8),$t0
+	xor	1($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t3
+
+	movzb	`&hi("$s3")`,$acc0
+	mov	12($key),$s3
+	movzb	`&hi("$s0")`,$acc2
+	xor	1($sbox,$acc0,8),$t2
+	mov	0($key),$s0
+	xor	1($sbox,$acc2,8),$t3
+
+	xor	$t0,$s0
+	mov	4($key),$s1
+	mov	8($key),$s2
+	xor	$t2,$s2
+	xor	$t1,$s1
+	xor	$t3,$s3
+___
+}
+
+sub declastvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	lea	2048($sbox),$sbox	# size optimization
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	movzb	($sbox,$acc0,1),$t0
+	movzb	($sbox,$acc1,1),$t1
+	movzb	($sbox,$acc2,1),$t2
+
+	movzb	`&lo("$s3")`,$acc0
+	movzb	`&hi("$s3")`,$acc1
+	movzb	`&hi("$s0")`,$acc2
+	movzb	($sbox,$acc0,1),$t3
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	movzb	($sbox,$acc2,1),$acc2	#$t1
+
+	shl	\$8,$acc1
+	shl	\$8,$acc2
+
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+	shr	\$16,$s3
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	shr	\$16,$s0
+	movzb	($sbox,$acc0,1),$acc0	#$t2
+	movzb	($sbox,$acc1,1),$acc1	#$t3
+
+	shl	\$8,$acc0
+	shl	\$8,$acc1
+	shr	\$16,$s1
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+	shr	\$16,$s2
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	movzb	($sbox,$acc0,1),$acc0	#$t0
+	movzb	($sbox,$acc1,1),$acc1	#$t1
+	movzb	($sbox,$acc2,1),$acc2	#$t2
+
+	shl	\$16,$acc0
+	shl	\$16,$acc1
+	shl	\$16,$acc2
+
+	xor	$acc0,$t0
+	xor	$acc1,$t1
+	xor	$acc2,$t2
+
+	movzb	`&lo("$s1")`,$acc0
+	movzb	`&hi("$s1")`,$acc1
+	movzb	`&hi("$s2")`,$acc2
+	movzb	($sbox,$acc0,1),$acc0	#$t3
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	movzb	($sbox,$acc2,1),$acc2	#$t1
+
+	shl	\$16,$acc0
+	shl	\$24,$acc1
+	shl	\$24,$acc2
+
+	xor	$acc0,$t3
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	mov	16+12($key),$s3
+	movzb	($sbox,$acc0,1),$acc0	#$t2
+	movzb	($sbox,$acc1,1),$acc1	#$t3
+	mov	16+0($key),$s0
+
+	shl	\$24,$acc0
+	shl	\$24,$acc1
+
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+
+	mov	16+4($key),$s1
+	mov	16+8($key),$s2
+	lea	-2048($sbox),$sbox
+	xor	$t0,$s0
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub decstep()
+{ my ($i,@s) = @_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	$code.="	mov	$s[0],$out\n"		if ($i!=3);
+			$tmp1=$s[2]			if ($i==3);
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+	$code.="	and	\$0xFF,$out\n";
+
+	$code.="	mov	0($sbox,$out,8),$out\n";
+	$code.="	shr	\$16,$tmp1\n";
+			$tmp2=$s[3]			if ($i==3);
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+
+			$tmp0=$s[1]			if ($i==3);
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	and	\$0xFF,$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+
+	$code.="	xor	3($sbox,$tmp0,8),$out\n";
+	$code.="	xor	2($sbox,$tmp1,8),$out\n";
+	$code.="	xor	1($sbox,$tmp2,8),$out\n";
+
+	$code.="	mov	$t2,$s[1]\n"		if ($i==3);
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	mov	$t0,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+sub declast()
+{ my ($i,@s)=@_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	$code.="	mov	$s[0],$out\n"		if ($i!=3);
+			$tmp1=$s[2]			if ($i==3);
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+	$code.="	and	\$0xFF,$out\n";
+
+	$code.="	movzb	2048($sbox,$out,1),$out\n";
+	$code.="	shr	\$16,$tmp1\n";
+			$tmp2=$s[3]			if ($i==3);
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+
+			$tmp0=$s[1]			if ($i==3);
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	and	\$0xFF,$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+
+	$code.="	movzb	2048($sbox,$tmp0,1),$tmp0\n";
+	$code.="	movzb	2048($sbox,$tmp1,1),$tmp1\n";
+	$code.="	movzb	2048($sbox,$tmp2,1),$tmp2\n";
+
+	$code.="	shl	\$8,$tmp0\n";
+	$code.="	shl	\$16,$tmp1\n";
+	$code.="	shl	\$24,$tmp2\n";
+
+	$code.="	xor	$tmp0,$out\n";
+	$code.="	mov	$t2,$s[1]\n"		if ($i==3);
+	$code.="	xor	$tmp1,$out\n";
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	xor	$tmp2,$out\n";
+	$code.="	mov	$t0,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+$code.=<<___;
+.type	_x86_64_AES_decrypt,\@abi-omnipotent
+.align	16
+_x86_64_AES_decrypt:
+	xor	0($key),$s0			# xor with key
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+
+	mov	240($key),$rnds			# load key->rounds
+	sub	\$1,$rnds
+	jmp	.Ldec_loop
+.align	16
+.Ldec_loop:
+___
+	if ($verticalspin) { &decvert(); }
+	else {	&decstep(0,$s0,$s3,$s2,$s1);
+		&decstep(1,$s1,$s0,$s3,$s2);
+		&decstep(2,$s2,$s1,$s0,$s3);
+		&decstep(3,$s3,$s2,$s1,$s0);
+		$code.=<<___;
+		lea	16($key),$key
+		xor	0($key),$s0			# xor with key
+		xor	4($key),$s1
+		xor	8($key),$s2
+		xor	12($key),$s3
+___
+	}
+$code.=<<___;
+	sub	\$1,$rnds
+	jnz	.Ldec_loop
+___
+	if ($verticalspin) { &declastvert(); }
+	else {	&declast(0,$s0,$s3,$s2,$s1);
+		&declast(1,$s1,$s0,$s3,$s2);
+		&declast(2,$s2,$s1,$s0,$s3);
+		&declast(3,$s3,$s2,$s1,$s0);
+		$code.=<<___;
+		xor	16+0($key),$s0			# xor with key
+		xor	16+4($key),$s1
+		xor	16+8($key),$s2
+		xor	16+12($key),$s3
+___
+	}
+$code.=<<___;
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_decrypt,.-_x86_64_AES_decrypt
+___
+
+sub deccompactvert()
+{ my ($t3,$t4,$t5)=("%r8d","%r9d","%r13d");
+
+$code.=<<___;
+	movzb	`&lo("$s0")`,$t0
+	movzb	`&lo("$s1")`,$t1
+	movzb	`&lo("$s2")`,$t2
+	movzb	`&lo("$s3")`,$t3
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	shr	\$16,$s3
+	movzb	`&hi("$s1")`,$acc2
+	movzb	($sbox,$t0,1),$t0
+	movzb	($sbox,$t1,1),$t1
+	movzb	($sbox,$t2,1),$t2
+	movzb	($sbox,$t3,1),$t3
+
+	movzb	($sbox,$acc0,1),$t4	#$t0
+	movzb	`&hi("$s2")`,$acc0
+	movzb	($sbox,$acc1,1),$t5	#$t1
+	movzb	($sbox,$acc2,1),$acc2	#$t2
+	movzb	($sbox,$acc0,1),$acc0	#$t3
+
+	shr	\$16,$s2
+	shl	\$8,$t5
+	shl	\$8,$t4
+	movzb	`&lo("$s2")`,$acc1
+	shr	\$16,$s0
+	xor	$t4,$t0
+	shr	\$16,$s1
+	movzb	`&lo("$s3")`,$t4
+
+	shl	\$8,$acc2
+	xor	$t5,$t1
+	shl	\$8,$acc0
+	movzb	`&lo("$s0")`,$t5
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	xor	$acc2,$t2
+	movzb	`&lo("$s1")`,$acc2
+
+	shl	\$16,$acc1
+	xor	$acc0,$t3
+	movzb	($sbox,$t4,1),$t4	#$t1
+	movzb	`&hi("$s1")`,$acc0
+	movzb	($sbox,$acc2,1),$acc2	#$t3
+	xor	$acc1,$t0
+	movzb	($sbox,$t5,1),$t5	#$t2
+	movzb	`&hi("$s2")`,$acc1
+
+	shl	\$16,$acc2
+	shl	\$16,$t4
+	shl	\$16,$t5
+	xor	$acc2,$t3
+	movzb	`&hi("$s3")`,$acc2
+	xor	$t4,$t1
+	shr	\$8,$s0
+	xor	$t5,$t2
+
+	movzb	($sbox,$acc0,1),$acc0	#$t0
+	movzb	($sbox,$acc1,1),$s1	#$t1
+	movzb	($sbox,$acc2,1),$s2	#$t2
+	movzb	($sbox,$s0,1),$s3	#$t3
+
+	mov	$t0,$s0
+	shl	\$24,$acc0
+	shl	\$24,$s1
+	shl	\$24,$s2
+	xor	$acc0,$s0
+	shl	\$24,$s3
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+# parallelized version! input is pair of 64-bit values: %rax=s1.s0
+# and %rcx=s3.s2, output is four 32-bit values in %eax=s0, %ebx=s1,
+# %ecx=s2 and %edx=s3.
+sub dectransform()
+{ my ($tp10,$tp20,$tp40,$tp80,$acc0)=("%rax","%r8", "%r9", "%r10","%rbx");
+  my ($tp18,$tp28,$tp48,$tp88,$acc8)=("%rcx","%r11","%r12","%r13","%rdx");
+  my $prefetch = shift;
+
+$code.=<<___;
+	mov	$mask80,$tp40
+	mov	$mask80,$tp48
+	and	$tp10,$tp40
+	and	$tp18,$tp48
+	mov	$tp40,$acc0
+	mov	$tp48,$acc8
+	shr	\$7,$tp40
+	lea	($tp10,$tp10),$tp20
+	shr	\$7,$tp48
+	lea	($tp18,$tp18),$tp28
+	sub	$tp40,$acc0
+	sub	$tp48,$acc8
+	and	$maskfe,$tp20
+	and	$maskfe,$tp28
+	and	$mask1b,$acc0
+	and	$mask1b,$acc8
+	xor	$acc0,$tp20
+	xor	$acc8,$tp28
+	mov	$mask80,$tp80
+	mov	$mask80,$tp88
+
+	and	$tp20,$tp80
+	and	$tp28,$tp88
+	mov	$tp80,$acc0
+	mov	$tp88,$acc8
+	shr	\$7,$tp80
+	lea	($tp20,$tp20),$tp40
+	shr	\$7,$tp88
+	lea	($tp28,$tp28),$tp48
+	sub	$tp80,$acc0
+	sub	$tp88,$acc8
+	and	$maskfe,$tp40
+	and	$maskfe,$tp48
+	and	$mask1b,$acc0
+	and	$mask1b,$acc8
+	xor	$acc0,$tp40
+	xor	$acc8,$tp48
+	mov	$mask80,$tp80
+	mov	$mask80,$tp88
+
+	and	$tp40,$tp80
+	and	$tp48,$tp88
+	mov	$tp80,$acc0
+	mov	$tp88,$acc8
+	shr	\$7,$tp80
+	 xor	$tp10,$tp20		# tp2^=tp1
+	shr	\$7,$tp88
+	 xor	$tp18,$tp28		# tp2^=tp1
+	sub	$tp80,$acc0
+	sub	$tp88,$acc8
+	lea	($tp40,$tp40),$tp80
+	lea	($tp48,$tp48),$tp88
+	 xor	$tp10,$tp40		# tp4^=tp1
+	 xor	$tp18,$tp48		# tp4^=tp1
+	and	$maskfe,$tp80
+	and	$maskfe,$tp88
+	and	$mask1b,$acc0
+	and	$mask1b,$acc8
+	xor	$acc0,$tp80
+	xor	$acc8,$tp88
+
+	xor	$tp80,$tp10		# tp1^=tp8
+	xor	$tp88,$tp18		# tp1^=tp8
+	xor	$tp80,$tp20		# tp2^tp1^=tp8
+	xor	$tp88,$tp28		# tp2^tp1^=tp8
+	mov	$tp10,$acc0
+	mov	$tp18,$acc8
+	xor	$tp80,$tp40		# tp4^tp1^=tp8
+	shr	\$32,$acc0
+	xor	$tp88,$tp48		# tp4^tp1^=tp8
+	shr	\$32,$acc8
+	xor	$tp20,$tp80		# tp8^=tp8^tp2^tp1=tp2^tp1
+	rol	\$8,`&LO("$tp10")`	# ROTATE(tp1^tp8,8)
+	xor	$tp28,$tp88		# tp8^=tp8^tp2^tp1=tp2^tp1
+	rol	\$8,`&LO("$tp18")`	# ROTATE(tp1^tp8,8)
+	xor	$tp40,$tp80		# tp2^tp1^=tp8^tp4^tp1=tp8^tp4^tp2
+	rol	\$8,`&LO("$acc0")`	# ROTATE(tp1^tp8,8)
+	xor	$tp48,$tp88		# tp2^tp1^=tp8^tp4^tp1=tp8^tp4^tp2
+
+	rol	\$8,`&LO("$acc8")`	# ROTATE(tp1^tp8,8)
+	xor	`&LO("$tp80")`,`&LO("$tp10")`
+	shr	\$32,$tp80
+	xor	`&LO("$tp88")`,`&LO("$tp18")`
+	shr	\$32,$tp88
+	xor	`&LO("$tp80")`,`&LO("$acc0")`
+	xor	`&LO("$tp88")`,`&LO("$acc8")`
+
+	mov	$tp20,$tp80
+	rol	\$24,`&LO("$tp20")`	# ROTATE(tp2^tp1^tp8,24)
+	mov	$tp28,$tp88
+	rol	\$24,`&LO("$tp28")`	# ROTATE(tp2^tp1^tp8,24)
+	shr	\$32,$tp80
+	xor	`&LO("$tp20")`,`&LO("$tp10")`
+	shr	\$32,$tp88
+	xor	`&LO("$tp28")`,`&LO("$tp18")`
+	rol	\$24,`&LO("$tp80")`	# ROTATE(tp2^tp1^tp8,24)
+	mov	$tp40,$tp20
+	rol	\$24,`&LO("$tp88")`	# ROTATE(tp2^tp1^tp8,24)
+	mov	$tp48,$tp28
+	shr	\$32,$tp20
+	xor	`&LO("$tp80")`,`&LO("$acc0")`
+	shr	\$32,$tp28
+	xor	`&LO("$tp88")`,`&LO("$acc8")`
+
+	`"mov	0($sbox),$mask80"	if ($prefetch)`
+	rol	\$16,`&LO("$tp40")`	# ROTATE(tp4^tp1^tp8,16)
+	`"mov	64($sbox),$maskfe"	if ($prefetch)`
+	rol	\$16,`&LO("$tp48")`	# ROTATE(tp4^tp1^tp8,16)
+	`"mov	128($sbox),$mask1b"	if ($prefetch)`
+	rol	\$16,`&LO("$tp20")`	# ROTATE(tp4^tp1^tp8,16)
+	`"mov	192($sbox),$tp80"	if ($prefetch)`
+	xor	`&LO("$tp40")`,`&LO("$tp10")`
+	rol	\$16,`&LO("$tp28")`	# ROTATE(tp4^tp1^tp8,16)
+	xor	`&LO("$tp48")`,`&LO("$tp18")`
+	`"mov	256($sbox),$tp88"	if ($prefetch)`
+	xor	`&LO("$tp20")`,`&LO("$acc0")`
+	xor	`&LO("$tp28")`,`&LO("$acc8")`
+___
+}
+
+$code.=<<___;
+.type	_x86_64_AES_decrypt_compact,\@abi-omnipotent
+.align	16
+_x86_64_AES_decrypt_compact:
+	lea	128($sbox),$inp			# size optimization
+	mov	0-128($inp),$acc1		# prefetch Td4
+	mov	32-128($inp),$acc2
+	mov	64-128($inp),$t0
+	mov	96-128($inp),$t1
+	mov	128-128($inp),$acc1
+	mov	160-128($inp),$acc2
+	mov	192-128($inp),$t0
+	mov	224-128($inp),$t1
+	jmp	.Ldec_loop_compact
+
+.align	16
+.Ldec_loop_compact:
+		xor	0($key),$s0		# xor with key
+		xor	4($key),$s1
+		xor	8($key),$s2
+		xor	12($key),$s3
+		lea	16($key),$key
+___
+		&deccompactvert();
+$code.=<<___;
+		cmp	16(%rsp),$key
+		je	.Ldec_compact_done
+
+		mov	256+0($sbox),$mask80
+		shl	\$32,%rbx
+		shl	\$32,%rdx
+		mov	256+8($sbox),$maskfe
+		or	%rbx,%rax
+		or	%rdx,%rcx
+		mov	256+16($sbox),$mask1b
+___
+		&dectransform(1);
+$code.=<<___;
+	jmp	.Ldec_loop_compact
+.align	16
+.Ldec_compact_done:
+	xor	0($key),$s0
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_decrypt_compact,.-_x86_64_AES_decrypt_compact
+___
+
+# void AES_decrypt (const void *inp,void *out,const AES_KEY *key);
+$code.=<<___;
+.globl	AES_decrypt
+.type	AES_decrypt,\@function,3
+.align	16
+.globl	asm_AES_decrypt
+.hidden	asm_AES_decrypt
+asm_AES_decrypt:
+AES_decrypt:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	# allocate frame "above" key schedule
+	mov	%rsp,%r10
+	lea	-63(%rdx),%rcx	# %rdx is key argument
+	and	\$-64,%rsp
+	sub	%rsp,%rcx
+	neg	%rcx
+	and	\$0x3c0,%rcx
+	sub	%rcx,%rsp
+	sub	\$32,%rsp
+
+	mov	%rsi,16(%rsp)	# save out
+	mov	%r10,24(%rsp)	# save real stack pointer
+.Ldec_prologue:
+
+	mov	%rdx,$key
+	mov	240($key),$rnds	# load rounds
+
+	mov	0(%rdi),$s0	# load input vector
+	mov	4(%rdi),$s1
+	mov	8(%rdi),$s2
+	mov	12(%rdi),$s3
+
+	shl	\$4,$rnds
+	lea	($key,$rnds),%rbp
+	mov	$key,(%rsp)	# key schedule
+	mov	%rbp,8(%rsp)	# end of key schedule
+
+	# pick Td4 copy which can't "overlap" with stack frame or key schedule
+	lea	.LAES_Td+2048(%rip),$sbox
+	lea	768(%rsp),%rbp
+	sub	$sbox,%rbp
+	and	\$0x300,%rbp
+	lea	($sbox,%rbp),$sbox
+	shr	\$3,%rbp	# recall "magic" constants!
+	add	%rbp,$sbox
+
+	call	_x86_64_AES_decrypt_compact
+
+	mov	16(%rsp),$out	# restore out
+	mov	24(%rsp),%rsi	# restore saved stack pointer
+	mov	$s0,0($out)	# write output vector
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Ldec_epilogue:
+	ret
+.size	AES_decrypt,.-AES_decrypt
+___
+#------------------------------------------------------------------#
+
+sub enckey()
+{
+$code.=<<___;
+	movz	%dl,%esi		# rk[i]>>0
+	movzb	-128(%rbp,%rsi),%ebx
+	movz	%dh,%esi		# rk[i]>>8
+	shl	\$24,%ebx
+	xor	%ebx,%eax
+
+	movzb	-128(%rbp,%rsi),%ebx
+	shr	\$16,%edx
+	movz	%dl,%esi		# rk[i]>>16
+	xor	%ebx,%eax
+
+	movzb	-128(%rbp,%rsi),%ebx
+	movz	%dh,%esi		# rk[i]>>24
+	shl	\$8,%ebx
+	xor	%ebx,%eax
+
+	movzb	-128(%rbp,%rsi),%ebx
+	shl	\$16,%ebx
+	xor	%ebx,%eax
+
+	xor	1024-128(%rbp,%rcx,4),%eax		# rcon
+___
+}
+
+# int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)
+$code.=<<___;
+.globl	AES_set_encrypt_key
+.type	AES_set_encrypt_key,\@function,3
+.align	16
+AES_set_encrypt_key:
+	push	%rbx
+	push	%rbp
+	push	%r12			# redundant, but allows to share 
+	push	%r13			# exception handler...
+	push	%r14
+	push	%r15
+	sub	\$8,%rsp
+.Lenc_key_prologue:
+
+	call	_x86_64_AES_set_encrypt_key
+
+	mov	40(%rsp),%rbp
+	mov	48(%rsp),%rbx
+	add	\$56,%rsp
+.Lenc_key_epilogue:
+	ret
+.size	AES_set_encrypt_key,.-AES_set_encrypt_key
+
+.type	_x86_64_AES_set_encrypt_key,\@abi-omnipotent
+.align	16
+_x86_64_AES_set_encrypt_key:
+	mov	%esi,%ecx			# %ecx=bits
+	mov	%rdi,%rsi			# %rsi=userKey
+	mov	%rdx,%rdi			# %rdi=key
+
+	test	\$-1,%rsi
+	jz	.Lbadpointer
+	test	\$-1,%rdi
+	jz	.Lbadpointer
+
+	lea	.LAES_Te(%rip),%rbp
+	lea	2048+128(%rbp),%rbp
+
+	# prefetch Te4
+	mov	0-128(%rbp),%eax
+	mov	32-128(%rbp),%ebx
+	mov	64-128(%rbp),%r8d
+	mov	96-128(%rbp),%edx
+	mov	128-128(%rbp),%eax
+	mov	160-128(%rbp),%ebx
+	mov	192-128(%rbp),%r8d
+	mov	224-128(%rbp),%edx
+
+	cmp	\$128,%ecx
+	je	.L10rounds
+	cmp	\$192,%ecx
+	je	.L12rounds
+	cmp	\$256,%ecx
+	je	.L14rounds
+	mov	\$-2,%rax			# invalid number of bits
+	jmp	.Lexit
+
+.L10rounds:
+	mov	0(%rsi),%rax			# copy first 4 dwords
+	mov	8(%rsi),%rdx
+	mov	%rax,0(%rdi)
+	mov	%rdx,8(%rdi)
+
+	shr	\$32,%rdx
+	xor	%ecx,%ecx
+	jmp	.L10shortcut
+.align	4
+.L10loop:
+		mov	0(%rdi),%eax			# rk[0]
+		mov	12(%rdi),%edx			# rk[3]
+.L10shortcut:
+___
+		&enckey	();
+$code.=<<___;
+		mov	%eax,16(%rdi)			# rk[4]
+		xor	4(%rdi),%eax
+		mov	%eax,20(%rdi)			# rk[5]
+		xor	8(%rdi),%eax
+		mov	%eax,24(%rdi)			# rk[6]
+		xor	12(%rdi),%eax
+		mov	%eax,28(%rdi)			# rk[7]
+		add	\$1,%ecx
+		lea	16(%rdi),%rdi
+		cmp	\$10,%ecx
+	jl	.L10loop
+
+	movl	\$10,80(%rdi)			# setup number of rounds
+	xor	%rax,%rax
+	jmp	.Lexit
+
+.L12rounds:
+	mov	0(%rsi),%rax			# copy first 6 dwords
+	mov	8(%rsi),%rbx
+	mov	16(%rsi),%rdx
+	mov	%rax,0(%rdi)
+	mov	%rbx,8(%rdi)
+	mov	%rdx,16(%rdi)
+
+	shr	\$32,%rdx
+	xor	%ecx,%ecx
+	jmp	.L12shortcut
+.align	4
+.L12loop:
+		mov	0(%rdi),%eax			# rk[0]
+		mov	20(%rdi),%edx			# rk[5]
+.L12shortcut:
+___
+		&enckey	();
+$code.=<<___;
+		mov	%eax,24(%rdi)			# rk[6]
+		xor	4(%rdi),%eax
+		mov	%eax,28(%rdi)			# rk[7]
+		xor	8(%rdi),%eax
+		mov	%eax,32(%rdi)			# rk[8]
+		xor	12(%rdi),%eax
+		mov	%eax,36(%rdi)			# rk[9]
+
+		cmp	\$7,%ecx
+		je	.L12break
+		add	\$1,%ecx
+
+		xor	16(%rdi),%eax
+		mov	%eax,40(%rdi)			# rk[10]
+		xor	20(%rdi),%eax
+		mov	%eax,44(%rdi)			# rk[11]
+
+		lea	24(%rdi),%rdi
+	jmp	.L12loop
+.L12break:
+	movl	\$12,72(%rdi)		# setup number of rounds
+	xor	%rax,%rax
+	jmp	.Lexit
+
+.L14rounds:		
+	mov	0(%rsi),%rax			# copy first 8 dwords
+	mov	8(%rsi),%rbx
+	mov	16(%rsi),%rcx
+	mov	24(%rsi),%rdx
+	mov	%rax,0(%rdi)
+	mov	%rbx,8(%rdi)
+	mov	%rcx,16(%rdi)
+	mov	%rdx,24(%rdi)
+
+	shr	\$32,%rdx
+	xor	%ecx,%ecx
+	jmp	.L14shortcut
+.align	4
+.L14loop:
+		mov	0(%rdi),%eax			# rk[0]
+		mov	28(%rdi),%edx			# rk[4]
+.L14shortcut:
+___
+		&enckey	();
+$code.=<<___;
+		mov	%eax,32(%rdi)			# rk[8]
+		xor	4(%rdi),%eax
+		mov	%eax,36(%rdi)			# rk[9]
+		xor	8(%rdi),%eax
+		mov	%eax,40(%rdi)			# rk[10]
+		xor	12(%rdi),%eax
+		mov	%eax,44(%rdi)			# rk[11]
+
+		cmp	\$6,%ecx
+		je	.L14break
+		add	\$1,%ecx
+
+		mov	%eax,%edx
+		mov	16(%rdi),%eax			# rk[4]
+		movz	%dl,%esi			# rk[11]>>0
+		movzb	-128(%rbp,%rsi),%ebx
+		movz	%dh,%esi			# rk[11]>>8
+		xor	%ebx,%eax
+
+		movzb	-128(%rbp,%rsi),%ebx
+		shr	\$16,%edx
+		shl	\$8,%ebx
+		movz	%dl,%esi			# rk[11]>>16
+		xor	%ebx,%eax
+
+		movzb	-128(%rbp,%rsi),%ebx
+		movz	%dh,%esi			# rk[11]>>24
+		shl	\$16,%ebx
+		xor	%ebx,%eax
+
+		movzb	-128(%rbp,%rsi),%ebx
+		shl	\$24,%ebx
+		xor	%ebx,%eax
+
+		mov	%eax,48(%rdi)			# rk[12]
+		xor	20(%rdi),%eax
+		mov	%eax,52(%rdi)			# rk[13]
+		xor	24(%rdi),%eax
+		mov	%eax,56(%rdi)			# rk[14]
+		xor	28(%rdi),%eax
+		mov	%eax,60(%rdi)			# rk[15]
+
+		lea	32(%rdi),%rdi
+	jmp	.L14loop
+.L14break:
+	movl	\$14,48(%rdi)		# setup number of rounds
+	xor	%rax,%rax
+	jmp	.Lexit
+
+.Lbadpointer:
+	mov	\$-1,%rax
+.Lexit:
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_set_encrypt_key,.-_x86_64_AES_set_encrypt_key
+___
+
+sub deckey_ref()
+{ my ($i,$ptr,$te,$td) = @_;
+  my ($tp1,$tp2,$tp4,$tp8,$acc)=("%eax","%ebx","%edi","%edx","%r8d");
+$code.=<<___;
+	mov	$i($ptr),$tp1
+	mov	$tp1,$acc
+	and	\$0x80808080,$acc
+	mov	$acc,$tp4
+	shr	\$7,$tp4
+	lea	0($tp1,$tp1),$tp2
+	sub	$tp4,$acc
+	and	\$0xfefefefe,$tp2
+	and	\$0x1b1b1b1b,$acc
+	xor	$tp2,$acc
+	mov	$acc,$tp2
+
+	and	\$0x80808080,$acc
+	mov	$acc,$tp8
+	shr	\$7,$tp8
+	lea	0($tp2,$tp2),$tp4
+	sub	$tp8,$acc
+	and	\$0xfefefefe,$tp4
+	and	\$0x1b1b1b1b,$acc
+	 xor	$tp1,$tp2		# tp2^tp1
+	xor	$tp4,$acc
+	mov	$acc,$tp4
+
+	and	\$0x80808080,$acc
+	mov	$acc,$tp8
+	shr	\$7,$tp8
+	sub	$tp8,$acc
+	lea	0($tp4,$tp4),$tp8
+	 xor	$tp1,$tp4		# tp4^tp1
+	and	\$0xfefefefe,$tp8
+	and	\$0x1b1b1b1b,$acc
+	xor	$acc,$tp8
+
+	xor	$tp8,$tp1		# tp1^tp8
+	rol	\$8,$tp1		# ROTATE(tp1^tp8,8)
+	xor	$tp8,$tp2		# tp2^tp1^tp8
+	xor	$tp8,$tp4		# tp4^tp1^tp8
+	xor	$tp2,$tp8
+	xor	$tp4,$tp8		# tp8^(tp8^tp4^tp1)^(tp8^tp2^tp1)=tp8^tp4^tp2
+
+	xor	$tp8,$tp1
+	rol	\$24,$tp2		# ROTATE(tp2^tp1^tp8,24)
+	xor	$tp2,$tp1
+	rol	\$16,$tp4		# ROTATE(tp4^tp1^tp8,16)
+	xor	$tp4,$tp1
+
+	mov	$tp1,$i($ptr)
+___
+}
+
+# int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)
+$code.=<<___;
+.globl	AES_set_decrypt_key
+.type	AES_set_decrypt_key,\@function,3
+.align	16
+AES_set_decrypt_key:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	push	%rdx			# save key schedule
+.Ldec_key_prologue:
+
+	call	_x86_64_AES_set_encrypt_key
+	mov	(%rsp),%r8		# restore key schedule
+	cmp	\$0,%eax
+	jne	.Labort
+
+	mov	240(%r8),%r14d		# pull number of rounds
+	xor	%rdi,%rdi
+	lea	(%rdi,%r14d,4),%rcx
+	mov	%r8,%rsi
+	lea	(%r8,%rcx,4),%rdi	# pointer to last chunk
+.align	4
+.Linvert:
+		mov	0(%rsi),%rax
+		mov	8(%rsi),%rbx
+		mov	0(%rdi),%rcx
+		mov	8(%rdi),%rdx
+		mov	%rax,0(%rdi)
+		mov	%rbx,8(%rdi)
+		mov	%rcx,0(%rsi)
+		mov	%rdx,8(%rsi)
+		lea	16(%rsi),%rsi
+		lea	-16(%rdi),%rdi
+		cmp	%rsi,%rdi
+	jne	.Linvert
+
+	lea	.LAES_Te+2048+1024(%rip),%rax	# rcon
+
+	mov	40(%rax),$mask80
+	mov	48(%rax),$maskfe
+	mov	56(%rax),$mask1b
+
+	mov	%r8,$key
+	sub	\$1,%r14d
+.align	4
+.Lpermute:
+		lea	16($key),$key
+		mov	0($key),%rax
+		mov	8($key),%rcx
+___
+		&dectransform ();
+$code.=<<___;
+		mov	%eax,0($key)
+		mov	%ebx,4($key)
+		mov	%ecx,8($key)
+		mov	%edx,12($key)
+		sub	\$1,%r14d
+	jnz	.Lpermute
+
+	xor	%rax,%rax
+.Labort:
+	mov	8(%rsp),%r15
+	mov	16(%rsp),%r14
+	mov	24(%rsp),%r13
+	mov	32(%rsp),%r12
+	mov	40(%rsp),%rbp
+	mov	48(%rsp),%rbx
+	add	\$56,%rsp
+.Ldec_key_epilogue:
+	ret
+.size	AES_set_decrypt_key,.-AES_set_decrypt_key
+___
+
+# void AES_cbc_encrypt (const void char *inp, unsigned char *out,
+#			size_t length, const AES_KEY *key,
+#			unsigned char *ivp,const int enc);
+{
+# stack frame layout
+# -8(%rsp)		return address
+my $keyp="0(%rsp)";		# one to pass as $key
+my $keyend="8(%rsp)";		# &(keyp->rd_key[4*keyp->rounds])
+my $_rsp="16(%rsp)";		# saved %rsp
+my $_inp="24(%rsp)";		# copy of 1st parameter, inp
+my $_out="32(%rsp)";		# copy of 2nd parameter, out
+my $_len="40(%rsp)";		# copy of 3rd parameter, length
+my $_key="48(%rsp)";		# copy of 4th parameter, key
+my $_ivp="56(%rsp)";		# copy of 5th parameter, ivp
+my $ivec="64(%rsp)";		# ivec[16]
+my $aes_key="80(%rsp)";		# copy of aes_key
+my $mark="80+240(%rsp)";	# copy of aes_key->rounds
+
+$code.=<<___;
+.globl	AES_cbc_encrypt
+.type	AES_cbc_encrypt,\@function,6
+.align	16
+.extern	OPENSSL_ia32cap_P
+.globl	asm_AES_cbc_encrypt
+.hidden	asm_AES_cbc_encrypt
+asm_AES_cbc_encrypt:
+AES_cbc_encrypt:
+	cmp	\$0,%rdx	# check length
+	je	.Lcbc_epilogue
+	pushfq
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lcbc_prologue:
+
+	cld
+	mov	%r9d,%r9d	# clear upper half of enc
+
+	lea	.LAES_Te(%rip),$sbox
+	cmp	\$0,%r9
+	jne	.Lcbc_picked_te
+	lea	.LAES_Td(%rip),$sbox
+.Lcbc_picked_te:
+
+	mov	OPENSSL_ia32cap_P(%rip),%r10d
+	cmp	\$$speed_limit,%rdx
+	jb	.Lcbc_slow_prologue
+	test	\$15,%rdx
+	jnz	.Lcbc_slow_prologue
+	bt	\$28,%r10d
+	jc	.Lcbc_slow_prologue
+
+	# allocate aligned stack frame...
+	lea	-88-248(%rsp),$key
+	and	\$-64,$key
+
+	# ... and make sure it doesn't alias with AES_T[ed] modulo 4096
+	mov	$sbox,%r10
+	lea	2304($sbox),%r11
+	mov	$key,%r12
+	and	\$0xFFF,%r10	# s = $sbox&0xfff
+	and	\$0xFFF,%r11	# e = ($sbox+2048)&0xfff
+	and	\$0xFFF,%r12	# p = %rsp&0xfff
+
+	cmp	%r11,%r12	# if (p=>e) %rsp =- (p-e);
+	jb	.Lcbc_te_break_out
+	sub	%r11,%r12
+	sub	%r12,$key
+	jmp	.Lcbc_te_ok
+.Lcbc_te_break_out:		# else %rsp -= (p-s)&0xfff + framesz
+	sub	%r10,%r12
+	and	\$0xFFF,%r12
+	add	\$320,%r12
+	sub	%r12,$key
+.align	4
+.Lcbc_te_ok:
+
+	xchg	%rsp,$key
+	#add	\$8,%rsp	# reserve for return address!
+	mov	$key,$_rsp	# save %rsp
+.Lcbc_fast_body:
+	mov	%rdi,$_inp	# save copy of inp
+	mov	%rsi,$_out	# save copy of out
+	mov	%rdx,$_len	# save copy of len
+	mov	%rcx,$_key	# save copy of key
+	mov	%r8,$_ivp	# save copy of ivp
+	movl	\$0,$mark	# copy of aes_key->rounds = 0;
+	mov	%r8,%rbp	# rearrange input arguments
+	mov	%r9,%rbx
+	mov	%rsi,$out
+	mov	%rdi,$inp
+	mov	%rcx,$key
+
+	mov	240($key),%eax		# key->rounds
+	# do we copy key schedule to stack?
+	mov	$key,%r10
+	sub	$sbox,%r10
+	and	\$0xfff,%r10
+	cmp	\$2304,%r10
+	jb	.Lcbc_do_ecopy
+	cmp	\$4096-248,%r10
+	jb	.Lcbc_skip_ecopy
+.align	4
+.Lcbc_do_ecopy:
+		mov	$key,%rsi
+		lea	$aes_key,%rdi
+		lea	$aes_key,$key
+		mov	\$240/8,%ecx
+		.long	0x90A548F3	# rep movsq
+		mov	%eax,(%rdi)	# copy aes_key->rounds
+.Lcbc_skip_ecopy:
+	mov	$key,$keyp	# save key pointer
+
+	mov	\$18,%ecx
+.align	4
+.Lcbc_prefetch_te:
+		mov	0($sbox),%r10
+		mov	32($sbox),%r11
+		mov	64($sbox),%r12
+		mov	96($sbox),%r13
+		lea	128($sbox),$sbox
+		sub	\$1,%ecx
+	jnz	.Lcbc_prefetch_te
+	lea	-2304($sbox),$sbox
+
+	cmp	\$0,%rbx
+	je	.LFAST_DECRYPT
+
+#----------------------------- ENCRYPT -----------------------------#
+	mov	0(%rbp),$s0		# load iv
+	mov	4(%rbp),$s1
+	mov	8(%rbp),$s2
+	mov	12(%rbp),$s3
+
+.align	4
+.Lcbc_fast_enc_loop:
+		xor	0($inp),$s0
+		xor	4($inp),$s1
+		xor	8($inp),$s2
+		xor	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# if ($verticalspin) save inp
+
+		call	_x86_64_AES_encrypt
+
+		mov	$_inp,$inp	# if ($verticalspin) restore inp
+		mov	$_len,%r10
+		mov	$s0,0($out)
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+		sub	\$16,%r10
+		test	\$-16,%r10
+		mov	%r10,$_len
+	jnz	.Lcbc_fast_enc_loop
+	mov	$_ivp,%rbp	# restore ivp
+	mov	$s0,0(%rbp)	# save ivec
+	mov	$s1,4(%rbp)
+	mov	$s2,8(%rbp)
+	mov	$s3,12(%rbp)
+
+	jmp	.Lcbc_fast_cleanup
+
+#----------------------------- DECRYPT -----------------------------#
+.align	16
+.LFAST_DECRYPT:
+	cmp	$inp,$out
+	je	.Lcbc_fast_dec_in_place
+
+	mov	%rbp,$ivec
+.align	4
+.Lcbc_fast_dec_loop:
+		mov	0($inp),$s0	# read input
+		mov	4($inp),$s1
+		mov	8($inp),$s2
+		mov	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# if ($verticalspin) save inp
+
+		call	_x86_64_AES_decrypt
+
+		mov	$ivec,%rbp	# load ivp
+		mov	$_inp,$inp	# if ($verticalspin) restore inp
+		mov	$_len,%r10	# load len
+		xor	0(%rbp),$s0	# xor iv
+		xor	4(%rbp),$s1
+		xor	8(%rbp),$s2
+		xor	12(%rbp),$s3
+		mov	$inp,%rbp	# current input, next iv
+
+		sub	\$16,%r10
+		mov	%r10,$_len	# update len
+		mov	%rbp,$ivec	# update ivp
+
+		mov	$s0,0($out)	# write output
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+	jnz	.Lcbc_fast_dec_loop
+	mov	$_ivp,%r12		# load user ivp
+	mov	0(%rbp),%r10		# load iv
+	mov	8(%rbp),%r11
+	mov	%r10,0(%r12)		# copy back to user
+	mov	%r11,8(%r12)
+	jmp	.Lcbc_fast_cleanup
+
+.align	16
+.Lcbc_fast_dec_in_place:
+	mov	0(%rbp),%r10		# copy iv to stack
+	mov	8(%rbp),%r11
+	mov	%r10,0+$ivec
+	mov	%r11,8+$ivec
+.align	4
+.Lcbc_fast_dec_in_place_loop:
+		mov	0($inp),$s0	# load input
+		mov	4($inp),$s1
+		mov	8($inp),$s2
+		mov	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# if ($verticalspin) save inp
+
+		call	_x86_64_AES_decrypt
+
+		mov	$_inp,$inp	# if ($verticalspin) restore inp
+		mov	$_len,%r10
+		xor	0+$ivec,$s0
+		xor	4+$ivec,$s1
+		xor	8+$ivec,$s2
+		xor	12+$ivec,$s3
+
+		mov	0($inp),%r11	# load input
+		mov	8($inp),%r12
+		sub	\$16,%r10
+		jz	.Lcbc_fast_dec_in_place_done
+
+		mov	%r11,0+$ivec	# copy input to iv
+		mov	%r12,8+$ivec
+
+		mov	$s0,0($out)	# save output [zaps input]
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+		mov	%r10,$_len
+	jmp	.Lcbc_fast_dec_in_place_loop
+.Lcbc_fast_dec_in_place_done:
+	mov	$_ivp,%rdi
+	mov	%r11,0(%rdi)	# copy iv back to user
+	mov	%r12,8(%rdi)
+
+	mov	$s0,0($out)	# save output [zaps input]
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+.align	4
+.Lcbc_fast_cleanup:
+	cmpl	\$0,$mark	# was the key schedule copied?
+	lea	$aes_key,%rdi
+	je	.Lcbc_exit
+		mov	\$240/8,%ecx
+		xor	%rax,%rax
+		.long	0x90AB48F3	# rep stosq
+
+	jmp	.Lcbc_exit
+
+#--------------------------- SLOW ROUTINE ---------------------------#
+.align	16
+.Lcbc_slow_prologue:
+	# allocate aligned stack frame...
+	lea	-88(%rsp),%rbp
+	and	\$-64,%rbp
+	# ... just "above" key schedule
+	lea	-88-63(%rcx),%r10
+	sub	%rbp,%r10
+	neg	%r10
+	and	\$0x3c0,%r10
+	sub	%r10,%rbp
+
+	xchg	%rsp,%rbp
+	#add	\$8,%rsp	# reserve for return address!
+	mov	%rbp,$_rsp	# save %rsp
+.Lcbc_slow_body:
+	#mov	%rdi,$_inp	# save copy of inp
+	#mov	%rsi,$_out	# save copy of out
+	#mov	%rdx,$_len	# save copy of len
+	#mov	%rcx,$_key	# save copy of key
+	mov	%r8,$_ivp	# save copy of ivp
+	mov	%r8,%rbp	# rearrange input arguments
+	mov	%r9,%rbx
+	mov	%rsi,$out
+	mov	%rdi,$inp
+	mov	%rcx,$key
+	mov	%rdx,%r10
+
+	mov	240($key),%eax
+	mov	$key,$keyp	# save key pointer
+	shl	\$4,%eax
+	lea	($key,%rax),%rax
+	mov	%rax,$keyend
+
+	# pick Te4 copy which can't "overlap" with stack frame or key scdedule
+	lea	2048($sbox),$sbox
+	lea	768-8(%rsp),%rax
+	sub	$sbox,%rax
+	and	\$0x300,%rax
+	lea	($sbox,%rax),$sbox
+
+	cmp	\$0,%rbx
+	je	.LSLOW_DECRYPT
+
+#--------------------------- SLOW ENCRYPT ---------------------------#
+	test	\$-16,%r10		# check upon length
+	mov	0(%rbp),$s0		# load iv
+	mov	4(%rbp),$s1
+	mov	8(%rbp),$s2
+	mov	12(%rbp),$s3
+	jz	.Lcbc_slow_enc_tail	# short input...
+
+.align	4
+.Lcbc_slow_enc_loop:
+		xor	0($inp),$s0
+		xor	4($inp),$s1
+		xor	8($inp),$s2
+		xor	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# save inp
+		mov	$out,$_out	# save out
+		mov	%r10,$_len	# save len
+
+		call	_x86_64_AES_encrypt_compact
+
+		mov	$_inp,$inp	# restore inp
+		mov	$_out,$out	# restore out
+		mov	$_len,%r10	# restore len
+		mov	$s0,0($out)
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+		sub	\$16,%r10
+		test	\$-16,%r10
+	jnz	.Lcbc_slow_enc_loop
+	test	\$15,%r10
+	jnz	.Lcbc_slow_enc_tail
+	mov	$_ivp,%rbp	# restore ivp
+	mov	$s0,0(%rbp)	# save ivec
+	mov	$s1,4(%rbp)
+	mov	$s2,8(%rbp)
+	mov	$s3,12(%rbp)
+
+	jmp	.Lcbc_exit
+
+.align	4
+.Lcbc_slow_enc_tail:
+	mov	%rax,%r11
+	mov	%rcx,%r12
+	mov	%r10,%rcx
+	mov	$inp,%rsi
+	mov	$out,%rdi
+	.long	0x9066A4F3		# rep movsb
+	mov	\$16,%rcx		# zero tail
+	sub	%r10,%rcx
+	xor	%rax,%rax
+	.long	0x9066AAF3		# rep stosb
+	mov	$out,$inp		# this is not a mistake!
+	mov	\$16,%r10		# len=16
+	mov	%r11,%rax
+	mov	%r12,%rcx
+	jmp	.Lcbc_slow_enc_loop	# one more spin...
+#--------------------------- SLOW DECRYPT ---------------------------#
+.align	16
+.LSLOW_DECRYPT:
+	shr	\$3,%rax
+	add	%rax,$sbox		# recall "magic" constants!
+
+	mov	0(%rbp),%r11		# copy iv to stack
+	mov	8(%rbp),%r12
+	mov	%r11,0+$ivec
+	mov	%r12,8+$ivec
+
+.align	4
+.Lcbc_slow_dec_loop:
+		mov	0($inp),$s0	# load input
+		mov	4($inp),$s1
+		mov	8($inp),$s2
+		mov	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# save inp
+		mov	$out,$_out	# save out
+		mov	%r10,$_len	# save len
+
+		call	_x86_64_AES_decrypt_compact
+
+		mov	$_inp,$inp	# restore inp
+		mov	$_out,$out	# restore out
+		mov	$_len,%r10
+		xor	0+$ivec,$s0
+		xor	4+$ivec,$s1
+		xor	8+$ivec,$s2
+		xor	12+$ivec,$s3
+
+		mov	0($inp),%r11	# load input
+		mov	8($inp),%r12
+		sub	\$16,%r10
+		jc	.Lcbc_slow_dec_partial
+		jz	.Lcbc_slow_dec_done
+
+		mov	%r11,0+$ivec	# copy input to iv
+		mov	%r12,8+$ivec
+
+		mov	$s0,0($out)	# save output [can zap input]
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+	jmp	.Lcbc_slow_dec_loop
+.Lcbc_slow_dec_done:
+	mov	$_ivp,%rdi
+	mov	%r11,0(%rdi)		# copy iv back to user
+	mov	%r12,8(%rdi)
+
+	mov	$s0,0($out)		# save output [can zap input]
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+	jmp	.Lcbc_exit
+
+.align	4
+.Lcbc_slow_dec_partial:
+	mov	$_ivp,%rdi
+	mov	%r11,0(%rdi)		# copy iv back to user
+	mov	%r12,8(%rdi)
+
+	mov	$s0,0+$ivec		# save output to stack
+	mov	$s1,4+$ivec
+	mov	$s2,8+$ivec
+	mov	$s3,12+$ivec
+
+	mov	$out,%rdi
+	lea	$ivec,%rsi
+	lea	16(%r10),%rcx
+	.long	0x9066A4F3	# rep movsb
+	jmp	.Lcbc_exit
+
+.align	16
+.Lcbc_exit:
+	mov	$_rsp,%rsi
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lcbc_popfq:
+	popfq
+.Lcbc_epilogue:
+	ret
+.size	AES_cbc_encrypt,.-AES_cbc_encrypt
+___
+}
+
+$code.=<<___;
+.align	64
+.LAES_Te:
+___
+	&_data_word(0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6);
+	&_data_word(0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591);
+	&_data_word(0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56);
+	&_data_word(0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec);
+	&_data_word(0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa);
+	&_data_word(0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb);
+	&_data_word(0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45);
+	&_data_word(0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b);
+	&_data_word(0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c);
+	&_data_word(0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83);
+	&_data_word(0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9);
+	&_data_word(0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a);
+	&_data_word(0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d);
+	&_data_word(0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f);
+	&_data_word(0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df);
+	&_data_word(0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea);
+	&_data_word(0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34);
+	&_data_word(0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b);
+	&_data_word(0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d);
+	&_data_word(0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413);
+	&_data_word(0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1);
+	&_data_word(0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6);
+	&_data_word(0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972);
+	&_data_word(0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85);
+	&_data_word(0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed);
+	&_data_word(0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511);
+	&_data_word(0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe);
+	&_data_word(0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b);
+	&_data_word(0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05);
+	&_data_word(0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1);
+	&_data_word(0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142);
+	&_data_word(0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf);
+	&_data_word(0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3);
+	&_data_word(0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e);
+	&_data_word(0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a);
+	&_data_word(0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6);
+	&_data_word(0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3);
+	&_data_word(0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b);
+	&_data_word(0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428);
+	&_data_word(0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad);
+	&_data_word(0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14);
+	&_data_word(0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8);
+	&_data_word(0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4);
+	&_data_word(0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2);
+	&_data_word(0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda);
+	&_data_word(0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949);
+	&_data_word(0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf);
+	&_data_word(0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810);
+	&_data_word(0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c);
+	&_data_word(0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697);
+	&_data_word(0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e);
+	&_data_word(0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f);
+	&_data_word(0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc);
+	&_data_word(0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c);
+	&_data_word(0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969);
+	&_data_word(0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27);
+	&_data_word(0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122);
+	&_data_word(0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433);
+	&_data_word(0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9);
+	&_data_word(0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5);
+	&_data_word(0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a);
+	&_data_word(0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0);
+	&_data_word(0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e);
+	&_data_word(0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c);
+
+#Te4	# four copies of Te4 to choose from to avoid L1 aliasing
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+#rcon:
+$code.=<<___;
+	.long	0x00000001, 0x00000002, 0x00000004, 0x00000008
+	.long	0x00000010, 0x00000020, 0x00000040, 0x00000080
+	.long	0x0000001b, 0x00000036, 0x80808080, 0x80808080
+	.long	0xfefefefe, 0xfefefefe, 0x1b1b1b1b, 0x1b1b1b1b
+___
+$code.=<<___;
+.align	64
+.LAES_Td:
+___
+	&_data_word(0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a);
+	&_data_word(0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b);
+	&_data_word(0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5);
+	&_data_word(0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5);
+	&_data_word(0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d);
+	&_data_word(0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b);
+	&_data_word(0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295);
+	&_data_word(0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e);
+	&_data_word(0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927);
+	&_data_word(0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d);
+	&_data_word(0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362);
+	&_data_word(0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9);
+	&_data_word(0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52);
+	&_data_word(0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566);
+	&_data_word(0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3);
+	&_data_word(0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed);
+	&_data_word(0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e);
+	&_data_word(0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4);
+	&_data_word(0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4);
+	&_data_word(0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd);
+	&_data_word(0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d);
+	&_data_word(0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060);
+	&_data_word(0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967);
+	&_data_word(0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879);
+	&_data_word(0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000);
+	&_data_word(0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c);
+	&_data_word(0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36);
+	&_data_word(0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624);
+	&_data_word(0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b);
+	&_data_word(0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c);
+	&_data_word(0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12);
+	&_data_word(0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14);
+	&_data_word(0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3);
+	&_data_word(0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b);
+	&_data_word(0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8);
+	&_data_word(0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684);
+	&_data_word(0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7);
+	&_data_word(0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177);
+	&_data_word(0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947);
+	&_data_word(0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322);
+	&_data_word(0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498);
+	&_data_word(0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f);
+	&_data_word(0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54);
+	&_data_word(0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382);
+	&_data_word(0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf);
+	&_data_word(0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb);
+	&_data_word(0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83);
+	&_data_word(0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef);
+	&_data_word(0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029);
+	&_data_word(0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235);
+	&_data_word(0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733);
+	&_data_word(0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117);
+	&_data_word(0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4);
+	&_data_word(0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546);
+	&_data_word(0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb);
+	&_data_word(0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d);
+	&_data_word(0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb);
+	&_data_word(0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a);
+	&_data_word(0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773);
+	&_data_word(0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478);
+	&_data_word(0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2);
+	&_data_word(0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff);
+	&_data_word(0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664);
+	&_data_word(0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0);
+
+#Td4:	# four copies of Td4 to choose from to avoid L1 aliasing
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+___
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+___
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+___
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+.asciz  "AES for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	block_se_handler,\@abi-omnipotent
+.align	16
+block_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_block_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_block_prologue
+
+	mov	24(%rax),%rax		# pull saved real stack pointer
+	lea	48(%rax),%rax		# adjust...
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_block_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	block_se_handler,.-block_se_handler
+
+.type	key_se_handler,\@abi-omnipotent
+.align	16
+key_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_key_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_key_prologue
+
+	lea	56(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_key_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	key_se_handler,.-key_se_handler
+
+.type	cbc_se_handler,\@abi-omnipotent
+.align	16
+cbc_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lcbc_prologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_prologue
+	jb	.Lin_cbc_prologue
+
+	lea	.Lcbc_fast_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_fast_body
+	jb	.Lin_cbc_frame_setup
+
+	lea	.Lcbc_slow_prologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_slow_prologue
+	jb	.Lin_cbc_body
+
+	lea	.Lcbc_slow_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_slow_body
+	jb	.Lin_cbc_frame_setup
+
+.Lin_cbc_body:
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lcbc_epilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lcbc_epilogue
+	jae	.Lin_cbc_prologue
+
+	lea	8(%rax),%rax
+
+	lea	.Lcbc_popfq(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lcbc_popfq
+	jae	.Lin_cbc_prologue
+
+	mov	`16-8`(%rax),%rax	# biased $_rsp
+	lea	56(%rax),%rax
+
+.Lin_cbc_frame_setup:
+	mov	-16(%rax),%rbx
+	mov	-24(%rax),%rbp
+	mov	-32(%rax),%r12
+	mov	-40(%rax),%r13
+	mov	-48(%rax),%r14
+	mov	-56(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_cbc_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+.Lcommon_seh_exit:
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	cbc_se_handler,.-cbc_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_AES_encrypt
+	.rva	.LSEH_end_AES_encrypt
+	.rva	.LSEH_info_AES_encrypt
+
+	.rva	.LSEH_begin_AES_decrypt
+	.rva	.LSEH_end_AES_decrypt
+	.rva	.LSEH_info_AES_decrypt
+
+	.rva	.LSEH_begin_AES_set_encrypt_key
+	.rva	.LSEH_end_AES_set_encrypt_key
+	.rva	.LSEH_info_AES_set_encrypt_key
+
+	.rva	.LSEH_begin_AES_set_decrypt_key
+	.rva	.LSEH_end_AES_set_decrypt_key
+	.rva	.LSEH_info_AES_set_decrypt_key
+
+	.rva	.LSEH_begin_AES_cbc_encrypt
+	.rva	.LSEH_end_AES_cbc_encrypt
+	.rva	.LSEH_info_AES_cbc_encrypt
+
+.section	.xdata
+.align	8
+.LSEH_info_AES_encrypt:
+	.byte	9,0,0,0
+	.rva	block_se_handler
+	.rva	.Lenc_prologue,.Lenc_epilogue	# HandlerData[]
+.LSEH_info_AES_decrypt:
+	.byte	9,0,0,0
+	.rva	block_se_handler
+	.rva	.Ldec_prologue,.Ldec_epilogue	# HandlerData[]
+.LSEH_info_AES_set_encrypt_key:
+	.byte	9,0,0,0
+	.rva	key_se_handler
+	.rva	.Lenc_key_prologue,.Lenc_key_epilogue	# HandlerData[]
+.LSEH_info_AES_set_decrypt_key:
+	.byte	9,0,0,0
+	.rva	key_se_handler
+	.rva	.Ldec_key_prologue,.Ldec_key_epilogue	# HandlerData[]
+.LSEH_info_AES_cbc_encrypt:
+	.byte	9,0,0,0
+	.rva	cbc_se_handler
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/aesni-x86.pl b/crypto/aes/asm/aesni-x86.pl
new file mode 100644
index 0000000..3dc345b
--- /dev/null
+++ b/crypto/aes/asm/aesni-x86.pl
@@ -0,0 +1,2189 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements support for Intel AES-NI extension. In
+# OpenSSL context it's used with Intel engine, but can also be used as
+# drop-in replacement for crypto/aes/asm/aes-586.pl [see below for
+# details].
+#
+# Performance.
+#
+# To start with see corresponding paragraph in aesni-x86_64.pl...
+# Instead of filling table similar to one found there I've chosen to
+# summarize *comparison* results for raw ECB, CTR and CBC benchmarks.
+# The simplified table below represents 32-bit performance relative
+# to 64-bit one in every given point. Ratios vary for different
+# encryption modes, therefore interval values.
+#
+#	16-byte     64-byte     256-byte    1-KB        8-KB
+#	53-67%      67-84%      91-94%      95-98%      97-99.5%
+#
+# Lower ratios for smaller block sizes are perfectly understandable,
+# because function call overhead is higher in 32-bit mode. Largest
+# 8-KB block performance is virtually same: 32-bit code is less than
+# 1% slower for ECB, CBC and CCM, and ~3% slower otherwise.
+
+# January 2011
+#
+# See aesni-x86_64.pl for details. Unlike x86_64 version this module
+# interleaves at most 6 aes[enc|dec] instructions, because there are
+# not enough registers for 8x interleave [which should be optimal for
+# Sandy Bridge]. Actually, performance results for 6x interleave
+# factor presented in aesni-x86_64.pl (except for CTR) are for this
+# module.
+
+# April 2011
+#
+# Add aesni_xts_[en|de]crypt. Westmere spends 1.50 cycles processing
+# one byte out of 8KB with 128-bit key, Sandy Bridge - 1.09.
+
+$PREFIX="aesni";	# if $PREFIX is set to "AES", the script
+			# generates drop-in replacement for
+			# crypto/aes/asm/aes-586.pl:-)
+$inline=1;		# inline _aesni_[en|de]crypt
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],$0);
+
+if ($PREFIX eq "aesni")	{ $movekey=*movups; }
+else			{ $movekey=*movups; }
+
+$len="eax";
+$rounds="ecx";
+$key="edx";
+$inp="esi";
+$out="edi";
+$rounds_="ebx";	# backup copy for $rounds
+$key_="ebp";	# backup copy for $key
+
+$rndkey0="xmm0";
+$rndkey1="xmm1";
+$inout0="xmm2";
+$inout1="xmm3";
+$inout2="xmm4";
+$inout3="xmm5";	$in1="xmm5";
+$inout4="xmm6";	$in0="xmm6";
+$inout5="xmm7";	$ivec="xmm7";
+
+# AESNI extenstion
+sub aeskeygenassist
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x66,0x0f,0x3a,0xdf,0xc0|($1<<3)|$2,$imm);	}
+}
+sub aescommon
+{ my($opcodelet,$dst,$src)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x66,0x0f,0x38,$opcodelet,0xc0|($1<<3)|$2);}
+}
+sub aesimc	{ aescommon(0xdb,@_); }
+sub aesenc	{ aescommon(0xdc,@_); }
+sub aesenclast	{ aescommon(0xdd,@_); }
+sub aesdec	{ aescommon(0xde,@_); }
+sub aesdeclast	{ aescommon(0xdf,@_); }
+
+# Inline version of internal aesni_[en|de]crypt1
+{ my $sn;
+sub aesni_inline_generate1
+{ my ($p,$inout,$ivec)=@_; $inout=$inout0 if (!defined($inout));
+  $sn++;
+
+    &$movekey		($rndkey0,&QWP(0,$key));
+    &$movekey		($rndkey1,&QWP(16,$key));
+    &xorps		($ivec,$rndkey0)	if (defined($ivec));
+    &lea		($key,&DWP(32,$key));
+    &xorps		($inout,$ivec)		if (defined($ivec));
+    &xorps		($inout,$rndkey0)	if (!defined($ivec));
+    &set_label("${p}1_loop_$sn");
+	eval"&aes${p}	($inout,$rndkey1)";
+	&dec		($rounds);
+	&$movekey	($rndkey1,&QWP(0,$key));
+	&lea		($key,&DWP(16,$key));
+    &jnz		(&label("${p}1_loop_$sn"));
+    eval"&aes${p}last	($inout,$rndkey1)";
+}}
+
+sub aesni_generate1	# fully unrolled loop
+{ my ($p,$inout)=@_; $inout=$inout0 if (!defined($inout));
+
+    &function_begin_B("_aesni_${p}rypt1");
+	&movups		($rndkey0,&QWP(0,$key));
+	&$movekey	($rndkey1,&QWP(0x10,$key));
+	&xorps		($inout,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0x20,$key));
+	&lea		($key,&DWP(0x30,$key));
+	&cmp		($rounds,11);
+	&jb		(&label("${p}128"));
+	&lea		($key,&DWP(0x20,$key));
+	&je		(&label("${p}192"));
+	&lea		($key,&DWP(0x20,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(-0x40,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-0x30,$key));
+    &set_label("${p}192");
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(-0x20,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-0x10,$key));
+    &set_label("${p}128");
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x10,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0x20,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x30,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0x40,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x50,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0x60,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x70,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+    eval"&aes${p}last	($inout,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt1");
+}
+
+# void $PREFIX_encrypt (const void *inp,void *out,const AES_KEY *key);
+&aesni_generate1("enc") if (!$inline);
+&function_begin_B("${PREFIX}_encrypt");
+	&mov	("eax",&wparam(0));
+	&mov	($key,&wparam(2));
+	&movups	($inout0,&QWP(0,"eax"));
+	&mov	($rounds,&DWP(240,$key));
+	&mov	("eax",&wparam(1));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&movups	(&QWP(0,"eax"),$inout0);
+	&ret	();
+&function_end_B("${PREFIX}_encrypt");
+
+# void $PREFIX_decrypt (const void *inp,void *out,const AES_KEY *key);
+&aesni_generate1("dec") if(!$inline);
+&function_begin_B("${PREFIX}_decrypt");
+	&mov	("eax",&wparam(0));
+	&mov	($key,&wparam(2));
+	&movups	($inout0,&QWP(0,"eax"));
+	&mov	($rounds,&DWP(240,$key));
+	&mov	("eax",&wparam(1));
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&movups	(&QWP(0,"eax"),$inout0);
+	&ret	();
+&function_end_B("${PREFIX}_decrypt");
+
+# _aesni_[en|de]cryptN are private interfaces, N denotes interleave
+# factor. Why 3x subroutine were originally used in loops? Even though
+# aes[enc|dec] latency was originally 6, it could be scheduled only
+# every *2nd* cycle. Thus 3x interleave was the one providing optimal
+# utilization, i.e. when subroutine's throughput is virtually same as
+# of non-interleaved subroutine [for number of input blocks up to 3].
+# This is why it makes no sense to implement 2x subroutine.
+# aes[enc|dec] latency in next processor generation is 8, but the
+# instructions can be scheduled every cycle. Optimal interleave for
+# new processor is therefore 8x, but it's unfeasible to accommodate it
+# in XMM registers addreassable in 32-bit mode and therefore 6x is
+# used instead...
+
+sub aesni_generate3
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt3");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&shr		($rounds,1);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&lea		($key,&DWP(32,$key));
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);
+	&pxor		($inout2,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0,$key));
+
+    &set_label("${p}3_loop");
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	&dec		($rounds);
+	eval"&aes${p}	($inout2,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(16,$key));
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	&lea		($key,&DWP(32,$key));
+	eval"&aes${p}	($inout2,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&jnz		(&label("${p}3_loop"));
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}	($inout2,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    eval"&aes${p}last	($inout2,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt3");
+}
+
+# 4x interleave is implemented to improve small block performance,
+# most notably [and naturally] 4 block by ~30%. One can argue that one
+# should have implemented 5x as well, but improvement  would be <20%,
+# so it's not worth it...
+sub aesni_generate4
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt4");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&shr		($rounds,1);
+	&lea		($key,&DWP(32,$key));
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);
+	&pxor		($inout2,$rndkey0);
+	&pxor		($inout3,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0,$key));
+
+    &set_label("${p}4_loop");
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	&dec		($rounds);
+	eval"&aes${p}	($inout2,$rndkey1)";
+	eval"&aes${p}	($inout3,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(16,$key));
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	&lea		($key,&DWP(32,$key));
+	eval"&aes${p}	($inout2,$rndkey0)";
+	eval"&aes${p}	($inout3,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0,$key));
+    &jnz		(&label("${p}4_loop"));
+
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}	($inout2,$rndkey1)";
+    eval"&aes${p}	($inout3,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    eval"&aes${p}last	($inout2,$rndkey0)";
+    eval"&aes${p}last	($inout3,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt4");
+}
+
+sub aesni_generate6
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt6");
+    &static_label("_aesni_${p}rypt6_enter");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&shr		($rounds,1);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&lea		($key,&DWP(32,$key));
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);	# pxor does better here
+	eval"&aes${p}	($inout0,$rndkey1)";
+	&pxor		($inout2,$rndkey0);
+	eval"&aes${p}	($inout1,$rndkey1)";
+	&pxor		($inout3,$rndkey0);
+	&dec		($rounds);
+	eval"&aes${p}	($inout2,$rndkey1)";
+	&pxor		($inout4,$rndkey0);
+	eval"&aes${p}	($inout3,$rndkey1)";
+	&pxor		($inout5,$rndkey0);
+	eval"&aes${p}	($inout4,$rndkey1)";
+	&$movekey	($rndkey0,&QWP(0,$key));
+	eval"&aes${p}	($inout5,$rndkey1)";
+	&jmp		(&label("_aesni_${p}rypt6_enter"));
+
+    &set_label("${p}6_loop",16);
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	&dec		($rounds);
+	eval"&aes${p}	($inout2,$rndkey1)";
+	eval"&aes${p}	($inout3,$rndkey1)";
+	eval"&aes${p}	($inout4,$rndkey1)";
+	eval"&aes${p}	($inout5,$rndkey1)";
+    &set_label("_aesni_${p}rypt6_enter",16);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	&lea		($key,&DWP(32,$key));
+	eval"&aes${p}	($inout2,$rndkey0)";
+	eval"&aes${p}	($inout3,$rndkey0)";
+	eval"&aes${p}	($inout4,$rndkey0)";
+	eval"&aes${p}	($inout5,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0,$key));
+    &jnz		(&label("${p}6_loop"));
+
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}	($inout2,$rndkey1)";
+    eval"&aes${p}	($inout3,$rndkey1)";
+    eval"&aes${p}	($inout4,$rndkey1)";
+    eval"&aes${p}	($inout5,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    eval"&aes${p}last	($inout2,$rndkey0)";
+    eval"&aes${p}last	($inout3,$rndkey0)";
+    eval"&aes${p}last	($inout4,$rndkey0)";
+    eval"&aes${p}last	($inout5,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt6");
+}
+&aesni_generate3("enc") if ($PREFIX eq "aesni");
+&aesni_generate3("dec");
+&aesni_generate4("enc") if ($PREFIX eq "aesni");
+&aesni_generate4("dec");
+&aesni_generate6("enc") if ($PREFIX eq "aesni");
+&aesni_generate6("dec");
+
+if ($PREFIX eq "aesni") {
+######################################################################
+# void aesni_ecb_encrypt (const void *in, void *out,
+#                         size_t length, const AES_KEY *key,
+#                         int enc);
+&function_begin("aesni_ecb_encrypt");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&and	($len,-16);
+	&jz	(&label("ecb_ret"));
+	&mov	($rounds,&DWP(240,$key));
+	&test	($rounds_,$rounds_);
+	&jz	(&label("ecb_decrypt"));
+
+	&mov	($key_,$key);		# backup $key
+	&mov	($rounds_,$rounds);	# backup $rounds
+	&cmp	($len,0x60);
+	&jb	(&label("ecb_enc_tail"));
+
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+	&sub	($len,0x60);
+	&jmp	(&label("ecb_enc_loop6_enter"));
+
+&set_label("ecb_enc_loop6",16);
+	&movups	(&QWP(0,$out),$inout0);
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+&set_label("ecb_enc_loop6_enter");
+
+	&call	("_aesni_encrypt6");
+
+	&mov	($key,$key_);		# restore $key
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&sub	($len,0x60);
+	&jnc	(&label("ecb_enc_loop6"));
+
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&add	($len,0x60);
+	&jz	(&label("ecb_ret"));
+
+&set_label("ecb_enc_tail");
+	&movups	($inout0,&QWP(0,$inp));
+	&cmp	($len,0x20);
+	&jb	(&label("ecb_enc_one"));
+	&movups	($inout1,&QWP(0x10,$inp));
+	&je	(&label("ecb_enc_two"));
+	&movups	($inout2,&QWP(0x20,$inp));
+	&cmp	($len,0x40);
+	&jb	(&label("ecb_enc_three"));
+	&movups	($inout3,&QWP(0x30,$inp));
+	&je	(&label("ecb_enc_four"));
+	&movups	($inout4,&QWP(0x40,$inp));
+	&xorps	($inout5,$inout5);
+	&call	("_aesni_encrypt6");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_one",16);
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&movups	(&QWP(0,$out),$inout0);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_two",16);
+	&xorps	($inout2,$inout2);
+	&call	("_aesni_encrypt3");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_three",16);
+	&call	("_aesni_encrypt3");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_four",16);
+	&call	("_aesni_encrypt4");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&jmp	(&label("ecb_ret"));
+######################################################################
+&set_label("ecb_decrypt",16);
+	&mov	($key_,$key);		# backup $key
+	&mov	($rounds_,$rounds);	# backup $rounds
+	&cmp	($len,0x60);
+	&jb	(&label("ecb_dec_tail"));
+
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+	&sub	($len,0x60);
+	&jmp	(&label("ecb_dec_loop6_enter"));
+
+&set_label("ecb_dec_loop6",16);
+	&movups	(&QWP(0,$out),$inout0);
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+&set_label("ecb_dec_loop6_enter");
+
+	&call	("_aesni_decrypt6");
+
+	&mov	($key,$key_);		# restore $key
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&sub	($len,0x60);
+	&jnc	(&label("ecb_dec_loop6"));
+
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&add	($len,0x60);
+	&jz	(&label("ecb_ret"));
+
+&set_label("ecb_dec_tail");
+	&movups	($inout0,&QWP(0,$inp));
+	&cmp	($len,0x20);
+	&jb	(&label("ecb_dec_one"));
+	&movups	($inout1,&QWP(0x10,$inp));
+	&je	(&label("ecb_dec_two"));
+	&movups	($inout2,&QWP(0x20,$inp));
+	&cmp	($len,0x40);
+	&jb	(&label("ecb_dec_three"));
+	&movups	($inout3,&QWP(0x30,$inp));
+	&je	(&label("ecb_dec_four"));
+	&movups	($inout4,&QWP(0x40,$inp));
+	&xorps	($inout5,$inout5);
+	&call	("_aesni_decrypt6");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_one",16);
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&movups	(&QWP(0,$out),$inout0);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_two",16);
+	&xorps	($inout2,$inout2);
+	&call	("_aesni_decrypt3");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_three",16);
+	&call	("_aesni_decrypt3");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_four",16);
+	&call	("_aesni_decrypt4");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+
+&set_label("ecb_ret");
+&function_end("aesni_ecb_encrypt");
+
+######################################################################
+# void aesni_ccm64_[en|de]crypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec,char *cmac);
+#
+# Handles only complete blocks, operates on 64-bit counter and
+# does not update *ivec! Nor does it finalize CMAC value
+# (see engine/eng_aesni.c for details)
+#
+{ my $cmac=$inout1;
+&function_begin("aesni_ccm64_encrypt_blocks");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&mov	($rounds,&wparam(5));
+	&mov	($key_,"esp");
+	&sub	("esp",60);
+	&and	("esp",-16);			# align stack
+	&mov	(&DWP(48,"esp"),$key_);
+
+	&movdqu	($ivec,&QWP(0,$rounds_));	# load ivec
+	&movdqu	($cmac,&QWP(0,$rounds));	# load cmac
+	&mov	($rounds,&DWP(240,$key));
+
+	# compose byte-swap control mask for pshufb on stack
+	&mov	(&DWP(0,"esp"),0x0c0d0e0f);
+	&mov	(&DWP(4,"esp"),0x08090a0b);
+	&mov	(&DWP(8,"esp"),0x04050607);
+	&mov	(&DWP(12,"esp"),0x00010203);
+
+	# compose counter increment vector on stack
+	&mov	($rounds_,1);
+	&xor	($key_,$key_);
+	&mov	(&DWP(16,"esp"),$rounds_);
+	&mov	(&DWP(20,"esp"),$key_);
+	&mov	(&DWP(24,"esp"),$key_);
+	&mov	(&DWP(28,"esp"),$key_);
+
+	&shr	($rounds,1);
+	&lea	($key_,&DWP(0,$key));
+	&movdqa	($inout3,&QWP(0,"esp"));
+	&movdqa	($inout0,$ivec);
+	&mov	($rounds_,$rounds);
+	&pshufb	($ivec,$inout3);
+
+&set_label("ccm64_enc_outer");
+	&$movekey	($rndkey0,&QWP(0,$key_));
+	&mov		($rounds,$rounds_);
+	&movups		($in0,&QWP(0,$inp));
+
+	&xorps		($inout0,$rndkey0);
+	&$movekey	($rndkey1,&QWP(16,$key_));
+	&xorps		($rndkey0,$in0);
+	&lea		($key,&DWP(32,$key_));
+	&xorps		($cmac,$rndkey0);		# cmac^=inp
+	&$movekey	($rndkey0,&QWP(0,$key));
+
+&set_label("ccm64_enc2_loop");
+	&aesenc		($inout0,$rndkey1);
+	&dec		($rounds);
+	&aesenc		($cmac,$rndkey1);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&aesenc		($inout0,$rndkey0);
+	&lea		($key,&DWP(32,$key));
+	&aesenc		($cmac,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&jnz		(&label("ccm64_enc2_loop"));
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($cmac,$rndkey1);
+	&paddq		($ivec,&QWP(16,"esp"));
+	&aesenclast	($inout0,$rndkey0);
+	&aesenclast	($cmac,$rndkey0);
+
+	&dec	($len);
+	&lea	($inp,&DWP(16,$inp));
+	&xorps	($in0,$inout0);			# inp^=E(ivec)
+	&movdqa	($inout0,$ivec);
+	&movups	(&QWP(0,$out),$in0);		# save output
+	&lea	($out,&DWP(16,$out));
+	&pshufb	($inout0,$inout3);
+	&jnz	(&label("ccm64_enc_outer"));
+
+	&mov	("esp",&DWP(48,"esp"));
+	&mov	($out,&wparam(5));
+	&movups	(&QWP(0,$out),$cmac);
+&function_end("aesni_ccm64_encrypt_blocks");
+
+&function_begin("aesni_ccm64_decrypt_blocks");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&mov	($rounds,&wparam(5));
+	&mov	($key_,"esp");
+	&sub	("esp",60);
+	&and	("esp",-16);			# align stack
+	&mov	(&DWP(48,"esp"),$key_);
+
+	&movdqu	($ivec,&QWP(0,$rounds_));	# load ivec
+	&movdqu	($cmac,&QWP(0,$rounds));	# load cmac
+	&mov	($rounds,&DWP(240,$key));
+
+	# compose byte-swap control mask for pshufb on stack
+	&mov	(&DWP(0,"esp"),0x0c0d0e0f);
+	&mov	(&DWP(4,"esp"),0x08090a0b);
+	&mov	(&DWP(8,"esp"),0x04050607);
+	&mov	(&DWP(12,"esp"),0x00010203);
+
+	# compose counter increment vector on stack
+	&mov	($rounds_,1);
+	&xor	($key_,$key_);
+	&mov	(&DWP(16,"esp"),$rounds_);
+	&mov	(&DWP(20,"esp"),$key_);
+	&mov	(&DWP(24,"esp"),$key_);
+	&mov	(&DWP(28,"esp"),$key_);
+
+	&movdqa	($inout3,&QWP(0,"esp"));	# bswap mask
+	&movdqa	($inout0,$ivec);
+
+	&mov	($key_,$key);
+	&mov	($rounds_,$rounds);
+
+	&pshufb	($ivec,$inout3);
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&movups	($in0,&QWP(0,$inp));		# load inp
+	&paddq	($ivec,&QWP(16,"esp"));
+	&lea	($inp,&QWP(16,$inp));
+	&jmp	(&label("ccm64_dec_outer"));
+
+&set_label("ccm64_dec_outer",16);
+	&xorps	($in0,$inout0);			# inp ^= E(ivec)
+	&movdqa	($inout0,$ivec);
+	&mov	($rounds,$rounds_);
+	&movups	(&QWP(0,$out),$in0);		# save output
+	&lea	($out,&DWP(16,$out));
+	&pshufb	($inout0,$inout3);
+
+	&sub	($len,1);
+	&jz	(&label("ccm64_dec_break"));
+
+	&$movekey	($rndkey0,&QWP(0,$key_));
+	&shr		($rounds,1);
+	&$movekey	($rndkey1,&QWP(16,$key_));
+	&xorps		($in0,$rndkey0);
+	&lea		($key,&DWP(32,$key_));
+	&xorps		($inout0,$rndkey0);
+	&xorps		($cmac,$in0);		# cmac^=out
+	&$movekey	($rndkey0,&QWP(0,$key));
+
+&set_label("ccm64_dec2_loop");
+	&aesenc		($inout0,$rndkey1);
+	&dec		($rounds);
+	&aesenc		($cmac,$rndkey1);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&aesenc		($inout0,$rndkey0);
+	&lea		($key,&DWP(32,$key));
+	&aesenc		($cmac,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&jnz		(&label("ccm64_dec2_loop"));
+	&movups		($in0,&QWP(0,$inp));	# load inp
+	&paddq		($ivec,&QWP(16,"esp"));
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($cmac,$rndkey1);
+	&lea		($inp,&QWP(16,$inp));
+	&aesenclast	($inout0,$rndkey0);
+	&aesenclast	($cmac,$rndkey0);
+	&jmp	(&label("ccm64_dec_outer"));
+
+&set_label("ccm64_dec_break",16);
+	&mov	($key,$key_);
+	if ($inline)
+	{   &aesni_inline_generate1("enc",$cmac,$in0);	}
+	else
+	{   &call	("_aesni_encrypt1",$cmac);	}
+
+	&mov	("esp",&DWP(48,"esp"));
+	&mov	($out,&wparam(5));
+	&movups	(&QWP(0,$out),$cmac);
+&function_end("aesni_ccm64_decrypt_blocks");
+}
+
+######################################################################
+# void aesni_ctr32_encrypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec);
+#
+# Handles only complete blocks, operates on 32-bit counter and
+# does not update *ivec! (see engine/eng_aesni.c for details)
+#
+# stack layout:
+#	0	pshufb mask
+#	16	vector addend: 0,6,6,6
+# 	32	counter-less ivec
+#	48	1st triplet of counter vector
+#	64	2nd triplet of counter vector
+#	80	saved %esp
+
+&function_begin("aesni_ctr32_encrypt_blocks");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&mov	($key_,"esp");
+	&sub	("esp",88);
+	&and	("esp",-16);			# align stack
+	&mov	(&DWP(80,"esp"),$key_);
+
+	&cmp	($len,1);
+	&je	(&label("ctr32_one_shortcut"));
+
+	&movdqu	($inout5,&QWP(0,$rounds_));	# load ivec
+
+	# compose byte-swap control mask for pshufb on stack
+	&mov	(&DWP(0,"esp"),0x0c0d0e0f);
+	&mov	(&DWP(4,"esp"),0x08090a0b);
+	&mov	(&DWP(8,"esp"),0x04050607);
+	&mov	(&DWP(12,"esp"),0x00010203);
+
+	# compose counter increment vector on stack
+	&mov	($rounds,6);
+	&xor	($key_,$key_);
+	&mov	(&DWP(16,"esp"),$rounds);
+	&mov	(&DWP(20,"esp"),$rounds);
+	&mov	(&DWP(24,"esp"),$rounds);
+	&mov	(&DWP(28,"esp"),$key_);
+
+	&pextrd	($rounds_,$inout5,3);		# pull 32-bit counter
+	&pinsrd	($inout5,$key_,3);		# wipe 32-bit counter
+
+	&mov	($rounds,&DWP(240,$key));	# key->rounds
+
+	# compose 2 vectors of 3x32-bit counters
+	&bswap	($rounds_);
+	&pxor	($rndkey1,$rndkey1);
+	&pxor	($rndkey0,$rndkey0);
+	&movdqa	($inout0,&QWP(0,"esp"));	# load byte-swap mask
+	&pinsrd	($rndkey1,$rounds_,0);
+	&lea	($key_,&DWP(3,$rounds_));
+	&pinsrd	($rndkey0,$key_,0);
+	&inc	($rounds_);
+	&pinsrd	($rndkey1,$rounds_,1);
+	&inc	($key_);
+	&pinsrd	($rndkey0,$key_,1);
+	&inc	($rounds_);
+	&pinsrd	($rndkey1,$rounds_,2);
+	&inc	($key_);
+	&pinsrd	($rndkey0,$key_,2);
+	&movdqa	(&QWP(48,"esp"),$rndkey1);	# save 1st triplet
+	&pshufb	($rndkey1,$inout0);		# byte swap
+	&movdqa	(&QWP(64,"esp"),$rndkey0);	# save 2nd triplet
+	&pshufb	($rndkey0,$inout0);		# byte swap
+
+	&pshufd	($inout0,$rndkey1,3<<6);	# place counter to upper dword
+	&pshufd	($inout1,$rndkey1,2<<6);
+	&cmp	($len,6);
+	&jb	(&label("ctr32_tail"));
+	&movdqa	(&QWP(32,"esp"),$inout5);	# save counter-less ivec
+	&shr	($rounds,1);
+	&mov	($key_,$key);			# backup $key
+	&mov	($rounds_,$rounds);		# backup $rounds
+	&sub	($len,6);
+	&jmp	(&label("ctr32_loop6"));
+
+&set_label("ctr32_loop6",16);
+	&pshufd	($inout2,$rndkey1,1<<6);
+	&movdqa	($rndkey1,&QWP(32,"esp"));	# pull counter-less ivec
+	&pshufd	($inout3,$rndkey0,3<<6);
+	&por	($inout0,$rndkey1);		# merge counter-less ivec
+	&pshufd	($inout4,$rndkey0,2<<6);
+	&por	($inout1,$rndkey1);
+	&pshufd	($inout5,$rndkey0,1<<6);
+	&por	($inout2,$rndkey1);
+	&por	($inout3,$rndkey1);
+	&por	($inout4,$rndkey1);
+	&por	($inout5,$rndkey1);
+
+	# inlining _aesni_encrypt6's prologue gives ~4% improvement...
+	&$movekey	($rndkey0,&QWP(0,$key_));
+	&$movekey	($rndkey1,&QWP(16,$key_));
+	&lea		($key,&DWP(32,$key_));
+	&dec		($rounds);
+	&pxor		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);
+	&aesenc		($inout0,$rndkey1);
+	&pxor		($inout2,$rndkey0);
+	&aesenc		($inout1,$rndkey1);
+	&pxor		($inout3,$rndkey0);
+	&aesenc		($inout2,$rndkey1);
+	&pxor		($inout4,$rndkey0);
+	&aesenc		($inout3,$rndkey1);
+	&pxor		($inout5,$rndkey0);
+	&aesenc		($inout4,$rndkey1);
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&aesenc		($inout5,$rndkey1);
+
+	&call		(&label("_aesni_encrypt6_enter"));
+
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout1,$rndkey0);
+	&movups	(&QWP(0,$out),$inout0);
+	&movdqa	($rndkey0,&QWP(16,"esp"));	# load increment
+	&xorps	($inout2,$rndkey1);
+	&movdqa	($rndkey1,&QWP(48,"esp"));	# load 1st triplet
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+
+	&paddd	($rndkey1,$rndkey0);		# 1st triplet increment
+	&paddd	($rndkey0,&QWP(64,"esp"));	# 2nd triplet increment
+	&movdqa	($inout0,&QWP(0,"esp"));	# load byte swap mask
+
+	&movups	($inout1,&QWP(0x30,$inp));
+	&movups	($inout2,&QWP(0x40,$inp));
+	&xorps	($inout3,$inout1);
+	&movups	($inout1,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+	&movdqa	(&QWP(48,"esp"),$rndkey1);	# save 1st triplet
+	&pshufb	($rndkey1,$inout0);		# byte swap
+	&xorps	($inout4,$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&xorps	($inout5,$inout1);
+	&movdqa	(&QWP(64,"esp"),$rndkey0);	# save 2nd triplet
+	&pshufb	($rndkey0,$inout0);		# byte swap
+	&movups	(&QWP(0x40,$out),$inout4);
+	&pshufd	($inout0,$rndkey1,3<<6);
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+
+	&mov	($rounds,$rounds_);
+	&pshufd	($inout1,$rndkey1,2<<6);
+	&sub	($len,6);
+	&jnc	(&label("ctr32_loop6"));
+
+	&add	($len,6);
+	&jz	(&label("ctr32_ret"));
+	&mov	($key,$key_);
+	&lea	($rounds,&DWP(1,"",$rounds,2));	# restore $rounds
+	&movdqa	($inout5,&QWP(32,"esp"));	# pull count-less ivec
+
+&set_label("ctr32_tail");
+	&por	($inout0,$inout5);
+	&cmp	($len,2);
+	&jb	(&label("ctr32_one"));
+
+	&pshufd	($inout2,$rndkey1,1<<6);
+	&por	($inout1,$inout5);
+	&je	(&label("ctr32_two"));
+
+	&pshufd	($inout3,$rndkey0,3<<6);
+	&por	($inout2,$inout5);
+	&cmp	($len,4);
+	&jb	(&label("ctr32_three"));
+
+	&pshufd	($inout4,$rndkey0,2<<6);
+	&por	($inout3,$inout5);
+	&je	(&label("ctr32_four"));
+
+	&por	($inout4,$inout5);
+	&call	("_aesni_encrypt6");
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout1,$rndkey0);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout2,$rndkey1);
+	&movups	($rndkey1,&QWP(0x40,$inp));
+	&xorps	($inout3,$rndkey0);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout4,$rndkey1);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_one_shortcut",16);
+	&movups	($inout0,&QWP(0,$rounds_));	# load ivec
+	&mov	($rounds,&DWP(240,$key));
+	
+&set_label("ctr32_one");
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&movups	($in0,&QWP(0,$inp));
+	&xorps	($in0,$inout0);
+	&movups	(&QWP(0,$out),$in0);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_two",16);
+	&call	("_aesni_encrypt3");
+	&movups	($inout3,&QWP(0,$inp));
+	&movups	($inout4,&QWP(0x10,$inp));
+	&xorps	($inout0,$inout3);
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_three",16);
+	&call	("_aesni_encrypt3");
+	&movups	($inout3,&QWP(0,$inp));
+	&movups	($inout4,&QWP(0x10,$inp));
+	&xorps	($inout0,$inout3);
+	&movups	($inout5,&QWP(0x20,$inp));
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_four",16);
+	&call	("_aesni_encrypt4");
+	&movups	($inout4,&QWP(0,$inp));
+	&movups	($inout5,&QWP(0x10,$inp));
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout0,$inout4);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout1,$inout5);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout2,$rndkey1);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&xorps	($inout3,$rndkey0);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+
+&set_label("ctr32_ret");
+	&mov	("esp",&DWP(80,"esp"));
+&function_end("aesni_ctr32_encrypt_blocks");
+
+######################################################################
+# void aesni_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2
+#	const unsigned char iv[16]);
+#
+{ my ($tweak,$twtmp,$twres,$twmask)=($rndkey1,$rndkey0,$inout0,$inout1);
+
+&function_begin("aesni_xts_encrypt");
+	&mov	($key,&wparam(4));		# key2
+	&mov	($inp,&wparam(5));		# clear-text tweak
+
+	&mov	($rounds,&DWP(240,$key));	# key2->rounds
+	&movups	($inout0,&QWP(0,$inp));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));		# key1
+
+	&mov	($key_,"esp");
+	&sub	("esp",16*7+8);
+	&mov	($rounds,&DWP(240,$key));	# key1->rounds
+	&and	("esp",-16);			# align stack
+
+	&mov	(&DWP(16*6+0,"esp"),0x87);	# compose the magic constant
+	&mov	(&DWP(16*6+4,"esp"),0);
+	&mov	(&DWP(16*6+8,"esp"),1);
+	&mov	(&DWP(16*6+12,"esp"),0);
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save original $len
+	&mov	(&DWP(16*7+4,"esp"),$key_);	# save original %esp
+
+	&movdqa	($tweak,$inout0);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($twmask,&QWP(6*16,"esp"));	# 0x0...010...87
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+
+	&and	($len,-16);
+	&mov	($key_,$key);			# backup $key
+	&mov	($rounds_,$rounds);		# backup $rounds
+	&sub	($len,16*6);
+	&jc	(&label("xts_enc_short"));
+
+	&shr	($rounds,1);
+	&mov	($rounds_,$rounds);
+	&jmp	(&label("xts_enc_loop6"));
+
+&set_label("xts_enc_loop6",16);
+	for ($i=0;$i<4;$i++) {
+	    &pshufd	($twres,$twtmp,0x13);
+	    &pxor	($twtmp,$twtmp);
+	    &movdqa	(&QWP(16*$i,"esp"),$tweak);
+	    &paddq	($tweak,$tweak);	# &psllq($tweak,1);
+	    &pand	($twres,$twmask);	# isolate carry and residue
+	    &pcmpgtd	($twtmp,$tweak);	# broadcast upper bits
+	    &pxor	($tweak,$twres);
+	}
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*$i++,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	 &$movekey	($rndkey0,&QWP(0,$key_));
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	 &movups	($inout0,&QWP(0,$inp));	# load input
+	&pxor	($inout5,$tweak);
+
+	# inline _aesni_encrypt6 prologue and flip xor with tweak and key[0]
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	 &xorps		($inout0,$rndkey0);	# input^=rndkey[0]
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	 &pxor		($inout1,$rndkey0);
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	 &pxor		($inout2,$rndkey0);
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	 &pxor		($inout3,$rndkey0);
+	&movdqu	($rndkey1,&QWP(16*5,$inp));
+	 &pxor		($inout4,$rndkey0);
+	&lea	($inp,&DWP(16*6,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqa	(&QWP(16*$i,"esp"),$inout5);	# save last tweak
+	&pxor	($inout5,$rndkey1);
+
+	 &$movekey	($rndkey1,&QWP(16,$key_));
+	 &lea		($key,&DWP(32,$key_));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	 &aesenc	($inout0,$rndkey1);
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	 &aesenc	($inout1,$rndkey1);
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	 &dec		($rounds);
+	 &aesenc	($inout2,$rndkey1);
+	&pxor	($inout4,&QWP(16*4,"esp"));
+	 &aesenc	($inout3,$rndkey1);
+	&pxor		($inout5,$rndkey0);
+	 &aesenc	($inout4,$rndkey1);
+	 &$movekey	($rndkey0,&QWP(0,$key));
+	 &aesenc	($inout5,$rndkey1);
+	&call		(&label("_aesni_encrypt6_enter"));
+
+	&movdqa	($tweak,&QWP(16*5,"esp"));	# last tweak
+       &pxor	($twtmp,$twtmp);
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+       &pcmpgtd	($twtmp,$tweak);		# broadcast upper bits
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*2,$out),$inout2);
+	&xorps	($inout4,&QWP(16*4,"esp"));
+	&movups	(&QWP(16*3,$out),$inout3);
+	&xorps	($inout5,$tweak);
+	&movups	(&QWP(16*4,$out),$inout4);
+       &pshufd	($twres,$twtmp,0x13);
+	&movups	(&QWP(16*5,$out),$inout5);
+	&lea	($out,&DWP(16*6,$out));
+       &movdqa	($twmask,&QWP(16*6,"esp"));	# 0x0...010...87
+
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&mov	($rounds,$rounds_);		# restore $rounds
+	&pxor	($tweak,$twres);
+
+	&sub	($len,16*6);
+	&jnc	(&label("xts_enc_loop6"));
+
+	&lea	($rounds,&DWP(1,"",$rounds,2));	# restore $rounds
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds_,$rounds);
+
+&set_label("xts_enc_short");
+	&add	($len,16*6);
+	&jz	(&label("xts_enc_done6x"));
+
+	&movdqa	($inout3,$tweak);		# put aside previous tweak
+	&cmp	($len,0x20);
+	&jb	(&label("xts_enc_one"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&je	(&label("xts_enc_two"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout4,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&cmp	($len,0x40);
+	&jb	(&label("xts_enc_three"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout5,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&movdqa	(&QWP(16*0,"esp"),$inout3);
+	&movdqa	(&QWP(16*1,"esp"),$inout4);
+	&je	(&label("xts_enc_four"));
+
+	&movdqa	(&QWP(16*2,"esp"),$inout5);
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*3,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($inout0,1);
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	&pxor	($inout5,$tweak);
+
+	&movdqu	($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	&lea	($inp,&DWP(16*5,$inp));
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	&movdqa	(&QWP(16*4,"esp"),$inout5);	# save last tweak
+	&pxor	($inout4,$inout5);
+
+	&call	("_aesni_encrypt6");
+
+	&movaps	($tweak,&QWP(16*4,"esp"));	# last tweak
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout4,$tweak);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&movups	(&QWP(16*4,$out),$inout4);
+	&lea	($out,&DWP(16*5,$out));
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_one",16);
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&lea	($inp,&DWP(16*1,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&lea	($out,&DWP(16*1,$out));
+
+	&movdqa	($tweak,$inout3);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_two",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&lea	($inp,&DWP(16*2,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout2);
+
+	&call	("_aesni_encrypt3");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&lea	($out,&DWP(16*2,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_three",16);
+	&movaps	($inout5,$tweak);		# put aside last tweak
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&lea	($inp,&DWP(16*3,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+
+	&call	("_aesni_encrypt3");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&lea	($out,&DWP(16*3,$out));
+
+	&movdqa	($tweak,$inout5);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_four",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&xorps	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movups	($inout3,&QWP(16*3,$inp));
+	&lea	($inp,&DWP(16*4,$inp));
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&xorps	($inout3,$inout4);
+
+	&call	("_aesni_encrypt4");
+
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,$inout4);
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&lea	($out,&DWP(16*4,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_done6x",16);		# $tweak is pre-calculated
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&and	($len,15);
+	&jz	(&label("xts_enc_ret"));
+	&movdqa	($inout3,$tweak);
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&jmp	(&label("xts_enc_steal"));
+
+&set_label("xts_enc_done",16);
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&pxor	($twtmp,$twtmp);
+	&and	($len,15);
+	&jz	(&label("xts_enc_ret"));
+
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&pshufd	($inout3,$twtmp,0x13);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($inout3,&QWP(16*6,"esp"));	# isolate carry and residue
+	&pxor	($inout3,$tweak);
+
+&set_label("xts_enc_steal");
+	&movz	($rounds,&BP(0,$inp));
+	&movz	($key,&BP(-16,$out));
+	&lea	($inp,&DWP(1,$inp));
+	&mov	(&BP(-16,$out),&LB($rounds));
+	&mov	(&BP(0,$out),&LB($key));
+	&lea	($out,&DWP(1,$out));
+	&sub	($len,1);
+	&jnz	(&label("xts_enc_steal"));
+
+	&sub	($out,&DWP(16*7+0,"esp"));	# rewind $out
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds,$rounds_);		# restore $rounds
+
+	&movups	($inout0,&QWP(-16,$out));	# load input
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(-16,$out),$inout0);	# write output
+
+&set_label("xts_enc_ret");
+	&mov	("esp",&DWP(16*7+4,"esp"));	# restore %esp
+&function_end("aesni_xts_encrypt");
+
+&function_begin("aesni_xts_decrypt");
+	&mov	($key,&wparam(4));		# key2
+	&mov	($inp,&wparam(5));		# clear-text tweak
+
+	&mov	($rounds,&DWP(240,$key));	# key2->rounds
+	&movups	($inout0,&QWP(0,$inp));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));		# key1
+
+	&mov	($key_,"esp");
+	&sub	("esp",16*7+8);
+	&and	("esp",-16);			# align stack
+
+	&xor	($rounds_,$rounds_);		# if(len%16) len-=16;
+	&test	($len,15);
+	&setnz	(&LB($rounds_));
+	&shl	($rounds_,4);
+	&sub	($len,$rounds_);
+
+	&mov	(&DWP(16*6+0,"esp"),0x87);	# compose the magic constant
+	&mov	(&DWP(16*6+4,"esp"),0);
+	&mov	(&DWP(16*6+8,"esp"),1);
+	&mov	(&DWP(16*6+12,"esp"),0);
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save original $len
+	&mov	(&DWP(16*7+4,"esp"),$key_);	# save original %esp
+
+	&mov	($rounds,&DWP(240,$key));	# key1->rounds
+	&mov	($key_,$key);			# backup $key
+	&mov	($rounds_,$rounds);		# backup $rounds
+
+	&movdqa	($tweak,$inout0);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($twmask,&QWP(6*16,"esp"));	# 0x0...010...87
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+
+	&and	($len,-16);
+	&sub	($len,16*6);
+	&jc	(&label("xts_dec_short"));
+
+	&shr	($rounds,1);
+	&mov	($rounds_,$rounds);
+	&jmp	(&label("xts_dec_loop6"));
+
+&set_label("xts_dec_loop6",16);
+	for ($i=0;$i<4;$i++) {
+	    &pshufd	($twres,$twtmp,0x13);
+	    &pxor	($twtmp,$twtmp);
+	    &movdqa	(&QWP(16*$i,"esp"),$tweak);
+	    &paddq	($tweak,$tweak);	# &psllq($tweak,1);
+	    &pand	($twres,$twmask);	# isolate carry and residue
+	    &pcmpgtd	($twtmp,$tweak);	# broadcast upper bits
+	    &pxor	($tweak,$twres);
+	}
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*$i++,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	 &$movekey	($rndkey0,&QWP(0,$key_));
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	 &movups	($inout0,&QWP(0,$inp));	# load input
+	&pxor	($inout5,$tweak);
+
+	# inline _aesni_encrypt6 prologue and flip xor with tweak and key[0]
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	 &xorps		($inout0,$rndkey0);	# input^=rndkey[0]
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	 &pxor		($inout1,$rndkey0);
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	 &pxor		($inout2,$rndkey0);
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	 &pxor		($inout3,$rndkey0);
+	&movdqu	($rndkey1,&QWP(16*5,$inp));
+	 &pxor		($inout4,$rndkey0);
+	&lea	($inp,&DWP(16*6,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqa	(&QWP(16*$i,"esp"),$inout5);	# save last tweak
+	&pxor	($inout5,$rndkey1);
+
+	 &$movekey	($rndkey1,&QWP(16,$key_));
+	 &lea		($key,&DWP(32,$key_));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	 &aesdec	($inout0,$rndkey1);
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	 &aesdec	($inout1,$rndkey1);
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	 &dec		($rounds);
+	 &aesdec	($inout2,$rndkey1);
+	&pxor	($inout4,&QWP(16*4,"esp"));
+	 &aesdec	($inout3,$rndkey1);
+	&pxor		($inout5,$rndkey0);
+	 &aesdec	($inout4,$rndkey1);
+	 &$movekey	($rndkey0,&QWP(0,$key));
+	 &aesdec	($inout5,$rndkey1);
+	&call		(&label("_aesni_decrypt6_enter"));
+
+	&movdqa	($tweak,&QWP(16*5,"esp"));	# last tweak
+       &pxor	($twtmp,$twtmp);
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+       &pcmpgtd	($twtmp,$tweak);		# broadcast upper bits
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*2,$out),$inout2);
+	&xorps	($inout4,&QWP(16*4,"esp"));
+	&movups	(&QWP(16*3,$out),$inout3);
+	&xorps	($inout5,$tweak);
+	&movups	(&QWP(16*4,$out),$inout4);
+       &pshufd	($twres,$twtmp,0x13);
+	&movups	(&QWP(16*5,$out),$inout5);
+	&lea	($out,&DWP(16*6,$out));
+       &movdqa	($twmask,&QWP(16*6,"esp"));	# 0x0...010...87
+
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&mov	($rounds,$rounds_);		# restore $rounds
+	&pxor	($tweak,$twres);
+
+	&sub	($len,16*6);
+	&jnc	(&label("xts_dec_loop6"));
+
+	&lea	($rounds,&DWP(1,"",$rounds,2));	# restore $rounds
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds_,$rounds);
+
+&set_label("xts_dec_short");
+	&add	($len,16*6);
+	&jz	(&label("xts_dec_done6x"));
+
+	&movdqa	($inout3,$tweak);		# put aside previous tweak
+	&cmp	($len,0x20);
+	&jb	(&label("xts_dec_one"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&je	(&label("xts_dec_two"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout4,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&cmp	($len,0x40);
+	&jb	(&label("xts_dec_three"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout5,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&movdqa	(&QWP(16*0,"esp"),$inout3);
+	&movdqa	(&QWP(16*1,"esp"),$inout4);
+	&je	(&label("xts_dec_four"));
+
+	&movdqa	(&QWP(16*2,"esp"),$inout5);
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*3,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($inout0,1);
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	&pxor	($inout5,$tweak);
+
+	&movdqu	($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	&lea	($inp,&DWP(16*5,$inp));
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	&movdqa	(&QWP(16*4,"esp"),$inout5);	# save last tweak
+	&pxor	($inout4,$inout5);
+
+	&call	("_aesni_decrypt6");
+
+	&movaps	($tweak,&QWP(16*4,"esp"));	# last tweak
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout4,$tweak);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&movups	(&QWP(16*4,$out),$inout4);
+	&lea	($out,&DWP(16*5,$out));
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_one",16);
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&lea	($inp,&DWP(16*1,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&lea	($out,&DWP(16*1,$out));
+
+	&movdqa	($tweak,$inout3);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_two",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&lea	($inp,&DWP(16*2,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+
+	&call	("_aesni_decrypt3");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&lea	($out,&DWP(16*2,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_three",16);
+	&movaps	($inout5,$tweak);		# put aside last tweak
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&lea	($inp,&DWP(16*3,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+
+	&call	("_aesni_decrypt3");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&lea	($out,&DWP(16*3,$out));
+
+	&movdqa	($tweak,$inout5);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_four",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&xorps	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movups	($inout3,&QWP(16*3,$inp));
+	&lea	($inp,&DWP(16*4,$inp));
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&xorps	($inout3,$inout4);
+
+	&call	("_aesni_decrypt4");
+
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,$inout4);
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&lea	($out,&DWP(16*4,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_done6x",16);		# $tweak is pre-calculated
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&and	($len,15);
+	&jz	(&label("xts_dec_ret"));
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&jmp	(&label("xts_dec_only_one_more"));
+
+&set_label("xts_dec_done",16);
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&pxor	($twtmp,$twtmp);
+	&and	($len,15);
+	&jz	(&label("xts_dec_ret"));
+
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($twmask,&QWP(16*6,"esp"));
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+
+&set_label("xts_dec_only_one_more");
+	&pshufd	($inout3,$twtmp,0x13);
+	&movdqa	($inout4,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($inout3,$twmask);		# isolate carry and residue
+	&pxor	($inout3,$tweak);
+
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds,$rounds_);		# restore $rounds
+
+	&movups	($inout0,&QWP(0,$inp));		# load input
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(0,$out),$inout0);		# write output
+
+&set_label("xts_dec_steal");
+	&movz	($rounds,&BP(16,$inp));
+	&movz	($key,&BP(0,$out));
+	&lea	($inp,&DWP(1,$inp));
+	&mov	(&BP(0,$out),&LB($rounds));
+	&mov	(&BP(16,$out),&LB($key));
+	&lea	($out,&DWP(1,$out));
+	&sub	($len,1);
+	&jnz	(&label("xts_dec_steal"));
+
+	&sub	($out,&DWP(16*7+0,"esp"));	# rewind $out
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds,$rounds_);		# restore $rounds
+
+	&movups	($inout0,&QWP(0,$out));		# load input
+	&xorps	($inout0,$inout4);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$inout4);		# output^=tweak
+	&movups	(&QWP(0,$out),$inout0);		# write output
+
+&set_label("xts_dec_ret");
+	&mov	("esp",&DWP(16*7+4,"esp"));	# restore %esp
+&function_end("aesni_xts_decrypt");
+}
+}
+
+######################################################################
+# void $PREFIX_cbc_encrypt (const void *inp, void *out,
+#                           size_t length, const AES_KEY *key,
+#                           unsigned char *ivp,const int enc);
+&function_begin("${PREFIX}_cbc_encrypt");
+	&mov	($inp,&wparam(0));
+	&mov	($rounds_,"esp");
+	&mov	($out,&wparam(1));
+	&sub	($rounds_,24);
+	&mov	($len,&wparam(2));
+	&and	($rounds_,-16);
+	&mov	($key,&wparam(3));
+	&mov	($key_,&wparam(4));
+	&test	($len,$len);
+	&jz	(&label("cbc_abort"));
+
+	&cmp	(&wparam(5),0);
+	&xchg	($rounds_,"esp");		# alloca
+	&movups	($ivec,&QWP(0,$key_));		# load IV
+	&mov	($rounds,&DWP(240,$key));
+	&mov	($key_,$key);			# backup $key
+	&mov	(&DWP(16,"esp"),$rounds_);	# save original %esp
+	&mov	($rounds_,$rounds);		# backup $rounds
+	&je	(&label("cbc_decrypt"));
+
+	&movaps	($inout0,$ivec);
+	&cmp	($len,16);
+	&jb	(&label("cbc_enc_tail"));
+	&sub	($len,16);
+	&jmp	(&label("cbc_enc_loop"));
+
+&set_label("cbc_enc_loop",16);
+	&movups	($ivec,&QWP(0,$inp));		# input actually
+	&lea	($inp,&DWP(16,$inp));
+	if ($inline)
+	{   &aesni_inline_generate1("enc",$inout0,$ivec);	}
+	else
+	{   &xorps($inout0,$ivec); &call("_aesni_encrypt1");	}
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&mov	($key,$key_);		# restore $key
+	&movups	(&QWP(0,$out),$inout0);	# store output
+	&lea	($out,&DWP(16,$out));
+	&sub	($len,16);
+	&jnc	(&label("cbc_enc_loop"));
+	&add	($len,16);
+	&jnz	(&label("cbc_enc_tail"));
+	&movaps	($ivec,$inout0);
+	&jmp	(&label("cbc_ret"));
+
+&set_label("cbc_enc_tail");
+	&mov	("ecx",$len);		# zaps $rounds
+	&data_word(0xA4F3F689);		# rep movsb
+	&mov	("ecx",16);		# zero tail
+	&sub	("ecx",$len);
+	&xor	("eax","eax");		# zaps $len
+	&data_word(0xAAF3F689);		# rep stosb
+	&lea	($out,&DWP(-16,$out));	# rewind $out by 1 block
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&mov	($inp,$out);		# $inp and $out are the same
+	&mov	($key,$key_);		# restore $key
+	&jmp	(&label("cbc_enc_loop"));
+######################################################################
+&set_label("cbc_decrypt",16);
+	&cmp	($len,0x50);
+	&jbe	(&label("cbc_dec_tail"));
+	&movaps	(&QWP(0,"esp"),$ivec);		# save IV
+	&sub	($len,0x50);
+	&jmp	(&label("cbc_dec_loop6_enter"));
+
+&set_label("cbc_dec_loop6",16);
+	&movaps	(&QWP(0,"esp"),$rndkey0);	# save IV
+	&movups	(&QWP(0,$out),$inout5);
+	&lea	($out,&DWP(0x10,$out));
+&set_label("cbc_dec_loop6_enter");
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+
+	&call	("_aesni_decrypt6");
+
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,&QWP(0,"esp"));	# ^=IV
+	&xorps	($inout1,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout2,$rndkey0);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout3,$rndkey1);
+	&movups	($rndkey1,&QWP(0x40,$inp));
+	&xorps	($inout4,$rndkey0);
+	&movups	($rndkey0,&QWP(0x50,$inp));	# IV
+	&xorps	($inout5,$rndkey1);
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&lea	($inp,&DWP(0x60,$inp));
+	&movups	(&QWP(0x20,$out),$inout2);
+	&mov	($rounds,$rounds_)		# restore $rounds
+	&movups	(&QWP(0x30,$out),$inout3);
+	&mov	($key,$key_);			# restore $key
+	&movups	(&QWP(0x40,$out),$inout4);
+	&lea	($out,&DWP(0x50,$out));
+	&sub	($len,0x60);
+	&ja	(&label("cbc_dec_loop6"));
+
+	&movaps	($inout0,$inout5);
+	&movaps	($ivec,$rndkey0);
+	&add	($len,0x50);
+	&jle	(&label("cbc_dec_tail_collected"));
+	&movups	(&QWP(0,$out),$inout0);
+	&lea	($out,&DWP(0x10,$out));
+&set_label("cbc_dec_tail");
+	&movups	($inout0,&QWP(0,$inp));
+	&movaps	($in0,$inout0);
+	&cmp	($len,0x10);
+	&jbe	(&label("cbc_dec_one"));
+
+	&movups	($inout1,&QWP(0x10,$inp));
+	&movaps	($in1,$inout1);
+	&cmp	($len,0x20);
+	&jbe	(&label("cbc_dec_two"));
+
+	&movups	($inout2,&QWP(0x20,$inp));
+	&cmp	($len,0x30);
+	&jbe	(&label("cbc_dec_three"));
+
+	&movups	($inout3,&QWP(0x30,$inp));
+	&cmp	($len,0x40);
+	&jbe	(&label("cbc_dec_four"));
+
+	&movups	($inout4,&QWP(0x40,$inp));
+	&movaps	(&QWP(0,"esp"),$ivec);		# save IV
+	&movups	($inout0,&QWP(0,$inp));
+	&xorps	($inout5,$inout5);
+	&call	("_aesni_decrypt6");
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,&QWP(0,"esp"));	# ^= IV
+	&xorps	($inout1,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout2,$rndkey0);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout3,$rndkey1);
+	&movups	($ivec,&QWP(0x40,$inp));	# IV
+	&xorps	($inout4,$rndkey0);
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&lea	($out,&DWP(0x40,$out));
+	&movaps	($inout0,$inout4);
+	&sub	($len,0x50);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_one",16);
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$ivec);
+	&movaps	($ivec,$in0);
+	&sub	($len,0x10);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_two",16);
+	&xorps	($inout2,$inout2);
+	&call	("_aesni_decrypt3");
+	&xorps	($inout0,$ivec);
+	&xorps	($inout1,$in0);
+	&movups	(&QWP(0,$out),$inout0);
+	&movaps	($inout0,$inout1);
+	&lea	($out,&DWP(0x10,$out));
+	&movaps	($ivec,$in1);
+	&sub	($len,0x20);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_three",16);
+	&call	("_aesni_decrypt3");
+	&xorps	($inout0,$ivec);
+	&xorps	($inout1,$in0);
+	&xorps	($inout2,$in1);
+	&movups	(&QWP(0,$out),$inout0);
+	&movaps	($inout0,$inout2);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&lea	($out,&DWP(0x20,$out));
+	&movups	($ivec,&QWP(0x20,$inp));
+	&sub	($len,0x30);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_four",16);
+	&call	("_aesni_decrypt4");
+	&movups	($rndkey1,&QWP(0x10,$inp));
+	&movups	($rndkey0,&QWP(0x20,$inp));
+	&xorps	($inout0,$ivec);
+	&movups	($ivec,&QWP(0x30,$inp));
+	&xorps	($inout1,$in0);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout2,$rndkey1);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&xorps	($inout3,$rndkey0);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&lea	($out,&DWP(0x30,$out));
+	&movaps	($inout0,$inout3);
+	&sub	($len,0x40);
+
+&set_label("cbc_dec_tail_collected");
+	&and	($len,15);
+	&jnz	(&label("cbc_dec_tail_partial"));
+	&movups	(&QWP(0,$out),$inout0);
+	&jmp	(&label("cbc_ret"));
+
+&set_label("cbc_dec_tail_partial",16);
+	&movaps	(&QWP(0,"esp"),$inout0);
+	&mov	("ecx",16);
+	&mov	($inp,"esp");
+	&sub	("ecx",$len);
+	&data_word(0xA4F3F689);		# rep movsb
+
+&set_label("cbc_ret");
+	&mov	("esp",&DWP(16,"esp"));	# pull original %esp
+	&mov	($key_,&wparam(4));
+	&movups	(&QWP(0,$key_),$ivec);	# output IV
+&set_label("cbc_abort");
+&function_end("${PREFIX}_cbc_encrypt");
+
+######################################################################
+# Mechanical port from aesni-x86_64.pl.
+#
+# _aesni_set_encrypt_key is private interface,
+# input:
+#	"eax"	const unsigned char *userKey
+#	$rounds	int bits
+#	$key	AES_KEY *key
+# output:
+#	"eax"	return code
+#	$round	rounds
+
+&function_begin_B("_aesni_set_encrypt_key");
+	&test	("eax","eax");
+	&jz	(&label("bad_pointer"));
+	&test	($key,$key);
+	&jz	(&label("bad_pointer"));
+
+	&movups	("xmm0",&QWP(0,"eax"));	# pull first 128 bits of *userKey
+	&xorps	("xmm4","xmm4");	# low dword of xmm4 is assumed 0
+	&lea	($key,&DWP(16,$key));
+	&cmp	($rounds,256);
+	&je	(&label("14rounds"));
+	&cmp	($rounds,192);
+	&je	(&label("12rounds"));
+	&cmp	($rounds,128);
+	&jne	(&label("bad_keybits"));
+
+&set_label("10rounds",16);
+	&mov		($rounds,9);
+	&$movekey	(&QWP(-16,$key),"xmm0");	# round 0
+	&aeskeygenassist("xmm1","xmm0",0x01);		# round 1
+	&call		(&label("key_128_cold"));
+	&aeskeygenassist("xmm1","xmm0",0x2);		# round 2
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x04);		# round 3
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x08);		# round 4
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x10);		# round 5
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x20);		# round 6
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x40);		# round 7
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x80);		# round 8
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x1b);		# round 9
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x36);		# round 10
+	&call		(&label("key_128"));
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&mov		(&DWP(80,$key),$rounds);
+	&xor		("eax","eax");
+	&ret();
+
+&set_label("key_128",16);
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&lea		($key,&DWP(16,$key));
+&set_label("key_128_cold");
+	&shufps		("xmm4","xmm0",0b00010000);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm4","xmm0",0b10001100);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm1","xmm1",0b11111111);	# critical path
+	&xorps		("xmm0","xmm1");
+	&ret();
+
+&set_label("12rounds",16);
+	&movq		("xmm2",&QWP(16,"eax"));	# remaining 1/3 of *userKey
+	&mov		($rounds,11);
+	&$movekey	(&QWP(-16,$key),"xmm0")		# round 0
+	&aeskeygenassist("xmm1","xmm2",0x01);		# round 1,2
+	&call		(&label("key_192a_cold"));
+	&aeskeygenassist("xmm1","xmm2",0x02);		# round 2,3
+	&call		(&label("key_192b"));
+	&aeskeygenassist("xmm1","xmm2",0x04);		# round 4,5
+	&call		(&label("key_192a"));
+	&aeskeygenassist("xmm1","xmm2",0x08);		# round 5,6
+	&call		(&label("key_192b"));
+	&aeskeygenassist("xmm1","xmm2",0x10);		# round 7,8
+	&call		(&label("key_192a"));
+	&aeskeygenassist("xmm1","xmm2",0x20);		# round 8,9
+	&call		(&label("key_192b"));
+	&aeskeygenassist("xmm1","xmm2",0x40);		# round 10,11
+	&call		(&label("key_192a"));
+	&aeskeygenassist("xmm1","xmm2",0x80);		# round 11,12
+	&call		(&label("key_192b"));
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&mov		(&DWP(48,$key),$rounds);
+	&xor		("eax","eax");
+	&ret();
+
+&set_label("key_192a",16);
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&lea		($key,&DWP(16,$key));
+&set_label("key_192a_cold",16);
+	&movaps		("xmm5","xmm2");
+&set_label("key_192b_warm");
+	&shufps		("xmm4","xmm0",0b00010000);
+	&movdqa		("xmm3","xmm2");
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm4","xmm0",0b10001100);
+	&pslldq		("xmm3",4);
+	&xorps		("xmm0","xmm4");
+	&pshufd		("xmm1","xmm1",0b01010101);	# critical path
+	&pxor		("xmm2","xmm3");
+	&pxor		("xmm0","xmm1");
+	&pshufd		("xmm3","xmm0",0b11111111);
+	&pxor		("xmm2","xmm3");
+	&ret();
+
+&set_label("key_192b",16);
+	&movaps		("xmm3","xmm0");
+	&shufps		("xmm5","xmm0",0b01000100);
+	&$movekey	(&QWP(0,$key),"xmm5");
+	&shufps		("xmm3","xmm2",0b01001110);
+	&$movekey	(&QWP(16,$key),"xmm3");
+	&lea		($key,&DWP(32,$key));
+	&jmp		(&label("key_192b_warm"));
+
+&set_label("14rounds",16);
+	&movups		("xmm2",&QWP(16,"eax"));	# remaining half of *userKey
+	&mov		($rounds,13);
+	&lea		($key,&DWP(16,$key));
+	&$movekey	(&QWP(-32,$key),"xmm0");	# round 0
+	&$movekey	(&QWP(-16,$key),"xmm2");	# round 1
+	&aeskeygenassist("xmm1","xmm2",0x01);		# round 2
+	&call		(&label("key_256a_cold"));
+	&aeskeygenassist("xmm1","xmm0",0x01);		# round 3
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x02);		# round 4
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x02);		# round 5
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x04);		# round 6
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x04);		# round 7
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x08);		# round 8
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x08);		# round 9
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x10);		# round 10
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x10);		# round 11
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x20);		# round 12
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x20);		# round 13
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x40);		# round 14
+	&call		(&label("key_256a"));
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&mov		(&DWP(16,$key),$rounds);
+	&xor		("eax","eax");
+	&ret();
+
+&set_label("key_256a",16);
+	&$movekey	(&QWP(0,$key),"xmm2");
+	&lea		($key,&DWP(16,$key));
+&set_label("key_256a_cold");
+	&shufps		("xmm4","xmm0",0b00010000);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm4","xmm0",0b10001100);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm1","xmm1",0b11111111);	# critical path
+	&xorps		("xmm0","xmm1");
+	&ret();
+
+&set_label("key_256b",16);
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&lea		($key,&DWP(16,$key));
+
+	&shufps		("xmm4","xmm2",0b00010000);
+	&xorps		("xmm2","xmm4");
+	&shufps		("xmm4","xmm2",0b10001100);
+	&xorps		("xmm2","xmm4");
+	&shufps		("xmm1","xmm1",0b10101010);	# critical path
+	&xorps		("xmm2","xmm1");
+	&ret();
+
+&set_label("bad_pointer",4);
+	&mov	("eax",-1);
+	&ret	();
+&set_label("bad_keybits",4);
+	&mov	("eax",-2);
+	&ret	();
+&function_end_B("_aesni_set_encrypt_key");
+
+# int $PREFIX_set_encrypt_key (const unsigned char *userKey, int bits,
+#                              AES_KEY *key)
+&function_begin_B("${PREFIX}_set_encrypt_key");
+	&mov	("eax",&wparam(0));
+	&mov	($rounds,&wparam(1));
+	&mov	($key,&wparam(2));
+	&call	("_aesni_set_encrypt_key");
+	&ret	();
+&function_end_B("${PREFIX}_set_encrypt_key");
+
+# int $PREFIX_set_decrypt_key (const unsigned char *userKey, int bits,
+#                              AES_KEY *key)
+&function_begin_B("${PREFIX}_set_decrypt_key");
+	&mov	("eax",&wparam(0));
+	&mov	($rounds,&wparam(1));
+	&mov	($key,&wparam(2));
+	&call	("_aesni_set_encrypt_key");
+	&mov	($key,&wparam(2));
+	&shl	($rounds,4)	# rounds-1 after _aesni_set_encrypt_key
+	&test	("eax","eax");
+	&jnz	(&label("dec_key_ret"));
+	&lea	("eax",&DWP(16,$key,$rounds));	# end of key schedule
+
+	&$movekey	("xmm0",&QWP(0,$key));	# just swap
+	&$movekey	("xmm1",&QWP(0,"eax"));
+	&$movekey	(&QWP(0,"eax"),"xmm0");
+	&$movekey	(&QWP(0,$key),"xmm1");
+	&lea		($key,&DWP(16,$key));
+	&lea		("eax",&DWP(-16,"eax"));
+
+&set_label("dec_key_inverse");
+	&$movekey	("xmm0",&QWP(0,$key));	# swap and inverse
+	&$movekey	("xmm1",&QWP(0,"eax"));
+	&aesimc		("xmm0","xmm0");
+	&aesimc		("xmm1","xmm1");
+	&lea		($key,&DWP(16,$key));
+	&lea		("eax",&DWP(-16,"eax"));
+	&$movekey	(&QWP(16,"eax"),"xmm0");
+	&$movekey	(&QWP(-16,$key),"xmm1");
+	&cmp		("eax",$key);
+	&ja		(&label("dec_key_inverse"));
+
+	&$movekey	("xmm0",&QWP(0,$key));	# inverse middle
+	&aesimc		("xmm0","xmm0");
+	&$movekey	(&QWP(0,$key),"xmm0");
+
+	&xor		("eax","eax");		# return success
+&set_label("dec_key_ret");
+	&ret	();
+&function_end_B("${PREFIX}_set_decrypt_key");
+&asciz("AES for Intel AES-NI, CRYPTOGAMS by <appro\@openssl.org>");
+
+&asm_finish();
diff --git a/crypto/aes/asm/aesni-x86_64.pl b/crypto/aes/asm/aesni-x86_64.pl
new file mode 100644
index 0000000..0dbb194
--- /dev/null
+++ b/crypto/aes/asm/aesni-x86_64.pl
@@ -0,0 +1,3069 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements support for Intel AES-NI extension. In
+# OpenSSL context it's used with Intel engine, but can also be used as
+# drop-in replacement for crypto/aes/asm/aes-x86_64.pl [see below for
+# details].
+#
+# Performance.
+#
+# Given aes(enc|dec) instructions' latency asymptotic performance for
+# non-parallelizable modes such as CBC encrypt is 3.75 cycles per byte
+# processed with 128-bit key. And given their throughput asymptotic
+# performance for parallelizable modes is 1.25 cycles per byte. Being
+# asymptotic limit it's not something you commonly achieve in reality,
+# but how close does one get? Below are results collected for
+# different modes and block sized. Pairs of numbers are for en-/
+# decryption.
+#
+#	16-byte     64-byte     256-byte    1-KB        8-KB
+# ECB	4.25/4.25   1.38/1.38   1.28/1.28   1.26/1.26	1.26/1.26
+# CTR	5.42/5.42   1.92/1.92   1.44/1.44   1.28/1.28   1.26/1.26
+# CBC	4.38/4.43   4.15/1.43   4.07/1.32   4.07/1.29   4.06/1.28
+# CCM	5.66/9.42   4.42/5.41   4.16/4.40   4.09/4.15   4.06/4.07   
+# OFB	5.42/5.42   4.64/4.64   4.44/4.44   4.39/4.39   4.38/4.38
+# CFB	5.73/5.85   5.56/5.62   5.48/5.56   5.47/5.55   5.47/5.55
+#
+# ECB, CTR, CBC and CCM results are free from EVP overhead. This means
+# that otherwise used 'openssl speed -evp aes-128-??? -engine aesni
+# [-decrypt]' will exhibit 10-15% worse results for smaller blocks.
+# The results were collected with specially crafted speed.c benchmark
+# in order to compare them with results reported in "Intel Advanced
+# Encryption Standard (AES) New Instruction Set" White Paper Revision
+# 3.0 dated May 2010. All above results are consistently better. This
+# module also provides better performance for block sizes smaller than
+# 128 bytes in points *not* represented in the above table.
+#
+# Looking at the results for 8-KB buffer.
+#
+# CFB and OFB results are far from the limit, because implementation
+# uses "generic" CRYPTO_[c|o]fb128_encrypt interfaces relying on
+# single-block aesni_encrypt, which is not the most optimal way to go.
+# CBC encrypt result is unexpectedly high and there is no documented
+# explanation for it. Seemingly there is a small penalty for feeding
+# the result back to AES unit the way it's done in CBC mode. There is
+# nothing one can do and the result appears optimal. CCM result is
+# identical to CBC, because CBC-MAC is essentially CBC encrypt without
+# saving output. CCM CTR "stays invisible," because it's neatly
+# interleaved wih CBC-MAC. This provides ~30% improvement over
+# "straghtforward" CCM implementation with CTR and CBC-MAC performed
+# disjointly. Parallelizable modes practically achieve the theoretical
+# limit.
+#
+# Looking at how results vary with buffer size.
+#
+# Curves are practically saturated at 1-KB buffer size. In most cases
+# "256-byte" performance is >95%, and "64-byte" is ~90% of "8-KB" one.
+# CTR curve doesn't follow this pattern and is "slowest" changing one
+# with "256-byte" result being 87% of "8-KB." This is because overhead
+# in CTR mode is most computationally intensive. Small-block CCM
+# decrypt is slower than encrypt, because first CTR and last CBC-MAC
+# iterations can't be interleaved.
+#
+# Results for 192- and 256-bit keys.
+#
+# EVP-free results were observed to scale perfectly with number of
+# rounds for larger block sizes, i.e. 192-bit result being 10/12 times
+# lower and 256-bit one - 10/14. Well, in CBC encrypt case differences
+# are a tad smaller, because the above mentioned penalty biases all
+# results by same constant value. In similar way function call
+# overhead affects small-block performance, as well as OFB and CFB
+# results. Differences are not large, most common coefficients are
+# 10/11.7 and 10/13.4 (as opposite to 10/12.0 and 10/14.0), but one
+# observe even 10/11.2 and 10/12.4 (CTR, OFB, CFB)...
+
+# January 2011
+#
+# While Westmere processor features 6 cycles latency for aes[enc|dec]
+# instructions, which can be scheduled every second cycle, Sandy
+# Bridge spends 8 cycles per instruction, but it can schedule them
+# every cycle. This means that code targeting Westmere would perform
+# suboptimally on Sandy Bridge. Therefore this update.
+#
+# In addition, non-parallelizable CBC encrypt (as well as CCM) is
+# optimized. Relative improvement might appear modest, 8% on Westmere,
+# but in absolute terms it's 3.77 cycles per byte encrypted with
+# 128-bit key on Westmere, and 5.07 - on Sandy Bridge. These numbers
+# should be compared to asymptotic limits of 3.75 for Westmere and
+# 5.00 for Sandy Bridge. Actually, the fact that they get this close
+# to asymptotic limits is quite amazing. Indeed, the limit is
+# calculated as latency times number of rounds, 10 for 128-bit key,
+# and divided by 16, the number of bytes in block, or in other words
+# it accounts *solely* for aesenc instructions. But there are extra
+# instructions, and numbers so close to the asymptotic limits mean
+# that it's as if it takes as little as *one* additional cycle to
+# execute all of them. How is it possible? It is possible thanks to
+# out-of-order execution logic, which manages to overlap post-
+# processing of previous block, things like saving the output, with
+# actual encryption of current block, as well as pre-processing of
+# current block, things like fetching input and xor-ing it with
+# 0-round element of the key schedule, with actual encryption of
+# previous block. Keep this in mind...
+#
+# For parallelizable modes, such as ECB, CBC decrypt, CTR, higher
+# performance is achieved by interleaving instructions working on
+# independent blocks. In which case asymptotic limit for such modes
+# can be obtained by dividing above mentioned numbers by AES
+# instructions' interleave factor. Westmere can execute at most 3 
+# instructions at a time, meaning that optimal interleave factor is 3,
+# and that's where the "magic" number of 1.25 come from. "Optimal
+# interleave factor" means that increase of interleave factor does
+# not improve performance. The formula has proven to reflect reality
+# pretty well on Westmere... Sandy Bridge on the other hand can
+# execute up to 8 AES instructions at a time, so how does varying
+# interleave factor affect the performance? Here is table for ECB
+# (numbers are cycles per byte processed with 128-bit key):
+#
+# instruction interleave factor		3x	6x	8x
+# theoretical asymptotic limit		1.67	0.83	0.625
+# measured performance for 8KB block	1.05	0.86	0.84
+#
+# "as if" interleave factor		4.7x	5.8x	6.0x
+#
+# Further data for other parallelizable modes:
+#
+# CBC decrypt				1.16	0.93	0.93
+# CTR					1.14	0.91	n/a
+#
+# Well, given 3x column it's probably inappropriate to call the limit
+# asymptotic, if it can be surpassed, isn't it? What happens there?
+# Rewind to CBC paragraph for the answer. Yes, out-of-order execution
+# magic is responsible for this. Processor overlaps not only the
+# additional instructions with AES ones, but even AES instuctions
+# processing adjacent triplets of independent blocks. In the 6x case
+# additional instructions  still claim disproportionally small amount
+# of additional cycles, but in 8x case number of instructions must be
+# a tad too high for out-of-order logic to cope with, and AES unit
+# remains underutilized... As you can see 8x interleave is hardly
+# justifiable, so there no need to feel bad that 32-bit aesni-x86.pl
+# utilizies 6x interleave because of limited register bank capacity.
+#
+# Higher interleave factors do have negative impact on Westmere
+# performance. While for ECB mode it's negligible ~1.5%, other
+# parallelizables perform ~5% worse, which is outweighed by ~25%
+# improvement on Sandy Bridge. To balance regression on Westmere
+# CTR mode was implemented with 6x aesenc interleave factor.
+
+# April 2011
+#
+# Add aesni_xts_[en|de]crypt. Westmere spends 1.33 cycles processing
+# one byte out of 8KB with 128-bit key, Sandy Bridge - 0.97. Just like
+# in CTR mode AES instruction interleave factor was chosen to be 6x.
+
+$PREFIX="aesni";	# if $PREFIX is set to "AES", the script
+			# generates drop-in replacement for
+			# crypto/aes/asm/aes-x86_64.pl:-)
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$movkey = $PREFIX eq "aesni" ? "movups" : "movups";
+@_4args=$win64?	("%rcx","%rdx","%r8", "%r9") :	# Win64 order
+		("%rdi","%rsi","%rdx","%rcx");	# Unix order
+
+$code=".text\n";
+
+$rounds="%eax";	# input to and changed by aesni_[en|de]cryptN !!!
+# this is natural Unix argument order for public $PREFIX_[ecb|cbc]_encrypt ...
+$inp="%rdi";
+$out="%rsi";
+$len="%rdx";
+$key="%rcx";	# input to and changed by aesni_[en|de]cryptN !!!
+$ivp="%r8";	# cbc, ctr, ...
+
+$rnds_="%r10d";	# backup copy for $rounds
+$key_="%r11";	# backup copy for $key
+
+# %xmm register layout
+$rndkey0="%xmm0";	$rndkey1="%xmm1";
+$inout0="%xmm2";	$inout1="%xmm3";
+$inout2="%xmm4";	$inout3="%xmm5";
+$inout4="%xmm6";	$inout5="%xmm7";
+$inout6="%xmm8";	$inout7="%xmm9";
+
+$in2="%xmm6";		$in1="%xmm7";	# used in CBC decrypt, CTR, ...
+$in0="%xmm8";		$iv="%xmm9";
+
+# Inline version of internal aesni_[en|de]crypt1.
+#
+# Why folded loop? Because aes[enc|dec] is slow enough to accommodate
+# cycles which take care of loop variables...
+{ my $sn;
+sub aesni_generate1 {
+my ($p,$key,$rounds,$inout,$ivec)=@_;	$inout=$inout0 if (!defined($inout));
+++$sn;
+$code.=<<___;
+	$movkey	($key),$rndkey0
+	$movkey	16($key),$rndkey1
+___
+$code.=<<___ if (defined($ivec));
+	xorps	$rndkey0,$ivec
+	lea	32($key),$key
+	xorps	$ivec,$inout
+___
+$code.=<<___ if (!defined($ivec));
+	lea	32($key),$key
+	xorps	$rndkey0,$inout
+___
+$code.=<<___;
+.Loop_${p}1_$sn:
+	aes${p}	$rndkey1,$inout
+	dec	$rounds
+	$movkey	($key),$rndkey1
+	lea	16($key),$key
+	jnz	.Loop_${p}1_$sn	# loop body is 16 bytes
+	aes${p}last	$rndkey1,$inout
+___
+}}
+# void $PREFIX_[en|de]crypt (const void *inp,void *out,const AES_KEY *key);
+#
+{ my ($inp,$out,$key) = @_4args;
+
+$code.=<<___;
+.globl	${PREFIX}_encrypt
+.type	${PREFIX}_encrypt,\@abi-omnipotent
+.align	16
+${PREFIX}_encrypt:
+	movups	($inp),$inout0		# load input
+	mov	240($key),$rounds	# key->rounds
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	movups	$inout0,($out)		# output
+	ret
+.size	${PREFIX}_encrypt,.-${PREFIX}_encrypt
+
+.globl	${PREFIX}_decrypt
+.type	${PREFIX}_decrypt,\@abi-omnipotent
+.align	16
+${PREFIX}_decrypt:
+	movups	($inp),$inout0		# load input
+	mov	240($key),$rounds	# key->rounds
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	movups	$inout0,($out)		# output
+	ret
+.size	${PREFIX}_decrypt, .-${PREFIX}_decrypt
+___
+}
+
+# _aesni_[en|de]cryptN are private interfaces, N denotes interleave
+# factor. Why 3x subroutine were originally used in loops? Even though
+# aes[enc|dec] latency was originally 6, it could be scheduled only
+# every *2nd* cycle. Thus 3x interleave was the one providing optimal
+# utilization, i.e. when subroutine's throughput is virtually same as
+# of non-interleaved subroutine [for number of input blocks up to 3].
+# This is why it makes no sense to implement 2x subroutine.
+# aes[enc|dec] latency in next processor generation is 8, but the
+# instructions can be scheduled every cycle. Optimal interleave for
+# new processor is therefore 8x...
+sub aesni_generate3 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-2] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt3,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt3:
+	$movkey	($key),$rndkey0
+	shr	\$1,$rounds
+	$movkey	16($key),$rndkey1
+	lea	32($key),$key
+	xorps	$rndkey0,$inout0
+	xorps	$rndkey0,$inout1
+	xorps	$rndkey0,$inout2
+	$movkey		($key),$rndkey0
+
+.L${dir}_loop3:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	dec		$rounds
+	aes${dir}	$rndkey1,$inout2
+	$movkey		16($key),$rndkey1
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	lea		32($key),$key
+	aes${dir}	$rndkey0,$inout2
+	$movkey		($key),$rndkey0
+	jnz		.L${dir}_loop3
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	ret
+.size	_aesni_${dir}rypt3,.-_aesni_${dir}rypt3
+___
+}
+# 4x interleave is implemented to improve small block performance,
+# most notably [and naturally] 4 block by ~30%. One can argue that one
+# should have implemented 5x as well, but improvement would be <20%,
+# so it's not worth it...
+sub aesni_generate4 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-3] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt4,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt4:
+	$movkey	($key),$rndkey0
+	shr	\$1,$rounds
+	$movkey	16($key),$rndkey1
+	lea	32($key),$key
+	xorps	$rndkey0,$inout0
+	xorps	$rndkey0,$inout1
+	xorps	$rndkey0,$inout2
+	xorps	$rndkey0,$inout3
+	$movkey	($key),$rndkey0
+
+.L${dir}_loop4:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	dec		$rounds
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	$movkey		16($key),$rndkey1
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	lea		32($key),$key
+	aes${dir}	$rndkey0,$inout2
+	aes${dir}	$rndkey0,$inout3
+	$movkey		($key),$rndkey0
+	jnz		.L${dir}_loop4
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	aes${dir}last	$rndkey0,$inout3
+	ret
+.size	_aesni_${dir}rypt4,.-_aesni_${dir}rypt4
+___
+}
+sub aesni_generate6 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-5] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt6,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt6:
+	$movkey		($key),$rndkey0
+	shr		\$1,$rounds
+	$movkey		16($key),$rndkey1
+	lea		32($key),$key
+	xorps		$rndkey0,$inout0
+	pxor		$rndkey0,$inout1
+	aes${dir}	$rndkey1,$inout0
+	pxor		$rndkey0,$inout2
+	aes${dir}	$rndkey1,$inout1
+	pxor		$rndkey0,$inout3
+	aes${dir}	$rndkey1,$inout2
+	pxor		$rndkey0,$inout4
+	aes${dir}	$rndkey1,$inout3
+	pxor		$rndkey0,$inout5
+	dec		$rounds
+	aes${dir}	$rndkey1,$inout4
+	$movkey		($key),$rndkey0
+	aes${dir}	$rndkey1,$inout5
+	jmp		.L${dir}_loop6_enter
+.align	16
+.L${dir}_loop6:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	dec		$rounds
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+.L${dir}_loop6_enter:				# happens to be 16-byte aligned
+	$movkey		16($key),$rndkey1
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	lea		32($key),$key
+	aes${dir}	$rndkey0,$inout2
+	aes${dir}	$rndkey0,$inout3
+	aes${dir}	$rndkey0,$inout4
+	aes${dir}	$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	jnz		.L${dir}_loop6
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	aes${dir}last	$rndkey0,$inout3
+	aes${dir}last	$rndkey0,$inout4
+	aes${dir}last	$rndkey0,$inout5
+	ret
+.size	_aesni_${dir}rypt6,.-_aesni_${dir}rypt6
+___
+}
+sub aesni_generate8 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-7] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt8,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt8:
+	$movkey		($key),$rndkey0
+	shr		\$1,$rounds
+	$movkey		16($key),$rndkey1
+	lea		32($key),$key
+	xorps		$rndkey0,$inout0
+	xorps		$rndkey0,$inout1
+	aes${dir}	$rndkey1,$inout0
+	pxor		$rndkey0,$inout2
+	aes${dir}	$rndkey1,$inout1
+	pxor		$rndkey0,$inout3
+	aes${dir}	$rndkey1,$inout2
+	pxor		$rndkey0,$inout4
+	aes${dir}	$rndkey1,$inout3
+	pxor		$rndkey0,$inout5
+	dec		$rounds
+	aes${dir}	$rndkey1,$inout4
+	pxor		$rndkey0,$inout6
+	aes${dir}	$rndkey1,$inout5
+	pxor		$rndkey0,$inout7
+	$movkey		($key),$rndkey0
+	aes${dir}	$rndkey1,$inout6
+	aes${dir}	$rndkey1,$inout7
+	$movkey		16($key),$rndkey1
+	jmp		.L${dir}_loop8_enter
+.align	16
+.L${dir}_loop8:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	dec		$rounds
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	aes${dir}	$rndkey1,$inout6
+	aes${dir}	$rndkey1,$inout7
+	$movkey		16($key),$rndkey1
+.L${dir}_loop8_enter:				# happens to be 16-byte aligned
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	lea		32($key),$key
+	aes${dir}	$rndkey0,$inout2
+	aes${dir}	$rndkey0,$inout3
+	aes${dir}	$rndkey0,$inout4
+	aes${dir}	$rndkey0,$inout5
+	aes${dir}	$rndkey0,$inout6
+	aes${dir}	$rndkey0,$inout7
+	$movkey		($key),$rndkey0
+	jnz		.L${dir}_loop8
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	aes${dir}	$rndkey1,$inout6
+	aes${dir}	$rndkey1,$inout7
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	aes${dir}last	$rndkey0,$inout3
+	aes${dir}last	$rndkey0,$inout4
+	aes${dir}last	$rndkey0,$inout5
+	aes${dir}last	$rndkey0,$inout6
+	aes${dir}last	$rndkey0,$inout7
+	ret
+.size	_aesni_${dir}rypt8,.-_aesni_${dir}rypt8
+___
+}
+&aesni_generate3("enc") if ($PREFIX eq "aesni");
+&aesni_generate3("dec");
+&aesni_generate4("enc") if ($PREFIX eq "aesni");
+&aesni_generate4("dec");
+&aesni_generate6("enc") if ($PREFIX eq "aesni");
+&aesni_generate6("dec");
+&aesni_generate8("enc") if ($PREFIX eq "aesni");
+&aesni_generate8("dec");
+
+if ($PREFIX eq "aesni") {
+########################################################################
+# void aesni_ecb_encrypt (const void *in, void *out,
+#			  size_t length, const AES_KEY *key,
+#			  int enc);
+$code.=<<___;
+.globl	aesni_ecb_encrypt
+.type	aesni_ecb_encrypt,\@function,5
+.align	16
+aesni_ecb_encrypt:
+	and	\$-16,$len
+	jz	.Lecb_ret
+
+	mov	240($key),$rounds	# key->rounds
+	$movkey	($key),$rndkey0
+	mov	$key,$key_		# backup $key
+	mov	$rounds,$rnds_		# backup $rounds
+	test	%r8d,%r8d		# 5th argument
+	jz	.Lecb_decrypt
+#--------------------------- ECB ENCRYPT ------------------------------#
+	cmp	\$0x80,$len
+	jb	.Lecb_enc_tail
+
+	movdqu	($inp),$inout0
+	movdqu	0x10($inp),$inout1
+	movdqu	0x20($inp),$inout2
+	movdqu	0x30($inp),$inout3
+	movdqu	0x40($inp),$inout4
+	movdqu	0x50($inp),$inout5
+	movdqu	0x60($inp),$inout6
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp
+	sub	\$0x80,$len
+	jmp	.Lecb_enc_loop8_enter
+.align 16
+.Lecb_enc_loop8:
+	movups	$inout0,($out)
+	mov	$key_,$key		# restore $key
+	movdqu	($inp),$inout0
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout1,0x10($out)
+	movdqu	0x10($inp),$inout1
+	movups	$inout2,0x20($out)
+	movdqu	0x20($inp),$inout2
+	movups	$inout3,0x30($out)
+	movdqu	0x30($inp),$inout3
+	movups	$inout4,0x40($out)
+	movdqu	0x40($inp),$inout4
+	movups	$inout5,0x50($out)
+	movdqu	0x50($inp),$inout5
+	movups	$inout6,0x60($out)
+	movdqu	0x60($inp),$inout6
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp
+.Lecb_enc_loop8_enter:
+
+	call	_aesni_encrypt8
+
+	sub	\$0x80,$len
+	jnc	.Lecb_enc_loop8
+
+	movups	$inout0,($out)
+	mov	$key_,$key		# restore $key
+	movups	$inout1,0x10($out)
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	movups	$inout6,0x60($out)
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out
+	add	\$0x80,$len
+	jz	.Lecb_ret
+
+.Lecb_enc_tail:
+	movups	($inp),$inout0
+	cmp	\$0x20,$len
+	jb	.Lecb_enc_one
+	movups	0x10($inp),$inout1
+	je	.Lecb_enc_two
+	movups	0x20($inp),$inout2
+	cmp	\$0x40,$len
+	jb	.Lecb_enc_three
+	movups	0x30($inp),$inout3
+	je	.Lecb_enc_four
+	movups	0x40($inp),$inout4
+	cmp	\$0x60,$len
+	jb	.Lecb_enc_five
+	movups	0x50($inp),$inout5
+	je	.Lecb_enc_six
+	movdqu	0x60($inp),$inout6
+	call	_aesni_encrypt8
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	movups	$inout6,0x60($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_one:
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	movups	$inout0,($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_two:
+	xorps	$inout2,$inout2
+	call	_aesni_encrypt3
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_three:
+	call	_aesni_encrypt3
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_four:
+	call	_aesni_encrypt4
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_five:
+	xorps	$inout5,$inout5
+	call	_aesni_encrypt6
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_six:
+	call	_aesni_encrypt6
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	jmp	.Lecb_ret
+#--------------------------- ECB DECRYPT ------------------------------#
+.align	16
+.Lecb_decrypt:
+	cmp	\$0x80,$len
+	jb	.Lecb_dec_tail
+
+	movdqu	($inp),$inout0
+	movdqu	0x10($inp),$inout1
+	movdqu	0x20($inp),$inout2
+	movdqu	0x30($inp),$inout3
+	movdqu	0x40($inp),$inout4
+	movdqu	0x50($inp),$inout5
+	movdqu	0x60($inp),$inout6
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp
+	sub	\$0x80,$len
+	jmp	.Lecb_dec_loop8_enter
+.align 16
+.Lecb_dec_loop8:
+	movups	$inout0,($out)
+	mov	$key_,$key		# restore $key
+	movdqu	($inp),$inout0
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout1,0x10($out)
+	movdqu	0x10($inp),$inout1
+	movups	$inout2,0x20($out)
+	movdqu	0x20($inp),$inout2
+	movups	$inout3,0x30($out)
+	movdqu	0x30($inp),$inout3
+	movups	$inout4,0x40($out)
+	movdqu	0x40($inp),$inout4
+	movups	$inout5,0x50($out)
+	movdqu	0x50($inp),$inout5
+	movups	$inout6,0x60($out)
+	movdqu	0x60($inp),$inout6
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp
+.Lecb_dec_loop8_enter:
+
+	call	_aesni_decrypt8
+
+	$movkey	($key_),$rndkey0
+	sub	\$0x80,$len
+	jnc	.Lecb_dec_loop8
+
+	movups	$inout0,($out)
+	mov	$key_,$key		# restore $key
+	movups	$inout1,0x10($out)
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	movups	$inout6,0x60($out)
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out
+	add	\$0x80,$len
+	jz	.Lecb_ret
+
+.Lecb_dec_tail:
+	movups	($inp),$inout0
+	cmp	\$0x20,$len
+	jb	.Lecb_dec_one
+	movups	0x10($inp),$inout1
+	je	.Lecb_dec_two
+	movups	0x20($inp),$inout2
+	cmp	\$0x40,$len
+	jb	.Lecb_dec_three
+	movups	0x30($inp),$inout3
+	je	.Lecb_dec_four
+	movups	0x40($inp),$inout4
+	cmp	\$0x60,$len
+	jb	.Lecb_dec_five
+	movups	0x50($inp),$inout5
+	je	.Lecb_dec_six
+	movups	0x60($inp),$inout6
+	$movkey	($key),$rndkey0
+	call	_aesni_decrypt8
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	movups	$inout6,0x60($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_one:
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	movups	$inout0,($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_two:
+	xorps	$inout2,$inout2
+	call	_aesni_decrypt3
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_three:
+	call	_aesni_decrypt3
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_four:
+	call	_aesni_decrypt4
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_five:
+	xorps	$inout5,$inout5
+	call	_aesni_decrypt6
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_six:
+	call	_aesni_decrypt6
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+
+.Lecb_ret:
+	ret
+.size	aesni_ecb_encrypt,.-aesni_ecb_encrypt
+___
+
+{
+######################################################################
+# void aesni_ccm64_[en|de]crypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec,char *cmac);
+#
+# Handles only complete blocks, operates on 64-bit counter and
+# does not update *ivec! Nor does it finalize CMAC value
+# (see engine/eng_aesni.c for details)
+#
+{
+my $cmac="%r9";	# 6th argument
+
+my $increment="%xmm6";
+my $bswap_mask="%xmm7";
+
+$code.=<<___;
+.globl	aesni_ccm64_encrypt_blocks
+.type	aesni_ccm64_encrypt_blocks,\@function,6
+.align	16
+aesni_ccm64_encrypt_blocks:
+___
+$code.=<<___ if ($win64);
+	lea	-0x58(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+.Lccm64_enc_body:
+___
+$code.=<<___;
+	mov	240($key),$rounds		# key->rounds
+	movdqu	($ivp),$iv
+	movdqa	.Lincrement64(%rip),$increment
+	movdqa	.Lbswap_mask(%rip),$bswap_mask
+
+	shr	\$1,$rounds
+	lea	0($key),$key_
+	movdqu	($cmac),$inout1
+	movdqa	$iv,$inout0
+	mov	$rounds,$rnds_
+	pshufb	$bswap_mask,$iv
+	jmp	.Lccm64_enc_outer
+.align	16
+.Lccm64_enc_outer:
+	$movkey	($key_),$rndkey0
+	mov	$rnds_,$rounds
+	movups	($inp),$in0			# load inp
+
+	xorps	$rndkey0,$inout0		# counter
+	$movkey	16($key_),$rndkey1
+	xorps	$in0,$rndkey0
+	lea	32($key_),$key
+	xorps	$rndkey0,$inout1		# cmac^=inp
+	$movkey	($key),$rndkey0
+
+.Lccm64_enc2_loop:
+	aesenc	$rndkey1,$inout0
+	dec	$rounds
+	aesenc	$rndkey1,$inout1
+	$movkey	16($key),$rndkey1
+	aesenc	$rndkey0,$inout0
+	lea	32($key),$key
+	aesenc	$rndkey0,$inout1
+	$movkey	0($key),$rndkey0
+	jnz	.Lccm64_enc2_loop
+	aesenc	$rndkey1,$inout0
+	aesenc	$rndkey1,$inout1
+	paddq	$increment,$iv
+	aesenclast	$rndkey0,$inout0
+	aesenclast	$rndkey0,$inout1
+
+	dec	$len
+	lea	16($inp),$inp
+	xorps	$inout0,$in0			# inp ^= E(iv)
+	movdqa	$iv,$inout0
+	movups	$in0,($out)			# save output
+	lea	16($out),$out
+	pshufb	$bswap_mask,$inout0
+	jnz	.Lccm64_enc_outer
+
+	movups	$inout1,($cmac)
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	lea	0x58(%rsp),%rsp
+.Lccm64_enc_ret:
+___
+$code.=<<___;
+	ret
+.size	aesni_ccm64_encrypt_blocks,.-aesni_ccm64_encrypt_blocks
+___
+######################################################################
+$code.=<<___;
+.globl	aesni_ccm64_decrypt_blocks
+.type	aesni_ccm64_decrypt_blocks,\@function,6
+.align	16
+aesni_ccm64_decrypt_blocks:
+___
+$code.=<<___ if ($win64);
+	lea	-0x58(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+.Lccm64_dec_body:
+___
+$code.=<<___;
+	mov	240($key),$rounds		# key->rounds
+	movups	($ivp),$iv
+	movdqu	($cmac),$inout1
+	movdqa	.Lincrement64(%rip),$increment
+	movdqa	.Lbswap_mask(%rip),$bswap_mask
+
+	movaps	$iv,$inout0
+	mov	$rounds,$rnds_
+	mov	$key,$key_
+	pshufb	$bswap_mask,$iv
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	movups	($inp),$in0			# load inp
+	paddq	$increment,$iv
+	lea	16($inp),$inp
+	jmp	.Lccm64_dec_outer
+.align	16
+.Lccm64_dec_outer:
+	xorps	$inout0,$in0			# inp ^= E(iv)
+	movdqa	$iv,$inout0
+	mov	$rnds_,$rounds
+	movups	$in0,($out)			# save output
+	lea	16($out),$out
+	pshufb	$bswap_mask,$inout0
+
+	sub	\$1,$len
+	jz	.Lccm64_dec_break
+
+	$movkey	($key_),$rndkey0
+	shr	\$1,$rounds
+	$movkey	16($key_),$rndkey1
+	xorps	$rndkey0,$in0
+	lea	32($key_),$key
+	xorps	$rndkey0,$inout0
+	xorps	$in0,$inout1			# cmac^=out
+	$movkey	($key),$rndkey0
+
+.Lccm64_dec2_loop:
+	aesenc	$rndkey1,$inout0
+	dec	$rounds
+	aesenc	$rndkey1,$inout1
+	$movkey	16($key),$rndkey1
+	aesenc	$rndkey0,$inout0
+	lea	32($key),$key
+	aesenc	$rndkey0,$inout1
+	$movkey	0($key),$rndkey0
+	jnz	.Lccm64_dec2_loop
+	movups	($inp),$in0			# load inp
+	paddq	$increment,$iv
+	aesenc	$rndkey1,$inout0
+	aesenc	$rndkey1,$inout1
+	lea	16($inp),$inp
+	aesenclast	$rndkey0,$inout0
+	aesenclast	$rndkey0,$inout1
+	jmp	.Lccm64_dec_outer
+
+.align	16
+.Lccm64_dec_break:
+	#xorps	$in0,$inout1			# cmac^=out
+___
+	&aesni_generate1("enc",$key_,$rounds,$inout1,$in0);
+$code.=<<___;
+	movups	$inout1,($cmac)
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	lea	0x58(%rsp),%rsp
+.Lccm64_dec_ret:
+___
+$code.=<<___;
+	ret
+.size	aesni_ccm64_decrypt_blocks,.-aesni_ccm64_decrypt_blocks
+___
+}
+######################################################################
+# void aesni_ctr32_encrypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec);
+#
+# Handles only complete blocks, operates on 32-bit counter and
+# does not update *ivec! (see engine/eng_aesni.c for details)
+#
+{
+my $reserved = $win64?0:-0x28;
+my ($in0,$in1,$in2,$in3)=map("%xmm$_",(8..11));
+my ($iv0,$iv1,$ivec)=("%xmm12","%xmm13","%xmm14");
+my $bswap_mask="%xmm15";
+
+$code.=<<___;
+.globl	aesni_ctr32_encrypt_blocks
+.type	aesni_ctr32_encrypt_blocks,\@function,5
+.align	16
+aesni_ctr32_encrypt_blocks:
+___
+$code.=<<___ if ($win64);
+	lea	-0xc8(%rsp),%rsp
+	movaps	%xmm6,0x20(%rsp)
+	movaps	%xmm7,0x30(%rsp)
+	movaps	%xmm8,0x40(%rsp)
+	movaps	%xmm9,0x50(%rsp)
+	movaps	%xmm10,0x60(%rsp)
+	movaps	%xmm11,0x70(%rsp)
+	movaps	%xmm12,0x80(%rsp)
+	movaps	%xmm13,0x90(%rsp)
+	movaps	%xmm14,0xa0(%rsp)
+	movaps	%xmm15,0xb0(%rsp)
+.Lctr32_body:
+___
+$code.=<<___;
+	cmp	\$1,$len
+	je	.Lctr32_one_shortcut
+
+	movdqu	($ivp),$ivec
+	movdqa	.Lbswap_mask(%rip),$bswap_mask
+	xor	$rounds,$rounds
+	pextrd	\$3,$ivec,$rnds_		# pull 32-bit counter
+	pinsrd	\$3,$rounds,$ivec		# wipe 32-bit counter
+
+	mov	240($key),$rounds		# key->rounds
+	bswap	$rnds_
+	pxor	$iv0,$iv0			# vector of 3 32-bit counters
+	pxor	$iv1,$iv1			# vector of 3 32-bit counters
+	pinsrd	\$0,$rnds_,$iv0
+	lea	3($rnds_),$key_
+	pinsrd	\$0,$key_,$iv1
+	inc	$rnds_
+	pinsrd	\$1,$rnds_,$iv0
+	inc	$key_
+	pinsrd	\$1,$key_,$iv1
+	inc	$rnds_
+	pinsrd	\$2,$rnds_,$iv0
+	inc	$key_
+	pinsrd	\$2,$key_,$iv1
+	movdqa	$iv0,$reserved(%rsp)
+	pshufb	$bswap_mask,$iv0
+	movdqa	$iv1,`$reserved+0x10`(%rsp)
+	pshufb	$bswap_mask,$iv1
+
+	pshufd	\$`3<<6`,$iv0,$inout0		# place counter to upper dword
+	pshufd	\$`2<<6`,$iv0,$inout1
+	pshufd	\$`1<<6`,$iv0,$inout2
+	cmp	\$6,$len
+	jb	.Lctr32_tail
+	shr	\$1,$rounds
+	mov	$key,$key_			# backup $key
+	mov	$rounds,$rnds_			# backup $rounds
+	sub	\$6,$len
+	jmp	.Lctr32_loop6
+
+.align	16
+.Lctr32_loop6:
+	pshufd	\$`3<<6`,$iv1,$inout3
+	por	$ivec,$inout0			# merge counter-less ivec
+	 $movkey	($key_),$rndkey0
+	pshufd	\$`2<<6`,$iv1,$inout4
+	por	$ivec,$inout1
+	 $movkey	16($key_),$rndkey1
+	pshufd	\$`1<<6`,$iv1,$inout5
+	por	$ivec,$inout2
+	por	$ivec,$inout3
+	 xorps		$rndkey0,$inout0
+	por	$ivec,$inout4
+	por	$ivec,$inout5
+
+	# inline _aesni_encrypt6 and interleave last rounds
+	# with own code...
+
+	pxor		$rndkey0,$inout1
+	aesenc		$rndkey1,$inout0
+	lea		32($key_),$key
+	pxor		$rndkey0,$inout2
+	aesenc		$rndkey1,$inout1
+	 movdqa		.Lincrement32(%rip),$iv1
+	pxor		$rndkey0,$inout3
+	aesenc		$rndkey1,$inout2
+	 movdqa		$reserved(%rsp),$iv0
+	pxor		$rndkey0,$inout4
+	aesenc		$rndkey1,$inout3
+	pxor		$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	dec		$rounds
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	jmp		.Lctr32_enc_loop6_enter
+.align	16
+.Lctr32_enc_loop6:
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	dec		$rounds
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+.Lctr32_enc_loop6_enter:
+	$movkey		16($key),$rndkey1
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	lea		32($key),$key
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	jnz		.Lctr32_enc_loop6
+
+	aesenc		$rndkey1,$inout0
+	 paddd		$iv1,$iv0		# increment counter vector
+	aesenc		$rndkey1,$inout1
+	 paddd		`$reserved+0x10`(%rsp),$iv1
+	aesenc		$rndkey1,$inout2
+	 movdqa		$iv0,$reserved(%rsp)	# save counter vector
+	aesenc		$rndkey1,$inout3
+	 movdqa		$iv1,`$reserved+0x10`(%rsp)
+	aesenc		$rndkey1,$inout4
+	 pshufb		$bswap_mask,$iv0	# byte swap
+	aesenc		$rndkey1,$inout5
+	 pshufb		$bswap_mask,$iv1
+
+	aesenclast	$rndkey0,$inout0
+	 movups		($inp),$in0		# load input
+	aesenclast	$rndkey0,$inout1
+	 movups		0x10($inp),$in1
+	aesenclast	$rndkey0,$inout2
+	 movups		0x20($inp),$in2
+	aesenclast	$rndkey0,$inout3
+	 movups		0x30($inp),$in3
+	aesenclast	$rndkey0,$inout4
+	 movups		0x40($inp),$rndkey1
+	aesenclast	$rndkey0,$inout5
+	 movups		0x50($inp),$rndkey0
+	 lea	0x60($inp),$inp
+
+	xorps	$inout0,$in0			# xor
+	 pshufd	\$`3<<6`,$iv0,$inout0
+	xorps	$inout1,$in1
+	 pshufd	\$`2<<6`,$iv0,$inout1
+	movups	$in0,($out)			# store output
+	xorps	$inout2,$in2
+	 pshufd	\$`1<<6`,$iv0,$inout2
+	movups	$in1,0x10($out)
+	xorps	$inout3,$in3
+	movups	$in2,0x20($out)
+	xorps	$inout4,$rndkey1
+	movups	$in3,0x30($out)
+	xorps	$inout5,$rndkey0
+	movups	$rndkey1,0x40($out)
+	movups	$rndkey0,0x50($out)
+	lea	0x60($out),$out
+	mov	$rnds_,$rounds
+	sub	\$6,$len
+	jnc	.Lctr32_loop6
+
+	add	\$6,$len
+	jz	.Lctr32_done
+	mov	$key_,$key			# restore $key
+	lea	1($rounds,$rounds),$rounds	# restore original value
+
+.Lctr32_tail:
+	por	$ivec,$inout0
+	movups	($inp),$in0
+	cmp	\$2,$len
+	jb	.Lctr32_one
+
+	por	$ivec,$inout1
+	movups	0x10($inp),$in1
+	je	.Lctr32_two
+
+	pshufd	\$`3<<6`,$iv1,$inout3
+	por	$ivec,$inout2
+	movups	0x20($inp),$in2
+	cmp	\$4,$len
+	jb	.Lctr32_three
+
+	pshufd	\$`2<<6`,$iv1,$inout4
+	por	$ivec,$inout3
+	movups	0x30($inp),$in3
+	je	.Lctr32_four
+
+	por	$ivec,$inout4
+	xorps	$inout5,$inout5
+
+	call	_aesni_encrypt6
+
+	movups	0x40($inp),$rndkey1
+	xorps	$inout0,$in0
+	xorps	$inout1,$in1
+	movups	$in0,($out)
+	xorps	$inout2,$in2
+	movups	$in1,0x10($out)
+	xorps	$inout3,$in3
+	movups	$in2,0x20($out)
+	xorps	$inout4,$rndkey1
+	movups	$in3,0x30($out)
+	movups	$rndkey1,0x40($out)
+	jmp	.Lctr32_done
+
+.align	16
+.Lctr32_one_shortcut:
+	movups	($ivp),$inout0
+	movups	($inp),$in0
+	mov	240($key),$rounds		# key->rounds
+.Lctr32_one:
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	xorps	$inout0,$in0
+	movups	$in0,($out)
+	jmp	.Lctr32_done
+
+.align	16
+.Lctr32_two:
+	xorps	$inout2,$inout2
+	call	_aesni_encrypt3
+	xorps	$inout0,$in0
+	xorps	$inout1,$in1
+	movups	$in0,($out)
+	movups	$in1,0x10($out)
+	jmp	.Lctr32_done
+
+.align	16
+.Lctr32_three:
+	call	_aesni_encrypt3
+	xorps	$inout0,$in0
+	xorps	$inout1,$in1
+	movups	$in0,($out)
+	xorps	$inout2,$in2
+	movups	$in1,0x10($out)
+	movups	$in2,0x20($out)
+	jmp	.Lctr32_done
+
+.align	16
+.Lctr32_four:
+	call	_aesni_encrypt4
+	xorps	$inout0,$in0
+	xorps	$inout1,$in1
+	movups	$in0,($out)
+	xorps	$inout2,$in2
+	movups	$in1,0x10($out)
+	xorps	$inout3,$in3
+	movups	$in2,0x20($out)
+	movups	$in3,0x30($out)
+
+.Lctr32_done:
+___
+$code.=<<___ if ($win64);
+	movaps	0x20(%rsp),%xmm6
+	movaps	0x30(%rsp),%xmm7
+	movaps	0x40(%rsp),%xmm8
+	movaps	0x50(%rsp),%xmm9
+	movaps	0x60(%rsp),%xmm10
+	movaps	0x70(%rsp),%xmm11
+	movaps	0x80(%rsp),%xmm12
+	movaps	0x90(%rsp),%xmm13
+	movaps	0xa0(%rsp),%xmm14
+	movaps	0xb0(%rsp),%xmm15
+	lea	0xc8(%rsp),%rsp
+.Lctr32_ret:
+___
+$code.=<<___;
+	ret
+.size	aesni_ctr32_encrypt_blocks,.-aesni_ctr32_encrypt_blocks
+___
+}
+
+######################################################################
+# void aesni_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2
+#	const unsigned char iv[16]);
+#
+{
+my @tweak=map("%xmm$_",(10..15));
+my ($twmask,$twres,$twtmp)=("%xmm8","%xmm9",@tweak[4]);
+my ($key2,$ivp,$len_)=("%r8","%r9","%r9");
+my $frame_size = 0x68 + ($win64?160:0);
+
+$code.=<<___;
+.globl	aesni_xts_encrypt
+.type	aesni_xts_encrypt,\@function,6
+.align	16
+aesni_xts_encrypt:
+	lea	-$frame_size(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,0x60(%rsp)
+	movaps	%xmm7,0x70(%rsp)
+	movaps	%xmm8,0x80(%rsp)
+	movaps	%xmm9,0x90(%rsp)
+	movaps	%xmm10,0xa0(%rsp)
+	movaps	%xmm11,0xb0(%rsp)
+	movaps	%xmm12,0xc0(%rsp)
+	movaps	%xmm13,0xd0(%rsp)
+	movaps	%xmm14,0xe0(%rsp)
+	movaps	%xmm15,0xf0(%rsp)
+.Lxts_enc_body:
+___
+$code.=<<___;
+	movups	($ivp),@tweak[5]		# load clear-text tweak
+	mov	240(%r8),$rounds		# key2->rounds
+	mov	240($key),$rnds_		# key1->rounds
+___
+	# generate the tweak
+	&aesni_generate1("enc",$key2,$rounds,@tweak[5]);
+$code.=<<___;
+	mov	$key,$key_			# backup $key
+	mov	$rnds_,$rounds			# backup $rounds
+	mov	$len,$len_			# backup $len
+	and	\$-16,$len
+
+	movdqa	.Lxts_magic(%rip),$twmask
+	pxor	$twtmp,$twtmp
+	pcmpgtd	@tweak[5],$twtmp		# broadcast upper bits
+___
+    for ($i=0;$i<4;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[$i]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	pand	$twmask,$twres			# isolate carry and residue
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	pxor	$twres,@tweak[5]
+___
+    }
+$code.=<<___;
+	sub	\$16*6,$len
+	jc	.Lxts_enc_short
+
+	shr	\$1,$rounds
+	sub	\$1,$rounds
+	mov	$rounds,$rnds_
+	jmp	.Lxts_enc_grandloop
+
+.align	16
+.Lxts_enc_grandloop:
+	pshufd	\$0x13,$twtmp,$twres
+	movdqa	@tweak[5],@tweak[4]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	movdqu	`16*0`($inp),$inout0		# load input
+	pand	$twmask,$twres			# isolate carry and residue
+	movdqu	`16*1`($inp),$inout1
+	pxor	$twres,@tweak[5]
+
+	movdqu	`16*2`($inp),$inout2
+	pxor	@tweak[0],$inout0		# input^=tweak
+	movdqu	`16*3`($inp),$inout3
+	pxor	@tweak[1],$inout1
+	movdqu	`16*4`($inp),$inout4
+	pxor	@tweak[2],$inout2
+	movdqu	`16*5`($inp),$inout5
+	lea	`16*6`($inp),$inp
+	pxor	@tweak[3],$inout3
+	$movkey		($key_),$rndkey0
+	pxor	@tweak[4],$inout4
+	pxor	@tweak[5],$inout5
+
+	# inline _aesni_encrypt6 and interleave first and last rounds
+	# with own code...
+	$movkey		16($key_),$rndkey1
+	pxor		$rndkey0,$inout0
+	pxor		$rndkey0,$inout1
+	 movdqa	@tweak[0],`16*0`(%rsp)		# put aside tweaks
+	aesenc		$rndkey1,$inout0
+	lea		32($key_),$key
+	pxor		$rndkey0,$inout2
+	 movdqa	@tweak[1],`16*1`(%rsp)
+	aesenc		$rndkey1,$inout1
+	pxor		$rndkey0,$inout3
+	 movdqa	@tweak[2],`16*2`(%rsp)
+	aesenc		$rndkey1,$inout2
+	pxor		$rndkey0,$inout4
+	 movdqa	@tweak[3],`16*3`(%rsp)
+	aesenc		$rndkey1,$inout3
+	pxor		$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	dec		$rounds
+	 movdqa	@tweak[4],`16*4`(%rsp)
+	aesenc		$rndkey1,$inout4
+	 movdqa	@tweak[5],`16*5`(%rsp)
+	aesenc		$rndkey1,$inout5
+	pxor	$twtmp,$twtmp
+	pcmpgtd	@tweak[5],$twtmp
+	jmp		.Lxts_enc_loop6_enter
+
+.align	16
+.Lxts_enc_loop6:
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	dec		$rounds
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+.Lxts_enc_loop6_enter:
+	$movkey		16($key),$rndkey1
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	lea		32($key),$key
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	jnz		.Lxts_enc_loop6
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesenc		$rndkey1,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesenc		$rndkey1,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcast upper bits
+	 aesenc		$rndkey1,$inout2
+	pxor	$twres,@tweak[5]
+	 aesenc		$rndkey1,$inout3
+	 aesenc		$rndkey1,$inout4
+	 aesenc		$rndkey1,$inout5
+	 $movkey	16($key),$rndkey1
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[0]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesenc		$rndkey0,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesenc		$rndkey0,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	 aesenc		$rndkey0,$inout2
+	pxor	$twres,@tweak[5]
+	 aesenc		$rndkey0,$inout3
+	 aesenc		$rndkey0,$inout4
+	 aesenc		$rndkey0,$inout5
+	 $movkey	32($key),$rndkey0
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[1]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesenc		$rndkey1,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesenc		$rndkey1,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	 aesenc		$rndkey1,$inout2
+	pxor	$twres,@tweak[5]
+	 aesenc		$rndkey1,$inout3
+	 aesenc		$rndkey1,$inout4
+	 aesenc		$rndkey1,$inout5
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[2]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesenclast	$rndkey0,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesenclast	$rndkey0,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	 aesenclast	$rndkey0,$inout2
+	pxor	$twres,@tweak[5]
+	 aesenclast	$rndkey0,$inout3
+	 aesenclast	$rndkey0,$inout4
+	 aesenclast	$rndkey0,$inout5
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[3]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 xorps	`16*0`(%rsp),$inout0		# output^=tweak
+	pand	$twmask,$twres			# isolate carry and residue
+	 xorps	`16*1`(%rsp),$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	pxor	$twres,@tweak[5]
+
+	xorps	`16*2`(%rsp),$inout2
+	movups	$inout0,`16*0`($out)		# write output
+	xorps	`16*3`(%rsp),$inout3
+	movups	$inout1,`16*1`($out)
+	xorps	`16*4`(%rsp),$inout4
+	movups	$inout2,`16*2`($out)
+	xorps	`16*5`(%rsp),$inout5
+	movups	$inout3,`16*3`($out)
+	mov	$rnds_,$rounds			# restore $rounds
+	movups	$inout4,`16*4`($out)
+	movups	$inout5,`16*5`($out)
+	lea	`16*6`($out),$out
+	sub	\$16*6,$len
+	jnc	.Lxts_enc_grandloop
+
+	lea	3($rounds,$rounds),$rounds	# restore original value
+	mov	$key_,$key			# restore $key
+	mov	$rounds,$rnds_			# backup $rounds
+
+.Lxts_enc_short:
+	add	\$16*6,$len
+	jz	.Lxts_enc_done
+
+	cmp	\$0x20,$len
+	jb	.Lxts_enc_one
+	je	.Lxts_enc_two
+
+	cmp	\$0x40,$len
+	jb	.Lxts_enc_three
+	je	.Lxts_enc_four
+
+	pshufd	\$0x13,$twtmp,$twres
+	movdqa	@tweak[5],@tweak[4]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	 movdqu	($inp),$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 movdqu	16*1($inp),$inout1
+	pxor	$twres,@tweak[5]
+
+	movdqu	16*2($inp),$inout2
+	pxor	@tweak[0],$inout0
+	movdqu	16*3($inp),$inout3
+	pxor	@tweak[1],$inout1
+	movdqu	16*4($inp),$inout4
+	lea	16*5($inp),$inp
+	pxor	@tweak[2],$inout2
+	pxor	@tweak[3],$inout3
+	pxor	@tweak[4],$inout4
+
+	call	_aesni_encrypt6
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[5],@tweak[0]
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movdqu	$inout0,($out)
+	xorps	@tweak[3],$inout3
+	movdqu	$inout1,16*1($out)
+	xorps	@tweak[4],$inout4
+	movdqu	$inout2,16*2($out)
+	movdqu	$inout3,16*3($out)
+	movdqu	$inout4,16*4($out)
+	lea	16*5($out),$out
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_one:
+	movups	($inp),$inout0
+	lea	16*1($inp),$inp
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[1],@tweak[0]
+	movups	$inout0,($out)
+	lea	16*1($out),$out
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_two:
+	movups	($inp),$inout0
+	movups	16($inp),$inout1
+	lea	32($inp),$inp
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+
+	call	_aesni_encrypt3
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[2],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movups	$inout0,($out)
+	movups	$inout1,16*1($out)
+	lea	16*2($out),$out
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_three:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	lea	16*3($inp),$inp
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+
+	call	_aesni_encrypt3
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[3],@tweak[0]
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movups	$inout0,($out)
+	movups	$inout1,16*1($out)
+	movups	$inout2,16*2($out)
+	lea	16*3($out),$out
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_four:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	xorps	@tweak[0],$inout0
+	movups	16*3($inp),$inout3
+	lea	16*4($inp),$inp
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	xorps	@tweak[3],$inout3
+
+	call	_aesni_encrypt4
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[5],@tweak[0]
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movups	$inout0,($out)
+	xorps	@tweak[3],$inout3
+	movups	$inout1,16*1($out)
+	movups	$inout2,16*2($out)
+	movups	$inout3,16*3($out)
+	lea	16*4($out),$out
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_done:
+	and	\$15,$len_
+	jz	.Lxts_enc_ret
+	mov	$len_,$len
+
+.Lxts_enc_steal:
+	movzb	($inp),%eax			# borrow $rounds ...
+	movzb	-16($out),%ecx			# ... and $key
+	lea	1($inp),$inp
+	mov	%al,-16($out)
+	mov	%cl,0($out)
+	lea	1($out),$out
+	sub	\$1,$len
+	jnz	.Lxts_enc_steal
+
+	sub	$len_,$out			# rewind $out
+	mov	$key_,$key			# restore $key
+	mov	$rnds_,$rounds			# restore $rounds
+
+	movups	-16($out),$inout0
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movups	$inout0,-16($out)
+
+.Lxts_enc_ret:
+___
+$code.=<<___ if ($win64);
+	movaps	0x60(%rsp),%xmm6
+	movaps	0x70(%rsp),%xmm7
+	movaps	0x80(%rsp),%xmm8
+	movaps	0x90(%rsp),%xmm9
+	movaps	0xa0(%rsp),%xmm10
+	movaps	0xb0(%rsp),%xmm11
+	movaps	0xc0(%rsp),%xmm12
+	movaps	0xd0(%rsp),%xmm13
+	movaps	0xe0(%rsp),%xmm14
+	movaps	0xf0(%rsp),%xmm15
+___
+$code.=<<___;
+	lea	$frame_size(%rsp),%rsp
+.Lxts_enc_epilogue:
+	ret
+.size	aesni_xts_encrypt,.-aesni_xts_encrypt
+___
+
+$code.=<<___;
+.globl	aesni_xts_decrypt
+.type	aesni_xts_decrypt,\@function,6
+.align	16
+aesni_xts_decrypt:
+	lea	-$frame_size(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,0x60(%rsp)
+	movaps	%xmm7,0x70(%rsp)
+	movaps	%xmm8,0x80(%rsp)
+	movaps	%xmm9,0x90(%rsp)
+	movaps	%xmm10,0xa0(%rsp)
+	movaps	%xmm11,0xb0(%rsp)
+	movaps	%xmm12,0xc0(%rsp)
+	movaps	%xmm13,0xd0(%rsp)
+	movaps	%xmm14,0xe0(%rsp)
+	movaps	%xmm15,0xf0(%rsp)
+.Lxts_dec_body:
+___
+$code.=<<___;
+	movups	($ivp),@tweak[5]		# load clear-text tweak
+	mov	240($key2),$rounds		# key2->rounds
+	mov	240($key),$rnds_		# key1->rounds
+___
+	# generate the tweak
+	&aesni_generate1("enc",$key2,$rounds,@tweak[5]);
+$code.=<<___;
+	xor	%eax,%eax			# if ($len%16) len-=16;
+	test	\$15,$len
+	setnz	%al
+	shl	\$4,%rax
+	sub	%rax,$len
+
+	mov	$key,$key_			# backup $key
+	mov	$rnds_,$rounds			# backup $rounds
+	mov	$len,$len_			# backup $len
+	and	\$-16,$len
+
+	movdqa	.Lxts_magic(%rip),$twmask
+	pxor	$twtmp,$twtmp
+	pcmpgtd	@tweak[5],$twtmp		# broadcast upper bits
+___
+    for ($i=0;$i<4;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[$i]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	pand	$twmask,$twres			# isolate carry and residue
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	pxor	$twres,@tweak[5]
+___
+    }
+$code.=<<___;
+	sub	\$16*6,$len
+	jc	.Lxts_dec_short
+
+	shr	\$1,$rounds
+	sub	\$1,$rounds
+	mov	$rounds,$rnds_
+	jmp	.Lxts_dec_grandloop
+
+.align	16
+.Lxts_dec_grandloop:
+	pshufd	\$0x13,$twtmp,$twres
+	movdqa	@tweak[5],@tweak[4]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	movdqu	`16*0`($inp),$inout0		# load input
+	pand	$twmask,$twres			# isolate carry and residue
+	movdqu	`16*1`($inp),$inout1
+	pxor	$twres,@tweak[5]
+
+	movdqu	`16*2`($inp),$inout2
+	pxor	@tweak[0],$inout0		# input^=tweak
+	movdqu	`16*3`($inp),$inout3
+	pxor	@tweak[1],$inout1
+	movdqu	`16*4`($inp),$inout4
+	pxor	@tweak[2],$inout2
+	movdqu	`16*5`($inp),$inout5
+	lea	`16*6`($inp),$inp
+	pxor	@tweak[3],$inout3
+	$movkey		($key_),$rndkey0
+	pxor	@tweak[4],$inout4
+	pxor	@tweak[5],$inout5
+
+	# inline _aesni_decrypt6 and interleave first and last rounds
+	# with own code...
+	$movkey		16($key_),$rndkey1
+	pxor		$rndkey0,$inout0
+	pxor		$rndkey0,$inout1
+	 movdqa	@tweak[0],`16*0`(%rsp)		# put aside tweaks
+	aesdec		$rndkey1,$inout0
+	lea		32($key_),$key
+	pxor		$rndkey0,$inout2
+	 movdqa	@tweak[1],`16*1`(%rsp)
+	aesdec		$rndkey1,$inout1
+	pxor		$rndkey0,$inout3
+	 movdqa	@tweak[2],`16*2`(%rsp)
+	aesdec		$rndkey1,$inout2
+	pxor		$rndkey0,$inout4
+	 movdqa	@tweak[3],`16*3`(%rsp)
+	aesdec		$rndkey1,$inout3
+	pxor		$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	dec		$rounds
+	 movdqa	@tweak[4],`16*4`(%rsp)
+	aesdec		$rndkey1,$inout4
+	 movdqa	@tweak[5],`16*5`(%rsp)
+	aesdec		$rndkey1,$inout5
+	pxor	$twtmp,$twtmp
+	pcmpgtd	@tweak[5],$twtmp
+	jmp		.Lxts_dec_loop6_enter
+
+.align	16
+.Lxts_dec_loop6:
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	dec		$rounds
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	aesdec		$rndkey1,$inout4
+	aesdec		$rndkey1,$inout5
+.Lxts_dec_loop6_enter:
+	$movkey		16($key),$rndkey1
+	aesdec		$rndkey0,$inout0
+	aesdec		$rndkey0,$inout1
+	lea		32($key),$key
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	aesdec		$rndkey0,$inout4
+	aesdec		$rndkey0,$inout5
+	$movkey		($key),$rndkey0
+	jnz		.Lxts_dec_loop6
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesdec		$rndkey1,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesdec		$rndkey1,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcast upper bits
+	 aesdec		$rndkey1,$inout2
+	pxor	$twres,@tweak[5]
+	 aesdec		$rndkey1,$inout3
+	 aesdec		$rndkey1,$inout4
+	 aesdec		$rndkey1,$inout5
+	 $movkey	16($key),$rndkey1
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[0]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesdec		$rndkey0,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesdec		$rndkey0,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	 aesdec		$rndkey0,$inout2
+	pxor	$twres,@tweak[5]
+	 aesdec		$rndkey0,$inout3
+	 aesdec		$rndkey0,$inout4
+	 aesdec		$rndkey0,$inout5
+	 $movkey	32($key),$rndkey0
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[1]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesdec		$rndkey1,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesdec		$rndkey1,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	 aesdec		$rndkey1,$inout2
+	pxor	$twres,@tweak[5]
+	 aesdec		$rndkey1,$inout3
+	 aesdec		$rndkey1,$inout4
+	 aesdec		$rndkey1,$inout5
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[2]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 aesdeclast	$rndkey0,$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 aesdeclast	$rndkey0,$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	 aesdeclast	$rndkey0,$inout2
+	pxor	$twres,@tweak[5]
+	 aesdeclast	$rndkey0,$inout3
+	 aesdeclast	$rndkey0,$inout4
+	 aesdeclast	$rndkey0,$inout5
+
+	pshufd	\$0x13,$twtmp,$twres
+	pxor	$twtmp,$twtmp
+	movdqa	@tweak[5],@tweak[3]
+	paddq	@tweak[5],@tweak[5]		# psllq	1,$tweak
+	 xorps	`16*0`(%rsp),$inout0		# output^=tweak
+	pand	$twmask,$twres			# isolate carry and residue
+	 xorps	`16*1`(%rsp),$inout1
+	pcmpgtd	@tweak[5],$twtmp		# broadcat upper bits
+	pxor	$twres,@tweak[5]
+
+	xorps	`16*2`(%rsp),$inout2
+	movups	$inout0,`16*0`($out)		# write output
+	xorps	`16*3`(%rsp),$inout3
+	movups	$inout1,`16*1`($out)
+	xorps	`16*4`(%rsp),$inout4
+	movups	$inout2,`16*2`($out)
+	xorps	`16*5`(%rsp),$inout5
+	movups	$inout3,`16*3`($out)
+	mov	$rnds_,$rounds			# restore $rounds
+	movups	$inout4,`16*4`($out)
+	movups	$inout5,`16*5`($out)
+	lea	`16*6`($out),$out
+	sub	\$16*6,$len
+	jnc	.Lxts_dec_grandloop
+
+	lea	3($rounds,$rounds),$rounds	# restore original value
+	mov	$key_,$key			# restore $key
+	mov	$rounds,$rnds_			# backup $rounds
+
+.Lxts_dec_short:
+	add	\$16*6,$len
+	jz	.Lxts_dec_done
+
+	cmp	\$0x20,$len
+	jb	.Lxts_dec_one
+	je	.Lxts_dec_two
+
+	cmp	\$0x40,$len
+	jb	.Lxts_dec_three
+	je	.Lxts_dec_four
+
+	pshufd	\$0x13,$twtmp,$twres
+	movdqa	@tweak[5],@tweak[4]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	 movdqu	($inp),$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 movdqu	16*1($inp),$inout1
+	pxor	$twres,@tweak[5]
+
+	movdqu	16*2($inp),$inout2
+	pxor	@tweak[0],$inout0
+	movdqu	16*3($inp),$inout3
+	pxor	@tweak[1],$inout1
+	movdqu	16*4($inp),$inout4
+	lea	16*5($inp),$inp
+	pxor	@tweak[2],$inout2
+	pxor	@tweak[3],$inout3
+	pxor	@tweak[4],$inout4
+
+	call	_aesni_decrypt6
+
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movdqu	$inout0,($out)
+	xorps	@tweak[3],$inout3
+	movdqu	$inout1,16*1($out)
+	xorps	@tweak[4],$inout4
+	movdqu	$inout2,16*2($out)
+	 pxor		$twtmp,$twtmp
+	movdqu	$inout3,16*3($out)
+	 pcmpgtd	@tweak[5],$twtmp
+	movdqu	$inout4,16*4($out)
+	lea	16*5($out),$out
+	 pshufd		\$0x13,$twtmp,@tweak[1]	# $twres
+	and	\$15,$len_
+	jz	.Lxts_dec_ret
+
+	movdqa	@tweak[5],@tweak[0]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	pand	$twmask,@tweak[1]		# isolate carry and residue
+	pxor	@tweak[5],@tweak[1]
+	jmp	.Lxts_dec_done2
+
+.align	16
+.Lxts_dec_one:
+	movups	($inp),$inout0
+	lea	16*1($inp),$inp
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[1],@tweak[0]
+	movups	$inout0,($out)
+	movdqa	@tweak[2],@tweak[1]
+	lea	16*1($out),$out
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_two:
+	movups	($inp),$inout0
+	movups	16($inp),$inout1
+	lea	32($inp),$inp
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+
+	call	_aesni_decrypt3
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[2],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movdqa	@tweak[3],@tweak[1]
+	movups	$inout0,($out)
+	movups	$inout1,16*1($out)
+	lea	16*2($out),$out
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_three:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	lea	16*3($inp),$inp
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+
+	call	_aesni_decrypt3
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[3],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movdqa	@tweak[5],@tweak[1]
+	xorps	@tweak[2],$inout2
+	movups	$inout0,($out)
+	movups	$inout1,16*1($out)
+	movups	$inout2,16*2($out)
+	lea	16*3($out),$out
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_four:
+	pshufd	\$0x13,$twtmp,$twres
+	movdqa	@tweak[5],@tweak[4]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	 movups	($inp),$inout0
+	pand	$twmask,$twres			# isolate carry and residue
+	 movups	16*1($inp),$inout1
+	pxor	$twres,@tweak[5]
+
+	movups	16*2($inp),$inout2
+	xorps	@tweak[0],$inout0
+	movups	16*3($inp),$inout3
+	lea	16*4($inp),$inp
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	xorps	@tweak[3],$inout3
+
+	call	_aesni_decrypt4
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[4],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movdqa	@tweak[5],@tweak[1]
+	xorps	@tweak[2],$inout2
+	movups	$inout0,($out)
+	xorps	@tweak[3],$inout3
+	movups	$inout1,16*1($out)
+	movups	$inout2,16*2($out)
+	movups	$inout3,16*3($out)
+	lea	16*4($out),$out
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_done:
+	and	\$15,$len_
+	jz	.Lxts_dec_ret
+.Lxts_dec_done2:
+	mov	$len_,$len
+	mov	$key_,$key			# restore $key
+	mov	$rnds_,$rounds			# restore $rounds
+
+	movups	($inp),$inout0
+	xorps	@tweak[1],$inout0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[1],$inout0
+	movups	$inout0,($out)
+
+.Lxts_dec_steal:
+	movzb	16($inp),%eax			# borrow $rounds ...
+	movzb	($out),%ecx			# ... and $key
+	lea	1($inp),$inp
+	mov	%al,($out)
+	mov	%cl,16($out)
+	lea	1($out),$out
+	sub	\$1,$len
+	jnz	.Lxts_dec_steal
+
+	sub	$len_,$out			# rewind $out
+	mov	$key_,$key			# restore $key
+	mov	$rnds_,$rounds			# restore $rounds
+
+	movups	($out),$inout0
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movups	$inout0,($out)
+
+.Lxts_dec_ret:
+___
+$code.=<<___ if ($win64);
+	movaps	0x60(%rsp),%xmm6
+	movaps	0x70(%rsp),%xmm7
+	movaps	0x80(%rsp),%xmm8
+	movaps	0x90(%rsp),%xmm9
+	movaps	0xa0(%rsp),%xmm10
+	movaps	0xb0(%rsp),%xmm11
+	movaps	0xc0(%rsp),%xmm12
+	movaps	0xd0(%rsp),%xmm13
+	movaps	0xe0(%rsp),%xmm14
+	movaps	0xf0(%rsp),%xmm15
+___
+$code.=<<___;
+	lea	$frame_size(%rsp),%rsp
+.Lxts_dec_epilogue:
+	ret
+.size	aesni_xts_decrypt,.-aesni_xts_decrypt
+___
+} }}
+
+########################################################################
+# void $PREFIX_cbc_encrypt (const void *inp, void *out,
+#			    size_t length, const AES_KEY *key,
+#			    unsigned char *ivp,const int enc);
+{
+my $reserved = $win64?0x40:-0x18;	# used in decrypt
+$code.=<<___;
+.globl	${PREFIX}_cbc_encrypt
+.type	${PREFIX}_cbc_encrypt,\@function,6
+.align	16
+${PREFIX}_cbc_encrypt:
+	test	$len,$len		# check length
+	jz	.Lcbc_ret
+
+	mov	240($key),$rnds_	# key->rounds
+	mov	$key,$key_		# backup $key
+	test	%r9d,%r9d		# 6th argument
+	jz	.Lcbc_decrypt
+#--------------------------- CBC ENCRYPT ------------------------------#
+	movups	($ivp),$inout0		# load iv as initial state
+	mov	$rnds_,$rounds
+	cmp	\$16,$len
+	jb	.Lcbc_enc_tail
+	sub	\$16,$len
+	jmp	.Lcbc_enc_loop
+.align	16
+.Lcbc_enc_loop:
+	movups	($inp),$inout1		# load input
+	lea	16($inp),$inp
+	#xorps	$inout1,$inout0
+___
+	&aesni_generate1("enc",$key,$rounds,$inout0,$inout1);
+$code.=<<___;
+	mov	$rnds_,$rounds		# restore $rounds
+	mov	$key_,$key		# restore $key
+	movups	$inout0,0($out)		# store output
+	lea	16($out),$out
+	sub	\$16,$len
+	jnc	.Lcbc_enc_loop
+	add	\$16,$len
+	jnz	.Lcbc_enc_tail
+	movups	$inout0,($ivp)
+	jmp	.Lcbc_ret
+
+.Lcbc_enc_tail:
+	mov	$len,%rcx	# zaps $key
+	xchg	$inp,$out	# $inp is %rsi and $out is %rdi now
+	.long	0x9066A4F3	# rep movsb
+	mov	\$16,%ecx	# zero tail
+	sub	$len,%rcx
+	xor	%eax,%eax
+	.long	0x9066AAF3	# rep stosb
+	lea	-16(%rdi),%rdi	# rewind $out by 1 block
+	mov	$rnds_,$rounds	# restore $rounds
+	mov	%rdi,%rsi	# $inp and $out are the same
+	mov	$key_,$key	# restore $key
+	xor	$len,$len	# len=16
+	jmp	.Lcbc_enc_loop	# one more spin
+#--------------------------- CBC DECRYPT ------------------------------#
+.align	16
+.Lcbc_decrypt:
+___
+$code.=<<___ if ($win64);
+	lea	-0x58(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+.Lcbc_decrypt_body:
+___
+$code.=<<___;
+	movups	($ivp),$iv
+	mov	$rnds_,$rounds
+	cmp	\$0x70,$len
+	jbe	.Lcbc_dec_tail
+	shr	\$1,$rnds_
+	sub	\$0x70,$len
+	mov	$rnds_,$rounds
+	movaps	$iv,$reserved(%rsp)
+	jmp	.Lcbc_dec_loop8_enter
+.align	16
+.Lcbc_dec_loop8:
+	movaps	$rndkey0,$reserved(%rsp)	# save IV
+	movups	$inout7,($out)
+	lea	0x10($out),$out
+.Lcbc_dec_loop8_enter:
+	$movkey		($key),$rndkey0
+	movups	($inp),$inout0			# load input
+	movups	0x10($inp),$inout1
+	$movkey		16($key),$rndkey1
+
+	lea		32($key),$key
+	movdqu	0x20($inp),$inout2
+	xorps		$rndkey0,$inout0
+	movdqu	0x30($inp),$inout3
+	xorps		$rndkey0,$inout1
+	movdqu	0x40($inp),$inout4
+	aesdec		$rndkey1,$inout0
+	pxor		$rndkey0,$inout2
+	movdqu	0x50($inp),$inout5
+	aesdec		$rndkey1,$inout1
+	pxor		$rndkey0,$inout3
+	movdqu	0x60($inp),$inout6
+	aesdec		$rndkey1,$inout2
+	pxor		$rndkey0,$inout4
+	movdqu	0x70($inp),$inout7
+	aesdec		$rndkey1,$inout3
+	pxor		$rndkey0,$inout5
+	dec		$rounds
+	aesdec		$rndkey1,$inout4
+	pxor		$rndkey0,$inout6
+	aesdec		$rndkey1,$inout5
+	pxor		$rndkey0,$inout7
+	$movkey		($key),$rndkey0
+	aesdec		$rndkey1,$inout6
+	aesdec		$rndkey1,$inout7
+	$movkey		16($key),$rndkey1
+
+	call		.Ldec_loop8_enter
+
+	movups	($inp),$rndkey1		# re-load input
+	movups	0x10($inp),$rndkey0
+	xorps	$reserved(%rsp),$inout0	# ^= IV
+	xorps	$rndkey1,$inout1
+	movups	0x20($inp),$rndkey1
+	xorps	$rndkey0,$inout2
+	movups	0x30($inp),$rndkey0
+	xorps	$rndkey1,$inout3
+	movups	0x40($inp),$rndkey1
+	xorps	$rndkey0,$inout4
+	movups	0x50($inp),$rndkey0
+	xorps	$rndkey1,$inout5
+	movups	0x60($inp),$rndkey1
+	xorps	$rndkey0,$inout6
+	movups	0x70($inp),$rndkey0	# IV
+	xorps	$rndkey1,$inout7
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout4,0x40($out)
+	mov	$key_,$key		# restore $key
+	movups	$inout5,0x50($out)
+	lea	0x80($inp),$inp
+	movups	$inout6,0x60($out)
+	lea	0x70($out),$out
+	sub	\$0x80,$len
+	ja	.Lcbc_dec_loop8
+
+	movaps	$inout7,$inout0
+	movaps	$rndkey0,$iv
+	add	\$0x70,$len
+	jle	.Lcbc_dec_tail_collected
+	movups	$inout0,($out)
+	lea	1($rnds_,$rnds_),$rounds
+	lea	0x10($out),$out
+.Lcbc_dec_tail:
+	movups	($inp),$inout0
+	movaps	$inout0,$in0
+	cmp	\$0x10,$len
+	jbe	.Lcbc_dec_one
+
+	movups	0x10($inp),$inout1
+	movaps	$inout1,$in1
+	cmp	\$0x20,$len
+	jbe	.Lcbc_dec_two
+
+	movups	0x20($inp),$inout2
+	movaps	$inout2,$in2
+	cmp	\$0x30,$len
+	jbe	.Lcbc_dec_three
+
+	movups	0x30($inp),$inout3
+	cmp	\$0x40,$len
+	jbe	.Lcbc_dec_four
+
+	movups	0x40($inp),$inout4
+	cmp	\$0x50,$len
+	jbe	.Lcbc_dec_five
+
+	movups	0x50($inp),$inout5
+	cmp	\$0x60,$len
+	jbe	.Lcbc_dec_six
+
+	movups	0x60($inp),$inout6
+	movaps	$iv,$reserved(%rsp)	# save IV
+	call	_aesni_decrypt8
+	movups	($inp),$rndkey1
+	movups	0x10($inp),$rndkey0
+	xorps	$reserved(%rsp),$inout0	# ^= IV
+	xorps	$rndkey1,$inout1
+	movups	0x20($inp),$rndkey1
+	xorps	$rndkey0,$inout2
+	movups	0x30($inp),$rndkey0
+	xorps	$rndkey1,$inout3
+	movups	0x40($inp),$rndkey1
+	xorps	$rndkey0,$inout4
+	movups	0x50($inp),$rndkey0
+	xorps	$rndkey1,$inout5
+	movups	0x60($inp),$iv		# IV
+	xorps	$rndkey0,$inout6
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	lea	0x60($out),$out
+	movaps	$inout6,$inout0
+	sub	\$0x70,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_one:
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	$iv,$inout0
+	movaps	$in0,$iv
+	sub	\$0x10,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_two:
+	xorps	$inout2,$inout2
+	call	_aesni_decrypt3
+	xorps	$iv,$inout0
+	xorps	$in0,$inout1
+	movups	$inout0,($out)
+	movaps	$in1,$iv
+	movaps	$inout1,$inout0
+	lea	0x10($out),$out
+	sub	\$0x20,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_three:
+	call	_aesni_decrypt3
+	xorps	$iv,$inout0
+	xorps	$in0,$inout1
+	movups	$inout0,($out)
+	xorps	$in1,$inout2
+	movups	$inout1,0x10($out)
+	movaps	$in2,$iv
+	movaps	$inout2,$inout0
+	lea	0x20($out),$out
+	sub	\$0x30,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_four:
+	call	_aesni_decrypt4
+	xorps	$iv,$inout0
+	movups	0x30($inp),$iv
+	xorps	$in0,$inout1
+	movups	$inout0,($out)
+	xorps	$in1,$inout2
+	movups	$inout1,0x10($out)
+	xorps	$in2,$inout3
+	movups	$inout2,0x20($out)
+	movaps	$inout3,$inout0
+	lea	0x30($out),$out
+	sub	\$0x40,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_five:
+	xorps	$inout5,$inout5
+	call	_aesni_decrypt6
+	movups	0x10($inp),$rndkey1
+	movups	0x20($inp),$rndkey0
+	xorps	$iv,$inout0
+	xorps	$in0,$inout1
+	xorps	$rndkey1,$inout2
+	movups	0x30($inp),$rndkey1
+	xorps	$rndkey0,$inout3
+	movups	0x40($inp),$iv
+	xorps	$rndkey1,$inout4
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	lea	0x40($out),$out
+	movaps	$inout4,$inout0
+	sub	\$0x50,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_six:
+	call	_aesni_decrypt6
+	movups	0x10($inp),$rndkey1
+	movups	0x20($inp),$rndkey0
+	xorps	$iv,$inout0
+	xorps	$in0,$inout1
+	xorps	$rndkey1,$inout2
+	movups	0x30($inp),$rndkey1
+	xorps	$rndkey0,$inout3
+	movups	0x40($inp),$rndkey0
+	xorps	$rndkey1,$inout4
+	movups	0x50($inp),$iv
+	xorps	$rndkey0,$inout5
+	movups	$inout0,($out)
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	lea	0x50($out),$out
+	movaps	$inout5,$inout0
+	sub	\$0x60,$len
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_tail_collected:
+	and	\$15,$len
+	movups	$iv,($ivp)
+	jnz	.Lcbc_dec_tail_partial
+	movups	$inout0,($out)
+	jmp	.Lcbc_dec_ret
+.align	16
+.Lcbc_dec_tail_partial:
+	movaps	$inout0,$reserved(%rsp)
+	mov	\$16,%rcx
+	mov	$out,%rdi
+	sub	$len,%rcx
+	lea	$reserved(%rsp),%rsi
+	.long	0x9066A4F3	# rep movsb
+
+.Lcbc_dec_ret:
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	lea	0x58(%rsp),%rsp
+___
+$code.=<<___;
+.Lcbc_ret:
+	ret
+.size	${PREFIX}_cbc_encrypt,.-${PREFIX}_cbc_encrypt
+___
+} 
+# int $PREFIX_set_[en|de]crypt_key (const unsigned char *userKey,
+#				int bits, AES_KEY *key)
+{ my ($inp,$bits,$key) = @_4args;
+  $bits =~ s/%r/%e/;
+
+$code.=<<___;
+.globl	${PREFIX}_set_decrypt_key
+.type	${PREFIX}_set_decrypt_key,\@abi-omnipotent
+.align	16
+${PREFIX}_set_decrypt_key:
+	.byte	0x48,0x83,0xEC,0x08	# sub rsp,8
+	call	__aesni_set_encrypt_key
+	shl	\$4,$bits		# rounds-1 after _aesni_set_encrypt_key
+	test	%eax,%eax
+	jnz	.Ldec_key_ret
+	lea	16($key,$bits),$inp	# points at the end of key schedule
+
+	$movkey	($key),%xmm0		# just swap
+	$movkey	($inp),%xmm1
+	$movkey	%xmm0,($inp)
+	$movkey	%xmm1,($key)
+	lea	16($key),$key
+	lea	-16($inp),$inp
+
+.Ldec_key_inverse:
+	$movkey	($key),%xmm0		# swap and inverse
+	$movkey	($inp),%xmm1
+	aesimc	%xmm0,%xmm0
+	aesimc	%xmm1,%xmm1
+	lea	16($key),$key
+	lea	-16($inp),$inp
+	$movkey	%xmm0,16($inp)
+	$movkey	%xmm1,-16($key)
+	cmp	$key,$inp
+	ja	.Ldec_key_inverse
+
+	$movkey	($key),%xmm0		# inverse middle
+	aesimc	%xmm0,%xmm0
+	$movkey	%xmm0,($inp)
+.Ldec_key_ret:
+	add	\$8,%rsp
+	ret
+.LSEH_end_set_decrypt_key:
+.size	${PREFIX}_set_decrypt_key,.-${PREFIX}_set_decrypt_key
+___
+
+# This is based on submission by
+#
+#	Huang Ying <ying.huang@intel.com>
+#	Vinodh Gopal <vinodh.gopal@intel.com>
+#	Kahraman Akdemir
+#
+# Agressively optimized in respect to aeskeygenassist's critical path
+# and is contained in %xmm0-5 to meet Win64 ABI requirement.
+#
+$code.=<<___;
+.globl	${PREFIX}_set_encrypt_key
+.type	${PREFIX}_set_encrypt_key,\@abi-omnipotent
+.align	16
+${PREFIX}_set_encrypt_key:
+__aesni_set_encrypt_key:
+	.byte	0x48,0x83,0xEC,0x08	# sub rsp,8
+	mov	\$-1,%rax
+	test	$inp,$inp
+	jz	.Lenc_key_ret
+	test	$key,$key
+	jz	.Lenc_key_ret
+
+	movups	($inp),%xmm0		# pull first 128 bits of *userKey
+	xorps	%xmm4,%xmm4		# low dword of xmm4 is assumed 0
+	lea	16($key),%rax
+	cmp	\$256,$bits
+	je	.L14rounds
+	cmp	\$192,$bits
+	je	.L12rounds
+	cmp	\$128,$bits
+	jne	.Lbad_keybits
+
+.L10rounds:
+	mov	\$9,$bits			# 10 rounds for 128-bit key
+	$movkey	%xmm0,($key)			# round 0
+	aeskeygenassist	\$0x1,%xmm0,%xmm1	# round 1
+	call		.Lkey_expansion_128_cold
+	aeskeygenassist	\$0x2,%xmm0,%xmm1	# round 2
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x4,%xmm0,%xmm1	# round 3
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x8,%xmm0,%xmm1	# round 4
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x10,%xmm0,%xmm1	# round 5
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x20,%xmm0,%xmm1	# round 6
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x40,%xmm0,%xmm1	# round 7
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x80,%xmm0,%xmm1	# round 8
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x1b,%xmm0,%xmm1	# round 9
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x36,%xmm0,%xmm1	# round 10
+	call		.Lkey_expansion_128
+	$movkey	%xmm0,(%rax)
+	mov	$bits,80(%rax)	# 240(%rdx)
+	xor	%eax,%eax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L12rounds:
+	movq	16($inp),%xmm2			# remaining 1/3 of *userKey
+	mov	\$11,$bits			# 12 rounds for 192
+	$movkey	%xmm0,($key)			# round 0
+	aeskeygenassist	\$0x1,%xmm2,%xmm1	# round 1,2
+	call		.Lkey_expansion_192a_cold
+	aeskeygenassist	\$0x2,%xmm2,%xmm1	# round 2,3
+	call		.Lkey_expansion_192b
+	aeskeygenassist	\$0x4,%xmm2,%xmm1	# round 4,5
+	call		.Lkey_expansion_192a
+	aeskeygenassist	\$0x8,%xmm2,%xmm1	# round 5,6
+	call		.Lkey_expansion_192b
+	aeskeygenassist	\$0x10,%xmm2,%xmm1	# round 7,8
+	call		.Lkey_expansion_192a
+	aeskeygenassist	\$0x20,%xmm2,%xmm1	# round 8,9
+	call		.Lkey_expansion_192b
+	aeskeygenassist	\$0x40,%xmm2,%xmm1	# round 10,11
+	call		.Lkey_expansion_192a
+	aeskeygenassist	\$0x80,%xmm2,%xmm1	# round 11,12
+	call		.Lkey_expansion_192b
+	$movkey	%xmm0,(%rax)
+	mov	$bits,48(%rax)	# 240(%rdx)
+	xor	%rax, %rax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L14rounds:
+	movups	16($inp),%xmm2			# remaning half of *userKey
+	mov	\$13,$bits			# 14 rounds for 256
+	lea	16(%rax),%rax
+	$movkey	%xmm0,($key)			# round 0
+	$movkey	%xmm2,16($key)			# round 1
+	aeskeygenassist	\$0x1,%xmm2,%xmm1	# round 2
+	call		.Lkey_expansion_256a_cold
+	aeskeygenassist	\$0x1,%xmm0,%xmm1	# round 3
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x2,%xmm2,%xmm1	# round 4
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x2,%xmm0,%xmm1	# round 5
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x4,%xmm2,%xmm1	# round 6
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x4,%xmm0,%xmm1	# round 7
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x8,%xmm2,%xmm1	# round 8
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x8,%xmm0,%xmm1	# round 9
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x10,%xmm2,%xmm1	# round 10
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x10,%xmm0,%xmm1	# round 11
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x20,%xmm2,%xmm1	# round 12
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x20,%xmm0,%xmm1	# round 13
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x40,%xmm2,%xmm1	# round 14
+	call		.Lkey_expansion_256a
+	$movkey	%xmm0,(%rax)
+	mov	$bits,16(%rax)	# 240(%rdx)
+	xor	%rax,%rax
+	jmp	.Lenc_key_ret
+
+.align	16
+.Lbad_keybits:
+	mov	\$-2,%rax
+.Lenc_key_ret:
+	add	\$8,%rsp
+	ret
+.LSEH_end_set_encrypt_key:
+
+.align	16
+.Lkey_expansion_128:
+	$movkey	%xmm0,(%rax)
+	lea	16(%rax),%rax
+.Lkey_expansion_128_cold:
+	shufps	\$0b00010000,%xmm0,%xmm4
+	xorps	%xmm4, %xmm0
+	shufps	\$0b10001100,%xmm0,%xmm4
+	xorps	%xmm4, %xmm0
+	shufps	\$0b11111111,%xmm1,%xmm1	# critical path
+	xorps	%xmm1,%xmm0
+	ret
+
+.align 16
+.Lkey_expansion_192a:
+	$movkey	%xmm0,(%rax)
+	lea	16(%rax),%rax
+.Lkey_expansion_192a_cold:
+	movaps	%xmm2, %xmm5
+.Lkey_expansion_192b_warm:
+	shufps	\$0b00010000,%xmm0,%xmm4
+	movdqa	%xmm2,%xmm3
+	xorps	%xmm4,%xmm0
+	shufps	\$0b10001100,%xmm0,%xmm4
+	pslldq	\$4,%xmm3
+	xorps	%xmm4,%xmm0
+	pshufd	\$0b01010101,%xmm1,%xmm1	# critical path
+	pxor	%xmm3,%xmm2
+	pxor	%xmm1,%xmm0
+	pshufd	\$0b11111111,%xmm0,%xmm3
+	pxor	%xmm3,%xmm2
+	ret
+
+.align 16
+.Lkey_expansion_192b:
+	movaps	%xmm0,%xmm3
+	shufps	\$0b01000100,%xmm0,%xmm5
+	$movkey	%xmm5,(%rax)
+	shufps	\$0b01001110,%xmm2,%xmm3
+	$movkey	%xmm3,16(%rax)
+	lea	32(%rax),%rax
+	jmp	.Lkey_expansion_192b_warm
+
+.align	16
+.Lkey_expansion_256a:
+	$movkey	%xmm2,(%rax)
+	lea	16(%rax),%rax
+.Lkey_expansion_256a_cold:
+	shufps	\$0b00010000,%xmm0,%xmm4
+	xorps	%xmm4,%xmm0
+	shufps	\$0b10001100,%xmm0,%xmm4
+	xorps	%xmm4,%xmm0
+	shufps	\$0b11111111,%xmm1,%xmm1	# critical path
+	xorps	%xmm1,%xmm0
+	ret
+
+.align 16
+.Lkey_expansion_256b:
+	$movkey	%xmm0,(%rax)
+	lea	16(%rax),%rax
+
+	shufps	\$0b00010000,%xmm2,%xmm4
+	xorps	%xmm4,%xmm2
+	shufps	\$0b10001100,%xmm2,%xmm4
+	xorps	%xmm4,%xmm2
+	shufps	\$0b10101010,%xmm1,%xmm1	# critical path
+	xorps	%xmm1,%xmm2
+	ret
+.size	${PREFIX}_set_encrypt_key,.-${PREFIX}_set_encrypt_key
+.size	__aesni_set_encrypt_key,.-__aesni_set_encrypt_key
+___
+}
+
+$code.=<<___;
+.align	64
+.Lbswap_mask:
+	.byte	15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
+.Lincrement32:
+	.long	6,6,6,0
+.Lincrement64:
+	.long	1,0,0,0
+.Lxts_magic:
+	.long	0x87,0,1,0
+
+.asciz  "AES for Intel AES-NI, CRYPTOGAMS by <appro\@openssl.org>"
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+___
+$code.=<<___ if ($PREFIX eq "aesni");
+.type	ecb_se_handler,\@abi-omnipotent
+.align	16
+ecb_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	jmp	.Lcommon_seh_tail
+.size	ecb_se_handler,.-ecb_se_handler
+
+.type	ccm64_se_handler,\@abi-omnipotent
+.align	16
+ccm64_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	0(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$8,%ecx		# 4*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0x58(%rax),%rax		# adjust stack pointer
+
+	jmp	.Lcommon_seh_tail
+.size	ccm64_se_handler,.-ccm64_se_handler
+
+.type	ctr32_se_handler,\@abi-omnipotent
+.align	16
+ctr32_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lctr32_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<"prologue" label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lctr32_ret(%rip),%r10
+	cmp	%r10,%rbx
+	jae	.Lcommon_seh_tail
+
+	lea	0x20(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0xc8(%rax),%rax		# adjust stack pointer
+
+	jmp	.Lcommon_seh_tail
+.size	ctr32_se_handler,.-ctr32_se_handler
+
+.type	xts_se_handler,\@abi-omnipotent
+.align	16
+xts_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue lable
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	0x60(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0x68+160(%rax),%rax	# adjust stack pointer
+
+	jmp	.Lcommon_seh_tail
+.size	xts_se_handler,.-xts_se_handler
+___
+$code.=<<___;
+.type	cbc_se_handler,\@abi-omnipotent
+.align	16
+cbc_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	152($context),%rax	# pull context->Rsp
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lcbc_decrypt(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<"prologue" label
+	jb	.Lcommon_seh_tail
+
+	lea	.Lcbc_decrypt_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<cbc_decrypt_body
+	jb	.Lrestore_cbc_rax
+
+	lea	.Lcbc_ret(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>="epilogue" label
+	jae	.Lcommon_seh_tail
+
+	lea	0(%rax),%rsi		# top of stack
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$8,%ecx		# 4*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0x58(%rax),%rax		# adjust stack pointer
+	jmp	.Lcommon_seh_tail
+
+.Lrestore_cbc_rax:
+	mov	120($context),%rax
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	cbc_se_handler,.-cbc_se_handler
+
+.section	.pdata
+.align	4
+___
+$code.=<<___ if ($PREFIX eq "aesni");
+	.rva	.LSEH_begin_aesni_ecb_encrypt
+	.rva	.LSEH_end_aesni_ecb_encrypt
+	.rva	.LSEH_info_ecb
+
+	.rva	.LSEH_begin_aesni_ccm64_encrypt_blocks
+	.rva	.LSEH_end_aesni_ccm64_encrypt_blocks
+	.rva	.LSEH_info_ccm64_enc
+
+	.rva	.LSEH_begin_aesni_ccm64_decrypt_blocks
+	.rva	.LSEH_end_aesni_ccm64_decrypt_blocks
+	.rva	.LSEH_info_ccm64_dec
+
+	.rva	.LSEH_begin_aesni_ctr32_encrypt_blocks
+	.rva	.LSEH_end_aesni_ctr32_encrypt_blocks
+	.rva	.LSEH_info_ctr32
+
+	.rva	.LSEH_begin_aesni_xts_encrypt
+	.rva	.LSEH_end_aesni_xts_encrypt
+	.rva	.LSEH_info_xts_enc
+
+	.rva	.LSEH_begin_aesni_xts_decrypt
+	.rva	.LSEH_end_aesni_xts_decrypt
+	.rva	.LSEH_info_xts_dec
+___
+$code.=<<___;
+	.rva	.LSEH_begin_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_end_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_info_cbc
+
+	.rva	${PREFIX}_set_decrypt_key
+	.rva	.LSEH_end_set_decrypt_key
+	.rva	.LSEH_info_key
+
+	.rva	${PREFIX}_set_encrypt_key
+	.rva	.LSEH_end_set_encrypt_key
+	.rva	.LSEH_info_key
+.section	.xdata
+.align	8
+___
+$code.=<<___ if ($PREFIX eq "aesni");
+.LSEH_info_ecb:
+	.byte	9,0,0,0
+	.rva	ecb_se_handler
+.LSEH_info_ccm64_enc:
+	.byte	9,0,0,0
+	.rva	ccm64_se_handler
+	.rva	.Lccm64_enc_body,.Lccm64_enc_ret	# HandlerData[]
+.LSEH_info_ccm64_dec:
+	.byte	9,0,0,0
+	.rva	ccm64_se_handler
+	.rva	.Lccm64_dec_body,.Lccm64_dec_ret	# HandlerData[]
+.LSEH_info_ctr32:
+	.byte	9,0,0,0
+	.rva	ctr32_se_handler
+.LSEH_info_xts_enc:
+	.byte	9,0,0,0
+	.rva	xts_se_handler
+	.rva	.Lxts_enc_body,.Lxts_enc_epilogue	# HandlerData[]
+.LSEH_info_xts_dec:
+	.byte	9,0,0,0
+	.rva	xts_se_handler
+	.rva	.Lxts_dec_body,.Lxts_dec_epilogue	# HandlerData[]
+___
+$code.=<<___;
+.LSEH_info_cbc:
+	.byte	9,0,0,0
+	.rva	cbc_se_handler
+.LSEH_info_key:
+	.byte	0x01,0x04,0x01,0x00
+	.byte	0x04,0x02,0x00,0x00	# sub rsp,8
+___
+}
+
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if($dst>=8);
+    $rex|=0x01			if($src>=8);
+    push @opcode,$rex|0x40	if($rex);
+}
+
+sub aesni {
+  my $line=shift;
+  my @opcode=(0x66);
+
+    if ($line=~/(aeskeygenassist)\s+\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	rex(\@opcode,$4,$3);
+	push @opcode,0x0f,0x3a,0xdf;
+	push @opcode,0xc0|($3&7)|(($4&7)<<3);	# ModR/M
+	my $c=$2;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	return ".byte\t".join(',',@opcode);
+    }
+    elsif ($line=~/(aes[a-z]+)\s+%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	my %opcodelet = (
+		"aesimc" => 0xdb,
+		"aesenc" => 0xdc,	"aesenclast" => 0xdd,
+		"aesdec" => 0xde,	"aesdeclast" => 0xdf
+	);
+	return undef if (!defined($opcodelet{$1}));
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x38,$opcodelet{$1};
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);	# ModR/M
+	return ".byte\t".join(',',@opcode);
+    }
+    return $line;
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+$code =~ s/\b(aes.*%xmm[0-9]+).*$/aesni($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/bsaes-armv7.pl b/crypto/aes/asm/bsaes-armv7.pl
new file mode 100644
index 0000000..3da8542
--- /dev/null
+++ b/crypto/aes/asm/bsaes-armv7.pl
@@ -0,0 +1,2469 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Specific modes and adaptation for Linux kernel by Ard Biesheuvel
+# <ard.biesheuvel@linaro.org>. Permission to use under GPL terms is
+# granted.
+# ====================================================================
+
+# Bit-sliced AES for ARM NEON
+#
+# February 2012.
+#
+# This implementation is direct adaptation of bsaes-x86_64 module for
+# ARM NEON. Except that this module is endian-neutral [in sense that
+# it can be compiled for either endianness] by courtesy of vld1.8's
+# neutrality. Initial version doesn't implement interface to OpenSSL,
+# only low-level primitives and unsupported entry points, just enough
+# to collect performance results, which for Cortex-A8 core are:
+#
+# encrypt	19.5 cycles per byte processed with 128-bit key
+# decrypt	22.1 cycles per byte processed with 128-bit key
+# key conv.	440  cycles per 128-bit key/0.18 of 8x block
+#
+# Snapdragon S4 encrypts byte in 17.6 cycles and decrypts in 19.7,
+# which is [much] worse than anticipated (for further details see
+# http://www.openssl.org/~appro/Snapdragon-S4.html).
+#
+# Cortex-A15 manages in 14.2/16.1 cycles [when integer-only code
+# manages in 20.0 cycles].
+#
+# When comparing to x86_64 results keep in mind that NEON unit is
+# [mostly] single-issue and thus can't [fully] benefit from
+# instruction-level parallelism. And when comparing to aes-armv4
+# results keep in mind key schedule conversion overhead (see
+# bsaes-x86_64.pl for further details)...
+#
+#						<appro@openssl.org>
+
+# April-August 2013
+#
+# Add CBC, CTR and XTS subroutines, adapt for kernel use.
+#
+#					<ard.biesheuvel@linaro.org>
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+my ($inp,$out,$len,$key)=("r0","r1","r2","r3");
+my @XMM=map("q$_",(0..15));
+
+{
+my ($key,$rounds,$const)=("r4","r5","r6");
+
+sub Dlo()   { shift=~m|q([1]?[0-9])|?"d".($1*2):"";     }
+sub Dhi()   { shift=~m|q([1]?[0-9])|?"d".($1*2+1):"";   }
+
+sub Sbox {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b0, b1, b4, b6, b3, b7, b2, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InBasisChange	(@b);
+	&Inv_GF256	(@b[6,5,0,3,7,1,4,2],@t,@s);
+	&OutBasisChange	(@b[7,1,4,2,6,5,0,3]);
+}
+
+sub InBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b5, b0, b3, b7, b1, b4, b2] < msb 
+my @b=@_[0..7];
+$code.=<<___;
+	veor	@b[2], @b[2], @b[1]
+	veor	@b[5], @b[5], @b[6]
+	veor	@b[3], @b[3], @b[0]
+	veor	@b[6], @b[6], @b[2]
+	veor	@b[5], @b[5], @b[0]
+
+	veor	@b[6], @b[6], @b[3]
+	veor	@b[3], @b[3], @b[7]
+	veor	@b[7], @b[7], @b[5]
+	veor	@b[3], @b[3], @b[4]
+	veor	@b[4], @b[4], @b[5]
+
+	veor	@b[2], @b[2], @b[7]
+	veor	@b[3], @b[3], @b[1]
+	veor	@b[1], @b[1], @b[5]
+___
+}
+
+sub OutBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b1, b2, b4, b7, b0, b3, b5] < msb
+my @b=@_[0..7];
+$code.=<<___;
+	veor	@b[0], @b[0], @b[6]
+	veor	@b[1], @b[1], @b[4]
+	veor	@b[4], @b[4], @b[6]
+	veor	@b[2], @b[2], @b[0]
+	veor	@b[6], @b[6], @b[1]
+
+	veor	@b[1], @b[1], @b[5]
+	veor	@b[5], @b[5], @b[3]
+	veor	@b[3], @b[3], @b[7]
+	veor	@b[7], @b[7], @b[5]
+	veor	@b[2], @b[2], @b[5]
+
+	veor	@b[4], @b[4], @b[7]
+___
+}
+
+sub InvSbox {
+# input in lsb 	> [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb	> [b0, b1, b6, b4, b2, b7, b3, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InvInBasisChange	(@b);
+	&Inv_GF256		(@b[5,1,2,6,3,7,0,4],@t,@s);
+	&InvOutBasisChange	(@b[3,7,0,4,5,1,2,6]);
+}
+
+sub InvInBasisChange {		# OutBasisChange in reverse (with twist)
+my @b=@_[5,1,2,6,3,7,0,4];
+$code.=<<___
+	 veor	@b[1], @b[1], @b[7]
+	veor	@b[4], @b[4], @b[7]
+
+	veor	@b[7], @b[7], @b[5]
+	 veor	@b[1], @b[1], @b[3]
+	veor	@b[2], @b[2], @b[5]
+	veor	@b[3], @b[3], @b[7]
+
+	veor	@b[6], @b[6], @b[1]
+	veor	@b[2], @b[2], @b[0]
+	 veor	@b[5], @b[5], @b[3]
+	veor	@b[4], @b[4], @b[6]
+	veor	@b[0], @b[0], @b[6]
+	veor	@b[1], @b[1], @b[4]
+___
+}
+
+sub InvOutBasisChange {		# InBasisChange in reverse
+my @b=@_[2,5,7,3,6,1,0,4];
+$code.=<<___;
+	veor	@b[1], @b[1], @b[5]
+	veor	@b[2], @b[2], @b[7]
+
+	veor	@b[3], @b[3], @b[1]
+	veor	@b[4], @b[4], @b[5]
+	veor	@b[7], @b[7], @b[5]
+	veor	@b[3], @b[3], @b[4]
+	 veor 	@b[5], @b[5], @b[0]
+	veor	@b[3], @b[3], @b[7]
+	 veor	@b[6], @b[6], @b[2]
+	 veor	@b[2], @b[2], @b[1]
+	veor	@b[6], @b[6], @b[3]
+
+	veor	@b[3], @b[3], @b[0]
+	veor	@b[5], @b[5], @b[6]
+___
+}
+
+sub Mul_GF4 {
+#;*************************************************************
+#;* Mul_GF4: Input x0-x1,y0-y1 Output x0-x1 Temp t0 (8) *
+#;*************************************************************
+my ($x0,$x1,$y0,$y1,$t0,$t1)=@_;
+$code.=<<___;
+	veor 	$t0, $y0, $y1
+	vand	$t0, $t0, $x0
+	veor	$x0, $x0, $x1
+	vand	$t1, $x1, $y0
+	vand	$x0, $x0, $y1
+	veor	$x1, $t1, $t0
+	veor	$x0, $x0, $t1
+___
+}
+
+sub Mul_GF4_N {				# not used, see next subroutine
+# multiply and scale by N
+my ($x0,$x1,$y0,$y1,$t0)=@_;
+$code.=<<___;
+	veor	$t0, $y0, $y1
+	vand	$t0, $t0, $x0
+	veor	$x0, $x0, $x1
+	vand	$x1, $x1, $y0
+	vand	$x0, $x0, $y1
+	veor	$x1, $x1, $x0
+	veor	$x0, $x0, $t0
+___
+}
+
+sub Mul_GF4_N_GF4 {
+# interleaved Mul_GF4_N and Mul_GF4
+my ($x0,$x1,$y0,$y1,$t0,
+    $x2,$x3,$y2,$y3,$t1)=@_;
+$code.=<<___;
+	veor	$t0, $y0, $y1
+	 veor 	$t1, $y2, $y3
+	vand	$t0, $t0, $x0
+	 vand	$t1, $t1, $x2
+	veor	$x0, $x0, $x1
+	 veor	$x2, $x2, $x3
+	vand	$x1, $x1, $y0
+	 vand	$x3, $x3, $y2
+	vand	$x0, $x0, $y1
+	 vand	$x2, $x2, $y3
+	veor	$x1, $x1, $x0
+	 veor	$x2, $x2, $x3
+	veor	$x0, $x0, $t0
+	 veor	$x3, $x3, $t1
+___
+}
+sub Mul_GF16_2 {
+my @x=@_[0..7];
+my @y=@_[8..11];
+my @t=@_[12..15];
+$code.=<<___;
+	veor	@t[0], @x[0], @x[2]
+	veor	@t[1], @x[1], @x[3]
+___
+	&Mul_GF4  	(@x[0], @x[1], @y[0], @y[1], @t[2..3]);
+$code.=<<___;
+	veor	@y[0], @y[0], @y[2]
+	veor	@y[1], @y[1], @y[3]
+___
+	Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[2], @x[3], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	veor	@x[0], @x[0], @t[0]
+	veor	@x[2], @x[2], @t[0]
+	veor	@x[1], @x[1], @t[1]
+	veor	@x[3], @x[3], @t[1]
+
+	veor	@t[0], @x[4], @x[6]
+	veor	@t[1], @x[5], @x[7]
+___
+	&Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[6], @x[7], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	veor	@y[0], @y[0], @y[2]
+	veor	@y[1], @y[1], @y[3]
+___
+	&Mul_GF4  	(@x[4], @x[5], @y[0], @y[1], @t[2..3]);
+$code.=<<___;
+	veor	@x[4], @x[4], @t[0]
+	veor	@x[6], @x[6], @t[0]
+	veor	@x[5], @x[5], @t[1]
+	veor	@x[7], @x[7], @t[1]
+___
+}
+sub Inv_GF256 {
+#;********************************************************************
+#;* Inv_GF256: Input x0-x7 Output x0-x7 Temp t0-t3,s0-s3 (144)       *
+#;********************************************************************
+my @x=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+# direct optimizations from hardware
+$code.=<<___;
+	veor	@t[3], @x[4], @x[6]
+	veor	@t[2], @x[5], @x[7]
+	veor	@t[1], @x[1], @x[3]
+	veor	@s[1], @x[7], @x[6]
+	 vmov	@t[0], @t[2]
+	veor	@s[0], @x[0], @x[2]
+
+	vorr	@t[2], @t[2], @t[1]
+	veor	@s[3], @t[3], @t[0]
+	vand	@s[2], @t[3], @s[0]
+	vorr	@t[3], @t[3], @s[0]
+	veor	@s[0], @s[0], @t[1]
+	vand	@t[0], @t[0], @t[1]
+	veor	@t[1], @x[3], @x[2]
+	vand	@s[3], @s[3], @s[0]
+	vand	@s[1], @s[1], @t[1]
+	veor	@t[1], @x[4], @x[5]
+	veor	@s[0], @x[1], @x[0]
+	veor	@t[3], @t[3], @s[1]
+	veor	@t[2], @t[2], @s[1]
+	vand	@s[1], @t[1], @s[0]
+	vorr	@t[1], @t[1], @s[0]
+	veor	@t[3], @t[3], @s[3]
+	veor	@t[0], @t[0], @s[1]
+	veor	@t[2], @t[2], @s[2]
+	veor	@t[1], @t[1], @s[3]
+	veor	@t[0], @t[0], @s[2]
+	vand	@s[0], @x[7], @x[3]
+	veor	@t[1], @t[1], @s[2]
+	vand	@s[1], @x[6], @x[2]
+	vand	@s[2], @x[5], @x[1]
+	vorr	@s[3], @x[4], @x[0]
+	veor	@t[3], @t[3], @s[0]
+	veor	@t[1], @t[1], @s[2]
+	veor	@t[0], @t[0], @s[3]
+	veor	@t[2], @t[2], @s[1]
+
+	@ Inv_GF16 \t0, \t1, \t2, \t3, \s0, \s1, \s2, \s3
+
+	@ new smaller inversion
+
+	vand	@s[2], @t[3], @t[1]
+	vmov	@s[0], @t[0]
+
+	veor	@s[1], @t[2], @s[2]
+	veor	@s[3], @t[0], @s[2]
+	veor	@s[2], @t[0], @s[2]	@ @s[2]=@s[3]
+
+	vbsl	@s[1], @t[1], @t[0]
+	vbsl	@s[3], @t[3], @t[2]
+	veor	@t[3], @t[3], @t[2]
+
+	vbsl	@s[0], @s[1], @s[2]
+	vbsl	@t[0], @s[2], @s[1]
+
+	vand	@s[2], @s[0], @s[3]
+	veor	@t[1], @t[1], @t[0]
+
+	veor	@s[2], @s[2], @t[3]
+___
+# output in s3, s2, s1, t1
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \t2, \t3, \t0, \t1, \s0, \s1, \s2, \s3
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \s3, \s2, \s1, \t1, \s0, \t0, \t2, \t3
+	&Mul_GF16_2(@x,@s[3,2,1],@t[1],@s[0],@t[0,2,3]);
+
+### output msb > [x3,x2,x1,x0,x7,x6,x5,x4] < lsb
+}
+
+# AES linear components
+
+sub ShiftRows {
+my @x=@_[0..7];
+my @t=@_[8..11];
+my $mask=pop;
+$code.=<<___;
+	vldmia	$key!, {@t[0]-@t[3]}
+	veor	@t[0], @t[0], @x[0]
+	veor	@t[1], @t[1], @x[1]
+	vtbl.8	`&Dlo(@x[0])`, {@t[0]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[0])`, {@t[0]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[0]}
+	veor	@t[2], @t[2], @x[2]
+	vtbl.8	`&Dlo(@x[1])`, {@t[1]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[1])`, {@t[1]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[1]}
+	veor	@t[3], @t[3], @x[3]
+	vtbl.8	`&Dlo(@x[2])`, {@t[2]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[2])`, {@t[2]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[2]}
+	vtbl.8	`&Dlo(@x[3])`, {@t[3]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[3])`, {@t[3]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[3]}
+	veor	@t[0], @t[0], @x[4]
+	veor	@t[1], @t[1], @x[5]
+	vtbl.8	`&Dlo(@x[4])`, {@t[0]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[4])`, {@t[0]}, `&Dhi($mask)`
+	veor	@t[2], @t[2], @x[6]
+	vtbl.8	`&Dlo(@x[5])`, {@t[1]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[5])`, {@t[1]}, `&Dhi($mask)`
+	veor	@t[3], @t[3], @x[7]
+	vtbl.8	`&Dlo(@x[6])`, {@t[2]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[6])`, {@t[2]}, `&Dhi($mask)`
+	vtbl.8	`&Dlo(@x[7])`, {@t[3]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[7])`, {@t[3]}, `&Dhi($mask)`
+___
+}
+
+sub MixColumns {
+# modified to emit output in order suitable for feeding back to aesenc[last]
+my @x=@_[0..7];
+my @t=@_[8..15];
+my $inv=@_[16];	# optional
+$code.=<<___;
+	vext.8	@t[0], @x[0], @x[0], #12	@ x0 <<< 32
+	vext.8	@t[1], @x[1], @x[1], #12
+	 veor	@x[0], @x[0], @t[0]		@ x0 ^ (x0 <<< 32)
+	vext.8	@t[2], @x[2], @x[2], #12
+	 veor	@x[1], @x[1], @t[1]
+	vext.8	@t[3], @x[3], @x[3], #12
+	 veor	@x[2], @x[2], @t[2]
+	vext.8	@t[4], @x[4], @x[4], #12
+	 veor	@x[3], @x[3], @t[3]
+	vext.8	@t[5], @x[5], @x[5], #12
+	 veor	@x[4], @x[4], @t[4]
+	vext.8	@t[6], @x[6], @x[6], #12
+	 veor	@x[5], @x[5], @t[5]
+	vext.8	@t[7], @x[7], @x[7], #12
+	 veor	@x[6], @x[6], @t[6]
+
+	veor	@t[1], @t[1], @x[0]
+	 veor	@x[7], @x[7], @t[7]
+	 vext.8	@x[0], @x[0], @x[0], #8		@ (x0 ^ (x0 <<< 32)) <<< 64)
+	veor	@t[2], @t[2], @x[1]
+	veor	@t[0], @t[0], @x[7]
+	veor	@t[1], @t[1], @x[7]
+	 vext.8	@x[1], @x[1], @x[1], #8
+	veor	@t[5], @t[5], @x[4]
+	 veor	@x[0], @x[0], @t[0]
+	veor	@t[6], @t[6], @x[5]
+	 veor	@x[1], @x[1], @t[1]
+	 vext.8	@t[0], @x[4], @x[4], #8
+	veor	@t[4], @t[4], @x[3]
+	 vext.8	@t[1], @x[5], @x[5], #8
+	veor	@t[7], @t[7], @x[6]
+	 vext.8	@x[4], @x[3], @x[3], #8
+	veor	@t[3], @t[3], @x[2]
+	 vext.8	@x[5], @x[7], @x[7], #8
+	veor	@t[4], @t[4], @x[7]
+	 vext.8	@x[3], @x[6], @x[6], #8
+	veor	@t[3], @t[3], @x[7]
+	 vext.8	@x[6], @x[2], @x[2], #8
+	veor	@x[7], @t[1], @t[5]
+___
+$code.=<<___ if (!$inv);
+	veor	@x[2], @t[0], @t[4]
+	veor	@x[4], @x[4], @t[3]
+	veor	@x[5], @x[5], @t[7]
+	veor	@x[3], @x[3], @t[6]
+	 @ vmov	@x[2], @t[0]
+	veor	@x[6], @x[6], @t[2]
+	 @ vmov	@x[7], @t[1]
+___
+$code.=<<___ if ($inv);
+	veor	@t[3], @t[3], @x[4]
+	veor	@x[5], @x[5], @t[7]
+	veor	@x[2], @x[3], @t[6]
+	veor	@x[3], @t[0], @t[4]
+	veor	@x[4], @x[6], @t[2]
+	vmov	@x[6], @t[3]
+	 @ vmov	@x[7], @t[1]
+___
+}
+
+sub InvMixColumns_orig {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+$code.=<<___;
+	@ multiplication by 0x0e
+	vext.8	@t[7], @x[7], @x[7], #12
+	vmov	@t[2], @x[2]
+	veor	@x[2], @x[2], @x[5]		@ 2 5
+	veor	@x[7], @x[7], @x[5]		@ 7 5
+	vext.8	@t[0], @x[0], @x[0], #12
+	vmov	@t[5], @x[5]
+	veor	@x[5], @x[5], @x[0]		@ 5 0		[1]
+	veor	@x[0], @x[0], @x[1]		@ 0 1
+	vext.8	@t[1], @x[1], @x[1], #12
+	veor	@x[1], @x[1], @x[2]		@ 1 25
+	veor	@x[0], @x[0], @x[6]		@ 01 6		[2]
+	vext.8	@t[3], @x[3], @x[3], #12
+	veor	@x[1], @x[1], @x[3]		@ 125 3		[4]
+	veor	@x[2], @x[2], @x[0]		@ 25 016	[3]
+	veor	@x[3], @x[3], @x[7]		@ 3 75
+	veor	@x[7], @x[7], @x[6]		@ 75 6		[0]
+	vext.8	@t[6], @x[6], @x[6], #12
+	vmov	@t[4], @x[4]
+	veor	@x[6], @x[6], @x[4]		@ 6 4
+	veor	@x[4], @x[4], @x[3]		@ 4 375		[6]
+	veor	@x[3], @x[3], @x[7]		@ 375 756=36
+	veor	@x[6], @x[6], @t[5]		@ 64 5		[7]
+	veor	@x[3], @x[3], @t[2]		@ 36 2
+	vext.8	@t[5], @t[5], @t[5], #12
+	veor	@x[3], @x[3], @t[4]		@ 362 4		[5]
+___
+					my @y = @x[7,5,0,2,1,3,4,6];
+$code.=<<___;
+	@ multiplication by 0x0b
+	veor	@y[1], @y[1], @y[0]
+	veor	@y[0], @y[0], @t[0]
+	vext.8	@t[2], @t[2], @t[2], #12
+	veor	@y[1], @y[1], @t[1]
+	veor	@y[0], @y[0], @t[5]
+	vext.8	@t[4], @t[4], @t[4], #12
+	veor	@y[1], @y[1], @t[6]
+	veor	@y[0], @y[0], @t[7]
+	veor	@t[7], @t[7], @t[6]		@ clobber t[7]
+
+	veor	@y[3], @y[3], @t[0]
+	 veor	@y[1], @y[1], @y[0]
+	vext.8	@t[0], @t[0], @t[0], #12
+	veor	@y[2], @y[2], @t[1]
+	veor	@y[4], @y[4], @t[1]
+	vext.8	@t[1], @t[1], @t[1], #12
+	veor	@y[2], @y[2], @t[2]
+	veor	@y[3], @y[3], @t[2]
+	veor	@y[5], @y[5], @t[2]
+	veor	@y[2], @y[2], @t[7]
+	vext.8	@t[2], @t[2], @t[2], #12
+	veor	@y[3], @y[3], @t[3]
+	veor	@y[6], @y[6], @t[3]
+	veor	@y[4], @y[4], @t[3]
+	veor	@y[7], @y[7], @t[4]
+	vext.8	@t[3], @t[3], @t[3], #12
+	veor	@y[5], @y[5], @t[4]
+	veor	@y[7], @y[7], @t[7]
+	veor	@t[7], @t[7], @t[5]		@ clobber t[7] even more
+	veor	@y[3], @y[3], @t[5]
+	veor	@y[4], @y[4], @t[4]
+
+	veor	@y[5], @y[5], @t[7]
+	vext.8	@t[4], @t[4], @t[4], #12
+	veor	@y[6], @y[6], @t[7]
+	veor	@y[4], @y[4], @t[7]
+
+	veor	@t[7], @t[7], @t[5]
+	vext.8	@t[5], @t[5], @t[5], #12
+
+	@ multiplication by 0x0d
+	veor	@y[4], @y[4], @y[7]
+	 veor	@t[7], @t[7], @t[6]		@ restore t[7]
+	veor	@y[7], @y[7], @t[4]
+	vext.8	@t[6], @t[6], @t[6], #12
+	veor	@y[2], @y[2], @t[0]
+	veor	@y[7], @y[7], @t[5]
+	vext.8	@t[7], @t[7], @t[7], #12
+	veor	@y[2], @y[2], @t[2]
+
+	veor	@y[3], @y[3], @y[1]
+	veor	@y[1], @y[1], @t[1]
+	veor	@y[0], @y[0], @t[0]
+	veor	@y[3], @y[3], @t[0]
+	veor	@y[1], @y[1], @t[5]
+	veor	@y[0], @y[0], @t[5]
+	vext.8	@t[0], @t[0], @t[0], #12
+	veor	@y[1], @y[1], @t[7]
+	veor	@y[0], @y[0], @t[6]
+	veor	@y[3], @y[3], @y[1]
+	veor	@y[4], @y[4], @t[1]
+	vext.8	@t[1], @t[1], @t[1], #12
+
+	veor	@y[7], @y[7], @t[7]
+	veor	@y[4], @y[4], @t[2]
+	veor	@y[5], @y[5], @t[2]
+	veor	@y[2], @y[2], @t[6]
+	veor	@t[6], @t[6], @t[3]		@ clobber t[6]
+	vext.8	@t[2], @t[2], @t[2], #12
+	veor	@y[4], @y[4], @y[7]
+	veor	@y[3], @y[3], @t[6]
+
+	veor	@y[6], @y[6], @t[6]
+	veor	@y[5], @y[5], @t[5]
+	vext.8	@t[5], @t[5], @t[5], #12
+	veor	@y[6], @y[6], @t[4]
+	vext.8	@t[4], @t[4], @t[4], #12
+	veor	@y[5], @y[5], @t[6]
+	veor	@y[6], @y[6], @t[7]
+	vext.8	@t[7], @t[7], @t[7], #12
+	veor	@t[6], @t[6], @t[3]		@ restore t[6]
+	vext.8	@t[3], @t[3], @t[3], #12
+
+	@ multiplication by 0x09
+	veor	@y[4], @y[4], @y[1]
+	veor	@t[1], @t[1], @y[1]		@ t[1]=y[1]
+	veor	@t[0], @t[0], @t[5]		@ clobber t[0]
+	vext.8	@t[6], @t[6], @t[6], #12
+	veor	@t[1], @t[1], @t[5]
+	veor	@y[3], @y[3], @t[0]
+	veor	@t[0], @t[0], @y[0]		@ t[0]=y[0]
+	veor	@t[1], @t[1], @t[6]
+	veor	@t[6], @t[6], @t[7]		@ clobber t[6]
+	veor	@y[4], @y[4], @t[1]
+	veor	@y[7], @y[7], @t[4]
+	veor	@y[6], @y[6], @t[3]
+	veor	@y[5], @y[5], @t[2]
+	veor	@t[4], @t[4], @y[4]		@ t[4]=y[4]
+	veor	@t[3], @t[3], @y[3]		@ t[3]=y[3]
+	veor	@t[5], @t[5], @y[5]		@ t[5]=y[5]
+	veor	@t[2], @t[2], @y[2]		@ t[2]=y[2]
+	veor	@t[3], @t[3], @t[7]
+	veor	@XMM[5], @t[5], @t[6]
+	veor	@XMM[6], @t[6], @y[6]		@ t[6]=y[6]
+	veor	@XMM[2], @t[2], @t[6]
+	veor	@XMM[7], @t[7], @y[7]		@ t[7]=y[7]
+
+	vmov	@XMM[0], @t[0]
+	vmov	@XMM[1], @t[1]
+	@ vmov	@XMM[2], @t[2]
+	vmov	@XMM[3], @t[3]
+	vmov	@XMM[4], @t[4]
+	@ vmov	@XMM[5], @t[5]
+	@ vmov	@XMM[6], @t[6]
+	@ vmov	@XMM[7], @t[7]
+___
+}
+
+sub InvMixColumns {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+# Thanks to Jussi Kivilinna for providing pointer to
+#
+# | 0e 0b 0d 09 |   | 02 03 01 01 |   | 05 00 04 00 |
+# | 09 0e 0b 0d | = | 01 02 03 01 | x | 00 05 00 04 |
+# | 0d 09 0e 0b |   | 01 01 02 03 |   | 04 00 05 00 |
+# | 0b 0d 09 0e |   | 03 01 01 02 |   | 00 04 00 05 |
+
+$code.=<<___;
+	@ multiplication by 0x05-0x00-0x04-0x00
+	vext.8	@t[0], @x[0], @x[0], #8
+	vext.8	@t[6], @x[6], @x[6], #8
+	vext.8	@t[7], @x[7], @x[7], #8
+	veor	@t[0], @t[0], @x[0]
+	vext.8	@t[1], @x[1], @x[1], #8
+	veor	@t[6], @t[6], @x[6]
+	vext.8	@t[2], @x[2], @x[2], #8
+	veor	@t[7], @t[7], @x[7]
+	vext.8	@t[3], @x[3], @x[3], #8
+	veor	@t[1], @t[1], @x[1]
+	vext.8	@t[4], @x[4], @x[4], #8
+	veor	@t[2], @t[2], @x[2]
+	vext.8	@t[5], @x[5], @x[5], #8
+	veor	@t[3], @t[3], @x[3]
+	veor	@t[4], @t[4], @x[4]
+	veor	@t[5], @t[5], @x[5]
+
+	 veor	@x[0], @x[0], @t[6]
+	 veor	@x[1], @x[1], @t[6]
+	 veor	@x[2], @x[2], @t[0]
+	 veor	@x[4], @x[4], @t[2]
+	 veor	@x[3], @x[3], @t[1]
+	 veor	@x[1], @x[1], @t[7]
+	 veor	@x[2], @x[2], @t[7]
+	 veor	@x[4], @x[4], @t[6]
+	 veor	@x[5], @x[5], @t[3]
+	 veor	@x[3], @x[3], @t[6]
+	 veor	@x[6], @x[6], @t[4]
+	 veor	@x[4], @x[4], @t[7]
+	 veor	@x[5], @x[5], @t[7]
+	 veor	@x[7], @x[7], @t[5]
+___
+	&MixColumns	(@x,@t,1);	# flipped 2<->3 and 4<->6
+}
+
+sub swapmove {
+my ($a,$b,$n,$mask,$t)=@_;
+$code.=<<___;
+	vshr.u64	$t, $b, #$n
+	veor		$t, $t, $a
+	vand		$t, $t, $mask
+	veor		$a, $a, $t
+	vshl.u64	$t, $t, #$n
+	veor		$b, $b, $t
+___
+}
+sub swapmove2x {
+my ($a0,$b0,$a1,$b1,$n,$mask,$t0,$t1)=@_;
+$code.=<<___;
+	vshr.u64	$t0, $b0, #$n
+	 vshr.u64	$t1, $b1, #$n
+	veor		$t0, $t0, $a0
+	 veor		$t1, $t1, $a1
+	vand		$t0, $t0, $mask
+	 vand		$t1, $t1, $mask
+	veor		$a0, $a0, $t0
+	vshl.u64	$t0, $t0, #$n
+	 veor		$a1, $a1, $t1
+	 vshl.u64	$t1, $t1, #$n
+	veor		$b0, $b0, $t0
+	 veor		$b1, $b1, $t1
+___
+}
+
+sub bitslice {
+my @x=reverse(@_[0..7]);
+my ($t0,$t1,$t2,$t3)=@_[8..11];
+$code.=<<___;
+	vmov.i8	$t0,#0x55			@ compose .LBS0
+	vmov.i8	$t1,#0x33			@ compose .LBS1
+___
+	&swapmove2x(@x[0,1,2,3],1,$t0,$t2,$t3);
+	&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+$code.=<<___;
+	vmov.i8	$t0,#0x0f			@ compose .LBS2
+___
+	&swapmove2x(@x[0,2,1,3],2,$t1,$t2,$t3);
+	&swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+
+	&swapmove2x(@x[0,4,1,5],4,$t0,$t2,$t3);
+	&swapmove2x(@x[2,6,3,7],4,$t0,$t2,$t3);
+}
+
+$code.=<<___;
+#if defined(__arm__)
+#ifndef __KERNEL__
+# include "arm_arch.h"
+
+# define VFP_ABI_PUSH	vstmdb	sp!,{d8-d15}
+# define VFP_ABI_POP	vldmia	sp!,{d8-d15}
+# define VFP_ABI_FRAME	0x40
+#else
+# define VFP_ABI_PUSH
+# define VFP_ABI_POP
+# define VFP_ABI_FRAME	0
+# define BSAES_ASM_EXTENDED_KEY
+# define XTS_CHAIN_TWEAK
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+#endif
+
+#ifdef __thumb__
+# define adrl adr
+#endif
+
+#if __ARM_ARCH__>=7
+.text
+.syntax	unified 	@ ARMv7-capable assembler is expected to handle this
+#ifdef __thumb2__
+.thumb
+#else
+.code   32
+#endif
+
+.fpu	neon
+
+.type	_bsaes_decrypt8,%function
+.align	4
+_bsaes_decrypt8:
+	adr	$const,_bsaes_decrypt8
+	vldmia	$key!, {@XMM[9]}		@ round 0 key
+	add	$const,$const,#.LM0ISR-_bsaes_decrypt8
+
+	vldmia	$const!, {@XMM[8]}		@ .LM0ISR
+	veor	@XMM[10], @XMM[0], @XMM[9]	@ xor with round0 key
+	veor	@XMM[11], @XMM[1], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[0])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[0])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	veor	@XMM[12], @XMM[2], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[1])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[1])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+	veor	@XMM[13], @XMM[3], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[2])`, {@XMM[12]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[2])`, {@XMM[12]}, `&Dhi(@XMM[8])`
+	veor	@XMM[14], @XMM[4], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[3])`, {@XMM[13]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[3])`, {@XMM[13]}, `&Dhi(@XMM[8])`
+	veor	@XMM[15], @XMM[5], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[4])`, {@XMM[14]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[4])`, {@XMM[14]}, `&Dhi(@XMM[8])`
+	veor	@XMM[10], @XMM[6], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[5])`, {@XMM[15]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[5])`, {@XMM[15]}, `&Dhi(@XMM[8])`
+	veor	@XMM[11], @XMM[7], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[6])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[6])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	 vtbl.8	`&Dlo(@XMM[7])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[7])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	sub	$rounds,$rounds,#1
+	b	.Ldec_sbox
+.align	4
+.Ldec_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8..12]);
+$code.=".Ldec_sbox:\n";
+	&InvSbox	(@XMM[0..7, 8..15]);
+$code.=<<___;
+	subs	$rounds,$rounds,#1
+	bcc	.Ldec_done
+___
+	&InvMixColumns	(@XMM[0,1,6,4,2,7,3,5, 8..15]);
+$code.=<<___;
+	vldmia	$const, {@XMM[12]}		@ .LISR
+	ite	eq				@ Thumb2 thing, sanity check in ARM
+	addeq	$const,$const,#0x10
+	bne	.Ldec_loop
+	vldmia	$const, {@XMM[12]}		@ .LISRM0
+	b	.Ldec_loop
+.align	4
+.Ldec_done:
+___
+	&bitslice	(@XMM[0,1,6,4,2,7,3,5, 8..11]);
+$code.=<<___;
+	vldmia	$key, {@XMM[8]}			@ last round key
+	veor	@XMM[6], @XMM[6], @XMM[8]
+	veor	@XMM[4], @XMM[4], @XMM[8]
+	veor	@XMM[2], @XMM[2], @XMM[8]
+	veor	@XMM[7], @XMM[7], @XMM[8]
+	veor	@XMM[3], @XMM[3], @XMM[8]
+	veor	@XMM[5], @XMM[5], @XMM[8]
+	veor	@XMM[0], @XMM[0], @XMM[8]
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	bx	lr
+.size	_bsaes_decrypt8,.-_bsaes_decrypt8
+
+.type	_bsaes_const,%object
+.align	6
+_bsaes_const:
+.LM0ISR:	@ InvShiftRows constants
+	.quad	0x0a0e0206070b0f03, 0x0004080c0d010509
+.LISR:
+	.quad	0x0504070602010003, 0x0f0e0d0c080b0a09
+.LISRM0:
+	.quad	0x01040b0e0205080f, 0x0306090c00070a0d
+.LM0SR:		@ ShiftRows constants
+	.quad	0x0a0e02060f03070b, 0x0004080c05090d01
+.LSR:
+	.quad	0x0504070600030201, 0x0f0e0d0c0a09080b
+.LSRM0:
+	.quad	0x0304090e00050a0f, 0x01060b0c0207080d
+.LM0:
+	.quad	0x02060a0e03070b0f, 0x0004080c0105090d
+.LREVM0SR:
+	.quad	0x090d01050c000408, 0x03070b0f060a0e02
+.asciz	"Bit-sliced AES for NEON, CRYPTOGAMS by <appro\@openssl.org>"
+.align	6
+.size	_bsaes_const,.-_bsaes_const
+
+.type	_bsaes_encrypt8,%function
+.align	4
+_bsaes_encrypt8:
+	adr	$const,_bsaes_encrypt8
+	vldmia	$key!, {@XMM[9]}		@ round 0 key
+	sub	$const,$const,#_bsaes_encrypt8-.LM0SR
+
+	vldmia	$const!, {@XMM[8]}		@ .LM0SR
+_bsaes_encrypt8_alt:
+	veor	@XMM[10], @XMM[0], @XMM[9]	@ xor with round0 key
+	veor	@XMM[11], @XMM[1], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[0])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[0])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	veor	@XMM[12], @XMM[2], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[1])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[1])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+	veor	@XMM[13], @XMM[3], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[2])`, {@XMM[12]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[2])`, {@XMM[12]}, `&Dhi(@XMM[8])`
+	veor	@XMM[14], @XMM[4], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[3])`, {@XMM[13]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[3])`, {@XMM[13]}, `&Dhi(@XMM[8])`
+	veor	@XMM[15], @XMM[5], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[4])`, {@XMM[14]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[4])`, {@XMM[14]}, `&Dhi(@XMM[8])`
+	veor	@XMM[10], @XMM[6], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[5])`, {@XMM[15]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[5])`, {@XMM[15]}, `&Dhi(@XMM[8])`
+	veor	@XMM[11], @XMM[7], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[6])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[6])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	 vtbl.8	`&Dlo(@XMM[7])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[7])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+_bsaes_encrypt8_bitslice:
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	sub	$rounds,$rounds,#1
+	b	.Lenc_sbox
+.align	4
+.Lenc_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8..12]);
+$code.=".Lenc_sbox:\n";
+	&Sbox		(@XMM[0..7, 8..15]);
+$code.=<<___;
+	subs	$rounds,$rounds,#1
+	bcc	.Lenc_done
+___
+	&MixColumns	(@XMM[0,1,4,6,3,7,2,5, 8..15]);
+$code.=<<___;
+	vldmia	$const, {@XMM[12]}		@ .LSR
+	ite	eq				@ Thumb2 thing, samity check in ARM
+	addeq	$const,$const,#0x10
+	bne	.Lenc_loop
+	vldmia	$const, {@XMM[12]}		@ .LSRM0
+	b	.Lenc_loop
+.align	4
+.Lenc_done:
+___
+	# output in lsb > [t0, t1, t4, t6, t3, t7, t2, t5] < msb
+	&bitslice	(@XMM[0,1,4,6,3,7,2,5, 8..11]);
+$code.=<<___;
+	vldmia	$key, {@XMM[8]}			@ last round key
+	veor	@XMM[4], @XMM[4], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[8]
+	veor	@XMM[3], @XMM[3], @XMM[8]
+	veor	@XMM[7], @XMM[7], @XMM[8]
+	veor	@XMM[2], @XMM[2], @XMM[8]
+	veor	@XMM[5], @XMM[5], @XMM[8]
+	veor	@XMM[0], @XMM[0], @XMM[8]
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	bx	lr
+.size	_bsaes_encrypt8,.-_bsaes_encrypt8
+___
+}
+{
+my ($out,$inp,$rounds,$const)=("r12","r4","r5","r6");
+
+sub bitslice_key {
+my @x=reverse(@_[0..7]);
+my ($bs0,$bs1,$bs2,$t2,$t3)=@_[8..12];
+
+	&swapmove	(@x[0,1],1,$bs0,$t2,$t3);
+$code.=<<___;
+	@ &swapmove(@x[2,3],1,$t0,$t2,$t3);
+	vmov	@x[2], @x[0]
+	vmov	@x[3], @x[1]
+___
+	#&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+
+	&swapmove2x	(@x[0,2,1,3],2,$bs1,$t2,$t3);
+$code.=<<___;
+	@ &swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+	vmov	@x[4], @x[0]
+	vmov	@x[6], @x[2]
+	vmov	@x[5], @x[1]
+	vmov	@x[7], @x[3]
+___
+	&swapmove2x	(@x[0,4,1,5],4,$bs2,$t2,$t3);
+	&swapmove2x	(@x[2,6,3,7],4,$bs2,$t2,$t3);
+}
+
+$code.=<<___;
+.type	_bsaes_key_convert,%function
+.align	4
+_bsaes_key_convert:
+	adr	$const,_bsaes_key_convert
+	vld1.8	{@XMM[7]},  [$inp]!		@ load round 0 key
+	sub	$const,$const,#_bsaes_key_convert-.LM0
+	vld1.8	{@XMM[15]}, [$inp]!		@ load round 1 key
+
+	vmov.i8	@XMM[8],  #0x01			@ bit masks
+	vmov.i8	@XMM[9],  #0x02
+	vmov.i8	@XMM[10], #0x04
+	vmov.i8	@XMM[11], #0x08
+	vmov.i8	@XMM[12], #0x10
+	vmov.i8	@XMM[13], #0x20
+	vldmia	$const, {@XMM[14]}		@ .LM0
+
+#ifdef __ARMEL__
+	vrev32.8	@XMM[7],  @XMM[7]
+	vrev32.8	@XMM[15], @XMM[15]
+#endif
+	sub	$rounds,$rounds,#1
+	vstmia	$out!, {@XMM[7]}		@ save round 0 key
+	b	.Lkey_loop
+
+.align	4
+.Lkey_loop:
+	vtbl.8	`&Dlo(@XMM[7])`,{@XMM[15]},`&Dlo(@XMM[14])`
+	vtbl.8	`&Dhi(@XMM[7])`,{@XMM[15]},`&Dhi(@XMM[14])`
+	vmov.i8	@XMM[6],  #0x40
+	vmov.i8	@XMM[15], #0x80
+
+	vtst.8	@XMM[0], @XMM[7], @XMM[8]
+	vtst.8	@XMM[1], @XMM[7], @XMM[9]
+	vtst.8	@XMM[2], @XMM[7], @XMM[10]
+	vtst.8	@XMM[3], @XMM[7], @XMM[11]
+	vtst.8	@XMM[4], @XMM[7], @XMM[12]
+	vtst.8	@XMM[5], @XMM[7], @XMM[13]
+	vtst.8	@XMM[6], @XMM[7], @XMM[6]
+	vtst.8	@XMM[7], @XMM[7], @XMM[15]
+	vld1.8	{@XMM[15]}, [$inp]!		@ load next round key
+	vmvn	@XMM[0], @XMM[0]		@ "pnot"
+	vmvn	@XMM[1], @XMM[1]
+	vmvn	@XMM[5], @XMM[5]
+	vmvn	@XMM[6], @XMM[6]
+#ifdef __ARMEL__
+	vrev32.8	@XMM[15], @XMM[15]
+#endif
+	subs	$rounds,$rounds,#1
+	vstmia	$out!,{@XMM[0]-@XMM[7]}		@ write bit-sliced round key
+	bne	.Lkey_loop
+
+	vmov.i8	@XMM[7],#0x63			@ compose .L63
+	@ don't save last round key
+	bx	lr
+.size	_bsaes_key_convert,.-_bsaes_key_convert
+___
+}
+
+if (0) {		# following four functions are unsupported interface
+			# used for benchmarking...
+$code.=<<___;
+.globl	bsaes_enc_key_convert
+.type	bsaes_enc_key_convert,%function
+.align	4
+bsaes_enc_key_convert:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+
+	ldr	r5,[$inp,#240]			@ pass rounds
+	mov	r4,$inp				@ pass key
+	mov	r12,$out			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7],@XMM[7],@XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_enc_key_convert,.-bsaes_enc_key_convert
+
+.globl	bsaes_encrypt_128
+.type	bsaes_encrypt_128,%function
+.align	4
+bsaes_encrypt_128:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+.Lenc128_loop:
+	vld1.8	{@XMM[0]-@XMM[1]}, [$inp]!	@ load input
+	vld1.8	{@XMM[2]-@XMM[3]}, [$inp]!
+	mov	r4,$key				@ pass the key
+	vld1.8	{@XMM[4]-@XMM[5]}, [$inp]!
+	mov	r5,#10				@ pass rounds
+	vld1.8	{@XMM[6]-@XMM[7]}, [$inp]!
+
+	bl	_bsaes_encrypt8
+
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	subs	$len,$len,#0x80
+	vst1.8	{@XMM[5]}, [$out]!
+	bhi	.Lenc128_loop
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_encrypt_128,.-bsaes_encrypt_128
+
+.globl	bsaes_dec_key_convert
+.type	bsaes_dec_key_convert,%function
+.align	4
+bsaes_dec_key_convert:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+
+	ldr	r5,[$inp,#240]			@ pass rounds
+	mov	r4,$inp				@ pass key
+	mov	r12,$out			@ pass key schedule
+	bl	_bsaes_key_convert
+	vldmia	$out, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	$out, {@XMM[7]}
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_dec_key_convert,.-bsaes_dec_key_convert
+
+.globl	bsaes_decrypt_128
+.type	bsaes_decrypt_128,%function
+.align	4
+bsaes_decrypt_128:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+.Ldec128_loop:
+	vld1.8	{@XMM[0]-@XMM[1]}, [$inp]!	@ load input
+	vld1.8	{@XMM[2]-@XMM[3]}, [$inp]!
+	mov	r4,$key				@ pass the key
+	vld1.8	{@XMM[4]-@XMM[5]}, [$inp]!
+	mov	r5,#10				@ pass rounds
+	vld1.8	{@XMM[6]-@XMM[7]}, [$inp]!
+
+	bl	_bsaes_decrypt8
+
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	subs	$len,$len,#0x80
+	vst1.8	{@XMM[5]}, [$out]!
+	bhi	.Ldec128_loop
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_decrypt_128,.-bsaes_decrypt_128
+___
+}
+{
+my ($inp,$out,$len,$key, $ivp,$fp,$rounds)=map("r$_",(0..3,8..10));
+my ($keysched)=("sp");
+
+$code.=<<___;
+.extern AES_cbc_encrypt
+.extern AES_decrypt
+
+.global	bsaes_cbc_encrypt
+.type	bsaes_cbc_encrypt,%function
+.align	5
+bsaes_cbc_encrypt:
+#ifndef	__KERNEL__
+	cmp	$len, #128
+#ifndef	__thumb__
+	blo	AES_cbc_encrypt
+#else
+	bhs	1f
+	b	AES_cbc_encrypt
+1:
+#endif
+#endif
+
+	@ it is up to the caller to make sure we are called with enc == 0
+
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}
+	VFP_ABI_PUSH
+	ldr	$ivp, [ip]			@ IV is 1st arg on the stack
+	mov	$len, $len, lsr#4		@ len in 16 byte blocks
+	sub	sp, #0x10			@ scratch space to carry over the IV
+	mov	$fp, sp				@ save sp
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	add	r12, #`128-32`			@ sifze of bit-slices key schedule
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12				@ sp is $keysched
+	bl	_bsaes_key_convert
+	vldmia	$keysched, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	$keysched, {@XMM[7]}
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	@ populate the key schedule
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	add	r4, $key, #248
+	vldmia	r4, {@XMM[6]}
+	vstmia	r12, {@XMM[15]}			@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	r4, {@XMM[7]}
+
+.align	2
+0:
+#endif
+
+	vld1.8	{@XMM[15]}, [$ivp]		@ load IV
+	b	.Lcbc_dec_loop
+
+.align	4
+.Lcbc_dec_loop:
+	subs	$len, $len, #0x8
+	bmi	.Lcbc_dec_loop_finish
+
+	vld1.8	{@XMM[0]-@XMM[1]}, [$inp]!	@ load input
+	vld1.8	{@XMM[2]-@XMM[3]}, [$inp]!
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	mov	r4, $keysched			@ pass the key
+#else
+	add	r4, $key, #248
+#endif
+	vld1.8	{@XMM[4]-@XMM[5]}, [$inp]!
+	mov	r5, $rounds
+	vld1.8	{@XMM[6]-@XMM[7]}, [$inp]
+	sub	$inp, $inp, #0x60
+	vstmia	$fp, {@XMM[15]}			@ put aside IV
+
+	bl	_bsaes_decrypt8
+
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[12]-@XMM[13]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vld1.8	{@XMM[14]-@XMM[15]}, [$inp]!
+	veor	@XMM[7], @XMM[7], @XMM[12]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor	@XMM[3], @XMM[3], @XMM[13]
+	vst1.8	{@XMM[6]}, [$out]!
+	veor	@XMM[5], @XMM[5], @XMM[14]
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	vst1.8	{@XMM[5]}, [$out]!
+
+	b	.Lcbc_dec_loop
+
+.Lcbc_dec_loop_finish:
+	adds	$len, $len, #8
+	beq	.Lcbc_dec_done
+
+	vld1.8	{@XMM[0]}, [$inp]!		@ load input
+	cmp	$len, #2
+	blo	.Lcbc_dec_one
+	vld1.8	{@XMM[1]}, [$inp]!
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	mov	r4, $keysched			@ pass the key
+#else
+	add	r4, $key, #248
+#endif
+	mov	r5, $rounds
+	vstmia	$fp, {@XMM[15]}			@ put aside IV
+	beq	.Lcbc_dec_two
+	vld1.8	{@XMM[2]}, [$inp]!
+	cmp	$len, #4
+	blo	.Lcbc_dec_three
+	vld1.8	{@XMM[3]}, [$inp]!
+	beq	.Lcbc_dec_four
+	vld1.8	{@XMM[4]}, [$inp]!
+	cmp	$len, #6
+	blo	.Lcbc_dec_five
+	vld1.8	{@XMM[5]}, [$inp]!
+	beq	.Lcbc_dec_six
+	vld1.8	{@XMM[6]}, [$inp]!
+	sub	$inp, $inp, #0x70
+
+	bl	_bsaes_decrypt8
+
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[12]-@XMM[13]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[7], @XMM[7], @XMM[12]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor	@XMM[3], @XMM[3], @XMM[13]
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_six:
+	sub	$inp, $inp, #0x60
+	bl	_bsaes_decrypt8
+	vldmia	$fp,{@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[12]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[7], @XMM[7], @XMM[12]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_five:
+	sub	$inp, $inp, #0x50
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_four:
+	sub	$inp, $inp, #0x40
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_three:
+	sub	$inp, $inp, #0x30
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_two:
+	sub	$inp, $inp, #0x20
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]}, [$inp]!		@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[15]}, [$inp]!		@ reload input
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_one:
+	sub	$inp, $inp, #0x10
+	mov	$rounds, $out			@ save original out pointer
+	mov	$out, $fp			@ use the iv scratch space as out buffer
+	mov	r2, $key
+	vmov	@XMM[4],@XMM[15]		@ just in case ensure that IV
+	vmov	@XMM[5],@XMM[0]			@ and input are preserved
+	bl	AES_decrypt
+	vld1.8	{@XMM[0]}, [$fp,:64]		@ load result
+	veor	@XMM[0], @XMM[0], @XMM[4]	@ ^= IV
+	vmov	@XMM[15], @XMM[5]		@ @XMM[5] holds input
+	vst1.8	{@XMM[0]}, [$rounds]		@ write output
+
+.Lcbc_dec_done:
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+.Lcbc_dec_bzero:				@ wipe key schedule [if any]
+	vstmia		$keysched!, {q0-q1}
+	cmp		$keysched, $fp
+	bne		.Lcbc_dec_bzero
+#endif
+
+	mov	sp, $fp
+	add	sp, #0x10			@ add sp,$fp,#0x10 is no good for thumb
+	vst1.8	{@XMM[15]}, [$ivp]		@ return IV
+	VFP_ABI_POP
+	ldmia	sp!, {r4-r10, pc}
+.size	bsaes_cbc_encrypt,.-bsaes_cbc_encrypt
+___
+}
+{
+my ($inp,$out,$len,$key, $ctr,$fp,$rounds)=(map("r$_",(0..3,8..10)));
+my $const = "r6";	# shared with _bsaes_encrypt8_alt
+my $keysched = "sp";
+
+$code.=<<___;
+.extern	AES_encrypt
+.global	bsaes_ctr32_encrypt_blocks
+.type	bsaes_ctr32_encrypt_blocks,%function
+.align	5
+bsaes_ctr32_encrypt_blocks:
+	cmp	$len, #8			@ use plain AES for
+	blo	.Lctr_enc_short			@ small sizes
+
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}
+	VFP_ABI_PUSH
+	ldr	$ctr, [ip]			@ ctr is 1st arg on the stack
+	sub	sp, sp, #0x10			@ scratch space to carry over the ctr
+	mov	$fp, sp				@ save sp
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	add	r12, #`128-32`			@ size of bit-sliced key schedule
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12				@ sp is $keysched
+	bl	_bsaes_key_convert
+	veor	@XMM[7],@XMM[7],@XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+
+	vld1.8	{@XMM[0]}, [$ctr]		@ load counter
+	add	$ctr, $const, #.LREVM0SR-.LM0	@ borrow $ctr
+	vldmia	$keysched, {@XMM[4]}		@ load round0 key
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	@ populate the key schedule
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7],@XMM[7],@XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+
+.align	2
+0:	add	r12, $key, #248
+	vld1.8	{@XMM[0]}, [$ctr]		@ load counter
+	adrl	$ctr, .LREVM0SR			@ borrow $ctr
+	vldmia	r12, {@XMM[4]}			@ load round0 key
+	sub	sp, #0x10			@ place for adjusted round0 key
+#endif
+
+	vmov.i32	@XMM[8],#1		@ compose 1<<96
+	veor		@XMM[9],@XMM[9],@XMM[9]
+	vrev32.8	@XMM[0],@XMM[0]
+	vext.8		@XMM[8],@XMM[9],@XMM[8],#4
+	vrev32.8	@XMM[4],@XMM[4]
+	vadd.u32	@XMM[9],@XMM[8],@XMM[8]	@ compose 2<<96
+	vstmia	$keysched, {@XMM[4]}		@ save adjusted round0 key
+	b	.Lctr_enc_loop
+
+.align	4
+.Lctr_enc_loop:
+	vadd.u32	@XMM[10], @XMM[8], @XMM[9]	@ compose 3<<96
+	vadd.u32	@XMM[1], @XMM[0], @XMM[8]	@ +1
+	vadd.u32	@XMM[2], @XMM[0], @XMM[9]	@ +2
+	vadd.u32	@XMM[3], @XMM[0], @XMM[10]	@ +3
+	vadd.u32	@XMM[4], @XMM[1], @XMM[10]
+	vadd.u32	@XMM[5], @XMM[2], @XMM[10]
+	vadd.u32	@XMM[6], @XMM[3], @XMM[10]
+	vadd.u32	@XMM[7], @XMM[4], @XMM[10]
+	vadd.u32	@XMM[10], @XMM[5], @XMM[10]	@ next counter
+
+	@ Borrow prologue from _bsaes_encrypt8 to use the opportunity
+	@ to flip byte order in 32-bit counter
+
+	vldmia		$keysched, {@XMM[9]}		@ load round0 key
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, $keysched, #0x10		@ pass next round key
+#else
+	add		r4, $key, #`248+16`
+#endif
+	vldmia		$ctr, {@XMM[8]}			@ .LREVM0SR
+	mov		r5, $rounds			@ pass rounds
+	vstmia		$fp, {@XMM[10]}			@ save next counter
+	sub		$const, $ctr, #.LREVM0SR-.LSR	@ pass constants
+
+	bl		_bsaes_encrypt8_alt
+
+	subs		$len, $len, #8
+	blo		.Lctr_enc_loop_done
+
+	vld1.8		{@XMM[8]-@XMM[9]}, [$inp]!	@ load input
+	vld1.8		{@XMM[10]-@XMM[11]}, [$inp]!
+	veor		@XMM[0], @XMM[8]
+	veor		@XMM[1], @XMM[9]
+	vld1.8		{@XMM[12]-@XMM[13]}, [$inp]!
+	veor		@XMM[4], @XMM[10]
+	veor		@XMM[6], @XMM[11]
+	vld1.8		{@XMM[14]-@XMM[15]}, [$inp]!
+	veor		@XMM[3], @XMM[12]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor		@XMM[7], @XMM[13]
+	veor		@XMM[2], @XMM[14]
+	vst1.8		{@XMM[4]}, [$out]!
+	veor		@XMM[5], @XMM[15]
+	vst1.8		{@XMM[6]}, [$out]!
+	vmov.i32	@XMM[8], #1			@ compose 1<<96
+	vst1.8		{@XMM[3]}, [$out]!
+	veor		@XMM[9], @XMM[9], @XMM[9]
+	vst1.8		{@XMM[7]}, [$out]!
+	vext.8		@XMM[8], @XMM[9], @XMM[8], #4
+	vst1.8		{@XMM[2]}, [$out]!
+	vadd.u32	@XMM[9],@XMM[8],@XMM[8]		@ compose 2<<96
+	vst1.8		{@XMM[5]}, [$out]!
+	vldmia		$fp, {@XMM[0]}			@ load counter
+
+	bne		.Lctr_enc_loop
+	b		.Lctr_enc_done
+
+.align	4
+.Lctr_enc_loop_done:
+	add		$len, $len, #8
+	vld1.8		{@XMM[8]}, [$inp]!	@ load input
+	veor		@XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [$out]!	@ write output
+	cmp		$len, #2
+	blo		.Lctr_enc_done
+	vld1.8		{@XMM[9]}, [$inp]!
+	veor		@XMM[1], @XMM[9]
+	vst1.8		{@XMM[1]}, [$out]!
+	beq		.Lctr_enc_done
+	vld1.8		{@XMM[10]}, [$inp]!
+	veor		@XMM[4], @XMM[10]
+	vst1.8		{@XMM[4]}, [$out]!
+	cmp		$len, #4
+	blo		.Lctr_enc_done
+	vld1.8		{@XMM[11]}, [$inp]!
+	veor		@XMM[6], @XMM[11]
+	vst1.8		{@XMM[6]}, [$out]!
+	beq		.Lctr_enc_done
+	vld1.8		{@XMM[12]}, [$inp]!
+	veor		@XMM[3], @XMM[12]
+	vst1.8		{@XMM[3]}, [$out]!
+	cmp		$len, #6
+	blo		.Lctr_enc_done
+	vld1.8		{@XMM[13]}, [$inp]!
+	veor		@XMM[7], @XMM[13]
+	vst1.8		{@XMM[7]}, [$out]!
+	beq		.Lctr_enc_done
+	vld1.8		{@XMM[14]}, [$inp]
+	veor		@XMM[2], @XMM[14]
+	vst1.8		{@XMM[2]}, [$out]!
+
+.Lctr_enc_done:
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+#ifndef	BSAES_ASM_EXTENDED_KEY
+.Lctr_enc_bzero:			@ wipe key schedule [if any]
+	vstmia		$keysched!, {q0-q1}
+	cmp		$keysched, $fp
+	bne		.Lctr_enc_bzero
+#else
+	vstmia		$keysched, {q0-q1}
+#endif
+
+	mov	sp, $fp
+	add	sp, #0x10		@ add sp,$fp,#0x10 is no good for thumb
+	VFP_ABI_POP
+	ldmia	sp!, {r4-r10, pc}	@ return
+
+.align	4
+.Lctr_enc_short:
+	ldr	ip, [sp]		@ ctr pointer is passed on stack
+	stmdb	sp!, {r4-r8, lr}
+
+	mov	r4, $inp		@ copy arguments
+	mov	r5, $out
+	mov	r6, $len
+	mov	r7, $key
+	ldr	r8, [ip, #12]		@ load counter LSW
+	vld1.8	{@XMM[1]}, [ip]		@ load whole counter value
+#ifdef __ARMEL__
+	rev	r8, r8
+#endif
+	sub	sp, sp, #0x10
+	vst1.8	{@XMM[1]}, [sp,:64]	@ copy counter value
+	sub	sp, sp, #0x10
+
+.Lctr_enc_short_loop:
+	add	r0, sp, #0x10		@ input counter value
+	mov	r1, sp			@ output on the stack
+	mov	r2, r7			@ key
+
+	bl	AES_encrypt
+
+	vld1.8	{@XMM[0]}, [r4]!	@ load input
+	vld1.8	{@XMM[1]}, [sp,:64]	@ load encrypted counter
+	add	r8, r8, #1
+#ifdef __ARMEL__
+	rev	r0, r8
+	str	r0, [sp, #0x1c]		@ next counter value
+#else
+	str	r8, [sp, #0x1c]		@ next counter value
+#endif
+	veor	@XMM[0],@XMM[0],@XMM[1]
+	vst1.8	{@XMM[0]}, [r5]!	@ store output
+	subs	r6, r6, #1
+	bne	.Lctr_enc_short_loop
+
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+	vstmia		sp!, {q0-q1}
+
+	ldmia	sp!, {r4-r8, pc}
+.size	bsaes_ctr32_encrypt_blocks,.-bsaes_ctr32_encrypt_blocks
+___
+}
+{
+######################################################################
+# void bsaes_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2,
+#	const unsigned char iv[16]);
+#
+my ($inp,$out,$len,$key,$rounds,$magic,$fp)=(map("r$_",(7..10,1..3)));
+my $const="r6";		# returned by _bsaes_key_convert
+my $twmask=@XMM[5];
+my @T=@XMM[6..7];
+
+$code.=<<___;
+.globl	bsaes_xts_encrypt
+.type	bsaes_xts_encrypt,%function
+.align	4
+bsaes_xts_encrypt:
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}		@ 0x20
+	VFP_ABI_PUSH
+	mov	r6, sp				@ future $fp
+
+	mov	$inp, r0
+	mov	$out, r1
+	mov	$len, r2
+	mov	$key, r3
+
+	sub	r0, sp, #0x10			@ 0x10
+	bic	r0, #0xf			@ align at 16 bytes
+	mov	sp, r0
+
+#ifdef	XTS_CHAIN_TWEAK
+	ldr	r0, [ip]			@ pointer to input tweak
+#else
+	@ generate initial tweak
+	ldr	r0, [ip, #4]			@ iv[]
+	mov	r1, sp
+	ldr	r2, [ip, #0]			@ key2
+	bl	AES_encrypt
+	mov	r0,sp				@ pointer to initial tweak
+#endif
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+	mov	$fp, r6
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	@ add	r12, #`128-32`			@ size of bit-sliced key schedule
+	sub	r12, #`32+16`			@ place for tweak[9]
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12
+	add	r12, #0x90			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7], @XMM[7], @XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7], @XMM[7], @XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}
+
+.align	2
+0:	sub	sp, #0x90			@ place for tweak[9]
+#endif
+
+	vld1.8	{@XMM[8]}, [r0]			@ initial tweak
+	adr	$magic, .Lxts_magic
+
+	subs	$len, #0x80
+	blo	.Lxts_enc_short
+	b	.Lxts_enc_loop
+
+.align	4
+.Lxts_enc_loop:
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	vadd.u64	@XMM[8], @XMM[15], @XMM[15]
+	vst1.64		{@XMM[15]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	veor		@XMM[8], @XMM[8], @T[0]
+	vst1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]-@XMM[7]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	veor		@XMM[7], @XMM[7], @XMM[15]
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	vld1.64		{@XMM[14]-@XMM[15]}, [r0,:128]!
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[2], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	veor		@XMM[13], @XMM[5], @XMM[15]
+	vst1.8		{@XMM[12]-@XMM[13]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	subs		$len, #0x80
+	bpl		.Lxts_enc_loop
+
+.Lxts_enc_short:
+	adds		$len, #0x70
+	bmi		.Lxts_enc_done
+
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+	subs		$len, #0x10
+	bmi		.Lxts_enc_`$i-9`
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	sub		$len, #0x10
+	vst1.64		{@XMM[15]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	vld1.64		{@XMM[14]}, [r0,:128]!
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[2], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	vst1.8		{@XMM[12]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_6:
+	vst1.64		{@XMM[14]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[4], @XMM[4], @XMM[12]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[5], @XMM[5], @XMM[13]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+
+@ put this in range for both ARM and Thumb mode adr instructions
+.align	5
+.Lxts_magic:
+	.quad	1, 0x87
+
+.align	5
+.Lxts_enc_5:
+	vst1.64		{@XMM[13]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[3], @XMM[3], @XMM[11]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[4], @XMM[4], @XMM[12]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	vst1.8		{@XMM[10]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_4:
+	vst1.64		{@XMM[12]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[2], @XMM[2], @XMM[10]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[3], @XMM[3], @XMM[11]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_3:
+	vst1.64		{@XMM[11]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[1], @XMM[1], @XMM[9]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[2], @XMM[2], @XMM[10]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	vld1.64		{@XMM[10]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	vst1.8		{@XMM[8]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_2:
+	vst1.64		{@XMM[10]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[0], @XMM[0], @XMM[8]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[1], @XMM[1], @XMM[9]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_1:
+	mov		r0, sp
+	veor		@XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp				@ preserve fp
+
+	bl		AES_encrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [$out]!
+	mov		$fp, r4
+
+	vmov		@XMM[8], @XMM[9]		@ next round tweak
+
+.Lxts_enc_done:
+#ifndef	XTS_CHAIN_TWEAK
+	adds		$len, #0x10
+	beq		.Lxts_enc_ret
+	sub		r6, $out, #0x10
+
+.Lxts_enc_steal:
+	ldrb		r0, [$inp], #1
+	ldrb		r1, [$out, #-0x10]
+	strb		r0, [$out, #-0x10]
+	strb		r1, [$out], #1
+
+	subs		$len, #1
+	bhi		.Lxts_enc_steal
+
+	vld1.8		{@XMM[0]}, [r6]
+	mov		r0, sp
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp			@ preserve fp
+
+	bl		AES_encrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [r6]
+	mov		$fp, r4
+#endif
+
+.Lxts_enc_ret:
+	bic		r0, $fp, #0xf
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+#ifdef	XTS_CHAIN_TWEAK
+	ldr		r1, [$fp, #0x20+VFP_ABI_FRAME]	@ chain tweak
+#endif
+.Lxts_enc_bzero:				@ wipe key schedule [if any]
+	vstmia		sp!, {q0-q1}
+	cmp		sp, r0
+	bne		.Lxts_enc_bzero
+
+	mov		sp, $fp
+#ifdef	XTS_CHAIN_TWEAK
+	vst1.8		{@XMM[8]}, [r1]
+#endif
+	VFP_ABI_POP
+	ldmia		sp!, {r4-r10, pc}	@ return
+
+.size	bsaes_xts_encrypt,.-bsaes_xts_encrypt
+
+.globl	bsaes_xts_decrypt
+.type	bsaes_xts_decrypt,%function
+.align	4
+bsaes_xts_decrypt:
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}		@ 0x20
+	VFP_ABI_PUSH
+	mov	r6, sp				@ future $fp
+
+	mov	$inp, r0
+	mov	$out, r1
+	mov	$len, r2
+	mov	$key, r3
+
+	sub	r0, sp, #0x10			@ 0x10
+	bic	r0, #0xf			@ align at 16 bytes
+	mov	sp, r0
+
+#ifdef	XTS_CHAIN_TWEAK
+	ldr	r0, [ip]			@ pointer to input tweak
+#else
+	@ generate initial tweak
+	ldr	r0, [ip, #4]			@ iv[]
+	mov	r1, sp
+	ldr	r2, [ip, #0]			@ key2
+	bl	AES_encrypt
+	mov	r0, sp				@ pointer to initial tweak
+#endif
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+	mov	$fp, r6
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	@ add	r12, #`128-32`			@ size of bit-sliced key schedule
+	sub	r12, #`32+16`			@ place for tweak[9]
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12
+	add	r12, #0x90			@ pass key schedule
+	bl	_bsaes_key_convert
+	add	r4, sp, #0x90
+	vldmia	r4, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	r4, {@XMM[7]}
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	add	r4, $key, #248
+	vldmia	r4, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	r4, {@XMM[7]}
+
+.align	2
+0:	sub	sp, #0x90			@ place for tweak[9]
+#endif
+	vld1.8	{@XMM[8]}, [r0]			@ initial tweak
+	adr	$magic, .Lxts_magic
+
+	tst	$len, #0xf			@ if not multiple of 16
+	it	ne				@ Thumb2 thing, sanity check in ARM
+	subne	$len, #0x10			@ subtract another 16 bytes
+	subs	$len, #0x80
+
+	blo	.Lxts_dec_short
+	b	.Lxts_dec_loop
+
+.align	4
+.Lxts_dec_loop:
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	vadd.u64	@XMM[8], @XMM[15], @XMM[15]
+	vst1.64		{@XMM[15]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	veor		@XMM[8], @XMM[8], @T[0]
+	vst1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]-@XMM[7]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	veor		@XMM[7], @XMM[7], @XMM[15]
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	vld1.64		{@XMM[14]-@XMM[15]}, [r0,:128]!
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[3], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	veor		@XMM[13], @XMM[5], @XMM[15]
+	vst1.8		{@XMM[12]-@XMM[13]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	subs		$len, #0x80
+	bpl		.Lxts_dec_loop
+
+.Lxts_dec_short:
+	adds		$len, #0x70
+	bmi		.Lxts_dec_done
+
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+	subs		$len, #0x10
+	bmi		.Lxts_dec_`$i-9`
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	sub		$len, #0x10
+	vst1.64		{@XMM[15]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	vld1.64		{@XMM[14]}, [r0,:128]!
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[3], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	vst1.8		{@XMM[12]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_6:
+	vst1.64		{@XMM[14]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[4], @XMM[4], @XMM[12]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[5], @XMM[5], @XMM[13]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_5:
+	vst1.64		{@XMM[13]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[3], @XMM[3], @XMM[11]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[4], @XMM[4], @XMM[12]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	vst1.8		{@XMM[10]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_4:
+	vst1.64		{@XMM[12]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[2], @XMM[2], @XMM[10]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[3], @XMM[3], @XMM[11]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_3:
+	vst1.64		{@XMM[11]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[1], @XMM[1], @XMM[9]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[2], @XMM[2], @XMM[10]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	vld1.64		{@XMM[10]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	vst1.8		{@XMM[8]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_2:
+	vst1.64		{@XMM[10]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[0], @XMM[0], @XMM[8]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[1], @XMM[1], @XMM[9]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_1:
+	mov		r0, sp
+	veor		@XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp				@ preserve fp
+	mov		r5, $magic			@ preserve magic
+
+	bl		AES_decrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [$out]!
+	mov		$fp, r4
+	mov		$magic, r5
+
+	vmov		@XMM[8], @XMM[9]		@ next round tweak
+
+.Lxts_dec_done:
+#ifndef	XTS_CHAIN_TWEAK
+	adds		$len, #0x10
+	beq		.Lxts_dec_ret
+
+	@ calculate one round of extra tweak for the stolen ciphertext
+	vldmia		$magic, {$twmask}
+	vshr.s64	@XMM[6], @XMM[8], #63
+	vand		@XMM[6], @XMM[6], $twmask
+	vadd.u64	@XMM[9], @XMM[8], @XMM[8]
+	vswp		`&Dhi("@XMM[6]")`,`&Dlo("@XMM[6]")`
+	veor		@XMM[9], @XMM[9], @XMM[6]
+
+	@ perform the final decryption with the last tweak value
+	vld1.8		{@XMM[0]}, [$inp]!
+	mov		r0, sp
+	veor		@XMM[0], @XMM[0], @XMM[9]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp			@ preserve fp
+
+	bl		AES_decrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[9]
+	vst1.8		{@XMM[0]}, [$out]
+
+	mov		r6, $out
+.Lxts_dec_steal:
+	ldrb		r1, [$out]
+	ldrb		r0, [$inp], #1
+	strb		r1, [$out, #0x10]
+	strb		r0, [$out], #1
+
+	subs		$len, #1
+	bhi		.Lxts_dec_steal
+
+	vld1.8		{@XMM[0]}, [r6]
+	mov		r0, sp
+	veor		@XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+
+	bl		AES_decrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [r6]
+	mov		$fp, r4
+#endif
+
+.Lxts_dec_ret:
+	bic		r0, $fp, #0xf
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+#ifdef	XTS_CHAIN_TWEAK
+	ldr		r1, [$fp, #0x20+VFP_ABI_FRAME]	@ chain tweak
+#endif
+.Lxts_dec_bzero:				@ wipe key schedule [if any]
+	vstmia		sp!, {q0-q1}
+	cmp		sp, r0
+	bne		.Lxts_dec_bzero
+
+	mov		sp, $fp
+#ifdef	XTS_CHAIN_TWEAK
+	vst1.8		{@XMM[8]}, [r1]
+#endif
+	VFP_ABI_POP
+	ldmia		sp!, {r4-r10, pc}	@ return
+
+.size	bsaes_xts_decrypt,.-bsaes_xts_decrypt
+___
+}
+$code.=<<___;
+#endif
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+open SELF,$0;
+while(<SELF>) {
+	next if (/^#!/);
+        last if (!s/^#/@/ and !/^$/);
+        print;
+}
+close SELF;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/bsaes-x86_64.pl b/crypto/aes/asm/bsaes-x86_64.pl
new file mode 100644
index 0000000..41b90f0
--- /dev/null
+++ b/crypto/aes/asm/bsaes-x86_64.pl
@@ -0,0 +1,3108 @@
+#!/usr/bin/env perl
+
+###################################################################
+### AES-128 [originally in CTR mode]				###
+### bitsliced implementation for Intel Core 2 processors	###
+### requires support of SSE extensions up to SSSE3		###
+### Author: Emilia Käsper and Peter Schwabe			###
+### Date: 2009-03-19						###
+### Public domain						###
+###								###
+### See http://homes.esat.kuleuven.be/~ekasper/#software for	###
+### further information.					###
+###################################################################
+#
+# September 2011.
+#
+# Started as transliteration to "perlasm" the original code has
+# undergone following changes:
+#
+# - code was made position-independent;
+# - rounds were folded into a loop resulting in >5x size reduction
+#   from 12.5KB to 2.2KB;
+# - above was possibile thanks to mixcolumns() modification that
+#   allowed to feed its output back to aesenc[last], this was
+#   achieved at cost of two additional inter-registers moves;
+# - some instruction reordering and interleaving;
+# - this module doesn't implement key setup subroutine, instead it
+#   relies on conversion of "conventional" key schedule as returned
+#   by AES_set_encrypt_key (see discussion below);
+# - first and last round keys are treated differently, which allowed
+#   to skip one shiftrows(), reduce bit-sliced key schedule and
+#   speed-up conversion by 22%;
+# - support for 192- and 256-bit keys was added;
+#
+# Resulting performance in CPU cycles spent to encrypt one byte out
+# of 4096-byte buffer with 128-bit key is:
+#
+#		Emilia's	this(*)		difference
+#
+# Core 2    	9.30		8.69		+7%
+# Nehalem(**) 	7.63		6.98		+9%
+# Atom	    	17.1		17.4		-2%(***)
+#
+# (*)	Comparison is not completely fair, because "this" is ECB,
+#	i.e. no extra processing such as counter values calculation
+#	and xor-ing input as in Emilia's CTR implementation is
+#	performed. However, the CTR calculations stand for not more
+#	than 1% of total time, so comparison is *rather* fair.
+#
+# (**)	Results were collected on Westmere, which is considered to
+#	be equivalent to Nehalem for this code.
+#
+# (***)	Slowdown on Atom is rather strange per se, because original
+#	implementation has a number of 9+-bytes instructions, which
+#	are bad for Atom front-end, and which I eliminated completely.
+#	In attempt to address deterioration sbox() was tested in FP
+#	SIMD "domain" (movaps instead of movdqa, xorps instead of
+#	pxor, etc.). While it resulted in nominal 4% improvement on
+#	Atom, it hurted Westmere by more than 2x factor.
+#
+# As for key schedule conversion subroutine. Interface to OpenSSL
+# relies on per-invocation on-the-fly conversion. This naturally
+# has impact on performance, especially for short inputs. Conversion
+# time in CPU cycles and its ratio to CPU cycles spent in 8x block
+# function is:
+#
+# 		conversion	conversion/8x block
+# Core 2	240		0.22
+# Nehalem	180		0.20
+# Atom		430		0.19
+#
+# The ratio values mean that 128-byte blocks will be processed
+# 16-18% slower, 256-byte blocks - 9-10%, 384-byte blocks - 6-7%,
+# etc. Then keep in mind that input sizes not divisible by 128 are
+# *effectively* slower, especially shortest ones, e.g. consecutive
+# 144-byte blocks are processed 44% slower than one would expect,
+# 272 - 29%, 400 - 22%, etc. Yet, despite all these "shortcomings"
+# it's still faster than ["hyper-threading-safe" code path in]
+# aes-x86_64.pl on all lengths above 64 bytes...
+#
+# October 2011.
+#
+# Add decryption procedure. Performance in CPU cycles spent to decrypt
+# one byte out of 4096-byte buffer with 128-bit key is:
+#
+# Core 2	9.83
+# Nehalem	7.74
+# Atom		19.0
+#
+# November 2011.
+#
+# Add bsaes_xts_[en|de]crypt. Less-than-80-bytes-block performance is
+# suboptimal, but XTS is meant to be used with larger blocks...
+#
+#						<appro@openssl.org>
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+my ($inp,$out,$len,$key,$ivp)=("%rdi","%rsi","%rdx","%rcx");
+my @XMM=map("%xmm$_",(15,0..14));	# best on Atom, +10% over (0..15)
+my $ecb=0;	# suppress unreferenced ECB subroutines, spare some space...
+
+{
+my ($key,$rounds,$const)=("%rax","%r10d","%r11");
+
+sub Sbox {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b0, b1, b4, b6, b3, b7, b2, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InBasisChange	(@b);
+	&Inv_GF256	(@b[6,5,0,3,7,1,4,2],@t,@s);
+	&OutBasisChange	(@b[7,1,4,2,6,5,0,3]);
+}
+
+sub InBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b5, b0, b3, b7, b1, b4, b2] < msb 
+my @b=@_[0..7];
+$code.=<<___;
+	pxor	@b[6], @b[5]
+	pxor	@b[1], @b[2]
+	pxor	@b[0], @b[3]
+	pxor	@b[2], @b[6]
+	pxor 	@b[0], @b[5]
+
+	pxor	@b[3], @b[6]
+	pxor	@b[7], @b[3]
+	pxor	@b[5], @b[7]
+	pxor	@b[4], @b[3]
+	pxor	@b[5], @b[4]
+	pxor	@b[1], @b[3]
+
+	pxor	@b[7], @b[2]
+	pxor	@b[5], @b[1]
+___
+}
+
+sub OutBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b1, b2, b4, b7, b0, b3, b5] < msb
+my @b=@_[0..7];
+$code.=<<___;
+	pxor	@b[6], @b[0]
+	pxor	@b[4], @b[1]
+	pxor	@b[0], @b[2]
+	pxor	@b[6], @b[4]
+	pxor	@b[1], @b[6]
+
+	pxor	@b[5], @b[1]
+	pxor	@b[3], @b[5]
+	pxor	@b[7], @b[3]
+	pxor	@b[5], @b[7]
+	pxor	@b[5], @b[2]
+
+	pxor	@b[7], @b[4]
+___
+}
+
+sub InvSbox {
+# input in lsb 	> [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb	> [b0, b1, b6, b4, b2, b7, b3, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InvInBasisChange	(@b);
+	&Inv_GF256		(@b[5,1,2,6,3,7,0,4],@t,@s);
+	&InvOutBasisChange	(@b[3,7,0,4,5,1,2,6]);
+}
+
+sub InvInBasisChange {		# OutBasisChange in reverse
+my @b=@_[5,1,2,6,3,7,0,4];
+$code.=<<___
+	pxor	@b[7], @b[4]
+
+	pxor	@b[5], @b[7]
+	pxor	@b[5], @b[2]
+	pxor	@b[7], @b[3]
+	pxor	@b[3], @b[5]
+	pxor	@b[5], @b[1]
+
+	pxor	@b[1], @b[6]
+	pxor	@b[0], @b[2]
+	pxor	@b[6], @b[4]
+	pxor	@b[6], @b[0]
+	pxor	@b[4], @b[1]
+___
+}
+
+sub InvOutBasisChange {		# InBasisChange in reverse
+my @b=@_[2,5,7,3,6,1,0,4];
+$code.=<<___;
+	pxor	@b[5], @b[1]
+	pxor	@b[7], @b[2]
+
+	pxor	@b[1], @b[3]
+	pxor	@b[5], @b[4]
+	pxor	@b[5], @b[7]
+	pxor	@b[4], @b[3]
+	 pxor 	@b[0], @b[5]
+	pxor	@b[7], @b[3]
+	 pxor	@b[2], @b[6]
+	 pxor	@b[1], @b[2]
+	pxor	@b[3], @b[6]
+
+	pxor	@b[0], @b[3]
+	pxor	@b[6], @b[5]
+___
+}
+
+sub Mul_GF4 {
+#;*************************************************************
+#;* Mul_GF4: Input x0-x1,y0-y1 Output x0-x1 Temp t0 (8) *
+#;*************************************************************
+my ($x0,$x1,$y0,$y1,$t0)=@_;
+$code.=<<___;
+	movdqa	$y0, $t0
+	pxor 	$y1, $t0
+	pand	$x0, $t0
+	pxor	$x1, $x0
+	pand	$y0, $x1
+	pand	$y1, $x0
+	pxor	$x1, $x0
+	pxor	$t0, $x1
+___
+}
+
+sub Mul_GF4_N {				# not used, see next subroutine
+# multiply and scale by N
+my ($x0,$x1,$y0,$y1,$t0)=@_;
+$code.=<<___;
+	movdqa	$y0, $t0
+	pxor	$y1, $t0
+	pand	$x0, $t0
+	pxor	$x1, $x0
+	pand	$y0, $x1
+	pand	$y1, $x0
+	pxor	$x0, $x1
+	pxor	$t0, $x0
+___
+}
+
+sub Mul_GF4_N_GF4 {
+# interleaved Mul_GF4_N and Mul_GF4
+my ($x0,$x1,$y0,$y1,$t0,
+    $x2,$x3,$y2,$y3,$t1)=@_;
+$code.=<<___;
+	movdqa	$y0, $t0
+	 movdqa	$y2, $t1
+	pxor	$y1, $t0
+	 pxor 	$y3, $t1
+	pand	$x0, $t0
+	 pand	$x2, $t1
+	pxor	$x1, $x0
+	 pxor	$x3, $x2
+	pand	$y0, $x1
+	 pand	$y2, $x3
+	pand	$y1, $x0
+	 pand	$y3, $x2
+	pxor	$x0, $x1
+	 pxor	$x3, $x2
+	pxor	$t0, $x0
+	 pxor	$t1, $x3
+___
+}
+sub Mul_GF16_2 {
+my @x=@_[0..7];
+my @y=@_[8..11];
+my @t=@_[12..15];
+$code.=<<___;
+	movdqa	@x[0], @t[0]
+	movdqa	@x[1], @t[1]
+___
+	&Mul_GF4  	(@x[0], @x[1], @y[0], @y[1], @t[2]);
+$code.=<<___;
+	pxor	@x[2], @t[0]
+	pxor	@x[3], @t[1]
+	pxor	@y[2], @y[0]
+	pxor	@y[3], @y[1]
+___
+	Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[2], @x[3], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	pxor	@t[0], @x[0]
+	pxor	@t[0], @x[2]
+	pxor	@t[1], @x[1]
+	pxor	@t[1], @x[3]
+
+	movdqa	@x[4], @t[0]
+	movdqa	@x[5], @t[1]
+	pxor	@x[6], @t[0]
+	pxor	@x[7], @t[1]
+___
+	&Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[6], @x[7], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	pxor	@y[2], @y[0]
+	pxor	@y[3], @y[1]
+___
+	&Mul_GF4  	(@x[4], @x[5], @y[0], @y[1], @t[3]);
+$code.=<<___;
+	pxor	@t[0], @x[4]
+	pxor	@t[0], @x[6]
+	pxor	@t[1], @x[5]
+	pxor	@t[1], @x[7]
+___
+}
+sub Inv_GF256 {
+#;********************************************************************
+#;* Inv_GF256: Input x0-x7 Output x0-x7 Temp t0-t3,s0-s3 (144)       *
+#;********************************************************************
+my @x=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+# direct optimizations from hardware
+$code.=<<___;
+	movdqa	@x[4], @t[3]
+	movdqa	@x[5], @t[2]
+	movdqa	@x[1], @t[1]
+	movdqa	@x[7], @s[1]
+	movdqa	@x[0], @s[0]
+
+	pxor	@x[6], @t[3]
+	pxor	@x[7], @t[2]
+	pxor	@x[3], @t[1]
+	 movdqa	@t[3], @s[2]
+	pxor	@x[6], @s[1]
+	 movdqa	@t[2], @t[0]
+	pxor	@x[2], @s[0]
+	 movdqa	@t[3], @s[3]
+
+	por	@t[1], @t[2]
+	por	@s[0], @t[3]
+	pxor	@t[0], @s[3]
+	pand	@s[0], @s[2]
+	pxor	@t[1], @s[0]
+	pand	@t[1], @t[0]
+	pand	@s[0], @s[3]
+	movdqa	@x[3], @s[0]
+	pxor	@x[2], @s[0]
+	pand	@s[0], @s[1]
+	pxor	@s[1], @t[3]
+	pxor	@s[1], @t[2]
+	movdqa	@x[4], @s[1]
+	movdqa	@x[1], @s[0]
+	pxor	@x[5], @s[1]
+	pxor	@x[0], @s[0]
+	movdqa	@s[1], @t[1]
+	pand	@s[0], @s[1]
+	por	@s[0], @t[1]
+	pxor	@s[1], @t[0]
+	pxor	@s[3], @t[3]
+	pxor	@s[2], @t[2]
+	pxor	@s[3], @t[1]
+	movdqa	@x[7], @s[0]
+	pxor	@s[2], @t[0]
+	movdqa	@x[6], @s[1]
+	pxor	@s[2], @t[1]
+	movdqa	@x[5], @s[2]
+	pand	@x[3], @s[0]
+	movdqa	@x[4], @s[3]
+	pand	@x[2], @s[1]
+	pand	@x[1], @s[2]
+	por	@x[0], @s[3]
+	pxor	@s[0], @t[3]
+	pxor	@s[1], @t[2]
+	pxor	@s[2], @t[1]
+	pxor	@s[3], @t[0] 
+
+	#Inv_GF16 \t0, \t1, \t2, \t3, \s0, \s1, \s2, \s3
+
+	# new smaller inversion
+
+	movdqa	@t[3], @s[0]
+	pand	@t[1], @t[3]
+	pxor	@t[2], @s[0]
+
+	movdqa	@t[0], @s[2]
+	movdqa	@s[0], @s[3]
+	pxor	@t[3], @s[2]
+	pand	@s[2], @s[3]
+
+	movdqa	@t[1], @s[1]
+	pxor	@t[2], @s[3]
+	pxor	@t[0], @s[1]
+
+	pxor	@t[2], @t[3]
+
+	pand	@t[3], @s[1]
+
+	movdqa	@s[2], @t[2]
+	pxor	@t[0], @s[1]
+
+	pxor	@s[1], @t[2]
+	pxor	@s[1], @t[1]
+
+	pand	@t[0], @t[2]
+
+	pxor	@t[2], @s[2]
+	pxor	@t[2], @t[1]
+
+	pand	@s[3], @s[2]
+
+	pxor	@s[0], @s[2]
+___
+# output in s3, s2, s1, t1
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \t2, \t3, \t0, \t1, \s0, \s1, \s2, \s3
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \s3, \s2, \s1, \t1, \s0, \t0, \t2, \t3
+	&Mul_GF16_2(@x,@s[3,2,1],@t[1],@s[0],@t[0,2,3]);
+
+### output msb > [x3,x2,x1,x0,x7,x6,x5,x4] < lsb
+}
+
+# AES linear components
+
+sub ShiftRows {
+my @x=@_[0..7];
+my $mask=pop;
+$code.=<<___;
+	pxor	0x00($key),@x[0]
+	pxor	0x10($key),@x[1]
+	pshufb	$mask,@x[0]
+	pxor	0x20($key),@x[2]
+	pshufb	$mask,@x[1]
+	pxor	0x30($key),@x[3]
+	pshufb	$mask,@x[2]
+	pxor	0x40($key),@x[4]
+	pshufb	$mask,@x[3]
+	pxor	0x50($key),@x[5]
+	pshufb	$mask,@x[4]
+	pxor	0x60($key),@x[6]
+	pshufb	$mask,@x[5]
+	pxor	0x70($key),@x[7]
+	pshufb	$mask,@x[6]
+	lea	0x80($key),$key
+	pshufb	$mask,@x[7]
+___
+}
+
+sub MixColumns {
+# modified to emit output in order suitable for feeding back to aesenc[last]
+my @x=@_[0..7];
+my @t=@_[8..15];
+my $inv=@_[16];	# optional
+$code.=<<___;
+	pshufd	\$0x93, @x[0], @t[0]	# x0 <<< 32
+	pshufd	\$0x93, @x[1], @t[1]
+	 pxor	@t[0], @x[0]		# x0 ^ (x0 <<< 32)
+	pshufd	\$0x93, @x[2], @t[2]
+	 pxor	@t[1], @x[1]
+	pshufd	\$0x93, @x[3], @t[3]
+	 pxor	@t[2], @x[2]
+	pshufd	\$0x93, @x[4], @t[4]
+	 pxor	@t[3], @x[3]
+	pshufd	\$0x93, @x[5], @t[5]
+	 pxor	@t[4], @x[4]
+	pshufd	\$0x93, @x[6], @t[6]
+	 pxor	@t[5], @x[5]
+	pshufd	\$0x93, @x[7], @t[7]
+	 pxor	@t[6], @x[6]
+	 pxor	@t[7], @x[7]
+
+	pxor	@x[0], @t[1]
+	pxor	@x[7], @t[0]
+	pxor	@x[7], @t[1]
+	 pshufd	\$0x4E, @x[0], @x[0] 	# (x0 ^ (x0 <<< 32)) <<< 64)
+	pxor	@x[1], @t[2]
+	 pshufd	\$0x4E, @x[1], @x[1]
+	pxor	@x[4], @t[5]
+	 pxor	@t[0], @x[0]
+	pxor	@x[5], @t[6]
+	 pxor	@t[1], @x[1]
+	pxor	@x[3], @t[4]
+	 pshufd	\$0x4E, @x[4], @t[0]
+	pxor	@x[6], @t[7]
+	 pshufd	\$0x4E, @x[5], @t[1]
+	pxor	@x[2], @t[3]
+	 pshufd	\$0x4E, @x[3], @x[4]
+	pxor	@x[7], @t[3]
+	 pshufd	\$0x4E, @x[7], @x[5]
+	pxor	@x[7], @t[4]
+	 pshufd	\$0x4E, @x[6], @x[3]
+	pxor	@t[4], @t[0]
+	 pshufd	\$0x4E, @x[2], @x[6]
+	pxor	@t[5], @t[1]
+___
+$code.=<<___ if (!$inv);
+	pxor	@t[3], @x[4]
+	pxor	@t[7], @x[5]
+	pxor	@t[6], @x[3]
+	 movdqa	@t[0], @x[2]
+	pxor	@t[2], @x[6]
+	 movdqa	@t[1], @x[7]
+___
+$code.=<<___ if ($inv);
+	pxor	@x[4], @t[3]
+	pxor	@t[7], @x[5]
+	pxor	@x[3], @t[6]
+	 movdqa	@t[0], @x[3]
+	pxor	@t[2], @x[6]
+	 movdqa	@t[6], @x[2]
+	 movdqa	@t[1], @x[7]
+	 movdqa	@x[6], @x[4]
+	 movdqa	@t[3], @x[6]
+___
+}
+
+sub InvMixColumns_orig {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+$code.=<<___;
+	# multiplication by 0x0e
+	pshufd	\$0x93, @x[7], @t[7]
+	movdqa	@x[2], @t[2]
+	pxor	@x[5], @x[7]		# 7 5
+	pxor	@x[5], @x[2]		# 2 5
+	pshufd	\$0x93, @x[0], @t[0]
+	movdqa	@x[5], @t[5]
+	pxor	@x[0], @x[5]		# 5 0		[1]
+	pxor	@x[1], @x[0]		# 0 1
+	pshufd	\$0x93, @x[1], @t[1]
+	pxor	@x[2], @x[1]		# 1 25
+	pxor	@x[6], @x[0]		# 01 6		[2]
+	pxor	@x[3], @x[1]		# 125 3		[4]
+	pshufd	\$0x93, @x[3], @t[3]
+	pxor	@x[0], @x[2]		# 25 016	[3]
+	pxor	@x[7], @x[3]		# 3 75
+	pxor	@x[6], @x[7]		# 75 6		[0]
+	pshufd	\$0x93, @x[6], @t[6]
+	movdqa	@x[4], @t[4]
+	pxor	@x[4], @x[6]		# 6 4
+	pxor	@x[3], @x[4]		# 4 375		[6]
+	pxor	@x[7], @x[3]		# 375 756=36
+	pxor	@t[5], @x[6]		# 64 5		[7]
+	pxor	@t[2], @x[3]		# 36 2
+	pxor	@t[4], @x[3]		# 362 4		[5]
+	pshufd	\$0x93, @t[5], @t[5]
+___
+					my @y = @x[7,5,0,2,1,3,4,6];
+$code.=<<___;
+	# multiplication by 0x0b
+	pxor	@y[0], @y[1]
+	pxor	@t[0], @y[0]
+	pxor	@t[1], @y[1]
+	pshufd	\$0x93, @t[2], @t[2]
+	pxor	@t[5], @y[0]
+	pxor	@t[6], @y[1]
+	pxor	@t[7], @y[0]
+	pshufd	\$0x93, @t[4], @t[4]
+	pxor	@t[6], @t[7]		# clobber t[7]
+	pxor	@y[0], @y[1]
+
+	pxor	@t[0], @y[3]
+	pshufd	\$0x93, @t[0], @t[0]
+	pxor	@t[1], @y[2]
+	pxor	@t[1], @y[4]
+	pxor	@t[2], @y[2]
+	pshufd	\$0x93, @t[1], @t[1]
+	pxor	@t[2], @y[3]
+	pxor	@t[2], @y[5]
+	pxor	@t[7], @y[2]
+	pshufd	\$0x93, @t[2], @t[2]
+	pxor	@t[3], @y[3]
+	pxor	@t[3], @y[6]
+	pxor	@t[3], @y[4]
+	pshufd	\$0x93, @t[3], @t[3]
+	pxor	@t[4], @y[7]
+	pxor	@t[4], @y[5]
+	pxor	@t[7], @y[7]
+	pxor	@t[5], @y[3]
+	pxor	@t[4], @y[4]
+	pxor	@t[5], @t[7]		# clobber t[7] even more
+
+	pxor	@t[7], @y[5]
+	pshufd	\$0x93, @t[4], @t[4]
+	pxor	@t[7], @y[6]
+	pxor	@t[7], @y[4]
+
+	pxor	@t[5], @t[7]
+	pshufd	\$0x93, @t[5], @t[5]
+	pxor	@t[6], @t[7]		# restore t[7]
+
+	# multiplication by 0x0d
+	pxor	@y[7], @y[4]
+	pxor	@t[4], @y[7]
+	pshufd	\$0x93, @t[6], @t[6]
+	pxor	@t[0], @y[2]
+	pxor	@t[5], @y[7]
+	pxor	@t[2], @y[2]
+	pshufd	\$0x93, @t[7], @t[7]
+
+	pxor	@y[1], @y[3]
+	pxor	@t[1], @y[1]
+	pxor	@t[0], @y[0]
+	pxor	@t[0], @y[3]
+	pxor	@t[5], @y[1]
+	pxor	@t[5], @y[0]
+	pxor	@t[7], @y[1]
+	pshufd	\$0x93, @t[0], @t[0]
+	pxor	@t[6], @y[0]
+	pxor	@y[1], @y[3]
+	pxor	@t[1], @y[4]
+	pshufd	\$0x93, @t[1], @t[1]
+
+	pxor	@t[7], @y[7]
+	pxor	@t[2], @y[4]
+	pxor	@t[2], @y[5]
+	pshufd	\$0x93, @t[2], @t[2]
+	pxor	@t[6], @y[2]
+	pxor	@t[3], @t[6]		# clobber t[6]
+	pxor	@y[7], @y[4]
+	pxor	@t[6], @y[3]
+
+	pxor	@t[6], @y[6]
+	pxor	@t[5], @y[5]
+	pxor	@t[4], @y[6]
+	pshufd	\$0x93, @t[4], @t[4]
+	pxor	@t[6], @y[5]
+	pxor	@t[7], @y[6]
+	pxor	@t[3], @t[6]		# restore t[6]
+
+	pshufd	\$0x93, @t[5], @t[5]
+	pshufd	\$0x93, @t[6], @t[6]
+	pshufd	\$0x93, @t[7], @t[7]
+	pshufd	\$0x93, @t[3], @t[3]
+
+	# multiplication by 0x09
+	pxor	@y[1], @y[4]
+	pxor	@y[1], @t[1]		# t[1]=y[1]
+	pxor	@t[5], @t[0]		# clobber t[0]
+	pxor	@t[5], @t[1]
+	pxor	@t[0], @y[3]
+	pxor	@y[0], @t[0]		# t[0]=y[0]
+	pxor	@t[6], @t[1]
+	pxor	@t[7], @t[6]		# clobber t[6]
+	pxor	@t[1], @y[4]
+	pxor	@t[4], @y[7]
+	pxor	@y[4], @t[4]		# t[4]=y[4]
+	pxor	@t[3], @y[6]
+	pxor	@y[3], @t[3]		# t[3]=y[3]
+	pxor	@t[2], @y[5]
+	pxor	@y[2], @t[2]		# t[2]=y[2]
+	pxor	@t[7], @t[3]
+	pxor	@y[5], @t[5]		# t[5]=y[5]
+	pxor	@t[6], @t[2]
+	pxor	@t[6], @t[5]
+	pxor	@y[6], @t[6]		# t[6]=y[6]
+	pxor	@y[7], @t[7]		# t[7]=y[7]
+
+	movdqa	@t[0],@XMM[0]
+	movdqa	@t[1],@XMM[1]
+	movdqa	@t[2],@XMM[2]
+	movdqa	@t[3],@XMM[3]
+	movdqa	@t[4],@XMM[4]
+	movdqa	@t[5],@XMM[5]
+	movdqa	@t[6],@XMM[6]
+	movdqa	@t[7],@XMM[7]
+___
+}
+
+sub InvMixColumns {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+# Thanks to Jussi Kivilinna for providing pointer to
+#
+# | 0e 0b 0d 09 |   | 02 03 01 01 |   | 05 00 04 00 |
+# | 09 0e 0b 0d | = | 01 02 03 01 | x | 00 05 00 04 |
+# | 0d 09 0e 0b |   | 01 01 02 03 |   | 04 00 05 00 |
+# | 0b 0d 09 0e |   | 03 01 01 02 |   | 00 04 00 05 |
+
+$code.=<<___;
+	# multiplication by 0x05-0x00-0x04-0x00
+	pshufd	\$0x4E, @x[0], @t[0]
+	pshufd	\$0x4E, @x[6], @t[6]
+	pxor	@x[0], @t[0]
+	pshufd	\$0x4E, @x[7], @t[7]
+	pxor	@x[6], @t[6]
+	pshufd	\$0x4E, @x[1], @t[1]
+	pxor	@x[7], @t[7]
+	pshufd	\$0x4E, @x[2], @t[2]
+	pxor	@x[1], @t[1]
+	pshufd	\$0x4E, @x[3], @t[3]
+	pxor	@x[2], @t[2]
+	 pxor	@t[6], @x[0]
+	 pxor	@t[6], @x[1]
+	pshufd	\$0x4E, @x[4], @t[4]
+	pxor	@x[3], @t[3]
+	 pxor	@t[0], @x[2]
+	 pxor	@t[1], @x[3]
+	pshufd	\$0x4E, @x[5], @t[5]
+	pxor	@x[4], @t[4]
+	 pxor	@t[7], @x[1]
+	 pxor	@t[2], @x[4]
+	pxor	@x[5], @t[5]
+
+	 pxor	@t[7], @x[2]
+	 pxor	@t[6], @x[3]
+	 pxor	@t[6], @x[4]
+	 pxor	@t[3], @x[5]
+	 pxor	@t[4], @x[6]
+	 pxor	@t[7], @x[4]
+	 pxor	@t[7], @x[5]
+	 pxor	@t[5], @x[7]
+___
+	&MixColumns	(@x,@t,1);	# flipped 2<->3 and 4<->6
+}
+
+sub aesenc {				# not used
+my @b=@_[0..7];
+my @t=@_[8..15];
+$code.=<<___;
+	movdqa	0x30($const),@t[0]	# .LSR
+___
+	&ShiftRows	(@b,@t[0]);
+	&Sbox		(@b,@t);
+	&MixColumns	(@b[0,1,4,6,3,7,2,5],@t);
+}
+
+sub aesenclast {			# not used
+my @b=@_[0..7];
+my @t=@_[8..15];
+$code.=<<___;
+	movdqa	0x40($const),@t[0]	# .LSRM0
+___
+	&ShiftRows	(@b,@t[0]);
+	&Sbox		(@b,@t);
+$code.=<<___
+	pxor	0x00($key),@b[0]
+	pxor	0x10($key),@b[1]
+	pxor	0x20($key),@b[4]
+	pxor	0x30($key),@b[6]
+	pxor	0x40($key),@b[3]
+	pxor	0x50($key),@b[7]
+	pxor	0x60($key),@b[2]
+	pxor	0x70($key),@b[5]
+___
+}
+
+sub swapmove {
+my ($a,$b,$n,$mask,$t)=@_;
+$code.=<<___;
+	movdqa	$b,$t
+	psrlq	\$$n,$b
+	pxor  	$a,$b
+	pand	$mask,$b
+	pxor	$b,$a
+	psllq	\$$n,$b
+	pxor	$t,$b
+___
+}
+sub swapmove2x {
+my ($a0,$b0,$a1,$b1,$n,$mask,$t0,$t1)=@_;
+$code.=<<___;
+	movdqa	$b0,$t0
+	psrlq	\$$n,$b0
+	 movdqa	$b1,$t1
+	 psrlq	\$$n,$b1
+	pxor  	$a0,$b0
+	 pxor  	$a1,$b1
+	pand	$mask,$b0
+	 pand	$mask,$b1
+	pxor	$b0,$a0
+	psllq	\$$n,$b0
+	 pxor	$b1,$a1
+	 psllq	\$$n,$b1
+	pxor	$t0,$b0
+	 pxor	$t1,$b1
+___
+}
+
+sub bitslice {
+my @x=reverse(@_[0..7]);
+my ($t0,$t1,$t2,$t3)=@_[8..11];
+$code.=<<___;
+	movdqa	0x00($const),$t0	# .LBS0
+	movdqa	0x10($const),$t1	# .LBS1
+___
+	&swapmove2x(@x[0,1,2,3],1,$t0,$t2,$t3);
+	&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+$code.=<<___;
+	movdqa	0x20($const),$t0	# .LBS2
+___
+	&swapmove2x(@x[0,2,1,3],2,$t1,$t2,$t3);
+	&swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+
+	&swapmove2x(@x[0,4,1,5],4,$t0,$t2,$t3);
+	&swapmove2x(@x[2,6,3,7],4,$t0,$t2,$t3);
+}
+
+$code.=<<___;
+.text
+
+.extern	asm_AES_encrypt
+.extern	asm_AES_decrypt
+
+.type	_bsaes_encrypt8,\@abi-omnipotent
+.align	64
+_bsaes_encrypt8:
+	lea	.LBS0(%rip), $const	# constants table
+
+	movdqa	($key), @XMM[9]		# round 0 key
+	lea	0x10($key), $key
+	movdqa	0x50($const), @XMM[8]	# .LM0SR
+	pxor	@XMM[9], @XMM[0]	# xor with round0 key
+	pxor	@XMM[9], @XMM[1]
+	 pshufb	@XMM[8], @XMM[0]
+	pxor	@XMM[9], @XMM[2]
+	 pshufb	@XMM[8], @XMM[1]
+	pxor	@XMM[9], @XMM[3]
+	 pshufb	@XMM[8], @XMM[2]
+	pxor	@XMM[9], @XMM[4]
+	 pshufb	@XMM[8], @XMM[3]
+	pxor	@XMM[9], @XMM[5]
+	 pshufb	@XMM[8], @XMM[4]
+	pxor	@XMM[9], @XMM[6]
+	 pshufb	@XMM[8], @XMM[5]
+	pxor	@XMM[9], @XMM[7]
+	 pshufb	@XMM[8], @XMM[6]
+	 pshufb	@XMM[8], @XMM[7]
+_bsaes_encrypt8_bitslice:
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	dec	$rounds
+	jmp	.Lenc_sbox
+.align	16
+.Lenc_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8]);
+$code.=".Lenc_sbox:\n";
+	&Sbox		(@XMM[0..7, 8..15]);
+$code.=<<___;
+	dec	$rounds
+	jl	.Lenc_done
+___
+	&MixColumns	(@XMM[0,1,4,6,3,7,2,5, 8..15]);
+$code.=<<___;
+	movdqa	0x30($const), @XMM[8]	# .LSR
+	jnz	.Lenc_loop
+	movdqa	0x40($const), @XMM[8]	# .LSRM0
+	jmp	.Lenc_loop
+.align	16
+.Lenc_done:
+___
+	# output in lsb > [t0, t1, t4, t6, t3, t7, t2, t5] < msb
+	&bitslice	(@XMM[0,1,4,6,3,7,2,5, 8..11]);
+$code.=<<___;
+	movdqa	($key), @XMM[8]		# last round key
+	pxor	@XMM[8], @XMM[4]
+	pxor	@XMM[8], @XMM[6]
+	pxor	@XMM[8], @XMM[3]
+	pxor	@XMM[8], @XMM[7]
+	pxor	@XMM[8], @XMM[2]
+	pxor	@XMM[8], @XMM[5]
+	pxor	@XMM[8], @XMM[0]
+	pxor	@XMM[8], @XMM[1]
+	ret
+.size	_bsaes_encrypt8,.-_bsaes_encrypt8
+
+.type	_bsaes_decrypt8,\@abi-omnipotent
+.align	64
+_bsaes_decrypt8:
+	lea	.LBS0(%rip), $const	# constants table
+
+	movdqa	($key), @XMM[9]		# round 0 key
+	lea	0x10($key), $key
+	movdqa	-0x30($const), @XMM[8]	# .LM0ISR
+	pxor	@XMM[9], @XMM[0]	# xor with round0 key
+	pxor	@XMM[9], @XMM[1]
+	 pshufb	@XMM[8], @XMM[0]
+	pxor	@XMM[9], @XMM[2]
+	 pshufb	@XMM[8], @XMM[1]
+	pxor	@XMM[9], @XMM[3]
+	 pshufb	@XMM[8], @XMM[2]
+	pxor	@XMM[9], @XMM[4]
+	 pshufb	@XMM[8], @XMM[3]
+	pxor	@XMM[9], @XMM[5]
+	 pshufb	@XMM[8], @XMM[4]
+	pxor	@XMM[9], @XMM[6]
+	 pshufb	@XMM[8], @XMM[5]
+	pxor	@XMM[9], @XMM[7]
+	 pshufb	@XMM[8], @XMM[6]
+	 pshufb	@XMM[8], @XMM[7]
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	dec	$rounds
+	jmp	.Ldec_sbox
+.align	16
+.Ldec_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8]);
+$code.=".Ldec_sbox:\n";
+	&InvSbox	(@XMM[0..7, 8..15]);
+$code.=<<___;
+	dec	$rounds
+	jl	.Ldec_done
+___
+	&InvMixColumns	(@XMM[0,1,6,4,2,7,3,5, 8..15]);
+$code.=<<___;
+	movdqa	-0x10($const), @XMM[8]	# .LISR
+	jnz	.Ldec_loop
+	movdqa	-0x20($const), @XMM[8]	# .LISRM0
+	jmp	.Ldec_loop
+.align	16
+.Ldec_done:
+___
+	&bitslice	(@XMM[0,1,6,4,2,7,3,5, 8..11]);
+$code.=<<___;
+	movdqa	($key), @XMM[8]		# last round key
+	pxor	@XMM[8], @XMM[6]
+	pxor	@XMM[8], @XMM[4]
+	pxor	@XMM[8], @XMM[2]
+	pxor	@XMM[8], @XMM[7]
+	pxor	@XMM[8], @XMM[3]
+	pxor	@XMM[8], @XMM[5]
+	pxor	@XMM[8], @XMM[0]
+	pxor	@XMM[8], @XMM[1]
+	ret
+.size	_bsaes_decrypt8,.-_bsaes_decrypt8
+___
+}
+{
+my ($out,$inp,$rounds,$const)=("%rax","%rcx","%r10d","%r11");
+
+sub bitslice_key {
+my @x=reverse(@_[0..7]);
+my ($bs0,$bs1,$bs2,$t2,$t3)=@_[8..12];
+
+	&swapmove	(@x[0,1],1,$bs0,$t2,$t3);
+$code.=<<___;
+	#&swapmove(@x[2,3],1,$t0,$t2,$t3);
+	movdqa	@x[0], @x[2]
+	movdqa	@x[1], @x[3]
+___
+	#&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+
+	&swapmove2x	(@x[0,2,1,3],2,$bs1,$t2,$t3);
+$code.=<<___;
+	#&swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+	movdqa	@x[0], @x[4]
+	movdqa	@x[2], @x[6]
+	movdqa	@x[1], @x[5]
+	movdqa	@x[3], @x[7]
+___
+	&swapmove2x	(@x[0,4,1,5],4,$bs2,$t2,$t3);
+	&swapmove2x	(@x[2,6,3,7],4,$bs2,$t2,$t3);
+}
+
+$code.=<<___;
+.type	_bsaes_key_convert,\@abi-omnipotent
+.align	16
+_bsaes_key_convert:
+	lea	.Lmasks(%rip), $const
+	movdqu	($inp), %xmm7		# load round 0 key
+	lea	0x10($inp), $inp
+	movdqa	0x00($const), %xmm0	# 0x01...
+	movdqa	0x10($const), %xmm1	# 0x02...
+	movdqa	0x20($const), %xmm2	# 0x04...
+	movdqa	0x30($const), %xmm3	# 0x08...
+	movdqa	0x40($const), %xmm4	# .LM0
+	pcmpeqd	%xmm5, %xmm5		# .LNOT
+
+	movdqu	($inp), %xmm6		# load round 1 key
+	movdqa	%xmm7, ($out)		# save round 0 key
+	lea	0x10($out), $out
+	dec	$rounds
+	jmp	.Lkey_loop
+.align	16
+.Lkey_loop:
+	pshufb	%xmm4, %xmm6		# .LM0
+
+	movdqa	%xmm0,	%xmm8
+	movdqa	%xmm1,	%xmm9
+
+	pand	%xmm6,	%xmm8
+	pand	%xmm6,	%xmm9
+	movdqa	%xmm2,	%xmm10
+	pcmpeqb	%xmm0,	%xmm8
+	psllq	\$4,	%xmm0		# 0x10...
+	movdqa	%xmm3,	%xmm11
+	pcmpeqb	%xmm1,	%xmm9
+	psllq	\$4,	%xmm1		# 0x20...
+
+	pand	%xmm6,	%xmm10
+	pand	%xmm6,	%xmm11
+	movdqa	%xmm0,	%xmm12
+	pcmpeqb	%xmm2,	%xmm10
+	psllq	\$4,	%xmm2		# 0x40...
+	movdqa	%xmm1,	%xmm13
+	pcmpeqb	%xmm3,	%xmm11
+	psllq	\$4,	%xmm3		# 0x80...
+
+	movdqa	%xmm2,	%xmm14
+	movdqa	%xmm3,	%xmm15
+	 pxor	%xmm5,	%xmm8		# "pnot"
+	 pxor	%xmm5,	%xmm9
+
+	pand	%xmm6,	%xmm12
+	pand	%xmm6,	%xmm13
+	 movdqa	%xmm8, 0x00($out)	# write bit-sliced round key
+	pcmpeqb	%xmm0,	%xmm12
+	psrlq	\$4,	%xmm0		# 0x01...
+	 movdqa	%xmm9, 0x10($out)
+	pcmpeqb	%xmm1,	%xmm13
+	psrlq	\$4,	%xmm1		# 0x02...
+	 lea	0x10($inp), $inp
+
+	pand	%xmm6,	%xmm14
+	pand	%xmm6,	%xmm15
+	 movdqa	%xmm10, 0x20($out)
+	pcmpeqb	%xmm2,	%xmm14
+	psrlq	\$4,	%xmm2		# 0x04...
+	 movdqa	%xmm11, 0x30($out)
+	pcmpeqb	%xmm3,	%xmm15
+	psrlq	\$4,	%xmm3		# 0x08...
+	 movdqu	($inp), %xmm6		# load next round key
+
+	pxor	%xmm5, %xmm13		# "pnot"
+	pxor	%xmm5, %xmm14
+	movdqa	%xmm12, 0x40($out)
+	movdqa	%xmm13, 0x50($out)
+	movdqa	%xmm14, 0x60($out)
+	movdqa	%xmm15, 0x70($out)
+	lea	0x80($out),$out
+	dec	$rounds
+	jnz	.Lkey_loop
+
+	movdqa	0x50($const), %xmm7	# .L63
+	#movdqa	%xmm6, ($out)		# don't save last round key
+	ret
+.size	_bsaes_key_convert,.-_bsaes_key_convert
+___
+}
+
+if (0 && !$win64) {	# following four functions are unsupported interface
+			# used for benchmarking...
+$code.=<<___;
+.globl	bsaes_enc_key_convert
+.type	bsaes_enc_key_convert,\@function,2
+.align	16
+bsaes_enc_key_convert:
+	mov	240($inp),%r10d		# pass rounds
+	mov	$inp,%rcx		# pass key
+	mov	$out,%rax		# pass key schedule
+	call	_bsaes_key_convert
+	pxor	%xmm6,%xmm7		# fix up last round key
+	movdqa	%xmm7,(%rax)		# save last round key
+	ret
+.size	bsaes_enc_key_convert,.-bsaes_enc_key_convert
+
+.globl	bsaes_encrypt_128
+.type	bsaes_encrypt_128,\@function,4
+.align	16
+bsaes_encrypt_128:
+.Lenc128_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	movdqu	0x60($inp), @XMM[6]
+	movdqu	0x70($inp), @XMM[7]
+	mov	$key, %rax		# pass the $key
+	lea	0x80($inp), $inp
+	mov	\$10,%r10d
+
+	call	_bsaes_encrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$0x80,$len
+	ja	.Lenc128_loop
+	ret
+.size	bsaes_encrypt_128,.-bsaes_encrypt_128
+
+.globl	bsaes_dec_key_convert
+.type	bsaes_dec_key_convert,\@function,2
+.align	16
+bsaes_dec_key_convert:
+	mov	240($inp),%r10d		# pass rounds
+	mov	$inp,%rcx		# pass key
+	mov	$out,%rax		# pass key schedule
+	call	_bsaes_key_convert
+	pxor	($out),%xmm7		# fix up round 0 key
+	movdqa	%xmm6,(%rax)		# save last round key
+	movdqa	%xmm7,($out)
+	ret
+.size	bsaes_dec_key_convert,.-bsaes_dec_key_convert
+
+.globl	bsaes_decrypt_128
+.type	bsaes_decrypt_128,\@function,4
+.align	16
+bsaes_decrypt_128:
+.Ldec128_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	movdqu	0x60($inp), @XMM[6]
+	movdqu	0x70($inp), @XMM[7]
+	mov	$key, %rax		# pass the $key
+	lea	0x80($inp), $inp
+	mov	\$10,%r10d
+
+	call	_bsaes_decrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$0x80,$len
+	ja	.Ldec128_loop
+	ret
+.size	bsaes_decrypt_128,.-bsaes_decrypt_128
+___
+}
+{
+######################################################################
+#
+# OpenSSL interface
+#
+my ($arg1,$arg2,$arg3,$arg4,$arg5,$arg6)=$win64	? ("%rcx","%rdx","%r8","%r9","%r10","%r11d")
+						: ("%rdi","%rsi","%rdx","%rcx","%r8","%r9d");
+my ($inp,$out,$len,$key)=("%r12","%r13","%r14","%r15");
+
+if ($ecb) {
+$code.=<<___;
+.globl	bsaes_ecb_encrypt_blocks
+.type	bsaes_ecb_encrypt_blocks,\@abi-omnipotent
+.align	16
+bsaes_ecb_encrypt_blocks:
+	mov	%rsp, %rax
+.Lecb_enc_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lecb_enc_body:
+___
+$code.=<<___;
+	mov	%rsp,%rbp		# backup %rsp
+	mov	240($arg4),%eax		# rounds
+	mov	$arg1,$inp		# backup arguments
+	mov	$arg2,$out
+	mov	$arg3,$len
+	mov	$arg4,$key
+	cmp	\$8,$arg3
+	jb	.Lecb_enc_short
+
+	mov	%eax,%ebx		# backup rounds
+	shl	\$7,%rax		# 128 bytes per inner round key
+	sub	\$`128-32`,%rax		# size of bit-sliced key schedule
+	sub	%rax,%rsp
+	mov	%rsp,%rax		# pass key schedule
+	mov	$key,%rcx		# pass key
+	mov	%ebx,%r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	%xmm6,%xmm7		# fix up last round key
+	movdqa	%xmm7,(%rax)		# save last round key
+
+	sub	\$8,$len
+.Lecb_enc_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	mov	%rsp, %rax		# pass key schedule
+	movdqu	0x60($inp), @XMM[6]
+	mov	%ebx,%r10d		# pass rounds
+	movdqu	0x70($inp), @XMM[7]
+	lea	0x80($inp), $inp
+
+	call	_bsaes_encrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$8,$len
+	jnc	.Lecb_enc_loop
+
+	add	\$8,$len
+	jz	.Lecb_enc_done
+
+	movdqu	0x00($inp), @XMM[0]	# load input
+	mov	%rsp, %rax		# pass key schedule
+	mov	%ebx,%r10d		# pass rounds
+	cmp	\$2,$len
+	jb	.Lecb_enc_one
+	movdqu	0x10($inp), @XMM[1]
+	je	.Lecb_enc_two
+	movdqu	0x20($inp), @XMM[2]
+	cmp	\$4,$len
+	jb	.Lecb_enc_three
+	movdqu	0x30($inp), @XMM[3]
+	je	.Lecb_enc_four
+	movdqu	0x40($inp), @XMM[4]
+	cmp	\$6,$len
+	jb	.Lecb_enc_five
+	movdqu	0x50($inp), @XMM[5]
+	je	.Lecb_enc_six
+	movdqu	0x60($inp), @XMM[6]
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_six:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_five:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_four:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_three:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_two:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_one:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_short:
+	lea	($inp), $arg1
+	lea	($out), $arg2
+	lea	($key), $arg3
+	call	asm_AES_encrypt
+	lea	16($inp), $inp
+	lea	16($out), $out
+	dec	$len
+	jnz	.Lecb_enc_short
+
+.Lecb_enc_done:
+	lea	(%rsp),%rax
+	pxor	%xmm0, %xmm0
+.Lecb_enc_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	jb	.Lecb_enc_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lecb_enc_epilogue:
+	ret
+.size	bsaes_ecb_encrypt_blocks,.-bsaes_ecb_encrypt_blocks
+
+.globl	bsaes_ecb_decrypt_blocks
+.type	bsaes_ecb_decrypt_blocks,\@abi-omnipotent
+.align	16
+bsaes_ecb_decrypt_blocks:
+	mov	%rsp, %rax
+.Lecb_dec_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lecb_dec_body:
+___
+$code.=<<___;
+	mov	%rsp,%rbp		# backup %rsp
+	mov	240($arg4),%eax		# rounds
+	mov	$arg1,$inp		# backup arguments
+	mov	$arg2,$out
+	mov	$arg3,$len
+	mov	$arg4,$key
+	cmp	\$8,$arg3
+	jb	.Lecb_dec_short
+
+	mov	%eax,%ebx		# backup rounds
+	shl	\$7,%rax		# 128 bytes per inner round key
+	sub	\$`128-32`,%rax		# size of bit-sliced key schedule
+	sub	%rax,%rsp
+	mov	%rsp,%rax		# pass key schedule
+	mov	$key,%rcx		# pass key
+	mov	%ebx,%r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	(%rsp),%xmm7		# fix up 0 round key
+	movdqa	%xmm6,(%rax)		# save last round key
+	movdqa	%xmm7,(%rsp)
+
+	sub	\$8,$len
+.Lecb_dec_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	mov	%rsp, %rax		# pass key schedule
+	movdqu	0x60($inp), @XMM[6]
+	mov	%ebx,%r10d		# pass rounds
+	movdqu	0x70($inp), @XMM[7]
+	lea	0x80($inp), $inp
+
+	call	_bsaes_decrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$8,$len
+	jnc	.Lecb_dec_loop
+
+	add	\$8,$len
+	jz	.Lecb_dec_done
+
+	movdqu	0x00($inp), @XMM[0]	# load input
+	mov	%rsp, %rax		# pass key schedule
+	mov	%ebx,%r10d		# pass rounds
+	cmp	\$2,$len
+	jb	.Lecb_dec_one
+	movdqu	0x10($inp), @XMM[1]
+	je	.Lecb_dec_two
+	movdqu	0x20($inp), @XMM[2]
+	cmp	\$4,$len
+	jb	.Lecb_dec_three
+	movdqu	0x30($inp), @XMM[3]
+	je	.Lecb_dec_four
+	movdqu	0x40($inp), @XMM[4]
+	cmp	\$6,$len
+	jb	.Lecb_dec_five
+	movdqu	0x50($inp), @XMM[5]
+	je	.Lecb_dec_six
+	movdqu	0x60($inp), @XMM[6]
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_six:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_five:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_four:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_three:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_two:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_one:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_short:
+	lea	($inp), $arg1
+	lea	($out), $arg2
+	lea	($key), $arg3
+	call	asm_AES_decrypt
+	lea	16($inp), $inp
+	lea	16($out), $out
+	dec	$len
+	jnz	.Lecb_dec_short
+
+.Lecb_dec_done:
+	lea	(%rsp),%rax
+	pxor	%xmm0, %xmm0
+.Lecb_dec_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	jb	.Lecb_dec_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lecb_dec_epilogue:
+	ret
+.size	bsaes_ecb_decrypt_blocks,.-bsaes_ecb_decrypt_blocks
+___
+}
+$code.=<<___;
+.extern	asm_AES_cbc_encrypt
+.globl	bsaes_cbc_encrypt
+.type	bsaes_cbc_encrypt,\@abi-omnipotent
+.align	16
+bsaes_cbc_encrypt:
+___
+$code.=<<___ if ($win64);
+	mov	48(%rsp),$arg6		# pull direction flag
+___
+$code.=<<___;
+	cmp	\$0,$arg6
+	jne	asm_AES_cbc_encrypt
+	cmp	\$128,$arg3
+	jb	asm_AES_cbc_encrypt
+
+	mov	%rsp, %rax
+.Lcbc_dec_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lcbc_dec_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	mov	240($arg4), %eax	# rounds
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+	mov	$arg5, %rbx
+	shr	\$4, $len		# bytes to blocks
+
+	mov	%eax, %edx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%edx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	(%rsp),%xmm7		# fix up 0 round key
+	movdqa	%xmm6,(%rax)		# save last round key
+	movdqa	%xmm7,(%rsp)
+
+	movdqu	(%rbx), @XMM[15]	# load IV
+	sub	\$8,$len
+.Lcbc_dec_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	mov	%rsp, %rax		# pass key schedule
+	movdqu	0x60($inp), @XMM[6]
+	mov	%edx,%r10d		# pass rounds
+	movdqu	0x70($inp), @XMM[7]
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+
+	call	_bsaes_decrypt8
+
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[11], @XMM[2]
+	movdqu	0x50($inp), @XMM[13]
+	pxor	@XMM[12], @XMM[7]
+	movdqu	0x60($inp), @XMM[14]
+	pxor	@XMM[13], @XMM[3]
+	movdqu	0x70($inp), @XMM[15]	# IV
+	pxor	@XMM[14], @XMM[5]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	lea	0x80($inp), $inp
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$8,$len
+	jnc	.Lcbc_dec_loop
+
+	add	\$8,$len
+	jz	.Lcbc_dec_done
+
+	movdqu	0x00($inp), @XMM[0]	# load input
+	mov	%rsp, %rax		# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+	cmp	\$2,$len
+	jb	.Lcbc_dec_one
+	movdqu	0x10($inp), @XMM[1]
+	je	.Lcbc_dec_two
+	movdqu	0x20($inp), @XMM[2]
+	cmp	\$4,$len
+	jb	.Lcbc_dec_three
+	movdqu	0x30($inp), @XMM[3]
+	je	.Lcbc_dec_four
+	movdqu	0x40($inp), @XMM[4]
+	cmp	\$6,$len
+	jb	.Lcbc_dec_five
+	movdqu	0x50($inp), @XMM[5]
+	je	.Lcbc_dec_six
+	movdqu	0x60($inp), @XMM[6]
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[11], @XMM[2]
+	movdqu	0x50($inp), @XMM[13]
+	pxor	@XMM[12], @XMM[7]
+	movdqu	0x60($inp), @XMM[15]	# IV
+	pxor	@XMM[13], @XMM[3]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_six:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[11], @XMM[2]
+	movdqu	0x50($inp), @XMM[15]	# IV
+	pxor	@XMM[12], @XMM[7]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_five:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[15]	# IV
+	pxor	@XMM[11], @XMM[2]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_four:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[15]	# IV
+	pxor	@XMM[10], @XMM[4]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_three:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[15]	# IV
+	pxor	@XMM[9], @XMM[6]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_two:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[15]	# IV
+	pxor	@XMM[8], @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_one:
+	lea	($inp), $arg1
+	lea	0x20(%rbp), $arg2	# buffer output
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[15]	# ^= IV
+	movdqu	@XMM[15], ($out)	# write output
+	movdqa	@XMM[0], @XMM[15]	# IV
+
+.Lcbc_dec_done:
+	movdqu	@XMM[15], (%rbx)	# return IV
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lcbc_dec_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lcbc_dec_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lcbc_dec_epilogue:
+	ret
+.size	bsaes_cbc_encrypt,.-bsaes_cbc_encrypt
+
+.globl	bsaes_ctr32_encrypt_blocks
+.type	bsaes_ctr32_encrypt_blocks,\@abi-omnipotent
+.align	16
+bsaes_ctr32_encrypt_blocks:
+	mov	%rsp, %rax
+.Lctr_enc_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lctr_enc_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	movdqu	($arg5), %xmm0		# load counter
+	mov	240($arg4), %eax	# rounds
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+	movdqa	%xmm0, 0x20(%rbp)	# copy counter
+	cmp	\$8, $arg3
+	jb	.Lctr_enc_short
+
+	mov	%eax, %ebx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%ebx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	%xmm6,%xmm7		# fix up last round key
+	movdqa	%xmm7,(%rax)		# save last round key
+
+	movdqa	(%rsp), @XMM[9]		# load round0 key
+	lea	.LADD1(%rip), %r11
+	movdqa	0x20(%rbp), @XMM[0]	# counter copy
+	movdqa	-0x20(%r11), @XMM[8]	# .LSWPUP
+	pshufb	@XMM[8], @XMM[9]	# byte swap upper part
+	pshufb	@XMM[8], @XMM[0]
+	movdqa	@XMM[9], (%rsp)		# save adjusted round0 key
+	jmp	.Lctr_enc_loop
+.align	16
+.Lctr_enc_loop:
+	movdqa	@XMM[0], 0x20(%rbp)	# save counter
+	movdqa	@XMM[0], @XMM[1]	# prepare 8 counter values
+	movdqa	@XMM[0], @XMM[2]
+	paddd	0x00(%r11), @XMM[1]	# .LADD1
+	movdqa	@XMM[0], @XMM[3]
+	paddd	0x10(%r11), @XMM[2]	# .LADD2
+	movdqa	@XMM[0], @XMM[4]
+	paddd	0x20(%r11), @XMM[3]	# .LADD3
+	movdqa	@XMM[0], @XMM[5]
+	paddd	0x30(%r11), @XMM[4]	# .LADD4
+	movdqa	@XMM[0], @XMM[6]
+	paddd	0x40(%r11), @XMM[5]	# .LADD5
+	movdqa	@XMM[0], @XMM[7]
+	paddd	0x50(%r11), @XMM[6]	# .LADD6
+	paddd	0x60(%r11), @XMM[7]	# .LADD7
+
+	# Borrow prologue from _bsaes_encrypt8 to use the opportunity
+	# to flip byte order in 32-bit counter
+	movdqa	(%rsp), @XMM[9]		# round 0 key
+	lea	0x10(%rsp), %rax	# pass key schedule
+	movdqa	-0x10(%r11), @XMM[8]	# .LSWPUPM0SR
+	pxor	@XMM[9], @XMM[0]	# xor with round0 key
+	pxor	@XMM[9], @XMM[1]
+	 pshufb	@XMM[8], @XMM[0]
+	pxor	@XMM[9], @XMM[2]
+	 pshufb	@XMM[8], @XMM[1]
+	pxor	@XMM[9], @XMM[3]
+	 pshufb	@XMM[8], @XMM[2]
+	pxor	@XMM[9], @XMM[4]
+	 pshufb	@XMM[8], @XMM[3]
+	pxor	@XMM[9], @XMM[5]
+	 pshufb	@XMM[8], @XMM[4]
+	pxor	@XMM[9], @XMM[6]
+	 pshufb	@XMM[8], @XMM[5]
+	pxor	@XMM[9], @XMM[7]
+	 pshufb	@XMM[8], @XMM[6]
+	lea	.LBS0(%rip), %r11	# constants table
+	 pshufb	@XMM[8], @XMM[7]
+	mov	%ebx,%r10d		# pass rounds
+
+	call	_bsaes_encrypt8_bitslice
+
+	sub	\$8,$len
+	jc	.Lctr_enc_loop_done
+
+	movdqu	0x00($inp), @XMM[8]	# load input
+	movdqu	0x10($inp), @XMM[9]
+	movdqu	0x20($inp), @XMM[10]
+	movdqu	0x30($inp), @XMM[11]
+	movdqu	0x40($inp), @XMM[12]
+	movdqu	0x50($inp), @XMM[13]
+	movdqu	0x60($inp), @XMM[14]
+	movdqu	0x70($inp), @XMM[15]
+	lea	0x80($inp),$inp
+	pxor	@XMM[0], @XMM[8]
+	movdqa	0x20(%rbp), @XMM[0]	# load counter
+	pxor	@XMM[9], @XMM[1]
+	movdqu	@XMM[8], 0x00($out)	# write output
+	pxor	@XMM[10], @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	@XMM[11], @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	@XMM[12], @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	@XMM[13], @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	pxor	@XMM[14], @XMM[2]
+	movdqu	@XMM[7], 0x50($out)
+	pxor	@XMM[15], @XMM[5]
+	movdqu	@XMM[2], 0x60($out)
+	lea	.LADD1(%rip), %r11
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	paddd	0x70(%r11), @XMM[0]	# .LADD8
+	jnz	.Lctr_enc_loop
+
+	jmp	.Lctr_enc_done
+.align	16
+.Lctr_enc_loop_done:
+	add	\$8, $len
+	movdqu	0x00($inp), @XMM[8]	# load input
+	pxor	@XMM[8], @XMM[0]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	cmp	\$2,$len
+	jb	.Lctr_enc_done
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[9], @XMM[1]
+	movdqu	@XMM[1], 0x10($out)
+	je	.Lctr_enc_done
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	@XMM[4], 0x20($out)
+	cmp	\$4,$len
+	jb	.Lctr_enc_done
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[11], @XMM[6]
+	movdqu	@XMM[6], 0x30($out)
+	je	.Lctr_enc_done
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[12], @XMM[3]
+	movdqu	@XMM[3], 0x40($out)
+	cmp	\$6,$len
+	jb	.Lctr_enc_done
+	movdqu	0x50($inp), @XMM[13]
+	pxor	@XMM[13], @XMM[7]
+	movdqu	@XMM[7], 0x50($out)
+	je	.Lctr_enc_done
+	movdqu	0x60($inp), @XMM[14]
+	pxor	@XMM[14], @XMM[2]
+	movdqu	@XMM[2], 0x60($out)
+	jmp	.Lctr_enc_done
+
+.align	16
+.Lctr_enc_short:
+	lea	0x20(%rbp), $arg1
+	lea	0x30(%rbp), $arg2
+	lea	($key), $arg3
+	call	asm_AES_encrypt
+	movdqu	($inp), @XMM[1]
+	lea	16($inp), $inp
+	mov	0x2c(%rbp), %eax	# load 32-bit counter
+	bswap	%eax
+	pxor	0x30(%rbp), @XMM[1]
+	inc	%eax			# increment
+	movdqu	@XMM[1], ($out)
+	bswap	%eax
+	lea	16($out), $out
+	mov	%eax, 0x2c(%rsp)	# save 32-bit counter
+	dec	$len
+	jnz	.Lctr_enc_short
+
+.Lctr_enc_done:
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lctr_enc_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lctr_enc_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lctr_enc_epilogue:
+	ret
+.size	bsaes_ctr32_encrypt_blocks,.-bsaes_ctr32_encrypt_blocks
+___
+######################################################################
+# void bsaes_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2,
+#	const unsigned char iv[16]);
+#
+my ($twmask,$twres,$twtmp)=@XMM[13..15];
+$arg6=~s/d$//;
+
+$code.=<<___;
+.globl	bsaes_xts_encrypt
+.type	bsaes_xts_encrypt,\@abi-omnipotent
+.align	16
+bsaes_xts_encrypt:
+	mov	%rsp, %rax
+.Lxts_enc_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull key2
+	mov	0xa8(%rsp),$arg6	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lxts_enc_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+
+	lea	($arg6), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($arg5), $arg3
+	call	asm_AES_encrypt		# generate initial tweak
+
+	mov	240($key), %eax		# rounds
+	mov	$len, %rbx		# backup $len
+
+	mov	%eax, %edx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%edx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	%xmm6, %xmm7		# fix up last round key
+	movdqa	%xmm7, (%rax)		# save last round key
+
+	and	\$-16, $len
+	sub	\$0x80, %rsp		# place for tweak[8]
+	movdqa	0x20(%rbp), @XMM[7]	# initial tweak
+
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+
+	sub	\$0x80, $len
+	jc	.Lxts_enc_short
+	jmp	.Lxts_enc_loop
+
+.align	16
+.Lxts_enc_loop:
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqu	0x70($inp), @XMM[8+7]
+	lea	0x80($inp), $inp
+	movdqa	@XMM[7], 0x70(%rsp)
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	pxor	@XMM[8+7], @XMM[7]
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	pxor	0x60(%rsp), @XMM[2]
+	movdqu	@XMM[7], 0x50($out)
+	pxor	0x70(%rsp), @XMM[5]
+	movdqu	@XMM[2], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# prepare next iteration tweak
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+
+	sub	\$0x80,$len
+	jnc	.Lxts_enc_loop
+
+.Lxts_enc_short:
+	add	\$0x80, $len
+	jz	.Lxts_enc_done
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+	cmp	\$`0x10*$i`,$len
+	je	.Lxts_enc_$i
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqa	@XMM[7], 0x70(%rsp)
+	lea	0x70($inp), $inp
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	pxor	0x60(%rsp), @XMM[2]
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	lea	0x70($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_6:
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x60($inp), $inp
+	pxor	@XMM[8+5], @XMM[5]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	lea	0x60($out), $out
+
+	movdqa	0x60(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_5:
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x50($inp), $inp
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	lea	0x50($out), $out
+
+	movdqa	0x50(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_4:
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x40($inp), $inp
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	lea	0x40($out), $out
+
+	movdqa	0x40(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_3:
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x30($inp), $inp
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	lea	0x30($out), $out
+
+	movdqa	0x30(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_2:
+	pxor	@XMM[8+0], @XMM[0]
+	lea	0x20($inp), $inp
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	lea	0x20($out), $out
+
+	movdqa	0x20(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_1:
+	pxor	@XMM[0], @XMM[8]
+	lea	0x10($inp), $inp
+	movdqa	@XMM[8], 0x20(%rbp)
+	lea	0x20(%rbp), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($key), $arg3
+	call	asm_AES_encrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[0]	# ^= tweak[]
+	#pxor	@XMM[8], @XMM[0]
+	#lea	0x80(%rsp), %rax	# pass key schedule
+	#mov	%edx, %r10d		# pass rounds
+	#call	_bsaes_encrypt8
+	#pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	lea	0x10($out), $out
+
+	movdqa	0x10(%rsp), @XMM[7]	# next iteration tweak
+
+.Lxts_enc_done:
+	and	\$15, %ebx
+	jz	.Lxts_enc_ret
+	mov	$out, %rdx
+
+.Lxts_enc_steal:
+	movzb	($inp), %eax
+	movzb	-16(%rdx), %ecx
+	lea	1($inp), $inp
+	mov	%al, -16(%rdx)
+	mov	%cl, 0(%rdx)
+	lea	1(%rdx), %rdx
+	sub	\$1,%ebx
+	jnz	.Lxts_enc_steal
+
+	movdqu	-16($out), @XMM[0]
+	lea	0x20(%rbp), $arg1
+	pxor	@XMM[7], @XMM[0]
+	lea	0x20(%rbp), $arg2
+	movdqa	@XMM[0], 0x20(%rbp)
+	lea	($key), $arg3
+	call	asm_AES_encrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[7]
+	movdqu	@XMM[7], -16($out)
+
+.Lxts_enc_ret:
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lxts_enc_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lxts_enc_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lxts_enc_epilogue:
+	ret
+.size	bsaes_xts_encrypt,.-bsaes_xts_encrypt
+
+.globl	bsaes_xts_decrypt
+.type	bsaes_xts_decrypt,\@abi-omnipotent
+.align	16
+bsaes_xts_decrypt:
+	mov	%rsp, %rax
+.Lxts_dec_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull key2
+	mov	0xa8(%rsp),$arg6	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lxts_dec_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+
+	lea	($arg6), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($arg5), $arg3
+	call	asm_AES_encrypt		# generate initial tweak
+
+	mov	240($key), %eax		# rounds
+	mov	$len, %rbx		# backup $len
+
+	mov	%eax, %edx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%edx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	(%rsp), %xmm7		# fix up round 0 key
+	movdqa	%xmm6, (%rax)		# save last round key
+	movdqa	%xmm7, (%rsp)
+
+	xor	%eax, %eax		# if ($len%16) len-=16;
+	and	\$-16, $len
+	test	\$15, %ebx
+	setnz	%al
+	shl	\$4, %rax
+	sub	%rax, $len
+
+	sub	\$0x80, %rsp		# place for tweak[8]
+	movdqa	0x20(%rbp), @XMM[7]	# initial tweak
+
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+
+	sub	\$0x80, $len
+	jc	.Lxts_dec_short
+	jmp	.Lxts_dec_loop
+
+.align	16
+.Lxts_dec_loop:
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqu	0x70($inp), @XMM[8+7]
+	lea	0x80($inp), $inp
+	movdqa	@XMM[7], 0x70(%rsp)
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	pxor	@XMM[8+7], @XMM[7]
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[2], 0x40($out)
+	pxor	0x60(%rsp), @XMM[3]
+	movdqu	@XMM[7], 0x50($out)
+	pxor	0x70(%rsp), @XMM[5]
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# prepare next iteration tweak
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+
+	sub	\$0x80,$len
+	jnc	.Lxts_dec_loop
+
+.Lxts_dec_short:
+	add	\$0x80, $len
+	jz	.Lxts_dec_done
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+	cmp	\$`0x10*$i`,$len
+	je	.Lxts_dec_$i
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqa	@XMM[7], 0x70(%rsp)
+	lea	0x70($inp), $inp
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[2], 0x40($out)
+	pxor	0x60(%rsp), @XMM[3]
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	lea	0x70($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_6:
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x60($inp), $inp
+	pxor	@XMM[8+5], @XMM[5]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	lea	0x60($out), $out
+
+	movdqa	0x60(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_5:
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x50($inp), $inp
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	lea	0x50($out), $out
+
+	movdqa	0x50(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_4:
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x40($inp), $inp
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	lea	0x40($out), $out
+
+	movdqa	0x40(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_3:
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x30($inp), $inp
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	lea	0x30($out), $out
+
+	movdqa	0x30(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_2:
+	pxor	@XMM[8+0], @XMM[0]
+	lea	0x20($inp), $inp
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	lea	0x20($out), $out
+
+	movdqa	0x20(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_1:
+	pxor	@XMM[0], @XMM[8]
+	lea	0x10($inp), $inp
+	movdqa	@XMM[8], 0x20(%rbp)
+	lea	0x20(%rbp), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[0]	# ^= tweak[]
+	#pxor	@XMM[8], @XMM[0]
+	#lea	0x80(%rsp), %rax	# pass key schedule
+	#mov	%edx, %r10d		# pass rounds
+	#call	_bsaes_decrypt8
+	#pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	lea	0x10($out), $out
+
+	movdqa	0x10(%rsp), @XMM[7]	# next iteration tweak
+
+.Lxts_dec_done:
+	and	\$15, %ebx
+	jz	.Lxts_dec_ret
+
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp
+	pshufd	\$0x13, $twtmp, $twres
+	movdqa	@XMM[7], @XMM[6]
+	paddq	@XMM[7], @XMM[7]	# psllq 1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	movdqu	($inp), @XMM[0]
+	pxor	$twres, @XMM[7]
+
+	lea	0x20(%rbp), $arg1
+	pxor	@XMM[7], @XMM[0]
+	lea	0x20(%rbp), $arg2
+	movdqa	@XMM[0], 0x20(%rbp)
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[7]
+	mov	$out, %rdx
+	movdqu	@XMM[7], ($out)
+
+.Lxts_dec_steal:
+	movzb	16($inp), %eax
+	movzb	(%rdx), %ecx
+	lea	1($inp), $inp
+	mov	%al, (%rdx)
+	mov	%cl, 16(%rdx)
+	lea	1(%rdx), %rdx
+	sub	\$1,%ebx
+	jnz	.Lxts_dec_steal
+
+	movdqu	($out), @XMM[0]
+	lea	0x20(%rbp), $arg1
+	pxor	@XMM[6], @XMM[0]
+	lea	0x20(%rbp), $arg2
+	movdqa	@XMM[0], 0x20(%rbp)
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[6]
+	movdqu	@XMM[6], ($out)
+
+.Lxts_dec_ret:
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lxts_dec_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lxts_dec_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lxts_dec_epilogue:
+	ret
+.size	bsaes_xts_decrypt,.-bsaes_xts_decrypt
+___
+}
+$code.=<<___;
+.type	_bsaes_const,\@object
+.align	64
+_bsaes_const:
+.LM0ISR:	# InvShiftRows constants
+	.quad	0x0a0e0206070b0f03, 0x0004080c0d010509
+.LISRM0:
+	.quad	0x01040b0e0205080f, 0x0306090c00070a0d
+.LISR:
+	.quad	0x0504070602010003, 0x0f0e0d0c080b0a09
+.LBS0:		# bit-slice constants
+	.quad	0x5555555555555555, 0x5555555555555555
+.LBS1:
+	.quad	0x3333333333333333, 0x3333333333333333
+.LBS2:
+	.quad	0x0f0f0f0f0f0f0f0f, 0x0f0f0f0f0f0f0f0f
+.LSR:		# shiftrows constants
+	.quad	0x0504070600030201, 0x0f0e0d0c0a09080b
+.LSRM0:
+	.quad	0x0304090e00050a0f, 0x01060b0c0207080d
+.LM0SR:
+	.quad	0x0a0e02060f03070b, 0x0004080c05090d01
+.LSWPUP:	# byte-swap upper dword
+	.quad	0x0706050403020100, 0x0c0d0e0f0b0a0908
+.LSWPUPM0SR:
+	.quad	0x0a0d02060c03070b, 0x0004080f05090e01
+.LADD1:		# counter increment constants
+	.quad	0x0000000000000000, 0x0000000100000000
+.LADD2:
+	.quad	0x0000000000000000, 0x0000000200000000
+.LADD3:
+	.quad	0x0000000000000000, 0x0000000300000000
+.LADD4:
+	.quad	0x0000000000000000, 0x0000000400000000
+.LADD5:
+	.quad	0x0000000000000000, 0x0000000500000000
+.LADD6:
+	.quad	0x0000000000000000, 0x0000000600000000
+.LADD7:
+	.quad	0x0000000000000000, 0x0000000700000000
+.LADD8:
+	.quad	0x0000000000000000, 0x0000000800000000
+.Lxts_magic:
+	.long	0x87,0,1,0
+.Lmasks:
+	.quad	0x0101010101010101, 0x0101010101010101
+	.quad	0x0202020202020202, 0x0202020202020202
+	.quad	0x0404040404040404, 0x0404040404040404
+	.quad	0x0808080808080808, 0x0808080808080808
+.LM0:
+	.quad	0x02060a0e03070b0f, 0x0004080c0105090d
+.L63:
+	.quad	0x6363636363636363, 0x6363636363636363
+.asciz	"Bit-sliced AES for x86_64/SSSE3, Emilia Käsper, Peter Schwabe, Andy Polyakov"
+.align	64
+.size	_bsaes_const,.-_bsaes_const
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	mov	160($context),%rax	# pull context->Rbp
+
+	lea	0x40(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0xa0(%rax),%rax		# adjust stack pointer
+
+	mov	0x70(%rax),%rbp
+	mov	0x68(%rax),%rbx
+	mov	0x60(%rax),%r12
+	mov	0x58(%rax),%r13
+	mov	0x50(%rax),%r14
+	mov	0x48(%rax),%r15
+	lea	0x78(%rax),%rax		# adjust stack pointer
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	%rax,152($context)	# restore context->Rsp
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+___
+$code.=<<___ if ($ecb);
+	.rva	.Lecb_enc_prologue
+	.rva	.Lecb_enc_epilogue
+	.rva	.Lecb_enc_info
+
+	.rva	.Lecb_dec_prologue
+	.rva	.Lecb_dec_epilogue
+	.rva	.Lecb_dec_info
+___
+$code.=<<___;
+	.rva	.Lcbc_dec_prologue
+	.rva	.Lcbc_dec_epilogue
+	.rva	.Lcbc_dec_info
+
+	.rva	.Lctr_enc_prologue
+	.rva	.Lctr_enc_epilogue
+	.rva	.Lctr_enc_info
+
+	.rva	.Lxts_enc_prologue
+	.rva	.Lxts_enc_epilogue
+	.rva	.Lxts_enc_info
+
+	.rva	.Lxts_dec_prologue
+	.rva	.Lxts_dec_epilogue
+	.rva	.Lxts_dec_info
+
+.section	.xdata
+.align	8
+___
+$code.=<<___ if ($ecb);
+.Lecb_enc_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lecb_enc_body,.Lecb_enc_epilogue	# HandlerData[]
+.Lecb_dec_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lecb_dec_body,.Lecb_dec_epilogue	# HandlerData[]
+___
+$code.=<<___;
+.Lcbc_dec_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lcbc_dec_body,.Lcbc_dec_epilogue	# HandlerData[]
+.Lctr_enc_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lctr_enc_body,.Lctr_enc_epilogue	# HandlerData[]
+.Lxts_enc_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lxts_enc_body,.Lxts_enc_epilogue	# HandlerData[]
+.Lxts_dec_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lxts_dec_body,.Lxts_dec_epilogue	# HandlerData[]
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/vpaes-x86.pl b/crypto/aes/asm/vpaes-x86.pl
new file mode 100644
index 0000000..bacf42c
--- /dev/null
+++ b/crypto/aes/asm/vpaes-x86.pl
@@ -0,0 +1,902 @@
+#!/usr/bin/env perl
+
+######################################################################
+## Constant-time SSSE3 AES core implementation.
+## version 0.1
+##
+## By Mike Hamburg (Stanford University), 2009
+## Public domain.
+##
+## For details see http://shiftleft.org/papers/vector_aes/ and
+## http://crypto.stanford.edu/vpaes/.
+
+######################################################################
+# September 2011.
+#
+# Port vpaes-x86_64.pl as 32-bit "almost" drop-in replacement for
+# aes-586.pl. "Almost" refers to the fact that AES_cbc_encrypt
+# doesn't handle partial vectors (doesn't have to if called from
+# EVP only). "Drop-in" implies that this module doesn't share key
+# schedule structure with the original nor does it make assumption
+# about its alignment...
+#
+# Performance summary. aes-586.pl column lists large-block CBC
+# encrypt/decrypt/with-hyper-threading-off(*) results in cycles per
+# byte processed with 128-bit key, and vpaes-x86.pl column - [also
+# large-block CBC] encrypt/decrypt.
+#
+#		aes-586.pl		vpaes-x86.pl
+#
+# Core 2(**)	28.1/41.4/18.3		21.9/25.2(***)
+# Nehalem	27.9/40.4/18.1		10.2/11.9
+# Atom		70.7/92.1/60.1		61.1/75.4(***)
+#
+# (*)	"Hyper-threading" in the context refers rather to cache shared
+#	among multiple cores, than to specifically Intel HTT. As vast
+#	majority of contemporary cores share cache, slower code path
+#	is common place. In other words "with-hyper-threading-off"
+#	results are presented mostly for reference purposes.
+#
+# (**)	"Core 2" refers to initial 65nm design, a.k.a. Conroe.
+#
+# (***)	Less impressive improvement on Core 2 and Atom is due to slow
+#	pshufb,	yet it's respectable +28%/64%  improvement on Core 2
+#	and +15% on Atom (as implied, over "hyper-threading-safe"
+#	code path).
+#
+#						<appro@openssl.org>
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"vpaes-x86.pl",$x86only = $ARGV[$#ARGV] eq "386");
+
+$PREFIX="vpaes";
+
+my  ($round, $base, $magic, $key, $const, $inp, $out)=
+    ("eax",  "ebx", "ecx",  "edx","ebp",  "esi","edi");
+
+&static_label("_vpaes_consts");
+&static_label("_vpaes_schedule_low_round");
+
+&set_label("_vpaes_consts",64);
+$k_inv=-0x30;		# inv, inva
+	&data_word(0x0D080180,0x0E05060F,0x0A0B0C02,0x04070309);
+	&data_word(0x0F0B0780,0x01040A06,0x02050809,0x030D0E0C);
+
+$k_s0F=-0x10;		# s0F
+	&data_word(0x0F0F0F0F,0x0F0F0F0F,0x0F0F0F0F,0x0F0F0F0F);
+
+$k_ipt=0x00;		# input transform (lo, hi)
+	&data_word(0x5A2A7000,0xC2B2E898,0x52227808,0xCABAE090);
+	&data_word(0x317C4D00,0x4C01307D,0xB0FDCC81,0xCD80B1FC);
+
+$k_sb1=0x20;		# sb1u, sb1t
+	&data_word(0xCB503E00,0xB19BE18F,0x142AF544,0xA5DF7A6E);
+	&data_word(0xFAE22300,0x3618D415,0x0D2ED9EF,0x3BF7CCC1);
+$k_sb2=0x40;		# sb2u, sb2t
+	&data_word(0x0B712400,0xE27A93C6,0xBC982FCD,0x5EB7E955);
+	&data_word(0x0AE12900,0x69EB8840,0xAB82234A,0xC2A163C8);
+$k_sbo=0x60;		# sbou, sbot
+	&data_word(0x6FBDC700,0xD0D26D17,0xC502A878,0x15AABF7A);
+	&data_word(0x5FBB6A00,0xCFE474A5,0x412B35FA,0x8E1E90D1);
+
+$k_mc_forward=0x80;	# mc_forward
+	&data_word(0x00030201,0x04070605,0x080B0A09,0x0C0F0E0D);
+	&data_word(0x04070605,0x080B0A09,0x0C0F0E0D,0x00030201);
+	&data_word(0x080B0A09,0x0C0F0E0D,0x00030201,0x04070605);
+	&data_word(0x0C0F0E0D,0x00030201,0x04070605,0x080B0A09);
+
+$k_mc_backward=0xc0;	# mc_backward
+	&data_word(0x02010003,0x06050407,0x0A09080B,0x0E0D0C0F);
+	&data_word(0x0E0D0C0F,0x02010003,0x06050407,0x0A09080B);
+	&data_word(0x0A09080B,0x0E0D0C0F,0x02010003,0x06050407);
+	&data_word(0x06050407,0x0A09080B,0x0E0D0C0F,0x02010003);
+
+$k_sr=0x100;		# sr
+	&data_word(0x03020100,0x07060504,0x0B0A0908,0x0F0E0D0C);
+	&data_word(0x0F0A0500,0x030E0904,0x07020D08,0x0B06010C);
+	&data_word(0x0B020900,0x0F060D04,0x030A0108,0x070E050C);
+	&data_word(0x070A0D00,0x0B0E0104,0x0F020508,0x0306090C);
+
+$k_rcon=0x140;		# rcon
+	&data_word(0xAF9DEEB6,0x1F8391B9,0x4D7C7D81,0x702A9808);
+
+$k_s63=0x150;		# s63: all equal to 0x63 transformed
+	&data_word(0x5B5B5B5B,0x5B5B5B5B,0x5B5B5B5B,0x5B5B5B5B);
+
+$k_opt=0x160;		# output transform
+	&data_word(0xD6B66000,0xFF9F4929,0xDEBE6808,0xF7974121);
+	&data_word(0x50BCEC00,0x01EDBD51,0xB05C0CE0,0xE10D5DB1);
+
+$k_deskew=0x180;	# deskew tables: inverts the sbox's "skew"
+	&data_word(0x47A4E300,0x07E4A340,0x5DBEF91A,0x1DFEB95A);
+	&data_word(0x83EA6900,0x5F36B5DC,0xF49D1E77,0x2841C2AB);
+##
+##  Decryption stuff
+##  Key schedule constants
+##
+$k_dksd=0x1a0;		# decryption key schedule: invskew x*D
+	&data_word(0xA3E44700,0xFEB91A5D,0x5A1DBEF9,0x0740E3A4);
+	&data_word(0xB5368300,0x41C277F4,0xAB289D1E,0x5FDC69EA);
+$k_dksb=0x1c0;		# decryption key schedule: invskew x*B
+	&data_word(0x8550D500,0x9A4FCA1F,0x1CC94C99,0x03D65386);
+	&data_word(0xB6FC4A00,0x115BEDA7,0x7E3482C8,0xD993256F);
+$k_dkse=0x1e0;		# decryption key schedule: invskew x*E + 0x63
+	&data_word(0x1FC9D600,0xD5031CCA,0x994F5086,0x53859A4C);
+	&data_word(0x4FDC7BE8,0xA2319605,0x20B31487,0xCD5EF96A);
+$k_dks9=0x200;		# decryption key schedule: invskew x*9
+	&data_word(0x7ED9A700,0xB6116FC8,0x82255BFC,0x4AED9334);
+	&data_word(0x27143300,0x45765162,0xE9DAFDCE,0x8BB89FAC);
+
+##
+##  Decryption stuff
+##  Round function constants
+##
+$k_dipt=0x220;		# decryption input transform
+	&data_word(0x0B545F00,0x0F505B04,0x114E451A,0x154A411E);
+	&data_word(0x60056500,0x86E383E6,0xF491F194,0x12771772);
+
+$k_dsb9=0x240;		# decryption sbox output *9*u, *9*t
+	&data_word(0x9A86D600,0x851C0353,0x4F994CC9,0xCAD51F50);
+	&data_word(0xECD74900,0xC03B1789,0xB2FBA565,0x725E2C9E);
+$k_dsbd=0x260;		# decryption sbox output *D*u, *D*t
+	&data_word(0xE6B1A200,0x7D57CCDF,0x882A4439,0xF56E9B13);
+	&data_word(0x24C6CB00,0x3CE2FAF7,0x15DEEFD3,0x2931180D);
+$k_dsbb=0x280;		# decryption sbox output *B*u, *B*t
+	&data_word(0x96B44200,0xD0226492,0xB0F2D404,0x602646F6);
+	&data_word(0xCD596700,0xC19498A6,0x3255AA6B,0xF3FF0C3E);
+$k_dsbe=0x2a0;		# decryption sbox output *E*u, *E*t
+	&data_word(0x26D4D000,0x46F29296,0x64B4F6B0,0x22426004);
+	&data_word(0xFFAAC100,0x0C55A6CD,0x98593E32,0x9467F36B);
+$k_dsbo=0x2c0;		# decryption sbox final output
+	&data_word(0x7EF94000,0x1387EA53,0xD4943E2D,0xC7AA6DB9);
+	&data_word(0x93441D00,0x12D7560F,0xD8C58E9C,0xCA4B8159);
+&asciz	("Vector Permutation AES for x86/SSSE3, Mike Hamburg (Stanford University)");
+&align	(64);
+
+&function_begin_B("_vpaes_preheat");
+	&add	($const,&DWP(0,"esp"));
+	&movdqa	("xmm7",&QWP($k_inv,$const));
+	&movdqa	("xmm6",&QWP($k_s0F,$const));
+	&ret	();
+&function_end_B("_vpaes_preheat");
+
+##
+##  _aes_encrypt_core
+##
+##  AES-encrypt %xmm0.
+##
+##  Inputs:
+##     %xmm0 = input
+##     %xmm6-%xmm7 as in _vpaes_preheat
+##    (%edx) = scheduled keys
+##
+##  Output in %xmm0
+##  Clobbers  %xmm1-%xmm5, %eax, %ebx, %ecx, %edx
+##
+##
+&function_begin_B("_vpaes_encrypt_core");
+	&mov	($magic,16);
+	&mov	($round,&DWP(240,$key));
+	&movdqa	("xmm1","xmm6")
+	&movdqa	("xmm2",&QWP($k_ipt,$const));
+	&pandn	("xmm1","xmm0");
+	&pand	("xmm0","xmm6");
+	&movdqu	("xmm5",&QWP(0,$key));
+	&pshufb	("xmm2","xmm0");
+	&movdqa	("xmm0",&QWP($k_ipt+16,$const));
+	&pxor	("xmm2","xmm5");
+	&psrld	("xmm1",4);
+	&add	($key,16);
+	&pshufb	("xmm0","xmm1");
+	&lea	($base,&DWP($k_mc_backward,$const));
+	&pxor	("xmm0","xmm2");
+	&jmp	(&label("enc_entry"));
+
+
+&set_label("enc_loop",16);
+	# middle of middle round
+	&movdqa	("xmm4",&QWP($k_sb1,$const));	# 4 : sb1u
+	&movdqa	("xmm0",&QWP($k_sb1+16,$const));# 0 : sb1t
+	&pshufb	("xmm4","xmm2");		# 4 = sb1u
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&pxor	("xmm4","xmm5");		# 4 = sb1u + k
+	&movdqa	("xmm5",&QWP($k_sb2,$const));	# 4 : sb2u
+	&pxor	("xmm0","xmm4");		# 0 = A
+	&movdqa	("xmm1",&QWP(-0x40,$base,$magic));# .Lk_mc_forward[]
+	&pshufb	("xmm5","xmm2");		# 4 = sb2u
+	&movdqa	("xmm2",&QWP($k_sb2+16,$const));# 2 : sb2t
+	&movdqa	("xmm4",&QWP(0,$base,$magic));	# .Lk_mc_backward[]
+	&pshufb	("xmm2","xmm3");		# 2 = sb2t
+	&movdqa	("xmm3","xmm0");		# 3 = A
+	&pxor	("xmm2","xmm5");		# 2 = 2A
+	&pshufb	("xmm0","xmm1");		# 0 = B
+	&add	($key,16);			# next key
+	&pxor	("xmm0","xmm2");		# 0 = 2A+B
+	&pshufb	("xmm3","xmm4");		# 3 = D
+	&add	($magic,16);			# next mc
+	&pxor	("xmm3","xmm0");		# 3 = 2A+B+D
+	&pshufb	("xmm0","xmm1");		# 0 = 2B+C
+	&and	($magic,0x30);			# ... mod 4
+	&sub	($round,1);			# nr--
+	&pxor	("xmm0","xmm3");		# 0 = 2A+3B+C+D
+
+&set_label("enc_entry");
+	# top of round
+	&movdqa	("xmm1","xmm6");		# 1 : i
+	&movdqa	("xmm5",&QWP($k_inv+16,$const));# 2 : a/k
+	&pandn	("xmm1","xmm0");		# 1 = i<<4
+	&psrld	("xmm1",4);			# 1 = i
+	&pand	("xmm0","xmm6");		# 0 = k
+	&pshufb	("xmm5","xmm0");		# 2 = a/k
+	&movdqa	("xmm3","xmm7");		# 3 : 1/i
+	&pxor	("xmm0","xmm1");		# 0 = j
+	&pshufb	("xmm3","xmm1");		# 3 = 1/i
+	&movdqa	("xmm4","xmm7");		# 4 : 1/j
+	&pxor	("xmm3","xmm5");		# 3 = iak = 1/i + a/k
+	&pshufb	("xmm4","xmm0");		# 4 = 1/j
+	&movdqa	("xmm2","xmm7");		# 2 : 1/iak
+	&pxor	("xmm4","xmm5");		# 4 = jak = 1/j + a/k
+	&pshufb	("xmm2","xmm3");		# 2 = 1/iak
+	&movdqa	("xmm3","xmm7");		# 3 : 1/jak
+	&pxor	("xmm2","xmm0");		# 2 = io
+	&pshufb	("xmm3","xmm4");		# 3 = 1/jak
+	&movdqu	("xmm5",&QWP(0,$key));
+	&pxor	("xmm3","xmm1");		# 3 = jo
+	&jnz	(&label("enc_loop"));
+
+	# middle of last round
+	&movdqa	("xmm4",&QWP($k_sbo,$const));	# 3 : sbou      .Lk_sbo
+	&movdqa	("xmm0",&QWP($k_sbo+16,$const));# 3 : sbot      .Lk_sbo+16
+	&pshufb	("xmm4","xmm2");		# 4 = sbou
+	&pxor	("xmm4","xmm5");		# 4 = sb1u + k
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&movdqa	("xmm1",&QWP(0x40,$base,$magic));# .Lk_sr[]
+	&pxor	("xmm0","xmm4");		# 0 = A
+	&pshufb	("xmm0","xmm1");
+	&ret	();
+&function_end_B("_vpaes_encrypt_core");
+
+##
+##  Decryption core
+##
+##  Same API as encryption core.
+##
+&function_begin_B("_vpaes_decrypt_core");
+	&lea	($base,&DWP($k_dsbd,$const));
+	&mov	($round,&DWP(240,$key));
+	&movdqa	("xmm1","xmm6");
+	&movdqa	("xmm2",&QWP($k_dipt-$k_dsbd,$base));
+	&pandn	("xmm1","xmm0");
+	&mov	($magic,$round);
+	&psrld	("xmm1",4)
+	&movdqu	("xmm5",&QWP(0,$key));
+	&shl	($magic,4);
+	&pand	("xmm0","xmm6");
+	&pshufb	("xmm2","xmm0");
+	&movdqa	("xmm0",&QWP($k_dipt-$k_dsbd+16,$base));
+	&xor	($magic,0x30);
+	&pshufb	("xmm0","xmm1");
+	&and	($magic,0x30);
+	&pxor	("xmm2","xmm5");
+	&movdqa	("xmm5",&QWP($k_mc_forward+48,$const));
+	&pxor	("xmm0","xmm2");
+	&add	($key,16);
+	&lea	($magic,&DWP($k_sr-$k_dsbd,$base,$magic));
+	&jmp	(&label("dec_entry"));
+
+&set_label("dec_loop",16);
+##
+##  Inverse mix columns
+##
+	&movdqa	("xmm4",&QWP(-0x20,$base));	# 4 : sb9u
+	&movdqa	("xmm1",&QWP(-0x10,$base));	# 0 : sb9t
+	&pshufb	("xmm4","xmm2");		# 4 = sb9u
+	&pshufb	("xmm1","xmm3");		# 0 = sb9t
+	&pxor	("xmm0","xmm4");
+	&movdqa	("xmm4",&QWP(0,$base));		# 4 : sbdu
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&movdqa	("xmm1",&QWP(0x10,$base));	# 0 : sbdt
+
+	&pshufb	("xmm4","xmm2");		# 4 = sbdu
+	&pshufb	("xmm0","xmm5");		# MC ch
+	&pshufb	("xmm1","xmm3");		# 0 = sbdt
+	&pxor	("xmm0","xmm4");		# 4 = ch
+	&movdqa	("xmm4",&QWP(0x20,$base));	# 4 : sbbu
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&movdqa	("xmm1",&QWP(0x30,$base));	# 0 : sbbt
+
+	&pshufb	("xmm4","xmm2");		# 4 = sbbu
+	&pshufb	("xmm0","xmm5");		# MC ch
+	&pshufb	("xmm1","xmm3");		# 0 = sbbt
+	&pxor	("xmm0","xmm4");		# 4 = ch
+	&movdqa	("xmm4",&QWP(0x40,$base));	# 4 : sbeu
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&movdqa	("xmm1",&QWP(0x50,$base));	# 0 : sbet
+
+	&pshufb	("xmm4","xmm2");		# 4 = sbeu
+	&pshufb	("xmm0","xmm5");		# MC ch
+	&pshufb	("xmm1","xmm3");		# 0 = sbet
+	&pxor	("xmm0","xmm4");		# 4 = ch
+	&add	($key,16);			# next round key
+	&palignr("xmm5","xmm5",12);
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&sub	($round,1);			# nr--
+
+&set_label("dec_entry");
+	# top of round
+	&movdqa	("xmm1","xmm6");		# 1 : i
+	&movdqa	("xmm2",&QWP($k_inv+16,$const));# 2 : a/k
+	&pandn	("xmm1","xmm0");		# 1 = i<<4
+	&pand	("xmm0","xmm6");		# 0 = k
+	&psrld	("xmm1",4);			# 1 = i
+	&pshufb	("xmm2","xmm0");		# 2 = a/k
+	&movdqa	("xmm3","xmm7");		# 3 : 1/i
+	&pxor	("xmm0","xmm1");		# 0 = j
+	&pshufb	("xmm3","xmm1");		# 3 = 1/i
+	&movdqa	("xmm4","xmm7");		# 4 : 1/j
+	&pxor	("xmm3","xmm2");		# 3 = iak = 1/i + a/k
+	&pshufb	("xmm4","xmm0");		# 4 = 1/j
+	&pxor	("xmm4","xmm2");		# 4 = jak = 1/j + a/k
+	&movdqa	("xmm2","xmm7");		# 2 : 1/iak
+	&pshufb	("xmm2","xmm3");		# 2 = 1/iak
+	&movdqa	("xmm3","xmm7");		# 3 : 1/jak
+	&pxor	("xmm2","xmm0");		# 2 = io
+	&pshufb	("xmm3","xmm4");		# 3 = 1/jak
+	&movdqu	("xmm0",&QWP(0,$key));
+	&pxor	("xmm3","xmm1");		# 3 = jo
+	&jnz	(&label("dec_loop"));
+
+	# middle of last round
+	&movdqa	("xmm4",&QWP(0x60,$base));	# 3 : sbou
+	&pshufb	("xmm4","xmm2");		# 4 = sbou
+	&pxor	("xmm4","xmm0");		# 4 = sb1u + k
+	&movdqa	("xmm0",&QWP(0x70,$base));	# 0 : sbot
+	&movdqa	("xmm2",&QWP(0,$magic));
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&pxor	("xmm0","xmm4");		# 0 = A
+	&pshufb	("xmm0","xmm2");
+	&ret	();
+&function_end_B("_vpaes_decrypt_core");
+
+########################################################
+##                                                    ##
+##                  AES key schedule                  ##
+##                                                    ##
+########################################################
+&function_begin_B("_vpaes_schedule_core");
+	&add	($const,&DWP(0,"esp"));
+	&movdqu	("xmm0",&QWP(0,$inp));		# load key (unaligned)
+	&movdqa	("xmm2",&QWP($k_rcon,$const));	# load rcon
+
+	# input transform
+	&movdqa	("xmm3","xmm0");
+	&lea	($base,&DWP($k_ipt,$const));
+	&movdqa	(&QWP(4,"esp"),"xmm2");		# xmm8
+	&call	("_vpaes_schedule_transform");
+	&movdqa	("xmm7","xmm0");
+
+	&test	($out,$out);
+	&jnz	(&label("schedule_am_decrypting"));
+
+	# encrypting, output zeroth round key after transform
+	&movdqu	(&QWP(0,$key),"xmm0");
+	&jmp	(&label("schedule_go"));
+
+&set_label("schedule_am_decrypting");
+	# decrypting, output zeroth round key after shiftrows
+	&movdqa	("xmm1",&QWP($k_sr,$const,$magic));
+	&pshufb	("xmm3","xmm1");
+	&movdqu	(&QWP(0,$key),"xmm3");
+	&xor	($magic,0x30);
+
+&set_label("schedule_go");
+	&cmp	($round,192);
+	&ja	(&label("schedule_256"));
+	&je	(&label("schedule_192"));
+	# 128: fall though
+
+##
+##  .schedule_128
+##
+##  128-bit specific part of key schedule.
+##
+##  This schedule is really simple, because all its parts
+##  are accomplished by the subroutines.
+##
+&set_label("schedule_128");
+	&mov	($round,10);
+
+&set_label("loop_schedule_128");
+	&call	("_vpaes_schedule_round");
+	&dec	($round);
+	&jz	(&label("schedule_mangle_last"));
+	&call	("_vpaes_schedule_mangle");	# write output
+	&jmp	(&label("loop_schedule_128"));
+
+##
+##  .aes_schedule_192
+##
+##  192-bit specific part of key schedule.
+##
+##  The main body of this schedule is the same as the 128-bit
+##  schedule, but with more smearing.  The long, high side is
+##  stored in %xmm7 as before, and the short, low side is in
+##  the high bits of %xmm6.
+##
+##  This schedule is somewhat nastier, however, because each
+##  round produces 192 bits of key material, or 1.5 round keys.
+##  Therefore, on each cycle we do 2 rounds and produce 3 round
+##  keys.
+##
+&set_label("schedule_192",16);
+	&movdqu	("xmm0",&QWP(8,$inp));		# load key part 2 (very unaligned)
+	&call	("_vpaes_schedule_transform");	# input transform	
+	&movdqa	("xmm6","xmm0");		# save short part
+	&pxor	("xmm4","xmm4");		# clear 4
+	&movhlps("xmm6","xmm4");		# clobber low side with zeros
+	&mov	($round,4);
+
+&set_label("loop_schedule_192");
+	&call	("_vpaes_schedule_round");
+	&palignr("xmm0","xmm6",8);
+	&call	("_vpaes_schedule_mangle");	# save key n
+	&call	("_vpaes_schedule_192_smear");
+	&call	("_vpaes_schedule_mangle");	# save key n+1
+	&call	("_vpaes_schedule_round");
+	&dec	($round);
+	&jz	(&label("schedule_mangle_last"));
+	&call	("_vpaes_schedule_mangle");	# save key n+2
+	&call	("_vpaes_schedule_192_smear");
+	&jmp	(&label("loop_schedule_192"));
+
+##
+##  .aes_schedule_256
+##
+##  256-bit specific part of key schedule.
+##
+##  The structure here is very similar to the 128-bit
+##  schedule, but with an additional "low side" in
+##  %xmm6.  The low side's rounds are the same as the
+##  high side's, except no rcon and no rotation.
+##
+&set_label("schedule_256",16);
+	&movdqu	("xmm0",&QWP(16,$inp));		# load key part 2 (unaligned)
+	&call	("_vpaes_schedule_transform");	# input transform	
+	&mov	($round,7);
+
+&set_label("loop_schedule_256");
+	&call	("_vpaes_schedule_mangle");	# output low result
+	&movdqa	("xmm6","xmm0");		# save cur_lo in xmm6
+
+	# high round
+	&call	("_vpaes_schedule_round");
+	&dec	($round);
+	&jz	(&label("schedule_mangle_last"));
+	&call	("_vpaes_schedule_mangle");	
+
+	# low round. swap xmm7 and xmm6
+	&pshufd	("xmm0","xmm0",0xFF);
+	&movdqa	(&QWP(20,"esp"),"xmm7");
+	&movdqa	("xmm7","xmm6");
+	&call	("_vpaes_schedule_low_round");
+	&movdqa	("xmm7",&QWP(20,"esp"));
+
+	&jmp	(&label("loop_schedule_256"));
+
+##
+##  .aes_schedule_mangle_last
+##
+##  Mangler for last round of key schedule
+##  Mangles %xmm0
+##    when encrypting, outputs out(%xmm0) ^ 63
+##    when decrypting, outputs unskew(%xmm0)
+##
+##  Always called right before return... jumps to cleanup and exits
+##
+&set_label("schedule_mangle_last",16);
+	# schedule last round key from xmm0
+	&lea	($base,&DWP($k_deskew,$const));
+	&test	($out,$out);
+	&jnz	(&label("schedule_mangle_last_dec"));
+
+	# encrypting
+	&movdqa	("xmm1",&QWP($k_sr,$const,$magic));
+	&pshufb	("xmm0","xmm1");		# output permute
+	&lea	($base,&DWP($k_opt,$const));	# prepare to output transform
+	&add	($key,32);
+
+&set_label("schedule_mangle_last_dec");
+	&add	($key,-16);
+	&pxor	("xmm0",&QWP($k_s63,$const));
+	&call	("_vpaes_schedule_transform");	# output transform
+	&movdqu	(&QWP(0,$key),"xmm0");		# save last key
+
+	# cleanup
+	&pxor	("xmm0","xmm0");
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&pxor	("xmm5","xmm5");
+	&pxor	("xmm6","xmm6");
+	&pxor	("xmm7","xmm7");
+	&ret	();
+&function_end_B("_vpaes_schedule_core");
+
+##
+##  .aes_schedule_192_smear
+##
+##  Smear the short, low side in the 192-bit key schedule.
+##
+##  Inputs:
+##    %xmm7: high side, b  a  x  y
+##    %xmm6:  low side, d  c  0  0
+##    %xmm13: 0
+##
+##  Outputs:
+##    %xmm6: b+c+d  b+c  0  0
+##    %xmm0: b+c+d  b+c  b  a
+##
+&function_begin_B("_vpaes_schedule_192_smear");
+	&pshufd	("xmm1","xmm6",0x80);		# d c 0 0 -> c 0 0 0
+	&pshufd	("xmm0","xmm7",0xFE);		# b a _ _ -> b b b a
+	&pxor	("xmm6","xmm1");		# -> c+d c 0 0
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm6","xmm0");		# -> b+c+d b+c b a
+	&movdqa	("xmm0","xmm6");
+	&movhlps("xmm6","xmm1");		# clobber low side with zeros
+	&ret	();
+&function_end_B("_vpaes_schedule_192_smear");
+
+##
+##  .aes_schedule_round
+##
+##  Runs one main round of the key schedule on %xmm0, %xmm7
+##
+##  Specifically, runs subbytes on the high dword of %xmm0
+##  then rotates it by one byte and xors into the low dword of
+##  %xmm7.
+##
+##  Adds rcon from low byte of %xmm8, then rotates %xmm8 for
+##  next rcon.
+##
+##  Smears the dwords of %xmm7 by xoring the low into the
+##  second low, result into third, result into highest.
+##
+##  Returns results in %xmm7 = %xmm0.
+##  Clobbers %xmm1-%xmm5.
+##
+&function_begin_B("_vpaes_schedule_round");
+	# extract rcon from xmm8
+	&movdqa	("xmm2",&QWP(8,"esp"));		# xmm8
+	&pxor	("xmm1","xmm1");
+	&palignr("xmm1","xmm2",15);
+	&palignr("xmm2","xmm2",15);
+	&pxor	("xmm7","xmm1");
+
+	# rotate
+	&pshufd	("xmm0","xmm0",0xFF);
+	&palignr("xmm0","xmm0",1);
+
+	# fall through...
+	&movdqa	(&QWP(8,"esp"),"xmm2");		# xmm8
+
+	# low round: same as high round, but no rotation and no rcon.
+&set_label("_vpaes_schedule_low_round");
+	# smear xmm7
+	&movdqa	("xmm1","xmm7");
+	&pslldq	("xmm7",4);
+	&pxor	("xmm7","xmm1");
+	&movdqa	("xmm1","xmm7");
+	&pslldq	("xmm7",8);
+	&pxor	("xmm7","xmm1");
+	&pxor	("xmm7",&QWP($k_s63,$const));
+
+	# subbyte
+	&movdqa	("xmm4",&QWP($k_s0F,$const));
+	&movdqa	("xmm5",&QWP($k_inv,$const));	# 4 : 1/j
+	&movdqa	("xmm1","xmm4");	
+	&pandn	("xmm1","xmm0");
+	&psrld	("xmm1",4);			# 1 = i
+	&pand	("xmm0","xmm4");		# 0 = k
+	&movdqa	("xmm2",&QWP($k_inv+16,$const));# 2 : a/k
+	&pshufb	("xmm2","xmm0");		# 2 = a/k
+	&pxor	("xmm0","xmm1");		# 0 = j
+	&movdqa	("xmm3","xmm5");		# 3 : 1/i
+	&pshufb	("xmm3","xmm1");		# 3 = 1/i
+	&pxor	("xmm3","xmm2");		# 3 = iak = 1/i + a/k
+	&movdqa	("xmm4","xmm5");		# 4 : 1/j
+	&pshufb	("xmm4","xmm0");		# 4 = 1/j
+	&pxor	("xmm4","xmm2");		# 4 = jak = 1/j + a/k
+	&movdqa	("xmm2","xmm5");		# 2 : 1/iak
+	&pshufb	("xmm2","xmm3");		# 2 = 1/iak
+	&pxor	("xmm2","xmm0");		# 2 = io
+	&movdqa	("xmm3","xmm5");		# 3 : 1/jak
+	&pshufb	("xmm3","xmm4");		# 3 = 1/jak
+	&pxor	("xmm3","xmm1");		# 3 = jo
+	&movdqa	("xmm4",&QWP($k_sb1,$const));	# 4 : sbou
+	&pshufb	("xmm4","xmm2");		# 4 = sbou
+	&movdqa	("xmm0",&QWP($k_sb1+16,$const));# 0 : sbot
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&pxor	("xmm0","xmm4");		# 0 = sbox output
+
+	# add in smeared stuff
+	&pxor	("xmm0","xmm7");
+	&movdqa	("xmm7","xmm0");
+	&ret	();
+&function_end_B("_vpaes_schedule_round");
+
+##
+##  .aes_schedule_transform
+##
+##  Linear-transform %xmm0 according to tables at (%ebx)
+##
+##  Output in %xmm0
+##  Clobbers %xmm1, %xmm2
+##
+&function_begin_B("_vpaes_schedule_transform");
+	&movdqa	("xmm2",&QWP($k_s0F,$const));
+	&movdqa	("xmm1","xmm2");
+	&pandn	("xmm1","xmm0");
+	&psrld	("xmm1",4);
+	&pand	("xmm0","xmm2");
+	&movdqa	("xmm2",&QWP(0,$base));
+	&pshufb	("xmm2","xmm0");
+	&movdqa	("xmm0",&QWP(16,$base));
+	&pshufb	("xmm0","xmm1");
+	&pxor	("xmm0","xmm2");
+	&ret	();
+&function_end_B("_vpaes_schedule_transform");
+
+##
+##  .aes_schedule_mangle
+##
+##  Mangle xmm0 from (basis-transformed) standard version
+##  to our version.
+##
+##  On encrypt,
+##    xor with 0x63
+##    multiply by circulant 0,1,1,1
+##    apply shiftrows transform
+##
+##  On decrypt,
+##    xor with 0x63
+##    multiply by "inverse mixcolumns" circulant E,B,D,9
+##    deskew
+##    apply shiftrows transform
+##
+##
+##  Writes out to (%edx), and increments or decrements it
+##  Keeps track of round number mod 4 in %ecx
+##  Preserves xmm0
+##  Clobbers xmm1-xmm5
+##
+&function_begin_B("_vpaes_schedule_mangle");
+	&movdqa	("xmm4","xmm0");	# save xmm0 for later
+	&movdqa	("xmm5",&QWP($k_mc_forward,$const));
+	&test	($out,$out);
+	&jnz	(&label("schedule_mangle_dec"));
+
+	# encrypting
+	&add	($key,16);
+	&pxor	("xmm4",&QWP($k_s63,$const));
+	&pshufb	("xmm4","xmm5");
+	&movdqa	("xmm3","xmm4");
+	&pshufb	("xmm4","xmm5");
+	&pxor	("xmm3","xmm4");
+	&pshufb	("xmm4","xmm5");
+	&pxor	("xmm3","xmm4");
+
+	&jmp	(&label("schedule_mangle_both"));
+
+&set_label("schedule_mangle_dec",16);
+	# inverse mix columns
+	&movdqa	("xmm2",&QWP($k_s0F,$const));
+	&lea	($inp,&DWP($k_dksd,$const));
+	&movdqa	("xmm1","xmm2");
+	&pandn	("xmm1","xmm4");
+	&psrld	("xmm1",4);			# 1 = hi
+	&pand	("xmm4","xmm2");		# 4 = lo
+
+	&movdqa	("xmm2",&QWP(0,$inp));
+	&pshufb	("xmm2","xmm4");
+	&movdqa	("xmm3",&QWP(0x10,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+	&pshufb	("xmm3","xmm5");
+
+	&movdqa	("xmm2",&QWP(0x20,$inp));
+	&pshufb	("xmm2","xmm4");
+	&pxor	("xmm2","xmm3");
+	&movdqa	("xmm3",&QWP(0x30,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+	&pshufb	("xmm3","xmm5");
+
+	&movdqa	("xmm2",&QWP(0x40,$inp));
+	&pshufb	("xmm2","xmm4");
+	&pxor	("xmm2","xmm3");
+	&movdqa	("xmm3",&QWP(0x50,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+	&pshufb	("xmm3","xmm5");
+
+	&movdqa	("xmm2",&QWP(0x60,$inp));
+	&pshufb	("xmm2","xmm4");
+	&pxor	("xmm2","xmm3");
+	&movdqa	("xmm3",&QWP(0x70,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+
+	&add	($key,-16);
+
+&set_label("schedule_mangle_both");
+	&movdqa	("xmm1",&QWP($k_sr,$const,$magic));
+	&pshufb	("xmm3","xmm1");
+	&add	($magic,-16);
+	&and	($magic,0x30);
+	&movdqu	(&QWP(0,$key),"xmm3");
+	&ret	();
+&function_end_B("_vpaes_schedule_mangle");
+
+#
+# Interface to OpenSSL
+#
+&function_begin("${PREFIX}_set_encrypt_key");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($round,&wparam(1));		# bits
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&mov	($base,$round);
+	&shr	($base,5);
+	&add	($base,5);
+	&mov	(&DWP(240,$key),$base);		# AES_KEY->rounds = nbits/32+5;
+	&mov	($magic,0x30);
+	&mov	($out,0);
+
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_schedule_core");
+&set_label("pic_point");
+
+	&mov	("esp",&DWP(48,"esp"));
+	&xor	("eax","eax");
+&function_end("${PREFIX}_set_encrypt_key");
+
+&function_begin("${PREFIX}_set_decrypt_key");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($round,&wparam(1));		# bits
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&mov	($base,$round);
+	&shr	($base,5);
+	&add	($base,5);
+	&mov	(&DWP(240,$key),$base);	# AES_KEY->rounds = nbits/32+5;
+	&shl	($base,4);
+	&lea	($key,&DWP(16,$key,$base));
+
+	&mov	($out,1);
+	&mov	($magic,$round);
+	&shr	($magic,1);
+	&and	($magic,32);
+	&xor	($magic,32);			# nbist==192?0:32;
+
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_schedule_core");
+&set_label("pic_point");
+
+	&mov	("esp",&DWP(48,"esp"));
+	&xor	("eax","eax");
+&function_end("${PREFIX}_set_decrypt_key");
+
+&function_begin("${PREFIX}_encrypt");
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_preheat");
+&set_label("pic_point");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($out,&wparam(1));		# out
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&movdqu	("xmm0",&QWP(0,$inp));
+	&call	("_vpaes_encrypt_core");
+	&movdqu	(&QWP(0,$out),"xmm0");
+
+	&mov	("esp",&DWP(48,"esp"));
+&function_end("${PREFIX}_encrypt");
+
+&function_begin("${PREFIX}_decrypt");
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_preheat");
+&set_label("pic_point");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($out,&wparam(1));		# out
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&movdqu	("xmm0",&QWP(0,$inp));
+	&call	("_vpaes_decrypt_core");
+	&movdqu	(&QWP(0,$out),"xmm0");
+
+	&mov	("esp",&DWP(48,"esp"));
+&function_end("${PREFIX}_decrypt");
+
+&function_begin("${PREFIX}_cbc_encrypt");
+	&mov	($inp,&wparam(0));		# inp
+	&mov	($out,&wparam(1));		# out
+	&mov	($round,&wparam(2));		# len
+	&mov	($key,&wparam(3));		# key
+	&sub	($round,16);
+	&jc	(&label("cbc_abort"));
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($const,&wparam(4));		# ivp
+	&and	($base,-16);
+	&mov	($magic,&wparam(5));		# enc
+	&xchg	($base,"esp");			# alloca
+	&movdqu	("xmm1",&QWP(0,$const));	# load IV
+	&sub	($out,$inp);
+	&mov	(&DWP(48,"esp"),$base);
+
+	&mov	(&DWP(0,"esp"),$out);		# save out
+	&mov	(&DWP(4,"esp"),$key)		# save key
+	&mov	(&DWP(8,"esp"),$const);		# save ivp
+	&mov	($out,$round);			# $out works as $len
+
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_preheat");
+&set_label("pic_point");
+	&cmp	($magic,0);
+	&je	(&label("cbc_dec_loop"));
+	&jmp	(&label("cbc_enc_loop"));
+
+&set_label("cbc_enc_loop",16);
+	&movdqu	("xmm0",&QWP(0,$inp));		# load input
+	&pxor	("xmm0","xmm1");		# inp^=iv
+	&call	("_vpaes_encrypt_core");
+	&mov	($base,&DWP(0,"esp"));		# restore out
+	&mov	($key,&DWP(4,"esp"));		# restore key
+	&movdqa	("xmm1","xmm0");
+	&movdqu	(&QWP(0,$base,$inp),"xmm0");	# write output
+	&lea	($inp,&DWP(16,$inp));
+	&sub	($out,16);
+	&jnc	(&label("cbc_enc_loop"));
+	&jmp	(&label("cbc_done"));
+
+&set_label("cbc_dec_loop",16);
+	&movdqu	("xmm0",&QWP(0,$inp));		# load input
+	&movdqa	(&QWP(16,"esp"),"xmm1");	# save IV
+	&movdqa	(&QWP(32,"esp"),"xmm0");	# save future IV
+	&call	("_vpaes_decrypt_core");
+	&mov	($base,&DWP(0,"esp"));		# restore out
+	&mov	($key,&DWP(4,"esp"));		# restore key
+	&pxor	("xmm0",&QWP(16,"esp"));	# out^=iv
+	&movdqa	("xmm1",&QWP(32,"esp"));	# load next IV
+	&movdqu	(&QWP(0,$base,$inp),"xmm0");	# write output
+	&lea	($inp,&DWP(16,$inp));
+	&sub	($out,16);
+	&jnc	(&label("cbc_dec_loop"));
+
+&set_label("cbc_done");
+	&mov	($base,&DWP(8,"esp"));		# restore ivp
+	&mov	("esp",&DWP(48,"esp"));
+	&movdqu	(&QWP(0,$base),"xmm1");		# write IV
+&set_label("cbc_abort");
+&function_end("${PREFIX}_cbc_encrypt");
+
+&asm_finish();
diff --git a/crypto/aes/asm/vpaes-x86_64.pl b/crypto/aes/asm/vpaes-x86_64.pl
new file mode 100644
index 0000000..212394b
--- /dev/null
+++ b/crypto/aes/asm/vpaes-x86_64.pl
@@ -0,0 +1,1206 @@
+#!/usr/bin/env perl
+
+######################################################################
+## Constant-time SSSE3 AES core implementation.
+## version 0.1
+##
+## By Mike Hamburg (Stanford University), 2009
+## Public domain.
+##
+## For details see http://shiftleft.org/papers/vector_aes/ and
+## http://crypto.stanford.edu/vpaes/.
+
+######################################################################
+# September 2011.
+#
+# Interface to OpenSSL as "almost" drop-in replacement for
+# aes-x86_64.pl. "Almost" refers to the fact that AES_cbc_encrypt
+# doesn't handle partial vectors (doesn't have to if called from
+# EVP only). "Drop-in" implies that this module doesn't share key
+# schedule structure with the original nor does it make assumption
+# about its alignment...
+#
+# Performance summary. aes-x86_64.pl column lists large-block CBC
+# encrypt/decrypt/with-hyper-threading-off(*) results in cycles per
+# byte processed with 128-bit key, and vpaes-x86_64.pl column -
+# [also large-block CBC] encrypt/decrypt.
+#
+#		aes-x86_64.pl		vpaes-x86_64.pl
+#
+# Core 2(**)	29.6/41.1/14.3		21.9/25.2(***)
+# Nehalem	29.6/40.3/14.6		10.0/11.8
+# Atom		57.3/74.2/32.1		60.9/77.2(***)
+#
+# (*)	"Hyper-threading" in the context refers rather to cache shared
+#	among multiple cores, than to specifically Intel HTT. As vast
+#	majority of contemporary cores share cache, slower code path
+#	is common place. In other words "with-hyper-threading-off"
+#	results are presented mostly for reference purposes.
+#
+# (**)	"Core 2" refers to initial 65nm design, a.k.a. Conroe.
+#
+# (***)	Less impressive improvement on Core 2 and Atom is due to slow
+#	pshufb,	yet it's respectable +36%/62% improvement on Core 2
+#	(as implied, over "hyper-threading-safe" code path).
+#
+#						<appro@openssl.org>
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$PREFIX="vpaes";
+
+$code.=<<___;
+.text
+
+##
+##  _aes_encrypt_core
+##
+##  AES-encrypt %xmm0.
+##
+##  Inputs:
+##     %xmm0 = input
+##     %xmm9-%xmm15 as in _vpaes_preheat
+##    (%rdx) = scheduled keys
+##
+##  Output in %xmm0
+##  Clobbers  %xmm1-%xmm5, %r9, %r10, %r11, %rax
+##  Preserves %xmm6 - %xmm8 so you get some local vectors
+##
+##
+.type	_vpaes_encrypt_core,\@abi-omnipotent
+.align 16
+_vpaes_encrypt_core:
+	mov	%rdx,	%r9
+	mov	\$16,	%r11
+	mov	240(%rdx),%eax
+	movdqa	%xmm9,	%xmm1
+	movdqa	.Lk_ipt(%rip), %xmm2	# iptlo
+	pandn	%xmm0,	%xmm1
+	movdqu	(%r9),	%xmm5		# round0 key
+	psrld	\$4,	%xmm1
+	pand	%xmm9,	%xmm0
+	pshufb	%xmm0,	%xmm2
+	movdqa	.Lk_ipt+16(%rip), %xmm0	# ipthi
+	pshufb	%xmm1,	%xmm0
+	pxor	%xmm5,	%xmm2
+	add	\$16,	%r9
+	pxor	%xmm2,	%xmm0
+	lea	.Lk_mc_backward(%rip),%r10
+	jmp	.Lenc_entry
+
+.align 16
+.Lenc_loop:
+	# middle of middle round
+	movdqa  %xmm13,	%xmm4	# 4 : sb1u
+	movdqa  %xmm12,	%xmm0	# 0 : sb1t
+	pshufb  %xmm2,	%xmm4	# 4 = sb1u
+	pshufb  %xmm3,	%xmm0	# 0 = sb1t
+	pxor	%xmm5,	%xmm4	# 4 = sb1u + k
+	movdqa  %xmm15,	%xmm5	# 4 : sb2u
+	pxor	%xmm4,	%xmm0	# 0 = A
+	movdqa	-0x40(%r11,%r10), %xmm1		# .Lk_mc_forward[]
+	pshufb	%xmm2,	%xmm5	# 4 = sb2u
+	movdqa	(%r11,%r10), %xmm4		# .Lk_mc_backward[]
+	movdqa	%xmm14, %xmm2	# 2 : sb2t
+	pshufb	%xmm3,  %xmm2	# 2 = sb2t
+	movdqa	%xmm0,  %xmm3	# 3 = A
+	pxor	%xmm5,	%xmm2	# 2 = 2A
+	pshufb  %xmm1,  %xmm0	# 0 = B
+	add	\$16,	%r9	# next key
+	pxor	%xmm2,  %xmm0	# 0 = 2A+B
+	pshufb	%xmm4,	%xmm3	# 3 = D
+	add	\$16,	%r11	# next mc
+	pxor	%xmm0,	%xmm3	# 3 = 2A+B+D
+	pshufb  %xmm1,	%xmm0	# 0 = 2B+C
+	and	\$0x30,	%r11	# ... mod 4
+	sub	\$1,%rax	# nr--
+	pxor	%xmm3,	%xmm0	# 0 = 2A+3B+C+D
+
+.Lenc_entry:
+	# top of round
+	movdqa  %xmm9, 	%xmm1	# 1 : i
+	movdqa	%xmm11, %xmm5	# 2 : a/k
+	pandn	%xmm0, 	%xmm1	# 1 = i<<4
+	psrld	\$4,   	%xmm1   # 1 = i
+	pand	%xmm9, 	%xmm0   # 0 = k
+	pshufb  %xmm0,  %xmm5	# 2 = a/k
+	movdqa	%xmm10,	%xmm3  	# 3 : 1/i
+	pxor	%xmm1,	%xmm0	# 0 = j
+	pshufb  %xmm1, 	%xmm3  	# 3 = 1/i
+	movdqa	%xmm10,	%xmm4  	# 4 : 1/j
+	pxor	%xmm5, 	%xmm3  	# 3 = iak = 1/i + a/k
+	pshufb	%xmm0, 	%xmm4  	# 4 = 1/j
+	movdqa	%xmm10,	%xmm2  	# 2 : 1/iak
+	pxor	%xmm5, 	%xmm4  	# 4 = jak = 1/j + a/k
+	pshufb  %xmm3,	%xmm2  	# 2 = 1/iak
+	movdqa	%xmm10, %xmm3   # 3 : 1/jak
+	pxor	%xmm0, 	%xmm2  	# 2 = io
+	pshufb  %xmm4,  %xmm3   # 3 = 1/jak
+	movdqu	(%r9),	%xmm5
+	pxor	%xmm1,  %xmm3   # 3 = jo
+	jnz	.Lenc_loop
+
+	# middle of last round
+	movdqa	-0x60(%r10), %xmm4	# 3 : sbou	.Lk_sbo
+	movdqa	-0x50(%r10), %xmm0	# 0 : sbot	.Lk_sbo+16
+	pshufb  %xmm2,  %xmm4	# 4 = sbou
+	pxor	%xmm5,  %xmm4	# 4 = sb1u + k
+	pshufb  %xmm3,	%xmm0	# 0 = sb1t
+	movdqa	0x40(%r11,%r10), %xmm1		# .Lk_sr[]
+	pxor	%xmm4,	%xmm0	# 0 = A
+	pshufb	%xmm1,	%xmm0
+	ret
+.size	_vpaes_encrypt_core,.-_vpaes_encrypt_core
+	
+##
+##  Decryption core
+##
+##  Same API as encryption core.
+##
+.type	_vpaes_decrypt_core,\@abi-omnipotent
+.align	16
+_vpaes_decrypt_core:
+	mov	%rdx,	%r9		# load key
+	mov	240(%rdx),%eax
+	movdqa	%xmm9,	%xmm1
+	movdqa	.Lk_dipt(%rip), %xmm2	# iptlo
+	pandn	%xmm0,	%xmm1
+	mov	%rax,	%r11
+	psrld	\$4,	%xmm1
+	movdqu	(%r9),	%xmm5		# round0 key
+	shl	\$4,	%r11
+	pand	%xmm9,	%xmm0
+	pshufb	%xmm0,	%xmm2
+	movdqa	.Lk_dipt+16(%rip), %xmm0 # ipthi
+	xor	\$0x30,	%r11
+	lea	.Lk_dsbd(%rip),%r10
+	pshufb	%xmm1,	%xmm0
+	and	\$0x30,	%r11
+	pxor	%xmm5,	%xmm2
+	movdqa	.Lk_mc_forward+48(%rip), %xmm5
+	pxor	%xmm2,	%xmm0
+	add	\$16,	%r9
+	add	%r10,	%r11
+	jmp	.Ldec_entry
+
+.align 16
+.Ldec_loop:
+##
+##  Inverse mix columns
+##
+	movdqa  -0x20(%r10),%xmm4	# 4 : sb9u
+	movdqa  -0x10(%r10),%xmm1	# 0 : sb9t
+	pshufb	%xmm2,	%xmm4		# 4 = sb9u
+	pshufb	%xmm3,	%xmm1		# 0 = sb9t
+	pxor	%xmm4,	%xmm0
+	movdqa  0x00(%r10),%xmm4	# 4 : sbdu
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	movdqa  0x10(%r10),%xmm1	# 0 : sbdt
+
+	pshufb	%xmm2,	%xmm4		# 4 = sbdu
+	pshufb	%xmm5,	%xmm0		# MC ch
+	pshufb	%xmm3,	%xmm1		# 0 = sbdt
+	pxor	%xmm4,	%xmm0		# 4 = ch
+	movdqa  0x20(%r10),%xmm4	# 4 : sbbu
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	movdqa  0x30(%r10),%xmm1	# 0 : sbbt
+
+	pshufb	%xmm2,	%xmm4		# 4 = sbbu
+	pshufb	%xmm5,	%xmm0		# MC ch
+	pshufb	%xmm3,	%xmm1		# 0 = sbbt
+	pxor	%xmm4,	%xmm0		# 4 = ch
+	movdqa  0x40(%r10),%xmm4	# 4 : sbeu
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	movdqa  0x50(%r10),%xmm1	# 0 : sbet
+
+	pshufb	%xmm2,	%xmm4		# 4 = sbeu
+	pshufb	%xmm5,	%xmm0		# MC ch
+	pshufb	%xmm3,	%xmm1		# 0 = sbet
+	pxor	%xmm4,	%xmm0		# 4 = ch
+	add	\$16, %r9		# next round key
+	palignr	\$12,	%xmm5,	%xmm5
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	sub	\$1,%rax		# nr--
+
+.Ldec_entry:
+	# top of round
+	movdqa  %xmm9, 	%xmm1	# 1 : i
+	pandn	%xmm0, 	%xmm1	# 1 = i<<4
+	movdqa	%xmm11, %xmm2	# 2 : a/k
+	psrld	\$4,    %xmm1	# 1 = i
+	pand	%xmm9, 	%xmm0	# 0 = k
+	pshufb  %xmm0,  %xmm2	# 2 = a/k
+	movdqa	%xmm10,	%xmm3	# 3 : 1/i
+	pxor	%xmm1,	%xmm0	# 0 = j
+	pshufb  %xmm1, 	%xmm3	# 3 = 1/i
+	movdqa	%xmm10,	%xmm4	# 4 : 1/j
+	pxor	%xmm2, 	%xmm3	# 3 = iak = 1/i + a/k
+	pshufb	%xmm0, 	%xmm4	# 4 = 1/j
+	pxor	%xmm2, 	%xmm4	# 4 = jak = 1/j + a/k
+	movdqa	%xmm10,	%xmm2	# 2 : 1/iak
+	pshufb  %xmm3,	%xmm2	# 2 = 1/iak
+	movdqa	%xmm10, %xmm3	# 3 : 1/jak
+	pxor	%xmm0, 	%xmm2	# 2 = io
+	pshufb  %xmm4,  %xmm3	# 3 = 1/jak
+	movdqu	(%r9),	%xmm0
+	pxor	%xmm1,  %xmm3	# 3 = jo
+	jnz	.Ldec_loop
+
+	# middle of last round
+	movdqa	0x60(%r10), %xmm4	# 3 : sbou
+	pshufb  %xmm2,  %xmm4	# 4 = sbou
+	pxor	%xmm0,  %xmm4	# 4 = sb1u + k
+	movdqa	0x70(%r10), %xmm0	# 0 : sbot
+	movdqa	-0x160(%r11), %xmm2	# .Lk_sr-.Lk_dsbd=-0x160
+	pshufb  %xmm3,	%xmm0	# 0 = sb1t
+	pxor	%xmm4,	%xmm0	# 0 = A
+	pshufb	%xmm2,	%xmm0
+	ret
+.size	_vpaes_decrypt_core,.-_vpaes_decrypt_core
+
+########################################################
+##                                                    ##
+##                  AES key schedule                  ##
+##                                                    ##
+########################################################
+.type	_vpaes_schedule_core,\@abi-omnipotent
+.align	16
+_vpaes_schedule_core:
+	# rdi = key
+	# rsi = size in bits
+	# rdx = buffer
+	# rcx = direction.  0=encrypt, 1=decrypt
+
+	call	_vpaes_preheat		# load the tables
+	movdqa	.Lk_rcon(%rip), %xmm8	# load rcon
+	movdqu	(%rdi),	%xmm0		# load key (unaligned)
+
+	# input transform
+	movdqa	%xmm0,	%xmm3
+	lea	.Lk_ipt(%rip), %r11
+	call	_vpaes_schedule_transform
+	movdqa	%xmm0,	%xmm7
+
+	lea	.Lk_sr(%rip),%r10
+	test	%rcx,	%rcx
+	jnz	.Lschedule_am_decrypting
+
+	# encrypting, output zeroth round key after transform
+	movdqu	%xmm0,	(%rdx)
+	jmp	.Lschedule_go
+
+.Lschedule_am_decrypting:
+	# decrypting, output zeroth round key after shiftrows
+	movdqa	(%r8,%r10),%xmm1
+	pshufb  %xmm1,	%xmm3
+	movdqu	%xmm3,	(%rdx)
+	xor	\$0x30, %r8
+
+.Lschedule_go:
+	cmp	\$192,	%esi
+	ja	.Lschedule_256
+	je	.Lschedule_192
+	# 128: fall though
+
+##
+##  .schedule_128
+##
+##  128-bit specific part of key schedule.
+##
+##  This schedule is really simple, because all its parts
+##  are accomplished by the subroutines.
+##
+.Lschedule_128:
+	mov	\$10, %esi
+	
+.Loop_schedule_128:
+	call 	_vpaes_schedule_round
+	dec	%rsi
+	jz 	.Lschedule_mangle_last
+	call	_vpaes_schedule_mangle	# write output
+	jmp 	.Loop_schedule_128
+
+##
+##  .aes_schedule_192
+##
+##  192-bit specific part of key schedule.
+##
+##  The main body of this schedule is the same as the 128-bit
+##  schedule, but with more smearing.  The long, high side is
+##  stored in %xmm7 as before, and the short, low side is in
+##  the high bits of %xmm6.
+##
+##  This schedule is somewhat nastier, however, because each
+##  round produces 192 bits of key material, or 1.5 round keys.
+##  Therefore, on each cycle we do 2 rounds and produce 3 round
+##  keys.
+##
+.align	16
+.Lschedule_192:
+	movdqu	8(%rdi),%xmm0		# load key part 2 (very unaligned)
+	call	_vpaes_schedule_transform	# input transform
+	movdqa	%xmm0,	%xmm6		# save short part
+	pxor	%xmm4,	%xmm4		# clear 4
+	movhlps	%xmm4,	%xmm6		# clobber low side with zeros
+	mov	\$4,	%esi
+
+.Loop_schedule_192:
+	call	_vpaes_schedule_round
+	palignr	\$8,%xmm6,%xmm0	
+	call	_vpaes_schedule_mangle	# save key n
+	call	_vpaes_schedule_192_smear
+	call	_vpaes_schedule_mangle	# save key n+1
+	call	_vpaes_schedule_round
+	dec	%rsi
+	jz 	.Lschedule_mangle_last
+	call	_vpaes_schedule_mangle	# save key n+2
+	call	_vpaes_schedule_192_smear
+	jmp	.Loop_schedule_192
+
+##
+##  .aes_schedule_256
+##
+##  256-bit specific part of key schedule.
+##
+##  The structure here is very similar to the 128-bit
+##  schedule, but with an additional "low side" in
+##  %xmm6.  The low side's rounds are the same as the
+##  high side's, except no rcon and no rotation.
+##
+.align	16
+.Lschedule_256:
+	movdqu	16(%rdi),%xmm0		# load key part 2 (unaligned)
+	call	_vpaes_schedule_transform	# input transform
+	mov	\$7, %esi
+	
+.Loop_schedule_256:
+	call	_vpaes_schedule_mangle	# output low result
+	movdqa	%xmm0,	%xmm6		# save cur_lo in xmm6
+
+	# high round
+	call	_vpaes_schedule_round
+	dec	%rsi
+	jz 	.Lschedule_mangle_last
+	call	_vpaes_schedule_mangle	
+
+	# low round. swap xmm7 and xmm6
+	pshufd	\$0xFF,	%xmm0,	%xmm0
+	movdqa	%xmm7,	%xmm5
+	movdqa	%xmm6,	%xmm7
+	call	_vpaes_schedule_low_round
+	movdqa	%xmm5,	%xmm7
+	
+	jmp	.Loop_schedule_256
+
+	
+##
+##  .aes_schedule_mangle_last
+##
+##  Mangler for last round of key schedule
+##  Mangles %xmm0
+##    when encrypting, outputs out(%xmm0) ^ 63
+##    when decrypting, outputs unskew(%xmm0)
+##
+##  Always called right before return... jumps to cleanup and exits
+##
+.align	16
+.Lschedule_mangle_last:
+	# schedule last round key from xmm0
+	lea	.Lk_deskew(%rip),%r11	# prepare to deskew
+	test	%rcx, 	%rcx
+	jnz	.Lschedule_mangle_last_dec
+
+	# encrypting
+	movdqa	(%r8,%r10),%xmm1
+	pshufb	%xmm1,	%xmm0		# output permute
+	lea	.Lk_opt(%rip),	%r11	# prepare to output transform
+	add	\$32,	%rdx
+
+.Lschedule_mangle_last_dec:
+	add	\$-16,	%rdx
+	pxor	.Lk_s63(%rip),	%xmm0
+	call	_vpaes_schedule_transform # output transform
+	movdqu	%xmm0,	(%rdx)		# save last key
+
+	# cleanup
+	pxor	%xmm0,  %xmm0
+	pxor	%xmm1,  %xmm1
+	pxor	%xmm2,  %xmm2
+	pxor	%xmm3,  %xmm3
+	pxor	%xmm4,  %xmm4
+	pxor	%xmm5,  %xmm5
+	pxor	%xmm6,  %xmm6
+	pxor	%xmm7,  %xmm7
+	ret
+.size	_vpaes_schedule_core,.-_vpaes_schedule_core
+
+##
+##  .aes_schedule_192_smear
+##
+##  Smear the short, low side in the 192-bit key schedule.
+##
+##  Inputs:
+##    %xmm7: high side, b  a  x  y
+##    %xmm6:  low side, d  c  0  0
+##    %xmm13: 0
+##
+##  Outputs:
+##    %xmm6: b+c+d  b+c  0  0
+##    %xmm0: b+c+d  b+c  b  a
+##
+.type	_vpaes_schedule_192_smear,\@abi-omnipotent
+.align	16
+_vpaes_schedule_192_smear:
+	pshufd	\$0x80,	%xmm6,	%xmm1	# d c 0 0 -> c 0 0 0
+	pshufd	\$0xFE,	%xmm7,	%xmm0	# b a _ _ -> b b b a
+	pxor	%xmm1,	%xmm6		# -> c+d c 0 0
+	pxor	%xmm1,	%xmm1
+	pxor	%xmm0,	%xmm6		# -> b+c+d b+c b a
+	movdqa	%xmm6,	%xmm0
+	movhlps	%xmm1,	%xmm6		# clobber low side with zeros
+	ret
+.size	_vpaes_schedule_192_smear,.-_vpaes_schedule_192_smear
+
+##
+##  .aes_schedule_round
+##
+##  Runs one main round of the key schedule on %xmm0, %xmm7
+##
+##  Specifically, runs subbytes on the high dword of %xmm0
+##  then rotates it by one byte and xors into the low dword of
+##  %xmm7.
+##
+##  Adds rcon from low byte of %xmm8, then rotates %xmm8 for
+##  next rcon.
+##
+##  Smears the dwords of %xmm7 by xoring the low into the
+##  second low, result into third, result into highest.
+##
+##  Returns results in %xmm7 = %xmm0.
+##  Clobbers %xmm1-%xmm4, %r11.
+##
+.type	_vpaes_schedule_round,\@abi-omnipotent
+.align	16
+_vpaes_schedule_round:
+	# extract rcon from xmm8
+	pxor	%xmm1,	%xmm1
+	palignr	\$15,	%xmm8,	%xmm1
+	palignr	\$15,	%xmm8,	%xmm8
+	pxor	%xmm1,	%xmm7
+
+	# rotate
+	pshufd	\$0xFF,	%xmm0,	%xmm0
+	palignr	\$1,	%xmm0,	%xmm0
+	
+	# fall through...
+	
+	# low round: same as high round, but no rotation and no rcon.
+_vpaes_schedule_low_round:
+	# smear xmm7
+	movdqa	%xmm7,	%xmm1
+	pslldq	\$4,	%xmm7
+	pxor	%xmm1,	%xmm7
+	movdqa	%xmm7,	%xmm1
+	pslldq	\$8,	%xmm7
+	pxor	%xmm1,	%xmm7
+	pxor	.Lk_s63(%rip), %xmm7
+
+	# subbytes
+	movdqa  %xmm9, 	%xmm1
+	pandn	%xmm0, 	%xmm1
+	psrld	\$4,    %xmm1		# 1 = i
+	pand	%xmm9, 	%xmm0		# 0 = k
+	movdqa	%xmm11, %xmm2		# 2 : a/k
+	pshufb  %xmm0,  %xmm2		# 2 = a/k
+	pxor	%xmm1,	%xmm0		# 0 = j
+	movdqa	%xmm10,	%xmm3		# 3 : 1/i
+	pshufb  %xmm1, 	%xmm3		# 3 = 1/i
+	pxor	%xmm2, 	%xmm3		# 3 = iak = 1/i + a/k
+	movdqa	%xmm10,	%xmm4		# 4 : 1/j
+	pshufb	%xmm0, 	%xmm4		# 4 = 1/j
+	pxor	%xmm2, 	%xmm4		# 4 = jak = 1/j + a/k
+	movdqa	%xmm10,	%xmm2		# 2 : 1/iak
+	pshufb  %xmm3,	%xmm2		# 2 = 1/iak
+	pxor	%xmm0, 	%xmm2		# 2 = io
+	movdqa	%xmm10, %xmm3		# 3 : 1/jak
+	pshufb  %xmm4,  %xmm3		# 3 = 1/jak
+	pxor	%xmm1,  %xmm3		# 3 = jo
+	movdqa	%xmm13, %xmm4		# 4 : sbou
+	pshufb  %xmm2,  %xmm4		# 4 = sbou
+	movdqa	%xmm12, %xmm0		# 0 : sbot
+	pshufb  %xmm3,	%xmm0		# 0 = sb1t
+	pxor	%xmm4, 	%xmm0		# 0 = sbox output
+
+	# add in smeared stuff
+	pxor	%xmm7,	%xmm0	
+	movdqa	%xmm0,	%xmm7
+	ret
+.size	_vpaes_schedule_round,.-_vpaes_schedule_round
+
+##
+##  .aes_schedule_transform
+##
+##  Linear-transform %xmm0 according to tables at (%r11)
+##
+##  Requires that %xmm9 = 0x0F0F... as in preheat
+##  Output in %xmm0
+##  Clobbers %xmm1, %xmm2
+##
+.type	_vpaes_schedule_transform,\@abi-omnipotent
+.align	16
+_vpaes_schedule_transform:
+	movdqa	%xmm9,	%xmm1
+	pandn	%xmm0,	%xmm1
+	psrld	\$4,	%xmm1
+	pand	%xmm9,	%xmm0
+	movdqa	(%r11), %xmm2 	# lo
+	pshufb	%xmm0,	%xmm2
+	movdqa	16(%r11), %xmm0 # hi
+	pshufb	%xmm1,	%xmm0
+	pxor	%xmm2,	%xmm0
+	ret
+.size	_vpaes_schedule_transform,.-_vpaes_schedule_transform
+
+##
+##  .aes_schedule_mangle
+##
+##  Mangle xmm0 from (basis-transformed) standard version
+##  to our version.
+##
+##  On encrypt,
+##    xor with 0x63
+##    multiply by circulant 0,1,1,1
+##    apply shiftrows transform
+##
+##  On decrypt,
+##    xor with 0x63
+##    multiply by "inverse mixcolumns" circulant E,B,D,9
+##    deskew
+##    apply shiftrows transform
+##
+##
+##  Writes out to (%rdx), and increments or decrements it
+##  Keeps track of round number mod 4 in %r8
+##  Preserves xmm0
+##  Clobbers xmm1-xmm5
+##
+.type	_vpaes_schedule_mangle,\@abi-omnipotent
+.align	16
+_vpaes_schedule_mangle:
+	movdqa	%xmm0,	%xmm4	# save xmm0 for later
+	movdqa	.Lk_mc_forward(%rip),%xmm5
+	test	%rcx, 	%rcx
+	jnz	.Lschedule_mangle_dec
+
+	# encrypting
+	add	\$16,	%rdx
+	pxor	.Lk_s63(%rip),%xmm4
+	pshufb	%xmm5,	%xmm4
+	movdqa	%xmm4,	%xmm3
+	pshufb	%xmm5,	%xmm4
+	pxor	%xmm4,	%xmm3
+	pshufb	%xmm5,	%xmm4
+	pxor	%xmm4,	%xmm3
+
+	jmp	.Lschedule_mangle_both
+.align	16
+.Lschedule_mangle_dec:
+	# inverse mix columns
+	lea	.Lk_dksd(%rip),%r11
+	movdqa	%xmm9,	%xmm1
+	pandn	%xmm4,	%xmm1
+	psrld	\$4,	%xmm1	# 1 = hi
+	pand	%xmm9,	%xmm4	# 4 = lo
+
+	movdqa	0x00(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	movdqa	0x10(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+	pshufb	%xmm5,	%xmm3
+
+	movdqa	0x20(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	pxor	%xmm3,	%xmm2
+	movdqa	0x30(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+	pshufb	%xmm5,	%xmm3
+
+	movdqa	0x40(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	pxor	%xmm3,	%xmm2
+	movdqa	0x50(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+	pshufb	%xmm5,	%xmm3
+
+	movdqa	0x60(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	pxor	%xmm3,	%xmm2
+	movdqa	0x70(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+
+	add	\$-16,	%rdx
+
+.Lschedule_mangle_both:
+	movdqa	(%r8,%r10),%xmm1
+	pshufb	%xmm1,%xmm3
+	add	\$-16,	%r8
+	and	\$0x30,	%r8
+	movdqu	%xmm3,	(%rdx)
+	ret
+.size	_vpaes_schedule_mangle,.-_vpaes_schedule_mangle
+
+#
+# Interface to OpenSSL
+#
+.globl	${PREFIX}_set_encrypt_key
+.type	${PREFIX}_set_encrypt_key,\@function,3
+.align	16
+${PREFIX}_set_encrypt_key:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lenc_key_body:
+___
+$code.=<<___;
+	mov	%esi,%eax
+	shr	\$5,%eax
+	add	\$5,%eax
+	mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+
+	mov	\$0,%ecx
+	mov	\$0x30,%r8d
+	call	_vpaes_schedule_core
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Lenc_key_epilogue:
+___
+$code.=<<___;
+	xor	%eax,%eax
+	ret
+.size	${PREFIX}_set_encrypt_key,.-${PREFIX}_set_encrypt_key
+
+.globl	${PREFIX}_set_decrypt_key
+.type	${PREFIX}_set_decrypt_key,\@function,3
+.align	16
+${PREFIX}_set_decrypt_key:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Ldec_key_body:
+___
+$code.=<<___;
+	mov	%esi,%eax
+	shr	\$5,%eax
+	add	\$5,%eax
+	mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+	shl	\$4,%eax
+	lea	16(%rdx,%rax),%rdx
+
+	mov	\$1,%ecx
+	mov	%esi,%r8d
+	shr	\$1,%r8d
+	and	\$32,%r8d
+	xor	\$32,%r8d	# nbits==192?0:32
+	call	_vpaes_schedule_core
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Ldec_key_epilogue:
+___
+$code.=<<___;
+	xor	%eax,%eax
+	ret
+.size	${PREFIX}_set_decrypt_key,.-${PREFIX}_set_decrypt_key
+
+.globl	${PREFIX}_encrypt
+.type	${PREFIX}_encrypt,\@function,3
+.align	16
+${PREFIX}_encrypt:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lenc_body:
+___
+$code.=<<___;
+	movdqu	(%rdi),%xmm0
+	call	_vpaes_preheat
+	call	_vpaes_encrypt_core
+	movdqu	%xmm0,(%rsi)
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Lenc_epilogue:
+___
+$code.=<<___;
+	ret
+.size	${PREFIX}_encrypt,.-${PREFIX}_encrypt
+
+.globl	${PREFIX}_decrypt
+.type	${PREFIX}_decrypt,\@function,3
+.align	16
+${PREFIX}_decrypt:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Ldec_body:
+___
+$code.=<<___;
+	movdqu	(%rdi),%xmm0
+	call	_vpaes_preheat
+	call	_vpaes_decrypt_core
+	movdqu	%xmm0,(%rsi)
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Ldec_epilogue:
+___
+$code.=<<___;
+	ret
+.size	${PREFIX}_decrypt,.-${PREFIX}_decrypt
+___
+{
+my ($inp,$out,$len,$key,$ivp,$enc)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9");
+# void AES_cbc_encrypt (const void char *inp, unsigned char *out,
+#                       size_t length, const AES_KEY *key,
+#                       unsigned char *ivp,const int enc);
+$code.=<<___;
+.globl	${PREFIX}_cbc_encrypt
+.type	${PREFIX}_cbc_encrypt,\@function,6
+.align	16
+${PREFIX}_cbc_encrypt:
+	xchg	$key,$len
+___
+($len,$key)=($key,$len);
+$code.=<<___;
+	sub	\$16,$len
+	jc	.Lcbc_abort
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lcbc_body:
+___
+$code.=<<___;
+	movdqu	($ivp),%xmm6		# load IV
+	sub	$inp,$out
+	call	_vpaes_preheat
+	cmp	\$0,${enc}d
+	je	.Lcbc_dec_loop
+	jmp	.Lcbc_enc_loop
+.align	16
+.Lcbc_enc_loop:
+	movdqu	($inp),%xmm0
+	pxor	%xmm6,%xmm0
+	call	_vpaes_encrypt_core
+	movdqa	%xmm0,%xmm6
+	movdqu	%xmm0,($out,$inp)
+	lea	16($inp),$inp
+	sub	\$16,$len
+	jnc	.Lcbc_enc_loop
+	jmp	.Lcbc_done
+.align	16
+.Lcbc_dec_loop:
+	movdqu	($inp),%xmm0
+	movdqa	%xmm0,%xmm7
+	call	_vpaes_decrypt_core
+	pxor	%xmm6,%xmm0
+	movdqa	%xmm7,%xmm6
+	movdqu	%xmm0,($out,$inp)
+	lea	16($inp),$inp
+	sub	\$16,$len
+	jnc	.Lcbc_dec_loop
+.Lcbc_done:
+	movdqu	%xmm6,($ivp)		# save IV
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Lcbc_epilogue:
+___
+$code.=<<___;
+.Lcbc_abort:
+	ret
+.size	${PREFIX}_cbc_encrypt,.-${PREFIX}_cbc_encrypt
+___
+}
+$code.=<<___;
+##
+##  _aes_preheat
+##
+##  Fills register %r10 -> .aes_consts (so you can -fPIC)
+##  and %xmm9-%xmm15 as specified below.
+##
+.type	_vpaes_preheat,\@abi-omnipotent
+.align	16
+_vpaes_preheat:
+	lea	.Lk_s0F(%rip), %r10
+	movdqa	-0x20(%r10), %xmm10	# .Lk_inv
+	movdqa	-0x10(%r10), %xmm11	# .Lk_inv+16
+	movdqa	0x00(%r10), %xmm9	# .Lk_s0F
+	movdqa	0x30(%r10), %xmm13	# .Lk_sb1
+	movdqa	0x40(%r10), %xmm12	# .Lk_sb1+16
+	movdqa	0x50(%r10), %xmm15	# .Lk_sb2
+	movdqa	0x60(%r10), %xmm14	# .Lk_sb2+16
+	ret
+.size	_vpaes_preheat,.-_vpaes_preheat
+########################################################
+##                                                    ##
+##                     Constants                      ##
+##                                                    ##
+########################################################
+.type	_vpaes_consts,\@object
+.align	64
+_vpaes_consts:
+.Lk_inv:	# inv, inva
+	.quad	0x0E05060F0D080180, 0x040703090A0B0C02
+	.quad	0x01040A060F0B0780, 0x030D0E0C02050809
+
+.Lk_s0F:	# s0F
+	.quad	0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F
+
+.Lk_ipt:	# input transform (lo, hi)
+	.quad	0xC2B2E8985A2A7000, 0xCABAE09052227808
+	.quad	0x4C01307D317C4D00, 0xCD80B1FCB0FDCC81
+
+.Lk_sb1:	# sb1u, sb1t
+	.quad	0xB19BE18FCB503E00, 0xA5DF7A6E142AF544
+	.quad	0x3618D415FAE22300, 0x3BF7CCC10D2ED9EF
+.Lk_sb2:	# sb2u, sb2t
+	.quad	0xE27A93C60B712400, 0x5EB7E955BC982FCD
+	.quad	0x69EB88400AE12900, 0xC2A163C8AB82234A
+.Lk_sbo:	# sbou, sbot
+	.quad	0xD0D26D176FBDC700, 0x15AABF7AC502A878
+	.quad	0xCFE474A55FBB6A00, 0x8E1E90D1412B35FA
+
+.Lk_mc_forward:	# mc_forward
+	.quad	0x0407060500030201, 0x0C0F0E0D080B0A09
+	.quad	0x080B0A0904070605, 0x000302010C0F0E0D
+	.quad	0x0C0F0E0D080B0A09, 0x0407060500030201
+	.quad	0x000302010C0F0E0D, 0x080B0A0904070605
+
+.Lk_mc_backward:# mc_backward
+	.quad	0x0605040702010003, 0x0E0D0C0F0A09080B
+	.quad	0x020100030E0D0C0F, 0x0A09080B06050407
+	.quad	0x0E0D0C0F0A09080B, 0x0605040702010003
+	.quad	0x0A09080B06050407, 0x020100030E0D0C0F
+
+.Lk_sr:		# sr
+	.quad	0x0706050403020100, 0x0F0E0D0C0B0A0908
+	.quad	0x030E09040F0A0500, 0x0B06010C07020D08
+	.quad	0x0F060D040B020900, 0x070E050C030A0108
+	.quad	0x0B0E0104070A0D00, 0x0306090C0F020508
+
+.Lk_rcon:	# rcon
+	.quad	0x1F8391B9AF9DEEB6, 0x702A98084D7C7D81
+
+.Lk_s63:	# s63: all equal to 0x63 transformed
+	.quad	0x5B5B5B5B5B5B5B5B, 0x5B5B5B5B5B5B5B5B
+
+.Lk_opt:	# output transform
+	.quad	0xFF9F4929D6B66000, 0xF7974121DEBE6808
+	.quad	0x01EDBD5150BCEC00, 0xE10D5DB1B05C0CE0
+
+.Lk_deskew:	# deskew tables: inverts the sbox's "skew"
+	.quad	0x07E4A34047A4E300, 0x1DFEB95A5DBEF91A
+	.quad	0x5F36B5DC83EA6900, 0x2841C2ABF49D1E77
+
+##
+##  Decryption stuff
+##  Key schedule constants
+##
+.Lk_dksd:	# decryption key schedule: invskew x*D
+	.quad	0xFEB91A5DA3E44700, 0x0740E3A45A1DBEF9
+	.quad	0x41C277F4B5368300, 0x5FDC69EAAB289D1E
+.Lk_dksb:	# decryption key schedule: invskew x*B
+	.quad	0x9A4FCA1F8550D500, 0x03D653861CC94C99
+	.quad	0x115BEDA7B6FC4A00, 0xD993256F7E3482C8
+.Lk_dkse:	# decryption key schedule: invskew x*E + 0x63
+	.quad	0xD5031CCA1FC9D600, 0x53859A4C994F5086
+	.quad	0xA23196054FDC7BE8, 0xCD5EF96A20B31487
+.Lk_dks9:	# decryption key schedule: invskew x*9
+	.quad	0xB6116FC87ED9A700, 0x4AED933482255BFC
+	.quad	0x4576516227143300, 0x8BB89FACE9DAFDCE
+
+##
+##  Decryption stuff
+##  Round function constants
+##
+.Lk_dipt:	# decryption input transform
+	.quad	0x0F505B040B545F00, 0x154A411E114E451A
+	.quad	0x86E383E660056500, 0x12771772F491F194
+
+.Lk_dsb9:	# decryption sbox output *9*u, *9*t
+	.quad	0x851C03539A86D600, 0xCAD51F504F994CC9
+	.quad	0xC03B1789ECD74900, 0x725E2C9EB2FBA565
+.Lk_dsbd:	# decryption sbox output *D*u, *D*t
+	.quad	0x7D57CCDFE6B1A200, 0xF56E9B13882A4439
+	.quad	0x3CE2FAF724C6CB00, 0x2931180D15DEEFD3
+.Lk_dsbb:	# decryption sbox output *B*u, *B*t
+	.quad	0xD022649296B44200, 0x602646F6B0F2D404
+	.quad	0xC19498A6CD596700, 0xF3FF0C3E3255AA6B
+.Lk_dsbe:	# decryption sbox output *E*u, *E*t
+	.quad	0x46F2929626D4D000, 0x2242600464B4F6B0
+	.quad	0x0C55A6CDFFAAC100, 0x9467F36B98593E32
+.Lk_dsbo:	# decryption sbox final output
+	.quad	0x1387EA537EF94000, 0xC7AA6DB9D4943E2D
+	.quad	0x12D7560F93441D00, 0xCA4B8159D8C58E9C
+.asciz	"Vector Permutaion AES for x86_64/SSSE3, Mike Hamburg (Stanford University)"
+.align	64
+.size	_vpaes_consts,.-_vpaes_consts
+___
+
+if ($win64) {
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	16(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0xb8(%rax),%rax		# adjust stack pointer
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_${PREFIX}_set_encrypt_key
+	.rva	.LSEH_end_${PREFIX}_set_encrypt_key
+	.rva	.LSEH_info_${PREFIX}_set_encrypt_key
+
+	.rva	.LSEH_begin_${PREFIX}_set_decrypt_key
+	.rva	.LSEH_end_${PREFIX}_set_decrypt_key
+	.rva	.LSEH_info_${PREFIX}_set_decrypt_key
+
+	.rva	.LSEH_begin_${PREFIX}_encrypt
+	.rva	.LSEH_end_${PREFIX}_encrypt
+	.rva	.LSEH_info_${PREFIX}_encrypt
+
+	.rva	.LSEH_begin_${PREFIX}_decrypt
+	.rva	.LSEH_end_${PREFIX}_decrypt
+	.rva	.LSEH_info_${PREFIX}_decrypt
+
+	.rva	.LSEH_begin_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_end_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_info_${PREFIX}_cbc_encrypt
+
+.section	.xdata
+.align	8
+.LSEH_info_${PREFIX}_set_encrypt_key:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lenc_key_body,.Lenc_key_epilogue	# HandlerData[]
+.LSEH_info_${PREFIX}_set_decrypt_key:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Ldec_key_body,.Ldec_key_epilogue	# HandlerData[]
+.LSEH_info_${PREFIX}_encrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lenc_body,.Lenc_epilogue		# HandlerData[]
+.LSEH_info_${PREFIX}_decrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Ldec_body,.Ldec_epilogue		# HandlerData[]
+.LSEH_info_${PREFIX}_cbc_encrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lcbc_body,.Lcbc_epilogue		# HandlerData[]
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/internal.h b/crypto/aes/internal.h
new file mode 100644
index 0000000..3dc5c63
--- /dev/null
+++ b/crypto/aes/internal.h
@@ -0,0 +1,87 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#ifndef OPENSSL_HEADER_AES_INTERNAL_H
+#define OPENSSL_HEADER_AES_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#if defined(_MSC_VER) && \
+    (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(p) SWAP(*((uint32_t *)(p)))
+#define PUTU32(ct, st) \
+  { *((uint32_t *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt)                                         \
+  (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ \
+   ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3]))
+#define PUTU32(ct, st)          \
+  {                             \
+    (ct)[0] = (uint8_t)((st) >> 24); \
+    (ct)[1] = (uint8_t)((st) >> 16); \
+    (ct)[2] = (uint8_t)((st) >> 8);  \
+    (ct)[3] = (uint8_t)(st);         \
+  }
+#endif
+
+#define MAXKC (256 / 32)
+#define MAXKB (256 / 8)
+#define MAXNR 14
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_AES_INTERNAL_H */
diff --git a/crypto/aes/mode_wrappers.c b/crypto/aes/mode_wrappers.c
new file mode 100644
index 0000000..0ee2326
--- /dev/null
+++ b/crypto/aes/mode_wrappers.c
@@ -0,0 +1,99 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <openssl/aes.h>
+
+#include "assert.h"
+
+#include <openssl/modes.h>
+
+
+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);
+}
+
+void AES_ecb_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key,
+                     const int enc) {
+  assert(in && out && key);
+  assert((AES_ENCRYPT == enc) || (AES_DECRYPT == enc));
+
+  if (AES_ENCRYPT == enc) {
+    AES_encrypt(in, out, key);
+  } else {
+    AES_decrypt(in, out, key);
+  }
+}
+
+#if defined(OPENSSL_NO_ASM) || \
+    (!defined(OPENSSL_X86_64) && !defined(OPENSSL_X86))
+void AES_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                     const AES_KEY *key, uint8_t *ivec, const int enc) {
+
+  if (enc) {
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, (block128_f)AES_encrypt);
+  } else {
+    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, (block128_f)AES_decrypt);
+  }
+}
+#endif
+
+void AES_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                        const AES_KEY *key, uint8_t *ivec, int *num) {
+  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, num,
+                        (block128_f)AES_encrypt);
+}
+
+void AES_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                        const AES_KEY *key, uint8_t *ivec, int *num,
+                        int enc) {
+  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, num, enc,
+                        (block128_f)AES_encrypt);
+}
diff --git a/crypto/arm_arch.h b/crypto/arm_arch.h
new file mode 100644
index 0000000..d528eee
--- /dev/null
+++ b/crypto/arm_arch.h
@@ -0,0 +1,100 @@
+/* ====================================================================
+ * Copyright (c) 1998-2011 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_ARM_ARCH_H
+#define OPENSSL_HEADER_ARM_ARCH_H
+
+#if !defined(__ARM_ARCH__)
+# if defined(__CC_ARM)
+#  define __ARM_ARCH__ __TARGET_ARCH_ARM
+#  if defined(__BIG_ENDIAN)
+#   define __ARMEB__
+#  else
+#   define __ARMEL__
+#  endif
+# elif defined(__GNUC__)
+  /* Why doesn't gcc define __ARM_ARCH__? Instead it defines
+   * bunch of below macros. See all_architectires[] table in
+   * gcc/config/arm/arm.c. On a side note it defines
+   * __ARMEL__/__ARMEB__ for little-/big-endian. */
+#  if	defined(__ARM_ARCH_7__)	|| defined(__ARM_ARCH_7A__)	|| \
+	defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__)	|| \
+	defined(__ARM_ARCH_7EM__)
+#   define __ARM_ARCH__ 7
+#  elif	defined(__ARM_ARCH_6__)	|| defined(__ARM_ARCH_6J__)	|| \
+	defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__)	|| \
+	defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__)	|| \
+	defined(__ARM_ARCH_6T2__)
+#   define __ARM_ARCH__ 6
+#  elif	defined(__ARM_ARCH_5__)	|| defined(__ARM_ARCH_5T__)	|| \
+	defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__)	|| \
+	defined(__ARM_ARCH_5TEJ__)
+#   define __ARM_ARCH__ 5
+#  elif	defined(__ARM_ARCH_4__)	|| defined(__ARM_ARCH_4T__)
+#   define __ARM_ARCH__ 4
+#  else
+#   error "unsupported ARM architecture"
+#  endif
+# endif
+#endif
+
+#if !__ASSEMBLER__
+/* OPENSSL_armcap_P contains flags describing the capabilities of the CPU and
+ * is easy for assembly code to acesss. For C code, see the functions in
+ * |cpu.h|. */
+extern unsigned int OPENSSL_armcap_P;
+
+#define ARMV7_NEON      (1<<0)
+#endif
+
+
+#endif  /* OPENSSL_HEADER_THREAD_H */
diff --git a/crypto/asn1/CMakeLists.txt b/crypto/asn1/CMakeLists.txt
new file mode 100644
index 0000000..884ecd2
--- /dev/null
+++ b/crypto/asn1/CMakeLists.txt
@@ -0,0 +1,46 @@
+include_directories(. .. ../../include)
+
+add_library(
+	asn1
+
+	OBJECT
+
+	a_bitstr.c
+	a_bool.c
+	a_bytes.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_error.c
+	asn1_lib.c
+	asn1_par.c
+	asn_pack.c
+	bio_asn1.c
+	bio_ndef.c
+	f_enum.c
+	f_int.c
+	f_string.c
+	t_bitst.c
+	t_pkey.c
+	tasn_dec.c
+	tasn_enc.c
+	tasn_fre.c
+	tasn_new.c
+	tasn_prn.c
+	tasn_typ.c
+	tasn_utl.c
+	x_bignum.c
+	x_long.c
+)
diff --git a/crypto/asn1/a_bitstr.c b/crypto/asn1/a_bitstr.c
new file mode 100644
index 0000000..4917bda
--- /dev/null
+++ b/crypto/asn1/a_bitstr.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)
+{ return M_ASN1_BIT_STRING_set(x, d, len); }
+
+int i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
+	{
+	int ret,j,bits,len;
+	unsigned char *p,*d;
+
+	if (a == NULL) return(0);
+
+	len=a->length;
+
+	if (len > 0)
+		{
+		if (a->flags & ASN1_STRING_FLAG_BITS_LEFT)
+			{
+			bits=(int)a->flags&0x07;
+			}
+		else
+			{
+			for ( ; len > 0; len--)
+				{
+				if (a->data[len-1]) break;
+				}
+			j=a->data[len-1];
+			if      (j & 0x01) bits=0;
+			else if (j & 0x02) bits=1;
+			else if (j & 0x04) bits=2;
+			else if (j & 0x08) bits=3;
+			else if (j & 0x10) bits=4;
+			else if (j & 0x20) bits=5;
+			else if (j & 0x40) bits=6;
+			else if (j & 0x80) bits=7;
+			else bits=0; /* should not happen */
+			}
+		}
+	else
+		bits=0;
+
+	ret=1+len;
+	if (pp == NULL) return(ret);
+
+	p= *pp;
+
+	*(p++)=(unsigned char)bits;
+	d=a->data;
+	memcpy(p,d,len);
+	p+=len;
+	if (len > 0) p[-1]&=(0xff<<bits);
+	*pp=p;
+	return(ret);
+	}
+
+ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
+	const unsigned char **pp, long len)
+	{
+	ASN1_BIT_STRING *ret=NULL;
+	const unsigned char *p;
+	unsigned char *s;
+	int i;
+
+	if (len < 1)
+		{
+		i=ASN1_R_STRING_TOO_SHORT;
+		goto err;
+		}
+
+	if ((a == NULL) || ((*a) == NULL))
+		{
+		if ((ret=M_ASN1_BIT_STRING_new()) == NULL) return(NULL);
+		}
+	else
+		ret=(*a);
+
+	p= *pp;
+	i= *(p++);
+	/* We do this to preserve the settings.  If we modify
+	 * the settings, via the _set_bit function, we will recalculate
+	 * on output */
+	ret->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */
+	ret->flags|=(ASN1_STRING_FLAG_BITS_LEFT|(i&0x07)); /* set */
+
+	if (len-- > 1) /* using one because of the bits left byte */
+		{
+		s=(unsigned char *)OPENSSL_malloc((int)len);
+		if (s == NULL)
+			{
+			i=ERR_R_MALLOC_FAILURE;
+			goto err;
+			}
+		memcpy(s,p,(int)len);
+		s[len-1]&=(0xff<<i);
+		p+=len;
+		}
+	else
+		s=NULL;
+
+	ret->length=(int)len;
+	if (ret->data != NULL) OPENSSL_free(ret->data);
+	ret->data=s;
+	ret->type=V_ASN1_BIT_STRING;
+	if (a != NULL) (*a)=ret;
+	*pp=p;
+	return(ret);
+err:
+	OPENSSL_PUT_ERROR(ASN1, c2i_ASN1_BIT_STRING, i);
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		M_ASN1_BIT_STRING_free(ret);
+	return(NULL);
+	}
+
+/* These next 2 functions from Goetz Babin-Ebell <babinebell@trustcenter.de>
+ */
+int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value)
+	{
+	int w,v,iv;
+	unsigned char *c;
+
+	w=n/8;
+	v=1<<(7-(n&0x07));
+	iv= ~v;
+	if (!value) v=0;
+
+	if (a == NULL)
+		return 0;
+
+	a->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear, set on write */
+
+	if ((a->length < (w+1)) || (a->data == NULL))
+		{
+		if (!value) return(1); /* Don't need to set */
+		if (a->data == NULL)
+			c=(unsigned char *)OPENSSL_malloc(w+1);
+		else
+			c=(unsigned char *)OPENSSL_realloc_clean(a->data,
+								 a->length,
+								 w+1);
+		if (c == NULL)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_BIT_STRING_set_bit, ERR_R_MALLOC_FAILURE);
+			return 0;
+			}
+  		if (w+1-a->length > 0) memset(c+a->length, 0, w+1-a->length);
+		a->data=c;
+		a->length=w+1;
+	}
+	a->data[w]=((a->data[w])&iv)|v;
+	while ((a->length > 0) && (a->data[a->length-1] == 0))
+		a->length--;
+	return(1);
+	}
+
+int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *a, int n)
+	{
+	int w,v;
+
+	w=n/8;
+	v=1<<(7-(n&0x07));
+	if ((a == NULL) || (a->length < (w+1)) || (a->data == NULL))
+		return(0);
+	return((a->data[w]&v) != 0);
+	}
+
+/*
+ * Checks if the given bit string contains only bits specified by 
+ * the flags vector. Returns 0 if there is at least one bit set in 'a'
+ * which is not specified in 'flags', 1 otherwise.
+ * 'len' is the length of 'flags'.
+ */
+int ASN1_BIT_STRING_check(ASN1_BIT_STRING *a,
+			  unsigned char *flags, int flags_len)
+	{
+	int i, ok;
+	/* Check if there is one bit set at all. */
+	if (!a || !a->data) return 1;
+
+	/* Check each byte of the internal representation of the bit string. */
+	ok = 1;
+	for (i = 0; i < a->length && ok; ++i)
+		{
+		unsigned char mask = i < flags_len ? ~flags[i] : 0xff;
+		/* We are done if there is an unneeded bit set. */
+		ok = (a->data[i] & mask) == 0;
+		}
+	return ok;
+	}
diff --git a/crypto/asn1/a_bool.c b/crypto/asn1/a_bool.c
new file mode 100644
index 0000000..c30ee48
--- /dev/null
+++ b/crypto/asn1/a_bool.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+int i2d_ASN1_BOOLEAN(int a, unsigned char **pp)
+	{
+	int r;
+	unsigned char *p;
+
+	r=ASN1_object_size(0,1,V_ASN1_BOOLEAN);
+	if (pp == NULL) return(r);
+	p= *pp;
+
+	ASN1_put_object(&p,0,1,V_ASN1_BOOLEAN,V_ASN1_UNIVERSAL);
+	*(p++)= (unsigned char)a;
+	*pp=p;
+	return(r);
+	}
+
+int d2i_ASN1_BOOLEAN(int *a, const unsigned char **pp, long length)
+	{
+	int ret= -1;
+	const unsigned char *p;
+	long len;
+	int inf,tag,xclass;
+	int i=0;
+
+	p= *pp;
+	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
+	if (inf & 0x80)
+		{
+		i=ASN1_R_BAD_OBJECT_HEADER;
+		goto err;
+		}
+
+	if (tag != V_ASN1_BOOLEAN)
+		{
+		i=ASN1_R_EXPECTING_A_BOOLEAN;
+		goto err;
+		}
+
+	if (len != 1)
+		{
+		i=ASN1_R_BOOLEAN_IS_WRONG_LENGTH;
+		goto err;
+		}
+	ret= (int)*(p++);
+	if (a != NULL) (*a)=ret;
+	*pp=p;
+	return(ret);
+err:
+	OPENSSL_PUT_ERROR(ASN1, d2i_ASN1_BOOLEAN, i);
+	return(ret);
+	}
diff --git a/crypto/asn1/a_bytes.c b/crypto/asn1/a_bytes.c
new file mode 100644
index 0000000..b9dd816
--- /dev/null
+++ b/crypto/asn1/a_bytes.c
@@ -0,0 +1,315 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+static int asn1_collate_primitive(ASN1_STRING *a, ASN1_const_CTX *c);
+/* type is a 'bitmap' of acceptable string types.
+ */
+ASN1_STRING *d2i_ASN1_type_bytes(ASN1_STRING **a, const unsigned char **pp,
+	     long length, int type)
+	{
+	ASN1_STRING *ret=NULL;
+	const unsigned char *p;
+	unsigned char *s;
+	long len;
+	int inf,tag,xclass;
+	int i=0;
+
+	p= *pp;
+	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
+	if (inf & 0x80) goto err;
+
+	if (tag >= 32)
+		{
+		i=ASN1_R_TAG_VALUE_TOO_HIGH;
+		goto err;
+		}
+	if (!(ASN1_tag2bit(tag) & type))
+		{
+		i=ASN1_R_WRONG_TYPE;
+		goto err;
+		}
+
+	/* If a bit-string, exit early */
+	if (tag == V_ASN1_BIT_STRING)
+		return(d2i_ASN1_BIT_STRING(a,pp,length));
+
+	if ((a == NULL) || ((*a) == NULL))
+		{
+		if ((ret=ASN1_STRING_new()) == NULL) return(NULL);
+		}
+	else
+		ret=(*a);
+
+	if (len != 0)
+		{
+		s=(unsigned char *)OPENSSL_malloc((int)len+1);
+		if (s == NULL)
+			{
+			i=ERR_R_MALLOC_FAILURE;
+			goto err;
+			}
+		memcpy(s,p,(int)len);
+		s[len]='\0';
+		p+=len;
+		}
+	else
+		s=NULL;
+
+	if (ret->data != NULL) OPENSSL_free(ret->data);
+	ret->length=(int)len;
+	ret->data=s;
+	ret->type=tag;
+	if (a != NULL) (*a)=ret;
+	*pp=p;
+	return(ret);
+err:
+	OPENSSL_PUT_ERROR(ASN1, d2i_ASN1_type_bytes, i);
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		ASN1_STRING_free(ret);
+	return(NULL);
+	}
+
+int i2d_ASN1_bytes(ASN1_STRING *a, unsigned char **pp, int tag, int xclass)
+	{
+	int ret,r,constructed;
+	unsigned char *p;
+
+	if (a == NULL)  return(0);
+
+	if (tag == V_ASN1_BIT_STRING)
+		return(i2d_ASN1_BIT_STRING(a,pp));
+		
+	ret=a->length;
+	r=ASN1_object_size(0,ret,tag);
+	if (pp == NULL) return(r);
+	p= *pp;
+
+	if ((tag == V_ASN1_SEQUENCE) || (tag == V_ASN1_SET))
+		constructed=1;
+	else
+		constructed=0;
+	ASN1_put_object(&p,constructed,ret,tag,xclass);
+	memcpy(p,a->data,a->length);
+	p+=a->length;
+	*pp= p;
+	return(r);
+	}
+
+ASN1_STRING *d2i_ASN1_bytes(ASN1_STRING **a, const unsigned char **pp,
+	     long length, int Ptag, int Pclass)
+	{
+	ASN1_STRING *ret=NULL;
+	const unsigned char *p;
+	unsigned char *s;
+	long len;
+	int inf,tag,xclass;
+	int i=0;
+
+	if ((a == NULL) || ((*a) == NULL))
+		{
+		if ((ret=ASN1_STRING_new()) == NULL) return(NULL);
+		}
+	else
+		ret=(*a);
+
+	p= *pp;
+	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
+	if (inf & 0x80)
+		{
+		i=ASN1_R_BAD_OBJECT_HEADER;
+		goto err;
+		}
+
+	if (tag != Ptag)
+		{
+		i=ASN1_R_WRONG_TAG;
+		goto err;
+		}
+
+	if (inf & V_ASN1_CONSTRUCTED)
+		{
+		ASN1_const_CTX c;
+
+		c.pp=pp;
+		c.p=p;
+		c.inf=inf;
+		c.slen=len;
+		c.tag=Ptag;
+		c.xclass=Pclass;
+		c.max=(length == 0)?0:(p+length);
+		if (!asn1_collate_primitive(ret,&c)) 
+			goto err; 
+		else
+			{
+			p=c.p;
+			}
+		}
+	else
+		{
+		if (len != 0)
+			{
+			if ((ret->length < len) || (ret->data == NULL))
+				{
+				if (ret->data != NULL) OPENSSL_free(ret->data);
+				s=(unsigned char *)OPENSSL_malloc((int)len + 1);
+				if (s == NULL)
+					{
+					i=ERR_R_MALLOC_FAILURE;
+					goto err;
+					}
+				}
+			else
+				s=ret->data;
+			memcpy(s,p,(int)len);
+			s[len] = '\0';
+			p+=len;
+			}
+		else
+			{
+			s=NULL;
+			if (ret->data != NULL) OPENSSL_free(ret->data);
+			}
+
+		ret->length=(int)len;
+		ret->data=s;
+		ret->type=Ptag;
+		}
+
+	if (a != NULL) (*a)=ret;
+	*pp=p;
+	return(ret);
+err:
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		ASN1_STRING_free(ret);
+	OPENSSL_PUT_ERROR(ASN1, d2i_ASN1_bytes, i);
+	return(NULL);
+	}
+
+
+/* We are about to parse 0..n d2i_ASN1_bytes objects, we are to collapse
+ * them into the one structure that is then returned */
+/* There have been a few bug fixes for this function from
+ * Paul Keogh <paul.keogh@sse.ie>, many thanks to him */
+static int asn1_collate_primitive(ASN1_STRING *a, ASN1_const_CTX *c)
+	{
+	ASN1_STRING *os=NULL;
+	BUF_MEM b;
+	int num;
+
+	b.length=0;
+	b.max=0;
+	b.data=NULL;
+
+	if (a == NULL)
+		{
+		c->error=ERR_R_PASSED_NULL_PARAMETER;
+		goto err;
+		}
+
+	num=0;
+	for (;;)
+		{
+		if (c->inf & 1)
+			{
+			c->eos=ASN1_const_check_infinite_end(&c->p,
+				(long)(c->max-c->p));
+			if (c->eos) break;
+			}
+		else
+			{
+			if (c->slen <= 0) break;
+			}
+
+		c->q=c->p;
+		if (d2i_ASN1_bytes(&os,&c->p,c->max-c->p,c->tag,c->xclass)
+			== NULL)
+			{
+			c->error=ERR_R_ASN1_LIB;
+			goto err;
+			}
+
+		if (!BUF_MEM_grow_clean(&b,num+os->length))
+			{
+			c->error=ERR_R_BUF_LIB;
+			goto err;
+			}
+		memcpy(&(b.data[num]),os->data,os->length);
+		if (!(c->inf & 1))
+			c->slen-=(c->p-c->q);
+		num+=os->length;
+		}
+
+	if (!asn1_const_Finish(c)) goto err;
+
+	a->length=num;
+	if (a->data != NULL) OPENSSL_free(a->data);
+	a->data=(unsigned char *)b.data;
+	if (os != NULL) ASN1_STRING_free(os);
+	return(1);
+err:
+	OPENSSL_PUT_ERROR(ASN1, asn1_collate_primitive, c->error);
+	if (os != NULL) ASN1_STRING_free(os);
+	if (b.data != NULL) OPENSSL_free(b.data);
+	return(0);
+	}
+
diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
new file mode 100644
index 0000000..c28532b
--- /dev/null
+++ b/crypto/asn1/a_d2i_fp.c
@@ -0,0 +1,286 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <limits.h>
+
+#include <openssl/buf.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, ASN1_d2i_fp, 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);
+	}
+
+#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)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_d2i_fp, ERR_R_BUF_LIB);
+                return(NULL);
+		}
+        BIO_set_fp(b,in,BIO_NOCLOSE);
+        ret=ASN1_item_d2i_bio(it,b,x);
+        BIO_free(b);
+        return(ret);
+        }
+#endif
+
+#define HEADER_SIZE   8
+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, asn1_d2i_read_bio, 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, asn1_d2i_read_bio, 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_d2i_read_bio, ASN1_R_NOT_ENOUGH_DATA);
+				goto err;
+				}
+			if (i > 0)
+				{
+				if (len+i < len)
+					{
+					OPENSSL_PUT_ERROR(ASN1, asn1_d2i_read_bio, 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)
+			{
+			unsigned long 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_d2i_read_bio, 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))
+				{
+				want-=(len-off);
+				if (want > INT_MAX /* BIO_read takes an int length */ ||
+					len+want < len)
+						{
+						OPENSSL_PUT_ERROR(ASN1, asn1_d2i_read_bio, ASN1_R_TOO_LONG);
+						goto err;
+						}
+				if (!BUF_MEM_grow_clean(b,len+want))
+					{
+					OPENSSL_PUT_ERROR(ASN1, asn1_d2i_read_bio, ERR_R_MALLOC_FAILURE);
+					goto err;
+					}
+				while (want > 0)
+					{
+					i=BIO_read(in,&(b->data[len]),want);
+					if (i <= 0)
+						{
+						OPENSSL_PUT_ERROR(ASN1, asn1_d2i_read_bio, ASN1_R_NOT_ENOUGH_DATA);
+						goto err;
+						}
+					/* This can't overflow because
+					 * |len+want| didn't overflow. */
+					len+=i;
+					want-=i;
+					}
+				}
+			if (off + c.slen < off)
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_d2i_read_bio, 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_d2i_read_bio, ASN1_R_TOO_LONG);
+		goto err;
+		}
+
+	*pb = b;
+	return off;
+err:
+	if (b != NULL) BUF_MEM_free(b);
+	return -1;
+	}
diff --git a/crypto/asn1/a_dup.c b/crypto/asn1/a_dup.c
new file mode 100644
index 0000000..8ec1c5f
--- /dev/null
+++ b/crypto/asn1/a_dup.c
@@ -0,0 +1,103 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <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, ASN1_dup, 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 directly dup
+ * the underlying structure instead of doing and encode and decode. */
+void *ASN1_item_dup(const ASN1_ITEM *it, void *x)
+	{
+	unsigned char *b = NULL;
+	const unsigned char *p;
+	long i;
+	void *ret;
+
+	if (x == NULL) return(NULL);
+
+	i=ASN1_item_i2d(x,&b,it);
+	if (b == NULL)
+		{ OPENSSL_PUT_ERROR(ASN1, ASN1_item_dup, ERR_R_MALLOC_FAILURE); return(NULL); }
+	p= b;
+	ret=ASN1_item_d2i(NULL,&p,i, it);
+	OPENSSL_free(b);
+	return(ret);
+	}
diff --git a/crypto/asn1/a_enum.c b/crypto/asn1/a_enum.c
new file mode 100644
index 0000000..d6d237a
--- /dev/null
+++ b/crypto/asn1/a_enum.c
@@ -0,0 +1,181 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+/* 
+ * Code for ENUMERATED type: identical to INTEGER apart from a different tag.
+ * for comments on encoding see a_int.c
+ */
+
+int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)
+	{
+	int j,k;
+	unsigned int i;
+	unsigned char buf[sizeof(long)+1];
+	long d;
+
+	a->type=V_ASN1_ENUMERATED;
+	if (a->length < (int)(sizeof(long)+1))
+		{
+		if (a->data != NULL)
+			OPENSSL_free(a->data);
+		if ((a->data=(unsigned char *)OPENSSL_malloc(sizeof(long)+1)) != NULL)
+			memset((char *)a->data,0,sizeof(long)+1);
+		}
+	if (a->data == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_ENUMERATED_set, ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+	d=v;
+	if (d < 0)
+		{
+		d= -d;
+		a->type=V_ASN1_NEG_ENUMERATED;
+		}
+
+	for (i=0; i<sizeof(long); i++)
+		{
+		if (d == 0) break;
+		buf[i]=(int)d&0xff;
+		d>>=8;
+		}
+	j=0;
+	for (k=i-1; k >=0; k--)
+		a->data[j++]=buf[k];
+	a->length=j;
+	return(1);
+	}
+
+long ASN1_ENUMERATED_get(ASN1_ENUMERATED *a)
+	{
+	int neg=0,i;
+	long r=0;
+
+	if (a == NULL) return(0L);
+	i=a->type;
+	if (i == V_ASN1_NEG_ENUMERATED)
+		neg=1;
+	else if (i != V_ASN1_ENUMERATED)
+		return -1;
+	
+	if (a->length > (int)sizeof(long))
+		{
+		/* hmm... a bit ugly */
+		return(0xffffffffL);
+		}
+	if (a->data == NULL)
+		return 0;
+
+	for (i=0; i<a->length; i++)
+		{
+		r<<=8;
+		r|=(unsigned char)a->data[i];
+		}
+	if (neg) r= -r;
+	return(r);
+	}
+
+ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(BIGNUM *bn, ASN1_ENUMERATED *ai)
+	{
+	ASN1_ENUMERATED *ret;
+	int len,j;
+
+	if (ai == NULL)
+		ret=M_ASN1_ENUMERATED_new();
+	else
+		ret=ai;
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, BN_to_ASN1_ENUMERATED, ASN1_R_NESTED_ASN1_ERROR);
+		goto err;
+		}
+	if(BN_is_negative(bn)) ret->type = V_ASN1_NEG_ENUMERATED;
+	else ret->type=V_ASN1_ENUMERATED;
+	j=BN_num_bits(bn);
+	len=((j == 0)?0:((j/8)+1));
+	if (ret->length < len+4)
+		{
+		unsigned char *new_data=OPENSSL_realloc(ret->data, len+4);
+		if (!new_data)
+			{
+			OPENSSL_PUT_ERROR(ASN1, BN_to_ASN1_ENUMERATED, ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+		ret->data=new_data;
+		}
+
+	ret->length=BN_bn2bin(bn,ret->data);
+	return(ret);
+err:
+	if (ret != ai) M_ASN1_ENUMERATED_free(ret);
+	return(NULL);
+	}
+
+BIGNUM *ASN1_ENUMERATED_to_BN(ASN1_ENUMERATED *ai, BIGNUM *bn)
+	{
+	BIGNUM *ret;
+
+	if ((ret=BN_bin2bn(ai->data,ai->length,bn)) == NULL)
+		OPENSSL_PUT_ERROR(ASN1, ASN1_ENUMERATED_to_BN, ASN1_R_BN_LIB);
+	else if(ai->type == V_ASN1_NEG_ENUMERATED) BN_set_negative(ret,1);
+	return(ret);
+	}
diff --git a/crypto/asn1/a_gentm.c b/crypto/asn1/a_gentm.c
new file mode 100644
index 0000000..1b8d78e
--- /dev/null
+++ b/crypto/asn1/a_gentm.c
@@ -0,0 +1,255 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/time_support.h>
+
+
+int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
+	{
+	static const int min[9]={ 0, 0, 1, 1, 0, 0, 0, 0, 0};
+	static const int max[9]={99, 99,12,31,23,59,59,12,59};
+	char *a;
+	int n,i,l,o;
+
+	if (d->type != V_ASN1_GENERALIZEDTIME) return(0);
+	l=d->length;
+	a=(char *)d->data;
+	o=0;
+	/* GENERALIZEDTIME is similar to UTCTIME except the year is
+         * represented as YYYY. This stuff treats everything as a two digit
+         * field so make first two fields 00 to 99
+         */
+	if (l < 13) goto err;
+	for (i=0; i<7; i++)
+		{
+		if ((i == 6) && ((a[o] == 'Z') ||
+			(a[o] == '+') || (a[o] == '-')))
+			{
+			i++;
+			if (tm)
+				tm->tm_sec = 0;
+			break;
+			}
+		if ((a[o] < '0') || (a[o] > '9')) goto err;
+		n= a[o]-'0';
+		if (++o > l) goto err;
+
+		if ((a[o] < '0') || (a[o] > '9')) goto err;
+		n=(n*10)+ a[o]-'0';
+		if (++o > l) goto err;
+
+		if ((n < min[i]) || (n > max[i])) goto err;
+		if (tm)
+			{
+			switch(i)
+				{
+			case 0:
+				tm->tm_year = n * 100 - 1900;
+				break;
+			case 1:
+				tm->tm_year += n;
+				break;
+			case 2:
+				tm->tm_mon = n - 1;
+				break;
+			case 3:
+				tm->tm_mday = n;
+				break;
+			case 4:
+				tm->tm_hour = n;
+				break;
+			case 5:
+				tm->tm_min = n;
+				break;
+			case 6:
+				tm->tm_sec = n;
+				break;
+				}
+			}
+		}
+	/* Optional fractional seconds: decimal point followed by one
+	 * or more digits.
+	 */
+	if (a[o] == '.')
+		{
+		if (++o > l) goto err;
+		i = o;
+		while ((a[o] >= '0') && (a[o] <= '9') && (o <= l))
+			o++;
+		/* Must have at least one digit after decimal point */
+		if (i == o) goto err;
+		}
+
+	if (a[o] == 'Z')
+		o++;
+	else if ((a[o] == '+') || (a[o] == '-'))
+		{
+		int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+		o++;
+		if (o+4 > l) goto err;
+		for (i=7; i<9; i++)
+			{
+			if ((a[o] < '0') || (a[o] > '9')) goto err;
+			n= a[o]-'0';
+			o++;
+			if ((a[o] < '0') || (a[o] > '9')) goto err;
+			n=(n*10)+ a[o]-'0';
+			if ((n < min[i]) || (n > max[i])) goto err;
+			if (tm)
+				{
+				if (i == 7)
+					offset = n * 3600;
+				else if (i == 8)
+					offset += n * 60;
+				}
+			o++;
+			}
+		if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign))
+			return 0;
+		}
+	else if (a[o])
+		{
+		/* Missing time zone information. */
+		goto err;
+		}
+	return(o == l);
+err:
+	return(0);
+	}
+
+int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *d)
+	{
+	return asn1_generalizedtime_to_tm(NULL, d);
+	}
+
+int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str)
+	{
+	ASN1_GENERALIZEDTIME t;
+
+	t.type=V_ASN1_GENERALIZEDTIME;
+	t.length=strlen(str);
+	t.data=(unsigned char *)str;
+	if (ASN1_GENERALIZEDTIME_check(&t))
+		{
+		if (s != NULL)
+			{
+			if (!ASN1_STRING_set((ASN1_STRING *)s,
+				(unsigned char *)str,t.length))
+				return 0;
+			s->type=V_ASN1_GENERALIZEDTIME;
+			}
+		return(1);
+		}
+	else
+		return(0);
+	}
+
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s,
+	     time_t t)
+	{
+		return ASN1_GENERALIZEDTIME_adj(s, t, 0, 0);
+	}
+
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
+	     time_t t, int offset_day, long offset_sec)
+	{
+	char *p;
+	struct tm *ts;
+	struct tm data;
+	size_t len = 20; 
+
+	if (s == NULL)
+		s=M_ASN1_GENERALIZEDTIME_new();
+	if (s == NULL)
+		return(NULL);
+
+	ts=OPENSSL_gmtime(&t, &data);
+	if (ts == NULL)
+		return(NULL);
+
+	if (offset_day || offset_sec)
+		{ 
+		if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+			return NULL;
+		}
+
+	p=(char *)s->data;
+	if ((p == NULL) || ((size_t)s->length < len))
+		{
+		p=OPENSSL_malloc(len);
+		if (p == NULL)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_GENERALIZEDTIME_adj, ERR_R_MALLOC_FAILURE);
+			return(NULL);
+			}
+		if (s->data != NULL)
+			OPENSSL_free(s->data);
+		s->data=(unsigned char *)p;
+		}
+
+	BIO_snprintf(p,len,"%04d%02d%02d%02d%02d%02dZ",ts->tm_year + 1900,
+		     ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
+	s->length=strlen(p);
+	s->type=V_ASN1_GENERALIZEDTIME;
+#ifdef CHARSET_EBCDIC_not
+	ebcdic2ascii(s->data, s->data, s->length);
+#endif
+	return(s);
+	}
diff --git a/crypto/asn1/a_i2d_fp.c b/crypto/asn1/a_i2d_fp.c
new file mode 100644
index 0000000..11e40d3
--- /dev/null
+++ b/crypto/asn1/a_i2d_fp.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <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, ASN1_i2d_fp, 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, unsigned char *x)
+	{
+	char *b;
+	unsigned char *p;
+	int i,j=0,n,ret=1;
+
+	n=i2d(x,NULL);
+	b=(char *)OPENSSL_malloc(n);
+	if (b == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_i2d_bio, 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)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_i2d_fp, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,out,BIO_NOCLOSE);
+        ret=ASN1_item_i2d_bio(it,b,x);
+        BIO_free(b);
+        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);
+	if (b == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_i2d_bio, ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+
+	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);
+	}
diff --git a/crypto/asn1/a_int.c b/crypto/asn1/a_int.c
new file mode 100644
index 0000000..6d7e225
--- /dev/null
+++ b/crypto/asn1/a_int.c
@@ -0,0 +1,454 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+ASN1_INTEGER *ASN1_INTEGER_dup(const ASN1_INTEGER *x)
+{ return M_ASN1_INTEGER_dup(x);}
+
+int ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y)
+	{ 
+	int neg, ret;
+	/* Compare signs */
+	neg = x->type & V_ASN1_NEG;
+	if (neg != (y->type & V_ASN1_NEG))
+		{
+		if (neg)
+			return -1;
+		else
+			return 1;
+		}
+
+	ret = ASN1_STRING_cmp(x, y);
+
+	if (neg)
+		return -ret;
+	else
+		return ret;
+	}
+	
+
+/* 
+ * This converts an ASN1 INTEGER into its content encoding.
+ * The internal representation is an ASN1_STRING whose data is a big endian
+ * representation of the value, ignoring the sign. The sign is determined by
+ * the type: V_ASN1_INTEGER for positive and V_ASN1_NEG_INTEGER for negative. 
+ *
+ * Positive integers are no problem: they are almost the same as the DER
+ * encoding, except if the first byte is >= 0x80 we need to add a zero pad.
+ *
+ * Negative integers are a bit trickier...
+ * The DER representation of negative integers is in 2s complement form.
+ * The internal form is converted by complementing each octet and finally 
+ * adding one to the result. This can be done less messily with a little trick.
+ * If the internal form has trailing zeroes then they will become FF by the
+ * complement and 0 by the add one (due to carry) so just copy as many trailing 
+ * zeros to the destination as there are in the source. The carry will add one
+ * to the last none zero octet: so complement this octet and add one and finally
+ * complement any left over until you get to the start of the string.
+ *
+ * Padding is a little trickier too. If the first bytes is > 0x80 then we pad
+ * with 0xff. However if the first byte is 0x80 and one of the following bytes
+ * is non-zero we pad with 0xff. The reason for this distinction is that 0x80
+ * followed by optional zeros isn't padded.
+ */
+
+int i2c_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
+	{
+	int pad=0,ret,i,neg;
+	unsigned char *p,*n,pb=0;
+
+	if (a == NULL) return(0);
+	neg=a->type & V_ASN1_NEG;
+	if (a->length == 0)
+		ret=1;
+	else
+		{
+		ret=a->length;
+		i=a->data[0];
+		if (!neg && (i > 127)) {
+			pad=1;
+			pb=0;
+		} else if(neg) {
+			if(i>128) {
+				pad=1;
+				pb=0xFF;
+			} else if(i == 128) {
+			/*
+			 * Special case: if any other bytes non zero we pad:
+			 * otherwise we don't.
+			 */
+				for(i = 1; i < a->length; i++) if(a->data[i]) {
+						pad=1;
+						pb=0xFF;
+						break;
+				}
+			}
+		}
+		ret+=pad;
+		}
+	if (pp == NULL) return(ret);
+	p= *pp;
+
+	if (pad) *(p++)=pb;
+	if (a->length == 0) *(p++)=0;
+	else if (!neg) memcpy(p,a->data,(unsigned int)a->length);
+	else {
+		/* Begin at the end of the encoding */
+		n=a->data + a->length - 1;
+		p += a->length - 1;
+		i = a->length;
+		/* Copy zeros to destination as long as source is zero */
+		while(!*n) {
+			*(p--) = 0;
+			n--;
+			i--;
+		}
+		/* Complement and increment next octet */
+		*(p--) = ((*(n--)) ^ 0xff) + 1;
+		i--;
+		/* Complement any octets left */
+		for(;i > 0; i--) *(p--) = *(n--) ^ 0xff;
+	}
+
+	*pp+=ret;
+	return(ret);
+	}
+
+/* Convert just ASN1 INTEGER content octets to ASN1_INTEGER structure */
+
+ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **pp,
+	     long len)
+	{
+	ASN1_INTEGER *ret=NULL;
+	const unsigned char *p, *pend;
+	unsigned char *to,*s;
+	int i;
+
+	if ((a == NULL) || ((*a) == NULL))
+		{
+		if ((ret=M_ASN1_INTEGER_new()) == NULL) return(NULL);
+		ret->type=V_ASN1_INTEGER;
+		}
+	else
+		ret=(*a);
+
+	p= *pp;
+	pend = p + len;
+
+	/* We must OPENSSL_malloc stuff, even for 0 bytes otherwise it
+	 * signifies a missing NULL parameter. */
+	s=(unsigned char *)OPENSSL_malloc((int)len+1);
+	if (s == NULL)
+		{
+		i=ERR_R_MALLOC_FAILURE;
+		goto err;
+		}
+	to=s;
+	if(!len) {
+		/* Strictly speaking this is an illegal INTEGER but we
+		 * tolerate it.
+		 */
+		ret->type=V_ASN1_INTEGER;
+	} else if (*p & 0x80) /* a negative number */
+		{
+		ret->type=V_ASN1_NEG_INTEGER;
+		if ((*p == 0xff) && (len != 1)) {
+			p++;
+			len--;
+		}
+		i = len;
+		p += i - 1;
+		to += i - 1;
+		while((!*p) && i) {
+			*(to--) = 0;
+			i--;
+			p--;
+		}
+		/* Special case: if all zeros then the number will be of
+		 * the form FF followed by n zero bytes: this corresponds to
+		 * 1 followed by n zero bytes. We've already written n zeros
+		 * so we just append an extra one and set the first byte to
+		 * a 1. This is treated separately because it is the only case
+		 * where the number of bytes is larger than len.
+		 */
+		if(!i) {
+			*s = 1;
+			s[len] = 0;
+			len++;
+		} else {
+			*(to--) = (*(p--) ^ 0xff) + 1;
+			i--;
+			for(;i > 0; i--) *(to--) = *(p--) ^ 0xff;
+		}
+	} else {
+		ret->type=V_ASN1_INTEGER;
+		if ((*p == 0) && (len != 1))
+			{
+			p++;
+			len--;
+			}
+		memcpy(s,p,(int)len);
+	}
+
+	if (ret->data != NULL) OPENSSL_free(ret->data);
+	ret->data=s;
+	ret->length=(int)len;
+	if (a != NULL) (*a)=ret;
+	*pp=pend;
+	return(ret);
+err:
+	OPENSSL_PUT_ERROR(ASN1, c2i_ASN1_INTEGER, i);
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		M_ASN1_INTEGER_free(ret);
+	return(NULL);
+	}
+
+
+/* This is a version of d2i_ASN1_INTEGER that ignores the sign bit of
+ * ASN1 integers: some broken software can encode a positive INTEGER
+ * with its MSB set as negative (it doesn't add a padding zero).
+ */
+
+ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a, const unsigned char **pp,
+	     long length)
+	{
+	ASN1_INTEGER *ret=NULL;
+	const unsigned char *p;
+	unsigned char *s;
+	long len;
+	int inf,tag,xclass;
+	int i;
+
+	if ((a == NULL) || ((*a) == NULL))
+		{
+		if ((ret=M_ASN1_INTEGER_new()) == NULL) return(NULL);
+		ret->type=V_ASN1_INTEGER;
+		}
+	else
+		ret=(*a);
+
+	p= *pp;
+	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
+	if (inf & 0x80)
+		{
+		i=ASN1_R_BAD_OBJECT_HEADER;
+		goto err;
+		}
+
+	if (tag != V_ASN1_INTEGER)
+		{
+		i=ASN1_R_EXPECTING_AN_INTEGER;
+		goto err;
+		}
+
+	/* We must OPENSSL_malloc stuff, even for 0 bytes otherwise it
+	 * signifies a missing NULL parameter. */
+	s=(unsigned char *)OPENSSL_malloc((int)len+1);
+	if (s == NULL)
+		{
+		i=ERR_R_MALLOC_FAILURE;
+		goto err;
+		}
+	ret->type=V_ASN1_INTEGER;
+	if(len) {
+		if ((*p == 0) && (len != 1))
+			{
+			p++;
+			len--;
+			}
+		memcpy(s,p,(int)len);
+		p+=len;
+	}
+
+	if (ret->data != NULL) OPENSSL_free(ret->data);
+	ret->data=s;
+	ret->length=(int)len;
+	if (a != NULL) (*a)=ret;
+	*pp=p;
+	return(ret);
+err:
+	OPENSSL_PUT_ERROR(ASN1, d2i_ASN1_UINTEGER, i);
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		M_ASN1_INTEGER_free(ret);
+	return(NULL);
+	}
+
+int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
+	{
+	int j,k;
+	unsigned int i;
+	unsigned char buf[sizeof(long)+1];
+	long d;
+
+	a->type=V_ASN1_INTEGER;
+	if (a->length < (int)(sizeof(long)+1))
+		{
+		if (a->data != NULL)
+			OPENSSL_free(a->data);
+		if ((a->data=(unsigned char *)OPENSSL_malloc(sizeof(long)+1)) != NULL)
+			memset((char *)a->data,0,sizeof(long)+1);
+		}
+	if (a->data == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_INTEGER_set, ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+	d=v;
+	if (d < 0)
+		{
+		d= -d;
+		a->type=V_ASN1_NEG_INTEGER;
+		}
+
+	for (i=0; i<sizeof(long); i++)
+		{
+		if (d == 0) break;
+		buf[i]=(int)d&0xff;
+		d>>=8;
+		}
+	j=0;
+	for (k=i-1; k >=0; k--)
+		a->data[j++]=buf[k];
+	a->length=j;
+	return(1);
+	}
+
+long ASN1_INTEGER_get(const ASN1_INTEGER *a)
+	{
+	int neg=0,i;
+	long r=0;
+
+	if (a == NULL) return(0L);
+	i=a->type;
+	if (i == V_ASN1_NEG_INTEGER)
+		neg=1;
+	else if (i != V_ASN1_INTEGER)
+		return -1;
+	
+	if (a->length > (int)sizeof(long))
+		{
+		/* hmm... a bit ugly, return all ones */
+		return -1;
+		}
+	if (a->data == NULL)
+		return 0;
+
+	for (i=0; i<a->length; i++)
+		{
+		r<<=8;
+		r|=(unsigned char)a->data[i];
+		}
+	if (neg) r= -r;
+	return(r);
+	}
+
+ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai)
+	{
+	ASN1_INTEGER *ret;
+	int len,j;
+
+	if (ai == NULL)
+		ret=M_ASN1_INTEGER_new();
+	else
+		ret=ai;
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, BN_to_ASN1_INTEGER, ASN1_R_NESTED_ASN1_ERROR);
+		goto err;
+		}
+	if (BN_is_negative(bn))
+		ret->type = V_ASN1_NEG_INTEGER;
+	else ret->type=V_ASN1_INTEGER;
+	j=BN_num_bits(bn);
+	len=((j == 0)?0:((j/8)+1));
+	if (ret->length < len+4)
+		{
+		unsigned char *new_data=OPENSSL_realloc(ret->data, len+4);
+		if (!new_data)
+			{
+			OPENSSL_PUT_ERROR(ASN1, BN_to_ASN1_INTEGER, ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+		ret->data=new_data;
+		}
+	ret->length=BN_bn2bin(bn,ret->data);
+	/* Correct zero case */
+	if(!ret->length)
+		{
+		ret->data[0] = 0;
+		ret->length = 1;
+		}
+	return(ret);
+err:
+	if (ret != ai) M_ASN1_INTEGER_free(ret);
+	return(NULL);
+	}
+
+BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai, BIGNUM *bn)
+	{
+	BIGNUM *ret;
+
+	if ((ret=BN_bin2bn(ai->data,ai->length,bn)) == NULL)
+		OPENSSL_PUT_ERROR(ASN1, ASN1_INTEGER_to_BN, ASN1_R_BN_LIB);
+	else if(ai->type == V_ASN1_NEG_INTEGER)
+		BN_set_negative(ret, 1);
+	return(ret);
+	}
diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c
new file mode 100644
index 0000000..6543f52
--- /dev/null
+++ b/crypto/asn1/a_mbstr.c
@@ -0,0 +1,395 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+static int traverse_string(const unsigned char *p, int len, int inform,
+		 int (*rfunc)(unsigned long value, void *in), void *arg);
+static int in_utf8(unsigned long value, void *arg);
+static int out_utf8(unsigned long value, void *arg);
+static int type_str(unsigned long value, void *arg);
+static int cpy_asc(unsigned long value, void *arg);
+static int cpy_bmp(unsigned long value, void *arg);
+static int cpy_univ(unsigned long value, void *arg);
+static int cpy_utf8(unsigned long value, void *arg);
+static int is_printable(unsigned long value);
+
+/* These functions take a string in UTF8, ASCII or multibyte form and
+ * a mask of permissible ASN1 string types. It then works out the minimal
+ * type (using the order Printable < IA5 < T61 < BMP < Universal < UTF8)
+ * and creates a string of the correct type with the supplied data.
+ * Yes this is horrible: it has to be :-(
+ * The 'ncopy' form checks minimum and maximum size limits too.
+ */
+
+int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
+					int inform, unsigned long mask)
+{
+	return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
+}
+
+int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
+					int inform, unsigned long mask, 
+					long minsize, long maxsize)
+{
+	int str_type;
+	int ret;
+	char free_out;
+	int outform, outlen = 0;
+	ASN1_STRING *dest;
+	unsigned char *p;
+	int nchar;
+	char strbuf[32];
+	int (*cpyfunc)(unsigned long,void *) = NULL;
+	if(len == -1) len = strlen((const char *)in);
+	if(!mask) mask = DIRSTRING_TYPE;
+
+	/* First do a string check and work out the number of characters */
+	switch(inform) {
+
+		case MBSTRING_BMP:
+		if(len & 1) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_INVALID_BMPSTRING_LENGTH);
+			return -1;
+		}
+		nchar = len >> 1;
+		break;
+
+		case MBSTRING_UNIV:
+		if(len & 3) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
+			return -1;
+		}
+		nchar = len >> 2;
+		break;
+
+		case MBSTRING_UTF8:
+		nchar = 0;
+		/* This counts the characters and does utf8 syntax checking */
+		ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
+		if(ret < 0) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_INVALID_UTF8STRING);
+			return -1;
+		}
+		break;
+
+		case MBSTRING_ASC:
+		nchar = len;
+		break;
+
+		default:
+		OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_UNKNOWN_FORMAT);
+		return -1;
+	}
+
+	if((minsize > 0) && (nchar < minsize)) {
+		OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_STRING_TOO_SHORT);
+		BIO_snprintf(strbuf, sizeof strbuf, "%ld", minsize);
+		ERR_add_error_data(2, "minsize=", strbuf);
+		return -1;
+	}
+
+	if((maxsize > 0) && (nchar > maxsize)) {
+		OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_STRING_TOO_LONG);
+		BIO_snprintf(strbuf, sizeof strbuf, "%ld", maxsize);
+		ERR_add_error_data(2, "maxsize=", strbuf);
+		return -1;
+	}
+
+	/* Now work out minimal type (if any) */
+	if(traverse_string(in, len, inform, type_str, &mask) < 0) {
+		OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ASN1_R_ILLEGAL_CHARACTERS);
+		return -1;
+	}
+
+
+	/* Now work out output format and string type */
+	outform = MBSTRING_ASC;
+	if(mask & B_ASN1_PRINTABLESTRING) str_type = V_ASN1_PRINTABLESTRING;
+	else if(mask & B_ASN1_IA5STRING) str_type = V_ASN1_IA5STRING;
+	else if(mask & B_ASN1_T61STRING) str_type = V_ASN1_T61STRING;
+	else if(mask & B_ASN1_BMPSTRING) {
+		str_type = V_ASN1_BMPSTRING;
+		outform = MBSTRING_BMP;
+	} else if(mask & B_ASN1_UNIVERSALSTRING) {
+		str_type = V_ASN1_UNIVERSALSTRING;
+		outform = MBSTRING_UNIV;
+	} else {
+		str_type = V_ASN1_UTF8STRING;
+		outform = MBSTRING_UTF8;
+	}
+	if(!out) return str_type;
+	if(*out) {
+		free_out = 0;
+		dest = *out;
+		if(dest->data) {
+			dest->length = 0;
+			OPENSSL_free(dest->data);
+			dest->data = NULL;
+		}
+		dest->type = str_type;
+	} else {
+		free_out = 1;
+		dest = ASN1_STRING_type_new(str_type);
+		if(!dest) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy,  ERR_R_MALLOC_FAILURE);
+			return -1;
+		}
+		*out = dest;
+	}
+	/* If both the same type just copy across */
+	if(inform == outform) {
+		if(!ASN1_STRING_set(dest, in, len)) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy, ERR_R_MALLOC_FAILURE);
+			return -1;
+		}
+		return str_type;
+	} 
+
+	/* Work out how much space the destination will need */
+	switch(outform) {
+		case MBSTRING_ASC:
+		outlen = nchar;
+		cpyfunc = cpy_asc;
+		break;
+
+		case MBSTRING_BMP:
+		outlen = nchar << 1;
+		cpyfunc = cpy_bmp;
+		break;
+
+		case MBSTRING_UNIV:
+		outlen = nchar << 2;
+		cpyfunc = cpy_univ;
+		break;
+
+		case MBSTRING_UTF8:
+		outlen = 0;
+		traverse_string(in, len, inform, out_utf8, &outlen);
+		cpyfunc = cpy_utf8;
+		break;
+	}
+	if(!(p = OPENSSL_malloc(outlen + 1))) {
+		if(free_out) ASN1_STRING_free(dest);
+		OPENSSL_PUT_ERROR(ASN1, ASN1_mbstring_ncopy, ERR_R_MALLOC_FAILURE);
+		return -1;
+	}
+	dest->length = outlen;
+	dest->data = p;
+	p[outlen] = 0;
+	traverse_string(in, len, inform, cpyfunc, &p);
+	return str_type;	
+}
+
+/* This function traverses a string and passes the value of each character
+ * to an optional function along with a void * argument.
+ */
+
+static int traverse_string(const unsigned char *p, int len, int inform,
+		 int (*rfunc)(unsigned long value, void *in), void *arg)
+{
+	unsigned long value;
+	int ret;
+	while(len) {
+		if(inform == MBSTRING_ASC) {
+			value = *p++;
+			len--;
+		} else if(inform == MBSTRING_BMP) {
+			value = *p++ << 8;
+			value |= *p++;
+			len -= 2;
+		} else if(inform == MBSTRING_UNIV) {
+			value = ((unsigned long)*p++) << 24;
+			value |= ((unsigned long)*p++) << 16;
+			value |= *p++ << 8;
+			value |= *p++;
+			len -= 4;
+		} else {
+			ret = UTF8_getc(p, len, &value);
+			if(ret < 0) return -1;
+			len -= ret;
+			p += ret;
+		}
+		if(rfunc) {
+			ret = rfunc(value, arg);
+			if(ret <= 0) return ret;
+		}
+	}
+	return 1;
+}
+
+/* Various utility functions for traverse_string */
+
+/* Just count number of characters */
+
+static int in_utf8(unsigned long value, void *arg)
+{
+	int *nchar;
+	nchar = arg;
+	(*nchar)++;
+	return 1;
+}
+
+/* Determine size of output as a UTF8 String */
+
+static int out_utf8(unsigned long value, void *arg)
+{
+	int *outlen;
+	outlen = arg;
+	*outlen += UTF8_putc(NULL, -1, value);
+	return 1;
+}
+
+/* Determine the "type" of a string: check each character against a
+ * supplied "mask".
+ */
+
+static int type_str(unsigned long value, void *arg)
+{
+	unsigned long types;
+	types = *((unsigned long *)arg);
+	if((types & B_ASN1_PRINTABLESTRING) && !is_printable(value))
+					types &= ~B_ASN1_PRINTABLESTRING;
+	if((types & B_ASN1_IA5STRING) && (value > 127))
+					types &= ~B_ASN1_IA5STRING;
+	if((types & B_ASN1_T61STRING) && (value > 0xff))
+					types &= ~B_ASN1_T61STRING;
+	if((types & B_ASN1_BMPSTRING) && (value > 0xffff))
+					types &= ~B_ASN1_BMPSTRING;
+	if(!types) return -1;
+	*((unsigned long *)arg) = types;
+	return 1;
+}
+
+/* Copy one byte per character ASCII like strings */
+
+static int cpy_asc(unsigned long value, void *arg)
+{
+	unsigned char **p, *q;
+	p = arg;
+	q = *p;
+	*q = (unsigned char) value;
+	(*p)++;
+	return 1;
+}
+
+/* Copy two byte per character BMPStrings */
+
+static int cpy_bmp(unsigned long value, void *arg)
+{
+	unsigned char **p, *q;
+	p = arg;
+	q = *p;
+	*q++ = (unsigned char) ((value >> 8) & 0xff);
+	*q = (unsigned char) (value & 0xff);
+	*p += 2;
+	return 1;
+}
+
+/* Copy four byte per character UniversalStrings */
+
+static int cpy_univ(unsigned long value, void *arg)
+{
+	unsigned char **p, *q;
+	p = arg;
+	q = *p;
+	*q++ = (unsigned char) ((value >> 24) & 0xff);
+	*q++ = (unsigned char) ((value >> 16) & 0xff);
+	*q++ = (unsigned char) ((value >> 8) & 0xff);
+	*q = (unsigned char) (value & 0xff);
+	*p += 4;
+	return 1;
+}
+
+/* Copy to a UTF8String */
+
+static int cpy_utf8(unsigned long value, void *arg)
+{
+	unsigned char **p;
+	int ret;
+	p = arg;
+	/* We already know there is enough room so pass 0xff as the length */
+	ret = UTF8_putc(*p, 0xff, value);
+	*p += ret;
+	return 1;
+}
+
+/* Return 1 if the character is permitted in a PrintableString */
+static int is_printable(unsigned long value)
+{
+	int ch;
+	if(value > 0x7f) return 0;
+	ch = (int) value;
+	/* Note: we can't use 'isalnum' because certain accented 
+	 * characters may count as alphanumeric in some environments.
+	 */
+#ifndef CHARSET_EBCDIC
+	if((ch >= 'a') && (ch <= 'z')) return 1;
+	if((ch >= 'A') && (ch <= 'Z')) return 1;
+	if((ch >= '0') && (ch <= '9')) return 1;
+	if ((ch == ' ') || strchr("'()+,-./:=?", ch)) return 1;
+#else /*CHARSET_EBCDIC*/
+	if((ch >= os_toascii['a']) && (ch <= os_toascii['z'])) return 1;
+	if((ch >= os_toascii['A']) && (ch <= os_toascii['Z'])) return 1;
+	if((ch >= os_toascii['0']) && (ch <= os_toascii['9'])) return 1;
+	if ((ch == os_toascii[' ']) || strchr("'()+,-./:=?", os_toebcdic[ch])) return 1;
+#endif /*CHARSET_EBCDIC*/
+	return 0;
+}
diff --git a/crypto/asn1/a_object.c b/crypto/asn1/a_object.c
new file mode 100644
index 0000000..f2a9e2d
--- /dev/null
+++ b/crypto/asn1/a_object.c
@@ -0,0 +1,399 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <limits.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+
+int i2d_ASN1_OBJECT(ASN1_OBJECT *a, unsigned char **pp)
+	{
+	unsigned char *p;
+	int objsize;
+
+	if ((a == NULL) || (a->data == NULL)) return(0);
+
+	objsize = ASN1_object_size(0,a->length,V_ASN1_OBJECT);
+	if (pp == NULL) return objsize;
+
+	p= *pp;
+	ASN1_put_object(&p,0,a->length,V_ASN1_OBJECT,V_ASN1_UNIVERSAL);
+	memcpy(p,a->data,a->length);
+	p+=a->length;
+
+	*pp=p;
+	return(objsize);
+	}
+
+int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
+	{
+	int i,first,len=0,c, use_bn;
+	char ftmp[24], *tmp = ftmp;
+	int tmpsize = sizeof ftmp;
+	const char *p;
+	unsigned long l;
+	BIGNUM *bl = NULL;
+
+	if (num == 0)
+		return(0);
+	else if (num == -1)
+		num=strlen(buf);
+
+	p=buf;
+	c= *(p++);
+	num--;
+	if ((c >= '0') && (c <= '2'))
+		{
+		first= c-'0';
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(ASN1, a2d_ASN1_OBJECT, ASN1_R_FIRST_NUM_TOO_LARGE);
+		goto err;
+		}
+
+	if (num <= 0)
+		{
+		OPENSSL_PUT_ERROR(ASN1, a2d_ASN1_OBJECT, ASN1_R_MISSING_SECOND_NUMBER);
+		goto err;
+		}
+	c= *(p++);
+	num--;
+	for (;;)
+		{
+		if (num <= 0) break;
+		if ((c != '.') && (c != ' '))
+			{
+			OPENSSL_PUT_ERROR(ASN1, a2d_ASN1_OBJECT, ASN1_R_INVALID_SEPARATOR);
+			goto err;
+			}
+		l=0;
+		use_bn = 0;
+		for (;;)
+			{
+			if (num <= 0) break;
+			num--;
+			c= *(p++);
+			if ((c == ' ') || (c == '.'))
+				break;
+			if ((c < '0') || (c > '9'))
+				{
+				OPENSSL_PUT_ERROR(ASN1, a2d_ASN1_OBJECT, ASN1_R_INVALID_DIGIT);
+				goto err;
+				}
+			if (!use_bn && l >= ((ULONG_MAX - 80) / 10L))
+				{
+				use_bn = 1;
+				if (!bl)
+					bl = BN_new();
+				if (!bl || !BN_set_word(bl, l))
+					goto err;
+				}
+			if (use_bn)
+				{
+				if (!BN_mul_word(bl, 10L)
+					|| !BN_add_word(bl, c-'0'))
+					goto err;
+				}
+			else
+				l=l*10L+(long)(c-'0');
+			}
+		if (len == 0)
+			{
+			if ((first < 2) && (l >= 40))
+				{
+				OPENSSL_PUT_ERROR(ASN1, a2d_ASN1_OBJECT, ASN1_R_SECOND_NUMBER_TOO_LARGE);
+				goto err;
+				}
+			if (use_bn)
+				{
+				if (!BN_add_word(bl, first * 40))
+					goto err;
+				}
+			else
+				l+=(long)first*40;
+			}
+		i=0;
+		if (use_bn)
+			{
+			int blsize;
+			blsize = BN_num_bits(bl);
+			blsize = (blsize + 6)/7;
+			if (blsize > tmpsize)
+				{
+				if (tmp != ftmp)
+					OPENSSL_free(tmp);
+				tmpsize = blsize + 32;
+				tmp = OPENSSL_malloc(tmpsize);
+				if (!tmp)
+					goto err;
+				}
+			while(blsize--)
+				tmp[i++] = (unsigned char)BN_div_word(bl, 0x80L);
+			}
+		else
+			{
+					
+			for (;;)
+				{
+				tmp[i++]=(unsigned char)l&0x7f;
+				l>>=7L;
+				if (l == 0L) break;
+				}
+
+			}
+		if (out != NULL)
+			{
+			if (len+i > olen)
+				{
+				OPENSSL_PUT_ERROR(ASN1, a2d_ASN1_OBJECT, ASN1_R_BUFFER_TOO_SMALL);
+				goto err;
+				}
+			while (--i > 0)
+				out[len++]=tmp[i]|0x80;
+			out[len++]=tmp[0];
+			}
+		else
+			len+=i;
+		}
+	if (tmp != ftmp)
+		OPENSSL_free(tmp);
+	if (bl)
+		BN_free(bl);
+	return(len);
+err:
+	if (tmp != ftmp)
+		OPENSSL_free(tmp);
+	if (bl)
+		BN_free(bl);
+	return(0);
+	}
+
+int i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *a)
+{
+	return OBJ_obj2txt(buf, buf_len, a, 0);
+}
+
+int i2a_ASN1_OBJECT(BIO *bp, ASN1_OBJECT *a)
+	{
+	char buf[80], *p = buf;
+	int i;
+
+	if ((a == NULL) || (a->data == NULL))
+		return(BIO_write(bp,"NULL",4));
+	i=i2t_ASN1_OBJECT(buf,sizeof buf,a);
+	if (i > (int)(sizeof(buf) - 1))
+		{
+		p = OPENSSL_malloc(i + 1);
+		if (!p)
+			return -1;
+		i2t_ASN1_OBJECT(p,i + 1,a);
+		}
+	if (i <= 0)
+		return BIO_write(bp, "<INVALID>", 9);
+	BIO_write(bp,p,i);
+	if (p != buf)
+		OPENSSL_free(p);
+	return(i);
+	}
+
+ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
+	     long length)
+{
+	const unsigned char *p;
+	long len;
+	int tag,xclass;
+	int inf,i;
+	ASN1_OBJECT *ret = NULL;
+	p= *pp;
+	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
+	if (inf & 0x80)
+		{
+		i=ASN1_R_BAD_OBJECT_HEADER;
+		goto err;
+		}
+
+	if (tag != V_ASN1_OBJECT)
+		{
+		i=ASN1_R_EXPECTING_AN_OBJECT;
+		goto err;
+		}
+	ret = c2i_ASN1_OBJECT(a, &p, len);
+	if(ret) *pp = p;
+	return ret;
+err:
+	OPENSSL_PUT_ERROR(ASN1, d2i_ASN1_OBJECT, i);
+	return(NULL);
+}
+ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
+	     long len)
+	{
+	ASN1_OBJECT *ret=NULL;
+	const unsigned char *p;
+	unsigned char *data;
+	int i;
+	/* Sanity check OID encoding: can't have leading 0x80 in
+	 * subidentifiers, see: X.690 8.19.2
+	 */
+	for (i = 0, p = *pp; i < len; i++, p++)
+		{
+		if (*p == 0x80 && (!i || !(p[-1] & 0x80)))
+			{
+			OPENSSL_PUT_ERROR(ASN1, c2i_ASN1_OBJECT, ASN1_R_INVALID_OBJECT_ENCODING);
+			return NULL;
+			}
+		}
+
+	/* only the ASN1_OBJECTs from the 'table' will have values
+	 * for ->sn or ->ln */
+	if ((a == NULL) || ((*a) == NULL) ||
+		!((*a)->flags & ASN1_OBJECT_FLAG_DYNAMIC))
+		{
+		if ((ret=ASN1_OBJECT_new()) == NULL) return(NULL);
+		}
+	else	ret=(*a);
+
+	p= *pp;
+	/* detach data from object */
+	data = (unsigned char *)ret->data;
+	ret->data = NULL;
+	/* once detached we can change it */
+	if ((data == NULL) || (ret->length < len))
+		{
+		ret->length=0;
+		if (data != NULL) OPENSSL_free(data);
+		data=(unsigned char *)OPENSSL_malloc(len ? (int)len : 1);
+		if (data == NULL)
+			{ i=ERR_R_MALLOC_FAILURE; goto err; }
+		ret->flags|=ASN1_OBJECT_FLAG_DYNAMIC_DATA;
+		}
+	memcpy(data,p,(int)len);
+	/* reattach data to object, after which it remains const */
+	ret->data  =data;
+	ret->length=(int)len;
+	ret->sn=NULL;
+	ret->ln=NULL;
+	/* ret->flags=ASN1_OBJECT_FLAG_DYNAMIC; we know it is dynamic */
+	p+=len;
+
+	if (a != NULL) (*a)=ret;
+	*pp=p;
+	return(ret);
+err:
+	OPENSSL_PUT_ERROR(ASN1, c2i_ASN1_OBJECT, i);
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		ASN1_OBJECT_free(ret);
+	return(NULL);
+	}
+
+ASN1_OBJECT *ASN1_OBJECT_new(void)
+	{
+	ASN1_OBJECT *ret;
+
+	ret=(ASN1_OBJECT *)OPENSSL_malloc(sizeof(ASN1_OBJECT));
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_OBJECT_new, ERR_R_MALLOC_FAILURE);
+		return(NULL);
+		}
+	ret->length=0;
+	ret->data=NULL;
+	ret->nid=0;
+	ret->sn=NULL;
+	ret->ln=NULL;
+	ret->flags=ASN1_OBJECT_FLAG_DYNAMIC;
+	return(ret);
+	}
+
+void ASN1_OBJECT_free(ASN1_OBJECT *a)
+	{
+	if (a == NULL) return;
+	if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC_STRINGS)
+		{
+#ifndef CONST_STRICT /* disable purely for compile-time strict const checking. Doing this on a "real" compile will cause memory leaks */
+		if (a->sn != NULL) OPENSSL_free((void *)a->sn);
+		if (a->ln != NULL) OPENSSL_free((void *)a->ln);
+#endif
+		a->sn=a->ln=NULL;
+		}
+	if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC_DATA)
+		{
+		if (a->data != NULL) OPENSSL_free((void *)a->data);
+		a->data=NULL;
+		a->length=0;
+		}
+	if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC)
+		OPENSSL_free(a);
+	}
+
+ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data, int len,
+	     const char *sn, const char *ln)
+	{
+	ASN1_OBJECT o;
+
+	o.sn=sn;
+	o.ln=ln;
+	o.data=data;
+	o.nid=nid;
+	o.length=len;
+	o.flags=ASN1_OBJECT_FLAG_DYNAMIC|ASN1_OBJECT_FLAG_DYNAMIC_STRINGS|
+		ASN1_OBJECT_FLAG_DYNAMIC_DATA;
+	return(OBJ_dup(&o));
+	}
diff --git a/crypto/asn1/a_octet.c b/crypto/asn1/a_octet.c
new file mode 100644
index 0000000..583c9e9
--- /dev/null
+++ b/crypto/asn1/a_octet.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+ASN1_OCTET_STRING *ASN1_OCTET_STRING_dup(const ASN1_OCTET_STRING *x)
+{ return M_ASN1_OCTET_STRING_dup(x); }
+
+int ASN1_OCTET_STRING_cmp(const ASN1_OCTET_STRING *a, const ASN1_OCTET_STRING *b)
+{ return M_ASN1_OCTET_STRING_cmp(a, b); }
+
+int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *x, const unsigned char *d, int len)
+{ return M_ASN1_OCTET_STRING_set(x, d, len); }
diff --git a/crypto/asn1/a_print.c b/crypto/asn1/a_print.c
new file mode 100644
index 0000000..e2cde68
--- /dev/null
+++ b/crypto/asn1/a_print.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+int ASN1_PRINTABLE_type(const unsigned char *s, int len)
+	{
+	int c;
+	int ia5=0;
+	int t61=0;
+
+	if (len <= 0) len= -1;
+	if (s == NULL) return(V_ASN1_PRINTABLESTRING);
+
+	while ((*s) && (len-- != 0))
+		{
+		c= *(s++);
+#ifndef CHARSET_EBCDIC
+		if (!(	((c >= 'a') && (c <= 'z')) ||
+			((c >= 'A') && (c <= 'Z')) ||
+			(c == ' ') ||
+			((c >= '0') && (c <= '9')) ||
+			(c == ' ') || (c == '\'') ||
+			(c == '(') || (c == ')') ||
+			(c == '+') || (c == ',') ||
+			(c == '-') || (c == '.') ||
+			(c == '/') || (c == ':') ||
+			(c == '=') || (c == '?')))
+			ia5=1;
+		if (c&0x80)
+			t61=1;
+#else
+		if (!isalnum(c) && (c != ' ') &&
+		    strchr("'()+,-./:=?", c) == NULL)
+			ia5=1;
+		if (os_toascii[c] & 0x80)
+			t61=1;
+#endif
+		}
+	if (t61) return(V_ASN1_T61STRING);
+	if (ia5) return(V_ASN1_IA5STRING);
+	return(V_ASN1_PRINTABLESTRING);
+	}
+
+int ASN1_UNIVERSALSTRING_to_string(ASN1_UNIVERSALSTRING *s)
+	{
+	int i;
+	unsigned char *p;
+
+	if (s->type != V_ASN1_UNIVERSALSTRING) return(0);
+	if ((s->length%4) != 0) return(0);
+	p=s->data;
+	for (i=0; i<s->length; i+=4)
+		{
+		if ((p[0] != '\0') || (p[1] != '\0') || (p[2] != '\0'))
+			break;
+		else
+			p+=4;
+		}
+	if (i < s->length) return(0);
+	p=s->data;
+	for (i=3; i<s->length; i+=4)
+		{
+		*(p++)=s->data[i];
+		}
+	*(p)='\0';
+	s->length/=4;
+	s->type=ASN1_PRINTABLE_type(s->data,s->length);
+	return(1);
+	}
diff --git a/crypto/asn1/a_strnid.c b/crypto/asn1/a_strnid.c
new file mode 100644
index 0000000..836c6d9
--- /dev/null
+++ b/crypto/asn1/a_strnid.c
@@ -0,0 +1,283 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <stdlib.h>  /* For bsearch */
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+
+static STACK_OF(ASN1_STRING_TABLE) *stable = NULL;
+static void st_free(ASN1_STRING_TABLE *tbl);
+
+/* This is the global mask for the mbstring functions: this is use to
+ * mask out certain types (such as BMPString and UTF8String) because
+ * certain software (e.g. Netscape) has problems with them.
+ */
+
+static unsigned long global_mask = 0xFFFFFFFFL;
+
+void ASN1_STRING_set_default_mask(unsigned long mask)
+{
+	global_mask = mask;
+}
+
+unsigned long ASN1_STRING_get_default_mask(void)
+{
+	return global_mask;
+}
+
+/* This function sets the default to various "flavours" of configuration.
+ * based on an ASCII string. Currently this is:
+ * MASK:XXXX : a numerical mask value.
+ * nobmp : Don't use BMPStrings (just Printable, T61).
+ * pkix : PKIX recommendation in RFC2459.
+ * utf8only : only use UTF8Strings (RFC2459 recommendation for 2004).
+ * default:   the default value, Printable, T61, BMP.
+ */
+
+int ASN1_STRING_set_default_mask_asc(const char *p)
+{
+	unsigned long mask;
+	char *end;
+	if(!strncmp(p, "MASK:", 5)) {
+		if(!p[5]) return 0;
+		mask = strtoul(p + 5, &end, 0);
+		if(*end) return 0;
+	} else if(!strcmp(p, "nombstr"))
+			 mask = ~((unsigned long)(B_ASN1_BMPSTRING|B_ASN1_UTF8STRING));
+	else if(!strcmp(p, "pkix"))
+			mask = ~((unsigned long)B_ASN1_T61STRING);
+	else if(!strcmp(p, "utf8only")) mask = B_ASN1_UTF8STRING;
+	else if(!strcmp(p, "default"))
+	    mask = 0xFFFFFFFFL;
+	else return 0;
+	ASN1_STRING_set_default_mask(mask);
+	return 1;
+}
+
+/* The following function generates an ASN1_STRING based on limits in a table.
+ * Frequently the types and length of an ASN1_STRING are restricted by a 
+ * corresponding OID. For example certificates and certificate requests.
+ */
+
+ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out, const unsigned char *in,
+					int inlen, int inform, int nid)
+{
+	ASN1_STRING_TABLE *tbl;
+	ASN1_STRING *str = NULL;
+	unsigned long mask;
+	int ret;
+	if(!out) out = &str;
+	tbl = ASN1_STRING_TABLE_get(nid);
+	if(tbl) {
+		mask = tbl->mask;
+		if(!(tbl->flags & STABLE_NO_MASK)) mask &= global_mask;
+		ret = ASN1_mbstring_ncopy(out, in, inlen, inform, mask,
+					tbl->minsize, tbl->maxsize);
+	} else ret = ASN1_mbstring_copy(out, in, inlen, inform, DIRSTRING_TYPE & global_mask);
+	if(ret <= 0) return NULL;
+	return *out;
+}
+
+/* Now the tables and helper functions for the string table:
+ */
+
+/* size limits: this stuff is taken straight from RFC3280 */
+
+#define ub_name				32768
+#define ub_common_name			64
+#define ub_locality_name		128
+#define ub_state_name			128
+#define ub_organization_name		64
+#define ub_organization_unit_name	64
+#define ub_title			64
+#define ub_email_address		128
+#define ub_serial_number		64
+
+
+/* This table must be kept in NID order */
+
+static const ASN1_STRING_TABLE tbl_standard[] = {
+{NID_commonName,		1, ub_common_name, DIRSTRING_TYPE, 0},
+{NID_countryName,		2, 2, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
+{NID_localityName,		1, ub_locality_name, DIRSTRING_TYPE, 0},
+{NID_stateOrProvinceName,	1, ub_state_name, DIRSTRING_TYPE, 0},
+{NID_organizationName,		1, ub_organization_name, DIRSTRING_TYPE, 0},
+{NID_organizationalUnitName,	1, ub_organization_unit_name, DIRSTRING_TYPE, 0},
+{NID_pkcs9_emailAddress,	1, ub_email_address, B_ASN1_IA5STRING, STABLE_NO_MASK},
+{NID_pkcs9_unstructuredName,	1, -1, PKCS9STRING_TYPE, 0},
+{NID_pkcs9_challengePassword,	1, -1, PKCS9STRING_TYPE, 0},
+{NID_pkcs9_unstructuredAddress,	1, -1, DIRSTRING_TYPE, 0},
+{NID_givenName,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_surname,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_initials,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_serialNumber,		1, ub_serial_number, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
+{NID_friendlyName,		-1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK},
+{NID_name,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_dnQualifier,		-1, -1, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
+{NID_domainComponent,		1, -1, B_ASN1_IA5STRING, STABLE_NO_MASK},
+{NID_ms_csp_name,		-1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK}
+};
+
+static int sk_table_cmp(const ASN1_STRING_TABLE **a,
+			const ASN1_STRING_TABLE **b)
+{
+	return (*a)->nid - (*b)->nid;
+}
+
+static int table_cmp(const void *in_a, const void *in_b)
+{
+	const ASN1_STRING_TABLE *a = in_a;
+	const ASN1_STRING_TABLE *b = in_b;
+	return a->nid - b->nid;
+}
+
+ASN1_STRING_TABLE *ASN1_STRING_TABLE_get(int nid)
+{
+	int found;
+	size_t idx;
+	ASN1_STRING_TABLE *ttmp;
+	ASN1_STRING_TABLE fnd;
+	fnd.nid = nid;
+
+	ttmp = bsearch(&fnd, tbl_standard, sizeof(tbl_standard)/sizeof(ASN1_STRING_TABLE), sizeof(ASN1_STRING_TABLE), table_cmp);
+	if(ttmp) return ttmp;
+	if(!stable) return NULL;
+	found = sk_ASN1_STRING_TABLE_find(stable, &idx, &fnd);
+	if (!found) return NULL;
+	return sk_ASN1_STRING_TABLE_value(stable, idx);
+}
+	
+int ASN1_STRING_TABLE_add(int nid,
+		 long minsize, long maxsize, unsigned long mask,
+				unsigned long flags)
+{
+	ASN1_STRING_TABLE *tmp;
+	char new_nid = 0;
+	flags &= ~STABLE_FLAGS_MALLOC;
+	if(!stable) stable = sk_ASN1_STRING_TABLE_new(sk_table_cmp);
+	if(!stable) {
+		OPENSSL_PUT_ERROR(ASN1, ASN1_STRING_TABLE_add,  ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	if(!(tmp = ASN1_STRING_TABLE_get(nid))) {
+		tmp = OPENSSL_malloc(sizeof(ASN1_STRING_TABLE));
+		if(!tmp) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_STRING_TABLE_add,  ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+		tmp->flags = flags | STABLE_FLAGS_MALLOC;
+		tmp->nid = nid;
+		new_nid = 1;
+	} else tmp->flags = (tmp->flags & STABLE_FLAGS_MALLOC) | flags;
+	if(minsize != -1) tmp->minsize = minsize;
+	if(maxsize != -1) tmp->maxsize = maxsize;
+	tmp->mask = mask;
+	if(new_nid) sk_ASN1_STRING_TABLE_push(stable, tmp);
+	return 1;
+}
+
+void ASN1_STRING_TABLE_cleanup(void)
+{
+	STACK_OF(ASN1_STRING_TABLE) *tmp;
+	tmp = stable;
+	if(!tmp) return;
+	stable = NULL;
+	sk_ASN1_STRING_TABLE_pop_free(tmp, st_free);
+}
+
+static void st_free(ASN1_STRING_TABLE *tbl)
+{
+	if(tbl->flags & STABLE_FLAGS_MALLOC) OPENSSL_free(tbl);
+}
+
+
+#ifdef STRING_TABLE_TEST
+
+main()
+{
+	ASN1_STRING_TABLE *tmp;
+	int i, last_nid = -1;
+
+	for (tmp = tbl_standard, i = 0;
+		i < sizeof(tbl_standard)/sizeof(ASN1_STRING_TABLE); i++, tmp++)
+		{
+			if (tmp->nid < last_nid)
+				{
+				last_nid = 0;
+				break;
+				}
+			last_nid = tmp->nid;
+		}
+
+	if (last_nid != 0)
+		{
+		printf("Table order OK\n");
+		exit(0);
+		}
+
+	for (tmp = tbl_standard, i = 0;
+		i < sizeof(tbl_standard)/sizeof(ASN1_STRING_TABLE); i++, tmp++)
+			printf("Index %d, NID %d, Name=%s\n", i, tmp->nid,
+							OBJ_nid2ln(tmp->nid));
+
+}
+
+#endif
diff --git a/crypto/asn1/a_time.c b/crypto/asn1/a_time.c
new file mode 100644
index 0000000..3b80ba4
--- /dev/null
+++ b/crypto/asn1/a_time.c
@@ -0,0 +1,233 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/time_support.h>
+
+#include "asn1_locl.h"
+
+
+/* This is an implementation of the ASN1 Time structure which is:
+ *    Time ::= CHOICE {
+ *      utcTime        UTCTime,
+ *      generalTime    GeneralizedTime }
+ * written by Steve Henson.
+ */
+
+IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME)
+
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_TIME)
+
+#if 0
+int i2d_ASN1_TIME(ASN1_TIME *a, unsigned char **pp)
+	{
+#ifdef CHARSET_EBCDIC
+	/* KLUDGE! We convert to ascii before writing DER */
+	char tmp[24];
+	ASN1_STRING tmpstr;
+
+	if(a->type == V_ASN1_UTCTIME || a->type == V_ASN1_GENERALIZEDTIME) {
+	    int len;
+
+	    tmpstr = *(ASN1_STRING *)a;
+	    len = tmpstr.length;
+	    ebcdic2ascii(tmp, tmpstr.data, (len >= sizeof tmp) ? sizeof tmp : len);
+	    tmpstr.data = tmp;
+	    a = (ASN1_GENERALIZEDTIME *) &tmpstr;
+	}
+#endif
+	if(a->type == V_ASN1_UTCTIME || a->type == V_ASN1_GENERALIZEDTIME)
+				return(i2d_ASN1_bytes((ASN1_STRING *)a,pp,
+				     a->type ,V_ASN1_UNIVERSAL));
+	OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_EXPECTING_A_TIME);
+	return -1;
+	}
+#endif
+
+
+ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t)
+	{
+	return ASN1_TIME_adj(s, t, 0, 0);
+	}
+
+ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t,
+				int offset_day, long offset_sec)
+	{
+	struct tm *ts;
+	struct tm data;
+
+	ts=OPENSSL_gmtime(&t,&data);
+	if (ts == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_TIME_adj,  ASN1_R_ERROR_GETTING_TIME);
+		return NULL;
+		}
+	if (offset_day || offset_sec)
+		{ 
+		if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+			return NULL;
+		}
+	if((ts->tm_year >= 50) && (ts->tm_year < 150))
+			return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec);
+	return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec);
+	}
+
+int ASN1_TIME_check(ASN1_TIME *t)
+	{
+	if (t->type == V_ASN1_GENERALIZEDTIME)
+		return ASN1_GENERALIZEDTIME_check(t);
+	else if (t->type == V_ASN1_UTCTIME)
+		return ASN1_UTCTIME_check(t);
+	return 0;
+	}
+
+/* Convert an ASN1_TIME structure to GeneralizedTime */
+ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *t, ASN1_GENERALIZEDTIME **out)
+	{
+	ASN1_GENERALIZEDTIME *ret;
+	char *str;
+	int newlen;
+
+	if (!ASN1_TIME_check(t)) return NULL;
+
+	if (!out || !*out)
+		{
+		if (!(ret = ASN1_GENERALIZEDTIME_new ()))
+			return NULL;
+		if (out) *out = ret;
+		}
+	else ret = *out;
+
+	/* If already GeneralizedTime just copy across */
+	if (t->type == V_ASN1_GENERALIZEDTIME)
+		{
+		if(!ASN1_STRING_set(ret, t->data, t->length))
+			return NULL;
+		return ret;
+		}
+
+	/* grow the string */
+	if (!ASN1_STRING_set(ret, NULL, t->length + 2))
+		return NULL;
+	/* ASN1_STRING_set() allocated 'len + 1' bytes. */
+	newlen = t->length + 2 + 1;
+	str = (char *)ret->data;
+	/* Work out the century and prepend */
+	if (t->data[0] >= '5') BUF_strlcpy(str, "19", newlen);
+	else BUF_strlcpy(str, "20", newlen);
+
+	BUF_strlcat(str, (char *)t->data, newlen);
+
+	return ret;
+	}
+
+int ASN1_TIME_set_string(ASN1_TIME *s, const char *str)
+	{
+	ASN1_TIME t;
+
+	t.length = strlen(str);
+	t.data = (unsigned char *)str;
+	t.flags = 0;
+	
+	t.type = V_ASN1_UTCTIME;
+
+	if (!ASN1_TIME_check(&t))
+		{
+		t.type = V_ASN1_GENERALIZEDTIME;
+		if (!ASN1_TIME_check(&t))
+			return 0;
+		}
+	
+	if (s && !ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
+			return 0;
+
+	return 1;
+	}
+
+static int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *t)
+	{
+	if (t == NULL)
+		{
+		time_t now_t;
+		time(&now_t);
+		if (OPENSSL_gmtime(&now_t, tm))
+			return 1;
+		return 0;
+		}
+		
+	if (t->type == V_ASN1_UTCTIME)
+		return asn1_utctime_to_tm(tm, t);
+	else if (t->type == V_ASN1_GENERALIZEDTIME)
+		return asn1_generalizedtime_to_tm(tm, t);
+
+	return 0;
+	}
+
+int ASN1_TIME_diff(int *pday, int *psec,
+			const ASN1_TIME *from, const ASN1_TIME *to)
+	{
+	struct tm tm_from, tm_to;
+	if (!asn1_time_to_tm(&tm_from, from))
+		return 0;
+	if (!asn1_time_to_tm(&tm_to, to))
+		return 0;
+	return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
+	}	
diff --git a/crypto/asn1/a_type.c b/crypto/asn1/a_type.c
new file mode 100644
index 0000000..62bffde
--- /dev/null
+++ b/crypto/asn1/a_type.c
@@ -0,0 +1,157 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+
+int ASN1_TYPE_get(ASN1_TYPE *a)
+	{
+	if ((a->value.ptr != NULL) || (a->type == V_ASN1_NULL))
+		return(a->type);
+	else
+		return(0);
+	}
+
+void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value)
+	{
+	if (a->value.ptr != NULL)
+		{
+		ASN1_TYPE **tmp_a = &a;
+		ASN1_primitive_free((ASN1_VALUE **)tmp_a, NULL);
+		}
+	a->type=type;
+	if (type == V_ASN1_BOOLEAN)
+		a->value.boolean = value ? 0xff : 0;
+	else
+		a->value.ptr=value;
+	}
+
+int ASN1_TYPE_set1(ASN1_TYPE *a, int type, const void *value)
+	{
+	if (!value || (type == V_ASN1_BOOLEAN))
+		{
+		void *p = (void *)value;
+		ASN1_TYPE_set(a, type, p);
+		}
+	else if (type == V_ASN1_OBJECT)
+		{
+		ASN1_OBJECT *odup;
+		odup = OBJ_dup(value);
+		if (!odup)
+			return 0;
+		ASN1_TYPE_set(a, type, odup);
+		}
+	else
+		{
+		ASN1_STRING *sdup;
+		sdup = ASN1_STRING_dup(value);
+		if (!sdup)
+			return 0;
+		ASN1_TYPE_set(a, type, sdup);
+		}
+	return 1;
+	}
+
+/* Returns 0 if they are equal, != 0 otherwise. */
+int ASN1_TYPE_cmp(ASN1_TYPE *a, ASN1_TYPE *b)
+	{
+	int result = -1;
+
+	if (!a || !b || a->type != b->type) return -1;
+
+	switch (a->type)
+		{
+	case V_ASN1_OBJECT:
+		result = OBJ_cmp(a->value.object, b->value.object);
+		break;
+	case V_ASN1_NULL:
+		result = 0;	/* They do not have content. */
+		break;
+	case V_ASN1_INTEGER:
+	case V_ASN1_NEG_INTEGER:
+	case V_ASN1_ENUMERATED:
+	case V_ASN1_NEG_ENUMERATED:
+	case V_ASN1_BIT_STRING:
+	case V_ASN1_OCTET_STRING:
+	case V_ASN1_SEQUENCE:
+	case V_ASN1_SET:
+	case V_ASN1_NUMERICSTRING:
+	case V_ASN1_PRINTABLESTRING:
+	case V_ASN1_T61STRING:
+	case V_ASN1_VIDEOTEXSTRING:
+	case V_ASN1_IA5STRING:
+	case V_ASN1_UTCTIME:
+	case V_ASN1_GENERALIZEDTIME:
+	case V_ASN1_GRAPHICSTRING:
+	case V_ASN1_VISIBLESTRING:
+	case V_ASN1_GENERALSTRING:
+	case V_ASN1_UNIVERSALSTRING:
+	case V_ASN1_BMPSTRING:
+	case V_ASN1_UTF8STRING:
+	case V_ASN1_OTHER:
+	default:
+		result = ASN1_STRING_cmp((ASN1_STRING *) a->value.ptr,
+					 (ASN1_STRING *) b->value.ptr);
+		break;
+		}
+
+	return result;
+	}
diff --git a/crypto/asn1/a_utctm.c b/crypto/asn1/a_utctm.c
new file mode 100644
index 0000000..72a09be
--- /dev/null
+++ b/crypto/asn1/a_utctm.c
@@ -0,0 +1,348 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/time_support.h>
+
+
+#if 0
+int i2d_ASN1_UTCTIME(ASN1_UTCTIME *a, unsigned char **pp)
+	{
+#ifndef CHARSET_EBCDIC
+	return(i2d_ASN1_bytes((ASN1_STRING *)a,pp,
+		V_ASN1_UTCTIME,V_ASN1_UNIVERSAL));
+#else
+	/* KLUDGE! We convert to ascii before writing DER */
+	int len;
+	char tmp[24];
+	ASN1_STRING x = *(ASN1_STRING *)a;
+
+	len = x.length;
+	ebcdic2ascii(tmp, x.data, (len >= sizeof tmp) ? sizeof tmp : len);
+	x.data = tmp;
+	return i2d_ASN1_bytes(&x, pp, V_ASN1_UTCTIME,V_ASN1_UNIVERSAL);
+#endif
+	}
+
+
+ASN1_UTCTIME *d2i_ASN1_UTCTIME(ASN1_UTCTIME **a, unsigned char **pp,
+	     long length)
+	{
+	ASN1_UTCTIME *ret=NULL;
+
+	ret=(ASN1_UTCTIME *)d2i_ASN1_bytes((ASN1_STRING **)a,pp,length,
+		V_ASN1_UTCTIME,V_ASN1_UNIVERSAL);
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, XXX, ERR_R_NESTED_ASN1_ERROR);
+		return(NULL);
+		}
+#ifdef CHARSET_EBCDIC
+	ascii2ebcdic(ret->data, ret->data, ret->length);
+#endif
+	if (!ASN1_UTCTIME_check(ret))
+		{
+		OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_INVALID_TIME_FORMAT);
+		goto err;
+		}
+
+	return(ret);
+err:
+	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
+		M_ASN1_UTCTIME_free(ret);
+	return(NULL);
+	}
+
+#endif
+
+int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d)
+	{
+	static const int min[8]={ 0, 1, 1, 0, 0, 0, 0, 0};
+	static const int max[8]={99,12,31,23,59,59,12,59};
+	char *a;
+	int n,i,l,o;
+
+	if (d->type != V_ASN1_UTCTIME) return(0);
+	l=d->length;
+	a=(char *)d->data;
+	o=0;
+
+	if (l < 11) goto err;
+	for (i=0; i<6; i++)
+		{
+		if ((i == 5) && ((a[o] == 'Z') ||
+			(a[o] == '+') || (a[o] == '-')))
+			{
+			i++;
+			if (tm)
+				tm->tm_sec = 0;
+			break;
+			}
+		if ((a[o] < '0') || (a[o] > '9')) goto err;
+		n= a[o]-'0';
+		if (++o > l) goto err;
+
+		if ((a[o] < '0') || (a[o] > '9')) goto err;
+		n=(n*10)+ a[o]-'0';
+		if (++o > l) goto err;
+
+		if ((n < min[i]) || (n > max[i])) goto err;
+		if (tm)
+			{
+			switch(i)
+				{
+			case 0:
+				tm->tm_year = n < 50 ? n + 100 : n;
+				break;
+			case 1:
+				tm->tm_mon = n - 1;
+				break;
+			case 2:
+				tm->tm_mday = n;
+				break;
+			case 3:
+				tm->tm_hour = n;
+				break;
+			case 4:
+				tm->tm_min = n;
+				break;
+			case 5:
+				tm->tm_sec = n;
+				break;
+				}
+			}
+		}
+	if (a[o] == 'Z')
+		o++;
+	else if ((a[o] == '+') || (a[o] == '-'))
+		{
+		int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+		o++;
+		if (o+4 > l) goto err;
+		for (i=6; i<8; i++)
+			{
+			if ((a[o] < '0') || (a[o] > '9')) goto err;
+			n= a[o]-'0';
+			o++;
+			if ((a[o] < '0') || (a[o] > '9')) goto err;
+			n=(n*10)+ a[o]-'0';
+			if ((n < min[i]) || (n > max[i])) goto err;
+			if (tm)
+				{
+				if (i == 6)
+					offset = n * 3600;
+				else if (i == 7)
+					offset += n * 60;
+				}
+			o++;
+			}
+		if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign))
+			return 0;
+		}
+	return o == l;
+err:
+	return 0;
+	}
+
+int ASN1_UTCTIME_check(const ASN1_UTCTIME *d)
+	{
+	return asn1_utctime_to_tm(NULL, d);
+	}
+
+int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str)
+	{
+	ASN1_UTCTIME t;
+
+	t.type=V_ASN1_UTCTIME;
+	t.length=strlen(str);
+	t.data=(unsigned char *)str;
+	if (ASN1_UTCTIME_check(&t))
+		{
+		if (s != NULL)
+			{
+			if (!ASN1_STRING_set((ASN1_STRING *)s,
+				(unsigned char *)str,t.length))
+				return 0;
+			s->type = V_ASN1_UTCTIME;
+			}
+		return(1);
+		}
+	else
+		return(0);
+	}
+
+ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t)
+	{
+	return ASN1_UTCTIME_adj(s, t, 0, 0);
+	}
+
+ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
+				int offset_day, long offset_sec)
+	{
+	char *p;
+	struct tm *ts;
+	struct tm data;
+	size_t len = 20;
+
+	if (s == NULL)
+		s=M_ASN1_UTCTIME_new();
+	if (s == NULL)
+		return(NULL);
+
+	ts=OPENSSL_gmtime(&t, &data);
+	if (ts == NULL)
+		return(NULL);
+
+	if (offset_day || offset_sec)
+		{ 
+		if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+			return NULL;
+		}
+
+	if((ts->tm_year < 50) || (ts->tm_year >= 150))
+		return NULL;
+
+	p=(char *)s->data;
+	if ((p == NULL) || ((size_t)s->length < len))
+		{
+		p=OPENSSL_malloc(len);
+		if (p == NULL)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_UTCTIME_adj, ERR_R_MALLOC_FAILURE);
+			return(NULL);
+			}
+		if (s->data != NULL)
+			OPENSSL_free(s->data);
+		s->data=(unsigned char *)p;
+		}
+
+	BIO_snprintf(p,len,"%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
+		     ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
+	s->length=strlen(p);
+	s->type=V_ASN1_UTCTIME;
+#ifdef CHARSET_EBCDIC_not
+	ebcdic2ascii(s->data, s->data, s->length);
+#endif
+	return(s);
+	}
+
+
+int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t)
+	{
+	struct tm stm, ttm;
+	int day, sec;
+
+	if (!asn1_utctime_to_tm(&stm, s))
+		return -2;
+
+	if (!OPENSSL_gmtime(&t, &ttm))
+		return -2;
+
+	if (!OPENSSL_gmtime_diff(&day, &sec, &stm, &ttm))
+		return -2;
+
+	if (day > 0)
+		return 1;
+	if (day < 0)
+		return -1;
+	if (sec > 0)
+		return 1;
+	if (sec < 0)
+		return -1;
+	return 0;
+	}
+
+
+#if 0
+time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s)
+	{
+	struct tm tm;
+	int offset;
+
+	memset(&tm,'\0',sizeof tm);
+
+#define g2(p) (((p)[0]-'0')*10+(p)[1]-'0')
+	tm.tm_year=g2(s->data);
+	if(tm.tm_year < 50)
+		tm.tm_year+=100;
+	tm.tm_mon=g2(s->data+2)-1;
+	tm.tm_mday=g2(s->data+4);
+	tm.tm_hour=g2(s->data+6);
+	tm.tm_min=g2(s->data+8);
+	tm.tm_sec=g2(s->data+10);
+	if(s->data[12] == 'Z')
+		offset=0;
+	else
+		{
+		offset=g2(s->data+13)*60+g2(s->data+15);
+		if(s->data[12] == '-')
+			offset= -offset;
+		}
+#undef g2
+
+	return mktime(&tm)-offset*60; /* FIXME: mktime assumes the current timezone
+	                               * instead of UTC, and unless we rewrite OpenSSL
+				       * in Lisp we cannot locally change the timezone
+				       * without possibly interfering with other parts
+	                               * of the program. timegm, which uses UTC, is
+				       * non-standard.
+	                               * Also time_t is inappropriate for general
+	                               * UTC times because it may a 32 bit type. */
+	}
+#endif
diff --git a/crypto/asn1/a_utf8.c b/crypto/asn1/a_utf8.c
new file mode 100644
index 0000000..ed6e98d
--- /dev/null
+++ b/crypto/asn1/a_utf8.c
@@ -0,0 +1,210 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+/* UTF8 utilities */
+
+/* This parses a UTF8 string one character at a time. It is passed a pointer
+ * to the string and the length of the string. It sets 'value' to the value of
+ * the current character. It returns the number of characters read or a
+ * negative error code:
+ * -1 = string too short
+ * -2 = illegal character
+ * -3 = subsequent characters not of the form 10xxxxxx
+ * -4 = character encoded incorrectly (not minimal length).
+ */
+
+int UTF8_getc(const unsigned char *str, int len, unsigned long *val)
+{
+	const unsigned char *p;
+	unsigned long value;
+	int ret;
+	if(len <= 0) return 0;
+	p = str;
+
+	/* Check syntax and work out the encoded value (if correct) */
+	if((*p & 0x80) == 0) {
+		value = *p++ & 0x7f;
+		ret = 1;
+	} else if((*p & 0xe0) == 0xc0) {
+		if(len < 2) return -1;
+		if((p[1] & 0xc0) != 0x80) return -3;
+		value = (*p++ & 0x1f) << 6;
+		value |= *p++ & 0x3f;
+		if(value < 0x80) return -4;
+		ret = 2;
+	} else if((*p & 0xf0) == 0xe0) {
+		if(len < 3) return -1;
+		if( ((p[1] & 0xc0) != 0x80)
+		   || ((p[2] & 0xc0) != 0x80) ) return -3;
+		value = (*p++ & 0xf) << 12;
+		value |= (*p++ & 0x3f) << 6;
+		value |= *p++ & 0x3f;
+		if(value < 0x800) return -4;
+		ret = 3;
+	} else if((*p & 0xf8) == 0xf0) {
+		if(len < 4) return -1;
+		if( ((p[1] & 0xc0) != 0x80)
+		   || ((p[2] & 0xc0) != 0x80) 
+		   || ((p[3] & 0xc0) != 0x80) ) return -3;
+		value = ((unsigned long)(*p++ & 0x7)) << 18;
+		value |= (*p++ & 0x3f) << 12;
+		value |= (*p++ & 0x3f) << 6;
+		value |= *p++ & 0x3f;
+		if(value < 0x10000) return -4;
+		ret = 4;
+	} else if((*p & 0xfc) == 0xf8) {
+		if(len < 5) return -1;
+		if( ((p[1] & 0xc0) != 0x80)
+		   || ((p[2] & 0xc0) != 0x80) 
+		   || ((p[3] & 0xc0) != 0x80) 
+		   || ((p[4] & 0xc0) != 0x80) ) return -3;
+		value = ((unsigned long)(*p++ & 0x3)) << 24;
+		value |= ((unsigned long)(*p++ & 0x3f)) << 18;
+		value |= ((unsigned long)(*p++ & 0x3f)) << 12;
+		value |= (*p++ & 0x3f) << 6;
+		value |= *p++ & 0x3f;
+		if(value < 0x200000) return -4;
+		ret = 5;
+	} else if((*p & 0xfe) == 0xfc) {
+		if(len < 6) return -1;
+		if( ((p[1] & 0xc0) != 0x80)
+		   || ((p[2] & 0xc0) != 0x80) 
+		   || ((p[3] & 0xc0) != 0x80) 
+		   || ((p[4] & 0xc0) != 0x80) 
+		   || ((p[5] & 0xc0) != 0x80) ) return -3;
+		value = ((unsigned long)(*p++ & 0x1)) << 30;
+		value |= ((unsigned long)(*p++ & 0x3f)) << 24;
+		value |= ((unsigned long)(*p++ & 0x3f)) << 18;
+		value |= ((unsigned long)(*p++ & 0x3f)) << 12;
+		value |= (*p++ & 0x3f) << 6;
+		value |= *p++ & 0x3f;
+		if(value < 0x4000000) return -4;
+		ret = 6;
+	} else return -2;
+	*val = value;
+	return ret;
+}
+
+/* This takes a character 'value' and writes the UTF8 encoded value in
+ * 'str' where 'str' is a buffer containing 'len' characters. Returns
+ * the number of characters written or -1 if 'len' is too small. 'str' can
+ * be set to NULL in which case it just returns the number of characters.
+ * It will need at most 6 characters.
+ */
+
+int UTF8_putc(unsigned char *str, int len, unsigned long value)
+{
+	if(!str) len = 6;	/* Maximum we will need */
+	else if(len <= 0) return -1;
+	if(value < 0x80) {
+		if(str) *str = (unsigned char)value;
+		return 1;
+	}
+	if(value < 0x800) {
+		if(len < 2) return -1;
+		if(str) {
+			*str++ = (unsigned char)(((value >> 6) & 0x1f) | 0xc0);
+			*str = (unsigned char)((value & 0x3f) | 0x80);
+		}
+		return 2;
+	}
+	if(value < 0x10000) {
+		if(len < 3) return -1;
+		if(str) {
+			*str++ = (unsigned char)(((value >> 12) & 0xf) | 0xe0);
+			*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+			*str = (unsigned char)((value & 0x3f) | 0x80);
+		}
+		return 3;
+	}
+	if(value < 0x200000) {
+		if(len < 4) return -1;
+		if(str) {
+			*str++ = (unsigned char)(((value >> 18) & 0x7) | 0xf0);
+			*str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
+			*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+			*str = (unsigned char)((value & 0x3f) | 0x80);
+		}
+		return 4;
+	}
+	if(value < 0x4000000) {
+		if(len < 5) return -1;
+		if(str) {
+			*str++ = (unsigned char)(((value >> 24) & 0x3) | 0xf8);
+			*str++ = (unsigned char)(((value >> 18) & 0x3f) | 0x80);
+			*str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
+			*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+			*str = (unsigned char)((value & 0x3f) | 0x80);
+		}
+		return 5;
+	}
+	if(len < 6) return -1;
+	if(str) {
+		*str++ = (unsigned char)(((value >> 30) & 0x1) | 0xfc);
+		*str++ = (unsigned char)(((value >> 24) & 0x3f) | 0x80);
+		*str++ = (unsigned char)(((value >> 18) & 0x3f) | 0x80);
+		*str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
+		*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+		*str = (unsigned char)((value & 0x3f) | 0x80);
+	}
+	return 6;
+}
diff --git a/crypto/asn1/asn1.h b/crypto/asn1/asn1.h
new file mode 100644
index 0000000..79635bb
--- /dev/null
+++ b/crypto/asn1/asn1.h
@@ -0,0 +1,1333 @@
+/* crypto/asn1/asn1.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_ASN1_H
+#define HEADER_ASN1_H
+
+#include <openssl/base.h>
+
+#include <time.h>
+
+#ifndef OPENSSL_NO_BIO
+#include <openssl/bio.h>
+#endif
+#include <openssl/stack.h>
+
+#include <openssl/bn.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#define V_ASN1_UNIVERSAL		0x00
+#define	V_ASN1_APPLICATION		0x40
+#define V_ASN1_CONTEXT_SPECIFIC		0x80
+#define V_ASN1_PRIVATE			0xc0
+
+#define V_ASN1_CONSTRUCTED		0x20
+#define V_ASN1_PRIMITIVE_TAG		0x1f
+#define V_ASN1_PRIMATIVE_TAG		0x1f
+
+#define V_ASN1_APP_CHOOSE		-2	/* let the recipient choose */
+#define V_ASN1_OTHER			-3	/* used in ASN1_TYPE */
+#define V_ASN1_ANY			-4	/* used in ASN1 template code */
+
+#define V_ASN1_NEG			0x100	/* negative flag */
+
+#define V_ASN1_UNDEF			-1
+#define V_ASN1_EOC			0
+#define V_ASN1_BOOLEAN			1	/**/
+#define V_ASN1_INTEGER			2
+#define V_ASN1_NEG_INTEGER		(2 | V_ASN1_NEG)
+#define V_ASN1_BIT_STRING		3
+#define V_ASN1_OCTET_STRING		4
+#define V_ASN1_NULL			5
+#define V_ASN1_OBJECT			6
+#define V_ASN1_OBJECT_DESCRIPTOR	7
+#define V_ASN1_EXTERNAL			8
+#define V_ASN1_REAL			9
+#define V_ASN1_ENUMERATED		10
+#define V_ASN1_NEG_ENUMERATED		(10 | V_ASN1_NEG)
+#define V_ASN1_UTF8STRING		12
+#define V_ASN1_SEQUENCE			16
+#define V_ASN1_SET			17
+#define V_ASN1_NUMERICSTRING		18	/**/
+#define V_ASN1_PRINTABLESTRING		19
+#define V_ASN1_T61STRING		20
+#define V_ASN1_TELETEXSTRING		20	/* alias */
+#define V_ASN1_VIDEOTEXSTRING		21	/**/
+#define V_ASN1_IA5STRING		22
+#define V_ASN1_UTCTIME			23
+#define V_ASN1_GENERALIZEDTIME		24	/**/
+#define V_ASN1_GRAPHICSTRING		25	/**/
+#define V_ASN1_ISO64STRING		26	/**/
+#define V_ASN1_VISIBLESTRING		26	/* alias */
+#define V_ASN1_GENERALSTRING		27	/**/
+#define V_ASN1_UNIVERSALSTRING		28	/**/
+#define V_ASN1_BMPSTRING		30
+
+/* For use with d2i_ASN1_type_bytes() */
+#define B_ASN1_NUMERICSTRING	0x0001
+#define B_ASN1_PRINTABLESTRING	0x0002
+#define B_ASN1_T61STRING	0x0004
+#define B_ASN1_TELETEXSTRING	0x0004
+#define B_ASN1_VIDEOTEXSTRING	0x0008
+#define B_ASN1_IA5STRING	0x0010
+#define B_ASN1_GRAPHICSTRING	0x0020
+#define B_ASN1_ISO64STRING	0x0040
+#define B_ASN1_VISIBLESTRING	0x0040
+#define B_ASN1_GENERALSTRING	0x0080
+#define B_ASN1_UNIVERSALSTRING	0x0100
+#define B_ASN1_OCTET_STRING	0x0200
+#define B_ASN1_BIT_STRING	0x0400
+#define B_ASN1_BMPSTRING	0x0800
+#define B_ASN1_UNKNOWN		0x1000
+#define B_ASN1_UTF8STRING	0x2000
+#define B_ASN1_UTCTIME		0x4000
+#define B_ASN1_GENERALIZEDTIME	0x8000
+#define B_ASN1_SEQUENCE		0x10000
+
+/* For use with ASN1_mbstring_copy() */
+#define MBSTRING_FLAG		0x1000
+#define MBSTRING_UTF8		(MBSTRING_FLAG)
+#define MBSTRING_ASC		(MBSTRING_FLAG|1)
+#define MBSTRING_BMP		(MBSTRING_FLAG|2)
+#define MBSTRING_UNIV		(MBSTRING_FLAG|4)
+
+#define SMIME_OLDMIME		0x400
+#define SMIME_CRLFEOL		0x800
+#define SMIME_STREAM		0x1000
+
+#define DECLARE_ASN1_SET_OF(type) /* filled in by mkstack.pl */
+#define IMPLEMENT_ASN1_SET_OF(type) /* nothing, no longer needed */
+
+/* We MUST make sure that, except for constness, asn1_ctx_st and
+   asn1_const_ctx are exactly the same.  Fortunately, as soon as
+   the old ASN1 parsing macros are gone, we can throw this away
+   as well... */
+typedef struct asn1_ctx_st
+	{
+	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' */
+	unsigned char *max; /* largest value of p allowed */
+	unsigned char *q;/* temporary variable */
+	unsigned char **pp;/* variable */
+	int line;	/* used in error processing */
+	} ASN1_CTX;
+
+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;
+
+/* These are used internally in the ASN1_OBJECT to keep track of
+ * whether the names and data need to be free()ed */
+#define ASN1_OBJECT_FLAG_DYNAMIC	 0x01	/* internal use */
+#define ASN1_OBJECT_FLAG_CRITICAL	 0x02	/* critical x509v3 object id */
+#define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04	/* internal use */
+#define ASN1_OBJECT_FLAG_DYNAMIC_DATA 	 0x08	/* internal use */
+struct asn1_object_st
+	{
+	const char *sn,*ln;
+	int nid;
+	int length;
+	const unsigned char *data;	/* data remains const after init */
+	int flags;	/* Should we free this one */
+	};
+
+#define ASN1_STRING_FLAG_BITS_LEFT 0x08 /* Set if 0x07 has bits left value */
+/* This indicates that the ASN1_STRING is not a real value but just a place
+ * holder for the location where indefinite length constructed data should
+ * be inserted in the memory buffer 
+ */
+#define ASN1_STRING_FLAG_NDEF 0x010 
+
+/* This flag is used by the CMS code to indicate that a string is not
+ * complete and is a place holder for content when it had all been 
+ * accessed. The flag will be reset when content has been written to it.
+ */
+
+#define ASN1_STRING_FLAG_CONT 0x020 
+/* This flag is used by ASN1 code to indicate an ASN1_STRING is an MSTRING
+ * type.
+ */
+#define ASN1_STRING_FLAG_MSTRING 0x040 
+/* This is the base type that holds just about everything :-) */
+struct asn1_string_st
+	{
+	int length;
+	int type;
+	unsigned char *data;
+	/* The value of the following field depends on the type being
+	 * held.  It is mostly being used for BIT_STRING so if the
+	 * input data has a non-zero 'unused bits' value, it will be
+	 * handled correctly */
+	long flags;
+	};
+
+/* ASN1_ENCODING structure: this is used to save the received
+ * encoding of an ASN1 type. This is useful to get round
+ * problems with invalid encodings which can break signatures.
+ */
+
+typedef struct ASN1_ENCODING_st
+	{
+	unsigned char *enc;	/* DER encoding */
+	long len;		/* Length of encoding */
+	int modified;		 /* set to 1 if 'enc' is invalid */
+	} ASN1_ENCODING;
+
+/* Used with ASN1 LONG type: if a long is set to this it is omitted */
+#define ASN1_LONG_UNDEF	0x7fffffffL
+
+#define STABLE_FLAGS_MALLOC	0x01
+#define STABLE_NO_MASK		0x02
+#define DIRSTRING_TYPE	\
+ (B_ASN1_PRINTABLESTRING|B_ASN1_T61STRING|B_ASN1_BMPSTRING|B_ASN1_UTF8STRING)
+#define PKCS9STRING_TYPE (DIRSTRING_TYPE|B_ASN1_IA5STRING)
+
+typedef struct asn1_string_table_st {
+	int nid;
+	long minsize;
+	long maxsize;
+	unsigned long mask;
+	unsigned long flags;
+} ASN1_STRING_TABLE;
+
+/* size limits: this stuff is taken straight from RFC2459 */
+
+#define ub_name				32768
+#define ub_common_name			64
+#define ub_locality_name		128
+#define ub_state_name			128
+#define ub_organization_name		64
+#define ub_organization_unit_name	64
+#define ub_title			64
+#define ub_email_address		128
+
+/* Declarations for template structures: for full definitions
+ * see asn1t.h
+ */
+typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE;
+typedef struct ASN1_TLC_st ASN1_TLC;
+/* This is just an opaque pointer */
+typedef struct ASN1_VALUE_st ASN1_VALUE;
+
+/* Declare ASN1 functions: the implement macro in in asn1t.h */
+
+#define DECLARE_ASN1_FUNCTIONS(type) DECLARE_ASN1_FUNCTIONS_name(type, type)
+
+#define DECLARE_ASN1_ALLOC_FUNCTIONS(type) \
+	DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, type)
+
+#define DECLARE_ASN1_FUNCTIONS_name(type, name) \
+	DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
+	DECLARE_ASN1_ENCODE_FUNCTIONS(type, name, name)
+
+#define DECLARE_ASN1_FUNCTIONS_fname(type, itname, name) \
+	DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
+	DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name)
+
+#define	DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name) \
+	type *d2i_##name(type **a, const unsigned char **in, long len); \
+	int i2d_##name(type *a, unsigned char **out); \
+	DECLARE_ASN1_ITEM(itname)
+
+#define	DECLARE_ASN1_ENCODE_FUNCTIONS_const(type, name) \
+	type *d2i_##name(type **a, const unsigned char **in, long len); \
+	int i2d_##name(const type *a, unsigned char **out); \
+	DECLARE_ASN1_ITEM(name)
+
+#define	DECLARE_ASN1_NDEF_FUNCTION(name) \
+	int i2d_##name##_NDEF(name *a, unsigned char **out);
+
+#define DECLARE_ASN1_FUNCTIONS_const(name) \
+	DECLARE_ASN1_ALLOC_FUNCTIONS(name) \
+	DECLARE_ASN1_ENCODE_FUNCTIONS_const(name, name)
+
+#define DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
+	type *name##_new(void); \
+	void name##_free(type *a);
+
+#define DECLARE_ASN1_PRINT_FUNCTION(stname) \
+	DECLARE_ASN1_PRINT_FUNCTION_fname(stname, stname)
+
+#define DECLARE_ASN1_PRINT_FUNCTION_fname(stname, fname) \
+	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))
+
+#define TYPEDEF_D2I_OF(type) typedef type *d2i_of_##type(type **,const unsigned char **,long)
+#define TYPEDEF_I2D_OF(type) typedef int i2d_of_##type(const type *,unsigned char **)
+#define TYPEDEF_D2I2D_OF(type) TYPEDEF_D2I_OF(type); TYPEDEF_I2D_OF(type)
+
+TYPEDEF_D2I2D_OF(void);
+
+/* The following macros and typedefs allow an ASN1_ITEM
+ * to be embedded in a structure and referenced. Since
+ * the ASN1_ITEM pointers need to be globally accessible
+ * (possibly from shared libraries) they may exist in
+ * different forms. On platforms that support it the
+ * ASN1_ITEM structure itself will be globally exported.
+ * Other platforms will export a function that returns
+ * an ASN1_ITEM pointer.
+ *
+ * To handle both cases transparently the macros below
+ * should be used instead of hard coding an ASN1_ITEM
+ * pointer in a structure.
+ *
+ * The structure will look like this:
+ *
+ * typedef struct SOMETHING_st {
+ *      ...
+ *      ASN1_ITEM_EXP *iptr;
+ *      ...
+ * } SOMETHING; 
+ *
+ * It would be initialised as e.g.:
+ *
+ * SOMETHING somevar = {...,ASN1_ITEM_ref(X509),...};
+ *
+ * and the actual pointer extracted with:
+ *
+ * const ASN1_ITEM *it = ASN1_ITEM_ptr(somevar.iptr);
+ *
+ * Finally an ASN1_ITEM pointer can be extracted from an
+ * appropriate reference with: ASN1_ITEM_rptr(X509). This
+ * would be used when a function takes an ASN1_ITEM * argument.
+ *
+ */
+
+#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION
+
+/* ASN1_ITEM pointer exported type */
+typedef const ASN1_ITEM ASN1_ITEM_EXP;
+
+/* Macro to obtain ASN1_ITEM pointer from exported type */
+#define ASN1_ITEM_ptr(iptr) (iptr)
+
+/* Macro to include ASN1_ITEM pointer from base type */
+#define ASN1_ITEM_ref(iptr) (&(iptr##_it))
+
+#define ASN1_ITEM_rptr(ref) (&(ref##_it))
+
+#define DECLARE_ASN1_ITEM(name) \
+	extern const ASN1_ITEM name##_it;
+
+#else
+
+/* Platforms that can't easily handle shared global variables are declared
+ * as functions returning ASN1_ITEM pointers.
+ */
+
+/* ASN1_ITEM pointer exported type */
+typedef const ASN1_ITEM * ASN1_ITEM_EXP(void);
+
+/* Macro to obtain ASN1_ITEM pointer from exported type */
+#define ASN1_ITEM_ptr(iptr) (iptr())
+
+/* Macro to include ASN1_ITEM pointer from base type */
+#define ASN1_ITEM_ref(iptr) (iptr##_it)
+
+#define ASN1_ITEM_rptr(ref) (ref##_it())
+
+#define DECLARE_ASN1_ITEM(name) \
+	const ASN1_ITEM * name##_it(void);
+
+#endif
+
+/* Parameters used by ASN1_STRING_print_ex() */
+
+/* These determine which characters to escape:
+ * RFC2253 special characters, control characters and
+ * MSB set characters
+ */
+
+#define ASN1_STRFLGS_ESC_2253		1
+#define ASN1_STRFLGS_ESC_CTRL		2
+#define ASN1_STRFLGS_ESC_MSB		4
+
+
+/* This flag determines how we do escaping: normally
+ * RC2253 backslash only, set this to use backslash and
+ * quote.
+ */
+
+#define ASN1_STRFLGS_ESC_QUOTE		8
+
+
+/* These three flags are internal use only. */
+
+/* Character is a valid PrintableString character */
+#define CHARTYPE_PRINTABLESTRING	0x10
+/* Character needs escaping if it is the first character */
+#define CHARTYPE_FIRST_ESC_2253		0x20
+/* Character needs escaping if it is the last character */
+#define CHARTYPE_LAST_ESC_2253		0x40
+
+/* NB the internal flags are safely reused below by flags
+ * handled at the top level.
+ */
+
+/* If this is set we convert all character strings
+ * to UTF8 first 
+ */
+
+#define ASN1_STRFLGS_UTF8_CONVERT	0x10
+
+/* If this is set we don't attempt to interpret content:
+ * just assume all strings are 1 byte per character. This
+ * will produce some pretty odd looking output!
+ */
+
+#define ASN1_STRFLGS_IGNORE_TYPE	0x20
+
+/* If this is set we include the string type in the output */
+#define ASN1_STRFLGS_SHOW_TYPE		0x40
+
+/* This determines which strings to display and which to
+ * 'dump' (hex dump of content octets or DER encoding). We can
+ * only dump non character strings or everything. If we
+ * don't dump 'unknown' they are interpreted as character
+ * strings with 1 octet per character and are subject to
+ * the usual escaping options.
+ */
+
+#define ASN1_STRFLGS_DUMP_ALL		0x80
+#define ASN1_STRFLGS_DUMP_UNKNOWN	0x100
+
+/* These determine what 'dumping' does, we can dump the
+ * content octets or the DER encoding: both use the
+ * RFC2253 #XXXXX notation.
+ */
+
+#define ASN1_STRFLGS_DUMP_DER		0x200
+
+/* All the string flags consistent with RFC2253,
+ * escaping control characters isn't essential in
+ * RFC2253 but it is advisable anyway.
+ */
+
+#define ASN1_STRFLGS_RFC2253	(ASN1_STRFLGS_ESC_2253 | \
+				ASN1_STRFLGS_ESC_CTRL | \
+				ASN1_STRFLGS_ESC_MSB | \
+				ASN1_STRFLGS_UTF8_CONVERT | \
+				ASN1_STRFLGS_DUMP_UNKNOWN | \
+				ASN1_STRFLGS_DUMP_DER)
+
+DECLARE_ASN1_SET_OF(ASN1_INTEGER)
+
+typedef struct asn1_type_st
+	{
+	int type;
+	union	{
+		char *ptr;
+		ASN1_BOOLEAN		boolean;
+		ASN1_STRING *		asn1_string;
+		ASN1_OBJECT *		object;
+		ASN1_INTEGER *		integer;
+		ASN1_ENUMERATED *	enumerated;
+		ASN1_BIT_STRING *	bit_string;
+		ASN1_OCTET_STRING *	octet_string;
+		ASN1_PRINTABLESTRING *	printablestring;
+		ASN1_T61STRING *	t61string;
+		ASN1_IA5STRING *	ia5string;
+		ASN1_GENERALSTRING *	generalstring;
+		ASN1_BMPSTRING *	bmpstring;
+		ASN1_UNIVERSALSTRING *	universalstring;
+		ASN1_UTCTIME *		utctime;
+		ASN1_GENERALIZEDTIME *	generalizedtime;
+		ASN1_VISIBLESTRING *	visiblestring;
+		ASN1_UTF8STRING *	utf8string;
+		/* set and sequence are left complete and still
+		 * contain the set or sequence bytes */
+		ASN1_STRING *		set;
+		ASN1_STRING *		sequence;
+		ASN1_VALUE *		asn1_value;
+		} value;
+	} ASN1_TYPE;
+
+DECLARE_ASN1_SET_OF(ASN1_TYPE)
+
+typedef STACK_OF(ASN1_TYPE) ASN1_SEQUENCE_ANY;
+
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(ASN1_SEQUENCE_ANY, ASN1_SEQUENCE_ANY)
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(ASN1_SEQUENCE_ANY, ASN1_SET_ANY)
+
+struct X509_algor_st
+       {
+       ASN1_OBJECT *algorithm;
+       ASN1_TYPE *parameter;
+       } /* X509_ALGOR */;
+DEFINE_STACK_OF(X509_ALGOR);
+
+DECLARE_ASN1_FUNCTIONS(X509_ALGOR);
+
+typedef struct NETSCAPE_X509_st
+	{
+	ASN1_OCTET_STRING *header;
+	X509 *cert;
+	} NETSCAPE_X509;
+
+/* This is used to contain a list of bit names */
+typedef struct BIT_STRING_BITNAME_st {
+	int bitnum;
+	const char *lname;
+	const char *sname;
+} BIT_STRING_BITNAME;
+
+
+#define M_ASN1_STRING_length(x)	((x)->length)
+#define M_ASN1_STRING_length_set(x, n)	((x)->length = (n))
+#define M_ASN1_STRING_type(x)	((x)->type)
+#define M_ASN1_STRING_data(x)	((x)->data)
+
+/* Macros for string operations */
+#define M_ASN1_BIT_STRING_new()	(ASN1_BIT_STRING *)\
+		ASN1_STRING_type_new(V_ASN1_BIT_STRING)
+#define M_ASN1_BIT_STRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_BIT_STRING_dup(a) (ASN1_BIT_STRING *)\
+		ASN1_STRING_dup((const ASN1_STRING *)a)
+#define M_ASN1_BIT_STRING_cmp(a,b) ASN1_STRING_cmp(\
+		(const ASN1_STRING *)a,(const ASN1_STRING *)b)
+#define M_ASN1_BIT_STRING_set(a,b,c) ASN1_STRING_set((ASN1_STRING *)a,b,c)
+
+#define M_ASN1_INTEGER_new()	(ASN1_INTEGER *)\
+		ASN1_STRING_type_new(V_ASN1_INTEGER)
+#define M_ASN1_INTEGER_free(a)		ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_INTEGER_dup(a) (ASN1_INTEGER *)\
+		ASN1_STRING_dup((const ASN1_STRING *)a)
+#define M_ASN1_INTEGER_cmp(a,b)	ASN1_STRING_cmp(\
+		(const ASN1_STRING *)a,(const ASN1_STRING *)b)
+
+#define M_ASN1_ENUMERATED_new()	(ASN1_ENUMERATED *)\
+		ASN1_STRING_type_new(V_ASN1_ENUMERATED)
+#define M_ASN1_ENUMERATED_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_ENUMERATED_dup(a) (ASN1_ENUMERATED *)\
+		ASN1_STRING_dup((const ASN1_STRING *)a)
+#define M_ASN1_ENUMERATED_cmp(a,b)	ASN1_STRING_cmp(\
+		(const ASN1_STRING *)a,(const ASN1_STRING *)b)
+
+#define M_ASN1_OCTET_STRING_new()	(ASN1_OCTET_STRING *)\
+		ASN1_STRING_type_new(V_ASN1_OCTET_STRING)
+#define M_ASN1_OCTET_STRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_OCTET_STRING_dup(a) (ASN1_OCTET_STRING *)\
+		ASN1_STRING_dup((const ASN1_STRING *)a)
+#define M_ASN1_OCTET_STRING_cmp(a,b) ASN1_STRING_cmp(\
+		(const ASN1_STRING *)a,(const ASN1_STRING *)b)
+#define M_ASN1_OCTET_STRING_set(a,b,c)	ASN1_STRING_set((ASN1_STRING *)a,b,c)
+#define M_ASN1_OCTET_STRING_print(a,b)	ASN1_STRING_print(a,(ASN1_STRING *)b)
+#define M_i2d_ASN1_OCTET_STRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_OCTET_STRING,\
+		V_ASN1_UNIVERSAL)
+
+#define B_ASN1_TIME \
+			B_ASN1_UTCTIME | \
+			B_ASN1_GENERALIZEDTIME
+
+#define B_ASN1_PRINTABLE \
+			B_ASN1_NUMERICSTRING| \
+			B_ASN1_PRINTABLESTRING| \
+			B_ASN1_T61STRING| \
+			B_ASN1_IA5STRING| \
+			B_ASN1_BIT_STRING| \
+			B_ASN1_UNIVERSALSTRING|\
+			B_ASN1_BMPSTRING|\
+			B_ASN1_UTF8STRING|\
+			B_ASN1_SEQUENCE|\
+			B_ASN1_UNKNOWN
+
+#define B_ASN1_DIRECTORYSTRING \
+			B_ASN1_PRINTABLESTRING| \
+			B_ASN1_TELETEXSTRING|\
+			B_ASN1_BMPSTRING|\
+			B_ASN1_UNIVERSALSTRING|\
+			B_ASN1_UTF8STRING
+
+#define B_ASN1_DISPLAYTEXT \
+			B_ASN1_IA5STRING| \
+			B_ASN1_VISIBLESTRING| \
+			B_ASN1_BMPSTRING|\
+			B_ASN1_UTF8STRING
+
+#define M_ASN1_PRINTABLE_new()	ASN1_STRING_type_new(V_ASN1_T61STRING)
+#define M_ASN1_PRINTABLE_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_PRINTABLE(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,\
+		pp,a->type,V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_PRINTABLE(a,pp,l) \
+		d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, \
+			B_ASN1_PRINTABLE)
+
+#define M_DIRECTORYSTRING_new() ASN1_STRING_type_new(V_ASN1_PRINTABLESTRING)
+#define M_DIRECTORYSTRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_DIRECTORYSTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,\
+						pp,a->type,V_ASN1_UNIVERSAL)
+#define M_d2i_DIRECTORYSTRING(a,pp,l) \
+		d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, \
+			B_ASN1_DIRECTORYSTRING)
+
+#define M_DISPLAYTEXT_new() ASN1_STRING_type_new(V_ASN1_VISIBLESTRING)
+#define M_DISPLAYTEXT_free(a) ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_DISPLAYTEXT(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,\
+						pp,a->type,V_ASN1_UNIVERSAL)
+#define M_d2i_DISPLAYTEXT(a,pp,l) \
+		d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, \
+			B_ASN1_DISPLAYTEXT)
+
+#define M_ASN1_PRINTABLESTRING_new() (ASN1_PRINTABLESTRING *)\
+		ASN1_STRING_type_new(V_ASN1_PRINTABLESTRING)
+#define M_ASN1_PRINTABLESTRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_PRINTABLESTRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_PRINTABLESTRING,\
+		V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_PRINTABLESTRING(a,pp,l) \
+		(ASN1_PRINTABLESTRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_PRINTABLESTRING)
+
+#define M_ASN1_T61STRING_new()	(ASN1_T61STRING *)\
+		ASN1_STRING_type_new(V_ASN1_T61STRING)
+#define M_ASN1_T61STRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_T61STRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_T61STRING,\
+		V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_T61STRING(a,pp,l) \
+		(ASN1_T61STRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_T61STRING)
+
+#define M_ASN1_IA5STRING_new()	(ASN1_IA5STRING *)\
+		ASN1_STRING_type_new(V_ASN1_IA5STRING)
+#define M_ASN1_IA5STRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_IA5STRING_dup(a)	\
+		(ASN1_IA5STRING *)ASN1_STRING_dup((const ASN1_STRING *)a)
+#define M_i2d_ASN1_IA5STRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_IA5STRING,\
+			V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_IA5STRING(a,pp,l) \
+		(ASN1_IA5STRING *)d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l,\
+			B_ASN1_IA5STRING)
+
+#define M_ASN1_UTCTIME_new()	(ASN1_UTCTIME *)\
+		ASN1_STRING_type_new(V_ASN1_UTCTIME)
+#define M_ASN1_UTCTIME_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_UTCTIME_dup(a) (ASN1_UTCTIME *)\
+		ASN1_STRING_dup((const ASN1_STRING *)a)
+
+#define M_ASN1_GENERALIZEDTIME_new()	(ASN1_GENERALIZEDTIME *)\
+		ASN1_STRING_type_new(V_ASN1_GENERALIZEDTIME)
+#define M_ASN1_GENERALIZEDTIME_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_GENERALIZEDTIME_dup(a) (ASN1_GENERALIZEDTIME *)ASN1_STRING_dup(\
+	(const ASN1_STRING *)a)
+
+#define M_ASN1_TIME_new()	(ASN1_TIME *)\
+		ASN1_STRING_type_new(V_ASN1_UTCTIME)
+#define M_ASN1_TIME_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_ASN1_TIME_dup(a) (ASN1_TIME *)\
+	ASN1_STRING_dup((const ASN1_STRING *)a)
+
+#define M_ASN1_GENERALSTRING_new()	(ASN1_GENERALSTRING *)\
+		ASN1_STRING_type_new(V_ASN1_GENERALSTRING)
+#define M_ASN1_GENERALSTRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_GENERALSTRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_GENERALSTRING,\
+			V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_GENERALSTRING(a,pp,l) \
+		(ASN1_GENERALSTRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_GENERALSTRING)
+
+#define M_ASN1_UNIVERSALSTRING_new()	(ASN1_UNIVERSALSTRING *)\
+		ASN1_STRING_type_new(V_ASN1_UNIVERSALSTRING)
+#define M_ASN1_UNIVERSALSTRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_UNIVERSALSTRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UNIVERSALSTRING,\
+			V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_UNIVERSALSTRING(a,pp,l) \
+		(ASN1_UNIVERSALSTRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_UNIVERSALSTRING)
+
+#define M_ASN1_BMPSTRING_new()	(ASN1_BMPSTRING *)\
+		ASN1_STRING_type_new(V_ASN1_BMPSTRING)
+#define M_ASN1_BMPSTRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_BMPSTRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_BMPSTRING,\
+			V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_BMPSTRING(a,pp,l) \
+		(ASN1_BMPSTRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_BMPSTRING)
+
+#define M_ASN1_VISIBLESTRING_new()	(ASN1_VISIBLESTRING *)\
+		ASN1_STRING_type_new(V_ASN1_VISIBLESTRING)
+#define M_ASN1_VISIBLESTRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_VISIBLESTRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_VISIBLESTRING,\
+			V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_VISIBLESTRING(a,pp,l) \
+		(ASN1_VISIBLESTRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_VISIBLESTRING)
+
+#define M_ASN1_UTF8STRING_new()	(ASN1_UTF8STRING *)\
+		ASN1_STRING_type_new(V_ASN1_UTF8STRING)
+#define M_ASN1_UTF8STRING_free(a)	ASN1_STRING_free((ASN1_STRING *)a)
+#define M_i2d_ASN1_UTF8STRING(a,pp) \
+		i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UTF8STRING,\
+			V_ASN1_UNIVERSAL)
+#define M_d2i_ASN1_UTF8STRING(a,pp,l) \
+		(ASN1_UTF8STRING *)d2i_ASN1_type_bytes\
+		((ASN1_STRING **)a,pp,l,B_ASN1_UTF8STRING)
+
+  /* for the is_set parameter to i2d_ASN1_SET */
+#define IS_SEQUENCE	0
+#define IS_SET		1
+
+DECLARE_ASN1_FUNCTIONS_fname(ASN1_TYPE, ASN1_ANY, ASN1_TYPE)
+
+int ASN1_TYPE_get(ASN1_TYPE *a);
+void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value);
+int ASN1_TYPE_set1(ASN1_TYPE *a, int type, const void *value);
+int            ASN1_TYPE_cmp(ASN1_TYPE *a, ASN1_TYPE *b);
+
+ASN1_OBJECT *	ASN1_OBJECT_new(void );
+void		ASN1_OBJECT_free(ASN1_OBJECT *a);
+int		i2d_ASN1_OBJECT(ASN1_OBJECT *a,unsigned char **pp);
+ASN1_OBJECT *	c2i_ASN1_OBJECT(ASN1_OBJECT **a,const unsigned char **pp,
+			long length);
+ASN1_OBJECT *	d2i_ASN1_OBJECT(ASN1_OBJECT **a,const unsigned char **pp,
+			long length);
+
+DECLARE_ASN1_ITEM(ASN1_OBJECT)
+
+DECLARE_ASN1_SET_OF(ASN1_OBJECT)
+
+ASN1_STRING *	ASN1_STRING_new(void);
+void		ASN1_STRING_free(ASN1_STRING *a);
+int		ASN1_STRING_copy(ASN1_STRING *dst, const ASN1_STRING *str);
+ASN1_STRING *	ASN1_STRING_dup(const ASN1_STRING *a);
+ASN1_STRING *	ASN1_STRING_type_new(int type );
+int 		ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b);
+  /* Since this is used to store all sorts of things, via macros, for now, make
+     its data void * */
+int 		ASN1_STRING_set(ASN1_STRING *str, const void *data, int len);
+void		ASN1_STRING_set0(ASN1_STRING *str, void *data, int len);
+int ASN1_STRING_length(const ASN1_STRING *x);
+void ASN1_STRING_length_set(ASN1_STRING *x, int n);
+int ASN1_STRING_type(ASN1_STRING *x);
+unsigned char * ASN1_STRING_data(ASN1_STRING *x);
+
+DECLARE_ASN1_FUNCTIONS(ASN1_BIT_STRING)
+int		i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a,unsigned char **pp);
+ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,const unsigned char **pp,
+			long length);
+int		ASN1_BIT_STRING_set(ASN1_BIT_STRING *a, unsigned char *d,
+			int length );
+int		ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value);
+int		ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *a, int n);
+int            ASN1_BIT_STRING_check(ASN1_BIT_STRING *a,
+                                     unsigned char *flags, int flags_len);
+
+#ifndef OPENSSL_NO_BIO
+int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs,
+				BIT_STRING_BITNAME *tbl, int indent);
+#endif
+int ASN1_BIT_STRING_num_asc(char *name, BIT_STRING_BITNAME *tbl);
+int ASN1_BIT_STRING_set_asc(ASN1_BIT_STRING *bs, char *name, int value,
+				BIT_STRING_BITNAME *tbl);
+
+int		i2d_ASN1_BOOLEAN(int a,unsigned char **pp);
+int 		d2i_ASN1_BOOLEAN(int *a,const unsigned char **pp,long length);
+
+DECLARE_ASN1_FUNCTIONS(ASN1_INTEGER)
+int		i2c_ASN1_INTEGER(ASN1_INTEGER *a,unsigned char **pp);
+ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a,const unsigned char **pp,
+			long length);
+ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a,const unsigned char **pp,
+			long length);
+ASN1_INTEGER *	ASN1_INTEGER_dup(const ASN1_INTEGER *x);
+int ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y);
+
+DECLARE_ASN1_FUNCTIONS(ASN1_ENUMERATED)
+
+int ASN1_UTCTIME_check(const ASN1_UTCTIME *a);
+ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s,time_t t);
+ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
+				int offset_day, long offset_sec);
+int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str);
+int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
+#if 0
+time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s);
+#endif
+
+int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *a);
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s,time_t t);
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
+	     time_t t, int offset_day, long offset_sec);
+int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str);
+int ASN1_TIME_diff(int *pday, int *psec,
+			const ASN1_TIME *from, const ASN1_TIME *to);
+
+DECLARE_ASN1_FUNCTIONS(ASN1_OCTET_STRING)
+ASN1_OCTET_STRING *	ASN1_OCTET_STRING_dup(const ASN1_OCTET_STRING *a);
+int 	ASN1_OCTET_STRING_cmp(const ASN1_OCTET_STRING *a, const ASN1_OCTET_STRING *b);
+int 	ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *str, const unsigned char *data, int len);
+
+DECLARE_ASN1_FUNCTIONS(ASN1_VISIBLESTRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_UNIVERSALSTRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_UTF8STRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_NULL)
+DECLARE_ASN1_FUNCTIONS(ASN1_BMPSTRING)
+
+int UTF8_getc(const unsigned char *str, int len, unsigned long *val);
+int UTF8_putc(unsigned char *str, int len, unsigned long value);
+
+DECLARE_ASN1_FUNCTIONS_name(ASN1_STRING, ASN1_PRINTABLE)
+
+DECLARE_ASN1_FUNCTIONS_name(ASN1_STRING, DIRECTORYSTRING)
+DECLARE_ASN1_FUNCTIONS_name(ASN1_STRING, DISPLAYTEXT)
+DECLARE_ASN1_FUNCTIONS(ASN1_PRINTABLESTRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_T61STRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_IA5STRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_GENERALSTRING)
+DECLARE_ASN1_FUNCTIONS(ASN1_UTCTIME)
+DECLARE_ASN1_FUNCTIONS(ASN1_GENERALIZEDTIME)
+DECLARE_ASN1_FUNCTIONS(ASN1_TIME)
+
+DECLARE_ASN1_ITEM(ASN1_OCTET_STRING_NDEF)
+
+ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s,time_t t);
+ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s,time_t t,
+				int offset_day, long offset_sec);
+int ASN1_TIME_check(ASN1_TIME *t);
+ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *t, ASN1_GENERALIZEDTIME **out);
+int ASN1_TIME_set_string(ASN1_TIME *s, const char *str);
+
+int i2d_ASN1_SET(STACK_OF(OPENSSL_BLOCK) *a, unsigned char **pp,
+		 i2d_of_void *i2d, int ex_tag, int ex_class,
+		 int is_set);
+STACK_OF(OPENSSL_BLOCK) *d2i_ASN1_SET(STACK_OF(OPENSSL_BLOCK) **a,
+			      const unsigned char **pp,
+			      long length, d2i_of_void *d2i,
+			      void (*free_func)(OPENSSL_BLOCK), int ex_tag,
+			      int ex_class);
+
+#ifndef OPENSSL_NO_BIO
+int i2a_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *a);
+int a2i_ASN1_INTEGER(BIO *bp,ASN1_INTEGER *bs,char *buf,int size);
+int i2a_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *a);
+int a2i_ASN1_ENUMERATED(BIO *bp,ASN1_ENUMERATED *bs,char *buf,int size);
+int i2a_ASN1_OBJECT(BIO *bp,ASN1_OBJECT *a);
+int a2i_ASN1_STRING(BIO *bp,ASN1_STRING *bs,char *buf,int size);
+int i2a_ASN1_STRING(BIO *bp, ASN1_STRING *a, int type);
+#endif
+int i2t_ASN1_OBJECT(char *buf,int buf_len,ASN1_OBJECT *a);
+
+int a2d_ASN1_OBJECT(unsigned char *out,int olen, const char *buf, int num);
+ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,int len,
+	const char *sn, const char *ln);
+
+int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
+long ASN1_INTEGER_get(const ASN1_INTEGER *a);
+ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai);
+BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai,BIGNUM *bn);
+
+int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v);
+long ASN1_ENUMERATED_get(ASN1_ENUMERATED *a);
+ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(BIGNUM *bn, ASN1_ENUMERATED *ai);
+BIGNUM *ASN1_ENUMERATED_to_BN(ASN1_ENUMERATED *ai,BIGNUM *bn);
+
+/* General */
+/* given a string, return the correct type, max is the maximum length */
+int ASN1_PRINTABLE_type(const unsigned char *s, int max);
+
+int i2d_ASN1_bytes(ASN1_STRING *a, unsigned char **pp, int tag, int xclass);
+ASN1_STRING *d2i_ASN1_bytes(ASN1_STRING **a, const unsigned char **pp,
+	long length, int Ptag, int Pclass);
+unsigned long ASN1_tag2bit(int tag);
+/* type is one or more of the B_ASN1_ values. */
+ASN1_STRING *d2i_ASN1_type_bytes(ASN1_STRING **a,const unsigned char **pp,
+		long length,int type);
+
+/* PARSING */
+int asn1_Finish(ASN1_CTX *c);
+int asn1_const_Finish(ASN1_const_CTX *c);
+
+/* SPECIALS */
+int ASN1_get_object(const unsigned char **pp, long *plength, int *ptag,
+	int *pclass, long omax);
+int ASN1_check_infinite_end(unsigned char **p,long len);
+int ASN1_const_check_infinite_end(const unsigned char **p,long len);
+void ASN1_put_object(unsigned char **pp, int constructed, int length,
+	int tag, int xclass);
+int ASN1_put_eoc(unsigned char **pp);
+int ASN1_object_size(int constructed, int length, int tag);
+
+/* Used to implement other functions */
+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)))
+
+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
+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)))
+
+void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x);
+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)))
+
+int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x);
+int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags);
+#endif
+
+int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in);
+
+#ifndef OPENSSL_NO_BIO
+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)))
+
+void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
+int ASN1_i2d_bio(i2d_of_void *i2d,BIO *out, unsigned char *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)))
+
+int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x);
+int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);
+int ASN1_GENERALIZEDTIME_print(BIO *fp, const ASN1_GENERALIZEDTIME *a);
+int ASN1_TIME_print(BIO *fp, const ASN1_TIME *a);
+int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v);
+int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags);
+int ASN1_bn_print(BIO *bp, const char *number, const BIGNUM *num,
+				unsigned char *buf, int off);
+int ASN1_parse(BIO *bp,const unsigned char *pp,long len,int indent);
+int ASN1_parse_dump(BIO *bp,const unsigned char *pp,long len,int indent,int dump);
+#endif
+const char *ASN1_tag2str(int tag);
+
+/* Used to load and write netscape format cert */
+
+DECLARE_ASN1_FUNCTIONS(NETSCAPE_X509)
+
+int ASN1_UNIVERSALSTRING_to_string(ASN1_UNIVERSALSTRING *s);
+
+STACK_OF(OPENSSL_BLOCK) *ASN1_seq_unpack(const unsigned char *buf, int len,
+				 d2i_of_void *d2i, void (*free_func)(OPENSSL_BLOCK));
+unsigned char *ASN1_seq_pack(STACK_OF(OPENSSL_BLOCK) *safes, i2d_of_void *i2d,
+			     unsigned char **buf, int *len );
+void *ASN1_unpack_string(ASN1_STRING *oct, d2i_of_void *d2i);
+void *ASN1_item_unpack(ASN1_STRING *oct, const ASN1_ITEM *it);
+ASN1_STRING *ASN1_pack_string(void *obj, i2d_of_void *i2d,
+			      ASN1_OCTET_STRING **oct);
+
+#define ASN1_pack_string_of(type,obj,i2d,oct) \
+    (ASN1_pack_string(CHECKED_PTR_OF(type, obj), \
+		      CHECKED_I2D_OF(type, i2d), \
+		      oct))
+
+ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_OCTET_STRING **oct);
+
+void ASN1_STRING_set_default_mask(unsigned long mask);
+int ASN1_STRING_set_default_mask_asc(const char *p);
+unsigned long ASN1_STRING_get_default_mask(void);
+int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
+					int inform, unsigned long mask);
+int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
+					int inform, unsigned long mask, 
+					long minsize, long maxsize);
+
+ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out, 
+		const unsigned char *in, int inlen, int inform, int nid);
+ASN1_STRING_TABLE *ASN1_STRING_TABLE_get(int nid);
+int ASN1_STRING_TABLE_add(int, long, long, unsigned long, unsigned long);
+void ASN1_STRING_TABLE_cleanup(void);
+
+/* ASN1 template functions */
+
+/* Old API compatible functions */
+ASN1_VALUE *ASN1_item_new(const ASN1_ITEM *it);
+void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it);
+ASN1_VALUE * ASN1_item_d2i(ASN1_VALUE **val, const unsigned char **in, long len, const ASN1_ITEM *it);
+int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it);
+int ASN1_item_ndef_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it);
+
+void ASN1_add_oid_module(void);
+
+ASN1_TYPE *ASN1_generate_nconf(char *str, CONF *nconf);
+ASN1_TYPE *ASN1_generate_v3(char *str, X509V3_CTX *cnf);
+
+/* ASN1 Print flags */
+
+/* Indicate missing OPTIONAL fields */
+#define ASN1_PCTX_FLAGS_SHOW_ABSENT		0x001	
+/* Mark start and end of SEQUENCE */
+#define ASN1_PCTX_FLAGS_SHOW_SEQUENCE		0x002
+/* Mark start and end of SEQUENCE/SET OF */
+#define ASN1_PCTX_FLAGS_SHOW_SSOF		0x004
+/* Show the ASN1 type of primitives */
+#define ASN1_PCTX_FLAGS_SHOW_TYPE		0x008
+/* Don't show ASN1 type of ANY */
+#define ASN1_PCTX_FLAGS_NO_ANY_TYPE		0x010
+/* Don't show ASN1 type of MSTRINGs */
+#define ASN1_PCTX_FLAGS_NO_MSTRING_TYPE		0x020
+/* Don't show field names in SEQUENCE */
+#define ASN1_PCTX_FLAGS_NO_FIELD_NAME		0x040
+/* Show structure names of each SEQUENCE field */
+#define ASN1_PCTX_FLAGS_SHOW_FIELD_STRUCT_NAME	0x080
+/* Don't show structure name even at top level */
+#define ASN1_PCTX_FLAGS_NO_STRUCT_NAME		0x100
+
+int ASN1_item_print(BIO *out, ASN1_VALUE *ifld, int indent,
+				const ASN1_ITEM *it, const ASN1_PCTX *pctx);
+ASN1_PCTX *ASN1_PCTX_new(void);
+void ASN1_PCTX_free(ASN1_PCTX *p);
+unsigned long ASN1_PCTX_get_flags(ASN1_PCTX *p);
+void ASN1_PCTX_set_flags(ASN1_PCTX *p, unsigned long flags);
+unsigned long ASN1_PCTX_get_nm_flags(ASN1_PCTX *p);
+void ASN1_PCTX_set_nm_flags(ASN1_PCTX *p, unsigned long flags);
+unsigned long ASN1_PCTX_get_cert_flags(ASN1_PCTX *p);
+void ASN1_PCTX_set_cert_flags(ASN1_PCTX *p, unsigned long flags);
+unsigned long ASN1_PCTX_get_oid_flags(ASN1_PCTX *p);
+void ASN1_PCTX_set_oid_flags(ASN1_PCTX *p, unsigned long flags);
+unsigned long ASN1_PCTX_get_str_flags(ASN1_PCTX *p);
+void ASN1_PCTX_set_str_flags(ASN1_PCTX *p, unsigned long flags);
+
+BIO_METHOD *BIO_f_asn1(void);
+
+BIO *BIO_new_NDEF(BIO *out, ASN1_VALUE *val, const ASN1_ITEM *it);
+
+int i2d_ASN1_bio_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+				const ASN1_ITEM *it);
+int PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+				const char *hdr,
+				const ASN1_ITEM *it);
+ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it);
+int SMIME_crlf_copy(BIO *in, BIO *out, int flags);
+int SMIME_text(BIO *in, BIO *out);
+
+/* BEGIN ERROR CODES */
+/* The following lines are auto generated by the script mkerr.pl. Any changes
+ * made after this point may be overwritten when the script is next run.
+ */
+void ERR_load_ASN1_strings(void);
+
+typedef int asn1_ps_func(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+int BIO_asn1_set_prefix(BIO *b, asn1_ps_func *prefix,
+					asn1_ps_func *prefix_free);
+int BIO_asn1_get_prefix(BIO *b, asn1_ps_func **pprefix,
+					asn1_ps_func **pprefix_free);
+int BIO_asn1_set_suffix(BIO *b, asn1_ps_func *suffix,
+					asn1_ps_func *suffix_free);
+int BIO_asn1_get_suffix(BIO *b, asn1_ps_func **psuffix,
+					asn1_ps_func **psuffix_free);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#define ASN1_F_asn1_template_ex_d2i 100
+#define ASN1_F_ASN1_dup 101
+#define ASN1_F_a2i_ASN1_STRING 102
+#define ASN1_F_ASN1_d2i_fp 103
+#define ASN1_F_d2i_ASN1_OBJECT 104
+#define ASN1_F_asn1_item_ex_combine_new 105
+#define ASN1_F_ASN1_template_new 106
+#define ASN1_F_asn1_do_adb 107
+#define ASN1_F_asn1_d2i_read_bio 108
+#define ASN1_F_asn1_ex_c2i 109
+#define ASN1_F_c2i_ASN1_INTEGER 110
+#define ASN1_F_ASN1_PCTX_new 111
+#define ASN1_F_ASN1_item_unpack 112
+#define ASN1_F_d2i_ASN1_type_bytes 113
+#define ASN1_F_a2i_ASN1_INTEGER 114
+#define ASN1_F_asn1_collect 115
+#define ASN1_F_ASN1_item_dup 116
+#define ASN1_F_ASN1_ENUMERATED_set 117
+#define ASN1_F_c2i_ASN1_OBJECT 118
+#define ASN1_F_ASN1_unpack_string 119
+#define ASN1_F_d2i_ASN1_UINTEGER 120
+#define ASN1_F_long_c2i 121
+#define ASN1_F_ASN1_seq_pack 122
+#define ASN1_F_a2d_ASN1_OBJECT 123
+#define ASN1_F_ASN1_STRING_type_new 124
+#define ASN1_F_ASN1_INTEGER_set 125
+#define ASN1_F_BN_to_ASN1_INTEGER 126
+#define ASN1_F_BIO_new_NDEF 127
+#define ASN1_F_ASN1_ENUMERATED_to_BN 128
+#define ASN1_F_ASN1_item_ex_d2i 129
+#define ASN1_F_ASN1_INTEGER_to_BN 130
+#define ASN1_F_i2d_ASN1_TIME 131
+#define ASN1_F_ASN1_TIME_adj 132
+#define ASN1_F_ASN1_BIT_STRING_set_bit 133
+#define ASN1_F_ASN1_seq_unpack 134
+#define ASN1_F_ASN1_item_pack 135
+#define ASN1_F_ASN1_STRING_set 136
+#define ASN1_F_ASN1_UTCTIME_adj 137
+#define ASN1_F_ASN1_mbstring_ncopy 138
+#define ASN1_F_d2i_ASN1_BOOLEAN 139
+#define ASN1_F_ASN1_OBJECT_new 140
+#define ASN1_F_asn1_template_noexp_d2i 141
+#define ASN1_F_c2i_ASN1_BIT_STRING 142
+#define ASN1_F_BN_to_ASN1_ENUMERATED 143
+#define ASN1_F_asn1_d2i_ex_primitive 144
+#define ASN1_F_ASN1_i2d_bio 145
+#define ASN1_F_ASN1_item_i2d_bio 146
+#define ASN1_F_d2i_ASN1_UTCTIME 147
+#define ASN1_F_ASN1_STRING_TABLE_add 148
+#define ASN1_F_asn1_find_end 149
+#define ASN1_F_ASN1_item_d2i_fp 150
+#define ASN1_F_collect_data 151
+#define ASN1_F_asn1_check_tlen 152
+#define ASN1_F_ASN1_i2d_fp 153
+#define ASN1_F_ASN1_item_i2d_fp 154
+#define ASN1_F_ASN1_GENERALIZEDTIME_adj 155
+#define ASN1_F_asn1_collate_primitive 156
+#define ASN1_F_ASN1_pack_string 157
+#define ASN1_F_ASN1_get_object 158
+#define ASN1_F_d2i_ASN1_bytes 159
+#define ASN1_F_a2i_ASN1_ENUMERATED 160
+#define ASN1_R_ASN1_SIG_PARSE_ERROR 100
+#define ASN1_R_ADDING_OBJECT 101
+#define ASN1_R_MIME_NO_CONTENT_TYPE 102
+#define ASN1_R_UNKNOWN_OBJECT_TYPE 103
+#define ASN1_R_ILLEGAL_FORMAT 104
+#define ASN1_R_HEADER_TOO_LONG 105
+#define ASN1_R_INVALID_UTF8STRING 106
+#define ASN1_R_EXPLICIT_LENGTH_MISMATCH 107
+#define ASN1_R_ILLEGAL_TAGGED_ANY 108
+#define ASN1_R_DATA_IS_WRONG 109
+#define ASN1_R_NOT_ASCII_FORMAT 110
+#define ASN1_R_NOT_ENOUGH_DATA 111
+#define ASN1_R_MSTRING_NOT_UNIVERSAL 112
+#define ASN1_R_UNKOWN_FORMAT 113
+#define ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM 114
+#define ASN1_R_BAD_PASSWORD_READ 115
+#define ASN1_R_BAD_OBJECT_HEADER 116
+#define ASN1_R_ILLEGAL_CHARACTERS 117
+#define ASN1_R_CONTEXT_NOT_INITIALISED 118
+#define ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG 119
+#define ASN1_R_BN_LIB 120
+#define ASN1_R_NO_MATCHING_CHOICE_TYPE 121
+#define ASN1_R_SEQUENCE_NOT_CONSTRUCTED 122
+#define ASN1_R_ASN1_PARSE_ERROR 123
+#define ASN1_R_NO_MULTIPART_BOUNDARY 124
+#define ASN1_R_INVALID_SEPARATOR 125
+#define ASN1_R_MALLOC_FAILURE 126
+#define ASN1_R_ILLEGAL_NULL 127
+#define ASN1_R_INVALID_MIME_TYPE 128
+#define ASN1_R_INVALID_NUMBER 129
+#define ASN1_R_STRING_TOO_LONG 130
+#define ASN1_R_BAD_GET_ASN1_OBJECT_CALL 131
+#define ASN1_R_UNABLE_TO_DECODE_RSA_KEY 132
+#define ASN1_R_EXPECTING_A_TIME 133
+#define ASN1_R_TAG_VALUE_TOO_HIGH 134
+#define ASN1_R_NESTED_ASN1_STRING 135
+#define ASN1_R_ILLEGAL_BITSTRING_FORMAT 136
+#define ASN1_R_MISSING_SECOND_NUMBER 137
+#define ASN1_R_TIME_NOT_ASCII_FORMAT 138
+#define ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD 139
+#define ASN1_R_WRONG_TYPE 140
+#define ASN1_R_EXPECTING_AN_INTEGER 141
+#define ASN1_R_DEPTH_EXCEEDED 142
+#define ASN1_R_ILLEGAL_OBJECT 143
+#define ASN1_R_UNKNOWN_TAG 144
+#define ASN1_R_ILLEGAL_IMPLICIT_TAG 145
+#define ASN1_R_AUX_ERROR 146
+#define ASN1_R_SEQUENCE_LENGTH_MISMATCH 147
+#define ASN1_R_FIELD_MISSING 148
+#define ASN1_R_TYPE_NOT_CONSTRUCTED 149
+#define ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH 150
+#define ASN1_R_FIRST_NUM_TOO_LARGE 151
+#define ASN1_R_INVALID_DIGIT 152
+#define ASN1_R_MSTRING_WRONG_TAG 153
+#define ASN1_R_OBJECT_NOT_ASCII_FORMAT 154
+#define ASN1_R_UNSUPPORTED_TYPE 155
+#define ASN1_R_ERROR_LOADING_SECTION 156
+#define ASN1_R_ODD_NUMBER_OF_CHARS 157
+#define ASN1_R_ASN1_LENGTH_MISMATCH 158
+#define ASN1_R_MISSING_EOC 159
+#define ASN1_R_ILLEGAL_INTEGER 160
+#define ASN1_R_ILLEGAL_HEX 161
+#define ASN1_R_NESTED_ASN1_ERROR 162
+#define ASN1_R_TOO_LONG 163
+#define ASN1_R_LENGTH_ERROR 164
+#define ASN1_R_DECODING_ERROR 165
+#define ASN1_R_MIME_SIG_PARSE_ERROR 166
+#define ASN1_R_ILLEGAL_NULL_VALUE 167
+#define ASN1_R_EXPECTING_A_BOOLEAN 168
+#define ASN1_R_STREAMING_NOT_SUPPORTED 169
+#define ASN1_R_INVALID_BMPSTRING_LENGTH 170
+#define ASN1_R_INTEGER_NOT_ASCII_FORMAT 171
+#define ASN1_R_INVALID_MODIFIER 172
+#define ASN1_R_UNEXPECTED_EOC 173
+#define ASN1_R_ILLEGAL_NESTED_TAGGING 174
+#define ASN1_R_IV_TOO_LARGE 175
+#define ASN1_R_INTEGER_TOO_LARGE_FOR_LONG 176
+#define ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE 177
+#define ASN1_R_BUFFER_TOO_SMALL 178
+#define ASN1_R_INVALID_UNIVERSALSTRING_LENGTH 179
+#define ASN1_R_WRONG_PUBLIC_KEY_TYPE 180
+#define ASN1_R_UNSUPPORTED_ENCRYPTION_ALGORITHM 181
+#define ASN1_R_MIME_PARSE_ERROR 182
+#define ASN1_R_INVALID_OBJECT_ENCODING 183
+#define ASN1_R_PRIVATE_KEY_HEADER_MISSING 184
+#define ASN1_R_UNSUPPORTED_CIPHER 185
+#define ASN1_R_NO_MULTIPART_BODY_FAILURE 186
+#define ASN1_R_NO_CONTENT_TYPE 187
+#define ASN1_R_SECOND_NUMBER_TOO_LARGE 188
+#define ASN1_R_INVALID_TIME_FORMAT 189
+#define ASN1_R_NO_DEFAULT_DIGEST 190
+#define ASN1_R_ERROR_SETTING_CIPHER_PARAMS 191
+#define ASN1_R_EXPECTING_AN_OBJECT 192
+#define ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE 193
+#define ASN1_R_ERROR_GETTING_TIME 194
+#define ASN1_R_MISSING_VALUE 195
+#define ASN1_R_LIST_ERROR 196
+#define ASN1_R_DECODE_ERROR 197
+#define ASN1_R_NON_HEX_CHARACTERS 198
+#define ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE 199
+#define ASN1_R_UNKNOWN_FORMAT 200
+#define ASN1_R_EXPECTING_AN_ASN1_SEQUENCE 201
+#define ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM 202
+#define ASN1_R_STRING_TOO_SHORT 203
+#define ASN1_R_ILLEGAL_OPTIONAL_ANY 204
+#define ASN1_R_BMPSTRING_IS_WRONG_LENGTH 205
+#define ASN1_R_NO_SIG_CONTENT_TYPE 206
+#define ASN1_R_ENCODE_ERROR 207
+#define ASN1_R_SHORT_LINE 208
+#define ASN1_R_ILLEGAL_TIME_VALUE 209
+#define ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY 210
+#define ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER 211
+#define ASN1_R_BAD_CLASS 212
+#define ASN1_R_BAD_TAG 213
+#define ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE 214
+#define ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED 215
+#define ASN1_R_ILLEGAL_BOOLEAN 216
+#define ASN1_R_SIG_INVALID_MIME_TYPE 217
+#define ASN1_R_NULL_IS_WRONG_LENGTH 218
+#define ASN1_R_MISSING_ASN1_EOS 219
+#define ASN1_R_ERROR_PARSING_SET_ELEMENT 220
+#define ASN1_R_WRONG_TAG 221
+#define ASN1_R_BOOLEAN_IS_WRONG_LENGTH 222
+#define ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED 223
+
+#endif
diff --git a/crypto/asn1/asn1_error.c b/crypto/asn1/asn1_error.c
new file mode 100644
index 0000000..3354ffe
--- /dev/null
+++ b/crypto/asn1/asn1_error.c
@@ -0,0 +1,206 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "asn1.h"
+
+const ERR_STRING_DATA ASN1_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_BIT_STRING_set_bit, 0), "ASN1_BIT_STRING_set_bit"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_ENUMERATED_set, 0), "ASN1_ENUMERATED_set"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_ENUMERATED_to_BN, 0), "ASN1_ENUMERATED_to_BN"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_GENERALIZEDTIME_adj, 0), "ASN1_GENERALIZEDTIME_adj"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_INTEGER_set, 0), "ASN1_INTEGER_set"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_INTEGER_to_BN, 0), "ASN1_INTEGER_to_BN"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_OBJECT_new, 0), "ASN1_OBJECT_new"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_PCTX_new, 0), "ASN1_PCTX_new"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_STRING_TABLE_add, 0), "ASN1_STRING_TABLE_add"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_STRING_set, 0), "ASN1_STRING_set"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_STRING_type_new, 0), "ASN1_STRING_type_new"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_TIME_adj, 0), "ASN1_TIME_adj"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_UTCTIME_adj, 0), "ASN1_UTCTIME_adj"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_d2i_fp, 0), "ASN1_d2i_fp"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_dup, 0), "ASN1_dup"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_get_object, 0), "ASN1_get_object"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_i2d_bio, 0), "ASN1_i2d_bio"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_i2d_fp, 0), "ASN1_i2d_fp"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_d2i_fp, 0), "ASN1_item_d2i_fp"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_dup, 0), "ASN1_item_dup"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_ex_d2i, 0), "ASN1_item_ex_d2i"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_i2d_bio, 0), "ASN1_item_i2d_bio"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_i2d_fp, 0), "ASN1_item_i2d_fp"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_pack, 0), "ASN1_item_pack"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_item_unpack, 0), "ASN1_item_unpack"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_mbstring_ncopy, 0), "ASN1_mbstring_ncopy"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_pack_string, 0), "ASN1_pack_string"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_seq_pack, 0), "ASN1_seq_pack"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_seq_unpack, 0), "ASN1_seq_unpack"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_template_new, 0), "ASN1_template_new"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_ASN1_unpack_string, 0), "ASN1_unpack_string"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_BIO_new_NDEF, 0), "BIO_new_NDEF"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_BN_to_ASN1_ENUMERATED, 0), "BN_to_ASN1_ENUMERATED"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_BN_to_ASN1_INTEGER, 0), "BN_to_ASN1_INTEGER"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_a2d_ASN1_OBJECT, 0), "a2d_ASN1_OBJECT"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_a2i_ASN1_ENUMERATED, 0), "a2i_ASN1_ENUMERATED"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_a2i_ASN1_INTEGER, 0), "a2i_ASN1_INTEGER"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_a2i_ASN1_STRING, 0), "a2i_ASN1_STRING"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_check_tlen, 0), "asn1_check_tlen"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_collate_primitive, 0), "asn1_collate_primitive"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_collect, 0), "asn1_collect"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_d2i_ex_primitive, 0), "asn1_d2i_ex_primitive"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_d2i_read_bio, 0), "asn1_d2i_read_bio"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_do_adb, 0), "asn1_do_adb"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_ex_c2i, 0), "asn1_ex_c2i"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_find_end, 0), "asn1_find_end"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_item_ex_combine_new, 0), "asn1_item_ex_combine_new"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_template_ex_d2i, 0), "asn1_template_ex_d2i"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_asn1_template_noexp_d2i, 0), "asn1_template_noexp_d2i"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_c2i_ASN1_BIT_STRING, 0), "c2i_ASN1_BIT_STRING"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_c2i_ASN1_INTEGER, 0), "c2i_ASN1_INTEGER"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_c2i_ASN1_OBJECT, 0), "c2i_ASN1_OBJECT"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_collect_data, 0), "collect_data"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_d2i_ASN1_BOOLEAN, 0), "d2i_ASN1_BOOLEAN"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_d2i_ASN1_OBJECT, 0), "d2i_ASN1_OBJECT"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_d2i_ASN1_UINTEGER, 0), "d2i_ASN1_UINTEGER"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_d2i_ASN1_UTCTIME, 0), "d2i_ASN1_UTCTIME"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_d2i_ASN1_bytes, 0), "d2i_ASN1_bytes"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_d2i_ASN1_type_bytes, 0), "d2i_ASN1_type_bytes"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_i2d_ASN1_TIME, 0), "i2d_ASN1_TIME"},
+  {ERR_PACK(ERR_LIB_ASN1, ASN1_F_long_c2i, 0), "long_c2i"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ADDING_OBJECT), "ADDING_OBJECT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ASN1_LENGTH_MISMATCH), "ASN1_LENGTH_MISMATCH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ASN1_PARSE_ERROR), "ASN1_PARSE_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ASN1_SIG_PARSE_ERROR), "ASN1_SIG_PARSE_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_AUX_ERROR), "AUX_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BAD_CLASS), "BAD_CLASS"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BAD_GET_ASN1_OBJECT_CALL), "BAD_GET_ASN1_OBJECT_CALL"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BAD_OBJECT_HEADER), "BAD_OBJECT_HEADER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BAD_PASSWORD_READ), "BAD_PASSWORD_READ"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BAD_TAG), "BAD_TAG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BMPSTRING_IS_WRONG_LENGTH), "BMPSTRING_IS_WRONG_LENGTH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BN_LIB), "BN_LIB"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BOOLEAN_IS_WRONG_LENGTH), "BOOLEAN_IS_WRONG_LENGTH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_BUFFER_TOO_SMALL), "BUFFER_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER), "CIPHER_HAS_NO_OBJECT_IDENTIFIER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_CONTEXT_NOT_INITIALISED), "CONTEXT_NOT_INITIALISED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_DATA_IS_WRONG), "DATA_IS_WRONG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_DECODE_ERROR), "DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_DECODING_ERROR), "DECODING_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_DEPTH_EXCEEDED), "DEPTH_EXCEEDED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED), "DIGEST_AND_KEY_TYPE_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ENCODE_ERROR), "ENCODE_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ERROR_GETTING_TIME), "ERROR_GETTING_TIME"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ERROR_LOADING_SECTION), "ERROR_LOADING_SECTION"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ERROR_PARSING_SET_ELEMENT), "ERROR_PARSING_SET_ELEMENT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ERROR_SETTING_CIPHER_PARAMS), "ERROR_SETTING_CIPHER_PARAMS"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPECTING_AN_ASN1_SEQUENCE), "EXPECTING_AN_ASN1_SEQUENCE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPECTING_AN_INTEGER), "EXPECTING_AN_INTEGER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPECTING_AN_OBJECT), "EXPECTING_AN_OBJECT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPECTING_A_BOOLEAN), "EXPECTING_A_BOOLEAN"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPECTING_A_TIME), "EXPECTING_A_TIME"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPLICIT_LENGTH_MISMATCH), "EXPLICIT_LENGTH_MISMATCH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED), "EXPLICIT_TAG_NOT_CONSTRUCTED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_FIELD_MISSING), "FIELD_MISSING"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_FIRST_NUM_TOO_LARGE), "FIRST_NUM_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_HEADER_TOO_LONG), "HEADER_TOO_LONG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_BITSTRING_FORMAT), "ILLEGAL_BITSTRING_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_BOOLEAN), "ILLEGAL_BOOLEAN"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_CHARACTERS), "ILLEGAL_CHARACTERS"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_FORMAT), "ILLEGAL_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_HEX), "ILLEGAL_HEX"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_IMPLICIT_TAG), "ILLEGAL_IMPLICIT_TAG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_INTEGER), "ILLEGAL_INTEGER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_NESTED_TAGGING), "ILLEGAL_NESTED_TAGGING"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_NULL), "ILLEGAL_NULL"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_NULL_VALUE), "ILLEGAL_NULL_VALUE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_OBJECT), "ILLEGAL_OBJECT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_OPTIONAL_ANY), "ILLEGAL_OPTIONAL_ANY"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE), "ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_TAGGED_ANY), "ILLEGAL_TAGGED_ANY"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ILLEGAL_TIME_VALUE), "ILLEGAL_TIME_VALUE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INTEGER_NOT_ASCII_FORMAT), "INTEGER_NOT_ASCII_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG), "INTEGER_TOO_LARGE_FOR_LONG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_BMPSTRING_LENGTH), "INVALID_BMPSTRING_LENGTH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_DIGIT), "INVALID_DIGIT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_MIME_TYPE), "INVALID_MIME_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_MODIFIER), "INVALID_MODIFIER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_NUMBER), "INVALID_NUMBER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_OBJECT_ENCODING), "INVALID_OBJECT_ENCODING"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_SEPARATOR), "INVALID_SEPARATOR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_TIME_FORMAT), "INVALID_TIME_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH), "INVALID_UNIVERSALSTRING_LENGTH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_INVALID_UTF8STRING), "INVALID_UTF8STRING"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_IV_TOO_LARGE), "IV_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_LENGTH_ERROR), "LENGTH_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_LIST_ERROR), "LIST_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MALLOC_FAILURE), "MALLOC_FAILURE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MIME_NO_CONTENT_TYPE), "MIME_NO_CONTENT_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MIME_PARSE_ERROR), "MIME_PARSE_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MIME_SIG_PARSE_ERROR), "MIME_SIG_PARSE_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MISSING_ASN1_EOS), "MISSING_ASN1_EOS"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MISSING_EOC), "MISSING_EOC"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MISSING_SECOND_NUMBER), "MISSING_SECOND_NUMBER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MISSING_VALUE), "MISSING_VALUE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MSTRING_NOT_UNIVERSAL), "MSTRING_NOT_UNIVERSAL"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MSTRING_WRONG_TAG), "MSTRING_WRONG_TAG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NESTED_ASN1_ERROR), "NESTED_ASN1_ERROR"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NESTED_ASN1_STRING), "NESTED_ASN1_STRING"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NON_HEX_CHARACTERS), "NON_HEX_CHARACTERS"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NOT_ASCII_FORMAT), "NOT_ASCII_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NOT_ENOUGH_DATA), "NOT_ENOUGH_DATA"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NO_CONTENT_TYPE), "NO_CONTENT_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NO_DEFAULT_DIGEST), "NO_DEFAULT_DIGEST"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NO_MATCHING_CHOICE_TYPE), "NO_MATCHING_CHOICE_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NO_MULTIPART_BODY_FAILURE), "NO_MULTIPART_BODY_FAILURE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NO_MULTIPART_BOUNDARY), "NO_MULTIPART_BOUNDARY"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NO_SIG_CONTENT_TYPE), "NO_SIG_CONTENT_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NULL_IS_WRONG_LENGTH), "NULL_IS_WRONG_LENGTH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_OBJECT_NOT_ASCII_FORMAT), "OBJECT_NOT_ASCII_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_ODD_NUMBER_OF_CHARS), "ODD_NUMBER_OF_CHARS"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_PRIVATE_KEY_HEADER_MISSING), "PRIVATE_KEY_HEADER_MISSING"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_SECOND_NUMBER_TOO_LARGE), "SECOND_NUMBER_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_SEQUENCE_LENGTH_MISMATCH), "SEQUENCE_LENGTH_MISMATCH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_SEQUENCE_NOT_CONSTRUCTED), "SEQUENCE_NOT_CONSTRUCTED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG), "SEQUENCE_OR_SET_NEEDS_CONFIG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_SHORT_LINE), "SHORT_LINE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_SIG_INVALID_MIME_TYPE), "SIG_INVALID_MIME_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_STREAMING_NOT_SUPPORTED), "STREAMING_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_STRING_TOO_LONG), "STRING_TOO_LONG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_STRING_TOO_SHORT), "STRING_TOO_SHORT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_TAG_VALUE_TOO_HIGH), "TAG_VALUE_TOO_HIGH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD), "THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_TIME_NOT_ASCII_FORMAT), "TIME_NOT_ASCII_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_TOO_LONG), "TOO_LONG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_TYPE_NOT_CONSTRUCTED), "TYPE_NOT_CONSTRUCTED"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNABLE_TO_DECODE_RSA_KEY), "UNABLE_TO_DECODE_RSA_KEY"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY), "UNABLE_TO_DECODE_RSA_PRIVATE_KEY"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNEXPECTED_EOC), "UNEXPECTED_EOC"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH), "UNIVERSALSTRING_IS_WRONG_LENGTH"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKNOWN_FORMAT), "UNKNOWN_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM), "UNKNOWN_MESSAGE_DIGEST_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKNOWN_OBJECT_TYPE), "UNKNOWN_OBJECT_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE), "UNKNOWN_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM), "UNKNOWN_SIGNATURE_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKNOWN_TAG), "UNKNOWN_TAG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNKOWN_FORMAT), "UNKOWN_FORMAT"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE), "UNSUPPORTED_ANY_DEFINED_BY_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNSUPPORTED_CIPHER), "UNSUPPORTED_CIPHER"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNSUPPORTED_ENCRYPTION_ALGORITHM), "UNSUPPORTED_ENCRYPTION_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE), "UNSUPPORTED_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_UNSUPPORTED_TYPE), "UNSUPPORTED_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_WRONG_PUBLIC_KEY_TYPE), "WRONG_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_WRONG_TAG), "WRONG_TAG"},
+  {ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_WRONG_TYPE), "WRONG_TYPE"},
+  {0, NULL},
+};
diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
new file mode 100644
index 0000000..ca7209e
--- /dev/null
+++ b/crypto/asn1/asn1_lib.c
@@ -0,0 +1,483 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <limits.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+OPENSSL_DECLARE_ERROR_REASON(ASN1, MALLOC_FAILURE);
+
+
+static int asn1_get_length(const unsigned char **pp,int *inf,long *rl,int max);
+static void asn1_put_length(unsigned char **pp, int length);
+
+static int _asn1_check_infinite_end(const unsigned char **p, long len)
+	{
+	/* If there is 0 or 1 byte left, the length check should pick
+	 * things up */
+	if (len <= 0)
+		return(1);
+	else if ((len >= 2) && ((*p)[0] == 0) && ((*p)[1] == 0))
+		{
+		(*p)+=2;
+		return(1);
+		}
+	return(0);
+	}
+
+int ASN1_check_infinite_end(unsigned char **p, long len)
+	{
+	return _asn1_check_infinite_end((const unsigned char **)p, len);
+	}
+
+int ASN1_const_check_infinite_end(const unsigned char **p, long len)
+	{
+	return _asn1_check_infinite_end(p, len);
+	}
+
+
+int ASN1_get_object(const unsigned char **pp, long *plength, int *ptag,
+	int *pclass, long omax)
+	{
+	int i,ret;
+	long l;
+	const unsigned char *p= *pp;
+	int tag,xclass,inf;
+	long max=omax;
+
+	if (!max) goto err;
+	ret=(*p&V_ASN1_CONSTRUCTED);
+	xclass=(*p&V_ASN1_PRIVATE);
+	i= *p&V_ASN1_PRIMITIVE_TAG;
+	if (i == V_ASN1_PRIMITIVE_TAG)
+		{		/* high-tag */
+		p++;
+		if (--max == 0) goto err;
+		l=0;
+		while (*p&0x80)
+			{
+			l<<=7L;
+			l|= *(p++)&0x7f;
+			if (--max == 0) goto err;
+			if (l > (INT_MAX >> 7L)) goto err;
+			}
+		l<<=7L;
+		l|= *(p++)&0x7f;
+		tag=(int)l;
+		if (--max == 0) goto err;
+		}
+	else
+		{ 
+		tag=i;
+		p++;
+		if (--max == 0) goto err;
+		}
+	*ptag=tag;
+	*pclass=xclass;
+	if (!asn1_get_length(&p,&inf,plength,(int)max)) goto err;
+
+#if 0
+	fprintf(stderr,"p=%d + *plength=%ld > omax=%ld + *pp=%d  (%d > %d)\n", 
+		(int)p,*plength,omax,(int)*pp,(int)(p+ *plength),
+		(int)(omax+ *pp));
+
+#endif
+	if (*plength > (omax - (p - *pp)))
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_get_object, ASN1_R_TOO_LONG);
+		/* Set this so that even if things are not long enough
+		 * the values are set correctly */
+		ret|=0x80;
+		}
+	*pp=p;
+	return(ret|inf);
+err:
+	OPENSSL_PUT_ERROR(ASN1, ASN1_get_object, ASN1_R_HEADER_TOO_LONG);
+	return(0x80);
+	}
+
+static int asn1_get_length(const unsigned char **pp, int *inf, long *rl, int max)
+	{
+	const unsigned char *p= *pp;
+	unsigned long ret=0;
+	unsigned int i;
+
+	if (max-- < 1) return(0);
+	if (*p == 0x80)
+		{
+		*inf=1;
+		ret=0;
+		p++;
+		}
+	else
+		{
+		*inf=0;
+		i= *p&0x7f;
+		if (*(p++) & 0x80)
+			{
+			if (i > sizeof(long))
+				return 0;
+			if (max-- == 0) return(0);
+			while (i-- > 0)
+				{
+				ret<<=8L;
+				ret|= *(p++);
+				if (max-- == 0) return(0);
+				}
+			}
+		else
+			ret=i;
+		}
+	if (ret > LONG_MAX)
+		return 0;
+	*pp=p;
+	*rl=(long)ret;
+	return(1);
+	}
+
+/* class 0 is constructed
+ * constructed == 2 for indefinite length constructed */
+void ASN1_put_object(unsigned char **pp, int constructed, int length, int tag,
+	     int xclass)
+	{
+	unsigned char *p= *pp;
+	int i, ttag;
+
+	i=(constructed)?V_ASN1_CONSTRUCTED:0;
+	i|=(xclass&V_ASN1_PRIVATE);
+	if (tag < 31)
+		*(p++)=i|(tag&V_ASN1_PRIMITIVE_TAG);
+	else
+		{
+		*(p++)=i|V_ASN1_PRIMITIVE_TAG;
+		for(i = 0, ttag = tag; ttag > 0; i++) ttag >>=7;
+		ttag = i;
+		while(i-- > 0)
+			{
+			p[i] = tag & 0x7f;
+			if(i != (ttag - 1)) p[i] |= 0x80;
+			tag >>= 7;
+			}
+		p += ttag;
+		}
+	if (constructed == 2)
+		*(p++)=0x80;
+	else
+		asn1_put_length(&p,length);
+	*pp=p;
+	}
+
+int ASN1_put_eoc(unsigned char **pp)
+	{
+	unsigned char *p = *pp;
+	*p++ = 0;
+	*p++ = 0;
+	*pp = p;
+	return 2;
+	}
+
+static void asn1_put_length(unsigned char **pp, int length)
+	{
+	unsigned char *p= *pp;
+	int i,l;
+	if (length <= 127)
+		*(p++)=(unsigned char)length;
+	else
+		{
+		l=length;
+		for (i=0; l > 0; i++)
+			l>>=8;
+		*(p++)=i|0x80;
+		l=i;
+		while (i-- > 0)
+			{
+			p[i]=length&0xff;
+			length>>=8;
+			}
+		p+=l;
+		}
+	*pp=p;
+	}
+
+int ASN1_object_size(int constructed, int length, int tag)
+	{
+	int ret;
+
+	ret=length;
+	ret++;
+	if (tag >= 31)
+		{
+		while (tag > 0)
+			{
+			tag>>=7;
+			ret++;
+			}
+		}
+	if (constructed == 2)
+		return ret + 3;
+	ret++;
+	if (length > 127)
+		{
+		while (length > 0)
+			{
+			length>>=8;
+			ret++;
+			}
+		}
+	return(ret);
+	}
+
+static int _asn1_Finish(ASN1_const_CTX *c)
+	{
+	if ((c->inf == (1|V_ASN1_CONSTRUCTED)) && (!c->eos))
+		{
+		if (!ASN1_const_check_infinite_end(&c->p,c->slen))
+			{
+			c->error=ASN1_R_MISSING_ASN1_EOS;
+			return(0);
+			}
+		}
+	if (	((c->slen != 0) && !(c->inf & 1)) ||
+		((c->slen < 0) && (c->inf & 1)))
+		{
+		c->error=ASN1_R_ASN1_LENGTH_MISMATCH;
+		return(0);
+		}
+	return(1);
+	}
+
+int asn1_Finish(ASN1_CTX *c)
+	{
+	return _asn1_Finish((ASN1_const_CTX *)c);
+	}
+
+int asn1_const_Finish(ASN1_const_CTX *c)
+	{
+	return _asn1_Finish(c);
+	}
+
+int asn1_GetSequence(ASN1_const_CTX *c, long *length)
+	{
+	const unsigned char *q;
+
+	q=c->p;
+	c->inf=ASN1_get_object(&(c->p),&(c->slen),&(c->tag),&(c->xclass),
+		*length);
+	if (c->inf & 0x80)
+		{
+		c->error=ASN1_R_BAD_GET_ASN1_OBJECT_CALL;
+		return(0);
+		}
+	if (c->tag != V_ASN1_SEQUENCE)
+		{
+		c->error=ASN1_R_EXPECTING_AN_ASN1_SEQUENCE;
+		return(0);
+		}
+	(*length)-=(c->p-q);
+	if (c->max && (*length < 0))
+		{
+		c->error=ASN1_R_ASN1_LENGTH_MISMATCH;
+		return(0);
+		}
+	if (c->inf == (1|V_ASN1_CONSTRUCTED))
+		c->slen= *length+ *(c->pp)-c->p;
+	c->eos=0;
+	return(1);
+	}
+
+int ASN1_STRING_copy(ASN1_STRING *dst, const ASN1_STRING *str)
+	{
+	if (str == NULL)
+		return 0;
+	dst->type = str->type;
+	if (!ASN1_STRING_set(dst,str->data,str->length))
+		return 0;
+	dst->flags = str->flags;
+	return 1;
+	}
+
+ASN1_STRING *ASN1_STRING_dup(const ASN1_STRING *str)
+	{
+	ASN1_STRING *ret;
+	if (!str)
+		 return NULL;
+	ret=ASN1_STRING_new();
+	if (!ret)
+		return NULL;
+	if (!ASN1_STRING_copy(ret,str))
+		{
+		ASN1_STRING_free(ret);
+		return NULL;
+		}
+	return ret;
+	}
+
+int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len)
+	{
+	unsigned char *c;
+	const char *data=_data;
+
+	if (len < 0)
+		{
+		if (data == NULL)
+			return(0);
+		else
+			len=strlen(data);
+		}
+	if ((str->length < len) || (str->data == NULL))
+		{
+		c=str->data;
+		if (c == NULL)
+			str->data=OPENSSL_malloc(len+1);
+		else
+			str->data=OPENSSL_realloc(c,len+1);
+
+		if (str->data == NULL)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_STRING_set, ERR_R_MALLOC_FAILURE);
+			str->data=c;
+			return(0);
+			}
+		}
+	str->length=len;
+	if (data != NULL)
+		{
+		memcpy(str->data,data,len);
+		/* an allowance for strings :-) */
+		str->data[len]='\0';
+		}
+	return(1);
+	}
+
+void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len)
+	{
+	if (str->data)
+		OPENSSL_free(str->data);
+	str->data = data;
+	str->length = len;
+	}
+
+ASN1_STRING *ASN1_STRING_new(void)
+	{
+	return(ASN1_STRING_type_new(V_ASN1_OCTET_STRING));
+	}
+
+
+ASN1_STRING *ASN1_STRING_type_new(int type)
+	{
+	ASN1_STRING *ret;
+
+	ret=(ASN1_STRING *)OPENSSL_malloc(sizeof(ASN1_STRING));
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_STRING_type_new, ERR_R_MALLOC_FAILURE);
+		return(NULL);
+		}
+	ret->length=0;
+	ret->type=type;
+	ret->data=NULL;
+	ret->flags=0;
+	return(ret);
+	}
+
+void ASN1_STRING_free(ASN1_STRING *a)
+	{
+	if (a == NULL) return;
+	if (a->data && !(a->flags & ASN1_STRING_FLAG_NDEF))
+		OPENSSL_free(a->data);
+	OPENSSL_free(a);
+	}
+
+int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b)
+	{
+	int i;
+
+	i=(a->length-b->length);
+	if (i == 0)
+		{
+		i=memcmp(a->data,b->data,a->length);
+		if (i == 0)
+			return(a->type-b->type);
+		else
+			return(i);
+		}
+	else
+		return(i);
+	}
+
+void asn1_add_error(const unsigned char *address, int offset)
+	{
+	char buf1[DECIMAL_SIZE(address)+1],buf2[DECIMAL_SIZE(offset)+1];
+
+	BIO_snprintf(buf1,sizeof buf1,"%lu",(unsigned long)address);
+	BIO_snprintf(buf2,sizeof buf2,"%d",offset);
+	ERR_add_error_data(4,"address=",buf1," offset=",buf2);
+	}
+
+int ASN1_STRING_length(const ASN1_STRING *x)
+{ return M_ASN1_STRING_length(x); }
+
+void ASN1_STRING_length_set(ASN1_STRING *x, int len)
+{ M_ASN1_STRING_length_set(x, len); return; }
+
+int ASN1_STRING_type(ASN1_STRING *x)
+{ return M_ASN1_STRING_type(x); }
+
+unsigned char * ASN1_STRING_data(ASN1_STRING *x)
+{ return M_ASN1_STRING_data(x); }
diff --git a/crypto/asn1/asn1_locl.h b/crypto/asn1/asn1_locl.h
new file mode 100644
index 0000000..1444390
--- /dev/null
+++ b/crypto/asn1/asn1_locl.h
@@ -0,0 +1,95 @@
+/* asn1t.h */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/* Internal ASN1 structures and functions: not for application use */
+
+int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d);
+int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d);
+
+/* ASN1 print context structure */
+
+struct asn1_pctx_st
+	{
+	unsigned long flags;
+	unsigned long nm_flags;
+	unsigned long cert_flags;
+	unsigned long oid_flags;
+	unsigned long str_flags;
+	} /* ASN1_PCTX */;
+
+/* ASN1 public key method structure */
+
+
+/* Method to handle CRL access.
+ * In general a CRL could be very large (several Mb) and can consume large
+ * amounts of resources if stored in memory by multiple processes.
+ * This method allows general CRL operations to be redirected to more
+ * efficient callbacks: for example a CRL entry database.
+ */
+
+#define X509_CRL_METHOD_DYNAMIC		1
+
+struct x509_crl_method_st
+	{
+	int flags;
+	int (*crl_init)(X509_CRL *crl);
+	int (*crl_free)(X509_CRL *crl);
+	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
+				ASN1_INTEGER *ser, X509_NAME *issuer);
+	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk);
+	};
diff --git a/crypto/asn1/asn1_mac.h b/crypto/asn1/asn1_mac.h
new file mode 100644
index 0000000..a69bea4
--- /dev/null
+++ b/crypto/asn1/asn1_mac.h
@@ -0,0 +1,578 @@
+/* crypto/asn1/asn1_mac.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_ASN1_MAC_H
+#define HEADER_ASN1_MAC_H
+
+#include <openssl/asn1.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifndef ASN1_MAC_ERR_LIB
+#define ASN1_MAC_ERR_LIB	ERR_LIB_ASN1
+#endif
+
+#define ASN1_MAC_H_err(f,r,line) \
+	ERR_put_error(ASN1_MAC_ERR_LIB,(f),(r),__FILE__,(line))
+
+#define M_ASN1_D2I_vars(a,type,func) \
+	ASN1_const_CTX c; \
+	type ret=NULL; \
+	\
+	c.pp=(const unsigned char **)pp; \
+	c.q= *(const unsigned char **)pp; \
+	c.error=ASN1_R_NESTED_ASN1_ERROR; \
+	if ((a == NULL) || ((*a) == NULL)) \
+		{ if ((ret=(type)func()) == NULL) \
+			{ c.line=__LINE__; goto err; } } \
+	else	ret=(*a);
+
+#define M_ASN1_D2I_Init() \
+	c.p= *(const unsigned char **)pp; \
+	c.max=(length == 0)?0:(c.p+length);
+
+#define M_ASN1_D2I_Finish_2(a) \
+	if (!asn1_const_Finish(&c)) \
+		{ c.line=__LINE__; goto err; } \
+	*(const unsigned char **)pp=c.p; \
+	if (a != NULL) (*a)=ret; \
+	return(ret);
+
+#define M_ASN1_D2I_Finish(a,func,e) \
+	M_ASN1_D2I_Finish_2(a); \
+err:\
+	ASN1_MAC_H_err((e),c.error,c.line); \
+	asn1_add_error(*(const unsigned char **)pp,(int)(c.q- *pp)); \
+	if ((ret != NULL) && ((a == NULL) || (*a != ret))) func(ret); \
+	return(NULL)
+
+#define M_ASN1_D2I_start_sequence() \
+	if (!asn1_GetSequence(&c,&length)) \
+		{ c.line=__LINE__; goto err; }
+/* Begin reading ASN1 without a surrounding sequence */
+#define M_ASN1_D2I_begin() \
+	c.slen = length;
+
+/* End reading ASN1 with no check on length */
+#define M_ASN1_D2I_Finish_nolen(a, func, e) \
+	*pp=c.p; \
+	if (a != NULL) (*a)=ret; \
+	return(ret); \
+err:\
+	ASN1_MAC_H_err((e),c.error,c.line); \
+	asn1_add_error(*pp,(int)(c.q- *pp)); \
+	if ((ret != NULL) && ((a == NULL) || (*a != ret))) func(ret); \
+	return(NULL)
+
+#define M_ASN1_D2I_end_sequence() \
+	(((c.inf&1) == 0)?(c.slen <= 0): \
+		(c.eos=ASN1_const_check_infinite_end(&c.p,c.slen)))
+
+/* Don't use this with d2i_ASN1_BOOLEAN() */
+#define M_ASN1_D2I_get(b, func) \
+	c.q=c.p; \
+	if (func(&(b),&c.p,c.slen) == NULL) \
+		{c.line=__LINE__; goto err; } \
+	c.slen-=(c.p-c.q);
+
+/* Don't use this with d2i_ASN1_BOOLEAN() */
+#define M_ASN1_D2I_get_x(type,b,func) \
+	c.q=c.p; \
+	if (((D2I_OF(type))func)(&(b),&c.p,c.slen) == NULL) \
+		{c.line=__LINE__; goto err; } \
+	c.slen-=(c.p-c.q);
+
+/* use this instead () */
+#define M_ASN1_D2I_get_int(b,func) \
+	c.q=c.p; \
+	if (func(&(b),&c.p,c.slen) < 0) \
+		{c.line=__LINE__; goto err; } \
+	c.slen-=(c.p-c.q);
+
+#define M_ASN1_D2I_get_opt(b,func,type) \
+	if ((c.slen != 0) && ((M_ASN1_next & (~V_ASN1_CONSTRUCTED)) \
+		== (V_ASN1_UNIVERSAL|(type)))) \
+		{ \
+		M_ASN1_D2I_get(b,func); \
+		}
+
+#define M_ASN1_D2I_get_int_opt(b,func,type) \
+	if ((c.slen != 0) && ((M_ASN1_next & (~V_ASN1_CONSTRUCTED)) \
+		== (V_ASN1_UNIVERSAL|(type)))) \
+		{ \
+		M_ASN1_D2I_get_int(b,func); \
+		}
+
+#define M_ASN1_D2I_get_imp(b,func, type) \
+	M_ASN1_next=(_tmp& V_ASN1_CONSTRUCTED)|type; \
+	c.q=c.p; \
+	if (func(&(b),&c.p,c.slen) == NULL) \
+		{c.line=__LINE__; M_ASN1_next_prev = _tmp; goto err; } \
+	c.slen-=(c.p-c.q);\
+	M_ASN1_next_prev=_tmp;
+
+#define M_ASN1_D2I_get_IMP_opt(b,func,tag,type) \
+	if ((c.slen != 0) && ((M_ASN1_next & (~V_ASN1_CONSTRUCTED)) == \
+		(V_ASN1_CONTEXT_SPECIFIC|(tag)))) \
+		{ \
+		unsigned char _tmp = M_ASN1_next; \
+		M_ASN1_D2I_get_imp(b,func, type);\
+		}
+
+#define M_ASN1_D2I_get_set(r,func,free_func) \
+		M_ASN1_D2I_get_imp_set(r,func,free_func, \
+			V_ASN1_SET,V_ASN1_UNIVERSAL);
+
+#define M_ASN1_D2I_get_set_type(type,r,func,free_func) \
+		M_ASN1_D2I_get_imp_set_type(type,r,func,free_func, \
+			V_ASN1_SET,V_ASN1_UNIVERSAL);
+
+#define M_ASN1_D2I_get_set_opt(r,func,free_func) \
+	if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \
+		V_ASN1_CONSTRUCTED|V_ASN1_SET)))\
+		{ M_ASN1_D2I_get_set(r,func,free_func); }
+
+#define M_ASN1_D2I_get_set_opt_type(type,r,func,free_func) \
+	if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \
+		V_ASN1_CONSTRUCTED|V_ASN1_SET)))\
+		{ M_ASN1_D2I_get_set_type(type,r,func,free_func); }
+
+#define M_ASN1_I2D_len_SET_opt(a,f) \
+	if ((a != NULL) && (sk_num(a) != 0)) \
+		M_ASN1_I2D_len_SET(a,f);
+
+#define M_ASN1_I2D_put_SET_opt(a,f) \
+	if ((a != NULL) && (sk_num(a) != 0)) \
+		M_ASN1_I2D_put_SET(a,f);
+
+#define M_ASN1_I2D_put_SEQUENCE_opt(a,f) \
+	if ((a != NULL) && (sk_num(a) != 0)) \
+		M_ASN1_I2D_put_SEQUENCE(a,f);
+
+#define M_ASN1_I2D_put_SEQUENCE_opt_type(type,a,f) \
+	if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+		M_ASN1_I2D_put_SEQUENCE_type(type,a,f);
+
+#define M_ASN1_D2I_get_IMP_set_opt(b,func,free_func,tag) \
+	if ((c.slen != 0) && \
+		(M_ASN1_next == \
+		(V_ASN1_CONTEXT_SPECIFIC|V_ASN1_CONSTRUCTED|(tag))))\
+		{ \
+		M_ASN1_D2I_get_imp_set(b,func,free_func,\
+			tag,V_ASN1_CONTEXT_SPECIFIC); \
+		}
+
+#define M_ASN1_D2I_get_IMP_set_opt_type(type,b,func,free_func,tag) \
+	if ((c.slen != 0) && \
+		(M_ASN1_next == \
+		(V_ASN1_CONTEXT_SPECIFIC|V_ASN1_CONSTRUCTED|(tag))))\
+		{ \
+		M_ASN1_D2I_get_imp_set_type(type,b,func,free_func,\
+			tag,V_ASN1_CONTEXT_SPECIFIC); \
+		}
+
+#define M_ASN1_D2I_get_seq(r,func,free_func) \
+		M_ASN1_D2I_get_imp_set(r,func,free_func,\
+			V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL);
+
+#define M_ASN1_D2I_get_seq_type(type,r,func,free_func) \
+		M_ASN1_D2I_get_imp_set_type(type,r,func,free_func,\
+					    V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL)
+
+#define M_ASN1_D2I_get_seq_opt(r,func,free_func) \
+	if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \
+		V_ASN1_CONSTRUCTED|V_ASN1_SEQUENCE)))\
+		{ M_ASN1_D2I_get_seq(r,func,free_func); }
+
+#define M_ASN1_D2I_get_seq_opt_type(type,r,func,free_func) \
+	if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \
+		V_ASN1_CONSTRUCTED|V_ASN1_SEQUENCE)))\
+		{ M_ASN1_D2I_get_seq_type(type,r,func,free_func); }
+
+#define M_ASN1_D2I_get_IMP_set(r,func,free_func,x) \
+		M_ASN1_D2I_get_imp_set(r,func,free_func,\
+			x,V_ASN1_CONTEXT_SPECIFIC);
+
+#define M_ASN1_D2I_get_IMP_set_type(type,r,func,free_func,x) \
+		M_ASN1_D2I_get_imp_set_type(type,r,func,free_func,\
+			x,V_ASN1_CONTEXT_SPECIFIC);
+
+#define M_ASN1_D2I_get_imp_set(r,func,free_func,a,b) \
+	c.q=c.p; \
+	if (d2i_ASN1_SET(&(r),&c.p,c.slen,(char *(*)())func,\
+		(void (*)())free_func,a,b) == NULL) \
+		{ c.line=__LINE__; goto err; } \
+	c.slen-=(c.p-c.q);
+
+#define M_ASN1_D2I_get_imp_set_type(type,r,func,free_func,a,b) \
+	c.q=c.p; \
+	if (d2i_ASN1_SET_OF_##type(&(r),&c.p,c.slen,func,\
+				   free_func,a,b) == NULL) \
+		{ c.line=__LINE__; goto err; } \
+	c.slen-=(c.p-c.q);
+
+#define M_ASN1_D2I_get_set_strings(r,func,a,b) \
+	c.q=c.p; \
+	if (d2i_ASN1_STRING_SET(&(r),&c.p,c.slen,a,b) == NULL) \
+		{ c.line=__LINE__; goto err; } \
+	c.slen-=(c.p-c.q);
+
+#define M_ASN1_D2I_get_EXP_opt(r,func,tag) \
+	if ((c.slen != 0L) && (M_ASN1_next == \
+		(V_ASN1_CONSTRUCTED|V_ASN1_CONTEXT_SPECIFIC|tag))) \
+		{ \
+		int Tinf,Ttag,Tclass; \
+		long Tlen; \
+		\
+		c.q=c.p; \
+		Tinf=ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen); \
+		if (Tinf & 0x80) \
+			{ c.error=ASN1_R_BAD_OBJECT_HEADER; \
+			c.line=__LINE__; goto err; } \
+		if (Tinf == (V_ASN1_CONSTRUCTED+1)) \
+					Tlen = c.slen - (c.p - c.q) - 2; \
+		if (func(&(r),&c.p,Tlen) == NULL) \
+			{ c.line=__LINE__; goto err; } \
+		if (Tinf == (V_ASN1_CONSTRUCTED+1)) { \
+			Tlen = c.slen - (c.p - c.q); \
+			if(!ASN1_const_check_infinite_end(&c.p, Tlen)) \
+				{ c.error=ASN1_R_MISSING_ASN1_EOS; \
+				c.line=__LINE__; goto err; } \
+		}\
+		c.slen-=(c.p-c.q); \
+		}
+
+#define M_ASN1_D2I_get_EXP_set_opt(r,func,free_func,tag,b) \
+	if ((c.slen != 0) && (M_ASN1_next == \
+		(V_ASN1_CONSTRUCTED|V_ASN1_CONTEXT_SPECIFIC|tag))) \
+		{ \
+		int Tinf,Ttag,Tclass; \
+		long Tlen; \
+		\
+		c.q=c.p; \
+		Tinf=ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen); \
+		if (Tinf & 0x80) \
+			{ c.error=ASN1_R_BAD_OBJECT_HEADER; \
+			c.line=__LINE__; goto err; } \
+		if (Tinf == (V_ASN1_CONSTRUCTED+1)) \
+					Tlen = c.slen - (c.p - c.q) - 2; \
+		if (d2i_ASN1_SET(&(r),&c.p,Tlen,(char *(*)())func, \
+			(void (*)())free_func, \
+			b,V_ASN1_UNIVERSAL) == NULL) \
+			{ c.line=__LINE__; goto err; } \
+		if (Tinf == (V_ASN1_CONSTRUCTED+1)) { \
+			Tlen = c.slen - (c.p - c.q); \
+			if(!ASN1_check_infinite_end(&c.p, Tlen)) \
+				{ c.error=ASN1_R_MISSING_ASN1_EOS; \
+				c.line=__LINE__; goto err; } \
+		}\
+		c.slen-=(c.p-c.q); \
+		}
+
+#define M_ASN1_D2I_get_EXP_set_opt_type(type,r,func,free_func,tag,b) \
+	if ((c.slen != 0) && (M_ASN1_next == \
+		(V_ASN1_CONSTRUCTED|V_ASN1_CONTEXT_SPECIFIC|tag))) \
+		{ \
+		int Tinf,Ttag,Tclass; \
+		long Tlen; \
+		\
+		c.q=c.p; \
+		Tinf=ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen); \
+		if (Tinf & 0x80) \
+			{ c.error=ASN1_R_BAD_OBJECT_HEADER; \
+			c.line=__LINE__; goto err; } \
+		if (Tinf == (V_ASN1_CONSTRUCTED+1)) \
+					Tlen = c.slen - (c.p - c.q) - 2; \
+		if (d2i_ASN1_SET_OF_##type(&(r),&c.p,Tlen,func, \
+			free_func,b,V_ASN1_UNIVERSAL) == NULL) \
+			{ c.line=__LINE__; goto err; } \
+		if (Tinf == (V_ASN1_CONSTRUCTED+1)) { \
+			Tlen = c.slen - (c.p - c.q); \
+			if(!ASN1_check_infinite_end(&c.p, Tlen)) \
+				{ c.error=ASN1_R_MISSING_ASN1_EOS; \
+				c.line=__LINE__; goto err; } \
+		}\
+		c.slen-=(c.p-c.q); \
+		}
+
+/* New macros */
+#define M_ASN1_New_Malloc(ret,type) \
+	if ((ret=(type *)OPENSSL_malloc(sizeof(type))) == NULL) \
+		{ c.line=__LINE__; goto err2; }
+
+#define M_ASN1_New(arg,func) \
+	if (((arg)=func()) == NULL) return(NULL)
+
+#define M_ASN1_New_Error(a) \
+/*	err:	ASN1_MAC_H_err((a),ASN1_R_NESTED_ASN1_ERROR,c.line); \
+		return(NULL);*/ \
+	err2:	ASN1_MAC_H_err((a),ASN1_R_MALLOC_FAILURE,c.line); \
+		return(NULL)
+
+
+/* BIG UGLY WARNING!  This is so damn ugly I wanna puke.  Unfortunately,
+   some macros that use ASN1_const_CTX still insist on writing in the input
+   stream.  ARGH!  ARGH!  ARGH!  Let's get rid of this macro package.
+   Please?						-- Richard Levitte */
+#define M_ASN1_next		(*((unsigned char *)(c.p)))
+#define M_ASN1_next_prev	(*((unsigned char *)(c.q)))
+
+/*************************************************/
+
+#define M_ASN1_I2D_vars(a)	int r=0,ret=0; \
+				unsigned char *p; \
+				if (a == NULL) return(0)
+
+/* Length Macros */
+#define M_ASN1_I2D_len(a,f)	ret+=f(a,NULL)
+#define M_ASN1_I2D_len_IMP_opt(a,f)	if (a != NULL) M_ASN1_I2D_len(a,f)
+
+#define M_ASN1_I2D_len_SET(a,f) \
+		ret+=i2d_ASN1_SET(a,NULL,f,V_ASN1_SET,V_ASN1_UNIVERSAL,IS_SET);
+
+#define M_ASN1_I2D_len_SET_type(type,a,f) \
+		ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,V_ASN1_SET, \
+					    V_ASN1_UNIVERSAL,IS_SET);
+
+#define M_ASN1_I2D_len_SEQUENCE(a,f) \
+		ret+=i2d_ASN1_SET(a,NULL,f,V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL, \
+				  IS_SEQUENCE);
+
+#define M_ASN1_I2D_len_SEQUENCE_type(type,a,f) \
+		ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,V_ASN1_SEQUENCE, \
+					    V_ASN1_UNIVERSAL,IS_SEQUENCE)
+
+#define M_ASN1_I2D_len_SEQUENCE_opt(a,f) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			M_ASN1_I2D_len_SEQUENCE(a,f);
+
+#define M_ASN1_I2D_len_SEQUENCE_opt_type(type,a,f) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+			M_ASN1_I2D_len_SEQUENCE_type(type,a,f);
+
+#define M_ASN1_I2D_len_IMP_SET(a,f,x) \
+		ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC,IS_SET);
+
+#define M_ASN1_I2D_len_IMP_SET_type(type,a,f,x) \
+		ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,x, \
+					    V_ASN1_CONTEXT_SPECIFIC,IS_SET);
+
+#define M_ASN1_I2D_len_IMP_SET_opt(a,f,x) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC, \
+					  IS_SET);
+
+#define M_ASN1_I2D_len_IMP_SET_opt_type(type,a,f,x) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+			ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,x, \
+					       V_ASN1_CONTEXT_SPECIFIC,IS_SET);
+
+#define M_ASN1_I2D_len_IMP_SEQUENCE(a,f,x) \
+		ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC, \
+				  IS_SEQUENCE);
+
+#define M_ASN1_I2D_len_IMP_SEQUENCE_opt(a,f,x) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC, \
+					  IS_SEQUENCE);
+
+#define M_ASN1_I2D_len_IMP_SEQUENCE_opt_type(type,a,f,x) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+			ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,x, \
+						    V_ASN1_CONTEXT_SPECIFIC, \
+						    IS_SEQUENCE);
+
+#define M_ASN1_I2D_len_EXP_opt(a,f,mtag,v) \
+		if (a != NULL)\
+			{ \
+			v=f(a,NULL); \
+			ret+=ASN1_object_size(1,v,mtag); \
+			}
+
+#define M_ASN1_I2D_len_EXP_SET_opt(a,f,mtag,tag,v) \
+		if ((a != NULL) && (sk_num(a) != 0))\
+			{ \
+			v=i2d_ASN1_SET(a,NULL,f,tag,V_ASN1_UNIVERSAL,IS_SET); \
+			ret+=ASN1_object_size(1,v,mtag); \
+			}
+
+#define M_ASN1_I2D_len_EXP_SEQUENCE_opt(a,f,mtag,tag,v) \
+		if ((a != NULL) && (sk_num(a) != 0))\
+			{ \
+			v=i2d_ASN1_SET(a,NULL,f,tag,V_ASN1_UNIVERSAL, \
+				       IS_SEQUENCE); \
+			ret+=ASN1_object_size(1,v,mtag); \
+			}
+
+#define M_ASN1_I2D_len_EXP_SEQUENCE_opt_type(type,a,f,mtag,tag,v) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0))\
+			{ \
+			v=i2d_ASN1_SET_OF_##type(a,NULL,f,tag, \
+						 V_ASN1_UNIVERSAL, \
+						 IS_SEQUENCE); \
+			ret+=ASN1_object_size(1,v,mtag); \
+			}
+
+/* Put Macros */
+#define M_ASN1_I2D_put(a,f)	f(a,&p)
+
+#define M_ASN1_I2D_put_IMP_opt(a,f,t)	\
+		if (a != NULL) \
+			{ \
+			unsigned char *q=p; \
+			f(a,&p); \
+			*q=(V_ASN1_CONTEXT_SPECIFIC|t|(*q&V_ASN1_CONSTRUCTED));\
+			}
+
+#define M_ASN1_I2D_put_SET(a,f) i2d_ASN1_SET(a,&p,f,V_ASN1_SET,\
+			V_ASN1_UNIVERSAL,IS_SET)
+#define M_ASN1_I2D_put_SET_type(type,a,f) \
+     i2d_ASN1_SET_OF_##type(a,&p,f,V_ASN1_SET,V_ASN1_UNIVERSAL,IS_SET)
+#define M_ASN1_I2D_put_IMP_SET(a,f,x) i2d_ASN1_SET(a,&p,f,x,\
+			V_ASN1_CONTEXT_SPECIFIC,IS_SET)
+#define M_ASN1_I2D_put_IMP_SET_type(type,a,f,x) \
+     i2d_ASN1_SET_OF_##type(a,&p,f,x,V_ASN1_CONTEXT_SPECIFIC,IS_SET)
+#define M_ASN1_I2D_put_IMP_SEQUENCE(a,f,x) i2d_ASN1_SET(a,&p,f,x,\
+			V_ASN1_CONTEXT_SPECIFIC,IS_SEQUENCE)
+
+#define M_ASN1_I2D_put_SEQUENCE(a,f) i2d_ASN1_SET(a,&p,f,V_ASN1_SEQUENCE,\
+					     V_ASN1_UNIVERSAL,IS_SEQUENCE)
+
+#define M_ASN1_I2D_put_SEQUENCE_type(type,a,f) \
+     i2d_ASN1_SET_OF_##type(a,&p,f,V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL, \
+			    IS_SEQUENCE)
+
+#define M_ASN1_I2D_put_SEQUENCE_opt(a,f) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			M_ASN1_I2D_put_SEQUENCE(a,f);
+
+#define M_ASN1_I2D_put_IMP_SET_opt(a,f,x) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			{ i2d_ASN1_SET(a,&p,f,x,V_ASN1_CONTEXT_SPECIFIC, \
+				       IS_SET); }
+
+#define M_ASN1_I2D_put_IMP_SET_opt_type(type,a,f,x) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+			{ i2d_ASN1_SET_OF_##type(a,&p,f,x, \
+						 V_ASN1_CONTEXT_SPECIFIC, \
+						 IS_SET); }
+
+#define M_ASN1_I2D_put_IMP_SEQUENCE_opt(a,f,x) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			{ i2d_ASN1_SET(a,&p,f,x,V_ASN1_CONTEXT_SPECIFIC, \
+				       IS_SEQUENCE); }
+
+#define M_ASN1_I2D_put_IMP_SEQUENCE_opt_type(type,a,f,x) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+			{ i2d_ASN1_SET_OF_##type(a,&p,f,x, \
+						 V_ASN1_CONTEXT_SPECIFIC, \
+						 IS_SEQUENCE); }
+
+#define M_ASN1_I2D_put_EXP_opt(a,f,tag,v) \
+		if (a != NULL) \
+			{ \
+			ASN1_put_object(&p,1,v,tag,V_ASN1_CONTEXT_SPECIFIC); \
+			f(a,&p); \
+			}
+
+#define M_ASN1_I2D_put_EXP_SET_opt(a,f,mtag,tag,v) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			{ \
+			ASN1_put_object(&p,1,v,mtag,V_ASN1_CONTEXT_SPECIFIC); \
+			i2d_ASN1_SET(a,&p,f,tag,V_ASN1_UNIVERSAL,IS_SET); \
+			}
+
+#define M_ASN1_I2D_put_EXP_SEQUENCE_opt(a,f,mtag,tag,v) \
+		if ((a != NULL) && (sk_num(a) != 0)) \
+			{ \
+			ASN1_put_object(&p,1,v,mtag,V_ASN1_CONTEXT_SPECIFIC); \
+			i2d_ASN1_SET(a,&p,f,tag,V_ASN1_UNIVERSAL,IS_SEQUENCE); \
+			}
+
+#define M_ASN1_I2D_put_EXP_SEQUENCE_opt_type(type,a,f,mtag,tag,v) \
+		if ((a != NULL) && (sk_##type##_num(a) != 0)) \
+			{ \
+			ASN1_put_object(&p,1,v,mtag,V_ASN1_CONTEXT_SPECIFIC); \
+			i2d_ASN1_SET_OF_##type(a,&p,f,tag,V_ASN1_UNIVERSAL, \
+					       IS_SEQUENCE); \
+			}
+
+#define M_ASN1_I2D_seq_total() \
+		r=ASN1_object_size(1,ret,V_ASN1_SEQUENCE); \
+		if (pp == NULL) return(r); \
+		p= *pp; \
+		ASN1_put_object(&p,1,ret,V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL)
+
+#define M_ASN1_I2D_INF_seq_start(tag,ctx) \
+		*(p++)=(V_ASN1_CONSTRUCTED|(tag)|(ctx)); \
+		*(p++)=0x80
+
+#define M_ASN1_I2D_INF_seq_end() *(p++)=0x00; *(p++)=0x00
+
+#define M_ASN1_I2D_finish()	*pp=p; \
+				return(r);
+
+int asn1_GetSequence(ASN1_const_CTX *c, long *length);
+void asn1_add_error(const unsigned char *address,int offset);
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/crypto/asn1/asn1_par.c b/crypto/asn1/asn1_par.c
new file mode 100644
index 0000000..53b11fe
--- /dev/null
+++ b/crypto/asn1/asn1_par.c
@@ -0,0 +1,435 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+static int asn1_print_info(BIO *bp, int tag, int xclass,int constructed,
+	int indent);
+static int asn1_parse2(BIO *bp, const unsigned char **pp, long length,
+	int offset, int depth, int indent, int dump);
+static int asn1_print_info(BIO *bp, int tag, int xclass, int constructed,
+	     int indent)
+	{
+	static const char fmt[]="%-18s";
+	char str[128];
+	const char *p;
+
+	if (constructed & V_ASN1_CONSTRUCTED)
+		p="cons: ";
+	else
+		p="prim: ";
+	if (BIO_write(bp,p,6) < 6) goto err;
+	BIO_indent(bp,indent,128);
+
+	p=str;
+	if ((xclass & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
+		BIO_snprintf(str,sizeof str,"priv [ %d ] ",tag);
+	else if ((xclass & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
+		BIO_snprintf(str,sizeof str,"cont [ %d ]",tag);
+	else if ((xclass & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
+		BIO_snprintf(str,sizeof str,"appl [ %d ]",tag);
+	else if (tag > 30)
+		BIO_snprintf(str,sizeof str,"<ASN1 %d>",tag);
+	else
+		p = ASN1_tag2str(tag);
+
+	if (BIO_printf(bp,fmt,p) <= 0)
+		goto err;
+	return(1);
+err:
+	return(0);
+	}
+
+int ASN1_parse(BIO *bp, const unsigned char *pp, long len, int indent)
+	{
+	return(asn1_parse2(bp,&pp,len,0,0,indent,0));
+	}
+
+int ASN1_parse_dump(BIO *bp, const unsigned char *pp, long len, int indent, int dump)
+	{
+	return(asn1_parse2(bp,&pp,len,0,0,indent,dump));
+	}
+
+static int asn1_parse2(BIO *bp, const unsigned char **pp, long length, int offset,
+	     int depth, int indent, int dump)
+	{
+	const unsigned char *p,*ep,*tot,*op,*opp;
+	long len;
+	int tag,xclass,ret=0;
+	int nl,hl,j,r;
+	ASN1_OBJECT *o=NULL;
+	ASN1_OCTET_STRING *os=NULL;
+	/* ASN1_BMPSTRING *bmp=NULL;*/
+	int dump_indent;
+
+#if 0
+	dump_indent = indent;
+#else
+	dump_indent = 6;	/* Because we know BIO_dump_indent() */
+#endif
+	p= *pp;
+	tot=p+length;
+	op=p-1;
+	while ((p < tot) && (op < p))
+		{
+		op=p;
+		j=ASN1_get_object(&p,&len,&tag,&xclass,length);
+#ifdef LINT
+		j=j;
+#endif
+		if (j & 0x80)
+			{
+			if (BIO_write(bp,"Error in encoding\n",18) <= 0)
+				goto end;
+			ret=0;
+			goto end;
+			}
+		hl=(p-op);
+		length-=hl;
+		/* if j == 0x21 it is a constructed indefinite length object */
+		if (BIO_printf(bp,"%5ld:",(long)offset+(long)(op- *pp))
+			<= 0) goto end;
+
+		if (j != (V_ASN1_CONSTRUCTED | 1))
+			{
+			if (BIO_printf(bp,"d=%-2d hl=%ld l=%4ld ",
+				depth,(long)hl,len) <= 0)
+				goto end;
+			}
+		else
+			{
+			if (BIO_printf(bp,"d=%-2d hl=%ld l=inf  ",
+				depth,(long)hl) <= 0)
+				goto end;
+			}
+		if (!asn1_print_info(bp,tag,xclass,j,(indent)?depth:0))
+			goto end;
+		if (j & V_ASN1_CONSTRUCTED)
+			{
+			ep=p+len;
+			if (BIO_write(bp,"\n",1) <= 0) goto end;
+			if (len > length)
+				{
+				BIO_printf(bp,
+					"length is greater than %ld\n",length);
+				ret=0;
+				goto end;
+				}
+			if ((j == 0x21) && (len == 0))
+				{
+				for (;;)
+					{
+					r=asn1_parse2(bp,&p,(long)(tot-p),
+						offset+(p - *pp),depth+1,
+						indent,dump);
+					if (r == 0) { ret=0; goto end; }
+					if ((r == 2) || (p >= tot)) break;
+					}
+				}
+			else
+				while (p < ep)
+					{
+					r=asn1_parse2(bp,&p,(long)len,
+						offset+(p - *pp),depth+1,
+						indent,dump);
+					if (r == 0) { ret=0; goto end; }
+					}
+			}
+		else if (xclass != 0)
+			{
+			p+=len;
+			if (BIO_write(bp,"\n",1) <= 0) goto end;
+			}
+		else
+			{
+			nl=0;
+			if (	(tag == V_ASN1_PRINTABLESTRING) ||
+				(tag == V_ASN1_T61STRING) ||
+				(tag == V_ASN1_IA5STRING) ||
+				(tag == V_ASN1_VISIBLESTRING) ||
+				(tag == V_ASN1_NUMERICSTRING) ||
+				(tag == V_ASN1_UTF8STRING) ||
+				(tag == V_ASN1_UTCTIME) ||
+				(tag == V_ASN1_GENERALIZEDTIME))
+				{
+				if (BIO_write(bp,":",1) <= 0) goto end;
+				if ((len > 0) &&
+					BIO_write(bp,(const char *)p,(int)len)
+					!= (int)len)
+					goto end;
+				}
+			else if (tag == V_ASN1_OBJECT)
+				{
+				opp=op;
+				if (d2i_ASN1_OBJECT(&o,&opp,len+hl) != NULL)
+					{
+					if (BIO_write(bp,":",1) <= 0) goto end;
+					i2a_ASN1_OBJECT(bp,o);
+					}
+				else
+					{
+					if (BIO_write(bp,":BAD OBJECT",11) <= 0)
+						goto end;
+					}
+				}
+			else if (tag == V_ASN1_BOOLEAN)
+				{
+				int ii;
+
+				opp=op;
+				ii=d2i_ASN1_BOOLEAN(NULL,&opp,len+hl);
+				if (ii < 0)
+					{
+					if (BIO_write(bp,"Bad boolean\n",12) <= 0)
+						goto end;
+					}
+				BIO_printf(bp,":%d",ii);
+				}
+			else if (tag == V_ASN1_BMPSTRING)
+				{
+				/* do the BMP thang */
+				}
+			else if (tag == V_ASN1_OCTET_STRING)
+				{
+				int i,printable=1;
+
+				opp=op;
+				os=d2i_ASN1_OCTET_STRING(NULL,&opp,len+hl);
+				if (os != NULL && os->length > 0)
+					{
+					opp = os->data;
+					/* testing whether the octet string is
+					 * printable */
+					for (i=0; i<os->length; i++)
+						{
+						if ((	(opp[i] < ' ') &&
+							(opp[i] != '\n') &&
+							(opp[i] != '\r') &&
+							(opp[i] != '\t')) ||
+							(opp[i] > '~'))
+							{
+							printable=0;
+							break;
+							}
+						}
+					if (printable)
+					/* printable string */
+						{
+						if (BIO_write(bp,":",1) <= 0)
+							goto end;
+						if (BIO_write(bp,(const char *)opp,
+							os->length) <= 0)
+							goto end;
+						}
+					else if (!dump)
+					/* not printable => print octet string
+					 * as hex dump */
+						{
+						if (BIO_write(bp,"[HEX DUMP]:",11) <= 0)
+							goto end;
+						for (i=0; i<os->length; i++)
+							{
+							if (BIO_printf(bp,"%02X"
+								, opp[i]) <= 0)
+								goto end;
+							}
+						}
+					else
+					/* print the normal dump */
+						{
+						if (!nl) 
+							{
+							if (BIO_write(bp,"\n",1) <= 0)
+								goto end;
+							}
+						if (!BIO_hexdump(bp, opp,
+							((dump == -1 || dump > 
+							os->length)?os->length:dump),
+							dump_indent))
+							goto end;
+						nl=1;
+						}
+					}
+				if (os != NULL)
+					{
+					M_ASN1_OCTET_STRING_free(os);
+					os=NULL;
+					}
+				}
+			else if (tag == V_ASN1_INTEGER)
+				{
+				ASN1_INTEGER *bs;
+				int i;
+
+				opp=op;
+				bs=d2i_ASN1_INTEGER(NULL,&opp,len+hl);
+				if (bs != NULL)
+					{
+					if (BIO_write(bp,":",1) <= 0) goto end;
+					if (bs->type == V_ASN1_NEG_INTEGER)
+						if (BIO_write(bp,"-",1) <= 0)
+							goto end;
+					for (i=0; i<bs->length; i++)
+						{
+						if (BIO_printf(bp,"%02X",
+							bs->data[i]) <= 0)
+							goto end;
+						}
+					if (bs->length == 0)
+						{
+						if (BIO_write(bp,"00",2) <= 0)
+							goto end;
+						}
+					}
+				else
+					{
+					if (BIO_write(bp,"BAD INTEGER",11) <= 0)
+						goto end;
+					}
+				M_ASN1_INTEGER_free(bs);
+				}
+			else if (tag == V_ASN1_ENUMERATED)
+				{
+				ASN1_ENUMERATED *bs;
+				int i;
+
+				opp=op;
+				bs=d2i_ASN1_ENUMERATED(NULL,&opp,len+hl);
+				if (bs != NULL)
+					{
+					if (BIO_write(bp,":",1) <= 0) goto end;
+					if (bs->type == V_ASN1_NEG_ENUMERATED)
+						if (BIO_write(bp,"-",1) <= 0)
+							goto end;
+					for (i=0; i<bs->length; i++)
+						{
+						if (BIO_printf(bp,"%02X",
+							bs->data[i]) <= 0)
+							goto end;
+						}
+					if (bs->length == 0)
+						{
+						if (BIO_write(bp,"00",2) <= 0)
+							goto end;
+						}
+					}
+				else
+					{
+					if (BIO_write(bp,"BAD ENUMERATED",11) <= 0)
+						goto end;
+					}
+				M_ASN1_ENUMERATED_free(bs);
+				}
+			else if (len > 0 && dump)
+				{
+				if (!nl) 
+					{
+					if (BIO_write(bp,"\n",1) <= 0)
+						goto end;
+					}
+				if (!BIO_hexdump(bp,p,
+					((dump == -1 || dump > len)?len:dump),
+					dump_indent))
+					goto end;
+				nl=1;
+				}
+
+			if (!nl) 
+				{
+				if (BIO_write(bp,"\n",1) <= 0) goto end;
+				}
+			p+=len;
+			if ((tag == V_ASN1_EOC) && (xclass == 0))
+				{
+				ret=2; /* End of sequence */
+				goto end;
+				}
+			}
+		length-=len;
+		}
+	ret=1;
+end:
+	if (o != NULL) ASN1_OBJECT_free(o);
+	if (os != NULL) M_ASN1_OCTET_STRING_free(os);
+	*pp=p;
+	return(ret);
+	}
+
+const char *ASN1_tag2str(int tag)
+{
+	static const char * const tag2str[] = {
+	 "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING", /* 0-4 */
+	 "NULL", "OBJECT", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL", /* 5-9 */
+	 "ENUMERATED", "<ASN1 11>", "UTF8STRING", "<ASN1 13>", 	    /* 10-13 */
+	"<ASN1 14>", "<ASN1 15>", "SEQUENCE", "SET", 		    /* 15-17 */
+	"NUMERICSTRING", "PRINTABLESTRING", "T61STRING",	    /* 18-20 */
+	"VIDEOTEXSTRING", "IA5STRING", "UTCTIME","GENERALIZEDTIME", /* 21-24 */
+	"GRAPHICSTRING", "VISIBLESTRING", "GENERALSTRING",	    /* 25-27 */
+	"UNIVERSALSTRING", "<ASN1 29>", "BMPSTRING"		    /* 28-30 */
+	};
+
+	if((tag == V_ASN1_NEG_INTEGER) || (tag == V_ASN1_NEG_ENUMERATED))
+							tag &= ~0x100;
+
+	if(tag < 0 || tag > 30) return "(unknown)";
+	return tag2str[tag];
+}
+
diff --git a/crypto/asn1/asn1t.h b/crypto/asn1/asn1t.h
new file mode 100644
index 0000000..ea59aaa
--- /dev/null
+++ b/crypto/asn1/asn1t.h
@@ -0,0 +1,959 @@
+/* asn1t.h */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2000.
+ */
+/* ====================================================================
+ * Copyright (c) 2000-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+#ifndef HEADER_ASN1T_H
+#define HEADER_ASN1T_H
+
+#include <stddef.h>
+#include <openssl/asn1.h>
+
+#ifdef OPENSSL_BUILD_SHLIBCRYPTO
+# undef OPENSSL_EXTERN
+# define OPENSSL_EXTERN OPENSSL_EXPORT
+#endif
+
+/* ASN1 template defines, structures and functions */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION
+
+/* Macro to obtain ASN1_ADB pointer from a type (only used internally) */
+#define ASN1_ADB_ptr(iptr) ((const ASN1_ADB *)(iptr))
+
+
+/* Macros for start and end of ASN1_ITEM definition */
+
+#define ASN1_ITEM_start(itname) \
+	const ASN1_ITEM itname##_it = {
+
+#define ASN1_ITEM_end(itname) \
+		};
+
+#else
+
+/* Macro to obtain ASN1_ADB pointer from a type (only used internally) */
+#define ASN1_ADB_ptr(iptr) ((const ASN1_ADB *)(iptr()))
+
+
+/* Macros for start and end of ASN1_ITEM definition */
+
+#define ASN1_ITEM_start(itname) \
+	const ASN1_ITEM * itname##_it(void) \
+	{ \
+		static const ASN1_ITEM local_it = { 
+
+#define ASN1_ITEM_end(itname) \
+		}; \
+	return &local_it; \
+	}
+
+#endif
+
+
+/* Macros to aid ASN1 template writing */
+
+#define ASN1_ITEM_TEMPLATE(tname) \
+	static const ASN1_TEMPLATE tname##_item_tt 
+
+#define ASN1_ITEM_TEMPLATE_END(tname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_PRIMITIVE,\
+		-1,\
+		&tname##_item_tt,\
+		0,\
+		NULL,\
+		0,\
+		#tname \
+	ASN1_ITEM_end(tname)
+
+
+/* This is a ASN1 type which just embeds a template */
+ 
+/* This pair helps declare a SEQUENCE. We can do:
+ *
+ * 	ASN1_SEQUENCE(stname) = {
+ * 		... SEQUENCE components ...
+ * 	} ASN1_SEQUENCE_END(stname)
+ *
+ * 	This will produce an ASN1_ITEM called stname_it
+ *	for a structure called stname.
+ *
+ * 	If you want the same structure but a different
+ *	name then use:
+ *
+ * 	ASN1_SEQUENCE(itname) = {
+ * 		... SEQUENCE components ...
+ * 	} ASN1_SEQUENCE_END_name(stname, itname)
+ *
+ *	This will create an item called itname_it using
+ *	a structure called stname.
+ */
+
+#define ASN1_SEQUENCE(tname) \
+	static const ASN1_TEMPLATE tname##_seq_tt[] 
+
+#define ASN1_SEQUENCE_END(stname) ASN1_SEQUENCE_END_name(stname, stname)
+
+#define ASN1_SEQUENCE_END_name(stname, tname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_SEQUENCE,\
+		V_ASN1_SEQUENCE,\
+		tname##_seq_tt,\
+		sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\
+		NULL,\
+		sizeof(stname),\
+		#stname \
+	ASN1_ITEM_end(tname)
+
+#define ASN1_NDEF_SEQUENCE(tname) \
+	ASN1_SEQUENCE(tname)
+
+#define ASN1_NDEF_SEQUENCE_cb(tname, cb) \
+	ASN1_SEQUENCE_cb(tname, cb)
+
+#define ASN1_SEQUENCE_cb(tname, cb) \
+	static const ASN1_AUX tname##_aux = {NULL, 0, 0, 0, cb, 0}; \
+	ASN1_SEQUENCE(tname)
+
+#define ASN1_BROKEN_SEQUENCE(tname) \
+	static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_BROKEN, 0, 0, 0, 0}; \
+	ASN1_SEQUENCE(tname)
+
+#define ASN1_SEQUENCE_ref(tname, cb, lck) \
+	static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_REFCOUNT, offsetof(tname, references), lck, cb, 0}; \
+	ASN1_SEQUENCE(tname)
+
+#define ASN1_SEQUENCE_enc(tname, enc, cb) \
+	static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_ENCODING, 0, 0, cb, offsetof(tname, enc)}; \
+	ASN1_SEQUENCE(tname)
+
+#define ASN1_NDEF_SEQUENCE_END(tname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_NDEF_SEQUENCE,\
+		V_ASN1_SEQUENCE,\
+		tname##_seq_tt,\
+		sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\
+		NULL,\
+		sizeof(tname),\
+		#tname \
+	ASN1_ITEM_end(tname)
+
+#define ASN1_BROKEN_SEQUENCE_END(stname) ASN1_SEQUENCE_END_ref(stname, stname)
+
+#define ASN1_SEQUENCE_END_enc(stname, tname) ASN1_SEQUENCE_END_ref(stname, tname)
+
+#define ASN1_SEQUENCE_END_cb(stname, tname) ASN1_SEQUENCE_END_ref(stname, tname)
+
+#define ASN1_SEQUENCE_END_ref(stname, tname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_SEQUENCE,\
+		V_ASN1_SEQUENCE,\
+		tname##_seq_tt,\
+		sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\
+		&tname##_aux,\
+		sizeof(stname),\
+		#stname \
+	ASN1_ITEM_end(tname)
+
+#define ASN1_NDEF_SEQUENCE_END_cb(stname, tname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_NDEF_SEQUENCE,\
+		V_ASN1_SEQUENCE,\
+		tname##_seq_tt,\
+		sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\
+		&tname##_aux,\
+		sizeof(stname),\
+		#stname \
+	ASN1_ITEM_end(tname)
+
+
+/* This pair helps declare a CHOICE type. We can do:
+ *
+ * 	ASN1_CHOICE(chname) = {
+ * 		... CHOICE options ...
+ * 	ASN1_CHOICE_END(chname)
+ *
+ * 	This will produce an ASN1_ITEM called chname_it
+ *	for a structure called chname. The structure
+ *	definition must look like this:
+ *	typedef struct {
+ *		int type;
+ *		union {
+ *			ASN1_SOMETHING *opt1;
+ *			ASN1_SOMEOTHER *opt2;
+ *		} value;
+ *	} chname;
+ *	
+ *	the name of the selector must be 'type'.
+ * 	to use an alternative selector name use the
+ *      ASN1_CHOICE_END_selector() version.
+ */
+
+#define ASN1_CHOICE(tname) \
+	static const ASN1_TEMPLATE tname##_ch_tt[] 
+
+#define ASN1_CHOICE_cb(tname, cb) \
+	static const ASN1_AUX tname##_aux = {NULL, 0, 0, 0, cb, 0}; \
+	ASN1_CHOICE(tname)
+
+#define ASN1_CHOICE_END(stname) ASN1_CHOICE_END_name(stname, stname)
+
+#define ASN1_CHOICE_END_name(stname, tname) ASN1_CHOICE_END_selector(stname, tname, type)
+
+#define ASN1_CHOICE_END_selector(stname, tname, selname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_CHOICE,\
+		offsetof(stname,selname) ,\
+		tname##_ch_tt,\
+		sizeof(tname##_ch_tt) / sizeof(ASN1_TEMPLATE),\
+		NULL,\
+		sizeof(stname),\
+		#stname \
+	ASN1_ITEM_end(tname)
+
+#define ASN1_CHOICE_END_cb(stname, tname, selname) \
+	;\
+	ASN1_ITEM_start(tname) \
+		ASN1_ITYPE_CHOICE,\
+		offsetof(stname,selname) ,\
+		tname##_ch_tt,\
+		sizeof(tname##_ch_tt) / sizeof(ASN1_TEMPLATE),\
+		&tname##_aux,\
+		sizeof(stname),\
+		#stname \
+	ASN1_ITEM_end(tname)
+
+/* This helps with the template wrapper form of ASN1_ITEM */
+
+#define ASN1_EX_TEMPLATE_TYPE(flags, tag, name, type) { \
+	(flags), (tag), 0,\
+	#name, ASN1_ITEM_ref(type) }
+
+/* These help with SEQUENCE or CHOICE components */
+
+/* used to declare other types */
+
+#define ASN1_EX_TYPE(flags, tag, stname, field, type) { \
+	(flags), (tag), offsetof(stname, field),\
+	#field, ASN1_ITEM_ref(type) }
+
+/* used when the structure is combined with the parent */
+
+#define ASN1_EX_COMBINE(flags, tag, type) { \
+	(flags)|ASN1_TFLG_COMBINE, (tag), 0, NULL, ASN1_ITEM_ref(type) }
+
+/* implicit and explicit helper macros */
+
+#define ASN1_IMP_EX(stname, field, type, tag, ex) \
+		ASN1_EX_TYPE(ASN1_TFLG_IMPLICIT | ex, tag, stname, field, type)
+
+#define ASN1_EXP_EX(stname, field, type, tag, ex) \
+		ASN1_EX_TYPE(ASN1_TFLG_EXPLICIT | ex, tag, stname, field, type)
+
+/* Any defined by macros: the field used is in the table itself */
+
+#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION
+#define ASN1_ADB_OBJECT(tblname) { ASN1_TFLG_ADB_OID, -1, 0, #tblname, (const ASN1_ITEM *)&(tblname##_adb) }
+#define ASN1_ADB_INTEGER(tblname) { ASN1_TFLG_ADB_INT, -1, 0, #tblname, (const ASN1_ITEM *)&(tblname##_adb) }
+#else
+#define ASN1_ADB_OBJECT(tblname) { ASN1_TFLG_ADB_OID, -1, 0, #tblname, tblname##_adb }
+#define ASN1_ADB_INTEGER(tblname) { ASN1_TFLG_ADB_INT, -1, 0, #tblname, tblname##_adb }
+#endif
+/* Plain simple type */
+#define ASN1_SIMPLE(stname, field, type) ASN1_EX_TYPE(0,0, stname, field, type)
+
+/* OPTIONAL simple type */
+#define ASN1_OPT(stname, field, type) ASN1_EX_TYPE(ASN1_TFLG_OPTIONAL, 0, stname, field, type)
+
+/* IMPLICIT tagged simple type */
+#define ASN1_IMP(stname, field, type, tag) ASN1_IMP_EX(stname, field, type, tag, 0)
+
+/* IMPLICIT tagged OPTIONAL simple type */
+#define ASN1_IMP_OPT(stname, field, type, tag) ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_OPTIONAL)
+
+/* Same as above but EXPLICIT */
+
+#define ASN1_EXP(stname, field, type, tag) ASN1_EXP_EX(stname, field, type, tag, 0)
+#define ASN1_EXP_OPT(stname, field, type, tag) ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_OPTIONAL)
+
+/* SEQUENCE OF type */
+#define ASN1_SEQUENCE_OF(stname, field, type) \
+		ASN1_EX_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, stname, field, type)
+
+/* OPTIONAL SEQUENCE OF */
+#define ASN1_SEQUENCE_OF_OPT(stname, field, type) \
+		ASN1_EX_TYPE(ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL, 0, stname, field, type)
+
+/* Same as above but for SET OF */
+
+#define ASN1_SET_OF(stname, field, type) \
+		ASN1_EX_TYPE(ASN1_TFLG_SET_OF, 0, stname, field, type)
+
+#define ASN1_SET_OF_OPT(stname, field, type) \
+		ASN1_EX_TYPE(ASN1_TFLG_SET_OF|ASN1_TFLG_OPTIONAL, 0, stname, field, type)
+
+/* Finally compound types of SEQUENCE, SET, IMPLICIT, EXPLICIT and OPTIONAL */
+
+#define ASN1_IMP_SET_OF(stname, field, type, tag) \
+			ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF)
+
+#define ASN1_EXP_SET_OF(stname, field, type, tag) \
+			ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF)
+
+#define ASN1_IMP_SET_OF_OPT(stname, field, type, tag) \
+			ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF|ASN1_TFLG_OPTIONAL)
+
+#define ASN1_EXP_SET_OF_OPT(stname, field, type, tag) \
+			ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF|ASN1_TFLG_OPTIONAL)
+
+#define ASN1_IMP_SEQUENCE_OF(stname, field, type, tag) \
+			ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF)
+
+#define ASN1_IMP_SEQUENCE_OF_OPT(stname, field, type, tag) \
+			ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL)
+
+#define ASN1_EXP_SEQUENCE_OF(stname, field, type, tag) \
+			ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF)
+
+#define ASN1_EXP_SEQUENCE_OF_OPT(stname, field, type, tag) \
+			ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL)
+
+/* EXPLICIT using indefinite length constructed form */
+#define ASN1_NDEF_EXP(stname, field, type, tag) \
+			ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_NDEF)
+
+/* EXPLICIT OPTIONAL using indefinite length constructed form */
+#define ASN1_NDEF_EXP_OPT(stname, field, type, tag) \
+			ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_OPTIONAL|ASN1_TFLG_NDEF)
+
+/* Macros for the ASN1_ADB structure */
+
+#define ASN1_ADB(name) \
+	static const ASN1_ADB_TABLE name##_adbtbl[] 
+
+#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION
+
+#define ASN1_ADB_END(name, flags, field, app_table, def, none) \
+	;\
+	static const ASN1_ADB name##_adb = {\
+		flags,\
+		offsetof(name, field),\
+		app_table,\
+		name##_adbtbl,\
+		sizeof(name##_adbtbl) / sizeof(ASN1_ADB_TABLE),\
+		def,\
+		none\
+	}
+
+#else
+
+#define ASN1_ADB_END(name, flags, field, app_table, def, none) \
+	;\
+	static const ASN1_ITEM *name##_adb(void) \
+	{ \
+	static const ASN1_ADB internal_adb = \
+		{\
+		flags,\
+		offsetof(name, field),\
+		app_table,\
+		name##_adbtbl,\
+		sizeof(name##_adbtbl) / sizeof(ASN1_ADB_TABLE),\
+		def,\
+		none\
+		}; \
+		return (const ASN1_ITEM *) &internal_adb; \
+	} \
+	void dummy_function(void)
+
+#endif
+
+#define ADB_ENTRY(val, template) {val, template}
+
+#define ASN1_ADB_TEMPLATE(name) \
+	static const ASN1_TEMPLATE name##_tt 
+
+/* This is the ASN1 template structure that defines
+ * a wrapper round the actual type. It determines the
+ * actual position of the field in the value structure,
+ * various flags such as OPTIONAL and the field name.
+ */
+
+struct ASN1_TEMPLATE_st {
+unsigned long flags;		/* Various flags */
+long tag;			/* tag, not used if no tagging */
+unsigned long offset;		/* Offset of this field in structure */
+#ifndef NO_ASN1_FIELD_NAMES
+const char *field_name;		/* Field name */
+#endif
+ASN1_ITEM_EXP *item;		/* Relevant ASN1_ITEM or ASN1_ADB */
+};
+
+/* Macro to extract ASN1_ITEM and ASN1_ADB pointer from ASN1_TEMPLATE */
+
+#define ASN1_TEMPLATE_item(t) (t->item_ptr)
+#define ASN1_TEMPLATE_adb(t) (t->item_ptr)
+
+typedef struct ASN1_ADB_TABLE_st ASN1_ADB_TABLE;
+typedef struct ASN1_ADB_st ASN1_ADB;
+
+struct ASN1_ADB_st {
+	unsigned long flags;	/* Various flags */
+	unsigned long offset;	/* Offset of selector field */
+	STACK_OF(ASN1_ADB_TABLE) **app_items; /* Application defined items */
+	const ASN1_ADB_TABLE *tbl;	/* Table of possible types */
+	long tblcount;		/* Number of entries in tbl */
+	const ASN1_TEMPLATE *default_tt;  /* Type to use if no match */
+	const ASN1_TEMPLATE *null_tt;  /* Type to use if selector is NULL */
+};
+
+struct ASN1_ADB_TABLE_st {
+	long value;		/* NID for an object or value for an int */
+	const ASN1_TEMPLATE tt;		/* item for this value */
+};
+
+/* template flags */
+
+/* Field is optional */
+#define ASN1_TFLG_OPTIONAL	(0x1)
+
+/* Field is a SET OF */
+#define ASN1_TFLG_SET_OF	(0x1 << 1)
+
+/* Field is a SEQUENCE OF */
+#define ASN1_TFLG_SEQUENCE_OF	(0x2 << 1)
+
+/* Special case: this refers to a SET OF that
+ * will be sorted into DER order when encoded *and*
+ * the corresponding STACK will be modified to match
+ * the new order.
+ */
+#define ASN1_TFLG_SET_ORDER	(0x3 << 1)
+
+/* Mask for SET OF or SEQUENCE OF */
+#define ASN1_TFLG_SK_MASK	(0x3 << 1)
+
+/* These flags mean the tag should be taken from the
+ * tag field. If EXPLICIT then the underlying type
+ * is used for the inner tag.
+ */
+
+/* IMPLICIT tagging */
+#define ASN1_TFLG_IMPTAG	(0x1 << 3)
+
+
+/* EXPLICIT tagging, inner tag from underlying type */
+#define ASN1_TFLG_EXPTAG	(0x2 << 3)
+
+#define ASN1_TFLG_TAG_MASK	(0x3 << 3)
+
+/* context specific IMPLICIT */
+#define ASN1_TFLG_IMPLICIT	ASN1_TFLG_IMPTAG|ASN1_TFLG_CONTEXT
+
+/* context specific EXPLICIT */
+#define ASN1_TFLG_EXPLICIT	ASN1_TFLG_EXPTAG|ASN1_TFLG_CONTEXT
+
+/* If tagging is in force these determine the
+ * type of tag to use. Otherwise the tag is
+ * determined by the underlying type. These 
+ * values reflect the actual octet format.
+ */
+
+/* Universal tag */ 
+#define ASN1_TFLG_UNIVERSAL	(0x0<<6)
+/* Application tag */ 
+#define ASN1_TFLG_APPLICATION	(0x1<<6)
+/* Context specific tag */ 
+#define ASN1_TFLG_CONTEXT	(0x2<<6)
+/* Private tag */ 
+#define ASN1_TFLG_PRIVATE	(0x3<<6)
+
+#define ASN1_TFLG_TAG_CLASS	(0x3<<6)
+
+/* These are for ANY DEFINED BY type. In this case
+ * the 'item' field points to an ASN1_ADB structure
+ * which contains a table of values to decode the
+ * relevant type
+ */
+
+#define ASN1_TFLG_ADB_MASK	(0x3<<8)
+
+#define ASN1_TFLG_ADB_OID	(0x1<<8)
+
+#define ASN1_TFLG_ADB_INT	(0x1<<9)
+
+/* This flag means a parent structure is passed
+ * instead of the field: this is useful is a
+ * SEQUENCE is being combined with a CHOICE for
+ * example. Since this means the structure and
+ * item name will differ we need to use the
+ * ASN1_CHOICE_END_name() macro for example.
+ */
+
+#define ASN1_TFLG_COMBINE	(0x1<<10)
+
+/* This flag when present in a SEQUENCE OF, SET OF
+ * or EXPLICIT causes indefinite length constructed
+ * encoding to be used if required.
+ */
+
+#define ASN1_TFLG_NDEF		(0x1<<11)
+
+/* This is the actual ASN1 item itself */
+
+struct ASN1_ITEM_st {
+char itype;			/* The item type, primitive, SEQUENCE, CHOICE or extern */
+long utype;			/* underlying type */
+const ASN1_TEMPLATE *templates;	/* If SEQUENCE or CHOICE this contains the contents */
+long tcount;			/* Number of templates if SEQUENCE or CHOICE */
+const void *funcs;		/* functions that handle this type */
+long size;			/* Structure size (usually)*/
+#ifndef NO_ASN1_FIELD_NAMES
+const char *sname;		/* Structure name */
+#endif
+};
+
+/* These are values for the itype field and
+ * determine how the type is interpreted.
+ *
+ * For PRIMITIVE types the underlying type
+ * determines the behaviour if items is NULL.
+ *
+ * Otherwise templates must contain a single 
+ * template and the type is treated in the
+ * same way as the type specified in the template.
+ *
+ * For SEQUENCE types the templates field points
+ * to the members, the size field is the
+ * structure size.
+ *
+ * For CHOICE types the templates field points
+ * to each possible member (typically a union)
+ * and the 'size' field is the offset of the
+ * selector.
+ *
+ * The 'funcs' field is used for application
+ * specific functions. 
+ *
+ * For COMPAT types the funcs field gives a
+ * set of functions that handle this type, this
+ * supports the old d2i, i2d convention.
+ *
+ * The EXTERN type uses a new style d2i/i2d.
+ * The new style should be used where possible
+ * because it avoids things like the d2i IMPLICIT
+ * hack.
+ *
+ * MSTRING is a multiple string type, it is used
+ * for a CHOICE of character strings where the
+ * actual strings all occupy an ASN1_STRING
+ * structure. In this case the 'utype' field
+ * has a special meaning, it is used as a mask
+ * of acceptable types using the B_ASN1 constants.
+ *
+ * NDEF_SEQUENCE is the same as SEQUENCE except
+ * that it will use indefinite length constructed
+ * encoding if requested.
+ *
+ */
+
+#define ASN1_ITYPE_PRIMITIVE		0x0
+
+#define ASN1_ITYPE_SEQUENCE		0x1
+
+#define ASN1_ITYPE_CHOICE		0x2
+
+#define ASN1_ITYPE_COMPAT		0x3
+
+#define ASN1_ITYPE_EXTERN		0x4
+
+#define ASN1_ITYPE_MSTRING		0x5
+
+#define ASN1_ITYPE_NDEF_SEQUENCE	0x6
+
+/* Cache for ASN1 tag and length, so we
+ * don't keep re-reading it for things
+ * like CHOICE
+ */
+
+struct ASN1_TLC_st{
+	char valid;	/* Values below are valid */
+	int ret;	/* return value */
+	long plen;	/* length */
+	int ptag;	/* class value */
+	int pclass;	/* class value */
+	int hdrlen;	/* header length */
+};
+
+/* Typedefs for ASN1 function pointers */
+
+typedef ASN1_VALUE * ASN1_new_func(void);
+typedef void ASN1_free_func(ASN1_VALUE *a);
+typedef ASN1_VALUE * ASN1_d2i_func(ASN1_VALUE **a, const unsigned char ** in, long length);
+typedef int ASN1_i2d_func(ASN1_VALUE * a, unsigned char **in);
+
+typedef int ASN1_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it,
+					int tag, int aclass, char opt, ASN1_TLC *ctx);
+
+typedef int ASN1_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass);
+typedef int ASN1_ex_new_func(ASN1_VALUE **pval, const ASN1_ITEM *it);
+typedef void ASN1_ex_free_func(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+typedef int ASN1_ex_print_func(BIO *out, ASN1_VALUE **pval, 
+						int indent, const char *fname, 
+						const ASN1_PCTX *pctx);
+
+typedef int ASN1_primitive_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it);
+typedef int ASN1_primitive_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it);
+typedef int ASN1_primitive_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it, int indent, const ASN1_PCTX *pctx);
+
+typedef struct ASN1_COMPAT_FUNCS_st {
+	ASN1_new_func *asn1_new;
+	ASN1_free_func *asn1_free;
+	ASN1_d2i_func *asn1_d2i;
+	ASN1_i2d_func *asn1_i2d;
+} ASN1_COMPAT_FUNCS;
+
+typedef struct ASN1_EXTERN_FUNCS_st {
+	void *app_data;
+	ASN1_ex_new_func *asn1_ex_new;
+	ASN1_ex_free_func *asn1_ex_free;
+	ASN1_ex_free_func *asn1_ex_clear;
+	ASN1_ex_d2i *asn1_ex_d2i;
+	ASN1_ex_i2d *asn1_ex_i2d;
+	ASN1_ex_print_func *asn1_ex_print;
+} ASN1_EXTERN_FUNCS;
+
+typedef struct ASN1_PRIMITIVE_FUNCS_st {
+	void *app_data;
+	unsigned long flags;
+	ASN1_ex_new_func *prim_new;
+	ASN1_ex_free_func *prim_free;
+	ASN1_ex_free_func *prim_clear;
+	ASN1_primitive_c2i *prim_c2i;
+	ASN1_primitive_i2c *prim_i2c;
+	ASN1_primitive_print *prim_print;
+} ASN1_PRIMITIVE_FUNCS;
+
+/* This is the ASN1_AUX structure: it handles various
+ * miscellaneous requirements. For example the use of
+ * reference counts and an informational callback.
+ *
+ * The "informational callback" is called at various
+ * points during the ASN1 encoding and decoding. It can
+ * be used to provide minor customisation of the structures
+ * used. This is most useful where the supplied routines
+ * *almost* do the right thing but need some extra help
+ * at a few points. If the callback returns zero then
+ * it is assumed a fatal error has occurred and the 
+ * main operation should be abandoned.
+ *
+ * If major changes in the default behaviour are required
+ * then an external type is more appropriate.
+ */
+
+typedef int ASN1_aux_cb(int operation, ASN1_VALUE **in, const ASN1_ITEM *it,
+				void *exarg);
+
+typedef struct ASN1_AUX_st {
+	void *app_data;
+	int flags;
+	int ref_offset;		/* Offset of reference value */
+	int ref_lock;		/* Lock type to use */
+	ASN1_aux_cb *asn1_cb;
+	int enc_offset;		/* Offset of ASN1_ENCODING structure */
+} ASN1_AUX;
+
+/* For print related callbacks exarg points to this structure */
+typedef struct ASN1_PRINT_ARG_st {
+	BIO *out;
+	int indent;
+	const ASN1_PCTX *pctx;
+} ASN1_PRINT_ARG;
+
+/* For streaming related callbacks exarg points to this structure */
+typedef struct ASN1_STREAM_ARG_st {
+	/* BIO to stream through */
+	BIO *out;
+	/* BIO with filters appended */
+	BIO *ndef_bio;
+	/* Streaming I/O boundary */
+	unsigned char **boundary;
+} ASN1_STREAM_ARG;
+
+/* Flags in ASN1_AUX */
+
+/* Use a reference count */
+#define ASN1_AFLG_REFCOUNT	1
+/* Save the encoding of structure (useful for signatures) */
+#define ASN1_AFLG_ENCODING	2
+/* The Sequence length is invalid */
+#define ASN1_AFLG_BROKEN	4
+
+/* operation values for asn1_cb */
+
+#define ASN1_OP_NEW_PRE		0
+#define ASN1_OP_NEW_POST	1
+#define ASN1_OP_FREE_PRE	2
+#define ASN1_OP_FREE_POST	3
+#define ASN1_OP_D2I_PRE		4
+#define ASN1_OP_D2I_POST	5
+#define ASN1_OP_I2D_PRE		6
+#define ASN1_OP_I2D_POST	7
+#define ASN1_OP_PRINT_PRE	8
+#define ASN1_OP_PRINT_POST	9
+#define ASN1_OP_STREAM_PRE	10
+#define ASN1_OP_STREAM_POST	11
+#define ASN1_OP_DETACHED_PRE	12
+#define ASN1_OP_DETACHED_POST	13
+
+/* Macro to implement a primitive type */
+#define IMPLEMENT_ASN1_TYPE(stname) IMPLEMENT_ASN1_TYPE_ex(stname, stname, 0)
+#define IMPLEMENT_ASN1_TYPE_ex(itname, vname, ex) \
+				ASN1_ITEM_start(itname) \
+					ASN1_ITYPE_PRIMITIVE, V_##vname, NULL, 0, NULL, ex, #itname \
+				ASN1_ITEM_end(itname)
+
+/* Macro to implement a multi string type */
+#define IMPLEMENT_ASN1_MSTRING(itname, mask) \
+				ASN1_ITEM_start(itname) \
+					ASN1_ITYPE_MSTRING, mask, NULL, 0, NULL, sizeof(ASN1_STRING), #itname \
+				ASN1_ITEM_end(itname)
+
+/* Macro to implement an ASN1_ITEM in terms of old style funcs */
+
+#define IMPLEMENT_COMPAT_ASN1(sname) IMPLEMENT_COMPAT_ASN1_type(sname, V_ASN1_SEQUENCE)
+
+#define IMPLEMENT_COMPAT_ASN1_type(sname, tag) \
+	static const ASN1_COMPAT_FUNCS sname##_ff = { \
+		(ASN1_new_func *)sname##_new, \
+		(ASN1_free_func *)sname##_free, \
+		(ASN1_d2i_func *)d2i_##sname, \
+		(ASN1_i2d_func *)i2d_##sname, \
+	}; \
+	ASN1_ITEM_start(sname) \
+		ASN1_ITYPE_COMPAT, \
+		tag, \
+		NULL, \
+		0, \
+		&sname##_ff, \
+		0, \
+		#sname \
+	ASN1_ITEM_end(sname)
+
+#define IMPLEMENT_EXTERN_ASN1(sname, tag, fptrs) \
+	ASN1_ITEM_start(sname) \
+		ASN1_ITYPE_EXTERN, \
+		tag, \
+		NULL, \
+		0, \
+		&fptrs, \
+		0, \
+		#sname \
+	ASN1_ITEM_end(sname)
+
+/* Macro to implement standard functions in terms of ASN1_ITEM structures */
+
+#define IMPLEMENT_ASN1_FUNCTIONS(stname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, stname, stname)
+
+#define IMPLEMENT_ASN1_FUNCTIONS_name(stname, itname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, itname)
+
+#define IMPLEMENT_ASN1_FUNCTIONS_ENCODE_name(stname, itname) \
+			IMPLEMENT_ASN1_FUNCTIONS_ENCODE_fname(stname, itname, itname)
+
+#define IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(stname) \
+		IMPLEMENT_ASN1_ALLOC_FUNCTIONS_pfname(static, stname, stname, stname)
+
+#define IMPLEMENT_ASN1_ALLOC_FUNCTIONS(stname) \
+		IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, stname, stname)
+
+#define IMPLEMENT_ASN1_ALLOC_FUNCTIONS_pfname(pre, stname, itname, fname) \
+	pre stname *fname##_new(void) \
+	{ \
+		return (stname *)ASN1_item_new(ASN1_ITEM_rptr(itname)); \
+	} \
+	pre void fname##_free(stname *a) \
+	{ \
+		ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname)); \
+	}
+
+#define IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) \
+	stname *fname##_new(void) \
+	{ \
+		return (stname *)ASN1_item_new(ASN1_ITEM_rptr(itname)); \
+	} \
+	void fname##_free(stname *a) \
+	{ \
+		ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname)); \
+	}
+
+#define IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, fname) \
+	IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \
+	IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname)
+
+#define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \
+	stname *d2i_##fname(stname **a, const unsigned char **in, long len) \
+	{ \
+		return (stname *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(itname));\
+	} \
+	int i2d_##fname(stname *a, unsigned char **out) \
+	{ \
+		return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(itname));\
+	} 
+
+#define IMPLEMENT_ASN1_NDEF_FUNCTION(stname) \
+	int i2d_##stname##_NDEF(stname *a, unsigned char **out) \
+	{ \
+		return ASN1_item_ndef_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(stname));\
+	} 
+
+/* This includes evil casts to remove const: they will go away when full
+ * ASN1 constification is done.
+ */
+#define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(stname, itname, fname) \
+	stname *d2i_##fname(stname **a, const unsigned char **in, long len) \
+	{ \
+		return (stname *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(itname));\
+	} \
+	int i2d_##fname(const stname *a, unsigned char **out) \
+	{ \
+		return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(itname));\
+	} 
+
+#define IMPLEMENT_ASN1_DUP_FUNCTION(stname) \
+	stname * stname##_dup(stname *x) \
+        { \
+        return ASN1_item_dup(ASN1_ITEM_rptr(stname), x); \
+        }
+
+#define IMPLEMENT_ASN1_PRINT_FUNCTION(stname) \
+	IMPLEMENT_ASN1_PRINT_FUNCTION_fname(stname, stname, stname)
+
+#define IMPLEMENT_ASN1_PRINT_FUNCTION_fname(stname, itname, fname) \
+	int fname##_print_ctx(BIO *out, stname *x, int indent, \
+						const ASN1_PCTX *pctx) \
+	{ \
+		return ASN1_item_print(out, (ASN1_VALUE *)x, indent, \
+			ASN1_ITEM_rptr(itname), pctx); \
+	} 
+
+#define IMPLEMENT_ASN1_FUNCTIONS_const(name) \
+		IMPLEMENT_ASN1_FUNCTIONS_const_fname(name, name, name)
+
+#define IMPLEMENT_ASN1_FUNCTIONS_const_fname(stname, itname, fname) \
+	IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(stname, itname, fname) \
+	IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname)
+
+/* external definitions for primitive types */
+
+DECLARE_ASN1_ITEM(ASN1_BOOLEAN)
+DECLARE_ASN1_ITEM(ASN1_TBOOLEAN)
+DECLARE_ASN1_ITEM(ASN1_FBOOLEAN)
+DECLARE_ASN1_ITEM(ASN1_SEQUENCE)
+DECLARE_ASN1_ITEM(CBIGNUM)
+DECLARE_ASN1_ITEM(BIGNUM)
+DECLARE_ASN1_ITEM(LONG)
+DECLARE_ASN1_ITEM(ZLONG)
+
+DECLARE_STACK_OF(ASN1_VALUE)
+
+/* Functions used internally by the ASN1 code */
+
+int ASN1_item_ex_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+void ASN1_item_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+int ASN1_template_new(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+void ASN1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+int ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_TEMPLATE *tt);
+int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it,
+				int tag, int aclass, char opt, ASN1_TLC *ctx);
+
+int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass);
+int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_TEMPLATE *tt);
+void ASN1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it);
+int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it);
+
+int asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it);
+int asn1_set_choice_selector(ASN1_VALUE **pval, int value, const ASN1_ITEM *it);
+
+ASN1_VALUE ** asn1_get_field_ptr(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+
+const ASN1_TEMPLATE *asn1_do_adb(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt, int nullerr);
+
+int asn1_do_lock(ASN1_VALUE **pval, int op, const ASN1_ITEM *it);
+
+void asn1_enc_init(ASN1_VALUE **pval, const ASN1_ITEM *it);
+void asn1_enc_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval, const ASN1_ITEM *it);
+int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen, const ASN1_ITEM *it);
+
+#ifdef  __cplusplus
+}
+#endif
+#endif
diff --git a/crypto/asn1/asn_pack.c b/crypto/asn1/asn_pack.c
new file mode 100644
index 0000000..3845906
--- /dev/null
+++ b/crypto/asn1/asn_pack.c
@@ -0,0 +1,193 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+#if 0
+#ifndef NO_ASN1_OLD
+
+/* ASN1 packing and unpacking functions */
+
+/* Turn an ASN1 encoded SEQUENCE OF into a STACK of structures */
+
+STACK_OF(OPENSSL_BLOCK) *ASN1_seq_unpack(const unsigned char *buf, int len,
+			 d2i_of_void *d2i, void (*free_func)(OPENSSL_BLOCK))
+{
+    STACK_OF(OPENSSL_BLOCK) *sk;
+    const unsigned char *pbuf;
+    pbuf =  buf;
+    if (!(sk = d2i_ASN1_SET(NULL, &pbuf, len, d2i, free_func,
+					V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL)))
+		 OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_DECODE_ERROR);
+    return sk;
+}
+
+/* Turn a STACK structures into an ASN1 encoded SEQUENCE OF structure in a
+ * OPENSSL_malloc'ed buffer
+ */
+
+unsigned char *ASN1_seq_pack(STACK_OF(OPENSSL_BLOCK) *safes, i2d_of_void *i2d,
+			     unsigned char **buf, int *len)
+{
+	int safelen;
+	unsigned char *safe, *p;
+	if (!(safelen = i2d_ASN1_SET(safes, NULL, i2d, V_ASN1_SEQUENCE,
+					      V_ASN1_UNIVERSAL, IS_SEQUENCE))) {
+		OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_ENCODE_ERROR);
+		return NULL;
+	}
+	if (!(safe = OPENSSL_malloc (safelen))) {
+		OPENSSL_PUT_ERROR(ASN1, XXX, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	p = safe;
+	i2d_ASN1_SET(safes, &p, i2d, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL,
+								 IS_SEQUENCE);
+	if (len) *len = safelen;
+	if (buf) *buf = safe;
+	return safe;
+}
+
+/* Extract an ASN1 object from an ASN1_STRING */
+
+void *ASN1_unpack_string(ASN1_STRING *oct, d2i_of_void *d2i)
+{
+	const unsigned char *p;
+	char *ret;
+
+	p = oct->data;
+	if(!(ret = d2i(NULL, &p, oct->length)))
+		OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_DECODE_ERROR);
+	return ret;
+}
+
+/* Pack an ASN1 object into an ASN1_STRING */
+
+ASN1_STRING *ASN1_pack_string(void *obj, i2d_of_void *i2d, ASN1_STRING **oct)
+{
+	unsigned char *p;
+	ASN1_STRING *octmp;
+
+	if (!oct || !*oct) {
+		if (!(octmp = ASN1_STRING_new ())) {
+			OPENSSL_PUT_ERROR(ASN1, XXX, ERR_R_MALLOC_FAILURE);
+			return NULL;
+		}
+		if (oct) *oct = octmp;
+	} else octmp = *oct;
+		
+	if (!(octmp->length = i2d(obj, NULL))) {
+		OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_ENCODE_ERROR);
+		return NULL;
+	}
+	if (!(p = OPENSSL_malloc (octmp->length))) {
+		OPENSSL_PUT_ERROR(ASN1, XXX, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	octmp->data = p;
+	i2d (obj, &p);
+	return octmp;
+}
+
+#endif
+#endif
+
+/* ASN1_ITEM versions of the above */
+
+ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_STRING **oct)
+{
+	ASN1_STRING *octmp;
+
+	if (!oct || !*oct) {
+		if (!(octmp = ASN1_STRING_new ())) {
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_pack, ERR_R_MALLOC_FAILURE);
+			return NULL;
+		}
+		if (oct) *oct = octmp;
+	} else octmp = *oct;
+
+	if(octmp->data) {
+		OPENSSL_free(octmp->data);
+		octmp->data = NULL;
+	}
+		
+	if (!(octmp->length = ASN1_item_i2d(obj, &octmp->data, it))) {
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_pack, ASN1_R_ENCODE_ERROR);
+		return NULL;
+	}
+	if (!octmp->data) {
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_pack, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	return octmp;
+}
+
+/* Extract an ASN1 object from an ASN1_STRING */
+
+void *ASN1_item_unpack(ASN1_STRING *oct, const ASN1_ITEM *it)
+{
+	const unsigned char *p;
+	void *ret;
+
+	p = oct->data;
+	if(!(ret = ASN1_item_d2i(NULL, &p, oct->length, it)))
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_unpack, ASN1_R_DECODE_ERROR);
+	return ret;
+}
diff --git a/crypto/asn1/bio_asn1.c b/crypto/asn1/bio_asn1.c
new file mode 100644
index 0000000..5658839
--- /dev/null
+++ b/crypto/asn1/bio_asn1.c
@@ -0,0 +1,492 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <assert.h>
+
+#include <openssl/bio.h>
+#include <openssl/mem.h>
+
+
+/* Must be large enough for biggest tag+length */
+#define DEFAULT_ASN1_BUF_SIZE 20
+
+typedef enum 
+	{
+	ASN1_STATE_START,
+	ASN1_STATE_PRE_COPY,
+	ASN1_STATE_HEADER,
+	ASN1_STATE_HEADER_COPY,
+	ASN1_STATE_DATA_COPY,
+	ASN1_STATE_POST_COPY,
+	ASN1_STATE_DONE
+	} asn1_bio_state_t;
+
+typedef struct BIO_ASN1_EX_FUNCS_st
+	{
+	asn1_ps_func	*ex_func;
+	asn1_ps_func	*ex_free_func;
+	} BIO_ASN1_EX_FUNCS;
+
+typedef struct BIO_ASN1_BUF_CTX_t
+	{
+	/* Internal state */
+	asn1_bio_state_t state;
+	/* Internal buffer */
+	unsigned char *buf;
+	/* Size of buffer */
+	int bufsize;
+	/* Current position in buffer */
+	int bufpos;
+	/* Current buffer length */
+	int buflen;
+	/* Amount of data to copy */
+	int copylen;
+	/* Class and tag to use */
+	int asn1_class, asn1_tag;
+	asn1_ps_func *prefix, *prefix_free, *suffix, *suffix_free;
+	/* Extra buffer for prefix and suffix data */
+	unsigned char *ex_buf;
+	int ex_len;
+	int ex_pos;
+	void *ex_arg;
+	} BIO_ASN1_BUF_CTX;
+
+
+static int asn1_bio_write(BIO *h, const char *buf,int num);
+static int asn1_bio_read(BIO *h, char *buf, int size);
+static int asn1_bio_puts(BIO *h, const char *str);
+static int asn1_bio_gets(BIO *h, char *str, int size);
+static long asn1_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int asn1_bio_new(BIO *h);
+static int asn1_bio_free(BIO *data);
+static long asn1_bio_callback_ctrl(BIO *h, int cmd, bio_info_cb fp);
+
+static int asn1_bio_init(BIO_ASN1_BUF_CTX *ctx, int size);
+static int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+				asn1_ps_func *cleanup, asn1_bio_state_t next);
+static int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+				asn1_ps_func *setup, 
+				asn1_bio_state_t ex_state,
+				asn1_bio_state_t other_state);
+
+static BIO_METHOD methods_asn1=
+	{
+	BIO_TYPE_ASN1,
+	"asn1",
+	asn1_bio_write,
+	asn1_bio_read,
+	asn1_bio_puts,
+	asn1_bio_gets,
+	asn1_bio_ctrl,
+	asn1_bio_new,
+	asn1_bio_free,
+	asn1_bio_callback_ctrl,
+	};
+
+BIO_METHOD *BIO_f_asn1(void)
+	{
+	return(&methods_asn1);
+	}
+
+
+static int asn1_bio_new(BIO *b)
+	{
+	BIO_ASN1_BUF_CTX *ctx;
+	ctx = OPENSSL_malloc(sizeof(BIO_ASN1_BUF_CTX));
+	if (!ctx)
+		return 0;
+	if (!asn1_bio_init(ctx, DEFAULT_ASN1_BUF_SIZE))
+		return 0;
+	b->init = 1;
+	b->ptr = (char *)ctx;
+	b->flags = 0;
+	return 1;
+	}
+
+static int asn1_bio_init(BIO_ASN1_BUF_CTX *ctx, int size)
+	{
+	ctx->buf = OPENSSL_malloc(size);
+	if (!ctx->buf)
+		return 0;
+	ctx->bufsize = size;
+	ctx->bufpos = 0;
+	ctx->buflen = 0;
+	ctx->copylen = 0;
+	ctx->asn1_class = V_ASN1_UNIVERSAL;
+	ctx->asn1_tag = V_ASN1_OCTET_STRING;
+	ctx->ex_buf = 0;
+	ctx->ex_pos = 0;
+	ctx->ex_len = 0;
+	ctx->state = ASN1_STATE_START;
+	return 1;
+	}
+
+static int asn1_bio_free(BIO *b)
+	{
+	BIO_ASN1_BUF_CTX *ctx;
+	ctx = (BIO_ASN1_BUF_CTX *) b->ptr;
+	if (ctx == NULL)
+		return 0;
+	if (ctx->buf)
+		OPENSSL_free(ctx->buf);
+	OPENSSL_free(ctx);
+	b->init = 0;
+	b->ptr = NULL;
+	b->flags = 0;
+	return 1;
+	}
+
+static int asn1_bio_write(BIO *b, const char *in , int inl)
+	{
+	BIO_ASN1_BUF_CTX *ctx;
+	int wrmax, wrlen, ret;
+	unsigned char *p;
+	if (!in || (inl < 0) || (b->next_bio == NULL))
+		return 0;
+	ctx = (BIO_ASN1_BUF_CTX *) b->ptr;
+	if (ctx == NULL)
+		return 0;
+
+	wrlen = 0;
+	ret = -1;
+
+	for(;;)
+		{
+		switch (ctx->state)
+			{
+
+			/* Setup prefix data, call it */
+			case ASN1_STATE_START:
+			if (!asn1_bio_setup_ex(b, ctx, ctx->prefix,
+				ASN1_STATE_PRE_COPY, ASN1_STATE_HEADER))
+				return 0;
+			break;
+
+			/* Copy any pre data first */
+			case ASN1_STATE_PRE_COPY:
+
+			ret = asn1_bio_flush_ex(b, ctx, ctx->prefix_free,
+							ASN1_STATE_HEADER);
+
+			if (ret <= 0)
+				goto done;
+
+			break;
+
+			case ASN1_STATE_HEADER:
+			ctx->buflen =
+				ASN1_object_size(0, inl, ctx->asn1_tag) - inl;
+			assert(ctx->buflen <= ctx->bufsize);
+			p = ctx->buf;
+			ASN1_put_object(&p, 0, inl,
+					ctx->asn1_tag, ctx->asn1_class);
+			ctx->copylen = inl;
+			ctx->state = ASN1_STATE_HEADER_COPY;
+
+			break;
+
+			case ASN1_STATE_HEADER_COPY:	
+			ret = BIO_write(b->next_bio,
+					ctx->buf + ctx->bufpos, ctx->buflen);
+			if (ret <= 0)
+				goto done;
+
+			ctx->buflen -= ret;
+			if (ctx->buflen)
+				ctx->bufpos += ret;
+			else
+				{
+				ctx->bufpos = 0;
+				ctx->state = ASN1_STATE_DATA_COPY;
+				}
+
+			break;
+
+			case ASN1_STATE_DATA_COPY:
+
+			if (inl > ctx->copylen)
+				wrmax = ctx->copylen;
+			else
+				wrmax = inl;
+			ret = BIO_write(b->next_bio, in, wrmax);
+			if (ret <= 0)
+				break;
+			wrlen += ret;
+			ctx->copylen -= ret;
+			in += ret;
+			inl -= ret;
+
+			if (ctx->copylen == 0)
+				ctx->state = ASN1_STATE_HEADER;
+
+			if (inl == 0)
+				goto done;
+
+			break;
+
+			default:
+			BIO_clear_retry_flags(b);
+			return 0;
+
+			}
+
+		}
+
+	done:
+	BIO_clear_retry_flags(b);
+	BIO_copy_next_retry(b);
+
+	return (wrlen > 0) ? wrlen : ret;
+
+	}
+
+static int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+				asn1_ps_func *cleanup, asn1_bio_state_t next)
+	{
+	int ret;
+	if (ctx->ex_len <= 0)
+		return 1;
+	for(;;)
+		{
+		ret = BIO_write(b->next_bio, ctx->ex_buf + ctx->ex_pos,
+								ctx->ex_len);
+		if (ret <= 0)
+			break;
+		ctx->ex_len -= ret;
+		if (ctx->ex_len > 0)
+			ctx->ex_pos += ret;
+		else
+			{
+			if(cleanup)
+				cleanup(b, &ctx->ex_buf, &ctx->ex_len,
+								&ctx->ex_arg);
+			ctx->state = next;
+			ctx->ex_pos = 0;
+			break;
+			}
+		}
+	return ret;
+	}
+
+static int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+				asn1_ps_func *setup, 
+				asn1_bio_state_t ex_state,
+				asn1_bio_state_t other_state)
+	{
+	if (setup && !setup(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg))
+		{
+		BIO_clear_retry_flags(b);
+		return 0;
+		}
+	if (ctx->ex_len > 0)
+		ctx->state = ex_state;
+	else
+		ctx->state = other_state;
+	return 1;
+	}
+
+static int asn1_bio_read(BIO *b, char *in , int inl)
+	{
+	if (!b->next_bio)
+		return 0;
+	return BIO_read(b->next_bio, in , inl);
+	}
+
+static int asn1_bio_puts(BIO *b, const char *str)
+	{
+	return asn1_bio_write(b, str, strlen(str));
+	}
+
+static int asn1_bio_gets(BIO *b, char *str, int size)
+	{
+	if (!b->next_bio)
+		return 0;
+	return BIO_gets(b->next_bio, str , size);
+	}
+
+static long asn1_bio_callback_ctrl(BIO *b, int cmd, bio_info_cb fp)
+	{
+	if (b->next_bio == NULL) return(0);
+	return BIO_callback_ctrl(b->next_bio,cmd,fp);
+	}
+
+static long asn1_bio_ctrl(BIO *b, int cmd, long arg1, void *arg2)
+	{
+	BIO_ASN1_BUF_CTX *ctx;
+	BIO_ASN1_EX_FUNCS *ex_func;
+	long ret = 1;
+	ctx = (BIO_ASN1_BUF_CTX *) b->ptr;
+	if (ctx == NULL)
+		return 0;
+	switch(cmd)
+		{
+
+		case BIO_C_SET_PREFIX:
+		ex_func = arg2;
+		ctx->prefix  = ex_func->ex_func;
+		ctx->prefix_free  = ex_func->ex_free_func;
+		break;
+
+		case BIO_C_GET_PREFIX:
+		ex_func = arg2;
+		ex_func->ex_func = ctx->prefix;
+		ex_func->ex_free_func = ctx->prefix_free;
+		break;
+
+		case BIO_C_SET_SUFFIX:
+		ex_func = arg2;
+		ctx->suffix  = ex_func->ex_func;
+		ctx->suffix_free  = ex_func->ex_free_func;
+		break;
+
+		case BIO_C_GET_SUFFIX:
+		ex_func = arg2;
+		ex_func->ex_func = ctx->suffix;
+		ex_func->ex_free_func = ctx->suffix_free;
+		break;
+
+		case BIO_C_SET_EX_ARG:
+		ctx->ex_arg = arg2;
+		break;
+
+		case BIO_C_GET_EX_ARG:
+		*(void **)arg2 = ctx->ex_arg;
+		break;
+
+		case BIO_CTRL_FLUSH:
+		if (!b->next_bio)
+			return 0;
+
+		/* Call post function if possible */
+		if (ctx->state == ASN1_STATE_HEADER)
+			{
+			if (!asn1_bio_setup_ex(b, ctx, ctx->suffix,
+				ASN1_STATE_POST_COPY, ASN1_STATE_DONE))
+				return 0;
+			}
+
+		if (ctx->state == ASN1_STATE_POST_COPY)
+			{
+			ret = asn1_bio_flush_ex(b, ctx, ctx->suffix_free,
+							ASN1_STATE_DONE);
+			if (ret <= 0)
+				return ret;
+			}
+
+		if (ctx->state == ASN1_STATE_DONE)
+			return BIO_ctrl(b->next_bio, cmd, arg1, arg2);
+		else
+			{
+			BIO_clear_retry_flags(b);
+			return 0;
+			}
+		break;
+
+
+		default:
+		if (!b->next_bio)
+			return 0;
+		return BIO_ctrl(b->next_bio, cmd, arg1, arg2);
+
+		}
+
+	return ret;
+	}
+
+static int asn1_bio_set_ex(BIO *b, int cmd,
+		asn1_ps_func *ex_func, asn1_ps_func *ex_free_func)
+	{
+	BIO_ASN1_EX_FUNCS extmp;
+	extmp.ex_func = ex_func;
+	extmp.ex_free_func = ex_free_func;
+	return BIO_ctrl(b, cmd, 0, &extmp);
+	}
+
+static int asn1_bio_get_ex(BIO *b, int cmd,
+		asn1_ps_func **ex_func, asn1_ps_func **ex_free_func)
+	{
+	BIO_ASN1_EX_FUNCS extmp;
+	int ret;
+	ret = BIO_ctrl(b, cmd, 0, &extmp);
+	if (ret > 0)
+		{
+		*ex_func = extmp.ex_func;
+		*ex_free_func = extmp.ex_free_func;
+		}
+	return ret;
+	}
+
+int BIO_asn1_set_prefix(BIO *b, asn1_ps_func *prefix, asn1_ps_func *prefix_free)
+	{
+	return asn1_bio_set_ex(b, BIO_C_SET_PREFIX, prefix, prefix_free);
+	}
+
+int BIO_asn1_get_prefix(BIO *b, asn1_ps_func **pprefix, asn1_ps_func **pprefix_free)
+	{
+	return asn1_bio_get_ex(b, BIO_C_GET_PREFIX, pprefix, pprefix_free);
+	}
+
+int BIO_asn1_set_suffix(BIO *b, asn1_ps_func *suffix, asn1_ps_func *suffix_free)
+	{
+	return asn1_bio_set_ex(b, BIO_C_SET_SUFFIX, suffix, suffix_free);
+	}
+
+int BIO_asn1_get_suffix(BIO *b, asn1_ps_func **psuffix, asn1_ps_func **psuffix_free)
+	{
+	return asn1_bio_get_ex(b, BIO_C_GET_SUFFIX, psuffix, psuffix_free);
+	}
diff --git a/crypto/asn1/bio_ndef.c b/crypto/asn1/bio_ndef.c
new file mode 100644
index 0000000..c814814
--- /dev/null
+++ b/crypto/asn1/bio_ndef.c
@@ -0,0 +1,248 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <assert.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+/* Experimental NDEF ASN1 BIO support routines */
+
+/* The usage is quite simple, initialize an ASN1 structure,
+ * get a BIO from it then any data written through the BIO
+ * will end up translated to approptiate format on the fly.
+ * The data is streamed out and does *not* need to be
+ * all held in memory at once.
+ *
+ * When the BIO is flushed the output is finalized and any
+ * signatures etc written out.
+ *
+ * The BIO is a 'proper' BIO and can handle non blocking I/O
+ * correctly.
+ *
+ * The usage is simple. The implementation is *not*...
+ */
+
+/* BIO support data stored in the ASN1 BIO ex_arg */
+
+typedef struct ndef_aux_st
+	{
+	/* ASN1 structure this BIO refers to */
+	ASN1_VALUE *val;
+	const ASN1_ITEM *it;
+	/* Top of the BIO chain */
+	BIO *ndef_bio;
+	/* Output BIO */
+	BIO *out;
+	/* Boundary where content is inserted */
+	unsigned char **boundary;
+	/* DER buffer start */
+	unsigned char *derbuf;
+	} NDEF_SUPPORT;
+
+static int ndef_prefix(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+static int ndef_prefix_free(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+static int ndef_suffix(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+static int ndef_suffix_free(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+
+BIO *BIO_new_NDEF(BIO *out, ASN1_VALUE *val, const ASN1_ITEM *it)
+	{
+	NDEF_SUPPORT *ndef_aux = NULL;
+	BIO *asn_bio = NULL;
+	const ASN1_AUX *aux = it->funcs;
+	ASN1_STREAM_ARG sarg;
+
+	if (!aux || !aux->asn1_cb)
+		{
+		OPENSSL_PUT_ERROR(ASN1, BIO_new_NDEF, ASN1_R_STREAMING_NOT_SUPPORTED);
+		return NULL;
+		}
+	ndef_aux = OPENSSL_malloc(sizeof(NDEF_SUPPORT));
+	asn_bio = BIO_new(BIO_f_asn1());
+
+	/* ASN1 bio needs to be next to output BIO */
+
+	out = BIO_push(asn_bio, out);
+
+	if (!ndef_aux || !asn_bio || !out)
+		goto err;
+
+	BIO_asn1_set_prefix(asn_bio, ndef_prefix, ndef_prefix_free);
+	BIO_asn1_set_suffix(asn_bio, ndef_suffix, ndef_suffix_free);
+
+	/* Now let callback prepend any digest, cipher etc BIOs
+	 * ASN1 structure needs.
+	 */
+
+	sarg.out = out;
+	sarg.ndef_bio = NULL;
+	sarg.boundary = NULL;
+
+	if (aux->asn1_cb(ASN1_OP_STREAM_PRE, &val, it, &sarg) <= 0)
+		goto err;
+
+	ndef_aux->val = val;
+	ndef_aux->it = it;
+	ndef_aux->ndef_bio = sarg.ndef_bio;
+	ndef_aux->boundary = sarg.boundary;
+	ndef_aux->out = out;
+
+	BIO_ctrl(asn_bio, BIO_C_SET_EX_ARG, 0, ndef_aux);
+
+	return sarg.ndef_bio;
+
+	err:
+	if (asn_bio)
+		BIO_free(asn_bio);
+	if (ndef_aux)
+		OPENSSL_free(ndef_aux);
+	return NULL;
+	}
+
+static int ndef_prefix(BIO *b, unsigned char **pbuf, int *plen, void *parg)
+	{
+	NDEF_SUPPORT *ndef_aux;
+	unsigned char *p;
+	int derlen;
+
+	if (!parg)
+		return 0;
+
+	ndef_aux = *(NDEF_SUPPORT **)parg;
+
+	derlen = ASN1_item_ndef_i2d(ndef_aux->val, NULL, ndef_aux->it);
+	p = OPENSSL_malloc(derlen);
+	ndef_aux->derbuf = p;
+	*pbuf = p;
+	derlen = ASN1_item_ndef_i2d(ndef_aux->val, &p, ndef_aux->it);
+
+	if (!*ndef_aux->boundary)
+		return 0;
+
+	*plen = *ndef_aux->boundary - *pbuf;
+
+	return 1;
+	}
+
+static int ndef_prefix_free(BIO *b, unsigned char **pbuf, int *plen, void *parg)
+	{
+	NDEF_SUPPORT *ndef_aux;
+
+	if (!parg)
+		return 0;
+
+	ndef_aux = *(NDEF_SUPPORT **)parg;
+
+	if (ndef_aux->derbuf)
+		OPENSSL_free(ndef_aux->derbuf);
+
+	ndef_aux->derbuf = NULL;
+	*pbuf = NULL;
+	*plen = 0;
+	return 1;
+	}
+
+static int ndef_suffix_free(BIO *b, unsigned char **pbuf, int *plen, void *parg)
+	{
+	NDEF_SUPPORT **pndef_aux = (NDEF_SUPPORT **)parg;
+	if (!ndef_prefix_free(b, pbuf, plen, parg))
+		return 0;
+	OPENSSL_free(*pndef_aux);
+	*pndef_aux = NULL;
+	return 1;
+	}
+
+static int ndef_suffix(BIO *b, unsigned char **pbuf, int *plen, void *parg)
+	{
+	NDEF_SUPPORT *ndef_aux;
+	unsigned char *p;
+	int derlen;
+	const ASN1_AUX *aux;
+	ASN1_STREAM_ARG sarg;
+
+	if (!parg)
+		return 0;
+
+	ndef_aux = *(NDEF_SUPPORT **)parg;
+
+	aux = ndef_aux->it->funcs;
+
+	/* Finalize structures */
+	sarg.ndef_bio = ndef_aux->ndef_bio;
+	sarg.out = ndef_aux->out;
+	sarg.boundary = ndef_aux->boundary;
+	if (aux->asn1_cb(ASN1_OP_STREAM_POST,
+				&ndef_aux->val, ndef_aux->it, &sarg) <= 0)
+		return 0;
+
+	derlen = ASN1_item_ndef_i2d(ndef_aux->val, NULL, ndef_aux->it);
+	p = OPENSSL_malloc(derlen);
+	ndef_aux->derbuf = p;
+	*pbuf = p;
+	derlen = ASN1_item_ndef_i2d(ndef_aux->val, &p, ndef_aux->it);
+
+	if (!*ndef_aux->boundary)
+		return 0;
+	*pbuf = *ndef_aux->boundary;
+	*plen = derlen - (*ndef_aux->boundary - ndef_aux->derbuf);
+
+	return 1;
+	}
diff --git a/crypto/asn1/charmap.pl b/crypto/asn1/charmap.pl
new file mode 100644
index 0000000..2875c59
--- /dev/null
+++ b/crypto/asn1/charmap.pl
@@ -0,0 +1,80 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+my ($i, @arr);
+
+# Set up an array with the type of ASCII characters
+# Each set bit represents a character property.
+
+# RFC2253 character properties
+my $RFC2253_ESC = 1;	# Character escaped with \
+my $ESC_CTRL	= 2;	# Escaped control character
+# These are used with RFC1779 quoting using "
+my $NOESC_QUOTE	= 8;	# Not escaped if quoted
+my $PSTRING_CHAR = 0x10;	# Valid PrintableString character
+my $RFC2253_FIRST_ESC = 0x20; # Escaped with \ if first character
+my $RFC2253_LAST_ESC = 0x40;  # Escaped with \ if last character
+
+for($i = 0; $i < 128; $i++) {
+	# Set the RFC2253 escape characters (control)
+	$arr[$i] = 0;
+	if(($i < 32) || ($i > 126)) {
+		$arr[$i] |= $ESC_CTRL;
+	}
+
+	# Some PrintableString characters
+	if(		   ( ( $i >= ord("a")) && ( $i <= ord("z")) )
+			|| (  ( $i >= ord("A")) && ( $i <= ord("Z")) )
+			|| (  ( $i >= ord("0")) && ( $i <= ord("9")) )  ) {
+		$arr[$i] |= $PSTRING_CHAR;
+	}
+}
+
+# Now setup the rest
+
+# Remaining RFC2253 escaped characters
+
+$arr[ord(" ")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC | $RFC2253_LAST_ESC;
+$arr[ord("#")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC;
+
+$arr[ord(",")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord("+")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord("\"")] |= $RFC2253_ESC;
+$arr[ord("\\")] |= $RFC2253_ESC;
+$arr[ord("<")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord(">")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord(";")] |= $NOESC_QUOTE | $RFC2253_ESC;
+
+# Remaining PrintableString characters
+
+$arr[ord(" ")] |= $PSTRING_CHAR;
+$arr[ord("'")] |= $PSTRING_CHAR;
+$arr[ord("(")] |= $PSTRING_CHAR;
+$arr[ord(")")] |= $PSTRING_CHAR;
+$arr[ord("+")] |= $PSTRING_CHAR;
+$arr[ord(",")] |= $PSTRING_CHAR;
+$arr[ord("-")] |= $PSTRING_CHAR;
+$arr[ord(".")] |= $PSTRING_CHAR;
+$arr[ord("/")] |= $PSTRING_CHAR;
+$arr[ord(":")] |= $PSTRING_CHAR;
+$arr[ord("=")] |= $PSTRING_CHAR;
+$arr[ord("?")] |= $PSTRING_CHAR;
+
+# Now generate the C code
+
+print <<EOF;
+/* Auto generated with chartype.pl script.
+ * Mask of various character properties
+ */
+
+static unsigned char char_type[] = {
+EOF
+
+for($i = 0; $i < 128; $i++) {
+	print("\n") if($i && (($i % 16) == 0));
+	printf("%2d", $arr[$i]);
+	print(",") if ($i != 127);
+}
+print("\n};\n\n");
+
diff --git a/crypto/asn1/f_enum.c b/crypto/asn1/f_enum.c
new file mode 100644
index 0000000..5725824
--- /dev/null
+++ b/crypto/asn1/f_enum.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+/* Based on a_int.c: equivalent ENUMERATED functions */
+
+int i2a_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *a)
+	{
+	int i,n=0;
+	static const char *h="0123456789ABCDEF";
+	char buf[2];
+
+	if (a == NULL) return(0);
+
+	if (a->length == 0)
+		{
+		if (BIO_write(bp,"00",2) != 2) goto err;
+		n=2;
+		}
+	else
+		{
+		for (i=0; i<a->length; i++)
+			{
+			if ((i != 0) && (i%35 == 0))
+				{
+				if (BIO_write(bp,"\\\n",2) != 2) goto err;
+				n+=2;
+				}
+			buf[0]=h[((unsigned char)a->data[i]>>4)&0x0f];
+			buf[1]=h[((unsigned char)a->data[i]   )&0x0f];
+			if (BIO_write(bp,buf,2) != 2) goto err;
+			n+=2;
+			}
+		}
+	return(n);
+err:
+	return(-1);
+	}
+
+int a2i_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *bs, char *buf, int size)
+	{
+	int ret=0;
+	int i,j,k,m,n,again,bufsize;
+	unsigned char *s=NULL,*sp;
+	unsigned char *bufp;
+	int num=0,slen=0,first=1;
+
+	bs->type=V_ASN1_ENUMERATED;
+
+	bufsize=BIO_gets(bp,buf,size);
+	for (;;)
+		{
+		if (bufsize < 1) goto err_sl;
+		i=bufsize;
+		if (buf[i-1] == '\n') buf[--i]='\0';
+		if (i == 0) goto err_sl;
+		if (buf[i-1] == '\r') buf[--i]='\0';
+		if (i == 0) goto err_sl;
+		again=(buf[i-1] == '\\');
+
+		for (j=0; j<i; j++)
+			{
+			if (!(	((buf[j] >= '0') && (buf[j] <= '9')) ||
+				((buf[j] >= 'a') && (buf[j] <= 'f')) ||
+				((buf[j] >= 'A') && (buf[j] <= 'F'))))
+				{
+				i=j;
+				break;
+				}
+			}
+		buf[i]='\0';
+		/* We have now cleared all the crap off the end of the
+		 * line */
+		if (i < 2) goto err_sl;
+
+		bufp=(unsigned char *)buf;
+		if (first)
+			{
+			first=0;
+			if ((bufp[0] == '0') && (buf[1] == '0'))
+				{
+				bufp+=2;
+				i-=2;
+				}
+			}
+		k=0;
+		i-=again;
+		if (i%2 != 0)
+			{
+			OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_ENUMERATED, ASN1_R_ODD_NUMBER_OF_CHARS);
+			goto err;
+			}
+		i/=2;
+		if (num+i > slen)
+			{
+			if (s == NULL)
+				sp=(unsigned char *)OPENSSL_malloc(
+					(unsigned int)num+i*2);
+			else
+				sp=(unsigned char *)OPENSSL_realloc(s,
+					(unsigned int)num+i*2);
+			if (sp == NULL)
+				{
+				OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_ENUMERATED, ERR_R_MALLOC_FAILURE);
+				if (s != NULL) OPENSSL_free(s);
+				goto err;
+				}
+			s=sp;
+			slen=num+i*2;
+			}
+		for (j=0; j<i; j++,k+=2)
+			{
+			for (n=0; n<2; n++)
+				{
+				m=bufp[k+n];
+				if ((m >= '0') && (m <= '9'))
+					m-='0';
+				else if ((m >= 'a') && (m <= 'f'))
+					m=m-'a'+10;
+				else if ((m >= 'A') && (m <= 'F'))
+					m=m-'A'+10;
+				else
+					{
+					OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_ENUMERATED, ASN1_R_NON_HEX_CHARACTERS);
+					goto err;
+					}
+				s[num+j]<<=4;
+				s[num+j]|=m;
+				}
+			}
+		num+=i;
+		if (again)
+			bufsize=BIO_gets(bp,buf,size);
+		else
+			break;
+		}
+	bs->length=num;
+	bs->data=s;
+	ret=1;
+err:
+	if (0)
+		{
+err_sl:
+		OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_ENUMERATED, ASN1_R_SHORT_LINE);
+		}
+	return(ret);
+	}
+
diff --git a/crypto/asn1/f_int.c b/crypto/asn1/f_int.c
new file mode 100644
index 0000000..d9ddcf8
--- /dev/null
+++ b/crypto/asn1/f_int.c
@@ -0,0 +1,218 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+int i2a_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *a)
+	{
+	int i,n=0;
+	static const char *h="0123456789ABCDEF";
+	char buf[2];
+
+	if (a == NULL) return(0);
+
+	if (a->type & V_ASN1_NEG)
+		{
+		if (BIO_write(bp, "-", 1) != 1) goto err;
+		n = 1;
+		}
+
+	if (a->length == 0)
+		{
+		if (BIO_write(bp,"00",2) != 2) goto err;
+		n += 2;
+		}
+	else
+		{
+		for (i=0; i<a->length; i++)
+			{
+			if ((i != 0) && (i%35 == 0))
+				{
+				if (BIO_write(bp,"\\\n",2) != 2) goto err;
+				n+=2;
+				}
+			buf[0]=h[((unsigned char)a->data[i]>>4)&0x0f];
+			buf[1]=h[((unsigned char)a->data[i]   )&0x0f];
+			if (BIO_write(bp,buf,2) != 2) goto err;
+			n+=2;
+			}
+		}
+	return(n);
+err:
+	return(-1);
+	}
+
+int a2i_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *bs, char *buf, int size)
+	{
+	int ret=0;
+	int i,j,k,m,n,again,bufsize;
+	unsigned char *s=NULL,*sp;
+	unsigned char *bufp;
+	int num=0,slen=0,first=1;
+
+	bs->type=V_ASN1_INTEGER;
+
+	bufsize=BIO_gets(bp,buf,size);
+	for (;;)
+		{
+		if (bufsize < 1) goto err_sl;
+		i=bufsize;
+		if (buf[i-1] == '\n') buf[--i]='\0';
+		if (i == 0) goto err_sl;
+		if (buf[i-1] == '\r') buf[--i]='\0';
+		if (i == 0) goto err_sl;
+		again=(buf[i-1] == '\\');
+
+		for (j=0; j<i; j++)
+			{
+#ifndef CHARSET_EBCDIC
+			if (!(	((buf[j] >= '0') && (buf[j] <= '9')) ||
+				((buf[j] >= 'a') && (buf[j] <= 'f')) ||
+				((buf[j] >= 'A') && (buf[j] <= 'F'))))
+#else
+			/* This #ifdef is not strictly necessary, since
+			 * the characters A...F a...f 0...9 are contiguous
+			 * (yes, even in EBCDIC - but not the whole alphabet).
+			 * Nevertheless, isxdigit() is faster.
+			 */
+			if (!isxdigit(buf[j]))
+#endif
+				{
+				i=j;
+				break;
+				}
+			}
+		buf[i]='\0';
+		/* We have now cleared all the crap off the end of the
+		 * line */
+		if (i < 2) goto err_sl;
+
+		bufp=(unsigned char *)buf;
+		if (first)
+			{
+			first=0;
+			if ((bufp[0] == '0') && (buf[1] == '0'))
+				{
+				bufp+=2;
+				i-=2;
+				}
+			}
+		k=0;
+		i-=again;
+		if (i%2 != 0)
+			{
+			OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_INTEGER, ASN1_R_ODD_NUMBER_OF_CHARS);
+			goto err;
+			}
+		i/=2;
+		if (num+i > slen)
+			{
+			if (s == NULL)
+				sp=(unsigned char *)OPENSSL_malloc(
+					(unsigned int)num+i*2);
+			else
+				sp=OPENSSL_realloc_clean(s,slen,num+i*2);
+			if (sp == NULL)
+				{
+				OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_INTEGER, ERR_R_MALLOC_FAILURE);
+				if (s != NULL) OPENSSL_free(s);
+				goto err;
+				}
+			s=sp;
+			slen=num+i*2;
+			}
+		for (j=0; j<i; j++,k+=2)
+			{
+			for (n=0; n<2; n++)
+				{
+				m=bufp[k+n];
+				if ((m >= '0') && (m <= '9'))
+					m-='0';
+				else if ((m >= 'a') && (m <= 'f'))
+					m=m-'a'+10;
+				else if ((m >= 'A') && (m <= 'F'))
+					m=m-'A'+10;
+				else
+					{
+					OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_INTEGER, ASN1_R_NON_HEX_CHARACTERS);
+					goto err;
+					}
+				s[num+j]<<=4;
+				s[num+j]|=m;
+				}
+			}
+		num+=i;
+		if (again)
+			bufsize=BIO_gets(bp,buf,size);
+		else
+			break;
+		}
+	bs->length=num;
+	bs->data=s;
+	ret=1;
+err:
+	if (0)
+		{
+err_sl:
+		OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_INTEGER, ASN1_R_SHORT_LINE);
+		}
+	return(ret);
+	}
+
diff --git a/crypto/asn1/f_string.c b/crypto/asn1/f_string.c
new file mode 100644
index 0000000..95e59ff
--- /dev/null
+++ b/crypto/asn1/f_string.c
@@ -0,0 +1,212 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+
+int i2a_ASN1_STRING(BIO *bp, ASN1_STRING *a, int type)
+	{
+	int i,n=0;
+	static const char *h="0123456789ABCDEF";
+	char buf[2];
+
+	if (a == NULL) return(0);
+
+	if (a->length == 0)
+		{
+		if (BIO_write(bp,"0",1) != 1) goto err;
+		n=1;
+		}
+	else
+		{
+		for (i=0; i<a->length; i++)
+			{
+			if ((i != 0) && (i%35 == 0))
+				{
+				if (BIO_write(bp,"\\\n",2) != 2) goto err;
+				n+=2;
+				}
+			buf[0]=h[((unsigned char)a->data[i]>>4)&0x0f];
+			buf[1]=h[((unsigned char)a->data[i]   )&0x0f];
+			if (BIO_write(bp,buf,2) != 2) goto err;
+			n+=2;
+			}
+		}
+	return(n);
+err:
+	return(-1);
+	}
+
+int a2i_ASN1_STRING(BIO *bp, ASN1_STRING *bs, char *buf, int size)
+	{
+	int ret=0;
+	int i,j,k,m,n,again,bufsize;
+	unsigned char *s=NULL,*sp;
+	unsigned char *bufp;
+	int num=0,slen=0,first=1;
+
+	bufsize=BIO_gets(bp,buf,size);
+	for (;;)
+		{
+		if (bufsize < 1)
+			{
+			if (first)
+				break;
+			else
+				goto err_sl;
+			}
+		first=0;
+
+		i=bufsize;
+		if (buf[i-1] == '\n') buf[--i]='\0';
+		if (i == 0) goto err_sl;
+		if (buf[i-1] == '\r') buf[--i]='\0';
+		if (i == 0) goto err_sl;
+		again=(buf[i-1] == '\\');
+
+		for (j=i-1; j>0; j--)
+			{
+#ifndef CHARSET_EBCDIC
+			if (!(	((buf[j] >= '0') && (buf[j] <= '9')) ||
+				((buf[j] >= 'a') && (buf[j] <= 'f')) ||
+				((buf[j] >= 'A') && (buf[j] <= 'F'))))
+#else
+			/* This #ifdef is not strictly necessary, since
+			 * the characters A...F a...f 0...9 are contiguous
+			 * (yes, even in EBCDIC - but not the whole alphabet).
+			 * Nevertheless, isxdigit() is faster.
+			 */
+			if (!isxdigit(buf[j]))
+#endif
+				{
+				i=j;
+				break;
+				}
+			}
+		buf[i]='\0';
+		/* We have now cleared all the crap off the end of the
+		 * line */
+		if (i < 2) goto err_sl;
+
+		bufp=(unsigned char *)buf;
+
+		k=0;
+		i-=again;
+		if (i%2 != 0)
+			{
+			OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_STRING, ASN1_R_ODD_NUMBER_OF_CHARS);
+			goto err;
+			}
+		i/=2;
+		if (num+i > slen)
+			{
+			if (s == NULL)
+				sp=(unsigned char *)OPENSSL_malloc(
+					(unsigned int)num+i*2);
+			else
+				sp=(unsigned char *)OPENSSL_realloc(s,
+					(unsigned int)num+i*2);
+			if (sp == NULL)
+				{
+				OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_STRING, ERR_R_MALLOC_FAILURE);
+				if (s != NULL) OPENSSL_free(s);
+				goto err;
+				}
+			s=sp;
+			slen=num+i*2;
+			}
+		for (j=0; j<i; j++,k+=2)
+			{
+			for (n=0; n<2; n++)
+				{
+				m=bufp[k+n];
+				if ((m >= '0') && (m <= '9'))
+					m-='0';
+				else if ((m >= 'a') && (m <= 'f'))
+					m=m-'a'+10;
+				else if ((m >= 'A') && (m <= 'F'))
+					m=m-'A'+10;
+				else
+					{
+					OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_STRING, ASN1_R_NON_HEX_CHARACTERS);
+					goto err;
+					}
+				s[num+j]<<=4;
+				s[num+j]|=m;
+				}
+			}
+		num+=i;
+		if (again)
+			bufsize=BIO_gets(bp,buf,size);
+		else
+			break;
+		}
+	bs->length=num;
+	bs->data=s;
+	ret=1;
+err:
+	if (0)
+		{
+err_sl:
+		OPENSSL_PUT_ERROR(ASN1, a2i_ASN1_STRING, ASN1_R_SHORT_LINE);
+		}
+	return(ret);
+	}
+
diff --git a/crypto/asn1/t_bitst.c b/crypto/asn1/t_bitst.c
new file mode 100644
index 0000000..cc2c50b
--- /dev/null
+++ b/crypto/asn1/t_bitst.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/mem.h>
+
+
+int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs,
+				BIT_STRING_BITNAME *tbl, int indent)
+{
+	BIT_STRING_BITNAME *bnam;
+	char first = 1;
+	BIO_printf(out, "%*s", indent, "");
+	for(bnam = tbl; bnam->lname; bnam++) {
+		if(ASN1_BIT_STRING_get_bit(bs, bnam->bitnum)) {
+			if(!first) BIO_puts(out, ", ");
+			BIO_puts(out, bnam->lname);
+			first = 0;
+		}
+	}
+	BIO_puts(out, "\n");
+	return 1;
+}
+
+int ASN1_BIT_STRING_set_asc(ASN1_BIT_STRING *bs, char *name, int value,
+				BIT_STRING_BITNAME *tbl)
+{
+	int bitnum;
+	bitnum = ASN1_BIT_STRING_num_asc(name, tbl);
+	if(bitnum < 0) return 0;
+	if(bs) {
+		if(!ASN1_BIT_STRING_set_bit(bs, bitnum, value))
+			return 0;
+	}
+	return 1;
+}
+
+int ASN1_BIT_STRING_num_asc(char *name, BIT_STRING_BITNAME *tbl)
+{
+	BIT_STRING_BITNAME *bnam;
+	for(bnam = tbl; bnam->lname; bnam++) {
+		if(!strcmp(bnam->sname, name) ||
+			!strcmp(bnam->lname, name) ) return bnam->bitnum;
+	}
+	return -1;
+}
diff --git a/crypto/asn1/t_pkey.c b/crypto/asn1/t_pkey.c
new file mode 100644
index 0000000..6ac9b3d
--- /dev/null
+++ b/crypto/asn1/t_pkey.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/bio.h>
+#include <openssl/mem.h>
+
+
+int ASN1_bn_print(BIO *bp, const char *number, const BIGNUM *num,
+			unsigned char *buf, int off)
+	{
+	int n,i;
+	const char *neg;
+
+	if (num == NULL) return(1);
+	neg = (BN_is_negative(num))?"-":"";
+	if(!BIO_indent(bp,off,128))
+		return 0;
+	if (BN_is_zero(num))
+		{
+		if (BIO_printf(bp, "%s 0\n", number) <= 0)
+			return 0;
+		return 1;
+		}
+
+	if (BN_num_bytes(num) <= sizeof(long))
+		{
+		if (BIO_printf(bp,"%s %s%lu (%s0x%lx)\n",number,neg,
+			(unsigned long)num->d[0],neg,(unsigned long)num->d[0])
+			<= 0) return(0);
+		}
+	else
+		{
+		buf[0]=0;
+		if (BIO_printf(bp,"%s%s",number,
+			(neg[0] == '-')?" (Negative)":"") <= 0)
+			return(0);
+		n=BN_bn2bin(num,&buf[1]);
+	
+		if (buf[1] & 0x80)
+			n++;
+		else	buf++;
+
+		for (i=0; i<n; i++)
+			{
+			if ((i%15) == 0)
+				{
+				if(BIO_puts(bp,"\n") <= 0
+				   || !BIO_indent(bp,off+4,128))
+				    return 0;
+				}
+			if (BIO_printf(bp,"%02x%s",buf[i],((i+1) == n)?"":":")
+				<= 0) return(0);
+			}
+		if (BIO_write(bp,"\n",1) <= 0) return(0);
+		}
+	return(1);
+	}
diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c
new file mode 100644
index 0000000..80c8b7d
--- /dev/null
+++ b/crypto/asn1/tasn_dec.c
@@ -0,0 +1,1303 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+static int asn1_check_eoc(const unsigned char **in, long len);
+static int asn1_find_end(const unsigned char **in, long len, char inf);
+
+static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len,
+			char inf, int tag, int aclass, int depth);
+
+static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen);
+
+static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
+				char *inf, char *cst,
+				const unsigned char **in, long len,
+				int exptag, int expclass, char opt,
+				ASN1_TLC *ctx);
+
+static int asn1_template_ex_d2i(ASN1_VALUE **pval,
+				const unsigned char **in, long len,
+				const ASN1_TEMPLATE *tt, char opt,
+				ASN1_TLC *ctx);
+static int asn1_template_noexp_d2i(ASN1_VALUE **val,
+				const unsigned char **in, long len,
+				const ASN1_TEMPLATE *tt, char opt,
+				ASN1_TLC *ctx);
+static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
+				const unsigned char **in, long len,
+				const ASN1_ITEM *it,
+				int tag, int aclass, char opt, ASN1_TLC *ctx);
+
+/* Table to convert tags to bit values, used for MSTRING type */
+static const unsigned long tag2bit[32] = {
+0,	0,	0,	B_ASN1_BIT_STRING,	/* tags  0 -  3 */
+B_ASN1_OCTET_STRING,	0,	0,		B_ASN1_UNKNOWN,/* tags  4- 7 */
+B_ASN1_UNKNOWN,	B_ASN1_UNKNOWN,	B_ASN1_UNKNOWN,	B_ASN1_UNKNOWN,/* tags  8-11 */
+B_ASN1_UTF8STRING,B_ASN1_UNKNOWN,B_ASN1_UNKNOWN,B_ASN1_UNKNOWN,/* tags 12-15 */
+B_ASN1_SEQUENCE,0,B_ASN1_NUMERICSTRING,B_ASN1_PRINTABLESTRING, /* tags 16-19 */
+B_ASN1_T61STRING,B_ASN1_VIDEOTEXSTRING,B_ASN1_IA5STRING,       /* tags 20-22 */
+B_ASN1_UTCTIME, B_ASN1_GENERALIZEDTIME,			       /* tags 23-24 */	
+B_ASN1_GRAPHICSTRING,B_ASN1_ISO64STRING,B_ASN1_GENERALSTRING,  /* tags 25-27 */
+B_ASN1_UNIVERSALSTRING,B_ASN1_UNKNOWN,B_ASN1_BMPSTRING,B_ASN1_UNKNOWN, /* tags 28-31 */
+	};
+
+unsigned long ASN1_tag2bit(int tag)
+	{
+	if ((tag < 0) || (tag > 30)) return 0;
+	return tag2bit[tag];
+	}
+
+/* Macro to initialize and invalidate the cache */
+
+#define asn1_tlc_clear(c)	if (c) (c)->valid = 0
+/* Version to avoid compiler warning about 'c' always non-NULL */
+#define asn1_tlc_clear_nc(c)	(c)->valid = 0
+
+/* Decode an ASN1 item, this currently behaves just 
+ * like a standard 'd2i' function. 'in' points to 
+ * a buffer to read the data from, in future we will
+ * have more advanced versions that can input data
+ * a piece at a time and this will simply be a special
+ * case.
+ */
+
+ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval,
+		const unsigned char **in, long len, const ASN1_ITEM *it)
+	{
+	ASN1_TLC c;
+	ASN1_VALUE *ptmpval = NULL;
+	if (!pval)
+		pval = &ptmpval;
+	asn1_tlc_clear_nc(&c);
+	if (ASN1_item_ex_d2i(pval, in, len, it, -1, 0, 0, &c) > 0) 
+		return *pval;
+	return NULL;
+	}
+
+int ASN1_template_d2i(ASN1_VALUE **pval,
+		const unsigned char **in, long len, const ASN1_TEMPLATE *tt)
+	{
+	ASN1_TLC c;
+	asn1_tlc_clear_nc(&c);
+	return asn1_template_ex_d2i(pval, in, len, tt, 0, &c);
+	}
+
+
+/* Decode an item, taking care of IMPLICIT tagging, if any.
+ * If 'opt' set and tag mismatch return -1 to handle OPTIONAL
+ */
+
+int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
+			const ASN1_ITEM *it,
+			int tag, int aclass, char opt, ASN1_TLC *ctx)
+	{
+	const ASN1_TEMPLATE *tt, *errtt = NULL;
+	const ASN1_COMPAT_FUNCS *cf;
+	const ASN1_EXTERN_FUNCS *ef;
+	const ASN1_AUX *aux = it->funcs;
+	ASN1_aux_cb *asn1_cb;
+	const unsigned char *p = NULL, *q;
+	unsigned char *wp=NULL;	/* BIG FAT WARNING!  BREAKS CONST WHERE USED */
+	unsigned char imphack = 0, oclass;
+	char seq_eoc, seq_nolen, cst, isopt;
+	long tmplen;
+	int i;
+	int otag;
+	int ret = 0;
+	ASN1_VALUE **pchptr, *ptmpval;
+	if (!pval)
+		return 0;
+	if (aux && aux->asn1_cb)
+		asn1_cb = aux->asn1_cb;
+	else asn1_cb = 0;
+
+	switch(it->itype)
+		{
+		case ASN1_ITYPE_PRIMITIVE:
+		if (it->templates)
+			{
+			/* tagging or OPTIONAL is currently illegal on an item
+			 * template because the flags can't get passed down.
+			 * In practice this isn't a problem: we include the
+			 * relevant flags from the item template in the
+			 * template itself.
+			 */
+			if ((tag != -1) || opt)
+				{
+				OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE);
+				goto err;
+				}
+			return asn1_template_ex_d2i(pval, in, len,
+					it->templates, opt, ctx);
+		}
+		return asn1_d2i_ex_primitive(pval, in, len, it,
+						tag, aclass, opt, ctx);
+		break;
+
+		case ASN1_ITYPE_MSTRING:
+		p = *in;
+		/* Just read in tag and class */
+		ret = asn1_check_tlen(NULL, &otag, &oclass, NULL, NULL,
+						&p, len, -1, 0, 1, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+
+		/* Must be UNIVERSAL class */
+		if (oclass != V_ASN1_UNIVERSAL)
+			{
+			/* If OPTIONAL, assume this is OK */
+			if (opt) return -1;
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_MSTRING_NOT_UNIVERSAL);
+			goto err;
+			}
+		/* Check tag matches bit map */
+		if (!(ASN1_tag2bit(otag) & it->utype))
+			{
+			/* If OPTIONAL, assume this is OK */
+			if (opt)
+				return -1;
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_MSTRING_WRONG_TAG);
+			goto err;
+			}
+		return asn1_d2i_ex_primitive(pval, in, len,
+						it, otag, 0, 0, ctx);
+
+		case ASN1_ITYPE_EXTERN:
+		/* Use new style d2i */
+		ef = it->funcs;
+		return ef->asn1_ex_d2i(pval, in, len,
+						it, tag, aclass, opt, ctx);
+
+		case ASN1_ITYPE_COMPAT:
+		/* we must resort to old style evil hackery */
+		cf = it->funcs;
+
+		/* If OPTIONAL see if it is there */
+		if (opt)
+			{
+			int exptag;
+			p = *in;
+			if (tag == -1)
+				exptag = it->utype;
+			else exptag = tag;
+			/* Don't care about anything other than presence
+			 * of expected tag */
+
+			ret = asn1_check_tlen(NULL, NULL, NULL, NULL, NULL,
+					&p, len, exptag, aclass, 1, ctx);
+			if (!ret)
+				{
+				OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+				goto err;
+				}
+			if (ret == -1)
+				return -1;
+			}
+
+		/* This is the old style evil hack IMPLICIT handling:
+		 * since the underlying code is expecting a tag and
+		 * class other than the one present we change the
+		 * buffer temporarily then change it back afterwards.
+		 * This doesn't and never did work for tags > 30.
+		 *
+		 * Yes this is *horrible* but it is only needed for
+		 * old style d2i which will hopefully not be around
+		 * for much longer.
+		 * FIXME: should copy the buffer then modify it so
+		 * the input buffer can be const: we should *always*
+		 * copy because the old style d2i might modify the
+		 * buffer.
+		 */
+
+		if (tag != -1)
+			{
+			wp = *(unsigned char **)in;
+			imphack = *wp;
+			if (p == NULL)
+				{
+				OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+				goto err;
+				}
+			*wp = (unsigned char)((*p & V_ASN1_CONSTRUCTED)
+								| it->utype);
+			}
+
+		ptmpval = cf->asn1_d2i(pval, in, len);
+
+		if (tag != -1)
+			*wp = imphack;
+
+		if (ptmpval)
+			return 1;
+
+		OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+		goto err;
+
+
+		case ASN1_ITYPE_CHOICE:
+		if (asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it, NULL))
+				goto auxerr;
+
+		/* Allocate structure */
+		if (!*pval && !ASN1_item_ex_new(pval, it))
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+		/* CHOICE type, try each possibility in turn */
+		p = *in;
+		for (i = 0, tt=it->templates; i < it->tcount; i++, tt++)
+			{
+			pchptr = asn1_get_field_ptr(pval, tt);
+			/* We mark field as OPTIONAL so its absence
+			 * can be recognised.
+			 */
+			ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx);
+			/* If field not present, try the next one */
+			if (ret == -1)
+				continue;
+			/* If positive return, read OK, break loop */
+			if (ret > 0)
+				break;
+			/* Otherwise must be an ASN1 parsing error */
+			errtt = tt;
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+
+		/* Did we fall off the end without reading anything? */
+		if (i == it->tcount)
+			{
+			/* If OPTIONAL, this is OK */
+			if (opt)
+				{
+				/* Free and zero it */
+				ASN1_item_ex_free(pval, it);
+				return -1;
+				}
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NO_MATCHING_CHOICE_TYPE);
+			goto err;
+			}
+
+		asn1_set_choice_selector(pval, i, it);
+		*in = p;
+		if (asn1_cb && !asn1_cb(ASN1_OP_D2I_POST, pval, it, NULL))
+				goto auxerr;
+		return 1;
+
+		case ASN1_ITYPE_NDEF_SEQUENCE:
+		case ASN1_ITYPE_SEQUENCE:
+		p = *in;
+		tmplen = len;
+
+		/* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
+		if (tag == -1)
+			{
+			tag = V_ASN1_SEQUENCE;
+			aclass = V_ASN1_UNIVERSAL;
+			}
+		/* Get SEQUENCE length and update len, p */
+		ret = asn1_check_tlen(&len, NULL, NULL, &seq_eoc, &cst,
+					&p, len, tag, aclass, opt, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+		else if (ret == -1)
+			return -1;
+		if (aux && (aux->flags & ASN1_AFLG_BROKEN))
+			{
+			len = tmplen - (p - *in);
+			seq_nolen = 1;
+			}
+		/* If indefinite we don't do a length check */
+		else seq_nolen = seq_eoc;
+		if (!cst)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_SEQUENCE_NOT_CONSTRUCTED);
+			goto err;
+			}
+
+		if (!*pval && !ASN1_item_ex_new(pval, it))
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+
+		if (asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it, NULL))
+				goto auxerr;
+
+		/* Get each field entry */
+		for (i = 0, tt = it->templates; i < it->tcount; i++, tt++)
+			{
+			const ASN1_TEMPLATE *seqtt;
+			ASN1_VALUE **pseqval;
+			seqtt = asn1_do_adb(pval, tt, 1);
+			if (!seqtt)
+				goto err;
+			pseqval = asn1_get_field_ptr(pval, seqtt);
+			/* Have we ran out of data? */
+			if (!len)
+				break;
+			q = p;
+			if (asn1_check_eoc(&p, len))
+				{
+				if (!seq_eoc)
+					{
+					OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_UNEXPECTED_EOC);
+					goto err;
+					}
+				len -= p - q;
+				seq_eoc = 0;
+				q = p;
+				break;
+				}
+			/* This determines the OPTIONAL flag value. The field
+			 * cannot be omitted if it is the last of a SEQUENCE
+			 * and there is still data to be read. This isn't
+			 * strictly necessary but it increases efficiency in
+			 * some cases.
+			 */
+			if (i == (it->tcount - 1))
+				isopt = 0;
+			else isopt = (char)(seqtt->flags & ASN1_TFLG_OPTIONAL);
+			/* attempt to read in field, allowing each to be
+			 * OPTIONAL */
+
+			ret = asn1_template_ex_d2i(pseqval, &p, len,
+							seqtt, isopt, ctx);
+			if (!ret)
+				{
+				errtt = seqtt;
+				goto err;
+				}
+			else if (ret == -1)
+				{
+				/* OPTIONAL component absent.
+				 * Free and zero the field.
+				 */
+				ASN1_template_free(pseqval, seqtt);
+				continue;
+				}
+			/* Update length */
+			len -= p - q;
+			}
+
+		/* Check for EOC if expecting one */
+		if (seq_eoc && !asn1_check_eoc(&p, len))
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_MISSING_EOC);
+			goto err;
+			}
+		/* Check all data read */
+		if (!seq_nolen && len)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_SEQUENCE_LENGTH_MISMATCH);
+			goto err;
+			}
+
+		/* If we get here we've got no more data in the SEQUENCE,
+		 * however we may not have read all fields so check all
+		 * remaining are OPTIONAL and clear any that are.
+		 */
+		for (; i < it->tcount; tt++, i++)
+			{
+			const ASN1_TEMPLATE *seqtt;
+			seqtt = asn1_do_adb(pval, tt, 1);
+			if (!seqtt)
+				goto err;
+			if (seqtt->flags & ASN1_TFLG_OPTIONAL)
+				{
+				ASN1_VALUE **pseqval;
+				pseqval = asn1_get_field_ptr(pval, seqtt);
+				ASN1_template_free(pseqval, seqtt);
+				}
+			else
+				{
+				errtt = seqtt;
+				OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_FIELD_MISSING);
+				goto err;
+				}
+			}
+		/* Save encoding */
+		if (!asn1_enc_save(pval, *in, p - *in, it))
+			goto auxerr;
+		*in = p;
+		if (asn1_cb && !asn1_cb(ASN1_OP_D2I_POST, pval, it, NULL))
+				goto auxerr;
+		return 1;
+
+		default:
+		return 0;
+		}
+	auxerr:
+	OPENSSL_PUT_ERROR(ASN1, ASN1_item_ex_d2i,  ASN1_R_AUX_ERROR);
+	err:
+	ASN1_item_ex_free(pval, it);
+	if (errtt)
+		ERR_add_error_data(4, "Field=", errtt->field_name,
+					", Type=", it->sname);
+	else
+		ERR_add_error_data(2, "Type=", it->sname);
+	return 0;
+	}
+
+/* Templates are handled with two separate functions.
+ * One handles any EXPLICIT tag and the other handles the rest.
+ */
+
+static int asn1_template_ex_d2i(ASN1_VALUE **val,
+				const unsigned char **in, long inlen,
+				const ASN1_TEMPLATE *tt, char opt,
+							ASN1_TLC *ctx)
+	{
+	int flags, aclass;
+	int ret;
+	long len;
+	const unsigned char *p, *q;
+	char exp_eoc;
+	if (!val)
+		return 0;
+	flags = tt->flags;
+	aclass = flags & ASN1_TFLG_TAG_CLASS;
+
+	p = *in;
+
+	/* Check if EXPLICIT tag expected */
+	if (flags & ASN1_TFLG_EXPTAG)
+		{
+		char cst;
+		/* Need to work out amount of data available to the inner
+		 * content and where it starts: so read in EXPLICIT header to
+		 * get the info.
+		 */
+		ret = asn1_check_tlen(&len, NULL, NULL, &exp_eoc, &cst,
+					&p, inlen, tt->tag, aclass, opt, ctx);
+		q = p;
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			return 0;
+			}
+		else if (ret == -1)
+			return -1;
+		if (!cst)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_ex_d2i,  ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED);
+			return 0;
+			}
+		/* We've found the field so it can't be OPTIONAL now */
+		ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_ex_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			return 0;
+			}
+		/* We read the field in OK so update length */
+		len -= p - q;
+		if (exp_eoc)
+			{
+			/* If NDEF we must have an EOC here */
+			if (!asn1_check_eoc(&p, len))
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_template_ex_d2i,  ASN1_R_MISSING_EOC);
+				goto err;
+				}
+			}
+		else
+			{
+			/* Otherwise we must hit the EXPLICIT tag end or its
+			 * an error */
+			if (len)
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_template_ex_d2i,  ASN1_R_EXPLICIT_LENGTH_MISMATCH);
+				goto err;
+				}
+			}
+		}
+		else 
+			return asn1_template_noexp_d2i(val, in, inlen,
+								tt, opt, ctx);
+
+	*in = p;
+	return 1;
+
+	err:
+	ASN1_template_free(val, tt);
+	return 0;
+	}
+
+static int asn1_template_noexp_d2i(ASN1_VALUE **val,
+				const unsigned char **in, long len,
+				const ASN1_TEMPLATE *tt, char opt,
+				ASN1_TLC *ctx)
+	{
+	int flags, aclass;
+	int ret;
+	const unsigned char *p, *q;
+	if (!val)
+		return 0;
+	flags = tt->flags;
+	aclass = flags & ASN1_TFLG_TAG_CLASS;
+
+	p = *in;
+	q = p;
+
+	if (flags & ASN1_TFLG_SK_MASK)
+		{
+		/* SET OF, SEQUENCE OF */
+		int sktag, skaclass;
+		char sk_eoc;
+		/* First work out expected inner tag value */
+		if (flags & ASN1_TFLG_IMPTAG)
+			{
+			sktag = tt->tag;
+			skaclass = aclass;
+			}
+		else
+			{
+			skaclass = V_ASN1_UNIVERSAL;
+			if (flags & ASN1_TFLG_SET_OF)
+				sktag = V_ASN1_SET;
+			else
+				sktag = V_ASN1_SEQUENCE;
+			}
+		/* Get the tag */
+		ret = asn1_check_tlen(&len, NULL, NULL, &sk_eoc, NULL,
+					&p, len, sktag, skaclass, opt, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			return 0;
+			}
+		else if (ret == -1)
+			return -1;
+		if (!*val)
+			*val = (ASN1_VALUE *)sk_new_null();
+		else
+			{
+			/* We've got a valid STACK: free up any items present */
+			STACK_OF(ASN1_VALUE) *sktmp
+			    = (STACK_OF(ASN1_VALUE) *)*val;
+			ASN1_VALUE *vtmp;
+			while(sk_ASN1_VALUE_num(sktmp) > 0)
+				{
+				vtmp = sk_ASN1_VALUE_pop(sktmp);
+				ASN1_item_ex_free(&vtmp,
+						ASN1_ITEM_ptr(tt->item));
+				}
+			}
+				
+		if (!*val)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+
+		/* Read as many items as we can */
+		while(len > 0)
+			{
+			ASN1_VALUE *skfield;
+			q = p;
+			/* See if EOC found */
+			if (asn1_check_eoc(&p, len))
+				{
+				if (!sk_eoc)
+					{
+					OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ASN1_R_UNEXPECTED_EOC);
+					goto err;
+					}
+				len -= p - q;
+				sk_eoc = 0;
+				break;
+				}
+			skfield = NULL;
+			if (!ASN1_item_ex_d2i(&skfield, &p, len,
+						ASN1_ITEM_ptr(tt->item),
+						-1, 0, 0, ctx))
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+				goto err;
+				}
+			len -= p - q;
+			if (!sk_ASN1_VALUE_push((STACK_OF(ASN1_VALUE) *)*val,
+						skfield))
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+			}
+		if (sk_eoc)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ASN1_R_MISSING_EOC);
+			goto err;
+			}
+		}
+	else if (flags & ASN1_TFLG_IMPTAG)
+		{
+		/* IMPLICIT tagging */
+		ret = ASN1_item_ex_d2i(val, &p, len,
+			ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+		else if (ret == -1)
+			return -1;
+		}
+	else
+		{
+		/* Nothing special */
+		ret = ASN1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
+							-1, 0, opt, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_template_noexp_d2i,  ASN1_R_NESTED_ASN1_ERROR);
+			goto err;
+			}
+		else if (ret == -1)
+			return -1;
+		}
+
+	*in = p;
+	return 1;
+
+	err:
+	ASN1_template_free(val, tt);
+	return 0;
+	}
+
+static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
+				const unsigned char **in, long inlen, 
+				const ASN1_ITEM *it,
+				int tag, int aclass, char opt, ASN1_TLC *ctx)
+	{
+	int ret = 0, utype;
+	long plen;
+	char cst, inf, free_cont = 0;
+	const unsigned char *p;
+	BUF_MEM buf;
+	const unsigned char *cont = NULL;
+	long len; 
+	if (!pval)
+		{
+		OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ASN1_R_ILLEGAL_NULL);
+		return 0; /* Should never happen */
+		}
+
+	if (it->itype == ASN1_ITYPE_MSTRING)
+		{
+		utype = tag;
+		tag = -1;
+		}
+	else
+		utype = it->utype;
+
+	if (utype == V_ASN1_ANY)
+		{
+		/* If type is ANY need to figure out type from tag */
+		unsigned char oclass;
+		if (tag >= 0)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ASN1_R_ILLEGAL_TAGGED_ANY);
+			return 0;
+			}
+		if (opt)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ASN1_R_ILLEGAL_OPTIONAL_ANY);
+			return 0;
+			}
+		p = *in;
+		ret = asn1_check_tlen(NULL, &utype, &oclass, NULL, NULL,
+					&p, inlen, -1, 0, 0, ctx);
+		if (!ret)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ASN1_R_NESTED_ASN1_ERROR);
+			return 0;
+			}
+		if (oclass != V_ASN1_UNIVERSAL)
+			utype = V_ASN1_OTHER;
+		}
+	if (tag == -1)
+		{
+		tag = utype;
+		aclass = V_ASN1_UNIVERSAL;
+		}
+	p = *in;
+	/* Check header */
+	ret = asn1_check_tlen(&plen, NULL, NULL, &inf, &cst,
+				&p, inlen, tag, aclass, opt, ctx);
+	if (!ret)
+		{
+		OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ASN1_R_NESTED_ASN1_ERROR);
+		return 0;
+		}
+	else if (ret == -1)
+		return -1;
+        ret = 0;
+	/* SEQUENCE, SET and "OTHER" are left in encoded form */
+	if ((utype == V_ASN1_SEQUENCE)
+		|| (utype == V_ASN1_SET) || (utype == V_ASN1_OTHER))
+		{
+		/* Clear context cache for type OTHER because the auto clear
+		 * when we have a exact match wont work
+		 */
+		if (utype == V_ASN1_OTHER)
+			{
+			asn1_tlc_clear(ctx);
+			}
+		/* SEQUENCE and SET must be constructed */
+		else if (!cst)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ASN1_R_TYPE_NOT_CONSTRUCTED);
+			return 0;
+			}
+
+		cont = *in;
+		/* If indefinite length constructed find the real end */
+		if (inf)
+			{
+			if (!asn1_find_end(&p, plen, inf))
+				 goto err;
+			len = p - cont;
+			}
+		else
+			{
+			len = p - cont + plen;
+			p += plen;
+			buf.data = NULL;
+			}
+		}
+	else if (cst)
+		{
+		buf.length = 0;
+		buf.max = 0;
+		buf.data = NULL;
+		/* Should really check the internal tags are correct but
+		 * some things may get this wrong. The relevant specs
+		 * say that constructed string types should be OCTET STRINGs
+		 * internally irrespective of the type. So instead just check
+		 * for UNIVERSAL class and ignore the tag.
+		 */
+		if (!asn1_collect(&buf, &p, plen, inf, -1, V_ASN1_UNIVERSAL, 0))
+			{
+			free_cont = 1;
+			goto err;
+			}
+		len = buf.length;
+		/* Append a final null to string */
+		if (!BUF_MEM_grow_clean(&buf, len + 1))
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_d2i_ex_primitive,  ERR_R_MALLOC_FAILURE);
+			return 0;
+			}
+		buf.data[len] = 0;
+		cont = (const unsigned char *)buf.data;
+		free_cont = 1;
+		}
+	else
+		{
+		cont = p;
+		len = plen;
+		p += plen;
+		}
+
+	/* We now have content length and type: translate into a structure */
+	if (!asn1_ex_c2i(pval, cont, len, utype, &free_cont, it))
+		goto err;
+
+	*in = p;
+	ret = 1;
+	err:
+	if (free_cont && buf.data) OPENSSL_free(buf.data);
+	return ret;
+	}
+
+/* Translate ASN1 content octets into a structure */
+
+int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+			int utype, char *free_cont, const ASN1_ITEM *it)
+	{
+	ASN1_VALUE **opval = NULL;
+	ASN1_STRING *stmp;
+	ASN1_TYPE *typ = NULL;
+	int ret = 0;
+	const ASN1_PRIMITIVE_FUNCS *pf;
+	ASN1_INTEGER **tint;
+	pf = it->funcs;
+
+	if (pf && pf->prim_c2i)
+		return pf->prim_c2i(pval, cont, len, utype, free_cont, it);
+	/* If ANY type clear type and set pointer to internal value */
+	if (it->utype == V_ASN1_ANY)
+		{
+		if (!*pval)
+			{
+			typ = ASN1_TYPE_new();
+			if (typ == NULL)
+				goto err;
+			*pval = (ASN1_VALUE *)typ;
+			}
+		else
+			typ = (ASN1_TYPE *)*pval;
+
+		if (utype != typ->type)
+			ASN1_TYPE_set(typ, utype, NULL);
+		opval = pval;
+		pval = &typ->value.asn1_value;
+		}
+	switch(utype)
+		{
+		case V_ASN1_OBJECT:
+		if (!c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len))
+			goto err;
+		break;
+
+		case V_ASN1_NULL:
+		if (len)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_ex_c2i,  ASN1_R_NULL_IS_WRONG_LENGTH);
+			goto err;
+			}
+		*pval = (ASN1_VALUE *)1;
+		break;
+
+		case V_ASN1_BOOLEAN:
+		if (len != 1)
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_ex_c2i,  ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
+			goto err;
+			}
+		else
+			{
+			ASN1_BOOLEAN *tbool;
+			tbool = (ASN1_BOOLEAN *)pval;
+			*tbool = *cont;
+			}
+		break;
+
+		case V_ASN1_BIT_STRING:
+		if (!c2i_ASN1_BIT_STRING((ASN1_BIT_STRING **)pval, &cont, len))
+			goto err;
+		break;
+
+		case V_ASN1_INTEGER:
+		case V_ASN1_NEG_INTEGER:
+		case V_ASN1_ENUMERATED:
+		case V_ASN1_NEG_ENUMERATED:
+		tint = (ASN1_INTEGER **)pval;
+		if (!c2i_ASN1_INTEGER(tint, &cont, len))
+			goto err;
+		/* Fixup type to match the expected form */
+		(*tint)->type = utype | ((*tint)->type & V_ASN1_NEG);
+		break;
+
+		case V_ASN1_OCTET_STRING:
+		case V_ASN1_NUMERICSTRING:
+		case V_ASN1_PRINTABLESTRING:
+		case V_ASN1_T61STRING:
+		case V_ASN1_VIDEOTEXSTRING:
+		case V_ASN1_IA5STRING:
+		case V_ASN1_UTCTIME:
+		case V_ASN1_GENERALIZEDTIME:
+		case V_ASN1_GRAPHICSTRING:
+		case V_ASN1_VISIBLESTRING:
+		case V_ASN1_GENERALSTRING:
+		case V_ASN1_UNIVERSALSTRING:
+		case V_ASN1_BMPSTRING:
+		case V_ASN1_UTF8STRING:
+		case V_ASN1_OTHER:
+		case V_ASN1_SET:
+		case V_ASN1_SEQUENCE:
+		default:
+		if (utype == V_ASN1_BMPSTRING && (len & 1))
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_ex_c2i,  ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
+			goto err;
+			}
+		if (utype == V_ASN1_UNIVERSALSTRING && (len & 3))
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_ex_c2i,  ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH);
+			goto err;
+			}
+		/* All based on ASN1_STRING and handled the same */
+		if (!*pval)
+			{
+			stmp = ASN1_STRING_type_new(utype);
+			if (!stmp)
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_ex_c2i,  ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+			*pval = (ASN1_VALUE *)stmp;
+			}
+		else
+			{
+			stmp = (ASN1_STRING *)*pval;
+			stmp->type = utype;
+			}
+		/* If we've already allocated a buffer use it */
+		if (*free_cont)
+			{
+			if (stmp->data)
+				OPENSSL_free(stmp->data);
+			stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */
+			stmp->length = len;
+			*free_cont = 0;
+			}
+		else
+			{
+			if (!ASN1_STRING_set(stmp, cont, len))
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_ex_c2i,  ERR_R_MALLOC_FAILURE);
+				ASN1_STRING_free(stmp);	
+				*pval = NULL;
+				goto err;
+				}
+			}
+		break;
+		}
+	/* If ASN1_ANY and NULL type fix up value */
+	if (typ && (utype == V_ASN1_NULL))
+		 typ->value.ptr = NULL;
+
+	ret = 1;
+	err:
+	if (!ret)
+		{
+		ASN1_TYPE_free(typ);
+		if (opval)
+			*opval = NULL;
+		}
+	return ret;
+	}
+
+
+/* This function finds the end of an ASN1 structure when passed its maximum
+ * length, whether it is indefinite length and a pointer to the content.
+ * This is more efficient than calling asn1_collect because it does not
+ * recurse on each indefinite length header.
+ */
+
+static int asn1_find_end(const unsigned char **in, long len, char inf)
+	{
+	int expected_eoc;
+	long plen;
+	const unsigned char *p = *in, *q;
+	/* If not indefinite length constructed just add length */
+	if (inf == 0)
+		{
+		*in += len;
+		return 1;
+		}
+	expected_eoc = 1;
+	/* Indefinite length constructed form. Find the end when enough EOCs
+	 * are found. If more indefinite length constructed headers
+	 * are encountered increment the expected eoc count otherwise just
+	 * skip to the end of the data.
+	 */
+	while (len > 0)
+		{
+		if(asn1_check_eoc(&p, len))
+			{
+			expected_eoc--;
+			if (expected_eoc == 0)
+				break;
+			len -= 2;
+			continue;
+			}
+		q = p;
+		/* Just read in a header: only care about the length */
+		if(!asn1_check_tlen(&plen, NULL, NULL, &inf, NULL, &p, len,
+				-1, 0, 0, NULL))
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_find_end,  ASN1_R_NESTED_ASN1_ERROR);
+			return 0;
+			}
+		if (inf)
+			expected_eoc++;
+		else
+			p += plen;
+		len -= p - q;
+		}
+	if (expected_eoc)
+		{
+		OPENSSL_PUT_ERROR(ASN1, asn1_find_end,  ASN1_R_MISSING_EOC);
+		return 0;
+		}
+	*in = p;
+	return 1;
+	}
+/* This function collects the asn1 data from a constructred string
+ * type into a buffer. The values of 'in' and 'len' should refer
+ * to the contents of the constructed type and 'inf' should be set
+ * if it is indefinite length.
+ */
+
+#ifndef ASN1_MAX_STRING_NEST
+/* This determines how many levels of recursion are permitted in ASN1
+ * string types. If it is not limited stack overflows can occur. If set
+ * to zero no recursion is allowed at all. Although zero should be adequate
+ * examples exist that require a value of 1. So 5 should be more than enough.
+ */
+#define ASN1_MAX_STRING_NEST 5
+#endif
+
+
+static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len,
+			char inf, int tag, int aclass, int depth)
+	{
+	const unsigned char *p, *q;
+	long plen;
+	char cst, ininf;
+	p = *in;
+	inf &= 1;
+	/* If no buffer and not indefinite length constructed just pass over
+	 * the encoded data */
+	if (!buf && !inf)
+		{
+		*in += len;
+		return 1;
+		}
+	while(len > 0)
+		{
+		q = p;
+		/* Check for EOC */
+		if (asn1_check_eoc(&p, len))
+			{
+			/* EOC is illegal outside indefinite length
+			 * constructed form */
+			if (!inf)
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_collect,  ASN1_R_UNEXPECTED_EOC);
+				return 0;
+				}
+			inf = 0;
+			break;
+			}
+
+		if (!asn1_check_tlen(&plen, NULL, NULL, &ininf, &cst, &p,
+					len, tag, aclass, 0, NULL))
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_collect,  ASN1_R_NESTED_ASN1_ERROR);
+			return 0;
+			}
+
+		/* If indefinite length constructed update max length */
+		if (cst)
+			{
+			if (depth >= ASN1_MAX_STRING_NEST)
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_collect,  ASN1_R_NESTED_ASN1_STRING);
+				return 0;
+				}
+			if (!asn1_collect(buf, &p, plen, ininf, tag, aclass,
+						depth + 1))
+				return 0;
+			}
+		else if (plen && !collect_data(buf, &p, plen))
+			return 0;
+		len -= p - q;
+		}
+	if (inf)
+		{
+		OPENSSL_PUT_ERROR(ASN1, asn1_collect,  ASN1_R_MISSING_EOC);
+		return 0;
+		}
+	*in = p;
+	return 1;
+	}
+
+static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen)
+	{
+	int len;
+	if (buf)
+		{
+		len = buf->length;
+		if (!BUF_MEM_grow_clean(buf, len + plen))
+			{
+			OPENSSL_PUT_ERROR(ASN1, asn1_collect,  ERR_R_MALLOC_FAILURE);
+			return 0;
+			}
+		memcpy(buf->data + len, *p, plen);
+		}
+	*p += plen;
+	return 1;
+	}
+
+/* Check for ASN1 EOC and swallow it if found */
+
+static int asn1_check_eoc(const unsigned char **in, long len)
+	{
+	const unsigned char *p;
+	if (len < 2) return 0;
+	p = *in;
+	if (!p[0] && !p[1])
+		{
+		*in += 2;
+		return 1;
+		}
+	return 0;
+	}
+
+/* Check an ASN1 tag and length: a bit like ASN1_get_object
+ * but it sets the length for indefinite length constructed
+ * form, we don't know the exact length but we can set an
+ * upper bound to the amount of data available minus the
+ * header length just read.
+ */
+
+static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
+				char *inf, char *cst,
+				const unsigned char **in, long len,
+				int exptag, int expclass, char opt,
+				ASN1_TLC *ctx)
+	{
+	int i;
+	int ptag, pclass;
+	long plen;
+	const unsigned char *p, *q;
+	p = *in;
+	q = p;
+
+	if (ctx && ctx->valid)
+		{
+		i = ctx->ret;
+		plen = ctx->plen;
+		pclass = ctx->pclass;
+		ptag = ctx->ptag;
+		p += ctx->hdrlen;
+		}
+	else
+		{
+		i = ASN1_get_object(&p, &plen, &ptag, &pclass, len);
+		if (ctx)
+			{
+			ctx->ret = i;
+			ctx->plen = plen;
+			ctx->pclass = pclass;
+			ctx->ptag = ptag;
+			ctx->hdrlen = p - q;
+			ctx->valid = 1;
+			/* If definite length, and no error, length +
+			 * header can't exceed total amount of data available. 
+			 */
+			if (!(i & 0x81) && ((plen + ctx->hdrlen) > len))
+				{
+				OPENSSL_PUT_ERROR(ASN1, asn1_check_tlen,  ASN1_R_TOO_LONG);
+				asn1_tlc_clear(ctx);
+				return 0;
+				}
+			}
+		}
+
+	if (i & 0x80)
+		{
+		OPENSSL_PUT_ERROR(ASN1, asn1_check_tlen,  ASN1_R_BAD_OBJECT_HEADER);
+		asn1_tlc_clear(ctx);
+		return 0;
+		}
+	if (exptag >= 0)
+		{
+		if ((exptag != ptag) || (expclass != pclass))
+			{
+			/* If type is OPTIONAL, not an error:
+			 * indicate missing type.
+			 */
+			if (opt) return -1;
+			asn1_tlc_clear(ctx);
+			OPENSSL_PUT_ERROR(ASN1, asn1_check_tlen,  ASN1_R_WRONG_TAG);
+			return 0;
+			}
+		/* We have a tag and class match:
+		 * assume we are going to do something with it */
+		asn1_tlc_clear(ctx);
+		}
+
+	if (i & 1)
+		plen = len - (p - q);
+
+	if (inf)
+		*inf = i & 1;
+
+	if (cst)
+		*cst = i & V_ASN1_CONSTRUCTED;
+
+	if (olen)
+		*olen = plen;
+
+	if (oclass)
+		*oclass = pclass;
+
+	if (otag)
+		*otag = ptag;
+
+	*in = p;
+	return 1;
+	}
diff --git a/crypto/asn1/tasn_enc.c b/crypto/asn1/tasn_enc.c
new file mode 100644
index 0000000..5912e01
--- /dev/null
+++ b/crypto/asn1/tasn_enc.c
@@ -0,0 +1,688 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/mem.h>
+
+
+static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
+					const ASN1_ITEM *it,
+					int tag, int aclass);
+static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
+					int skcontlen, const ASN1_ITEM *item,
+					int do_sort, int iclass);
+static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
+					const ASN1_TEMPLATE *tt,
+					int tag, int aclass);
+static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
+					const ASN1_ITEM *it, int flags);
+
+/* Top level i2d equivalents: the 'ndef' variant instructs the encoder
+ * to use indefinite length constructed encoding, where appropriate
+ */
+
+int ASN1_item_ndef_i2d(ASN1_VALUE *val, unsigned char **out,
+						const ASN1_ITEM *it)
+	{
+	return asn1_item_flags_i2d(val, out, it, ASN1_TFLG_NDEF);
+	}
+
+int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it)
+	{
+	return asn1_item_flags_i2d(val, out, it, 0);
+	}
+
+/* Encode an ASN1 item, this is use by the
+ * standard 'i2d' function. 'out' points to 
+ * a buffer to output the data to.
+ *
+ * The new i2d has one additional feature. If the output
+ * buffer is NULL (i.e. *out == NULL) then a buffer is
+ * allocated and populated with the encoding.
+ */
+
+static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
+					const ASN1_ITEM *it, int flags)
+	{
+	if (out && !*out)
+		{
+		unsigned char *p, *buf;
+		int len;
+		len = ASN1_item_ex_i2d(&val, NULL, it, -1, flags);
+		if (len <= 0)
+			return len;
+		buf = OPENSSL_malloc(len);
+		if (!buf)
+			return -1;
+		p = buf;
+		ASN1_item_ex_i2d(&val, &p, it, -1, flags);
+		*out = buf;
+		return len;
+		}
+
+	return ASN1_item_ex_i2d(&val, out, it, -1, flags);
+	}
+
+/* Encode an item, taking care of IMPLICIT tagging (if any).
+ * This function performs the normal item handling: it can be
+ * used in external types.
+ */
+
+int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
+			const ASN1_ITEM *it, int tag, int aclass)
+	{
+	const ASN1_TEMPLATE *tt = NULL;
+	unsigned char *p = NULL;
+	int i, seqcontlen, seqlen, ndef = 1;
+	const ASN1_COMPAT_FUNCS *cf;
+	const ASN1_EXTERN_FUNCS *ef;
+	const ASN1_AUX *aux = it->funcs;
+	ASN1_aux_cb *asn1_cb = 0;
+
+	if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval)
+		return 0;
+
+	if (aux && aux->asn1_cb)
+		 asn1_cb = aux->asn1_cb;
+
+	switch(it->itype)
+		{
+
+		case ASN1_ITYPE_PRIMITIVE:
+		if (it->templates)
+			return asn1_template_ex_i2d(pval, out, it->templates,
+								tag, aclass);
+		return asn1_i2d_ex_primitive(pval, out, it, tag, aclass);
+		break;
+
+		case ASN1_ITYPE_MSTRING:
+		return asn1_i2d_ex_primitive(pval, out, it, -1, aclass);
+
+		case ASN1_ITYPE_CHOICE:
+		if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL))
+				return 0;
+		i = asn1_get_choice_selector(pval, it);
+		if ((i >= 0) && (i < it->tcount))
+			{
+			ASN1_VALUE **pchval;
+			const ASN1_TEMPLATE *chtt;
+			chtt = it->templates + i;
+			pchval = asn1_get_field_ptr(pval, chtt);
+			return asn1_template_ex_i2d(pchval, out, chtt,
+								-1, aclass);
+			}
+		/* Fixme: error condition if selector out of range */
+		if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL))
+				return 0;
+		break;
+
+		case ASN1_ITYPE_EXTERN:
+		/* If new style i2d it does all the work */
+		ef = it->funcs;
+		return ef->asn1_ex_i2d(pval, out, it, tag, aclass);
+
+		case ASN1_ITYPE_COMPAT:
+		/* old style hackery... */
+		cf = it->funcs;
+		if (out)
+			p = *out;
+		i = cf->asn1_i2d(*pval, out);
+		/* Fixup for IMPLICIT tag: note this messes up for tags > 30,
+		 * but so did the old code. Tags > 30 are very rare anyway.
+		 */
+		if (out && (tag != -1))
+			*p = aclass | tag | (*p & V_ASN1_CONSTRUCTED);
+		return i;
+		
+		case ASN1_ITYPE_NDEF_SEQUENCE:
+		/* Use indefinite length constructed if requested */
+		if (aclass & ASN1_TFLG_NDEF) ndef = 2;
+		/* fall through */
+
+		case ASN1_ITYPE_SEQUENCE:
+		i = asn1_enc_restore(&seqcontlen, out, pval, it);
+		/* An error occurred */
+		if (i < 0)
+			return 0;
+		/* We have a valid cached encoding... */
+		if (i > 0)
+			return seqcontlen;
+		/* Otherwise carry on */
+		seqcontlen = 0;
+		/* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
+		if (tag == -1)
+			{
+			tag = V_ASN1_SEQUENCE;
+			/* Retain any other flags in aclass */
+			aclass = (aclass & ~ASN1_TFLG_TAG_CLASS)
+					| V_ASN1_UNIVERSAL;
+			}
+		if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL))
+				return 0;
+		/* First work out sequence content length */
+		for (i = 0, tt = it->templates; i < it->tcount; tt++, i++)
+			{
+			const ASN1_TEMPLATE *seqtt;
+			ASN1_VALUE **pseqval;
+			seqtt = asn1_do_adb(pval, tt, 1);
+			if (!seqtt)
+				return 0;
+			pseqval = asn1_get_field_ptr(pval, seqtt);
+			/* FIXME: check for errors in enhanced version */
+			seqcontlen += asn1_template_ex_i2d(pseqval, NULL, seqtt,
+								-1, aclass);
+			}
+
+		seqlen = ASN1_object_size(ndef, seqcontlen, tag);
+		if (!out)
+			return seqlen;
+		/* Output SEQUENCE header */
+		ASN1_put_object(out, ndef, seqcontlen, tag, aclass);
+		for (i = 0, tt = it->templates; i < it->tcount; tt++, i++)
+			{
+			const ASN1_TEMPLATE *seqtt;
+			ASN1_VALUE **pseqval;
+			seqtt = asn1_do_adb(pval, tt, 1);
+			if (!seqtt)
+				return 0;
+			pseqval = asn1_get_field_ptr(pval, seqtt);
+			/* FIXME: check for errors in enhanced version */
+			asn1_template_ex_i2d(pseqval, out, seqtt, -1, aclass);
+			}
+		if (ndef == 2)
+			ASN1_put_eoc(out);
+		if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL))
+				return 0;
+		return seqlen;
+
+		default:
+		return 0;
+
+		}
+	return 0;
+	}
+
+int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out,
+							const ASN1_TEMPLATE *tt)
+	{
+	return asn1_template_ex_i2d(pval, out, tt, -1, 0);
+	}
+
+static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
+				const ASN1_TEMPLATE *tt, int tag, int iclass)
+	{
+	int i, ret, flags, ttag, tclass, ndef;
+	size_t j;
+	flags = tt->flags;
+	/* Work out tag and class to use: tagging may come
+	 * either from the template or the arguments, not both
+	 * because this would create ambiguity. Additionally
+	 * the iclass argument may contain some additional flags
+	 * which should be noted and passed down to other levels.
+	 */
+	if (flags & ASN1_TFLG_TAG_MASK)
+		{
+		/* Error if argument and template tagging */
+		if (tag != -1)
+			/* FIXME: error code here */
+			return -1;
+		/* Get tagging from template */
+		ttag = tt->tag;
+		tclass = flags & ASN1_TFLG_TAG_CLASS;
+		}
+	else if (tag != -1)
+		{
+		/* No template tagging, get from arguments */
+		ttag = tag;
+		tclass = iclass & ASN1_TFLG_TAG_CLASS;
+		}
+	else
+		{
+		ttag = -1;
+		tclass = 0;
+		}
+	/* 
+	 * Remove any class mask from iflag.
+	 */
+	iclass &= ~ASN1_TFLG_TAG_CLASS;
+
+	/* At this point 'ttag' contains the outer tag to use,
+	 * 'tclass' is the class and iclass is any flags passed
+	 * to this function.
+	 */
+
+	/* if template and arguments require ndef, use it */
+	if ((flags & ASN1_TFLG_NDEF) && (iclass & ASN1_TFLG_NDEF))
+		ndef = 2;
+	else ndef = 1;
+
+	if (flags & ASN1_TFLG_SK_MASK)
+		{
+		/* SET OF, SEQUENCE OF */
+		STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval;
+		int isset, sktag, skaclass;
+		int skcontlen, sklen;
+		ASN1_VALUE *skitem;
+
+		if (!*pval)
+			return 0;
+
+		if (flags & ASN1_TFLG_SET_OF)
+			{
+			isset = 1;
+			/* 2 means we reorder */
+			if (flags & ASN1_TFLG_SEQUENCE_OF)
+				isset = 2;
+			}
+		else isset = 0;
+
+		/* Work out inner tag value: if EXPLICIT
+		 * or no tagging use underlying type.
+		 */
+		if ((ttag != -1) && !(flags & ASN1_TFLG_EXPTAG))
+			{
+			sktag = ttag;
+			skaclass = tclass;
+			}
+		else
+			{
+			skaclass = V_ASN1_UNIVERSAL;
+			if (isset)
+				sktag = V_ASN1_SET;
+			else sktag = V_ASN1_SEQUENCE;
+			}
+
+		/* Determine total length of items */
+		skcontlen = 0;
+		for (j = 0; j < sk_ASN1_VALUE_num(sk); j++)
+			{
+			skitem = sk_ASN1_VALUE_value(sk, j);
+			skcontlen += ASN1_item_ex_i2d(&skitem, NULL,
+						ASN1_ITEM_ptr(tt->item),
+							-1, iclass);
+			}
+		sklen = ASN1_object_size(ndef, skcontlen, sktag);
+		/* If EXPLICIT need length of surrounding tag */
+		if (flags & ASN1_TFLG_EXPTAG)
+			ret = ASN1_object_size(ndef, sklen, ttag);
+		else ret = sklen;
+
+		if (!out)
+			return ret;
+
+		/* Now encode this lot... */
+		/* EXPLICIT tag */
+		if (flags & ASN1_TFLG_EXPTAG)
+			ASN1_put_object(out, ndef, sklen, ttag, tclass);
+		/* SET or SEQUENCE and IMPLICIT tag */
+		ASN1_put_object(out, ndef, skcontlen, sktag, skaclass);
+		/* And the stuff itself */
+		asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item),
+								isset, iclass);
+		if (ndef == 2)
+			{
+			ASN1_put_eoc(out);
+			if (flags & ASN1_TFLG_EXPTAG)
+				ASN1_put_eoc(out);
+			}
+
+		return ret;
+		}
+
+	if (flags & ASN1_TFLG_EXPTAG)
+		{
+		/* EXPLICIT tagging */
+		/* Find length of tagged item */
+		i = ASN1_item_ex_i2d(pval, NULL, ASN1_ITEM_ptr(tt->item),
+								-1, iclass);
+		if (!i)
+			return 0;
+		/* Find length of EXPLICIT tag */
+		ret = ASN1_object_size(ndef, i, ttag);
+		if (out)
+			{
+			/* Output tag and item */
+			ASN1_put_object(out, ndef, i, ttag, tclass);
+			ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item),
+								-1, iclass);
+			if (ndef == 2)
+				ASN1_put_eoc(out);
+			}
+		return ret;
+		}
+
+	/* Either normal or IMPLICIT tagging: combine class and flags */
+	return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item),
+						ttag, tclass | iclass);
+
+}
+
+/* Temporary structure used to hold DER encoding of items for SET OF */
+
+typedef	struct {
+	unsigned char *data;
+	int length;
+	ASN1_VALUE *field;
+} DER_ENC;
+
+static int der_cmp(const void *a, const void *b)
+	{
+	const DER_ENC *d1 = a, *d2 = b;
+	int cmplen, i;
+	cmplen = (d1->length < d2->length) ? d1->length : d2->length;
+	i = memcmp(d1->data, d2->data, cmplen);
+	if (i)
+		return i;
+	return d1->length - d2->length;
+	}
+
+/* Output the content octets of SET OF or SEQUENCE OF */
+
+static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
+					int skcontlen, const ASN1_ITEM *item,
+					int do_sort, int iclass)
+	{
+	size_t i;
+	ASN1_VALUE *skitem;
+	unsigned char *tmpdat = NULL, *p = NULL;
+	DER_ENC *derlst = NULL, *tder;
+	if (do_sort)
+		 {
+		/* Don't need to sort less than 2 items */
+		if (sk_ASN1_VALUE_num(sk) < 2)
+			do_sort = 0;
+		else
+			{
+			derlst = OPENSSL_malloc(sk_ASN1_VALUE_num(sk)
+						* sizeof(*derlst));
+			tmpdat = OPENSSL_malloc(skcontlen);
+			if (!derlst || !tmpdat)
+				return 0;
+			}
+		}
+	/* If not sorting just output each item */
+	if (!do_sort)
+		{
+		for (i = 0; i < sk_ASN1_VALUE_num(sk); i++)
+			{
+			skitem = sk_ASN1_VALUE_value(sk, i);
+			ASN1_item_ex_i2d(&skitem, out, item, -1, iclass);
+			}
+		return 1;
+		}
+	p = tmpdat;
+
+	/* Doing sort: build up a list of each member's DER encoding */
+	for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++)
+		{
+		skitem = sk_ASN1_VALUE_value(sk, i);
+		tder->data = p;
+		tder->length = ASN1_item_ex_i2d(&skitem, &p, item, -1, iclass);
+		tder->field = skitem;
+		}
+
+	/* Now sort them */
+	qsort(derlst, sk_ASN1_VALUE_num(sk), sizeof(*derlst), der_cmp);
+	/* Output sorted DER encoding */	
+	p = *out;
+	for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++)
+		{
+		memcpy(p, tder->data, tder->length);
+		p += tder->length;
+		}
+	*out = p;
+	/* If do_sort is 2 then reorder the STACK */
+	if (do_sort == 2)
+		{
+		for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk);
+							i++, tder++)
+			(void)sk_ASN1_VALUE_set(sk, i, tder->field);
+		}
+	OPENSSL_free(derlst);
+	OPENSSL_free(tmpdat);
+	return 1;
+	}
+
+static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
+				const ASN1_ITEM *it, int tag, int aclass)
+	{
+	int len;
+	int utype;
+	int usetag;
+	int ndef = 0;
+
+	utype = it->utype;
+
+	/* Get length of content octets and maybe find
+	 * out the underlying type.
+	 */
+
+	len = asn1_ex_i2c(pval, NULL, &utype, it);
+
+	/* If SEQUENCE, SET or OTHER then header is
+	 * included in pseudo content octets so don't
+	 * include tag+length. We need to check here
+	 * because the call to asn1_ex_i2c() could change
+	 * utype.
+	 */
+	if ((utype == V_ASN1_SEQUENCE) || (utype == V_ASN1_SET) ||
+	   (utype == V_ASN1_OTHER))
+		usetag = 0;
+	else usetag = 1;
+
+	/* -1 means omit type */
+
+	if (len == -1)
+		return 0;
+
+	/* -2 return is special meaning use ndef */
+	if (len == -2)
+		{
+		ndef = 2;
+		len = 0;
+		}
+
+	/* If not implicitly tagged get tag from underlying type */
+	if (tag == -1) tag = utype;
+
+	/* Output tag+length followed by content octets */
+	if (out)
+		{
+		if (usetag)
+			ASN1_put_object(out, ndef, len, tag, aclass);
+		asn1_ex_i2c(pval, *out, &utype, it);
+		if (ndef)
+			ASN1_put_eoc(out);
+		else
+			*out += len;
+		}
+
+	if (usetag)
+		return ASN1_object_size(ndef, len, tag);
+	return len;
+	}
+
+/* Produce content octets from a structure */
+
+int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype,
+				const ASN1_ITEM *it)
+	{
+	ASN1_BOOLEAN *tbool = NULL;
+	ASN1_STRING *strtmp;
+	ASN1_OBJECT *otmp;
+	int utype;
+	const unsigned char *cont;
+	unsigned char c;
+	int len;
+	const ASN1_PRIMITIVE_FUNCS *pf;
+	pf = it->funcs;
+	if (pf && pf->prim_i2c)
+		return pf->prim_i2c(pval, cout, putype, it);
+
+	/* Should type be omitted? */
+	if ((it->itype != ASN1_ITYPE_PRIMITIVE)
+		|| (it->utype != V_ASN1_BOOLEAN))
+		{
+		if (!*pval) return -1;
+		}
+
+	if (it->itype == ASN1_ITYPE_MSTRING)
+		{
+		/* If MSTRING type set the underlying type */
+		strtmp = (ASN1_STRING *)*pval;
+		utype = strtmp->type;
+		*putype = utype;
+		}
+	else if (it->utype == V_ASN1_ANY)
+		{
+		/* If ANY set type and pointer to value */
+		ASN1_TYPE *typ;
+		typ = (ASN1_TYPE *)*pval;
+		utype = typ->type;
+		*putype = utype;
+		pval = &typ->value.asn1_value;
+		}
+	else utype = *putype;
+
+	switch(utype)
+		{
+		case V_ASN1_OBJECT:
+		otmp = (ASN1_OBJECT *)*pval;
+		cont = otmp->data;
+		len = otmp->length;
+		break;
+
+		case V_ASN1_NULL:
+		cont = NULL;
+		len = 0;
+		break;
+
+		case V_ASN1_BOOLEAN:
+		tbool = (ASN1_BOOLEAN *)pval;
+		if (*tbool == -1)
+			return -1;
+		if (it->utype != V_ASN1_ANY)
+			{
+			/* Default handling if value == size field then omit */
+			if (*tbool && (it->size > 0))
+				return -1;
+			if (!*tbool && !it->size)
+				return -1;
+			}
+		c = (unsigned char)*tbool;
+		cont = &c;
+		len = 1;
+		break;
+
+		case V_ASN1_BIT_STRING:
+		return i2c_ASN1_BIT_STRING((ASN1_BIT_STRING *)*pval,
+							cout ? &cout : NULL);
+		break;
+
+		case V_ASN1_INTEGER:
+		case V_ASN1_NEG_INTEGER:
+		case V_ASN1_ENUMERATED:
+		case V_ASN1_NEG_ENUMERATED:
+		/* These are all have the same content format
+		 * as ASN1_INTEGER
+		 */
+		return i2c_ASN1_INTEGER((ASN1_INTEGER *)*pval,
+							cout ? &cout : NULL);
+		break;
+
+		case V_ASN1_OCTET_STRING:
+		case V_ASN1_NUMERICSTRING:
+		case V_ASN1_PRINTABLESTRING:
+		case V_ASN1_T61STRING:
+		case V_ASN1_VIDEOTEXSTRING:
+		case V_ASN1_IA5STRING:
+		case V_ASN1_UTCTIME:
+		case V_ASN1_GENERALIZEDTIME:
+		case V_ASN1_GRAPHICSTRING:
+		case V_ASN1_VISIBLESTRING:
+		case V_ASN1_GENERALSTRING:
+		case V_ASN1_UNIVERSALSTRING:
+		case V_ASN1_BMPSTRING:
+		case V_ASN1_UTF8STRING:
+		case V_ASN1_SEQUENCE:
+		case V_ASN1_SET:
+		default:
+		/* All based on ASN1_STRING and handled the same */
+		strtmp = (ASN1_STRING *)*pval;
+		/* Special handling for NDEF */
+		if ((it->size == ASN1_TFLG_NDEF)
+			&& (strtmp->flags & ASN1_STRING_FLAG_NDEF))
+			{
+			if (cout)
+				{
+				strtmp->data = cout;
+				strtmp->length = 0;
+				}
+			/* Special return code */
+			return -2;
+			}
+		cont = strtmp->data;
+		len = strtmp->length;
+
+		break;
+
+		}
+	if (cout && len)
+		memcpy(cout, cont, len);
+	return len;
+	}
diff --git a/crypto/asn1/tasn_fre.c b/crypto/asn1/tasn_fre.c
new file mode 100644
index 0000000..c344ed7
--- /dev/null
+++ b/crypto/asn1/tasn_fre.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/mem.h>
+
+
+static void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int combine);
+
+/* Free up an ASN1 structure */
+
+void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it)
+	{
+	asn1_item_combine_free(&val, it, 0);
+	}
+
+void ASN1_item_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+	{
+	asn1_item_combine_free(pval, it, 0);
+	}
+
+static void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int combine)
+	{
+	const ASN1_TEMPLATE *tt = NULL, *seqtt;
+	const ASN1_EXTERN_FUNCS *ef;
+	const ASN1_COMPAT_FUNCS *cf;
+	const ASN1_AUX *aux = it->funcs;
+	ASN1_aux_cb *asn1_cb;
+	int i;
+	if (!pval)
+		return;
+	if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval)
+		return;
+	if (aux && aux->asn1_cb)
+		asn1_cb = aux->asn1_cb;
+	else
+		asn1_cb = 0;
+
+	switch(it->itype)
+		{
+
+		case ASN1_ITYPE_PRIMITIVE:
+		if (it->templates)
+			ASN1_template_free(pval, it->templates);
+		else
+			ASN1_primitive_free(pval, it);
+		break;
+
+		case ASN1_ITYPE_MSTRING:
+		ASN1_primitive_free(pval, it);
+		break;
+
+		case ASN1_ITYPE_CHOICE:
+		if (asn1_cb)
+			{
+			i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
+			if (i == 2)
+				return;
+			}
+		i = asn1_get_choice_selector(pval, it);
+		if ((i >= 0) && (i < it->tcount))
+			{
+			ASN1_VALUE **pchval;
+			tt = it->templates + i;
+			pchval = asn1_get_field_ptr(pval, tt);
+			ASN1_template_free(pchval, tt);
+			}
+		if (asn1_cb)
+			asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
+		if (!combine)
+			{
+			OPENSSL_free(*pval);
+			*pval = NULL;
+			}
+		break;
+
+		case ASN1_ITYPE_COMPAT:
+		cf = it->funcs;
+		if (cf && cf->asn1_free)
+			cf->asn1_free(*pval);
+		break;
+
+		case ASN1_ITYPE_EXTERN:
+		ef = it->funcs;
+		if (ef && ef->asn1_ex_free)
+			ef->asn1_ex_free(pval, it);
+		break;
+
+		case ASN1_ITYPE_NDEF_SEQUENCE:
+		case ASN1_ITYPE_SEQUENCE:
+		if (asn1_do_lock(pval, -1, it) > 0)
+			return;
+		if (asn1_cb)
+			{
+			i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
+			if (i == 2)
+				return;
+			}		
+		asn1_enc_free(pval, it);
+		/* If we free up as normal we will invalidate any
+		 * ANY DEFINED BY field and we wont be able to 
+		 * determine the type of the field it defines. So
+		 * free up in reverse order.
+		 */
+		tt = it->templates + it->tcount - 1;
+		for (i = 0; i < it->tcount; tt--, i++)
+			{
+			ASN1_VALUE **pseqval;
+			seqtt = asn1_do_adb(pval, tt, 0);
+			if (!seqtt)
+				continue;
+			pseqval = asn1_get_field_ptr(pval, seqtt);
+			ASN1_template_free(pseqval, seqtt);
+			}
+		if (asn1_cb)
+			asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
+		if (!combine)
+			{
+			OPENSSL_free(*pval);
+			*pval = NULL;
+			}
+		break;
+		}
+	}
+
+void ASN1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+	{
+	size_t i;
+	if (tt->flags & ASN1_TFLG_SK_MASK)
+		{
+		STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval;
+		for (i = 0; i < sk_ASN1_VALUE_num(sk); i++)
+			{
+			ASN1_VALUE *vtmp;
+			vtmp = sk_ASN1_VALUE_value(sk, i);
+			asn1_item_combine_free(&vtmp, ASN1_ITEM_ptr(tt->item),
+									0);
+			}
+		sk_ASN1_VALUE_free(sk);
+		*pval = NULL;
+		}
+	else
+		asn1_item_combine_free(pval, ASN1_ITEM_ptr(tt->item),
+						tt->flags & ASN1_TFLG_COMBINE);
+	}
+
+void ASN1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+	{
+	int utype;
+	if (it)
+		{
+		const ASN1_PRIMITIVE_FUNCS *pf;
+		pf = it->funcs;
+		if (pf && pf->prim_free)
+			{
+			pf->prim_free(pval, it);
+			return;
+			}
+		}
+	/* Special case: if 'it' is NULL free contents of ASN1_TYPE */
+	if (!it)
+		{
+		ASN1_TYPE *typ = (ASN1_TYPE *)*pval;
+		utype = typ->type;
+		pval = &typ->value.asn1_value;
+		if (!*pval)
+			return;
+		}
+	else if (it->itype == ASN1_ITYPE_MSTRING)
+		{
+		utype = -1;
+		if (!*pval)
+			return;
+		}
+	else
+		{
+		utype = it->utype;
+		if ((utype != V_ASN1_BOOLEAN) && !*pval)
+			return;
+		}
+
+	switch(utype)
+		{
+		case V_ASN1_OBJECT:
+		ASN1_OBJECT_free((ASN1_OBJECT *)*pval);
+		break;
+
+		case V_ASN1_BOOLEAN:
+		if (it)
+			*(ASN1_BOOLEAN *)pval = it->size;
+		else
+			*(ASN1_BOOLEAN *)pval = -1;
+		return;
+
+		case V_ASN1_NULL:
+		break;
+
+		case V_ASN1_ANY:
+		ASN1_primitive_free(pval, NULL);
+		OPENSSL_free(*pval);
+		break;
+
+		default:
+		ASN1_STRING_free((ASN1_STRING *)*pval);
+		*pval = NULL;
+		break;
+		}
+	*pval = NULL;
+	}
diff --git a/crypto/asn1/tasn_new.c b/crypto/asn1/tasn_new.c
new file mode 100644
index 0000000..8a2b27d
--- /dev/null
+++ b/crypto/asn1/tasn_new.c
@@ -0,0 +1,394 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+
+static int asn1_item_ex_combine_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
+								int combine);
+static void asn1_item_clear(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static void asn1_template_clear(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+static void asn1_primitive_clear(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+ASN1_VALUE *ASN1_item_new(const ASN1_ITEM *it)
+	{
+	ASN1_VALUE *ret = NULL;
+	if (ASN1_item_ex_new(&ret, it) > 0)
+		return ret;
+	return NULL;
+	}
+
+/* Allocate an ASN1 structure */
+
+int ASN1_item_ex_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+	{
+	return asn1_item_ex_combine_new(pval, it, 0);
+	}
+
+static int asn1_item_ex_combine_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
+								int combine)
+	{
+	const ASN1_TEMPLATE *tt = NULL;
+	const ASN1_COMPAT_FUNCS *cf;
+	const ASN1_EXTERN_FUNCS *ef;
+	const ASN1_AUX *aux = it->funcs;
+	ASN1_aux_cb *asn1_cb;
+	ASN1_VALUE **pseqval;
+	int i;
+	if (aux && aux->asn1_cb)
+		asn1_cb = aux->asn1_cb;
+	else
+		asn1_cb = 0;
+
+	if (!combine) *pval = NULL;
+
+#ifdef CRYPTO_MDEBUG
+	if (it->sname)
+		CRYPTO_push_info(it->sname);
+#endif
+
+	switch(it->itype)
+		{
+
+		case ASN1_ITYPE_EXTERN:
+		ef = it->funcs;
+		if (ef && ef->asn1_ex_new)
+			{
+			if (!ef->asn1_ex_new(pval, it))
+				goto memerr;
+			}
+		break;
+
+		case ASN1_ITYPE_COMPAT:
+		cf = it->funcs;
+		if (cf && cf->asn1_new) {
+			*pval = cf->asn1_new();
+			if (!*pval)
+				goto memerr;
+		}
+		break;
+
+		case ASN1_ITYPE_PRIMITIVE:
+		if (it->templates)
+			{
+			if (!ASN1_template_new(pval, it->templates))
+				goto memerr;
+			}
+		else if (!ASN1_primitive_new(pval, it))
+				goto memerr;
+		break;
+
+		case ASN1_ITYPE_MSTRING:
+		if (!ASN1_primitive_new(pval, it))
+				goto memerr;
+		break;
+
+		case ASN1_ITYPE_CHOICE:
+		if (asn1_cb)
+			{
+			i = asn1_cb(ASN1_OP_NEW_PRE, pval, it, NULL);
+			if (!i)
+				goto auxerr;
+			if (i==2)
+				{
+#ifdef CRYPTO_MDEBUG
+				if (it->sname)
+					CRYPTO_pop_info();
+#endif
+				return 1;
+				}
+			}
+		if (!combine)
+			{
+			*pval = OPENSSL_malloc(it->size);
+			if (!*pval)
+				goto memerr;
+			memset(*pval, 0, it->size);
+			}
+		asn1_set_choice_selector(pval, -1, it);
+		if (asn1_cb && !asn1_cb(ASN1_OP_NEW_POST, pval, it, NULL))
+				goto auxerr;
+		break;
+
+		case ASN1_ITYPE_NDEF_SEQUENCE:
+		case ASN1_ITYPE_SEQUENCE:
+		if (asn1_cb)
+			{
+			i = asn1_cb(ASN1_OP_NEW_PRE, pval, it, NULL);
+			if (!i)
+				goto auxerr;
+			if (i==2)
+				{
+#ifdef CRYPTO_MDEBUG
+				if (it->sname)
+					CRYPTO_pop_info();
+#endif
+				return 1;
+				}
+			}
+		if (!combine)
+			{
+			*pval = OPENSSL_malloc(it->size);
+			if (!*pval)
+				goto memerr;
+			memset(*pval, 0, it->size);
+			asn1_do_lock(pval, 0, it);
+			asn1_enc_init(pval, it);
+			}
+		for (i = 0, tt = it->templates; i < it->tcount; tt++, i++)
+			{
+			pseqval = asn1_get_field_ptr(pval, tt);
+			if (!ASN1_template_new(pseqval, tt))
+				goto memerr;
+			}
+		if (asn1_cb && !asn1_cb(ASN1_OP_NEW_POST, pval, it, NULL))
+				goto auxerr;
+		break;
+	}
+#ifdef CRYPTO_MDEBUG
+	if (it->sname) CRYPTO_pop_info();
+#endif
+	return 1;
+
+	memerr:
+	OPENSSL_PUT_ERROR(ASN1, asn1_item_ex_combine_new,  ERR_R_MALLOC_FAILURE);
+#ifdef CRYPTO_MDEBUG
+	if (it->sname) CRYPTO_pop_info();
+#endif
+	return 0;
+
+	auxerr:
+	OPENSSL_PUT_ERROR(ASN1, asn1_item_ex_combine_new,  ASN1_R_AUX_ERROR);
+	ASN1_item_ex_free(pval, it);
+#ifdef CRYPTO_MDEBUG
+	if (it->sname) CRYPTO_pop_info();
+#endif
+	return 0;
+
+	}
+
+static void asn1_item_clear(ASN1_VALUE **pval, const ASN1_ITEM *it)
+	{
+	const ASN1_EXTERN_FUNCS *ef;
+
+	switch(it->itype)
+		{
+
+		case ASN1_ITYPE_EXTERN:
+		ef = it->funcs;
+		if (ef && ef->asn1_ex_clear) 
+			ef->asn1_ex_clear(pval, it);
+		else *pval = NULL;
+		break;
+
+
+		case ASN1_ITYPE_PRIMITIVE:
+		if (it->templates) 
+			asn1_template_clear(pval, it->templates);
+		else
+			asn1_primitive_clear(pval, it);
+		break;
+
+		case ASN1_ITYPE_MSTRING:
+		asn1_primitive_clear(pval, it);
+		break;
+
+		case ASN1_ITYPE_COMPAT:
+		case ASN1_ITYPE_CHOICE:
+		case ASN1_ITYPE_SEQUENCE:
+		case ASN1_ITYPE_NDEF_SEQUENCE:
+		*pval = NULL;
+		break;
+		}
+	}
+
+
+int ASN1_template_new(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+	{
+	const ASN1_ITEM *it = ASN1_ITEM_ptr(tt->item);
+	int ret;
+	if (tt->flags & ASN1_TFLG_OPTIONAL)
+		{
+		asn1_template_clear(pval, tt);
+		return 1;
+		}
+	/* If ANY DEFINED BY nothing to do */
+
+	if (tt->flags & ASN1_TFLG_ADB_MASK)
+		{
+		*pval = NULL;
+		return 1;
+		}
+#ifdef CRYPTO_MDEBUG
+	if (tt->field_name)
+		CRYPTO_push_info(tt->field_name);
+#endif
+	/* If SET OF or SEQUENCE OF, its a STACK */
+	if (tt->flags & ASN1_TFLG_SK_MASK)
+		{
+		STACK_OF(ASN1_VALUE) *skval;
+		skval = sk_ASN1_VALUE_new_null();
+		if (!skval)
+			{
+			OPENSSL_PUT_ERROR(ASN1, ASN1_template_new,  ERR_R_MALLOC_FAILURE);
+			ret = 0;
+			goto done;
+			}
+		*pval = (ASN1_VALUE *)skval;
+		ret = 1;
+		goto done;
+		}
+	/* Otherwise pass it back to the item routine */
+	ret = asn1_item_ex_combine_new(pval, it, tt->flags & ASN1_TFLG_COMBINE);
+	done:
+#ifdef CRYPTO_MDEBUG
+	if (it->sname)
+		CRYPTO_pop_info();
+#endif
+	return ret;
+	}
+
+static void asn1_template_clear(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+	{
+	/* If ADB or STACK just NULL the field */
+	if (tt->flags & (ASN1_TFLG_ADB_MASK|ASN1_TFLG_SK_MASK)) 
+		*pval = NULL;
+	else
+		asn1_item_clear(pval, ASN1_ITEM_ptr(tt->item));
+	}
+
+
+/* NB: could probably combine most of the real XXX_new() behaviour and junk
+ * all the old functions.
+ */
+
+int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+	{
+	ASN1_TYPE *typ;
+	ASN1_STRING *str;
+	int utype;
+
+	if (it && it->funcs)
+		{
+		const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
+		if (pf->prim_new)
+			return pf->prim_new(pval, it);
+		}
+
+	if (!it || (it->itype == ASN1_ITYPE_MSTRING))
+		utype = -1;
+	else
+		utype = it->utype;
+	switch(utype)
+		{
+		case V_ASN1_OBJECT:
+		*pval = (ASN1_VALUE *)OBJ_nid2obj(NID_undef);
+		return 1;
+
+		case V_ASN1_BOOLEAN:
+		*(ASN1_BOOLEAN *)pval = it->size;
+		return 1;
+
+		case V_ASN1_NULL:
+		*pval = (ASN1_VALUE *)1;
+		return 1;
+
+		case V_ASN1_ANY:
+		typ = OPENSSL_malloc(sizeof(ASN1_TYPE));
+		if (!typ)
+			return 0;
+		typ->value.ptr = NULL;
+		typ->type = -1;
+		*pval = (ASN1_VALUE *)typ;
+		break;
+
+		default:
+		str = ASN1_STRING_type_new(utype);
+		if (it->itype == ASN1_ITYPE_MSTRING && str)
+			str->flags |= ASN1_STRING_FLAG_MSTRING;
+		*pval = (ASN1_VALUE *)str;
+		break;
+		}
+	if (*pval)
+		return 1;
+	return 0;
+	}
+
+static void asn1_primitive_clear(ASN1_VALUE **pval, const ASN1_ITEM *it)
+	{
+	int utype;
+	if (it && it->funcs)
+		{
+		const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
+		if (pf->prim_clear)
+			pf->prim_clear(pval, it);
+		else 
+			*pval = NULL;
+		return;
+		}
+	if (!it || (it->itype == ASN1_ITYPE_MSTRING))
+		utype = -1;
+	else
+		utype = it->utype;
+	if (utype == V_ASN1_BOOLEAN)
+		*(ASN1_BOOLEAN *)pval = it->size;
+	else *pval = NULL;
+	}
diff --git a/crypto/asn1/tasn_prn.c b/crypto/asn1/tasn_prn.c
new file mode 100644
index 0000000..7b10cfe
--- /dev/null
+++ b/crypto/asn1/tasn_prn.c
@@ -0,0 +1,639 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <time.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/mem.h>
+
+#include "asn1_locl.h"
+
+
+
+/* Print routines.
+ */
+
+/* ASN1_PCTX routines */
+
+ASN1_PCTX default_pctx = 
+	{
+	ASN1_PCTX_FLAGS_SHOW_ABSENT,	/* flags */
+	0,	/* nm_flags */
+	0,	/* cert_flags */
+	0,	/* oid_flags */
+	0	/* str_flags */
+	};
+	
+
+ASN1_PCTX *ASN1_PCTX_new(void)
+	{
+	ASN1_PCTX *ret;
+	ret = OPENSSL_malloc(sizeof(ASN1_PCTX));
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(ASN1, ASN1_PCTX_new, ERR_R_MALLOC_FAILURE);
+		return NULL;
+		}
+	ret->flags = 0;
+	ret->nm_flags = 0;
+	ret->cert_flags = 0;
+	ret->oid_flags = 0;
+	ret->str_flags = 0;
+	return ret;
+	}
+
+void ASN1_PCTX_free(ASN1_PCTX *p)
+	{
+	OPENSSL_free(p);
+	}
+
+unsigned long ASN1_PCTX_get_flags(ASN1_PCTX *p)
+	{
+	return p->flags;
+	}
+
+void ASN1_PCTX_set_flags(ASN1_PCTX *p, unsigned long flags)
+	{
+	p->flags = flags;
+	}
+
+unsigned long ASN1_PCTX_get_nm_flags(ASN1_PCTX *p)
+	{
+	return p->nm_flags;
+	}
+
+void ASN1_PCTX_set_nm_flags(ASN1_PCTX *p, unsigned long flags)
+	{
+	p->nm_flags = flags;
+	}
+
+unsigned long ASN1_PCTX_get_cert_flags(ASN1_PCTX *p)
+	{
+	return p->cert_flags;
+	}
+
+void ASN1_PCTX_set_cert_flags(ASN1_PCTX *p, unsigned long flags)
+	{
+	p->cert_flags = flags;
+	}
+
+unsigned long ASN1_PCTX_get_oid_flags(ASN1_PCTX *p)
+	{
+	return p->oid_flags;
+	}
+
+void ASN1_PCTX_set_oid_flags(ASN1_PCTX *p, unsigned long flags)
+	{
+	p->oid_flags = flags;
+	}
+
+unsigned long ASN1_PCTX_get_str_flags(ASN1_PCTX *p)
+	{
+	return p->str_flags;
+	}
+
+void ASN1_PCTX_set_str_flags(ASN1_PCTX *p, unsigned long flags)
+	{
+	p->str_flags = flags;
+	}
+
+/* Main print routines */
+
+static int asn1_item_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+				const ASN1_ITEM *it,
+				const char *fname, const char *sname,
+				int nohdr, const ASN1_PCTX *pctx);
+
+int asn1_template_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+				const ASN1_TEMPLATE *tt, const ASN1_PCTX *pctx);
+
+static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld,
+				const ASN1_ITEM *it, int indent,
+				const char *fname, const char *sname,
+				const ASN1_PCTX *pctx);
+
+static int asn1_print_fsname(BIO *out, int indent,
+			const char *fname, const char *sname,
+			const ASN1_PCTX *pctx);
+
+int ASN1_item_print(BIO *out, ASN1_VALUE *ifld, int indent,
+				const ASN1_ITEM *it, const ASN1_PCTX *pctx)
+	{
+	const char *sname;
+	if (pctx == NULL)
+		pctx = &default_pctx;
+	if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME)
+		sname = NULL;
+	else
+		sname = it->sname;
+	return asn1_item_print_ctx(out, &ifld, indent, it,
+							NULL, sname, 0, pctx);
+	}
+
+static int asn1_item_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+				const ASN1_ITEM *it,
+				const char *fname, const char *sname,
+				int nohdr, const ASN1_PCTX *pctx)
+	{
+	const ASN1_TEMPLATE *tt;
+	const ASN1_EXTERN_FUNCS *ef;
+	ASN1_VALUE **tmpfld;
+	const ASN1_AUX *aux = it->funcs;
+	ASN1_aux_cb *asn1_cb;
+	ASN1_PRINT_ARG parg;
+	int i;
+	if (aux && aux->asn1_cb)
+		{
+		parg.out = out;
+		parg.indent = indent;
+		parg.pctx = pctx;
+		asn1_cb = aux->asn1_cb;
+		}
+	else asn1_cb = 0;
+
+	if(*fld == NULL)
+		{
+		if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_ABSENT)
+			{
+			if (!nohdr && !asn1_print_fsname(out, indent,
+							fname, sname, pctx))
+				return 0;
+			if (BIO_puts(out, "<ABSENT>\n") <= 0)
+				return 0;
+			}
+		return 1;
+		}
+
+	switch(it->itype)
+		{
+		case ASN1_ITYPE_PRIMITIVE:
+		if(it->templates)
+			{
+			if (!asn1_template_print_ctx(out, fld, indent,
+							it->templates, pctx))
+				return 0;
+			}
+		/* fall thru */
+		case ASN1_ITYPE_MSTRING:
+		if (!asn1_primitive_print(out, fld, it,
+				indent, fname, sname,pctx))
+			return 0;
+		break;
+
+		case ASN1_ITYPE_EXTERN:
+		if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx))
+			return 0;
+		/* Use new style print routine if possible */
+		ef = it->funcs;
+		if (ef && ef->asn1_ex_print)
+			{
+			i = ef->asn1_ex_print(out, fld, indent, "", pctx);
+			if (!i)
+				return 0;
+			if ((i == 2) && (BIO_puts(out, "\n") <= 0))
+				return 0;
+			return 1;
+			}
+		else if (sname && 
+			BIO_printf(out, ":EXTERNAL TYPE %s\n", sname) <= 0)
+			return 0;
+		break;
+
+		case ASN1_ITYPE_CHOICE:
+#if 0
+		if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx))
+			return 0;
+#endif
+		/* CHOICE type, get selector */
+		i = asn1_get_choice_selector(fld, it);
+		/* This should never happen... */
+		if((i < 0) || (i >= it->tcount))
+			{
+			if (BIO_printf(out,
+				"ERROR: selector [%d] invalid\n", i) <= 0)
+				return 0;
+			return 1;
+			}
+		tt = it->templates + i;
+		tmpfld = asn1_get_field_ptr(fld, tt);
+		if (!asn1_template_print_ctx(out, tmpfld, indent, tt, pctx))
+			return 0;
+		break;
+
+		case ASN1_ITYPE_SEQUENCE:
+		case ASN1_ITYPE_NDEF_SEQUENCE:
+		if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx))
+			return 0;
+		if (fname || sname)
+			{
+			if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE)
+				{
+				if (BIO_puts(out, " {\n") <= 0)
+					return 0;
+				}
+			else
+				{
+				if (BIO_puts(out, "\n") <= 0)
+					return 0;
+				}
+			}
+
+		if (asn1_cb)
+			{
+			i = asn1_cb(ASN1_OP_PRINT_PRE, fld, it, &parg);
+			if (i == 0)
+				return 0;
+			if (i == 2)
+				return 1;
+			}
+
+		/* Print each field entry */
+		for(i = 0, tt = it->templates; i < it->tcount; i++, tt++)
+			{
+			const ASN1_TEMPLATE *seqtt;
+			seqtt = asn1_do_adb(fld, tt, 1);
+			tmpfld = asn1_get_field_ptr(fld, seqtt);
+			if (!asn1_template_print_ctx(out, tmpfld,
+						indent + 2, seqtt, pctx))
+				return 0;
+			}
+		if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE)
+			{
+			if (BIO_printf(out, "%*s}\n", indent, "") < 0)
+				return 0;
+			}
+
+		if (asn1_cb)
+			{
+			i = asn1_cb(ASN1_OP_PRINT_POST, fld, it, &parg);
+			if (i == 0)
+				return 0;
+			}
+		break;
+
+		default:
+		BIO_printf(out, "Unprocessed type %d\n", it->itype);
+		return 0;
+		}
+
+	return 1;
+	}
+
+int asn1_template_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+				const ASN1_TEMPLATE *tt, const ASN1_PCTX *pctx)
+	{
+	int flags;
+	size_t i;
+	const char *sname, *fname;
+	flags = tt->flags;
+	if(pctx->flags & ASN1_PCTX_FLAGS_SHOW_FIELD_STRUCT_NAME)
+		sname = ASN1_ITEM_ptr(tt->item)->sname;
+	else
+		sname = NULL;
+	if(pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME)
+		fname = NULL;
+	else
+		fname = tt->field_name;
+	if(flags & ASN1_TFLG_SK_MASK)
+		{
+		char *tname;
+		ASN1_VALUE *skitem;
+		STACK_OF(ASN1_VALUE) *stack;
+
+		/* SET OF, SEQUENCE OF */
+		if (fname)
+			{
+			if(pctx->flags & ASN1_PCTX_FLAGS_SHOW_SSOF)
+				{
+				if(flags & ASN1_TFLG_SET_OF)
+					tname = "SET";
+				else
+					tname = "SEQUENCE";
+				if (BIO_printf(out, "%*s%s OF %s {\n",
+					indent, "", tname, tt->field_name) <= 0)
+					return 0;
+				}
+			else if (BIO_printf(out, "%*s%s:\n", indent, "",
+					fname) <= 0)
+				return 0;
+			}
+		stack = (STACK_OF(ASN1_VALUE) *)*fld;
+		for(i = 0; i < sk_ASN1_VALUE_num(stack); i++)
+			{
+			if ((i > 0) && (BIO_puts(out, "\n") <= 0))
+				return 0;
+
+			skitem = sk_ASN1_VALUE_value(stack, i);
+			if (!asn1_item_print_ctx(out, &skitem, indent + 2,
+				ASN1_ITEM_ptr(tt->item), NULL, NULL, 1, pctx))
+				return 0;
+			}
+		if (!i && BIO_printf(out, "%*s<EMPTY>\n", indent + 2, "") <= 0)
+				return 0;
+		if(pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE)
+			{
+			if (BIO_printf(out, "%*s}\n", indent, "") <= 0)
+				return 0;
+			}
+		return 1;
+		}
+	return asn1_item_print_ctx(out, fld, indent, ASN1_ITEM_ptr(tt->item),
+							fname, sname, 0, pctx);
+	}
+
+static int asn1_print_fsname(BIO *out, int indent,
+			const char *fname, const char *sname,
+			const ASN1_PCTX *pctx)
+	{
+	static char spaces[] = "                    ";
+	const int nspaces = sizeof(spaces) - 1;
+
+#if 0
+	if (!sname && !fname)
+		return 1;
+#endif
+
+	while (indent > nspaces)
+		{
+		if (BIO_write(out, spaces, nspaces) != nspaces)
+			return 0;
+		indent -= nspaces;
+		}
+	if (BIO_write(out, spaces, indent) != indent)
+		return 0;
+	if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME)
+		sname = NULL;
+	if (pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME)
+		fname = NULL;
+	if (!sname && !fname)
+		return 1;
+	if (fname)
+		{
+		if (BIO_puts(out, fname) <= 0)
+			return 0;
+		}
+	if (sname)
+		{
+		if (fname)
+			{
+			if (BIO_printf(out, " (%s)", sname) <= 0)
+				return 0;
+			}
+		else
+			{
+			if (BIO_puts(out, sname) <= 0)
+				return 0;
+			}
+		}
+	if (BIO_write(out, ": ", 2) != 2)
+		return 0;
+	return 1;
+	}
+
+static int asn1_print_boolean_ctx(BIO *out, int boolval,
+							const ASN1_PCTX *pctx)
+	{
+	const char *str;
+	switch (boolval)
+		{
+		case -1:
+		str = "BOOL ABSENT";
+		break;
+
+		case 0:
+		str = "FALSE";
+		break;
+
+		default:
+		str = "TRUE";
+		break;
+
+		}
+
+	if (BIO_puts(out, str) <= 0)
+		return 0;
+	return 1;
+
+	}
+
+static int asn1_print_integer_ctx(BIO *out, ASN1_INTEGER *str,
+						const ASN1_PCTX *pctx)
+	{
+	BIGNUM *bn = NULL;
+	char *s = NULL;
+	int ret = 1;
+
+	bn = ASN1_INTEGER_to_BN(str, NULL);
+	if (bn == NULL) {
+		return 0;
+	}
+	s = BN_bn2dec(bn);
+	BN_free(bn);
+	if (s == NULL) {
+		return 0;
+	}
+
+	if (BIO_puts(out, s) <= 0) {
+		ret = 0;
+	}
+	OPENSSL_free(s);
+	return ret;
+	}
+
+static int asn1_print_oid_ctx(BIO *out, const ASN1_OBJECT *oid,
+						const ASN1_PCTX *pctx)
+	{
+	char objbuf[80];
+	const char *ln;
+	ln = OBJ_nid2ln(OBJ_obj2nid(oid));
+	if(!ln)
+		ln = "";
+	OBJ_obj2txt(objbuf, sizeof objbuf, oid, 1);
+	if (BIO_printf(out, "%s (%s)", ln, objbuf) <= 0)
+		return 0;
+	return 1;
+	}
+
+static int asn1_print_obstring_ctx(BIO *out, ASN1_STRING *str, int indent,
+						const ASN1_PCTX *pctx)
+	{
+	if (str->type == V_ASN1_BIT_STRING)
+		{
+		if (BIO_printf(out, " (%ld unused bits)\n",
+					str->flags & 0x7) <= 0)
+				return 0;
+		}
+	else if (BIO_puts(out, "\n") <= 0)
+		return 0;
+	if (str->length > 0 && !BIO_hexdump(out, str->data, str->length, indent + 2)) {
+		return 0;
+	}
+	return 1;
+	}
+
+static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld,
+				const ASN1_ITEM *it, int indent,
+				const char *fname, const char *sname,
+				const ASN1_PCTX *pctx)
+	{
+	long utype;
+	ASN1_STRING *str;
+	int ret = 1, needlf = 1;
+	const char *pname;
+	const ASN1_PRIMITIVE_FUNCS *pf;
+	pf = it->funcs;
+	if (!asn1_print_fsname(out, indent, fname, sname, pctx))
+			return 0;
+	if (pf && pf->prim_print)
+		return pf->prim_print(out, fld, it, indent, pctx);
+	str = (ASN1_STRING *)*fld;
+	if (it->itype == ASN1_ITYPE_MSTRING)
+		utype = str->type & ~V_ASN1_NEG;
+	else
+		utype = it->utype;
+	if (utype == V_ASN1_ANY)
+		{
+		ASN1_TYPE *atype = (ASN1_TYPE *)*fld;
+		utype = atype->type;
+		fld = &atype->value.asn1_value;
+		str = (ASN1_STRING *)*fld;
+		if (pctx->flags & ASN1_PCTX_FLAGS_NO_ANY_TYPE)
+			pname = NULL;
+		else 
+			pname = ASN1_tag2str(utype);
+		}
+	else
+		{
+		if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_TYPE)
+			pname = ASN1_tag2str(utype);
+		else 
+			pname = NULL;
+		}
+
+	if (utype == V_ASN1_NULL)
+		{
+		if (BIO_puts(out, "NULL\n") <= 0)
+			return 0;
+		return 1;
+		}
+
+	if (pname)
+		{
+		if (BIO_puts(out, pname) <= 0)
+			return 0;
+		if (BIO_puts(out, ":") <= 0)
+			return 0;
+		}
+
+	switch (utype)
+		{
+		case V_ASN1_BOOLEAN:
+			{
+			int boolval = *(int *)fld;
+			if (boolval == -1)
+				boolval = it->size;
+			ret = asn1_print_boolean_ctx(out, boolval, pctx);
+			}
+		break;
+
+		case V_ASN1_INTEGER:
+		case V_ASN1_ENUMERATED:
+		ret = asn1_print_integer_ctx(out, str, pctx);
+		break;
+
+		case V_ASN1_UTCTIME:
+		ret = ASN1_UTCTIME_print(out, str);
+		break;
+
+		case V_ASN1_GENERALIZEDTIME:
+		ret = ASN1_GENERALIZEDTIME_print(out, str);
+		break;
+
+		case V_ASN1_OBJECT:
+		ret = asn1_print_oid_ctx(out, (const ASN1_OBJECT *)*fld, pctx);
+		break;
+
+		case V_ASN1_OCTET_STRING:
+		case V_ASN1_BIT_STRING:
+		ret = asn1_print_obstring_ctx(out, str, indent, pctx);
+		needlf = 0;
+		break;
+
+		case V_ASN1_SEQUENCE:
+		case V_ASN1_SET:
+		case V_ASN1_OTHER:
+		if (BIO_puts(out, "\n") <= 0)
+			return 0;
+		if (ASN1_parse_dump(out, str->data, str->length,
+						indent, 0) <= 0)
+			ret = 0;
+		needlf = 0;
+		break;
+
+		default:
+		ret = ASN1_STRING_print_ex(out, str, pctx->str_flags);
+
+		}
+	if (!ret)
+		return 0;
+	if (needlf && BIO_puts(out, "\n") <= 0)
+		return 0;
+	return 1;
+	}
diff --git a/crypto/asn1/tasn_typ.c b/crypto/asn1/tasn_typ.c
new file mode 100644
index 0000000..f2bbbc8
--- /dev/null
+++ b/crypto/asn1/tasn_typ.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+
+
+/* Declarations for string types */
+
+IMPLEMENT_ASN1_TYPE(ASN1_INTEGER);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_INTEGER);
+
+IMPLEMENT_ASN1_TYPE(ASN1_ENUMERATED);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_ENUMERATED);
+
+IMPLEMENT_ASN1_TYPE(ASN1_BIT_STRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_BIT_STRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_OCTET_STRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_OCTET_STRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_NULL);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_NULL);
+
+IMPLEMENT_ASN1_TYPE(ASN1_OBJECT);
+
+IMPLEMENT_ASN1_TYPE(ASN1_UTF8STRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_UTF8STRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_PRINTABLESTRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_PRINTABLESTRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_T61STRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_T61STRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_IA5STRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_IA5STRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_GENERALSTRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_GENERALSTRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_UTCTIME);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_UTCTIME);
+
+IMPLEMENT_ASN1_TYPE(ASN1_GENERALIZEDTIME);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_GENERALIZEDTIME);
+
+IMPLEMENT_ASN1_TYPE(ASN1_VISIBLESTRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_VISIBLESTRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_UNIVERSALSTRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_UNIVERSALSTRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_BMPSTRING);
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_BMPSTRING);
+
+IMPLEMENT_ASN1_TYPE(ASN1_ANY);
+
+/* Just swallow an ASN1_SEQUENCE in an ASN1_STRING */;
+IMPLEMENT_ASN1_TYPE(ASN1_SEQUENCE);
+
+IMPLEMENT_ASN1_FUNCTIONS_fname(ASN1_TYPE, ASN1_ANY, ASN1_TYPE);
+
+/* Multistring types */;
+
+IMPLEMENT_ASN1_MSTRING(ASN1_PRINTABLE, B_ASN1_PRINTABLE);
+IMPLEMENT_ASN1_FUNCTIONS_name(ASN1_STRING, ASN1_PRINTABLE);
+
+IMPLEMENT_ASN1_MSTRING(DISPLAYTEXT, B_ASN1_DISPLAYTEXT);
+IMPLEMENT_ASN1_FUNCTIONS_name(ASN1_STRING, DISPLAYTEXT);
+
+IMPLEMENT_ASN1_MSTRING(DIRECTORYSTRING, B_ASN1_DIRECTORYSTRING);
+IMPLEMENT_ASN1_FUNCTIONS_name(ASN1_STRING, DIRECTORYSTRING);
+
+/* Three separate BOOLEAN type: normal, DEFAULT TRUE and DEFAULT FALSE */;
+IMPLEMENT_ASN1_TYPE_ex(ASN1_BOOLEAN, ASN1_BOOLEAN, -1);
+IMPLEMENT_ASN1_TYPE_ex(ASN1_TBOOLEAN, ASN1_BOOLEAN, 1);
+IMPLEMENT_ASN1_TYPE_ex(ASN1_FBOOLEAN, ASN1_BOOLEAN, 0);
+
+/* Special, OCTET STRING with indefinite length constructed support */;
+
+IMPLEMENT_ASN1_TYPE_ex(ASN1_OCTET_STRING_NDEF, ASN1_OCTET_STRING,
+                       ASN1_TFLG_NDEF);
+
+ASN1_ITEM_TEMPLATE(ASN1_SEQUENCE_ANY) = ASN1_EX_TEMPLATE_TYPE(
+    ASN1_TFLG_SEQUENCE_OF, 0, ASN1_SEQUENCE_ANY, ASN1_ANY);
+ASN1_ITEM_TEMPLATE_END(ASN1_SEQUENCE_ANY);
+
+ASN1_ITEM_TEMPLATE(ASN1_SET_ANY) = ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF, 0,
+                                                         ASN1_SET_ANY,
+                                                         ASN1_ANY);
+ASN1_ITEM_TEMPLATE_END(ASN1_SET_ANY);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(ASN1_SEQUENCE_ANY,
+                                            ASN1_SEQUENCE_ANY,
+                                            ASN1_SEQUENCE_ANY);
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(ASN1_SEQUENCE_ANY, ASN1_SET_ANY,
+                                            ASN1_SET_ANY);
diff --git a/crypto/asn1/tasn_utl.c b/crypto/asn1/tasn_utl.c
new file mode 100644
index 0000000..250975a
--- /dev/null
+++ b/crypto/asn1/tasn_utl.c
@@ -0,0 +1,258 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/err.h>
+
+
+/* Utility functions for manipulating fields and offsets */
+
+/* Add 'offset' to 'addr' */
+#define offset2ptr(addr, offset) (void *)(((char *) addr) + offset)
+
+/* Given an ASN1_ITEM CHOICE type return the selector value */
+int asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it) {
+  int *sel = offset2ptr(*pval, it->utype);
+  return *sel;
+}
+
+/* Given an ASN1_ITEM CHOICE type set the selector value, return old value. */
+int asn1_set_choice_selector(ASN1_VALUE **pval, int value,
+                             const ASN1_ITEM *it) {
+  int *sel, ret;
+  sel = offset2ptr(*pval, it->utype);
+  ret = *sel;
+  *sel = value;
+  return ret;
+}
+
+/* Do reference counting. The value 'op' decides what to do. if it is +1 then
+ * the count is incremented. If op is 0 count is set to 1. If op is -1 count is
+ * decremented and the return value is the current refrence count or 0 if no
+ * reference count exists. */
+int asn1_do_lock(ASN1_VALUE **pval, int op, const ASN1_ITEM *it) {
+  const ASN1_AUX *aux;
+  int *lck, ret;
+  if (it->itype != ASN1_ITYPE_SEQUENCE &&
+      it->itype != ASN1_ITYPE_NDEF_SEQUENCE) {
+    return 0;
+  }
+  aux = it->funcs;
+  if (!aux || !(aux->flags & ASN1_AFLG_REFCOUNT)) {
+    return 0;
+  }
+  lck = offset2ptr(*pval, aux->ref_offset);
+  if (op == 0) {
+    *lck = 1;
+    return 1;
+  }
+  ret = CRYPTO_add(lck, op, aux->ref_lock);
+  return ret;
+}
+
+static ASN1_ENCODING *asn1_get_enc_ptr(ASN1_VALUE **pval, const ASN1_ITEM *it) {
+  const ASN1_AUX *aux;
+  if (!pval || !*pval) {
+    return NULL;
+  }
+  aux = it->funcs;
+  if (!aux || !(aux->flags & ASN1_AFLG_ENCODING)) {
+    return NULL;
+  }
+  return offset2ptr(*pval, aux->enc_offset);
+}
+
+void asn1_enc_init(ASN1_VALUE **pval, const ASN1_ITEM *it) {
+  ASN1_ENCODING *enc;
+  enc = asn1_get_enc_ptr(pval, it);
+  if (enc) {
+    enc->enc = NULL;
+    enc->len = 0;
+    enc->modified = 1;
+  }
+}
+
+void asn1_enc_free(ASN1_VALUE **pval, const ASN1_ITEM *it) {
+  ASN1_ENCODING *enc;
+  enc = asn1_get_enc_ptr(pval, it);
+  if (enc) {
+    if (enc->enc) {
+      OPENSSL_free(enc->enc);
+    }
+    enc->enc = NULL;
+    enc->len = 0;
+    enc->modified = 1;
+  }
+}
+
+int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen,
+                  const ASN1_ITEM *it) {
+  ASN1_ENCODING *enc;
+  enc = asn1_get_enc_ptr(pval, it);
+  if (!enc) {
+    return 1;
+  }
+
+  if (enc->enc) {
+    OPENSSL_free(enc->enc);
+  }
+  enc->enc = OPENSSL_malloc(inlen);
+  if (!enc->enc) {
+    return 0;
+  }
+  memcpy(enc->enc, in, inlen);
+  enc->len = inlen;
+  enc->modified = 0;
+
+  return 1;
+}
+
+int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval,
+                     const ASN1_ITEM *it) {
+  ASN1_ENCODING *enc;
+  enc = asn1_get_enc_ptr(pval, it);
+  if (!enc || enc->modified) {
+    return 0;
+  }
+  if (out) {
+    memcpy(*out, enc->enc, enc->len);
+    *out += enc->len;
+  }
+  if (len) {
+    *len = enc->len;
+  }
+  return 1;
+}
+
+/* Given an ASN1_TEMPLATE get a pointer to a field */
+ASN1_VALUE **asn1_get_field_ptr(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt) {
+  ASN1_VALUE **pvaltmp;
+  if (tt->flags & ASN1_TFLG_COMBINE) {
+    return pval;
+  }
+  pvaltmp = offset2ptr(*pval, tt->offset);
+  /* NOTE for BOOLEAN types the field is just a plain int so we can't return
+   * int **, so settle for (int *). */
+  return pvaltmp;
+}
+
+/* Handle ANY DEFINED BY template, find the selector, look up the relevant
+ * ASN1_TEMPLATE in the table and return it. */
+const ASN1_TEMPLATE *asn1_do_adb(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt,
+                                 int nullerr) {
+  const ASN1_ADB *adb;
+  const ASN1_ADB_TABLE *atbl;
+  long selector;
+  ASN1_VALUE **sfld;
+  int i;
+  if (!(tt->flags & ASN1_TFLG_ADB_MASK)) {
+    return tt;
+  }
+
+  /* Else ANY DEFINED BY ... get the table */
+  adb = ASN1_ADB_ptr(tt->item);
+
+  /* Get the selector field */
+  sfld = offset2ptr(*pval, adb->offset);
+
+  /* Check if NULL */
+  if (!sfld) {
+    if (!adb->null_tt) {
+      goto err;
+    }
+    return adb->null_tt;
+  }
+
+  /* Convert type to a long:
+   * NB: don't check for NID_undef here because it
+   * might be a legitimate value in the table */
+  if (tt->flags & ASN1_TFLG_ADB_OID) {
+    selector = OBJ_obj2nid((ASN1_OBJECT *)*sfld);
+  } else {
+    selector = ASN1_INTEGER_get((ASN1_INTEGER *)*sfld);
+  }
+
+  /* Try to find matching entry in table Maybe should check application types
+   * first to allow application override? Might also be useful to have a flag
+   * which indicates table is sorted and we can do a binary search. For now
+   * stick to a linear search. */
+
+  for (atbl = adb->tbl, i = 0; i < adb->tblcount; i++, atbl++) {
+    if (atbl->value == selector) {
+      return &atbl->tt;
+    }
+  }
+
+  /* FIXME: need to search application table too */
+
+  /* No match, return default type */
+  if (!adb->default_tt) {
+    goto err;
+  }
+  return adb->default_tt;
+
+err:
+  /* FIXME: should log the value or OID of unsupported type */
+  if (nullerr) {
+    OPENSSL_PUT_ERROR(ASN1, asn1_do_adb,
+                      ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE);
+  }
+  return NULL;
+}
diff --git a/crypto/asn1/x_bignum.c b/crypto/asn1/x_bignum.c
new file mode 100644
index 0000000..2ffa093
--- /dev/null
+++ b/crypto/asn1/x_bignum.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+
+
+/* Custom primitive type for BIGNUM handling. This reads in an ASN1_INTEGER as a
+ * BIGNUM directly. Currently it ignores the sign which isn't a problem since all
+ * BIGNUMs used are non negative and anything that looks negative is normally due
+ * to an encoding error.
+ */
+
+#define BN_SENSITIVE	1
+
+static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static void bn_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+static int bn_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it);
+static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it);
+
+static ASN1_PRIMITIVE_FUNCS bignum_pf = {
+	NULL, 0,
+	bn_new,
+	bn_free,
+	0,
+	bn_c2i,
+	bn_i2c
+};
+
+ASN1_ITEM_start(BIGNUM)
+	ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &bignum_pf, 0, "BIGNUM"
+ASN1_ITEM_end(BIGNUM)
+
+ASN1_ITEM_start(CBIGNUM)
+	ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &bignum_pf, BN_SENSITIVE, "BIGNUM"
+ASN1_ITEM_end(CBIGNUM)
+
+static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+	*pval = (ASN1_VALUE *)BN_new();
+	if(*pval) return 1;
+	else return 0;
+}
+
+static void bn_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+	if(!*pval) return;
+	if(it->size & BN_SENSITIVE) BN_clear_free((BIGNUM *)*pval);
+	else BN_free((BIGNUM *)*pval);
+	*pval = NULL;
+}
+
+static int bn_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it)
+{
+	BIGNUM *bn;
+	int pad;
+	if(!*pval) return -1;
+	bn = (BIGNUM *)*pval;
+	/* If MSB set in an octet we need a padding byte */
+	if(BN_num_bits(bn) & 0x7) pad = 0;
+	else pad = 1;
+	if(cont) {
+		if(pad) *cont++ = 0;
+		BN_bn2bin(bn, cont);
+	}
+	return pad + BN_num_bytes(bn);
+}
+
+static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+		  int utype, char *free_cont, const ASN1_ITEM *it)
+{
+	BIGNUM *bn;
+	if(!*pval) bn_new(pval, it);
+	bn  = (BIGNUM *)*pval;
+	if(!BN_bin2bn(cont, len, bn)) {
+		bn_free(pval, it);
+		return 0;
+	}
+	return 1;
+}
diff --git a/crypto/asn1/x_long.c b/crypto/asn1/x_long.c
new file mode 100644
index 0000000..cb0dad1
--- /dev/null
+++ b/crypto/asn1/x_long.c
@@ -0,0 +1,180 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+/* Custom primitive type for long handling. This converts between an ASN1_INTEGER
+ * and a long directly.
+ */
+
+
+static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it);
+static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it);
+static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it, int indent, const ASN1_PCTX *pctx);
+
+static ASN1_PRIMITIVE_FUNCS long_pf = {
+	NULL, 0,
+	long_new,
+	long_free,
+	long_free,	/* Clear should set to initial value */
+	long_c2i,
+	long_i2c,
+	long_print
+};
+
+ASN1_ITEM_start(LONG)
+	ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &long_pf, ASN1_LONG_UNDEF, "LONG"
+ASN1_ITEM_end(LONG)
+
+ASN1_ITEM_start(ZLONG)
+	ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &long_pf, 0, "ZLONG"
+ASN1_ITEM_end(ZLONG)
+
+static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+	*(long *)pval = it->size;
+	return 1;
+}
+
+static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+	*(long *)pval = it->size;
+}
+
+static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it)
+{
+	long ltmp;
+	unsigned long utmp;
+	int clen, pad, i;
+	/* this exists to bypass broken gcc optimization */
+	char *cp = (char *)pval;
+
+	/* use memcpy, because we may not be long aligned */
+	memcpy(&ltmp, cp, sizeof(long));
+
+	if(ltmp == it->size) return -1;
+	/* Convert the long to positive: we subtract one if negative so
+	 * we can cleanly handle the padding if only the MSB of the leading
+	 * octet is set. 
+	 */
+	if(ltmp < 0) utmp = -ltmp - 1;
+	else utmp = ltmp;
+	clen = BN_num_bits_word(utmp);
+	/* If MSB of leading octet set we need to pad */
+	if(!(clen & 0x7)) pad = 1;
+	else pad = 0;
+
+	/* Convert number of bits to number of octets */
+	clen = (clen + 7) >> 3;
+
+	if(cont) {
+		if(pad) *cont++ = (ltmp < 0) ? 0xff : 0;
+		for(i = clen - 1; i >= 0; i--) {
+			cont[i] = (unsigned char)(utmp & 0xff);
+			if(ltmp < 0) cont[i] ^= 0xff;
+			utmp >>= 8;
+		}
+	}
+	return clen + pad;
+}
+
+static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+		    int utype, char *free_cont, const ASN1_ITEM *it)
+{
+	int neg, i;
+	long ltmp;
+	unsigned long utmp = 0;
+	char *cp = (char *)pval;
+	if(len > (int)sizeof(long)) {
+		OPENSSL_PUT_ERROR(ASN1, long_c2i,  ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
+		return 0;
+	}
+	/* Is it negative? */
+	if(len && (cont[0] & 0x80)) neg = 1;
+	else neg = 0;
+	utmp = 0;
+	for(i = 0; i < len; i++) {
+		utmp <<= 8;
+		if(neg) utmp |= cont[i] ^ 0xff;
+		else utmp |= cont[i];
+	}
+	ltmp = (long)utmp;
+	if(neg) {
+		ltmp++;
+		ltmp = -ltmp;
+	}
+	if(ltmp == it->size) {
+		OPENSSL_PUT_ERROR(ASN1, long_c2i,  ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
+		return 0;
+	}
+	memcpy(cp, &ltmp, sizeof(long));
+	return 1;
+}
+
+static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
+			int indent, const ASN1_PCTX *pctx)
+	{
+	return BIO_printf(out, "%ld\n", *(long *)pval);
+	}
diff --git a/crypto/base.h b/crypto/base.h
new file mode 100644
index 0000000..d4b4028
--- /dev/null
+++ b/crypto/base.h
@@ -0,0 +1,167 @@
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_BASE_H
+#define OPENSSL_HEADER_BASE_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+
+#if defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64)
+#define OPENSSL_64_BIT
+#define OPENSSL_X86_64
+#elif defined(__x86) || defined(__i386) || defined(__i386__) || defined(_M_IX86)
+#define OPENSSL_32_BIT
+#define OPENSSL_X86
+#elif defined(__aarch64__)
+#define OPENSSL_64_BIT
+#define OPENSSL_AARCH64
+#elif defined(__arm) || defined(__arm__) || defined(_M_ARM)
+#define OPENSSL_32_BIT
+#define OPENSSL_ARM
+#else
+#error "Unknown target CPU"
+#endif
+
+#define OPENSSL_IS_BORINGSSL
+#define OPENSSL_VERSION_NUMBER 0x10002000
+
+
+/* This file should be the first included by all BoringSSL headers. */
+
+typedef int ASN1_BOOLEAN;
+typedef int ASN1_NULL;
+typedef struct ASN1_ITEM_st ASN1_ITEM;
+typedef struct asn1_object_st ASN1_OBJECT;
+typedef struct asn1_pctx_st ASN1_PCTX;
+typedef struct asn1_string_st ASN1_BIT_STRING;
+typedef struct asn1_string_st ASN1_BMPSTRING;
+typedef struct asn1_string_st ASN1_ENUMERATED;
+typedef struct asn1_string_st ASN1_GENERALIZEDTIME;
+typedef struct asn1_string_st ASN1_GENERALSTRING;
+typedef struct asn1_string_st ASN1_IA5STRING;
+typedef struct asn1_string_st ASN1_INTEGER;
+typedef struct asn1_string_st ASN1_OCTET_STRING;
+typedef struct asn1_string_st ASN1_PRINTABLESTRING;
+typedef struct asn1_string_st ASN1_STRING;
+typedef struct asn1_string_st ASN1_T61STRING;
+typedef struct asn1_string_st ASN1_TIME;
+typedef struct asn1_string_st ASN1_UNIVERSALSTRING;
+typedef struct asn1_string_st ASN1_UTCTIME;
+typedef struct asn1_string_st ASN1_UTF8STRING;
+typedef struct asn1_string_st ASN1_VISIBLESTRING;
+
+typedef struct AUTHORITY_KEYID_st AUTHORITY_KEYID;
+typedef struct DIST_POINT_st DIST_POINT;
+typedef struct ISSUING_DIST_POINT_st ISSUING_DIST_POINT;
+typedef struct NAME_CONSTRAINTS_st NAME_CONSTRAINTS;
+typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE;
+typedef struct X509_POLICY_LEVEL_st X509_POLICY_LEVEL;
+typedef struct X509_POLICY_NODE_st X509_POLICY_NODE;
+typedef struct X509_POLICY_TREE_st X509_POLICY_TREE;
+typedef struct X509_algor_st X509_ALGOR;
+typedef struct X509_crl_st X509_CRL;
+typedef struct X509_name_st X509_NAME;
+typedef struct X509_pubkey_st X509_PUBKEY;
+typedef struct bignum_ctx BN_CTX;
+typedef struct bignum_st BIGNUM;
+typedef struct bio_method_st BIO_METHOD;
+typedef struct bio_st BIO;
+typedef struct bn_gencb_st BN_GENCB;
+typedef struct bn_mont_ctx_st BN_MONT_CTX;
+typedef struct buf_mem_st BUF_MEM;
+typedef struct cbb_st CBB;
+typedef struct cbs_st CBS;
+typedef struct comp_ctx_st COMP_CTX;
+typedef struct comp_method_st COMP_METHOD;
+typedef struct conf_st CONF;
+typedef struct dh_method DH_METHOD;
+typedef struct dh_st DH;
+typedef struct dsa_method DSA_METHOD;
+typedef struct dsa_st DSA;
+typedef struct ec_key_st EC_KEY;
+typedef struct ecdsa_method_st ECDSA_METHOD;
+typedef struct ecdsa_sig_st ECDSA_SIG;
+typedef struct engine_st ENGINE;
+typedef struct env_md_ctx_st EVP_MD_CTX;
+typedef struct env_md_st EVP_MD;
+typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
+typedef struct evp_cipher_st EVP_CIPHER;
+typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;
+typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
+typedef struct evp_pkey_method_st EVP_PKEY_METHOD;
+typedef struct evp_pkey_st EVP_PKEY;
+typedef struct hmac_ctx_st HMAC_CTX;
+typedef struct md5_state_st MD5_CTX;
+typedef struct pkcs8_priv_key_info_st PKCS8_PRIV_KEY_INFO;
+typedef struct rand_meth_st RAND_METHOD;
+typedef struct rsa_meth_st RSA_METHOD;
+typedef struct rsa_st RSA;
+typedef struct sha256_state_st SHA256_CTX;
+typedef struct sha512_state_st SHA512_CTX;
+typedef struct sha_state_st SHA_CTX;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct ssl_st SSL;
+typedef struct st_ERR_FNS ERR_FNS;
+typedef struct v3_ext_ctx X509V3_CTX;
+typedef struct x509_crl_method_st X509_CRL_METHOD;
+typedef struct x509_revoked_st X509_REVOKED;
+typedef struct x509_st X509;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+typedef struct x509_store_st X509_STORE;
+typedef void *OPENSSL_BLOCK;
+
+
+#endif  /* OPENSSL_HEADER_BASE_H */
diff --git a/crypto/base64/CMakeLists.txt b/crypto/base64/CMakeLists.txt
new file mode 100644
index 0000000..2601abd
--- /dev/null
+++ b/crypto/base64/CMakeLists.txt
@@ -0,0 +1,9 @@
+include_directories(. .. ../../include)
+
+add_library(
+	base64
+
+	OBJECT
+
+	base64.c
+)
diff --git a/crypto/base64/base64.c b/crypto/base64/base64.c
new file mode 100644
index 0000000..fb9aa36
--- /dev/null
+++ b/crypto/base64/base64.c
@@ -0,0 +1,391 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/base64.h>
+
+#include <assert.h>
+
+
+static const unsigned char data_bin2ascii[65] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define conv_bin2ascii(a) (data_bin2ascii[(a) & 0x3f])
+#define conv_ascii2bin(a) (data_ascii2bin[(a) & 0x7f])
+
+/* 64 char lines
+ * pad input with 0
+ * left over chars are set to =
+ * 1 byte  => xx==
+ * 2 bytes => xxx=
+ * 3 bytes => xxxx
+ */
+#define BIN_PER_LINE    (64/4*3)
+#define CHUNKS_PER_LINE (64/4)
+#define CHAR_PER_LINE   (64+1)
+
+/* 0xF0 is a EOLN
+ * 0xF1 is ignore but next needs to be 0xF0 (for \r\n processing).
+ * 0xF2 is EOF
+ * 0xE0 is ignore at start of line.
+ * 0xFF is error */
+
+#define B64_EOLN 0xF0
+#define B64_CR 0xF1
+#define B64_EOF 0xF2
+#define B64_WS 0xE0
+#define B64_ERROR 0xFF
+#define B64_NOT_BASE64(a) (((a) | 0x13) == 0xF3)
+
+static const unsigned char data_ascii2bin[128] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0xFF,
+    0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF,
+    0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
+    0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+    0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+void EVP_EncodeInit(EVP_ENCODE_CTX *ctx) {
+  ctx->length = 48;
+  ctx->num = 0;
+  ctx->line_num = 0;
+}
+
+void EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
+                      const uint8_t *in, size_t in_len) {
+  unsigned i, j;
+  unsigned total = 0;
+
+  *out_len = 0;
+  if (in_len == 0) {
+    return;
+  }
+
+  assert(ctx->length <= sizeof(ctx->enc_data));
+
+  if ((ctx->num + in_len) < ctx->length) {
+    memcpy(&ctx->enc_data[ctx->num], in, in_len);
+    ctx->num += in_len;
+    return;
+  }
+  if (ctx->num != 0) {
+    i = ctx->length - ctx->num;
+    memcpy(&ctx->enc_data[ctx->num], in, i);
+    in += i;
+    in_len -= i;
+    j = EVP_EncodeBlock(out, ctx->enc_data, ctx->length);
+    ctx->num = 0;
+    out += j;
+    *(out++) = '\n';
+    *out = '\0';
+    total = j + 1;
+  }
+  while (in_len >= ctx->length) {
+    j = EVP_EncodeBlock(out, in, ctx->length);
+    in += ctx->length;
+    in_len -= ctx->length;
+    out += j;
+    *(out++) = '\n';
+    *out = '\0';
+    total += j + 1;
+  }
+  if (in_len != 0) {
+    memcpy(&ctx->enc_data[0], in, in_len);
+  }
+  ctx->num = in_len;
+  *out_len = total;
+}
+
+void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len) {
+  unsigned ret = 0;
+
+  if (ctx->num != 0) {
+    ret = EVP_EncodeBlock(out, ctx->enc_data, ctx->num);
+    out[ret++] = '\n';
+    out[ret] = '\0';
+    ctx->num = 0;
+  }
+  *out_len = ret;
+}
+
+size_t EVP_EncodeBlock(uint8_t *dst, const uint8_t *src, size_t src_len) {
+  unsigned long l;
+  size_t i, ret = 0;
+
+  for (i = src_len; i > 0; i -= 3) {
+    if (i >= 3) {
+      l = (((unsigned long)src[0]) << 16L) | (((unsigned long)src[1]) << 8L) | src[2];
+      *(dst++) = conv_bin2ascii(l >> 18L);
+      *(dst++) = conv_bin2ascii(l >> 12L);
+      *(dst++) = conv_bin2ascii(l >> 6L);
+      *(dst++) = conv_bin2ascii(l);
+    } else {
+      l = ((unsigned long)src[0]) << 16L;
+      if (i == 2) {
+        l |= ((unsigned long)src[1] << 8L);
+      }
+
+      *(dst++) = conv_bin2ascii(l >> 18L);
+      *(dst++) = conv_bin2ascii(l >> 12L);
+      *(dst++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L);
+      *(dst++) = '=';
+    }
+    ret += 4;
+    src += 3;
+  }
+
+  *dst = '\0';
+  return ret;
+}
+
+void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) {
+  ctx->length = 30;
+  ctx->num = 0;
+  ctx->line_num = 0;
+  ctx->expect_nl = 0;
+}
+
+int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
+                     const uint8_t *in, size_t in_len) {
+  int seof = -1, eof = 0, rv = -1, v, tmp, exp_nl;
+  uint8_t *d;
+  unsigned i, n, ln, ret = 0;
+
+  n = ctx->num;
+  d = ctx->enc_data;
+  ln = ctx->line_num;
+  exp_nl = ctx->expect_nl;
+
+  /* last line of input. */
+  if (in_len == 0 || (n == 0 && conv_ascii2bin(in[0]) == B64_EOF)) {
+    rv = 0;
+    goto end;
+  }
+
+  /* We parse the input data */
+  for (i = 0; i < in_len; i++) {
+    /* If the current line is > 80 characters, scream alot */
+    if (ln >= 80) {
+      rv = -1;
+      goto end;
+    }
+
+    /* Get char and put it into the buffer */
+    tmp = *(in++);
+    v = conv_ascii2bin(tmp);
+    /* only save the good data :-) */
+    if (!B64_NOT_BASE64(v)) {
+      assert(n < sizeof(ctx->enc_data));
+      d[n++] = tmp;
+      ln++;
+    } else if (v == B64_ERROR) {
+      rv = -1;
+      goto end;
+    }
+
+    /* have we seen a '=' which is 'definitly' the last
+     * input line.  seof will point to the character that
+     * holds it. and eof will hold how many characters to
+     * chop off. */
+    if (tmp == '=') {
+      if (seof == -1) {
+        seof = n;
+      }
+      eof++;
+    }
+
+    if (v == B64_CR) {
+      ln = 0;
+      if (exp_nl) {
+        continue;
+      }
+    }
+
+    /* eoln */
+    if (v == B64_EOLN) {
+      ln = 0;
+      if (exp_nl) {
+        exp_nl = 0;
+        continue;
+      }
+    }
+    exp_nl = 0;
+
+    /* If we are at the end of input and it looks like a
+     * line, process it. */
+    if ((i + 1) == in_len && (((n & 3) == 0) || eof)) {
+      v = B64_EOF;
+      /* In case things were given us in really small
+         records (so two '=' were given in separate
+         updates), eof may contain the incorrect number
+         of ending bytes to skip, so let's redo the count */
+      eof = 0;
+      if (d[n - 1] == '=') {
+        eof++;
+      }
+      if (d[n - 2] == '=') {
+        eof++;
+      }
+      /* There will never be more than two '=' */
+    }
+
+    if ((v == B64_EOF && (n & 3) == 0) || n >= 64) {
+      /* This is needed to work correctly on 64 byte input
+       * lines.  We process the line and then need to
+       * accept the '\n' */
+      if (v != B64_EOF && n >= 64) {
+        exp_nl = 1;
+      }
+      if (n > 0) {
+        v = EVP_DecodeBlock(out, d, n);
+        n = 0;
+        if (v < 0) {
+          rv = 0;
+          goto end;
+        }
+        ret += (v - eof);
+      } else {
+        eof = 1;
+        v = 0;
+      }
+
+      /* This is the case where we have had a short
+       * but valid input line */
+      if (v < (int)ctx->length && eof) {
+        rv = 0;
+        goto end;
+      } else {
+        ctx->length = v;
+      }
+
+      if (seof >= 0) {
+        rv = 0;
+        goto end;
+      }
+      out += v;
+    }
+  }
+  rv = 1;
+
+end:
+  *out_len = ret;
+  ctx->num = n;
+  ctx->line_num = ln;
+  ctx->expect_nl = exp_nl;
+  return rv;
+}
+
+int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *outl) {
+  int i;
+
+  *outl = 0;
+  if (ctx->num != 0) {
+    i = EVP_DecodeBlock(out, ctx->enc_data, ctx->num);
+    if (i < 0) {
+      return -1;
+    }
+    ctx->num = 0;
+    *outl = i;
+    return 1;
+  } else {
+    return 1;
+  }
+}
+
+size_t EVP_DecodeBlock(uint8_t *dst, const uint8_t *src, size_t src_len) {
+  int a, b, c, d;
+  unsigned long l;
+  size_t i, ret = 0;
+
+  /* trim white space from the start of the line. */
+  while (conv_ascii2bin(*src) == B64_WS && src_len > 0) {
+    src++;
+    src_len--;
+  }
+
+  /* strip off stuff at the end of the line
+   * ascii2bin values B64_WS, B64_EOLN, B64_EOLN and B64_EOF */
+  while (src_len > 3 && B64_NOT_BASE64(conv_ascii2bin(src[src_len - 1]))) {
+    src_len--;
+  }
+
+  if (src_len % 4 != 0) {
+    return -1;
+  }
+
+  for (i = 0; i < src_len; i += 4) {
+    a = conv_ascii2bin(*(src++));
+    b = conv_ascii2bin(*(src++));
+    c = conv_ascii2bin(*(src++));
+    d = conv_ascii2bin(*(src++));
+    if ((a & 0x80) || (b & 0x80) || (c & 0x80) || (d & 0x80)) {
+      return -1;
+    }
+    l = ((((unsigned long)a) << 18L) | (((unsigned long)b) << 12L) |
+         (((unsigned long)c) << 6L) | (((unsigned long)d)));
+    *(dst++) = (uint8_t)(l >> 16L) & 0xff;
+    *(dst++) = (uint8_t)(l >> 8L) & 0xff;
+    *(dst++) = (uint8_t)(l) & 0xff;
+    ret += 3;
+  }
+
+  return ret;
+}
diff --git a/crypto/base64/base64.h b/crypto/base64/base64.h
new file mode 100644
index 0000000..afafaef
--- /dev/null
+++ b/crypto/base64/base64.h
@@ -0,0 +1,141 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_BASE64_H
+#define OPENSSL_HEADER_BASE64_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* base64 functions.
+ *
+ * For historical reasons, these functions have the EVP_ prefix but just do
+ * base64 encoding and decoding. */
+
+
+typedef struct evp_encode_ctx_st EVP_ENCODE_CTX;
+
+
+/* Encoding */
+
+/* EVP_EncodeInit initialises |*ctx|, which is typically stack allocated, for
+ * an encoding operation. */
+void EVP_EncodeInit(EVP_ENCODE_CTX *ctx);
+
+/* EVP_EncodeUpdate encodes |in_len| bytes from |in| and writes an encoded
+ * version of them to |out| and sets |*out_len| to the number of bytes written.
+ * Some state may be contained in |ctx| so |EVP_EncodeFinal| must be used to
+ * flush it before using the encoded data. */
+void EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
+                      const uint8_t *in, size_t in_len);
+
+/* EVP_EncodeFinal flushes any remaining output bytes from |ctx| to |out| and
+ * sets |*out_len| to the number of bytes written. */
+void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len);
+
+/* EVP_EncodeBlock encodes |src_len| bytes from |src| and writes the result to
+ * |dst|. It returns the number of bytes written. */
+size_t EVP_EncodeBlock(uint8_t *dst, const uint8_t *src, size_t src_len);
+
+
+/* Decoding */
+
+/* EVP_DecodeInit initialises |*ctx|, which is typically stack allocated, for
+ * a decoding operation. */
+void EVP_DecodeInit(EVP_ENCODE_CTX *ctx);
+
+/* EVP_DecodeUpdate decodes |in_len| bytes from |in| and writes the decoded
+ * data to |out| and sets |*out_len| to the number of bytes written. Some state
+ * may be contained in |ctx| so |EVP_DecodeFinal| must be used to flush it
+ * before using the encoded data.
+ *
+ * It returns -1 on error, one if a full line of input was processed and zero
+ * if the line was short (i.e. it was the last line). */
+int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
+                     const uint8_t *in, size_t in_len);
+
+/* EVP_DecodeFinal flushes any remaining output bytes from |ctx| to |out| and
+ * sets |*out_len| to the number of bytes written. It returns one on success
+ * and minus one on error. */
+int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len);
+
+/* EVP_DecodeBlock encodes |src_len| bytes from |src| and writes the result to
+ * |dst|. It returns the number of bytes written. */
+size_t EVP_DecodeBlock(uint8_t *dst, const uint8_t *src, size_t src_len);
+
+
+struct evp_encode_ctx_st {
+  unsigned num;    /* number saved in a partial encode/decode */
+  unsigned length; /* The length is either the output line length
+               * (in input bytes) or the shortest input line
+               * length that is ok.  Once decoding begins,
+               * the length is adjusted up each time a longer
+               * line is decoded */
+  uint8_t enc_data[80]; /* data to encode */
+  unsigned line_num;    /* number read on current line */
+  int expect_nl;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_BASE64_H */
diff --git a/crypto/bio/CMakeLists.txt b/crypto/bio/CMakeLists.txt
new file mode 100644
index 0000000..86092f0
--- /dev/null
+++ b/crypto/bio/CMakeLists.txt
@@ -0,0 +1,28 @@
+include_directories(. .. ../../include)
+
+add_library(
+	bio
+
+	OBJECT
+
+	bio.c
+	bio_error.c
+	bio_mem.c
+	buffer.c
+	connect.c
+	fd.c
+	file.c
+	hexdump.c
+	pair.c
+	printf.c
+	socket.c
+	socket_helper.c
+)
+
+add_executable(
+	bio_test
+
+	bio_test.c
+)
+
+target_link_libraries(bio_test crypto)
diff --git a/crypto/bio/bio.c b/crypto/bio/bio.c
new file mode 100644
index 0000000..d9f7f40
--- /dev/null
+++ b/crypto/bio/bio.c
@@ -0,0 +1,452 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <limits.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/thread.h>
+
+
+/* BIO_set initialises a BIO structure to have the given type and sets the
+ * reference count to one. It returns one on success or zero on error. */
+static int bio_set(BIO *bio, const BIO_METHOD *method) {
+  /* This function can be called with a stack allocated |BIO| so we have to
+   * assume that the contents of |BIO| are arbitary. This also means that it'll
+   * leak memory if you call |BIO_set| twice on the same BIO. */
+  memset(bio, 0, sizeof(BIO));
+
+  bio->method = method;
+  bio->shutdown = 1;
+  bio->references = 1;
+
+  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data)) {
+    return 0;
+  }
+
+  if (method->create != NULL) {
+    if (!method->create(bio)) {
+      CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+BIO *BIO_new(const BIO_METHOD *method) {
+  BIO *ret = OPENSSL_malloc(sizeof(BIO));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(BIO, BIO_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  if (!bio_set(ret, method)) {
+    OPENSSL_free(ret);
+    ret = NULL;
+  }
+
+  return ret;
+}
+
+int BIO_free(BIO *bio) {
+  BIO *next_bio;
+
+  for (; bio != NULL; bio = next_bio) {
+    int refs = CRYPTO_add(&bio->references, -1, CRYPTO_LOCK_BIO);
+    if (refs > 0) {
+      return 0;
+    }
+
+    if (bio->callback != NULL) {
+      int i = (int)bio->callback(bio, BIO_CB_FREE, NULL, 0, 0, 1);
+      if (i <= 0) {
+        return i;
+      }
+    }
+
+    next_bio = BIO_pop(bio);
+
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
+
+    if (bio->method != NULL && bio->method->destroy != NULL) {
+      bio->method->destroy(bio);
+    }
+
+    OPENSSL_free(bio);
+  }
+  return 1;
+}
+
+void BIO_vfree(BIO *bio) {
+  BIO_free(bio);
+}
+
+void BIO_free_all(BIO *bio) {
+  BIO_free(bio);
+}
+
+static int bio_io(BIO *bio, void *buf, int len, size_t method_offset,
+                  int callback_flags, unsigned long *num) {
+  int i;
+  typedef int (*io_func_t)(BIO *, char *, int);
+  io_func_t io_func = NULL;
+
+  if (bio != NULL && bio->method != NULL) {
+    io_func =
+        *((const io_func_t *)(((const uint8_t *)bio->method) + method_offset));
+  }
+
+  if (io_func == NULL) {
+    OPENSSL_PUT_ERROR(BIO, bio_io, BIO_R_UNSUPPORTED_METHOD);
+    return -2;
+  }
+
+  if (bio->callback != NULL) {
+    i = (int) bio->callback(bio, callback_flags, buf, len, 0L, 1L);
+    if (i <= 0) {
+      return i;
+    }
+  }
+
+  if (!bio->init) {
+    OPENSSL_PUT_ERROR(BIO, bio_io, BIO_R_UNINITIALIZED);
+    return -2;
+  }
+
+  i = 0;
+  if (buf != NULL && len > 0) {
+    i = io_func(bio, buf, len);
+  }
+
+  if (i > 0) {
+    *num += i;
+  }
+
+  if (bio->callback != NULL) {
+    i = (int)(bio->callback(bio, callback_flags | BIO_CB_RETURN, buf, len, 0L,
+                            (long)i));
+  }
+
+  return i;
+}
+
+int BIO_read(BIO *bio, void *buf, int len) {
+  return bio_io(bio, buf, len, offsetof(BIO_METHOD, bread), BIO_CB_READ,
+                &bio->num_read);
+}
+
+int BIO_gets(BIO *bio, char *buf, int len) {
+  return bio_io(bio, buf, len, offsetof(BIO_METHOD, bgets), BIO_CB_GETS,
+                &bio->num_read);
+}
+
+int BIO_write(BIO *bio, const void *in, int inl) {
+  return bio_io(bio, (char *)in, inl, offsetof(BIO_METHOD, bwrite),
+                BIO_CB_WRITE, &bio->num_write);
+}
+
+int BIO_puts(BIO *bio, const char *in) {
+  return BIO_write(bio, in, strlen(in));
+}
+
+int BIO_flush(BIO *bio) {
+  return BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, NULL);
+}
+
+long BIO_ctrl(BIO *bio, int cmd, long larg, void *parg) {
+  long ret;
+
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (bio->method == NULL || bio->method->ctrl == NULL) {
+    OPENSSL_PUT_ERROR(BIO, BIO_ctrl, BIO_R_UNSUPPORTED_METHOD);
+    return -2;
+  }
+
+  if (bio->callback != NULL) {
+    ret = bio->callback(bio, BIO_CB_CTRL, parg, cmd, larg, 1);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  ret = bio->method->ctrl(bio, cmd, larg, parg);
+
+  if (bio->callback != NULL) {
+    ret = bio->callback(bio, BIO_CB_CTRL | BIO_CB_RETURN, parg, cmd, larg, ret);
+  }
+
+  return ret;
+}
+
+char *BIO_ptr_ctrl(BIO *b, int cmd, long larg) {
+  char *p = NULL;
+
+  if (BIO_ctrl(b, cmd, larg, (void *)&p) <= 0) {
+    return NULL;
+  }
+
+  return p;
+}
+
+long BIO_int_ctrl(BIO *b, int cmd, long larg, int iarg) {
+  int i = iarg;
+
+  return BIO_ctrl(b, cmd, larg, (void *)&i);
+}
+
+int BIO_reset(BIO *bio) {
+  return BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
+}
+
+void BIO_set_flags(BIO *bio, int flags) {
+  bio->flags |= flags;
+}
+
+int BIO_test_flags(const BIO *bio, int flags) {
+  return bio->flags & flags;
+}
+
+int BIO_should_read(const BIO *bio) {
+  return BIO_test_flags(bio, BIO_FLAGS_READ);
+}
+
+int BIO_should_write(const BIO *bio) {
+  return BIO_test_flags(bio, BIO_FLAGS_WRITE);
+}
+
+int BIO_should_retry(const BIO *bio) {
+  return BIO_test_flags(bio, BIO_FLAGS_SHOULD_RETRY);
+}
+
+int BIO_should_io_special(const BIO *bio) {
+  return BIO_test_flags(bio, BIO_FLAGS_IO_SPECIAL);
+}
+
+int BIO_get_retry_reason(const BIO *bio) { return bio->retry_reason; }
+
+void BIO_clear_flags(BIO *bio, int flags) {
+  bio->flags &= ~flags;
+}
+
+void BIO_set_retry_read(BIO *bio) {
+  bio->flags |= BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY;
+}
+
+void BIO_set_retry_write(BIO *bio) {
+  bio->flags |= BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY;
+}
+
+static const int kRetryFlags = BIO_FLAGS_RWS | BIO_FLAGS_SHOULD_RETRY;
+
+int BIO_get_retry_flags(BIO *bio) {
+  return bio->flags & kRetryFlags;
+}
+
+void BIO_clear_retry_flags(BIO *bio) {
+  bio->flags &= ~kRetryFlags;
+  bio->retry_reason = 0;
+}
+
+int BIO_method_type(const BIO *bio) { return bio->method->type; }
+
+void BIO_copy_next_retry(BIO *bio) {
+  BIO_clear_retry_flags(bio);
+  BIO_set_flags(bio, BIO_get_retry_flags(bio->next_bio));
+  bio->retry_reason = bio->next_bio->retry_reason;
+}
+
+long BIO_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+  long ret;
+  bio_info_cb cb;
+
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (bio->method == NULL || bio->method->callback_ctrl == NULL) {
+    OPENSSL_PUT_ERROR(BIO, BIO_callback_ctrl, BIO_R_UNSUPPORTED_METHOD);
+    return 0;
+  }
+
+  cb = bio->callback;
+
+  if (cb != NULL) {
+    ret = cb(bio, BIO_CB_CTRL, (void *)&fp, cmd, 0, 1L);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  ret = bio->method->callback_ctrl(bio, cmd, fp);
+
+  if (cb != NULL) {
+    ret = cb(bio, BIO_CB_CTRL | BIO_CB_RETURN, (void *)&fp, cmd, 0, ret);
+  }
+
+  return ret;
+}
+
+size_t BIO_pending(const BIO *bio) {
+  return BIO_ctrl((BIO *) bio, BIO_CTRL_PENDING, 0, NULL);
+}
+
+size_t BIO_wpending(const BIO *bio) {
+  return BIO_ctrl((BIO *) bio, BIO_CTRL_WPENDING, 0, NULL);
+}
+
+int BIO_set_close(BIO *bio, int close_flag) {
+  return BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, close_flag, NULL);
+}
+
+BIO *BIO_push(BIO *bio, BIO *appended_bio) {
+  BIO *last_bio;
+
+  if (bio == NULL) {
+    return bio;
+  }
+
+  last_bio = bio;
+  while (last_bio->next_bio != NULL) {
+    last_bio = last_bio->next_bio;
+  }
+
+  last_bio->next_bio = appended_bio;
+  /* TODO(fork): this seems very suspect. If we got rid of BIO SSL, we could
+   * get rid of this. */
+  BIO_ctrl(bio, BIO_CTRL_PUSH, 0, bio);
+
+  return bio;
+}
+
+BIO *BIO_pop(BIO *bio) {
+  BIO *ret;
+
+  if (bio == NULL) {
+    return NULL;
+  }
+  ret = bio->next_bio;
+  BIO_ctrl(bio, BIO_CTRL_POP, 0, bio);
+  bio->next_bio = NULL;
+  return ret;
+}
+
+BIO *BIO_next(BIO *bio) {
+  if (!bio) {
+    return NULL;
+  }
+  return bio->next_bio;
+}
+
+BIO *BIO_find_type(BIO *bio, int type) {
+  int method_type, mask;
+
+  if (!bio) {
+    return NULL;
+  }
+  mask = type & 0xff;
+
+  do {
+    if (bio->method != NULL) {
+      method_type = bio->method->type;
+
+      if (!mask) {
+        if (method_type & type) {
+          return bio;
+        }
+      } else if (method_type == type) {
+        return bio;
+      }
+    }
+    bio = bio->next_bio;
+  } while (bio != NULL);
+
+  return NULL;
+}
+
+int BIO_indent(BIO *bio, unsigned indent, unsigned max_indent) {
+  if (indent > max_indent) {
+    indent = max_indent;
+  }
+
+  while (indent--) {
+    if (BIO_puts(bio, " ") != 1) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+void BIO_print_errors_fp(FILE *out) {
+  BIO *bio = BIO_new_fp(out, BIO_NOCLOSE);
+  BIO_print_errors(bio);
+  BIO_free(bio);
+}
+
+static int print_bio(const char *str, size_t len, void *bio) {
+  return BIO_write((BIO *)bio, str, len);
+}
+
+void BIO_print_errors(BIO *bio) {
+  ERR_print_errors_cb(print_bio, bio);
+}
diff --git a/crypto/bio/bio.h b/crypto/bio/bio.h
new file mode 100644
index 0000000..76054c3
--- /dev/null
+++ b/crypto/bio/bio.h
@@ -0,0 +1,790 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_BIO_H
+#define OPENSSL_HEADER_BIO_H
+
+#include <openssl/base.h>
+
+#include <stdarg.h>
+#include <stdio.h>  /* For FILE */
+
+#include <openssl/ex_data.h>
+#include <openssl/stack.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* BIO abstracts over a file-descriptor like interface. */
+
+
+/* Allocation and freeing. */
+
+DEFINE_STACK_OF(BIO);
+
+/* BIO_new creates a new BIO with the given type and a reference count of one.
+ * It returns the fresh |BIO|, or NULL on error. */
+BIO *BIO_new(const BIO_METHOD *type);
+
+/* BIO_free decrements the reference count of |bio|. If the reference count
+ * drops to zero, it (optionally) calls the BIO's callback with |BIO_CB_FREE|,
+ * frees the ex_data and then, if the BIO has a destroy callback for the
+ * method, calls it. Finally it frees |bio| itself. It then repeats that for
+ * the next BIO in the chain, if any.
+ *
+ * It returns one on success or zero otherwise. */
+int BIO_free(BIO *bio);
+
+/* BIO_vfree performs the same actions as |BIO_free|, but has a void return
+ * value. This is provided for API-compat.
+ *
+ * TODO(fork): remove. */
+void BIO_vfree(BIO *bio);
+
+
+/* Basic I/O. */
+
+/* BIO_read attempts to read |len| bytes into |data|. It returns the number of
+ * bytes read, zero on EOF, or a negative number on error. */
+int BIO_read(BIO *bio, void *data, int len);
+
+/* BIO_gets "reads a line" from |bio| and puts at most |size| bytes into |buf|.
+ * It returns the number of bytes read or a negative number on error. The
+ * phrase "reads a line" is in quotes in the previous sentence because the
+ * exact operation depends on the BIO's method. For example, a digest BIO will
+ * return the digest in response to a |BIO_gets| call.
+ *
+ * TODO(fork): audit the set of BIOs that we end up needing. If all actually
+ * return a line for this call, remove the warning above. */
+int BIO_gets(BIO *bio, char *buf, int size);
+
+/* BIO_write writes |len| bytes from |data| to BIO. It returns the number of
+ * bytes written or a negative number on error. */
+int BIO_write(BIO *bio, const void *data, int 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. */
+int BIO_puts(BIO *bio, const char *buf);
+
+/* BIO_flush flushes any buffered output. It returns one on success and zero
+ * otherwise. */
+int BIO_flush(BIO *bio);
+
+
+/* Low-level control functions.
+ *
+ * These are generic functions for sending control requests to a BIO. In
+ * general one should use the wrapper functions like |BIO_get_close|. */
+
+/* BIO_ctrl sends the control request |cmd| to |bio|. The |cmd| argument should
+ * be one of the |BIO_C_*| values. */
+long BIO_ctrl(BIO *bio, int cmd, long larg, void *parg);
+
+/* BIO_ptr_ctrl acts like |BIO_ctrl| but passes the address of a |void*|
+ * pointer as |parg| and returns the value that is written to it, or NULL if
+ * the control request returns <= 0. */
+char *BIO_ptr_ctrl(BIO *bp, int cmd, long larg);
+
+/* BIO_int_ctrl acts like |BIO_ctrl| but passes the address of a copy of |iarg|
+ * as |parg|. */
+long BIO_int_ctrl(BIO *bp, int cmd, long larg, int iarg);
+
+/* BIO_reset resets |bio| to its initial state, the precise meaning of which
+ * depends on the concrete type of |bio|. It returns one on success and zero
+ * otherwise. */
+int BIO_reset(BIO *bio);
+
+/* BIO_set_flags ORs |flags| with |bio->flags|. */
+void BIO_set_flags(BIO *bio, int flags);
+
+/* BIO_test_flags returns |bio->flags| AND |flags|. */
+int BIO_test_flags(const BIO *bio, int flags);
+
+/* BIO_should_read returns non-zero if |bio| encountered a temporary error
+ * while reading (i.e. EAGAIN), indicating that the caller should retry the
+ * read. */
+int BIO_should_read(const BIO *bio);
+
+/* BIO_should_write returns non-zero if |bio| encountered a temporary error
+ * while writing (i.e. EAGAIN), indicating that the caller should retry the
+ * write. */
+int BIO_should_write(const BIO *bio);
+
+/* BIO_should_retry returns non-zero if the reason that caused a failed I/O
+ * operation is temporary and thus the operation should be retried. Otherwise,
+ * it was a permanent error and it returns zero. */
+int BIO_should_retry(const BIO *bio);
+
+/* BIO_should_io_special returns non-zero if |bio| encountered a temporary
+ * error while performing a special I/O operation, indicating that the caller
+ * should retry. The operation that caused the error is returned by
+ * |BIO_get_retry_reason|. */
+int BIO_should_io_special(const BIO *bio);
+
+/* BIO_RR_SSL_X509_LOOKUP indicates that an SSL BIO blocked because the SSL
+ * library returned with SSL_ERROR_WANT_X509_LOOKUP.
+ *
+ * TODO(fork): remove. */
+#define BIO_RR_SSL_X509_LOOKUP 0x01
+
+/* BIO_RR_CONNECT indicates that a connect would have blocked */
+#define BIO_RR_CONNECT 0x02
+
+/* BIO_RR_ACCEPT indicates that an accept would have blocked */
+#define BIO_RR_ACCEPT 0x03
+
+/* BIO_get_retry_reason returns the special I/O operation that needs to be
+ * retried. The return value is one of the |BIO_RR_*| values. */
+int BIO_get_retry_reason(const BIO *bio);
+
+/* BIO_clear_flags ANDs |bio->flags| with the bitwise-complement of |flags|. */
+void BIO_clear_flags(BIO *bio, int flags);
+
+/* BIO_set_retry_read sets the |BIO_FLAGS_READ| and |BIO_FLAGS_SHOULD_RETRY|
+ * flags on |bio|. */
+void BIO_set_retry_read(BIO *bio);
+
+/* BIO_set_retry_read sets the |BIO_FLAGS_WRITE| and |BIO_FLAGS_SHOULD_RETRY|
+ * flags on |bio|. */
+void BIO_set_retry_write(BIO *bio);
+
+/* BIO_get_retry_flags gets the |BIO_FLAGS_READ|, |BIO_FLAGS_WRITE|,
+ * |BIO_FLAGS_IO_SPECIAL| and |BIO_FLAGS_SHOULD_RETRY| flags from |bio|. */
+int BIO_get_retry_flags(BIO *bio);
+
+/* BIO_clear_retry_flags clears the |BIO_FLAGS_READ|, |BIO_FLAGS_WRITE|,
+ * |BIO_FLAGS_IO_SPECIAL| and |BIO_FLAGS_SHOULD_RETRY| flags from |bio|. */
+void BIO_clear_retry_flags(BIO *bio);
+
+/* BIO_method_type returns the type of |bio|, which is one of the |BIO_TYPE_*|
+ * values. */
+int BIO_method_type(const BIO *bio);
+
+/* bio_info_cb is the type of a callback function that can be called for most
+ * BIO operations. The |event| argument is one of |BIO_CB_*| and can be ORed
+ * with |BIO_CB_RETURN| if the callback is being made after the operation in
+ * question. In that case, |return_value| will contain the return value from
+ * the operation. */
+typedef long (*bio_info_cb)(BIO *bio, int event, const char *parg, int cmd,
+                            long larg, long return_value);
+
+/* BIO_callback_ctrl allows the callback function to be manipulated. The |cmd|
+ * arg will generally be |BIO_CTRL_SET_CALLBACK| but arbitary command values
+ * can be interpreted by the |BIO|. */
+long BIO_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp);
+
+/* BIO_pending returns the number of bytes pending to be read. */
+size_t BIO_pending(const BIO *bio);
+
+/* BIO_wpending returns the number of bytes pending to be written. */
+size_t BIO_wpending(const BIO *bio);
+
+/* BIO_set_close sets the close flag for |bio|. The meaning of which depends on
+ * the type of |bio| but, for example, a memory BIO interprets the close flag
+ * as meaning that it owns its buffer. It returns one on success and zero
+ * otherwise. */
+int BIO_set_close(BIO *bio, int close_flag);
+
+
+/* Managing chains of BIOs.
+ *
+ * BIOs can be put into chains where the output of one is used as the input of
+ * the next etc. The most common case is a buffering BIO, which accepts and
+ * buffers writes until flushed into the next BIO in the chain. */
+
+/* BIO_push adds |appended_bio| to the end of the chain with |bio| at the head.
+ * It returns |bio|. Note that |appended_bio| may be the head of a chain itself
+ * and thus this function can be used to join two chains.
+ *
+ * BIO_push takes ownership of the caller's reference to |appended_bio|. */
+BIO *BIO_push(BIO *bio, BIO *appended_bio);
+
+/* BIO_pop removes |bio| from the head of a chain and returns the next BIO in
+ * the chain, or NULL if there is no next BIO.
+ *
+ * The caller takes ownership of the chain's reference to |bio|. */
+BIO *BIO_pop(BIO *bio);
+
+/* BIO_next returns the next BIO in the chain after |bio|, or NULL if there is
+ * no such BIO. */
+BIO *BIO_next(BIO *bio);
+
+/* BIO_free_all calls |BIO_free|.
+ *
+ * TODO(fork): update callers and remove. */
+void BIO_free_all(BIO *bio);
+
+/* BIO_find_type walks a chain of BIOs and returns the first that matches
+ * |type|, which is one of the |BIO_TYPE_*| values. */
+BIO *BIO_find_type(BIO *bio, int type);
+
+/* BIO_copy_next_retry sets the retry flags and |retry_reason| of |bio| from
+ * the next BIO in the chain. */
+void BIO_copy_next_retry(BIO *bio);
+
+
+/* Printf functions.
+ *
+ * These functions are versions of printf functions that output to a BIO rather
+ * than a FILE. */
+#ifdef __GNUC__
+#define __bio_h__attr__ __attribute__
+#else
+#define __bio_h__attr__(x)
+#endif
+int BIO_printf(BIO *bio, const char *format, ...)
+    __bio_h__attr__((__format__(__printf__, 2, 3)));
+
+int BIO_vprintf(BIO *bio, const char *format, va_list args)
+    __bio_h__attr__((__format__(__printf__, 2, 0)));
+#undef __bio_h__attr__
+
+
+/* Utility functions. */
+
+/* BIO_indent prints min(|indent|, |max_indent|) spaces. It returns one on
+ * success and zero otherwise. */
+int BIO_indent(BIO *bio, unsigned indent, unsigned max_indent);
+
+/* BIO_hexdump writes a hex dump of |data| to |bio|. Each line will be indented
+ * by |indent| spaces. */
+int BIO_hexdump(BIO *bio, const uint8_t *data, size_t len, unsigned indent);
+
+/* BIO_print_errors_fp prints the current contents of the error stack to |out|
+ * using human readable strings where possible. */
+void BIO_print_errors_fp(FILE *out);
+
+/* BIO_print_errors prints the current contents of the error stack to |bio|
+ * using human readable strings where possible. */
+void BIO_print_errors(BIO *bio);
+
+
+/* Memory BIOs.
+ *
+ * Memory BIOs can be used as a read-only source (with |BIO_new_mem_buf|) or a
+ * writable sink (with |BIO_new|, |BIO_s_mem| and |BIO_get_mem_buf|). Data
+ * written to a writable, memory BIO can be recalled by reading from it.
+ *
+ * Calling |BIO_reset| on a read-only BIO resets it to the original contents.
+ * On a writable BIO, it clears any data.
+ *
+ * If the close flag is set to |BIO_NOCLOSE| (not the default) then the
+ * underlying |BUF_MEM| will not be freed when the |BIO| is freed.
+ *
+ * Memory BIOs support |BIO_gets| and |BIO_puts|.
+ *
+ * |BIO_eof| is true if no data is in the BIO.
+ *
+ * |BIO_ctrl_pending| returns the number of bytes currently stored. */
+
+/* BIO_s_mem returns a |BIO_METHOD| that uses a in-memory buffer. */
+const BIO_METHOD *BIO_s_mem(void);
+
+/* BIO_new_mem_buf creates BIO that reads and writes from |len| bytes at |buf|.
+ * It does not take ownership of |buf|. It returns the BIO or NULL on error.
+ *
+ * If |len| is negative, then |buf| is treated as a NUL-terminated string, but
+ * don't depend on this in new code. */
+BIO *BIO_new_mem_buf(void *buf, int len);
+
+/* BIO_get_mem_data sets |*contents| to point to the current contents of |bio|
+ * and returns the length of the data. */
+long BIO_get_mem_data(BIO *bio, char **contents);
+
+/* BIO_get_mem_ptr sets |*out| to a BUF_MEM containing the current contents of
+ * |bio|. It returns one on success or zero on error. */
+int BIO_get_mem_ptr(BIO *bio, BUF_MEM **out);
+
+/* BIO_set_mem_buf sets |b| as the contents of |bio|. If |take_ownership| is
+ * non-zero, then |b| will be freed when |bio| is closed. Returns one on
+ * success or zero otherwise. */
+int BIO_set_mem_buf(BIO *bio, BUF_MEM *b, int take_ownership);
+
+/* BIO_set_mem_eof_return sets the value that will be returned from reading
+ * |bio| when empty. If |eof_value| is zero then an empty memory BIO will
+ * return EOF (that is it will return zero and |BIO_should_retry| will be
+ * false). If |eof_value| is non zero then it will return |eof_value| when it
+ * is empty and it will set the read retry flag (that is |BIO_read_retry| is
+ * true). To avoid ambiguity with a normal positive return value, |eof_value|
+ * should be set to a negative value, typically -1.
+ *
+ * For a read-only BIO, the default is zero (EOF). For a writable BIO, the
+ * default is -1 so that additional data can be written once exhausted. */
+int BIO_set_mem_eof_return(BIO *bio, int eof_value);
+
+
+/* File descriptor BIOs.
+ *
+ * File descriptor BIOs are wrappers around the system's |read| and |write|
+ * functions. If the close flag is set then then |close| is called on the
+ * underlying file descriptor when the BIO is freed.
+ *
+ * |BIO_reset| attempts to seek the file pointer to the start of file using
+ * |lseek|.
+ *
+ * |BIO_seek| sets the file pointer to position |off| from start of file using
+ * |lseek|.
+ *
+ * |BIO_tell| returns the current file position. */
+
+/* BIO_s_fd returns a |BIO_METHOD| for file descriptor fds. */
+const BIO_METHOD *BIO_s_fd(void);
+
+/* BIO_new_fd creates a new file descriptor BIO wrapping |fd|. If |close_flag|
+ * is non-zero, then |fd| will be closed when the BIO is. */
+BIO *BIO_new_fd(int fd, int close_flag);
+
+/* BIO_set_fd sets the file descriptor of |bio| to |fd|. If |close_flag| is
+ * non-zero then |fd| will be closed when |bio| is. It returns one on success
+ * or zero on error. */
+int BIO_set_fd(BIO *bio, int fd, int close_flag);
+
+/* BIO_get_fd sets |*out_fd| to the file descriptor currently in use by |bio|.
+ * It returns one on success and zero on error. */
+int BIO_get_fd(BIO *bio, int *out_fd);
+
+
+/* File BIOs.
+ *
+ * File BIOs are wrappers around a C |FILE| object.
+ *
+ * |BIO_flush| on a file BIO calls |fflush| on the wrapped stream.
+ *
+ * |BIO_reset| attempts to seek the file pointer to the start of file using
+ * |fseek|.
+ *
+ * |BIO_seek| sets the file pointer to the given position from the start of
+ * file using |fseek|.
+ *
+ * |BIO_eof| calls |feof|.
+ *
+ * Setting the close flag causes |fclose| to be called on the stream when the
+ * BIO is freed. */
+
+/* BIO_s_file returns a BIO_METHOD that wraps a |FILE|. */
+const BIO_METHOD *BIO_s_file(void);
+
+/* BIO_new_file creates a file BIO by opening |filename| with the given mode.
+ * See the |fopen| manual page for details of the mode argument. */
+BIO *BIO_new_file(const char *filename, const char *mode);
+
+/* BIO_new_fp creates a new file BIO that wraps the given |FILE|. If
+ * |close_flag| is |BIO_CLOSE|, then |fclose| will be called on |stream| when
+ * the BIO is closed. */
+BIO *BIO_new_fp(FILE *stream, int close_flag);
+
+/* BIO_get_fp sets |*out_file| to the current |FILE| for |bio|. It returns one
+ * on success and zero otherwise. */
+int BIO_get_fp(BIO *bio, FILE **out_file);
+
+/* BIO_set_fp sets the |FILE| for |bio|. If |close_flag| is |BIO_CLOSE| then
+ * |fclose| will be called on |file| when |bio| is closed. It returns one on
+ * sucess and zero otherwise. */
+int BIO_set_fp(BIO *bio, FILE *file, int close_flag);
+
+/* BIO_read_filename opens |filename| for reading and sets the result as the
+ * |FILE| for |bio|. It returns one on success and zero otherwise. The |FILE|
+ * will be closed when |bio| is freed. */
+int BIO_read_filename(BIO *bio, const char *filename);
+
+/* BIO_write_filename opens |filename| for writing and sets the result as the
+ * |FILE| for |bio|. It returns one on success and zero otherwise. The |FILE|
+ * will be closed when |bio| is freed. */
+int BIO_write_filename(BIO *bio, const char *filename);
+
+/* BIO_append_filename opens |filename| for appending and sets the result as
+ * the |FILE| for |bio|. It returns one on success and zero otherwise. The
+ * |FILE| will be closed when |bio| is freed. */
+int BIO_append_filename(BIO *bio, const char *filename);
+
+/* BIO_rw_filename opens |filename| for reading and writing and sets the result
+ * as the |FILE| for |bio|. It returns one on success and zero otherwise. The
+ * |FILE| will be closed when |bio| is freed. */
+int BIO_rw_filename(BIO *bio, const char *filename);
+
+
+/* Buffer BIOs.
+ *
+ * Buffer BIOs are a filter-type BIO, i.e. they are designed to be used in a
+ * chain of BIOs. They provide buffering to reduce the number of operations on
+ * the underlying BIOs. */
+
+const BIO_METHOD *BIO_f_buffer(void);
+
+/* BIO_set_read_buffer_size sets the size, in bytes, of the read buffer and
+ * clears it. It returns one on success and zero on failure. */
+int BIO_set_read_buffer_size(BIO *bio, int buffer_size);
+
+/* BIO_set_write_buffer_size sets the size, in bytes, of the write buffer and
+ * clears it. It returns one on success and zero on failure. */
+int BIO_set_write_buffer_size(BIO *bio, int buffer_size);
+
+
+/* Socket BIOs. */
+
+const BIO_METHOD *BIO_s_socket(void);
+
+/* BIO_new_socket allocates and initialises a fresh BIO which will read and
+ * write to the socket |fd|. If |close_flag| is |BIO_CLOSE| then closing the
+ * BIO will close |fd|. It returns the fresh |BIO| or NULL on error. */
+BIO *BIO_new_socket(int fd, int close_flag);
+
+
+/* Connect BIOs.
+ *
+ * A connection BIO creates a network connection and transfers data over the
+ * resulting socket. */
+
+const BIO_METHOD *BIO_s_connect(void);
+
+/* BIO_new_connect returns a BIO that connects to the given hostname and port.
+ * The |host_and_optional_port| argument should be of the form
+ * "www.example.com" or "www.example.com:443". If the port is omitted, it must
+ * be provided with |BIO_set_conn_port|.
+ *
+ * It returns the new BIO on success, or NULL on error. */
+BIO *BIO_new_connect(const char *host_and_optional_port);
+
+/* BIO_set_conn_hostname sets |host_and_optional_port| as the hostname and
+ * optional port that |bio| will connect to. If the port is omitted, it must be
+ * provided with |BIO_set_conn_port|.
+ *
+ * It returns one on success and zero otherwise. */
+int BIO_set_conn_hostname(BIO *bio, const char *host_and_optional_port);
+
+/* BIO_set_conn_port sets |port_str| as the port or service name that |bio|
+ * will connect to. It returns one on success and zero otherwise. */
+int BIO_set_conn_port(BIO *bio, const char *port_str);
+
+/* BIO_set_nbio sets whether |bio| will use non-blocking I/O operations. It
+ * returns one on success and zero otherwise. */
+int BIO_set_nbio(BIO *bio, int on);
+
+
+/* Datagram BIOs.
+ *
+ * TODO(fork): not implemented. */
+
+#define BIO_CTRL_DGRAM_QUERY_MTU 40 /* as kernel for current MTU */
+
+#define BIO_CTRL_DGRAM_SET_MTU 42 /* set cached value for  MTU. want to use
+                                     this if asking the kernel fails */
+
+#define BIO_CTRL_DGRAM_MTU_EXCEEDED 43 /* check whether the MTU was exceed in
+                                          the previous write operation. */
+
+#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT \
+  45 /* Next DTLS handshake timeout to adjust socket timeouts */
+
+#define BIO_CTRL_DGRAM_GET_PEER           46
+
+#define BIO_CTRL_DGRAM_GET_FALLBACK_MTU   47
+
+
+/* BIO Pairs.
+ *
+ * BIO pairs provide a "loopback" like system: a pair of BIOs where data
+ * written to one can be read from the other and vice versa. */
+
+/* BIO_new_bio_pair sets |*out1| and |*out2| to two freshly created BIOs where
+ * data written to one can be read from the other and vice versa. The
+ * |writebuf1| argument gives the size of the buffer used in |*out1| and
+ * |writebuf2| for |*out2|. It returns one on success and zero on error. */
+int BIO_new_bio_pair(BIO **out1, size_t writebuf1, BIO **out2,
+                     size_t writebuf2);
+
+/* BIO_s_bio returns the method for a BIO pair. */
+const BIO_METHOD *BIO_s_bio(void);
+
+/* BIO_ctrl_get_read_request returns the number of bytes that the other side of
+ * |bio| tried (unsuccessfully) to read. */
+size_t BIO_ctrl_get_read_request(BIO *bio);
+
+/* BIO_ctrl_get_write_guarantee returns the number of bytes that |bio| (which
+ * must have been returned by |BIO_new_bio_pair|) will accept on the next
+ * |BIO_write| call. */
+size_t BIO_ctrl_get_write_guarantee(BIO *bio);
+
+/* BIO_shutdown_wr marks |bio| as closed, from the point of view of the other
+ * side of the pair. Future |BIO_write| calls on |bio| will fail. It returns
+ * one on success and zero otherwise. */
+int BIO_shutdown_wr(BIO *bio);
+
+
+/* BIO_NOCLOSE and |BIO_CLOSE| can be used as symbolic arguments when a "close
+ * flag" is passed to a BIO function. */
+#define BIO_NOCLOSE 0
+#define BIO_CLOSE 1
+
+/* These are passed to the BIO callback */
+#define BIO_CB_FREE 0x01
+#define BIO_CB_READ 0x02
+#define BIO_CB_WRITE 0x03
+#define BIO_CB_PUTS 0x04
+#define BIO_CB_GETS 0x05
+#define BIO_CB_CTRL 0x06
+
+/* The callback is called before and after the underling operation,
+ * The BIO_CB_RETURN flag indicates if it is after the call */
+#define BIO_CB_RETURN 0x80
+
+/* 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  /* opt - internal, used to signify change */
+#define BIO_CTRL_POP		7  /* opt - internal, used to signify change */
+#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 */
+
+
+/* Private functions */
+
+#define BIO_FLAGS_READ 0x01
+#define BIO_FLAGS_WRITE 0x02
+#define BIO_FLAGS_IO_SPECIAL 0x04
+#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. */
+#define BIO_FLAGS_MEM_RDONLY 0x200
+
+/* These are the 'types' of BIOs */
+#define BIO_TYPE_NONE 0
+#define BIO_TYPE_MEM (1 | 0x0400)
+#define BIO_TYPE_FILE (2 | 0x0400)
+#define BIO_TYPE_FD (4 | 0x0400 | 0x0100)
+#define BIO_TYPE_SOCKET (5 | 0x0400 | 0x0100)
+#define BIO_TYPE_NULL (6 | 0x0400)
+#define BIO_TYPE_SSL (7 | 0x0200)
+#define BIO_TYPE_MD (8 | 0x0200)                /* passive filter */
+#define BIO_TYPE_BUFFER (9 | 0x0200)            /* filter */
+#define BIO_TYPE_CIPHER (10 | 0x0200)           /* filter */
+#define BIO_TYPE_BASE64 (11 | 0x0200)           /* filter */
+#define BIO_TYPE_CONNECT (12 | 0x0400 | 0x0100) /* socket - connect */
+#define BIO_TYPE_ACCEPT (13 | 0x0400 | 0x0100)  /* socket for accept */
+#define BIO_TYPE_PROXY_CLIENT (14 | 0x0200)     /* client proxy BIO */
+#define BIO_TYPE_PROXY_SERVER (15 | 0x0200)     /* server proxy BIO */
+#define BIO_TYPE_NBIO_TEST (16 | 0x0200)        /* server proxy BIO */
+#define BIO_TYPE_NULL_FILTER (17 | 0x0200)
+#define BIO_TYPE_BER (18 | 0x0200)        /* BER -> bin filter */
+#define BIO_TYPE_BIO (19 | 0x0400)        /* (half a) BIO pair */
+#define BIO_TYPE_LINEBUFFER (20 | 0x0200) /* filter */
+#define BIO_TYPE_DGRAM (21 | 0x0400 | 0x0100)
+#define BIO_TYPE_ASN1 (22 | 0x0200) /* filter */
+#define BIO_TYPE_COMP (23 | 0x0200) /* filter */
+
+#define BIO_TYPE_DESCRIPTOR 0x0100 /* socket, fd, connect or accept */
+#define BIO_TYPE_FILTER 0x0200
+#define BIO_TYPE_SOURCE_SINK 0x0400
+
+struct bio_method_st {
+  int type;
+  const char *name;
+  int (*bwrite)(BIO *, const char *, int);
+  int (*bread)(BIO *, char *, int);
+  /* TODO(fork): remove bputs. */
+  int (*bputs)(BIO *, const char *);
+  int (*bgets)(BIO *, char *, int);
+  long (*ctrl)(BIO *, int, long, void *);
+  int (*create)(BIO *);
+  int (*destroy)(BIO *);
+  long (*callback_ctrl)(BIO *, int, bio_info_cb);
+};
+
+struct bio_st {
+  const BIO_METHOD *method;
+  /* bio, mode, argp, argi, argl, ret */
+  long (*callback)(struct bio_st *, int, const char *, int, long, long);
+  char *cb_arg; /* first argument for the callback */
+
+  /* init is non-zero if this |BIO| has been initialised. */
+  int init;
+  /* shutdown is often used by specific |BIO_METHOD|s to determine whether
+   * they own some underlying resource. This flag can often by controlled by
+   * |BIO_set_close|. For example, whether an fd BIO closes the underlying fd
+   * when it, itself, is closed. */
+  int shutdown;
+  int flags;
+  int retry_reason;
+  /* num is a BIO-specific value. For example, in fd BIOs it's used to store a
+   * file descriptor. */
+  int num;
+  /* TODO(fork): reference counting is only used by the SSL BIO code. If we can
+   * dump that then we can remove this. We could also drop
+   * BIO_CTRL_PUSH/BIO_CTRL_POP. */
+  int references;
+  void *ptr;
+  /* next_bio points to the next |BIO| in a chain. This |BIO| owns a reference
+   * to |next_bio|. */
+  struct bio_st *next_bio; /* used by filter BIOs */
+  /* TODO(fork): either bring back BIO_number_read and write or remove these. */
+  unsigned long num_read;
+  unsigned long num_write;
+
+  CRYPTO_EX_DATA ex_data;
+};
+
+#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_CONNECT			123
+#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_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
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define BIO_F_bio_make_pair 100
+#define BIO_F_bio_ctrl 101
+#define BIO_F_buffer_ctrl 102
+#define BIO_F_BIO_new_file 103
+#define BIO_F_file_read 104
+#define BIO_F_BIO_new 105
+#define BIO_F_bio_io 106
+#define BIO_F_BIO_new_mem_buf 107
+#define BIO_F_mem_write 108
+#define BIO_F_conn_state 109
+#define BIO_F_conn_ctrl 110
+#define BIO_F_file_ctrl 111
+#define BIO_F_BIO_callback_ctrl 112
+#define BIO_F_bio_ip_and_port_to_socket_and_addr 113
+#define BIO_F_bio_write 114
+#define BIO_F_BIO_ctrl 115
+#define BIO_R_UNSUPPORTED_METHOD 100
+#define BIO_R_NO_PORT_SPECIFIED 101
+#define BIO_R_NO_HOSTNAME_SPECIFIED 102
+#define BIO_R_IN_USE 103
+#define BIO_R_UNINITIALIZED 104
+#define BIO_R_CONNECT_ERROR 105
+#define BIO_R_KEEPALIVE 106
+#define BIO_R_BROKEN_PIPE 107
+#define BIO_R_NBIO_CONNECT_ERROR 108
+#define BIO_R_BAD_FOPEN_MODE 109
+#define BIO_R_ASN1_OBJECT_TOO_LONG 110
+#define BIO_R_INVALID_ARGUMENT 111
+#define BIO_R_WRITE_TO_READ_ONLY_BIO 112
+#define BIO_R_ERROR_SETTING_NBIO 113
+#define BIO_R_SYS_LIB 114
+#define BIO_R_NO_SUCH_FILE 115
+#define BIO_R_NULL_PARAMETER 116
+#define BIO_R_UNABLE_TO_CREATE_SOCKET 117
+
+#endif  /* OPENSSL_HEADER_BIO_H */
diff --git a/crypto/bio/bio_error.c b/crypto/bio/bio_error.c
new file mode 100644
index 0000000..f4ce8f8
--- /dev/null
+++ b/crypto/bio/bio_error.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "bio.h"
+
+const ERR_STRING_DATA BIO_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_callback_ctrl, 0), "BIO_callback_ctrl"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ctrl, 0), "BIO_ctrl"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_new, 0), "BIO_new"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_new_file, 0), "BIO_new_file"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_new_mem_buf, 0), "BIO_new_mem_buf"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_bio_ctrl, 0), "bio_ctrl"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_bio_io, 0), "bio_io"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_bio_ip_and_port_to_socket_and_addr, 0), "bio_ip_and_port_to_socket_and_addr"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_bio_make_pair, 0), "bio_make_pair"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_bio_write, 0), "bio_write"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_buffer_ctrl, 0), "buffer_ctrl"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_conn_ctrl, 0), "conn_ctrl"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_conn_state, 0), "conn_state"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_file_ctrl, 0), "file_ctrl"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_file_read, 0), "file_read"},
+  {ERR_PACK(ERR_LIB_BIO, BIO_F_mem_write, 0), "mem_write"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_ASN1_OBJECT_TOO_LONG), "ASN1_OBJECT_TOO_LONG"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_BAD_FOPEN_MODE), "BAD_FOPEN_MODE"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_BROKEN_PIPE), "BROKEN_PIPE"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_CONNECT_ERROR), "CONNECT_ERROR"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_ERROR_SETTING_NBIO), "ERROR_SETTING_NBIO"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_INVALID_ARGUMENT), "INVALID_ARGUMENT"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_IN_USE), "IN_USE"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_KEEPALIVE), "KEEPALIVE"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NBIO_CONNECT_ERROR), "NBIO_CONNECT_ERROR"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_HOSTNAME_SPECIFIED), "NO_HOSTNAME_SPECIFIED"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_PORT_SPECIFIED), "NO_PORT_SPECIFIED"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_SUCH_FILE), "NO_SUCH_FILE"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NULL_PARAMETER), "NULL_PARAMETER"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_SYS_LIB), "SYS_LIB"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_CREATE_SOCKET), "UNABLE_TO_CREATE_SOCKET"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNINITIALIZED), "UNINITIALIZED"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNSUPPORTED_METHOD), "UNSUPPORTED_METHOD"},
+  {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_WRITE_TO_READ_ONLY_BIO), "WRITE_TO_READ_ONLY_BIO"},
+  {0, NULL},
+};
diff --git a/crypto/bio/bio_mem.c b/crypto/bio/bio_mem.c
new file mode 100644
index 0000000..4d047cf
--- /dev/null
+++ b/crypto/bio/bio_mem.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <limits.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+BIO *BIO_new_mem_buf(void *buf, int len) {
+  BIO *ret;
+  BUF_MEM *b;
+  const size_t size = len < 0 ? strlen((char *)buf) : (size_t)len;
+
+  if (!buf && len != 0) {
+    OPENSSL_PUT_ERROR(BIO, BIO_new_mem_buf, BIO_R_NULL_PARAMETER);
+    return NULL;
+  }
+
+  ret = BIO_new(BIO_s_mem());
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  b = (BUF_MEM *)ret->ptr;
+  b->data = buf;
+  b->length = size;
+  b->max = size;
+
+  ret->flags |= BIO_FLAGS_MEM_RDONLY;
+
+  /* |num| is used to store the value that this BIO will return when it runs
+   * out of data. If it's negative then the retry flags will also be set. Since
+   * this is static data, retrying wont help */
+  ret->num = 0;
+
+  return ret;
+}
+
+static int mem_new(BIO *bio) {
+  BUF_MEM *b;
+
+  b = BUF_MEM_new();
+  if (b == NULL) {
+    return 0;
+  }
+
+  /* |shutdown| is used to store the close flag: whether the BIO has ownership
+   * of the BUF_MEM. */
+  bio->shutdown = 1;
+  bio->init = 1;
+  bio->num = -1;
+  bio->ptr = (char *)b;
+
+  return 1;
+}
+
+static int mem_free(BIO *bio) {
+  BUF_MEM *b;
+
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (!bio->shutdown || !bio->init || bio->ptr == NULL) {
+    return 1;
+  }
+
+  b = (BUF_MEM *)bio->ptr;
+  if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
+    b->data = NULL;
+  }
+  BUF_MEM_free(b);
+  bio->ptr = NULL;
+  return 1;
+}
+
+static int mem_read(BIO *bio, char *out, int outl) {
+  int ret = -1;
+  BUF_MEM *b;
+
+  b = (BUF_MEM *)bio->ptr;
+  BIO_clear_retry_flags(bio);
+  ret = outl;
+  if (ret > (int)b->length) {
+    ret = b->length;
+  }
+
+  if (ret > 0) {
+    memcpy(out, b->data, ret);
+    b->length -= ret;
+    if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
+      b->data += ret;
+    } else {
+      memmove(b->data, &b->data[ret], b->length);
+    }
+  } else if (b->length == 0) {
+    ret = bio->num;
+    if (ret != 0) {
+      BIO_set_retry_read(bio);
+    }
+  }
+  return ret;
+}
+
+static int mem_write(BIO *bio, const char *in, int inl) {
+  int ret = -1;
+  int blen;
+  BUF_MEM *b;
+
+  b = (BUF_MEM *)bio->ptr;
+
+  if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
+    OPENSSL_PUT_ERROR(BIO, mem_write, BIO_R_WRITE_TO_READ_ONLY_BIO);
+    goto err;
+  }
+
+  BIO_clear_retry_flags(bio);
+  blen = b->length;
+  if (INT_MAX - blen < inl) {
+    goto err;
+  }
+  if (BUF_MEM_grow_clean(b, blen + inl) != (blen + inl)) {
+    goto err;
+  }
+  memcpy(&b->data[blen], in, inl);
+  ret = inl;
+
+err:
+  return ret;
+}
+
+static int mem_puts(BIO *bp, const char *str) {
+  return mem_write(bp, str, strlen(str));
+}
+
+static int mem_gets(BIO *bio, char *buf, int size) {
+  int i, j;
+  char *p;
+  BUF_MEM *b = (BUF_MEM *)bio->ptr;
+
+  BIO_clear_retry_flags(bio);
+  j = b->length;
+  if (size - 1 < j) {
+    j = size - 1;
+  }
+  if (j <= 0) {
+    if (size > 0) {
+      *buf = 0;
+    }
+    return 0;
+  }
+
+  p = b->data;
+  for (i = 0; i < j; i++) {
+    if (p[i] == '\n') {
+      i++;
+      break;
+    }
+  }
+
+  /* i is now the max num of bytes to copy, either j or up to and including the
+   * first newline */
+
+  i = mem_read(bio, buf, i);
+  if (i > 0) {
+    buf[i] = '\0';
+  }
+  return i;
+}
+
+static long mem_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+  long ret = 1;
+  char **pptr;
+
+  BUF_MEM *b = (BUF_MEM *)bio->ptr;
+
+  switch (cmd) {
+    case BIO_CTRL_RESET:
+      if (b->data != NULL) {
+        /* For read only case reset to the start again */
+        if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
+          b->data -= b->max - b->length;
+          b->length = b->max;
+        } else {
+          memset(b->data, 0, b->max);
+          b->length = 0;
+        }
+      }
+      break;
+    case BIO_CTRL_EOF:
+      ret = (long)(b->length == 0);
+      break;
+    case BIO_C_SET_BUF_MEM_EOF_RETURN:
+      bio->num = (int)num;
+      break;
+    case BIO_CTRL_INFO:
+      ret = (long)b->length;
+      if (ptr != NULL) {
+        pptr = (char **)ptr;
+        *pptr = (char *)&(b->data[0]);
+      }
+      break;
+    case BIO_C_SET_BUF_MEM:
+      mem_free(bio);
+      bio->shutdown = (int)num;
+      bio->ptr = ptr;
+      break;
+    case BIO_C_GET_BUF_MEM_PTR:
+      if (ptr != NULL) {
+        pptr = (char **)ptr;
+        *pptr = (char *)b;
+      }
+      break;
+    case BIO_CTRL_GET_CLOSE:
+      ret = (long)bio->shutdown;
+      break;
+    case BIO_CTRL_SET_CLOSE:
+      bio->shutdown = (int)num;
+      break;
+
+    case BIO_CTRL_WPENDING:
+      ret = 0L;
+      break;
+    case BIO_CTRL_PENDING:
+      ret = (long)b->length;
+      break;
+    case BIO_CTRL_FLUSH:
+      ret = 1;
+      break;
+    default:
+      ret = 0;
+      break;
+  }
+  return ret;
+}
+
+static const BIO_METHOD mem_method = {
+    BIO_TYPE_MEM, "memory buffer", mem_write, mem_read, mem_puts,
+    mem_gets,     mem_ctrl,        mem_new,   mem_free, NULL, };
+
+const BIO_METHOD *BIO_s_mem(void) { return &mem_method; }
+
+long BIO_get_mem_data(BIO *bio, char **contents) {
+  return BIO_ctrl(bio, BIO_CTRL_INFO, 0, (char *) contents);
+}
+
+int BIO_get_mem_ptr(BIO *bio, BUF_MEM **out) {
+  return BIO_ctrl(bio, BIO_C_GET_BUF_MEM_PTR, 0, (char *) out);
+}
+
+int BIO_set_mem_buf(BIO *bio, BUF_MEM *b, int take_ownership) {
+  return BIO_ctrl(bio, BIO_C_SET_BUF_MEM, take_ownership, (char *) b);
+}
+
+int BIO_set_mem_eof_return(BIO *bio, int eof_value) {
+  return BIO_ctrl(bio, BIO_C_SET_BUF_MEM_EOF_RETURN, eof_value, NULL);
+}
diff --git a/crypto/bio/bio_test.c b/crypto/bio/bio_test.c
new file mode 100644
index 0000000..a38674f
--- /dev/null
+++ b/crypto/bio/bio_test.c
@@ -0,0 +1,93 @@
+/* TODO(fork): license */
+
+#define _BSD_SOURCE
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+
+
+static int test_socket_connect() {
+  int listening_sock = socket(AF_INET, SOCK_STREAM, 0);
+  int sock;
+  struct sockaddr_in sin;
+  socklen_t sockaddr_len = sizeof(sin);
+  static const char kTestMessage[] = "test";
+  char hostname[80], buf[5];
+  BIO *bio;
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  if (!inet_aton("127.0.0.1", &sin.sin_addr)) {
+    perror("inet_aton");
+    return 0;
+  }
+
+  if (bind(listening_sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
+    perror("bind");
+    return 0;
+  }
+
+  if (listen(listening_sock, 1)) {
+    perror("listen");
+    return 0;
+  }
+
+  if (getsockname(listening_sock, (struct sockaddr *)&sin, &sockaddr_len) ||
+      sockaddr_len != sizeof(sin)) {
+    perror("getsockname");
+    return 0;
+  }
+
+  snprintf(hostname, sizeof(hostname), "%s:%d", "127.0.0.1",
+           ntohs(sin.sin_port));
+  bio = BIO_new_connect(hostname);
+  if (!bio) {
+    fprintf(stderr, "BIO_new_connect failed.\n");
+    return 0;
+  }
+
+  if (BIO_write(bio, kTestMessage, sizeof(kTestMessage)) !=
+      sizeof(kTestMessage)) {
+    fprintf(stderr, "BIO_write failed.\n");
+    BIO_print_errors_fp(stderr);
+    return 0;
+  }
+
+  sock = accept(listening_sock, (struct sockaddr *) &sin, &sockaddr_len);
+  if (sock < 0) {
+    perror("accept");
+    return 0;
+  }
+
+  if (read(sock, buf, sizeof(buf)) != sizeof(kTestMessage)) {
+    perror("read");
+    return 0;
+  }
+
+  if (memcmp(buf, kTestMessage, sizeof(kTestMessage))) {
+    return 0;
+  }
+
+  close(sock);
+  close(listening_sock);
+  BIO_free(bio);
+
+  return 1;
+}
+
+int main() {
+  ERR_load_crypto_strings();
+
+  if (!test_socket_connect()) {
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/bio/buffer.c b/crypto/bio/buffer.c
new file mode 100644
index 0000000..144afd2
--- /dev/null
+++ b/crypto/bio/buffer.c
@@ -0,0 +1,497 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+#define DEFAULT_BUFFER_SIZE 4096
+
+typedef struct bio_f_buffer_ctx_struct {
+  /* Buffers are setup like this:
+   *
+   * <---------------------- size ----------------------->
+   * +---------------------------------------------------+
+   * | consumed | remaining          | free space        |
+   * +---------------------------------------------------+
+   * <-- off --><------- len ------->
+   */
+
+  int ibuf_size;  /* how big is the input buffer */
+  int obuf_size;  /* how big is the output buffer */
+
+  char *ibuf;   /* the char array */
+  int ibuf_len; /* how many bytes are in it */
+  int ibuf_off; /* write/read offset */
+
+  char *obuf;   /* the char array */
+  int obuf_len; /* how many bytes are in it */
+  int obuf_off; /* write/read offset */
+} BIO_F_BUFFER_CTX;
+
+static int buffer_new(BIO *bio) {
+  BIO_F_BUFFER_CTX *ctx;
+
+  ctx = OPENSSL_malloc(sizeof(BIO_F_BUFFER_CTX));
+  if (ctx == NULL) {
+    return 0;
+  }
+  memset(ctx, 0, sizeof(BIO_F_BUFFER_CTX));
+
+  ctx->ibuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
+  if (ctx->ibuf == NULL) {
+    goto err1;
+  }
+  ctx->obuf = (char *)OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
+  if (ctx->obuf == NULL) {
+    goto err2;
+  }
+  ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
+  ctx->obuf_size = DEFAULT_BUFFER_SIZE;
+
+  bio->init = 1;
+  bio->ptr = (char *)ctx;
+  return 1;
+
+err2:
+  OPENSSL_free(ctx->ibuf);
+
+err1:
+  OPENSSL_free(ctx);
+  return 0;
+}
+
+static int buffer_free(BIO *bio) {
+  BIO_F_BUFFER_CTX *ctx;
+
+  if (bio == NULL) {
+    return 0;
+  }
+
+  ctx = (BIO_F_BUFFER_CTX *)bio->ptr;
+  if (ctx->ibuf != NULL) {
+    OPENSSL_free(ctx->ibuf);
+  }
+  if (ctx->obuf != NULL) {
+    OPENSSL_free(ctx->obuf);
+  }
+  OPENSSL_free(bio->ptr);
+
+  bio->ptr = NULL;
+  bio->init = 0;
+  bio->flags = 0;
+
+  return 1;
+}
+
+static int buffer_read(BIO *bio, char *out, int outl) {
+  int i, num = 0;
+  BIO_F_BUFFER_CTX *ctx;
+
+  ctx = (BIO_F_BUFFER_CTX *)bio->ptr;
+
+  if (ctx == NULL || bio->next_bio == NULL) {
+    return 0;
+  }
+
+  num = 0;
+  BIO_clear_retry_flags(bio);
+
+  for (;;) {
+    i = ctx->ibuf_len;
+    /* If there is stuff left over, grab it */
+    if (i != 0) {
+      if (i > outl) {
+        i = outl;
+      }
+      memcpy(out, &ctx->ibuf[ctx->ibuf_off], i);
+      ctx->ibuf_off += i;
+      ctx->ibuf_len -= i;
+      num += i;
+      if (outl == i) {
+        return num;
+      }
+      outl -= i;
+      out += i;
+    }
+
+    /* We may have done a partial read. Try to do more. We have nothing in the
+     * buffer. If we get an error and have read some data, just return it and
+     * let them retry to get the error again. Copy direct to parent address
+     * space */
+    if (outl > ctx->ibuf_size) {
+      for (;;) {
+        i = BIO_read(bio->next_bio, out, outl);
+        if (i <= 0) {
+          BIO_copy_next_retry(bio);
+          if (i < 0) {
+            return (num > 0) ? num : i;
+          }
+          return num;
+        }
+        num += i;
+        if (outl == i) {
+          return num;
+        }
+        out += i;
+        outl -= i;
+      }
+    }
+    /* else */
+
+    /* we are going to be doing some buffering */
+    i = BIO_read(bio->next_bio, ctx->ibuf, ctx->ibuf_size);
+    if (i <= 0) {
+      BIO_copy_next_retry(bio);
+      if (i < 0) {
+        return (num > 0) ? num : i;
+      }
+      return num;
+    }
+    ctx->ibuf_off = 0;
+    ctx->ibuf_len = i;
+  }
+}
+
+static int buffer_write(BIO *b, const char *in, int inl) {
+  int i, num = 0;
+  BIO_F_BUFFER_CTX *ctx;
+
+  ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+  if (ctx == NULL || b->next_bio == NULL) {
+    return 0;
+  }
+
+  BIO_clear_retry_flags(b);
+
+  for (;;) {
+    i = ctx->obuf_size - (ctx->obuf_off + ctx->obuf_len);
+    /* add to buffer and return */
+    if (i >= inl) {
+      memcpy(&ctx->obuf[ctx->obuf_off + ctx->obuf_len], in, inl);
+      ctx->obuf_len += inl;
+      return num + inl;
+    }
+    /* else */
+    /* stuff already in buffer, so add to it first, then flush */
+    if (ctx->obuf_len != 0) {
+      if (i > 0) {
+        memcpy(&ctx->obuf[ctx->obuf_off + ctx->obuf_len], in, i);
+        in += i;
+        inl -= i;
+        num += i;
+        ctx->obuf_len += i;
+      }
+
+      /* we now have a full buffer needing flushing */
+      for (;;) {
+        i = BIO_write(b->next_bio, &ctx->obuf[ctx->obuf_off], ctx->obuf_len);
+        if (i <= 0) {
+          BIO_copy_next_retry(b);
+
+          if (i < 0) {
+            return (num > 0) ? num : i;
+          }
+          return num;
+        }
+        ctx->obuf_off += i;
+        ctx->obuf_len -= i;
+        if (ctx->obuf_len == 0) {
+          break;
+        }
+      }
+    }
+
+    /* we only get here if the buffer has been flushed and we
+     * still have stuff to write */
+    ctx->obuf_off = 0;
+
+    /* we now have inl bytes to write */
+    while (inl >= ctx->obuf_size) {
+      i = BIO_write(b->next_bio, in, inl);
+      if (i <= 0) {
+        BIO_copy_next_retry(b);
+        if (i < 0) {
+          return (num > 0) ? num : i;
+        }
+        return num;
+      }
+      num += i;
+      in += i;
+      inl -= i;
+      if (inl == 0) {
+        return num;
+      }
+    }
+
+    /* copy the rest into the buffer since we have only a small
+     * amount left */
+  }
+}
+
+static long buffer_ctrl(BIO *b, int cmd, long num, void *ptr) {
+  BIO_F_BUFFER_CTX *ctx;
+  long ret = 1;
+  char *p1, *p2;
+  int r, *ip;
+  int ibs, obs;
+
+  ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+
+  switch (cmd) {
+    case BIO_CTRL_RESET:
+      ctx->ibuf_off = 0;
+      ctx->ibuf_len = 0;
+      ctx->obuf_off = 0;
+      ctx->obuf_len = 0;
+      if (b->next_bio == NULL) {
+        return 0;
+      }
+      ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+      break;
+
+    case BIO_CTRL_INFO:
+      ret = ctx->obuf_len;
+      break;
+
+    case BIO_CTRL_WPENDING:
+      ret = (long)ctx->obuf_len;
+      if (ret == 0) {
+        if (b->next_bio == NULL)
+          return 0;
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+      }
+      break;
+
+    case BIO_CTRL_PENDING:
+      ret = (long)ctx->ibuf_len;
+      if (ret == 0) {
+        if (b->next_bio == NULL) {
+          return 0;
+        }
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+      }
+      break;
+
+    case BIO_C_SET_BUFF_SIZE:
+      ip = (int *)ptr;
+      if (*ip == 0) {
+        ibs = (int)num;
+        obs = ctx->obuf_size;
+      } else /* if (*ip == 1) */ {
+        ibs = ctx->ibuf_size;
+        obs = (int)num;
+      }
+      p1 = ctx->ibuf;
+      p2 = ctx->obuf;
+      if (ibs > DEFAULT_BUFFER_SIZE && ibs != ctx->ibuf_size) {
+        p1 = (char *)OPENSSL_malloc(ibs);
+        if (p1 == NULL) {
+          goto malloc_error;
+        }
+      }
+      if (obs > DEFAULT_BUFFER_SIZE && obs != ctx->obuf_size) {
+        p2 = (char *)OPENSSL_malloc(obs);
+        if (p2 == NULL) {
+          if (p1 != ctx->ibuf) {
+            OPENSSL_free(p1);
+          }
+          goto malloc_error;
+        }
+      }
+
+      if (ctx->ibuf != p1) {
+        OPENSSL_free(ctx->ibuf);
+        ctx->ibuf = p1;
+        ctx->ibuf_size = ibs;
+      }
+      ctx->ibuf_off = 0;
+      ctx->ibuf_len = 0;
+
+      if (ctx->obuf != p2) {
+        OPENSSL_free(ctx->obuf);
+        ctx->obuf = p2;
+        ctx->obuf_size = obs;
+      }
+      ctx->obuf_off = 0;
+      ctx->obuf_len = 0;
+      break;
+
+    case BIO_CTRL_FLUSH:
+      if (b->next_bio == NULL) {
+        return 0;
+      }
+
+      while (ctx->obuf_len > 0) {
+        BIO_clear_retry_flags(b);
+        r = BIO_write(b->next_bio, &(ctx->obuf[ctx->obuf_off]),
+                      ctx->obuf_len);
+        BIO_copy_next_retry(b);
+        if (r <= 0) {
+          return r;
+        }
+        ctx->obuf_off += r;
+        ctx->obuf_len -= r;
+      }
+
+      ctx->obuf_len = 0;
+      ctx->obuf_off = 0;
+      ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+      break;
+
+    default:
+      if (b->next_bio == NULL) {
+        return 0;
+      }
+      BIO_clear_retry_flags(b);
+      ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+      BIO_copy_next_retry(b);
+      break;
+  }
+  return ret;
+
+malloc_error:
+  OPENSSL_PUT_ERROR(BIO, buffer_ctrl, ERR_R_MALLOC_FAILURE);
+  return 0;
+}
+
+static long buffer_callback_ctrl(BIO *b, int cmd, bio_info_cb fp) {
+  long ret = 1;
+
+  if (b->next_bio == NULL) {
+    return 0;
+  }
+
+  switch (cmd) {
+    default:
+      ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
+      break;
+  }
+  return ret;
+}
+
+static int buffer_gets(BIO *b, char *buf, int size) {
+  BIO_F_BUFFER_CTX *ctx;
+  int num = 0, i, flag;
+  char *p;
+
+  ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+  if (buf == NULL || size <= 0) {
+    return 0;
+  }
+
+  size--; /* reserve space for a '\0' */
+  BIO_clear_retry_flags(b);
+
+  for (;;) {
+    if (ctx->ibuf_len > 0) {
+      p = &ctx->ibuf[ctx->ibuf_off];
+      flag = 0;
+      for (i = 0; (i < ctx->ibuf_len) && (i < size); i++) {
+        *(buf++) = p[i];
+        if (p[i] == '\n') {
+          flag = 1;
+          i++;
+          break;
+        }
+      }
+      num += i;
+      size -= i;
+      ctx->ibuf_len -= i;
+      ctx->ibuf_off += i;
+      if (flag || size == 0) {
+        *buf = '\0';
+        return num;
+      }
+    } else /* read another chunk */
+    {
+      i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
+      if (i <= 0) {
+        BIO_copy_next_retry(b);
+        *buf = '\0';
+        if (i < 0) {
+          return (num > 0) ? num : i;
+        }
+        return num;
+      }
+      ctx->ibuf_len = i;
+      ctx->ibuf_off = 0;
+    }
+  }
+}
+
+static int buffer_puts(BIO *b, const char *str) {
+  return buffer_write(b, str, strlen(str));
+}
+
+static BIO_METHOD methods_buffer = {
+    BIO_TYPE_BUFFER, "buffer",             buffer_write, buffer_read,
+    buffer_puts,     buffer_gets,          buffer_ctrl,  buffer_new,
+    buffer_free,     buffer_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_buffer(void) { return &methods_buffer; }
+
+int BIO_set_read_buffer_size(BIO *bio, int buffer_size) {
+  return BIO_int_ctrl(bio, BIO_C_SET_BUFF_SIZE, buffer_size, 0);
+}
+
+int BIO_set_write_buffer_size(BIO *bio, int buffer_size) {
+  return BIO_int_ctrl(bio, BIO_C_SET_BUFF_SIZE, buffer_size, 1);
+}
diff --git a/crypto/bio/connect.c b/crypto/bio/connect.c
new file mode 100644
index 0000000..3798a1c
--- /dev/null
+++ b/crypto/bio/connect.c
@@ -0,0 +1,527 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+enum {
+  BIO_CONN_S_BEFORE,
+  BIO_CONN_S_BLOCKED_CONNECT,
+  BIO_CONN_S_OK,
+};
+
+typedef struct bio_connect_st {
+  int state;
+
+  char *param_hostname;
+  char *param_port;
+  int nbio;
+
+  uint8_t ip[4];
+  unsigned short port;
+
+  struct sockaddr_storage them;
+
+  /* the file descriptor is kept in bio->num in order to match the socket
+   * BIO. */
+
+  /* info_callback is called when the connection is initially made
+   * callback(BIO,state,ret);  The callback should return 'ret', state is for
+   * compatibility with the SSL info_callback. */
+  int (*info_callback)(const BIO *bio, int state, int ret);
+} BIO_CONNECT;
+
+/* maybe_copy_ipv4_address sets |*ipv4| to the IPv4 address from |ss| (in
+ * big-endian order), if |ss| contains an IPv4 socket address. */
+static void maybe_copy_ipv4_address(uint8_t *ipv4,
+                                    const struct sockaddr_storage *ss) {
+  const struct sockaddr_in *sin;
+
+  if (ss->ss_family != AF_INET) {
+    return;
+  }
+
+  sin = (const struct sockaddr_in*) ss;
+  memcpy(ipv4, &sin->sin_addr, 4);
+}
+
+static int conn_state(BIO *bio, BIO_CONNECT *c) {
+  int ret = -1, i;
+  char *p, *q;
+  int (*cb)(const BIO *, int, int) = NULL;
+
+  if (c->info_callback != NULL) {
+    cb = c->info_callback;
+  }
+
+  for (;;) {
+    switch (c->state) {
+      case BIO_CONN_S_BEFORE:
+        p = c->param_hostname;
+        if (p == NULL) {
+          OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_NO_HOSTNAME_SPECIFIED);
+          goto exit_loop;
+        }
+        for (; *p != 0; p++) {
+          if (*p == ':' || *p == '/') {
+            break;
+          }
+        }
+
+        i = *p;
+        if (i == ':' || i == '/') {
+          *(p++) = 0;
+          if (i == ':') {
+            for (q = p; *q; q++) {
+              if (*q == '/') {
+                *q = 0;
+                break;
+              }
+            }
+            if (c->param_port != NULL) {
+              OPENSSL_free(c->param_port);
+            }
+            c->param_port = BUF_strdup(p);
+          }
+        }
+
+        if (c->param_port == NULL) {
+          OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_NO_PORT_SPECIFIED);
+          ERR_add_error_data(2, "host=", c->param_hostname);
+          goto exit_loop;
+        }
+
+        if (!bio_ip_and_port_to_socket_and_addr(
+                &bio->num, &c->them, c->param_hostname, c->param_port)) {
+          OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_UNABLE_TO_CREATE_SOCKET);
+          ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port);
+          goto exit_loop;
+        }
+
+        memset(c->ip, 0, 4);
+        maybe_copy_ipv4_address(c->ip, &c->them);
+
+        if (c->nbio) {
+          if (!bio_socket_nbio(bio->num, 1)) {
+            OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_ERROR_SETTING_NBIO);
+            ERR_add_error_data(4, "host=", c->param_hostname, ":",
+                               c->param_port);
+            goto exit_loop;
+          }
+        }
+
+        i = 1;
+        ret = setsockopt(bio->num, SOL_SOCKET, SO_KEEPALIVE, (char *)&i,
+                         sizeof(i));
+        if (ret < 0) {
+          OPENSSL_PUT_SYSTEM_ERROR(setsockopt);
+          OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_KEEPALIVE);
+          ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port);
+          goto exit_loop;
+        }
+
+        BIO_clear_retry_flags(bio);
+        ret = connect(bio->num, (struct sockaddr*) &c->them, sizeof(c->them));
+        if (ret < 0) {
+          if (bio_fd_should_retry(ret)) {
+            BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY));
+            c->state = BIO_CONN_S_BLOCKED_CONNECT;
+            bio->retry_reason = BIO_RR_CONNECT;
+          } else {
+            OPENSSL_PUT_SYSTEM_ERROR(connect);
+            OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_CONNECT_ERROR);
+            ERR_add_error_data(4, "host=", c->param_hostname, ":",
+                               c->param_port);
+          }
+          goto exit_loop;
+        } else {
+          c->state = BIO_CONN_S_OK;
+        }
+        break;
+
+      case BIO_CONN_S_BLOCKED_CONNECT:
+        i = bio_sock_error(bio->num);
+        if (i) {
+          if (bio_fd_should_retry(ret)) {
+            BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY));
+            c->state = BIO_CONN_S_BLOCKED_CONNECT;
+            bio->retry_reason = BIO_RR_CONNECT;
+            ret = -1;
+          } else {
+            BIO_clear_retry_flags(bio);
+            OPENSSL_PUT_SYSTEM_ERROR(connect);
+            OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_NBIO_CONNECT_ERROR);
+            ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port);
+            ret = 0;
+          }
+          goto exit_loop;
+        } else {
+          c->state = BIO_CONN_S_OK;
+        }
+        break;
+
+      case BIO_CONN_S_OK:
+        ret = 1;
+        goto exit_loop;
+      default:
+        assert(0);
+        goto exit_loop;
+    }
+
+    if (cb != NULL) {
+      ret = cb((BIO *)bio, c->state, ret);
+      if (ret == 0) {
+        goto end;
+      }
+    }
+  }
+
+exit_loop:
+  if (cb != NULL) {
+    ret = cb((BIO *)bio, c->state, ret);
+  }
+
+end:
+  return ret;
+}
+
+static BIO_CONNECT *BIO_CONNECT_new(void) {
+  BIO_CONNECT *ret = OPENSSL_malloc(sizeof(BIO_CONNECT));
+
+  if (ret == NULL) {
+    return NULL;
+  }
+  memset(ret, 0, sizeof(BIO_CONNECT));
+
+  ret->state = BIO_CONN_S_BEFORE;
+  return ret;
+}
+
+static void BIO_CONNECT_free(BIO_CONNECT *c) {
+  if (c == NULL) {
+    return;
+  }
+
+  if (c->param_hostname != NULL) {
+    OPENSSL_free(c->param_hostname);
+  }
+  if (c->param_port != NULL) {
+    OPENSSL_free(c->param_port);
+  }
+  OPENSSL_free(c);
+}
+
+static int conn_new(BIO *bio) {
+  bio->init = 0;
+  bio->num = -1;
+  bio->flags = 0;
+  bio->ptr = (char *)BIO_CONNECT_new();
+  return bio->ptr != NULL;
+}
+
+static void conn_close_socket(BIO *bio) {
+  BIO_CONNECT *c = (BIO_CONNECT *) bio->ptr;
+
+  if (bio->num == -1) {
+    return;
+  }
+
+  /* Only do a shutdown if things were established */
+  if (c->state == BIO_CONN_S_OK) {
+    shutdown(bio->num, 2);
+  }
+  close(bio->num);
+  bio->num = -1;
+}
+
+static int conn_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (bio->shutdown) {
+    conn_close_socket(bio);
+  }
+
+  BIO_CONNECT_free((BIO_CONNECT*) bio->ptr);
+
+  return 1;
+}
+
+static int conn_read(BIO *bio, char *out, int out_len) {
+  int ret = 0;
+  BIO_CONNECT *data;
+
+  data = (BIO_CONNECT *)bio->ptr;
+  if (data->state != BIO_CONN_S_OK) {
+    ret = conn_state(bio, data);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  bio_clear_socket_error();
+  ret = recv(bio->num, out, out_len, 0);
+  BIO_clear_retry_flags(bio);
+  if (ret <= 0) {
+    if (bio_fd_should_retry(ret)) {
+      BIO_set_retry_read(bio);
+    }
+  }
+
+  return ret;
+}
+
+static int conn_write(BIO *bio, const char *in, int in_len) {
+  int ret;
+  BIO_CONNECT *data;
+
+  data = (BIO_CONNECT *)bio->ptr;
+  if (data->state != BIO_CONN_S_OK) {
+    ret = conn_state(bio, data);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  bio_clear_socket_error();
+  ret = send(bio->num, in, in_len, 0);
+  BIO_clear_retry_flags(bio);
+  if (ret <= 0) {
+    if (bio_fd_should_retry(ret)) {
+      BIO_set_retry_write(bio);
+    }
+  }
+
+  return ret;
+}
+
+static long conn_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+  int *ip;
+  const char **pptr;
+  long ret = 1;
+  BIO_CONNECT *data;
+
+  data = (BIO_CONNECT *)bio->ptr;
+
+  switch (cmd) {
+    case BIO_CTRL_RESET:
+      ret = 0;
+      data->state = BIO_CONN_S_BEFORE;
+      conn_close_socket(bio);
+      bio->flags = 0;
+      break;
+    case BIO_C_DO_STATE_MACHINE:
+      /* use this one to start the connection */
+      if (data->state != BIO_CONN_S_OK)
+        ret = (long)conn_state(bio, data);
+      else
+        ret = 1;
+      break;
+    case BIO_C_GET_CONNECT:
+      /* TODO(fork): can this be removed? (Or maybe this whole file). */
+      if (ptr != NULL) {
+        pptr = (const char **)ptr;
+        if (num == 0) {
+          *pptr = data->param_hostname;
+        } else if (num == 1) {
+          *pptr = data->param_port;
+        } else if (num == 2) {
+          *pptr = (char *) &data->ip[0];
+        } else if (num == 3) {
+          *((int *)ptr) = data->port;
+        }
+        if (!bio->init) {
+          *pptr = "not initialized";
+        }
+        ret = 1;
+      }
+      break;
+    case BIO_C_SET_CONNECT:
+      if (ptr != NULL) {
+        bio->init = 1;
+        if (num == 0) {
+          if (data->param_hostname != NULL) {
+            OPENSSL_free(data->param_hostname);
+          }
+          data->param_hostname = BUF_strdup(ptr);
+        } else if (num == 1) {
+          if (data->param_port != NULL) {
+            OPENSSL_free(data->param_port);
+          }
+          data->param_port = BUF_strdup(ptr);
+        } else {
+          ret = 0;
+        }
+      }
+      break;
+    case BIO_C_SET_NBIO:
+      data->nbio = (int)num;
+      break;
+    case BIO_C_GET_FD:
+      if (bio->init) {
+        ip = (int *)ptr;
+        if (ip != NULL) {
+          *ip = bio->num;
+        }
+        ret = 1;
+      } else {
+        ret = 0;
+      }
+      break;
+    case BIO_CTRL_GET_CLOSE:
+      ret = bio->shutdown;
+      break;
+    case BIO_CTRL_SET_CLOSE:
+      bio->shutdown = (int)num;
+      break;
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+      ret = 0;
+      break;
+    case BIO_CTRL_FLUSH:
+      break;
+    case BIO_CTRL_SET_CALLBACK: {
+#if 0 /* FIXME: Should this be used?  -- Richard Levitte */
+		OPENSSL_PUT_ERROR(BIO, XXX, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+		ret = -1;
+#else
+      ret = 0;
+#endif
+    } break;
+    case BIO_CTRL_GET_CALLBACK: {
+      int (**fptr)(const BIO *bio, int state, int xret);
+      fptr = (int (**)(const BIO *bio, int state, int xret))ptr;
+      *fptr = data->info_callback;
+    } break;
+    default:
+      ret = 0;
+      break;
+  }
+  return (ret);
+}
+
+static long conn_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+  long ret = 1;
+  BIO_CONNECT *data;
+
+  data = (BIO_CONNECT *)bio->ptr;
+
+  switch (cmd) {
+    case BIO_CTRL_SET_CALLBACK: {
+      data->info_callback = (int (*)(const struct bio_st *, int, int))fp;
+    } break;
+    default:
+      ret = 0;
+      break;
+  }
+  return ret;
+}
+
+static int conn_puts(BIO *bp, const char *str) {
+  return conn_write(bp, str, strlen(str));
+}
+
+BIO *BIO_new_connect(const char *hostname) {
+  BIO *ret;
+
+  ret = BIO_new(BIO_s_connect());
+  if (ret == NULL) {
+    return NULL;
+  }
+  if (!BIO_set_conn_hostname(ret, hostname)) {
+    BIO_free(ret);
+    return NULL;
+  }
+  return ret;
+}
+
+static const BIO_METHOD methods_connectp = {
+    BIO_TYPE_CONNECT, "socket connect",         conn_write, conn_read,
+    conn_puts,        NULL /* connect_gets, */, conn_ctrl,  conn_new,
+    conn_free,        conn_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_s_connect(void) { return &methods_connectp; }
+
+int BIO_set_conn_hostname(BIO *bio, const char *name) {
+  return BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, (void*) name);
+}
+
+int BIO_set_conn_port(BIO *bio, const char *port_str) {
+  return BIO_ctrl(bio, BIO_C_SET_CONNECT, 1, (void*) port_str);
+}
+
+int BIO_set_nbio(BIO *bio, int on) {
+  return BIO_ctrl(bio, BIO_C_SET_NBIO, on, NULL);
+}
diff --git a/crypto/bio/fd.c b/crypto/bio/fd.c
new file mode 100644
index 0000000..c40056d
--- /dev/null
+++ b/crypto/bio/fd.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <errno.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <unistd.h>
+#endif
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+static int bio_fd_non_fatal_error(int err) {
+  if (
+#ifdef EWOULDBLOCK
+    err == EWOULDBLOCK ||
+#endif
+#ifdef WSAEWOULDBLOCK
+    err == WSAEWOULDBLOCK ||
+#endif
+#ifdef ENOTCONN
+    err == ENOTCONN ||
+#endif
+#ifdef EINTR
+    err == EINTR ||
+#endif
+#ifdef EAGAIN
+    err == EAGAIN ||
+#endif
+#ifdef EPROTO
+    err == EPROTO ||
+#endif
+#ifdef EINPROGRESS
+    err == EINPROGRESS ||
+#endif
+#ifdef EALREADY
+    err == EALREADY ||
+#endif
+    0) {
+    return 1;
+  }
+  return 0;
+}
+
+#if defined(WIN32)
+static int bio_fd_should_retry(int i) {
+  if (i == -1) {
+    return bio_fd_non_fatal_error((int)GetLastError());
+  }
+  return 0;
+}
+#else
+int bio_fd_should_retry(int i) {
+  if (i == -1) {
+    return bio_fd_non_fatal_error(errno);
+  }
+  return 0;
+}
+#endif
+
+BIO *BIO_new_fd(int fd, int close_flag) {
+  BIO *ret = BIO_new(BIO_s_fd());
+  if (ret == NULL) {
+    return NULL;
+  }
+  BIO_set_fd(ret, fd, close_flag);
+  return ret;
+}
+
+static int fd_new(BIO *bio) {
+  /* num is used to store the file descriptor. */
+  bio->num = -1;
+  return 1;
+}
+
+static int fd_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (bio->shutdown) {
+    if (bio->init) {
+      close(bio->num);
+    }
+    bio->init = 0;
+  }
+  return 1;
+}
+
+static int fd_read(BIO *b, char *out, int outl) {
+  int ret = 0;
+
+  ret = read(b->num, out, outl);
+  BIO_clear_retry_flags(b);
+  if (ret <= 0) {
+    if (bio_fd_should_retry(ret)) {
+      BIO_set_retry_read(b);
+    }
+  }
+
+  return ret;
+}
+
+static int fd_write(BIO *b, const char *in, int inl) {
+  int ret = write(b->num, in, inl);
+  BIO_clear_retry_flags(b);
+  if (ret <= 0) {
+    if (bio_fd_should_retry(ret)) {
+      BIO_set_retry_write(b);
+    }
+  }
+
+  return ret;
+}
+
+static long fd_ctrl(BIO *b, int cmd, long num, void *ptr) {
+  long ret = 1;
+  int *ip;
+
+  switch (cmd) {
+    case BIO_CTRL_RESET:
+      num = 0;
+    case BIO_C_FILE_SEEK:
+      ret = 0;
+      if (b->init) {
+        ret = (long)lseek(b->num, num, SEEK_SET);
+      }
+      break;
+    case BIO_C_FILE_TELL:
+    case BIO_CTRL_INFO:
+      ret = 0;
+      if (b->init) {
+        ret = (long)lseek(b->num, 0, SEEK_CUR);
+      }
+      break;
+    case BIO_C_SET_FD:
+      fd_free(b);
+      b->num = *((int *)ptr);
+      b->shutdown = (int)num;
+      b->init = 1;
+      break;
+    case BIO_C_GET_FD:
+      if (b->init) {
+        ip = (int *)ptr;
+        if (ip != NULL) {
+          *ip = b->num;
+        }
+        return 1;
+      } else {
+        ret = 0;
+      }
+      break;
+    case BIO_CTRL_GET_CLOSE:
+      ret = b->shutdown;
+      break;
+    case BIO_CTRL_SET_CLOSE:
+      b->shutdown = (int)num;
+      break;
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+      ret = 0;
+      break;
+    case BIO_CTRL_FLUSH:
+      ret = 1;
+      break;
+    default:
+      ret = 0;
+      break;
+  }
+
+  return ret;
+}
+
+static int fd_puts(BIO *bp, const char *str) {
+  return fd_write(bp, str, strlen(str));
+}
+
+static int fd_gets(BIO *bp, char *buf, int size) {
+  char *ptr = buf;
+  char *end = buf + size - 1;
+
+  if (size <= 0) {
+    return 0;
+  }
+
+  while (ptr < end && fd_read(bp, ptr, 1) > 0 && ptr[0] != '\n') {
+    ptr++;
+  }
+
+  ptr[0] = '\0';
+
+  return ptr - buf;
+}
+
+static const BIO_METHOD methods_fdp = {
+    BIO_TYPE_FD, "file descriptor", fd_write, fd_read, fd_puts,
+    fd_gets,     fd_ctrl,           fd_new,   fd_free, NULL, };
+
+const BIO_METHOD *BIO_s_fd(void) { return &methods_fdp; }
+
+int BIO_set_fd(BIO *bio, int fd, int close_flag) {
+  return BIO_int_ctrl(bio, BIO_C_SET_FD, close_flag, fd);
+}
+
+int BIO_get_fd(BIO *bio, int *out_fd) {
+  return BIO_ctrl(bio, BIO_C_GET_FD, 0, (char *) out_fd);
+}
diff --git a/crypto/bio/file.c b/crypto/bio/file.c
new file mode 100644
index 0000000..e1a67ca
--- /dev/null
+++ b/crypto/bio/file.c
@@ -0,0 +1,349 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#if defined(__linux) || defined(__sun) || defined(__hpux)
+/* Following definition aliases fopen to fopen64 on above mentioned
+ * platforms. This makes it possible to open and sequentially access
+ * files larger than 2GB from 32-bit application. It does not allow to
+ * traverse them beyond 2GB with fseek/ftell, but on the other hand *no*
+ * 32-bit platform permits that, not with fseek/ftell. Not to mention
+ * that breaking 2GB limit for seeking would require surgery to *our*
+ * API. But sequential access suffices for practical cases when you
+ * can run into large files, such as fingerprinting, so we can let API
+ * alone. For reference, the list of 32-bit platforms which allow for
+ * sequential access of large files without extra "magic" comprise *BSD,
+ * Darwin, IRIX...
+ */
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+#endif
+
+#include <openssl/bio.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+#define BIO_FP_READ 0x02
+#define BIO_FP_WRITE 0x04
+#define BIO_FP_APPEND 0x08
+
+static FILE *open_file(const char *filename, const char *mode) {
+#if defined(_WIN32) && defined(CP_UTF8)
+  int sz, len_0 = (int)strlen(filename) + 1;
+  DWORD flags;
+
+  /* Basically there are three cases to cover: a) filename is pure ASCII
+   * string; b) actual UTF-8 encoded string and c) locale-ized string, i.e. one
+   * containing 8-bit characters that are meaningful in current system locale.
+   * If filename is pure ASCII or real UTF-8 encoded string,
+   * MultiByteToWideChar succeeds and _wfopen works. If filename is locale-ized
+   * string, chances are that MultiByteToWideChar fails reporting
+   * ERROR_NO_UNICODE_TRANSLATION, in which case we fall back to fopen... */
+  if ((sz = MultiByteToWideChar(CP_UTF8, (flags = MB_ERR_INVALID_CHARS),
+                                filename, len_0, NULL, 0)) > 0 ||
+      (GetLastError() == ERROR_INVALID_FLAGS &&
+       (sz = MultiByteToWideChar(CP_UTF8, (flags = 0), filename, len_0, NULL,
+                                 0)) > 0)) {
+    WCHAR wmode[8];
+    WCHAR *wfilename = _alloca(sz * sizeof(WCHAR));
+
+    if (MultiByteToWideChar(CP_UTF8, flags, filename, len_0, wfilename, sz) &&
+        MultiByteToWideChar(CP_UTF8, 0, mode, strlen(mode) + 1, wmode,
+                            sizeof(wmode) / sizeof(wmode[0])) &&
+        (file = _wfopen(wfilename, wmode)) == NULL &&
+        (errno == ENOENT ||
+         errno == EBADF)) /* UTF-8 decode succeeded, but no file, filename
+                           * could still have been locale-ized... */
+      return fopen(filename, mode);
+  } else if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
+    return fopen(filename, mode);
+  }
+#else
+  return fopen(filename, mode);
+#endif
+}
+
+BIO *BIO_new_file(const char *filename, const char *mode) {
+  BIO *ret;
+  FILE *file;
+
+  file = open_file(filename, mode);
+  if (file == NULL) {
+    OPENSSL_PUT_SYSTEM_ERROR(fopen);
+
+    ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
+    if (errno == ENOENT) {
+      OPENSSL_PUT_ERROR(BIO, BIO_new_file, BIO_R_NO_SUCH_FILE);
+    } else {
+      OPENSSL_PUT_ERROR(BIO, BIO_new_file, BIO_R_SYS_LIB);
+    }
+    return NULL;
+  }
+
+  ret = BIO_new(BIO_s_file());
+  if (ret == NULL) {
+    fclose(file);
+    return NULL;
+  }
+
+  BIO_set_fp(ret, file, BIO_CLOSE);
+  return ret;
+}
+
+BIO *BIO_new_fp(FILE *stream, int close_flag) {
+  BIO *ret = BIO_new(BIO_s_file());
+
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  BIO_set_fp(ret, stream, close_flag);
+  return ret;
+}
+
+static int file_new(BIO *bio) { return 1; }
+
+static int file_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (!bio->shutdown) {
+    return 1;
+  }
+
+  if (bio->init && bio->ptr != NULL) {
+    fclose(bio->ptr);
+    bio->ptr = NULL;
+  }
+  bio->init = 0;
+
+  return 1;
+}
+
+static int file_read(BIO *b, char *out, int outl) {
+  int ret = 0;
+
+  if (!b->init) {
+    return 0;
+  }
+
+  ret = fread(out, 1, outl, (FILE *)b->ptr);
+  if (ret == 0 && ferror((FILE *)b->ptr)) {
+    OPENSSL_PUT_SYSTEM_ERROR(fread);
+    OPENSSL_PUT_ERROR(BIO, file_read, ERR_R_SYS_LIB);
+    ret = -1;
+  }
+
+  return ret;
+}
+
+static int file_write(BIO *b, const char *in, int inl) {
+  int ret = 0;
+
+  if (!b->init) {
+    return 0;
+  }
+
+  ret = fwrite(in, inl, 1, (FILE *)b->ptr);
+  if (ret > 0) {
+    ret = inl;
+  }
+  return ret;
+}
+
+static long file_ctrl(BIO *b, int cmd, long num, void *ptr) {
+  long ret = 1;
+  FILE *fp = (FILE *)b->ptr;
+  FILE **fpp;
+  char p[4];
+
+  switch (cmd) {
+    case BIO_CTRL_RESET:
+      num = 0;
+    case BIO_C_FILE_SEEK:
+      ret = (long)fseek(fp, num, 0);
+      break;
+    case BIO_CTRL_EOF:
+      ret = (long)feof(fp);
+      break;
+    case BIO_C_FILE_TELL:
+    case BIO_CTRL_INFO:
+      ret = ftell(fp);
+      break;
+    case BIO_C_SET_FILE_PTR:
+      file_free(b);
+      b->shutdown = (int)num & BIO_CLOSE;
+      b->ptr = ptr;
+      b->init = 1;
+      break;
+    case BIO_C_SET_FILENAME:
+      file_free(b);
+      b->shutdown = (int)num & BIO_CLOSE;
+      if (num & BIO_FP_APPEND) {
+        if (num & BIO_FP_READ) {
+          BUF_strlcpy(p, "a+", sizeof(p));
+        } else {
+          BUF_strlcpy(p, "a", sizeof(p));
+        }
+      } else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE)) {
+        BUF_strlcpy(p, "r+", sizeof(p));
+      } else if (num & BIO_FP_WRITE) {
+        BUF_strlcpy(p, "w", sizeof(p));
+      } else if (num & BIO_FP_READ) {
+        BUF_strlcpy(p, "r", sizeof(p));
+      } else {
+        OPENSSL_PUT_ERROR(BIO, file_ctrl, BIO_R_BAD_FOPEN_MODE);
+        ret = 0;
+        break;
+      }
+      fp = open_file(ptr, p);
+      if (fp == NULL) {
+        OPENSSL_PUT_SYSTEM_ERROR(fopen);
+        ERR_add_error_data(5, "fopen('", ptr, "','", p, "')");
+        OPENSSL_PUT_ERROR(BIO, file_ctrl, ERR_R_SYS_LIB);
+        ret = 0;
+        break;
+      }
+      b->ptr = fp;
+      b->init = 1;
+      break;
+    case BIO_C_GET_FILE_PTR:
+      /* the ptr parameter is actually a FILE ** in this case. */
+      if (ptr != NULL) {
+        fpp = (FILE **)ptr;
+        *fpp = (FILE *)b->ptr;
+      }
+      break;
+    case BIO_CTRL_GET_CLOSE:
+      ret = (long)b->shutdown;
+      break;
+    case BIO_CTRL_SET_CLOSE:
+      b->shutdown = (int)num;
+      break;
+    case BIO_CTRL_FLUSH:
+      ret = 0 == fflush((FILE *)b->ptr);
+      break;
+    case BIO_CTRL_WPENDING:
+    case BIO_CTRL_PENDING:
+    default:
+      ret = 0;
+      break;
+  }
+  return ret;
+}
+
+static int file_gets(BIO *bp, char *buf, int size) {
+  int ret = 0;
+
+  if (size == 0) {
+    return 0;
+  }
+
+  if (!fgets(buf, size, (FILE *)bp->ptr)) {
+    buf[0] = 0;
+    goto err;
+  }
+  ret = strlen(buf);
+
+err:
+  return ret;
+}
+
+static int file_puts(BIO *bp, const char *str) {
+  return file_write(bp, str, strlen(str));
+}
+
+static const BIO_METHOD methods_filep = {
+    BIO_TYPE_FILE, "FILE pointer", file_write, file_read, file_puts,
+    file_gets,     file_ctrl,      file_new,   file_free, NULL, };
+
+const BIO_METHOD *BIO_s_file(void) { return &methods_filep; }
+
+
+int BIO_get_fp(BIO *bio, FILE **out_file) {
+  return BIO_ctrl(bio, BIO_C_GET_FILE_PTR, 0, (char*) out_file);
+}
+
+int BIO_set_fp(BIO *bio, FILE *file, int close_flag) {
+  return BIO_ctrl(bio, BIO_C_SET_FILE_PTR, close_flag, (char *) file);
+}
+
+int BIO_read_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_READ,
+                  (char *)filename);
+}
+
+int BIO_write_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_WRITE,
+                  (char *)filename);
+}
+
+int BIO_append_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_APPEND,
+                  (char *)filename);
+}
+
+int BIO_rw_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME,
+                  BIO_CLOSE | BIO_FP_READ | BIO_FP_WRITE, (char *)filename);
+}
diff --git a/crypto/bio/hexdump.c b/crypto/bio/hexdump.c
new file mode 100644
index 0000000..d31d3a4
--- /dev/null
+++ b/crypto/bio/hexdump.c
@@ -0,0 +1,190 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <limits.h>
+
+
+/* hexdump_ctx contains the state of a hexdump. */
+struct hexdump_ctx {
+  BIO *bio;
+  char right_chars[18]; /* the contents of the right-hand side, ASCII dump. */
+  unsigned used;        /* number of bytes in the current line. */
+  size_t n;             /* number of bytes total. */
+  unsigned indent;
+};
+
+static void hexbyte(char *out, uint8_t b) {
+  static const char hextable[] = "0123456789abcdef";
+  out[0] = hextable[b>>4];
+  out[1] = hextable[b&0x0f];
+}
+
+static char to_char(uint8_t b) {
+  if (b < 32 || b > 126) {
+          return '.';
+  }
+  return b;
+}
+
+/* hexdump adds |len| bytes of |data| to the current hex dump described by
+ * |ctx|. */
+static int hexdump(struct hexdump_ctx *ctx, const uint8_t *data, size_t len) {
+  size_t i;
+  char buf[10];
+  unsigned l;
+
+  /* Output lines look like:
+   * 00000010  2e 2f 30 31 32 33 34 35  36 37 38 ... 3c 3d // |./0123456789:;<=|
+   * ^ offset                          ^ extra space           ^ ASCII of line
+   */
+
+  for (i = 0; i < len; i++) {
+    if (ctx->used == 0) {
+      /* The beginning of a line. */
+      BIO_indent(ctx->bio, ctx->indent, UINT_MAX);
+
+      hexbyte(&buf[0], ctx->n >> 24);
+      hexbyte(&buf[2], ctx->n >> 16);
+      hexbyte(&buf[4], ctx->n >> 8);
+      hexbyte(&buf[6], ctx->n);
+      buf[8] = buf[9] = ' ';
+      if (BIO_write(ctx->bio, buf, 10) < 0) {
+        return 0;
+      }
+    }
+
+    hexbyte(buf, data[i]);
+    buf[2] = ' ';
+    l = 3;
+    if (ctx->used == 7) {
+      /* There's an additional space after the 8th byte. */
+      buf[3] = ' ';
+      l = 4;
+    } else if (ctx->used == 15) {
+      /* At the end of the line there's an extra space and the bar for the
+       * right column. */
+      buf[3] = ' ';
+      buf[4] = '|';
+      l = 5;
+    }
+
+    if (BIO_write(ctx->bio, buf, l) < 0) {
+      return 0;
+    }
+    ctx->right_chars[ctx->used] = to_char(data[i]);
+    ctx->used++;
+    ctx->n++;
+    if (ctx->used == 16) {
+      ctx->right_chars[16] = '|';
+      ctx->right_chars[17] = '\n';
+      if (BIO_write(ctx->bio, ctx->right_chars, sizeof(ctx->right_chars)) < 0) {
+        return 0;
+      }
+      ctx->used = 0;
+    }
+  }
+
+  return 1;
+}
+
+/* finish flushes any buffered data in |ctx|. */
+static int finish(struct hexdump_ctx *ctx) {
+  /* See the comments in |hexdump| for the details of this format. */
+  const unsigned n_bytes = ctx->used;
+  unsigned l;
+  char buf[5];
+
+  if (n_bytes == 0) {
+    return 1;
+  }
+
+  memset(buf, ' ', 4);
+  buf[4] = '|';
+
+  for (; ctx->used < 16; ctx->used++) {
+    l = 3;
+    if (ctx->used == 7) {
+      l = 4;
+    } else if (ctx->used == 15) {
+      l = 5;
+    }
+    if (BIO_write(ctx->bio, buf, l) < 0) {
+      return 0;
+    }
+  }
+
+  ctx->right_chars[n_bytes] = '|';
+  ctx->right_chars[n_bytes + 1] = '\n';
+  if (BIO_write(ctx->bio, ctx->right_chars, n_bytes + 2) < 0) {
+    return 0;
+  }
+  return 1;
+}
+
+int BIO_hexdump(BIO *bio, const uint8_t *data, size_t len, unsigned indent) {
+  struct hexdump_ctx ctx;
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.bio = bio;
+  ctx.indent = indent;
+
+  if (!hexdump(&ctx, data, len) || !finish(&ctx)) {
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/crypto/bio/internal.h b/crypto/bio/internal.h
new file mode 100644
index 0000000..d498708
--- /dev/null
+++ b/crypto/bio/internal.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_BIO_INTERNAL_H
+#define OPENSSL_HEADER_BIO_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <sys/socket.h>
+#else
+#include <WinSock2.h>
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* BIO_ip_and_port_to_socket_and_addr creates a socket and fills in |*out_addr|
+ * with the correct values for connecting to |hostname| on |port_str|. It
+ * returns one on success or zero on error. */
+int bio_ip_and_port_to_socket_and_addr(int *out_sock,
+                                       struct sockaddr_storage *out_addr,
+                                       const char *hostname,
+                                       const char *port_str);
+
+/* BIO_socket_nbio sets whether |sock| is non-blocking. It returns one on
+ * success and zero otherwise. */
+int bio_socket_nbio(int sock, int on);
+
+/* BIO_clear_socket_error clears the last system socket error.
+ *
+ * TODO(fork): remove all callers of this. */
+void bio_clear_socket_error();
+
+/* BIO_sock_error returns the last socket error on |sock|. */
+int bio_sock_error(int sock);
+
+/* BIO_fd_should_retry returns non-zero if |return_value| indicates an error
+ * and |errno| indicates that it's non-fatal. */
+int bio_fd_should_retry(int return_value);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_BIO_INTERNAL_H */
diff --git a/crypto/bio/pair.c b/crypto/bio/pair.c
new file mode 100644
index 0000000..a2cc6d6
--- /dev/null
+++ b/crypto/bio/pair.c
@@ -0,0 +1,549 @@
+/* ====================================================================
+ * Copyright (c) 1998-2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bio.h>
+
+#include <assert.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+struct bio_bio_st {
+  BIO *peer; /* NULL if buf == NULL.
+              * If peer != NULL, then peer->ptr is also a bio_bio_st,
+              * and its "peer" member points back to us.
+              * peer != NULL iff init != 0 in the BIO. */
+
+  /* This is for what we write (i.e. reading uses peer's struct): */
+  int closed;    /* valid iff peer != NULL */
+  size_t len;    /* valid iff buf != NULL; 0 if peer == NULL */
+  size_t offset; /* valid iff buf != NULL; 0 if len == 0 */
+  size_t size;
+  char *buf; /* "size" elements (if != NULL) */
+
+  size_t request; /* valid iff peer != NULL; 0 if len != 0,
+                   * otherwise set by peer to number of bytes
+                   * it (unsuccessfully) tried to read,
+                   * never more than buffer space (size-len) warrants. */
+};
+
+static int bio_new(BIO *bio) {
+  struct bio_bio_st *b;
+
+  b = OPENSSL_malloc(sizeof *b);
+  if (b == NULL) {
+    return 0;
+  }
+
+  b->peer = NULL;
+  b->size = 17 * 1024; /* enough for one TLS record (just a default) */
+  b->buf = NULL;
+
+  bio->ptr = b;
+  return 1;
+}
+
+static void bio_destroy_pair(BIO *bio) {
+  struct bio_bio_st *b = bio->ptr;
+  BIO *peer_bio;
+  struct bio_bio_st *peer_b;
+
+  if (b == NULL) {
+    return;
+  }
+
+  peer_bio = b->peer;
+  if (peer_bio == NULL) {
+    return;
+  }
+
+  peer_b = peer_bio->ptr;
+
+  assert(peer_b != NULL);
+  assert(peer_b->peer == bio);
+
+  peer_b->peer = NULL;
+  peer_bio->init = 0;
+  assert(peer_b->buf != NULL);
+  peer_b->len = 0;
+  peer_b->offset = 0;
+
+  b->peer = NULL;
+  bio->init = 0;
+  assert(b->buf != NULL);
+  b->len = 0;
+  b->offset = 0;
+}
+
+static int bio_free(BIO *bio) {
+  struct bio_bio_st *b;
+
+  if (bio == NULL) {
+    return 0;
+  }
+  b = bio->ptr;
+
+  assert(b != NULL);
+
+  if (b->peer) {
+    bio_destroy_pair(bio);
+  }
+
+  if (b->buf != NULL) {
+    OPENSSL_free(b->buf);
+  }
+
+  OPENSSL_free(b);
+
+  return 1;
+}
+
+static int bio_read(BIO *bio, char *buf, int size_) {
+  size_t size = size_;
+  size_t rest;
+  struct bio_bio_st *b, *peer_b;
+
+  BIO_clear_retry_flags(bio);
+
+  if (!bio->init) {
+    return 0;
+  }
+
+  b = bio->ptr;
+  assert(b != NULL);
+  assert(b->peer != NULL);
+  peer_b = b->peer->ptr;
+  assert(peer_b != NULL);
+  assert(peer_b->buf != NULL);
+
+  peer_b->request = 0; /* will be set in "retry_read" situation */
+
+  if (buf == NULL || size == 0) {
+    return 0;
+  }
+
+  if (peer_b->len == 0) {
+    if (peer_b->closed) {
+      return 0; /* writer has closed, and no data is left */
+    } else {
+      BIO_set_retry_read(bio); /* buffer is empty */
+      if (size <= peer_b->size) {
+        peer_b->request = size;
+      } else {
+        /* don't ask for more than the peer can
+         * deliver in one write */
+        peer_b->request = peer_b->size;
+      }
+      return -1;
+    }
+  }
+
+  /* we can read */
+  if (peer_b->len < size) {
+    size = peer_b->len;
+  }
+
+  /* now read "size" bytes */
+  rest = size;
+
+  assert(rest > 0);
+  /* one or two iterations */
+  do {
+    size_t chunk;
+
+    assert(rest <= peer_b->len);
+    if (peer_b->offset + rest <= peer_b->size) {
+      chunk = rest;
+    } else {
+      /* wrap around ring buffer */
+      chunk = peer_b->size - peer_b->offset;
+    }
+    assert(peer_b->offset + chunk <= peer_b->size);
+
+    memcpy(buf, peer_b->buf + peer_b->offset, chunk);
+
+    peer_b->len -= chunk;
+    if (peer_b->len) {
+      peer_b->offset += chunk;
+      assert(peer_b->offset <= peer_b->size);
+      if (peer_b->offset == peer_b->size) {
+        peer_b->offset = 0;
+      }
+      buf += chunk;
+    } else {
+      /* buffer now empty, no need to advance "buf" */
+      assert(chunk == rest);
+      peer_b->offset = 0;
+    }
+    rest -= chunk;
+  } while (rest);
+
+  return size;
+}
+
+static int bio_write(BIO *bio, const char *buf, int num_) {
+  size_t num = num_;
+  size_t rest;
+  struct bio_bio_st *b;
+
+  BIO_clear_retry_flags(bio);
+
+  if (!bio->init || buf == NULL || num == 0) {
+    return 0;
+  }
+
+  b = bio->ptr;
+  assert(b != NULL);
+  assert(b->peer != NULL);
+  assert(b->buf != NULL);
+
+  b->request = 0;
+  if (b->closed) {
+    /* we already closed */
+    OPENSSL_PUT_ERROR(BIO, bio_write, BIO_R_BROKEN_PIPE);
+    return -1;
+  }
+
+  assert(b->len <= b->size);
+
+  if (b->len == b->size) {
+    BIO_set_retry_write(bio); /* buffer is full */
+    return -1;
+  }
+
+  /* we can write */
+  if (num > b->size - b->len) {
+    num = b->size - b->len;
+  }
+
+  /* now write "num" bytes */
+  rest = num;
+
+  assert(rest > 0);
+  /* one or two iterations */
+  do {
+    size_t write_offset;
+    size_t chunk;
+
+    assert(b->len + rest <= b->size);
+
+    write_offset = b->offset + b->len;
+    if (write_offset >= b->size) {
+      write_offset -= b->size;
+    }
+    /* b->buf[write_offset] is the first byte we can write to. */
+
+    if (write_offset + rest <= b->size) {
+      chunk = rest;
+    } else {
+      /* wrap around ring buffer */
+      chunk = b->size - write_offset;
+    }
+
+    memcpy(b->buf + write_offset, buf, chunk);
+
+    b->len += chunk;
+
+    assert(b->len <= b->size);
+
+    rest -= chunk;
+    buf += chunk;
+  } while (rest);
+
+  return num;
+}
+
+static int bio_make_pair(BIO *bio1, BIO *bio2) {
+  struct bio_bio_st *b1, *b2;
+
+  assert(bio1 != NULL);
+  assert(bio2 != NULL);
+
+  b1 = bio1->ptr;
+  b2 = bio2->ptr;
+
+  if (b1->peer != NULL || b2->peer != NULL) {
+    OPENSSL_PUT_ERROR(BIO, bio_make_pair, BIO_R_IN_USE);
+    return 0;
+  }
+
+  if (b1->buf == NULL) {
+    b1->buf = OPENSSL_malloc(b1->size);
+    if (b1->buf == NULL) {
+      OPENSSL_PUT_ERROR(BIO, bio_make_pair, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+    b1->len = 0;
+    b1->offset = 0;
+  }
+
+  if (b2->buf == NULL) {
+    b2->buf = OPENSSL_malloc(b2->size);
+    if (b2->buf == NULL) {
+      OPENSSL_PUT_ERROR(BIO, bio_make_pair, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+    b2->len = 0;
+    b2->offset = 0;
+  }
+
+  b1->peer = bio2;
+  b1->closed = 0;
+  b1->request = 0;
+  b2->peer = bio1;
+  b2->closed = 0;
+  b2->request = 0;
+
+  bio1->init = 1;
+  bio2->init = 1;
+
+  return 1;
+}
+
+static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+  long ret;
+  struct bio_bio_st *b = bio->ptr;
+
+  assert(b != NULL);
+
+  switch (cmd) {
+    /* specific CTRL codes */
+
+    case BIO_C_SET_BUFF_SIZE:
+      if (b->peer) {
+        OPENSSL_PUT_ERROR(BIO, bio_ctrl, BIO_R_IN_USE);
+        ret = 0;
+      } else if (num == 0) {
+        OPENSSL_PUT_ERROR(BIO, bio_ctrl, BIO_R_INVALID_ARGUMENT);
+        ret = 0;
+      } else {
+        size_t new_size = num;
+
+        if (b->size != new_size) {
+          if (b->buf) {
+            OPENSSL_free(b->buf);
+            b->buf = NULL;
+          }
+          b->size = new_size;
+        }
+        ret = 1;
+      }
+      break;
+
+    case BIO_C_GET_WRITE_BUF_SIZE:
+      ret = (long)b->size;
+      break;
+
+    case BIO_C_GET_WRITE_GUARANTEE:
+      /* How many bytes can the caller feed to the next write
+       * without having to keep any? */
+      if (b->peer == NULL || b->closed) {
+        ret = 0;
+      } else {
+        ret = (long)b->size - b->len;
+      }
+      break;
+
+    case BIO_C_GET_READ_REQUEST:
+      /* If the peer unsuccessfully tried to read, how many bytes
+       * were requested?  (As with BIO_CTRL_PENDING, that number
+       * can usually be treated as boolean.) */
+      ret = (long)b->request;
+      break;
+
+    case BIO_C_RESET_READ_REQUEST:
+      /* Reset request.  (Can be useful after read attempts
+       * at the other side that are meant to be non-blocking,
+       * e.g. when probing SSL_read to see if any data is
+       * available.) */
+      b->request = 0;
+      ret = 1;
+      break;
+
+    case BIO_C_SHUTDOWN_WR:
+      /* similar to shutdown(..., SHUT_WR) */
+      b->closed = 1;
+      ret = 1;
+      break;
+
+    /* standard CTRL codes follow */
+
+    case BIO_CTRL_RESET:
+      if (b->buf != NULL) {
+        b->len = 0;
+        b->offset = 0;
+      }
+      ret = 0;
+      break;
+
+    case BIO_CTRL_GET_CLOSE:
+      ret = bio->shutdown;
+      break;
+
+    case BIO_CTRL_SET_CLOSE:
+      bio->shutdown = (int)num;
+      ret = 1;
+      break;
+
+    case BIO_CTRL_PENDING:
+      if (b->peer != NULL) {
+        struct bio_bio_st *peer_b = b->peer->ptr;
+        ret = (long)peer_b->len;
+      } else {
+        ret = 0;
+      }
+      break;
+
+    case BIO_CTRL_WPENDING:
+      ret = 0;
+      if (b->buf != NULL) {
+        ret = (long)b->len;
+      }
+      break;
+
+    case BIO_CTRL_FLUSH:
+      ret = 1;
+      break;
+
+    case BIO_CTRL_EOF: {
+      BIO *other_bio = ptr;
+
+      if (other_bio) {
+        struct bio_bio_st *other_b = other_bio->ptr;
+        assert(other_b != NULL);
+        ret = other_b->len == 0 && other_b->closed;
+      } else {
+        ret = 1;
+      }
+    } break;
+
+    default:
+      ret = 0;
+  }
+  return ret;
+}
+
+static int bio_puts(BIO *bio, const char *str) {
+  return bio_write(bio, str, strlen(str));
+}
+
+int BIO_new_bio_pair(BIO **bio1_p, size_t writebuf1, BIO **bio2_p,
+                     size_t writebuf2) {
+  BIO *bio1 = NULL, *bio2 = NULL;
+  long r;
+  int ret = 0;
+
+  bio1 = BIO_new(BIO_s_bio());
+  if (bio1 == NULL) {
+    goto err;
+  }
+  bio2 = BIO_new(BIO_s_bio());
+  if (bio2 == NULL) {
+    goto err;
+  }
+
+  if (writebuf1) {
+    r = BIO_set_write_buffer_size(bio1, writebuf1);
+    if (!r) {
+      goto err;
+    }
+  }
+  if (writebuf2) {
+    r = BIO_set_write_buffer_size(bio2, writebuf2);
+    if (!r) {
+      goto err;
+    }
+  }
+
+  if (!bio_make_pair(bio1, bio2)) {
+    goto err;
+  }
+  ret = 1;
+
+err:
+  if (ret == 0) {
+    if (bio1) {
+      BIO_free(bio1);
+      bio1 = NULL;
+    }
+    if (bio2) {
+      BIO_free(bio2);
+      bio2 = NULL;
+    }
+  }
+
+  *bio1_p = bio1;
+  *bio2_p = bio2;
+  return ret;
+}
+
+static const BIO_METHOD methods_biop = {
+    BIO_TYPE_BIO, "BIO pair",             bio_write, bio_read,
+    bio_puts,     NULL /* no bio_gets */, bio_ctrl,  bio_new,
+    bio_free,     NULL /* no bio_callback_ctrl */
+};
+
+const BIO_METHOD *BIO_s_bio(void) { return &methods_biop; }
+
+size_t BIO_ctrl_get_read_request(BIO *bio) {
+  return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL);
+}
+
+size_t BIO_ctrl_get_write_guarantee(BIO *bio) {
+  return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL);
+}
+
+int BIO_shutdown_wr(BIO *bio) {
+  return BIO_ctrl(bio, BIO_C_SHUTDOWN_WR, 0, NULL);
+}
diff --git a/crypto/bio/printf.c b/crypto/bio/printf.c
new file mode 100644
index 0000000..e513ff3
--- /dev/null
+++ b/crypto/bio/printf.c
@@ -0,0 +1,110 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#define _BSD_SOURCE  /* for snprintf, vprintf etc */
+
+#include <openssl/bio.h>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <openssl/mem.h>
+
+
+int BIO_printf(BIO *bio, const char *format, ...) {
+  va_list args;
+  int ret;
+
+  va_start(args, format);
+
+  ret = BIO_vprintf(bio, format, args);
+
+  va_end(args);
+  return ret;
+}
+
+int BIO_vprintf(BIO *bio, const char *format, va_list args) {
+  char buf[256], *out, out_malloced = 0;
+  int out_len, ret;
+
+  /* Note: this is assuming that va_list is ok to copy as POD. If the system
+   * defines it with a pointer to mutable state then passing it twice to
+   * vsnprintf will not work. */
+  out_len = vsnprintf(buf, sizeof(buf), format, args);
+  if (out_len >= sizeof(buf)) {
+    const int requested_len = out_len;
+    /* The output was truncated. */
+    out = OPENSSL_malloc(requested_len);
+    out_malloced = 1;
+    if (out == NULL) {
+      /* Unclear what can be done in this situation. OpenSSL has historically
+       * crashed and that seems better than producing the wrong output. */
+      abort();
+    }
+    out_len = vsnprintf(out, requested_len, format, args);
+    assert(out_len == requested_len);
+  } else {
+    out = buf;
+  }
+
+  ret = BIO_write(bio, out, out_len);
+  if (out_malloced) {
+    OPENSSL_free(out);
+  }
+
+  return ret;
+}
diff --git a/crypto/bio/socket.c b/crypto/bio/socket.c
new file mode 100644
index 0000000..93dd512
--- /dev/null
+++ b/crypto/bio/socket.c
@@ -0,0 +1,180 @@
+/* crypto/bio/bss_sock.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <fcntl.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <unistd.h>
+#endif
+
+#include "internal.h"
+
+
+static int sock_new(BIO *bio) {
+  bio->init = 0;
+  bio->num = 0;
+  bio->ptr = NULL;
+  bio->flags = 0;
+  return 1;
+}
+
+static int sock_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
+  if (bio->shutdown) {
+    if (bio->init) {
+      close(bio->num);
+    }
+    bio->init = 0;
+    bio->flags = 0;
+  }
+  return 1;
+}
+
+static int sock_read(BIO *b, char *out, int outl) {
+  int ret = 0;
+
+  if (out == NULL) {
+    return 0;
+  }
+
+  bio_clear_socket_error();
+  ret = recv(b->num, out, outl, 0);
+  BIO_clear_retry_flags(b);
+  if (ret <= 0) {
+    if (bio_fd_should_retry(ret)) {
+      BIO_set_retry_read(b);
+    }
+  }
+  return ret;
+}
+
+static int sock_write(BIO *b, const char *in, int inl) {
+  int ret;
+
+  bio_clear_socket_error();
+  ret = send(b->num, in, inl, 0);
+  BIO_clear_retry_flags(b);
+  if (ret <= 0) {
+    if (bio_fd_should_retry(ret)) {
+      BIO_set_retry_write(b);
+    }
+  }
+  return ret;
+}
+
+static int sock_puts(BIO *bp, const char *str) {
+  return sock_write(bp, str, strlen(str));
+}
+
+static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) {
+  long ret = 1;
+  int *ip;
+
+  switch (cmd) {
+    case BIO_C_SET_FD:
+      sock_free(b);
+      b->num = *((int *)ptr);
+      b->shutdown = (int)num;
+      b->init = 1;
+      break;
+    case BIO_C_GET_FD:
+      if (b->init) {
+        ip = (int *)ptr;
+        if (ip != NULL)
+          *ip = b->num;
+        ret = b->num;
+      } else
+        ret = -1;
+      break;
+    case BIO_CTRL_GET_CLOSE:
+      ret = b->shutdown;
+      break;
+    case BIO_CTRL_SET_CLOSE:
+      b->shutdown = (int)num;
+      break;
+    case BIO_CTRL_FLUSH:
+      ret = 1;
+      break;
+    default:
+      ret = 0;
+      break;
+  }
+  return ret;
+}
+
+static const BIO_METHOD methods_sockp = {
+    BIO_TYPE_SOCKET,  "socket",  sock_write, sock_read, sock_puts,
+    NULL /* gets, */, sock_ctrl, sock_new,   sock_free, NULL,
+};
+
+const BIO_METHOD *BIO_s_socket(void) { return &methods_sockp; }
+
+BIO *BIO_new_socket(int fd, int close_flag) {
+  BIO *ret;
+
+  ret = BIO_new(BIO_s_socket());
+  if (ret == NULL) {
+    return NULL;
+  }
+  BIO_set_fd(ret, fd, close_flag);
+  return ret;
+}
diff --git a/crypto/bio/socket_helper.c b/crypto/bio/socket_helper.c
new file mode 100644
index 0000000..d304e12
--- /dev/null
+++ b/crypto/bio/socket_helper.c
@@ -0,0 +1,108 @@
+/* Copyright (c) 2014, 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. */
+
+#define _POSIX_SOURCE
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <netdb.h>
+#include <unistd.h>
+#else
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+typedef int socklen_t;
+#endif
+
+#include "internal.h"
+
+
+int bio_ip_and_port_to_socket_and_addr(int *out_sock,
+                                       struct sockaddr_storage *out_addr,
+                                       const char *hostname,
+                                       const char *port_str) {
+  struct addrinfo hint, *result, *cur;
+  int ret;
+
+  *out_sock = -1;
+
+  memset(&hint, 0, sizeof(hint));
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+
+  ret = getaddrinfo(hostname, port_str, &hint, &result);
+  if (ret != 0) {
+    OPENSSL_PUT_ERROR(SYS, getaddrinfo, 0);
+    ERR_add_error_data(2, gai_strerror(ret));
+    return 0;
+  }
+
+  ret = 0;
+
+  for (cur = result; cur; cur = cur->ai_next) {
+    if (cur->ai_addrlen > sizeof(struct sockaddr_storage)) {
+      continue;
+    }
+    memset(out_addr, 0, sizeof(struct sockaddr_storage));
+    memcpy(out_addr, cur->ai_addr, cur->ai_addrlen);
+
+    *out_sock = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
+    if (*out_sock < 0) {
+      OPENSSL_PUT_SYSTEM_ERROR(socket);
+      goto out;
+    }
+
+    ret = 1;
+    break;
+  }
+
+out:
+  freeaddrinfo(result);
+  return ret;
+}
+
+int bio_socket_nbio(int sock, int on) {
+#if defined(OPENSSL_WINDOWS)
+  u_long arg = on;
+
+  return 0 == ioctlsocket(sock, FIONBIO, &arg);
+#else
+  int flags = fcntl(sock, F_GETFL, 0);
+  if (flags < 0) {
+    return 0;
+  }
+  if (!on) {
+    flags &= ~O_NONBLOCK;
+  } else {
+    flags |= O_NONBLOCK;
+  }
+  return fcntl(sock, F_SETFL, flags) == 0;
+#endif
+}
+
+void bio_clear_socket_error() {}
+
+int bio_sock_error(int sock) {
+  int error;
+  socklen_t error_size = sizeof(error);
+
+  if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &error_size) < 0) {
+    return 1;
+  }
+  return error;
+}
diff --git a/crypto/bn/CMakeLists.txt b/crypto/bn/CMakeLists.txt
new file mode 100644
index 0000000..5d2dfcc
--- /dev/null
+++ b/crypto/bn/CMakeLists.txt
@@ -0,0 +1,81 @@
+include_directories(. .. ../../include)
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		BN_ARCH_SOURCES
+
+		asm/x86_64-gcc.c
+		x86_64-mont.${ASM_EXT}
+		x86_64-mont5.${ASM_EXT}
+		modexp512-x86_64.${ASM_EXT}
+		rsaz-x86_64.${ASM_EXT}
+		rsaz-avx2.${ASM_EXT}
+
+		rsaz_exp.c
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		BN_ARCH_SOURCES
+
+		bn-586.${ASM_EXT}
+		co-586.${ASM_EXT}
+		x86-mont.${ASM_EXT}
+		x86.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "arm")
+	set(
+		BN_ARCH_SOURCES
+
+		armv4-mont.${ASM_EXT}
+	)
+endif()
+
+add_library(
+	bn
+
+	OBJECT
+
+	bn_error.c
+	add.c
+	bn.c
+	cmp.c
+	convert.c
+	ctx.c
+	div.c
+	exponentiation.c
+	generic.c
+	gcd.c
+	kronecker.c
+	montgomery.c
+	mul.c
+	prime.c
+	random.c
+	shift.c
+	sqrt.c
+
+	${BN_ARCH_SOURCES}
+)
+
+perlasm(x86_64-mont.${ASM_EXT} asm/x86_64-mont.pl)
+perlasm(x86_64-mont5.${ASM_EXT} asm/x86_64-mont5.pl)
+perlasm(x86_64-gf2m.${ASM_EXT} asm/x86_64-gf2m.pl)
+perlasm(modexp512-x86_64.${ASM_EXT} asm/modexp512-x86_64.pl)
+perlasm(rsaz-x86_64.${ASM_EXT} asm/rsaz-x86_64.pl)
+perlasm(rsaz-avx2.${ASM_EXT} asm/rsaz-avx2.pl)
+perlasm(bn-586.${ASM_EXT} asm/bn-586.pl)
+perlasm(co-586.${ASM_EXT} asm/co-586.pl)
+perlasm(x86-mont.${ASM_EXT} asm/x86-mont.pl)
+perlasm(x86.${ASM_EXT} asm/x86.pl)
+perlasm(armv4-mont.${ASM_EXT} asm/armv4-mont.pl)
+
+add_executable(
+	bn_test
+
+	bn_test.c
+)
+
+target_link_libraries(bn_test crypto)
diff --git a/crypto/bn/add.c b/crypto/bn/add.c
new file mode 100644
index 0000000..1c6b2d7
--- /dev/null
+++ b/crypto/bn/add.c
@@ -0,0 +1,394 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+  const BIGNUM *tmp;
+  int a_neg = a->neg, ret;
+
+  /*  a +  b	a+b
+   *  a + -b	a-b
+   * -a +  b	b-a
+   * -a + -b	-(a+b)
+   */
+  if (a_neg ^ b->neg) {
+    /* only one is negative */
+    if (a_neg) {
+      tmp = a;
+      a = b;
+      b = tmp;
+    }
+
+    /* we are now a - b */
+    if (BN_ucmp(a, b) < 0) {
+      if (!BN_usub(r, b, a)) {
+        return 0;
+      }
+      r->neg = 1;
+    } else {
+      if (!BN_usub(r, a, b)) {
+        return 0;
+      }
+      r->neg = 0;
+    }
+    return 1;
+  }
+
+  ret = BN_uadd(r, a, b);
+  r->neg = a_neg;
+  return ret;
+}
+
+int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+  int max, min, dif;
+  BN_ULONG *ap, *bp, *rp, carry, t1, t2;
+  const BIGNUM *tmp;
+
+  if (a->top < b->top) {
+    tmp = a;
+    a = b;
+    b = tmp;
+  }
+  max = a->top;
+  min = b->top;
+  dif = max - min;
+
+  if (bn_wexpand(r, max + 1) == NULL) {
+    return 0;
+  }
+
+  r->top = max;
+
+  ap = a->d;
+  bp = b->d;
+  rp = r->d;
+
+  carry = bn_add_words(rp, ap, bp, min);
+  rp += min;
+  ap += min;
+  bp += min;
+
+  if (carry) {
+    while (dif) {
+      dif--;
+      t1 = *(ap++);
+      t2 = (t1 + 1) & BN_MASK2;
+      *(rp++) = t2;
+      if (t2) {
+        carry = 0;
+        break;
+      }
+    }
+    if (carry) {
+      /* carry != 0 => dif == 0 */
+      *rp = 1;
+      r->top++;
+    }
+  }
+
+  if (dif && rp != ap) {
+    while (dif--) {
+      /* copy remaining words if ap != rp */
+      *(rp++) = *(ap++);
+    }
+  }
+
+  r->neg = 0;
+  return 1;
+}
+
+int BN_add_word(BIGNUM *a, BN_ULONG w) {
+  BN_ULONG l;
+  int i;
+
+  w &= BN_MASK2;
+
+  /* degenerate case: w is zero */
+  if (!w) {
+    return 1;
+  }
+
+  /* degenerate case: a is zero */
+  if (BN_is_zero(a)) {
+    return BN_set_word(a, w);
+  }
+
+  /* handle 'a' when negative */
+  if (a->neg) {
+    a->neg = 0;
+    i = BN_sub_word(a, w);
+    if (!BN_is_zero(a)) {
+      a->neg = !(a->neg);
+    }
+    return i;
+  }
+
+  for (i = 0; w != 0 && i < a->top; i++) {
+    a->d[i] = l = (a->d[i] + w) & BN_MASK2;
+    w = (w > l) ? 1 : 0;
+  }
+
+  if (w && i == a->top) {
+    if (bn_wexpand(a, a->top + 1) == NULL) {
+      return 0;
+    }
+    a->top++;
+    a->d[i] = w;
+  }
+
+  return 1;
+}
+
+int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+  int max;
+  int add = 0, neg = 0;
+  const BIGNUM *tmp;
+
+  /*  a -  b	a-b
+   *  a - -b	a+b
+   * -a -  b	-(a+b)
+   * -a - -b	b-a
+   */
+  if (a->neg) {
+    if (b->neg) {
+      tmp = a;
+      a = b;
+      b = tmp;
+    } else {
+      add = 1;
+      neg = 1;
+    }
+  } else {
+    if (b->neg) {
+      add = 1;
+      neg = 0;
+    }
+  }
+
+  if (add) {
+    if (!BN_uadd(r, a, b)) {
+      return 0;
+    }
+
+    r->neg = neg;
+    return 1;
+  }
+
+  /* We are actually doing a - b :-) */
+
+  max = (a->top > b->top) ? a->top : b->top;
+  if (bn_wexpand(r, max) == NULL) {
+    return 0;
+  }
+
+  if (BN_ucmp(a, b) < 0) {
+    if (!BN_usub(r, b, a)) {
+      return 0;
+    }
+    r->neg = 1;
+  } else {
+    if (!BN_usub(r, a, b)) {
+      return 0;
+    }
+    r->neg = 0;
+  }
+
+  return 1;
+}
+
+int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+  int max, min, dif;
+  register BN_ULONG t1, t2, *ap, *bp, *rp;
+  int i, carry;
+
+  max = a->top;
+  min = b->top;
+  dif = max - min;
+
+  if (dif < 0) /* hmm... should not be happening */
+  {
+    OPENSSL_PUT_ERROR(BN, BN_usub, BN_R_ARG2_LT_ARG3);
+    return 0;
+  }
+
+  if (bn_wexpand(r, max) == NULL) {
+    return 0;
+  }
+
+  ap = a->d;
+  bp = b->d;
+  rp = r->d;
+
+  carry = 0;
+  for (i = min; i != 0; i--) {
+    t1 = *(ap++);
+    t2 = *(bp++);
+    if (carry) {
+      carry = (t1 <= t2);
+      t1 = (t1 - t2 - 1) & BN_MASK2;
+    } else {
+      carry = (t1 < t2);
+      t1 = (t1 - t2) & BN_MASK2;
+    }
+    *(rp++) = t1 & BN_MASK2;
+  }
+
+  if (carry) /* subtracted */
+  {
+    if (!dif) {
+      /* error: a < b */
+      return 0;
+    }
+
+    while (dif) {
+      dif--;
+      t1 = *(ap++);
+      t2 = (t1 - 1) & BN_MASK2;
+      *(rp++) = t2;
+      if (t1) {
+        break;
+      }
+    }
+  }
+
+  if (rp != ap) {
+    for (;;) {
+      if (!dif--) {
+        break;
+      }
+      rp[0] = ap[0];
+      if (!dif--) {
+        break;
+      }
+      rp[1] = ap[1];
+      if (!dif--) {
+        break;
+      }
+      rp[2] = ap[2];
+      if (!dif--) {
+        break;
+      }
+      rp[3] = ap[3];
+      rp += 4;
+      ap += 4;
+    }
+  }
+
+  r->top = max;
+  r->neg = 0;
+  bn_correct_top(r);
+
+  return 1;
+}
+
+int BN_sub_word(BIGNUM *a, BN_ULONG w) {
+  int i;
+
+  w &= BN_MASK2;
+
+  /* degenerate case: w is zero */
+  if (!w) {
+    return 1;
+  }
+
+  /* degenerate case: a is zero */
+  if (BN_is_zero(a)) {
+    i = BN_set_word(a, w);
+    if (i != 0) {
+      BN_set_negative(a, 1);
+    }
+    return i;
+  }
+
+  /* handle 'a' when negative */
+  if (a->neg) {
+    a->neg = 0;
+    i = BN_add_word(a, w);
+    a->neg = 1;
+    return i;
+  }
+
+  if ((a->top == 1) && (a->d[0] < w)) {
+    a->d[0] = w - a->d[0];
+    a->neg = 1;
+    return 1;
+  }
+
+  i = 0;
+  for (;;) {
+    if (a->d[i] >= w) {
+      a->d[i] -= w;
+      break;
+    } else {
+      a->d[i] = (a->d[i] - w) & BN_MASK2;
+      i++;
+      w = 1;
+    }
+  }
+
+  if ((a->d[i] == 0) && (i == (a->top - 1))) {
+    a->top--;
+  }
+
+  return 1;
+}
diff --git a/crypto/bn/asm/armv4-mont.pl b/crypto/bn/asm/armv4-mont.pl
new file mode 100644
index 0000000..fe81f9b
--- /dev/null
+++ b/crypto/bn/asm/armv4-mont.pl
@@ -0,0 +1,669 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# January 2007.
+
+# Montgomery multiplication for ARMv4.
+#
+# Performance improvement naturally varies among CPU implementations
+# and compilers. The code was observed to provide +65-35% improvement
+# [depending on key length, less for longer keys] on ARM920T, and
+# +115-80% on Intel IXP425. This is compared to pre-bn_mul_mont code
+# base and compiler generated code with in-lined umull and even umlal
+# instructions. The latter means that this code didn't really have an 
+# "advantage" of utilizing some "secret" instruction.
+#
+# The code is interoperable with Thumb ISA and is rather compact, less
+# than 1/2KB. Windows CE port would be trivial, as it's exclusively
+# about decorations, ABI and instruction syntax are identical.
+
+# November 2013
+#
+# Add NEON code path, which handles lengths divisible by 8. RSA/DSA
+# performance improvement on Cortex-A8 is ~45-100% depending on key
+# length, more for longer keys. On Cortex-A15 the span is ~10-105%.
+# On Snapdragon S4 improvement was measured to vary from ~70% to
+# incredible ~380%, yes, 4.8x faster, for RSA4096 sign. But this is
+# rather because original integer-only code seems to perform
+# suboptimally on S4. Situation on Cortex-A9 is unfortunately
+# different. It's being looked into, but the trouble is that
+# performance for vectors longer than 256 bits is actually couple
+# of percent worse than for integer-only code. The code is chosen
+# for execution on all NEON-capable processors, because gain on
+# others outweighs the marginal loss on Cortex-A9.
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$num="r0";	# starts as num argument, but holds &tp[num-1]
+$ap="r1";
+$bp="r2"; $bi="r2"; $rp="r2";
+$np="r3";
+$tp="r4";
+$aj="r5";
+$nj="r6";
+$tj="r7";
+$n0="r8";
+###########	# r9 is reserved by ELF as platform specific, e.g. TLS pointer
+$alo="r10";	# sl, gcc uses it to keep @GOT
+$ahi="r11";	# fp
+$nlo="r12";	# ip
+###########	# r13 is stack pointer
+$nhi="r14";	# lr
+###########	# r15 is program counter
+
+#### argument block layout relative to &tp[num-1], a.k.a. $num
+$_rp="$num,#12*4";
+# ap permanently resides in r1
+$_bp="$num,#13*4";
+# np permanently resides in r3
+$_n0="$num,#14*4";
+$_num="$num,#15*4";	$_bpend=$_num;
+
+$code=<<___;
+#include "arm_arch.h"
+
+.text
+.code	32
+
+#if __ARM_ARCH__>=7
+.align	5
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-bn_mul_mont
+#endif
+
+.global	bn_mul_mont
+.type	bn_mul_mont,%function
+
+.align	5
+bn_mul_mont:
+	ldr	ip,[sp,#4]		@ load num
+	stmdb	sp!,{r0,r2}		@ sp points at argument block
+#if __ARM_ARCH__>=7
+	tst	ip,#7
+	bne	.Lialu
+	adr	r0,bn_mul_mont
+	ldr	r2,.LOPENSSL_armcap
+	ldr	r0,[r0,r2]
+	tst	r0,#1			@ NEON available?
+	ldmia	sp, {r0,r2}
+	beq	.Lialu
+	add	sp,sp,#8
+	b	bn_mul8x_mont_neon
+.align	4
+.Lialu:
+#endif
+	cmp	ip,#2
+	mov	$num,ip			@ load num
+	movlt	r0,#0
+	addlt	sp,sp,#2*4
+	blt	.Labrt
+
+	stmdb	sp!,{r4-r12,lr}		@ save 10 registers
+
+	mov	$num,$num,lsl#2		@ rescale $num for byte count
+	sub	sp,sp,$num		@ alloca(4*num)
+	sub	sp,sp,#4		@ +extra dword
+	sub	$num,$num,#4		@ "num=num-1"
+	add	$tp,$bp,$num		@ &bp[num-1]
+
+	add	$num,sp,$num		@ $num to point at &tp[num-1]
+	ldr	$n0,[$_n0]		@ &n0
+	ldr	$bi,[$bp]		@ bp[0]
+	ldr	$aj,[$ap],#4		@ ap[0],ap++
+	ldr	$nj,[$np],#4		@ np[0],np++
+	ldr	$n0,[$n0]		@ *n0
+	str	$tp,[$_bpend]		@ save &bp[num]
+
+	umull	$alo,$ahi,$aj,$bi	@ ap[0]*bp[0]
+	str	$n0,[$_n0]		@ save n0 value
+	mul	$n0,$alo,$n0		@ "tp[0]"*n0
+	mov	$nlo,#0
+	umlal	$alo,$nlo,$nj,$n0	@ np[0]*n0+"t[0]"
+	mov	$tp,sp
+
+.L1st:
+	ldr	$aj,[$ap],#4		@ ap[j],ap++
+	mov	$alo,$ahi
+	ldr	$nj,[$np],#4		@ np[j],np++
+	mov	$ahi,#0
+	umlal	$alo,$ahi,$aj,$bi	@ ap[j]*bp[0]
+	mov	$nhi,#0
+	umlal	$nlo,$nhi,$nj,$n0	@ np[j]*n0
+	adds	$nlo,$nlo,$alo
+	str	$nlo,[$tp],#4		@ tp[j-1]=,tp++
+	adc	$nlo,$nhi,#0
+	cmp	$tp,$num
+	bne	.L1st
+
+	adds	$nlo,$nlo,$ahi
+	ldr	$tp,[$_bp]		@ restore bp
+	mov	$nhi,#0
+	ldr	$n0,[$_n0]		@ restore n0
+	adc	$nhi,$nhi,#0
+	str	$nlo,[$num]		@ tp[num-1]=
+	str	$nhi,[$num,#4]		@ tp[num]=
+
+.Louter:
+	sub	$tj,$num,sp		@ "original" $num-1 value
+	sub	$ap,$ap,$tj		@ "rewind" ap to &ap[1]
+	ldr	$bi,[$tp,#4]!		@ *(++bp)
+	sub	$np,$np,$tj		@ "rewind" np to &np[1]
+	ldr	$aj,[$ap,#-4]		@ ap[0]
+	ldr	$alo,[sp]		@ tp[0]
+	ldr	$nj,[$np,#-4]		@ np[0]
+	ldr	$tj,[sp,#4]		@ tp[1]
+
+	mov	$ahi,#0
+	umlal	$alo,$ahi,$aj,$bi	@ ap[0]*bp[i]+tp[0]
+	str	$tp,[$_bp]		@ save bp
+	mul	$n0,$alo,$n0
+	mov	$nlo,#0
+	umlal	$alo,$nlo,$nj,$n0	@ np[0]*n0+"tp[0]"
+	mov	$tp,sp
+
+.Linner:
+	ldr	$aj,[$ap],#4		@ ap[j],ap++
+	adds	$alo,$ahi,$tj		@ +=tp[j]
+	ldr	$nj,[$np],#4		@ np[j],np++
+	mov	$ahi,#0
+	umlal	$alo,$ahi,$aj,$bi	@ ap[j]*bp[i]
+	mov	$nhi,#0
+	umlal	$nlo,$nhi,$nj,$n0	@ np[j]*n0
+	adc	$ahi,$ahi,#0
+	ldr	$tj,[$tp,#8]		@ tp[j+1]
+	adds	$nlo,$nlo,$alo
+	str	$nlo,[$tp],#4		@ tp[j-1]=,tp++
+	adc	$nlo,$nhi,#0
+	cmp	$tp,$num
+	bne	.Linner
+
+	adds	$nlo,$nlo,$ahi
+	mov	$nhi,#0
+	ldr	$tp,[$_bp]		@ restore bp
+	adc	$nhi,$nhi,#0
+	ldr	$n0,[$_n0]		@ restore n0
+	adds	$nlo,$nlo,$tj
+	ldr	$tj,[$_bpend]		@ restore &bp[num]
+	adc	$nhi,$nhi,#0
+	str	$nlo,[$num]		@ tp[num-1]=
+	str	$nhi,[$num,#4]		@ tp[num]=
+
+	cmp	$tp,$tj
+	bne	.Louter
+
+	ldr	$rp,[$_rp]		@ pull rp
+	add	$num,$num,#4		@ $num to point at &tp[num]
+	sub	$aj,$num,sp		@ "original" num value
+	mov	$tp,sp			@ "rewind" $tp
+	mov	$ap,$tp			@ "borrow" $ap
+	sub	$np,$np,$aj		@ "rewind" $np to &np[0]
+
+	subs	$tj,$tj,$tj		@ "clear" carry flag
+.Lsub:	ldr	$tj,[$tp],#4
+	ldr	$nj,[$np],#4
+	sbcs	$tj,$tj,$nj		@ tp[j]-np[j]
+	str	$tj,[$rp],#4		@ rp[j]=
+	teq	$tp,$num		@ preserve carry
+	bne	.Lsub
+	sbcs	$nhi,$nhi,#0		@ upmost carry
+	mov	$tp,sp			@ "rewind" $tp
+	sub	$rp,$rp,$aj		@ "rewind" $rp
+
+	and	$ap,$tp,$nhi
+	bic	$np,$rp,$nhi
+	orr	$ap,$ap,$np		@ ap=borrow?tp:rp
+
+.Lcopy:	ldr	$tj,[$ap],#4		@ copy or in-place refresh
+	str	sp,[$tp],#4		@ zap tp
+	str	$tj,[$rp],#4
+	cmp	$tp,$num
+	bne	.Lcopy
+
+	add	sp,$num,#4		@ skip over tp[num+1]
+	ldmia	sp!,{r4-r12,lr}		@ restore registers
+	add	sp,sp,#2*4		@ skip over {r0,r2}
+	mov	r0,#1
+.Labrt:	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+.size	bn_mul_mont,.-bn_mul_mont
+___
+{
+sub Dlo()   { shift=~m|q([1]?[0-9])|?"d".($1*2):"";     }
+sub Dhi()   { shift=~m|q([1]?[0-9])|?"d".($1*2+1):"";   }
+
+my ($A0,$A1,$A2,$A3)=map("d$_",(0..3));
+my ($N0,$N1,$N2,$N3)=map("d$_",(4..7));
+my ($Z,$Temp)=("q4","q5");
+my ($A0xB,$A1xB,$A2xB,$A3xB,$A4xB,$A5xB,$A6xB,$A7xB)=map("q$_",(6..13));
+my ($Bi,$Ni,$M0)=map("d$_",(28..31));
+my $zero=&Dlo($Z);
+my $temp=&Dlo($Temp);
+
+my ($rptr,$aptr,$bptr,$nptr,$n0,$num)=map("r$_",(0..5));
+my ($tinptr,$toutptr,$inner,$outer)=map("r$_",(6..9));
+
+$code.=<<___;
+#if __ARM_ARCH__>=7
+.fpu	neon
+
+.type	bn_mul8x_mont_neon,%function
+.align	5
+bn_mul8x_mont_neon:
+	mov	ip,sp
+	stmdb	sp!,{r4-r11}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+	ldmia	ip,{r4-r5}		@ load rest of parameter block
+
+	sub		$toutptr,sp,#16
+	vld1.32		{${Bi}[0]}, [$bptr,:32]!
+	sub		$toutptr,$toutptr,$num,lsl#4
+	vld1.32		{$A0-$A3},  [$aptr]!		@ can't specify :32 :-(
+	and		$toutptr,$toutptr,#-64
+	vld1.32		{${M0}[0]}, [$n0,:32]
+	mov		sp,$toutptr			@ alloca
+	veor		$zero,$zero,$zero
+	subs		$inner,$num,#8
+	vzip.16		$Bi,$zero
+
+	vmull.u32	$A0xB,$Bi,${A0}[0]
+	vmull.u32	$A1xB,$Bi,${A0}[1]
+	vmull.u32	$A2xB,$Bi,${A1}[0]
+	vshl.i64	$temp,`&Dhi("$A0xB")`,#16
+	vmull.u32	$A3xB,$Bi,${A1}[1]
+
+	vadd.u64	$temp,$temp,`&Dlo("$A0xB")`
+	veor		$zero,$zero,$zero
+	vmul.u32	$Ni,$temp,$M0
+
+	vmull.u32	$A4xB,$Bi,${A2}[0]
+	 vld1.32	{$N0-$N3}, [$nptr]!
+	vmull.u32	$A5xB,$Bi,${A2}[1]
+	vmull.u32	$A6xB,$Bi,${A3}[0]
+	vzip.16		$Ni,$zero
+	vmull.u32	$A7xB,$Bi,${A3}[1]
+
+	bne	.LNEON_1st
+
+	@ special case for num=8, everything is in register bank...
+
+	vmlal.u32	$A0xB,$Ni,${N0}[0]
+	sub		$outer,$num,#1
+	vmlal.u32	$A1xB,$Ni,${N0}[1]
+	vmlal.u32	$A2xB,$Ni,${N1}[0]
+	vmlal.u32	$A3xB,$Ni,${N1}[1]
+
+	vmlal.u32	$A4xB,$Ni,${N2}[0]
+	vmov		$Temp,$A0xB
+	vmlal.u32	$A5xB,$Ni,${N2}[1]
+	vmov		$A0xB,$A1xB
+	vmlal.u32	$A6xB,$Ni,${N3}[0]
+	vmov		$A1xB,$A2xB
+	vmlal.u32	$A7xB,$Ni,${N3}[1]
+	vmov		$A2xB,$A3xB
+	vmov		$A3xB,$A4xB
+	vshr.u64	$temp,$temp,#16
+	vmov		$A4xB,$A5xB
+	vmov		$A5xB,$A6xB
+	vadd.u64	$temp,$temp,`&Dhi("$Temp")`
+	vmov		$A6xB,$A7xB
+	veor		$A7xB,$A7xB
+	vshr.u64	$temp,$temp,#16
+
+	b	.LNEON_outer8
+
+.align	4
+.LNEON_outer8:
+	vld1.32		{${Bi}[0]}, [$bptr,:32]!
+	veor		$zero,$zero,$zero
+	vzip.16		$Bi,$zero
+	vadd.u64	`&Dlo("$A0xB")`,`&Dlo("$A0xB")`,$temp
+
+	vmlal.u32	$A0xB,$Bi,${A0}[0]
+	vmlal.u32	$A1xB,$Bi,${A0}[1]
+	vmlal.u32	$A2xB,$Bi,${A1}[0]
+	vshl.i64	$temp,`&Dhi("$A0xB")`,#16
+	vmlal.u32	$A3xB,$Bi,${A1}[1]
+
+	vadd.u64	$temp,$temp,`&Dlo("$A0xB")`
+	veor		$zero,$zero,$zero
+	subs		$outer,$outer,#1
+	vmul.u32	$Ni,$temp,$M0
+
+	vmlal.u32	$A4xB,$Bi,${A2}[0]
+	vmlal.u32	$A5xB,$Bi,${A2}[1]
+	vmlal.u32	$A6xB,$Bi,${A3}[0]
+	vzip.16		$Ni,$zero
+	vmlal.u32	$A7xB,$Bi,${A3}[1]
+
+	vmlal.u32	$A0xB,$Ni,${N0}[0]
+	vmlal.u32	$A1xB,$Ni,${N0}[1]
+	vmlal.u32	$A2xB,$Ni,${N1}[0]
+	vmlal.u32	$A3xB,$Ni,${N1}[1]
+
+	vmlal.u32	$A4xB,$Ni,${N2}[0]
+	vmov		$Temp,$A0xB
+	vmlal.u32	$A5xB,$Ni,${N2}[1]
+	vmov		$A0xB,$A1xB
+	vmlal.u32	$A6xB,$Ni,${N3}[0]
+	vmov		$A1xB,$A2xB
+	vmlal.u32	$A7xB,$Ni,${N3}[1]
+	vmov		$A2xB,$A3xB
+	vmov		$A3xB,$A4xB
+	vshr.u64	$temp,$temp,#16
+	vmov		$A4xB,$A5xB
+	vmov		$A5xB,$A6xB
+	vadd.u64	$temp,$temp,`&Dhi("$Temp")`
+	vmov		$A6xB,$A7xB
+	veor		$A7xB,$A7xB
+	vshr.u64	$temp,$temp,#16
+
+	bne	.LNEON_outer8
+
+	vadd.u64	`&Dlo("$A0xB")`,`&Dlo("$A0xB")`,$temp
+	mov		$toutptr,sp
+	vshr.u64	$temp,`&Dlo("$A0xB")`,#16
+	mov		$inner,$num
+	vadd.u64	`&Dhi("$A0xB")`,`&Dhi("$A0xB")`,$temp
+	add		$tinptr,sp,#16
+	vshr.u64	$temp,`&Dhi("$A0xB")`,#16
+	vzip.16		`&Dlo("$A0xB")`,`&Dhi("$A0xB")`
+
+	b	.LNEON_tail2
+
+.align	4
+.LNEON_1st:
+	vmlal.u32	$A0xB,$Ni,${N0}[0]
+	 vld1.32	{$A0-$A3}, [$aptr]!
+	vmlal.u32	$A1xB,$Ni,${N0}[1]
+	subs		$inner,$inner,#8
+	vmlal.u32	$A2xB,$Ni,${N1}[0]
+	vmlal.u32	$A3xB,$Ni,${N1}[1]
+
+	vmlal.u32	$A4xB,$Ni,${N2}[0]
+	 vld1.32	{$N0-$N1}, [$nptr]!
+	vmlal.u32	$A5xB,$Ni,${N2}[1]
+	 vst1.64	{$A0xB-$A1xB}, [$toutptr,:256]!
+	vmlal.u32	$A6xB,$Ni,${N3}[0]
+	vmlal.u32	$A7xB,$Ni,${N3}[1]
+	 vst1.64	{$A2xB-$A3xB}, [$toutptr,:256]!
+
+	vmull.u32	$A0xB,$Bi,${A0}[0]
+	 vld1.32	{$N2-$N3}, [$nptr]!
+	vmull.u32	$A1xB,$Bi,${A0}[1]
+	 vst1.64	{$A4xB-$A5xB}, [$toutptr,:256]!
+	vmull.u32	$A2xB,$Bi,${A1}[0]
+	vmull.u32	$A3xB,$Bi,${A1}[1]
+	 vst1.64	{$A6xB-$A7xB}, [$toutptr,:256]!
+
+	vmull.u32	$A4xB,$Bi,${A2}[0]
+	vmull.u32	$A5xB,$Bi,${A2}[1]
+	vmull.u32	$A6xB,$Bi,${A3}[0]
+	vmull.u32	$A7xB,$Bi,${A3}[1]
+
+	bne	.LNEON_1st
+
+	vmlal.u32	$A0xB,$Ni,${N0}[0]
+	add		$tinptr,sp,#16
+	vmlal.u32	$A1xB,$Ni,${N0}[1]
+	sub		$aptr,$aptr,$num,lsl#2		@ rewind $aptr
+	vmlal.u32	$A2xB,$Ni,${N1}[0]
+	 vld1.64	{$Temp}, [sp,:128]
+	vmlal.u32	$A3xB,$Ni,${N1}[1]
+	sub		$outer,$num,#1
+
+	vmlal.u32	$A4xB,$Ni,${N2}[0]
+	vst1.64		{$A0xB-$A1xB}, [$toutptr,:256]!
+	vmlal.u32	$A5xB,$Ni,${N2}[1]
+	vshr.u64	$temp,$temp,#16
+	 vld1.64	{$A0xB},       [$tinptr, :128]!
+	vmlal.u32	$A6xB,$Ni,${N3}[0]
+	vst1.64		{$A2xB-$A3xB}, [$toutptr,:256]!
+	vmlal.u32	$A7xB,$Ni,${N3}[1]
+
+	vst1.64		{$A4xB-$A5xB}, [$toutptr,:256]!
+	vadd.u64	$temp,$temp,`&Dhi("$Temp")`
+	veor		$Z,$Z,$Z
+	vst1.64		{$A6xB-$A7xB}, [$toutptr,:256]!
+	 vld1.64	{$A1xB-$A2xB}, [$tinptr, :256]!
+	vst1.64		{$Z},          [$toutptr,:128]
+	vshr.u64	$temp,$temp,#16
+
+	b		.LNEON_outer
+
+.align	4
+.LNEON_outer:
+	vld1.32		{${Bi}[0]}, [$bptr,:32]!
+	sub		$nptr,$nptr,$num,lsl#2		@ rewind $nptr
+	vld1.32		{$A0-$A3},  [$aptr]!
+	veor		$zero,$zero,$zero
+	mov		$toutptr,sp
+	vzip.16		$Bi,$zero
+	sub		$inner,$num,#8
+	vadd.u64	`&Dlo("$A0xB")`,`&Dlo("$A0xB")`,$temp
+
+	vmlal.u32	$A0xB,$Bi,${A0}[0]
+	 vld1.64	{$A3xB-$A4xB},[$tinptr,:256]!
+	vmlal.u32	$A1xB,$Bi,${A0}[1]
+	vmlal.u32	$A2xB,$Bi,${A1}[0]
+	 vld1.64	{$A5xB-$A6xB},[$tinptr,:256]!
+	vmlal.u32	$A3xB,$Bi,${A1}[1]
+
+	vshl.i64	$temp,`&Dhi("$A0xB")`,#16
+	veor		$zero,$zero,$zero
+	vadd.u64	$temp,$temp,`&Dlo("$A0xB")`
+	 vld1.64	{$A7xB},[$tinptr,:128]!
+	vmul.u32	$Ni,$temp,$M0
+
+	vmlal.u32	$A4xB,$Bi,${A2}[0]
+	 vld1.32	{$N0-$N3}, [$nptr]!
+	vmlal.u32	$A5xB,$Bi,${A2}[1]
+	vmlal.u32	$A6xB,$Bi,${A3}[0]
+	vzip.16		$Ni,$zero
+	vmlal.u32	$A7xB,$Bi,${A3}[1]
+
+.LNEON_inner:
+	vmlal.u32	$A0xB,$Ni,${N0}[0]
+	 vld1.32	{$A0-$A3}, [$aptr]!
+	vmlal.u32	$A1xB,$Ni,${N0}[1]
+	 subs		$inner,$inner,#8
+	vmlal.u32	$A2xB,$Ni,${N1}[0]
+	vmlal.u32	$A3xB,$Ni,${N1}[1]
+	vst1.64		{$A0xB-$A1xB}, [$toutptr,:256]!
+
+	vmlal.u32	$A4xB,$Ni,${N2}[0]
+	 vld1.64	{$A0xB},       [$tinptr, :128]!
+	vmlal.u32	$A5xB,$Ni,${N2}[1]
+	vst1.64		{$A2xB-$A3xB}, [$toutptr,:256]!
+	vmlal.u32	$A6xB,$Ni,${N3}[0]
+	 vld1.64	{$A1xB-$A2xB}, [$tinptr, :256]!
+	vmlal.u32	$A7xB,$Ni,${N3}[1]
+	vst1.64		{$A4xB-$A5xB}, [$toutptr,:256]!
+
+	vmlal.u32	$A0xB,$Bi,${A0}[0]
+	 vld1.64	{$A3xB-$A4xB}, [$tinptr, :256]!
+	vmlal.u32	$A1xB,$Bi,${A0}[1]
+	vst1.64		{$A6xB-$A7xB}, [$toutptr,:256]!
+	vmlal.u32	$A2xB,$Bi,${A1}[0]
+	 vld1.64	{$A5xB-$A6xB}, [$tinptr, :256]!
+	vmlal.u32	$A3xB,$Bi,${A1}[1]
+	 vld1.32	{$N0-$N3}, [$nptr]!
+
+	vmlal.u32	$A4xB,$Bi,${A2}[0]
+	 vld1.64	{$A7xB},       [$tinptr, :128]!
+	vmlal.u32	$A5xB,$Bi,${A2}[1]
+	vmlal.u32	$A6xB,$Bi,${A3}[0]
+	vmlal.u32	$A7xB,$Bi,${A3}[1]
+
+	bne	.LNEON_inner
+
+	vmlal.u32	$A0xB,$Ni,${N0}[0]
+	add		$tinptr,sp,#16
+	vmlal.u32	$A1xB,$Ni,${N0}[1]
+	sub		$aptr,$aptr,$num,lsl#2		@ rewind $aptr
+	vmlal.u32	$A2xB,$Ni,${N1}[0]
+	 vld1.64	{$Temp}, [sp,:128]
+	vmlal.u32	$A3xB,$Ni,${N1}[1]
+	subs		$outer,$outer,#1
+
+	vmlal.u32	$A4xB,$Ni,${N2}[0]
+	vst1.64		{$A0xB-$A1xB}, [$toutptr,:256]!
+	vmlal.u32	$A5xB,$Ni,${N2}[1]
+	 vld1.64	{$A0xB},       [$tinptr, :128]!
+	vshr.u64	$temp,$temp,#16
+	vst1.64		{$A2xB-$A3xB}, [$toutptr,:256]!
+	vmlal.u32	$A6xB,$Ni,${N3}[0]
+	 vld1.64	{$A1xB-$A2xB}, [$tinptr, :256]!
+	vmlal.u32	$A7xB,$Ni,${N3}[1]
+
+	vst1.64		{$A4xB-$A5xB}, [$toutptr,:256]!
+	vadd.u64	$temp,$temp,`&Dhi("$Temp")`
+	vst1.64		{$A6xB-$A7xB}, [$toutptr,:256]!
+	vshr.u64	$temp,$temp,#16
+
+	bne	.LNEON_outer
+
+	mov		$toutptr,sp
+	mov		$inner,$num
+
+.LNEON_tail:
+	vadd.u64	`&Dlo("$A0xB")`,`&Dlo("$A0xB")`,$temp
+	vld1.64		{$A3xB-$A4xB}, [$tinptr, :256]!
+	vshr.u64	$temp,`&Dlo("$A0xB")`,#16
+	vadd.u64	`&Dhi("$A0xB")`,`&Dhi("$A0xB")`,$temp
+	vld1.64		{$A5xB-$A6xB}, [$tinptr, :256]!
+	vshr.u64	$temp,`&Dhi("$A0xB")`,#16
+	vld1.64		{$A7xB},       [$tinptr, :128]!
+	vzip.16		`&Dlo("$A0xB")`,`&Dhi("$A0xB")`
+
+.LNEON_tail2:
+	vadd.u64	`&Dlo("$A1xB")`,`&Dlo("$A1xB")`,$temp
+	vst1.32		{`&Dlo("$A0xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A1xB")`,#16
+	vadd.u64	`&Dhi("$A1xB")`,`&Dhi("$A1xB")`,$temp
+	vshr.u64	$temp,`&Dhi("$A1xB")`,#16
+	vzip.16		`&Dlo("$A1xB")`,`&Dhi("$A1xB")`
+
+	vadd.u64	`&Dlo("$A2xB")`,`&Dlo("$A2xB")`,$temp
+	vst1.32		{`&Dlo("$A1xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A2xB")`,#16
+	vadd.u64	`&Dhi("$A2xB")`,`&Dhi("$A2xB")`,$temp
+	vshr.u64	$temp,`&Dhi("$A2xB")`,#16
+	vzip.16		`&Dlo("$A2xB")`,`&Dhi("$A2xB")`
+
+	vadd.u64	`&Dlo("$A3xB")`,`&Dlo("$A3xB")`,$temp
+	vst1.32		{`&Dlo("$A2xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A3xB")`,#16
+	vadd.u64	`&Dhi("$A3xB")`,`&Dhi("$A3xB")`,$temp
+	vshr.u64	$temp,`&Dhi("$A3xB")`,#16
+	vzip.16		`&Dlo("$A3xB")`,`&Dhi("$A3xB")`
+
+	vadd.u64	`&Dlo("$A4xB")`,`&Dlo("$A4xB")`,$temp
+	vst1.32		{`&Dlo("$A3xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A4xB")`,#16
+	vadd.u64	`&Dhi("$A4xB")`,`&Dhi("$A4xB")`,$temp
+	vshr.u64	$temp,`&Dhi("$A4xB")`,#16
+	vzip.16		`&Dlo("$A4xB")`,`&Dhi("$A4xB")`
+
+	vadd.u64	`&Dlo("$A5xB")`,`&Dlo("$A5xB")`,$temp
+	vst1.32		{`&Dlo("$A4xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A5xB")`,#16
+	vadd.u64	`&Dhi("$A5xB")`,`&Dhi("$A5xB")`,$temp
+	vshr.u64	$temp,`&Dhi("$A5xB")`,#16
+	vzip.16		`&Dlo("$A5xB")`,`&Dhi("$A5xB")`
+
+	vadd.u64	`&Dlo("$A6xB")`,`&Dlo("$A6xB")`,$temp
+	vst1.32		{`&Dlo("$A5xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A6xB")`,#16
+	vadd.u64	`&Dhi("$A6xB")`,`&Dhi("$A6xB")`,$temp
+	vld1.64		{$A0xB}, [$tinptr, :128]!
+	vshr.u64	$temp,`&Dhi("$A6xB")`,#16
+	vzip.16		`&Dlo("$A6xB")`,`&Dhi("$A6xB")`
+
+	vadd.u64	`&Dlo("$A7xB")`,`&Dlo("$A7xB")`,$temp
+	vst1.32		{`&Dlo("$A6xB")`[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,`&Dlo("$A7xB")`,#16
+	vadd.u64	`&Dhi("$A7xB")`,`&Dhi("$A7xB")`,$temp
+	vld1.64		{$A1xB-$A2xB},	[$tinptr, :256]!
+	vshr.u64	$temp,`&Dhi("$A7xB")`,#16
+	vzip.16		`&Dlo("$A7xB")`,`&Dhi("$A7xB")`
+	subs		$inner,$inner,#8
+	vst1.32		{`&Dlo("$A7xB")`[0]}, [$toutptr, :32]!
+
+	bne	.LNEON_tail
+
+	vst1.32	{${temp}[0]}, [$toutptr, :32]		@ top-most bit
+	sub	$nptr,$nptr,$num,lsl#2			@ rewind $nptr
+	subs	$aptr,sp,#0				@ clear carry flag
+	add	$bptr,sp,$num,lsl#2
+
+.LNEON_sub:
+	ldmia	$aptr!, {r4-r7}
+	ldmia	$nptr!, {r8-r11}
+	sbcs	r8, r4,r8
+	sbcs	r9, r5,r9
+	sbcs	r10,r6,r10
+	sbcs	r11,r7,r11
+	teq	$aptr,$bptr				@ preserves carry
+	stmia	$rptr!, {r8-r11}
+	bne	.LNEON_sub
+
+	ldr	r10, [$aptr]				@ load top-most bit
+	veor	q0,q0,q0
+	sub	r11,$bptr,sp				@ this is num*4
+	veor	q1,q1,q1
+	mov	$aptr,sp
+	sub	$rptr,$rptr,r11				@ rewind $rptr
+	mov	$nptr,$bptr				@ second 3/4th of frame
+	sbcs	r10,r10,#0				@ result is carry flag
+
+.LNEON_copy_n_zap:
+	ldmia	$aptr!, {r4-r7}
+	ldmia	$rptr,  {r8-r11}
+	movcc	r8, r4
+	vst1.64	{q0-q1}, [$nptr,:256]!			@ wipe
+	movcc	r9, r5
+	movcc	r10,r6
+	vst1.64	{q0-q1}, [$nptr,:256]!			@ wipe
+	movcc	r11,r7
+	ldmia	$aptr, {r4-r7}
+	stmia	$rptr!, {r8-r11}
+	sub	$aptr,$aptr,#16
+	ldmia	$rptr, {r8-r11}
+	movcc	r8, r4
+	vst1.64	{q0-q1}, [$aptr,:256]!			@ wipe
+	movcc	r9, r5
+	movcc	r10,r6
+	vst1.64	{q0-q1}, [$nptr,:256]!			@ wipe
+	movcc	r11,r7
+	teq	$aptr,$bptr				@ preserves carry
+	stmia	$rptr!, {r8-r11}
+	bne	.LNEON_copy_n_zap
+
+	sub	sp,ip,#96
+        vldmia  sp!,{d8-d15}
+        ldmia   sp!,{r4-r11}
+	bx	lr
+.size	bn_mul8x_mont_neon,.-bn_mul8x_mont_neon
+#endif
+___
+}
+$code.=<<___;
+.asciz	"Montgomery multiplication for ARMv4/NEON, CRYPTOGAMS by <appro\@openssl.org>"
+.align	2
+#if __ARM_ARCH__>=7
+.comm	OPENSSL_armcap_P,4,4
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/bn-586.pl b/crypto/bn/asm/bn-586.pl
new file mode 100644
index 0000000..26d9bcb
--- /dev/null
+++ b/crypto/bn/asm/bn-586.pl
@@ -0,0 +1,774 @@
+#!/usr/bin/env perl
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],$0);
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+&bn_mul_add_words("bn_mul_add_words");
+&bn_mul_words("bn_mul_words");
+&bn_sqr_words("bn_sqr_words");
+&bn_div_words("bn_div_words");
+&bn_add_words("bn_add_words");
+&bn_sub_words("bn_sub_words");
+&bn_sub_part_words("bn_sub_part_words");
+
+&asm_finish();
+
+sub bn_mul_add_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,$sse2?"EXTRN\t_OPENSSL_ia32cap_P:DWORD":"");
+
+	$r="eax";
+	$a="edx";
+	$c="ecx";
+
+	if ($sse2) {
+		&picmeup("eax","OPENSSL_ia32cap_P");
+		&bt(&DWP(0,"eax"),26);
+		&jnc(&label("maw_non_sse2"));
+
+		&mov($r,&wparam(0));
+		&mov($a,&wparam(1));
+		&mov($c,&wparam(2));
+		&movd("mm0",&wparam(3));	# mm0 = w
+		&pxor("mm1","mm1");		# mm1 = carry_in
+		&jmp(&label("maw_sse2_entry"));
+		
+	&set_label("maw_sse2_unrolled",16);
+		&movd("mm3",&DWP(0,$r,"",0));	# mm3 = r[0]
+		&paddq("mm1","mm3");		# mm1 = carry_in + r[0]
+		&movd("mm2",&DWP(0,$a,"",0));	# mm2 = a[0]
+		&pmuludq("mm2","mm0");		# mm2 = w*a[0]
+		&movd("mm4",&DWP(4,$a,"",0));	# mm4 = a[1]
+		&pmuludq("mm4","mm0");		# mm4 = w*a[1]
+		&movd("mm6",&DWP(8,$a,"",0));	# mm6 = a[2]
+		&pmuludq("mm6","mm0");		# mm6 = w*a[2]
+		&movd("mm7",&DWP(12,$a,"",0));	# mm7 = a[3]
+		&pmuludq("mm7","mm0");		# mm7 = w*a[3]
+		&paddq("mm1","mm2");		# mm1 = carry_in + r[0] + w*a[0]
+		&movd("mm3",&DWP(4,$r,"",0));	# mm3 = r[1]
+		&paddq("mm3","mm4");		# mm3 = r[1] + w*a[1]
+		&movd("mm5",&DWP(8,$r,"",0));	# mm5 = r[2]
+		&paddq("mm5","mm6");		# mm5 = r[2] + w*a[2]
+		&movd("mm4",&DWP(12,$r,"",0));	# mm4 = r[3]
+		&paddq("mm7","mm4");		# mm7 = r[3] + w*a[3]
+		&movd(&DWP(0,$r,"",0),"mm1");
+		&movd("mm2",&DWP(16,$a,"",0));	# mm2 = a[4]
+		&pmuludq("mm2","mm0");		# mm2 = w*a[4]
+		&psrlq("mm1",32);		# mm1 = carry0
+		&movd("mm4",&DWP(20,$a,"",0));	# mm4 = a[5]
+		&pmuludq("mm4","mm0");		# mm4 = w*a[5]
+		&paddq("mm1","mm3");		# mm1 = carry0 + r[1] + w*a[1]
+		&movd("mm6",&DWP(24,$a,"",0));	# mm6 = a[6]
+		&pmuludq("mm6","mm0");		# mm6 = w*a[6]
+		&movd(&DWP(4,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry1
+		&movd("mm3",&DWP(28,$a,"",0));	# mm3 = a[7]
+		&add($a,32);
+		&pmuludq("mm3","mm0");		# mm3 = w*a[7]
+		&paddq("mm1","mm5");		# mm1 = carry1 + r[2] + w*a[2]
+		&movd("mm5",&DWP(16,$r,"",0));	# mm5 = r[4]
+		&paddq("mm2","mm5");		# mm2 = r[4] + w*a[4]
+		&movd(&DWP(8,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry2
+		&paddq("mm1","mm7");		# mm1 = carry2 + r[3] + w*a[3]
+		&movd("mm5",&DWP(20,$r,"",0));	# mm5 = r[5]
+		&paddq("mm4","mm5");		# mm4 = r[5] + w*a[5]
+		&movd(&DWP(12,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry3
+		&paddq("mm1","mm2");		# mm1 = carry3 + r[4] + w*a[4]
+		&movd("mm5",&DWP(24,$r,"",0));	# mm5 = r[6]
+		&paddq("mm6","mm5");		# mm6 = r[6] + w*a[6]
+		&movd(&DWP(16,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry4
+		&paddq("mm1","mm4");		# mm1 = carry4 + r[5] + w*a[5]
+		&movd("mm5",&DWP(28,$r,"",0));	# mm5 = r[7]
+		&paddq("mm3","mm5");		# mm3 = r[7] + w*a[7]
+		&movd(&DWP(20,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry5
+		&paddq("mm1","mm6");		# mm1 = carry5 + r[6] + w*a[6]
+		&movd(&DWP(24,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry6
+		&paddq("mm1","mm3");		# mm1 = carry6 + r[7] + w*a[7]
+		&movd(&DWP(28,$r,"",0),"mm1");
+		&lea($r,&DWP(32,$r));
+		&psrlq("mm1",32);		# mm1 = carry_out
+
+		&sub($c,8);
+		&jz(&label("maw_sse2_exit"));
+	&set_label("maw_sse2_entry");
+		&test($c,0xfffffff8);
+		&jnz(&label("maw_sse2_unrolled"));
+
+	&set_label("maw_sse2_loop",4);
+		&movd("mm2",&DWP(0,$a));	# mm2 = a[i]
+		&movd("mm3",&DWP(0,$r));	# mm3 = r[i]
+		&pmuludq("mm2","mm0");		# a[i] *= w
+		&lea($a,&DWP(4,$a));
+		&paddq("mm1","mm3");		# carry += r[i]
+		&paddq("mm1","mm2");		# carry += a[i]*w
+		&movd(&DWP(0,$r),"mm1");	# r[i] = carry_low
+		&sub($c,1);
+		&psrlq("mm1",32);		# carry = carry_high
+		&lea($r,&DWP(4,$r));
+		&jnz(&label("maw_sse2_loop"));
+	&set_label("maw_sse2_exit");
+		&movd("eax","mm1");		# c = carry_out
+		&emms();
+		&ret();
+
+	&set_label("maw_non_sse2",16);
+	}
+
+	# function_begin prologue
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	$Low="eax";
+	$High="edx";
+	$a="ebx";
+	$w="ebp";
+	$r="edi";
+	$c="esi";
+
+	&xor($c,$c);		# clear carry
+	&mov($r,&wparam(0));	#
+
+	&mov("ecx",&wparam(2));	#
+	&mov($a,&wparam(1));	#
+
+	&and("ecx",0xfffffff8);	# num / 8
+	&mov($w,&wparam(3));	#
+
+	&push("ecx");		# Up the stack for a tmp variable
+
+	&jz(&label("maw_finish"));
+
+	&set_label("maw_loop",16);
+
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+
+		 &mov("eax",&DWP($i,$a)); 	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+= c
+		&adc("edx",0);			# H(t)+=carry
+		 &add("eax",&DWP($i,$r));	# L(t)+= *r
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i,$r),"eax");	# *r= L(t);
+		&mov($c,"edx");			# c=  H(t);
+		}
+
+	&comment("");
+	&sub("ecx",8);
+	&lea($a,&DWP(32,$a));
+	&lea($r,&DWP(32,$r));
+	&jnz(&label("maw_loop"));
+
+	&set_label("maw_finish",0);
+	&mov("ecx",&wparam(2));	# get num
+	&and("ecx",7);
+	&jnz(&label("maw_finish2"));	# helps branch prediction
+	&jmp(&label("maw_end"));
+
+	&set_label("maw_finish2",1);
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		 &mov("eax",&DWP($i*4,$a));	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		&adc("edx",0);			# H(t)+=carry
+		 &add("eax",&DWP($i*4,$r));	# L(t)+= *r
+		&adc("edx",0);			# H(t)+=carry
+		 &dec("ecx") if ($i != 7-1);
+		&mov(&DWP($i*4,$r),"eax");	# *r= L(t);
+		 &mov($c,"edx");		# c=  H(t);
+		&jz(&label("maw_end")) if ($i != 7-1);
+		}
+	&set_label("maw_end",0);
+	&mov("eax",$c);
+
+	&pop("ecx");	# clear variable from
+
+	&function_end($name);
+	}
+
+sub bn_mul_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,$sse2?"EXTRN\t_OPENSSL_ia32cap_P:DWORD":"");
+
+	$r="eax";
+	$a="edx";
+	$c="ecx";
+
+	if ($sse2) {
+		&picmeup("eax","OPENSSL_ia32cap_P");
+		&bt(&DWP(0,"eax"),26);
+		&jnc(&label("mw_non_sse2"));
+
+		&mov($r,&wparam(0));
+		&mov($a,&wparam(1));
+		&mov($c,&wparam(2));
+		&movd("mm0",&wparam(3));	# mm0 = w
+		&pxor("mm1","mm1");		# mm1 = carry = 0
+
+	&set_label("mw_sse2_loop",16);
+		&movd("mm2",&DWP(0,$a));	# mm2 = a[i]
+		&pmuludq("mm2","mm0");		# a[i] *= w
+		&lea($a,&DWP(4,$a));
+		&paddq("mm1","mm2");		# carry += a[i]*w
+		&movd(&DWP(0,$r),"mm1");	# r[i] = carry_low
+		&sub($c,1);
+		&psrlq("mm1",32);		# carry = carry_high
+		&lea($r,&DWP(4,$r));
+		&jnz(&label("mw_sse2_loop"));
+
+		&movd("eax","mm1");		# return carry
+		&emms();
+		&ret();
+	&set_label("mw_non_sse2",16);
+	}
+
+	# function_begin prologue
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	$Low="eax";
+	$High="edx";
+	$a="ebx";
+	$w="ecx";
+	$r="edi";
+	$c="esi";
+	$num="ebp";
+
+	&xor($c,$c);		# clear carry
+	&mov($r,&wparam(0));	#
+	&mov($a,&wparam(1));	#
+	&mov($num,&wparam(2));	#
+	&mov($w,&wparam(3));	#
+
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("mw_finish"));
+
+	&set_label("mw_loop",0);
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+
+		 &mov("eax",&DWP($i,$a,"",0)); 	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 # XXX
+
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i,$r,"",0),"eax");	# *r= L(t);
+
+		&mov($c,"edx");			# c=  H(t);
+		}
+
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jz(&label("mw_finish"));
+	&jmp(&label("mw_loop"));
+
+	&set_label("mw_finish",0);
+	&mov($num,&wparam(2));	# get num
+	&and($num,7);
+	&jnz(&label("mw_finish2"));
+	&jmp(&label("mw_end"));
+
+	&set_label("mw_finish2",1);
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		 &mov("eax",&DWP($i*4,$a,"",0));# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 # XXX
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i*4,$r,"",0),"eax");# *r= L(t);
+		&mov($c,"edx");			# c=  H(t);
+		 &dec($num) if ($i != 7-1);
+		&jz(&label("mw_end")) if ($i != 7-1);
+		}
+	&set_label("mw_end",0);
+	&mov("eax",$c);
+
+	&function_end($name);
+	}
+
+sub bn_sqr_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,$sse2?"EXTRN\t_OPENSSL_ia32cap_P:DWORD":"");
+
+	$r="eax";
+	$a="edx";
+	$c="ecx";
+
+	if ($sse2) {
+		&picmeup("eax","OPENSSL_ia32cap_P");
+		&bt(&DWP(0,"eax"),26);
+		&jnc(&label("sqr_non_sse2"));
+
+		&mov($r,&wparam(0));
+		&mov($a,&wparam(1));
+		&mov($c,&wparam(2));
+
+	&set_label("sqr_sse2_loop",16);
+		&movd("mm0",&DWP(0,$a));	# mm0 = a[i]
+		&pmuludq("mm0","mm0");		# a[i] *= a[i]
+		&lea($a,&DWP(4,$a));		# a++
+		&movq(&QWP(0,$r),"mm0");	# r[i] = a[i]*a[i]
+		&sub($c,1);
+		&lea($r,&DWP(8,$r));		# r += 2
+		&jnz(&label("sqr_sse2_loop"));
+
+		&emms();
+		&ret();
+	&set_label("sqr_non_sse2",16);
+	}
+
+	# function_begin prologue
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	$r="esi";
+	$a="edi";
+	$num="ebx";
+
+	&mov($r,&wparam(0));	#
+	&mov($a,&wparam(1));	#
+	&mov($num,&wparam(2));	#
+
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("sw_finish"));
+
+	&set_label("sw_loop",0);
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+		&mov("eax",&DWP($i,$a,"",0)); 	# *a
+		 # XXX
+		&mul("eax");			# *a * *a
+		&mov(&DWP($i*2,$r,"",0),"eax");	#
+		 &mov(&DWP($i*2+4,$r,"",0),"edx");#
+		}
+
+	&comment("");
+	&add($a,32);
+	&add($r,64);
+	&sub($num,8);
+	&jnz(&label("sw_loop"));
+
+	&set_label("sw_finish",0);
+	&mov($num,&wparam(2));	# get num
+	&and($num,7);
+	&jz(&label("sw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov("eax",&DWP($i*4,$a,"",0));	# *a
+		 # XXX
+		&mul("eax");			# *a * *a
+		&mov(&DWP($i*8,$r,"",0),"eax");	#
+		 &dec($num) if ($i != 7-1);
+		&mov(&DWP($i*8+4,$r,"",0),"edx");
+		 &jz(&label("sw_end")) if ($i != 7-1);
+		}
+	&set_label("sw_end",0);
+
+	&function_end($name);
+	}
+
+sub bn_div_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,"");
+	&mov("edx",&wparam(0));	#
+	&mov("eax",&wparam(1));	#
+	&mov("ecx",&wparam(2));	#
+	&div("ecx");
+	&ret();
+	&function_end_B($name);
+	}
+
+sub bn_add_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&add($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &add($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+		&add($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &add($tmp1,$tmp2);
+		&adc($c,0);
+		 &dec($num) if ($i != 6);
+		&mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
+sub bn_sub_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &dec($num) if ($i != 6);
+		&mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
+sub bn_sub_part_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP(0,$a,"",0));	# *a
+		 &mov($tmp2,&DWP(0,$b,"",0));# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		&mov(&DWP(0,$r,"",0),$tmp1);	# *r
+		&add($a, 4);
+		&add($b, 4);
+		&add($r, 4);
+		 &dec($num) if ($i != 6);
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+	&cmp(&wparam(4),0);
+	&je(&label("pw_end"));
+
+	&mov($num,&wparam(4));	# get dl
+	&cmp($num,0);
+	&je(&label("pw_end"));
+	&jge(&label("pw_pos"));
+
+	&comment("pw_neg");
+	&mov($tmp2,0);
+	&sub($tmp2,$num);
+	&mov($num,$tmp2);
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("pw_neg_finish"));
+
+	&set_label("pw_neg_loop",0);
+	for ($i=0; $i<8; $i++)
+	{
+	    &comment("dl<0 Round $i");
+
+	    &mov($tmp1,0);
+	    &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+	    &sub($tmp1,$c);
+	    &mov($c,0);
+	    &adc($c,$c);
+	    &sub($tmp1,$tmp2);
+	    &adc($c,0);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+	}
+	    
+	&comment("");
+	&add($b,32);
+	&add($r,32);
+	&sub($num,8);
+	&jnz(&label("pw_neg_loop"));
+	    
+	&set_label("pw_neg_finish",0);
+	&mov($tmp2,&wparam(4));	# get dl
+	&mov($num,0);
+	&sub($num,$tmp2);
+	&and($num,7);
+	&jz(&label("pw_end"));
+	    
+	for ($i=0; $i<7; $i++)
+	{
+	    &comment("dl<0 Tail Round $i");
+	    &mov($tmp1,0);
+	    &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+	    &sub($tmp1,$c);
+	    &mov($c,0);
+	    &adc($c,$c);
+	    &sub($tmp1,$tmp2);
+	    &adc($c,0);
+	    &dec($num) if ($i != 6);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &jz(&label("pw_end")) if ($i != 6);
+	}
+
+	&jmp(&label("pw_end"));
+	
+	&set_label("pw_pos",0);
+	
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("pw_pos_finish"));
+
+	&set_label("pw_pos_loop",0);
+
+	for ($i=0; $i<8; $i++)
+	{
+	    &comment("dl>0 Round $i");
+
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &sub($tmp1,$c);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &jnc(&label("pw_nc".$i));
+	}
+	    
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jnz(&label("pw_pos_loop"));
+	    
+	&set_label("pw_pos_finish",0);
+	&mov($num,&wparam(4));	# get dl
+	&and($num,7);
+	&jz(&label("pw_end"));
+	    
+	for ($i=0; $i<7; $i++)
+	{
+	    &comment("dl>0 Tail Round $i");
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &sub($tmp1,$c);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &jnc(&label("pw_tail_nc".$i));
+	    &dec($num) if ($i != 6);
+	    &jz(&label("pw_end")) if ($i != 6);
+	}
+	&mov($c,1);
+	&jmp(&label("pw_end"));
+
+	&set_label("pw_nc_loop",0);
+	for ($i=0; $i<8; $i++)
+	{
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &set_label("pw_nc".$i,0);
+	}
+	    
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jnz(&label("pw_nc_loop"));
+	    
+	&mov($num,&wparam(4));	# get dl
+	&and($num,7);
+	&jz(&label("pw_nc_end"));
+	    
+	for ($i=0; $i<7; $i++)
+	{
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &set_label("pw_tail_nc".$i,0);
+	    &dec($num) if ($i != 6);
+	    &jz(&label("pw_nc_end")) if ($i != 6);
+	}
+
+	&set_label("pw_nc_end",0);
+	&mov($c,0);
+
+	&set_label("pw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
diff --git a/crypto/bn/asm/co-586.pl b/crypto/bn/asm/co-586.pl
new file mode 100644
index 0000000..57101a6
--- /dev/null
+++ b/crypto/bn/asm/co-586.pl
@@ -0,0 +1,287 @@
+#!/usr/local/bin/perl
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],$0);
+
+&bn_mul_comba("bn_mul_comba8",8);
+&bn_mul_comba("bn_mul_comba4",4);
+&bn_sqr_comba("bn_sqr_comba8",8);
+&bn_sqr_comba("bn_sqr_comba4",4);
+
+&asm_finish();
+
+sub mul_add_c
+	{
+	local($a,$ai,$b,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("mul a[$ai]*b[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$b,"",0));
+
+	&mul("edx");
+	&add($c0,"eax");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# laod next a
+	 &mov("eax",&wparam(0)) if $pos > 0;			# load r[]
+	 ###
+	&adc($c1,"edx");
+	 &mov("edx",&DWP(($nb)*4,$b,"",0)) if $pos == 0;	# laod next b
+	 &mov("edx",&DWP(($nb)*4,$b,"",0)) if $pos == 1;	# laod next b
+	 ###
+	&adc($c2,0);
+	 # is pos > 1, it means it is the last loop 
+	 &mov(&DWP($i*4,"eax","",0),$c0) if $pos > 0;		# save r[];
+	&mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;		# laod next a
+	}
+
+sub sqr_add_c
+	{
+	local($r,$a,$ai,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("sqr a[$ai]*a[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$b,"",0));
+
+	if ($ai == $bi)
+		{ &mul("eax");}
+	else
+		{ &mul("edx");}
+	&add($c0,"eax");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# load next a
+	 ###
+	&adc($c1,"edx");
+	 &mov("edx",&DWP(($nb)*4,$a,"",0)) if ($pos == 1) && ($na != $nb);
+	 ###
+	&adc($c2,0);
+	 # is pos > 1, it means it is the last loop 
+	 &mov(&DWP($i*4,$r,"",0),$c0) if $pos > 0;		# save r[];
+	&mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;		# load next b
+	}
+
+sub sqr_add_c2
+	{
+	local($r,$a,$ai,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("sqr a[$ai]*a[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$a,"",0));
+
+	if ($ai == $bi)
+		{ &mul("eax");}
+	else
+		{ &mul("edx");}
+	&add("eax","eax");
+	 ###
+	&adc("edx","edx");
+	 ###
+	&adc($c2,0);
+	 &add($c0,"eax");
+	&adc($c1,"edx");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# load next a
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;	# load next b
+	&adc($c2,0);
+	&mov(&DWP($i*4,$r,"",0),$c0) if $pos > 0;		# save r[];
+	 &mov("edx",&DWP(($nb)*4,$a,"",0)) if ($pos <= 1) && ($na != $nb);
+	 ###
+	}
+
+sub bn_mul_comba
+	{
+	local($name,$num)=@_;
+	local($a,$b,$c0,$c1,$c2);
+	local($i,$as,$ae,$bs,$be,$ai,$bi);
+	local($tot,$end);
+
+	&function_begin_B($name,"");
+
+	$c0="ebx";
+	$c1="ecx";
+	$c2="ebp";
+	$a="esi";
+	$b="edi";
+	
+	$as=0;
+	$ae=0;
+	$bs=0;
+	$be=0;
+	$tot=$num+$num-1;
+
+	&push("esi");
+	 &mov($a,&wparam(1));
+	&push("edi");
+	 &mov($b,&wparam(2));
+	&push("ebp");
+	 &push("ebx");
+
+	&xor($c0,$c0);
+	 &mov("eax",&DWP(0,$a,"",0));	# load the first word 
+	&xor($c1,$c1);
+	 &mov("edx",&DWP(0,$b,"",0));	# load the first second 
+
+	for ($i=0; $i<$tot; $i++)
+		{
+		$ai=$as;
+		$bi=$bs;
+		$end=$be+1;
+
+		&comment("################## Calculate word $i"); 
+
+		for ($j=$bs; $j<$end; $j++)
+			{
+			&xor($c2,$c2) if ($j == $bs);
+			if (($j+1) == $end)
+				{
+				$v=1;
+				$v=2 if (($i+1) == $tot);
+				}
+			else
+				{ $v=0; }
+			if (($j+1) != $end)
+				{
+				$na=($ai-1);
+				$nb=($bi+1);
+				}
+			else
+				{
+				$na=$as+($i < ($num-1));
+				$nb=$bs+($i >= ($num-1));
+				}
+#printf STDERR "[$ai,$bi] -> [$na,$nb]\n";
+			&mul_add_c($a,$ai,$b,$bi,$c0,$c1,$c2,$v,$i,$na,$nb);
+			if ($v)
+				{
+				&comment("saved r[$i]");
+				# &mov("eax",&wparam(0));
+				# &mov(&DWP($i*4,"eax","",0),$c0);
+				($c0,$c1,$c2)=($c1,$c2,$c0);
+				}
+			$ai--;
+			$bi++;
+			}
+		$as++ if ($i < ($num-1));
+		$ae++ if ($i >= ($num-1));
+
+		$bs++ if ($i >= ($num-1));
+		$be++ if ($i < ($num-1));
+		}
+	&comment("save r[$i]");
+	# &mov("eax",&wparam(0));
+	&mov(&DWP($i*4,"eax","",0),$c0);
+
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
+
+sub bn_sqr_comba
+	{
+	local($name,$num)=@_;
+	local($r,$a,$c0,$c1,$c2)=@_;
+	local($i,$as,$ae,$bs,$be,$ai,$bi);
+	local($b,$tot,$end,$half);
+
+	&function_begin_B($name,"");
+
+	$c0="ebx";
+	$c1="ecx";
+	$c2="ebp";
+	$a="esi";
+	$r="edi";
+
+	&push("esi");
+	 &push("edi");
+	&push("ebp");
+	 &push("ebx");
+	&mov($r,&wparam(0));
+	 &mov($a,&wparam(1));
+	&xor($c0,$c0);
+	 &xor($c1,$c1);
+	&mov("eax",&DWP(0,$a,"",0)); # load the first word
+
+	$as=0;
+	$ae=0;
+	$bs=0;
+	$be=0;
+	$tot=$num+$num-1;
+
+	for ($i=0; $i<$tot; $i++)
+		{
+		$ai=$as;
+		$bi=$bs;
+		$end=$be+1;
+
+		&comment("############### Calculate word $i");
+		for ($j=$bs; $j<$end; $j++)
+			{
+			&xor($c2,$c2) if ($j == $bs);
+			if (($ai-1) < ($bi+1))
+				{
+				$v=1;
+				$v=2 if ($i+1) == $tot;
+				}
+			else
+				{ $v=0; }
+			if (!$v)
+				{
+				$na=$ai-1;
+				$nb=$bi+1;
+				}
+			else
+				{
+				$na=$as+($i < ($num-1));
+				$nb=$bs+($i >= ($num-1));
+				}
+			if ($ai == $bi)
+				{
+				&sqr_add_c($r,$a,$ai,$bi,
+					$c0,$c1,$c2,$v,$i,$na,$nb);
+				}
+			else
+				{
+				&sqr_add_c2($r,$a,$ai,$bi,
+					$c0,$c1,$c2,$v,$i,$na,$nb);
+				}
+			if ($v)
+				{
+				&comment("saved r[$i]");
+				#&mov(&DWP($i*4,$r,"",0),$c0);
+				($c0,$c1,$c2)=($c1,$c2,$c0);
+				last;
+				}
+			$ai--;
+			$bi++;
+			}
+		$as++ if ($i < ($num-1));
+		$ae++ if ($i >= ($num-1));
+
+		$bs++ if ($i >= ($num-1));
+		$be++ if ($i < ($num-1));
+		}
+	&mov(&DWP($i*4,$r,"",0),$c0);
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
diff --git a/crypto/bn/asm/modexp512-x86_64.pl b/crypto/bn/asm/modexp512-x86_64.pl
new file mode 100644
index 0000000..bfd6e97
--- /dev/null
+++ b/crypto/bn/asm/modexp512-x86_64.pl
@@ -0,0 +1,1497 @@
+#!/usr/bin/env perl
+#
+# Copyright (c) 2010-2011 Intel Corp.
+#   Author: Vinodh.Gopal@intel.com
+#           Jim Guilford
+#           Erdinc.Ozturk@intel.com
+#           Maxim.Perminov@intel.com
+#
+# More information about algorithm used can be found at:
+#   http://www.cse.buffalo.edu/srds2009/escs2009_submission_Gopal.pdf
+#
+# ====================================================================
+# Copyright (c) 2011 The OpenSSL Project.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+#    software must display the following acknowledgment:
+#    "This product includes software developed by the OpenSSL Project
+#    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+#
+# 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission. For written permission, please contact
+#    licensing@OpenSSL.org.
+#
+# 5. Products derived from this software may not be called "OpenSSL"
+#    nor may "OpenSSL" appear in their names without prior written
+#    permission of the OpenSSL Project.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+#    acknowledgment:
+#    "This product includes software developed by the OpenSSL Project
+#    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+#
+# THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+my $win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+use strict;
+my $code=".text\n\n";
+my $m=0;
+
+#
+# Define x512 macros
+#
+
+#MULSTEP_512_ADD	MACRO	x7, x6, x5, x4, x3, x2, x1, x0, dst, src1, src2, add_src, tmp1, tmp2
+#
+# uses rax, rdx, and args
+sub MULSTEP_512_ADD
+{
+ my ($x, $DST, $SRC2, $ASRC, $OP, $TMP)=@_;
+ my @X=@$x;	# make a copy
+$code.=<<___;
+	 mov	(+8*0)($SRC2), %rax
+	 mul	$OP			# rdx:rax = %OP * [0]
+	 mov	($ASRC), $X[0]
+	 add	%rax, $X[0]
+	 adc	\$0, %rdx
+	 mov	$X[0], $DST
+___
+for(my $i=1;$i<8;$i++) {
+$code.=<<___;
+	 mov	%rdx, $TMP
+
+	 mov	(+8*$i)($SRC2), %rax
+	 mul	$OP			# rdx:rax = %OP * [$i]
+	 mov	(+8*$i)($ASRC), $X[$i]
+	 add	%rax, $X[$i]
+	 adc	\$0, %rdx
+	 add	$TMP, $X[$i]
+	 adc	\$0, %rdx
+___
+}
+$code.=<<___;
+	 mov	%rdx, $X[0]
+___
+}
+
+#MULSTEP_512	MACRO	x7, x6, x5, x4, x3, x2, x1, x0, dst, src2, src1_val, tmp
+#
+# uses rax, rdx, and args
+sub MULSTEP_512
+{
+ my ($x, $DST, $SRC2, $OP, $TMP)=@_;
+ my @X=@$x;	# make a copy
+$code.=<<___;
+	 mov	(+8*0)($SRC2), %rax
+	 mul	$OP			# rdx:rax = %OP * [0]
+	 add	%rax, $X[0]
+	 adc	\$0, %rdx
+	 mov	$X[0], $DST
+___
+for(my $i=1;$i<8;$i++) {
+$code.=<<___;
+	 mov	%rdx, $TMP
+
+	 mov	(+8*$i)($SRC2), %rax
+	 mul	$OP			# rdx:rax = %OP * [$i]
+	 add	%rax, $X[$i]
+	 adc	\$0, %rdx
+	 add	$TMP, $X[$i]
+	 adc	\$0, %rdx
+___
+}
+$code.=<<___;
+	 mov	%rdx, $X[0]
+___
+}
+
+#
+# Swizzle Macros
+#
+
+# macro to copy data from flat space to swizzled table
+#MACRO swizzle	pDst, pSrc, tmp1, tmp2
+# pDst and pSrc are modified
+sub swizzle
+{
+ my ($pDst, $pSrc, $cnt, $d0)=@_;
+$code.=<<___;
+	 mov	\$8, $cnt
+loop_$m:
+	 mov	($pSrc), $d0
+	 mov	$d0#w, ($pDst)
+	 shr	\$16, $d0
+	 mov	$d0#w, (+64*1)($pDst)
+	 shr	\$16, $d0
+	 mov	$d0#w, (+64*2)($pDst)
+	 shr	\$16, $d0
+	 mov	$d0#w, (+64*3)($pDst)
+	 lea	8($pSrc), $pSrc
+	 lea	64*4($pDst), $pDst
+	 dec	$cnt
+	 jnz	loop_$m
+___
+
+ $m++;
+}
+
+# macro to copy data from swizzled table to  flat space
+#MACRO unswizzle	pDst, pSrc, tmp*3
+sub unswizzle
+{
+ my ($pDst, $pSrc, $cnt, $d0, $d1)=@_;
+$code.=<<___;
+	 mov	\$4, $cnt
+loop_$m:
+	 movzxw	(+64*3+256*0)($pSrc), $d0
+	 movzxw	(+64*3+256*1)($pSrc), $d1
+	 shl	\$16, $d0
+	 shl	\$16, $d1
+	 mov	(+64*2+256*0)($pSrc), $d0#w
+	 mov	(+64*2+256*1)($pSrc), $d1#w
+	 shl	\$16, $d0
+	 shl	\$16, $d1
+	 mov	(+64*1+256*0)($pSrc), $d0#w
+	 mov	(+64*1+256*1)($pSrc), $d1#w
+	 shl	\$16, $d0
+	 shl	\$16, $d1
+	 mov	(+64*0+256*0)($pSrc), $d0#w
+	 mov	(+64*0+256*1)($pSrc), $d1#w
+	 mov	$d0, (+8*0)($pDst)
+	 mov	$d1, (+8*1)($pDst)
+	 lea	256*2($pSrc), $pSrc
+	 lea	8*2($pDst), $pDst
+	 sub	\$1, $cnt
+	 jnz	loop_$m
+___
+
+ $m++;
+}
+
+#
+# Data Structures
+#
+
+# Reduce Data
+#
+#
+# Offset  Value
+# 0C0     Carries
+# 0B8     X2[10]
+# 0B0     X2[9]
+# 0A8     X2[8]
+# 0A0     X2[7]
+# 098     X2[6]
+# 090     X2[5]
+# 088     X2[4]
+# 080     X2[3]
+# 078     X2[2]
+# 070     X2[1]
+# 068     X2[0]
+# 060     X1[12]  P[10]
+# 058     X1[11]  P[9]  Z[8]
+# 050     X1[10]  P[8]  Z[7]
+# 048     X1[9]   P[7]  Z[6]
+# 040     X1[8]   P[6]  Z[5]
+# 038     X1[7]   P[5]  Z[4]
+# 030     X1[6]   P[4]  Z[3]
+# 028     X1[5]   P[3]  Z[2]
+# 020     X1[4]   P[2]  Z[1]
+# 018     X1[3]   P[1]  Z[0]
+# 010     X1[2]   P[0]  Y[2]
+# 008     X1[1]   Q[1]  Y[1]
+# 000     X1[0]   Q[0]  Y[0]
+
+my $X1_offset           =  0;			# 13 qwords
+my $X2_offset           =  $X1_offset + 13*8;			# 11 qwords
+my $Carries_offset      =  $X2_offset + 11*8;			# 1 qword
+my $Q_offset            =  0;			# 2 qwords
+my $P_offset            =  $Q_offset + 2*8;			# 11 qwords
+my $Y_offset            =  0;			# 3 qwords
+my $Z_offset            =  $Y_offset + 3*8;			# 9 qwords
+
+my $Red_Data_Size       =  $Carries_offset + 1*8;			# (25 qwords)
+
+#
+# Stack Frame
+#
+#
+# offset	value
+# ...		<old stack contents>
+# ...
+# 280		Garray
+
+# 278		tmp16[15]
+# ...		...
+# 200		tmp16[0]
+
+# 1F8		tmp[7]
+# ...		...
+# 1C0		tmp[0]
+
+# 1B8		GT[7]
+# ...		...
+# 180		GT[0]
+
+# 178		Reduce Data
+# ...		...
+# 0B8		Reduce Data
+# 0B0		reserved
+# 0A8		reserved
+# 0A0		reserved
+# 098		reserved
+# 090		reserved
+# 088		reduce result addr
+# 080		exp[8]
+
+# ...
+# 048		exp[1]
+# 040		exp[0]
+
+# 038		reserved
+# 030		loop_idx
+# 028		pg
+# 020		i
+# 018		pData	; arg 4
+# 010		pG	; arg 2
+# 008		pResult	; arg 1
+# 000		rsp	; stack pointer before subtract
+
+my $rsp_offset          =  0;
+my $pResult_offset      =  8*1 + $rsp_offset;
+my $pG_offset           =  8*1 + $pResult_offset;
+my $pData_offset        =  8*1 + $pG_offset;
+my $i_offset            =  8*1 + $pData_offset;
+my $pg_offset           =  8*1 + $i_offset;
+my $loop_idx_offset     =  8*1 + $pg_offset;
+my $reserved1_offset    =  8*1 + $loop_idx_offset;
+my $exp_offset          =  8*1 + $reserved1_offset;
+my $red_result_addr_offset=  8*9 + $exp_offset;
+my $reserved2_offset    =  8*1 + $red_result_addr_offset;
+my $Reduce_Data_offset  =  8*5 + $reserved2_offset;
+my $GT_offset           =  $Red_Data_Size + $Reduce_Data_offset;
+my $tmp_offset          =  8*8 + $GT_offset;
+my $tmp16_offset        =  8*8 + $tmp_offset;
+my $garray_offset       =  8*16 + $tmp16_offset;
+my $mem_size            =  8*8*32 + $garray_offset;
+
+#
+# Offsets within Reduce Data
+#
+#
+#	struct MODF_2FOLD_MONT_512_C1_DATA {
+#	UINT64 t[8][8];
+#	UINT64 m[8];
+#	UINT64 m1[8]; /* 2^768 % m */
+#	UINT64 m2[8]; /* 2^640 % m */
+#	UINT64 k1[2]; /* (- 1/m) % 2^128 */
+#	};
+
+my $T                   =  0;
+my $M                   =  512;			# = 8 * 8 * 8
+my $M1                  =  576;			# = 8 * 8 * 9 /* += 8 * 8 */
+my $M2                  =  640;			# = 8 * 8 * 10 /* += 8 * 8 */
+my $K1                  =  704;			# = 8 * 8 * 11 /* += 8 * 8 */
+
+#
+#   FUNCTIONS
+#
+
+{{{
+#
+# MULADD_128x512 : Function to multiply 128-bits (2 qwords) by 512-bits (8 qwords)
+#                       and add 512-bits (8 qwords)
+#                       to get 640 bits (10 qwords)
+# Input: 128-bit mul source: [rdi+8*1], rbp
+#        512-bit mul source: [rsi+8*n]
+#        512-bit add source: r15, r14, ..., r9, r8
+# Output: r9, r8, r15, r14, r13, r12, r11, r10, [rcx+8*1], [rcx+8*0]
+# Clobbers all regs except: rcx, rsi, rdi
+$code.=<<___;
+.type	MULADD_128x512,\@abi-omnipotent
+.align	16
+MULADD_128x512:
+___
+	&MULSTEP_512([map("%r$_",(8..15))], "(+8*0)(%rcx)", "%rsi", "%rbp", "%rbx");
+$code.=<<___;
+	 mov	(+8*1)(%rdi), %rbp
+___
+	&MULSTEP_512([map("%r$_",(9..15,8))], "(+8*1)(%rcx)", "%rsi", "%rbp", "%rbx");
+$code.=<<___;
+	 ret
+.size	MULADD_128x512,.-MULADD_128x512
+___
+}}}
+
+{{{
+#MULADD_256x512	MACRO	pDst, pA, pB, OP, TMP, X7, X6, X5, X4, X3, X2, X1, X0
+#
+# Inputs: pDst: Destination  (768 bits, 12 qwords)
+#         pA:   Multiplicand (1024 bits, 16 qwords)
+#         pB:   Multiplicand (512 bits, 8 qwords)
+# Dst = Ah * B + Al
+# where Ah is (in qwords) A[15:12] (256 bits) and Al is A[7:0] (512 bits)
+# Results in X3 X2 X1 X0 X7 X6 X5 X4 Dst[3:0]
+# Uses registers: arguments, RAX, RDX
+sub MULADD_256x512
+{
+ my ($pDst, $pA, $pB, $OP, $TMP, $X)=@_;
+$code.=<<___;
+	mov	(+8*12)($pA), $OP
+___
+	&MULSTEP_512_ADD($X, "(+8*0)($pDst)", $pB, $pA, $OP, $TMP);
+	push(@$X,shift(@$X));
+
+$code.=<<___;
+	 mov	(+8*13)($pA), $OP
+___
+	&MULSTEP_512($X, "(+8*1)($pDst)", $pB, $OP, $TMP);
+	push(@$X,shift(@$X));
+
+$code.=<<___;
+	 mov	(+8*14)($pA), $OP
+___
+	&MULSTEP_512($X, "(+8*2)($pDst)", $pB, $OP, $TMP);
+	push(@$X,shift(@$X));
+
+$code.=<<___;
+	 mov	(+8*15)($pA), $OP
+___
+	&MULSTEP_512($X, "(+8*3)($pDst)", $pB, $OP, $TMP);
+	push(@$X,shift(@$X));
+}
+
+#
+# mont_reduce(UINT64 *x,  /* 1024 bits, 16 qwords */
+#	       UINT64 *m,  /*  512 bits,  8 qwords */
+#	       MODF_2FOLD_MONT_512_C1_DATA *data,
+#             UINT64 *r)  /*  512 bits,  8 qwords */
+# Input:  x (number to be reduced): tmp16 (Implicit)
+#         m (modulus):              [pM]  (Implicit)
+#         data (reduce data):       [pData] (Implicit)
+# Output: r (result):		     Address in [red_res_addr]
+#         result also in: r9, r8, r15, r14, r13, r12, r11, r10
+
+my @X=map("%r$_",(8..15));
+
+$code.=<<___;
+.type	mont_reduce,\@abi-omnipotent
+.align	16
+mont_reduce:
+___
+
+my $STACK_DEPTH         =  8;
+	#
+	# X1 = Xh * M1 + Xl
+$code.=<<___;
+	 lea	(+$Reduce_Data_offset+$X1_offset+$STACK_DEPTH)(%rsp), %rdi			# pX1 (Dst) 769 bits, 13 qwords
+	 mov	(+$pData_offset+$STACK_DEPTH)(%rsp), %rsi			# pM1 (Bsrc) 512 bits, 8 qwords
+	 add	\$$M1, %rsi
+	 lea	(+$tmp16_offset+$STACK_DEPTH)(%rsp), %rcx			# X (Asrc) 1024 bits, 16 qwords
+
+___
+
+	&MULADD_256x512("%rdi", "%rcx", "%rsi", "%rbp", "%rbx", \@X);	# rotates @X 4 times
+	# results in r11, r10, r9, r8, r15, r14, r13, r12, X1[3:0]
+
+$code.=<<___;
+	 xor	%rax, %rax
+	# X1 += xl
+	 add	(+8*8)(%rcx), $X[4]
+	 adc	(+8*9)(%rcx), $X[5]
+	 adc	(+8*10)(%rcx), $X[6]
+	 adc	(+8*11)(%rcx), $X[7]
+	 adc	\$0, %rax
+	# X1 is now rax, r11-r8, r15-r12, tmp16[3:0]
+
+	#
+	# check for carry ;; carry stored in rax
+	 mov	$X[4], (+8*8)(%rdi)			# rdi points to X1
+	 mov	$X[5], (+8*9)(%rdi)
+	 mov	$X[6], %rbp
+	 mov	$X[7], (+8*11)(%rdi)
+
+	 mov	%rax, (+$Reduce_Data_offset+$Carries_offset+$STACK_DEPTH)(%rsp)
+
+	 mov	(+8*0)(%rdi), $X[4]
+	 mov	(+8*1)(%rdi), $X[5]
+	 mov	(+8*2)(%rdi), $X[6]
+	 mov	(+8*3)(%rdi), $X[7]
+
+	# X1 is now stored in: X1[11], rbp, X1[9:8], r15-r8
+	# rdi -> X1
+	# rsi -> M1
+
+	#
+	# X2 = Xh * M2 + Xl
+	# do first part (X2 = Xh * M2)
+	 add	\$8*10, %rdi			# rdi -> pXh ; 128 bits, 2 qwords
+				#        Xh is actually { [rdi+8*1], rbp }
+	 add	\$`$M2-$M1`, %rsi			# rsi -> M2
+	 lea	(+$Reduce_Data_offset+$X2_offset+$STACK_DEPTH)(%rsp), %rcx			# rcx -> pX2 ; 641 bits, 11 qwords
+___
+	unshift(@X,pop(@X));	unshift(@X,pop(@X));
+$code.=<<___;
+
+	 call	MULADD_128x512			# args in rcx, rdi / rbp, rsi, r15-r8
+	# result in r9, r8, r15, r14, r13, r12, r11, r10, X2[1:0]
+	 mov	(+$Reduce_Data_offset+$Carries_offset+$STACK_DEPTH)(%rsp), %rax
+
+	# X2 += Xl
+	 add	(+8*8-8*10)(%rdi), $X[6]		# (-8*10) is to adjust rdi -> Xh to Xl
+	 adc	(+8*9-8*10)(%rdi), $X[7]
+	 mov	$X[6], (+8*8)(%rcx)
+	 mov	$X[7], (+8*9)(%rcx)
+
+	 adc	%rax, %rax
+	 mov	%rax, (+$Reduce_Data_offset+$Carries_offset+$STACK_DEPTH)(%rsp)
+
+	 lea	(+$Reduce_Data_offset+$Q_offset+$STACK_DEPTH)(%rsp), %rdi			# rdi -> pQ ; 128 bits, 2 qwords
+	 add	\$`$K1-$M2`, %rsi			# rsi -> pK1 ; 128 bits, 2 qwords
+
+	# MUL_128x128t128	rdi, rcx, rsi	; Q = X2 * K1 (bottom half)
+	# B1:B0 = rsi[1:0] = K1[1:0]
+	# A1:A0 = rcx[1:0] = X2[1:0]
+	# Result = rdi[1],rbp = Q[1],rbp
+	 mov	(%rsi), %r8			# B0
+	 mov	(+8*1)(%rsi), %rbx			# B1
+
+	 mov	(%rcx), %rax			# A0
+	 mul	%r8			# B0
+	 mov	%rax, %rbp
+	 mov	%rdx, %r9
+
+	 mov	(+8*1)(%rcx), %rax			# A1
+	 mul	%r8			# B0
+	 add	%rax, %r9
+
+	 mov	(%rcx), %rax			# A0
+	 mul	%rbx			# B1
+	 add	%rax, %r9
+
+	 mov	%r9, (+8*1)(%rdi)
+	# end MUL_128x128t128
+
+	 sub	\$`$K1-$M`, %rsi
+
+	 mov	(%rcx), $X[6]
+	 mov	(+8*1)(%rcx), $X[7]			# r9:r8 = X2[1:0]
+
+	 call	MULADD_128x512			# args in rcx, rdi / rbp, rsi, r15-r8
+	# result in r9, r8, r15, r14, r13, r12, r11, r10, X2[1:0]
+
+	# load first half of m to rdx, rdi, rbx, rax
+	# moved this here for efficiency
+	 mov	(+8*0)(%rsi), %rax
+	 mov	(+8*1)(%rsi), %rbx
+	 mov	(+8*2)(%rsi), %rdi
+	 mov	(+8*3)(%rsi), %rdx
+
+	# continue with reduction
+	 mov	(+$Reduce_Data_offset+$Carries_offset+$STACK_DEPTH)(%rsp), %rbp
+
+	 add	(+8*8)(%rcx), $X[6]
+	 adc	(+8*9)(%rcx), $X[7]
+
+	#accumulate the final carry to rbp
+	 adc	%rbp, %rbp
+
+	# Add in overflow corrections: R = (X2>>128) += T[overflow]
+	# R = {r9, r8, r15, r14, ..., r10}
+	 shl	\$3, %rbp
+	 mov	(+$pData_offset+$STACK_DEPTH)(%rsp), %rcx			# rsi -> Data (and points to T)
+	 add	%rcx, %rbp			# pT ; 512 bits, 8 qwords, spread out
+
+	# rsi will be used to generate a mask after the addition
+	 xor	%rsi, %rsi
+
+	 add	(+8*8*0)(%rbp), $X[0]
+	 adc	(+8*8*1)(%rbp), $X[1]
+	 adc	(+8*8*2)(%rbp), $X[2]
+	 adc	(+8*8*3)(%rbp), $X[3]
+	 adc	(+8*8*4)(%rbp), $X[4]
+	 adc	(+8*8*5)(%rbp), $X[5]
+	 adc	(+8*8*6)(%rbp), $X[6]
+	 adc	(+8*8*7)(%rbp), $X[7]
+
+	# if there is a carry:	rsi = 0xFFFFFFFFFFFFFFFF
+	# if carry is clear:	rsi = 0x0000000000000000
+	 sbb	\$0, %rsi
+
+	# if carry is clear, subtract 0. Otherwise, subtract 256 bits of m
+	 and	%rsi, %rax
+	 and	%rsi, %rbx
+	 and	%rsi, %rdi
+	 and	%rsi, %rdx
+
+	 mov	\$1, %rbp
+	 sub	%rax, $X[0]
+	 sbb	%rbx, $X[1]
+	 sbb	%rdi, $X[2]
+	 sbb	%rdx, $X[3]
+
+	# if there is a borrow:		rbp = 0
+	# if there is no borrow:	rbp = 1
+	# this is used to save the borrows in between the first half and the 2nd half of the subtraction of m
+	 sbb	\$0, %rbp
+
+	#load second half of m to rdx, rdi, rbx, rax
+
+	 add	\$$M, %rcx
+	 mov	(+8*4)(%rcx), %rax
+	 mov	(+8*5)(%rcx), %rbx
+	 mov	(+8*6)(%rcx), %rdi
+	 mov	(+8*7)(%rcx), %rdx
+
+	# use the rsi mask as before
+	# if carry is clear, subtract 0. Otherwise, subtract 256 bits of m
+	 and	%rsi, %rax
+	 and	%rsi, %rbx
+	 and	%rsi, %rdi
+	 and	%rsi, %rdx
+
+	# if rbp = 0, there was a borrow before, it is moved to the carry flag
+	# if rbp = 1, there was not a borrow before, carry flag is cleared
+	 sub	\$1, %rbp
+
+	 sbb	%rax, $X[4]
+	 sbb	%rbx, $X[5]
+	 sbb	%rdi, $X[6]
+	 sbb	%rdx, $X[7]
+
+	# write R back to memory
+
+	 mov	(+$red_result_addr_offset+$STACK_DEPTH)(%rsp), %rsi
+	 mov	$X[0], (+8*0)(%rsi)
+	 mov	$X[1], (+8*1)(%rsi)
+	 mov	$X[2], (+8*2)(%rsi)
+	 mov	$X[3], (+8*3)(%rsi)
+	 mov	$X[4], (+8*4)(%rsi)
+	 mov	$X[5], (+8*5)(%rsi)
+	 mov	$X[6], (+8*6)(%rsi)
+	 mov	$X[7], (+8*7)(%rsi)
+
+	 ret
+.size	mont_reduce,.-mont_reduce
+___
+}}}
+
+{{{
+#MUL_512x512	MACRO	pDst, pA, pB, x7, x6, x5, x4, x3, x2, x1, x0, tmp*2
+#
+# Inputs: pDst: Destination  (1024 bits, 16 qwords)
+#         pA:   Multiplicand (512 bits, 8 qwords)
+#         pB:   Multiplicand (512 bits, 8 qwords)
+# Uses registers rax, rdx, args
+#   B operand in [pB] and also in x7...x0
+sub MUL_512x512
+{
+ my ($pDst, $pA, $pB, $x, $OP, $TMP, $pDst_o)=@_;
+ my ($pDst,  $pDst_o) = ($pDst =~ m/([^+]*)\+?(.*)?/);
+ my @X=@$x;	# make a copy
+
+$code.=<<___;
+	 mov	(+8*0)($pA), $OP
+
+	 mov	$X[0], %rax
+	 mul	$OP			# rdx:rax = %OP * [0]
+	 mov	%rax, (+$pDst_o+8*0)($pDst)
+	 mov	%rdx, $X[0]
+___
+for(my $i=1;$i<8;$i++) {
+$code.=<<___;
+	 mov	$X[$i], %rax
+	 mul	$OP			# rdx:rax = %OP * [$i]
+	 add	%rax, $X[$i-1]
+	 adc	\$0, %rdx
+	 mov	%rdx, $X[$i]
+___
+}
+
+for(my $i=1;$i<8;$i++) {
+$code.=<<___;
+	 mov	(+8*$i)($pA), $OP
+___
+
+	&MULSTEP_512(\@X, "(+$pDst_o+8*$i)($pDst)", $pB, $OP, $TMP);
+	push(@X,shift(@X));
+}
+
+$code.=<<___;
+	 mov	$X[0], (+$pDst_o+8*8)($pDst)
+	 mov	$X[1], (+$pDst_o+8*9)($pDst)
+	 mov	$X[2], (+$pDst_o+8*10)($pDst)
+	 mov	$X[3], (+$pDst_o+8*11)($pDst)
+	 mov	$X[4], (+$pDst_o+8*12)($pDst)
+	 mov	$X[5], (+$pDst_o+8*13)($pDst)
+	 mov	$X[6], (+$pDst_o+8*14)($pDst)
+	 mov	$X[7], (+$pDst_o+8*15)($pDst)
+___
+}
+
+#
+# mont_mul_a3b : subroutine to compute (Src1 * Src2) % M (all 512-bits)
+# Input:  src1: Address of source 1: rdi
+#         src2: Address of source 2: rsi
+# Output: dst:  Address of destination: [red_res_addr]
+#    src2 and result also in: r9, r8, r15, r14, r13, r12, r11, r10
+# Temp:   Clobbers [tmp16], all registers
+$code.=<<___;
+.type	mont_mul_a3b,\@abi-omnipotent
+.align	16
+mont_mul_a3b:
+	#
+	# multiply tmp = src1 * src2
+	# For multiply: dst = rcx, src1 = rdi, src2 = rsi
+	# stack depth is extra 8 from call
+___
+	&MUL_512x512("%rsp+$tmp16_offset+8", "%rdi", "%rsi", [map("%r$_",(10..15,8..9))], "%rbp", "%rbx");
+$code.=<<___;
+	#
+	# Dst = tmp % m
+	# Call reduce(tmp, m, data, dst)
+
+	# tail recursion optimization: jmp to mont_reduce and return from there
+	 jmp	mont_reduce
+	# call	mont_reduce
+	# ret
+.size	mont_mul_a3b,.-mont_mul_a3b
+___
+}}}
+
+{{{
+#SQR_512 MACRO pDest, pA, x7, x6, x5, x4, x3, x2, x1, x0, tmp*4
+#
+# Input in memory [pA] and also in x7...x0
+# Uses all argument registers plus rax and rdx
+#
+# This version computes all of the off-diagonal terms into memory,
+# and then it adds in the diagonal terms
+
+sub SQR_512
+{
+ my ($pDst, $pA, $x, $A, $tmp, $x7, $x6, $pDst_o)=@_;
+ my ($pDst,  $pDst_o) = ($pDst =~ m/([^+]*)\+?(.*)?/);
+ my @X=@$x;	# make a copy
+$code.=<<___;
+	# ------------------
+	# first pass 01...07
+	# ------------------
+	 mov	$X[0], $A
+
+	 mov	$X[1],%rax
+	 mul	$A
+	 mov	%rax, (+$pDst_o+8*1)($pDst)
+___
+for(my $i=2;$i<8;$i++) {
+$code.=<<___;
+	 mov	%rdx, $X[$i-2]
+	 mov	$X[$i],%rax
+	 mul	$A
+	 add	%rax, $X[$i-2]
+	 adc	\$0, %rdx
+___
+}
+$code.=<<___;
+	 mov	%rdx, $x7
+
+	 mov	$X[0], (+$pDst_o+8*2)($pDst)
+
+	# ------------------
+	# second pass 12...17
+	# ------------------
+
+	 mov	(+8*1)($pA), $A
+
+	 mov	(+8*2)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[1]
+	 adc	\$0, %rdx
+	 mov	$X[1], (+$pDst_o+8*3)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	(+8*3)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[2]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[2]
+	 adc	\$0, %rdx
+	 mov	$X[2], (+$pDst_o+8*4)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	(+8*4)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[3]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[3]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[0]
+	 mov	(+8*5)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[4]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[4]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[0]
+	 mov	$X[6],%rax
+	 mul	$A
+	 add	%rax, $X[5]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[5]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[0]
+	 mov	$X[7],%rax
+	 mul	$A
+	 add	%rax, $x7
+	 adc	\$0, %rdx
+	 add	$X[0], $x7
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[1]
+
+	# ------------------
+	# third pass 23...27
+	# ------------------
+	 mov	(+8*2)($pA), $A
+
+	 mov	(+8*3)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[3]
+	 adc	\$0, %rdx
+	 mov	$X[3], (+$pDst_o+8*5)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	(+8*4)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[4]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[4]
+	 adc	\$0, %rdx
+	 mov	$X[4], (+$pDst_o+8*6)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	(+8*5)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[5]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[5]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[0]
+	 mov	$X[6],%rax
+	 mul	$A
+	 add	%rax, $x7
+	 adc	\$0, %rdx
+	 add	$X[0], $x7
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[0]
+	 mov	$X[7],%rax
+	 mul	$A
+	 add	%rax, $X[1]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[1]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[2]
+
+	# ------------------
+	# fourth pass 34...37
+	# ------------------
+
+	 mov	(+8*3)($pA), $A
+
+	 mov	(+8*4)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[5]
+	 adc	\$0, %rdx
+	 mov	$X[5], (+$pDst_o+8*7)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	(+8*5)($pA),%rax
+	 mul	$A
+	 add	%rax, $x7
+	 adc	\$0, %rdx
+	 add	$X[0], $x7
+	 adc	\$0, %rdx
+	 mov	$x7, (+$pDst_o+8*8)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	$X[6],%rax
+	 mul	$A
+	 add	%rax, $X[1]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[1]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[0]
+	 mov	$X[7],%rax
+	 mul	$A
+	 add	%rax, $X[2]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[2]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[5]
+
+	# ------------------
+	# fifth pass 45...47
+	# ------------------
+	 mov	(+8*4)($pA), $A
+
+	 mov	(+8*5)($pA),%rax
+	 mul	$A
+	 add	%rax, $X[1]
+	 adc	\$0, %rdx
+	 mov	$X[1], (+$pDst_o+8*9)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	$X[6],%rax
+	 mul	$A
+	 add	%rax, $X[2]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[2]
+	 adc	\$0, %rdx
+	 mov	$X[2], (+$pDst_o+8*10)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	$X[7],%rax
+	 mul	$A
+	 add	%rax, $X[5]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[5]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $X[1]
+
+	# ------------------
+	# sixth pass 56...57
+	# ------------------
+	 mov	(+8*5)($pA), $A
+
+	 mov	$X[6],%rax
+	 mul	$A
+	 add	%rax, $X[5]
+	 adc	\$0, %rdx
+	 mov	$X[5], (+$pDst_o+8*11)($pDst)
+
+	 mov	%rdx, $X[0]
+	 mov	$X[7],%rax
+	 mul	$A
+	 add	%rax, $X[1]
+	 adc	\$0, %rdx
+	 add	$X[0], $X[1]
+	 adc	\$0, %rdx
+	 mov	$X[1], (+$pDst_o+8*12)($pDst)
+
+	 mov	%rdx, $X[2]
+
+	# ------------------
+	# seventh pass 67
+	# ------------------
+	 mov	$X[6], $A
+
+	 mov	$X[7],%rax
+	 mul	$A
+	 add	%rax, $X[2]
+	 adc	\$0, %rdx
+	 mov	$X[2], (+$pDst_o+8*13)($pDst)
+
+	 mov	%rdx, (+$pDst_o+8*14)($pDst)
+
+	# start finalize (add	in squares, and double off-terms)
+	 mov	(+$pDst_o+8*1)($pDst), $X[0]
+	 mov	(+$pDst_o+8*2)($pDst), $X[1]
+	 mov	(+$pDst_o+8*3)($pDst), $X[2]
+	 mov	(+$pDst_o+8*4)($pDst), $X[3]
+	 mov	(+$pDst_o+8*5)($pDst), $X[4]
+	 mov	(+$pDst_o+8*6)($pDst), $X[5]
+
+	 mov	(+8*3)($pA), %rax
+	 mul	%rax
+	 mov	%rax, $x6
+	 mov	%rdx, $X[6]
+
+	 add	$X[0], $X[0]
+	 adc	$X[1], $X[1]
+	 adc	$X[2], $X[2]
+	 adc	$X[3], $X[3]
+	 adc	$X[4], $X[4]
+	 adc	$X[5], $X[5]
+	 adc	\$0, $X[6]
+
+	 mov	(+8*0)($pA), %rax
+	 mul	%rax
+	 mov	%rax, (+$pDst_o+8*0)($pDst)
+	 mov	%rdx, $A
+
+	 mov	(+8*1)($pA), %rax
+	 mul	%rax
+
+	 add	$A, $X[0]
+	 adc	%rax, $X[1]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $A
+	 mov	$X[0], (+$pDst_o+8*1)($pDst)
+	 mov	$X[1], (+$pDst_o+8*2)($pDst)
+
+	 mov	(+8*2)($pA), %rax
+	 mul	%rax
+
+	 add	$A, $X[2]
+	 adc	%rax, $X[3]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $A
+
+	 mov	$X[2], (+$pDst_o+8*3)($pDst)
+	 mov	$X[3], (+$pDst_o+8*4)($pDst)
+
+	 xor	$tmp, $tmp
+	 add	$A, $X[4]
+	 adc	$x6, $X[5]
+	 adc	\$0, $tmp
+
+	 mov	$X[4], (+$pDst_o+8*5)($pDst)
+	 mov	$X[5], (+$pDst_o+8*6)($pDst)
+
+	# %%tmp has 0/1 in column 7
+	# %%A6 has a full value in column 7
+
+	 mov	(+$pDst_o+8*7)($pDst), $X[0]
+	 mov	(+$pDst_o+8*8)($pDst), $X[1]
+	 mov	(+$pDst_o+8*9)($pDst), $X[2]
+	 mov	(+$pDst_o+8*10)($pDst), $X[3]
+	 mov	(+$pDst_o+8*11)($pDst), $X[4]
+	 mov	(+$pDst_o+8*12)($pDst), $X[5]
+	 mov	(+$pDst_o+8*13)($pDst), $x6
+	 mov	(+$pDst_o+8*14)($pDst), $x7
+
+	 mov	$X[7], %rax
+	 mul	%rax
+	 mov	%rax, $X[7]
+	 mov	%rdx, $A
+
+	 add	$X[0], $X[0]
+	 adc	$X[1], $X[1]
+	 adc	$X[2], $X[2]
+	 adc	$X[3], $X[3]
+	 adc	$X[4], $X[4]
+	 adc	$X[5], $X[5]
+	 adc	$x6, $x6
+	 adc	$x7, $x7
+	 adc	\$0, $A
+
+	 add	$tmp, $X[0]
+
+	 mov	(+8*4)($pA), %rax
+	 mul	%rax
+
+	 add	$X[6], $X[0]
+	 adc	%rax, $X[1]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $tmp
+
+	 mov	$X[0], (+$pDst_o+8*7)($pDst)
+	 mov	$X[1], (+$pDst_o+8*8)($pDst)
+
+	 mov	(+8*5)($pA), %rax
+	 mul	%rax
+
+	 add	$tmp, $X[2]
+	 adc	%rax, $X[3]
+	 adc	\$0, %rdx
+
+	 mov	%rdx, $tmp
+
+	 mov	$X[2], (+$pDst_o+8*9)($pDst)
+	 mov	$X[3], (+$pDst_o+8*10)($pDst)
+
+	 mov	(+8*6)($pA), %rax
+	 mul	%rax
+
+	 add	$tmp, $X[4]
+	 adc	%rax, $X[5]
+	 adc	\$0, %rdx
+
+	 mov	$X[4], (+$pDst_o+8*11)($pDst)
+	 mov	$X[5], (+$pDst_o+8*12)($pDst)
+
+	 add	%rdx, $x6
+	 adc	$X[7], $x7
+	 adc	\$0, $A
+
+	 mov	$x6, (+$pDst_o+8*13)($pDst)
+	 mov	$x7, (+$pDst_o+8*14)($pDst)
+	 mov	$A, (+$pDst_o+8*15)($pDst)
+___
+}
+
+#
+# sqr_reduce: subroutine to compute Result = reduce(Result * Result)
+#
+# input and result also in: r9, r8, r15, r14, r13, r12, r11, r10
+#
+$code.=<<___;
+.type	sqr_reduce,\@abi-omnipotent
+.align	16
+sqr_reduce:
+	 mov	(+$pResult_offset+8)(%rsp), %rcx
+___
+	&SQR_512("%rsp+$tmp16_offset+8", "%rcx", [map("%r$_",(10..15,8..9))], "%rbx", "%rbp", "%rsi", "%rdi");
+$code.=<<___;
+	# tail recursion optimization: jmp to mont_reduce and return from there
+	 jmp	mont_reduce
+	# call	mont_reduce
+	# ret
+.size	sqr_reduce,.-sqr_reduce
+___
+}}}
+
+#
+# MAIN FUNCTION
+#
+
+#mod_exp_512(UINT64 *result, /* 512 bits, 8 qwords */
+#           UINT64 *g,   /* 512 bits, 8 qwords */
+#           UINT64 *exp, /* 512 bits, 8 qwords */
+#           struct mod_ctx_512 *data)
+
+# window size = 5
+# table size = 2^5 = 32
+#table_entries	equ	32
+#table_size	equ	table_entries * 8
+$code.=<<___;
+.globl	mod_exp_512
+.type	mod_exp_512,\@function,4
+mod_exp_512:
+	 push	%rbp
+	 push	%rbx
+	 push	%r12
+	 push	%r13
+	 push	%r14
+	 push	%r15
+
+	# adjust stack down and then align it with cache boundary
+	 mov	%rsp, %r8
+	 sub	\$$mem_size, %rsp
+	 and	\$-64, %rsp
+
+	# store previous stack pointer and arguments
+	 mov	%r8, (+$rsp_offset)(%rsp)
+	 mov	%rdi, (+$pResult_offset)(%rsp)
+	 mov	%rsi, (+$pG_offset)(%rsp)
+	 mov	%rcx, (+$pData_offset)(%rsp)
+.Lbody:
+	# transform g into montgomery space
+	# GT = reduce(g * C2) = reduce(g * (2^256))
+	# reduce expects to have the input in [tmp16]
+	 pxor	%xmm4, %xmm4
+	 movdqu	(+16*0)(%rsi), %xmm0
+	 movdqu	(+16*1)(%rsi), %xmm1
+	 movdqu	(+16*2)(%rsi), %xmm2
+	 movdqu	(+16*3)(%rsi), %xmm3
+	 movdqa	%xmm4, (+$tmp16_offset+16*0)(%rsp)
+	 movdqa	%xmm4, (+$tmp16_offset+16*1)(%rsp)
+	 movdqa	%xmm4, (+$tmp16_offset+16*6)(%rsp)
+	 movdqa	%xmm4, (+$tmp16_offset+16*7)(%rsp)
+	 movdqa	%xmm0, (+$tmp16_offset+16*2)(%rsp)
+	 movdqa	%xmm1, (+$tmp16_offset+16*3)(%rsp)
+	 movdqa	%xmm2, (+$tmp16_offset+16*4)(%rsp)
+	 movdqa	%xmm3, (+$tmp16_offset+16*5)(%rsp)
+
+	# load pExp before rdx gets blown away
+	 movdqu	(+16*0)(%rdx), %xmm0
+	 movdqu	(+16*1)(%rdx), %xmm1
+	 movdqu	(+16*2)(%rdx), %xmm2
+	 movdqu	(+16*3)(%rdx), %xmm3
+
+	 lea	(+$GT_offset)(%rsp), %rbx
+	 mov	%rbx, (+$red_result_addr_offset)(%rsp)
+	 call	mont_reduce
+
+	# Initialize tmp = C
+	 lea	(+$tmp_offset)(%rsp), %rcx
+	 xor	%rax, %rax
+	 mov	%rax, (+8*0)(%rcx)
+	 mov	%rax, (+8*1)(%rcx)
+	 mov	%rax, (+8*3)(%rcx)
+	 mov	%rax, (+8*4)(%rcx)
+	 mov	%rax, (+8*5)(%rcx)
+	 mov	%rax, (+8*6)(%rcx)
+	 mov	%rax, (+8*7)(%rcx)
+	 mov	%rax, (+$exp_offset+8*8)(%rsp)
+	 movq	\$1, (+8*2)(%rcx)
+
+	 lea	(+$garray_offset)(%rsp), %rbp
+	 mov	%rcx, %rsi			# pTmp
+	 mov	%rbp, %rdi			# Garray[][0]
+___
+
+	&swizzle("%rdi", "%rcx", "%rax", "%rbx");
+
+	# for (rax = 31; rax != 0; rax--) {
+	#     tmp = reduce(tmp * G)
+	#     swizzle(pg, tmp);
+	#     pg += 2; }
+$code.=<<___;
+	 mov	\$31, %rax
+	 mov	%rax, (+$i_offset)(%rsp)
+	 mov	%rbp, (+$pg_offset)(%rsp)
+	# rsi -> pTmp
+	 mov	%rsi, (+$red_result_addr_offset)(%rsp)
+	 mov	(+8*0)(%rsi), %r10
+	 mov	(+8*1)(%rsi), %r11
+	 mov	(+8*2)(%rsi), %r12
+	 mov	(+8*3)(%rsi), %r13
+	 mov	(+8*4)(%rsi), %r14
+	 mov	(+8*5)(%rsi), %r15
+	 mov	(+8*6)(%rsi), %r8
+	 mov	(+8*7)(%rsi), %r9
+init_loop:
+	 lea	(+$GT_offset)(%rsp), %rdi
+	 call	mont_mul_a3b
+	 lea	(+$tmp_offset)(%rsp), %rsi
+	 mov	(+$pg_offset)(%rsp), %rbp
+	 add	\$2, %rbp
+	 mov	%rbp, (+$pg_offset)(%rsp)
+	 mov	%rsi, %rcx			# rcx = rsi = addr of tmp
+___
+
+	&swizzle("%rbp", "%rcx", "%rax", "%rbx");
+$code.=<<___;
+	 mov	(+$i_offset)(%rsp), %rax
+	 sub	\$1, %rax
+	 mov	%rax, (+$i_offset)(%rsp)
+	 jne	init_loop
+
+	#
+	# Copy exponent onto stack
+	 movdqa	%xmm0, (+$exp_offset+16*0)(%rsp)
+	 movdqa	%xmm1, (+$exp_offset+16*1)(%rsp)
+	 movdqa	%xmm2, (+$exp_offset+16*2)(%rsp)
+	 movdqa	%xmm3, (+$exp_offset+16*3)(%rsp)
+
+
+	#
+	# Do exponentiation
+	# Initialize result to G[exp{511:507}]
+	 mov	(+$exp_offset+62)(%rsp), %eax
+	 mov	%rax, %rdx
+	 shr	\$11, %rax
+	 and	\$0x07FF, %edx
+	 mov	%edx, (+$exp_offset+62)(%rsp)
+	 lea	(+$garray_offset)(%rsp,%rax,2), %rsi
+	 mov	(+$pResult_offset)(%rsp), %rdx
+___
+
+	&unswizzle("%rdx", "%rsi", "%rbp", "%rbx", "%rax");
+
+	#
+	# Loop variables
+	# rcx = [loop_idx] = index: 510-5 to 0 by 5
+$code.=<<___;
+	 movq	\$505, (+$loop_idx_offset)(%rsp)
+
+	 mov	(+$pResult_offset)(%rsp), %rcx
+	 mov	%rcx, (+$red_result_addr_offset)(%rsp)
+	 mov	(+8*0)(%rcx), %r10
+	 mov	(+8*1)(%rcx), %r11
+	 mov	(+8*2)(%rcx), %r12
+	 mov	(+8*3)(%rcx), %r13
+	 mov	(+8*4)(%rcx), %r14
+	 mov	(+8*5)(%rcx), %r15
+	 mov	(+8*6)(%rcx), %r8
+	 mov	(+8*7)(%rcx), %r9
+	 jmp	sqr_2
+
+main_loop_a3b:
+	 call	sqr_reduce
+	 call	sqr_reduce
+	 call	sqr_reduce
+sqr_2:
+	 call	sqr_reduce
+	 call	sqr_reduce
+
+	#
+	# Do multiply, first look up proper value in Garray
+	 mov	(+$loop_idx_offset)(%rsp), %rcx			# bit index
+	 mov	%rcx, %rax
+	 shr	\$4, %rax			# rax is word pointer
+	 mov	(+$exp_offset)(%rsp,%rax,2), %edx
+	 and	\$15, %rcx
+	 shrq	%cl, %rdx
+	 and	\$0x1F, %rdx
+
+	 lea	(+$garray_offset)(%rsp,%rdx,2), %rsi
+	 lea	(+$tmp_offset)(%rsp), %rdx
+	 mov	%rdx, %rdi
+___
+
+	&unswizzle("%rdx", "%rsi", "%rbp", "%rbx", "%rax");
+	# rdi = tmp = pG
+
+	#
+	# Call mod_mul_a1(pDst,  pSrc1, pSrc2, pM, pData)
+	#                 result result pG     M   Data
+$code.=<<___;
+	 mov	(+$pResult_offset)(%rsp), %rsi
+	 call	mont_mul_a3b
+
+	#
+	# finish loop
+	 mov	(+$loop_idx_offset)(%rsp), %rcx
+	 sub	\$5, %rcx
+	 mov	%rcx, (+$loop_idx_offset)(%rsp)
+	 jge	main_loop_a3b
+
+	#
+
+end_main_loop_a3b:
+	# transform result out of Montgomery space
+	# result = reduce(result)
+	 mov	(+$pResult_offset)(%rsp), %rdx
+	 pxor	%xmm4, %xmm4
+	 movdqu	(+16*0)(%rdx), %xmm0
+	 movdqu	(+16*1)(%rdx), %xmm1
+	 movdqu	(+16*2)(%rdx), %xmm2
+	 movdqu	(+16*3)(%rdx), %xmm3
+	 movdqa	%xmm4, (+$tmp16_offset+16*4)(%rsp)
+	 movdqa	%xmm4, (+$tmp16_offset+16*5)(%rsp)
+	 movdqa	%xmm4, (+$tmp16_offset+16*6)(%rsp)
+	 movdqa	%xmm4, (+$tmp16_offset+16*7)(%rsp)
+	 movdqa	%xmm0, (+$tmp16_offset+16*0)(%rsp)
+	 movdqa	%xmm1, (+$tmp16_offset+16*1)(%rsp)
+	 movdqa	%xmm2, (+$tmp16_offset+16*2)(%rsp)
+	 movdqa	%xmm3, (+$tmp16_offset+16*3)(%rsp)
+	 call	mont_reduce
+
+	# If result > m, subract m
+	# load result into r15:r8
+	 mov	(+$pResult_offset)(%rsp), %rax
+	 mov	(+8*0)(%rax), %r8
+	 mov	(+8*1)(%rax), %r9
+	 mov	(+8*2)(%rax), %r10
+	 mov	(+8*3)(%rax), %r11
+	 mov	(+8*4)(%rax), %r12
+	 mov	(+8*5)(%rax), %r13
+	 mov	(+8*6)(%rax), %r14
+	 mov	(+8*7)(%rax), %r15
+
+	# subtract m
+	 mov	(+$pData_offset)(%rsp), %rbx
+	 add	\$$M, %rbx
+
+	 sub	(+8*0)(%rbx), %r8
+	 sbb	(+8*1)(%rbx), %r9
+	 sbb	(+8*2)(%rbx), %r10
+	 sbb	(+8*3)(%rbx), %r11
+	 sbb	(+8*4)(%rbx), %r12
+	 sbb	(+8*5)(%rbx), %r13
+	 sbb	(+8*6)(%rbx), %r14
+	 sbb	(+8*7)(%rbx), %r15
+
+	# if Carry is clear, replace result with difference
+	 mov	(+8*0)(%rax), %rsi
+	 mov	(+8*1)(%rax), %rdi
+	 mov	(+8*2)(%rax), %rcx
+	 mov	(+8*3)(%rax), %rdx
+	 cmovnc	%r8, %rsi
+	 cmovnc	%r9, %rdi
+	 cmovnc	%r10, %rcx
+	 cmovnc	%r11, %rdx
+	 mov	%rsi, (+8*0)(%rax)
+	 mov	%rdi, (+8*1)(%rax)
+	 mov	%rcx, (+8*2)(%rax)
+	 mov	%rdx, (+8*3)(%rax)
+
+	 mov	(+8*4)(%rax), %rsi
+	 mov	(+8*5)(%rax), %rdi
+	 mov	(+8*6)(%rax), %rcx
+	 mov	(+8*7)(%rax), %rdx
+	 cmovnc	%r12, %rsi
+	 cmovnc	%r13, %rdi
+	 cmovnc	%r14, %rcx
+	 cmovnc	%r15, %rdx
+	 mov	%rsi, (+8*4)(%rax)
+	 mov	%rdi, (+8*5)(%rax)
+	 mov	%rcx, (+8*6)(%rax)
+	 mov	%rdx, (+8*7)(%rax)
+
+	 mov	(+$rsp_offset)(%rsp), %rsi
+	 mov	0(%rsi),%r15
+	 mov	8(%rsi),%r14
+	 mov	16(%rsi),%r13
+	 mov	24(%rsi),%r12
+	 mov	32(%rsi),%rbx
+	 mov	40(%rsi),%rbp
+	 lea	48(%rsi),%rsp
+.Lepilogue:
+	 ret
+.size mod_exp_512, . - mod_exp_512
+___
+
+if ($win64) {
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+my $rec="%rcx";
+my $frame="%rdx";
+my $context="%r8";
+my $disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	mod_exp_512_se_handler,\@abi-omnipotent
+.align	16
+mod_exp_512_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lbody(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	mov	$rsp_offset(%rax),%rax	# pull saved Rsp
+
+	mov	32(%rax),%rbx
+	mov	40(%rax),%rbp
+	mov	24(%rax),%r12
+	mov	16(%rax),%r13
+	mov	8(%rax),%r14
+	mov	0(%rax),%r15
+	lea	48(%rax),%rax
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	mod_exp_512_se_handler,.-mod_exp_512_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_mod_exp_512
+	.rva	.LSEH_end_mod_exp_512
+	.rva	.LSEH_info_mod_exp_512
+
+.section	.xdata
+.align	8
+.LSEH_info_mod_exp_512:
+	.byte	9,0,0,0
+	.rva	mod_exp_512_se_handler
+___
+}
+
+sub reg_part {
+my ($reg,$conv)=@_;
+    if ($reg =~ /%r[0-9]+/)	{ $reg .= $conv; }
+    elsif ($conv eq "b")	{ $reg =~ s/%[er]([^x]+)x?/%$1l/;	}
+    elsif ($conv eq "w")	{ $reg =~ s/%[er](.+)/%$1/;		}
+    elsif ($conv eq "d")	{ $reg =~ s/%[er](.+)/%e$1/;		}
+    return $reg;
+}
+
+$code =~ s/(%[a-z0-9]+)#([bwd])/reg_part($1,$2)/gem;
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/(\(\+[^)]+\))/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/rsaz-avx2.pl b/crypto/bn/asm/rsaz-avx2.pl
new file mode 100644
index 0000000..3eb9556
--- /dev/null
+++ b/crypto/bn/asm/rsaz-avx2.pl
@@ -0,0 +1,1875 @@
+#!/usr/bin/env perl
+
+##############################################################################
+#                                                                            #
+#  Copyright (c) 2012, Intel Corporation                                     #
+#                                                                            #
+#  All rights reserved.                                                      #
+#                                                                            #
+#  Redistribution and use in source and binary forms, with or without        #
+#  modification, are permitted provided that the following conditions are    #
+#  met:                                                                      #
+#                                                                            #
+#  *  Redistributions of source code must retain the above copyright         #
+#     notice, this list of conditions and the following disclaimer.          #
+#                                                                            #
+#  *  Redistributions in binary form must reproduce the above copyright      #
+#     notice, this list of conditions and the following disclaimer in the    #
+#     documentation and/or other materials provided with the                 #
+#     distribution.                                                          #
+#                                                                            #
+#  *  Neither the name of the Intel Corporation nor the names of its         #
+#     contributors may be used to endorse or promote products derived from   #
+#     this software without specific prior written permission.               #
+#                                                                            #
+#                                                                            #
+#  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          #
+#  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         #
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        #
+#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            #
+#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     #
+#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       #
+#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        #
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    #
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      #
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        #
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              #
+#                                                                            #
+##############################################################################
+# Developers and authors:                                                    #
+# Shay Gueron (1, 2), and Vlad Krasnov (1)                                   #
+# (1) Intel Corporation, Israel Development Center, Haifa, Israel            #
+# (2) University of Haifa, Israel                                            #
+##############################################################################
+# Reference:                                                                 #
+# [1] S. Gueron, V. Krasnov: "Software Implementation of Modular             #
+#     Exponentiation,  Using Advanced Vector Instructions Architectures",    #
+#     F. Ozbudak and F. Rodriguez-Henriquez (Eds.): WAIFI 2012, LNCS 7369,   #
+#     pp. 119?135, 2012. Springer-Verlag Berlin Heidelberg 2012              #
+# [2] S. Gueron: "Efficient Software Implementations of Modular              #
+#     Exponentiation", Journal of Cryptographic Engineering 2:31-43 (2012).  #
+# [3] S. Gueron, V. Krasnov: "Speeding up Big-numbers Squaring",IEEE         #
+#     Proceedings of 9th International Conference on Information Technology: #
+#     New Generations (ITNG 2012), pp.821-823 (2012)                         #
+# [4] S. Gueron, V. Krasnov: "[PATCH] Efficient and side channel analysis    #
+#     resistant 1024-bit modular exponentiation, for optimizing RSA2048      #
+#     on AVX2 capable x86_64 platforms",                                     #
+#     http://rt.openssl.org/Ticket/Display.html?id=2850&user=guest&pass=guest#
+##############################################################################
+#
+# +13% improvement over original submission by <appro@openssl.org>
+#
+# rsa2048 sign/sec	OpenSSL 1.0.1	scalar(*)	this
+# 2.3GHz Haswell	621		765/+23%	1113/+79%
+#
+# (*)	if system doesn't support AVX2, for reference purposes;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+open OUT,"| $^X $xlate $flavour $output";
+*STDOUT = *OUT;
+
+if ($avx>1) {{{
+{ # void AMS_WW(
+my $rp="%rdi";	# BN_ULONG *rp,
+my $ap="%rsi";	# const BN_ULONG *ap,
+my $np="%rdx";	# const BN_ULONG *np,
+my $n0="%ecx";	# const BN_ULONG n0,
+my $rep="%r8d";	# int repeat);
+
+# The registers that hold the accumulated redundant result
+# The AMM works on 1024 bit operands, and redundant word size is 29
+# Therefore: ceil(1024/29)/4 = 9
+my $ACC0="%ymm0";
+my $ACC1="%ymm1";
+my $ACC2="%ymm2";
+my $ACC3="%ymm3";
+my $ACC4="%ymm4";
+my $ACC5="%ymm5";
+my $ACC6="%ymm6";
+my $ACC7="%ymm7";
+my $ACC8="%ymm8";
+my $ACC9="%ymm9";
+# Registers that hold the broadcasted words of bp, currently used
+my $B1="%ymm10";
+my $B2="%ymm11";
+# Registers that hold the broadcasted words of Y, currently used
+my $Y1="%ymm12";
+my $Y2="%ymm13";
+# Helper registers
+my $TEMP1="%ymm14";
+my $AND_MASK="%ymm15";
+# alu registers that hold the first words of the ACC
+my $r0="%r9";
+my $r1="%r10";
+my $r2="%r11";
+my $r3="%r12";
+
+my $i="%r14d";			# loop counter
+my $tmp = "%r15";
+
+my $FrameSize=32*18+32*8;	# place for A^2 and 2*A
+
+my $aap=$r0;
+my $tp0="%rbx";
+my $tp1=$r3;
+my $tpa=$tmp;
+
+$np="%r13";			# reassigned argument
+
+$code.=<<___;
+.text
+
+.globl	rsaz_1024_sqr_avx2
+.type	rsaz_1024_sqr_avx2,\@function,5
+.align	64
+rsaz_1024_sqr_avx2:		# 702 cycles, 14% faster than rsaz_1024_mul_avx2
+	lea	(%rsp), %rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	vmovaps	%xmm6,-0xd8(%rax)
+	vmovaps	%xmm7,-0xc8(%rax)
+	vmovaps	%xmm8,-0xb8(%rax)
+	vmovaps	%xmm9,-0xa8(%rax)
+	vmovaps	%xmm10,-0x98(%rax)
+	vmovaps	%xmm11,-0x88(%rax)
+	vmovaps	%xmm12,-0x78(%rax)
+	vmovaps	%xmm13,-0x68(%rax)
+	vmovaps	%xmm14,-0x58(%rax)
+	vmovaps	%xmm15,-0x48(%rax)
+.Lsqr_1024_body:
+___
+$code.=<<___;
+	mov	%rax,%rbp
+	mov	%rdx, $np			# reassigned argument
+	sub	\$$FrameSize, %rsp
+	mov	$np, $tmp
+	sub	\$-128, $rp			# size optimization
+	sub	\$-128, $ap
+	sub	\$-128, $np
+
+	and	\$4095, $tmp			# see if $np crosses page
+	add	\$32*10, $tmp
+	shr	\$12, $tmp
+	vpxor	$ACC9,$ACC9,$ACC9
+	jz	.Lsqr_1024_no_n_copy
+
+	# unaligned 256-bit load that crosses page boundary can
+	# cause >2x performance degradation here, so if $np does
+	# cross page boundary, copy it to stack and make sure stack
+	# frame doesn't...
+	sub		\$32*10,%rsp
+	vmovdqu		32*0-128($np), $ACC0
+	and		\$-2048, %rsp
+	vmovdqu		32*1-128($np), $ACC1
+	vmovdqu		32*2-128($np), $ACC2
+	vmovdqu		32*3-128($np), $ACC3
+	vmovdqu		32*4-128($np), $ACC4
+	vmovdqu		32*5-128($np), $ACC5
+	vmovdqu		32*6-128($np), $ACC6
+	vmovdqu		32*7-128($np), $ACC7
+	vmovdqu		32*8-128($np), $ACC8
+	lea		$FrameSize+128(%rsp),$np
+	vmovdqu		$ACC0, 32*0-128($np)
+	vmovdqu		$ACC1, 32*1-128($np)
+	vmovdqu		$ACC2, 32*2-128($np)
+	vmovdqu		$ACC3, 32*3-128($np)
+	vmovdqu		$ACC4, 32*4-128($np)
+	vmovdqu		$ACC5, 32*5-128($np)
+	vmovdqu		$ACC6, 32*6-128($np)
+	vmovdqu		$ACC7, 32*7-128($np)
+	vmovdqu		$ACC8, 32*8-128($np)
+	vmovdqu		$ACC9, 32*9-128($np)	# $ACC9 is zero
+
+.Lsqr_1024_no_n_copy:
+	and		\$-1024, %rsp
+
+	vmovdqu		32*1-128($ap), $ACC1
+	vmovdqu		32*2-128($ap), $ACC2
+	vmovdqu		32*3-128($ap), $ACC3
+	vmovdqu		32*4-128($ap), $ACC4
+	vmovdqu		32*5-128($ap), $ACC5
+	vmovdqu		32*6-128($ap), $ACC6
+	vmovdqu		32*7-128($ap), $ACC7
+	vmovdqu		32*8-128($ap), $ACC8
+
+	lea	192(%rsp), $tp0			# 64+128=192
+	vpbroadcastq	.Land_mask(%rip), $AND_MASK
+	jmp	.LOOP_GRANDE_SQR_1024
+
+.align	32
+.LOOP_GRANDE_SQR_1024:
+	lea	32*18+128(%rsp), $aap		# size optimization
+	lea	448(%rsp), $tp1			# 64+128+256=448
+
+	# the squaring is performed as described in Variant B of
+	# "Speeding up Big-Number Squaring", so start by calculating
+	# the A*2=A+A vector
+	vpaddq		$ACC1, $ACC1, $ACC1
+	 vpbroadcastq	32*0-128($ap), $B1
+	vpaddq		$ACC2, $ACC2, $ACC2
+	vmovdqa		$ACC1, 32*0-128($aap)
+	vpaddq		$ACC3, $ACC3, $ACC3
+	vmovdqa		$ACC2, 32*1-128($aap)
+	vpaddq		$ACC4, $ACC4, $ACC4
+	vmovdqa		$ACC3, 32*2-128($aap)
+	vpaddq		$ACC5, $ACC5, $ACC5
+	vmovdqa		$ACC4, 32*3-128($aap)
+	vpaddq		$ACC6, $ACC6, $ACC6
+	vmovdqa		$ACC5, 32*4-128($aap)
+	vpaddq		$ACC7, $ACC7, $ACC7
+	vmovdqa		$ACC6, 32*5-128($aap)
+	vpaddq		$ACC8, $ACC8, $ACC8
+	vmovdqa		$ACC7, 32*6-128($aap)
+	vpxor		$ACC9, $ACC9, $ACC9
+	vmovdqa		$ACC8, 32*7-128($aap)
+
+	vpmuludq	32*0-128($ap), $B1, $ACC0
+	 vpbroadcastq	32*1-128($ap), $B2
+	 vmovdqu	$ACC9, 32*9-192($tp0)	# zero upper half
+	vpmuludq	$B1, $ACC1, $ACC1
+	 vmovdqu	$ACC9, 32*10-448($tp1)
+	vpmuludq	$B1, $ACC2, $ACC2
+	 vmovdqu	$ACC9, 32*11-448($tp1)
+	vpmuludq	$B1, $ACC3, $ACC3
+	 vmovdqu	$ACC9, 32*12-448($tp1)
+	vpmuludq	$B1, $ACC4, $ACC4
+	 vmovdqu	$ACC9, 32*13-448($tp1)
+	vpmuludq	$B1, $ACC5, $ACC5
+	 vmovdqu	$ACC9, 32*14-448($tp1)
+	vpmuludq	$B1, $ACC6, $ACC6
+	 vmovdqu	$ACC9, 32*15-448($tp1)
+	vpmuludq	$B1, $ACC7, $ACC7
+	 vmovdqu	$ACC9, 32*16-448($tp1)
+	vpmuludq	$B1, $ACC8, $ACC8
+	 vpbroadcastq	32*2-128($ap), $B1
+	 vmovdqu	$ACC9, 32*17-448($tp1)
+
+	mov	$ap, $tpa
+	mov 	\$4, $i
+	jmp	.Lsqr_entry_1024
+___
+$TEMP0=$Y1;
+$TEMP2=$Y2;
+$code.=<<___;
+.align	32
+.LOOP_SQR_1024:
+	 vpbroadcastq	32*1-128($tpa), $B2
+	vpmuludq	32*0-128($ap), $B1, $ACC0
+	vpaddq		32*0-192($tp0), $ACC0, $ACC0
+	vpmuludq	32*0-128($aap), $B1, $ACC1
+	vpaddq		32*1-192($tp0), $ACC1, $ACC1
+	vpmuludq	32*1-128($aap), $B1, $ACC2
+	vpaddq		32*2-192($tp0), $ACC2, $ACC2
+	vpmuludq	32*2-128($aap), $B1, $ACC3
+	vpaddq		32*3-192($tp0), $ACC3, $ACC3
+	vpmuludq	32*3-128($aap), $B1, $ACC4
+	vpaddq		32*4-192($tp0), $ACC4, $ACC4
+	vpmuludq	32*4-128($aap), $B1, $ACC5
+	vpaddq		32*5-192($tp0), $ACC5, $ACC5
+	vpmuludq	32*5-128($aap), $B1, $ACC6
+	vpaddq		32*6-192($tp0), $ACC6, $ACC6
+	vpmuludq	32*6-128($aap), $B1, $ACC7
+	vpaddq		32*7-192($tp0), $ACC7, $ACC7
+	vpmuludq	32*7-128($aap), $B1, $ACC8
+	 vpbroadcastq	32*2-128($tpa), $B1
+	vpaddq		32*8-192($tp0), $ACC8, $ACC8
+.Lsqr_entry_1024:
+	vmovdqu		$ACC0, 32*0-192($tp0)
+	vmovdqu		$ACC1, 32*1-192($tp0)
+
+	vpmuludq	32*1-128($ap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	vpmuludq	32*1-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC3, $ACC3
+	vpmuludq	32*2-128($aap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC4, $ACC4
+	vpmuludq	32*3-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	32*4-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC6, $ACC6
+	vpmuludq	32*5-128($aap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC7, $ACC7
+	vpmuludq	32*6-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	32*7-128($aap), $B2, $ACC0
+	 vpbroadcastq	32*3-128($tpa), $B2
+	vpaddq		32*9-192($tp0), $ACC0, $ACC0
+
+	vmovdqu		$ACC2, 32*2-192($tp0)
+	vmovdqu		$ACC3, 32*3-192($tp0)
+
+	vpmuludq	32*2-128($ap), $B1, $TEMP2
+	vpaddq		$TEMP2, $ACC4, $ACC4
+	vpmuludq	32*2-128($aap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	32*3-128($aap), $B1, $TEMP1
+	vpaddq		$TEMP1, $ACC6, $ACC6
+	vpmuludq	32*4-128($aap), $B1, $TEMP2
+	vpaddq		$TEMP2, $ACC7, $ACC7
+	vpmuludq	32*5-128($aap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	32*6-128($aap), $B1, $TEMP1
+	vpaddq		$TEMP1, $ACC0, $ACC0
+	vpmuludq	32*7-128($aap), $B1, $ACC1
+	 vpbroadcastq	32*4-128($tpa), $B1
+	vpaddq		32*10-448($tp1), $ACC1, $ACC1
+
+	vmovdqu		$ACC4, 32*4-192($tp0)
+	vmovdqu		$ACC5, 32*5-192($tp0)
+
+	vpmuludq	32*3-128($ap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC6, $ACC6
+	vpmuludq	32*3-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC7, $ACC7
+	vpmuludq	32*4-128($aap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC8, $ACC8
+	vpmuludq	32*5-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpmuludq	32*6-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpmuludq	32*7-128($aap), $B2, $ACC2
+	 vpbroadcastq	32*5-128($tpa), $B2
+	vpaddq		32*11-448($tp1), $ACC2, $ACC2	
+
+	vmovdqu		$ACC6, 32*6-192($tp0)
+	vmovdqu		$ACC7, 32*7-192($tp0)
+
+	vpmuludq	32*4-128($ap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	32*4-128($aap), $B1, $TEMP1
+	vpaddq		$TEMP1, $ACC0, $ACC0
+	vpmuludq	32*5-128($aap), $B1, $TEMP2
+	vpaddq		$TEMP2, $ACC1, $ACC1
+	vpmuludq	32*6-128($aap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	vpmuludq	32*7-128($aap), $B1, $ACC3
+	 vpbroadcastq	32*6-128($tpa), $B1
+	vpaddq		32*12-448($tp1), $ACC3, $ACC3
+
+	vmovdqu		$ACC8, 32*8-192($tp0)
+	vmovdqu		$ACC0, 32*9-192($tp0)
+	lea		8($tp0), $tp0
+
+	vpmuludq	32*5-128($ap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC1, $ACC1
+	vpmuludq	32*5-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	vpmuludq	32*6-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC3, $ACC3
+	vpmuludq	32*7-128($aap), $B2, $ACC4
+	 vpbroadcastq	32*7-128($tpa), $B2
+	vpaddq		32*13-448($tp1), $ACC4, $ACC4
+
+	vmovdqu		$ACC1, 32*10-448($tp1)
+	vmovdqu		$ACC2, 32*11-448($tp1)
+
+	vpmuludq	32*6-128($ap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC3, $ACC3
+	vpmuludq	32*6-128($aap), $B1, $TEMP1
+	 vpbroadcastq	32*8-128($tpa), $ACC0		# borrow $ACC0 for $B1
+	vpaddq		$TEMP1, $ACC4, $ACC4
+	vpmuludq	32*7-128($aap), $B1, $ACC5
+	 vpbroadcastq	32*0+8-128($tpa), $B1		# for next iteration
+	vpaddq		32*14-448($tp1), $ACC5, $ACC5
+
+	vmovdqu		$ACC3, 32*12-448($tp1)
+	vmovdqu		$ACC4, 32*13-448($tp1)
+	lea		8($tpa), $tpa
+
+	vpmuludq	32*7-128($ap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	32*7-128($aap), $B2, $ACC6
+	vpaddq		32*15-448($tp1), $ACC6, $ACC6
+
+	vpmuludq	32*8-128($ap), $ACC0, $ACC7
+	vmovdqu		$ACC5, 32*14-448($tp1)
+	vpaddq		32*16-448($tp1), $ACC7, $ACC7
+	vmovdqu		$ACC6, 32*15-448($tp1)
+	vmovdqu		$ACC7, 32*16-448($tp1)
+	lea		8($tp1), $tp1
+
+	dec	$i        
+	jnz	.LOOP_SQR_1024
+___
+$ZERO = $ACC9;
+$TEMP0 = $B1;
+$TEMP2 = $B2;
+$TEMP3 = $Y1;
+$TEMP4 = $Y2;
+$code.=<<___;
+	#we need to fix indexes 32-39 to avoid overflow
+	vmovdqu		32*8(%rsp), $ACC8		# 32*8-192($tp0),
+	vmovdqu		32*9(%rsp), $ACC1		# 32*9-192($tp0)
+	vmovdqu		32*10(%rsp), $ACC2		# 32*10-192($tp0)
+	lea		192(%rsp), $tp0			# 64+128=192
+
+	vpsrlq		\$29, $ACC8, $TEMP1
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpxor		$ZERO, $ZERO, $ZERO
+	vpermq		\$0x93, $TEMP2, $TEMP2
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpblendd	\$3, $TEMP2, $ZERO, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vmovdqu		$ACC1, 32*9-192($tp0)
+	vmovdqu		$ACC2, 32*10-192($tp0)
+
+	mov	(%rsp), %rax
+	mov	8(%rsp), $r1
+	mov	16(%rsp), $r2
+	mov	24(%rsp), $r3
+	vmovdqu	32*1(%rsp), $ACC1
+	vmovdqu	32*2-192($tp0), $ACC2
+	vmovdqu	32*3-192($tp0), $ACC3
+	vmovdqu	32*4-192($tp0), $ACC4
+	vmovdqu	32*5-192($tp0), $ACC5
+	vmovdqu	32*6-192($tp0), $ACC6
+	vmovdqu	32*7-192($tp0), $ACC7
+
+	mov	%rax, $r0
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+	vmovd	%eax, $Y1
+
+	mov	%rax, %rdx
+	imulq	-128($np), %rax
+	 vpbroadcastq	$Y1, $Y1
+	add	%rax, $r0
+	mov	%rdx, %rax
+	imulq	8-128($np), %rax
+	shr	\$29, $r0
+	add	%rax, $r1
+	mov	%rdx, %rax
+	imulq	16-128($np), %rax
+	add	$r0, $r1
+	add	%rax, $r2
+	imulq	24-128($np), %rdx
+	add	%rdx, $r3
+
+	mov	$r1, %rax
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	mov \$9, $i
+	jmp .LOOP_REDUCE_1024
+
+.align	32
+.LOOP_REDUCE_1024:
+	vmovd	%eax, $Y2
+	vpbroadcastq	$Y2, $Y2
+
+	vpmuludq	32*1-128($np), $Y1, $TEMP0
+	 mov	%rax, %rdx
+	 imulq	-128($np), %rax
+	vpaddq		$TEMP0, $ACC1, $ACC1
+	 add	%rax, $r1
+	vpmuludq	32*2-128($np), $Y1, $TEMP1
+	 mov	%rdx, %rax
+	 imulq	8-128($np), %rax
+	vpaddq		$TEMP1, $ACC2, $ACC2
+	vpmuludq	32*3-128($np), $Y1, $TEMP2
+	 .byte	0x67
+	 add	%rax, $r2
+	 .byte	0x67
+	 mov	%rdx, %rax
+	 imulq	16-128($np), %rax
+	 shr	\$29, $r1
+	vpaddq		$TEMP2, $ACC3, $ACC3
+	vpmuludq	32*4-128($np), $Y1, $TEMP0
+	 add	%rax, $r3
+	 add	$r1, $r2
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpmuludq	32*5-128($np), $Y1, $TEMP1
+	 mov	$r2, %rax
+	 imull	$n0, %eax
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpmuludq	32*6-128($np), $Y1, $TEMP2
+	 and	\$0x1fffffff, %eax
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpmuludq	32*7-128($np), $Y1, $TEMP0
+	vpaddq		$TEMP0, $ACC7, $ACC7
+	vpmuludq	32*8-128($np), $Y1, $TEMP1
+	 vmovd	%eax, $Y1
+	 #vmovdqu	32*1-8-128($np), $TEMP2		# moved below
+	vpaddq		$TEMP1, $ACC8, $ACC8
+	 #vmovdqu	32*2-8-128($np), $TEMP0		# moved below
+	 vpbroadcastq	$Y1, $Y1
+
+	vpmuludq	32*1-8-128($np), $Y2, $TEMP2	# see above
+	vmovdqu		32*3-8-128($np), $TEMP1
+	 mov	%rax, %rdx
+	 imulq	-128($np), %rax
+	vpaddq		$TEMP2, $ACC1, $ACC1
+	vpmuludq	32*2-8-128($np), $Y2, $TEMP0	# see above
+	vmovdqu		32*4-8-128($np), $TEMP2
+	 add	%rax, $r2
+	 mov	%rdx, %rax
+	 imulq	8-128($np), %rax
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	 add	$r3, %rax
+	 shr	\$29, $r2
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	vmovdqu		32*5-8-128($np), $TEMP0
+	 add	$r2, %rax
+	vpaddq		$TEMP1, $ACC3, $ACC3
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*6-8-128($np), $TEMP1
+	 .byte	0x67
+	 mov	%rax, $r3
+	 imull	$n0, %eax
+	vpaddq		$TEMP2, $ACC4, $ACC4
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	.byte	0xc4,0x41,0x7e,0x6f,0x9d,0x58,0x00,0x00,0x00	# vmovdqu		32*7-8-128($np), $TEMP2
+	 and	\$0x1fffffff, %eax
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	vmovdqu		32*8-8-128($np), $TEMP0
+	vpaddq		$TEMP1, $ACC6, $ACC6
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*9-8-128($np), $ACC9
+	 vmovd	%eax, $ACC0			# borrow ACC0 for Y2
+	 imulq	-128($np), %rax
+	vpaddq		$TEMP2, $ACC7, $ACC7
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	 vmovdqu	32*1-16-128($np), $TEMP1
+	 vpbroadcastq	$ACC0, $ACC0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	$Y2, $ACC9, $ACC9
+	 vmovdqu	32*2-16-128($np), $TEMP2
+	 add	%rax, $r3
+
+___
+($ACC0,$Y2)=($Y2,$ACC0);
+$code.=<<___;
+	 vmovdqu	32*1-24-128($np), $ACC0
+	vpmuludq	$Y1, $TEMP1, $TEMP1
+	vmovdqu		32*3-16-128($np), $TEMP0
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	 vpmuludq	$Y2, $ACC0, $ACC0
+	vpmuludq	$Y1, $TEMP2, $TEMP2
+	.byte	0xc4,0x41,0x7e,0x6f,0xb5,0xf0,0xff,0xff,0xff	# vmovdqu		32*4-16-128($np), $TEMP1
+	 vpaddq		$ACC1, $ACC0, $ACC0
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpmuludq	$Y1, $TEMP0, $TEMP0
+	vmovdqu		32*5-16-128($np), $TEMP2
+	 .byte	0x67
+	 vmovq		$ACC0, %rax
+	 vmovdqu	$ACC0, (%rsp)		# transfer $r0-$r3
+	vpaddq		$TEMP0, $ACC3, $ACC3
+	vpmuludq	$Y1, $TEMP1, $TEMP1
+	vmovdqu		32*6-16-128($np), $TEMP0
+	vpaddq		$TEMP1, $ACC4, $ACC4
+	vpmuludq	$Y1, $TEMP2, $TEMP2
+	vmovdqu		32*7-16-128($np), $TEMP1
+	vpaddq		$TEMP2, $ACC5, $ACC5
+	vpmuludq	$Y1, $TEMP0, $TEMP0
+	vmovdqu		32*8-16-128($np), $TEMP2
+	vpaddq		$TEMP0, $ACC6, $ACC6
+	vpmuludq	$Y1, $TEMP1, $TEMP1
+	 shr	\$29, $r3
+	vmovdqu		32*9-16-128($np), $TEMP0
+	 add	$r3, %rax
+	vpaddq		$TEMP1, $ACC7, $ACC7
+	vpmuludq	$Y1, $TEMP2, $TEMP2
+	 #vmovdqu	32*2-24-128($np), $TEMP1	# moved below
+	 mov	%rax, $r0
+	 imull	$n0, %eax
+	vpaddq		$TEMP2, $ACC8, $ACC8
+	vpmuludq	$Y1, $TEMP0, $TEMP0
+	 and	\$0x1fffffff, %eax
+	 vmovd	%eax, $Y1
+	 vmovdqu	32*3-24-128($np), $TEMP2
+	.byte	0x67
+	vpaddq		$TEMP0, $ACC9, $ACC9
+	 vpbroadcastq	$Y1, $Y1
+
+	vpmuludq	32*2-24-128($np), $Y2, $TEMP1	# see above
+	vmovdqu		32*4-24-128($np), $TEMP0
+	 mov	%rax, %rdx
+	 imulq	-128($np), %rax
+	 mov	8(%rsp), $r1
+	vpaddq		$TEMP1, $ACC2, $ACC1
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*5-24-128($np), $TEMP1
+	 add	%rax, $r0
+	 mov	%rdx, %rax
+	 imulq	8-128($np), %rax
+	 .byte	0x67
+	 shr	\$29, $r0
+	 mov	16(%rsp), $r2
+	vpaddq		$TEMP2, $ACC3, $ACC2
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	vmovdqu		32*6-24-128($np), $TEMP2
+	 add	%rax, $r1
+	 mov	%rdx, %rax
+	 imulq	16-128($np), %rax
+	vpaddq		$TEMP0, $ACC4, $ACC3
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	vmovdqu		32*7-24-128($np), $TEMP0
+	 imulq	24-128($np), %rdx		# future $r3
+	 add	%rax, $r2
+	 lea	($r0,$r1), %rax
+	vpaddq		$TEMP1, $ACC5, $ACC4
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*8-24-128($np), $TEMP1
+	 mov	%rax, $r1
+	 imull	$n0, %eax
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	vpaddq		$TEMP2, $ACC6, $ACC5
+	vmovdqu		32*9-24-128($np), $TEMP2
+	 and	\$0x1fffffff, %eax
+	vpaddq		$TEMP0, $ACC7, $ACC6
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	 add	24(%rsp), %rdx
+	vpaddq		$TEMP1, $ACC8, $ACC7
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vpaddq		$TEMP2, $ACC9, $ACC8
+	 vmovq	$r3, $ACC9
+	 mov	%rdx, $r3
+
+	dec	$i
+	jnz	.LOOP_REDUCE_1024
+___
+($ACC0,$Y2)=($Y2,$ACC0);
+$code.=<<___;
+	lea	448(%rsp), $tp1			# size optimization
+	vpaddq	$ACC9, $Y2, $ACC0
+	vpxor	$ZERO, $ZERO, $ZERO
+
+	vpaddq		32*9-192($tp0), $ACC0, $ACC0
+	vpaddq		32*10-448($tp1), $ACC1, $ACC1
+	vpaddq		32*11-448($tp1), $ACC2, $ACC2
+	vpaddq		32*12-448($tp1), $ACC3, $ACC3
+	vpaddq		32*13-448($tp1), $ACC4, $ACC4
+	vpaddq		32*14-448($tp1), $ACC5, $ACC5
+	vpaddq		32*15-448($tp1), $ACC6, $ACC6
+	vpaddq		32*16-448($tp1), $ACC7, $ACC7
+	vpaddq		32*17-448($tp1), $ACC8, $ACC8
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpermq		\$0x93, $TEMP3, $TEMP3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vpaddq		$TEMP4, $ACC4, $ACC4
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpermq		\$0x93, $TEMP3, $TEMP3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vmovdqu		$ACC0, 32*0-128($rp)
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vmovdqu		$ACC1, 32*1-128($rp)
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vmovdqu		$ACC2, 32*2-128($rp)
+	vpaddq		$TEMP4, $ACC4, $ACC4
+	vmovdqu		$ACC3, 32*3-128($rp)
+___
+$TEMP5=$ACC0;
+$code.=<<___;
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vpaddq		$TEMP4, $ACC8, $ACC8
+     
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vmovdqu		$ACC4, 32*4-128($rp)
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vmovdqu		$ACC5, 32*5-128($rp)
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vmovdqu		$ACC6, 32*6-128($rp)
+	vpaddq		$TEMP4, $ACC8, $ACC8
+	vmovdqu		$ACC7, 32*7-128($rp)
+	vmovdqu		$ACC8, 32*8-128($rp)
+
+	mov	$rp, $ap
+	dec	$rep
+	jne	.LOOP_GRANDE_SQR_1024
+
+	vzeroall
+	mov	%rbp, %rax
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lsqr_1024_epilogue:
+	ret
+.size	rsaz_1024_sqr_avx2,.-rsaz_1024_sqr_avx2
+___
+}
+
+{ # void AMM_WW(
+my $rp="%rdi";	# BN_ULONG *rp,
+my $ap="%rsi";	# const BN_ULONG *ap,
+my $bp="%rdx";	# const BN_ULONG *bp,
+my $np="%rcx";	# const BN_ULONG *np,
+my $n0="%r8d";	# unsigned int n0);
+
+# The registers that hold the accumulated redundant result
+# The AMM works on 1024 bit operands, and redundant word size is 29
+# Therefore: ceil(1024/29)/4 = 9
+my $ACC0="%ymm0";
+my $ACC1="%ymm1";
+my $ACC2="%ymm2";
+my $ACC3="%ymm3";
+my $ACC4="%ymm4";
+my $ACC5="%ymm5";
+my $ACC6="%ymm6";
+my $ACC7="%ymm7";
+my $ACC8="%ymm8";
+my $ACC9="%ymm9";
+
+# Registers that hold the broadcasted words of multiplier, currently used
+my $Bi="%ymm10";
+my $Yi="%ymm11";
+
+# Helper registers
+my $TEMP0=$ACC0;
+my $TEMP1="%ymm12";
+my $TEMP2="%ymm13";
+my $ZERO="%ymm14";
+my $AND_MASK="%ymm15";
+
+# alu registers that hold the first words of the ACC
+my $r0="%r9";
+my $r1="%r10";
+my $r2="%r11";
+my $r3="%r12";
+
+my $i="%r14d";
+my $tmp="%r15";
+
+$bp="%r13";	# reassigned argument
+
+$code.=<<___;
+.globl	rsaz_1024_mul_avx2
+.type	rsaz_1024_mul_avx2,\@function,5
+.align	64
+rsaz_1024_mul_avx2:
+	lea	(%rsp), %rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	vzeroupper
+	lea	-0xa8(%rsp),%rsp
+	vmovaps	%xmm6,-0xd8(%rax)
+	vmovaps	%xmm7,-0xc8(%rax)
+	vmovaps	%xmm8,-0xb8(%rax)
+	vmovaps	%xmm9,-0xa8(%rax)
+	vmovaps	%xmm10,-0x98(%rax)
+	vmovaps	%xmm11,-0x88(%rax)
+	vmovaps	%xmm12,-0x78(%rax)
+	vmovaps	%xmm13,-0x68(%rax)
+	vmovaps	%xmm14,-0x58(%rax)
+	vmovaps	%xmm15,-0x48(%rax)
+.Lmul_1024_body:
+___
+$code.=<<___;
+	mov	%rax,%rbp
+	vzeroall
+	mov	%rdx, $bp	# reassigned argument
+	sub	\$64,%rsp
+
+	# unaligned 256-bit load that crosses page boundary can
+	# cause severe performance degradation here, so if $ap does
+	# cross page boundary, swap it with $bp [meaning that caller
+	# is advised to lay down $ap and $bp next to each other, so
+	# that only one can cross page boundary].
+	.byte	0x67,0x67
+	mov	$ap, $tmp
+	and	\$4095, $tmp
+	add	\$32*10, $tmp
+	shr	\$12, $tmp
+	mov	$ap, $tmp
+	cmovnz	$bp, $ap
+	cmovnz	$tmp, $bp
+
+	mov	$np, $tmp
+	sub	\$-128,$ap	# size optimization
+	sub	\$-128,$np
+	sub	\$-128,$rp
+
+	and	\$4095, $tmp	# see if $np crosses page
+	add	\$32*10, $tmp
+	.byte	0x67,0x67
+	shr	\$12, $tmp
+	jz	.Lmul_1024_no_n_copy
+
+	# unaligned 256-bit load that crosses page boundary can
+	# cause severe performance degradation here, so if $np does
+	# cross page boundary, copy it to stack and make sure stack
+	# frame doesn't...
+	sub		\$32*10,%rsp
+	vmovdqu		32*0-128($np), $ACC0
+	and		\$-512, %rsp
+	vmovdqu		32*1-128($np), $ACC1
+	vmovdqu		32*2-128($np), $ACC2
+	vmovdqu		32*3-128($np), $ACC3
+	vmovdqu		32*4-128($np), $ACC4
+	vmovdqu		32*5-128($np), $ACC5
+	vmovdqu		32*6-128($np), $ACC6
+	vmovdqu		32*7-128($np), $ACC7
+	vmovdqu		32*8-128($np), $ACC8
+	lea		64+128(%rsp),$np
+	vmovdqu		$ACC0, 32*0-128($np)
+	vpxor		$ACC0, $ACC0, $ACC0
+	vmovdqu		$ACC1, 32*1-128($np)
+	vpxor		$ACC1, $ACC1, $ACC1
+	vmovdqu		$ACC2, 32*2-128($np)
+	vpxor		$ACC2, $ACC2, $ACC2
+	vmovdqu		$ACC3, 32*3-128($np)
+	vpxor		$ACC3, $ACC3, $ACC3
+	vmovdqu		$ACC4, 32*4-128($np)
+	vpxor		$ACC4, $ACC4, $ACC4
+	vmovdqu		$ACC5, 32*5-128($np)
+	vpxor		$ACC5, $ACC5, $ACC5
+	vmovdqu		$ACC6, 32*6-128($np)
+	vpxor		$ACC6, $ACC6, $ACC6
+	vmovdqu		$ACC7, 32*7-128($np)
+	vpxor		$ACC7, $ACC7, $ACC7
+	vmovdqu		$ACC8, 32*8-128($np)
+	vmovdqa		$ACC0, $ACC8
+	vmovdqu		$ACC9, 32*9-128($np)	# $ACC9 is zero after vzeroall
+.Lmul_1024_no_n_copy:
+	and	\$-64,%rsp
+
+	mov	($bp), %rbx
+	vpbroadcastq ($bp), $Bi
+	vmovdqu	$ACC0, (%rsp)			# clear top of stack
+	xor	$r0, $r0
+	.byte	0x67
+	xor	$r1, $r1
+	xor	$r2, $r2
+	xor	$r3, $r3
+
+	vmovdqu	.Land_mask(%rip), $AND_MASK
+	mov	\$9, $i
+	jmp	.Loop_mul_1024
+
+.align	32
+.Loop_mul_1024:
+	 vpsrlq		\$29, $ACC3, $ACC9		# correct $ACC3(*)
+	mov	%rbx, %rax
+	imulq	-128($ap), %rax
+	add	$r0, %rax
+	mov	%rbx, $r1
+	imulq	8-128($ap), $r1
+	add	8(%rsp), $r1
+
+	mov	%rax, $r0
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	 mov	%rbx, $r2
+	 imulq	16-128($ap), $r2
+	 add	16(%rsp), $r2
+
+	 mov	%rbx, $r3
+	 imulq	24-128($ap), $r3
+	 add	24(%rsp), $r3
+	vpmuludq	32*1-128($ap),$Bi,$TEMP0
+	 vmovd		%eax, $Yi
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	32*2-128($ap),$Bi,$TEMP1
+	 vpbroadcastq	$Yi, $Yi
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	32*3-128($ap),$Bi,$TEMP2
+	 vpand		$AND_MASK, $ACC3, $ACC3		# correct $ACC3
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	32*4-128($ap),$Bi,$TEMP0
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	32*5-128($ap),$Bi,$TEMP1
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	32*6-128($ap),$Bi,$TEMP2
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	32*7-128($ap),$Bi,$TEMP0
+	 vpermq		\$0x93, $ACC9, $ACC9		# correct $ACC3
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	32*8-128($ap),$Bi,$TEMP1
+	 vpbroadcastq	8($bp), $Bi
+	vpaddq		$TEMP1,$ACC8,$ACC8
+
+	mov	%rax,%rdx
+	imulq	-128($np),%rax
+	add	%rax,$r0
+	mov	%rdx,%rax
+	imulq	8-128($np),%rax
+	add	%rax,$r1
+	mov	%rdx,%rax
+	imulq	16-128($np),%rax
+	add	%rax,$r2
+	shr	\$29, $r0
+	imulq	24-128($np),%rdx
+	add	%rdx,$r3
+	add	$r0, $r1
+
+	vpmuludq	32*1-128($np),$Yi,$TEMP2
+	 vmovq		$Bi, %rbx
+	vpaddq		$TEMP2,$ACC1,$ACC1
+	vpmuludq	32*2-128($np),$Yi,$TEMP0
+	vpaddq		$TEMP0,$ACC2,$ACC2
+	vpmuludq	32*3-128($np),$Yi,$TEMP1
+	vpaddq		$TEMP1,$ACC3,$ACC3
+	vpmuludq	32*4-128($np),$Yi,$TEMP2
+	vpaddq		$TEMP2,$ACC4,$ACC4
+	vpmuludq	32*5-128($np),$Yi,$TEMP0
+	vpaddq		$TEMP0,$ACC5,$ACC5
+	vpmuludq	32*6-128($np),$Yi,$TEMP1
+	vpaddq		$TEMP1,$ACC6,$ACC6
+	vpmuludq	32*7-128($np),$Yi,$TEMP2
+	 vpblendd	\$3, $ZERO, $ACC9, $ACC9	# correct $ACC3
+	vpaddq		$TEMP2,$ACC7,$ACC7
+	vpmuludq	32*8-128($np),$Yi,$TEMP0
+	 vpaddq		$ACC9, $ACC3, $ACC3		# correct $ACC3
+	vpaddq		$TEMP0,$ACC8,$ACC8
+
+	mov	%rbx, %rax
+	imulq	-128($ap),%rax
+	add	%rax,$r1
+	 vmovdqu	-8+32*1-128($ap),$TEMP1
+	mov	%rbx, %rax
+	imulq	8-128($ap),%rax
+	add	%rax,$r2
+	 vmovdqu	-8+32*2-128($ap),$TEMP2
+
+	mov	$r1, %rax
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	 imulq	16-128($ap),%rbx
+	 add	%rbx,$r3
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	 vmovd		%eax, $Yi
+	vmovdqu		-8+32*3-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC1,$ACC1
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	 vpbroadcastq	$Yi, $Yi
+	vmovdqu		-8+32*4-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC2,$ACC2
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*5-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC3,$ACC3
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*6-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC4,$ACC4
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-8+32*7-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC5,$ACC5
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*8-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC6,$ACC6
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*9-128($ap),$ACC9
+	vpaddq		$TEMP1,$ACC7,$ACC7
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vpaddq		$TEMP2,$ACC8,$ACC8
+	vpmuludq	$Bi,$ACC9,$ACC9
+	 vpbroadcastq	16($bp), $Bi
+
+	mov	%rax,%rdx
+	imulq	-128($np),%rax
+	add	%rax,$r1
+	 vmovdqu	-8+32*1-128($np),$TEMP0
+	mov	%rdx,%rax
+	imulq	8-128($np),%rax
+	add	%rax,$r2
+	 vmovdqu	-8+32*2-128($np),$TEMP1
+	shr	\$29, $r1
+	imulq	16-128($np),%rdx
+	add	%rdx,$r3
+	add	$r1, $r2
+
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	 vmovq		$Bi, %rbx
+	vmovdqu		-8+32*3-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*4-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-8+32*5-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*6-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*7-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-8+32*8-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*9-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vpaddq		$TEMP2,$ACC9,$ACC9
+
+	 vmovdqu	-16+32*1-128($ap),$TEMP0
+	mov	%rbx,%rax
+	imulq	-128($ap),%rax
+	add	$r2,%rax
+
+	 vmovdqu	-16+32*2-128($ap),$TEMP1
+	mov	%rax,$r2
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	 imulq	8-128($ap),%rbx
+	 add	%rbx,$r3
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	 vmovd		%eax, $Yi
+	vmovdqu		-16+32*3-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	 vpbroadcastq	$Yi, $Yi
+	vmovdqu		-16+32*4-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*5-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*6-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-16+32*7-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*8-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*9-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	 vpbroadcastq	24($bp), $Bi
+	vpaddq		$TEMP2,$ACC9,$ACC9
+
+	 vmovdqu	-16+32*1-128($np),$TEMP0
+	mov	%rax,%rdx
+	imulq	-128($np),%rax
+	add	%rax,$r2
+	 vmovdqu	-16+32*2-128($np),$TEMP1
+	imulq	8-128($np),%rdx
+	add	%rdx,$r3
+	shr	\$29, $r2
+
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	 vmovq		$Bi, %rbx
+	vmovdqu		-16+32*3-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-16+32*4-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*5-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*6-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-16+32*7-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*8-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*9-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	 vmovdqu	-24+32*1-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	 vmovdqu	-24+32*2-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC9,$ACC9
+
+	add	$r2, $r3
+	imulq	-128($ap),%rbx
+	add	%rbx,$r3
+
+	mov	$r3, %rax
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	 vmovd		%eax, $Yi
+	vmovdqu		-24+32*3-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	 vpbroadcastq	$Yi, $Yi
+	vmovdqu		-24+32*4-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*5-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*6-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-24+32*7-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*8-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*9-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	 vpbroadcastq	32($bp), $Bi
+	vpaddq		$TEMP2,$ACC9,$ACC9
+	 add		\$32, $bp			# $bp++
+
+	vmovdqu		-24+32*1-128($np),$TEMP0
+	imulq	-128($np),%rax
+	add	%rax,$r3
+	shr	\$29, $r3
+
+	vmovdqu		-24+32*2-128($np),$TEMP1
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	 vmovq		$Bi, %rbx
+	vmovdqu		-24+32*3-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC0		# $ACC0==$TEMP0
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	 vmovdqu	$ACC0, (%rsp)			# transfer $r0-$r3
+	vpaddq		$TEMP1,$ACC2,$ACC1
+	vmovdqu		-24+32*4-128($np),$TEMP0
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*5-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC2
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*6-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC3
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-24+32*7-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC4
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*8-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC5
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*9-128($np),$TEMP2
+	 mov	$r3, $r0
+	vpaddq		$TEMP0,$ACC7,$ACC6
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	 add	(%rsp), $r0
+	vpaddq		$TEMP1,$ACC8,$ACC7
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	 vmovq	$r3, $TEMP1
+	vpaddq		$TEMP2,$ACC9,$ACC8
+
+	dec	$i
+	jnz	.Loop_mul_1024
+___
+
+# (*)	Original implementation was correcting ACC1-ACC3 for overflow
+#	after 7 loop runs, or after 28 iterations, or 56 additions.
+#	But as we underutilize resources, it's possible to correct in
+#	each iteration with marginal performance loss. But then, as
+#	we do it in each iteration, we can correct less digits, and
+#	avoid performance penalties completely. Also note that we
+#	correct only three digits out of four. This works because
+#	most significant digit is subjected to less additions.
+
+$TEMP0 = $ACC9;
+$TEMP3 = $Bi;
+$TEMP4 = $Yi;
+$code.=<<___;
+	vpermq		\$0, $AND_MASK, $AND_MASK
+	vpaddq		(%rsp), $TEMP1, $ACC0
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vpaddq		$TEMP4, $ACC4, $ACC4
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpermq		\$0x93, $TEMP3, $TEMP3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vpaddq		$TEMP4, $ACC4, $ACC4
+
+	vmovdqu		$ACC0, 0-128($rp)
+	vmovdqu		$ACC1, 32-128($rp)
+	vmovdqu		$ACC2, 64-128($rp)
+	vmovdqu		$ACC3, 96-128($rp)
+___
+
+$TEMP5=$ACC0;
+$code.=<<___;
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vpaddq		$TEMP4, $ACC8, $ACC8
+
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vpaddq		$TEMP4, $ACC8, $ACC8
+
+	vmovdqu		$ACC4, 128-128($rp)
+	vmovdqu		$ACC5, 160-128($rp)    
+	vmovdqu		$ACC6, 192-128($rp)
+	vmovdqu		$ACC7, 224-128($rp)
+	vmovdqu		$ACC8, 256-128($rp)
+	vzeroupper
+
+	mov	%rbp, %rax
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lmul_1024_epilogue:
+	ret
+.size	rsaz_1024_mul_avx2,.-rsaz_1024_mul_avx2
+___
+}
+{
+my ($out,$inp) = $win64 ? ("%rcx","%rdx") : ("%rdi","%rsi");
+my @T = map("%r$_",(8..11));
+
+$code.=<<___;
+.globl	rsaz_1024_red2norm_avx2
+.type	rsaz_1024_red2norm_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_red2norm_avx2:
+	sub	\$-128,$inp	# size optimization
+	xor	%rax,%rax
+___
+
+for ($j=0,$i=0; $i<16; $i++) {
+    my $k=0;
+    while (29*$j<64*($i+1)) {	# load data till boundary
+	$code.="	mov	`8*$j-128`($inp), @T[0]\n";
+	$j++; $k++; push(@T,shift(@T));
+    }
+    $l=$k;
+    while ($k>1) {		# shift loaded data but last value
+	$code.="	shl	\$`29*($j-$k)`,@T[-$k]\n";
+	$k--;
+    }
+    $code.=<<___;		# shift last value
+	mov	@T[-1], @T[0]
+	shl	\$`29*($j-1)`, @T[-1]
+	shr	\$`-29*($j-1)`, @T[0]
+___
+    while ($l) {		# accumulate all values
+	$code.="	add	@T[-$l], %rax\n";
+	$l--;
+    }
+	$code.=<<___;
+	adc	\$0, @T[0]	# consume eventual carry
+	mov	%rax, 8*$i($out)
+	mov	@T[0], %rax
+___
+    push(@T,shift(@T));
+}
+$code.=<<___;
+	ret
+.size	rsaz_1024_red2norm_avx2,.-rsaz_1024_red2norm_avx2
+
+.globl	rsaz_1024_norm2red_avx2
+.type	rsaz_1024_norm2red_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_norm2red_avx2:
+	sub	\$-128,$out	# size optimization
+	mov	($inp),@T[0]
+	mov	\$0x1fffffff,%eax
+___
+for ($j=0,$i=0; $i<16; $i++) {
+    $code.="	mov	`8*($i+1)`($inp),@T[1]\n"	if ($i<15);
+    $code.="	xor	@T[1],@T[1]\n"			if ($i==15);
+    my $k=1;
+    while (29*($j+1)<64*($i+1)) {
+    	$code.=<<___;
+	mov	@T[0],@T[-$k]
+	shr	\$`29*$j`,@T[-$k]
+	and	%rax,@T[-$k]				# &0x1fffffff
+	mov	@T[-$k],`8*$j-128`($out)
+___
+	$j++; $k++;
+    }
+    $code.=<<___;
+	shrd	\$`29*$j`,@T[1],@T[0]
+	and	%rax,@T[0]
+	mov	@T[0],`8*$j-128`($out)
+___
+    $j++;
+    push(@T,shift(@T));
+}
+$code.=<<___;
+	mov	@T[0],`8*$j-128`($out)			# zero
+	mov	@T[0],`8*($j+1)-128`($out)
+	mov	@T[0],`8*($j+2)-128`($out)
+	mov	@T[0],`8*($j+3)-128`($out)
+	ret
+.size	rsaz_1024_norm2red_avx2,.-rsaz_1024_norm2red_avx2
+___
+}
+{
+my ($out,$inp,$power) = $win64 ? ("%rcx","%rdx","%r8d") : ("%rdi","%rsi","%edx");
+
+$code.=<<___;
+.globl	rsaz_1024_scatter5_avx2
+.type	rsaz_1024_scatter5_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_scatter5_avx2:
+	vzeroupper
+	vmovdqu	.Lscatter_permd(%rip),%ymm5
+	shl	\$4,$power
+	lea	($out,$power),$out
+	mov	\$9,%eax
+	jmp	.Loop_scatter_1024
+
+.align	32
+.Loop_scatter_1024:
+	vmovdqu		($inp),%ymm0
+	lea		32($inp),$inp
+	vpermd		%ymm0,%ymm5,%ymm0
+	vmovdqu		%xmm0,($out)
+	lea		16*32($out),$out
+	dec	%eax
+	jnz	.Loop_scatter_1024
+
+	vzeroupper
+	ret
+.size	rsaz_1024_scatter5_avx2,.-rsaz_1024_scatter5_avx2
+
+.globl	rsaz_1024_gather5_avx2
+.type	rsaz_1024_gather5_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_gather5_avx2:
+___
+$code.=<<___ if ($win64);
+	lea	-0x88(%rsp),%rax
+	vzeroupper
+.LSEH_begin_rsaz_1024_gather5:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax),%rsp
+	.byte	0xc5,0xf8,0x29,0x70,0xe0	#vmovaps %xmm6,-0x20(%rax)
+	.byte	0xc5,0xf8,0x29,0x78,0xf0	#vmovaps %xmm7,-0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x40,0x00	#vmovaps %xmm8,0(%rax)
+	.byte	0xc5,0x78,0x29,0x48,0x10	#vmovaps %xmm9,0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x50,0x20	#vmovaps %xmm10,0x20(%rax)
+	.byte	0xc5,0x78,0x29,0x58,0x30	#vmovaps %xmm11,0x30(%rax)
+	.byte	0xc5,0x78,0x29,0x60,0x40	#vmovaps %xmm12,0x40(%rax)
+	.byte	0xc5,0x78,0x29,0x68,0x50	#vmovaps %xmm13,0x50(%rax)
+	.byte	0xc5,0x78,0x29,0x70,0x60	#vmovaps %xmm14,0x60(%rax)
+	.byte	0xc5,0x78,0x29,0x78,0x70	#vmovaps %xmm15,0x70(%rax)
+___
+$code.=<<___;
+	lea	.Lgather_table(%rip),%r11
+	mov	$power,%eax
+	and	\$3,$power
+	shr	\$2,%eax			# cache line number
+	shl	\$4,$power			# offset within cache line
+
+	vmovdqu		-32(%r11),%ymm7		# .Lgather_permd
+	vpbroadcastb	8(%r11,%rax), %xmm8
+	vpbroadcastb	7(%r11,%rax), %xmm9
+	vpbroadcastb	6(%r11,%rax), %xmm10
+	vpbroadcastb	5(%r11,%rax), %xmm11
+	vpbroadcastb	4(%r11,%rax), %xmm12
+	vpbroadcastb	3(%r11,%rax), %xmm13
+	vpbroadcastb	2(%r11,%rax), %xmm14
+	vpbroadcastb	1(%r11,%rax), %xmm15
+
+	lea	64($inp,$power),$inp
+	mov	\$64,%r11			# size optimization
+	mov	\$9,%eax
+	jmp	.Loop_gather_1024
+
+.align	32
+.Loop_gather_1024:
+	vpand		-64($inp),		%xmm8,%xmm0
+	vpand		($inp),			%xmm9,%xmm1
+	vpand		64($inp),		%xmm10,%xmm2
+	vpand		($inp,%r11,2),		%xmm11,%xmm3
+	 vpor					%xmm0,%xmm1,%xmm1
+	vpand		64($inp,%r11,2),	%xmm12,%xmm4
+	 vpor					%xmm2,%xmm3,%xmm3
+	vpand		($inp,%r11,4),		%xmm13,%xmm5
+	 vpor					%xmm1,%xmm3,%xmm3
+	vpand		64($inp,%r11,4),	%xmm14,%xmm6
+	 vpor					%xmm4,%xmm5,%xmm5
+	vpand		-128($inp,%r11,8),	%xmm15,%xmm2
+	lea		($inp,%r11,8),$inp
+	 vpor					%xmm3,%xmm5,%xmm5
+	 vpor					%xmm2,%xmm6,%xmm6
+	 vpor					%xmm5,%xmm6,%xmm6
+	vpermd		%ymm6,%ymm7,%ymm6
+	vmovdqu		%ymm6,($out)
+	lea		32($out),$out
+	dec	%eax
+	jnz	.Loop_gather_1024
+
+	vpxor	%ymm0,%ymm0,%ymm0
+	vmovdqu	%ymm0,($out)
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	movaps	0x40(%rsp),%xmm10
+	movaps	0x50(%rsp),%xmm11
+	movaps	0x60(%rsp),%xmm12
+	movaps	0x70(%rsp),%xmm13
+	movaps	0x80(%rsp),%xmm14
+	movaps	0x90(%rsp),%xmm15
+	lea	0xa8(%rsp),%rsp
+.LSEH_end_rsaz_1024_gather5:
+___
+$code.=<<___;
+	ret
+.size	rsaz_1024_gather5_avx2,.-rsaz_1024_gather5_avx2
+___
+}
+
+$code.=<<___;
+.extern	OPENSSL_ia32cap_P
+.globl	rsaz_avx2_eligible
+.type	rsaz_avx2_eligible,\@abi-omnipotent
+.align	32
+rsaz_avx2_eligible:
+	mov	OPENSSL_ia32cap_P+8(%rip),%eax
+	and	\$`1<<5`,%eax
+	shr	\$5,%eax
+	ret
+.size	rsaz_avx2_eligible,.-rsaz_avx2_eligible
+
+.align	64
+.Land_mask:
+	.quad	0x1fffffff,0x1fffffff,0x1fffffff,-1
+.Lscatter_permd:
+	.long	0,2,4,6,7,7,7,7
+.Lgather_permd:
+	.long	0,7,1,7,2,7,3,7
+.Lgather_table:
+	.byte	0,0,0,0,0,0,0,0, 0xff,0,0,0,0,0,0,0
+.align	64
+___
+
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___
+.extern	__imp_RtlVirtualUnwind
+.type	rsaz_se_handler,\@abi-omnipotent
+.align	16
+rsaz_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	160($context),%rax	# pull context->Rbp
+
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	mov	%r15,240($context)
+	mov	%r14,232($context)
+	mov	%r13,224($context)
+	mov	%r12,216($context)
+	mov	%rbp,160($context)
+	mov	%rbx,144($context)
+
+	lea	-0xd8(%rax),%rsi	# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	rsaz_se_handler,.-rsaz_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_rsaz_1024_sqr_avx2
+	.rva	.LSEH_end_rsaz_1024_sqr_avx2
+	.rva	.LSEH_info_rsaz_1024_sqr_avx2
+
+	.rva	.LSEH_begin_rsaz_1024_mul_avx2
+	.rva	.LSEH_end_rsaz_1024_mul_avx2
+	.rva	.LSEH_info_rsaz_1024_mul_avx2
+
+	.rva	.LSEH_begin_rsaz_1024_gather5
+	.rva	.LSEH_end_rsaz_1024_gather5
+	.rva	.LSEH_info_rsaz_1024_gather5
+.section	.xdata
+.align	8
+.LSEH_info_rsaz_1024_sqr_avx2:
+	.byte	9,0,0,0
+	.rva	rsaz_se_handler
+	.rva	.Lsqr_1024_body,.Lsqr_1024_epilogue
+.LSEH_info_rsaz_1024_mul_avx2:
+	.byte	9,0,0,0
+	.rva	rsaz_se_handler
+	.rva	.Lmul_1024_body,.Lmul_1024_epilogue
+.LSEH_info_rsaz_1024_gather5:
+	.byte	0x01,0x33,0x16,0x00
+	.byte	0x36,0xf8,0x09,0x00	#vmovaps 0x90(rsp),xmm15
+	.byte	0x31,0xe8,0x08,0x00	#vmovaps 0x80(rsp),xmm14
+	.byte	0x2c,0xd8,0x07,0x00	#vmovaps 0x70(rsp),xmm13
+	.byte	0x27,0xc8,0x06,0x00	#vmovaps 0x60(rsp),xmm12
+	.byte	0x22,0xb8,0x05,0x00	#vmovaps 0x50(rsp),xmm11
+	.byte	0x1d,0xa8,0x04,0x00	#vmovaps 0x40(rsp),xmm10
+	.byte	0x18,0x98,0x03,0x00	#vmovaps 0x30(rsp),xmm9
+	.byte	0x13,0x88,0x02,0x00	#vmovaps 0x20(rsp),xmm8
+	.byte	0x0e,0x78,0x01,0x00	#vmovaps 0x10(rsp),xmm7
+	.byte	0x09,0x68,0x00,0x00	#vmovaps 0x00(rsp),xmm6
+	.byte	0x04,0x01,0x15,0x00	#sub	rsp,0xa8
+___
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/ge;
+
+	s/\b(sh[rl]d?\s+\$)(-?[0-9]+)/$1.$2%64/ge		or
+
+	s/\b(vmov[dq])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vmovdqu)\b(.+)%x%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vpinsr[qd])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go	or
+	s/\b(vpextr[qd])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go	or
+	s/\b(vpbroadcast[qd]\s+)%ymm([0-9]+)/$1%xmm$2/go;
+	print $_,"\n";
+}
+
+}}} else {{{
+print <<___;	# assembler is too old
+.text
+
+.globl	rsaz_avx2_eligible
+.type	rsaz_avx2_eligible,\@abi-omnipotent
+rsaz_avx2_eligible:
+	xor	%eax,%eax
+	ret
+.size	rsaz_avx2_eligible,.-rsaz_avx2_eligible
+
+.globl	rsaz_1024_sqr_avx2
+.globl	rsaz_1024_mul_avx2
+.globl	rsaz_1024_norm2red_avx2
+.globl	rsaz_1024_red2norm_avx2
+.globl	rsaz_1024_scatter5_avx2
+.globl	rsaz_1024_gather5_avx2
+.type	rsaz_1024_sqr_avx2,\@abi-omnipotent
+rsaz_1024_sqr_avx2:
+rsaz_1024_mul_avx2:
+rsaz_1024_norm2red_avx2:
+rsaz_1024_red2norm_avx2:
+rsaz_1024_scatter5_avx2:
+rsaz_1024_gather5_avx2:
+	.byte	0x0f,0x0b	# ud2
+	ret
+.size	rsaz_1024_sqr_avx2,.-rsaz_1024_sqr_avx2
+___
+}}}
+
+close STDOUT;
diff --git a/crypto/bn/asm/rsaz-x86_64.pl b/crypto/bn/asm/rsaz-x86_64.pl
new file mode 100644
index 0000000..120b473
--- /dev/null
+++ b/crypto/bn/asm/rsaz-x86_64.pl
@@ -0,0 +1,2139 @@
+#!/usr/bin/env perl
+
+##############################################################################
+#                                                                            #
+#  Copyright (c) 2012, Intel Corporation                                     #
+#                                                                            #
+#  All rights reserved.                                                      #
+#                                                                            #
+#  Redistribution and use in source and binary forms, with or without        #
+#  modification, are permitted provided that the following conditions are    #
+#  met:                                                                      #
+#                                                                            #
+#  *  Redistributions of source code must retain the above copyright         #
+#     notice, this list of conditions and the following disclaimer.          #
+#                                                                            #
+#  *  Redistributions in binary form must reproduce the above copyright      #
+#     notice, this list of conditions and the following disclaimer in the    #
+#     documentation and/or other materials provided with the                 #
+#     distribution.                                                          #
+#                                                                            #
+#  *  Neither the name of the Intel Corporation nor the names of its         #
+#     contributors may be used to endorse or promote products derived from   #
+#     this software without specific prior written permission.               #
+#                                                                            #
+#                                                                            #
+#  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          #
+#  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         #
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        #
+#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            #
+#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     #
+#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       #
+#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        #
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    #
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      #
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        #
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              #
+#                                                                            #
+##############################################################################
+# Developers and authors:                                                    #
+# Shay Gueron (1, 2), and Vlad Krasnov (1)                                   #
+# (1) Intel Architecture Group, Microprocessor and Chipset Development,      #
+#     Israel Development Center, Haifa, Israel                               #
+# (2) University of Haifa                                                    #
+##############################################################################
+# Reference:                                                                 #
+# [1] S. Gueron, "Efficient Software Implementations of Modular              #
+#     Exponentiation", http://eprint.iacr.org/2011/239                       #
+# [2] S. Gueron, V. Krasnov. "Speeding up Big-Numbers Squaring".             #
+#     IEEE Proceedings of 9th International Conference on Information        #
+#     Technology: New Generations (ITNG 2012), 821-823 (2012).               #
+# [3] S. Gueron, Efficient Software Implementations of Modular Exponentiation#
+#     Journal of Cryptographic Engineering 2:31-43 (2012).                   #
+# [4] S. Gueron, V. Krasnov: "[PATCH] Efficient and side channel analysis    #
+#     resistant 512-bit and 1024-bit modular exponentiation for optimizing   #
+#     RSA1024 and RSA2048 on x86_64 platforms",                              #
+#     http://rt.openssl.org/Ticket/Display.html?id=2582&user=guest&pass=guest#
+##############################################################################
+
+# While original submission covers 512- and 1024-bit exponentiation,
+# this module is limited to 512-bit version only (and as such
+# accelerates RSA1024 sign). This is because improvement for longer
+# keys is not high enough to justify the effort, highest measured
+# was ~5% on Westmere. [This is relative to OpenSSL 1.0.2, upcoming
+# for the moment of this writing!] Nor does this module implement
+# "monolithic" complete exponentiation jumbo-subroutine, but adheres
+# to more modular mixture of C and assembly. And it's optimized even
+# for processors other than Intel Core family (see table below for
+# improvement coefficients).
+# 						<appro@openssl.org>
+#
+# RSA1024 sign/sec	this/original	|this/rsax(*)	this/fips(*)
+#			----------------+---------------------------
+# Opteron		+13%		|+5%		+20%
+# Bulldozer		-0%		|-1%		+10%
+# P4			+11%		|+7%		+8%
+# Westmere		+5%		|+14%		+17%
+# Sandy Bridge		+2%		|+12%		+29%
+# Ivy Bridge		+1%		|+11%		+35%
+# Haswell(**)		-0%		|+12%		+39%
+# Atom			+13%		|+11%		+4%
+# VIA Nano		+70%		|+9%		+25%
+#
+# (*)	rsax engine and fips numbers are presented for reference
+#	purposes;
+# (**)	MULX was attempted, but found to give only marginal improvement;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| $^X $xlate $flavour $output";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$addx = ($1>=11);
+}
+
+($out, $inp, $mod) = ("%rdi", "%rsi", "%rbp");	# common internal API
+{
+my ($out,$inp,$mod,$n0,$times) = ("%rdi","%rsi","%rdx","%rcx","%r8d");
+
+$code.=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	rsaz_512_sqr
+.type	rsaz_512_sqr,\@function,5
+.align	32
+rsaz_512_sqr:				# 25-29% faster than rsaz_512_mul
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$128+24, %rsp
+.Lsqr_body:
+	movq	$mod, %rbp		# common argument
+	movq	($inp), %rdx
+	movq	8($inp), %rax
+	movq	$n0, 128(%rsp)
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Loop_sqrx
+___
+$code.=<<___;
+	jmp	.Loop_sqr
+
+.align	32
+.Loop_sqr:
+	movl	$times,128+8(%rsp)
+#first iteration
+	movq	%rdx, %rbx
+	mulq	%rdx
+	movq	%rax, %r8
+	movq	16($inp), %rax
+	movq	%rdx, %r9
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	24($inp), %rax
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	32($inp), %rax
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	40($inp), %rax
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	48($inp), %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	56($inp), %rax
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	%rbx, %rax
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	addq	%r8, %r8		#shlq	\$1, %r8
+	movq	%r9, %rcx
+	adcq	%r9, %r9		#shld	\$1, %r8, %r9
+
+	mulq	%rax
+	movq	%rax, (%rsp)
+	addq	%rdx, %r8
+	adcq	\$0, %r9
+
+	movq	%r8, 8(%rsp)
+	shrq	\$63, %rcx
+
+#second iteration
+	movq	8($inp), %r8
+	movq	16($inp), %rax
+	mulq	%r8
+	addq	%rax, %r10
+	movq	24($inp), %rax
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r11
+	movq	32($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r11
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r12
+	movq	40($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r12
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r13
+	movq	48($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r13
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r14
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r14
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r15
+	movq	%r8, %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r15
+	movq	%rdx, %r8
+	movq	%r10, %rdx
+	adcq	\$0, %r8
+
+	add	%rdx, %rdx
+	lea	(%rcx,%r10,2), %r10	#shld	\$1, %rcx, %r10
+	movq	%r11, %rbx
+	adcq	%r11, %r11		#shld	\$1, %r10, %r11
+
+	mulq	%rax
+	addq	%rax, %r9
+	adcq	%rdx, %r10
+	adcq	\$0, %r11
+
+	movq	%r9, 16(%rsp)
+	movq	%r10, 24(%rsp)
+	shrq	\$63, %rbx
+	
+#third iteration
+	movq	16($inp), %r9	
+	movq	24($inp), %rax
+	mulq	%r9
+	addq	%rax, %r12
+	movq	32($inp), %rax
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	addq	%rax, %r13
+	movq	40($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r13
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	addq	%rax, %r14
+	movq	48($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r14
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	 movq	%r12, %r10
+	 lea	(%rbx,%r12,2), %r12	#shld	\$1, %rbx, %r12
+	addq	%rax, %r15
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r15
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	 shrq	\$63, %r10
+	addq	%rax, %r8
+	movq	%r9, %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	movq	%r13, %rcx
+	leaq	(%r10,%r13,2), %r13	#shld	\$1, %r12, %r13
+
+	mulq	%rax
+	addq	%rax, %r11
+	adcq	%rdx, %r12
+	adcq	\$0, %r13
+
+	movq	%r11, 32(%rsp)
+	movq	%r12, 40(%rsp)
+	shrq	\$63, %rcx
+
+#fourth iteration
+	movq	24($inp), %r10
+	movq	32($inp), %rax
+	mulq	%r10
+	addq	%rax, %r14
+	movq	40($inp), %rax
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r10
+	addq	%rax, %r15
+	movq	48($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r15
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r10
+	 movq	%r14, %r12
+	 leaq	(%rcx,%r14,2), %r14	#shld	\$1, %rcx, %r14
+	addq	%rax, %r8
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r8
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r10
+	 shrq	\$63, %r12
+	addq	%rax, %r9
+	movq	%r10, %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	movq	%r15, %rbx
+	leaq	(%r12,%r15,2),%r15	#shld	\$1, %r14, %r15
+
+	mulq	%rax
+	addq	%rax, %r13
+	adcq	%rdx, %r14
+	adcq	\$0, %r15
+
+	movq	%r13, 48(%rsp)
+	movq	%r14, 56(%rsp)
+	shrq	\$63, %rbx
+
+#fifth iteration
+	movq	32($inp), %r11
+	movq	40($inp), %rax
+	mulq	%r11
+	addq	%rax, %r8
+	movq	48($inp), %rax
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r11
+	addq	%rax, %r9
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	 movq	%r8, %r12
+	 leaq	(%rbx,%r8,2), %r8	#shld	\$1, %rbx, %r8
+	addq	%rcx, %r9
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r11
+	 shrq	\$63, %r12
+	addq	%rax, %r10
+	movq	%r11, %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r10
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	movq	%r9, %rcx
+	leaq	(%r12,%r9,2), %r9	#shld	\$1, %r8, %r9
+
+	mulq	%rax
+	addq	%rax, %r15
+	adcq	%rdx, %r8
+	adcq	\$0, %r9
+
+	movq	%r15, 64(%rsp)
+	movq	%r8, 72(%rsp)
+	shrq	\$63, %rcx
+
+#sixth iteration
+	movq	40($inp), %r12
+	movq	48($inp), %rax
+	mulq	%r12
+	addq	%rax, %r10
+	movq	56($inp), %rax
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r12
+	addq	%rax, %r11
+	movq	%r12, %rax
+	 movq	%r10, %r15
+	 leaq	(%rcx,%r10,2), %r10	#shld	\$1, %rcx, %r10
+	adcq	\$0, %rdx
+	 shrq	\$63, %r15
+	addq	%rbx, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	movq	%r11, %rbx
+	leaq	(%r15,%r11,2), %r11	#shld	\$1, %r10, %r11
+
+	mulq	%rax
+	addq	%rax, %r9
+	adcq	%rdx, %r10
+	adcq	\$0, %r11
+
+	movq	%r9, 80(%rsp)
+	movq	%r10, 88(%rsp)
+
+#seventh iteration
+	movq	48($inp), %r13
+	movq	56($inp), %rax
+	mulq	%r13
+	addq	%rax, %r12
+	movq	%r13, %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	xorq	%r14, %r14
+	shlq	\$1, %rbx
+	adcq	%r12, %r12		#shld	\$1, %rbx, %r12
+	adcq	%r13, %r13		#shld	\$1, %r12, %r13
+	adcq	%r14, %r14		#shld	\$1, %r13, %r14
+
+	mulq	%rax
+	addq	%rax, %r11
+	adcq	%rdx, %r12
+	adcq	\$0, %r13
+
+	movq	%r11, 96(%rsp)
+	movq	%r12, 104(%rsp)
+
+#eighth iteration
+	movq	56($inp), %rax
+	mulq	%rax
+	addq	%rax, %r13
+	adcq	\$0, %rdx
+
+	addq	%rdx, %r14
+
+	movq	%r13, 112(%rsp)
+	movq	%r14, 120(%rsp)
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	movq	%r8, %rdx
+	movq	%r9, %rax
+	movl	128+8(%rsp), $times
+	movq	$out, $inp
+
+	decl	$times
+	jnz	.Loop_sqr
+___
+if ($addx) {
+$code.=<<___;
+	jmp	.Lsqr_tail
+
+.align	32
+.Loop_sqrx:
+	movl	$times,128+8(%rsp)
+	movq	$out, %xmm0		# off-load
+	movq	%rbp, %xmm1		# off-load
+#first iteration	
+	mulx	%rax, %r8, %r9
+
+	mulx	16($inp), %rcx, %r10
+	xor	%rbp, %rbp		# cf=0, of=0
+
+	mulx	24($inp), %rax, %r11
+	adcx	%rcx, %r9
+
+	mulx	32($inp), %rcx, %r12
+	adcx	%rax, %r10
+
+	mulx	40($inp), %rax, %r13
+	adcx	%rcx, %r11
+
+	.byte	0xc4,0x62,0xf3,0xf6,0xb6,0x30,0x00,0x00,0x00	# mulx	48($inp), %rcx, %r14
+	adcx	%rax, %r12
+	adcx	%rcx, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xbe,0x38,0x00,0x00,0x00	# mulx	56($inp), %rax, %r15
+	adcx	%rax, %r14
+	adcx	%rbp, %r15		# %rbp is 0
+
+	mov	%r9, %rcx
+	shld	\$1, %r8, %r9
+	shl	\$1, %r8
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rdx, %r8
+	 mov	8($inp), %rdx
+	adcx	%rbp, %r9
+
+	mov	%rax, (%rsp)
+	mov	%r8, 8(%rsp)
+
+#second iteration	
+	mulx	16($inp), %rax, %rbx
+	adox	%rax, %r10
+	adcx	%rbx, %r11
+
+	.byte	0xc4,0x62,0xc3,0xf6,0x86,0x18,0x00,0x00,0x00	# mulx	24($inp), $out, %r8
+	adox	$out, %r11
+	adcx	%r8, %r12
+
+	mulx	32($inp), %rax, %rbx
+	adox	%rax, %r12
+	adcx	%rbx, %r13
+
+	mulx	40($inp), $out, %r8
+	adox	$out, %r13
+	adcx	%r8, %r14
+
+	.byte	0xc4,0xe2,0xfb,0xf6,0x9e,0x30,0x00,0x00,0x00	# mulx	48($inp), %rax, %rbx
+	adox	%rax, %r14
+	adcx	%rbx, %r15
+
+	.byte	0xc4,0x62,0xc3,0xf6,0x86,0x38,0x00,0x00,0x00	# mulx	56($inp), $out, %r8
+	adox	$out, %r15
+	adcx	%rbp, %r8
+	adox	%rbp, %r8
+
+	mov	%r11, %rbx
+	shld	\$1, %r10, %r11
+	shld	\$1, %rcx, %r10
+
+	xor	%ebp,%ebp
+	mulx	%rdx, %rax, %rcx
+	 mov	16($inp), %rdx
+	adcx	%rax, %r9
+	adcx	%rcx, %r10
+	adcx	%rbp, %r11
+
+	mov	%r9, 16(%rsp)
+	.byte	0x4c,0x89,0x94,0x24,0x18,0x00,0x00,0x00		# mov	%r10, 24(%rsp)
+	
+#third iteration	
+	.byte	0xc4,0x62,0xc3,0xf6,0x8e,0x18,0x00,0x00,0x00	# mulx	24($inp), $out, %r9
+	adox	$out, %r12
+	adcx	%r9, %r13
+
+	mulx	32($inp), %rax, %rcx
+	adox	%rax, %r13
+	adcx	%rcx, %r14
+
+	mulx	40($inp), $out, %r9
+	adox	$out, %r14
+	adcx	%r9, %r15
+
+	.byte	0xc4,0xe2,0xfb,0xf6,0x8e,0x30,0x00,0x00,0x00	# mulx	48($inp), %rax, %rcx
+	adox	%rax, %r15
+	adcx	%rcx, %r8
+
+	.byte	0xc4,0x62,0xc3,0xf6,0x8e,0x38,0x00,0x00,0x00	# mulx	56($inp), $out, %r9
+	adox	$out, %r8
+	adcx	%rbp, %r9
+	adox	%rbp, %r9
+
+	mov	%r13, %rcx
+	shld	\$1, %r12, %r13
+	shld	\$1, %rbx, %r12
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r11
+	adcx	%rdx, %r12
+	 mov	24($inp), %rdx
+	adcx	%rbp, %r13
+
+	mov	%r11, 32(%rsp)
+	.byte	0x4c,0x89,0xa4,0x24,0x28,0x00,0x00,0x00		# mov	%r12, 40(%rsp)
+	
+#fourth iteration	
+	.byte	0xc4,0xe2,0xfb,0xf6,0x9e,0x20,0x00,0x00,0x00	# mulx	32($inp), %rax, %rbx
+	adox	%rax, %r14
+	adcx	%rbx, %r15
+
+	mulx	40($inp), $out, %r10
+	adox	$out, %r15
+	adcx	%r10, %r8
+
+	mulx	48($inp), %rax, %rbx
+	adox	%rax, %r8
+	adcx	%rbx, %r9
+
+	mulx	56($inp), $out, %r10
+	adox	$out, %r9
+	adcx	%rbp, %r10
+	adox	%rbp, %r10
+
+	.byte	0x66
+	mov	%r15, %rbx
+	shld	\$1, %r14, %r15
+	shld	\$1, %rcx, %r14
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r13
+	adcx	%rdx, %r14
+	 mov	32($inp), %rdx
+	adcx	%rbp, %r15
+
+	mov	%r13, 48(%rsp)
+	mov	%r14, 56(%rsp)
+	
+#fifth iteration	
+	.byte	0xc4,0x62,0xc3,0xf6,0x9e,0x28,0x00,0x00,0x00	# mulx	40($inp), $out, %r11
+	adox	$out, %r8
+	adcx	%r11, %r9
+
+	mulx	48($inp), %rax, %rcx
+	adox	%rax, %r9
+	adcx	%rcx, %r10
+
+	mulx	56($inp), $out, %r11
+	adox	$out, %r10
+	adcx	%rbp, %r11
+	adox	%rbp, %r11
+
+	mov	%r9, %rcx
+	shld	\$1, %r8, %r9
+	shld	\$1, %rbx, %r8
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r15
+	adcx	%rdx, %r8
+	 mov	40($inp), %rdx
+	adcx	%rbp, %r9
+
+	mov	%r15, 64(%rsp)
+	mov	%r8, 72(%rsp)
+	
+#sixth iteration	
+	.byte	0xc4,0xe2,0xfb,0xf6,0x9e,0x30,0x00,0x00,0x00	# mulx	48($inp), %rax, %rbx
+	adox	%rax, %r10
+	adcx	%rbx, %r11
+
+	.byte	0xc4,0x62,0xc3,0xf6,0xa6,0x38,0x00,0x00,0x00	# mulx	56($inp), $out, %r12
+	adox	$out, %r11
+	adcx	%rbp, %r12
+	adox	%rbp, %r12
+
+	mov	%r11, %rbx
+	shld	\$1, %r10, %r11
+	shld	\$1, %rcx, %r10
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r9
+	adcx	%rdx, %r10
+	 mov	48($inp), %rdx
+	adcx	%rbp, %r11
+
+	mov	%r9, 80(%rsp)
+	mov	%r10, 88(%rsp)
+
+#seventh iteration
+	.byte	0xc4,0x62,0xfb,0xf6,0xae,0x38,0x00,0x00,0x00	# mulx	56($inp), %rax, %r13
+	adox	%rax, %r12
+	adox	%rbp, %r13
+
+	xor	%r14, %r14
+	shld	\$1, %r13, %r14
+	shld	\$1, %r12, %r13
+	shld	\$1, %rbx, %r12
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r11
+	adcx	%rdx, %r12
+	 mov	56($inp), %rdx
+	adcx	%rbp, %r13
+
+	.byte	0x4c,0x89,0x9c,0x24,0x60,0x00,0x00,0x00		# mov	%r11, 96(%rsp)
+	.byte	0x4c,0x89,0xa4,0x24,0x68,0x00,0x00,0x00		# mov	%r12, 104(%rsp)
+
+#eighth iteration
+	mulx	%rdx, %rax, %rdx
+	adox	%rax, %r13
+	adox	%rbp, %rdx
+
+	.byte	0x66
+	add	%rdx, %r14
+
+	movq	%r13, 112(%rsp)
+	movq	%r14, 120(%rsp)
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	128(%rsp), %rdx		# pull $n0
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	movq	%r8, %rdx
+	movq	%r9, %rax
+	movl	128+8(%rsp), $times
+	movq	$out, $inp
+
+	decl	$times
+	jnz	.Loop_sqrx
+
+.Lsqr_tail:
+___
+}
+$code.=<<___;
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lsqr_epilogue:
+	ret
+.size	rsaz_512_sqr,.-rsaz_512_sqr
+___
+}
+{
+my ($out,$ap,$bp,$mod,$n0) = ("%rdi","%rsi","%rdx","%rcx","%r8");
+$code.=<<___;
+.globl	rsaz_512_mul
+.type	rsaz_512_mul,\@function,5
+.align	32
+rsaz_512_mul:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$128+24, %rsp
+.Lmul_body:
+	movq	$out, %xmm0		# off-load arguments
+	movq	$mod, %xmm1
+	movq	$n0, 128(%rsp)
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Lmulx
+___
+$code.=<<___;
+	movq	($bp), %rbx		# pass b[0]
+	movq	$bp, %rbp		# pass argument
+	call	__rsaz_512_mul
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lmul_tail
+
+.align	32
+.Lmulx:
+	movq	$bp, %rbp		# pass argument
+	movq	($bp), %rdx		# pass b[0]
+	call	__rsaz_512_mulx
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	128(%rsp), %rdx		# pull $n0
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+.Lmul_tail:
+___
+$code.=<<___;
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_epilogue:
+	ret
+.size	rsaz_512_mul,.-rsaz_512_mul
+___
+}
+{
+my ($out,$ap,$bp,$mod,$n0,$pwr) = ("%rdi","%rsi","%rdx","%rcx","%r8","%r9d");
+$code.=<<___;
+.globl	rsaz_512_mul_gather4
+.type	rsaz_512_mul_gather4,\@function,6
+.align	32
+rsaz_512_mul_gather4:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	$pwr, $pwr
+	subq	\$128+24, %rsp
+.Lmul_gather4_body:
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Lmulx_gather
+___
+$code.=<<___;
+	movl	64($bp,$pwr,4), %eax
+	movq	$out, %xmm0		# off-load arguments
+	movl	($bp,$pwr,4), %ebx
+	movq	$mod, %xmm1
+	movq	$n0, 128(%rsp)
+
+	shlq	\$32, %rax
+	or	%rax, %rbx
+	movq	($ap), %rax
+	 movq	8($ap), %rcx
+	 leaq	128($bp,$pwr,4), %rbp
+	mulq	%rbx			# 0 iteration
+	movq	%rax, (%rsp)
+	movq	%rcx, %rax
+	movq	%rdx, %r8
+
+	mulq	%rbx
+	 movd	(%rbp), %xmm4
+	addq	%rax, %r8
+	movq	16($ap), %rax
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	 movd	64(%rbp), %xmm5
+	addq	%rax, %r9
+	movq	24($ap), %rax
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	 pslldq	\$4, %xmm5
+	addq	%rax, %r10
+	movq	32($ap), %rax
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	 por	%xmm5, %xmm4
+	addq	%rax, %r11
+	movq	40($ap), %rax
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	48($ap), %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	 leaq	128(%rbp), %rbp
+	addq	%rax, %r13
+	movq	56($ap), %rax
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+	
+	mulq	%rbx
+	 movq	%xmm4, %rbx
+	addq	%rax, %r14
+	 movq	($ap), %rax
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	leaq	8(%rsp), %rdi
+	movl	\$7, %ecx
+	jmp	.Loop_mul_gather
+
+.align	32
+.Loop_mul_gather:
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	8($ap), %rax
+	movq	%r8, (%rdi)
+	movq	%rdx, %r8
+	adcq	\$0, %r8
+
+	mulq	%rbx
+	 movd	(%rbp), %xmm4
+	addq	%rax, %r9
+	movq	16($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r9, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	 movd	64(%rbp), %xmm5
+	addq	%rax, %r10
+	movq	24($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r10, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	 pslldq	\$4, %xmm5
+	addq	%rax, %r11
+	movq	32($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r11, %r10
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	 por	%xmm5, %xmm4
+	addq	%rax, %r12
+	movq	40($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r12, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	48($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r13, %r12
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	56($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r14, %r13
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	 movq	%xmm4, %rbx
+	addq	%rax, %r15
+	 movq	($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r15, %r14
+	movq	%rdx, %r15	
+	adcq	\$0, %r15
+
+	leaq	128(%rbp), %rbp
+	leaq	8(%rdi), %rdi
+
+	decl	%ecx
+	jnz	.Loop_mul_gather
+
+	movq	%r8, (%rdi)
+	movq	%r9, 8(%rdi)
+	movq	%r10, 16(%rdi)
+	movq	%r11, 24(%rdi)
+	movq	%r12, 32(%rdi)
+	movq	%r13, 40(%rdi)
+	movq	%r14, 48(%rdi)
+	movq	%r15, 56(%rdi)
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lmul_gather_tail
+
+.align	32
+.Lmulx_gather:
+	mov	64($bp,$pwr,4), %eax
+	movq	$out, %xmm0		# off-load arguments
+	lea	128($bp,$pwr,4), %rbp
+	mov	($bp,$pwr,4), %edx
+	movq	$mod, %xmm1
+	mov	$n0, 128(%rsp)
+
+	shl	\$32, %rax
+	or	%rax, %rdx
+	mulx	($ap), %rbx, %r8	# 0 iteration
+	mov	%rbx, (%rsp)
+	xor	%edi, %edi		# cf=0, of=0
+
+	mulx	8($ap), %rax, %r9
+	 movd	(%rbp), %xmm4
+
+	mulx	16($ap), %rbx, %r10
+	 movd	64(%rbp), %xmm5
+	adcx	%rax, %r8
+
+	mulx	24($ap), %rax, %r11
+	 pslldq	\$4, %xmm5
+	adcx	%rbx, %r9
+
+	mulx	32($ap), %rbx, %r12
+	 por	%xmm5, %xmm4
+	adcx	%rax, %r10
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rbx, %r11
+
+	mulx	48($ap), %rbx, %r14
+	 lea	128(%rbp), %rbp
+	adcx	%rax, %r12
+	
+	mulx	56($ap), %rax, %r15
+	 movq	%xmm4, %rdx
+	adcx	%rbx, %r13
+	adcx	%rax, %r14
+	mov	%r8, %rbx
+	adcx	%rdi, %r15		# %rdi is 0
+
+	mov	\$-7, %rcx
+	jmp	.Loop_mulx_gather
+
+.align	32
+.Loop_mulx_gather:
+	mulx	($ap), %rax, %r8
+	adcx	%rax, %rbx
+	adox	%r9, %r8
+
+	mulx	8($ap), %rax, %r9
+	.byte	0x66,0x0f,0x6e,0xa5,0x00,0x00,0x00,0x00		# movd	(%rbp), %xmm4
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	mulx	16($ap), %rax, %r10
+	 movd	64(%rbp), %xmm5
+	 lea	128(%rbp), %rbp
+	adcx	%rax, %r9
+	adox	%r11, %r10
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x9e,0x18,0x00,0x00,0x00	# mulx	24($ap), %rax, %r11
+	 pslldq	\$4, %xmm5
+	 por	%xmm5, %xmm4
+	adcx	%rax, %r10
+	adox	%r12, %r11
+
+	mulx	32($ap), %rax, %r12
+	adcx	%rax, %r11
+	adox	%r13, %r12
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xb6,0x30,0x00,0x00,0x00	# mulx	48($ap), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	mulx	56($ap), %rax, %r15
+	 movq	%xmm4, %rdx
+	 mov	%rbx, 64(%rsp,%rcx,8)
+	adcx	%rax, %r14
+	adox	%rdi, %r15
+	mov	%r8, %rbx
+	adcx	%rdi, %r15		# cf=0
+
+	inc	%rcx			# of=0
+	jnz	.Loop_mulx_gather
+
+	mov	%r8, 64(%rsp)
+	mov	%r9, 64+8(%rsp)
+	mov	%r10, 64+16(%rsp)
+	mov	%r11, 64+24(%rsp)
+	mov	%r12, 64+32(%rsp)
+	mov	%r13, 64+40(%rsp)
+	mov	%r14, 64+48(%rsp)
+	mov	%r15, 64+56(%rsp)
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	mov	128(%rsp), %rdx		# pull $n0
+	mov	(%rsp), %r8
+	mov	8(%rsp), %r9
+	mov	16(%rsp), %r10
+	mov	24(%rsp), %r11
+	mov	32(%rsp), %r12
+	mov	40(%rsp), %r13
+	mov	48(%rsp), %r14
+	mov	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+
+.Lmul_gather_tail:
+___
+$code.=<<___;
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_gather4_epilogue:
+	ret
+.size	rsaz_512_mul_gather4,.-rsaz_512_mul_gather4
+___
+}
+{
+my ($out,$ap,$mod,$n0,$tbl,$pwr) = ("%rdi","%rsi","%rdx","%rcx","%r8","%r9d");
+$code.=<<___;
+.globl	rsaz_512_mul_scatter4
+.type	rsaz_512_mul_scatter4,\@function,6
+.align	32
+rsaz_512_mul_scatter4:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	$pwr, $pwr
+	subq	\$128+24, %rsp
+.Lmul_scatter4_body:
+	leaq	($tbl,$pwr,4), $tbl
+	movq	$out, %xmm0		# off-load arguments
+	movq	$mod, %xmm1
+	movq	$tbl, %xmm2
+	movq	$n0, 128(%rsp)
+
+	movq	$out, %rbp
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Lmulx_scatter
+___
+$code.=<<___;
+	movq	($out),%rbx		# pass b[0]
+	call	__rsaz_512_mul
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lmul_scatter_tail
+	
+.align	32
+.Lmulx_scatter:
+	movq	($out), %rdx		# pass b[0]
+	call	__rsaz_512_mulx
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	128(%rsp), %rdx		# pull $n0
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+
+.Lmul_scatter_tail:
+___
+$code.=<<___;
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	movq	%xmm2, $inp
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	movl	%r8d, 64*0($inp)	# scatter
+	shrq	\$32, %r8
+	movl	%r9d, 64*2($inp)
+	shrq	\$32, %r9
+	movl	%r10d, 64*4($inp)
+	shrq	\$32, %r10
+	movl	%r11d, 64*6($inp)
+	shrq	\$32, %r11
+	movl	%r12d, 64*8($inp)
+	shrq	\$32, %r12
+	movl	%r13d, 64*10($inp)
+	shrq	\$32, %r13
+	movl	%r14d, 64*12($inp)
+	shrq	\$32, %r14
+	movl	%r15d, 64*14($inp)
+	shrq	\$32, %r15
+	movl	%r8d, 64*1($inp)
+	movl	%r9d, 64*3($inp)
+	movl	%r10d, 64*5($inp)
+	movl	%r11d, 64*7($inp)
+	movl	%r12d, 64*9($inp)
+	movl	%r13d, 64*11($inp)
+	movl	%r14d, 64*13($inp)
+	movl	%r15d, 64*15($inp)
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_scatter4_epilogue:
+	ret
+.size	rsaz_512_mul_scatter4,.-rsaz_512_mul_scatter4
+___
+}
+{
+my ($out,$inp,$mod,$n0) = ("%rdi","%rsi","%rdx","%rcx");
+$code.=<<___;
+.globl	rsaz_512_mul_by_one
+.type	rsaz_512_mul_by_one,\@function,4
+.align	32
+rsaz_512_mul_by_one:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$128+24, %rsp
+.Lmul_by_one_body:
+___
+$code.=<<___ if ($addx);
+	movl	OPENSSL_ia32cap_P+8(%rip),%eax
+___
+$code.=<<___;
+	movq	$mod, %rbp	# reassign argument
+	movq	$n0, 128(%rsp)
+
+	movq	($inp), %r8
+	pxor	%xmm0, %xmm0
+	movq	8($inp), %r9
+	movq	16($inp), %r10
+	movq	24($inp), %r11
+	movq	32($inp), %r12
+	movq	40($inp), %r13
+	movq	48($inp), %r14
+	movq	56($inp), %r15
+
+	movdqa	%xmm0, (%rsp)
+	movdqa	%xmm0, 16(%rsp)
+	movdqa	%xmm0, 32(%rsp)
+	movdqa	%xmm0, 48(%rsp)
+	movdqa	%xmm0, 64(%rsp)
+	movdqa	%xmm0, 80(%rsp)
+	movdqa	%xmm0, 96(%rsp)
+___
+$code.=<<___ if ($addx);
+	andl	\$0x80100,%eax
+	cmpl	\$0x80100,%eax		# check for MULX and ADO/CX
+	je	.Lby_one_callx
+___
+$code.=<<___;
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lby_one_tail
+.align	32
+.Lby_one_callx:
+	movq	128(%rsp), %rdx		# pull $n0
+	call	__rsaz_512_reducex
+.Lby_one_tail:
+___
+$code.=<<___;
+	movq	%r8, ($out)
+	movq	%r9, 8($out)
+	movq	%r10, 16($out)
+	movq	%r11, 24($out)
+	movq	%r12, 32($out)
+	movq	%r13, 40($out)
+	movq	%r14, 48($out)
+	movq	%r15, 56($out)
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_by_one_epilogue:
+	ret
+.size	rsaz_512_mul_by_one,.-rsaz_512_mul_by_one
+___
+}
+{	# __rsaz_512_reduce
+	#
+	# input:	%r8-%r15, %rbp - mod, 128(%rsp) - n0
+	# output:	%r8-%r15
+	# clobbers:	everything except %rbp and %rdi
+$code.=<<___;
+.type	__rsaz_512_reduce,\@abi-omnipotent
+.align	32
+__rsaz_512_reduce:
+	movq	%r8, %rbx
+	imulq	128+8(%rsp), %rbx
+	movq	0(%rbp), %rax
+	movl	\$8, %ecx
+	jmp	.Lreduction_loop
+
+.align	32
+.Lreduction_loop:
+	mulq	%rbx
+	movq	8(%rbp), %rax
+	negq	%r8
+	movq	%rdx, %r8
+	adcq	\$0, %r8
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	16(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r9, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	24(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r10, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	32(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r11, %r10
+	 movq	128+8(%rsp), %rsi
+	#movq	%rdx, %r11
+	#adcq	\$0, %r11
+	adcq	\$0, %rdx
+	movq	%rdx, %r11
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	40(%rbp), %rax
+	adcq	\$0, %rdx
+	 imulq	%r8, %rsi
+	addq	%r12, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	48(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r13, %r12
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	56(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r14, %r13
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	 movq	%rsi, %rbx
+	addq	%rax, %r15
+	 movq	0(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r15, %r14
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	decl	%ecx
+	jne	.Lreduction_loop
+
+	ret
+.size	__rsaz_512_reduce,.-__rsaz_512_reduce
+___
+}
+if ($addx) {
+	# __rsaz_512_reducex
+	#
+	# input:	%r8-%r15, %rbp - mod, 128(%rsp) - n0
+	# output:	%r8-%r15
+	# clobbers:	everything except %rbp and %rdi
+$code.=<<___;
+.type	__rsaz_512_reducex,\@abi-omnipotent
+.align	32
+__rsaz_512_reducex:
+	#movq	128+8(%rsp), %rdx		# pull $n0
+	imulq	%r8, %rdx
+	xorq	%rsi, %rsi			# cf=0,of=0
+	movl	\$8, %ecx
+	jmp	.Lreduction_loopx
+
+.align	32
+.Lreduction_loopx:
+	mov	%r8, %rbx
+	mulx	0(%rbp), %rax, %r8
+	adcx	%rbx, %rax
+	adox	%r9, %r8
+
+	mulx	8(%rbp), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	mulx	16(%rbp), %rbx, %r10
+	adcx	%rbx, %r9
+	adox	%r11, %r10
+
+	mulx	24(%rbp), %rbx, %r11
+	adcx	%rbx, %r10
+	adox	%r12, %r11
+
+	.byte	0xc4,0x62,0xe3,0xf6,0xa5,0x20,0x00,0x00,0x00	# mulx	32(%rbp), %rbx, %r12
+	 mov	%rdx, %rax
+	 mov	%r8, %rdx
+	adcx	%rbx, %r11
+	adox	%r13, %r12
+
+	 mulx	128+8(%rsp), %rbx, %rdx
+	 mov	%rax, %rdx
+
+	mulx	40(%rbp), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xb5,0x30,0x00,0x00,0x00	# mulx	48(%rbp), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	mulx	56(%rbp), %rax, %r15
+	 mov	%rbx, %rdx
+	adcx	%rax, %r14
+	adox	%rsi, %r15			# %rsi is 0
+	adcx	%rsi, %r15			# cf=0
+
+	decl	%ecx				# of=0
+	jne	.Lreduction_loopx
+
+	ret
+.size	__rsaz_512_reducex,.-__rsaz_512_reducex
+___
+}
+{	# __rsaz_512_subtract
+	# input: %r8-%r15, %rdi - $out, %rbp - $mod, %rcx - mask
+	# output:
+	# clobbers: everything but %rdi, %rsi and %rbp
+$code.=<<___;
+.type	__rsaz_512_subtract,\@abi-omnipotent
+.align	32
+__rsaz_512_subtract:
+	movq	%r8, ($out)
+	movq	%r9, 8($out)
+	movq	%r10, 16($out)
+	movq	%r11, 24($out)
+	movq	%r12, 32($out)
+	movq	%r13, 40($out)
+	movq	%r14, 48($out)
+	movq	%r15, 56($out)
+
+	movq	0($mod), %r8
+	movq	8($mod), %r9
+	negq	%r8
+	notq	%r9
+	andq	%rcx, %r8
+	movq	16($mod), %r10
+	andq	%rcx, %r9
+	notq	%r10
+	movq	24($mod), %r11
+	andq	%rcx, %r10
+	notq	%r11
+	movq	32($mod), %r12
+	andq	%rcx, %r11
+	notq	%r12
+	movq	40($mod), %r13
+	andq	%rcx, %r12
+	notq	%r13
+	movq	48($mod), %r14
+	andq	%rcx, %r13
+	notq	%r14
+	movq	56($mod), %r15
+	andq	%rcx, %r14
+	notq	%r15
+	andq	%rcx, %r15
+
+	addq	($out), %r8
+	adcq	8($out), %r9
+	adcq	16($out), %r10
+	adcq	24($out), %r11
+	adcq	32($out), %r12
+	adcq	40($out), %r13
+	adcq	48($out), %r14
+	adcq	56($out), %r15
+
+	movq	%r8, ($out)
+	movq	%r9, 8($out)
+	movq	%r10, 16($out)
+	movq	%r11, 24($out)
+	movq	%r12, 32($out)
+	movq	%r13, 40($out)
+	movq	%r14, 48($out)
+	movq	%r15, 56($out)
+
+	ret
+.size	__rsaz_512_subtract,.-__rsaz_512_subtract
+___
+}
+{	# __rsaz_512_mul
+	#
+	# input: %rsi - ap, %rbp - bp
+	# ouput:
+	# clobbers: everything
+my ($ap,$bp) = ("%rsi","%rbp");
+$code.=<<___;
+.type	__rsaz_512_mul,\@abi-omnipotent
+.align	32
+__rsaz_512_mul:
+	leaq	8(%rsp), %rdi
+
+	movq	($ap), %rax
+	mulq	%rbx
+	movq	%rax, (%rdi)
+	movq	8($ap), %rax
+	movq	%rdx, %r8
+
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	16($ap), %rax
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	24($ap), %rax
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	32($ap), %rax
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	40($ap), %rax
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	48($ap), %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	56($ap), %rax
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+	
+	mulq	%rbx
+	addq	%rax, %r14
+	 movq	($ap), %rax
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	leaq	8($bp), $bp
+	leaq	8(%rdi), %rdi
+
+	movl	\$7, %ecx
+	jmp	.Loop_mul
+
+.align	32
+.Loop_mul:
+	movq	($bp), %rbx
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	8($ap), %rax
+	movq	%r8, (%rdi)
+	movq	%rdx, %r8
+	adcq	\$0, %r8
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	16($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r9, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	24($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r10, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	32($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r11, %r10
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	40($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r12, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	48($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r13, %r12
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	56($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r14, %r13
+	movq	%rdx, %r14
+	 leaq	8($bp), $bp
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	addq	%rax, %r15
+	 movq	($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r15, %r14
+	movq	%rdx, %r15	
+	adcq	\$0, %r15
+
+	leaq	8(%rdi), %rdi
+
+	decl	%ecx
+	jnz	.Loop_mul
+
+	movq	%r8, (%rdi)
+	movq	%r9, 8(%rdi)
+	movq	%r10, 16(%rdi)
+	movq	%r11, 24(%rdi)
+	movq	%r12, 32(%rdi)
+	movq	%r13, 40(%rdi)
+	movq	%r14, 48(%rdi)
+	movq	%r15, 56(%rdi)
+
+	ret
+.size	__rsaz_512_mul,.-__rsaz_512_mul
+___
+}
+if ($addx) {
+	# __rsaz_512_mulx
+	#
+	# input: %rsi - ap, %rbp - bp
+	# ouput:
+	# clobbers: everything
+my ($ap,$bp,$zero) = ("%rsi","%rbp","%rdi");
+$code.=<<___;
+.type	__rsaz_512_mulx,\@abi-omnipotent
+.align	32
+__rsaz_512_mulx:
+	mulx	($ap), %rbx, %r8	# initial %rdx preloaded by caller
+	mov	\$-6, %rcx
+
+	mulx	8($ap), %rax, %r9
+	movq	%rbx, 8(%rsp)
+
+	mulx	16($ap), %rbx, %r10
+	adc	%rax, %r8
+
+	mulx	24($ap), %rax, %r11
+	adc	%rbx, %r9
+
+	mulx	32($ap), %rbx, %r12
+	adc	%rax, %r10
+
+	mulx	40($ap), %rax, %r13
+	adc	%rbx, %r11
+
+	mulx	48($ap), %rbx, %r14
+	adc	%rax, %r12
+
+	mulx	56($ap), %rax, %r15
+	 mov	8($bp), %rdx
+	adc	%rbx, %r13
+	adc	%rax, %r14
+	adc	\$0, %r15
+
+	xor	$zero, $zero		# cf=0,of=0
+	jmp	.Loop_mulx
+
+.align	32
+.Loop_mulx:
+	movq	%r8, %rbx
+	mulx	($ap), %rax, %r8
+	adcx	%rax, %rbx
+	adox	%r9, %r8
+
+	mulx	8($ap), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	mulx	16($ap), %rax, %r10
+	adcx	%rax, %r9
+	adox	%r11, %r10
+
+	mulx	24($ap), %rax, %r11
+	adcx	%rax, %r10
+	adox	%r12, %r11
+
+	.byte	0x3e,0xc4,0x62,0xfb,0xf6,0xa6,0x20,0x00,0x00,0x00	# mulx	32($ap), %rax, %r12
+	adcx	%rax, %r11
+	adox	%r13, %r12
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	mulx	48($ap), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	mulx	56($ap), %rax, %r15
+	 movq	64($bp,%rcx,8), %rdx
+	 movq	%rbx, 8+64-8(%rsp,%rcx,8)
+	adcx	%rax, %r14
+	adox	$zero, %r15
+	adcx	$zero, %r15		# cf=0
+
+	inc	%rcx			# of=0
+	jnz	.Loop_mulx
+
+	movq	%r8, %rbx
+	mulx	($ap), %rax, %r8
+	adcx	%rax, %rbx
+	adox	%r9, %r8
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x8e,0x08,0x00,0x00,0x00	# mulx	8($ap), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x96,0x10,0x00,0x00,0x00	# mulx	16($ap), %rax, %r10
+	adcx	%rax, %r9
+	adox	%r11, %r10
+
+	mulx	24($ap), %rax, %r11
+	adcx	%rax, %r10
+	adox	%r12, %r11
+
+	mulx	32($ap), %rax, %r12
+	adcx	%rax, %r11
+	adox	%r13, %r12
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xb6,0x30,0x00,0x00,0x00	# mulx	48($ap), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xbe,0x38,0x00,0x00,0x00	# mulx	56($ap), %rax, %r15
+	adcx	%rax, %r14
+	adox	$zero, %r15
+	adcx	$zero, %r15
+
+	mov	%rbx, 8+64-8(%rsp)
+	mov	%r8, 8+64(%rsp)
+	mov	%r9, 8+64+8(%rsp)
+	mov	%r10, 8+64+16(%rsp)
+	mov	%r11, 8+64+24(%rsp)
+	mov	%r12, 8+64+32(%rsp)
+	mov	%r13, 8+64+40(%rsp)
+	mov	%r14, 8+64+48(%rsp)
+	mov	%r15, 8+64+56(%rsp)
+
+	ret
+.size	__rsaz_512_mulx,.-__rsaz_512_mulx
+___
+}
+{
+my ($out,$inp,$power)= $win64 ? ("%rcx","%rdx","%r8d") : ("%rdi","%rsi","%edx");
+$code.=<<___;
+.globl	rsaz_512_scatter4
+.type	rsaz_512_scatter4,\@abi-omnipotent
+.align	16
+rsaz_512_scatter4:
+	leaq	($out,$power,4), $out
+	movl	\$8, %r9d
+	jmp	.Loop_scatter
+.align	16
+.Loop_scatter:
+	movq	($inp), %rax
+	leaq	8($inp), $inp
+	movl	%eax, ($out)
+	shrq	\$32, %rax
+	movl	%eax, 64($out)
+	leaq	128($out), $out
+	decl	%r9d
+	jnz	.Loop_scatter
+	ret
+.size	rsaz_512_scatter4,.-rsaz_512_scatter4
+
+.globl	rsaz_512_gather4
+.type	rsaz_512_gather4,\@abi-omnipotent
+.align	16
+rsaz_512_gather4:
+	leaq	($inp,$power,4), $inp
+	movl	\$8, %r9d
+	jmp	.Loop_gather
+.align	16
+.Loop_gather:
+	movl	($inp), %eax
+	movl	64($inp), %r8d
+	leaq	128($inp), $inp
+	shlq	\$32, %r8
+	or	%r8, %rax
+	movq	%rax, ($out)
+	leaq	8($out), $out
+	decl	%r9d
+	jnz	.Loop_gather
+	ret
+.size	rsaz_512_gather4,.-rsaz_512_gather4
+___
+}
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<end of prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	128+24+48(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	sqr_handler,.-sqr_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_rsaz_512_sqr
+	.rva	.LSEH_end_rsaz_512_sqr
+	.rva	.LSEH_info_rsaz_512_sqr
+
+	.rva	.LSEH_begin_rsaz_512_mul
+	.rva	.LSEH_end_rsaz_512_mul
+	.rva	.LSEH_info_rsaz_512_mul
+
+	.rva	.LSEH_begin_rsaz_512_mul_gather4
+	.rva	.LSEH_end_rsaz_512_mul_gather4
+	.rva	.LSEH_info_rsaz_512_mul_gather4
+
+	.rva	.LSEH_begin_rsaz_512_mul_scatter4
+	.rva	.LSEH_end_rsaz_512_mul_scatter4
+	.rva	.LSEH_info_rsaz_512_mul_scatter4
+
+	.rva	.LSEH_begin_rsaz_512_mul_by_one
+	.rva	.LSEH_end_rsaz_512_mul_by_one
+	.rva	.LSEH_info_rsaz_512_mul_by_one
+
+.section	.xdata
+.align	8
+.LSEH_info_rsaz_512_sqr:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lsqr_body,.Lsqr_epilogue			# HandlerData[]
+.LSEH_info_rsaz_512_mul:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_body,.Lmul_epilogue			# HandlerData[]
+.LSEH_info_rsaz_512_mul_gather4:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_gather4_body,.Lmul_gather4_epilogue	# HandlerData[]
+.LSEH_info_rsaz_512_mul_scatter4:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_scatter4_body,.Lmul_scatter4_epilogue	# HandlerData[]
+.LSEH_info_rsaz_512_mul_by_one:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_by_one_body,.Lmul_by_one_epilogue		# HandlerData[]
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/x86-mont.pl b/crypto/bn/asm/x86-mont.pl
new file mode 100644
index 0000000..e8f6b05
--- /dev/null
+++ b/crypto/bn/asm/x86-mont.pl
@@ -0,0 +1,593 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# October 2005
+#
+# This is a "teaser" code, as it can be improved in several ways...
+# First of all non-SSE2 path should be implemented (yes, for now it
+# performs Montgomery multiplication/convolution only on SSE2-capable
+# CPUs such as P4, others fall down to original code). Then inner loop
+# can be unrolled and modulo-scheduled to improve ILP and possibly
+# moved to 128-bit XMM register bank (though it would require input
+# rearrangement and/or increase bus bandwidth utilization). Dedicated
+# squaring procedure should give further performance improvement...
+# Yet, for being draft, the code improves rsa512 *sign* benchmark by
+# 110%(!), rsa1024 one - by 70% and rsa4096 - by 20%:-)
+
+# December 2006
+#
+# Modulo-scheduling SSE2 loops results in further 15-20% improvement.
+# Integer-only code [being equipped with dedicated squaring procedure]
+# gives ~40% on rsa512 sign benchmark...
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],$0);
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+&function_begin("bn_mul_mont");
+
+$i="edx";
+$j="ecx";
+$ap="esi";	$tp="esi";		# overlapping variables!!!
+$rp="edi";	$bp="edi";		# overlapping variables!!!
+$np="ebp";
+$num="ebx";
+
+$_num=&DWP(4*0,"esp");			# stack top layout
+$_rp=&DWP(4*1,"esp");
+$_ap=&DWP(4*2,"esp");
+$_bp=&DWP(4*3,"esp");
+$_np=&DWP(4*4,"esp");
+$_n0=&DWP(4*5,"esp");	$_n0q=&QWP(4*5,"esp");
+$_sp=&DWP(4*6,"esp");
+$_bpend=&DWP(4*7,"esp");
+$frame=32;				# size of above frame rounded up to 16n
+
+	&xor	("eax","eax");
+	&mov	("edi",&wparam(5));	# int num
+	&cmp	("edi",4);
+	&jl	(&label("just_leave"));
+
+	&lea	("esi",&wparam(0));	# put aside pointer to argument block
+	&lea	("edx",&wparam(1));	# load ap
+	&mov	("ebp","esp");		# saved stack pointer!
+	&add	("edi",2);		# extra two words on top of tp
+	&neg	("edi");
+	&lea	("esp",&DWP(-$frame,"esp","edi",4));	# alloca($frame+4*(num+2))
+	&neg	("edi");
+
+	# minimize cache contention by arraning 2K window between stack
+	# pointer and ap argument [np is also position sensitive vector,
+	# but it's assumed to be near ap, as it's allocated at ~same
+	# time].
+	&mov	("eax","esp");
+	&sub	("eax","edx");
+	&and	("eax",2047);
+	&sub	("esp","eax");		# this aligns sp and ap modulo 2048
+
+	&xor	("edx","esp");
+	&and	("edx",2048);
+	&xor	("edx",2048);
+	&sub	("esp","edx");		# this splits them apart modulo 4096
+
+	&and	("esp",-64);		# align to cache line
+
+	################################# load argument block...
+	&mov	("eax",&DWP(0*4,"esi"));# BN_ULONG *rp
+	&mov	("ebx",&DWP(1*4,"esi"));# const BN_ULONG *ap
+	&mov	("ecx",&DWP(2*4,"esi"));# const BN_ULONG *bp
+	&mov	("edx",&DWP(3*4,"esi"));# const BN_ULONG *np
+	&mov	("esi",&DWP(4*4,"esi"));# const BN_ULONG *n0
+	#&mov	("edi",&DWP(5*4,"esi"));# int num
+
+	&mov	("esi",&DWP(0,"esi"));	# pull n0[0]
+	&mov	($_rp,"eax");		# ... save a copy of argument block
+	&mov	($_ap,"ebx");
+	&mov	($_bp,"ecx");
+	&mov	($_np,"edx");
+	&mov	($_n0,"esi");
+	&lea	($num,&DWP(-3,"edi"));	# num=num-1 to assist modulo-scheduling
+	#&mov	($_num,$num);		# redundant as $num is not reused
+	&mov	($_sp,"ebp");		# saved stack pointer!
+
+if($sse2) {
+$acc0="mm0";	# mmx register bank layout
+$acc1="mm1";
+$car0="mm2";
+$car1="mm3";
+$mul0="mm4";
+$mul1="mm5";
+$temp="mm6";
+$mask="mm7";
+
+	&picmeup("eax","OPENSSL_ia32cap_P");
+	&bt	(&DWP(0,"eax"),26);
+	&jnc	(&label("non_sse2"));
+
+	&mov	("eax",-1);
+	&movd	($mask,"eax");		# mask 32 lower bits
+
+	&mov	($ap,$_ap);		# load input pointers
+	&mov	($bp,$_bp);
+	&mov	($np,$_np);
+
+	&xor	($i,$i);		# i=0
+	&xor	($j,$j);		# j=0
+
+	&movd	($mul0,&DWP(0,$bp));		# bp[0]
+	&movd	($mul1,&DWP(0,$ap));		# ap[0]
+	&movd	($car1,&DWP(0,$np));		# np[0]
+
+	&pmuludq($mul1,$mul0);			# ap[0]*bp[0]
+	&movq	($car0,$mul1);
+	&movq	($acc0,$mul1);			# I wish movd worked for
+	&pand	($acc0,$mask);			# inter-register transfers
+
+	&pmuludq($mul1,$_n0q);			# *=n0
+
+	&pmuludq($car1,$mul1);			# "t[0]"*np[0]*n0
+	&paddq	($car1,$acc0);
+
+	&movd	($acc1,&DWP(4,$np));		# np[1]
+	&movd	($acc0,&DWP(4,$ap));		# ap[1]
+
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+
+	&inc	($j);				# j++
+&set_label("1st",16);
+	&pmuludq($acc0,$mul0);			# ap[j]*bp[0]
+	&pmuludq($acc1,$mul1);			# np[j]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&pand	($acc0,$mask);
+	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
+	&paddq	($car1,$acc0);			# +=ap[j]*bp[0];
+	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
+	&psrlq	($car0,32);
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[j-1]=
+	&psrlq	($car1,32);
+
+	&lea	($j,&DWP(1,$j));
+	&cmp	($j,$num);
+	&jl	(&label("1st"));
+
+	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[0]
+	&pmuludq($acc1,$mul1);			# np[num-1]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&pand	($acc0,$mask);
+	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[0];
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
+
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+
+	&paddq	($car1,$car0);
+	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
+
+	&inc	($i);				# i++
+&set_label("outer");
+	&xor	($j,$j);			# j=0
+
+	&movd	($mul0,&DWP(0,$bp,$i,4));	# bp[i]
+	&movd	($mul1,&DWP(0,$ap));		# ap[0]
+	&movd	($temp,&DWP($frame,"esp"));	# tp[0]
+	&movd	($car1,&DWP(0,$np));		# np[0]
+	&pmuludq($mul1,$mul0);			# ap[0]*bp[i]
+
+	&paddq	($mul1,$temp);			# +=tp[0]
+	&movq	($acc0,$mul1);
+	&movq	($car0,$mul1);
+	&pand	($acc0,$mask);
+
+	&pmuludq($mul1,$_n0q);			# *=n0
+
+	&pmuludq($car1,$mul1);
+	&paddq	($car1,$acc0);
+
+	&movd	($temp,&DWP($frame+4,"esp"));	# tp[1]
+	&movd	($acc1,&DWP(4,$np));		# np[1]
+	&movd	($acc0,&DWP(4,$ap));		# ap[1]
+
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+	&paddq	($car0,$temp);			# +=tp[1]
+
+	&inc	($j);				# j++
+	&dec	($num);
+&set_label("inner");
+	&pmuludq($acc0,$mul0);			# ap[j]*bp[i]
+	&pmuludq($acc1,$mul1);			# np[j]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&movd	($temp,&DWP($frame+4,"esp",$j,4));# tp[j+1]
+	&pand	($acc0,$mask);
+	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
+	&paddq	($car1,$acc0);			# +=ap[j]*bp[i]+tp[j]
+	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
+	&psrlq	($car0,32);
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);# tp[j-1]=
+	&psrlq	($car1,32);
+	&paddq	($car0,$temp);			# +=tp[j+1]
+
+	&dec	($num);
+	&lea	($j,&DWP(1,$j));		# j++
+	&jnz	(&label("inner"));
+
+	&mov	($num,$j);
+	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[i]
+	&pmuludq($acc1,$mul1);			# np[num-1]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&pand	($acc0,$mask);
+	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[i]+tp[num-1]
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+
+	&movd	($temp,&DWP($frame+4,"esp",$num,4));	# += tp[num]
+	&paddq	($car1,$car0);
+	&paddq	($car1,$temp);
+	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
+
+	&lea	($i,&DWP(1,$i));		# i++
+	&cmp	($i,$num);
+	&jle	(&label("outer"));
+
+	&emms	();				# done with mmx bank
+	&jmp	(&label("common_tail"));
+
+&set_label("non_sse2",16);
+}
+
+if (0) {
+	&mov	("esp",$_sp);
+	&xor	("eax","eax");	# signal "not fast enough [yet]"
+	&jmp	(&label("just_leave"));
+	# While the below code provides competitive performance for
+	# all key lengthes on modern Intel cores, it's still more
+	# than 10% slower for 4096-bit key elsewhere:-( "Competitive"
+	# means compared to the original integer-only assembler.
+	# 512-bit RSA sign is better by ~40%, but that's about all
+	# one can say about all CPUs...
+} else {
+$inp="esi";	# integer path uses these registers differently
+$word="edi";
+$carry="ebp";
+
+	&mov	($inp,$_ap);
+	&lea	($carry,&DWP(1,$num));
+	&mov	($word,$_bp);
+	&xor	($j,$j);				# j=0
+	&mov	("edx",$inp);
+	&and	($carry,1);				# see if num is even
+	&sub	("edx",$word);				# see if ap==bp
+	&lea	("eax",&DWP(4,$word,$num,4));		# &bp[num]
+	&or	($carry,"edx");
+	&mov	($word,&DWP(0,$word));			# bp[0]
+	&jz	(&label("bn_sqr_mont"));
+	&mov	($_bpend,"eax");
+	&mov	("eax",&DWP(0,$inp));
+	&xor	("edx","edx");
+
+&set_label("mull",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*bp[0]
+	&add	($carry,"eax");
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j+1]
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("mull"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[num-1]*bp[0]
+	 &mov	($word,$_n0);
+	&add	("eax",$carry);
+	 &mov	($inp,$_np);
+	&adc	("edx",0);
+	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+
+	&mov	(&DWP($frame,"esp",$num,4),"eax");	# tp[num-1]=
+	&xor	($j,$j);
+	&mov	(&DWP($frame+4,"esp",$num,4),"edx");	# tp[num]=
+	&mov	(&DWP($frame+8,"esp",$num,4),$j);	# tp[num+1]=
+
+	&mov	("eax",&DWP(0,$inp));			# np[0]
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+	&adc	("edx",0);
+	&inc	($j);
+
+	&jmp	(&label("2ndmadd"));
+
+&set_label("1stmadd",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*bp[i]
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j+1]
+	&adc	("edx",0);
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("1stmadd"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[num-1]*bp[i]
+	&add	("eax",&DWP($frame,"esp",$num,4));	# +=tp[num-1]
+	 &mov	($word,$_n0);
+	&adc	("edx",0);
+	 &mov	($inp,$_np);
+	&add	($carry,"eax");
+	&adc	("edx",0);
+	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+
+	&xor	($j,$j);
+	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
+	&mov	(&DWP($frame,"esp",$num,4),$carry);	# tp[num-1]=
+	&adc	($j,0);
+	 &mov	("eax",&DWP(0,$inp));			# np[0]
+	&mov	(&DWP($frame+4,"esp",$num,4),"edx");	# tp[num]=
+	&mov	(&DWP($frame+8,"esp",$num,4),$j);	# tp[num+1]=
+
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+	&adc	("edx",0);
+	&mov	($j,1);
+
+&set_label("2ndmadd",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(0,$inp,$j,4));		# np[j+1]
+	&adc	("edx",0);
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-8,"esp",$j,4),$carry);	# tp[j-1]=
+	&jl	(&label("2ndmadd"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$num,4));	# +=tp[num-1]
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&adc	("edx",0);
+	&mov	(&DWP($frame-4,"esp",$num,4),$carry);	# tp[num-2]=
+
+	&xor	("eax","eax");
+	 &mov	($j,$_bp);				# &bp[i]
+	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
+	&adc	("eax",&DWP($frame+8,"esp",$num,4));	# +=tp[num+1]
+	 &lea	($j,&DWP(4,$j));
+	&mov	(&DWP($frame,"esp",$num,4),"edx");	# tp[num-1]=
+	 &cmp	($j,$_bpend);
+	&mov	(&DWP($frame+4,"esp",$num,4),"eax");	# tp[num]=
+	&je	(&label("common_tail"));
+
+	&mov	($word,&DWP(0,$j));			# bp[i+1]
+	&mov	($inp,$_ap);
+	&mov	($_bp,$j);				# &bp[++i]
+	&xor	($j,$j);
+	&xor	("edx","edx");
+	&mov	("eax",&DWP(0,$inp));
+	&jmp	(&label("1stmadd"));
+
+&set_label("bn_sqr_mont",16);
+$sbit=$num;
+	&mov	($_num,$num);
+	&mov	($_bp,$j);				# i=0
+
+	&mov	("eax",$word);				# ap[0]
+	&mul	($word);				# ap[0]*ap[0]
+	&mov	(&DWP($frame,"esp"),"eax");		# tp[0]=
+	&mov	($sbit,"edx");
+	&shr	("edx",1);
+	&and	($sbit,1);
+	&inc	($j);
+&set_label("sqr",16);
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j]
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*ap[0]
+	&add	("eax",$carry);
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&lea	($carry,&DWP(0,$sbit,"eax",2));
+	&shr	("eax",31);
+	&cmp	($j,$_num);
+	&mov	($sbit,"eax");
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("sqr"));
+
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[num-1]
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[num-1]*ap[0]
+	&add	("eax",$carry);
+	 &mov	($word,$_n0);
+	&adc	("edx",0);
+	 &mov	($inp,$_np);
+	&lea	($carry,&DWP(0,$sbit,"eax",2));
+	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+	&shr	("eax",31);
+	&mov	(&DWP($frame,"esp",$j,4),$carry);	# tp[num-1]=
+
+	&lea	($carry,&DWP(0,"eax","edx",2));
+	 &mov	("eax",&DWP(0,$inp));			# np[0]
+	&shr	("edx",31);
+	&mov	(&DWP($frame+4,"esp",$j,4),$carry);	# tp[num]=
+	&mov	(&DWP($frame+8,"esp",$j,4),"edx");	# tp[num+1]=
+
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&mov	($num,$j);
+	&adc	("edx",0);
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+	&mov	($j,1);
+
+&set_label("3rdmadd",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(4,$inp,$j,4));		# np[j+1]
+	&adc	("edx",0);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j-1]=
+
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j+1]*m
+	&add	($carry,&DWP($frame+4,"esp",$j,4));	# +=tp[j+1]
+	&lea	($j,&DWP(2,$j));
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(0,$inp,$j,4));		# np[j+2]
+	&adc	("edx",0);
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-8,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("3rdmadd"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$num,4));	# +=tp[num-1]
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&adc	("edx",0);
+	&mov	(&DWP($frame-4,"esp",$num,4),$carry);	# tp[num-2]=
+
+	&mov	($j,$_bp);				# i
+	&xor	("eax","eax");
+	&mov	($inp,$_ap);
+	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
+	&adc	("eax",&DWP($frame+8,"esp",$num,4));	# +=tp[num+1]
+	&mov	(&DWP($frame,"esp",$num,4),"edx");	# tp[num-1]=
+	&cmp	($j,$num);
+	&mov	(&DWP($frame+4,"esp",$num,4),"eax");	# tp[num]=
+	&je	(&label("common_tail"));
+
+	&mov	($word,&DWP(4,$inp,$j,4));		# ap[i]
+	&lea	($j,&DWP(1,$j));
+	&mov	("eax",$word);
+	&mov	($_bp,$j);				# ++i
+	&mul	($word);				# ap[i]*ap[i]
+	&add	("eax",&DWP($frame,"esp",$j,4));	# +=tp[i]
+	&adc	("edx",0);
+	&mov	(&DWP($frame,"esp",$j,4),"eax");	# tp[i]=
+	&xor	($carry,$carry);
+	&cmp	($j,$num);
+	&lea	($j,&DWP(1,$j));
+	&je	(&label("sqrlast"));
+
+	&mov	($sbit,"edx");				# zaps $num
+	&shr	("edx",1);
+	&and	($sbit,1);
+&set_label("sqradd",16);
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j]
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*ap[i]
+	&add	("eax",$carry);
+	&lea	($carry,&DWP(0,"eax","eax"));
+	&adc	("edx",0);
+	&shr	("eax",31);
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&lea	($j,&DWP(1,$j));
+	&adc	("eax",0);
+	&add	($carry,$sbit);
+	&adc	("eax",0);
+	&cmp	($j,$_num);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&mov	($sbit,"eax");
+	&jle	(&label("sqradd"));
+
+	&mov	($carry,"edx");
+	&add	("edx","edx");
+	&shr	($carry,31);
+	&add	("edx",$sbit);
+	&adc	($carry,0);
+&set_label("sqrlast");
+	&mov	($word,$_n0);
+	&mov	($inp,$_np);
+	&imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+
+	&add	("edx",&DWP($frame,"esp",$j,4));	# +=tp[num]
+	&mov	("eax",&DWP(0,$inp));			# np[0]
+	&adc	($carry,0);
+	&mov	(&DWP($frame,"esp",$j,4),"edx");	# tp[num]=
+	&mov	(&DWP($frame+4,"esp",$j,4),$carry);	# tp[num+1]=
+
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&lea	($num,&DWP(-1,$j));
+	&adc	("edx",0);
+	&mov	($j,1);
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+
+	&jmp	(&label("3rdmadd"));
+}
+
+&set_label("common_tail",16);
+	&mov	($np,$_np);			# load modulus pointer
+	&mov	($rp,$_rp);			# load result pointer
+	&lea	($tp,&DWP($frame,"esp"));	# [$ap and $bp are zapped]
+
+	&mov	("eax",&DWP(0,$tp));		# tp[0]
+	&mov	($j,$num);			# j=num-1
+	&xor	($i,$i);			# i=0 and clear CF!
+
+&set_label("sub",16);
+	&sbb	("eax",&DWP(0,$np,$i,4));
+	&mov	(&DWP(0,$rp,$i,4),"eax");	# rp[i]=tp[i]-np[i]
+	&dec	($j);				# doesn't affect CF!
+	&mov	("eax",&DWP(4,$tp,$i,4));	# tp[i+1]
+	&lea	($i,&DWP(1,$i));		# i++
+	&jge	(&label("sub"));
+
+	&sbb	("eax",0);			# handle upmost overflow bit
+	&and	($tp,"eax");
+	&not	("eax");
+	&mov	($np,$rp);
+	&and	($np,"eax");
+	&or	($tp,$np);			# tp=carry?tp:rp
+
+&set_label("copy",16);				# copy or in-place refresh
+	&mov	("eax",&DWP(0,$tp,$num,4));
+	&mov	(&DWP(0,$rp,$num,4),"eax");	# rp[i]=tp[i]
+	&mov	(&DWP($frame,"esp",$num,4),$j);	# zap temporary vector
+	&dec	($num);
+	&jge	(&label("copy"));
+
+	&mov	("esp",$_sp);		# pull saved stack pointer
+	&mov	("eax",1);
+&set_label("just_leave");
+&function_end("bn_mul_mont");
+
+&asciz("Montgomery Multiplication for x86, CRYPTOGAMS by <appro\@openssl.org>");
+
+&asm_finish();
diff --git a/crypto/bn/asm/x86.pl b/crypto/bn/asm/x86.pl
new file mode 100644
index 0000000..783f8dd
--- /dev/null
+++ b/crypto/bn/asm/x86.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+require("x86/mul_add.pl");
+require("x86/mul.pl");
+require("x86/sqr.pl");
+require("x86/div.pl");
+require("x86/add.pl");
+require("x86/sub.pl");
+require("x86/comba.pl");
+
+&asm_init($ARGV[0],$0);
+
+&bn_mul_add_words("bn_mul_add_words");
+&bn_mul_words("bn_mul_words");
+&bn_sqr_words("bn_sqr_words");
+&bn_div_words("bn_div_words");
+&bn_add_words("bn_add_words");
+&bn_sub_words("bn_sub_words");
+&bn_mul_comba("bn_mul_comba8",8);
+&bn_mul_comba("bn_mul_comba4",4);
+&bn_sqr_comba("bn_sqr_comba8",8);
+&bn_sqr_comba("bn_sqr_comba4",4);
+
+&asm_finish();
diff --git a/crypto/bn/asm/x86/add.pl b/crypto/bn/asm/x86/add.pl
new file mode 100644
index 0000000..0b5cf58
--- /dev/null
+++ b/crypto/bn/asm/x86/add.pl
@@ -0,0 +1,76 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub bn_add_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&add($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &add($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+		&add($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &add($tmp1,$tmp2);
+		&adc($c,0);
+		 &dec($num) if ($i != 6);
+		&mov(&DWP($i*4,$r,"",0),$tmp1);	# *a
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
+1;
diff --git a/crypto/bn/asm/x86/comba.pl b/crypto/bn/asm/x86/comba.pl
new file mode 100644
index 0000000..2291253
--- /dev/null
+++ b/crypto/bn/asm/x86/comba.pl
@@ -0,0 +1,277 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub mul_add_c
+	{
+	local($a,$ai,$b,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("mul a[$ai]*b[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$b,"",0));
+
+	&mul("edx");
+	&add($c0,"eax");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# laod next a
+	 &mov("eax",&wparam(0)) if $pos > 0;			# load r[]
+	 ###
+	&adc($c1,"edx");
+	 &mov("edx",&DWP(($nb)*4,$b,"",0)) if $pos == 0;	# laod next b
+	 &mov("edx",&DWP(($nb)*4,$b,"",0)) if $pos == 1;	# laod next b
+	 ###
+	&adc($c2,0);
+	 # is pos > 1, it means it is the last loop 
+	 &mov(&DWP($i*4,"eax","",0),$c0) if $pos > 0;		# save r[];
+	&mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;		# laod next a
+	}
+
+sub sqr_add_c
+	{
+	local($r,$a,$ai,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("sqr a[$ai]*a[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$b,"",0));
+
+	if ($ai == $bi)
+		{ &mul("eax");}
+	else
+		{ &mul("edx");}
+	&add($c0,"eax");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# load next a
+	 ###
+	&adc($c1,"edx");
+	 &mov("edx",&DWP(($nb)*4,$a,"",0)) if ($pos == 1) && ($na != $nb);
+	 ###
+	&adc($c2,0);
+	 # is pos > 1, it means it is the last loop 
+	 &mov(&DWP($i*4,$r,"",0),$c0) if $pos > 0;		# save r[];
+	&mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;		# load next b
+	}
+
+sub sqr_add_c2
+	{
+	local($r,$a,$ai,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("sqr a[$ai]*a[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$a,"",0));
+
+	if ($ai == $bi)
+		{ &mul("eax");}
+	else
+		{ &mul("edx");}
+	&add("eax","eax");
+	 ###
+	&adc("edx","edx");
+	 ###
+	&adc($c2,0);
+	 &add($c0,"eax");
+	&adc($c1,"edx");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# load next a
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;	# load next b
+	&adc($c2,0);
+	&mov(&DWP($i*4,$r,"",0),$c0) if $pos > 0;		# save r[];
+	 &mov("edx",&DWP(($nb)*4,$a,"",0)) if ($pos <= 1) && ($na != $nb);
+	 ###
+	}
+
+sub bn_mul_comba
+	{
+	local($name,$num)=@_;
+	local($a,$b,$c0,$c1,$c2);
+	local($i,$as,$ae,$bs,$be,$ai,$bi);
+	local($tot,$end);
+
+	&function_begin_B($name,"");
+
+	$c0="ebx";
+	$c1="ecx";
+	$c2="ebp";
+	$a="esi";
+	$b="edi";
+	
+	$as=0;
+	$ae=0;
+	$bs=0;
+	$be=0;
+	$tot=$num+$num-1;
+
+	&push("esi");
+	 &mov($a,&wparam(1));
+	&push("edi");
+	 &mov($b,&wparam(2));
+	&push("ebp");
+	 &push("ebx");
+
+	&xor($c0,$c0);
+	 &mov("eax",&DWP(0,$a,"",0));	# load the first word 
+	&xor($c1,$c1);
+	 &mov("edx",&DWP(0,$b,"",0));	# load the first second 
+
+	for ($i=0; $i<$tot; $i++)
+		{
+		$ai=$as;
+		$bi=$bs;
+		$end=$be+1;
+
+		&comment("################## Calculate word $i"); 
+
+		for ($j=$bs; $j<$end; $j++)
+			{
+			&xor($c2,$c2) if ($j == $bs);
+			if (($j+1) == $end)
+				{
+				$v=1;
+				$v=2 if (($i+1) == $tot);
+				}
+			else
+				{ $v=0; }
+			if (($j+1) != $end)
+				{
+				$na=($ai-1);
+				$nb=($bi+1);
+				}
+			else
+				{
+				$na=$as+($i < ($num-1));
+				$nb=$bs+($i >= ($num-1));
+				}
+#printf STDERR "[$ai,$bi] -> [$na,$nb]\n";
+			&mul_add_c($a,$ai,$b,$bi,$c0,$c1,$c2,$v,$i,$na,$nb);
+			if ($v)
+				{
+				&comment("saved r[$i]");
+				# &mov("eax",&wparam(0));
+				# &mov(&DWP($i*4,"eax","",0),$c0);
+				($c0,$c1,$c2)=($c1,$c2,$c0);
+				}
+			$ai--;
+			$bi++;
+			}
+		$as++ if ($i < ($num-1));
+		$ae++ if ($i >= ($num-1));
+
+		$bs++ if ($i >= ($num-1));
+		$be++ if ($i < ($num-1));
+		}
+	&comment("save r[$i]");
+	# &mov("eax",&wparam(0));
+	&mov(&DWP($i*4,"eax","",0),$c0);
+
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
+
+sub bn_sqr_comba
+	{
+	local($name,$num)=@_;
+	local($r,$a,$c0,$c1,$c2)=@_;
+	local($i,$as,$ae,$bs,$be,$ai,$bi);
+	local($b,$tot,$end,$half);
+
+	&function_begin_B($name,"");
+
+	$c0="ebx";
+	$c1="ecx";
+	$c2="ebp";
+	$a="esi";
+	$r="edi";
+
+	&push("esi");
+	 &push("edi");
+	&push("ebp");
+	 &push("ebx");
+	&mov($r,&wparam(0));
+	 &mov($a,&wparam(1));
+	&xor($c0,$c0);
+	 &xor($c1,$c1);
+	&mov("eax",&DWP(0,$a,"",0)); # load the first word
+
+	$as=0;
+	$ae=0;
+	$bs=0;
+	$be=0;
+	$tot=$num+$num-1;
+
+	for ($i=0; $i<$tot; $i++)
+		{
+		$ai=$as;
+		$bi=$bs;
+		$end=$be+1;
+
+		&comment("############### Calculate word $i");
+		for ($j=$bs; $j<$end; $j++)
+			{
+			&xor($c2,$c2) if ($j == $bs);
+			if (($ai-1) < ($bi+1))
+				{
+				$v=1;
+				$v=2 if ($i+1) == $tot;
+				}
+			else
+				{ $v=0; }
+			if (!$v)
+				{
+				$na=$ai-1;
+				$nb=$bi+1;
+				}
+			else
+				{
+				$na=$as+($i < ($num-1));
+				$nb=$bs+($i >= ($num-1));
+				}
+			if ($ai == $bi)
+				{
+				&sqr_add_c($r,$a,$ai,$bi,
+					$c0,$c1,$c2,$v,$i,$na,$nb);
+				}
+			else
+				{
+				&sqr_add_c2($r,$a,$ai,$bi,
+					$c0,$c1,$c2,$v,$i,$na,$nb);
+				}
+			if ($v)
+				{
+				&comment("saved r[$i]");
+				#&mov(&DWP($i*4,$r,"",0),$c0);
+				($c0,$c1,$c2)=($c1,$c2,$c0);
+				last;
+				}
+			$ai--;
+			$bi++;
+			}
+		$as++ if ($i < ($num-1));
+		$ae++ if ($i >= ($num-1));
+
+		$bs++ if ($i >= ($num-1));
+		$be++ if ($i < ($num-1));
+		}
+	&mov(&DWP($i*4,$r,"",0),$c0);
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
+
+1;
diff --git a/crypto/bn/asm/x86/div.pl b/crypto/bn/asm/x86/div.pl
new file mode 100644
index 0000000..0e90152
--- /dev/null
+++ b/crypto/bn/asm/x86/div.pl
@@ -0,0 +1,15 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub bn_div_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+	&mov("edx",&wparam(0));	#
+	&mov("eax",&wparam(1));	#
+	&mov("ebx",&wparam(2));	#
+	&div("ebx");
+	&function_end($name);
+	}
+1;
diff --git a/crypto/bn/asm/x86/mul.pl b/crypto/bn/asm/x86/mul.pl
new file mode 100644
index 0000000..674cb9b
--- /dev/null
+++ b/crypto/bn/asm/x86/mul.pl
@@ -0,0 +1,77 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub bn_mul_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$Low="eax";
+	$High="edx";
+	$a="ebx";
+	$w="ecx";
+	$r="edi";
+	$c="esi";
+	$num="ebp";
+
+	&xor($c,$c);		# clear carry
+	&mov($r,&wparam(0));	#
+	&mov($a,&wparam(1));	#
+	&mov($num,&wparam(2));	#
+	&mov($w,&wparam(3));	#
+
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("mw_finish"));
+
+	&set_label("mw_loop",0);
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+
+		 &mov("eax",&DWP($i,$a,"",0)); 	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 # XXX
+
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i,$r,"",0),"eax");	# *r= L(t);
+
+		&mov($c,"edx");			# c=  H(t);
+		}
+
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jz(&label("mw_finish"));
+	&jmp(&label("mw_loop"));
+
+	&set_label("mw_finish",0);
+	&mov($num,&wparam(2));	# get num
+	&and($num,7);
+	&jnz(&label("mw_finish2"));
+	&jmp(&label("mw_end"));
+
+	&set_label("mw_finish2",1);
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		 &mov("eax",&DWP($i*4,$a,"",0));# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 # XXX
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i*4,$r,"",0),"eax");# *r= L(t);
+		&mov($c,"edx");			# c=  H(t);
+		 &dec($num) if ($i != 7-1);
+		&jz(&label("mw_end")) if ($i != 7-1);
+		}
+	&set_label("mw_end",0);
+	&mov("eax",$c);
+
+	&function_end($name);
+	}
+
+1;
diff --git a/crypto/bn/asm/x86/mul_add.pl b/crypto/bn/asm/x86/mul_add.pl
new file mode 100644
index 0000000..61830d3
--- /dev/null
+++ b/crypto/bn/asm/x86/mul_add.pl
@@ -0,0 +1,87 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub bn_mul_add_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$Low="eax";
+	$High="edx";
+	$a="ebx";
+	$w="ebp";
+	$r="edi";
+	$c="esi";
+
+	&xor($c,$c);		# clear carry
+	&mov($r,&wparam(0));	#
+
+	&mov("ecx",&wparam(2));	#
+	&mov($a,&wparam(1));	#
+
+	&and("ecx",0xfffffff8);	# num / 8
+	&mov($w,&wparam(3));	#
+
+	&push("ecx");		# Up the stack for a tmp variable
+
+	&jz(&label("maw_finish"));
+
+	&set_label("maw_loop",0);
+
+	&mov(&swtmp(0),"ecx");	#
+
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+
+		 &mov("eax",&DWP($i,$a,"",0)); 	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);		# L(t)+= *r
+		 &mov($c,&DWP($i,$r,"",0));	# L(t)+= *r
+		&adc("edx",0);			# H(t)+=carry
+		 &add("eax",$c);		# L(t)+=c
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i,$r,"",0),"eax");	# *r= L(t);
+		&mov($c,"edx");			# c=  H(t);
+		}
+
+	&comment("");
+	&mov("ecx",&swtmp(0));	#
+	&add($a,32);
+	&add($r,32);
+	&sub("ecx",8);
+	&jnz(&label("maw_loop"));
+
+	&set_label("maw_finish",0);
+	&mov("ecx",&wparam(2));	# get num
+	&and("ecx",7);
+	&jnz(&label("maw_finish2"));	# helps branch prediction
+	&jmp(&label("maw_end"));
+
+	&set_label("maw_finish2",1);
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		 &mov("eax",&DWP($i*4,$a,"",0));# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 &mov($c,&DWP($i*4,$r,"",0));	# L(t)+= *r
+		&adc("edx",0);			# H(t)+=carry
+		 &add("eax",$c);
+		&adc("edx",0);			# H(t)+=carry
+		 &dec("ecx") if ($i != 7-1);
+		&mov(&DWP($i*4,$r,"",0),"eax");	# *r= L(t);
+		 &mov($c,"edx");			# c=  H(t);
+		&jz(&label("maw_end")) if ($i != 7-1);
+		}
+	&set_label("maw_end",0);
+	&mov("eax",$c);
+
+	&pop("ecx");	# clear variable from
+
+	&function_end($name);
+	}
+
+1;
diff --git a/crypto/bn/asm/x86/sqr.pl b/crypto/bn/asm/x86/sqr.pl
new file mode 100644
index 0000000..1f90993
--- /dev/null
+++ b/crypto/bn/asm/x86/sqr.pl
@@ -0,0 +1,60 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub bn_sqr_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$r="esi";
+	$a="edi";
+	$num="ebx";
+
+	&mov($r,&wparam(0));	#
+	&mov($a,&wparam(1));	#
+	&mov($num,&wparam(2));	#
+
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("sw_finish"));
+
+	&set_label("sw_loop",0);
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+		&mov("eax",&DWP($i,$a,"",0)); 	# *a
+		 # XXX
+		&mul("eax");			# *a * *a
+		&mov(&DWP($i*2,$r,"",0),"eax");	#
+		 &mov(&DWP($i*2+4,$r,"",0),"edx");#
+		}
+
+	&comment("");
+	&add($a,32);
+	&add($r,64);
+	&sub($num,8);
+	&jnz(&label("sw_loop"));
+
+	&set_label("sw_finish",0);
+	&mov($num,&wparam(2));	# get num
+	&and($num,7);
+	&jz(&label("sw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov("eax",&DWP($i*4,$a,"",0));	# *a
+		 # XXX
+		&mul("eax");			# *a * *a
+		&mov(&DWP($i*8,$r,"",0),"eax");	#
+		 &dec($num) if ($i != 7-1);
+		&mov(&DWP($i*8+4,$r,"",0),"edx");
+		 &jz(&label("sw_end")) if ($i != 7-1);
+		}
+	&set_label("sw_end",0);
+
+	&function_end($name);
+	}
+
+1;
diff --git a/crypto/bn/asm/x86/sub.pl b/crypto/bn/asm/x86/sub.pl
new file mode 100644
index 0000000..837b0e1
--- /dev/null
+++ b/crypto/bn/asm/x86/sub.pl
@@ -0,0 +1,76 @@
+#!/usr/local/bin/perl
+# x86 assember
+
+sub bn_sub_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &dec($num) if ($i != 6);
+		&mov(&DWP($i*4,$r,"",0),$tmp1);	# *a
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
+1;
diff --git a/crypto/bn/asm/x86_64-gcc.c b/crypto/bn/asm/x86_64-gcc.c
new file mode 100644
index 0000000..836f001
--- /dev/null
+++ b/crypto/bn/asm/x86_64-gcc.c
@@ -0,0 +1,613 @@
+#include <openssl/bn.h>
+
+#if defined(OPENSSL_X86_64) && !defined(OPENSSL_WINDOWS)
+
+#include "../internal.h"
+
+/* x86_64 BIGNUM accelerator version 0.1, December 2002.
+ *
+ * Implemented by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+ * project.
+ *
+ * Rights for redistribution and usage in source and binary forms are
+ * granted according to the OpenSSL license. Warranty of any kind is
+ * disclaimed.
+ *
+ * Q. Version 0.1? It doesn't sound like Andy, he used to assign real
+ *    versions, like 1.0...
+ * A. Well, that's because this code is basically a quick-n-dirty
+ *    proof-of-concept hack. As you can see it's implemented with
+ *    inline assembler, which means that you're bound to GCC and that
+ *    there might be enough room for further improvement.
+ *
+ * Q. Why inline assembler?
+ * A. x86_64 features own ABI which I'm not familiar with. This is
+ *    why I decided to let the compiler take care of subroutine
+ *    prologue/epilogue as well as register allocation. For reference.
+ *    Win64 implements different ABI for AMD64, different from Linux.
+ *
+ * Q. How much faster does it get?
+ * A. 'apps/openssl speed rsa dsa' output with no-asm:
+ *
+ *	                  sign    verify    sign/s verify/s
+ *	rsa  512 bits   0.0006s   0.0001s   1683.8  18456.2
+ *	rsa 1024 bits   0.0028s   0.0002s    356.0   6407.0
+ *	rsa 2048 bits   0.0172s   0.0005s     58.0   1957.8
+ *	rsa 4096 bits   0.1155s   0.0018s      8.7    555.6
+ *	                  sign    verify    sign/s verify/s
+ *	dsa  512 bits   0.0005s   0.0006s   2100.8   1768.3
+ *	dsa 1024 bits   0.0014s   0.0018s    692.3    559.2
+ *	dsa 2048 bits   0.0049s   0.0061s    204.7    165.0
+ *
+ *    'apps/openssl speed rsa dsa' output with this module:
+ *
+ *	                  sign    verify    sign/s verify/s
+ *	rsa  512 bits   0.0004s   0.0000s   2767.1  33297.9
+ *	rsa 1024 bits   0.0012s   0.0001s    867.4  14674.7
+ *	rsa 2048 bits   0.0061s   0.0002s    164.0   5270.0
+ *	rsa 4096 bits   0.0384s   0.0006s     26.1   1650.8
+ *	                  sign    verify    sign/s verify/s
+ *	dsa  512 bits   0.0002s   0.0003s   4442.2   3786.3
+ *	dsa 1024 bits   0.0005s   0.0007s   1835.1   1497.4
+ *	dsa 2048 bits   0.0016s   0.0020s    620.4    504.6
+ *
+ *    For the reference. IA-32 assembler implementation performs
+ *    very much like 64-bit code compiled with no-asm on the same
+ *    machine.
+ */
+
+#undef mul
+#undef mul_add
+
+#define asm __asm__
+
+/*
+ * "m"(a), "+m"(r)	is the way to favor DirectPath µ-code;
+ * "g"(0)		let the compiler to decide where does it
+ *			want to keep the value of zero;
+ */
+#define mul_add(r, a, word, carry)                                     \
+  do {                                                                 \
+    register BN_ULONG high, low;                                       \
+    asm("mulq %3" : "=a"(low), "=d"(high) : "a"(word), "m"(a) : "cc"); \
+    asm("addq %2,%0; adcq %3,%1"                                       \
+        : "+r"(carry), "+d"(high)                                      \
+        : "a"(low), "g"(0)                                             \
+        : "cc");                                                       \
+    asm("addq %2,%0; adcq %3,%1"                                       \
+        : "+m"(r), "+d"(high)                                          \
+        : "r"(carry), "g"(0)                                           \
+        : "cc");                                                       \
+    carry = high;                                                      \
+  } while (0)
+
+#define mul(r, a, word, carry)                                         \
+  do {                                                                 \
+    register BN_ULONG high, low;                                       \
+    asm("mulq %3" : "=a"(low), "=d"(high) : "a"(word), "g"(a) : "cc"); \
+    asm("addq %2,%0; adcq %3,%1"                                       \
+        : "+r"(carry), "+d"(high)                                      \
+        : "a"(low), "g"(0)                                             \
+        : "cc");                                                       \
+    (r) = carry, carry = high;                                         \
+  } while (0)
+#undef sqr
+#define sqr(r0, r1, a) asm("mulq %2" : "=a"(r0), "=d"(r1) : "a"(a) : "cc");
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w) {
+  BN_ULONG c1 = 0;
+
+  if (num <= 0)
+    return (c1);
+
+  while (num & ~3) {
+    mul_add(rp[0], ap[0], w, c1);
+    mul_add(rp[1], ap[1], w, c1);
+    mul_add(rp[2], ap[2], w, c1);
+    mul_add(rp[3], ap[3], w, c1);
+    ap += 4;
+    rp += 4;
+    num -= 4;
+  }
+  if (num) {
+    mul_add(rp[0], ap[0], w, c1);
+    if (--num == 0)
+      return c1;
+    mul_add(rp[1], ap[1], w, c1);
+    if (--num == 0)
+      return c1;
+    mul_add(rp[2], ap[2], w, c1);
+    return c1;
+  }
+
+  return (c1);
+}
+
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w) {
+  BN_ULONG c1 = 0;
+
+  if (num <= 0)
+    return (c1);
+
+  while (num & ~3) {
+    mul(rp[0], ap[0], w, c1);
+    mul(rp[1], ap[1], w, c1);
+    mul(rp[2], ap[2], w, c1);
+    mul(rp[3], ap[3], w, c1);
+    ap += 4;
+    rp += 4;
+    num -= 4;
+  }
+  if (num) {
+    mul(rp[0], ap[0], w, c1);
+    if (--num == 0)
+      return c1;
+    mul(rp[1], ap[1], w, c1);
+    if (--num == 0)
+      return c1;
+    mul(rp[2], ap[2], w, c1);
+  }
+  return (c1);
+}
+
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n) {
+  if (n <= 0)
+    return;
+
+  while (n & ~3) {
+    sqr(r[0], r[1], a[0]);
+    sqr(r[2], r[3], a[1]);
+    sqr(r[4], r[5], a[2]);
+    sqr(r[6], r[7], a[3]);
+    a += 4;
+    r += 8;
+    n -= 4;
+  }
+  if (n) {
+    sqr(r[0], r[1], a[0]);
+    if (--n == 0)
+      return;
+    sqr(r[2], r[3], a[1]);
+    if (--n == 0)
+      return;
+    sqr(r[4], r[5], a[2]);
+  }
+}
+
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) {
+  BN_ULONG ret, waste;
+
+  asm("divq	%4" : "=a"(ret), "=d"(waste) : "a"(l), "d"(h), "g"(d) : "cc");
+
+  return ret;
+}
+
+BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                      int n) {
+  BN_ULONG ret;
+  size_t i = 0;
+
+  if (n <= 0)
+    return 0;
+
+  asm("	subq	%0,%0		\n" /* clear carry */
+      "	jmp	1f		\n"
+      ".p2align 4			\n"
+      "1:	movq	(%4,%2,8),%0	\n"
+      "	adcq	(%5,%2,8),%0	\n"
+      "	movq	%0,(%3,%2,8)	\n"
+      "	lea	1(%2),%2	\n"
+      "	loop	1b		\n"
+      "	sbbq	%0,%0		\n"
+      : "=&r"(ret), "+c"(n), "+r"(i)
+      : "r"(rp), "r"(ap), "r"(bp)
+      : "cc");
+
+  return ret & 1;
+}
+
+#ifndef SIMICS
+BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                      int n) {
+  BN_ULONG ret;
+  size_t i = 0;
+
+  if (n <= 0)
+    return 0;
+
+  asm("	subq	%0,%0		\n" /* clear borrow */
+      "	jmp	1f		\n"
+      ".p2align 4			\n"
+      "1:	movq	(%4,%2,8),%0	\n"
+      "	sbbq	(%5,%2,8),%0	\n"
+      "	movq	%0,(%3,%2,8)	\n"
+      "	lea	1(%2),%2	\n"
+      "	loop	1b		\n"
+      "	sbbq	%0,%0		\n"
+      : "=&r"(ret), "+c"(n), "+r"(i)
+      : "r"(rp), "r"(ap), "r"(bp)
+      : "cc");
+
+  return ret & 1;
+}
+#else
+/* Simics 1.4<7 has buggy sbbq:-( */
+#define BN_MASK2 0xffffffffffffffffL
+BN_ULONG bn_sub_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n) {
+  BN_ULONG t1, t2;
+  int c = 0;
+
+  if (n <= 0)
+    return ((BN_ULONG)0);
+
+  for (;;) {
+    t1 = a[0];
+    t2 = b[0];
+    r[0] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    if (--n <= 0)
+      break;
+
+    t1 = a[1];
+    t2 = b[1];
+    r[1] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    if (--n <= 0)
+      break;
+
+    t1 = a[2];
+    t2 = b[2];
+    r[2] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    if (--n <= 0)
+      break;
+
+    t1 = a[3];
+    t2 = b[3];
+    r[3] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    if (--n <= 0)
+      break;
+
+    a += 4;
+    b += 4;
+    r += 4;
+  }
+  return (c);
+}
+#endif
+
+/* mul_add_c(a,b,c0,c1,c2)  -- c+=a*b for three word number c=(c2,c1,c0) */
+/* mul_add_c2(a,b,c0,c1,c2) -- c+=2*a*b for three word number c=(c2,c1,c0) */
+/* sqr_add_c(a,i,c0,c1,c2)  -- c+=a[i]^2 for three word number c=(c2,c1,c0) */
+/* sqr_add_c2(a,i,c0,c1,c2) -- c+=2*a[i]*a[j] for three word number c=(c2,c1,c0)
+ */
+
+#if 0
+/* original macros are kept for reference purposes */
+#define mul_add_c(a, b, c0, c1, c2) \
+  {                                 \
+    BN_ULONG ta = (a), tb = (b);    \
+    t1 = ta * tb;                   \
+    t2 = BN_UMULT_HIGH(ta, tb);     \
+    c0 += t1;                       \
+    t2 += (c0 < t1) ? 1 : 0;        \
+    c1 += t2;                       \
+    c2 += (c1 < t2) ? 1 : 0;        \
+  }
+
+#define mul_add_c2(a, b, c0, c1, c2) \
+  {                                  \
+    BN_ULONG ta = (a), tb = (b), t0; \
+    t1 = BN_UMULT_HIGH(ta, tb);      \
+    t0 = ta * tb;                    \
+    t2 = t1 + t1;                    \
+    c2 += (t2 < t1) ? 1 : 0;         \
+    t1 = t0 + t0;                    \
+    t2 += (t1 < t0) ? 1 : 0;         \
+    c0 += t1;                        \
+    t2 += (c0 < t1) ? 1 : 0;         \
+    c1 += t2;                        \
+    c2 += (c1 < t2) ? 1 : 0;         \
+  }
+#else
+#define mul_add_c(a, b, c0, c1, c2)                              \
+  do {                                                           \
+    asm("mulq %3" : "=a"(t1), "=d"(t2) : "a"(a), "m"(b) : "cc"); \
+    asm("addq %2,%0; adcq %3,%1"                                 \
+        : "+r"(c0), "+d"(t2)                                     \
+        : "a"(t1), "g"(0)                                        \
+        : "cc");                                                 \
+    asm("addq %2,%0; adcq %3,%1"                                 \
+        : "+r"(c1), "+r"(c2)                                     \
+        : "d"(t2), "g"(0)                                        \
+        : "cc");                                                 \
+  } while (0)
+
+#define sqr_add_c(a, i, c0, c1, c2)                         \
+  do {                                                      \
+    asm("mulq %2" : "=a"(t1), "=d"(t2) : "a"(a[i]) : "cc"); \
+    asm("addq %2,%0; adcq %3,%1"                            \
+        : "+r"(c0), "+d"(t2)                                \
+        : "a"(t1), "g"(0)                                   \
+        : "cc");                                            \
+    asm("addq %2,%0; adcq %3,%1"                            \
+        : "+r"(c1), "+r"(c2)                                \
+        : "d"(t2), "g"(0)                                   \
+        : "cc");                                            \
+  } while (0)
+
+#define mul_add_c2(a, b, c0, c1, c2)                                    \
+  do {                                                                  \
+    asm("mulq %3" : "=a"(t1), "=d"(t2) : "a"(a), "m"(b) : "cc");        \
+    asm("addq %0,%0; adcq %2,%1" : "+d"(t2), "+r"(c2) : "g"(0) : "cc"); \
+    asm("addq %0,%0; adcq %2,%1" : "+a"(t1), "+d"(t2) : "g"(0) : "cc"); \
+    asm("addq %2,%0; adcq %3,%1"                                        \
+        : "+r"(c0), "+d"(t2)                                            \
+        : "a"(t1), "g"(0)                                               \
+        : "cc");                                                        \
+    asm("addq %2,%0; adcq %3,%1"                                        \
+        : "+r"(c1), "+r"(c2)                                            \
+        : "d"(t2), "g"(0)                                               \
+        : "cc");                                                        \
+  } while (0)
+#endif
+
+#define sqr_add_c2(a, i, j, c0, c1, c2) mul_add_c2((a)[i], (a)[j], c0, c1, c2)
+
+void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b) {
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  mul_add_c(a[0], b[0], c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  mul_add_c(a[0], b[1], c2, c3, c1);
+  mul_add_c(a[1], b[0], c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  mul_add_c(a[2], b[0], c3, c1, c2);
+  mul_add_c(a[1], b[1], c3, c1, c2);
+  mul_add_c(a[0], b[2], c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  mul_add_c(a[0], b[3], c1, c2, c3);
+  mul_add_c(a[1], b[2], c1, c2, c3);
+  mul_add_c(a[2], b[1], c1, c2, c3);
+  mul_add_c(a[3], b[0], c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  mul_add_c(a[4], b[0], c2, c3, c1);
+  mul_add_c(a[3], b[1], c2, c3, c1);
+  mul_add_c(a[2], b[2], c2, c3, c1);
+  mul_add_c(a[1], b[3], c2, c3, c1);
+  mul_add_c(a[0], b[4], c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  mul_add_c(a[0], b[5], c3, c1, c2);
+  mul_add_c(a[1], b[4], c3, c1, c2);
+  mul_add_c(a[2], b[3], c3, c1, c2);
+  mul_add_c(a[3], b[2], c3, c1, c2);
+  mul_add_c(a[4], b[1], c3, c1, c2);
+  mul_add_c(a[5], b[0], c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  mul_add_c(a[6], b[0], c1, c2, c3);
+  mul_add_c(a[5], b[1], c1, c2, c3);
+  mul_add_c(a[4], b[2], c1, c2, c3);
+  mul_add_c(a[3], b[3], c1, c2, c3);
+  mul_add_c(a[2], b[4], c1, c2, c3);
+  mul_add_c(a[1], b[5], c1, c2, c3);
+  mul_add_c(a[0], b[6], c1, c2, c3);
+  r[6] = c1;
+  c1 = 0;
+  mul_add_c(a[0], b[7], c2, c3, c1);
+  mul_add_c(a[1], b[6], c2, c3, c1);
+  mul_add_c(a[2], b[5], c2, c3, c1);
+  mul_add_c(a[3], b[4], c2, c3, c1);
+  mul_add_c(a[4], b[3], c2, c3, c1);
+  mul_add_c(a[5], b[2], c2, c3, c1);
+  mul_add_c(a[6], b[1], c2, c3, c1);
+  mul_add_c(a[7], b[0], c2, c3, c1);
+  r[7] = c2;
+  c2 = 0;
+  mul_add_c(a[7], b[1], c3, c1, c2);
+  mul_add_c(a[6], b[2], c3, c1, c2);
+  mul_add_c(a[5], b[3], c3, c1, c2);
+  mul_add_c(a[4], b[4], c3, c1, c2);
+  mul_add_c(a[3], b[5], c3, c1, c2);
+  mul_add_c(a[2], b[6], c3, c1, c2);
+  mul_add_c(a[1], b[7], c3, c1, c2);
+  r[8] = c3;
+  c3 = 0;
+  mul_add_c(a[2], b[7], c1, c2, c3);
+  mul_add_c(a[3], b[6], c1, c2, c3);
+  mul_add_c(a[4], b[5], c1, c2, c3);
+  mul_add_c(a[5], b[4], c1, c2, c3);
+  mul_add_c(a[6], b[3], c1, c2, c3);
+  mul_add_c(a[7], b[2], c1, c2, c3);
+  r[9] = c1;
+  c1 = 0;
+  mul_add_c(a[7], b[3], c2, c3, c1);
+  mul_add_c(a[6], b[4], c2, c3, c1);
+  mul_add_c(a[5], b[5], c2, c3, c1);
+  mul_add_c(a[4], b[6], c2, c3, c1);
+  mul_add_c(a[3], b[7], c2, c3, c1);
+  r[10] = c2;
+  c2 = 0;
+  mul_add_c(a[4], b[7], c3, c1, c2);
+  mul_add_c(a[5], b[6], c3, c1, c2);
+  mul_add_c(a[6], b[5], c3, c1, c2);
+  mul_add_c(a[7], b[4], c3, c1, c2);
+  r[11] = c3;
+  c3 = 0;
+  mul_add_c(a[7], b[5], c1, c2, c3);
+  mul_add_c(a[6], b[6], c1, c2, c3);
+  mul_add_c(a[5], b[7], c1, c2, c3);
+  r[12] = c1;
+  c1 = 0;
+  mul_add_c(a[6], b[7], c2, c3, c1);
+  mul_add_c(a[7], b[6], c2, c3, c1);
+  r[13] = c2;
+  c2 = 0;
+  mul_add_c(a[7], b[7], c3, c1, c2);
+  r[14] = c3;
+  r[15] = c1;
+}
+
+void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b) {
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  mul_add_c(a[0], b[0], c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  mul_add_c(a[0], b[1], c2, c3, c1);
+  mul_add_c(a[1], b[0], c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  mul_add_c(a[2], b[0], c3, c1, c2);
+  mul_add_c(a[1], b[1], c3, c1, c2);
+  mul_add_c(a[0], b[2], c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  mul_add_c(a[0], b[3], c1, c2, c3);
+  mul_add_c(a[1], b[2], c1, c2, c3);
+  mul_add_c(a[2], b[1], c1, c2, c3);
+  mul_add_c(a[3], b[0], c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  mul_add_c(a[3], b[1], c2, c3, c1);
+  mul_add_c(a[2], b[2], c2, c3, c1);
+  mul_add_c(a[1], b[3], c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  mul_add_c(a[2], b[3], c3, c1, c2);
+  mul_add_c(a[3], b[2], c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  mul_add_c(a[3], b[3], c1, c2, c3);
+  r[6] = c1;
+  r[7] = c2;
+}
+
+void bn_sqr_comba8(BN_ULONG *r, const BN_ULONG *a) {
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  sqr_add_c(a, 0, c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 1, 0, c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  sqr_add_c(a, 1, c3, c1, c2);
+  sqr_add_c2(a, 2, 0, c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  sqr_add_c2(a, 3, 0, c1, c2, c3);
+  sqr_add_c2(a, 2, 1, c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  sqr_add_c(a, 2, c2, c3, c1);
+  sqr_add_c2(a, 3, 1, c2, c3, c1);
+  sqr_add_c2(a, 4, 0, c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  sqr_add_c2(a, 5, 0, c3, c1, c2);
+  sqr_add_c2(a, 4, 1, c3, c1, c2);
+  sqr_add_c2(a, 3, 2, c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  sqr_add_c(a, 3, c1, c2, c3);
+  sqr_add_c2(a, 4, 2, c1, c2, c3);
+  sqr_add_c2(a, 5, 1, c1, c2, c3);
+  sqr_add_c2(a, 6, 0, c1, c2, c3);
+  r[6] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 7, 0, c2, c3, c1);
+  sqr_add_c2(a, 6, 1, c2, c3, c1);
+  sqr_add_c2(a, 5, 2, c2, c3, c1);
+  sqr_add_c2(a, 4, 3, c2, c3, c1);
+  r[7] = c2;
+  c2 = 0;
+  sqr_add_c(a, 4, c3, c1, c2);
+  sqr_add_c2(a, 5, 3, c3, c1, c2);
+  sqr_add_c2(a, 6, 2, c3, c1, c2);
+  sqr_add_c2(a, 7, 1, c3, c1, c2);
+  r[8] = c3;
+  c3 = 0;
+  sqr_add_c2(a, 7, 2, c1, c2, c3);
+  sqr_add_c2(a, 6, 3, c1, c2, c3);
+  sqr_add_c2(a, 5, 4, c1, c2, c3);
+  r[9] = c1;
+  c1 = 0;
+  sqr_add_c(a, 5, c2, c3, c1);
+  sqr_add_c2(a, 6, 4, c2, c3, c1);
+  sqr_add_c2(a, 7, 3, c2, c3, c1);
+  r[10] = c2;
+  c2 = 0;
+  sqr_add_c2(a, 7, 4, c3, c1, c2);
+  sqr_add_c2(a, 6, 5, c3, c1, c2);
+  r[11] = c3;
+  c3 = 0;
+  sqr_add_c(a, 6, c1, c2, c3);
+  sqr_add_c2(a, 7, 5, c1, c2, c3);
+  r[12] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 7, 6, c2, c3, c1);
+  r[13] = c2;
+  c2 = 0;
+  sqr_add_c(a, 7, c3, c1, c2);
+  r[14] = c3;
+  r[15] = c1;
+}
+
+void bn_sqr_comba4(BN_ULONG *r, const BN_ULONG *a) {
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  sqr_add_c(a, 0, c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 1, 0, c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  sqr_add_c(a, 1, c3, c1, c2);
+  sqr_add_c2(a, 2, 0, c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  sqr_add_c2(a, 3, 0, c1, c2, c3);
+  sqr_add_c2(a, 2, 1, c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  sqr_add_c(a, 2, c2, c3, c1);
+  sqr_add_c2(a, 3, 1, c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  sqr_add_c2(a, 3, 2, c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  sqr_add_c(a, 3, c1, c2, c3);
+  r[6] = c1;
+  r[7] = c2;
+}
+
+#endif  /* defined(OPENSSL_X86_64) && !defined(OPENSSL_WINDOWS) */
diff --git a/crypto/bn/asm/x86_64-mont.pl b/crypto/bn/asm/x86_64-mont.pl
new file mode 100644
index 0000000..3803928
--- /dev/null
+++ b/crypto/bn/asm/x86_64-mont.pl
@@ -0,0 +1,1400 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# October 2005.
+#
+# Montgomery multiplication routine for x86_64. While it gives modest
+# 9% improvement of rsa4096 sign on Opteron, rsa512 sign runs more
+# than twice, >2x, as fast. Most common rsa1024 sign is improved by
+# respectful 50%. It remains to be seen if loop unrolling and
+# dedicated squaring routine can provide further improvement...
+
+# July 2011.
+#
+# Add dedicated squaring procedure. Performance improvement varies
+# from platform to platform, but in average it's ~5%/15%/25%/33%
+# for 512-/1024-/2048-/4096-bit RSA *sign* benchmarks respectively.
+
+# August 2011.
+#
+# Unroll and modulo-schedule inner loops in such manner that they
+# are "fallen through" for input lengths of 8, which is critical for
+# 1024-bit RSA *sign*. Average performance improvement in comparison
+# to *initial* version of this module from 2005 is ~0%/30%/40%/45%
+# for 512-/1024-/2048-/4096-bit RSA *sign* benchmarks respectively.
+
+# June 2013.
+#
+# Optimize reduction in squaring procedure and improve 1024+-bit RSA
+# sign performance by 10-16% on Intel Sandy Bridge and later
+# (virtually same on non-Intel processors).
+
+# August 2013.
+#
+# Add MULX/ADOX/ADCX code path.
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$addx = ($1>=11);
+}
+
+# int bn_mul_mont(
+$rp="%rdi";	# BN_ULONG *rp,
+$ap="%rsi";	# const BN_ULONG *ap,
+$bp="%rdx";	# const BN_ULONG *bp,
+$np="%rcx";	# const BN_ULONG *np,
+$n0="%r8";	# const BN_ULONG *n0,
+$num="%r9";	# int num);
+$lo0="%r10";
+$hi0="%r11";
+$hi1="%r13";
+$i="%r14";
+$j="%r15";
+$m0="%rbx";
+$m1="%rbp";
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	bn_mul_mont
+.type	bn_mul_mont,\@function,6
+.align	16
+bn_mul_mont:
+	test	\$3,${num}d
+	jnz	.Lmul_enter
+	cmp	\$8,${num}d
+	jb	.Lmul_enter
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+___
+$code.=<<___;
+	cmp	$ap,$bp
+	jne	.Lmul4x_enter
+	test	\$7,${num}d
+	jz	.Lsqr8x_enter
+	jmp	.Lmul4x_enter
+
+.align	16
+.Lmul_enter:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	${num}d,${num}d
+	lea	2($num),%r10
+	mov	%rsp,%r11
+	neg	%r10
+	lea	(%rsp,%r10,8),%rsp	# tp=alloca(8*(num+2))
+	and	\$-1024,%rsp		# minimize TLB usage
+
+	mov	%r11,8(%rsp,$num,8)	# tp[num+1]=%rsp
+.Lmul_body:
+	mov	$bp,%r12		# reassign $bp
+___
+		$bp="%r12";
+$code.=<<___;
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($bp),$m0		# m0=bp[0]
+	mov	($ap),%rax
+
+	xor	$i,$i			# i=0
+	xor	$j,$j			# j=0
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$lo0
+	mov	($np),%rax
+
+	imulq	$lo0,$m1		# "tp[0]"*n0
+	mov	%rdx,$hi0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.L1st_enter
+
+.align	16
+.L1st:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	mov	$lo0,$hi0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.L1st_enter:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	lea	1($j),$j		# j++
+	mov	%rdx,$lo0
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.L1st
+
+	add	%rax,$hi1
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+	mov	$lo0,$hi0
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	jmp	.Louter
+.align	16
+.Louter:
+	mov	($bp,$i,8),$m0		# m0=bp[i]
+	xor	$j,$j			# j=0
+	mov	$n0,$m1
+	mov	(%rsp),$lo0
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$lo0		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	imulq	$lo0,$m1		# tp[0]*n0
+	mov	%rdx,$hi0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	8(%rsp),$lo0		# tp[1]
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.Linner_enter
+
+.align	16
+.Linner:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.Linner_enter:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$lo0		# ap[j]*bp[i]+tp[j]
+	mov	%rdx,$hi0
+	adc	\$0,$hi0
+	lea	1($j),$j		# j++
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.Linner
+
+	add	%rax,$hi1
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# pull upmost overflow bit
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	cmp	$num,$i
+	jb	.Louter
+
+	xor	$i,$i			# i=0 and clear CF!
+	mov	(%rsp),%rax		# tp[0]
+	lea	(%rsp),$ap		# borrow ap for tp
+	mov	$num,$j			# j=num
+	jmp	.Lsub
+.align	16
+.Lsub:	sbb	($np,$i,8),%rax
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]-np[i]
+	mov	8($ap,$i,8),%rax	# tp[i+1]
+	lea	1($i),$i		# i++
+	dec	$j			# doesnn't affect CF!
+	jnz	.Lsub
+
+	sbb	\$0,%rax		# handle upmost overflow bit
+	xor	$i,$i
+	and	%rax,$ap
+	not	%rax
+	mov	$rp,$np
+	and	%rax,$np
+	mov	$num,$j			# j=num
+	or	$np,$ap			# ap=borrow?tp:rp
+.align	16
+.Lcopy:					# copy or in-place refresh
+	mov	($ap,$i,8),%rax
+	mov	$i,(%rsp,$i,8)		# zap temporary vector
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]
+	lea	1($i),$i
+	sub	\$1,$j
+	jnz	.Lcopy
+
+	mov	8(%rsp,$num,8),%rsi	# restore %rsp
+	mov	\$1,%rax
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lmul_epilogue:
+	ret
+.size	bn_mul_mont,.-bn_mul_mont
+___
+{{{
+my @A=("%r10","%r11");
+my @N=("%r13","%rdi");
+$code.=<<___;
+.type	bn_mul4x_mont,\@function,6
+.align	16
+bn_mul4x_mont:
+.Lmul4x_enter:
+___
+$code.=<<___ if ($addx);
+	and	\$0x80100,%r11d
+	cmp	\$0x80100,%r11d
+	je	.Lmulx4x_enter
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	${num}d,${num}d
+	lea	4($num),%r10
+	mov	%rsp,%r11
+	neg	%r10
+	lea	(%rsp,%r10,8),%rsp	# tp=alloca(8*(num+4))
+	and	\$-1024,%rsp		# minimize TLB usage
+
+	mov	%r11,8(%rsp,$num,8)	# tp[num+1]=%rsp
+.Lmul4x_body:
+	mov	$rp,16(%rsp,$num,8)	# tp[num+2]=$rp
+	mov	%rdx,%r12		# reassign $bp
+___
+		$bp="%r12";
+$code.=<<___;
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($bp),$m0		# m0=bp[0]
+	mov	($ap),%rax
+
+	xor	$i,$i			# i=0
+	xor	$j,$j			# j=0
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$A[0]
+	mov	($np),%rax
+
+	imulq	$A[0],$m1		# "tp[0]"*n0
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0
+	add	%rax,$A[1]
+	mov	8($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1
+	add	%rax,$N[1]
+	mov	16($ap),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	lea	4($j),$j		# j++
+	adc	\$0,%rdx
+	mov	$N[1],(%rsp)
+	mov	%rdx,$N[0]
+	jmp	.L1st4x
+.align	16
+.L1st4x:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-8(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	8($np,$j,8),%rax
+	adc	\$0,%rdx
+	lea	4($j),$j		# j++
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	-16($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-32(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+	cmp	$num,$j
+	jb	.L1st4x
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	mov	$N[0],-8(%rsp,$j,8)
+	mov	$N[1],(%rsp,$j,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+.align	4
+.Louter4x:
+	mov	($bp,$i,8),$m0		# m0=bp[i]
+	xor	$j,$j			# j=0
+	mov	(%rsp),$A[0]
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$A[0]		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	imulq	$A[0],$m1		# tp[0]*n0
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# "$N[0]", discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	8($np),%rax
+	adc	\$0,%rdx
+	add	8(%rsp),$A[1]		# +tp[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	lea	4($j),$j		# j+=2
+	adc	\$0,%rdx
+	mov	$N[1],(%rsp)		# tp[j-1]
+	mov	%rdx,$N[0]
+	jmp	.Linner4x
+.align	16
+.Linner4x:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-16(%rsp,$j,8),$A[0]	# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-8(%rsp,$j,8),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	(%rsp,$j,8),$A[0]	# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[0],-8(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	8($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	8(%rsp,$j,8),$A[1]
+	adc	\$0,%rdx
+	lea	4($j),$j		# j++
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	-16($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[1],-32(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+	cmp	$num,$j
+	jb	.Linner4x
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-16(%rsp,$j,8),$A[0]	# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-8(%rsp,$j,8),$A[1]
+	adc	\$0,%rdx
+	lea	1($i),$i		# i++
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	add	(%rsp,$num,8),$N[0]	# pull upmost overflow bit
+	adc	\$0,$N[1]
+	mov	$N[0],-8(%rsp,$j,8)
+	mov	$N[1],(%rsp,$j,8)	# store upmost overflow bit
+
+	cmp	$num,$i
+	jb	.Louter4x
+___
+{
+my @ri=("%rax","%rdx",$m0,$m1);
+$code.=<<___;
+	mov	16(%rsp,$num,8),$rp	# restore $rp
+	mov	0(%rsp),@ri[0]		# tp[0]
+	pxor	%xmm0,%xmm0
+	mov	8(%rsp),@ri[1]		# tp[1]
+	shr	\$2,$num		# num/=4
+	lea	(%rsp),$ap		# borrow ap for tp
+	xor	$i,$i			# i=0 and clear CF!
+
+	sub	0($np),@ri[0]
+	mov	16($ap),@ri[2]		# tp[2]
+	mov	24($ap),@ri[3]		# tp[3]
+	sbb	8($np),@ri[1]
+	lea	-1($num),$j		# j=num/4-1
+	jmp	.Lsub4x
+.align	16
+.Lsub4x:
+	mov	@ri[0],0($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	mov	@ri[1],8($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	sbb	16($np,$i,8),@ri[2]
+	mov	32($ap,$i,8),@ri[0]	# tp[i+1]
+	mov	40($ap,$i,8),@ri[1]
+	sbb	24($np,$i,8),@ri[3]
+	mov	@ri[2],16($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	mov	@ri[3],24($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	sbb	32($np,$i,8),@ri[0]
+	mov	48($ap,$i,8),@ri[2]
+	mov	56($ap,$i,8),@ri[3]
+	sbb	40($np,$i,8),@ri[1]
+	lea	4($i),$i		# i++
+	dec	$j			# doesnn't affect CF!
+	jnz	.Lsub4x
+
+	mov	@ri[0],0($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	mov	32($ap,$i,8),@ri[0]	# load overflow bit
+	sbb	16($np,$i,8),@ri[2]
+	mov	@ri[1],8($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	sbb	24($np,$i,8),@ri[3]
+	mov	@ri[2],16($rp,$i,8)	# rp[i]=tp[i]-np[i]
+
+	sbb	\$0,@ri[0]		# handle upmost overflow bit
+	mov	@ri[3],24($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	xor	$i,$i			# i=0
+	and	@ri[0],$ap
+	not	@ri[0]
+	mov	$rp,$np
+	and	@ri[0],$np
+	lea	-1($num),$j
+	or	$np,$ap			# ap=borrow?tp:rp
+
+	movdqu	($ap),%xmm1
+	movdqa	%xmm0,(%rsp)
+	movdqu	%xmm1,($rp)
+	jmp	.Lcopy4x
+.align	16
+.Lcopy4x:					# copy or in-place refresh
+	movdqu	16($ap,$i),%xmm2
+	movdqu	32($ap,$i),%xmm1
+	movdqa	%xmm0,16(%rsp,$i)
+	movdqu	%xmm2,16($rp,$i)
+	movdqa	%xmm0,32(%rsp,$i)
+	movdqu	%xmm1,32($rp,$i)
+	lea	32($i),$i
+	dec	$j
+	jnz	.Lcopy4x
+
+	shl	\$2,$num
+	movdqu	16($ap,$i),%xmm2
+	movdqa	%xmm0,16(%rsp,$i)
+	movdqu	%xmm2,16($rp,$i)
+___
+}
+$code.=<<___;
+	mov	8(%rsp,$num,8),%rsi	# restore %rsp
+	mov	\$1,%rax
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lmul4x_epilogue:
+	ret
+.size	bn_mul4x_mont,.-bn_mul4x_mont
+___
+}}}
+{{{
+######################################################################
+# void bn_sqr8x_mont(
+my $rptr="%rdi";	# const BN_ULONG *rptr,
+my $aptr="%rsi";	# const BN_ULONG *aptr,
+my $bptr="%rdx";	# not used
+my $nptr="%rcx";	# const BN_ULONG *nptr,
+my $n0  ="%r8";		# const BN_ULONG *n0);
+my $num ="%r9";		# int num, has to be divisible by 8
+
+my ($i,$j,$tptr)=("%rbp","%rcx",$rptr);
+my @A0=("%r10","%r11");
+my @A1=("%r12","%r13");
+my ($a0,$a1,$ai)=("%r14","%r15","%rbx");
+
+$code.=<<___;
+.extern	bn_sqr8x_internal		# see x86_64-mont5 module
+.extern	bn_sqrx8x_internal		# see x86_64-mont5 module
+
+.type	bn_sqr8x_mont,\@function,6
+.align	32
+bn_sqr8x_mont:
+.Lsqr8x_enter:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	${num}d,%r10d
+	shl	\$3,${num}d		# convert $num to bytes
+	shl	\$3+2,%r10		# 4*$num
+	neg	$num
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr modulo
+	# 4096. this is done to allow memory disambiguation logic
+	# do its job.
+	#
+	lea	-64(%rsp,$num,4),%r11
+	mov	($n0),$n0		# *n0
+	sub	$aptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lsqr8x_sp_alt
+	sub	%r11,%rsp		# align with $aptr
+	lea	-64(%rsp,$num,4),%rsp	# alloca(frame+4*$num)
+	jmp	.Lsqr8x_sp_done
+
+.align	32
+.Lsqr8x_sp_alt:
+	lea	4096-64(,$num,4),%r10	# 4096-frame-4*$num
+	lea	-64(%rsp,$num,4),%rsp	# alloca(frame+4*$num)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rsp
+.Lsqr8x_sp_done:
+	and	\$-64,%rsp
+	mov	$num,%r10	
+	neg	$num
+
+	lea	64(%rsp,$num,2),%r11	# copy of modulus
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lsqr8x_body:
+
+	mov	$num,$i
+	movq	%r11, %xmm2		# save pointer to modulus copy
+	shr	\$3+2,$i
+	mov	OPENSSL_ia32cap_P+8(%rip),%eax
+	jmp	.Lsqr8x_copy_n
+
+.align	32
+.Lsqr8x_copy_n:
+	movq	8*0($nptr),%xmm0
+	movq	8*1($nptr),%xmm1
+	movq	8*2($nptr),%xmm3
+	movq	8*3($nptr),%xmm4
+	lea	8*4($nptr),$nptr
+	movdqa	%xmm0,16*0(%r11)
+	movdqa	%xmm1,16*1(%r11)
+	movdqa	%xmm3,16*2(%r11)
+	movdqa	%xmm4,16*3(%r11)
+	lea	16*4(%r11),%r11
+	dec	$i
+	jnz	.Lsqr8x_copy_n
+
+	pxor	%xmm0,%xmm0
+	movq	$rptr,%xmm1		# save $rptr
+	movq	%r10, %xmm3		# -$num
+___
+$code.=<<___ if ($addx);
+	and	\$0x80100,%eax
+	cmp	\$0x80100,%eax
+	jne	.Lsqr8x_nox
+
+	call	bn_sqrx8x_internal	# see x86_64-mont5 module
+
+	pxor	%xmm0,%xmm0
+	lea	48(%rsp),%rax
+	lea	64(%rsp,$num,2),%rdx
+	shr	\$3+2,$num
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lsqr8x_zero
+
+.align	32
+.Lsqr8x_nox:
+___
+$code.=<<___;
+	call	bn_sqr8x_internal	# see x86_64-mont5 module
+
+	pxor	%xmm0,%xmm0
+	lea	48(%rsp),%rax
+	lea	64(%rsp,$num,2),%rdx
+	shr	\$3+2,$num
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lsqr8x_zero
+
+.align	32
+.Lsqr8x_zero:
+	movdqa	%xmm0,16*0(%rax)	# wipe t
+	movdqa	%xmm0,16*1(%rax)
+	movdqa	%xmm0,16*2(%rax)
+	movdqa	%xmm0,16*3(%rax)
+	lea	16*4(%rax),%rax
+	movdqa	%xmm0,16*0(%rdx)	# wipe n
+	movdqa	%xmm0,16*1(%rdx)
+	movdqa	%xmm0,16*2(%rdx)
+	movdqa	%xmm0,16*3(%rdx)
+	lea	16*4(%rdx),%rdx
+	dec	$num
+	jnz	.Lsqr8x_zero
+
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lsqr8x_epilogue:
+	ret
+.size	bn_sqr8x_mont,.-bn_sqr8x_mont
+___
+}}}
+
+if ($addx) {{{
+my $bp="%rdx";	# original value
+
+$code.=<<___;
+.type	bn_mulx4x_mont,\@function,6
+.align	32
+bn_mulx4x_mont:
+.Lmulx4x_enter:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	shl	\$3,${num}d		# convert $num to bytes
+	.byte	0x67
+	xor	%r10,%r10
+	sub	$num,%r10		# -$num
+	mov	($n0),$n0		# *n0
+	lea	-72(%rsp,%r10),%rsp	# alloca(frame+$num+8)
+	lea	($bp,$num),%r10
+	and	\$-128,%rsp
+	##############################################################
+	# Stack layout
+	# +0	num
+	# +8	off-loaded &b[i]
+	# +16	end of b[num]
+	# +24	saved n0
+	# +32	saved rp
+	# +40	saved %rsp
+	# +48	inner counter
+	# +56
+	# +64	tmp[num+1]
+	#
+	mov	$num,0(%rsp)		# save $num
+	shr	\$5,$num
+	mov	%r10,16(%rsp)		# end of b[num]
+	sub	\$1,$num
+	mov	$n0, 24(%rsp)		# save *n0
+	mov	$rp, 32(%rsp)		# save $rp
+	mov	%rax,40(%rsp)		# save original %rsp
+	mov	$num,48(%rsp)		# inner counter
+	jmp	.Lmulx4x_body
+
+.align	32
+.Lmulx4x_body:
+___
+my ($aptr, $bptr, $nptr, $tptr, $mi,  $bi,  $zero, $num)=
+   ("%rsi","%rdi","%rcx","%rbx","%r8","%r9","%rbp","%rax");
+my $rptr=$bptr;
+$code.=<<___;
+	lea	8($bp),$bptr
+	mov	($bp),%rdx		# b[0], $bp==%rdx actually
+	lea	64+32(%rsp),$tptr
+	mov	%rdx,$bi
+
+	mulx	0*8($aptr),$mi,%rax	# a[0]*b[0]
+	mulx	1*8($aptr),%r11,%r14	# a[1]*b[0]
+	add	%rax,%r11
+	mov	$bptr,8(%rsp)		# off-load &b[i]
+	mulx	2*8($aptr),%r12,%r13	# ...
+	adc	%r14,%r12
+	adc	\$0,%r13
+
+	mov	$mi,$bptr		# borrow $bptr
+	imulq	24(%rsp),$mi		# "t[0]"*n0
+	xor	$zero,$zero		# cf=0, of=0
+
+	mulx	3*8($aptr),%rax,%r14
+	 mov	$mi,%rdx
+	lea	4*8($aptr),$aptr
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+
+	mulx	0*8($nptr),%rax,%r10
+	adcx	%rax,$bptr		# discarded
+	adox	%r11,%r10
+	mulx	1*8($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	.byte	0xc4,0x62,0xfb,0xf6,0xa1,0x10,0x00,0x00,0x00	# mulx	2*8($nptr),%rax,%r12
+	mov	48(%rsp),$bptr		# counter value
+	mov	%r10,-4*8($tptr)
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-3*8($tptr)
+	adcx	%rax,%r12
+	adox	$zero,%r15		# of=0
+	lea	4*8($nptr),$nptr
+	mov	%r12,-2*8($tptr)
+
+	jmp	.Lmulx4x_1st
+
+.align	32
+.Lmulx4x_1st:
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[0]
+	adcx	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[0]
+	adcx	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 .byte	0x67,0x67
+	 mov	$mi,%rdx
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+
+	adox	%r15,%r10
+	mulx	0*8($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*8($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*8($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	mov	%r11,-4*8($tptr)
+	adox	%r15,%r13
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	lea	4*8($nptr),$nptr
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_1st
+
+	mov	0(%rsp),$num		# load num
+	mov	8(%rsp),$bptr		# re-load &b[i]
+	adc	$zero,%r15		# modulo-scheduled
+	add	%r15,%r14
+	sbb	%r15,%r15		# top-most carry
+	mov	%r14,-1*8($tptr)
+	jmp	.Lmulx4x_outer
+
+.align	32
+.Lmulx4x_outer:
+	mov	($bptr),%rdx		# b[i]
+	lea	8($bptr),$bptr		# b++
+	sub	$num,$aptr		# rewind $aptr
+	mov	%r15,($tptr)		# save top-most carry
+	lea	64+4*8(%rsp),$tptr
+	sub	$num,$nptr		# rewind $nptr
+
+	mulx	0*8($aptr),$mi,%r11	# a[0]*b[i]
+	xor	%ebp,%ebp		# xor	$zero,$zero	# cf=0, of=0
+	mov	%rdx,$bi
+	mulx	1*8($aptr),%r14,%r12	# a[1]*b[i]
+	adox	-4*8($tptr),$mi
+	adcx	%r14,%r11
+	mulx	2*8($aptr),%r15,%r13	# ...
+	adox	-3*8($tptr),%r11
+	adcx	%r15,%r12
+	adox	$zero,%r12
+	adcx	$zero,%r13
+
+	mov	$bptr,8(%rsp)		# off-load &b[i]
+	.byte	0x67
+	mov	$mi,%r15
+	imulq	24(%rsp),$mi		# "t[0]"*n0
+	xor	%ebp,%ebp		# xor	$zero,$zero	# cf=0, of=0
+
+	mulx	3*8($aptr),%rax,%r14
+	 mov	$mi,%rdx
+	adox	-2*8($tptr),%r12
+	adcx	%rax,%r13
+	adox	-1*8($tptr),%r13
+	adcx	$zero,%r14
+	lea	4*8($aptr),$aptr
+	adox	$zero,%r14
+
+	mulx	0*8($nptr),%rax,%r10
+	adcx	%rax,%r15		# discarded
+	adox	%r11,%r10
+	mulx	1*8($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	mulx	2*8($nptr),%rax,%r12
+	mov	%r10,-4*8($tptr)
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-3*8($tptr)
+	lea	4*8($nptr),$nptr
+	adcx	%rax,%r12
+	adox	$zero,%r15		# of=0
+	mov	48(%rsp),$bptr		# counter value
+	mov	%r12,-2*8($tptr)
+
+	jmp	.Lmulx4x_inner
+
+.align	32
+.Lmulx4x_inner:
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[i]
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	adox	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[i]
+	adcx	0*8($tptr),%r10
+	adox	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	1*8($tptr),%r11
+	adox	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 mov	$mi,%rdx
+	adcx	2*8($tptr),%r12
+	adox	%rax,%r13
+	adcx	3*8($tptr),%r13
+	adox	$zero,%r14		# of=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+	adcx	$zero,%r14		# cf=0
+
+	adox	%r15,%r10
+	mulx	0*8($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*8($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*8($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	adox	%r15,%r13
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-4*8($tptr)
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	lea	4*8($nptr),$nptr
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_inner
+
+	mov	0(%rsp),$num		# load num
+	mov	8(%rsp),$bptr		# re-load &b[i]
+	adc	$zero,%r15		# modulo-scheduled
+	sub	0*8($tptr),$zero	# pull top-most carry
+	adc	%r15,%r14
+	mov	-8($nptr),$mi
+	sbb	%r15,%r15		# top-most carry
+	mov	%r14,-1*8($tptr)
+
+	cmp	16(%rsp),$bptr
+	jne	.Lmulx4x_outer
+
+	sub	%r14,$mi		# compare top-most words
+	sbb	$mi,$mi
+	or	$mi,%r15
+
+	neg	$num
+	xor	%rdx,%rdx
+	mov	32(%rsp),$rptr		# restore rp
+	lea	64(%rsp),$tptr
+
+	pxor	%xmm0,%xmm0
+	mov	0*8($nptr,$num),%r8
+	mov	1*8($nptr,$num),%r9
+	neg	%r8
+	jmp	.Lmulx4x_sub_entry
+
+.align	32
+.Lmulx4x_sub:
+	mov	0*8($nptr,$num),%r8
+	mov	1*8($nptr,$num),%r9
+	not	%r8
+.Lmulx4x_sub_entry:
+	mov	2*8($nptr,$num),%r10
+	not	%r9
+	and	%r15,%r8
+	mov	3*8($nptr,$num),%r11
+	not	%r10
+	and	%r15,%r9
+	not	%r11
+	and	%r15,%r10
+	and	%r15,%r11
+
+	neg	%rdx			# mov %rdx,%cf
+	adc	0*8($tptr),%r8
+	adc	1*8($tptr),%r9
+	movdqa	%xmm0,($tptr)
+	adc	2*8($tptr),%r10
+	adc	3*8($tptr),%r11
+	movdqa	%xmm0,16($tptr)
+	lea	4*8($tptr),$tptr
+	sbb	%rdx,%rdx		# mov %cf,%rdx
+
+	mov	%r8,0*8($rptr)
+	mov	%r9,1*8($rptr)
+	mov	%r10,2*8($rptr)
+	mov	%r11,3*8($rptr)
+	lea	4*8($rptr),$rptr
+
+	add	\$32,$num
+	jnz	.Lmulx4x_sub
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmulx4x_epilogue:
+	ret
+.size	bn_mulx4x_mont,.-bn_mulx4x_mont
+___
+}}}
+$code.=<<___;
+.asciz	"Montgomery Multiplication for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+.align	16
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	mul_handler,\@abi-omnipotent
+.align	16
+mul_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<end of prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	192($context),%r10	# pull $num
+	mov	8(%rax,%r10,8),%rax	# pull saved stack pointer
+	lea	48(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+	jmp	.Lcommon_seh_tail
+.size	mul_handler,.-mul_handler
+
+.type	sqr_handler,\@abi-omnipotent
+.align	16
+sqr_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<.Lsqr_body
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=.Lsqr_epilogue
+	jae	.Lcommon_seh_tail
+
+	mov	40(%rax),%rax		# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	sqr_handler,.-sqr_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_bn_mul_mont
+	.rva	.LSEH_end_bn_mul_mont
+	.rva	.LSEH_info_bn_mul_mont
+
+	.rva	.LSEH_begin_bn_mul4x_mont
+	.rva	.LSEH_end_bn_mul4x_mont
+	.rva	.LSEH_info_bn_mul4x_mont
+
+	.rva	.LSEH_begin_bn_sqr8x_mont
+	.rva	.LSEH_end_bn_sqr8x_mont
+	.rva	.LSEH_info_bn_sqr8x_mont
+___
+$code.=<<___ if ($addx);
+	.rva	.LSEH_begin_bn_mulx4x_mont
+	.rva	.LSEH_end_bn_mulx4x_mont
+	.rva	.LSEH_info_bn_mulx4x_mont
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_bn_mul_mont:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul_body,.Lmul_epilogue	# HandlerData[]
+.LSEH_info_bn_mul4x_mont:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul4x_body,.Lmul4x_epilogue	# HandlerData[]
+.LSEH_info_bn_sqr8x_mont:
+	.byte	9,0,0,0
+	.rva	sqr_handler
+	.rva	.Lsqr8x_body,.Lsqr8x_epilogue	# HandlerData[]
+___
+$code.=<<___ if ($addx);
+.LSEH_info_bn_mulx4x_mont:
+	.byte	9,0,0,0
+	.rva	sqr_handler
+	.rva	.Lmulx4x_body,.Lmulx4x_epilogue	# HandlerData[]
+___
+}
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/x86_64-mont5.pl b/crypto/bn/asm/x86_64-mont5.pl
new file mode 100644
index 0000000..c107df9
--- /dev/null
+++ b/crypto/bn/asm/x86_64-mont5.pl
@@ -0,0 +1,3514 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# August 2011.
+#
+# Companion to x86_64-mont.pl that optimizes cache-timing attack
+# countermeasures. The subroutines are produced by replacing bp[i]
+# references in their x86_64-mont.pl counterparts with cache-neutral
+# references to powers table computed in BN_mod_exp_mont_consttime.
+# In addition subroutine that scatters elements of the powers table
+# is implemented, so that scatter-/gathering can be tuned without
+# bn_exp.c modifications.
+
+# August 2013.
+#
+# Add MULX/AD*X code paths and additional interfaces to optimize for
+# branch prediction unit. For input lengths that are multiples of 8
+# the np argument is not just modulus value, but one interleaved
+# with 0. This is to optimize post-condition...
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$addx = ($1>=11);
+}
+
+# int bn_mul_mont_gather5(
+$rp="%rdi";	# BN_ULONG *rp,
+$ap="%rsi";	# const BN_ULONG *ap,
+$bp="%rdx";	# const BN_ULONG *bp,
+$np="%rcx";	# const BN_ULONG *np,
+$n0="%r8";	# const BN_ULONG *n0,
+$num="%r9";	# int num,
+		# int idx);	# 0 to 2^5-1, "index" in $bp holding
+				# pre-computed powers of a', interlaced
+				# in such manner that b[0] is $bp[idx],
+				# b[1] is [2^5+idx], etc.
+$lo0="%r10";
+$hi0="%r11";
+$hi1="%r13";
+$i="%r14";
+$j="%r15";
+$m0="%rbx";
+$m1="%rbp";
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	bn_mul_mont_gather5
+.type	bn_mul_mont_gather5,\@function,6
+.align	64
+bn_mul_mont_gather5:
+	test	\$7,${num}d
+	jnz	.Lmul_enter
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+___
+$code.=<<___;
+	jmp	.Lmul4x_enter
+
+.align	16
+.Lmul_enter:
+	mov	${num}d,${num}d
+	mov	%rsp,%rax
+	mov	`($win64?56:8)`(%rsp),%r10d	# load 7th argument
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0x28(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	lea	2($num),%r11
+	neg	%r11
+	lea	(%rsp,%r11,8),%rsp	# tp=alloca(8*(num+2))
+	and	\$-1024,%rsp		# minimize TLB usage
+
+	mov	%rax,8(%rsp,$num,8)	# tp[num+1]=%rsp
+.Lmul_body:
+	mov	$bp,%r12		# reassign $bp
+___
+		$bp="%r12";
+		$STRIDE=2**5*8;		# 5 is "window size"
+		$N=$STRIDE/4;		# should match cache line size
+$code.=<<___;
+	mov	%r10,%r11
+	shr	\$`log($N/8)/log(2)`,%r10
+	and	\$`$N/8-1`,%r11
+	not	%r10
+	lea	.Lmagic_masks(%rip),%rax
+	and	\$`2**5/($N/8)-1`,%r10	# 5 is "window size"
+	lea	96($bp,%r11,8),$bp	# pointer within 1st cache line
+	movq	0(%rax,%r10,8),%xmm4	# set of masks denoting which
+	movq	8(%rax,%r10,8),%xmm5	# cache line contains element
+	movq	16(%rax,%r10,8),%xmm6	# denoted by 7th argument
+	movq	24(%rax,%r10,8),%xmm7
+
+	movq	`0*$STRIDE/4-96`($bp),%xmm0
+	movq	`1*$STRIDE/4-96`($bp),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bp),%xmm2
+	pand	%xmm5,%xmm1
+	movq	`3*$STRIDE/4-96`($bp),%xmm3
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	pand	%xmm7,%xmm3
+	por	%xmm2,%xmm0
+	lea	$STRIDE($bp),$bp
+	por	%xmm3,%xmm0
+
+	movq	%xmm0,$m0		# m0=bp[0]
+
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($ap),%rax
+
+	xor	$i,$i			# i=0
+	xor	$j,$j			# j=0
+
+	movq	`0*$STRIDE/4-96`($bp),%xmm0
+	movq	`1*$STRIDE/4-96`($bp),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bp),%xmm2
+	pand	%xmm5,%xmm1
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$lo0
+	mov	($np),%rax
+
+	movq	`3*$STRIDE/4-96`($bp),%xmm3
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	pand	%xmm7,%xmm3
+
+	imulq	$lo0,$m1		# "tp[0]"*n0
+	mov	%rdx,$hi0
+
+	por	%xmm2,%xmm0
+	lea	$STRIDE($bp),$bp
+	por	%xmm3,%xmm0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.L1st_enter
+
+.align	16
+.L1st:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	mov	$lo0,$hi0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.L1st_enter:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	lea	1($j),$j		# j++
+	mov	%rdx,$lo0
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.L1st
+
+	movq	%xmm0,$m0		# bp[1]
+
+	add	%rax,$hi1
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+	mov	$lo0,$hi0
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	jmp	.Louter
+.align	16
+.Louter:
+	xor	$j,$j			# j=0
+	mov	$n0,$m1
+	mov	(%rsp),$lo0
+
+	movq	`0*$STRIDE/4-96`($bp),%xmm0
+	movq	`1*$STRIDE/4-96`($bp),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bp),%xmm2
+	pand	%xmm5,%xmm1
+
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$lo0		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	movq	`3*$STRIDE/4-96`($bp),%xmm3
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	pand	%xmm7,%xmm3
+
+	imulq	$lo0,$m1		# tp[0]*n0
+	mov	%rdx,$hi0
+
+	por	%xmm2,%xmm0
+	lea	$STRIDE($bp),$bp
+	por	%xmm3,%xmm0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	8(%rsp),$lo0		# tp[1]
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.Linner_enter
+
+.align	16
+.Linner:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.Linner_enter:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$lo0		# ap[j]*bp[i]+tp[j]
+	mov	%rdx,$hi0
+	adc	\$0,$hi0
+	lea	1($j),$j		# j++
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.Linner
+
+	movq	%xmm0,$m0		# bp[i+1]
+
+	add	%rax,$hi1
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# pull upmost overflow bit
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	cmp	$num,$i
+	jb	.Louter
+
+	xor	$i,$i			# i=0 and clear CF!
+	mov	(%rsp),%rax		# tp[0]
+	lea	(%rsp),$ap		# borrow ap for tp
+	mov	$num,$j			# j=num
+	jmp	.Lsub
+.align	16
+.Lsub:	sbb	($np,$i,8),%rax
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]-np[i]
+	mov	8($ap,$i,8),%rax	# tp[i+1]
+	lea	1($i),$i		# i++
+	dec	$j			# doesnn't affect CF!
+	jnz	.Lsub
+
+	sbb	\$0,%rax		# handle upmost overflow bit
+	xor	$i,$i
+	and	%rax,$ap
+	not	%rax
+	mov	$rp,$np
+	and	%rax,$np
+	mov	$num,$j			# j=num
+	or	$np,$ap			# ap=borrow?tp:rp
+.align	16
+.Lcopy:					# copy or in-place refresh
+	mov	($ap,$i,8),%rax
+	mov	$i,(%rsp,$i,8)		# zap temporary vector
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]
+	lea	1($i),$i
+	sub	\$1,$j
+	jnz	.Lcopy
+
+	mov	8(%rsp,$num,8),%rsi	# restore %rsp
+	mov	\$1,%rax
+___
+$code.=<<___ if ($win64);
+	movaps	-88(%rsi),%xmm6
+	movaps	-72(%rsi),%xmm7
+___
+$code.=<<___;
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmul_epilogue:
+	ret
+.size	bn_mul_mont_gather5,.-bn_mul_mont_gather5
+___
+{{{
+my @A=("%r10","%r11");
+my @N=("%r13","%rdi");
+$code.=<<___;
+.type	bn_mul4x_mont_gather5,\@function,6
+.align	32
+bn_mul4x_mont_gather5:
+.Lmul4x_enter:
+___
+$code.=<<___ if ($addx);
+	and	\$0x80100,%r11d
+	cmp	\$0x80100,%r11d
+	je	.Lmulx4x_enter
+___
+$code.=<<___;
+	.byte	0x67
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0x28(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	.byte	0x67
+	mov	${num}d,%r10d
+	shl	\$3,${num}d
+	shl	\$3+2,%r10d		# 4*$num
+	neg	$num			# -$num
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr+4*$num
+	# modulo 4096, which covers ret[num], am[num] and n[2*num]
+	# (see bn_exp.c). this is done to allow memory disambiguation
+	# logic do its magic. [excessive frame is allocated in order
+	# to allow bn_from_mont8x to clear it.]
+	#
+	lea	-64(%rsp,$num,2),%r11
+	sub	$ap,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lmul4xsp_alt
+	sub	%r11,%rsp		# align with $ap
+	lea	-64(%rsp,$num,2),%rsp	# alloca(128+num*8)
+	jmp	.Lmul4xsp_done
+
+.align	32
+.Lmul4xsp_alt:
+	lea	4096-64(,$num,2),%r10
+	lea	-64(%rsp,$num,2),%rsp	# alloca(128+num*8)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rsp
+.Lmul4xsp_done:
+	and	\$-64,%rsp
+	neg	$num
+
+	mov	%rax,40(%rsp)
+.Lmul4x_body:
+
+	call	mul4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+___
+$code.=<<___ if ($win64);
+	movaps	-88(%rsi),%xmm6
+	movaps	-72(%rsi),%xmm7
+___
+$code.=<<___;
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmul4x_epilogue:
+	ret
+.size	bn_mul4x_mont_gather5,.-bn_mul4x_mont_gather5
+
+.type	mul4x_internal,\@abi-omnipotent
+.align	32
+mul4x_internal:
+	shl	\$5,$num
+	mov	`($win64?56:8)`(%rax),%r10d	# load 7th argument
+	lea	256(%rdx,$num),%r13
+	shr	\$5,$num		# restore $num
+___
+		$bp="%r12";
+		$STRIDE=2**5*8;		# 5 is "window size"
+		$N=$STRIDE/4;		# should match cache line size
+		$tp=$i;
+$code.=<<___;
+	mov	%r10,%r11
+	shr	\$`log($N/8)/log(2)`,%r10
+	and	\$`$N/8-1`,%r11
+	not	%r10
+	lea	.Lmagic_masks(%rip),%rax
+	and	\$`2**5/($N/8)-1`,%r10	# 5 is "window size"
+	lea	96(%rdx,%r11,8),$bp	# pointer within 1st cache line
+	movq	0(%rax,%r10,8),%xmm4	# set of masks denoting which
+	movq	8(%rax,%r10,8),%xmm5	# cache line contains element
+	add	\$7,%r11
+	movq	16(%rax,%r10,8),%xmm6	# denoted by 7th argument
+	movq	24(%rax,%r10,8),%xmm7
+	and	\$7,%r11
+
+	movq	`0*$STRIDE/4-96`($bp),%xmm0
+	lea	$STRIDE($bp),$tp	# borrow $tp
+	movq	`1*$STRIDE/4-96`($bp),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bp),%xmm2
+	pand	%xmm5,%xmm1
+	movq	`3*$STRIDE/4-96`($bp),%xmm3
+	pand	%xmm6,%xmm2
+	.byte	0x67
+	por	%xmm1,%xmm0
+	movq	`0*$STRIDE/4-96`($tp),%xmm1
+	.byte	0x67
+	pand	%xmm7,%xmm3
+	.byte	0x67
+	por	%xmm2,%xmm0
+	movq	`1*$STRIDE/4-96`($tp),%xmm2
+	.byte	0x67
+	pand	%xmm4,%xmm1
+	.byte	0x67
+	por	%xmm3,%xmm0
+	movq	`2*$STRIDE/4-96`($tp),%xmm3
+
+	movq	%xmm0,$m0		# m0=bp[0]
+	movq	`3*$STRIDE/4-96`($tp),%xmm0
+	mov	%r13,16+8(%rsp)		# save end of b[num]
+	mov	$rp, 56+8(%rsp)		# save $rp
+
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($ap),%rax
+	lea	($ap,$num),$ap		# end of a[num]
+	neg	$num
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$A[0]
+	mov	($np),%rax
+
+	pand	%xmm5,%xmm2
+	pand	%xmm6,%xmm3
+	por	%xmm2,%xmm1
+
+	imulq	$A[0],$m1		# "tp[0]"*n0
+	##############################################################
+	# $tp is chosen so that writing to top-most element of the
+	# vector occurs just "above" references to powers table,
+	# "above" modulo cache-line size, which effectively precludes
+	# possibility of memory disambiguation logic failure when
+	# accessing the table.
+	# 
+	lea	64+8(%rsp,%r11,8),$tp
+	mov	%rdx,$A[1]
+
+	pand	%xmm7,%xmm0
+	por	%xmm3,%xmm1
+	lea	2*$STRIDE($bp),$bp
+	por	%xmm1,%xmm0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# discarded
+	mov	8($ap,$num),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0
+	add	%rax,$A[1]
+	mov	16*1($np),%rax		# interleaved with 0, therefore 16*n
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1
+	add	%rax,$N[1]
+	mov	16($ap,$num),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	lea	4*8($num),$j		# j=4
+	lea	16*4($np),$np
+	adc	\$0,%rdx
+	mov	$N[1],($tp)
+	mov	%rdx,$N[0]
+	jmp	.L1st4x
+
+.align	32
+.L1st4x:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-16*2($np),%rax
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-16*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	16*0($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-8($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	16*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	lea	16*4($np),$np
+	adc	\$0,%rdx
+	mov	$N[1],($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	add	\$32,$j			# j+=4
+	jnz	.L1st4x
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-16*2($np),%rax
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-16*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$num),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	movq	%xmm0,$m0		# bp[1]
+	lea	($np,$num,2),$np	# rewind $np
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	mov	$N[0],-8($tp)
+
+	jmp	.Louter4x
+
+.align	32
+.Louter4x:
+	mov	($tp,$num),$A[0]
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$A[0]		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	movq	`0*$STRIDE/4-96`($bp),%xmm0
+	movq	`1*$STRIDE/4-96`($bp),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bp),%xmm2
+	pand	%xmm5,%xmm1
+	movq	`3*$STRIDE/4-96`($bp),%xmm3
+
+	imulq	$A[0],$m1		# tp[0]*n0
+	.byte	0x67
+	mov	%rdx,$A[1]
+	mov	$N[1],($tp)		# store upmost overflow bit
+
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	pand	%xmm7,%xmm3
+	por	%xmm2,%xmm0
+	lea	($tp,$num),$tp		# rewind $tp
+	lea	$STRIDE($bp),$bp
+	por	%xmm3,%xmm0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# "$N[0]", discarded
+	mov	8($ap,$num),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	16*1($np),%rax		# interleaved with 0, therefore 16*n
+	adc	\$0,%rdx
+	add	8($tp),$A[1]		# +tp[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap,$num),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	lea	4*8($num),$j		# j=4
+	lea	16*4($np),$np
+	adc	\$0,%rdx
+	mov	%rdx,$N[0]
+	jmp	.Linner4x
+
+.align	32
+.Linner4x:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-16*2($np),%rax
+	adc	\$0,%rdx
+	add	16($tp),$A[0]		# ap[j]*bp[i]+tp[j]
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[1],-32($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	-16*1($np),%rax
+	adc	\$0,%rdx
+	add	-8($tp),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	16*0($np),%rax
+	adc	\$0,%rdx
+	add	($tp),$A[0]		# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	16*1($np),%rax
+	adc	\$0,%rdx
+	add	8($tp),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	lea	16*4($np),$np
+	adc	\$0,%rdx
+	mov	$N[0],-8($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	add	\$32,$j			# j+=4
+	jnz	.Linner4x
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-16*2($np),%rax
+	adc	\$0,%rdx
+	add	16($tp),$A[0]		# ap[j]*bp[i]+tp[j]
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[1],-32($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	$m1,%rax
+	mov	-16*1($np),$m1
+	adc	\$0,%rdx
+	add	-8($tp),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$num),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	movq	%xmm0,$m0		# bp[i+1]
+	mov	$N[1],-16($tp)		# tp[j-1]
+	lea	($np,$num,2),$np	# rewind $np
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	add	($tp),$N[0]		# pull upmost overflow bit
+	adc	\$0,$N[1]		# upmost overflow bit
+	mov	$N[0],-8($tp)
+
+	cmp	16+8(%rsp),$bp
+	jb	.Louter4x
+___
+if (1) {
+$code.=<<___;
+	sub	$N[0],$m1		# compare top-most words
+	adc	$j,$j			# $j is zero
+	or	$j,$N[1]
+	xor	\$1,$N[1]
+	lea	($tp,$num),%rbx		# tptr in .sqr4x_sub
+	lea	($np,$N[1],8),%rbp	# nptr in .sqr4x_sub
+	mov	%r9,%rcx
+	sar	\$3+2,%rcx		# cf=0
+	mov	56+8(%rsp),%rdi		# rptr in .sqr4x_sub
+	jmp	.Lsqr4x_sub
+___
+} else {
+my @ri=("%rax",$bp,$m0,$m1);
+my $rp="%rdx";
+$code.=<<___
+	xor	\$1,$N[1]
+	lea	($tp,$num),$tp		# rewind $tp
+	sar	\$5,$num		# cf=0
+	lea	($np,$N[1],8),$np
+	mov	56+8(%rsp),$rp		# restore $rp
+	jmp	.Lsub4x
+
+.align	32
+.Lsub4x:
+	.byte	0x66
+	mov	8*0($tp),@ri[0]
+	mov	8*1($tp),@ri[1]
+	.byte	0x66
+	sbb	16*0($np),@ri[0]
+	mov	8*2($tp),@ri[2]
+	sbb	16*1($np),@ri[1]
+	mov	3*8($tp),@ri[3]
+	lea	4*8($tp),$tp
+	sbb	16*2($np),@ri[2]
+	mov	@ri[0],8*0($rp)
+	sbb	16*3($np),@ri[3]
+	lea	16*4($np),$np
+	mov	@ri[1],8*1($rp)
+	mov	@ri[2],8*2($rp)
+	mov	@ri[3],8*3($rp)
+	lea	8*4($rp),$rp
+
+	inc	$num
+	jnz	.Lsub4x
+
+	ret
+___
+}
+$code.=<<___;
+.size	mul4x_internal,.-mul4x_internal
+___
+}}}
+{{{
+######################################################################
+# void bn_power5(
+my $rptr="%rdi";	# BN_ULONG *rptr,
+my $aptr="%rsi";	# const BN_ULONG *aptr,
+my $bptr="%rdx";	# const void *table,
+my $nptr="%rcx";	# const BN_ULONG *nptr,
+my $n0  ="%r8";		# const BN_ULONG *n0);
+my $num ="%r9";		# int num, has to be divisible by 8
+			# int pwr 
+
+my ($i,$j,$tptr)=("%rbp","%rcx",$rptr);
+my @A0=("%r10","%r11");
+my @A1=("%r12","%r13");
+my ($a0,$a1,$ai)=("%r14","%r15","%rbx");
+
+$code.=<<___;
+.globl	bn_power5
+.type	bn_power5,\@function,6
+.align	32
+bn_power5:
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+	and	\$0x80100,%r11d
+	cmp	\$0x80100,%r11d
+	je	.Lpowerx5_enter
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0x28(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	mov	${num}d,%r10d
+	shl	\$3,${num}d		# convert $num to bytes
+	shl	\$3+2,%r10d		# 4*$num
+	neg	$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr+4*$num
+	# modulo 4096, which covers ret[num], am[num] and n[2*num]
+	# (see bn_exp.c). this is done to allow memory disambiguation
+	# logic do its magic.
+	#
+	lea	-64(%rsp,$num,2),%r11
+	sub	$aptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lpwr_sp_alt
+	sub	%r11,%rsp		# align with $aptr
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+2*$num)
+	jmp	.Lpwr_sp_done
+
+.align	32
+.Lpwr_sp_alt:
+	lea	4096-64(,$num,2),%r10	# 4096-frame-2*$num
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+2*$num)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rsp
+.Lpwr_sp_done:
+	and	\$-64,%rsp
+	mov	$num,%r10	
+	neg	$num
+
+	##############################################################
+	# Stack layout
+	#
+	# +0	saved $num, used in reduction section
+	# +8	&t[2*$num], used in reduction section
+	# +32	saved *n0
+	# +40	saved %rsp
+	# +48	t[2*$num]
+	#
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lpower5_body:
+	movq	$rptr,%xmm1		# save $rptr
+	movq	$nptr,%xmm2		# save $nptr
+	movq	%r10, %xmm3		# -$num
+	movq	$bptr,%xmm4
+
+	call	__bn_sqr8x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_sqr8x_internal
+
+	movq	%xmm2,$nptr
+	movq	%xmm4,$bptr
+	mov	$aptr,$rptr
+	mov	40(%rsp),%rax
+	lea	32(%rsp),$n0
+
+	call	mul4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lpower5_epilogue:
+	ret
+.size	bn_power5,.-bn_power5
+
+.globl	bn_sqr8x_internal
+.hidden	bn_sqr8x_internal
+.type	bn_sqr8x_internal,\@abi-omnipotent
+.align	32
+bn_sqr8x_internal:
+__bn_sqr8x_internal:
+	##############################################################
+	# Squaring part:
+	#
+	# a) multiply-n-add everything but a[i]*a[i];
+	# b) shift result of a) by 1 to the left and accumulate
+	#    a[i]*a[i] products;
+	#
+	##############################################################
+	#                                                     a[1]a[0]
+	#                                                 a[2]a[0]
+	#                                             a[3]a[0]
+	#                                             a[2]a[1]
+	#                                         a[4]a[0]
+	#                                         a[3]a[1]
+	#                                     a[5]a[0]
+	#                                     a[4]a[1]
+	#                                     a[3]a[2]
+	#                                 a[6]a[0]
+	#                                 a[5]a[1]
+	#                                 a[4]a[2]
+	#                             a[7]a[0]
+	#                             a[6]a[1]
+	#                             a[5]a[2]
+	#                             a[4]a[3]
+	#                         a[7]a[1]
+	#                         a[6]a[2]
+	#                         a[5]a[3]
+	#                     a[7]a[2]
+	#                     a[6]a[3]
+	#                     a[5]a[4]
+	#                 a[7]a[3]
+	#                 a[6]a[4]
+	#             a[7]a[4]
+	#             a[6]a[5]
+	#         a[7]a[5]
+	#     a[7]a[6]
+	#                                                     a[1]a[0]
+	#                                                 a[2]a[0]
+	#                                             a[3]a[0]
+	#                                         a[4]a[0]
+	#                                     a[5]a[0]
+	#                                 a[6]a[0]
+	#                             a[7]a[0]
+	#                                             a[2]a[1]
+	#                                         a[3]a[1]
+	#                                     a[4]a[1]
+	#                                 a[5]a[1]
+	#                             a[6]a[1]
+	#                         a[7]a[1]
+	#                                     a[3]a[2]
+	#                                 a[4]a[2]
+	#                             a[5]a[2]
+	#                         a[6]a[2]
+	#                     a[7]a[2]
+	#                             a[4]a[3]
+	#                         a[5]a[3]
+	#                     a[6]a[3]
+	#                 a[7]a[3]
+	#                     a[5]a[4]
+	#                 a[6]a[4]
+	#             a[7]a[4]
+	#             a[6]a[5]
+	#         a[7]a[5]
+	#     a[7]a[6]
+	#                                                         a[0]a[0]
+	#                                                 a[1]a[1]
+	#                                         a[2]a[2]
+	#                                 a[3]a[3]
+	#                         a[4]a[4]
+	#                 a[5]a[5]
+	#         a[6]a[6]
+	# a[7]a[7]
+
+	lea	32(%r10),$i		# $i=-($num-32)
+	lea	($aptr,$num),$aptr	# end of a[] buffer, ($aptr,$i)=&ap[2]
+
+	mov	$num,$j			# $j=$num
+
+					# comments apply to $num==8 case
+	mov	-32($aptr,$i),$a0	# a[0]
+	lea	48+8(%rsp,$num,2),$tptr	# end of tp[] buffer, &tp[2*$num]
+	mov	-24($aptr,$i),%rax	# a[1]
+	lea	-32($tptr,$i),$tptr	# end of tp[] window, &tp[2*$num-"$i"]
+	mov	-16($aptr,$i),$ai	# a[2]
+	mov	%rax,$a1
+
+	mul	$a0			# a[1]*a[0]
+	mov	%rax,$A0[0]		# a[1]*a[0]
+	 mov	$ai,%rax		# a[2]
+	mov	%rdx,$A0[1]
+	mov	$A0[0],-24($tptr,$i)	# t[1]
+
+	mul	$a0			# a[2]*a[0]
+	add	%rax,$A0[1]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	mov	$A0[1],-16($tptr,$i)	# t[2]
+	mov	%rdx,$A0[0]
+
+
+	 mov	-8($aptr,$i),$ai	# a[3]
+	mul	$a1			# a[2]*a[1]
+	mov	%rax,$A1[0]		# a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[1]
+
+	 lea	($i),$j
+	mul	$a0			# a[3]*a[0]
+	add	%rax,$A0[0]		# a[3]*a[0]+a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$j)	# t[3]
+	jmp	.Lsqr4x_1st
+
+.align	32
+.Lsqr4x_1st:
+	 mov	($aptr,$j),$ai		# a[4]
+	mul	$a1			# a[3]*a[1]
+	add	%rax,$A1[1]		# a[3]*a[1]+t[4]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[0]
+	adc	\$0,$A1[0]
+
+	mul	$a0			# a[4]*a[0]
+	add	%rax,$A0[1]		# a[4]*a[0]+a[3]*a[1]+t[4]
+	 mov	$ai,%rax		# a[3]
+	 mov	8($aptr,$j),$ai		# a[5]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]
+	adc	\$0,$A0[0]
+
+
+	mul	$a1			# a[4]*a[3]
+	add	%rax,$A1[0]		# a[4]*a[3]+t[5]
+	 mov	$ai,%rax
+	 mov	$A0[1],($tptr,$j)	# t[4]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[5]*a[2]
+	add	%rax,$A0[0]		# a[5]*a[2]+a[4]*a[3]+t[5]
+	 mov	$ai,%rax
+	 mov	16($aptr,$j),$ai	# a[6]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+
+	mul	$a1			# a[5]*a[3]
+	add	%rax,$A1[1]		# a[5]*a[3]+t[6]
+	 mov	$ai,%rax
+	 mov	$A0[0],8($tptr,$j)	# t[5]
+	mov	%rdx,$A1[0]
+	adc	\$0,$A1[0]
+
+	mul	$a0			# a[6]*a[2]
+	add	%rax,$A0[1]		# a[6]*a[2]+a[5]*a[3]+t[6]
+	 mov	$ai,%rax		# a[3]
+	 mov	24($aptr,$j),$ai	# a[7]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]
+	adc	\$0,$A0[0]
+
+
+	mul	$a1			# a[6]*a[5]
+	add	%rax,$A1[0]		# a[6]*a[5]+t[7]
+	 mov	$ai,%rax
+	 mov	$A0[1],16($tptr,$j)	# t[6]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+	 lea	32($j),$j
+
+	mul	$a0			# a[7]*a[4]
+	add	%rax,$A0[0]		# a[7]*a[4]+a[6]*a[5]+t[6]
+	 mov	$ai,%rax
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$j)	# t[7]
+
+	cmp	\$0,$j
+	jne	.Lsqr4x_1st
+
+	mul	$a1			# a[7]*a[5]
+	add	%rax,$A1[1]
+	lea	16($i),$i
+	adc	\$0,%rdx
+	add	$A0[1],$A1[1]
+	adc	\$0,%rdx
+
+	mov	$A1[1],($tptr)		# t[8]
+	mov	%rdx,$A1[0]
+	mov	%rdx,8($tptr)		# t[9]
+	jmp	.Lsqr4x_outer
+
+.align	32
+.Lsqr4x_outer:				# comments apply to $num==6 case
+	mov	-32($aptr,$i),$a0	# a[0]
+	lea	48+8(%rsp,$num,2),$tptr	# end of tp[] buffer, &tp[2*$num]
+	mov	-24($aptr,$i),%rax	# a[1]
+	lea	-32($tptr,$i),$tptr	# end of tp[] window, &tp[2*$num-"$i"]
+	mov	-16($aptr,$i),$ai	# a[2]
+	mov	%rax,$a1
+
+	mul	$a0			# a[1]*a[0]
+	mov	-24($tptr,$i),$A0[0]	# t[1]
+	add	%rax,$A0[0]		# a[1]*a[0]+t[1]
+	 mov	$ai,%rax		# a[2]
+	adc	\$0,%rdx
+	mov	$A0[0],-24($tptr,$i)	# t[1]
+	mov	%rdx,$A0[1]
+
+	mul	$a0			# a[2]*a[0]
+	add	%rax,$A0[1]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	-16($tptr,$i),$A0[1]	# a[2]*a[0]+t[2]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	mov	$A0[1],-16($tptr,$i)	# t[2]
+
+	xor	$A1[0],$A1[0]
+
+	 mov	-8($aptr,$i),$ai	# a[3]
+	mul	$a1			# a[2]*a[1]
+	add	%rax,$A1[0]		# a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	-8($tptr,$i),$A1[0]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[3]*a[0]
+	add	%rax,$A0[0]		# a[3]*a[0]+a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	$A1[0],$A0[0]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$i)	# t[3]
+
+	lea	($i),$j
+	jmp	.Lsqr4x_inner
+
+.align	32
+.Lsqr4x_inner:
+	 mov	($aptr,$j),$ai		# a[4]
+	mul	$a1			# a[3]*a[1]
+	add	%rax,$A1[1]		# a[3]*a[1]+t[4]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[0]
+	adc	\$0,$A1[0]
+	add	($tptr,$j),$A1[1]
+	adc	\$0,$A1[0]
+
+	.byte	0x67
+	mul	$a0			# a[4]*a[0]
+	add	%rax,$A0[1]		# a[4]*a[0]+a[3]*a[1]+t[4]
+	 mov	$ai,%rax		# a[3]
+	 mov	8($aptr,$j),$ai		# a[5]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]
+	adc	\$0,$A0[0]
+
+	mul	$a1			# a[4]*a[3]
+	add	%rax,$A1[0]		# a[4]*a[3]+t[5]
+	mov	$A0[1],($tptr,$j)	# t[4]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+	add	8($tptr,$j),$A1[0]
+	lea	16($j),$j		# j++
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[5]*a[2]
+	add	%rax,$A0[0]		# a[5]*a[2]+a[4]*a[3]+t[5]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	$A1[0],$A0[0]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$j)	# t[5], "preloaded t[1]" below
+
+	cmp	\$0,$j
+	jne	.Lsqr4x_inner
+
+	.byte	0x67
+	mul	$a1			# a[5]*a[3]
+	add	%rax,$A1[1]
+	adc	\$0,%rdx
+	add	$A0[1],$A1[1]
+	adc	\$0,%rdx
+
+	mov	$A1[1],($tptr)		# t[6], "preloaded t[2]" below
+	mov	%rdx,$A1[0]
+	mov	%rdx,8($tptr)		# t[7], "preloaded t[3]" below
+
+	add	\$16,$i
+	jnz	.Lsqr4x_outer
+
+					# comments apply to $num==4 case
+	mov	-32($aptr),$a0		# a[0]
+	lea	48+8(%rsp,$num,2),$tptr	# end of tp[] buffer, &tp[2*$num]
+	mov	-24($aptr),%rax		# a[1]
+	lea	-32($tptr,$i),$tptr	# end of tp[] window, &tp[2*$num-"$i"]
+	mov	-16($aptr),$ai		# a[2]
+	mov	%rax,$a1
+
+	mul	$a0			# a[1]*a[0]
+	add	%rax,$A0[0]		# a[1]*a[0]+t[1], preloaded t[1]
+	 mov	$ai,%rax		# a[2]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+
+	mul	$a0			# a[2]*a[0]
+	add	%rax,$A0[1]
+	 mov	$ai,%rax
+	 mov	$A0[0],-24($tptr)	# t[1]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]		# a[2]*a[0]+t[2], preloaded t[2]
+	 mov	-8($aptr),$ai		# a[3]
+	adc	\$0,$A0[0]
+
+	mul	$a1			# a[2]*a[1]
+	add	%rax,$A1[0]		# a[2]*a[1]+t[3], preloaded t[3]
+	 mov	$ai,%rax
+	 mov	$A0[1],-16($tptr)	# t[2]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[3]*a[0]
+	add	%rax,$A0[0]		# a[3]*a[0]+a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr)	# t[3]
+
+	mul	$a1			# a[3]*a[1]
+	add	%rax,$A1[1]
+	 mov	-16($aptr),%rax		# a[2]
+	adc	\$0,%rdx
+	add	$A0[1],$A1[1]
+	adc	\$0,%rdx
+
+	mov	$A1[1],($tptr)		# t[4]
+	mov	%rdx,$A1[0]
+	mov	%rdx,8($tptr)		# t[5]
+
+	mul	$ai			# a[2]*a[3]
+___
+{
+my ($shift,$carry)=($a0,$a1);
+my @S=(@A1,$ai,$n0);
+$code.=<<___;
+	 add	\$16,$i
+	 xor	$shift,$shift
+	 sub	$num,$i			# $i=16-$num
+	 xor	$carry,$carry
+
+	add	$A1[0],%rax		# t[5]
+	adc	\$0,%rdx
+	mov	%rax,8($tptr)		# t[5]
+	mov	%rdx,16($tptr)		# t[6]
+	mov	$carry,24($tptr)	# t[7]
+
+	 mov	-16($aptr,$i),%rax	# a[0]
+	lea	48+8(%rsp),$tptr
+	 xor	$A0[0],$A0[0]		# t[0]
+	 mov	8($tptr),$A0[1]		# t[1]
+
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	24($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	-8($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[0],($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1 | shift
+	 mov	$S[1],8($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	 mov	32($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	40($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[2]
+	 mov	0($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[2],16($tptr)
+	adc	%rdx,$S[3]
+	lea	16($i),$i
+	mov	$S[3],24($tptr)
+	sbb	$carry,$carry		# mov cf,$carry
+	lea	64($tptr),$tptr
+	jmp	.Lsqr4x_shift_n_add
+
+.align	32
+.Lsqr4x_shift_n_add:
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	-16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	-8($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	-8($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[0],-32($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1 | shift
+	 mov	$S[1],-24($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	 mov	0($tptr),$A0[0]		# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	8($tptr),$A0[1]		# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[2]
+	 mov	0($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[2],-16($tptr)
+	adc	%rdx,$S[3]
+
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	 mov	$S[3],-8($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	24($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	8($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[0],0($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1 | shift
+	 mov	$S[1],8($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	 mov	32($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	40($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[2]
+	 mov	16($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[2],16($tptr)
+	adc	%rdx,$S[3]
+	mov	$S[3],24($tptr)
+	sbb	$carry,$carry		# mov cf,$carry
+	lea	64($tptr),$tptr
+	add	\$32,$i
+	jnz	.Lsqr4x_shift_n_add
+
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	.byte	0x67
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	-16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	-8($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	-8($aptr),%rax		# a[i+1]	# prefetch
+	mov	$S[0],-32($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1|shift
+	 mov	$S[1],-24($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	adc	%rax,$S[2]
+	adc	%rdx,$S[3]
+	mov	$S[2],-16($tptr)
+	mov	$S[3],-8($tptr)
+___
+}
+######################################################################
+# Montgomery reduction part, "word-by-word" algorithm.
+#
+# This new path is inspired by multiple submissions from Intel, by
+# Shay Gueron, Vlad Krasnov, Erdinc Ozturk, James Guilford,
+# Vinodh Gopal...
+{
+my ($nptr,$tptr,$carry,$m0)=("%rbp","%rdi","%rsi","%rbx");
+
+$code.=<<___;
+	movq	%xmm2,$nptr
+sqr8x_reduction:
+	xor	%rax,%rax
+	lea	($nptr,$num,2),%rcx	# end of n[]
+	lea	48+8(%rsp,$num,2),%rdx	# end of t[] buffer
+	mov	%rcx,0+8(%rsp)
+	lea	48+8(%rsp,$num),$tptr	# end of initial t[] window
+	mov	%rdx,8+8(%rsp)
+	neg	$num
+	jmp	.L8x_reduction_loop
+
+.align	32
+.L8x_reduction_loop:
+	lea	($tptr,$num),$tptr	# start of current t[] window
+	.byte	0x66
+	mov	8*0($tptr),$m0
+	mov	8*1($tptr),%r9
+	mov	8*2($tptr),%r10
+	mov	8*3($tptr),%r11
+	mov	8*4($tptr),%r12
+	mov	8*5($tptr),%r13
+	mov	8*6($tptr),%r14
+	mov	8*7($tptr),%r15
+	mov	%rax,(%rdx)		# store top-most carry bit
+	lea	8*8($tptr),$tptr
+
+	.byte	0x67
+	mov	$m0,%r8
+	imulq	32+8(%rsp),$m0		# n0*a[0]
+	mov	16*0($nptr),%rax	# n[0]
+	mov	\$8,%ecx
+	jmp	.L8x_reduce
+
+.align	32
+.L8x_reduce:
+	mulq	$m0
+	 mov	16*1($nptr),%rax	# n[1]
+	neg	%r8
+	mov	%rdx,%r8
+	adc	\$0,%r8
+
+	mulq	$m0
+	add	%rax,%r9
+	 mov	16*2($nptr),%rax
+	adc	\$0,%rdx
+	add	%r9,%r8
+	 mov	$m0,48-8+8(%rsp,%rcx,8)	# put aside n0*a[i]
+	mov	%rdx,%r9
+	adc	\$0,%r9
+
+	mulq	$m0
+	add	%rax,%r10
+	 mov	16*3($nptr),%rax
+	adc	\$0,%rdx
+	add	%r10,%r9
+	 mov	32+8(%rsp),$carry	# pull n0, borrow $carry
+	mov	%rdx,%r10
+	adc	\$0,%r10
+
+	mulq	$m0
+	add	%rax,%r11
+	 mov	16*4($nptr),%rax
+	adc	\$0,%rdx
+	 imulq	%r8,$carry		# modulo-scheduled
+	add	%r11,%r10
+	mov	%rdx,%r11
+	adc	\$0,%r11
+
+	mulq	$m0
+	add	%rax,%r12
+	 mov	16*5($nptr),%rax
+	adc	\$0,%rdx
+	add	%r12,%r11
+	mov	%rdx,%r12
+	adc	\$0,%r12
+
+	mulq	$m0
+	add	%rax,%r13
+	 mov	16*6($nptr),%rax
+	adc	\$0,%rdx
+	add	%r13,%r12
+	mov	%rdx,%r13
+	adc	\$0,%r13
+
+	mulq	$m0
+	add	%rax,%r14
+	 mov	16*7($nptr),%rax
+	adc	\$0,%rdx
+	add	%r14,%r13
+	mov	%rdx,%r14
+	adc	\$0,%r14
+
+	mulq	$m0
+	 mov	$carry,$m0		# n0*a[i]
+	add	%rax,%r15
+	 mov	16*0($nptr),%rax	# n[0]
+	adc	\$0,%rdx
+	add	%r15,%r14
+	mov	%rdx,%r15
+	adc	\$0,%r15
+
+	dec	%ecx
+	jnz	.L8x_reduce
+
+	lea	16*8($nptr),$nptr
+	xor	%rax,%rax
+	mov	8+8(%rsp),%rdx		# pull end of t[]
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.L8x_no_tail
+
+	.byte	0x66
+	add	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	sbb	$carry,$carry		# top carry
+
+	mov	48+56+8(%rsp),$m0	# pull n0*a[0]
+	mov	\$8,%ecx
+	mov	16*0($nptr),%rax
+	jmp	.L8x_tail
+
+.align	32
+.L8x_tail:
+	mulq	$m0
+	add	%rax,%r8
+	 mov	16*1($nptr),%rax
+	 mov	%r8,($tptr)		# save result
+	mov	%rdx,%r8
+	adc	\$0,%r8
+
+	mulq	$m0
+	add	%rax,%r9
+	 mov	16*2($nptr),%rax
+	adc	\$0,%rdx
+	add	%r9,%r8
+	 lea	8($tptr),$tptr		# $tptr++
+	mov	%rdx,%r9
+	adc	\$0,%r9
+
+	mulq	$m0
+	add	%rax,%r10
+	 mov	16*3($nptr),%rax
+	adc	\$0,%rdx
+	add	%r10,%r9
+	mov	%rdx,%r10
+	adc	\$0,%r10
+
+	mulq	$m0
+	add	%rax,%r11
+	 mov	16*4($nptr),%rax
+	adc	\$0,%rdx
+	add	%r11,%r10
+	mov	%rdx,%r11
+	adc	\$0,%r11
+
+	mulq	$m0
+	add	%rax,%r12
+	 mov	16*5($nptr),%rax
+	adc	\$0,%rdx
+	add	%r12,%r11
+	mov	%rdx,%r12
+	adc	\$0,%r12
+
+	mulq	$m0
+	add	%rax,%r13
+	 mov	16*6($nptr),%rax
+	adc	\$0,%rdx
+	add	%r13,%r12
+	mov	%rdx,%r13
+	adc	\$0,%r13
+
+	mulq	$m0
+	add	%rax,%r14
+	 mov	16*7($nptr),%rax
+	adc	\$0,%rdx
+	add	%r14,%r13
+	mov	%rdx,%r14
+	adc	\$0,%r14
+
+	mulq	$m0
+	 mov	48-16+8(%rsp,%rcx,8),$m0# pull n0*a[i]
+	add	%rax,%r15
+	adc	\$0,%rdx
+	add	%r15,%r14
+	 mov	16*0($nptr),%rax	# pull n[0]
+	mov	%rdx,%r15
+	adc	\$0,%r15
+
+	dec	%ecx
+	jnz	.L8x_tail
+
+	lea	16*8($nptr),$nptr
+	mov	8+8(%rsp),%rdx		# pull end of t[]
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.L8x_tail_done		# break out of loop
+
+	 mov	48+56+8(%rsp),$m0	# pull n0*a[0]
+	neg	$carry
+	 mov	8*0($nptr),%rax		# pull n[0]
+	adc	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	sbb	$carry,$carry		# top carry
+
+	mov	\$8,%ecx
+	jmp	.L8x_tail
+
+.align	32
+.L8x_tail_done:
+	add	(%rdx),%r8		# can this overflow?
+	xor	%rax,%rax
+
+	neg	$carry
+.L8x_no_tail:
+	adc	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	adc	\$0,%rax		# top-most carry
+	 mov	-16($nptr),%rcx		# np[num-1]
+	 xor	$carry,$carry
+
+	movq	%xmm2,$nptr		# restore $nptr
+
+	mov	%r8,8*0($tptr)		# store top 512 bits
+	mov	%r9,8*1($tptr)
+	 movq	%xmm3,$num		# $num is %r9, can't be moved upwards
+	mov	%r10,8*2($tptr)
+	mov	%r11,8*3($tptr)
+	mov	%r12,8*4($tptr)
+	mov	%r13,8*5($tptr)
+	mov	%r14,8*6($tptr)
+	mov	%r15,8*7($tptr)
+	lea	8*8($tptr),$tptr
+
+	cmp	%rdx,$tptr		# end of t[]?
+	jb	.L8x_reduction_loop
+___
+}
+##############################################################
+# Post-condition, 4x unrolled
+#
+{
+my ($tptr,$nptr)=("%rbx","%rbp");
+$code.=<<___;
+	#xor	%rsi,%rsi		# %rsi was $carry above
+	sub	%r15,%rcx		# compare top-most words
+	lea	(%rdi,$num),$tptr	# %rdi was $tptr above
+	adc	%rsi,%rsi
+	mov	$num,%rcx
+	or	%rsi,%rax
+	movq	%xmm1,$rptr		# restore $rptr
+	xor	\$1,%rax
+	movq	%xmm1,$aptr		# prepare for back-to-back call
+	lea	($nptr,%rax,8),$nptr
+	sar	\$3+2,%rcx		# cf=0
+	jmp	.Lsqr4x_sub
+
+.align	32
+.Lsqr4x_sub:
+	.byte	0x66
+	mov	8*0($tptr),%r12
+	mov	8*1($tptr),%r13
+	sbb	16*0($nptr),%r12
+	mov	8*2($tptr),%r14
+	sbb	16*1($nptr),%r13
+	mov	8*3($tptr),%r15
+	lea	8*4($tptr),$tptr
+	sbb	16*2($nptr),%r14
+	mov	%r12,8*0($rptr)
+	sbb	16*3($nptr),%r15
+	lea	16*4($nptr),$nptr
+	mov	%r13,8*1($rptr)
+	mov	%r14,8*2($rptr)
+	mov	%r15,8*3($rptr)
+	lea	8*4($rptr),$rptr
+
+	inc	%rcx			# pass %cf
+	jnz	.Lsqr4x_sub
+___
+}
+$code.=<<___;
+	mov	$num,%r10		# prepare for back-to-back call
+	neg	$num			# restore $num	
+	ret
+.size	bn_sqr8x_internal,.-bn_sqr8x_internal
+___
+{
+$code.=<<___;
+.globl	bn_from_montgomery
+.type	bn_from_montgomery,\@abi-omnipotent
+.align	32
+bn_from_montgomery:
+	testl	\$7,`($win64?"48(%rsp)":"%r9d")`
+	jz	bn_from_mont8x
+	xor	%eax,%eax
+	ret
+.size	bn_from_montgomery,.-bn_from_montgomery
+
+.type	bn_from_mont8x,\@function,6
+.align	32
+bn_from_mont8x:
+	.byte	0x67
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0x28(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	.byte	0x67
+	mov	${num}d,%r10d
+	shl	\$3,${num}d		# convert $num to bytes
+	shl	\$3+2,%r10d		# 4*$num
+	neg	$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr+4*$num
+	# modulo 4096, which covers ret[num], am[num] and n[2*num]
+	# (see bn_exp.c). this is done to allow memory disambiguation
+	# logic do its magic.
+	#
+	lea	-64(%rsp,$num,2),%r11
+	sub	$aptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lfrom_sp_alt
+	sub	%r11,%rsp		# align with $aptr
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+2*$num)
+	jmp	.Lfrom_sp_done
+
+.align	32
+.Lfrom_sp_alt:
+	lea	4096-64(,$num,2),%r10	# 4096-frame-2*$num
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+2*$num)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rsp
+.Lfrom_sp_done:
+	and	\$-64,%rsp
+	mov	$num,%r10	
+	neg	$num
+
+	##############################################################
+	# Stack layout
+	#
+	# +0	saved $num, used in reduction section
+	# +8	&t[2*$num], used in reduction section
+	# +32	saved *n0
+	# +40	saved %rsp
+	# +48	t[2*$num]
+	#
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lfrom_body:
+	mov	$num,%r11
+	lea	48(%rsp),%rax
+	pxor	%xmm0,%xmm0
+	jmp	.Lmul_by_1
+
+.align	32
+.Lmul_by_1:
+	movdqu	($aptr),%xmm1
+	movdqu	16($aptr),%xmm2
+	movdqu	32($aptr),%xmm3
+	movdqa	%xmm0,(%rax,$num)
+	movdqu	48($aptr),%xmm4
+	movdqa	%xmm0,16(%rax,$num)
+	.byte	0x48,0x8d,0xb6,0x40,0x00,0x00,0x00	# lea	64($aptr),$aptr
+	movdqa	%xmm1,(%rax)
+	movdqa	%xmm0,32(%rax,$num)
+	movdqa	%xmm2,16(%rax)
+	movdqa	%xmm0,48(%rax,$num)
+	movdqa	%xmm3,32(%rax)
+	movdqa	%xmm4,48(%rax)
+	lea	64(%rax),%rax
+	sub	\$64,%r11
+	jnz	.Lmul_by_1
+
+	movq	$rptr,%xmm1
+	movq	$nptr,%xmm2
+	.byte	0x67
+	mov	$nptr,%rbp
+	movq	%r10, %xmm3		# -num
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+	and	\$0x80100,%r11d
+	cmp	\$0x80100,%r11d
+	jne	.Lfrom_mont_nox
+
+	lea	(%rax,$num),$rptr
+	call	sqrx8x_reduction
+
+	pxor	%xmm0,%xmm0
+	lea	48(%rsp),%rax
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lfrom_mont_zero
+
+.align	32
+.Lfrom_mont_nox:
+___
+$code.=<<___;
+	call	sqr8x_reduction
+
+	pxor	%xmm0,%xmm0
+	lea	48(%rsp),%rax
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lfrom_mont_zero
+
+.align	32
+.Lfrom_mont_zero:
+	movdqa	%xmm0,16*0(%rax)
+	movdqa	%xmm0,16*1(%rax)
+	movdqa	%xmm0,16*2(%rax)
+	movdqa	%xmm0,16*3(%rax)
+	lea	16*4(%rax),%rax
+	sub	\$32,$num
+	jnz	.Lfrom_mont_zero
+
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lfrom_epilogue:
+	ret
+.size	bn_from_mont8x,.-bn_from_mont8x
+___
+}
+}}}
+
+if ($addx) {{{
+my $bp="%rdx";	# restore original value
+
+$code.=<<___;
+.type	bn_mulx4x_mont_gather5,\@function,6
+.align	32
+bn_mulx4x_mont_gather5:
+.Lmulx4x_enter:
+	.byte	0x67
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0x28(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	.byte	0x67
+	mov	${num}d,%r10d
+	shl	\$3,${num}d		# convert $num to bytes
+	shl	\$3+2,%r10d		# 4*$num
+	neg	$num			# -$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr+4*$num
+	# modulo 4096, which covers a[num], ret[num] and n[2*num]
+	# (see bn_exp.c). this is done to allow memory disambiguation
+	# logic do its magic. [excessive frame is allocated in order
+	# to allow bn_from_mont8x to clear it.]
+	#
+	lea	-64(%rsp,$num,2),%r11
+	sub	$ap,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lmulx4xsp_alt
+	sub	%r11,%rsp		# align with $aptr
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+$num)
+	jmp	.Lmulx4xsp_done
+
+.align	32
+.Lmulx4xsp_alt:
+	lea	4096-64(,$num,2),%r10	# 4096-frame-$num
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+$num)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rsp
+.Lmulx4xsp_done:	
+	and	\$-64,%rsp		# ensure alignment
+	##############################################################
+	# Stack layout
+	# +0	-num
+	# +8	off-loaded &b[i]
+	# +16	end of b[num]
+	# +24	inner counter
+	# +32	saved n0
+	# +40	saved %rsp
+	# +48
+	# +56	saved rp
+	# +64	tmp[num+1]
+	#
+	mov	$n0, 32(%rsp)		# save *n0
+	mov	%rax,40(%rsp)		# save original %rsp
+.Lmulx4x_body:
+	call	mulx4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+___
+$code.=<<___ if ($win64);
+	movaps	-88(%rsi),%xmm6
+	movaps	-72(%rsi),%xmm7
+___
+$code.=<<___;
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmulx4x_epilogue:
+	ret
+.size	bn_mulx4x_mont_gather5,.-bn_mulx4x_mont_gather5
+
+.type	mulx4x_internal,\@abi-omnipotent
+.align	32
+mulx4x_internal:
+	.byte	0x4c,0x89,0x8c,0x24,0x08,0x00,0x00,0x00	# mov	$num,8(%rsp)		# save -$num
+	.byte	0x67
+	neg	$num			# restore $num
+	shl	\$5,$num
+	lea	256($bp,$num),%r13
+	shr	\$5+5,$num
+	mov	`($win64?56:8)`(%rax),%r10d	# load 7th argument
+	sub	\$1,$num
+	mov	%r13,16+8(%rsp)		# end of b[num]
+	mov	$num,24+8(%rsp)		# inner counter
+	mov	$rp, 56+8(%rsp)		# save $rp
+___
+my ($aptr, $bptr, $nptr, $tptr, $mi,  $bi,  $zero, $num)=
+   ("%rsi","%rdi","%rcx","%rbx","%r8","%r9","%rbp","%rax");
+my $rptr=$bptr;
+my $STRIDE=2**5*8;		# 5 is "window size"
+my $N=$STRIDE/4;		# should match cache line size
+$code.=<<___;
+	mov	%r10,%r11
+	shr	\$`log($N/8)/log(2)`,%r10
+	and	\$`$N/8-1`,%r11
+	not	%r10
+	lea	.Lmagic_masks(%rip),%rax
+	and	\$`2**5/($N/8)-1`,%r10	# 5 is "window size"
+	lea	96($bp,%r11,8),$bptr	# pointer within 1st cache line
+	movq	0(%rax,%r10,8),%xmm4	# set of masks denoting which
+	movq	8(%rax,%r10,8),%xmm5	# cache line contains element
+	add	\$7,%r11
+	movq	16(%rax,%r10,8),%xmm6	# denoted by 7th argument
+	movq	24(%rax,%r10,8),%xmm7
+	and	\$7,%r11
+
+	movq	`0*$STRIDE/4-96`($bptr),%xmm0
+	lea	$STRIDE($bptr),$tptr	# borrow $tptr
+	movq	`1*$STRIDE/4-96`($bptr),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bptr),%xmm2
+	pand	%xmm5,%xmm1
+	movq	`3*$STRIDE/4-96`($bptr),%xmm3
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	movq	`0*$STRIDE/4-96`($tptr),%xmm1
+	pand	%xmm7,%xmm3
+	por	%xmm2,%xmm0
+	movq	`1*$STRIDE/4-96`($tptr),%xmm2
+	por	%xmm3,%xmm0
+	.byte	0x67,0x67
+	pand	%xmm4,%xmm1
+	movq	`2*$STRIDE/4-96`($tptr),%xmm3
+
+	movq	%xmm0,%rdx		# bp[0]
+	movq	`3*$STRIDE/4-96`($tptr),%xmm0
+	lea	2*$STRIDE($bptr),$bptr	# next &b[i]
+	pand	%xmm5,%xmm2
+	.byte	0x67,0x67
+	pand	%xmm6,%xmm3
+	##############################################################
+	# $tptr is chosen so that writing to top-most element of the
+	# vector occurs just "above" references to powers table,
+	# "above" modulo cache-line size, which effectively precludes
+	# possibility of memory disambiguation logic failure when
+	# accessing the table.
+	# 
+	lea	64+8*4+8(%rsp,%r11,8),$tptr
+
+	mov	%rdx,$bi
+	mulx	0*8($aptr),$mi,%rax	# a[0]*b[0]
+	mulx	1*8($aptr),%r11,%r12	# a[1]*b[0]
+	add	%rax,%r11
+	mulx	2*8($aptr),%rax,%r13	# ...
+	adc	%rax,%r12
+	adc	\$0,%r13
+	mulx	3*8($aptr),%rax,%r14
+
+	mov	$mi,%r15
+	imulq	32+8(%rsp),$mi		# "t[0]"*n0
+	xor	$zero,$zero		# cf=0, of=0
+	mov	$mi,%rdx
+
+	por	%xmm2,%xmm1
+	pand	%xmm7,%xmm0
+	por	%xmm3,%xmm1
+	mov	$bptr,8+8(%rsp)		# off-load &b[i]
+	por	%xmm1,%xmm0
+
+	.byte	0x48,0x8d,0xb6,0x20,0x00,0x00,0x00	# lea	4*8($aptr),$aptr
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+
+	mulx	0*16($nptr),%rax,%r10
+	adcx	%rax,%r15		# discarded
+	adox	%r11,%r10
+	mulx	1*16($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	mulx	2*16($nptr),%rax,%r12
+	mov	24+8(%rsp),$bptr	# counter value
+	.byte	0x66
+	mov	%r10,-8*4($tptr)
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*16($nptr),%rax,%r15
+	 .byte	0x67,0x67
+	 mov	$bi,%rdx
+	mov	%r11,-8*3($tptr)
+	adcx	%rax,%r12
+	adox	$zero,%r15		# of=0
+	.byte	0x48,0x8d,0x89,0x40,0x00,0x00,0x00	# lea	4*16($nptr),$nptr
+	mov	%r12,-8*2($tptr)
+	#jmp	.Lmulx4x_1st
+
+.align	32
+.Lmulx4x_1st:
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[0]
+	adcx	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[0]
+	adcx	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 .byte	0x67,0x67
+	 mov	$mi,%rdx
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+
+	adox	%r15,%r10
+	mulx	0*16($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*16($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*16($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	mov	%r11,-4*8($tptr)
+	adox	%r15,%r13
+	mulx	3*16($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	lea	4*16($nptr),$nptr
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_1st
+
+	mov	8(%rsp),$num		# load -num
+	movq	%xmm0,%rdx		# bp[1]
+	adc	$zero,%r15		# modulo-scheduled
+	lea	($aptr,$num),$aptr	# rewind $aptr
+	add	%r15,%r14
+	mov	8+8(%rsp),$bptr		# re-load &b[i]
+	adc	$zero,$zero		# top-most carry
+	mov	%r14,-1*8($tptr)
+	jmp	.Lmulx4x_outer
+
+.align	32
+.Lmulx4x_outer:
+	mov	$zero,($tptr)		# save top-most carry
+	lea	4*8($tptr,$num),$tptr	# rewind $tptr
+	mulx	0*8($aptr),$mi,%r11	# a[0]*b[i]
+	xor	$zero,$zero		# cf=0, of=0
+	mov	%rdx,$bi
+	mulx	1*8($aptr),%r14,%r12	# a[1]*b[i]
+	adox	-4*8($tptr),$mi		# +t[0]
+	adcx	%r14,%r11
+	mulx	2*8($aptr),%r15,%r13	# ...
+	adox	-3*8($tptr),%r11
+	adcx	%r15,%r12
+	mulx	3*8($aptr),%rdx,%r14
+	adox	-2*8($tptr),%r12
+	adcx	%rdx,%r13
+	lea	($nptr,$num,2),$nptr	# rewind $nptr
+	lea	4*8($aptr),$aptr
+	adox	-1*8($tptr),%r13
+	adcx	$zero,%r14
+	adox	$zero,%r14
+
+	.byte	0x67
+	mov	$mi,%r15
+	imulq	32+8(%rsp),$mi		# "t[0]"*n0
+
+	movq	`0*$STRIDE/4-96`($bptr),%xmm0
+	.byte	0x67,0x67
+	mov	$mi,%rdx
+	movq	`1*$STRIDE/4-96`($bptr),%xmm1
+	.byte	0x67
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-96`($bptr),%xmm2
+	.byte	0x67
+	pand	%xmm5,%xmm1
+	movq	`3*$STRIDE/4-96`($bptr),%xmm3
+	add	\$$STRIDE,$bptr		# next &b[i]
+	.byte	0x67
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	pand	%xmm7,%xmm3
+	xor	$zero,$zero		# cf=0, of=0
+	mov	$bptr,8+8(%rsp)		# off-load &b[i]
+
+	mulx	0*16($nptr),%rax,%r10
+	adcx	%rax,%r15		# discarded
+	adox	%r11,%r10
+	mulx	1*16($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	mulx	2*16($nptr),%rax,%r12
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*16($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	 por	%xmm2,%xmm0
+	mov	24+8(%rsp),$bptr	# counter value
+	mov	%r10,-8*4($tptr)
+	 por	%xmm3,%xmm0
+	adcx	%rax,%r12
+	mov	%r11,-8*3($tptr)
+	adox	$zero,%r15		# of=0
+	mov	%r12,-8*2($tptr)
+	lea	4*16($nptr),$nptr
+	jmp	.Lmulx4x_inner
+
+.align	32
+.Lmulx4x_inner:
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[i]
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	adox	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[i]
+	adcx	0*8($tptr),%r10
+	adox	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	1*8($tptr),%r11
+	adox	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 mov	$mi,%rdx
+	adcx	2*8($tptr),%r12
+	adox	%rax,%r13
+	adcx	3*8($tptr),%r13
+	adox	$zero,%r14		# of=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+	adcx	$zero,%r14		# cf=0
+
+	adox	%r15,%r10
+	mulx	0*16($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*16($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*16($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	adox	%r15,%r13
+	mov	%r11,-4*8($tptr)
+	mulx	3*16($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	lea	4*16($nptr),$nptr
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_inner
+
+	mov	0+8(%rsp),$num		# load -num
+	movq	%xmm0,%rdx		# bp[i+1]
+	adc	$zero,%r15		# modulo-scheduled
+	sub	0*8($tptr),$bptr	# pull top-most carry to %cf
+	mov	8+8(%rsp),$bptr		# re-load &b[i]
+	mov	16+8(%rsp),%r10
+	adc	%r15,%r14
+	lea	($aptr,$num),$aptr	# rewind $aptr
+	adc	$zero,$zero		# top-most carry
+	mov	%r14,-1*8($tptr)
+
+	cmp	%r10,$bptr
+	jb	.Lmulx4x_outer
+
+	mov	-16($nptr),%r10
+	xor	%r15,%r15
+	sub	%r14,%r10		# compare top-most words
+	adc	%r15,%r15
+	or	%r15,$zero
+	xor	\$1,$zero
+	lea	($tptr,$num),%rdi	# rewind $tptr
+	lea	($nptr,$num,2),$nptr	# rewind $nptr
+	.byte	0x67,0x67
+	sar	\$3+2,$num		# cf=0
+	lea	($nptr,$zero,8),%rbp
+	mov	56+8(%rsp),%rdx		# restore rp
+	mov	$num,%rcx
+	jmp	.Lsqrx4x_sub		# common post-condition
+.size	mulx4x_internal,.-mulx4x_internal
+___
+}{
+######################################################################
+# void bn_power5(
+my $rptr="%rdi";	# BN_ULONG *rptr,
+my $aptr="%rsi";	# const BN_ULONG *aptr,
+my $bptr="%rdx";	# const void *table,
+my $nptr="%rcx";	# const BN_ULONG *nptr,
+my $n0  ="%r8";		# const BN_ULONG *n0);
+my $num ="%r9";		# int num, has to be divisible by 8
+			# int pwr);
+
+my ($i,$j,$tptr)=("%rbp","%rcx",$rptr);
+my @A0=("%r10","%r11");
+my @A1=("%r12","%r13");
+my ($a0,$a1,$ai)=("%r14","%r15","%rbx");
+
+$code.=<<___;
+.type	bn_powerx5,\@function,6
+.align	32
+bn_powerx5:
+.Lpowerx5_enter:
+	.byte	0x67
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0x28(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	.byte	0x67
+	mov	${num}d,%r10d
+	shl	\$3,${num}d		# convert $num to bytes
+	shl	\$3+2,%r10d		# 4*$num
+	neg	$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr+4*$num
+	# modulo 4096, which covers ret[num], am[num] and n[2*num]
+	# (see bn_exp.c). this is done to allow memory disambiguation
+	# logic do its magic.
+	#
+	lea	-64(%rsp,$num,2),%r11
+	sub	$aptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lpwrx_sp_alt
+	sub	%r11,%rsp		# align with $aptr
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+2*$num)
+	jmp	.Lpwrx_sp_done
+
+.align	32
+.Lpwrx_sp_alt:
+	lea	4096-64(,$num,2),%r10	# 4096-frame-2*$num
+	lea	-64(%rsp,$num,2),%rsp	# alloca(frame+2*$num)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rsp
+.Lpwrx_sp_done:
+	and	\$-64,%rsp
+	mov	$num,%r10	
+	neg	$num
+
+	##############################################################
+	# Stack layout
+	#
+	# +0	saved $num, used in reduction section
+	# +8	&t[2*$num], used in reduction section
+	# +16	intermediate carry bit
+	# +24	top-most carry bit, used in reduction section
+	# +32	saved *n0
+	# +40	saved %rsp
+	# +48	t[2*$num]
+	#
+	pxor	%xmm0,%xmm0
+	movq	$rptr,%xmm1		# save $rptr
+	movq	$nptr,%xmm2		# save $nptr
+	movq	%r10, %xmm3		# -$num
+	movq	$bptr,%xmm4
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lpowerx5_body:
+
+	call	__bn_sqrx8x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_sqrx8x_internal
+
+	mov	%r10,$num		# -num
+	mov	$aptr,$rptr
+	movq	%xmm2,$nptr
+	movq	%xmm4,$bptr
+	mov	40(%rsp),%rax
+
+	call	mulx4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+___
+$code.=<<___ if ($win64);
+	movaps	-88(%rsi),%xmm6
+	movaps	-72(%rsi),%xmm7
+___
+$code.=<<___;
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lpowerx5_epilogue:
+	ret
+.size	bn_powerx5,.-bn_powerx5
+
+.globl	bn_sqrx8x_internal
+.hidden	bn_sqrx8x_internal
+.type	bn_sqrx8x_internal,\@abi-omnipotent
+.align	32
+bn_sqrx8x_internal:
+__bn_sqrx8x_internal:
+	##################################################################
+	# Squaring part:
+	#
+	# a) multiply-n-add everything but a[i]*a[i];
+	# b) shift result of a) by 1 to the left and accumulate
+	#    a[i]*a[i] products;
+	#
+	##################################################################
+	# a[7]a[7]a[6]a[6]a[5]a[5]a[4]a[4]a[3]a[3]a[2]a[2]a[1]a[1]a[0]a[0]
+	#                                                     a[1]a[0]
+	#                                                 a[2]a[0]
+	#                                             a[3]a[0]
+	#                                             a[2]a[1]
+	#                                         a[3]a[1]
+	#                                     a[3]a[2]
+	#
+	#                                         a[4]a[0]
+	#                                     a[5]a[0]
+	#                                 a[6]a[0]
+	#                             a[7]a[0]
+	#                                     a[4]a[1]
+	#                                 a[5]a[1]
+	#                             a[6]a[1]
+	#                         a[7]a[1]
+	#                                 a[4]a[2]
+	#                             a[5]a[2]
+	#                         a[6]a[2]
+	#                     a[7]a[2]
+	#                             a[4]a[3]
+	#                         a[5]a[3]
+	#                     a[6]a[3]
+	#                 a[7]a[3]
+	#
+	#                     a[5]a[4]
+	#                 a[6]a[4]
+	#             a[7]a[4]
+	#             a[6]a[5]
+	#         a[7]a[5]
+	#     a[7]a[6]
+	# a[7]a[7]a[6]a[6]a[5]a[5]a[4]a[4]a[3]a[3]a[2]a[2]a[1]a[1]a[0]a[0]
+___
+{
+my ($zero,$carry)=("%rbp","%rcx");
+my $aaptr=$zero;
+$code.=<<___;
+	lea	48+8(%rsp),$tptr
+	lea	($aptr,$num),$aaptr
+	mov	$num,0+8(%rsp)			# save $num
+	mov	$aaptr,8+8(%rsp)		# save end of $aptr
+	jmp	.Lsqr8x_zero_start
+
+.align	32
+.byte	0x66,0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00
+.Lsqrx8x_zero:
+	.byte	0x3e
+	movdqa	%xmm0,0*8($tptr)
+	movdqa	%xmm0,2*8($tptr)
+	movdqa	%xmm0,4*8($tptr)
+	movdqa	%xmm0,6*8($tptr)
+.Lsqr8x_zero_start:			# aligned at 32
+	movdqa	%xmm0,8*8($tptr)
+	movdqa	%xmm0,10*8($tptr)
+	movdqa	%xmm0,12*8($tptr)
+	movdqa	%xmm0,14*8($tptr)
+	lea	16*8($tptr),$tptr
+	sub	\$64,$num
+	jnz	.Lsqrx8x_zero
+
+	mov	0*8($aptr),%rdx		# a[0], modulo-scheduled
+	#xor	%r9,%r9			# t[1], ex-$num, zero already
+	xor	%r10,%r10
+	xor	%r11,%r11
+	xor	%r12,%r12
+	xor	%r13,%r13
+	xor	%r14,%r14
+	xor	%r15,%r15
+	lea	48+8(%rsp),$tptr
+	xor	$zero,$zero		# cf=0, cf=0
+	jmp	.Lsqrx8x_outer_loop
+
+.align	32
+.Lsqrx8x_outer_loop:
+	mulx	1*8($aptr),%r8,%rax	# a[1]*a[0]
+	adcx	%r9,%r8			# a[1]*a[0]+=t[1]
+	adox	%rax,%r10
+	mulx	2*8($aptr),%r9,%rax	# a[2]*a[0]
+	adcx	%r10,%r9
+	adox	%rax,%r11
+	.byte	0xc4,0xe2,0xab,0xf6,0x86,0x18,0x00,0x00,0x00	# mulx	3*8($aptr),%r10,%rax	# ...
+	adcx	%r11,%r10
+	adox	%rax,%r12
+	.byte	0xc4,0xe2,0xa3,0xf6,0x86,0x20,0x00,0x00,0x00	# mulx	4*8($aptr),%r11,%rax
+	adcx	%r12,%r11
+	adox	%rax,%r13
+	mulx	5*8($aptr),%r12,%rax
+	adcx	%r13,%r12
+	adox	%rax,%r14
+	mulx	6*8($aptr),%r13,%rax
+	adcx	%r14,%r13
+	adox	%r15,%rax
+	mulx	7*8($aptr),%r14,%r15
+	 mov	1*8($aptr),%rdx		# a[1]
+	adcx	%rax,%r14
+	adox	$zero,%r15
+	adc	8*8($tptr),%r15
+	mov	%r8,1*8($tptr)		# t[1]
+	mov	%r9,2*8($tptr)		# t[2]
+	sbb	$carry,$carry		# mov %cf,$carry
+	xor	$zero,$zero		# cf=0, of=0
+
+
+	mulx	2*8($aptr),%r8,%rbx	# a[2]*a[1]
+	mulx	3*8($aptr),%r9,%rax	# a[3]*a[1]
+	adcx	%r10,%r8
+	adox	%rbx,%r9
+	mulx	4*8($aptr),%r10,%rbx	# ...
+	adcx	%r11,%r9
+	adox	%rax,%r10
+	.byte	0xc4,0xe2,0xa3,0xf6,0x86,0x28,0x00,0x00,0x00	# mulx	5*8($aptr),%r11,%rax
+	adcx	%r12,%r10
+	adox	%rbx,%r11
+	.byte	0xc4,0xe2,0x9b,0xf6,0x9e,0x30,0x00,0x00,0x00	# mulx	6*8($aptr),%r12,%rbx
+	adcx	%r13,%r11
+	adox	%r14,%r12
+	.byte	0xc4,0x62,0x93,0xf6,0xb6,0x38,0x00,0x00,0x00	# mulx	7*8($aptr),%r13,%r14
+	 mov	2*8($aptr),%rdx		# a[2]
+	adcx	%rax,%r12
+	adox	%rbx,%r13
+	adcx	%r15,%r13
+	adox	$zero,%r14		# of=0
+	adcx	$zero,%r14		# cf=0
+
+	mov	%r8,3*8($tptr)		# t[3]
+	mov	%r9,4*8($tptr)		# t[4]
+
+	mulx	3*8($aptr),%r8,%rbx	# a[3]*a[2]
+	mulx	4*8($aptr),%r9,%rax	# a[4]*a[2]
+	adcx	%r10,%r8
+	adox	%rbx,%r9
+	mulx	5*8($aptr),%r10,%rbx	# ...
+	adcx	%r11,%r9
+	adox	%rax,%r10
+	.byte	0xc4,0xe2,0xa3,0xf6,0x86,0x30,0x00,0x00,0x00	# mulx	6*8($aptr),%r11,%rax
+	adcx	%r12,%r10
+	adox	%r13,%r11
+	.byte	0xc4,0x62,0x9b,0xf6,0xae,0x38,0x00,0x00,0x00	# mulx	7*8($aptr),%r12,%r13
+	.byte	0x3e
+	 mov	3*8($aptr),%rdx		# a[3]
+	adcx	%rbx,%r11
+	adox	%rax,%r12
+	adcx	%r14,%r12
+	mov	%r8,5*8($tptr)		# t[5]
+	mov	%r9,6*8($tptr)		# t[6]
+	 mulx	4*8($aptr),%r8,%rax	# a[4]*a[3]
+	adox	$zero,%r13		# of=0
+	adcx	$zero,%r13		# cf=0
+
+	mulx	5*8($aptr),%r9,%rbx	# a[5]*a[3]
+	adcx	%r10,%r8
+	adox	%rax,%r9
+	mulx	6*8($aptr),%r10,%rax	# ...
+	adcx	%r11,%r9
+	adox	%r12,%r10
+	mulx	7*8($aptr),%r11,%r12
+	 mov	4*8($aptr),%rdx		# a[4]
+	 mov	5*8($aptr),%r14		# a[5]
+	adcx	%rbx,%r10
+	adox	%rax,%r11
+	 mov	6*8($aptr),%r15		# a[6]
+	adcx	%r13,%r11
+	adox	$zero,%r12		# of=0
+	adcx	$zero,%r12		# cf=0
+
+	mov	%r8,7*8($tptr)		# t[7]
+	mov	%r9,8*8($tptr)		# t[8]
+
+	mulx	%r14,%r9,%rax		# a[5]*a[4]
+	 mov	7*8($aptr),%r8		# a[7]
+	adcx	%r10,%r9
+	mulx	%r15,%r10,%rbx		# a[6]*a[4]
+	adox	%rax,%r10
+	adcx	%r11,%r10
+	mulx	%r8,%r11,%rax		# a[7]*a[4]
+	 mov	%r14,%rdx		# a[5]
+	adox	%rbx,%r11
+	adcx	%r12,%r11
+	#adox	$zero,%rax		# of=0
+	adcx	$zero,%rax		# cf=0
+
+	mulx	%r15,%r14,%rbx		# a[6]*a[5]
+	mulx	%r8,%r12,%r13		# a[7]*a[5]
+	 mov	%r15,%rdx		# a[6]
+	 lea	8*8($aptr),$aptr
+	adcx	%r14,%r11
+	adox	%rbx,%r12
+	adcx	%rax,%r12
+	adox	$zero,%r13
+
+	.byte	0x67,0x67
+	mulx	%r8,%r8,%r14		# a[7]*a[6]
+	adcx	%r8,%r13
+	adcx	$zero,%r14
+
+	cmp	8+8(%rsp),$aptr
+	je	.Lsqrx8x_outer_break
+
+	neg	$carry			# mov $carry,%cf
+	mov	\$-8,%rcx
+	mov	$zero,%r15
+	mov	8*8($tptr),%r8
+	adcx	9*8($tptr),%r9		# +=t[9]
+	adcx	10*8($tptr),%r10	# ...
+	adcx	11*8($tptr),%r11
+	adc	12*8($tptr),%r12
+	adc	13*8($tptr),%r13
+	adc	14*8($tptr),%r14
+	adc	15*8($tptr),%r15
+	lea	($aptr),$aaptr
+	lea	2*64($tptr),$tptr
+	sbb	%rax,%rax		# mov %cf,$carry
+
+	mov	-64($aptr),%rdx		# a[0]
+	mov	%rax,16+8(%rsp)		# offload $carry
+	mov	$tptr,24+8(%rsp)
+
+	#lea	8*8($tptr),$tptr	# see 2*8*8($tptr) above
+	xor	%eax,%eax		# cf=0, of=0
+	jmp	.Lsqrx8x_loop
+
+.align	32
+.Lsqrx8x_loop:
+	mov	%r8,%rbx
+	mulx	0*8($aaptr),%rax,%r8	# a[8]*a[i]
+	adcx	%rax,%rbx		# +=t[8]
+	adox	%r9,%r8
+
+	mulx	1*8($aaptr),%rax,%r9	# ...
+	adcx	%rax,%r8
+	adox	%r10,%r9
+
+	mulx	2*8($aaptr),%rax,%r10
+	adcx	%rax,%r9
+	adox	%r11,%r10
+
+	mulx	3*8($aaptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xa5,0x20,0x00,0x00,0x00	# mulx	4*8($aaptr),%rax,%r12
+	adcx	%rax,%r11
+	adox	%r13,%r12
+
+	mulx	5*8($aaptr),%rax,%r13
+	adcx	%rax,%r12
+	adox	%r14,%r13
+
+	mulx	6*8($aaptr),%rax,%r14
+	 mov	%rbx,($tptr,%rcx,8)	# store t[8+i]
+	 mov	\$0,%ebx
+	adcx	%rax,%r13
+	adox	%r15,%r14
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xbd,0x38,0x00,0x00,0x00	# mulx	7*8($aaptr),%rax,%r15
+	 mov	8($aptr,%rcx,8),%rdx	# a[i]
+	adcx	%rax,%r14
+	adox	%rbx,%r15		# %rbx is 0, of=0
+	adcx	%rbx,%r15		# cf=0
+
+	.byte	0x67
+	inc	%rcx			# of=0
+	jnz	.Lsqrx8x_loop
+
+	lea	8*8($aaptr),$aaptr
+	mov	\$-8,%rcx
+	cmp	8+8(%rsp),$aaptr	# done?
+	je	.Lsqrx8x_break
+
+	sub	16+8(%rsp),%rbx		# mov 16(%rsp),%cf
+	.byte	0x66
+	mov	-64($aptr),%rdx
+	adcx	0*8($tptr),%r8
+	adcx	1*8($tptr),%r9
+	adc	2*8($tptr),%r10
+	adc	3*8($tptr),%r11
+	adc	4*8($tptr),%r12
+	adc	5*8($tptr),%r13
+	adc	6*8($tptr),%r14
+	adc	7*8($tptr),%r15
+	lea	8*8($tptr),$tptr
+	.byte	0x67
+	sbb	%rax,%rax		# mov %cf,%rax
+	xor	%ebx,%ebx		# cf=0, of=0
+	mov	%rax,16+8(%rsp)		# offload carry
+	jmp	.Lsqrx8x_loop
+
+.align	32
+.Lsqrx8x_break:
+	sub	16+8(%rsp),%r8		# consume last carry
+	mov	24+8(%rsp),$carry	# initial $tptr, borrow $carry
+	mov	0*8($aptr),%rdx		# a[8], modulo-scheduled
+	xor	%ebp,%ebp		# xor	$zero,$zero
+	mov	%r8,0*8($tptr)
+	cmp	$carry,$tptr		# cf=0, of=0
+	je	.Lsqrx8x_outer_loop
+
+	mov	%r9,1*8($tptr)
+	 mov	1*8($carry),%r9
+	mov	%r10,2*8($tptr)
+	 mov	2*8($carry),%r10
+	mov	%r11,3*8($tptr)
+	 mov	3*8($carry),%r11
+	mov	%r12,4*8($tptr)
+	 mov	4*8($carry),%r12
+	mov	%r13,5*8($tptr)
+	 mov	5*8($carry),%r13
+	mov	%r14,6*8($tptr)
+	 mov	6*8($carry),%r14
+	mov	%r15,7*8($tptr)
+	 mov	7*8($carry),%r15
+	mov	$carry,$tptr
+	jmp	.Lsqrx8x_outer_loop
+
+.align	32
+.Lsqrx8x_outer_break:
+	mov	%r9,9*8($tptr)		# t[9]
+	 movq	%xmm3,%rcx		# -$num
+	mov	%r10,10*8($tptr)	# ...
+	mov	%r11,11*8($tptr)
+	mov	%r12,12*8($tptr)
+	mov	%r13,13*8($tptr)
+	mov	%r14,14*8($tptr)
+___
+}{
+my $i="%rcx";
+$code.=<<___;
+	lea	48+8(%rsp),$tptr
+	mov	($aptr,$i),%rdx		# a[0]
+
+	mov	8($tptr),$A0[1]		# t[1]
+	xor	$A0[0],$A0[0]		# t[0], of=0, cf=0
+	mov	0+8(%rsp),$num		# restore $num
+	adox	$A0[1],$A0[1]
+	 mov	16($tptr),$A1[0]	# t[2]	# prefetch
+	 mov	24($tptr),$A1[1]	# t[3]	# prefetch
+	#jmp	.Lsqrx4x_shift_n_add	# happens to be aligned
+
+.align	32
+.Lsqrx4x_shift_n_add:
+	mulx	%rdx,%rax,%rbx
+	 adox	$A1[0],$A1[0]
+	adcx	$A0[0],%rax
+	 .byte	0x48,0x8b,0x94,0x0e,0x08,0x00,0x00,0x00	# mov	8($aptr,$i),%rdx	# a[i+1]	# prefetch
+	 .byte	0x4c,0x8b,0x97,0x20,0x00,0x00,0x00	# mov	32($tptr),$A0[0]	# t[2*i+4]	# prefetch
+	 adox	$A1[1],$A1[1]
+	adcx	$A0[1],%rbx
+	 mov	40($tptr),$A0[1]		# t[2*i+4+1]	# prefetch
+	mov	%rax,0($tptr)
+	mov	%rbx,8($tptr)
+
+	mulx	%rdx,%rax,%rbx
+	 adox	$A0[0],$A0[0]
+	adcx	$A1[0],%rax
+	 mov	16($aptr,$i),%rdx	# a[i+2]	# prefetch
+	 mov	48($tptr),$A1[0]	# t[2*i+6]	# prefetch
+	 adox	$A0[1],$A0[1]
+	adcx	$A1[1],%rbx
+	 mov	56($tptr),$A1[1]	# t[2*i+6+1]	# prefetch
+	mov	%rax,16($tptr)
+	mov	%rbx,24($tptr)
+
+	mulx	%rdx,%rax,%rbx
+	 adox	$A1[0],$A1[0]
+	adcx	$A0[0],%rax
+	 mov	24($aptr,$i),%rdx	# a[i+3]	# prefetch
+	 lea	32($i),$i
+	 mov	64($tptr),$A0[0]	# t[2*i+8]	# prefetch
+	 adox	$A1[1],$A1[1]
+	adcx	$A0[1],%rbx
+	 mov	72($tptr),$A0[1]	# t[2*i+8+1]	# prefetch
+	mov	%rax,32($tptr)
+	mov	%rbx,40($tptr)
+
+	mulx	%rdx,%rax,%rbx
+	 adox	$A0[0],$A0[0]
+	adcx	$A1[0],%rax
+	jrcxz	.Lsqrx4x_shift_n_add_break
+	 .byte	0x48,0x8b,0x94,0x0e,0x00,0x00,0x00,0x00	# mov	0($aptr,$i),%rdx	# a[i+4]	# prefetch
+	 adox	$A0[1],$A0[1]
+	adcx	$A1[1],%rbx
+	 mov	80($tptr),$A1[0]	# t[2*i+10]	# prefetch
+	 mov	88($tptr),$A1[1]	# t[2*i+10+1]	# prefetch
+	mov	%rax,48($tptr)
+	mov	%rbx,56($tptr)
+	lea	64($tptr),$tptr
+	nop
+	jmp	.Lsqrx4x_shift_n_add
+
+.align	32
+.Lsqrx4x_shift_n_add_break:
+	adcx	$A1[1],%rbx
+	mov	%rax,48($tptr)
+	mov	%rbx,56($tptr)
+	lea	64($tptr),$tptr		# end of t[] buffer
+___
+}
+######################################################################
+# Montgomery reduction part, "word-by-word" algorithm.
+#
+# This new path is inspired by multiple submissions from Intel, by
+# Shay Gueron, Vlad Krasnov, Erdinc Ozturk, James Guilford,
+# Vinodh Gopal...
+{
+my ($nptr,$carry,$m0)=("%rbp","%rsi","%rdx");
+
+$code.=<<___;
+	movq	%xmm2,$nptr
+sqrx8x_reduction:
+	xor	%eax,%eax		# initial top-most carry bit
+	mov	32+8(%rsp),%rbx		# n0
+	mov	48+8(%rsp),%rdx		# "%r8", 8*0($tptr)
+	lea	-128($nptr,$num,2),%rcx	# end of n[]
+	#lea	48+8(%rsp,$num,2),$tptr	# end of t[] buffer
+	mov	%rcx, 0+8(%rsp)		# save end of n[]
+	mov	$tptr,8+8(%rsp)		# save end of t[]
+
+	lea	48+8(%rsp),$tptr		# initial t[] window
+	jmp	.Lsqrx8x_reduction_loop
+
+.align	32
+.Lsqrx8x_reduction_loop:
+	mov	8*1($tptr),%r9
+	mov	8*2($tptr),%r10
+	mov	8*3($tptr),%r11
+	mov	8*4($tptr),%r12
+	mov	%rdx,%r8
+	imulq	%rbx,%rdx		# n0*a[i]
+	mov	8*5($tptr),%r13
+	mov	8*6($tptr),%r14
+	mov	8*7($tptr),%r15
+	mov	%rax,24+8(%rsp)		# store top-most carry bit
+
+	lea	8*8($tptr),$tptr
+	xor	$carry,$carry		# cf=0,of=0
+	mov	\$-8,%rcx
+	jmp	.Lsqrx8x_reduce
+
+.align	32
+.Lsqrx8x_reduce:
+	mov	%r8, %rbx
+	mulx	16*0($nptr),%rax,%r8	# n[0]
+	adcx	%rbx,%rax		# discarded
+	adox	%r9,%r8
+
+	mulx	16*1($nptr),%rbx,%r9	# n[1]
+	adcx	%rbx,%r8
+	adox	%r10,%r9
+
+	mulx	16*2($nptr),%rbx,%r10
+	adcx	%rbx,%r9
+	adox	%r11,%r10
+
+	mulx	16*3($nptr),%rbx,%r11
+	adcx	%rbx,%r10
+	adox	%r12,%r11
+
+	.byte	0xc4,0x62,0xe3,0xf6,0xa5,0x40,0x00,0x00,0x00	# mulx	16*4($nptr),%rbx,%r12
+	 mov	%rdx,%rax
+	 mov	%r8,%rdx
+	adcx	%rbx,%r11
+	adox	%r13,%r12
+
+	 mulx	32+8(%rsp),%rbx,%rdx	# %rdx discarded
+	 mov	%rax,%rdx
+	 mov	%rax,64+48+8(%rsp,%rcx,8)	# put aside n0*a[i]
+
+	mulx	16*5($nptr),%rax,%r13
+	adcx	%rax,%r12
+	adox	%r14,%r13
+
+	mulx	16*6($nptr),%rax,%r14
+	adcx	%rax,%r13
+	adox	%r15,%r14
+
+	mulx	16*7($nptr),%rax,%r15
+	 mov	%rbx,%rdx
+	adcx	%rax,%r14
+	adox	$carry,%r15		# $carry is 0
+	adcx	$carry,%r15		# cf=0
+
+	.byte	0x67,0x67,0x67
+	inc	%rcx			# of=0
+	jnz	.Lsqrx8x_reduce
+
+	mov	$carry,%rax		# xor	%rax,%rax
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.Lsqrx8x_no_tail
+
+	mov	48+8(%rsp),%rdx		# pull n0*a[0]
+	add	8*0($tptr),%r8
+	lea	16*8($nptr),$nptr
+	mov	\$-8,%rcx
+	adcx	8*1($tptr),%r9
+	adcx	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	lea	8*8($tptr),$tptr
+	sbb	%rax,%rax		# top carry
+
+	xor	$carry,$carry		# of=0, cf=0
+	mov	%rax,16+8(%rsp)
+	jmp	.Lsqrx8x_tail
+
+.align	32
+.Lsqrx8x_tail:
+	mov	%r8,%rbx
+	mulx	16*0($nptr),%rax,%r8
+	adcx	%rax,%rbx
+	adox	%r9,%r8
+
+	mulx	16*1($nptr),%rax,%r9
+	adcx	%rax,%r8
+	adox	%r10,%r9
+
+	mulx	16*2($nptr),%rax,%r10
+	adcx	%rax,%r9
+	adox	%r11,%r10
+
+	mulx	16*3($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xa5,0x40,0x00,0x00,0x00	# mulx	16*4($nptr),%rax,%r12
+	adcx	%rax,%r11
+	adox	%r13,%r12
+
+	mulx	16*5($nptr),%rax,%r13
+	adcx	%rax,%r12
+	adox	%r14,%r13
+
+	mulx	16*6($nptr),%rax,%r14
+	adcx	%rax,%r13
+	adox	%r15,%r14
+
+	mulx	16*7($nptr),%rax,%r15
+	 mov	72+48+8(%rsp,%rcx,8),%rdx	# pull n0*a[i]
+	adcx	%rax,%r14
+	adox	$carry,%r15
+	 mov	%rbx,($tptr,%rcx,8)	# save result
+	 mov	%r8,%rbx
+	adcx	$carry,%r15		# cf=0
+
+	inc	%rcx			# of=0
+	jnz	.Lsqrx8x_tail
+
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.Lsqrx8x_tail_done	# break out of loop
+
+	sub	16+8(%rsp),$carry	# mov 16(%rsp),%cf
+	 mov	48+8(%rsp),%rdx		# pull n0*a[0]
+	 lea	16*8($nptr),$nptr
+	adc	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	lea	8*8($tptr),$tptr
+	sbb	%rax,%rax
+	sub	\$8,%rcx		# mov	\$-8,%rcx
+
+	xor	$carry,$carry		# of=0, cf=0
+	mov	%rax,16+8(%rsp)
+	jmp	.Lsqrx8x_tail
+
+.align	32
+.Lsqrx8x_tail_done:
+	add	24+8(%rsp),%r8		# can this overflow?
+	mov	$carry,%rax		# xor	%rax,%rax
+
+	sub	16+8(%rsp),$carry	# mov 16(%rsp),%cf
+.Lsqrx8x_no_tail:			# %cf is 0 if jumped here
+	adc	8*0($tptr),%r8
+	 movq	%xmm3,%rcx
+	adc	8*1($tptr),%r9
+	 mov	16*7($nptr),$carry
+	 movq	%xmm2,$nptr		# restore $nptr
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	adc	%rax,%rax		# top-most carry
+
+	mov	32+8(%rsp),%rbx		# n0
+	mov	8*8($tptr,%rcx),%rdx	# modulo-scheduled "%r8"
+
+	mov	%r8,8*0($tptr)		# store top 512 bits
+	 lea	8*8($tptr),%r8		# borrow %r8
+	mov	%r9,8*1($tptr)
+	mov	%r10,8*2($tptr)
+	mov	%r11,8*3($tptr)
+	mov	%r12,8*4($tptr)
+	mov	%r13,8*5($tptr)
+	mov	%r14,8*6($tptr)
+	mov	%r15,8*7($tptr)
+
+	lea	8*8($tptr,%rcx),$tptr	# start of current t[] window
+	cmp	8+8(%rsp),%r8		# end of t[]?
+	jb	.Lsqrx8x_reduction_loop
+___
+}
+##############################################################
+# Post-condition, 4x unrolled
+#
+{
+my ($rptr,$nptr)=("%rdx","%rbp");
+my @ri=map("%r$_",(10..13));
+my @ni=map("%r$_",(14..15));
+$code.=<<___;
+	xor	%rbx,%rbx
+	sub	%r15,%rsi		# compare top-most words
+	adc	%rbx,%rbx
+	mov	%rcx,%r10		# -$num
+	.byte	0x67
+	or	%rbx,%rax
+	.byte	0x67
+	mov	%rcx,%r9		# -$num
+	xor	\$1,%rax
+	sar	\$3+2,%rcx		# cf=0
+	#lea	48+8(%rsp,%r9),$tptr
+	lea	($nptr,%rax,8),$nptr
+	movq	%xmm1,$rptr		# restore $rptr
+	movq	%xmm1,$aptr		# prepare for back-to-back call
+	jmp	.Lsqrx4x_sub
+
+.align	32
+.Lsqrx4x_sub:
+	.byte	0x66
+	mov	8*0($tptr),%r12
+	mov	8*1($tptr),%r13
+	sbb	16*0($nptr),%r12
+	mov	8*2($tptr),%r14
+	sbb	16*1($nptr),%r13
+	mov	8*3($tptr),%r15
+	lea	8*4($tptr),$tptr
+	sbb	16*2($nptr),%r14
+	mov	%r12,8*0($rptr)
+	sbb	16*3($nptr),%r15
+	lea	16*4($nptr),$nptr
+	mov	%r13,8*1($rptr)
+	mov	%r14,8*2($rptr)
+	mov	%r15,8*3($rptr)
+	lea	8*4($rptr),$rptr
+
+	inc	%rcx
+	jnz	.Lsqrx4x_sub
+___
+}
+$code.=<<___;
+	neg	%r9			# restore $num
+
+	ret
+.size	bn_sqrx8x_internal,.-bn_sqrx8x_internal
+___
+}}}
+{
+my ($inp,$num,$tbl,$idx)=$win64?("%rcx","%edx","%r8", "%r9d") : # Win64 order
+				("%rdi","%esi","%rdx","%ecx");  # Unix order
+my $out=$inp;
+my $STRIDE=2**5*8;
+my $N=$STRIDE/4;
+
+$code.=<<___;
+.globl	bn_get_bits5
+.type	bn_get_bits5,\@abi-omnipotent
+.align	16
+bn_get_bits5:
+	mov	$inp,%r10
+	mov	$num,%ecx
+	shr	\$3,$num
+	movzw	(%r10,$num),%eax
+	and	\$7,%ecx
+	shrl	%cl,%eax
+	and	\$31,%eax
+	ret
+.size	bn_get_bits5,.-bn_get_bits5
+
+.globl	bn_scatter5
+.type	bn_scatter5,\@abi-omnipotent
+.align	16
+bn_scatter5:
+	cmp	\$0, $num
+	jz	.Lscatter_epilogue
+	lea	($tbl,$idx,8),$tbl
+.Lscatter:
+	mov	($inp),%rax
+	lea	8($inp),$inp
+	mov	%rax,($tbl)
+	lea	32*8($tbl),$tbl
+	sub	\$1,$num
+	jnz	.Lscatter
+.Lscatter_epilogue:
+	ret
+.size	bn_scatter5,.-bn_scatter5
+
+.globl	bn_gather5
+.type	bn_gather5,\@abi-omnipotent
+.align	16
+bn_gather5:
+___
+$code.=<<___ if ($win64);
+.LSEH_begin_bn_gather5:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x83,0xec,0x28		#sub	\$0x28,%rsp
+	.byte	0x0f,0x29,0x34,0x24		#movaps	%xmm6,(%rsp)
+	.byte	0x0f,0x29,0x7c,0x24,0x10	#movdqa	%xmm7,0x10(%rsp)
+___
+$code.=<<___;
+	mov	$idx,%r11d
+	shr	\$`log($N/8)/log(2)`,$idx
+	and	\$`$N/8-1`,%r11
+	not	$idx
+	lea	.Lmagic_masks(%rip),%rax
+	and	\$`2**5/($N/8)-1`,$idx	# 5 is "window size"
+	lea	128($tbl,%r11,8),$tbl	# pointer within 1st cache line
+	movq	0(%rax,$idx,8),%xmm4	# set of masks denoting which
+	movq	8(%rax,$idx,8),%xmm5	# cache line contains element
+	movq	16(%rax,$idx,8),%xmm6	# denoted by 7th argument
+	movq	24(%rax,$idx,8),%xmm7
+	jmp	.Lgather
+.align	16
+.Lgather:
+	movq	`0*$STRIDE/4-128`($tbl),%xmm0
+	movq	`1*$STRIDE/4-128`($tbl),%xmm1
+	pand	%xmm4,%xmm0
+	movq	`2*$STRIDE/4-128`($tbl),%xmm2
+	pand	%xmm5,%xmm1
+	movq	`3*$STRIDE/4-128`($tbl),%xmm3
+	pand	%xmm6,%xmm2
+	por	%xmm1,%xmm0
+	pand	%xmm7,%xmm3
+	.byte	0x67,0x67
+	por	%xmm2,%xmm0
+	lea	$STRIDE($tbl),$tbl
+	por	%xmm3,%xmm0
+
+	movq	%xmm0,($out)		# m0=bp[0]
+	lea	8($out),$out
+	sub	\$1,$num
+	jnz	.Lgather
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	lea	0x28(%rsp),%rsp
+___
+$code.=<<___;
+	ret
+.LSEH_end_bn_gather5:
+.size	bn_gather5,.-bn_gather5
+___
+}
+$code.=<<___;
+.align	64
+.Lmagic_masks:
+	.long	0,0, 0,0, 0,0, -1,-1
+	.long	0,0, 0,0, 0,0,  0,0
+.asciz	"Montgomery Multiplication with scatter/gather for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	mul_handler,\@abi-omnipotent
+.align	16
+mul_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<end of prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	.Lmul_epilogue(%rip),%r10
+	cmp	%r10,%rbx
+	jb	.Lbody_40
+
+	mov	192($context),%r10	# pull $num
+	mov	8(%rax,%r10,8),%rax	# pull saved stack pointer
+	jmp	.Lbody_proceed
+
+.Lbody_40:
+	mov	40(%rax),%rax		# pull saved stack pointer
+.Lbody_proceed:
+
+	movaps	-88(%rax),%xmm0
+	movaps	-72(%rax),%xmm1
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+	movups	%xmm0,512($context)	# restore context->Xmm6
+	movups	%xmm1,528($context)	# restore context->Xmm7
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	mul_handler,.-mul_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_bn_mul_mont_gather5
+	.rva	.LSEH_end_bn_mul_mont_gather5
+	.rva	.LSEH_info_bn_mul_mont_gather5
+
+	.rva	.LSEH_begin_bn_mul4x_mont_gather5
+	.rva	.LSEH_end_bn_mul4x_mont_gather5
+	.rva	.LSEH_info_bn_mul4x_mont_gather5
+
+	.rva	.LSEH_begin_bn_power5
+	.rva	.LSEH_end_bn_power5
+	.rva	.LSEH_info_bn_power5
+
+	.rva	.LSEH_begin_bn_from_mont8x
+	.rva	.LSEH_end_bn_from_mont8x
+	.rva	.LSEH_info_bn_from_mont8x
+___
+$code.=<<___ if ($addx);
+	.rva	.LSEH_begin_bn_mulx4x_mont_gather5
+	.rva	.LSEH_end_bn_mulx4x_mont_gather5
+	.rva	.LSEH_info_bn_mulx4x_mont_gather5
+
+	.rva	.LSEH_begin_bn_powerx5
+	.rva	.LSEH_end_bn_powerx5
+	.rva	.LSEH_info_bn_powerx5
+___
+$code.=<<___;
+	.rva	.LSEH_begin_bn_gather5
+	.rva	.LSEH_end_bn_gather5
+	.rva	.LSEH_info_bn_gather5
+
+.section	.xdata
+.align	8
+.LSEH_info_bn_mul_mont_gather5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul_body,.Lmul_epilogue		# HandlerData[]
+.align	8
+.LSEH_info_bn_mul4x_mont_gather5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul4x_body,.Lmul4x_epilogue		# HandlerData[]
+.align	8
+.LSEH_info_bn_power5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lpower5_body,.Lpower5_epilogue		# HandlerData[]
+.align	8
+.LSEH_info_bn_from_mont8x:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lfrom_body,.Lfrom_epilogue		# HandlerData[]
+___
+$code.=<<___ if ($addx);
+.align	8
+.LSEH_info_bn_mulx4x_mont_gather5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmulx4x_body,.Lmulx4x_epilogue		# HandlerData[]
+.align	8
+.LSEH_info_bn_powerx5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lpowerx5_body,.Lpowerx5_epilogue	# HandlerData[]
+___
+$code.=<<___;
+.align	8
+.LSEH_info_bn_gather5:
+        .byte   0x01,0x0d,0x05,0x00
+        .byte   0x0d,0x78,0x01,0x00	#movaps	0x10(rsp),xmm7
+        .byte   0x08,0x68,0x00,0x00	#movaps	(rsp),xmm6
+        .byte   0x04,0x42,0x00,0x00	#sub	rsp,0x28
+.align	8
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/bn.c b/crypto/bn/bn.c
new file mode 100644
index 0000000..a996a47
--- /dev/null
+++ b/crypto/bn/bn.c
@@ -0,0 +1,336 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include <limits.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+BIGNUM *BN_new(void) {
+  BIGNUM *bn = OPENSSL_malloc(sizeof(BIGNUM));
+
+  if (bn == NULL) {
+    OPENSSL_PUT_ERROR(BN, BN_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(bn, 0, sizeof(BIGNUM));
+  bn->flags = BN_FLG_MALLOCED;
+
+  return bn;
+}
+
+void BN_init(BIGNUM *bn) {
+  memset(bn, 0, sizeof(BIGNUM));
+}
+
+void BN_free(BIGNUM *bn) {
+  if (bn == NULL) {
+    return;
+  }
+
+  if (bn->d != NULL && (bn->flags & BN_FLG_STATIC_DATA) == 0) {
+    OPENSSL_free(bn->d);
+  }
+
+  if (bn->flags & BN_FLG_MALLOCED) {
+    OPENSSL_free(bn);
+  } else {
+    bn->d = NULL;
+  }
+}
+
+void BN_clear_free(BIGNUM *bn) {
+  char should_free;
+
+  if (bn == NULL) {
+    return;
+  }
+
+  if (bn->d != NULL) {
+    OPENSSL_cleanse(bn->d, bn->dmax * sizeof(bn->d[0]));
+    if ((bn->flags & BN_FLG_STATIC_DATA) == 0) {
+      OPENSSL_free(bn->d);
+    }
+  }
+
+  should_free = (bn->flags & BN_FLG_MALLOCED) != 0;
+  OPENSSL_cleanse(bn, sizeof(BIGNUM));
+  if (should_free) {
+    OPENSSL_free(bn);
+  }
+}
+
+BIGNUM *BN_dup(const BIGNUM *src) {
+  BIGNUM *copy;
+
+  if (src == NULL) {
+    return NULL;
+  }
+
+  copy = BN_new();
+  if (copy == NULL) {
+    return NULL;
+  }
+
+  if (!BN_copy(copy, src)) {
+    BN_free(copy);
+    return NULL;
+  }
+
+  return copy;
+}
+
+BIGNUM *BN_copy(BIGNUM *dest, const BIGNUM *src) {
+  if (src == dest) {
+    return dest;
+  }
+
+  if (bn_wexpand(dest, src->top) == NULL) {
+    return NULL;
+  }
+
+  memcpy(dest->d, src->d, sizeof(src->d[0]) * src->top);
+
+  dest->top = src->top;
+  dest->neg = src->neg;
+  return dest;
+}
+
+void BN_clear(BIGNUM *bn) {
+  if (bn->d != NULL) {
+    memset(bn->d, 0, bn->dmax * sizeof(bn->d[0]));
+  }
+
+  bn->top = 0;
+  bn->neg = 0;
+}
+
+const BIGNUM *BN_value_one(void) {
+  static const BN_ULONG data_one = 1;
+  static const BIGNUM const_one = {(BN_ULONG *)&data_one, 1, 1, 0,
+                                   BN_FLG_STATIC_DATA};
+
+  return &const_one;
+}
+
+void BN_with_flags(BIGNUM *out, const BIGNUM *in, int flags) {
+  memcpy(out, in, sizeof(BIGNUM));
+  out->flags &= ~BN_FLG_MALLOCED;
+  out->flags |= BN_FLG_STATIC_DATA | flags;
+}
+
+/* BN_num_bits_word returns the minimum number of bits needed to represent the
+ * value in |l|. */
+unsigned BN_num_bits_word(BN_ULONG l) {
+  static const unsigned char bits[256] = {
+      0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+      5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};
+
+#if defined(OPENSSL_64_BIT)
+  if (l & 0xffffffff00000000L) {
+    if (l & 0xffff000000000000L) {
+      if (l & 0xff00000000000000L) {
+        return (bits[(int)(l >> 56)] + 56);
+      } else
+        return (bits[(int)(l >> 48)] + 48);
+    } else {
+      if (l & 0x0000ff0000000000L) {
+        return (bits[(int)(l >> 40)] + 40);
+      } else
+        return (bits[(int)(l >> 32)] + 32);
+    }
+  } else
+#endif
+  {
+    if (l & 0xffff0000L) {
+      if (l & 0xff000000L) {
+        return (bits[(int)(l >> 24L)] + 24);
+      } else {
+        return (bits[(int)(l >> 16L)] + 16);
+      }
+    } else {
+      if (l & 0xff00L) {
+        return (bits[(int)(l >> 8)] + 8);
+      } else {
+        return (bits[(int)(l)]);
+      }
+    }
+  }
+}
+
+unsigned BN_num_bits(const BIGNUM *bn) {
+  const int max = bn->top - 1;
+
+  if (BN_is_zero(bn)) {
+    return 0;
+  }
+
+  return max*BN_BITS2 + BN_num_bits_word(bn->d[max]);
+}
+
+unsigned BN_num_bytes(const BIGNUM *bn) {
+  return (BN_num_bits(bn) + 7) / 8;
+}
+
+void BN_zero(BIGNUM *bn) {
+  bn->top = bn->neg = 0;
+}
+
+int BN_one(BIGNUM *bn) {
+  return BN_set_word(bn, 1);
+}
+
+int BN_set_word(BIGNUM *bn, BN_ULONG value) {
+  if (value == 0) {
+    BN_zero(bn);
+    return 1;
+  }
+
+  if (bn_wexpand(bn, 1) == NULL) {
+    return 0;
+  }
+
+  bn->neg = 0;
+  bn->d[0] = value;
+  bn->top = 1;
+  return 1;
+}
+
+int BN_is_negative(const BIGNUM *bn) {
+  return bn->neg != 0;
+}
+
+void BN_set_negative(BIGNUM *bn, int sign) {
+  if (sign && !BN_is_zero(bn)) {
+    bn->neg = 1;
+  } else {
+    bn->neg = 0;
+  }
+}
+
+BIGNUM *bn_wexpand(BIGNUM *bn, unsigned words) {
+  BN_ULONG *a;
+
+  if (words <= (unsigned) bn->dmax) {
+    return bn;
+  }
+
+  if (words > (INT_MAX / (4 * BN_BITS2))) {
+    OPENSSL_PUT_ERROR(BN, bn_wexpand, BN_R_BIGNUM_TOO_LONG);
+    return NULL;
+  }
+
+  if (bn->flags & BN_FLG_STATIC_DATA) {
+    OPENSSL_PUT_ERROR(BN, bn_wexpand, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA);
+    return NULL;
+  }
+
+  a = (BN_ULONG *)OPENSSL_malloc(sizeof(BN_ULONG) * words);
+  if (a == NULL) {
+    OPENSSL_PUT_ERROR(BN, bn_wexpand, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memcpy(a, bn->d, sizeof(BN_ULONG) * bn->top);
+
+  if (bn->d) {
+    OPENSSL_free(bn->d);
+  }
+  bn->d = a;
+  bn->dmax = words;
+
+  return bn;
+}
+
+BIGNUM *bn_expand(BIGNUM *bn, unsigned bits) {
+  return bn_wexpand(bn, (bits+BN_BITS2-1)/BN_BITS2);
+}
+
+void bn_correct_top(BIGNUM *bn) {
+  BN_ULONG *ftl;
+  int tmp_top = bn->top;
+
+  if (tmp_top > 0) {
+    for (ftl = &(bn->d[tmp_top - 1]); tmp_top > 0; tmp_top--) {
+      if (*(ftl--)) {
+        break;
+      }
+    }
+    bn->top = tmp_top;
+  }
+}
+
+int BN_get_flags(const BIGNUM *bn, int flags) {
+  return bn->flags & flags;
+}
+
+void BN_set_flags(BIGNUM *bn, int flags) {
+  bn->flags |= flags;
+}
diff --git a/crypto/bn/bn.h b/crypto/bn/bn.h
new file mode 100644
index 0000000..16ce65f
--- /dev/null
+++ b/crypto/bn/bn.h
@@ -0,0 +1,790 @@
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the Eric Young open source
+ * license provided above.
+ *
+ * The binary polynomial arithmetic software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#ifndef OPENSSL_HEADER_BN_H
+#define OPENSSL_HEADER_BN_H
+
+#include <openssl/base.h>
+
+#include <stdio.h>  /* for FILE* */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* BN provides support for working with arbitary sized integers. For example,
+ * although the largest integer supported by the compiler might be 64 bits, BN
+ * will allow you to work with numbers until you run out of memory. */
+
+
+/* BN_ULONG is the native word size when working with big integers. */
+#if defined(OPENSSL_64_BIT)
+#define BN_ULONG uint64_t
+#define BN_BITS2 64
+#elif defined(OPENSSL_32_BIT)
+#define BN_ULONG uint32_t
+#define BN_BITS2 32
+#else
+#error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
+#endif
+
+
+/* Allocation and freeing. */
+
+/* BN_new creates a new, allocated BIGNUM and initialises it. */
+BIGNUM *BN_new(void);
+
+/* BN_init initialises a stack allocated |BIGNUM|. */
+void BN_init(BIGNUM *bn);
+
+/* BN_free frees the data referenced by |bn| and, if |bn| was originally
+ * allocated on the heap, frees |bn| also. */
+void BN_free(BIGNUM *bn);
+
+/* BN_clear_free erases and frees the data referenced by |bn| and, if |bn| was
+ * originally allocated on the heap, frees |bn| also. */
+void BN_clear_free(BIGNUM *bn);
+
+/* BN_dup allocates a new BIGNUM and sets it equal to |src|. It returns the
+ * allocated BIGNUM on success or NULL otherwise. */
+BIGNUM *BN_dup(const BIGNUM *src);
+
+/* BN_copy sets |dest| equal to |src| and returns |dest|. */
+BIGNUM *BN_copy(BIGNUM *dest, const BIGNUM *src);
+
+/* BN_clear sets |bn| to zero and erases the old data. */
+void BN_clear(BIGNUM *bn);
+
+/* BN_value_one returns a static BIGNUM with value 1. */
+const BIGNUM *BN_value_one(void);
+
+/* BN_with_flags initialises a stack allocated |BIGNUM| with pointers to the
+ * contents of |in| but with |flags| ORed into the flags field.
+ *
+ * Note: the two BIGNUMs share state and so |out| should /not/ be passed to
+ * |BN_free|. */
+void BN_with_flags(BIGNUM *out, const BIGNUM *in, int flags);
+
+
+/* Basic functions. */
+
+/* BN_num_bits returns the minimum number of bits needed to represent the
+ * absolute value of |bn|. */
+unsigned BN_num_bits(const BIGNUM *bn);
+
+/* BN_num_bytes returns the minimum number of bytes needed to represent the
+ * absolute value of |bn|. */
+unsigned BN_num_bytes(const BIGNUM *bn);
+
+/* BN_zero sets |bn| to zero. */
+void BN_zero(BIGNUM *bn);
+
+/* BN_one sets |bn| to one. It returns one on success or zero on allocation
+ * failure. */
+int BN_one(BIGNUM *bn);
+
+/* BN_set_word sets |bn| to |value|. It returns one on success or zero on
+ * allocation failure. */
+int BN_set_word(BIGNUM *bn, BN_ULONG value);
+
+/* BN_set_negative sets the sign of |bn|. */
+void BN_set_negative(BIGNUM *bn, int sign);
+
+/* BN_is_negative returns one if |bn| is negative and zero otherwise. */
+int BN_is_negative(const BIGNUM *bn);
+
+/* BN_get_flags returns |bn->flags| & |flags|. */
+int BN_get_flags(const BIGNUM *bn, int flags);
+
+/* BN_set_flags sets |flags| on |bn|. */
+void BN_set_flags(BIGNUM *bn, int flags);
+
+
+/* Conversion functions. */
+
+/* BN_bin2bn sets |*ret| to the value of |len| bytes from |in|, interpreted as
+ * a big-endian number, and returns |ret|. If |ret| is NULL then a fresh
+ * |BIGNUM| is allocated and returned. It returns NULL on allocation
+ * failure. */
+BIGNUM *BN_bin2bn(const uint8_t *in, size_t len, BIGNUM *ret);
+
+/* BN_bn2bin serialises the absolute value of |in| to |out| as a big-endian
+ * integer, which must have |BN_num_bytes| of space available. It returns the
+ * number of bytes written. */
+size_t BN_bn2bin(const BIGNUM *in, uint8_t *out);
+
+/* BN_bn2hex returns an allocated string that contains a NUL-terminated, hex
+ * representation of |bn|. If |bn| is negative, the first char in the resulting
+ * string will be '-'. Returns NULL on allocation failure. */
+char *BN_bn2hex(const BIGNUM *bn);
+
+/* BN_hex2bn parses the leading hex number from |in|, which may be proceeded by
+ * a '-' to indicate a negative number and may contain trailing, non-hex data.
+ * If |outp| is not NULL, it constructs a BIGNUM equal to the hex number and
+ * stores it in |*outp|. If |*outp| is NULL then it allocates a new BIGNUM and
+ * updates |*outp|. It returns the number of bytes of |in| processed or zero on
+ * error. */
+int BN_hex2bn(BIGNUM **outp, const char *in);
+
+/* BN_bn2dec returns an allocated string that contains a NUL-terminated,
+ * decimal representation of |bn|. If |bn| is negative, the first char in the
+ * resulting string will be '-'. Returns NULL on allocation failure. */
+char *BN_bn2dec(const BIGNUM *a);
+
+/* BN_dec2bn parses the leading decimal number from |in|, which may be
+ * proceeded by a '-' to indicate a negative number and may contain trailing,
+ * non-decimal data. If |outp| is not NULL, it constructs a BIGNUM equal to the
+ * decimal number and stores it in |*outp|. If |*outp| is NULL then it
+ * allocates a new BIGNUM and updates |*outp|. It returns the number of bytes
+ * of |in| processed or zero on error. */
+int BN_dec2bn(BIGNUM **outp, const char *in);
+
+/* BN_asc2bn acts like |BN_dec2bn| or |BN_hex2bn| depending on whether |in|
+ * begins with "0X" or "0x" (indicating hex) or not (indicating decimal). A
+ * leading '-' is still permitted and comes before the optional 0X/0x. It
+ * returns one on success or zero on error. */
+int BN_asc2bn(BIGNUM **outp, const char *in);
+
+/* BN_print writes a hex encoding of |a| to |bio|. It returns one on success
+ * and zero on error. */
+int BN_print(BIO *bio, const BIGNUM *a);
+
+/* BN_print_fp acts like |BIO_print|, but wraps |fp| in a |BIO| first. */
+int BN_print_fp(FILE *fp, const BIGNUM *a);
+
+/* BN_get_word returns the absolute value of |bn| as a single word. If |bn| is
+ * too large to be represented as a single word, the maximum possible value
+ * will be returned. */
+BN_ULONG BN_get_word(const BIGNUM *bn);
+
+
+/* BIGNUM pools.
+ *
+ * Certain BIGNUM operations need to use many temporary variables and
+ * allocating and freeing them can be quite slow. Thus such opertions typically
+ * take a |BN_CTX| parameter, which contains a pool of |BIGNUMs|. The |ctx|
+ * argument to a public function may be NULL, in which case a local |BN_CTX|
+ * will be created just for the lifetime of that call.
+ *
+ * A function must call |BN_CTX_start| first. Then, |BN_CTX_get| may be called
+ * repeatedly to obtain temporary |BIGNUM|s. All |BN_CTX_get| calls must be made
+ * before calling any other functions that use the |ctx| as an argument.
+ *
+ * Finally, |BN_CTX_end| must be called before returning from the function.
+ * When |BN_CTX_end| is called, the |BIGNUM| pointers obtained from
+ * |BN_CTX_get| become invalid. */
+
+/* BN_CTX_new returns a new, empty BN_CTX or NULL on allocation failure. */
+BN_CTX *BN_CTX_new(void);
+
+/* BN_CTX_free frees all BIGNUMs contained in |ctx| and then frees |ctx|
+ * itself. */
+void BN_CTX_free(BN_CTX *ctx);
+
+/* BN_CTX_start "pushes" a new entry onto the |ctx| stack and allows future
+ * calls to |BN_CTX_get|. */
+void BN_CTX_start(BN_CTX *ctx);
+
+/* BN_CTX_get returns a new |BIGNUM|, or NULL on allocation failure. Once
+ * |BN_CTX_get| has returned NULL, all future calls will also return NULL until
+ * |BN_CTX_end| is called. */
+BIGNUM *BN_CTX_get(BN_CTX *ctx);
+
+/* BN_CTX_end invalidates all |BIGNUM|s returned from |BN_CTX_get| since the
+ * matching |BN_CTX_start| call. */
+void BN_CTX_end(BN_CTX *ctx);
+
+
+/* Simple arithmetic */
+
+/* BN_add sets |r| = |a| + |b|, where |r| may be the same pointer as either |a|
+ * or |b|. It returns one on success and zero on allocation failure. */
+int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+
+/* BN_uadd sets |r| = |a| + |b|, where |a| and |b| are non-negative and |r| may
+ * be the same pointer as either |a| or |b|. It returns one on success and zero
+ * on allocation failure. */
+int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+
+/* BN_add_word adds |w| to |a|. It returns one on success and zero otherwise. */
+int BN_add_word(BIGNUM *a, BN_ULONG w);
+
+/* BN_sub sets |r| = |a| + |b|, where |r| must be a distinct pointer from |a|
+ * and |b|. It returns one on success and zero on allocation failure. */
+int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+
+/* BN_usub sets |r| = |a| + |b|, where |a| and |b| are non-negative integers,
+ * |b| < |a| and |r| must be a distinct pointer from |a| and |b|. It returns
+ * one on success and zero on allocation failure. */
+int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+
+/* BN_sub_word subtracts |w| from |a|. It returns one on success and zero on
+ * allocation failure. */
+int BN_sub_word(BIGNUM *a, BN_ULONG w);
+
+/* BN_mul sets |r| = |a| * |b|, where |r| may be the same pointer as |a| or
+ * |b|. Returns one on success and zero otherwise. */
+int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+
+/* BN_mul_word sets |bn| = |bn| * |w|. It returns one on success or zero on
+ * allocation failure. */
+int BN_mul_word(BIGNUM *bn, BN_ULONG w);
+
+/* BN_sqr sets |r| = |a|^2 (i.e. squares), where |r| may be the same pointer as
+ * |a|. Returns one on success and zero otherwise. This is more efficient than
+ * BN_mul(r, a, a, ctx). */
+int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx);
+
+/* BN_div divides |numerator| by |divisor| and places the result in |quotient|
+ * and the remainder in |rem|. Either of |quotient| or |rem| may be NULL, in
+ * which case the respective value is not returned. The result is rounded
+ * towards zero; thus if |numerator| is negative, the remainder will be zero or
+ * negative. It returns one on success or zero on error. */
+int BN_div(BIGNUM *quotient, BIGNUM *rem, const BIGNUM *numerator,
+           const BIGNUM *divisor, BN_CTX *ctx);
+
+/* BN_div_word sets |numerator| = |numerator|/|divisor| and returns the
+ * remainder or (BN_ULONG)-1 on error. */
+BN_ULONG BN_div_word(BIGNUM *numerator, BN_ULONG divisor);
+
+
+/* Comparison functions */
+
+/* BN_cmp returns a value less than, equal to or greater than zero if |a| is
+ * less than, equal to or greater than |b|, respectively. */
+int BN_cmp(const BIGNUM *a, const BIGNUM *b);
+
+/* BN_ucmp returns a value less than, equal to or greater than zero if the
+ * absolute value of |a| is less than, equal to or greater than the absolute
+ * value of |b|, respectively. */
+int BN_ucmp(const BIGNUM *a, const BIGNUM *b);
+
+/* BN_abs_is_word returns one if the absolute value of |bn| equals |w| and zero
+ * otherwise. */
+int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w);
+
+/* BN_is_zero returns one if |bn| is zero and zero otherwise. */
+int BN_is_zero(const BIGNUM *bn);
+
+/* BN_is_one returns one if |bn| equals one and zero otherwise. */
+int BN_is_one(const BIGNUM *bn);
+
+/* BN_is_word returns one if |bn| is exactly |w| and zero otherwise. */
+int BN_is_word(const BIGNUM *bn, BN_ULONG w);
+
+/* BN_is_odd returns one if |bn| is odd and zero otherwise. */
+int BN_is_odd(const BIGNUM *bn);
+
+
+/* Bitwise operations. */
+
+/* BN_lshift sets |r| equal to |a| << n. The |a| and |r| arguments may be the
+ * same |BIGNUM|. It returns one on success and zero on allocation failure. */
+int BN_lshift(BIGNUM *r, const BIGNUM *a, int n);
+
+/* BN_lshift1 sets |r| equal to |a| << 1, where |r| and |a| may be the same
+ * pointer. It returns one on success and zero on allocation failure. */
+int BN_lshift1(BIGNUM *r, const BIGNUM *a);
+
+/* BN_rshift sets |r| equal to |a| >> n, where |r| and |a| may be the same
+ * pointer. It returns one on success and zero on allocation failure. */
+int BN_rshift(BIGNUM *r, const BIGNUM *a, int n);
+
+/* BN_rshift1 sets |r| equal to |a| >> 1, where |r| and |a| may be the same
+ * pointer. It returns one on success and zero on allocation failure. */
+int BN_rshift1(BIGNUM *r, const BIGNUM *a);
+
+/* BN_set_bit sets the |n|th, least-significant bit in |a|. For example, if |a|
+ * is 2 then setting bit zero will make it 3. It returns one on success or zero
+ * on allocation failure. */
+int BN_set_bit(BIGNUM *a, int n);
+
+/* BN_clear_bit clears the |n|th, least-significant bit in |a|. For example, if
+ * |a| is 3, clearing bit zero will make it two. It returns one on success or
+ * zero on allocation failure. */
+int BN_clear_bit(BIGNUM *a, int n);
+
+/* BN_is_bit_set returns the value of the |n|th, least-significant bit in |a|,
+ * or zero if the bit doesn't exist. */
+int BN_is_bit_set(const BIGNUM *a, int n);
+
+/* BN_mask_bits truncates |a| so that it is only |n| bits long. It returns one
+ * on success or zero if |n| is greater than the length of |a| already. */
+int BN_mask_bits(BIGNUM *a, int n);
+
+
+/* Modulo arithmetic. */
+
+/* BN_mod_word returns |a| mod |w|. */
+BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w);
+
+/* BN_mod is a helper macro that calls |BN_div| and discards the quotient. */
+#define BN_mod(rem, numerator, divisor, ctx) \
+  BN_div(NULL, (rem), (numerator), (divisor), (ctx))
+
+/* BN_nnmod is a non-negative modulo function. It acts like |BN_mod|, but 0 <=
+ * |rem| < |divisor| is always true. */
+int BN_nnmod(BIGNUM *rem, const BIGNUM *numerator, const BIGNUM *divisor,
+             BN_CTX *ctx);
+
+/* BN_mod_add sets |r| = |a| + |b| mod |m|. It returns one on success and zero
+ * on error. */
+int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx);
+
+/* BN_mod_add_quick acts like |BN_mod_add| but requires that |a| and |b| be
+ * non-negative and less than |m|. */
+int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m);
+
+/* BN_mod_sub sets |r| = |a| - |b| mod |m|. It returns one on success and zero
+ * on error. */
+int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx);
+
+/* BN_mod_sub_quick acts like |BN_mod_sub| but requires that |a| and |b| be
+ * non-negative and less than |m|. */
+int BN_mod_sub_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m);
+
+/* BN_mod_mul sets |r| = |a|*|b| mod |m|. It returns one on success and zero
+ * on error. */
+int BN_mod_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx);
+
+/* BN_mod_mul sets |r| = |a|^2 mod |m|. It returns one on success and zero
+ * on error. */
+int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
+
+/* BN_mod_lshift sets |r| = (|a| << n) mod |m|, where |r| and |a| may be the
+ * same pointer. It returns one on success and zero on error. */
+int BN_mod_lshift(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
+                  BN_CTX *ctx);
+
+/* BN_mod_lshift_quick acts like |BN_mod_lshift| but requires that |a| be
+ * non-negative and less than |m|. */
+int BN_mod_lshift_quick(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m);
+
+/* BN_mod_lshift1 sets |r| = (|a| << 1) mod |m|, where |r| and |a| may be the
+ * same pointer. It returns one on success and zero on error. */
+int BN_mod_lshift1(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
+
+/* BN_mod_lshift1_quick acts like |BN_mod_lshift1| but requires that |a| be
+ * non-negative and less than |m|. */
+int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *m);
+
+/* BN_mod_sqrt returns a |BIGNUM|, r, such that r^2 == a (mod p). */
+BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx);
+
+
+/* Random and prime number generation. */
+
+/* BN_rand sets |rnd| to a random number of length |bits|. If |top| is zero,
+ * the most-significant bit will be set. If |top| is one, the two most
+ * significant bits will be set.
+ *
+ * If |top| is -1 then no extra action will be taken and |BN_num_bits(rnd)| may
+ * not equal |bits| if the most significant bits randomly ended up as zeros.
+ *
+ * If |bottom| is non-zero, the least-significant bit will be set. The function
+ * returns one on success or zero otherwise. */
+int BN_rand(BIGNUM *rnd, int bits, int top, int bottom);
+
+/* BN_pseudo_rand is an alias for |BN_rand|. */
+int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom);
+
+/* BN_rand_range sets |rnd| to a random value [0..range). It returns one on
+ * success and zero otherwise. */
+int BN_rand_range(BIGNUM *rnd, const BIGNUM *range);
+
+/* BN_pseudo_rand_range is an alias for BN_rand_range. */
+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.
+ *
+ * The callback receives the address of that |BN_GENCB| structure as its last
+ * argument and the user is free to put an arbitary pointer in |arg|. The other
+ * arguments are set as follows:
+ *   event=BN_GENCB_GENERATED, n=i:   after generating the i'th possible prime
+ *                                    number.
+ *   event=BN_GENCB_PRIME_TEST, n=-1: when finished trial division primality
+ *                                    checks.
+ *   event=BN_GENCB_PRIME_TEST, n=i:  when the i'th primality test has finished.
+ *
+ * The callback can return zero to abort the generation progress or one to
+ * allow it to continue.
+ *
+ * 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 *);
+};
+
+/* BN_GENCB_set configures |callback| to call |f| and sets |callout->arg| to
+ * |arg|. */
+void BN_GENCB_set(BN_GENCB *callback,
+                  int (*f)(int event, int n, struct bn_gencb_st *),
+                  void *arg);
+
+/* BN_GENCB_call calls |callback|, if not NULL, and returns the return value of
+ * the callback, or 1 if |callback| is NULL. */
+int BN_GENCB_call(BN_GENCB *callback, int event, int n);
+
+/* BN_generate_prime_ex sets |ret| to a prime number of |bits| length. If safe
+ * is non-zero then the prime will be such that (ret-1)/2 is also a prime.
+ * (This is needed for Diffie-Hellman groups to ensure that the only subgroups
+ * are of size 2 and (p-1)/2.).
+ *
+ * If |add| is not NULL, the prime will fulfill the condition |ret| % |add| ==
+ * |rem| in order to suit a given generator. (If |rem| is NULL then |ret| %
+ * |add| == 1.)
+ *
+ * If |cb| is not NULL, it will be called during processing to give an
+ * indication of progress. See the comments for |BN_GENCB|. It returns one on
+ * success and zero otherwise. */
+int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, const BIGNUM *add,
+                         const BIGNUM *rem, BN_GENCB *cb);
+
+/* BN_prime_checks is magic value that can be used as the |checks| argument to
+ * the primality testing functions in order to automatically select a number of
+ * Miller-Rabin checks that gives a false positive rate of ~2^{-80}. */
+#define BN_prime_checks 0
+
+/* BN_primality_test sets |*is_probably_prime| to one if |candidate| is
+ * probably a prime number by the Miller-Rabin test or zero if it's certainly
+ * not.
+ *
+ * If |do_trial_division| is non-zero then |candidate| will be tested against a
+ * list of small primes before Miller-Rabin tests. The probability of this
+ * function returning a false positive is 2^{2*checks}. If |checks| is
+ * |BN_prime_checks| then a value that results in approximately 2^{-80} false
+ * positive probability is used. If |cb| is not NULL then it is called during
+ * the checking process. See the comment above |BN_GENCB|.
+ *
+ * The function returns one on success and zero on error.
+ *
+ * (If you are unsure whether you want |do_trial_division|, don't set it.) */
+int BN_primality_test(int *is_probably_prime, const BIGNUM *candidate,
+                      int checks, BN_CTX *ctx, int do_trial_division,
+                      BN_GENCB *cb);
+
+/* BN_is_prime_fasttest_ex returns one if |candidate| is probably a prime
+ * number by the Miller-Rabin test, zero if it's certainly not and -1 on error.
+ *
+ * If |do_trial_division| is non-zero then |candidate| will be tested against a
+ * list of small primes before Miller-Rabin tests. The probability of this
+ * function returning one when |candidate| is composite is 2^{2*checks}. If
+ * |checks| is |BN_prime_checks| then a value that results in approximately
+ * 2^{-80} false positive probability is used. If |cb| is not NULL then it is
+ * called during the checking process. See the comment above |BN_GENCB|.
+ *
+ * WARNING: deprecated. Use |BN_primality_test|. */
+int BN_is_prime_fasttest_ex(const BIGNUM *candidate, int checks, BN_CTX *ctx,
+                            int do_trial_division, BN_GENCB *cb);
+
+/* BN_is_prime_ex acts the same as |BN_is_prime_fasttest_ex| with
+ * |do_trial_division| set to zero.
+ *
+ * WARNING: deprecated: Use |BN_primality_test|. */
+int BN_is_prime_ex(const BIGNUM *candidate, int checks, BN_CTX *ctx,
+                   BN_GENCB *cb);
+
+
+/* Number theory functions */
+
+/* BN_gcd sets |r| = gcd(|a|, |b|). It returns one on success and zero
+ * otherwise. */
+int BN_gcd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+
+/* BN_mod_inverse sets |out| equal to |a|^-1, mod |n|. If either of |a| or |n|
+ * have |BN_FLG_CONSTTIME| set then the operation is performed in constant
+ * time. If |out| is NULL, a fresh BIGNUM is allocated. It returns the result
+ * or NULL on error. */
+BIGNUM *BN_mod_inverse(BIGNUM *out, const BIGNUM *a, const BIGNUM *n,
+                       BN_CTX *ctx);
+
+/* BN_kronecker returns the Kronecker symbol of |a| and |b| (which is -1, 0 or
+ * 1), or -2 on error. */
+int BN_kronecker(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+
+
+/* Montgomery arithmetic. */
+
+/* BN_MONT_CTX contains the precomputed values needed to work in a specific
+ * Montgomery domain. */
+
+/* BN_MONT_CTX_new returns a fresh BN_MONT_CTX or NULL on allocation failure. */
+BN_MONT_CTX *BN_MONT_CTX_new(void);
+
+/* BN_MONT_CTX_init initialises a stack allocated |BN_MONT_CTX|. */
+void BN_MONT_CTX_init(BN_MONT_CTX *mont);
+
+/* BN_MONT_CTX_free frees the contexts of |mont| and, if it was originally
+ * allocated with |BN_MONT_CTX_new|, |mont| itself. */
+void BN_MONT_CTX_free(BN_MONT_CTX *mont);
+
+/* BN_MONT_CTX_copy sets |to| equal to |from|. It returns |to| on success or
+ * NULL on error. */
+BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to, BN_MONT_CTX *from);
+
+/* BN_MONT_CTX_set sets up a Montgomery context given the modulus, |mod|. It
+ * returns one on success and zero on error. */
+int BN_MONT_CTX_set(BN_MONT_CTX *mont, const BIGNUM *mod, BN_CTX *ctx);
+
+/* BN_MONT_CTX_set_locked takes the lock indicated by |lock| and checks whether
+ * |*pmont| is NULL. If so, it creates a new |BN_MONT_CTX| and sets the modulus
+ * for it to |mod|. It then stores it as |*pmont| and returns it, or NULL on
+ * error.
+ *
+ * If |*pmont| is already non-NULL then the existing value is returned. */
+BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, int lock,
+                                    const BIGNUM *mod, BN_CTX *ctx);
+
+/* BN_to_montgomery sets |ret| equal to |a| in the Montgomery domain. It
+ * returns one on success and zero on error. */
+int BN_to_montgomery(BIGNUM *ret, const BIGNUM *a, const BN_MONT_CTX *mont,
+                     BN_CTX *ctx);
+
+/* BN_from_montgomery sets |ret| equal to |a| * R^-1, i.e. translates values
+ * out of the Montgomery domain. It returns one on success or zero on error. */
+int BN_from_montgomery(BIGNUM *ret, const BIGNUM *a, const BN_MONT_CTX *mont,
+                       BN_CTX *ctx);
+
+/* BN_mod_mul_montgomery set |r| equal to |a| * |b|, in the Montgomery domain.
+ * Both |a| and |b| must already be in the Montgomery domain (by
+ * |BN_to_montgomery|). It returns one on success or zero on error. */
+int BN_mod_mul_montgomery(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                          const BN_MONT_CTX *mont, BN_CTX *ctx);
+
+
+/* Exponentiation. */
+
+/* BN_exp sets |r| equal to |a|^{|p|}. It does so with a square-and-multiply
+ * algorithm that leaks side-channel information. It returns one on success or
+ * zero otherwise. */
+int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx);
+
+/* BN_exp sets |r| equal to |a|^{|p|} mod |m|. It does so with the best
+ * algorithm for the values provided and can run in constant time if
+ * |BN_FLG_CONSTTIME| is set for |p|. It returns one on success or zero
+ * otherwise. */
+int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
+               BN_CTX *ctx);
+
+int BN_mod_exp_mont(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                    const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+
+int BN_mod_exp_mont_consttime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
+                              const BIGNUM *m, BN_CTX *ctx,
+                              BN_MONT_CTX *in_mont);
+
+int BN_mod_exp_mont_word(BIGNUM *r, BN_ULONG a, const BIGNUM *p,
+                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+int BN_mod_exp2_mont(BIGNUM *r, const BIGNUM *a1, const BIGNUM *p1,
+                     const BIGNUM *a2, const BIGNUM *p2, const BIGNUM *m,
+                     BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+int BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                      const BIGNUM *m, BN_CTX *ctx);
+
+
+
+/* Private functions */
+
+struct bignum_st {
+  BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit chunks in little-endian
+                  order. */
+  int top;   /* Index of last used element in |d|, plus one. */
+  int dmax;  /* Size of |d|, in words. */
+  int neg;   /* one if the number is negative */
+  int flags; /* bitmask of BN_FLG_* values */
+};
+
+struct bn_mont_ctx_st {
+  BIGNUM RR; /* used to convert to montgomery form */
+  BIGNUM N;  /* The modulus */
+  BIGNUM Ni; /* R*(1/R mod N) - N*Ni = 1
+              * (Ni is only stored for bignum algorithm) */
+  BN_ULONG n0[2]; /* least significant word(s) of Ni;
+                     (type changed with 0.9.9, was "BN_ULONG n0;" before) */
+  int flags;
+  int ri;    /* number of bits in R */
+};
+
+unsigned BN_num_bits_word(BN_ULONG l);
+
+#define BN_FLG_MALLOCED 0x01
+#define BN_FLG_STATIC_DATA 0x02
+/* avoid leaking exponent information through timing, BN_mod_exp_mont() will
+ * call BN_mod_exp_mont_consttime, BN_div() will call BN_div_no_branch,
+ * BN_mod_inverse() will call BN_mod_inverse_no_branch. */
+#define BN_FLG_CONSTTIME 0x04
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define BN_F_BN_bn2hex 100
+#define BN_F_BN_new 101
+#define BN_F_BN_exp 102
+#define BN_F_mod_exp_recp 103
+#define BN_F_BN_mod_sqrt 104
+#define BN_F_BN_rand 105
+#define BN_F_BN_rand_range 106
+#define BN_F_bn_wexpand 107
+#define BN_F_BN_mod_exp_mont 108
+#define BN_F_BN_mod_exp2_mont 109
+#define BN_F_BN_CTX_get 110
+#define BN_F_BN_mod_inverse 111
+#define BN_F_BN_bn2dec 112
+#define BN_F_BN_div 113
+#define BN_F_BN_div_recp 114
+#define BN_F_BN_mod_exp_mont_consttime 115
+#define BN_F_BN_mod_exp_mont_word 116
+#define BN_F_BN_CTX_start 117
+#define BN_F_BN_usub 118
+#define BN_F_BN_mod_lshift_quick 119
+#define BN_F_BN_CTX_new 120
+#define BN_F_BN_mod_inverse_no_branch 121
+#define BN_R_NOT_A_SQUARE 100
+#define BN_R_TOO_MANY_ITERATIONS 101
+#define BN_R_INPUT_NOT_REDUCED 102
+#define BN_R_TOO_MANY_TEMPORARY_VARIABLES 103
+#define BN_R_NO_INVERSE 104
+#define BN_R_NOT_INITIALIZED 105
+#define BN_R_DIV_BY_ZERO 106
+#define BN_R_CALLED_WITH_EVEN_MODULUS 107
+#define BN_R_EXPAND_ON_STATIC_BIGNUM_DATA 108
+#define BN_R_BAD_RECIPROCAL 109
+#define BN_R_P_IS_NOT_PRIME 110
+#define BN_R_INVALID_RANGE 111
+#define BN_R_ARG2_LT_ARG3 112
+#define BN_R_BIGNUM_TOO_LONG 113
+
+#endif  /* OPENSSL_HEADER_BN_H */
diff --git a/crypto/bn/bn_error.c b/crypto/bn/bn_error.c
new file mode 100644
index 0000000..a73571c
--- /dev/null
+++ b/crypto/bn/bn_error.c
@@ -0,0 +1,57 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "bn.h"
+
+const ERR_STRING_DATA BN_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_CTX_get, 0), "BN_CTX_get"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_CTX_new, 0), "BN_CTX_new"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_CTX_start, 0), "BN_CTX_start"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_bn2dec, 0), "BN_bn2dec"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_bn2hex, 0), "BN_bn2hex"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_div, 0), "BN_div"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_div_recp, 0), "BN_div_recp"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_exp, 0), "BN_exp"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_exp2_mont, 0), "BN_mod_exp2_mont"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_exp_mont, 0), "BN_mod_exp_mont"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_exp_mont_consttime, 0), "BN_mod_exp_mont_consttime"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_exp_mont_word, 0), "BN_mod_exp_mont_word"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_inverse, 0), "BN_mod_inverse"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_inverse_no_branch, 0), "BN_mod_inverse_no_branch"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_lshift_quick, 0), "BN_mod_lshift_quick"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_mod_sqrt, 0), "BN_mod_sqrt"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_new, 0), "BN_new"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_rand, 0), "BN_rand"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_rand_range, 0), "BN_rand_range"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_BN_usub, 0), "BN_usub"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_bn_wexpand, 0), "bn_wexpand"},
+  {ERR_PACK(ERR_LIB_BN, BN_F_mod_exp_recp, 0), "mod_exp_recp"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_ARG2_LT_ARG3), "ARG2_LT_ARG3"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_BAD_RECIPROCAL), "BAD_RECIPROCAL"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_BIGNUM_TOO_LONG), "BIGNUM_TOO_LONG"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_CALLED_WITH_EVEN_MODULUS), "CALLED_WITH_EVEN_MODULUS"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_DIV_BY_ZERO), "DIV_BY_ZERO"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA), "EXPAND_ON_STATIC_BIGNUM_DATA"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_INPUT_NOT_REDUCED), "INPUT_NOT_REDUCED"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_INVALID_RANGE), "INVALID_RANGE"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_NOT_A_SQUARE), "NOT_A_SQUARE"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_NOT_INITIALIZED), "NOT_INITIALIZED"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_NO_INVERSE), "NO_INVERSE"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_P_IS_NOT_PRIME), "P_IS_NOT_PRIME"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_TOO_MANY_ITERATIONS), "TOO_MANY_ITERATIONS"},
+  {ERR_PACK(ERR_LIB_BN, 0, BN_R_TOO_MANY_TEMPORARY_VARIABLES), "TOO_MANY_TEMPORARY_VARIABLES"},
+  {0, NULL},
+};
diff --git a/crypto/bn/bn_test.c b/crypto/bn/bn_test.c
new file mode 100644
index 0000000..1a89802
--- /dev/null
+++ b/crypto/bn/bn_test.c
@@ -0,0 +1,1152 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the Eric Young open source
+ * license provided above.
+ *
+ * The binary polynomial arithmetic software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <stdio.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+
+#include "internal.h"
+
+static const int num0 = 100; /* number of tests */
+static const int num1 = 50;  /* additional tests for some functions */
+static const int num2 = 5;   /* number of tests for slow functions */
+
+int test_add(BIO *bp);
+int test_sub(BIO *bp);
+int test_lshift1(BIO *bp);
+int test_lshift(BIO *bp, BN_CTX *ctx, BIGNUM *a_);
+int test_rshift1(BIO *bp);
+int test_rshift(BIO *bp, BN_CTX *ctx);
+int test_sqr(BIO *bp, BN_CTX *ctx);
+int test_mul(BIO *bp);
+int test_div(BIO *bp, BN_CTX *ctx);
+int rand_neg(void);
+
+int test_div_word(BIO *bp);
+int test_mont(BIO *bp, BN_CTX *ctx);
+int test_mod(BIO *bp, BN_CTX *ctx);
+int test_mod_mul(BIO *bp, BN_CTX *ctx);
+int test_mod_exp(BIO *bp, BN_CTX *ctx);
+int test_mod_exp_mont_consttime(BIO *bp, BN_CTX *ctx);
+int test_exp(BIO *bp, BN_CTX *ctx);
+int test_mod_sqrt(BIO *bp, BN_CTX *ctx);
+#if 0
+int test_gf2m_add(BIO *bp);
+int test_gf2m_mod(BIO *bp);
+int test_gf2m_mod_mul(BIO *bp, BN_CTX *ctx);
+int test_gf2m_mod_sqr(BIO *bp, BN_CTX *ctx);
+int test_gf2m_mod_inv(BIO *bp, BN_CTX *ctx);
+int test_gf2m_mod_div(BIO *bp, BN_CTX *ctx);
+int test_gf2m_mod_exp(BIO *bp, BN_CTX *ctx);
+int test_gf2m_mod_sqrt(BIO *bp, BN_CTX *ctx);
+int test_gf2m_mod_solve_quad(BIO *bp, BN_CTX *ctx);
+#endif
+static int results = 0;
+
+static unsigned char lst[] =
+    "\xC6\x4F\x43\x04\x2A\xEA\xCA\x6E\x58\x36\x80\x5B\xE8\xC9"
+    "\x9B\x04\x5D\x48\x36\xC2\xFD\x16\xC9\x64\xF0";
+
+static void ERR_print_errors_fp(FILE *out) {
+}
+
+static void message(BIO *out, char *m) {
+  BIO_puts(out, "print \"test ");
+  BIO_puts(out, m);
+  BIO_puts(out, "\\n\"\n");
+}
+
+int main(int argc, char *argv[]) {
+  BN_CTX *ctx;
+  BIO *out = NULL;
+  char *outfile = NULL;
+
+  results = 0;
+
+  argc--;
+  argv++;
+  while (argc >= 1) {
+    if (strcmp(*argv, "-results") == 0)
+      results = 1;
+    else if (strcmp(*argv, "-out") == 0) {
+      if (--argc < 1)
+        break;
+      outfile = *(++argv);
+    }
+    argc--;
+    argv++;
+  }
+
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL)
+    return 1;
+
+  out = BIO_new(BIO_s_file());
+  if (out == NULL) {
+    return 1;
+  }
+
+  if (outfile == NULL) {
+    BIO_set_fp(out, stdout, BIO_NOCLOSE);
+  } else {
+    if (!BIO_write_filename(out, outfile)) {
+      perror(outfile);
+      return 1;
+    }
+  }
+
+  if (!results)
+    BIO_puts(out, "obase=16\nibase=16\n");
+
+  message(out, "BN_add");
+  if (!test_add(out))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_sub");
+  if (!test_sub(out))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_lshift1");
+  if (!test_lshift1(out))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_lshift (fixed)");
+  if (!test_lshift(out, ctx, BN_bin2bn(lst, sizeof(lst) - 1, NULL)))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_lshift");
+  if (!test_lshift(out, ctx, NULL))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_rshift1");
+  if (!test_rshift1(out))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_rshift");
+  if (!test_rshift(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_sqr");
+  if (!test_sqr(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mul");
+  if (!test_mul(out))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_div");
+  if (!test_div(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_div_word");
+  if (!test_div_word(out))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mod");
+  if (!test_mod(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mod_mul");
+  if (!test_mod_mul(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mont");
+  if (!test_mont(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mod_exp");
+  if (!test_mod_exp(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mod_exp_mont_consttime");
+  if (!test_mod_exp_mont_consttime(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_exp");
+  if (!test_exp(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  message(out, "BN_mod_sqrt");
+  if (!test_mod_sqrt(out, ctx))
+    goto err;
+  (void)BIO_flush(out);
+
+  BN_CTX_free(ctx);
+  BIO_free(out);
+
+  printf("PASS\n");
+  return 0;
+
+err:
+  BIO_puts(out, "1\n"); /* make sure the Perl script fed by bc notices
+                         * the failure, see test_bn in test/Makefile.ssl*/
+  (void)BIO_flush(out);
+
+  return 1;
+}
+
+int test_add(BIO *bp) {
+  BIGNUM a, b, c;
+  int i;
+
+  BN_init(&a);
+  BN_init(&b);
+  BN_init(&c);
+
+  BN_rand(&a, 512, 0, 0);
+  for (i = 0; i < num0; i++) {
+    BN_rand(&b, 450 + i, 0, 0);
+    a.neg = rand_neg();
+    b.neg = rand_neg();
+    BN_add(&c, &a, &b);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " + ");
+        BN_print(bp, &b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &c);
+      BIO_puts(bp, "\n");
+    }
+    a.neg = !a.neg;
+    b.neg = !b.neg;
+    BN_add(&c, &c, &b);
+    BN_add(&c, &c, &a);
+    if (!BN_is_zero(&c)) {
+      fprintf(stderr, "Add test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(&a);
+  BN_free(&b);
+  BN_free(&c);
+  return (1);
+}
+
+int test_sub(BIO *bp) {
+  BIGNUM a, b, c;
+  int i;
+
+  BN_init(&a);
+  BN_init(&b);
+  BN_init(&c);
+
+  for (i = 0; i < num0 + num1; i++) {
+    if (i < num1) {
+      BN_rand(&a, 512, 0, 0);
+      BN_copy(&b, &a);
+      if (BN_set_bit(&a, i) == 0)
+        return (0);
+      BN_add_word(&b, i);
+    } else {
+      BN_rand(&b, 400 + i - num1, 0, 0);
+      a.neg = rand_neg();
+      b.neg = rand_neg();
+    }
+    BN_sub(&c, &a, &b);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " - ");
+        BN_print(bp, &b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &c);
+      BIO_puts(bp, "\n");
+    }
+    BN_add(&c, &c, &b);
+    BN_sub(&c, &c, &a);
+    if (!BN_is_zero(&c)) {
+      fprintf(stderr, "Subtract test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(&a);
+  BN_free(&b);
+  BN_free(&c);
+  return (1);
+}
+
+int test_div(BIO *bp, BN_CTX *ctx) {
+  BIGNUM a, b, c, d, e;
+  int i;
+
+  BN_init(&a);
+  BN_init(&b);
+  BN_init(&c);
+  BN_init(&d);
+  BN_init(&e);
+
+  for (i = 0; i < num0 + num1; i++) {
+    if (i < num1) {
+      BN_rand(&a, 400, 0, 0);
+      BN_copy(&b, &a);
+      BN_lshift(&a, &a, i);
+      BN_add_word(&a, i);
+    } else
+      BN_rand(&b, 50 + 3 * (i - num1), 0, 0);
+    a.neg = rand_neg();
+    b.neg = rand_neg();
+    BN_div(&d, &c, &a, &b, ctx);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " / ");
+        BN_print(bp, &b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &d);
+      BIO_puts(bp, "\n");
+
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " % ");
+        BN_print(bp, &b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &c);
+      BIO_puts(bp, "\n");
+    }
+    BN_mul(&e, &d, &b, ctx);
+    BN_add(&d, &e, &c);
+    BN_sub(&d, &d, &a);
+    if (!BN_is_zero(&d)) {
+      fprintf(stderr, "Division test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(&a);
+  BN_free(&b);
+  BN_free(&c);
+  BN_free(&d);
+  BN_free(&e);
+  return (1);
+}
+
+int test_lshift1(BIO *bp) {
+  BIGNUM *a, *b, *c;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+
+  BN_rand(a, 200, 0, 0); /**/
+  a->neg = rand_neg();
+  for (i = 0; i < num0; i++) {
+    BN_lshift1(b, a);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " * 2");
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, b);
+      BIO_puts(bp, "\n");
+    }
+    BN_add(c, a, a);
+    BN_sub(a, b, c);
+    if (!BN_is_zero(a)) {
+      fprintf(stderr, "Left shift one test failed!\n");
+      return 0;
+    }
+
+    BN_copy(a, b);
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  return (1);
+}
+
+int test_rshift(BIO *bp, BN_CTX *ctx) {
+  BIGNUM *a, *b, *c, *d, *e;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+  d = BN_new();
+  e = BN_new();
+  BN_one(c);
+
+  BN_rand(a, 200, 0, 0); /**/
+  a->neg = rand_neg();
+  for (i = 0; i < num0; i++) {
+    BN_rshift(b, a, i + 1);
+    BN_add(c, c, c);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " / ");
+        BN_print(bp, c);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, b);
+      BIO_puts(bp, "\n");
+    }
+    BN_div(d, e, a, c, ctx);
+    BN_sub(d, d, b);
+    if (!BN_is_zero(d)) {
+      fprintf(stderr, "Right shift test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  BN_free(d);
+  BN_free(e);
+  return (1);
+}
+
+int test_rshift1(BIO *bp) {
+  BIGNUM *a, *b, *c;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+
+  BN_rand(a, 200, 0, 0); /**/
+  a->neg = rand_neg();
+  for (i = 0; i < num0; i++) {
+    BN_rshift1(b, a);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " / 2");
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, b);
+      BIO_puts(bp, "\n");
+    }
+    BN_sub(c, a, b);
+    BN_sub(c, c, b);
+    if (!BN_is_zero(c) && !BN_abs_is_word(c, 1)) {
+      fprintf(stderr, "Right shift one test failed!\n");
+      return 0;
+    }
+    BN_copy(a, b);
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  return (1);
+}
+
+int test_lshift(BIO *bp, BN_CTX *ctx, BIGNUM *a_) {
+  BIGNUM *a, *b, *c, *d;
+  int i;
+
+  b = BN_new();
+  c = BN_new();
+  d = BN_new();
+  BN_one(c);
+
+  if (a_)
+    a = a_;
+  else {
+    a = BN_new();
+    BN_rand(a, 200, 0, 0); /**/
+    a->neg = rand_neg();
+  }
+  for (i = 0; i < num0; i++) {
+    BN_lshift(b, a, i + 1);
+    BN_add(c, c, c);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " * ");
+        BN_print(bp, c);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, b);
+      BIO_puts(bp, "\n");
+    }
+    BN_mul(d, a, c, ctx);
+    BN_sub(d, d, b);
+    if (!BN_is_zero(d)) {
+      fprintf(stderr, "Left shift test failed!\n");
+      fprintf(stderr, "a=");
+      BN_print_fp(stderr, a);
+      fprintf(stderr, "\nb=");
+      BN_print_fp(stderr, b);
+      fprintf(stderr, "\nc=");
+      BN_print_fp(stderr, c);
+      fprintf(stderr, "\nd=");
+      BN_print_fp(stderr, d);
+      fprintf(stderr, "\n");
+      return 0;
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  BN_free(d);
+  return (1);
+}
+
+int test_mul(BIO *bp) {
+  BIGNUM a, b, c, d, e;
+  int i;
+  BN_CTX *ctx;
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL)
+    abort();
+
+  BN_init(&a);
+  BN_init(&b);
+  BN_init(&c);
+  BN_init(&d);
+  BN_init(&e);
+
+  for (i = 0; i < num0 + num1; i++) {
+    if (i <= num1) {
+      BN_rand(&a, 100, 0, 0);
+      BN_rand(&b, 100, 0, 0);
+    } else
+      BN_rand(&b, i - num1, 0, 0);
+    a.neg = rand_neg();
+    b.neg = rand_neg();
+    BN_mul(&c, &a, &b, ctx);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " * ");
+        BN_print(bp, &b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &c);
+      BIO_puts(bp, "\n");
+    }
+    BN_div(&d, &e, &c, &a, ctx);
+    BN_sub(&d, &d, &b);
+    if (!BN_is_zero(&d) || !BN_is_zero(&e)) {
+      fprintf(stderr, "Multiplication test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(&a);
+  BN_free(&b);
+  BN_free(&c);
+  BN_free(&d);
+  BN_free(&e);
+  BN_CTX_free(ctx);
+  return (1);
+}
+
+int test_sqr(BIO *bp, BN_CTX *ctx) {
+  BIGNUM a, c, d, e;
+  int i;
+
+  BN_init(&a);
+  BN_init(&c);
+  BN_init(&d);
+  BN_init(&e);
+
+  for (i = 0; i < num0; i++) {
+    BN_rand(&a, 40 + i * 10, 0, 0);
+    a.neg = rand_neg();
+    BN_sqr(&c, &a, ctx);
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " * ");
+        BN_print(bp, &a);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &c);
+      BIO_puts(bp, "\n");
+    }
+    BN_div(&d, &e, &c, &a, ctx);
+    BN_sub(&d, &d, &a);
+    if (!BN_is_zero(&d) || !BN_is_zero(&e)) {
+      fprintf(stderr, "Square test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(&a);
+  BN_free(&c);
+  BN_free(&d);
+  BN_free(&e);
+  return (1);
+}
+
+
+int rand_neg(void) {
+  static unsigned int neg = 0;
+  static int sign[8] = {0, 0, 0, 1, 1, 0, 1, 1};
+
+  return (sign[(neg++) % 8]);
+}
+
+static void print_word(BIO *bp, BN_ULONG w) {
+#ifdef OPENSSL_64_BIT
+  if (sizeof(w) > sizeof(unsigned long)) {
+    unsigned long h = (unsigned long)(w >> 32), l = (unsigned long)(w);
+
+    if (h)
+      BIO_printf(bp, "%lX%08lX", h, l);
+    else
+      BIO_printf(bp, "%lX", l);
+    return;
+  }
+#endif
+  BIO_printf(bp, BN_HEX_FMT1, w);
+}
+
+int test_div_word(BIO *bp) {
+  BIGNUM a, b;
+  BN_ULONG r, s;
+  int i;
+
+  BN_init(&a);
+  BN_init(&b);
+
+  for (i = 0; i < num0; i++) {
+    do {
+      BN_rand(&a, 512, -1, 0);
+      BN_rand(&b, BN_BITS2, -1, 0);
+      s = b.d[0];
+    } while (!s);
+
+    BN_copy(&b, &a);
+    r = BN_div_word(&b, s);
+
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " / ");
+        print_word(bp, s);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &b);
+      BIO_puts(bp, "\n");
+
+      if (!results) {
+        BN_print(bp, &a);
+        BIO_puts(bp, " % ");
+        print_word(bp, s);
+        BIO_puts(bp, " - ");
+      }
+      print_word(bp, r);
+      BIO_puts(bp, "\n");
+    }
+    BN_mul_word(&b, s);
+    BN_add_word(&b, r);
+    BN_sub(&b, &a, &b);
+    if (!BN_is_zero(&b)) {
+      fprintf(stderr, "Division (word) test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(&a);
+  BN_free(&b);
+  return (1);
+}
+
+int test_mont(BIO *bp, BN_CTX *ctx) {
+  BIGNUM a, b, c, d, A, B;
+  BIGNUM n;
+  int i;
+  BN_MONT_CTX *mont;
+
+  BN_init(&a);
+  BN_init(&b);
+  BN_init(&c);
+  BN_init(&d);
+  BN_init(&A);
+  BN_init(&B);
+  BN_init(&n);
+
+  mont = BN_MONT_CTX_new();
+  if (mont == NULL)
+    return 0;
+
+  BN_rand(&a, 100, 0, 0); /**/
+  BN_rand(&b, 100, 0, 0); /**/
+  for (i = 0; i < num2; i++) {
+    int bits = (200 * (i + 1)) / num2;
+
+    if (bits == 0)
+      continue;
+    BN_rand(&n, bits, 0, 1);
+    BN_MONT_CTX_set(mont, &n, ctx);
+
+    BN_nnmod(&a, &a, &n, ctx);
+    BN_nnmod(&b, &b, &n, ctx);
+
+    BN_to_montgomery(&A, &a, mont, ctx);
+    BN_to_montgomery(&B, &b, mont, ctx);
+
+    BN_mod_mul_montgomery(&c, &A, &B, mont, ctx); /**/
+    BN_from_montgomery(&A, &c, mont, ctx);        /**/
+    if (bp != NULL) {
+      if (!results) {
+#ifdef undef
+        fprintf(stderr, "%d * %d %% %d\n", BN_num_bits(&a), BN_num_bits(&b),
+                BN_num_bits(mont->N));
+#endif
+        BN_print(bp, &a);
+        BIO_puts(bp, " * ");
+        BN_print(bp, &b);
+        BIO_puts(bp, " % ");
+        BN_print(bp, &(mont->N));
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, &A);
+      BIO_puts(bp, "\n");
+    }
+    BN_mod_mul(&d, &a, &b, &n, ctx);
+    BN_sub(&d, &d, &A);
+    if (!BN_is_zero(&d)) {
+      fprintf(stderr, "Montgomery multiplication test failed!\n");
+      return 0;
+    }
+  }
+  BN_MONT_CTX_free(mont);
+  BN_free(&a);
+  BN_free(&b);
+  BN_free(&c);
+  BN_free(&d);
+  BN_free(&A);
+  BN_free(&B);
+  BN_free(&n);
+  return (1);
+}
+
+int test_mod(BIO *bp, BN_CTX *ctx) {
+  BIGNUM *a, *b, *c, *d, *e;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+  d = BN_new();
+  e = BN_new();
+
+  BN_rand(a, 1024, 0, 0); /**/
+  for (i = 0; i < num0; i++) {
+    BN_rand(b, 450 + i * 10, 0, 0); /**/
+    a->neg = rand_neg();
+    b->neg = rand_neg();
+    BN_mod(c, a, b, ctx); /**/
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " % ");
+        BN_print(bp, b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, c);
+      BIO_puts(bp, "\n");
+    }
+    BN_div(d, e, a, b, ctx);
+    BN_sub(e, e, c);
+    if (!BN_is_zero(e)) {
+      fprintf(stderr, "Modulo test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  BN_free(d);
+  BN_free(e);
+  return (1);
+}
+
+int test_mod_mul(BIO *bp, BN_CTX *ctx) {
+  BIGNUM *a, *b, *c, *d, *e;
+  int i, j;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+  d = BN_new();
+  e = BN_new();
+
+  for (j = 0; j < 3; j++) {
+    BN_rand(c, 1024, 0, 0); /**/
+    for (i = 0; i < num0; i++) {
+      BN_rand(a, 475 + i * 10, 0, 0); /**/
+      BN_rand(b, 425 + i * 11, 0, 0); /**/
+      a->neg = rand_neg();
+      b->neg = rand_neg();
+      if (!BN_mod_mul(e, a, b, c, ctx)) {
+        unsigned long l;
+
+        while ((l = ERR_get_error()))
+          fprintf(stderr, "ERROR:%s\n", ERR_error_string(l, NULL));
+        abort();
+      }
+      if (bp != NULL) {
+        if (!results) {
+          BN_print(bp, a);
+          BIO_puts(bp, " * ");
+          BN_print(bp, b);
+          BIO_puts(bp, " % ");
+          BN_print(bp, c);
+          if ((a->neg ^ b->neg) && !BN_is_zero(e)) {
+            /* If  (a*b) % c  is negative,  c  must be added
+             * in order to obtain the normalized remainder
+             * (new with OpenSSL 0.9.7, previous versions of
+             * BN_mod_mul could generate negative results)
+             */
+            BIO_puts(bp, " + ");
+            BN_print(bp, c);
+          }
+          BIO_puts(bp, " - ");
+        }
+        BN_print(bp, e);
+        BIO_puts(bp, "\n");
+      }
+      BN_mul(d, a, b, ctx);
+      BN_sub(d, d, e);
+      BN_div(a, b, d, c, ctx);
+      if (!BN_is_zero(b)) {
+        fprintf(stderr, "Modulo multiply test failed!\n");
+        ERR_print_errors_fp(stderr);
+        return 0;
+      }
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  BN_free(d);
+  BN_free(e);
+  return (1);
+}
+
+int test_mod_exp(BIO *bp, BN_CTX *ctx) {
+  BIGNUM *a, *b, *c, *d, *e;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+  d = BN_new();
+  e = BN_new();
+
+  BN_rand(c, 30, 0, 1); /* must be odd for montgomery */
+  for (i = 0; i < num2; i++) {
+    BN_rand(a, 20 + i * 5, 0, 0); /**/
+    BN_rand(b, 2 + i, 0, 0);      /**/
+
+    if (!BN_mod_exp(d, a, b, c, ctx))
+      return (0);
+
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " ^ ");
+        BN_print(bp, b);
+        BIO_puts(bp, " % ");
+        BN_print(bp, c);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, d);
+      BIO_puts(bp, "\n");
+    }
+    BN_exp(e, a, b, ctx);
+    BN_sub(e, e, d);
+    BN_div(a, b, e, c, ctx);
+    if (!BN_is_zero(b)) {
+      fprintf(stderr, "Modulo exponentiation test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  BN_free(d);
+  BN_free(e);
+  return (1);
+}
+
+int test_mod_exp_mont_consttime(BIO *bp, BN_CTX *ctx) {
+  BIGNUM *a, *b, *c, *d, *e;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  c = BN_new();
+  d = BN_new();
+  e = BN_new();
+
+  BN_rand(c, 30, 0, 1); /* must be odd for montgomery */
+  for (i = 0; i < num2; i++) {
+    BN_rand(a, 20 + i * 5, 0, 0); /**/
+    BN_rand(b, 2 + i, 0, 0);      /**/
+
+    if (!BN_mod_exp_mont_consttime(d, a, b, c, ctx, NULL))
+      return (00);
+
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " ^ ");
+        BN_print(bp, b);
+        BIO_puts(bp, " % ");
+        BN_print(bp, c);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, d);
+      BIO_puts(bp, "\n");
+    }
+    BN_exp(e, a, b, ctx);
+    BN_sub(e, e, d);
+    BN_div(a, b, e, c, ctx);
+    if (!BN_is_zero(b)) {
+      fprintf(stderr, "Modulo exponentiation test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(c);
+  BN_free(d);
+  BN_free(e);
+  return (1);
+}
+
+int test_exp(BIO *bp, BN_CTX *ctx) {
+  BIGNUM *a, *b, *d, *e, *one;
+  int i;
+
+  a = BN_new();
+  b = BN_new();
+  d = BN_new();
+  e = BN_new();
+  one = BN_new();
+  BN_one(one);
+
+  for (i = 0; i < num2; i++) {
+    BN_rand(a, 20 + i * 5, 0, 0); /**/
+    BN_rand(b, 2 + i, 0, 0);      /**/
+
+    if (BN_exp(d, a, b, ctx) <= 0)
+      return (0);
+
+    if (bp != NULL) {
+      if (!results) {
+        BN_print(bp, a);
+        BIO_puts(bp, " ^ ");
+        BN_print(bp, b);
+        BIO_puts(bp, " - ");
+      }
+      BN_print(bp, d);
+      BIO_puts(bp, "\n");
+    }
+    BN_one(e);
+    for (; !BN_is_zero(b); BN_sub(b, b, one))
+      BN_mul(e, e, a, ctx);
+    BN_sub(e, e, d);
+    if (!BN_is_zero(e)) {
+      fprintf(stderr, "Exponentiation test failed!\n");
+      return 0;
+    }
+  }
+  BN_free(a);
+  BN_free(b);
+  BN_free(d);
+  BN_free(e);
+  BN_free(one);
+  return (1);
+}
+
+static int genprime_cb(int p, int n, BN_GENCB *arg) {
+  char c = '*';
+
+  if (p == 0)
+    c = '.';
+  if (p == 1)
+    c = '+';
+  if (p == 2)
+    c = '*';
+  if (p == 3)
+    c = '\n';
+  putc(c, stdout);
+  fflush(stdout);
+  return 1;
+}
+
+int test_mod_sqrt(BIO *bp, BN_CTX *ctx) {
+  BN_GENCB cb;
+  BIGNUM *a, *p, *r;
+  int i, j;
+  int ret = 0;
+
+  a = BN_new();
+  p = BN_new();
+  r = BN_new();
+  if (a == NULL || p == NULL || r == NULL)
+    goto err;
+
+  BN_GENCB_set(&cb, genprime_cb, NULL);
+
+  for (i = 0; i < 16; i++) {
+    if (i < 8) {
+      unsigned primes[8] = {2, 3, 5, 7, 11, 13, 17, 19};
+
+      if (!BN_set_word(p, primes[i]))
+        goto err;
+    } else {
+      if (!BN_set_word(a, 32))
+        goto err;
+      if (!BN_set_word(r, 2 * i + 1))
+        goto err;
+
+      if (!BN_generate_prime_ex(p, 256, 0, a, r, &cb))
+        goto err;
+      putc('\n', stdout);
+    }
+    p->neg = rand_neg();
+
+    for (j = 0; j < num2; j++) {
+      /* construct 'a' such that it is a square modulo p,
+       * but in general not a proper square and not reduced modulo p */
+      if (!BN_rand(r, 256, 0, 3))
+        goto err;
+      if (!BN_nnmod(r, r, p, ctx))
+        goto err;
+      if (!BN_mod_sqr(r, r, p, ctx))
+        goto err;
+      if (!BN_rand(a, 256, 0, 3))
+        goto err;
+      if (!BN_nnmod(a, a, p, ctx))
+        goto err;
+      if (!BN_mod_sqr(a, a, p, ctx))
+        goto err;
+      if (!BN_mul(a, a, r, ctx))
+        goto err;
+      if (rand_neg())
+        if (!BN_sub(a, a, p))
+          goto err;
+
+      if (!BN_mod_sqrt(r, a, p, ctx))
+        goto err;
+      if (!BN_mod_sqr(r, r, p, ctx))
+        goto err;
+
+      if (!BN_nnmod(a, a, p, ctx))
+        goto err;
+
+      if (BN_cmp(a, r) != 0) {
+        fprintf(stderr, "BN_mod_sqrt failed: a = ");
+        BN_print_fp(stderr, a);
+        fprintf(stderr, ", r = ");
+        BN_print_fp(stderr, r);
+        fprintf(stderr, ", p = ");
+        BN_print_fp(stderr, p);
+        fprintf(stderr, "\n");
+        goto err;
+      }
+
+      putc('.', stdout);
+      fflush(stdout);
+    }
+
+    putc('\n', stdout);
+    fflush(stderr);
+  }
+  ret = 1;
+err:
+  if (a != NULL)
+    BN_free(a);
+  if (p != NULL)
+    BN_free(p);
+  if (r != NULL)
+    BN_free(r);
+  return ret;
+}
diff --git a/crypto/bn/cmp.c b/crypto/bn/cmp.c
new file mode 100644
index 0000000..fce7233
--- /dev/null
+++ b/crypto/bn/cmp.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include "internal.h"
+
+
+int BN_ucmp(const BIGNUM *a, const BIGNUM *b) {
+  int i;
+  BN_ULONG t1, t2, *ap, *bp;
+
+  i = a->top - b->top;
+  if (i != 0) {
+    return i;
+  }
+
+  ap = a->d;
+  bp = b->d;
+  for (i = a->top - 1; i >= 0; i--) {
+    t1 = ap[i];
+    t2 = bp[i];
+    if (t1 != t2) {
+      return (t1 > t2) ? 1 : -1;
+    }
+  }
+
+  return 0;
+}
+
+int BN_cmp(const BIGNUM *a, const BIGNUM *b) {
+  int i;
+  int gt, lt;
+  BN_ULONG t1, t2;
+
+  if ((a == NULL) || (b == NULL)) {
+    if (a != NULL) {
+      return -1;
+    } else if (b != NULL) {
+      return 1;
+    } else {
+      return 0;
+    }
+  }
+
+  if (a->neg != b->neg) {
+    if (a->neg) {
+      return -1;
+    }
+    return 1;
+  }
+  if (a->neg == 0) {
+    gt = 1;
+    lt = -1;
+  } else {
+    gt = -1;
+    lt = 1;
+  }
+
+  if (a->top > b->top) {
+    return gt;
+  }
+  if (a->top < b->top) {
+    return lt;
+  }
+
+  for (i = a->top - 1; i >= 0; i--) {
+    t1 = a->d[i];
+    t2 = b->d[i];
+    if (t1 > t2) {
+      return gt;
+    } if (t1 < t2) {
+      return lt;
+    }
+  }
+
+  return 0;
+}
+
+int bn_cmp_words(const BN_ULONG *a, const BN_ULONG *b, int n) {
+  int i;
+  BN_ULONG aa, bb;
+
+  aa = a[n - 1];
+  bb = b[n - 1];
+  if (aa != bb) {
+    return (aa > bb) ? 1 : -1;
+  }
+
+  for (i = n - 2; i >= 0; i--) {
+    aa = a[i];
+    bb = b[i];
+    if (aa != bb) {
+      return (aa > bb) ? 1 : -1;
+    }
+  }
+  return 0;
+}
+
+int bn_cmp_part_words(const BN_ULONG *a, const BN_ULONG *b, int cl, int dl) {
+  int n, i;
+  n = cl - 1;
+
+  if (dl < 0) {
+    for (i = dl; i < 0; i++) {
+      if (b[n - i] != 0) {
+        return -1; /* a < b */
+      }
+    }
+  }
+  if (dl > 0) {
+    for (i = dl; i > 0; i--) {
+      if (a[n + i] != 0) {
+        return 1; /* a > b */
+      }
+    }
+  }
+
+  return bn_cmp_words(a, b, cl);
+}
+
+int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w) {
+  switch (bn->top) {
+    case 1:
+      return bn->d[0] == w;
+    case 0:
+      return w == 0;
+    default:
+      return 0;
+  }
+}
+
+int BN_is_zero(const BIGNUM *bn) {
+  return bn->top == 0;
+}
+
+int BN_is_one(const BIGNUM *bn) {
+  return bn->neg == 0 && BN_abs_is_word(bn, 1);
+}
+
+int BN_is_word(const BIGNUM *bn, BN_ULONG w) {
+  return BN_abs_is_word(bn, w) && (w == 0 || bn->neg == 0);
+}
+
+int BN_is_odd(const BIGNUM *bn) {
+  return bn->top > 0 && (bn->d[0] & 1) == 1;
+}
diff --git a/crypto/bn/convert.c b/crypto/bn/convert.c
new file mode 100644
index 0000000..c6b391a
--- /dev/null
+++ b/crypto/bn/convert.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+BIGNUM *BN_bin2bn(const uint8_t *in, size_t len, BIGNUM *ret) {
+  unsigned num_words, m;
+  BN_ULONG word = 0;
+  BIGNUM *bn = NULL;
+
+  if (ret == NULL) {
+    ret = bn = BN_new();
+  }
+
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  if (len == 0) {
+    ret->top = 0;
+    return ret;
+  }
+
+  num_words = ((len - 1) / BN_BYTES) + 1;
+  m = (len - 1) % BN_BYTES;
+  if (bn_wexpand(ret, num_words) == NULL) {
+    if (bn) {
+      BN_free(bn);
+    }
+    return NULL;
+  }
+
+  ret->top = num_words;
+  ret->neg = 0;
+
+  while (len--) {
+    word = (word << 8) | *(in++);
+    if (m-- == 0) {
+      ret->d[--num_words] = word;
+      word = 0;
+      m = BN_BYTES - 1;
+    }
+  }
+
+  /* need to call this due to clear byte at top if avoiding having the top bit
+   * set (-ve number) */
+  bn_correct_top(ret);
+  return ret;
+}
+
+size_t BN_bn2bin(const BIGNUM *in, uint8_t *out) {
+  size_t n, i;
+  BN_ULONG l;
+
+  n = i = BN_num_bytes(in);
+  while (i--) {
+    l = in->d[i / BN_BYTES];
+    *(out++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff;
+  }
+  return n;
+}
+
+static const char hextable[] = "0123456789abcdef";
+
+char *BN_bn2hex(const BIGNUM *bn) {
+  int i, j, v, z = 0;
+  char *buf;
+  char *p;
+
+  buf = (char *)OPENSSL_malloc(bn->top * BN_BYTES * 2 + 2);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(BN, BN_bn2hex, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  p = buf;
+  if (bn->neg) {
+    *(p++) = '-';
+  }
+
+  if (BN_is_zero(bn)) {
+    *(p++) = '0';
+  }
+
+  for (i = bn->top - 1; i >= 0; i--) {
+    for (j = BN_BITS2 - 8; j >= 0; j -= 8) {
+      /* strip leading zeros */
+      v = ((int)(bn->d[i] >> (long)j)) & 0xff;
+      if (z || v != 0) {
+        *(p++) = hextable[v >> 4];
+        *(p++) = hextable[v & 0x0f];
+        z = 1;
+      }
+    }
+  }
+  *p = '\0';
+
+  return buf;
+}
+
+/* decode_hex decodes |i| bytes of hex data from |in| and updates |bn|. */
+static void decode_hex(BIGNUM *bn, const char *in, int i) {
+  int h, m, j, k, c;
+  BN_ULONG l=0;
+
+  j = i; /* least significant 'hex' */
+  m = 0;
+  h = 0;
+  while (j > 0) {
+    m = ((BN_BYTES * 2) <= j) ? (BN_BYTES * 2) : j;
+    l = 0;
+    for (;;) {
+      c = in[j - m];
+      if ((c >= '0') && (c <= '9')) {
+        k = c - '0';
+      } else if ((c >= 'a') && (c <= 'f')) {
+        k = c - 'a' + 10;
+      } else if ((c >= 'A') && (c <= 'F')) {
+        k = c - 'A' + 10;
+      } else {
+        k = 0; /* paranoia */
+      }
+
+      l = (l << 4) | k;
+
+      if (--m <= 0) {
+        bn->d[h++] = l;
+        break;
+      }
+    }
+
+    j -= (BN_BYTES * 2);
+  }
+
+  bn->top = h;
+}
+
+/* decode_dec decodes |i| bytes of decimal data from |in| and updates |bn|. */
+static void decode_dec(BIGNUM *bn, const char *in, int i) {
+  int j;
+  BN_ULONG l = 0;
+
+  j = BN_DEC_NUM - (i % BN_DEC_NUM);
+  if (j == BN_DEC_NUM) {
+    j = 0;
+  }
+  l = 0;
+  while (*in) {
+    l *= 10;
+    l += *in - '0';
+    in++;
+    if (++j == BN_DEC_NUM) {
+      BN_mul_word(bn, BN_DEC_CONV);
+      BN_add_word(bn, l);
+      l = 0;
+      j = 0;
+    }
+  }
+}
+
+typedef void (*decode_func) (BIGNUM *bn, const char *in, int i);
+typedef int (*char_test_func) (int c);
+
+static int bn_x2bn(BIGNUM **outp, const char *in, decode_func decode, char_test_func want_char) {
+  BIGNUM *ret = NULL;
+  int neg = 0, i;
+  int num;
+
+  if (in == NULL || *in == 0) {
+    return 0;
+  }
+
+  if (*in == '-') {
+    neg = 1;
+    in++;
+  }
+
+  for (i = 0; want_char((unsigned char)in[i]); i++) {}
+
+  num = i + neg;
+  if (outp == NULL) {
+    return num;
+  }
+
+  /* in is the start of the hex digits, and it is 'i' long */
+  if (*outp == NULL) {
+    ret = BN_new();
+    if (ret == NULL) {
+      return 0;
+    }
+  } else {
+    ret = *outp;
+    BN_zero(ret);
+  }
+  ret->neg = neg;
+
+  /* i is the number of hex digests; */
+  if (bn_expand(ret, i * 4) == NULL) {
+    goto err;
+  }
+
+  decode(ret, in, i);
+
+  bn_correct_top(ret);
+
+  *outp = ret;
+  return num;
+
+err:
+  if (*outp == NULL) {
+    BN_free(ret);
+  }
+
+  return 0;
+}
+
+int BN_hex2bn(BIGNUM **outp, const char *in) {
+  return bn_x2bn(outp, in, decode_hex, isxdigit);
+}
+
+char *BN_bn2dec(const BIGNUM *a) {
+  int i = 0, num, ok = 0;
+  char *buf = NULL;
+  char *p;
+  BIGNUM *t = NULL;
+  BN_ULONG *bn_data = NULL, *lp;
+
+  /* get an upper bound for the length of the decimal integer
+   * num <= (BN_num_bits(a) + 1) * log(2)
+   *     <= 3 * BN_num_bits(a) * 0.1001 + log(2) + 1     (rounding error)
+   *     <= BN_num_bits(a)/10 + BN_num_bits/1000 + 1 + 1
+   */
+  i = BN_num_bits(a) * 3;
+  num = i / 10 + i / 1000 + 1 + 1;
+  bn_data =
+      (BN_ULONG *)OPENSSL_malloc((num / BN_DEC_NUM + 1) * sizeof(BN_ULONG));
+  buf = (char *)OPENSSL_malloc(num + 3);
+  if ((buf == NULL) || (bn_data == NULL)) {
+    OPENSSL_PUT_ERROR(BN, BN_bn2dec, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  t = BN_dup(a);
+  if (t == NULL) {
+    goto err;
+  }
+
+#define BUF_REMAIN (num + 3 - (size_t)(p - buf))
+  p = buf;
+  lp = bn_data;
+  if (BN_is_zero(t)) {
+    *(p++) = '0';
+    *(p++) = '\0';
+  } else {
+    if (BN_is_negative(t)) {
+      *p++ = '-';
+    }
+
+    i = 0;
+    while (!BN_is_zero(t)) {
+      *lp = BN_div_word(t, BN_DEC_CONV);
+      lp++;
+    }
+    lp--;
+    /* We now have a series of blocks, BN_DEC_NUM chars
+     * in length, where the last one needs truncation.
+     * The blocks need to be reversed in order. */
+    BIO_snprintf(p, BUF_REMAIN, BN_DEC_FMT1, *lp);
+    while (*p) {
+      p++;
+    }
+    while (lp != bn_data) {
+      lp--;
+      BIO_snprintf(p, BUF_REMAIN, BN_DEC_FMT2, *lp);
+      while (*p) {
+        p++;
+      }
+    }
+  }
+  ok = 1;
+
+err:
+  if (bn_data != NULL) {
+    OPENSSL_free(bn_data);
+  }
+  if (t != NULL) {
+    BN_free(t);
+  }
+  if (!ok && buf) {
+    OPENSSL_free(buf);
+    buf = NULL;
+  }
+
+  return buf;
+}
+
+int BN_dec2bn(BIGNUM **outp, const char *in) {
+  return bn_x2bn(outp, in, decode_dec, isdigit);
+}
+
+int BN_asc2bn(BIGNUM **outp, const char *in) {
+  const char *const orig_in = in;
+  if (*in == '-') {
+    in++;
+  }
+
+  if (in[0] == '0' && (in[1] == 'X' || in[1] == 'x')) {
+    if (!BN_hex2bn(outp, in+2)) {
+      return 0;
+    }
+  } else {
+    if (!BN_dec2bn(outp, in)) {
+      return 0;
+    }
+  }
+
+  if (*orig_in == '-') {
+    (*outp)->neg = 1;
+  }
+
+  return 1;
+}
+
+int BN_print(BIO *bp, const BIGNUM *a) {
+  int i, j, v, z = 0;
+  int ret = 0;
+
+  if (a->neg && BIO_write(bp, "-", 1) != 1) {
+    goto end;
+  }
+
+  if (BN_is_zero(a) && BIO_write(bp, "0", 1) != 1) {
+    goto end;
+  }
+
+  for (i = a->top - 1; i >= 0; i--) {
+    for (j = BN_BITS2 - 4; j >= 0; j -= 4) {
+      /* strip leading zeros */
+      v = ((int)(a->d[i] >> (long)j)) & 0x0f;
+      if (z || v != 0) {
+        if (BIO_write(bp, &hextable[v], 1) != 1) {
+          goto end;
+        }
+        z = 1;
+      }
+    }
+  }
+  ret = 1;
+
+end:
+  return ret;
+}
+
+int BN_print_fp(FILE *fp, const BIGNUM *a) {
+  BIO *b;
+  int ret;
+
+  b = BIO_new(BIO_s_file());
+  if (b == NULL) {
+    return 0;
+  }
+  BIO_set_fp(b, fp, BIO_NOCLOSE);
+  ret = BN_print(b, a);
+  BIO_free(b);
+
+  return ret;
+}
+
+BN_ULONG BN_get_word(const BIGNUM *bn) {
+  switch (bn->top) {
+    case 0:
+      return 0;
+    case 1:
+      return bn->d[0];
+    default:
+      return BN_MASK2;
+  }
+}
diff --git a/crypto/bn/ctx.c b/crypto/bn/ctx.c
new file mode 100644
index 0000000..a89f87d
--- /dev/null
+++ b/crypto/bn/ctx.c
@@ -0,0 +1,313 @@
+/* Written by Ulf Moeller for the OpenSSL project. */
+/* ====================================================================
+ * Copyright (c) 1998-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <openssl/bn.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+/* How many bignums are in each "pool item"; */
+#define BN_CTX_POOL_SIZE 16
+/* The stack frame info is resizing, set a first-time expansion size; */
+#define BN_CTX_START_FRAMES 32
+
+/* A bundle of bignums that can be linked with other bundles */
+typedef struct bignum_pool_item {
+  /* The bignum values */
+  BIGNUM vals[BN_CTX_POOL_SIZE];
+  /* Linked-list admin */
+  struct bignum_pool_item *prev, *next;
+} BN_POOL_ITEM;
+
+
+typedef struct bignum_pool {
+  /* Linked-list admin */
+  BN_POOL_ITEM *head, *current, *tail;
+  /* Stack depth and allocation size */
+  unsigned used, size;
+} BN_POOL;
+
+static void BN_POOL_init(BN_POOL *);
+static void BN_POOL_finish(BN_POOL *);
+static BIGNUM *BN_POOL_get(BN_POOL *);
+static void BN_POOL_release(BN_POOL *, unsigned int);
+
+/************/
+/* BN_STACK */
+/************/
+
+/* A wrapper to manage the "stack frames" */
+typedef struct bignum_ctx_stack {
+  /* Array of indexes into the bignum stack */
+  unsigned int *indexes;
+  /* Number of stack frames, and the size of the allocated array */
+  unsigned int depth, size;
+} BN_STACK;
+
+static void		BN_STACK_init(BN_STACK *);
+static void		BN_STACK_finish(BN_STACK *);
+static int		BN_STACK_push(BN_STACK *, unsigned int);
+static unsigned int	BN_STACK_pop(BN_STACK *);
+
+/**********/
+/* BN_CTX */
+/**********/
+
+/* The opaque BN_CTX type */
+struct bignum_ctx {
+  /* The bignum bundles */
+  BN_POOL pool;
+  /* The "stack frames", if you will */
+  BN_STACK stack;
+  /* The number of bignums currently assigned */
+  unsigned int used;
+  /* Depth of stack overflow */
+  int err_stack;
+  /* Block "gets" until an "end" (compatibility behaviour) */
+  int too_many;
+};
+
+BN_CTX *BN_CTX_new(void) {
+  BN_CTX *ret = OPENSSL_malloc(sizeof(BN_CTX));
+  if (!ret) {
+    OPENSSL_PUT_ERROR(BN, BN_CTX_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  /* Initialise the structure */
+  BN_POOL_init(&ret->pool);
+  BN_STACK_init(&ret->stack);
+  ret->used = 0;
+  ret->err_stack = 0;
+  ret->too_many = 0;
+  return ret;
+}
+
+void BN_CTX_free(BN_CTX *ctx) {
+  if (ctx == NULL) {
+    return;
+  }
+
+  BN_STACK_finish(&ctx->stack);
+  BN_POOL_finish(&ctx->pool);
+  OPENSSL_free(ctx);
+}
+
+void BN_CTX_start(BN_CTX *ctx) {
+  /* If we're already overflowing ... */
+  if (ctx->err_stack || ctx->too_many) {
+    ctx->err_stack++;
+  } else if (!BN_STACK_push(&ctx->stack, ctx->used)) {
+    /* (Try to) get a new frame pointer */
+    OPENSSL_PUT_ERROR(BN, BN_CTX_start, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
+    ctx->err_stack++;
+  }
+}
+
+BIGNUM *BN_CTX_get(BN_CTX *ctx) {
+  BIGNUM *ret;
+  if (ctx->err_stack || ctx->too_many) {
+    return NULL;
+  }
+
+  ret = BN_POOL_get(&ctx->pool);
+  if (ret == NULL) {
+    /* Setting too_many prevents repeated "get" attempts from
+     * cluttering the error stack. */
+    ctx->too_many = 1;
+    OPENSSL_PUT_ERROR(BN, BN_CTX_get, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
+    return NULL;
+  }
+
+  /* OK, make sure the returned bignum is "zero" */
+  BN_zero(ret);
+  ctx->used++;
+  return ret;
+}
+
+void BN_CTX_end(BN_CTX *ctx) {
+  if (ctx->err_stack) {
+    ctx->err_stack--;
+  } else {
+    unsigned int fp = BN_STACK_pop(&ctx->stack);
+    /* Does this stack frame have anything to release? */
+    if (fp < ctx->used) {
+      BN_POOL_release(&ctx->pool, ctx->used - fp);
+    }
+
+    ctx->used = fp;
+    /* Unjam "too_many" in case "get" had failed */
+    ctx->too_many = 0;
+  }
+}
+
+/************/
+/* BN_STACK */
+/************/
+
+static void BN_STACK_init(BN_STACK *st) {
+  st->indexes = NULL;
+  st->depth = st->size = 0;
+}
+
+static void BN_STACK_finish(BN_STACK *st) {
+  if (st->size)
+    OPENSSL_free(st->indexes);
+}
+
+static int BN_STACK_push(BN_STACK *st, unsigned int idx) {
+  if (st->depth == st->size)
+      /* Need to expand */
+  {
+    unsigned int newsize =
+        (st->size ? (st->size * 3 / 2) : BN_CTX_START_FRAMES);
+    unsigned int *newitems = OPENSSL_malloc(newsize * sizeof(unsigned int));
+    if (!newitems) {
+      return 0;
+    }
+    if (st->depth) {
+      memcpy(newitems, st->indexes, st->depth * sizeof(unsigned int));
+    }
+    if (st->size) {
+      OPENSSL_free(st->indexes);
+    }
+    st->indexes = newitems;
+    st->size = newsize;
+  }
+
+  st->indexes[(st->depth)++] = idx;
+  return 1;
+}
+
+static unsigned int BN_STACK_pop(BN_STACK *st) {
+  return st->indexes[--(st->depth)];
+}
+
+static void BN_POOL_init(BN_POOL *p) {
+  p->head = p->current = p->tail = NULL;
+  p->used = p->size = 0;
+}
+
+static void BN_POOL_finish(BN_POOL *p) {
+  while (p->head) {
+    unsigned int loop = 0;
+    BIGNUM *bn = p->head->vals;
+    while (loop++ < BN_CTX_POOL_SIZE) {
+      if (bn->d) {
+        BN_clear_free(bn);
+      }
+      bn++;
+    }
+
+    p->current = p->head->next;
+    OPENSSL_free(p->head);
+    p->head = p->current;
+  }
+}
+
+static BIGNUM *BN_POOL_get(BN_POOL *p) {
+  if (p->used == p->size) {
+    BIGNUM *bn;
+    unsigned int loop = 0;
+    BN_POOL_ITEM *item = OPENSSL_malloc(sizeof(BN_POOL_ITEM));
+    if (!item) {
+      return NULL;
+    }
+
+    /* Initialise the structure */
+    bn = item->vals;
+    while (loop++ < BN_CTX_POOL_SIZE) {
+      BN_init(bn++);
+    }
+
+    item->prev = p->tail;
+    item->next = NULL;
+    /* Link it in */
+    if (!p->head) {
+      p->head = p->current = p->tail = item;
+    } else {
+      p->tail->next = item;
+      p->tail = item;
+      p->current = item;
+    }
+
+    p->size += BN_CTX_POOL_SIZE;
+    p->used++;
+    /* Return the first bignum from the new pool */
+    return item->vals;
+  }
+
+  if (!p->used) {
+    p->current = p->head;
+  } else if ((p->used % BN_CTX_POOL_SIZE) == 0) {
+    p->current = p->current->next;
+  }
+
+  return p->current->vals + ((p->used++) % BN_CTX_POOL_SIZE);
+}
+
+static void BN_POOL_release(BN_POOL *p, unsigned int num) {
+  unsigned int offset = (p->used - 1) % BN_CTX_POOL_SIZE;
+  p->used -= num;
+
+  while (num--) {
+    if (!offset) {
+      offset = BN_CTX_POOL_SIZE - 1;
+      p->current = p->current->prev;
+    } else {
+      offset--;
+    }
+  }
+}
diff --git a/crypto/bn/div.c b/crypto/bn/div.c
new file mode 100644
index 0000000..5f92e9e
--- /dev/null
+++ b/crypto/bn/div.c
@@ -0,0 +1,619 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include <limits.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+
+
+#define asm __asm__
+
+#if !defined(OPENSSL_NO_ASM)
+# if defined(__GNUC__) && __GNUC__>=2
+#  if defined(OPENSSL_X86)
+   /*
+    * There were two reasons for implementing this template:
+    * - GNU C generates a call to a function (__udivdi3 to be exact)
+    *   in reply to ((((BN_ULLONG)n0)<<BN_BITS2)|n1)/d0 (I fail to
+    *   understand why...);
+    * - divl doesn't only calculate quotient, but also leaves
+    *   remainder in %edx which we can definitely use here:-)
+    *
+    *					<appro@fy.chalmers.se>
+    */
+#undef div_asm
+#  define div_asm(n0,n1,d0)		\
+	({  asm volatile (			\
+		"divl	%4"			\
+		: "=a"(q), "=d"(rem)		\
+		: "a"(n1), "d"(n0), "g"(d0)	\
+		: "cc");			\
+	    q;					\
+	})
+#  define REMAINDER_IS_ALREADY_CALCULATED
+#  elif defined(OPENSSL_X86_64)
+   /*
+    * Same story here, but it's 128-bit by 64-bit division. Wow!
+    *					<appro@fy.chalmers.se>
+    */
+#  undef div_asm
+#  define div_asm(n0,n1,d0)		\
+	({  asm volatile (			\
+		"divq	%4"			\
+		: "=a"(q), "=d"(rem)		\
+		: "a"(n1), "d"(n0), "g"(d0)	\
+		: "cc");			\
+	    q;					\
+	})
+#  define REMAINDER_IS_ALREADY_CALCULATED
+#  endif /* __<cpu> */
+# endif /* __GNUC__ */
+#endif /* OPENSSL_NO_ASM */
+
+/* BN_div computes  dv := num / divisor,  rounding towards
+ * zero, and sets up rm  such that  dv*divisor + rm = num  holds.
+ * Thus:
+ *     dv->neg == num->neg ^ divisor->neg  (unless the result is zero)
+ *     rm->neg == num->neg                 (unless the remainder is zero)
+ * If 'dv' or 'rm' is NULL, the respective value is not returned. */
+int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
+           BN_CTX *ctx) {
+  int norm_shift, i, loop;
+  BIGNUM *tmp, wnum, *snum, *sdiv, *res;
+  BN_ULONG *resp, *wnump;
+  BN_ULONG d0, d1;
+  int num_n, div_n;
+  int no_branch = 0;
+
+  /* Invalid zero-padding would have particularly bad consequences
+   * in the case of 'num'. */
+  if (num->top > 0 && num->d[num->top - 1] == 0) {
+    OPENSSL_PUT_ERROR(BN, BN_div, BN_R_NOT_INITIALIZED);
+    return 0;
+  }
+
+  if ((num->flags & BN_FLG_CONSTTIME) != 0 ||
+      (divisor->flags & BN_FLG_CONSTTIME) != 0) {
+    no_branch = 1;
+  }
+
+  if (BN_is_zero(divisor)) {
+    OPENSSL_PUT_ERROR(BN, BN_div, BN_R_DIV_BY_ZERO);
+    return 0;
+  }
+
+  if (!no_branch && BN_ucmp(num, divisor) < 0) {
+    if (rm != NULL) {
+      if (BN_copy(rm, num) == NULL) {
+        return 0;
+      }
+    }
+    if (dv != NULL) {
+      BN_zero(dv);
+    }
+    return 1;
+  }
+
+  BN_CTX_start(ctx);
+  tmp = BN_CTX_get(ctx);
+  snum = BN_CTX_get(ctx);
+  sdiv = BN_CTX_get(ctx);
+  if (dv == NULL) {
+    res = BN_CTX_get(ctx);
+  } else {
+    res = dv;
+  }
+  if (sdiv == NULL || res == NULL || tmp == NULL || snum == NULL) {
+    goto err;
+  }
+
+  /* First we normalise the numbers */
+  norm_shift = BN_BITS2 - ((BN_num_bits(divisor)) % BN_BITS2);
+  if (!(BN_lshift(sdiv, divisor, norm_shift))) {
+    goto err;
+  }
+  sdiv->neg = 0;
+  norm_shift += BN_BITS2;
+  if (!(BN_lshift(snum, num, norm_shift))) {
+    goto err;
+  }
+  snum->neg = 0;
+
+  if (no_branch) {
+    /* Since we don't know whether snum is larger than sdiv,
+     * we pad snum with enough zeroes without changing its
+     * value.
+     */
+    if (snum->top <= sdiv->top + 1) {
+      if (bn_wexpand(snum, sdiv->top + 2) == NULL) {
+        goto err;
+      }
+      for (i = snum->top; i < sdiv->top + 2; i++) {
+        snum->d[i] = 0;
+      }
+      snum->top = sdiv->top + 2;
+    } else {
+      if (bn_wexpand(snum, snum->top + 1) == NULL) {
+        goto err;
+      }
+      snum->d[snum->top] = 0;
+      snum->top++;
+    }
+  }
+
+  div_n = sdiv->top;
+  num_n = snum->top;
+  loop = num_n - div_n;
+  /* Lets setup a 'window' into snum
+   * This is the part that corresponds to the current
+   * 'area' being divided */
+  wnum.neg = 0;
+  wnum.d = &(snum->d[loop]);
+  wnum.top = div_n;
+  /* only needed when BN_ucmp messes up the values between top and max */
+  wnum.dmax = snum->dmax - loop; /* so we don't step out of bounds */
+
+  /* Get the top 2 words of sdiv */
+  /* div_n=sdiv->top; */
+  d0 = sdiv->d[div_n - 1];
+  d1 = (div_n == 1) ? 0 : sdiv->d[div_n - 2];
+
+  /* pointer to the 'top' of snum */
+  wnump = &(snum->d[num_n - 1]);
+
+  /* Setup to 'res' */
+  res->neg = (num->neg ^ divisor->neg);
+  if (!bn_wexpand(res, (loop + 1))) {
+    goto err;
+  }
+  res->top = loop - no_branch;
+  resp = &(res->d[loop - 1]);
+
+  /* space for temp */
+  if (!bn_wexpand(tmp, (div_n + 1))) {
+    goto err;
+  }
+
+  if (!no_branch) {
+    if (BN_ucmp(&wnum, sdiv) >= 0) {
+      bn_sub_words(wnum.d, wnum.d, sdiv->d, div_n);
+      *resp = 1;
+    } else {
+      res->top--;
+    }
+  }
+
+  /* if res->top == 0 then clear the neg value otherwise decrease
+   * the resp pointer */
+  if (res->top == 0) {
+    res->neg = 0;
+  } else {
+    resp--;
+  }
+
+  for (i = 0; i < loop - 1; i++, wnump--, resp--) {
+    BN_ULONG q, l0;
+    /* the first part of the loop uses the top two words of snum and sdiv to
+     * calculate a BN_ULONG q such that | wnum - sdiv * q | < sdiv */
+    BN_ULONG n0, n1, rem = 0;
+
+    n0 = wnump[0];
+    n1 = wnump[-1];
+    if (n0 == d0) {
+      q = BN_MASK2;
+    } else {
+      /* n0 < d0 */
+#ifdef BN_LLONG
+      BN_ULLONG t2;
+
+#if defined(BN_LLONG) && defined(BN_DIV2W) && !defined(div_asm)
+      q = (BN_ULONG)(((((BN_ULLONG)n0) << BN_BITS2) | n1) / d0);
+#else
+      q = div_asm(n0, n1, d0);
+#endif
+
+#ifndef REMAINDER_IS_ALREADY_CALCULATED
+      /* rem doesn't have to be BN_ULLONG. The least we know it's less that d0,
+       * isn't it? */
+      rem = (n1 - q * d0) & BN_MASK2;
+#endif
+
+      t2 = (BN_ULLONG)d1 * q;
+
+      for (;;) {
+        if (t2 <= ((((BN_ULLONG)rem) << BN_BITS2) | wnump[-2]))
+          break;
+        q--;
+        rem += d0;
+        if (rem < d0)
+          break; /* don't let rem overflow */
+        t2 -= d1;
+      }
+#else /* !BN_LLONG */
+      BN_ULONG t2l, t2h;
+
+#if defined(div_asm)
+      q = div_asm(n0, n1, d0);
+#else
+      q = bn_div_words(n0, n1, d0);
+#endif
+
+#ifndef REMAINDER_IS_ALREADY_CALCULATED
+      rem = (n1 - q * d0) & BN_MASK2;
+#endif
+
+#if defined(BN_UMULT_LOHI)
+      BN_UMULT_LOHI(t2l, t2h, d1, q);
+#elif defined(BN_UMULT_HIGH)
+      t2l = d1 * q;
+      t2h = BN_UMULT_HIGH(d1, q);
+#else
+      {
+        BN_ULONG ql, qh;
+        t2l = LBITS(d1);
+        t2h = HBITS(d1);
+        ql = LBITS(q);
+        qh = HBITS(q);
+        mul64(t2l, t2h, ql, qh); /* t2=(BN_ULLONG)d1*q; */
+      }
+#endif
+
+      for (;;) {
+        if ((t2h < rem) || ((t2h == rem) && (t2l <= wnump[-2])))
+          break;
+        q--;
+        rem += d0;
+        if (rem < d0)
+          break; /* don't let rem overflow */
+        if (t2l < d1)
+          t2h--;
+        t2l -= d1;
+      }
+#endif /* !BN_LLONG */
+    }
+
+    l0 = bn_mul_words(tmp->d, sdiv->d, div_n, q);
+    tmp->d[div_n] = l0;
+    wnum.d--;
+    /* ingore top values of the bignums just sub the two
+     * BN_ULONG arrays with bn_sub_words */
+    if (bn_sub_words(wnum.d, wnum.d, tmp->d, div_n + 1)) {
+      /* Note: As we have considered only the leading
+       * two BN_ULONGs in the calculation of q, sdiv * q
+       * might be greater than wnum (but then (q-1) * sdiv
+       * is less or equal than wnum)
+       */
+      q--;
+      if (bn_add_words(wnum.d, wnum.d, sdiv->d, div_n)) {
+        /* we can't have an overflow here (assuming
+         * that q != 0, but if q == 0 then tmp is
+         * zero anyway) */
+        (*wnump)++;
+      }
+    }
+    /* store part of the result */
+    *resp = q;
+  }
+  bn_correct_top(snum);
+  if (rm != NULL) {
+    /* Keep a copy of the neg flag in num because if rm==num
+     * BN_rshift() will overwrite it.
+     */
+    int neg = num->neg;
+    BN_rshift(rm, snum, norm_shift);
+    if (!BN_is_zero(rm)) {
+      rm->neg = neg;
+    }
+  }
+  if (no_branch) {
+    bn_correct_top(res);
+  }
+  BN_CTX_end(ctx);
+  return 1;
+
+err:
+  BN_CTX_end(ctx);
+  return 0;
+}
+
+int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) {
+  if (!(BN_mod(r, m, d, ctx))) {
+    return 0;
+  }
+  if (!r->neg) {
+    return 1;
+  }
+
+  /* now -|d| < r < 0, so we have to set r := r + |d|. */
+  return (d->neg ? BN_sub : BN_add)(r, r, d);
+}
+
+int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx) {
+  if (!BN_add(r, a, b)) {
+    return 0;
+  }
+  return BN_nnmod(r, r, m, ctx);
+}
+
+int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m) {
+  if (!BN_uadd(r, a, b)) {
+    return 0;
+  }
+  if (BN_ucmp(r, m) >= 0) {
+    return BN_usub(r, r, m);
+  }
+  return 1;
+}
+
+int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx) {
+  if (!BN_sub(r, a, b)) {
+    return 0;
+  }
+  return BN_nnmod(r, r, m, ctx);
+}
+
+/* BN_mod_sub variant that may be used if both  a  and  b  are non-negative
+ * and less than  m */
+int BN_mod_sub_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m) {
+  if (!BN_sub(r, a, b)) {
+    return 0;
+  }
+  if (r->neg) {
+    return BN_add(r, r, m);
+  }
+  return 1;
+}
+
+int BN_mod_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx) {
+  BIGNUM *t;
+  int ret = 0;
+
+  BN_CTX_start(ctx);
+  t = BN_CTX_get(ctx);
+  if (t == NULL) {
+    goto err;
+  }
+
+  if (a == b) {
+    if (!BN_sqr(t, a, ctx)) {
+      goto err;
+    }
+  } else {
+    if (!BN_mul(t, a, b, ctx)) {
+      goto err;
+    }
+  }
+
+  if (!BN_nnmod(r, t, m, ctx)) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx) {
+  if (!BN_sqr(r, a, ctx)) {
+    return 0;
+  }
+
+  /* r->neg == 0,  thus we don't need BN_nnmod */
+  return BN_mod(r, r, m, ctx);
+}
+
+int BN_mod_lshift(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
+                  BN_CTX *ctx) {
+  BIGNUM *abs_m = NULL;
+  int ret;
+
+  if (!BN_nnmod(r, a, m, ctx)) {
+    return 0;
+  }
+
+  if (m->neg) {
+    abs_m = BN_dup(m);
+    if (abs_m == NULL) {
+      return 0;
+    }
+    abs_m->neg = 0;
+  }
+
+  ret = BN_mod_lshift_quick(r, r, n, (abs_m ? abs_m : m));
+
+  if (abs_m) {
+    BN_free(abs_m);
+  }
+  return ret;
+}
+
+int BN_mod_lshift_quick(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m) {
+  if (r != a) {
+    if (BN_copy(r, a) == NULL) {
+      return 0;
+    }
+  }
+
+  while (n > 0) {
+    int max_shift;
+
+    /* 0 < r < m */
+    max_shift = BN_num_bits(m) - BN_num_bits(r);
+    /* max_shift >= 0 */
+
+    if (max_shift < 0) {
+      OPENSSL_PUT_ERROR(BN, BN_mod_lshift_quick, BN_R_INPUT_NOT_REDUCED);
+      return 0;
+    }
+
+    if (max_shift > n) {
+      max_shift = n;
+    }
+
+    if (max_shift) {
+      if (!BN_lshift(r, r, max_shift)) {
+        return 0;
+      }
+      n -= max_shift;
+    } else {
+      if (!BN_lshift1(r, r)) {
+        return 0;
+      }
+      --n;
+    }
+
+    /* BN_num_bits(r) <= BN_num_bits(m) */
+    if (BN_cmp(r, m) >= 0) {
+      if (!BN_sub(r, r, m)) {
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+
+int BN_mod_lshift1(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx) {
+  if (!BN_lshift1(r, a)) {
+    return 0;
+  }
+
+  return BN_nnmod(r, r, m, ctx);
+}
+
+int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *m) {
+  if (!BN_lshift1(r, a)) {
+    return 0;
+  }
+  if (BN_cmp(r, m) >= 0) {
+    return BN_sub(r, r, m);
+  }
+
+  return 1;
+}
+
+BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w) {
+  BN_ULONG ret = 0;
+  int i, j;
+
+  w &= BN_MASK2;
+
+  if (!w) {
+    /* actually this an error (division by zero) */
+    return (BN_ULONG) - 1;
+  }
+
+  if (a->top == 0) {
+    return 0;
+  }
+
+  /* normalize input (so bn_div_words doesn't complain) */
+  j = BN_BITS2 - BN_num_bits_word(w);
+  w <<= j;
+  if (!BN_lshift(a, a, j)) {
+    return (BN_ULONG) - 1;
+  }
+
+  for (i = a->top - 1; i >= 0; i--) {
+    BN_ULONG l, d;
+
+    l = a->d[i];
+    d = bn_div_words(ret, l, w);
+    ret = (l - ((d * w) & BN_MASK2)) & BN_MASK2;
+    a->d[i] = d;
+  }
+
+  if ((a->top > 0) && (a->d[a->top - 1] == 0)) {
+    a->top--;
+  }
+
+  ret >>= j;
+  return ret;
+}
+
+BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w) {
+#ifndef BN_LLONG
+  BN_ULONG ret = 0;
+#else
+  BN_ULLONG ret = 0;
+#endif
+  int i;
+
+  if (w == 0) {
+    return (BN_ULONG) -1;
+  }
+
+  w &= BN_MASK2;
+  for (i = a->top - 1; i >= 0; i--) {
+#ifndef BN_LLONG
+    ret = ((ret << BN_BITS4) | ((a->d[i] >> BN_BITS4) & BN_MASK2l)) % w;
+    ret = ((ret << BN_BITS4) | (a->d[i] & BN_MASK2l)) % w;
+#else
+    ret = (BN_ULLONG)(((ret << (BN_ULLONG)BN_BITS2) | a->d[i]) % (BN_ULLONG)w);
+#endif
+  }
+  return (BN_ULONG)ret;
+}
diff --git a/crypto/bn/exponentiation.c b/crypto/bn/exponentiation.c
new file mode 100644
index 0000000..b51f15f
--- /dev/null
+++ b/crypto/bn/exponentiation.c
@@ -0,0 +1,1500 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include <openssl/cpu.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64)
+#define OPENSSL_BN_ASM_MONT5
+#define RSAZ_ENABLED
+
+#include "rsaz_exp.h"
+#endif
+
+int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) {
+  int i, bits, ret = 0;
+  BIGNUM *v, *rr;
+
+  if ((p->flags & BN_FLG_CONSTTIME) != 0) {
+    /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+    OPENSSL_PUT_ERROR(BN, BN_exp, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
+  BN_CTX_start(ctx);
+  if (r == a || r == p) {
+    rr = BN_CTX_get(ctx);
+  } else {
+    rr = r;
+  }
+
+  v = BN_CTX_get(ctx);
+  if (rr == NULL || v == NULL) {
+    goto err;
+  }
+
+  if (BN_copy(v, a) == NULL) {
+    goto err;
+  }
+  bits = BN_num_bits(p);
+
+  if (BN_is_odd(p)) {
+    if (BN_copy(rr, a) == NULL) {
+      goto err;
+    }
+  } else {
+    if (!BN_one(rr)) {
+      goto err;
+    }
+  }
+
+  for (i = 1; i < bits; i++) {
+    if (!BN_sqr(v, v, ctx)) {
+      goto err;
+    }
+    if (BN_is_bit_set(p, i)) {
+      if (!BN_mul(rr, rr, v, ctx)) {
+        goto err;
+      }
+    }
+  }
+  ret = 1;
+
+err:
+  if (r != rr) {
+    BN_copy(r, rr);
+  }
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+/* maximum precomputation table size for *variable* sliding windows */
+#define TABLE_SIZE 32
+
+typedef struct bn_recp_ctx_st {
+  BIGNUM N;  /* the divisor */
+  BIGNUM Nr; /* the reciprocal */
+  int num_bits;
+  int shift;
+  int flags;
+} BN_RECP_CTX;
+
+static void BN_RECP_CTX_init(BN_RECP_CTX *recp) {
+  BN_init(&recp->N);
+  BN_init(&recp->Nr);
+  recp->num_bits = 0;
+  recp->flags = 0;
+}
+
+static void BN_RECP_CTX_free(BN_RECP_CTX *recp) {
+  if (recp == NULL) {
+    return;
+  }
+
+  BN_free(&recp->N);
+  BN_free(&recp->Nr);
+}
+
+static int BN_RECP_CTX_set(BN_RECP_CTX *recp, const BIGNUM *d, BN_CTX *ctx) {
+  if (!BN_copy(&(recp->N), d)) {
+    return 0;
+  }
+  BN_zero(&recp->Nr);
+  recp->num_bits = BN_num_bits(d);
+  recp->shift = 0;
+
+  return 1;
+}
+
+/* len is the expected size of the result We actually calculate with an extra
+ * word of precision, so we can do faster division if the remainder is not
+ * required.
+ * r := 2^len / m */
+static int BN_reciprocal(BIGNUM *r, const BIGNUM *m, int len, BN_CTX *ctx) {
+  int ret = -1;
+  BIGNUM *t;
+
+  BN_CTX_start(ctx);
+  t = BN_CTX_get(ctx);
+  if (t == NULL) {
+    goto err;
+  }
+
+  if (!BN_set_bit(t, len)) {
+    goto err;
+  }
+
+  if (!BN_div(r, NULL, t, m, ctx)) {
+    goto err;
+  }
+
+  ret = len;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+static int BN_div_recp(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m,
+                       BN_RECP_CTX *recp, BN_CTX *ctx) {
+  int i, j, ret = 0;
+  BIGNUM *a, *b, *d, *r;
+
+  BN_CTX_start(ctx);
+  a = BN_CTX_get(ctx);
+  b = BN_CTX_get(ctx);
+  if (dv != NULL) {
+    d = dv;
+  } else {
+    d = BN_CTX_get(ctx);
+  }
+
+  if (rem != NULL) {
+    r = rem;
+  } else {
+    r = BN_CTX_get(ctx);
+  }
+
+  if (a == NULL || b == NULL || d == NULL || r == NULL) {
+    goto err;
+  }
+
+  if (BN_ucmp(m, &(recp->N)) < 0) {
+    BN_zero(d);
+    if (!BN_copy(r, m)) {
+      return 0;
+    }
+    BN_CTX_end(ctx);
+    return 1;
+  }
+
+  /* We want the remainder
+   * Given input of ABCDEF / ab
+   * we need multiply ABCDEF by 3 digests of the reciprocal of ab */
+
+  /* i := max(BN_num_bits(m), 2*BN_num_bits(N)) */
+  i = BN_num_bits(m);
+  j = recp->num_bits << 1;
+  if (j > i) {
+    i = j;
+  }
+
+  /* Nr := round(2^i / N) */
+  if (i != recp->shift) {
+    recp->shift =
+        BN_reciprocal(&(recp->Nr), &(recp->N), i,
+                      ctx); /* BN_reciprocal returns i, or -1 for an error */
+  }
+
+  if (recp->shift == -1) {
+    goto err;
+  }
+
+  /* d := |round(round(m / 2^BN_num_bits(N)) * recp->Nr / 2^(i -
+   * BN_num_bits(N)))|
+   *    = |round(round(m / 2^BN_num_bits(N)) * round(2^i / N) / 2^(i -
+   * BN_num_bits(N)))|
+   *   <= |(m / 2^BN_num_bits(N)) * (2^i / N) * (2^BN_num_bits(N) / 2^i)|
+   *    = |m/N| */
+  if (!BN_rshift(a, m, recp->num_bits)) {
+    goto err;
+  }
+  if (!BN_mul(b, a, &(recp->Nr), ctx)) {
+    goto err;
+  }
+  if (!BN_rshift(d, b, i - recp->num_bits)) {
+    goto err;
+  }
+  d->neg = 0;
+
+  if (!BN_mul(b, &(recp->N), d, ctx)) {
+    goto err;
+  }
+  if (!BN_usub(r, m, b)) {
+    goto err;
+  }
+  r->neg = 0;
+
+  j = 0;
+  while (BN_ucmp(r, &(recp->N)) >= 0) {
+    if (j++ > 2) {
+      OPENSSL_PUT_ERROR(BN, BN_div_recp, BN_R_BAD_RECIPROCAL);
+      goto err;
+    }
+    if (!BN_usub(r, r, &(recp->N))) {
+      goto err;
+    }
+    if (!BN_add_word(d, 1)) {
+      goto err;
+    }
+  }
+
+  r->neg = BN_is_zero(r) ? 0 : m->neg;
+  d->neg = m->neg ^ recp->N.neg;
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+static int BN_mod_mul_reciprocal(BIGNUM *r, const BIGNUM *x, const BIGNUM *y,
+                                 BN_RECP_CTX *recp, BN_CTX *ctx) {
+  int ret = 0;
+  BIGNUM *a;
+  const BIGNUM *ca;
+
+  BN_CTX_start(ctx);
+  a = BN_CTX_get(ctx);
+  if (a == NULL) {
+    goto err;
+  }
+
+  if (y != NULL) {
+    if (x == y) {
+      if (!BN_sqr(a, x, ctx)) {
+        goto err;
+      }
+    } else {
+      if (!BN_mul(a, x, y, ctx)) {
+        goto err;
+      }
+    }
+    ca = a;
+  } else {
+    ca = x; /* Just do the mod */
+  }
+
+  ret = BN_div_recp(NULL, r, ca, recp, ctx);
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+/* BN_window_bits_for_exponent_size -- macro for sliding window mod_exp
+ * functions
+ *
+ * For window size 'w' (w >= 2) and a random 'b' bits exponent, the number of
+ * multiplications is a constant plus on average
+ *
+ *    2^(w-1) + (b-w)/(w+1);
+ *
+ * here 2^(w-1)  is for precomputing the table (we actually need entries only
+ * for windows that have the lowest bit set), and (b-w)/(w+1)  is an
+ * approximation for the expected number of w-bit windows, not counting the
+ * first one.
+ *
+ * Thus we should use
+ *
+ *    w >= 6  if        b > 671
+ *     w = 5  if  671 > b > 239
+ *     w = 4  if  239 > b >  79
+ *     w = 3  if   79 > b >  23
+ *    w <= 2  if   23 > b
+ *
+ * (with draws in between).  Very small exponents are often selected
+ * with low Hamming weight, so we use  w = 1  for b <= 23. */
+#define BN_window_bits_for_exponent_size(b) \
+		((b) > 671 ? 6 : \
+		 (b) > 239 ? 5 : \
+		 (b) >  79 ? 4 : \
+		 (b) >  23 ? 3 : 1)
+
+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, wend, window, wvalue;
+  int start = 1;
+  BIGNUM *aa;
+  /* Table of variables obtained from 'ctx' */
+  BIGNUM *val[TABLE_SIZE];
+  BN_RECP_CTX recp;
+
+  if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+    /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+    OPENSSL_PUT_ERROR(BN, mod_exp_recp, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
+  bits = BN_num_bits(p);
+
+  if (bits == 0) {
+    ret = BN_one(r);
+    return ret;
+  }
+
+  BN_CTX_start(ctx);
+  aa = BN_CTX_get(ctx);
+  val[0] = BN_CTX_get(ctx);
+  if (!aa || !val[0]) {
+    goto err;
+  }
+
+  BN_RECP_CTX_init(&recp);
+  if (m->neg) {
+    /* ignore sign of 'm' */
+    if (!BN_copy(aa, m)) {
+      goto err;
+    }
+    aa->neg = 0;
+    if (BN_RECP_CTX_set(&recp, aa, ctx) <= 0) {
+      goto err;
+    }
+  } else {
+    if (BN_RECP_CTX_set(&recp, m, ctx) <= 0) {
+      goto err;
+    }
+  }
+
+  if (!BN_nnmod(val[0], a, m, ctx)) {
+    goto err; /* 1 */
+  }
+  if (BN_is_zero(val[0])) {
+    BN_zero(r);
+    ret = 1;
+    goto err;
+  }
+
+  window = BN_window_bits_for_exponent_size(bits);
+  if (window > 1) {
+    if (!BN_mod_mul_reciprocal(aa, val[0], val[0], &recp, ctx)) {
+      goto err; /* 2 */
+    }
+    j = 1 << (window - 1);
+    for (i = 1; i < j; i++) {
+      if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
+          !BN_mod_mul_reciprocal(val[i], val[i - 1], aa, &recp, ctx)) {
+        goto err;
+      }
+    }
+  }
+
+  start = 1; /* This is used to avoid multiplication etc
+              * when there is only the value '1' in the
+              * buffer. */
+  wvalue = 0;        /* The 'value' of the window */
+  wstart = bits - 1; /* The top bit of the window */
+  wend = 0;          /* The bottom bit of the window */
+
+  if (!BN_one(r)) {
+    goto err;
+  }
+
+  for (;;) {
+    if (BN_is_bit_set(p, wstart) == 0) {
+      if (!start) {
+        if (!BN_mod_mul_reciprocal(r, r, r, &recp, ctx)) {
+          goto err;
+        }
+      }
+      if (wstart == 0) {
+        break;
+      }
+      wstart--;
+      continue;
+    }
+
+    /* We now have wstart on a 'set' bit, we now need to work out
+     * how bit a window to do.  To do this we need to scan
+     * forward until the last set bit before the end of the
+     * window */
+    j = wstart;
+    wvalue = 1;
+    wend = 0;
+    for (i = 1; i < window; i++) {
+      if (wstart - i < 0) {
+        break;
+      }
+      if (BN_is_bit_set(p, wstart - i)) {
+        wvalue <<= (i - wend);
+        wvalue |= 1;
+        wend = i;
+      }
+    }
+
+    /* wend is the size of the current window */
+    j = wend + 1;
+    /* add the 'bytes above' */
+    if (!start) {
+      for (i = 0; i < j; i++) {
+        if (!BN_mod_mul_reciprocal(r, r, r, &recp, ctx)) {
+          goto err;
+        }
+      }
+    }
+
+    /* wvalue will be an odd number < 2^window */
+    if (!BN_mod_mul_reciprocal(r, r, val[wvalue >> 1], &recp, ctx)) {
+      goto err;
+    }
+
+    /* move the 'window' down further */
+    wstart -= wend + 1;
+    wvalue = 0;
+    start = 0;
+    if (wstart < 0) {
+      break;
+    }
+  }
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  BN_RECP_CTX_free(&recp);
+  return ret;
+}
+
+int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
+               BN_CTX *ctx) {
+  /* For even modulus  m = 2^k*m_odd,  it might make sense to compute
+   * a^p mod m_odd  and  a^p mod 2^k  separately (with Montgomery
+   * exponentiation for the odd part), using appropriate exponent
+   * reductions, and combine the results using the CRT.
+   *
+   * For now, we use Montgomery only if the modulus is odd; otherwise,
+   * exponentiation using the reciprocal-based quick remaindering
+   * algorithm is used.
+   *
+   * (Timing obtained with expspeed.c [computations  a^p mod m
+   * where  a, p, m  are of the same length: 256, 512, 1024, 2048,
+   * 4096, 8192 bits], compared to the running time of the
+   * standard algorithm:
+   *
+   *   BN_mod_exp_mont   33 .. 40 %  [AMD K6-2, Linux, debug configuration]
+   *                     55 .. 77 %  [UltraSparc processor, but
+   *                                  debug-solaris-sparcv8-gcc conf.]
+   *
+   *   BN_mod_exp_recp   50 .. 70 %  [AMD K6-2, Linux, debug configuration]
+   *                     62 .. 118 % [UltraSparc, debug-solaris-sparcv8-gcc]
+   *
+   * On the Sparc, BN_mod_exp_recp was faster than BN_mod_exp_mont
+   * at 2048 and more bits, but at 512 and 1024 bits, it was
+   * slower even than the standard algorithm!
+   *
+   * "Real" timings [linux-elf, solaris-sparcv9-gcc configurations]
+   * should be obtained when the new Montgomery reduction code
+   * has been integrated into OpenSSL.) */
+
+  if (BN_is_odd(m)) {
+    if (a->top == 1 && !a->neg && BN_get_flags(p, BN_FLG_CONSTTIME) == 0) {
+      BN_ULONG A = a->d[0];
+      return BN_mod_exp_mont_word(r, A, p, m, ctx, NULL);
+    }
+
+    return BN_mod_exp_mont(r, a, p, m, ctx, NULL);
+  }
+
+  return mod_exp_recp(r, a, p, m, ctx);
+}
+
+int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
+                    const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont) {
+  int i, j, bits, ret = 0, wstart, wend, window, wvalue;
+  int start = 1;
+  BIGNUM *d, *r;
+  const BIGNUM *aa;
+  /* Table of variables obtained from 'ctx' */
+  BIGNUM *val[TABLE_SIZE];
+  BN_MONT_CTX *mont = NULL;
+
+  if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+    return BN_mod_exp_mont_consttime(rr, a, p, m, ctx, in_mont);
+  }
+
+  if (!BN_is_odd(m)) {
+    OPENSSL_PUT_ERROR(BN, BN_mod_exp_mont, BN_R_CALLED_WITH_EVEN_MODULUS);
+    return 0;
+  }
+  bits = BN_num_bits(p);
+  if (bits == 0) {
+    ret = BN_one(rr);
+    return ret;
+  }
+
+  BN_CTX_start(ctx);
+  d = BN_CTX_get(ctx);
+  r = BN_CTX_get(ctx);
+  val[0] = BN_CTX_get(ctx);
+  if (!d || !r || !val[0]) {
+    goto err;
+  }
+
+  /* If this is not done, things will break in the montgomery part */
+
+  if (in_mont != NULL) {
+    mont = in_mont;
+  } else {
+    mont = BN_MONT_CTX_new();
+    if (mont == NULL) {
+      goto err;
+    }
+    if (!BN_MONT_CTX_set(mont, m, ctx)) {
+      goto err;
+    }
+  }
+
+  if (a->neg || BN_ucmp(a, m) >= 0) {
+    if (!BN_nnmod(val[0], a, m, ctx)) {
+      goto err;
+    }
+    aa = val[0];
+  } else {
+    aa = a;
+  }
+
+  if (BN_is_zero(aa)) {
+    BN_zero(rr);
+    ret = 1;
+    goto err;
+  }
+  if (!BN_to_montgomery(val[0], aa, mont, ctx)) {
+    goto err; /* 1 */
+  }
+
+  window = BN_window_bits_for_exponent_size(bits);
+  if (window > 1) {
+    if (!BN_mod_mul_montgomery(d, val[0], val[0], mont, ctx)) {
+      goto err; /* 2 */
+    }
+    j = 1 << (window - 1);
+    for (i = 1; i < j; i++) {
+      if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
+          !BN_mod_mul_montgomery(val[i], val[i - 1], d, mont, ctx)) {
+        goto err;
+      }
+    }
+  }
+
+  start = 1; /* This is used to avoid multiplication etc
+              * when there is only the value '1' in the
+              * buffer. */
+  wvalue = 0;        /* The 'value' of the window */
+  wstart = bits - 1; /* The top bit of the window */
+  wend = 0;          /* The bottom bit of the window */
+
+  j = m->top; /* borrow j */
+  if (m->d[j - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
+    if (bn_wexpand(r, j) == NULL)
+      goto err;
+    /* 2^(top*BN_BITS2) - m */
+    r->d[0] = (0 - m->d[0]) & BN_MASK2;
+    for (i = 1; i < j; i++)
+      r->d[i] = (~m->d[i]) & BN_MASK2;
+    r->top = j;
+  } else if (!BN_to_montgomery(r, BN_value_one(), mont, ctx)) {
+    goto err;
+  }
+
+  for (;;) {
+    if (BN_is_bit_set(p, wstart) == 0) {
+      if (!start) {
+        if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
+          goto err;
+      }
+      if (wstart == 0) {
+        break;
+      }
+      wstart--;
+      continue;
+    }
+
+    /* We now have wstart on a 'set' bit, we now need to work out how bit a
+     * window to do.  To do this we need to scan forward until the last set bit
+     * before the end of the window */
+    j = wstart;
+    wvalue = 1;
+    wend = 0;
+    for (i = 1; i < window; i++) {
+      if (wstart - i < 0) {
+        break;
+      }
+      if (BN_is_bit_set(p, wstart - i)) {
+        wvalue <<= (i - wend);
+        wvalue |= 1;
+        wend = i;
+      }
+    }
+
+    /* wend is the size of the current window */
+    j = wend + 1;
+    /* add the 'bytes above' */
+    if (!start) {
+      for (i = 0; i < j; i++) {
+        if (!BN_mod_mul_montgomery(r, r, r, mont, ctx)) {
+          goto err;
+        }
+      }
+    }
+
+    /* wvalue will be an odd number < 2^window */
+    if (!BN_mod_mul_montgomery(r, r, val[wvalue >> 1], mont, ctx)) {
+      goto err;
+    }
+
+    /* move the 'window' down further */
+    wstart -= wend + 1;
+    wvalue = 0;
+    start = 0;
+    if (wstart < 0) {
+      break;
+    }
+  }
+
+  if (!BN_from_montgomery(rr, r, mont, ctx)) {
+    goto err;
+  }
+  ret = 1;
+
+err:
+  if (in_mont == NULL && mont != NULL) {
+    BN_MONT_CTX_free(mont);
+  }
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+/* BN_mod_exp_mont_consttime() stores the precomputed powers in a specific
+ * layout so that accessing any of these table values shows the same access
+ * pattern as far as cache lines are concerned. The following functions are
+ * used to transfer a BIGNUM from/to that table. */
+static int copy_to_prebuf(const BIGNUM *b, int top, unsigned char *buf, int idx,
+                          int width) {
+  size_t i, j;
+
+  if (top > b->top) {
+    top = b->top; /* this works because 'buf' is explicitly zeroed */
+  }
+  for (i = 0, j = idx; i < top * sizeof b->d[0]; i++, j += width) {
+    buf[j] = ((unsigned char *)b->d)[i];
+  }
+
+  return 1;
+}
+
+static int copy_from_prebuf(BIGNUM *b, int top, unsigned char *buf, int idx,
+                            int width) {
+  size_t i, j;
+
+  if (bn_wexpand(b, top) == NULL) {
+    return 0;
+  }
+
+  for (i = 0, j = idx; i < top * sizeof b->d[0]; i++, j += width) {
+    ((unsigned char *)b->d)[i] = buf[j];
+  }
+
+  b->top = top;
+  bn_correct_top(b);
+  return 1;
+}
+
+/* BN_mod_exp_mont_conttime 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)
+#define MOD_EXP_CTIME_MIN_CACHE_LINE_MASK \
+  (MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH - 1)
+
+/* Window sizes optimized for fixed window size modular exponentiation
+ * algorithm (BN_mod_exp_mont_consttime).
+ *
+ * To achieve the security goals of BN_mode_exp_mont_consttime, the maximum
+ * size of the window must not exceed
+ * log_2(MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH). 
+ *
+ * Window size thresholds are defined for cache line sizes of 32 and 64, cache
+ * line sizes where log_2(32)=5 and log_2(64)=6 respectively. A window size of
+ * 7 should only be used on processors that have a 128 byte or greater cache
+ * line size. */
+#if MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH == 64
+
+#define BN_window_bits_for_ctime_exponent_size(b) \
+  ((b) > 937 ? 6 : (b) > 306 ? 5 : (b) > 89 ? 4 : (b) > 22 ? 3 : 1)
+#define BN_MAX_WINDOW_BITS_FOR_CTIME_EXPONENT_SIZE (6)
+
+#elif MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH == 32
+
+#define BN_window_bits_for_ctime_exponent_size(b) \
+  ((b) > 306 ? 5 : (b) > 89 ? 4 : (b) > 22 ? 3 : 1)
+#define BN_MAX_WINDOW_BITS_FOR_CTIME_EXPONENT_SIZE (5)
+
+#endif
+
+/* Given a pointer value, compute the next address that is a cache line
+ * multiple. */
+#define MOD_EXP_CTIME_ALIGN(x_)          \
+  ((unsigned char *)(x_) +               \
+   (MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH - \
+    (((size_t)(x_)) & (MOD_EXP_CTIME_MIN_CACHE_LINE_MASK))))
+
+/* This variant of BN_mod_exp_mont() uses fixed windows and the special
+ * precomputation memory layout to limit data-dependency to a minimum
+ * to protect secret exponents (cf. the hyper-threading timing attacks
+ * pointed out by Colin Percival,
+ * http://www.daemonology.net/hyperthreading-considered-harmful/)
+ */
+int BN_mod_exp_mont_consttime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
+                              const BIGNUM *m, BN_CTX *ctx,
+                              BN_MONT_CTX *in_mont) {
+  int i, bits, ret = 0, window, wvalue;
+  int top;
+  BN_MONT_CTX *mont = NULL;
+
+  int numPowers;
+  unsigned char *powerbufFree = NULL;
+  int powerbufLen = 0;
+  unsigned char *powerbuf = NULL;
+  BIGNUM tmp, am;
+
+  top = m->top;
+
+  if (!(m->d[0] & 1)) {
+    OPENSSL_PUT_ERROR(BN, BN_mod_exp_mont_consttime,
+                      BN_R_CALLED_WITH_EVEN_MODULUS);
+    return 0;
+  }
+  bits = BN_num_bits(p);
+  if (bits == 0) {
+    ret = BN_one(rr);
+    return ret;
+  }
+
+  BN_CTX_start(ctx);
+
+  /* Allocate a montgomery context if it was not supplied by the caller.
+   * If this is not done, things will break in the montgomery part.
+   */
+  if (in_mont != NULL)
+    mont = in_mont;
+  else {
+    if ((mont = BN_MONT_CTX_new()) == NULL)
+      goto err;
+    if (!BN_MONT_CTX_set(mont, m, ctx))
+      goto err;
+  }
+
+#ifdef RSAZ_ENABLED
+  /* If the size of the operands allow it, perform the optimized
+   * RSAZ exponentiation. For further information see
+   * crypto/bn/rsaz_exp.c and accompanying assembly modules. */
+  if (((OPENSSL_ia32cap_P[2] & 0x80100) != 0x80100) /* check for MULX/AD*X */
+      && (16 == a->top) && (16 == p->top) && (BN_num_bits(m) == 1024) &&
+      rsaz_avx2_eligible()) {
+    if (NULL == bn_wexpand(rr, 16))
+      goto err;
+    RSAZ_1024_mod_exp_avx2(rr->d, a->d, p->d, m->d, mont->RR.d, mont->n0[0]);
+    rr->top = 16;
+    rr->neg = 0;
+    bn_correct_top(rr);
+    ret = 1;
+    goto err;
+  } else if ((8 == a->top) && (8 == p->top) && (BN_num_bits(m) == 512)) {
+    if (NULL == bn_wexpand(rr, 8))
+      goto err;
+    RSAZ_512_mod_exp(rr->d, a->d, p->d, m->d, mont->n0[0], mont->RR.d);
+    rr->top = 8;
+    rr->neg = 0;
+    bn_correct_top(rr);
+    ret = 1;
+    goto err;
+  }
+#endif
+
+  /* Get the window size to use with size of p. */
+  window = BN_window_bits_for_ctime_exponent_size(bits);
+#if defined(OPENSSL_BN_ASM_MONT5)
+  if (window >= 5) {
+    window = 5; /* ~5% improvement for RSA2048 sign, and even for RSA4096 */
+    if ((top & 7) == 0)
+      powerbufLen += 2 * top * sizeof(m->d[0]);
+  }
+#endif
+  (void)0;
+
+  /* Allocate a buffer large enough to hold all of the pre-computed
+   * powers of am, am itself and tmp.
+   */
+  numPowers = 1 << window;
+  powerbufLen +=
+      sizeof(m->d[0]) *
+      (top * numPowers + ((2 * top) > numPowers ? (2 * top) : numPowers));
+#ifdef alloca
+  if (powerbufLen < 3072)
+    powerbufFree = alloca(powerbufLen + MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH);
+  else
+#endif
+      if ((powerbufFree = (unsigned char *)OPENSSL_malloc(
+               powerbufLen + MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH)) == NULL)
+    goto err;
+
+  powerbuf = MOD_EXP_CTIME_ALIGN(powerbufFree);
+  memset(powerbuf, 0, powerbufLen);
+
+#ifdef alloca
+  if (powerbufLen < 3072)
+    powerbufFree = NULL;
+#endif
+
+  /* lay down tmp and am right after powers table */
+  tmp.d = (BN_ULONG *)(powerbuf + sizeof(m->d[0]) * top * numPowers);
+  am.d = tmp.d + top;
+  tmp.top = am.top = 0;
+  tmp.dmax = am.dmax = top;
+  tmp.neg = am.neg = 0;
+  tmp.flags = am.flags = BN_FLG_STATIC_DATA;
+
+/* prepare a^0 in Montgomery domain */
+/* by Shay Gueron's suggestion */
+  if (m->d[top - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
+    /* 2^(top*BN_BITS2) - m */
+    tmp.d[0] = (0 - m->d[0]) & BN_MASK2;
+    for (i = 1; i < top; i++)
+      tmp.d[i] = (~m->d[i]) & BN_MASK2;
+    tmp.top = top;
+  } else if (!BN_to_montgomery(&tmp, BN_value_one(), mont, ctx))
+    goto err;
+
+  /* prepare a^1 in Montgomery domain */
+  if (a->neg || BN_ucmp(a, m) >= 0) {
+    if (!BN_mod(&am, a, m, ctx))
+      goto err;
+    if (!BN_to_montgomery(&am, &am, mont, ctx))
+      goto err;
+  } else if (!BN_to_montgomery(&am, a, mont, ctx))
+    goto err;
+
+#if defined(OPENSSL_BN_ASM_MONT5)
+  /* This optimization uses ideas from http://eprint.iacr.org/2011/239,
+   * specifically optimization of cache-timing attack countermeasures
+   * and pre-computation optimization. */
+
+  /* Dedicated window==4 case improves 512-bit RSA sign by ~15%, but as
+   * 512-bit RSA is hardly relevant, we omit it to spare size... */
+  if (window == 5) {
+    void bn_mul_mont_gather5(BN_ULONG * rp, const BN_ULONG * ap,
+                             const void * table, const BN_ULONG * np,
+                             const BN_ULONG * n0, int num, int power);
+    void bn_scatter5(const BN_ULONG * inp, size_t num, void * table,
+                     size_t power);
+    void bn_gather5(BN_ULONG * out, size_t num, void * table, size_t power);
+    void bn_power5(BN_ULONG * rp, const BN_ULONG * ap, const void * table,
+                   const BN_ULONG * np, const BN_ULONG * n0, int num,
+                   int power);
+    int bn_get_bits5(const BN_ULONG * ap, int off);
+    int bn_from_montgomery(BN_ULONG * rp, const BN_ULONG * ap,
+                           const BN_ULONG * not_used, const BN_ULONG * np,
+                           const BN_ULONG * n0, int num);
+
+    BN_ULONG *np = mont->N.d, *n0 = mont->n0, *np2;
+
+    /* BN_to_montgomery can contaminate words above .top
+     * [in BN_DEBUG[_DEBUG] build]... */
+    for (i = am.top; i < top; i++)
+      am.d[i] = 0;
+    for (i = tmp.top; i < top; i++)
+      tmp.d[i] = 0;
+
+    if (top & 7)
+      np2 = np;
+    else
+      for (np2 = am.d + top, i = 0; i < top; i++)
+        np2[2 * i] = np[i];
+
+    bn_scatter5(tmp.d, top, powerbuf, 0);
+    bn_scatter5(am.d, am.top, powerbuf, 1);
+    bn_mul_mont(tmp.d, am.d, am.d, np, n0, top);
+    bn_scatter5(tmp.d, top, powerbuf, 2);
+
+    /* same as above, but uses squaring for 1/2 of operations */
+    for (i = 4; i < 32; i *= 2) {
+      bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+      bn_scatter5(tmp.d, top, powerbuf, i);
+    }
+    for (i = 3; i < 8; i += 2) {
+      int j;
+      bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np2, n0, top, i - 1);
+      bn_scatter5(tmp.d, top, powerbuf, i);
+      for (j = 2 * i; j < 32; j *= 2) {
+        bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+        bn_scatter5(tmp.d, top, powerbuf, j);
+      }
+    }
+    for (; i < 16; i += 2) {
+      bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np2, n0, top, i - 1);
+      bn_scatter5(tmp.d, top, powerbuf, i);
+      bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+      bn_scatter5(tmp.d, top, powerbuf, 2 * i);
+    }
+    for (; i < 32; i += 2) {
+      bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np2, n0, top, i - 1);
+      bn_scatter5(tmp.d, top, powerbuf, i);
+    }
+
+    bits--;
+    for (wvalue = 0, i = bits % 5; i >= 0; i--, bits--)
+      wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+    bn_gather5(tmp.d, top, powerbuf, wvalue);
+
+    /* Scan the exponent one window at a time starting from the most
+     * significant bits.
+     */
+    if (top & 7)
+      while (bits >= 0) {
+        for (wvalue = 0, i = 0; i < 5; i++, bits--)
+          wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+
+        bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+        bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+        bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+        bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+        bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+        bn_mul_mont_gather5(tmp.d, tmp.d, powerbuf, np, n0, top, wvalue);
+      }
+    else {
+      while (bits >= 0) {
+        wvalue = bn_get_bits5(p->d, bits - 4);
+        bits -= 5;
+        bn_power5(tmp.d, tmp.d, powerbuf, np2, n0, top, wvalue);
+      }
+    }
+
+    ret = bn_from_montgomery(tmp.d, tmp.d, NULL, np2, n0, top);
+    tmp.top = top;
+    bn_correct_top(&tmp);
+    if (ret) {
+      if (!BN_copy(rr, &tmp))
+        ret = 0;
+      goto err; /* non-zero ret means it's not error */
+    }
+  } else
+#endif
+  {
+    if (!copy_to_prebuf(&tmp, top, powerbuf, 0, numPowers))
+      goto err;
+    if (!copy_to_prebuf(&am, top, powerbuf, 1, numPowers))
+      goto err;
+
+    /* If the window size is greater than 1, then calculate
+     * val[i=2..2^winsize-1]. Powers are computed as a*a^(i-1)
+     * (even powers could instead be computed as (a^(i/2))^2
+     * to use the slight performance advantage of sqr over mul).
+     */
+    if (window > 1) {
+      if (!BN_mod_mul_montgomery(&tmp, &am, &am, mont, ctx))
+        goto err;
+      if (!copy_to_prebuf(&tmp, top, powerbuf, 2, numPowers))
+        goto err;
+      for (i = 3; i < numPowers; i++) {
+        /* Calculate a^i = a^(i-1) * a */
+        if (!BN_mod_mul_montgomery(&tmp, &am, &tmp, mont, ctx))
+          goto err;
+        if (!copy_to_prebuf(&tmp, top, powerbuf, i, numPowers))
+          goto err;
+      }
+    }
+
+    bits--;
+    for (wvalue = 0, i = bits % window; i >= 0; i--, bits--)
+      wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+    if (!copy_from_prebuf(&tmp, top, powerbuf, wvalue, numPowers))
+      goto err;
+
+    /* Scan the exponent one window at a time starting from the most
+     * significant bits.
+     */
+    while (bits >= 0) {
+      wvalue = 0; /* The 'value' of the window */
+
+      /* Scan the window, squaring the result as we go */
+      for (i = 0; i < window; i++, bits--) {
+        if (!BN_mod_mul_montgomery(&tmp, &tmp, &tmp, mont, ctx))
+          goto err;
+        wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+      }
+
+      /* Fetch the appropriate pre-computed value from the pre-buf */
+      if (!copy_from_prebuf(&am, top, powerbuf, wvalue, numPowers))
+        goto err;
+
+      /* Multiply the result into the intermediate result */
+      if (!BN_mod_mul_montgomery(&tmp, &tmp, &am, mont, ctx))
+        goto err;
+    }
+  }
+
+  /* Convert the final result from montgomery to standard format */
+  if (!BN_from_montgomery(rr, &tmp, mont, ctx))
+    goto err;
+  ret = 1;
+err:
+  if ((in_mont == NULL) && (mont != NULL))
+    BN_MONT_CTX_free(mont);
+  if (powerbuf != NULL) {
+    OPENSSL_cleanse(powerbuf, powerbufLen);
+    if (powerbufFree)
+      OPENSSL_free(powerbufFree);
+  }
+  BN_CTX_end(ctx);
+  return (ret);
+}
+
+int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p,
+                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont) {
+  BN_MONT_CTX *mont = NULL;
+  int b, bits, ret = 0;
+  int r_is_one;
+  BN_ULONG w, next_w;
+  BIGNUM *d, *r, *t;
+  BIGNUM *swap_tmp;
+#define BN_MOD_MUL_WORD(r, w, m)   \
+  (BN_mul_word(r, (w)) &&          \
+   (/* BN_ucmp(r, (m)) < 0 ? 1 :*/ \
+    (BN_mod(t, r, m, ctx) && (swap_tmp = r, r = t, t = swap_tmp, 1))))
+  /* BN_MOD_MUL_WORD is only used with 'w' large, so the BN_ucmp test is
+   * probably more overhead than always using BN_mod (which uses BN_copy if a
+   * similar test returns true). We can use BN_mod and do not need BN_nnmod
+   * because our accumulator is never negative (the result of BN_mod does not
+   * depend on the sign of the modulus). */
+#define BN_TO_MONTGOMERY_WORD(r, w, mont) \
+  (BN_set_word(r, (w)) && BN_to_montgomery(r, r, (mont), ctx))
+
+  if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+    /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+    OPENSSL_PUT_ERROR(BN, BN_mod_exp_mont_word,
+        ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
+  if (!BN_is_odd(m)) {
+    OPENSSL_PUT_ERROR(BN, BN_mod_exp_mont_word, BN_R_CALLED_WITH_EVEN_MODULUS);
+    return 0;
+  }
+
+  if (m->top == 1) {
+    a %= m->d[0]; /* make sure that 'a' is reduced */
+  }
+
+  bits = BN_num_bits(p);
+  if (bits == 0) {
+    ret = BN_one(rr);
+    return ret;
+  }
+  if (a == 0) {
+    BN_zero(rr);
+    ret = 1;
+    return ret;
+  }
+
+  BN_CTX_start(ctx);
+  d = BN_CTX_get(ctx);
+  r = BN_CTX_get(ctx);
+  t = BN_CTX_get(ctx);
+  if (d == NULL || r == NULL || t == NULL) {
+    goto err;
+  }
+
+  if (in_mont != NULL)
+    mont = in_mont;
+  else {
+    if ((mont = BN_MONT_CTX_new()) == NULL) {
+      goto err;
+    }
+    if (!BN_MONT_CTX_set(mont, m, ctx)) {
+      goto err;
+    }
+  }
+
+  r_is_one = 1; /* except for Montgomery factor */
+
+  /* bits-1 >= 0 */
+
+  /* The result is accumulated in the product r*w. */
+  w = a; /* bit 'bits-1' of 'p' is always set */
+  for (b = bits - 2; b >= 0; b--) {
+    /* First, square r*w. */
+    next_w = w * w;
+    if ((next_w / w) != w) {
+      /* overflow */
+      if (r_is_one) {
+        if (!BN_TO_MONTGOMERY_WORD(r, w, mont)) {
+          goto err;
+        }
+        r_is_one = 0;
+      } else {
+        if (!BN_MOD_MUL_WORD(r, w, m)) {
+          goto err;
+        }
+      }
+      next_w = 1;
+    }
+
+    w = next_w;
+    if (!r_is_one) {
+      if (!BN_mod_mul_montgomery(r, r, r, mont, ctx)) {
+        goto err;
+      }
+    }
+
+    /* Second, multiply r*w by 'a' if exponent bit is set. */
+    if (BN_is_bit_set(p, b)) {
+      next_w = w * a;
+      if ((next_w / a) != w) {
+        /* overflow */
+        if (r_is_one) {
+          if (!BN_TO_MONTGOMERY_WORD(r, w, mont)) {
+            goto err;
+          }
+          r_is_one = 0;
+        } else {
+          if (!BN_MOD_MUL_WORD(r, w, m)) {
+            goto err;
+          }
+        }
+        next_w = a;
+      }
+      w = next_w;
+    }
+  }
+
+  /* Finally, set r:=r*w. */
+  if (w != 1) {
+    if (r_is_one) {
+      if (!BN_TO_MONTGOMERY_WORD(r, w, mont)) {
+        goto err;
+      }
+      r_is_one = 0;
+    } else {
+      if (!BN_MOD_MUL_WORD(r, w, m)) {
+        goto err;
+      }
+    }
+  }
+
+  if (r_is_one) {
+    /* can happen only if a == 1*/
+    if (!BN_one(rr)) {
+      goto err;
+    }
+  } else {
+    if (!BN_from_montgomery(rr, r, mont, ctx)) {
+      goto err;
+    }
+  }
+  ret = 1;
+
+err:
+  if (in_mont == NULL && mont != NULL) {
+    BN_MONT_CTX_free(mont);
+  }
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+#define TABLE_SIZE 32
+
+int BN_mod_exp2_mont(BIGNUM *rr, const BIGNUM *a1, const BIGNUM *p1,
+                     const BIGNUM *a2, const BIGNUM *p2, const BIGNUM *m,
+                     BN_CTX *ctx, BN_MONT_CTX *in_mont) {
+  int i, j, bits, b, bits1, bits2, ret = 0, wpos1, wpos2, window1, window2,
+                                   wvalue1, wvalue2;
+  int r_is_one = 1;
+  BIGNUM *d, *r;
+  const BIGNUM *a_mod_m;
+  /* Tables of variables obtained from 'ctx' */
+  BIGNUM *val1[TABLE_SIZE], *val2[TABLE_SIZE];
+  BN_MONT_CTX *mont = NULL;
+
+  if (!(m->d[0] & 1)) {
+    OPENSSL_PUT_ERROR(BN, BN_mod_exp2_mont, BN_R_CALLED_WITH_EVEN_MODULUS);
+    return 0;
+  }
+  bits1 = BN_num_bits(p1);
+  bits2 = BN_num_bits(p2);
+  if (bits1 == 0 && bits2 == 0) {
+    ret = BN_one(rr);
+    return ret;
+  }
+
+  bits = (bits1 > bits2) ? bits1 : bits2;
+
+  BN_CTX_start(ctx);
+  d = BN_CTX_get(ctx);
+  r = BN_CTX_get(ctx);
+  val1[0] = BN_CTX_get(ctx);
+  val2[0] = BN_CTX_get(ctx);
+  if (!d || !r || !val1[0] || !val2[0]) {
+    goto err;
+  }
+
+  if (in_mont != NULL) {
+    mont = in_mont;
+  } else {
+    mont = BN_MONT_CTX_new();
+    if (mont == NULL) {
+      goto err;
+    }
+    if (!BN_MONT_CTX_set(mont, m, ctx)) {
+      goto err;
+    }
+  }
+
+  window1 = BN_window_bits_for_exponent_size(bits1);
+  window2 = BN_window_bits_for_exponent_size(bits2);
+
+  /* Build table for a1:   val1[i] := a1^(2*i + 1) mod m  for i = 0 ..
+   * 2^(window1-1) */
+  if (a1->neg || BN_ucmp(a1, m) >= 0) {
+    if (!BN_mod(val1[0], a1, m, ctx)) {
+      goto err;
+    }
+    a_mod_m = val1[0];
+  } else {
+    a_mod_m = a1;
+  }
+
+  if (BN_is_zero(a_mod_m)) {
+    BN_zero(rr);
+    ret = 1;
+    goto err;
+  }
+
+  if (!BN_to_montgomery(val1[0], a_mod_m, mont, ctx)) {
+    goto err;
+  }
+
+  if (window1 > 1) {
+    if (!BN_mod_mul_montgomery(d, val1[0], val1[0], mont, ctx)) {
+      goto err;
+    }
+
+    j = 1 << (window1 - 1);
+    for (i = 1; i < j; i++) {
+      if (((val1[i] = BN_CTX_get(ctx)) == NULL) ||
+          !BN_mod_mul_montgomery(val1[i], val1[i - 1], d, mont, ctx)) {
+        goto err;
+      }
+    }
+  }
+
+  /* Build table for a2:   val2[i] := a2^(2*i + 1) mod m  for i = 0 ..
+   * 2^(window2-1) */
+  if (a2->neg || BN_ucmp(a2, m) >= 0) {
+    if (!BN_mod(val2[0], a2, m, ctx)) {
+      goto err;
+    }
+    a_mod_m = val2[0];
+  } else {
+    a_mod_m = a2;
+  }
+
+  if (BN_is_zero(a_mod_m)) {
+    BN_zero(rr);
+    ret = 1;
+    goto err;
+  }
+
+  if (!BN_to_montgomery(val2[0], a_mod_m, mont, ctx)) {
+    goto err;
+  }
+
+  if (window2 > 1) {
+    if (!BN_mod_mul_montgomery(d, val2[0], val2[0], mont, ctx)) {
+      goto err;
+    }
+
+    j = 1 << (window2 - 1);
+    for (i = 1; i < j; i++) {
+      if (((val2[i] = BN_CTX_get(ctx)) == NULL) ||
+          !BN_mod_mul_montgomery(val2[i], val2[i - 1], d, mont, ctx)) {
+        goto err;
+      }
+    }
+  }
+
+  /* Now compute the power product, using independent windows. */
+  r_is_one = 1;
+  wvalue1 = 0; /* The 'value' of the first window */
+  wvalue2 = 0; /* The 'value' of the second window */
+  wpos1 = 0;   /* If wvalue1 > 0, the bottom bit of the first window */
+  wpos2 = 0;   /* If wvalue2 > 0, the bottom bit of the second window */
+
+  if (!BN_to_montgomery(r, BN_value_one(), mont, ctx)) {
+    goto err;
+  }
+
+  for (b = bits - 1; b >= 0; b--) {
+    if (!r_is_one) {
+      if (!BN_mod_mul_montgomery(r, r, r, mont, ctx)) {
+        goto err;
+      }
+    }
+
+    if (!wvalue1 && BN_is_bit_set(p1, b)) {
+      /* consider bits b-window1+1 .. b for this window */
+      i = b - window1 + 1;
+      while (!BN_is_bit_set(p1, i)) /* works for i<0 */
+        i++;
+      wpos1 = i;
+      wvalue1 = 1;
+      for (i = b - 1; i >= wpos1; i--) {
+        wvalue1 <<= 1;
+        if (BN_is_bit_set(p1, i))
+          wvalue1++;
+      }
+    }
+
+    if (!wvalue2 && BN_is_bit_set(p2, b)) {
+      /* consider bits b-window2+1 .. b for this window */
+      i = b - window2 + 1;
+      while (!BN_is_bit_set(p2, i))
+        i++;
+      wpos2 = i;
+      wvalue2 = 1;
+      for (i = b - 1; i >= wpos2; i--) {
+        wvalue2 <<= 1;
+        if (BN_is_bit_set(p2, i))
+          wvalue2++;
+      }
+    }
+
+    if (wvalue1 && b == wpos1) {
+      /* wvalue1 is odd and < 2^window1 */
+      if (!BN_mod_mul_montgomery(r, r, val1[wvalue1 >> 1], mont, ctx)) {
+        goto err;
+      }
+      wvalue1 = 0;
+      r_is_one = 0;
+    }
+
+    if (wvalue2 && b == wpos2) {
+      /* wvalue2 is odd and < 2^window2 */
+      if (!BN_mod_mul_montgomery(r, r, val2[wvalue2 >> 1], mont, ctx)) {
+        goto err;
+      }
+      wvalue2 = 0;
+      r_is_one = 0;
+    }
+  }
+
+  if (!BN_from_montgomery(rr, r, mont, ctx)) {
+    goto err;
+  }
+  ret = 1;
+
+err:
+  if (in_mont == NULL && mont != NULL) {
+    BN_MONT_CTX_free(mont);
+  }
+  BN_CTX_end(ctx);
+  return ret;
+}
diff --git a/crypto/bn/gcd.c b/crypto/bn/gcd.c
new file mode 100644
index 0000000..2dce296
--- /dev/null
+++ b/crypto/bn/gcd.c
@@ -0,0 +1,704 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include <openssl/err.h>
+
+#include "internal.h"
+
+static BIGNUM *euclid(BIGNUM *a, BIGNUM *b) {
+  BIGNUM *t;
+  int shifts = 0;
+
+  /* 0 <= b <= a */
+  while (!BN_is_zero(b)) {
+    /* 0 < b <= a */
+
+    if (BN_is_odd(a)) {
+      if (BN_is_odd(b)) {
+        if (!BN_sub(a, a, b)) {
+          goto err;
+        }
+        if (!BN_rshift1(a, a)) {
+          goto err;
+        }
+        if (BN_cmp(a, b) < 0) {
+          t = a;
+          a = b;
+          b = t;
+        }
+      } else {
+        /* a odd - b even */
+        if (!BN_rshift1(b, b)) {
+          goto err;
+        }
+        if (BN_cmp(a, b) < 0) {
+          t = a;
+          a = b;
+          b = t;
+        }
+      }
+    } else {
+      /* a is even */
+      if (BN_is_odd(b)) {
+        if (!BN_rshift1(a, a)) {
+          goto err;
+        }
+        if (BN_cmp(a, b) < 0) {
+          t = a;
+          a = b;
+          b = t;
+        }
+      } else {
+        /* a even - b even */
+        if (!BN_rshift1(a, a)) {
+          goto err;
+        }
+        if (!BN_rshift1(b, b)) {
+          goto err;
+        }
+        shifts++;
+      }
+    }
+    /* 0 <= b <= a */
+  }
+
+  if (shifts) {
+    if (!BN_lshift(a, a, shifts)) {
+      goto err;
+    }
+  }
+
+  return a;
+
+err:
+  return NULL;
+}
+
+int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) {
+  BIGNUM *a, *b, *t;
+  int ret = 0;
+
+  BN_CTX_start(ctx);
+  a = BN_CTX_get(ctx);
+  b = BN_CTX_get(ctx);
+
+  if (a == NULL || b == NULL) {
+    goto err;
+  }
+  if (BN_copy(a, in_a) == NULL) {
+    goto err;
+  }
+  if (BN_copy(b, in_b) == NULL) {
+    goto err;
+  }
+
+  a->neg = 0;
+  b->neg = 0;
+
+  if (BN_cmp(a, b) < 0) {
+    t = a;
+    a = b;
+    b = t;
+  }
+  t = euclid(a, b);
+  if (t == NULL) {
+    goto err;
+  }
+
+  if (BN_copy(r, t) == NULL) {
+    goto err;
+  }
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+/* solves ax == 1 (mod n) */
+static BIGNUM *BN_mod_inverse_no_branch(BIGNUM *out, const BIGNUM *a,
+                                        const BIGNUM *n, BN_CTX *ctx);
+
+BIGNUM *BN_mod_inverse(BIGNUM *out, const BIGNUM *a, const BIGNUM *n,
+                       BN_CTX *ctx) {
+  BIGNUM *A, *B, *X, *Y, *M, *D, *T, *R = NULL;
+  BIGNUM *ret = NULL;
+  int sign;
+
+  if ((a->flags & BN_FLG_CONSTTIME) != 0 ||
+      (n->flags & BN_FLG_CONSTTIME) != 0) {
+    return BN_mod_inverse_no_branch(out, a, n, ctx);
+  }
+
+  BN_CTX_start(ctx);
+  A = BN_CTX_get(ctx);
+  B = BN_CTX_get(ctx);
+  X = BN_CTX_get(ctx);
+  D = BN_CTX_get(ctx);
+  M = BN_CTX_get(ctx);
+  Y = BN_CTX_get(ctx);
+  T = BN_CTX_get(ctx);
+  if (T == NULL) {
+    goto err;
+  }
+
+  if (out == NULL) {
+    R = BN_new();
+  } else {
+    R = out;
+  }
+  if (R == NULL) {
+    goto err;
+  }
+
+  BN_one(X);
+  BN_zero(Y);
+  if (BN_copy(B, a) == NULL) {
+    goto err;
+  }
+  if (BN_copy(A, n) == NULL) {
+    goto err;
+  }
+  A->neg = 0;
+  if (B->neg || (BN_ucmp(B, A) >= 0)) {
+    if (!BN_nnmod(B, B, A, ctx)) {
+      goto err;
+    }
+  }
+  sign = -1;
+  /* From  B = a mod |n|,  A = |n|  it follows that
+   *
+   *      0 <= B < A,
+   *     -sign*X*a  ==  B   (mod |n|),
+   *      sign*Y*a  ==  A   (mod |n|).
+   */
+
+  if (BN_is_odd(n) && (BN_num_bits(n) <= (BN_BITS <= 32 ? 450 : 2048))) {
+    /* Binary inversion algorithm; requires odd modulus.
+     * This is faster than the general algorithm if the modulus
+     * is sufficiently small (about 400 .. 500 bits on 32-bit
+     * sytems, but much more on 64-bit systems) */
+    int shift;
+
+    while (!BN_is_zero(B)) {
+      /*      0 < B < |n|,
+       *      0 < A <= |n|,
+       * (1) -sign*X*a  ==  B   (mod |n|),
+       * (2)  sign*Y*a  ==  A   (mod |n|) */
+
+      /* Now divide  B  by the maximum possible power of two in the integers,
+       * and divide  X  by the same value mod |n|.
+       * When we're done, (1) still holds. */
+      shift = 0;
+      while (!BN_is_bit_set(B, shift)) {
+        /* note that 0 < B */
+        shift++;
+
+        if (BN_is_odd(X)) {
+          if (!BN_uadd(X, X, n)) {
+            goto err;
+          }
+        }
+        /* now X is even, so we can easily divide it by two */
+        if (!BN_rshift1(X, X)) {
+          goto err;
+        }
+      }
+      if (shift > 0) {
+        if (!BN_rshift(B, B, shift)) {
+          goto err;
+        }
+      }
+
+      /* Same for A and Y. Afterwards, (2) still holds. */
+      shift = 0;
+      while (!BN_is_bit_set(A, shift)) {
+        /* note that 0 < A */
+        shift++;
+
+        if (BN_is_odd(Y)) {
+          if (!BN_uadd(Y, Y, n)) {
+            goto err;
+          }
+        }
+        /* now Y is even */
+        if (!BN_rshift1(Y, Y)) {
+          goto err;
+        }
+      }
+      if (shift > 0) {
+        if (!BN_rshift(A, A, shift)) {
+          goto err;
+        }
+      }
+
+      /* We still have (1) and (2).
+       * Both  A  and  B  are odd.
+       * The following computations ensure that
+       *
+       *     0 <= B < |n|,
+       *      0 < A < |n|,
+       * (1) -sign*X*a  ==  B   (mod |n|),
+       * (2)  sign*Y*a  ==  A   (mod |n|),
+       *
+       * and that either  A  or  B  is even in the next iteration. */
+      if (BN_ucmp(B, A) >= 0) {
+        /* -sign*(X + Y)*a == B - A  (mod |n|) */
+        if (!BN_uadd(X, X, Y)) {
+          goto err;
+        }
+        /* NB: we could use BN_mod_add_quick(X, X, Y, n), but that
+         * actually makes the algorithm slower */
+        if (!BN_usub(B, B, A)) {
+          goto err;
+        }
+      } else {
+        /*  sign*(X + Y)*a == A - B  (mod |n|) */
+        if (!BN_uadd(Y, Y, X)) {
+          goto err;
+        }
+        /* as above, BN_mod_add_quick(Y, Y, X, n) would slow things down */
+        if (!BN_usub(A, A, B)) {
+          goto err;
+        }
+      }
+    }
+  } else {
+    /* general inversion algorithm */
+
+    while (!BN_is_zero(B)) {
+      BIGNUM *tmp;
+
+      /*
+       *      0 < B < A,
+       * (*) -sign*X*a  ==  B   (mod |n|),
+       *      sign*Y*a  ==  A   (mod |n|) */
+
+      /* (D, M) := (A/B, A%B) ... */
+      if (BN_num_bits(A) == BN_num_bits(B)) {
+        if (!BN_one(D)) {
+          goto err;
+        }
+        if (!BN_sub(M, A, B)) {
+          goto err;
+        }
+      } else if (BN_num_bits(A) == BN_num_bits(B) + 1) {
+        /* A/B is 1, 2, or 3 */
+        if (!BN_lshift1(T, B)) {
+          goto err;
+        }
+        if (BN_ucmp(A, T) < 0) {
+          /* A < 2*B, so D=1 */
+          if (!BN_one(D)) {
+            goto err;
+          }
+          if (!BN_sub(M, A, B)) {
+            goto err;
+          }
+        } else {
+          /* A >= 2*B, so D=2 or D=3 */
+          if (!BN_sub(M, A, T)) {
+            goto err;
+          }
+          if (!BN_add(D, T, B)) {
+            goto err; /* use D (:= 3*B) as temp */
+          }
+          if (BN_ucmp(A, D) < 0) {
+            /* A < 3*B, so D=2 */
+            if (!BN_set_word(D, 2)) {
+              goto err;
+            }
+            /* M (= A - 2*B) already has the correct value */
+          } else {
+            /* only D=3 remains */
+            if (!BN_set_word(D, 3)) {
+              goto err;
+            }
+            /* currently  M = A - 2*B,  but we need  M = A - 3*B */
+            if (!BN_sub(M, M, B)) {
+              goto err;
+            }
+          }
+        }
+      } else {
+        if (!BN_div(D, M, A, B, ctx)) {
+          goto err;
+        }
+      }
+
+      /* Now
+       *      A = D*B + M;
+       * thus we have
+       * (**)  sign*Y*a  ==  D*B + M   (mod |n|). */
+
+      tmp = A; /* keep the BIGNUM object, the value does not matter */
+
+      /* (A, B) := (B, A mod B) ... */
+      A = B;
+      B = M;
+      /* ... so we have  0 <= B < A  again */
+
+      /* Since the former  M  is now  B  and the former  B  is now  A,
+       * (**) translates into
+       *       sign*Y*a  ==  D*A + B    (mod |n|),
+       * i.e.
+       *       sign*Y*a - D*A  ==  B    (mod |n|).
+       * Similarly, (*) translates into
+       *      -sign*X*a  ==  A          (mod |n|).
+       *
+       * Thus,
+       *   sign*Y*a + D*sign*X*a  ==  B  (mod |n|),
+       * i.e.
+       *        sign*(Y + D*X)*a  ==  B  (mod |n|).
+       *
+       * So if we set  (X, Y, sign) := (Y + D*X, X, -sign),  we arrive back at
+       *      -sign*X*a  ==  B   (mod |n|),
+       *       sign*Y*a  ==  A   (mod |n|).
+       * Note that  X  and  Y  stay non-negative all the time. */
+
+      /* most of the time D is very small, so we can optimize tmp := D*X+Y */
+      if (BN_is_one(D)) {
+        if (!BN_add(tmp, X, Y)) {
+          goto err;
+        }
+      } else {
+        if (BN_is_word(D, 2)) {
+          if (!BN_lshift1(tmp, X)) {
+            goto err;
+          }
+        } else if (BN_is_word(D, 4)) {
+          if (!BN_lshift(tmp, X, 2)) {
+            goto err;
+          }
+        } else if (D->top == 1) {
+          if (!BN_copy(tmp, X)) {
+            goto err;
+          }
+          if (!BN_mul_word(tmp, D->d[0])) {
+            goto err;
+          }
+        } else {
+          if (!BN_mul(tmp, D, X, ctx)) {
+            goto err;
+          }
+        }
+        if (!BN_add(tmp, tmp, Y)) {
+          goto err;
+        }
+      }
+
+      M = Y; /* keep the BIGNUM object, the value does not matter */
+      Y = X;
+      X = tmp;
+      sign = -sign;
+    }
+  }
+
+  /* The while loop (Euclid's algorithm) ends when
+   *      A == gcd(a,n);
+   * we have
+   *       sign*Y*a  ==  A  (mod |n|),
+   * where  Y  is non-negative. */
+
+  if (sign < 0) {
+    if (!BN_sub(Y, n, Y)) {
+      goto err;
+    }
+  }
+  /* Now  Y*a  ==  A  (mod |n|).  */
+
+  if (BN_is_one(A)) {
+    /* Y*a == 1  (mod |n|) */
+    if (!Y->neg && BN_ucmp(Y, n) < 0) {
+      if (!BN_copy(R, Y)) {
+        goto err;
+      }
+    } else {
+      if (!BN_nnmod(R, Y, n, ctx)) {
+        goto err;
+      }
+    }
+  } else {
+    OPENSSL_PUT_ERROR(BN, BN_mod_inverse, BN_R_NO_INVERSE);
+    goto err;
+  }
+  ret = R;
+
+err:
+  if (ret == NULL && out == NULL) {
+    BN_free(R);
+  }
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+/* BN_mod_inverse_no_branch is a special version of BN_mod_inverse.
+ * It does not contain branches that may leak sensitive information. */
+static BIGNUM *BN_mod_inverse_no_branch(BIGNUM *out, const BIGNUM *a,
+                                        const BIGNUM *n, BN_CTX *ctx) {
+  BIGNUM *A, *B, *X, *Y, *M, *D, *T, *R = NULL;
+  BIGNUM local_A, local_B;
+  BIGNUM *pA, *pB;
+  BIGNUM *ret = NULL;
+  int sign;
+
+  BN_CTX_start(ctx);
+  A = BN_CTX_get(ctx);
+  B = BN_CTX_get(ctx);
+  X = BN_CTX_get(ctx);
+  D = BN_CTX_get(ctx);
+  M = BN_CTX_get(ctx);
+  Y = BN_CTX_get(ctx);
+  T = BN_CTX_get(ctx);
+  if (T == NULL) {
+    goto err;
+  }
+
+  if (out == NULL) {
+    R = BN_new();
+  } else {
+    R = out;
+  }
+  if (R == NULL) {
+    goto err;
+  }
+
+  BN_one(X);
+  BN_zero(Y);
+  if (BN_copy(B, a) == NULL) {
+    goto err;
+  }
+  if (BN_copy(A, n) == NULL) {
+    goto err;
+  }
+  A->neg = 0;
+
+  if (B->neg || (BN_ucmp(B, A) >= 0)) {
+    /* Turn BN_FLG_CONSTTIME flag on, so that when BN_div is invoked,
+     * BN_div_no_branch will be called eventually.
+     */
+    pB = &local_B;
+    BN_with_flags(pB, B, BN_FLG_CONSTTIME);
+    if (!BN_nnmod(B, pB, A, ctx))
+      goto err;
+  }
+  sign = -1;
+  /* From  B = a mod |n|,  A = |n|  it follows that
+   *
+   *      0 <= B < A,
+   *     -sign*X*a  ==  B   (mod |n|),
+   *      sign*Y*a  ==  A   (mod |n|).
+   */
+
+  while (!BN_is_zero(B)) {
+    BIGNUM *tmp;
+
+    /*
+     *      0 < B < A,
+     * (*) -sign*X*a  ==  B   (mod |n|),
+     *      sign*Y*a  ==  A   (mod |n|)
+     */
+
+    /* Turn BN_FLG_CONSTTIME flag on, so that when BN_div is invoked,
+     * BN_div_no_branch will be called eventually.
+     */
+    pA = &local_A;
+    BN_with_flags(pA, A, BN_FLG_CONSTTIME);
+
+    /* (D, M) := (A/B, A%B) ... */
+    if (!BN_div(D, M, pA, B, ctx)) {
+      goto err;
+    }
+
+    /* Now
+     *      A = D*B + M;
+     * thus we have
+     * (**)  sign*Y*a  ==  D*B + M   (mod |n|).
+     */
+
+    tmp = A; /* keep the BIGNUM object, the value does not matter */
+
+    /* (A, B) := (B, A mod B) ... */
+    A = B;
+    B = M;
+    /* ... so we have  0 <= B < A  again */
+
+    /* Since the former  M  is now  B  and the former  B  is now  A,
+     * (**) translates into
+     *       sign*Y*a  ==  D*A + B    (mod |n|),
+     * i.e.
+     *       sign*Y*a - D*A  ==  B    (mod |n|).
+     * Similarly, (*) translates into
+     *      -sign*X*a  ==  A          (mod |n|).
+     *
+     * Thus,
+     *   sign*Y*a + D*sign*X*a  ==  B  (mod |n|),
+     * i.e.
+     *        sign*(Y + D*X)*a  ==  B  (mod |n|).
+     *
+     * So if we set  (X, Y, sign) := (Y + D*X, X, -sign),  we arrive back at
+     *      -sign*X*a  ==  B   (mod |n|),
+     *       sign*Y*a  ==  A   (mod |n|).
+     * Note that  X  and  Y  stay non-negative all the time.
+     */
+
+    if (!BN_mul(tmp, D, X, ctx)) {
+      goto err;
+    }
+    if (!BN_add(tmp, tmp, Y)) {
+      goto err;
+    }
+
+    M = Y; /* keep the BIGNUM object, the value does not matter */
+    Y = X;
+    X = tmp;
+    sign = -sign;
+  }
+
+  /*
+   * The while loop (Euclid's algorithm) ends when
+   *      A == gcd(a,n);
+   * we have
+   *       sign*Y*a  ==  A  (mod |n|),
+   * where  Y  is non-negative.
+   */
+
+  if (sign < 0) {
+    if (!BN_sub(Y, n, Y)) {
+      goto err;
+    }
+  }
+  /* Now  Y*a  ==  A  (mod |n|).  */
+
+  if (BN_is_one(A)) {
+    /* Y*a == 1  (mod |n|) */
+    if (!Y->neg && BN_ucmp(Y, n) < 0) {
+      if (!BN_copy(R, Y)) {
+        goto err;
+      }
+    } else {
+      if (!BN_nnmod(R, Y, n, ctx)) {
+        goto err;
+      }
+    }
+  } else {
+    OPENSSL_PUT_ERROR(BN, BN_mod_inverse_no_branch, BN_R_NO_INVERSE);
+    goto err;
+  }
+  ret = R;
+
+err:
+  if (ret == NULL && out == NULL) {
+    BN_free(R);
+  }
+
+  BN_CTX_end(ctx);
+  return ret;
+}
diff --git a/crypto/bn/generic.c b/crypto/bn/generic.c
new file mode 100644
index 0000000..b745750
--- /dev/null
+++ b/crypto/bn/generic.c
@@ -0,0 +1,1189 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+#if defined(OPENSSL_WINDOWS) || defined(OPENSSL_NO_ASM) || \
+    (!defined(OPENSSL_X86_64) && !defined(OPENSSL_X86))
+
+#if defined(OPENSSL_WINDOWS)
+#define alloca _alloca
+#else
+#include <alloca.h>
+#endif
+
+#ifdef BN_LLONG
+#define mul_add(r, a, w, c)             \
+  {                                     \
+    BN_ULLONG t;                        \
+    t = (BN_ULLONG)w * (a) + (r) + (c); \
+    (r) = Lw(t);                        \
+    (c) = Hw(t);                        \
+  }
+
+#define mul(r, a, w, c)           \
+  {                               \
+    BN_ULLONG t;                  \
+    t = (BN_ULLONG)w * (a) + (c); \
+    (r) = Lw(t);                  \
+    (c) = Hw(t);                  \
+  }
+
+#define sqr(r0, r1, a)        \
+  {                           \
+    BN_ULLONG t;              \
+    t = (BN_ULLONG)(a) * (a); \
+    (r0) = Lw(t);             \
+    (r1) = Hw(t);             \
+  }
+
+#elif defined(BN_UMULT_LOHI)
+#define mul_add(r, a, w, c)             \
+  {                                     \
+    BN_ULONG high, low, ret, tmp = (a); \
+    ret = (r);                          \
+    BN_UMULT_LOHI(low, high, w, tmp);   \
+    ret += (c);                         \
+    (c) = (ret < (c)) ? 1 : 0;          \
+    (c) += high;                        \
+    ret += low;                         \
+    (c) += (ret < low) ? 1 : 0;         \
+    (r) = ret;                          \
+  }
+
+#define mul(r, a, w, c)                \
+  {                                    \
+    BN_ULONG high, low, ret, ta = (a); \
+    BN_UMULT_LOHI(low, high, w, ta);   \
+    ret = low + (c);                   \
+    (c) = high;                        \
+    (c) += (ret < low) ? 1 : 0;        \
+    (r) = ret;                         \
+  }
+
+#define sqr(r0, r1, a)               \
+  {                                  \
+    BN_ULONG tmp = (a);              \
+    BN_UMULT_LOHI(r0, r1, tmp, tmp); \
+  }
+
+#elif defined(BN_UMULT_HIGH)
+#define mul_add(r, a, w, c)             \
+  {                                     \
+    BN_ULONG high, low, ret, tmp = (a); \
+    ret = (r);                          \
+    high = BN_UMULT_HIGH(w, tmp);       \
+    ret += (c);                         \
+    low = (w) * tmp;                    \
+    (c) = (ret < (c)) ? 1 : 0;          \
+    (c) += high;                        \
+    ret += low;                         \
+    (c) += (ret < low) ? 1 : 0;         \
+    (r) = ret;                          \
+  }
+
+#define mul(r, a, w, c)                \
+  {                                    \
+    BN_ULONG high, low, ret, ta = (a); \
+    low = (w) * ta;                    \
+    high = BN_UMULT_HIGH(w, ta);       \
+    ret = low + (c);                   \
+    (c) = high;                        \
+    (c) += (ret < low) ? 1 : 0;        \
+    (r) = ret;                         \
+  }
+
+#define sqr(r0, r1, a)              \
+  {                                 \
+    BN_ULONG tmp = (a);             \
+    (r0) = tmp * tmp;               \
+    (r1) = BN_UMULT_HIGH(tmp, tmp); \
+  }
+
+#else
+/*************************************************************
+ * No long long type
+ */
+
+#define LBITS(a) ((a) & BN_MASK2l)
+#define HBITS(a) (((a) >> BN_BITS4) & BN_MASK2l)
+#define L2HBITS(a) (((a) << BN_BITS4) & BN_MASK2)
+
+#define LLBITS(a) ((a) & BN_MASKl)
+#define LHBITS(a) (((a) >> BN_BITS2) & BN_MASKl)
+#define LL2HBITS(a) ((BN_ULLONG)((a) & BN_MASKl) << BN_BITS2)
+
+#define mul64(l, h, bl, bh)       \
+  {                               \
+    BN_ULONG m, m1, lt, ht;       \
+                                  \
+    lt = l;                       \
+    ht = h;                       \
+    m = (bh) * (lt);              \
+    lt = (bl) * (lt);             \
+    m1 = (bl) * (ht);             \
+    ht = (bh) * (ht);             \
+    m = (m + m1) & BN_MASK2;      \
+    if (m < m1)                   \
+      ht += L2HBITS((BN_ULONG)1); \
+    ht += HBITS(m);               \
+    m1 = L2HBITS(m);              \
+    lt = (lt + m1) & BN_MASK2;    \
+    if (lt < m1)                  \
+      ht++;                       \
+    (l) = lt;                     \
+    (h) = ht;                     \
+  }
+
+#define sqr64(lo, ho, in)                    \
+  {                                          \
+    BN_ULONG l, h, m;                        \
+                                             \
+    h = (in);                                \
+    l = LBITS(h);                            \
+    h = HBITS(h);                            \
+    m = (l) * (h);                           \
+    l *= l;                                  \
+    h *= h;                                  \
+    h += (m & BN_MASK2h1) >> (BN_BITS4 - 1); \
+    m = (m & BN_MASK2l) << (BN_BITS4 + 1);   \
+    l = (l + m) & BN_MASK2;                  \
+    if (l < m)                               \
+      h++;                                   \
+    (lo) = l;                                \
+    (ho) = h;                                \
+  }
+
+#define mul_add(r, a, bl, bh, c) \
+  {                              \
+    BN_ULONG l, h;               \
+                                 \
+    h = (a);                     \
+    l = LBITS(h);                \
+    h = HBITS(h);                \
+    mul64(l, h, (bl), (bh));     \
+                                 \
+    /* non-multiply part */      \
+    l = (l + (c)) & BN_MASK2;    \
+    if (l < (c))                 \
+      h++;                       \
+    (c) = (r);                   \
+    l = (l + (c)) & BN_MASK2;    \
+    if (l < (c))                 \
+      h++;                       \
+    (c) = h & BN_MASK2;          \
+    (r) = l;                     \
+  }
+
+#define mul(r, a, bl, bh, c)  \
+  {                           \
+    BN_ULONG l, h;            \
+                              \
+    h = (a);                  \
+    l = LBITS(h);             \
+    h = HBITS(h);             \
+    mul64(l, h, (bl), (bh));  \
+                              \
+    /* non-multiply part */   \
+    l += (c);                 \
+    if ((l & BN_MASK2) < (c)) \
+      h++;                    \
+    (c) = h & BN_MASK2;       \
+    (r) = l & BN_MASK2;       \
+  }
+#endif /* !BN_LLONG */
+
+#if defined(BN_LLONG) || defined(BN_UMULT_HIGH)
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w) {
+  BN_ULONG c1 = 0;
+
+  assert(num >= 0);
+  if (num <= 0) {
+    return c1;
+  }
+
+  while (num & ~3) {
+    mul_add(rp[0], ap[0], w, c1);
+    mul_add(rp[1], ap[1], w, c1);
+    mul_add(rp[2], ap[2], w, c1);
+    mul_add(rp[3], ap[3], w, c1);
+    ap += 4;
+    rp += 4;
+    num -= 4;
+  }
+
+  while (num) {
+    mul_add(rp[0], ap[0], w, c1);
+    ap++;
+    rp++;
+    num--;
+  }
+
+  return c1;
+}
+
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w) {
+  BN_ULONG c1 = 0;
+
+  assert(num >= 0);
+  if (num <= 0) {
+    return c1;
+  }
+
+  while (num & ~3) {
+    mul(rp[0], ap[0], w, c1);
+    mul(rp[1], ap[1], w, c1);
+    mul(rp[2], ap[2], w, c1);
+    mul(rp[3], ap[3], w, c1);
+    ap += 4;
+    rp += 4;
+    num -= 4;
+  }
+  while (num) {
+    mul(rp[0], ap[0], w, c1);
+    ap++;
+    rp++;
+    num--;
+  }
+  return c1;
+}
+
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n) {
+  assert(n >= 0);
+  if (n <= 0) {
+    return;
+  }
+
+  while (n & ~3) {
+    sqr(r[0], r[1], a[0]);
+    sqr(r[2], r[3], a[1]);
+    sqr(r[4], r[5], a[2]);
+    sqr(r[6], r[7], a[3]);
+    a += 4;
+    r += 8;
+    n -= 4;
+  }
+  while (n) {
+    sqr(r[0], r[1], a[0]);
+    a++;
+    r += 2;
+    n--;
+  }
+}
+
+#else /* !(defined(BN_LLONG) || defined(BN_UMULT_HIGH)) */
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w) {
+  BN_ULONG c = 0;
+  BN_ULONG bl, bh;
+
+  assert(num >= 0);
+  if (num <= 0) {
+    return (BN_ULONG)0;
+  }
+
+  bl = LBITS(w);
+  bh = HBITS(w);
+
+  while (num & ~3) {
+    mul_add(rp[0], ap[0], bl, bh, c);
+    mul_add(rp[1], ap[1], bl, bh, c);
+    mul_add(rp[2], ap[2], bl, bh, c);
+    mul_add(rp[3], ap[3], bl, bh, c);
+    ap += 4;
+    rp += 4;
+    num -= 4;
+  }
+  while (num) {
+    mul_add(rp[0], ap[0], bl, bh, c);
+    ap++;
+    rp++;
+    num--;
+  }
+  return c;
+}
+
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w) {
+  BN_ULONG carry = 0;
+  BN_ULONG bl, bh;
+
+  assert(num >= 0);
+  if (num <= 0) {
+    return (BN_ULONG)0;
+  }
+
+  bl = LBITS(w);
+  bh = HBITS(w);
+
+  while (num & ~3) {
+    mul(rp[0], ap[0], bl, bh, carry);
+    mul(rp[1], ap[1], bl, bh, carry);
+    mul(rp[2], ap[2], bl, bh, carry);
+    mul(rp[3], ap[3], bl, bh, carry);
+    ap += 4;
+    rp += 4;
+    num -= 4;
+  }
+  while (num) {
+    mul(rp[0], ap[0], bl, bh, carry);
+    ap++;
+    rp++;
+    num--;
+  }
+  return carry;
+}
+
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n) {
+  assert(n >= 0);
+  if (n <= 0) {
+    return;
+  }
+
+  while (n & ~3) {
+    sqr64(r[0], r[1], a[0]);
+    sqr64(r[2], r[3], a[1]);
+    sqr64(r[4], r[5], a[2]);
+    sqr64(r[6], r[7], a[3]);
+    a += 4;
+    r += 8;
+    n -= 4;
+  }
+  while (n) {
+    sqr64(r[0], r[1], a[0]);
+    a++;
+    r += 2;
+    n--;
+  }
+}
+
+#endif /* !(defined(BN_LLONG) || defined(BN_UMULT_HIGH)) */
+
+#if defined(BN_LLONG) && defined(BN_DIV2W)
+
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) {
+  return (BN_ULONG)(((((BN_ULLONG)h) << BN_BITS2) | l) / (BN_ULLONG)d);
+}
+
+#else
+
+/* Divide h,l by d and return the result. */
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) {
+  BN_ULONG dh, dl, q, ret = 0, th, tl, t;
+  int i, count = 2;
+
+  if (d == 0) {
+    return BN_MASK2;
+  }
+
+  i = BN_num_bits_word(d);
+  assert((i == BN_BITS2) || (h <= (BN_ULONG)1 << i));
+
+  i = BN_BITS2 - i;
+  if (h >= d) {
+    h -= d;
+  }
+
+  if (i) {
+    d <<= i;
+    h = (h << i) | (l >> (BN_BITS2 - i));
+    l <<= i;
+  }
+  dh = (d & BN_MASK2h) >> BN_BITS4;
+  dl = (d & BN_MASK2l);
+  for (;;) {
+    if ((h >> BN_BITS4) == dh) {
+      q = BN_MASK2l;
+    } else {
+      q = h / dh;
+    }
+
+    th = q * dh;
+    tl = dl * q;
+    for (;;) {
+      t = h - th;
+      if ((t & BN_MASK2h) ||
+          ((tl) <= ((t << BN_BITS4) | ((l & BN_MASK2h) >> BN_BITS4)))) {
+        break;
+      }
+      q--;
+      th -= dh;
+      tl -= dl;
+    }
+    t = (tl >> BN_BITS4);
+    tl = (tl << BN_BITS4) & BN_MASK2h;
+    th += t;
+
+    if (l < tl) {
+      th++;
+    }
+    l -= tl;
+    if (h < th) {
+      h += d;
+      q--;
+    }
+    h -= th;
+
+    if (--count == 0) {
+      break;
+    }
+
+    ret = q << BN_BITS4;
+    h = ((h << BN_BITS4) | (l >> BN_BITS4)) & BN_MASK2;
+    l = (l & BN_MASK2l) << BN_BITS4;
+  }
+
+  ret |= q;
+  return ret;
+}
+
+#endif /* !defined(BN_LLONG) && defined(BN_DIV2W) */
+
+#ifdef BN_LLONG
+BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                      int n) {
+  BN_ULLONG ll = 0;
+
+  assert(n >= 0);
+  if (n <= 0) {
+    return (BN_ULONG)0;
+  }
+
+  while (n & ~3) {
+    ll += (BN_ULLONG)a[0] + b[0];
+    r[0] = (BN_ULONG)ll & BN_MASK2;
+    ll >>= BN_BITS2;
+    ll += (BN_ULLONG)a[1] + b[1];
+    r[1] = (BN_ULONG)ll & BN_MASK2;
+    ll >>= BN_BITS2;
+    ll += (BN_ULLONG)a[2] + b[2];
+    r[2] = (BN_ULONG)ll & BN_MASK2;
+    ll >>= BN_BITS2;
+    ll += (BN_ULLONG)a[3] + b[3];
+    r[3] = (BN_ULONG)ll & BN_MASK2;
+    ll >>= BN_BITS2;
+    a += 4;
+    b += 4;
+    r += 4;
+    n -= 4;
+  }
+  while (n) {
+    ll += (BN_ULLONG)a[0] + b[0];
+    r[0] = (BN_ULONG)ll & BN_MASK2;
+    ll >>= BN_BITS2;
+    a++;
+    b++;
+    r++;
+    n--;
+  }
+  return (BN_ULONG)ll;
+}
+
+#else /* !BN_LLONG */
+
+BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                      int n) {
+  BN_ULONG c, l, t;
+
+  assert(n >= 0);
+  if (n <= 0) {
+    return (BN_ULONG)0;
+  }
+
+  c = 0;
+  while (n & ~3) {
+    t = a[0];
+    t = (t + c) & BN_MASK2;
+    c = (t < c);
+    l = (t + b[0]) & BN_MASK2;
+    c += (l < t);
+    r[0] = l;
+    t = a[1];
+    t = (t + c) & BN_MASK2;
+    c = (t < c);
+    l = (t + b[1]) & BN_MASK2;
+    c += (l < t);
+    r[1] = l;
+    t = a[2];
+    t = (t + c) & BN_MASK2;
+    c = (t < c);
+    l = (t + b[2]) & BN_MASK2;
+    c += (l < t);
+    r[2] = l;
+    t = a[3];
+    t = (t + c) & BN_MASK2;
+    c = (t < c);
+    l = (t + b[3]) & BN_MASK2;
+    c += (l < t);
+    r[3] = l;
+    a += 4;
+    b += 4;
+    r += 4;
+    n -= 4;
+  }
+  while (n) {
+    t = a[0];
+    t = (t + c) & BN_MASK2;
+    c = (t < c);
+    l = (t + b[0]) & BN_MASK2;
+    c += (l < t);
+    r[0] = l;
+    a++;
+    b++;
+    r++;
+    n--;
+  }
+  return (BN_ULONG)c;
+}
+
+#endif /* !BN_LLONG */
+
+BN_ULONG bn_sub_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                      int n) {
+  BN_ULONG t1, t2;
+  int c = 0;
+
+  assert(n >= 0);
+  if (n <= 0) {
+    return (BN_ULONG)0;
+  }
+
+  while (n & ~3) {
+    t1 = a[0];
+    t2 = b[0];
+    r[0] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    t1 = a[1];
+    t2 = b[1];
+    r[1] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    t1 = a[2];
+    t2 = b[2];
+    r[2] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    t1 = a[3];
+    t2 = b[3];
+    r[3] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    a += 4;
+    b += 4;
+    r += 4;
+    n -= 4;
+  }
+  while (n) {
+    t1 = a[0];
+    t2 = b[0];
+    r[0] = (t1 - t2 - c) & BN_MASK2;
+    if (t1 != t2)
+      c = (t1 < t2);
+    a++;
+    b++;
+    r++;
+    n--;
+  }
+  return c;
+}
+
+/* mul_add_c(a,b,c0,c1,c2)  -- c+=a*b for three word number c=(c2,c1,c0) */
+/* mul_add_c2(a,b,c0,c1,c2) -- c+=2*a*b for three word number c=(c2,c1,c0) */
+/* sqr_add_c(a,i,c0,c1,c2)  -- c+=a[i]^2 for three word number c=(c2,c1,c0) */
+/* sqr_add_c2(a,i,c0,c1,c2) -- c+=2*a[i]*a[j] for three word number c=(c2,c1,c0) */
+
+#ifdef BN_LLONG
+#define mul_add_c(a, b, c0, c1, c2) \
+  t = (BN_ULLONG)a * b;             \
+  t1 = (BN_ULONG)Lw(t);             \
+  t2 = (BN_ULONG)Hw(t);             \
+  c0 = (c0 + t1) & BN_MASK2;        \
+  if ((c0) < t1)                    \
+    t2++;                           \
+  c1 = (c1 + t2) & BN_MASK2;        \
+  if ((c1) < t2)                    \
+    c2++;
+
+#define mul_add_c2(a, b, c0, c1, c2)           \
+  t = (BN_ULLONG)a * b;                        \
+  tt = (t + t) & BN_MASK;                      \
+  if (tt < t)                                  \
+    c2++;                                      \
+  t1 = (BN_ULONG)Lw(tt);                       \
+  t2 = (BN_ULONG)Hw(tt);                       \
+  c0 = (c0 + t1) & BN_MASK2;                   \
+  if ((c0 < t1) && (((++t2) & BN_MASK2) == 0)) \
+    c2++;                                      \
+  c1 = (c1 + t2) & BN_MASK2;                   \
+  if ((c1) < t2)                               \
+    c2++;
+
+#define sqr_add_c(a, i, c0, c1, c2) \
+  t = (BN_ULLONG)a[i] * a[i];       \
+  t1 = (BN_ULONG)Lw(t);             \
+  t2 = (BN_ULONG)Hw(t);             \
+  c0 = (c0 + t1) & BN_MASK2;        \
+  if ((c0) < t1)                    \
+    t2++;                           \
+  c1 = (c1 + t2) & BN_MASK2;        \
+  if ((c1) < t2)                    \
+    c2++;
+
+#define sqr_add_c2(a, i, j, c0, c1, c2) mul_add_c2((a)[i], (a)[j], c0, c1, c2)
+
+#elif defined(BN_UMULT_LOHI)
+
+#define mul_add_c(a, b, c0, c1, c2) \
+  {                                 \
+    BN_ULONG ta = (a), tb = (b);    \
+    BN_UMULT_LOHI(t1, t2, ta, tb);  \
+    c0 += t1;                       \
+    t2 += (c0 < t1) ? 1 : 0;        \
+    c1 += t2;                       \
+    c2 += (c1 < t2) ? 1 : 0;        \
+  }
+
+#define mul_add_c2(a, b, c0, c1, c2) \
+  {                                  \
+    BN_ULONG ta = (a), tb = (b), t0; \
+    BN_UMULT_LOHI(t0, t1, ta, tb);   \
+    t2 = t1 + t1;                    \
+    c2 += (t2 < t1) ? 1 : 0;         \
+    t1 = t0 + t0;                    \
+    t2 += (t1 < t0) ? 1 : 0;         \
+    c0 += t1;                        \
+    t2 += (c0 < t1) ? 1 : 0;         \
+    c1 += t2;                        \
+    c2 += (c1 < t2) ? 1 : 0;         \
+  }
+
+#define sqr_add_c(a, i, c0, c1, c2) \
+  {                                 \
+    BN_ULONG ta = (a)[i];           \
+    BN_UMULT_LOHI(t1, t2, ta, ta);  \
+    c0 += t1;                       \
+    t2 += (c0 < t1) ? 1 : 0;        \
+    c1 += t2;                       \
+    c2 += (c1 < t2) ? 1 : 0;        \
+  }
+
+#define sqr_add_c2(a, i, j, c0, c1, c2) mul_add_c2((a)[i], (a)[j], c0, c1, c2)
+
+#elif defined(BN_UMULT_HIGH)
+
+#define mul_add_c(a, b, c0, c1, c2) \
+  {                                 \
+    BN_ULONG ta = (a), tb = (b);    \
+    t1 = ta * tb;                   \
+    t2 = BN_UMULT_HIGH(ta, tb);     \
+    c0 += t1;                       \
+    t2 += (c0 < t1) ? 1 : 0;        \
+    c1 += t2;                       \
+    c2 += (c1 < t2) ? 1 : 0;        \
+  }
+
+#define mul_add_c2(a, b, c0, c1, c2) \
+  {                                  \
+    BN_ULONG ta = (a), tb = (b), t0; \
+    t1 = BN_UMULT_HIGH(ta, tb);      \
+    t0 = ta * tb;                    \
+    t2 = t1 + t1;                    \
+    c2 += (t2 < t1) ? 1 : 0;         \
+    t1 = t0 + t0;                    \
+    t2 += (t1 < t0) ? 1 : 0;         \
+    c0 += t1;                        \
+    t2 += (c0 < t1) ? 1 : 0;         \
+    c1 += t2;                        \
+    c2 += (c1 < t2) ? 1 : 0;         \
+  }
+
+#define sqr_add_c(a, i, c0, c1, c2) \
+  {                                 \
+    BN_ULONG ta = (a)[i];           \
+    t1 = ta * ta;                   \
+    t2 = BN_UMULT_HIGH(ta, ta);     \
+    c0 += t1;                       \
+    t2 += (c0 < t1) ? 1 : 0;        \
+    c1 += t2;                       \
+    c2 += (c1 < t2) ? 1 : 0;        \
+  }
+
+#define sqr_add_c2(a, i, j, c0, c1, c2) mul_add_c2((a)[i], (a)[j], c0, c1, c2)
+
+#else /* !BN_LLONG */
+#define mul_add_c(a, b, c0, c1, c2) \
+  t1 = LBITS(a);                    \
+  t2 = HBITS(a);                    \
+  bl = LBITS(b);                    \
+  bh = HBITS(b);                    \
+  mul64(t1, t2, bl, bh);            \
+  c0 = (c0 + t1) & BN_MASK2;        \
+  if ((c0) < t1)                    \
+    t2++;                           \
+  c1 = (c1 + t2) & BN_MASK2;        \
+  if ((c1) < t2)                    \
+    c2++;
+
+#define mul_add_c2(a, b, c0, c1, c2)           \
+  t1 = LBITS(a);                               \
+  t2 = HBITS(a);                               \
+  bl = LBITS(b);                               \
+  bh = HBITS(b);                               \
+  mul64(t1, t2, bl, bh);                       \
+  if (t2 & BN_TBIT)                            \
+    c2++;                                      \
+  t2 = (t2 + t2) & BN_MASK2;                   \
+  if (t1 & BN_TBIT)                            \
+    t2++;                                      \
+  t1 = (t1 + t1) & BN_MASK2;                   \
+  c0 = (c0 + t1) & BN_MASK2;                   \
+  if ((c0 < t1) && (((++t2) & BN_MASK2) == 0)) \
+    c2++;                                      \
+  c1 = (c1 + t2) & BN_MASK2;                   \
+  if ((c1) < t2)                               \
+    c2++;
+
+#define sqr_add_c(a, i, c0, c1, c2) \
+  sqr64(t1, t2, (a)[i]);            \
+  c0 = (c0 + t1) & BN_MASK2;        \
+  if ((c0) < t1)                    \
+    t2++;                           \
+  c1 = (c1 + t2) & BN_MASK2;        \
+  if ((c1) < t2)                    \
+    c2++;
+
+#define sqr_add_c2(a, i, j, c0, c1, c2) mul_add_c2((a)[i], (a)[j], c0, c1, c2)
+#endif /* !BN_LLONG */
+
+void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b) {
+#ifdef BN_LLONG
+  BN_ULLONG t;
+#else
+  BN_ULONG bl, bh;
+#endif
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  mul_add_c(a[0], b[0], c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  mul_add_c(a[0], b[1], c2, c3, c1);
+  mul_add_c(a[1], b[0], c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  mul_add_c(a[2], b[0], c3, c1, c2);
+  mul_add_c(a[1], b[1], c3, c1, c2);
+  mul_add_c(a[0], b[2], c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  mul_add_c(a[0], b[3], c1, c2, c3);
+  mul_add_c(a[1], b[2], c1, c2, c3);
+  mul_add_c(a[2], b[1], c1, c2, c3);
+  mul_add_c(a[3], b[0], c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  mul_add_c(a[4], b[0], c2, c3, c1);
+  mul_add_c(a[3], b[1], c2, c3, c1);
+  mul_add_c(a[2], b[2], c2, c3, c1);
+  mul_add_c(a[1], b[3], c2, c3, c1);
+  mul_add_c(a[0], b[4], c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  mul_add_c(a[0], b[5], c3, c1, c2);
+  mul_add_c(a[1], b[4], c3, c1, c2);
+  mul_add_c(a[2], b[3], c3, c1, c2);
+  mul_add_c(a[3], b[2], c3, c1, c2);
+  mul_add_c(a[4], b[1], c3, c1, c2);
+  mul_add_c(a[5], b[0], c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  mul_add_c(a[6], b[0], c1, c2, c3);
+  mul_add_c(a[5], b[1], c1, c2, c3);
+  mul_add_c(a[4], b[2], c1, c2, c3);
+  mul_add_c(a[3], b[3], c1, c2, c3);
+  mul_add_c(a[2], b[4], c1, c2, c3);
+  mul_add_c(a[1], b[5], c1, c2, c3);
+  mul_add_c(a[0], b[6], c1, c2, c3);
+  r[6] = c1;
+  c1 = 0;
+  mul_add_c(a[0], b[7], c2, c3, c1);
+  mul_add_c(a[1], b[6], c2, c3, c1);
+  mul_add_c(a[2], b[5], c2, c3, c1);
+  mul_add_c(a[3], b[4], c2, c3, c1);
+  mul_add_c(a[4], b[3], c2, c3, c1);
+  mul_add_c(a[5], b[2], c2, c3, c1);
+  mul_add_c(a[6], b[1], c2, c3, c1);
+  mul_add_c(a[7], b[0], c2, c3, c1);
+  r[7] = c2;
+  c2 = 0;
+  mul_add_c(a[7], b[1], c3, c1, c2);
+  mul_add_c(a[6], b[2], c3, c1, c2);
+  mul_add_c(a[5], b[3], c3, c1, c2);
+  mul_add_c(a[4], b[4], c3, c1, c2);
+  mul_add_c(a[3], b[5], c3, c1, c2);
+  mul_add_c(a[2], b[6], c3, c1, c2);
+  mul_add_c(a[1], b[7], c3, c1, c2);
+  r[8] = c3;
+  c3 = 0;
+  mul_add_c(a[2], b[7], c1, c2, c3);
+  mul_add_c(a[3], b[6], c1, c2, c3);
+  mul_add_c(a[4], b[5], c1, c2, c3);
+  mul_add_c(a[5], b[4], c1, c2, c3);
+  mul_add_c(a[6], b[3], c1, c2, c3);
+  mul_add_c(a[7], b[2], c1, c2, c3);
+  r[9] = c1;
+  c1 = 0;
+  mul_add_c(a[7], b[3], c2, c3, c1);
+  mul_add_c(a[6], b[4], c2, c3, c1);
+  mul_add_c(a[5], b[5], c2, c3, c1);
+  mul_add_c(a[4], b[6], c2, c3, c1);
+  mul_add_c(a[3], b[7], c2, c3, c1);
+  r[10] = c2;
+  c2 = 0;
+  mul_add_c(a[4], b[7], c3, c1, c2);
+  mul_add_c(a[5], b[6], c3, c1, c2);
+  mul_add_c(a[6], b[5], c3, c1, c2);
+  mul_add_c(a[7], b[4], c3, c1, c2);
+  r[11] = c3;
+  c3 = 0;
+  mul_add_c(a[7], b[5], c1, c2, c3);
+  mul_add_c(a[6], b[6], c1, c2, c3);
+  mul_add_c(a[5], b[7], c1, c2, c3);
+  r[12] = c1;
+  c1 = 0;
+  mul_add_c(a[6], b[7], c2, c3, c1);
+  mul_add_c(a[7], b[6], c2, c3, c1);
+  r[13] = c2;
+  c2 = 0;
+  mul_add_c(a[7], b[7], c3, c1, c2);
+  r[14] = c3;
+  r[15] = c1;
+}
+
+void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b) {
+#ifdef BN_LLONG
+  BN_ULLONG t;
+#else
+  BN_ULONG bl, bh;
+#endif
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  mul_add_c(a[0], b[0], c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  mul_add_c(a[0], b[1], c2, c3, c1);
+  mul_add_c(a[1], b[0], c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  mul_add_c(a[2], b[0], c3, c1, c2);
+  mul_add_c(a[1], b[1], c3, c1, c2);
+  mul_add_c(a[0], b[2], c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  mul_add_c(a[0], b[3], c1, c2, c3);
+  mul_add_c(a[1], b[2], c1, c2, c3);
+  mul_add_c(a[2], b[1], c1, c2, c3);
+  mul_add_c(a[3], b[0], c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  mul_add_c(a[3], b[1], c2, c3, c1);
+  mul_add_c(a[2], b[2], c2, c3, c1);
+  mul_add_c(a[1], b[3], c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  mul_add_c(a[2], b[3], c3, c1, c2);
+  mul_add_c(a[3], b[2], c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  mul_add_c(a[3], b[3], c1, c2, c3);
+  r[6] = c1;
+  r[7] = c2;
+}
+
+void bn_sqr_comba8(BN_ULONG *r, const BN_ULONG *a) {
+#ifdef BN_LLONG
+  BN_ULLONG t, tt;
+#else
+  BN_ULONG bl, bh;
+#endif
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  sqr_add_c(a, 0, c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 1, 0, c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  sqr_add_c(a, 1, c3, c1, c2);
+  sqr_add_c2(a, 2, 0, c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  sqr_add_c2(a, 3, 0, c1, c2, c3);
+  sqr_add_c2(a, 2, 1, c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  sqr_add_c(a, 2, c2, c3, c1);
+  sqr_add_c2(a, 3, 1, c2, c3, c1);
+  sqr_add_c2(a, 4, 0, c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  sqr_add_c2(a, 5, 0, c3, c1, c2);
+  sqr_add_c2(a, 4, 1, c3, c1, c2);
+  sqr_add_c2(a, 3, 2, c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  sqr_add_c(a, 3, c1, c2, c3);
+  sqr_add_c2(a, 4, 2, c1, c2, c3);
+  sqr_add_c2(a, 5, 1, c1, c2, c3);
+  sqr_add_c2(a, 6, 0, c1, c2, c3);
+  r[6] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 7, 0, c2, c3, c1);
+  sqr_add_c2(a, 6, 1, c2, c3, c1);
+  sqr_add_c2(a, 5, 2, c2, c3, c1);
+  sqr_add_c2(a, 4, 3, c2, c3, c1);
+  r[7] = c2;
+  c2 = 0;
+  sqr_add_c(a, 4, c3, c1, c2);
+  sqr_add_c2(a, 5, 3, c3, c1, c2);
+  sqr_add_c2(a, 6, 2, c3, c1, c2);
+  sqr_add_c2(a, 7, 1, c3, c1, c2);
+  r[8] = c3;
+  c3 = 0;
+  sqr_add_c2(a, 7, 2, c1, c2, c3);
+  sqr_add_c2(a, 6, 3, c1, c2, c3);
+  sqr_add_c2(a, 5, 4, c1, c2, c3);
+  r[9] = c1;
+  c1 = 0;
+  sqr_add_c(a, 5, c2, c3, c1);
+  sqr_add_c2(a, 6, 4, c2, c3, c1);
+  sqr_add_c2(a, 7, 3, c2, c3, c1);
+  r[10] = c2;
+  c2 = 0;
+  sqr_add_c2(a, 7, 4, c3, c1, c2);
+  sqr_add_c2(a, 6, 5, c3, c1, c2);
+  r[11] = c3;
+  c3 = 0;
+  sqr_add_c(a, 6, c1, c2, c3);
+  sqr_add_c2(a, 7, 5, c1, c2, c3);
+  r[12] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 7, 6, c2, c3, c1);
+  r[13] = c2;
+  c2 = 0;
+  sqr_add_c(a, 7, c3, c1, c2);
+  r[14] = c3;
+  r[15] = c1;
+}
+
+void bn_sqr_comba4(BN_ULONG *r, const BN_ULONG *a) {
+#ifdef BN_LLONG
+  BN_ULLONG t, tt;
+#else
+  BN_ULONG bl, bh;
+#endif
+  BN_ULONG t1, t2;
+  BN_ULONG c1, c2, c3;
+
+  c1 = 0;
+  c2 = 0;
+  c3 = 0;
+  sqr_add_c(a, 0, c1, c2, c3);
+  r[0] = c1;
+  c1 = 0;
+  sqr_add_c2(a, 1, 0, c2, c3, c1);
+  r[1] = c2;
+  c2 = 0;
+  sqr_add_c(a, 1, c3, c1, c2);
+  sqr_add_c2(a, 2, 0, c3, c1, c2);
+  r[2] = c3;
+  c3 = 0;
+  sqr_add_c2(a, 3, 0, c1, c2, c3);
+  sqr_add_c2(a, 2, 1, c1, c2, c3);
+  r[3] = c1;
+  c1 = 0;
+  sqr_add_c(a, 2, c2, c3, c1);
+  sqr_add_c2(a, 3, 1, c2, c3, c1);
+  r[4] = c2;
+  c2 = 0;
+  sqr_add_c2(a, 3, 2, c3, c1, c2);
+  r[5] = c3;
+  c3 = 0;
+  sqr_add_c(a, 3, c1, c2, c3);
+  r[6] = c1;
+  r[7] = c2;
+}
+
+#if defined(OPENSSL_NO_ASM) || (!defined(OPENSSL_ARM) && !defined(OPENSSL_X86_64))
+/* This is essentially reference implementation, which may or may not
+ * result in performance improvement. E.g. on IA-32 this routine was
+ * observed to give 40% faster rsa1024 private key operations and 10%
+ * faster rsa4096 ones, while on AMD64 it improves rsa1024 sign only
+ * by 10% and *worsens* rsa4096 sign by 15%. Once again, it's a
+ * reference implementation, one to be used as starting point for
+ * platform-specific assembler. Mentioned numbers apply to compiler
+ * generated code compiled with and without -DOPENSSL_BN_ASM_MONT and
+ * can vary not only from platform to platform, but even for compiler
+ * versions. Assembler vs. assembler improvement coefficients can
+ * [and are known to] differ and are to be documented elsewhere. */
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0p, int num) {
+  BN_ULONG c0, c1, ml, *tp, n0;
+#ifdef mul64
+  BN_ULONG mh;
+#endif
+  volatile BN_ULONG *vp;
+  int i = 0, j;
+
+#if 0 /* template for platform-specific implementation */
+	if (ap==bp)	return bn_sqr_mont(rp,ap,np,n0p,num);
+#endif
+  vp = tp = alloca((num + 2) * sizeof(BN_ULONG));
+
+  n0 = *n0p;
+
+  c0 = 0;
+  ml = bp[0];
+#ifdef mul64
+  mh = HBITS(ml);
+  ml = LBITS(ml);
+  for (j = 0; j < num; ++j)
+    mul(tp[j], ap[j], ml, mh, c0);
+#else
+  for (j = 0; j < num; ++j)
+    mul(tp[j], ap[j], ml, c0);
+#endif
+
+  tp[num] = c0;
+  tp[num + 1] = 0;
+  goto enter;
+
+  for (i = 0; i < num; i++) {
+    c0 = 0;
+    ml = bp[i];
+#ifdef mul64
+    mh = HBITS(ml);
+    ml = LBITS(ml);
+    for (j = 0; j < num; ++j)
+      mul_add(tp[j], ap[j], ml, mh, c0);
+#else
+    for (j = 0; j < num; ++j)
+      mul_add(tp[j], ap[j], ml, c0);
+#endif
+    c1 = (tp[num] + c0) & BN_MASK2;
+    tp[num] = c1;
+    tp[num + 1] = (c1 < c0 ? 1 : 0);
+  enter:
+    c1 = tp[0];
+    ml = (c1 * n0) & BN_MASK2;
+    c0 = 0;
+#ifdef mul64
+    mh = HBITS(ml);
+    ml = LBITS(ml);
+    mul_add(c1, np[0], ml, mh, c0);
+#else
+    mul_add(c1, ml, np[0], c0);
+#endif
+    for (j = 1; j < num; j++) {
+      c1 = tp[j];
+#ifdef mul64
+      mul_add(c1, np[j], ml, mh, c0);
+#else
+      mul_add(c1, ml, np[j], c0);
+#endif
+      tp[j - 1] = c1 & BN_MASK2;
+    }
+    c1 = (tp[num] + c0) & BN_MASK2;
+    tp[num - 1] = c1;
+    tp[num] = tp[num + 1] + (c1 < c0 ? 1 : 0);
+  }
+
+  if (tp[num] != 0 || tp[num - 1] >= np[num - 1]) {
+    c0 = bn_sub_words(rp, tp, np, num);
+    if (tp[num] != 0 || c0 == 0) {
+      for (i = 0; i < num + 2; i++)
+        vp[i] = 0;
+      return 1;
+    }
+  }
+  for (i = 0; i < num; i++)
+    rp[i] = tp[i], vp[i] = 0;
+  vp[num] = 0;
+  vp[num + 1] = 0;
+  return 1;
+}
+#endif
+
+#endif
diff --git a/crypto/bn/internal.h b/crypto/bn/internal.h
new file mode 100644
index 0000000..3d6d75d
--- /dev/null
+++ b/crypto/bn/internal.h
@@ -0,0 +1,291 @@
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the Eric Young open source
+ * license provided above.
+ *
+ * The binary polynomial arithmetic software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#ifndef OPENSSL_HEADER_BN_INTERNAL_H
+#define OPENSSL_HEADER_BN_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* BN_wexpand ensures that |bn| has at least |words| works of space without
+ * altering its value. It returns one on success or zero on allocation
+ * failure. */
+BIGNUM *bn_wexpand(BIGNUM *bn, unsigned words);
+
+/* bn_expand acts the same as |BN_wexpand|, but takes a number of bits rather
+ * than a number of words. */
+BIGNUM *bn_expand(BIGNUM *bn, unsigned bits);
+
+/* bn_correct_top decrements |bn->top| until |bn->d[top-1]| is non-zero or
+ * until |top| is zero. */
+void bn_correct_top(BIGNUM *bn);
+
+#if defined(OPENSSL_64_BIT)
+
+#define BN_ULLONG	unsigned long long
+#define BN_LONG		long
+#define BN_BITS		128
+#define BN_BYTES	8
+#define BN_BITS4	32
+#define BN_MASK		(0xffffffffffffffffffffffffffffffffLL)
+#define BN_MASK2	(0xffffffffffffffffL)
+#define BN_MASK2l	(0xffffffffL)
+#define BN_MASK2h	(0xffffffff00000000L)
+#define BN_MASK2h1	(0xffffffff80000000L)
+#define BN_TBIT		(0x8000000000000000L)
+#define BN_DEC_CONV	(10000000000000000000UL)
+#define BN_DEC_FMT1	"%lu"
+#define BN_DEC_FMT2	"%019lu"
+#define BN_DEC_NUM	19
+#define BN_HEX_FMT1	"%lX"
+#define BN_HEX_FMT2	"%016lX"
+
+#elif defined(OPENSSL_32_BIT)
+
+#define BN_ULLONG	unsigned long long
+#define BN_MASK	(0xffffffffffffffffLL)
+#define BN_LONG		int32_t
+#define BN_BITS		64
+#define BN_BYTES	4
+#define BN_BITS4	16
+#define BN_MASK2	(0xffffffffL)
+#define BN_MASK2l	(0xffff)
+#define BN_MASK2h1	(0xffff8000L)
+#define BN_MASK2h	(0xffff0000L)
+#define BN_TBIT		(0x80000000L)
+#define BN_DEC_CONV	(1000000000L)
+#define BN_DEC_FMT1	"%u"
+#define BN_DEC_FMT2	"%09u"
+#define BN_DEC_NUM	9
+#define BN_HEX_FMT1	"%X"
+#define BN_HEX_FMT2	"%08X"
+
+#else
+#error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
+#endif
+
+/* Pentium pro 16,16,16,32,64 */
+/* Alpha       16,16,16,16.64 */
+#define BN_MULL_SIZE_NORMAL (16)              /* 32 */
+#define BN_MUL_RECURSIVE_SIZE_NORMAL (16)     /* 32 less than */
+#define BN_SQR_RECURSIVE_SIZE_NORMAL (16)     /* 32 */
+#define BN_MUL_LOW_RECURSIVE_SIZE_NORMAL (32) /* 32 */
+#define BN_MONT_CTX_SET_SIZE_WORD (64)        /* 32 */
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w);
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w);
+void     bn_sqr_words(BN_ULONG *rp, const BN_ULONG *ap, int num);
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d);
+BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,int num);
+BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,int num);
+
+void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b);
+void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b);
+void bn_sqr_comba8(BN_ULONG *r, const BN_ULONG *a);
+void bn_sqr_comba4(BN_ULONG *r, const BN_ULONG *a);
+
+/* bn_cmp_words returns a value less than, equal to or greater than zero if
+ * the, length |n|, array |a| is less than, equal to or greater than |b|. */
+int bn_cmp_words(const BN_ULONG *a, const BN_ULONG *b, int n);
+
+/* bn_cmp_words returns a value less than, equal to or greater than zero if the
+ * array |a| is less than, equal to or greater than |b|. The arrays can be of
+ * different lengths: |cl| gives the minimum of the two lengths and |dl| gives
+ * the length of |a| minus the length of |b|. */
+int bn_cmp_part_words(const BN_ULONG *a, const BN_ULONG *b, int cl, int dl);
+
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0, int num);
+
+#define LBITS(a) ((a) & BN_MASK2l)
+#define HBITS(a) (((a) >> BN_BITS4) & BN_MASK2l)
+#define L2HBITS(a) (((a) << BN_BITS4) & BN_MASK2)
+
+#define LLBITS(a) ((a) & BN_MASKl)
+#define LHBITS(a) (((a) >> BN_BITS2) & BN_MASKl)
+#define LL2HBITS(a) ((BN_ULLONG)((a) & BN_MASKl) << BN_BITS2)
+
+#define mul64(l, h, bl, bh)       \
+  {                               \
+    BN_ULONG m, m1, lt, ht;       \
+                                  \
+    lt = l;                       \
+    ht = h;                       \
+    m = (bh) * (lt);              \
+    lt = (bl) * (lt);             \
+    m1 = (bl) * (ht);             \
+    ht = (bh) * (ht);             \
+    m = (m + m1) & BN_MASK2;      \
+    if (m < m1)                   \
+      ht += L2HBITS((BN_ULONG)1); \
+    ht += HBITS(m);               \
+    m1 = L2HBITS(m);              \
+    lt = (lt + m1) & BN_MASK2;    \
+    if (lt < m1)                  \
+      ht++;                       \
+    (l) = lt;                     \
+    (h) = ht;                     \
+  }
+
+#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64)
+# if defined(__GNUC__) && __GNUC__>=2
+#  define BN_UMULT_HIGH(a,b)	({	\
+	register BN_ULONG ret,discard;	\
+	__asm__ ("mulq	%3"		\
+	     : "=a"(discard),"=d"(ret)	\
+	     : "a"(a), "g"(b)		\
+	     : "cc");			\
+	ret;			})
+#  define BN_UMULT_LOHI(low,high,a,b)	\
+	__asm__ ("mulq	%3"		\
+		: "=a"(low),"=d"(high)	\
+		: "a"(a),"g"(b)		\
+		: "cc");
+# endif
+# if defined(_MSC_VER) && _MSC_VER>=1400
+   unsigned __int64 __umulh	(unsigned __int64 a,unsigned __int64 b);
+   unsigned __int64 _umul128	(unsigned __int64 a,unsigned __int64 b,
+				 unsigned __int64 *h);
+#  pragma intrinsic(__umulh,_umul128)
+#  define BN_UMULT_HIGH(a,b)		__umulh((a),(b))
+#  define BN_UMULT_LOHI(low,high,a,b)	((low)=_umul128((a),(b),&(high)))
+# endif
+#elif !defined(OPENSSL_NO_ASM) && defined(OPENSSL_AARCH64)
+# if defined(__GNUC__) && __GNUC__>=2
+#  define BN_UMULT_HIGH(a,b)	({	\
+	register BN_ULONG ret;		\
+	__asm__ ("umulh	%0,%1,%2"	\
+	     : "=r"(ret)		\
+	     : "r"(a), "r"(b));		\
+	ret;			})
+# endif
+#endif
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_BN_INTERNAL_H */
diff --git a/crypto/bn/kronecker.c b/crypto/bn/kronecker.c
new file mode 100644
index 0000000..23ef79a
--- /dev/null
+++ b/crypto/bn/kronecker.c
@@ -0,0 +1,175 @@
+/* ====================================================================
+ * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include "internal.h"
+
+
+/* least significant word */
+#define BN_lsw(n) (((n)->top == 0) ? (BN_ULONG) 0 : (n)->d[0])
+
+/* Returns -2 for errors because both -1 and 0 are valid results. */
+int BN_kronecker(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
+  int i;
+  int ret = -2;
+  BIGNUM *A, *B, *tmp;
+  /* In 'tab', only odd-indexed entries are relevant:
+   * For any odd BIGNUM n,
+   *     tab[BN_lsw(n) & 7]
+   * is $(-1)^{(n^2-1)/8}$ (using TeX notation).
+   * Note that the sign of n does not matter. */
+  static const int tab[8] = {0, 1, 0, -1, 0, -1, 0, 1};
+
+  BN_CTX_start(ctx);
+  A = BN_CTX_get(ctx);
+  B = BN_CTX_get(ctx);
+  if (B == NULL) {
+    goto end;
+  }
+
+  if (!BN_copy(A, a) ||
+      !BN_copy(B, b)) {
+    goto end;
+  }
+
+  /* Kronecker symbol, imlemented according to Henri Cohen,
+   * "A Course in Computational Algebraic Number Theory"
+   * (algorithm 1.4.10). */
+
+  /* Cohen's step 1: */
+
+  if (BN_is_zero(B)) {
+    ret = BN_abs_is_word(A, 1);
+    goto end;
+  }
+
+  /* Cohen's step 2: */
+
+  if (!BN_is_odd(A) && !BN_is_odd(B)) {
+    ret = 0;
+    goto end;
+  }
+
+  /* now B is non-zero */
+  i = 0;
+  while (!BN_is_bit_set(B, i)) {
+    i++;
+  }
+  if (!BN_rshift(B, B, i)) {
+    goto end;
+  }
+  if (i & 1) {
+    /* i is odd */
+    /* (thus B was even, thus A must be odd!)  */
+
+    /* set 'ret' to $(-1)^{(A^2-1)/8}$ */
+    ret = tab[BN_lsw(A) & 7];
+  } else {
+    /* i is even */
+    ret = 1;
+  }
+
+  if (B->neg) {
+    B->neg = 0;
+    if (A->neg) {
+      ret = -ret;
+    }
+  }
+
+  /* now B is positive and odd, so what remains to be done is to compute the
+   * Jacobi symbol (A/B) and multiply it by 'ret' */
+
+  while (1) {
+    /* Cohen's step 3: */
+
+    /* B is positive and odd */
+    if (BN_is_zero(A)) {
+      ret = BN_is_one(B) ? ret : 0;
+      goto end;
+    }
+
+    /* now A is non-zero */
+    i = 0;
+    while (!BN_is_bit_set(A, i)) {
+      i++;
+    }
+    if (!BN_rshift(A, A, i)) {
+      goto end;
+    }
+    if (i & 1) {
+      /* i is odd */
+      /* multiply 'ret' by  $(-1)^{(B^2-1)/8}$ */
+      ret = ret * tab[BN_lsw(B) & 7];
+    }
+
+    /* Cohen's step 4: */
+    /* multiply 'ret' by  $(-1)^{(A-1)(B-1)/4}$ */
+    if ((A->neg ? ~BN_lsw(A) : BN_lsw(A)) & BN_lsw(B) & 2) {
+      ret = -ret;
+    }
+
+    /* (A, B) := (B mod |A|, |A|) */
+    if (!BN_nnmod(B, B, A, ctx)) {
+      ret = -2;
+      goto end;
+    }
+    tmp = A;
+    A = B;
+    B = tmp;
+    tmp->neg = 0;
+  }
+
+end:
+  BN_CTX_end(ctx);
+  return ret;
+}
diff --git a/crypto/bn/montgomery.c b/crypto/bn/montgomery.c
new file mode 100644
index 0000000..4c803d2
--- /dev/null
+++ b/crypto/bn/montgomery.c
@@ -0,0 +1,561 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include <openssl/mem.h>
+#include <openssl/thread.h>
+
+#include "internal.h"
+
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
+#define OPENSSL_BN_ASM_MONT
+#endif
+
+BN_MONT_CTX *BN_MONT_CTX_new(void) {
+  BN_MONT_CTX *ret = OPENSSL_malloc(sizeof(BN_MONT_CTX));
+
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  BN_MONT_CTX_init(ret);
+  ret->flags = BN_FLG_MALLOCED;
+  return ret;
+}
+
+void BN_MONT_CTX_init(BN_MONT_CTX *mont) {
+  memset(mont, 0, sizeof(BN_MONT_CTX));
+  BN_init(&mont->RR);
+  BN_init(&mont->N);
+  BN_init(&mont->Ni);
+}
+
+void BN_MONT_CTX_free(BN_MONT_CTX *mont) {
+  if (mont == NULL) {
+    return;
+  }
+
+  BN_free(&mont->RR);
+  BN_free(&mont->N);
+  BN_free(&mont->Ni);
+  if (mont->flags & BN_FLG_MALLOCED) {
+    OPENSSL_free(mont);
+  }
+}
+
+BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to, BN_MONT_CTX *from) {
+  if (to == from) {
+    return to;
+  }
+
+  if (!BN_copy(&to->RR, &from->RR) ||
+      !BN_copy(&to->N, &from->N) ||
+      !BN_copy(&to->Ni, &from->Ni)) {
+    return NULL;
+  }
+  to->ri = from->ri;
+  to->n0[0] = from->n0[0];
+  to->n0[1] = from->n0[1];
+  return to;
+}
+
+int BN_MONT_CTX_set(BN_MONT_CTX *mont, const BIGNUM *mod, BN_CTX *ctx) {
+  int ret = 0;
+  BIGNUM *Ri, *R;
+  BIGNUM tmod;
+  BN_ULONG buf[2];
+
+  BN_CTX_start(ctx);
+  Ri = BN_CTX_get(ctx);
+  if (Ri == NULL) {
+    goto err;
+  }
+  R = &mont->RR; /* grab RR as a temp */
+  if (!BN_copy(&mont->N, mod)) {
+    goto err; /* Set N */
+  }
+  mont->N.neg = 0;
+
+  BN_init(&tmod);
+  tmod.d = buf;
+  tmod.dmax = 2;
+  tmod.neg = 0;
+
+  mont->ri = (BN_num_bits(mod) + (BN_BITS2 - 1)) / BN_BITS2 * BN_BITS2;
+
+#if defined(OPENSSL_BN_ASM_MONT) && (BN_BITS2 <= 32)
+  /* Only certain BN_BITS2<=32 platforms actually make use of
+   * n0[1], and we could use the #else case (with a shorter R
+   * value) for the others.  However, currently only the assembler
+   * files do know which is which. */
+
+  BN_zero(R);
+  if (!BN_set_bit(R, 2 * BN_BITS2)) {
+    goto err;
+  }
+
+  tmod.top = 0;
+  if ((buf[0] = mod->d[0])) {
+    tmod.top = 1;
+  }
+  if ((buf[1] = mod->top > 1 ? mod->d[1] : 0)) {
+    tmod.top = 2;
+  }
+
+  if (BN_mod_inverse(Ri, R, &tmod, ctx) == NULL) {
+    goto err;
+  }
+  if (!BN_lshift(Ri, Ri, 2 * BN_BITS2)) {
+    goto err; /* R*Ri */
+  }
+  if (!BN_is_zero(Ri)) {
+    if (!BN_sub_word(Ri, 1)) {
+      goto err;
+    }
+  } else {
+    /* if N mod word size == 1 */
+    if (bn_expand(Ri, (int)sizeof(BN_ULONG) * 2) == NULL) {
+      goto err;
+    }
+    /* Ri-- (mod double word size) */
+    Ri->neg = 0;
+    Ri->d[0] = BN_MASK2;
+    Ri->d[1] = BN_MASK2;
+    Ri->top = 2;
+  }
+
+  if (!BN_div(Ri, NULL, Ri, &tmod, ctx)) {
+    goto err;
+  }
+  /* Ni = (R*Ri-1)/N,
+   * keep only couple of least significant words: */
+  mont->n0[0] = (Ri->top > 0) ? Ri->d[0] : 0;
+  mont->n0[1] = (Ri->top > 1) ? Ri->d[1] : 0;
+#else
+  BN_zero(R);
+  if (!BN_set_bit(R, BN_BITS2)) {
+    goto err; /* R */
+  }
+
+  buf[0] = mod->d[0]; /* tmod = N mod word size */
+  buf[1] = 0;
+  tmod.top = buf[0] != 0 ? 1 : 0;
+  /* Ri = R^-1 mod N*/
+  if (BN_mod_inverse(Ri, R, &tmod, ctx) == NULL) {
+    goto err;
+  }
+  if (!BN_lshift(Ri, Ri, BN_BITS2)) {
+    goto err; /* R*Ri */
+  }
+  if (!BN_is_zero(Ri)) {
+    if (!BN_sub_word(Ri, 1)) {
+      goto err;
+    }
+  } else {
+    /* if N mod word size == 1 */
+    if (!BN_set_word(Ri, BN_MASK2)) {
+      goto err; /* Ri-- (mod word size) */
+    }
+  }
+  if (!BN_div(Ri, NULL, Ri, &tmod, ctx)) {
+    goto err;
+  }
+  /* Ni = (R*Ri-1)/N,
+   * keep only least significant word: */
+  mont->n0[0] = (Ri->top > 0) ? Ri->d[0] : 0;
+  mont->n0[1] = 0;
+#endif
+
+  /* setup RR for conversions */
+  BN_zero(&(mont->RR));
+  if (!BN_set_bit(&(mont->RR), mont->ri * 2)) {
+    goto err;
+  }
+  if (!BN_mod(&(mont->RR), &(mont->RR), &(mont->N), ctx)) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, int lock,
+                                    const BIGNUM *mod, BN_CTX *ctx) {
+  int got_write_lock = 0;
+  BN_MONT_CTX *ret;
+
+  CRYPTO_r_lock(lock);
+  if (!*pmont) {
+    CRYPTO_r_unlock(lock);
+    CRYPTO_w_lock(lock);
+    got_write_lock = 1;
+
+    if (!*pmont) {
+      ret = BN_MONT_CTX_new();
+      if (ret && !BN_MONT_CTX_set(ret, mod, ctx)) {
+        BN_MONT_CTX_free(ret);
+      } else {
+        *pmont = ret;
+      }
+    }
+  }
+
+  ret = *pmont;
+
+  if (got_write_lock) {
+    CRYPTO_w_unlock(lock);
+  } else {
+    CRYPTO_r_unlock(lock);
+  }
+
+  return ret;
+}
+
+int BN_to_montgomery(BIGNUM *ret, const BIGNUM *a, const BN_MONT_CTX *mont,
+                     BN_CTX *ctx) {
+  return BN_mod_mul_montgomery(ret, a, &mont->RR, mont, ctx);
+}
+
+#if 0
+static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r,
+                                   const BN_MONT_CTX *mont) {
+  const BIGNUM *n;
+  BN_ULONG *ap, *np, *rp, n0, v, carry;
+  int nl, max, i;
+
+  n = &mont->N;
+  nl = n->top;
+  if (nl == 0) {
+    ret->top = 0;
+    return 1;
+  }
+
+  max = (2 * nl); /* carry is stored separately */
+  if (bn_wexpand(r, max) == NULL) {
+    return 0;
+  }
+
+  r->neg ^= n->neg;
+  np = n->d;
+  rp = r->d;
+
+  /* clear the top words of T */
+  if (max > r->top) {
+    memset(&rp[r->top], 0, (max - r->top) * sizeof(BN_ULONG));
+  }
+
+  r->top = max;
+  n0 = mont->n0[0];
+
+  for (carry = 0, i = 0; i < nl; i++, rp++) {
+    v = bn_mul_add_words(rp, np, nl, (rp[0] * n0) & BN_MASK2);
+    v = (v + carry + rp[nl]) & BN_MASK2;
+    carry |= (v != rp[nl]);
+    carry &= (v <= rp[nl]);
+    rp[nl] = v;
+  }
+
+  if (bn_wexpand(ret, nl) == NULL) {
+    return 0;
+  }
+  ret->top = nl;
+  ret->neg = r->neg;
+
+  rp = ret->d;
+  ap = &(r->d[nl]);
+
+  {
+    BN_ULONG *nrp;
+    size_t m;
+
+    v = bn_sub_words(rp, ap, np, nl) - carry;
+    /* if subtraction result is real, then trick unconditional memcpy below to
+     * perform in-place "refresh" instead of actual copy. */
+    m = (0 - (size_t)v);
+    nrp = (BN_ULONG *)(((intptr_t)rp & ~m) | ((intptr_t)ap & m));
+
+    for (i = 0, nl -= 4; i < nl; i += 4) {
+      BN_ULONG t1, t2, t3, t4;
+
+      t1 = nrp[i + 0];
+      t2 = nrp[i + 1];
+      t3 = nrp[i + 2];
+      ap[i + 0] = 0;
+      t4 = nrp[i + 3];
+      ap[i + 1] = 0;
+      rp[i + 0] = t1;
+      ap[i + 2] = 0;
+      rp[i + 1] = t2;
+      ap[i + 3] = 0;
+      rp[i + 2] = t3;
+      rp[i + 3] = t4;
+    }
+
+    for (nl += 4; i < nl; i++) {
+      rp[i] = nrp[i], ap[i] = 0;
+    }
+  }
+
+  bn_correct_top(r);
+  bn_correct_top(ret);
+
+  return 1;
+}
+#endif
+
+#define PTR_SIZE_INT size_t
+
+static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r, const BN_MONT_CTX *mont)
+	{
+	BIGNUM *n;
+	BN_ULONG *ap,*np,*rp,n0,v,carry;
+	int nl,max,i;
+
+	n= (BIGNUM*) &(mont->N);
+	nl=n->top;
+	if (nl == 0) { ret->top=0; return(1); }
+
+	max=(2*nl); /* carry is stored separately */
+	if (bn_wexpand(r,max) == NULL) return(0);
+
+	r->neg^=n->neg;
+	np=n->d;
+	rp=r->d;
+
+	/* clear the top words of T */
+#if 1
+	for (i=r->top; i<max; i++) /* memset? XXX */
+		rp[i]=0;
+#else
+	memset(&(rp[r->top]),0,(max-r->top)*sizeof(BN_ULONG)); 
+#endif
+
+	r->top=max;
+	n0=mont->n0[0];
+
+	for (carry=0, i=0; i<nl; i++, rp++)
+		{
+		v=bn_mul_add_words(rp,np,nl,(rp[0]*n0)&BN_MASK2);
+		v = (v+carry+rp[nl])&BN_MASK2;
+		carry |= (v != rp[nl]);
+		carry &= (v <= rp[nl]);
+		rp[nl]=v;
+		}
+
+	if (bn_wexpand(ret,nl) == NULL) return(0);
+	ret->top=nl;
+	ret->neg=r->neg;
+
+	rp=ret->d;
+	ap=&(r->d[nl]);
+
+	{
+	BN_ULONG *nrp;
+	size_t m;
+
+	v=bn_sub_words(rp,ap,np,nl)-carry;
+	/* if subtraction result is real, then
+	 * trick unconditional memcpy below to perform in-place
+	 * "refresh" instead of actual copy. */
+	m=(0-(size_t)v);
+	nrp=(BN_ULONG *)(((PTR_SIZE_INT)rp&~m)|((PTR_SIZE_INT)ap&m));
+
+	for (i=0,nl-=4; i<nl; i+=4)
+		{
+		BN_ULONG t1,t2,t3,t4;
+		
+		t1=nrp[i+0];
+		t2=nrp[i+1];
+		t3=nrp[i+2];	ap[i+0]=0;
+		t4=nrp[i+3];	ap[i+1]=0;
+		rp[i+0]=t1;	ap[i+2]=0;
+		rp[i+1]=t2;	ap[i+3]=0;
+		rp[i+2]=t3;
+		rp[i+3]=t4;
+		}
+	for (nl+=4; i<nl; i++)
+		rp[i]=nrp[i], ap[i]=0;
+	}
+	bn_correct_top(r);
+	bn_correct_top(ret);
+
+	return(1);
+	}
+
+int BN_from_montgomery(BIGNUM *ret, const BIGNUM *a, const BN_MONT_CTX *mont,
+                       BN_CTX *ctx) {
+  int retn = 0;
+  BIGNUM *t;
+
+  BN_CTX_start(ctx);
+  t = BN_CTX_get(ctx);
+  if (t == NULL) {
+    return 0;
+  }
+
+  if (BN_copy(t, a))
+    retn = BN_from_montgomery_word(ret, t, mont);
+  BN_CTX_end(ctx);
+
+  return retn;
+}
+
+int BN_mod_mul_montgomery(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                          const BN_MONT_CTX *mont, BN_CTX *ctx) {
+  BIGNUM *tmp;
+  int ret = 0;
+
+#if defined(OPENSSL_BN_ASM_MONT)
+  int num = mont->N.top;
+
+  if (num > 1 && a->top == num && b->top == num) {
+    if (bn_wexpand(r, num) == NULL) {
+      return 0;
+    }
+    if (bn_mul_mont(r->d, a->d, b->d, mont->N.d, mont->n0, num)) {
+      r->neg = a->neg ^ b->neg;
+      r->top = num;
+      bn_correct_top(r);
+      return 1;
+    }
+  }
+#endif
+
+  BN_CTX_start(ctx);
+  tmp = BN_CTX_get(ctx);
+  if (tmp == NULL) {
+    goto err;
+  }
+
+  if (a == b) {
+    if (!BN_sqr(tmp, a, ctx)) {
+      goto err;
+    }
+  } else {
+    if (!BN_mul(tmp, a, b, ctx)) {
+      goto err;
+    }
+  }
+
+  /* reduce from aRR to aR */
+  if (!BN_from_montgomery_word(r, tmp, mont)) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
diff --git a/crypto/bn/mul.c b/crypto/bn/mul.c
new file mode 100644
index 0000000..af26eba
--- /dev/null
+++ b/crypto/bn/mul.c
@@ -0,0 +1,884 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb) {
+  BN_ULONG *rr;
+
+  if (na < nb) {
+    int itmp;
+    BN_ULONG *ltmp;
+
+    itmp = na;
+    na = nb;
+    nb = itmp;
+    ltmp = a;
+    a = b;
+    b = ltmp;
+  }
+  rr = &(r[na]);
+  if (nb <= 0) {
+    (void)bn_mul_words(r, a, na, 0);
+    return;
+  } else {
+    rr[0] = bn_mul_words(r, a, na, b[0]);
+  }
+
+  for (;;) {
+    if (--nb <= 0) {
+      return;
+    }
+    rr[1] = bn_mul_add_words(&(r[1]), a, na, b[1]);
+    if (--nb <= 0) {
+      return;
+    }
+    rr[2] = bn_mul_add_words(&(r[2]), a, na, b[2]);
+    if (--nb <= 0) {
+      return;
+    }
+    rr[3] = bn_mul_add_words(&(r[3]), a, na, b[3]);
+    if (--nb <= 0) {
+      return;
+    }
+    rr[4] = bn_mul_add_words(&(r[4]), a, na, b[4]);
+    rr += 4;
+    r += 4;
+    b += 4;
+  }
+}
+
+void bn_mul_low_normal(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n) {
+  bn_mul_words(r, a, n, b[0]);
+
+  for (;;) {
+    if (--n <= 0) {
+      return;
+    }
+    bn_mul_add_words(&(r[1]), a, n, b[1]);
+    if (--n <= 0) {
+      return;
+    }
+    bn_mul_add_words(&(r[2]), a, n, b[2]);
+    if (--n <= 0) {
+      return;
+    }
+    bn_mul_add_words(&(r[3]), a, n, b[3]);
+    if (--n <= 0) {
+      return;
+    }
+    bn_mul_add_words(&(r[4]), a, n, b[4]);
+    r += 4;
+    b += 4;
+  }
+}
+
+#if !defined(OPENSSL_X86)
+/* Here follows specialised variants of bn_add_words() and bn_sub_words(). They
+ * have the property performing operations on arrays of different sizes. The
+ * sizes of those arrays is expressed through cl, which is the common length (
+ * basicall, min(len(a),len(b)) ), and dl, which is the delta between the two
+ * lengths, calculated as len(a)-len(b). All lengths are the number of
+ * BN_ULONGs...  For the operations that require a result array as parameter,
+ * it must have the length cl+abs(dl). These functions should probably end up
+ * in bn_asm.c as soon as there are assembler counterparts for the systems that
+ * use assembler files.  */
+
+static BN_ULONG bn_sub_part_words(BN_ULONG *r, const BN_ULONG *a,
+                                  const BN_ULONG *b, int cl, int dl) {
+  BN_ULONG c, t;
+
+  assert(cl >= 0);
+  c = bn_sub_words(r, a, b, cl);
+
+  if (dl == 0)
+    return c;
+
+  r += cl;
+  a += cl;
+  b += cl;
+
+  if (dl < 0) {
+    for (;;) {
+      t = b[0];
+      r[0] = (0 - t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 1;
+      }
+      if (++dl >= 0) {
+        break;
+      }
+
+      t = b[1];
+      r[1] = (0 - t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 1;
+      }
+      if (++dl >= 0) {
+        break;
+      }
+
+      t = b[2];
+      r[2] = (0 - t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 1;
+      }
+      if (++dl >= 0) {
+        break;
+      }
+
+      t = b[3];
+      r[3] = (0 - t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 1;
+      }
+      if (++dl >= 0) {
+        break;
+      }
+
+      b += 4;
+      r += 4;
+    }
+  } else {
+    int save_dl = dl;
+    while (c) {
+      t = a[0];
+      r[0] = (t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 0;
+      }
+      if (--dl <= 0) {
+        break;
+      }
+
+      t = a[1];
+      r[1] = (t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 0;
+      }
+      if (--dl <= 0) {
+        break;
+      }
+
+      t = a[2];
+      r[2] = (t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 0;
+      }
+      if (--dl <= 0) {
+        break;
+      }
+
+      t = a[3];
+      r[3] = (t - c) & BN_MASK2;
+      if (t != 0) {
+        c = 0;
+      }
+      if (--dl <= 0) {
+        break;
+      }
+
+      save_dl = dl;
+      a += 4;
+      r += 4;
+    }
+    if (dl > 0) {
+      if (save_dl > dl) {
+        switch (save_dl - dl) {
+          case 1:
+            r[1] = a[1];
+            if (--dl <= 0) {
+              break;
+            }
+          case 2:
+            r[2] = a[2];
+            if (--dl <= 0) {
+              break;
+            }
+          case 3:
+            r[3] = a[3];
+            if (--dl <= 0) {
+              break;
+            }
+        }
+        a += 4;
+        r += 4;
+      }
+    }
+
+    if (dl > 0) {
+      for (;;) {
+        r[0] = a[0];
+        if (--dl <= 0) {
+          break;
+        }
+        r[1] = a[1];
+        if (--dl <= 0) {
+          break;
+        }
+        r[2] = a[2];
+        if (--dl <= 0) {
+          break;
+        }
+        r[3] = a[3];
+        if (--dl <= 0) {
+          break;
+        }
+
+        a += 4;
+        r += 4;
+      }
+    }
+  }
+
+  return c;
+}
+#else
+/* On other platforms the function is defined in asm. */
+BN_ULONG bn_sub_part_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                           int cl, int dl);
+#endif
+
+/* Karatsuba recursive multiplication algorithm
+ * (cf. Knuth, The Art of Computer Programming, Vol. 2) */
+
+/* r is 2*n2 words in size,
+ * a and b are both n2 words in size.
+ * n2 must be a power of 2.
+ * We multiply and return the result.
+ * t must be 2*n2 words in size
+ * We calculate
+ * a[0]*b[0]
+ * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0])
+ * a[1]*b[1]
+ */
+/* dnX may not be positive, but n2/2+dnX has to be */
+static void bn_mul_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2,
+                             int dna, int dnb, BN_ULONG *t) {
+  int n = n2 / 2, c1, c2;
+  int tna = n + dna, tnb = n + dnb;
+  unsigned int neg, zero;
+  BN_ULONG ln, lo, *p;
+
+  /* Only call bn_mul_comba 8 if n2 == 8 and the
+   * two arrays are complete [steve]
+   */
+  if (n2 == 8 && dna == 0 && dnb == 0) {
+    bn_mul_comba8(r, a, b);
+    return;
+  }
+
+  /* Else do normal multiply */
+  if (n2 < BN_MUL_RECURSIVE_SIZE_NORMAL) {
+    bn_mul_normal(r, a, n2 + dna, b, n2 + dnb);
+    if ((dna + dnb) < 0)
+      memset(&r[2 * n2 + dna + dnb], 0, sizeof(BN_ULONG) * -(dna + dnb));
+    return;
+  }
+
+  /* r=(a[0]-a[1])*(b[1]-b[0]) */
+  c1 = bn_cmp_part_words(a, &(a[n]), tna, n - tna);
+  c2 = bn_cmp_part_words(&(b[n]), b, tnb, tnb - n);
+  zero = neg = 0;
+  switch (c1 * 3 + c2) {
+    case -4:
+      bn_sub_part_words(t, &(a[n]), a, tna, tna - n);       /* - */
+      bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+      break;
+    case -3:
+      zero = 1;
+      break;
+    case -2:
+      bn_sub_part_words(t, &(a[n]), a, tna, tna - n);       /* - */
+      bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n); /* + */
+      neg = 1;
+      break;
+    case -1:
+    case 0:
+    case 1:
+      zero = 1;
+      break;
+    case 2:
+      bn_sub_part_words(t, a, &(a[n]), tna, n - tna);       /* + */
+      bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+      neg = 1;
+      break;
+    case 3:
+      zero = 1;
+      break;
+    case 4:
+      bn_sub_part_words(t, a, &(a[n]), tna, n - tna);
+      bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n);
+      break;
+  }
+
+  if (n == 4 && dna == 0 && dnb == 0) {
+    /* XXX: bn_mul_comba4 could take extra args to do this well */
+    if (!zero) {
+      bn_mul_comba4(&(t[n2]), t, &(t[n]));
+    } else {
+      memset(&(t[n2]), 0, 8 * sizeof(BN_ULONG));
+    }
+
+    bn_mul_comba4(r, a, b);
+    bn_mul_comba4(&(r[n2]), &(a[n]), &(b[n]));
+  } else if (n == 8 && dna == 0 && dnb == 0) {
+    /* XXX: bn_mul_comba8 could take extra args to do this well */
+    if (!zero) {
+      bn_mul_comba8(&(t[n2]), t, &(t[n]));
+    } else {
+      memset(&(t[n2]), 0, 16 * sizeof(BN_ULONG));
+    }
+
+    bn_mul_comba8(r, a, b);
+    bn_mul_comba8(&(r[n2]), &(a[n]), &(b[n]));
+  } else {
+    p = &(t[n2 * 2]);
+    if (!zero) {
+      bn_mul_recursive(&(t[n2]), t, &(t[n]), n, 0, 0, p);
+    } else {
+      memset(&(t[n2]), 0, n2 * sizeof(BN_ULONG));
+    }
+    bn_mul_recursive(r, a, b, n, 0, 0, p);
+    bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]), n, dna, dnb, p);
+  }
+
+  /* t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign
+   * r[10] holds (a[0]*b[0])
+   * r[32] holds (b[1]*b[1]) */
+
+  c1 = (int)(bn_add_words(t, r, &(r[n2]), n2));
+
+  if (neg) {
+    /* if t[32] is negative */
+    c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2));
+  } else {
+    /* Might have a carry */
+    c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), t, n2));
+  }
+
+  /* t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1])
+   * r[10] holds (a[0]*b[0])
+   * r[32] holds (b[1]*b[1])
+   * c1 holds the carry bits */
+  c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2));
+  if (c1) {
+    p = &(r[n + n2]);
+    lo = *p;
+    ln = (lo + c1) & BN_MASK2;
+    *p = ln;
+
+    /* The overflow will stop before we over write
+     * words we should not overwrite */
+    if (ln < (BN_ULONG)c1) {
+      do {
+        p++;
+        lo = *p;
+        ln = (lo + 1) & BN_MASK2;
+        *p = ln;
+      } while (ln == 0);
+    }
+  }
+}
+
+/* n+tn is the word length
+ * t needs to be n*4 is size, as does r */
+/* tnX may not be negative but less than n */
+static void bn_mul_part_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n,
+                                  int tna, int tnb, BN_ULONG *t) {
+  int i, j, n2 = n * 2;
+  int c1, c2, neg;
+  BN_ULONG ln, lo, *p;
+
+  if (n < 8) {
+    bn_mul_normal(r, a, n + tna, b, n + tnb);
+    return;
+  }
+
+  /* r=(a[0]-a[1])*(b[1]-b[0]) */
+  c1 = bn_cmp_part_words(a, &(a[n]), tna, n - tna);
+  c2 = bn_cmp_part_words(&(b[n]), b, tnb, tnb - n);
+  neg = 0;
+  switch (c1 * 3 + c2) {
+    case -4:
+      bn_sub_part_words(t, &(a[n]), a, tna, tna - n);       /* - */
+      bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+      break;
+    case -3:
+    /* break; */
+    case -2:
+      bn_sub_part_words(t, &(a[n]), a, tna, tna - n);       /* - */
+      bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n); /* + */
+      neg = 1;
+      break;
+    case -1:
+    case 0:
+    case 1:
+    /* break; */
+    case 2:
+      bn_sub_part_words(t, a, &(a[n]), tna, n - tna);       /* + */
+      bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+      neg = 1;
+      break;
+    case 3:
+    /* break; */
+    case 4:
+      bn_sub_part_words(t, a, &(a[n]), tna, n - tna);
+      bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n);
+      break;
+  }
+
+  if (n == 8) {
+    bn_mul_comba8(&(t[n2]), t, &(t[n]));
+    bn_mul_comba8(r, a, b);
+    bn_mul_normal(&(r[n2]), &(a[n]), tna, &(b[n]), tnb);
+    memset(&(r[n2 + tna + tnb]), 0, sizeof(BN_ULONG) * (n2 - tna - tnb));
+  } else {
+    p = &(t[n2 * 2]);
+    bn_mul_recursive(&(t[n2]), t, &(t[n]), n, 0, 0, p);
+    bn_mul_recursive(r, a, b, n, 0, 0, p);
+    i = n / 2;
+    /* If there is only a bottom half to the number,
+     * just do it */
+    if (tna > tnb) {
+      j = tna - i;
+    } else {
+      j = tnb - i;
+    }
+
+    if (j == 0) {
+      bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]), i, tna - i, tnb - i, p);
+      memset(&(r[n2 + i * 2]), 0, sizeof(BN_ULONG) * (n2 - i * 2));
+    } else if (j > 0) {
+      /* eg, n == 16, i == 8 and tn == 11 */
+      bn_mul_part_recursive(&(r[n2]), &(a[n]), &(b[n]), i, tna - i, tnb - i, p);
+      memset(&(r[n2 + tna + tnb]), 0, sizeof(BN_ULONG) * (n2 - tna - tnb));
+    } else {
+      /* (j < 0) eg, n == 16, i == 8 and tn == 5 */
+      memset(&(r[n2]), 0, sizeof(BN_ULONG) * n2);
+      if (tna < BN_MUL_RECURSIVE_SIZE_NORMAL &&
+          tnb < BN_MUL_RECURSIVE_SIZE_NORMAL) {
+        bn_mul_normal(&(r[n2]), &(a[n]), tna, &(b[n]), tnb);
+      } else {
+        for (;;) {
+          i /= 2;
+          /* these simplified conditions work
+           * exclusively because difference
+           * between tna and tnb is 1 or 0 */
+          if (i < tna || i < tnb) {
+            bn_mul_part_recursive(&(r[n2]), &(a[n]), &(b[n]), i, tna - i,
+                                  tnb - i, p);
+            break;
+          } else if (i == tna || i == tnb) {
+            bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]), i, tna - i, tnb - i,
+                             p);
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  /* t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign
+   * r[10] holds (a[0]*b[0])
+   * r[32] holds (b[1]*b[1])
+   */
+
+  c1 = (int)(bn_add_words(t, r, &(r[n2]), n2));
+
+  if (neg) {
+    /* if t[32] is negative */
+    c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2));
+  } else {
+    /* Might have a carry */
+    c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), t, n2));
+  }
+
+  /* t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1])
+   * r[10] holds (a[0]*b[0])
+   * r[32] holds (b[1]*b[1])
+   * c1 holds the carry bits */
+  c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2));
+  if (c1) {
+    p = &(r[n + n2]);
+    lo = *p;
+    ln = (lo + c1) & BN_MASK2;
+    *p = ln;
+
+    /* The overflow will stop before we over write
+     * words we should not overwrite */
+    if (ln < (BN_ULONG)c1) {
+      do {
+        p++;
+        lo = *p;
+        ln = (lo + 1) & BN_MASK2;
+        *p = ln;
+      } while (ln == 0);
+    }
+  }
+}
+
+int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
+  int ret = 0;
+  int top, al, bl;
+  BIGNUM *rr;
+  int i;
+  BIGNUM *t = NULL;
+  int j = 0, k;
+
+  al = a->top;
+  bl = b->top;
+
+  if ((al == 0) || (bl == 0)) {
+    BN_zero(r);
+    return 1;
+  }
+  top = al + bl;
+
+  BN_CTX_start(ctx);
+  if ((r == a) || (r == b)) {
+    if ((rr = BN_CTX_get(ctx)) == NULL) {
+      goto err;
+    }
+  } else {
+    rr = r;
+  }
+  rr->neg = a->neg ^ b->neg;
+
+  i = al - bl;
+  if (i == 0) {
+    if (al == 8) {
+      if (bn_wexpand(rr, 16) == NULL) {
+        goto err;
+      }
+      rr->top = 16;
+      bn_mul_comba8(rr->d, a->d, b->d);
+      goto end;
+    }
+  }
+
+  if ((al >= BN_MULL_SIZE_NORMAL) && (bl >= BN_MULL_SIZE_NORMAL)) {
+    if (i >= -1 && i <= 1) {
+      /* Find out the power of two lower or equal
+         to the longest of the two numbers */
+      if (i >= 0) {
+        j = BN_num_bits_word((BN_ULONG)al);
+      }
+      if (i == -1) {
+        j = BN_num_bits_word((BN_ULONG)bl);
+      }
+      j = 1 << (j - 1);
+      assert(j <= al || j <= bl);
+      k = j + j;
+      t = BN_CTX_get(ctx);
+      if (t == NULL) {
+        goto err;
+      }
+      if (al > j || bl > j) {
+        if (bn_wexpand(t, k * 4) == NULL) {
+          goto err;
+        }
+        if (bn_wexpand(rr, k * 4) == NULL) {
+          goto err;
+        }
+        bn_mul_part_recursive(rr->d, a->d, b->d, j, al - j, bl - j, t->d);
+      } else {
+        /* al <= j || bl <= j */
+        if (bn_wexpand(t, k * 2) == NULL) {
+          goto err;
+        }
+        if (bn_wexpand(rr, k * 2) == NULL) {
+          goto err;
+        }
+        bn_mul_recursive(rr->d, a->d, b->d, j, al - j, bl - j, t->d);
+      }
+      rr->top = top;
+      goto end;
+    }
+  }
+
+  if (bn_wexpand(rr, top) == NULL) {
+    goto err;
+  }
+  rr->top = top;
+  bn_mul_normal(rr->d, a->d, al, b->d, bl);
+
+end:
+  bn_correct_top(rr);
+  if (r != rr) {
+    BN_copy(r, rr);
+  }
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+/* tmp must have 2*n words */
+static void bn_sqr_normal(BN_ULONG *r, const BN_ULONG *a, int n, BN_ULONG *tmp) {
+  int i, j, max;
+  const BN_ULONG *ap;
+  BN_ULONG *rp;
+
+  max = n * 2;
+  ap = a;
+  rp = r;
+  rp[0] = rp[max - 1] = 0;
+  rp++;
+  j = n;
+
+  if (--j > 0) {
+    ap++;
+    rp[j] = bn_mul_words(rp, ap, j, ap[-1]);
+    rp += 2;
+  }
+
+  for (i = n - 2; i > 0; i--) {
+    j--;
+    ap++;
+    rp[j] = bn_mul_add_words(rp, ap, j, ap[-1]);
+    rp += 2;
+  }
+
+  bn_add_words(r, r, r, max);
+
+  /* There will not be a carry */
+
+  bn_sqr_words(tmp, a, n);
+
+  bn_add_words(r, r, tmp, max);
+}
+
+/* r is 2*n words in size,
+ * a and b are both n words in size.    (There's not actually a 'b' here ...)
+ * n must be a power of 2.
+ * We multiply and return the result.
+ * t must be 2*n words in size
+ * We calculate
+ * a[0]*b[0]
+ * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0])
+ * a[1]*b[1]
+ */
+static void bn_sqr_recursive(BN_ULONG *r, const BN_ULONG *a, int n2, BN_ULONG *t) {
+  int n = n2 / 2;
+  int zero, c1;
+  BN_ULONG ln, lo, *p;
+
+  if (n2 == 4) {
+    bn_sqr_comba4(r, a);
+    return;
+  } else if (n2 == 8) {
+    bn_sqr_comba8(r, a);
+    return;
+  }
+  if (n2 < BN_SQR_RECURSIVE_SIZE_NORMAL) {
+    bn_sqr_normal(r, a, n2, t);
+    return;
+  }
+  /* r=(a[0]-a[1])*(a[1]-a[0]) */
+  c1 = bn_cmp_words(a, &(a[n]), n);
+  zero = 0;
+  if (c1 > 0) {
+    bn_sub_words(t, a, &(a[n]), n);
+  } else if (c1 < 0) {
+    bn_sub_words(t, &(a[n]), a, n);
+  } else {
+    zero = 1;
+  }
+
+  /* The result will always be negative unless it is zero */
+  p = &(t[n2 * 2]);
+
+  if (!zero) {
+    bn_sqr_recursive(&(t[n2]), t, n, p);
+  } else {
+    memset(&(t[n2]), 0, n2 * sizeof(BN_ULONG));
+  }
+  bn_sqr_recursive(r, a, n, p);
+  bn_sqr_recursive(&(r[n2]), &(a[n]), n, p);
+
+  /* t[32] holds (a[0]-a[1])*(a[1]-a[0]), it is negative or zero
+   * r[10] holds (a[0]*b[0])
+   * r[32] holds (b[1]*b[1]) */
+
+  c1 = (int)(bn_add_words(t, r, &(r[n2]), n2));
+
+  /* t[32] is negative */
+  c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2));
+
+  /* t[32] holds (a[0]-a[1])*(a[1]-a[0])+(a[0]*a[0])+(a[1]*a[1])
+   * r[10] holds (a[0]*a[0])
+   * r[32] holds (a[1]*a[1])
+   * c1 holds the carry bits */
+  c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2));
+  if (c1) {
+    p = &(r[n + n2]);
+    lo = *p;
+    ln = (lo + c1) & BN_MASK2;
+    *p = ln;
+
+    /* The overflow will stop before we over write
+     * words we should not overwrite */
+    if (ln < (BN_ULONG)c1) {
+      do {
+        p++;
+        lo = *p;
+        ln = (lo + 1) & BN_MASK2;
+        *p = ln;
+      } while (ln == 0);
+    }
+  }
+}
+
+int BN_mul_word(BIGNUM *bn, BN_ULONG w) {
+  BN_ULONG ll;
+
+  w &= BN_MASK2;
+  if (!bn->top) {
+    return 1;
+  }
+
+  if (w == 0) {
+    BN_zero(bn);
+    return 1;
+  }
+
+  ll = bn_mul_words(bn->d, bn->d, bn->top, w);
+  if (ll) {
+    if (bn_wexpand(bn, bn->top + 1) == NULL) {
+      return 0;
+    }
+    bn->d[bn->top++] = ll;
+  }
+
+  return 1;
+}
+
+int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) {
+  int max, al;
+  int ret = 0;
+  BIGNUM *tmp, *rr;
+
+  al = a->top;
+  if (al <= 0) {
+    r->top = 0;
+    return 1;
+  }
+
+  BN_CTX_start(ctx);
+  rr = (a != r) ? r : BN_CTX_get(ctx);
+  tmp = BN_CTX_get(ctx);
+  if (!rr || !tmp) {
+    goto err;
+  }
+
+  max = 2 * al; /* Non-zero (from above) */
+  if (bn_wexpand(rr, max) == NULL) {
+    goto err;
+  }
+
+  if (al == 4) {
+    bn_sqr_comba4(rr->d, a->d);
+  } else if (al == 8) {
+    bn_sqr_comba8(rr->d, a->d);
+  } else {
+    if (al < BN_SQR_RECURSIVE_SIZE_NORMAL) {
+      BN_ULONG t[BN_SQR_RECURSIVE_SIZE_NORMAL * 2];
+      bn_sqr_normal(rr->d, a->d, al, t);
+    } else {
+      int j, k;
+
+      j = BN_num_bits_word((BN_ULONG)al);
+      j = 1 << (j - 1);
+      k = j + j;
+      if (al == j) {
+        if (bn_wexpand(tmp, k * 2) == NULL) {
+          goto err;
+        }
+        bn_sqr_recursive(rr->d, a->d, al, tmp->d);
+      } else {
+        if (bn_wexpand(tmp, max) == NULL) {
+          goto err;
+        }
+        bn_sqr_normal(rr->d, a->d, al, tmp->d);
+      }
+    }
+  }
+
+  rr->neg = 0;
+  /* If the most-significant half of the top word of 'a' is zero, then
+   * the square of 'a' will max-1 words. */
+  if (a->d[al - 1] == (a->d[al - 1] & BN_MASK2l)) {
+    rr->top = max - 1;
+  } else {
+    rr->top = max;
+  }
+
+  if (rr != r) {
+    BN_copy(r, rr);
+  }
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
diff --git a/crypto/bn/prime.c b/crypto/bn/prime.c
new file mode 100644
index 0000000..1b8221b
--- /dev/null
+++ b/crypto/bn/prime.c
@@ -0,0 +1,790 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+/* number of Miller-Rabin iterations for an error rate  of less than 2^-80
+ * for random 'b'-bit input, b >= 100 (taken from table 4.4 in the Handbook
+ * of Applied Cryptography [Menezes, van Oorschot, Vanstone; CRC Press 1996];
+ * original paper: Damgaard, Landrock, Pomerance: Average case error estimates
+ * for the strong probable prime test. -- Math. Comp. 61 (1993) 177-194) */
+#define BN_prime_checks_for_size(b) ((b) >= 1300 ?  2 : \
+                                (b) >=  850 ?  3 : \
+                                (b) >=  650 ?  4 : \
+                                (b) >=  550 ?  5 : \
+                                (b) >=  450 ?  6 : \
+                                (b) >=  400 ?  7 : \
+                                (b) >=  350 ?  8 : \
+                                (b) >=  300 ?  9 : \
+                                (b) >=  250 ? 12 : \
+                                (b) >=  200 ? 15 : \
+                                (b) >=  150 ? 18 : \
+                                /* b >= 100 */ 27)
+
+/* The quick sieve algorithm approach to weeding out primes is Philip
+ * Zimmermann's, as implemented in PGP.  I have had a read of his comments and
+ * implemented my own version. */
+
+/* NUMPRIMES is the number of primes that fit into a uint16_t. */
+#define NUMPRIMES 2048
+
+/* primes is defined at the bottom of the file and contains all the primes that
+ * fit into a uint16_t. */
+static const uint16_t primes[NUMPRIMES];
+
+static int witness(BIGNUM *w, const BIGNUM *a, const BIGNUM *a1,
+                   const BIGNUM *a1_odd, int k, BN_CTX *ctx, BN_MONT_CTX *mont);
+static int probable_prime(BIGNUM *rnd, int bits);
+static int probable_prime_dh(BIGNUM *rnd, int bits, const BIGNUM *add,
+                             const BIGNUM *rem, BN_CTX *ctx);
+static int probable_prime_dh_safe(BIGNUM *rnd, int bits, const BIGNUM *add,
+                                  const BIGNUM *rem, BN_CTX *ctx);
+
+void BN_GENCB_set(BN_GENCB *callback,
+                  int (*f)(int event, int n, struct bn_gencb_st *),
+                  void *arg) {
+  callback->callback = f;
+  callback->arg = arg;
+}
+
+int BN_GENCB_call(BN_GENCB *callback, int event, int n) {
+  if (!callback) {
+    return 1;
+  }
+
+  return callback->callback(event, n, callback);
+}
+
+int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, const BIGNUM *add,
+                         const BIGNUM *rem, BN_GENCB *cb) {
+  BIGNUM *t;
+  int found = 0;
+  int i, j, c1 = 0;
+  BN_CTX *ctx;
+  int checks = BN_prime_checks_for_size(bits);
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  t = BN_CTX_get(ctx);
+  if (!t) {
+    goto err;
+  }
+
+loop:
+  /* make a random number and set the top and bottom bits */
+  if (add == NULL) {
+    if (!probable_prime(ret, bits)) {
+      goto err;
+    }
+  } else {
+    if (safe) {
+      if (!probable_prime_dh_safe(ret, bits, add, rem, ctx)) {
+        goto err;
+      }
+    } else {
+      if (!probable_prime_dh(ret, bits, add, rem, ctx)) {
+        goto err;
+      }
+    }
+  }
+
+  if (!BN_GENCB_call(cb, BN_GENCB_GENERATED, c1++)) {
+    /* aborted */
+    goto err;
+  }
+
+  if (!safe) {
+    i = BN_is_prime_fasttest_ex(ret, checks, ctx, 0, cb);
+    if (i == -1) {
+      goto err;
+    } else if (i == 0) {
+      goto loop;
+    }
+  } else {
+    /* for "safe prime" generation, check that (p-1)/2 is prime. Since a prime
+     * is odd, We just need to divide by 2 */
+    if (!BN_rshift1(t, ret)) {
+      goto err;
+    }
+
+    for (i = 0; i < checks; i++) {
+      j = BN_is_prime_fasttest_ex(ret, 1, ctx, 0, NULL);
+      if (j == -1) {
+        goto err;
+      } else if (j == 0) {
+        goto loop;
+      }
+
+      j = BN_is_prime_fasttest_ex(t, 1, ctx, 0, NULL);
+      if (j == -1) {
+        goto err;
+      } else if (j == 0) {
+        goto loop;
+      }
+
+      if (!BN_GENCB_call(cb, i, c1 - 1)) {
+        goto err;
+      }
+      /* We have a safe prime test pass */
+    }
+  }
+
+  /* we have a prime :-) */
+  found = 1;
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+
+  return found;
+}
+
+int BN_primality_test(int *is_probably_prime, const BIGNUM *candidate,
+                      int checks, BN_CTX *ctx, int do_trial_division,
+                      BN_GENCB *cb) {
+  switch (BN_is_prime_fasttest_ex(candidate, checks, ctx, do_trial_division, cb)) {
+    case 1:
+      *is_probably_prime = 1;
+      return 1;
+    case 0:
+      *is_probably_prime = 0;
+      return 1;
+    default:
+      *is_probably_prime = 0;
+      return 0;
+  }
+}
+
+int BN_is_prime_ex(const BIGNUM *candidate, int checks, BN_CTX *ctx, BN_GENCB *cb) {
+  return BN_is_prime_fasttest_ex(candidate, checks, ctx, 0, cb);
+}
+
+int BN_is_prime_fasttest_ex(const BIGNUM *a, int checks, BN_CTX *ctx_passed,
+                            int do_trial_division, BN_GENCB *cb) {
+  int i, j, ret = -1;
+  int k;
+  BN_CTX *ctx = NULL;
+  BIGNUM *A1, *A1_odd, *check; /* taken from ctx */
+  BN_MONT_CTX *mont = NULL;
+  const BIGNUM *A = NULL;
+
+  if (BN_cmp(a, BN_value_one()) <= 0) {
+    return 0;
+  }
+
+  if (checks == BN_prime_checks) {
+    checks = BN_prime_checks_for_size(BN_num_bits(a));
+  }
+
+  /* first look for small factors */
+  if (!BN_is_odd(a)) {
+    /* a is even => a is prime if and only if a == 2 */
+    return BN_is_word(a, 2);
+  }
+
+  if (do_trial_division) {
+    for (i = 1; i < NUMPRIMES; i++) {
+      if (BN_mod_word(a, primes[i]) == 0) {
+        return 0;
+      }
+    }
+
+    if (!BN_GENCB_call(cb, 1, -1)) {
+      goto err;
+    }
+  }
+
+  if (ctx_passed != NULL) {
+    ctx = ctx_passed;
+  } else if ((ctx = BN_CTX_new()) == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+
+  /* A := abs(a) */
+  if (a->neg) {
+    BIGNUM *t;
+    if ((t = BN_CTX_get(ctx)) == NULL) {
+      goto err;
+    }
+    BN_copy(t, a);
+    t->neg = 0;
+    A = t;
+  } else {
+    A = a;
+  }
+
+  A1 = BN_CTX_get(ctx);
+  A1_odd = BN_CTX_get(ctx);
+  check = BN_CTX_get(ctx);
+  if (check == NULL) {
+    goto err;
+  }
+
+  /* compute A1 := A - 1 */
+  if (!BN_copy(A1, A)) {
+    goto err;
+  }
+  if (!BN_sub_word(A1, 1)) {
+    goto err;
+  }
+  if (BN_is_zero(A1)) {
+    ret = 0;
+    goto err;
+  }
+
+  /* write  A1  as  A1_odd * 2^k */
+  k = 1;
+  while (!BN_is_bit_set(A1, k)) {
+    k++;
+  }
+  if (!BN_rshift(A1_odd, A1, k)) {
+    goto err;
+  }
+
+  /* Montgomery setup for computations mod A */
+  mont = BN_MONT_CTX_new();
+  if (mont == NULL) {
+    goto err;
+  }
+  if (!BN_MONT_CTX_set(mont, A, ctx)) {
+    goto err;
+  }
+
+  for (i = 0; i < checks; i++) {
+    if (!BN_pseudo_rand_range(check, A1)) {
+      goto err;
+    }
+    if (!BN_add_word(check, 1)) {
+      goto err;
+    }
+    /* now 1 <= check < A */
+
+    j = witness(check, A, A1, A1_odd, k, ctx, mont);
+    if (j == -1) {
+      goto err;
+    }
+    if (j) {
+      ret = 0;
+      goto err;
+    }
+    if (!BN_GENCB_call(cb, 1, i)) {
+      goto err;
+    }
+  }
+  ret = 1;
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    if (ctx_passed == NULL) {
+      BN_CTX_free(ctx);
+    }
+  }
+  if (mont != NULL) {
+    BN_MONT_CTX_free(mont);
+  }
+
+  return ret;
+}
+
+static int witness(BIGNUM *w, const BIGNUM *a, const BIGNUM *a1,
+                   const BIGNUM *a1_odd, int k, BN_CTX *ctx,
+                   BN_MONT_CTX *mont) {
+  if (!BN_mod_exp_mont(w, w, a1_odd, a, ctx, mont)) { /* w := w^a1_odd mod a */
+    return -1;
+  }
+  if (BN_is_one(w)) {
+    return 0; /* probably prime */
+  }
+  if (BN_cmp(w, a1) == 0) {
+    return 0; /* w == -1 (mod a),  'a' is probably prime */
+  }
+
+  while (--k) {
+    if (!BN_mod_mul(w, w, w, a, ctx)) { /* w := w^2 mod a */
+      return -1;
+    }
+
+    if (BN_is_one(w)) {
+      return 1; /* 'a' is composite, otherwise a previous 'w' would
+                 * have been == -1 (mod 'a') */
+    }
+
+    if (BN_cmp(w, a1) == 0) {
+      return 0; /* w == -1 (mod a), 'a' is probably prime */
+    }
+  }
+
+  /* If we get here, 'w' is the (a-1)/2-th power of the original 'w',
+   * and it is neither -1 nor +1 -- so 'a' cannot be prime */
+  return 1;
+}
+
+static int probable_prime(BIGNUM *rnd, int bits) {
+  int i;
+  uint16_t mods[NUMPRIMES];
+  BN_ULONG delta, maxdelta;
+
+again:
+  if (!BN_rand(rnd, bits, 1, 1)) {
+    return 0;
+  }
+
+  /* we now have a random number 'rand' to test. */
+  for (i = 1; i < NUMPRIMES; i++) {
+    mods[i] = (uint16_t)BN_mod_word(rnd, (BN_ULONG)primes[i]);
+  }
+  maxdelta = BN_MASK2 - primes[NUMPRIMES - 1];
+  delta = 0;
+
+loop:
+  for (i = 1; i < NUMPRIMES; i++) {
+    /* check that rnd is not a prime and also
+     * that gcd(rnd-1,primes) == 1 (except for 2) */
+    if (((mods[i] + delta) % primes[i]) <= 1) {
+      delta += 2;
+      if (delta > maxdelta) {
+        goto again;
+      }
+      goto loop;
+    }
+  }
+
+  if (!BN_add_word(rnd, delta)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int probable_prime_dh(BIGNUM *rnd, int bits, const BIGNUM *add,
+                             const BIGNUM *rem, BN_CTX *ctx) {
+  int i, ret = 0;
+  BIGNUM *t1;
+
+  BN_CTX_start(ctx);
+  if ((t1 = BN_CTX_get(ctx)) == NULL) {
+    goto err;
+  }
+
+  if (!BN_rand(rnd, bits, 0, 1)) {
+    goto err;
+  }
+
+  /* we need ((rnd-rem) % add) == 0 */
+
+  if (!BN_mod(t1, rnd, add, ctx)) {
+    goto err;
+  }
+  if (!BN_sub(rnd, rnd, t1)) {
+    goto err;
+  }
+  if (rem == NULL) {
+    if (!BN_add_word(rnd, 1)) {
+      goto err;
+    }
+  } else {
+    if (!BN_add(rnd, rnd, rem)) {
+      goto err;
+    }
+  }
+  /* we now have a random number 'rand' to test. */
+
+loop:
+  for (i = 1; i < NUMPRIMES; i++) {
+    /* check that rnd is a prime */
+    if (BN_mod_word(rnd, (BN_ULONG)primes[i]) <= 1) {
+      if (!BN_add(rnd, rnd, add)) {
+        goto err;
+      }
+      goto loop;
+    }
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+static int probable_prime_dh_safe(BIGNUM *p, int bits, const BIGNUM *padd,
+                                  const BIGNUM *rem, BN_CTX *ctx) {
+  int i, ret = 0;
+  BIGNUM *t1, *qadd, *q;
+
+  bits--;
+  BN_CTX_start(ctx);
+  t1 = BN_CTX_get(ctx);
+  q = BN_CTX_get(ctx);
+  qadd = BN_CTX_get(ctx);
+  if (qadd == NULL) {
+    goto err;
+  }
+
+  if (!BN_rshift1(qadd, padd)) {
+    goto err;
+  }
+
+  if (!BN_rand(q, bits, 0, 1)) {
+    goto err;
+  }
+
+  /* we need ((rnd-rem) % add) == 0 */
+  if (!BN_mod(t1, q, qadd, ctx)) {
+    goto err;
+  }
+
+  if (!BN_sub(q, q, t1)) {
+    goto err;
+  }
+
+  if (rem == NULL) {
+    if (!BN_add_word(q, 1)) {
+      goto err;
+    }
+  } else {
+    if (!BN_rshift1(t1, rem)) {
+      goto err;
+    }
+    if (!BN_add(q, q, t1)) {
+      goto err;
+    }
+  }
+
+  /* we now have a random number 'rand' to test. */
+  if (!BN_lshift1(p, q)) {
+    goto err;
+  }
+  if (!BN_add_word(p, 1)) {
+    goto err;
+  }
+
+loop:
+  for (i = 1; i < NUMPRIMES; i++) {
+    /* check that p and q are prime */
+    /* check that for p and q
+     * gcd(p-1,primes) == 1 (except for 2) */
+    if ((BN_mod_word(p, (BN_ULONG)primes[i]) == 0) ||
+        (BN_mod_word(q, (BN_ULONG)primes[i]) == 0)) {
+      if (!BN_add(p, p, padd)) {
+        goto err;
+      }
+      if (!BN_add(q, q, qadd)) {
+        goto err;
+      }
+      goto loop;
+    }
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+static const uint16_t primes[NUMPRIMES] = {
+    2,     3,     5,     7,     11,    13,    17,    19,    23,    29,    31,
+    37,    41,    43,    47,    53,    59,    61,    67,    71,    73,    79,
+    83,    89,    97,    101,   103,   107,   109,   113,   127,   131,   137,
+    139,   149,   151,   157,   163,   167,   173,   179,   181,   191,   193,
+    197,   199,   211,   223,   227,   229,   233,   239,   241,   251,   257,
+    263,   269,   271,   277,   281,   283,   293,   307,   311,   313,   317,
+    331,   337,   347,   349,   353,   359,   367,   373,   379,   383,   389,
+    397,   401,   409,   419,   421,   431,   433,   439,   443,   449,   457,
+    461,   463,   467,   479,   487,   491,   499,   503,   509,   521,   523,
+    541,   547,   557,   563,   569,   571,   577,   587,   593,   599,   601,
+    607,   613,   617,   619,   631,   641,   643,   647,   653,   659,   661,
+    673,   677,   683,   691,   701,   709,   719,   727,   733,   739,   743,
+    751,   757,   761,   769,   773,   787,   797,   809,   811,   821,   823,
+    827,   829,   839,   853,   857,   859,   863,   877,   881,   883,   887,
+    907,   911,   919,   929,   937,   941,   947,   953,   967,   971,   977,
+    983,   991,   997,   1009,  1013,  1019,  1021,  1031,  1033,  1039,  1049,
+    1051,  1061,  1063,  1069,  1087,  1091,  1093,  1097,  1103,  1109,  1117,
+    1123,  1129,  1151,  1153,  1163,  1171,  1181,  1187,  1193,  1201,  1213,
+    1217,  1223,  1229,  1231,  1237,  1249,  1259,  1277,  1279,  1283,  1289,
+    1291,  1297,  1301,  1303,  1307,  1319,  1321,  1327,  1361,  1367,  1373,
+    1381,  1399,  1409,  1423,  1427,  1429,  1433,  1439,  1447,  1451,  1453,
+    1459,  1471,  1481,  1483,  1487,  1489,  1493,  1499,  1511,  1523,  1531,
+    1543,  1549,  1553,  1559,  1567,  1571,  1579,  1583,  1597,  1601,  1607,
+    1609,  1613,  1619,  1621,  1627,  1637,  1657,  1663,  1667,  1669,  1693,
+    1697,  1699,  1709,  1721,  1723,  1733,  1741,  1747,  1753,  1759,  1777,
+    1783,  1787,  1789,  1801,  1811,  1823,  1831,  1847,  1861,  1867,  1871,
+    1873,  1877,  1879,  1889,  1901,  1907,  1913,  1931,  1933,  1949,  1951,
+    1973,  1979,  1987,  1993,  1997,  1999,  2003,  2011,  2017,  2027,  2029,
+    2039,  2053,  2063,  2069,  2081,  2083,  2087,  2089,  2099,  2111,  2113,
+    2129,  2131,  2137,  2141,  2143,  2153,  2161,  2179,  2203,  2207,  2213,
+    2221,  2237,  2239,  2243,  2251,  2267,  2269,  2273,  2281,  2287,  2293,
+    2297,  2309,  2311,  2333,  2339,  2341,  2347,  2351,  2357,  2371,  2377,
+    2381,  2383,  2389,  2393,  2399,  2411,  2417,  2423,  2437,  2441,  2447,
+    2459,  2467,  2473,  2477,  2503,  2521,  2531,  2539,  2543,  2549,  2551,
+    2557,  2579,  2591,  2593,  2609,  2617,  2621,  2633,  2647,  2657,  2659,
+    2663,  2671,  2677,  2683,  2687,  2689,  2693,  2699,  2707,  2711,  2713,
+    2719,  2729,  2731,  2741,  2749,  2753,  2767,  2777,  2789,  2791,  2797,
+    2801,  2803,  2819,  2833,  2837,  2843,  2851,  2857,  2861,  2879,  2887,
+    2897,  2903,  2909,  2917,  2927,  2939,  2953,  2957,  2963,  2969,  2971,
+    2999,  3001,  3011,  3019,  3023,  3037,  3041,  3049,  3061,  3067,  3079,
+    3083,  3089,  3109,  3119,  3121,  3137,  3163,  3167,  3169,  3181,  3187,
+    3191,  3203,  3209,  3217,  3221,  3229,  3251,  3253,  3257,  3259,  3271,
+    3299,  3301,  3307,  3313,  3319,  3323,  3329,  3331,  3343,  3347,  3359,
+    3361,  3371,  3373,  3389,  3391,  3407,  3413,  3433,  3449,  3457,  3461,
+    3463,  3467,  3469,  3491,  3499,  3511,  3517,  3527,  3529,  3533,  3539,
+    3541,  3547,  3557,  3559,  3571,  3581,  3583,  3593,  3607,  3613,  3617,
+    3623,  3631,  3637,  3643,  3659,  3671,  3673,  3677,  3691,  3697,  3701,
+    3709,  3719,  3727,  3733,  3739,  3761,  3767,  3769,  3779,  3793,  3797,
+    3803,  3821,  3823,  3833,  3847,  3851,  3853,  3863,  3877,  3881,  3889,
+    3907,  3911,  3917,  3919,  3923,  3929,  3931,  3943,  3947,  3967,  3989,
+    4001,  4003,  4007,  4013,  4019,  4021,  4027,  4049,  4051,  4057,  4073,
+    4079,  4091,  4093,  4099,  4111,  4127,  4129,  4133,  4139,  4153,  4157,
+    4159,  4177,  4201,  4211,  4217,  4219,  4229,  4231,  4241,  4243,  4253,
+    4259,  4261,  4271,  4273,  4283,  4289,  4297,  4327,  4337,  4339,  4349,
+    4357,  4363,  4373,  4391,  4397,  4409,  4421,  4423,  4441,  4447,  4451,
+    4457,  4463,  4481,  4483,  4493,  4507,  4513,  4517,  4519,  4523,  4547,
+    4549,  4561,  4567,  4583,  4591,  4597,  4603,  4621,  4637,  4639,  4643,
+    4649,  4651,  4657,  4663,  4673,  4679,  4691,  4703,  4721,  4723,  4729,
+    4733,  4751,  4759,  4783,  4787,  4789,  4793,  4799,  4801,  4813,  4817,
+    4831,  4861,  4871,  4877,  4889,  4903,  4909,  4919,  4931,  4933,  4937,
+    4943,  4951,  4957,  4967,  4969,  4973,  4987,  4993,  4999,  5003,  5009,
+    5011,  5021,  5023,  5039,  5051,  5059,  5077,  5081,  5087,  5099,  5101,
+    5107,  5113,  5119,  5147,  5153,  5167,  5171,  5179,  5189,  5197,  5209,
+    5227,  5231,  5233,  5237,  5261,  5273,  5279,  5281,  5297,  5303,  5309,
+    5323,  5333,  5347,  5351,  5381,  5387,  5393,  5399,  5407,  5413,  5417,
+    5419,  5431,  5437,  5441,  5443,  5449,  5471,  5477,  5479,  5483,  5501,
+    5503,  5507,  5519,  5521,  5527,  5531,  5557,  5563,  5569,  5573,  5581,
+    5591,  5623,  5639,  5641,  5647,  5651,  5653,  5657,  5659,  5669,  5683,
+    5689,  5693,  5701,  5711,  5717,  5737,  5741,  5743,  5749,  5779,  5783,
+    5791,  5801,  5807,  5813,  5821,  5827,  5839,  5843,  5849,  5851,  5857,
+    5861,  5867,  5869,  5879,  5881,  5897,  5903,  5923,  5927,  5939,  5953,
+    5981,  5987,  6007,  6011,  6029,  6037,  6043,  6047,  6053,  6067,  6073,
+    6079,  6089,  6091,  6101,  6113,  6121,  6131,  6133,  6143,  6151,  6163,
+    6173,  6197,  6199,  6203,  6211,  6217,  6221,  6229,  6247,  6257,  6263,
+    6269,  6271,  6277,  6287,  6299,  6301,  6311,  6317,  6323,  6329,  6337,
+    6343,  6353,  6359,  6361,  6367,  6373,  6379,  6389,  6397,  6421,  6427,
+    6449,  6451,  6469,  6473,  6481,  6491,  6521,  6529,  6547,  6551,  6553,
+    6563,  6569,  6571,  6577,  6581,  6599,  6607,  6619,  6637,  6653,  6659,
+    6661,  6673,  6679,  6689,  6691,  6701,  6703,  6709,  6719,  6733,  6737,
+    6761,  6763,  6779,  6781,  6791,  6793,  6803,  6823,  6827,  6829,  6833,
+    6841,  6857,  6863,  6869,  6871,  6883,  6899,  6907,  6911,  6917,  6947,
+    6949,  6959,  6961,  6967,  6971,  6977,  6983,  6991,  6997,  7001,  7013,
+    7019,  7027,  7039,  7043,  7057,  7069,  7079,  7103,  7109,  7121,  7127,
+    7129,  7151,  7159,  7177,  7187,  7193,  7207,  7211,  7213,  7219,  7229,
+    7237,  7243,  7247,  7253,  7283,  7297,  7307,  7309,  7321,  7331,  7333,
+    7349,  7351,  7369,  7393,  7411,  7417,  7433,  7451,  7457,  7459,  7477,
+    7481,  7487,  7489,  7499,  7507,  7517,  7523,  7529,  7537,  7541,  7547,
+    7549,  7559,  7561,  7573,  7577,  7583,  7589,  7591,  7603,  7607,  7621,
+    7639,  7643,  7649,  7669,  7673,  7681,  7687,  7691,  7699,  7703,  7717,
+    7723,  7727,  7741,  7753,  7757,  7759,  7789,  7793,  7817,  7823,  7829,
+    7841,  7853,  7867,  7873,  7877,  7879,  7883,  7901,  7907,  7919,  7927,
+    7933,  7937,  7949,  7951,  7963,  7993,  8009,  8011,  8017,  8039,  8053,
+    8059,  8069,  8081,  8087,  8089,  8093,  8101,  8111,  8117,  8123,  8147,
+    8161,  8167,  8171,  8179,  8191,  8209,  8219,  8221,  8231,  8233,  8237,
+    8243,  8263,  8269,  8273,  8287,  8291,  8293,  8297,  8311,  8317,  8329,
+    8353,  8363,  8369,  8377,  8387,  8389,  8419,  8423,  8429,  8431,  8443,
+    8447,  8461,  8467,  8501,  8513,  8521,  8527,  8537,  8539,  8543,  8563,
+    8573,  8581,  8597,  8599,  8609,  8623,  8627,  8629,  8641,  8647,  8663,
+    8669,  8677,  8681,  8689,  8693,  8699,  8707,  8713,  8719,  8731,  8737,
+    8741,  8747,  8753,  8761,  8779,  8783,  8803,  8807,  8819,  8821,  8831,
+    8837,  8839,  8849,  8861,  8863,  8867,  8887,  8893,  8923,  8929,  8933,
+    8941,  8951,  8963,  8969,  8971,  8999,  9001,  9007,  9011,  9013,  9029,
+    9041,  9043,  9049,  9059,  9067,  9091,  9103,  9109,  9127,  9133,  9137,
+    9151,  9157,  9161,  9173,  9181,  9187,  9199,  9203,  9209,  9221,  9227,
+    9239,  9241,  9257,  9277,  9281,  9283,  9293,  9311,  9319,  9323,  9337,
+    9341,  9343,  9349,  9371,  9377,  9391,  9397,  9403,  9413,  9419,  9421,
+    9431,  9433,  9437,  9439,  9461,  9463,  9467,  9473,  9479,  9491,  9497,
+    9511,  9521,  9533,  9539,  9547,  9551,  9587,  9601,  9613,  9619,  9623,
+    9629,  9631,  9643,  9649,  9661,  9677,  9679,  9689,  9697,  9719,  9721,
+    9733,  9739,  9743,  9749,  9767,  9769,  9781,  9787,  9791,  9803,  9811,
+    9817,  9829,  9833,  9839,  9851,  9857,  9859,  9871,  9883,  9887,  9901,
+    9907,  9923,  9929,  9931,  9941,  9949,  9967,  9973,  10007, 10009, 10037,
+    10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133,
+    10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223,
+    10243, 10247, 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313,
+    10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429,
+    10433, 10453, 10457, 10459, 10463, 10477, 10487, 10499, 10501, 10513, 10529,
+    10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639,
+    10651, 10657, 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733,
+    10739, 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859,
+    10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957,
+    10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069, 11071,
+    11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171,
+    11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279,
+    11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393,
+    11399, 11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491,
+    11497, 11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617,
+    11621, 11633, 11657, 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731,
+    11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831,
+    11833, 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933,
+    11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 12037,
+    12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119,
+    12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241,
+    12251, 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343,
+    12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437,
+    12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527,
+    12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, 12613,
+    12619, 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713,
+    12721, 12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823,
+    12829, 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923,
+    12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009,
+    13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127,
+    13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229,
+    13241, 13249, 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337,
+    13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457,
+    13463, 13469, 13477, 13487, 13499, 13513, 13523, 13537, 13553, 13567, 13577,
+    13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687,
+    13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759,
+    13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877,
+    13879, 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967,
+    13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083,
+    14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221,
+    14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 14327, 14341, 14347,
+    14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447,
+    14449, 14461, 14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 14551,
+    14557, 14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653,
+    14657, 14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747,
+    14753, 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831,
+    14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939,
+    14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073,
+    15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161,
+    15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269,
+    15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349,
+    15359, 15361, 15373, 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443,
+    15451, 15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559,
+    15569, 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 15647, 15649,
+    15661, 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749,
+    15761, 15767, 15773, 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859,
+    15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959,
+    15971, 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069,
+    16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187,
+    16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301,
+    16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, 16421,
+    16427, 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529,
+    16547, 16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649,
+    16651, 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747,
+    16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843, 16871, 16879, 16883,
+    16889, 16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981,
+    16987, 16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077,
+    17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191,
+    17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321,
+    17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 17393, 17401,
+    17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491,
+    17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599,
+    17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, 17729,
+    17737, 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839,
+    17851, 17863, };
diff --git a/crypto/bn/random.c b/crypto/bn/random.c
new file mode 100644
index 0000000..1f1d732
--- /dev/null
+++ b/crypto/bn/random.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/rand.h>
+
+int BN_rand(BIGNUM *rnd, int bits, int top, int bottom) {
+  uint8_t *buf = NULL;
+  int ret = 0, bit, bytes, mask;
+
+  if (bits == 0) {
+    BN_zero(rnd);
+    return 1;
+  }
+
+  bytes = (bits + 7) / 8;
+  bit = (bits - 1) % 8;
+  mask = 0xff << (bit + 1);
+
+  buf = OPENSSL_malloc(bytes);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(BN, BN_rand, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  /* make a random number and set the top and bottom bits */
+  if (RAND_pseudo_bytes(buf, bytes) <= 0)
+    goto err;
+
+  if (top != -1) {
+    if (top) {
+      if (bit == 0) {
+        buf[0] = 1;
+        buf[1] |= 0x80;
+      } else {
+        buf[0] |= (3 << (bit - 1));
+      }
+    } else {
+      buf[0] |= (1 << bit);
+    }
+  }
+
+  buf[0] &= ~mask;
+
+  /* set bottom bit if requested */
+  if (bottom)  {
+    buf[bytes - 1] |= 1;
+  }
+
+  if (!BN_bin2bn(buf, bytes, rnd)) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  if (buf != NULL) {
+    OPENSSL_cleanse(buf, bytes);
+    OPENSSL_free(buf);
+  }
+  return (ret);
+}
+
+int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom) {
+  return BN_rand(rnd, bits, top, bottom);
+}
+
+int BN_rand_range(BIGNUM *r, const BIGNUM *range) {
+  unsigned n;
+  unsigned count = 100;
+
+  if (range->neg || BN_is_zero(range)) {
+    OPENSSL_PUT_ERROR(BN, BN_rand_range, BN_R_INVALID_RANGE);
+    return 0;
+  }
+
+  n = BN_num_bits(range); /* n > 0 */
+
+  /* BN_is_bit_set(range, n - 1) always holds */
+  if (n == 1) {
+    BN_zero(r);
+  } else if (!BN_is_bit_set(range, n - 2) && !BN_is_bit_set(range, n - 3)) {
+    /* range = 100..._2,
+     * so  3*range (= 11..._2)  is exactly one bit longer than  range */
+    do {
+      if (!BN_rand(r, n + 1, -1 /* don't set most significant bits */,
+                   0 /* don't set least significant bits */)) {
+        return 0;
+      }
+
+      /* If r < 3*range, use r := r MOD range (which is either r, r - range, or
+       * r - 2*range). Otherwise, iterate again. Since 3*range = 11..._2, each
+       * iteration succeeds with probability >= .75. */
+      if (BN_cmp(r, range) >= 0) {
+        if (!BN_sub(r, r, range)) {
+          return 0;
+        }
+        if (BN_cmp(r, range) >= 0) {
+          if (!BN_sub(r, r, range)) {
+            return 0;
+          }
+        }
+      }
+
+      if (!--count) {
+        OPENSSL_PUT_ERROR(BN, BN_rand_range, BN_R_TOO_MANY_ITERATIONS);
+        return 0;
+      }
+    } while (BN_cmp(r, range) >= 0);
+  } else {
+    do {
+      /* range = 11..._2  or  range = 101..._2 */
+      if (!BN_rand(r, n, -1, 0)) {
+        return 0;
+      }
+
+      if (!--count) {
+        OPENSSL_PUT_ERROR(BN, BN_rand_range, BN_R_TOO_MANY_ITERATIONS);
+        return 0;
+      }
+    } while (BN_cmp(r, range) >= 0);
+  }
+
+  return 1;
+}
+
+int BN_pseudo_rand_range(BIGNUM *r, const BIGNUM *range) {
+  return BN_rand_range(r, range);
+}
diff --git a/crypto/bn/rsaz_exp.c b/crypto/bn/rsaz_exp.c
new file mode 100644
index 0000000..43bb351
--- /dev/null
+++ b/crypto/bn/rsaz_exp.c
@@ -0,0 +1,320 @@
+/*****************************************************************************
+*                                                                            *
+*  Copyright (c) 2012, Intel Corporation                                     *
+*                                                                            *
+*  All rights reserved.                                                      *
+*                                                                            *
+*  Redistribution and use in source and binary forms, with or without        *
+*  modification, are permitted provided that the following conditions are    *
+*  met:                                                                      *
+*                                                                            *
+*  *  Redistributions of source code must retain the above copyright         *
+*     notice, this list of conditions and the following disclaimer.          *
+*                                                                            *
+*  *  Redistributions in binary form must reproduce the above copyright      *
+*     notice, this list of conditions and the following disclaimer in the    *
+*     documentation and/or other materials provided with the                 *
+*     distribution.                                                          *
+*                                                                            *
+*  *  Neither the name of the Intel Corporation nor the names of its         *
+*     contributors may be used to endorse or promote products derived from   *
+*     this software without specific prior written permission.               *
+*                                                                            *
+*                                                                            *
+*  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          *
+*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         *
+*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        *
+*  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            *
+*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     *
+*  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
+*  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
+*  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
+*  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
+*  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
+*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
+*                                                                            *
+******************************************************************************
+* Developers and authors:                                                    *
+* Shay Gueron (1, 2), and Vlad Krasnov (1)                                   *
+* (1) Intel Corporation, Israel Development Center, Haifa, Israel            *
+* (2) University of Haifa, Israel                                            *
+*****************************************************************************/
+
+#include "rsaz_exp.h"
+
+#include <openssl/mem.h>
+
+/*
+ * See crypto/bn/asm/rsaz-avx2.pl for further details.
+ */
+void rsaz_1024_norm2red_avx2(void *red,const void *norm);
+void rsaz_1024_mul_avx2(void *ret,const void *a,const void *b,const void *n,BN_ULONG k);
+void rsaz_1024_sqr_avx2(void *ret,const void *a,const void *n,BN_ULONG k,int cnt);
+void rsaz_1024_scatter5_avx2(void *tbl,const void *val,int i);
+void rsaz_1024_gather5_avx2(void *val,const void *tbl,int i);
+void rsaz_1024_red2norm_avx2(void *norm,const void *red);
+
+#if defined(__GNUC__)
+# define ALIGN64	__attribute__((aligned(64)))
+#elif defined(_MSC_VER)
+# define ALIGN64	__declspec(align(64))
+#elif defined(__SUNPRO_C)
+# define ALIGN64
+# pragma align 64(one,two80)
+#else
+# define ALIGN64	/* not fatal, might hurt performance a little */
+#endif
+
+ALIGN64 static const BN_ULONG one[40] =
+	{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,0,0,0,0,0,0,0,0};
+ALIGN64 static const BN_ULONG two80[40] =
+	{0,0,1<<22,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,0,0,0,0,0,0};
+
+void RSAZ_1024_mod_exp_avx2(BN_ULONG result_norm[16],
+	const BN_ULONG base_norm[16], const BN_ULONG exponent[16],
+	const BN_ULONG m_norm[16], const BN_ULONG RR[16], BN_ULONG k0)
+{
+	unsigned char	 storage[320*3+32*9*16+64];	/* 5.5KB */
+	unsigned char	*p_str = storage + (64-((size_t)storage%64));
+	unsigned char	*a_inv, *m, *result,
+			*table_s = p_str+320*3,
+			*R2      = table_s;	/* borrow */
+	int index;
+	int wvalue;
+
+	if ((((size_t)p_str&4095)+320)>>12) {
+		result = p_str;
+		a_inv = p_str + 320;
+		m = p_str + 320*2;	/* should not cross page */
+	} else {
+		m = p_str;		/* should not cross page */
+		result = p_str + 320;
+		a_inv = p_str + 320*2;
+	}
+
+	rsaz_1024_norm2red_avx2(m, m_norm);
+	rsaz_1024_norm2red_avx2(a_inv, base_norm);
+	rsaz_1024_norm2red_avx2(R2, RR);
+
+	rsaz_1024_mul_avx2(R2, R2, R2, m, k0);
+	rsaz_1024_mul_avx2(R2, R2, two80, m, k0);
+
+	/* table[0] = 1 */
+	rsaz_1024_mul_avx2(result, R2, one, m, k0);
+	/* table[1] = a_inv^1 */
+	rsaz_1024_mul_avx2(a_inv, a_inv, R2, m, k0);
+
+	rsaz_1024_scatter5_avx2(table_s,result,0);
+	rsaz_1024_scatter5_avx2(table_s,a_inv,1);
+
+	/* table[2] = a_inv^2 */
+	rsaz_1024_sqr_avx2(result, a_inv, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,2);
+#if 0
+	/* this is almost 2x smaller and less than 1% slower */
+	for (index=3; index<32; index++) {
+		rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+		rsaz_1024_scatter5_avx2(table_s,result,index);
+	}
+#else
+	/* table[4] = a_inv^4 */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,4);
+	/* table[8] = a_inv^8 */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,8);
+	/* table[16] = a_inv^16 */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,16);
+	/* table[17] = a_inv^17 */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,17);
+
+	/* table[3] */
+	rsaz_1024_gather5_avx2(result,table_s,2);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,3);
+	/* table[6] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,6);
+	/* table[12] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,12);
+ 	/* table[24] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,24);
+	/* table[25] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,25);
+
+	/* table[5] */
+	rsaz_1024_gather5_avx2(result,table_s,4);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,5);
+	/* table[10] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,10);
+	/* table[20] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,20);
+	/* table[21] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,21);
+
+	/* table[7] */
+	rsaz_1024_gather5_avx2(result,table_s,6);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,7);
+	/* table[14] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,14);
+	/* table[28] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,28);
+	/* table[29] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,29);
+
+	/* table[9] */
+	rsaz_1024_gather5_avx2(result,table_s,8);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,9);
+	/* table[18] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,18);
+	/* table[19] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,19);
+
+	/* table[11] */
+	rsaz_1024_gather5_avx2(result,table_s,10);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,11);
+	/* table[22] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,22);
+	/* table[23] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,23);
+
+	/* table[13] */
+	rsaz_1024_gather5_avx2(result,table_s,12);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,13);
+	/* table[26] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,26);
+	/* table[27] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,27);
+
+	/* table[15] */
+	rsaz_1024_gather5_avx2(result,table_s,14);
+	rsaz_1024_mul_avx2(result,result,a_inv,m,k0);
+	rsaz_1024_scatter5_avx2(table_s,result,15);
+	/* table[30] */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+	rsaz_1024_scatter5_avx2(table_s,result,30);
+	/* table[31] */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	rsaz_1024_scatter5_avx2(table_s,result,31);
+#endif
+
+	/* load first window */
+	p_str = (unsigned char*)exponent;
+	wvalue = p_str[127] >> 3;
+	rsaz_1024_gather5_avx2(result,table_s,wvalue);
+
+	index = 1014;
+
+	while(index > -1) {	/* loop for the remaining 127 windows */
+
+		rsaz_1024_sqr_avx2(result, result, m, k0, 5);
+
+		wvalue = *((unsigned short*)&p_str[index/8]);
+		wvalue = (wvalue>> (index%8)) & 31;
+		index-=5;
+
+		rsaz_1024_gather5_avx2(a_inv,table_s,wvalue);	/* borrow a_inv */
+		rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+	}
+
+	/* square four times */
+	rsaz_1024_sqr_avx2(result, result, m, k0, 4);
+
+	wvalue = p_str[0] & 15;
+
+	rsaz_1024_gather5_avx2(a_inv,table_s,wvalue);	/* borrow a_inv */
+	rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+
+	/* from Montgomery */
+	rsaz_1024_mul_avx2(result, result, one, m, k0);
+
+	rsaz_1024_red2norm_avx2(result_norm, result);
+
+	OPENSSL_cleanse(storage,sizeof(storage));
+}
+
+/*
+ * See crypto/bn/rsaz-x86_64.pl for further details.
+ */
+void rsaz_512_mul(void *ret,const void *a,const void *b,const void *n,BN_ULONG k);
+void rsaz_512_mul_scatter4(void *ret,const void *a,const void *n,BN_ULONG k,const void *tbl,unsigned int power);
+void rsaz_512_mul_gather4(void *ret,const void *a,const void *tbl,const void *n,BN_ULONG k,unsigned int power);
+void rsaz_512_mul_by_one(void *ret,const void *a,const void *n,BN_ULONG k);
+void rsaz_512_sqr(void *ret,const void *a,const void *n,BN_ULONG k,int cnt);
+void rsaz_512_scatter4(void *tbl, const BN_ULONG *val, int power);
+void rsaz_512_gather4(BN_ULONG *val, const void *tbl, int power);
+
+void RSAZ_512_mod_exp(BN_ULONG result[8],
+	const BN_ULONG base[8], const BN_ULONG exponent[8],
+	const BN_ULONG m[8], BN_ULONG k0, const BN_ULONG RR[8])
+{
+	unsigned char	 storage[16*8*8+64*2+64];	/* 1.2KB */
+	unsigned char	*table = storage + (64-((size_t)storage%64));
+	BN_ULONG	*a_inv = (BN_ULONG *)(table+16*8*8),
+			*temp  = (BN_ULONG *)(table+16*8*8+8*8);
+	unsigned char	*p_str = (unsigned char*)exponent;
+	int index;
+	unsigned int wvalue;
+
+	/* table[0] = 1_inv */
+	temp[0] = 0-m[0];	temp[1] = ~m[1];
+	temp[2] = ~m[2];	temp[3] = ~m[3];
+	temp[4] = ~m[4];	temp[5] = ~m[5];
+	temp[6] = ~m[6];	temp[7] = ~m[7];
+	rsaz_512_scatter4(table, temp, 0);
+
+	/* table [1] = a_inv^1 */
+	rsaz_512_mul(a_inv, base, RR, m, k0);
+	rsaz_512_scatter4(table, a_inv, 1);
+
+	/* table [2] = a_inv^2 */
+	rsaz_512_sqr(temp, a_inv, m, k0, 1);
+	rsaz_512_scatter4(table, temp, 2);
+
+	for (index=3; index<16; index++)
+		rsaz_512_mul_scatter4(temp, a_inv, m, k0, table, index);
+
+	/* load first window */
+	wvalue = p_str[63];
+
+	rsaz_512_gather4(temp, table, wvalue>>4);
+	rsaz_512_sqr(temp, temp, m, k0, 4);
+	rsaz_512_mul_gather4(temp, temp, table, m, k0, wvalue&0xf);
+
+	for (index=62; index>=0; index--) {
+		wvalue = p_str[index];
+
+		rsaz_512_sqr(temp, temp, m, k0, 4);
+		rsaz_512_mul_gather4(temp, temp, table, m, k0, wvalue>>4);
+
+		rsaz_512_sqr(temp, temp, m, k0, 4);
+		rsaz_512_mul_gather4(temp, temp, table, m, k0, wvalue&0x0f);
+	}
+
+	/* from Montgomery */
+	rsaz_512_mul_by_one(result, temp, m, k0);
+
+	OPENSSL_cleanse(storage,sizeof(storage));
+}
diff --git a/crypto/bn/rsaz_exp.h b/crypto/bn/rsaz_exp.h
new file mode 100644
index 0000000..4241a1f
--- /dev/null
+++ b/crypto/bn/rsaz_exp.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+* Copyright(c) 2012, Intel Corp.                                             
+* Developers and authors:                                                    
+* Shay Gueron (1, 2), and Vlad Krasnov (1)                                   
+* (1) Intel Corporation, Israel Development Center, Haifa, Israel                               
+* (2) University of Haifa, Israel                                              
+******************************************************************************
+* LICENSE:                                                                
+* This submission to OpenSSL is to be made available under the OpenSSL  
+* license, and only to the OpenSSL project, in order to allow integration    
+* into the publicly distributed code. 
+* The use of this code, or portions of this code, or concepts embedded in
+* this code, or modification of this code and/or algorithm(s) in it, or the
+* use of this code for any other purpose than stated above, requires special
+* licensing.                                                                  
+******************************************************************************
+* DISCLAIMER:                                                                
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS AND THE COPYRIGHT OWNERS     
+* ``AS IS''. ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
+* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS OR THE COPYRIGHT
+* OWNERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF    
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS   
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN    
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)    
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+* POSSIBILITY OF SUCH DAMAGE.                                                
+******************************************************************************/
+
+#ifndef RSAZ_EXP_H
+#define RSAZ_EXP_H
+
+#include <openssl/bn.h>
+
+void RSAZ_1024_mod_exp_avx2(BN_ULONG result[16],
+	const BN_ULONG base_norm[16], const BN_ULONG exponent[16],
+	const BN_ULONG m_norm[16], const BN_ULONG RR[16], BN_ULONG k0);
+int rsaz_avx2_eligible();
+
+void RSAZ_512_mod_exp(BN_ULONG result[8],
+	const BN_ULONG base_norm[8], const BN_ULONG exponent[8],
+	const BN_ULONG m_norm[8], BN_ULONG k0, const BN_ULONG RR[8]);
+#endif
diff --git a/crypto/bn/shift.c b/crypto/bn/shift.c
new file mode 100644
index 0000000..384a697
--- /dev/null
+++ b/crypto/bn/shift.c
@@ -0,0 +1,284 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bn.h>
+
+#include "internal.h"
+
+int BN_lshift(BIGNUM *r, const BIGNUM *a, int n) {
+  int i, nw, lb, rb;
+  BN_ULONG *t, *f;
+  BN_ULONG l;
+
+  r->neg = a->neg;
+  nw = n / BN_BITS2;
+  if (bn_wexpand(r, a->top + nw + 1) == NULL) {
+    return 0;
+  }
+  lb = n % BN_BITS2;
+  rb = BN_BITS2 - lb;
+  f = a->d;
+  t = r->d;
+  t[a->top + nw] = 0;
+  if (lb == 0) {
+    for (i = a->top - 1; i >= 0; i--) {
+      t[nw + i] = f[i];
+    }
+  } else {
+    for (i = a->top - 1; i >= 0; i--) {
+      l = f[i];
+      t[nw + i + 1] |= (l >> rb) & BN_MASK2;
+      t[nw + i] = (l << lb) & BN_MASK2;
+    }
+  }
+  memset(t, 0, nw * sizeof(t[0]));
+  r->top = a->top + nw + 1;
+  bn_correct_top(r);
+
+  return 1;
+}
+
+int BN_lshift1(BIGNUM *r, const BIGNUM *a) {
+  BN_ULONG *ap, *rp, t, c;
+  int i;
+
+  if (r != a) {
+    r->neg = a->neg;
+    if (bn_wexpand(r, a->top + 1) == NULL) {
+      return 0;
+    }
+    r->top = a->top;
+  } else {
+    if (bn_wexpand(r, a->top + 1) == NULL) {
+      return 0;
+    }
+  }
+  ap = a->d;
+  rp = r->d;
+  c = 0;
+  for (i = 0; i < a->top; i++) {
+    t = *(ap++);
+    *(rp++) = ((t << 1) | c) & BN_MASK2;
+    c = (t & BN_TBIT) ? 1 : 0;
+  }
+  if (c) {
+    *rp = 1;
+    r->top++;
+  }
+
+  return 1;
+}
+
+int BN_rshift(BIGNUM *r, const BIGNUM *a, int n) {
+  int i, j, nw, lb, rb;
+  BN_ULONG *t, *f;
+  BN_ULONG l, tmp;
+
+  nw = n / BN_BITS2;
+  rb = n % BN_BITS2;
+  lb = BN_BITS2 - rb;
+  if (nw >= a->top || a->top == 0) {
+    BN_zero(r);
+    return 1;
+  }
+  i = (BN_num_bits(a) - n + (BN_BITS2 - 1)) / BN_BITS2;
+  if (r != a) {
+    r->neg = a->neg;
+    if (bn_wexpand(r, i) == NULL) {
+      return 0;
+    }
+  } else {
+    if (n == 0) {
+      return 1; /* or the copying loop will go berserk */
+    }
+  }
+
+  f = &(a->d[nw]);
+  t = r->d;
+  j = a->top - nw;
+  r->top = i;
+
+  if (rb == 0) {
+    for (i = j; i != 0; i--) {
+      *(t++) = *(f++);
+    }
+  } else {
+    l = *(f++);
+    for (i = j - 1; i != 0; i--) {
+      tmp = (l >> rb) & BN_MASK2;
+      l = *(f++);
+      *(t++) = (tmp | (l << lb)) & BN_MASK2;
+    }
+    if ((l = (l >> rb) & BN_MASK2)) {
+      *(t) = l;
+    }
+  }
+
+  return 1;
+}
+
+int BN_rshift1(BIGNUM *r, const BIGNUM *a) {
+  BN_ULONG *ap, *rp, t, c;
+  int i, j;
+
+  if (BN_is_zero(a)) {
+    BN_zero(r);
+    return 1;
+  }
+  i = a->top;
+  ap = a->d;
+  j = i - (ap[i - 1] == 1);
+  if (a != r) {
+    if (bn_wexpand(r, j) == NULL) {
+      return 0;
+    }
+    r->neg = a->neg;
+  }
+  rp = r->d;
+  t = ap[--i];
+  c = (t & 1) ? BN_TBIT : 0;
+  if (t >>= 1) {
+    rp[i] = t;
+  }
+  while (i > 0) {
+    t = ap[--i];
+    rp[i] = ((t >> 1) & BN_MASK2) | c;
+    c = (t & 1) ? BN_TBIT : 0;
+  }
+  r->top = j;
+
+  return 1;
+}
+
+int BN_set_bit(BIGNUM *a, int n) {
+  int i, j, k;
+
+  if (n < 0) {
+    return 0;
+  }
+
+  i = n / BN_BITS2;
+  j = n % BN_BITS2;
+  if (a->top <= i) {
+    if (bn_wexpand(a, i + 1) == NULL) {
+      return 0;
+    }
+    for (k = a->top; k < i + 1; k++) {
+      a->d[k] = 0;
+    }
+    a->top = i + 1;
+  }
+
+  a->d[i] |= (((BN_ULONG)1) << j);
+
+  return 1;
+}
+
+int BN_clear_bit(BIGNUM *a, int n) {
+  int i, j;
+
+  if (n < 0) {
+    return 0;
+  }
+
+  i = n / BN_BITS2;
+  j = n % BN_BITS2;
+  if (a->top <= i) {
+    return 0;
+  }
+
+  a->d[i] &= (~(((BN_ULONG)1) << j));
+  bn_correct_top(a);
+  return 1;
+}
+
+int BN_is_bit_set(const BIGNUM *a, int n) {
+  int i, j;
+
+  if (n < 0) {
+    return 0;
+  }
+  i = n / BN_BITS2;
+  j = n % BN_BITS2;
+  if (a->top <= i) {
+    return 0;
+  }
+
+  return (a->d[i]>>j)&1;
+}
+
+int BN_mask_bits(BIGNUM *a, int n) {
+  int b, w;
+
+  if (n < 0) {
+    return 0;
+  }
+
+  w = n / BN_BITS2;
+  b = n % BN_BITS2;
+  if (w >= a->top) {
+    return 0;
+  }
+  if (b == 0) {
+    a->top = w;
+  } else {
+    a->top = w + 1;
+    a->d[w] &= ~(BN_MASK2 << b);
+  }
+
+  bn_correct_top(a);
+  return 1;
+}
diff --git a/crypto/bn/sqrt.c b/crypto/bn/sqrt.c
new file mode 100644
index 0000000..3ec763b
--- /dev/null
+++ b/crypto/bn/sqrt.c
@@ -0,0 +1,430 @@
+/* Written by Lenka Fibikova <fibikova@exp-math.uni-essen.de>
+ * and Bodo Moeller for the OpenSSL project. */
+/* ====================================================================
+ * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/bn.h>
+
+#include <openssl/err.h>
+
+
+/* Returns 'ret' such that
+ *      ret^2 == a (mod p),
+ * using the Tonelli/Shanks algorithm (cf. Henri Cohen, "A Course
+ * in Algebraic Computational Number Theory", algorithm 1.5.1).
+ * 'p' must be prime! */
+BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) {
+  BIGNUM *ret = in;
+  int err = 1;
+  int r;
+  BIGNUM *A, *b, *q, *t, *x, *y;
+  int e, i, j;
+
+  if (!BN_is_odd(p) || BN_abs_is_word(p, 1)) {
+    if (BN_abs_is_word(p, 2)) {
+      if (ret == NULL) {
+        ret = BN_new();
+      }
+      if (ret == NULL) {
+        goto end;
+      }
+      if (!BN_set_word(ret, BN_is_bit_set(a, 0))) {
+        if (ret != in) {
+          BN_free(ret);
+        }
+        return NULL;
+      }
+      return ret;
+    }
+
+    OPENSSL_PUT_ERROR(BN, BN_mod_sqrt, BN_R_P_IS_NOT_PRIME);
+    return (NULL);
+  }
+
+  if (BN_is_zero(a) || BN_is_one(a)) {
+    if (ret == NULL) {
+      ret = BN_new();
+    }
+    if (ret == NULL) {
+      goto end;
+    }
+    if (!BN_set_word(ret, BN_is_one(a))) {
+      if (ret != in) {
+        BN_free(ret);
+      }
+      return NULL;
+    }
+    return ret;
+  }
+
+  BN_CTX_start(ctx);
+  A = BN_CTX_get(ctx);
+  b = BN_CTX_get(ctx);
+  q = BN_CTX_get(ctx);
+  t = BN_CTX_get(ctx);
+  x = BN_CTX_get(ctx);
+  y = BN_CTX_get(ctx);
+  if (y == NULL) {
+    goto end;
+  }
+
+  if (ret == NULL) {
+    ret = BN_new();
+  }
+  if (ret == NULL) {
+    goto end;
+  }
+
+  /* A = a mod p */
+  if (!BN_nnmod(A, a, p, ctx)) {
+    goto end;
+  }
+
+  /* now write  |p| - 1  as  2^e*q  where  q  is odd */
+  e = 1;
+  while (!BN_is_bit_set(p, e)) {
+    e++;
+  }
+  /* we'll set  q  later (if needed) */
+
+  if (e == 1) {
+    /* The easy case:  (|p|-1)/2  is odd, so 2 has an inverse
+     * modulo  (|p|-1)/2,  and square roots can be computed
+     * directly by modular exponentiation.
+     * We have
+     *     2 * (|p|+1)/4 == 1   (mod (|p|-1)/2),
+     * so we can use exponent  (|p|+1)/4,  i.e.  (|p|-3)/4 + 1.
+     */
+    if (!BN_rshift(q, p, 2)) {
+      goto end;
+    }
+    q->neg = 0;
+    if (!BN_add_word(q, 1) ||
+        !BN_mod_exp(ret, A, q, p, ctx)) {
+      goto end;
+    }
+    err = 0;
+    goto vrfy;
+  }
+
+  if (e == 2) {
+    /* |p| == 5  (mod 8)
+     *
+     * In this case  2  is always a non-square since
+     * Legendre(2,p) = (-1)^((p^2-1)/8)  for any odd prime.
+     * So if  a  really is a square, then  2*a  is a non-square.
+     * Thus for
+     *      b := (2*a)^((|p|-5)/8),
+     *      i := (2*a)*b^2
+     * we have
+     *     i^2 = (2*a)^((1 + (|p|-5)/4)*2)
+     *         = (2*a)^((p-1)/2)
+     *         = -1;
+     * so if we set
+     *      x := a*b*(i-1),
+     * then
+     *     x^2 = a^2 * b^2 * (i^2 - 2*i + 1)
+     *         = a^2 * b^2 * (-2*i)
+     *         = a*(-i)*(2*a*b^2)
+     *         = a*(-i)*i
+     *         = a.
+     *
+     * (This is due to A.O.L. Atkin,
+     * <URL:
+     *http://listserv.nodak.edu/scripts/wa.exe?A2=ind9211&L=nmbrthry&O=T&P=562>,
+     * November 1992.)
+     */
+
+    /* t := 2*a */
+    if (!BN_mod_lshift1_quick(t, A, p)) {
+      goto end;
+    }
+
+    /* b := (2*a)^((|p|-5)/8) */
+    if (!BN_rshift(q, p, 3)) {
+      goto end;
+    }
+    q->neg = 0;
+    if (!BN_mod_exp(b, t, q, p, ctx)) {
+      goto end;
+    }
+
+    /* y := b^2 */
+    if (!BN_mod_sqr(y, b, p, ctx)) {
+      goto end;
+    }
+
+    /* t := (2*a)*b^2 - 1*/
+    if (!BN_mod_mul(t, t, y, p, ctx) ||
+        !BN_sub_word(t, 1)) {
+      goto end;
+    }
+
+    /* x = a*b*t */
+    if (!BN_mod_mul(x, A, b, p, ctx) ||
+        !BN_mod_mul(x, x, t, p, ctx)) {
+      goto end;
+    }
+
+    if (!BN_copy(ret, x)) {
+      goto end;
+    }
+    err = 0;
+    goto vrfy;
+  }
+
+  /* e > 2, so we really have to use the Tonelli/Shanks algorithm.
+   * First, find some  y  that is not a square. */
+  if (!BN_copy(q, p)) {
+    goto end; /* use 'q' as temp */
+  }
+  q->neg = 0;
+  i = 2;
+  do {
+    /* For efficiency, try small numbers first;
+     * if this fails, try random numbers.
+     */
+    if (i < 22) {
+      if (!BN_set_word(y, i)) {
+        goto end;
+      }
+    } else {
+      if (!BN_pseudo_rand(y, BN_num_bits(p), 0, 0)) {
+        goto end;
+      }
+      if (BN_ucmp(y, p) >= 0) {
+        if (!(p->neg ? BN_add : BN_sub)(y, y, p)) {
+          goto end;
+        }
+      }
+      /* now 0 <= y < |p| */
+      if (BN_is_zero(y)) {
+        if (!BN_set_word(y, i)) {
+          goto end;
+        }
+      }
+    }
+
+    r = BN_kronecker(y, q, ctx); /* here 'q' is |p| */
+    if (r < -1) {
+      goto end;
+    }
+    if (r == 0) {
+      /* m divides p */
+      OPENSSL_PUT_ERROR(BN, BN_mod_sqrt, BN_R_P_IS_NOT_PRIME);
+      goto end;
+    }
+  } while (r == 1 && ++i < 82);
+
+  if (r != -1) {
+    /* Many rounds and still no non-square -- this is more likely
+     * a bug than just bad luck.
+     * Even if  p  is not prime, we should have found some  y
+     * such that r == -1.
+     */
+    OPENSSL_PUT_ERROR(BN, BN_mod_sqrt, BN_R_TOO_MANY_ITERATIONS);
+    goto end;
+  }
+
+  /* Here's our actual 'q': */
+  if (!BN_rshift(q, q, e)) {
+    goto end;
+  }
+
+  /* Now that we have some non-square, we can find an element
+   * of order  2^e  by computing its q'th power. */
+  if (!BN_mod_exp(y, y, q, p, ctx)) {
+    goto end;
+  }
+  if (BN_is_one(y)) {
+    OPENSSL_PUT_ERROR(BN, BN_mod_sqrt, BN_R_P_IS_NOT_PRIME);
+    goto end;
+  }
+
+  /* Now we know that (if  p  is indeed prime) there is an integer
+   * k,  0 <= k < 2^e,  such that
+   *
+   *      a^q * y^k == 1   (mod p).
+   *
+   * As  a^q  is a square and  y  is not,  k  must be even.
+   * q+1  is even, too, so there is an element
+   *
+   *     X := a^((q+1)/2) * y^(k/2),
+   *
+   * and it satisfies
+   *
+   *     X^2 = a^q * a     * y^k
+   *         = a,
+   *
+   * so it is the square root that we are looking for.
+   */
+
+  /* t := (q-1)/2  (note that  q  is odd) */
+  if (!BN_rshift1(t, q)) {
+    goto end;
+  }
+
+  /* x := a^((q-1)/2) */
+  if (BN_is_zero(t)) /* special case: p = 2^e + 1 */
+  {
+    if (!BN_nnmod(t, A, p, ctx)) {
+      goto end;
+    }
+    if (BN_is_zero(t)) {
+      /* special case: a == 0  (mod p) */
+      BN_zero(ret);
+      err = 0;
+      goto end;
+    } else if (!BN_one(x)) {
+      goto end;
+    }
+  } else {
+    if (!BN_mod_exp(x, A, t, p, ctx)) {
+      goto end;
+    }
+    if (BN_is_zero(x)) {
+      /* special case: a == 0  (mod p) */
+      BN_zero(ret);
+      err = 0;
+      goto end;
+    }
+  }
+
+  /* b := a*x^2  (= a^q) */
+  if (!BN_mod_sqr(b, x, p, ctx) ||
+      !BN_mod_mul(b, b, A, p, ctx)) {
+    goto end;
+  }
+
+  /* x := a*x    (= a^((q+1)/2)) */
+  if (!BN_mod_mul(x, x, A, p, ctx)) {
+    goto end;
+  }
+
+  while (1) {
+    /* Now  b  is  a^q * y^k  for some even  k  (0 <= k < 2^E
+     * where  E  refers to the original value of  e,  which we
+     * don't keep in a variable),  and  x  is  a^((q+1)/2) * y^(k/2).
+     *
+     * We have  a*b = x^2,
+     *    y^2^(e-1) = -1,
+     *    b^2^(e-1) = 1.
+     */
+
+    if (BN_is_one(b)) {
+      if (!BN_copy(ret, x)) {
+        goto end;
+      }
+      err = 0;
+      goto vrfy;
+    }
+
+
+    /* find smallest  i  such that  b^(2^i) = 1 */
+    i = 1;
+    if (!BN_mod_sqr(t, b, p, ctx)) {
+      goto end;
+    }
+    while (!BN_is_one(t)) {
+      i++;
+      if (i == e) {
+        OPENSSL_PUT_ERROR(BN, BN_mod_sqrt, BN_R_NOT_A_SQUARE);
+        goto end;
+      }
+      if (!BN_mod_mul(t, t, t, p, ctx)) {
+        goto end;
+      }
+    }
+
+
+    /* t := y^2^(e - i - 1) */
+    if (!BN_copy(t, y)) {
+      goto end;
+    }
+    for (j = e - i - 1; j > 0; j--) {
+      if (!BN_mod_sqr(t, t, p, ctx)) {
+        goto end;
+      }
+    }
+    if (!BN_mod_mul(y, t, t, p, ctx) ||
+        !BN_mod_mul(x, x, t, p, ctx) ||
+        !BN_mod_mul(b, b, y, p, ctx)) {
+      goto end;
+    }
+    e = i;
+  }
+
+vrfy:
+  if (!err) {
+    /* verify the result -- the input might have been not a square
+     * (test added in 0.9.8) */
+
+    if (!BN_mod_sqr(x, ret, p, ctx)) {
+      err = 1;
+    }
+
+    if (!err && 0 != BN_cmp(x, A)) {
+      OPENSSL_PUT_ERROR(BN, BN_mod_sqrt, BN_R_NOT_A_SQUARE);
+      err = 1;
+    }
+  }
+
+end:
+  if (err) {
+    if (ret != NULL && ret != in) {
+      BN_clear_free(ret);
+    }
+    ret = NULL;
+  }
+  BN_CTX_end(ctx);
+  return ret;
+}
diff --git a/crypto/buf/CMakeLists.txt b/crypto/buf/CMakeLists.txt
new file mode 100644
index 0000000..c039260
--- /dev/null
+++ b/crypto/buf/CMakeLists.txt
@@ -0,0 +1,10 @@
+include_directories(. .. ../../include)
+
+add_library(
+	buf
+
+	OBJECT
+
+	buf.c
+	buf_error.c
+)
diff --git a/crypto/buf/buf.c b/crypto/buf/buf.c
new file mode 100644
index 0000000..fe55c0c
--- /dev/null
+++ b/crypto/buf/buf.c
@@ -0,0 +1,218 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/buf.h>
+
+#include <openssl/mem.h>
+#include <openssl/err.h>
+
+
+BUF_MEM *BUF_MEM_new(void) {
+  BUF_MEM *ret;
+
+  ret = OPENSSL_malloc(sizeof(BUF_MEM));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(BUF, BUF_MEM_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(ret, 0, sizeof(BUF_MEM));
+  return ret;
+}
+
+void BUF_MEM_free(BUF_MEM *buf) {
+  if (buf == NULL) {
+    return;
+  }
+
+  if (buf->data != NULL) {
+    OPENSSL_cleanse(buf->data, buf->max);
+    OPENSSL_free(buf->data);
+  }
+
+  OPENSSL_free(buf);
+}
+
+static size_t buf_mem_grow(BUF_MEM *buf, size_t len, char clean) {
+  char *new_buf;
+  size_t n, alloc_size;
+
+  if (buf->length >= len) {
+    buf->length = len;
+    return len;
+  }
+  if (buf->max >= len) {
+    memset(&buf->data[buf->length], 0, len - buf->length);
+    buf->length = len;
+    return len;
+  }
+
+  n = len + 3;
+  if (n < len) {
+    /* overflow */
+    OPENSSL_PUT_ERROR(BUF, buf_mem_grow, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  n = n / 3;
+  alloc_size = n * 4;
+  if (alloc_size / 4 != n) {
+    /* overflow */
+    OPENSSL_PUT_ERROR(BUF, buf_mem_grow, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  if (buf->data == NULL) {
+    new_buf = OPENSSL_malloc(alloc_size);
+  } else {
+    if (clean) {
+      new_buf = OPENSSL_realloc_clean(buf->data, buf->max, alloc_size);
+    } else {
+      new_buf = OPENSSL_realloc(buf->data, alloc_size);
+    }
+  }
+
+  if (new_buf == NULL) {
+    OPENSSL_PUT_ERROR(BUF, buf_mem_grow, ERR_R_MALLOC_FAILURE);
+    len = 0;
+  } else {
+    buf->data = new_buf;
+    buf->max = alloc_size;
+    memset(&buf->data[buf->length], 0, len - buf->length);
+    buf->length = len;
+  }
+
+  return len;
+}
+
+size_t BUF_MEM_grow(BUF_MEM *buf, size_t len) {
+  return buf_mem_grow(buf, len, 0 /* don't clear old buffer contents. */);
+}
+
+size_t BUF_MEM_grow_clean(BUF_MEM *buf, size_t len) {
+  return buf_mem_grow(buf, len, 1 /* clear old buffer contents. */);
+}
+
+char *BUF_strdup(const char *buf) {
+  if (buf == NULL) {
+    return NULL;
+  }
+
+  return BUF_strndup(buf, strlen(buf));
+}
+
+char *BUF_strndup(const char *buf, size_t size) {
+  char *ret;
+  size_t alloc_size;
+
+  if (buf == NULL) {
+    return NULL;
+  }
+
+  alloc_size = size + 1;
+  if (alloc_size < size) {
+    /* overflow */
+    OPENSSL_PUT_ERROR(BUF, BUF_strndup, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  ret = OPENSSL_malloc(alloc_size);
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(BUF, BUF_strndup, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  BUF_strlcpy(ret, buf, alloc_size);
+  return ret;
+}
+
+size_t BUF_strlcpy(char *dst, const char *src, size_t dst_size) {
+  size_t l = 0;
+
+  for (; dst_size > 1 && *src; dst_size--) {
+    *dst++ = *src++;
+    l++;
+  }
+
+  if (dst_size) {
+    *dst = 0;
+  }
+
+  return l + strlen(src);
+}
+
+size_t BUF_strlcat(char *dst, const char *src, size_t dst_size) {
+  size_t l = 0;
+  for (; dst_size > 0 && *dst; dst_size--, dst++) {
+    l++;
+  }
+  return l + BUF_strlcpy(dst, src, dst_size);
+}
+
+void *BUF_memdup(const void *data, size_t dst_size) {
+  void *ret;
+
+  if (data == NULL) {
+    return NULL;
+  }
+
+  ret = OPENSSL_malloc(dst_size);
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(BUF, BUF_memdup, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memcpy(ret, data, dst_size);
+  return ret;
+}
diff --git a/crypto/buf/buf.h b/crypto/buf/buf.h
new file mode 100644
index 0000000..4cfeee4
--- /dev/null
+++ b/crypto/buf/buf.h
@@ -0,0 +1,115 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_BUFFER_H
+#define OPENSSL_HEADER_BUFFER_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* BUF_MEM is a generic buffer object used by OpenSSL. */
+struct buf_mem_st {
+  size_t length; /* current number of bytes */
+  char *data;
+  size_t max; /* size of buffer */
+};
+
+/* BUF_MEM_new creates a new BUF_MEM which has no allocated data buffer. */
+BUF_MEM *BUF_MEM_new(void);
+
+/* BUF_MEM_free frees |buf->data| if needed and then frees |buf| itself. */
+void BUF_MEM_free(BUF_MEM *buf);
+
+/* BUF_MEM_grow ensures that |buf| has length |len| and allocates memory if
+ * needed. If the length of |buf| increased, the new bytes are filled with
+ * zeros. It returns the length of |buf|, or zero if there's an error. */
+size_t BUF_MEM_grow(BUF_MEM *buf, size_t len);
+
+/* BUF_MEM_grow_clean acts the same as |BUF_MEM_grow|, but clears the previous
+ * contents of memory if reallocing. */
+size_t BUF_MEM_grow_clean(BUF_MEM *str, size_t len);
+
+/* BUF_strdup returns an allocated, duplicate of |str|. */
+char *BUF_strdup(const char *str);
+
+/* BUF_strndup returns an allocated, duplicate of |str|, which is, at most,
+ * |size| bytes. The result is always NUL terminated. */
+char *BUF_strndup(const char *str, size_t size);
+
+/* BUF_memdup returns an allocated, duplicate of |size| bytes from |data|. */
+void *BUF_memdup(const void *data, size_t size);
+
+/* BUF_strlcpy acts like strlcpy(3). */
+size_t BUF_strlcpy(char *dst, const char *src, size_t dst_size);
+
+/* BUF_strlcat acts like strlcat(3). */
+size_t BUF_strlcat(char *dst, const char *src, size_t size);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define BUF_F_BUF_MEM_new 100
+#define BUF_F_buf_mem_grow 101
+#define BUF_F_BUF_strndup 102
+#define BUF_F_BUF_memdup 103
+
+#endif  /* OPENSSL_HEADER_BUFFER_H */
diff --git a/crypto/buf/buf_error.c b/crypto/buf/buf_error.c
new file mode 100644
index 0000000..66be604
--- /dev/null
+++ b/crypto/buf/buf_error.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "buf.h"
+
+const ERR_STRING_DATA BUF_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_BUF, BUF_F_BUF_MEM_new, 0), "BUF_MEM_new"},
+  {ERR_PACK(ERR_LIB_BUF, BUF_F_BUF_memdup, 0), "BUF_memdup"},
+  {ERR_PACK(ERR_LIB_BUF, BUF_F_BUF_strndup, 0), "BUF_strndup"},
+  {ERR_PACK(ERR_LIB_BUF, BUF_F_buf_mem_grow, 0), "buf_mem_grow"},
+  {0, NULL},
+};
diff --git a/crypto/bytestring/CMakeLists.txt b/crypto/bytestring/CMakeLists.txt
new file mode 100644
index 0000000..409a0ce
--- /dev/null
+++ b/crypto/bytestring/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(. .. ../../include)
+
+add_library(
+	bytestring
+
+	OBJECT
+
+	cbs.c
+	cbb.c
+)
+
+add_executable(
+	bytestring_test
+
+	bytestring_test.c
+)
+
+target_link_libraries(bytestring_test crypto)
diff --git a/crypto/bytestring/bytestring.h b/crypto/bytestring/bytestring.h
new file mode 100644
index 0000000..8f788a7
--- /dev/null
+++ b/crypto/bytestring/bytestring.h
@@ -0,0 +1,239 @@
+/* Copyright (c) 2014, 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_BYTESTRING_H
+#define OPENSSL_HEADER_BYTESTRING_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Bytestrings are used for parsing and building TLS and ASN.1 messages.
+ *
+ * A "CBS" (CRYPTO ByteString) represents a string of bytes in memory and
+ * provides utility functions for safely parsing length-prefixed structures
+ * like TLS and ASN.1 from it.
+ *
+ * A "CBB" (CRYPTO ByteBuilder) is a memory buffer that grows as needed and
+ * provides utility functions for building length-prefixed messages. */
+
+
+/* CRYPTO ByteString */
+
+struct cbs_st {
+  const uint8_t *data;
+  size_t len;
+};
+
+/* CBS_init sets |cbs| to point to |data|. It does not take ownership of
+ * |data|. */
+void CBS_init(CBS *cbs, const uint8_t *data, size_t len);
+
+/* CBS_skip advances |cbs| by |len| bytes. It returns one on success and zero
+ * otherwise. */
+int CBS_skip(CBS *cbs, size_t len);
+
+/* CBS_data returns a pointer to the contains of |cbs|. */
+const uint8_t *CBS_data(const CBS *cbs);
+
+/* CBS_len returns the number of bytes remaining in |cbs|. */
+size_t CBS_len(const CBS *cbs);
+
+/* CBS_get_u8 sets |*out| to the next uint8_t from |cbs| and advances |cbs|. It
+ * returns one on success and zero on error. */
+int CBS_get_u8(CBS *cbs, uint8_t *out);
+
+/* CBS_get_u16 sets |*out| to the next, big-endian uint16_t from |cbs| and
+ * advances |cbs|. It returns one on success and zero on error. */
+int CBS_get_u16(CBS *cbs, uint16_t *out);
+
+/* CBS_get_u24 sets |*out| to the next, big-endian 24-bit value from |cbs| and
+ * advances |cbs|. It returns one on success and zero on error. */
+int CBS_get_u24(CBS *cbs, uint32_t *out);
+
+/* CBS_get_u32 sets |*out| to the next, big-endian uint32_t value from |cbs|
+ * and advances |cbs|. It returns one on success and zero on error. */
+int CBS_get_u32(CBS *cbs, uint32_t *out);
+
+/* CBS_get_bytes sets |*out| to the next |len| bytes from |cbs| and advances
+ * |cbs|. It returns one on success and zero on error. */
+int CBS_get_bytes(CBS *cbs, CBS *out, size_t len);
+
+/* CBS_get_u8_length_prefixed sets |*out| to the contents of an 8-bit,
+ * length-prefixed value from |cbs| and advances |cbs| over it. It returns one
+ * on success and zero on error. */
+int CBS_get_u8_length_prefixed(CBS *cbs, CBS *out);
+
+/* CBS_get_u16_length_prefixed sets |*out| to the contents of a 16-bit,
+ * big-endian, length-prefixed value from |cbs| and advances |cbs| over it. It
+ * returns one on success and zero on error. */
+int CBS_get_u16_length_prefixed(CBS *cbs, CBS *out);
+
+/* CBS_get_u24_length_prefixed sets |*out| to the contents of a 24-bit,
+ * big-endian, length-prefixed value from |cbs| and advances |cbs| over it. It
+ * returns one on success and zero on error. */
+int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out);
+
+
+/* Parsing ASN.1 */
+
+#define CBS_ASN1_BOOLEAN 0x1
+#define CBS_ASN1_INTEGER 0x2
+#define CBS_ASN1_BITSTRING 0x3
+#define CBS_ASN1_OCTETSTRING 0x4
+#define CBS_ASN1_OBJECT 0x6
+#define CBS_ASN1_SEQUENCE (0x10 | CBS_ASN1_CONSTRUCTED)
+#define CBS_ASN1_SET (0x11 | CBS_ASN1_CONSTRUCTED)
+
+#define CBS_ASN1_CONSTRUCTED 0x20
+#define CBS_ASN1_CONTEXT_SPECIFIC 0x80
+
+#define CBS_ASN1_ANY 0x10000
+
+/* CBS_get_asn1 sets |*out| to the contents of DER-encoded, ASN.1 element (not
+ * including tag and length bytes) and advances |cbs| over it. The ASN.1
+ * element must match |tag_value|. It returns one on success and zero
+ * on error.
+ *
+ * Tag numbers greater than 31 are not supported. */
+int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value);
+
+/* CBS_get_asn1_ber sets |*out| to the contents of BER-encoded, ASN.1 element
+ * (not including tag and length bytes) and advances |cbs| over it. The ASN.1
+ * element must match |tag_value|. It returns one on success and zero on error.
+ *
+ * The major difference between this function and |CBS_get_asn1| is that
+ * indefinite-length elements may be processed by this function.
+ *
+ * Tag numbers greater than 31 are not supported. */
+int CBS_get_asn1_ber(CBS *cbs, CBS *out, unsigned tag_value);
+
+/* CBS_get_asn1_element acts like |CBS_get_asn1| but |out| will include the
+ * ASN.1 header bytes too. */
+int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value);
+
+
+/* CRYPTO ByteBuilder.
+ *
+ * |CBB| objects allow one to build length-prefixed serialisations. A |CBB|
+ * object is associated with a buffer and new buffers are created with
+ * |CBB_init|. Several |CBB| objects can point at the same buffer when a
+ * length-prefix is pending, however only a single |CBB| can be 'current' at
+ * any one time. For example, if one calls |CBB_add_u8_length_prefixed| then
+ * the new |CBB| points at the same buffer as the original. But if the original
+ * |CBB| is used then the length prefix is written out and the new |CBB| must
+ * not be used again.
+ *
+ * If one needs to force a length prefix to be written out because a |CBB| is
+ * going out of scope, use |CBB_flush|. */
+
+struct cbb_buffer_st {
+  uint8_t *buf;
+  size_t len;      /* The number of valid bytes. */
+  size_t cap;      /* The size of buf. */
+  char can_resize; /* One iff |buf| is owned by this object. If not then |buf|
+                      cannot be resized. */
+};
+
+struct cbb_st {
+  struct cbb_buffer_st *base;
+  /* offset is the offset from the start of |base->buf| to the position of any
+   * pending length-prefix. */
+  size_t offset;
+  /* child points to a child CBB if a length-prefix is pending. */
+  struct cbb_st *child;
+  /* pending_len_len contains the number of bytes in a pending length-prefix,
+   * or zero if no length-prefix is pending. */
+  uint8_t pending_len_len;
+  char pending_is_asn1;
+  /* is_top_level is true iff this is a top-level |CBB| (as opposed to a child
+   * |CBB|). Top-level objects are valid arguments for |CBB_finish|. */
+  char is_top_level;
+};
+
+/* CBB_init initialises |cbb| with |initial_capacity|. Since a |CBB| grows as
+ * needed, the |initial_capacity| is just a hint. It returns one on success or
+ * zero on error. */
+int CBB_init(CBB *cbb, size_t initial_capacity);
+
+/* CBB_init_fixed initialises |cbb| to write to |len| bytes at |buf|. Since
+ * |buf| cannot grow, trying to write more than |len| bytes will cause CBB
+ * functions to fail. It returns one on success or zero on error. */
+int CBB_init_fixed(CBB *cbb, uint8_t *buf, size_t len);
+
+/* CBB_cleanup frees all resources owned by |cbb| and other |CBB| objects
+ * writing to the same buffer. This should be used in an error case where a
+ * serialisation is abandoned. */
+void CBB_cleanup(CBB *cbb);
+
+/* CBB_finish completes any pending length prefix and sets |*out_data| to a
+ * malloced buffer and |*out_len| to the length of that buffer. The caller
+ * takes ownership of the buffer and, unless the buffer was fixed with
+ * |CBB_init_fixed|, must call |OPENSSL_free| when done.
+ *
+ * It can only be called on a "top level" |CBB|, i.e. one initialised with
+ * |CBB_init| or |CBB_init_fixed|. It returns one on success and zero on
+ * error. */
+int CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len);
+
+/* CBB_flush causes any pending length prefixes to be written out and any child
+ * |CBB| objects of |cbb| to be invalidated. It returns one on success or zero
+ * on error. */
+int CBB_flush(CBB *cbb);
+
+/* CBB_add_u8_length_prefixed sets |*out_contents| to a new child of |cbb|. The
+ * data written to |*out_contents| will be prefixed in |cbb| with an 8-bit
+ * length. It returns one on success or zero on error. */
+int CBB_add_u8_length_prefixed(CBB *cbb, CBB *out_contents);
+
+/* CBB_add_u16_length_prefixed sets |*out_contents| to a new child of |cbb|.
+ * The data written to |*out_contents| will be prefixed in |cbb| with a 16-bit,
+ * big-endian length. It returns one on success or zero on error. */
+int CBB_add_u16_length_prefixed(CBB *cbb, CBB *out_contents);
+
+/* CBB_add_u24_length_prefixed sets |*out_contents| to a new child of |cbb|.
+ * The data written to |*out_contents| will be prefixed in |cbb| with a 24-bit,
+ * big-endian length. It returns one on success or zero on error. */
+int CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents);
+
+/* CBB_add_asn sets |*out_contents| to a |CBB| into which the contents of an
+ * ASN.1 object can be written. The |tag| argument will be used as the tag for
+ * the object. It returns one on success or zero on error. */
+int CBB_add_asn1(CBB *cbb, CBB *out_contents, uint8_t tag);
+
+/* CBB_add_bytes appends |len| bytes from |data| to |cbb|. It returns one on
+ * success and zero otherwise. */
+int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len);
+
+/* CBB_add_u8 appends an 8-bit number from |value| to |cbb|. It returns one on
+ * success and zero otherwise. */
+int CBB_add_u8(CBB *cbb, uint8_t value);
+
+/* CBB_add_u8 appends a 16-bit, big-endian number from |value| to |cbb|. It
+ * returns one on success and zero otherwise. */
+int CBB_add_u16(CBB *cbb, uint16_t value);
+
+/* CBB_add_u24 appends a 24-bit, big-endian number from |value| to |cbb|. It
+ * returns one on success and zero otherwise. */
+int CBB_add_u24(CBB *cbb, uint32_t value);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_BYTESTRING_H */
diff --git a/crypto/bytestring/bytestring_test.c b/crypto/bytestring/bytestring_test.c
new file mode 100644
index 0000000..fbfe61b
--- /dev/null
+++ b/crypto/bytestring/bytestring_test.c
@@ -0,0 +1,411 @@
+/* Copyright (c) 2014, 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 <stdio.h>
+#include <stdlib.h>
+
+#include <openssl/bytestring.h>
+
+
+static int test_skip() {
+  static const uint8_t kData[] = {1, 2, 3};
+  CBS data;
+
+  CBS_init(&data, kData, sizeof(kData));
+  return CBS_len(&data) == 3 &&
+      CBS_skip(&data, 1) &&
+      CBS_len(&data) == 2 &&
+      CBS_skip(&data, 2) &&
+      CBS_len(&data) == 0 &&
+      !CBS_skip(&data, 1);
+}
+
+static int test_get_u() {
+  static const uint8_t kData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  uint8_t u8;
+  uint16_t u16;
+  uint32_t u32;
+  CBS data;
+
+  CBS_init(&data, kData, sizeof(kData));
+  return CBS_get_u8(&data, &u8) &&
+    u8 == 1 &&
+    CBS_get_u16(&data, &u16) &&
+    u16 == 0x203 &&
+    CBS_get_u24(&data, &u32) &&
+    u32 == 0x40506 &&
+    CBS_get_u32(&data, &u32) &&
+    u32 == 0x708090a &&
+    !CBS_get_u8(&data, &u8);
+}
+
+static int test_get_prefixed() {
+  static const uint8_t kData[] = {1, 2, 0, 2, 3, 4, 0, 0, 3, 3, 2, 1};
+  uint8_t u8;
+  uint16_t u16;
+  uint32_t u32;
+  CBS data, prefixed;
+
+  CBS_init(&data, kData, sizeof(kData));
+  return CBS_get_u8_length_prefixed(&data, &prefixed) &&
+    CBS_len(&prefixed) == 1 &&
+    CBS_get_u8(&prefixed, &u8) &&
+    u8 == 2 &&
+    CBS_get_u16_length_prefixed(&data, &prefixed) &&
+    CBS_len(&prefixed) == 2 &&
+    CBS_get_u16(&prefixed, &u16) &&
+    u16 == 0x304 &&
+    CBS_get_u24_length_prefixed(&data, &prefixed) &&
+    CBS_len(&prefixed) == 3 &&
+    CBS_get_u24(&prefixed, &u32) &&
+    u32 == 0x30201;
+}
+
+static int test_get_prefixed_bad() {
+  static const uint8_t kData1[] = {2, 1};
+  static const uint8_t kData2[] = {0, 2, 1};
+  static const uint8_t kData3[] = {0, 0, 2, 1};
+  CBS data, prefixed;
+
+  CBS_init(&data, kData1, sizeof(kData1));
+  if (CBS_get_u8_length_prefixed(&data, &prefixed)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData2, sizeof(kData2));
+  if (CBS_get_u16_length_prefixed(&data, &prefixed)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData3, sizeof(kData3));
+  if (CBS_get_u24_length_prefixed(&data, &prefixed)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_get_asn1() {
+  static const uint8_t kData1[] = {0x30, 2, 1, 2};
+  static const uint8_t kData2[] = {0x30, 3, 1, 2};
+  static const uint8_t kData3[] = {0x30, 0x80};
+  static const uint8_t kData4[] = {0x30, 0x81, 1, 1};
+  static const uint8_t kData5[] = {0x30, 0x82, 0, 1, 1};
+
+  CBS data, contents;
+
+  CBS_init(&data, kData1, sizeof(kData1));
+  if (!CBS_get_asn1(&data, &contents, 0x30) ||
+      CBS_len(&contents) != 2 ||
+      memcmp(CBS_data(&contents), "\x01\x02", 2) != 0) {
+    return 0;
+  }
+
+  CBS_init(&data, kData2, sizeof(kData2));
+  /* data is truncated */
+  if (CBS_get_asn1(&data, &contents, 0x30)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData3, sizeof(kData3));
+  /* zero byte length of length */
+  if (CBS_get_asn1(&data, &contents, 0x30)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData4, sizeof(kData4));
+  /* long form mistakenly used. */
+  if (CBS_get_asn1(&data, &contents, 0x30)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData5, sizeof(kData5));
+  /* length takes too many bytes. */
+  if (CBS_get_asn1(&data, &contents, 0x30)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData1, sizeof(kData1));
+  /* wrong tag. */
+  if (CBS_get_asn1(&data, &contents, 0x31)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_get_indef() {
+  static const uint8_t kData1[] = {0x30, 0x80, 0x00, 0x00};
+  static const uint8_t kDataWithoutEOC[] = {0x30, 0x80, 0x01, 0x00};
+  static const uint8_t kDataWithBadInternalLength[] = {0x30, 0x80, 0x01, 0x01};
+  static const uint8_t kDataNested[] = {0x30, 0x80, 0x30, 0x80, 0x30, 0x80,
+                                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+  CBS data, contents;
+  CBS_init(&data, kData1, sizeof(kData1));
+  if (CBS_get_asn1(&data, &contents, 0x30)) {
+    /* Indefinite lengths should not be supported in DER mode. */
+    fprintf(stderr, "Indefinite length parsed by CBS_get_asn1.\n");
+    return 0;
+  }
+
+  if (!CBS_get_asn1_ber(&data, &contents, 0x30) ||
+      CBS_len(&contents) != 0 ||
+      CBS_len(&data) != 0) {
+    fprintf(stderr, "Simple indefinite length failed.\n");
+    return 0;
+  }
+
+  CBS_init(&data, kDataWithoutEOC, sizeof(kDataWithoutEOC));
+  if (CBS_get_asn1_ber(&data, &contents, 0x30)) {
+    fprintf(stderr, "Parsed without EOC.\n");
+    return 0;
+  }
+
+  CBS_init(&data, kDataWithBadInternalLength,
+           sizeof(kDataWithBadInternalLength));
+  if (CBS_get_asn1_ber(&data, &contents, 0x30)) {
+    fprintf(stderr, "Parsed with internal length.\n");
+    return 0;
+  }
+
+  CBS_init(&data, kDataNested, sizeof(kDataNested));
+  if (!CBS_get_asn1_ber(&data, &contents, 0x30) ||
+      CBS_len(&contents) != 8 ||
+      CBS_len(&data) != 0) {
+    fprintf(stderr, "Nested indefinite lengths failed.\n");
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_cbb_basic() {
+  static const uint8_t kExpected[] = {1, 2, 3, 4, 5, 6, 7, 8};
+  uint8_t *buf;
+  size_t buf_len;
+  int ok;
+  CBB cbb;
+
+  if (!CBB_init(&cbb, 100)) {
+    return 0;
+  }
+  CBB_cleanup(&cbb);
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_u8(&cbb, 1) ||
+      !CBB_add_u16(&cbb, 0x203) ||
+      !CBB_add_u24(&cbb, 0x40506) ||
+      !CBB_add_bytes(&cbb, (const uint8_t*) "\x07\x08", 2) ||
+      !CBB_finish(&cbb, &buf, &buf_len)) {
+    return 0;
+  }
+
+  ok = buf_len == sizeof(kExpected) && memcmp(buf, kExpected, buf_len) == 0;
+  free(buf);
+  return ok;
+}
+
+static int test_cbb_fixed() {
+  CBB cbb;
+  uint8_t buf[1];
+  uint8_t *out_buf;
+  size_t out_size;
+
+  if (!CBB_init_fixed(&cbb, NULL, 0) ||
+      CBB_add_u8(&cbb, 1) ||
+      !CBB_finish(&cbb, &out_buf, &out_size) ||
+      out_buf != NULL ||
+      out_size != 0) {
+    return 0;
+  }
+
+  if (!CBB_init_fixed(&cbb, buf, 1) ||
+      !CBB_add_u8(&cbb, 1) ||
+      CBB_add_u8(&cbb, 2) ||
+      !CBB_finish(&cbb, &out_buf, &out_size) ||
+      out_buf != buf ||
+      out_size != 1 ||
+      buf[0] != 1) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_cbb_finish_child() {
+  CBB cbb, child;
+  uint8_t *out_buf;
+  size_t out_size;
+
+  if (!CBB_init(&cbb, 16) ||
+      !CBB_add_u8_length_prefixed(&cbb, &child) ||
+      CBB_finish(&child, &out_buf, &out_size) ||
+      !CBB_finish(&cbb, &out_buf, &out_size) ||
+      out_size != 1 ||
+      out_buf[0] != 0) {
+    return 0;
+  }
+
+  free(out_buf);
+  return 1;
+}
+
+static int test_cbb_prefixed() {
+  static const uint8_t kExpected[] = {0, 1, 1, 0, 2, 2, 3, 0, 0, 3,
+                                      4, 5, 6, 5, 4, 1, 0, 1, 2};
+  uint8_t *buf;
+  size_t buf_len;
+  CBB cbb, contents, inner_contents, inner_inner_contents;
+  int ok;
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_u8_length_prefixed(&cbb, &contents) ||
+      !CBB_add_u8_length_prefixed(&cbb, &contents) ||
+      !CBB_add_u8(&contents, 1) ||
+      !CBB_add_u16_length_prefixed(&cbb, &contents) ||
+      !CBB_add_u16(&contents, 0x203) ||
+      !CBB_add_u24_length_prefixed(&cbb, &contents) ||
+      !CBB_add_u24(&contents, 0x40506) ||
+      !CBB_add_u8_length_prefixed(&cbb, &contents) ||
+      !CBB_add_u8_length_prefixed(&contents, &inner_contents) ||
+      !CBB_add_u8(&inner_contents, 1) ||
+      !CBB_add_u16_length_prefixed(&inner_contents, &inner_inner_contents) ||
+      !CBB_add_u8(&inner_inner_contents, 2) ||
+      !CBB_finish(&cbb, &buf, &buf_len)) {
+    return 0;
+  }
+
+  ok = buf_len == sizeof(kExpected) && memcmp(buf, kExpected, buf_len) == 0;
+  free(buf);
+  return ok;
+}
+
+static int test_cbb_misuse() {
+  CBB cbb, child, contents;
+  uint8_t *buf;
+  size_t buf_len;
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_u8_length_prefixed(&cbb, &child) ||
+      !CBB_add_u8(&child, 1) ||
+      !CBB_add_u8(&cbb, 2)) {
+    return 0;
+  }
+
+  /* Since we wrote to |cbb|, |child| is now invalid and attempts to write to
+   * it should fail. */
+  if (CBB_add_u8(&child, 1) ||
+      CBB_add_u16(&child, 1) ||
+      CBB_add_u24(&child, 1) ||
+      CBB_add_u8_length_prefixed(&child, &contents) ||
+      CBB_add_u16_length_prefixed(&child, &contents) ||
+      CBB_add_asn1(&child, &contents, 1) ||
+      CBB_add_bytes(&child, (const uint8_t*) "a", 1)) {
+    fprintf(stderr, "CBB operation on invalid CBB did not fail.\n");
+    return 0;
+  }
+
+  if (!CBB_finish(&cbb, &buf, &buf_len) ||
+      buf_len != 3 ||
+      memcmp(buf, "\x01\x01\x02", 3) != 0) {
+    return 0;
+  }
+
+  free(buf);
+
+  return 1;
+}
+
+static int test_cbb_asn1() {
+  static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
+  uint8_t *buf, *test_data;
+  size_t buf_len;
+  CBB cbb, contents, inner_contents;
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_asn1(&cbb, &contents, 0x30) ||
+      !CBB_add_bytes(&contents, (const uint8_t*) "\x01\x02\x03", 3) ||
+      !CBB_finish(&cbb, &buf, &buf_len)) {
+    return 0;
+  }
+
+  if (buf_len != sizeof(kExpected) || memcmp(buf, kExpected, buf_len) != 0) {
+    return 0;
+  }
+  free(buf);
+
+  test_data = calloc(1, 100000);
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_asn1(&cbb, &contents, 0x30) ||
+      !CBB_add_bytes(&contents, test_data, 130) ||
+      !CBB_finish(&cbb, &buf, &buf_len)) {
+    return 0;
+  }
+
+  if (buf_len != 3 + 130 || memcmp(buf, "\x30\x81\x82", 3) != 0) {
+    return 0;
+  }
+  free(buf);
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_asn1(&cbb, &contents, 0x30) ||
+      !CBB_add_bytes(&contents, test_data, 1000) ||
+      !CBB_finish(&cbb, &buf, &buf_len)) {
+    return 0;
+  }
+
+  if (buf_len != 4 + 1000 || memcmp(buf, "\x30\x82\x03\xe8", 4) != 0) {
+    return 0;
+  }
+  free(buf);
+
+  if (!CBB_init(&cbb, 0) ||
+      !CBB_add_asn1(&cbb, &contents, 0x30) ||
+      !CBB_add_asn1(&contents, &inner_contents, 0x30) ||
+      !CBB_add_bytes(&inner_contents, test_data, 100000) ||
+      !CBB_finish(&cbb, &buf, &buf_len)) {
+    return 0;
+  }
+
+  if (buf_len != 5 + 5 + 100000 ||
+      memcmp(buf, "\x30\x83\x01\x86\xa5\x30\x83\x01\x86\xa0", 10) != 0) {
+    return 0;
+  }
+  free(buf);
+
+  free(test_data);
+  return 1;
+}
+
+int main() {
+  if (!test_skip() ||
+      !test_get_u() ||
+      !test_get_prefixed() ||
+      !test_get_prefixed_bad() ||
+      !test_get_asn1() ||
+      !test_get_indef() ||
+      !test_cbb_basic() ||
+      !test_cbb_fixed() ||
+      !test_cbb_finish_child() ||
+      !test_cbb_misuse() ||
+      !test_cbb_prefixed() ||
+      !test_cbb_asn1()) {
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/bytestring/cbb.c b/crypto/bytestring/cbb.c
new file mode 100644
index 0000000..3e35d5d
--- /dev/null
+++ b/crypto/bytestring/cbb.c
@@ -0,0 +1,317 @@
+/* Copyright (c) 2014, 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/bytestring.h>
+
+#include <openssl/mem.h>
+
+
+static int cbb_init(CBB *cbb, uint8_t *buf, size_t cap) {
+  struct cbb_buffer_st *base;
+
+  base = OPENSSL_malloc(sizeof(struct cbb_buffer_st));
+  if (base == NULL) {
+    OPENSSL_free(buf);
+    return 0;
+  }
+
+  base->buf = buf;
+  base->len = 0;
+  base->cap = cap;
+  base->can_resize = 1;
+
+  memset(cbb, 0, sizeof(CBB));
+  cbb->base = base;
+  cbb->is_top_level = 1;
+  return 1;
+}
+
+int CBB_init(CBB *cbb, size_t initial_capacity) {
+  uint8_t *buf;
+
+  buf = OPENSSL_malloc(initial_capacity);
+  if (initial_capacity > 0 && buf == NULL) {
+    return 0;
+  }
+
+  return cbb_init(cbb, buf, initial_capacity);
+}
+
+int CBB_init_fixed(CBB *cbb, uint8_t *buf, size_t len) {
+  if (!cbb_init(cbb, buf, len)) {
+    return 0;
+  }
+
+  cbb->base->can_resize = 0;
+  return 1;
+}
+
+void CBB_cleanup(CBB *cbb) {
+  if (cbb->base) {
+    if (cbb->base->buf && cbb->base->can_resize) {
+      OPENSSL_free(cbb->base->buf);
+    }
+    OPENSSL_free(cbb->base);
+  }
+  cbb->base = NULL;
+}
+
+static int cbb_buffer_add(struct cbb_buffer_st *base, uint8_t **out,
+                          size_t len) {
+  size_t newlen;
+
+  if (base == NULL) {
+    return 0;
+  }
+
+  newlen = base->len + len;
+  if (newlen < base->len) {
+    /* Overflow */
+    return 0;
+  }
+
+  if (newlen > base->cap) {
+    size_t newcap = base->cap * 2;
+    uint8_t *newbuf;
+
+    if (!base->can_resize) {
+      return 0;
+    }
+
+    if (newcap < base->cap || newcap < newlen) {
+      newcap = newlen;
+    }
+    newbuf = OPENSSL_realloc(base->buf, newcap);
+    if (newbuf == NULL) {
+      return 0;
+    }
+
+    base->buf = newbuf;
+    base->cap = newcap;
+  }
+
+  if (out) {
+    *out = base->buf + base->len;
+  }
+  base->len = newlen;
+  return 1;
+}
+
+static int cbb_buffer_add_u(struct cbb_buffer_st *base, uint32_t v,
+                            size_t len_len) {
+  uint8_t *buf;
+  size_t i;
+
+  if (len_len == 0) {
+    return 1;
+  }
+  if (!cbb_buffer_add(base, &buf, len_len)) {
+    return 0;
+  }
+
+  for (i = len_len - 1; i < len_len; i--) {
+    buf[i] = v;
+    v >>= 8;
+  }
+  return 1;
+}
+
+int CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len) {
+  if (!cbb->is_top_level) {
+    return 0;
+  }
+
+  if (!CBB_flush(cbb)) {
+    return 0;
+  }
+
+  *out_data = cbb->base->buf;
+  *out_len = cbb->base->len;
+  cbb->base->buf = NULL;
+  CBB_cleanup(cbb);
+  return 1;
+}
+
+/* CBB_flush recurses and then writes out any pending length prefix. The
+ * current length of the underlying base is taken to be the length of the
+ * length-prefixed data. */
+int CBB_flush(CBB *cbb) {
+  size_t child_start, i, len;
+
+  if (cbb->base == NULL) {
+    return 0;
+  }
+
+  if (cbb->child == NULL || cbb->pending_len_len == 0) {
+    return 1;
+  }
+
+  child_start = cbb->offset + cbb->pending_len_len;
+
+  if (!CBB_flush(cbb->child) ||
+      child_start < cbb->offset ||
+      cbb->base->len < child_start) {
+    return 0;
+  }
+
+  len = cbb->base->len - child_start;
+
+  if (cbb->pending_is_asn1) {
+    /* For ASN.1 we assume that we'll only need a single byte for the length.
+     * If that turned out to be incorrect, we have to move the contents along
+     * in order to make space. */
+    size_t len_len;
+    uint8_t initial_length_byte;
+
+    if (len > 0xfffffffe) {
+      /* Too large. */
+      return 0;
+    } else if (len > 0xffffff) {
+      len_len = 5;
+      initial_length_byte = 0x80 | 4;
+    } else if (len > 0xffff) {
+      len_len = 4;
+      initial_length_byte = 0x80 | 3;
+    } else if (len > 0xff) {
+      len_len = 3;
+      initial_length_byte = 0x80 | 2;
+    } else if (len > 0x7f) {
+      len_len = 2;
+      initial_length_byte = 0x80 | 1;
+    } else {
+      len_len = 1;
+      initial_length_byte = len;
+      len = 0;
+    }
+
+    if (len_len != 1) {
+      /* We need to move the contents along in order to make space. */
+      size_t extra_bytes = len_len - 1;
+      if (!cbb_buffer_add(cbb->base, NULL, extra_bytes)) {
+        return 0;
+      }
+      memmove(cbb->base->buf + cbb->offset + extra_bytes,
+              cbb->base->buf + cbb->offset, len);
+    }
+    cbb->base->buf[cbb->offset++] = initial_length_byte;
+    cbb->pending_len_len = len_len - 1;
+  }
+
+  for (i = cbb->pending_len_len - 1; i < cbb->pending_len_len; i--) {
+    cbb->base->buf[cbb->offset + i] = len;
+    len >>= 8;
+  }
+  if (len != 0) {
+    return 0;
+  }
+
+  cbb->child->base = NULL;
+  cbb->child = NULL;
+  cbb->pending_len_len = 0;
+  cbb->pending_is_asn1 = 0;
+  cbb->offset = 0;
+
+  return 1;
+}
+
+
+static int cbb_add_length_prefixed(CBB *cbb, CBB *out_contents,
+                                   size_t len_len) {
+  uint8_t *prefix_bytes;
+
+  if (!CBB_flush(cbb)) {
+    return 0;
+  }
+
+  cbb->offset = cbb->base->len;
+  if (!cbb_buffer_add(cbb->base, &prefix_bytes, len_len)) {
+    return 0;
+  }
+
+  memset(prefix_bytes, 0, len_len);
+  memset(out_contents, 0, sizeof(CBB));
+  out_contents->base = cbb->base;
+  cbb->child = out_contents;
+  cbb->pending_len_len = len_len;
+  cbb->pending_is_asn1 = 0;
+
+  return 1;
+}
+
+int CBB_add_u8_length_prefixed(CBB *cbb, CBB *out_contents) {
+  return cbb_add_length_prefixed(cbb, out_contents, 1);
+}
+
+int CBB_add_u16_length_prefixed(CBB *cbb, CBB *out_contents) {
+  return cbb_add_length_prefixed(cbb, out_contents, 2);
+}
+
+int CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents) {
+  return cbb_add_length_prefixed(cbb, out_contents, 3);
+}
+
+int CBB_add_asn1(CBB *cbb, CBB *out_contents, uint8_t tag) {
+  if (!CBB_flush(cbb) ||
+      !CBB_add_u8(cbb, tag)) {
+    return 0;
+  }
+
+  cbb->offset = cbb->base->len;
+  if (!CBB_add_u8(cbb, 0)) {
+    return 0;
+  }
+
+  memset(out_contents, 0, sizeof(CBB));
+  out_contents->base = cbb->base;
+  cbb->child = out_contents;
+  cbb->pending_len_len = 1;
+  cbb->pending_is_asn1 = 1;
+
+  return 1;
+}
+
+int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len) {
+  uint8_t *dest;
+
+  if (!CBB_flush(cbb) ||
+      !cbb_buffer_add(cbb->base, &dest, len)) {
+    return 0;
+  }
+  memcpy(dest, data, len);
+  return 1;
+}
+
+int CBB_add_u8(CBB *cbb, uint8_t value) {
+  if (!CBB_flush(cbb)) {
+    return 0;
+  }
+
+  return cbb_buffer_add_u(cbb->base, value, 1);
+}
+
+int CBB_add_u16(CBB *cbb, uint16_t value) {
+  if (!CBB_flush(cbb)) {
+    return 0;
+  }
+
+  return cbb_buffer_add_u(cbb->base, value, 2);
+}
+
+int CBB_add_u24(CBB *cbb, uint32_t value) {
+  if (!CBB_flush(cbb)) {
+    return 0;
+  }
+
+  return cbb_buffer_add_u(cbb->base, value, 3);
+}
diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c
new file mode 100644
index 0000000..8ab8a74
--- /dev/null
+++ b/crypto/bytestring/cbs.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2014, 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/bytestring.h>
+
+#include <assert.h>
+
+
+void CBS_init(CBS *cbs, const uint8_t *data, size_t len) {
+  cbs->data = data;
+  cbs->len = len;
+}
+
+static int cbs_get(CBS *cbs, const uint8_t **p, size_t n) {
+  if (cbs->len < n) {
+    return 0;
+  }
+
+  *p = cbs->data;
+  cbs->data += n;
+  cbs->len -= n;
+  return 1;
+}
+
+int CBS_skip(CBS *cbs, size_t len) {
+  const uint8_t *dummy;
+  return cbs_get(cbs, &dummy, len);
+}
+
+const uint8_t *CBS_data(const CBS *cbs) {
+  return cbs->data;
+}
+
+size_t CBS_len(const CBS *cbs) {
+  return cbs->len;
+}
+
+static int cbs_get_u(CBS *cbs, uint32_t *out, size_t len) {
+  uint32_t result = 0;
+  size_t i;
+  const uint8_t *data;
+
+  if (!cbs_get(cbs, &data, len)) {
+    return 0;
+  }
+  for (i = 0; i < len; i++) {
+    result <<= 8;
+    result |= data[i];
+  }
+  *out = result;
+  return 1;
+}
+
+int CBS_get_u8(CBS *cbs, uint8_t *out) {
+  const uint8_t *v;
+  if (!cbs_get(cbs, &v, 1)) {
+    return 0;
+  }
+  *out = *v;
+  return 1;
+}
+
+int CBS_get_u16(CBS *cbs, uint16_t *out) {
+  uint32_t v;
+  if (!cbs_get_u(cbs, &v, 2)) {
+    return 0;
+  }
+  *out = v;
+  return 1;
+}
+
+int CBS_get_u24(CBS *cbs, uint32_t *out) {
+  return cbs_get_u(cbs, out, 3);
+}
+
+int CBS_get_u32(CBS *cbs, uint32_t *out) {
+  return cbs_get_u(cbs, out, 4);
+}
+
+int CBS_get_bytes(CBS *cbs, CBS *out, size_t len) {
+  const uint8_t *v;
+  if (!cbs_get(cbs, &v, len)) {
+    return 0;
+  }
+  CBS_init(out, v, len);
+  return 1;
+}
+
+static int cbs_get_length_prefixed(CBS *cbs, CBS *out, size_t len_len) {
+  uint32_t len;
+  if (!cbs_get_u(cbs, &len, len_len)) {
+    return 0;
+  }
+  return CBS_get_bytes(cbs, out, len);
+}
+
+int CBS_get_u8_length_prefixed(CBS *cbs, CBS *out) {
+  return cbs_get_length_prefixed(cbs, out, 1);
+}
+
+int CBS_get_u16_length_prefixed(CBS *cbs, CBS *out) {
+  return cbs_get_length_prefixed(cbs, out, 2);
+}
+
+int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) {
+  return cbs_get_length_prefixed(cbs, out, 3);
+}
+
+static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+                                size_t *out_header_len, unsigned depth,
+                                int *was_indefinite_len);
+
+/* cbs_get_asn1_indefinite_len sets |*out| to be a CBS that covers an
+ * indefinite length element in |cbs| and advances |*in|. On entry, |cbs| will
+ * not have had the tag and length byte removed. On exit, |*out| does not cover
+ * the EOC element, but |*in| is skipped over it.
+ *
+ * The |depth| argument counts the number of times the code has recursed trying
+ * to find an indefinite length. */
+static int cbs_get_asn1_indefinite_len(CBS *in, CBS *out, unsigned depth) {
+  static const size_t kEOCLength = 2;
+  size_t header_len;
+  unsigned tag;
+  int was_indefinite_len;
+  CBS orig = *in, child;
+
+  if (!CBS_skip(in, 2 /* tag plus 0x80 byte for indefinite len */)) {
+    return 0;
+  }
+
+  for (;;) {
+    if (!cbs_get_asn1_element(in, &child, &tag, &header_len, depth + 1,
+                              &was_indefinite_len)) {
+      return 0;
+    }
+
+    if (!was_indefinite_len && CBS_len(&child) == kEOCLength &&
+        header_len == kEOCLength && tag == 0) {
+      break;
+    }
+  }
+
+  return CBS_get_bytes(&orig, out, CBS_len(&orig) - CBS_len(in) - kEOCLength);
+}
+
+/* MAX_DEPTH the maximum number of levels of indefinite lengths that we'll
+ * support. */
+#define MAX_DEPTH 64
+
+static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+                                size_t *out_header_len, unsigned depth,
+                                int *was_indefinite_len) {
+  uint8_t tag, length_byte;
+  CBS header = *cbs;
+  if (!CBS_get_u8(&header, &tag) ||
+      !CBS_get_u8(&header, &length_byte)) {
+    return 0;
+  }
+
+  if ((tag & 0x1f) == 0x1f) {
+    /* Long form tags are not supported. */
+    return 0;
+  }
+
+  *out_tag = tag;
+  if (was_indefinite_len) {
+    *was_indefinite_len = 0;
+  }
+
+  size_t len;
+  if ((length_byte & 0x80) == 0) {
+    /* Short form length. */
+    len = ((size_t) length_byte) + 2;
+    *out_header_len = 2;
+  } else {
+    /* Long form length. */
+    const size_t num_bytes = length_byte & 0x7f;
+    uint32_t len32;
+
+    if (depth < MAX_DEPTH && num_bytes == 0) {
+      /* indefinite length */
+      *out_header_len = 2;
+      if (was_indefinite_len) {
+        *was_indefinite_len = 1;
+      }
+      return cbs_get_asn1_indefinite_len(cbs, out, depth);
+    }
+
+    if (num_bytes == 0 || num_bytes > 4) {
+      return 0;
+    }
+    if (!cbs_get_u(&header, &len32, num_bytes)) {
+      return 0;
+    }
+    if (len32 < 128) {
+      /* Length should have used short-form encoding. */
+      return 0;
+    }
+    if ((len32 >> ((num_bytes-1)*8)) == 0) {
+      /* Length should have been at least one byte shorter. */
+      return 0;
+    }
+    len = len32;
+    if (len + 2 + num_bytes < len) {
+      /* Overflow. */
+      return 0;
+    }
+    len += 2 + num_bytes;
+    *out_header_len = 2 + num_bytes;
+  }
+
+  return CBS_get_bytes(cbs, out, len);
+}
+
+static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value, int ber,
+                        int skip_header) {
+  size_t header_len;
+  unsigned tag;
+  CBS throwaway;
+
+  if (out == NULL) {
+    out = &throwaway;
+  }
+
+  if (!cbs_get_asn1_element(cbs, out, &tag, &header_len, ber ? 0 : MAX_DEPTH,
+                            NULL) ||
+      tag != tag_value) {
+    return 0;
+  }
+
+  if (skip_header && !CBS_skip(out, header_len)) {
+    assert(0);
+    return 0;
+  }
+
+  return 1;
+}
+
+int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value) {
+  return cbs_get_asn1(cbs, out, tag_value, 0 /* DER */,
+                      1 /* skip header */);
+}
+
+int CBS_get_asn1_ber(CBS *cbs, CBS *out, unsigned tag_value) {
+  return cbs_get_asn1(cbs, out, tag_value, 1 /* BER */,
+                      1 /* skip header */);
+}
+
+int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value) {
+  return cbs_get_asn1(cbs, out, tag_value, 0 /* DER */,
+                      0 /* include header */);
+}
diff --git a/crypto/cipher/CMakeLists.txt b/crypto/cipher/CMakeLists.txt
new file mode 100644
index 0000000..55a5f87
--- /dev/null
+++ b/crypto/cipher/CMakeLists.txt
@@ -0,0 +1,24 @@
+include_directories(. .. ../../include)
+
+add_library(
+	cipher
+
+	OBJECT
+
+	cipher.c
+	cipher_error.c
+	derive_key.c
+
+	e_null.c
+	e_rc4.c
+	e_des.c
+	e_aes.c
+)
+
+add_executable(
+	cipher_test
+
+	cipher_test.c
+)
+
+target_link_libraries(cipher_test crypto)
diff --git a/crypto/cipher/cipher.c b/crypto/cipher/cipher.c
new file mode 100644
index 0000000..bf5c559
--- /dev/null
+++ b/crypto/cipher/cipher.c
@@ -0,0 +1,596 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/cipher.h>
+
+#include <string.h>
+#include <assert.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+const EVP_CIPHER *EVP_get_cipherbynid(int nid) {
+  switch (nid) {
+    case NID_des_ede3_cbc:
+      return EVP_des_ede3_cbc();
+    case NID_des_ede_cbc:
+      return EVP_des_cbc();
+    case NID_aes_128_cbc:
+      return EVP_aes_128_cbc();
+    case NID_aes_256_cbc:
+      return EVP_aes_256_cbc();
+    default:
+      return NULL;
+  }
+}
+
+void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *ctx) {
+  memset(ctx, 0, sizeof(EVP_CIPHER_CTX));
+}
+
+EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void) {
+  EVP_CIPHER_CTX *ctx = OPENSSL_malloc(sizeof(EVP_CIPHER_CTX));
+  if (ctx) {
+    EVP_CIPHER_CTX_init(ctx);
+  }
+  return ctx;
+}
+
+int EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                   const unsigned char *key, const unsigned char *iv, int enc) {
+  if (cipher) {
+    EVP_CIPHER_CTX_init(ctx);
+  }
+  return EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, enc);
+}
+
+int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c) {
+  if (c->cipher != NULL && c->cipher->cleanup && !c->cipher->cleanup(c)) {
+    return 0;
+  }
+
+  if (c->cipher_data) {
+    OPENSSL_cleanse(c->cipher_data, c->cipher->ctx_size);
+    OPENSSL_free(c->cipher_data);
+  }
+
+  memset(c, 0, sizeof(EVP_CIPHER_CTX));
+  return 1;
+}
+
+void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx) {
+  if (ctx) {
+    EVP_CIPHER_CTX_cleanup(ctx);
+    OPENSSL_free(ctx);
+  }
+}
+
+int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) {
+  if (in == NULL || in->cipher == NULL) {
+    OPENSSL_PUT_ERROR(CIPHER, EVP_CIPHER_CTX_copy, CIPHER_R_INPUT_NOT_INITIALIZED);
+    return 0;
+  }
+
+  EVP_CIPHER_CTX_cleanup(out);
+  memcpy(out, in, sizeof(EVP_CIPHER_CTX));
+
+  if (in->cipher_data && in->cipher->ctx_size) {
+    out->cipher_data = OPENSSL_malloc(in->cipher->ctx_size);
+    if (!out->cipher_data) {
+      OPENSSL_PUT_ERROR(CIPHER, EVP_CIPHER_CTX_copy, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+    memcpy(out->cipher_data, in->cipher_data, in->cipher->ctx_size);
+  }
+
+  return 1;
+}
+
+int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                      ENGINE *engine, const uint8_t *key, const uint8_t *iv,
+                      int enc) {
+  if (enc == -1) {
+    enc = ctx->encrypt;
+  } else {
+    if (enc) {
+      enc = 1;
+    }
+    ctx->encrypt = enc;
+  }
+
+  if (cipher) {
+    /* Ensure a context left from last time is cleared (the previous check
+     * attempted to avoid this if the same ENGINE and EVP_CIPHER could be
+     * used). */
+    if (ctx->cipher) {
+      EVP_CIPHER_CTX_cleanup(ctx);
+      /* Restore encrypt and flags */
+      ctx->encrypt = enc;
+    }
+
+    ctx->cipher = cipher;
+    if (ctx->cipher->ctx_size) {
+      ctx->cipher_data = OPENSSL_malloc(ctx->cipher->ctx_size);
+      if (!ctx->cipher_data) {
+        OPENSSL_PUT_ERROR(CIPHER, EVP_CipherInit_ex, ERR_R_MALLOC_FAILURE);
+        return 0;
+      }
+    } else {
+      ctx->cipher_data = NULL;
+    }
+
+    ctx->key_len = cipher->key_len;
+    ctx->flags = 0;
+
+    if (ctx->cipher->flags & EVP_CIPH_CTRL_INIT) {
+      if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_INIT, 0, NULL)) {
+        OPENSSL_PUT_ERROR(CIPHER, EVP_CipherInit_ex, CIPHER_R_INITIALIZATION_ERROR);
+        return 0;
+      }
+    }
+  } else if (!ctx->cipher) {
+    OPENSSL_PUT_ERROR(CIPHER, EVP_CipherInit_ex, CIPHER_R_NO_CIPHER_SET);
+    return 0;
+  }
+
+  /* we assume block size is a power of 2 in *cryptUpdate */
+  assert(ctx->cipher->block_size == 1 || ctx->cipher->block_size == 8 ||
+         ctx->cipher->block_size == 16);
+
+  if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_CUSTOM_IV)) {
+    switch (EVP_CIPHER_CTX_mode(ctx)) {
+      case EVP_CIPH_STREAM_CIPHER:
+      case EVP_CIPH_ECB_MODE:
+        break;
+
+      case EVP_CIPH_CFB_MODE:
+      case EVP_CIPH_OFB_MODE:
+        ctx->num = 0;
+        /* fall-through */
+
+      case EVP_CIPH_CBC_MODE:
+        assert(EVP_CIPHER_CTX_iv_length(ctx) <= sizeof(ctx->iv));
+        if (iv) {
+          memcpy(ctx->oiv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+        }
+        memcpy(ctx->iv, ctx->oiv, EVP_CIPHER_CTX_iv_length(ctx));
+        break;
+
+      case EVP_CIPH_CTR_MODE:
+        ctx->num = 0;
+        /* Don't reuse IV for CTR mode */
+        if (iv) {
+          memcpy(ctx->iv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+        }
+        break;
+
+      default:
+        return 0;
+    }
+  }
+
+  if (key || (ctx->cipher->flags & EVP_CIPH_ALWAYS_CALL_INIT)) {
+    if (!ctx->cipher->init(ctx, key, iv, enc)) {
+      return 0;
+    }
+  }
+
+  ctx->buf_len = 0;
+  ctx->final_used = 0;
+  ctx->block_mask = ctx->cipher->block_size - 1;
+  return 1;
+}
+
+int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                       ENGINE *impl, const uint8_t *key, const uint8_t *iv) {
+  return EVP_CipherInit_ex(ctx, cipher, impl, key, iv, 1);
+}
+
+int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                       ENGINE *impl, const uint8_t *key, const uint8_t *iv) {
+  return EVP_CipherInit_ex(ctx, cipher, impl, key, iv, 0);
+}
+
+int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
+                      const uint8_t *in, int in_len) {
+  int i, j, bl;
+
+  if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+    i = ctx->cipher->cipher(ctx, out, in, in_len);
+    if (i < 0) {
+      return 0;
+    } else {
+      *out_len = i;
+    }
+    return 1;
+  }
+
+  if (in_len <= 0) {
+    *out_len = 0;
+    return in_len == 0;
+  }
+
+  if (ctx->buf_len == 0 && (in_len & ctx->block_mask) == 0) {
+    if (ctx->cipher->cipher(ctx, out, in, in_len)) {
+      *out_len = in_len;
+      return 1;
+    } else {
+      *out_len = 0;
+      return 0;
+    }
+  }
+
+  i = ctx->buf_len;
+  bl = ctx->cipher->block_size;
+  assert(bl <= (int)sizeof(ctx->buf));
+  if (i != 0) {
+    if (i + in_len < bl) {
+      memcpy(&ctx->buf[i], in, in_len);
+      ctx->buf_len += in_len;
+      *out_len = 0;
+      return 1;
+    } else {
+      j = bl - i;
+      memcpy(&ctx->buf[i], in, j);
+      if (!ctx->cipher->cipher(ctx, out, ctx->buf, bl)) {
+        return 0;
+      }
+      in_len -= j;
+      in += j;
+      out += bl;
+      *out_len = bl;
+    }
+  } else {
+    *out_len = 0;
+  }
+
+  i = in_len & ctx->block_mask;
+  in_len -= i;
+  if (in_len > 0) {
+    if (!ctx->cipher->cipher(ctx, out, in, in_len)) {
+      return 0;
+    }
+    *out_len += in_len;
+  }
+
+  if (i != 0) {
+    memcpy(ctx->buf, &in[in_len], i);
+  }
+  ctx->buf_len = i;
+  return 1;
+}
+
+int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) {
+  int n, ret;
+  unsigned int i, b, bl;
+
+  if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+    ret = ctx->cipher->cipher(ctx, out, NULL, 0);
+    if (ret < 0) {
+      return 0;
+    } else {
+      *out_len = ret;
+    }
+    return 1;
+  }
+
+  b = ctx->cipher->block_size;
+  assert(b <= sizeof(ctx->buf));
+  if (b == 1) {
+    *out_len = 0;
+    return 1;
+  }
+
+  bl = ctx->buf_len;
+  if (ctx->flags & EVP_CIPH_NO_PADDING) {
+    if (bl) {
+      OPENSSL_PUT_ERROR(CIPHER, EVP_EncryptFinal_ex,
+                        CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);
+      return 0;
+    }
+    *out_len = 0;
+    return 1;
+  }
+
+  n = b - bl;
+  for (i = bl; i < b; i++) {
+    ctx->buf[i] = n;
+  }
+  ret = ctx->cipher->cipher(ctx, out, ctx->buf, b);
+
+  if (ret) {
+    *out_len = b;
+  }
+
+  return ret;
+}
+
+int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
+                      const uint8_t *in, int in_len) {
+  int fix_len;
+  unsigned int b;
+
+  if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+    int r = ctx->cipher->cipher(ctx, out, in, in_len);
+    if (r < 0) {
+      *out_len = 0;
+      return 0;
+    } else {
+      *out_len = r;
+    }
+    return 1;
+  }
+
+  if (in_len <= 0) {
+    *out_len = 0;
+    return in_len == 0;
+  }
+
+  if (ctx->flags & EVP_CIPH_NO_PADDING) {
+    return EVP_EncryptUpdate(ctx, out, out_len, in, in_len);
+  }
+
+  b = ctx->cipher->block_size;
+  assert(b <= sizeof(ctx->final));
+
+  if (ctx->final_used) {
+    memcpy(out, ctx->final, b);
+    out += b;
+    fix_len = 1;
+  } else {
+    fix_len = 0;
+  }
+
+  if (!EVP_EncryptUpdate(ctx, out, out_len, in, in_len)) {
+    return 0;
+  }
+
+  /* if we have 'decrypted' a multiple of block size, make sure
+   * we have a copy of this last block */
+  if (b > 1 && !ctx->buf_len) {
+    *out_len -= b;
+    ctx->final_used = 1;
+    memcpy(ctx->final, &out[*out_len], b);
+  } else {
+    ctx->final_used = 0;
+  }
+
+  if (fix_len) {
+    *out_len += b;
+  }
+
+  return 1;
+}
+
+int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *out_len) {
+  int i, n;
+  unsigned int b;
+  *out_len = 0;
+
+  if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+    i = ctx->cipher->cipher(ctx, out, NULL, 0);
+    if (i < 0) {
+      return 0;
+    } else {
+      *out_len = i;
+    }
+    return 1;
+  }
+
+  b = ctx->cipher->block_size;
+  if (ctx->flags & EVP_CIPH_NO_PADDING) {
+    if (ctx->buf_len) {
+      OPENSSL_PUT_ERROR(CIPHER, EVP_DecryptFinal_ex,
+                        CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);
+      return 0;
+    }
+    *out_len = 0;
+    return 1;
+  }
+
+  if (b > 1) {
+    if (ctx->buf_len || !ctx->final_used) {
+      OPENSSL_PUT_ERROR(CIPHER, EVP_DecryptFinal_ex,
+                        CIPHER_R_WRONG_FINAL_BLOCK_LENGTH);
+      return 0;
+    }
+    assert(b <= sizeof(ctx->final));
+
+    /* The following assumes that the ciphertext has been authenticated.
+     * Otherwise it provides a padding oracle. */
+    n = ctx->final[b - 1];
+    if (n == 0 || n > (int)b) {
+      OPENSSL_PUT_ERROR(CIPHER, EVP_DecryptFinal_ex, CIPHER_R_BAD_DECRYPT);
+      return 0;
+    }
+
+    for (i = 0; i < n; i++) {
+      if (ctx->final[--b] != n) {
+        OPENSSL_PUT_ERROR(CIPHER, EVP_DecryptFinal_ex, CIPHER_R_BAD_DECRYPT);
+        return 0;
+      }
+    }
+
+    n = ctx->cipher->block_size - n;
+    for (i = 0; i < n; i++) {
+      out[i] = ctx->final[i];
+    }
+    *out_len = n;
+  } else {
+    *out_len = 0;
+  }
+
+  return 1;
+}
+
+int EVP_Cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
+               size_t in_len) {
+  return ctx->cipher->cipher(ctx, out, in, in_len);
+}
+
+int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
+                     const uint8_t *in, int in_len) {
+  if (ctx->encrypt) {
+    return EVP_EncryptUpdate(ctx, out, out_len, in, in_len);
+  } else {
+    return EVP_DecryptUpdate(ctx, out, out_len, in, in_len);
+  }
+}
+
+int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) {
+  if (ctx->encrypt) {
+    return EVP_EncryptFinal_ex(ctx, out, out_len);
+  } else {
+    return EVP_DecryptFinal_ex(ctx, out, out_len);
+  }
+}
+
+const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx) {
+  return ctx->cipher;
+}
+
+int EVP_CIPHER_CTX_nid(const EVP_CIPHER_CTX *ctx) {
+  return ctx->cipher->nid;
+}
+
+unsigned EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *ctx) {
+  return ctx->cipher->block_size;
+}
+
+unsigned EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *ctx) {
+  return ctx->key_len;
+}
+
+unsigned EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx) {
+  return ctx->cipher->iv_len;
+}
+
+void *EVP_CIPHER_CTX_get_app_data(const EVP_CIPHER_CTX *ctx) {
+  return ctx->app_data;
+}
+
+void EVP_CIPHER_CTX_set_app_data(EVP_CIPHER_CTX *ctx, void *data) {
+  ctx->app_data = data;
+}
+
+uint32_t EVP_CIPHER_CTX_flags(const EVP_CIPHER_CTX *ctx) {
+  return ctx->cipher->flags & ~EVP_CIPH_MODE_MASK;
+}
+
+uint32_t EVP_CIPHER_CTX_mode(const EVP_CIPHER_CTX *ctx) {
+  return ctx->cipher->flags & EVP_CIPH_MODE_MASK;
+}
+
+int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int command, int arg, void *ptr) {
+  int ret;
+  if (!ctx->cipher) {
+    OPENSSL_PUT_ERROR(CIPHER, EVP_CIPHER_CTX_ctrl, CIPHER_R_NO_CIPHER_SET);
+    return 0;
+  }
+
+  if (!ctx->cipher->ctrl) {
+    OPENSSL_PUT_ERROR(CIPHER, EVP_CIPHER_CTX_ctrl, CIPHER_R_CTRL_NOT_IMPLEMENTED);
+    return 0;
+  }
+
+  ret = ctx->cipher->ctrl(ctx, command, arg, ptr);
+  if (ret == -1) {
+    OPENSSL_PUT_ERROR(CIPHER, EVP_CIPHER_CTX_ctrl,
+                      CIPHER_R_CTRL_OPERATION_NOT_IMPLEMENTED);
+    return 0;
+  }
+
+  return ret;
+}
+
+int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int pad) {
+  if (pad) {
+    ctx->flags &= ~EVP_CIPH_NO_PADDING;
+  } else {
+    ctx->flags |= EVP_CIPH_NO_PADDING;
+  }
+  return 1;
+}
+
+int EVP_CIPHER_nid(const EVP_CIPHER *cipher) { return cipher->nid; }
+
+const char *EVP_CIPHER_name(const EVP_CIPHER *cipher) {
+  return OBJ_nid2sn(cipher->nid);
+}
+
+unsigned EVP_CIPHER_block_size(const EVP_CIPHER *cipher) {
+  return cipher->block_size;
+}
+
+unsigned EVP_CIPHER_key_length(const EVP_CIPHER *cipher) {
+  return cipher->key_len;
+}
+
+unsigned EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) {
+  return cipher->iv_len;
+}
+
+uint32_t EVP_CIPHER_flags(const EVP_CIPHER *cipher) {
+  return cipher->flags & ~EVP_CIPH_MODE_MASK;
+}
+
+uint32_t EVP_CIPHER_mode(const EVP_CIPHER *cipher) {
+  return cipher->flags & EVP_CIPH_MODE_MASK;
+}
diff --git a/crypto/cipher/cipher.h b/crypto/cipher/cipher.h
new file mode 100644
index 0000000..2d105d5
--- /dev/null
+++ b/crypto/cipher/cipher.h
@@ -0,0 +1,442 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_CIPHER_H
+#define OPENSSL_HEADER_CIPHER_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Ciphers. */
+
+
+/* Cipher primitives.
+ *
+ * The following functions return |EVP_CIPHER| objects that implement the named
+ * cipher algorithm. */
+
+const EVP_CIPHER *EVP_rc4(void);
+
+const EVP_CIPHER *EVP_des_cbc(void);
+const EVP_CIPHER *EVP_des_ede3_cbc(void);
+
+const EVP_CIPHER *EVP_aes_128_ecb(void);
+const EVP_CIPHER *EVP_aes_128_cbc(void);
+const EVP_CIPHER *EVP_aes_128_ctr(void);
+const EVP_CIPHER *EVP_aes_128_gcm(void);
+
+const EVP_CIPHER *EVP_aes_256_ecb(void);
+const EVP_CIPHER *EVP_aes_256_cbc(void);
+const EVP_CIPHER *EVP_aes_256_ctr(void);
+const EVP_CIPHER *EVP_aes_256_gcm(void);
+
+/* EVP_enc_null returns a 'cipher' that passes plaintext through as
+ * ciphertext. */
+const EVP_CIPHER *EVP_enc_null(void);
+
+/* EVP_get_cipherbynid returns the cipher corresponding to the given NID, or
+ * NULL if no such cipher is known. */
+const EVP_CIPHER *EVP_get_cipherbynid(int nid);
+
+
+/* Cipher context allocation.
+ *
+ * An |EVP_CIPHER_CTX| represents the state of an encryption or decryption in
+ * progress. */
+
+/* EVP_CIPHER_CTX_init initialises an, already allocated, |EVP_CIPHER_CTX|. */
+void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_new allocates a fresh |EVP_CIPHER_CTX|, calls
+ * |EVP_CIPHER_CTX_init| and returns it, or NULL on allocation failure. */
+EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
+
+/* EVP_CIPHER_CTX_cleanup frees any memory referenced by |ctx|. It returns one
+ * on success and zero otherwise. */
+int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_free calls |EVP_CIPHER_CTX_cleanup| on |ctx| and then frees
+ * |ctx| itself. */
+void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_copy sets |out| to be a duplicate of the current state of
+ * |in|. The |out| argument must have been previously initialised. */
+int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in);
+
+
+/* Cipher context configuration. */
+
+/* EVP_CipherInit_ex configures |ctx| for a fresh encryption (or decryption, if
+ * |enc| is zero) operation using |cipher|. If |ctx| has been previously
+ * configured with a cipher then |cipher|, |key| and |iv| may be |NULL| and
+ * |enc| may be -1 to reuse the previous values. The operation will use |key|
+ * as the key and |iv| as the IV (if any). These should have the correct
+ * lengths given by |EVP_CIPHER_key_length| and |EVP_CIPHER_iv_length|. It
+ * returns one on success and zero on error. */
+int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                      ENGINE *engine, const uint8_t *key, const uint8_t *iv,
+                      int enc);
+
+/* EVP_EncryptInit_ex calls |EVP_CipherInit_ex| with |enc| equal to one. */
+int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                       ENGINE *impl, const uint8_t *key, const uint8_t *iv);
+
+/* EVP_DecryptInit_ex calls |EVP_CipherInit_ex| with |enc| equal to zero. */
+int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                       ENGINE *impl, const uint8_t *key, const uint8_t *iv);
+
+
+/* Cipher operations. */
+
+/* EVP_EncryptUpdate encrypts |in_len| bytes from |in| to |out|. The number
+ * of output bytes may be up to |in_len| plus the block length minus one and
+ * |out| must have sufficient space. The number of bytes actually output is
+ * written to |*out_len|. It returns one on success and zero otherwise. */
+int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
+                      const uint8_t *in, int in_len);
+
+/* EVP_EncryptFinal_ex writes at most a block of ciphertext to |out| and sets
+ * |*out_len| to the number of bytes written. If padding is enabled (the
+ * default) then standard padding is applied to create the final block. If
+ * padding is disabled (with |EVP_CIPHER_CTX_set_padding|) then any partial
+ * block remaining will cause an error. The function returns one on success and
+ * zero otherwise. */
+int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len);
+
+/* EVP_DecryptUpdate decrypts |in_len| bytes from |in| to |out|. The number of
+ * output bytes may be up to |in_len| plus the block length minus one and |out|
+ * must have sufficient space. The number of bytes actually output is written
+ * to |*out_len|. It returns one on success and zero otherwise. */
+int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
+                      const uint8_t *in, int in_len);
+
+/* EVP_DecryptFinal_ex writes at most a block of ciphertext to |out| and sets
+ * |*out_len| to the number of bytes written. If padding is enabled (the
+ * default) then padding is removed from the final block.
+ *
+ * WARNING: it is unsafe to call this function with unauthenticted
+ * ciphertext if padding is enabled. */
+int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *out_len);
+
+/* EVP_Cipher performs a one-shot encryption/decryption operation. No partial
+ * blocks etc are maintained between calls. It returns the number of bytes
+ * written or -1 on error.
+ *
+ * WARNING: this differs from the usual return value convention. */
+int EVP_Cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
+               size_t in_len);
+
+/* EVP_CipherUpdate calls either |EVP_EncryptUpdate| or |EVP_DecryptUpdate|
+ * depending on how |ctx| has been setup. */
+int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
+                     const uint8_t *in, int in_len);
+
+/* EVP_CipherFinal_ex calls either |EVP_EncryptFinal_ex| or
+ * |EVP_DecryptFinal_ex| depending on how |ctx| has been setup. */
+int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len);
+
+
+/* Cipher context accessors. */
+
+/* EVP_CIPHER_CTX_cipher returns the |EVP_CIPHER| underlying |ctx|, or NULL if
+ * none has been set. */
+const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_nid returns a NID identifying the |EVP_CIPHER| underlying
+ * |ctx| (e.g. |NID_rc4|). It will crash if no cipher has been configured. */
+int EVP_CIPHER_CTX_nid(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_block_size returns the block size, in bytes, of the cipher
+ * underlying |ctx|, or one if the cipher is a stream cipher. It will crash if
+ * no cipher has been configured. */
+unsigned EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_key_length returns the key size, in bytes, of the cipher
+ * underlying |ctx| or zero if no cipher has been configured. */
+unsigned EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_iv_length returns the IV size, in bytes, of the cipher
+ * underlying |ctx|. It will crash if no cipher has been configured. */
+unsigned EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_get_app_data returns the opaque, application data pointer for
+ * |ctx|, or NULL if none has been set. */
+void *EVP_CIPHER_CTX_get_app_data(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_set_app_data sets the opaque, application data pointer for
+ * |ctx| to |data|. */
+void EVP_CIPHER_CTX_set_app_data(EVP_CIPHER_CTX *ctx, void *data);
+
+/* EVP_CIPHER_CTX_flags returns a value which is the OR of zero or more
+ * |EVP_CIPH_*| flags. It will crash if no cipher has been configured. */
+uint32_t EVP_CIPHER_CTX_flags(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_mode returns one of the |EVP_CIPH_*| cipher mode values
+ * enumerated below. It will crash if no cipher has been configured. */
+uint32_t EVP_CIPHER_CTX_mode(const EVP_CIPHER_CTX *ctx);
+
+/* EVP_CIPHER_CTX_ctrl is an |ioctl| like function. The |command| argument
+ * should be one of the |EVP_CTRL_*| values. The |arg| and |ptr| arguments are
+ * specific to the command in question. */
+int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int command, int arg, void *ptr);
+
+/* EVP_CIPHER_CTX_set_padding sets whether padding is enabled for |ctx| and
+ * returns one. Pass a non-zero |pad| to enable padding (the default) or zero
+ * to disable. */
+int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int pad);
+
+
+/* Cipher accessors. */
+
+/* EVP_CIPHER_nid returns a NID identifing |cipher|. (For example,
+ * |NID_rc4|.) */
+int EVP_CIPHER_nid(const EVP_CIPHER *cipher);
+
+/* EVP_CIPHER_name returns the short name for |cipher| or NULL if no name is
+ * known. */
+const char *EVP_CIPHER_name(const EVP_CIPHER *cipher);
+
+/* EVP_CIPHER_block_size returns the block size, in bytes, for |cipher|, or one
+ * if |cipher| is a stream cipher. */
+unsigned EVP_CIPHER_block_size(const EVP_CIPHER *cipher);
+
+/* EVP_CIPHER_key_length returns the key size, in bytes, for |cipher|. If
+ * |cipher| can take a variable key length then this function returns the
+ * default key length and |EVP_CIPHER_flags| will return a value with
+ * |EVP_CIPH_VARIABLE_LENGTH| set. */
+unsigned EVP_CIPHER_key_length(const EVP_CIPHER *cipher);
+
+/* EVP_CIPHER_iv_length returns the IV size, in bytes, of |cipher|, or zero if
+ * |cipher| doesn't take an IV. */
+unsigned EVP_CIPHER_iv_length(const EVP_CIPHER *cipher);
+
+/* EVP_CIPHER_flags returns a value which is the OR of zero or more
+ * |EVP_CIPH_*| flags. */
+uint32_t EVP_CIPHER_flags(const EVP_CIPHER *cipher);
+
+/* EVP_CIPHER_mode returns one of the cipher mode values enumerated below. */
+uint32_t EVP_CIPHER_mode(const EVP_CIPHER *cipher);
+
+
+/* Key derivation. */
+
+/* EVP_BytesToKey generates a key and IV for the cipher |type| by iterating
+ * |md| |count| times using |data| and |salt|. On entry, the |key| and |iv|
+ * buffers must have enough space to hold a key and IV for |type|. It returns
+ * the length of the key on success or zero on error. */
+int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
+                   const uint8_t *salt, const uint8_t *data, size_t data_len,
+                   unsigned count, uint8_t *key, uint8_t *iv);
+
+
+/* Cipher modes (for |EVP_CIPHER_mode|). */
+
+#define EVP_CIPH_STREAM_CIPHER 0x0
+#define EVP_CIPH_ECB_MODE 0x1
+#define EVP_CIPH_CBC_MODE 0x2
+#define EVP_CIPH_CFB_MODE 0x3
+#define EVP_CIPH_OFB_MODE 0x4
+#define EVP_CIPH_CTR_MODE 0x5
+#define EVP_CIPH_GCM_MODE 0x6
+
+
+/* Cipher flags (for |EVP_CIPHER_flags|). */
+
+/* EVP_CIPH_VARIABLE_LENGTH indicates that the cipher takes a variable length
+ * key. */
+#define EVP_CIPH_VARIABLE_LENGTH 0x40
+
+/* EVP_CIPH_ALWAYS_CALL_INIT indicates that the |init| function for the cipher
+ * should always be called when initialising a new operation, even if the key
+ * is NULL to indicate that the same key is being used. */
+#define EVP_CIPH_ALWAYS_CALL_INIT 0x80
+
+/* EVP_CIPH_CUSTOM_IV indicates that the cipher manages the IV itself rather
+ * than keeping it in the |iv| member of |EVP_CIPHER_CTX|. */
+#define EVP_CIPH_CUSTOM_IV 0x100
+
+/* EVP_CIPH_CTRL_INIT indicates that EVP_CTRL_INIT should be used when
+ * initialising an |EVP_CIPHER_CTX|. */
+#define EVP_CIPH_CTRL_INIT 0x200
+
+/* EVP_CIPH_FLAG_CUSTOM_CIPHER indicates that the cipher manages blocking
+ * itself. This causes EVP_(En|De)crypt_ex to be simple wrapper functions. */
+#define EVP_CIPH_FLAG_CUSTOM_CIPHER 0x400
+
+/* EVP_CIPH_FLAG_AEAD_CIPHER specifies that the cipher is an AEAD. This is an
+ * older version of the proper AEAD interface. See aead.h for the current
+ * one. */
+#define EVP_CIPH_FLAG_AEAD_CIPHER 0x800
+
+
+/* Private functions. */
+
+/* EVP_CIPH_NO_PADDING disables padding in block ciphers. */
+#define EVP_CIPH_NO_PADDING 0x800
+
+/* 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
+#define EVP_CTRL_SET_RC2_KEY_BITS 0x3
+#define EVP_CTRL_GET_RC5_ROUNDS 0x4
+#define EVP_CTRL_SET_RC5_ROUNDS 0x5
+#define EVP_CTRL_RAND_KEY 0x6
+#define EVP_CTRL_PBE_PRF_NID 0x7
+#define EVP_CTRL_COPY 0x8
+#define EVP_CTRL_GCM_SET_IVLEN 0x9
+#define EVP_CTRL_GCM_GET_TAG 0x10
+#define EVP_CTRL_GCM_SET_TAG 0x11
+#define EVP_CTRL_GCM_SET_IV_FIXED 0x12
+#define EVP_CTRL_GCM_IV_GEN 0x13
+#define EVP_CTRL_AEAD_TLS1_AAD 0x16
+#define EVP_CTRL_AEAD_SET_MAC_KEY 0x17
+/* Set 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 */
+#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
+
+#define EVP_MAX_KEY_LENGTH 64
+#define EVP_MAX_IV_LENGTH 16
+#define EVP_MAX_BLOCK_LENGTH 32
+
+struct evp_cipher_ctx_st {
+  /* cipher contains the underlying cipher for this context. */
+  const EVP_CIPHER *cipher;
+
+  /* app_data is a pointer to opaque, user data. */
+  void *app_data;      /* application stuff */
+
+  /* cipher_data points to the |cipher| specific state. */
+  void *cipher_data;
+
+  /* key_len contains the length of the key, which may differ from
+   * |cipher->key_len| if the cipher can take a variable key length. */
+  unsigned key_len;
+
+  /* encrypt is one if encrypting and zero if decrypting. */
+  int encrypt;
+
+  /* flags contains the OR of zero or more |EVP_CIPH_*| flags, above. */
+  uint32_t flags;
+
+  /* oiv contains the original IV value. */
+  uint8_t oiv[EVP_MAX_IV_LENGTH];
+
+  /* iv contains the current IV value, which may have been updated. */
+  uint8_t iv[EVP_MAX_IV_LENGTH];
+
+  /* buf contains a partial block which is used by, for example, CTR mode to
+   * store unused keystream bytes. */
+  uint8_t buf[EVP_MAX_BLOCK_LENGTH];
+
+  /* buf_len contains the number of bytes of a partial block contained in
+   * |buf|. */
+  int buf_len;
+
+  /* num contains the number of bytes of |iv| which are valid for modes that
+   * manage partial blocks themselves. */
+  int num;
+
+  /* final_used is non-zero if the |final| buffer contains plaintext. */
+  int final_used;
+
+  /* block_mask contains |cipher->block_size| minus one. (The block size
+   * assumed to be a power of two.) */
+  int block_mask;
+
+  uint8_t final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */
+} /* EVP_CIPHER_CTX */;
+
+typedef struct evp_cipher_info_st {
+  const EVP_CIPHER *cipher;
+  unsigned char iv[EVP_MAX_IV_LENGTH];
+} EVP_CIPHER_INFO;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define CIPHER_F_EVP_CipherInit_ex 100
+#define CIPHER_F_EVP_EncryptFinal_ex 101
+#define CIPHER_F_EVP_DecryptFinal_ex 102
+#define CIPHER_F_EVP_CIPHER_CTX_ctrl 103
+#define CIPHER_F_aes_init_key 104
+#define CIPHER_F_aesni_init_key 105
+#define CIPHER_F_EVP_CIPHER_CTX_copy 106
+#define CIPHER_R_WRAP_MODE_NOT_ALLOWED 100
+#define CIPHER_R_AES_KEY_SETUP_FAILED 101
+#define CIPHER_R_INPUT_NOT_INITIALIZED 102
+#define CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH 103
+#define CIPHER_R_INITIALIZATION_ERROR 104
+#define CIPHER_R_CTRL_NOT_IMPLEMENTED 105
+#define CIPHER_R_NO_CIPHER_SET 106
+#define CIPHER_R_BAD_DECRYPT 107
+#define CIPHER_R_WRONG_FINAL_BLOCK_LENGTH 108
+#define CIPHER_R_CTRL_OPERATION_NOT_IMPLEMENTED 109
+
+#endif  /* OPENSSL_HEADER_CIPHER_H */
diff --git a/crypto/cipher/cipher_error.c b/crypto/cipher/cipher_error.c
new file mode 100644
index 0000000..bddecdd
--- /dev/null
+++ b/crypto/cipher/cipher_error.c
@@ -0,0 +1,38 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "cipher.h"
+
+const ERR_STRING_DATA CIPHER_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_EVP_CIPHER_CTX_copy, 0), "EVP_CIPHER_CTX_copy"},
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_EVP_CIPHER_CTX_ctrl, 0), "EVP_CIPHER_CTX_ctrl"},
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_EVP_CipherInit_ex, 0), "EVP_CipherInit_ex"},
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_EVP_DecryptFinal_ex, 0), "EVP_DecryptFinal_ex"},
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_EVP_EncryptFinal_ex, 0), "EVP_EncryptFinal_ex"},
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aes_init_key, 0), "aes_init_key"},
+  {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aesni_init_key, 0), "aesni_init_key"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_AES_KEY_SETUP_FAILED), "AES_KEY_SETUP_FAILED"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_BAD_DECRYPT), "BAD_DECRYPT"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_CTRL_NOT_IMPLEMENTED), "CTRL_NOT_IMPLEMENTED"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_CTRL_OPERATION_NOT_IMPLEMENTED), "CTRL_OPERATION_NOT_IMPLEMENTED"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH), "DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_INITIALIZATION_ERROR), "INITIALIZATION_ERROR"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_INPUT_NOT_INITIALIZED), "INPUT_NOT_INITIALIZED"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_NO_CIPHER_SET), "NO_CIPHER_SET"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_WRAP_MODE_NOT_ALLOWED), "WRAP_MODE_NOT_ALLOWED"},
+  {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_WRONG_FINAL_BLOCK_LENGTH), "WRONG_FINAL_BLOCK_LENGTH"},
+  {0, NULL},
+};
diff --git a/crypto/cipher/cipher_test.c b/crypto/cipher/cipher_test.c
new file mode 100644
index 0000000..3dadb8a
--- /dev/null
+++ b/crypto/cipher/cipher_test.c
@@ -0,0 +1,409 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/cipher.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+
+
+static void hexdump(FILE *f, const char *title, const uint8_t *s, int l) {
+  int n = 0;
+
+  fprintf(f, "%s", title);
+  for (; n < l; ++n) {
+    if ((n % 16) == 0)
+      fprintf(f, "\n%04x", n);
+    fprintf(f, " %02x", s[n]);
+  }
+  fprintf(f, "\n");
+}
+
+static int convert(uint8_t *s) {
+  uint8_t *d;
+
+  for (d = s; *s; s += 2, ++d) {
+    unsigned int n;
+
+    if (!s[1]) {
+      fprintf(stderr, "Odd number of hex digits!");
+      exit(4);
+    }
+    sscanf((char *)s, "%2x", &n);
+    *d = (uint8_t)n;
+  }
+  return s - d;
+}
+
+static char *sstrsep(char **string, const char *delim) {
+  char isdelim[256];
+  char *token = *string;
+
+  if (**string == 0) {
+    return NULL;
+  }
+
+  memset(isdelim, 0, 256);
+  isdelim[0] = 1;
+
+  while (*delim) {
+    isdelim[(uint8_t)(*delim)] = 1;
+    delim++;
+  }
+
+  while (!isdelim[(uint8_t)(**string)]) {
+    (*string)++;
+  }
+
+  if (**string) {
+    **string = 0;
+    (*string)++;
+  }
+
+  return token;
+}
+
+static uint8_t *ustrsep(char **p, const char *sep) {
+  return (uint8_t *)sstrsep(p, sep);
+}
+
+static void test1(const EVP_CIPHER *c, const uint8_t *key, int kn,
+                  const uint8_t *iv, int in, const uint8_t *plaintext, int pn,
+                  const uint8_t *ciphertext, int cn, const uint8_t *aad, int an,
+                  const uint8_t *tag, int tn, int encdec) {
+  EVP_CIPHER_CTX ctx;
+  uint8_t out[4096];
+  int outl, outl2, mode;
+
+  printf("Testing cipher %s%s\n", EVP_CIPHER_name(c),
+         (encdec == 1 ? "(encrypt)"
+                      : (encdec == 0 ? "(decrypt)" : "(encrypt/decrypt)")));
+  hexdump(stdout, "Key", key, kn);
+  if (in) {
+    hexdump(stdout, "IV", iv, in);
+  }
+  hexdump(stdout, "Plaintext", plaintext, pn);
+  hexdump(stdout, "Ciphertext", ciphertext, cn);
+  if (an) {
+    hexdump(stdout, "AAD", aad, an);
+  }
+  if (tn) {
+    hexdump(stdout, "Tag", tag, tn);
+  }
+  mode = EVP_CIPHER_mode(c);
+  if (kn != EVP_CIPHER_key_length(c)) {
+    fprintf(stderr, "Key length doesn't match, got %d expected %lu\n", kn,
+            (unsigned long)EVP_CIPHER_key_length(c));
+    exit(5);
+  }
+  EVP_CIPHER_CTX_init(&ctx);
+  if (encdec != 0) {
+    if (mode == EVP_CIPH_GCM_MODE) {
+      if (!EVP_EncryptInit_ex(&ctx, c, NULL, NULL, NULL)) {
+        fprintf(stderr, "EncryptInit failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(10);
+      }
+      if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, in, NULL)) {
+        fprintf(stderr, "IV length set failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(11);
+      }
+      if (!EVP_EncryptInit_ex(&ctx, NULL, NULL, key, iv)) {
+        fprintf(stderr, "Key/IV set failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(12);
+      }
+      if (an && !EVP_EncryptUpdate(&ctx, NULL, &outl, aad, an)) {
+        fprintf(stderr, "AAD set failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(13);
+      }
+    } else if (!EVP_EncryptInit_ex(&ctx, c, NULL, key, iv)) {
+      fprintf(stderr, "EncryptInit failed\n");
+      BIO_print_errors_fp(stderr);
+      exit(10);
+    }
+    EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+    if (!EVP_EncryptUpdate(&ctx, out, &outl, plaintext, pn)) {
+      fprintf(stderr, "Encrypt failed\n");
+      BIO_print_errors_fp(stderr);
+      exit(6);
+    }
+    if (!EVP_EncryptFinal_ex(&ctx, out + outl, &outl2)) {
+      fprintf(stderr, "EncryptFinal failed\n");
+      BIO_print_errors_fp(stderr);
+      exit(7);
+    }
+
+    if (outl + outl2 != cn) {
+      fprintf(stderr, "Ciphertext length mismatch got %d expected %d\n",
+              outl + outl2, cn);
+      exit(8);
+    }
+
+    if (memcmp(out, ciphertext, cn)) {
+      fprintf(stderr, "Ciphertext mismatch\n");
+      hexdump(stderr, "Got", out, cn);
+      hexdump(stderr, "Expected", ciphertext, cn);
+      exit(9);
+    }
+    if (mode == EVP_CIPH_GCM_MODE) {
+      uint8_t rtag[16];
+      /* Note: EVP_CTRL_CCM_GET_TAG has same value as
+       * EVP_CTRL_GCM_GET_TAG
+       */
+      if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, tn, rtag)) {
+        fprintf(stderr, "Get tag failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(14);
+      }
+      if (memcmp(rtag, tag, tn)) {
+        fprintf(stderr, "Tag mismatch\n");
+        hexdump(stderr, "Got", rtag, tn);
+        hexdump(stderr, "Expected", tag, tn);
+        exit(9);
+      }
+    }
+  }
+
+  if (encdec <= 0) {
+    if (mode == EVP_CIPH_GCM_MODE) {
+      if (!EVP_DecryptInit_ex(&ctx, c, NULL, NULL, NULL)) {
+        fprintf(stderr, "EncryptInit failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(10);
+      }
+      if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, in, NULL)) {
+        fprintf(stderr, "IV length set failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(11);
+      }
+      if (!EVP_DecryptInit_ex(&ctx, NULL, NULL, key, iv)) {
+        fprintf(stderr, "Key/IV set failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(12);
+      }
+      if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, tn, (void *)tag)) {
+        fprintf(stderr, "Set tag failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(14);
+      }
+      if (an && !EVP_DecryptUpdate(&ctx, NULL, &outl, aad, an)) {
+        fprintf(stderr, "AAD set failed\n");
+        BIO_print_errors_fp(stderr);
+        exit(13);
+      }
+    } else if (!EVP_DecryptInit_ex(&ctx, c, NULL, key, iv)) {
+      fprintf(stderr, "DecryptInit failed\n");
+      BIO_print_errors_fp(stderr);
+      exit(11);
+    }
+    EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+    if (!EVP_DecryptUpdate(&ctx, out, &outl, ciphertext, cn)) {
+      fprintf(stderr, "Decrypt failed\n");
+      BIO_print_errors_fp(stderr);
+      exit(6);
+    }
+    outl2 = 0;
+    if (!EVP_DecryptFinal_ex(&ctx, out + outl, &outl2)) {
+      fprintf(stderr, "DecryptFinal failed\n");
+      BIO_print_errors_fp(stderr);
+      exit(7);
+    }
+
+    if (outl + outl2 != pn) {
+      fprintf(stderr, "Plaintext length mismatch got %d expected %d\n",
+              outl + outl2, pn);
+      exit(8);
+    }
+
+    if (memcmp(out, plaintext, pn)) {
+      fprintf(stderr, "Plaintext mismatch\n");
+      hexdump(stderr, "Got", out, pn);
+      hexdump(stderr, "Expected", plaintext, pn);
+      exit(9);
+    }
+  }
+
+  EVP_CIPHER_CTX_cleanup(&ctx);
+
+  printf("\n");
+}
+
+static int test_cipher(const char *cipher, const uint8_t *key, int kn,
+                       const uint8_t *iv, int in, const uint8_t *plaintext,
+                       int pn, const uint8_t *ciphertext, int cn,
+                       const uint8_t *aad, int an, const uint8_t *tag, int tn,
+                       int encdec) {
+  const EVP_CIPHER *c;
+
+  if (strcmp(cipher, "DES-CBC") == 0) {
+    c = EVP_des_cbc();
+  } else if (strcmp(cipher, "DES-EDE3-CBC") == 0) {
+    c = EVP_des_ede3_cbc();
+  } else if (strcmp(cipher, "RC4") == 0) {
+    c = EVP_rc4();
+  } else if (strcmp(cipher, "AES-128-ECB") == 0) {
+    c = EVP_aes_128_ecb();
+  } else if (strcmp(cipher, "AES-256-ECB") == 0) {
+    c = EVP_aes_256_ecb();
+  } else if (strcmp(cipher, "AES-128-CBC") == 0) {
+    c = EVP_aes_128_cbc();
+  } else if (strcmp(cipher, "AES-128-GCM") == 0) {
+    c = EVP_aes_128_gcm();
+  } else if (strcmp(cipher, "AES-256-CBC") == 0) {
+    c = EVP_aes_256_cbc();
+  } else if (strcmp(cipher, "AES-128-CTR") == 0) {
+    c = EVP_aes_128_ctr();
+  } else if (strcmp(cipher, "AES-256-CTR") == 0) {
+    c = EVP_aes_256_ctr();
+  } else if (strcmp(cipher, "AES-256-GCM") == 0) {
+    c = EVP_aes_256_gcm();
+  } else {
+    fprintf(stderr, "Unknown cipher type %s\n", cipher);
+    return 0;
+  }
+
+  test1(c, key, kn, iv, in, plaintext, pn, ciphertext, cn, aad, an, tag, tn,
+        encdec);
+
+  return 1;
+}
+
+int main(int argc, char **argv) {
+  const char *input_file;
+  FILE *f;
+
+  if (argc != 2) {
+    fprintf(stderr, "%s <test file>\n", argv[0]);
+    return 1;
+  }
+
+  input_file = argv[1];
+
+  f = fopen(input_file, "r");
+  if (!f) {
+    perror(input_file);
+    return 2;
+  }
+
+  ERR_load_crypto_strings();
+
+  for (;;) {
+    char line[4096];
+    char *p;
+    char *cipher;
+    uint8_t *iv, *key, *plaintext, *ciphertext, *aad, *tag;
+    int encdec;
+    int kn, in, pn, cn;
+    int an = 0;
+    int tn = 0;
+
+    if (!fgets((char *)line, sizeof line, f)) {
+      break;
+    }
+    if (line[0] == '#' || line[0] == '\n') {
+      continue;
+    }
+    p = line;
+    cipher = sstrsep(&p, ":");
+    key = ustrsep(&p, ":");
+    iv = ustrsep(&p, ":");
+    plaintext = ustrsep(&p, ":");
+    ciphertext = ustrsep(&p, ":");
+    if (p[-1] == '\n') {
+      encdec = -1;
+      p[-1] = '\0';
+      tag = aad = NULL;
+      an = tn = 0;
+    } else {
+      aad = ustrsep(&p, ":");
+      tag = ustrsep(&p, ":");
+      if (tag == NULL) {
+        p = (char *)aad;
+        tag = aad = NULL;
+        an = tn = 0;
+      }
+      if (p[-1] == '\n') {
+        encdec = -1;
+        p[-1] = '\0';
+      } else
+        encdec = atoi(sstrsep(&p, "\n"));
+    }
+
+    kn = convert(key);
+    in = convert(iv);
+    pn = convert(plaintext);
+    cn = convert(ciphertext);
+    if (aad) {
+      an = convert(aad);
+      tn = convert(tag);
+    }
+
+    if (!test_cipher(cipher, key, kn, iv, in, plaintext, pn, ciphertext, cn,
+                     aad, an, tag, tn, encdec)) {
+      return 3;
+    }
+  }
+  fclose(f);
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/cipher/cipher_test.txt b/crypto/cipher/cipher_test.txt
new file mode 100644
index 0000000..b250df3
--- /dev/null
+++ b/crypto/cipher/cipher_test.txt
@@ -0,0 +1,81 @@
+# RC4 tests (from rc4test)
+RC4:0123456789abcdef0123456789abcdef::0123456789abcdef:75b7878099e0c596
+RC4:0123456789abcdef0123456789abcdef::0000000000000000:7494c2e7104b0879
+RC4:00000000000000000000000000000000::0000000000000000:de188941a3375d3a
+RC4:ef012345ef012345ef012345ef012345::0000000000000000000000000000000000000000:d6a141a7ec3c38dfbd615a1162e1c7ba36b67858
+RC4:0123456789abcdef0123456789abcdef::123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345678:66a0949f8af7d6891f7f832ba833c00c892ebe30143ce28740011ecf
+RC4:ef012345ef012345ef012345ef012345::00000000000000000000:d6a141a7ec3c38dfbd61
+
+# DES EDE3 CBC tests (from destest)
+DES-EDE3-CBC:0123456789abcdeff1e0d3c2b5a49786fedcba9876543210:fedcba9876543210:37363534333231204E6F77206973207468652074696D6520666F722000000000:3FE301C962AC01D02213763C1CBD4CDC799657C064ECF5D41C673812CFDE9675
+
+# AES 128 ECB tests (from FIPS-197 test vectors, encrypt)
+AES-128-ECB:000102030405060708090A0B0C0D0E0F::00112233445566778899AABBCCDDEEFF:69C4E0D86A7B0430D8CDB78070B4C55A:1
+
+# AES 256 ECB tests (from FIPS-197 test vectors, encrypt)
+AES-256-ECB:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F::00112233445566778899AABBCCDDEEFF:8EA2B7CA516745BFEAFC49904B496089:1
+
+# AES 128 CBC tests (from NIST test vectors, decrypt)
+
+# AES tests from NIST document SP800-38A
+# For all ECB encrypts and decrypts, the transformed sequence is
+#   AES-bits-ECB:key::plaintext:ciphertext:encdec
+# ECB-AES128.Encrypt and ECB-AES128.Decrypt
+AES-128-ECB:2B7E151628AED2A6ABF7158809CF4F3C::6BC1BEE22E409F96E93D7E117393172A:3AD77BB40D7A3660A89ECAF32466EF97
+AES-128-ECB:2B7E151628AED2A6ABF7158809CF4F3C::AE2D8A571E03AC9C9EB76FAC45AF8E51:F5D3D58503B9699DE785895A96FDBAAF
+AES-128-ECB:2B7E151628AED2A6ABF7158809CF4F3C::30C81C46A35CE411E5FBC1191A0A52EF:43B1CD7F598ECE23881B00E3ED030688
+AES-128-ECB:2B7E151628AED2A6ABF7158809CF4F3C::F69F2445DF4F9B17AD2B417BE66C3710:7B0C785E27E8AD3F8223207104725DD4
+# ECB-AES256.Encrypt and ECB-AES256.Decrypt
+AES-256-ECB:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4::6BC1BEE22E409F96E93D7E117393172A:F3EED1BDB5D2A03C064B5A7E3DB181F8
+AES-256-ECB:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4::AE2D8A571E03AC9C9EB76FAC45AF8E51:591CCB10D410ED26DC5BA74A31362870
+AES-256-ECB:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4::30C81C46A35CE411E5FBC1191A0A52EF:B6ED21B99CA6F4F9F153E7B1BEAFED1D
+AES-256-ECB:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4::F69F2445DF4F9B17AD2B417BE66C3710:23304B7A39F9F3FF067D8D8F9E24ECC7
+# For all CBC encrypts and decrypts, the transformed sequence is
+#   AES-bits-CBC:key:IV/ciphertext':plaintext:ciphertext:encdec
+# CBC-AES128.Encrypt and CBC-AES128.Decrypt
+AES-128-CBC:2B7E151628AED2A6ABF7158809CF4F3C:000102030405060708090A0B0C0D0E0F:6BC1BEE22E409F96E93D7E117393172A:7649ABAC8119B246CEE98E9B12E9197D
+AES-128-CBC:2B7E151628AED2A6ABF7158809CF4F3C:7649ABAC8119B246CEE98E9B12E9197D:AE2D8A571E03AC9C9EB76FAC45AF8E51:5086CB9B507219EE95DB113A917678B2
+AES-128-CBC:2B7E151628AED2A6ABF7158809CF4F3C:5086CB9B507219EE95DB113A917678B2:30C81C46A35CE411E5FBC1191A0A52EF:73BED6B8E3C1743B7116E69E22229516
+AES-128-CBC:2B7E151628AED2A6ABF7158809CF4F3C:73BED6B8E3C1743B7116E69E22229516:F69F2445DF4F9B17AD2B417BE66C3710:3FF1CAA1681FAC09120ECA307586E1A7
+# CBC-AES256.Encrypt and CBC-AES256.Decrypt
+AES-256-CBC:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4:000102030405060708090A0B0C0D0E0F:6BC1BEE22E409F96E93D7E117393172A:F58C4C04D6E5F1BA779EABFB5F7BFBD6
+AES-256-CBC:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4:F58C4C04D6E5F1BA779EABFB5F7BFBD6:AE2D8A571E03AC9C9EB76FAC45AF8E51:9CFC4E967EDB808D679F777BC6702C7D
+AES-256-CBC:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4:9CFC4E967EDB808D679F777BC6702C7D:30C81C46A35CE411E5FBC1191A0A52EF:39F23369A9D9BACFA530E26304231461
+AES-256-CBC:603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4:39F23369A9D9BACFA530E26304231461:F69F2445DF4F9B17AD2B417BE66C3710:B2EB05E2C39BE9FCDA6C19078C6A9D1B
+
+# AES Counter test vectors from RFC3686
+AES-128-CTR:AE6852F8121067CC4BF7A5765577F39E:00000030000000000000000000000001:53696E676C6520626C6F636B206D7367:E4095D4FB7A7B3792D6175A3261311B8:1
+AES-128-CTR:7E24067817FAE0D743D6CE1F32539163:006CB6DBC0543B59DA48D90B00000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:5104A106168A72D9790D41EE8EDAD388EB2E1EFC46DA57C8FCE630DF9141BE28:1
+AES-128-CTR:7691BE035E5020A8AC6E618529F9A0DC:00E0017B27777F3F4A1786F000000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223:C1CF48A89F2FFDD9CF4652E9EFDB72D74540A42BDE6D7836D59A5CEAAEF3105325B2072F:1
+
+AES-256-CTR:776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104:00000060DB5672C97AA8F0B200000001:53696E676C6520626C6F636B206D7367:145AD01DBF824EC7560863DC71E3E0C0:1
+AES-256-CTR:F6D66D6BD52D59BB0796365879EFF886C66DD51A5B6A99744B50590C87A23884:00FAAC24C1585EF15A43D87500000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:F05E231B3894612C49EE000B804EB2A9B8306B508F839D6A5530831D9344AF1C:1
+AES-256-CTR:FF7A617CE69148E4F1726E2F43581DE2AA62D9F805532EDFF1EED687FB54153D:001CC5B751A51D70A1C1114800000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223:EB6C52821D0BBBF7CE7594462ACA4FAAB407DF866569FD07F48CC0B583D6071F1EC0E6B8:1
+
+# AES GCM test vectors from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
+AES-128-GCM:00000000000000000000000000000000:000000000000000000000000::::58e2fccefa7e3061367f1d57a4e7455a
+AES-128-GCM:00000000000000000000000000000000:000000000000000000000000:00000000000000000000000000000000:0388dace60b6a392f328c2b971b2fe78::ab6e47d42cec13bdf53a67b21257bddf
+AES-128-GCM:feffe9928665731c6d6a8f9467308308:cafebabefacedbaddecaf888:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255:42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985::4d5c2af327cd64a62cf35abd2ba6fab4
+AES-128-GCM:feffe9928665731c6d6a8f9467308308:cafebabefacedbaddecaf888:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39:42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091:feedfacedeadbeeffeedfacedeadbeefabaddad2:5bc94fbc3221a5db94fae95ae7121a47
+AES-128-GCM:feffe9928665731c6d6a8f9467308308:cafebabefacedbad:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39:61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598:feedfacedeadbeeffeedfacedeadbeefabaddad2:3612d2e79e3b0785561be14aaca2fccb
+AES-128-GCM:feffe9928665731c6d6a8f9467308308:9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39:8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5:feedfacedeadbeeffeedfacedeadbeefabaddad2:619cc5aefffe0bfa462af43c1699d050
+AES-256-GCM:0000000000000000000000000000000000000000000000000000000000000000:000000000000000000000000::::530f8afbc74536b9a963b4f1c4cb738b
+AES-256-GCM:0000000000000000000000000000000000000000000000000000000000000000:000000000000000000000000:00000000000000000000000000000000:cea7403d4d606b6e074ec5d3baf39d18::d0d1c8a799996bf0265b98b5d48ab919
+AES-256-GCM:feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308:cafebabefacedbaddecaf888:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255:522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad::b094dac5d93471bdec1a502270e3cc6c
+AES-256-GCM:feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308:cafebabefacedbaddecaf888:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39:522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662:feedfacedeadbeeffeedfacedeadbeefabaddad2:76fc6ece0f4e1768cddf8853bb2d551b
+AES-256-GCM:feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308:cafebabefacedbad:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39:c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f:feedfacedeadbeeffeedfacedeadbeefabaddad2:3a337dbf46a792c45e454913fe2ea8f2
+AES-256-GCM:feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308:9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b:d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39:5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f:feedfacedeadbeeffeedfacedeadbeefabaddad2:a44a8266ee1c8eb0c8b5d4cf5ae9f19a
+# local add-ons, primarily streaming ghash tests
+# 128 bytes aad
+AES-128-GCM:00000000000000000000000000000000:000000000000000000000000:::d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad:5fea793a2d6f974d37e68e0cb8ff9492
+# 48 bytes plaintext
+AES-128-GCM:00000000000000000000000000000000:000000000000000000000000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0::9dd0a376b08e40eb00c35f29f9ea61a4
+# 80 bytes plaintext
+AES-128-GCM:00000000000000000000000000000000:000000000000000000000000:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0c94da219118e297d7b7ebcbcc9c388f28ade7d85a8ee35616f7124a9d5270291::98885a3a22bd4742fe7b72172193b163
+# 128 bytes plaintext
+AES-128-GCM:00000000000000000000000000000000:000000000000000000000000:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0c94da219118e297d7b7ebcbcc9c388f28ade7d85a8ee35616f7124a9d527029195b84d1b96c690ff2f2de30bf2ec89e00253786e126504f0dab90c48a30321de3345e6b0461e7c9e6c6b7afedde83f40::cac45f60e31efd3b5a43b98a22ce1aa1
+# 192 bytes plaintext, iv is chosen so that initial counter LSB is 0xFF
+AES-128-GCM:00000000000000000000000000000000:ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606::566f8ef683078bfdeeffa869d751a017
+# 80 bytes plaintext, submitted by Intel
+AES-128-GCM:843ffcf5d2b72694d19ed01d01249412:dbcca32ebf9b804617c3aa9e:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f:6268c6fa2a80b2d137467f092f657ac04d89be2beaa623d61b5a868c8f03ff95d3dcee23ad2f1ab3a6c80eaf4b140eb05de3457f0fbc111a6b43d0763aa422a3013cf1dc37fe417d1fbfc449b75d4cc5:00000000000000000000000000000000101112131415161718191a1b1c1d1e1f:3b629ccfbc1119b7319e1dce2cd6fd6d
+
diff --git a/crypto/cipher/derive_key.c b/crypto/cipher/derive_key.c
new file mode 100644
index 0000000..9e1634a
--- /dev/null
+++ b/crypto/cipher/derive_key.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/cipher.h>
+
+#include <assert.h>
+
+#include <openssl/digest.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+#define PKCS5_SALT_LEN 8
+
+int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
+                   const uint8_t *salt, const uint8_t *data, size_t data_len,
+                   unsigned count, uint8_t *key, uint8_t *iv) {
+  EVP_MD_CTX c;
+  uint8_t md_buf[EVP_MAX_MD_SIZE];
+  unsigned niv, nkey, addmd = 0;
+  unsigned mds = 0, i;
+  int rv = 0;
+
+  nkey = type->key_len;
+  niv = type->iv_len;
+
+  assert(nkey <= EVP_MAX_KEY_LENGTH);
+  assert(niv <= EVP_MAX_IV_LENGTH);
+
+  if (data == NULL) {
+    return nkey;
+  }
+
+  EVP_MD_CTX_init(&c);
+  for (;;) {
+    if (!EVP_DigestInit_ex(&c, md, NULL)) {
+      return 0;
+    }
+    if (addmd++) {
+      if (!EVP_DigestUpdate(&c, md_buf, mds)) {
+        goto err;
+      }
+    }
+    if (!EVP_DigestUpdate(&c, data, data_len)) {
+      goto err;
+    }
+    if (salt != NULL) {
+      if (!EVP_DigestUpdate(&c, salt, PKCS5_SALT_LEN)) {
+        goto err;
+      }
+    }
+    if (!EVP_DigestFinal_ex(&c, md_buf, &mds)) {
+      goto err;
+    }
+
+    for (i = 1; i < count; i++) {
+      if (!EVP_DigestInit_ex(&c, md, NULL) ||
+          !EVP_DigestUpdate(&c, md_buf, mds) ||
+          !EVP_DigestFinal_ex(&c, md_buf, &mds)) {
+        goto err;
+      }
+    }
+
+    i = 0;
+    if (nkey) {
+      for (;;) {
+        if (nkey == 0 || i == mds) {
+          break;
+        }
+        if (key != NULL) {
+          *(key++) = md_buf[i];
+        }
+        nkey--;
+        i++;
+      }
+    }
+
+    if (niv && i != mds) {
+      for (;;) {
+        if (niv == 0 || i == mds) {
+          break;
+        }
+        if (iv != NULL) {
+          *(iv++) = md_buf[i];
+        }
+        niv--;
+        i++;
+      }
+    }
+    if (nkey == 0 && niv == 0) {
+      break;
+    }
+  }
+  rv = type->key_len;
+
+err:
+  EVP_MD_CTX_cleanup(&c);
+  OPENSSL_cleanse(md_buf, EVP_MAX_MD_SIZE);
+  return rv;
+}
diff --git a/crypto/cipher/e_aes.c b/crypto/cipher/e_aes.c
new file mode 100644
index 0000000..c22e306
--- /dev/null
+++ b/crypto/cipher/e_aes.c
@@ -0,0 +1,915 @@
+/* ====================================================================
+ * Copyright (c) 2001-2011 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <openssl/aes.h>
+#include <openssl/cipher.h>
+#include <openssl/cpu.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/modes.h>
+#include <openssl/rand.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+#include "../modes/internal.h"
+
+
+typedef struct {
+  union {
+    double align;
+    AES_KEY ks;
+  } ks;
+  block128_f block;
+  union {
+    cbc128_f cbc;
+    ctr128_f ctr;
+  } stream;
+} EVP_AES_KEY;
+
+typedef struct {
+  union {
+    double align;
+    AES_KEY ks;
+  } ks;        /* AES key schedule to use */
+  int key_set; /* Set if key initialised */
+  int iv_set;  /* Set if an iv is set */
+  GCM128_CONTEXT gcm;
+  uint8_t *iv; /* Temporary IV store */
+  int ivlen;         /* IV length */
+  int taglen;
+  int iv_gen;      /* It is OK to generate IVs */
+  int tls_aad_len; /* TLS AAD length */
+  ctr128_f ctr;
+} EVP_AES_GCM_CTX;
+
+
+void AES_ctr32_encrypt(const uint8_t *in, uint8_t *out, size_t blocks,
+                       const AES_KEY *key, const uint8_t ivec[AES_BLOCK_SIZE]);
+
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86_64) || defined(OPENSSL_X86))
+#define VPAES
+extern unsigned int OPENSSL_ia32cap_P[];
+
+static char vpaes_capable() {
+  return (OPENSSL_ia32cap_P[1] & (1 << (41 - 32))) != 0;
+}
+
+#if defined(OPENSSL_X86_64)
+#define BSAES
+static char bsaes_capable() {
+  return vpaes_capable();
+}
+#endif
+
+#elif !defined(OPENSSL_NO_ASM) && defined(OPENSSL_ARM)
+#include "../arm_arch.h"
+#if __ARM_ARCH__ >= 7
+#define BSAES
+static char bsaes_capable() {
+  return CRYPTO_is_NEON_capable();
+}
+#endif  /* __ARM_ARCH__ >= 7 */
+#endif  /* OPENSSL_ARM */
+
+#if defined(BSAES)
+/* On platforms where BSAES gets defined (just above), then these functions are
+ * provided by asm. */
+void bsaes_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                       const AES_KEY *key, uint8_t ivec[16], int enc);
+void bsaes_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t len,
+                                const AES_KEY *key, const uint8_t ivec[16]);
+#else
+static char bsaes_capable() {
+  return 0;
+}
+
+/* On other platforms, bsaes_capable() will always return false and so the
+ * following will never be called. */
+void bsaes_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                       const AES_KEY *key, uint8_t ivec[16], int enc) {
+  abort();
+}
+
+void bsaes_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t len,
+                                const AES_KEY *key, const uint8_t ivec[16]) {
+  abort();
+}
+#endif
+
+#if defined(VPAES)
+/* On platforms where BSAES gets defined (just above), then these functions are
+ * provided by asm. */
+int vpaes_set_encrypt_key(const uint8_t *userKey, int bits, AES_KEY *key);
+int vpaes_set_decrypt_key(const uint8_t *userKey, int bits, AES_KEY *key);
+
+void vpaes_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+void vpaes_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+
+void vpaes_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                       const AES_KEY *key, uint8_t *ivec, int enc);
+#else
+static char vpaes_capable() {
+  return 0;
+}
+
+/* On other platforms, vpaes_capable() will always return false and so the
+ * following will never be called. */
+int vpaes_set_encrypt_key(const uint8_t *userKey, int bits, AES_KEY *key) {
+  abort();
+}
+int vpaes_set_decrypt_key(const uint8_t *userKey, int bits, AES_KEY *key) {
+  abort();
+}
+void vpaes_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  abort();
+}
+void vpaes_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  abort();
+}
+void vpaes_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                       const AES_KEY *key, uint8_t *ivec, int enc) {
+  abort();
+}
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86_64) || defined(OPENSSL_X86))
+int aesni_set_encrypt_key(const uint8_t *userKey, int bits, AES_KEY *key);
+int aesni_set_decrypt_key(const uint8_t *userKey, int bits, AES_KEY *key);
+
+void aesni_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+void aesni_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+
+void aesni_ecb_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                       const AES_KEY *key, int enc);
+void aesni_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                       const AES_KEY *key, uint8_t *ivec, int enc);
+
+void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
+                                const void *key, const uint8_t *ivec);
+
+#if defined(OPENSSL_X86_64)
+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);
+#define AES_gcm_encrypt aesni_gcm_encrypt
+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);
+#define AES_gcm_decrypt aesni_gcm_decrypt
+void gcm_ghash_avx(uint64_t Xi[2], const u128 Htable[16], const uint8_t *in,
+                   size_t len);
+#define AES_GCM_ASM(gctx) \
+  (gctx->ctr == aesni_ctr32_encrypt_blocks && gctx->gcm.ghash == gcm_ghash_avx)
+#endif
+
+#else
+
+/* On other platforms, aesni_capable() will always return false and so the
+ * following will never be called. */
+void aesni_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  abort();
+}
+int aesni_set_encrypt_key(const uint8_t *userKey, int bits, AES_KEY *key) {
+  abort();
+}
+void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
+                                const void *key, const uint8_t *ivec) {
+  abort();
+}
+
+#endif
+
+static int aes_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                        const uint8_t *iv, int enc) {
+  int ret, mode;
+  EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
+
+  mode = ctx->cipher->flags & EVP_CIPH_MODE_MASK;
+  if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
+    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;
+    } 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;
+    } 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;
+    }
+  } 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;
+  } 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;
+  } 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;
+  }
+
+  if (ret < 0) {
+    OPENSSL_PUT_ERROR(CIPHER, aes_init_key, CIPHER_R_AES_KEY_SETUP_FAILED);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int aes_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len) {
+  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);
+  } else if (ctx->encrypt) {
+    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+  } else {
+    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+  }
+
+  return 1;
+}
+
+static int aes_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len) {
+  size_t bl = ctx->cipher->block_size;
+  size_t i;
+  EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
+
+  if (len < bl) {
+    return 1;
+  }
+
+  for (i = 0, len -= bl; i <= len; i += bl) {
+    (*dat->block)(in + i, out + i, &dat->ks);
+  }
+
+  return 1;
+}
+
+static int aes_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len) {
+  unsigned int num = ctx->num;
+  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, &num,
+                                dat->stream.ctr);
+  } else {
+    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks, ctx->iv, ctx->buf, &num,
+                          dat->block);
+  }
+  ctx->num = (size_t)num;
+  return 1;
+}
+
+static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                            const uint8_t *iv, int enc) {
+  EVP_AES_GCM_CTX *gctx = ctx->cipher_data;
+  if (!iv && !key) {
+    return 1;
+  }
+
+  if (key) {
+    if (bsaes_capable()) {
+      AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks.ks);
+      CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks, (block128_f)AES_encrypt);
+      gctx->ctr = (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    } else if (vpaes_capable()) {
+      vpaes_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks.ks);
+      CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks, (block128_f)vpaes_encrypt);
+      gctx->ctr = NULL;
+    } else {
+      AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks.ks);
+      CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks, (block128_f)AES_encrypt);
+      gctx->ctr = NULL;
+    }
+
+    /* If we have an iv can set it directly, otherwise use
+     * saved IV. */
+    if (iv == NULL && gctx->iv_set) {
+      iv = gctx->iv;
+    }
+    if (iv) {
+      CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+      gctx->iv_set = 1;
+    }
+    gctx->key_set = 1;
+  } else {
+    /* If key set use IV, otherwise copy */
+    if (gctx->key_set) {
+      CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+    } else {
+      memcpy(gctx->iv, iv, gctx->ivlen);
+    }
+    gctx->iv_set = 1;
+    gctx->iv_gen = 0;
+  }
+  return 1;
+}
+
+static int aes_gcm_cleanup(EVP_CIPHER_CTX *c) {
+  EVP_AES_GCM_CTX *gctx = c->cipher_data;
+  OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
+  if (gctx->iv != c->iv) {
+    OPENSSL_free(gctx->iv);
+  }
+  return 1;
+}
+
+/* increment counter (64-bit int) by 1 */
+static void ctr64_inc(uint8_t *counter) {
+  int n = 8;
+  uint8_t c;
+
+  do {
+    --n;
+    c = counter[n];
+    ++c;
+    counter[n] = c;
+    if (c) {
+      return;
+    }
+  } while (n);
+}
+
+static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) {
+  EVP_AES_GCM_CTX *gctx = c->cipher_data;
+  switch (type) {
+    case EVP_CTRL_INIT:
+      gctx->key_set = 0;
+      gctx->iv_set = 0;
+      gctx->ivlen = c->cipher->iv_len;
+      gctx->iv = c->iv;
+      gctx->taglen = -1;
+      gctx->iv_gen = 0;
+      gctx->tls_aad_len = -1;
+      return 1;
+
+    case EVP_CTRL_GCM_SET_IVLEN:
+      if (arg <= 0) {
+        return 0;
+      }
+
+      /* Allocate memory for IV if needed */
+      if (arg > EVP_MAX_IV_LENGTH && arg > gctx->ivlen) {
+        if (gctx->iv != c->iv) {
+          OPENSSL_free(gctx->iv);
+        }
+        gctx->iv = OPENSSL_malloc(arg);
+        if (!gctx->iv) {
+          return 0;
+        }
+      }
+      gctx->ivlen = arg;
+      return 1;
+
+    case EVP_CTRL_GCM_SET_TAG:
+      if (arg <= 0 || arg > 16 || c->encrypt) {
+        return 0;
+      }
+      memcpy(c->buf, ptr, arg);
+      gctx->taglen = arg;
+      return 1;
+
+    case EVP_CTRL_GCM_GET_TAG:
+      if (arg <= 0 || arg > 16 || !c->encrypt || gctx->taglen < 0) {
+        return 0;
+      }
+      memcpy(ptr, c->buf, arg);
+      return 1;
+
+    case EVP_CTRL_GCM_SET_IV_FIXED:
+      /* Special case: -1 length restores whole IV */
+      if (arg == -1) {
+        memcpy(gctx->iv, ptr, gctx->ivlen);
+        gctx->iv_gen = 1;
+        return 1;
+      }
+      /* Fixed field must be at least 4 bytes and invocation field
+       * at least 8. */
+      if (arg < 4 || (gctx->ivlen - arg) < 8) {
+        return 0;
+      }
+      if (arg) {
+        memcpy(gctx->iv, ptr, arg);
+      }
+      if (c->encrypt &&
+          RAND_pseudo_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0) {
+        return 0;
+      }
+      gctx->iv_gen = 1;
+      return 1;
+
+    case EVP_CTRL_GCM_IV_GEN:
+      if (gctx->iv_gen == 0 || gctx->key_set == 0) {
+        return 0;
+      }
+      CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+      if (arg <= 0 || arg > gctx->ivlen) {
+        arg = gctx->ivlen;
+      }
+      memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
+      /* Invocation field will be at least 8 bytes in size and
+       * so no need to check wrap around or increment more than
+       * last 8 bytes. */
+      ctr64_inc(gctx->iv + gctx->ivlen - 8);
+      gctx->iv_set = 1;
+      return 1;
+
+    case EVP_CTRL_GCM_SET_IV_INV:
+      if (gctx->iv_gen == 0 || gctx->key_set == 0 || c->encrypt) {
+        return 0;
+      }
+      memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
+      CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+      gctx->iv_set = 1;
+      return 1;
+
+    case EVP_CTRL_AEAD_TLS1_AAD:
+      /* Save the AAD for later use */
+      if (arg != 13) {
+        return 0;
+      }
+      memcpy(c->buf, ptr, arg);
+      gctx->tls_aad_len = arg;
+      {
+        unsigned int len = c->buf[arg - 2] << 8 | c->buf[arg - 1];
+        /* Correct length for explicit IV */
+        len -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
+        /* If decrypting correct for tag too */
+        if (!c->encrypt)
+          len -= EVP_GCM_TLS_TAG_LEN;
+        c->buf[arg - 2] = len >> 8;
+        c->buf[arg - 1] = len & 0xff;
+      }
+
+      /* Extra padding: tag appended to record */
+      return EVP_GCM_TLS_TAG_LEN;
+
+    default:
+      return -1;
+  }
+}
+
+/* Handle TLS GCM packet format. This consists of the last portion of the IV
+ * followed by the payload and finally the tag. On encrypt generate IV, encrypt
+ * payload and write the tag. On verify retrieve IV, decrypt payload and verify
+ * tag. */
+
+static int aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out,
+                              const uint8_t *in, size_t len) {
+  EVP_AES_GCM_CTX *gctx = ctx->cipher_data;
+  int rv = -1;
+  /* Encrypt/decrypt must be performed in place */
+  if (out != in || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN)) {
+    return -1;
+  }
+  /* Set IV from start of buffer or generate IV and write to start
+   * of buffer.
+   */
+  if (EVP_CIPHER_CTX_ctrl(
+          ctx, ctx->encrypt ? EVP_CTRL_GCM_IV_GEN : EVP_CTRL_GCM_SET_IV_INV,
+          EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
+    goto err;
+  /* Use saved AAD */
+  if (!CRYPTO_gcm128_aad(&gctx->gcm, ctx->buf, gctx->tls_aad_len))
+    goto err;
+  /* Fix buffer and length to point to payload */
+  in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+  out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+  len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+  if (ctx->encrypt) {
+    /* Encrypt payload */
+    if (gctx->ctr) {
+      size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+      if (len >= 32 && AES_GCM_ASM(gctx)) {
+        if (!CRYPTO_gcm128_encrypt(&gctx->gcm, NULL, NULL, 0))
+          return -1;
+
+        bulk = AES_gcm_encrypt(in, out, len, gctx->gcm.key, gctx->gcm.Yi.c,
+                               gctx->gcm.Xi.u);
+        gctx->gcm.len.u[1] += bulk;
+      }
+#endif
+      if (!CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm, in + bulk, out + bulk,
+                                      len - bulk, gctx->ctr))
+        goto err;
+    } else {
+      size_t bulk = 0;
+      if (!CRYPTO_gcm128_encrypt(&gctx->gcm, in + bulk, out + bulk, len - bulk))
+        goto err;
+    }
+    out += len;
+    /* Finally write tag */
+    CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
+    rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+  } else {
+    /* Decrypt */
+    if (gctx->ctr) {
+      size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+      if (len >= 16 && AES_GCM_ASM(gctx)) {
+        if (!CRYPTO_gcm128_decrypt(&gctx->gcm, NULL, NULL, 0))
+          return -1;
+
+        bulk = AES_gcm_decrypt(in, out, len, gctx->gcm.key, gctx->gcm.Yi.c,
+                               gctx->gcm.Xi.u);
+        gctx->gcm.len.u[1] += bulk;
+      }
+#endif
+      if (!CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm, in + bulk, out + bulk,
+                                      len - bulk, gctx->ctr))
+        goto err;
+    } else {
+      size_t bulk = 0;
+      if (!CRYPTO_gcm128_decrypt(&gctx->gcm, in + bulk, out + bulk, len - bulk))
+        goto err;
+    }
+    /* Retrieve tag */
+    CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, EVP_GCM_TLS_TAG_LEN);
+    /* If tag mismatch wipe buffer */
+    if (memcmp(ctx->buf, in + len, EVP_GCM_TLS_TAG_LEN)) {
+      OPENSSL_cleanse(out, len);
+      goto err;
+    }
+    rv = len;
+  }
+
+err:
+  gctx->iv_set = 0;
+  gctx->tls_aad_len = -1;
+  return rv;
+}
+
+static int aes_gcm_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
+                          size_t len) {
+  EVP_AES_GCM_CTX *gctx = ctx->cipher_data;
+  /* If not set up, return error */
+  if (!gctx->key_set)
+    return -1;
+
+  if (gctx->tls_aad_len >= 0)
+    return aes_gcm_tls_cipher(ctx, out, in, len);
+
+  if (!gctx->iv_set)
+    return -1;
+  if (in) {
+    if (out == NULL) {
+      if (!CRYPTO_gcm128_aad(&gctx->gcm, in, len))
+        return -1;
+    } else if (ctx->encrypt) {
+      if (gctx->ctr) {
+        size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+        if (len >= 32 && AES_GCM_ASM(gctx)) {
+          size_t res = (16 - gctx->gcm.mres) % 16;
+
+          if (!CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, res))
+            return -1;
+
+          bulk = AES_gcm_encrypt(in + res, out + res, len - res, gctx->gcm.key,
+                                 gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+          gctx->gcm.len.u[1] += bulk;
+          bulk += res;
+        }
+#endif
+        if (!CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm, in + bulk, out + bulk,
+                                        len - bulk, gctx->ctr))
+          return -1;
+      } else {
+        size_t bulk = 0;
+        if (!CRYPTO_gcm128_encrypt(&gctx->gcm, in + bulk, out + bulk,
+                                  len - bulk))
+          return -1;
+      }
+    } else {
+      if (gctx->ctr) {
+        size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+        if (len >= 16 && AES_GCM_ASM(gctx)) {
+          size_t res = (16 - gctx->gcm.mres) % 16;
+
+          if (!CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, res))
+            return -1;
+
+          bulk = AES_gcm_decrypt(in + res, out + res, len - res, gctx->gcm.key,
+                                 gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+          gctx->gcm.len.u[1] += bulk;
+          bulk += res;
+        }
+#endif
+        if (!CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm, in + bulk, out + bulk,
+                                        len - bulk, gctx->ctr))
+          return -1;
+      } else {
+        size_t bulk = 0;
+        if (!CRYPTO_gcm128_decrypt(&gctx->gcm, in + bulk, out + bulk,
+                                  len - bulk))
+          return -1;
+      }
+    }
+    return len;
+  } else {
+    if (!ctx->encrypt) {
+      if (gctx->taglen < 0)
+        return -1;
+      if (!CRYPTO_gcm128_finish(&gctx->gcm, ctx->buf, gctx->taglen) != 0)
+        return -1;
+      gctx->iv_set = 0;
+      return 0;
+    }
+    CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, 16);
+    gctx->taglen = 16;
+    /* Don't reuse the IV */
+    gctx->iv_set = 0;
+    return 0;
+  }
+}
+
+static const EVP_CIPHER aes_128_cbc = {
+    NID_aes_128_cbc,     16 /* block_size */, 16 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CBC_MODE,
+    NULL /* app_data */, aes_init_key,        aes_cbc_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aes_128_ctr = {
+    NID_aes_128_ctr,     1 /* block_size */,  16 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CTR_MODE,
+    NULL /* app_data */, aes_init_key,        aes_ctr_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aes_128_ecb = {
+    NID_aes_128_ecb,     16 /* block_size */, 16 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_ECB_MODE,
+    NULL /* app_data */, aes_init_key,        aes_ecb_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aes_128_gcm = {
+    NID_aes_128_gcm, 1 /* block_size */, 16 /* key_size */, 12 /* iv_len */,
+    sizeof(EVP_AES_GCM_CTX),
+    EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER |
+        EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |
+        EVP_CIPH_FLAG_AEAD_CIPHER,
+    NULL /* app_data */, aes_gcm_init_key, aes_gcm_cipher, aes_gcm_cleanup,
+    aes_gcm_ctrl};
+
+
+static const EVP_CIPHER aes_256_cbc = {
+    NID_aes_128_cbc,     16 /* block_size */, 32 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CBC_MODE,
+    NULL /* app_data */, aes_init_key,        aes_cbc_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aes_256_ctr = {
+    NID_aes_128_ctr,     1 /* block_size */,  32 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CTR_MODE,
+    NULL /* app_data */, aes_init_key,        aes_ctr_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aes_256_ecb = {
+    NID_aes_128_ecb,     16 /* block_size */, 32 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_ECB_MODE,
+    NULL /* app_data */, aes_init_key,        aes_ecb_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aes_256_gcm = {
+    NID_aes_128_gcm, 1 /* block_size */, 32 /* key_size */, 12 /* iv_len */,
+    sizeof(EVP_AES_GCM_CTX),
+    EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER |
+        EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |
+        EVP_CIPH_FLAG_AEAD_CIPHER,
+    NULL /* app_data */, aes_gcm_init_key, aes_gcm_cipher, aes_gcm_cleanup,
+    aes_gcm_ctrl};
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86_64) || defined(OPENSSL_X86))
+
+/* AES-NI section. */
+
+static char aesni_capable() {
+  return (OPENSSL_ia32cap_P[1] & (1 << (57 - 32))) != 0;
+}
+
+static int aesni_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                          const uint8_t *iv, int enc) {
+  int ret, mode;
+  EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
+
+  mode = ctx->cipher->flags & EVP_CIPH_MODE_MASK;
+  if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
+    ret = aesni_set_decrypt_key(key, ctx->key_len * 8, ctx->cipher_data);
+    dat->block = (block128_f)aesni_decrypt;
+    dat->stream.cbc =
+        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)aesni_cbc_encrypt : NULL;
+  } else {
+    ret = aesni_set_encrypt_key(key, ctx->key_len * 8, ctx->cipher_data);
+    dat->block = (block128_f)aesni_encrypt;
+    if (mode == EVP_CIPH_CBC_MODE) {
+      dat->stream.cbc = (cbc128_f)aesni_cbc_encrypt;
+    } else if (mode == EVP_CIPH_CTR_MODE) {
+      dat->stream.ctr = (ctr128_f)aesni_ctr32_encrypt_blocks;
+    } else {
+      dat->stream.cbc = NULL;
+    }
+  }
+
+  if (ret < 0) {
+    OPENSSL_PUT_ERROR(CIPHER, aesni_init_key, CIPHER_R_AES_KEY_SETUP_FAILED);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int aesni_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out,
+                            const uint8_t *in, size_t len) {
+  aesni_cbc_encrypt(in, out, len, ctx->cipher_data, ctx->iv, ctx->encrypt);
+
+  return 1;
+}
+
+static int aesni_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out,
+                            const uint8_t *in, size_t len) {
+  size_t bl = ctx->cipher->block_size;
+
+  if (len < bl) {
+    return 1;
+  }
+
+  aesni_ecb_encrypt(in, out, len, ctx->cipher_data, ctx->encrypt);
+
+  return 1;
+}
+
+static int aesni_gcm_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                              const uint8_t *iv, int enc) {
+  EVP_AES_GCM_CTX *gctx = ctx->cipher_data;
+  if (!iv && !key)
+    return 1;
+  if (key) {
+    aesni_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks.ks);
+    CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks, (block128_f)aesni_encrypt);
+    gctx->ctr = (ctr128_f)aesni_ctr32_encrypt_blocks;
+    /* If we have an iv can set it directly, otherwise use
+     * saved IV. */
+    if (iv == NULL && gctx->iv_set) {
+      iv = gctx->iv;
+    }
+    if (iv) {
+      CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+      gctx->iv_set = 1;
+    }
+    gctx->key_set = 1;
+  } else {
+    /* If key set use IV, otherwise copy */
+    if (gctx->key_set) {
+      CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+    } else {
+      memcpy(gctx->iv, iv, gctx->ivlen);
+    }
+    gctx->iv_set = 1;
+    gctx->iv_gen = 0;
+  }
+  return 1;
+}
+
+static const EVP_CIPHER aesni_128_cbc = {
+    NID_aes_128_cbc,     16 /* block_size */, 16 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CBC_MODE,
+    NULL /* app_data */, aesni_init_key,      aesni_cbc_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aesni_128_ctr = {
+    NID_aes_128_ctr,     1 /* block_size */,  16 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CTR_MODE,
+    NULL /* app_data */, aesni_init_key,      aes_ctr_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aesni_128_ecb = {
+    NID_aes_128_ecb,     16 /* block_size */, 16 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_ECB_MODE,
+    NULL /* app_data */, aesni_init_key,      aesni_ecb_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aesni_128_gcm = {
+    NID_aes_128_gcm, 1 /* block_size */, 16 /* key_size */, 12 /* iv_len */,
+    sizeof(EVP_AES_GCM_CTX),
+    EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER |
+        EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |
+        EVP_CIPH_FLAG_AEAD_CIPHER,
+    NULL /* app_data */, aesni_gcm_init_key, aes_gcm_cipher, aes_gcm_cleanup,
+    aes_gcm_ctrl};
+
+
+static const EVP_CIPHER aesni_256_cbc = {
+    NID_aes_128_cbc,     16 /* block_size */, 32 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CBC_MODE,
+    NULL /* app_data */, aesni_init_key,      aesni_cbc_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aesni_256_ctr = {
+    NID_aes_128_ctr,     1 /* block_size */,  32 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_CTR_MODE,
+    NULL /* app_data */, aesni_init_key,      aes_ctr_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aesni_256_ecb = {
+    NID_aes_128_ecb,     16 /* block_size */, 32 /* key_size */,
+    16 /* iv_len */,     sizeof(EVP_AES_KEY), EVP_CIPH_ECB_MODE,
+    NULL /* app_data */, aesni_init_key,      aesni_ecb_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */};
+
+static const EVP_CIPHER aesni_256_gcm = {
+    NID_aes_256_gcm, 1 /* block_size */, 32 /* key_size */, 12 /* iv_len */,
+    sizeof(EVP_AES_GCM_CTX),
+    EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER |
+        EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |
+        EVP_CIPH_FLAG_AEAD_CIPHER,
+    NULL /* app_data */, aesni_gcm_init_key, aes_gcm_cipher, aes_gcm_cleanup,
+    aes_gcm_ctrl};
+
+#define EVP_CIPHER_FUNCTION(keybits, mode)             \
+  const EVP_CIPHER *EVP_aes_##keybits##_##mode(void) { \
+    if (aesni_capable()) {                             \
+      return &aesni_##keybits##_##mode;                \
+    } else {                                           \
+      return &aes_##keybits##_##mode;                  \
+    }                                                  \
+  }
+
+#else  /* ^^^  OPENSSL_X86_64 || OPENSSL_X86 */
+
+#define EVP_CIPHER_FUNCTION(keybits, mode)             \
+  const EVP_CIPHER *EVP_aes_##keybits##_##mode(void) { \
+    return &aes_##keybits##_##mode;                    \
+  }
+
+#endif
+
+EVP_CIPHER_FUNCTION(128, cbc)
+EVP_CIPHER_FUNCTION(128, ctr)
+EVP_CIPHER_FUNCTION(128, ecb)
+EVP_CIPHER_FUNCTION(128, gcm)
+
+EVP_CIPHER_FUNCTION(256, cbc)
+EVP_CIPHER_FUNCTION(256, ctr)
+EVP_CIPHER_FUNCTION(256, ecb)
+EVP_CIPHER_FUNCTION(256, gcm)
diff --git a/crypto/cipher/e_des.c b/crypto/cipher/e_des.c
new file mode 100644
index 0000000..d4b04f4
--- /dev/null
+++ b/crypto/cipher/e_des.c
@@ -0,0 +1,157 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/cipher.h>
+#include <openssl/des.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+#define EVP_MAXCHUNK (1<<30)
+
+typedef struct {
+  union {
+    double align;
+    DES_key_schedule ks;
+  } ks;
+} EVP_DES_KEY;
+
+static int des_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                        const uint8_t *iv, int enc) {
+  DES_cblock *deskey = (DES_cblock *)key;
+  EVP_DES_KEY *dat = (EVP_DES_KEY *)ctx->cipher_data;
+
+  DES_set_key(deskey, &dat->ks.ks);
+  return 1;
+}
+
+static int des_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
+                          size_t in_len) {
+  EVP_DES_KEY *dat = (EVP_DES_KEY *)ctx->cipher_data;
+
+  while (in_len >= EVP_MAXCHUNK) {
+    DES_ncbc_encrypt(in, out, EVP_MAXCHUNK, &dat->ks.ks, (DES_cblock *)ctx->iv,
+                     ctx->encrypt);
+    in_len -= EVP_MAXCHUNK;
+    in += EVP_MAXCHUNK;
+    out += EVP_MAXCHUNK;
+  }
+
+  if (in_len) {
+    DES_ncbc_encrypt(in, out, (long)in_len, &dat->ks.ks,
+                     (DES_cblock *)ctx->iv, ctx->encrypt);
+  }
+
+  return 1;
+}
+
+static const EVP_CIPHER des_cbc = {
+    NID_des_cbc,         8 /* block_size */,  8 /* key_size */,
+    8 /* iv_len */,      sizeof(EVP_DES_KEY), EVP_CIPH_CBC_MODE,
+    NULL /* app_data */, des_init_key,        des_cbc_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */, };
+
+const EVP_CIPHER *EVP_des_cbc(void) { return &des_cbc; }
+
+
+typedef struct {
+  union {
+    double align;
+    DES_key_schedule ks[3];
+  } ks;
+} DES_EDE_KEY;
+
+
+static int des_ede3_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                             const uint8_t *iv, int enc) {
+  DES_cblock *deskey = (DES_cblock *)key;
+  DES_EDE_KEY *dat = (DES_EDE_KEY*) ctx->cipher_data;
+
+  DES_set_key(&deskey[0], &dat->ks.ks[0]);
+  DES_set_key(&deskey[1], &dat->ks.ks[1]);
+  DES_set_key(&deskey[2], &dat->ks.ks[2]);
+
+  return 1;
+}
+
+static int des_ede3_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out,
+                              const uint8_t *in, size_t in_len) {
+  DES_EDE_KEY *dat = (DES_EDE_KEY*) ctx->cipher_data;
+
+  while (in_len >= EVP_MAXCHUNK) {
+    DES_ede3_cbc_encrypt(in, out, EVP_MAXCHUNK, &dat->ks.ks[0], &dat->ks.ks[1],
+                         &dat->ks.ks[2], (DES_cblock *)ctx->iv, ctx->encrypt);
+    in_len -= EVP_MAXCHUNK;
+    in += EVP_MAXCHUNK;
+    out += EVP_MAXCHUNK;
+  }
+
+  if (in_len) {
+    DES_ede3_cbc_encrypt(in, out, in_len, &dat->ks.ks[0], &dat->ks.ks[1],
+                         &dat->ks.ks[2], (DES_cblock *)ctx->iv, ctx->encrypt);
+  }
+
+  return 1;
+}
+
+static const EVP_CIPHER des3_cbc = {
+    NID_des_cbc,         8 /* block_size */,  24 /* key_size */,
+    8 /* iv_len */,      sizeof(DES_EDE_KEY), EVP_CIPH_CBC_MODE,
+    NULL /* app_data */, des_ede3_init_key,   des_ede3_cbc_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */, };
+
+const EVP_CIPHER *EVP_des_ede3_cbc(void) { return &des3_cbc; }
diff --git a/crypto/cipher/e_null.c b/crypto/cipher/e_null.c
new file mode 100644
index 0000000..196520d
--- /dev/null
+++ b/crypto/cipher/e_null.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/cipher.h>
+
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+static int null_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                         const uint8_t *iv, int enc) {
+  return 1;
+}
+
+static int null_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out,
+                       const uint8_t *in, size_t in_len) {
+  if (in != out) {
+    memcpy(out, in, in_len);
+  }
+  return 1;
+}
+
+static const EVP_CIPHER n_cipher = {
+    NID_undef,        1 /* block size */, 0 /* key_len */,     0 /* iv_len */,
+    0 /* ctx_size */, 0 /* flags */,      NULL /* app_data */, null_init_key,
+    null_cipher,      NULL /* cleanup */, NULL /* ctrl */,
+};
+
+const EVP_CIPHER *EVP_enc_null(void) { return &n_cipher; }
diff --git a/crypto/cipher/e_rc4.c b/crypto/cipher/e_rc4.c
new file mode 100644
index 0000000..840855a
--- /dev/null
+++ b/crypto/cipher/e_rc4.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/cipher.h>
+#include <openssl/md5.h>
+#include <openssl/obj.h>
+#include <openssl/rc4.h>
+
+#include "internal.h"
+
+
+static int rc4_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
+                        const uint8_t *iv, int enc) {
+  RC4_KEY *rc4key = (RC4_KEY *)ctx->cipher_data;
+
+  RC4_set_key(rc4key, EVP_CIPHER_CTX_key_length(ctx), key);
+  return 1;
+}
+
+static int rc4_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
+                      size_t in_len) {
+  RC4_KEY *rc4key = (RC4_KEY *)ctx->cipher_data;
+
+  RC4(rc4key, in_len, in, out);
+  return 1;
+}
+
+static const EVP_CIPHER rc4 = {
+    NID_rc4,             1 /* block_size */, 16 /* key_size */,
+    0 /* iv_len */,      sizeof(RC4_KEY),    EVP_CIPH_VARIABLE_LENGTH,
+    NULL /* app_data */, rc4_init_key,       rc4_cipher,
+    NULL /* cleanup */,  NULL /* ctrl */, };
+
+const EVP_CIPHER *EVP_rc4(void) { return &rc4; }
diff --git a/crypto/cipher/internal.h b/crypto/cipher/internal.h
new file mode 100644
index 0000000..1edc059
--- /dev/null
+++ b/crypto/cipher/internal.h
@@ -0,0 +1,113 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_CIPHER_INTERNAL_H
+#define OPENSSL_HEADER_CIPHER_INTERNAL_H
+
+#include <openssl/base.h>
+
+#include <openssl/asn1t.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+struct evp_cipher_st {
+  /* type contains a NID identifing the cipher. (For example, NID_rc4.) */
+  int nid;
+
+  /* block_size contains the block size, in bytes, of the cipher, or 1 for a
+   * stream cipher. */
+  unsigned block_size;
+
+  /* key_len contains the key size, in bytes, for the cipher. If the cipher
+   * takes a variable key size then this contains the default size. */
+  unsigned key_len;
+
+  /* iv_len contains the IV size, in bytes, or zero if inapplicable. */
+  unsigned iv_len;
+
+  /* ctx_size contains the size, in bytes, of the per-key context for this
+   * cipher. */
+  unsigned ctx_size;
+
+  /* flags contains the OR of a number of flags. See |EVP_CIPH_*|. */
+  uint32_t flags;
+
+  /* app_data is a pointer to opaque, user data. */
+  void *app_data;
+
+  int (*init)(EVP_CIPHER_CTX *ctx, const uint8_t *key, const uint8_t *iv,
+              int enc);
+
+  int (*cipher)(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
+                size_t inl);
+
+  int (*cleanup)(EVP_CIPHER_CTX *);
+
+  int (*ctrl)(EVP_CIPHER_CTX *, int type, int arg, void *ptr);
+};
+
+/* EVP_CIPH_MODE_MASK contains the bits of |flags| that represent the mode. */
+#define EVP_CIPH_MODE_MASK 0x3f
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_CIPHER_INTERNAL_H */
diff --git a/crypto/comp/CMakeLists.txt b/crypto/comp/CMakeLists.txt
new file mode 100644
index 0000000..0d543ad
--- /dev/null
+++ b/crypto/comp/CMakeLists.txt
@@ -0,0 +1,10 @@
+include_directories(. .. ../../include)
+
+add_library(
+	comp
+
+	OBJECT
+
+	comp.c
+	zlib.c
+)
diff --git a/crypto/comp/comp.c b/crypto/comp/comp.c
new file mode 100644
index 0000000..e4c9a80
--- /dev/null
+++ b/crypto/comp/comp.c
@@ -0,0 +1,119 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/comp.h>
+
+#include <openssl/mem.h>
+
+
+COMP_CTX *COMP_CTX_new(COMP_METHOD *meth) {
+  COMP_CTX *ret;
+
+  ret = OPENSSL_malloc(sizeof(COMP_CTX));
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  memset(ret, 0, sizeof(COMP_CTX));
+  ret->meth = meth;
+  if (ret->meth->init != NULL && !ret->meth->init(ret)) {
+    OPENSSL_free(ret);
+    ret = NULL;
+  }
+
+  return ret;
+}
+
+void COMP_CTX_free(COMP_CTX *ctx) {
+  if (ctx == NULL) {
+    return;
+  }
+
+  if (ctx->meth->finish != NULL) {
+    ctx->meth->finish(ctx);
+  }
+
+  OPENSSL_free(ctx);
+}
+
+int COMP_compress_block(COMP_CTX *ctx, uint8_t *out, size_t out_len,
+                        uint8_t *in, size_t in_len) {
+  int ret;
+  if (ctx->meth->compress == NULL) {
+    return -1;
+  }
+  ret = ctx->meth->compress(ctx, out, out_len, in, in_len);
+  if (ret > 0) {
+    ctx->compress_in += in_len;
+    ctx->compress_out += ret;
+  }
+  return ret;
+}
+
+int COMP_expand_block(COMP_CTX *ctx, uint8_t *out, size_t out_len, uint8_t *in,
+                      size_t in_len) {
+  int ret;
+
+  if (ctx->meth->expand == NULL) {
+    return -1;
+  }
+  ret = ctx->meth->expand(ctx, out, out_len, in, in_len);
+  if (ret > 0) {
+    ctx->expand_in += in_len;
+    ctx->expand_out += ret;
+  }
+  return ret;
+}
diff --git a/crypto/comp/comp.h b/crypto/comp/comp.h
new file mode 100644
index 0000000..12a967a
--- /dev/null
+++ b/crypto/comp/comp.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_COMP_H
+#define OPENSSL_HEADER_COMP_H
+
+#include <openssl/base.h>
+
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Compression methods */
+
+/* COMP_zlib returns a method that implements standard, zlib compression. */
+COMP_METHOD *COMP_zlib(void);
+
+
+/* Compression contexts */
+
+/* COMP_CTX_new allocates a fresh |COMP_CTX| which will compress with the given
+ * compression method. It returns the new |COMP_CTX| or NULL on error. */
+COMP_CTX *COMP_CTX_new(COMP_METHOD *meth);
+
+/* COMP_CTX_free frees all data owned by |ctx| and |ctx| itself. */
+void COMP_CTX_free(COMP_CTX *ctx);
+
+
+int COMP_compress_block(COMP_CTX *ctx, uint8_t *out, size_t out_len,
+                        uint8_t *in, size_t in_len);
+
+int COMP_expand_block(COMP_CTX *ctx, uint8_t *out, size_t out_len,
+                      uint8_t *in, size_t in_len);
+
+
+/* Private functions */
+
+struct comp_ctx_st {
+  COMP_METHOD *meth;
+  unsigned long compress_in;
+  unsigned long compress_out;
+  unsigned long expand_in;
+  unsigned long expand_out;
+
+  CRYPTO_EX_DATA ex_data;
+};
+
+struct comp_method_st {
+  int type;         /* NID for compression library */
+  const char *name; /* A text string to identify the library */
+  int (*init)(COMP_CTX *ctx);
+  void (*finish)(COMP_CTX *ctx);
+  int (*compress)(COMP_CTX *ctx, uint8_t *out, unsigned int olen, uint8_t *in,
+                  unsigned int ilen);
+  int (*expand)(COMP_CTX *ctx, uint8_t *out, unsigned int olen, uint8_t *in,
+                unsigned int ilen);
+  /* The following two do NOTHING, but are kept for backward compatibility */
+  long (*ctrl)(void);
+  long (*callback_ctrl)(void);
+} /* COMP_METHOD */;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_HMAC_H */
diff --git a/crypto/comp/zlib.c b/crypto/comp/zlib.c
new file mode 100644
index 0000000..627fdc3
--- /dev/null
+++ b/crypto/comp/zlib.c
@@ -0,0 +1,62 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/comp.h>
+
+
+COMP_METHOD *COMP_zlib(void) {
+  return NULL;
+}
diff --git a/crypto/conf/CMakeLists.txt b/crypto/conf/CMakeLists.txt
new file mode 100644
index 0000000..05439d5
--- /dev/null
+++ b/crypto/conf/CMakeLists.txt
@@ -0,0 +1,10 @@
+include_directories(. .. ../../include)
+
+add_library(
+	conf
+
+	OBJECT
+
+	conf.c
+	conf_error.c
+)
diff --git a/crypto/conf/conf.c b/crypto/conf/conf.c
new file mode 100644
index 0000000..898a9ba
--- /dev/null
+++ b/crypto/conf/conf.c
@@ -0,0 +1,741 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/conf.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include <openssl/bio.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "conf_def.h"
+
+
+static uint32_t conf_value_hash(const CONF_VALUE *v) {
+  return (lh_strhash(v->section) << 2) ^ lh_strhash(v->name);
+}
+
+static int conf_value_cmp(const CONF_VALUE *a, const CONF_VALUE *b) {
+  int i;
+
+  if (a->section != b->section) {
+    i = strcmp(a->section, b->section);
+    if (i) {
+      return i;
+    }
+  }
+
+  if (a->name != NULL && b->name != NULL) {
+    return strcmp(a->name, b->name);
+  } else if (a->name == b->name) {
+    return 0;
+  } else {
+    return (a->name == NULL) ? -1 : 1;
+  }
+}
+
+CONF *NCONF_new() {
+  CONF *conf;
+
+  conf = OPENSSL_malloc(sizeof(CONF));
+  if (conf == NULL) {
+    return NULL;
+  }
+
+  conf->data = lh_CONF_VALUE_new(conf_value_hash, conf_value_cmp);
+  if (conf->data == NULL) {
+    OPENSSL_free(conf);
+    return NULL;
+  }
+
+  return conf;
+}
+
+static void value_free_contents(CONF_VALUE *value) {
+  if (value->section) {
+    OPENSSL_free(value->section);
+  }
+  if (value->name) {
+    OPENSSL_free(value->name);
+    if (value->value) {
+      OPENSSL_free(value->value);
+    }
+  } else {
+    if (value->value) {
+      sk_CONF_VALUE_free((STACK_OF(CONF_VALUE)*)value->value);
+    }
+  }
+}
+
+static void value_free(CONF_VALUE *value) {
+  value_free_contents(value);
+  OPENSSL_free(value);
+}
+
+void NCONF_free(CONF *conf) {
+  if (conf == NULL || conf->data == NULL) {
+    return;
+  }
+
+  lh_CONF_VALUE_doall(conf->data, value_free_contents);
+  lh_CONF_VALUE_free(conf->data);
+  OPENSSL_free(conf);
+}
+
+CONF_VALUE *NCONF_new_section(const CONF *conf, const char *section) {
+  STACK_OF(CONF_VALUE) *sk = NULL;
+  int ok = 0, i;
+  CONF_VALUE *v = NULL, *old_value;
+
+  sk = sk_CONF_VALUE_new_null();
+  v = OPENSSL_malloc(sizeof(CONF_VALUE));
+  if (sk == NULL || v == NULL) {
+    goto err;
+  }
+  i = strlen(section) + 1;
+  v->section = OPENSSL_malloc(i);
+  if (v->section == NULL) {
+    goto err;
+  }
+
+  memcpy(v->section, section, i);
+  v->section[i-1] = 0;
+  v->name = NULL;
+  v->value = (char *)sk;
+
+  if (!lh_CONF_VALUE_insert(conf->data, &old_value, v)) {
+    goto err;
+  }
+  if (old_value) {
+    value_free(old_value);
+  }
+  ok = 1;
+
+err:
+  if (!ok) {
+    if (sk != NULL) {
+      sk_CONF_VALUE_free(sk);
+    }
+    if (v != NULL) {
+      OPENSSL_free(v);
+    }
+    v = NULL;
+  }
+  return v;
+}
+
+static int str_copy(CONF *conf, char *section, char **pto, char *from) {
+  int q, r, rr = 0, to = 0, len = 0;
+  char *s, *e, *rp, *rrp, *np, *cp, v;
+  const char *p;
+  BUF_MEM *buf;
+
+  buf = BUF_MEM_new();
+  if (buf == NULL) {
+    return 0;
+  }
+
+  len = strlen(from) + 1;
+  if (!BUF_MEM_grow(buf, len)) {
+    goto err;
+  }
+
+  for (;;) {
+    if (IS_QUOTE(conf, *from)) {
+      q = *from;
+      from++;
+      while (!IS_EOF(conf, *from) && (*from != q)) {
+        if (IS_ESC(conf, *from)) {
+          from++;
+          if (IS_EOF(conf, *from)) {
+            break;
+          }
+        }
+        buf->data[to++] = *(from++);
+      }
+      if (*from == q) {
+        from++;
+      }
+    } else if (IS_DQUOTE(conf, *from)) {
+      q = *from;
+      from++;
+      while (!IS_EOF(conf, *from)) {
+        if (*from == q) {
+          if (*(from + 1) == q) {
+            from++;
+          } else {
+            break;
+          }
+        }
+        buf->data[to++] = *(from++);
+      }
+      if (*from == q) {
+        from++;
+      }
+    } else if (IS_ESC(conf, *from)) {
+      from++;
+      v = *(from++);
+      if (IS_EOF(conf, v)) {
+        break;
+      } else if (v == 'r') {
+        v = '\r';
+      } else if (v == 'n') {
+        v = '\n';
+      } else if (v == 'b') {
+        v = '\b';
+      } else if (v == 't') {
+        v = '\t';
+      }
+      buf->data[to++] = v;
+    } else if (IS_EOF(conf, *from)) {
+      break;
+    } else if (*from == '$') {
+      /* try to expand it */
+      rrp = NULL;
+      s = &(from[1]);
+      if (*s == '{') {
+        q = '}';
+      } else if (*s == '(') {
+        q = ')';
+      } else {
+        q = 0;
+      }
+
+      if (q) {
+        s++;
+      }
+      cp = section;
+      e = np = s;
+      while (IS_ALPHA_NUMERIC(conf, *e)) {
+        e++;
+      }
+      if (e[0] == ':' && e[1] == ':') {
+        cp = np;
+        rrp = e;
+        rr = *e;
+        *rrp = '\0';
+        e += 2;
+        np = e;
+        while (IS_ALPHA_NUMERIC(conf, *e)) {
+          e++;
+        }
+      }
+      r = *e;
+      *e = '\0';
+      rp = e;
+      if (q) {
+        if (r != q) {
+          OPENSSL_PUT_ERROR(CONF, str_copy, CONF_R_NO_CLOSE_BRACE);
+          goto err;
+        }
+        e++;
+      }
+      /* So at this point we have
+       * np which is the start of the name string which is
+       *   '\0' terminated.
+       * cp which is the start of the section string which is
+       *   '\0' terminated.
+       * e is the 'next point after'.
+       * r and rr are the chars replaced by the '\0'
+       * rp and rrp is where 'r' and 'rr' came from. */
+      p = NCONF_get_string(conf, cp, np);
+      if (rrp != NULL) {
+        *rrp = rr;
+      }
+      *rp = r;
+      if (p == NULL) {
+        OPENSSL_PUT_ERROR(CONF, str_copy, CONF_R_VARIABLE_HAS_NO_VALUE);
+        goto err;
+      }
+      BUF_MEM_grow_clean(buf, (strlen(p) + buf->length - (e - from)));
+      while (*p) {
+        buf->data[to++] = *(p++);
+      }
+
+      /* Since we change the pointer 'from', we also have
+         to change the perceived length of the string it
+         points at.  /RL */
+      len -= e - from;
+      from = e;
+
+      /* In case there were no braces or parenthesis around
+         the variable reference, we have to put back the
+         character that was replaced with a '\0'.  /RL */
+      *rp = r;
+    } else {
+      buf->data[to++] = *(from++);
+    }
+  }
+
+  buf->data[to] = '\0';
+  if (*pto != NULL) {
+    OPENSSL_free(*pto);
+  }
+  *pto = buf->data;
+  OPENSSL_free(buf);
+  return 1;
+
+err:
+  if (buf != NULL) {
+    BUF_MEM_free(buf);
+  }
+  return 0;
+}
+
+static CONF_VALUE *get_section(const CONF *conf, const char *section) {
+  CONF_VALUE template;
+
+  memset(&template, 0, sizeof(template));
+  template.section = (char *) section;
+  return lh_CONF_VALUE_retrieve(conf->data, &template);
+}
+
+STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf, const char *section) {
+  CONF_VALUE *section_value = get_section(conf, section);
+  if (section_value == NULL) {
+    return NULL;
+  }
+  return (STACK_OF(CONF_VALUE)*) section_value->value;
+}
+
+const char *NCONF_get_string(const CONF *conf, const char *section,
+                             const char *name) {
+  CONF_VALUE template, *value;
+
+  memset(&template, 0, sizeof(template));
+  template.section = (char *) section;
+  template.name = (char *) name;
+  value = lh_CONF_VALUE_retrieve(conf->data, &template);
+  if (value == NULL) {
+    return NULL;
+  }
+  return value->value;
+}
+
+int add_string(const CONF *conf, CONF_VALUE *section, CONF_VALUE *value) {
+  STACK_OF(CONF_VALUE) *section_stack = (STACK_OF(CONF_VALUE)*) section->value;
+  CONF_VALUE *old_value;
+
+  value->section = section->section;
+  if (!sk_CONF_VALUE_push(section_stack, value)) {
+    return 0;
+  }
+
+  if (!lh_CONF_VALUE_insert(conf->data, &old_value, value)) {
+    return 0;
+  }
+  if (old_value != NULL) {
+    (void)sk_CONF_VALUE_delete_ptr(section_stack, old_value);
+    value_free(old_value);
+  }
+
+  return 1;
+}
+
+static char *eat_ws(CONF *conf, char *p) {
+  while (IS_WS(conf, *p) && !IS_EOF(conf, *p)) {
+    p++;
+  }
+  return p;
+}
+
+#define scan_esc(conf, p) (((IS_EOF((conf), (p)[1])) ? ((p) + 1) : ((p) + 2)))
+
+static char *eat_alpha_numeric(CONF *conf, char *p) {
+  for (;;) {
+    if (IS_ESC(conf, *p)) {
+      p = scan_esc(conf, p);
+      continue;
+    }
+    if (!IS_ALPHA_NUMERIC_PUNCT(conf, *p)) {
+      return p;
+    }
+    p++;
+  }
+}
+
+static char *scan_quote(CONF *conf, char *p) {
+  int q = *p;
+
+  p++;
+  while (!IS_EOF(conf, *p) && *p != q) {
+    if (IS_ESC(conf, *p)) {
+      p++;
+      if (IS_EOF(conf, *p)) {
+        return p;
+      }
+    }
+    p++;
+  }
+  if (*p == q) {
+    p++;
+  }
+  return p;
+}
+
+
+static char *scan_dquote(CONF *conf, char *p) {
+  int q = *p;
+
+  p++;
+  while (!(IS_EOF(conf, *p))) {
+    if (*p == q) {
+      if (*(p + 1) == q) {
+        p++;
+      } else {
+        break;
+      }
+    }
+    p++;
+  }
+  if (*p == q) {
+    p++;
+  }
+  return p;
+}
+
+static void clear_comments(CONF *conf, char *p) {
+  for (;;) {
+    if (IS_FCOMMENT(conf, *p)) {
+      *p = '\0';
+      return;
+    }
+    if (!IS_WS(conf, *p)) {
+      break;
+    }
+    p++;
+  }
+
+  for (;;) {
+    if (IS_COMMENT(conf, *p)) {
+      *p = '\0';
+      return;
+    }
+    if (IS_DQUOTE(conf, *p)) {
+      p = scan_dquote(conf, p);
+      continue;
+    }
+    if (IS_QUOTE(conf, *p)) {
+      p = scan_quote(conf, p);
+      continue;
+    }
+    if (IS_ESC(conf, *p)) {
+      p = scan_esc(conf, p);
+      continue;
+    }
+    if (IS_EOF(conf, *p)) {
+      return;
+    } else {
+      p++;
+    }
+  }
+}
+
+static int def_load_bio(CONF *conf, BIO *in, long *out_error_line) {
+  static const size_t CONFBUFSIZE = 512;
+  int bufnum = 0, i, ii;
+  BUF_MEM *buff = NULL;
+  char *s, *p, *end;
+  int again;
+  long eline = 0;
+  char btmp[DECIMAL_SIZE(eline) + 1];
+  CONF_VALUE *v = NULL, *tv;
+  CONF_VALUE *sv = NULL;
+  char *section = NULL, *buf;
+  char *start, *psection, *pname;
+
+  if ((buff = BUF_MEM_new()) == NULL) {
+    OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_BUF_LIB);
+    goto err;
+  }
+
+  section = (char *)OPENSSL_malloc(10);
+  if (section == NULL) {
+    OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  BUF_strlcpy(section, "default", 10);
+
+  sv = NCONF_new_section(conf, section);
+  if (sv == NULL) {
+    OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
+    goto err;
+  }
+
+  bufnum = 0;
+  again = 0;
+  for (;;) {
+    if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) {
+      OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_BUF_LIB);
+      goto err;
+    }
+    p = &(buff->data[bufnum]);
+    *p = '\0';
+    BIO_gets(in, p, CONFBUFSIZE - 1);
+    p[CONFBUFSIZE - 1] = '\0';
+    ii = i = strlen(p);
+    if (i == 0 && !again)
+      break;
+    again = 0;
+    while (i > 0) {
+      if ((p[i - 1] != '\r') && (p[i - 1] != '\n'))
+        break;
+      else
+        i--;
+    }
+    /* we removed some trailing stuff so there is a new
+     * line on the end. */
+    if (ii && i == ii)
+      again = 1; /* long line */
+    else {
+      p[i] = '\0';
+      eline++; /* another input line */
+    }
+
+    /* we now have a line with trailing \r\n removed */
+
+    /* i is the number of bytes */
+    bufnum += i;
+
+    v = NULL;
+    /* check for line continuation */
+    if (bufnum >= 1) {
+      /* If we have bytes and the last char '\\' and
+       * second last char is not '\\' */
+      p = &(buff->data[bufnum - 1]);
+      if (IS_ESC(conf, p[0]) && ((bufnum <= 1) || !IS_ESC(conf, p[-1]))) {
+        bufnum--;
+        again = 1;
+      }
+    }
+    if (again)
+      continue;
+    bufnum = 0;
+    buf = buff->data;
+
+    clear_comments(conf, buf);
+    s = eat_ws(conf, buf);
+    if (IS_EOF(conf, *s))
+      continue; /* blank line */
+    if (*s == '[') {
+      char *ss;
+
+      s++;
+      start = eat_ws(conf, s);
+      ss = start;
+    again:
+      end = eat_alpha_numeric(conf, ss);
+      p = eat_ws(conf, end);
+      if (*p != ']') {
+        if (*p != '\0') {
+          ss = p;
+          goto again;
+        }
+        OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_MISSING_CLOSE_SQUARE_BRACKET);
+        goto err;
+      }
+      *end = '\0';
+      if (!str_copy(conf, NULL, &section, start))
+        goto err;
+      if ((sv = get_section(conf, section)) == NULL)
+        sv = NCONF_new_section(conf, section);
+      if (sv == NULL) {
+        OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
+        goto err;
+      }
+      continue;
+    } else {
+      pname = s;
+      psection = NULL;
+      end = eat_alpha_numeric(conf, s);
+      if ((end[0] == ':') && (end[1] == ':')) {
+        *end = '\0';
+        end += 2;
+        psection = pname;
+        pname = end;
+        end = eat_alpha_numeric(conf, end);
+      }
+      p = eat_ws(conf, end);
+      if (*p != '=') {
+        OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_MISSING_EQUAL_SIGN);
+        goto err;
+      }
+      *end = '\0';
+      p++;
+      start = eat_ws(conf, p);
+      while (!IS_EOF(conf, *p))
+        p++;
+      p--;
+      while ((p != start) && (IS_WS(conf, *p)))
+        p--;
+      p++;
+      *p = '\0';
+
+      if (!(v = (CONF_VALUE *)OPENSSL_malloc(sizeof(CONF_VALUE)))) {
+        OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
+        goto err;
+      }
+      if (psection == NULL)
+        psection = section;
+      v->name = (char *)OPENSSL_malloc(strlen(pname) + 1);
+      v->value = NULL;
+      if (v->name == NULL) {
+        OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
+        goto err;
+      }
+      BUF_strlcpy(v->name, pname, strlen(pname) + 1);
+      if (!str_copy(conf, psection, &(v->value), start))
+        goto err;
+
+      if (strcmp(psection, section) != 0) {
+        if ((tv = get_section(conf, psection)) == NULL)
+          tv = NCONF_new_section(conf, psection);
+        if (tv == NULL) {
+          OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
+          goto err;
+        }
+      } else
+        tv = sv;
+      if (add_string(conf, tv, v) == 0) {
+        OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
+        goto err;
+      }
+      v = NULL;
+    }
+  }
+  if (buff != NULL)
+    BUF_MEM_free(buff);
+  if (section != NULL)
+    OPENSSL_free(section);
+  return 1;
+
+err:
+  if (buff != NULL)
+    BUF_MEM_free(buff);
+  if (section != NULL)
+    OPENSSL_free(section);
+  if (out_error_line != NULL)
+    *out_error_line = eline;
+  BIO_snprintf(btmp, sizeof btmp, "%ld", eline);
+  ERR_add_error_data(2, "line ", btmp);
+
+  if (v != NULL) {
+    if (v->name != NULL)
+      OPENSSL_free(v->name);
+    if (v->value != NULL)
+      OPENSSL_free(v->value);
+    if (v != NULL)
+      OPENSSL_free(v);
+  }
+  return 0;
+}
+
+int NCONF_load(CONF *conf, const char *filename, long *out_error_line) {
+  BIO *in = BIO_new_file(filename, "rb");
+  int ret;
+
+  if (in == NULL) {
+    OPENSSL_PUT_ERROR(CONF, NCONF_load, ERR_R_SYS_LIB);
+    return 0;
+  }
+
+  ret = def_load_bio(conf, in, out_error_line);
+  BIO_free(in);
+
+  return ret;
+}
+
+int CONF_parse_list(const char *list, char sep, int remove_whitespace,
+                    int (*list_cb)(const char *elem, int len, void *usr),
+                    void *arg) {
+  int ret;
+  const char *lstart, *tmpend, *p;
+
+  if (list == NULL) {
+    OPENSSL_PUT_ERROR(CONF, CONF_parse_list, CONF_R_LIST_CANNOT_BE_NULL);
+    return 0;
+  }
+
+  lstart = list;
+  for (;;) {
+    if (remove_whitespace) {
+      while (*lstart && isspace((unsigned char)*lstart))
+        lstart++;
+    }
+    p = strchr(lstart, sep);
+    if (p == lstart || !*lstart)
+      ret = list_cb(NULL, 0, arg);
+    else {
+      if (p)
+        tmpend = p - 1;
+      else
+        tmpend = lstart + strlen(lstart) - 1;
+      if (remove_whitespace) {
+        while (isspace((unsigned char)*tmpend))
+          tmpend--;
+      }
+      ret = list_cb(lstart, tmpend - lstart + 1, arg);
+    }
+    if (ret <= 0)
+      return ret;
+    if (p == NULL)
+      return 1;
+    lstart = p + 1;
+  }
+}
diff --git a/crypto/conf/conf.h b/crypto/conf/conf.h
new file mode 100644
index 0000000..51c5525
--- /dev/null
+++ b/crypto/conf/conf.h
@@ -0,0 +1,143 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_CONF_H
+#define OPENSSL_HEADER_CONF_H
+
+#include <openssl/base.h>
+
+#include <openssl/stack.h>
+#include <openssl/lhash.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Config files look like:
+ *
+ *   # Comment
+ *
+ *   # This key is in the default section.
+ *   key=value
+ *
+ *   [section_name]
+ *   key2=value2
+ *
+ * Config files are representated by a |CONF|. */
+
+typedef struct {
+  char *section;
+  char *name;
+  char *value;
+} CONF_VALUE;
+
+struct conf_st {
+  LHASH_OF(CONF_VALUE) *data;
+};
+
+
+/* NCONF_new returns a fresh, empty |CONF|, or NULL on error. */
+CONF *NCONF_new();
+
+/* NCONF_free frees all the data owned by |conf| and then |conf| itself. */
+void NCONF_free(CONF *conf);
+
+/* NCONF_load parses the file named |filename| and adds the values found to
+ * |conf|. It returns one on success and zero on error. In the event of an
+ * error, if |out_error_line| is not NULL, |*out_error_line| is set to the
+ * number of the line that contained the error. */
+int NCONF_load(CONF *conf, const char *filename, long *out_error_line);
+
+/* NCONF_get_section returns a stack of values for a given section in |conf|.
+ * If |section| is NULL, the default section is returned. It returns NULL on
+ * error. */
+STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf, const char *section);
+
+/* NCONF_get_string returns the value of the key |name|, in section |section|.
+ * The |section| argument may be NULL to indicate the default section. It
+ * returns the value or NULL on error. */
+const char *NCONF_get_string(const CONF *conf, const char *section,
+                             const char *name);
+
+
+/* Utility functions */
+
+/* CONF_parse_list takes a list separated by 'sep' and calls |list_cb| giving
+ * the start and length of each member, optionally stripping leading and
+ * trailing whitespace. This can be used to parse comma separated lists for
+ * example. If |list_cb| returns <= 0, then the iteration is halted and that
+ * value is returned immediately. Otherwise it returns one. */
+int CONF_parse_list(const char *list, char sep, int remove_whitespace,
+                    int (*list_cb)(const char *elem, int len, void *usr),
+                    void *arg);
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define CONF_F_CONF_parse_list 100
+#define CONF_F_str_copy 101
+#define CONF_F_def_load_bio 102
+#define CONF_F_NCONF_load 103
+#define CONF_R_MISSING_EQUAL_SIGN 100
+#define CONF_R_LIST_CANNOT_BE_NULL 101
+#define CONF_R_NO_CLOSE_BRACE 102
+#define CONF_R_VARIABLE_HAS_NO_VALUE 103
+#define CONF_R_UNABLE_TO_CREATE_NEW_SECTION 104
+#define CONF_R_MISSING_CLOSE_SQUARE_BRACKET 105
+
+#endif  /* OPENSSL_HEADER_THREAD_H */
diff --git a/crypto/conf/conf_def.h b/crypto/conf/conf_def.h
new file mode 100644
index 0000000..2fc035b
--- /dev/null
+++ b/crypto/conf/conf_def.h
@@ -0,0 +1,127 @@
+/* crypto/conf/conf_def.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/* THIS FILE WAS AUTOMAGICALLY GENERATED!
+   Please modify and use keysets.pl to regenerate it. */
+
+#define CONF_NUMBER		1
+#define CONF_UPPER		2
+#define CONF_LOWER		4
+#define CONF_UNDER		256
+#define CONF_PUNCTUATION	512
+#define CONF_WS			16
+#define CONF_ESC		32
+#define CONF_QUOTE		64
+#define CONF_DQUOTE		1024
+#define CONF_COMMENT		128
+#define CONF_FCOMMENT		2048
+#define CONF_EOF		8
+#define CONF_HIGHBIT		4096
+#define CONF_ALPHA		(CONF_UPPER|CONF_LOWER)
+#define CONF_ALPHA_NUMERIC	(CONF_ALPHA|CONF_NUMBER|CONF_UNDER)
+#define CONF_ALPHA_NUMERIC_PUNCT (CONF_ALPHA|CONF_NUMBER|CONF_UNDER| \
+					CONF_PUNCTUATION)
+
+#define KEYTYPES(c)		CONF_type_default
+#define IS_COMMENT(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_COMMENT)
+#define IS_FCOMMENT(c,a)	(KEYTYPES(c)[(a)&0xff]&CONF_FCOMMENT)
+#define IS_EOF(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_EOF)
+#define IS_ESC(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_ESC)
+#define IS_NUMBER(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_NUMBER)
+#define IS_WS(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_WS)
+#define IS_ALPHA_NUMERIC(c,a)	(KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC)
+#define IS_ALPHA_NUMERIC_PUNCT(c,a) \
+				(KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC_PUNCT)
+#define IS_QUOTE(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_QUOTE)
+#define IS_DQUOTE(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_DQUOTE)
+#define IS_HIGHBIT(c,a)		(KEYTYPES(c)[(a)&0xff]&CONF_HIGHBIT)
+
+static unsigned short CONF_type_default[256]={
+	0x0008,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+	0x0000,0x0010,0x0010,0x0000,0x0000,0x0010,0x0000,0x0000,
+	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+	0x0010,0x0200,0x0040,0x0080,0x0000,0x0200,0x0200,0x0040,
+	0x0000,0x0000,0x0200,0x0200,0x0200,0x0200,0x0200,0x0200,
+	0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,
+	0x0001,0x0001,0x0000,0x0200,0x0000,0x0000,0x0000,0x0200,
+	0x0200,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,
+	0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,
+	0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,
+	0x0002,0x0002,0x0002,0x0000,0x0020,0x0000,0x0200,0x0100,
+	0x0040,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,
+	0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,
+	0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,
+	0x0004,0x0004,0x0004,0x0000,0x0200,0x0000,0x0200,0x0000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
+	};
diff --git a/crypto/conf/conf_error.c b/crypto/conf/conf_error.c
new file mode 100644
index 0000000..b739d19
--- /dev/null
+++ b/crypto/conf/conf_error.c
@@ -0,0 +1,31 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "conf.h"
+
+const ERR_STRING_DATA CONF_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_CONF, CONF_F_CONF_parse_list, 0), "CONF_parse_list"},
+  {ERR_PACK(ERR_LIB_CONF, CONF_F_NCONF_load, 0), "NCONF_load"},
+  {ERR_PACK(ERR_LIB_CONF, CONF_F_def_load_bio, 0), "def_load_bio"},
+  {ERR_PACK(ERR_LIB_CONF, CONF_F_str_copy, 0), "str_copy"},
+  {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_LIST_CANNOT_BE_NULL), "LIST_CANNOT_BE_NULL"},
+  {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_MISSING_CLOSE_SQUARE_BRACKET), "MISSING_CLOSE_SQUARE_BRACKET"},
+  {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_MISSING_EQUAL_SIGN), "MISSING_EQUAL_SIGN"},
+  {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_NO_CLOSE_BRACE), "NO_CLOSE_BRACE"},
+  {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_UNABLE_TO_CREATE_NEW_SECTION), "UNABLE_TO_CREATE_NEW_SECTION"},
+  {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_VARIABLE_HAS_NO_VALUE), "VARIABLE_HAS_NO_VALUE"},
+  {0, NULL},
+};
diff --git a/crypto/cpu-arm.c b/crypto/cpu-arm.c
new file mode 100644
index 0000000..8b97fc1
--- /dev/null
+++ b/crypto/cpu-arm.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/cpu.h>
+
+#if defined(OPENSSL_ARM)
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "arm_arch.h"
+
+#if defined(__ARM_NEON__)
+uint32_t OPENSSL_armcap_P = ARMV7_NEON;
+#else
+uint32_t OPENSSL_armcap_P = ARMV7_NEON;
+#endif
+
+char CRYPTO_is_NEON_capable() {
+  return (OPENSSL_armcap_P & ARMV7_NEON) != 0;
+}
+
+void CRYPTO_set_NEON_capable(char neon_capable) {
+  if (neon_capable) {
+    OPENSSL_armcap_P |= ARMV7_NEON;
+  } else {
+    OPENSSL_armcap_P &= ~ARMV7_NEON;
+  }
+}
+
+#endif  /* defined(OPENSSL_ARM) */
diff --git a/crypto/cpu-intel.c b/crypto/cpu-intel.c
new file mode 100644
index 0000000..cda80ea
--- /dev/null
+++ b/crypto/cpu-intel.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <openssl/cpu.h>
+
+
+#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+
+#include <stdio.h>
+#include <inttypes.h>
+
+uint32_t OPENSSL_ia32cap_P[4];
+
+/* OPENSSL_ia32_cpuid is defined in cpu-x86_64-asm.pl. */
+extern uint64_t OPENSSL_ia32_cpuid(uint32_t*);
+
+#if !defined(OPENSSL_WINDOWS)
+void OPENSSL_cpuid_setup(void) __attribute__ ((constructor));
+#endif
+
+/* handle_cpu_env applies the value from |in| to the CPUID values in |out[0]|
+ * and |out[1]|. See the comment in |OPENSSL_cpuid_setup| about this. */
+static void handle_cpu_env(uint32_t *out, const char *in) {
+  const int invert = in[0] == '~';
+  uint64_t v;
+
+  if (!sscanf(in + invert, "%" PRIi64, &v)) {
+    return;
+  }
+
+  if (invert) {
+    out[0] &= ~v;
+    out[1] &= ~(v >> 32);
+  } else {
+    out[0] = v;
+    out[1] = v >> 32;
+  }
+}
+
+#if defined(OPENSSL_WINDOWS)
+#pragma section(".CRT$XCU", read)
+void __cdecl OPENSSL_cpuid_setup(void);
+__declspec(allocate(".CRT$XCU")) void(*cpuid_constructor)(void) = OPENSSL_cpuid_setup;
+void __cdecl OPENSSL_cpuid_setup(void) {
+#else
+void OPENSSL_cpuid_setup(void) {
+#endif
+  const char *env1, *env2;
+
+#if defined(OPENSSL_X86_64)
+  OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);
+#else
+  uint64_t vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);
+  /* 1<<10 sets a reserved bit to indicate that the variable
+   * was already initialised. */
+  OPENSSL_ia32cap_P[0] = ((uint32_t)vec) | (1 << 10);
+  OPENSSL_ia32cap_P[1] = vec >> 32;
+#endif
+
+  env1 = getenv("OPENSSL_ia32cap");
+  if (env1 == NULL) {
+    return;
+  }
+
+  /* OPENSSL_ia32cap can contain zero, one or two values, separated with a ':'.
+   * Each value is a 64-bit, unsigned value which may start with "0x" to
+   * indicate a hex value. Prior to the 64-bit value, a '~' may be given.
+   *
+   * If '~' isn't present, then the value is taken as the result of the CPUID.
+   * Otherwise the value is inverted and ANDed with the probed CPUID result.
+   *
+   * The first value determines OPENSSL_ia32cap_P[0] and [1]. The second [2]
+   * and [3]. */
+
+  handle_cpu_env(&OPENSSL_ia32cap_P[0], env1);
+  env2 = strchr(env1, ':');
+  if (env2 != NULL) {
+    handle_cpu_env(&OPENSSL_ia32cap_P[2], env2 + 1);
+  }
+}
+
+#endif  /* OPENSSL_X86 || OPENSSL_X86_64 */
diff --git a/crypto/cpu-x86-asm.pl b/crypto/cpu-x86-asm.pl
new file mode 100644
index 0000000..151493a
--- /dev/null
+++ b/crypto/cpu-x86-asm.pl
@@ -0,0 +1,372 @@
+#!/usr/bin/env perl
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC, "${dir}perlasm", "perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"crypto/cpu-x86-asm");
+
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&function_begin("OPENSSL_ia32_cpuid");
+	&xor	("edx","edx");
+	&pushf	();
+	&pop	("eax");
+	&mov	("ecx","eax");
+	&xor	("eax",1<<21);
+	&push	("eax");
+	&popf	();
+	&pushf	();
+	&pop	("eax");
+	&xor	("ecx","eax");
+	&xor	("eax","eax");
+	&bt	("ecx",21);
+	&jnc	(&label("nocpuid"));
+	&mov	("esi",&wparam(0));
+	&mov	(&DWP(8,"esi"),"eax");	# clear 3rd word
+	&cpuid	();
+	&mov	("edi","eax");		# max value for standard query level
+
+	&xor	("eax","eax");
+	&cmp	("ebx",0x756e6547);	# "Genu"
+	&setne	(&LB("eax"));
+	&mov	("ebp","eax");
+	&cmp	("edx",0x49656e69);	# "ineI"
+	&setne	(&LB("eax"));
+	&or	("ebp","eax");
+	&cmp	("ecx",0x6c65746e);	# "ntel"
+	&setne	(&LB("eax"));
+	&or	("ebp","eax");		# 0 indicates Intel CPU
+	&jz	(&label("intel"));
+
+	&cmp	("ebx",0x68747541);	# "Auth"
+	&setne	(&LB("eax"));
+	&mov	("esi","eax");
+	&cmp	("edx",0x69746E65);	# "enti"
+	&setne	(&LB("eax"));
+	&or	("esi","eax");
+	&cmp	("ecx",0x444D4163);	# "cAMD"
+	&setne	(&LB("eax"));
+	&or	("esi","eax");		# 0 indicates AMD CPU
+	&jnz	(&label("intel"));
+
+	# AMD specific
+	&mov	("eax",0x80000000);
+	&cpuid	();
+	&cmp	("eax",0x80000001);
+	&jb	(&label("intel"));
+	&mov	("esi","eax");
+	&mov	("eax",0x80000001);
+	&cpuid	();
+	&or	("ebp","ecx");
+	&and	("ebp",1<<11|1);	# isolate XOP bit
+	&cmp	("esi",0x80000008);
+	&jb	(&label("intel"));
+
+	&mov	("eax",0x80000008);
+	&cpuid	();
+	&movz	("esi",&LB("ecx"));	# number of cores - 1
+	&inc	("esi");		# number of cores
+
+	&mov	("eax",1);
+	&xor	("ecx","ecx");
+	&cpuid	();
+	&bt	("edx",28);
+	&jnc	(&label("generic"));
+	&shr	("ebx",16);
+	&and	("ebx",0xff);
+	&cmp	("ebx","esi");
+	&ja	(&label("generic"));
+	&and	("edx",0xefffffff);	# clear hyper-threading bit
+	&jmp	(&label("generic"));
+	
+&set_label("intel");
+	&cmp	("edi",7);
+	&jb	(&label("cacheinfo"));
+
+	&mov	("esi",&wparam(0));
+	&mov	("eax",7);
+	&xor	("ecx","ecx");
+	&cpuid	();
+	&mov	(&DWP(8,"esi"),"ebx");
+
+&set_label("cacheinfo");
+	&cmp	("edi",4);
+	&mov	("edi",-1);
+	&jb	(&label("nocacheinfo"));
+
+	&mov	("eax",4);
+	&mov	("ecx",0);		# query L1D
+	&cpuid	();
+	&mov	("edi","eax");
+	&shr	("edi",14);
+	&and	("edi",0xfff);		# number of cores -1 per L1D
+
+&set_label("nocacheinfo");
+	&mov	("eax",1);
+	&xor	("ecx","ecx");
+	&cpuid	();
+	&and	("edx",0xbfefffff);	# force reserved bits #20, #30 to 0
+	&cmp	("ebp",0);
+	&jne	(&label("notintel"));
+	&or	("edx",1<<30);		# set reserved bit#30 on Intel CPUs
+	&and	(&HB("eax"),15);	# familiy ID
+	&cmp	(&HB("eax"),15);	# P4?
+	&jne	(&label("notintel"));
+	&or	("edx",1<<20);		# set reserved bit#20 to engage RC4_CHAR
+&set_label("notintel");
+	&bt	("edx",28);		# test hyper-threading bit
+	&jnc	(&label("generic"));
+	&and	("edx",0xefffffff);
+	&cmp	("edi",0);
+	&je	(&label("generic"));
+
+	&or	("edx",0x10000000);
+	&shr	("ebx",16);
+	&cmp	(&LB("ebx"),1);
+	&ja	(&label("generic"));
+	&and	("edx",0xefffffff);	# clear hyper-threading bit if not
+
+&set_label("generic");
+	&and	("ebp",1<<11);		# isolate AMD XOP flag
+	&and	("ecx",0xfffff7ff);	# force 11th bit to 0
+	&mov	("esi","edx");
+	&or	("ebp","ecx");		# merge AMD XOP flag
+
+	&bt	("ecx",27);		# check OSXSAVE bit
+	&jnc	(&label("clear_avx"));
+	&xor	("ecx","ecx");
+	&data_byte(0x0f,0x01,0xd0);	# xgetbv
+	&and	("eax",6);
+	&cmp	("eax",6);
+	&je	(&label("done"));
+	&cmp	("eax",2);
+	&je	(&label("clear_avx"));
+&set_label("clear_xmm");
+	&and	("ebp",0xfdfffffd);	# clear AESNI and PCLMULQDQ bits
+	&and	("esi",0xfeffffff);	# clear FXSR
+&set_label("clear_avx");
+	&and	("ebp",0xefffe7ff);	# clear AVX, FMA and AMD XOP bits
+	&mov	("edi",&wparam(0));
+	&and	(&DWP(8,"edi"),0xffffffdf);	# clear AVX2
+&set_label("done");
+	&mov	("eax","esi");
+	&mov	("edx","ebp");
+&set_label("nocpuid");
+&function_end("OPENSSL_ia32_cpuid");
+
+&external_label("OPENSSL_ia32cap_P");
+
+&function_begin_B("OPENSSL_rdtsc","EXTRN\t_OPENSSL_ia32cap_P:DWORD");
+	&xor	("eax","eax");
+	&xor	("edx","edx");
+	&picmeup("ecx","OPENSSL_ia32cap_P");
+	&bt	(&DWP(0,"ecx"),4);
+	&jnc	(&label("notsc"));
+	&rdtsc	();
+&set_label("notsc");
+	&ret	();
+&function_end_B("OPENSSL_rdtsc");
+
+# This works in Ring 0 only [read DJGPP+MS-DOS+privileged DPMI host],
+# but it's safe to call it on any [supported] 32-bit platform...
+# Just check for [non-]zero return value...
+&function_begin_B("OPENSSL_instrument_halt","EXTRN\t_OPENSSL_ia32cap_P:DWORD");
+	&picmeup("ecx","OPENSSL_ia32cap_P");
+	&bt	(&DWP(0,"ecx"),4);
+	&jnc	(&label("nohalt"));	# no TSC
+
+	&data_word(0x9058900e);		# push %cs; pop %eax
+	&and	("eax",3);
+	&jnz	(&label("nohalt"));	# not enough privileges
+
+	&pushf	();
+	&pop	("eax");
+	&bt	("eax",9);
+	&jnc	(&label("nohalt"));	# interrupts are disabled
+
+	&rdtsc	();
+	&push	("edx");
+	&push	("eax");
+	&halt	();
+	&rdtsc	();
+
+	&sub	("eax",&DWP(0,"esp"));
+	&sbb	("edx",&DWP(4,"esp"));
+	&add	("esp",8);
+	&ret	();
+
+&set_label("nohalt");
+	&xor	("eax","eax");
+	&xor	("edx","edx");
+	&ret	();
+&function_end_B("OPENSSL_instrument_halt");
+
+# Essentially there is only one use for this function. Under DJGPP:
+#
+#	#include <go32.h>
+#	...
+#	i=OPENSSL_far_spin(_dos_ds,0x46c);
+#	...
+# to obtain the number of spins till closest timer interrupt.
+
+&function_begin_B("OPENSSL_far_spin");
+	&pushf	();
+	&pop	("eax");
+	&bt	("eax",9);
+	&jnc	(&label("nospin"));	# interrupts are disabled
+
+	&mov	("eax",&DWP(4,"esp"));
+	&mov	("ecx",&DWP(8,"esp"));
+	&data_word (0x90d88e1e);	# push %ds, mov %eax,%ds
+	&xor	("eax","eax");
+	&mov	("edx",&DWP(0,"ecx"));
+	&jmp	(&label("spin"));
+
+	&align	(16);
+&set_label("spin");
+	&inc	("eax");
+	&cmp	("edx",&DWP(0,"ecx"));
+	&je	(&label("spin"));
+
+	&data_word (0x1f909090);	# pop	%ds
+	&ret	();
+
+&set_label("nospin");
+	&xor	("eax","eax");
+	&xor	("edx","edx");
+	&ret	();
+&function_end_B("OPENSSL_far_spin");
+
+&function_begin_B("OPENSSL_wipe_cpu","EXTRN\t_OPENSSL_ia32cap_P:DWORD");
+	&xor	("eax","eax");
+	&xor	("edx","edx");
+	&picmeup("ecx","OPENSSL_ia32cap_P");
+	&mov	("ecx",&DWP(0,"ecx"));
+	&bt	(&DWP(0,"ecx"),1);
+	&jnc	(&label("no_x87"));
+	if ($sse2) {
+		&and	("ecx",1<<26|1<<24);	# check SSE2 and FXSR bits
+		&cmp	("ecx",1<<26|1<<24);
+		&jne	(&label("no_sse2"));
+		&pxor	("xmm0","xmm0");
+		&pxor	("xmm1","xmm1");
+		&pxor	("xmm2","xmm2");
+		&pxor	("xmm3","xmm3");
+		&pxor	("xmm4","xmm4");
+		&pxor	("xmm5","xmm5");
+		&pxor	("xmm6","xmm6");
+		&pxor	("xmm7","xmm7");
+	&set_label("no_sse2");
+	}
+	# just a bunch of fldz to zap the fp/mm bank followed by finit...
+	&data_word(0xeed9eed9,0xeed9eed9,0xeed9eed9,0xeed9eed9,0x90e3db9b);
+&set_label("no_x87");
+	&lea	("eax",&DWP(4,"esp"));
+	&ret	();
+&function_end_B("OPENSSL_wipe_cpu");
+
+&function_begin_B("OPENSSL_atomic_add");
+	&mov	("edx",&DWP(4,"esp"));	# fetch the pointer, 1st arg
+	&mov	("ecx",&DWP(8,"esp"));	# fetch the increment, 2nd arg
+	&push	("ebx");
+	&nop	();
+	&mov	("eax",&DWP(0,"edx"));
+&set_label("spin");
+	&lea	("ebx",&DWP(0,"eax","ecx"));
+	&nop	();
+	&data_word(0x1ab10ff0);	# lock;	cmpxchg	%ebx,(%edx)	# %eax is envolved and is always reloaded
+	&jne	(&label("spin"));
+	&mov	("eax","ebx");	# OpenSSL expects the new value
+	&pop	("ebx");
+	&ret	();
+&function_end_B("OPENSSL_atomic_add");
+
+# This function can become handy under Win32 in situations when
+# we don't know which calling convention, __stdcall or __cdecl(*),
+# indirect callee is using. In C it can be deployed as
+#
+#ifdef OPENSSL_CPUID_OBJ
+#	type OPENSSL_indirect_call(void *f,...);
+#	...
+#	OPENSSL_indirect_call(func,[up to $max arguments]);
+#endif
+#
+# (*)	it's designed to work even for __fastcall if number of
+#	arguments is 1 or 2!
+&function_begin_B("OPENSSL_indirect_call");
+	{
+	my ($max,$i)=(7,);	# $max has to be chosen as 4*n-1
+				# in order to preserve eventual
+				# stack alignment
+	&push	("ebp");
+	&mov	("ebp","esp");
+	&sub	("esp",$max*4);
+	&mov	("ecx",&DWP(12,"ebp"));
+	&mov	(&DWP(0,"esp"),"ecx");
+	&mov	("edx",&DWP(16,"ebp"));
+	&mov	(&DWP(4,"esp"),"edx");
+	for($i=2;$i<$max;$i++)
+		{
+		# Some copies will be redundant/bogus...
+		&mov	("eax",&DWP(12+$i*4,"ebp"));
+		&mov	(&DWP(0+$i*4,"esp"),"eax");
+		}
+	&call_ptr	(&DWP(8,"ebp"));# make the call...
+	&mov	("esp","ebp");	# ... and just restore the stack pointer
+				# without paying attention to what we called,
+				# (__cdecl *func) or (__stdcall *one).
+	&pop	("ebp");
+	&ret	();
+	}
+&function_end_B("OPENSSL_indirect_call");
+
+&function_begin_B("OPENSSL_cleanse");
+	&mov	("edx",&wparam(0));
+	&mov	("ecx",&wparam(1));
+	&xor	("eax","eax");
+	&cmp	("ecx",7);
+	&jae	(&label("lot"));
+	&cmp	("ecx",0);
+	&je	(&label("ret"));
+&set_label("little");
+	&mov	(&BP(0,"edx"),"al");
+	&sub	("ecx",1);
+	&lea	("edx",&DWP(1,"edx"));
+	&jnz	(&label("little"));
+&set_label("ret");
+	&ret	();
+
+&set_label("lot",16);
+	&test	("edx",3);
+	&jz	(&label("aligned"));
+	&mov	(&BP(0,"edx"),"al");
+	&lea	("ecx",&DWP(-1,"ecx"));
+	&lea	("edx",&DWP(1,"edx"));
+	&jmp	(&label("lot"));
+&set_label("aligned");
+	&mov	(&DWP(0,"edx"),"eax");
+	&lea	("ecx",&DWP(-4,"ecx"));
+	&test	("ecx",-4);
+	&lea	("edx",&DWP(4,"edx"));
+	&jnz	(&label("aligned"));
+	&cmp	("ecx",0);
+	&jne	(&label("little"));
+	&ret	();
+&function_end_B("OPENSSL_cleanse");
+
+&function_begin_B("OPENSSL_ia32_rdrand");
+	&mov	("ecx",8);
+&set_label("loop");
+	&rdrand	("eax");
+	&jc	(&label("break"));
+	&loop	(&label("loop"));
+&set_label("break");
+	&cmp	("eax",0);
+	&cmove	("eax","ecx");
+	&ret	();
+&function_end_B("OPENSSL_ia32_rdrand");
+
+&hidden("OPENSSL_ia32cap_P");
+
+&asm_finish();
diff --git a/crypto/cpu-x86_64-asm.pl b/crypto/cpu-x86_64-asm.pl
new file mode 100644
index 0000000..59cfd18
--- /dev/null
+++ b/crypto/cpu-x86_64-asm.pl
@@ -0,0 +1,167 @@
+#!/usr/bin/env perl
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+($arg1,$arg2,$arg3,$arg4)=$win64?("%rcx","%rdx","%r8", "%r9") :	# Win64 order
+				 ("%rdi","%rsi","%rdx","%rcx");	# Unix order
+
+print<<___;
+.text
+
+.globl	OPENSSL_ia32_cpuid
+.type	OPENSSL_ia32_cpuid,\@function,1
+.align	16
+OPENSSL_ia32_cpuid:
+	# On Windows, $arg1 is rcx, but that will be clobbered. So make Windows
+	# use the same register as Unix.
+	mov	$arg1,%rdi
+	mov	%rbx,%r8		# save %rbx
+
+	xor	%eax,%eax
+	mov	%eax,8(%rdi)		# clear 3rd word
+	cpuid
+	mov	%eax,%r11d		# max value for standard query level
+
+	xor	%eax,%eax
+	cmp	\$0x756e6547,%ebx	# "Genu"
+	setne	%al
+	mov	%eax,%r9d
+	cmp	\$0x49656e69,%edx	# "ineI"
+	setne	%al
+	or	%eax,%r9d
+	cmp	\$0x6c65746e,%ecx	# "ntel"
+	setne	%al
+	or	%eax,%r9d		# 0 indicates Intel CPU
+	jz	.Lintel
+
+	cmp	\$0x68747541,%ebx	# "Auth"
+	setne	%al
+	mov	%eax,%r10d
+	cmp	\$0x69746E65,%edx	# "enti"
+	setne	%al
+	or	%eax,%r10d
+	cmp	\$0x444D4163,%ecx	# "cAMD"
+	setne	%al
+	or	%eax,%r10d		# 0 indicates AMD CPU
+	jnz	.Lintel
+
+	# AMD specific
+	# See http://developer.amd.com/wordpress/media/2012/10/254811.pdf (1)
+
+	mov	\$0x80000000,%eax
+	cpuid
+	# Returns "The largest CPUID extended function input value supported by
+	# the processor implementation." in EAX.
+	cmp	\$0x80000001,%eax
+	jb	.Lintel
+	mov	%eax,%r10d
+	mov	\$0x80000001,%eax
+	cpuid
+	# Returns feature bits in ECX. See page 20 of [1].
+	# TODO(fork): I think this should be a MOV.
+	or	%ecx,%r9d
+	and	\$0x00000801,%r9d	# isolate AMD XOP bit, 1<<11
+
+	cmp	\$0x80000008,%r10d
+	jb	.Lintel
+
+	mov	\$0x80000008,%eax
+	cpuid
+	# Returns APIC ID and number of cores in ECX. See page 27 of [1].
+	movzb	%cl,%r10		# number of cores - 1
+	inc	%r10			# number of cores
+
+	mov	\$1,%eax
+	cpuid
+	# See page 13 of [1].
+	bt	\$28,%edx		# test hyper-threading bit
+	jnc	.Lgeneric
+	shr	\$16,%ebx		# number of logical processors
+	cmp	%r10b,%bl
+	ja	.Lgeneric
+	and	\$0xefffffff,%edx	# Clear hyper-threading bit.
+	jmp	.Lgeneric
+
+.Lintel:
+	cmp	\$4,%r11d
+	mov	\$-1,%r10d
+	jb	.Lnocacheinfo
+
+	mov	\$4,%eax
+	mov	\$0,%ecx		# query L1D
+	cpuid
+	mov	%eax,%r10d
+	shr	\$14,%r10d
+	and	\$0xfff,%r10d		# number of cores -1 per L1D
+
+	cmp	\$7,%r11d
+	jb	.Lnocacheinfo
+
+	mov	\$7,%eax
+	xor	%ecx,%ecx
+	cpuid
+	mov	%ebx,8(%rdi)
+
+.Lnocacheinfo:
+	mov	\$1,%eax
+	cpuid
+	# Gets feature information. See table 3-21 in the Intel manual.
+	and	\$0xbfefffff,%edx	# force reserved bits to 0
+	cmp	\$0,%r9d
+	jne	.Lnotintel
+	or	\$0x40000000,%edx	# set reserved bit#30 on Intel CPUs
+	and	\$15,%ah
+	cmp	\$15,%ah		# examine Family ID
+	jne	.Lnotintel
+	or	\$0x00100000,%edx	# set reserved bit#20 to engage RC4_CHAR
+.Lnotintel:
+	bt	\$28,%edx		# test hyper-threading bit
+	jnc	.Lgeneric
+	and	\$0xefffffff,%edx	# ~(1<<28) - clear hyper-threading.
+	cmp	\$0,%r10d
+	je	.Lgeneric
+
+	or	\$0x10000000,%edx	# 1<<28
+	shr	\$16,%ebx
+	cmp	\$1,%bl			# see if cache is shared
+	ja	.Lgeneric
+	and	\$0xefffffff,%edx	# ~(1<<28)
+.Lgeneric:
+	and	\$0x00000800,%r9d	# isolate AMD XOP flag
+	and	\$0xfffff7ff,%ecx
+	or	%ecx,%r9d		# merge AMD XOP flag
+
+	mov	%edx,%r10d		# %r9d:%r10d is copy of %ecx:%edx
+	bt	\$27,%r9d		# check OSXSAVE bit
+	jnc	.Lclear_avx
+	xor	%ecx,%ecx		# XCR0
+	.byte	0x0f,0x01,0xd0		# xgetbv
+	and	\$6,%eax		# isolate XMM and YMM state support
+	cmp	\$6,%eax
+	je	.Ldone
+.Lclear_avx:
+	mov	\$0xefffe7ff,%eax	# ~(1<<28|1<<12|1<<11)
+	and	%eax,%r9d		# clear AVX, FMA and AMD XOP bits
+	andl	\$0xffffffdf,8(%rdi)	# cleax AVX2, ~(1<<5)
+.Ldone:
+	movl	%r9d,4(%rdi)
+	movl	%r10d,0(%rdi)
+	mov	%r8,%rbx		# restore %rbx
+	ret
+.size	OPENSSL_ia32_cpuid,.-OPENSSL_ia32_cpuid
+
+___
+
+close STDOUT;	# flush
diff --git a/crypto/cpu.h b/crypto/cpu.h
new file mode 100644
index 0000000..02f6da2
--- /dev/null
+++ b/crypto/cpu.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_CPU_H
+#define OPENSSL_HEADER_CPU_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+/* OPENSSL_ia32cap_P contains the Intel CPUID bits when running on an x86 or
+ * x86-64 system.
+ *
+ *   Index 0:
+ *     EDX for CPUID where EAX = 1
+ *     Bit 30 is used to indicate an Intel CPU
+ *     Bit 20 is used to indicate RC4_CHAR
+ *   Index 1:
+ *     ECX for CPUID where EAX = 1
+ *   Index 2:
+ *     EBX for CPUID where EAX = 7 */
+extern uint32_t OPENSSL_ia32cap_P[4];
+#endif
+
+#if defined(OPENSSL_ARM)
+/* CRYPTO_is_NEON_capable returns true if the current CPU has a NEON unit. Note
+ * that |OPENSSL_armcap_P| also exists and contains the same information in a
+ * form that's easier for assembly to use. */
+char CRYPTO_is_NEON_capable();
+
+/* CRYPTO_set_NEON_capable sets the return value of |CRYPTO_is_NEON_capable|.
+ * By default, unless the code was compiled with |-mfpu=neon|, NEON is assumed
+ * not to be present. It is not autodetected. */
+void CRYPTO_set_NEON_capable(char neon_capable);
+#endif  /* OPENSSL_ARM */
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_CPU_H */
diff --git a/crypto/crypto_error.c b/crypto/crypto_error.c
new file mode 100644
index 0000000..483bbe3
--- /dev/null
+++ b/crypto/crypto_error.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "crypto_error.h"
+
+const ERR_STRING_DATA CRYPTO_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_CRYPTO_set_ex_data, 0), "CRYPTO_set_ex_data"},
+  {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_get_class, 0), "get_class"},
+  {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_get_func_pointers, 0), "get_func_pointers"},
+  {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_get_new_index, 0), "get_new_index"},
+  {0, NULL},
+};
diff --git a/crypto/crypto_error.h b/crypto/crypto_error.h
new file mode 100644
index 0000000..fd3d607
--- /dev/null
+++ b/crypto/crypto_error.h
@@ -0,0 +1,5 @@
+#define CRYPTO_F_CRYPTO_set_ex_data 100
+#define CRYPTO_F_get_class 101
+#define CRYPTO_F_get_new_index 102
+#define CRYPTO_F_get_func_pointers 103
+
diff --git a/crypto/des/CMakeLists.txt b/crypto/des/CMakeLists.txt
new file mode 100644
index 0000000..7ece452
--- /dev/null
+++ b/crypto/des/CMakeLists.txt
@@ -0,0 +1,9 @@
+include_directories(. .. ../../include)
+
+add_library(
+	des
+
+	OBJECT
+
+	des.c
+)
diff --git a/crypto/des/des.c b/crypto/des/des.c
new file mode 100644
index 0000000..ca87ee3
--- /dev/null
+++ b/crypto/des/des.c
@@ -0,0 +1,714 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/des.h>
+
+#include "internal.h"
+
+
+static const uint32_t des_skb[8][64] = {
+    {/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+     0x00000000L, 0x00000010L, 0x20000000L, 0x20000010L, 0x00010000L,
+     0x00010010L, 0x20010000L, 0x20010010L, 0x00000800L, 0x00000810L,
+     0x20000800L, 0x20000810L, 0x00010800L, 0x00010810L, 0x20010800L,
+     0x20010810L, 0x00000020L, 0x00000030L, 0x20000020L, 0x20000030L,
+     0x00010020L, 0x00010030L, 0x20010020L, 0x20010030L, 0x00000820L,
+     0x00000830L, 0x20000820L, 0x20000830L, 0x00010820L, 0x00010830L,
+     0x20010820L, 0x20010830L, 0x00080000L, 0x00080010L, 0x20080000L,
+     0x20080010L, 0x00090000L, 0x00090010L, 0x20090000L, 0x20090010L,
+     0x00080800L, 0x00080810L, 0x20080800L, 0x20080810L, 0x00090800L,
+     0x00090810L, 0x20090800L, 0x20090810L, 0x00080020L, 0x00080030L,
+     0x20080020L, 0x20080030L, 0x00090020L, 0x00090030L, 0x20090020L,
+     0x20090030L, 0x00080820L, 0x00080830L, 0x20080820L, 0x20080830L,
+     0x00090820L, 0x00090830L, 0x20090820L, 0x20090830L, },
+    {/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
+     0x00000000L, 0x02000000L, 0x00002000L, 0x02002000L, 0x00200000L,
+     0x02200000L, 0x00202000L, 0x02202000L, 0x00000004L, 0x02000004L,
+     0x00002004L, 0x02002004L, 0x00200004L, 0x02200004L, 0x00202004L,
+     0x02202004L, 0x00000400L, 0x02000400L, 0x00002400L, 0x02002400L,
+     0x00200400L, 0x02200400L, 0x00202400L, 0x02202400L, 0x00000404L,
+     0x02000404L, 0x00002404L, 0x02002404L, 0x00200404L, 0x02200404L,
+     0x00202404L, 0x02202404L, 0x10000000L, 0x12000000L, 0x10002000L,
+     0x12002000L, 0x10200000L, 0x12200000L, 0x10202000L, 0x12202000L,
+     0x10000004L, 0x12000004L, 0x10002004L, 0x12002004L, 0x10200004L,
+     0x12200004L, 0x10202004L, 0x12202004L, 0x10000400L, 0x12000400L,
+     0x10002400L, 0x12002400L, 0x10200400L, 0x12200400L, 0x10202400L,
+     0x12202400L, 0x10000404L, 0x12000404L, 0x10002404L, 0x12002404L,
+     0x10200404L, 0x12200404L, 0x10202404L, 0x12202404L, },
+    {/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
+     0x00000000L, 0x00000001L, 0x00040000L, 0x00040001L, 0x01000000L,
+     0x01000001L, 0x01040000L, 0x01040001L, 0x00000002L, 0x00000003L,
+     0x00040002L, 0x00040003L, 0x01000002L, 0x01000003L, 0x01040002L,
+     0x01040003L, 0x00000200L, 0x00000201L, 0x00040200L, 0x00040201L,
+     0x01000200L, 0x01000201L, 0x01040200L, 0x01040201L, 0x00000202L,
+     0x00000203L, 0x00040202L, 0x00040203L, 0x01000202L, 0x01000203L,
+     0x01040202L, 0x01040203L, 0x08000000L, 0x08000001L, 0x08040000L,
+     0x08040001L, 0x09000000L, 0x09000001L, 0x09040000L, 0x09040001L,
+     0x08000002L, 0x08000003L, 0x08040002L, 0x08040003L, 0x09000002L,
+     0x09000003L, 0x09040002L, 0x09040003L, 0x08000200L, 0x08000201L,
+     0x08040200L, 0x08040201L, 0x09000200L, 0x09000201L, 0x09040200L,
+     0x09040201L, 0x08000202L, 0x08000203L, 0x08040202L, 0x08040203L,
+     0x09000202L, 0x09000203L, 0x09040202L, 0x09040203L, },
+    {/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
+     0x00000000L, 0x00100000L, 0x00000100L, 0x00100100L, 0x00000008L,
+     0x00100008L, 0x00000108L, 0x00100108L, 0x00001000L, 0x00101000L,
+     0x00001100L, 0x00101100L, 0x00001008L, 0x00101008L, 0x00001108L,
+     0x00101108L, 0x04000000L, 0x04100000L, 0x04000100L, 0x04100100L,
+     0x04000008L, 0x04100008L, 0x04000108L, 0x04100108L, 0x04001000L,
+     0x04101000L, 0x04001100L, 0x04101100L, 0x04001008L, 0x04101008L,
+     0x04001108L, 0x04101108L, 0x00020000L, 0x00120000L, 0x00020100L,
+     0x00120100L, 0x00020008L, 0x00120008L, 0x00020108L, 0x00120108L,
+     0x00021000L, 0x00121000L, 0x00021100L, 0x00121100L, 0x00021008L,
+     0x00121008L, 0x00021108L, 0x00121108L, 0x04020000L, 0x04120000L,
+     0x04020100L, 0x04120100L, 0x04020008L, 0x04120008L, 0x04020108L,
+     0x04120108L, 0x04021000L, 0x04121000L, 0x04021100L, 0x04121100L,
+     0x04021008L, 0x04121008L, 0x04021108L, 0x04121108L, },
+    {/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+     0x00000000L, 0x10000000L, 0x00010000L, 0x10010000L, 0x00000004L,
+     0x10000004L, 0x00010004L, 0x10010004L, 0x20000000L, 0x30000000L,
+     0x20010000L, 0x30010000L, 0x20000004L, 0x30000004L, 0x20010004L,
+     0x30010004L, 0x00100000L, 0x10100000L, 0x00110000L, 0x10110000L,
+     0x00100004L, 0x10100004L, 0x00110004L, 0x10110004L, 0x20100000L,
+     0x30100000L, 0x20110000L, 0x30110000L, 0x20100004L, 0x30100004L,
+     0x20110004L, 0x30110004L, 0x00001000L, 0x10001000L, 0x00011000L,
+     0x10011000L, 0x00001004L, 0x10001004L, 0x00011004L, 0x10011004L,
+     0x20001000L, 0x30001000L, 0x20011000L, 0x30011000L, 0x20001004L,
+     0x30001004L, 0x20011004L, 0x30011004L, 0x00101000L, 0x10101000L,
+     0x00111000L, 0x10111000L, 0x00101004L, 0x10101004L, 0x00111004L,
+     0x10111004L, 0x20101000L, 0x30101000L, 0x20111000L, 0x30111000L,
+     0x20101004L, 0x30101004L, 0x20111004L, 0x30111004L, },
+    {/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
+     0x00000000L, 0x08000000L, 0x00000008L, 0x08000008L, 0x00000400L,
+     0x08000400L, 0x00000408L, 0x08000408L, 0x00020000L, 0x08020000L,
+     0x00020008L, 0x08020008L, 0x00020400L, 0x08020400L, 0x00020408L,
+     0x08020408L, 0x00000001L, 0x08000001L, 0x00000009L, 0x08000009L,
+     0x00000401L, 0x08000401L, 0x00000409L, 0x08000409L, 0x00020001L,
+     0x08020001L, 0x00020009L, 0x08020009L, 0x00020401L, 0x08020401L,
+     0x00020409L, 0x08020409L, 0x02000000L, 0x0A000000L, 0x02000008L,
+     0x0A000008L, 0x02000400L, 0x0A000400L, 0x02000408L, 0x0A000408L,
+     0x02020000L, 0x0A020000L, 0x02020008L, 0x0A020008L, 0x02020400L,
+     0x0A020400L, 0x02020408L, 0x0A020408L, 0x02000001L, 0x0A000001L,
+     0x02000009L, 0x0A000009L, 0x02000401L, 0x0A000401L, 0x02000409L,
+     0x0A000409L, 0x02020001L, 0x0A020001L, 0x02020009L, 0x0A020009L,
+     0x02020401L, 0x0A020401L, 0x02020409L, 0x0A020409L, },
+    {/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
+     0x00000000L, 0x00000100L, 0x00080000L, 0x00080100L, 0x01000000L,
+     0x01000100L, 0x01080000L, 0x01080100L, 0x00000010L, 0x00000110L,
+     0x00080010L, 0x00080110L, 0x01000010L, 0x01000110L, 0x01080010L,
+     0x01080110L, 0x00200000L, 0x00200100L, 0x00280000L, 0x00280100L,
+     0x01200000L, 0x01200100L, 0x01280000L, 0x01280100L, 0x00200010L,
+     0x00200110L, 0x00280010L, 0x00280110L, 0x01200010L, 0x01200110L,
+     0x01280010L, 0x01280110L, 0x00000200L, 0x00000300L, 0x00080200L,
+     0x00080300L, 0x01000200L, 0x01000300L, 0x01080200L, 0x01080300L,
+     0x00000210L, 0x00000310L, 0x00080210L, 0x00080310L, 0x01000210L,
+     0x01000310L, 0x01080210L, 0x01080310L, 0x00200200L, 0x00200300L,
+     0x00280200L, 0x00280300L, 0x01200200L, 0x01200300L, 0x01280200L,
+     0x01280300L, 0x00200210L, 0x00200310L, 0x00280210L, 0x00280310L,
+     0x01200210L, 0x01200310L, 0x01280210L, 0x01280310L, },
+    {/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
+     0x00000000L, 0x04000000L, 0x00040000L, 0x04040000L, 0x00000002L,
+     0x04000002L, 0x00040002L, 0x04040002L, 0x00002000L, 0x04002000L,
+     0x00042000L, 0x04042000L, 0x00002002L, 0x04002002L, 0x00042002L,
+     0x04042002L, 0x00000020L, 0x04000020L, 0x00040020L, 0x04040020L,
+     0x00000022L, 0x04000022L, 0x00040022L, 0x04040022L, 0x00002020L,
+     0x04002020L, 0x00042020L, 0x04042020L, 0x00002022L, 0x04002022L,
+     0x00042022L, 0x04042022L, 0x00000800L, 0x04000800L, 0x00040800L,
+     0x04040800L, 0x00000802L, 0x04000802L, 0x00040802L, 0x04040802L,
+     0x00002800L, 0x04002800L, 0x00042800L, 0x04042800L, 0x00002802L,
+     0x04002802L, 0x00042802L, 0x04042802L, 0x00000820L, 0x04000820L,
+     0x00040820L, 0x04040820L, 0x00000822L, 0x04000822L, 0x00040822L,
+     0x04040822L, 0x00002820L, 0x04002820L, 0x00042820L, 0x04042820L,
+     0x00002822L, 0x04002822L, 0x00042822L, 0x04042822L, }};
+
+static const uint32_t DES_SPtrans[8][64] = {
+    {/* nibble 0 */
+     0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L, 0x02000000L,
+     0x00080802L, 0x00080002L, 0x02000002L, 0x00080802L, 0x02080800L,
+     0x02080000L, 0x00000802L, 0x02000802L, 0x02000000L, 0x00000000L,
+     0x00080002L, 0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L,
+     0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L, 0x00000002L,
+     0x00000800L, 0x00080800L, 0x02080002L, 0x00000800L, 0x02000802L,
+     0x02080002L, 0x00000000L, 0x00000000L, 0x02080802L, 0x02000800L,
+     0x00080002L, 0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L,
+     0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L, 0x00080802L,
+     0x00000002L, 0x02000002L, 0x02080000L, 0x02080802L, 0x00080800L,
+     0x02080000L, 0x02000802L, 0x02000000L, 0x00000802L, 0x00080002L,
+     0x00000000L, 0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L,
+     0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L, },
+    {/* nibble 1 */
+     0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L, 0x40000010L,
+     0x00008010L, 0x40008000L, 0x00108000L, 0x00008000L, 0x40100010L,
+     0x00000010L, 0x40008000L, 0x00100010L, 0x40108000L, 0x40100000L,
+     0x00000010L, 0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L,
+     0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L, 0x40008010L,
+     0x00108010L, 0x40108000L, 0x40000010L, 0x40000000L, 0x00100000L,
+     0x00008010L, 0x40108010L, 0x00100010L, 0x40108000L, 0x40008000L,
+     0x00108010L, 0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L,
+     0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L, 0x00008000L,
+     0x40000000L, 0x00108010L, 0x40008010L, 0x40108000L, 0x00008000L,
+     0x00000000L, 0x40000010L, 0x00000010L, 0x40108010L, 0x00108000L,
+     0x40100000L, 0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L,
+     0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L, },
+    {/* nibble 2 */
+     0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L, 0x00040001L,
+     0x04000000L, 0x04000101L, 0x00040100L, 0x04000100L, 0x00040000L,
+     0x04040000L, 0x00000001L, 0x04040101L, 0x00000101L, 0x00000001L,
+     0x04040001L, 0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L,
+     0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L, 0x04040001L,
+     0x04000100L, 0x00040101L, 0x04040000L, 0x00040100L, 0x00000000L,
+     0x04000000L, 0x00040101L, 0x04040100L, 0x00000100L, 0x00000001L,
+     0x00040000L, 0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L,
+     0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L, 0x00040001L,
+     0x04000000L, 0x04040101L, 0x00000001L, 0x00040101L, 0x04000001L,
+     0x04000000L, 0x04040101L, 0x00040000L, 0x04000100L, 0x04000101L,
+     0x00040100L, 0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L,
+     0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L, },
+    {/* nibble 3 */
+     0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L, 0x00000000L,
+     0x10400000L, 0x10001008L, 0x00400008L, 0x10401000L, 0x10000008L,
+     0x10000000L, 0x00001008L, 0x10000008L, 0x00401008L, 0x00400000L,
+     0x10000000L, 0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L,
+     0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L, 0x00001008L,
+     0x00000000L, 0x00400008L, 0x10401000L, 0x10001000L, 0x10400008L,
+     0x10401008L, 0x00400000L, 0x10400008L, 0x00001008L, 0x00400000L,
+     0x10000008L, 0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L,
+     0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L, 0x00000000L,
+     0x10400008L, 0x10401000L, 0x00001000L, 0x10000000L, 0x10401008L,
+     0x00401008L, 0x00400000L, 0x10401008L, 0x00000008L, 0x10001000L,
+     0x00401008L, 0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L,
+     0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L, },
+    {/* nibble 4 */
+     0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L, 0x08010020L,
+     0x08000400L, 0x00010420L, 0x08010000L, 0x00010000L, 0x00000020L,
+     0x08000020L, 0x00010400L, 0x08000420L, 0x08010020L, 0x08010400L,
+     0x00000000L, 0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L,
+     0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L, 0x00000020L,
+     0x08000420L, 0x08010420L, 0x00010020L, 0x08010000L, 0x00000400L,
+     0x00000420L, 0x08010400L, 0x08010400L, 0x08000420L, 0x00010020L,
+     0x08010000L, 0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L,
+     0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L, 0x00010420L,
+     0x08000000L, 0x00000400L, 0x00010020L, 0x08000420L, 0x00000400L,
+     0x00000000L, 0x08010420L, 0x08010020L, 0x08010400L, 0x00000420L,
+     0x00010000L, 0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L,
+     0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L, },
+    {/* nibble 5 */
+     0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L, 0x00200040L,
+     0x00002000L, 0x80002040L, 0x00200000L, 0x00002040L, 0x80202040L,
+     0x00202000L, 0x80000000L, 0x80002000L, 0x80000040L, 0x80200000L,
+     0x00202040L, 0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L,
+     0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L, 0x80202040L,
+     0x80200000L, 0x80000000L, 0x00002040L, 0x00000040L, 0x00202000L,
+     0x00202040L, 0x80002000L, 0x00002040L, 0x80000000L, 0x80002000L,
+     0x00202040L, 0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L,
+     0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L, 0x00200040L,
+     0x80202040L, 0x00202000L, 0x00000040L, 0x80202040L, 0x00202000L,
+     0x00200000L, 0x80002040L, 0x80000040L, 0x80200000L, 0x00202040L,
+     0x00000000L, 0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L,
+     0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L, },
+    {/* nibble 6 */
+     0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L, 0x01004204L,
+     0x00004004L, 0x00004200L, 0x00000000L, 0x01000000L, 0x01000204L,
+     0x00000204L, 0x01004000L, 0x00000004L, 0x01004200L, 0x01004000L,
+     0x00000204L, 0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L,
+     0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L, 0x01004004L,
+     0x00004204L, 0x01004200L, 0x00000004L, 0x00004204L, 0x01004004L,
+     0x00000200L, 0x01000000L, 0x00004204L, 0x01004000L, 0x01004004L,
+     0x00000204L, 0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L,
+     0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L, 0x00000200L,
+     0x01000004L, 0x00000004L, 0x01000200L, 0x00000000L, 0x01000204L,
+     0x01000200L, 0x00004200L, 0x00000204L, 0x00004000L, 0x01004204L,
+     0x01000000L, 0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L,
+     0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L, },
+    {/* nibble 7 */
+     0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L, 0x20020000L,
+     0x00800080L, 0x20800000L, 0x20820080L, 0x00000080L, 0x20000000L,
+     0x00820000L, 0x00020080L, 0x00820080L, 0x20020080L, 0x20000080L,
+     0x20800000L, 0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L,
+     0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L, 0x20000000L,
+     0x00800000L, 0x20020080L, 0x20800080L, 0x00800000L, 0x00020000L,
+     0x20820000L, 0x00000080L, 0x00800000L, 0x00020000L, 0x20000080L,
+     0x20820080L, 0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L,
+     0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L, 0x20820000L,
+     0x00000080L, 0x00800080L, 0x20020000L, 0x20820080L, 0x00800000L,
+     0x20800000L, 0x20000080L, 0x00820000L, 0x00020080L, 0x20020080L,
+     0x20800000L, 0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L,
+     0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L, }};
+
+#define HPERM_OP(a, t, n, m)                  \
+  ((t) = ((((a) << (16 - (n))) ^ (a)) & (m)), \
+   (a) = (a) ^ (t) ^ (t >> (16 - (n))))
+
+void DES_set_key(const DES_cblock *key, DES_key_schedule *schedule) {
+  static const int shifts2[16] = {0, 0, 1, 1, 1, 1, 1, 1,
+                                  0, 1, 1, 1, 1, 1, 1, 0};
+  uint32_t c, d, t, s, t2;
+  const uint8_t *in;
+  uint32_t *k;
+  int i;
+
+  k = &schedule->ks->deslong[0];
+  in = key->bytes;
+
+  c2l(in, c);
+  c2l(in, d);
+
+  /* do PC1 in 47 simple operations :-)
+   * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov)
+   * for the inspiration. :-) */
+  PERM_OP(d, c, t, 4, 0x0f0f0f0fL);
+  HPERM_OP(c, t, -2, 0xcccc0000L);
+  HPERM_OP(d, t, -2, 0xcccc0000L);
+  PERM_OP(d, c, t, 1, 0x55555555L);
+  PERM_OP(c, d, t, 8, 0x00ff00ffL);
+  PERM_OP(d, c, t, 1, 0x55555555L);
+  d = (((d & 0x000000ffL) << 16L) | (d & 0x0000ff00L) |
+       ((d & 0x00ff0000L) >> 16L) | ((c & 0xf0000000L) >> 4L));
+  c &= 0x0fffffffL;
+
+  for (i = 0; i < ITERATIONS; i++) {
+    if (shifts2[i]) {
+      c = ((c >> 2L) | (c << 26L));
+      d = ((d >> 2L) | (d << 26L));
+    } else {
+      c = ((c >> 1L) | (c << 27L));
+      d = ((d >> 1L) | (d << 27L));
+    }
+    c &= 0x0fffffffL;
+    d &= 0x0fffffffL;
+    /* could be a few less shifts but I am to lazy at this
+     * point in time to investigate */
+    s = des_skb[0][(c) & 0x3f] |
+        des_skb[1][((c >> 6L) & 0x03) | ((c >> 7L) & 0x3c)] |
+        des_skb[2][((c >> 13L) & 0x0f) | ((c >> 14L) & 0x30)] |
+        des_skb[3][((c >> 20L) & 0x01) | ((c >> 21L) & 0x06) |
+                   ((c >> 22L) & 0x38)];
+    t = des_skb[4][(d) & 0x3f] |
+        des_skb[5][((d >> 7L) & 0x03) | ((d >> 8L) & 0x3c)] |
+        des_skb[6][(d >> 15L) & 0x3f] |
+        des_skb[7][((d >> 21L) & 0x0f) | ((d >> 22L) & 0x30)];
+
+    /* table contained 0213 4657 */
+    t2 = ((t << 16L) | (s & 0x0000ffffL)) & 0xffffffffL;
+    *(k++) = ROTATE(t2, 30) & 0xffffffffL;
+
+    t2 = ((s >> 16L) | (t & 0xffff0000L));
+    *(k++) = ROTATE(t2, 26) & 0xffffffffL;
+  }
+}
+
+static void DES_encrypt1(uint32_t *data, const DES_key_schedule *ks, int enc) {
+  uint32_t l, r, t, u;
+  const uint32_t *s;
+
+  r = data[0];
+  l = data[1];
+
+  IP(r, l);
+  /* Things have been modified so that the initial rotate is done outside
+   * the loop.  This required the DES_SPtrans values in sp.h to be
+   * rotated 1 bit to the right. One perl script later and things have a
+   * 5% speed up on a sparc2. Thanks to Richard Outerbridge
+   * <71755.204@CompuServe.COM> for pointing this out. */
+  /* clear the top bits on machines with 8byte longs */
+  /* shift left by 2 */
+  r = ROTATE(r, 29) & 0xffffffffL;
+  l = ROTATE(l, 29) & 0xffffffffL;
+
+  s = ks->ks->deslong;
+  /* I don't know if it is worth the effort of loop unrolling the
+   * inner loop */
+  if (enc) {
+    D_ENCRYPT(l, r, 0);  /*  1 */
+    D_ENCRYPT(r, l, 2);  /*  2 */
+    D_ENCRYPT(l, r, 4);  /*  3 */
+    D_ENCRYPT(r, l, 6);  /*  4 */
+    D_ENCRYPT(l, r, 8);  /*  5 */
+    D_ENCRYPT(r, l, 10); /*  6 */
+    D_ENCRYPT(l, r, 12); /*  7 */
+    D_ENCRYPT(r, l, 14); /*  8 */
+    D_ENCRYPT(l, r, 16); /*  9 */
+    D_ENCRYPT(r, l, 18); /*  10 */
+    D_ENCRYPT(l, r, 20); /*  11 */
+    D_ENCRYPT(r, l, 22); /*  12 */
+    D_ENCRYPT(l, r, 24); /*  13 */
+    D_ENCRYPT(r, l, 26); /*  14 */
+    D_ENCRYPT(l, r, 28); /*  15 */
+    D_ENCRYPT(r, l, 30); /*  16 */
+  } else {
+    D_ENCRYPT(l, r, 30); /* 16 */
+    D_ENCRYPT(r, l, 28); /* 15 */
+    D_ENCRYPT(l, r, 26); /* 14 */
+    D_ENCRYPT(r, l, 24); /* 13 */
+    D_ENCRYPT(l, r, 22); /* 12 */
+    D_ENCRYPT(r, l, 20); /* 11 */
+    D_ENCRYPT(l, r, 18); /* 10 */
+    D_ENCRYPT(r, l, 16); /*  9 */
+    D_ENCRYPT(l, r, 14); /*  8 */
+    D_ENCRYPT(r, l, 12); /*  7 */
+    D_ENCRYPT(l, r, 10); /*  6 */
+    D_ENCRYPT(r, l, 8);  /*  5 */
+    D_ENCRYPT(l, r, 6);  /*  4 */
+    D_ENCRYPT(r, l, 4);  /*  3 */
+    D_ENCRYPT(l, r, 2);  /*  2 */
+    D_ENCRYPT(r, l, 0);  /*  1 */
+  }
+
+  /* rotate and clear the top bits on machines with 8byte longs */
+  l = ROTATE(l, 3) & 0xffffffffL;
+  r = ROTATE(r, 3) & 0xffffffffL;
+
+  FP(r, l);
+  data[0] = l;
+  data[1] = r;
+  l = r = t = u = 0;
+}
+
+static void DES_encrypt2(uint32_t *data, const DES_key_schedule *ks, int enc) {
+  uint32_t l, r, t, u;
+  const uint32_t *s;
+
+  r = data[0];
+  l = data[1];
+
+  /* Things have been modified so that the initial rotate is done outside the
+   * loop.  This required the DES_SPtrans values in sp.h to be rotated 1 bit to
+   * the right. One perl script later and things have a 5% speed up on a
+   * sparc2. Thanks to Richard Outerbridge <71755.204@CompuServe.COM> for
+   * pointing this out. */
+  /* clear the top bits on machines with 8byte longs */
+  r = ROTATE(r, 29) & 0xffffffffL;
+  l = ROTATE(l, 29) & 0xffffffffL;
+
+  s = ks->ks->deslong;
+  /* I don't know if it is worth the effort of loop unrolling the
+   * inner loop */
+  if (enc) {
+    D_ENCRYPT(l, r, 0);  /*  1 */
+    D_ENCRYPT(r, l, 2);  /*  2 */
+    D_ENCRYPT(l, r, 4);  /*  3 */
+    D_ENCRYPT(r, l, 6);  /*  4 */
+    D_ENCRYPT(l, r, 8);  /*  5 */
+    D_ENCRYPT(r, l, 10); /*  6 */
+    D_ENCRYPT(l, r, 12); /*  7 */
+    D_ENCRYPT(r, l, 14); /*  8 */
+    D_ENCRYPT(l, r, 16); /*  9 */
+    D_ENCRYPT(r, l, 18); /*  10 */
+    D_ENCRYPT(l, r, 20); /*  11 */
+    D_ENCRYPT(r, l, 22); /*  12 */
+    D_ENCRYPT(l, r, 24); /*  13 */
+    D_ENCRYPT(r, l, 26); /*  14 */
+    D_ENCRYPT(l, r, 28); /*  15 */
+    D_ENCRYPT(r, l, 30); /*  16 */
+  } else {
+    D_ENCRYPT(l, r, 30); /* 16 */
+    D_ENCRYPT(r, l, 28); /* 15 */
+    D_ENCRYPT(l, r, 26); /* 14 */
+    D_ENCRYPT(r, l, 24); /* 13 */
+    D_ENCRYPT(l, r, 22); /* 12 */
+    D_ENCRYPT(r, l, 20); /* 11 */
+    D_ENCRYPT(l, r, 18); /* 10 */
+    D_ENCRYPT(r, l, 16); /*  9 */
+    D_ENCRYPT(l, r, 14); /*  8 */
+    D_ENCRYPT(r, l, 12); /*  7 */
+    D_ENCRYPT(l, r, 10); /*  6 */
+    D_ENCRYPT(r, l, 8);  /*  5 */
+    D_ENCRYPT(l, r, 6);  /*  4 */
+    D_ENCRYPT(r, l, 4);  /*  3 */
+    D_ENCRYPT(l, r, 2);  /*  2 */
+    D_ENCRYPT(r, l, 0);  /*  1 */
+  }
+  /* rotate and clear the top bits on machines with 8byte longs */
+  data[0] = ROTATE(l, 3) & 0xffffffffL;
+  data[1] = ROTATE(r, 3) & 0xffffffffL;
+  l = r = t = u = 0;
+}
+
+static void DES_encrypt3(uint32_t *data, const DES_key_schedule *ks1,
+                         const DES_key_schedule *ks2,
+                         const DES_key_schedule *ks3) {
+  uint32_t l, r;
+
+  l = data[0];
+  r = data[1];
+  IP(l, r);
+  data[0] = l;
+  data[1] = r;
+  DES_encrypt2((uint32_t *)data, ks1, DES_ENCRYPT);
+  DES_encrypt2((uint32_t *)data, ks2, DES_DECRYPT);
+  DES_encrypt2((uint32_t *)data, ks3, DES_ENCRYPT);
+  l = data[0];
+  r = data[1];
+  FP(r, l);
+  data[0] = l;
+  data[1] = r;
+}
+
+static void DES_decrypt3(uint32_t *data, const DES_key_schedule *ks1,
+                         const DES_key_schedule *ks2,
+                         const DES_key_schedule *ks3) {
+  uint32_t l, r;
+
+  l = data[0];
+  r = data[1];
+  IP(l, r);
+  data[0] = l;
+  data[1] = r;
+  DES_encrypt2((uint32_t *)data, ks3, DES_DECRYPT);
+  DES_encrypt2((uint32_t *)data, ks2, DES_ENCRYPT);
+  DES_encrypt2((uint32_t *)data, ks1, DES_DECRYPT);
+  l = data[0];
+  r = data[1];
+  FP(r, l);
+  data[0] = l;
+  data[1] = r;
+}
+
+void DES_ecb_encrypt(const DES_cblock *in_block, DES_cblock *out_block,
+                     const DES_key_schedule *schedule, int is_encrypt) {
+  uint32_t l;
+  uint32_t ll[2];
+  const uint8_t *in = in_block->bytes;
+  uint8_t *out = out_block->bytes;
+
+  c2l(in, l);
+  ll[0] = l;
+  c2l(in, l);
+  ll[1] = l;
+  DES_encrypt1(ll, schedule, is_encrypt);
+  l = ll[0];
+  l2c(l, out);
+  l = ll[1];
+  l2c(l, out);
+  l = ll[0] = ll[1] = 0;
+}
+
+void DES_ncbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                      const DES_key_schedule *schedule, DES_cblock *ivec,
+                      int enc) {
+  uint32_t tin0, tin1;
+  uint32_t tout0, tout1, xor0, xor1;
+  uint32_t tin[2];
+  unsigned char *iv;
+
+  iv = ivec->bytes;
+
+  if (enc) {
+    c2l(iv, tout0);
+    c2l(iv, tout1);
+    for (; len >= 8; len -= 8) {
+      c2l(in, tin0);
+      c2l(in, tin1);
+      tin0 ^= tout0;
+      tin[0] = tin0;
+      tin1 ^= tout1;
+      tin[1] = tin1;
+      DES_encrypt1((uint32_t *)tin, schedule, DES_ENCRYPT);
+      tout0 = tin[0];
+      l2c(tout0, out);
+      tout1 = tin[1];
+      l2c(tout1, out);
+    }
+    if (len != 0) {
+      c2ln(in, tin0, tin1, len);
+      tin0 ^= tout0;
+      tin[0] = tin0;
+      tin1 ^= tout1;
+      tin[1] = tin1;
+      DES_encrypt1((uint32_t *)tin, schedule, DES_ENCRYPT);
+      tout0 = tin[0];
+      l2c(tout0, out);
+      tout1 = tin[1];
+      l2c(tout1, out);
+    }
+    iv = ivec->bytes;
+    l2c(tout0, iv);
+    l2c(tout1, iv);
+  } else {
+    c2l(iv, xor0);
+    c2l(iv, xor1);
+    for (; len >= 8; len -= 8) {
+      c2l(in, tin0);
+      tin[0] = tin0;
+      c2l(in, tin1);
+      tin[1] = tin1;
+      DES_encrypt1((uint32_t *)tin, schedule, DES_DECRYPT);
+      tout0 = tin[0] ^ xor0;
+      tout1 = tin[1] ^ xor1;
+      l2c(tout0, out);
+      l2c(tout1, out);
+      xor0 = tin0;
+      xor1 = tin1;
+    }
+    if (len != 0) {
+      c2l(in, tin0);
+      tin[0] = tin0;
+      c2l(in, tin1);
+      tin[1] = tin1;
+      DES_encrypt1((uint32_t *)tin, schedule, DES_DECRYPT);
+      tout0 = tin[0] ^ xor0;
+      tout1 = tin[1] ^ xor1;
+      l2cn(tout0, tout1, out, len);
+      xor0 = tin0;
+      xor1 = tin1;
+    }
+    iv = ivec->bytes;
+    l2c(xor0, iv);
+    l2c(xor1, iv);
+  }
+  tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+  tin[0] = tin[1] = 0;
+}
+
+void DES_ede3_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                          const DES_key_schedule *ks1,
+                          const DES_key_schedule *ks2,
+                          const DES_key_schedule *ks3, DES_cblock *ivec,
+                          int enc) {
+  uint32_t tin0, tin1;
+  uint32_t tout0, tout1, xor0, xor1;
+  uint32_t tin[2];
+  uint8_t *iv;
+
+  iv = ivec->bytes;
+
+  if (enc) {
+    c2l(iv, tout0);
+    c2l(iv, tout1);
+    for (; len >= 8; len -= 8) {
+      c2l(in, tin0);
+      c2l(in, tin1);
+      tin0 ^= tout0;
+      tin1 ^= tout1;
+
+      tin[0] = tin0;
+      tin[1] = tin1;
+      DES_encrypt3((uint32_t *)tin, ks1, ks2, ks3);
+      tout0 = tin[0];
+      tout1 = tin[1];
+
+      l2c(tout0, out);
+      l2c(tout1, out);
+    }
+    if (len != 0) {
+      c2ln(in, tin0, tin1, len);
+      tin0 ^= tout0;
+      tin1 ^= tout1;
+
+      tin[0] = tin0;
+      tin[1] = tin1;
+      DES_encrypt3((uint32_t *)tin, ks1, ks2, ks3);
+      tout0 = tin[0];
+      tout1 = tin[1];
+
+      l2c(tout0, out);
+      l2c(tout1, out);
+    }
+    iv = ivec->bytes;
+    l2c(tout0, iv);
+    l2c(tout1, iv);
+  } else {
+    uint32_t t0, t1;
+
+    c2l(iv, xor0);
+    c2l(iv, xor1);
+    for (; len >= 8; len -= 8) {
+      c2l(in, tin0);
+      c2l(in, tin1);
+
+      t0 = tin0;
+      t1 = tin1;
+
+      tin[0] = tin0;
+      tin[1] = tin1;
+      DES_decrypt3((uint32_t *)tin, ks1, ks2, ks3);
+      tout0 = tin[0];
+      tout1 = tin[1];
+
+      tout0 ^= xor0;
+      tout1 ^= xor1;
+      l2c(tout0, out);
+      l2c(tout1, out);
+      xor0 = t0;
+      xor1 = t1;
+    }
+    if (len != 0) {
+      c2l(in, tin0);
+      c2l(in, tin1);
+
+      t0 = tin0;
+      t1 = tin1;
+
+      tin[0] = tin0;
+      tin[1] = tin1;
+      DES_decrypt3((uint32_t *)tin, ks1, ks2, ks3);
+      tout0 = tin[0];
+      tout1 = tin[1];
+
+      tout0 ^= xor0;
+      tout1 ^= xor1;
+      l2cn(tout0, tout1, out, len);
+      xor0 = t0;
+      xor1 = t1;
+    }
+
+    iv = ivec->bytes;
+    l2c(xor0, iv);
+    l2c(xor1, iv);
+  }
+
+  tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+  tin[0] = tin[1] = 0;
+}
diff --git a/crypto/des/des.h b/crypto/des/des.h
new file mode 100644
index 0000000..a43e1d6
--- /dev/null
+++ b/crypto/des/des.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_DES_H
+#define OPENSSL_HEADER_DES_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+typedef struct DES_cblock_st {
+  uint8_t bytes[8];
+} DES_cblock;
+
+typedef struct DES_ks {
+  union {
+    DES_cblock cblock;
+    /* make sure things are correct size on machines with
+     * 8 byte longs */
+    uint32_t deslong[2];
+  } ks[16];
+} DES_key_schedule;
+
+
+#define DES_KEY_SZ (sizeof(DES_cblock))
+#define DES_SCHEDULE_SZ (sizeof(DES_key_schedule))
+
+#define DES_ENCRYPT 1
+#define DES_DECRYPT 0
+
+#define DES_CBC_MODE 0
+#define DES_PCBC_MODE 1
+
+/* DES_set_key performs a key schedule and initialises |schedule| with |key|. */
+void DES_set_key(const DES_cblock *key, DES_key_schedule *schedule);
+
+/* DES_ecb_encrypt encrypts (or decrypts, if |is_encrypt| is |DES_DECRYPT|) a
+ * single DES block (8 bytes) from in to out, using the key configured in
+ * |schedule|. */
+void DES_ecb_encrypt(const DES_cblock *in, DES_cblock *out,
+                     const DES_key_schedule *schedule, int is_encrypt);
+
+/* DES_ncbc_encrypt encrypts (or decrypts, if |enc| is |DES_DECRYPT|) |len|
+ * bytes from |in| to |out| with DES in CBC mode. */
+void DES_ncbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                      const DES_key_schedule *schedule, DES_cblock *ivec,
+                      int enc);
+
+/* DES_ede3_cbc_encrypt encrypts (or decrypts, if |enc| is |DES_DECRYPT|) |len|
+ * bytes from |in| to |out| with 3DES in CBC mode. 3DES uses three keys, thus
+ * the function takes three different |DES_key_schedule|s. */
+void DES_ede3_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                          const DES_key_schedule *ks1,
+                          const DES_key_schedule *ks2,
+                          const DES_key_schedule *ks3, DES_cblock *ivec,
+                          int enc);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_DES_H */
diff --git a/crypto/des/internal.h b/crypto/des/internal.h
new file mode 100644
index 0000000..d3a5cec
--- /dev/null
+++ b/crypto/des/internal.h
@@ -0,0 +1,229 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_DES_INTERNAL_H
+#define OPENSSL_HEADER_DES_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define c2l(c, l)                                                 \
+  (l = ((uint32_t)(*((c)++))), l |= ((uint32_t)(*((c)++))) << 8L, \
+   l |= ((uint32_t)(*((c)++))) << 16L, l |= ((uint32_t)(*((c)++))) << 24L)
+
+#define l2c(l, c)                                   \
+  (*((c)++) = (unsigned char)(((l)) & 0xff),        \
+   *((c)++) = (unsigned char)(((l) >> 8L) & 0xff),  \
+   *((c)++) = (unsigned char)(((l) >> 16L) & 0xff), \
+   *((c)++) = (unsigned char)(((l) >> 24L) & 0xff))
+
+/* NOTE - c is not incremented as per c2l */
+#define c2ln(c, l1, l2, n)                   \
+  {                                          \
+    c += n;                                  \
+    l1 = l2 = 0;                             \
+    switch (n) {                             \
+      case 8:                                \
+        l2 = ((uint32_t)(*(--(c)))) << 24L;  \
+      case 7:                                \
+        l2 |= ((uint32_t)(*(--(c)))) << 16L; \
+      case 6:                                \
+        l2 |= ((uint32_t)(*(--(c)))) << 8L;  \
+      case 5:                                \
+        l2 |= ((uint32_t)(*(--(c))));        \
+      case 4:                                \
+        l1 = ((uint32_t)(*(--(c)))) << 24L;  \
+      case 3:                                \
+        l1 |= ((uint32_t)(*(--(c)))) << 16L; \
+      case 2:                                \
+        l1 |= ((uint32_t)(*(--(c)))) << 8L;  \
+      case 1:                                \
+        l1 |= ((uint32_t)(*(--(c))));        \
+    }                                        \
+  }
+
+/* NOTE - c is not incremented as per l2c */
+#define l2cn(l1, l2, c, n)                                \
+  {                                                       \
+    c += n;                                               \
+    switch (n) {                                          \
+      case 8:                                             \
+        *(--(c)) = (unsigned char)(((l2) >> 24L) & 0xff); \
+      case 7:                                             \
+        *(--(c)) = (unsigned char)(((l2) >> 16L) & 0xff); \
+      case 6:                                             \
+        *(--(c)) = (unsigned char)(((l2) >> 8L) & 0xff);  \
+      case 5:                                             \
+        *(--(c)) = (unsigned char)(((l2)) & 0xff);        \
+      case 4:                                             \
+        *(--(c)) = (unsigned char)(((l1) >> 24L) & 0xff); \
+      case 3:                                             \
+        *(--(c)) = (unsigned char)(((l1) >> 16L) & 0xff); \
+      case 2:                                             \
+        *(--(c)) = (unsigned char)(((l1) >> 8L) & 0xff);  \
+      case 1:                                             \
+        *(--(c)) = (unsigned char)(((l1)) & 0xff);        \
+    }                                                     \
+  }
+
+/* IP and FP
+ * The problem is more of a geometric problem that random bit fiddling.
+ 0  1  2  3  4  5  6  7      62 54 46 38 30 22 14  6
+ 8  9 10 11 12 13 14 15      60 52 44 36 28 20 12  4
+16 17 18 19 20 21 22 23      58 50 42 34 26 18 10  2
+24 25 26 27 28 29 30 31  to  56 48 40 32 24 16  8  0
+
+32 33 34 35 36 37 38 39      63 55 47 39 31 23 15  7
+40 41 42 43 44 45 46 47      61 53 45 37 29 21 13  5
+48 49 50 51 52 53 54 55      59 51 43 35 27 19 11  3
+56 57 58 59 60 61 62 63      57 49 41 33 25 17  9  1
+
+The output has been subject to swaps of the form
+0 1 -> 3 1 but the odd and even bits have been put into
+2 3    2 0
+different words.  The main trick is to remember that
+t=((l>>size)^r)&(mask);
+r^=t;
+l^=(t<<size);
+can be used to swap and move bits between words.
+
+So l =  0  1  2  3  r = 16 17 18 19
+        4  5  6  7      20 21 22 23
+        8  9 10 11      24 25 26 27
+       12 13 14 15      28 29 30 31
+becomes (for size == 2 and mask == 0x3333)
+   t =   2^16  3^17 -- --   l =  0  1 16 17  r =  2  3 18 19
+         6^20  7^21 -- --        4  5 20 21       6  7 22 23
+        10^24 11^25 -- --        8  9 24 25      10 11 24 25
+        14^28 15^29 -- --       12 13 28 29      14 15 28 29
+
+Thanks for hints from Richard Outerbridge - he told me IP&FP
+could be done in 15 xor, 10 shifts and 5 ands.
+When I finally started to think of the problem in 2D
+I first got ~42 operations without xors.  When I remembered
+how to use xors :-) I got it to its final state.
+*/
+#define PERM_OP(a, b, t, n, m) \
+  ((t) = ((((a) >> (n)) ^ (b)) & (m)), (b) ^= (t), (a) ^= ((t) << (n)))
+
+#define IP(l, r)                        \
+  {                                     \
+    uint32_t tt;                        \
+    PERM_OP(r, l, tt, 4, 0x0f0f0f0fL);  \
+    PERM_OP(l, r, tt, 16, 0x0000ffffL); \
+    PERM_OP(r, l, tt, 2, 0x33333333L);  \
+    PERM_OP(l, r, tt, 8, 0x00ff00ffL);  \
+    PERM_OP(r, l, tt, 1, 0x55555555L);  \
+  }
+
+#define FP(l, r)                        \
+  {                                     \
+    uint32_t tt;                        \
+    PERM_OP(l, r, tt, 1, 0x55555555L);  \
+    PERM_OP(r, l, tt, 8, 0x00ff00ffL);  \
+    PERM_OP(l, r, tt, 2, 0x33333333L);  \
+    PERM_OP(r, l, tt, 16, 0x0000ffffL); \
+    PERM_OP(l, r, tt, 4, 0x0f0f0f0fL);  \
+  }
+
+#define LOAD_DATA(R, S, u, t, E0, E1) \
+  u = R ^ s[S];                            \
+  t = R ^ s[S + 1]
+
+#define D_ENCRYPT(LL, R, S)                                                    \
+  {                                                                            \
+    LOAD_DATA(R, S, u, t, E0, E1);                                             \
+    t = ROTATE(t, 4);                                                          \
+    LL ^=                                                                      \
+        DES_SPtrans[0][(u >> 2L) & 0x3f] ^ DES_SPtrans[2][(u >> 10L) & 0x3f] ^ \
+        DES_SPtrans[4][(u >> 18L) & 0x3f] ^                                    \
+        DES_SPtrans[6][(u >> 26L) & 0x3f] ^ DES_SPtrans[1][(t >> 2L) & 0x3f] ^ \
+        DES_SPtrans[3][(t >> 10L) & 0x3f] ^                                    \
+        DES_SPtrans[5][(t >> 18L) & 0x3f] ^ DES_SPtrans[7][(t >> 26L) & 0x3f]; \
+  }
+
+#define ITERATIONS 16
+#define HALF_ITERATIONS 8
+
+#if defined(_MSC_VER)
+#define ROTATE(a, n) (_lrotr(a, n))
+#elif defined(__ICC)
+#define ROTATE(a, n) (_rotr(a, n))
+#elif defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM) && \
+      !defined(__STRICT_ANSI__) && \
+      (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
+#define ROTATE(a, n)                                       \
+  ({                                                       \
+    unsigned int ret;                                      \
+    asm("rorl %1,%0" : "=r"(ret) : "I"(n), "0"(a) : "cc"); \
+    ret;                                                   \
+  })
+#endif
+
+#ifndef ROTATE
+#define ROTATE(a, n) (((a) >> (n)) + ((a) << (32 - (n))))
+#endif
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_DES_INTERNAL_H */
diff --git a/crypto/dh/CMakeLists.txt b/crypto/dh/CMakeLists.txt
new file mode 100644
index 0000000..719606c
--- /dev/null
+++ b/crypto/dh/CMakeLists.txt
@@ -0,0 +1,22 @@
+include_directories(. .. ../../include)
+
+add_library(
+	dh
+
+	OBJECT
+
+	dh.c
+	dh_impl.c
+	params.c
+	check.c
+	dh_asn1.c
+	dh_error.c
+)
+
+add_executable(
+	dh_test
+
+	dh_test.c
+)
+
+target_link_libraries(dh_test crypto)
diff --git a/crypto/dh/check.c b/crypto/dh/check.c
new file mode 100644
index 0000000..f5099d5
--- /dev/null
+++ b/crypto/dh/check.c
@@ -0,0 +1,177 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/dh.h>
+
+#include <openssl/bn.h>
+
+#include "internal.h"
+
+
+int DH_check_pub_key(const DH *dh, const BIGNUM *pub_key, int *ret) {
+  int ok = 0;
+  BIGNUM q;
+
+  *ret = 0;
+  BN_init(&q);
+  if (!BN_set_word(&q, 1)) {
+    goto err;
+  }
+
+  if (BN_cmp(pub_key, &q) <= 0) {
+    *ret |= DH_CHECK_PUBKEY_TOO_SMALL;
+  }
+  if (!BN_copy(&q, dh->p) ||
+      !BN_sub_word(&q, 1)) {
+    goto err;
+  }
+  if (BN_cmp(pub_key, &q) >= 0) {
+    *ret |= DH_CHECK_PUBKEY_TOO_LARGE;
+  }
+
+  ok = 1;
+
+err:
+  BN_free(&q);
+  return ok;
+}
+
+
+int DH_check(const DH *dh, int *ret) {
+  /* Check that p is a safe prime and if g is 2, 3 or 5, check that it is a
+   * suitable generator where:
+   *   for 2, p mod 24 == 11
+   *   for 3, p mod 12 == 5
+   *   for 5, p mod 10 == 3 or 7
+   * should hold.
+   */
+  int ok = 0;
+  BN_CTX *ctx = NULL;
+  BN_ULONG l;
+  BIGNUM *t1 = NULL, *t2 = NULL;
+
+  *ret = 0;
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  t1 = BN_CTX_get(ctx);
+  t2 = BN_CTX_get(ctx);
+  if (t2 == NULL) {
+    goto err;
+  }
+
+  if (dh->q) {
+    if (BN_cmp(dh->g, BN_value_one()) <= 0) {
+      *ret |= DH_CHECK_NOT_SUITABLE_GENERATOR;
+    } else if (BN_cmp(dh->g, dh->p) >= 0) {
+      *ret |= DH_CHECK_NOT_SUITABLE_GENERATOR;
+    } else {
+      /* Check g^q == 1 mod p */
+      if (!BN_mod_exp(t1, dh->g, dh->q, dh->p, ctx)) {
+        goto err;
+      }
+      if (!BN_is_one(t1)) {
+        *ret |= DH_CHECK_NOT_SUITABLE_GENERATOR;
+      }
+    }
+    if (!BN_is_prime_ex(dh->q, BN_prime_checks, ctx, NULL)) {
+      *ret |= DH_CHECK_Q_NOT_PRIME;
+    }
+    /* Check p == 1 mod q  i.e. q divides p - 1 */
+    if (!BN_div(t1, t2, dh->p, dh->q, ctx)) {
+      goto err;
+    }
+    if (!BN_is_one(t2)) {
+      *ret |= DH_CHECK_INVALID_Q_VALUE;
+    }
+    if (dh->j && BN_cmp(dh->j, t1)) {
+      *ret |= DH_CHECK_INVALID_J_VALUE;
+    }
+  } else if (BN_is_word(dh->g, DH_GENERATOR_2)) {
+    l = BN_mod_word(dh->p, 24);
+    if (l != 11) {
+      *ret |= DH_CHECK_NOT_SUITABLE_GENERATOR;
+    }
+  } else if (BN_is_word(dh->g, DH_GENERATOR_5)) {
+    l = BN_mod_word(dh->p, 10);
+    if (l != 3 && l != 7) {
+      *ret |= DH_CHECK_NOT_SUITABLE_GENERATOR;
+    }
+  } else {
+    *ret |= DH_CHECK_UNABLE_TO_CHECK_GENERATOR;
+  }
+
+  if (!BN_is_prime_ex(dh->p, BN_prime_checks, ctx, NULL)) {
+    *ret |= DH_CHECK_P_NOT_PRIME;
+  } else if (!dh->q) {
+    if (!BN_rshift1(t1, dh->p)) {
+      goto err;
+    }
+    if (!BN_is_prime_ex(t1, BN_prime_checks, ctx, NULL)) {
+      *ret |= DH_CHECK_P_NOT_SAFE_PRIME;
+    }
+  }
+  ok = 1;
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  return ok;
+}
diff --git a/crypto/dh/dh.c b/crypto/dh/dh.c
new file mode 100644
index 0000000..3e6ae6d
--- /dev/null
+++ b/crypto/dh/dh.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/dh.h>
+
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/ex_data.h>
+#include <openssl/mem.h>
+#include <openssl/thread.h>
+
+#include "internal.h"
+
+
+extern const DH_METHOD DH_default_method;
+
+DH *DH_new(void) { return DH_new_method(NULL); }
+
+DH *DH_new_method(const ENGINE *engine) {
+  DH *dh = (DH *)OPENSSL_malloc(sizeof(DH));
+  if (dh == NULL) {
+    OPENSSL_PUT_ERROR(DH, DH_new_method, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(dh, 0, sizeof(DH));
+
+  if (engine) {
+    dh->meth = ENGINE_get_DH_method(engine);
+  }
+
+  if (dh->meth == NULL) {
+    dh->meth = (DH_METHOD*) &DH_default_method;
+  }
+  METHOD_ref(dh->meth);
+
+  dh->references = 1;
+  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_DH, dh, &dh->ex_data)) {
+    OPENSSL_free(dh);
+    return NULL;
+  }
+
+  if (dh->meth->init && !dh->meth->init(dh)) {
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DH, dh, &dh->ex_data);
+    METHOD_unref(dh->meth);
+    OPENSSL_free(dh);
+    return NULL;
+  }
+
+  return dh;
+}
+
+void DH_free(DH *dh) {
+  if (dh == NULL) {
+    return;
+  }
+
+  if (CRYPTO_add(&dh->references, -1, CRYPTO_LOCK_DH) > 0) {
+    return;
+  }
+
+  if (dh->meth->finish) {
+    dh->meth->finish(dh);
+  }
+  METHOD_unref(dh->meth);
+
+  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DH, dh, &dh->ex_data);
+
+  if (dh->method_mont_p) BN_MONT_CTX_free(dh->method_mont_p);
+  if (dh->p != NULL) BN_clear_free(dh->p);
+  if (dh->g != NULL) BN_clear_free(dh->g);
+  if (dh->q != NULL) BN_clear_free(dh->q);
+  if (dh->j != NULL) BN_clear_free(dh->j);
+  if (dh->seed) OPENSSL_free(dh->seed);
+  if (dh->counter != NULL) BN_clear_free(dh->counter);
+  if (dh->pub_key != NULL) BN_clear_free(dh->pub_key);
+  if (dh->priv_key != NULL) BN_clear_free(dh->priv_key);
+
+  OPENSSL_free(dh);
+}
+
+int DH_generate_parameters_ex(DH *dh, int prime_bits, int generator, BN_GENCB *cb) {
+  if (dh->meth->generate_parameters) {
+    return dh->meth->generate_parameters(dh, prime_bits, generator, cb);
+  }
+  return DH_default_method.generate_parameters(dh, prime_bits, generator, cb);
+}
+
+int DH_generate_key(DH *dh) {
+  if (dh->meth->generate_key) {
+    return dh->meth->generate_key(dh);
+  }
+  return DH_default_method.generate_key(dh);
+}
+
+ssize_t DH_compute_key(unsigned char *out, const BIGNUM *peers_key, DH *dh) {
+  if (dh->meth->compute_key) {
+    return dh->meth->compute_key(dh, out, peers_key);
+  }
+  return DH_default_method.compute_key(dh, out, peers_key);
+}
+
+int DH_size(const DH *dh) { return BN_num_bytes(dh->p); }
+
+int DH_up_ref(DH *r) {
+  CRYPTO_add(&r->references, 1, CRYPTO_LOCK_DH);
+  return 1;
+}
+
+static int int_dh_bn_cpy(BIGNUM **dst, const BIGNUM *src) {
+  BIGNUM *a = NULL;
+
+  if (src) {
+    a = BN_dup(src);
+    if (!a) {
+      return 0;
+    }
+  }
+
+  if (*dst) {
+    BN_free(*dst);
+  }
+  *dst = a;
+  return 1;
+}
+
+static int int_dh_param_copy(DH *to, const DH *from, int is_x942) {
+  if (is_x942 == -1) {
+    is_x942 = !!from->q;
+  }
+  if (!int_dh_bn_cpy(&to->p, from->p) ||
+      !int_dh_bn_cpy(&to->g, from->g)) {
+    return 0;
+  }
+
+  if (!is_x942) {
+    return 1;
+  }
+
+  if (!int_dh_bn_cpy(&to->q, from->q) ||
+      !int_dh_bn_cpy(&to->j, from->j)) {
+    return 0;
+  }
+
+  if (to->seed) {
+    OPENSSL_free(to->seed);
+    to->seed = NULL;
+    to->seedlen = 0;
+  }
+  if (from->seed) {
+    to->seed = BUF_memdup(from->seed, from->seedlen);
+    if (!to->seed) {
+      return 0;
+    }
+    to->seedlen = from->seedlen;
+  }
+
+  return 1;
+}
+
+DH *DHparams_dup(const DH *dh) {
+  DH *ret = DH_new();
+  if (!ret) {
+    return NULL;
+  }
+
+  if (!int_dh_param_copy(ret, dh, -1)) {
+    DH_free(ret);
+    return NULL;
+  }
+
+  return ret;
+}
+
+int DH_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                        CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
+  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_DH, argl, argp, new_func,
+                                 dup_func, free_func);
+}
+
+int DH_set_ex_data(DH *d, int idx, void *arg) {
+  return (CRYPTO_set_ex_data(&d->ex_data, idx, arg));
+}
+
+void *DH_get_ex_data(DH *d, int idx) {
+  return (CRYPTO_get_ex_data(&d->ex_data, idx));
+}
diff --git a/crypto/dh/dh.h b/crypto/dh/dh.h
new file mode 100644
index 0000000..d1b84f9
--- /dev/null
+++ b/crypto/dh/dh.h
@@ -0,0 +1,259 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_DH_H
+#define OPENSSL_HEADER_DH_H
+
+#include <openssl/base.h>
+
+#include <openssl/engine.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* DH contains functions for performing Diffie-Hellman key agreement in
+ * multiplicative groups. */
+
+
+/* Allocation and destruction. */
+
+/* DH_new returns a new, empty DH object or NULL on error. */
+DH *DH_new(void);
+
+/* DH_new_method acts the same as |DH_new| but takes an explicit |ENGINE|. */
+DH *DH_new_method(const ENGINE *engine);
+
+/* DH_free decrements the reference count of |dh| and frees it if the reference
+ * count drops to zero. */
+void DH_free(DH *dh);
+
+/* DH_up_ref increments the reference count of |dh|. */
+int DH_up_ref(DH *dh);
+
+
+/* Standard parameters.
+ *
+ * These functions return new DH objects with standard parameters configured
+ * that use the given ENGINE, which may be NULL. They return NULL on allocation
+ * failure. */
+
+/* These parameters are taken from RFC 5114. */
+
+DH *DH_get_1024_160(const ENGINE *engine);
+DH *DH_get_2048_224(const ENGINE *engine);
+DH *DH_get_2048_256(const ENGINE *engine);
+
+
+/* Parameter generation. */
+
+#define DH_GENERATOR_2 2
+#define DH_GENERATOR_5 5
+
+/* DH_generate_parameters_ex generates a suitable Diffie-Hellman group with a
+ * prime that is |prime_bits| long and stores it in |dh|. The generator of the
+ * group will be |generator|, which should be |DH_GENERATOR_2| unless there's a
+ * good reason to use a different value. The |cb| argument contains a callback
+ * function that will be called during the generation. See the documentation in
+ * |bn.h| about this. In addition to the callback invocations from |BN|, |cb|
+ * will also be called with |event| equal to three when the generation is
+ * complete. */
+int DH_generate_parameters_ex(DH *dh, int prime_bits, int generator,
+                              BN_GENCB *cb);
+
+
+/* Diffie-Hellman operations. */
+
+/* DH_generate_key generates a new, random, private key and stores it in
+ * |dh|. It returns one on success and zero on error. */
+int DH_generate_key(DH *dh);
+
+/* DH_compute_key calculates the shared key between |dh| and |peers_key| and
+ * writes it as a big-endian integer into |out|, which must have |DH_size|
+ * bytes of space. It returns the number of bytes written, or a negative number
+ * on error. */
+ssize_t DH_compute_key(uint8_t *out, const BIGNUM *peers_key, DH *dh);
+
+
+/* Utility functions. */
+
+/* DH_size returns the number of bytes in the DH group's prime. */
+int DH_size(const DH *dh);
+
+#define DH_CHECK_P_NOT_PRIME 0x01
+#define DH_CHECK_P_NOT_SAFE_PRIME 0x02
+#define DH_CHECK_UNABLE_TO_CHECK_GENERATOR 0x04
+#define DH_CHECK_NOT_SUITABLE_GENERATOR 0x08
+#define DH_CHECK_Q_NOT_PRIME 0x10
+#define DH_CHECK_INVALID_Q_VALUE 0x20
+#define DH_CHECK_INVALID_J_VALUE 0x40
+
+/* DH_check checks the suitability of |dh| as a Diffie-Hellman group. and sets
+ * |DH_CHECK_*| flags in |*out_flags| if it finds any errors. It returns one if
+ * |*out_flags| was successfully set and zero on error.
+ *
+ * Note: these checks may be quite computationally expensive. */
+int DH_check(const DH *dh, int *out_flags);
+
+#define DH_CHECK_PUBKEY_TOO_SMALL 1
+#define DH_CHECK_PUBKEY_TOO_LARGE 2
+
+/* DH_check_pub_key checks the suitability of |pub_key| as a public key for the
+ * DH group in |dh| and sets |DH_CHECK_PUBKEY_*| flags in |*out_flags| if it
+ * finds any errors. It returns one if |*out_flags| was successfully set and
+ * zero on error. */
+int DH_check_pub_key(const DH *dh, const BIGNUM *pub_key, int *out_flags);
+
+/* DHparams_dup allocates a fresh |DH| and copies the parameters from |dh| into
+ * it. It returns the new |DH| or NULL on error. */
+DH *DHparams_dup(const DH *dh);
+
+
+/* ASN.1 functions. */
+
+/* d2i_DHparams parses an ASN.1, DER encoded Diffie-Hellman parameters
+ * structure from |len| bytes at |*inp|. If |ret| is not NULL then, on exit, a
+ * pointer to the result is in |*ret|. If |*ret| is already non-NULL on entry
+ * then the result is written directly into |*ret|, otherwise a fresh |DH| is
+ * allocated. On successful exit, |*inp| is advanced past the DER structure. It
+ * returns the result or NULL on error. */
+DH *d2i_DHparams(DH **ret, const unsigned char **inp, long len);
+
+/* i2d_DHparams marshals |in| to an ASN.1, DER structure. If |outp| is not NULL
+ * then the result is written to |*outp| and |*outp| is advanced just past the
+ * output. It returns the number of bytes in the result, whether written or
+ * not, or a negative value on error. */
+int i2d_DHparams(const DH *in, unsigned char **outp);
+
+
+/* ex_data functions.
+ *
+ * These functions are wrappers. See |ex_data.h| for details. */
+
+int DH_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                        CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int DH_set_ex_data(DH *d, int idx, void *arg);
+void *DH_get_ex_data(DH *d, int idx);
+
+
+/* dh_method contains function pointers to override the implementation of DH.
+ * See |engine.h| for details. */
+struct dh_method {
+  struct openssl_method_common_st common;
+
+  /* app_data is an opaque pointer for the method to use. */
+  void *app_data;
+
+  /* init is called just before the return of |DH_new_method|. It returns one
+   * on success or zero on error. */
+  int (*init)(DH *dh);
+
+  /* finish is called before |dh| is destructed. */
+  void (*finish)(DH *dh);
+
+  /* generate_parameters is called by |DH_generate_parameters_ex|. */
+  int (*generate_parameters)(DH *dh, int prime_bits, int generator,
+                             BN_GENCB *cb);
+
+  /* generate_parameters is called by |DH_generate_key|. */
+  int (*generate_key)(DH *dh);
+
+  /* generate_parameters is called by |DH_compute_key|. */
+  ssize_t (*compute_key)(DH *dh, uint8_t *out, const BIGNUM *pub_key);
+};
+
+struct dh_st {
+  DH_METHOD *meth;
+
+  BIGNUM *p;
+  BIGNUM *g;
+  BIGNUM *pub_key;  /* g^x */
+  BIGNUM *priv_key; /* x */
+
+  /* priv_length contains the length, in bits, of the private value. If zero,
+   * the private value will be the same length as |p|. */
+  unsigned priv_length;
+  BN_MONT_CTX *method_mont_p;
+
+  /* Place holders if we want to do X9.42 DH */
+  BIGNUM *q;
+  BIGNUM *j;
+  unsigned char *seed;
+  int seedlen;
+  BIGNUM *counter;
+
+  int flags;
+  int references;
+  CRYPTO_EX_DATA ex_data;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define DH_F_generate_parameters 100
+#define DH_F_generate_key 101
+#define DH_F_compute_key 102
+#define DH_F_DH_new_method 103
+#define DH_R_INVALID_PUBKEY 100
+#define DH_R_BAD_GENERATOR 101
+#define DH_R_MODULUS_TOO_LARGE 102
+#define DH_R_NO_PRIVATE_VALUE 103
+
+#endif  /* OPENSSL_HEADER_DH_H */
diff --git a/crypto/dh/dh_asn1.c b/crypto/dh/dh_asn1.c
new file mode 100644
index 0000000..73cd4df
--- /dev/null
+++ b/crypto/dh/dh_asn1.c
@@ -0,0 +1,84 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2000.
+ */
+/* ====================================================================
+ * Copyright (c) 2000-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/dh.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+#include "internal.h"
+
+/* Override the default free and new methods */
+static int dh_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                 void *exarg) {
+  if (operation == ASN1_OP_NEW_PRE) {
+    *pval = (ASN1_VALUE *)DH_new();
+    if (*pval) {
+      return 2;
+    }
+    return 0;
+  } else if (operation == ASN1_OP_FREE_PRE) {
+    DH_free((DH *)*pval);
+    *pval = NULL;
+    return 2;
+  }
+  return 1;
+}
+
+ASN1_SEQUENCE_cb(DHparams, dh_cb) = {
+    ASN1_SIMPLE(DH, p, BIGNUM), ASN1_SIMPLE(DH, g, BIGNUM),
+    ASN1_OPT(DH, priv_length, ZLONG)} ASN1_SEQUENCE_END_cb(DH, DHparams);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DH, DHparams, DHparams)
diff --git a/crypto/dh/dh_error.c b/crypto/dh/dh_error.c
new file mode 100644
index 0000000..8a7041c
--- /dev/null
+++ b/crypto/dh/dh_error.c
@@ -0,0 +1,29 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "dh.h"
+
+const ERR_STRING_DATA DH_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_DH, DH_F_DH_new_method, 0), "DH_new_method"},
+  {ERR_PACK(ERR_LIB_DH, DH_F_compute_key, 0), "compute_key"},
+  {ERR_PACK(ERR_LIB_DH, DH_F_generate_key, 0), "generate_key"},
+  {ERR_PACK(ERR_LIB_DH, DH_F_generate_parameters, 0), "generate_parameters"},
+  {ERR_PACK(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR), "BAD_GENERATOR"},
+  {ERR_PACK(ERR_LIB_DH, 0, DH_R_INVALID_PUBKEY), "INVALID_PUBKEY"},
+  {ERR_PACK(ERR_LIB_DH, 0, DH_R_MODULUS_TOO_LARGE), "MODULUS_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_DH, 0, DH_R_NO_PRIVATE_VALUE), "NO_PRIVATE_VALUE"},
+  {0, NULL},
+};
diff --git a/crypto/dh/dh_impl.c b/crypto/dh/dh_impl.c
new file mode 100644
index 0000000..edc3db7
--- /dev/null
+++ b/crypto/dh/dh_impl.c
@@ -0,0 +1,323 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/dh.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+
+#define OPENSSL_DH_MAX_MODULUS_BITS 10000
+
+static int generate_parameters(DH *ret, int prime_bits, int generator, BN_GENCB *cb) {
+  /* We generate DH parameters as follows
+   * find a prime q which is prime_bits/2 bits long.
+   * p=(2*q)+1 or (p-1)/2 = q
+   * For this case, g is a generator if
+   * g^((p-1)/q) mod p != 1 for values of q which are the factors of p-1.
+   * Since the factors of p-1 are q and 2, we just need to check
+   * g^2 mod p != 1 and g^q mod p != 1.
+   *
+   * Having said all that,
+   * there is another special case method for the generators 2, 3 and 5.
+   * for 2, p mod 24 == 11
+   * for 3, p mod 12 == 5  <<<<< does not work for safe primes.
+   * for 5, p mod 10 == 3 or 7
+   *
+   * Thanks to Phil Karn <karn@qualcomm.com> for the pointers about the
+   * special generators and for answering some of my questions.
+   *
+   * I've implemented the second simple method :-).
+   * Since DH should be using a safe prime (both p and q are prime),
+   * this generator function can take a very very long time to run.
+   */
+
+  /* Actually there is no reason to insist that 'generator' be a generator.
+   * It's just as OK (and in some sense better) to use a generator of the
+   * order-q subgroup.
+   */
+
+  BIGNUM *t1, *t2;
+  int g, ok = 0;
+  BN_CTX *ctx = NULL;
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  t1 = BN_CTX_get(ctx);
+  t2 = BN_CTX_get(ctx);
+  if (t1 == NULL || t2 == NULL) {
+    goto err;
+  }
+
+  /* Make sure 'ret' has the necessary elements */
+  if (!ret->p && ((ret->p = BN_new()) == NULL)) {
+    goto err;
+  }
+  if (!ret->g && ((ret->g = BN_new()) == NULL)) {
+    goto err;
+  }
+
+  if (generator <= 1) {
+    OPENSSL_PUT_ERROR(DH, generate_parameters, DH_R_BAD_GENERATOR);
+    goto err;
+  }
+  if (generator == DH_GENERATOR_2) {
+    if (!BN_set_word(t1, 24)) {
+      goto err;
+    }
+    if (!BN_set_word(t2, 11)) {
+      goto err;
+    }
+    g = 2;
+  } else if (generator == DH_GENERATOR_5) {
+    if (!BN_set_word(t1, 10)) {
+      goto err;
+    }
+    if (!BN_set_word(t2, 3)) {
+      goto err;
+    }
+    /* BN_set_word(t3,7); just have to miss
+     * out on these ones :-( */
+    g = 5;
+  } else {
+    /* in the general case, don't worry if 'generator' is a
+     * generator or not: since we are using safe primes,
+     * it will generate either an order-q or an order-2q group,
+     * which both is OK */
+    if (!BN_set_word(t1, 2)) {
+      goto err;
+    }
+    if (!BN_set_word(t2, 1)) {
+      goto err;
+    }
+    g = generator;
+  }
+
+  if (!BN_generate_prime_ex(ret->p, prime_bits, 1, t1, t2, cb)) {
+    goto err;
+  }
+  if (!BN_GENCB_call(cb, 3, 0)) {
+    goto err;
+  }
+  if (!BN_set_word(ret->g, g)) {
+    goto err;
+  }
+  ok = 1;
+
+err:
+  if (!ok) {
+    OPENSSL_PUT_ERROR(DH, generate_parameters, ERR_R_BN_LIB);
+  }
+
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  return ok;
+}
+
+static int generate_key(DH *dh) {
+  int ok = 0;
+  int generate_new_key = 0;
+  unsigned l;
+  BN_CTX *ctx;
+  BN_MONT_CTX *mont = NULL;
+  BIGNUM *pub_key = NULL, *priv_key = NULL;
+  BIGNUM local_priv;
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+  if (dh->priv_key == NULL) {
+    priv_key = BN_new();
+    if (priv_key == NULL) {
+      goto err;
+    }
+    generate_new_key = 1;
+  } else {
+    priv_key = dh->priv_key;
+  }
+
+  if (dh->pub_key == NULL) {
+    pub_key = BN_new();
+    if (pub_key == NULL) {
+      goto err;
+    }
+  } else {
+    pub_key = dh->pub_key;
+  }
+
+  mont =
+      BN_MONT_CTX_set_locked(&dh->method_mont_p, CRYPTO_LOCK_DH, dh->p, ctx);
+  if (!mont) {
+    goto err;
+  }
+
+  if (generate_new_key) {
+    if (dh->q) {
+      do {
+        if (!BN_rand_range(priv_key, dh->q)) {
+          goto err;
+        }
+      } while (BN_is_zero(priv_key) || BN_is_one(priv_key));
+    } else {
+      /* secret exponent length */
+      l = dh->priv_length ? dh->priv_length : BN_num_bits(dh->p) - 1;
+      if (!BN_rand(priv_key, l, 0, 0)) {
+        goto err;
+      }
+    }
+  }
+
+  BN_with_flags(&local_priv, priv_key, BN_FLG_CONSTTIME);
+  if (!BN_mod_exp_mont(pub_key, dh->g, &local_priv, dh->p, ctx, mont)) {
+    goto err;
+  }
+
+  dh->pub_key = pub_key;
+  dh->priv_key = priv_key;
+  ok = 1;
+
+err:
+  if (ok != 1) {
+    OPENSSL_PUT_ERROR(DH, generate_key, ERR_R_BN_LIB);
+  }
+
+  if (pub_key != NULL && dh->pub_key == NULL) {
+    BN_free(pub_key);
+  }
+  if (priv_key != NULL && dh->priv_key == NULL) {
+    BN_free(priv_key);
+  }
+  BN_CTX_free(ctx);
+  return ok;
+}
+
+static ssize_t compute_key(DH *dh, unsigned char *out, const BIGNUM *pub_key) {
+  BN_CTX *ctx = NULL;
+  BN_MONT_CTX *mont = NULL;
+  BIGNUM *shared_key;
+  int ret = -1;
+  int check_result;
+  BIGNUM local_priv;
+
+  if (BN_num_bits(dh->p) > OPENSSL_DH_MAX_MODULUS_BITS) {
+    OPENSSL_PUT_ERROR(DH, compute_key, DH_R_MODULUS_TOO_LARGE);
+    goto err;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  shared_key = BN_CTX_get(ctx);
+  if (shared_key == NULL) {
+    goto err;
+  }
+
+  if (dh->priv_key == NULL) {
+    OPENSSL_PUT_ERROR(DH, compute_key, DH_R_NO_PRIVATE_VALUE);
+    goto err;
+  }
+
+  mont =
+      BN_MONT_CTX_set_locked(&dh->method_mont_p, CRYPTO_LOCK_DH, dh->p, ctx);
+  if (!mont) {
+    goto err;
+  }
+
+  if (!DH_check_pub_key(dh, pub_key, &check_result) || check_result) {
+    OPENSSL_PUT_ERROR(DH, compute_key, DH_R_INVALID_PUBKEY);
+    goto err;
+  }
+
+  BN_with_flags(&local_priv, dh->priv_key, BN_FLG_CONSTTIME);
+  if (!BN_mod_exp_mont(shared_key, pub_key, &local_priv, dh->p, ctx,
+                       mont)) {
+    OPENSSL_PUT_ERROR(DH, compute_key, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  ret = BN_bn2bin(shared_key, out);
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+
+  return ret;
+}
+
+const struct dh_method DH_default_method = {
+  {
+    0 /* references */,
+    1 /* is_static */,
+  },
+  NULL /* app_data */,
+  NULL /* init */,
+  NULL /* finish */,
+  generate_parameters,
+  generate_key,
+  compute_key,
+};
diff --git a/crypto/dh/dh_test.c b/crypto/dh/dh_test.c
new file mode 100644
index 0000000..73dbba9
--- /dev/null
+++ b/crypto/dh/dh_test.c
@@ -0,0 +1,498 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/dh.h>
+
+#include <stdio.h>
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+static int cb(int p, int n, BN_GENCB *arg) {
+  char c = '*';
+
+  if (p == 0)
+    c = '.';
+  if (p == 1)
+    c = '+';
+  if (p == 2)
+    c = '*';
+  if (p == 3)
+    c = '\n';
+  BIO_write(arg->arg, &c, 1);
+  (void)BIO_flush(arg->arg);
+
+  return 1;
+}
+
+static int run_rfc5114_tests(void);
+
+int main(int argc, char *argv[]) {
+  BN_GENCB _cb;
+  DH *a;
+  DH *b = NULL;
+  char buf[12];
+  unsigned char *abuf = NULL, *bbuf = NULL;
+  int i, alen, blen, aout, bout, ret = 1;
+  BIO *out;
+
+  out = BIO_new(BIO_s_file());
+  if (out == NULL) {
+    return 1;
+  }
+  BIO_set_fp(out, stdout, BIO_NOCLOSE);
+
+  BN_GENCB_set(&_cb, &cb, out);
+  if (((a = DH_new()) == NULL) ||
+      !DH_generate_parameters_ex(a, 64, DH_GENERATOR_5, &_cb)) {
+    goto err;
+  }
+
+  if (!DH_check(a, &i))
+    goto err;
+  if (i & DH_CHECK_P_NOT_PRIME)
+    BIO_puts(out, "p value is not prime\n");
+  if (i & DH_CHECK_P_NOT_SAFE_PRIME)
+    BIO_puts(out, "p value is not a safe prime\n");
+  if (i & DH_CHECK_UNABLE_TO_CHECK_GENERATOR)
+    BIO_puts(out, "unable to check the generator value\n");
+  if (i & DH_CHECK_NOT_SUITABLE_GENERATOR)
+    BIO_puts(out, "the g value is not a generator\n");
+
+  BIO_puts(out, "\np    =");
+  BN_print(out, a->p);
+  BIO_puts(out, "\ng    =");
+  BN_print(out, a->g);
+  BIO_puts(out, "\n");
+
+  b = DH_new();
+  if (b == NULL) {
+    goto err;
+  }
+
+  b->p = BN_dup(a->p);
+  b->g = BN_dup(a->g);
+  if (b->p == NULL || b->g == NULL) {
+    goto err;
+  }
+
+  if (!DH_generate_key(a))
+    goto err;
+  BIO_puts(out, "pri 1=");
+  BN_print(out, a->priv_key);
+  BIO_puts(out, "\npub 1=");
+  BN_print(out, a->pub_key);
+  BIO_puts(out, "\n");
+
+  if (!DH_generate_key(b))
+    goto err;
+  BIO_puts(out, "pri 2=");
+  BN_print(out, b->priv_key);
+  BIO_puts(out, "\npub 2=");
+  BN_print(out, b->pub_key);
+  BIO_puts(out, "\n");
+
+  alen = DH_size(a);
+  abuf = (unsigned char *)OPENSSL_malloc(alen);
+  aout = DH_compute_key(abuf, b->pub_key, a);
+
+  BIO_puts(out, "key1 =");
+  for (i = 0; i < aout; i++) {
+    sprintf(buf, "%02X", abuf[i]);
+    BIO_puts(out, buf);
+  }
+  BIO_puts(out, "\n");
+
+  blen = DH_size(b);
+  bbuf = (unsigned char *)OPENSSL_malloc(blen);
+  bout = DH_compute_key(bbuf, a->pub_key, b);
+
+  BIO_puts(out, "key2 =");
+  for (i = 0; i < bout; i++) {
+    sprintf(buf, "%02X", bbuf[i]);
+    BIO_puts(out, buf);
+  }
+  BIO_puts(out, "\n");
+  if ((aout < 4) || (bout != aout) || (memcmp(abuf, bbuf, aout) != 0)) {
+    fprintf(stderr, "Error in DH routines\n");
+    ret = 1;
+  } else
+    ret = 0;
+
+  if (!run_rfc5114_tests())
+    ret = 1;
+
+err:
+  BIO_print_errors_fp(stderr);
+
+  if (abuf != NULL)
+    OPENSSL_free(abuf);
+  if (bbuf != NULL)
+    OPENSSL_free(bbuf);
+  if (b != NULL)
+    DH_free(b);
+  if (a != NULL)
+    DH_free(a);
+  BIO_free(out);
+  return ret;
+}
+
+/* Test data from RFC 5114 */
+
+static const unsigned char dhtest_1024_160_xA[] = {
+    0xB9, 0xA3, 0xB3, 0xAE, 0x8F, 0xEF, 0xC1, 0xA2, 0x93, 0x04,
+    0x96, 0x50, 0x70, 0x86, 0xF8, 0x45, 0x5D, 0x48, 0x94, 0x3E};
+static const unsigned char dhtest_1024_160_yA[] = {
+    0x2A, 0x85, 0x3B, 0x3D, 0x92, 0x19, 0x75, 0x01, 0xB9, 0x01, 0x5B, 0x2D,
+    0xEB, 0x3E, 0xD8, 0x4F, 0x5E, 0x02, 0x1D, 0xCC, 0x3E, 0x52, 0xF1, 0x09,
+    0xD3, 0x27, 0x3D, 0x2B, 0x75, 0x21, 0x28, 0x1C, 0xBA, 0xBE, 0x0E, 0x76,
+    0xFF, 0x57, 0x27, 0xFA, 0x8A, 0xCC, 0xE2, 0x69, 0x56, 0xBA, 0x9A, 0x1F,
+    0xCA, 0x26, 0xF2, 0x02, 0x28, 0xD8, 0x69, 0x3F, 0xEB, 0x10, 0x84, 0x1D,
+    0x84, 0xA7, 0x36, 0x00, 0x54, 0xEC, 0xE5, 0xA7, 0xF5, 0xB7, 0xA6, 0x1A,
+    0xD3, 0xDF, 0xB3, 0xC6, 0x0D, 0x2E, 0x43, 0x10, 0x6D, 0x87, 0x27, 0xDA,
+    0x37, 0xDF, 0x9C, 0xCE, 0x95, 0xB4, 0x78, 0x75, 0x5D, 0x06, 0xBC, 0xEA,
+    0x8F, 0x9D, 0x45, 0x96, 0x5F, 0x75, 0xA5, 0xF3, 0xD1, 0xDF, 0x37, 0x01,
+    0x16, 0x5F, 0xC9, 0xE5, 0x0C, 0x42, 0x79, 0xCE, 0xB0, 0x7F, 0x98, 0x95,
+    0x40, 0xAE, 0x96, 0xD5, 0xD8, 0x8E, 0xD7, 0x76};
+static const unsigned char dhtest_1024_160_xB[] = {
+    0x93, 0x92, 0xC9, 0xF9, 0xEB, 0x6A, 0x7A, 0x6A, 0x90, 0x22,
+    0xF7, 0xD8, 0x3E, 0x72, 0x23, 0xC6, 0x83, 0x5B, 0xBD, 0xDA};
+static const unsigned char dhtest_1024_160_yB[] = {
+    0x71, 0x7A, 0x6C, 0xB0, 0x53, 0x37, 0x1F, 0xF4, 0xA3, 0xB9, 0x32, 0x94,
+    0x1C, 0x1E, 0x56, 0x63, 0xF8, 0x61, 0xA1, 0xD6, 0xAD, 0x34, 0xAE, 0x66,
+    0x57, 0x6D, 0xFB, 0x98, 0xF6, 0xC6, 0xCB, 0xF9, 0xDD, 0xD5, 0xA5, 0x6C,
+    0x78, 0x33, 0xF6, 0xBC, 0xFD, 0xFF, 0x09, 0x55, 0x82, 0xAD, 0x86, 0x8E,
+    0x44, 0x0E, 0x8D, 0x09, 0xFD, 0x76, 0x9E, 0x3C, 0xEC, 0xCD, 0xC3, 0xD3,
+    0xB1, 0xE4, 0xCF, 0xA0, 0x57, 0x77, 0x6C, 0xAA, 0xF9, 0x73, 0x9B, 0x6A,
+    0x9F, 0xEE, 0x8E, 0x74, 0x11, 0xF8, 0xD6, 0xDA, 0xC0, 0x9D, 0x6A, 0x4E,
+    0xDB, 0x46, 0xCC, 0x2B, 0x5D, 0x52, 0x03, 0x09, 0x0E, 0xAE, 0x61, 0x26,
+    0x31, 0x1E, 0x53, 0xFD, 0x2C, 0x14, 0xB5, 0x74, 0xE6, 0xA3, 0x10, 0x9A,
+    0x3D, 0xA1, 0xBE, 0x41, 0xBD, 0xCE, 0xAA, 0x18, 0x6F, 0x5C, 0xE0, 0x67,
+    0x16, 0xA2, 0xB6, 0xA0, 0x7B, 0x3C, 0x33, 0xFE};
+static const unsigned char dhtest_1024_160_Z[] = {
+    0x5C, 0x80, 0x4F, 0x45, 0x4D, 0x30, 0xD9, 0xC4, 0xDF, 0x85, 0x27, 0x1F,
+    0x93, 0x52, 0x8C, 0x91, 0xDF, 0x6B, 0x48, 0xAB, 0x5F, 0x80, 0xB3, 0xB5,
+    0x9C, 0xAA, 0xC1, 0xB2, 0x8F, 0x8A, 0xCB, 0xA9, 0xCD, 0x3E, 0x39, 0xF3,
+    0xCB, 0x61, 0x45, 0x25, 0xD9, 0x52, 0x1D, 0x2E, 0x64, 0x4C, 0x53, 0xB8,
+    0x07, 0xB8, 0x10, 0xF3, 0x40, 0x06, 0x2F, 0x25, 0x7D, 0x7D, 0x6F, 0xBF,
+    0xE8, 0xD5, 0xE8, 0xF0, 0x72, 0xE9, 0xB6, 0xE9, 0xAF, 0xDA, 0x94, 0x13,
+    0xEA, 0xFB, 0x2E, 0x8B, 0x06, 0x99, 0xB1, 0xFB, 0x5A, 0x0C, 0xAC, 0xED,
+    0xDE, 0xAE, 0xAD, 0x7E, 0x9C, 0xFB, 0xB3, 0x6A, 0xE2, 0xB4, 0x20, 0x83,
+    0x5B, 0xD8, 0x3A, 0x19, 0xFB, 0x0B, 0x5E, 0x96, 0xBF, 0x8F, 0xA4, 0xD0,
+    0x9E, 0x34, 0x55, 0x25, 0x16, 0x7E, 0xCD, 0x91, 0x55, 0x41, 0x6F, 0x46,
+    0xF4, 0x08, 0xED, 0x31, 0xB6, 0x3C, 0x6E, 0x6D};
+static const unsigned char dhtest_2048_224_xA[] = {
+    0x22, 0xE6, 0x26, 0x01, 0xDB, 0xFF, 0xD0, 0x67, 0x08, 0xA6,
+    0x80, 0xF7, 0x47, 0xF3, 0x61, 0xF7, 0x6D, 0x8F, 0x4F, 0x72,
+    0x1A, 0x05, 0x48, 0xE4, 0x83, 0x29, 0x4B, 0x0C};
+static const unsigned char dhtest_2048_224_yA[] = {
+    0x1B, 0x3A, 0x63, 0x45, 0x1B, 0xD8, 0x86, 0xE6, 0x99, 0xE6, 0x7B, 0x49,
+    0x4E, 0x28, 0x8B, 0xD7, 0xF8, 0xE0, 0xD3, 0x70, 0xBA, 0xDD, 0xA7, 0xA0,
+    0xEF, 0xD2, 0xFD, 0xE7, 0xD8, 0xF6, 0x61, 0x45, 0xCC, 0x9F, 0x28, 0x04,
+    0x19, 0x97, 0x5E, 0xB8, 0x08, 0x87, 0x7C, 0x8A, 0x4C, 0x0C, 0x8E, 0x0B,
+    0xD4, 0x8D, 0x4A, 0x54, 0x01, 0xEB, 0x1E, 0x87, 0x76, 0xBF, 0xEE, 0xE1,
+    0x34, 0xC0, 0x38, 0x31, 0xAC, 0x27, 0x3C, 0xD9, 0xD6, 0x35, 0xAB, 0x0C,
+    0xE0, 0x06, 0xA4, 0x2A, 0x88, 0x7E, 0x3F, 0x52, 0xFB, 0x87, 0x66, 0xB6,
+    0x50, 0xF3, 0x80, 0x78, 0xBC, 0x8E, 0xE8, 0x58, 0x0C, 0xEF, 0xE2, 0x43,
+    0x96, 0x8C, 0xFC, 0x4F, 0x8D, 0xC3, 0xDB, 0x08, 0x45, 0x54, 0x17, 0x1D,
+    0x41, 0xBF, 0x2E, 0x86, 0x1B, 0x7B, 0xB4, 0xD6, 0x9D, 0xD0, 0xE0, 0x1E,
+    0xA3, 0x87, 0xCB, 0xAA, 0x5C, 0xA6, 0x72, 0xAF, 0xCB, 0xE8, 0xBD, 0xB9,
+    0xD6, 0x2D, 0x4C, 0xE1, 0x5F, 0x17, 0xDD, 0x36, 0xF9, 0x1E, 0xD1, 0xEE,
+    0xDD, 0x65, 0xCA, 0x4A, 0x06, 0x45, 0x5C, 0xB9, 0x4C, 0xD4, 0x0A, 0x52,
+    0xEC, 0x36, 0x0E, 0x84, 0xB3, 0xC9, 0x26, 0xE2, 0x2C, 0x43, 0x80, 0xA3,
+    0xBF, 0x30, 0x9D, 0x56, 0x84, 0x97, 0x68, 0xB7, 0xF5, 0x2C, 0xFD, 0xF6,
+    0x55, 0xFD, 0x05, 0x3A, 0x7E, 0xF7, 0x06, 0x97, 0x9E, 0x7E, 0x58, 0x06,
+    0xB1, 0x7D, 0xFA, 0xE5, 0x3A, 0xD2, 0xA5, 0xBC, 0x56, 0x8E, 0xBB, 0x52,
+    0x9A, 0x7A, 0x61, 0xD6, 0x8D, 0x25, 0x6F, 0x8F, 0xC9, 0x7C, 0x07, 0x4A,
+    0x86, 0x1D, 0x82, 0x7E, 0x2E, 0xBC, 0x8C, 0x61, 0x34, 0x55, 0x31, 0x15,
+    0xB7, 0x0E, 0x71, 0x03, 0x92, 0x0A, 0xA1, 0x6D, 0x85, 0xE5, 0x2B, 0xCB,
+    0xAB, 0x8D, 0x78, 0x6A, 0x68, 0x17, 0x8F, 0xA8, 0xFF, 0x7C, 0x2F, 0x5C,
+    0x71, 0x64, 0x8D, 0x6F};
+static const unsigned char dhtest_2048_224_xB[] = {
+    0x4F, 0xF3, 0xBC, 0x96, 0xC7, 0xFC, 0x6A, 0x6D, 0x71, 0xD3,
+    0xB3, 0x63, 0x80, 0x0A, 0x7C, 0xDF, 0xEF, 0x6F, 0xC4, 0x1B,
+    0x44, 0x17, 0xEA, 0x15, 0x35, 0x3B, 0x75, 0x90};
+static const unsigned char dhtest_2048_224_yB[] = {
+    0x4D, 0xCE, 0xE9, 0x92, 0xA9, 0x76, 0x2A, 0x13, 0xF2, 0xF8, 0x38, 0x44,
+    0xAD, 0x3D, 0x77, 0xEE, 0x0E, 0x31, 0xC9, 0x71, 0x8B, 0x3D, 0xB6, 0xC2,
+    0x03, 0x5D, 0x39, 0x61, 0x18, 0x2C, 0x3E, 0x0B, 0xA2, 0x47, 0xEC, 0x41,
+    0x82, 0xD7, 0x60, 0xCD, 0x48, 0xD9, 0x95, 0x99, 0x97, 0x06, 0x22, 0xA1,
+    0x88, 0x1B, 0xBA, 0x2D, 0xC8, 0x22, 0x93, 0x9C, 0x78, 0xC3, 0x91, 0x2C,
+    0x66, 0x61, 0xFA, 0x54, 0x38, 0xB2, 0x07, 0x66, 0x22, 0x2B, 0x75, 0xE2,
+    0x4C, 0x2E, 0x3A, 0xD0, 0xC7, 0x28, 0x72, 0x36, 0x12, 0x95, 0x25, 0xEE,
+    0x15, 0xB5, 0xDD, 0x79, 0x98, 0xAA, 0x04, 0xC4, 0xA9, 0x69, 0x6C, 0xAC,
+    0xD7, 0x17, 0x20, 0x83, 0xA9, 0x7A, 0x81, 0x66, 0x4E, 0xAD, 0x2C, 0x47,
+    0x9E, 0x44, 0x4E, 0x4C, 0x06, 0x54, 0xCC, 0x19, 0xE2, 0x8D, 0x77, 0x03,
+    0xCE, 0xE8, 0xDA, 0xCD, 0x61, 0x26, 0xF5, 0xD6, 0x65, 0xEC, 0x52, 0xC6,
+    0x72, 0x55, 0xDB, 0x92, 0x01, 0x4B, 0x03, 0x7E, 0xB6, 0x21, 0xA2, 0xAC,
+    0x8E, 0x36, 0x5D, 0xE0, 0x71, 0xFF, 0xC1, 0x40, 0x0A, 0xCF, 0x07, 0x7A,
+    0x12, 0x91, 0x3D, 0xD8, 0xDE, 0x89, 0x47, 0x34, 0x37, 0xAB, 0x7B, 0xA3,
+    0x46, 0x74, 0x3C, 0x1B, 0x21, 0x5D, 0xD9, 0xC1, 0x21, 0x64, 0xA7, 0xE4,
+    0x05, 0x31, 0x18, 0xD1, 0x99, 0xBE, 0xC8, 0xEF, 0x6F, 0xC5, 0x61, 0x17,
+    0x0C, 0x84, 0xC8, 0x7D, 0x10, 0xEE, 0x9A, 0x67, 0x4A, 0x1F, 0xA8, 0xFF,
+    0xE1, 0x3B, 0xDF, 0xBA, 0x1D, 0x44, 0xDE, 0x48, 0x94, 0x6D, 0x68, 0xDC,
+    0x0C, 0xDD, 0x77, 0x76, 0x35, 0xA7, 0xAB, 0x5B, 0xFB, 0x1E, 0x4B, 0xB7,
+    0xB8, 0x56, 0xF9, 0x68, 0x27, 0x73, 0x4C, 0x18, 0x41, 0x38, 0xE9, 0x15,
+    0xD9, 0xC3, 0x00, 0x2E, 0xBC, 0xE5, 0x31, 0x20, 0x54, 0x6A, 0x7E, 0x20,
+    0x02, 0x14, 0x2B, 0x6C};
+static const unsigned char dhtest_2048_224_Z[] = {
+    0x34, 0xD9, 0xBD, 0xDC, 0x1B, 0x42, 0x17, 0x6C, 0x31, 0x3F, 0xEA, 0x03,
+    0x4C, 0x21, 0x03, 0x4D, 0x07, 0x4A, 0x63, 0x13, 0xBB, 0x4E, 0xCD, 0xB3,
+    0x70, 0x3F, 0xFF, 0x42, 0x45, 0x67, 0xA4, 0x6B, 0xDF, 0x75, 0x53, 0x0E,
+    0xDE, 0x0A, 0x9D, 0xA5, 0x22, 0x9D, 0xE7, 0xD7, 0x67, 0x32, 0x28, 0x6C,
+    0xBC, 0x0F, 0x91, 0xDA, 0x4C, 0x3C, 0x85, 0x2F, 0xC0, 0x99, 0xC6, 0x79,
+    0x53, 0x1D, 0x94, 0xC7, 0x8A, 0xB0, 0x3D, 0x9D, 0xEC, 0xB0, 0xA4, 0xE4,
+    0xCA, 0x8B, 0x2B, 0xB4, 0x59, 0x1C, 0x40, 0x21, 0xCF, 0x8C, 0xE3, 0xA2,
+    0x0A, 0x54, 0x1D, 0x33, 0x99, 0x40, 0x17, 0xD0, 0x20, 0x0A, 0xE2, 0xC9,
+    0x51, 0x6E, 0x2F, 0xF5, 0x14, 0x57, 0x79, 0x26, 0x9E, 0x86, 0x2B, 0x0F,
+    0xB4, 0x74, 0xA2, 0xD5, 0x6D, 0xC3, 0x1E, 0xD5, 0x69, 0xA7, 0x70, 0x0B,
+    0x4C, 0x4A, 0xB1, 0x6B, 0x22, 0xA4, 0x55, 0x13, 0x53, 0x1E, 0xF5, 0x23,
+    0xD7, 0x12, 0x12, 0x07, 0x7B, 0x5A, 0x16, 0x9B, 0xDE, 0xFF, 0xAD, 0x7A,
+    0xD9, 0x60, 0x82, 0x84, 0xC7, 0x79, 0x5B, 0x6D, 0x5A, 0x51, 0x83, 0xB8,
+    0x70, 0x66, 0xDE, 0x17, 0xD8, 0xD6, 0x71, 0xC9, 0xEB, 0xD8, 0xEC, 0x89,
+    0x54, 0x4D, 0x45, 0xEC, 0x06, 0x15, 0x93, 0xD4, 0x42, 0xC6, 0x2A, 0xB9,
+    0xCE, 0x3B, 0x1C, 0xB9, 0x94, 0x3A, 0x1D, 0x23, 0xA5, 0xEA, 0x3B, 0xCF,
+    0x21, 0xA0, 0x14, 0x71, 0xE6, 0x7E, 0x00, 0x3E, 0x7F, 0x8A, 0x69, 0xC7,
+    0x28, 0xBE, 0x49, 0x0B, 0x2F, 0xC8, 0x8C, 0xFE, 0xB9, 0x2D, 0xB6, 0xA2,
+    0x15, 0xE5, 0xD0, 0x3C, 0x17, 0xC4, 0x64, 0xC9, 0xAC, 0x1A, 0x46, 0xE2,
+    0x03, 0xE1, 0x3F, 0x95, 0x29, 0x95, 0xFB, 0x03, 0xC6, 0x9D, 0x3C, 0xC4,
+    0x7F, 0xCB, 0x51, 0x0B, 0x69, 0x98, 0xFF, 0xD3, 0xAA, 0x6D, 0xE7, 0x3C,
+    0xF9, 0xF6, 0x38, 0x69};
+static const unsigned char dhtest_2048_256_xA[] = {
+    0x08, 0x81, 0x38, 0x2C, 0xDB, 0x87, 0x66, 0x0C, 0x6D, 0xC1, 0x3E,
+    0x61, 0x49, 0x38, 0xD5, 0xB9, 0xC8, 0xB2, 0xF2, 0x48, 0x58, 0x1C,
+    0xC5, 0xE3, 0x1B, 0x35, 0x45, 0x43, 0x97, 0xFC, 0xE5, 0x0E};
+static const unsigned char dhtest_2048_256_yA[] = {
+    0x2E, 0x93, 0x80, 0xC8, 0x32, 0x3A, 0xF9, 0x75, 0x45, 0xBC, 0x49, 0x41,
+    0xDE, 0xB0, 0xEC, 0x37, 0x42, 0xC6, 0x2F, 0xE0, 0xEC, 0xE8, 0x24, 0xA6,
+    0xAB, 0xDB, 0xE6, 0x6C, 0x59, 0xBE, 0xE0, 0x24, 0x29, 0x11, 0xBF, 0xB9,
+    0x67, 0x23, 0x5C, 0xEB, 0xA3, 0x5A, 0xE1, 0x3E, 0x4E, 0xC7, 0x52, 0xBE,
+    0x63, 0x0B, 0x92, 0xDC, 0x4B, 0xDE, 0x28, 0x47, 0xA9, 0xC6, 0x2C, 0xB8,
+    0x15, 0x27, 0x45, 0x42, 0x1F, 0xB7, 0xEB, 0x60, 0xA6, 0x3C, 0x0F, 0xE9,
+    0x15, 0x9F, 0xCC, 0xE7, 0x26, 0xCE, 0x7C, 0xD8, 0x52, 0x3D, 0x74, 0x50,
+    0x66, 0x7E, 0xF8, 0x40, 0xE4, 0x91, 0x91, 0x21, 0xEB, 0x5F, 0x01, 0xC8,
+    0xC9, 0xB0, 0xD3, 0xD6, 0x48, 0xA9, 0x3B, 0xFB, 0x75, 0x68, 0x9E, 0x82,
+    0x44, 0xAC, 0x13, 0x4A, 0xF5, 0x44, 0x71, 0x1C, 0xE7, 0x9A, 0x02, 0xDC,
+    0xC3, 0x42, 0x26, 0x68, 0x47, 0x80, 0xDD, 0xDC, 0xB4, 0x98, 0x59, 0x41,
+    0x06, 0xC3, 0x7F, 0x5B, 0xC7, 0x98, 0x56, 0x48, 0x7A, 0xF5, 0xAB, 0x02,
+    0x2A, 0x2E, 0x5E, 0x42, 0xF0, 0x98, 0x97, 0xC1, 0xA8, 0x5A, 0x11, 0xEA,
+    0x02, 0x12, 0xAF, 0x04, 0xD9, 0xB4, 0xCE, 0xBC, 0x93, 0x7C, 0x3C, 0x1A,
+    0x3E, 0x15, 0xA8, 0xA0, 0x34, 0x2E, 0x33, 0x76, 0x15, 0xC8, 0x4E, 0x7F,
+    0xE3, 0xB8, 0xB9, 0xB8, 0x7F, 0xB1, 0xE7, 0x3A, 0x15, 0xAF, 0x12, 0xA3,
+    0x0D, 0x74, 0x6E, 0x06, 0xDF, 0xC3, 0x4F, 0x29, 0x0D, 0x79, 0x7C, 0xE5,
+    0x1A, 0xA1, 0x3A, 0xA7, 0x85, 0xBF, 0x66, 0x58, 0xAF, 0xF5, 0xE4, 0xB0,
+    0x93, 0x00, 0x3C, 0xBE, 0xAF, 0x66, 0x5B, 0x3C, 0x2E, 0x11, 0x3A, 0x3A,
+    0x4E, 0x90, 0x52, 0x69, 0x34, 0x1D, 0xC0, 0x71, 0x14, 0x26, 0x68, 0x5F,
+    0x4E, 0xF3, 0x7E, 0x86, 0x8A, 0x81, 0x26, 0xFF, 0x3F, 0x22, 0x79, 0xB5,
+    0x7C, 0xA6, 0x7E, 0x29};
+static const unsigned char dhtest_2048_256_xB[] = {
+    0x7D, 0x62, 0xA7, 0xE3, 0xEF, 0x36, 0xDE, 0x61, 0x7B, 0x13, 0xD1,
+    0xAF, 0xB8, 0x2C, 0x78, 0x0D, 0x83, 0xA2, 0x3B, 0xD4, 0xEE, 0x67,
+    0x05, 0x64, 0x51, 0x21, 0xF3, 0x71, 0xF5, 0x46, 0xA5, 0x3D};
+static const unsigned char dhtest_2048_256_yB[] = {
+    0x57, 0x5F, 0x03, 0x51, 0xBD, 0x2B, 0x1B, 0x81, 0x74, 0x48, 0xBD, 0xF8,
+    0x7A, 0x6C, 0x36, 0x2C, 0x1E, 0x28, 0x9D, 0x39, 0x03, 0xA3, 0x0B, 0x98,
+    0x32, 0xC5, 0x74, 0x1F, 0xA2, 0x50, 0x36, 0x3E, 0x7A, 0xCB, 0xC7, 0xF7,
+    0x7F, 0x3D, 0xAC, 0xBC, 0x1F, 0x13, 0x1A, 0xDD, 0x8E, 0x03, 0x36, 0x7E,
+    0xFF, 0x8F, 0xBB, 0xB3, 0xE1, 0xC5, 0x78, 0x44, 0x24, 0x80, 0x9B, 0x25,
+    0xAF, 0xE4, 0xD2, 0x26, 0x2A, 0x1A, 0x6F, 0xD2, 0xFA, 0xB6, 0x41, 0x05,
+    0xCA, 0x30, 0xA6, 0x74, 0xE0, 0x7F, 0x78, 0x09, 0x85, 0x20, 0x88, 0x63,
+    0x2F, 0xC0, 0x49, 0x23, 0x37, 0x91, 0xAD, 0x4E, 0xDD, 0x08, 0x3A, 0x97,
+    0x8B, 0x88, 0x3E, 0xE6, 0x18, 0xBC, 0x5E, 0x0D, 0xD0, 0x47, 0x41, 0x5F,
+    0x2D, 0x95, 0xE6, 0x83, 0xCF, 0x14, 0x82, 0x6B, 0x5F, 0xBE, 0x10, 0xD3,
+    0xCE, 0x41, 0xC6, 0xC1, 0x20, 0xC7, 0x8A, 0xB2, 0x00, 0x08, 0xC6, 0x98,
+    0xBF, 0x7F, 0x0B, 0xCA, 0xB9, 0xD7, 0xF4, 0x07, 0xBE, 0xD0, 0xF4, 0x3A,
+    0xFB, 0x29, 0x70, 0xF5, 0x7F, 0x8D, 0x12, 0x04, 0x39, 0x63, 0xE6, 0x6D,
+    0xDD, 0x32, 0x0D, 0x59, 0x9A, 0xD9, 0x93, 0x6C, 0x8F, 0x44, 0x13, 0x7C,
+    0x08, 0xB1, 0x80, 0xEC, 0x5E, 0x98, 0x5C, 0xEB, 0xE1, 0x86, 0xF3, 0xD5,
+    0x49, 0x67, 0x7E, 0x80, 0x60, 0x73, 0x31, 0xEE, 0x17, 0xAF, 0x33, 0x80,
+    0xA7, 0x25, 0xB0, 0x78, 0x23, 0x17, 0xD7, 0xDD, 0x43, 0xF5, 0x9D, 0x7A,
+    0xF9, 0x56, 0x8A, 0x9B, 0xB6, 0x3A, 0x84, 0xD3, 0x65, 0xF9, 0x22, 0x44,
+    0xED, 0x12, 0x09, 0x88, 0x21, 0x93, 0x02, 0xF4, 0x29, 0x24, 0xC7, 0xCA,
+    0x90, 0xB8, 0x9D, 0x24, 0xF7, 0x1B, 0x0A, 0xB6, 0x97, 0x82, 0x3D, 0x7D,
+    0xEB, 0x1A, 0xFF, 0x5B, 0x0E, 0x8E, 0x4A, 0x45, 0xD4, 0x9F, 0x7F, 0x53,
+    0x75, 0x7E, 0x19, 0x13};
+static const unsigned char dhtest_2048_256_Z[] = {
+    0x86, 0xC7, 0x0B, 0xF8, 0xD0, 0xBB, 0x81, 0xBB, 0x01, 0x07, 0x8A, 0x17,
+    0x21, 0x9C, 0xB7, 0xD2, 0x72, 0x03, 0xDB, 0x2A, 0x19, 0xC8, 0x77, 0xF1,
+    0xD1, 0xF1, 0x9F, 0xD7, 0xD7, 0x7E, 0xF2, 0x25, 0x46, 0xA6, 0x8F, 0x00,
+    0x5A, 0xD5, 0x2D, 0xC8, 0x45, 0x53, 0xB7, 0x8F, 0xC6, 0x03, 0x30, 0xBE,
+    0x51, 0xEA, 0x7C, 0x06, 0x72, 0xCA, 0xC1, 0x51, 0x5E, 0x4B, 0x35, 0xC0,
+    0x47, 0xB9, 0xA5, 0x51, 0xB8, 0x8F, 0x39, 0xDC, 0x26, 0xDA, 0x14, 0xA0,
+    0x9E, 0xF7, 0x47, 0x74, 0xD4, 0x7C, 0x76, 0x2D, 0xD1, 0x77, 0xF9, 0xED,
+    0x5B, 0xC2, 0xF1, 0x1E, 0x52, 0xC8, 0x79, 0xBD, 0x95, 0x09, 0x85, 0x04,
+    0xCD, 0x9E, 0xEC, 0xD8, 0xA8, 0xF9, 0xB3, 0xEF, 0xBD, 0x1F, 0x00, 0x8A,
+    0xC5, 0x85, 0x30, 0x97, 0xD9, 0xD1, 0x83, 0x7F, 0x2B, 0x18, 0xF7, 0x7C,
+    0xD7, 0xBE, 0x01, 0xAF, 0x80, 0xA7, 0xC7, 0xB5, 0xEA, 0x3C, 0xA5, 0x4C,
+    0xC0, 0x2D, 0x0C, 0x11, 0x6F, 0xEE, 0x3F, 0x95, 0xBB, 0x87, 0x39, 0x93,
+    0x85, 0x87, 0x5D, 0x7E, 0x86, 0x74, 0x7E, 0x67, 0x6E, 0x72, 0x89, 0x38,
+    0xAC, 0xBF, 0xF7, 0x09, 0x8E, 0x05, 0xBE, 0x4D, 0xCF, 0xB2, 0x40, 0x52,
+    0xB8, 0x3A, 0xEF, 0xFB, 0x14, 0x78, 0x3F, 0x02, 0x9A, 0xDB, 0xDE, 0x7F,
+    0x53, 0xFA, 0xE9, 0x20, 0x84, 0x22, 0x40, 0x90, 0xE0, 0x07, 0xCE, 0xE9,
+    0x4D, 0x4B, 0xF2, 0xBA, 0xCE, 0x9F, 0xFD, 0x4B, 0x57, 0xD2, 0xAF, 0x7C,
+    0x72, 0x4D, 0x0C, 0xAA, 0x19, 0xBF, 0x05, 0x01, 0xF6, 0xF1, 0x7B, 0x4A,
+    0xA1, 0x0F, 0x42, 0x5E, 0x3E, 0xA7, 0x60, 0x80, 0xB4, 0xB9, 0xD6, 0xB3,
+    0xCE, 0xFE, 0xA1, 0x15, 0xB2, 0xCE, 0xB8, 0x78, 0x9B, 0xB8, 0xA3, 0xB0,
+    0xEA, 0x87, 0xFE, 0xBE, 0x63, 0xB6, 0xC8, 0xF8, 0x46, 0xEC, 0x6D, 0xB0,
+    0xC2, 0x6C, 0x5D, 0x7C};
+
+typedef struct {
+  DH *(*get_param)(const ENGINE *engine);
+  const unsigned char *xA;
+  size_t xA_len;
+  const unsigned char *yA;
+  size_t yA_len;
+  const unsigned char *xB;
+  size_t xB_len;
+  const unsigned char *yB;
+  size_t yB_len;
+  const unsigned char *Z;
+  size_t Z_len;
+} rfc5114_td;
+
+#define make_rfc5114_td(pre)                                                  \
+  {                                                                           \
+    DH_get_##pre, dhtest_##pre##_xA, sizeof(dhtest_##pre##_xA),               \
+        dhtest_##pre##_yA, sizeof(dhtest_##pre##_yA), dhtest_##pre##_xB,      \
+        sizeof(dhtest_##pre##_xB), dhtest_##pre##_yB,                         \
+        sizeof(dhtest_##pre##_yB), dhtest_##pre##_Z, sizeof(dhtest_##pre##_Z) \
+  }
+
+static const rfc5114_td rfctd[] = {make_rfc5114_td(1024_160),
+                                   make_rfc5114_td(2048_224),
+                                   make_rfc5114_td(2048_256)};
+
+static int run_rfc5114_tests(void) {
+  int i;
+  DH *dhA = NULL, *dhB = NULL;
+  unsigned char *Z1 = NULL, *Z2 = NULL;
+
+  for (i = 0; i < (int)(sizeof(rfctd) / sizeof(rfc5114_td)); i++) {
+    const rfc5114_td *td = rfctd + i;
+    /* Set up DH structures setting key components */
+    dhA = td->get_param(NULL);
+    dhB = td->get_param(NULL);
+    if (!dhA || !dhB)
+      goto bad_err;
+
+    dhA->priv_key = BN_bin2bn(td->xA, td->xA_len, NULL);
+    dhA->pub_key = BN_bin2bn(td->yA, td->yA_len, NULL);
+
+    dhB->priv_key = BN_bin2bn(td->xB, td->xB_len, NULL);
+    dhB->pub_key = BN_bin2bn(td->yB, td->yB_len, NULL);
+
+    if (!dhA->priv_key || !dhA->pub_key || !dhB->priv_key || !dhB->pub_key)
+      goto bad_err;
+
+    if ((td->Z_len != (size_t)DH_size(dhA)) ||
+        (td->Z_len != (size_t)DH_size(dhB)))
+      goto err;
+
+    Z1 = OPENSSL_malloc(DH_size(dhA));
+    Z2 = OPENSSL_malloc(DH_size(dhB));
+    /* Work out shared secrets using both sides and compare
+     * with expected values.
+     */
+    if (!DH_compute_key(Z1, dhB->pub_key, dhA))
+      goto bad_err;
+    if (!DH_compute_key(Z2, dhA->pub_key, dhB))
+      goto bad_err;
+
+    if (memcmp(Z1, td->Z, td->Z_len))
+      goto err;
+    if (memcmp(Z2, td->Z, td->Z_len))
+      goto err;
+
+    printf("RFC5114 parameter test %d OK\n", i + 1);
+
+    DH_free(dhA);
+    dhA = NULL;
+    DH_free(dhB);
+    dhB = NULL;
+    OPENSSL_free(Z1);
+    Z1 = NULL;
+    OPENSSL_free(Z2);
+    Z2 = NULL;
+  }
+
+  printf("PASS\n");
+  return 1;
+
+bad_err:
+  fprintf(stderr, "Initalisation error RFC5114 set %d\n", i + 1);
+  BIO_print_errors_fp(stderr);
+
+err:
+  if (Z1 != NULL) {
+    OPENSSL_free(Z1);
+  }
+  if (Z2 != NULL) {
+    OPENSSL_free(Z2);
+  }
+  if (dhA != NULL) {
+    DH_free(dhA);
+  }
+  if (dhB != NULL) {
+    DH_free(dhB);
+  }
+
+  fprintf(stderr, "Test failed RFC5114 set %d\n", i + 1);
+  return 0;
+}
diff --git a/crypto/dh/internal.h b/crypto/dh/internal.h
new file mode 100644
index 0000000..e4c725c
--- /dev/null
+++ b/crypto/dh/internal.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_DH_INTERNAL_H
+#define OPENSSL_HEADER_DH_INTERNAL_H
+
+#include <openssl/base.h>
+
+#include <openssl/bn.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_DH_INTERNAL_H */
diff --git a/crypto/dh/params.c b/crypto/dh/params.c
new file mode 100644
index 0000000..4b30c04
--- /dev/null
+++ b/crypto/dh/params.c
@@ -0,0 +1,271 @@
+/* ====================================================================
+ * Copyright (c) 2011 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/dh.h>
+
+#include <openssl/bn.h>
+
+#include "internal.h"
+
+
+#if BN_BITS2 == 64
+static const BN_ULONG dh1024_160_p[] = {
+    0xDF1FB2BC2E4A4371ULL, 0xE68CFDA76D4DA708ULL, 0x45BF37DF365C1A65ULL,
+    0xA151AF5F0DC8B4BDULL, 0xFAA31A4FF55BCCC0ULL, 0x4EFFD6FAE5644738ULL,
+    0x98488E9C219A7372ULL, 0xACCBDD7D90C4BD70ULL, 0x24975C3CD49B83BFULL,
+    0x13ECB4AEA9061123ULL, 0x9838EF1E2EE652C0ULL, 0x6073E28675A23D18ULL,
+    0x9A6A9DCA52D23B61ULL, 0x52C99FBCFB06A3C6ULL, 0xDE92DE5EAE5D54ECULL,
+    0xB10B8F96A080E01DULL};
+static const BN_ULONG dh1024_160_g[] = {
+    0x855E6EEB22B3B2E5ULL, 0x858F4DCEF97C2A24ULL, 0x2D779D5918D08BC8ULL,
+    0xD662A4D18E73AFA3ULL, 0x1DBF0A0169B6A28AULL, 0xA6A24C087A091F53ULL,
+    0x909D0D2263F80A76ULL, 0xD7FBD7D3B9A92EE1ULL, 0x5E91547F9E2749F4ULL,
+    0x160217B4B01B886AULL, 0x777E690F5504F213ULL, 0x266FEA1E5C41564BULL,
+    0xD6406CFF14266D31ULL, 0xF8104DD258AC507FULL, 0x6765A442EFB99905ULL,
+    0xA4D1CBD5C3FD3412ULL};
+static const BN_ULONG dh1024_160_q[] = {
+    0x64B7CB9D49462353ULL, 0x81A8DF278ABA4E7DULL, 0x00000000F518AA87ULL};
+
+static const BN_ULONG dh2048_224_p[] = {
+    0x0AC4DFFE0C10E64FULL, 0xCF9DE5384E71B81CULL, 0x7EF363E2FFA31F71ULL,
+    0xE3FB73C16B8E75B9ULL, 0xC9B53DCF4BA80A29ULL, 0x23F10B0E16E79763ULL,
+    0xC52172E413042E9BULL, 0xBE60E69CC928B2B9ULL, 0x80CD86A1B9E587E8ULL,
+    0x315D75E198C641A4ULL, 0xCDF93ACC44328387ULL, 0x15987D9ADC0A486DULL,
+    0x7310F7121FD5A074ULL, 0x278273C7DE31EFDCULL, 0x1602E714415D9330ULL,
+    0x81286130BC8985DBULL, 0xB3BF8A3170918836ULL, 0x6A00E0A0B9C49708ULL,
+    0xC6BA0B2C8BBC27BEULL, 0xC9F98D11ED34DBF6ULL, 0x7AD5B7D0B6C12207ULL,
+    0xD91E8FEF55B7394BULL, 0x9037C9EDEFDA4DF8ULL, 0x6D3F8152AD6AC212ULL,
+    0x1DE6B85A1274A0A6ULL, 0xEB3D688A309C180EULL, 0xAF9A3C407BA1DF15ULL,
+    0xE6FA141DF95A56DBULL, 0xB54B1597B61D0A75ULL, 0xA20D64E5683B9FD1ULL,
+    0xD660FAA79559C51FULL, 0xAD107E1E9123A9D0ULL};
+static const BN_ULONG dh2048_224_g[] = {
+    0x84B890D3191F2BFAULL, 0x81BC087F2A7065B3ULL, 0x19C418E1F6EC0179ULL,
+    0x7B5A0F1C71CFFF4CULL, 0xEDFE72FE9B6AA4BDULL, 0x81E1BCFE94B30269ULL,
+    0x566AFBB48D6C0191ULL, 0xB539CCE3409D13CDULL, 0x6AA21E7F5F2FF381ULL,
+    0xD9E263E4770589EFULL, 0x10E183EDD19963DDULL, 0xB70A8137150B8EEBULL,
+    0x051AE3D428C8F8ACULL, 0xBB77A86F0C1AB15BULL, 0x6E3025E316A330EFULL,
+    0x19529A45D6F83456ULL, 0xF180EB34118E98D1ULL, 0xB5F6C6B250717CBEULL,
+    0x09939D54DA7460CDULL, 0xE247150422EA1ED4ULL, 0xB8A762D0521BC98AULL,
+    0xF4D027275AC1348BULL, 0xC17669101999024AULL, 0xBE5E9001A8D66AD7ULL,
+    0xC57DB17C620A8652ULL, 0xAB739D7700C29F52ULL, 0xDD921F01A70C4AFAULL,
+    0xA6824A4E10B9A6F0ULL, 0x74866A08CFE4FFE3ULL, 0x6CDEBE7B89998CAFULL,
+    0x9DF30B5C8FFDAC50ULL, 0xAC4032EF4F2D9AE3ULL};
+static const BN_ULONG dh2048_224_q[] = {
+    0xBF389A99B36371EBULL, 0x1F80535A4738CEBCULL, 0xC58D93FE99717710ULL,
+    0x00000000801C0D34ULL};
+
+static const BN_ULONG dh2048_256_p[] = {
+    0xDB094AE91E1A1597ULL, 0x693877FAD7EF09CAULL, 0x6116D2276E11715FULL,
+    0xA4B54330C198AF12ULL, 0x75F26375D7014103ULL, 0xC3A3960A54E710C3ULL,
+    0xDED4010ABD0BE621ULL, 0xC0B857F689962856ULL, 0xB3CA3F7971506026ULL,
+    0x1CCACB83E6B486F6ULL, 0x67E144E514056425ULL, 0xF6A167B5A41825D9ULL,
+    0x3AD8347796524D8EULL, 0xF13C6D9A51BFA4ABULL, 0x2D52526735488A0EULL,
+    0xB63ACAE1CAA6B790ULL, 0x4FDB70C581B23F76ULL, 0xBC39A0BF12307F5CULL,
+    0xB941F54EB1E59BB8ULL, 0x6C5BFC11D45F9088ULL, 0x22E0B1EF4275BF7BULL,
+    0x91F9E6725B4758C0ULL, 0x5A8A9D306BCF67EDULL, 0x209E0C6497517ABDULL,
+    0x3BF4296D830E9A7CULL, 0x16C3D91134096FAAULL, 0xFAF7DF4561B2AA30ULL,
+    0xE00DF8F1D61957D4ULL, 0x5D2CEED4435E3B00ULL, 0x8CEEF608660DD0F2ULL,
+    0xFFBBD19C65195999ULL, 0x87A8E61DB4B6663CULL};
+static const BN_ULONG dh2048_256_g[] = {
+    0x664B4C0F6CC41659ULL, 0x5E2327CFEF98C582ULL, 0xD647D148D4795451ULL,
+    0x2F63078490F00EF8ULL, 0x184B523D1DB246C3ULL, 0xC7891428CDC67EB6ULL,
+    0x7FD028370DF92B52ULL, 0xB3353BBB64E0EC37ULL, 0xECD06E1557CD0915ULL,
+    0xB7D2BBD2DF016199ULL, 0xC8484B1E052588B9ULL, 0xDB2A3B7313D3FE14ULL,
+    0xD052B985D182EA0AULL, 0xA4BD1BFFE83B9C80ULL, 0xDFC967C1FB3F2E55ULL,
+    0xB5045AF2767164E1ULL, 0x1D14348F6F2F9193ULL, 0x64E67982428EBC83ULL,
+    0x8AC376D282D6ED38ULL, 0x777DE62AAAB8A862ULL, 0xDDF463E5E9EC144BULL,
+    0x0196F931C77A57F2ULL, 0xA55AE31341000A65ULL, 0x901228F8C28CBB18ULL,
+    0xBC3773BF7E8C6F62ULL, 0xBE3A6C1B0C6B47B1ULL, 0xFF4FED4AAC0BB555ULL,
+    0x10DBC15077BE463FULL, 0x07F4793A1A0BA125ULL, 0x4CA7B18F21EF2054ULL,
+    0x2E77506660EDBD48ULL, 0x3FB32C9B73134D0BULL};
+static const BN_ULONG dh2048_256_q[] = {
+    0xA308B0FE64F5FBD3ULL, 0x99B1A47D1EB3750BULL, 0xB447997640129DA2ULL,
+    0x8CF83642A709A097ULL};
+
+#elif BN_BITS2 == 32
+
+static const BN_ULONG dh1024_160_p[] = {
+    0x2E4A4371, 0xDF1FB2BC, 0x6D4DA708, 0xE68CFDA7, 0x365C1A65, 0x45BF37DF,
+    0x0DC8B4BD, 0xA151AF5F, 0xF55BCCC0, 0xFAA31A4F, 0xE5644738, 0x4EFFD6FA,
+    0x219A7372, 0x98488E9C, 0x90C4BD70, 0xACCBDD7D, 0xD49B83BF, 0x24975C3C,
+    0xA9061123, 0x13ECB4AE, 0x2EE652C0, 0x9838EF1E, 0x75A23D18, 0x6073E286,
+    0x52D23B61, 0x9A6A9DCA, 0xFB06A3C6, 0x52C99FBC, 0xAE5D54EC, 0xDE92DE5E,
+    0xA080E01D, 0xB10B8F96};
+static const BN_ULONG dh1024_160_g[] = {
+    0x22B3B2E5, 0x855E6EEB, 0xF97C2A24, 0x858F4DCE, 0x18D08BC8, 0x2D779D59,
+    0x8E73AFA3, 0xD662A4D1, 0x69B6A28A, 0x1DBF0A01, 0x7A091F53, 0xA6A24C08,
+    0x63F80A76, 0x909D0D22, 0xB9A92EE1, 0xD7FBD7D3, 0x9E2749F4, 0x5E91547F,
+    0xB01B886A, 0x160217B4, 0x5504F213, 0x777E690F, 0x5C41564B, 0x266FEA1E,
+    0x14266D31, 0xD6406CFF, 0x58AC507F, 0xF8104DD2, 0xEFB99905, 0x6765A442,
+    0xC3FD3412, 0xA4D1CBD5};
+static const BN_ULONG dh1024_160_q[] = {0x49462353, 0x64B7CB9D, 0x8ABA4E7D,
+                                        0x81A8DF27, 0xF518AA87};
+
+static const BN_ULONG dh2048_224_p[] = {
+    0x0C10E64F, 0x0AC4DFFE, 0x4E71B81C, 0xCF9DE538, 0xFFA31F71, 0x7EF363E2,
+    0x6B8E75B9, 0xE3FB73C1, 0x4BA80A29, 0xC9B53DCF, 0x16E79763, 0x23F10B0E,
+    0x13042E9B, 0xC52172E4, 0xC928B2B9, 0xBE60E69C, 0xB9E587E8, 0x80CD86A1,
+    0x98C641A4, 0x315D75E1, 0x44328387, 0xCDF93ACC, 0xDC0A486D, 0x15987D9A,
+    0x1FD5A074, 0x7310F712, 0xDE31EFDC, 0x278273C7, 0x415D9330, 0x1602E714,
+    0xBC8985DB, 0x81286130, 0x70918836, 0xB3BF8A31, 0xB9C49708, 0x6A00E0A0,
+    0x8BBC27BE, 0xC6BA0B2C, 0xED34DBF6, 0xC9F98D11, 0xB6C12207, 0x7AD5B7D0,
+    0x55B7394B, 0xD91E8FEF, 0xEFDA4DF8, 0x9037C9ED, 0xAD6AC212, 0x6D3F8152,
+    0x1274A0A6, 0x1DE6B85A, 0x309C180E, 0xEB3D688A, 0x7BA1DF15, 0xAF9A3C40,
+    0xF95A56DB, 0xE6FA141D, 0xB61D0A75, 0xB54B1597, 0x683B9FD1, 0xA20D64E5,
+    0x9559C51F, 0xD660FAA7, 0x9123A9D0, 0xAD107E1E};
+static const BN_ULONG dh2048_224_g[] = {
+    0x191F2BFA, 0x84B890D3, 0x2A7065B3, 0x81BC087F, 0xF6EC0179, 0x19C418E1,
+    0x71CFFF4C, 0x7B5A0F1C, 0x9B6AA4BD, 0xEDFE72FE, 0x94B30269, 0x81E1BCFE,
+    0x8D6C0191, 0x566AFBB4, 0x409D13CD, 0xB539CCE3, 0x5F2FF381, 0x6AA21E7F,
+    0x770589EF, 0xD9E263E4, 0xD19963DD, 0x10E183ED, 0x150B8EEB, 0xB70A8137,
+    0x28C8F8AC, 0x051AE3D4, 0x0C1AB15B, 0xBB77A86F, 0x16A330EF, 0x6E3025E3,
+    0xD6F83456, 0x19529A45, 0x118E98D1, 0xF180EB34, 0x50717CBE, 0xB5F6C6B2,
+    0xDA7460CD, 0x09939D54, 0x22EA1ED4, 0xE2471504, 0x521BC98A, 0xB8A762D0,
+    0x5AC1348B, 0xF4D02727, 0x1999024A, 0xC1766910, 0xA8D66AD7, 0xBE5E9001,
+    0x620A8652, 0xC57DB17C, 0x00C29F52, 0xAB739D77, 0xA70C4AFA, 0xDD921F01,
+    0x10B9A6F0, 0xA6824A4E, 0xCFE4FFE3, 0x74866A08, 0x89998CAF, 0x6CDEBE7B,
+    0x8FFDAC50, 0x9DF30B5C, 0x4F2D9AE3, 0xAC4032EF};
+static const BN_ULONG dh2048_224_q[] = {0xB36371EB, 0xBF389A99, 0x4738CEBC,
+                                        0x1F80535A, 0x99717710, 0xC58D93FE,
+                                        0x801C0D34};
+
+static const BN_ULONG dh2048_256_p[] = {
+    0x1E1A1597, 0xDB094AE9, 0xD7EF09CA, 0x693877FA, 0x6E11715F, 0x6116D227,
+    0xC198AF12, 0xA4B54330, 0xD7014103, 0x75F26375, 0x54E710C3, 0xC3A3960A,
+    0xBD0BE621, 0xDED4010A, 0x89962856, 0xC0B857F6, 0x71506026, 0xB3CA3F79,
+    0xE6B486F6, 0x1CCACB83, 0x14056425, 0x67E144E5, 0xA41825D9, 0xF6A167B5,
+    0x96524D8E, 0x3AD83477, 0x51BFA4AB, 0xF13C6D9A, 0x35488A0E, 0x2D525267,
+    0xCAA6B790, 0xB63ACAE1, 0x81B23F76, 0x4FDB70C5, 0x12307F5C, 0xBC39A0BF,
+    0xB1E59BB8, 0xB941F54E, 0xD45F9088, 0x6C5BFC11, 0x4275BF7B, 0x22E0B1EF,
+    0x5B4758C0, 0x91F9E672, 0x6BCF67ED, 0x5A8A9D30, 0x97517ABD, 0x209E0C64,
+    0x830E9A7C, 0x3BF4296D, 0x34096FAA, 0x16C3D911, 0x61B2AA30, 0xFAF7DF45,
+    0xD61957D4, 0xE00DF8F1, 0x435E3B00, 0x5D2CEED4, 0x660DD0F2, 0x8CEEF608,
+    0x65195999, 0xFFBBD19C, 0xB4B6663C, 0x87A8E61D};
+static const BN_ULONG dh2048_256_g[] = {
+    0x6CC41659, 0x664B4C0F, 0xEF98C582, 0x5E2327CF, 0xD4795451, 0xD647D148,
+    0x90F00EF8, 0x2F630784, 0x1DB246C3, 0x184B523D, 0xCDC67EB6, 0xC7891428,
+    0x0DF92B52, 0x7FD02837, 0x64E0EC37, 0xB3353BBB, 0x57CD0915, 0xECD06E15,
+    0xDF016199, 0xB7D2BBD2, 0x052588B9, 0xC8484B1E, 0x13D3FE14, 0xDB2A3B73,
+    0xD182EA0A, 0xD052B985, 0xE83B9C80, 0xA4BD1BFF, 0xFB3F2E55, 0xDFC967C1,
+    0x767164E1, 0xB5045AF2, 0x6F2F9193, 0x1D14348F, 0x428EBC83, 0x64E67982,
+    0x82D6ED38, 0x8AC376D2, 0xAAB8A862, 0x777DE62A, 0xE9EC144B, 0xDDF463E5,
+    0xC77A57F2, 0x0196F931, 0x41000A65, 0xA55AE313, 0xC28CBB18, 0x901228F8,
+    0x7E8C6F62, 0xBC3773BF, 0x0C6B47B1, 0xBE3A6C1B, 0xAC0BB555, 0xFF4FED4A,
+    0x77BE463F, 0x10DBC150, 0x1A0BA125, 0x07F4793A, 0x21EF2054, 0x4CA7B18F,
+    0x60EDBD48, 0x2E775066, 0x73134D0B, 0x3FB32C9B};
+static const BN_ULONG dh2048_256_q[] = {0x64F5FBD3, 0xA308B0FE, 0x1EB3750B,
+                                        0x99B1A47D, 0x40129DA2, 0xB4479976,
+                                        0xA709A097, 0x8CF83642};
+
+#else
+#error "unsupported BN_BITS2"
+#endif
+
+#define STATIC_BIGNUM(x)                                     \
+  {                                                          \
+    (BN_ULONG *) x, sizeof(x) / sizeof(BN_ULONG),            \
+        sizeof(x) / sizeof(BN_ULONG), 0, BN_FLG_STATIC_DATA \
+  }
+
+struct standard_parameters {
+  BIGNUM p, q, g;
+};
+
+static const struct standard_parameters dh1024_160 = {
+  STATIC_BIGNUM(dh1024_160_p),
+  STATIC_BIGNUM(dh1024_160_q),
+  STATIC_BIGNUM(dh1024_160_g),
+};
+
+static const struct standard_parameters dh2048_224 = {
+  STATIC_BIGNUM(dh2048_224_p),
+  STATIC_BIGNUM(dh2048_224_q),
+  STATIC_BIGNUM(dh2048_224_g),
+};
+
+static const struct standard_parameters dh2048_256 = {
+  STATIC_BIGNUM(dh2048_256_p),
+  STATIC_BIGNUM(dh2048_256_q),
+  STATIC_BIGNUM(dh2048_256_g),
+};
+
+static DH *get_standard_parameters(const struct standard_parameters *params,
+                                   const ENGINE *engine) {
+  DH *dh;
+
+  dh = DH_new_method(engine);
+  if (!dh) {
+    return NULL;
+  }
+
+  dh->p = BN_dup(&params->p);
+  dh->q = BN_dup(&params->q);
+  dh->g = BN_dup(&params->g);
+  if (!dh->p || !dh->q || !dh->g) {
+    DH_free(dh);
+    return NULL;
+  }
+
+  return dh;
+}
+
+DH *DH_get_1024_160(const ENGINE *engine) {
+  return get_standard_parameters(&dh1024_160, engine);
+}
+
+DH *DH_get_2048_224(const ENGINE *engine) {
+  return get_standard_parameters(&dh2048_224, engine);
+}
+
+DH *DH_get_2048_256(const ENGINE *engine) {
+  return get_standard_parameters(&dh2048_256, engine);
+}
diff --git a/crypto/digest/CMakeLists.txt b/crypto/digest/CMakeLists.txt
new file mode 100644
index 0000000..a7ea708
--- /dev/null
+++ b/crypto/digest/CMakeLists.txt
@@ -0,0 +1,11 @@
+include_directories(. .. ../../include)
+
+add_library(
+	digest
+
+	OBJECT
+
+	digest.c
+	digests.c
+	digest_error.c
+)
diff --git a/crypto/digest/digest.c b/crypto/digest/digest.c
new file mode 100644
index 0000000..2bfb0fa
--- /dev/null
+++ b/crypto/digest/digest.c
@@ -0,0 +1,269 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/digest.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+int EVP_MD_type(const EVP_MD *md) { return md->type; }
+
+const char *EVP_MD_name(const EVP_MD *md) { return OBJ_nid2sn(md->type); }
+
+uint32_t EVP_MD_flags(const EVP_MD *md) { return md->flags; }
+
+size_t EVP_MD_size(const EVP_MD *md) { return md->md_size; }
+
+size_t EVP_MD_block_size(const EVP_MD *md) { return md->block_size; }
+
+
+void EVP_MD_CTX_init(EVP_MD_CTX *ctx) { memset(ctx, 0, sizeof(EVP_MD_CTX)); }
+
+EVP_MD_CTX *EVP_MD_CTX_create(void) {
+  EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof(EVP_MD_CTX));
+
+  if (ctx) {
+    EVP_MD_CTX_init(ctx);
+  }
+
+  return ctx;
+}
+
+int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) {
+  if (ctx->digest && ctx->digest->ctx_size && ctx->md_data) {
+    OPENSSL_cleanse(ctx->md_data, ctx->digest->ctx_size);
+    OPENSSL_free(ctx->md_data);
+  }
+
+  assert(ctx->pctx == NULL || ctx->pctx_ops != NULL);
+  if (ctx->pctx_ops) {
+    ctx->pctx_ops->free(ctx->pctx);
+  }
+
+  EVP_MD_CTX_init(ctx);
+
+  return 1;
+}
+
+void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) {
+  if (!ctx) {
+    return;
+  }
+
+  EVP_MD_CTX_cleanup(ctx);
+  OPENSSL_free(ctx);
+}
+
+int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
+  uint8_t *tmp_buf = NULL;
+
+  if (in == NULL || in->digest == NULL) {
+    OPENSSL_PUT_ERROR(DIGEST, EVP_MD_CTX_copy_ex,
+                      DIGEST_R_INPUT_NOT_INITIALIZED);
+    return 0;
+  }
+
+  if (out->digest == in->digest) {
+    /* |md_data| will be the correct size in this case so it's removed from
+     * |out| at this point so that |EVP_MD_CTX_cleanup| doesn't free it and
+     * then it's reused. */
+    tmp_buf = out->md_data;
+    out->md_data = NULL;
+  }
+
+  EVP_MD_CTX_cleanup(out);
+  memcpy(out, in, sizeof(EVP_MD_CTX));
+
+  if (in->md_data && in->digest->ctx_size) {
+    if (tmp_buf) {
+      out->md_data = tmp_buf;
+    } else {
+      out->md_data = OPENSSL_malloc(in->digest->ctx_size);
+      if (!out->md_data) {
+        OPENSSL_PUT_ERROR(DIGEST, EVP_MD_CTX_copy_ex, ERR_R_MALLOC_FAILURE);
+        return 0;
+      }
+    }
+    memcpy(out->md_data, in->md_data, in->digest->ctx_size);
+  }
+
+  assert(in->pctx == NULL || in->pctx_ops != NULL);
+  if (in->pctx && in->pctx_ops) {
+    out->pctx = in->pctx_ops->dup(in->pctx);
+    if (!out->pctx) {
+      EVP_MD_CTX_cleanup(out);
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
+  EVP_MD_CTX_init(out);
+  return EVP_MD_CTX_copy_ex(out, in);
+}
+
+int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *engine) {
+  if (ctx->digest != type) {
+    if (ctx->digest && ctx->digest->ctx_size) {
+      OPENSSL_free(ctx->md_data);
+    }
+    ctx->digest = type;
+    if (!(ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) && type->ctx_size) {
+      ctx->update = type->update;
+      ctx->md_data = OPENSSL_malloc(type->ctx_size);
+      if (ctx->md_data == NULL) {
+        OPENSSL_PUT_ERROR(DIGEST, EVP_DigestInit_ex, ERR_R_MALLOC_FAILURE);
+        return 0;
+      }
+    }
+  }
+
+  assert(ctx->pctx == NULL || ctx->pctx_ops != NULL);
+  if (ctx->pctx_ops) {
+    if (!ctx->pctx_ops->begin_digest(ctx)) {
+      return 0;
+    }
+  }
+
+  if (ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) {
+    return 1;
+  }
+
+  return ctx->digest->init(ctx);
+}
+
+int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type) {
+  EVP_MD_CTX_init(ctx);
+  return EVP_DigestInit_ex(ctx, type, NULL);
+}
+
+int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return ctx->update(ctx, data, len);
+}
+
+int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, uint8_t *md_out, unsigned int *size) {
+  int ret;
+
+  assert(ctx->digest->md_size <= EVP_MAX_MD_SIZE);
+  ret = ctx->digest->final(ctx, md_out);
+  if (size != NULL) {
+    *size = ctx->digest->md_size;
+  }
+  OPENSSL_cleanse(ctx->md_data, ctx->digest->ctx_size);
+
+  return ret;
+}
+
+int EVP_DigestFinal(EVP_MD_CTX *ctx, uint8_t *md, unsigned int *size) {
+  int ret = EVP_DigestFinal_ex(ctx, md, size);
+  EVP_MD_CTX_cleanup(ctx);
+  return ret;
+}
+
+int EVP_Digest(const void *data, size_t count, uint8_t *out_md,
+               unsigned int *out_size, const EVP_MD *type, ENGINE *impl) {
+  EVP_MD_CTX ctx;
+  int ret;
+
+  EVP_MD_CTX_init(&ctx);
+  ret = EVP_DigestInit_ex(&ctx, type, impl) &&
+        EVP_DigestUpdate(&ctx, data, count) &&
+        EVP_DigestFinal_ex(&ctx, out_md, out_size);
+  EVP_MD_CTX_cleanup(&ctx);
+
+  return ret;
+}
+
+
+const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *ctx) {
+  if (ctx == NULL) {
+    return NULL;
+  }
+  return ctx->digest;
+}
+
+unsigned EVP_MD_CTX_size(const EVP_MD_CTX *ctx) {
+  return EVP_MD_size(EVP_MD_CTX_md(ctx));
+}
+
+unsigned EVP_MD_CTX_block_size(const EVP_MD_CTX *ctx) {
+  return EVP_MD_block_size(EVP_MD_CTX_md(ctx));
+}
+
+int EVP_MD_CTX_type(const EVP_MD_CTX *ctx) {
+  return EVP_MD_type(EVP_MD_CTX_md(ctx));
+}
+
+void EVP_MD_CTX_set_flags(EVP_MD_CTX *ctx, uint32_t flags) {
+  ctx->flags |= flags;
+}
+
+void EVP_MD_CTX_clear_flags(EVP_MD_CTX *ctx, uint32_t flags) {
+  ctx->flags &= ~flags;
+}
+
+uint32_t EVP_MD_CTX_test_flags(const EVP_MD_CTX *ctx, uint32_t flags) {
+  return ctx->flags & flags;
+}
diff --git a/crypto/digest/digest.h b/crypto/digest/digest.h
new file mode 100644
index 0000000..9a91908
--- /dev/null
+++ b/crypto/digest/digest.h
@@ -0,0 +1,267 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_DIGEST_H
+#define OPENSSL_HEADER_DIGEST_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Digest functions.
+ *
+ * An EVP_MD abstracts the details of a specific hash function allowing code to
+ * deal with the concept of a "hash function" without needing to know exactly
+ * which hash function it is. */
+
+
+/* Hash algorithms.
+ *
+ * The following functions return |EVP_MD| objects that implement the named hash
+ * function. */
+
+const EVP_MD *EVP_md5(void);
+const EVP_MD *EVP_sha1(void);
+const EVP_MD *EVP_sha224(void);
+const EVP_MD *EVP_sha256(void);
+const EVP_MD *EVP_sha384(void);
+const EVP_MD *EVP_sha512(void);
+
+/* EVP_get_digestbynid returns an |EVP_MD| for the given NID, or NULL if no
+ * such digest is known. */
+const EVP_MD *EVP_get_digestbynid(int nid);
+
+/* EVP_get_digestbyobj returns an |EVP_MD| for the given |ASN1_OBJECT|, or NULL
+ * if no such digest is known. */
+const EVP_MD *EVP_get_digestbyobj(const ASN1_OBJECT *obj);
+
+
+/* Digest contexts.
+ *
+ * An EVP_MD_CTX represents the state of a specific digest operation in
+ * progress. */
+
+/* EVP_MD_CTX_init initialises an, already allocated, |EVP_MD_CTX|. */
+void EVP_MD_CTX_init(EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_create allocates and initialises a fresh |EVP_MD_CTX| and returns
+ * it, or NULL on allocation failure. */
+EVP_MD_CTX *EVP_MD_CTX_create(void);
+
+/* EVP_MD_CTX_cleanup frees any resources owned by |ctx| and resets it to a
+ * freshly initialised state. It does not free |ctx| itself. It returns one. */
+int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_destroy calls |EVP_MD_CTX_cleanup| and then frees |ctx| itself. */
+void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_copy_ex sets |out|, which must already be initialised, to be a
+ * copy of |in|. It returns one on success and zero on error. */
+int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in);
+
+
+/* Digest operations. */
+
+/* EVP_DigestInit_ex configures |ctx|, which must already have been
+ * initialised, for a fresh hashing operation using |type|. It returns one on
+ * success and zero otherwise. */
+int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *engine);
+
+/* EVP_DigestInit acts like |EVP_DigestInit_ex| except that |ctx| is
+ * initialised before use. */
+int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);
+
+/* EVP_DigestUpdate hashes |len| bytes from |data| into the hashing operation
+ * in |ctx|. It returns one on success and zero otherwise. */
+int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, size_t len);
+
+/* EVP_MAX_MD_SIZE is the largest digest size supported. Functions that output
+ * a digest generally require the buffer have at least this much space. */
+#define EVP_MAX_MD_SIZE 64 /* SHA-512 is the longest so far. */
+
+/* EVP_DigestFinal_ex finishes the digest in |ctx| and writes the output to
+ * |md_out|. At most |EVP_MAX_MD_SIZE| bytes are written. If |out_size| is not
+ * NULL then |*out_size| is set to the number of bytes written. It returns one
+ * on success and zero otherwise. After this call, the hash cannot be updated
+ * or finished again until |EVP_DigestFinal_ex| is called to start another
+ * hashing operation. */
+int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, uint8_t *md_out,
+                       unsigned int *out_size);
+
+/* EVP_DigestFinal acts like |EVP_DigestFinal_ex| except that
+ * |EVP_MD_CTX_cleanup| is called on |ctx| before returning. */
+int EVP_DigestFinal(EVP_MD_CTX *ctx, uint8_t *md_out, unsigned int *out_size);
+
+/* EVP_Digest performs a complete hashing operation in one call. It hashes
+ * |len| bytes from |data| and writes the digest to |md_out|. At most
+ * |EVP_MAX_MD_SIZE| bytes are written. If |out_size| is not NULL then
+ * |*out_size| is set to the number of bytes written. It returns one on success
+ * and zero otherwise. */
+int EVP_Digest(const void *data, size_t len, uint8_t *md_out,
+               unsigned int *md_out_size, const EVP_MD *type, ENGINE *impl);
+
+
+/* Digest function accessors.
+ *
+ * These functions allow code to learn details about an abstract hash
+ * function. */
+
+/* EVP_MD_type returns a NID identifing |md|. (For example, |NID_md5|.) */
+int EVP_MD_type(const EVP_MD *md);
+
+/* EVP_MD_name returns the short name for |md| or NULL if no name is known. */
+const char *EVP_MD_name(const EVP_MD *md);
+
+/* EVP_MD_flags returns the flags for |md|, which is a set of |EVP_MD_FLAG_*|
+ * values, ORed together. */
+uint32_t EVP_MD_flags(const EVP_MD *md);
+
+/* EVP_MD_size returns the digest size of |md|, in bytes. */
+size_t EVP_MD_size(const EVP_MD *md);
+
+/* EVP_MD_block_size returns the native block-size of |md|. */
+size_t EVP_MD_block_size(const EVP_MD *md);
+
+/* EVP_MD_FLAG_PKEY_DIGEST indicates the the digest function is used with a
+ * specific public key in order to verify signatures. (For example,
+ * EVP_dss1.) */
+#define EVP_MD_FLAG_PKEY_DIGEST 1
+
+/* EVP_MD_FLAG_DIGALGID_ABSENT indicates that the parameter type in an X.509
+ * DigestAlgorithmIdentifier representing this digest function should be
+ * undefined rather than NULL. */
+#define EVP_MD_FLAG_DIGALGID_ABSENT 2
+
+
+/* Deprecated functions. */
+
+/* EVP_MD_CTX_copy sets |out|, which must /not/ be initialised, to be a copy of
+ * |in|. It returns one on success and zero on error. */
+int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in);
+
+
+/* Digest operation accessors. */
+
+/* EVP_MD_CTX_md returns the underlying digest function, or NULL if one has not
+ * been set. */
+const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_size returns the digest size of |ctx|. It will crash if a digest
+ * hasn't been set on |ctx|. */
+unsigned EVP_MD_CTX_size(const EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_block_size returns the block size of the digest function used by
+ * |ctx|. It will crash if a digest hasn't been set on |ctx|. */
+unsigned EVP_MD_CTX_block_size(const EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_type returns a NID describing the digest function used by |ctx|.
+ * (For example, |NID_md5|.) It will crash if a digest hasn't been set on
+ * |ctx|. */
+int EVP_MD_CTX_type(const EVP_MD_CTX *ctx);
+
+/* EVP_MD_CTX_set_flags ORs |flags| into the flags member of |ctx|. */
+void EVP_MD_CTX_set_flags(EVP_MD_CTX *ctx, uint32_t flags);
+
+/* EVP_MD_CTX_clear_flags clears any bits from the flags member of |ctx| that
+ * are set in |flags|. */
+void EVP_MD_CTX_clear_flags(EVP_MD_CTX *ctx, uint32_t flags);
+
+/* EVP_MD_CTX_test_flags returns the AND of |flags| and the flags member of
+ * |ctx|. */
+uint32_t EVP_MD_CTX_test_flags(const EVP_MD_CTX *ctx, uint32_t flags);
+
+
+struct evp_md_pctx_ops;
+
+struct env_md_ctx_st {
+  /* digest is the underlying digest function, or NULL if not set. */
+  const EVP_MD *digest;
+  /* flags is the OR of a number of |EVP_MD_CTX_FLAG_*| values. */
+  uint32_t flags;
+  /* md_data points to a block of memory that contains the hash-specific
+   * context. */
+  void *md_data;
+  /* update is usually copied from |digest->update| but can differ in some
+   * cases, i.e. HMAC. */
+  int (*update)(EVP_MD_CTX *ctx, const void *data, size_t count);
+
+  /* pctx is an opaque (at this layer) pointer to additional context that
+   * EVP_PKEY functions may store in this object. */
+  EVP_PKEY_CTX *pctx;
+
+  /* pctx_ops, if not NULL, points to a vtable that contains functions to
+   * manipulate |pctx|. */
+  const struct evp_md_pctx_ops *pctx_ops;
+} /* EVP_MD_CTX */;
+
+/* EVP_MD_CTX_FLAG_NO_INIT causes the |EVP_MD|'s |init| function not to be
+ * called, the |update| member not to be copied from the |EVP_MD| in
+ * |EVP_DigestInit_ex| and for |md_data| not to be initialised. */
+#define EVP_MD_CTX_FLAG_NO_INIT 1
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define DIGEST_F_EVP_DigestInit_ex 100
+#define DIGEST_F_EVP_MD_CTX_copy_ex 101
+#define DIGEST_R_INPUT_NOT_INITIALIZED 100
+
+#endif  /* OPENSSL_HEADER_DIGEST_H */
diff --git a/crypto/digest/digest_error.c b/crypto/digest/digest_error.c
new file mode 100644
index 0000000..e80a5df
--- /dev/null
+++ b/crypto/digest/digest_error.c
@@ -0,0 +1,24 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "digest.h"
+
+const ERR_STRING_DATA DIGEST_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_DIGEST, DIGEST_F_EVP_DigestInit_ex, 0), "EVP_DigestInit_ex"},
+  {ERR_PACK(ERR_LIB_DIGEST, DIGEST_F_EVP_MD_CTX_copy_ex, 0), "EVP_MD_CTX_copy_ex"},
+  {ERR_PACK(ERR_LIB_DIGEST, 0, DIGEST_R_INPUT_NOT_INITIALIZED), "INPUT_NOT_INITIALIZED"},
+  {0, NULL},
+};
diff --git a/crypto/digest/digests.c b/crypto/digest/digests.c
new file mode 100644
index 0000000..b757ea8
--- /dev/null
+++ b/crypto/digest/digests.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/digest.h>
+
+#include <openssl/md5.h>
+#include <openssl/obj.h>
+#include <openssl/sha.h>
+
+#include "internal.h"
+
+
+static int md5_init(EVP_MD_CTX *ctx) { return MD5_Init(ctx->md_data); }
+
+static int md5_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  return MD5_Update(ctx->md_data, data, count);
+}
+
+static int md5_final(EVP_MD_CTX *ctx, unsigned char *out) {
+  return MD5_Final(out, ctx->md_data);
+}
+
+static const EVP_MD md5_md = {
+    NID_md5,    MD5_DIGEST_LENGTH, 0 /* flags */,       md5_init,
+    md5_update, md5_final,         64 /* block size */, sizeof(MD5_CTX),
+};
+
+const EVP_MD *EVP_md5(void) { return &md5_md; }
+
+
+static int sha1_init(EVP_MD_CTX *ctx) { return SHA1_Init(ctx->md_data); }
+
+static int sha1_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  return SHA1_Update(ctx->md_data, data, count);
+}
+
+static int sha1_final(EVP_MD_CTX *ctx, unsigned char *md) {
+  return SHA1_Final(md, ctx->md_data);
+}
+
+static const EVP_MD sha1_md = {
+    NID_sha1,    SHA_DIGEST_LENGTH, 0 /* flags */,       sha1_init,
+    sha1_update, sha1_final,        64 /* block size */, sizeof(SHA_CTX),
+};
+
+const EVP_MD *EVP_sha1(void) { return &sha1_md; }
+
+
+static int sha224_init(EVP_MD_CTX *ctx) { return SHA224_Init(ctx->md_data); }
+
+static int sha224_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  return SHA224_Update(ctx->md_data, data, count);
+}
+
+static int sha224_final(EVP_MD_CTX *ctx, unsigned char *md) {
+  return SHA224_Final(md, ctx->md_data);
+}
+
+static const EVP_MD sha224_md = {
+    NID_sha224,          SHA224_DIGEST_LENGTH, 0 /* flags */,
+    sha224_init,         sha224_update,        sha224_final,
+    64 /* block size */, sizeof(SHA256_CTX),
+};
+
+const EVP_MD *EVP_sha224(void) { return &sha224_md; }
+
+
+static int sha256_init(EVP_MD_CTX *ctx) { return SHA256_Init(ctx->md_data); }
+
+static int sha256_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  return SHA256_Update(ctx->md_data, data, count);
+}
+
+static int sha256_final(EVP_MD_CTX *ctx, unsigned char *md) {
+  return SHA256_Final(md, ctx->md_data);
+}
+
+static const EVP_MD sha256_md = {
+    NID_sha256,          SHA256_DIGEST_LENGTH, 0 /* flags */,
+    sha256_init,         sha256_update,        sha256_final,
+    64 /* block size */, sizeof(SHA256_CTX),
+};
+
+const EVP_MD *EVP_sha256(void) { return &sha256_md; }
+
+
+static int sha384_init(EVP_MD_CTX *ctx) { return SHA384_Init(ctx->md_data); }
+
+static int sha384_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  return SHA384_Update(ctx->md_data, data, count);
+}
+
+static int sha384_final(EVP_MD_CTX *ctx, unsigned char *md) {
+  return SHA384_Final(md, ctx->md_data);
+}
+
+static const EVP_MD sha384_md = {
+    NID_sha384,           SHA384_DIGEST_LENGTH, 0 /* flags */,
+    sha384_init,          sha384_update,        sha384_final,
+    128 /* block size */, sizeof(SHA512_CTX),
+};
+
+const EVP_MD *EVP_sha384(void) { return &sha384_md; }
+
+
+static int sha512_init(EVP_MD_CTX *ctx) { return SHA512_Init(ctx->md_data); }
+
+static int sha512_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  return SHA512_Update(ctx->md_data, data, count);
+}
+
+static int sha512_final(EVP_MD_CTX *ctx, unsigned char *md) {
+  return SHA512_Final(md, ctx->md_data);
+}
+
+static const EVP_MD sha512_md = {
+    NID_sha512,           SHA512_DIGEST_LENGTH, 0 /* flags */,
+    sha512_init,          sha512_update,        sha512_final,
+    128 /* block size */, sizeof(SHA512_CTX),
+};
+
+const EVP_MD *EVP_sha512(void) { return &sha512_md; }
+
+struct nid_to_digest {
+  int nid;
+  const EVP_MD *(*md_func)();
+};
+
+static const struct nid_to_digest nid_to_digest_mapping[] = {
+  { NID_md5, EVP_md5 },
+  { NID_sha1, EVP_sha1 },
+  { NID_sha224, EVP_sha224 },
+  { NID_sha256, EVP_sha256 },
+  { NID_sha384, EVP_sha384 },
+  { NID_sha512, EVP_sha512 },
+  { NID_dsaWithSHA, EVP_sha1 },
+  { NID_dsaWithSHA1, EVP_sha1 },
+  { NID_ecdsa_with_SHA1, EVP_sha1 },
+  { NID_md5WithRSAEncryption, EVP_md5 },
+  { NID_sha1WithRSAEncryption, EVP_sha1 },
+  { NID_sha224WithRSAEncryption, EVP_sha224 },
+  { NID_sha256WithRSAEncryption, EVP_sha256 },
+  { NID_sha384WithRSAEncryption, EVP_sha384 },
+  { NID_sha512WithRSAEncryption, EVP_sha512 },
+};
+
+const EVP_MD* EVP_get_digestbynid(int nid) {
+  unsigned i;
+
+  for (i = 0; i < sizeof(nid_to_digest_mapping) / sizeof(struct nid_to_digest);
+       i++) {
+    if (nid_to_digest_mapping[i].nid == nid) {
+      return nid_to_digest_mapping[i].md_func();
+    }
+  }
+
+  return NULL;
+}
+
+const EVP_MD* EVP_get_digestbyobj(const ASN1_OBJECT *obj) {
+  return EVP_get_digestbynid(OBJ_obj2nid(obj));
+}
diff --git a/crypto/digest/internal.h b/crypto/digest/internal.h
new file mode 100644
index 0000000..94dbfaa
--- /dev/null
+++ b/crypto/digest/internal.h
@@ -0,0 +1,117 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_DIGEST_INTERNAL_H
+#define OPENSSL_HEADER_DIGEST_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+struct env_md_st {
+  /* type contains a NID identifing the digest function. (For example,
+   * NID_md5.) */
+  int type;
+
+  /* md_size contains the size, in bytes, of the resulting digest. */
+  unsigned md_size;
+
+  /* flags contains the OR of |EVP_MD_FLAG_*| values. */
+  uint32_t flags;
+
+  /* init initialises the state in |ctx->md_data|. It returns one on success
+   * and zero otherwise. */
+  int (*init)(EVP_MD_CTX *ctx);
+
+  /* update hashes |len| bytes of |data| into the state in |ctx->md_data|. */
+  int (*update)(EVP_MD_CTX *ctx, const void *data, size_t count);
+
+  /* final completes the hash and writes |md_size| bytes of digest to |out|. */
+  int (*final)(EVP_MD_CTX *ctx, uint8_t *out);
+
+  /* block_size contains the hash's native block size. */
+  unsigned block_size;
+
+  /* ctx_size contains the size, in bytes, of the state of the hash function. */
+  unsigned ctx_size;
+};
+
+/* evp_md_pctx_ops contains function pointers to allow the |pctx| member of
+ * |EVP_MD_CTX| to be manipulated without breaking laying by calling EVP
+ * functions. */
+struct evp_md_pctx_ops {
+  /* free is called when an |EVP_MD_CTX| is being freed and the |pctx| also
+   * needs to be freed. */
+  void (*free) (EVP_PKEY_CTX *pctx);
+
+  /* dup is called when an |EVP_MD_CTX| is copied and so the |pctx| also needs
+   * to be copied. */
+  EVP_PKEY_CTX* (*dup) (EVP_PKEY_CTX *pctx);
+
+  /* begin_digest is called when a new digest operation is started. It returns
+   * one on success and zero otherwise. */
+  int (*begin_digest) (EVP_MD_CTX *ctx);
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_DIGEST_INTERNAL */
diff --git a/crypto/digest/md32_common.h b/crypto/digest/md32_common.h
new file mode 100644
index 0000000..d9e8432
--- /dev/null
+++ b/crypto/digest/md32_common.h
@@ -0,0 +1,370 @@
+/* ====================================================================
+ * Copyright (c) 1999-2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#ifndef OPENSSL_HEADER_MD32_COMMON_H
+#define OPENSSL_HEADER_MD32_COMMON_H
+
+#include <openssl/base.h>
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define asm __asm__
+
+/* This is a generic 32 bit "collector" for message digest algorithms.
+ * Whenever needed it collects input character stream into chunks of
+ * 32 bit values and invokes a block function that performs actual hash
+ * calculations.
+ *
+ * Porting guide.
+ *
+ * Obligatory macros:
+ *
+ * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
+ *	this macro defines byte order of input stream.
+ * HASH_CBLOCK
+ *	size of a unit chunk HASH_BLOCK operates on.
+ * HASH_LONG
+ *	has to be at lest 32 bit wide, if it's wider, then
+ *	HASH_LONG_LOG2 *has to* be defined along
+ * HASH_CTX
+ *	context structure that at least contains following
+ *	members:
+ *		typedef struct {
+ *			...
+ *			HASH_LONG	Nl,Nh;
+ *			either {
+ *			HASH_LONG	data[HASH_LBLOCK];
+ *			unsigned char	data[HASH_CBLOCK];
+ *			};
+ *			unsigned int	num;
+ *			...
+ *			} HASH_CTX;
+ *	data[] vector is expected to be zeroed upon first call to
+ *	HASH_UPDATE.
+ * HASH_UPDATE
+ *	name of "Update" function, implemented here.
+ * HASH_TRANSFORM
+ *	name of "Transform" function, implemented here.
+ * HASH_FINAL
+ *	name of "Final" function, implemented here.
+ * HASH_BLOCK_DATA_ORDER
+ *	name of "block" function capable of treating *unaligned* input
+ *	message in original (data) byte order, implemented externally.
+ * HASH_MAKE_STRING
+ *	macro convering context variables to an ASCII hash string.
+ *
+ * MD5 example:
+ *
+ *	#define DATA_ORDER_IS_LITTLE_ENDIAN
+ *
+ *	#define HASH_LONG		MD5_LONG
+ *	#define HASH_LONG_LOG2		MD5_LONG_LOG2
+ *	#define HASH_CTX		MD5_CTX
+ *	#define HASH_CBLOCK		MD5_CBLOCK
+ *	#define HASH_UPDATE		MD5_Update
+ *	#define HASH_TRANSFORM		MD5_Transform
+ *	#define HASH_FINAL		MD5_Final
+ *	#define HASH_BLOCK_DATA_ORDER	md5_block_data_order
+ *
+ *					<appro@fy.chalmers.se>
+ */
+
+#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+#error "DATA_ORDER must be defined!"
+#endif
+
+#ifndef HASH_CBLOCK
+#error "HASH_CBLOCK must be defined!"
+#endif
+#ifndef HASH_LONG
+#error "HASH_LONG must be defined!"
+#endif
+#ifndef HASH_CTX
+#error "HASH_CTX must be defined!"
+#endif
+
+#ifndef HASH_UPDATE
+#error "HASH_UPDATE must be defined!"
+#endif
+#ifndef HASH_TRANSFORM
+#error "HASH_TRANSFORM must be defined!"
+#endif
+#ifndef HASH_FINAL
+#error "HASH_FINAL must be defined!"
+#endif
+
+#ifndef HASH_BLOCK_DATA_ORDER
+#error "HASH_BLOCK_DATA_ORDER must be defined!"
+#endif
+
+/*
+ * Engage compiler specific rotate intrinsic function if available.
+ */
+#undef ROTATE
+# if defined(_MSC_VER)
+#  define ROTATE(a,n)	_lrotl(a,n)
+# elif defined(__ICC)
+#  define ROTATE(a,n)	_rotl(a,n)
+# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM)
+  /*
+   * Some GNU C inline assembler templates. Note that these are
+   * rotates by *constant* number of bits! But that's exactly
+   * what we need here...
+   * 					<appro@fy.chalmers.se>
+   */
+#  if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+#   define ROTATE(a,n)	({ register unsigned int ret;	\
+				asm (			\
+				"roll %1,%0"		\
+				: "=r"(ret)		\
+				: "I"(n), "0"((unsigned int)(a))	\
+				: "cc");		\
+			   ret;				\
+			})
+# endif
+#endif /* PEDANTIC */
+
+#ifndef ROTATE
+#define ROTATE(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
+
+#if defined(DATA_ORDER_IS_BIG_ENDIAN)
+
+#ifndef PEDANTIC
+# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM)
+# if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+    /*
+     * This gives ~30-40% performance improvement in SHA-256 compiled
+     * with gcc [on P4]. Well, first macro to be frank. We can pull
+     * this trick on x86* platforms only, because these CPUs can fetch
+     * unaligned data without raising an exception.
+     */
+#   define HOST_c2l(c,l)	({ unsigned int r=*((const unsigned int *)(c));	\
+				   asm ("bswapl %0":"=r"(r):"0"(r));	\
+				   (c)+=4; (l)=r;			})
+#   define HOST_l2c(l,c)	({ unsigned int r=(l);			\
+				   asm ("bswapl %0":"=r"(r):"0"(r));	\
+				   *((unsigned int *)(c))=r; (c)+=4; r;	})
+#  elif defined(__aarch64__)
+#   if defined(__BYTE_ORDER__)
+#    if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+#     define HOST_c2l(c,l)	({ unsigned int r;		\
+				   asm ("rev	%w0,%w1"	\
+					:"=r"(r)		\
+					:"r"(*((const unsigned int *)(c))));\
+				   (c)+=4; (l)=r;		})
+#     define HOST_l2c(l,c)	({ unsigned int r;		\
+				   asm ("rev	%w0,%w1"	\
+					:"=r"(r)		\
+					:"r"((unsigned int)(l)));\
+				   *((unsigned int *)(c))=r; (c)+=4; r;	})
+#    elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+#     define HOST_c2l(c,l)	((l)=*((const unsigned int *)(c)), (c)+=4, (l))
+#     define HOST_l2c(l,c)	(*((unsigned int *)(c))=(l), (c)+=4, (l))
+#    endif
+#   endif
+#  endif
+# endif
+#endif
+
+#ifndef HOST_c2l
+#define HOST_c2l(c,l)	(l =(((unsigned long)(*((c)++)))<<24),		\
+			 l|=(((unsigned long)(*((c)++)))<<16),		\
+			 l|=(((unsigned long)(*((c)++)))<< 8),		\
+			 l|=(((unsigned long)(*((c)++)))    ),		\
+			 l)
+#endif
+#ifndef HOST_l2c
+#define HOST_l2c(l,c)	(*((c)++)=(unsigned char)(((l)>>24)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>>16)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>> 8)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)    )&0xff),	\
+			 l)
+#endif
+
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+
+#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+   /* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */
+#  define HOST_c2l(c,l)	((l)=*((const unsigned int *)(c)), (c)+=4, l)
+#  define HOST_l2c(l,c)	(*((unsigned int *)(c))=(l), (c)+=4, l)
+#endif
+
+#ifndef HOST_c2l
+#define HOST_c2l(c,l)	(l =(((unsigned long)(*((c)++)))    ),		\
+			 l|=(((unsigned long)(*((c)++)))<< 8),		\
+			 l|=(((unsigned long)(*((c)++)))<<16),		\
+			 l|=(((unsigned long)(*((c)++)))<<24),		\
+			 l)
+#endif
+#ifndef HOST_l2c
+#define HOST_l2c(l,c)	(*((c)++)=(unsigned char)(((l)    )&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>> 8)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>>16)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>>24)&0xff),	\
+			 l)
+#endif
+
+#endif
+
+int HASH_UPDATE (HASH_CTX *c, const void *data_, size_t len)
+	{
+	const unsigned char *data=data_;
+	unsigned char *p;
+	HASH_LONG l;
+	size_t n;
+
+	if (len==0) return 1;
+
+	l=(c->Nl+(((HASH_LONG)len)<<3))&0xffffffffUL;
+	/* 95-05-24 eay Fixed a bug with the overflow handling, thanks to
+	 * Wei Dai <weidai@eskimo.com> for pointing it out. */
+	if (l < c->Nl) /* overflow */
+		c->Nh++;
+	c->Nh+=(HASH_LONG)(len>>29);	/* might cause compiler warning on 16-bit */
+	c->Nl=l;
+
+	n = c->num;
+	if (n != 0)
+		{
+		p=(unsigned char *)c->data;
+
+		if (len >= HASH_CBLOCK || len+n >= HASH_CBLOCK)
+			{
+			memcpy (p+n,data,HASH_CBLOCK-n);
+			HASH_BLOCK_DATA_ORDER (c,p,1);
+			n      = HASH_CBLOCK-n;
+			data  += n;
+			len   -= n;
+			c->num = 0;
+			memset (p,0,HASH_CBLOCK);	/* keep it zeroed */
+			}
+		else
+			{
+			memcpy (p+n,data,len);
+			c->num += (unsigned int)len;
+			return 1;
+			}
+		}
+
+	n = len/HASH_CBLOCK;
+	if (n > 0)
+		{
+		HASH_BLOCK_DATA_ORDER (c,data,n);
+		n    *= HASH_CBLOCK;
+		data += n;
+		len  -= n;
+		}
+
+	if (len != 0)
+		{
+		p = (unsigned char *)c->data;
+		c->num = (unsigned int)len;
+		memcpy (p,data,len);
+		}
+	return 1;
+	}
+
+
+void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data)
+	{
+	HASH_BLOCK_DATA_ORDER (c,data,1);
+	}
+
+
+int HASH_FINAL (unsigned char *md, HASH_CTX *c)
+	{
+	unsigned char *p = (unsigned char *)c->data;
+	size_t n = c->num;
+
+	p[n] = 0x80; /* there is always room for one */
+	n++;
+
+	if (n > (HASH_CBLOCK-8))
+		{
+		memset (p+n,0,HASH_CBLOCK-n);
+		n=0;
+		HASH_BLOCK_DATA_ORDER (c,p,1);
+		}
+	memset (p+n,0,HASH_CBLOCK-8-n);
+
+	p += HASH_CBLOCK-8;
+#if   defined(DATA_ORDER_IS_BIG_ENDIAN)
+	(void)HOST_l2c(c->Nh,p);
+	(void)HOST_l2c(c->Nl,p);
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+	(void)HOST_l2c(c->Nl,p);
+	(void)HOST_l2c(c->Nh,p);
+#endif
+	p -= HASH_CBLOCK;
+	HASH_BLOCK_DATA_ORDER (c,p,1);
+	c->num=0;
+	memset (p,0,HASH_CBLOCK);
+
+#ifndef HASH_MAKE_STRING
+#error "HASH_MAKE_STRING must be defined!"
+#else
+	HASH_MAKE_STRING(c,md);
+#endif
+
+	return 1;
+	}
+
+#ifndef MD32_REG_T
+#define MD32_REG_T int
+#endif
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_MD32_COMMON_H */
diff --git a/crypto/directory.h b/crypto/directory.h
new file mode 100644
index 0000000..e54df3c
--- /dev/null
+++ b/crypto/directory.h
@@ -0,0 +1,65 @@
+/* Copied from Richard Levitte's (richard@levitte.org) LP library.  All
+ * symbol names have been changed, with permission from the author. */
+
+/* $LP: LPlib/source/LPdir.h,v 1.1 2004/06/14 08:56:04 _cvs_levitte Exp $ */
+/*
+ * Copyright (c) 2004, Richard Levitte <richard@levitte.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef OPENSSL_HEADER_DIRECTORY_H
+#define OPENSSL_HEADER_DIRECTORY_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Directory functions abstract the O/S specific operations for opening and
+ * reading directories in the filesystem. */
+
+
+/* OPENSSL_dir_context_st is an opaque structure that represents an open
+ * directory and a position in that directory. */
+typedef struct OPENSSL_dir_context_st OPENSSL_DIR_CTX;
+
+/* OPENSSL_DIR_read reads a single filename from |ctx|. On the first call,
+ * |directory| must be given and |*ctx| must be NULL. Subsequent calls with the
+ * same |*ctx| will return subsequent file names until it returns NULL to
+ * indicate EOF. The strings returned reference a buffer internal to the
+ * |OPENSSL_DIR_CTX| and will be overridden by subsequent calls. */
+const char *OPENSSL_DIR_read(OPENSSL_DIR_CTX **ctx, const char *directory);
+
+/* OPENSSL_DIR_end closes |*ctx|. It returns one on success and zero on
+ * error. */
+int OPENSSL_DIR_end(OPENSSL_DIR_CTX **ctx);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_DIRECTORY_H */
diff --git a/crypto/directory_posix.c b/crypto/directory_posix.c
new file mode 100644
index 0000000..990f663
--- /dev/null
+++ b/crypto/directory_posix.c
@@ -0,0 +1,89 @@
+/* $LP: LPlib/source/LPdir_unix.c,v 1.11 2004/09/23 22:07:22 _cvs_levitte Exp $ */
+/*
+ * Copyright (c) 2004, Richard Levitte <richard@levitte.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#define _POSIX_C_SOURCE 1  /* for readdir_r */
+
+#include "directory.h"
+
+
+#if !defined(OPENSSL_WINDOWS)
+
+#include <dirent.h>
+#include <errno.h>
+
+struct OPENSSL_dir_context_st {
+  DIR *dir;
+  struct dirent dirent;
+};
+
+const char *OPENSSL_DIR_read(OPENSSL_DIR_CTX **ctx, const char *directory) {
+  struct dirent *dirent;
+
+  if (ctx == NULL || directory == NULL) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  errno = 0;
+  if (*ctx == NULL) {
+    *ctx = malloc(sizeof(OPENSSL_DIR_CTX));
+    if (*ctx == NULL) {
+      errno = ENOMEM;
+      return 0;
+    }
+    memset(*ctx, 0, sizeof(OPENSSL_DIR_CTX));
+
+    (*ctx)->dir = opendir(directory);
+    if ((*ctx)->dir == NULL) {
+      int save_errno = errno; /* Probably not needed, but I'm paranoid */
+      free(*ctx);
+      *ctx = NULL;
+      errno = save_errno;
+      return 0;
+    }
+  }
+
+  if (readdir_r((*ctx)->dir, &(*ctx)->dirent, &dirent) != 0 ||
+      dirent == NULL) {
+    return 0;
+  }
+
+  return (*ctx)->dirent.d_name;
+}
+
+int OPENSSL_DIR_end(OPENSSL_DIR_CTX **ctx) {
+  if (ctx != NULL && *ctx != NULL) {
+    int r = closedir((*ctx)->dir);
+    free(*ctx);
+    *ctx = NULL;
+    return r == 0;
+  }
+
+  errno = EINVAL;
+  return 0;
+}
+
+#endif  /* !OPENSSL_WINDOWS */
diff --git a/crypto/directory_win.c b/crypto/directory_win.c
new file mode 100644
index 0000000..4956383
--- /dev/null
+++ b/crypto/directory_win.c
@@ -0,0 +1,138 @@
+/* $LP: LPlib/source/LPdir_win.c,v 1.10 2004/08/26 13:36:05 _cvs_levitte Exp $ */
+/*
+ * Copyright (c) 2004, Richard Levitte <richard@levitte.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "directory.h"
+
+
+#if defined(OPENSSL_WINDOWS)
+
+#include <windows.h>
+#include <tchar.h>
+#include <errno.h>
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+struct OPENSSL_dir_context_st {
+  WIN32_FIND_DATA ctx;
+  HANDLE handle;
+  char entry_name[NAME_MAX + 1];
+};
+
+const char *OPENSSL_DIR_read(OPENSSL_DIR_CTX **ctx, const char *directory) {
+  if (ctx == NULL || directory == NULL) {
+    errno = EINVAL;
+    return 0;
+  }
+
+  errno = 0;
+  if (*ctx == NULL) {
+    *ctx = malloc(sizeof(OPENSSL_DIR_CTX));
+    if (*ctx == NULL) {
+      errno = ENOMEM;
+      return 0;
+    }
+    memset(*ctx, 0, sizeof(OPENSSL_DIR_CTX));
+
+    if (sizeof(TCHAR) != sizeof(char)) {
+      TCHAR *wdir = NULL;
+      /* len_0 denotes string length *with* trailing 0 */
+      size_t index = 0, len_0 = strlen(directory) + 1;
+
+      wdir = (TCHAR *)malloc(len_0 * sizeof(TCHAR));
+      if (wdir == NULL) {
+        free(*ctx);
+        *ctx = NULL;
+        errno = ENOMEM;
+        return 0;
+      }
+
+      if (!MultiByteToWideChar(CP_ACP, 0, directory, len_0, (WCHAR *)wdir,
+                               len_0)) {
+        for (index = 0; index < len_0; index++) {
+          wdir[index] = (TCHAR)directory[index];
+        }
+      }
+
+      (*ctx)->handle = FindFirstFile(wdir, &(*ctx)->ctx);
+
+      free(wdir);
+    } else {
+      (*ctx)->handle = FindFirstFile((TCHAR *)directory, &(*ctx)->ctx);
+    }
+
+    if ((*ctx)->handle == INVALID_HANDLE_VALUE) {
+      free(*ctx);
+      *ctx = NULL;
+      errno = EINVAL;
+      return 0;
+    }
+  } else {
+    if (FindNextFile((*ctx)->handle, &(*ctx)->ctx) == FALSE) {
+      return 0;
+    }
+  }
+
+  if (sizeof(TCHAR) != sizeof(char)) {
+    TCHAR *wdir = (*ctx)->ctx.cFileName;
+    size_t index, len_0 = 0;
+
+    while (wdir[len_0] && len_0 < (sizeof((*ctx)->entry_name) - 1)) {
+      len_0++;
+    }
+    len_0++;
+
+    if (!WideCharToMultiByte(CP_ACP, 0, (WCHAR *)wdir, len_0,
+                             (*ctx)->entry_name, sizeof((*ctx)->entry_name),
+                             NULL, 0)) {
+      for (index = 0; index < len_0; index++) {
+        (*ctx)->entry_name[index] = (char)wdir[index];
+      }
+    }
+  } else {
+    strncpy((*ctx)->entry_name, (const char *)(*ctx)->ctx.cFileName,
+            sizeof((*ctx)->entry_name) - 1);
+  }
+
+  (*ctx)->entry_name[sizeof((*ctx)->entry_name) - 1] = '\0';
+
+  return (*ctx)->entry_name;
+}
+
+int OPENSSL_DIR_end(OPENSSL_DIR_CTX **ctx) {
+  if (ctx != NULL && *ctx != NULL) {
+    FindClose((*ctx)->handle);
+    free(*ctx);
+    *ctx = NULL;
+    return 1;
+  }
+  errno = EINVAL;
+  return 0;
+}
+
+#endif  /* OPENSSL_WINDOWS */
diff --git a/crypto/dsa/CMakeLists.txt b/crypto/dsa/CMakeLists.txt
new file mode 100644
index 0000000..6dfb2a6
--- /dev/null
+++ b/crypto/dsa/CMakeLists.txt
@@ -0,0 +1,20 @@
+include_directories(. .. ../../include)
+
+add_library(
+	dsa
+
+	OBJECT
+
+	dsa.c
+	dsa_impl.c
+	dsa_asn1.c
+	dsa_error.c
+)
+
+add_executable(
+	dsa_test
+
+	dsa_test.c
+)
+
+target_link_libraries(dsa_test crypto)
diff --git a/crypto/dsa/dsa.c b/crypto/dsa/dsa.c
new file mode 100644
index 0000000..4ae6876
--- /dev/null
+++ b/crypto/dsa/dsa.c
@@ -0,0 +1,334 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ * The DSS routines are based on patches supplied by
+ * Steven Schoch <schoch@sheba.arc.nasa.gov>. */
+
+#include <openssl/dsa.h>
+
+#include <openssl/asn1.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/ex_data.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+extern const DSA_METHOD DSA_default_method;
+
+DSA *DSA_new(void) { return DSA_new_method(NULL); }
+
+DSA *DSA_new_method(const ENGINE *engine) {
+  DSA *dsa = (DSA *)OPENSSL_malloc(sizeof(DSA));
+  if (dsa == NULL) {
+    OPENSSL_PUT_ERROR(DSA, DSA_new_method, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(dsa, 0, sizeof(DSA));
+
+  if (engine) {
+    dsa->meth = ENGINE_get_DSA_method(engine);
+  }
+
+  if (dsa->meth == NULL) {
+    dsa->meth = (DSA_METHOD*) &DSA_default_method;
+  }
+  METHOD_ref(dsa->meth);
+
+  dsa->write_params = 1;
+  dsa->references = 1;
+
+  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_DSA, dsa, &dsa->ex_data)) {
+    METHOD_unref(dsa->meth);
+    OPENSSL_free(dsa);
+    return NULL;
+  }
+
+  if (dsa->meth->init && !dsa->meth->init(dsa)) {
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DSA, dsa, &dsa->ex_data);
+    METHOD_unref(dsa->meth);
+    OPENSSL_free(dsa);
+    return NULL;
+  }
+
+  return dsa;
+}
+
+void DSA_free(DSA *dsa) {
+  if (dsa == NULL) {
+    return;
+  }
+
+  if (CRYPTO_add(&dsa->references, -1, CRYPTO_LOCK_DSA) > 0) {
+    return;
+  }
+
+  if (dsa->meth->finish) {
+    dsa->meth->finish(dsa);
+  }
+  METHOD_unref(dsa->meth);
+
+  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DSA, dsa, &dsa->ex_data);
+
+  if (dsa->p != NULL)
+    BN_clear_free(dsa->p);
+  if (dsa->q != NULL)
+    BN_clear_free(dsa->q);
+  if (dsa->g != NULL)
+    BN_clear_free(dsa->g);
+  if (dsa->pub_key != NULL)
+    BN_clear_free(dsa->pub_key);
+  if (dsa->priv_key != NULL)
+    BN_clear_free(dsa->priv_key);
+  if (dsa->kinv != NULL)
+    BN_clear_free(dsa->kinv);
+  if (dsa->r != NULL)
+    BN_clear_free(dsa->r);
+  OPENSSL_free(dsa);
+}
+
+int DSA_up_ref(DSA *dsa) {
+  CRYPTO_add(&dsa->references, 1, CRYPTO_LOCK_DSA);
+  return 1;
+}
+
+int DSA_generate_parameters_ex(DSA *dsa, unsigned bits, const uint8_t *seed_in,
+                               size_t seed_len, int *out_counter,
+                               unsigned long *out_h, BN_GENCB *cb) {
+  if (dsa->meth->generate_parameters) {
+    return dsa->meth->generate_parameters(dsa, bits, seed_in, seed_len,
+                                          out_counter, out_h, cb);
+  }
+  return DSA_default_method.generate_parameters(dsa, bits, seed_in, seed_len,
+                                                out_counter, out_h, cb);
+}
+
+int DSA_generate_key(DSA *dsa) {
+  if (dsa->meth->keygen) {
+    return dsa->meth->keygen(dsa);
+  }
+  return DSA_default_method.keygen(dsa);
+}
+
+DSA_SIG *DSA_SIG_new(void) {
+  DSA_SIG *sig;
+  sig = OPENSSL_malloc(sizeof(DSA_SIG));
+  if (!sig) {
+    return NULL;
+  }
+  sig->r = NULL;
+  sig->s = NULL;
+  return sig;
+}
+
+void DSA_SIG_free(DSA_SIG *sig) {
+  if (!sig) {
+    return;
+  }
+
+  if (sig->r) {
+    BN_free(sig->r);
+  }
+  if (sig->s) {
+    BN_free(sig->s);
+  }
+  OPENSSL_free(sig);
+}
+
+DSA_SIG *DSA_do_sign(const uint8_t *digest, size_t digest_len, DSA *dsa) {
+  if (dsa->meth->sign) {
+    return dsa->meth->sign(digest, digest_len, dsa);
+  }
+  return DSA_default_method.sign(digest, digest_len, dsa);
+}
+
+int DSA_do_verify(const uint8_t *digest, size_t digest_len, DSA_SIG *sig,
+                  const DSA *dsa) {
+  int valid, ret;
+
+  if (dsa->meth->verify) {
+    ret = dsa->meth->verify(&valid, digest, digest_len, sig, dsa);
+  } else {
+    ret = DSA_default_method.verify(&valid, digest, digest_len, sig, dsa);
+  }
+
+  if (!ret) {
+    return -1;
+  } else if (!valid) {
+    return 0;
+  }
+  return 1;
+}
+
+int DSA_do_check_signature(int *out_valid, const uint8_t *digest,
+                           size_t digest_len, DSA_SIG *sig, const DSA *dsa) {
+  if (dsa->meth->verify) {
+    return dsa->meth->verify(out_valid, digest, digest_len, sig, dsa);
+  }
+
+  return DSA_default_method.verify(out_valid, digest, digest_len, sig, dsa);
+}
+
+int DSA_sign(int type, const uint8_t *digest, size_t digest_len,
+             uint8_t *out_sig, unsigned int *out_siglen, DSA *dsa) {
+  DSA_SIG *s;
+
+  s = DSA_do_sign(digest, digest_len, dsa);
+  if (s == NULL) {
+    *out_siglen = 0;
+    return 0;
+  }
+
+  *out_siglen = i2d_DSA_SIG(s, &out_sig);
+  DSA_SIG_free(s);
+  return 1;
+}
+
+int DSA_verify(int type, const uint8_t *digest, size_t digest_len,
+               const uint8_t *sig, size_t sig_len, const DSA *dsa) {
+  DSA_SIG *s = NULL;
+  int ret = -1, valid;
+
+  s = DSA_SIG_new();
+  if (s == NULL) {
+    goto err;
+  }
+
+  if (d2i_DSA_SIG(&s, &sig, sig_len) == NULL) {
+    goto err;
+  }
+
+  if (!DSA_do_check_signature(&valid, digest, digest_len, s, dsa)) {
+    goto err;
+  }
+
+  ret = valid;
+
+err:
+  if (s) {
+    DSA_SIG_free(s);
+  }
+  return ret;
+}
+
+int DSA_check_signature(int *out_valid, const uint8_t *digest,
+                        size_t digest_len, const uint8_t *sig, size_t sig_len,
+                        const DSA *dsa) {
+  DSA_SIG *s = NULL;
+  int ret = 0;
+
+  s = DSA_SIG_new();
+  if (s == NULL) {
+    goto err;
+  }
+
+  if (d2i_DSA_SIG(&s, &sig, sig_len) == NULL) {
+    goto err;
+  }
+
+  ret = DSA_do_check_signature(out_valid, digest, digest_len, s, dsa);
+
+err:
+  if (s) {
+    DSA_SIG_free(s);
+  }
+  return ret;
+}
+
+int DSA_size(const DSA *dsa) {
+  int ret, i;
+  ASN1_INTEGER bs;
+  unsigned char buf[4]; /* 4 bytes looks really small.
+                           However, i2d_ASN1_INTEGER() will not look
+                           beyond the first byte, as long as the second
+                           parameter is NULL. */
+
+  i = BN_num_bits(dsa->q);
+  bs.length = (i + 7) / 8;
+  bs.data = buf;
+  bs.type = V_ASN1_INTEGER;
+  /* If the top bit is set the asn1 encoding is 1 larger. */
+  buf[0] = 0xff;
+
+  i = i2d_ASN1_INTEGER(&bs, NULL);
+  i += i; /* r and s */
+  ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE);
+  return ret;
+}
+
+int DSA_sign_setup(const DSA *dsa, BN_CTX *ctx, BIGNUM **out_kinv,
+                   BIGNUM **out_r) {
+  if (dsa->meth->sign_setup) {
+    return dsa->meth->sign_setup(dsa, ctx, out_kinv, out_r);
+  }
+
+  return DSA_default_method.sign_setup(dsa, ctx, out_kinv, out_r);
+}
+
+int DSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                         CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
+  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_DSA, argl, argp, new_func,
+                                 dup_func, free_func);
+}
+
+int DSA_set_ex_data(DSA *d, int idx, void *arg) {
+  return CRYPTO_set_ex_data(&d->ex_data, idx, arg);
+}
+
+void *DSA_get_ex_data(const DSA *d, int idx) {
+  return CRYPTO_get_ex_data(&d->ex_data, idx);
+}
diff --git a/crypto/dsa/dsa.h b/crypto/dsa/dsa.h
new file mode 100644
index 0000000..857edc0
--- /dev/null
+++ b/crypto/dsa/dsa.h
@@ -0,0 +1,359 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ * The DSS routines are based on patches supplied by
+ * Steven Schoch <schoch@sheba.arc.nasa.gov>. */
+
+#ifndef OPENSSL_HEADER_DSA_H
+#define OPENSSL_HEADER_DSA_H
+
+#include <openssl/base.h>
+
+#include <openssl/engine.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* DSA contains functions for signing and verifing with the Digital Signature
+ * Algorithm. */
+
+
+/* Allocation and destruction. */
+
+/* DSA_new returns a new, empty DSA object or NULL on error. */
+DSA *DSA_new(void);
+
+/* DSA_new_method acts the same as |DH_new| but takes an explicit |ENGINE|. */
+DSA *DSA_new_method(const ENGINE *engine);
+
+/* DSA_free decrements the reference count of |dsa| and frees it if the
+ * reference count drops to zero. */
+void DSA_free(DSA *dsa);
+
+/* DSA_up_ref increments the reference count of |dsa|. */
+int DSA_up_ref(DSA *dsa);
+
+
+/* Parameter generation. */
+
+/* DSA_generate_parameters_ex generates a set of DSA parameters by following
+ * the procedure given in FIPS 186-4, appendix A.
+ * (http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf)
+ *
+ * The larger prime will have a length of |bits| (e.g. 2048). The |seed| value
+ * allows others to generate and verify the same parameters and should be
+ * random input which is kept for reference. If |out_counter| or |out_h| are
+ * not NULL then the counter and h value used in the generation are written to
+ * them.
+ *
+ * The |cb| argument is passed to |BN_generate_prime_ex| and is thus called
+ * during the generation process in order to indicate progress. See the
+ * comments for that function for details. In addition to the calls made by
+ * |BN_generate_prime_ex|, |DSA_generate_parameters_ex| will call it with
+ * |event| equal to 2 and 3 at different stages of the process.
+ *
+ * It returns one on success and zero otherwise. */
+int DSA_generate_parameters_ex(DSA *dsa, unsigned bits, const uint8_t *seed,
+                               size_t seed_len, int *out_counter,
+                               unsigned long *out_h, BN_GENCB *cb);
+
+/* DSAparams_dup returns a freshly allocated |DSA| that contains a copy of the
+ * parameters from |dsa|. It returns NULL on error. */
+DSA *DSAparams_dup(const DSA *dsa);
+
+
+/* Key generation. */
+
+/* DSA_generate_key generates a public/private key pair in |dsa|, which must
+ * already have parameters setup. It returns one on success and zero on
+ * error. */
+int DSA_generate_key(DSA *dsa);
+
+
+/* Signatures. */
+
+/* DSA_SIG contains a DSA signature as a pair of integers. */
+typedef struct DSA_SIG_st {
+  BIGNUM *r, *s;
+} DSA_SIG;
+
+/* DSA_SIG_new returns a freshly allocated, DIG_SIG structure or NULL on error.
+ * Both |r| and |s| in the signature will be NULL. */
+DSA_SIG *DSA_SIG_new(void);
+
+/* DSA_SIG_free frees the contents of |sig| and then frees |sig| itself. */
+void DSA_SIG_free(DSA_SIG *sig);
+
+/* DSA_do_sign returns a signature of the hash in |digest| by the key in |dsa|
+ * and returns an allocated, DSA_SIG structure, or NULL on error. */
+DSA_SIG *DSA_do_sign(const uint8_t *digest, size_t digest_len, DSA *dsa);
+
+/* DSA_do_verify verifies that |sig| is a valid signature, by the public key in
+ * |dsa|, of the hash in |digest|. It returns one if so, zero if invalid and -1
+ * on error.
+ *
+ * WARNING: do not use. This function returns -1 for error, 0 for invalid and 1
+ * for valid. However, this is dangerously different to the usual OpenSSL
+ * convention and could be a disaster if a user did |if (DSA_do_verify(...))|.
+ * Because of this, |DSA_check_signature| is a safer version of this.
+ *
+ * TODO(fork): deprecate. */
+int DSA_do_verify(const uint8_t *digest, size_t digest_len, DSA_SIG *sig,
+                  const DSA *dsa);
+
+/* DSA_check_signature sets |*out_valid| to zero. Then it verifies that |sig|
+ * is a valid signature, by the public key in |dsa| of the hash in |digest|
+ * and, if so, it sets |*out_valid| to one.
+ *
+ * It returns one if it was able to verify the signature as valid or invalid,
+ * and zero on error. */
+int DSA_do_check_signature(int *out_valid, const uint8_t *digest,
+                           size_t digest_len, DSA_SIG *sig, const DSA *dsa);
+
+
+/* ASN.1 signatures.
+ *
+ * These functions also perform DSA signature operations, but deal with ASN.1
+ * encoded signatures as opposed to raw |BIGNUM|s. If you don't know what
+ * encoding a DSA signature is in, it's probably ASN.1. */
+
+/* DSA_sign signs |digest| with the key in |dsa| and writes the resulting
+ * signature, in ASN.1 form, to |out_sig| and the length of the signature to
+ * |*out_siglen|. There must be, at least, |DSA_size(dsa)| bytes of space in
+ * |out_sig|. It returns one on success and zero otherwise.
+ *
+ * (The |type| argument is ignored.) */
+int DSA_sign(int type, const uint8_t *digest, size_t digest_len,
+             uint8_t *out_sig, unsigned int *out_siglen, DSA *dsa);
+
+/* DSA_verify verifies that |sig| is a valid, ASN.1 signature, by the public
+ * key in |dsa|, of the hash in |digest|. It returns one if so, zero if invalid
+ * and -1 on error.
+ *
+ * (The |type| argument is ignored.)
+ *
+ * WARNING: do not use. This function returns -1 for error, 0 for invalid and 1
+ * for valid. However, this is dangerously different to the usual OpenSSL
+ * convention and could be a disaster if a user did |if (DSA_do_verify(...))|.
+ * Because of this, |DSA_check_signature| is a safer version of this.
+ *
+ * TODO(fork): deprecate. */
+int DSA_verify(int type, const uint8_t *digest, size_t digest_len,
+               const uint8_t *sig, size_t sig_len, const DSA *dsa);
+
+/* DSA_check_signature sets |*out_valid| to zero. Then it verifies that |sig|
+ * is a valid, ASN.1 signature, by the public key in |dsa|, of the hash in
+ * |digest|. If so, it sets |*out_valid| to one.
+ *
+ * It returns one if it was able to verify the signature as valid or invalid,
+ * and zero on error. */
+int DSA_check_signature(int *out_valid, const uint8_t *digest,
+                        size_t digest_len, const uint8_t *sig, size_t sig_len,
+                        const DSA *dsa);
+
+/* DSA_size returns the size, in bytes, of an ASN.1 encoded, DSA signature
+ * generated by |dsa|. Parameters must already have been setup in |dsa|. */
+int DSA_size(const DSA *dsa);
+
+
+/* ASN.1 encoding. */
+
+/* d2i_DSA_SIG parses an ASN.1, DER-encoded, DSA signature from |len| bytes at
+ * |*inp|. If |out_sig| is not NULL then, on exit, a pointer to the result is
+ * in |*out_sig|. If |*out_sig| is already non-NULL on entry then the result is
+ * written directly into |*out_sig|, otherwise a fresh |DSA_SIG| is allocated.
+ * On successful exit, |*inp| is advanced past the DER structure. It returns
+ * the result or NULL on error. */
+DSA_SIG *d2i_DSA_SIG(DSA_SIG **out_sig, const uint8_t **inp, long len);
+
+/* i2d_DSA_SIG marshals |in| to an ASN.1, DER structure. If |outp| is not NULL
+ * then the result is written to |*outp| and |*outp| is advanced just past the
+ * output. It returns the number of bytes in the result, whether written or not,
+ * or a negative value on error. */
+int i2d_DSA_SIG(const DSA_SIG *in, uint8_t **outp);
+
+/* d2i_DSAPublicKey parses an ASN.1, DER-encoded, DSA public key from |len|
+ * bytes at |*inp|. If |out| is not NULL then, on exit, a pointer to the result
+ * is in |*out|. If |*out| is already non-NULL on entry then the result is
+ * written directly into |*out|, otherwise a fresh |DSA| is allocated. On
+ * successful exit, |*inp| is advanced past the DER structure. It returns the
+ * result or NULL on error. */
+DSA *d2i_DSAPublicKey(DSA **out, const uint8_t **inp, long len);
+
+/* i2d_DSAPublicKey marshals a public key from |in| to an ASN.1, DER structure.
+ * If |outp| is not NULL then the result is written to |*outp| and |*outp| is
+ * advanced just past the output. It returns the number of bytes in the result,
+ * whether written or not, or a negative value on error. */
+int i2d_DSAPublicKey(const DSA *in, unsigned char **outp);
+
+/* d2i_DSAPrivateKey parses an ASN.1, DER-encoded, DSA private key from |len|
+ * bytes at |*inp|. If |out| is not NULL then, on exit, a pointer to the result
+ * is in |*out|. If |*out| is already non-NULL on entry then the result is
+ * written directly into |*out|, otherwise a fresh |DSA| is allocated. On
+ * successful exit, |*inp| is advanced past the DER structure. It returns the
+ * result or NULL on error. */
+DSA *d2i_DSAPrivateKey(DSA **out, const uint8_t **inp, long len);
+
+/* i2d_DSAPrivateKey marshals a private key from |in| to an ASN.1, DER structure.
+ * If |outp| is not NULL then the result is written to |*outp| and |*outp| is
+ * advanced just past the output. It returns the number of bytes in the result,
+ * whether written or not, or a negative value on error. */
+int i2d_DSAPrivateKey(const DSA *in, unsigned char **outp);
+
+/* d2i_DSAparams parses ASN.1, DER-encoded, DSA parameters from |len| bytes at
+ * |*inp|. If |out| is not NULL then, on exit, a pointer to the result is in
+ * |*out|. If |*out| is already non-NULL on entry then the result is written
+ * directly into |*out|, otherwise a fresh |DSA| is allocated. On successful
+ * exit, |*inp| is advanced past the DER structure. It returns the result or
+ * NULL on error. */
+DSA *d2i_DSAparams(DSA **out, const uint8_t **inp, long len);
+
+/* i2d_DSAparams marshals DSA parameters from |in| to an ASN.1, DER structure.
+ * If |outp| is not NULL then the result is written to |*outp| and |*outp| is
+ * advanced just past the output. It returns the number of bytes in the result,
+ * whether written or not, or a negative value on error. */
+int i2d_DSAparams(const DSA *in, unsigned char **outp);
+
+
+/* Precomputation. */
+
+/* DSA_sign_setup precomputes the message independent part of the DSA signature
+ * and writes them to |*out_kinv| and |*out_r|. Returns one on success, zero on
+ * error.
+ *
+ * TODO(fork): decide what to do with this. Since making DSA* opaque there's no
+ * way for the user to install them. Also, it forces the DSA* not to be const
+ * when passing to the signing function. */
+int DSA_sign_setup(const DSA *dsa, BN_CTX *ctx, BIGNUM **out_kinv,
+                   BIGNUM **out_r);
+
+
+/* ex_data functions.
+ *
+ * These functions are wrappers. See |ex_data.h| for details. */
+
+int DSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                         CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int DSA_set_ex_data(DSA *d, int idx, void *arg);
+void *DSA_get_ex_data(const DSA *d, int idx);
+
+
+struct dsa_method {
+  struct openssl_method_common_st common;
+
+  void *app_data;
+
+  int (*init)(DSA *dsa);
+  int (*finish)(DSA *dsa);
+
+  DSA_SIG *(*sign)(const uint8_t *digest, size_t digest_len, DSA *dsa);
+
+  int (*sign_setup)(const DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp);
+
+  int (*verify)(int *out_valid, const uint8_t *digest, size_t digest_len,
+                DSA_SIG *sig, const DSA *dsa);
+
+  /* generate_parameters, if non-NULL, is used to generate DSA parameters. */
+  int (*generate_parameters)(DSA *dsa, unsigned bits, const uint8_t *seed,
+                             size_t seed_len, int *counter_ret,
+                             unsigned long *h_ret, BN_GENCB *cb);
+
+  /* keygen, if non-NULL, is used to generate DSA keys. */
+  int (*keygen)(DSA *dsa);
+};
+
+struct dsa_st {
+  long version;
+  int write_params;
+  BIGNUM *p;
+  BIGNUM *q; /* == 20 */
+  BIGNUM *g;
+
+  BIGNUM *pub_key;  /* y public key */
+  BIGNUM *priv_key; /* x private key */
+
+  BIGNUM *kinv; /* Signing pre-calc */
+  BIGNUM *r;    /* Signing pre-calc */
+
+  int flags;
+  /* Normally used to cache montgomery values */
+  BN_MONT_CTX *method_mont_p;
+  int references;
+  CRYPTO_EX_DATA ex_data;
+  DSA_METHOD *meth;
+  /* functional reference if 'meth' is ENGINE-provided */
+  ENGINE *engine;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define DSA_F_sign 100
+#define DSA_F_verify 101
+#define DSA_F_dsa_sig_cb 102
+#define DSA_F_DSA_new_method 103
+#define DSA_F_sign_setup 104
+#define DSA_R_NEED_NEW_SETUP_VALUES 100
+#define DSA_R_BAD_Q_VALUE 101
+#define DSA_R_MODULUS_TOO_LARGE 102
+#define DSA_R_MISSING_PARAMETERS 103
+
+#endif  /* OPENSSL_HEADER_DSA_H */
diff --git a/crypto/dsa/dsa_asn1.c b/crypto/dsa/dsa_asn1.c
new file mode 100644
index 0000000..72b2cc6
--- /dev/null
+++ b/crypto/dsa/dsa_asn1.c
@@ -0,0 +1,148 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2000. */
+/* ====================================================================
+ * Copyright (c) 2000-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/dsa.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+static int dsa_sig_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                      void *exarg) {
+  if (operation != ASN1_OP_NEW_PRE) {
+    return 1;
+  }
+
+  DSA_SIG *sig;
+  sig = OPENSSL_malloc(sizeof(DSA_SIG));
+  if (!sig) {
+    OPENSSL_PUT_ERROR(DSA, dsa_sig_cb, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  memset(sig, 0, sizeof(DSA_SIG));
+  *pval = (ASN1_VALUE *)sig;
+  return 2;
+}
+
+ASN1_SEQUENCE_cb(DSA_SIG, dsa_sig_cb) = {
+    ASN1_SIMPLE(DSA_SIG, r, CBIGNUM),
+    ASN1_SIMPLE(DSA_SIG, s, CBIGNUM)} ASN1_SEQUENCE_END_cb(DSA_SIG, DSA_SIG);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA_SIG, DSA_SIG, DSA_SIG);
+
+
+static int dsa_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                  void *exarg) {
+  switch (operation) {
+    case ASN1_OP_NEW_PRE:
+      *pval = (ASN1_VALUE *)DSA_new();
+      if (*pval) {
+        return 2;
+      }
+      return 0;
+
+    case ASN1_OP_FREE_PRE:
+      DSA_free((DSA *)*pval);
+      *pval = NULL;
+      return 2;
+
+    default:
+      return 1;
+  }
+}
+
+ASN1_SEQUENCE_cb(DSAPrivateKey, dsa_cb) = {
+    ASN1_SIMPLE(DSA, version, LONG),
+    ASN1_SIMPLE(DSA, p, BIGNUM),
+    ASN1_SIMPLE(DSA, q, BIGNUM),
+    ASN1_SIMPLE(DSA, g, BIGNUM),
+    ASN1_SIMPLE(DSA, pub_key, BIGNUM),
+    ASN1_SIMPLE(DSA, priv_key, BIGNUM)} ASN1_SEQUENCE_END_cb(DSA,
+                                                             DSAPrivateKey);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAPrivateKey, DSAPrivateKey);
+
+ASN1_SEQUENCE_cb(DSAparams, dsa_cb) = {
+    ASN1_SIMPLE(DSA, p, BIGNUM), ASN1_SIMPLE(DSA, q, BIGNUM),
+    ASN1_SIMPLE(DSA, g, BIGNUM), } ASN1_SEQUENCE_END_cb(DSA, DSAparams);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAparams, DSAparams);
+
+
+/* DSA public key is a bit trickier... its effectively a CHOICE type decided by
+ * a field called write_params which can either write out just the public key
+ * as an INTEGER or the parameters and public key in a SEQUENCE. */
+
+ASN1_SEQUENCE(dsa_pub_internal) = {
+	ASN1_SIMPLE(DSA, pub_key, BIGNUM),
+	ASN1_SIMPLE(DSA, p, BIGNUM),
+	ASN1_SIMPLE(DSA, q, BIGNUM),
+	ASN1_SIMPLE(DSA, g, BIGNUM)
+} ASN1_SEQUENCE_END_name(DSA, dsa_pub_internal);
+
+ASN1_CHOICE_cb(DSAPublicKey, dsa_cb) = {
+	ASN1_SIMPLE(DSA, pub_key, BIGNUM),
+	ASN1_EX_COMBINE(0, 0, dsa_pub_internal)
+} ASN1_CHOICE_END_cb(DSA, DSAPublicKey, write_params);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAPublicKey, DSAPublicKey);
+
+DSA *DSAparams_dup(const DSA *dsa) {
+  return ASN1_item_dup(ASN1_ITEM_rptr(DSAparams), (DSA*) dsa);
+}
diff --git a/crypto/dsa/dsa_error.c b/crypto/dsa/dsa_error.c
new file mode 100644
index 0000000..b6e08ae
--- /dev/null
+++ b/crypto/dsa/dsa_error.c
@@ -0,0 +1,30 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "dsa.h"
+
+const ERR_STRING_DATA DSA_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_DSA, DSA_F_DSA_new_method, 0), "DSA_new_method"},
+  {ERR_PACK(ERR_LIB_DSA, DSA_F_dsa_sig_cb, 0), "dsa_sig_cb"},
+  {ERR_PACK(ERR_LIB_DSA, DSA_F_sign, 0), "sign"},
+  {ERR_PACK(ERR_LIB_DSA, DSA_F_sign_setup, 0), "sign_setup"},
+  {ERR_PACK(ERR_LIB_DSA, DSA_F_verify, 0), "verify"},
+  {ERR_PACK(ERR_LIB_DSA, 0, DSA_R_BAD_Q_VALUE), "BAD_Q_VALUE"},
+  {ERR_PACK(ERR_LIB_DSA, 0, DSA_R_MISSING_PARAMETERS), "MISSING_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_DSA, 0, DSA_R_MODULUS_TOO_LARGE), "MODULUS_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_DSA, 0, DSA_R_NEED_NEW_SETUP_VALUES), "NEED_NEW_SETUP_VALUES"},
+  {0, NULL},
+};
diff --git a/crypto/dsa/dsa_impl.c b/crypto/dsa/dsa_impl.c
new file mode 100644
index 0000000..dacc742
--- /dev/null
+++ b/crypto/dsa/dsa_impl.c
@@ -0,0 +1,723 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ * The DSS routines are based on patches supplied by
+ * Steven Schoch <schoch@sheba.arc.nasa.gov>. */
+
+#include <openssl/dsa.h>
+
+#include <openssl/bn.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include "internal.h"
+
+#define OPENSSL_DSA_MAX_MODULUS_BITS 10000
+
+/* Primality test according to FIPS PUB 186[-1], Appendix 2.1: 50 rounds of
+ * Rabin-Miller */
+#define DSS_prime_checks 50
+
+static int sign_setup(const DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp,
+                      BIGNUM **rp) {
+  BN_CTX *ctx;
+  BIGNUM k, kq, *K, *kinv = NULL, *r = NULL;
+  int ret = 0;
+
+  if (!dsa->p || !dsa->q || !dsa->g) {
+    OPENSSL_PUT_ERROR(DSA, sign_setup, DSA_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  BN_init(&k);
+  BN_init(&kq);
+
+  ctx = ctx_in;
+  if (ctx == NULL) {
+    ctx = BN_CTX_new();
+    if (ctx == NULL) {
+      goto err;
+    }
+  }
+
+  r = BN_new();
+  if (r == NULL) {
+    goto err;
+  }
+
+  /* Get random k */
+  do {
+    if (!BN_rand_range(&k, dsa->q)) {
+      goto err;
+    }
+  } while (BN_is_zero(&k));
+
+  BN_set_flags(&k, BN_FLG_CONSTTIME);
+
+  if (!BN_MONT_CTX_set_locked((BN_MONT_CTX **)&dsa->method_mont_p,
+                              CRYPTO_LOCK_DSA, dsa->p, ctx)) {
+    goto err;
+  }
+
+  /* Compute r = (g^k mod p) mod q */
+  if (!BN_copy(&kq, &k))
+    goto err;
+
+  /* We do not want timing information to leak the length of k,
+   * so we compute g^k using an equivalent exponent of fixed length.
+   *
+   * (This is a kludge that we need because the BN_mod_exp_mont()
+   * does not let us specify the desired timing behaviour.) */
+
+  if (!BN_add(&kq, &kq, dsa->q))
+    goto err;
+  if (BN_num_bits(&kq) <= BN_num_bits(dsa->q)) {
+    if (!BN_add(&kq, &kq, dsa->q))
+      goto err;
+  }
+
+  K = &kq;
+
+  if (!BN_mod_exp_mont(r, dsa->g, K, dsa->p, ctx, dsa->method_mont_p)) {
+    goto err;
+  }
+  if (!BN_mod(r, r, dsa->q, ctx)) {
+    goto err;
+  }
+
+  /* Compute  part of 's = inv(k) (m + xr) mod q' */
+  kinv = BN_mod_inverse(NULL, &k, dsa->q, ctx);
+  if (kinv == NULL) {
+    goto err;
+  }
+
+  if (*kinvp != NULL) {
+    BN_clear_free(*kinvp);
+  }
+  *kinvp = kinv;
+  kinv = NULL;
+  if (*rp != NULL) {
+    BN_clear_free(*rp);
+  }
+  *rp = r;
+  ret = 1;
+
+err:
+  if (!ret) {
+    OPENSSL_PUT_ERROR(DSA, sign_setup, ERR_R_BN_LIB);
+    if (r != NULL) {
+      BN_clear_free(r);
+    }
+  }
+
+  if (ctx_in == NULL) {
+    BN_CTX_free(ctx);
+  }
+  BN_clear_free(&k);
+  BN_clear_free(&kq);
+  return ret;
+}
+
+static DSA_SIG *sign(const uint8_t *digest, size_t digest_len, DSA *dsa) {
+  BIGNUM *kinv = NULL, *r = NULL, *s = NULL;
+  BIGNUM m;
+  BIGNUM xr;
+  BN_CTX *ctx = NULL;
+  int reason = ERR_R_BN_LIB;
+  DSA_SIG *ret = NULL;
+  int noredo = 0;
+
+  BN_init(&m);
+  BN_init(&xr);
+
+  if (!dsa->p || !dsa->q || !dsa->g) {
+    reason = DSA_R_MISSING_PARAMETERS;
+    goto err;
+  }
+
+  s = BN_new();
+  if (s == NULL) {
+    goto err;
+  }
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+redo:
+  if (dsa->kinv == NULL || dsa->r == NULL) {
+    if (!DSA_sign_setup(dsa, ctx, &kinv, &r)) {
+      goto err;
+    }
+  } else {
+    kinv = dsa->kinv;
+    dsa->kinv = NULL;
+    r = dsa->r;
+    dsa->r = NULL;
+    noredo = 1;
+  }
+
+  if (digest_len > BN_num_bytes(dsa->q)) {
+    /* if the digest length is greater than the size of q use the
+     * BN_num_bits(dsa->q) leftmost bits of the digest, see
+     * fips 186-3, 4.2 */
+    digest_len = BN_num_bytes(dsa->q);
+  }
+
+  if (BN_bin2bn(digest, digest_len, &m) == NULL) {
+    goto err;
+  }
+
+  /* Compute  s = inv(k) (m + xr) mod q */
+  if (!BN_mod_mul(&xr, dsa->priv_key, r, dsa->q, ctx)) {
+    goto err; /* s = xr */
+  }
+  if (!BN_add(s, &xr, &m)) {
+    goto err; /* s = m + xr */
+  }
+  if (BN_cmp(s, dsa->q) > 0) {
+    if (!BN_sub(s, s, dsa->q)) {
+      goto err;
+    }
+  }
+  if (!BN_mod_mul(s, s, kinv, dsa->q, ctx)) {
+    goto err;
+  }
+
+  ret = DSA_SIG_new();
+  if (ret == NULL) {
+    goto err;
+  }
+  /* Redo if r or s is zero as required by FIPS 186-3: this is
+   * very unlikely. */
+  if (BN_is_zero(r) || BN_is_zero(s)) {
+    if (noredo) {
+      reason = DSA_R_NEED_NEW_SETUP_VALUES;
+      goto err;
+    }
+    goto redo;
+  }
+  ret->r = r;
+  ret->s = s;
+
+err:
+  if (!ret) {
+    OPENSSL_PUT_ERROR(DSA, sign, reason);
+    BN_free(r);
+    BN_free(s);
+  }
+  if (ctx != NULL) {
+    BN_CTX_free(ctx);
+  }
+  BN_clear_free(&m);
+  BN_clear_free(&xr);
+  if (kinv != NULL) {
+    /* dsa->kinv is NULL now if we used it */
+    BN_clear_free(kinv);
+  }
+
+  return ret;
+}
+
+static int verify(int *out_valid, const uint8_t *dgst, size_t digest_len,
+                  DSA_SIG *sig, const DSA *dsa) {
+  BN_CTX *ctx;
+  BIGNUM u1, u2, t1;
+  BN_MONT_CTX *mont = NULL;
+  int ret = 0;
+  unsigned i;
+
+  *out_valid = 0;
+
+  if (!dsa->p || !dsa->q || !dsa->g) {
+    OPENSSL_PUT_ERROR(DSA, verify, DSA_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  i = BN_num_bits(dsa->q);
+  /* fips 186-3 allows only different sizes for q */
+  if (i != 160 && i != 224 && i != 256) {
+    OPENSSL_PUT_ERROR(DSA, verify, DSA_R_BAD_Q_VALUE);
+    return 0;
+  }
+
+  if (BN_num_bits(dsa->p) > OPENSSL_DSA_MAX_MODULUS_BITS) {
+    OPENSSL_PUT_ERROR(DSA, verify, DSA_R_MODULUS_TOO_LARGE);
+    return 0;
+  }
+
+  BN_init(&u1);
+  BN_init(&u2);
+  BN_init(&t1);
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+  if (BN_is_zero(sig->r) || BN_is_negative(sig->r) ||
+      BN_ucmp(sig->r, dsa->q) >= 0) {
+    ret = 1;
+    goto err;
+  }
+  if (BN_is_zero(sig->s) || BN_is_negative(sig->s) ||
+      BN_ucmp(sig->s, dsa->q) >= 0) {
+    ret = 1;
+    goto err;
+  }
+
+  /* Calculate W = inv(S) mod Q
+   * save W in u2 */
+  if (BN_mod_inverse(&u2, sig->s, dsa->q, ctx) == NULL) {
+    goto err;
+  }
+
+  /* save M in u1 */
+  if (digest_len > (i >> 3)) {
+    /* if the digest length is greater than the size of q use the
+     * BN_num_bits(dsa->q) leftmost bits of the digest, see
+     * fips 186-3, 4.2 */
+    digest_len = (i >> 3);
+  }
+
+  if (BN_bin2bn(dgst, digest_len, &u1) == NULL) {
+    goto err;
+  }
+
+  /* u1 = M * w mod q */
+  if (!BN_mod_mul(&u1, &u1, &u2, dsa->q, ctx)) {
+    goto err;
+  }
+
+  /* u2 = r * w mod q */
+  if (!BN_mod_mul(&u2, sig->r, &u2, dsa->q, ctx)) {
+    goto err;
+  }
+
+  mont = BN_MONT_CTX_set_locked((BN_MONT_CTX **)&dsa->method_mont_p,
+                                CRYPTO_LOCK_DSA, dsa->p, ctx);
+  if (!mont) {
+    goto err;
+  }
+
+  if (!BN_mod_exp2_mont(&t1, dsa->g, &u1, dsa->pub_key, &u2, dsa->p, ctx, mont)) {
+    goto err;
+  }
+
+  /* BN_copy(&u1,&t1); */
+  /* let u1 = u1 mod q */
+  if (!BN_mod(&u1, &t1, dsa->q, ctx)) {
+    goto err;
+  }
+
+  /* V is now in u1.  If the signature is correct, it will be
+   * equal to R. */
+  *out_valid = BN_ucmp(&u1, sig->r) == 0;
+  ret = 1;
+
+err:
+  if (ret != 1) {
+    OPENSSL_PUT_ERROR(DSA, verify, ERR_R_BN_LIB);
+  }
+  if (ctx != NULL) {
+    BN_CTX_free(ctx);
+  }
+  BN_free(&u1);
+  BN_free(&u2);
+  BN_free(&t1);
+
+  return ret;
+}
+
+static int keygen(DSA *dsa) {
+  int ok = 0;
+  BN_CTX *ctx = NULL;
+  BIGNUM *pub_key = NULL, *priv_key = NULL;
+  BIGNUM prk;
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+  priv_key = dsa->priv_key;
+  if (priv_key == NULL) {
+    priv_key = BN_new();
+    if (priv_key == NULL) {
+      goto err;
+    }
+  }
+
+  do {
+    if (!BN_rand_range(priv_key, dsa->q)) {
+      goto err;
+    }
+  } while (BN_is_zero(priv_key));
+
+  pub_key = dsa->pub_key;
+  if (pub_key == NULL) {
+    pub_key = BN_new();
+    if (pub_key == NULL) {
+      goto err;
+    }
+  }
+
+  BN_init(&prk);
+  BN_with_flags(&prk, priv_key, BN_FLG_CONSTTIME);
+
+  if (!BN_mod_exp(pub_key, dsa->g, &prk, dsa->p, ctx)) {
+    goto err;
+  }
+
+  dsa->priv_key = priv_key;
+  dsa->pub_key = pub_key;
+  ok = 1;
+
+err:
+  if (pub_key != NULL && dsa->pub_key == NULL) {
+    BN_free(pub_key);
+  }
+  if (priv_key != NULL && dsa->priv_key == NULL) {
+    BN_free(priv_key);
+  }
+  if (ctx != NULL) {
+    BN_CTX_free(ctx);
+  }
+
+  return ok;
+}
+
+static int paramgen(DSA *ret, unsigned bits, const uint8_t *seed_in,
+                    size_t seed_len, int *counter_ret, unsigned long *h_ret,
+                    BN_GENCB *cb) {
+  int ok = 0;
+  unsigned char seed[SHA256_DIGEST_LENGTH];
+  unsigned char md[SHA256_DIGEST_LENGTH];
+  unsigned char buf[SHA256_DIGEST_LENGTH], buf2[SHA256_DIGEST_LENGTH];
+  BIGNUM *r0, *W, *X, *c, *test;
+  BIGNUM *g = NULL, *q = NULL, *p = NULL;
+  BN_MONT_CTX *mont = NULL;
+  int k, n = 0, m = 0;
+  unsigned i;
+  int counter = 0;
+  int r = 0;
+  BN_CTX *ctx = NULL;
+  unsigned int h = 2;
+  unsigned qbits, qsize;
+  const EVP_MD *evpmd;
+
+  if (bits >= 2048) {
+    qbits = 256;
+    evpmd = EVP_sha256();
+  } else {
+    qbits = 160;
+    evpmd = EVP_sha1();
+  }
+  qsize = qbits / 8;
+
+  if (qsize != SHA_DIGEST_LENGTH && qsize != SHA224_DIGEST_LENGTH &&
+      qsize != SHA256_DIGEST_LENGTH)
+    /* invalid q size */
+    return 0;
+
+  if (bits < 512)
+    bits = 512;
+
+  bits = (bits + 63) / 64 * 64;
+
+  /* NB: seed_len == 0 is special case: copy generated seed to
+   * seed_in if it is not NULL. */
+  if (seed_len && (seed_len < (size_t)qsize))
+    seed_in = NULL; /* seed buffer too small -- ignore */
+  if (seed_len > (size_t)qsize)
+    seed_len = qsize; /* App. 2.2 of FIPS PUB 186 allows larger SEED,
+                       * but our internal buffers are restricted to 160 bits*/
+  if (seed_in != NULL)
+    memcpy(seed, seed_in, seed_len);
+
+  if ((ctx = BN_CTX_new()) == NULL)
+    goto err;
+
+  if ((mont = BN_MONT_CTX_new()) == NULL)
+    goto err;
+
+  BN_CTX_start(ctx);
+  r0 = BN_CTX_get(ctx);
+  g = BN_CTX_get(ctx);
+  W = BN_CTX_get(ctx);
+  q = BN_CTX_get(ctx);
+  X = BN_CTX_get(ctx);
+  c = BN_CTX_get(ctx);
+  p = BN_CTX_get(ctx);
+  test = BN_CTX_get(ctx);
+
+  if (!BN_lshift(test, BN_value_one(), bits - 1))
+    goto err;
+
+  for (;;) {
+    for (;;) /* find q */
+    {
+      int seed_is_random;
+
+      /* step 1 */
+      if (!BN_GENCB_call(cb, 0, m++))
+        goto err;
+
+      if (!seed_len) {
+        RAND_pseudo_bytes(seed, qsize);
+        seed_is_random = 1;
+      } else {
+        seed_is_random = 0;
+        seed_len = 0; /* use random seed if 'seed_in' turns out to be bad*/
+      }
+      memcpy(buf, seed, qsize);
+      memcpy(buf2, seed, qsize);
+      /* precompute "SEED + 1" for step 7: */
+      for (i = qsize - 1; i < qsize; i--) {
+        buf[i]++;
+        if (buf[i] != 0)
+          break;
+      }
+
+      /* step 2 */
+      if (!EVP_Digest(seed, qsize, md, NULL, evpmd, NULL))
+        goto err;
+      if (!EVP_Digest(buf, qsize, buf2, NULL, evpmd, NULL))
+        goto err;
+      for (i = 0; i < qsize; i++)
+        md[i] ^= buf2[i];
+
+      /* step 3 */
+      md[0] |= 0x80;
+      md[qsize - 1] |= 0x01;
+      if (!BN_bin2bn(md, qsize, q))
+        goto err;
+
+      /* step 4 */
+      r = BN_is_prime_fasttest_ex(q, DSS_prime_checks, ctx, seed_is_random, cb);
+      if (r > 0)
+        break;
+      if (r != 0)
+        goto err;
+
+      /* do a callback call */
+      /* step 5 */
+    }
+
+    if (!BN_GENCB_call(cb, 2, 0))
+      goto err;
+    if (!BN_GENCB_call(cb, 3, 0))
+      goto err;
+
+    /* step 6 */
+    counter = 0;
+    /* "offset = 2" */
+
+    n = (bits - 1) / 160;
+
+    for (;;) {
+      if ((counter != 0) && !BN_GENCB_call(cb, 0, counter))
+        goto err;
+
+      /* step 7 */
+      BN_zero(W);
+      /* now 'buf' contains "SEED + offset - 1" */
+      for (k = 0; k <= n; k++) {
+        /* obtain "SEED + offset + k" by incrementing: */
+        for (i = qsize - 1; i < qsize; i--) {
+          buf[i]++;
+          if (buf[i] != 0)
+            break;
+        }
+
+        if (!EVP_Digest(buf, qsize, md, NULL, evpmd, NULL))
+          goto err;
+
+        /* step 8 */
+        if (!BN_bin2bn(md, qsize, r0))
+          goto err;
+        if (!BN_lshift(r0, r0, (qsize << 3) * k))
+          goto err;
+        if (!BN_add(W, W, r0))
+          goto err;
+      }
+
+      /* more of step 8 */
+      if (!BN_mask_bits(W, bits - 1))
+        goto err;
+      if (!BN_copy(X, W))
+        goto err;
+      if (!BN_add(X, X, test))
+        goto err;
+
+      /* step 9 */
+      if (!BN_lshift1(r0, q))
+        goto err;
+      if (!BN_mod(c, X, r0, ctx))
+        goto err;
+      if (!BN_sub(r0, c, BN_value_one()))
+        goto err;
+      if (!BN_sub(p, X, r0))
+        goto err;
+
+      /* step 10 */
+      if (BN_cmp(p, test) >= 0) {
+        /* step 11 */
+        r = BN_is_prime_fasttest_ex(p, DSS_prime_checks, ctx, 1, cb);
+        if (r > 0)
+          goto end; /* found it */
+        if (r != 0)
+          goto err;
+      }
+
+      /* step 13 */
+      counter++;
+      /* "offset = offset + n + 1" */
+
+      /* step 14 */
+      if (counter >= 4096)
+        break;
+    }
+  }
+end:
+  if (!BN_GENCB_call(cb, 2, 1))
+    goto err;
+
+  /* We now need to generate g */
+  /* Set r0=(p-1)/q */
+  if (!BN_sub(test, p, BN_value_one()))
+    goto err;
+  if (!BN_div(r0, NULL, test, q, ctx))
+    goto err;
+
+  if (!BN_set_word(test, h))
+    goto err;
+  if (!BN_MONT_CTX_set(mont, p, ctx))
+    goto err;
+
+  for (;;) {
+    /* g=test^r0%p */
+    if (!BN_mod_exp_mont(g, test, r0, p, ctx, mont))
+      goto err;
+    if (!BN_is_one(g))
+      break;
+    if (!BN_add(test, test, BN_value_one()))
+      goto err;
+    h++;
+  }
+
+  if (!BN_GENCB_call(cb, 3, 1))
+    goto err;
+
+  ok = 1;
+
+err:
+  if (ok) {
+    if (ret->p)
+      BN_free(ret->p);
+    if (ret->q)
+      BN_free(ret->q);
+    if (ret->g)
+      BN_free(ret->g);
+    ret->p = BN_dup(p);
+    ret->q = BN_dup(q);
+    ret->g = BN_dup(g);
+    if (ret->p == NULL || ret->q == NULL || ret->g == NULL) {
+      ok = 0;
+      goto err;
+    }
+    if (counter_ret != NULL)
+      *counter_ret = counter;
+    if (h_ret != NULL)
+      *h_ret = h;
+  }
+
+  if (ctx) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+
+  if (mont != NULL)
+    BN_MONT_CTX_free(mont);
+
+  return ok;
+}
+
+static int finish(DSA *dsa) {
+  BN_MONT_CTX_free(dsa->method_mont_p);
+  dsa->method_mont_p = NULL;
+  return 1;
+}
+
+const struct dsa_method DSA_default_method = {
+  {
+    0 /* references */,
+    1 /* is_static */,
+  },
+  NULL /* app_data */,
+
+  NULL /* init */,
+  finish /* finish */,
+
+  sign,
+  sign_setup,
+  verify,
+
+  paramgen,
+  keygen,
+};
diff --git a/crypto/dsa/dsa_test.c b/crypto/dsa/dsa_test.c
new file mode 100644
index 0000000..8841c12
--- /dev/null
+++ b/crypto/dsa/dsa_test.c
@@ -0,0 +1,214 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ * The DSS routines are based on patches supplied by
+ * Steven Schoch <schoch@sheba.arc.nasa.gov>. */
+
+#include <openssl/dsa.h>
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+
+#include "internal.h"
+
+
+static int dsa_cb(int p, int n, BN_GENCB *arg);
+
+/* seed, out_p, out_q, out_g are taken from the updated Appendix 5 to
+ * FIPS PUB 186 and also appear in Appendix 5 to FIPS PIB 186-1 */
+static unsigned char seed[20] = {0xd5, 0x01, 0x4e, 0x4b, 0x60, 0xef, 0x2b,
+                                 0xa8, 0xb6, 0x21, 0x1b, 0x40, 0x62, 0xba,
+                                 0x32, 0x24, 0xe0, 0x42, 0x7d, 0xd3, };
+
+static unsigned char out_p[] = {
+    0x8d, 0xf2, 0xa4, 0x94, 0x49, 0x22, 0x76, 0xaa, 0x3d, 0x25, 0x75,
+    0x9b, 0xb0, 0x68, 0x69, 0xcb, 0xea, 0xc0, 0xd8, 0x3a, 0xfb, 0x8d,
+    0x0c, 0xf7, 0xcb, 0xb8, 0x32, 0x4f, 0x0d, 0x78, 0x82, 0xe5, 0xd0,
+    0x76, 0x2f, 0xc5, 0xb7, 0x21, 0x0e, 0xaf, 0xc2, 0xe9, 0xad, 0xac,
+    0x32, 0xab, 0x7a, 0xac, 0x49, 0x69, 0x3d, 0xfb, 0xf8, 0x37, 0x24,
+    0xc2, 0xec, 0x07, 0x36, 0xee, 0x31, 0xc8, 0x02, 0x91, };
+
+static unsigned char out_q[] = {0xc7, 0x73, 0x21, 0x8c, 0x73, 0x7e, 0xc8,
+                                0xee, 0x99, 0x3b, 0x4f, 0x2d, 0xed, 0x30,
+                                0xf4, 0x8e, 0xda, 0xce, 0x91, 0x5f, };
+
+static unsigned char out_g[] = {
+    0x62, 0x6d, 0x02, 0x78, 0x39, 0xea, 0x0a, 0x13, 0x41, 0x31, 0x63,
+    0xa5, 0x5b, 0x4c, 0xb5, 0x00, 0x29, 0x9d, 0x55, 0x22, 0x95, 0x6c,
+    0xef, 0xcb, 0x3b, 0xff, 0x10, 0xf3, 0x99, 0xce, 0x2c, 0x2e, 0x71,
+    0xcb, 0x9d, 0xe5, 0xfa, 0x24, 0xba, 0xbf, 0x58, 0xe5, 0xb7, 0x95,
+    0x21, 0x92, 0x5c, 0x9c, 0xc4, 0x2e, 0x9f, 0x6f, 0x46, 0x4b, 0x08,
+    0x8c, 0xc5, 0x72, 0xaf, 0x53, 0xe6, 0xd7, 0x88, 0x02, };
+
+static const uint8_t str1[]="12345678901234567890";
+
+static BIO *bio_err = NULL;
+static BIO *bio_out = NULL;
+
+int main(int argc, char **argv) {
+  BN_GENCB cb;
+  DSA *dsa = NULL;
+  int counter, ok = 0, i, j;
+  unsigned char buf[256];
+  unsigned long h;
+  unsigned char sig[256];
+  unsigned int siglen;
+
+  bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
+  bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
+
+  BIO_printf(bio_out, "test generation of DSA parameters\n");
+
+  BN_GENCB_set(&cb, dsa_cb, bio_out);
+  dsa = DSA_new();
+  if (dsa == NULL ||
+      !DSA_generate_parameters_ex(dsa, 512, seed, 20, &counter, &h, &cb)) {
+    goto end;
+  }
+
+  BIO_printf(bio_out, "seed\n");
+  for (i = 0; i < 20; i += 4) {
+    BIO_printf(bio_out, "%02X%02X%02X%02X ", seed[i], seed[i + 1], seed[i + 2],
+               seed[i + 3]);
+  }
+  BIO_printf(bio_out, "\ncounter=%d h=%ld\n", counter, h);
+
+  if (counter != 105) {
+    BIO_printf(bio_err, "counter should be 105\n");
+    goto end;
+  }
+  if (h != 2) {
+    BIO_printf(bio_err, "h should be 2\n");
+    goto end;
+  }
+
+  i = BN_bn2bin(dsa->q, buf);
+  j = sizeof(out_q);
+  if (i != j || memcmp(buf, out_q, i) != 0) {
+    BIO_printf(bio_err, "q value is wrong\n");
+    goto end;
+  }
+
+  i = BN_bn2bin(dsa->p, buf);
+  j = sizeof(out_p);
+  if (i != j || memcmp(buf, out_p, i) != 0) {
+    BIO_printf(bio_err, "p value is wrong\n");
+    goto end;
+  }
+
+  i = BN_bn2bin(dsa->g, buf);
+  j = sizeof(out_g);
+  if (i != j || memcmp(buf, out_g, i) != 0) {
+    BIO_printf(bio_err, "g value is wrong\n");
+    goto end;
+  }
+
+  DSA_generate_key(dsa);
+  DSA_sign(0, str1, 20, sig, &siglen, dsa);
+  if (DSA_verify(0, str1, 20, sig, siglen, dsa) == 1) {
+    ok = 1;
+  } else {
+    BIO_printf(bio_err, "verification failure\n");
+  }
+
+end:
+  if (!ok) {
+    BIO_print_errors(bio_err);
+  }
+  if (dsa != NULL) {
+    DSA_free(dsa);
+  }
+
+  BIO_free(bio_err);
+  BIO_free(bio_out);
+
+  if (ok) {
+    printf("PASS\n");
+  }
+
+  return ok == 1 ? 0 : 1;
+}
+
+static int dsa_cb(int p, int n, BN_GENCB *arg) {
+  char c = '*';
+  static int ok = 0, num = 0;
+
+  switch (p) {
+  case 0:
+    c = '.';
+    num++;
+    break;
+  case 1:
+    c = '+';
+    break;
+  case 2:
+    c = '*';
+    ok++;
+    break;
+  case 3:
+    c = '\n';
+  }
+
+  BIO_write(arg->arg, &c, 1);
+  (void)BIO_flush(arg->arg);
+
+  if (!ok && p == 0 && num > 1) {
+    BIO_printf((BIO *)arg, "error in dsatest\n");
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/crypto/dsa/internal.h b/crypto/dsa/internal.h
new file mode 100644
index 0000000..ef99158
--- /dev/null
+++ b/crypto/dsa/internal.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ * The DSS routines are based on patches supplied by
+ * Steven Schoch <schoch@sheba.arc.nasa.gov>. */
+
+#ifndef OPENSSL_HEADER_DSA_INTERNAL_H
+#define OPENSSL_HEADER_DSA_INTERNAL_H
+
+#include <openssl/base.h>
+
+#include <openssl/bn.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_DSA_INTERNAL_H */
diff --git a/crypto/ec/CMakeLists.txt b/crypto/ec/CMakeLists.txt
new file mode 100644
index 0000000..019916f
--- /dev/null
+++ b/crypto/ec/CMakeLists.txt
@@ -0,0 +1,24 @@
+include_directories(. .. ../../include)
+
+add_library(
+	ec
+
+	OBJECT
+
+	ec.c
+	oct.c
+	simple.c
+	ec_montgomery.c
+	wnaf.c
+	ec_key.c
+	ec_asn1.c
+	ec_error.c
+)
+
+add_executable(
+	example_mul
+
+	example_mul.c
+)
+
+target_link_libraries(example_mul crypto)
diff --git a/crypto/ec/ec.c b/crypto/ec/ec.c
new file mode 100644
index 0000000..ef022bc
--- /dev/null
+++ b/crypto/ec/ec.c
@@ -0,0 +1,872 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <openssl/ec.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+/* curve_data contains data about a built-in elliptic curve. */
+struct curve_data {
+  /* comment is a human-readable string describing the curve. */
+  const char *comment;
+  /* param_len is the number of bytes needed to store a field element. */
+  uint8_t param_len;
+  /* cofactor is the cofactor of the group (i.e. the number of elements in the
+   * group divided by the size of the main subgroup. */
+  uint8_t cofactor; /* promoted to BN_ULONG */
+  /* data points to an array of 6*|param_len| bytes which hold the field
+   * elements of the following (in big-endian order): prime, a, b, generator x,
+   * generator y, order. */
+  const uint8_t data[];
+};
+
+static const struct curve_data P224 = {
+    "NIST P-224",
+    28,
+    1,
+    {/* p */
+     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, 0x01,
+     /* a */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFE,
+     /* b */
+     0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 0xF5, 0x41, 0x32, 0x56,
+     0x50, 0x44, 0xB0, 0xB7, 0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43,
+     0x23, 0x55, 0xFF, 0xB4,
+     /* x */
+     0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F, 0x32, 0x13, 0x90, 0xB9,
+     0x4A, 0x03, 0xC1, 0xD3, 0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xD6,
+     0x11, 0x5C, 0x1D, 0x21,
+     /* y */
+     0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6,
+     0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64, 0x44, 0xd5, 0x81, 0x99,
+     0x85, 0x00, 0x7e, 0x34,
+     /* order */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0x16, 0xA2, 0xE0, 0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 0x45,
+     0x5C, 0x5C, 0x2A, 0x3D,
+    }};
+
+static const struct curve_data P256 = {
+    "NIST P-256",
+    32,
+    1,
+    {/* p */
+     0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     /* a */
+     0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+     /* b */
+     0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55,
+     0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6,
+     0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B,
+     /* x */
+     0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5,
+     0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0,
+     0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
+     /* y */
+     0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a,
+     0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+     0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5,
+     /* order */
+     0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+     0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51}};
+
+static const struct curve_data P384 = {
+    "NIST P-384",
+    48,
+    1,
+    {/* p */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+     /* a */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC,
+     /* b */
+     0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B,
+     0xE3, 0xF8, 0x2D, 0x19, 0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12,
+     0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A, 0xC6, 0x56, 0x39, 0x8D,
+     0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF,
+     /* x */
+     0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E,
+     0xF3, 0x20, 0xAD, 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98,
+     0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, 0x55, 0x02, 0xF2, 0x5D,
+     0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7,
+     /* y */
+     0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, 0x5d, 0x9e, 0x98, 0xbf,
+     0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+     0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, 0xce,
+     0x1d, 0x7e, 0x81, 0x9d, 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f,
+     /* order */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, 0x58, 0x1A, 0x0D, 0xB2,
+     0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73}};
+
+static const struct curve_data P521 = {
+    "NIST P-521",
+    66,
+    1,
+    {/* p */
+     0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     /* a */
+     0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+     /* b */
+     0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C, 0x9A, 0x1F, 0x92, 0x9A,
+     0x21, 0xA0, 0xB6, 0x85, 0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3,
+     0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1, 0x09, 0xE1, 0x56, 0x19,
+     0x39, 0x51, 0xEC, 0x7E, 0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1,
+     0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C, 0x34, 0xF1, 0xEF, 0x45,
+     0x1F, 0xD4, 0x6B, 0x50, 0x3F, 0x00,
+     /* x */
+     0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E,
+     0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F,
+     0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, 0x4B,
+     0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF,
+     0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E,
+     0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66,
+     /* y */
+     0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b, 0xc0, 0x04, 0x5c, 0x8a,
+     0x5f, 0xb4, 0x2c, 0x7d, 0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+     0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e, 0x66, 0x2c, 0x97, 0xee,
+     0x72, 0x99, 0x5e, 0xf4, 0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+     0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72, 0xc2, 0x40, 0x88, 0xbe,
+     0x94, 0x76, 0x9f, 0xd1, 0x66, 0x50,
+     /* order */
+     0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x51, 0x86,
+     0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09,
+     0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F,
+     0xB7, 0x1E, 0x91, 0x38, 0x64, 0x09}};
+
+struct built_in_curve {
+  int nid;
+  const struct curve_data *data;
+  const EC_METHOD *(*method)(void);
+};
+
+static const struct built_in_curve built_in_curves[] = {
+  {NID_secp224r1, &P224, 0},
+  {NID_X9_62_prime256v1, &P256, 0},
+  {NID_secp384r1, &P384, 0},
+  {NID_secp521r1, &P521, 0},
+  {NID_undef, 0, 0},
+};
+
+EC_GROUP *ec_group_new(const EC_METHOD *meth) {
+  EC_GROUP *ret;
+
+  if (meth == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new, EC_R_SLOT_FULL);
+    return NULL;
+  }
+
+  if (meth->group_init == 0) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return NULL;
+  }
+
+  ret = OPENSSL_malloc(sizeof(EC_GROUP));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  memset(ret, 0, sizeof(EC_GROUP));
+
+  ret->meth = meth;
+  BN_init(&ret->order);
+  BN_init(&ret->cofactor);
+  ret->asn1_form = POINT_CONVERSION_UNCOMPRESSED;
+
+  if (!meth->group_init(ret)) {
+    OPENSSL_free(ret);
+    return NULL;
+  }
+
+  return ret;
+}
+
+static EC_GROUP *ec_group_new_curve_GFp(const BIGNUM *p, const BIGNUM *a,
+                                        const BIGNUM *b, BN_CTX *ctx) {
+  const EC_METHOD *meth = EC_GFp_mont_method();
+  EC_GROUP *ret;
+
+  ret = ec_group_new(meth);
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  if (ret->meth->group_set_curve == 0) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_curve_GFp,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (!ret->meth->group_set_curve(ret, p, a, b, ctx)) {
+    EC_GROUP_free(ret);
+    return NULL;
+  }
+  return ret;
+}
+
+static EC_GROUP *ec_group_new_from_data(const struct built_in_curve *curve) {
+  EC_GROUP *group = NULL;
+  EC_POINT *P = NULL;
+  BN_CTX *ctx = NULL;
+  BIGNUM *p = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL, *order = NULL;
+  int ok = 0;
+  unsigned param_len;
+  const EC_METHOD *meth;
+  const struct curve_data *data;
+  const uint8_t *params;
+
+  if ((ctx = BN_CTX_new()) == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  data = curve->data;
+  param_len = data->param_len;
+  params = data->data;
+
+  if (!(p = BN_bin2bn(params + 0 * param_len, param_len, NULL)) ||
+      !(a = BN_bin2bn(params + 1 * param_len, param_len, NULL)) ||
+      !(b = BN_bin2bn(params + 2 * param_len, param_len, NULL))) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  if (curve->method != 0) {
+    meth = curve->method();
+    if (((group = ec_group_new(meth)) == NULL) ||
+        (!(group->meth->group_set_curve(group, p, a, b, ctx)))) {
+      OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_EC_LIB);
+      goto err;
+    }
+  } else {
+    if ((group = ec_group_new_curve_GFp(p, a, b, ctx)) == NULL) {
+      OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_EC_LIB);
+      goto err;
+    }
+  }
+
+  if ((P = EC_POINT_new(group)) == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_EC_LIB);
+    goto err;
+  }
+
+  if (!(x = BN_bin2bn(params + 3 * param_len, param_len, NULL)) ||
+      !(y = BN_bin2bn(params + 4 * param_len, param_len, NULL))) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  if (!EC_POINT_set_affine_coordinates_GFp(group, P, x, y, ctx)) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_EC_LIB);
+    goto err;
+  }
+  if (!(order = BN_bin2bn(params + 5 * param_len, param_len, NULL)) ||
+      !BN_set_word(x, (BN_ULONG)data->cofactor)) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  group->generator = P;
+  P = NULL;
+  if (!BN_copy(&group->order, order) ||
+      !BN_set_word(&group->cofactor, (BN_ULONG)data->cofactor)) {
+    OPENSSL_PUT_ERROR(EC, ec_group_new_from_data, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  ok = 1;
+
+err:
+  if (!ok) {
+    EC_GROUP_free(group);
+    group = NULL;
+  }
+  if (P)
+    EC_POINT_free(P);
+  if (ctx)
+    BN_CTX_free(ctx);
+  if (p)
+    BN_free(p);
+  if (a)
+    BN_free(a);
+  if (b)
+    BN_free(b);
+  if (order)
+    BN_free(order);
+  if (x)
+    BN_free(x);
+  if (y)
+    BN_free(y);
+  return group;
+}
+
+EC_GROUP *EC_GROUP_new_by_curve_name(int nid) {
+  unsigned i;
+  const struct built_in_curve *curve;
+  EC_GROUP *ret = NULL;
+
+  for (i = 0; built_in_curves[i].nid != NID_undef; i++) {
+    curve = &built_in_curves[i];
+    if (curve->nid == nid) {
+      ret = ec_group_new_from_data(curve);
+      break;
+    }
+  }
+
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_GROUP_new_by_curve_name, EC_R_UNKNOWN_GROUP);
+    return NULL;
+  }
+
+  ret->curve_name = nid;
+  return ret;
+}
+
+void EC_GROUP_free(EC_GROUP *group) {
+  if (!group) {
+    return;
+  }
+
+  if (group->meth->group_finish != 0) {
+    group->meth->group_finish(group);
+  }
+
+  ec_pre_comp_free(group->pre_comp);
+
+  if (group->generator != NULL) {
+    EC_POINT_free(group->generator);
+  }
+  BN_free(&group->order);
+  BN_free(&group->cofactor);
+
+  OPENSSL_free(group);
+}
+
+int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src) {
+  if (dest->meth->group_copy == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_GROUP_copy, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (dest->meth != src->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_GROUP_copy, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  if (dest == src) {
+    return 1;
+  }
+
+  ec_pre_comp_free(dest->pre_comp);
+  dest->pre_comp = ec_pre_comp_dup(src->pre_comp);
+
+  if (src->generator != NULL) {
+    if (dest->generator == NULL) {
+      dest->generator = EC_POINT_new(dest);
+      if (dest->generator == NULL) {
+        return 0;
+      }
+    }
+    if (!EC_POINT_copy(dest->generator, src->generator)) {
+      return 0;
+    }
+  } else {
+    /* src->generator == NULL */
+    if (dest->generator != NULL) {
+      EC_POINT_clear_free(dest->generator);
+      dest->generator = NULL;
+    }
+  }
+
+  if (!BN_copy(&dest->order, &src->order) ||
+      !BN_copy(&dest->cofactor, &src->cofactor)) {
+    return 0;
+  }
+
+  dest->curve_name = src->curve_name;
+  dest->asn1_form = src->asn1_form;
+
+  return dest->meth->group_copy(dest, src);
+}
+
+EC_GROUP *EC_GROUP_dup(const EC_GROUP *a) {
+  EC_GROUP *t = NULL;
+  int ok = 0;
+
+  if (a == NULL) {
+    return NULL;
+  }
+
+  t = ec_group_new(a->meth);
+  if (t == NULL) {
+    return NULL;
+  }
+  if (!EC_GROUP_copy(t, a)) {
+    goto err;
+  }
+
+  ok = 1;
+
+err:
+  if (!ok) {
+    if (t) {
+      EC_GROUP_free(t);
+    }
+    return NULL;
+  } else {
+    return t;
+  }
+}
+
+int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b) {
+  if (a->curve_name == NID_undef || b->curve_name == NID_undef) {
+    return 0;
+  }
+  return a->curve_name == b->curve_name;
+}
+
+const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group) {
+  return group->generator;
+}
+
+int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order, BN_CTX *ctx) {
+  if (!BN_copy(order, &group->order)) {
+    return 0;
+  }
+
+  return !BN_is_zero(order);
+}
+
+int EC_GROUP_get_cofactor(const EC_GROUP *group, BIGNUM *cofactor,
+                          BN_CTX *ctx) {
+  if (!BN_copy(cofactor, &group->cofactor)) {
+    return 0;
+  }
+
+  return !BN_is_zero(&group->cofactor);
+}
+
+int EC_GROUP_get_curve_name(const EC_GROUP *group) { return group->curve_name; }
+
+int EC_GROUP_get_degree(const EC_GROUP *group) {
+  if (group->meth->group_get_degree == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_GROUP_get_degree,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  return group->meth->group_get_degree(group);
+}
+
+void EC_GROUP_set_point_conversion_form(EC_GROUP *group,
+                                        point_conversion_form_t form) {
+  group->asn1_form = form;
+}
+
+int EC_GROUP_precompute_mult(EC_GROUP *group, BN_CTX *ctx) {
+  if (group->meth->mul == 0) {
+    /* use default */
+    return ec_wNAF_precompute_mult(group, ctx);
+  }
+
+  if (group->meth->precompute_mult != 0) {
+    return group->meth->precompute_mult(group, ctx);
+  }
+
+  return 1; /* nothing to do, so report success */
+}
+
+int EC_GROUP_have_precompute_mult(const EC_GROUP *group) {
+  if (group->meth->mul == 0) {
+    /* use default */
+    return ec_wNAF_have_precompute_mult(group);
+  }
+
+  if (group->meth->have_precompute_mult != 0) {
+    return group->meth->have_precompute_mult(group);
+  }
+
+  return 0; /* cannot tell whether precomputation has been performed */
+}
+
+EC_POINT *EC_POINT_new(const EC_GROUP *group) {
+  EC_POINT *ret;
+
+  if (group == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_new, ERR_R_PASSED_NULL_PARAMETER);
+    return NULL;
+  }
+  if (group->meth->point_init == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_new, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return NULL;
+  }
+
+  ret = OPENSSL_malloc(sizeof *ret);
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  ret->meth = group->meth;
+
+  if (!ret->meth->point_init(ret)) {
+    OPENSSL_free(ret);
+    return NULL;
+  }
+
+  return ret;
+}
+
+void EC_POINT_free(EC_POINT *point) {
+  if (!point) {
+    return;
+  }
+
+  if (point->meth->point_finish != 0) {
+    point->meth->point_finish(point);
+  }
+  OPENSSL_free(point);
+}
+
+void EC_POINT_clear_free(EC_POINT *point) {
+  if (!point) {
+    return;
+  }
+
+  if (point->meth->point_clear_finish != 0) {
+    point->meth->point_clear_finish(point);
+  } else if (point->meth->point_finish != 0) {
+    point->meth->point_finish(point);
+  }
+  OPENSSL_cleanse(point, sizeof *point);
+  OPENSSL_free(point);
+}
+
+int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src) {
+  if (dest->meth->point_copy == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_copy, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (dest->meth != src->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_copy, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  if (dest == src) {
+    return 1;
+  }
+  return dest->meth->point_copy(dest, src);
+}
+
+EC_POINT *EC_POINT_dup(const EC_POINT *a, const EC_GROUP *group) {
+  EC_POINT *t;
+  int r;
+
+  if (a == NULL) {
+    return NULL;
+  }
+
+  t = EC_POINT_new(group);
+  if (t == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_dup, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  r = EC_POINT_copy(t, a);
+  if (!r) {
+    EC_POINT_free(t);
+    return NULL;
+  } else {
+    return t;
+  }
+}
+
+int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point) {
+  if (group->meth->point_set_to_infinity == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_set_to_infinity,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_set_to_infinity, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->point_set_to_infinity(group, point);
+}
+
+int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point) {
+  if (group->meth->is_at_infinity == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_is_at_infinity,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_is_at_infinity, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->is_at_infinity(group, point);
+}
+
+int EC_POINT_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
+                         BN_CTX *ctx) {
+  if (group->meth->is_on_curve == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_is_on_curve,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_is_on_curve, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->is_on_curve(group, point, ctx);
+}
+
+int EC_POINT_cmp(const EC_GROUP *group, const EC_POINT *a, const EC_POINT *b,
+                 BN_CTX *ctx) {
+  if (group->meth->point_cmp == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_cmp, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return -1;
+  }
+  if ((group->meth != a->meth) || (a->meth != b->meth)) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_cmp, EC_R_INCOMPATIBLE_OBJECTS);
+    return -1;
+  }
+  return group->meth->point_cmp(group, a, b, ctx);
+}
+
+int EC_POINT_make_affine(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx) {
+  if (group->meth->make_affine == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_make_affine,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_make_affine, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->make_affine(group, point, ctx);
+}
+
+int EC_POINTs_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[],
+                          BN_CTX *ctx) {
+  size_t i;
+
+  if (group->meth->points_make_affine == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINTs_make_affine,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  for (i = 0; i < num; i++) {
+    if (group->meth != points[i]->meth) {
+      OPENSSL_PUT_ERROR(EC, EC_POINTs_make_affine, EC_R_INCOMPATIBLE_OBJECTS);
+      return 0;
+    }
+  }
+  return group->meth->points_make_affine(group, num, points, ctx);
+}
+
+int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group,
+                                        const EC_POINT *point, BIGNUM *x,
+                                        BIGNUM *y, BN_CTX *ctx) {
+  if (group->meth->point_get_affine_coordinates == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_get_affine_coordinates_GFp,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_get_affine_coordinates_GFp,
+                      EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->point_get_affine_coordinates(group, point, x, y, ctx);
+}
+
+int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
+                                        const BIGNUM *x, const BIGNUM *y,
+                                        BN_CTX *ctx) {
+  if (group->meth->point_set_affine_coordinates == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_set_affine_coordinates_GFp,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_set_affine_coordinates_GFp,
+                      EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->point_set_affine_coordinates(group, point, x, y, ctx);
+}
+
+int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                 const EC_POINT *b, BN_CTX *ctx) {
+  if (group->meth->add == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_add, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if ((group->meth != r->meth) || (r->meth != a->meth) ||
+      (a->meth != b->meth)) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_add, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->add(group, r, a, b, ctx);
+}
+
+
+int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                 BN_CTX *ctx) {
+  if (group->meth->dbl == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_dbl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if ((group->meth != r->meth) || (r->meth != a->meth)) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_dbl, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->dbl(group, r, a, ctx);
+}
+
+
+int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a, BN_CTX *ctx) {
+  if (group->meth->dbl == 0) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_invert, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != a->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_invert, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->invert(group, a, ctx);
+}
+
+int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
+                 const EC_POINT *point, const BIGNUM *p_scalar, BN_CTX *ctx) {
+  /* just a convenient interface to EC_POINTs_mul() */
+
+  const EC_POINT *points[1];
+  const BIGNUM *scalars[1];
+
+  points[0] = point;
+  scalars[0] = p_scalar;
+
+  return EC_POINTs_mul(group, r, g_scalar, (point != NULL && p_scalar != NULL),
+                       points, scalars, ctx);
+}
+
+int EC_POINTs_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                  size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+                  BN_CTX *ctx) {
+  if (group->meth->mul == 0) {
+    /* use default. Warning, not constant-time. */
+    return ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx);
+  }
+
+  return group->meth->mul(group, r, scalar, num, points, scalars, ctx);
+}
+
+int ec_point_set_Jprojective_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
+                                             const BIGNUM *x, const BIGNUM *y,
+                                             const BIGNUM *z, BN_CTX *ctx) {
+  if (group->meth->point_set_Jprojective_coordinates_GFp == 0) {
+    OPENSSL_PUT_ERROR(EC, ec_point_set_Jprojective_coordinates_GFp,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, ec_point_set_Jprojective_coordinates_GFp,
+                      EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  return group->meth->point_set_Jprojective_coordinates_GFp(group, point, x, y,
+                                                            z, ctx);
+}
diff --git a/crypto/ec/ec.h b/crypto/ec/ec.h
new file mode 100644
index 0000000..368432e
--- /dev/null
+++ b/crypto/ec/ec.h
@@ -0,0 +1,367 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#ifndef OPENSSL_HEADER_EC_H
+#define OPENSSL_HEADER_EC_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+typedef struct ec_group_st EC_GROUP;
+typedef struct ec_point_st EC_POINT;
+
+/** Enum for the point conversion form as defined in X9.62 (ECDSA)
+ *  for the encoding of a elliptic curve point (x,y) */
+typedef enum {
+	/** the point is encoded as z||x, where the octet z specifies 
+	 *  which solution of the quadratic equation y is  */
+	POINT_CONVERSION_COMPRESSED = 2,
+	/** the point is encoded as z||x||y, where z is the octet 0x02  */
+	POINT_CONVERSION_UNCOMPRESSED = 4,
+	/** the point is encoded as z||x||y, where the octet z specifies
+         *  which solution of the quadratic equation y is  */
+	POINT_CONVERSION_HYBRID = 6
+} point_conversion_form_t;
+
+
+/* Elliptic curve groups. */
+
+/* EC_GROUP_new_by_curve_name returns a fresh EC_GROUP object for the elliptic
+ * curve specified by |nid|, or NULL on error.
+ *
+ * The supported NIDs are:
+ *   NID_secp224r1,
+ *   NID_X9_62_prime256v1,
+ *   NID_secp384r1,
+ *   NID_secp521r1 */
+EC_GROUP *EC_GROUP_new_by_curve_name(int nid);
+
+/* EC_GROUP_free frees |group| and the data that it points to. */
+void EC_GROUP_free(EC_GROUP *group);
+
+/* EC_GROUP_copy sets |*dest| equal to |*src|. It returns one on success and
+ * zero otherwise. */
+int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src);
+
+/* EC_GROUP_dup returns a fresh |EC_GROUP| which is equal to |a| or NULL on
+ * error. */
+EC_GROUP *EC_GROUP_dup(const EC_GROUP *a);
+
+/* EC_GROUP_cmp returns one if |a| and |b| are the same group and zero
+ * otherwise. */
+int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b);
+
+/* EC_GROUP_get0_generator returns a pointer to the internal |EC_POINT| object
+ * in |group| that specifies the generator for the group. */
+const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group);
+
+/* EC_GROUP_get_order sets |*order| to the order of |group| using |ctx|, if
+ * it's not NULL. It returns one on success and zero otherwise. */
+int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order, BN_CTX *ctx);
+
+/* EC_GROUP_get_cofactor sets |*cofactor| to the cofactor of |group| using
+ * |ctx|, if it's not NULL. It returns one on success and zero otherwise. */
+int EC_GROUP_get_cofactor(const EC_GROUP *group, BIGNUM *cofactor, BN_CTX *ctx);
+
+/* EC_GROUP_get_curve_name returns a NID that identifies |group|. */
+int EC_GROUP_get_curve_name(const EC_GROUP *group);
+
+/* EC_GROUP_get_degree returns the number of bits needed to represent an
+ * element of the field underlying |group|. */
+int EC_GROUP_get_degree(const EC_GROUP *group);
+
+/* EC_GROUP_set_point_conversion_form sets the form that serialised points will
+ * take as one of the |POINT_CONVERSION_*| values. */
+void EC_GROUP_set_point_conversion_form(EC_GROUP *group,
+                                        point_conversion_form_t form);
+
+/* EC_GROUP_precompute_mult precomputes multiplies of the generator in order to
+ * speed up operations that involve calculating generator multiples. It returns
+ * one on sucess and zero otherwise. If |ctx| is not NULL, it may be used. */
+int EC_GROUP_precompute_mult(EC_GROUP *group, BN_CTX *ctx);
+
+/* EC_GROUP_have_precompute_mult returns one if |group| contains precomputed
+ * generator multiples. */
+int EC_GROUP_have_precompute_mult(const EC_GROUP *group);
+
+
+/* Points on elliptic curves. */
+
+/* EC_POINT_new returns a fresh |EC_POINT| object in the given group, or NULL
+ * on error. */
+EC_POINT *EC_POINT_new(const EC_GROUP *group);
+
+/* EC_POINT_free frees |point| and the data that it points to. */
+void EC_POINT_free(EC_POINT *point);
+
+/* EC_POINT_clear_free clears the data that |point| points to, frees it and
+ * then frees |point| itself. */
+void EC_POINT_clear_free(EC_POINT *point);
+
+/* EC_POINT_copy sets |*dest| equal to |*src|. It returns one on success and
+ * zero otherwise. */
+int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src);
+
+/* EC_POINT_dup returns a fresh |EC_POINT| that contains the same values as
+ * |src|, or NULL on error. */
+EC_POINT *EC_POINT_dup(const EC_POINT *src, const EC_GROUP *group);
+
+/* EC_POINT_set_to_infinity sets |point| to be the "point at infinity" for the
+ * given group. */
+int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point);
+
+/* EC_POINT_is_at_infinity returns one iff |point| is the point at infinity and
+ * zero otherwise. */
+int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point);
+
+/* EC_POINT_is_on_curve returns one if |point| is an element of |group| and
+ * zero otheriwse. If |ctx| is non-NULL, it may be used. */
+int EC_POINT_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
+                         BN_CTX *ctx);
+
+/* EC_POINT_cmp returns zero if |a| is equal to |b|, greater than zero is
+ * non-equal and -1 on error. If |ctx| is not NULL, it may be used. */
+int EC_POINT_cmp(const EC_GROUP *group, const EC_POINT *a, const EC_POINT *b,
+                 BN_CTX *ctx);
+
+/* EC_POINT_make_affine converts |point| to affine form, internally. It returns
+ * one on success and zero otherwise. If |ctx| is not NULL, it may be used. */
+int EC_POINT_make_affine(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx);
+
+/* EC_POINTs_make_affine converts |num| points from |points| to affine form,
+ * internally. It returns one on success and zero otherwise. If |ctx| is not
+ * NULL, it may be used. */
+int EC_POINTs_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[],
+                          BN_CTX *ctx);
+
+
+/* Point conversion. */
+
+/* EC_POINT_get_affine_coordinates_GFp sets |x| and |y| to the affine value of
+ * |point| using |ctx|, if it's not NULL. It returns one on success and zero
+ * otherwise. */
+int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group,
+                                        const EC_POINT *point, BIGNUM *x,
+                                        BIGNUM *y, BN_CTX *ctx);
+
+/* EC_POINT_set_affine_coordinates sets the value of |p| to be (|x|, |y|). The
+ * |ctx| argument may be used if not NULL. */
+int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
+                                        const BIGNUM *x, const BIGNUM *y,
+                                        BN_CTX *ctx);
+
+/* EC_POINT_point2oct serialises |point| into the X9.62 form given by |form|
+ * into, at most, |len| bytes at |buf|. It returns the number of bytes written
+ * or zero on error if |buf| is non-NULL, else the number of bytes needed. The
+ * |ctx| argument may be used if not NULL. */
+size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
+                          point_conversion_form_t form, uint8_t *buf,
+                          size_t len, BN_CTX *ctx);
+
+/* EC_POINT_oct2point sets |point| from |len| bytes of X9.62 format
+ * serialisation in |buf|. It returns one on success and zero otherwise. The
+ * |ctx| argument may be used if not NULL. */
+int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
+                       const uint8_t *buf, size_t len, BN_CTX *ctx);
+
+/* EC_POINT_set_compressed_coordinates_GFp sets |point| to equal the point with
+ * the given |x| coordinate and the y coordinate specified by |y_bit| (see
+ * X9.62). It returns one on success and zero otherwise. */
+int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
+                                            EC_POINT *point, const BIGNUM *x,
+                                            int y_bit, BN_CTX *ctx);
+
+
+/* Group operations. */
+
+/* EC_POINT_add sets |r| equal to |a| plus |b|. It returns one on success and
+ * zero otherwise. If |ctx| is not NULL, it may be used. */
+int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                 const EC_POINT *b, BN_CTX *ctx);
+
+/* EC_POINT_dbl sets |r| equal to |a| plus |a|. It returns one on success and
+ * zero otherwise. If |ctx| is not NULL, it may be used. */
+int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                 BN_CTX *ctx);
+
+/* EC_POINT_dbl sets |a| equal to minus |a|. It returns one on success and zero
+ * otherwise. If |ctx| is not NULL, it may be used. */
+int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a, BN_CTX *ctx);
+
+/* EC_POINT_mul sets r = generator*n + q*m. It returns one on success and zero
+ * otherwise. If |ctx| is not NULL, it may be used. */
+int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n,
+                 const EC_POINT *q, const BIGNUM *m, BN_CTX *ctx);
+
+/* EC_POINTs_mul sets r = generator*n + sum(p[i]*m[i]). It returns one on
+ * success and zero otherwise. If |ctx| is not NULL, it may be used. */
+int EC_POINTs_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n,
+                  size_t num, const EC_POINT *p[], const BIGNUM *m[],
+                  BN_CTX *ctx);
+
+
+/* Old code expects to get EC_KEY from ec.h. */
+#if !defined(OPENSSL_HEADER_EC_KEY_H)
+#include <openssl/ec_key.h>
+#endif
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define EC_F_ec_pre_comp_new 100
+#define EC_F_ec_GFp_mont_field_decode 101
+#define EC_F_ec_group_new_from_data 102
+#define EC_F_ec_GFp_simple_point_get_affine_coordinates 103
+#define EC_F_ec_GFp_simple_make_affine 104
+#define EC_F_EC_KEY_new_method 105
+#define EC_F_ec_GFp_mont_field_encode 106
+#define EC_F_EC_GROUP_new_by_curve_name 107
+#define EC_F_ec_group_new 108
+#define EC_F_ec_asn1_group2pkparameters 109
+#define EC_F_EC_POINT_set_compressed_coordinates_GFp 110
+#define EC_F_ec_GFp_mont_field_sqr 111
+#define EC_F_EC_POINT_make_affine 112
+#define EC_F_i2d_ECParameters 113
+#define EC_F_ec_wNAF_mul 114
+#define EC_F_EC_GROUP_copy 115
+#define EC_F_EC_POINT_cmp 116
+#define EC_F_ec_GFp_mont_field_mul 117
+#define EC_F_EC_POINT_dup 118
+#define EC_F_EC_POINT_invert 119
+#define EC_F_ec_GFp_simple_point_set_affine_coordinates 120
+#define EC_F_ec_GFp_simple_points_make_affine 121
+#define EC_F_i2o_ECPublicKey 122
+#define EC_F_EC_KEY_check_key 123
+#define EC_F_ec_wNAF_precompute_mult 124
+#define EC_F_EC_POINT_oct2point 125
+#define EC_F_EC_POINT_is_at_infinity 126
+#define EC_F_EC_POINT_get_affine_coordinates_GFp 127
+#define EC_F_ec_point_set_Jprojective_coordinates_GFp 128
+#define EC_F_o2i_ECPublicKey 129
+#define EC_F_ec_GFp_mont_field_set_to_one 130
+#define EC_F_ec_group_new_curve_GFp 131
+#define EC_F_EC_POINT_dbl 132
+#define EC_F_ec_asn1_pkparameters2group 133
+#define EC_F_i2d_ECPKParameters 134
+#define EC_F_EC_KEY_copy 135
+#define EC_F_EC_POINT_new 136
+#define EC_F_EC_POINT_point2oct 137
+#define EC_F_EC_POINT_copy 138
+#define EC_F_EC_POINT_is_on_curve 139
+#define EC_F_ec_GFp_simple_group_set_curve 140
+#define EC_F_i2d_ECPrivateKey 141
+#define EC_F_d2i_ECParameters 142
+#define EC_F_ec_GFp_mont_group_set_curve 143
+#define EC_F_EC_POINT_set_to_infinity 144
+#define EC_F_EC_POINTs_make_affine 145
+#define EC_F_compute_wNAF 146
+#define EC_F_ec_GFp_simple_point2oct 147
+#define EC_F_EC_GROUP_get_degree 148
+#define EC_F_ec_GFp_simple_group_check_discriminant 149
+#define EC_F_d2i_ECPKParameters 150
+#define EC_F_d2i_ECPrivateKey 151
+#define EC_F_ec_GFp_simple_oct2point 152
+#define EC_F_EC_POINT_set_affine_coordinates_GFp 153
+#define EC_F_EC_KEY_set_public_key_affine_coordinates 154
+#define EC_F_EC_KEY_generate_key 155
+#define EC_F_ec_GFp_simple_set_compressed_coordinates 156
+#define EC_F_EC_POINT_add 157
+#define EC_R_PKPARAMETERS2GROUP_FAILURE 100
+#define EC_R_NON_NAMED_CURVE 101
+#define EC_R_COORDINATES_OUT_OF_RANGE 102
+#define EC_R_POINT_AT_INFINITY 103
+#define EC_R_NOT_INITIALIZED 104
+#define EC_R_MISSING_PRIVATE_KEY 105
+#define EC_R_GROUP2PKPARAMETERS_FAILURE 106
+#define EC_R_INVALID_ENCODING 107
+#define EC_R_BUFFER_TOO_SMALL 108
+#define EC_R_D2I_ECPKPARAMETERS_FAILURE 109
+#define EC_R_INVALID_FORM 110
+#define EC_R_INVALID_PRIVATE_KEY 111
+#define EC_R_INVALID_COMPRESSED_POINT 112
+#define EC_R_MISSING_PARAMETERS 113
+#define EC_R_INVALID_FIELD 114
+#define EC_R_INVALID_COMPRESSION_BIT 115
+#define EC_R_GF2M_NOT_SUPPORTED 116
+#define EC_R_POINT_IS_NOT_ON_CURVE 117
+#define EC_R_UNKNOWN_ORDER 118
+#define EC_R_UNKNOWN_GROUP 119
+#define EC_R_WRONG_ORDER 120
+#define EC_R_UNDEFINED_GENERATOR 121
+#define EC_R_INCOMPATIBLE_OBJECTS 122
+#define EC_R_I2D_ECPKPARAMETERS_FAILURE 123
+#define EC_R_EC_GROUP_NEW_BY_NAME_FAILURE 124
+#define EC_R_INVALID_GROUP_ORDER 125
+#define EC_R_SLOT_FULL 126
+
+#endif  /* OPENSSL_HEADER_EC_H */
diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c
new file mode 100644
index 0000000..7b4f74f
--- /dev/null
+++ b/crypto/ec/ec_asn1.c
@@ -0,0 +1,556 @@
+/* Written by Nils Larsch for the OpenSSL project. */
+/* ====================================================================
+ * Copyright (c) 2000-2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ec.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+typedef struct x9_62_fieldid_st {
+  ASN1_OBJECT *fieldType;
+  union {
+    char *ptr;
+    /* NID_X9_62_prime_field */
+    ASN1_INTEGER *prime;
+    /* anything else */
+    ASN1_TYPE *other;
+  } p;
+} X9_62_FIELDID;
+
+ASN1_ADB_TEMPLATE(fieldID_def) = ASN1_SIMPLE(X9_62_FIELDID, p.other, ASN1_ANY);
+
+ASN1_ADB(X9_62_FIELDID) = {
+  ADB_ENTRY(NID_X9_62_prime_field, ASN1_SIMPLE(X9_62_FIELDID, p.prime, ASN1_INTEGER)),
+} ASN1_ADB_END(X9_62_FIELDID, 0, fieldType, 0, &fieldID_def_tt, NULL);
+
+ASN1_SEQUENCE(X9_62_FIELDID) = {
+  ASN1_SIMPLE(X9_62_FIELDID, fieldType, ASN1_OBJECT),
+  ASN1_ADB_OBJECT(X9_62_FIELDID)
+} ASN1_SEQUENCE_END(X9_62_FIELDID);
+
+typedef struct x9_62_curve_st {
+  ASN1_OCTET_STRING *a;
+  ASN1_OCTET_STRING *b;
+  ASN1_BIT_STRING *seed;
+} X9_62_CURVE;
+
+ASN1_SEQUENCE(X9_62_CURVE) = {
+  ASN1_SIMPLE(X9_62_CURVE, a, ASN1_OCTET_STRING),
+  ASN1_SIMPLE(X9_62_CURVE, b, ASN1_OCTET_STRING),
+  ASN1_OPT(X9_62_CURVE, seed, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(X9_62_CURVE);
+
+typedef struct ec_parameters_st {
+  long version;
+  X9_62_FIELDID *fieldID;
+  X9_62_CURVE *curve;
+  ASN1_OCTET_STRING *base;
+  ASN1_INTEGER *order;
+  ASN1_INTEGER *cofactor;
+} ECPARAMETERS;
+
+ASN1_SEQUENCE(ECPARAMETERS) = {
+    ASN1_SIMPLE(ECPARAMETERS, version, LONG),
+    ASN1_SIMPLE(ECPARAMETERS, fieldID, X9_62_FIELDID),
+    ASN1_SIMPLE(ECPARAMETERS, curve, X9_62_CURVE),
+    ASN1_SIMPLE(ECPARAMETERS, base, ASN1_OCTET_STRING),
+    ASN1_SIMPLE(ECPARAMETERS, order, ASN1_INTEGER),
+    ASN1_OPT(ECPARAMETERS, cofactor, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(ECPARAMETERS);
+
+DECLARE_ASN1_ALLOC_FUNCTIONS(ECPARAMETERS);
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(ECPARAMETERS);
+
+typedef struct ecpk_parameters_st {
+  int type;
+  union {
+    ASN1_OBJECT *named_curve;
+    ECPARAMETERS *parameters;
+  } value;
+} ECPKPARAMETERS;
+
+/* SEC1 ECPrivateKey */
+typedef struct ec_privatekey_st {
+  long version;
+  ASN1_OCTET_STRING *privateKey;
+  ECPKPARAMETERS *parameters;
+  ASN1_BIT_STRING *publicKey;
+} EC_PRIVATEKEY;
+
+ASN1_CHOICE(ECPKPARAMETERS) = {
+    ASN1_SIMPLE(ECPKPARAMETERS, value.named_curve, ASN1_OBJECT),
+    ASN1_SIMPLE(ECPKPARAMETERS, value.parameters, ECPARAMETERS),
+} ASN1_CHOICE_END(ECPKPARAMETERS);
+
+DECLARE_ASN1_FUNCTIONS_const(ECPKPARAMETERS);
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(ECPKPARAMETERS, ECPKPARAMETERS);
+IMPLEMENT_ASN1_FUNCTIONS_const(ECPKPARAMETERS);
+
+ASN1_SEQUENCE(EC_PRIVATEKEY) = {
+    ASN1_SIMPLE(EC_PRIVATEKEY, version, LONG),
+    ASN1_SIMPLE(EC_PRIVATEKEY, privateKey, ASN1_OCTET_STRING),
+    ASN1_EXP_OPT(EC_PRIVATEKEY, parameters, ECPKPARAMETERS, 0),
+    ASN1_EXP_OPT(EC_PRIVATEKEY, publicKey, ASN1_BIT_STRING, 1),
+} ASN1_SEQUENCE_END(EC_PRIVATEKEY);
+
+DECLARE_ASN1_FUNCTIONS_const(EC_PRIVATEKEY);
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(EC_PRIVATEKEY, EC_PRIVATEKEY);
+IMPLEMENT_ASN1_FUNCTIONS_const(EC_PRIVATEKEY);
+
+
+ECPKPARAMETERS *ec_asn1_group2pkparameters(const EC_GROUP *group,
+                                           ECPKPARAMETERS *params) {
+  int ok = 0, nid;
+  ECPKPARAMETERS *ret = params;
+
+  if (ret == NULL) {
+    ret = ECPKPARAMETERS_new();
+    if (ret == NULL) {
+      OPENSSL_PUT_ERROR(EC, ec_asn1_group2pkparameters, ERR_R_MALLOC_FAILURE);
+      return NULL;
+    }
+  } else {
+    if (ret->value.named_curve) {
+      ASN1_OBJECT_free(ret->value.named_curve);
+    }
+  }
+
+  /* use the ASN.1 OID to describe the the elliptic curve parameters. */
+  nid = EC_GROUP_get_curve_name(group);
+  if (nid) {
+    ret->type = 0;
+    ret->value.named_curve = (ASN1_OBJECT*) OBJ_nid2obj(nid);
+    ok = ret->value.named_curve != NULL;
+  }
+
+  if (!ok) {
+    ECPKPARAMETERS_free(ret);
+    return NULL;
+  }
+
+  return ret;
+}
+
+EC_GROUP *ec_asn1_pkparameters2group(const ECPKPARAMETERS *params) {
+  EC_GROUP *ret = NULL;
+  int nid = 0;
+
+  if (params == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_asn1_pkparameters2group, EC_R_MISSING_PARAMETERS);
+    return NULL;
+  }
+
+  if (params->type != 0) {
+    OPENSSL_PUT_ERROR(EC, ec_asn1_pkparameters2group, EC_R_NON_NAMED_CURVE);
+    return NULL;
+  }
+
+  nid = OBJ_obj2nid(params->value.named_curve);
+  ret = EC_GROUP_new_by_curve_name(nid);
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_asn1_pkparameters2group,
+                      EC_R_EC_GROUP_NEW_BY_NAME_FAILURE);
+    return NULL;
+  }
+
+  return ret;
+}
+
+static EC_GROUP *d2i_ECPKParameters(EC_GROUP **groupp, const uint8_t **inp,
+                                    long len) {
+  EC_GROUP *group = NULL;
+  ECPKPARAMETERS *params = NULL;
+
+  params = d2i_ECPKPARAMETERS(NULL, inp, len);
+  if (params == NULL) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECPKParameters, EC_R_D2I_ECPKPARAMETERS_FAILURE);
+    ECPKPARAMETERS_free(params);
+    return NULL;
+  }
+
+  group = ec_asn1_pkparameters2group(params);
+  if (group == NULL) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECPKParameters, EC_R_PKPARAMETERS2GROUP_FAILURE);
+    ECPKPARAMETERS_free(params);
+    return NULL;
+  }
+
+  if (groupp && *groupp) {
+    EC_GROUP_free(*groupp);
+  }
+  if (groupp) {
+    *groupp = group;
+  }
+
+  ECPKPARAMETERS_free(params);
+  return group;
+}
+
+static int i2d_ECPKParameters(const EC_GROUP *group, uint8_t **outp) {
+  int ret = 0;
+  ECPKPARAMETERS *tmp = ec_asn1_group2pkparameters(group, NULL);
+  if (tmp == NULL) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPKParameters, EC_R_GROUP2PKPARAMETERS_FAILURE);
+    return 0;
+  }
+  ret = i2d_ECPKPARAMETERS(tmp, outp);
+  if (ret == 0) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPKParameters, EC_R_I2D_ECPKPARAMETERS_FAILURE);
+    ECPKPARAMETERS_free(tmp);
+    return 0;
+  }
+  ECPKPARAMETERS_free(tmp);
+  return ret;
+}
+
+EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const uint8_t **in, long len) {
+  int ok = 0;
+  EC_KEY *ret = NULL;
+  EC_PRIVATEKEY *priv_key = NULL;
+
+  priv_key = EC_PRIVATEKEY_new();
+  if (priv_key == NULL) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  priv_key = d2i_EC_PRIVATEKEY(&priv_key, in, len);
+  if (priv_key == NULL) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_EC_LIB);
+    EC_PRIVATEKEY_free(priv_key);
+    return NULL;
+  }
+
+  if (a == NULL || *a == NULL) {
+    ret = EC_KEY_new();
+    if (ret == NULL) {
+      OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    if (a) {
+      *a = ret;
+    }
+  } else {
+    ret = *a;
+  }
+
+  if (priv_key->parameters) {
+    if (ret->group) {
+      EC_GROUP_free(ret->group);
+    }
+    ret->group = ec_asn1_pkparameters2group(priv_key->parameters);
+  }
+
+  if (ret->group == NULL) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_EC_LIB);
+    goto err;
+  }
+
+  ret->version = priv_key->version;
+
+  if (priv_key->privateKey) {
+    ret->priv_key =
+        BN_bin2bn(M_ASN1_STRING_data(priv_key->privateKey),
+                  M_ASN1_STRING_length(priv_key->privateKey), ret->priv_key);
+    if (ret->priv_key == NULL) {
+      OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_BN_LIB);
+      goto err;
+    }
+  } else {
+    OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, EC_R_MISSING_PRIVATE_KEY);
+    goto err;
+  }
+
+  /* TODO(fork): loading the public key is silly. Why not calculate it? */
+  if (priv_key->publicKey) {
+    const uint8_t *pub_oct;
+    size_t pub_oct_len;
+
+    if (ret->pub_key) {
+      EC_POINT_free(ret->pub_key);
+    }
+    ret->pub_key = EC_POINT_new(ret->group);
+    if (ret->pub_key == NULL) {
+      OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_EC_LIB);
+      goto err;
+    }
+    pub_oct = M_ASN1_STRING_data(priv_key->publicKey);
+    pub_oct_len = M_ASN1_STRING_length(priv_key->publicKey);
+    /* save the point conversion form */
+    ret->conv_form = (point_conversion_form_t)(pub_oct[0] & ~0x01);
+    if (!EC_POINT_oct2point(ret->group, ret->pub_key, pub_oct, pub_oct_len,
+                            NULL)) {
+      OPENSSL_PUT_ERROR(EC, d2i_ECPrivateKey, ERR_R_EC_LIB);
+      goto err;
+    }
+  }
+
+  ok = 1;
+
+err:
+  if (!ok) {
+    if (ret) {
+      EC_KEY_free(ret);
+    }
+    ret = NULL;
+  }
+
+  if (priv_key) {
+    EC_PRIVATEKEY_free(priv_key);
+  }
+
+  return ret;
+}
+
+int i2d_ECPrivateKey(const EC_KEY *key, uint8_t **outp) {
+  int ret = 0, ok = 0;
+  uint8_t *buffer = NULL;
+  size_t buf_len = 0, tmp_len;
+  EC_PRIVATEKEY *priv_key = NULL;
+
+  if (key == NULL || key->group == NULL || key->priv_key == NULL) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_PASSED_NULL_PARAMETER);
+    goto err;
+  }
+
+  priv_key = EC_PRIVATEKEY_new();
+  if (priv_key == NULL) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  priv_key->version = key->version;
+
+  buf_len = BN_num_bytes(key->priv_key);
+  buffer = OPENSSL_malloc(buf_len);
+  if (buffer == NULL) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (!BN_bn2bin(key->priv_key, buffer)) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  if (!M_ASN1_OCTET_STRING_set(priv_key->privateKey, buffer, buf_len)) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_ASN1_LIB);
+    goto err;
+  }
+
+  /* TODO(fork): replace this flexibility with key sensible default? */
+  if (!(key->enc_flag & EC_PKEY_NO_PARAMETERS)) {
+    if ((priv_key->parameters = ec_asn1_group2pkparameters(
+             key->group, priv_key->parameters)) == NULL) {
+      OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_EC_LIB);
+      goto err;
+    }
+  }
+
+  /* TODO(fork): replace this flexibility with key sensible default? */
+  if (!(key->enc_flag & EC_PKEY_NO_PUBKEY)) {
+    priv_key->publicKey = M_ASN1_BIT_STRING_new();
+    if (priv_key->publicKey == NULL) {
+      OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+
+    tmp_len = EC_POINT_point2oct(key->group, key->pub_key, key->conv_form, NULL,
+                                 0, NULL);
+
+    if (tmp_len > buf_len) {
+      uint8_t *tmp_buffer = OPENSSL_realloc(buffer, tmp_len);
+      if (!tmp_buffer) {
+        OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_MALLOC_FAILURE);
+        goto err;
+      }
+      buffer = tmp_buffer;
+      buf_len = tmp_len;
+    }
+
+    if (!EC_POINT_point2oct(key->group, key->pub_key, key->conv_form, buffer,
+                            buf_len, NULL)) {
+      OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_EC_LIB);
+      goto err;
+    }
+
+    priv_key->publicKey->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+    priv_key->publicKey->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    if (!M_ASN1_BIT_STRING_set(priv_key->publicKey, buffer, buf_len)) {
+      OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_ASN1_LIB);
+      goto err;
+    }
+  }
+
+  ret = i2d_EC_PRIVATEKEY(priv_key, outp);
+  if (ret == 0) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECPrivateKey, ERR_R_EC_LIB);
+    goto err;
+  }
+  ok = 1;
+
+err:
+  if (buffer) {
+    OPENSSL_free(buffer);
+  }
+  if (priv_key) {
+    EC_PRIVATEKEY_free(priv_key);
+  }
+  return (ok ? ret : 0);
+}
+
+int i2d_ECParameters(const EC_KEY *key, uint8_t **outp) {
+  if (key == NULL) {
+    OPENSSL_PUT_ERROR(EC, i2d_ECParameters, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  return i2d_ECPKParameters(key->group, outp);
+}
+
+EC_KEY *d2i_ECParameters(EC_KEY **key, const uint8_t **inp, long len) {
+  EC_KEY *ret;
+
+  if (inp == NULL || *inp == NULL) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECParameters, ERR_R_PASSED_NULL_PARAMETER);
+    return NULL;
+  }
+
+  if (key == NULL || *key == NULL) {
+    ret = EC_KEY_new();
+    if (ret == NULL) {
+      OPENSSL_PUT_ERROR(EC, d2i_ECParameters, ERR_R_MALLOC_FAILURE);
+      return NULL;
+    }
+    if (key) {
+      *key = ret;
+    }
+  } else {
+    ret = *key;
+  }
+
+  if (!d2i_ECPKParameters(&ret->group, inp, len)) {
+    OPENSSL_PUT_ERROR(EC, d2i_ECParameters, ERR_R_EC_LIB);
+    return NULL;
+  }
+
+  return ret;
+}
+
+EC_KEY *o2i_ECPublicKey(EC_KEY **keyp, const uint8_t **inp, long len) {
+  EC_KEY *ret = NULL;
+
+  if (keyp == NULL || *keyp == NULL || (*keyp)->group == NULL) {
+    OPENSSL_PUT_ERROR(EC, o2i_ECPublicKey, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  ret = *keyp;
+  if (ret->pub_key == NULL &&
+      (ret->pub_key = EC_POINT_new(ret->group)) == NULL) {
+    OPENSSL_PUT_ERROR(EC, o2i_ECPublicKey, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  if (!EC_POINT_oct2point(ret->group, ret->pub_key, *inp, len, NULL)) {
+    OPENSSL_PUT_ERROR(EC, o2i_ECPublicKey, ERR_R_EC_LIB);
+    return 0;
+  }
+  /* save the point conversion form */
+  ret->conv_form = (point_conversion_form_t)(*inp[0] & ~0x01);
+  *inp += len;
+  return ret;
+}
+
+int i2o_ECPublicKey(const EC_KEY *key, uint8_t **outp) {
+  size_t buf_len = 0;
+  int new_buffer = 0;
+
+  if (key == NULL) {
+    OPENSSL_PUT_ERROR(EC, i2o_ECPublicKey, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  buf_len = EC_POINT_point2oct(key->group, key->pub_key, key->conv_form, NULL,
+                               0, NULL);
+
+  if (outp == NULL || buf_len == 0) {
+    /* out == NULL => just return the length of the octet string */
+    return buf_len;
+  }
+
+  if (*outp == NULL) {
+    *outp = OPENSSL_malloc(buf_len);
+    if (*outp == NULL) {
+      OPENSSL_PUT_ERROR(EC, i2o_ECPublicKey, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+    new_buffer = 1;
+  }
+  if (!EC_POINT_point2oct(key->group, key->pub_key, key->conv_form, *outp,
+                          buf_len, NULL)) {
+    OPENSSL_PUT_ERROR(EC, i2o_ECPublicKey, ERR_R_EC_LIB);
+    OPENSSL_free(*outp);
+    *outp = NULL;
+    return 0;
+  }
+
+  if (!new_buffer) {
+    *outp += buf_len;
+  }
+  return buf_len;
+}
diff --git a/crypto/ec/ec_error.c b/crypto/ec/ec_error.c
new file mode 100644
index 0000000..195b1af
--- /dev/null
+++ b/crypto/ec/ec_error.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "ec.h"
+
+const ERR_STRING_DATA EC_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_GROUP_copy, 0), "EC_GROUP_copy"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_GROUP_get_degree, 0), "EC_GROUP_get_degree"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_GROUP_new_by_curve_name, 0), "EC_GROUP_new_by_curve_name"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_KEY_check_key, 0), "EC_KEY_check_key"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_KEY_copy, 0), "EC_KEY_copy"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_KEY_generate_key, 0), "EC_KEY_generate_key"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_KEY_new_method, 0), "EC_KEY_new_method"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_KEY_set_public_key_affine_coordinates, 0), "EC_KEY_set_public_key_affine_coordinates"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_add, 0), "EC_POINT_add"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_cmp, 0), "EC_POINT_cmp"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_copy, 0), "EC_POINT_copy"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_dbl, 0), "EC_POINT_dbl"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_dup, 0), "EC_POINT_dup"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_get_affine_coordinates_GFp, 0), "EC_POINT_get_affine_coordinates_GFp"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_invert, 0), "EC_POINT_invert"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_is_at_infinity, 0), "EC_POINT_is_at_infinity"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_is_on_curve, 0), "EC_POINT_is_on_curve"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_make_affine, 0), "EC_POINT_make_affine"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_new, 0), "EC_POINT_new"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_oct2point, 0), "EC_POINT_oct2point"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_point2oct, 0), "EC_POINT_point2oct"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_set_affine_coordinates_GFp, 0), "EC_POINT_set_affine_coordinates_GFp"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_set_compressed_coordinates_GFp, 0), "EC_POINT_set_compressed_coordinates_GFp"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINT_set_to_infinity, 0), "EC_POINT_set_to_infinity"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_EC_POINTs_make_affine, 0), "EC_POINTs_make_affine"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_compute_wNAF, 0), "compute_wNAF"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_d2i_ECPKParameters, 0), "d2i_ECPKParameters"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_d2i_ECParameters, 0), "d2i_ECParameters"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_d2i_ECPrivateKey, 0), "d2i_ECPrivateKey"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_mont_field_decode, 0), "ec_GFp_mont_field_decode"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_mont_field_encode, 0), "ec_GFp_mont_field_encode"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_mont_field_mul, 0), "ec_GFp_mont_field_mul"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_mont_field_set_to_one, 0), "ec_GFp_mont_field_set_to_one"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_mont_field_sqr, 0), "ec_GFp_mont_field_sqr"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_mont_group_set_curve, 0), "ec_GFp_mont_group_set_curve"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_group_check_discriminant, 0), "ec_GFp_simple_group_check_discriminant"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_group_set_curve, 0), "ec_GFp_simple_group_set_curve"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_make_affine, 0), "ec_GFp_simple_make_affine"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_oct2point, 0), "ec_GFp_simple_oct2point"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_point2oct, 0), "ec_GFp_simple_point2oct"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_point_get_affine_coordinates, 0), "ec_GFp_simple_point_get_affine_coordinates"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_point_set_affine_coordinates, 0), "ec_GFp_simple_point_set_affine_coordinates"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_points_make_affine, 0), "ec_GFp_simple_points_make_affine"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_GFp_simple_set_compressed_coordinates, 0), "ec_GFp_simple_set_compressed_coordinates"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_asn1_group2pkparameters, 0), "ec_asn1_group2pkparameters"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_asn1_pkparameters2group, 0), "ec_asn1_pkparameters2group"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_group_new, 0), "ec_group_new"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_group_new_curve_GFp, 0), "ec_group_new_curve_GFp"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_group_new_from_data, 0), "ec_group_new_from_data"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_point_set_Jprojective_coordinates_GFp, 0), "ec_point_set_Jprojective_coordinates_GFp"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_pre_comp_new, 0), "ec_pre_comp_new"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_wNAF_mul, 0), "ec_wNAF_mul"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_ec_wNAF_precompute_mult, 0), "ec_wNAF_precompute_mult"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_i2d_ECPKParameters, 0), "i2d_ECPKParameters"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_i2d_ECParameters, 0), "i2d_ECParameters"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_i2d_ECPrivateKey, 0), "i2d_ECPrivateKey"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_i2o_ECPublicKey, 0), "i2o_ECPublicKey"},
+  {ERR_PACK(ERR_LIB_EC, EC_F_o2i_ECPublicKey, 0), "o2i_ECPublicKey"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_BUFFER_TOO_SMALL), "BUFFER_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_COORDINATES_OUT_OF_RANGE), "COORDINATES_OUT_OF_RANGE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_D2I_ECPKPARAMETERS_FAILURE), "D2I_ECPKPARAMETERS_FAILURE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_EC_GROUP_NEW_BY_NAME_FAILURE), "EC_GROUP_NEW_BY_NAME_FAILURE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_GF2M_NOT_SUPPORTED), "GF2M_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_GROUP2PKPARAMETERS_FAILURE), "GROUP2PKPARAMETERS_FAILURE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_I2D_ECPKPARAMETERS_FAILURE), "I2D_ECPKPARAMETERS_FAILURE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INCOMPATIBLE_OBJECTS), "INCOMPATIBLE_OBJECTS"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_COMPRESSED_POINT), "INVALID_COMPRESSED_POINT"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_COMPRESSION_BIT), "INVALID_COMPRESSION_BIT"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_ENCODING), "INVALID_ENCODING"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_FIELD), "INVALID_FIELD"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_FORM), "INVALID_FORM"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_GROUP_ORDER), "INVALID_GROUP_ORDER"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_PRIVATE_KEY), "INVALID_PRIVATE_KEY"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_MISSING_PARAMETERS), "MISSING_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_MISSING_PRIVATE_KEY), "MISSING_PRIVATE_KEY"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_NON_NAMED_CURVE), "NON_NAMED_CURVE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_NOT_INITIALIZED), "NOT_INITIALIZED"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_PKPARAMETERS2GROUP_FAILURE), "PKPARAMETERS2GROUP_FAILURE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_POINT_AT_INFINITY), "POINT_AT_INFINITY"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_POINT_IS_NOT_ON_CURVE), "POINT_IS_NOT_ON_CURVE"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_SLOT_FULL), "SLOT_FULL"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_UNDEFINED_GENERATOR), "UNDEFINED_GENERATOR"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_UNKNOWN_GROUP), "UNKNOWN_GROUP"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_UNKNOWN_ORDER), "UNKNOWN_ORDER"},
+  {ERR_PACK(ERR_LIB_EC, 0, EC_R_WRONG_ORDER), "WRONG_ORDER"},
+  {0, NULL},
+};
diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c
new file mode 100644
index 0000000..9bef698
--- /dev/null
+++ b/crypto/ec/ec_key.c
@@ -0,0 +1,513 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <openssl/ec_key.h>
+
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/ex_data.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+EC_KEY *EC_KEY_new(void) { return EC_KEY_new_method(NULL); }
+
+EC_KEY *EC_KEY_new_method(const ENGINE *engine) {
+  EC_KEY *ret = (EC_KEY *)OPENSSL_malloc(sizeof(EC_KEY));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_new_method, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(ret, 0, sizeof(EC_KEY));
+
+  if (engine) {
+    ret->ecdsa_meth = ENGINE_get_ECDSA_method(engine);
+  }
+  if (ret->ecdsa_meth) {
+    METHOD_ref(ret->ecdsa_meth);
+  }
+
+  ret->version = 1;
+  ret->conv_form = POINT_CONVERSION_UNCOMPRESSED;
+  ret->references = 1;
+
+  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_EC_KEY, ret, &ret->ex_data)) {
+    goto err1;
+  }
+
+  if (ret->ecdsa_meth && ret->ecdsa_meth->init && !ret->ecdsa_meth->init(ret)) {
+    goto err2;
+  }
+
+  return ret;
+
+err2:
+  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, ret, &ret->ex_data);
+err1:
+  if (ret->ecdsa_meth) {
+    METHOD_unref(ret->ecdsa_meth);
+  }
+  OPENSSL_free(ret);
+  return NULL;
+}
+
+EC_KEY *EC_KEY_new_by_curve_name(int nid) {
+  EC_KEY *ret = EC_KEY_new();
+  if (ret == NULL) {
+    return NULL;
+  }
+  ret->group = EC_GROUP_new_by_curve_name(nid);
+  if (ret->group == NULL) {
+    EC_KEY_free(ret);
+    return NULL;
+  }
+  return ret;
+}
+
+void EC_KEY_free(EC_KEY *r) {
+  if (r == NULL) {
+    return;
+  }
+
+  if (CRYPTO_add(&r->references, -1, CRYPTO_LOCK_EC)) {
+    return;
+  }
+
+  if (r->ecdsa_meth) {
+    if (r->ecdsa_meth->finish) {
+      r->ecdsa_meth->finish(r);
+    }
+    METHOD_unref(r->ecdsa_meth);
+  }
+
+  if (r->group != NULL) {
+    EC_GROUP_free(r->group);
+  }
+  if (r->pub_key != NULL) {
+    EC_POINT_free(r->pub_key);
+  }
+  if (r->priv_key != NULL) {
+    BN_clear_free(r->priv_key);
+  }
+
+  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, r, &r->ex_data);
+
+  OPENSSL_cleanse((void *)r, sizeof(EC_KEY));
+  OPENSSL_free(r);
+}
+
+EC_KEY *EC_KEY_copy(EC_KEY *dest, const EC_KEY *src) {
+  if (dest == NULL || src == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_copy, ERR_R_PASSED_NULL_PARAMETER);
+    return NULL;
+  }
+  /* copy the parameters */
+  if (src->group) {
+    /* TODO(fork): duplicating the group seems wasteful. */
+    const EC_METHOD *meth = src->group->meth;
+    /* clear the old group */
+    if (dest->group) {
+      EC_GROUP_free(dest->group);
+    }
+    dest->group = ec_group_new(meth);
+    if (dest->group == NULL) {
+      return NULL;
+    }
+    if (!EC_GROUP_copy(dest->group, src->group)) {
+      return NULL;
+    }
+  }
+
+  /*  copy the public key */
+  if (src->pub_key && src->group) {
+    if (dest->pub_key) {
+      EC_POINT_free(dest->pub_key);
+    }
+    dest->pub_key = EC_POINT_new(src->group);
+    if (dest->pub_key == NULL) {
+      return NULL;
+    }
+    if (!EC_POINT_copy(dest->pub_key, src->pub_key)) {
+      return NULL;
+    }
+  }
+
+  /* copy the private key */
+  if (src->priv_key) {
+    if (dest->priv_key == NULL) {
+      dest->priv_key = BN_new();
+      if (dest->priv_key == NULL) {
+        return NULL;
+      }
+    }
+    if (!BN_copy(dest->priv_key, src->priv_key)) {
+      return NULL;
+    }
+  }
+  /* copy method/extra data */
+  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, dest, &dest->ex_data);
+  if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_EC_KEY, &dest->ex_data,
+                          &src->ex_data)) {
+    return NULL;
+  }
+
+  /* copy the rest */
+  dest->enc_flag = src->enc_flag;
+  dest->conv_form = src->conv_form;
+  dest->version = src->version;
+  dest->flags = src->flags;
+
+  return dest;
+}
+
+EC_KEY *EC_KEY_dup(const EC_KEY *ec_key) {
+  EC_KEY *ret = EC_KEY_new();
+  if (ret == NULL) {
+    return NULL;
+  }
+  if (EC_KEY_copy(ret, ec_key) == NULL) {
+    EC_KEY_free(ret);
+    return NULL;
+  }
+  return ret;
+}
+
+int EC_KEY_up_ref(EC_KEY *r) {
+  return CRYPTO_add(&r->references, 1, CRYPTO_LOCK_EC) > 1;
+}
+
+const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key) { return key->group; }
+
+int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group) {
+  if (key->group != NULL) {
+    EC_GROUP_free(key->group);
+  }
+  /* TODO(fork): duplicating the group seems wasteful but see
+   * |EC_KEY_set_conv_form|. */
+  key->group = EC_GROUP_dup(group);
+  return (key->group == NULL) ? 0 : 1;
+}
+
+const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key) {
+  return key->priv_key;
+}
+
+int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key) {
+  if (key->priv_key) {
+    BN_clear_free(key->priv_key);
+  }
+  key->priv_key = BN_dup(priv_key);
+  return (key->priv_key == NULL) ? 0 : 1;
+}
+
+const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key) {
+  return key->pub_key;
+}
+
+int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key) {
+  if (key->pub_key != NULL) {
+    EC_POINT_free(key->pub_key);
+  }
+  key->pub_key = EC_POINT_dup(pub_key, key->group);
+  return (key->pub_key == NULL) ? 0 : 1;
+}
+
+unsigned int EC_KEY_get_enc_flags(const EC_KEY *key) { return key->enc_flag; }
+
+void EC_KEY_set_enc_flags(EC_KEY *key, unsigned int flags) {
+  key->enc_flag = flags;
+}
+
+point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key) {
+  return key->conv_form;
+}
+
+void EC_KEY_set_conv_form(EC_KEY *key, point_conversion_form_t cform) {
+  key->conv_form = cform;
+  if (key->group != NULL) {
+    EC_GROUP_set_point_conversion_form(key->group, cform);
+  }
+}
+
+int EC_KEY_precompute_mult(EC_KEY *key, BN_CTX *ctx) {
+  if (key->group == NULL) {
+    return 0;
+  }
+  return EC_GROUP_precompute_mult(key->group, ctx);
+}
+
+int EC_KEY_check_key(const EC_KEY *eckey) {
+  int ok = 0;
+  BN_CTX *ctx = NULL;
+  const BIGNUM *order = NULL;
+  EC_POINT *point = NULL;
+
+  if (!eckey || !eckey->group || !eckey->pub_key) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  if (EC_POINT_is_at_infinity(eckey->group, eckey->pub_key)) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, EC_R_POINT_AT_INFINITY);
+    goto err;
+  }
+
+  ctx = BN_CTX_new();
+  point = EC_POINT_new(eckey->group);
+
+  if (ctx == NULL ||
+      point == NULL) {
+    goto err;
+  }
+
+  /* testing whether the pub_key is on the elliptic curve */
+  if (!EC_POINT_is_on_curve(eckey->group, eckey->pub_key, ctx)) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, EC_R_POINT_IS_NOT_ON_CURVE);
+    goto err;
+  }
+  /* testing whether pub_key * order is the point at infinity */
+  /* TODO(fork): can this be skipped if the cofactor is one or if we're about
+   * to check the private key, below? */
+  order = &eckey->group->order;
+  if (BN_is_zero(order)) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, EC_R_INVALID_GROUP_ORDER);
+    goto err;
+  }
+  if (!EC_POINT_mul(eckey->group, point, NULL, eckey->pub_key, order, ctx)) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, ERR_R_EC_LIB);
+    goto err;
+  }
+  if (!EC_POINT_is_at_infinity(eckey->group, point)) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, EC_R_WRONG_ORDER);
+    goto err;
+  }
+  /* in case the priv_key is present :
+   * check if generator * priv_key == pub_key
+   */
+  if (eckey->priv_key) {
+    if (BN_cmp(eckey->priv_key, order) >= 0) {
+      OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, EC_R_WRONG_ORDER);
+      goto err;
+    }
+    if (!EC_POINT_mul(eckey->group, point, eckey->priv_key, NULL, NULL, ctx)) {
+      OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, ERR_R_EC_LIB);
+      goto err;
+    }
+    if (EC_POINT_cmp(eckey->group, point, eckey->pub_key, ctx) != 0) {
+      OPENSSL_PUT_ERROR(EC, EC_KEY_check_key, EC_R_INVALID_PRIVATE_KEY);
+      goto err;
+    }
+  }
+  ok = 1;
+
+err:
+  if (ctx != NULL)
+    BN_CTX_free(ctx);
+  if (point != NULL)
+    EC_POINT_free(point);
+  return ok;
+}
+
+int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, BIGNUM *x,
+                                             BIGNUM *y) {
+  BN_CTX *ctx = NULL;
+  BIGNUM *tx, *ty;
+  EC_POINT *point = NULL;
+  int ok = 0;
+
+  if (!key || !key->group || !x || !y) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_set_public_key_affine_coordinates,
+                      ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  ctx = BN_CTX_new();
+  point = EC_POINT_new(key->group);
+
+  if (ctx == NULL ||
+      point == NULL) {
+    goto err;
+  }
+
+  tx = BN_CTX_get(ctx);
+  ty = BN_CTX_get(ctx);
+
+  if (!EC_POINT_set_affine_coordinates_GFp(key->group, point, x, y, ctx) ||
+      !EC_POINT_get_affine_coordinates_GFp(key->group, point, tx, ty, ctx)) {
+    goto err;
+  }
+
+  /* Check if retrieved coordinates match originals: if not values
+   * are out of range. */
+  if (BN_cmp(x, tx) || BN_cmp(y, ty)) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_set_public_key_affine_coordinates,
+                      EC_R_COORDINATES_OUT_OF_RANGE);
+    goto err;
+  }
+
+  if (!EC_KEY_set_public_key(key, point)) {
+    goto err;
+  }
+
+  if (EC_KEY_check_key(key) == 0) {
+    goto err;
+  }
+
+  ok = 1;
+
+err:
+  if (ctx)
+    BN_CTX_free(ctx);
+  if (point)
+    EC_POINT_free(point);
+  return ok;
+}
+
+int EC_KEY_generate_key(EC_KEY *eckey) {
+  int ok = 0;
+  BN_CTX *ctx = NULL;
+  BIGNUM *priv_key = NULL, *order = NULL;
+  EC_POINT *pub_key = NULL;
+
+  if (!eckey || !eckey->group) {
+    OPENSSL_PUT_ERROR(EC, EC_KEY_generate_key, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  order = BN_new();
+  ctx = BN_CTX_new();
+
+  if (order == NULL ||
+      ctx == NULL) {
+    goto err;
+  }
+
+  if (eckey->priv_key == NULL) {
+    priv_key = BN_new();
+    if (priv_key == NULL) {
+      goto err;
+    }
+  } else {
+    priv_key = eckey->priv_key;
+  }
+
+  if (!EC_GROUP_get_order(eckey->group, order, ctx)) {
+    goto err;
+  }
+
+  do {
+    if (!BN_rand_range(priv_key, order)) {
+      goto err;
+    }
+  } while (BN_is_zero(priv_key));
+
+  if (eckey->pub_key == NULL) {
+    pub_key = EC_POINT_new(eckey->group);
+    if (pub_key == NULL) {
+      goto err;
+    }
+  } else {
+    pub_key = eckey->pub_key;
+  }
+
+  if (!EC_POINT_mul(eckey->group, pub_key, priv_key, NULL, NULL, ctx)) {
+    goto err;
+  }
+
+  eckey->priv_key = priv_key;
+  eckey->pub_key = pub_key;
+
+  ok = 1;
+
+err:
+  if (order)
+    BN_free(order);
+  if (pub_key != NULL && eckey->pub_key == NULL)
+    EC_POINT_free(pub_key);
+  if (priv_key != NULL && eckey->priv_key == NULL)
+    BN_free(priv_key);
+  if (ctx != NULL)
+    BN_CTX_free(ctx);
+  return ok;
+}
+
+int EC_KEY_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                            CRYPTO_EX_dup *dup_func,
+                            CRYPTO_EX_free *free_func) {
+  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_EC_KEY, argl, argp, new_func,
+                                 dup_func, free_func);
+}
+
+int EC_KEY_set_ex_data(EC_KEY *d, int idx, void *arg) {
+  return CRYPTO_set_ex_data(&d->ex_data, idx, arg);
+}
+
+void *EC_KEY_get_ex_data(const EC_KEY *d, int idx) {
+  return CRYPTO_get_ex_data(&d->ex_data, idx);
+}
diff --git a/crypto/ec/ec_key.h b/crypto/ec/ec_key.h
new file mode 100644
index 0000000..ce4846b
--- /dev/null
+++ b/crypto/ec/ec_key.h
@@ -0,0 +1,260 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#ifndef OPENSSL_HEADER_EC_KEY_H
+#define OPENSSL_HEADER_EC_KEY_H
+
+#include <openssl/base.h>
+
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* ec_key.h conatins functions that handle elliptic-curve points that are
+ * public/private keys. */
+
+
+/* EC key objects. */
+
+/* EC_KEY_new returns a fresh |EC_KEY| object or NULL on error. */
+EC_KEY *EC_KEY_new(void);
+
+/* EC_KEY_new_method acts the same as |EC_KEY_new|, but takes an explicit
+ * |ENGINE|. */
+EC_KEY *EC_KEY_new_method(const ENGINE *engine);
+
+/* EC_KEY_new_by_curve_name returns a fresh EC_KEY for group specified by |nid|
+ * or NULL on error. */
+EC_KEY *EC_KEY_new_by_curve_name(int nid);
+
+/* EC_KEY_free frees all the data owned by |key| and |key| itself. */
+void EC_KEY_free(EC_KEY *key);
+
+/* EC_KEY_copy sets |dst| equal to |src| and returns |dst| or NULL on error. */
+EC_KEY *EC_KEY_copy(EC_KEY *dst, const EC_KEY *src);
+
+/* EC_KEY_dup returns a fresh copy of |src| or NULL on error. */
+EC_KEY *EC_KEY_dup(const EC_KEY *src);
+
+/* EC_KEY_up_ref increases the reference count of |key|. It returns one on
+ * success and zero otherwise. */
+int EC_KEY_up_ref(EC_KEY *key);
+
+/* EC_KEY_get0_group returns a pointer to the |EC_GROUP| object inside |key|. */
+const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);
+
+/* EC_KEY_set_group sets the |EC_GROUP| object that |key| will use to |group|.
+ * It returns one on success and zero otherwise. */
+int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group);
+
+/* EC_KEY_get0_private_key returns a pointer to the private key inside |key|. */
+const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key);
+
+/* EC_KEY_set_private_key sets the private key of |key| to |priv|. It returns
+ * one on success and zero otherwise. */
+int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv);
+
+/* EC_KEY_get0_public_key returns a pointer to the public key point inside
+ * |key|. */
+const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);
+
+/* EC_KEY_set_public_key sets the public key of |key| to |pub|, by copying it.
+ * It returns one on success and zero otherwise. */
+int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);
+
+#define EC_PKEY_NO_PARAMETERS 0x001
+#define EC_PKEY_NO_PUBKEY 0x002
+
+/* EC_KEY_get_enc_flags returns the encoding flags for |key|, which is a
+ * bitwise-OR of |EC_PKEY_*| values. */
+unsigned EC_KEY_get_enc_flags(const EC_KEY *key);
+
+/* EC_KEY_set_enc_flags sets the encoding flags for |key|, which is a
+ * bitwise-OR of |EC_PKEY_*| values. */
+void EC_KEY_set_enc_flags(EC_KEY *key, unsigned flags);
+
+/* EC_KEY_get_conv_form returns the conversation form that will be used by
+ * |key|. */
+point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key);
+
+/* EC_KEY_set_conv_form sets the conversion form to be used by |key|. */
+void EC_KEY_set_conv_form(EC_KEY *key, point_conversion_form_t cform);
+
+/* EC_KEY_precompute_mult precomputes multiplies of the generator of the
+ * underlying group in order to speed up operations that calculate generator
+ * multiples. If |ctx| is not NULL, it may be used. It returns one on success
+ * and zero otherwise. */
+int EC_KEY_precompute_mult(EC_KEY *key, BN_CTX *ctx);
+
+/* EC_KEY_check_key performs several checks on |key| (possibly including an
+ * expensive check that the public key is in the primary subgroup). It returns
+ * one if all checks pass and zero otherwise. If it returns zero then detail
+ * about the problem can be found on the error stack. */
+int EC_KEY_check_key(const EC_KEY *key);
+
+/* EC_KEY_set_public_key_affine_coordinates sets the public key in |key| to
+ * (|x|, |y|). It returns one on success and zero otherwise. */
+int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, BIGNUM *x, BIGNUM *y);
+
+
+/* Key generation. */
+
+/* EC_KEY_generate_key generates a random, private key, calculates the
+ * corresponding public key and stores both in |key|. It returns one on success
+ * or zero otherwise. */
+int EC_KEY_generate_key(EC_KEY *key);
+
+
+/* Serialisation. */
+
+/* d2i_ECPrivateKey parses an ASN.1, DER-encoded, private key from |len| bytes
+ * at |*inp|. If |out_key| is not NULL then, on exit, a pointer to the result
+ * is in |*out_key|. If |*out_key| is already non-NULL on entry then the result
+ * is written directly into |*out_key|, otherwise a fresh |EC_KEY| is
+ * allocated. On successful exit, |*inp| is advanced past the DER structure. It
+ * returns the result or NULL on error. */
+EC_KEY *d2i_ECPrivateKey(EC_KEY **out_key, const uint8_t **inp, long len);
+
+/* i2d_ECParameters marshals an EC private key from |key| to an ASN.1, DER
+ * structure. If |outp| is not NULL then the result is written to |*outp| and
+ * |*outp| is advanced just past the output. It returns the number of bytes in
+ * the result, whether written or not, or a negative value on error. */
+int i2d_ECPrivateKey(const EC_KEY *key, uint8_t **outp);
+
+/* d2i_ECParameters parses an ASN.1, DER-encoded, set of EC parameters from
+ * |len| bytes at |*inp|. If |out_key| is not NULL then, on exit, a pointer to
+ * the result is in |*out_key|. If |*out_key| is already non-NULL on entry then
+ * the result is written directly into |*out_key|, otherwise a fresh |EC_KEY|
+ * is allocated. On successful exit, |*inp| is advanced past the DER structure.
+ * It returns the result or NULL on error. */
+EC_KEY *d2i_ECParameters(EC_KEY **out_key, const uint8_t **inp, long len);
+
+/* i2d_ECParameters marshals EC parameters from |key| to an ASN.1, DER
+ * structure. If |outp| is not NULL then the result is written to |*outp| and
+ * |*outp| is advanced just past the output. It returns the number of bytes in
+ * the result, whether written or not, or a negative value on error. */
+int i2d_ECParameters(const EC_KEY *key, uint8_t **outp);
+
+/* o2i_ECPublicKey parses an EC point from |len| bytes at |*inp| into
+ * |*out_key|. Note that this differs from the d2i format in that |*out_key|
+ * must be non-NULL. On successful exit, |*inp| is advanced past the DER
+ * structure. It returns |*out_key| or NULL on error. */
+EC_KEY *o2i_ECPublicKey(EC_KEY **out_key, const uint8_t **inp, long len);
+
+/* i2o_ECPublicKey marshals an EC point from |key|. If |outp| is not NULL then
+ * the result is written to |*outp| and |*outp| is advanced just past the
+ * output. It returns the number of bytes in the result, whether written or
+ * not, or a negative value on error. */
+int i2o_ECPublicKey(const EC_KEY *key, unsigned char **outp);
+
+
+/* ex_data functions.
+ *
+ * These functions are wrappers. See |ex_data.h| for details. */
+
+int EC_KEY_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                            CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int EC_KEY_set_ex_data(EC_KEY *r, int idx, void *arg);
+void *EC_KEY_get_ex_data(const EC_KEY *r, int idx);
+
+
+/* ECDSA method. */
+
+/* ecdsa_method_st is a structure of function pointers for implementing ECDSA.
+ * See engine.h. */
+struct ecdsa_method_st {
+  struct openssl_method_common_st common;
+
+  void *app_data;
+
+  int (*init)(EC_KEY *key);
+  int (*finish)(EC_KEY *key);
+
+  /* size returns the maximum size of the DER encoded, ECDSA signature
+   * resulting from |key|. */
+  size_t (*size)(const EC_KEY *key);
+
+  /* sign matches the arguments and behaviour of |ECDSA_sign|. */
+  int (*sign)(const uint8_t *digest, size_t digest_len, uint8_t *sig,
+              unsigned int *sig_len, EC_KEY *eckey);
+
+  /* verify matches the arguments and behaviour of |ECDSA_verify|. */
+  int (*verify)(const uint8_t *digest, size_t digest_len, const uint8_t *sig,
+                size_t sig_len, EC_KEY *eckey);
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_EC_KEY_H */
diff --git a/crypto/ec/ec_montgomery.c b/crypto/ec/ec_montgomery.c
new file mode 100644
index 0000000..ab04556
--- /dev/null
+++ b/crypto/ec/ec_montgomery.c
@@ -0,0 +1,296 @@
+/* Originally written by Bodo Moeller and Nils Larsch for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <openssl/ec.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+const EC_METHOD *EC_GFp_mont_method(void) {
+  static const EC_METHOD ret = {EC_FLAGS_DEFAULT_OCT,
+                                ec_GFp_mont_group_init,
+                                ec_GFp_mont_group_finish,
+                                ec_GFp_mont_group_clear_finish,
+                                ec_GFp_mont_group_copy,
+                                ec_GFp_mont_group_set_curve,
+                                ec_GFp_simple_group_get_curve,
+                                ec_GFp_simple_group_get_degree,
+                                ec_GFp_simple_group_check_discriminant,
+                                ec_GFp_simple_point_init,
+                                ec_GFp_simple_point_finish,
+                                ec_GFp_simple_point_clear_finish,
+                                ec_GFp_simple_point_copy,
+                                ec_GFp_simple_point_set_to_infinity,
+                                ec_GFp_simple_set_Jprojective_coordinates_GFp,
+                                ec_GFp_simple_get_Jprojective_coordinates_GFp,
+                                ec_GFp_simple_point_set_affine_coordinates,
+                                ec_GFp_simple_point_get_affine_coordinates,
+                                0,
+                                0,
+                                0,
+                                ec_GFp_simple_add,
+                                ec_GFp_simple_dbl,
+                                ec_GFp_simple_invert,
+                                ec_GFp_simple_is_at_infinity,
+                                ec_GFp_simple_is_on_curve,
+                                ec_GFp_simple_cmp,
+                                ec_GFp_simple_make_affine,
+                                ec_GFp_simple_points_make_affine,
+                                0 /* mul */,
+                                0 /* precompute_mult */,
+                                0 /* have_precompute_mult */,
+                                ec_GFp_mont_field_mul,
+                                ec_GFp_mont_field_sqr,
+                                0 /* field_div */,
+                                ec_GFp_mont_field_encode,
+                                ec_GFp_mont_field_decode,
+                                ec_GFp_mont_field_set_to_one};
+
+  return &ret;
+}
+
+int ec_GFp_mont_group_init(EC_GROUP *group) {
+  int ok;
+
+  ok = ec_GFp_simple_group_init(group);
+  group->field_data1 = NULL;
+  group->field_data2 = NULL;
+  return ok;
+}
+
+void ec_GFp_mont_group_finish(EC_GROUP *group) {
+  if (group->field_data1 != NULL) {
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+  }
+  if (group->field_data2 != NULL) {
+    BN_free(group->field_data2);
+    group->field_data2 = NULL;
+  }
+  ec_GFp_simple_group_finish(group);
+}
+
+void ec_GFp_mont_group_clear_finish(EC_GROUP *group) {
+  if (group->field_data1 != NULL) {
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+  }
+  if (group->field_data2 != NULL) {
+    BN_clear_free(group->field_data2);
+    group->field_data2 = NULL;
+  }
+  ec_GFp_simple_group_clear_finish(group);
+}
+
+int ec_GFp_mont_group_copy(EC_GROUP *dest, const EC_GROUP *src) {
+  if (dest->field_data1 != NULL) {
+    BN_MONT_CTX_free(dest->field_data1);
+    dest->field_data1 = NULL;
+  }
+  if (dest->field_data2 != NULL) {
+    BN_clear_free(dest->field_data2);
+    dest->field_data2 = NULL;
+  }
+
+  if (!ec_GFp_simple_group_copy(dest, src))
+    return 0;
+
+  if (src->field_data1 != NULL) {
+    dest->field_data1 = BN_MONT_CTX_new();
+    if (dest->field_data1 == NULL)
+      return 0;
+    if (!BN_MONT_CTX_copy(dest->field_data1, src->field_data1))
+      goto err;
+  }
+  if (src->field_data2 != NULL) {
+    dest->field_data2 = BN_dup(src->field_data2);
+    if (dest->field_data2 == NULL)
+      goto err;
+  }
+
+  return 1;
+
+err:
+  if (dest->field_data1 != NULL) {
+    BN_MONT_CTX_free(dest->field_data1);
+    dest->field_data1 = NULL;
+  }
+  return 0;
+}
+
+int ec_GFp_mont_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  BN_MONT_CTX *mont = NULL;
+  BIGNUM *one = NULL;
+  int ret = 0;
+
+  if (group->field_data1 != NULL) {
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+  }
+  if (group->field_data2 != NULL) {
+    BN_free(group->field_data2);
+    group->field_data2 = NULL;
+  }
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  mont = BN_MONT_CTX_new();
+  if (mont == NULL)
+    goto err;
+  if (!BN_MONT_CTX_set(mont, p, ctx)) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_mont_group_set_curve, ERR_R_BN_LIB);
+    goto err;
+  }
+  one = BN_new();
+  if (one == NULL)
+    goto err;
+  if (!BN_to_montgomery(one, BN_value_one(), mont, ctx))
+    goto err;
+
+  group->field_data1 = mont;
+  mont = NULL;
+  group->field_data2 = one;
+  one = NULL;
+
+  ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+
+  if (!ret) {
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+    BN_free(group->field_data2);
+    group->field_data2 = NULL;
+  }
+
+err:
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  if (mont != NULL)
+    BN_MONT_CTX_free(mont);
+  return ret;
+}
+
+int ec_GFp_mont_field_mul(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                          const BIGNUM *b, BN_CTX *ctx) {
+  if (group->field_data1 == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_mont_field_mul, EC_R_NOT_INITIALIZED);
+    return 0;
+  }
+
+  return BN_mod_mul_montgomery(r, a, b, group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_sqr(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                          BN_CTX *ctx) {
+  if (group->field_data1 == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_mont_field_sqr, EC_R_NOT_INITIALIZED);
+    return 0;
+  }
+
+  return BN_mod_mul_montgomery(r, a, a, group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_encode(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *ctx) {
+  if (group->field_data1 == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_mont_field_encode, EC_R_NOT_INITIALIZED);
+    return 0;
+  }
+
+  return BN_to_montgomery(r, a, (BN_MONT_CTX *)group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_decode(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *ctx) {
+  if (group->field_data1 == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_mont_field_decode, EC_R_NOT_INITIALIZED);
+    return 0;
+  }
+
+  return BN_from_montgomery(r, a, group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_set_to_one(const EC_GROUP *group, BIGNUM *r,
+                                 BN_CTX *ctx) {
+  if (group->field_data2 == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_mont_field_set_to_one, EC_R_NOT_INITIALIZED);
+    return 0;
+  }
+
+  if (!BN_copy(r, group->field_data2))
+    return 0;
+  return 1;
+}
diff --git a/crypto/ec/example_mul.c b/crypto/ec/example_mul.c
new file mode 100644
index 0000000..1e83d61
--- /dev/null
+++ b/crypto/ec/example_mul.c
@@ -0,0 +1,130 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <stdio.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/obj.h>
+
+
+int example_EC_POINT_mul() {
+  /* This example ensures that 10×∞ + G = G, in P-256. */
+  EC_GROUP *group = NULL;
+  EC_POINT *p = NULL, *result = NULL;
+  BIGNUM *n = NULL;
+  int ret = 0;
+  const EC_POINT *generator;
+
+  group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+  p = EC_POINT_new(group);
+  result = EC_POINT_new(group);
+  n = BN_new();
+
+  if (p == NULL ||
+      result == NULL ||
+      group == NULL ||
+      n == NULL ||
+      !EC_POINT_set_to_infinity(group, p) ||
+      !BN_set_word(n, 10)) {
+    goto err;
+  }
+
+  /* First check that 10×∞ = ∞. */
+  if (!EC_POINT_mul(group, result, NULL, p, n, NULL) ||
+      !EC_POINT_is_at_infinity(group, result)) {
+    goto err;
+  }
+
+  generator = EC_GROUP_get0_generator(group);
+
+  /* Now check that 10×∞ + G = G. */
+  if (!EC_POINT_mul(group, result, BN_value_one(), p, n, NULL) ||
+      EC_POINT_cmp(group, result, generator, NULL) != 0) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_free(n);
+  EC_POINT_free(result);
+  EC_POINT_free(p);
+  EC_GROUP_free(group);
+
+  return ret;
+}
+
+int main() {
+  if (!example_EC_POINT_mul()) {
+    fprintf(stderr, "failed\n");
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/ec/internal.h b/crypto/ec/internal.h
new file mode 100644
index 0000000..dc79186
--- /dev/null
+++ b/crypto/ec/internal.h
@@ -0,0 +1,357 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#ifndef OPENSSL_HEADER_EC_INTERNAL_H
+#define OPENSSL_HEADER_EC_INTERNAL_H
+
+#include <openssl/base.h>
+
+#include <openssl/bn.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Use default functions for poin2oct, oct2point and compressed coordinates */
+#define EC_FLAGS_DEFAULT_OCT 0x1
+
+typedef struct ec_method_st EC_METHOD;
+
+struct ec_method_st {
+  /* Various method flags */
+  int flags;
+
+  /* used by EC_GROUP_new, EC_GROUP_free, EC_GROUP_clear_free, EC_GROUP_copy: */
+  int (*group_init)(EC_GROUP *);
+  void (*group_finish)(EC_GROUP *);
+  void (*group_clear_finish)(EC_GROUP *);
+  int (*group_copy)(EC_GROUP *, const EC_GROUP *);
+
+  /* used by EC_GROUP_set_curve_GFp, EC_GROUP_get_curve_GFp, */
+  /* EC_GROUP_set_curve_GF2m, and EC_GROUP_get_curve_GF2m: */
+  int (*group_set_curve)(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
+                         const BIGNUM *b, BN_CTX *);
+  int (*group_get_curve)(const EC_GROUP *, BIGNUM *p, BIGNUM *a, BIGNUM *b,
+                         BN_CTX *);
+
+  /* used by EC_GROUP_get_degree: */
+  int (*group_get_degree)(const EC_GROUP *);
+
+  /* used by EC_GROUP_check: */
+  int (*group_check_discriminant)(const EC_GROUP *, BN_CTX *);
+
+  /* used by EC_POINT_new, EC_POINT_free, EC_POINT_clear_free, EC_POINT_copy: */
+  int (*point_init)(EC_POINT *);
+  void (*point_finish)(EC_POINT *);
+  void (*point_clear_finish)(EC_POINT *);
+  int (*point_copy)(EC_POINT *, const EC_POINT *);
+
+  /* used by EC_POINT_set_to_infinity,
+   * EC_POINT_set_Jprojective_coordinates_GFp,
+   * EC_POINT_get_Jprojective_coordinates_GFp,
+   * EC_POINT_set_affine_coordinates_GFp,     ..._GF2m,
+   * EC_POINT_get_affine_coordinates_GFp,     ..._GF2m,
+   * EC_POINT_set_compressed_coordinates_GFp, ..._GF2m:
+   */
+  int (*point_set_to_infinity)(const EC_GROUP *, EC_POINT *);
+  int (*point_set_Jprojective_coordinates_GFp)(const EC_GROUP *, EC_POINT *,
+                                               const BIGNUM *x, const BIGNUM *y,
+                                               const BIGNUM *z, BN_CTX *);
+  int (*point_get_Jprojective_coordinates_GFp)(const EC_GROUP *,
+                                               const EC_POINT *, BIGNUM *x,
+                                               BIGNUM *y, BIGNUM *z, BN_CTX *);
+  int (*point_set_affine_coordinates)(const EC_GROUP *, EC_POINT *,
+                                      const BIGNUM *x, const BIGNUM *y,
+                                      BN_CTX *);
+  int (*point_get_affine_coordinates)(const EC_GROUP *, const EC_POINT *,
+                                      BIGNUM *x, BIGNUM *y, BN_CTX *);
+  int (*point_set_compressed_coordinates)(const EC_GROUP *, EC_POINT *,
+                                          const BIGNUM *x, int y_bit, BN_CTX *);
+
+  /* used by EC_POINT_point2oct, EC_POINT_oct2point: */
+  size_t (*point2oct)(const EC_GROUP *, const EC_POINT *,
+                      point_conversion_form_t form, unsigned char *buf,
+                      size_t len, BN_CTX *);
+  int (*oct2point)(const EC_GROUP *, EC_POINT *, const unsigned char *buf,
+                   size_t len, BN_CTX *);
+
+  /* used by EC_POINT_add, EC_POINT_dbl, ECP_POINT_invert: */
+  int (*add)(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+             const EC_POINT *b, BN_CTX *);
+  int (*dbl)(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, BN_CTX *);
+  int (*invert)(const EC_GROUP *, EC_POINT *, BN_CTX *);
+
+  /* used by EC_POINT_is_at_infinity, EC_POINT_is_on_curve, EC_POINT_cmp: */
+  int (*is_at_infinity)(const EC_GROUP *, const EC_POINT *);
+  int (*is_on_curve)(const EC_GROUP *, const EC_POINT *, BN_CTX *);
+  int (*point_cmp)(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b,
+                   BN_CTX *);
+
+  /* used by EC_POINT_make_affine, EC_POINTs_make_affine: */
+  int (*make_affine)(const EC_GROUP *, EC_POINT *, BN_CTX *);
+  int (*points_make_affine)(const EC_GROUP *, size_t num, EC_POINT * [],
+                            BN_CTX *);
+
+  /* used by EC_POINTs_mul, EC_POINT_mul, EC_POINT_precompute_mult,
+   * EC_POINT_have_precompute_mult
+   * (default implementations are used if the 'mul' pointer is 0): */
+  int (*mul)(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+             size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+             BN_CTX *);
+  int (*precompute_mult)(EC_GROUP *group, BN_CTX *);
+  int (*have_precompute_mult)(const EC_GROUP *group);
+
+
+  /* internal functions */
+
+  /* 'field_mul', 'field_sqr', and 'field_div' can be used by 'add' and 'dbl'
+   * so that the same implementations of point operations can be used with
+   * different optimized implementations of expensive field operations: */
+  int (*field_mul)(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                   const BIGNUM *b, BN_CTX *);
+  int (*field_sqr)(const EC_GROUP *, BIGNUM *r, const BIGNUM *a, BN_CTX *);
+  int (*field_div)(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                   const BIGNUM *b, BN_CTX *);
+
+  int (*field_encode)(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                      BN_CTX *); /* e.g. to Montgomery */
+  int (*field_decode)(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                      BN_CTX *); /* e.g. from Montgomery */
+  int (*field_set_to_one)(const EC_GROUP *, BIGNUM *r, BN_CTX *);
+} /* EC_METHOD */;
+
+const EC_METHOD* EC_GFp_mont_method();
+
+struct ec_pre_comp_st;
+void ec_pre_comp_free(struct ec_pre_comp_st *pre_comp);
+void *ec_pre_comp_dup(struct ec_pre_comp_st *pre_comp);
+
+struct ec_group_st {
+  const EC_METHOD *meth;
+
+  EC_POINT *generator; /* optional */
+  BIGNUM order, cofactor;
+
+  int curve_name; /* optional NID for named curve */
+  point_conversion_form_t asn1_form;
+
+  struct ec_pre_comp_st *pre_comp;
+
+  /* The following members are handled by the method functions,
+   * even if they appear generic */
+
+  BIGNUM field; /* Field specification.
+                 * For curves over GF(p), this is the modulus;
+                 * for curves over GF(2^m), this is the
+                 * irreducible polynomial defining the field. */
+
+  int poly[6]; /* Field specification for curves over GF(2^m).
+                * The irreducible f(t) is then of the form:
+                *     t^poly[0] + t^poly[1] + ... + t^poly[k]
+                * where m = poly[0] > poly[1] > ... > poly[k] = 0.
+                * The array is terminated with poly[k+1]=-1.
+                * All elliptic curve irreducibles have at most 5
+                * non-zero terms. */
+
+  BIGNUM a, b; /* Curve coefficients.
+                * (Here the assumption is that BIGNUMs can be used
+                * or abused for all kinds of fields, not just GF(p).)
+                * For characteristic  > 3,  the curve is defined
+                * by a Weierstrass equation of the form
+                *     y^2 = x^3 + a*x + b.
+                * For characteristic  2,  the curve is defined by
+                * an equation of the form
+                *     y^2 + x*y = x^3 + a*x^2 + b. */
+
+  int a_is_minus3; /* enable optimized point arithmetics for special case */
+
+  void *field_data1; /* method-specific (e.g., Montgomery structure) */
+  void *field_data2; /* method-specific */
+  int (*field_mod_func)(BIGNUM *, const BIGNUM *, const BIGNUM *,
+                        BN_CTX *); /* method-specific */
+} /* EC_GROUP */;
+
+struct ec_point_st {
+  const EC_METHOD *meth;
+
+  /* All members except 'meth' are handled by the method functions,
+   * even if they appear generic */
+
+  BIGNUM X;
+  BIGNUM Y;
+  BIGNUM Z; /* Jacobian projective coordinates:
+             * (X, Y, Z)  represents  (X/Z^2, Y/Z^3)  if  Z != 0 */
+  int Z_is_one; /* enable optimized point arithmetics for special case */
+} /* EC_POINT */;
+
+EC_GROUP *ec_group_new(const EC_METHOD *meth);
+
+int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+                BN_CTX *);
+int ec_wNAF_precompute_mult(EC_GROUP *group, BN_CTX *);
+int ec_wNAF_have_precompute_mult(const EC_GROUP *group);
+
+/* method functions in simple.c */
+int ec_GFp_simple_group_init(EC_GROUP *);
+void ec_GFp_simple_group_finish(EC_GROUP *);
+void ec_GFp_simple_group_clear_finish(EC_GROUP *);
+int ec_GFp_simple_group_copy(EC_GROUP *, const EC_GROUP *);
+int ec_GFp_simple_group_set_curve(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
+                                  const BIGNUM *b, BN_CTX *);
+int ec_GFp_simple_group_get_curve(const EC_GROUP *, BIGNUM *p, BIGNUM *a,
+                                  BIGNUM *b, BN_CTX *);
+int ec_GFp_simple_group_get_degree(const EC_GROUP *);
+int ec_GFp_simple_group_check_discriminant(const EC_GROUP *, BN_CTX *);
+int ec_GFp_simple_point_init(EC_POINT *);
+void ec_GFp_simple_point_finish(EC_POINT *);
+void ec_GFp_simple_point_clear_finish(EC_POINT *);
+int ec_GFp_simple_point_copy(EC_POINT *, const EC_POINT *);
+int ec_GFp_simple_point_set_to_infinity(const EC_GROUP *, EC_POINT *);
+int ec_GFp_simple_set_Jprojective_coordinates_GFp(const EC_GROUP *, EC_POINT *,
+                                                  const BIGNUM *x,
+                                                  const BIGNUM *y,
+                                                  const BIGNUM *z, BN_CTX *);
+int ec_GFp_simple_get_Jprojective_coordinates_GFp(const EC_GROUP *,
+                                                  const EC_POINT *, BIGNUM *x,
+                                                  BIGNUM *y, BIGNUM *z,
+                                                  BN_CTX *);
+int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *, EC_POINT *,
+                                               const BIGNUM *x, const BIGNUM *y,
+                                               BN_CTX *);
+int ec_GFp_simple_point_get_affine_coordinates(const EC_GROUP *,
+                                               const EC_POINT *, BIGNUM *x,
+                                               BIGNUM *y, BN_CTX *);
+int ec_GFp_simple_set_compressed_coordinates(const EC_GROUP *, EC_POINT *,
+                                             const BIGNUM *x, int y_bit,
+                                             BN_CTX *);
+int ec_GFp_simple_add(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                      const EC_POINT *b, BN_CTX *);
+int ec_GFp_simple_dbl(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                      BN_CTX *);
+int ec_GFp_simple_invert(const EC_GROUP *, EC_POINT *, BN_CTX *);
+int ec_GFp_simple_is_at_infinity(const EC_GROUP *, const EC_POINT *);
+int ec_GFp_simple_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *);
+int ec_GFp_simple_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b,
+                      BN_CTX *);
+int ec_GFp_simple_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *);
+int ec_GFp_simple_points_make_affine(const EC_GROUP *, size_t num,
+                                     EC_POINT * [], BN_CTX *);
+int ec_GFp_simple_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                            const BIGNUM *b, BN_CTX *);
+int ec_GFp_simple_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                            BN_CTX *);
+
+/* method functions in montgomery.c */
+int ec_GFp_mont_group_init(EC_GROUP *);
+int ec_GFp_mont_group_set_curve(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
+                                const BIGNUM *b, BN_CTX *);
+void ec_GFp_mont_group_finish(EC_GROUP *);
+void ec_GFp_mont_group_clear_finish(EC_GROUP *);
+int ec_GFp_mont_group_copy(EC_GROUP *, const EC_GROUP *);
+int ec_GFp_mont_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                          const BIGNUM *b, BN_CTX *);
+int ec_GFp_mont_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                          BN_CTX *);
+int ec_GFp_mont_field_encode(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *);
+int ec_GFp_mont_field_decode(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *);
+int ec_GFp_mont_field_set_to_one(const EC_GROUP *, BIGNUM *r, BN_CTX *);
+
+int ec_point_set_Jprojective_coordinates_GFp(const EC_GROUP *group,
+                                             EC_POINT *point, const BIGNUM *x,
+                                             const BIGNUM *y, const BIGNUM *z,
+                                             BN_CTX *ctx);
+
+struct ec_key_st {
+  int version;
+
+  EC_GROUP *group;
+
+  EC_POINT *pub_key;
+  BIGNUM *priv_key;
+
+  unsigned int enc_flag;
+  point_conversion_form_t conv_form;
+
+  int references;
+  int flags;
+
+  ECDSA_METHOD *ecdsa_meth;
+
+  CRYPTO_EX_DATA ex_data;
+} /* EC_KEY */;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_EC_INTERNAL_H */
diff --git a/crypto/ec/oct.c b/crypto/ec/oct.c
new file mode 100644
index 0000000..1c5ea14
--- /dev/null
+++ b/crypto/ec/oct.c
@@ -0,0 +1,495 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <openssl/ec.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+
+
+static size_t ec_GFp_simple_point2oct(const EC_GROUP *group,
+                                      const EC_POINT *point,
+                                      point_conversion_form_t form,
+                                      uint8_t *buf, size_t len, BN_CTX *ctx) {
+  size_t ret;
+  BN_CTX *new_ctx = NULL;
+  int used_ctx = 0;
+  BIGNUM *x, *y;
+  size_t field_len, i, skip;
+
+  if ((form != POINT_CONVERSION_COMPRESSED) &&
+      (form != POINT_CONVERSION_UNCOMPRESSED) &&
+      (form != POINT_CONVERSION_HYBRID)) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, EC_R_INVALID_FORM);
+    goto err;
+  }
+
+  if (EC_POINT_is_at_infinity(group, point)) {
+    /* encodes to a single 0 octet */
+    if (buf != NULL) {
+      if (len < 1) {
+        OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, EC_R_BUFFER_TOO_SMALL);
+        return 0;
+      }
+      buf[0] = 0;
+    }
+    return 1;
+  }
+
+
+  /* ret := required output buffer length */
+  field_len = BN_num_bytes(&group->field);
+  ret =
+      (form == POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len;
+
+  /* if 'buf' is NULL, just return required length */
+  if (buf != NULL) {
+    if (len < ret) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, EC_R_BUFFER_TOO_SMALL);
+      goto err;
+    }
+
+    if (ctx == NULL) {
+      ctx = new_ctx = BN_CTX_new();
+      if (ctx == NULL)
+        return 0;
+    }
+
+    BN_CTX_start(ctx);
+    used_ctx = 1;
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    if (y == NULL)
+      goto err;
+
+    if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
+      goto err;
+
+    if ((form == POINT_CONVERSION_COMPRESSED ||
+         form == POINT_CONVERSION_HYBRID) &&
+        BN_is_odd(y))
+      buf[0] = form + 1;
+    else
+      buf[0] = form;
+
+    i = 1;
+
+    skip = field_len - BN_num_bytes(x);
+    if (skip > field_len) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+    while (skip > 0) {
+      buf[i++] = 0;
+      skip--;
+    }
+    skip = BN_bn2bin(x, buf + i);
+    i += skip;
+    if (i != 1 + field_len) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+
+    if (form == POINT_CONVERSION_UNCOMPRESSED ||
+        form == POINT_CONVERSION_HYBRID) {
+      skip = field_len - BN_num_bytes(y);
+      if (skip > field_len) {
+        OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+      while (skip > 0) {
+        buf[i++] = 0;
+        skip--;
+      }
+      skip = BN_bn2bin(y, buf + i);
+      i += skip;
+    }
+
+    if (i != ret) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point2oct, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+  }
+
+  if (used_ctx)
+    BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+
+err:
+  if (used_ctx)
+    BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return 0;
+}
+
+
+static int ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
+                                   const uint8_t *buf, size_t len,
+                                   BN_CTX *ctx) {
+  point_conversion_form_t form;
+  int y_bit;
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *x, *y;
+  size_t field_len, enc_len;
+  int ret = 0;
+
+  if (len == 0) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+  form = buf[0];
+  y_bit = form & 1;
+  form = form & ~1U;
+  if ((form != 0) && (form != POINT_CONVERSION_COMPRESSED) &&
+      (form != POINT_CONVERSION_UNCOMPRESSED) &&
+      (form != POINT_CONVERSION_HYBRID)) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+    return 0;
+  }
+  if ((form == 0 || form == POINT_CONVERSION_UNCOMPRESSED) && y_bit) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+    return 0;
+  }
+
+  if (form == 0) {
+    if (len != 1) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+      return 0;
+    }
+
+    return EC_POINT_set_to_infinity(group, point);
+  }
+
+  field_len = BN_num_bytes(&group->field);
+  enc_len =
+      (form == POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len;
+
+  if (len != enc_len) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+    return 0;
+  }
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  x = BN_CTX_get(ctx);
+  y = BN_CTX_get(ctx);
+  if (y == NULL)
+    goto err;
+
+  if (!BN_bin2bn(buf + 1, field_len, x))
+    goto err;
+  if (BN_ucmp(x, &group->field) >= 0) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+    goto err;
+  }
+
+  if (form == POINT_CONVERSION_COMPRESSED) {
+    if (!EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit, ctx))
+      goto err;
+  } else {
+    if (!BN_bin2bn(buf + 1 + field_len, field_len, y))
+      goto err;
+    if (BN_ucmp(y, &group->field) >= 0) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+      goto err;
+    }
+    if (form == POINT_CONVERSION_HYBRID) {
+      if (y_bit != BN_is_odd(y)) {
+        OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_INVALID_ENCODING);
+        goto err;
+      }
+    }
+
+    if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+      goto err;
+  }
+
+  if (!EC_POINT_is_on_curve(group, point, ctx)) /* test required by X9.62 */
+  {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_oct2point, EC_R_POINT_IS_NOT_ON_CURVE);
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
+                       const uint8_t *buf, size_t len, BN_CTX *ctx) {
+  if (group->meth->oct2point == 0 &&
+      !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_oct2point,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_oct2point, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+    return ec_GFp_simple_oct2point(group, point, buf, len, ctx);
+  }
+
+  return group->meth->oct2point(group, point, buf, len, ctx);
+}
+
+size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
+                          point_conversion_form_t form, uint8_t *buf,
+                          size_t len, BN_CTX *ctx) {
+  if (group->meth->point2oct == 0 &&
+      !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_point2oct,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_point2oct, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+    return ec_GFp_simple_point2oct(group, point, form, buf, len, ctx);
+  }
+
+  return group->meth->point2oct(group, point, form, buf, len, ctx);
+}
+
+int ec_GFp_simple_set_compressed_coordinates(const EC_GROUP *group,
+                                             EC_POINT *point, const BIGNUM *x_,
+                                             int y_bit, BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *tmp1, *tmp2, *x, *y;
+  int ret = 0;
+
+  ERR_clear_error();
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL) {
+      return 0;
+    }
+  }
+
+  y_bit = (y_bit != 0);
+
+  BN_CTX_start(ctx);
+  tmp1 = BN_CTX_get(ctx);
+  tmp2 = BN_CTX_get(ctx);
+  x = BN_CTX_get(ctx);
+  y = BN_CTX_get(ctx);
+  if (y == NULL) {
+    goto err;
+  }
+
+  /* Recover y.  We have a Weierstrass equation
+   *     y^2 = x^3 + a*x + b,
+   * so  y  is one of the square roots of  x^3 + a*x + b. */
+
+  /* tmp1 := x^3 */
+  if (!BN_nnmod(x, x_, &group->field, ctx)) {
+    goto err;
+  }
+
+  if (group->meth->field_decode == 0) {
+    /* field_{sqr,mul} work on standard representation */
+    if (!group->meth->field_sqr(group, tmp2, x_, ctx) ||
+        !group->meth->field_mul(group, tmp1, tmp2, x_, ctx)) {
+      goto err;
+    }
+  } else {
+    if (!BN_mod_sqr(tmp2, x_, &group->field, ctx) ||
+        !BN_mod_mul(tmp1, tmp2, x_, &group->field, ctx)) {
+      goto err;
+    }
+  }
+
+  /* tmp1 := tmp1 + a*x */
+  if (group->a_is_minus3) {
+    if (!BN_mod_lshift1_quick(tmp2, x, &group->field) ||
+        !BN_mod_add_quick(tmp2, tmp2, x, &group->field) ||
+        !BN_mod_sub_quick(tmp1, tmp1, tmp2, &group->field)) {
+      goto err;
+    }
+  } else {
+    if (group->meth->field_decode) {
+      if (!group->meth->field_decode(group, tmp2, &group->a, ctx) ||
+          !BN_mod_mul(tmp2, tmp2, x, &group->field, ctx)) {
+        goto err;
+      }
+    } else {
+      /* field_mul works on standard representation */
+      if (!group->meth->field_mul(group, tmp2, &group->a, x, ctx)) {
+        goto err;
+      }
+    }
+
+    if (!BN_mod_add_quick(tmp1, tmp1, tmp2, &group->field)) {
+      goto err;
+    }
+  }
+
+  /* tmp1 := tmp1 + b */
+  if (group->meth->field_decode) {
+    if (!group->meth->field_decode(group, tmp2, &group->b, ctx) ||
+        !BN_mod_add_quick(tmp1, tmp1, tmp2, &group->field)) {
+      goto err;
+    }
+  } else {
+    if (!BN_mod_add_quick(tmp1, tmp1, &group->b, &group->field)) {
+      goto err;
+    }
+  }
+
+  if (!BN_mod_sqrt(y, tmp1, &group->field, ctx)) {
+    unsigned long err = ERR_peek_last_error();
+
+    if (ERR_GET_LIB(err) == ERR_LIB_BN &&
+        ERR_GET_REASON(err) == BN_R_NOT_A_SQUARE) {
+      ERR_clear_error();
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_set_compressed_coordinates, EC_R_INVALID_COMPRESSED_POINT);
+    } else {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_set_compressed_coordinates, ERR_R_BN_LIB);
+    }
+    goto err;
+  }
+
+  if (y_bit != BN_is_odd(y)) {
+    if (BN_is_zero(y)) {
+      int kron;
+
+      kron = BN_kronecker(x, &group->field, ctx);
+      if (kron == -2) {
+        goto err;
+      }
+
+      if (kron == 1) {
+        OPENSSL_PUT_ERROR(EC, ec_GFp_simple_set_compressed_coordinates,
+                          EC_R_INVALID_COMPRESSION_BIT);
+      } else {
+        /* BN_mod_sqrt() should have cought this error (not a square) */
+        OPENSSL_PUT_ERROR(EC, ec_GFp_simple_set_compressed_coordinates,
+                          EC_R_INVALID_COMPRESSED_POINT);
+      }
+      goto err;
+    }
+    if (!BN_usub(y, &group->field, y)) {
+      goto err;
+    }
+  }
+  if (y_bit != BN_is_odd(y)) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_set_compressed_coordinates,
+                      ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+    goto err;
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
+                                            EC_POINT *point, const BIGNUM *x,
+                                            int y_bit, BN_CTX *ctx) {
+  if (group->meth->point_set_compressed_coordinates == 0 &&
+      !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_set_compressed_coordinates_GFp,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  if (group->meth != point->meth) {
+    OPENSSL_PUT_ERROR(EC, EC_POINT_set_compressed_coordinates_GFp,
+                      EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+  if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+    return ec_GFp_simple_set_compressed_coordinates(group, point, x, y_bit,
+                                                    ctx);
+  }
+  return group->meth->point_set_compressed_coordinates(group, point, x, y_bit,
+                                                       ctx);
+}
diff --git a/crypto/ec/simple.c b/crypto/ec/simple.c
new file mode 100644
index 0000000..144d056
--- /dev/null
+++ b/crypto/ec/simple.c
@@ -0,0 +1,1371 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <openssl/ec.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+const EC_METHOD *EC_GFp_simple_method(void) {
+  static const EC_METHOD ret = {EC_FLAGS_DEFAULT_OCT,
+                                ec_GFp_simple_group_init,
+                                ec_GFp_simple_group_finish,
+                                ec_GFp_simple_group_clear_finish,
+                                ec_GFp_simple_group_copy,
+                                ec_GFp_simple_group_set_curve,
+                                ec_GFp_simple_group_get_curve,
+                                ec_GFp_simple_group_get_degree,
+                                ec_GFp_simple_group_check_discriminant,
+                                ec_GFp_simple_point_init,
+                                ec_GFp_simple_point_finish,
+                                ec_GFp_simple_point_clear_finish,
+                                ec_GFp_simple_point_copy,
+                                ec_GFp_simple_point_set_to_infinity,
+                                ec_GFp_simple_set_Jprojective_coordinates_GFp,
+                                ec_GFp_simple_get_Jprojective_coordinates_GFp,
+                                ec_GFp_simple_point_set_affine_coordinates,
+                                ec_GFp_simple_point_get_affine_coordinates,
+                                0,
+                                0,
+                                0,
+                                ec_GFp_simple_add,
+                                ec_GFp_simple_dbl,
+                                ec_GFp_simple_invert,
+                                ec_GFp_simple_is_at_infinity,
+                                ec_GFp_simple_is_on_curve,
+                                ec_GFp_simple_cmp,
+                                ec_GFp_simple_make_affine,
+                                ec_GFp_simple_points_make_affine,
+                                0 /* mul */,
+                                0 /* precompute_mult */,
+                                0 /* have_precompute_mult */,
+                                ec_GFp_simple_field_mul,
+                                ec_GFp_simple_field_sqr,
+                                0 /* field_div */,
+                                0 /* field_encode */,
+                                0 /* field_decode */,
+                                0 /* field_set_to_one */};
+
+  return &ret;
+}
+
+
+/* Most method functions in this file are designed to work with non-trivial
+ * representations of field elements if necessary (see ecp_mont.c): while
+ * standard modular addition and subtraction are used, the field_mul and
+ * field_sqr methods will be used for multiplication, and field_encode and
+ * field_decode (if defined) will be used for converting between
+ * representations.
+
+ * Functions ec_GFp_simple_points_make_affine() and
+ * ec_GFp_simple_point_get_affine_coordinates() specifically assume that if a
+ * non-trivial representation is used, it is a Montgomery representation (i.e.
+ * 'encoding' means multiplying by some factor R). */
+
+int ec_GFp_simple_group_init(EC_GROUP *group) {
+  BN_init(&group->field);
+  BN_init(&group->a);
+  BN_init(&group->b);
+  group->a_is_minus3 = 0;
+  return 1;
+}
+
+void ec_GFp_simple_group_finish(EC_GROUP *group) {
+  BN_free(&group->field);
+  BN_free(&group->a);
+  BN_free(&group->b);
+}
+
+void ec_GFp_simple_group_clear_finish(EC_GROUP *group) {
+  BN_clear_free(&group->field);
+  BN_clear_free(&group->a);
+  BN_clear_free(&group->b);
+}
+
+int ec_GFp_simple_group_copy(EC_GROUP *dest, const EC_GROUP *src) {
+  if (!BN_copy(&dest->field, &src->field) ||
+      !BN_copy(&dest->a, &src->a) ||
+      !BN_copy(&dest->b, &src->b)) {
+    return 0;
+  }
+
+  dest->a_is_minus3 = src->a_is_minus3;
+  return 1;
+}
+
+int ec_GFp_simple_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                  const BIGNUM *a, const BIGNUM *b,
+                                  BN_CTX *ctx) {
+  int ret = 0;
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *tmp_a;
+
+  /* p must be a prime > 3 */
+  if (BN_num_bits(p) <= 2 || !BN_is_odd(p)) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_group_set_curve, EC_R_INVALID_FIELD);
+    return 0;
+  }
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  tmp_a = BN_CTX_get(ctx);
+  if (tmp_a == NULL)
+    goto err;
+
+  /* group->field */
+  if (!BN_copy(&group->field, p))
+    goto err;
+  BN_set_negative(&group->field, 0);
+
+  /* group->a */
+  if (!BN_nnmod(tmp_a, a, p, ctx))
+    goto err;
+  if (group->meth->field_encode) {
+    if (!group->meth->field_encode(group, &group->a, tmp_a, ctx))
+      goto err;
+  } else if (!BN_copy(&group->a, tmp_a))
+    goto err;
+
+  /* group->b */
+  if (!BN_nnmod(&group->b, b, p, ctx))
+    goto err;
+  if (group->meth->field_encode)
+    if (!group->meth->field_encode(group, &group->b, &group->b, ctx))
+      goto err;
+
+  /* group->a_is_minus3 */
+  if (!BN_add_word(tmp_a, 3))
+    goto err;
+  group->a_is_minus3 = (0 == BN_cmp(tmp_a, &group->field));
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_group_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
+                                  BIGNUM *b, BN_CTX *ctx) {
+  int ret = 0;
+  BN_CTX *new_ctx = NULL;
+
+  if (p != NULL) {
+    if (!BN_copy(p, &group->field))
+      return 0;
+  }
+
+  if (a != NULL || b != NULL) {
+    if (group->meth->field_decode) {
+      if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+          return 0;
+      }
+      if (a != NULL) {
+        if (!group->meth->field_decode(group, a, &group->a, ctx))
+          goto err;
+      }
+      if (b != NULL) {
+        if (!group->meth->field_decode(group, b, &group->b, ctx))
+          goto err;
+      }
+    } else {
+      if (a != NULL) {
+        if (!BN_copy(a, &group->a))
+          goto err;
+      }
+      if (b != NULL) {
+        if (!BN_copy(b, &group->b))
+          goto err;
+      }
+    }
+  }
+
+  ret = 1;
+
+err:
+  if (new_ctx)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_group_get_degree(const EC_GROUP *group) {
+  return BN_num_bits(&group->field);
+}
+
+int ec_GFp_simple_group_check_discriminant(const EC_GROUP *group, BN_CTX *ctx) {
+  int ret = 0;
+  BIGNUM *a, *b, *order, *tmp_1, *tmp_2;
+  const BIGNUM *p = &group->field;
+  BN_CTX *new_ctx = NULL;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_group_check_discriminant,
+                        ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
+  BN_CTX_start(ctx);
+  a = BN_CTX_get(ctx);
+  b = BN_CTX_get(ctx);
+  tmp_1 = BN_CTX_get(ctx);
+  tmp_2 = BN_CTX_get(ctx);
+  order = BN_CTX_get(ctx);
+  if (order == NULL)
+    goto err;
+
+  if (group->meth->field_decode) {
+    if (!group->meth->field_decode(group, a, &group->a, ctx))
+      goto err;
+    if (!group->meth->field_decode(group, b, &group->b, ctx))
+      goto err;
+  } else {
+    if (!BN_copy(a, &group->a))
+      goto err;
+    if (!BN_copy(b, &group->b))
+      goto err;
+  }
+
+  /* check the discriminant:
+   * y^2 = x^3 + a*x + b is an elliptic curve <=> 4*a^3 + 27*b^2 != 0 (mod p)
+   * 0 =< a, b < p */
+  if (BN_is_zero(a)) {
+    if (BN_is_zero(b))
+      goto err;
+  } else if (!BN_is_zero(b)) {
+    if (!BN_mod_sqr(tmp_1, a, p, ctx))
+      goto err;
+    if (!BN_mod_mul(tmp_2, tmp_1, a, p, ctx))
+      goto err;
+    if (!BN_lshift(tmp_1, tmp_2, 2))
+      goto err;
+    /* tmp_1 = 4*a^3 */
+
+    if (!BN_mod_sqr(tmp_2, b, p, ctx))
+      goto err;
+    if (!BN_mul_word(tmp_2, 27))
+      goto err;
+    /* tmp_2 = 27*b^2 */
+
+    if (!BN_mod_add(a, tmp_1, tmp_2, p, ctx))
+      goto err;
+    if (BN_is_zero(a))
+      goto err;
+  }
+  ret = 1;
+
+err:
+  if (ctx != NULL)
+    BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_point_init(EC_POINT *point) {
+  BN_init(&point->X);
+  BN_init(&point->Y);
+  BN_init(&point->Z);
+  point->Z_is_one = 0;
+
+  return 1;
+}
+
+void ec_GFp_simple_point_finish(EC_POINT *point) {
+  BN_free(&point->X);
+  BN_free(&point->Y);
+  BN_free(&point->Z);
+}
+
+void ec_GFp_simple_point_clear_finish(EC_POINT *point) {
+  BN_clear_free(&point->X);
+  BN_clear_free(&point->Y);
+  BN_clear_free(&point->Z);
+  point->Z_is_one = 0;
+}
+
+int ec_GFp_simple_point_copy(EC_POINT *dest, const EC_POINT *src) {
+  if (!BN_copy(&dest->X, &src->X))
+    return 0;
+  if (!BN_copy(&dest->Y, &src->Y))
+    return 0;
+  if (!BN_copy(&dest->Z, &src->Z))
+    return 0;
+  dest->Z_is_one = src->Z_is_one;
+
+  return 1;
+}
+
+int ec_GFp_simple_point_set_to_infinity(const EC_GROUP *group,
+                                        EC_POINT *point) {
+  point->Z_is_one = 0;
+  BN_zero(&point->Z);
+  return 1;
+}
+
+int ec_GFp_simple_set_Jprojective_coordinates_GFp(
+    const EC_GROUP *group, EC_POINT *point, const BIGNUM *x, const BIGNUM *y,
+    const BIGNUM *z, BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  int ret = 0;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  if (x != NULL) {
+    if (!BN_nnmod(&point->X, x, &group->field, ctx))
+      goto err;
+    if (group->meth->field_encode) {
+      if (!group->meth->field_encode(group, &point->X, &point->X, ctx))
+        goto err;
+    }
+  }
+
+  if (y != NULL) {
+    if (!BN_nnmod(&point->Y, y, &group->field, ctx))
+      goto err;
+    if (group->meth->field_encode) {
+      if (!group->meth->field_encode(group, &point->Y, &point->Y, ctx))
+        goto err;
+    }
+  }
+
+  if (z != NULL) {
+    int Z_is_one;
+
+    if (!BN_nnmod(&point->Z, z, &group->field, ctx))
+      goto err;
+    Z_is_one = BN_is_one(&point->Z);
+    if (group->meth->field_encode) {
+      if (Z_is_one && (group->meth->field_set_to_one != 0)) {
+        if (!group->meth->field_set_to_one(group, &point->Z, ctx))
+          goto err;
+      } else {
+        if (!group->meth->field_encode(group, &point->Z, &point->Z, ctx))
+          goto err;
+      }
+    }
+    point->Z_is_one = Z_is_one;
+  }
+
+  ret = 1;
+
+err:
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_get_Jprojective_coordinates_GFp(const EC_GROUP *group,
+                                                  const EC_POINT *point,
+                                                  BIGNUM *x, BIGNUM *y,
+                                                  BIGNUM *z, BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  int ret = 0;
+
+  if (group->meth->field_decode != 0) {
+    if (ctx == NULL) {
+      ctx = new_ctx = BN_CTX_new();
+      if (ctx == NULL)
+        return 0;
+    }
+
+    if (x != NULL) {
+      if (!group->meth->field_decode(group, x, &point->X, ctx))
+        goto err;
+    }
+    if (y != NULL) {
+      if (!group->meth->field_decode(group, y, &point->Y, ctx))
+        goto err;
+    }
+    if (z != NULL) {
+      if (!group->meth->field_decode(group, z, &point->Z, ctx))
+        goto err;
+    }
+  } else {
+    if (x != NULL) {
+      if (!BN_copy(x, &point->X))
+        goto err;
+    }
+    if (y != NULL) {
+      if (!BN_copy(y, &point->Y))
+        goto err;
+    }
+    if (z != NULL) {
+      if (!BN_copy(z, &point->Z))
+        goto err;
+    }
+  }
+
+  ret = 1;
+
+err:
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *group,
+                                               EC_POINT *point, const BIGNUM *x,
+                                               const BIGNUM *y, BN_CTX *ctx) {
+  if (x == NULL || y == NULL) {
+    /* unlike for projective coordinates, we do not tolerate this */
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point_set_affine_coordinates,
+                      ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  return ec_point_set_Jprojective_coordinates_GFp(group, point, x, y,
+                                                  BN_value_one(), ctx);
+}
+
+int ec_GFp_simple_point_get_affine_coordinates(const EC_GROUP *group,
+                                               const EC_POINT *point, BIGNUM *x,
+                                               BIGNUM *y, BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *Z, *Z_1, *Z_2, *Z_3;
+  const BIGNUM *Z_;
+  int ret = 0;
+
+  if (EC_POINT_is_at_infinity(group, point)) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point_get_affine_coordinates,
+                      EC_R_POINT_AT_INFINITY);
+    return 0;
+  }
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  Z = BN_CTX_get(ctx);
+  Z_1 = BN_CTX_get(ctx);
+  Z_2 = BN_CTX_get(ctx);
+  Z_3 = BN_CTX_get(ctx);
+  if (Z_3 == NULL)
+    goto err;
+
+  /* transform  (X, Y, Z)  into  (x, y) := (X/Z^2, Y/Z^3) */
+
+  if (group->meth->field_decode) {
+    if (!group->meth->field_decode(group, Z, &point->Z, ctx))
+      goto err;
+    Z_ = Z;
+  } else {
+    Z_ = &point->Z;
+  }
+
+  if (BN_is_one(Z_)) {
+    if (group->meth->field_decode) {
+      if (x != NULL) {
+        if (!group->meth->field_decode(group, x, &point->X, ctx))
+          goto err;
+      }
+      if (y != NULL) {
+        if (!group->meth->field_decode(group, y, &point->Y, ctx))
+          goto err;
+      }
+    } else {
+      if (x != NULL) {
+        if (!BN_copy(x, &point->X))
+          goto err;
+      }
+      if (y != NULL) {
+        if (!BN_copy(y, &point->Y))
+          goto err;
+      }
+    }
+  } else {
+    if (!BN_mod_inverse(Z_1, Z_, &group->field, ctx)) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_point_get_affine_coordinates,
+                        ERR_R_BN_LIB);
+      goto err;
+    }
+
+    if (group->meth->field_encode == 0) {
+      /* field_sqr works on standard representation */
+      if (!group->meth->field_sqr(group, Z_2, Z_1, ctx))
+        goto err;
+    } else {
+      if (!BN_mod_sqr(Z_2, Z_1, &group->field, ctx))
+        goto err;
+    }
+
+    if (x != NULL) {
+      /* in the Montgomery case, field_mul will cancel out Montgomery factor in
+       * X: */
+      if (!group->meth->field_mul(group, x, &point->X, Z_2, ctx))
+        goto err;
+    }
+
+    if (y != NULL) {
+      if (group->meth->field_encode == 0) {
+        /* field_mul works on standard representation */
+        if (!group->meth->field_mul(group, Z_3, Z_2, Z_1, ctx))
+          goto err;
+      } else {
+        if (!BN_mod_mul(Z_3, Z_2, Z_1, &group->field, ctx))
+          goto err;
+      }
+
+      /* in the Montgomery case, field_mul will cancel out Montgomery factor in
+       * Y: */
+      if (!group->meth->field_mul(group, y, &point->Y, Z_3, ctx))
+        goto err;
+    }
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                      const EC_POINT *b, BN_CTX *ctx) {
+  int (*field_mul)(const EC_GROUP *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                   BN_CTX *);
+  int (*field_sqr)(const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+  const BIGNUM *p;
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *n0, *n1, *n2, *n3, *n4, *n5, *n6;
+  int ret = 0;
+
+  if (a == b)
+    return EC_POINT_dbl(group, r, a, ctx);
+  if (EC_POINT_is_at_infinity(group, a))
+    return EC_POINT_copy(r, b);
+  if (EC_POINT_is_at_infinity(group, b))
+    return EC_POINT_copy(r, a);
+
+  field_mul = group->meth->field_mul;
+  field_sqr = group->meth->field_sqr;
+  p = &group->field;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  n0 = BN_CTX_get(ctx);
+  n1 = BN_CTX_get(ctx);
+  n2 = BN_CTX_get(ctx);
+  n3 = BN_CTX_get(ctx);
+  n4 = BN_CTX_get(ctx);
+  n5 = BN_CTX_get(ctx);
+  n6 = BN_CTX_get(ctx);
+  if (n6 == NULL)
+    goto end;
+
+  /* Note that in this function we must not read components of 'a' or 'b'
+   * once we have written the corresponding components of 'r'.
+   * ('r' might be one of 'a' or 'b'.)
+   */
+
+  /* n1, n2 */
+  if (b->Z_is_one) {
+    if (!BN_copy(n1, &a->X))
+      goto end;
+    if (!BN_copy(n2, &a->Y))
+      goto end;
+    /* n1 = X_a */
+    /* n2 = Y_a */
+  } else {
+    if (!field_sqr(group, n0, &b->Z, ctx))
+      goto end;
+    if (!field_mul(group, n1, &a->X, n0, ctx))
+      goto end;
+    /* n1 = X_a * Z_b^2 */
+
+    if (!field_mul(group, n0, n0, &b->Z, ctx))
+      goto end;
+    if (!field_mul(group, n2, &a->Y, n0, ctx))
+      goto end;
+    /* n2 = Y_a * Z_b^3 */
+  }
+
+  /* n3, n4 */
+  if (a->Z_is_one) {
+    if (!BN_copy(n3, &b->X))
+      goto end;
+    if (!BN_copy(n4, &b->Y))
+      goto end;
+    /* n3 = X_b */
+    /* n4 = Y_b */
+  } else {
+    if (!field_sqr(group, n0, &a->Z, ctx))
+      goto end;
+    if (!field_mul(group, n3, &b->X, n0, ctx))
+      goto end;
+    /* n3 = X_b * Z_a^2 */
+
+    if (!field_mul(group, n0, n0, &a->Z, ctx))
+      goto end;
+    if (!field_mul(group, n4, &b->Y, n0, ctx))
+      goto end;
+    /* n4 = Y_b * Z_a^3 */
+  }
+
+  /* n5, n6 */
+  if (!BN_mod_sub_quick(n5, n1, n3, p))
+    goto end;
+  if (!BN_mod_sub_quick(n6, n2, n4, p))
+    goto end;
+  /* n5 = n1 - n3 */
+  /* n6 = n2 - n4 */
+
+  if (BN_is_zero(n5)) {
+    if (BN_is_zero(n6)) {
+      /* a is the same point as b */
+      BN_CTX_end(ctx);
+      ret = EC_POINT_dbl(group, r, a, ctx);
+      ctx = NULL;
+      goto end;
+    } else {
+      /* a is the inverse of b */
+      BN_zero(&r->Z);
+      r->Z_is_one = 0;
+      ret = 1;
+      goto end;
+    }
+  }
+
+  /* 'n7', 'n8' */
+  if (!BN_mod_add_quick(n1, n1, n3, p))
+    goto end;
+  if (!BN_mod_add_quick(n2, n2, n4, p))
+    goto end;
+  /* 'n7' = n1 + n3 */
+  /* 'n8' = n2 + n4 */
+
+  /* Z_r */
+  if (a->Z_is_one && b->Z_is_one) {
+    if (!BN_copy(&r->Z, n5))
+      goto end;
+  } else {
+    if (a->Z_is_one) {
+      if (!BN_copy(n0, &b->Z))
+        goto end;
+    } else if (b->Z_is_one) {
+      if (!BN_copy(n0, &a->Z))
+        goto end;
+    } else {
+      if (!field_mul(group, n0, &a->Z, &b->Z, ctx))
+        goto end;
+    }
+    if (!field_mul(group, &r->Z, n0, n5, ctx))
+      goto end;
+  }
+  r->Z_is_one = 0;
+  /* Z_r = Z_a * Z_b * n5 */
+
+  /* X_r */
+  if (!field_sqr(group, n0, n6, ctx))
+    goto end;
+  if (!field_sqr(group, n4, n5, ctx))
+    goto end;
+  if (!field_mul(group, n3, n1, n4, ctx))
+    goto end;
+  if (!BN_mod_sub_quick(&r->X, n0, n3, p))
+    goto end;
+  /* X_r = n6^2 - n5^2 * 'n7' */
+
+  /* 'n9' */
+  if (!BN_mod_lshift1_quick(n0, &r->X, p))
+    goto end;
+  if (!BN_mod_sub_quick(n0, n3, n0, p))
+    goto end;
+  /* n9 = n5^2 * 'n7' - 2 * X_r */
+
+  /* Y_r */
+  if (!field_mul(group, n0, n0, n6, ctx))
+    goto end;
+  if (!field_mul(group, n5, n4, n5, ctx))
+    goto end; /* now n5 is n5^3 */
+  if (!field_mul(group, n1, n2, n5, ctx))
+    goto end;
+  if (!BN_mod_sub_quick(n0, n0, n1, p))
+    goto end;
+  if (BN_is_odd(n0))
+    if (!BN_add(n0, n0, p))
+      goto end;
+  /* now  0 <= n0 < 2*p,  and n0 is even */
+  if (!BN_rshift1(&r->Y, n0))
+    goto end;
+  /* Y_r = (n6 * 'n9' - 'n8' * 'n5^3') / 2 */
+
+  ret = 1;
+
+end:
+  if (ctx) /* otherwise we already called BN_CTX_end */
+    BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                      BN_CTX *ctx) {
+  int (*field_mul)(const EC_GROUP *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                   BN_CTX *);
+  int (*field_sqr)(const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+  const BIGNUM *p;
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *n0, *n1, *n2, *n3;
+  int ret = 0;
+
+  if (EC_POINT_is_at_infinity(group, a)) {
+    BN_zero(&r->Z);
+    r->Z_is_one = 0;
+    return 1;
+  }
+
+  field_mul = group->meth->field_mul;
+  field_sqr = group->meth->field_sqr;
+  p = &group->field;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  n0 = BN_CTX_get(ctx);
+  n1 = BN_CTX_get(ctx);
+  n2 = BN_CTX_get(ctx);
+  n3 = BN_CTX_get(ctx);
+  if (n3 == NULL)
+    goto err;
+
+  /* Note that in this function we must not read components of 'a'
+   * once we have written the corresponding components of 'r'.
+   * ('r' might the same as 'a'.)
+   */
+
+  /* n1 */
+  if (a->Z_is_one) {
+    if (!field_sqr(group, n0, &a->X, ctx))
+      goto err;
+    if (!BN_mod_lshift1_quick(n1, n0, p))
+      goto err;
+    if (!BN_mod_add_quick(n0, n0, n1, p))
+      goto err;
+    if (!BN_mod_add_quick(n1, n0, &group->a, p))
+      goto err;
+    /* n1 = 3 * X_a^2 + a_curve */
+  } else if (group->a_is_minus3) {
+    if (!field_sqr(group, n1, &a->Z, ctx))
+      goto err;
+    if (!BN_mod_add_quick(n0, &a->X, n1, p))
+      goto err;
+    if (!BN_mod_sub_quick(n2, &a->X, n1, p))
+      goto err;
+    if (!field_mul(group, n1, n0, n2, ctx))
+      goto err;
+    if (!BN_mod_lshift1_quick(n0, n1, p))
+      goto err;
+    if (!BN_mod_add_quick(n1, n0, n1, p))
+      goto err;
+    /* n1 = 3 * (X_a + Z_a^2) * (X_a - Z_a^2)
+     *    = 3 * X_a^2 - 3 * Z_a^4 */
+  } else {
+    if (!field_sqr(group, n0, &a->X, ctx))
+      goto err;
+    if (!BN_mod_lshift1_quick(n1, n0, p))
+      goto err;
+    if (!BN_mod_add_quick(n0, n0, n1, p))
+      goto err;
+    if (!field_sqr(group, n1, &a->Z, ctx))
+      goto err;
+    if (!field_sqr(group, n1, n1, ctx))
+      goto err;
+    if (!field_mul(group, n1, n1, &group->a, ctx))
+      goto err;
+    if (!BN_mod_add_quick(n1, n1, n0, p))
+      goto err;
+    /* n1 = 3 * X_a^2 + a_curve * Z_a^4 */
+  }
+
+  /* Z_r */
+  if (a->Z_is_one) {
+    if (!BN_copy(n0, &a->Y))
+      goto err;
+  } else {
+    if (!field_mul(group, n0, &a->Y, &a->Z, ctx))
+      goto err;
+  }
+  if (!BN_mod_lshift1_quick(&r->Z, n0, p))
+    goto err;
+  r->Z_is_one = 0;
+  /* Z_r = 2 * Y_a * Z_a */
+
+  /* n2 */
+  if (!field_sqr(group, n3, &a->Y, ctx))
+    goto err;
+  if (!field_mul(group, n2, &a->X, n3, ctx))
+    goto err;
+  if (!BN_mod_lshift_quick(n2, n2, 2, p))
+    goto err;
+  /* n2 = 4 * X_a * Y_a^2 */
+
+  /* X_r */
+  if (!BN_mod_lshift1_quick(n0, n2, p))
+    goto err;
+  if (!field_sqr(group, &r->X, n1, ctx))
+    goto err;
+  if (!BN_mod_sub_quick(&r->X, &r->X, n0, p))
+    goto err;
+  /* X_r = n1^2 - 2 * n2 */
+
+  /* n3 */
+  if (!field_sqr(group, n0, n3, ctx))
+    goto err;
+  if (!BN_mod_lshift_quick(n3, n0, 3, p))
+    goto err;
+  /* n3 = 8 * Y_a^4 */
+
+  /* Y_r */
+  if (!BN_mod_sub_quick(n0, n2, &r->X, p))
+    goto err;
+  if (!field_mul(group, n0, n1, n0, ctx))
+    goto err;
+  if (!BN_mod_sub_quick(&r->Y, n0, n3, p))
+    goto err;
+  /* Y_r = n1 * (n2 - X_r) - n3 */
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_invert(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx) {
+  if (EC_POINT_is_at_infinity(group, point) || BN_is_zero(&point->Y))
+    /* point is its own inverse */
+    return 1;
+
+  return BN_usub(&point->Y, &group->field, &point->Y);
+}
+
+int ec_GFp_simple_is_at_infinity(const EC_GROUP *group, const EC_POINT *point) {
+  return !point->Z_is_one && BN_is_zero(&point->Z);
+}
+
+int ec_GFp_simple_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
+                              BN_CTX *ctx) {
+  int (*field_mul)(const EC_GROUP *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                   BN_CTX *);
+  int (*field_sqr)(const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+  const BIGNUM *p;
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *rh, *tmp, *Z4, *Z6;
+  int ret = -1;
+
+  if (EC_POINT_is_at_infinity(group, point))
+    return 1;
+
+  field_mul = group->meth->field_mul;
+  field_sqr = group->meth->field_sqr;
+  p = &group->field;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return -1;
+  }
+
+  BN_CTX_start(ctx);
+  rh = BN_CTX_get(ctx);
+  tmp = BN_CTX_get(ctx);
+  Z4 = BN_CTX_get(ctx);
+  Z6 = BN_CTX_get(ctx);
+  if (Z6 == NULL)
+    goto err;
+
+  /* We have a curve defined by a Weierstrass equation
+   *      y^2 = x^3 + a*x + b.
+   * The point to consider is given in Jacobian projective coordinates
+   * where  (X, Y, Z)  represents  (x, y) = (X/Z^2, Y/Z^3).
+   * Substituting this and multiplying by  Z^6  transforms the above equation
+   * into
+   *      Y^2 = X^3 + a*X*Z^4 + b*Z^6.
+   * To test this, we add up the right-hand side in 'rh'.
+   */
+
+  /* rh := X^2 */
+  if (!field_sqr(group, rh, &point->X, ctx))
+    goto err;
+
+  if (!point->Z_is_one) {
+    if (!field_sqr(group, tmp, &point->Z, ctx))
+      goto err;
+    if (!field_sqr(group, Z4, tmp, ctx))
+      goto err;
+    if (!field_mul(group, Z6, Z4, tmp, ctx))
+      goto err;
+
+    /* rh := (rh + a*Z^4)*X */
+    if (group->a_is_minus3) {
+      if (!BN_mod_lshift1_quick(tmp, Z4, p))
+        goto err;
+      if (!BN_mod_add_quick(tmp, tmp, Z4, p))
+        goto err;
+      if (!BN_mod_sub_quick(rh, rh, tmp, p))
+        goto err;
+      if (!field_mul(group, rh, rh, &point->X, ctx))
+        goto err;
+    } else {
+      if (!field_mul(group, tmp, Z4, &group->a, ctx))
+        goto err;
+      if (!BN_mod_add_quick(rh, rh, tmp, p))
+        goto err;
+      if (!field_mul(group, rh, rh, &point->X, ctx))
+        goto err;
+    }
+
+    /* rh := rh + b*Z^6 */
+    if (!field_mul(group, tmp, &group->b, Z6, ctx))
+      goto err;
+    if (!BN_mod_add_quick(rh, rh, tmp, p))
+      goto err;
+  } else {
+    /* point->Z_is_one */
+
+    /* rh := (rh + a)*X */
+    if (!BN_mod_add_quick(rh, rh, &group->a, p))
+      goto err;
+    if (!field_mul(group, rh, rh, &point->X, ctx))
+      goto err;
+    /* rh := rh + b */
+    if (!BN_mod_add_quick(rh, rh, &group->b, p))
+      goto err;
+  }
+
+  /* 'lh' := Y^2 */
+  if (!field_sqr(group, tmp, &point->Y, ctx))
+    goto err;
+
+  ret = (0 == BN_ucmp(tmp, rh));
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_cmp(const EC_GROUP *group, const EC_POINT *a,
+                      const EC_POINT *b, BN_CTX *ctx) {
+  /* return values:
+   *  -1   error
+   *   0   equal (in affine coordinates)
+   *   1   not equal
+   */
+
+  int (*field_mul)(const EC_GROUP *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                   BN_CTX *);
+  int (*field_sqr)(const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *tmp1, *tmp2, *Za23, *Zb23;
+  const BIGNUM *tmp1_, *tmp2_;
+  int ret = -1;
+
+  if (EC_POINT_is_at_infinity(group, a)) {
+    return EC_POINT_is_at_infinity(group, b) ? 0 : 1;
+  }
+
+  if (EC_POINT_is_at_infinity(group, b))
+    return 1;
+
+  if (a->Z_is_one && b->Z_is_one) {
+    return ((BN_cmp(&a->X, &b->X) == 0) && BN_cmp(&a->Y, &b->Y) == 0) ? 0 : 1;
+  }
+
+  field_mul = group->meth->field_mul;
+  field_sqr = group->meth->field_sqr;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return -1;
+  }
+
+  BN_CTX_start(ctx);
+  tmp1 = BN_CTX_get(ctx);
+  tmp2 = BN_CTX_get(ctx);
+  Za23 = BN_CTX_get(ctx);
+  Zb23 = BN_CTX_get(ctx);
+  if (Zb23 == NULL)
+    goto end;
+
+  /* We have to decide whether
+   *     (X_a/Z_a^2, Y_a/Z_a^3) = (X_b/Z_b^2, Y_b/Z_b^3),
+   * or equivalently, whether
+   *     (X_a*Z_b^2, Y_a*Z_b^3) = (X_b*Z_a^2, Y_b*Z_a^3).
+   */
+
+  if (!b->Z_is_one) {
+    if (!field_sqr(group, Zb23, &b->Z, ctx))
+      goto end;
+    if (!field_mul(group, tmp1, &a->X, Zb23, ctx))
+      goto end;
+    tmp1_ = tmp1;
+  } else
+    tmp1_ = &a->X;
+  if (!a->Z_is_one) {
+    if (!field_sqr(group, Za23, &a->Z, ctx))
+      goto end;
+    if (!field_mul(group, tmp2, &b->X, Za23, ctx))
+      goto end;
+    tmp2_ = tmp2;
+  } else
+    tmp2_ = &b->X;
+
+  /* compare  X_a*Z_b^2  with  X_b*Z_a^2 */
+  if (BN_cmp(tmp1_, tmp2_) != 0) {
+    ret = 1; /* points differ */
+    goto end;
+  }
+
+
+  if (!b->Z_is_one) {
+    if (!field_mul(group, Zb23, Zb23, &b->Z, ctx))
+      goto end;
+    if (!field_mul(group, tmp1, &a->Y, Zb23, ctx))
+      goto end;
+    /* tmp1_ = tmp1 */
+  } else
+    tmp1_ = &a->Y;
+  if (!a->Z_is_one) {
+    if (!field_mul(group, Za23, Za23, &a->Z, ctx))
+      goto end;
+    if (!field_mul(group, tmp2, &b->Y, Za23, ctx))
+      goto end;
+    /* tmp2_ = tmp2 */
+  } else
+    tmp2_ = &b->Y;
+
+  /* compare  Y_a*Z_b^3  with  Y_b*Z_a^3 */
+  if (BN_cmp(tmp1_, tmp2_) != 0) {
+    ret = 1; /* points differ */
+    goto end;
+  }
+
+  /* points are equal */
+  ret = 0;
+
+end:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_make_affine(const EC_GROUP *group, EC_POINT *point,
+                              BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *x, *y;
+  int ret = 0;
+
+  if (point->Z_is_one || EC_POINT_is_at_infinity(group, point))
+    return 1;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  x = BN_CTX_get(ctx);
+  y = BN_CTX_get(ctx);
+  if (y == NULL)
+    goto err;
+
+  if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
+    goto err;
+  if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+    goto err;
+  if (!point->Z_is_one) {
+    OPENSSL_PUT_ERROR(EC, ec_GFp_simple_make_affine, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  return ret;
+}
+
+int ec_GFp_simple_points_make_affine(const EC_GROUP *group, size_t num,
+                                     EC_POINT *points[], BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *tmp0, *tmp1;
+  size_t pow2 = 0;
+  BIGNUM **heap = NULL;
+  size_t i;
+  int ret = 0;
+
+  if (num == 0)
+    return 1;
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      return 0;
+  }
+
+  BN_CTX_start(ctx);
+  tmp0 = BN_CTX_get(ctx);
+  tmp1 = BN_CTX_get(ctx);
+  if (tmp0 == NULL || tmp1 == NULL)
+    goto err;
+
+  /* Before converting the individual points, compute inverses of all Z values.
+   * Modular inversion is rather slow, but luckily we can do with a single
+   * explicit inversion, plus about 3 multiplications per input value.
+   */
+
+  pow2 = 1;
+  while (num > pow2)
+    pow2 <<= 1;
+  /* Now pow2 is the smallest power of 2 satifsying pow2 >= num.
+   * We need twice that. */
+  pow2 <<= 1;
+
+  heap = OPENSSL_malloc(pow2 * sizeof heap[0]);
+  if (heap == NULL)
+    goto err;
+
+  /* The array is used as a binary tree, exactly as in heapsort:
+   *
+   *                               heap[1]
+   *                 heap[2]                     heap[3]
+   *          heap[4]       heap[5]       heap[6]       heap[7]
+   *   heap[8]heap[9] heap[10]heap[11] heap[12]heap[13] heap[14] heap[15]
+   *
+   * We put the Z's in the last line;
+   * then we set each other node to the product of its two child-nodes (where
+   * empty or 0 entries are treated as ones);
+   * then we invert heap[1];
+   * then we invert each other node by replacing it by the product of its
+   * parent (after inversion) and its sibling (before inversion).
+   */
+  heap[0] = NULL;
+  for (i = pow2 / 2 - 1; i > 0; i--)
+    heap[i] = NULL;
+  for (i = 0; i < num; i++)
+    heap[pow2 / 2 + i] = &points[i]->Z;
+  for (i = pow2 / 2 + num; i < pow2; i++)
+    heap[i] = NULL;
+
+  /* set each node to the product of its children */
+  for (i = pow2 / 2 - 1; i > 0; i--) {
+    heap[i] = BN_new();
+    if (heap[i] == NULL)
+      goto err;
+
+    if (heap[2 * i] != NULL) {
+      if ((heap[2 * i + 1] == NULL) || BN_is_zero(heap[2 * i + 1])) {
+        if (!BN_copy(heap[i], heap[2 * i]))
+          goto err;
+      } else {
+        if (BN_is_zero(heap[2 * i])) {
+          if (!BN_copy(heap[i], heap[2 * i + 1]))
+            goto err;
+        } else {
+          if (!group->meth->field_mul(group, heap[i], heap[2 * i],
+                                      heap[2 * i + 1], ctx))
+            goto err;
+        }
+      }
+    }
+  }
+
+  /* invert heap[1] */
+  if (!BN_is_zero(heap[1])) {
+    if (!BN_mod_inverse(heap[1], heap[1], &group->field, ctx)) {
+      OPENSSL_PUT_ERROR(EC, ec_GFp_simple_points_make_affine, ERR_R_BN_LIB);
+      goto err;
+    }
+  }
+  if (group->meth->field_encode != 0) {
+    /* in the Montgomery case, we just turned  R*H  (representing H)
+     * into  1/(R*H),  but we need  R*(1/H)  (representing 1/H);
+     * i.e. we have need to multiply by the Montgomery factor twice */
+    if (!group->meth->field_encode(group, heap[1], heap[1], ctx)) {
+      goto err;
+    }
+    if (!group->meth->field_encode(group, heap[1], heap[1], ctx)) {
+      goto err;
+    }
+  }
+
+  /* set other heap[i]'s to their inverses */
+  for (i = 2; i < pow2 / 2 + num; i += 2) {
+    /* i is even */
+    if ((heap[i + 1] != NULL) && !BN_is_zero(heap[i + 1])) {
+      if (!group->meth->field_mul(group, tmp0, heap[i / 2], heap[i + 1], ctx))
+        goto err;
+      if (!group->meth->field_mul(group, tmp1, heap[i / 2], heap[i], ctx))
+        goto err;
+      if (!BN_copy(heap[i], tmp0))
+        goto err;
+      if (!BN_copy(heap[i + 1], tmp1))
+        goto err;
+    } else {
+      if (!BN_copy(heap[i], heap[i / 2]))
+        goto err;
+    }
+  }
+
+  /* we have replaced all non-zero Z's by their inverses, now fix up all the
+   * points */
+  for (i = 0; i < num; i++) {
+    EC_POINT *p = points[i];
+
+    if (!BN_is_zero(&p->Z)) {
+      /* turn  (X, Y, 1/Z)  into  (X/Z^2, Y/Z^3, 1) */
+
+      if (!group->meth->field_sqr(group, tmp1, &p->Z, ctx))
+        goto err;
+      if (!group->meth->field_mul(group, &p->X, &p->X, tmp1, ctx))
+        goto err;
+
+      if (!group->meth->field_mul(group, tmp1, tmp1, &p->Z, ctx))
+        goto err;
+      if (!group->meth->field_mul(group, &p->Y, &p->Y, tmp1, ctx))
+        goto err;
+
+      if (group->meth->field_set_to_one != 0) {
+        if (!group->meth->field_set_to_one(group, &p->Z, ctx))
+          goto err;
+      } else {
+        if (!BN_one(&p->Z))
+          goto err;
+      }
+      p->Z_is_one = 1;
+    }
+  }
+
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  if (heap != NULL) {
+    /* heap[pow2/2] .. heap[pow2-1] have not been allocated locally! */
+    for (i = pow2 / 2 - 1; i > 0; i--) {
+      if (heap[i] != NULL)
+        BN_clear_free(heap[i]);
+    }
+    OPENSSL_free(heap);
+  }
+  return ret;
+}
+
+int ec_GFp_simple_field_mul(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                            const BIGNUM *b, BN_CTX *ctx) {
+  return BN_mod_mul(r, a, b, &group->field, ctx);
+}
+
+int ec_GFp_simple_field_sqr(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                            BN_CTX *ctx) {
+  return BN_mod_sqr(r, a, &group->field, ctx);
+}
diff --git a/crypto/ec/wnaf.c b/crypto/ec/wnaf.c
new file mode 100644
index 0000000..b86107d
--- /dev/null
+++ b/crypto/ec/wnaf.c
@@ -0,0 +1,841 @@
+/* Originally written by Bodo Moeller for the OpenSSL project.
+ * ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
+ * Laboratories. */
+
+#include <openssl/ec.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+/* This file implements the wNAF-based interleaving multi-exponentation method
+ * (<URL:http://www.informatik.tu-darmstadt.de/TI/Mitarbeiter/moeller.html#multiexp>);
+ * for multiplication with precomputation, we use wNAF splitting
+ * (<URL:http://www.informatik.tu-darmstadt.de/TI/Mitarbeiter/moeller.html#fastexp>).
+ * */
+
+/* structure for precomputed multiples of the generator */
+typedef struct ec_pre_comp_st {
+  const EC_GROUP *group; /* parent EC_GROUP object */
+  size_t blocksize;      /* block size for wNAF splitting */
+  size_t numblocks; /* max. number of blocks for which we have precomputation */
+  size_t w;         /* window size */
+  EC_POINT **points; /* array with pre-calculated multiples of generator:
+                      * 'num' pointers to EC_POINT objects followed by a NULL */
+  size_t num; /* numblocks * 2^(w-1) */
+  int references;
+} EC_PRE_COMP;
+
+static EC_PRE_COMP *ec_pre_comp_new(const EC_GROUP *group) {
+  EC_PRE_COMP *ret = NULL;
+
+  if (!group)
+    return NULL;
+
+  ret = (EC_PRE_COMP *)OPENSSL_malloc(sizeof(EC_PRE_COMP));
+  if (!ret) {
+    OPENSSL_PUT_ERROR(EC, ec_pre_comp_new, ERR_R_MALLOC_FAILURE);
+    return ret;
+  }
+  ret->group = group;
+  ret->blocksize = 8; /* default */
+  ret->numblocks = 0;
+  ret->w = 4; /* default */
+  ret->points = NULL;
+  ret->num = 0;
+  ret->references = 1;
+  return ret;
+}
+
+void *ec_pre_comp_dup(EC_PRE_COMP *pre_comp) {
+  if (pre_comp == NULL) {
+    return NULL;
+  }
+
+  CRYPTO_add(&pre_comp->references, 1, CRYPTO_LOCK_EC_PRE_COMP);
+  return pre_comp;
+}
+
+void ec_pre_comp_free(EC_PRE_COMP *pre_comp) {
+  int i;
+
+  if (!pre_comp) {
+    return;
+  }
+
+  i = CRYPTO_add(&pre_comp->references, -1, CRYPTO_LOCK_EC_PRE_COMP);
+  if (i > 0) {
+    return;
+  }
+
+  if (pre_comp->points) {
+    EC_POINT **p;
+
+    for (p = pre_comp->points; *p != NULL; p++) {
+      EC_POINT_free(*p);
+    }
+    OPENSSL_free(pre_comp->points);
+  }
+  OPENSSL_free(pre_comp);
+}
+
+
+/* Determine the modified width-(w+1) Non-Adjacent Form (wNAF) of 'scalar'.
+ * This is an array  r[]  of values that are either zero or odd with an
+ * absolute value less than  2^w  satisfying
+ *     scalar = \sum_j r[j]*2^j
+ * where at most one of any  w+1  consecutive digits is non-zero
+ * with the exception that the most significant digit may be only
+ * w-1 zeros away from that next non-zero digit.
+ */
+static signed char *compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len) {
+  int window_val;
+  int ok = 0;
+  signed char *r = NULL;
+  int sign = 1;
+  int bit, next_bit, mask;
+  size_t len = 0, j;
+
+  if (BN_is_zero(scalar)) {
+    r = OPENSSL_malloc(1);
+    if (!r) {
+      OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    r[0] = 0;
+    *ret_len = 1;
+    return r;
+  }
+
+  if (w <= 0 || w > 7) /* 'signed char' can represent integers with absolute
+                          values less than 2^7 */
+  {
+    OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+  bit = 1 << w;        /* at most 128 */
+  next_bit = bit << 1; /* at most 256 */
+  mask = next_bit - 1; /* at most 255 */
+
+  if (BN_is_negative(scalar)) {
+    sign = -1;
+  }
+
+  if (scalar->d == NULL || scalar->top == 0) {
+    OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  len = BN_num_bits(scalar);
+  r = OPENSSL_malloc(
+      len +
+      1); /* modified wNAF may be one digit longer than binary representation
+           * (*ret_len will be set to the actual length, i.e. at most
+           * BN_num_bits(scalar) + 1) */
+  if (r == NULL) {
+    OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  window_val = scalar->d[0] & mask;
+  j = 0;
+  while ((window_val != 0) ||
+         (j + w + 1 < len)) /* if j+w+1 >= len, window_val will not increase */
+  {
+    int digit = 0;
+
+    /* 0 <= window_val <= 2^(w+1) */
+
+    if (window_val & 1) {
+      /* 0 < window_val < 2^(w+1) */
+
+      if (window_val & bit) {
+        digit = window_val - next_bit; /* -2^w < digit < 0 */
+
+#if 1 /* modified wNAF */
+        if (j + w + 1 >= len) {
+          /* special case for generating modified wNAFs:
+           * no new bits will be added into window_val,
+           * so using a positive digit here will decrease
+           * the total length of the representation */
+
+          digit = window_val & (mask >> 1); /* 0 < digit < 2^w */
+        }
+#endif
+      } else {
+        digit = window_val; /* 0 < digit < 2^w */
+      }
+
+      if (digit <= -bit || digit >= bit || !(digit & 1)) {
+        OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+
+      window_val -= digit;
+
+      /* now window_val is 0 or 2^(w+1) in standard wNAF generation;
+       * for modified window NAFs, it may also be 2^w
+       */
+      if (window_val != 0 && window_val != next_bit && window_val != bit) {
+        OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+    }
+
+    r[j++] = sign * digit;
+
+    window_val >>= 1;
+    window_val += bit * BN_is_bit_set(scalar, j + w);
+
+    if (window_val > next_bit) {
+      OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+  }
+
+  if (j > len + 1) {
+    OPENSSL_PUT_ERROR(EC, compute_wNAF, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+  len = j;
+  ok = 1;
+
+err:
+  if (!ok) {
+    OPENSSL_free(r);
+    r = NULL;
+  }
+  if (ok)
+    *ret_len = len;
+  return r;
+}
+
+
+/* TODO: table should be optimised for the wNAF-based implementation,
+ *       sometimes smaller windows will give better performance
+ *       (thus the boundaries should be increased)
+ */
+#define EC_window_bits_for_scalar_size(b)                                      \
+  ((size_t)((b) >= 2000 ? 6 : (b) >= 800 ? 5 : (b) >= 300                      \
+                                                   ? 4                         \
+                                                   : (b) >= 70 ? 3 : (b) >= 20 \
+                                                                         ? 2   \
+                                                                         : 1))
+
+/* Compute
+ *      \sum scalars[i]*points[i],
+ * also including
+ *      scalar*generator
+ * in the addition if scalar != NULL
+ */
+int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+                BN_CTX *ctx) {
+  BN_CTX *new_ctx = NULL;
+  const EC_POINT *generator = NULL;
+  EC_POINT *tmp = NULL;
+  size_t totalnum;
+  size_t blocksize = 0, numblocks = 0; /* for wNAF splitting */
+  size_t pre_points_per_block = 0;
+  size_t i, j;
+  int k;
+  int r_is_inverted = 0;
+  int r_is_at_infinity = 1;
+  size_t *wsize = NULL;      /* individual window sizes */
+  signed char **wNAF = NULL; /* individual wNAFs */
+  size_t *wNAF_len = NULL;
+  size_t max_len = 0;
+  size_t num_val;
+  EC_POINT **val = NULL; /* precomputation */
+  EC_POINT **v;
+  EC_POINT ***val_sub =
+      NULL; /* pointers to sub-arrays of 'val' or 'pre_comp->points' */
+  const EC_PRE_COMP *pre_comp = NULL;
+  int num_scalar = 0; /* flag: will be set to 1 if 'scalar' must be treated like
+                       * other scalars,
+                       * i.e. precomputation is not available */
+  int ret = 0;
+
+  if (group->meth != r->meth) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, EC_R_INCOMPATIBLE_OBJECTS);
+    return 0;
+  }
+
+  if ((scalar == NULL) && (num == 0)) {
+    return EC_POINT_set_to_infinity(group, r);
+  }
+
+  for (i = 0; i < num; i++) {
+    if (group->meth != points[i]->meth) {
+      OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, EC_R_INCOMPATIBLE_OBJECTS);
+      return 0;
+    }
+  }
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      goto err;
+  }
+
+  if (scalar != NULL) {
+    generator = EC_GROUP_get0_generator(group);
+    if (generator == NULL) {
+      OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, EC_R_UNDEFINED_GENERATOR);
+      goto err;
+    }
+
+    /* look if we can use precomputed multiples of generator */
+
+    pre_comp = group->pre_comp;
+
+    if (pre_comp && pre_comp->numblocks &&
+        (EC_POINT_cmp(group, generator, pre_comp->points[0], ctx) == 0)) {
+      blocksize = pre_comp->blocksize;
+
+      /* determine maximum number of blocks that wNAF splitting may yield
+       * (NB: maximum wNAF length is bit length plus one) */
+      numblocks = (BN_num_bits(scalar) / blocksize) + 1;
+
+      /* we cannot use more blocks than we have precomputation for */
+      if (numblocks > pre_comp->numblocks)
+        numblocks = pre_comp->numblocks;
+
+      pre_points_per_block = (size_t)1 << (pre_comp->w - 1);
+
+      /* check that pre_comp looks sane */
+      if (pre_comp->num != (pre_comp->numblocks * pre_points_per_block)) {
+        OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+    } else {
+      /* can't use precomputation */
+      pre_comp = NULL;
+      numblocks = 1;
+      num_scalar = 1; /* treat 'scalar' like 'num'-th element of 'scalars' */
+    }
+  }
+
+  totalnum = num + numblocks;
+
+  wsize = OPENSSL_malloc(totalnum * sizeof wsize[0]);
+  wNAF_len = OPENSSL_malloc(totalnum * sizeof wNAF_len[0]);
+  wNAF = OPENSSL_malloc((totalnum + 1) *
+                        sizeof wNAF[0]); /* includes space for pivot */
+  val_sub = OPENSSL_malloc(totalnum * sizeof val_sub[0]);
+
+  if (!wsize || !wNAF_len || !wNAF || !val_sub) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  wNAF[0] = NULL; /* preliminary pivot */
+
+  /* num_val will be the total number of temporarily precomputed points */
+  num_val = 0;
+
+  for (i = 0; i < num + num_scalar; i++) {
+    size_t bits;
+
+    bits = i < num ? BN_num_bits(scalars[i]) : BN_num_bits(scalar);
+    wsize[i] = EC_window_bits_for_scalar_size(bits);
+    num_val += (size_t)1 << (wsize[i] - 1);
+    wNAF[i + 1] = NULL; /* make sure we always have a pivot */
+    wNAF[i] =
+        compute_wNAF((i < num ? scalars[i] : scalar), wsize[i], &wNAF_len[i]);
+    if (wNAF[i] == NULL)
+      goto err;
+    if (wNAF_len[i] > max_len)
+      max_len = wNAF_len[i];
+  }
+
+  if (numblocks) {
+    /* we go here iff scalar != NULL */
+
+    if (pre_comp == NULL) {
+      if (num_scalar != 1) {
+        OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+      /* we have already generated a wNAF for 'scalar' */
+    } else {
+      signed char *tmp_wNAF = NULL;
+      size_t tmp_len = 0;
+
+      if (num_scalar != 0) {
+        OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+
+      /* use the window size for which we have precomputation */
+      wsize[num] = pre_comp->w;
+      tmp_wNAF = compute_wNAF(scalar, wsize[num], &tmp_len);
+      if (!tmp_wNAF)
+        goto err;
+
+      if (tmp_len <= max_len) {
+        /* One of the other wNAFs is at least as long
+         * as the wNAF belonging to the generator,
+         * so wNAF splitting will not buy us anything. */
+
+        numblocks = 1;
+        totalnum = num + 1; /* don't use wNAF splitting */
+        wNAF[num] = tmp_wNAF;
+        wNAF[num + 1] = NULL;
+        wNAF_len[num] = tmp_len;
+        if (tmp_len > max_len)
+          max_len = tmp_len;
+        /* pre_comp->points starts with the points that we need here: */
+        val_sub[num] = pre_comp->points;
+      } else {
+        /* don't include tmp_wNAF directly into wNAF array
+         * - use wNAF splitting and include the blocks */
+
+        signed char *pp;
+        EC_POINT **tmp_points;
+
+        if (tmp_len < numblocks * blocksize) {
+          /* possibly we can do with fewer blocks than estimated */
+          numblocks = (tmp_len + blocksize - 1) / blocksize;
+          if (numblocks > pre_comp->numblocks) {
+            OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+            goto err;
+          }
+          totalnum = num + numblocks;
+        }
+
+        /* split wNAF in 'numblocks' parts */
+        pp = tmp_wNAF;
+        tmp_points = pre_comp->points;
+
+        for (i = num; i < totalnum; i++) {
+          if (i < totalnum - 1) {
+            wNAF_len[i] = blocksize;
+            if (tmp_len < blocksize) {
+              OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+              goto err;
+            }
+            tmp_len -= blocksize;
+          } else
+            /* last block gets whatever is left
+             * (this could be more or less than 'blocksize'!) */
+            wNAF_len[i] = tmp_len;
+
+          wNAF[i + 1] = NULL;
+          wNAF[i] = OPENSSL_malloc(wNAF_len[i]);
+          if (wNAF[i] == NULL) {
+            OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_MALLOC_FAILURE);
+            OPENSSL_free(tmp_wNAF);
+            goto err;
+          }
+          memcpy(wNAF[i], pp, wNAF_len[i]);
+          if (wNAF_len[i] > max_len)
+            max_len = wNAF_len[i];
+
+          if (*tmp_points == NULL) {
+            OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+            OPENSSL_free(tmp_wNAF);
+            goto err;
+          }
+          val_sub[i] = tmp_points;
+          tmp_points += pre_points_per_block;
+          pp += blocksize;
+        }
+        OPENSSL_free(tmp_wNAF);
+      }
+    }
+  }
+
+  /* All points we precompute now go into a single array 'val'.
+   * 'val_sub[i]' is a pointer to the subarray for the i-th point,
+   * or to a subarray of 'pre_comp->points' if we already have precomputation.
+   */
+  val = OPENSSL_malloc((num_val + 1) * sizeof val[0]);
+  if (val == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  val[num_val] = NULL; /* pivot element */
+
+  /* allocate points for precomputation */
+  v = val;
+  for (i = 0; i < num + num_scalar; i++) {
+    val_sub[i] = v;
+    for (j = 0; j < ((size_t)1 << (wsize[i] - 1)); j++) {
+      *v = EC_POINT_new(group);
+      if (*v == NULL)
+        goto err;
+      v++;
+    }
+  }
+  if (!(v == val + num_val)) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_mul, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  if (!(tmp = EC_POINT_new(group)))
+    goto err;
+
+  /* prepare precomputed values:
+   *    val_sub[i][0] :=     points[i]
+   *    val_sub[i][1] := 3 * points[i]
+   *    val_sub[i][2] := 5 * points[i]
+   *    ...
+   */
+  for (i = 0; i < num + num_scalar; i++) {
+    if (i < num) {
+      if (!EC_POINT_copy(val_sub[i][0], points[i]))
+        goto err;
+    } else {
+      if (!EC_POINT_copy(val_sub[i][0], generator))
+        goto err;
+    }
+
+    if (wsize[i] > 1) {
+      if (!EC_POINT_dbl(group, tmp, val_sub[i][0], ctx))
+        goto err;
+      for (j = 1; j < ((size_t)1 << (wsize[i] - 1)); j++) {
+        if (!EC_POINT_add(group, val_sub[i][j], val_sub[i][j - 1], tmp, ctx))
+          goto err;
+      }
+    }
+  }
+
+#if 1 /* optional; EC_window_bits_for_scalar_size assumes we do this step */
+  if (!EC_POINTs_make_affine(group, num_val, val, ctx))
+    goto err;
+#endif
+
+  r_is_at_infinity = 1;
+
+  for (k = max_len - 1; k >= 0; k--) {
+    if (!r_is_at_infinity) {
+      if (!EC_POINT_dbl(group, r, r, ctx))
+        goto err;
+    }
+
+    for (i = 0; i < totalnum; i++) {
+      if (wNAF_len[i] > (size_t)k) {
+        int digit = wNAF[i][k];
+        int is_neg;
+
+        if (digit) {
+          is_neg = digit < 0;
+
+          if (is_neg)
+            digit = -digit;
+
+          if (is_neg != r_is_inverted) {
+            if (!r_is_at_infinity) {
+              if (!EC_POINT_invert(group, r, ctx))
+                goto err;
+            }
+            r_is_inverted = !r_is_inverted;
+          }
+
+          /* digit > 0 */
+
+          if (r_is_at_infinity) {
+            if (!EC_POINT_copy(r, val_sub[i][digit >> 1]))
+              goto err;
+            r_is_at_infinity = 0;
+          } else {
+            if (!EC_POINT_add(group, r, r, val_sub[i][digit >> 1], ctx))
+              goto err;
+          }
+        }
+      }
+    }
+  }
+
+  if (r_is_at_infinity) {
+    if (!EC_POINT_set_to_infinity(group, r))
+      goto err;
+  } else {
+    if (r_is_inverted)
+      if (!EC_POINT_invert(group, r, ctx))
+        goto err;
+  }
+
+  ret = 1;
+
+err:
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  if (tmp != NULL)
+    EC_POINT_free(tmp);
+  if (wsize != NULL)
+    OPENSSL_free(wsize);
+  if (wNAF_len != NULL)
+    OPENSSL_free(wNAF_len);
+  if (wNAF != NULL) {
+    signed char **w;
+
+    for (w = wNAF; *w != NULL; w++)
+      OPENSSL_free(*w);
+
+    OPENSSL_free(wNAF);
+  }
+  if (val != NULL) {
+    for (v = val; *v != NULL; v++)
+      EC_POINT_clear_free(*v);
+
+    OPENSSL_free(val);
+  }
+  if (val_sub != NULL) {
+    OPENSSL_free(val_sub);
+  }
+  return ret;
+}
+
+
+/* ec_wNAF_precompute_mult()
+ * creates an EC_PRE_COMP object with preprecomputed multiples of the generator
+ * for use with wNAF splitting as implemented in ec_wNAF_mul().
+ *
+ * 'pre_comp->points' is an array of multiples of the generator
+ * of the following form:
+ * points[0] =     generator;
+ * points[1] = 3 * generator;
+ * ...
+ * points[2^(w-1)-1] =     (2^(w-1)-1) * generator;
+ * points[2^(w-1)]   =     2^blocksize * generator;
+ * points[2^(w-1)+1] = 3 * 2^blocksize * generator;
+ * ...
+ * points[2^(w-1)*(numblocks-1)-1] = (2^(w-1)) *  2^(blocksize*(numblocks-2)) *
+ *generator
+ * points[2^(w-1)*(numblocks-1)]   =              2^(blocksize*(numblocks-1)) *
+ *generator
+ * ...
+ * points[2^(w-1)*numblocks-1]     = (2^(w-1)) *  2^(blocksize*(numblocks-1)) *
+ *generator
+ * points[2^(w-1)*numblocks]       = NULL
+ */
+int ec_wNAF_precompute_mult(EC_GROUP *group, BN_CTX *ctx) {
+  const EC_POINT *generator;
+  EC_POINT *tmp_point = NULL, *base = NULL, **var;
+  BN_CTX *new_ctx = NULL;
+  BIGNUM *order;
+  size_t i, bits, w, pre_points_per_block, blocksize, numblocks, num;
+  EC_POINT **points = NULL;
+  EC_PRE_COMP *pre_comp;
+  int ret = 0;
+
+  /* if there is an old EC_PRE_COMP object, throw it away */
+  if (group->pre_comp) {
+    ec_pre_comp_free(group->pre_comp);
+    group->pre_comp = NULL;
+  }
+
+  if ((pre_comp = ec_pre_comp_new(group)) == NULL)
+    return 0;
+
+  generator = EC_GROUP_get0_generator(group);
+  if (generator == NULL) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_precompute_mult, EC_R_UNDEFINED_GENERATOR);
+    goto err;
+  }
+
+  if (ctx == NULL) {
+    ctx = new_ctx = BN_CTX_new();
+    if (ctx == NULL)
+      goto err;
+  }
+
+  BN_CTX_start(ctx);
+  order = BN_CTX_get(ctx);
+  if (order == NULL)
+    goto err;
+
+  if (!EC_GROUP_get_order(group, order, ctx))
+    goto err;
+  if (BN_is_zero(order)) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_precompute_mult, EC_R_UNKNOWN_ORDER);
+    goto err;
+  }
+
+  bits = BN_num_bits(order);
+  /* The following parameters mean we precompute (approximately)
+   * one point per bit.
+   *
+   * TBD: The combination  8, 4  is perfect for 160 bits; for other
+   * bit lengths, other parameter combinations might provide better
+   * efficiency.
+   */
+  blocksize = 8;
+  w = 4;
+  if (EC_window_bits_for_scalar_size(bits) > w) {
+    /* let's not make the window too small ... */
+    w = EC_window_bits_for_scalar_size(bits);
+  }
+
+  numblocks = (bits + blocksize - 1) /
+              blocksize; /* max. number of blocks to use for wNAF splitting */
+
+  pre_points_per_block = (size_t)1 << (w - 1);
+  num = pre_points_per_block *
+        numblocks; /* number of points to compute and store */
+
+  points = OPENSSL_malloc(sizeof(EC_POINT *) * (num + 1));
+  if (!points) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_precompute_mult, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  var = points;
+  var[num] = NULL; /* pivot */
+  for (i = 0; i < num; i++) {
+    if ((var[i] = EC_POINT_new(group)) == NULL) {
+      OPENSSL_PUT_ERROR(EC, ec_wNAF_precompute_mult, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
+
+  if (!(tmp_point = EC_POINT_new(group)) || !(base = EC_POINT_new(group))) {
+    OPENSSL_PUT_ERROR(EC, ec_wNAF_precompute_mult, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (!EC_POINT_copy(base, generator))
+    goto err;
+
+  /* do the precomputation */
+  for (i = 0; i < numblocks; i++) {
+    size_t j;
+
+    if (!EC_POINT_dbl(group, tmp_point, base, ctx))
+      goto err;
+
+    if (!EC_POINT_copy(*var++, base))
+      goto err;
+
+    for (j = 1; j < pre_points_per_block; j++, var++) {
+      /* calculate odd multiples of the current base point */
+      if (!EC_POINT_add(group, *var, tmp_point, *(var - 1), ctx))
+        goto err;
+    }
+
+    if (i < numblocks - 1) {
+      /* get the next base (multiply current one by 2^blocksize) */
+      size_t k;
+
+      if (blocksize <= 2) {
+        OPENSSL_PUT_ERROR(EC, ec_wNAF_precompute_mult, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+
+      if (!EC_POINT_dbl(group, base, tmp_point, ctx))
+        goto err;
+      for (k = 2; k < blocksize; k++) {
+        if (!EC_POINT_dbl(group, base, base, ctx))
+          goto err;
+      }
+    }
+  }
+
+  if (!EC_POINTs_make_affine(group, num, points, ctx))
+    goto err;
+
+  pre_comp->group = group;
+  pre_comp->blocksize = blocksize;
+  pre_comp->numblocks = numblocks;
+  pre_comp->w = w;
+  pre_comp->points = points;
+  points = NULL;
+  pre_comp->num = num;
+
+  group->pre_comp = pre_comp;
+  pre_comp = NULL;
+
+  ret = 1;
+
+err:
+  if (ctx != NULL)
+    BN_CTX_end(ctx);
+  if (new_ctx != NULL)
+    BN_CTX_free(new_ctx);
+  if (pre_comp)
+    ec_pre_comp_free(pre_comp);
+  if (points) {
+    EC_POINT **p;
+
+    for (p = points; *p != NULL; p++)
+      EC_POINT_free(*p);
+    OPENSSL_free(points);
+  }
+  if (tmp_point)
+    EC_POINT_free(tmp_point);
+  if (base)
+    EC_POINT_free(base);
+  return ret;
+}
+
+
+int ec_wNAF_have_precompute_mult(const EC_GROUP *group) {
+  return group->pre_comp != NULL;
+}
diff --git a/crypto/ecdh/CMakeLists.txt b/crypto/ecdh/CMakeLists.txt
new file mode 100644
index 0000000..0f71368
--- /dev/null
+++ b/crypto/ecdh/CMakeLists.txt
@@ -0,0 +1,10 @@
+include_directories(. .. ../../include)
+
+add_library(
+	ecdh
+
+	OBJECT
+
+	ecdh.c
+	ecdh_error.c
+)
diff --git a/crypto/ecdh/ecdh.c b/crypto/ecdh/ecdh.c
new file mode 100644
index 0000000..c64a0ad
--- /dev/null
+++ b/crypto/ecdh/ecdh.c
@@ -0,0 +1,218 @@
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ *
+ * The ECDH software is originally written by Douglas Stebila of
+ * Sun Microsystems Laboratories.
+ *
+ */
+/* ====================================================================
+ * Copyright (c) 2000-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ecdh.h>
+
+#include <openssl/bn.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
+                     EC_KEY *priv_key, void *(*KDF)(const void *in, size_t inlen,
+                                                void *out, size_t *outlen)) {
+  BN_CTX *ctx;
+  EC_POINT *tmp = NULL;
+  BIGNUM *x = NULL, *y = NULL;
+  const BIGNUM *priv;
+  const EC_GROUP *group;
+  int ret = -1;
+  size_t buflen, len;
+  uint8_t *buf = NULL;
+
+  if ((ctx = BN_CTX_new()) == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  x = BN_CTX_get(ctx);
+  y = BN_CTX_get(ctx);
+
+  priv = EC_KEY_get0_private_key(priv_key);
+  if (priv == NULL) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ECDH_R_NO_PRIVATE_VALUE);
+    goto err;
+  }
+
+  group = EC_KEY_get0_group(priv_key);
+
+  tmp = EC_POINT_new(group);
+  if (tmp == NULL) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (!EC_POINT_mul(group, tmp, NULL, pub_key, priv, ctx)) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ECDH_R_POINT_ARITHMETIC_FAILURE);
+    goto err;
+  }
+
+  if (!EC_POINT_get_affine_coordinates_GFp(group, tmp, x, y, ctx)) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ECDH_R_POINT_ARITHMETIC_FAILURE);
+    goto err;
+  }
+
+  buflen = (EC_GROUP_get_degree(group) + 7) / 8;
+  len = BN_num_bytes(x);
+  if (len > buflen) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+  buf = OPENSSL_malloc(buflen);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  memset(buf, 0, buflen - len);
+  if (len != (size_t)BN_bn2bin(x, buf + buflen - len)) {
+    OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  if (KDF != 0) {
+    if (KDF(buf, buflen, out, &outlen) == NULL) {
+      OPENSSL_PUT_ERROR(ECDH, ECDH_compute_key, ECDH_R_KDF_FAILED);
+      goto err;
+    }
+    ret = outlen;
+  } else {
+    /* no KDF, just copy as much as we can */
+    if (outlen > buflen) {
+      outlen = buflen;
+    }
+    memcpy(out, buf, outlen);
+    ret = outlen;
+  }
+
+err:
+  if (tmp)
+    EC_POINT_free(tmp);
+  if (ctx)
+    BN_CTX_end(ctx);
+  if (ctx)
+    BN_CTX_free(ctx);
+  if (buf)
+    OPENSSL_free(buf);
+  return ret;
+}
+
+/* Key derivation function from X9.62/SECG */
+/* Way more than we will ever need */
+#define ECDH_KDF_MAX	(1 << 30)
+
+int ECDH_KDF_X9_62(uint8_t *out, size_t outlen, const uint8_t *Z,
+                   size_t Zlen, const uint8_t *sinfo, size_t sinfolen,
+                   const EVP_MD *md) {
+  EVP_MD_CTX mctx;
+  int rv = 0;
+  unsigned int i;
+  size_t mdlen;
+  uint8_t ctr[4];
+
+  if (sinfolen > ECDH_KDF_MAX || outlen > ECDH_KDF_MAX || Zlen > ECDH_KDF_MAX) {
+    return 0;
+  }
+  mdlen = EVP_MD_size(md);
+  EVP_MD_CTX_init(&mctx);
+
+  for (i = 1;; i++) {
+    uint8_t mtmp[EVP_MAX_MD_SIZE];
+    EVP_DigestInit_ex(&mctx, md, NULL);
+    ctr[3] = i & 0xFF;
+    ctr[2] = (i >> 8) & 0xFF;
+    ctr[1] = (i >> 16) & 0xFF;
+    ctr[0] = (i >> 24) & 0xFF;
+    if (!EVP_DigestUpdate(&mctx, Z, Zlen) ||
+        !EVP_DigestUpdate(&mctx, ctr, sizeof(ctr)) ||
+        !EVP_DigestUpdate(&mctx, sinfo, sinfolen)) {
+      goto err;
+    }
+
+    if (outlen >= mdlen) {
+      if (!EVP_DigestFinal(&mctx, out, NULL)) {
+        goto err;
+      }
+      outlen -= mdlen;
+      if (outlen == 0) {
+        break;
+      }
+      out += mdlen;
+    } else {
+      if (!EVP_DigestFinal(&mctx, mtmp, NULL)) {
+        goto err;
+      }
+      memcpy(out, mtmp, outlen);
+      OPENSSL_cleanse(mtmp, mdlen);
+      break;
+    }
+  }
+  rv = 1;
+
+err:
+  EVP_MD_CTX_cleanup(&mctx);
+  return rv;
+}
diff --git a/crypto/ecdh/ecdh.h b/crypto/ecdh/ecdh.h
new file mode 100644
index 0000000..48aa08d
--- /dev/null
+++ b/crypto/ecdh/ecdh.h
@@ -0,0 +1,106 @@
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ *
+ * The ECDH software is originally written by Douglas Stebila of
+ * Sun Microsystems Laboratories.
+ *
+ */
+/* ====================================================================
+ * Copyright (c) 2000-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_ECDH_H
+#define OPENSSL_HEADER_ECDH_H
+
+#include <openssl/base.h>
+
+#include <openssl/ec_key.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* ECDH_compute_key calculates the shared key between |pub_key| and |priv_key|.
+ * If |KDF| is not NULL, then it is called with the bytes of the shared key and
+ * the parameter |out|. When |KDF| returns, the value of |*outlen| becomes the
+ * return value. Otherwise, as many bytes of the shared key as will fit are
+ * copied directly to, at most, |outlen| bytes at |out|. It returns the number
+ * of bytes written to |out|, or -1 on error. */
+int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
+                     EC_KEY *priv_key, void *(*KDF)(const void *in, size_t inlen,
+                                                void *out, size_t *outlen));
+
+/* ECDH_KDF_X9_62 writes |outlen| bytes to |out| using the KDF from X9.62
+ * applied to |Z| and |sinfo| and using the hash |md|. It returns one on
+ * success and zero otherwise. */
+int ECDH_KDF_X9_62(uint8_t *out, size_t outlen, const uint8_t *Z,
+                   size_t Zlen, const uint8_t *sinfo, size_t sinfolen,
+                   const EVP_MD *md);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define ECDH_F_ECDH_compute_key 100
+#define ECDH_R_POINT_ARITHMETIC_FAILURE 100
+#define ECDH_R_KDF_FAILED 101
+#define ECDH_R_NO_PRIVATE_VALUE 102
+
+#endif  /* OPENSSL_HEADER_ECDH_H */
diff --git a/crypto/ecdh/ecdh_error.c b/crypto/ecdh/ecdh_error.c
new file mode 100644
index 0000000..9e53467
--- /dev/null
+++ b/crypto/ecdh/ecdh_error.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "ecdh.h"
+
+const ERR_STRING_DATA ECDH_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_ECDH, ECDH_F_ECDH_compute_key, 0), "ECDH_compute_key"},
+  {ERR_PACK(ERR_LIB_ECDH, 0, ECDH_R_KDF_FAILED), "KDF_FAILED"},
+  {ERR_PACK(ERR_LIB_ECDH, 0, ECDH_R_NO_PRIVATE_VALUE), "NO_PRIVATE_VALUE"},
+  {ERR_PACK(ERR_LIB_ECDH, 0, ECDH_R_POINT_ARITHMETIC_FAILURE), "POINT_ARITHMETIC_FAILURE"},
+  {0, NULL},
+};
diff --git a/crypto/ecdsa/CMakeLists.txt b/crypto/ecdsa/CMakeLists.txt
new file mode 100644
index 0000000..3f0caf0
--- /dev/null
+++ b/crypto/ecdsa/CMakeLists.txt
@@ -0,0 +1,20 @@
+include_directories(. .. ../../include)
+
+add_library(
+	ecdsa
+
+	OBJECT
+
+	ecdsa.c
+	ecdsa_asn1.c
+	ecdsa_error.c
+)
+
+
+add_executable(
+	ecdsa_test
+
+	ecdsa_test.c
+)
+
+target_link_libraries(ecdsa_test crypto)
diff --git a/crypto/ecdsa/ecdsa.c b/crypto/ecdsa/ecdsa.c
new file mode 100644
index 0000000..cfe9ef6
--- /dev/null
+++ b/crypto/ecdsa/ecdsa.c
@@ -0,0 +1,465 @@
+/* ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ecdsa.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/thread.h>
+
+#include "../ec/internal.h"
+
+
+int ECDSA_sign(int type, const uint8_t *digest, size_t digest_len, uint8_t *sig,
+               unsigned int *sig_len, EC_KEY *eckey) {
+  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
+    return eckey->ecdsa_meth->sign(digest, digest_len, sig, sig_len, eckey);
+  }
+
+  return ECDSA_sign_ex(type, digest, digest_len, sig, sig_len, NULL, NULL,
+                       eckey);
+}
+
+int ECDSA_verify(int type, const uint8_t *digest, size_t digest_len,
+                 const uint8_t *sig, size_t sig_len, EC_KEY *eckey) {
+  ECDSA_SIG *s;
+  int ret = -1;
+
+  if (eckey->ecdsa_meth && eckey->ecdsa_meth->verify) {
+    return eckey->ecdsa_meth->verify(digest, digest_len, sig, sig_len, eckey);
+  }
+
+  s = ECDSA_SIG_new();
+  if (s == NULL || d2i_ECDSA_SIG(&s, &sig, sig_len) == NULL) {
+    goto err;
+  }
+  ret = ECDSA_do_verify(digest, digest_len, s, eckey);
+
+err:
+  if (s != NULL) {
+    ECDSA_SIG_free(s);
+  }
+  return ret;
+}
+
+/* digest_to_bn interprets |digest_len| bytes from |digest| as a big-endian
+ * number and sets |out| to that value. It then truncates |out| so that it's,
+ * at most, as long as |order|. It returns one on success and zero otherwise. */
+static int digest_to_bn(BIGNUM *out, const uint8_t *digest, size_t digest_len,
+                        const BIGNUM *order) {
+  size_t num_bits;
+
+  num_bits = BN_num_bits(order);
+  /* Need to truncate digest if it is too long: first truncate whole
+   * bytes. */
+  if (8 * digest_len > num_bits) {
+    digest_len = (num_bits + 7) / 8;
+  }
+  if (!BN_bin2bn(digest, digest_len, out)) {
+    OPENSSL_PUT_ERROR(ECDSA, digest_to_bn, ERR_R_BN_LIB);
+    return 0;
+  }
+
+  /* If still too long truncate remaining bits with a shift */
+  if ((8 * digest_len > num_bits) &&
+      !BN_rshift(out, out, 8 - (num_bits & 0x7))) {
+    OPENSSL_PUT_ERROR(ECDSA, digest_to_bn, ERR_R_BN_LIB);
+    return 0;
+  }
+
+  return 1;
+}
+
+ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
+                         EC_KEY *key) {
+  return ECDSA_do_sign_ex(digest, digest_len, NULL, NULL, key);
+}
+
+int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
+                    const ECDSA_SIG *sig, EC_KEY *eckey) {
+  int ret = -1;
+  BN_CTX *ctx;
+  BIGNUM *order, *u1, *u2, *m, *X;
+  EC_POINT *point = NULL;
+  const EC_GROUP *group;
+  const EC_POINT *pub_key;
+
+  if (eckey->ecdsa_meth && eckey->ecdsa_meth->verify) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_ex, ECDSA_R_NOT_IMPLEMENTED);
+    return -1;
+  }
+
+  /* check input values */
+  if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL ||
+      (pub_key = EC_KEY_get0_public_key(eckey)) == NULL || sig == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_MISSING_PARAMETERS);
+    return -1;
+  }
+
+  ctx = BN_CTX_new();
+  if (!ctx) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_MALLOC_FAILURE);
+    return -1;
+  }
+  BN_CTX_start(ctx);
+  order = BN_CTX_get(ctx);
+  u1 = BN_CTX_get(ctx);
+  u2 = BN_CTX_get(ctx);
+  m = BN_CTX_get(ctx);
+  X = BN_CTX_get(ctx);
+  if (!X) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  if (!EC_GROUP_get_order(group, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_EC_LIB);
+    goto err;
+  }
+
+  if (BN_is_zero(sig->r) || BN_is_negative(sig->r) ||
+      BN_ucmp(sig->r, order) >= 0 || BN_is_zero(sig->s) ||
+      BN_is_negative(sig->s) || BN_ucmp(sig->s, order) >= 0) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_BAD_SIGNATURE);
+    ret = 0; /* signature is invalid */
+    goto err;
+  }
+  /* calculate tmp1 = inv(S) mod order */
+  if (!BN_mod_inverse(u2, sig->s, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB);
+    goto err;
+  }
+  if (!digest_to_bn(m, digest, digest_len, order)) {
+    goto err;
+  }
+  /* u1 = m * tmp mod order */
+  if (!BN_mod_mul(u1, m, u2, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB);
+    goto err;
+  }
+  /* u2 = r * w mod q */
+  if (!BN_mod_mul(u2, sig->r, u2, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB);
+    goto err;
+  }
+
+  point = EC_POINT_new(group);
+  if (point == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  if (!EC_POINT_mul(group, point, u1, pub_key, u2, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_EC_LIB);
+    goto err;
+  }
+  if (!EC_POINT_get_affine_coordinates_GFp(group, point, X, NULL, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_EC_LIB);
+    goto err;
+  }
+  if (!BN_nnmod(u1, X, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB);
+    goto err;
+  }
+  /* if the signature is correct u1 is equal to sig->r */
+  ret = (BN_ucmp(u1, sig->r) == 0);
+
+err:
+  BN_CTX_end(ctx);
+  BN_CTX_free(ctx);
+  if (point) {
+    EC_POINT_free(point);
+  }
+  return ret;
+}
+
+int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
+                     BIGNUM **rp) {
+  BN_CTX *ctx = NULL;
+  BIGNUM *k = NULL, *r = NULL, *order = NULL, *X = NULL;
+  EC_POINT *tmp_point = NULL;
+  const EC_GROUP *group;
+  int ret = 0;
+
+  if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  if (ctx_in == NULL) {
+    if ((ctx = BN_CTX_new()) == NULL) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+  } else {
+    ctx = ctx_in;
+  }
+
+  k = BN_new(); /* this value is later returned in *kinvp */
+  r = BN_new(); /* this value is later returned in *rp    */
+  order = BN_new();
+  X = BN_new();
+  if (!k || !r || !order || !X) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  tmp_point = EC_POINT_new(group);
+  if (tmp_point == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_EC_LIB);
+    goto err;
+  }
+  if (!EC_GROUP_get_order(group, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_EC_LIB);
+    goto err;
+  }
+
+  do {
+    /* get random k */
+    do {
+      if (!BN_rand_range(k, order)) {
+        OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup,
+                          ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED);
+        goto err;
+      }
+    } while (BN_is_zero(k));
+
+    /* We do not want timing information to leak the length of k,
+     * so we compute G*k using an equivalent scalar of fixed
+     * bit-length. */
+
+    if (!BN_add(k, k, order)) {
+      goto err;
+    }
+    if (BN_num_bits(k) <= BN_num_bits(order)) {
+      if (!BN_add(k, k, order)) {
+        goto err;
+      }
+    }
+
+    /* compute r the x-coordinate of generator * k */
+    if (!EC_POINT_mul(group, tmp_point, k, NULL, NULL, ctx)) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_EC_LIB);
+      goto err;
+    }
+    if (!EC_POINT_get_affine_coordinates_GFp(group, tmp_point, X, NULL, ctx)) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_EC_LIB);
+      goto err;
+    }
+
+    if (!BN_nnmod(r, X, order, ctx)) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_BN_LIB);
+      goto err;
+    }
+  } while (BN_is_zero(r));
+
+  /* compute the inverse of k */
+  if (!BN_mod_inverse(k, k, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_setup, ERR_R_BN_LIB);
+    goto err;
+  }
+  /* clear old values if necessary */
+  if (*rp != NULL) {
+    BN_clear_free(*rp);
+  }
+  if (*kinvp != NULL) {
+    BN_clear_free(*kinvp);
+  }
+
+  /* save the pre-computed values  */
+  *rp = r;
+  *kinvp = k;
+  ret = 1;
+
+err:
+  if (!ret) {
+    if (k != NULL) {
+      BN_clear_free(k);
+    }
+    if (r != NULL) {
+      BN_clear_free(r);
+    }
+  }
+  if (ctx_in == NULL)
+    BN_CTX_free(ctx);
+  if (order != NULL)
+    BN_free(order);
+  if (tmp_point != NULL)
+    EC_POINT_free(tmp_point);
+  if (X)
+    BN_clear_free(X);
+  return ret;
+}
+
+ECDSA_SIG *ECDSA_do_sign_ex(const uint8_t *digest, size_t digest_len,
+                            const BIGNUM *in_kinv, const BIGNUM *in_r,
+                            EC_KEY *eckey) {
+  int ok = 0;
+  BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL, *order = NULL;
+  const BIGNUM *ckinv;
+  BN_CTX *ctx = NULL;
+  const EC_GROUP *group;
+  ECDSA_SIG *ret;
+  const BIGNUM *priv_key;
+
+  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_ex, ECDSA_R_NOT_IMPLEMENTED);
+    return NULL;
+  }
+
+  group = EC_KEY_get0_group(eckey);
+  priv_key = EC_KEY_get0_private_key(eckey);
+
+  if (group == NULL || priv_key == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_PASSED_NULL_PARAMETER);
+    return NULL;
+  }
+
+  ret = ECDSA_SIG_new();
+  if (!ret) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  s = ret->s;
+
+  if ((ctx = BN_CTX_new()) == NULL || (order = BN_new()) == NULL ||
+      (tmp = BN_new()) == NULL || (m = BN_new()) == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (!EC_GROUP_get_order(group, order, ctx)) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_EC_LIB);
+    goto err;
+  }
+  if (!digest_to_bn(m, digest, digest_len, order)) {
+    goto err;
+  }
+  for (;;) {
+    if (in_kinv == NULL || in_r == NULL) {
+      if (!ECDSA_sign_setup(eckey, ctx, &kinv, &ret->r)) {
+        OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_ECDSA_LIB);
+        goto err;
+      }
+      ckinv = kinv;
+    } else {
+      ckinv = in_kinv;
+      if (BN_copy(ret->r, in_r) == NULL) {
+        OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_MALLOC_FAILURE);
+        goto err;
+      }
+    }
+
+    if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx)) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_BN_LIB);
+      goto err;
+    }
+    if (!BN_mod_add_quick(s, tmp, m, order)) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_BN_LIB);
+      goto err;
+    }
+    if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
+      OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_BN_LIB);
+      goto err;
+    }
+    if (BN_is_zero(s)) {
+      /* if kinv and r have been supplied by the caller
+       * don't to generate new kinv and r values */
+      if (in_kinv != NULL && in_r != NULL) {
+        OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ECDSA_R_NEED_NEW_SETUP_VALUES);
+        goto err;
+      }
+    } else {
+      /* s != 0 => we have a valid signature */
+      break;
+    }
+  }
+
+  ok = 1;
+
+err:
+  if (!ok) {
+    ECDSA_SIG_free(ret);
+    ret = NULL;
+  }
+  if (ctx)
+    BN_CTX_free(ctx);
+  if (m)
+    BN_clear_free(m);
+  if (tmp)
+    BN_clear_free(tmp);
+  if (order)
+    BN_free(order);
+  if (kinv)
+    BN_clear_free(kinv);
+  return ret;
+}
+
+int ECDSA_sign_ex(int type, const uint8_t *digest, size_t digest_len,
+                  uint8_t *sig, unsigned int *sig_len, const BIGNUM *kinv,
+                  const BIGNUM *r, EC_KEY *eckey) {
+  ECDSA_SIG *s = NULL;
+
+  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_ex, ECDSA_R_NOT_IMPLEMENTED);
+    *sig_len = 0;
+    return 0;
+  }
+
+  s = ECDSA_do_sign_ex(digest, digest_len, kinv, r, eckey);
+  if (s == NULL) {
+    *sig_len = 0;
+    return 0;
+  }
+  *sig_len = i2d_ECDSA_SIG(s, &sig);
+  ECDSA_SIG_free(s);
+  return 1;
+}
diff --git a/crypto/ecdsa/ecdsa.h b/crypto/ecdsa/ecdsa.h
new file mode 100644
index 0000000..6ad7ad2
--- /dev/null
+++ b/crypto/ecdsa/ecdsa.h
@@ -0,0 +1,187 @@
+/* ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_ECDSA_H
+#define OPENSSL_HEADER_ECDSA_H
+
+#include <openssl/base.h>
+
+#include <openssl/ec_key.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* DSA contains functions for signing and verifing with the Digital Signature
+ * Algorithm over elliptic curves. */
+
+
+/* Signing and verifing. */
+
+/* ECDSA_sign signs |digest_len| bytes from |digest| with |key| and writes the
+ * resulting signature to |sig|, which must have |ECDSA_size(key)| bytes of
+ * space. On successful exit, |*sig_len| is set to the actual number of bytes
+ * written. The |type| argument should be zero. It returns one on success and
+ * zero otherwise. */
+int ECDSA_sign(int type, const uint8_t *digest, size_t digest_len, uint8_t *sig,
+               unsigned int *sig_len, EC_KEY *key);
+
+/* ECDSA_verify verifies that |sig_len| bytes from |sig| constitute a valid
+ * signature by |key| of |digest|. (The |type| argument should be zero.) It
+ * returns one on success, zero if the signature is invalid and -1 on error.
+ *
+ * DANGER: this function differs from the usual OpenSSL return value
+ * convention.
+ *
+ * TODO(fork): change this to return the usual 0/1. */
+int ECDSA_verify(int type, const uint8_t *digest, size_t digest_len,
+                 const uint8_t *sig, size_t sig_len, EC_KEY *key);
+
+/* ECDSA_size returns the maximum size of an ECDSA signature using |key|. It
+ * returns zero on error. */
+size_t ECDSA_size(const EC_KEY *key);
+
+
+/* Low-level signing and verification.
+ *
+ * Low-level functions handle signatures as |ECDSA_SIG| structures which allow
+ * the two values in an ECDSA signature to be handled separately. */
+
+struct ecdsa_sig_st {
+  BIGNUM *r;
+  BIGNUM *s;
+};
+
+/* ECDSA_SIG_new returns a fresh |ECDSA_SIG| structure or NULL on error. */
+ECDSA_SIG *ECDSA_SIG_new(void);
+
+/* ECDSA_SIG_free frees |sig| its member |BIGNUM|s. */
+void ECDSA_SIG_free(ECDSA_SIG *sig);
+
+/* ECDSA_sign signs |digest_len| bytes from |digest| with |key| and returns the
+ * resulting signature structure, or NULL on error.
+ *
+ * TODO(fork): remove this function. */
+ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
+                         EC_KEY *key);
+
+/* ECDSA_verify verifies that |sig| constitutes a valid signature by |key| of
+ * |digest|. It returns one on success, zero if the signature is invalid and -1
+ * on error.
+ *
+ * DANGER: this function differs from the usual OpenSSL return value
+ * convention.
+ *
+ * TODO(fork): remove this function. */
+int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
+                    const ECDSA_SIG *sig, EC_KEY *key);
+
+
+/* Signing with precomputation.
+ *
+ * Parts of the ECDSA signature can be independent of the message to be signed
+ * thus it's possible to precompute them and reduce the signing latency.
+ *
+ * TODO(fork): remove support for this as it cannot support safe-randomness. */
+
+/* ECDSA_sign_setup precomputes parts of an ECDSA signing operation. It sets
+ * |*kinv| and |*rp| to the precomputed values and uses the |ctx| argument, if
+ * not NULL. It returns one on success and zero otherwise. */
+int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **rp);
+
+/* ECDSA_do_sign_ex is the same as |ECDSA_do_sign| but takes precomputed values
+ * as generated by |ECDSA_sign_setup|. */
+ECDSA_SIG *ECDSA_do_sign_ex(const uint8_t *digest, size_t digest_len,
+                            const BIGNUM *kinv, const BIGNUM *rp,
+                            EC_KEY *eckey);
+
+/* ECDSA_sign_ex is the same as |ECDSA_sign| but takes precomputed values as
+ * generated by |ECDSA_sign_setup|. */
+int ECDSA_sign_ex(int type, const uint8_t *digest, size_t digest_len,
+                  uint8_t *sig, unsigned int *sig_len, const BIGNUM *kinv,
+                  const BIGNUM *rp, EC_KEY *eckey);
+
+
+/* ASN.1 functions. */
+
+/* d2i_ECDSA_SIG parses an ASN.1, DER-encoded, signature from |len| bytes at
+ * |*inp|. If |out| is not NULL then, on exit, a pointer to the result is in
+ * |*out|. If |*out| is already non-NULL on entry then the result is written
+ * directly into |*out|, otherwise a fresh |ECDSA_SIG| is allocated. On
+ * successful exit, |*inp| is advanced past the DER structure. It returns the
+ * result or NULL on error. */
+ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **out, const uint8_t **inp, long len);
+
+/* i2d_ECDSA_SIG marshals a signature from |sig| to an ASN.1, DER
+ * structure. If |outp| is not NULL then the result is written to |*outp| and
+ * |*outp| is advanced just past the output. It returns the number of bytes in
+ * the result, whether written or not, or a negative value on error. */
+int i2d_ECDSA_SIG(const ECDSA_SIG *sig, uint8_t **outp);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define ECDSA_F_digest_to_bn 100
+#define ECDSA_F_ECDSA_do_verify 101
+#define ECDSA_F_ECDSA_sign_setup 102
+#define ECDSA_F_ECDSA_do_sign_ex 103
+#define ECDSA_F_ECDSA_sign_ex 104
+#define ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED 100
+#define ECDSA_R_NEED_NEW_SETUP_VALUES 101
+#define ECDSA_R_MISSING_PARAMETERS 102
+#define ECDSA_R_BAD_SIGNATURE 103
+#define ECDSA_R_NOT_IMPLEMENTED 104
+
+#endif  /* OPENSSL_HEADER_ECDSA_H */
diff --git a/crypto/ecdsa/ecdsa_asn1.c b/crypto/ecdsa/ecdsa_asn1.c
new file mode 100644
index 0000000..9d68361
--- /dev/null
+++ b/crypto/ecdsa/ecdsa_asn1.c
@@ -0,0 +1,111 @@
+/* ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ecdsa.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/ec_key.h>
+
+#include "../ec/internal.h"
+
+
+ASN1_SEQUENCE(ECDSA_SIG) = {
+    ASN1_SIMPLE(ECDSA_SIG, r, CBIGNUM),
+    ASN1_SIMPLE(ECDSA_SIG, s, CBIGNUM),
+} ASN1_SEQUENCE_END(ECDSA_SIG);
+
+DECLARE_ASN1_FUNCTIONS_const(ECDSA_SIG);
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(ECDSA_SIG, ECDSA_SIG);
+IMPLEMENT_ASN1_FUNCTIONS_const(ECDSA_SIG);
+
+size_t ECDSA_size(const EC_KEY *key) {
+  size_t ret, i;
+  ASN1_INTEGER bs;
+  BIGNUM *order = NULL;
+  unsigned char buf[4];
+  const EC_GROUP *group;
+
+  if (key->ecdsa_meth && key->ecdsa_meth->size) {
+    return key->ecdsa_meth->size(key);
+  }
+
+  if (key == NULL) {
+    return 0;
+  }
+  group = EC_KEY_get0_group(key);
+  if (group == NULL) {
+    return 0;
+  }
+
+  order = BN_new();
+  if (order == NULL) {
+    return 0;
+  }
+  if (!EC_GROUP_get_order(group, order, NULL)) {
+    BN_clear_free(order);
+    return 0;
+  }
+
+  i = BN_num_bits(order);
+  bs.length = (i + 7) / 8;
+  bs.data = buf;
+  bs.type = V_ASN1_INTEGER;
+  /* If the top bit is set the ASN.1 encoding is 1 larger. */
+  buf[0] = 0xff;
+
+  i = i2d_ASN1_INTEGER(&bs, NULL);
+  i += i; /* r and s */
+  ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE);
+  BN_clear_free(order);
+  return ret;
+}
diff --git a/crypto/ecdsa/ecdsa_error.c b/crypto/ecdsa/ecdsa_error.c
new file mode 100644
index 0000000..4de508a
--- /dev/null
+++ b/crypto/ecdsa/ecdsa_error.c
@@ -0,0 +1,31 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "ecdsa.h"
+
+const ERR_STRING_DATA ECDSA_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_do_sign_ex, 0), "ECDSA_do_sign_ex"},
+  {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_do_verify, 0), "ECDSA_do_verify"},
+  {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_sign_ex, 0), "ECDSA_sign_ex"},
+  {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_sign_setup, 0), "ECDSA_sign_setup"},
+  {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_digest_to_bn, 0), "digest_to_bn"},
+  {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_BAD_SIGNATURE), "BAD_SIGNATURE"},
+  {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_MISSING_PARAMETERS), "MISSING_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_NEED_NEW_SETUP_VALUES), "NEED_NEW_SETUP_VALUES"},
+  {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_NOT_IMPLEMENTED), "NOT_IMPLEMENTED"},
+  {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED), "RANDOM_NUMBER_GENERATION_FAILED"},
+  {0, NULL},
+};
diff --git a/crypto/ecdsa/ecdsa_test.c b/crypto/ecdsa/ecdsa_test.c
new file mode 100644
index 0000000..64480e3
--- /dev/null
+++ b/crypto/ecdsa/ecdsa_test.c
@@ -0,0 +1,309 @@
+/* ====================================================================
+ * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ecdsa.h>
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rand.h>
+
+
+int test_builtin(BIO *out) {
+  size_t n = 0;
+  EC_KEY *eckey = NULL, *wrong_eckey = NULL;
+  EC_GROUP *group;
+  ECDSA_SIG *ecdsa_sig = NULL;
+  unsigned char digest[20], wrong_digest[20];
+  unsigned char *signature = NULL;
+  const unsigned char *sig_ptr;
+  unsigned char *sig_ptr2;
+  unsigned char *raw_buf = NULL;
+  unsigned int sig_len, degree, r_len, s_len, bn_len, buf_len;
+  int nid, ret = 0;
+
+  /* fill digest values with some random data */
+  if (!RAND_pseudo_bytes(digest, 20) || !RAND_pseudo_bytes(wrong_digest, 20)) {
+    BIO_printf(out, "ERROR: unable to get random data\n");
+    goto builtin_err;
+  }
+
+  /* create and verify a ecdsa signature with every availble curve
+   * (with ) */
+  BIO_printf(out,
+             "\ntesting ECDSA_sign() and ECDSA_verify() "
+             "with some internal curves:\n");
+
+  static const int kCurveNIDs[] = {NID_secp224r1, NID_X9_62_prime256v1,
+                                   NID_secp384r1, NID_secp521r1, NID_undef};
+
+  /* now create and verify a signature for every curve */
+  for (n = 0; kCurveNIDs[n] != NID_undef; n++) {
+    unsigned char dirt, offset;
+
+    nid = kCurveNIDs[n];
+    /* create new ecdsa key (== EC_KEY) */
+    eckey = EC_KEY_new();
+    if (eckey == NULL) {
+      goto builtin_err;
+    }
+    group = EC_GROUP_new_by_curve_name(nid);
+    if (group == NULL) {
+      goto builtin_err;
+    }
+    if (!EC_KEY_set_group(eckey, group)) {
+      goto builtin_err;
+    }
+    EC_GROUP_free(group);
+    degree = EC_GROUP_get_degree(EC_KEY_get0_group(eckey));
+    if (degree < 160) {
+      /* Too small to test. */
+      EC_KEY_free(eckey);
+      eckey = NULL;
+      continue;
+    }
+
+    BIO_printf(out, "%s: ", OBJ_nid2sn(nid));
+    /* create key */
+    if (!EC_KEY_generate_key(eckey)) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    /* create second key */
+    wrong_eckey = EC_KEY_new();
+    if (wrong_eckey == NULL) {
+      goto builtin_err;
+    }
+    group = EC_GROUP_new_by_curve_name(nid);
+    if (group == NULL) {
+      goto builtin_err;
+    }
+    if (EC_KEY_set_group(wrong_eckey, group) == 0) {
+      goto builtin_err;
+    }
+    EC_GROUP_free(group);
+    if (!EC_KEY_generate_key(wrong_eckey)) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+    /* check key */
+    if (!EC_KEY_check_key(eckey)) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+    /* create signature */
+    sig_len = ECDSA_size(eckey);
+    signature = OPENSSL_malloc(sig_len);
+    if (signature == NULL) {
+      goto builtin_err;
+    }
+    if (!ECDSA_sign(0, digest, 20, signature, &sig_len, eckey)) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+    /* verify signature */
+    if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) != 1) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+    /* verify signature with the wrong key */
+    if (ECDSA_verify(0, digest, 20, signature, sig_len, wrong_eckey) == 1) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+    /* wrong digest */
+    if (ECDSA_verify(0, wrong_digest, 20, signature, sig_len, eckey) == 1) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+    /* wrong length */
+    if (ECDSA_verify(0, digest, 20, signature, sig_len - 1, eckey) == 1) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+
+    /* Modify a single byte of the signature: to ensure we don't
+     * garble the ASN1 structure, we read the raw signature and
+     * modify a byte in one of the bignums directly. */
+    sig_ptr = signature;
+    ecdsa_sig = d2i_ECDSA_SIG(NULL, &sig_ptr, sig_len);
+    if (ecdsa_sig == NULL) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+
+    /* Store the two BIGNUMs in raw_buf. */
+    r_len = BN_num_bytes(ecdsa_sig->r);
+    s_len = BN_num_bytes(ecdsa_sig->s);
+    bn_len = (degree + 7) / 8;
+    if (r_len > bn_len || s_len > bn_len) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    buf_len = 2 * bn_len;
+    raw_buf = OPENSSL_malloc(buf_len);
+    if (raw_buf == NULL) {
+      goto builtin_err;
+    }
+    /* Pad the bignums with leading zeroes. */
+    memset(raw_buf, 0, buf_len);
+    BN_bn2bin(ecdsa_sig->r, raw_buf + bn_len - r_len);
+    BN_bn2bin(ecdsa_sig->s, raw_buf + buf_len - s_len);
+
+    /* Modify a single byte in the buffer. */
+    offset = raw_buf[10] % buf_len;
+    dirt = raw_buf[11] ? raw_buf[11] : 1;
+    raw_buf[offset] ^= dirt;
+    /* Now read the BIGNUMs back in from raw_buf. */
+    if (BN_bin2bn(raw_buf, bn_len, ecdsa_sig->r) == NULL ||
+        BN_bin2bn(raw_buf + bn_len, bn_len, ecdsa_sig->s) == NULL) {
+      goto builtin_err;
+    }
+
+    sig_ptr2 = signature;
+    sig_len = i2d_ECDSA_SIG(ecdsa_sig, &sig_ptr2);
+    if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) == 1) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    /* Sanity check: undo the modification and verify signature. */
+    raw_buf[offset] ^= dirt;
+    if (BN_bin2bn(raw_buf, bn_len, ecdsa_sig->r) == NULL ||
+        BN_bin2bn(raw_buf + bn_len, bn_len, ecdsa_sig->s) == NULL) {
+      goto builtin_err;
+    }
+
+    sig_ptr2 = signature;
+    sig_len = i2d_ECDSA_SIG(ecdsa_sig, &sig_ptr2);
+    if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) != 1) {
+      BIO_printf(out, " failed\n");
+      goto builtin_err;
+    }
+    BIO_printf(out, ".");
+    (void)BIO_flush(out);
+
+    BIO_printf(out, " ok\n");
+    /* cleanup */
+    /* clean bogus errors */
+    ERR_clear_error();
+    OPENSSL_free(signature);
+    signature = NULL;
+    EC_KEY_free(eckey);
+    eckey = NULL;
+    EC_KEY_free(wrong_eckey);
+    wrong_eckey = NULL;
+    ECDSA_SIG_free(ecdsa_sig);
+    ecdsa_sig = NULL;
+    OPENSSL_free(raw_buf);
+    raw_buf = NULL;
+  }
+
+  ret = 1;
+builtin_err:
+  if (eckey)
+    EC_KEY_free(eckey);
+  if (wrong_eckey)
+    EC_KEY_free(wrong_eckey);
+  if (ecdsa_sig)
+    ECDSA_SIG_free(ecdsa_sig);
+  if (signature)
+    OPENSSL_free(signature);
+  if (raw_buf)
+    OPENSSL_free(raw_buf);
+
+  return ret;
+}
+
+int main(void) {
+  int ret = 1;
+  BIO *out;
+
+  out = BIO_new_fp(stdout, BIO_NOCLOSE);
+
+  ERR_load_crypto_strings();
+
+  if (!test_builtin(out))
+    goto err;
+
+  ret = 0;
+
+err:
+  if (ret)
+    BIO_printf(out, "\nECDSA test failed\n");
+  else
+    BIO_printf(out, "\nPASS\n");
+  if (ret)
+    BIO_print_errors(out);
+
+  if (out != NULL)
+    BIO_free(out);
+
+  return ret;
+}
diff --git a/crypto/engine/CMakeLists.txt b/crypto/engine/CMakeLists.txt
new file mode 100644
index 0000000..5126e2a
--- /dev/null
+++ b/crypto/engine/CMakeLists.txt
@@ -0,0 +1,9 @@
+include_directories(. .. ../../include)
+
+add_library(
+	engine
+
+	OBJECT
+
+	engine.c
+)
diff --git a/crypto/engine/engine.c b/crypto/engine/engine.c
new file mode 100644
index 0000000..bb0886e
--- /dev/null
+++ b/crypto/engine/engine.c
@@ -0,0 +1,133 @@
+/* Copyright (c) 2014, 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/engine.h>
+
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/ec_key.h>
+#include <openssl/mem.h>
+#include <openssl/rsa.h>
+#include <openssl/thread.h>
+
+
+struct engine_st {
+  DH_METHOD *dh_method;
+  DSA_METHOD *dsa_method;
+  RSA_METHOD *rsa_method;
+  ECDSA_METHOD *ecdsa_method;
+};
+
+ENGINE *ENGINE_new() {
+  ENGINE *engine = OPENSSL_malloc(sizeof(ENGINE));
+  if (engine == NULL) {
+    return NULL;
+  }
+
+  memset(engine, 0, sizeof(ENGINE));
+  return engine;
+}
+
+void ENGINE_free(ENGINE *engine) {
+  if (engine->dh_method != NULL) {
+    METHOD_unref(engine->dh_method);
+  }
+
+  OPENSSL_free(engine);
+}
+
+/* set_method takes a pointer to a method and its given size and sets
+ * |*out_member| to point to a copy of it. The copy is |compiled_size| bytes
+ * long and has zero padding if needed. */
+static int set_method(void **out_member, const void *method, size_t method_size,
+                      size_t compiled_size) {
+  void *copy = OPENSSL_malloc(compiled_size);
+  if (copy == NULL) {
+    return 0;
+  }
+
+  memset(copy, 0, compiled_size);
+
+  if (method_size > compiled_size) {
+    method_size = compiled_size;
+  }
+  memcpy(copy, method, method_size);
+
+  METHOD_unref(*out_member);
+  *out_member = copy;
+
+  return 1;
+}
+
+int ENGINE_set_DH_method(ENGINE *engine, const DH_METHOD *method,
+                         size_t method_size) {
+  return set_method((void **)&engine->dh_method, method, method_size,
+                    sizeof(DH_METHOD));
+}
+
+DH_METHOD *ENGINE_get_DH_method(const ENGINE *engine) {
+  return engine->dh_method;
+}
+
+int ENGINE_set_DSA_method(ENGINE *engine, const DSA_METHOD *method,
+                         size_t method_size) {
+  return set_method((void **)&engine->dsa_method, method, method_size,
+                    sizeof(DSA_METHOD));
+}
+
+DSA_METHOD *ENGINE_get_DSA_method(const ENGINE *engine) {
+  return engine->dsa_method;
+}
+
+int ENGINE_set_RSA_method(ENGINE *engine, const RSA_METHOD *method,
+                         size_t method_size) {
+  return set_method((void **)&engine->rsa_method, method, method_size,
+                    sizeof(RSA_METHOD));
+}
+
+RSA_METHOD *ENGINE_get_RSA_method(const ENGINE *engine) {
+  return engine->rsa_method;
+}
+
+int ENGINE_set_ECDSA_method(ENGINE *engine, const ECDSA_METHOD *method,
+                            size_t method_size) {
+  return set_method((void **)&engine->ecdsa_method, method, method_size,
+                    sizeof(ECDSA_METHOD));
+}
+
+ECDSA_METHOD *ENGINE_get_ECDSA_method(const ENGINE *engine) {
+  return engine->ecdsa_method;
+}
+
+void METHOD_ref(void *method_in) {
+  struct openssl_method_common_st *method = method_in;
+
+  if (method->is_static) {
+    return;
+  }
+
+  CRYPTO_add(&method->references, 1, CRYPTO_LOCK_ENGINE);
+}
+
+void METHOD_unref(void *method_in) {
+  struct openssl_method_common_st *method = method_in;
+
+  if (method == NULL || method->is_static) {
+    return;
+  }
+
+  if (CRYPTO_add(&method->references, -1, CRYPTO_LOCK_ENGINE) == 0) {
+    OPENSSL_free(method);
+  }
+}
diff --git a/crypto/engine/engine.h b/crypto/engine/engine.h
new file mode 100644
index 0000000..c97196e
--- /dev/null
+++ b/crypto/engine/engine.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2014, 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_ENGINE_H
+#define OPENSSL_HEADER_ENGINE_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Engines are collections of methods. Methods are tables of function pointers,
+ * defined for certain algorithms, that allow operations on those algorithms to
+ * be overridden via a callback. This can be used, for example, to implement an
+ * RSA* that forwards operations to a hardware module.
+ *
+ * Methods are reference counted but |ENGINE|s are not. When creating a method,
+ * you should zero the whole structure and fill in the function pointers that
+ * you wish before setting it on an |ENGINE|. Any functions pointers that
+ * are NULL indicate that the default behaviour should be used. */
+
+
+/* Allocation and destruction. */
+
+/* ENGINE_new returns an empty ENGINE that uses the default method for all
+ * algorithms. */
+ENGINE *ENGINE_new();
+
+/* ENGINE_free decrements the reference counts for all methods linked from
+ * |engine| and frees |engine| itself. */
+void ENGINE_free(ENGINE *engine);
+
+
+/* Method accessors.
+ *
+ * Method accessors take a method pointer and the size of the structure. The
+ * size allows for ABI compatibility in the case that the method structure is
+ * extended with extra elements at the end. Methods are always copied by the
+ * set functions.
+ *
+ * Set functions return one on success and zero on allocation failure. */
+
+int ENGINE_set_DH_method(ENGINE *engine, const DH_METHOD *method,
+                         size_t method_size);
+DH_METHOD *ENGINE_get_DH_method(const ENGINE *engine);
+
+int ENGINE_set_DSA_method(ENGINE *engine, const DSA_METHOD *method,
+                          size_t method_size);
+DSA_METHOD *ENGINE_get_DSA_method(const ENGINE *engine);
+
+int ENGINE_set_RSA_method(ENGINE *engine, const RSA_METHOD *method,
+                          size_t method_size);
+RSA_METHOD *ENGINE_get_RSA_method(const ENGINE *engine);
+
+int ENGINE_set_ECDSA_method(ENGINE *engine, const ECDSA_METHOD *method,
+                            size_t method_size);
+ECDSA_METHOD *ENGINE_get_ECDSA_method(const ENGINE *engine);
+
+
+/* Generic method functions.
+ *
+ * These functions take a void* type but actually operate on all method
+ * structures. */
+
+/* METHOD_ref increments the reference count of |method|. */
+void METHOD_ref(void *method);
+
+/* METHOD_unref decrements the reference count of |method| and frees it if the
+ * reference count drops to zero. */
+void METHOD_unref(void *method);
+
+
+/* Private functions. */
+
+/* openssl_method_common_st contains the common part of all method structures.
+ * This must be the first member of all method structures. */
+struct openssl_method_common_st {
+  int references;
+  char is_static;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_ENGINE_H */
diff --git a/crypto/err/CMakeLists.txt b/crypto/err/CMakeLists.txt
new file mode 100644
index 0000000..1ac4951
--- /dev/null
+++ b/crypto/err/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(. .. ../../include)
+
+add_library(
+	err
+
+	OBJECT
+
+	err.c
+	err_impl.c
+)
+
+add_executable(
+	err_test
+
+	err_test.c
+)
+
+target_link_libraries(err_test crypto)
diff --git a/crypto/err/err.c b/crypto/err/err.c
new file mode 100644
index 0000000..b78ee6b
--- /dev/null
+++ b/crypto/err/err.c
@@ -0,0 +1,737 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/err.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/thread.h>
+
+
+/* err_fns contains a pointer to the current error implementation. */
+static const struct ERR_FNS_st *err_fns = NULL;
+extern const struct ERR_FNS_st openssl_err_default_impl;
+
+#define ERRFN(a) err_fns->a
+
+/* err_fns_check is an internal function that checks whether "err_fns" is set
+ * and if not, sets it to the default. */
+static void err_fns_check(void) {
+  /* In practice, this is not a race problem because loading the error strings
+   * at init time will cause this pointer to be set before the process goes
+   * multithreaded. */
+  if (err_fns) {
+    return;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+  if (!err_fns) {
+    err_fns = &openssl_err_default_impl;
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+}
+
+/* err_clear_data frees the optional |data| member of the given error. */
+static void err_clear_data(struct err_error_st *error) {
+  if (error->data != NULL && (error->flags & ERR_FLAG_MALLOCED) != 0) {
+    OPENSSL_free(error->data);
+  }
+  error->data = NULL;
+  error->flags &= ~ERR_FLAG_MALLOCED;
+}
+
+/* err_clear clears the given queued error. */
+static void err_clear(struct err_error_st *error) {
+  err_clear_data(error);
+  memset(error, 0, sizeof(struct err_error_st));
+}
+
+/* err_get_state gets the ERR_STATE object for the current thread. */
+static ERR_STATE *err_get_state(void) {
+  err_fns_check();
+  return ERRFN(get_state)();
+}
+
+static uint32_t get_error_values(int inc, int top, const char **file, int *line,
+                                 char **data, int *flags) {
+  unsigned i = 0;
+  ERR_STATE *state;
+  struct err_error_st *error;
+  uint32_t ret;
+
+  state = err_get_state();
+
+  if (state->bottom == state->top) {
+    return 0;
+  }
+
+  if (top) {
+    /* last error */
+    i = state->top;
+  } else {
+    i = (state->bottom + 1) % ERR_NUM_ERRORS;
+  }
+
+  error = &state->errors[i];
+  ret = error->packed;
+
+  if (file != NULL && line != NULL) {
+    if (error->file == NULL) {
+      *file = "NA";
+      *line = 0;
+    } else {
+      *file = error->file;
+      *line = error->line;
+    }
+  }
+
+  if (data != NULL) {
+    if (error->data == NULL) {
+      *data = "";
+      if (flags != NULL) {
+        *flags = 0;
+      }
+    } else {
+      *data = error->data;
+      if (flags != NULL) {
+        *flags = error->flags & ERR_FLAG_PUBLIC_MASK;
+      }
+      error->data = NULL;
+      error->flags = 0;
+    }
+  }
+
+  if (inc) {
+    assert(!top);
+    err_clear(error);
+    state->bottom = i;
+  }
+
+  return ret;
+}
+
+uint32_t ERR_get_error(void) {
+  return get_error_values(1, 0, NULL, NULL, NULL, NULL);
+}
+
+uint32_t ERR_get_error_line(const char **file, int *line) {
+  return get_error_values(1, 0, file, line, NULL, NULL);
+}
+
+uint32_t ERR_get_error_line_data(const char **file, int *line,
+                                 char **data, int *flags) {
+  return get_error_values(1, 0, file, line, data, flags);
+}
+
+uint32_t ERR_peek_error(void) {
+  return get_error_values(0, 0, NULL, NULL, NULL, NULL);
+}
+
+uint32_t ERR_peek_error_line(const char **file, int *line) {
+  return get_error_values(0, 0, file, line, NULL, NULL);
+}
+
+uint32_t ERR_peek_error_line_data(const char **file, int *line,
+                                  const char **data, int *flags) {
+  return get_error_values(0, 0, file, line, (char **) data, flags);
+}
+
+uint32_t ERR_peek_last_error(void) {
+  return get_error_values(0, 1, NULL, NULL, NULL, NULL);
+}
+
+uint32_t ERR_peek_last_error_line(const char **file, int *line) {
+  return get_error_values(0, 1, file, line, NULL, NULL);
+}
+
+uint32_t ERR_peek_last_error_line_data(const char **file, int *line,
+                                       const char **data, int *flags) {
+  return get_error_values(0, 1, file, line, (char **) data, flags);
+}
+
+void ERR_clear_error(void) {
+  ERR_STATE *const state = err_get_state();
+  unsigned i;
+
+  for (i = 0; i < ERR_NUM_ERRORS; i++) {
+    err_clear(&state->errors[i]);
+  }
+
+  state->top = state->bottom = 0;
+}
+
+void ERR_clear_system_error(void) {
+  errno = 0;
+}
+
+char *ERR_error_string(uint32_t packed_error, char *ret) {
+  static char buf[ERR_ERROR_STRING_BUF_LEN];
+
+  if (ret == NULL) {
+    /* TODO(fork): remove this. */
+    ret = buf;
+  }
+
+#if !defined(NDEBUG)
+  /* This is aimed to help catch callers who don't provide
+   * |ERR_ERROR_STRING_BUF_LEN| bytes of space. */
+  memset(ret, 0, ERR_ERROR_STRING_BUF_LEN);
+#endif
+
+  ERR_error_string_n(packed_error, ret, ERR_ERROR_STRING_BUF_LEN);
+
+  return ret;
+}
+
+void ERR_error_string_n(uint32_t packed_error, char *buf, size_t len) {
+  char lib_buf[64], func_buf[64], reason_buf[64];
+  const char *lib_str, *func_str, *reason_str;
+  unsigned lib, func, reason;
+
+  if (len == 0) {
+    return;
+  }
+
+  lib = ERR_GET_LIB(packed_error);
+  func = ERR_GET_FUNC(packed_error);
+  reason = ERR_GET_REASON(packed_error);
+
+  lib_str = ERR_lib_error_string(packed_error);
+  func_str = ERR_func_error_string(packed_error);
+  reason_str = ERR_reason_error_string(packed_error);
+
+  if (lib_str == NULL) {
+    BIO_snprintf(lib_buf, sizeof(lib_buf), "lib(%u)", lib);
+    lib_str = lib_buf;
+  }
+
+  if (func_str == NULL) {
+    BIO_snprintf(func_buf, sizeof(func_buf), "func(%u)", func);
+    func_str = func_buf;
+  }
+
+  if (reason_str == NULL) {
+    BIO_snprintf(reason_buf, sizeof(reason_buf), "reason(%u)", reason);
+    reason_str = reason_buf;
+  }
+
+  BIO_snprintf(buf, len, "error:%08" PRIx32 ":%s:%s:%s",
+               packed_error, lib_str, func_str, reason_str);
+
+  if (strlen(buf) == len - 1) {
+    /* output may be truncated; make sure we always have 5 colon-separated
+     * fields, i.e. 4 colons. */
+    static const unsigned num_colons = 4;
+    unsigned i;
+    char *s = buf;
+
+    if (len <= num_colons) {
+      /* In this situation it's not possible to ensure that the correct number
+       * of colons are included in the output. */
+      return;
+    }
+
+    for (i = 0; i < num_colons; i++) {
+      char *colon = strchr(s, ':');
+      char *last_pos = &buf[len - 1] - num_colons + i;
+
+      if (colon == NULL || colon > last_pos) {
+        /* set colon |i| at last possible position (buf[len-1] is the
+         * terminating 0). If we're setting this colon, then all whole of the
+         * rest of the string must be colons in order to have the correct
+         * number. */
+        memset(last_pos, ':', num_colons - i);
+        break;
+      }
+
+      s = colon + 1;
+    }
+  }
+}
+
+/* err_component_error_string returns the error string associated with
+ * |packed_error|, which must be of a special form matching the keys inserted
+ * into the error hash table. */
+static const char *err_component_error_string(uint32_t packed_error) {
+  ERR_STRING_DATA *p;
+
+  err_fns_check();
+  p = ERRFN(get_item)(packed_error);
+
+  if (p == NULL) {
+    return NULL;
+  }
+  return p->string;
+}
+
+const char *ERR_lib_error_string(uint32_t packed_error) {
+  return err_component_error_string(ERR_PACK(ERR_GET_LIB(packed_error), 0, 0));
+}
+
+const char *ERR_func_error_string(uint32_t packed_error) {
+  return err_component_error_string(
+      ERR_PACK(ERR_GET_LIB(packed_error), ERR_GET_FUNC(packed_error), 0));
+}
+
+const char *ERR_reason_error_string(uint32_t packed_error) {
+  const char *reason_str = err_component_error_string(
+      ERR_PACK(ERR_GET_LIB(packed_error), 0, ERR_GET_REASON(packed_error)));
+
+  if (reason_str != NULL) {
+    return reason_str;
+  }
+
+  return err_component_error_string(
+      ERR_PACK(0, 0, ERR_GET_REASON(packed_error)));
+}
+
+void ERR_print_errors_cb(ERR_print_errors_callback_t callback, void *ctx) {
+  CRYPTO_THREADID current_thread;
+  char buf[ERR_ERROR_STRING_BUF_LEN];
+  char buf2[1024];
+  unsigned long thread_hash;
+  const char *file;
+  char *data;
+  int line, flags;
+  uint32_t packed_error;
+
+  CRYPTO_THREADID_current(&current_thread);
+  thread_hash = CRYPTO_THREADID_hash(&current_thread);
+
+  for (;;) {
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    if (packed_error == 0) {
+      break;
+    }
+
+    ERR_error_string_n(packed_error, buf, sizeof(buf));
+    BIO_snprintf(buf2, sizeof(buf2), "%lu:%s:%s:%d:%s\n", thread_hash, buf,
+                 file, line, (flags & ERR_FLAG_STRING) ? data : "");
+    if (callback(buf2, strlen(buf2), ctx) <= 0) {
+      break;
+    }
+    if (flags & ERR_FLAG_MALLOCED) {
+      OPENSSL_free(data);
+    }
+  }
+}
+
+/* err_set_error_data sets the data on the most recent error. The |flags|
+ * argument is a combination of the |ERR_FLAG_*| values. */
+static void err_set_error_data(char *data, int flags) {
+  ERR_STATE *const state = err_get_state();
+  struct err_error_st *error;
+
+  if (state->top == state->bottom) {
+    return;
+  }
+
+  error = &state->errors[state->top];
+
+  err_clear_data(error);
+  error->data = data;
+  error->flags = flags;
+}
+
+void ERR_put_error(int library, int func, int reason, const char *file,
+                   unsigned line) {
+  ERR_STATE *const state = err_get_state();
+  struct err_error_st *error;
+
+  if (library == ERR_LIB_SYS && reason == 0) {
+#if defined(WIN32)
+    reason = GetLastError();
+#else
+    reason = errno;
+#endif
+  }
+
+  state->top = (state->top + 1) % ERR_NUM_ERRORS;
+  if (state->top == state->bottom) {
+    state->bottom = (state->bottom + 1) % ERR_NUM_ERRORS;
+  }
+
+  error = &state->errors[state->top];
+  err_clear(error);
+  error->file = file;
+  error->line = line;
+  error->packed = ERR_PACK(library, func, reason);
+}
+
+/* ERR_add_error_data_vdata takes a variable number of const char* pointers,
+ * concatenates them and sets the result as the data on the most recent
+ * error. */
+static void err_add_error_vdata(unsigned num, va_list args) {
+  size_t alloced, new_len, len = 0, substr_len;
+  char *buf;
+  const char *substr;
+  unsigned i;
+
+  alloced = 80;
+  buf = OPENSSL_malloc(alloced + 1);
+  if (buf == NULL) {
+    return;
+  }
+
+  for (i = 0; i < num; i++) {
+    substr = va_arg(args, const char *);
+    if (substr == NULL) {
+      continue;
+    }
+
+    substr_len = strlen(substr);
+    new_len = len + substr_len;
+    if (new_len > alloced) {
+      char *new_buf;
+
+      if (alloced + 20 + 1 < alloced) {
+        /* overflow. */
+        OPENSSL_free(buf);
+        return;
+      }
+
+      alloced = new_len + 20;
+      new_buf = OPENSSL_realloc(buf, alloced + 1);
+      if (new_buf == NULL) {
+        OPENSSL_free(buf);
+        return;
+      }
+      buf = new_buf;
+    }
+
+    memcpy(buf + len, substr, substr_len);
+    len = new_len;
+  }
+
+  buf[len] = 0;
+  err_set_error_data(buf, ERR_FLAG_MALLOCED | ERR_FLAG_STRING);
+}
+
+void ERR_add_error_data(unsigned count, ...) {
+  va_list args;
+  va_start(args, count);
+  err_add_error_vdata(count, args);
+  va_end(args);
+}
+
+void ERR_add_error_dataf(const char *format, ...) {
+  va_list ap;
+  char *buf;
+  static const unsigned buf_len = 256;
+
+  /* A fixed-size buffer is used because va_copy (which would be needed in
+   * order to call vsnprintf twice and measure the buffer) wasn't defined until
+   * C99. */
+  buf = OPENSSL_malloc(buf_len + 1);
+  if (buf == NULL) {
+    return;
+  }
+
+  va_start(ap, format);
+  BIO_vsnprintf(buf, buf_len, format, ap);
+  buf[buf_len] = 0;
+  va_end(ap);
+
+  err_set_error_data(buf, ERR_FLAG_MALLOCED | ERR_FLAG_STRING);
+}
+
+int ERR_set_mark(void) {
+  ERR_STATE *const state = err_get_state();
+
+  if (state->bottom == state->top) {
+    return 0;
+  }
+  state->errors[state->top].flags |= ERR_FLAG_MARK;
+  return 1;
+}
+
+int ERR_pop_to_mark(void) {
+  ERR_STATE *const state = err_get_state();
+  struct err_error_st *error;
+
+  while (state->bottom != state->top) {
+    error = &state->errors[state->top];
+
+    if ((error->flags & ERR_FLAG_MARK) != 0) {
+      break;
+    }
+
+    err_clear(error);
+    if (state->top == 0) {
+      state->top = ERR_NUM_ERRORS - 1;
+    } else {
+      state->top--;
+    }
+  }
+
+  if (state->bottom == state->top) {
+    return 0;
+  }
+
+  error->flags &= ~ERR_FLAG_MARK;
+  return 1;
+}
+
+static const char *const kLibraryNames[ERR_NUM_LIBS] = {
+    "invalid library (0)",
+    "unknown library",                            /* ERR_LIB_NONE */
+    "system library",                             /* ERR_LIB_SYS */
+    "bignum routines",                            /* ERR_LIB_BN */
+    "RSA routines",                               /* ERR_LIB_RSA */
+    "Diffie-Hellman routines",                    /* ERR_LIB_DH */
+    "public key routines",                        /* ERR_LIB_EVP */
+    "memory buffer routines",                     /* ERR_LIB_BUF */
+    "object identifier routines",                 /* ERR_LIB_OBJ */
+    "PEM routines",                               /* ERR_LIB_PEM */
+    "DSA routines",                               /* ERR_LIB_DSA */
+    "X.509 certificate routines",                 /* ERR_LIB_X509 */
+    "ASN.1 encoding routines",                    /* ERR_LIB_ASN1 */
+    "configuration file routines",                /* ERR_LIB_CONF */
+    "common libcrypto routines",                  /* ERR_LIB_CRYPTO */
+    "elliptic curve routines",                    /* ERR_LIB_EC */
+    "SSL routines",                               /* ERR_LIB_SSL */
+    "BIO routines",                               /* ERR_LIB_BIO */
+    "PKCS7 routines",                             /* ERR_LIB_PKCS7 */
+    "PKCS8 routines",                             /* ERR_LIB_PKCS8 */
+    "X509 V3 routines",                           /* ERR_LIB_X509V3 */
+    "PKCS12 routines",                            /* ERR_LIB_PKCS12 */
+    "random number generator",                    /* ERR_LIB_RAND */
+    "ENGINE routines",                            /* ERR_LIB_ENGINE */
+    "OCSP routines",                              /* ERR_LIB_OCSP */
+    "UI routines",                                /* ERR_LIB_UI */
+    "COMP routines",                              /* ERR_LIB_COMP */
+    "ECDSA routines",                             /* ERR_LIB_ECDSA */
+    "ECDH routines",                              /* ERR_LIB_ECDH */
+    "HMAC routines",                              /* ERR_LIB_HMAC */
+    "Digest functions",                           /* ERR_LIB_DIGEST */
+    "Cipher functions",                           /* ERR_LIB_CIPHER */
+    "User defined functions",                     /* ERR_LIB_USER */
+};
+
+#define NUM_SYS_ERRNOS 127
+
+/* kStaticErrors provides storage for ERR_STRING_DATA values that are created
+ * at init time because we assume that ERR_STRING_DATA structures aren't
+ * allocated on the heap. */
+static ERR_STRING_DATA kStaticErrors[ERR_NUM_LIBS * 2 + NUM_SYS_ERRNOS];
+
+static ERR_STRING_DATA kGlobalErrors[] = {
+    {ERR_R_MALLOC_FAILURE, "malloc failure"},
+    {ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, "function should not be called"},
+    {ERR_R_PASSED_NULL_PARAMETER, "passed a null parameter"},
+    {ERR_R_INTERNAL_ERROR, "internal error"},
+
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_fopen, 0), "fopen"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_fclose, 0), "fclose"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_fread, 0), "fread"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_fwrite, 0), "fwrite"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_socket, 0), "socket"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_setsockopt, 0), "setsockopt"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_connect, 0), "connect"},
+    {ERR_PACK(ERR_LIB_SYS, SYS_F_getaddrinfo, 0), "getaddrinfo"},
+
+    {0, NULL},
+};
+
+
+extern const ERR_STRING_DATA ASN1_error_string_data[];
+extern const ERR_STRING_DATA BIO_error_string_data[];
+extern const ERR_STRING_DATA BN_error_string_data[];
+extern const ERR_STRING_DATA BUF_error_string_data[];
+extern const ERR_STRING_DATA CIPHER_error_string_data[];
+extern const ERR_STRING_DATA CONF_error_string_data[];
+extern const ERR_STRING_DATA CRYPTO_error_string_data[];
+extern const ERR_STRING_DATA DH_error_string_data[];
+extern const ERR_STRING_DATA DIGEST_error_string_data[];
+extern const ERR_STRING_DATA DSA_error_string_data[];
+extern const ERR_STRING_DATA ECDH_error_string_data[];
+extern const ERR_STRING_DATA ECDSA_error_string_data[];
+extern const ERR_STRING_DATA EC_error_string_data[];
+extern const ERR_STRING_DATA EVP_error_string_data[];
+extern const ERR_STRING_DATA OBJ_error_string_data[];
+extern const ERR_STRING_DATA PEM_error_string_data[];
+extern const ERR_STRING_DATA RSA_error_string_data[];
+extern const ERR_STRING_DATA X509V3_error_string_data[];
+extern const ERR_STRING_DATA X509_error_string_data[];
+
+static void err_load_strings(void) {
+  unsigned i, j = 0;
+
+  err_fns_check();
+
+  /* This loop loads strings for the libraries for the ERR_R_*_LIB
+   * reasons. */
+  for (i = ERR_LIB_NONE; i < ERR_NUM_LIBS; i++) {
+    ERR_STRING_DATA *data = &kStaticErrors[j++];
+    data->string = kLibraryNames[i];
+    data->error = ERR_PACK(i, 0, 0);
+    ERRFN(set_item)(data);
+
+    data = &kStaticErrors[j++];
+    data->string = kLibraryNames[i];
+    data->error = ERR_PACK(0, 0, i);
+    ERRFN(set_item)(data);
+  }
+
+  for (i = 1; i < 1 + NUM_SYS_ERRNOS; i++) {
+    /* The "SYS" library sets errno values as the reason for its errors.
+     * Thus we load the first |NUM_SYS_ERRNOS| errno strings as the
+     * reason strings for that library. */
+
+    ERR_STRING_DATA *data = &kStaticErrors[j++];
+    data->string = strerror(i);
+    data->error = ERR_PACK(ERR_LIB_SYS, 0, i);
+    ERRFN(set_item)(data);
+  }
+
+  ERR_load_strings(kGlobalErrors);
+
+  ERR_load_strings(ASN1_error_string_data);
+  ERR_load_strings(BIO_error_string_data);
+  ERR_load_strings(BN_error_string_data);
+  ERR_load_strings(BUF_error_string_data);
+  ERR_load_strings(CIPHER_error_string_data);
+  ERR_load_strings(CONF_error_string_data);
+  ERR_load_strings(CRYPTO_error_string_data);
+  ERR_load_strings(DH_error_string_data);
+  ERR_load_strings(DIGEST_error_string_data);
+  ERR_load_strings(DSA_error_string_data);
+  ERR_load_strings(ECDH_error_string_data);
+  ERR_load_strings(ECDSA_error_string_data);
+  ERR_load_strings(EC_error_string_data);
+  ERR_load_strings(EVP_error_string_data);
+  ERR_load_strings(OBJ_error_string_data);
+  ERR_load_strings(PEM_error_string_data);
+  ERR_load_strings(RSA_error_string_data);
+  ERR_load_strings(X509V3_error_string_data);
+  ERR_load_strings(X509_error_string_data);
+}
+
+void ERR_load_strings(const ERR_STRING_DATA *str) {
+  err_fns_check();
+
+  while (str->error) {
+    ERRFN(set_item)(str);
+    str++;
+  }
+}
+
+void ERR_load_crypto_strings() { err_load_strings(); }
+
+void ERR_free_strings() {
+  err_fns_check();
+  ERRFN(shutdown)();
+}
+
+void ERR_load_BIO_strings() {}
diff --git a/crypto/err/err.h b/crypto/err/err.h
new file mode 100644
index 0000000..411bc9b
--- /dev/null
+++ b/crypto/err/err.h
@@ -0,0 +1,496 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_ERR_H
+#define OPENSSL_HEADER_ERR_H
+
+#include <openssl/base.h>
+#include <openssl/thread.h>
+#include <openssl/lhash.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Error queue handling functions.
+ *
+ * Errors in OpenSSL are generally signalled by the return value of a function.
+ * When a function fails it may add an entry to a per-thread error queue,
+ * which is managed by the functions in this header.
+ *
+ * Each error contains:
+ *   1) The library (i.e. ec, pem, rsa) which created it.
+ *   2) A function identifier and reason code.
+ *   3) The file and line number of the call that added the error.
+ *   4) A pointer to some error specific data, which may be NULL.
+ *
+ * The library identifier, function identifier and reason code are packed in a
+ * uint32_t and there exist various functions for unpacking it.
+ *
+ * The typical behaviour is that an error will occur deep in a call queue and
+ * that code will push an error onto the error queue. As the error queue
+ * unwinds, other functions will push their own errors. Thus, the "least
+ * recent" error is the most specific and the other errors will provide a
+ * backtrace of sorts. */
+
+
+/* Startup and shutdown. */
+
+/* ERR_load_crypto_strings initialises the error string hash with builtin
+ * values. If this is not called then the string forms of errors produced by
+ * the functions below will contain numeric identifiers rather than
+ * human-readable strings. */
+void ERR_load_crypto_strings();
+
+/* ERR_free_strings frees any internal error values that have been loaded. This
+ * should only be called at process shutdown. */
+void ERR_free_strings();
+
+
+/* Reading and formatting errors. */
+
+/* ERR_get_error gets the packed error code for the least recent error and
+ * removes that error from the queue. If there are no errors in the queue then
+ * it returns zero. */
+uint32_t ERR_get_error(void);
+
+/* ERR_get_error_line acts like |ERR_get_error|, except that the file and line
+ * number of the call that added the error are also returned. */
+uint32_t ERR_get_error_line(const char **file, int *line);
+
+/* ERR_get_error_line_data acts like |ERR_get_error_line|, but also returns the
+ * error-specific data pointer and flags. The flags are a bitwise-OR of
+ * |ERR_FLAG_*| values. */
+uint32_t ERR_get_error_line_data(const char **file, int *line,
+                                 char **data, int *flags);
+
+/* The "peek" functions act like the |ERR_get_error| functions, above, but they
+ * do not remove the error from the queue. */
+uint32_t ERR_peek_error(void);
+uint32_t ERR_peek_error_line(const char **file, int *line);
+uint32_t ERR_peek_error_line_data(const char **file, int *line,
+                                  const char **data, int *flags);
+
+/* The "peek last" functions act like the "peek" functions, above, except that
+ * they return the most recent error. */
+uint32_t ERR_peek_last_error(void);
+uint32_t ERR_peek_last_error_line(const char **file, int *line);
+uint32_t ERR_peek_last_error_line_data(const char **file, int *line,
+                                       const char **data, int *flags);
+
+/* ERR_error_string generates a human-readable string representing
+ * |packed_error|, places it at |buf| (which must be at least
+ * ERR_ERROR_STRING_BUF_LEN bytes long) and returns |buf|. If |buf| is NULL,
+ * the error string is placed in a static buffer which is returned. (The static
+ * buffer may be overridden by concurrent calls in other threads so this form
+ * is deprecated.)
+ *
+ * The string will have the following format:
+ *
+ *   error:[error code]:[library name]:[function name]:[reason string]
+ *
+ * error code is an 8 digit hexadecimal number; library name, function name
+ * and reason string are ASCII text.
+ *
+ * TODO(fork): remove in favour of |ERR_error_string_n|. */
+char *ERR_error_string(uint32_t packed_error, char *buf);
+#define ERR_ERROR_STRING_BUF_LEN 256
+
+/* ERR_error_string_n is a variant of |ERR_error_string| that writes at most
+ * len characters (including the terminating NUL) and truncates the string if
+ * necessary. If |len| is greater than zero then |buf| is always NUL
+ * terminated. */
+void ERR_error_string_n(uint32_t packed_error, char *buf, size_t len);
+
+/* ERR_lib_error_string returns a string representation of the library that
+ * generated |packed_error|. */
+const char *ERR_lib_error_string(uint32_t packed_error);
+
+/* ERR_func_error_string returns a string representation of the function that
+ * generated |packed_error|. */
+const char *ERR_func_error_string(uint32_t packed_error);
+
+/* ERR_reason_error_string returns a string representation of the reason for
+ * |packed_error|. */
+const char *ERR_reason_error_string(uint32_t packed_error);
+
+/* ERR_print_errors_callback_t is the type of a function used by
+ * |ERR_print_errors_cb|. It takes a pointer to a human readable string (and
+ * its length) that describes an entry in the error queue. The |ctx| argument
+ * is an opaque pointer given to |ERR_print_errors_cb|.
+ *
+ * It should return one on success or zero on error, which will stop the
+ * iteration over the error queue. */
+typedef int (*ERR_print_errors_callback_t)(const char *str, size_t len,
+                                           void *ctx);
+
+/* ERR_print_errors_cb calls |callback| with a string representation of each
+ * error in the current thread's error queue, from the least recent to the most
+ * recent error.
+ *
+ * The string will have the following format (which differs from
+ * |ERR_error_string|):
+ *
+ *   [thread id]:error:[error code]:[library name]:[function name]:
+ *   [reason string]:[file]:[line number]:[optional string data]
+ *
+ * (All in one line.)
+ *
+ * The callback can return one to continue the iteration or zero to stop it.
+ * The |ctx| argument is an opaque value that is passed through to the
+ * callback. */
+void ERR_print_errors_cb(ERR_print_errors_callback_t callback, void *ctx);
+
+
+/* Clearing errors. */
+
+/* ERR_clear_error clears the error queue for the current thread. */
+void ERR_clear_error(void);
+
+
+/* Private functions. */
+
+/* ERR_clear_system_error clears the system's error value (i.e. errno). */
+void ERR_clear_system_error(void);
+
+/* OPENSSL_PUT_ERROR is used by OpenSSL code to add an error to the error
+ * queue. */
+#define OPENSSL_PUT_ERROR(library, func, reason)                         \
+  ERR_put_error(ERR_LIB_##library, library##_F_##func, reason, __FILE__, \
+                __LINE__)
+
+/* OPENSSL_PUT_SYSTEM_ERROR is used by OpenSSL code to add an error from the
+ * operating system to the error queue. */
+/* TODO(fork): include errno. */
+#define OPENSSL_PUT_SYSTEM_ERROR(func) \
+  ERR_put_error(ERR_LIB_SYS, SYS_F_##func, 0, __FILE__, __LINE__);
+
+/* ERR_put_error adds an error to the error queue, dropping the least recent
+ * error if neccessary for space reasons. */
+void ERR_put_error(int library, int func, int reason, const char *file,
+                   unsigned line);
+
+/* ERR_add_error_data takes a variable number (|count|) of const char*
+ * pointers, concatenates them and sets the result as the data on the most
+ * recent error. */
+void ERR_add_error_data(unsigned count, ...);
+
+/* ERR_add_error_dataf takes a printf-style format and arguments, and sets the
+ * result as the data on the most recent error. */
+void ERR_add_error_dataf(const char *format, ...);
+
+/* ERR_set_mark "marks" the most recent error for use with |ERR_pop_to_mark|.
+ * It returns one if an error was marked and zero if there are no errors. */
+int ERR_set_mark(void);
+
+/* ERR_pop_to_mark removes errors from the most recent to the least recent
+ * until (and not including) a "marked" error. It returns zero if no marked
+ * error was found (and thus all errors were removed) and one otherwise. Errors
+ * are marked using |ERR_set_mark|. */
+int ERR_pop_to_mark(void);
+
+struct err_error_st {
+  /* file contains the filename where the error occured. */
+  const char *file;
+  /* data contains optional data. It must be freed with |OPENSSL_free| if
+   * |flags&ERR_FLAG_MALLOCED|. */
+  char *data;
+  /* packed contains the error library, function and reason, as packed by
+   * ERR_PACK. */
+  uint32_t packed;
+  /* line contains the line number where the error occured. */
+  uint16_t line;
+  /* flags contains a bitwise-OR of ERR_FLAG_* values. */
+  uint8_t flags;
+};
+
+/* ERR_FLAG_MALLOCED means the the |data| member must be freed when no longer
+ * needed. */
+#define ERR_FLAG_MALLOCED 1
+/* ERR_FLAG_STRING means that the |data| member is a NUL-terminated string that
+ * can be printed. */
+#define ERR_FLAG_STRING 2
+
+/* ERR_FLAG_PUBLIC_MASK is applied to the flags field before it is returned
+ * from functions like |ERR_get_error_line_data|. */
+#define ERR_FLAG_PUBLIC_MASK 0xf
+
+/* The following flag values are internal and are masked when flags are
+ * returned from functions like |ERR_get_error_line_data|. */
+
+/* ERR_FLAG_MARK is used to indicate a reversion point in the queue. See
+ * |ERR_pop_to_mark|. */
+#define ERR_FLAG_MARK 16
+
+/* ERR_NUM_ERRORS is the limit of the number of errors in the queue. */
+#define ERR_NUM_ERRORS 16
+
+/* ERR_STATE contains the per-thread, error queue. */
+typedef struct err_state_st {
+  /* tid is the identifier of the thread that owns this queue. */
+  CRYPTO_THREADID tid;
+
+  /* errors contains the ERR_NUM_ERRORS most recent errors, organised as a ring
+   * buffer. */
+  struct err_error_st errors[ERR_NUM_ERRORS];
+  /* top contains the index one past the most recent error. If |top| equals
+   * |bottom| then the queue is empty. */
+  unsigned top;
+  /* bottom contains the index of the last error in the queue. */
+  unsigned bottom;
+} ERR_STATE;
+
+enum {
+  ERR_LIB_NONE = 1,
+  ERR_LIB_SYS,
+  ERR_LIB_BN,
+  ERR_LIB_RSA,
+  ERR_LIB_DH,
+  ERR_LIB_EVP,
+  ERR_LIB_BUF,
+  ERR_LIB_OBJ,
+  ERR_LIB_PEM,
+  ERR_LIB_DSA,
+  ERR_LIB_X509,
+  ERR_LIB_ASN1,
+  ERR_LIB_CONF,
+  ERR_LIB_CRYPTO,
+  ERR_LIB_EC,
+  ERR_LIB_SSL,
+  ERR_LIB_BIO,
+  ERR_LIB_PKCS7,
+  ERR_LIB_PKCS8,
+  ERR_LIB_X509V3,
+  ERR_LIB_PKCS12,
+  ERR_LIB_RAND,
+  ERR_LIB_ENGINE,
+  ERR_LIB_OCSP,
+  ERR_LIB_UI,
+  ERR_LIB_COMP,
+  ERR_LIB_ECDSA,
+  ERR_LIB_ECDH,
+  ERR_LIB_HMAC,
+  ERR_LIB_DIGEST,
+  ERR_LIB_CIPHER,
+  ERR_LIB_USER,
+  ERR_NUM_LIBS
+};
+
+#define ERR_R_SYS_LIB ERR_LIB_SYS
+#define ERR_R_BN_LIB ERR_LIB_BN
+#define ERR_R_RSA_LIB ERR_LIB_RSA
+#define ERR_R_DH_LIB ERR_LIB_DH
+#define ERR_R_EVP_LIB ERR_LIB_EVP
+#define ERR_R_BUF_LIB ERR_LIB_BUF
+#define ERR_R_OBJ_LIB ERR_LIB_OBJ
+#define ERR_R_PEM_LIB ERR_LIB_PEM
+#define ERR_R_DSA_LIB ERR_LIB_DSA
+#define ERR_R_X509_LIB ERR_LIB_X509
+#define ERR_R_ASN1_LIB ERR_LIB_ASN1
+#define ERR_R_CONF_LIB ERR_LIB_CONF
+#define ERR_R_CRYPTO_LIB ERR_LIB_CRYPTO
+#define ERR_R_EC_LIB ERR_LIB_EC
+#define ERR_R_SSL_LIB ERR_LIB_SSL
+#define ERR_R_BIO_LIB ERR_LIB_BIO
+#define ERR_R_PKCS7_LIB ERR_LIB_PKCS7
+#define ERR_R_PKCS8_LIB ERR_LIB_PKCS8
+#define ERR_R_X509V3_LIB ERR_LIB_X509V3
+#define ERR_R_PKCS12_LIB ERR_LIB_PKCS12
+#define ERR_R_RAND_LIB ERR_LIB_RAND
+#define ERR_R_DSO_LIB ERR_LIB_DSO
+#define ERR_R_ENGINE_LIB ERR_LIB_ENGINE
+#define ERR_R_OCSP_LIB ERR_LIB_OCSP
+#define ERR_R_UI_LIB ERR_LIB_UI
+#define ERR_R_COMP_LIB ERR_LIB_COMP
+#define ERR_R_ECDSA_LIB ERR_LIB_ECDSA
+#define ERR_R_ECDH_LIB ERR_LIB_ECDH
+#define ERR_R_STORE_LIB ERR_LIB_STORE
+#define ERR_R_FIPS_LIB ERR_LIB_FIPS
+#define ERR_R_CMS_LIB ERR_LIB_CMS
+#define ERR_R_TS_LIB ERR_LIB_TS
+#define ERR_R_HMAC_LIB ERR_LIB_HMAC
+#define ERR_R_JPAKE_LIB ERR_LIB_JPAKE
+#define ERR_R_USER_LIB ERR_LIB_USER
+#define ERR_R_DIGEST_LIB ERR_LIB_DIGEST
+#define ERR_R_CIPHER_LIB ERR_LIB_CIPHER
+
+/* Global reasons. */
+#define ERR_R_FATAL 64
+#define ERR_R_MALLOC_FAILURE (1 | ERR_R_FATAL)
+#define ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED (2 | ERR_R_FATAL)
+#define ERR_R_PASSED_NULL_PARAMETER (3 | ERR_R_FATAL)
+#define ERR_R_INTERNAL_ERROR (4 | ERR_R_FATAL)
+
+/* System error functions */
+#define SYS_F_fopen 100
+#define SYS_F_fclose 101
+#define SYS_F_fread 102
+#define SYS_F_fwrite 103
+#define SYS_F_socket 104
+#define SYS_F_setsockopt 105
+#define SYS_F_connect 106
+#define SYS_F_getaddrinfo 107
+
+#define ERR_PACK(lib, func, reason)                                        \
+  (((((uint32_t)lib) & 0xff) << 24) | ((((uint32_t)func) & 0xfff) << 12) | \
+   ((((uint32_t)reason) & 0xfff)))
+
+#define ERR_GET_LIB(packed_error) (((packed_error) >> 24) & 0xff)
+#define ERR_GET_FUNC(packed_error) (((packed_error) >> 12) & 0xfff)
+#define ERR_GET_REASON(packed_error) ((packed_error) & 0xfff)
+
+/* ERR_STRING_DATA is the type of an lhash node that contains a mapping from a
+ * library, function or reason code to a string representation of it. */
+typedef struct err_string_data_st {
+  uint32_t error;
+  const char *string;
+} ERR_STRING_DATA;
+
+/* ERR_load_strings loads an array of ERR_STRING_DATA into the hash table. The
+ * array must be terminated by an entry with a NULL string. */
+void ERR_load_strings(const ERR_STRING_DATA *str);
+
+/* ERR_FNS_st is a structure of function pointers that contains the actual
+ * implementation of the error queue handling functions. */
+struct ERR_FNS_st {
+  void (*shutdown)(void);
+  ERR_STRING_DATA *(*get_item)(uint32_t packed_error);
+  ERR_STRING_DATA *(*set_item)(const ERR_STRING_DATA *);
+  ERR_STRING_DATA *(*del_item)(uint32_t packed_error);
+
+  /* get_state returns the ERR_STATE for the current thread. This function
+   * never returns NULL. */
+  ERR_STATE *(*get_state)(void);
+};
+
+/* OPENSSL_DECLARE_ERROR_REASON is used by util/make_errors.h (which generates
+ * the error defines) to recognise that an additional reason value is needed.
+ * This is needed when the reason value is used outside of an
+ * |OPENSSL_PUT_ERROR| macro. The resulting define will be
+ * ${lib}_R_${reason}. */
+#define OPENSSL_DECLARE_ERROR_REASON(lib, reason)
+
+/* OPENSSL_DECLARE_ERROR_FUNCTION is used by util/make_errors.h (which
+ * generates the error * defines to recognise that an additional function value
+ * is needed. This is * needed when the function value is used outside of an
+ * |OPENSSL_PUT_ERROR| * macro. The resulting define will be
+ * ${lib}_F_${reason}. */
+#define OPENSSL_DECLARE_ERROR_FUNCTION(lib, function_name)
+
+/* ERR_load_BIO_strings does nothing.
+ *
+ * TODO(fork): remove. libjingle calls this. */
+void ERR_load_BIO_strings();
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_ERR_H */
diff --git a/crypto/err/err_impl.c b/crypto/err/err_impl.c
new file mode 100644
index 0000000..ff2509d
--- /dev/null
+++ b/crypto/err/err_impl.c
@@ -0,0 +1,280 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/err.h>
+
+#include <assert.h>
+
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+
+
+DEFINE_LHASH_OF(ERR_STATE);
+DEFINE_LHASH_OF(ERR_STRING_DATA);
+
+/* state_hash is a map from thread ID to ERR_STATE. It works like thread-local
+ * storage. */
+static LHASH_OF(ERR_STATE) *state_hash = NULL;
+
+/* error_hash maps from a packed error to the string for that library /
+ * function / reason. */
+static LHASH_OF(ERR_STRING_DATA) *error_hash = NULL;
+
+/* err_string_data_hash is an lhash hash function for ERR_STRING_DATA. */
+static uint32_t err_string_data_hash(const ERR_STRING_DATA *a) {
+  return OPENSSL_hash32(&a->error, sizeof(a->error));
+}
+
+/* err_string_data_cmp is an lhash compare function for ERR_STRING_DATA. */
+static int err_string_data_cmp(const ERR_STRING_DATA *a,
+                               const ERR_STRING_DATA *b) {
+  return ((int)a->error) - ((int)b->error);
+}
+
+/* error_hash_get_write_locked returns the hash that maps from packed error to
+ * error string and creates it if need be. The caller must hold a write lock on
+ * LOCK_ERR. */
+static LHASH_OF(ERR_STRING_DATA) * error_hash_get_write_locked(void) {
+  if (!error_hash) {
+    error_hash = lh_ERR_STRING_DATA_new(err_string_data_hash, err_string_data_cmp);
+  }
+
+  return error_hash;
+}
+
+/* err_get_item returns an ERR_STRING_DATA with an |error| member that
+ * equals |packed_error|, or NULL if none can be found. */
+static ERR_STRING_DATA *err_get_item(uint32_t packed_error) {
+  ERR_STRING_DATA *ret = NULL, pattern;
+
+  pattern.error = packed_error;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+  if (error_hash) {
+    ret = lh_ERR_STRING_DATA_retrieve(error_hash, &pattern);
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+
+  return ret;
+}
+
+/* err_set_item adds an ERR_STRING_DATA to the global hash of error strings and
+ * returns the previous entry with the same |err->error| value, if any. */
+static ERR_STRING_DATA *err_set_item(const ERR_STRING_DATA *err) {
+  ERR_STRING_DATA *old_item = NULL;
+  LHASH_OF(ERR_STRING_DATA) *hash;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+  hash = error_hash_get_write_locked();
+  if (hash) {
+    lh_ERR_STRING_DATA_insert(hash, &old_item, (ERR_STRING_DATA*) err);
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+
+  return old_item;
+}
+
+/* err_set_item removes an item from the global hash of error strings for
+ * |packed_error| and returns the removed entry, if any. */
+static ERR_STRING_DATA *err_del_item(uint32_t packed_error) {
+  ERR_STRING_DATA *old_item = NULL, pattern;
+
+  pattern.error = packed_error;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+  if (error_hash) {
+    old_item = lh_ERR_STRING_DATA_delete(error_hash, &pattern);
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+
+  return old_item;
+}
+
+
+/* err_state_hash is an lhash hash function for ERR_STATE. */
+static uint32_t err_state_hash(const ERR_STATE *a) {
+  return CRYPTO_THREADID_hash(&a->tid);
+}
+
+/* err_state_cmp is an lhash compare function for ERR_STATE. */
+static int err_state_cmp(const ERR_STATE *a, const ERR_STATE *b) {
+  return CRYPTO_THREADID_cmp(&a->tid, &b->tid);
+}
+
+
+static ERR_STATE *err_get_state(void) {
+  CRYPTO_THREADID tid;
+  ERR_STATE pattern, *state, *race_state;
+  int insert_result;
+  static ERR_STATE fallback;
+
+  CRYPTO_THREADID_current(&tid);
+  memset(&pattern, 0, sizeof(pattern));
+  CRYPTO_THREADID_cpy(&pattern.tid, &tid);
+
+  CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+  if (state_hash == NULL) {
+    CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+    CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+    if (state_hash == NULL) {
+      state_hash = lh_ERR_STATE_new(err_state_hash, err_state_cmp);
+    }
+    CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+    CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+  }
+
+  state = lh_ERR_STATE_retrieve(state_hash, &pattern);
+  CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+  if (state != NULL) {
+    return state;
+  }
+
+  state = OPENSSL_malloc(sizeof(ERR_STATE));
+  if (state == NULL) {
+    CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+    /* The other error functions don't cope with a failure to get the error
+     * state, so we return a dummy value. */
+    return &fallback;
+  }
+
+  memset(state, 0, sizeof(ERR_STATE));
+  CRYPTO_THREADID_cpy(&state->tid, &tid);
+
+  CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+  insert_result = lh_ERR_STATE_insert(state_hash, &race_state, state);
+  CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+
+  if (!insert_result) {
+    /* Insertion failed because of malloc failure. */
+    OPENSSL_free(state);
+    return &fallback;
+  }
+
+  /* We cannot have raced with another thread to insert an ERR_STATE because no
+   * other thread should be inserting values for this thread. */
+  assert(race_state == NULL);
+
+  return state;
+}
+
+static void err_shutdown(void) {
+  CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+  if (error_hash) {
+    lh_ERR_STRING_DATA_free(error_hash);
+    error_hash = NULL;
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+}
+
+const struct ERR_FNS_st openssl_err_default_impl = {
+  err_shutdown,
+  err_get_item,
+  err_set_item,
+  err_del_item,
+  err_get_state,
+};
diff --git a/crypto/err/err_test.c b/crypto/err/err_test.c
new file mode 100644
index 0000000..adfc9d0
--- /dev/null
+++ b/crypto/err/err_test.c
@@ -0,0 +1,118 @@
+/* Copyright (c) 2014, 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 <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+static int test_overflow() {
+  unsigned i;
+
+  for (i = 0; i < ERR_NUM_ERRORS*2; i++) {
+    ERR_put_error(1, 2, 3, "test", 1);
+  }
+
+  for (i = 0; i < ERR_NUM_ERRORS - 1; i++) {
+    if (ERR_get_error() == 0) {
+      fprintf(stderr, "ERR_get_error failed at %u\n", i);
+      return 0;
+    }
+  }
+
+  if (ERR_get_error() != 0) {
+    fprintf(stderr, "ERR_get_error more than the expected number of values.\n");
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_put_error() {
+  uint32_t packed_error;
+  int line, flags;
+  const char *file;
+  char *data;
+
+  if (ERR_get_error() != 0) {
+    fprintf(stderr, "ERR_get_error returned value before an error was added.\n");
+    return 0;
+  }
+
+  ERR_put_error(1, 2, 3, "test", 4);
+  ERR_add_error_data(1, "testing");
+
+  packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+  if (strcmp(file, "test") != 0 ||
+      line != 4 ||
+      (flags & ERR_FLAG_STRING) == 0 ||
+      (flags & ERR_FLAG_MALLOCED) == 0 ||
+      ERR_GET_LIB(packed_error) != 1 ||
+      ERR_GET_FUNC(packed_error) != 2 ||
+      ERR_GET_REASON(packed_error) != 3 ||
+      strcmp(data, "testing") != 0) {
+    fprintf(stderr, "Bad error data returned.\n");
+    return 0;
+  }
+
+  OPENSSL_free(data);
+
+  return 1;
+}
+
+static int test_clear_error() {
+  if (ERR_get_error() != 0) {
+    fprintf(stderr, "ERR_get_error returned value before an error was added.\n");
+    return 0;
+  }
+
+  ERR_put_error(1, 2, 3, "test", 4);
+  ERR_clear_error();
+
+  if (ERR_get_error() != 0) {
+    fprintf(stderr, "Error remained after clearing.\n");
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_print() {
+  size_t i;
+  char buf[256];
+  uint32_t packed_error;
+
+  ERR_put_error(1, 2, 3, "test", 4);
+  ERR_add_error_data(1, "testing");
+  packed_error = ERR_get_error();
+
+  for (i = 0; i <= sizeof(buf); i++) {
+    ERR_error_string_n(packed_error, buf, i);
+  }
+
+  return 1;
+}
+
+int main() {
+  if (!test_overflow() ||
+      !test_put_error() ||
+      !test_clear_error() ||
+      !test_print()) {
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/evp/CMakeLists.txt b/crypto/evp/CMakeLists.txt
new file mode 100644
index 0000000..c3d8d8a
--- /dev/null
+++ b/crypto/evp/CMakeLists.txt
@@ -0,0 +1,30 @@
+include_directories(. .. ../../include)
+
+add_library(
+	evp
+
+	OBJECT
+
+	asn1.c
+	digestsign.c
+	evp.c
+	evp_ctx.c
+	evp_error.c
+	p_ec.c
+	p_ec_asn1.c
+	p_hmac.c
+	p_hmac_asn1.c
+	p_rsa.c
+	p_rsa_asn1.c
+	pbkdf.c
+	sign.c
+)
+
+
+add_executable(
+	example_sign
+
+	example_sign.c
+)
+
+target_link_libraries(example_sign crypto)
diff --git a/crypto/evp/asn1.c b/crypto/evp/asn1.c
new file mode 100644
index 0000000..50bdb06
--- /dev/null
+++ b/crypto/evp/asn1.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **out, const uint8_t **inp,
+                         long len) {
+  EVP_PKEY *ret;
+
+  if (out == NULL || *out == NULL) {
+    ret = EVP_PKEY_new();
+    if (ret == NULL) {
+      OPENSSL_PUT_ERROR(EVP, d2i_PrivateKey, ERR_R_EVP_LIB);
+      return NULL;
+    }
+  } else {
+    ret = *out;
+  }
+
+  if (!EVP_PKEY_set_type(ret, type)) {
+    OPENSSL_PUT_ERROR(EVP, d2i_PrivateKey, EVP_R_UNKNOWN_PUBLIC_KEY_TYPE);
+    goto err;
+  }
+
+  if (!ret->ameth->old_priv_decode ||
+      !ret->ameth->old_priv_decode(ret, inp, len)) {
+    if (ret->ameth->priv_decode) {
+      PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, inp, len);
+      if (!p8) {
+        goto err;
+      }
+      EVP_PKEY_free(ret);
+      ret = EVP_PKCS82PKEY(p8);
+      PKCS8_PRIV_KEY_INFO_free(p8);
+    } else {
+      OPENSSL_PUT_ERROR(EVP, d2i_PrivateKey, ERR_R_ASN1_LIB);
+      goto err;
+    }
+  }
+
+  if (out != NULL) {
+    *out = ret;
+  }
+  return ret;
+
+err:
+  if (ret != NULL && (out == NULL || *out != ret)) {
+    EVP_PKEY_free(ret);
+  }
+  return NULL;
+}
+
+EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **out, const uint8_t **inp, long len) {
+  STACK_OF(ASN1_TYPE) *inkey;
+  const uint8_t *p;
+  int keytype;
+  p = *inp;
+
+  /* Dirty trick: read in the ASN1 data into out STACK_OF(ASN1_TYPE):
+   * by analyzing it we can determine the passed structure: this
+   * assumes the input is surrounded by an ASN1 SEQUENCE. */
+  inkey = d2i_ASN1_SEQUENCE_ANY(NULL, &p, len);
+  /* Since we only need to discern "traditional format" RSA and DSA
+   * keys we can just count the elements. */
+  if (sk_ASN1_TYPE_num(inkey) == 6) {
+    keytype = EVP_PKEY_DSA;
+  } else if (sk_ASN1_TYPE_num(inkey) == 4) {
+    keytype = EVP_PKEY_EC;
+  } else if (sk_ASN1_TYPE_num(inkey) == 3) {
+    OPENSSL_PUT_ERROR(EVP, d2i_AutoPrivateKey, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+    return 0;
+
+    /* This seems to be PKCS8, not traditional format */
+    PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, inp, len);
+    EVP_PKEY *ret;
+
+    sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+    if (!p8) {
+      OPENSSL_PUT_ERROR(EVP, d2i_AutoPrivateKey,
+                        EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+      return NULL;
+    }
+    ret = EVP_PKCS82PKEY(p8);
+    PKCS8_PRIV_KEY_INFO_free(p8);
+    if (out) {
+      *out = ret;
+    }
+    return ret;
+  } else {
+    keytype = EVP_PKEY_RSA;
+  }
+
+  sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+  return d2i_PrivateKey(keytype, out, inp, len);
+}
+
+int i2d_PublicKey(EVP_PKEY *key, uint8_t **outp) {
+  switch (key->type) {
+    case EVP_PKEY_RSA:
+      return i2d_RSAPublicKey(key->pkey.rsa, outp);
+    case EVP_PKEY_DSA:
+      return i2d_DSAPublicKey(key->pkey.dsa, outp);
+    case EVP_PKEY_EC:
+      return i2o_ECPublicKey(key->pkey.ec, outp);
+    default:
+      OPENSSL_PUT_ERROR(EVP, i2d_PublicKey, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+      return -1;
+  }
+}
diff --git a/crypto/evp/digestsign.c b/crypto/evp/digestsign.c
new file mode 100644
index 0000000..08968ed
--- /dev/null
+++ b/crypto/evp/digestsign.c
@@ -0,0 +1,212 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006,2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/digest.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+#include "../digest/internal.h"
+
+
+/* md_begin_digset is a callback from the |EVP_MD_CTX| code that is called when
+ * a new digest is begun. */
+static int md_begin_digest(EVP_MD_CTX *ctx) {
+  int r = EVP_PKEY_CTX_ctrl(ctx->pctx, -1, EVP_PKEY_OP_TYPE_SIG,
+                            EVP_PKEY_CTRL_DIGESTINIT, 0, ctx);
+  return r > 0 || r == -2;
+}
+
+static const struct evp_md_pctx_ops md_pctx_ops = {
+  EVP_PKEY_CTX_free,
+  EVP_PKEY_CTX_dup,
+  md_begin_digest,
+};
+
+static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                          const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey,
+                          int is_verify) {
+  if (ctx->pctx == NULL) {
+    ctx->pctx = EVP_PKEY_CTX_new(pkey, e);
+  }
+  if (ctx->pctx == NULL) {
+    return 0;
+  }
+  ctx->pctx_ops = &md_pctx_ops;
+
+  if (type == NULL) {
+    type = EVP_sha1();
+  }
+
+  if (type == NULL) {
+    OPENSSL_PUT_ERROR(EVP, do_sigver_init, EVP_R_NO_DEFAULT_DIGEST);
+    return 0;
+  }
+
+  if (is_verify) {
+    if (ctx->pctx->pmeth->verifyctx_init) {
+      if (ctx->pctx->pmeth->verifyctx_init(ctx->pctx, ctx) <= 0) {
+        return 0;
+      }
+      ctx->pctx->operation = EVP_PKEY_OP_VERIFYCTX;
+    } else if (EVP_PKEY_verify_init(ctx->pctx) <= 0) {
+      return 0;
+    }
+  } else {
+    if (ctx->pctx->pmeth->signctx_init) {
+      if (ctx->pctx->pmeth->signctx_init(ctx->pctx, ctx) <= 0) {
+        return 0;
+      }
+      ctx->pctx->operation = EVP_PKEY_OP_SIGNCTX;
+    } else if (EVP_PKEY_sign_init(ctx->pctx) <= 0) {
+      return 0;
+    }
+  }
+  if (EVP_PKEY_CTX_set_signature_md(ctx->pctx, type) <= 0) {
+    return 0;
+  }
+  if (pctx) {
+    *pctx = ctx->pctx;
+  }
+  if (!EVP_DigestInit_ex(ctx, type, e)) {
+    return 0;
+  }
+  return 1;
+}
+
+int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type,
+                       ENGINE *e, EVP_PKEY *pkey) {
+  return do_sigver_init(ctx, pctx, type, e, pkey, 0);
+}
+
+int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                         const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey) {
+  return do_sigver_init(ctx, pctx, type, e, pkey, 1);
+}
+
+int EVP_DigestSignUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_DigestVerifyUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_DigestSignFinal(EVP_MD_CTX *ctx, uint8_t *out_sig,
+                        size_t *out_sig_len) {
+  int r = 0;
+  const int has_signctx = ctx->pctx->pmeth->signctx != NULL;
+
+  if (out_sig) {
+    EVP_MD_CTX tmp_ctx;
+    uint8_t md[EVP_MAX_MD_SIZE];
+    unsigned int mdlen;
+
+    EVP_MD_CTX_init(&tmp_ctx);
+    if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx)) {
+      return 0;
+    }
+    if (has_signctx) {
+      r = tmp_ctx.pctx->pmeth->signctx(tmp_ctx.pctx, out_sig, out_sig_len, &tmp_ctx);
+    } else {
+      r = EVP_DigestFinal_ex(&tmp_ctx, md, &mdlen);
+    }
+    EVP_MD_CTX_cleanup(&tmp_ctx);
+    if (has_signctx || !r) {
+      return r;
+    }
+    if (EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, md, mdlen) <= 0) {
+      return 0;
+    }
+  } else {
+    if (has_signctx) {
+      if (ctx->pctx->pmeth->signctx(ctx->pctx, out_sig, out_sig_len, ctx) <= 0) {
+        return 0;
+      }
+    } else {
+      size_t s = EVP_MD_size(ctx->digest);
+      if (EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, NULL, s) <= 0) {
+        return 0;
+      }
+    }
+  }
+  return 1;
+}
+
+int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
+                          size_t sig_len) {
+  EVP_MD_CTX tmp_ctx;
+  uint8_t md[EVP_MAX_MD_SIZE];
+  int r;
+  unsigned int mdlen;
+  const int has_verifyctx = ctx->pctx->pmeth->verifyctx != NULL;
+
+  EVP_MD_CTX_init(&tmp_ctx);
+  if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx)) {
+    return -1;
+  }
+  if (has_verifyctx) {
+    r = tmp_ctx.pctx->pmeth->verifyctx(tmp_ctx.pctx, sig, sig_len, &tmp_ctx);
+  } else {
+    r = EVP_DigestFinal_ex(&tmp_ctx, md, &mdlen);
+  }
+
+  EVP_MD_CTX_cleanup(&tmp_ctx);
+  if (has_verifyctx || !r) {
+    return r;
+  }
+  return EVP_PKEY_verify(ctx->pctx, sig, sig_len, md, mdlen);
+}
diff --git a/crypto/evp/evp.c b/crypto/evp/evp.c
new file mode 100644
index 0000000..1d6fd8c
--- /dev/null
+++ b/crypto/evp/evp.c
@@ -0,0 +1,425 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <assert.h>
+
+#include <openssl/bio.h>
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+
+#include "internal.h"
+
+
+extern const EVP_PKEY_ASN1_METHOD ec_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD hmac_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meth;
+
+EVP_PKEY *EVP_PKEY_new() {
+  EVP_PKEY *ret;
+
+  ret = OPENSSL_malloc(sizeof(EVP_PKEY));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(ret, 0, sizeof(EVP_PKEY));
+  ret->type = EVP_PKEY_NONE;
+  ret->references = 1;
+
+  return ret;
+}
+
+static void free_it(EVP_PKEY *pkey) {
+  if (pkey->ameth && pkey->ameth->pkey_free) {
+    pkey->ameth->pkey_free(pkey);
+    pkey->pkey.ptr = NULL;
+    pkey->type = EVP_PKEY_NONE;
+  }
+}
+
+void EVP_PKEY_free(EVP_PKEY *pkey) {
+  if (pkey == NULL) {
+    return;
+  }
+
+  if (CRYPTO_add(&pkey->references, -1, CRYPTO_LOCK_EVP_PKEY)) {
+    return;
+  }
+
+  free_it(pkey);
+  if (pkey->attributes) {
+    /* TODO(fork): layering: X509_ATTRIBUTE_free is an X.509 function. In
+     * practice this path isn't called but should be removed in the future. */
+    /*sk_X509_ATTRIBUTE_pop_free(pkey->attributes, X509_ATTRIBUTE_free);*/
+    assert(0);
+  }
+  OPENSSL_free(pkey);
+}
+
+int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+  if (a->type != b->type) {
+    return -1;
+  }
+
+  if (a->ameth) {
+    int ret;
+    /* Compare parameters if the algorithm has them */
+    if (a->ameth->param_cmp) {
+      ret = a->ameth->param_cmp(a, b);
+      if (ret <= 0)
+        return ret;
+    }
+
+    if (a->ameth->pub_cmp) {
+      return a->ameth->pub_cmp(a, b);
+    }
+  }
+
+  return -2;
+}
+
+EVP_PKEY *EVP_PKEY_dup(EVP_PKEY *pkey) {
+  CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+  return pkey;
+}
+
+int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) {
+  if (to->type != from->type) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_copy_parameters, EVP_R_DIFFERENT_KEY_TYPES);
+    goto err;
+  }
+
+  if (EVP_PKEY_missing_parameters(from)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_copy_parameters, EVP_R_MISSING_PARAMETERS);
+    goto err;
+  }
+
+  if (from->ameth && from->ameth->param_copy) {
+    return from->ameth->param_copy(to, from);
+  }
+
+err:
+  return 0;
+}
+
+int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey) {
+  if (pkey->ameth && pkey->ameth->param_missing) {
+    return pkey->ameth->param_missing(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_size(const EVP_PKEY *pkey) {
+  if (pkey && pkey->ameth && pkey->ameth->pkey_size) {
+    return pkey->ameth->pkey_size(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_bits(EVP_PKEY *pkey) {
+  if (pkey && pkey->ameth && pkey->ameth->pkey_bits) {
+    return pkey->ameth->pkey_bits(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_id(const EVP_PKEY *pkey) {
+  return pkey->type;
+}
+
+/* TODO(fork): remove the first argument. */
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find(ENGINE **pengine, int nid) {
+  switch (nid) {
+    case EVP_PKEY_RSA:
+    case EVP_PKEY_RSA2:
+      return &rsa_asn1_meth;
+    case EVP_PKEY_HMAC:
+      return &hmac_asn1_meth;
+    case EVP_PKEY_EC:
+      return &ec_asn1_meth;
+    default:
+      return NULL;
+  }
+}
+
+int EVP_PKEY_type(int nid) {
+  const EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_find(NULL, nid);
+  if (meth == NULL) {
+    return NID_undef;
+  }
+  return meth->pkey_id;
+}
+
+EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const uint8_t *mac_key,
+                               size_t mac_key_len) {
+  EVP_PKEY_CTX *mac_ctx = NULL;
+  EVP_PKEY *ret = NULL;
+
+  mac_ctx = EVP_PKEY_CTX_new_id(type, e);
+  if (!mac_ctx) {
+    return NULL;
+  }
+
+  if (EVP_PKEY_keygen_init(mac_ctx) <= 0 ||
+      EVP_PKEY_CTX_ctrl(mac_ctx, -1, EVP_PKEY_OP_KEYGEN,
+                        EVP_PKEY_CTRL_SET_MAC_KEY, mac_key_len,
+                        (uint8_t *)mac_key) <= 0 ||
+      EVP_PKEY_keygen(mac_ctx, &ret) <= 0) {
+    ret = NULL;
+    goto merr;
+  }
+
+merr:
+  if (mac_ctx)
+    EVP_PKEY_CTX_free(mac_ctx);
+  return ret;
+}
+
+int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key) {
+  if (EVP_PKEY_assign_RSA(pkey, key)) {
+    RSA_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_RSA, key);
+}
+
+RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_RSA) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_RSA, EVP_R_EXPECTING_AN_RSA_KEY);
+    return NULL;
+  }
+  RSA_up_ref(pkey->pkey.rsa);
+  return pkey->pkey.rsa;
+}
+
+int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key) {
+  if (EVP_PKEY_assign_DSA(pkey, key)) {
+    DSA_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_DSA(EVP_PKEY *pkey, DSA *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_DSA, key);
+}
+
+DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_DSA) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_DSA, EVP_R_EXPECTING_A_DSA_KEY);
+    return NULL;
+  }
+  DSA_up_ref(pkey->pkey.dsa);
+  return pkey->pkey.dsa;
+}
+
+int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key) {
+  if (EVP_PKEY_assign_EC_KEY(pkey, key)) {
+    EC_KEY_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_EC_KEY(EVP_PKEY *pkey, EC_KEY *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_EC, key);
+}
+
+EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_EC) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_EC_KEY, EVP_R_EXPECTING_AN_EC_KEY_KEY);
+    return NULL;
+  }
+  EC_KEY_up_ref(pkey->pkey.ec);
+  return pkey->pkey.ec;
+}
+
+int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key) {
+  if (EVP_PKEY_assign_DH(pkey, key)) {
+    DH_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_DH(EVP_PKEY *pkey, DH *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_EC, key);
+}
+
+DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_DH) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_DH, EVP_R_EXPECTING_A_DH_KEY);
+    return NULL;
+  }
+  DH_up_ref(pkey->pkey.dh);
+  return pkey->pkey.dh;
+}
+
+int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key) {
+  if (!EVP_PKEY_set_type(pkey, type)) {
+    return 0;
+  }
+  pkey->pkey.ptr = key;
+  return key != NULL;
+}
+
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str(ENGINE **pengine,
+                                                   const char *name,
+                                                   size_t len) {
+  if (len == 3 && memcmp(name, "RSA", 3) == 0) {
+    return &rsa_asn1_meth;
+  } else if (len == 4 && memcmp(name, "HMAC", 4) == 0) {
+    return &hmac_asn1_meth;
+  } if (len == 2 && memcmp(name, "EC", 4) == 0) {
+    return &ec_asn1_meth;
+  }
+  return NULL;
+}
+
+int EVP_PKEY_set_type(EVP_PKEY *pkey, int type) {
+  const EVP_PKEY_ASN1_METHOD *ameth;
+
+  if (pkey && pkey->pkey.ptr) {
+    free_it(pkey);
+  }
+
+  ameth = EVP_PKEY_asn1_find(NULL, type);
+  if (ameth == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_set_type, EVP_R_UNSUPPORTED_ALGORITHM);
+    ERR_add_error_dataf("algorithm %d (%s)", type, OBJ_nid2sn(type));
+    return 0;
+  }
+
+  if (pkey) {
+    pkey->ameth = ameth;
+    pkey->type = pkey->ameth->pkey_id;
+  }
+
+  return 1;
+}
+
+
+
+int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
+  if (a->type != b->type) {
+    return -1;
+  }
+  if (a->ameth && a->ameth->param_cmp) {
+    return a->ameth->param_cmp(a, b);
+  }
+  return -2;
+}
+
+static int print_unsupported(BIO *out, const EVP_PKEY *pkey, int indent,
+                             const char *kstr) {
+  BIO_indent(out, indent, 128);
+  BIO_printf(out, "%s algorithm \"%s\" unsupported\n", kstr,
+             OBJ_nid2ln(pkey->type));
+  return 1;
+}
+
+int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx) {
+  if (pkey->ameth && pkey->ameth->pub_print) {
+    return pkey->ameth->pub_print(out, pkey, indent, pctx);
+  }
+
+  return print_unsupported(out, pkey, indent, "Public Key");
+}
+
+int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *pctx) {
+  if (pkey->ameth && pkey->ameth->priv_print) {
+    return pkey->ameth->priv_print(out, pkey, indent, pctx);
+  }
+
+  return print_unsupported(out, pkey, indent, "Private Key");
+}
+
+int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx) {
+  if (pkey->ameth && pkey->ameth->param_print) {
+    return pkey->ameth->param_print(out, pkey, indent, pctx);
+  }
+
+  return print_unsupported(out, pkey, indent, "Parameters");
+}
+
+int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+  return EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_MD, 0,
+                           (void *)md);
+}
+
+int EVP_PKEY_CTX_get_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) {
+  return EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_GET_MD,
+                           0, (void *)out_md);
+}
+
+void OpenSSL_add_all_algorithms() {}
+
+void EVP_cleanup() {}
diff --git a/crypto/evp/evp.h b/crypto/evp/evp.h
new file mode 100644
index 0000000..ce40aa5
--- /dev/null
+++ b/crypto/evp/evp.h
@@ -0,0 +1,819 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_EVP_H
+#define OPENSSL_HEADER_EVP_H
+
+#include <openssl/base.h>
+#include <openssl/stack.h>
+
+/* OpenSSL included digest and cipher functions in this header so we include
+ * them for users that still expect that. */
+#include <openssl/cipher.h>
+#include <openssl/digest.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/thread.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* EVP abstracts over public/private key algorithms. */
+
+
+/* Public key objects. */
+
+/* EVP_PKEY_new creates a new, empty public-key object and returns it or NULL
+ * on allocation failure. */
+EVP_PKEY *EVP_PKEY_new();
+
+/* EVP_PKEY_free frees all data referenced by |pkey| and then frees |pkey|
+ * itself. */
+void EVP_PKEY_free(EVP_PKEY *pkey);
+
+/* EVP_PKEY_cmp compares |a| and |b| and returns one if they are equal, zero if
+ * not and a negative number on error.
+ *
+ * WARNING: this differs from the traditional return value of a "cmp"
+ * function. */
+int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b);
+
+/* EVP_PKEY_dup adds one to the reference count of |pkey| and returns
+ * |pkey|. */
+EVP_PKEY *EVP_PKEY_dup(EVP_PKEY *pkey);
+
+/* EVP_PKEY_copy_parameters sets the parameters of |to| to equal the parameters
+ * of |from|. It returns one on success and zero on error. */
+int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from);
+
+/* EVP_PKEY_missing_parameters returns one if |pkey| is missing needed
+ * parameters or zero if not, or if the algorithm doesn't take parameters. */
+int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey);
+
+/* EVP_PKEY_size returns the "size", in bytes, of |pkey|. For example, for an
+ * RSA key this returns the number of bytes needed to represent the modulus. */
+int EVP_PKEY_size(const EVP_PKEY *pkey);
+
+/* EVP_PKEY_bits returns the "size", in bits, of |pkey|. For example, for an
+ * RSA key, this returns the bit length of the modulus. */
+int EVP_PKEY_bits(EVP_PKEY *pkey);
+
+/* EVP_PKEY_id returns the type of |pkey|, which is one of the |EVP_PKEY_*|
+ * values. */
+int EVP_PKEY_id(const EVP_PKEY *pkey);
+
+/* EVP_PKEY_type returns a canonicalised form of |NID|. For example,
+ * |EVP_PKEY_RSA2| will be turned into |EVP_PKEY_RSA|. */
+int EVP_PKEY_type(int nid);
+
+/* EVP_PKEY_new_mac_key allocates a fresh |EVP_PKEY| of the given type (e.g.
+ * |EVP_PKEY_HMAC|), sets |mac_key| as the MAC key and "generates" a new key,
+ * suitable for signing. It returns the fresh |EVP_PKEY|, or NULL on error. */
+EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *engine, const uint8_t *mac_key,
+                               size_t mac_key_len);
+
+
+/* Getting and setting concrete public key types.
+ *
+ * The following functions get and set the underlying public key in an
+ * |EVP_PKEY| object. The |set1| functions take a additional reference to the
+ * underlying key and return one on success or zero on error. The |assign|
+ * functions adopt the caller's reference. The getters return a fresh reference
+ * to the underlying object. */
+
+int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key);
+int EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *key);
+RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey);
+
+int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, struct dsa_st *key);
+int EVP_PKEY_assign_DSA(EVP_PKEY *pkey, DSA *key);
+struct dsa_st *EVP_PKEY_get1_DSA(EVP_PKEY *pkey);
+
+int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, struct ec_key_st *key);
+int EVP_PKEY_assign_EC_KEY(EVP_PKEY *pkey, EC_KEY *key);
+struct ec_key_st *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey);
+
+int EVP_PKEY_set1_DH(EVP_PKEY *pkey, struct dh_st *key);
+int EVP_PKEY_assign_DH(EVP_PKEY *pkey, DH *key);
+struct dh_st *EVP_PKEY_get1_DH(EVP_PKEY *pkey);
+
+#define EVP_PKEY_NONE NID_undef
+#define EVP_PKEY_RSA NID_rsaEncryption
+#define EVP_PKEY_RSA2 NID_rsa
+#define EVP_PKEY_DSA NID_dsa
+#define EVP_PKEY_DH NID_dhKeyAgreement
+#define EVP_PKEY_DHX NID_dhpublicnumber
+#define EVP_PKEY_EC NID_X9_62_id_ecPublicKey
+#define EVP_PKEY_HMAC NID_hmac
+
+/* EVP_PKEY_assign sets the underlying key of |pkey| to |key|, which must be of
+ * the given type. The |type| argument should be one of the |EVP_PKEY_*|
+ * values. */
+int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
+
+/* EVP_PKEY_set_type sets the type of |pkey| to |type|, which should be one of
+ * the |EVP_PKEY_*| values. It returns one if sucessful or zero otherwise. If
+ * |pkey| is NULL, it simply reports whether the type is known. */
+int EVP_PKEY_set_type(EVP_PKEY *pkey, int type);
+
+/* EVP_PKEY_cmp_parameters compares the parameters of |a| and |b|. It returns
+ * one if they match, zero if not, or a negative number of on error.
+ *
+ * WARNING: the return value differs from the usual return value convention. */
+int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b);
+
+
+/* ASN.1 functions */
+
+/* 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
+ * |*out|. If |*out| is already non-NULL on entry then the result is written
+ * directly into |*out|, otherwise a fresh |EVP_PKEY| is allocated. On
+ * successful exit, |*inp| is advanced past the DER structure. It returns the
+ * result or NULL on error. */
+EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **out, const uint8_t **inp,
+                         long len);
+
+/* d2i_AutoPrivateKey acts the same as |d2i_PrivateKey|, but detects the type
+ * of the private key. */
+EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **out, const uint8_t **inp, long len);
+
+/* i2d_PrivateKey marshals a private key from |key| to an ASN.1, DER
+ * structure. If |outp| is not NULL then the result is written to |*outp| and
+ * |*outp| is advanced just past the output. It returns the number of bytes in
+ * the result, whether written or not, or a negative value on error. */
+int i2d_PrivateKey(const EVP_PKEY *key, uint8_t **outp);
+
+/* i2d_PublicKey marshals a public key from |key| to an ASN.1, DER
+ * structure. If |outp| is not NULL then the result is written to |*outp| and
+ * |*outp| is advanced just past the output. It returns the number of bytes in
+ * the result, whether written or not, or a negative value on error. */
+int i2d_PublicKey(EVP_PKEY *key, uint8_t **outp);
+
+
+/* Signing */
+
+/* EVP_DigestSignInit sets up |ctx| for a signing operation with |type| and
+ * |pkey|. The |ctx| argument must have been initialised with
+ * |EVP_MD_CTX_init|. If |pctx| is not NULL, the |EVP_PKEY_CTX| of the signing
+ * operation will be written to |*pctx|; this can be used to set alternative
+ * signing options.
+ *
+ * It returns one on success, or <= 0 on error. WARNING: this differs from the
+ * usual OpenSSL return convention. */
+int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type,
+                       ENGINE *e, EVP_PKEY *pkey);
+
+/* EVP_DigestSignUpdate appends |len| bytes from |data| to the data which will
+ * be signed in |EVP_DigestSignFinal|. It returns one on success and zero
+ * otherwise. */
+int EVP_DigestSignUpdate(EVP_MD_CTX *ctx, const void *data, size_t len);
+
+/* EVP_DigestSignFinal signs the data that has been included by one or more
+ * calls to |EVP_DigestSignUpdate|. If |out_sig| is NULL then |*out_sig_len| is
+ * set to the maximum number of output bytes. Otherwise, on entry,
+ * |*out_sig_len| must contain the length of the |out_sig| buffer. If the call
+ * is successful, the signature is written to |out_sig| and |*out_sig_len| is
+ * set to its length.
+ *
+ * It returns one on success and <= 0 on error. WARNING: this differs from the
+ * usual, OpenSSL return value convention. */
+int EVP_DigestSignFinal(EVP_MD_CTX *ctx, uint8_t *out_sig, size_t *out_sig_len);
+
+
+/* Verifying */
+
+/* EVP_DigestVerifyInit sets up |ctx| for a signature verification operation
+ * with |type| and |pkey|. The |ctx| argument must have been initialised with
+ * |EVP_MD_CTX_init|. If |pctx| is not NULL, the |EVP_PKEY_CTX| of the signing
+ * operation will be written to |*pctx|; this can be used to set alternative
+ * signing options.
+ *
+ * It returns one on success, or <= 0 on error. WARNING: this differs from the
+ * usual OpenSSL return convention. */
+int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                         const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey);
+
+/* EVP_DigestVerifyUpdate appends |len| bytes from |data| to the data which
+ * will be verified by |EVP_DigestVerifyFinal|. It returns one on success and
+ * zero otherwise. */
+int EVP_DigestVerifyUpdate(EVP_MD_CTX *ctx, const void *data, size_t len);
+
+/* EVP_DigestVerifyFinal verifies that |sig_len| bytes of |sig| are a valid
+ * signature for the data that has been included by one or more calls to
+ * |EVP_DigestVerifyUpdate|.
+ *
+ * It returns one on success and <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
+                          size_t sig_len);
+
+
+/* Signing (old functions) */
+
+/* EVP_SignInit_ex configures |ctx|, which must already have been initialised,
+ * for a fresh signing operation using the hash function |type|. It returns one
+ * on success and zero otherwise.
+ *
+ * (In order to initialise |ctx|, either obtain it initialised with
+ * |EVP_MD_CTX_create|, or use |EVP_MD_CTX_init|.) */
+int EVP_SignInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);
+
+/* EVP_SignInit is a deprecated version of |EVP_SignInit_ex|.
+ *
+ * TODO(fork): remove. */
+int EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type);
+
+/* EVP_SignUpdate appends |len| bytes from |data| to the data which will be
+ * signed in |EVP_SignFinal|. */
+int EVP_SignUpdate(EVP_MD_CTX *ctx, const void *data, size_t len);
+
+/* EVP_SignFinal signs the data that has been included by one or more calls to
+ * |EVP_SignUpdate|, using the key |pkey|, and writes it to |sig|. On entry,
+ * |sig| must point to at least |EVP_PKEY_size(pkey)| bytes of space. The
+ * actual size of the signature is written to |*out_sig_len|.
+ *
+ * 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. */
+int EVP_SignFinal(const EVP_MD_CTX *ctx, uint8_t *sig,
+                  unsigned int *out_sig_len, EVP_PKEY *pkey);
+
+
+/* Verifying (old functions) */
+
+/* EVP_VerifyInit_ex configures |ctx|, which must already have been
+ * initialised, for a fresh signature verification operation using the hash
+ * function |type|. It returns one on success and zero otherwise.
+ *
+ * (In order to initialise |ctx|, either obtain it initialised with
+ * |EVP_MD_CTX_create|, or use |EVP_MD_CTX_init|.) */
+int EVP_VerifyInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);
+
+/* EVP_VerifyInit is a deprecated version of |EVP_VerifyInit_ex|.
+ *
+ * TODO(fork): remove. */
+int EVP_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *type);
+
+/* EVP_VerifyUpdate appends |len| bytes from |data| to the data which will be
+ * signed in |EVP_VerifyFinal|. */
+int EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *data, size_t len);
+
+/* EVP_VerifyFinal verifies that |sig_len| bytes of |sig| are a valid
+ * signature, by |pkey|, for the data that has been included by one or more
+ * calls to |EVP_VerifyUpdate|.
+ *
+ * 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. */
+int EVP_VerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len,
+                    EVP_PKEY *pkey);
+
+
+/* Printing */
+
+/* EVP_PKEY_print_public prints a textual representation of the public key in
+ * |pkey| to |out|. Returns one on success or zero otherwise. */
+int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx);
+
+/* EVP_PKEY_print_public prints a textual representation of the private key in
+ * |pkey| to |out|. Returns one on success or zero otherwise. */
+int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *pctx);
+
+/* EVP_PKEY_print_public prints a textual representation of the parameters in
+ * |pkey| to |out|. Returns one on success or zero otherwise. */
+int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx);
+
+
+/* Password stretching.
+ *
+ * Password stretching functions take a low-entropy password and apply a slow
+ * function that results in a key suitable for use in symmetric
+ * cryptography. */
+
+/* PKCS5_PBKDF2_HMAC computes |iterations| iterations of PBKDF2 of |password|
+ * and |salt|, using |digest|, and outputs |key_len| bytes to |out_key|. It
+ * returns one on success and zero on error. */
+int PKCS5_PBKDF2_HMAC(const char *password, int password_len,
+                      const uint8_t *salt, size_t salt_len, unsigned iterations,
+                      const EVP_MD *digest, size_t key_len, uint8_t *out_key);
+
+/* PKCS5_PBKDF2_HMAC_SHA1 is the same as PKCS5_PBKDF2_HMAC, but with |digest|
+ * fixed to |EVP_sha1|. */
+int PKCS5_PBKDF2_HMAC_SHA1(const char *password, int password_len,
+                           const uint8_t *salt, size_t salt_len,
+                           unsigned iterations, size_t key_len,
+                           uint8_t *out_key);
+
+
+/* Public key contexts.
+ *
+ * |EVP_PKEY_CTX| objects hold the context of an operation (e.g. signing or
+ * encrypting) that uses a public key. */
+
+/* EVP_PKEY_CTX_new allocates a fresh |EVP_PKEY_CTX| for use with |pkey|. It
+ * returns the context or NULL on error. */
+EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
+
+/* EVP_PKEY_CTX_new allocates a fresh |EVP_PKEY_CTX| for a key of type |id|
+ * (e.g. |EVP_PKEY_HMAC|). This can be used for key generation where
+ * |EVP_PKEY_CTX_new| can't be used because there isn't an |EVP_PKEY| to pass
+ * it. It returns the context or NULL on error. */
+EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e);
+
+/* EVP_KEY_CTX_free frees |ctx| and the data it owns. */
+void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_CTX_dup allocates a fresh |EVP_PKEY_CTX| and sets it equal to the
+ * state of |ctx|. It returns the fresh |EVP_PKEY_CTX| or NULL on error. */
+EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_CTX_get0_pkey returns the |EVP_PKEY| associated with |ctx|. */
+EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_CTX_set_app_data sets an opaque pointer on |ctx|. */
+void EVP_PKEY_CTX_set_app_data(EVP_PKEY_CTX *ctx, void *data);
+
+/* EVP_PKEY_CTX_get_app_data returns the opaque pointer from |ctx| that was
+ * previously set with |EVP_PKEY_CTX_set_app_data|, or NULL if none has been
+ * set. */
+void *EVP_PKEY_CTX_get_app_data(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_CTX_ctrl performs |cmd| on |ctx|. The |keytype| and |optype|
+ * arguments can be -1 to specify that any type and operation are acceptable,
+ * otherwise |keytype| must match the type of |ctx| and the bits of |optype|
+ * must intersect the operation flags set on |ctx|.
+ *
+ * The |p1| and |p2| arguments depend on the value of |cmd|.
+ *
+ * It returns -2 if |cmd| is not recognised, -1 on error or a |cmd| specific
+ * value otherwise. */
+int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, int cmd,
+                      int p1, void *p2);
+
+/* EVP_PKEY_sign_init initialises an |EVP_PKEY_CTX| for a signing operation. It
+ * should be called before |EVP_PKEY_sign|.
+ *
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_sign signs |data_len| bytes from |data| using |ctx|. If |sig| is
+ * NULL, the size of the signature is written to |out_sig_len|. Otherwise,
+ * |*sig_len| must contain the number of bytes of space available at |sig|. If
+ * sufficient, the signature will be written to |sig| and |*sig_len| updated
+ * with the true length.
+ *
+ * It returns one on success or zero on error. (Note: this differs from
+ * OpenSSL, which can also return negative values to indicate an error. ) */
+int EVP_PKEY_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *sig_len,
+                  const uint8_t *data, size_t data_len);
+
+/* EVP_PKEY_verify_init initialises an |EVP_PKEY_CTX| for a signature
+ * verification operation. It should be called before |EVP_PKEY_verify|.
+ *
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_verify verifies that |sig_len| bytes from |sig| are a valid signature
+ * for |data|.
+ *
+ * It returns one on success or zero on error. (Note: this differs from
+ * OpenSSL, which can also return negative values to indicate an error. ) */
+int EVP_PKEY_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig, size_t sig_len,
+                    const uint8_t *data, size_t data_len);
+
+/* EVP_PKEY_encrypt_init initialises an |EVP_PKEY_CTX| for an encryption
+ * operation. It should be called before |EVP_PKEY_encrypt|.
+ *
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_encrypt encrypts |in_len| bytes from |in| and writes it to |out|.
+ * TODO(fork): need more details on |out_len|.
+ *
+ * It returns one on success or <= 0 on error. (Note: this differs from
+ * OpenSSL, which can also return negative values to indicate an error. ) */
+int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len,
+                     const uint8_t *in, size_t in_len);
+
+/* EVP_PKEY_decrypt_init initialises an |EVP_PKEY_CTX| for a decryption
+ * operation. It should be called before |EVP_PKEY_decrypt|.
+ *
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_decrypt decrypts |in_len| bytes from |in|, writes it to |out| and
+ * sets |*outlen| to the number of bytes written.
+ *
+ * It returns one on success or <= 0 on error. (Note: this differs from
+ * OpenSSL, which can also return negative values to indicate an error. ) */
+int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len,
+                     const uint8_t *in, size_t in_len);
+
+/* EVP_PKEY_derive_init initialises an |EVP_PKEY_CTX| for a key derivation
+ * operation. It should be called before |EVP_PKEY_derive_set_peer| and
+ * |EVP_PKEY_derive|.
+ *
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_derive_set_peer sets the peer's key to be used for key derivation
+ * by |ctx| to |peer|. It should be called after |EVP_PKEY_derive_init|. (For
+ * example, this is used to set the peer's key in (EC)DH.) It returns one on
+ * success and <= 0 on error. WARNING: this differs from the usual return value
+ * convention. */
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
+
+/* EVP_PKEY_derive derives a shared key between the two keys configured in
+ * |ctx|. If |key| is non-NULL then, on entry, |out_key_len| must contain the
+ * amount of space at |key|. If sufficient then the shared key will be written
+ * to |key| and |*out_key_len| will be set to the length. If |key| is NULL then
+ * |out_key_len| will be set the length.
+ *
+ * It returns one on success and <= 0 on error. WARNING: this differs from the
+ * usual return convention. */
+int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, uint8_t *key, size_t *out_key_len);
+
+/* EVP_PKEY_keygen_init initialises an |EVP_PKEY_CTX| for a key generation
+ * operation. It should be called before |EVP_PKEY_keygen|.
+ *
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * usual return value convention. */
+int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx);
+
+/* EVP_PKEY_keygen performs a key generation operation using the values from
+ * |ctx| and sets |*ppkey| to a fresh |EVP_PKEY| containing the resulting key.
+ * It returns one on success or <= 0 on error. WARNING: this differs from the
+ * normal return value convention. */
+int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
+
+
+/* EVP_PKEY_CTX_ctrl operations.
+ *
+ * These values are passed as the |cmd| argument to
+ * EVP_PKEY_CTX_ctrl */
+
+/* Generic. */
+
+/* EVP_PKEY_CTX_set_signature_md sets |md| as the digest to be used in a
+ * signature operation. It returns one on success or otherwise on error. See
+ * the return values of |EVP_PKEY_CTX_ctrl| for details. */
+int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+
+/* EVP_PKEY_CTX_get_signature_md sets |*out_md| to the digest to be used in a
+ * signature operation. It returns one on success or otherwise on error. See
+ * the return values of |EVP_PKEY_CTX_ctrl| for details. */
+int EVP_PKEY_CTX_get_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md);
+
+/* EVP_PKEY_CTRL_DIGESTINIT is an internal value. It's called by
+ * EVP_DigestInit_ex to signal the |EVP_PKEY| that a digest operation is
+ * starting. */
+#define EVP_PKEY_CTRL_DIGESTINIT 3
+
+/* EVP_PKEY_CTRL_PEER_KEY is called with different values of |p1|:
+ *   0: Is called from |EVP_PKEY_derive_set_peer| and |p2| contains a peer key.
+ *      If the return value is <= 0, the key is rejected.
+ *   1: Is called at the end of |EVP_PKEY_derive_set_peer| and |p2| contains a
+ *      peer key. If the return value is <= 0, the key is rejected.
+ *   2: Is called with |p2| == NULL to test whether the peer's key was used.
+ *      (EC)DH always return one in this case.
+ *   3: Is called with |p2| == NULL to set whether the peer's key was used.
+ *      (EC)DH always return one in this case. This was only used for GOST. */
+#define EVP_PKEY_CTRL_PEER_KEY 4
+
+/* EVP_PKEY_CTRL_SET_MAC_KEY sets a MAC key. For example, this can be done an
+ * |EVP_PKEY_CTX| prior to calling |EVP_PKEY_keygen| in order to generate an
+ * HMAC |EVP_PKEY| with the given key. It returns one on success and zero on
+ * error. */
+#define EVP_PKEY_CTRL_SET_MAC_KEY 5
+
+/* EVP_PKEY_ALG_CTRL is the base value from which key-type specific ctrl
+ * commands are numbered. */
+#define EVP_PKEY_ALG_CTRL 0x1000
+
+
+/* RSA specific control functions. */
+
+/* EVP_PKEY_CTX_set_rsa_padding sets the padding type to use. It should be one
+ * of the |RSA_*_PADDING| values. Returns one on success or another value on
+ * error. See |EVP_PKEY_CTX_ctrl| for the other return values, which are
+ * non-standard. */
+int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int padding);
+
+/* EVP_PKEY_CTX_get_rsa_padding sets |*out_padding| to the current padding
+ * value, which is one of the |RSA_*_PADDING| values. Returns one on success or
+ * another value on error. See |EVP_PKEY_CTX_ctrl| for the other return values,
+ * which are non-standard. */
+int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *out_padding);
+
+/* EVP_PKEY_CTX_set_rsa_pss_saltlen sets the length of the salt in a PSS-padded
+ * signature. A value of -1 cause the salt to be the same length as the digest
+ * in the signature. A value of -2 causes the salt to be the maximum length
+ * that will fit. Otherwise the value gives the size of the salt in bytes.
+ *
+ * Returns one on success or another value on error. See |EVP_PKEY_CTX_ctrl|
+ * for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int salt_len);
+
+/* EVP_PKEY_CTX_get_rsa_pss_saltlen sets |*out_salt_len| to the salt length of
+ * a PSS-padded signature. See the documentation for
+ * |EVP_PKEY_CTX_set_rsa_pss_saltlen| for details of the special values that it
+ * can take.
+ *
+ * Returns one on success or another value on error. See |EVP_PKEY_CTX_ctrl|
+ * for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *out_salt_len);
+
+/* EVP_PKEY_CTX_set_rsa_keygen_bits sets the size of the desired RSA modulus,
+ * in bits, for key generation. Returns one on success or another value on
+ * error. See |EVP_PKEY_CTX_ctrl| for the other return values, which are
+ * non-standard. */
+int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits);
+
+/* EVP_PKEY_CTX_set_rsa_keygen_pubexp sets |e| as the public exponent for key
+ * generation. Returns one on success or another value on error. See
+ * |EVP_PKEY_CTX_ctrl| for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *e);
+
+/* EVP_PKEY_CTX_set_rsa_oaep_md sets |md| as the digest used in OAEP padding.
+ * Returns one on success or another value on error. See |EVP_PKEY_CTX_ctrl|
+ * for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+
+/* EVP_PKEY_CTX_get_rsa_oaep_md sets |*out_md| to the digest function used in
+ * OAEP padding. Returns one on success or another value on error. See
+ * |EVP_PKEY_CTX_ctrl| for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_get_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md);
+
+/* EVP_PKEY_CTX_set_rsa_mgf1_md sets |md| as the digest used in MGF1. Returns
+ * one on success or another value on error. See |EVP_PKEY_CTX_ctrl| for the
+ * other return values, which are non-standard. */
+int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+
+/* EVP_PKEY_CTX_get_rsa_mgf1_md sets |*out_md| to the digest function used in
+ * MGF1. Returns one on success or another value on error. See
+ * |EVP_PKEY_CTX_ctrl| for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_get_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md);
+
+/* EVP_PKEY_CTX_set0_rsa_oaep_label sets |label_len| bytes from |label| as the
+ * label used in OAEP. DANGER: this call takes ownership of |label| and will
+ * call |free| on it when |ctx| is destroyed.
+ *
+ * Returns one on success or another value on error. See |EVP_PKEY_CTX_ctrl|
+ * for the other return values, which are non-standard. */
+int EVP_PKEY_CTX_set0_rsa_oaep_label(EVP_PKEY_CTX *ctx, const uint8_t *label,
+                                     size_t label_len);
+
+/* EVP_PKEY_CTX_get0_rsa_oaep_label sets |*out_label| to point to the internal
+ * buffer containing the OAEP label (which may be NULL) and returns the length
+ * of the label or a negative value on error. */
+int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx,
+                                     const uint8_t **out_label);
+
+
+/* EC specific */
+
+#define EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID		(EVP_PKEY_ALG_CTRL + 1)
+#define EVP_PKEY_CTRL_EC_PARAM_ENC			(EVP_PKEY_ALG_CTRL + 2)
+#define EVP_PKEY_CTRL_EC_ECDH_COFACTOR			(EVP_PKEY_ALG_CTRL + 3)
+#define EVP_PKEY_CTRL_EC_KDF_TYPE			(EVP_PKEY_ALG_CTRL + 4)
+#define EVP_PKEY_CTRL_EC_KDF_MD				(EVP_PKEY_ALG_CTRL + 5)
+#define EVP_PKEY_CTRL_GET_EC_KDF_MD			(EVP_PKEY_ALG_CTRL + 6)
+#define EVP_PKEY_CTRL_EC_KDF_OUTLEN			(EVP_PKEY_ALG_CTRL + 7)
+#define EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN			(EVP_PKEY_ALG_CTRL + 8)
+#define EVP_PKEY_CTRL_EC_KDF_UKM			(EVP_PKEY_ALG_CTRL + 9)
+#define EVP_PKEY_CTRL_GET_EC_KDF_UKM			(EVP_PKEY_ALG_CTRL + 10)
+
+#define EVP_PKEY_ECDH_KDF_NONE 1
+#define EVP_PKEY_ECDH_KDF_X9_62 2
+
+
+/* PKEY ctrl commands.
+ *
+ * These values are passed as the |op| argument to
+ * EVP_PKEY_ASN1_METHOD.pkey_ctrl. */
+
+/* ASN1_PKEY_CTRL_DEFAULT_MD_NID expects |arg2| to be an |int*| and sets the
+ * pointed at int to be the NID of the default hash function used in
+ * signing. */
+#define ASN1_PKEY_CTRL_DEFAULT_MD_NID 0x3
+
+
+/* Private functions */
+
+/* OpenSSL_add_all_algorithms does nothing. */
+void OpenSSL_add_all_algorithms();
+
+/* EVP_cleanup does nothing. */
+void EVP_cleanup();
+
+/* EVP_PKEY_asn1_find returns the ASN.1 method table for the given |nid|, which
+ * should be one of the |EVP_PKEY_*| values. It returns NULL if |nid| is
+ * unknown. */
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find(ENGINE **pengine, int nid);
+
+/* TODO(fork): move to PEM? */
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str(ENGINE **pengine,
+                                                   const char *name,
+                                                   size_t len);
+
+struct evp_pkey_st {
+  int references;
+
+  /* type contains one of the EVP_PKEY_* values or NID_undef and determines
+   * which element (if any) of the |pkey| union is valid. */
+  int type;
+
+  /* TODO(fork): document */
+  int save_type;
+
+  union {
+    char *ptr;
+    struct rsa_st *rsa; /* RSA */
+    struct dsa_st *dsa; /* DSA */
+    struct dh_st *dh; /* DH */
+    struct ec_key_st *ec; /* ECC */
+  } pkey;
+
+  ENGINE *engine;
+
+  /* TODO(fork): document */
+  int save_parameters;
+  /* ameth contains a pointer to a method table that contains many ASN.1
+   * methods for the key type. */
+  const EVP_PKEY_ASN1_METHOD *ameth;
+
+  /* TODO(fork): document; */
+  STACK_OF(X509_ATTRIBUTE) * attributes; /* [ 0 ] */
+} /* EVP_PKEY */;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define EVP_F_rsa_item_verify 100
+#define EVP_F_do_sigver_init 101
+#define EVP_F_eckey_priv_decode 102
+#define EVP_F_pkey_ec_sign 103
+#define EVP_F_EVP_PKEY_sign_init 104
+#define EVP_F_d2i_PrivateKey 105
+#define EVP_F_rsa_priv_encode 106
+#define EVP_F_rsa_mgf1_to_md 107
+#define EVP_F_EVP_PKEY_get1_DH 108
+#define EVP_F_EVP_PKEY_sign 109
+#define EVP_F_old_ec_priv_decode 110
+#define EVP_F_EVP_PKEY_get1_RSA 111
+#define EVP_F_pkey_ec_ctrl 112
+#define EVP_F_evp_pkey_ctx_new 113
+#define EVP_F_EVP_PKEY_verify 114
+#define EVP_F_EVP_PKEY_encrypt 115
+#define EVP_F_EVP_PKEY_keygen 116
+#define EVP_F_eckey_type2param 117
+#define EVP_F_eckey_priv_encode 118
+#define EVP_F_do_EC_KEY_print 119
+#define EVP_F_pkey_ec_keygen 120
+#define EVP_F_EVP_PKEY_encrypt_init 121
+#define EVP_F_pkey_rsa_ctrl 122
+#define EVP_F_rsa_priv_decode 123
+#define EVP_F_rsa_pss_to_ctx 124
+#define EVP_F_EVP_PKEY_get1_EC_KEY 125
+#define EVP_F_EVP_PKEY_verify_init 126
+#define EVP_F_EVP_PKEY_derive_init 127
+#define EVP_F_eckey_param2type 128
+#define EVP_F_eckey_pub_decode 129
+#define EVP_F_d2i_AutoPrivateKey 130
+#define EVP_F_eckey_param_decode 131
+#define EVP_F_EVP_PKEY_new 132
+#define EVP_F_pkey_ec_derive 133
+#define EVP_F_pkey_ec_paramgen 134
+#define EVP_F_EVP_PKEY_CTX_ctrl 135
+#define EVP_F_EVP_PKEY_decrypt_init 136
+#define EVP_F_EVP_PKEY_decrypt 137
+#define EVP_F_EVP_PKEY_copy_parameters 138
+#define EVP_F_EVP_PKEY_set_type 139
+#define EVP_F_EVP_PKEY_derive 140
+#define EVP_F_EVP_PKEY_keygen_init 141
+#define EVP_F_do_rsa_print 142
+#define EVP_F_old_rsa_priv_decode 143
+#define EVP_F_rsa_algor_to_md 144
+#define EVP_F_eckey_pub_encode 145
+#define EVP_F_EVP_PKEY_derive_set_peer 146
+#define EVP_F_pkey_rsa_sign 147
+#define EVP_F_check_padding_md 148
+#define EVP_F_i2d_PublicKey 149
+#define EVP_F_rsa_pub_decode 150
+#define EVP_F_EVP_PKEY_get1_DSA 151
+#define EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE 100
+#define EVP_R_UNSUPPORTED_SIGNATURE_TYPE 101
+#define EVP_R_INVALID_DIGEST_TYPE 102
+#define EVP_R_EXPECTING_A_DH_KEY 103
+#define EVP_R_OPERATON_NOT_INITIALIZED 104
+#define EVP_R_MISSING_PARAMETERS 105
+#define EVP_R_NO_DEFAULT_DIGEST 106
+#define EVP_R_UNKNOWN_DIGEST 107
+#define EVP_R_KEYS_NOT_SET 108
+#define EVP_R_X931_UNSUPPORTED 109
+#define EVP_R_DIGEST_DOES_NOT_MATCH 110
+#define EVP_R_DIFFERENT_PARAMETERS 111
+#define EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE 112
+#define EVP_R_DIFFERENT_KEY_TYPES 113
+#define EVP_R_NO_PARAMETERS_SET 114
+#define EVP_R_NO_NID_FOR_CURVE 115
+#define EVP_R_NO_OPERATION_SET 116
+#define EVP_R_UNSUPPORTED_ALGORITHM 117
+#define EVP_R_EXPECTING_AN_DSA_KEY 118
+#define EVP_R_UNKNOWN_MASK_DIGEST 119
+#define EVP_R_INVALID_SALT_LENGTH 120
+#define EVP_R_BUFFER_TOO_SMALL 121
+#define EVP_R_INVALID_PADDING_MODE 122
+#define EVP_R_INVALID_MGF1_MD 123
+#define EVP_R_SHARED_INFO_ERROR 124
+#define EVP_R_INVALID_KEYBITS 125
+#define EVP_R_PEER_KEY_ERROR 126
+#define EVP_R_EXPECTING_A_DSA_KEY 127
+#define EVP_R_UNSUPPORTED_MASK_ALGORITHM 128
+#define EVP_R_EXPECTING_AN_EC_KEY_KEY 129
+#define EVP_R_INVALID_TRAILER 130
+#define EVP_R_INVALID_DIGEST_LENGTH 131
+#define EVP_R_COMMAND_NOT_SUPPORTED 132
+#define EVP_R_EXPLICIT_EC_PARAMETERS_NOT_SUPPORTED 133
+#define EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE 134
+#define EVP_R_NO_MDC2_SUPPORT 135
+#define EVP_R_INVALID_CURVE 136
+#define EVP_R_NO_KEY_SET 137
+#define EVP_R_INVALID_PSS_PARAMETERS 138
+#define EVP_R_KDF_PARAMETER_ERROR 139
+#define EVP_R_UNSUPPORTED_MASK_PARAMETER 140
+#define EVP_R_EXPECTING_AN_RSA_KEY 141
+#define EVP_R_INVALID_OPERATION 142
+#define EVP_R_DECODE_ERROR 143
+#define EVP_R_INVALID_PSS_SALTLEN 144
+#define EVP_R_UNKNOWN_PUBLIC_KEY_TYPE 145
+
+#endif  /* OPENSSL_HEADER_EVP_H */
diff --git a/crypto/evp/evp_ctx.c b/crypto/evp/evp_ctx.c
new file mode 100644
index 0000000..5c3755a
--- /dev/null
+++ b/crypto/evp/evp_ctx.c
@@ -0,0 +1,548 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+extern const EVP_PKEY_METHOD rsa_pkey_meth;
+extern const EVP_PKEY_METHOD hmac_pkey_meth;
+extern const EVP_PKEY_METHOD ec_pkey_meth;
+
+static const EVP_PKEY_METHOD *evp_methods[] = {
+  &rsa_pkey_meth,
+  &hmac_pkey_meth,
+  &ec_pkey_meth,
+};
+
+static const EVP_PKEY_METHOD *evp_pkey_meth_find(int type) {
+  unsigned i;
+
+  for (i = 0; i < sizeof(evp_methods)/sizeof(EVP_PKEY_METHOD*); i++) {
+    if (evp_methods[i]->pkey_id == type) {
+      return evp_methods[i];
+    }
+  }
+
+  return NULL;
+}
+
+static EVP_PKEY_CTX *evp_pkey_ctx_new(EVP_PKEY *pkey, ENGINE *e, int id) {
+  EVP_PKEY_CTX *ret;
+  const EVP_PKEY_METHOD *pmeth;
+
+  if (id == -1) {
+    if (!pkey || !pkey->ameth) {
+      return NULL;
+    }
+    id = pkey->ameth->pkey_id;
+  }
+
+  pmeth = evp_pkey_meth_find(id);
+
+  if (pmeth == NULL) {
+    OPENSSL_PUT_ERROR(EVP, evp_pkey_ctx_new, EVP_R_UNSUPPORTED_ALGORITHM);
+    const char *name = OBJ_nid2sn(id);
+    ERR_add_error_dataf("algorithm %d (%s)", id, name);
+    return NULL;
+  }
+
+  ret = OPENSSL_malloc(sizeof(EVP_PKEY_CTX));
+  if (!ret) {
+    OPENSSL_PUT_ERROR(EVP, evp_pkey_ctx_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  memset(ret, 0, sizeof(EVP_PKEY_CTX));
+
+  ret->engine = e;
+  ret->pmeth = pmeth;
+  ret->operation = EVP_PKEY_OP_UNDEFINED;
+
+  if (pkey) {
+    ret->pkey = pkey;
+    CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+  }
+
+  if (pmeth->init) {
+    if (pmeth->init(ret) <= 0) {
+      EVP_PKEY_CTX_free(ret);
+      return NULL;
+    }
+  }
+
+  return ret;
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e) {
+  return evp_pkey_ctx_new(pkey, e, -1);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e) {
+  return evp_pkey_ctx_new(NULL, e, id);
+}
+
+void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx) {
+  if (ctx == NULL) {
+    return;
+  }
+  if (ctx->pmeth && ctx->pmeth->cleanup) {
+    ctx->pmeth->cleanup(ctx);
+  }
+  if (ctx->pkey) {
+    EVP_PKEY_free(ctx->pkey);
+  }
+  if (ctx->peerkey) {
+    EVP_PKEY_free(ctx->peerkey);
+  }
+  OPENSSL_free(ctx);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *pctx) {
+  EVP_PKEY_CTX *rctx;
+
+  if (!pctx->pmeth || !pctx->pmeth->copy) {
+    return NULL;
+  }
+
+  rctx = OPENSSL_malloc(sizeof(EVP_PKEY_CTX));
+  if (!rctx) {
+    return NULL;
+  }
+
+  memset(rctx, 0, sizeof(EVP_PKEY_CTX));
+
+  rctx->pmeth = pctx->pmeth;
+  rctx->engine = pctx->engine;
+  rctx->operation = pctx->operation;
+
+  if (pctx->pkey) {
+    CRYPTO_add(&pctx->pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+  }
+  rctx->pkey = pctx->pkey;
+
+  if (pctx->peerkey) {
+    CRYPTO_add(&pctx->peerkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+  }
+  rctx->peerkey = pctx->peerkey;
+
+  if (pctx->pmeth->copy(rctx, pctx) > 0) {
+    return rctx;
+  }
+
+  EVP_PKEY_CTX_free(rctx);
+  return NULL;
+}
+
+EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx) { return ctx->pkey; }
+
+void EVP_PKEY_CTX_set_app_data(EVP_PKEY_CTX *ctx, void *data) {
+  ctx->app_data = data;
+}
+
+void *EVP_PKEY_CTX_get_app_data(EVP_PKEY_CTX *ctx) { return ctx->app_data; }
+
+int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, int cmd,
+                      int p1, void *p2) {
+  int ret;
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->ctrl) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_COMMAND_NOT_SUPPORTED);
+    return -2;
+  }
+  if (keytype != -1 && ctx->pmeth->pkey_id != keytype) {
+    return -1;
+  }
+
+  if (ctx->operation == EVP_PKEY_OP_UNDEFINED) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_NO_OPERATION_SET);
+    return -1;
+  }
+
+  if (optype != -1 && !(ctx->operation & optype)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_INVALID_OPERATION);
+    return -1;
+  }
+
+  ret = ctx->pmeth->ctrl(ctx, cmd, p1, p2);
+
+  if (ret == -2) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_COMMAND_NOT_SUPPORTED);
+  }
+
+  return ret;
+}
+
+int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx) {
+  int ret;
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+
+  ctx->operation = EVP_PKEY_OP_SIGN;
+  if (!ctx->pmeth->sign_init) {
+    return 1;
+  }
+
+  ret = ctx->pmeth->sign_init(ctx);
+  /* TODO(fork): normalise the return value convention. */
+  if (ret <= 0) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+
+  return ret;
+}
+
+static int check_autoarg(const EVP_PKEY_CTX *ctx, const uint8_t *arg,
+                         size_t *arg_len) {
+  size_t size;
+
+  if (0 == (ctx->pmeth->flags & EVP_PKEY_FLAG_AUTOARGLEN)) {
+    return 1;
+  }
+
+  size = EVP_PKEY_size(ctx->pkey);
+  if (arg == NULL) {
+    *arg_len = size;
+    return 1;
+  }
+
+  if (*arg_len < size) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int EVP_PKEY_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *sig_len,
+                  const uint8_t *data, size_t data_len) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_SIGN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+  if (!check_autoarg(ctx, sig, sig_len)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign,
+                      EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+  return ctx->pmeth->sign(ctx, sig, sig_len, data, data_len);
+}
+
+int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx) {
+  int ret;
+
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->verify) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_verify_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  ctx->operation = EVP_PKEY_OP_VERIFY;
+  if (!ctx->pmeth->verify_init) {
+    return 1;
+  }
+  ret = ctx->pmeth->verify_init(ctx);
+  if (ret <= 0) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+  }
+  return ret;
+}
+
+int EVP_PKEY_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig, size_t sig_len,
+                    const uint8_t *data, size_t data_len) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->verify) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_verify,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  if (ctx->operation != EVP_PKEY_OP_VERIFY) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_verify, EVP_R_OPERATON_NOT_INITIALIZED);
+    return -1;
+  }
+  return ctx->pmeth->verify(ctx, sig, sig_len, data, data_len);
+}
+
+int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx) {
+  int ret;
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->encrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  ctx->operation = EVP_PKEY_OP_ENCRYPT;
+  if (!ctx->pmeth->encrypt_init) {
+    return 1;
+  }
+  ret = ctx->pmeth->encrypt_init(ctx);
+  if (ret <= 0) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+  }
+  return ret;
+}
+
+int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *outlen,
+                     const uint8_t *in, size_t inlen) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->encrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  if (ctx->operation != EVP_PKEY_OP_ENCRYPT) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt, EVP_R_OPERATON_NOT_INITIALIZED);
+    return -1;
+  }
+  if (!check_autoarg(ctx, out, outlen)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+  return ctx->pmeth->encrypt(ctx, out, outlen, in, inlen);
+}
+
+int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx) {
+  int ret;
+
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->decrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  ctx->operation = EVP_PKEY_OP_DECRYPT;
+  if (!ctx->pmeth->decrypt_init) {
+    return 1;
+  }
+  ret = ctx->pmeth->decrypt_init(ctx);
+  if (ret <= 0) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+  }
+  return ret;
+}
+
+int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *outlen,
+                     const uint8_t *in, size_t inlen) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->decrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  if (ctx->operation != EVP_PKEY_OP_DECRYPT) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt, EVP_R_OPERATON_NOT_INITIALIZED);
+    return -1;
+  }
+  if (!check_autoarg(ctx, out, outlen)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+  return ctx->pmeth->decrypt(ctx, out, outlen, in, inlen);
+}
+
+int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx) {
+  int ret;
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  ctx->operation = EVP_PKEY_OP_DERIVE;
+  if (!ctx->pmeth->derive_init) {
+    return 1;
+  }
+  ret = ctx->pmeth->derive_init(ctx);
+  if (ret <= 0) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+  }
+  return ret;
+}
+
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) {
+  int ret;
+  if (!ctx || !ctx->pmeth ||
+      !(ctx->pmeth->derive || ctx->pmeth->encrypt || ctx->pmeth->decrypt) ||
+      !ctx->pmeth->ctrl) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  if (ctx->operation != EVP_PKEY_OP_DERIVE &&
+      ctx->operation != EVP_PKEY_OP_ENCRYPT &&
+      ctx->operation != EVP_PKEY_OP_DECRYPT) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer,
+                      EVP_R_OPERATON_NOT_INITIALIZED);
+    return -1;
+  }
+
+  ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 0, peer);
+
+  if (ret <= 0) {
+    return ret;
+  }
+
+  if (ret == 2) {
+    return 1;
+  }
+
+  if (!ctx->pkey) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer, EVP_R_NO_KEY_SET);
+    return -1;
+  }
+
+  if (ctx->pkey->type != peer->type) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer, EVP_R_DIFFERENT_KEY_TYPES);
+    return -1;
+  }
+
+  /* ran@cryptocom.ru: For clarity.  The error is if parameters in peer are
+   * present (!missing) but don't match.  EVP_PKEY_cmp_parameters may return
+   * 1 (match), 0 (don't match) and -2 (comparison is not defined).  -1
+   * (different key types) is impossible here because it is checked earlier.
+   * -2 is OK for us here, as well as 1, so we can check for 0 only. */
+  if (!EVP_PKEY_missing_parameters(peer) &&
+      !EVP_PKEY_cmp_parameters(ctx->pkey, peer)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer,
+                      EVP_R_DIFFERENT_PARAMETERS);
+    return -1;
+  }
+
+  if (ctx->peerkey) {
+    EVP_PKEY_free(ctx->peerkey);
+  }
+  ctx->peerkey = peer;
+
+  ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 1, peer);
+
+  if (ret <= 0) {
+    ctx->peerkey = NULL;
+    return ret;
+  }
+
+  CRYPTO_add(&peer->references, 1, CRYPTO_LOCK_EVP_PKEY);
+  return 1;
+}
+
+int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, uint8_t *key, size_t *out_key_len) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  if (ctx->operation != EVP_PKEY_OP_DERIVE) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive, EVP_R_OPERATON_NOT_INITIALIZED);
+    return -1;
+  }
+  if (!check_autoarg(ctx, key, out_key_len)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+  return ctx->pmeth->derive(ctx, key, out_key_len);
+}
+
+int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx) {
+  int ret;
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  ctx->operation = EVP_PKEY_OP_KEYGEN;
+  if (!ctx->pmeth->keygen_init) {
+    return 1;
+  }
+  ret = ctx->pmeth->keygen_init(ctx);
+  if (ret <= 0) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+  }
+  return ret;
+}
+
+int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey) {
+  int ret;
+
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
+  }
+  if (ctx->operation != EVP_PKEY_OP_KEYGEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen, EVP_R_OPERATON_NOT_INITIALIZED);
+    return -1;
+  }
+
+  if (!ppkey) {
+    return -1;
+  }
+
+  if (!*ppkey) {
+    *ppkey = EVP_PKEY_new();
+  }
+
+  ret = ctx->pmeth->keygen(ctx, *ppkey);
+  if (ret <= 0) {
+    EVP_PKEY_free(*ppkey);
+    *ppkey = NULL;
+  }
+  return ret;
+}
diff --git a/crypto/evp/evp_error.c b/crypto/evp/evp_error.c
new file mode 100644
index 0000000..0b4d2f2
--- /dev/null
+++ b/crypto/evp/evp_error.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "evp.h"
+
+const ERR_STRING_DATA EVP_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_CTX_ctrl, 0), "EVP_PKEY_CTX_ctrl"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_copy_parameters, 0), "EVP_PKEY_copy_parameters"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_decrypt, 0), "EVP_PKEY_decrypt"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_decrypt_init, 0), "EVP_PKEY_decrypt_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_derive, 0), "EVP_PKEY_derive"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_derive_init, 0), "EVP_PKEY_derive_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_derive_set_peer, 0), "EVP_PKEY_derive_set_peer"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_encrypt, 0), "EVP_PKEY_encrypt"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_encrypt_init, 0), "EVP_PKEY_encrypt_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_DH, 0), "EVP_PKEY_get1_DH"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_DSA, 0), "EVP_PKEY_get1_DSA"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_EC_KEY, 0), "EVP_PKEY_get1_EC_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_RSA, 0), "EVP_PKEY_get1_RSA"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_keygen, 0), "EVP_PKEY_keygen"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_keygen_init, 0), "EVP_PKEY_keygen_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_new, 0), "EVP_PKEY_new"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_set_type, 0), "EVP_PKEY_set_type"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_sign, 0), "EVP_PKEY_sign"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_sign_init, 0), "EVP_PKEY_sign_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_verify, 0), "EVP_PKEY_verify"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_verify_init, 0), "EVP_PKEY_verify_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_check_padding_md, 0), "check_padding_md"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_d2i_AutoPrivateKey, 0), "d2i_AutoPrivateKey"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_d2i_PrivateKey, 0), "d2i_PrivateKey"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_do_EC_KEY_print, 0), "do_EC_KEY_print"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_do_rsa_print, 0), "do_rsa_print"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_do_sigver_init, 0), "do_sigver_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_param2type, 0), "eckey_param2type"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_param_decode, 0), "eckey_param_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_priv_decode, 0), "eckey_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_priv_encode, 0), "eckey_priv_encode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_pub_decode, 0), "eckey_pub_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_pub_encode, 0), "eckey_pub_encode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_type2param, 0), "eckey_type2param"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_evp_pkey_ctx_new, 0), "evp_pkey_ctx_new"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_i2d_PublicKey, 0), "i2d_PublicKey"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_old_ec_priv_decode, 0), "old_ec_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_old_rsa_priv_decode, 0), "old_rsa_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_ctrl, 0), "pkey_ec_ctrl"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_derive, 0), "pkey_ec_derive"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_keygen, 0), "pkey_ec_keygen"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_paramgen, 0), "pkey_ec_paramgen"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_sign, 0), "pkey_ec_sign"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_rsa_ctrl, 0), "pkey_rsa_ctrl"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_rsa_sign, 0), "pkey_rsa_sign"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_algor_to_md, 0), "rsa_algor_to_md"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_item_verify, 0), "rsa_item_verify"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_mgf1_to_md, 0), "rsa_mgf1_to_md"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_priv_decode, 0), "rsa_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_priv_encode, 0), "rsa_priv_encode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_pss_to_ctx, 0), "rsa_pss_to_ctx"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_pub_decode, 0), "rsa_pub_decode"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_BUFFER_TOO_SMALL), "BUFFER_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_COMMAND_NOT_SUPPORTED), "COMMAND_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DECODE_ERROR), "DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIFFERENT_KEY_TYPES), "DIFFERENT_KEY_TYPES"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIFFERENT_PARAMETERS), "DIFFERENT_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIGEST_DOES_NOT_MATCH), "DIGEST_DOES_NOT_MATCH"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_AN_DSA_KEY), "EXPECTING_AN_DSA_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_AN_EC_KEY_KEY), "EXPECTING_AN_EC_KEY_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_AN_RSA_KEY), "EXPECTING_AN_RSA_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_A_DH_KEY), "EXPECTING_A_DH_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_A_DSA_KEY), "EXPECTING_A_DSA_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPLICIT_EC_PARAMETERS_NOT_SUPPORTED), "EXPLICIT_EC_PARAMETERS_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE), "ILLEGAL_OR_UNSUPPORTED_PADDING_MODE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_CURVE), "INVALID_CURVE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_DIGEST_LENGTH), "INVALID_DIGEST_LENGTH"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_DIGEST_TYPE), "INVALID_DIGEST_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_KEYBITS), "INVALID_KEYBITS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_MGF1_MD), "INVALID_MGF1_MD"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_OPERATION), "INVALID_OPERATION"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_PADDING_MODE), "INVALID_PADDING_MODE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_PSS_PARAMETERS), "INVALID_PSS_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_PSS_SALTLEN), "INVALID_PSS_SALTLEN"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_SALT_LENGTH), "INVALID_SALT_LENGTH"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_TRAILER), "INVALID_TRAILER"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KDF_PARAMETER_ERROR), "KDF_PARAMETER_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYS_NOT_SET), "KEYS_NOT_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_MISSING_PARAMETERS), "MISSING_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DEFAULT_DIGEST), "NO_DEFAULT_DIGEST"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEY_SET), "NO_KEY_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_MDC2_SUPPORT), "NO_MDC2_SUPPORT"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_NID_FOR_CURVE), "NO_NID_FOR_CURVE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_OPERATION_SET), "NO_OPERATION_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_PARAMETERS_SET), "NO_PARAMETERS_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE), "OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_OPERATON_NOT_INITIALIZED), "OPERATON_NOT_INITIALIZED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_PEER_KEY_ERROR), "PEER_KEY_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_SHARED_INFO_ERROR), "SHARED_INFO_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_DIGEST), "UNKNOWN_DIGEST"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_MASK_DIGEST), "UNKNOWN_MASK_DIGEST"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_PUBLIC_KEY_TYPE), "UNKNOWN_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_ALGORITHM), "UNSUPPORTED_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_MASK_ALGORITHM), "UNSUPPORTED_MASK_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_MASK_PARAMETER), "UNSUPPORTED_MASK_PARAMETER"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE), "UNSUPPORTED_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_SIGNATURE_TYPE), "UNSUPPORTED_SIGNATURE_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_X931_UNSUPPORTED), "X931_UNSUPPORTED"},
+  {0, NULL},
+};
diff --git a/crypto/evp/example_sign.c b/crypto/evp/example_sign.c
new file mode 100644
index 0000000..4bbe194
--- /dev/null
+++ b/crypto/evp/example_sign.c
@@ -0,0 +1,198 @@
+/* Copyright (c) 2014, 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <openssl/bio.h>
+#include <openssl/digest.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+
+
+/* kExampleRSAKeyDER is an RSA private key in ASN.1, DER format. Of course, you
+ * should never use this key anywhere but in an example. */
+static const uint8_t kExampleRSAKeyDER[] = {
+    0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xf8,
+    0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9, 0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59,
+    0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb, 0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37,
+    0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0, 0xa6, 0x85, 0x15, 0x34, 0x75, 0x71,
+    0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef, 0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a,
+    0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa, 0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4,
+    0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76, 0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec,
+    0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25, 0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76,
+    0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79, 0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8,
+    0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49, 0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7,
+    0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd, 0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c,
+    0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61, 0x07, 0x02, 0x03, 0x01, 0x00, 0x01,
+    0x02, 0x81, 0x80, 0x74, 0x88, 0x64, 0x3f, 0x69, 0x45, 0x3a, 0x6d, 0xc7,
+    0x7f, 0xb9, 0xa3, 0xc0, 0x6e, 0xec, 0xdc, 0xd4, 0x5a, 0xb5, 0x32, 0x85,
+    0x5f, 0x19, 0xd4, 0xf8, 0xd4, 0x3f, 0x3c, 0xfa, 0xc2, 0xf6, 0x5f, 0xee,
+    0xe6, 0xba, 0x87, 0x74, 0x2e, 0xc7, 0x0c, 0xd4, 0x42, 0xb8, 0x66, 0x85,
+    0x9c, 0x7b, 0x24, 0x61, 0xaa, 0x16, 0x11, 0xf6, 0xb5, 0xb6, 0xa4, 0x0a,
+    0xc9, 0x55, 0x2e, 0x81, 0xa5, 0x47, 0x61, 0xcb, 0x25, 0x8f, 0xc2, 0x15,
+    0x7b, 0x0e, 0x7c, 0x36, 0x9f, 0x3a, 0xda, 0x58, 0x86, 0x1c, 0x5b, 0x83,
+    0x79, 0xe6, 0x2b, 0xcc, 0xe6, 0xfa, 0x2c, 0x61, 0xf2, 0x78, 0x80, 0x1b,
+    0xe2, 0xf3, 0x9d, 0x39, 0x2b, 0x65, 0x57, 0x91, 0x3d, 0x71, 0x99, 0x73,
+    0xa5, 0xc2, 0x79, 0x20, 0x8c, 0x07, 0x4f, 0xe5, 0xb4, 0x60, 0x1f, 0x99,
+    0xa2, 0xb1, 0x4f, 0x0c, 0xef, 0xbc, 0x59, 0x53, 0x00, 0x7d, 0xb1, 0x02,
+    0x41, 0x00, 0xfc, 0x7e, 0x23, 0x65, 0x70, 0xf8, 0xce, 0xd3, 0x40, 0x41,
+    0x80, 0x6a, 0x1d, 0x01, 0xd6, 0x01, 0xff, 0xb6, 0x1b, 0x3d, 0x3d, 0x59,
+    0x09, 0x33, 0x79, 0xc0, 0x4f, 0xde, 0x96, 0x27, 0x4b, 0x18, 0xc6, 0xd9,
+    0x78, 0xf1, 0xf4, 0x35, 0x46, 0xe9, 0x7c, 0x42, 0x7a, 0x5d, 0x9f, 0xef,
+    0x54, 0xb8, 0xf7, 0x9f, 0xc4, 0x33, 0x6c, 0xf3, 0x8c, 0x32, 0x46, 0x87,
+    0x67, 0x30, 0x7b, 0xa7, 0xac, 0xe3, 0x02, 0x41, 0x00, 0xfc, 0x2c, 0xdf,
+    0x0c, 0x0d, 0x88, 0xf5, 0xb1, 0x92, 0xa8, 0x93, 0x47, 0x63, 0x55, 0xf5,
+    0xca, 0x58, 0x43, 0xba, 0x1c, 0xe5, 0x9e, 0xb6, 0x95, 0x05, 0xcd, 0xb5,
+    0x82, 0xdf, 0xeb, 0x04, 0x53, 0x9d, 0xbd, 0xc2, 0x38, 0x16, 0xb3, 0x62,
+    0xdd, 0xa1, 0x46, 0xdb, 0x6d, 0x97, 0x93, 0x9f, 0x8a, 0xc3, 0x9b, 0x64,
+    0x7e, 0x42, 0xe3, 0x32, 0x57, 0x19, 0x1b, 0xd5, 0x6e, 0x85, 0xfa, 0xb8,
+    0x8d, 0x02, 0x41, 0x00, 0xbc, 0x3d, 0xde, 0x6d, 0xd6, 0x97, 0xe8, 0xba,
+    0x9e, 0x81, 0x37, 0x17, 0xe5, 0xa0, 0x64, 0xc9, 0x00, 0xb7, 0xe7, 0xfe,
+    0xf4, 0x29, 0xd9, 0x2e, 0x43, 0x6b, 0x19, 0x20, 0xbd, 0x99, 0x75, 0xe7,
+    0x76, 0xf8, 0xd3, 0xae, 0xaf, 0x7e, 0xb8, 0xeb, 0x81, 0xf4, 0x9d, 0xfe,
+    0x07, 0x2b, 0x0b, 0x63, 0x0b, 0x5a, 0x55, 0x90, 0x71, 0x7d, 0xf1, 0xdb,
+    0xd9, 0xb1, 0x41, 0x41, 0x68, 0x2f, 0x4e, 0x39, 0x02, 0x40, 0x5a, 0x34,
+    0x66, 0xd8, 0xf5, 0xe2, 0x7f, 0x18, 0xb5, 0x00, 0x6e, 0x26, 0x84, 0x27,
+    0x14, 0x93, 0xfb, 0xfc, 0xc6, 0x0f, 0x5e, 0x27, 0xe6, 0xe1, 0xe9, 0xc0,
+    0x8a, 0xe4, 0x34, 0xda, 0xe9, 0xa2, 0x4b, 0x73, 0xbc, 0x8c, 0xb9, 0xba,
+    0x13, 0x6c, 0x7a, 0x2b, 0x51, 0x84, 0xa3, 0x4a, 0xe0, 0x30, 0x10, 0x06,
+    0x7e, 0xed, 0x17, 0x5a, 0x14, 0x00, 0xc9, 0xef, 0x85, 0xea, 0x52, 0x2c,
+    0xbc, 0x65, 0x02, 0x40, 0x51, 0xe3, 0xf2, 0x83, 0x19, 0x9b, 0xc4, 0x1e,
+    0x2f, 0x50, 0x3d, 0xdf, 0x5a, 0xa2, 0x18, 0xca, 0x5f, 0x2e, 0x49, 0xaf,
+    0x6f, 0xcc, 0xfa, 0x65, 0x77, 0x94, 0xb5, 0xa1, 0x0a, 0xa9, 0xd1, 0x8a,
+    0x39, 0x37, 0xf4, 0x0b, 0xa0, 0xd7, 0x82, 0x27, 0x5e, 0xae, 0x17, 0x17,
+    0xa1, 0x1e, 0x54, 0x34, 0xbf, 0x6e, 0xc4, 0x8e, 0x99, 0x5d, 0x08, 0xf1,
+    0x2d, 0x86, 0x9d, 0xa5, 0x20, 0x1b, 0xe5, 0xdf,
+};
+
+static const uint8_t kMsg[] = {1, 2, 3, 4};
+
+static const uint8_t kSignature[] = {
+    0xa5, 0xf0, 0x8a, 0x47, 0x5d, 0x3c, 0xb3, 0xcc, 0xa9, 0x79, 0xaf, 0x4d,
+    0x8c, 0xae, 0x4c, 0x14, 0xef, 0xc2, 0x0b, 0x34, 0x36, 0xde, 0xf4, 0x3e,
+    0x3d, 0xbb, 0x4a, 0x60, 0x5c, 0xc8, 0x91, 0x28, 0xda, 0xfb, 0x7e, 0x04,
+    0x96, 0x7e, 0x63, 0x13, 0x90, 0xce, 0xb9, 0xb4, 0x62, 0x7a, 0xfd, 0x09,
+    0x3d, 0xc7, 0x67, 0x78, 0x54, 0x04, 0xeb, 0x52, 0x62, 0x6e, 0x24, 0x67,
+    0xb4, 0x40, 0xfc, 0x57, 0x62, 0xc6, 0xf1, 0x67, 0xc1, 0x97, 0x8f, 0x6a,
+    0xa8, 0xae, 0x44, 0x46, 0x5e, 0xab, 0x67, 0x17, 0x53, 0x19, 0x3a, 0xda,
+    0x5a, 0xc8, 0x16, 0x3e, 0x86, 0xd5, 0xc5, 0x71, 0x2f, 0xfc, 0x23, 0x48,
+    0xd9, 0x0b, 0x13, 0xdd, 0x7b, 0x5a, 0x25, 0x79, 0xef, 0xa5, 0x7b, 0x04,
+    0xed, 0x44, 0xf6, 0x18, 0x55, 0xe4, 0x0a, 0xe9, 0x57, 0x79, 0x5d, 0xd7,
+    0x55, 0xa7, 0xab, 0x45, 0x02, 0x97, 0x60, 0x42,
+};
+
+
+int example_EVP_DigestSignInit() {
+  int ret = 0;
+  EVP_PKEY *pkey = NULL;
+  RSA *rsa = NULL;
+  const uint8_t *derp = kExampleRSAKeyDER;
+  uint8_t *sig = NULL;
+  size_t sig_len;
+  EVP_MD_CTX md_ctx;
+
+  EVP_MD_CTX_init(&md_ctx);
+
+  if (!d2i_RSAPrivateKey(&rsa, &derp, sizeof(kExampleRSAKeyDER))) {
+    goto out;
+  }
+
+  sig_len = RSA_size(rsa);
+
+  pkey = EVP_PKEY_new();
+  sig = malloc(sig_len);
+  if (pkey == NULL ||
+      sig == NULL ||
+      !EVP_PKEY_set1_RSA(pkey, rsa) ||
+      EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1 ||
+      EVP_DigestSignUpdate(&md_ctx, kMsg, sizeof(kMsg)) != 1 ||
+      EVP_DigestSignFinal(&md_ctx, sig, &sig_len) != 1) {
+    goto out;
+  }
+  ret = 1;
+
+out:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+  if (rsa) {
+    RSA_free(rsa);
+  }
+  if (sig) {
+    free(sig);
+  }
+
+  return ret;
+}
+
+int example_EVP_DigestVerifyInit() {
+  int ret = 0;
+  EVP_PKEY *pkey = NULL;
+  RSA *rsa = NULL;
+  const uint8_t *derp = kExampleRSAKeyDER;
+  EVP_MD_CTX md_ctx;
+
+  EVP_MD_CTX_init(&md_ctx);
+
+  if (!d2i_RSAPrivateKey(&rsa, &derp, sizeof(kExampleRSAKeyDER))) {
+    goto out;
+  }
+
+  pkey = EVP_PKEY_new();
+  if (pkey == NULL ||
+      !EVP_PKEY_set1_RSA(pkey, rsa) ||
+      EVP_DigestVerifyInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1 ||
+      EVP_DigestVerifyUpdate(&md_ctx, kMsg, sizeof(kMsg)) != 1 ||
+      EVP_DigestVerifyFinal(&md_ctx, kSignature, sizeof(kSignature)) != 1) {
+    goto out;
+  }
+  ret = 1;
+
+out:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+  if (rsa) {
+    RSA_free(rsa);
+  }
+
+  return ret;
+}
+
+int main() {
+  if (!example_EVP_DigestSignInit()) {
+    fprintf(stderr, "EVP_DigestSignInit failed\n");
+    return 1;
+  }
+
+  if (!example_EVP_DigestVerifyInit()) {
+    fprintf(stderr, "EVP_DigestVerifyInit failed\n");
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
new file mode 100644
index 0000000..76f206e
--- /dev/null
+++ b/crypto/evp/internal.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_EVP_INTERNAL_H
+#define OPENSSL_HEADER_EVP_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* These values are flags for EVP_PKEY_ASN1_METHOD.flags. */
+#define ASN1_PKEY_ALIAS 0x1
+#define ASN1_PKEY_DYNAMIC 0x2
+#define ASN1_PKEY_SIGPARAM_NULL 0x4
+
+struct evp_pkey_asn1_method_st {
+  int pkey_id;
+  int pkey_base_id;
+  unsigned long pkey_flags;
+
+  char *pem_str;
+  char *info;
+
+  int (*pub_decode)(EVP_PKEY *pk, X509_PUBKEY *pub);
+  int (*pub_encode)(X509_PUBKEY *pub, const EVP_PKEY *pk);
+  int (*pub_cmp)(const EVP_PKEY *a, const EVP_PKEY *b);
+  int (*pub_print)(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx);
+
+  int (*priv_decode)(EVP_PKEY *pk, PKCS8_PRIV_KEY_INFO *p8inf);
+  int (*priv_encode)(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk);
+  int (*priv_print)(BIO *out, const EVP_PKEY *pkey, int indent,
+                    ASN1_PCTX *pctx);
+
+  int (*pkey_size)(const EVP_PKEY *pk);
+  int (*pkey_bits)(const EVP_PKEY *pk);
+
+  int (*param_decode)(EVP_PKEY *pkey, const unsigned char **pder, int derlen);
+  int (*param_encode)(const EVP_PKEY *pkey, unsigned char **pder);
+  int (*param_missing)(const EVP_PKEY *pk);
+  int (*param_copy)(EVP_PKEY *to, const EVP_PKEY *from);
+  int (*param_cmp)(const EVP_PKEY *a, const EVP_PKEY *b);
+  int (*param_print)(BIO *out, const EVP_PKEY *pkey, int indent,
+                     ASN1_PCTX *pctx);
+  int (*sig_print)(BIO *out, const X509_ALGOR *sigalg, const ASN1_STRING *sig,
+                   int indent, ASN1_PCTX *pctx);
+
+
+  void (*pkey_free)(EVP_PKEY *pkey);
+  int (*pkey_ctrl)(EVP_PKEY *pkey, int op, long arg1, void *arg2);
+
+  /* Legacy functions for old PEM */
+
+  int (*old_priv_decode)(EVP_PKEY *pkey, const unsigned char **pder,
+                         int derlen);
+  int (*old_priv_encode)(const EVP_PKEY *pkey, unsigned char **pder);
+  /* Custom ASN1 signature verification */
+  int (*item_verify)(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                     X509_ALGOR *a, ASN1_BIT_STRING *sig, EVP_PKEY *pkey);
+  int (*item_sign)(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                   X509_ALGOR *alg1, X509_ALGOR *alg2, ASN1_BIT_STRING *sig);
+
+} /* EVP_PKEY_ASN1_METHOD */;
+
+
+typedef int EVP_PKEY_gen_cb(EVP_PKEY_CTX *ctx);
+
+#define EVP_PKEY_OP_UNDEFINED 0
+#define EVP_PKEY_OP_PARAMGEN (1 << 1)
+#define EVP_PKEY_OP_KEYGEN (1 << 2)
+#define EVP_PKEY_OP_SIGN (1 << 3)
+#define EVP_PKEY_OP_VERIFY (1 << 4)
+#define EVP_PKEY_OP_VERIFYRECOVER (1 << 5)
+#define EVP_PKEY_OP_SIGNCTX (1 << 6)
+#define EVP_PKEY_OP_VERIFYCTX (1 << 7)
+#define EVP_PKEY_OP_ENCRYPT (1 << 8)
+#define EVP_PKEY_OP_DECRYPT (1 << 9)
+#define EVP_PKEY_OP_DERIVE (1 << 10)
+
+#define EVP_PKEY_OP_TYPE_SIG                                           \
+  (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYRECOVER | \
+   EVP_PKEY_OP_SIGNCTX | EVP_PKEY_OP_VERIFYCTX)
+
+#define EVP_PKEY_OP_TYPE_CRYPT (EVP_PKEY_OP_ENCRYPT | EVP_PKEY_OP_DECRYPT)
+
+#define EVP_PKEY_OP_TYPE_NOGEN \
+  (EVP_PKEY_OP_SIG | EVP_PKEY_OP_CRYPT | EVP_PKEY_OP_DERIVE)
+
+#define EVP_PKEY_OP_TYPE_GEN (EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN)
+
+#define EVP_PKEY_CTRL_MD 1
+#define EVP_PKEY_CTRL_GET_MD 2
+#define EVP_PKEY_CTRL_RSA_PADDING (EVP_PKEY_ALG_CTRL + 1)
+#define EVP_PKEY_CTRL_GET_RSA_PADDING (EVP_PKEY_ALG_CTRL + 2)
+#define EVP_PKEY_CTRL_RSA_PSS_SALTLEN (EVP_PKEY_ALG_CTRL + 3)
+#define EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN (EVP_PKEY_ALG_CTRL + 4)
+#define EVP_PKEY_CTRL_RSA_KEYGEN_BITS (EVP_PKEY_ALG_CTRL + 5)
+#define EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP	(EVP_PKEY_ALG_CTRL + 6)
+#define EVP_PKEY_CTRL_RSA_OAEP_MD (EVP_PKEY_ALG_CTRL + 7)
+#define EVP_PKEY_CTRL_GET_RSA_OAEP_MD (EVP_PKEY_ALG_CTRL + 8)
+#define EVP_PKEY_CTRL_RSA_MGF1_MD (EVP_PKEY_ALG_CTRL + 9)
+#define EVP_PKEY_CTRL_GET_RSA_MGF1_MD (EVP_PKEY_ALG_CTRL + 10)
+#define EVP_PKEY_CTRL_RSA_OAEP_LABEL (EVP_PKEY_ALG_CTRL + 11)
+#define EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL (EVP_PKEY_ALG_CTRL + 12)
+
+struct evp_pkey_ctx_st {
+  /* Method associated with this operation */
+  const EVP_PKEY_METHOD *pmeth;
+  /* Engine that implements this method or NULL if builtin */
+  ENGINE *engine;
+  /* Key: may be NULL */
+  EVP_PKEY *pkey;
+  /* Peer key for key agreement, may be NULL */
+  EVP_PKEY *peerkey;
+  /* operation contains one of the |EVP_PKEY_OP_*| values. */
+  int operation;
+  /* Algorithm specific data */
+  void *data;
+  /* Application specific data */
+  void *app_data;
+} /* EVP_PKEY_CTX */;
+
+/* EVP_PKEY_FLAG_AUTOARGLEN causes wrapper functions to automatically check the
+ * argument length to various functions (signing, decrypting etc) is equal to
+ * the value of |EVP_PKEY_size|. */
+#define EVP_PKEY_FLAG_AUTOARGLEN 2
+
+struct evp_pkey_method_st {
+  int pkey_id;
+  int flags;
+
+  int (*init)(EVP_PKEY_CTX *ctx);
+  int (*copy)(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src);
+  void (*cleanup)(EVP_PKEY_CTX *ctx);
+
+  int (*paramgen_init)(EVP_PKEY_CTX *ctx);
+  int (*paramgen)(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+
+  int (*keygen_init)(EVP_PKEY_CTX *ctx);
+  int (*keygen)(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+
+  int (*sign_init)(EVP_PKEY_CTX *ctx);
+  int (*sign)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+              const unsigned char *tbs, size_t tbslen);
+
+  int (*verify_init)(EVP_PKEY_CTX *ctx);
+  int (*verify)(EVP_PKEY_CTX *ctx, const unsigned char *sig, size_t siglen,
+                const unsigned char *tbs, size_t tbslen);
+
+  int (*signctx_init)(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
+  int (*signctx)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                 EVP_MD_CTX *mctx);
+
+  int (*verifyctx_init)(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
+  int (*verifyctx)(EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen,
+                   EVP_MD_CTX *mctx);
+
+  int (*encrypt_init)(EVP_PKEY_CTX *ctx);
+  int (*encrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+                 const unsigned char *in, size_t inlen);
+
+  int (*decrypt_init)(EVP_PKEY_CTX *ctx);
+  int (*decrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+                 const unsigned char *in, size_t inlen);
+
+  int (*derive_init)(EVP_PKEY_CTX *ctx);
+  int (*derive)(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
+
+  int (*ctrl)(EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
+  int (*ctrl_str)(EVP_PKEY_CTX *ctx, const char *type, const char *value);
+} /* EVP_PKEY_METHOD */;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_EVP_INTERNAL_H */
diff --git a/crypto/evp/p_ec.c b/crypto/evp/p_ec.c
new file mode 100644
index 0000000..2305e6e
--- /dev/null
+++ b/crypto/evp/p_ec.c
@@ -0,0 +1,423 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+#include "../ec/internal.h"
+
+
+typedef struct {
+  /* Key and paramgen group */
+  EC_GROUP *gen_group;
+  /* message digest */
+  const EVP_MD *md;
+  /* Duplicate key if custom cofactor needed */
+  EC_KEY *co_key;
+  /* Cofactor mode */
+  signed char cofactor_mode;
+  /* KDF (if any) to use for ECDH */
+  char kdf_type;
+  /* Message digest to use for key derivation */
+  const EVP_MD *kdf_md;
+  /* User key material */
+  unsigned char *kdf_ukm;
+  size_t kdf_ukmlen;
+  /* KDF output length */
+  size_t kdf_outlen;
+} EC_PKEY_CTX;
+
+
+static int pkey_ec_init(EVP_PKEY_CTX *ctx) {
+  EC_PKEY_CTX *dctx;
+  dctx = OPENSSL_malloc(sizeof(EC_PKEY_CTX));
+  if (!dctx) {
+    return 0;
+  }
+  memset(dctx, 0, sizeof(EC_PKEY_CTX));
+  dctx->cofactor_mode = -1;
+  dctx->kdf_type = EVP_PKEY_ECDH_KDF_NONE;
+
+  ctx->data = dctx;
+
+  return 1;
+}
+
+static int pkey_ec_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
+  EC_PKEY_CTX *dctx, *sctx;
+  if (!pkey_ec_init(dst)) {
+    return 0;
+  }
+  sctx = src->data;
+  dctx = dst->data;
+
+  if (sctx->gen_group) {
+    dctx->gen_group = EC_GROUP_dup(sctx->gen_group);
+    if (!dctx->gen_group) {
+      return 0;
+    }
+  }
+  dctx->md = sctx->md;
+
+  if (sctx->co_key) {
+    dctx->co_key = EC_KEY_dup(sctx->co_key);
+    if (!dctx->co_key) {
+      return 0;
+    }
+  }
+  dctx->kdf_type = sctx->kdf_type;
+  dctx->kdf_md = sctx->kdf_md;
+  dctx->kdf_outlen = sctx->kdf_outlen;
+  if (sctx->kdf_ukm) {
+    dctx->kdf_ukm = BUF_memdup(sctx->kdf_ukm, sctx->kdf_ukmlen);
+    if (!dctx->kdf_ukm) {
+      return 0;
+    }
+  } else {
+    dctx->kdf_ukm = NULL;
+  }
+  dctx->kdf_ukmlen = sctx->kdf_ukmlen;
+  return 1;
+}
+
+static void pkey_ec_cleanup(EVP_PKEY_CTX *ctx) {
+  EC_PKEY_CTX *dctx = ctx->data;
+  if (!dctx) {
+    return;
+  }
+
+  if (dctx->gen_group) {
+    EC_GROUP_free(dctx->gen_group);
+  }
+  if (dctx->co_key) {
+    EC_KEY_free(dctx->co_key);
+  }
+  if (dctx->kdf_ukm) {
+    OPENSSL_free(dctx->kdf_ukm);
+  }
+  OPENSSL_free(dctx);
+}
+
+static int pkey_ec_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *siglen,
+                        const uint8_t *tbs, size_t tbslen) {
+  int ret, type;
+  unsigned int sltmp;
+  EC_PKEY_CTX *dctx = ctx->data;
+  EC_KEY *ec = ctx->pkey->pkey.ec;
+
+  if (!sig) {
+    *siglen = ECDSA_size(ec);
+    return 1;
+  } else if (*siglen < (size_t)ECDSA_size(ec)) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_sign, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  type = NID_sha1;
+  if (dctx->md) {
+    type = EVP_MD_type(dctx->md);
+  }
+
+  ret = ECDSA_sign(type, tbs, tbslen, sig, &sltmp, ec);
+
+  if (ret <= 0) {
+    return ret;
+  }
+  *siglen = (size_t)sltmp;
+  return 1;
+}
+
+static int pkey_ec_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig, size_t siglen,
+                          const uint8_t *tbs, size_t tbslen) {
+  int type;
+  EC_PKEY_CTX *dctx = ctx->data;
+  EC_KEY *ec = ctx->pkey->pkey.ec;
+
+  type = NID_sha1;
+  if (dctx->md) {
+    type = EVP_MD_type(dctx->md);
+  }
+
+  return ECDSA_verify(type, tbs, tbslen, sig, siglen, ec);
+}
+
+static int pkey_ec_derive(EVP_PKEY_CTX *ctx, uint8_t *key,
+                          size_t *keylen) {
+  int ret;
+  size_t outlen;
+  const EC_POINT *pubkey = NULL;
+  EC_KEY *eckey;
+  EC_PKEY_CTX *dctx = ctx->data;
+
+  if (!ctx->pkey || !ctx->peerkey) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_derive, EVP_R_KEYS_NOT_SET);
+    return 0;
+  }
+
+  eckey = dctx->co_key ? dctx->co_key : ctx->pkey->pkey.ec;
+
+  if (!key) {
+    const EC_GROUP *group;
+    group = EC_KEY_get0_group(eckey);
+    *keylen = (EC_GROUP_get_degree(group) + 7) / 8;
+    return 1;
+  }
+  pubkey = EC_KEY_get0_public_key(ctx->peerkey->pkey.ec);
+
+  /* NB: unlike PKCS#3 DH, if *outlen is less than maximum size this is
+   * not an error, the result is truncated. */
+
+  outlen = *keylen;
+
+  ret = ECDH_compute_key(key, outlen, pubkey, eckey, 0);
+  if (ret < 0) {
+    return ret;
+  }
+  *keylen = ret;
+  return 1;
+}
+
+static int pkey_ec_kdf_derive(EVP_PKEY_CTX *ctx, uint8_t *key,
+                              size_t *keylen) {
+  EC_PKEY_CTX *dctx = ctx->data;
+  uint8_t *ktmp = NULL;
+  size_t ktmplen;
+  int rv = 0;
+
+  if (dctx->kdf_type == EVP_PKEY_ECDH_KDF_NONE) {
+    return pkey_ec_derive(ctx, key, keylen);
+  }
+  if (!key) {
+    *keylen = dctx->kdf_outlen;
+    return 1;
+  }
+  if (*keylen != dctx->kdf_outlen ||
+      !pkey_ec_derive(ctx, NULL, &ktmplen)) {
+    return 0;
+  }
+  ktmp = OPENSSL_malloc(ktmplen);
+  if (!ktmp) {
+    return 0;
+  }
+  if (!pkey_ec_derive(ctx, ktmp, &ktmplen)) {
+    goto err;
+  }
+
+  if (!ECDH_KDF_X9_62(key, *keylen, ktmp, ktmplen, dctx->kdf_ukm,
+                      dctx->kdf_ukmlen, dctx->kdf_md)) {
+    goto err;
+  }
+  rv = 1;
+
+err:
+  if (ktmp) {
+    OPENSSL_cleanse(ktmp, ktmplen);
+    OPENSSL_free(ktmp);
+  }
+  return rv;
+}
+
+static int pkey_ec_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+  EC_PKEY_CTX *dctx = ctx->data;
+  EC_GROUP *group;
+
+  switch (type) {
+    case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID:
+      group = EC_GROUP_new_by_curve_name(p1);
+      if (group == NULL) {
+        OPENSSL_PUT_ERROR(EVP, pkey_ec_ctrl, EVP_R_INVALID_CURVE);
+        return 0;
+      }
+      if (dctx->gen_group)
+        EC_GROUP_free(dctx->gen_group);
+      dctx->gen_group = group;
+      return 1;
+
+    case EVP_PKEY_CTRL_EC_KDF_TYPE:
+      if (p1 == -2)
+        return dctx->kdf_type;
+      if (p1 != EVP_PKEY_ECDH_KDF_NONE && p1 != EVP_PKEY_ECDH_KDF_X9_62)
+        return -2;
+      dctx->kdf_type = p1;
+      return 1;
+
+    case EVP_PKEY_CTRL_EC_KDF_MD:
+      dctx->kdf_md = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_EC_KDF_MD:
+      *(const EVP_MD **)p2 = dctx->kdf_md;
+      return 1;
+
+    case EVP_PKEY_CTRL_EC_KDF_OUTLEN:
+      if (p1 <= 0)
+        return -2;
+      dctx->kdf_outlen = (size_t)p1;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN:
+      *(int *)p2 = dctx->kdf_outlen;
+      return 1;
+
+    case EVP_PKEY_CTRL_EC_KDF_UKM:
+      if (dctx->kdf_ukm)
+        OPENSSL_free(dctx->kdf_ukm);
+      dctx->kdf_ukm = p2;
+      if (p2)
+        dctx->kdf_ukmlen = p1;
+      else
+        dctx->kdf_ukmlen = 0;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_EC_KDF_UKM:
+      *(unsigned char **)p2 = dctx->kdf_ukm;
+      return dctx->kdf_ukmlen;
+
+    case EVP_PKEY_CTRL_MD:
+      if (EVP_MD_type((const EVP_MD *)p2) != NID_sha1 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_ecdsa_with_SHA1 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha224 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha256 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha384 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha512) {
+        OPENSSL_PUT_ERROR(EVP, pkey_ec_ctrl, EVP_R_INVALID_DIGEST_TYPE);
+        return 0;
+      }
+      dctx->md = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+      *(const EVP_MD **)p2 = dctx->md;
+      return 1;
+
+    case EVP_PKEY_CTRL_PEER_KEY:
+    /* Default behaviour is OK */
+    case EVP_PKEY_CTRL_DIGESTINIT:
+      return 1;
+
+    default:
+      return -2;
+  }
+}
+
+static int pkey_ec_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  EC_KEY *ec = NULL;
+  EC_PKEY_CTX *dctx = ctx->data;
+  int ret = 0;
+
+  if (dctx->gen_group == NULL) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_paramgen, EVP_R_NO_PARAMETERS_SET);
+    return 0;
+  }
+  ec = EC_KEY_new();
+  if (!ec) {
+    return 0;
+  }
+  ret = EC_KEY_set_group(ec, dctx->gen_group);
+  if (ret) {
+    EVP_PKEY_assign_EC_KEY(pkey, ec);
+  } else {
+    EC_KEY_free(ec);
+  }
+  return ret;
+}
+
+static int pkey_ec_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  EC_KEY *ec = NULL;
+  EC_PKEY_CTX *dctx = ctx->data;
+  if (ctx->pkey == NULL && dctx->gen_group == NULL) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_keygen, EVP_R_NO_PARAMETERS_SET);
+    return 0;
+  }
+  ec = EC_KEY_new();
+  if (!ec) {
+    return 0;
+  }
+  EVP_PKEY_assign_EC_KEY(pkey, ec);
+  if (ctx->pkey) {
+    /* Note: if error return, pkey is freed by parent routine */
+    if (!EVP_PKEY_copy_parameters(pkey, ctx->pkey)) {
+      return 0;
+    }
+  } else {
+    if (!EC_KEY_set_group(ec, dctx->gen_group)) {
+      return 0;
+    }
+  }
+  return EC_KEY_generate_key(pkey->pkey.ec);
+}
+
+const EVP_PKEY_METHOD ec_pkey_meth = {
+    EVP_PKEY_EC,            0 /* flags */,        pkey_ec_init,
+    pkey_ec_copy,           pkey_ec_cleanup,      0 /* paramgen_init */,
+    pkey_ec_paramgen,       0 /* keygen_init */,  pkey_ec_keygen,
+    0 /* sign_init */,      pkey_ec_sign,         0 /* verify_init */,
+    pkey_ec_verify,         0 /* signctx_init */, 0 /* signctx */,
+    0 /* verifyctx_init */, 0 /* verifyctx */,    0 /* encrypt_init */,
+    0 /* encrypt */,        0 /* decrypt_init */, 0 /* decrypt */,
+    0 /* derive_init */,    pkey_ec_kdf_derive,   pkey_ec_ctrl,
+};
diff --git a/crypto/evp/p_ec_asn1.c b/crypto/evp/p_ec_asn1.c
new file mode 100644
index 0000000..d4eec13
--- /dev/null
+++ b/crypto/evp/p_ec_asn1.c
@@ -0,0 +1,577 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key) {
+  const EC_GROUP *group;
+  int nid;
+
+  if (ec_key == NULL || (group = EC_KEY_get0_group(ec_key)) == NULL) {
+    OPENSSL_PUT_ERROR(EVP, eckey_param2type, EVP_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  nid = EC_GROUP_get_curve_name(group);
+  if (nid == NID_undef) {
+    OPENSSL_PUT_ERROR(EVP, eckey_param2type, EVP_R_NO_NID_FOR_CURVE);
+    return 0;
+  }
+
+  *ppval = (void*) OBJ_nid2obj(nid);
+  *pptype = V_ASN1_OBJECT;
+  return 1;
+}
+
+static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
+  EC_KEY *ec_key = pkey->pkey.ec;
+  void *pval = NULL;
+  int ptype;
+  uint8_t *penc = NULL, *p;
+  int penclen;
+
+  if (!eckey_param2type(&ptype, &pval, ec_key)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_pub_encode, ERR_R_EC_LIB);
+    return 0;
+  }
+  penclen = i2o_ECPublicKey(ec_key, NULL);
+  if (penclen <= 0) {
+    goto err;
+  }
+  penc = OPENSSL_malloc(penclen);
+  if (!penc) {
+    goto err;
+  }
+  p = penc;
+  penclen = i2o_ECPublicKey(ec_key, &p);
+  if (penclen <= 0) {
+    goto err;
+  }
+  if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_EC), ptype, pval, penc,
+                             penclen)) {
+    return 1;
+  }
+
+err:
+  if (ptype == V_ASN1_OBJECT) {
+    ASN1_OBJECT_free(pval);
+  } else {
+    ASN1_STRING_free(pval);
+  }
+  if (penc) {
+    OPENSSL_free(penc);
+  }
+  return 0;
+}
+
+static EC_KEY *eckey_type2param(int ptype, void *pval) {
+  EC_KEY *eckey = NULL;
+
+  if (ptype == V_ASN1_SEQUENCE) {
+    ASN1_STRING *pstr = pval;
+    const uint8_t *pm = pstr->data;
+    int pmlen = pstr->length;
+
+    eckey = d2i_ECParameters(NULL, &pm, pmlen);
+    if (eckey == NULL) {
+      OPENSSL_PUT_ERROR(EVP, eckey_type2param, EVP_R_DECODE_ERROR);
+      goto err;
+    }
+  } else if (ptype == V_ASN1_OBJECT) {
+    ASN1_OBJECT *poid = pval;
+    EC_GROUP *group;
+
+    /* type == V_ASN1_OBJECT => the parameters are given
+     * by an asn1 OID */
+    eckey = EC_KEY_new();
+    if (eckey == NULL) {
+      OPENSSL_PUT_ERROR(EVP, eckey_type2param, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    group = EC_GROUP_new_by_curve_name(OBJ_obj2nid(poid));
+    if (group == NULL) {
+      goto err;
+    }
+    if (EC_KEY_set_group(eckey, group) == 0) {
+      goto err;
+    }
+    EC_GROUP_free(group);
+  } else {
+    OPENSSL_PUT_ERROR(EVP, eckey_type2param, EVP_R_DECODE_ERROR);
+    goto err;
+  }
+
+  return eckey;
+
+err:
+  if (eckey) {
+    EC_KEY_free(eckey);
+  }
+  return NULL;
+}
+
+static int eckey_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
+  const uint8_t *p = NULL;
+  void *pval;
+  int ptype, pklen;
+  EC_KEY *eckey = NULL;
+  X509_ALGOR *palg;
+
+  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) {
+    return 0;
+  }
+  X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+  eckey = eckey_type2param(ptype, pval);
+  if (!eckey) {
+    OPENSSL_PUT_ERROR(EVP, eckey_pub_decode, ERR_R_EC_LIB);
+    return 0;
+  }
+
+  /* We have parameters now set public key */
+  if (!o2i_ECPublicKey(&eckey, &p, pklen)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_pub_decode, EVP_R_DECODE_ERROR);
+    goto err;
+  }
+
+  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  return 1;
+
+err:
+  if (eckey)
+    EC_KEY_free(eckey);
+  return 0;
+}
+
+static int eckey_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+  int r;
+  const EC_GROUP *group = EC_KEY_get0_group(b->pkey.ec);
+  const EC_POINT *pa = EC_KEY_get0_public_key(a->pkey.ec),
+                 *pb = EC_KEY_get0_public_key(b->pkey.ec);
+  r = EC_POINT_cmp(group, pa, pb, NULL);
+  if (r == 0) {
+    return 1;
+  } else if (r == 1) {
+    return 0;
+  } else {
+    return -2;
+  }
+}
+
+static int eckey_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) {
+  const uint8_t *p = NULL;
+  void *pval;
+  int ptype, pklen;
+  EC_KEY *eckey = NULL;
+  X509_ALGOR *palg;
+
+  if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8)) {
+    return 0;
+  }
+  X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+  eckey = eckey_type2param(ptype, pval);
+
+  if (!eckey)
+    goto ecliberr;
+
+  /* We have parameters now set private key */
+  if (!d2i_ECPrivateKey(&eckey, &p, pklen)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, EVP_R_DECODE_ERROR);
+    goto ecerr;
+  }
+
+  /* calculate public key (if necessary) */
+  if (EC_KEY_get0_public_key(eckey) == NULL) {
+    const BIGNUM *priv_key;
+    const EC_GROUP *group;
+    EC_POINT *pub_key;
+    /* the public key was not included in the SEC1 private
+     * key => calculate the public key */
+    group = EC_KEY_get0_group(eckey);
+    pub_key = EC_POINT_new(group);
+    if (pub_key == NULL) {
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    if (!EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group))) {
+      EC_POINT_free(pub_key);
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    priv_key = EC_KEY_get0_private_key(eckey);
+    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL)) {
+      EC_POINT_free(pub_key);
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    if (EC_KEY_set_public_key(eckey, pub_key) == 0) {
+      EC_POINT_free(pub_key);
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    EC_POINT_free(pub_key);
+  }
+
+  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  return 1;
+
+ecliberr:
+  OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+ecerr:
+  if (eckey)
+    EC_KEY_free(eckey);
+  return 0;
+}
+
+static int eckey_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey) {
+  EC_KEY *ec_key;
+  uint8_t *ep, *p;
+  int eplen, ptype;
+  void *pval;
+  unsigned int tmp_flags, old_flags;
+
+  ec_key = pkey->pkey.ec;
+
+  if (!eckey_param2type(&ptype, &pval, ec_key)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
+  /* set the private key */
+
+  /* do not include the parameters in the SEC1 private key
+   * see PKCS#11 12.11 */
+  old_flags = EC_KEY_get_enc_flags(ec_key);
+  tmp_flags = old_flags | EC_PKEY_NO_PARAMETERS;
+  EC_KEY_set_enc_flags(ec_key, tmp_flags);
+  eplen = i2d_ECPrivateKey(ec_key, NULL);
+  if (!eplen) {
+    EC_KEY_set_enc_flags(ec_key, old_flags);
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, ERR_R_EC_LIB);
+    return 0;
+  }
+  ep = (uint8_t *)OPENSSL_malloc(eplen);
+  if (!ep) {
+    EC_KEY_set_enc_flags(ec_key, old_flags);
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  p = ep;
+  if (!i2d_ECPrivateKey(ec_key, &p)) {
+    EC_KEY_set_enc_flags(ec_key, old_flags);
+    OPENSSL_free(ep);
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, ERR_R_EC_LIB);
+    return 0;
+  }
+  /* restore old encoding flags */
+  EC_KEY_set_enc_flags(ec_key, old_flags);
+
+  if (!PKCS8_pkey_set0(p8, (ASN1_OBJECT *)OBJ_nid2obj(NID_X9_62_id_ecPublicKey),
+                       0, ptype, pval, ep, eplen)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int int_ec_size(const EVP_PKEY *pkey) {
+  return ECDSA_size(pkey->pkey.ec);
+}
+
+static int ec_bits(const EVP_PKEY *pkey) {
+  BIGNUM *order = BN_new();
+  const EC_GROUP *group;
+  int ret;
+
+  if (!order) {
+    ERR_clear_error();
+    return 0;
+  }
+  group = EC_KEY_get0_group(pkey->pkey.ec);
+  if (!EC_GROUP_get_order(group, order, NULL)) {
+    ERR_clear_error();
+    return 0;
+  }
+
+  ret = BN_num_bits(order);
+  BN_free(order);
+  return ret;
+}
+
+static int ec_missing_parameters(const EVP_PKEY *pkey) {
+  return EC_KEY_get0_group(pkey->pkey.ec) == NULL;
+}
+
+static int ec_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) {
+  EC_GROUP *group = EC_GROUP_dup(EC_KEY_get0_group(from->pkey.ec));
+  if (group == NULL ||
+      EC_KEY_set_group(to->pkey.ec, group) == 0) {
+    return 0;
+  }
+  EC_GROUP_free(group);
+  return 1;
+}
+
+static int ec_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
+  const EC_GROUP *group_a = EC_KEY_get0_group(a->pkey.ec),
+                 *group_b = EC_KEY_get0_group(b->pkey.ec);
+  return EC_GROUP_cmp(group_a, group_b);
+}
+
+static void int_ec_free(EVP_PKEY *pkey) { EC_KEY_free(pkey->pkey.ec); }
+
+static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, int ktype) {
+  uint8_t *buffer = NULL;
+  const char *ecstr;
+  size_t buf_len = 0, i;
+  int ret = 0, reason = ERR_R_BIO_LIB;
+  BIGNUM *order = NULL;
+  BN_CTX *ctx = NULL;
+  const EC_GROUP *group;
+  const EC_POINT *public_key;
+  const BIGNUM *priv_key;
+  uint8_t *pub_key_bytes = NULL;
+  size_t pub_key_bytes_len = 0;
+
+  if (x == NULL || (group = EC_KEY_get0_group(x)) == NULL) {
+    reason = ERR_R_PASSED_NULL_PARAMETER;
+    goto err;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    reason = ERR_R_MALLOC_FAILURE;
+    goto err;
+  }
+
+  if (ktype > 0) {
+    public_key = EC_KEY_get0_public_key(x);
+    pub_key_bytes_len = EC_POINT_point2oct(
+        group, public_key, EC_KEY_get_conv_form(x), NULL, 0, ctx);
+    if (pub_key_bytes_len == 0) {
+      reason = ERR_R_MALLOC_FAILURE;
+      goto err;
+    }
+    pub_key_bytes = OPENSSL_malloc(pub_key_bytes_len);
+    if (pub_key_bytes == NULL) {
+      reason = ERR_R_MALLOC_FAILURE;
+      goto err;
+    }
+    pub_key_bytes_len =
+        EC_POINT_point2oct(group, public_key, EC_KEY_get_conv_form(x),
+                           pub_key_bytes, pub_key_bytes_len, ctx);
+    if (pub_key_bytes_len == 0) {
+      reason = ERR_R_MALLOC_FAILURE;
+      goto err;
+    }
+    buf_len = pub_key_bytes_len;
+  }
+
+  if (ktype == 2) {
+    priv_key = EC_KEY_get0_private_key(x);
+    if (priv_key && (i = (size_t)BN_num_bytes(priv_key)) > buf_len)
+      buf_len = i;
+  } else
+    priv_key = NULL;
+
+  if (ktype > 0) {
+    buf_len += 10;
+    if ((buffer = OPENSSL_malloc(buf_len)) == NULL) {
+      reason = ERR_R_MALLOC_FAILURE;
+      goto err;
+    }
+  }
+  if (ktype == 2)
+    ecstr = "Private-Key";
+  else if (ktype == 1)
+    ecstr = "Public-Key";
+  else
+    ecstr = "ECDSA-Parameters";
+
+  if (!BIO_indent(bp, off, 128))
+    goto err;
+  if ((order = BN_new()) == NULL)
+    goto err;
+  if (!EC_GROUP_get_order(group, order, NULL))
+    goto err;
+  if (BIO_printf(bp, "%s: (%d bit)\n", ecstr, BN_num_bits(order)) <= 0)
+    goto err;
+
+  if ((priv_key != NULL) && !ASN1_bn_print(bp, "priv:", priv_key, buffer, off))
+    goto err;
+  if (pub_key_bytes != NULL) {
+    BIO_hexdump(bp, pub_key_bytes, pub_key_bytes_len, off);
+  }
+  /* TODO(fork): implement */
+  /*
+  if (!ECPKParameters_print(bp, group, off))
+    goto err; */
+  ret = 1;
+
+err:
+  if (!ret)
+    OPENSSL_PUT_ERROR(EVP, do_EC_KEY_print, reason);
+  if (pub_key_bytes)
+    OPENSSL_free(pub_key_bytes);
+  if (order)
+    BN_free(order);
+  if (ctx)
+    BN_CTX_free(ctx);
+  if (buffer != NULL)
+    OPENSSL_free(buffer);
+  return ret;
+}
+
+static int eckey_param_decode(EVP_PKEY *pkey, const uint8_t **pder,
+                              int derlen) {
+  EC_KEY *eckey;
+  if (!(eckey = d2i_ECParameters(NULL, pder, derlen))) {
+    OPENSSL_PUT_ERROR(EVP, eckey_param_decode, ERR_R_EC_LIB);
+    return 0;
+  }
+  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  return 1;
+}
+
+static int eckey_param_encode(const EVP_PKEY *pkey, uint8_t **pder) {
+  return i2d_ECParameters(pkey->pkey.ec, pder);
+}
+
+static int eckey_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                             ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 0);
+}
+
+static int eckey_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 1);
+}
+
+
+static int eckey_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 2);
+}
+
+static int old_ec_priv_decode(EVP_PKEY *pkey, const uint8_t **pder,
+                              int derlen) {
+  EC_KEY *ec;
+  if (!(ec = d2i_ECPrivateKey(NULL, pder, derlen))) {
+    OPENSSL_PUT_ERROR(EVP, old_ec_priv_decode, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+  EVP_PKEY_assign_EC_KEY(pkey, ec);
+  return 1;
+}
+
+static int old_ec_priv_encode(const EVP_PKEY *pkey, uint8_t **pder) {
+  return i2d_ECPrivateKey(pkey->pkey.ec, pder);
+}
+
+static int ec_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) {
+  switch (op) {
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+      *(int *)arg2 = NID_sha1;
+      return 2;
+
+    default:
+      return -2;
+  }
+}
+
+const EVP_PKEY_ASN1_METHOD ec_asn1_meth = {
+  EVP_PKEY_EC,
+  EVP_PKEY_EC,
+  0,
+  "EC",
+  "OpenSSL EC algorithm",
+
+  eckey_pub_decode,
+  eckey_pub_encode,
+  eckey_pub_cmp,
+  eckey_pub_print,
+
+  eckey_priv_decode,
+  eckey_priv_encode,
+  eckey_priv_print,
+
+  int_ec_size,
+  ec_bits,
+
+  eckey_param_decode,
+  eckey_param_encode,
+  ec_missing_parameters,
+  ec_copy_parameters,
+  ec_cmp_parameters,
+  eckey_param_print,
+  0,
+
+  int_ec_free,
+  ec_pkey_ctrl,
+  old_ec_priv_decode,
+  old_ec_priv_encode
+};
diff --git a/crypto/evp/p_hmac.c b/crypto/evp/p_hmac.c
new file mode 100644
index 0000000..b55db7a
--- /dev/null
+++ b/crypto/evp/p_hmac.c
@@ -0,0 +1,215 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2007.
+ */
+/* ====================================================================
+ * Copyright (c) 2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/hmac.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+typedef struct {
+  const EVP_MD *md;       /* MD for HMAC use */
+  ASN1_OCTET_STRING ktmp; /* Temp storage for key */
+  HMAC_CTX ctx;
+} HMAC_PKEY_CTX;
+
+static int pkey_hmac_init(EVP_PKEY_CTX *ctx) {
+  HMAC_PKEY_CTX *hctx;
+  hctx = OPENSSL_malloc(sizeof(HMAC_PKEY_CTX));
+  if (!hctx) {
+    return 0;
+  }
+  memset(hctx, 0, sizeof(HMAC_PKEY_CTX));
+  hctx->ktmp.type = V_ASN1_OCTET_STRING;
+  HMAC_CTX_init(&hctx->ctx);
+
+  ctx->data = hctx;
+
+  return 1;
+}
+
+static int pkey_hmac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
+  HMAC_PKEY_CTX *sctx, *dctx;
+  if (!pkey_hmac_init(dst)) {
+    return 0;
+  }
+  sctx = src->data;
+  dctx = dst->data;
+  dctx->md = sctx->md;
+  HMAC_CTX_init(&dctx->ctx);
+  if (!HMAC_CTX_copy(&dctx->ctx, &sctx->ctx)) {
+    return 0;
+  }
+  if (sctx->ktmp.data) {
+    if (!ASN1_OCTET_STRING_set(&dctx->ktmp, sctx->ktmp.data,
+                               sctx->ktmp.length)) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+static void pkey_hmac_cleanup(EVP_PKEY_CTX *ctx) {
+  HMAC_PKEY_CTX *hctx = ctx->data;
+
+  HMAC_CTX_cleanup(&hctx->ctx);
+  if (hctx->ktmp.data) {
+    if (hctx->ktmp.length) {
+      OPENSSL_cleanse(hctx->ktmp.data, hctx->ktmp.length);
+    }
+    OPENSSL_free(hctx->ktmp.data);
+    hctx->ktmp.data = NULL;
+  }
+  OPENSSL_free(hctx);
+}
+
+static int pkey_hmac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  ASN1_OCTET_STRING *hkey = NULL;
+  HMAC_PKEY_CTX *hctx = ctx->data;
+
+  if (!hctx->ktmp.data) {
+    return 0;
+  }
+  hkey = ASN1_OCTET_STRING_dup(&hctx->ktmp);
+  if (!hkey) {
+    return 0;
+  }
+  EVP_PKEY_assign(pkey, EVP_PKEY_HMAC, hkey);
+
+  return 1;
+}
+
+static int int_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  HMAC_PKEY_CTX *hctx = ctx->pctx->data;
+  return HMAC_Update(&hctx->ctx, data, count);
+}
+
+static int hmac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx) {
+  HMAC_PKEY_CTX *hctx = ctx->data;
+
+  HMAC_CTX_set_flags(&hctx->ctx, mctx->flags & ~EVP_MD_CTX_FLAG_NO_INIT);
+  EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT);
+  mctx->update = int_update;
+  return 1;
+}
+
+static int hmac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                        EVP_MD_CTX *mctx) {
+  unsigned int hlen;
+  HMAC_PKEY_CTX *hctx = ctx->data;
+  int l = EVP_MD_CTX_size(mctx);
+
+  if (l < 0) {
+    return 0;
+  }
+  *siglen = l;
+  if (!sig) {
+    return 1;
+  }
+
+  if (!HMAC_Final(&hctx->ctx, sig, &hlen)) {
+    return 0;
+  }
+  *siglen = (size_t)hlen;
+  return 1;
+}
+
+static int pkey_hmac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+  HMAC_PKEY_CTX *hctx = ctx->data;
+  ASN1_OCTET_STRING *key;
+
+  switch (type) {
+    case EVP_PKEY_CTRL_SET_MAC_KEY:
+      if ((!p2 && p1 > 0) || (p1 < -1)) {
+        return 0;
+      }
+      if (!ASN1_OCTET_STRING_set(&hctx->ktmp, p2, p1)) {
+        return 0;
+      }
+      break;
+
+    case EVP_PKEY_CTRL_MD:
+      hctx->md = p2;
+      break;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+      key = (ASN1_OCTET_STRING *)ctx->pkey->pkey.ptr;
+      if (!HMAC_Init_ex(&hctx->ctx, key->data, key->length, hctx->md,
+                        ctx->engine)) {
+        return 0;
+      }
+      break;
+
+    default:
+      return -2;
+  }
+  return 1;
+}
+
+const EVP_PKEY_METHOD hmac_pkey_meth = {
+    EVP_PKEY_HMAC,          0 /* flags */,        pkey_hmac_init,
+    pkey_hmac_copy,         pkey_hmac_cleanup,    0 /* paramgen_init */,
+    0 /* paramgen */,       0 /* keygen_init */,  pkey_hmac_keygen,
+    0 /* sign_init */,      0 /* sign */,         0 /* verify_init */,
+    0 /* verify */,         hmac_signctx_init,    hmac_signctx,
+    0 /* verifyctx_init */, 0 /* verifyctx */,    0 /* encrypt_init */,
+    0 /* encrypt */,        0 /* decrypt_init */, 0 /* decrypt */,
+    0 /* derive_init */,    0 /* derive */,       pkey_hmac_ctrl,
+    0,
+};
diff --git a/crypto/evp/p_hmac_asn1.c b/crypto/evp/p_hmac_asn1.c
new file mode 100644
index 0000000..cabd737
--- /dev/null
+++ b/crypto/evp/p_hmac_asn1.c
@@ -0,0 +1,99 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2007.
+ */
+/* ====================================================================
+ * Copyright (c) 2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/digest.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+static int hmac_size(const EVP_PKEY *pkey) { return EVP_MAX_MD_SIZE; }
+
+static void hmac_key_free(EVP_PKEY *pkey) {
+  ASN1_OCTET_STRING *os = (ASN1_OCTET_STRING *)pkey->pkey.ptr;
+  if (os) {
+    if (os->data) {
+      OPENSSL_cleanse(os->data, os->length);
+    }
+    ASN1_OCTET_STRING_free(os);
+  }
+}
+
+static int hmac_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) {
+  switch (op) {
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+      *(int *)arg2 = NID_sha1;
+      return 1;
+
+    default:
+      return -2;
+  }
+}
+
+const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = {
+    EVP_PKEY_HMAC,       EVP_PKEY_HMAC,         0 /* flags */,
+    "HMAC",              "OpenSSL HMAC method", 0 /* pub_decode */,
+    0 /* pub_encode */,  0 /* pub_cmp */,       0 /* pub_print */,
+    0 /*priv_decode */,  0 /* priv_encode */,   0 /* priv_print */,
+    hmac_size,           0 /* pkey_bits */,     0 /* param_decode */,
+    0 /* param_encode*/, 0 /* param_missing*/,  0 /* param_copy*/,
+    0 /* param_cmp*/,    0 /* param_print*/,    0 /* sig_print*/,
+    hmac_key_free,       hmac_pkey_ctrl,        0 /* old_priv_decode */,
+    0 /* old_priv_encode */
+};
diff --git a/crypto/evp/p_rsa.c b/crypto/evp/p_rsa.c
new file mode 100644
index 0000000..2fab371
--- /dev/null
+++ b/crypto/evp/p_rsa.c
@@ -0,0 +1,596 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+
+#include "../rsa/internal.h"
+#include "internal.h"
+
+
+typedef struct {
+  /* Key gen parameters */
+  int nbits;
+  BIGNUM *pub_exp;
+  /* RSA padding mode */
+  int pad_mode;
+  /* message digest */
+  const EVP_MD *md;
+  /* message digest for MGF1 */
+  const EVP_MD *mgf1md;
+  /* PSS salt length */
+  int saltlen;
+  /* tbuf is a buffer which is either NULL, or is the size of the RSA modulus.
+   * It's used to store the output of RSA operations. */
+  uint8_t *tbuf;
+  /* OAEP label */
+  uint8_t *oaep_label;
+  size_t oaep_labellen;
+} RSA_PKEY_CTX;
+
+static int pkey_rsa_init(EVP_PKEY_CTX *ctx) {
+  RSA_PKEY_CTX *rctx;
+  rctx = OPENSSL_malloc(sizeof(RSA_PKEY_CTX));
+  if (!rctx) {
+    return 0;
+  }
+  memset(rctx, 0, sizeof(RSA_PKEY_CTX));
+
+  rctx->nbits = 2048;
+  rctx->pad_mode = RSA_PKCS1_PADDING;
+  rctx->saltlen = -2;
+
+  ctx->data = rctx;
+
+  return 1;
+}
+
+static int pkey_rsa_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
+  RSA_PKEY_CTX *dctx, *sctx;
+  if (!pkey_rsa_init(dst)) {
+    return 0;
+  }
+  sctx = src->data;
+  dctx = dst->data;
+  dctx->nbits = sctx->nbits;
+  if (sctx->pub_exp) {
+    dctx->pub_exp = BN_dup(sctx->pub_exp);
+    if (!dctx->pub_exp) {
+      return 0;
+    }
+  }
+
+  dctx->pad_mode = sctx->pad_mode;
+  dctx->md = sctx->md;
+  dctx->mgf1md = sctx->mgf1md;
+  if (sctx->oaep_label) {
+    if (dctx->oaep_label) {
+      OPENSSL_free(dctx->oaep_label);
+    }
+    dctx->oaep_label = BUF_memdup(sctx->oaep_label, sctx->oaep_labellen);
+    if (!dctx->oaep_label) {
+      return 0;
+    }
+    dctx->oaep_labellen = sctx->oaep_labellen;
+  }
+
+  return 1;
+}
+
+static void pkey_rsa_cleanup(EVP_PKEY_CTX *ctx) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+
+  if (rctx == NULL) {
+    return;
+  }
+
+  if (rctx->pub_exp) {
+    BN_free(rctx->pub_exp);
+  }
+  if (rctx->tbuf) {
+    OPENSSL_free(rctx->tbuf);
+  }
+  if (rctx->oaep_label) {
+    OPENSSL_free(rctx->oaep_label);
+  }
+  OPENSSL_free(rctx);
+}
+
+static int setup_tbuf(RSA_PKEY_CTX *ctx, EVP_PKEY_CTX *pk) {
+  if (ctx->tbuf) {
+    return 1;
+  }
+  ctx->tbuf = OPENSSL_malloc(EVP_PKEY_size(pk->pkey));
+  if (!ctx->tbuf) {
+    return 0;
+  }
+  return 1;
+}
+
+static int pkey_rsa_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *siglen,
+                         const uint8_t *tbs, size_t tbslen) {
+  int ret;
+  RSA_PKEY_CTX *rctx = ctx->data;
+  RSA *rsa = ctx->pkey->pkey.rsa;
+
+  if (rctx->md) {
+    if (tbslen != EVP_MD_size(rctx->md)) {
+      OPENSSL_PUT_ERROR(EVP, pkey_rsa_sign, EVP_R_INVALID_DIGEST_LENGTH);
+      return -1;
+    }
+
+    if (EVP_MD_type(rctx->md) == NID_mdc2) {
+      OPENSSL_PUT_ERROR(EVP, pkey_rsa_sign, EVP_R_NO_MDC2_SUPPORT);
+      ret = -1;
+    } else if (rctx->pad_mode == RSA_PKCS1_PADDING) {
+      unsigned int sltmp;
+      ret = RSA_sign(EVP_MD_type(rctx->md), tbs, tbslen, sig, &sltmp, rsa);
+      if (ret <= 0) {
+        return ret;
+      }
+      ret = sltmp;
+    } else if (rctx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+      if (!setup_tbuf(rctx, ctx) ||
+          !RSA_padding_add_PKCS1_PSS_mgf1(rsa, rctx->tbuf, tbs, rctx->md,
+                                          rctx->mgf1md, rctx->saltlen)) {
+        return -1;
+      }
+      /* TODO(fork): don't use private_encrypt. */
+      ret = RSA_private_encrypt(RSA_size(rsa), rctx->tbuf, sig, rsa,
+                                RSA_NO_PADDING);
+    } else {
+      return -1;
+    }
+  } else {
+    /* TODO(fork): don't use private_encrypt. */
+    ret = RSA_private_encrypt(tbslen, tbs, sig, ctx->pkey->pkey.rsa,
+                              rctx->pad_mode);
+  }
+
+  if (ret < 0) {
+    return ret;
+  }
+  *siglen = ret;
+
+  return 1;
+}
+
+static int pkey_rsa_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig,
+                           size_t siglen, const uint8_t *tbs,
+                           size_t tbslen) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  RSA *rsa = ctx->pkey->pkey.rsa;
+  size_t rslen;
+
+  if (rctx->md) {
+    if (rctx->pad_mode == RSA_PKCS1_PADDING) {
+      return RSA_verify(EVP_MD_type(rctx->md), tbs, tbslen, sig, siglen, rsa);
+    }
+
+    if (rctx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+      int ret;
+      if (!setup_tbuf(rctx, ctx)) {
+        return -1;
+      }
+      /* TODO(fork): don't use public_decrypt. */
+      ret = RSA_public_decrypt(siglen, sig, rctx->tbuf, rsa, RSA_NO_PADDING);
+      if (ret <= 0) {
+        return 0;
+      }
+      ret = RSA_verify_PKCS1_PSS_mgf1(rsa, tbs, rctx->md, rctx->mgf1md,
+                                      rctx->tbuf, rctx->saltlen);
+      if (ret <= 0) {
+        return 0;
+      }
+      return 1;
+    } else {
+      return -1;
+    }
+  } else {
+    if (!setup_tbuf(rctx, ctx)) {
+      return -1;
+    }
+    rslen = RSA_public_decrypt(siglen, sig, rctx->tbuf, rsa, rctx->pad_mode);
+    if (rslen == 0) {
+      return 0;
+    }
+  }
+
+  if (rslen != tbslen || CRYPTO_memcmp(tbs, rctx->tbuf, rslen) != 0) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int pkey_rsa_encrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *outlen,
+                            const uint8_t *in, size_t inlen) {
+  int ret;
+  RSA_PKEY_CTX *rctx = ctx->data;
+
+  if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+    int klen = RSA_size(ctx->pkey->pkey.rsa);
+    if (!setup_tbuf(rctx, ctx)) {
+      return -1;
+    }
+    if (!RSA_padding_add_PKCS1_OAEP_mgf1(rctx->tbuf, klen, in, inlen,
+                                         rctx->oaep_label, rctx->oaep_labellen,
+                                         rctx->md, rctx->mgf1md)) {
+      return -1;
+    }
+    ret = RSA_public_encrypt(klen, rctx->tbuf, out, ctx->pkey->pkey.rsa,
+                             RSA_NO_PADDING);
+  } else {
+    ret =
+        RSA_public_encrypt(inlen, in, out, ctx->pkey->pkey.rsa, rctx->pad_mode);
+  }
+
+  if (ret < 0) {
+    return ret;
+  }
+  *outlen = ret;
+
+  return 1;
+}
+
+static int pkey_rsa_decrypt(EVP_PKEY_CTX *ctx, uint8_t *out,
+                            size_t *outlen, const uint8_t *in,
+                            size_t inlen) {
+  int ret;
+  RSA_PKEY_CTX *rctx = ctx->data;
+
+  if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+    int i;
+    if (!setup_tbuf(rctx, ctx)) {
+      return -1;
+    }
+    ret = RSA_private_decrypt(inlen, in, rctx->tbuf, ctx->pkey->pkey.rsa,
+                              RSA_NO_PADDING);
+    if (ret <= 0) {
+      return ret;
+    }
+    for (i = 0; i < ret; i++) {
+      if (rctx->tbuf[i]) {
+        break;
+      }
+    }
+    ret = RSA_padding_check_PKCS1_OAEP_mgf1(
+        out, ret, rctx->tbuf + i, ret - i, ret, rctx->oaep_label,
+        rctx->oaep_labellen, rctx->md, rctx->mgf1md);
+  } else {
+    ret = RSA_private_decrypt(inlen, in, out, ctx->pkey->pkey.rsa,
+                              rctx->pad_mode);
+  }
+
+  if (ret < 0) {
+    return ret;
+  }
+  *outlen = ret;
+
+  return 1;
+}
+
+static int check_padding_md(const EVP_MD *md, int padding) {
+  if (!md) {
+    return 1;
+  }
+
+  if (padding == RSA_NO_PADDING) {
+    OPENSSL_PUT_ERROR(EVP, check_padding_md, EVP_R_INVALID_PADDING_MODE);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int is_known_padding(int padding_mode) {
+  switch (padding_mode) {
+    case RSA_PKCS1_PADDING:
+    case RSA_SSLV23_PADDING:
+    case RSA_NO_PADDING:
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+static int pkey_rsa_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  switch (type) {
+    case EVP_PKEY_CTRL_RSA_PADDING:
+      if (!is_known_padding(p1) || !check_padding_md(rctx->md, p1) ||
+          (p1 == RSA_PKCS1_PSS_PADDING &&
+           0 == (ctx->operation & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY))) ||
+          (p1 == RSA_PKCS1_OAEP_PADDING &&
+           0 == (ctx->operation & EVP_PKEY_OP_TYPE_CRYPT))) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl,
+                          EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE);
+        return -2;
+      }
+      if ((p1 == RSA_PKCS1_PSS_PADDING || p1 == RSA_PKCS1_OAEP_PADDING) &&
+          rctx->md == NULL) {
+        rctx->md = EVP_sha1();
+      }
+      rctx->pad_mode = p1;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_RSA_PADDING:
+      *(int *)p2 = rctx->pad_mode;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_PSS_SALTLEN:
+    case EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN:
+      if (rctx->pad_mode != RSA_PKCS1_PSS_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PSS_SALTLEN);
+        return -2;
+      }
+      if (type == EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN) {
+        *(int *)p2 = rctx->saltlen;
+      } else {
+        if (p1 < -2) {
+          return -2;
+        }
+        rctx->saltlen = p1;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_KEYGEN_BITS:
+      if (p1 < 256) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_KEYBITS);
+        return -2;
+      }
+      rctx->nbits = p1;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP:
+      if (!p2) {
+        return -2;
+      }
+      BN_free(rctx->pub_exp);
+      rctx->pub_exp = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_OAEP_MD:
+    case EVP_PKEY_CTRL_GET_RSA_OAEP_MD:
+      if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PADDING_MODE);
+        return -2;
+      }
+      if (type == EVP_PKEY_CTRL_GET_RSA_OAEP_MD) {
+        *(const EVP_MD **)p2 = rctx->md;
+      } else {
+        rctx->md = p2;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_MD:
+      if (!check_padding_md(p2, rctx->pad_mode)) {
+        return 0;
+      }
+      rctx->md = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+      *(const EVP_MD **)p2 = rctx->md;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_MGF1_MD:
+    case EVP_PKEY_CTRL_GET_RSA_MGF1_MD:
+      if (rctx->pad_mode != RSA_PKCS1_PSS_PADDING &&
+          rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_MGF1_MD);
+        return -2;
+      }
+      if (type == EVP_PKEY_CTRL_GET_RSA_MGF1_MD) {
+        if (rctx->mgf1md) {
+          *(const EVP_MD **)p2 = rctx->mgf1md;
+        } else {
+          *(const EVP_MD **)p2 = rctx->md;
+        }
+      } else {
+        rctx->mgf1md = p2;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_OAEP_LABEL:
+      if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PADDING_MODE);
+        return -2;
+      }
+      if (rctx->oaep_label) {
+        OPENSSL_free(rctx->oaep_label);
+      }
+      if (p2 && p1 > 0) {
+        /* TODO(fork): this seems wrong. Shouldn't it take a copy of the
+         * buffer? */
+        rctx->oaep_label = p2;
+        rctx->oaep_labellen = p1;
+      } else {
+        rctx->oaep_label = NULL;
+        rctx->oaep_labellen = 0;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL:
+      if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PADDING_MODE);
+        return -2;
+      }
+      *(uint8_t **)p2 = rctx->oaep_label;
+      return rctx->oaep_labellen;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+      return 1;
+
+    default:
+      return -2;
+  }
+}
+
+static int pkey_rsa_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  RSA *rsa = NULL;
+  RSA_PKEY_CTX *rctx = ctx->data;
+
+  int ret;
+  if (!rctx->pub_exp) {
+    rctx->pub_exp = BN_new();
+    if (!rctx->pub_exp || !BN_set_word(rctx->pub_exp, RSA_F4))
+      return 0;
+  }
+  rsa = RSA_new();
+  if (!rsa) {
+    return 0;
+  }
+
+  ret = RSA_generate_key_ex(rsa, rctx->nbits, rctx->pub_exp, NULL);
+  if (ret > 0) {
+    EVP_PKEY_assign_RSA(pkey, rsa);
+  } else {
+    RSA_free(rsa);
+  }
+  return ret;
+}
+
+const EVP_PKEY_METHOD rsa_pkey_meth = {
+    EVP_PKEY_RSA,        EVP_PKEY_FLAG_AUTOARGLEN, pkey_rsa_init,
+    pkey_rsa_copy,       pkey_rsa_cleanup,         0 /* paramgen_init */,
+    0 /* paramgen */,    0 /* keygen_init */,      pkey_rsa_keygen,
+    0 /* sign_init */,   pkey_rsa_sign,            0 /* verify_init */,
+    pkey_rsa_verify,     0,                        0,
+    0,                   0,                        0 /* encrypt_init */,
+    pkey_rsa_encrypt,    0 /* decrypt_init */,     pkey_rsa_decrypt,
+    0 /* derive_init */, 0 /* derive */,           pkey_rsa_ctrl,
+};
+
+int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int padding) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING,
+                           padding, NULL);
+}
+
+int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *out_padding) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_GET_RSA_PADDING,
+                           0, out_padding);
+}
+
+int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int salt_len) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY),
+                           EVP_PKEY_CTRL_RSA_PSS_SALTLEN, salt_len, NULL);
+}
+
+int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *out_salt_len) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY),
+                           EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN, 0, out_salt_len);
+}
+
+int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
+                           EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL);
+}
+
+int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *e) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
+                           EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP, 0, e);
+}
+
+int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void *)md);
+}
+
+int EVP_PKEY_CTX_get_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_GET_RSA_OAEP_MD, 0, (void*) out_md);
+}
+
+int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void*) md);
+}
+
+int EVP_PKEY_CTX_get_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_GET_RSA_MGF1_MD, 0, (void*) out_md);
+}
+
+int EVP_PKEY_CTX_set0_rsa_oaep_label(EVP_PKEY_CTX *ctx, const uint8_t *label,
+                                     size_t label_len) {
+  int label_len_int = label_len;
+  if (((size_t) label_len_int) != label_len) {
+    return -2;
+  }
+
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_RSA_OAEP_LABEL, label_len,
+                           (void *)label);
+}
+
+int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx,
+                                     const uint8_t **out_label) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL, 0, (void *) out_label);
+}
diff --git a/crypto/evp/p_rsa_asn1.c b/crypto/evp/p_rsa_asn1.c
new file mode 100644
index 0000000..6b94ed7
--- /dev/null
+++ b/crypto/evp/p_rsa_asn1.c
@@ -0,0 +1,751 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include "../rsa/internal.h"
+#include "internal.h"
+
+
+static int rsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
+  uint8_t *encoded = NULL;
+  int len;
+  len = i2d_RSAPublicKey(pkey->pkey.rsa, &encoded);
+
+  if (len <= 0) {
+    return 0;
+  }
+
+  if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_RSA), V_ASN1_NULL, NULL,
+                              encoded, len)) {
+    OPENSSL_free(encoded);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int rsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
+  const uint8_t *p;
+  int pklen;
+  RSA *rsa;
+
+  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, NULL, pubkey)) {
+    return 0;
+  }
+  rsa = d2i_RSAPublicKey(NULL, &p, pklen);
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pub_decode, ERR_R_RSA_LIB);
+    return 0;
+  }
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+static int rsa_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+  return BN_cmp(b->pkey.rsa->n, a->pkey.rsa->n) == 0 &&
+         BN_cmp(b->pkey.rsa->e, a->pkey.rsa->e) == 0;
+}
+
+static int rsa_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey) {
+  uint8_t *rk = NULL;
+  int rklen;
+
+  rklen = i2d_RSAPrivateKey(pkey->pkey.rsa, &rk);
+
+  if (rklen <= 0) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_encode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  /* TODO(fork): const correctness in next line. */
+  if (!PKCS8_pkey_set0(p8, (ASN1_OBJECT *)OBJ_nid2obj(NID_rsaEncryption), 0,
+                       V_ASN1_NULL, NULL, rk, rklen)) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_encode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int rsa_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) {
+  const uint8_t *p;
+  int pklen;
+  RSA *rsa;
+
+  if (!PKCS8_pkey_get0(NULL, &p, &pklen, NULL, p8)) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_decode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  rsa = d2i_RSAPrivateKey(NULL, &p, pklen);
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_decode, ERR_R_RSA_LIB);
+    return 0;
+  }
+
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+static int int_rsa_size(const EVP_PKEY *pkey) {
+  return RSA_size(pkey->pkey.rsa);
+}
+
+static int rsa_bits(const EVP_PKEY *pkey) {
+  return BN_num_bits(pkey->pkey.rsa->n);
+}
+
+static void int_rsa_free(EVP_PKEY *pkey) { RSA_free(pkey->pkey.rsa); }
+
+static void update_buflen(const BIGNUM *b, size_t *pbuflen) {
+  size_t i;
+
+  if (!b) {
+    return;
+  }
+
+  i = BN_num_bytes(b);
+  if (*pbuflen < i) {
+    *pbuflen = i;
+  }
+}
+
+static int do_rsa_print(BIO *out, const RSA *rsa, int off,
+                        int include_private) {
+  char *str;
+  const char *s;
+  uint8_t *m = NULL;
+  int ret = 0, mod_len = 0;
+  size_t buf_len = 0;
+
+  update_buflen(rsa->n, &buf_len);
+  update_buflen(rsa->e, &buf_len);
+
+  if (include_private) {
+    update_buflen(rsa->d, &buf_len);
+    update_buflen(rsa->p, &buf_len);
+    update_buflen(rsa->q, &buf_len);
+    update_buflen(rsa->dmp1, &buf_len);
+    update_buflen(rsa->dmq1, &buf_len);
+    update_buflen(rsa->iqmp, &buf_len);
+  }
+
+  m = (uint8_t *)OPENSSL_malloc(buf_len + 10);
+  if (m == NULL) {
+    OPENSSL_PUT_ERROR(EVP, do_rsa_print, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (rsa->n != NULL) {
+    mod_len = BN_num_bits(rsa->n);
+  }
+
+  if (!BIO_indent(out, off, 128)) {
+    goto err;
+  }
+
+  if (include_private && rsa->d) {
+    if (BIO_printf(out, "Private-Key: (%d bit)\n", mod_len) <= 0) {
+      goto err;
+    }
+    str = "modulus:";
+    s = "publicExponent:";
+  } else {
+    if (BIO_printf(out, "Public-Key: (%d bit)\n", mod_len) <= 0) {
+      goto err;
+    }
+    str = "Modulus:";
+    s = "Exponent:";
+  }
+  if (!ASN1_bn_print(out, str, rsa->n, m, off) ||
+      !ASN1_bn_print(out, s, rsa->e, m, off)) {
+    goto err;
+  }
+
+  if (include_private) {
+    if (!ASN1_bn_print(out, "privateExponent:", rsa->d, m, off) ||
+        !ASN1_bn_print(out, "prime1:", rsa->p, m, off) ||
+        !ASN1_bn_print(out, "prime2:", rsa->q, m, off) ||
+        !ASN1_bn_print(out, "exponent1:", rsa->dmp1, m, off) ||
+        !ASN1_bn_print(out, "exponent2:", rsa->dmq1, m, off) ||
+        !ASN1_bn_print(out, "coefficient:", rsa->iqmp, m, off)) {
+      goto err;
+    }
+  }
+  ret = 1;
+
+err:
+  if (m != NULL) {
+    OPENSSL_free(m);
+  }
+  return ret;
+}
+
+static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx) {
+  return do_rsa_print(bp, pkey->pkey.rsa, indent, 0);
+}
+
+
+static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx) {
+  return do_rsa_print(bp, pkey->pkey.rsa, indent, 1);
+}
+
+/* Given an MGF1 Algorithm ID decode to an Algorithm Identifier */
+static X509_ALGOR *rsa_mgf1_decode(X509_ALGOR *alg) {
+  const uint8_t *p;
+  int plen;
+
+  if (alg == NULL ||
+      OBJ_obj2nid(alg->algorithm) != NID_mgf1 ||
+      alg->parameter->type != V_ASN1_SEQUENCE) {
+    return NULL;
+  }
+
+  p = alg->parameter->value.sequence->data;
+  plen = alg->parameter->value.sequence->length;
+  return d2i_X509_ALGOR(NULL, &p, plen);
+}
+
+static RSA_PSS_PARAMS *rsa_pss_decode(const X509_ALGOR *alg,
+                                      X509_ALGOR **pmaskHash) {
+  const uint8_t *p;
+  int plen;
+  RSA_PSS_PARAMS *pss;
+
+  *pmaskHash = NULL;
+
+  if (!alg->parameter || alg->parameter->type != V_ASN1_SEQUENCE) {
+    return NULL;
+  }
+  p = alg->parameter->value.sequence->data;
+  plen = alg->parameter->value.sequence->length;
+  pss = d2i_RSA_PSS_PARAMS(NULL, &p, plen);
+
+  if (!pss) {
+    return NULL;
+  }
+
+  *pmaskHash = rsa_mgf1_decode(pss->maskGenAlgorithm);
+
+  return pss;
+}
+
+static int rsa_pss_param_print(BIO *bp, RSA_PSS_PARAMS *pss,
+                               X509_ALGOR *maskHash, int indent) {
+  int rv = 0;
+
+  if (!pss) {
+    if (BIO_puts(bp, " (INVALID PSS PARAMETERS)\n") <= 0) {
+      return 0;
+    }
+    return 1;
+  }
+
+  if (BIO_puts(bp, "\n") <= 0 ||
+      !BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Hash Algorithm: ") <= 0) {
+    goto err;
+  }
+
+  if (pss->hashAlgorithm) {
+    if (i2a_ASN1_OBJECT(bp, pss->hashAlgorithm->algorithm) <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "sha1 (default)") <= 0) {
+    goto err;
+  }
+
+  if (BIO_puts(bp, "\n") <= 0 ||
+      !BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Mask Algorithm: ") <= 0) {
+    goto err;
+  }
+
+  if (pss->maskGenAlgorithm) {
+    if (i2a_ASN1_OBJECT(bp, pss->maskGenAlgorithm->algorithm) <= 0 ||
+        BIO_puts(bp, " with ") <= 0) {
+      goto err;
+    }
+
+    if (maskHash) {
+      if (i2a_ASN1_OBJECT(bp, maskHash->algorithm) <= 0) {
+        goto err;
+      }
+    } else if (BIO_puts(bp, "INVALID") <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "mgf1 with sha1 (default)") <= 0) {
+    goto err;
+  }
+  BIO_puts(bp, "\n");
+
+  if (!BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Salt Length: 0x") <= 0) {
+    goto err;
+  }
+
+  if (pss->saltLength) {
+    if (i2a_ASN1_INTEGER(bp, pss->saltLength) <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "0x14 (default)") <= 0) {
+    goto err;
+  }
+  BIO_puts(bp, "\n");
+
+  if (!BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Trailer Field: 0x") <= 0) {
+    goto err;
+  }
+
+  if (pss->trailerField) {
+    if (i2a_ASN1_INTEGER(bp, pss->trailerField) <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "BC (default)") <= 0) {
+    goto err;
+  }
+  BIO_puts(bp, "\n");
+
+  rv = 1;
+
+err:
+  return rv;
+}
+
+static int rsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
+                         const ASN1_STRING *sig, int indent, ASN1_PCTX *pctx) {
+  if (OBJ_obj2nid(sigalg->algorithm) == NID_rsassaPss) {
+    int rv;
+    RSA_PSS_PARAMS *pss;
+    X509_ALGOR *maskHash;
+
+    pss = rsa_pss_decode(sigalg, &maskHash);
+    rv = rsa_pss_param_print(bp, pss, maskHash, indent);
+    if (pss) {
+      RSA_PSS_PARAMS_free(pss);
+    }
+    if (maskHash) {
+      X509_ALGOR_free(maskHash);
+    }
+    if (!rv) {
+      return 0;
+    }
+  } else if (!sig && BIO_puts(bp, "\n") <= 0) {
+    return 0;
+  }
+
+  if (sig) {
+    return X509_signature_dump(bp, sig, indent);
+  }
+  return 1;
+}
+
+static int rsa_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) {
+  X509_ALGOR *alg = NULL;
+  switch (op) {
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+      *(int *)arg2 = NID_sha1;
+      return 1;
+
+    default:
+      return -2;
+  }
+
+  if (alg) {
+    X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL, 0);
+  }
+
+  return 1;
+}
+
+static int old_rsa_priv_decode(EVP_PKEY *pkey, const unsigned char **pder,
+                               int derlen) {
+  RSA *rsa = d2i_RSAPrivateKey(NULL, pder, derlen);
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(EVP, old_rsa_priv_decode, ERR_R_RSA_LIB);
+    return 0;
+  }
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+static int old_rsa_priv_encode(const EVP_PKEY *pkey, unsigned char **pder) {
+  return i2d_RSAPrivateKey(pkey->pkey.rsa, pder);
+}
+
+/* allocate and set algorithm ID from EVP_MD, default SHA1 */
+static int rsa_md_to_algor(X509_ALGOR **palg, const EVP_MD *md) {
+  if (EVP_MD_type(md) == NID_sha1) {
+    return 1;
+  }
+  *palg = X509_ALGOR_new();
+  if (!*palg) {
+    return 0;
+  }
+  X509_ALGOR_set_md(*palg, md);
+  return 1;
+}
+
+/* Allocate and set MGF1 algorithm ID from EVP_MD */
+static int rsa_md_to_mgf1(X509_ALGOR **palg, const EVP_MD *mgf1md) {
+  X509_ALGOR *algtmp = NULL;
+  ASN1_STRING *stmp = NULL;
+  *palg = NULL;
+
+  if (EVP_MD_type(mgf1md) == NID_sha1) {
+    return 1;
+  }
+  /* need to embed algorithm ID inside another */
+  if (!rsa_md_to_algor(&algtmp, mgf1md) ||
+      !ASN1_item_pack(algtmp, ASN1_ITEM_rptr(X509_ALGOR), &stmp)) {
+    goto err;
+  }
+  *palg = X509_ALGOR_new();
+  if (!*palg) {
+    goto err;
+  }
+  X509_ALGOR_set0(*palg, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, stmp);
+  stmp = NULL;
+
+err:
+  if (stmp)
+    ASN1_STRING_free(stmp);
+  if (algtmp)
+    X509_ALGOR_free(algtmp);
+  if (*palg)
+    return 1;
+
+  return 0;
+}
+
+/* convert algorithm ID to EVP_MD, default SHA1 */
+static const EVP_MD *rsa_algor_to_md(X509_ALGOR *alg) {
+  const EVP_MD *md;
+  if (!alg) {
+    return EVP_sha1();
+  }
+  md = EVP_get_digestbyobj(alg->algorithm);
+  if (md == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_algor_to_md, EVP_R_UNKNOWN_DIGEST);
+  }
+  return md;
+}
+
+/* convert MGF1 algorithm ID to EVP_MD, default SHA1 */
+static const EVP_MD *rsa_mgf1_to_md(X509_ALGOR *alg, X509_ALGOR *maskHash) {
+  const EVP_MD *md;
+  if (!alg) {
+    return EVP_sha1();
+  }
+  /* Check mask and lookup mask hash algorithm */
+  if (OBJ_obj2nid(alg->algorithm) != NID_mgf1) {
+    OPENSSL_PUT_ERROR(EVP, rsa_mgf1_to_md, EVP_R_UNSUPPORTED_MASK_ALGORITHM);
+    return NULL;
+  }
+  if (!maskHash) {
+    OPENSSL_PUT_ERROR(EVP, rsa_mgf1_to_md, EVP_R_UNSUPPORTED_MASK_PARAMETER);
+    return NULL;
+  }
+  md = EVP_get_digestbyobj(maskHash->algorithm);
+  if (md == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_mgf1_to_md, EVP_R_UNKNOWN_MASK_DIGEST);
+    return NULL;
+  }
+  return md;
+}
+
+/* rsa_ctx_to_pss converts EVP_PKEY_CTX in PSS mode into corresponding
+ * algorithm parameter, suitable for setting as an AlgorithmIdentifier. */
+static ASN1_STRING *rsa_ctx_to_pss(EVP_PKEY_CTX *pkctx) {
+  const EVP_MD *sigmd, *mgf1md;
+  RSA_PSS_PARAMS *pss = NULL;
+  ASN1_STRING *os = NULL;
+  EVP_PKEY *pk = EVP_PKEY_CTX_get0_pkey(pkctx);
+  int saltlen, rv = 0;
+
+  if (EVP_PKEY_CTX_get_signature_md(pkctx, &sigmd) <= 0 ||
+      EVP_PKEY_CTX_get_rsa_mgf1_md(pkctx, &mgf1md) <= 0 ||
+      !EVP_PKEY_CTX_get_rsa_pss_saltlen(pkctx, &saltlen)) {
+    goto err;
+  }
+
+  if (saltlen == -1) {
+    saltlen = EVP_MD_size(sigmd);
+  } else if (saltlen == -2) {
+    saltlen = EVP_PKEY_size(pk) - EVP_MD_size(sigmd) - 2;
+    if (((EVP_PKEY_bits(pk) - 1) & 0x7) == 0) {
+      saltlen--;
+    }
+  } else {
+    goto err;
+  }
+
+  pss = RSA_PSS_PARAMS_new();
+  if (!pss) {
+    goto err;
+  }
+
+  if (saltlen != 20) {
+    pss->saltLength = ASN1_INTEGER_new();
+    if (!pss->saltLength ||
+        !ASN1_INTEGER_set(pss->saltLength, saltlen)) {
+      goto err;
+    }
+  }
+
+  if (!rsa_md_to_algor(&pss->hashAlgorithm, sigmd) ||
+      !rsa_md_to_mgf1(&pss->maskGenAlgorithm, mgf1md)) {
+    goto err;
+  }
+
+  /* Finally create string with pss parameter encoding. */
+  if (!ASN1_item_pack(pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), &os)) {
+    goto err;
+  }
+  rv = 1;
+
+err:
+  if (pss)
+    RSA_PSS_PARAMS_free(pss);
+  if (rv)
+    return os;
+  if (os)
+    ASN1_STRING_free(os);
+  return NULL;
+}
+
+/* From PSS AlgorithmIdentifier set public key parameters. If pkey
+ * isn't NULL then the EVP_MD_CTX is setup and initalised. If it
+ * is NULL parameters are passed to pkctx instead. */
+static int rsa_pss_to_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pkctx,
+                          X509_ALGOR *sigalg, EVP_PKEY *pkey) {
+  int ret = -1;
+  int saltlen;
+  const EVP_MD *mgf1md = NULL, *md = NULL;
+  RSA_PSS_PARAMS *pss;
+  X509_ALGOR *maskHash;
+
+  /* Sanity check: make sure it is PSS */
+  if (OBJ_obj2nid(sigalg->algorithm) != NID_rsassaPss) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_UNSUPPORTED_SIGNATURE_TYPE);
+    return -1;
+  }
+  /* Decode PSS parameters */
+  pss = rsa_pss_decode(sigalg, &maskHash);
+  if (pss == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_INVALID_PSS_PARAMETERS);
+    goto err;
+  }
+
+  mgf1md = rsa_mgf1_to_md(pss->maskGenAlgorithm, maskHash);
+  if (!mgf1md) {
+    goto err;
+  }
+  md = rsa_algor_to_md(pss->hashAlgorithm);
+  if (!md) {
+    goto err;
+  }
+
+  saltlen = 20;
+  if (pss->saltLength) {
+    saltlen = ASN1_INTEGER_get(pss->saltLength);
+
+    /* Could perform more salt length sanity checks but the main
+     * RSA routines will trap other invalid values anyway. */
+    if (saltlen < 0) {
+      OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_INVALID_SALT_LENGTH);
+      goto err;
+    }
+  }
+
+  /* low-level routines support only trailer field 0xbc (value 1)
+   * and PKCS#1 says we should reject any other value anyway. */
+  if (pss->trailerField && ASN1_INTEGER_get(pss->trailerField) != 1) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_INVALID_TRAILER);
+    goto err;
+  }
+
+  if (pkey) {
+    if (!EVP_DigestVerifyInit(ctx, &pkctx, md, NULL, pkey)) {
+      goto err;
+    }
+  } else {
+    const EVP_MD *checkmd;
+    if (EVP_PKEY_CTX_get_signature_md(pkctx, &checkmd) <= 0) {
+      goto err;
+    }
+    if (EVP_MD_type(md) != EVP_MD_type(checkmd)) {
+      OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_DIGEST_DOES_NOT_MATCH);
+      goto err;
+    }
+  }
+
+  if (EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING) <= 0 ||
+      EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, saltlen) <= 0 ||
+      EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, mgf1md) <= 0) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  RSA_PSS_PARAMS_free(pss);
+  if (maskHash) {
+    X509_ALGOR_free(maskHash);
+  }
+  return ret;
+}
+
+/* Customised RSA item verification routine. This is called
+ * when a signature is encountered requiring special handling. We
+ * currently only handle PSS. */
+static int rsa_item_verify(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                           X509_ALGOR *sigalg, ASN1_BIT_STRING *sig,
+                           EVP_PKEY *pkey) {
+  /* Sanity check: make sure it is PSS */
+  if (OBJ_obj2nid(sigalg->algorithm) != NID_rsassaPss) {
+    OPENSSL_PUT_ERROR(EVP, rsa_item_verify, EVP_R_UNSUPPORTED_SIGNATURE_TYPE);
+    return -1;
+  }
+  if (rsa_pss_to_ctx(ctx, NULL, sigalg, pkey)) {
+    /* Carry on */
+    return 2;
+  }
+
+  return -1;
+}
+
+static int rsa_item_sign(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                         X509_ALGOR *alg1, X509_ALGOR *alg2,
+                         ASN1_BIT_STRING *sig) {
+  int pad_mode;
+  EVP_PKEY_CTX *pkctx = ctx->pctx;
+  if (EVP_PKEY_CTX_get_rsa_padding(pkctx, &pad_mode) <= 0) {
+    return 0;
+  }
+  if (pad_mode == RSA_PKCS1_PADDING) {
+    return 2;
+  }
+  if (pad_mode == RSA_PKCS1_PSS_PADDING) {
+    ASN1_STRING *os1 = rsa_ctx_to_pss(pkctx);
+    if (!os1) {
+      return 0;
+    }
+    /* Duplicate parameters if we have to */
+    if (alg2) {
+      ASN1_STRING *os2 = ASN1_STRING_dup(os1);
+      if (!os2) {
+        ASN1_STRING_free(os1);
+        return 0;
+      }
+      X509_ALGOR_set0(alg2, OBJ_nid2obj(NID_rsassaPss), V_ASN1_SEQUENCE, os2);
+    }
+    X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_rsassaPss), V_ASN1_SEQUENCE, os1);
+    return 3;
+  }
+  return 2;
+}
+
+const EVP_PKEY_ASN1_METHOD rsa_asn1_meth = {
+  EVP_PKEY_RSA,
+  EVP_PKEY_RSA,
+  ASN1_PKEY_SIGPARAM_NULL,
+
+  "RSA",
+  "OpenSSL RSA method",
+
+  rsa_pub_decode,
+  rsa_pub_encode,
+  rsa_pub_cmp,
+  rsa_pub_print,
+
+  rsa_priv_decode,
+  rsa_priv_encode,
+  rsa_priv_print,
+
+  int_rsa_size,
+  rsa_bits,
+
+  0,0,0,0,0,0,
+
+  rsa_sig_print,
+  int_rsa_free,
+  rsa_pkey_ctrl,
+
+  old_rsa_priv_decode,
+  old_rsa_priv_encode,
+
+  rsa_item_verify,
+  rsa_item_sign,
+};
+
+const EVP_PKEY_ASN1_METHOD rsa_asn1_meth_2 = {
+  EVP_PKEY_RSA2,
+  EVP_PKEY_RSA,
+  ASN1_PKEY_ALIAS,
+};
diff --git a/crypto/evp/pbkdf.c b/crypto/evp/pbkdf.c
new file mode 100644
index 0000000..4ab10f4
--- /dev/null
+++ b/crypto/evp/pbkdf.c
@@ -0,0 +1,140 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/hmac.h>
+
+
+int PKCS5_PBKDF2_HMAC(const char *password, int password_len,
+                      const uint8_t *salt, size_t salt_len, unsigned iterations,
+                      const EVP_MD *digest, size_t key_len, uint8_t *out_key) {
+  uint8_t digest_tmp[EVP_MAX_MD_SIZE], *p, itmp[4];
+  size_t cplen, mdlen, tkeylen, k;
+  unsigned j;
+  uint32_t i = 1;
+  HMAC_CTX hctx_tpl, hctx;
+
+  mdlen = EVP_MD_size(digest);
+  HMAC_CTX_init(&hctx_tpl);
+  p = out_key;
+  tkeylen = key_len;
+  if (password == NULL) {
+    password_len = 0;
+  } else if (password_len == -1) {
+    /* TODO(fork): remove this hack. The OpenSSL code took the strlen when the
+     * length is given as -1. */
+    password_len = strlen(password);
+  }
+  if (!HMAC_Init_ex(&hctx_tpl, password, password_len, digest, NULL)) {
+    HMAC_CTX_cleanup(&hctx_tpl);
+    return 0;
+  }
+  while (tkeylen) {
+    if (tkeylen > mdlen) {
+      cplen = mdlen;
+    } else {
+      cplen = tkeylen;
+    }
+    /* We are unlikely to ever use more than 256 blocks (5120 bits!)
+     * but just in case... */
+    itmp[0] = (uint8_t)((i >> 24) & 0xff);
+    itmp[1] = (uint8_t)((i >> 16) & 0xff);
+    itmp[2] = (uint8_t)((i >> 8) & 0xff);
+    itmp[3] = (uint8_t)(i & 0xff);
+    if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
+      HMAC_CTX_cleanup(&hctx_tpl);
+      return 0;
+    }
+    if (!HMAC_Update(&hctx, salt, salt_len) ||
+        !HMAC_Update(&hctx, itmp, 4) ||
+        !HMAC_Final(&hctx, digest_tmp, NULL)) {
+      HMAC_CTX_cleanup(&hctx_tpl);
+      HMAC_CTX_cleanup(&hctx);
+      return 0;
+    }
+    HMAC_CTX_cleanup(&hctx);
+    memcpy(p, digest_tmp, cplen);
+    for (j = 1; j < iterations; j++) {
+      if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
+        HMAC_CTX_cleanup(&hctx_tpl);
+        return 0;
+      }
+      if (!HMAC_Update(&hctx, digest_tmp, mdlen) ||
+          !HMAC_Final(&hctx, digest_tmp, NULL)) {
+        HMAC_CTX_cleanup(&hctx_tpl);
+        HMAC_CTX_cleanup(&hctx);
+        return 0;
+      }
+      HMAC_CTX_cleanup(&hctx);
+      for (k = 0; k < cplen; k++) {
+        p[k] ^= digest_tmp[k];
+      }
+    }
+    tkeylen -= cplen;
+    i++;
+    p += cplen;
+  }
+  HMAC_CTX_cleanup(&hctx_tpl);
+  return 1;
+}
+
+int PKCS5_PBKDF2_HMAC_SHA1(const char *password, int password_len,
+                           const uint8_t *salt, size_t salt_len,
+                           unsigned iterations, size_t key_len,
+                           uint8_t *out_key) {
+  return PKCS5_PBKDF2_HMAC(password, password_len, salt, salt_len, iterations,
+                           EVP_sha1(), key_len, out_key);
+}
diff --git a/crypto/evp/sign.c b/crypto/evp/sign.c
new file mode 100644
index 0000000..c32e5ce
--- /dev/null
+++ b/crypto/evp/sign.c
@@ -0,0 +1,162 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <openssl/digest.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+
+
+int EVP_SignInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) {
+  return EVP_DigestInit_ex(ctx, type, impl);
+}
+
+int EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type) {
+  return EVP_DigestInit(ctx, type);
+}
+
+int EVP_SignUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_SignFinal(const EVP_MD_CTX *ctx, uint8_t *sig,
+                  unsigned int *out_sig_len, EVP_PKEY *pkey) {
+  uint8_t m[EVP_MAX_MD_SIZE];
+  unsigned int m_len;
+  int ret = 0;
+  EVP_MD_CTX tmp_ctx;
+  EVP_PKEY_CTX *pkctx = NULL;
+  size_t sig_len = EVP_PKEY_size(pkey);
+
+  *out_sig_len = 0;
+  EVP_MD_CTX_init(&tmp_ctx);
+  if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx) ||
+      !EVP_DigestFinal_ex(&tmp_ctx, m, &m_len)) {
+    goto out;
+  }
+  EVP_MD_CTX_cleanup(&tmp_ctx);
+
+/* TODO(fork): this used to be used only with SHA-family hashes. Now we've
+ * removed the flag completely. Why was it added for just those hashes? */
+#if 0
+    if (ctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) {
+#endif
+
+  pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+  if (!pkctx || EVP_PKEY_sign_init(pkctx) <= 0 ||
+      EVP_PKEY_CTX_set_signature_md(pkctx, ctx->digest) <= 0 ||
+      EVP_PKEY_sign(pkctx, sig, &sig_len, m, m_len) <= 0) {
+    goto out;
+  }
+  *out_sig_len = sig_len;
+  ret = 1;
+
+out:
+  if (pkctx) {
+    EVP_PKEY_CTX_free(pkctx);
+  }
+
+  return ret;
+}
+
+int EVP_VerifyInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) {
+  return EVP_DigestInit_ex(ctx, type, impl);
+}
+
+int EVP_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *type) {
+  return EVP_DigestInit(ctx, type);
+}
+
+int EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_VerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len,
+                    EVP_PKEY *pkey) {
+  uint8_t m[EVP_MAX_MD_SIZE];
+  unsigned int m_len;
+  int ret = 0;
+  EVP_MD_CTX tmp_ctx;
+  EVP_PKEY_CTX *pkctx = NULL;
+
+  EVP_MD_CTX_init(&tmp_ctx);
+  if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx) ||
+      !EVP_DigestFinal_ex(&tmp_ctx, m, &m_len)) {
+    EVP_MD_CTX_cleanup(&tmp_ctx);
+    goto out;
+  }
+  EVP_MD_CTX_cleanup(&tmp_ctx);
+
+/* TODO(fork): this used to be used only with SHA-family hashes. Now we've
+ * removed the flag completely. Why was it added for just those hashes? */
+#if 0
+    if (ctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) {
+#endif
+  pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+  if (!pkctx ||
+      EVP_PKEY_verify_init(pkctx) <= 0 ||
+      EVP_PKEY_CTX_set_signature_md(pkctx, ctx->digest) <= 0) {
+    goto out;
+  }
+  ret = EVP_PKEY_verify(pkctx, sig, sig_len, m, m_len);
+
+out:
+  EVP_PKEY_CTX_free(pkctx);
+  return ret;
+}
+
diff --git a/crypto/ex_data.c b/crypto/ex_data.c
new file mode 100644
index 0000000..00b8ff3
--- /dev/null
+++ b/crypto/ex_data.c
@@ -0,0 +1,227 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ex_data.h>
+
+#include <openssl/err.h>
+#include <openssl/thread.h>
+
+#include "crypto_error.h"
+#include "internal.h"
+
+
+/* global_impl is the implementation that we use at runtime. */
+static const CRYPTO_EX_DATA_IMPL *global_impl = NULL;
+
+/* ex_data_default_impl is a the default implementation, defined in
+ * ex_data_impl.c. */
+extern const CRYPTO_EX_DATA_IMPL ex_data_default_impl;
+
+/* get_impl returns the current ex_data implementatation. */
+static const CRYPTO_EX_DATA_IMPL *get_impl() {
+  const CRYPTO_EX_DATA_IMPL *impl;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
+  impl = global_impl;
+  CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
+
+  if (impl != NULL) {
+    return impl;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  if (global_impl == NULL) {
+    global_impl = &ex_data_default_impl;
+  }
+  impl = global_impl;
+  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+  return impl;
+}
+
+int CRYPTO_get_ex_new_index(int class_value, long argl, void *argp,
+                            CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+                            CRYPTO_EX_free *free_func) {
+  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
+  return impl->get_new_index(class_value, argl, argp, new_func, dup_func,
+                             free_func);
+}
+
+int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int index, void *val) {
+  int n, i;
+
+  if (ad->sk == NULL) {
+    ad->sk = sk_void_new_null();
+    if (ad->sk == NULL) {
+      OPENSSL_PUT_ERROR(CRYPTO, CRYPTO_set_ex_data, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+  }
+
+  n = sk_void_num(ad->sk);
+
+  /* Add NULL values until the stack is long enough. */
+  for (i = n; i <= index; i++) {
+    if (!sk_void_push(ad->sk, NULL)) {
+      OPENSSL_PUT_ERROR(CRYPTO, CRYPTO_set_ex_data, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+  }
+
+  sk_void_set(ad->sk, index, val);
+  return 1;
+}
+
+void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx) {
+  if (ad->sk == NULL || idx >= sk_void_num(ad->sk)) {
+    return NULL;
+  }
+  return sk_void_value(ad->sk, idx);
+}
+
+int CRYPTO_ex_data_new_class(void) {
+  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
+  return impl->new_class();
+}
+
+int CRYPTO_new_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
+  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
+  return impl->new_ex_data(class_value, obj, ad);
+}
+
+int CRYPTO_dup_ex_data(int class_value, CRYPTO_EX_DATA *to,
+                       const CRYPTO_EX_DATA *from) {
+  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
+  return impl->dup_ex_data(class_value, to, from);
+}
+
+void CRYPTO_free_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
+  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
+  impl->free_ex_data(class_value, obj, ad);
+}
+
+const CRYPTO_EX_DATA_IMPL *CRYPTO_get_ex_data_implementation(void) {
+  return get_impl();
+}
+
+int CRYPTO_set_ex_data_implementation(const CRYPTO_EX_DATA_IMPL *impl) {
+  int ret = 0;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  if (global_impl == NULL) {
+    ret = 1;
+    global_impl = impl;
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+
+  return ret;
+}
+
+void CRYPTO_cleanup_all_ex_data(void) {
+  const CRYPTO_EX_DATA_IMPL *const impl = get_impl();
+  impl->cleanup();
+}
diff --git a/crypto/ex_data.h b/crypto/ex_data.h
new file mode 100644
index 0000000..9dfe5cf
--- /dev/null
+++ b/crypto/ex_data.h
@@ -0,0 +1,295 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_EX_DATA_H
+#define OPENSSL_HEADER_EX_DATA_H
+
+#include <openssl/base.h>
+
+#include <openssl/stack.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* ex_data is a mechanism for associating arbitrary extra data with objects.
+ * The different types of objects which can have data associated with them are
+ * called "classes" and there are predefined classes for all the OpenSSL
+ * objects that support ex_data.
+ *
+ * Within a given class, different users can be assigned indexes in which to
+ * store their data. Each index has callback functions that are called when a
+ * new object of that type is created, freed and duplicated. */
+
+
+typedef struct crypto_ex_data_st CRYPTO_EX_DATA;
+
+/* CRYPTO_EX_new is the type of a callback function that is called whenever a
+ * new object of a given class is created. For example, if this callback has
+ * been passed to |CRYPTO_get_ex_new_index| with a |class| of
+ * |CRYPTO_EX_INDEX_SSL| then it'll be called each time an SSL* is created.
+ *
+ * The callback is passed the new object (i.e. the SSL*) in |parent|. The
+ * arguments |argl| and |argp| contain opaque values that were given to
+ * |CRYPTO_get_ex_new_index|. The callback should return one on success, but
+ * the value is ignored.
+ *
+ * TODO(fork): the |ptr| argument is always NULL, no? */
+typedef int CRYPTO_EX_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+                          int index, long argl, void *argp);
+
+/* CRYPTO_EX_free is a callback function that is called when an object of the
+ * class is being destroyed. See |CRYPTO_EX_new| for a discussion of the
+ * arguments.
+ *
+ * If |CRYPTO_get_ex_new_index| was called after the creation of objects of the
+ * class that this applies to then, when those those objects are destroyed,
+ * this callback will be called with a NULL value for |ptr|. */
+typedef void CRYPTO_EX_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+                            int index, long argl, void *argp);
+
+/* CRYPTO_EX_dup is a callback function that is called when an object of the
+ * class is being copied and thus the ex_data linked to it also needs to be
+ * copied. On entry, |*from_d| points to the data for this index from the
+ * original object. When the callback returns, |*from_d| will be set as the
+ * data for this index in |to|.
+ *
+ * If |CRYPTO_get_ex_new_index| was called after the creation of objects of the
+ * class that this applies to then, when those those objects are copies, this
+ * callback will be called with a NULL value for |*from_d|. */
+typedef int CRYPTO_EX_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
+                          void **from_d, int index, long argl, void *argp);
+
+/* CRYPTO_get_ex_new_index allocates a new index for ex_data linked with
+ * objects of the given |class|. This should not be called directly, rather
+ * each class of object should provide a wrapper function that sets
+ * |class_value| correctly.
+ *
+ * The |class_value| argument should be one of |CRYPTO_EX_INDEX_*| or a
+ * user-defined class value returned from |CRYPTO_ex_data_new_class|.
+ *
+ * See the descriptions of the callback typedefs for details of when they are
+ * called. Any of the callback arguments may be NULL. The |argl| and |argp|
+ * arguments are opaque values that are passed to the callbacks.
+ *
+ * It returns the new index, or a negative number on error.
+ *
+ * TODO(fork): this should follow the standard calling convention.
+ *
+ * TODO(fork): replace the class_value with a pointer to EX_CLASS_ITEM. Saves
+ * having that hash table and some of the lock-bouncing. Maybe have every
+ * module have a private global EX_CLASS_ITEM somewhere and any direct callers
+ * of CRYPTO_{get,set}_ex_data{,_index} would have to always call the
+ * wrappers. */
+int CRYPTO_get_ex_new_index(int class_value, long argl, void *argp,
+                            CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+                            CRYPTO_EX_free *free_func);
+
+/* CRYPTO_set_ex_data sets an extra data pointer on a given object. This should
+ * not be called directly, rather each class of object should provide a wrapper
+ * function.
+ *
+ * The |index| argument should have been returned from a previous call to
+ * |CRYPTO_get_ex_new_index|. */
+int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int index, void *val);
+
+/* CRYPTO_set_ex_data return an extra data pointer for a given object, or NULL
+ * if no such index exists. This should not be called directly, rather each
+ * class of object should provide a wrapper function.
+ *
+ * The |index| argument should have been returned from a previous call to
+ * |CRYPTO_get_ex_new_index|. */
+void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int index);
+
+/* CRYPTO_EX_INDEX_* are the built-in classes of objects.
+ *
+ * User defined classes start at 100.
+ *
+ * TODO(fork): WARNING: these are called "INDEX", but they aren't! */
+#define CRYPTO_EX_INDEX_BIO 0
+#define CRYPTO_EX_INDEX_SSL 1
+#define CRYPTO_EX_INDEX_SSL_CTX 2
+#define CRYPTO_EX_INDEX_SSL_SESSION 3
+#define CRYPTO_EX_INDEX_X509_STORE 4
+#define CRYPTO_EX_INDEX_X509_STORE_CTX 5
+#define CRYPTO_EX_INDEX_RSA 6
+#define CRYPTO_EX_INDEX_DSA 7
+#define CRYPTO_EX_INDEX_DH 8
+#define CRYPTO_EX_INDEX_ENGINE 9
+#define CRYPTO_EX_INDEX_X509 10
+#define CRYPTO_EX_INDEX_UI 11
+#define CRYPTO_EX_INDEX_EC_KEY 12
+#define CRYPTO_EX_INDEX_EC_GROUP 13
+#define CRYPTO_EX_INDEX_COMP 14
+#define CRYPTO_EX_INDEX_STORE 15
+
+
+/* User-defined classes of objects.
+ *
+ * Core OpenSSL code has predefined class values given above (the
+ * |CRYPTO_EX_INDEX_*| values). It's possible to get dynamic class values
+ * assigned for user-defined objects. */
+
+/* CRYPTO_ex_data_new_class returns a fresh class value for a user-defined type
+ * that wishes to use ex_data.
+ *
+ * TODO(fork): hopefully remove this. */
+int CRYPTO_ex_data_new_class(void);
+
+
+/* Embedding, allocating and freeing |CRYPTO_EX_DATA| structures for objects
+ * that embed them. */
+
+/* CRYPTO_new_ex_data initialises a newly allocated |CRYPTO_EX_DATA| which is
+ * embedded inside of |obj| which is of class |class_value|. Returns one on
+ * success and zero otherwise. */
+int CRYPTO_new_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad);
+
+/* CRYPTO_dup_ex_data duplicates |from| into a freshly allocated
+ * |CRYPTO_EX_DATA|, |to|. Both of which are inside objects of the given
+ * class. It returns one on success and zero otherwise. */
+int CRYPTO_dup_ex_data(int class_value, CRYPTO_EX_DATA *to,
+                       const CRYPTO_EX_DATA *from);
+
+/* CRYPTO_free_ex_data frees |ad|, which is embedded inside |obj|, which is an
+ * object of the given class. */
+void CRYPTO_free_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad);
+
+
+/* Handling different ex_data implementations. */
+
+/* CRYPTO_EX_DATA_IMPL is the opaque type of an implementation of ex_data. */
+typedef struct st_CRYPTO_EX_DATA_IMPL CRYPTO_EX_DATA_IMPL;
+
+/* CRYPTO_get_ex_data_implementation returns the current implementation of
+ * ex_data. */
+const CRYPTO_EX_DATA_IMPL *CRYPTO_get_ex_data_implementation(void);
+
+/* CRYPTO_set_ex_data_implementation sets the implementation of ex_data to use,
+ * unless ex_data has already been used and the default implementation
+ * installed. It returns one on success and zero otherwise. */
+int CRYPTO_set_ex_data_implementation(const CRYPTO_EX_DATA_IMPL *impl);
+
+
+/* Private functions. */
+
+/* CRYPTO_cleanup_all_ex_data cleans up all ex_data state. It assumes that no
+ * other threads are executing code that might call ex_data functions. */
+void CRYPTO_cleanup_all_ex_data(void);
+
+struct crypto_ex_data_st {
+  STACK_OF(void) *sk;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_EX_DATA_H */
diff --git a/crypto/ex_data_impl.c b/crypto/ex_data_impl.c
new file mode 100644
index 0000000..23d1fac
--- /dev/null
+++ b/crypto/ex_data_impl.c
@@ -0,0 +1,400 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/ex_data.h>
+
+#include <assert.h>
+
+#include <openssl/err.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/stack.h>
+#include <openssl/thread.h>
+
+#include "crypto_error.h"
+#include "internal.h"
+
+typedef struct crypto_ex_data_func_st {
+  long argl;  /* Arbitary long */
+  void *argp; /* Arbitary void pointer */
+  CRYPTO_EX_new *new_func;
+  CRYPTO_EX_free *free_func;
+  CRYPTO_EX_dup *dup_func;
+} CRYPTO_EX_DATA_FUNCS;
+
+typedef struct st_ex_class_item {
+  STACK_OF(CRYPTO_EX_DATA_FUNCS) *meth;
+  int class_value;
+  /* TODO(fork): isn't |meth_num| just the length of |meth|? */
+  int meth_num;
+} EX_CLASS_ITEM;
+
+static LHASH_OF(EX_CLASS_ITEM) *global_classes = NULL;
+
+static int global_next_class = 100;
+
+static int new_class(void) {
+  int ret;
+  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  ret = global_next_class++;
+  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+
+  return ret;
+}
+
+/* class_hash is a hash function used by an LHASH of |EX_CLASS_ITEM|
+ * structures. */
+static uint32_t class_hash(const EX_CLASS_ITEM *a) {
+  return a->class_value;
+}
+
+/* class_cmp is a comparision function for an LHASH of |EX_CLASS_ITEM|
+ * structures. */
+static int class_cmp(const EX_CLASS_ITEM *a, const EX_CLASS_ITEM *b) {
+  return a->class_value - b->class_value;
+}
+
+/* data_funcs_free is a callback function from |sk_pop_free| that frees a
+ * |CRYPTO_EX_DATA_FUNCS|. */
+static void data_funcs_free(CRYPTO_EX_DATA_FUNCS *funcs) {
+  OPENSSL_free(funcs);
+}
+
+/* class_free is a callback function from lh_doall to free the EX_CLASS_ITEM
+ * structures. */
+static void class_free(EX_CLASS_ITEM *item) {
+  sk_CRYPTO_EX_DATA_FUNCS_pop_free(item->meth, data_funcs_free);
+}
+
+static LHASH_OF(EX_CLASS_ITEM) *get_classes() {
+  LHASH_OF(EX_CLASS_ITEM) *ret;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
+  ret = global_classes;
+  CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
+
+  if (ret != NULL) {
+    return ret;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  if (global_classes == NULL) {
+    global_classes = lh_EX_CLASS_ITEM_new(class_hash, class_cmp);
+  }
+  ret = global_classes;
+  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+
+  return ret;
+}
+
+static void cleanup(void) {
+  LHASH_OF(EX_CLASS_ITEM) *classes = get_classes();
+
+  lh_EX_CLASS_ITEM_doall(classes, class_free);
+  lh_EX_CLASS_ITEM_free(classes);
+
+  global_classes = NULL;
+}
+
+static EX_CLASS_ITEM *get_class(int class_value) {
+  LHASH_OF(EX_CLASS_ITEM) *const classes = get_classes();
+  EX_CLASS_ITEM template, *class_item;
+  int ok = 0;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  template.class_value = class_value;
+  class_item = lh_EX_CLASS_ITEM_retrieve(classes, &template);
+  if (class_item != NULL) {
+    ok = 1;
+  } else {
+    class_item = OPENSSL_malloc(sizeof(EX_CLASS_ITEM));
+    if (class_item) {
+      class_item->class_value = class_value;
+      class_item->meth_num = 0;
+      class_item->meth = sk_CRYPTO_EX_DATA_FUNCS_new_null();
+      if (class_item->meth != NULL) {
+        EX_CLASS_ITEM *old_data;
+        ok = lh_EX_CLASS_ITEM_insert(classes, &old_data, class_item);
+        assert(old_data == NULL);
+      }
+    }
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+
+  if (!ok) {
+    if (class_item) {
+      if (class_item->meth) {
+        sk_CRYPTO_EX_DATA_FUNCS_free(class_item->meth);
+      }
+      OPENSSL_free(class_item);
+      class_item = NULL;
+    }
+
+    OPENSSL_PUT_ERROR(CRYPTO, get_class, ERR_R_MALLOC_FAILURE);
+  }
+
+  return class_item;
+}
+
+static int get_new_index(int class_value, long argl, void *argp,
+                         CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+                         CRYPTO_EX_free *free_func) {
+  EX_CLASS_ITEM *const item = get_class(class_value);
+  CRYPTO_EX_DATA_FUNCS *funcs;
+  int ret = -1;
+
+  if (!item) {
+    return -1;
+  }
+
+  funcs = OPENSSL_malloc(sizeof(CRYPTO_EX_DATA_FUNCS));
+  if (funcs == NULL) {
+    OPENSSL_PUT_ERROR(CRYPTO, get_new_index, ERR_R_MALLOC_FAILURE);
+    return -1;
+  }
+
+  funcs->argl = argl;
+  funcs->argp = argp;
+  funcs->new_func = new_func;
+  funcs->dup_func = dup_func;
+  funcs->free_func = free_func;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+  /* TODO(fork): this loop appears to only ever run once. */
+  while (sk_CRYPTO_EX_DATA_FUNCS_num(item->meth) <= item->meth_num) {
+    if (!sk_CRYPTO_EX_DATA_FUNCS_push(item->meth, NULL)) {
+      OPENSSL_PUT_ERROR(CRYPTO, get_new_index, ERR_R_MALLOC_FAILURE);
+      OPENSSL_free(funcs);
+      goto err;
+    }
+  }
+
+  ret = item->meth_num++;
+  (void)sk_CRYPTO_EX_DATA_FUNCS_set(item->meth, ret, funcs);
+
+err:
+  CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+  return ret;
+}
+
+/* get_func_pointers takes a copy of the CRYPTO_EX_DATA_FUNCS pointers, if any,
+ * for the given class. If there are some pointers, it sets |*out| to point to
+ * a fresh stack of them. Otherwise it sets |*out| to NULL. It returns one on
+ * success or zero on error. */
+static int get_func_pointers(STACK_OF(CRYPTO_EX_DATA_FUNCS) **out,
+                             int class_value) {
+  EX_CLASS_ITEM *const item = get_class(class_value);
+  size_t n;
+
+  if (!item) {
+    return 0;
+  }
+
+  *out = NULL;
+
+  /* CRYPTO_EX_DATA_FUNCS structures are static once set, so we can take a
+   * shallow copy of the list under lock and then use the structures without
+   * the lock held. */
+  CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
+  n = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
+  if (n > 0) {
+    *out = sk_CRYPTO_EX_DATA_FUNCS_dup(item->meth);
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
+
+  if (n > 0 && *out == NULL) {
+    OPENSSL_PUT_ERROR(CRYPTO, get_func_pointers, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int new_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
+  STACK_OF(CRYPTO_EX_DATA_FUNCS) *func_pointers;
+  size_t i;
+
+  ad->sk = NULL;
+
+  if (!get_func_pointers(&func_pointers, class_value)) {
+    return 0;
+  }
+
+  for (i = 0; i < sk_CRYPTO_EX_DATA_FUNCS_num(func_pointers); i++) {
+    CRYPTO_EX_DATA_FUNCS *func_pointer =
+        sk_CRYPTO_EX_DATA_FUNCS_value(func_pointers, i);
+    if (func_pointer->new_func) {
+      func_pointer->new_func(obj, NULL, ad, i, func_pointer->argl,
+                             func_pointer->argp);
+    }
+  }
+
+  sk_CRYPTO_EX_DATA_FUNCS_free(func_pointers);
+
+  return 1;
+}
+
+static int dup_ex_data(int class_value, CRYPTO_EX_DATA *to,
+                       const CRYPTO_EX_DATA *from) {
+  STACK_OF(CRYPTO_EX_DATA_FUNCS) *func_pointers;
+  size_t i;
+
+  if (!from->sk) {
+    /* In this case, |from| is blank, which is also the initial state of |to|,
+     * so there's nothing to do. */
+    return 1;
+  }
+
+  if (!get_func_pointers(&func_pointers, class_value)) {
+    return 0;
+  }
+
+  for (i = 0; i < sk_CRYPTO_EX_DATA_FUNCS_num(func_pointers); i++) {
+    CRYPTO_EX_DATA_FUNCS *func_pointer =
+        sk_CRYPTO_EX_DATA_FUNCS_value(func_pointers, i);
+    void *ptr = CRYPTO_get_ex_data(from, i);
+    if (func_pointer->dup_func) {
+      func_pointer->dup_func(to, from, &ptr, i, func_pointer->argl,
+                             func_pointer->argp);
+    }
+    CRYPTO_set_ex_data(to, i, ptr);
+  }
+
+  sk_CRYPTO_EX_DATA_FUNCS_free(func_pointers);
+
+  return 1;
+}
+
+static void free_ex_data(int class_value, void *obj, CRYPTO_EX_DATA *ad) {
+  STACK_OF(CRYPTO_EX_DATA_FUNCS) *func_pointers;
+  size_t i;
+
+  if (!get_func_pointers(&func_pointers, class_value)) {
+    return;
+  }
+
+  for (i = 0; i < sk_CRYPTO_EX_DATA_FUNCS_num(func_pointers); i++) {
+    CRYPTO_EX_DATA_FUNCS *func_pointer =
+        sk_CRYPTO_EX_DATA_FUNCS_value(func_pointers, i);
+    if (func_pointer->free_func) {
+      void *ptr = CRYPTO_get_ex_data(ad, i);
+      func_pointer->free_func(obj, ptr, ad, i, func_pointer->argl,
+                              func_pointer->argp);
+    }
+  }
+
+  sk_CRYPTO_EX_DATA_FUNCS_free(func_pointers);
+
+  if (ad->sk) {
+    sk_void_free(ad->sk);
+    ad->sk = NULL;
+  }
+}
+
+const CRYPTO_EX_DATA_IMPL ex_data_default_impl = {
+    new_class, cleanup, get_new_index, new_ex_data, dup_ex_data, free_ex_data};
diff --git a/crypto/header_removed.h b/crypto/header_removed.h
new file mode 100644
index 0000000..1097d8e
--- /dev/null
+++ b/crypto/header_removed.h
@@ -0,0 +1,3 @@
+/* This header is linked to under the names of several headers that have been
+ * removed. It's possible to put a #error in here in order to catch that an
+ * clean up older code. */
diff --git a/crypto/hmac/CMakeLists.txt b/crypto/hmac/CMakeLists.txt
new file mode 100644
index 0000000..d647b0a
--- /dev/null
+++ b/crypto/hmac/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(. .. ../../include)
+
+add_library(
+	hmac
+
+	OBJECT
+
+	hmac.c
+)
+
+
+add_executable(
+	hmac_test
+
+	hmac_test.c
+)
+
+target_link_libraries(hmac_test crypto)
diff --git a/crypto/hmac/hmac.c b/crypto/hmac/hmac.c
new file mode 100644
index 0000000..1d37b9e
--- /dev/null
+++ b/crypto/hmac/hmac.c
@@ -0,0 +1,208 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/hmac.h>
+
+#include <assert.h>
+
+#include <openssl/mem.h>
+
+
+uint8_t *HMAC(const EVP_MD *evp_md, const void *key, size_t key_len,
+              const uint8_t *data, size_t data_len, uint8_t *out,
+              unsigned int *out_len) {
+  HMAC_CTX ctx;
+  static uint8_t static_out_buffer[EVP_MAX_MD_SIZE];
+
+  /* OpenSSL has traditionally supported using a static buffer if |out| is
+   * NULL. We maintain that but don't document it. This behaviour should be
+   * considered to be deprecated. */
+  if (out == NULL) {
+    out = static_out_buffer;
+  }
+
+  HMAC_CTX_init(&ctx);
+  if (!HMAC_Init(&ctx, key, key_len, evp_md) ||
+      !HMAC_Update(&ctx, data, data_len) ||
+      !HMAC_Final(&ctx, out, out_len)) {
+    out = NULL;
+  }
+
+  HMAC_CTX_cleanup(&ctx);
+  return out;
+}
+
+void HMAC_CTX_init(HMAC_CTX *ctx) {
+  EVP_MD_CTX_init(&ctx->i_ctx);
+  EVP_MD_CTX_init(&ctx->o_ctx);
+  EVP_MD_CTX_init(&ctx->md_ctx);
+}
+
+void HMAC_CTX_cleanup(HMAC_CTX *ctx) {
+  EVP_MD_CTX_cleanup(&ctx->i_ctx);
+  EVP_MD_CTX_cleanup(&ctx->o_ctx);
+  EVP_MD_CTX_cleanup(&ctx->md_ctx);
+  OPENSSL_cleanse(ctx, sizeof(ctx));
+}
+
+int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
+                 const EVP_MD *md, ENGINE *impl) {
+  unsigned i, reset = 0;
+  uint8_t pad[HMAC_MAX_MD_CBLOCK];
+
+  if (md != NULL) {
+    reset = 1;
+    ctx->md = md;
+  } else {
+    md = ctx->md;
+  }
+
+  if (key != NULL) {
+    size_t block_size = EVP_MD_block_size(md);
+    reset = 1;
+    assert(block_size <= sizeof(ctx->key));
+    if (block_size < key_len) {
+      if (!EVP_DigestInit_ex(&ctx->md_ctx, md, impl) ||
+          !EVP_DigestUpdate(&ctx->md_ctx, key, key_len) ||
+          !EVP_DigestFinal_ex(&(ctx->md_ctx), ctx->key, &ctx->key_length)) {
+        goto err;
+      }
+    } else {
+      assert(key_len >= 0 && key_len <= sizeof(ctx->key));
+      memcpy(ctx->key, key, key_len);
+      ctx->key_length = key_len;
+    }
+    if (ctx->key_length != HMAC_MAX_MD_CBLOCK) {
+      memset(&ctx->key[ctx->key_length], 0, sizeof(ctx->key) - ctx->key_length);
+    }
+  }
+
+  if (reset) {
+    for (i = 0; i < HMAC_MAX_MD_CBLOCK; i++) {
+      pad[i] = 0x36 ^ ctx->key[i];
+    }
+    if (!EVP_DigestInit_ex(&ctx->i_ctx, md, impl) ||
+        !EVP_DigestUpdate(&ctx->i_ctx, pad, EVP_MD_block_size(md))) {
+      goto err;
+    }
+
+    for (i = 0; i < HMAC_MAX_MD_CBLOCK; i++) {
+      pad[i] = 0x5c ^ ctx->key[i];
+    }
+    if (!EVP_DigestInit_ex(&ctx->o_ctx, md, impl) ||
+        !EVP_DigestUpdate(&ctx->o_ctx, pad, EVP_MD_block_size(md))) {
+      goto err;
+    }
+  }
+
+  if (!EVP_MD_CTX_copy_ex(&ctx->md_ctx, &ctx->i_ctx)) {
+    goto err;
+  }
+
+  return 1;
+
+err:
+  return 0;
+}
+
+int HMAC_Update(HMAC_CTX *ctx, const uint8_t *data, size_t data_len) {
+  return EVP_DigestUpdate(&ctx->md_ctx, data, data_len);
+}
+
+int HMAC_Final(HMAC_CTX *ctx, uint8_t *out, unsigned int *out_len) {
+  unsigned int i;
+  uint8_t buf[EVP_MAX_MD_SIZE];
+
+  if (!EVP_DigestFinal_ex(&ctx->md_ctx, buf, &i) ||
+      !EVP_MD_CTX_copy_ex(&ctx->md_ctx, &ctx->o_ctx) ||
+      !EVP_DigestUpdate(&ctx->md_ctx, buf, i) ||
+      !EVP_DigestFinal_ex(&ctx->md_ctx, out, out_len)) {
+    *out_len = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+size_t HMAC_size(const HMAC_CTX *ctx) {
+  return EVP_MD_size(ctx->md);
+}
+
+int HMAC_CTX_copy(HMAC_CTX *dest, const HMAC_CTX *src) {
+  if (!EVP_MD_CTX_copy(&dest->i_ctx, &src->i_ctx) ||
+      !EVP_MD_CTX_copy(&dest->o_ctx, &src->o_ctx) ||
+      !EVP_MD_CTX_copy(&dest->md_ctx, &src->md_ctx)) {
+    return 0;
+  }
+
+  memcpy(dest->key, src->key, HMAC_MAX_MD_CBLOCK);
+  dest->key_length = src->key_length;
+  dest->md = src->md;
+  return 1;
+}
+
+void HMAC_CTX_set_flags(HMAC_CTX *ctx, unsigned long flags) {
+  EVP_MD_CTX_set_flags(&ctx->i_ctx, flags);
+  EVP_MD_CTX_set_flags(&ctx->o_ctx, flags);
+  EVP_MD_CTX_set_flags(&ctx->md_ctx, flags);
+}
+
+int HMAC_Init(HMAC_CTX *ctx, const void *key, int key_len, const EVP_MD *md) {
+  if (key && md) {
+    HMAC_CTX_init(ctx);
+  }
+  return HMAC_Init_ex(ctx, key, key_len, md, NULL);
+}
diff --git a/crypto/hmac/hmac.h b/crypto/hmac/hmac.h
new file mode 100644
index 0000000..33c9061
--- /dev/null
+++ b/crypto/hmac/hmac.h
@@ -0,0 +1,155 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_HMAC_H
+#define OPENSSL_HEADER_HMAC_H
+
+#include <openssl/base.h>
+
+#include <openssl/digest.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* HMAC contains functions for constructing PRFs from Merkle–Damgård hash
+ * functions using HMAC. */
+
+
+/* One-shot operation. */
+
+/* HMAC calculates the HMAC of |data_len| bytes of |data|, using the given key
+ * and hash function, and writes the result to |out|. On entry, |out| must
+ * contain |EVP_MAX_MD_SIZE| bytes of space. The actual length of the result is
+ * written to |*out_len|. It returns |out| or NULL on error. */
+uint8_t *HMAC(const EVP_MD *evp_md, const void *key, size_t key_len,
+              const uint8_t *data, size_t data_len, uint8_t *out,
+              unsigned int *out_len);
+
+
+/* Incremental operation. */
+
+/* HMAC_CTX_init initialises |ctx| for use in an HMAC operation. It's assumed
+ * that HMAC_CTX objects will be allocated on the stack thus no allocation
+ * function is provided. If needed, allocate |sizeof(HMAC_CTX)| and call
+ * |HMAC_CTX_init| on it. */
+void HMAC_CTX_init(HMAC_CTX *ctx);
+
+/* HMAC_CTX_cleanup frees data owned by |ctx|. */
+void HMAC_CTX_cleanup(HMAC_CTX *ctx);
+
+/* HMAC_Init_ex sets up an initialised |HMAC_CTX| to use |md| as the hash
+ * function and |key| as the key. Any of |md| or |key| can be NULL, in which
+ * case the previous value will be used. It returns one on success or zero
+ * otherwise. */
+int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
+                 const EVP_MD *md, ENGINE *impl);
+
+/* HMAC_Update hashes |data_len| bytes from |data| into the current HMAC
+ * operation in |ctx|. It returns one on success and zero on error. */
+int HMAC_Update(HMAC_CTX *ctx, const uint8_t *data, size_t data_len);
+
+/* HMAC_Final completes the HMAC operation in |ctx| and writes the result to
+ * |out| and the sets |*out_len| to the length of the result. On entry, |out|
+ * must contain at least |EVP_MAX_MD_SIZE| bytes of space. It returns one on
+ * success or zero on error. */
+int HMAC_Final(HMAC_CTX *ctx, uint8_t *out, unsigned int *out_len);
+
+
+/* Utility functions. */
+
+/* HMAC_size returns the size, in bytes, of the HMAC that will be produced by
+ * |ctx|. On entry, |ctx| must have been setup with |HMAC_Init_ex|. */
+size_t HMAC_size(const HMAC_CTX *ctx);
+
+/* HMAC_CTX_copy sets |dest| equal to |src|. On entry, |dest| must have been
+ * initialised by calling |HMAC_CTX_init|. It returns one on success and zero
+ * on error. */
+int HMAC_CTX_copy(HMAC_CTX *dest, const HMAC_CTX *src);
+
+/* HMAC_CTX_set_flags ORs |flags| into the flags of the underlying digests of
+ * |ctx|, which must have been setup by a call to |HMAC_Init_ex|. See
+ * |EVP_MD_CTX_set_flags|.
+ *
+ * TODO(fork): remove? */
+void HMAC_CTX_set_flags(HMAC_CTX *ctx, unsigned long flags);
+
+
+/* Deprecated functions. */
+
+int HMAC_Init(HMAC_CTX *ctx, const void *key, int key_len, const EVP_MD *md);
+
+
+/* Private functions */
+
+#define HMAC_MAX_MD_CBLOCK 128 /* largest known is SHA512 */
+
+struct hmac_ctx_st {
+  const EVP_MD *md;
+  EVP_MD_CTX md_ctx;
+  EVP_MD_CTX i_ctx;
+  EVP_MD_CTX o_ctx;
+  unsigned int key_length;
+  unsigned char key[HMAC_MAX_MD_CBLOCK];
+} /* HMAC_CTX */;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_HMAC_H */
diff --git a/crypto/hmac/hmac_test.c b/crypto/hmac/hmac_test.c
new file mode 100644
index 0000000..e2ae4a6
--- /dev/null
+++ b/crypto/hmac/hmac_test.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/digest.h>
+#include <openssl/hmac.h>
+
+
+struct test_st {
+  unsigned char key[16];
+  unsigned key_len;
+  unsigned char data[64];
+  unsigned data_len;
+  const char *hex_digest;
+};
+
+#define NUM_TESTS 4
+
+static const struct test_st kTests[NUM_TESTS] = {
+  {
+    "", 0, "More text test vectors to stuff up EBCDIC machines :-)", 54,
+    "e9139d1e6ee064ef8cf514fc7dc83e86",
+  },
+  {
+    {
+      0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+      0x0b, 0x0b, 0x0b, 0x0b,
+    },
+    16,
+    "Hi There",
+    8,
+    "9294727a3638bb1c13f48ef8158bfc9d",
+  },
+  {
+    "Jefe", 4, "what do ya want for nothing?", 28,
+    "750c783e6ab0b503eaa86e310a5db738",
+  },
+  {
+    {
+      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+      0xaa, 0xaa, 0xaa, 0xaa,
+    },
+    16,
+    {
+      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+      0xdd, 0xdd,
+    },
+    50,
+    "56be34521d144c88dbb8c733f0e8b3f6",
+  },
+};
+
+static char *to_hex(const uint8_t *md, size_t md_len) {
+  size_t i;
+  static char buf[80];
+
+  for (i = 0; i < md_len; i++) {
+    sprintf(&(buf[i * 2]), "%02x", md[i]);
+  }
+  return buf;
+}
+
+int main(int argc, char *argv[]) {
+  unsigned i;
+  char *p;
+  int err = 0;
+  uint8_t out[EVP_MAX_MD_SIZE];
+  unsigned out_len;
+
+  for (i = 0; i < NUM_TESTS; i++) {
+    const struct test_st *test = &kTests[i];
+
+    if (NULL == HMAC(EVP_md5(), test->key, test->key_len, test->data,
+                     test->data_len, out, &out_len)) {
+      printf("%u: HMAC failed.\n", i);
+      err++;
+      continue;
+    }
+
+    p = to_hex(out, out_len);
+
+    if (strcmp(p, test->hex_digest) != 0) {
+      printf("%u: got %s instead of %s\n", i, p, test->hex_digest);
+      err++;
+    }
+  }
+
+  if (err) {
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/internal.h b/crypto/internal.h
new file mode 100644
index 0000000..8464239
--- /dev/null
+++ b/crypto/internal.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_CRYPTO_INTERNAL_H
+#define OPENSSL_HEADER_CRYPTO_INTERNAL_H
+
+#include <openssl/base.h>
+
+
+/* st_CRYPTO_EX_DATA_IMPL contains an ex_data implementation. See the comments
+ * in ex_data.h for details of the behaviour of each of the functions. */
+struct st_CRYPTO_EX_DATA_IMPL {
+  int (*new_class)(void);
+  void (*cleanup)(void);
+
+  int (*get_new_index)(int class_value, long argl, void *argp,
+                       CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+                       CRYPTO_EX_free *free_func);
+  int (*new_ex_data)(int class_value, void *obj, CRYPTO_EX_DATA *ad);
+  int (*dup_ex_data)(int class_value, CRYPTO_EX_DATA *to,
+                     const CRYPTO_EX_DATA *from);
+  void (*free_ex_data)(int class_value, void *obj, CRYPTO_EX_DATA *ad);
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_CRYPTO_INTERNAL_H */
diff --git a/crypto/lhash/CMakeLists.txt b/crypto/lhash/CMakeLists.txt
new file mode 100644
index 0000000..c3bf08e
--- /dev/null
+++ b/crypto/lhash/CMakeLists.txt
@@ -0,0 +1,17 @@
+include_directories(. .. ../../include)
+
+add_library(
+	lhash
+
+	OBJECT
+
+	lhash.c
+)
+
+add_executable(
+	lhash_test
+
+	lhash_test.c
+)
+
+target_link_libraries(lhash_test crypto)
diff --git a/crypto/lhash/lhash.c b/crypto/lhash/lhash.c
new file mode 100644
index 0000000..b601480
--- /dev/null
+++ b/crypto/lhash/lhash.c
@@ -0,0 +1,344 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include "lhash.h"
+
+#include <assert.h>
+#include <limits.h>
+
+#include <openssl/mem.h>
+
+/* kMinNumBuckets is the minimum size of the buckets array in an |_LHASH|. */
+static const size_t kMinNumBuckets = 16;
+
+/* kMaxAverageChainLength contains the maximum, average chain length. When the
+ * average chain length exceeds this value, the hash table will be resized. */
+static const size_t kMaxAverageChainLength = 2;
+static const size_t kMinAverageChainLength = 1;
+
+_LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp) {
+  _LHASH *ret;
+
+  ret = OPENSSL_malloc(sizeof(_LHASH));
+  if (ret == NULL) {
+    return NULL;
+  }
+  memset(ret, 0, sizeof(_LHASH));
+
+  ret->num_buckets = kMinNumBuckets;
+  ret->buckets = OPENSSL_malloc(sizeof(LHASH_ITEM *) * ret->num_buckets);
+  if (ret->buckets == NULL) {
+    OPENSSL_free(ret);
+    return NULL;
+  }
+  memset(ret->buckets, 0, sizeof(LHASH_ITEM *) * ret->num_buckets);
+
+  ret->comp = comp;
+  if (ret->comp == NULL) {
+    ret->comp = (lhash_cmp_func) strcmp;
+  }
+  ret->hash = hash;
+  if (ret->hash == NULL) {
+    ret->hash = (lhash_hash_func) lh_strhash;
+  }
+
+  return ret;
+}
+
+void lh_free(_LHASH *lh) {
+  size_t i;
+  LHASH_ITEM *n, *next;
+
+  if (lh == NULL) {
+    return;
+  }
+
+  for (i = 0; i < lh->num_buckets; i++) {
+    for (n = lh->buckets[i]; n != NULL; n = next) {
+      next = n->next;
+      OPENSSL_free(n);
+    }
+  }
+
+  OPENSSL_free(lh->buckets);
+  OPENSSL_free(lh);
+}
+
+size_t lh_num_items(const _LHASH *lh) { return lh->num_items; }
+
+/* get_next_ptr_and_hash returns a pointer to the pointer that points to the
+ * item equal to |data|. In other words, it searches for an item equal to |data|
+ * and, if it's at the start of a chain, then it returns a pointer to an
+ * element of |lh->buckets|, otherwise it returns a pointer to the |next|
+ * element of the previous item in the chain. If an element equal to |data| is
+ * not found, it returns a pointer that points to a NULL pointer. If |out_hash|
+ * is not NULL, then it also puts the hash value of |data| in |*out_hash|. */
+static LHASH_ITEM **get_next_ptr_and_hash(const _LHASH *lh, uint32_t *out_hash,
+                                          const void *data) {
+  const uint32_t hash = lh->hash(data);
+  LHASH_ITEM *cur, **ret;
+
+  if (out_hash != NULL) {
+    *out_hash = hash;
+  }
+
+  ret = &lh->buckets[hash % lh->num_buckets];
+  for (cur = *ret; cur != NULL; cur = *ret) {
+    if (lh->comp(cur->data, data) == 0) {
+      break;
+    }
+    ret = &cur->next;
+  }
+
+  return ret;
+}
+
+void *lh_retrieve(const _LHASH *lh, const void *data) {
+  LHASH_ITEM **next_ptr;
+
+  next_ptr = get_next_ptr_and_hash(lh, NULL, data);
+
+  if (*next_ptr == NULL) {
+    return NULL;
+  }
+
+  return (*next_ptr)->data;
+}
+
+/* lh_rebucket allocates a new array of |new_num_buckets| pointers and
+ * redistributes the existing items into it before making it |lh->buckets| and
+ * freeing the old array. */
+static void lh_rebucket(_LHASH *lh, const size_t new_num_buckets) {
+  LHASH_ITEM **new_buckets, *cur, *next;
+  size_t i, alloc_size;
+
+  alloc_size = sizeof(LHASH_ITEM *) * new_num_buckets;
+  if (alloc_size / sizeof(LHASH_ITEM*) != new_num_buckets) {
+    return;
+  }
+
+  new_buckets = OPENSSL_malloc(alloc_size);
+  if (new_buckets == NULL) {
+    return;
+  }
+  memset(new_buckets, 0, alloc_size);
+
+  for (i = 0; i < lh->num_buckets; i++) {
+    for (cur = lh->buckets[i]; cur != NULL; cur = next) {
+      const size_t new_bucket = cur->hash % new_num_buckets;
+      next = cur->next;
+      cur->next = new_buckets[new_bucket];
+      new_buckets[new_bucket] = cur;
+    }
+  }
+
+  OPENSSL_free(lh->buckets);
+
+  lh->num_buckets = new_num_buckets;
+  lh->buckets = new_buckets;
+}
+
+/* lh_maybe_resize resizes the |buckets| array if needed. */
+static void lh_maybe_resize(_LHASH *lh) {
+  size_t avg_chain_length;
+
+  if (lh->callback_depth > 0) {
+    /* Don't resize the hash if we are currently iterating over it. */
+    return;
+  }
+
+  assert(lh->num_buckets >= kMinNumBuckets);
+  avg_chain_length = lh->num_items / lh->num_buckets;
+
+  if (avg_chain_length > kMaxAverageChainLength) {
+    const size_t new_num_buckets = lh->num_buckets * 2;
+
+    if (new_num_buckets > lh->num_buckets) {
+      lh_rebucket(lh, new_num_buckets);
+    }
+  } else if (avg_chain_length < kMinAverageChainLength &&
+             lh->num_buckets > kMinNumBuckets) {
+    size_t new_num_buckets = lh->num_buckets / 2;
+
+    if (new_num_buckets < kMinNumBuckets) {
+      new_num_buckets = kMinNumBuckets;
+    }
+
+    lh_rebucket(lh, new_num_buckets);
+  }
+}
+
+int lh_insert(_LHASH *lh, void **old_data, void *data) {
+  uint32_t hash;
+  LHASH_ITEM **next_ptr, *item;
+
+  *old_data = NULL;
+  next_ptr = get_next_ptr_and_hash(lh, &hash, data);
+
+
+  if (*next_ptr != NULL) {
+    /* An element equal to |data| already exists in the hash table. It will be
+     * replaced. */
+    *old_data = (*next_ptr)->data;
+    (*next_ptr)->data = data;
+    return 1;
+  }
+
+  /* An element equal to |data| doesn't exist in the hash table yet. */
+  item = OPENSSL_malloc(sizeof(LHASH_ITEM));
+  if (item == NULL) {
+    return 0;
+  }
+
+  item->data = data;
+  item->hash = hash;
+  item->next = NULL;
+  *next_ptr = item;
+  lh->num_items++;
+  lh_maybe_resize(lh);
+
+  return 1;
+}
+
+void *lh_delete(_LHASH *lh, const void *data) {
+  LHASH_ITEM **next_ptr, *item, *ret;
+
+  next_ptr = get_next_ptr_and_hash(lh, NULL, data);
+
+  if (*next_ptr == NULL) {
+    /* No such element. */
+    return NULL;
+  }
+
+  item = *next_ptr;
+  *next_ptr = item->next;
+  ret = item->data;
+  OPENSSL_free(item);
+
+  lh->num_items--;
+  lh_maybe_resize(lh);
+
+  return ret;
+}
+
+static void lh_doall_internal(_LHASH *lh, void (*no_arg_func)(void *),
+                              void (*arg_func)(void *, void *), void *arg) {
+  size_t i;
+  LHASH_ITEM *cur, *next;
+
+  if (lh == NULL) {
+    return;
+  }
+
+  if (lh->callback_depth < UINT_MAX) {
+    /* |callback_depth| is a saturating counter. */
+    lh->callback_depth++;
+  }
+
+  for (i = 0; i < lh->num_buckets; i++) {
+    for (cur = lh->buckets[i]; cur != NULL; cur = next) {
+      next = cur->next;
+      if (arg_func) {
+        arg_func(cur->data, arg);
+      } else {
+        no_arg_func(cur->data);
+      }
+    }
+  }
+
+  if (lh->callback_depth < UINT_MAX) {
+    lh->callback_depth--;
+  }
+
+  /* The callback may have added or removed elements and the non-zero value of
+   * |callback_depth| will have suppressed any resizing. Thus any needed
+   * resizing is done here. */
+  lh_maybe_resize(lh);
+}
+
+void lh_doall(_LHASH *lh, void (*func)(void *)) {
+  lh_doall_internal(lh, func, NULL, NULL);
+}
+
+void lh_doall_arg(_LHASH *lh, void (*func)(void *, void *), void *arg) {
+  lh_doall_internal(lh, NULL, func, arg);
+}
+
+uint32_t lh_strhash(const char *c) {
+  /* The following hash seems to work very well on normal text strings
+   * no collisions on /usr/dict/words and it distributes on %2^n quite
+   * well, not as good as MD5, but still good. */
+  unsigned long ret = 0;
+  long n;
+  unsigned long v;
+  int r;
+
+  if ((c == NULL) || (*c == '\0')) {
+    return (ret);
+  }
+
+  n = 0x100;
+  while (*c) {
+    v = n | (*c);
+    n += 0x100;
+    r = (int)((v >> 2) ^ v) & 0x0f;
+    ret = (ret << r) | (ret >> (32 - r));
+    ret &= 0xFFFFFFFFL;
+    ret ^= v * v;
+    c++;
+  }
+
+  return ((ret >> 16) ^ ret);
+}
diff --git a/crypto/lhash/lhash.h b/crypto/lhash/lhash.h
new file mode 100644
index 0000000..82842d7
--- /dev/null
+++ b/crypto/lhash/lhash.h
@@ -0,0 +1,193 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_LHASH_H
+#define OPENSSL_HEADER_LHASH_H
+
+#include <openssl/base.h>
+#include <openssl/type_check.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* lhash is a traditional, chaining hash table that automatically expands and
+ * contracts as needed. One should not use the lh_* functions directly, rather
+ * use the type-safe macro wrappers:
+ *
+ * A hash table of a specific type of object has type |LHASH_OF(type)|. This
+ * can be defined (once) with |DEFINE_LHASH_OF(type)| and declared where needed
+ * with |DECLARE_LHASH_OF(type)|. For example:
+ *
+ *   struct foo {
+ *     int bar;
+ *   };
+ *
+ *   DEFINE_LHASH_OF(struct foo);
+ *
+ * Although note that the hash table will contain /pointers/ to |foo|.
+ *
+ * A macro will be defined for each of the lh_* functions below. For
+ * LHASH_OF(foo), the macros would be lh_foo_new, lh_foo_num_items etc. */
+
+
+#define LHASH_OF(type) struct lhash_st_##type
+
+#define DEFINE_LHASH_OF(type) LHASH_OF(type) { int dummy; }
+
+#define DECLARE_LHASH_OF(type) LHASH_OF(type);
+
+/* The make_macros.sh script in this directory parses the following lines and
+ * generates the lhash_macros.h file that contains macros for the following
+ * types of stacks:
+ *
+ * LHASH_OF:ASN1_OBJECT
+ * LHASH_OF:CONF_VALUE
+ * LHASH_OF:ERR_STATE
+ * LHASH_OF:ERR_STRING_DATA
+ * LHASH_OF:EX_CLASS_ITEM
+ * LHASH_OF:SSL_SESSION */
+
+#define IN_LHASH_H
+#include <openssl/lhash_macros.h>
+#undef IN_LHASH_H
+
+
+/* lhash_item_st is an element of a hash chain. It points to the opaque data
+ * for this element and to the next item in the chain. The linked-list is NULL
+ * terminated. */
+typedef struct lhash_item_st {
+  void *data;
+  struct lhash_item_st *next;
+  /* hash contains the cached, hash value of |data|. */
+  uint32_t hash;
+} LHASH_ITEM;
+
+/* lhash_cmp_func is a comparision function that returns a value equal, or not
+ * equal, to zero depending on whether |*a| is equal, or not equal to |*b|,
+ * respectively. Note the difference between this and |stack_cmp_func| in that
+ * this takes pointers to the objects directly. */
+typedef int (*lhash_cmp_func)(const void *a, const void *b);
+
+/* lhash_hash_func is a function that maps an object to a uniformly distributed
+ * uint32_t. */
+typedef uint32_t (*lhash_hash_func)(const void *a);
+
+typedef struct lhash_st {
+  /* num_items contains the total number of items in the hash table. */
+  size_t num_items;
+  /* buckets is an array of |num_buckets| pointers. Each points to the head of
+   * a chain of LHASH_ITEM objects that have the same hash value, mod
+   * |num_buckets|. */
+  LHASH_ITEM **buckets;
+  /* num_buckets contains the length of |buckets|. This value is always >=
+   * kMinNumBuckets. */
+  size_t num_buckets;
+  /* callback_depth contains the current depth of |lh_doall| or |lh_doall_arg|
+   * calls. If non-zero then this suppresses resizing of the |buckets| array,
+   * which would otherwise disrupt the iteration. */
+  unsigned callback_depth;
+
+  lhash_cmp_func comp;
+  lhash_hash_func hash;
+} _LHASH;
+
+/* lh_new returns a new, empty hash table or NULL on error. If |comp| is NULL,
+ * |strcmp| will be used. If |hash| is NULL, a generic hash function will be
+ * used. */
+_LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp);
+
+/* lh_free frees the hash table itself but none of the elements. See
+ * |lh_doall|. */
+void lh_free(_LHASH *lh);
+
+/* lh_num_items returns the number of items in |lh|. */
+size_t lh_num_items(const _LHASH *lh);
+
+/* lh_retrieve finds an element equal to |data| in the hash table and returns
+ * it. If no such element exists, it returns NULL. */
+void *lh_retrieve(const _LHASH *lh, const void *data);
+
+/* lh_insert inserts |data| into the hash table. If an existing element is
+ * equal to |data| (with respect to the comparison function) then |*old_data|
+ * will be set to that value and it will be replaced. Otherwise, or in the
+ * event of an error, |*old_data| will be set to NULL. It returns one on
+ * success or zero in the case of an allocation error. */
+int lh_insert(_LHASH *lh, void **old_data, void *data);
+
+/* lh_delete removes an element equal to |data| from the hash table and returns
+ * it. If no such element is found, it returns NULL. */
+void *lh_delete(_LHASH *lh, const void *data);
+
+/* lh_doall calls |func| on each element of the hash table.
+ * TODO(fork): rename this */
+void lh_doall(_LHASH *lh, void (*func)(void *));
+
+/* lh_doall_arg calls |func| on each element of the hash table and also passes
+ * |arg| as the second argument.
+ * TODO(fork): rename this */
+void lh_doall_arg(_LHASH *lh, void (*func)(void *, void *), void *arg);
+
+/* lh_strhash is the default hash function which processes NUL-terminated
+ * strings. */
+uint32_t lh_strhash(const char *c);
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_STACK_H */
diff --git a/crypto/lhash/lhash_macros.h b/crypto/lhash/lhash_macros.h
new file mode 100644
index 0000000..f84b5ed
--- /dev/null
+++ b/crypto/lhash/lhash_macros.h
@@ -0,0 +1,248 @@
+/* Copyright (c) 2014, 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(IN_LHASH_H)
+#error "Don't include this file directly. Include lhash.h"
+#endif
+
+/* ASN1_OBJECT */
+#define lh_ASN1_OBJECT_new(hash, comp)                                        \
+  ((LHASH_OF(ASN1_OBJECT) *)lh_new(                                           \
+      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const ASN1_OBJECT *), hash), \
+      CHECKED_CAST(lhash_cmp_func,                                            \
+                   int (*)(const ASN1_OBJECT *a, const ASN1_OBJECT *b),       \
+                   comp)))
+
+#define lh_ASN1_OBJECT_free(lh) \
+  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh));
+
+#define lh_ASN1_OBJECT_num_items(lh) \
+  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh))
+
+#define lh_ASN1_OBJECT_retrieve(lh, data)                  \
+  ((ASN1_OBJECT *)lh_retrieve(                             \
+      CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh), \
+      CHECKED_CAST(void *, ASN1_OBJECT *, data)))
+
+#define lh_ASN1_OBJECT_insert(lh, old_data, data)                \
+  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh), \
+            CHECKED_CAST(void **, ASN1_OBJECT **, old_data),     \
+            CHECKED_CAST(void *, ASN1_OBJECT *, data))
+
+#define lh_ASN1_OBJECT_delete(lh, data)                    \
+  ((ASN1_OBJECT *)lh_delete(                               \
+      CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh), \
+      CHECKED_CAST(void *, ASN1_OBJECT *, data)))
+
+#define lh_ASN1_OBJECT_doall(lh, func)                          \
+  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh), \
+           CHECKED_CAST(void (*)(void *), void (*)(ASN1_OBJECT *), func));
+
+#define lh_ASN1_OBJECT_doall_arg(lh, func, arg)                     \
+  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(ASN1_OBJECT) *, lh), \
+               CHECKED_CAST(void (*)(void *, void *),               \
+                            void (*)(ASN1_OBJECT *, void *), func), \
+               arg);
+
+/* CONF_VALUE */
+#define lh_CONF_VALUE_new(hash, comp)                                        \
+  ((LHASH_OF(CONF_VALUE) *)lh_new(                                           \
+      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const CONF_VALUE *), hash), \
+      CHECKED_CAST(lhash_cmp_func,                                           \
+                   int (*)(const CONF_VALUE *a, const CONF_VALUE *b), comp)))
+
+#define lh_CONF_VALUE_free(lh) \
+  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh));
+
+#define lh_CONF_VALUE_num_items(lh) \
+  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh))
+
+#define lh_CONF_VALUE_retrieve(lh, data)                  \
+  ((CONF_VALUE *)lh_retrieve(                             \
+      CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh), \
+      CHECKED_CAST(void *, CONF_VALUE *, data)))
+
+#define lh_CONF_VALUE_insert(lh, old_data, data)                \
+  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh), \
+            CHECKED_CAST(void **, CONF_VALUE **, old_data),     \
+            CHECKED_CAST(void *, CONF_VALUE *, data))
+
+#define lh_CONF_VALUE_delete(lh, data)                                         \
+  ((CONF_VALUE *)lh_delete(CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh), \
+                           CHECKED_CAST(void *, CONF_VALUE *, data)))
+
+#define lh_CONF_VALUE_doall(lh, func)                          \
+  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh), \
+           CHECKED_CAST(void (*)(void *), void (*)(CONF_VALUE *), func));
+
+#define lh_CONF_VALUE_doall_arg(lh, func, arg)                     \
+  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(CONF_VALUE) *, lh), \
+               CHECKED_CAST(void (*)(void *, void *),              \
+                            void (*)(CONF_VALUE *, void *), func), \
+               arg);
+
+/* ERR_STATE */
+#define lh_ERR_STATE_new(hash, comp)                                        \
+  ((LHASH_OF(ERR_STATE) *)lh_new(                                           \
+      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const ERR_STATE *), hash), \
+      CHECKED_CAST(lhash_cmp_func,                                          \
+                   int (*)(const ERR_STATE *a, const ERR_STATE *b), comp)))
+
+#define lh_ERR_STATE_free(lh) \
+  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh));
+
+#define lh_ERR_STATE_num_items(lh) \
+  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh))
+
+#define lh_ERR_STATE_retrieve(lh, data)                                        \
+  ((ERR_STATE *)lh_retrieve(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh), \
+                            CHECKED_CAST(void *, ERR_STATE *, data)))
+
+#define lh_ERR_STATE_insert(lh, old_data, data)                \
+  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh), \
+            CHECKED_CAST(void **, ERR_STATE **, old_data),     \
+            CHECKED_CAST(void *, ERR_STATE *, data))
+
+#define lh_ERR_STATE_delete(lh, data)                                        \
+  ((ERR_STATE *)lh_delete(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh), \
+                          CHECKED_CAST(void *, ERR_STATE *, data)))
+
+#define lh_ERR_STATE_doall(lh, func)                          \
+  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh), \
+           CHECKED_CAST(void (*)(void *), void (*)(ERR_STATE *), func));
+
+#define lh_ERR_STATE_doall_arg(lh, func, arg)                     \
+  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STATE) *, lh), \
+               CHECKED_CAST(void (*)(void *, void *),             \
+                            void (*)(ERR_STATE *, void *), func), \
+               arg);
+
+/* ERR_STRING_DATA */
+#define lh_ERR_STRING_DATA_new(hash, comp)                                 \
+  ((LHASH_OF(ERR_STRING_DATA) *)lh_new(                                    \
+      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const ERR_STRING_DATA *), \
+                   hash),                                                  \
+      CHECKED_CAST(                                                        \
+          lhash_cmp_func,                                                  \
+          int (*)(const ERR_STRING_DATA *a, const ERR_STRING_DATA *b), comp)))
+
+#define lh_ERR_STRING_DATA_free(lh) \
+  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh));
+
+#define lh_ERR_STRING_DATA_num_items(lh) \
+  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh))
+
+#define lh_ERR_STRING_DATA_retrieve(lh, data)                  \
+  ((ERR_STRING_DATA *)lh_retrieve(                             \
+      CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh), \
+      CHECKED_CAST(void *, ERR_STRING_DATA *, data)))
+
+#define lh_ERR_STRING_DATA_insert(lh, old_data, data)                \
+  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh), \
+            CHECKED_CAST(void **, ERR_STRING_DATA **, old_data),     \
+            CHECKED_CAST(void *, ERR_STRING_DATA *, data))
+
+#define lh_ERR_STRING_DATA_delete(lh, data)                    \
+  ((ERR_STRING_DATA *)lh_delete(                               \
+      CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh), \
+      CHECKED_CAST(void *, ERR_STRING_DATA *, data)))
+
+#define lh_ERR_STRING_DATA_doall(lh, func)                          \
+  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh), \
+           CHECKED_CAST(void (*)(void *), void (*)(ERR_STRING_DATA *), func));
+
+#define lh_ERR_STRING_DATA_doall_arg(lh, func, arg)                     \
+  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(ERR_STRING_DATA) *, lh), \
+               CHECKED_CAST(void (*)(void *, void *),                   \
+                            void (*)(ERR_STRING_DATA *, void *), func), \
+               arg);
+
+/* EX_CLASS_ITEM */
+#define lh_EX_CLASS_ITEM_new(hash, comp)                                    \
+  ((LHASH_OF(EX_CLASS_ITEM) *)lh_new(                                       \
+      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const EX_CLASS_ITEM *),    \
+                   hash),                                                   \
+      CHECKED_CAST(lhash_cmp_func,                                          \
+                   int (*)(const EX_CLASS_ITEM *a, const EX_CLASS_ITEM *b), \
+                   comp)))
+
+#define lh_EX_CLASS_ITEM_free(lh) \
+  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh));
+
+#define lh_EX_CLASS_ITEM_num_items(lh) \
+  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh))
+
+#define lh_EX_CLASS_ITEM_retrieve(lh, data)                  \
+  ((EX_CLASS_ITEM *)lh_retrieve(                             \
+      CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
+      CHECKED_CAST(void *, EX_CLASS_ITEM *, data)))
+
+#define lh_EX_CLASS_ITEM_insert(lh, old_data, data)                \
+  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
+            CHECKED_CAST(void **, EX_CLASS_ITEM **, old_data),     \
+            CHECKED_CAST(void *, EX_CLASS_ITEM *, data))
+
+#define lh_EX_CLASS_ITEM_delete(lh, data)                    \
+  ((EX_CLASS_ITEM *)lh_delete(                               \
+      CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
+      CHECKED_CAST(void *, EX_CLASS_ITEM *, data)))
+
+#define lh_EX_CLASS_ITEM_doall(lh, func)                          \
+  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
+           CHECKED_CAST(void (*)(void *), void (*)(EX_CLASS_ITEM *), func));
+
+#define lh_EX_CLASS_ITEM_doall_arg(lh, func, arg)                     \
+  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(EX_CLASS_ITEM) *, lh), \
+               CHECKED_CAST(void (*)(void *, void *),                 \
+                            void (*)(EX_CLASS_ITEM *, void *), func), \
+               arg);
+
+/* SSL_SESSION */
+#define lh_SSL_SESSION_new(hash, comp)                                        \
+  ((LHASH_OF(SSL_SESSION) *)lh_new(                                           \
+      CHECKED_CAST(lhash_hash_func, uint32_t (*)(const SSL_SESSION *), hash), \
+      CHECKED_CAST(lhash_cmp_func,                                            \
+                   int (*)(const SSL_SESSION *a, const SSL_SESSION *b),       \
+                   comp)))
+
+#define lh_SSL_SESSION_free(lh) \
+  lh_free(CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh));
+
+#define lh_SSL_SESSION_num_items(lh) \
+  lh_num_items(CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh))
+
+#define lh_SSL_SESSION_retrieve(lh, data)                  \
+  ((SSL_SESSION *)lh_retrieve(                             \
+      CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh), \
+      CHECKED_CAST(void *, SSL_SESSION *, data)))
+
+#define lh_SSL_SESSION_insert(lh, old_data, data)                \
+  lh_insert(CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh), \
+            CHECKED_CAST(void **, SSL_SESSION **, old_data),     \
+            CHECKED_CAST(void *, SSL_SESSION *, data))
+
+#define lh_SSL_SESSION_delete(lh, data)                    \
+  ((SSL_SESSION *)lh_delete(                               \
+      CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh), \
+      CHECKED_CAST(void *, SSL_SESSION *, data)))
+
+#define lh_SSL_SESSION_doall(lh, func)                          \
+  lh_doall(CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh), \
+           CHECKED_CAST(void (*)(void *), void (*)(SSL_SESSION *), func));
+
+#define lh_SSL_SESSION_doall_arg(lh, func, arg)                     \
+  lh_doall_arg(CHECKED_CAST(_LHASH *, LHASH_OF(SSL_SESSION) *, lh), \
+               CHECKED_CAST(void (*)(void *, void *),               \
+                            void (*)(SSL_SESSION *, void *), func), \
+               arg);
diff --git a/crypto/lhash/lhash_test.c b/crypto/lhash/lhash_test.c
new file mode 100644
index 0000000..e53a6f6
--- /dev/null
+++ b/crypto/lhash/lhash_test.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2014, 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. */
+
+#define _POSIX_SOURCE
+#define _BSD_SOURCE
+
+#include <openssl/lhash.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct dummy_lhash_node {
+  char *s;
+  struct dummy_lhash_node *next;
+};
+
+struct dummy_lhash {
+  struct dummy_lhash_node *head;
+};
+
+static void dummy_lh_free(struct dummy_lhash *lh) {
+  struct dummy_lhash_node *cur, *next;
+
+  for (cur = lh->head; cur != NULL; cur = next) {
+    next = cur->next;
+    free(cur->s);
+    free(cur);
+  }
+}
+
+static size_t dummy_lh_num_items(const struct dummy_lhash *lh) {
+  size_t count = 0;
+  struct dummy_lhash_node *cur;
+
+  for (cur = lh->head; cur != NULL; cur = cur->next) {
+    count++;
+  }
+
+  return count;
+}
+
+static char *dummy_lh_retrieve(struct dummy_lhash *lh, const char *s) {
+  struct dummy_lhash_node *cur;
+
+  for (cur = lh->head; cur != NULL; cur = cur->next) {
+    if (strcmp(cur->s, s) == 0) {
+      return cur->s;
+    }
+  }
+
+  return NULL;
+}
+
+static int dummy_lh_insert(struct dummy_lhash *lh, char **old_data, char *s) {
+  struct dummy_lhash_node *node, *cur;
+
+  for (cur = lh->head; cur != NULL; cur = cur->next) {
+    if (strcmp(cur->s, s) == 0) {
+      *old_data = cur->s;
+      cur->s = s;
+      return 1;
+    }
+  }
+
+  node = malloc(sizeof(struct dummy_lhash_node));
+  *old_data = NULL;
+  node->s = s;
+  node->next = lh->head;
+  lh->head = node;
+  return 1;
+}
+
+static char *dummy_lh_delete(struct dummy_lhash *lh, const void *s) {
+  struct dummy_lhash_node *cur, **next_ptr;
+  char *ret;
+
+  next_ptr = &lh->head;
+  for (cur = lh->head; cur != NULL; cur = cur->next) {
+    if (strcmp(cur->s, s) == 0) {
+      ret = cur->s;
+      *next_ptr = cur->next;
+      free(cur);
+      return ret;
+    }
+    next_ptr = &cur->next;
+  }
+
+  return NULL;
+}
+
+static char *rand_string(unsigned *rand_state) {
+  unsigned len = 1 + (rand_r(rand_state) % 3);
+  char *ret = malloc(len + 1);
+  unsigned i;
+
+  for (i = 0; i < len; i++) {
+    ret[i] = '0' + (rand_r(rand_state) & 7);
+  }
+  ret[i] = 0;
+
+  return ret;
+}
+
+int main(int argc, char **argv) {
+  _LHASH *lh = lh_new(NULL, NULL);
+  struct dummy_lhash dummy_lh = {NULL};
+  unsigned rand_state = 0;
+  unsigned i;
+
+  for (i = 0; i < 100000; i++) {
+    unsigned action;
+    char *s, *s1, *s2;
+
+    if (dummy_lh_num_items(&dummy_lh) != lh_num_items(lh)) {
+      fprintf(stderr, "Length mismatch\n");
+      return 1;
+    }
+
+    action = rand_r(&rand_state) % 3;
+    switch (action) {
+      case 0:
+        s = rand_string(&rand_state);
+        s1 = (char *)lh_retrieve(lh, s);
+        s2 = dummy_lh_retrieve(&dummy_lh, s);
+        if (s1 != NULL && (s2 == NULL || strcmp(s1, s2) != 0)) {
+          fprintf(stderr, "lh_retrieve failure\n");
+          abort();
+        }
+        free(s);
+        break;
+
+      case 1:
+        s = rand_string(&rand_state);
+        lh_insert(lh, (void **)&s1, s);
+        dummy_lh_insert(&dummy_lh, &s2, strdup(s));
+
+        if (s1 != NULL && (s2 == NULL || strcmp(s1, s2) != 0)) {
+          fprintf(stderr, "lh_insert failure\n");
+          abort();
+        }
+
+        if (s1) {
+          free(s1);
+        }
+        if (s2) {
+          free(s2);
+        }
+        break;
+
+      case 2:
+        s = rand_string(&rand_state);
+        s1 = lh_delete(lh, s);
+        s2 = dummy_lh_delete(&dummy_lh, s);
+
+        if (s1 != NULL && (s2 == NULL || strcmp(s1, s2) != 0)) {
+          fprintf(stderr, "lh_insert failure\n");
+          abort();
+        }
+
+        if (s1) {
+          free(s1);
+        }
+        if (s2) {
+          free(s2);
+        }
+        free(s);
+        break;
+
+      default:
+        abort();
+    }
+  }
+
+  lh_doall(lh, free);
+  lh_free(lh);
+  dummy_lh_free(&dummy_lh);
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/lhash/make_macros.sh b/crypto/lhash/make_macros.sh
new file mode 100644
index 0000000..79d1e57
--- /dev/null
+++ b/crypto/lhash/make_macros.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+cat > lhash_macros.h << EOF
+/* Copyright (c) 2014, 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(IN_LHASH_H)
+#error "Don't include this file directly. Include lhash.h"
+#endif
+
+EOF
+
+output_lhash () {
+  type=$1
+
+  cat >> lhash_macros.h << EOF
+/* ${type} */
+#define lh_${type}_new(hash, comp)\\
+((LHASH_OF(${type})*) lh_new(CHECKED_CAST(lhash_hash_func, uint32_t (*) (const ${type} *), hash), CHECKED_CAST(lhash_cmp_func, int (*) (const ${type} *a, const ${type} *b), comp)))
+
+#define lh_${type}_free(lh)\\
+  lh_free(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh));
+
+#define lh_${type}_num_items(lh)\\
+  lh_num_items(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh))
+
+#define lh_${type}_retrieve(lh, data)\\
+  ((${type}*) lh_retrieve(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh), CHECKED_CAST(void*, ${type}*, data)))
+
+#define lh_${type}_insert(lh, old_data, data)\\
+  lh_insert(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh), CHECKED_CAST(void**, ${type}**, old_data), CHECKED_CAST(void*, ${type}*, data))
+
+#define lh_${type}_delete(lh, data)\\
+  ((${type}*) lh_delete(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh), CHECKED_CAST(void*, ${type}*, data)))
+
+#define lh_${type}_doall(lh, func)\\
+  lh_doall(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh), CHECKED_CAST(void (*)(void*), void (*) (${type}*), func));
+
+#define lh_${type}_doall_arg(lh, func, arg)\\
+  lh_doall_arg(CHECKED_CAST(_LHASH*, LHASH_OF(${type})*, lh), CHECKED_CAST(void (*)(void*, void*), void (*) (${type}*, void*), func), arg);
+
+
+EOF
+}
+
+lhash_types=$(cat lhash.h | grep '^ \* LHASH_OF:' | sed -e 's/.*LHASH_OF://' -e 's/ .*//')
+
+for type in $lhash_types; do
+  echo Hash of ${type}
+  output_lhash "${type}"
+done
+
+clang-format -i lhash_macros.h
diff --git a/crypto/md5/CMakeLists.txt b/crypto/md5/CMakeLists.txt
new file mode 100644
index 0000000..ce2f8dc
--- /dev/null
+++ b/crypto/md5/CMakeLists.txt
@@ -0,0 +1,38 @@
+include_directories(. .. ../../include)
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		MD5_ARCH_SOURCES
+
+		md5-x86_64.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		MD5_ARCH_SOURCES
+
+		md5-586.${ASM_EXT}
+	)
+endif()
+
+add_library(
+	md5
+
+	OBJECT
+
+	md5.c
+
+	${MD5_ARCH_SOURCES}
+)
+
+perlasm(md5-x86_64.${ASM_EXT} asm/md5-x86_64.pl)
+perlasm(md5-586.${ASM_EXT} asm/md5-586.pl)
+
+add_executable(
+	md5_test
+
+	md5_test.c
+)
+
+target_link_libraries(md5_test crypto)
diff --git a/crypto/md5/asm/md5-586.pl b/crypto/md5/asm/md5-586.pl
new file mode 100644
index 0000000..6cb66bb
--- /dev/null
+++ b/crypto/md5/asm/md5-586.pl
@@ -0,0 +1,307 @@
+#!/usr/local/bin/perl
+
+# Normal is the
+# md5_block_x86(MD5_CTX *c, ULONG *X);
+# version, non-normal is the
+# md5_block_x86(MD5_CTX *c, ULONG *X,int blocks);
+
+$normal=0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],$0);
+
+$A="eax";
+$B="ebx";
+$C="ecx";
+$D="edx";
+$tmp1="edi";
+$tmp2="ebp";
+$X="esi";
+
+# What we need to load into $tmp for the next round
+%Ltmp1=("R0",&Np($C), "R1",&Np($C), "R2",&Np($C), "R3",&Np($D));
+@xo=(
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	# R0
+ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,	# R1
+ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,	# R2
+ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9,	# R3
+ );
+
+&md5_block("md5_block_asm_data_order");
+&asm_finish();
+
+sub Np
+	{
+	local($p)=@_;
+	local(%n)=($A,$D,$B,$A,$C,$B,$D,$C);
+	return($n{$p});
+	}
+
+sub R0
+	{
+	local($pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+
+	&mov($tmp1,$C)  if $pos < 0;
+	&mov($tmp2,&DWP($xo[$ki]*4,$K,"",0)) if $pos < 0; # very first one 
+
+	# body proper
+
+	&comment("R0 $ki");
+	&xor($tmp1,$d); # F function - part 2
+
+	&and($tmp1,$b); # F function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$d); # F function - part 4
+
+	&add($a,$tmp1);
+	&mov($tmp1,&Np($c)) if $pos < 1;	# next tmp1 for R0
+	&mov($tmp1,&Np($c)) if $pos == 1;	# next tmp1 for R1
+
+	&rotl($a,$s);
+
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0)) if ($pos != 2);
+
+	&add($a,$b);
+	}
+
+sub R1
+	{
+	local($pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+
+	&comment("R1 $ki");
+
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$b); # G function - part 2
+	&and($tmp1,$d); # G function - part 3
+
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0)) if ($pos != 2);
+	&xor($tmp1,$c);			# G function - part 4
+
+	&add($a,$tmp1);
+	&mov($tmp1,&Np($c)) if $pos < 1;	# G function - part 1
+	&mov($tmp1,&Np($c)) if $pos == 1;	# G function - part 1
+
+	&rotl($a,$s);
+
+	&add($a,$b);
+	}
+
+sub R2
+	{
+	local($n,$pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+	# This one is different, only 3 logical operations
+
+if (($n & 1) == 0)
+	{
+	&comment("R2 $ki");
+	# make sure to do 'D' first, not 'B', else we clash with
+	# the last add from the previous round.
+
+	&xor($tmp1,$d); # H function - part 2
+
+	&xor($tmp1,$b); # H function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&add($a,$tmp1);
+
+	&rotl($a,$s);
+
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0));
+	&mov($tmp1,&Np($c));
+	}
+else
+	{
+	&comment("R2 $ki");
+	# make sure to do 'D' first, not 'B', else we clash with
+	# the last add from the previous round.
+
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&add($b,$c);			# MOVED FORWARD
+	&xor($tmp1,$d); # H function - part 2
+
+	&xor($tmp1,$b); # H function - part 3
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0)) if ($pos != 2);
+
+	&add($a,$tmp1);
+	&mov($tmp1,&Np($c)) if $pos < 1;	# H function - part 1
+	&mov($tmp1,-1) if $pos == 1;		# I function - part 1
+
+	&rotl($a,$s);
+
+	&add($a,$b);
+	}
+	}
+
+sub R3
+	{
+	local($pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+
+	&comment("R3 $ki");
+
+	# &not($tmp1)
+	&xor($tmp1,$d) if $pos < 0; 	# I function - part 2
+
+	&or($tmp1,$b);				# I function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$c); 			# I function - part 4
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0))	if $pos != 2; # load X/k value
+	&mov($tmp2,&wparam(0)) if $pos == 2;
+
+	&add($a,$tmp1);
+	&mov($tmp1,-1) if $pos < 1;	# H function - part 1
+	&add($K,64) if $pos >=1 && !$normal;
+
+	&rotl($a,$s);
+
+	&xor($tmp1,&Np($d)) if $pos <= 0; 	# I function - part = first time
+	&mov($tmp1,&DWP( 0,$tmp2,"",0)) if $pos > 0;
+	&add($a,$b);
+	}
+
+
+sub md5_block
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,"",3);
+
+	# parameter 1 is the MD5_CTX structure.
+	# A	0
+	# B	4
+	# C	8
+	# D 	12
+
+	&push("esi");
+	 &push("edi");
+	&mov($tmp1,	&wparam(0)); # edi
+	 &mov($X,	&wparam(1)); # esi
+	&mov($C,	&wparam(2));
+	 &push("ebp");
+	&shl($C,	6);
+	&push("ebx");
+	 &add($C,	$X); # offset we end at
+	&sub($C,	64);
+	 &mov($A,	&DWP( 0,$tmp1,"",0));
+	&push($C);	# Put on the TOS
+	 &mov($B,	&DWP( 4,$tmp1,"",0));
+	&mov($C,	&DWP( 8,$tmp1,"",0));
+	 &mov($D,	&DWP(12,$tmp1,"",0));
+
+	&set_label("start") unless $normal;
+	&comment("");
+	&comment("R0 section");
+
+	&R0(-2,$A,$B,$C,$D,$X, 0, 7,0xd76aa478);
+	&R0( 0,$D,$A,$B,$C,$X, 1,12,0xe8c7b756);
+	&R0( 0,$C,$D,$A,$B,$X, 2,17,0x242070db);
+	&R0( 0,$B,$C,$D,$A,$X, 3,22,0xc1bdceee);
+	&R0( 0,$A,$B,$C,$D,$X, 4, 7,0xf57c0faf);
+	&R0( 0,$D,$A,$B,$C,$X, 5,12,0x4787c62a);
+	&R0( 0,$C,$D,$A,$B,$X, 6,17,0xa8304613);
+	&R0( 0,$B,$C,$D,$A,$X, 7,22,0xfd469501);
+	&R0( 0,$A,$B,$C,$D,$X, 8, 7,0x698098d8);
+	&R0( 0,$D,$A,$B,$C,$X, 9,12,0x8b44f7af);
+	&R0( 0,$C,$D,$A,$B,$X,10,17,0xffff5bb1);
+	&R0( 0,$B,$C,$D,$A,$X,11,22,0x895cd7be);
+	&R0( 0,$A,$B,$C,$D,$X,12, 7,0x6b901122);
+	&R0( 0,$D,$A,$B,$C,$X,13,12,0xfd987193);
+	&R0( 0,$C,$D,$A,$B,$X,14,17,0xa679438e);
+	&R0( 1,$B,$C,$D,$A,$X,15,22,0x49b40821);
+
+	&comment("");
+	&comment("R1 section");
+	&R1(-1,$A,$B,$C,$D,$X,16, 5,0xf61e2562);
+	&R1( 0,$D,$A,$B,$C,$X,17, 9,0xc040b340);
+	&R1( 0,$C,$D,$A,$B,$X,18,14,0x265e5a51);
+	&R1( 0,$B,$C,$D,$A,$X,19,20,0xe9b6c7aa);
+	&R1( 0,$A,$B,$C,$D,$X,20, 5,0xd62f105d);
+	&R1( 0,$D,$A,$B,$C,$X,21, 9,0x02441453);
+	&R1( 0,$C,$D,$A,$B,$X,22,14,0xd8a1e681);
+	&R1( 0,$B,$C,$D,$A,$X,23,20,0xe7d3fbc8);
+	&R1( 0,$A,$B,$C,$D,$X,24, 5,0x21e1cde6);
+	&R1( 0,$D,$A,$B,$C,$X,25, 9,0xc33707d6);
+	&R1( 0,$C,$D,$A,$B,$X,26,14,0xf4d50d87);
+	&R1( 0,$B,$C,$D,$A,$X,27,20,0x455a14ed);
+	&R1( 0,$A,$B,$C,$D,$X,28, 5,0xa9e3e905);
+	&R1( 0,$D,$A,$B,$C,$X,29, 9,0xfcefa3f8);
+	&R1( 0,$C,$D,$A,$B,$X,30,14,0x676f02d9);
+	&R1( 1,$B,$C,$D,$A,$X,31,20,0x8d2a4c8a);
+
+	&comment("");
+	&comment("R2 section");
+	&R2( 0,-1,$A,$B,$C,$D,$X,32, 4,0xfffa3942);
+	&R2( 1, 0,$D,$A,$B,$C,$X,33,11,0x8771f681);
+	&R2( 2, 0,$C,$D,$A,$B,$X,34,16,0x6d9d6122);
+	&R2( 3, 0,$B,$C,$D,$A,$X,35,23,0xfde5380c);
+	&R2( 4, 0,$A,$B,$C,$D,$X,36, 4,0xa4beea44);
+	&R2( 5, 0,$D,$A,$B,$C,$X,37,11,0x4bdecfa9);
+	&R2( 6, 0,$C,$D,$A,$B,$X,38,16,0xf6bb4b60);
+	&R2( 7, 0,$B,$C,$D,$A,$X,39,23,0xbebfbc70);
+	&R2( 8, 0,$A,$B,$C,$D,$X,40, 4,0x289b7ec6);
+	&R2( 9, 0,$D,$A,$B,$C,$X,41,11,0xeaa127fa);
+	&R2(10, 0,$C,$D,$A,$B,$X,42,16,0xd4ef3085);
+	&R2(11, 0,$B,$C,$D,$A,$X,43,23,0x04881d05);
+	&R2(12, 0,$A,$B,$C,$D,$X,44, 4,0xd9d4d039);
+	&R2(13, 0,$D,$A,$B,$C,$X,45,11,0xe6db99e5);
+	&R2(14, 0,$C,$D,$A,$B,$X,46,16,0x1fa27cf8);
+	&R2(15, 1,$B,$C,$D,$A,$X,47,23,0xc4ac5665);
+
+	&comment("");
+	&comment("R3 section");
+	&R3(-1,$A,$B,$C,$D,$X,48, 6,0xf4292244);
+	&R3( 0,$D,$A,$B,$C,$X,49,10,0x432aff97);
+	&R3( 0,$C,$D,$A,$B,$X,50,15,0xab9423a7);
+	&R3( 0,$B,$C,$D,$A,$X,51,21,0xfc93a039);
+	&R3( 0,$A,$B,$C,$D,$X,52, 6,0x655b59c3);
+	&R3( 0,$D,$A,$B,$C,$X,53,10,0x8f0ccc92);
+	&R3( 0,$C,$D,$A,$B,$X,54,15,0xffeff47d);
+	&R3( 0,$B,$C,$D,$A,$X,55,21,0x85845dd1);
+	&R3( 0,$A,$B,$C,$D,$X,56, 6,0x6fa87e4f);
+	&R3( 0,$D,$A,$B,$C,$X,57,10,0xfe2ce6e0);
+	&R3( 0,$C,$D,$A,$B,$X,58,15,0xa3014314);
+	&R3( 0,$B,$C,$D,$A,$X,59,21,0x4e0811a1);
+	&R3( 0,$A,$B,$C,$D,$X,60, 6,0xf7537e82);
+	&R3( 0,$D,$A,$B,$C,$X,61,10,0xbd3af235);
+	&R3( 0,$C,$D,$A,$B,$X,62,15,0x2ad7d2bb);
+	&R3( 2,$B,$C,$D,$A,$X,63,21,0xeb86d391);
+
+	# &mov($tmp2,&wparam(0));	# done in the last R3
+	# &mov($tmp1,	&DWP( 0,$tmp2,"",0)); # done is the last R3
+
+	&add($A,$tmp1);
+	 &mov($tmp1,	&DWP( 4,$tmp2,"",0));
+
+	&add($B,$tmp1);
+	&mov($tmp1,	&DWP( 8,$tmp2,"",0));
+
+	&add($C,$tmp1);
+	&mov($tmp1,	&DWP(12,$tmp2,"",0));
+
+	&add($D,$tmp1);
+	&mov(&DWP( 0,$tmp2,"",0),$A);
+
+	&mov(&DWP( 4,$tmp2,"",0),$B);
+	&mov($tmp1,&swtmp(0)) unless $normal;
+
+	&mov(&DWP( 8,$tmp2,"",0),$C);
+	 &mov(&DWP(12,$tmp2,"",0),$D);
+
+	&cmp($tmp1,$X) unless $normal;			# check count
+	 &jae(&label("start")) unless $normal;
+
+	&pop("eax"); # pop the temp variable off the stack
+	 &pop("ebx");
+	&pop("ebp");
+	 &pop("edi");
+	&pop("esi");
+	 &ret();
+	&function_end_B($name);
+	}
+
diff --git a/crypto/md5/asm/md5-x86_64.pl b/crypto/md5/asm/md5-x86_64.pl
new file mode 100644
index 0000000..fc684e0
--- /dev/null
+++ b/crypto/md5/asm/md5-x86_64.pl
@@ -0,0 +1,370 @@
+#!/usr/bin/perl -w
+#
+# MD5 optimized for AMD64.
+#
+# Author: Marc Bevand <bevand_m (at) epita.fr>
+# Licence: I hereby disclaim the copyright on this code and place it
+# in the public domain.
+#
+
+use strict;
+
+my $code;
+
+# round1_step() does:
+#   dst = x + ((dst + F(x,y,z) + X[k] + T_i) <<< s)
+#   %r10d = X[k_next]
+#   %r11d = z' (copy of z for the next step)
+# Each round1_step() takes about 5.3 clocks (9 instructions, 1.7 IPC)
+sub round1_step
+{
+    my ($pos, $dst, $x, $y, $z, $k_next, $T_i, $s) = @_;
+    $code .= " mov	0*4(%rsi),	%r10d		/* (NEXT STEP) X[0] */\n" if ($pos == -1);
+    $code .= " mov	%edx,		%r11d		/* (NEXT STEP) z' = %edx */\n" if ($pos == -1);
+    $code .= <<EOF;
+	xor	$y,		%r11d		/* y ^ ... */
+	lea	$T_i($dst,%r10d),$dst		/* Const + dst + ... */
+	and	$x,		%r11d		/* x & ... */
+	xor	$z,		%r11d		/* z ^ ... */
+	mov	$k_next*4(%rsi),%r10d		/* (NEXT STEP) X[$k_next] */
+	add	%r11d,		$dst		/* dst += ... */
+	rol	\$$s,		$dst		/* dst <<< s */
+	mov	$y,		%r11d		/* (NEXT STEP) z' = $y */
+	add	$x,		$dst		/* dst += x */
+EOF
+}
+
+# round2_step() does:
+#   dst = x + ((dst + G(x,y,z) + X[k] + T_i) <<< s)
+#   %r10d = X[k_next]
+#   %r11d = z' (copy of z for the next step)
+#   %r12d = z' (copy of z for the next step)
+# Each round2_step() takes about 5.4 clocks (11 instructions, 2.0 IPC)
+sub round2_step
+{
+    my ($pos, $dst, $x, $y, $z, $k_next, $T_i, $s) = @_;
+    $code .= " mov	1*4(%rsi),	%r10d		/* (NEXT STEP) X[1] */\n" if ($pos == -1);
+    $code .= " mov	%edx,		%r11d		/* (NEXT STEP) z' = %edx */\n" if ($pos == -1);
+    $code .= " mov	%edx,		%r12d		/* (NEXT STEP) z' = %edx */\n" if ($pos == -1);
+    $code .= <<EOF;
+	not	%r11d				/* not z */
+	lea	$T_i($dst,%r10d),$dst		/* Const + dst + ... */
+	and	$x,		%r12d		/* x & z */
+	and	$y,		%r11d		/* y & (not z) */
+	mov	$k_next*4(%rsi),%r10d		/* (NEXT STEP) X[$k_next] */
+	or	%r11d,		%r12d		/* (y & (not z)) | (x & z) */
+	mov	$y,		%r11d		/* (NEXT STEP) z' = $y */
+	add	%r12d,		$dst		/* dst += ... */
+	mov	$y,		%r12d		/* (NEXT STEP) z' = $y */
+	rol	\$$s,		$dst		/* dst <<< s */
+	add	$x,		$dst		/* dst += x */
+EOF
+}
+
+# round3_step() does:
+#   dst = x + ((dst + H(x,y,z) + X[k] + T_i) <<< s)
+#   %r10d = X[k_next]
+#   %r11d = y' (copy of y for the next step)
+# Each round3_step() takes about 4.2 clocks (8 instructions, 1.9 IPC)
+sub round3_step
+{
+    my ($pos, $dst, $x, $y, $z, $k_next, $T_i, $s) = @_;
+    $code .= " mov	5*4(%rsi),	%r10d		/* (NEXT STEP) X[5] */\n" if ($pos == -1);
+    $code .= " mov	%ecx,		%r11d		/* (NEXT STEP) y' = %ecx */\n" if ($pos == -1);
+    $code .= <<EOF;
+	lea	$T_i($dst,%r10d),$dst		/* Const + dst + ... */
+	mov	$k_next*4(%rsi),%r10d		/* (NEXT STEP) X[$k_next] */
+	xor	$z,		%r11d		/* z ^ ... */
+	xor	$x,		%r11d		/* x ^ ... */
+	add	%r11d,		$dst		/* dst += ... */
+	rol	\$$s,		$dst		/* dst <<< s */
+	mov	$x,		%r11d		/* (NEXT STEP) y' = $x */
+	add	$x,		$dst		/* dst += x */
+EOF
+}
+
+# round4_step() does:
+#   dst = x + ((dst + I(x,y,z) + X[k] + T_i) <<< s)
+#   %r10d = X[k_next]
+#   %r11d = not z' (copy of not z for the next step)
+# Each round4_step() takes about 5.2 clocks (9 instructions, 1.7 IPC)
+sub round4_step
+{
+    my ($pos, $dst, $x, $y, $z, $k_next, $T_i, $s) = @_;
+    $code .= " mov	0*4(%rsi),	%r10d		/* (NEXT STEP) X[0] */\n" if ($pos == -1);
+    $code .= " mov	\$0xffffffff,	%r11d\n" if ($pos == -1);
+    $code .= " xor	%edx,		%r11d		/* (NEXT STEP) not z' = not %edx*/\n"
+    if ($pos == -1);
+    $code .= <<EOF;
+	lea	$T_i($dst,%r10d),$dst		/* Const + dst + ... */
+	or	$x,		%r11d		/* x | ... */
+	xor	$y,		%r11d		/* y ^ ... */
+	add	%r11d,		$dst		/* dst += ... */
+	mov	$k_next*4(%rsi),%r10d		/* (NEXT STEP) X[$k_next] */
+	mov	\$0xffffffff,	%r11d
+	rol	\$$s,		$dst		/* dst <<< s */
+	xor	$y,		%r11d		/* (NEXT STEP) not z' = not $y */
+	add	$x,		$dst		/* dst += x */
+EOF
+}
+
+my $flavour = shift;
+my $output  = shift || "";
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+my $win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; my $dir=$1; my $xlate;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+no warnings qw(uninitialized);
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$code .= <<EOF;
+.text
+.align 16
+
+.globl md5_block_asm_data_order
+.type md5_block_asm_data_order,\@function,3
+md5_block_asm_data_order:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r14
+	push	%r15
+.Lprologue:
+
+	# rdi = arg #1 (ctx, MD5_CTX pointer)
+	# rsi = arg #2 (ptr, data pointer)
+	# rdx = arg #3 (nbr, number of 16-word blocks to process)
+	mov	%rdi,		%rbp	# rbp = ctx
+	shl	\$6,		%rdx	# rdx = nbr in bytes
+	lea	(%rsi,%rdx),	%rdi	# rdi = end
+	mov	0*4(%rbp),	%eax	# eax = ctx->A
+	mov	1*4(%rbp),	%ebx	# ebx = ctx->B
+	mov	2*4(%rbp),	%ecx	# ecx = ctx->C
+	mov	3*4(%rbp),	%edx	# edx = ctx->D
+	# end is 'rdi'
+	# ptr is 'rsi'
+	# A is 'eax'
+	# B is 'ebx'
+	# C is 'ecx'
+	# D is 'edx'
+
+	cmp	%rdi,		%rsi		# cmp end with ptr
+	je	.Lend				# jmp if ptr == end
+
+	# BEGIN of loop over 16-word blocks
+.Lloop:	# save old values of A, B, C, D
+	mov	%eax,		%r8d
+	mov	%ebx,		%r9d
+	mov	%ecx,		%r14d
+	mov	%edx,		%r15d
+EOF
+round1_step(-1,'%eax','%ebx','%ecx','%edx', '1','0xd76aa478', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx', '2','0xe8c7b756','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx', '3','0x242070db','17');
+round1_step( 0,'%ebx','%ecx','%edx','%eax', '4','0xc1bdceee','22');
+round1_step( 0,'%eax','%ebx','%ecx','%edx', '5','0xf57c0faf', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx', '6','0x4787c62a','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx', '7','0xa8304613','17');
+round1_step( 0,'%ebx','%ecx','%edx','%eax', '8','0xfd469501','22');
+round1_step( 0,'%eax','%ebx','%ecx','%edx', '9','0x698098d8', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx','10','0x8b44f7af','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx','11','0xffff5bb1','17');
+round1_step( 0,'%ebx','%ecx','%edx','%eax','12','0x895cd7be','22');
+round1_step( 0,'%eax','%ebx','%ecx','%edx','13','0x6b901122', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx','14','0xfd987193','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx','15','0xa679438e','17');
+round1_step( 1,'%ebx','%ecx','%edx','%eax', '0','0x49b40821','22');
+
+round2_step(-1,'%eax','%ebx','%ecx','%edx', '6','0xf61e2562', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx','11','0xc040b340', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx', '0','0x265e5a51','14');
+round2_step( 0,'%ebx','%ecx','%edx','%eax', '5','0xe9b6c7aa','20');
+round2_step( 0,'%eax','%ebx','%ecx','%edx','10','0xd62f105d', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx','15', '0x2441453', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx', '4','0xd8a1e681','14');
+round2_step( 0,'%ebx','%ecx','%edx','%eax', '9','0xe7d3fbc8','20');
+round2_step( 0,'%eax','%ebx','%ecx','%edx','14','0x21e1cde6', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx', '3','0xc33707d6', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx', '8','0xf4d50d87','14');
+round2_step( 0,'%ebx','%ecx','%edx','%eax','13','0x455a14ed','20');
+round2_step( 0,'%eax','%ebx','%ecx','%edx', '2','0xa9e3e905', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx', '7','0xfcefa3f8', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx','12','0x676f02d9','14');
+round2_step( 1,'%ebx','%ecx','%edx','%eax', '0','0x8d2a4c8a','20');
+
+round3_step(-1,'%eax','%ebx','%ecx','%edx', '8','0xfffa3942', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx','11','0x8771f681','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx','14','0x6d9d6122','16');
+round3_step( 0,'%ebx','%ecx','%edx','%eax', '1','0xfde5380c','23');
+round3_step( 0,'%eax','%ebx','%ecx','%edx', '4','0xa4beea44', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx', '7','0x4bdecfa9','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx','10','0xf6bb4b60','16');
+round3_step( 0,'%ebx','%ecx','%edx','%eax','13','0xbebfbc70','23');
+round3_step( 0,'%eax','%ebx','%ecx','%edx', '0','0x289b7ec6', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx', '3','0xeaa127fa','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx', '6','0xd4ef3085','16');
+round3_step( 0,'%ebx','%ecx','%edx','%eax', '9', '0x4881d05','23');
+round3_step( 0,'%eax','%ebx','%ecx','%edx','12','0xd9d4d039', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx','15','0xe6db99e5','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx', '2','0x1fa27cf8','16');
+round3_step( 1,'%ebx','%ecx','%edx','%eax', '0','0xc4ac5665','23');
+
+round4_step(-1,'%eax','%ebx','%ecx','%edx', '7','0xf4292244', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx','14','0x432aff97','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx', '5','0xab9423a7','15');
+round4_step( 0,'%ebx','%ecx','%edx','%eax','12','0xfc93a039','21');
+round4_step( 0,'%eax','%ebx','%ecx','%edx', '3','0x655b59c3', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx','10','0x8f0ccc92','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx', '1','0xffeff47d','15');
+round4_step( 0,'%ebx','%ecx','%edx','%eax', '8','0x85845dd1','21');
+round4_step( 0,'%eax','%ebx','%ecx','%edx','15','0x6fa87e4f', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx', '6','0xfe2ce6e0','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx','13','0xa3014314','15');
+round4_step( 0,'%ebx','%ecx','%edx','%eax', '4','0x4e0811a1','21');
+round4_step( 0,'%eax','%ebx','%ecx','%edx','11','0xf7537e82', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx', '2','0xbd3af235','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx', '9','0x2ad7d2bb','15');
+round4_step( 1,'%ebx','%ecx','%edx','%eax', '0','0xeb86d391','21');
+$code .= <<EOF;
+	# add old values of A, B, C, D
+	add	%r8d,	%eax
+	add	%r9d,	%ebx
+	add	%r14d,	%ecx
+	add	%r15d,	%edx
+
+	# loop control
+	add	\$64,		%rsi		# ptr += 64
+	cmp	%rdi,		%rsi		# cmp end with ptr
+	jb	.Lloop				# jmp if ptr < end
+	# END of loop over 16-word blocks
+
+.Lend:
+	mov	%eax,		0*4(%rbp)	# ctx->A = A
+	mov	%ebx,		1*4(%rbp)	# ctx->B = B
+	mov	%ecx,		2*4(%rbp)	# ctx->C = C
+	mov	%edx,		3*4(%rbp)	# ctx->D = D
+
+	mov	(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r12
+	mov	24(%rsp),%rbx
+	mov	32(%rsp),%rbp
+	add	\$40,%rsp
+.Lepilogue:
+	ret
+.size md5_block_asm_data_order,.-md5_block_asm_data_order
+EOF
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+my $rec="%rcx";
+my $frame="%rdx";
+my $context="%r8";
+my $disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	lea	40(%rax),%rax
+
+	mov	-8(%rax),%rbp
+	mov	-16(%rax),%rbx
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r14
+	mov	-40(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_md5_block_asm_data_order
+	.rva	.LSEH_end_md5_block_asm_data_order
+	.rva	.LSEH_info_md5_block_asm_data_order
+
+.section	.xdata
+.align	8
+.LSEH_info_md5_block_asm_data_order:
+	.byte	9,0,0,0
+	.rva	se_handler
+___
+}
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/md5/md5.c b/crypto/md5/md5.c
new file mode 100644
index 0000000..e20b86b
--- /dev/null
+++ b/crypto/md5/md5.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/md5.h>
+
+#include <string.h>
+
+#include <openssl/mem.h>
+
+
+uint8_t *MD5(const uint8_t *data, size_t len, uint8_t *out) {
+  MD5_CTX ctx;
+  static uint8_t digest[MD5_DIGEST_LENGTH];
+
+  /* TODO(fork): remove this static buffer. */
+  if (out == NULL) {
+    out = digest;
+  }
+
+  MD5_Init(&ctx);
+  MD5_Update(&ctx, data, len);
+  MD5_Final(out, &ctx);
+
+  return out;
+}
+
+int MD5_Init(MD5_CTX *md5) {
+  memset(md5, 0, sizeof(MD5_CTX));
+  md5->A = 0x67452301UL;
+  md5->B = 0xefcdab89UL;
+  md5->C = 0x98badcfeUL;
+  md5->D = 0x10325476UL;
+  return 1;
+}
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86_64) || defined(OPENSSL_X86))
+#define MD5_ASM
+#define md5_block_data_order md5_block_asm_data_order
+#endif
+
+
+void md5_block_data_order(MD5_CTX *md5, const void *p, size_t num);
+
+#define DATA_ORDER_IS_LITTLE_ENDIAN
+
+#define HASH_LONG uint32_t
+#define HASH_CTX MD5_CTX
+#define HASH_CBLOCK 64
+#define HASH_UPDATE MD5_Update
+#define HASH_TRANSFORM MD5_Transform
+#define HASH_FINAL MD5_Final
+#define HASH_MAKE_STRING(c, s) \
+  do {                         \
+    unsigned long ll;          \
+    ll = (c)->A;               \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->B;               \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->C;               \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->D;               \
+    (void) HOST_l2c(ll, (s));  \
+  } while (0)
+#define HASH_BLOCK_DATA_ORDER md5_block_data_order
+
+#include "../digest/md32_common.h"
+
+/* As pointed out by Wei Dai <weidai@eskimo.com>, the above can be
+ * simplified to the code below.  Wei attributes these optimizations
+ * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel.
+ */
+#define	F(b,c,d)	((((c) ^ (d)) & (b)) ^ (d))
+#define	G(b,c,d)	((((b) ^ (c)) & (d)) ^ (c))
+#define	H(b,c,d)	((b) ^ (c) ^ (d))
+#define	I(b,c,d)	(((~(d)) | (b)) ^ (c))
+
+#define R0(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+F((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };\
+
+#define R1(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+G((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };
+
+#define R2(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+H((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };
+
+#define R3(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+I((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };
+
+#ifndef md5_block_data_order
+#ifdef X
+#undef X
+#endif
+void md5_block_data_order(MD5_CTX *md5, const void *in_data, size_t num) {
+  const uint8_t *data = in_data;
+  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;
+#define X(i) XX##i
+
+  A = md5->A;
+  B = md5->B;
+  C = md5->C;
+  D = md5->D;
+
+  for (; num--;) {
+    HOST_c2l(data, l);
+    X(0) = l;
+    HOST_c2l(data, l);
+    X(1) = l;
+    /* Round 0 */
+    R0(A, B, C, D, X(0), 7, 0xd76aa478L);
+    HOST_c2l(data, l);
+    X(2) = l;
+    R0(D, A, B, C, X(1), 12, 0xe8c7b756L);
+    HOST_c2l(data, l);
+    X(3) = l;
+    R0(C, D, A, B, X(2), 17, 0x242070dbL);
+    HOST_c2l(data, l);
+    X(4) = l;
+    R0(B, C, D, A, X(3), 22, 0xc1bdceeeL);
+    HOST_c2l(data, l);
+    X(5) = l;
+    R0(A, B, C, D, X(4), 7, 0xf57c0fafL);
+    HOST_c2l(data, l);
+    X(6) = l;
+    R0(D, A, B, C, X(5), 12, 0x4787c62aL);
+    HOST_c2l(data, l);
+    X(7) = l;
+    R0(C, D, A, B, X(6), 17, 0xa8304613L);
+    HOST_c2l(data, l);
+    X(8) = l;
+    R0(B, C, D, A, X(7), 22, 0xfd469501L);
+    HOST_c2l(data, l);
+    X(9) = l;
+    R0(A, B, C, D, X(8), 7, 0x698098d8L);
+    HOST_c2l(data, l);
+    X(10) = l;
+    R0(D, A, B, C, X(9), 12, 0x8b44f7afL);
+    HOST_c2l(data, l);
+    X(11) = l;
+    R0(C, D, A, B, X(10), 17, 0xffff5bb1L);
+    HOST_c2l(data, l);
+    X(12) = l;
+    R0(B, C, D, A, X(11), 22, 0x895cd7beL);
+    HOST_c2l(data, l);
+    X(13) = l;
+    R0(A, B, C, D, X(12), 7, 0x6b901122L);
+    HOST_c2l(data, l);
+    X(14) = l;
+    R0(D, A, B, C, X(13), 12, 0xfd987193L);
+    HOST_c2l(data, l);
+    X(15) = l;
+    R0(C, D, A, B, X(14), 17, 0xa679438eL);
+    R0(B, C, D, A, X(15), 22, 0x49b40821L);
+    /* Round 1 */
+    R1(A, B, C, D, X(1), 5, 0xf61e2562L);
+    R1(D, A, B, C, X(6), 9, 0xc040b340L);
+    R1(C, D, A, B, X(11), 14, 0x265e5a51L);
+    R1(B, C, D, A, X(0), 20, 0xe9b6c7aaL);
+    R1(A, B, C, D, X(5), 5, 0xd62f105dL);
+    R1(D, A, B, C, X(10), 9, 0x02441453L);
+    R1(C, D, A, B, X(15), 14, 0xd8a1e681L);
+    R1(B, C, D, A, X(4), 20, 0xe7d3fbc8L);
+    R1(A, B, C, D, X(9), 5, 0x21e1cde6L);
+    R1(D, A, B, C, X(14), 9, 0xc33707d6L);
+    R1(C, D, A, B, X(3), 14, 0xf4d50d87L);
+    R1(B, C, D, A, X(8), 20, 0x455a14edL);
+    R1(A, B, C, D, X(13), 5, 0xa9e3e905L);
+    R1(D, A, B, C, X(2), 9, 0xfcefa3f8L);
+    R1(C, D, A, B, X(7), 14, 0x676f02d9L);
+    R1(B, C, D, A, X(12), 20, 0x8d2a4c8aL);
+    /* Round 2 */
+    R2(A, B, C, D, X(5), 4, 0xfffa3942L);
+    R2(D, A, B, C, X(8), 11, 0x8771f681L);
+    R2(C, D, A, B, X(11), 16, 0x6d9d6122L);
+    R2(B, C, D, A, X(14), 23, 0xfde5380cL);
+    R2(A, B, C, D, X(1), 4, 0xa4beea44L);
+    R2(D, A, B, C, X(4), 11, 0x4bdecfa9L);
+    R2(C, D, A, B, X(7), 16, 0xf6bb4b60L);
+    R2(B, C, D, A, X(10), 23, 0xbebfbc70L);
+    R2(A, B, C, D, X(13), 4, 0x289b7ec6L);
+    R2(D, A, B, C, X(0), 11, 0xeaa127faL);
+    R2(C, D, A, B, X(3), 16, 0xd4ef3085L);
+    R2(B, C, D, A, X(6), 23, 0x04881d05L);
+    R2(A, B, C, D, X(9), 4, 0xd9d4d039L);
+    R2(D, A, B, C, X(12), 11, 0xe6db99e5L);
+    R2(C, D, A, B, X(15), 16, 0x1fa27cf8L);
+    R2(B, C, D, A, X(2), 23, 0xc4ac5665L);
+    /* Round 3 */
+    R3(A, B, C, D, X(0), 6, 0xf4292244L);
+    R3(D, A, B, C, X(7), 10, 0x432aff97L);
+    R3(C, D, A, B, X(14), 15, 0xab9423a7L);
+    R3(B, C, D, A, X(5), 21, 0xfc93a039L);
+    R3(A, B, C, D, X(12), 6, 0x655b59c3L);
+    R3(D, A, B, C, X(3), 10, 0x8f0ccc92L);
+    R3(C, D, A, B, X(10), 15, 0xffeff47dL);
+    R3(B, C, D, A, X(1), 21, 0x85845dd1L);
+    R3(A, B, C, D, X(8), 6, 0x6fa87e4fL);
+    R3(D, A, B, C, X(15), 10, 0xfe2ce6e0L);
+    R3(C, D, A, B, X(6), 15, 0xa3014314L);
+    R3(B, C, D, A, X(13), 21, 0x4e0811a1L);
+    R3(A, B, C, D, X(4), 6, 0xf7537e82L);
+    R3(D, A, B, C, X(11), 10, 0xbd3af235L);
+    R3(C, D, A, B, X(2), 15, 0x2ad7d2bbL);
+    R3(B, C, D, A, X(9), 21, 0xeb86d391L);
+
+    A = md5->A += A;
+    B = md5->B += B;
+    C = md5->C += C;
+    D = md5->D += D;
+  }
+}
+#endif
diff --git a/crypto/md5/md5.h b/crypto/md5/md5.h
new file mode 100644
index 0000000..7a1a00f
--- /dev/null
+++ b/crypto/md5/md5.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_MD5_H
+#define OPENSSL_HEADER_MD5_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* MD5. */
+
+
+/* MD5_CBLOCK is the block size of MD5. */
+#define MD5_CBLOCK 64
+
+/* MD5_DIGEST_LENGTH is the length of an MD5 digest. */
+#define MD5_DIGEST_LENGTH 16
+
+/* MD51_Init initialises |md5| and returns 1. */
+int MD5_Init(MD5_CTX *md5);
+
+/* MD5_Update adds |len| bytes from |data| to |md5| and returns one. */
+int MD5_Update(MD5_CTX *md5, const void *data, size_t len);
+
+/* MD5_Final adds the final padding to |md5| and writes the resulting digest to
+ * |md|, which must have at least |MD5_DIGEST_LENGTH| bytes of space. It
+ * returns one. */
+int MD5_Final(uint8_t *md, MD5_CTX *md5);
+
+/* MD5 writes the digest of |len| bytes from |data| to |out| and returns |out|.
+ * There must be at least |MD5_DIGEST_LENGTH| bytes of space in |out|. */
+uint8_t *MD5(const uint8_t *data, size_t len, uint8_t *out);
+
+/* MD5_Transform is a low-level function that performs a single, MD5 block
+ * transformation using the state from |md5| and 64 bytes from |block|. */
+void MD5_Transform(MD5_CTX *md5, const uint8_t *block);
+
+struct md5_state_st {
+  uint32_t A, B, C, D;
+  uint32_t Nl, Nh;
+  uint32_t data[16];
+  unsigned int num;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_MD5_H */
diff --git a/crypto/md5/md5_test.c b/crypto/md5/md5_test.c
new file mode 100644
index 0000000..eb5984c
--- /dev/null
+++ b/crypto/md5/md5_test.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/md5.h>
+#include <openssl/digest.h>
+
+
+static const char *const test[] = {
+    "", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz",
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+    "12345678901234567890123456789012345678901234567890123456789012345678901234"
+    "567890",
+    NULL, };
+
+static const char *const expected[] = {
+    "d41d8cd98f00b204e9800998ecf8427e", "0cc175b9c0f1b6a831c399e269772661",
+    "900150983cd24fb0d6963f7d28e17f72", "f96b697d7cb7938d525a2f31aaf161d0",
+    "c3fcd3d76192e4007dfb496cca67e13b", "d174ab98d277d9f5a5611c2c9f419d9f",
+    "57edf4a22be3c955ac49da2e2107b67a", };
+
+int main(int argc, char **argv) {
+  size_t i, j;
+  uint8_t md[MD5_DIGEST_LENGTH];
+  char md_hex[sizeof(md) * 2 + 1];
+  int ok = 1;
+
+  for (i = 0; test[i] != NULL; i++) {
+    EVP_Digest(test[i], strlen(test[i]), md, NULL, EVP_md5(), NULL);
+    for (j = 0; j < sizeof(md); j++) {
+      sprintf(&md_hex[j * 2], "%02x", md[j]);
+    }
+    md_hex[MD5_DIGEST_LENGTH * 2] = 0;
+
+    if (strcmp(md_hex, expected[i]) != 0) {
+      fprintf(stderr, "#%u: got %s, wanted %s\n", (unsigned)i, md_hex,
+              expected[i]);
+      ok = 0;
+    }
+  }
+
+  if (ok) {
+    printf("PASS\n");
+  }
+
+  return ok ? 0 : 1;
+}
diff --git a/crypto/mem.c b/crypto/mem.c
new file mode 100644
index 0000000..8a8482c
--- /dev/null
+++ b/crypto/mem.c
@@ -0,0 +1,140 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#define _BSD_SOURCE /* needed for strdup, snprintf, vprintf etc */
+
+#include <openssl/mem.h>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+
+void *OPENSSL_realloc_clean(void *ptr, size_t old_size, size_t new_size) {
+  void *ret = NULL;
+
+  if (ptr == NULL) {
+    return OPENSSL_malloc(new_size);
+  }
+
+  if (new_size == 0) {
+    return NULL;
+  }
+
+  /* We don't support shrinking the buffer. Note the memcpy that copies
+   * |old_size| bytes to the new buffer, below. */
+  if (new_size < old_size) {
+    return NULL;
+  }
+
+  ret = OPENSSL_malloc(new_size);
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  memcpy(ret, ptr, old_size);
+  OPENSSL_cleanse(ptr, old_size);
+  OPENSSL_free(ptr);
+  return ret;
+}
+
+int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len) {
+  size_t i;
+  const uint8_t *a = in_a;
+  const uint8_t *b = in_b;
+  uint8_t x = 0;
+
+  for (i = 0; i < len; i++) {
+    x |= a[i] ^ b[i];
+  }
+
+  return x;
+}
+
+uint32_t OPENSSL_hash32(const void *ptr, size_t len) {
+  /* These are the FNV-1a parameters for 32 bits. */
+  static const uint32_t kPrime = 16777619u;
+  static const uint32_t kOffsetBasis = 2166136261u;
+
+  const uint8_t *in = ptr;
+  size_t i;
+  uint32_t h = kOffsetBasis;
+
+  for (i = 0; i < len; i++) {
+    h ^= in[i];
+    h *= kPrime;
+  }
+
+  return h;
+}
+
+char *OPENSSL_strdup(const char *s) { return strdup(s); }
+
+int BIO_snprintf(char *buf, size_t n, const char *format, ...) {
+  va_list args;
+  int ret;
+
+  va_start(args, format);
+
+  ret = BIO_vsnprintf(buf, n, format, args);
+
+  va_end(args);
+  return ret;
+}
+
+int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) {
+  return vsnprintf(buf, n, format, args);
+}
diff --git a/crypto/mem.h b/crypto/mem.h
new file mode 100644
index 0000000..134fef8
--- /dev/null
+++ b/crypto/mem.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_MEM_H
+#define OPENSSL_HEADER_MEM_H
+
+#include <openssl/base.h>
+
+#include <stdarg.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* OpenSSL has, historically, had a complex set of malloc debugging options.
+ * However, that was written in a time before Valgrind and ASAN. Since we now
+ * have those tools, the OpenSSL allocation functions are simply macros around
+ * the standard memory functions. */
+
+
+#define OPENSSL_malloc malloc
+#define OPENSSL_realloc realloc
+#define OPENSSL_free free
+
+/* OPENSSL_realloc_clean acts like |realloc|, but clears the previous memory
+ * buffer.  Because this is implemented as a wrapper around |malloc|, it needs
+ * to be given the size of the buffer pointed to by |ptr|. */
+void *OPENSSL_realloc_clean(void *ptr, size_t old_size, size_t new_size);
+
+/* OPENSSL_cleanse zeros out |len| bytes of memory at |ptr|. This is similar to
+ * |memset_s| from C11. */
+void OPENSSL_cleanse(void *ptr, size_t len);
+
+/* CRYPTO_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
+ * takes an amount of time dependent on |len|, but independent of the contents
+ * of |a| and |b|. Unlike memcmp, it cannot be used to put elements into a
+ * defined order as the return value when a != b is undefined, other than to be
+ * non-zero. */
+int CRYPTO_memcmp(const void *a, const void *b, size_t len);
+
+/* OPENSSL_hash32 implements the 32 bit, FNV-1a hash. */
+uint32_t OPENSSL_hash32(const void *ptr, size_t len);
+
+/* OPENSSL_strdup has the same behaviour as strdup(3). */
+char *OPENSSL_strdup(const char *s);
+
+/* DECIMAL_SIZE returns an upper bound for the length of the decimal
+ * representation of the given type. */
+#define DECIMAL_SIZE(type)	((sizeof(type)*8+2)/3+1)
+
+/* Printf functions.
+ *
+ * These functions are either OpenSSL wrappers for standard functions (i.e.
+ * |BIO_snprintf| and |BIO_vsnprintf|) which don't exist in C89, or are
+ * versions of printf functions that output to a BIO rather than a FILE. */
+#ifdef __GNUC__
+#define __bio_h__attr__ __attribute__
+#else
+#define __bio_h__attr__(x)
+#endif
+int BIO_snprintf(char *buf, size_t n, const char *format, ...)
+    __bio_h__attr__((__format__(__printf__, 3, 4)));
+
+int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args)
+    __bio_h__attr__((__format__(__printf__, 3, 0)));
+#undef __bio_h__attr__
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_MEM_H */
diff --git a/crypto/mem_clear.c b/crypto/mem_clear.c
new file mode 100644
index 0000000..c13ecd8
--- /dev/null
+++ b/crypto/mem_clear.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/mem.h>
+
+#include <string.h>
+
+
+#if !defined(OPENSSL_X86)
+
+/* OPENSSL_cleanse is given its own compilation unit in the hopes that the
+ * compiler won't be able to optimise it away. */
+/* TODO(fork): this is fragile. Preferably we would be able to use memset_s
+ * on platforms with it. */
+void OPENSSL_cleanse(void *ptr, size_t len) { memset(ptr, 0, len); }
+
+#endif
diff --git a/crypto/modes/CMakeLists.txt b/crypto/modes/CMakeLists.txt
new file mode 100644
index 0000000..4b18e3a
--- /dev/null
+++ b/crypto/modes/CMakeLists.txt
@@ -0,0 +1,53 @@
+include_directories(. .. ../../include)
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		MODES_ARCH_SOURCES
+
+		aesni-gcm-x86_64.${ASM_EXT}
+		ghash-x86_64.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		MODES_ARCH_SOURCES
+
+		ghash-x86.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "arm")
+	set(
+		MODES_ARCH_SOURCES
+
+		ghash-armv4.${ASM_EXT}
+	)
+endif()
+
+add_library(
+	modes
+
+	OBJECT
+
+	cbc.c
+	ctr.c
+	ofb.c
+	cfb.c
+	gcm.c
+
+	${MODES_ARCH_SOURCES}
+)
+
+perlasm(aesni-gcm-x86_64.${ASM_EXT} asm/aesni-gcm-x86_64.pl)
+perlasm(ghash-x86_64.${ASM_EXT} asm/ghash-x86_64.pl)
+perlasm(ghash-x86.${ASM_EXT} asm/ghash-x86.pl)
+perlasm(ghash-armv4.${ASM_EXT} asm/ghash-armv4.pl)
+
+add_executable(
+	gcm_test
+
+	gcm_test.c
+)
+
+target_link_libraries(gcm_test crypto)
diff --git a/crypto/modes/asm/aesni-gcm-x86_64.pl b/crypto/modes/asm/aesni-gcm-x86_64.pl
new file mode 100644
index 0000000..497b6e5
--- /dev/null
+++ b/crypto/modes/asm/aesni-gcm-x86_64.pl
@@ -0,0 +1,1050 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+#
+# AES-NI-CTR+GHASH stitch.
+#
+# February 2013
+#
+# OpenSSL GCM implementation is organized in such way that its
+# performance is rather close to the sum of its streamed components,
+# in the context parallelized AES-NI CTR and modulo-scheduled
+# PCLMULQDQ-enabled GHASH. Unfortunately, as no stitch implementation
+# was observed to perform significantly better than the sum of the
+# components on contemporary CPUs, the effort was deemed impossible to
+# justify. This module is based on combination of Intel submissions,
+# [1] and [2], with MOVBE twist suggested by Ilya Albrekht and Max
+# Locktyukhin of Intel Corp. who verified that it reduces shuffles
+# pressure with notable relative improvement, achieving 1.0 cycle per
+# byte processed with 128-bit key on Haswell processor.
+#
+# [1] http://rt.openssl.org/Ticket/Display.html?id=2900&user=guest&pass=guest
+# [2] http://www.intel.com/content/dam/www/public/us/en/documents/software-support/enabling-high-performance-gcm.pdf
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+if ($avx>1) {{{
+
+($inp,$out,$len,$key,$ivp,$Xip)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9");
+
+($Ii,$T1,$T2,$Hkey,
+ $Z0,$Z1,$Z2,$Z3,$Xi) = map("%xmm$_",(0..8));
+
+($inout0,$inout1,$inout2,$inout3,$inout4,$inout5,$rndkey) = map("%xmm$_",(9..15));
+
+($counter,$rounds,$ret,$const,$in0,$end0)=("%ebx","%ebp","%r10","%r11","%r14","%r15");
+
+$code=<<___;
+.text
+
+.type	_aesni_ctr32_ghash_6x,\@abi-omnipotent
+.align	32
+_aesni_ctr32_ghash_6x:
+	vmovdqu		0x20($const),$T2	# borrow $T2, .Lone_msb
+	sub		\$6,$len
+	vpxor		$Z0,$Z0,$Z0		# $Z0   = 0
+	vmovdqu		0x00-0x80($key),$rndkey
+	vpaddb		$T2,$T1,$inout1
+	vpaddb		$T2,$inout1,$inout2
+	vpaddb		$T2,$inout2,$inout3
+	vpaddb		$T2,$inout3,$inout4
+	vpaddb		$T2,$inout4,$inout5
+	vpxor		$rndkey,$T1,$inout0
+	vmovdqu		$Z0,16+8(%rsp)		# "$Z3" = 0
+	jmp		.Loop6x
+
+.align	32
+.Loop6x:
+	add		\$100663296,$counter
+	jc		.Lhandle_ctr32		# discard $inout[1-5]?
+	vmovdqu		0x00-0x20($Xip),$Hkey	# $Hkey^1
+	  vpaddb	$T2,$inout5,$T1		# next counter value
+	  vpxor		$rndkey,$inout1,$inout1
+	  vpxor		$rndkey,$inout2,$inout2
+
+.Lresume_ctr32:
+	vmovdqu		$T1,($ivp)		# save next counter value
+	vpclmulqdq	\$0x10,$Hkey,$Z3,$Z1
+	  vpxor		$rndkey,$inout3,$inout3
+	  vmovups	0x10-0x80($key),$T2	# borrow $T2 for $rndkey
+	vpclmulqdq	\$0x01,$Hkey,$Z3,$Z2
+	xor		%r12,%r12
+	cmp		$in0,$end0
+
+	  vaesenc	$T2,$inout0,$inout0
+	vmovdqu		0x30+8(%rsp),$Ii	# I[4]
+	  vpxor		$rndkey,$inout4,$inout4
+	vpclmulqdq	\$0x00,$Hkey,$Z3,$T1
+	  vaesenc	$T2,$inout1,$inout1
+	  vpxor		$rndkey,$inout5,$inout5
+	setnc		%r12b
+	vpclmulqdq	\$0x11,$Hkey,$Z3,$Z3
+	  vaesenc	$T2,$inout2,$inout2
+	vmovdqu		0x10-0x20($Xip),$Hkey	# $Hkey^2
+	neg		%r12
+	  vaesenc	$T2,$inout3,$inout3
+	 vpxor		$Z1,$Z2,$Z2
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Z1
+	 vpxor		$Z0,$Xi,$Xi		# modulo-scheduled
+	  vaesenc	$T2,$inout4,$inout4
+	 vpxor		$Z1,$T1,$Z0
+	and		\$0x60,%r12
+	  vmovups	0x20-0x80($key),$rndkey
+	vpclmulqdq	\$0x10,$Hkey,$Ii,$T1
+	  vaesenc	$T2,$inout5,$inout5
+
+	vpclmulqdq	\$0x01,$Hkey,$Ii,$T2
+	lea		($in0,%r12),$in0
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		16+8(%rsp),$Xi,$Xi	# modulo-scheduled [vpxor $Z3,$Xi,$Xi]
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Hkey
+	 vmovdqu	0x40+8(%rsp),$Ii	# I[3]
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x58($in0),%r13
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x50($in0),%r12
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x20+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x28+8(%rsp)
+	vmovdqu		0x30-0x20($Xip),$Z1	# borrow $Z1 for $Hkey^3
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x30-0x80($key),$rndkey
+	 vpxor		$T1,$Z2,$Z2
+	vpclmulqdq	\$0x00,$Z1,$Ii,$T1
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$T2,$Z2,$Z2
+	vpclmulqdq	\$0x10,$Z1,$Ii,$T2
+	  vaesenc	$rndkey,$inout1,$inout1
+	 vpxor		$Hkey,$Z3,$Z3
+	vpclmulqdq	\$0x01,$Z1,$Ii,$Hkey
+	  vaesenc	$rndkey,$inout2,$inout2
+	vpclmulqdq	\$0x11,$Z1,$Ii,$Z1
+	 vmovdqu	0x50+8(%rsp),$Ii	# I[2]
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vaesenc	$rndkey,$inout4,$inout4
+	 vpxor		$T1,$Z0,$Z0
+	vmovdqu		0x40-0x20($Xip),$T1	# borrow $T1 for $Hkey^4
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x40-0x80($key),$rndkey
+	 vpxor		$T2,$Z2,$Z2
+	vpclmulqdq	\$0x00,$T1,$Ii,$T2
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$Hkey,$Z2,$Z2
+	vpclmulqdq	\$0x10,$T1,$Ii,$Hkey
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x48($in0),%r13
+	 vpxor		$Z1,$Z3,$Z3
+	vpclmulqdq	\$0x01,$T1,$Ii,$Z1
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x40($in0),%r12
+	vpclmulqdq	\$0x11,$T1,$Ii,$T1
+	 vmovdqu	0x60+8(%rsp),$Ii	# I[1]
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x30+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x38+8(%rsp)
+	 vpxor		$T2,$Z0,$Z0
+	vmovdqu		0x60-0x20($Xip),$T2	# borrow $T2 for $Hkey^5
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x50-0x80($key),$rndkey
+	 vpxor		$Hkey,$Z2,$Z2
+	vpclmulqdq	\$0x00,$T2,$Ii,$Hkey
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$Z1,$Z2,$Z2
+	vpclmulqdq	\$0x10,$T2,$Ii,$Z1
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x38($in0),%r13
+	 vpxor		$T1,$Z3,$Z3
+	vpclmulqdq	\$0x01,$T2,$Ii,$T1
+	 vpxor		0x70+8(%rsp),$Xi,$Xi	# accumulate I[0]
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x30($in0),%r12
+	vpclmulqdq	\$0x11,$T2,$Ii,$T2
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x40+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x48+8(%rsp)
+	 vpxor		$Hkey,$Z0,$Z0
+	 vmovdqu	0x70-0x20($Xip),$Hkey	# $Hkey^6
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x60-0x80($key),$rndkey
+	 vpxor		$Z1,$Z2,$Z2
+	vpclmulqdq	\$0x10,$Hkey,$Xi,$Z1
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$T1,$Z2,$Z2
+	vpclmulqdq	\$0x01,$Hkey,$Xi,$T1
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x28($in0),%r13
+	 vpxor		$T2,$Z3,$Z3
+	vpclmulqdq	\$0x00,$Hkey,$Xi,$T2
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x20($in0),%r12
+	vpclmulqdq	\$0x11,$Hkey,$Xi,$Xi
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x50+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x58+8(%rsp)
+	vpxor		$Z1,$Z2,$Z2
+	  vaesenc	$rndkey,$inout5,$inout5
+	vpxor		$T1,$Z2,$Z2
+
+	  vmovups	0x70-0x80($key),$rndkey
+	vpslldq		\$8,$Z2,$Z1
+	vpxor		$T2,$Z0,$Z0
+	vmovdqu		0x10($const),$Hkey	# .Lpoly
+
+	  vaesenc	$rndkey,$inout0,$inout0
+	vpxor		$Xi,$Z3,$Z3
+	  vaesenc	$rndkey,$inout1,$inout1
+	vpxor		$Z1,$Z0,$Z0
+	movbe		0x18($in0),%r13
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x10($in0),%r12
+	vpalignr	\$8,$Z0,$Z0,$Ii		# 1st phase
+	vpclmulqdq	\$0x10,$Hkey,$Z0,$Z0
+	mov		%r13,0x60+8(%rsp)
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r12,0x68+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vmovups	0x80-0x80($key),$T1	# borrow $T1 for $rndkey
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vaesenc	$T1,$inout0,$inout0
+	  vmovups	0x90-0x80($key),$rndkey
+	  vaesenc	$T1,$inout1,$inout1
+	vpsrldq		\$8,$Z2,$Z2
+	  vaesenc	$T1,$inout2,$inout2
+	vpxor		$Z2,$Z3,$Z3
+	  vaesenc	$T1,$inout3,$inout3
+	vpxor		$Ii,$Z0,$Z0
+	movbe		0x08($in0),%r13
+	  vaesenc	$T1,$inout4,$inout4
+	movbe		0x00($in0),%r12
+	  vaesenc	$T1,$inout5,$inout5
+	  vmovups	0xa0-0x80($key),$T1
+	  cmp		\$11,$rounds
+	  jb		.Lenc_tail		# 128-bit key
+
+	  vaesenc	$rndkey,$inout0,$inout0
+	  vaesenc	$rndkey,$inout1,$inout1
+	  vaesenc	$rndkey,$inout2,$inout2
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vaesenc	$T1,$inout0,$inout0
+	  vaesenc	$T1,$inout1,$inout1
+	  vaesenc	$T1,$inout2,$inout2
+	  vaesenc	$T1,$inout3,$inout3
+	  vaesenc	$T1,$inout4,$inout4
+	  vmovups	0xb0-0x80($key),$rndkey
+	  vaesenc	$T1,$inout5,$inout5
+	  vmovups	0xc0-0x80($key),$T1
+	  je		.Lenc_tail		# 192-bit key
+
+	  vaesenc	$rndkey,$inout0,$inout0
+	  vaesenc	$rndkey,$inout1,$inout1
+	  vaesenc	$rndkey,$inout2,$inout2
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vaesenc	$T1,$inout0,$inout0
+	  vaesenc	$T1,$inout1,$inout1
+	  vaesenc	$T1,$inout2,$inout2
+	  vaesenc	$T1,$inout3,$inout3
+	  vaesenc	$T1,$inout4,$inout4
+	  vmovups	0xd0-0x80($key),$rndkey
+	  vaesenc	$T1,$inout5,$inout5
+	  vmovups	0xe0-0x80($key),$T1
+	  jmp		.Lenc_tail		# 256-bit key
+
+.align	32
+.Lhandle_ctr32:
+	vmovdqu		($const),$Ii		# borrow $Ii for .Lbswap_mask
+	  vpshufb	$Ii,$T1,$Z2		# byte-swap counter
+	  vmovdqu	0x30($const),$Z1	# borrow $Z1, .Ltwo_lsb
+	  vpaddd	0x40($const),$Z2,$inout1	# .Lone_lsb
+	  vpaddd	$Z1,$Z2,$inout2
+	vmovdqu		0x00-0x20($Xip),$Hkey	# $Hkey^1
+	  vpaddd	$Z1,$inout1,$inout3
+	  vpshufb	$Ii,$inout1,$inout1
+	  vpaddd	$Z1,$inout2,$inout4
+	  vpshufb	$Ii,$inout2,$inout2
+	  vpxor		$rndkey,$inout1,$inout1
+	  vpaddd	$Z1,$inout3,$inout5
+	  vpshufb	$Ii,$inout3,$inout3
+	  vpxor		$rndkey,$inout2,$inout2
+	  vpaddd	$Z1,$inout4,$T1		# byte-swapped next counter value
+	  vpshufb	$Ii,$inout4,$inout4
+	  vpshufb	$Ii,$inout5,$inout5
+	  vpshufb	$Ii,$T1,$T1		# next counter value
+	jmp		.Lresume_ctr32
+
+.align	32
+.Lenc_tail:
+	  vaesenc	$rndkey,$inout0,$inout0
+	vmovdqu		$Z3,16+8(%rsp)		# postpone vpxor $Z3,$Xi,$Xi
+	vpalignr	\$8,$Z0,$Z0,$Xi		# 2nd phase
+	  vaesenc	$rndkey,$inout1,$inout1
+	vpclmulqdq	\$0x10,$Hkey,$Z0,$Z0
+	  vpxor		0x00($inp),$T1,$T2
+	  vaesenc	$rndkey,$inout2,$inout2
+	  vpxor		0x10($inp),$T1,$Ii
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vpxor		0x20($inp),$T1,$Z1
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vpxor		0x30($inp),$T1,$Z2
+	  vaesenc	$rndkey,$inout5,$inout5
+	  vpxor		0x40($inp),$T1,$Z3
+	  vpxor		0x50($inp),$T1,$Hkey
+	  vmovdqu	($ivp),$T1		# load next counter value
+
+	  vaesenclast	$T2,$inout0,$inout0
+	  vmovdqu	0x20($const),$T2	# borrow $T2, .Lone_msb
+	  vaesenclast	$Ii,$inout1,$inout1
+	 vpaddb		$T2,$T1,$Ii
+	mov		%r13,0x70+8(%rsp)
+	lea		0x60($inp),$inp
+	  vaesenclast	$Z1,$inout2,$inout2
+	 vpaddb		$T2,$Ii,$Z1
+	mov		%r12,0x78+8(%rsp)
+	lea		0x60($out),$out
+	  vmovdqu	0x00-0x80($key),$rndkey
+	  vaesenclast	$Z2,$inout3,$inout3
+	 vpaddb		$T2,$Z1,$Z2
+	  vaesenclast	$Z3, $inout4,$inout4
+	 vpaddb		$T2,$Z2,$Z3
+	  vaesenclast	$Hkey,$inout5,$inout5
+	 vpaddb		$T2,$Z3,$Hkey
+
+	add		\$0x60,$ret
+	sub		\$0x6,$len
+	jc		.L6x_done
+
+	  vmovups	$inout0,-0x60($out)	# save output
+	 vpxor		$rndkey,$T1,$inout0
+	  vmovups	$inout1,-0x50($out)
+	 vmovdqa	$Ii,$inout1		# 0 latency
+	  vmovups	$inout2,-0x40($out)
+	 vmovdqa	$Z1,$inout2		# 0 latency
+	  vmovups	$inout3,-0x30($out)
+	 vmovdqa	$Z2,$inout3		# 0 latency
+	  vmovups	$inout4,-0x20($out)
+	 vmovdqa	$Z3,$inout4		# 0 latency
+	  vmovups	$inout5,-0x10($out)
+	 vmovdqa	$Hkey,$inout5		# 0 latency
+	vmovdqu		0x20+8(%rsp),$Z3	# I[5]
+	jmp		.Loop6x
+
+.L6x_done:
+	vpxor		16+8(%rsp),$Xi,$Xi	# modulo-scheduled
+	vpxor		$Z0,$Xi,$Xi		# modulo-scheduled
+
+	ret
+.size	_aesni_ctr32_ghash_6x,.-_aesni_ctr32_ghash_6x
+___
+######################################################################
+#
+# size_t aesni_gcm_[en|de]crypt(const void *inp, void *out, size_t len,
+#		const AES_KEY *key, unsigned char iv[16],
+#		struct { u128 Xi,H,Htbl[9]; } *Xip);
+$code.=<<___;
+.globl	aesni_gcm_decrypt
+.type	aesni_gcm_decrypt,\@function,6
+.align	32
+aesni_gcm_decrypt:
+	xor	$ret,$ret
+	cmp	\$0x60,$len			# minimal accepted length
+	jb	.Lgcm_dec_abort
+
+	lea	(%rsp),%rax			# save stack pointer
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,-0xd8(%rax)
+	movaps	%xmm7,-0xc8(%rax)
+	movaps	%xmm8,-0xb8(%rax)
+	movaps	%xmm9,-0xa8(%rax)
+	movaps	%xmm10,-0x98(%rax)
+	movaps	%xmm11,-0x88(%rax)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+.Lgcm_dec_body:
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($ivp),$T1		# input counter value
+	add		\$-128,%rsp
+	mov		12($ivp),$counter
+	lea		.Lbswap_mask(%rip),$const
+	lea		-0x80($key),$in0	# borrow $in0
+	mov		\$0xf80,$end0		# borrow $end0
+	vmovdqu		($Xip),$Xi		# load Xi
+	and		\$-128,%rsp		# ensure stack alignment
+	vmovdqu		($const),$Ii		# borrow $Ii for .Lbswap_mask
+	lea		0x80($key),$key		# size optimization
+	lea		0x20+0x20($Xip),$Xip	# size optimization
+	mov		0xf0-0x80($key),$rounds
+	vpshufb		$Ii,$Xi,$Xi
+
+	and		$end0,$in0
+	and		%rsp,$end0
+	sub		$in0,$end0
+	jc		.Ldec_no_key_aliasing
+	cmp		\$768,$end0
+	jnc		.Ldec_no_key_aliasing
+	sub		$end0,%rsp		# avoid aliasing with key
+.Ldec_no_key_aliasing:
+
+	vmovdqu		0x50($inp),$Z3		# I[5]
+	lea		($inp),$in0
+	vmovdqu		0x40($inp),$Z0
+	lea		-0xc0($inp,$len),$end0
+	vmovdqu		0x30($inp),$Z1
+	shr		\$4,$len
+	xor		$ret,$ret
+	vmovdqu		0x20($inp),$Z2
+	 vpshufb	$Ii,$Z3,$Z3		# passed to _aesni_ctr32_ghash_6x
+	vmovdqu		0x10($inp),$T2
+	 vpshufb	$Ii,$Z0,$Z0
+	vmovdqu		($inp),$Hkey
+	 vpshufb	$Ii,$Z1,$Z1
+	vmovdqu		$Z0,0x30(%rsp)
+	 vpshufb	$Ii,$Z2,$Z2
+	vmovdqu		$Z1,0x40(%rsp)
+	 vpshufb	$Ii,$T2,$T2
+	vmovdqu		$Z2,0x50(%rsp)
+	 vpshufb	$Ii,$Hkey,$Hkey
+	vmovdqu		$T2,0x60(%rsp)
+	vmovdqu		$Hkey,0x70(%rsp)
+
+	call		_aesni_ctr32_ghash_6x
+
+	vmovups		$inout0,-0x60($out)	# save output
+	vmovups		$inout1,-0x50($out)
+	vmovups		$inout2,-0x40($out)
+	vmovups		$inout3,-0x30($out)
+	vmovups		$inout4,-0x20($out)
+	vmovups		$inout5,-0x10($out)
+
+	vpshufb		($const),$Xi,$Xi	# .Lbswap_mask
+	vmovdqu		$Xi,-0x40($Xip)		# output Xi
+
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xd8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lgcm_dec_abort:
+	mov	$ret,%rax		# return value
+	ret
+.size	aesni_gcm_decrypt,.-aesni_gcm_decrypt
+___
+
+$code.=<<___;
+.type	_aesni_ctr32_6x,\@abi-omnipotent
+.align	32
+_aesni_ctr32_6x:
+	vmovdqu		0x00-0x80($key),$Z0	# borrow $Z0 for $rndkey
+	vmovdqu		0x20($const),$T2	# borrow $T2, .Lone_msb
+	lea		-1($rounds),%r13
+	vmovups		0x10-0x80($key),$rndkey
+	lea		0x20-0x80($key),%r12
+	vpxor		$Z0,$T1,$inout0
+	add		\$100663296,$counter
+	jc		.Lhandle_ctr32_2
+	vpaddb		$T2,$T1,$inout1
+	vpaddb		$T2,$inout1,$inout2
+	vpxor		$Z0,$inout1,$inout1
+	vpaddb		$T2,$inout2,$inout3
+	vpxor		$Z0,$inout2,$inout2
+	vpaddb		$T2,$inout3,$inout4
+	vpxor		$Z0,$inout3,$inout3
+	vpaddb		$T2,$inout4,$inout5
+	vpxor		$Z0,$inout4,$inout4
+	vpaddb		$T2,$inout5,$T1
+	vpxor		$Z0,$inout5,$inout5
+	jmp		.Loop_ctr32
+
+.align	16
+.Loop_ctr32:
+	vaesenc		$rndkey,$inout0,$inout0
+	vaesenc		$rndkey,$inout1,$inout1
+	vaesenc		$rndkey,$inout2,$inout2
+	vaesenc		$rndkey,$inout3,$inout3
+	vaesenc		$rndkey,$inout4,$inout4
+	vaesenc		$rndkey,$inout5,$inout5
+	vmovups		(%r12),$rndkey
+	lea		0x10(%r12),%r12
+	dec		%r13d
+	jnz		.Loop_ctr32
+
+	vmovdqu		(%r12),$Hkey		# last round key
+	vaesenc		$rndkey,$inout0,$inout0
+	vpxor		0x00($inp),$Hkey,$Z0
+	vaesenc		$rndkey,$inout1,$inout1
+	vpxor		0x10($inp),$Hkey,$Z1
+	vaesenc		$rndkey,$inout2,$inout2
+	vpxor		0x20($inp),$Hkey,$Z2
+	vaesenc		$rndkey,$inout3,$inout3
+	vpxor		0x30($inp),$Hkey,$Xi
+	vaesenc		$rndkey,$inout4,$inout4
+	vpxor		0x40($inp),$Hkey,$T2
+	vaesenc		$rndkey,$inout5,$inout5
+	vpxor		0x50($inp),$Hkey,$Hkey
+	lea		0x60($inp),$inp
+
+	vaesenclast	$Z0,$inout0,$inout0
+	vaesenclast	$Z1,$inout1,$inout1
+	vaesenclast	$Z2,$inout2,$inout2
+	vaesenclast	$Xi,$inout3,$inout3
+	vaesenclast	$T2,$inout4,$inout4
+	vaesenclast	$Hkey,$inout5,$inout5
+	vmovups		$inout0,0x00($out)
+	vmovups		$inout1,0x10($out)
+	vmovups		$inout2,0x20($out)
+	vmovups		$inout3,0x30($out)
+	vmovups		$inout4,0x40($out)
+	vmovups		$inout5,0x50($out)
+	lea		0x60($out),$out
+
+	ret
+.align	32
+.Lhandle_ctr32_2:
+	vpshufb		$Ii,$T1,$Z2		# byte-swap counter
+	vmovdqu		0x30($const),$Z1	# borrow $Z1, .Ltwo_lsb
+	vpaddd		0x40($const),$Z2,$inout1	# .Lone_lsb
+	vpaddd		$Z1,$Z2,$inout2
+	vpaddd		$Z1,$inout1,$inout3
+	vpshufb		$Ii,$inout1,$inout1
+	vpaddd		$Z1,$inout2,$inout4
+	vpshufb		$Ii,$inout2,$inout2
+	vpxor		$Z0,$inout1,$inout1
+	vpaddd		$Z1,$inout3,$inout5
+	vpshufb		$Ii,$inout3,$inout3
+	vpxor		$Z0,$inout2,$inout2
+	vpaddd		$Z1,$inout4,$T1		# byte-swapped next counter value
+	vpshufb		$Ii,$inout4,$inout4
+	vpxor		$Z0,$inout3,$inout3
+	vpshufb		$Ii,$inout5,$inout5
+	vpxor		$Z0,$inout4,$inout4
+	vpshufb		$Ii,$T1,$T1		# next counter value
+	vpxor		$Z0,$inout5,$inout5
+	jmp	.Loop_ctr32
+.size	_aesni_ctr32_6x,.-_aesni_ctr32_6x
+
+.globl	aesni_gcm_encrypt
+.type	aesni_gcm_encrypt,\@function,6
+.align	32
+aesni_gcm_encrypt:
+	xor	$ret,$ret
+	cmp	\$0x60*3,$len			# minimal accepted length
+	jb	.Lgcm_enc_abort
+
+	lea	(%rsp),%rax			# save stack pointer
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,-0xd8(%rax)
+	movaps	%xmm7,-0xc8(%rax)
+	movaps	%xmm8,-0xb8(%rax)
+	movaps	%xmm9,-0xa8(%rax)
+	movaps	%xmm10,-0x98(%rax)
+	movaps	%xmm11,-0x88(%rax)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+.Lgcm_enc_body:
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($ivp),$T1		# input counter value
+	add		\$-128,%rsp
+	mov		12($ivp),$counter
+	lea		.Lbswap_mask(%rip),$const
+	lea		-0x80($key),$in0	# borrow $in0
+	mov		\$0xf80,$end0		# borrow $end0
+	lea		0x80($key),$key		# size optimization
+	vmovdqu		($const),$Ii		# borrow $Ii for .Lbswap_mask
+	and		\$-128,%rsp		# ensure stack alignment
+	mov		0xf0-0x80($key),$rounds
+
+	and		$end0,$in0
+	and		%rsp,$end0
+	sub		$in0,$end0
+	jc		.Lenc_no_key_aliasing
+	cmp		\$768,$end0
+	jnc		.Lenc_no_key_aliasing
+	sub		$end0,%rsp		# avoid aliasing with key
+.Lenc_no_key_aliasing:
+
+	lea		($out),$in0
+	lea		-0xc0($out,$len),$end0
+	shr		\$4,$len
+
+	call		_aesni_ctr32_6x
+	vpshufb		$Ii,$inout0,$Xi		# save bswapped output on stack
+	vpshufb		$Ii,$inout1,$T2
+	vmovdqu		$Xi,0x70(%rsp)
+	vpshufb		$Ii,$inout2,$Z0
+	vmovdqu		$T2,0x60(%rsp)
+	vpshufb		$Ii,$inout3,$Z1
+	vmovdqu		$Z0,0x50(%rsp)
+	vpshufb		$Ii,$inout4,$Z2
+	vmovdqu		$Z1,0x40(%rsp)
+	vpshufb		$Ii,$inout5,$Z3		# passed to _aesni_ctr32_ghash_6x
+	vmovdqu		$Z2,0x30(%rsp)
+
+	call		_aesni_ctr32_6x
+
+	vmovdqu		($Xip),$Xi		# load Xi
+	lea		0x20+0x20($Xip),$Xip	# size optimization
+	sub		\$12,$len
+	mov		\$0x60*2,$ret
+	vpshufb		$Ii,$Xi,$Xi
+
+	call		_aesni_ctr32_ghash_6x
+	vmovdqu		0x20(%rsp),$Z3		# I[5]
+	 vmovdqu	($const),$Ii		# borrow $Ii for .Lbswap_mask
+	vmovdqu		0x00-0x20($Xip),$Hkey	# $Hkey^1
+	vpunpckhqdq	$Z3,$Z3,$T1
+	vmovdqu		0x20-0x20($Xip),$rndkey	# borrow $rndkey for $HK
+	 vmovups	$inout0,-0x60($out)	# save output
+	 vpshufb	$Ii,$inout0,$inout0	# but keep bswapped copy
+	vpxor		$Z3,$T1,$T1
+	 vmovups	$inout1,-0x50($out)
+	 vpshufb	$Ii,$inout1,$inout1
+	 vmovups	$inout2,-0x40($out)
+	 vpshufb	$Ii,$inout2,$inout2
+	 vmovups	$inout3,-0x30($out)
+	 vpshufb	$Ii,$inout3,$inout3
+	 vmovups	$inout4,-0x20($out)
+	 vpshufb	$Ii,$inout4,$inout4
+	 vmovups	$inout5,-0x10($out)
+	 vpshufb	$Ii,$inout5,$inout5
+	 vmovdqu	$inout0,0x10(%rsp)	# free $inout0
+___
+{ my ($HK,$T3)=($rndkey,$inout0);
+
+$code.=<<___;
+	 vmovdqu	0x30(%rsp),$Z2		# I[4]
+	 vmovdqu	0x10-0x20($Xip),$Ii	# borrow $Ii for $Hkey^2
+	 vpunpckhqdq	$Z2,$Z2,$T2
+	vpclmulqdq	\$0x00,$Hkey,$Z3,$Z1
+	 vpxor		$Z2,$T2,$T2
+	vpclmulqdq	\$0x11,$Hkey,$Z3,$Z3
+	vpclmulqdq	\$0x00,$HK,$T1,$T1
+
+	 vmovdqu	0x40(%rsp),$T3		# I[3]
+	vpclmulqdq	\$0x00,$Ii,$Z2,$Z0
+	 vmovdqu	0x30-0x20($Xip),$Hkey	# $Hkey^3
+	vpxor		$Z1,$Z0,$Z0
+	 vpunpckhqdq	$T3,$T3,$Z1
+	vpclmulqdq	\$0x11,$Ii,$Z2,$Z2
+	 vpxor		$T3,$Z1,$Z1
+	vpxor		$Z3,$Z2,$Z2
+	vpclmulqdq	\$0x10,$HK,$T2,$T2
+	 vmovdqu	0x50-0x20($Xip),$HK
+	vpxor		$T1,$T2,$T2
+
+	 vmovdqu	0x50(%rsp),$T1		# I[2]
+	vpclmulqdq	\$0x00,$Hkey,$T3,$Z3
+	 vmovdqu	0x40-0x20($Xip),$Ii	# borrow $Ii for $Hkey^4
+	vpxor		$Z0,$Z3,$Z3
+	 vpunpckhqdq	$T1,$T1,$Z0
+	vpclmulqdq	\$0x11,$Hkey,$T3,$T3
+	 vpxor		$T1,$Z0,$Z0
+	vpxor		$Z2,$T3,$T3
+	vpclmulqdq	\$0x00,$HK,$Z1,$Z1
+	vpxor		$T2,$Z1,$Z1
+
+	 vmovdqu	0x60(%rsp),$T2		# I[1]
+	vpclmulqdq	\$0x00,$Ii,$T1,$Z2
+	 vmovdqu	0x60-0x20($Xip),$Hkey	# $Hkey^5
+	vpxor		$Z3,$Z2,$Z2
+	 vpunpckhqdq	$T2,$T2,$Z3
+	vpclmulqdq	\$0x11,$Ii,$T1,$T1
+	 vpxor		$T2,$Z3,$Z3
+	vpxor		$T3,$T1,$T1
+	vpclmulqdq	\$0x10,$HK,$Z0,$Z0
+	 vmovdqu	0x80-0x20($Xip),$HK
+	vpxor		$Z1,$Z0,$Z0
+
+	 vpxor		0x70(%rsp),$Xi,$Xi	# accumulate I[0]
+	vpclmulqdq	\$0x00,$Hkey,$T2,$Z1
+	 vmovdqu	0x70-0x20($Xip),$Ii	# borrow $Ii for $Hkey^6
+	 vpunpckhqdq	$Xi,$Xi,$T3
+	vpxor		$Z2,$Z1,$Z1
+	vpclmulqdq	\$0x11,$Hkey,$T2,$T2
+	 vpxor		$Xi,$T3,$T3
+	vpxor		$T1,$T2,$T2
+	vpclmulqdq	\$0x00,$HK,$Z3,$Z3
+	vpxor		$Z0,$Z3,$Z0
+
+	vpclmulqdq	\$0x00,$Ii,$Xi,$Z2
+	 vmovdqu	0x00-0x20($Xip),$Hkey	# $Hkey^1
+	 vpunpckhqdq	$inout5,$inout5,$T1
+	vpclmulqdq	\$0x11,$Ii,$Xi,$Xi
+	 vpxor		$inout5,$T1,$T1
+	vpxor		$Z1,$Z2,$Z1
+	vpclmulqdq	\$0x10,$HK,$T3,$T3
+	 vmovdqu	0x20-0x20($Xip),$HK
+	vpxor		$T2,$Xi,$Z3
+	vpxor		$Z0,$T3,$Z2
+
+	 vmovdqu	0x10-0x20($Xip),$Ii	# borrow $Ii for $Hkey^2
+	  vpxor		$Z1,$Z3,$T3		# aggregated Karatsuba post-processing
+	vpclmulqdq	\$0x00,$Hkey,$inout5,$Z0
+	  vpxor		$T3,$Z2,$Z2
+	 vpunpckhqdq	$inout4,$inout4,$T2
+	vpclmulqdq	\$0x11,$Hkey,$inout5,$inout5
+	 vpxor		$inout4,$T2,$T2
+	  vpslldq	\$8,$Z2,$T3
+	vpclmulqdq	\$0x00,$HK,$T1,$T1
+	  vpxor		$T3,$Z1,$Xi
+	  vpsrldq	\$8,$Z2,$Z2
+	  vpxor		$Z2,$Z3,$Z3
+
+	vpclmulqdq	\$0x00,$Ii,$inout4,$Z1
+	 vmovdqu	0x30-0x20($Xip),$Hkey	# $Hkey^3
+	vpxor		$Z0,$Z1,$Z1
+	 vpunpckhqdq	$inout3,$inout3,$T3
+	vpclmulqdq	\$0x11,$Ii,$inout4,$inout4
+	 vpxor		$inout3,$T3,$T3
+	vpxor		$inout5,$inout4,$inout4
+	  vpalignr	\$8,$Xi,$Xi,$inout5	# 1st phase
+	vpclmulqdq	\$0x10,$HK,$T2,$T2
+	 vmovdqu	0x50-0x20($Xip),$HK
+	vpxor		$T1,$T2,$T2
+
+	vpclmulqdq	\$0x00,$Hkey,$inout3,$Z0
+	 vmovdqu	0x40-0x20($Xip),$Ii	# borrow $Ii for $Hkey^4
+	vpxor		$Z1,$Z0,$Z0
+	 vpunpckhqdq	$inout2,$inout2,$T1
+	vpclmulqdq	\$0x11,$Hkey,$inout3,$inout3
+	 vpxor		$inout2,$T1,$T1
+	vpxor		$inout4,$inout3,$inout3
+	  vxorps	0x10(%rsp),$Z3,$Z3	# accumulate $inout0
+	vpclmulqdq	\$0x00,$HK,$T3,$T3
+	vpxor		$T2,$T3,$T3
+
+	  vpclmulqdq	\$0x10,0x10($const),$Xi,$Xi
+	  vxorps	$inout5,$Xi,$Xi
+
+	vpclmulqdq	\$0x00,$Ii,$inout2,$Z1
+	 vmovdqu	0x60-0x20($Xip),$Hkey	# $Hkey^5
+	vpxor		$Z0,$Z1,$Z1
+	 vpunpckhqdq	$inout1,$inout1,$T2
+	vpclmulqdq	\$0x11,$Ii,$inout2,$inout2
+	 vpxor		$inout1,$T2,$T2
+	  vpalignr	\$8,$Xi,$Xi,$inout5	# 2nd phase
+	vpxor		$inout3,$inout2,$inout2
+	vpclmulqdq	\$0x10,$HK,$T1,$T1
+	 vmovdqu	0x80-0x20($Xip),$HK
+	vpxor		$T3,$T1,$T1
+
+	  vxorps	$Z3,$inout5,$inout5
+	  vpclmulqdq	\$0x10,0x10($const),$Xi,$Xi
+	  vxorps	$inout5,$Xi,$Xi
+
+	vpclmulqdq	\$0x00,$Hkey,$inout1,$Z0
+	 vmovdqu	0x70-0x20($Xip),$Ii	# borrow $Ii for $Hkey^6
+	vpxor		$Z1,$Z0,$Z0
+	 vpunpckhqdq	$Xi,$Xi,$T3
+	vpclmulqdq	\$0x11,$Hkey,$inout1,$inout1
+	 vpxor		$Xi,$T3,$T3
+	vpxor		$inout2,$inout1,$inout1
+	vpclmulqdq	\$0x00,$HK,$T2,$T2
+	vpxor		$T1,$T2,$T2
+
+	vpclmulqdq	\$0x00,$Ii,$Xi,$Z1
+	vpclmulqdq	\$0x11,$Ii,$Xi,$Z3
+	vpxor		$Z0,$Z1,$Z1
+	vpclmulqdq	\$0x10,$HK,$T3,$Z2
+	vpxor		$inout1,$Z3,$Z3
+	vpxor		$T2,$Z2,$Z2
+
+	vpxor		$Z1,$Z3,$Z0		# aggregated Karatsuba post-processing
+	vpxor		$Z0,$Z2,$Z2
+	vpslldq		\$8,$Z2,$T1
+	vmovdqu		0x10($const),$Hkey	# .Lpoly
+	vpsrldq		\$8,$Z2,$Z2
+	vpxor		$T1,$Z1,$Xi
+	vpxor		$Z2,$Z3,$Z3
+
+	vpalignr	\$8,$Xi,$Xi,$T2		# 1st phase
+	vpclmulqdq	\$0x10,$Hkey,$Xi,$Xi
+	vpxor		$T2,$Xi,$Xi
+
+	vpalignr	\$8,$Xi,$Xi,$T2		# 2nd phase
+	vpclmulqdq	\$0x10,$Hkey,$Xi,$Xi
+	vpxor		$Z3,$T2,$T2
+	vpxor		$T2,$Xi,$Xi
+___
+}
+$code.=<<___;
+	vpshufb		($const),$Xi,$Xi	# .Lbswap_mask
+	vmovdqu		$Xi,-0x40($Xip)		# output Xi
+
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lgcm_enc_abort:
+	mov	$ret,%rax		# return value
+	ret
+.size	aesni_gcm_encrypt,.-aesni_gcm_encrypt
+___
+
+$code.=<<___;
+.align	64
+.Lbswap_mask:
+	.byte	15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
+.Lpoly:
+	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2
+.Lone_msb:
+	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
+.Ltwo_lsb:
+	.byte	2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.Lone_lsb:
+	.byte	1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.asciz	"AES-NI GCM module for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+.align	64
+___
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___
+.extern	__imp_RtlVirtualUnwind
+.type	gcm_se_handler,\@abi-omnipotent
+.align	16
+gcm_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	120($context),%rax	# pull context->Rax
+
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	mov	%r15,240($context)
+	mov	%r14,232($context)
+	mov	%r13,224($context)
+	mov	%r12,216($context)
+	mov	%rbp,160($context)
+	mov	%rbx,144($context)
+
+	lea	-0xd8(%rax),%rsi	# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	gcm_se_handler,.-gcm_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_aesni_gcm_decrypt
+	.rva	.LSEH_end_aesni_gcm_decrypt
+	.rva	.LSEH_gcm_dec_info
+
+	.rva	.LSEH_begin_aesni_gcm_encrypt
+	.rva	.LSEH_end_aesni_gcm_encrypt
+	.rva	.LSEH_gcm_enc_info
+.section	.xdata
+.align	8
+.LSEH_gcm_dec_info:
+	.byte	9,0,0,0
+	.rva	gcm_se_handler
+	.rva	.Lgcm_dec_body,.Lgcm_dec_abort
+.LSEH_gcm_enc_info:
+	.byte	9,0,0,0
+	.rva	gcm_se_handler
+	.rva	.Lgcm_enc_body,.Lgcm_enc_abort
+___
+}
+}}} else {{{
+$code=<<___;	# assembler is too old
+.text
+
+.globl	aesni_gcm_encrypt
+.type	aesni_gcm_encrypt,\@abi-omnipotent
+aesni_gcm_encrypt:
+	xor	%eax,%eax
+	ret
+.size	aesni_gcm_encrypt,.-aesni_gcm_encrypt
+
+.globl	aesni_gcm_decrypt
+.type	aesni_gcm_decrypt,\@abi-omnipotent
+aesni_gcm_decrypt:
+	xor	%eax,%eax
+	ret
+.size	aesni_gcm_decrypt,.-aesni_gcm_decrypt
+___
+}}}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-armv4.pl b/crypto/modes/asm/ghash-armv4.pl
new file mode 100644
index 0000000..8e4a52f
--- /dev/null
+++ b/crypto/modes/asm/ghash-armv4.pl
@@ -0,0 +1,432 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# April 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+32 bytes shared table]. There is no
+# experimental performance data available yet. The only approximation
+# that can be made at this point is based on code size. Inner loop is
+# 32 instructions long and on single-issue core should execute in <40
+# cycles. Having verified that gcc 3.4 didn't unroll corresponding
+# loop, this assembler loop body was found to be ~3x smaller than
+# compiler-generated one...
+#
+# July 2010
+#
+# Rescheduling for dual-issue pipeline resulted in 8.5% improvement on
+# Cortex A8 core and ~25 cycles per processed byte (which was observed
+# to be ~3 times faster than gcc-generated code:-)
+#
+# February 2011
+#
+# Profiler-assisted and platform-specific optimization resulted in 7%
+# improvement on Cortex A8 core and ~23.5 cycles per byte.
+#
+# March 2011
+#
+# Add NEON implementation featuring polynomial multiplication, i.e. no
+# lookup tables involved. On Cortex A8 it was measured to process one
+# byte in 15 cycles or 55% faster than integer-only code.
+
+# ====================================================================
+# Note about "528B" variant. In ARM case it makes lesser sense to
+# implement it for following reasons:
+#
+# - performance improvement won't be anywhere near 50%, because 128-
+#   bit shift operation is neatly fused with 128-bit xor here, and
+#   "538B" variant would eliminate only 4-5 instructions out of 32
+#   in the inner loop (meaning that estimated improvement is ~15%);
+# - ARM-based systems are often embedded ones and extra memory
+#   consumption might be unappreciated (for so little improvement);
+#
+# Byte order [in]dependence. =========================================
+#
+# Caller is expected to maintain specific *dword* order in Htable,
+# namely with *least* significant dword of 128-bit value at *lower*
+# address. This differs completely from C code and has everything to
+# do with ldm instruction and order in which dwords are "consumed" by
+# algorithm. *Byte* order within these dwords in turn is whatever
+# *native* byte order on current platform. See gcm128.c for working
+# example...
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$Xi="r0";	# argument block
+$Htbl="r1";
+$inp="r2";
+$len="r3";
+
+$Zll="r4";	# variables
+$Zlh="r5";
+$Zhl="r6";
+$Zhh="r7";
+$Tll="r8";
+$Tlh="r9";
+$Thl="r10";
+$Thh="r11";
+$nlo="r12";
+################# r13 is stack pointer
+$nhi="r14";
+################# r15 is program counter
+
+$rem_4bit=$inp;	# used in gcm_gmult_4bit
+$cnt=$len;
+
+sub Zsmash() {
+  my $i=12;
+  my @args=@_;
+  for ($Zll,$Zlh,$Zhl,$Zhh) {
+    $code.=<<___;
+#if __ARM_ARCH__>=7 && defined(__ARMEL__)
+	rev	$_,$_
+	str	$_,[$Xi,#$i]
+#elif defined(__ARMEB__)
+	str	$_,[$Xi,#$i]
+#else
+	mov	$Tlh,$_,lsr#8
+	strb	$_,[$Xi,#$i+3]
+	mov	$Thl,$_,lsr#16
+	strb	$Tlh,[$Xi,#$i+2]
+	mov	$Thh,$_,lsr#24
+	strb	$Thl,[$Xi,#$i+1]
+	strb	$Thh,[$Xi,#$i]
+#endif
+___
+    $code.="\t".shift(@args)."\n";
+    $i-=4;
+  }
+}
+
+$code=<<___;
+#if defined(__arm__)
+#include "arm_arch.h"
+
+.text
+.code	32
+
+.type	rem_4bit,%object
+.align	5
+rem_4bit:
+.short	0x0000,0x1C20,0x3840,0x2460
+.short	0x7080,0x6CA0,0x48C0,0x54E0
+.short	0xE100,0xFD20,0xD940,0xC560
+.short	0x9180,0x8DA0,0xA9C0,0xB5E0
+.size	rem_4bit,.-rem_4bit
+
+.type	rem_4bit_get,%function
+rem_4bit_get:
+	sub	$rem_4bit,pc,#8
+	sub	$rem_4bit,$rem_4bit,#32	@ &rem_4bit
+	b	.Lrem_4bit_got
+	nop
+.size	rem_4bit_get,.-rem_4bit_get
+
+.global	gcm_ghash_4bit
+.type	gcm_ghash_4bit,%function
+gcm_ghash_4bit:
+	sub	r12,pc,#8
+	add	$len,$inp,$len		@ $len to point at the end
+	stmdb	sp!,{r3-r11,lr}		@ save $len/end too
+	sub	r12,r12,#48		@ &rem_4bit
+
+	ldmia	r12,{r4-r11}		@ copy rem_4bit ...
+	stmdb	sp!,{r4-r11}		@ ... to stack
+
+	ldrb	$nlo,[$inp,#15]
+	ldrb	$nhi,[$Xi,#15]
+.Louter:
+	eor	$nlo,$nlo,$nhi
+	and	$nhi,$nlo,#0xf0
+	and	$nlo,$nlo,#0x0f
+	mov	$cnt,#14
+
+	add	$Zhh,$Htbl,$nlo,lsl#4
+	ldmia	$Zhh,{$Zll-$Zhh}	@ load Htbl[nlo]
+	add	$Thh,$Htbl,$nhi
+	ldrb	$nlo,[$inp,#14]
+
+	and	$nhi,$Zll,#0xf		@ rem
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	add	$nhi,$nhi,$nhi
+	eor	$Zll,$Tll,$Zll,lsr#4
+	ldrh	$Tll,[sp,$nhi]		@ rem_4bit[rem]
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	ldrb	$nhi,[$Xi,#14]
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+	eor	$nlo,$nlo,$nhi
+	and	$nhi,$nlo,#0xf0
+	and	$nlo,$nlo,#0x0f
+	eor	$Zhh,$Zhh,$Tll,lsl#16
+
+.Linner:
+	add	$Thh,$Htbl,$nlo,lsl#4
+	and	$nlo,$Zll,#0xf		@ rem
+	subs	$cnt,$cnt,#1
+	add	$nlo,$nlo,$nlo
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nlo]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	ldrh	$Tll,[sp,$nlo]		@ rem_4bit[rem]
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	ldrplb	$nlo,[$inp,$cnt]
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+
+	add	$Thh,$Htbl,$nhi
+	and	$nhi,$Zll,#0xf		@ rem
+	eor	$Zhh,$Zhh,$Tll,lsl#16	@ ^= rem_4bit[rem]
+	add	$nhi,$nhi,$nhi
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	ldrplb	$Tll,[$Xi,$cnt]
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	ldrh	$Tlh,[sp,$nhi]
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eorpl	$nlo,$nlo,$Tll
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+	andpl	$nhi,$nlo,#0xf0
+	andpl	$nlo,$nlo,#0x0f
+	eor	$Zhh,$Zhh,$Tlh,lsl#16	@ ^= rem_4bit[rem]
+	bpl	.Linner
+
+	ldr	$len,[sp,#32]		@ re-load $len/end
+	add	$inp,$inp,#16
+	mov	$nhi,$Zll
+___
+	&Zsmash("cmp\t$inp,$len","ldrneb\t$nlo,[$inp,#15]");
+$code.=<<___;
+	bne	.Louter
+
+	add	sp,sp,#36
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r11,pc}
+#else
+	ldmia	sp!,{r4-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	gcm_ghash_4bit,.-gcm_ghash_4bit
+
+.global	gcm_gmult_4bit
+.type	gcm_gmult_4bit,%function
+gcm_gmult_4bit:
+	stmdb	sp!,{r4-r11,lr}
+	ldrb	$nlo,[$Xi,#15]
+	b	rem_4bit_get
+.Lrem_4bit_got:
+	and	$nhi,$nlo,#0xf0
+	and	$nlo,$nlo,#0x0f
+	mov	$cnt,#14
+
+	add	$Zhh,$Htbl,$nlo,lsl#4
+	ldmia	$Zhh,{$Zll-$Zhh}	@ load Htbl[nlo]
+	ldrb	$nlo,[$Xi,#14]
+
+	add	$Thh,$Htbl,$nhi
+	and	$nhi,$Zll,#0xf		@ rem
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	add	$nhi,$nhi,$nhi
+	eor	$Zll,$Tll,$Zll,lsr#4
+	ldrh	$Tll,[$rem_4bit,$nhi]	@ rem_4bit[rem]
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+	and	$nhi,$nlo,#0xf0
+	eor	$Zhh,$Zhh,$Tll,lsl#16
+	and	$nlo,$nlo,#0x0f
+
+.Loop:
+	add	$Thh,$Htbl,$nlo,lsl#4
+	and	$nlo,$Zll,#0xf		@ rem
+	subs	$cnt,$cnt,#1
+	add	$nlo,$nlo,$nlo
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nlo]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	ldrh	$Tll,[$rem_4bit,$nlo]	@ rem_4bit[rem]
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	ldrplb	$nlo,[$Xi,$cnt]
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+
+	add	$Thh,$Htbl,$nhi
+	and	$nhi,$Zll,#0xf		@ rem
+	eor	$Zhh,$Zhh,$Tll,lsl#16	@ ^= rem_4bit[rem]
+	add	$nhi,$nhi,$nhi
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	ldrh	$Tll,[$rem_4bit,$nhi]	@ rem_4bit[rem]
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+	andpl	$nhi,$nlo,#0xf0
+	andpl	$nlo,$nlo,#0x0f
+	eor	$Zhh,$Zhh,$Tll,lsl#16	@ ^= rem_4bit[rem]
+	bpl	.Loop
+___
+	&Zsmash();
+$code.=<<___;
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r11,pc}
+#else
+	ldmia	sp!,{r4-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	gcm_gmult_4bit,.-gcm_gmult_4bit
+___
+{
+my $cnt=$Htbl;	# $Htbl is used once in the very beginning
+
+my ($Hhi, $Hlo, $Zo, $T, $xi, $mod) = map("d$_",(0..7));
+my ($Qhi, $Qlo, $Z,  $R, $zero, $Qpost, $IN) = map("q$_",(8..15));
+
+# Z:Zo keeps 128-bit result shifted by 1 to the right, with bottom bit
+# in Zo. Or should I say "top bit", because GHASH is specified in
+# reverse bit order? Otherwise straightforward 128-bt H by one input
+# byte multiplication and modulo-reduction, times 16.
+
+sub Dlo()   { shift=~m|q([1]?[0-9])|?"d".($1*2):"";     }
+sub Dhi()   { shift=~m|q([1]?[0-9])|?"d".($1*2+1):"";   }
+sub Q()     { shift=~m|d([1-3]?[02468])|?"q".($1/2):""; }
+
+$code.=<<___;
+#if __ARM_ARCH__>=7
+.fpu	neon
+
+.global	gcm_gmult_neon
+.type	gcm_gmult_neon,%function
+.align	4
+gcm_gmult_neon:
+	sub		$Htbl,#16		@ point at H in GCM128_CTX
+	vld1.64		`&Dhi("$IN")`,[$Xi,:64]!@ load Xi
+	vmov.i32	$mod,#0xe1		@ our irreducible polynomial
+	vld1.64		`&Dlo("$IN")`,[$Xi,:64]!
+	vshr.u64	$mod,#32
+	vldmia		$Htbl,{$Hhi-$Hlo}	@ load H
+	veor		$zero,$zero
+#ifdef __ARMEL__
+	vrev64.8	$IN,$IN
+#endif
+	veor		$Qpost,$Qpost
+	veor		$R,$R
+	mov		$cnt,#16
+	veor		$Z,$Z
+	mov		$len,#16
+	veor		$Zo,$Zo
+	vdup.8		$xi,`&Dlo("$IN")`[0]	@ broadcast lowest byte
+	b		.Linner_neon
+.size	gcm_gmult_neon,.-gcm_gmult_neon
+
+.global	gcm_ghash_neon
+.type	gcm_ghash_neon,%function
+.align	4
+gcm_ghash_neon:
+	vld1.64		`&Dhi("$Z")`,[$Xi,:64]!	@ load Xi
+	vmov.i32	$mod,#0xe1		@ our irreducible polynomial
+	vld1.64		`&Dlo("$Z")`,[$Xi,:64]!
+	vshr.u64	$mod,#32
+	vldmia		$Xi,{$Hhi-$Hlo}		@ load H
+	veor		$zero,$zero
+	nop
+#ifdef __ARMEL__
+	vrev64.8	$Z,$Z
+#endif
+.Louter_neon:
+	vld1.64		`&Dhi($IN)`,[$inp]!	@ load inp
+	veor		$Qpost,$Qpost
+	vld1.64		`&Dlo($IN)`,[$inp]!
+	veor		$R,$R
+	mov		$cnt,#16
+#ifdef __ARMEL__
+	vrev64.8	$IN,$IN
+#endif
+	veor		$Zo,$Zo
+	veor		$IN,$Z			@ inp^=Xi
+	veor		$Z,$Z
+	vdup.8		$xi,`&Dlo("$IN")`[0]	@ broadcast lowest byte
+.Linner_neon:
+	subs		$cnt,$cnt,#1
+	vmull.p8	$Qlo,$Hlo,$xi		@ H.lo·Xi[i]
+	vmull.p8	$Qhi,$Hhi,$xi		@ H.hi·Xi[i]
+	vext.8		$IN,$zero,#1		@ IN>>=8
+
+	veor		$Z,$Qpost		@ modulo-scheduled part
+	vshl.i64	`&Dlo("$R")`,#48
+	vdup.8		$xi,`&Dlo("$IN")`[0]	@ broadcast lowest byte
+	veor		$T,`&Dlo("$Qlo")`,`&Dlo("$Z")`
+
+	veor		`&Dhi("$Z")`,`&Dlo("$R")`
+	vuzp.8		$Qlo,$Qhi
+	vsli.8		$Zo,$T,#1		@ compose the "carry" byte
+	vext.8		$Z,$zero,#1		@ Z>>=8
+
+	vmull.p8	$R,$Zo,$mod		@ "carry"·0xe1
+	vshr.u8		$Zo,$T,#7		@ save Z's bottom bit
+	vext.8		$Qpost,$Qlo,$zero,#1	@ Qlo>>=8
+	veor		$Z,$Qhi
+	bne		.Linner_neon
+
+	veor		$Z,$Qpost		@ modulo-scheduled artefact
+	vshl.i64	`&Dlo("$R")`,#48
+	veor		`&Dhi("$Z")`,`&Dlo("$R")`
+
+	@ finalization, normalize Z:Zo
+	vand		$Zo,$mod		@ suffices to mask the bit
+	vshr.u64	`&Dhi(&Q("$Zo"))`,`&Dlo("$Z")`,#63
+	vshl.i64	$Z,#1
+	subs		$len,#16
+	vorr		$Z,`&Q("$Zo")`		@ Z=Z:Zo<<1
+	bne		.Louter_neon
+
+#ifdef __ARMEL__
+	vrev64.8	$Z,$Z
+#endif
+	sub		$Xi,#16	
+	vst1.64		`&Dhi("$Z")`,[$Xi,:64]!	@ write out Xi
+	vst1.64		`&Dlo("$Z")`,[$Xi,:64]
+
+	bx	lr
+.size	gcm_ghash_neon,.-gcm_ghash_neon
+#endif
+___
+}
+$code.=<<___;
+.asciz  "GHASH for ARMv4/NEON, CRYPTOGAMS by <appro\@openssl.org>"
+.align  2
+
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+print $code;
+close STDOUT; # enforce flush
diff --git a/crypto/modes/asm/ghash-x86.pl b/crypto/modes/asm/ghash-x86.pl
new file mode 100644
index 0000000..d47e325
--- /dev/null
+++ b/crypto/modes/asm/ghash-x86.pl
@@ -0,0 +1,1391 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# March, May, June 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+64/128 bytes fixed table]. It has two
+# code paths: vanilla x86 and vanilla SSE. Former will be executed on
+# 486 and Pentium, latter on all others. SSE GHASH features so called
+# "528B" variant of "4-bit" method utilizing additional 256+16 bytes
+# of per-key storage [+512 bytes shared table]. Performance results
+# are for streamed GHASH subroutine and are expressed in cycles per
+# processed byte, less is better:
+#
+#		gcc 2.95.3(*)	SSE assembler	x86 assembler
+#
+# Pentium	105/111(**)	-		50
+# PIII		68 /75		12.2		24
+# P4		125/125		17.8		84(***)
+# Opteron	66 /70		10.1		30
+# Core2		54 /67		8.4		18
+# Atom		105/105		16.8		53
+# VIA Nano	69 /71		13.0		27
+#
+# (*)	gcc 3.4.x was observed to generate few percent slower code,
+#	which is one of reasons why 2.95.3 results were chosen,
+#	another reason is lack of 3.4.x results for older CPUs;
+#	comparison with SSE results is not completely fair, because C
+#	results are for vanilla "256B" implementation, while
+#	assembler results are for "528B";-)
+# (**)	second number is result for code compiled with -fPIC flag,
+#	which is actually more relevant, because assembler code is
+#	position-independent;
+# (***)	see comment in non-MMX routine for further details;
+#
+# To summarize, it's >2-5 times faster than gcc-generated code. To
+# anchor it to something else SHA1 assembler processes one byte in
+# ~7 cycles on contemporary x86 cores. As for choice of MMX/SSE
+# in particular, see comment at the end of the file...
+
+# May 2010
+#
+# Add PCLMULQDQ version performing at 2.10 cycles per processed byte.
+# The question is how close is it to theoretical limit? The pclmulqdq
+# instruction latency appears to be 14 cycles and there can't be more
+# than 2 of them executing at any given time. This means that single
+# Karatsuba multiplication would take 28 cycles *plus* few cycles for
+# pre- and post-processing. Then multiplication has to be followed by
+# modulo-reduction. Given that aggregated reduction method [see
+# "Carry-less Multiplication and Its Usage for Computing the GCM Mode"
+# white paper by Intel] allows you to perform reduction only once in
+# a while we can assume that asymptotic performance can be estimated
+# as (28+Tmod/Naggr)/16, where Tmod is time to perform reduction
+# and Naggr is the aggregation factor.
+#
+# Before we proceed to this implementation let's have closer look at
+# the best-performing code suggested by Intel in their white paper.
+# By tracing inter-register dependencies Tmod is estimated as ~19
+# cycles and Naggr chosen by Intel is 4, resulting in 2.05 cycles per
+# processed byte. As implied, this is quite optimistic estimate,
+# because it does not account for Karatsuba pre- and post-processing,
+# which for a single multiplication is ~5 cycles. Unfortunately Intel
+# does not provide performance data for GHASH alone. But benchmarking
+# AES_GCM_encrypt ripped out of Fig. 15 of the white paper with aadt
+# alone resulted in 2.46 cycles per byte of out 16KB buffer. Note that
+# the result accounts even for pre-computing of degrees of the hash
+# key H, but its portion is negligible at 16KB buffer size.
+#
+# Moving on to the implementation in question. Tmod is estimated as
+# ~13 cycles and Naggr is 2, giving asymptotic performance of ...
+# 2.16. How is it possible that measured performance is better than
+# optimistic theoretical estimate? There is one thing Intel failed
+# to recognize. By serializing GHASH with CTR in same subroutine
+# former's performance is really limited to above (Tmul + Tmod/Naggr)
+# equation. But if GHASH procedure is detached, the modulo-reduction
+# can be interleaved with Naggr-1 multiplications at instruction level
+# and under ideal conditions even disappear from the equation. So that
+# optimistic theoretical estimate for this implementation is ...
+# 28/16=1.75, and not 2.16. Well, it's probably way too optimistic,
+# at least for such small Naggr. I'd argue that (28+Tproc/Naggr),
+# where Tproc is time required for Karatsuba pre- and post-processing,
+# is more realistic estimate. In this case it gives ... 1.91 cycles.
+# Or in other words, depending on how well we can interleave reduction
+# and one of the two multiplications the performance should be betwen
+# 1.91 and 2.16. As already mentioned, this implementation processes
+# one byte out of 8KB buffer in 2.10 cycles, while x86_64 counterpart
+# - in 2.02. x86_64 performance is better, because larger register
+# bank allows to interleave reduction and multiplication better.
+#
+# Does it make sense to increase Naggr? To start with it's virtually
+# impossible in 32-bit mode, because of limited register bank
+# capacity. Otherwise improvement has to be weighed agiainst slower
+# setup, as well as code size and complexity increase. As even
+# optimistic estimate doesn't promise 30% performance improvement,
+# there are currently no plans to increase Naggr.
+#
+# Special thanks to David Woodhouse <dwmw2@infradead.org> for
+# providing access to a Westmere-based system on behalf of Intel
+# Open Source Technology Centre.
+
+# January 2010
+#
+# Tweaked to optimize transitions between integer and FP operations
+# on same XMM register, PCLMULQDQ subroutine was measured to process
+# one byte in 2.07 cycles on Sandy Bridge, and in 2.12 - on Westmere.
+# The minor regression on Westmere is outweighed by ~15% improvement
+# on Sandy Bridge. Strangely enough attempt to modify 64-bit code in
+# similar manner resulted in almost 20% degradation on Sandy Bridge,
+# where original 64-bit code processes one byte in 1.95 cycles.
+
+#####################################################################
+# For reference, AMD Bulldozer processes one byte in 1.98 cycles in
+# 32-bit mode and 1.89 in 64-bit.
+
+# February 2013
+#
+# Overhaul: aggregate Karatsuba post-processing, improve ILP in
+# reduction_alg9. Resulting performance is 1.96 cycles per byte on
+# Westmere, 1.95 - on Sandy/Ivy Bridge, 1.76 - on Bulldozer.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"ghash-x86.pl",$x86only = $ARGV[$#ARGV] eq "386");
+
+$sse2=1;
+#for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+($Zhh,$Zhl,$Zlh,$Zll) = ("ebp","edx","ecx","ebx");
+$inp  = "edi";
+$Htbl = "esi";
+
+$unroll = 0;	# Affects x86 loop. Folded loop performs ~7% worse
+		# than unrolled, which has to be weighted against
+		# 2.5x x86-specific code size reduction.
+
+sub x86_loop {
+    my $off = shift;
+    my $rem = "eax";
+
+	&mov	($Zhh,&DWP(4,$Htbl,$Zll));
+	&mov	($Zhl,&DWP(0,$Htbl,$Zll));
+	&mov	($Zlh,&DWP(12,$Htbl,$Zll));
+	&mov	($Zll,&DWP(8,$Htbl,$Zll));
+	&xor	($rem,$rem);	# avoid partial register stalls on PIII
+
+	# shrd practically kills P4, 2.5x deterioration, but P4 has
+	# MMX code-path to execute. shrd runs tad faster [than twice
+	# the shifts, move's and or's] on pre-MMX Pentium (as well as
+	# PIII and Core2), *but* minimizes code size, spares register
+	# and thus allows to fold the loop...
+	if (!$unroll) {
+	my $cnt = $inp;
+	&mov	($cnt,15);
+	&jmp	(&label("x86_loop"));
+	&set_label("x86_loop",16);
+	    for($i=1;$i<=2;$i++) {
+		&mov	(&LB($rem),&LB($Zll));
+		&shrd	($Zll,$Zlh,4);
+		&and	(&LB($rem),0xf);
+		&shrd	($Zlh,$Zhl,4);
+		&shrd	($Zhl,$Zhh,4);
+		&shr	($Zhh,4);
+		&xor	($Zhh,&DWP($off+16,"esp",$rem,4));
+
+		&mov	(&LB($rem),&BP($off,"esp",$cnt));
+		if ($i&1) {
+			&and	(&LB($rem),0xf0);
+		} else {
+			&shl	(&LB($rem),4);
+		}
+
+		&xor	($Zll,&DWP(8,$Htbl,$rem));
+		&xor	($Zlh,&DWP(12,$Htbl,$rem));
+		&xor	($Zhl,&DWP(0,$Htbl,$rem));
+		&xor	($Zhh,&DWP(4,$Htbl,$rem));
+
+		if ($i&1) {
+			&dec	($cnt);
+			&js	(&label("x86_break"));
+		} else {
+			&jmp	(&label("x86_loop"));
+		}
+	    }
+	&set_label("x86_break",16);
+	} else {
+	    for($i=1;$i<32;$i++) {
+		&comment($i);
+		&mov	(&LB($rem),&LB($Zll));
+		&shrd	($Zll,$Zlh,4);
+		&and	(&LB($rem),0xf);
+		&shrd	($Zlh,$Zhl,4);
+		&shrd	($Zhl,$Zhh,4);
+		&shr	($Zhh,4);
+		&xor	($Zhh,&DWP($off+16,"esp",$rem,4));
+
+		if ($i&1) {
+			&mov	(&LB($rem),&BP($off+15-($i>>1),"esp"));
+			&and	(&LB($rem),0xf0);
+		} else {
+			&mov	(&LB($rem),&BP($off+15-($i>>1),"esp"));
+			&shl	(&LB($rem),4);
+		}
+
+		&xor	($Zll,&DWP(8,$Htbl,$rem));
+		&xor	($Zlh,&DWP(12,$Htbl,$rem));
+		&xor	($Zhl,&DWP(0,$Htbl,$rem));
+		&xor	($Zhh,&DWP(4,$Htbl,$rem));
+	    }
+	}
+	&bswap	($Zll);
+	&bswap	($Zlh);
+	&bswap	($Zhl);
+	if (!$x86only) {
+		&bswap	($Zhh);
+	} else {
+		&mov	("eax",$Zhh);
+		&bswap	("eax");
+		&mov	($Zhh,"eax");
+	}
+}
+
+if ($unroll) {
+    &function_begin_B("_x86_gmult_4bit_inner");
+	&x86_loop(4);
+	&ret	();
+    &function_end_B("_x86_gmult_4bit_inner");
+}
+
+sub deposit_rem_4bit {
+    my $bias = shift;
+
+	&mov	(&DWP($bias+0, "esp"),0x0000<<16);
+	&mov	(&DWP($bias+4, "esp"),0x1C20<<16);
+	&mov	(&DWP($bias+8, "esp"),0x3840<<16);
+	&mov	(&DWP($bias+12,"esp"),0x2460<<16);
+	&mov	(&DWP($bias+16,"esp"),0x7080<<16);
+	&mov	(&DWP($bias+20,"esp"),0x6CA0<<16);
+	&mov	(&DWP($bias+24,"esp"),0x48C0<<16);
+	&mov	(&DWP($bias+28,"esp"),0x54E0<<16);
+	&mov	(&DWP($bias+32,"esp"),0xE100<<16);
+	&mov	(&DWP($bias+36,"esp"),0xFD20<<16);
+	&mov	(&DWP($bias+40,"esp"),0xD940<<16);
+	&mov	(&DWP($bias+44,"esp"),0xC560<<16);
+	&mov	(&DWP($bias+48,"esp"),0x9180<<16);
+	&mov	(&DWP($bias+52,"esp"),0x8DA0<<16);
+	&mov	(&DWP($bias+56,"esp"),0xA9C0<<16);
+	&mov	(&DWP($bias+60,"esp"),0xB5E0<<16);
+}
+
+$suffix = $x86only ? "" : "_x86";
+
+&function_begin("gcm_gmult_4bit".$suffix);
+	&stack_push(16+4+1);			# +1 for stack alignment
+	&mov	($inp,&wparam(0));		# load Xi
+	&mov	($Htbl,&wparam(1));		# load Htable
+
+	&mov	($Zhh,&DWP(0,$inp));		# load Xi[16]
+	&mov	($Zhl,&DWP(4,$inp));
+	&mov	($Zlh,&DWP(8,$inp));
+	&mov	($Zll,&DWP(12,$inp));
+
+	&deposit_rem_4bit(16);
+
+	&mov	(&DWP(0,"esp"),$Zhh);		# copy Xi[16] on stack
+	&mov	(&DWP(4,"esp"),$Zhl);
+	&mov	(&DWP(8,"esp"),$Zlh);
+	&mov	(&DWP(12,"esp"),$Zll);
+	&shr	($Zll,20);
+	&and	($Zll,0xf0);
+
+	if ($unroll) {
+		&call	("_x86_gmult_4bit_inner");
+	} else {
+		&x86_loop(0);
+		&mov	($inp,&wparam(0));
+	}
+
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(0,$inp),$Zhh);
+	&stack_pop(16+4+1);
+&function_end("gcm_gmult_4bit".$suffix);
+
+&function_begin("gcm_ghash_4bit".$suffix);
+	&stack_push(16+4+1);			# +1 for 64-bit alignment
+	&mov	($Zll,&wparam(0));		# load Xi
+	&mov	($Htbl,&wparam(1));		# load Htable
+	&mov	($inp,&wparam(2));		# load in
+	&mov	("ecx",&wparam(3));		# load len
+	&add	("ecx",$inp);
+	&mov	(&wparam(3),"ecx");
+
+	&mov	($Zhh,&DWP(0,$Zll));		# load Xi[16]
+	&mov	($Zhl,&DWP(4,$Zll));
+	&mov	($Zlh,&DWP(8,$Zll));
+	&mov	($Zll,&DWP(12,$Zll));
+
+	&deposit_rem_4bit(16);
+
+    &set_label("x86_outer_loop",16);
+	&xor	($Zll,&DWP(12,$inp));		# xor with input
+	&xor	($Zlh,&DWP(8,$inp));
+	&xor	($Zhl,&DWP(4,$inp));
+	&xor	($Zhh,&DWP(0,$inp));
+	&mov	(&DWP(12,"esp"),$Zll);		# dump it on stack
+	&mov	(&DWP(8,"esp"),$Zlh);
+	&mov	(&DWP(4,"esp"),$Zhl);
+	&mov	(&DWP(0,"esp"),$Zhh);
+
+	&shr	($Zll,20);
+	&and	($Zll,0xf0);
+
+	if ($unroll) {
+		&call	("_x86_gmult_4bit_inner");
+	} else {
+		&x86_loop(0);
+		&mov	($inp,&wparam(2));
+	}
+	&lea	($inp,&DWP(16,$inp));
+	&cmp	($inp,&wparam(3));
+	&mov	(&wparam(2),$inp)	if (!$unroll);
+	&jb	(&label("x86_outer_loop"));
+
+	&mov	($inp,&wparam(0));	# load Xi
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(0,$inp),$Zhh);
+	&stack_pop(16+4+1);
+&function_end("gcm_ghash_4bit".$suffix);
+
+if (!$x86only) {{{
+
+&static_label("rem_4bit");
+
+if (!$sse2) {{	# pure-MMX "May" version...
+
+$S=12;		# shift factor for rem_4bit
+
+&function_begin_B("_mmx_gmult_4bit_inner");
+# MMX version performs 3.5 times better on P4 (see comment in non-MMX
+# routine for further details), 100% better on Opteron, ~70% better
+# on Core2 and PIII... In other words effort is considered to be well
+# spent... Since initial release the loop was unrolled in order to
+# "liberate" register previously used as loop counter. Instead it's
+# used to optimize critical path in 'Z.hi ^= rem_4bit[Z.lo&0xf]'.
+# The path involves move of Z.lo from MMX to integer register,
+# effective address calculation and finally merge of value to Z.hi.
+# Reference to rem_4bit is scheduled so late that I had to >>4
+# rem_4bit elements. This resulted in 20-45% procent improvement
+# on contemporary µ-archs.
+{
+    my $cnt;
+    my $rem_4bit = "eax";
+    my @rem = ($Zhh,$Zll);
+    my $nhi = $Zhl;
+    my $nlo = $Zlh;
+
+    my ($Zlo,$Zhi) = ("mm0","mm1");
+    my $tmp = "mm2";
+
+	&xor	($nlo,$nlo);	# avoid partial register stalls on PIII
+	&mov	($nhi,$Zll);
+	&mov	(&LB($nlo),&LB($nhi));
+	&shl	(&LB($nlo),4);
+	&and	($nhi,0xf0);
+	&movq	($Zlo,&QWP(8,$Htbl,$nlo));
+	&movq	($Zhi,&QWP(0,$Htbl,$nlo));
+	&movd	($rem[0],$Zlo);
+
+	for ($cnt=28;$cnt>=-2;$cnt--) {
+	    my $odd = $cnt&1;
+	    my $nix = $odd ? $nlo : $nhi;
+
+		&shl	(&LB($nlo),4)			if ($odd);
+		&psrlq	($Zlo,4);
+		&movq	($tmp,$Zhi);
+		&psrlq	($Zhi,4);
+		&pxor	($Zlo,&QWP(8,$Htbl,$nix));
+		&mov	(&LB($nlo),&BP($cnt/2,$inp))	if (!$odd && $cnt>=0);
+		&psllq	($tmp,60);
+		&and	($nhi,0xf0)			if ($odd);
+		&pxor	($Zhi,&QWP(0,$rem_4bit,$rem[1],8)) if ($cnt<28);
+		&and	($rem[0],0xf);
+		&pxor	($Zhi,&QWP(0,$Htbl,$nix));
+		&mov	($nhi,$nlo)			if (!$odd && $cnt>=0);
+		&movd	($rem[1],$Zlo);
+		&pxor	($Zlo,$tmp);
+
+		push	(@rem,shift(@rem));		# "rotate" registers
+	}
+
+	&mov	($inp,&DWP(4,$rem_4bit,$rem[1],8));	# last rem_4bit[rem]
+
+	&psrlq	($Zlo,32);	# lower part of Zlo is already there
+	&movd	($Zhl,$Zhi);
+	&psrlq	($Zhi,32);
+	&movd	($Zlh,$Zlo);
+	&movd	($Zhh,$Zhi);
+	&shl	($inp,4);	# compensate for rem_4bit[i] being >>4
+
+	&bswap	($Zll);
+	&bswap	($Zhl);
+	&bswap	($Zlh);
+	&xor	($Zhh,$inp);
+	&bswap	($Zhh);
+
+	&ret	();
+}
+&function_end_B("_mmx_gmult_4bit_inner");
+
+&function_begin("gcm_gmult_4bit_mmx");
+	&mov	($inp,&wparam(0));	# load Xi
+	&mov	($Htbl,&wparam(1));	# load Htable
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
+
+	&movz	($Zll,&BP(15,$inp));
+
+	&call	("_mmx_gmult_4bit_inner");
+
+	&mov	($inp,&wparam(0));	# load Xi
+	&emms	();
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(0,$inp),$Zhh);
+&function_end("gcm_gmult_4bit_mmx");
+
+# Streamed version performs 20% better on P4, 7% on Opteron,
+# 10% on Core2 and PIII...
+&function_begin("gcm_ghash_4bit_mmx");
+	&mov	($Zhh,&wparam(0));	# load Xi
+	&mov	($Htbl,&wparam(1));	# load Htable
+	&mov	($inp,&wparam(2));	# load in
+	&mov	($Zlh,&wparam(3));	# load len
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
+
+	&add	($Zlh,$inp);
+	&mov	(&wparam(3),$Zlh);	# len to point at the end of input
+	&stack_push(4+1);		# +1 for stack alignment
+
+	&mov	($Zll,&DWP(12,$Zhh));	# load Xi[16]
+	&mov	($Zhl,&DWP(4,$Zhh));
+	&mov	($Zlh,&DWP(8,$Zhh));
+	&mov	($Zhh,&DWP(0,$Zhh));
+	&jmp	(&label("mmx_outer_loop"));
+
+    &set_label("mmx_outer_loop",16);
+	&xor	($Zll,&DWP(12,$inp));
+	&xor	($Zhl,&DWP(4,$inp));
+	&xor	($Zlh,&DWP(8,$inp));
+	&xor	($Zhh,&DWP(0,$inp));
+	&mov	(&wparam(2),$inp);
+	&mov	(&DWP(12,"esp"),$Zll);
+	&mov	(&DWP(4,"esp"),$Zhl);
+	&mov	(&DWP(8,"esp"),$Zlh);
+	&mov	(&DWP(0,"esp"),$Zhh);
+
+	&mov	($inp,"esp");
+	&shr	($Zll,24);
+
+	&call	("_mmx_gmult_4bit_inner");
+
+	&mov	($inp,&wparam(2));
+	&lea	($inp,&DWP(16,$inp));
+	&cmp	($inp,&wparam(3));
+	&jb	(&label("mmx_outer_loop"));
+
+	&mov	($inp,&wparam(0));	# load Xi
+	&emms	();
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(0,$inp),$Zhh);
+
+	&stack_pop(4+1);
+&function_end("gcm_ghash_4bit_mmx");
+
+}} else {{	# "June" MMX version...
+		# ... has slower "April" gcm_gmult_4bit_mmx with folded
+		# loop. This is done to conserve code size...
+$S=16;		# shift factor for rem_4bit
+
+sub mmx_loop() {
+# MMX version performs 2.8 times better on P4 (see comment in non-MMX
+# routine for further details), 40% better on Opteron and Core2, 50%
+# better on PIII... In other words effort is considered to be well
+# spent...
+    my $inp = shift;
+    my $rem_4bit = shift;
+    my $cnt = $Zhh;
+    my $nhi = $Zhl;
+    my $nlo = $Zlh;
+    my $rem = $Zll;
+
+    my ($Zlo,$Zhi) = ("mm0","mm1");
+    my $tmp = "mm2";
+
+	&xor	($nlo,$nlo);	# avoid partial register stalls on PIII
+	&mov	($nhi,$Zll);
+	&mov	(&LB($nlo),&LB($nhi));
+	&mov	($cnt,14);
+	&shl	(&LB($nlo),4);
+	&and	($nhi,0xf0);
+	&movq	($Zlo,&QWP(8,$Htbl,$nlo));
+	&movq	($Zhi,&QWP(0,$Htbl,$nlo));
+	&movd	($rem,$Zlo);
+	&jmp	(&label("mmx_loop"));
+
+    &set_label("mmx_loop",16);
+	&psrlq	($Zlo,4);
+	&and	($rem,0xf);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nhi));
+	&mov	(&LB($nlo),&BP(0,$inp,$cnt));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&dec	($cnt);
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nhi));
+	&mov	($nhi,$nlo);
+	&pxor	($Zlo,$tmp);
+	&js	(&label("mmx_break"));
+
+	&shl	(&LB($nlo),4);
+	&and	($rem,0xf);
+	&psrlq	($Zlo,4);
+	&and	($nhi,0xf0);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nlo));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nlo));
+	&pxor	($Zlo,$tmp);
+	&jmp	(&label("mmx_loop"));
+
+    &set_label("mmx_break",16);
+	&shl	(&LB($nlo),4);
+	&and	($rem,0xf);
+	&psrlq	($Zlo,4);
+	&and	($nhi,0xf0);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nlo));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nlo));
+	&pxor	($Zlo,$tmp);
+
+	&psrlq	($Zlo,4);
+	&and	($rem,0xf);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nhi));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nhi));
+	&pxor	($Zlo,$tmp);
+
+	&psrlq	($Zlo,32);	# lower part of Zlo is already there
+	&movd	($Zhl,$Zhi);
+	&psrlq	($Zhi,32);
+	&movd	($Zlh,$Zlo);
+	&movd	($Zhh,$Zhi);
+
+	&bswap	($Zll);
+	&bswap	($Zhl);
+	&bswap	($Zlh);
+	&bswap	($Zhh);
+}
+
+&function_begin("gcm_gmult_4bit_mmx");
+	&mov	($inp,&wparam(0));	# load Xi
+	&mov	($Htbl,&wparam(1));	# load Htable
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
+
+	&movz	($Zll,&BP(15,$inp));
+
+	&mmx_loop($inp,"eax");
+
+	&emms	();
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(0,$inp),$Zhh);
+&function_end("gcm_gmult_4bit_mmx");
+
+######################################################################
+# Below subroutine is "528B" variant of "4-bit" GCM GHASH function
+# (see gcm128.c for details). It provides further 20-40% performance
+# improvement over above mentioned "May" version.
+
+&static_label("rem_8bit");
+
+&function_begin("gcm_ghash_4bit_mmx");
+{ my ($Zlo,$Zhi) = ("mm7","mm6");
+  my $rem_8bit = "esi";
+  my $Htbl = "ebx";
+
+    # parameter block
+    &mov	("eax",&wparam(0));		# Xi
+    &mov	("ebx",&wparam(1));		# Htable
+    &mov	("ecx",&wparam(2));		# inp
+    &mov	("edx",&wparam(3));		# len
+    &mov	("ebp","esp");			# original %esp
+    &call	(&label("pic_point"));
+    &set_label	("pic_point");
+    &blindpop	($rem_8bit);
+    &lea	($rem_8bit,&DWP(&label("rem_8bit")."-".&label("pic_point"),$rem_8bit));
+
+    &sub	("esp",512+16+16);		# allocate stack frame...
+    &and	("esp",-64);			# ...and align it
+    &sub	("esp",16);			# place for (u8)(H[]<<4)
+
+    &add	("edx","ecx");			# pointer to the end of input
+    &mov	(&DWP(528+16+0,"esp"),"eax");	# save Xi
+    &mov	(&DWP(528+16+8,"esp"),"edx");	# save inp+len
+    &mov	(&DWP(528+16+12,"esp"),"ebp");	# save original %esp
+
+    { my @lo  = ("mm0","mm1","mm2");
+      my @hi  = ("mm3","mm4","mm5");
+      my @tmp = ("mm6","mm7");
+      my ($off1,$off2,$i) = (0,0,);
+
+      &add	($Htbl,128);			# optimize for size
+      &lea	("edi",&DWP(16+128,"esp"));
+      &lea	("ebp",&DWP(16+256+128,"esp"));
+
+      # decompose Htable (low and high parts are kept separately),
+      # generate Htable[]>>4, (u8)(Htable[]<<4), save to stack...
+      for ($i=0;$i<18;$i++) {
+
+	&mov	("edx",&DWP(16*$i+8-128,$Htbl))		if ($i<16);
+	&movq	($lo[0],&QWP(16*$i+8-128,$Htbl))	if ($i<16);
+	&psllq	($tmp[1],60)				if ($i>1);
+	&movq	($hi[0],&QWP(16*$i+0-128,$Htbl))	if ($i<16);
+	&por	($lo[2],$tmp[1])			if ($i>1);
+	&movq	(&QWP($off1-128,"edi"),$lo[1])		if ($i>0 && $i<17);
+	&psrlq	($lo[1],4)				if ($i>0 && $i<17);
+	&movq	(&QWP($off1,"edi"),$hi[1])		if ($i>0 && $i<17);
+	&movq	($tmp[0],$hi[1])			if ($i>0 && $i<17);
+	&movq	(&QWP($off2-128,"ebp"),$lo[2])		if ($i>1);
+	&psrlq	($hi[1],4)				if ($i>0 && $i<17);
+	&movq	(&QWP($off2,"ebp"),$hi[2])		if ($i>1);
+	&shl	("edx",4)				if ($i<16);
+	&mov	(&BP($i,"esp"),&LB("edx"))		if ($i<16);
+
+	unshift	(@lo,pop(@lo));			# "rotate" registers
+	unshift	(@hi,pop(@hi));
+	unshift	(@tmp,pop(@tmp));
+	$off1 += 8	if ($i>0);
+	$off2 += 8	if ($i>1);
+      }
+    }
+
+    &movq	($Zhi,&QWP(0,"eax"));
+    &mov	("ebx",&DWP(8,"eax"));
+    &mov	("edx",&DWP(12,"eax"));		# load Xi
+
+&set_label("outer",16);
+  { my $nlo = "eax";
+    my $dat = "edx";
+    my @nhi = ("edi","ebp");
+    my @rem = ("ebx","ecx");
+    my @red = ("mm0","mm1","mm2");
+    my $tmp = "mm3";
+
+    &xor	($dat,&DWP(12,"ecx"));		# merge input data
+    &xor	("ebx",&DWP(8,"ecx"));
+    &pxor	($Zhi,&QWP(0,"ecx"));
+    &lea	("ecx",&DWP(16,"ecx"));		# inp+=16
+    #&mov	(&DWP(528+12,"esp"),$dat);	# save inp^Xi
+    &mov	(&DWP(528+8,"esp"),"ebx");
+    &movq	(&QWP(528+0,"esp"),$Zhi);
+    &mov	(&DWP(528+16+4,"esp"),"ecx");	# save inp
+
+    &xor	($nlo,$nlo);
+    &rol	($dat,8);
+    &mov	(&LB($nlo),&LB($dat));
+    &mov	($nhi[1],$nlo);
+    &and	(&LB($nlo),0x0f);
+    &shr	($nhi[1],4);
+    &pxor	($red[0],$red[0]);
+    &rol	($dat,8);			# next byte
+    &pxor	($red[1],$red[1]);
+    &pxor	($red[2],$red[2]);
+
+    # Just like in "May" verson modulo-schedule for critical path in
+    # 'Z.hi ^= rem_8bit[Z.lo&0xff^((u8)H[nhi]<<4)]<<48'. Final 'pxor'
+    # is scheduled so late that rem_8bit[] has to be shifted *right*
+    # by 16, which is why last argument to pinsrw is 2, which
+    # corresponds to <<32=<<48>>16...
+    for ($j=11,$i=0;$i<15;$i++) {
+
+      if ($i>0) {
+	&pxor	($Zlo,&QWP(16,"esp",$nlo,8));		# Z^=H[nlo]
+	&rol	($dat,8);				# next byte
+	&pxor	($Zhi,&QWP(16+128,"esp",$nlo,8));
+
+	&pxor	($Zlo,$tmp);
+	&pxor	($Zhi,&QWP(16+256+128,"esp",$nhi[0],8));
+	&xor	(&LB($rem[1]),&BP(0,"esp",$nhi[0]));	# rem^(H[nhi]<<4)
+      } else {
+	&movq	($Zlo,&QWP(16,"esp",$nlo,8));
+	&movq	($Zhi,&QWP(16+128,"esp",$nlo,8));
+      }
+
+	&mov	(&LB($nlo),&LB($dat));
+	&mov	($dat,&DWP(528+$j,"esp"))		if (--$j%4==0);
+
+	&movd	($rem[0],$Zlo);
+	&movz	($rem[1],&LB($rem[1]))			if ($i>0);
+	&psrlq	($Zlo,8);				# Z>>=8
+
+	&movq	($tmp,$Zhi);
+	&mov	($nhi[0],$nlo);
+	&psrlq	($Zhi,8);
+
+	&pxor	($Zlo,&QWP(16+256+0,"esp",$nhi[1],8));	# Z^=H[nhi]>>4
+	&and	(&LB($nlo),0x0f);
+	&psllq	($tmp,56);
+
+	&pxor	($Zhi,$red[1])				if ($i>1);
+	&shr	($nhi[0],4);
+	&pinsrw	($red[0],&WP(0,$rem_8bit,$rem[1],2),2)	if ($i>0);
+
+	unshift	(@red,pop(@red));			# "rotate" registers
+	unshift	(@rem,pop(@rem));
+	unshift	(@nhi,pop(@nhi));
+    }
+
+    &pxor	($Zlo,&QWP(16,"esp",$nlo,8));		# Z^=H[nlo]
+    &pxor	($Zhi,&QWP(16+128,"esp",$nlo,8));
+    &xor	(&LB($rem[1]),&BP(0,"esp",$nhi[0]));	# rem^(H[nhi]<<4)
+
+    &pxor	($Zlo,$tmp);
+    &pxor	($Zhi,&QWP(16+256+128,"esp",$nhi[0],8));
+    &movz	($rem[1],&LB($rem[1]));
+
+    &pxor	($red[2],$red[2]);			# clear 2nd word
+    &psllq	($red[1],4);
+
+    &movd	($rem[0],$Zlo);
+    &psrlq	($Zlo,4);				# Z>>=4
+
+    &movq	($tmp,$Zhi);
+    &psrlq	($Zhi,4);
+    &shl	($rem[0],4);				# rem<<4
+
+    &pxor	($Zlo,&QWP(16,"esp",$nhi[1],8));	# Z^=H[nhi]
+    &psllq	($tmp,60);
+    &movz	($rem[0],&LB($rem[0]));
+
+    &pxor	($Zlo,$tmp);
+    &pxor	($Zhi,&QWP(16+128,"esp",$nhi[1],8));
+
+    &pinsrw	($red[0],&WP(0,$rem_8bit,$rem[1],2),2);
+    &pxor	($Zhi,$red[1]);
+
+    &movd	($dat,$Zlo);
+    &pinsrw	($red[2],&WP(0,$rem_8bit,$rem[0],2),3);	# last is <<48
+
+    &psllq	($red[0],12);				# correct by <<16>>4
+    &pxor	($Zhi,$red[0]);
+    &psrlq	($Zlo,32);
+    &pxor	($Zhi,$red[2]);
+
+    &mov	("ecx",&DWP(528+16+4,"esp"));	# restore inp
+    &movd	("ebx",$Zlo);
+    &movq	($tmp,$Zhi);			# 01234567
+    &psllw	($Zhi,8);			# 1.3.5.7.
+    &psrlw	($tmp,8);			# .0.2.4.6
+    &por	($Zhi,$tmp);			# 10325476
+    &bswap	($dat);
+    &pshufw	($Zhi,$Zhi,0b00011011);		# 76543210
+    &bswap	("ebx");
+    
+    &cmp	("ecx",&DWP(528+16+8,"esp"));	# are we done?
+    &jne	(&label("outer"));
+  }
+
+    &mov	("eax",&DWP(528+16+0,"esp"));	# restore Xi
+    &mov	(&DWP(12,"eax"),"edx");
+    &mov	(&DWP(8,"eax"),"ebx");
+    &movq	(&QWP(0,"eax"),$Zhi);
+
+    &mov	("esp",&DWP(528+16+12,"esp"));	# restore original %esp
+    &emms	();
+}
+&function_end("gcm_ghash_4bit_mmx");
+}}
+
+if ($sse2) {{
+######################################################################
+# PCLMULQDQ version.
+
+$Xip="eax";
+$Htbl="edx";
+$const="ecx";
+$inp="esi";
+$len="ebx";
+
+($Xi,$Xhi)=("xmm0","xmm1");	$Hkey="xmm2";
+($T1,$T2,$T3)=("xmm3","xmm4","xmm5");
+($Xn,$Xhn)=("xmm6","xmm7");
+
+&static_label("bswap");
+
+sub clmul64x64_T2 {	# minimal "register" pressure
+my ($Xhi,$Xi,$Hkey,$HK)=@_;
+
+	&movdqa		($Xhi,$Xi);		#
+	&pshufd		($T1,$Xi,0b01001110);
+	&pshufd		($T2,$Hkey,0b01001110)	if (!defined($HK));
+	&pxor		($T1,$Xi);		#
+	&pxor		($T2,$Hkey)		if (!defined($HK));
+			$HK=$T2			if (!defined($HK));
+
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pclmulqdq	($T1,$HK,0x00);		#######
+	&xorps		($T1,$Xi);		#
+	&xorps		($T1,$Xhi);		#
+
+	&movdqa		($T2,$T1);		#
+	&psrldq		($T1,8);
+	&pslldq		($T2,8);		#
+	&pxor		($Xhi,$T1);
+	&pxor		($Xi,$T2);		#
+}
+
+sub clmul64x64_T3 {
+# Even though this subroutine offers visually better ILP, it
+# was empirically found to be a tad slower than above version.
+# At least in gcm_ghash_clmul context. But it's just as well,
+# because loop modulo-scheduling is possible only thanks to
+# minimized "register" pressure...
+my ($Xhi,$Xi,$Hkey)=@_;
+
+	&movdqa		($T1,$Xi);		#
+	&movdqa		($Xhi,$Xi);
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pshufd		($T2,$T1,0b01001110);	#
+	&pshufd		($T3,$Hkey,0b01001110);
+	&pxor		($T2,$T1);		#
+	&pxor		($T3,$Hkey);
+	&pclmulqdq	($T2,$T3,0x00);		#######
+	&pxor		($T2,$Xi);		#
+	&pxor		($T2,$Xhi);		#
+
+	&movdqa		($T3,$T2);		#
+	&psrldq		($T2,8);
+	&pslldq		($T3,8);		#
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T3);		#
+}
+
+if (1) {		# Algorithm 9 with <<1 twist.
+			# Reduction is shorter and uses only two
+			# temporary registers, which makes it better
+			# candidate for interleaving with 64x64
+			# multiplication. Pre-modulo-scheduled loop
+			# was found to be ~20% faster than Algorithm 5
+			# below. Algorithm 9 was therefore chosen for
+			# further optimization...
+
+sub reduction_alg9 {	# 17/11 times faster than Intel version
+my ($Xhi,$Xi) = @_;
+
+	# 1st phase
+	&movdqa		($T2,$Xi);		#
+	&movdqa		($T1,$Xi);
+	&psllq		($Xi,5);
+	&pxor		($T1,$Xi);		#
+	&psllq		($Xi,1);
+	&pxor		($Xi,$T1);		#
+	&psllq		($Xi,57);		#
+	&movdqa		($T1,$Xi);		#
+	&pslldq		($Xi,8);
+	&psrldq		($T1,8);		#	
+	&pxor		($Xi,$T2);
+	&pxor		($Xhi,$T1);		#
+
+	# 2nd phase
+	&movdqa		($T2,$Xi);
+	&psrlq		($Xi,1);
+	&pxor		($Xhi,$T2);		#
+	&pxor		($T2,$Xi);
+	&psrlq		($Xi,5);
+	&pxor		($Xi,$T2);		#
+	&psrlq		($Xi,1);		#
+	&pxor		($Xi,$Xhi)		#
+}
+
+&function_begin_B("gcm_init_clmul");
+	&mov		($Htbl,&wparam(0));
+	&mov		($Xip,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Hkey,&QWP(0,$Xip));
+	&pshufd		($Hkey,$Hkey,0b01001110);# dword swap
+
+	# <<1 twist
+	&pshufd		($T2,$Hkey,0b11111111);	# broadcast uppermost dword
+	&movdqa		($T1,$Hkey);
+	&psllq		($Hkey,1);
+	&pxor		($T3,$T3);		#
+	&psrlq		($T1,63);
+	&pcmpgtd	($T3,$T2);		# broadcast carry bit
+	&pslldq		($T1,8);
+	&por		($Hkey,$T1);		# H<<=1
+
+	# magic reduction
+	&pand		($T3,&QWP(16,$const));	# 0x1c2_polynomial
+	&pxor		($Hkey,$T3);		# if(carry) H^=0x1c2_polynomial
+
+	# calculate H^2
+	&movdqa		($Xi,$Hkey);
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey);
+	&reduction_alg9	($Xhi,$Xi);
+
+	&pshufd		($T1,$Hkey,0b01001110);
+	&pshufd		($T2,$Xi,0b01001110);
+	&pxor		($T1,$Hkey);		# Karatsuba pre-processing
+	&movdqu		(&QWP(0,$Htbl),$Hkey);	# save H
+	&pxor		($T2,$Xi);		# Karatsuba pre-processing
+	&movdqu		(&QWP(16,$Htbl),$Xi);	# save H^2
+	&palignr	($T2,$T1,8);		# low part is H.lo^H.hi
+	&movdqu		(&QWP(32,$Htbl),$T2);	# save Karatsuba "salt"
+
+	&ret		();
+&function_end_B("gcm_init_clmul");
+
+&function_begin_B("gcm_gmult_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($T3,&QWP(0,$const));
+	&movups		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$T3);
+	&movups		($T2,&QWP(32,$Htbl));
+
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$T2);
+	&reduction_alg9	($Xhi,$Xi);
+
+	&pshufb		($Xi,$T3);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+
+	&ret	();
+&function_end_B("gcm_gmult_clmul");
+
+&function_begin("gcm_ghash_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+	&mov		($inp,&wparam(2));
+	&mov		($len,&wparam(3));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($T3,&QWP(0,$const));
+	&movdqu		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$T3);
+
+	&sub		($len,0x10);
+	&jz		(&label("odd_tail"));
+
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&movdqu		($Xn,&QWP(16,$inp));	# Ii+1
+	&pshufb		($T1,$T3);
+	&pshufb		($Xn,$T3);
+	&movdqu		($T3,&QWP(32,$Htbl));
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&pshufd		($T1,$Xn,0b01001110);	# H*Ii+1
+	&movdqa		($Xhn,$Xn);
+	&pxor		($T1,$Xn);		#
+
+	&pclmulqdq	($Xn,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhn,$Hkey,0x11);	#######
+	&movups		($Hkey,&QWP(16,$Htbl));	# load H^2
+	&pclmulqdq	($T1,$T3,0x00);		#######
+
+	&lea		($inp,&DWP(32,$inp));	# i+=2
+	&sub		($len,0x20);
+	&jbe		(&label("even_tail"));
+	&jmp		(&label("mod_loop"));
+
+&set_label("mod_loop",32);
+	&pshufd		($T2,$Xi,0b01001110);	# H^2*(Ii+Xi)
+	&movdqa		($Xhi,$Xi);
+	&pxor		($T2,$Xi);		#
+
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&movups		($Hkey,&QWP(0,$Htbl));	# load H
+	&pclmulqdq	($T2,$T3,0x10);		#######
+	&movdqa		($T3,&QWP(0,$const));
+
+	&xorps		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&xorps		($Xhi,$Xhn);
+	 &movdqu	($Xhn,&QWP(0,$inp));	# Ii
+	&pxor		($T1,$Xi);		# aggregated Karatsuba post-processing
+	 &movdqu	($Xn,&QWP(16,$inp));	# Ii+1
+	&pxor		($T1,$Xhi);		#
+
+	&pxor		($T2,$T1);		#
+	 &pshufb	($Xhn,$T3);
+
+	&movdqa		($T1,$T2);		#
+	&psrldq		($T2,8);
+	&pslldq		($T1,8);		#
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T1);		#
+	 &pshufb	($Xn,$T3);
+	 &pxor		($Xhi,$Xhn);		# "Ii+Xi", consume early
+
+	&movdqa		($Xhn,$Xn);		#&clmul64x64_TX	($Xhn,$Xn,$Hkey); H*Ii+1
+	  &movdqa	($T2,$Xi);		#&reduction_alg9($Xhi,$Xi); 1st phase
+	  &movdqa	($T1,$Xi);
+	  &psllq	($Xi,5);
+	  &pxor		($T1,$Xi);		#
+	  &psllq	($Xi,1);
+	  &pxor		($Xi,$T1);		#
+	&movups		($T3,&QWP(32,$Htbl));
+	&pclmulqdq	($Xn,$Hkey,0x00);	#######
+	  &psllq	($Xi,57);		#
+	  &movdqa	($T1,$Xi);		#
+	  &pslldq	($Xi,8);
+	  &psrldq	($T1,8);		#	
+	  &pxor		($Xi,$T2);
+	  &pxor		($Xhi,$T1);		#
+	&pshufd		($T1,$Xhn,0b01001110);
+	  &movdqa	($T2,$Xi);		# 2nd phase
+	  &psrlq	($Xi,1);
+	&pxor		($T1,$Xhn);
+	&pclmulqdq	($Xhn,$Hkey,0x11);	#######
+	&movups		($Hkey,&QWP(16,$Htbl));	# load H^2
+	  &pxor		($Xhi,$T2);		#
+	  &pxor		($T2,$Xi);
+	  &psrlq	($Xi,5);
+	  &pxor		($Xi,$T2);		#
+	  &psrlq	($Xi,1);		#
+	  &pxor		($Xi,$Xhi)		#
+	&pclmulqdq	($T1,$T3,0x00);		#######
+
+	&lea		($inp,&DWP(32,$inp));
+	&sub		($len,0x20);
+	&ja		(&label("mod_loop"));
+
+&set_label("even_tail");
+	&pshufd		($T2,$Xi,0b01001110);	# H^2*(Ii+Xi)
+	&movdqa		($Xhi,$Xi);
+	&pxor		($T2,$Xi);		#
+
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pclmulqdq	($T2,$T3,0x10);		#######
+	&movdqa		($T3,&QWP(0,$const));
+
+	&xorps		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&xorps		($Xhi,$Xhn);
+	&pxor		($T1,$Xi);		# aggregated Karatsuba post-processing
+	&pxor		($T1,$Xhi);		#
+
+	&pxor		($T2,$T1);		#
+
+	&movdqa		($T1,$T2);		#
+	&psrldq		($T2,8);
+	&pslldq		($T1,8);		#
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T1);		#
+
+	&reduction_alg9	($Xhi,$Xi);
+
+	&test		($len,$len);
+	&jnz		(&label("done"));
+
+	&movups		($Hkey,&QWP(0,$Htbl));	# load H
+&set_label("odd_tail");
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&pshufb		($T1,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey);	# H*(Ii+Xi)
+	&reduction_alg9	($Xhi,$Xi);
+
+&set_label("done");
+	&pshufb		($Xi,$T3);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+&function_end("gcm_ghash_clmul");
+
+} else {		# Algorith 5. Kept for reference purposes.
+
+sub reduction_alg5 {	# 19/16 times faster than Intel version
+my ($Xhi,$Xi)=@_;
+
+	# <<1
+	&movdqa		($T1,$Xi);		#
+	&movdqa		($T2,$Xhi);
+	&pslld		($Xi,1);
+	&pslld		($Xhi,1);		#
+	&psrld		($T1,31);
+	&psrld		($T2,31);		#
+	&movdqa		($T3,$T1);
+	&pslldq		($T1,4);
+	&psrldq		($T3,12);		#
+	&pslldq		($T2,4);
+	&por		($Xhi,$T3);		#
+	&por		($Xi,$T1);
+	&por		($Xhi,$T2);		#
+
+	# 1st phase
+	&movdqa		($T1,$Xi);
+	&movdqa		($T2,$Xi);
+	&movdqa		($T3,$Xi);		#
+	&pslld		($T1,31);
+	&pslld		($T2,30);
+	&pslld		($Xi,25);		#
+	&pxor		($T1,$T2);
+	&pxor		($T1,$Xi);		#
+	&movdqa		($T2,$T1);		#
+	&pslldq		($T1,12);
+	&psrldq		($T2,4);		#
+	&pxor		($T3,$T1);
+
+	# 2nd phase
+	&pxor		($Xhi,$T3);		#
+	&movdqa		($Xi,$T3);
+	&movdqa		($T1,$T3);
+	&psrld		($Xi,1);		#
+	&psrld		($T1,2);
+	&psrld		($T3,7);		#
+	&pxor		($Xi,$T1);
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T3);		#
+	&pxor		($Xi,$Xhi);		#
+}
+
+&function_begin_B("gcm_init_clmul");
+	&mov		($Htbl,&wparam(0));
+	&mov		($Xip,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Hkey,&QWP(0,$Xip));
+	&pshufd		($Hkey,$Hkey,0b01001110);# dword swap
+
+	# calculate H^2
+	&movdqa		($Xi,$Hkey);
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);
+	&reduction_alg5	($Xhi,$Xi);
+
+	&movdqu		(&QWP(0,$Htbl),$Hkey);	# save H
+	&movdqu		(&QWP(16,$Htbl),$Xi);	# save H^2
+
+	&ret		();
+&function_end_B("gcm_init_clmul");
+
+&function_begin_B("gcm_gmult_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($Xn,&QWP(0,$const));
+	&movdqu		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$Xn);
+
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);
+	&reduction_alg5	($Xhi,$Xi);
+
+	&pshufb		($Xi,$Xn);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+
+	&ret	();
+&function_end_B("gcm_gmult_clmul");
+
+&function_begin("gcm_ghash_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+	&mov		($inp,&wparam(2));
+	&mov		($len,&wparam(3));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($T3,&QWP(0,$const));
+	&movdqu		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$T3);
+
+	&sub		($len,0x10);
+	&jz		(&label("odd_tail"));
+
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&movdqu		($Xn,&QWP(16,$inp));	# Ii+1
+	&pshufb		($T1,$T3);
+	&pshufb		($Xn,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T3	($Xhn,$Xn,$Hkey);	# H*Ii+1
+	&movdqu		($Hkey,&QWP(16,$Htbl));	# load H^2
+
+	&sub		($len,0x20);
+	&lea		($inp,&DWP(32,$inp));	# i+=2
+	&jbe		(&label("even_tail"));
+
+&set_label("mod_loop");
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);	# H^2*(Ii+Xi)
+	&movdqu		($Hkey,&QWP(0,$Htbl));	# load H
+
+	&pxor		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&pxor		($Xhi,$Xhn);
+
+	&reduction_alg5	($Xhi,$Xi);
+
+	#######
+	&movdqa		($T3,&QWP(0,$const));
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&movdqu		($Xn,&QWP(16,$inp));	# Ii+1
+	&pshufb		($T1,$T3);
+	&pshufb		($Xn,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T3	($Xhn,$Xn,$Hkey);	# H*Ii+1
+	&movdqu		($Hkey,&QWP(16,$Htbl));	# load H^2
+
+	&sub		($len,0x20);
+	&lea		($inp,&DWP(32,$inp));
+	&ja		(&label("mod_loop"));
+
+&set_label("even_tail");
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);	# H^2*(Ii+Xi)
+
+	&pxor		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&pxor		($Xhi,$Xhn);
+
+	&reduction_alg5	($Xhi,$Xi);
+
+	&movdqa		($T3,&QWP(0,$const));
+	&test		($len,$len);
+	&jnz		(&label("done"));
+
+	&movdqu		($Hkey,&QWP(0,$Htbl));	# load H
+&set_label("odd_tail");
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&pshufb		($T1,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);	# H*(Ii+Xi)
+	&reduction_alg5	($Xhi,$Xi);
+
+	&movdqa		($T3,&QWP(0,$const));
+&set_label("done");
+	&pshufb		($Xi,$T3);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+&function_end("gcm_ghash_clmul");
+
+}
+
+&set_label("bswap",64);
+	&data_byte(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
+	&data_byte(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2);	# 0x1c2_polynomial
+&set_label("rem_8bit",64);
+	&data_short(0x0000,0x01C2,0x0384,0x0246,0x0708,0x06CA,0x048C,0x054E);
+	&data_short(0x0E10,0x0FD2,0x0D94,0x0C56,0x0918,0x08DA,0x0A9C,0x0B5E);
+	&data_short(0x1C20,0x1DE2,0x1FA4,0x1E66,0x1B28,0x1AEA,0x18AC,0x196E);
+	&data_short(0x1230,0x13F2,0x11B4,0x1076,0x1538,0x14FA,0x16BC,0x177E);
+	&data_short(0x3840,0x3982,0x3BC4,0x3A06,0x3F48,0x3E8A,0x3CCC,0x3D0E);
+	&data_short(0x3650,0x3792,0x35D4,0x3416,0x3158,0x309A,0x32DC,0x331E);
+	&data_short(0x2460,0x25A2,0x27E4,0x2626,0x2368,0x22AA,0x20EC,0x212E);
+	&data_short(0x2A70,0x2BB2,0x29F4,0x2836,0x2D78,0x2CBA,0x2EFC,0x2F3E);
+	&data_short(0x7080,0x7142,0x7304,0x72C6,0x7788,0x764A,0x740C,0x75CE);
+	&data_short(0x7E90,0x7F52,0x7D14,0x7CD6,0x7998,0x785A,0x7A1C,0x7BDE);
+	&data_short(0x6CA0,0x6D62,0x6F24,0x6EE6,0x6BA8,0x6A6A,0x682C,0x69EE);
+	&data_short(0x62B0,0x6372,0x6134,0x60F6,0x65B8,0x647A,0x663C,0x67FE);
+	&data_short(0x48C0,0x4902,0x4B44,0x4A86,0x4FC8,0x4E0A,0x4C4C,0x4D8E);
+	&data_short(0x46D0,0x4712,0x4554,0x4496,0x41D8,0x401A,0x425C,0x439E);
+	&data_short(0x54E0,0x5522,0x5764,0x56A6,0x53E8,0x522A,0x506C,0x51AE);
+	&data_short(0x5AF0,0x5B32,0x5974,0x58B6,0x5DF8,0x5C3A,0x5E7C,0x5FBE);
+	&data_short(0xE100,0xE0C2,0xE284,0xE346,0xE608,0xE7CA,0xE58C,0xE44E);
+	&data_short(0xEF10,0xEED2,0xEC94,0xED56,0xE818,0xE9DA,0xEB9C,0xEA5E);
+	&data_short(0xFD20,0xFCE2,0xFEA4,0xFF66,0xFA28,0xFBEA,0xF9AC,0xF86E);
+	&data_short(0xF330,0xF2F2,0xF0B4,0xF176,0xF438,0xF5FA,0xF7BC,0xF67E);
+	&data_short(0xD940,0xD882,0xDAC4,0xDB06,0xDE48,0xDF8A,0xDDCC,0xDC0E);
+	&data_short(0xD750,0xD692,0xD4D4,0xD516,0xD058,0xD19A,0xD3DC,0xD21E);
+	&data_short(0xC560,0xC4A2,0xC6E4,0xC726,0xC268,0xC3AA,0xC1EC,0xC02E);
+	&data_short(0xCB70,0xCAB2,0xC8F4,0xC936,0xCC78,0xCDBA,0xCFFC,0xCE3E);
+	&data_short(0x9180,0x9042,0x9204,0x93C6,0x9688,0x974A,0x950C,0x94CE);
+	&data_short(0x9F90,0x9E52,0x9C14,0x9DD6,0x9898,0x995A,0x9B1C,0x9ADE);
+	&data_short(0x8DA0,0x8C62,0x8E24,0x8FE6,0x8AA8,0x8B6A,0x892C,0x88EE);
+	&data_short(0x83B0,0x8272,0x8034,0x81F6,0x84B8,0x857A,0x873C,0x86FE);
+	&data_short(0xA9C0,0xA802,0xAA44,0xAB86,0xAEC8,0xAF0A,0xAD4C,0xAC8E);
+	&data_short(0xA7D0,0xA612,0xA454,0xA596,0xA0D8,0xA11A,0xA35C,0xA29E);
+	&data_short(0xB5E0,0xB422,0xB664,0xB7A6,0xB2E8,0xB32A,0xB16C,0xB0AE);
+	&data_short(0xBBF0,0xBA32,0xB874,0xB9B6,0xBCF8,0xBD3A,0xBF7C,0xBEBE);
+}}	# $sse2
+
+&set_label("rem_4bit",64);
+	&data_word(0,0x0000<<$S,0,0x1C20<<$S,0,0x3840<<$S,0,0x2460<<$S);
+	&data_word(0,0x7080<<$S,0,0x6CA0<<$S,0,0x48C0<<$S,0,0x54E0<<$S);
+	&data_word(0,0xE100<<$S,0,0xFD20<<$S,0,0xD940<<$S,0,0xC560<<$S);
+	&data_word(0,0x9180<<$S,0,0x8DA0<<$S,0,0xA9C0<<$S,0,0xB5E0<<$S);
+}}}	# !$x86only
+
+&asciz("GHASH for x86, CRYPTOGAMS by <appro\@openssl.org>");
+&asm_finish();
+
+# A question was risen about choice of vanilla MMX. Or rather why wasn't
+# SSE2 chosen instead? In addition to the fact that MMX runs on legacy
+# CPUs such as PIII, "4-bit" MMX version was observed to provide better
+# performance than *corresponding* SSE2 one even on contemporary CPUs.
+# SSE2 results were provided by Peter-Michael Hager. He maintains SSE2
+# implementation featuring full range of lookup-table sizes, but with
+# per-invocation lookup table setup. Latter means that table size is
+# chosen depending on how much data is to be hashed in every given call,
+# more data - larger table. Best reported result for Core2 is ~4 cycles
+# per processed byte out of 64KB block. This number accounts even for
+# 64KB table setup overhead. As discussed in gcm128.c we choose to be
+# more conservative in respect to lookup table sizes, but how do the
+# results compare? Minimalistic "256B" MMX version delivers ~11 cycles
+# on same platform. As also discussed in gcm128.c, next in line "8-bit
+# Shoup's" or "4KB" method should deliver twice the performance of
+# "256B" one, in other words not worse than ~6 cycles per byte. It
+# should be also be noted that in SSE2 case improvement can be "super-
+# linear," i.e. more than twice, mostly because >>8 maps to single
+# instruction on SSE2 register. This is unlike "4-bit" case when >>4
+# maps to same amount of instructions in both MMX and SSE2 cases.
+# Bottom line is that switch to SSE2 is considered to be justifiable
+# only in case we choose to implement "8-bit" method...
diff --git a/crypto/modes/asm/ghash-x86_64.pl b/crypto/modes/asm/ghash-x86_64.pl
new file mode 100644
index 0000000..7904248
--- /dev/null
+++ b/crypto/modes/asm/ghash-x86_64.pl
@@ -0,0 +1,1736 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# March, June 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that
+# it uses 256 bytes per-key table [+128 bytes shared table]. GHASH
+# function features so called "528B" variant utilizing additional
+# 256+16 bytes of per-key storage [+512 bytes shared table].
+# Performance results are for this streamed GHASH subroutine and are
+# expressed in cycles per processed byte, less is better:
+#
+#		gcc 3.4.x(*)	assembler
+#
+# P4		28.6		14.0		+100%
+# Opteron	19.3		7.7		+150%
+# Core2		17.8		8.1(**)		+120%
+# Atom		31.6		16.8		+88%
+# VIA Nano	21.8		10.1		+115%
+#
+# (*)	comparison is not completely fair, because C results are
+#	for vanilla "256B" implementation, while assembler results
+#	are for "528B";-)
+# (**)	it's mystery [to me] why Core2 result is not same as for
+#	Opteron;
+
+# May 2010
+#
+# Add PCLMULQDQ version performing at 2.02 cycles per processed byte.
+# See ghash-x86.pl for background information and details about coding
+# techniques.
+#
+# Special thanks to David Woodhouse <dwmw2@infradead.org> for
+# providing access to a Westmere-based system on behalf of Intel
+# Open Source Technology Centre.
+
+# December 2012
+#
+# Overhaul: aggregate Karatsuba post-processing, improve ILP in
+# reduction_alg9, increase reduction aggregate factor to 4x. As for
+# the latter. ghash-x86.pl discusses that it makes lesser sense to
+# increase aggregate factor. Then why increase here? Critical path
+# consists of 3 independent pclmulqdq instructions, Karatsuba post-
+# processing and reduction. "On top" of this we lay down aggregated
+# multiplication operations, triplets of independent pclmulqdq's. As
+# issue rate for pclmulqdq is limited, it makes lesser sense to
+# aggregate more multiplications than it takes to perform remaining
+# non-multiplication operations. 2x is near-optimal coefficient for
+# contemporary Intel CPUs (therefore modest improvement coefficient),
+# but not for Bulldozer. Latter is because logical SIMD operations
+# are twice as slow in comparison to Intel, so that critical path is
+# longer. A CPU with higher pclmulqdq issue rate would also benefit
+# from higher aggregate factor...
+#
+# Westmere	1.76(+14%)
+# Sandy Bridge	1.79(+9%)
+# Ivy Bridge	1.79(+8%)
+# Haswell	0.55(+93%) (if system doesn't support AVX)
+# Bulldozer	1.52(+25%)
+
+# March 2013
+#
+# ... 8x aggregate factor AVX code path is using reduction algorithm
+# suggested by Shay Gueron[1]. Even though contemporary AVX-capable
+# CPUs such as Sandy and Ivy Bridge can execute it, the code performs
+# sub-optimally in comparison to above mentioned version. But thanks
+# to Ilya Albrekht and Max Locktyukhin of Intel Corp. we knew that
+# it performs in 0.41 cycles per byte on Haswell processor.
+#
+# [1] http://rt.openssl.org/Ticket/Display.html?id=2900&user=guest&pass=guest
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$do4xaggr=1;
+
+# common register layout
+$nlo="%rax";
+$nhi="%rbx";
+$Zlo="%r8";
+$Zhi="%r9";
+$tmp="%r10";
+$rem_4bit = "%r11";
+
+$Xi="%rdi";
+$Htbl="%rsi";
+
+# per-function register layout
+$cnt="%rcx";
+$rem="%rdx";
+
+sub LB() { my $r=shift; $r =~ s/%[er]([a-d])x/%\1l/	or
+			$r =~ s/%[er]([sd]i)/%\1l/	or
+			$r =~ s/%[er](bp)/%\1l/		or
+			$r =~ s/%(r[0-9]+)[d]?/%\1b/;   $r; }
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+{ my $N;
+  sub loop() {
+  my $inp = shift;
+
+	$N++;
+$code.=<<___;
+	xor	$nlo,$nlo
+	xor	$nhi,$nhi
+	mov	`&LB("$Zlo")`,`&LB("$nlo")`
+	mov	`&LB("$Zlo")`,`&LB("$nhi")`
+	shl	\$4,`&LB("$nlo")`
+	mov	\$14,$cnt
+	mov	8($Htbl,$nlo),$Zlo
+	mov	($Htbl,$nlo),$Zhi
+	and	\$0xf0,`&LB("$nhi")`
+	mov	$Zlo,$rem
+	jmp	.Loop$N
+
+.align	16
+.Loop$N:
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	mov	($inp,$cnt),`&LB("$nlo")`
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nhi),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nhi),$Zhi
+	mov	`&LB("$nlo")`,`&LB("$nhi")`
+	xor	($rem_4bit,$rem,8),$Zhi
+	mov	$Zlo,$rem
+	shl	\$4,`&LB("$nlo")`
+	xor	$tmp,$Zlo
+	dec	$cnt
+	js	.Lbreak$N
+
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nlo),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nlo),$Zhi
+	and	\$0xf0,`&LB("$nhi")`
+	xor	($rem_4bit,$rem,8),$Zhi
+	mov	$Zlo,$rem
+	xor	$tmp,$Zlo
+	jmp	.Loop$N
+
+.align	16
+.Lbreak$N:
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nlo),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nlo),$Zhi
+	and	\$0xf0,`&LB("$nhi")`
+	xor	($rem_4bit,$rem,8),$Zhi
+	mov	$Zlo,$rem
+	xor	$tmp,$Zlo
+
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nhi),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nhi),$Zhi
+	xor	$tmp,$Zlo
+	xor	($rem_4bit,$rem,8),$Zhi
+
+	bswap	$Zlo
+	bswap	$Zhi
+___
+}}
+
+$code=<<___;
+.text
+
+.globl	gcm_gmult_4bit
+.type	gcm_gmult_4bit,\@function,2
+.align	16
+gcm_gmult_4bit:
+	push	%rbx
+	push	%rbp		# %rbp and %r12 are pushed exclusively in
+	push	%r12		# order to reuse Win64 exception handler...
+.Lgmult_prologue:
+
+	movzb	15($Xi),$Zlo
+	lea	.Lrem_4bit(%rip),$rem_4bit
+___
+	&loop	($Xi);
+$code.=<<___;
+	mov	$Zlo,8($Xi)
+	mov	$Zhi,($Xi)
+
+	mov	16(%rsp),%rbx
+	lea	24(%rsp),%rsp
+.Lgmult_epilogue:
+	ret
+.size	gcm_gmult_4bit,.-gcm_gmult_4bit
+___
+
+# per-function register layout
+$inp="%rdx";
+$len="%rcx";
+$rem_8bit=$rem_4bit;
+
+$code.=<<___;
+.globl	gcm_ghash_4bit
+.type	gcm_ghash_4bit,\@function,4
+.align	16
+gcm_ghash_4bit:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$280,%rsp
+.Lghash_prologue:
+	mov	$inp,%r14		# reassign couple of args
+	mov	$len,%r15
+___
+{ my $inp="%r14";
+  my $dat="%edx";
+  my $len="%r15";
+  my @nhi=("%ebx","%ecx");
+  my @rem=("%r12","%r13");
+  my $Hshr4="%rbp";
+
+	&sub	($Htbl,-128);		# size optimization
+	&lea	($Hshr4,"16+128(%rsp)");
+	{ my @lo =($nlo,$nhi);
+          my @hi =($Zlo,$Zhi);
+
+	  &xor	($dat,$dat);
+	  for ($i=0,$j=-2;$i<18;$i++,$j++) {
+	    &mov	("$j(%rsp)",&LB($dat))		if ($i>1);
+	    &or		($lo[0],$tmp)			if ($i>1);
+	    &mov	(&LB($dat),&LB($lo[1]))		if ($i>0 && $i<17);
+	    &shr	($lo[1],4)			if ($i>0 && $i<17);
+	    &mov	($tmp,$hi[1])			if ($i>0 && $i<17);
+	    &shr	($hi[1],4)			if ($i>0 && $i<17);
+	    &mov	("8*$j($Hshr4)",$hi[0])		if ($i>1);
+	    &mov	($hi[0],"16*$i+0-128($Htbl)")	if ($i<16);
+	    &shl	(&LB($dat),4)			if ($i>0 && $i<17);
+	    &mov	("8*$j-128($Hshr4)",$lo[0])	if ($i>1);
+	    &mov	($lo[0],"16*$i+8-128($Htbl)")	if ($i<16);
+	    &shl	($tmp,60)			if ($i>0 && $i<17);
+
+	    push	(@lo,shift(@lo));
+	    push	(@hi,shift(@hi));
+	  }
+	}
+	&add	($Htbl,-128);
+	&mov	($Zlo,"8($Xi)");
+	&mov	($Zhi,"0($Xi)");
+	&add	($len,$inp);		# pointer to the end of data
+	&lea	($rem_8bit,".Lrem_8bit(%rip)");
+	&jmp	(".Louter_loop");
+
+$code.=".align	16\n.Louter_loop:\n";
+	&xor	($Zhi,"($inp)");
+	&mov	("%rdx","8($inp)");
+	&lea	($inp,"16($inp)");
+	&xor	("%rdx",$Zlo);
+	&mov	("($Xi)",$Zhi);
+	&mov	("8($Xi)","%rdx");
+	&shr	("%rdx",32);
+
+	&xor	($nlo,$nlo);
+	&rol	($dat,8);
+	&mov	(&LB($nlo),&LB($dat));
+	&movz	($nhi[0],&LB($dat));
+	&shl	(&LB($nlo),4);
+	&shr	($nhi[0],4);
+
+	for ($j=11,$i=0;$i<15;$i++) {
+	    &rol	($dat,8);
+	    &xor	($Zlo,"8($Htbl,$nlo)")			if ($i>0);
+	    &xor	($Zhi,"($Htbl,$nlo)")			if ($i>0);
+	    &mov	($Zlo,"8($Htbl,$nlo)")			if ($i==0);
+	    &mov	($Zhi,"($Htbl,$nlo)")			if ($i==0);
+
+	    &mov	(&LB($nlo),&LB($dat));
+	    &xor	($Zlo,$tmp)				if ($i>0);
+	    &movzw	($rem[1],"($rem_8bit,$rem[1],2)")	if ($i>0);
+
+	    &movz	($nhi[1],&LB($dat));
+	    &shl	(&LB($nlo),4);
+	    &movzb	($rem[0],"(%rsp,$nhi[0])");
+
+	    &shr	($nhi[1],4)				if ($i<14);
+	    &and	($nhi[1],0xf0)				if ($i==14);
+	    &shl	($rem[1],48)				if ($i>0);
+	    &xor	($rem[0],$Zlo);
+
+	    &mov	($tmp,$Zhi);
+	    &xor	($Zhi,$rem[1])				if ($i>0);
+	    &shr	($Zlo,8);
+
+	    &movz	($rem[0],&LB($rem[0]));
+	    &mov	($dat,"$j($Xi)")			if (--$j%4==0);
+	    &shr	($Zhi,8);
+
+	    &xor	($Zlo,"-128($Hshr4,$nhi[0],8)");
+	    &shl	($tmp,56);
+	    &xor	($Zhi,"($Hshr4,$nhi[0],8)");
+
+	    unshift	(@nhi,pop(@nhi));		# "rotate" registers
+	    unshift	(@rem,pop(@rem));
+	}
+	&movzw	($rem[1],"($rem_8bit,$rem[1],2)");
+	&xor	($Zlo,"8($Htbl,$nlo)");
+	&xor	($Zhi,"($Htbl,$nlo)");
+
+	&shl	($rem[1],48);
+	&xor	($Zlo,$tmp);
+
+	&xor	($Zhi,$rem[1]);
+	&movz	($rem[0],&LB($Zlo));
+	&shr	($Zlo,4);
+
+	&mov	($tmp,$Zhi);
+	&shl	(&LB($rem[0]),4);
+	&shr	($Zhi,4);
+
+	&xor	($Zlo,"8($Htbl,$nhi[0])");
+	&movzw	($rem[0],"($rem_8bit,$rem[0],2)");
+	&shl	($tmp,60);
+
+	&xor	($Zhi,"($Htbl,$nhi[0])");
+	&xor	($Zlo,$tmp);
+	&shl	($rem[0],48);
+
+	&bswap	($Zlo);
+	&xor	($Zhi,$rem[0]);
+
+	&bswap	($Zhi);
+	&cmp	($inp,$len);
+	&jb	(".Louter_loop");
+}
+$code.=<<___;
+	mov	$Zlo,8($Xi)
+	mov	$Zhi,($Xi)
+
+	lea	280(%rsp),%rsi
+	mov	0(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lghash_epilogue:
+	ret
+.size	gcm_ghash_4bit,.-gcm_ghash_4bit
+___
+
+######################################################################
+# PCLMULQDQ version.
+
+@_4args=$win64?	("%rcx","%rdx","%r8", "%r9") :	# Win64 order
+		("%rdi","%rsi","%rdx","%rcx");	# Unix order
+
+($Xi,$Xhi)=("%xmm0","%xmm1");	$Hkey="%xmm2";
+($T1,$T2,$T3)=("%xmm3","%xmm4","%xmm5");
+
+sub clmul64x64_T2 {	# minimal register pressure
+my ($Xhi,$Xi,$Hkey,$HK)=@_;
+
+if (!defined($HK)) {	$HK = $T2;
+$code.=<<___;
+	movdqa		$Xi,$Xhi		#
+	pshufd		\$0b01001110,$Xi,$T1
+	pshufd		\$0b01001110,$Hkey,$T2
+	pxor		$Xi,$T1			#
+	pxor		$Hkey,$T2
+___
+} else {
+$code.=<<___;
+	movdqa		$Xi,$Xhi		#
+	pshufd		\$0b01001110,$Xi,$T1
+	pxor		$Xi,$T1			#
+___
+}
+$code.=<<___;
+	pclmulqdq	\$0x00,$Hkey,$Xi	#######
+	pclmulqdq	\$0x11,$Hkey,$Xhi	#######
+	pclmulqdq	\$0x00,$HK,$T1		#######
+	pxor		$Xi,$T1			#
+	pxor		$Xhi,$T1		#
+
+	movdqa		$T1,$T2			#
+	psrldq		\$8,$T1
+	pslldq		\$8,$T2			#
+	pxor		$T1,$Xhi
+	pxor		$T2,$Xi			#
+___
+}
+
+sub reduction_alg9 {	# 17/11 times faster than Intel version
+my ($Xhi,$Xi) = @_;
+
+$code.=<<___;
+	# 1st phase
+	movdqa		$Xi,$T2			#
+	movdqa		$Xi,$T1
+	psllq		\$5,$Xi
+	pxor		$Xi,$T1			#
+	psllq		\$1,$Xi
+	pxor		$T1,$Xi			#
+	psllq		\$57,$Xi		#
+	movdqa		$Xi,$T1			#
+	pslldq		\$8,$Xi
+	psrldq		\$8,$T1			#	
+	pxor		$T2,$Xi
+	pxor		$T1,$Xhi		#
+
+	# 2nd phase
+	movdqa		$Xi,$T2
+	psrlq		\$1,$Xi
+	pxor		$T2,$Xhi		#
+	pxor		$Xi,$T2
+	psrlq		\$5,$Xi
+	pxor		$T2,$Xi			#
+	psrlq		\$1,$Xi			#
+	pxor		$Xhi,$Xi		#
+___
+}
+
+{ my ($Htbl,$Xip)=@_4args;
+  my $HK="%xmm6";
+
+$code.=<<___;
+.globl	gcm_init_clmul
+.type	gcm_init_clmul,\@abi-omnipotent
+.align	16
+gcm_init_clmul:
+.L_init_clmul:
+___
+$code.=<<___ if ($win64);
+.LSEH_begin_gcm_init_clmul:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x83,0xec,0x18		#sub	$0x18,%rsp
+	.byte	0x0f,0x29,0x34,0x24		#movaps	%xmm6,(%rsp)
+___
+$code.=<<___;
+	movdqu		($Xip),$Hkey
+	pshufd		\$0b01001110,$Hkey,$Hkey	# dword swap
+
+	# <<1 twist
+	pshufd		\$0b11111111,$Hkey,$T2	# broadcast uppermost dword
+	movdqa		$Hkey,$T1
+	psllq		\$1,$Hkey
+	pxor		$T3,$T3			#
+	psrlq		\$63,$T1
+	pcmpgtd		$T2,$T3			# broadcast carry bit
+	pslldq		\$8,$T1
+	por		$T1,$Hkey		# H<<=1
+
+	# magic reduction
+	pand		.L0x1c2_polynomial(%rip),$T3
+	pxor		$T3,$Hkey		# if(carry) H^=0x1c2_polynomial
+
+	# calculate H^2
+	pshufd		\$0b01001110,$Hkey,$HK
+	movdqa		$Hkey,$Xi
+	pxor		$Hkey,$HK
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	pshufd		\$0b01001110,$Hkey,$T1
+	pshufd		\$0b01001110,$Xi,$T2
+	pxor		$Hkey,$T1		# Karatsuba pre-processing
+	movdqu		$Hkey,0x00($Htbl)	# save H
+	pxor		$Xi,$T2			# Karatsuba pre-processing
+	movdqu		$Xi,0x10($Htbl)		# save H^2
+	palignr		\$8,$T1,$T2		# low part is H.lo^H.hi...
+	movdqu		$T2,0x20($Htbl)		# save Karatsuba "salt"
+___
+if ($do4xaggr) {
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);	# H^3
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	movdqa		$Xi,$T3
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);	# H^4
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	pshufd		\$0b01001110,$T3,$T1
+	pshufd		\$0b01001110,$Xi,$T2
+	pxor		$T3,$T1			# Karatsuba pre-processing
+	movdqu		$T3,0x30($Htbl)		# save H^3
+	pxor		$Xi,$T2			# Karatsuba pre-processing
+	movdqu		$Xi,0x40($Htbl)		# save H^4
+	palignr		\$8,$T1,$T2		# low part is H^3.lo^H^3.hi...
+	movdqu		$T2,0x50($Htbl)		# save Karatsuba "salt"
+___
+}
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	lea	0x18(%rsp),%rsp
+.LSEH_end_gcm_init_clmul:
+___
+$code.=<<___;
+	ret
+.size	gcm_init_clmul,.-gcm_init_clmul
+___
+}
+
+{ my ($Xip,$Htbl)=@_4args;
+
+$code.=<<___;
+.globl	gcm_gmult_clmul
+.type	gcm_gmult_clmul,\@abi-omnipotent
+.align	16
+gcm_gmult_clmul:
+.L_gmult_clmul:
+	movdqu		($Xip),$Xi
+	movdqa		.Lbswap_mask(%rip),$T3
+	movdqu		($Htbl),$Hkey
+	movdqu		0x20($Htbl),$T2
+	pshufb		$T3,$Xi
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$T2);
+$code.=<<___ if (0 || (&reduction_alg9($Xhi,$Xi)&&0));
+	# experimental alternative. special thing about is that there
+	# no dependency between the two multiplications... 
+	mov		\$`0xE1<<1`,%eax
+	mov		\$0xA040608020C0E000,%r10	# ((7..0)·0xE0)&0xff
+	mov		\$0x07,%r11d
+	movq		%rax,$T1
+	movq		%r10,$T2
+	movq		%r11,$T3		# borrow $T3
+	pand		$Xi,$T3
+	pshufb		$T3,$T2			# ($Xi&7)·0xE0
+	movq		%rax,$T3
+	pclmulqdq	\$0x00,$Xi,$T1		# ·(0xE1<<1)
+	pxor		$Xi,$T2
+	pslldq		\$15,$T2
+	paddd		$T2,$T2			# <<(64+56+1)
+	pxor		$T2,$Xi
+	pclmulqdq	\$0x01,$T3,$Xi
+	movdqa		.Lbswap_mask(%rip),$T3	# reload $T3
+	psrldq		\$1,$T1
+	pxor		$T1,$Xhi
+	pslldq		\$7,$Xi
+	pxor		$Xhi,$Xi
+___
+$code.=<<___;
+	pshufb		$T3,$Xi
+	movdqu		$Xi,($Xip)
+	ret
+.size	gcm_gmult_clmul,.-gcm_gmult_clmul
+___
+}
+
+{ my ($Xip,$Htbl,$inp,$len)=@_4args;
+  my ($Xln,$Xmn,$Xhn,$Hkey2,$HK) = map("%xmm$_",(6..10));
+
+$code.=<<___;
+.globl	gcm_ghash_clmul
+.type	gcm_ghash_clmul,\@abi-omnipotent
+.align	32
+gcm_ghash_clmul:
+.L_ghash_clmul:
+___
+$code.=<<___ if ($win64);
+	lea	-0x88(%rsp),%rax
+.LSEH_begin_gcm_ghash_clmul:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax),%rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6,-0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7,-0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8,0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9,0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10,0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11,0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12,0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13,0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14,0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15,0x70(%rax)
+___
+$code.=<<___;
+	movdqa		.Lbswap_mask(%rip),$T3
+	mov		\$0xA040608020C0E000,%rax	# ((7..0)·0xE0)&0xff
+
+	movdqu		($Xip),$Xi
+	movdqu		($Htbl),$Hkey
+	movdqu		0x20($Htbl),$HK
+	pshufb		$T3,$Xi
+
+	sub		\$0x10,$len
+	jz		.Lodd_tail
+
+	movdqu		0x10($Htbl),$Hkey2
+___
+if ($do4xaggr) {
+my ($Xl,$Xm,$Xh,$Hkey3,$Hkey4)=map("%xmm$_",(11..15));
+
+$code.=<<___;
+	cmp		\$0x30,$len
+	jb		.Lskip4x
+
+	sub		\$0x30,$len
+	movdqu		0x30($Htbl),$Hkey3
+	movdqu		0x40($Htbl),$Hkey4
+
+	#######
+	# Xi+4 =[(H*Ii+3) + (H^2*Ii+2) + (H^3*Ii+1) + H^4*(Ii+Xi)] mod P
+	#
+	movdqu		0x30($inp),$Xln
+	 movdqu		0x20($inp),$Xl
+	pshufb		$T3,$Xln
+	 pshufb		$T3,$Xl
+	movdqa		$Xln,$Xhn
+	pshufd		\$0b01001110,$Xln,$Xmn
+	pxor		$Xln,$Xmn
+	pclmulqdq	\$0x00,$Hkey,$Xln
+	pclmulqdq	\$0x11,$Hkey,$Xhn
+	pclmulqdq	\$0x00,$HK,$Xmn
+
+	movdqa		$Xl,$Xh
+	pshufd		\$0b01001110,$Xl,$Xm
+	pxor		$Xl,$Xm
+	pclmulqdq	\$0x00,$Hkey2,$Xl
+	pclmulqdq	\$0x11,$Hkey2,$Xh
+	xorps		$Xl,$Xln
+	pclmulqdq	\$0x10,$HK,$Xm
+	xorps		$Xh,$Xhn
+	movups		0x50($Htbl),$HK
+	xorps		$Xm,$Xmn
+
+	movdqu		0x10($inp),$Xl
+	 movdqu		0($inp),$T1
+	pshufb		$T3,$Xl
+	 pshufb		$T3,$T1
+	movdqa		$Xl,$Xh
+	pshufd		\$0b01001110,$Xl,$Xm
+	 pxor		$T1,$Xi
+	pxor		$Xl,$Xm
+	pclmulqdq	\$0x00,$Hkey3,$Xl
+	 movdqa		$Xi,$Xhi
+	 pshufd		\$0b01001110,$Xi,$T1
+	 pxor		$Xi,$T1
+	pclmulqdq	\$0x11,$Hkey3,$Xh
+	xorps		$Xl,$Xln
+	pclmulqdq	\$0x00,$HK,$Xm
+	xorps		$Xh,$Xhn
+
+	lea	0x40($inp),$inp
+	sub	\$0x40,$len
+	jc	.Ltail4x
+
+	jmp	.Lmod4_loop
+.align	32
+.Lmod4_loop:
+	pclmulqdq	\$0x00,$Hkey4,$Xi
+	xorps		$Xm,$Xmn
+	 movdqu		0x30($inp),$Xl
+	 pshufb		$T3,$Xl
+	pclmulqdq	\$0x11,$Hkey4,$Xhi
+	xorps		$Xln,$Xi
+	 movdqu		0x20($inp),$Xln
+	 movdqa		$Xl,$Xh
+	 pshufd		\$0b01001110,$Xl,$Xm
+	pclmulqdq	\$0x10,$HK,$T1
+	xorps		$Xhn,$Xhi
+	 pxor		$Xl,$Xm
+	 pshufb		$T3,$Xln
+	movups		0x20($Htbl),$HK
+	 pclmulqdq	\$0x00,$Hkey,$Xl
+	xorps		$Xmn,$T1
+	 movdqa		$Xln,$Xhn
+	 pshufd		\$0b01001110,$Xln,$Xmn
+
+	pxor		$Xi,$T1			# aggregated Karatsuba post-processing
+	 pxor		$Xln,$Xmn
+	pxor		$Xhi,$T1		#
+	movdqa		$T1,$T2			#
+	pslldq		\$8,$T1
+	 pclmulqdq	\$0x11,$Hkey,$Xh
+	psrldq		\$8,$T2			#
+	pxor		$T1,$Xi
+	movdqa		.L7_mask(%rip),$T1
+	pxor		$T2,$Xhi		#
+	movq		%rax,$T2
+
+	pand		$Xi,$T1			# 1st phase
+	pshufb		$T1,$T2			#
+	 pclmulqdq	\$0x00,$HK,$Xm
+	pxor		$Xi,$T2			#
+	psllq		\$57,$T2		#
+	movdqa		$T2,$T1			#
+	pslldq		\$8,$T2
+	 pclmulqdq	\$0x00,$Hkey2,$Xln
+	psrldq		\$8,$T1			#	
+	pxor		$T2,$Xi
+	pxor		$T1,$Xhi		#
+	movdqu		0($inp),$T1
+
+	movdqa		$Xi,$T2			# 2nd phase
+	psrlq		\$1,$Xi
+	 pclmulqdq	\$0x11,$Hkey2,$Xhn
+	 xorps		$Xl,$Xln
+	 movdqu		0x10($inp),$Xl
+	 pshufb		$T3,$Xl
+	 pclmulqdq	\$0x10,$HK,$Xmn
+	 xorps		$Xh,$Xhn
+	 movups		0x50($Htbl),$HK
+	pshufb		$T3,$T1
+	pxor		$T2,$Xhi		#
+	pxor		$Xi,$T2
+	psrlq		\$5,$Xi
+
+	 movdqa		$Xl,$Xh
+	 pxor		$Xm,$Xmn
+	 pshufd		\$0b01001110,$Xl,$Xm
+	 pxor		$Xl,$Xm
+	 pclmulqdq	\$0x00,$Hkey3,$Xl
+	pxor		$T2,$Xi			#
+	pxor		$T1,$Xhi
+	psrlq		\$1,$Xi			#
+	 pclmulqdq	\$0x11,$Hkey3,$Xh
+	 xorps		$Xl,$Xln
+	pxor		$Xhi,$Xi		#
+
+	 pclmulqdq	\$0x00,$HK,$Xm
+	 xorps		$Xh,$Xhn
+
+	movdqa		$Xi,$Xhi
+	pshufd		\$0b01001110,$Xi,$T1
+	pxor		$Xi,$T1
+
+	lea	0x40($inp),$inp
+	sub	\$0x40,$len
+	jnc	.Lmod4_loop
+
+.Ltail4x:
+	pclmulqdq	\$0x00,$Hkey4,$Xi
+	xorps		$Xm,$Xmn
+	pclmulqdq	\$0x11,$Hkey4,$Xhi
+	xorps		$Xln,$Xi
+	pclmulqdq	\$0x10,$HK,$T1
+	xorps		$Xhn,$Xhi
+	pxor		$Xi,$Xhi		# aggregated Karatsuba post-processing
+	pxor		$Xmn,$T1
+
+	pxor		$Xhi,$T1		#
+	pxor		$Xi,$Xhi
+
+	movdqa		$T1,$T2			#
+	psrldq		\$8,$T1
+	pslldq		\$8,$T2			#
+	pxor		$T1,$Xhi
+	pxor		$T2,$Xi			#
+___
+	&reduction_alg9($Xhi,$Xi);
+$code.=<<___;
+	add	\$0x40,$len
+	jz	.Ldone
+	movdqu	0x20($Htbl),$HK
+	sub	\$0x10,$len
+	jz	.Lodd_tail
+.Lskip4x:
+___
+}
+$code.=<<___;
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+	movdqu		($inp),$T1		# Ii
+	movdqu		16($inp),$Xln		# Ii+1
+	pshufb		$T3,$T1
+	pshufb		$T3,$Xln
+	pxor		$T1,$Xi			# Ii+Xi
+
+	movdqa		$Xln,$Xhn
+	pshufd		\$0b01001110,$Xln,$T1
+	pxor		$Xln,$T1
+	pclmulqdq	\$0x00,$Hkey,$Xln
+	pclmulqdq	\$0x11,$Hkey,$Xhn
+	pclmulqdq	\$0x00,$HK,$T1
+
+	lea		32($inp),$inp		# i+=2
+	sub		\$0x20,$len
+	jbe		.Leven_tail
+	jmp		.Lmod_loop
+
+.align	32
+.Lmod_loop:
+	movdqa		$Xi,$Xhi
+	pshufd		\$0b01001110,$Xi,$T2	#
+	pxor		$Xi,$T2			#
+
+	pclmulqdq	\$0x00,$Hkey2,$Xi
+	pclmulqdq	\$0x11,$Hkey2,$Xhi
+	pclmulqdq	\$0x10,$HK,$T2
+
+	pxor		$Xln,$Xi		# (H*Ii+1) + H^2*(Ii+Xi)
+	pxor		$Xhn,$Xhi
+	  movdqu	($inp),$Xhn		# Ii
+	  pshufb	$T3,$Xhn
+	  movdqu	16($inp),$Xln		# Ii+1
+
+	pxor		$Xi,$T1			# aggregated Karatsuba post-processing
+	pxor		$Xhi,$T1
+	  pxor		$Xhn,$Xhi		# "Ii+Xi", consume early
+	pxor		$T1,$T2
+	 pshufb		$T3,$Xln
+	movdqa		$T2,$T1			#
+	psrldq		\$8,$T1
+	pslldq		\$8,$T2			#
+	pxor		$T1,$Xhi
+	pxor		$T2,$Xi			#
+
+	movdqa		$Xln,$Xhn		#
+
+	  movdqa	$Xi,$T2			# 1st phase
+	  movdqa	$Xi,$T1
+	  psllq		\$5,$Xi
+	pclmulqdq	\$0x00,$Hkey,$Xln	#######
+	  pxor		$Xi,$T1			#
+	  psllq		\$1,$Xi
+	  pxor		$T1,$Xi			#
+	  psllq		\$57,$Xi		#
+	  movdqa	$Xi,$T1			#
+	  pslldq	\$8,$Xi
+	  psrldq	\$8,$T1			#	
+	  pxor		$T2,$Xi
+	  pxor		$T1,$Xhi		#
+	pshufd		\$0b01001110,$Xhn,$T1
+	pxor		$Xhn,$T1		#
+
+	pclmulqdq	\$0x11,$Hkey,$Xhn	#######
+	  movdqa	$Xi,$T2			# 2nd phase
+	  psrlq		\$1,$Xi
+	  pxor		$T2,$Xhi		#
+	  pxor		$Xi,$T2
+	  psrlq		\$5,$Xi
+	  pxor		$T2,$Xi			#
+	  psrlq		\$1,$Xi			#
+	pclmulqdq	\$0x00,$HK,$T1		#######
+	  pxor		$Xhi,$Xi		#
+
+	lea		32($inp),$inp
+	sub		\$0x20,$len
+	ja		.Lmod_loop
+
+.Leven_tail:
+	 movdqa		$Xi,$Xhi
+	 pshufd		\$0b01001110,$Xi,$T2	#
+	 pxor		$Xi,$T2			#
+
+	pclmulqdq	\$0x00,$Hkey2,$Xi
+	pclmulqdq	\$0x11,$Hkey2,$Xhi
+	pclmulqdq	\$0x10,$HK,$T2
+
+	pxor		$Xln,$Xi		# (H*Ii+1) + H^2*(Ii+Xi)
+	pxor		$Xhn,$Xhi
+	pxor		$Xi,$T1
+	pxor		$Xhi,$T1
+	pxor		$T1,$T2
+	movdqa		$T2,$T1			#
+	psrldq		\$8,$T1
+	pslldq		\$8,$T2			#
+	pxor		$T1,$Xhi
+	pxor		$T2,$Xi			#
+___
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	test		$len,$len
+	jnz		.Ldone
+
+.Lodd_tail:
+	movdqu		($inp),$T1		# Ii
+	pshufb		$T3,$T1
+	pxor		$T1,$Xi			# Ii+Xi
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);	# H*(Ii+Xi)
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+.Ldone:
+	pshufb		$T3,$Xi
+	movdqu		$Xi,($Xip)
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	movaps	0x40(%rsp),%xmm10
+	movaps	0x50(%rsp),%xmm11
+	movaps	0x60(%rsp),%xmm12
+	movaps	0x70(%rsp),%xmm13
+	movaps	0x80(%rsp),%xmm14
+	movaps	0x90(%rsp),%xmm15
+	lea	0xa8(%rsp),%rsp
+.LSEH_end_gcm_ghash_clmul:
+___
+$code.=<<___;
+	ret
+.size	gcm_ghash_clmul,.-gcm_ghash_clmul
+___
+}
+
+$code.=<<___;
+.globl	gcm_init_avx
+.type	gcm_init_avx,\@abi-omnipotent
+.align	32
+gcm_init_avx:
+___
+if ($avx) {
+my ($Htbl,$Xip)=@_4args;
+my $HK="%xmm6";
+
+$code.=<<___ if ($win64);
+.LSEH_begin_gcm_init_avx:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x83,0xec,0x18		#sub	$0x18,%rsp
+	.byte	0x0f,0x29,0x34,0x24		#movaps	%xmm6,(%rsp)
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($Xip),$Hkey
+	vpshufd		\$0b01001110,$Hkey,$Hkey	# dword swap
+
+	# <<1 twist
+	vpshufd		\$0b11111111,$Hkey,$T2	# broadcast uppermost dword
+	vpsrlq		\$63,$Hkey,$T1
+	vpsllq		\$1,$Hkey,$Hkey
+	vpxor		$T3,$T3,$T3		#
+	vpcmpgtd	$T2,$T3,$T3		# broadcast carry bit
+	vpslldq		\$8,$T1,$T1
+	vpor		$T1,$Hkey,$Hkey		# H<<=1
+
+	# magic reduction
+	vpand		.L0x1c2_polynomial(%rip),$T3,$T3
+	vpxor		$T3,$Hkey,$Hkey		# if(carry) H^=0x1c2_polynomial
+
+	vpunpckhqdq	$Hkey,$Hkey,$HK
+	vmovdqa		$Hkey,$Xi
+	vpxor		$Hkey,$HK,$HK
+	mov		\$4,%r10		# up to H^8
+	jmp		.Linit_start_avx
+___
+
+sub clmul64x64_avx {
+my ($Xhi,$Xi,$Hkey,$HK)=@_;
+
+if (!defined($HK)) {	$HK = $T2;
+$code.=<<___;
+	vpunpckhqdq	$Xi,$Xi,$T1
+	vpunpckhqdq	$Hkey,$Hkey,$T2
+	vpxor		$Xi,$T1,$T1		#
+	vpxor		$Hkey,$T2,$T2
+___
+} else {
+$code.=<<___;
+	vpunpckhqdq	$Xi,$Xi,$T1
+	vpxor		$Xi,$T1,$T1		#
+___
+}
+$code.=<<___;
+	vpclmulqdq	\$0x11,$Hkey,$Xi,$Xhi	#######
+	vpclmulqdq	\$0x00,$Hkey,$Xi,$Xi	#######
+	vpclmulqdq	\$0x00,$HK,$T1,$T1	#######
+	vpxor		$Xi,$Xhi,$T2		#
+	vpxor		$T2,$T1,$T1		#
+
+	vpslldq		\$8,$T1,$T2		#
+	vpsrldq		\$8,$T1,$T1
+	vpxor		$T2,$Xi,$Xi		#
+	vpxor		$T1,$Xhi,$Xhi
+___
+}
+
+sub reduction_avx {
+my ($Xhi,$Xi) = @_;
+
+$code.=<<___;
+	vpsllq		\$57,$Xi,$T1		# 1st phase
+	vpsllq		\$62,$Xi,$T2
+	vpxor		$T1,$T2,$T2		#
+	vpsllq		\$63,$Xi,$T1
+	vpxor		$T1,$T2,$T2		#
+	vpslldq		\$8,$T2,$T1		#
+	vpsrldq		\$8,$T2,$T2
+	vpxor		$T1,$Xi,$Xi		#
+	vpxor		$T2,$Xhi,$Xhi
+
+	vpsrlq		\$1,$Xi,$T2		# 2nd phase
+	vpxor		$Xi,$Xhi,$Xhi
+	vpxor		$T2,$Xi,$Xi		#
+	vpsrlq		\$5,$T2,$T2
+	vpxor		$T2,$Xi,$Xi		#
+	vpsrlq		\$1,$Xi,$Xi		#
+	vpxor		$Xhi,$Xi,$Xi		#
+___
+}
+
+$code.=<<___;
+.align	32
+.Linit_loop_avx:
+	vpalignr	\$8,$T1,$T2,$T3		# low part is H.lo^H.hi...
+	vmovdqu		$T3,-0x10($Htbl)	# save Karatsuba "salt"
+___
+	&clmul64x64_avx	($Xhi,$Xi,$Hkey,$HK);	# calculate H^3,5,7
+	&reduction_avx	($Xhi,$Xi);
+$code.=<<___;
+.Linit_start_avx:
+	vmovdqa		$Xi,$T3
+___
+	&clmul64x64_avx	($Xhi,$Xi,$Hkey,$HK);	# calculate H^2,4,6,8
+	&reduction_avx	($Xhi,$Xi);
+$code.=<<___;
+	vpshufd		\$0b01001110,$T3,$T1
+	vpshufd		\$0b01001110,$Xi,$T2
+	vpxor		$T3,$T1,$T1		# Karatsuba pre-processing
+	vmovdqu		$T3,0x00($Htbl)		# save H^1,3,5,7
+	vpxor		$Xi,$T2,$T2		# Karatsuba pre-processing
+	vmovdqu		$Xi,0x10($Htbl)		# save H^2,4,6,8
+	lea		0x30($Htbl),$Htbl
+	sub		\$1,%r10
+	jnz		.Linit_loop_avx
+
+	vpalignr	\$8,$T2,$T1,$T3		# last "salt" is flipped
+	vmovdqu		$T3,-0x10($Htbl)
+
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	lea	0x18(%rsp),%rsp
+.LSEH_end_gcm_init_avx:
+___
+$code.=<<___;
+	ret
+.size	gcm_init_avx,.-gcm_init_avx
+___
+} else {
+$code.=<<___;
+	jmp	.L_init_clmul
+.size	gcm_init_avx,.-gcm_init_avx
+___
+}
+
+$code.=<<___;
+.globl	gcm_gmult_avx
+.type	gcm_gmult_avx,\@abi-omnipotent
+.align	32
+gcm_gmult_avx:
+	jmp	.L_gmult_clmul
+.size	gcm_gmult_avx,.-gcm_gmult_avx
+___
+
+$code.=<<___;
+.globl	gcm_ghash_avx
+.type	gcm_ghash_avx,\@abi-omnipotent
+.align	32
+gcm_ghash_avx:
+___
+if ($avx) {
+my ($Xip,$Htbl,$inp,$len)=@_4args;
+my ($Xlo,$Xhi,$Xmi,
+    $Zlo,$Zhi,$Zmi,
+    $Hkey,$HK,$T1,$T2,
+    $Xi,$Xo,$Tred,$bswap,$Ii,$Ij) = map("%xmm$_",(0..15));
+
+$code.=<<___ if ($win64);
+	lea	-0x88(%rsp),%rax
+.LSEH_begin_gcm_ghash_avx:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax),%rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6,-0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7,-0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8,0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9,0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10,0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11,0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12,0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13,0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14,0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15,0x70(%rax)
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($Xip),$Xi		# load $Xi
+	lea		.L0x1c2_polynomial(%rip),%r10
+	lea		0x40($Htbl),$Htbl	# size optimization
+	vmovdqu		.Lbswap_mask(%rip),$bswap
+	vpshufb		$bswap,$Xi,$Xi
+	cmp		\$0x80,$len
+	jb		.Lshort_avx
+	sub		\$0x80,$len
+
+	vmovdqu		0x70($inp),$Ii		# I[7]
+	vmovdqu		0x00-0x40($Htbl),$Hkey	# $Hkey^1
+	vpshufb		$bswap,$Ii,$Ii
+	vmovdqu		0x20-0x40($Htbl),$HK
+
+	vpunpckhqdq	$Ii,$Ii,$T2
+	 vmovdqu	0x60($inp),$Ij		# I[6]
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Ii,$T2,$T2
+	 vpshufb	$bswap,$Ij,$Ij
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0x10-0x40($Htbl),$Hkey	# $Hkey^2
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vmovdqu	0x50($inp),$Ii		# I[5]
+	vpclmulqdq	\$0x00,$HK,$T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+
+	 vpshufb	$bswap,$Ii,$Ii
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	 vmovdqu	0x30-0x40($Htbl),$Hkey	# $Hkey^3
+	 vpxor		$Ii,$T2,$T2
+	 vmovdqu	0x40($inp),$Ij		# I[4]
+	vpclmulqdq	\$0x10,$HK,$T1,$Zmi
+	 vmovdqu	0x50-0x40($Htbl),$HK
+
+	 vpshufb	$bswap,$Ij,$Ij
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Xhi,$Zhi,$Zhi
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0x40-0x40($Htbl),$Hkey	# $Hkey^4
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+
+	 vmovdqu	0x30($inp),$Ii		# I[3]
+	vpxor		$Zlo,$Xlo,$Xlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	vpxor		$Zhi,$Xhi,$Xhi
+	 vpshufb	$bswap,$Ii,$Ii
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	 vmovdqu	0x60-0x40($Htbl),$Hkey	# $Hkey^5
+	vpxor		$Zmi,$Xmi,$Xmi
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpclmulqdq	\$0x10,$HK,$T1,$Zmi
+	 vmovdqu	0x80-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+
+	 vmovdqu	0x20($inp),$Ij		# I[2]
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Xhi,$Zhi,$Zhi
+	 vpshufb	$bswap,$Ij,$Ij
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0x70-0x40($Htbl),$Hkey	# $Hkey^6
+	vpxor		$Xmi,$Zmi,$Zmi
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	vpclmulqdq	\$0x00,$HK,$T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+
+	 vmovdqu	0x10($inp),$Ii		# I[1]
+	vpxor		$Zlo,$Xlo,$Xlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	vpxor		$Zhi,$Xhi,$Xhi
+	 vpshufb	$bswap,$Ii,$Ii
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	 vmovdqu	0x90-0x40($Htbl),$Hkey	# $Hkey^7
+	vpxor		$Zmi,$Xmi,$Xmi
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpclmulqdq	\$0x10,$HK,$T1,$Zmi
+	 vmovdqu	0xb0-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+
+	 vmovdqu	($inp),$Ij		# I[0]
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Xhi,$Zhi,$Zhi
+	 vpshufb	$bswap,$Ij,$Ij
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0xa0-0x40($Htbl),$Hkey	# $Hkey^8
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x10,$HK,$T2,$Xmi
+
+	lea		0x80($inp),$inp
+	cmp		\$0x80,$len
+	jb		.Ltail_avx
+
+	vpxor		$Xi,$Ij,$Ij		# accumulate $Xi
+	sub		\$0x80,$len
+	jmp		.Loop8x_avx
+
+.align	32
+.Loop8x_avx:
+	vpunpckhqdq	$Ij,$Ij,$T1
+	 vmovdqu	0x70($inp),$Ii		# I[7]
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpxor		$Ij,$T1,$T1
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xi
+	 vpshufb	$bswap,$Ii,$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xo
+	 vmovdqu	0x00-0x40($Htbl),$Hkey	# $Hkey^1
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Tred
+	 vmovdqu	0x20-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+
+	  vmovdqu	0x60($inp),$Ij		# I[6]
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Zlo,$Xi,$Xi		# collect result
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	vxorps		$Zhi,$Xo,$Xo
+	  vmovdqu	0x10-0x40($Htbl),$Hkey	# $Hkey^2
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vpclmulqdq	\$0x00,$HK,  $T2,$Xmi
+	vpxor		$Zmi,$Tred,$Tred
+	 vxorps		$Ij,$T1,$T1
+
+	  vmovdqu	0x50($inp),$Ii		# I[5]
+	vpxor		$Xi,$Tred,$Tred		# aggregated Karatsuba post-processing
+	 vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	vpxor		$Xo,$Tred,$Tred
+	vpslldq		\$8,$Tred,$T2
+	 vpxor		$Xlo,$Zlo,$Zlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	vpsrldq		\$8,$Tred,$Tred
+	vpxor		$T2, $Xi, $Xi
+	  vmovdqu	0x30-0x40($Htbl),$Hkey	# $Hkey^3
+	  vpshufb	$bswap,$Ii,$Ii
+	vxorps		$Tred,$Xo, $Xo
+	 vpxor		$Xhi,$Zhi,$Zhi
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	 vpclmulqdq	\$0x10,$HK,  $T1,$Zmi
+	  vmovdqu	0x50-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+	 vpxor		$Xmi,$Zmi,$Zmi
+
+	  vmovdqu	0x40($inp),$Ij		# I[4]
+	vpalignr	\$8,$Xi,$Xi,$Tred	# 1st phase
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpxor		$Zlo,$Xlo,$Xlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	  vmovdqu	0x40-0x40($Htbl),$Hkey	# $Hkey^4
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vpxor		$Zhi,$Xhi,$Xhi
+	 vpclmulqdq	\$0x00,$HK,  $T2,$Xmi
+	 vxorps		$Ij,$T1,$T1
+	 vpxor		$Zmi,$Xmi,$Xmi
+
+	  vmovdqu	0x30($inp),$Ii		# I[3]
+	vpclmulqdq	\$0x10,(%r10),$Xi,$Xi
+	 vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	  vpshufb	$bswap,$Ii,$Ii
+	 vpxor		$Xlo,$Zlo,$Zlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	  vmovdqu	0x60-0x40($Htbl),$Hkey	# $Hkey^5
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	 vpxor		$Xhi,$Zhi,$Zhi
+	 vpclmulqdq	\$0x10,$HK,  $T1,$Zmi
+	  vmovdqu	0x80-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+	 vpxor		$Xmi,$Zmi,$Zmi
+
+	  vmovdqu	0x20($inp),$Ij		# I[2]
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpxor		$Zlo,$Xlo,$Xlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	  vmovdqu	0x70-0x40($Htbl),$Hkey	# $Hkey^6
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vpxor		$Zhi,$Xhi,$Xhi
+	 vpclmulqdq	\$0x00,$HK,  $T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+	 vpxor		$Zmi,$Xmi,$Xmi
+	vxorps		$Tred,$Xi,$Xi
+
+	  vmovdqu	0x10($inp),$Ii		# I[1]
+	vpalignr	\$8,$Xi,$Xi,$Tred	# 2nd phase
+	 vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	  vpshufb	$bswap,$Ii,$Ii
+	 vpxor		$Xlo,$Zlo,$Zlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	  vmovdqu	0x90-0x40($Htbl),$Hkey	# $Hkey^7
+	vpclmulqdq	\$0x10,(%r10),$Xi,$Xi
+	vxorps		$Xo,$Tred,$Tred
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	 vpxor		$Xhi,$Zhi,$Zhi
+	 vpclmulqdq	\$0x10,$HK,  $T1,$Zmi
+	  vmovdqu	0xb0-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+	 vpxor		$Xmi,$Zmi,$Zmi
+
+	  vmovdqu	($inp),$Ij		# I[0]
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	  vmovdqu	0xa0-0x40($Htbl),$Hkey	# $Hkey^8
+	vpxor		$Tred,$Ij,$Ij
+	 vpclmulqdq	\$0x10,$HK,  $T2,$Xmi
+	vpxor		$Xi,$Ij,$Ij		# accumulate $Xi
+
+	lea		0x80($inp),$inp
+	sub		\$0x80,$len
+	jnc		.Loop8x_avx
+
+	add		\$0x80,$len
+	jmp		.Ltail_no_xor_avx
+
+.align	32
+.Lshort_avx:
+	vmovdqu		-0x10($inp,$len),$Ii	# very last word
+	lea		($inp,$len),$inp
+	vmovdqu		0x00-0x40($Htbl),$Hkey	# $Hkey^1
+	vmovdqu		0x20-0x40($Htbl),$HK
+	vpshufb		$bswap,$Ii,$Ij
+
+	vmovdqa		$Xlo,$Zlo		# subtle way to zero $Zlo,
+	vmovdqa		$Xhi,$Zhi		# $Zhi and
+	vmovdqa		$Xmi,$Zmi		# $Zmi
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x20($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x10-0x40($Htbl),$Hkey	# $Hkey^2
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vpsrldq		\$8,$HK,$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x30($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x30-0x40($Htbl),$Hkey	# $Hkey^3
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vmovdqu		0x50-0x40($Htbl),$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x40($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x40-0x40($Htbl),$Hkey	# $Hkey^4
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vpsrldq		\$8,$HK,$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x50($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x60-0x40($Htbl),$Hkey	# $Hkey^5
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vmovdqu		0x80-0x40($Htbl),$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x60($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x70-0x40($Htbl),$Hkey	# $Hkey^6
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vpsrldq		\$8,$HK,$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x70($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x90-0x40($Htbl),$Hkey	# $Hkey^7
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vmovq		0xb8-0x40($Htbl),$HK
+	sub		\$0x10,$len
+	jmp		.Ltail_avx
+
+.align	32
+.Ltail_avx:
+	vpxor		$Xi,$Ij,$Ij		# accumulate $Xi
+.Ltail_no_xor_avx:
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+
+	vmovdqu		(%r10),$Tred
+
+	vpxor		$Xlo,$Zlo,$Xi
+	vpxor		$Xhi,$Zhi,$Xo
+	vpxor		$Xmi,$Zmi,$Zmi
+
+	vpxor		$Xi, $Zmi,$Zmi		# aggregated Karatsuba post-processing
+	vpxor		$Xo, $Zmi,$Zmi
+	vpslldq		\$8, $Zmi,$T2
+	vpsrldq		\$8, $Zmi,$Zmi
+	vpxor		$T2, $Xi, $Xi
+	vpxor		$Zmi,$Xo, $Xo
+
+	vpclmulqdq	\$0x10,$Tred,$Xi,$T2	# 1st phase
+	vpalignr	\$8,$Xi,$Xi,$Xi
+	vpxor		$T2,$Xi,$Xi
+
+	vpclmulqdq	\$0x10,$Tred,$Xi,$T2	# 2nd phase
+	vpalignr	\$8,$Xi,$Xi,$Xi
+	vpxor		$Xo,$Xi,$Xi
+	vpxor		$T2,$Xi,$Xi
+
+	cmp		\$0,$len
+	jne		.Lshort_avx
+
+	vpshufb		$bswap,$Xi,$Xi
+	vmovdqu		$Xi,($Xip)
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	movaps	0x40(%rsp),%xmm10
+	movaps	0x50(%rsp),%xmm11
+	movaps	0x60(%rsp),%xmm12
+	movaps	0x70(%rsp),%xmm13
+	movaps	0x80(%rsp),%xmm14
+	movaps	0x90(%rsp),%xmm15
+	lea	0xa8(%rsp),%rsp
+.LSEH_end_gcm_ghash_avx:
+___
+$code.=<<___;
+	ret
+.size	gcm_ghash_avx,.-gcm_ghash_avx
+___
+} else {
+$code.=<<___;
+	jmp	.L_ghash_clmul
+.size	gcm_ghash_avx,.-gcm_ghash_avx
+___
+}
+
+$code.=<<___;
+.align	64
+.Lbswap_mask:
+	.byte	15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
+.L0x1c2_polynomial:
+	.byte	1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2
+.L7_mask:
+	.long	7,0,7,0
+.L7_mask_poly:
+	.long	7,0,`0xE1<<1`,0
+.align	64
+.type	.Lrem_4bit,\@object
+.Lrem_4bit:
+	.long	0,`0x0000<<16`,0,`0x1C20<<16`,0,`0x3840<<16`,0,`0x2460<<16`
+	.long	0,`0x7080<<16`,0,`0x6CA0<<16`,0,`0x48C0<<16`,0,`0x54E0<<16`
+	.long	0,`0xE100<<16`,0,`0xFD20<<16`,0,`0xD940<<16`,0,`0xC560<<16`
+	.long	0,`0x9180<<16`,0,`0x8DA0<<16`,0,`0xA9C0<<16`,0,`0xB5E0<<16`
+.type	.Lrem_8bit,\@object
+.Lrem_8bit:
+	.value	0x0000,0x01C2,0x0384,0x0246,0x0708,0x06CA,0x048C,0x054E
+	.value	0x0E10,0x0FD2,0x0D94,0x0C56,0x0918,0x08DA,0x0A9C,0x0B5E
+	.value	0x1C20,0x1DE2,0x1FA4,0x1E66,0x1B28,0x1AEA,0x18AC,0x196E
+	.value	0x1230,0x13F2,0x11B4,0x1076,0x1538,0x14FA,0x16BC,0x177E
+	.value	0x3840,0x3982,0x3BC4,0x3A06,0x3F48,0x3E8A,0x3CCC,0x3D0E
+	.value	0x3650,0x3792,0x35D4,0x3416,0x3158,0x309A,0x32DC,0x331E
+	.value	0x2460,0x25A2,0x27E4,0x2626,0x2368,0x22AA,0x20EC,0x212E
+	.value	0x2A70,0x2BB2,0x29F4,0x2836,0x2D78,0x2CBA,0x2EFC,0x2F3E
+	.value	0x7080,0x7142,0x7304,0x72C6,0x7788,0x764A,0x740C,0x75CE
+	.value	0x7E90,0x7F52,0x7D14,0x7CD6,0x7998,0x785A,0x7A1C,0x7BDE
+	.value	0x6CA0,0x6D62,0x6F24,0x6EE6,0x6BA8,0x6A6A,0x682C,0x69EE
+	.value	0x62B0,0x6372,0x6134,0x60F6,0x65B8,0x647A,0x663C,0x67FE
+	.value	0x48C0,0x4902,0x4B44,0x4A86,0x4FC8,0x4E0A,0x4C4C,0x4D8E
+	.value	0x46D0,0x4712,0x4554,0x4496,0x41D8,0x401A,0x425C,0x439E
+	.value	0x54E0,0x5522,0x5764,0x56A6,0x53E8,0x522A,0x506C,0x51AE
+	.value	0x5AF0,0x5B32,0x5974,0x58B6,0x5DF8,0x5C3A,0x5E7C,0x5FBE
+	.value	0xE100,0xE0C2,0xE284,0xE346,0xE608,0xE7CA,0xE58C,0xE44E
+	.value	0xEF10,0xEED2,0xEC94,0xED56,0xE818,0xE9DA,0xEB9C,0xEA5E
+	.value	0xFD20,0xFCE2,0xFEA4,0xFF66,0xFA28,0xFBEA,0xF9AC,0xF86E
+	.value	0xF330,0xF2F2,0xF0B4,0xF176,0xF438,0xF5FA,0xF7BC,0xF67E
+	.value	0xD940,0xD882,0xDAC4,0xDB06,0xDE48,0xDF8A,0xDDCC,0xDC0E
+	.value	0xD750,0xD692,0xD4D4,0xD516,0xD058,0xD19A,0xD3DC,0xD21E
+	.value	0xC560,0xC4A2,0xC6E4,0xC726,0xC268,0xC3AA,0xC1EC,0xC02E
+	.value	0xCB70,0xCAB2,0xC8F4,0xC936,0xCC78,0xCDBA,0xCFFC,0xCE3E
+	.value	0x9180,0x9042,0x9204,0x93C6,0x9688,0x974A,0x950C,0x94CE
+	.value	0x9F90,0x9E52,0x9C14,0x9DD6,0x9898,0x995A,0x9B1C,0x9ADE
+	.value	0x8DA0,0x8C62,0x8E24,0x8FE6,0x8AA8,0x8B6A,0x892C,0x88EE
+	.value	0x83B0,0x8272,0x8034,0x81F6,0x84B8,0x857A,0x873C,0x86FE
+	.value	0xA9C0,0xA802,0xAA44,0xAB86,0xAEC8,0xAF0A,0xAD4C,0xAC8E
+	.value	0xA7D0,0xA612,0xA454,0xA596,0xA0D8,0xA11A,0xA35C,0xA29E
+	.value	0xB5E0,0xB422,0xB664,0xB7A6,0xB2E8,0xB32A,0xB16C,0xB0AE
+	.value	0xBBF0,0xBA32,0xB874,0xB9B6,0xBCF8,0xBD3A,0xBF7C,0xBEBE
+
+.asciz	"GHASH for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	24(%rax),%rax		# adjust "rsp"
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_gcm_gmult_4bit
+	.rva	.LSEH_end_gcm_gmult_4bit
+	.rva	.LSEH_info_gcm_gmult_4bit
+
+	.rva	.LSEH_begin_gcm_ghash_4bit
+	.rva	.LSEH_end_gcm_ghash_4bit
+	.rva	.LSEH_info_gcm_ghash_4bit
+
+	.rva	.LSEH_begin_gcm_init_clmul
+	.rva	.LSEH_end_gcm_init_clmul
+	.rva	.LSEH_info_gcm_init_clmul
+
+	.rva	.LSEH_begin_gcm_ghash_clmul
+	.rva	.LSEH_end_gcm_ghash_clmul
+	.rva	.LSEH_info_gcm_ghash_clmul
+___
+$code.=<<___	if ($avx);
+	.rva	.LSEH_begin_gcm_init_avx
+	.rva	.LSEH_end_gcm_init_avx
+	.rva	.LSEH_info_gcm_init_clmul
+
+	.rva	.LSEH_begin_gcm_ghash_avx
+	.rva	.LSEH_end_gcm_ghash_avx
+	.rva	.LSEH_info_gcm_ghash_clmul
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_gcm_gmult_4bit:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lgmult_prologue,.Lgmult_epilogue	# HandlerData
+.LSEH_info_gcm_ghash_4bit:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lghash_prologue,.Lghash_epilogue	# HandlerData
+.LSEH_info_gcm_init_clmul:
+	.byte	0x01,0x08,0x03,0x00
+	.byte	0x08,0x68,0x00,0x00	#movaps	0x00(rsp),xmm6
+	.byte	0x04,0x22,0x00,0x00	#sub	rsp,0x18
+.LSEH_info_gcm_ghash_clmul:
+	.byte	0x01,0x33,0x16,0x00
+	.byte	0x33,0xf8,0x09,0x00	#movaps 0x90(rsp),xmm15
+	.byte	0x2e,0xe8,0x08,0x00	#movaps 0x80(rsp),xmm14
+	.byte	0x29,0xd8,0x07,0x00	#movaps 0x70(rsp),xmm13
+	.byte	0x24,0xc8,0x06,0x00	#movaps 0x60(rsp),xmm12
+	.byte	0x1f,0xb8,0x05,0x00	#movaps 0x50(rsp),xmm11
+	.byte	0x1a,0xa8,0x04,0x00	#movaps 0x40(rsp),xmm10
+	.byte	0x15,0x98,0x03,0x00	#movaps 0x30(rsp),xmm9
+	.byte	0x10,0x88,0x02,0x00	#movaps 0x20(rsp),xmm8
+	.byte	0x0c,0x78,0x01,0x00	#movaps 0x10(rsp),xmm7
+	.byte	0x08,0x68,0x00,0x00	#movaps 0x00(rsp),xmm6
+	.byte	0x04,0x01,0x15,0x00	#sub	rsp,0xa8
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/modes/cbc.c b/crypto/modes/cbc.c
new file mode 100644
index 0000000..83d58f0
--- /dev/null
+++ b/crypto/modes/cbc.c
@@ -0,0 +1,202 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+#include <openssl/modes.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+#ifndef STRICT_ALIGNMENT
+#  define STRICT_ALIGNMENT 0
+#endif
+
+void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const void *key, uint8_t ivec[16],
+                           block128_f block) {
+  size_t n;
+  const uint8_t *iv = ivec;
+
+  assert(in && out && key && ivec);
+
+  if (STRICT_ALIGNMENT &&
+      ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+    while (len >= 16) {
+      for (n = 0; n < 16; ++n) {
+        out[n] = in[n] ^ iv[n];
+      }
+      (*block)(out, out, key);
+      iv = out;
+      len -= 16;
+      in += 16;
+      out += 16;
+    }
+  } else {
+    while (len >= 16) {
+      for (n = 0; n < 16; n += sizeof(size_t)) {
+        *(size_t *)(out + n) = *(size_t *)(in + n) ^ *(size_t *)(iv + n);
+      }
+      (*block)(out, out, key);
+      iv = out;
+      len -= 16;
+      in += 16;
+      out += 16;
+    }
+  }
+
+  while (len) {
+    for (n = 0; n < 16 && n < len; ++n) {
+      out[n] = in[n] ^ iv[n];
+    }
+    for (; n < 16; ++n) {
+      out[n] = iv[n];
+    }
+    (*block)(out, out, key);
+    iv = out;
+    if (len <= 16) {
+      break;
+    }
+    len -= 16;
+    in += 16;
+    out += 16;
+  }
+
+  memcpy(ivec, iv, 16);
+}
+
+void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const void *key, uint8_t ivec[16],
+                           block128_f block) {
+  size_t n;
+  union {
+    size_t t[16 / sizeof(size_t)];
+    uint8_t c[16];
+  } tmp;
+
+  assert(in && out && key && ivec);
+
+  if (in != out) {
+    const uint8_t *iv = ivec;
+
+    if (STRICT_ALIGNMENT &&
+        ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+      while (len >= 16) {
+        (*block)(in, out, key);
+        for (n = 0; n < 16; ++n)
+          out[n] ^= iv[n];
+        iv = in;
+        len -= 16;
+        in += 16;
+        out += 16;
+      }
+    } else if (16 % sizeof(size_t) == 0) { /* always true */
+      while (len >= 16) {
+        size_t *out_t = (size_t *)out, *iv_t = (size_t *)iv;
+
+        (*block)(in, out, key);
+        for (n = 0; n < 16 / sizeof(size_t); n++)
+          out_t[n] ^= iv_t[n];
+        iv = in;
+        len -= 16;
+        in += 16;
+        out += 16;
+      }
+    }
+    memcpy(ivec, iv, 16);
+  } else {
+    if (STRICT_ALIGNMENT &&
+        ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+      uint8_t c;
+      while (len >= 16) {
+        (*block)(in, tmp.c, key);
+        for (n = 0; n < 16; ++n) {
+          c = in[n];
+          out[n] = tmp.c[n] ^ ivec[n];
+          ivec[n] = c;
+        }
+        len -= 16;
+        in += 16;
+        out += 16;
+      }
+    } else if (16 % sizeof(size_t) == 0) { /* always true */
+      while (len >= 16) {
+        size_t c, *out_t = (size_t *)out, *ivec_t = (size_t *)ivec;
+        const size_t *in_t = (const size_t *)in;
+
+        (*block)(in, tmp.c, key);
+        for (n = 0; n < 16 / sizeof(size_t); n++) {
+          c = in_t[n];
+          out_t[n] = tmp.t[n] ^ ivec_t[n];
+          ivec_t[n] = c;
+        }
+        len -= 16;
+        in += 16;
+        out += 16;
+      }
+    }
+  }
+
+  while (len) {
+    uint8_t c;
+    (*block)(in, tmp.c, key);
+    for (n = 0; n < 16 && n < len; ++n) {
+      c = in[n];
+      out[n] = tmp.c[n] ^ ivec[n];
+      ivec[n] = c;
+    }
+    if (len <= 16) {
+      for (; n < 16; ++n) {
+        ivec[n] = in[n];
+      }
+      break;
+    }
+    len -= 16;
+    in += 16;
+    out += 16;
+  }
+}
diff --git a/crypto/modes/cfb.c b/crypto/modes/cfb.c
new file mode 100644
index 0000000..2ae4f5e
--- /dev/null
+++ b/crypto/modes/cfb.c
@@ -0,0 +1,227 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <openssl/modes.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const void *key, uint8_t ivec[16], int *num, int enc,
+                           block128_f block) {
+  unsigned int n;
+  size_t l = 0;
+
+  assert(in && out && key && ivec && num);
+  assert((16 % sizeof(size_t)) == 0);
+
+  n = *num;
+
+  if (enc) {
+    while (n && len) {
+      *(out++) = ivec[n] ^= *(in++);
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (STRICT_ALIGNMENT && ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+      while (l < len) {
+        if (n == 0) {
+          (*block)(ivec, ivec, key);
+        }
+        out[l] = ivec[n] ^= in[l];
+        ++l;
+        n = (n + 1) % 16;
+      }
+      *num = n;
+      return;
+    }
+    while (len >= 16) {
+      (*block)(ivec, ivec, key);
+      for (; n < 16; n += sizeof(size_t)) {
+        *(size_t *)(out + n) = *(size_t *)(ivec + n) ^= *(size_t *)(in + n);
+      }
+      len -= 16;
+      out += 16;
+      in += 16;
+      n = 0;
+    }
+    if (len) {
+      (*block)(ivec, ivec, key);
+      while (len--) {
+        out[n] = ivec[n] ^= in[n];
+        ++n;
+      }
+    }
+    *num = n;
+    return;
+  } else {
+    while (n && len) {
+      uint8_t c;
+      *(out++) = ivec[n] ^ (c = *(in++));
+      ivec[n] = c;
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (STRICT_ALIGNMENT && ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+      while (l < len) {
+        unsigned char c;
+        if (n == 0) {
+          (*block)(ivec, ivec, key);
+        }
+        out[l] = ivec[n] ^ (c = in[l]);
+        ivec[n] = c;
+        ++l;
+        n = (n + 1) % 16;
+      }
+      *num = n;
+      return;
+    }
+    while (len >= 16) {
+      (*block)(ivec, ivec, key);
+      for (; n < 16; n += sizeof(size_t)) {
+        size_t t = *(size_t *)(in + n);
+        *(size_t *)(out + n) = *(size_t *)(ivec + n) ^ t;
+        *(size_t *)(ivec + n) = t;
+      }
+      len -= 16;
+      out += 16;
+      in += 16;
+      n = 0;
+    }
+    if (len) {
+      (*block)(ivec, ivec, key);
+      while (len--) {
+        uint8_t c;
+        out[n] = ivec[n] ^ (c = in[n]);
+        ivec[n] = c;
+        ++n;
+      }
+    }
+    *num = n;
+    return;
+  }
+}
+
+
+/* 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,
+                               block128_f block) {
+  int n, rem, num;
+  uint8_t ovec[16 * 2 + 1]; /* +1 because we dererefence (but don't use) one
+                               byte off the end */
+
+  if (nbits <= 0 || nbits > 128) {
+    return;
+  }
+
+  /* fill in the first half of the new IV with the current IV */
+  memcpy(ovec, ivec, 16);
+  /* construct the new IV */
+  (*block)(ivec, ivec, key);
+  num = (nbits + 7) / 8;
+  if (enc) {
+    /* encrypt the input */
+    for (n = 0; n < num; ++n) {
+      out[n] = (ovec[16 + n] = in[n] ^ ivec[n]);
+    }
+  } else {
+    /* decrypt the input */
+    for (n = 0; n < num; ++n) {
+      out[n] = (ovec[16 + n] = in[n]) ^ ivec[n];
+    }
+  }
+  /* shift ovec left... */
+  rem = nbits % 8;
+  num = nbits / 8;
+  if (rem == 0) {
+    memcpy(ivec, ovec + num, 16);
+  } else {
+    for (n = 0; n < 16; ++n) {
+      ivec[n] = ovec[n + num] << rem | ovec[n + num + 1] >> (8 - rem);
+    }
+  }
+
+  /* it is not necessary to cleanse ovec, since the IV is not secret */
+}
+
+/* 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], int *num,
+                             int enc, block128_f block) {
+  size_t n;
+  uint8_t c[1], d[1];
+
+  assert(in && out && key && ivec && num);
+  assert(*num == 0);
+
+  for (n = 0; n < bits; ++n) {
+    c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
+    cfbr_encrypt_block(c, d, 1, key, ivec, enc, block);
+    out[n / 8] = (out[n / 8] & ~(1 << (unsigned int)(7 - n % 8))) |
+                 ((d[0] & 0x80) >> (unsigned int)(n % 8));
+  }
+}
+
+void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t length, const void *key,
+                             unsigned char ivec[16], int *num, int enc,
+                             block128_f block) {
+  size_t n;
+
+  assert(in && out && key && ivec && num);
+  assert(*num == 0);
+
+  for (n = 0; n < length; ++n) {
+    cfbr_encrypt_block(&in[n], &out[n], 8, key, ivec, enc, block);
+  }
+}
+
diff --git a/crypto/modes/ctr.c b/crypto/modes/ctr.c
new file mode 100644
index 0000000..4aca5b7
--- /dev/null
+++ b/crypto/modes/ctr.c
@@ -0,0 +1,217 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+#include <openssl/modes.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+/* NOTE: the IV/counter CTR mode is big-endian.  The code itself
+ * is endian-neutral. */
+
+/* increment counter (128-bit int) by 1 */
+static void ctr128_inc(uint8_t *counter) {
+  uint32_t n = 16;
+  uint8_t c;
+
+  do {
+    --n;
+    c = counter[n];
+    ++c;
+    counter[n] = c;
+    if (c) {
+      return;
+    }
+  } while (n);
+}
+
+/* The input encrypted as though 128bit counter mode is being used.  The extra
+ * state information to record how much of the 128bit block we have used is
+ * contained in *num, and the encrypted counter is kept in ecount_buf.  Both
+ * *num and ecount_buf must be initialised with zeros before the first call to
+ * CRYPTO_ctr128_encrypt().
+ *
+ * This algorithm assumes that the counter is in the x lower bits of the IV
+ * (ivec), and that the application has full control over overflow and the rest
+ * 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],
+                           uint8_t ecount_buf[16], unsigned int *num,
+                           block128_f block) {
+  unsigned int n;
+  size_t l=0;
+
+  assert(in && out && key && ecount_buf && num);
+  assert(*num < 16);
+  assert((16 % sizeof(size_t)) == 0);
+
+  n = *num;
+
+  while (n && len) {
+    *(out++) = *(in++) ^ ecount_buf[n];
+    --len;
+    n = (n + 1) % 16;
+  }
+
+  if (STRICT_ALIGNMENT &&
+      ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+    while (l < len) {
+      if (n == 0) {
+        (*block)(ivec, ecount_buf, key);
+        ctr128_inc(ivec);
+      }
+      out[l] = in[l] ^ ecount_buf[n];
+      ++l;
+      n = (n + 1) % 16;
+    }
+
+    *num = n;
+    return;
+  }
+
+  while (len >= 16) {
+    (*block)(ivec, ecount_buf, key);
+    ctr128_inc(ivec);
+    for (; n < 16; n += sizeof(size_t))
+      *(size_t *)(out + n) = *(size_t *)(in + n) ^ *(size_t *)(ecount_buf + n);
+    len -= 16;
+    out += 16;
+    in += 16;
+    n = 0;
+  }
+  if (len) {
+    (*block)(ivec, ecount_buf, key);
+    ctr128_inc(ivec);
+    while (len--) {
+      out[n] = in[n] ^ ecount_buf[n];
+      ++n;
+    }
+  }
+  *num = n;
+}
+
+/* increment upper 96 bits of 128-bit counter by 1 */
+static void ctr96_inc(uint8_t *counter) {
+  uint32_t n = 12;
+  uint8_t c;
+
+  do {
+    --n;
+    c = counter[n];
+    ++c;
+    counter[n] = c;
+    if (c) {
+      return;
+    }
+  } 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) {
+  unsigned int n, ctr32;
+
+  assert(in && out && key && ecount_buf && num);
+  assert(*num < 16);
+
+  n = *num;
+
+  while (n && len) {
+    *(out++) = *(in++) ^ ecount_buf[n];
+    --len;
+    n = (n + 1) % 16;
+  }
+
+  ctr32 = GETU32(ivec + 12);
+  while (len >= 16) {
+    size_t blocks = len / 16;
+    /* 1<<28 is just a not-so-small yet not-so-large number...
+     * Below condition is practically never met, but it has to
+     * be checked for code correctness. */
+    if (sizeof(size_t) > sizeof(unsigned int) && blocks > (1U << 28))
+      blocks = (1U << 28);
+    /* As (*func) operates on 32-bit counter, caller
+     * has to handle overflow. 'if' below detects the
+     * overflow, which is then handled by limiting the
+     * amount of blocks to the exact overflow point... */
+    ctr32 += (uint32_t)blocks;
+    if (ctr32 < blocks) {
+      blocks -= ctr32;
+      ctr32 = 0;
+    }
+    (*func)(in, out, blocks, key, ivec);
+    /* (*func) does not update ivec, caller does: */
+    PUTU32(ivec + 12, ctr32);
+    /* ... overflow was detected, propogate carry. */
+    if (ctr32 == 0)
+      ctr96_inc(ivec);
+    blocks *= 16;
+    len -= blocks;
+    out += blocks;
+    in += blocks;
+  }
+  if (len) {
+    memset(ecount_buf, 0, 16);
+    (*func)(ecount_buf, ecount_buf, 1, key, ivec);
+    ++ctr32;
+    PUTU32(ivec + 12, ctr32);
+    if (ctr32 == 0) {
+      ctr96_inc(ivec);
+    }
+    while (len--) {
+      out[n] = in[n] ^ ecount_buf[n];
+      ++n;
+    }
+  }
+
+  *num = n;
+}
diff --git a/crypto/modes/gcm.c b/crypto/modes/gcm.c
new file mode 100644
index 0000000..b950d9f
--- /dev/null
+++ b/crypto/modes/gcm.c
@@ -0,0 +1,1189 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <openssl/modes.h>
+
+#include <assert.h>
+
+#include <openssl/mem.h>
+#include <openssl/cpu.h>
+
+#include "internal.h"
+
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64) || defined(OPENSSL_ARM))
+#define GHASH_ASM
+#endif
+
+#if defined(BSWAP4) && STRICT_ALIGNMENT == 1
+/* redefine, because alignment is ensured */
+#undef GETU32
+#define GETU32(p) BSWAP4(*(const uint32_t *)(p))
+#undef PUTU32
+#define PUTU32(p, v) *(uint32_t *)(p) = BSWAP4(v)
+#endif
+
+#define PACK(s) ((size_t)(s) << (sizeof(size_t) * 8 - 16))
+#define REDUCE1BIT(V)                                     \
+  do {                                                    \
+    if (sizeof(size_t) == 8) {                            \
+      uint64_t T = U64(0xe100000000000000) & (0 - (V.lo & 1)); \
+      V.lo = (V.hi << 63) | (V.lo >> 1);                  \
+      V.hi = (V.hi >> 1) ^ T;                             \
+    } else {                                              \
+      uint32_t T = 0xe1000000U & (0 - (uint32_t)(V.lo & 1));        \
+      V.lo = (V.hi << 63) | (V.lo >> 1);                  \
+      V.hi = (V.hi >> 1) ^ ((uint64_t)T << 32);                \
+    }                                                     \
+  } while (0)
+
+
+static void gcm_init_4bit(u128 Htable[16], uint64_t H[2]) {
+  u128 V;
+
+  Htable[0].hi = 0;
+  Htable[0].lo = 0;
+  V.hi = H[0];
+  V.lo = H[1];
+
+  Htable[8] = V;
+  REDUCE1BIT(V);
+  Htable[4] = V;
+  REDUCE1BIT(V);
+  Htable[2] = V;
+  REDUCE1BIT(V);
+  Htable[1] = V;
+  Htable[3].hi = V.hi ^ Htable[2].hi, Htable[3].lo = V.lo ^ Htable[2].lo;
+  V = Htable[4];
+  Htable[5].hi = V.hi ^ Htable[1].hi, Htable[5].lo = V.lo ^ Htable[1].lo;
+  Htable[6].hi = V.hi ^ Htable[2].hi, Htable[6].lo = V.lo ^ Htable[2].lo;
+  Htable[7].hi = V.hi ^ Htable[3].hi, Htable[7].lo = V.lo ^ Htable[3].lo;
+  V = Htable[8];
+  Htable[9].hi = V.hi ^ Htable[1].hi, Htable[9].lo = V.lo ^ Htable[1].lo;
+  Htable[10].hi = V.hi ^ Htable[2].hi, Htable[10].lo = V.lo ^ Htable[2].lo;
+  Htable[11].hi = V.hi ^ Htable[3].hi, Htable[11].lo = V.lo ^ Htable[3].lo;
+  Htable[12].hi = V.hi ^ Htable[4].hi, Htable[12].lo = V.lo ^ Htable[4].lo;
+  Htable[13].hi = V.hi ^ Htable[5].hi, Htable[13].lo = V.lo ^ Htable[5].lo;
+  Htable[14].hi = V.hi ^ Htable[6].hi, Htable[14].lo = V.lo ^ Htable[6].lo;
+  Htable[15].hi = V.hi ^ Htable[7].hi, Htable[15].lo = V.lo ^ Htable[7].lo;
+
+#if defined(GHASH_ASM)
+  /* ARM assembler expects specific dword order in Htable. */
+  {
+    int j;
+    const union {
+      long one;
+      char little;
+    } is_endian = {1};
+
+    if (is_endian.little) {
+      for (j = 0; j < 16; ++j) {
+        V = Htable[j];
+        Htable[j].hi = V.lo;
+        Htable[j].lo = V.hi;
+      }
+    } else {
+      for (j = 0; j < 16; ++j) {
+        V = Htable[j];
+        Htable[j].hi = V.lo << 32 | V.lo >> 32;
+        Htable[j].lo = V.hi << 32 | V.hi >> 32;
+      }
+    }
+  }
+#endif
+}
+
+#if !defined(GHASH_ASM)
+static const size_t rem_4bit[16] = {
+    PACK(0x0000), PACK(0x1C20), PACK(0x3840), PACK(0x2460),
+    PACK(0x7080), PACK(0x6CA0), PACK(0x48C0), PACK(0x54E0),
+    PACK(0xE100), PACK(0xFD20), PACK(0xD940), PACK(0xC560),
+    PACK(0x9180), PACK(0x8DA0), PACK(0xA9C0), PACK(0xB5E0)};
+
+static void gcm_gmult_4bit(uint64_t Xi[2], const u128 Htable[16]) {
+  u128 Z;
+  int cnt = 15;
+  size_t rem, nlo, nhi;
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+
+  nlo = ((const uint8_t *)Xi)[15];
+  nhi = nlo >> 4;
+  nlo &= 0xf;
+
+  Z.hi = Htable[nlo].hi;
+  Z.lo = Htable[nlo].lo;
+
+  while (1) {
+    rem = (size_t)Z.lo & 0xf;
+    Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+    Z.hi = (Z.hi >> 4);
+    if (sizeof(size_t) == 8) {
+      Z.hi ^= rem_4bit[rem];
+    } else {
+      Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+    }
+
+    Z.hi ^= Htable[nhi].hi;
+    Z.lo ^= Htable[nhi].lo;
+
+    if (--cnt < 0) {
+      break;
+    }
+
+    nlo = ((const uint8_t *)Xi)[cnt];
+    nhi = nlo >> 4;
+    nlo &= 0xf;
+
+    rem = (size_t)Z.lo & 0xf;
+    Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+    Z.hi = (Z.hi >> 4);
+    if (sizeof(size_t) == 8) {
+      Z.hi ^= rem_4bit[rem];
+    } else {
+      Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+    }
+
+    Z.hi ^= Htable[nlo].hi;
+    Z.lo ^= Htable[nlo].lo;
+  }
+
+  if (is_endian.little) {
+#ifdef BSWAP8
+    Xi[0] = BSWAP8(Z.hi);
+    Xi[1] = BSWAP8(Z.lo);
+#else
+    uint8_t *p = (uint8_t *)Xi;
+    uint32_t v;
+    v = (uint32_t)(Z.hi >> 32);
+    PUTU32(p, v);
+    v = (uint32_t)(Z.hi);
+    PUTU32(p + 4, v);
+    v = (uint32_t)(Z.lo >> 32);
+    PUTU32(p + 8, v);
+    v = (uint32_t)(Z.lo);
+    PUTU32(p + 12, v);
+#endif
+  } else {
+    Xi[0] = Z.hi;
+    Xi[1] = Z.lo;
+  }
+}
+
+/* Streamed gcm_mult_4bit, see CRYPTO_gcm128_[en|de]crypt for
+ * details... Compiler-generated code doesn't seem to give any
+ * performance improvement, at least not on x86[_64]. It's here
+ * mostly as reference and a placeholder for possible future
+ * non-trivial optimization[s]... */
+static void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                           size_t len) {
+  u128 Z;
+  int cnt;
+  size_t rem, nlo, nhi;
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+
+  do {
+    cnt = 15;
+    nlo = ((const uint8_t *)Xi)[15];
+    nlo ^= inp[15];
+    nhi = nlo >> 4;
+    nlo &= 0xf;
+
+    Z.hi = Htable[nlo].hi;
+    Z.lo = Htable[nlo].lo;
+
+    while (1) {
+      rem = (size_t)Z.lo & 0xf;
+      Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+      Z.hi = (Z.hi >> 4);
+      if (sizeof(size_t) == 8) {
+        Z.hi ^= rem_4bit[rem];
+      } else {
+        Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+      }
+
+      Z.hi ^= Htable[nhi].hi;
+      Z.lo ^= Htable[nhi].lo;
+
+      if (--cnt < 0) {
+        break;
+      }
+
+      nlo = ((const uint8_t *)Xi)[cnt];
+      nlo ^= inp[cnt];
+      nhi = nlo >> 4;
+      nlo &= 0xf;
+
+      rem = (size_t)Z.lo & 0xf;
+      Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+      Z.hi = (Z.hi >> 4);
+      if (sizeof(size_t) == 8) {
+        Z.hi ^= rem_4bit[rem];
+      } else {
+        Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+      }
+
+      Z.hi ^= Htable[nlo].hi;
+      Z.lo ^= Htable[nlo].lo;
+    }
+
+    if (is_endian.little) {
+#ifdef BSWAP8
+      Xi[0] = BSWAP8(Z.hi);
+      Xi[1] = BSWAP8(Z.lo);
+#else
+      uint8_t *p = (uint8_t *)Xi;
+      uint32_t v;
+      v = (uint32_t)(Z.hi >> 32);
+      PUTU32(p, v);
+      v = (uint32_t)(Z.hi);
+      PUTU32(p + 4, v);
+      v = (uint32_t)(Z.lo >> 32);
+      PUTU32(p + 8, v);
+      v = (uint32_t)(Z.lo);
+      PUTU32(p + 12, v);
+#endif
+    } else {
+      Xi[0] = Z.hi;
+      Xi[1] = Z.lo;
+    }
+  } while (inp += 16, len -= 16);
+}
+#else /* GHASH_ASM */
+void gcm_gmult_4bit(uint64_t Xi[2], const u128 Htable[16]);
+void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                    size_t len);
+#endif
+
+#define GCM_MUL(ctx, Xi) gcm_gmult_4bit(ctx->Xi.u, ctx->Htable)
+#if defined(GHASH_ASM)
+#define GHASH(ctx, in, len) gcm_ghash_4bit((ctx)->Xi.u, (ctx)->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... */
+#define GHASH_CHUNK (3 * 1024)
+#endif
+
+
+#if defined(GHASH_ASM)
+#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+#define GHASH_ASM_X86_OR_64
+#define GCM_FUNCREF_4BIT
+void gcm_init_clmul(u128 Htable[16], const uint64_t Xi[2]);
+void gcm_gmult_clmul(uint64_t Xi[2], const u128 Htable[16]);
+void gcm_ghash_clmul(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                     size_t len);
+
+#if defined(OPENSSL_X86)
+#define gcm_init_avx gcm_init_clmul
+#define gcm_gmult_avx gcm_gmult_clmul
+#define gcm_ghash_avx gcm_ghash_clmul
+#else
+void gcm_init_avx(u128 Htable[16], const uint64_t Xi[2]);
+void gcm_gmult_avx(uint64_t Xi[2], const u128 Htable[16]);
+void gcm_ghash_avx(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp, size_t len);
+#endif
+
+#if defined(OPENSSL_X86)
+#define GHASH_ASM_X86
+void gcm_gmult_4bit_mmx(uint64_t Xi[2], const u128 Htable[16]);
+void gcm_ghash_4bit_mmx(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                        size_t len);
+
+void gcm_gmult_4bit_x86(uint64_t Xi[2], const u128 Htable[16]);
+void gcm_ghash_4bit_x86(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                        size_t len);
+#endif
+#elif defined(OPENSSL_ARM)
+#include "../arm_arch.h"
+#if __ARM_ARCH__ >= 7
+#define GHASH_ASM_ARM
+#define GCM_FUNCREF_4BIT
+void gcm_gmult_neon(uint64_t Xi[2], const u128 Htable[16]);
+void gcm_ghash_neon(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                    size_t len);
+#endif
+#endif
+#endif
+
+#ifdef GCM_FUNCREF_4BIT
+#undef GCM_MUL
+#define GCM_MUL(ctx, Xi) (*gcm_gmult_p)(ctx->Xi.u, ctx->Htable)
+#ifdef GHASH
+#undef GHASH
+#define GHASH(ctx, in, len) (*gcm_ghash_p)(ctx->Xi.u, ctx->Htable, in, len)
+#endif
+#endif
+
+GCM128_CONTEXT *CRYPTO_gcm128_new(void *key, block128_f block) {
+  GCM128_CONTEXT *ret;
+
+  ret = (GCM128_CONTEXT *)OPENSSL_malloc(sizeof(GCM128_CONTEXT));
+  if (ret != NULL) {
+    CRYPTO_gcm128_init(ret, key, block);
+  }
+
+  return ret;
+}
+
+void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, void *key, block128_f block) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+
+  memset(ctx, 0, sizeof(*ctx));
+  ctx->block = block;
+  ctx->key = key;
+
+  (*block)(ctx->H.c, ctx->H.c, key);
+
+  if (is_endian.little) {
+/* H is stored in host byte order */
+#ifdef BSWAP8
+    ctx->H.u[0] = BSWAP8(ctx->H.u[0]);
+    ctx->H.u[1] = BSWAP8(ctx->H.u[1]);
+#else
+    uint8_t *p = ctx->H.c;
+    uint64_t hi, lo;
+    hi = (uint64_t)GETU32(p) << 32 | GETU32(p + 4);
+    lo = (uint64_t)GETU32(p + 8) << 32 | GETU32(p + 12);
+    ctx->H.u[0] = hi;
+    ctx->H.u[1] = lo;
+#endif
+  }
+
+#if defined(GHASH_ASM_X86_OR_64)
+  if (OPENSSL_ia32cap_P[0] & (1 << 24) && /* check FXSR bit */
+      OPENSSL_ia32cap_P[1] & (1 << 1)) {  /* check PCLMULQDQ bit */
+    if (((OPENSSL_ia32cap_P[1] >> 22) & 0x41) == 0x41) { /* AVX+MOVBE */
+      gcm_init_avx(ctx->Htable, ctx->H.u);
+      ctx->gmult = gcm_gmult_avx;
+      ctx->ghash = gcm_ghash_avx;
+    } else {
+      gcm_init_clmul(ctx->Htable, ctx->H.u);
+      ctx->gmult = gcm_gmult_clmul;
+      ctx->ghash = gcm_ghash_clmul;
+    }
+    return;
+  }
+  gcm_init_4bit(ctx->Htable, ctx->H.u);
+#if defined(GHASH_ASM_X86) /* x86 only */
+  if (OPENSSL_ia32cap_P[0] & (1 << 25)) { /* check SSE bit */
+    ctx->gmult = gcm_gmult_4bit_mmx;
+    ctx->ghash = gcm_ghash_4bit_mmx;
+  } else {
+    ctx->gmult = gcm_gmult_4bit_x86;
+    ctx->ghash = gcm_ghash_4bit_x86;
+  }
+#else
+  ctx->gmult = gcm_gmult_4bit;
+  ctx->ghash = gcm_ghash_4bit;
+#endif
+#elif defined(GHASH_ASM_ARM)
+  if (CRYPTO_is_NEON_capable()) {
+    ctx->gmult = gcm_gmult_neon;
+    ctx->ghash = gcm_ghash_neon;
+  } else {
+    gcm_init_4bit(ctx->Htable, ctx->H.u);
+    ctx->gmult = gcm_gmult_4bit;
+    ctx->ghash = gcm_ghash_4bit;
+  }
+#else
+  ctx->gmult = gcm_gmult_4bit;
+  ctx->ghash = gcm_ghash_4bit;
+  gcm_init_4bit(ctx->Htable, ctx->H.u);
+#endif
+}
+
+void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const uint8_t *iv, size_t len) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+  unsigned int ctr;
+#ifdef GCM_FUNCREF_4BIT
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+#endif
+
+  ctx->Yi.u[0] = 0;
+  ctx->Yi.u[1] = 0;
+  ctx->Xi.u[0] = 0;
+  ctx->Xi.u[1] = 0;
+  ctx->len.u[0] = 0; /* AAD length */
+  ctx->len.u[1] = 0; /* message length */
+  ctx->ares = 0;
+  ctx->mres = 0;
+
+  if (len == 12) {
+    memcpy(ctx->Yi.c, iv, 12);
+    ctx->Yi.c[15] = 1;
+    ctr = 1;
+  } else {
+    size_t i;
+    uint64_t len0 = len;
+
+    while (len >= 16) {
+      for (i = 0; i < 16; ++i) {
+        ctx->Yi.c[i] ^= iv[i];
+      }
+      GCM_MUL(ctx, Yi);
+      iv += 16;
+      len -= 16;
+    }
+    if (len) {
+      for (i = 0; i < len; ++i) {
+        ctx->Yi.c[i] ^= iv[i];
+      }
+      GCM_MUL(ctx, Yi);
+    }
+    len0 <<= 3;
+    if (is_endian.little) {
+#ifdef BSWAP8
+      ctx->Yi.u[1] ^= BSWAP8(len0);
+#else
+      ctx->Yi.c[8] ^= (uint8_t)(len0 >> 56);
+      ctx->Yi.c[9] ^= (uint8_t)(len0 >> 48);
+      ctx->Yi.c[10] ^= (uint8_t)(len0 >> 40);
+      ctx->Yi.c[11] ^= (uint8_t)(len0 >> 32);
+      ctx->Yi.c[12] ^= (uint8_t)(len0 >> 24);
+      ctx->Yi.c[13] ^= (uint8_t)(len0 >> 16);
+      ctx->Yi.c[14] ^= (uint8_t)(len0 >> 8);
+      ctx->Yi.c[15] ^= (uint8_t)(len0);
+#endif
+    } else {
+      ctx->Yi.u[1] ^= len0;
+    }
+
+    GCM_MUL(ctx, Yi);
+
+    if (is_endian.little) {
+      ctr = GETU32(ctx->Yi.c + 12);
+    } else {
+      ctr = ctx->Yi.d[3];
+    }
+  }
+
+  (*ctx->block)(ctx->Yi.c, ctx->EK0.c, ctx->key);
+  ++ctr;
+  if (is_endian.little) {
+    PUTU32(ctx->Yi.c + 12, ctr);
+  } else {
+    ctx->Yi.d[3] = ctr;
+  }
+}
+
+int CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx, const uint8_t *aad, size_t len) {
+  size_t i;
+  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;
+#ifdef GHASH
+  void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                      size_t len) = ctx->ghash;
+#endif
+#endif
+
+  if (ctx->len.u[1]) {
+    return 0;
+  }
+
+  alen += len;
+  if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len)) {
+    return 0;
+  }
+  ctx->len.u[0] = alen;
+
+  n = ctx->ares;
+  if (n) {
+    while (n && len) {
+      ctx->Xi.c[n] ^= *(aad++);
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->ares = n;
+      return 1;
+    }
+  }
+
+#ifdef GHASH
+  if ((i = (len & (size_t) - 16))) {
+    GHASH(ctx, aad, i);
+    aad += i;
+    len -= i;
+  }
+#else
+  while (len >= 16) {
+    for (i = 0; i < 16; ++i) {
+      ctx->Xi.c[i] ^= aad[i];
+    }
+    GCM_MUL(ctx, Xi);
+    aad += 16;
+    len -= 16;
+  }
+#endif
+  if (len) {
+    n = (unsigned int)len;
+    for (i = 0; i < len; ++i)
+      ctx->Xi.c[i] ^= aad[i];
+  }
+
+  ctx->ares = n;
+  return 1;
+}
+
+int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const unsigned char *in,
+                          unsigned char *out, size_t len) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+  unsigned int n, ctr;
+  size_t i;
+  uint64_t mlen = ctx->len.u[1];
+  block128_f block = ctx->block;
+  void *key = ctx->key;
+#ifdef GCM_FUNCREF_4BIT
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+#ifdef GHASH
+  void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                      size_t len) = ctx->ghash;
+#endif
+#endif
+
+  mlen += len;
+  if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) {
+    return 0;
+  }
+  ctx->len.u[1] = mlen;
+
+  if (ctx->ares) {
+    /* First call to encrypt finalizes GHASH(AAD) */
+    GCM_MUL(ctx, Xi);
+    ctx->ares = 0;
+  }
+
+  if (is_endian.little) {
+    ctr = GETU32(ctx->Yi.c + 12);
+  } else {
+    ctr = ctx->Yi.d[3];
+  }
+
+  n = ctx->mres;
+  if (n) {
+    while (n && len) {
+      ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->mres = n;
+      return 1;
+    }
+  }
+  if (STRICT_ALIGNMENT && ((size_t)in | (size_t)out) % sizeof(size_t) != 0) {
+    for (i = 0; i < len; ++i) {
+      if (n == 0) {
+        (*block)(ctx->Yi.c, ctx->EKi.c, key);
+        ++ctr;
+        if (is_endian.little) {
+          PUTU32(ctx->Yi.c + 12, ctr);
+        } else {
+          ctx->Yi.d[3] = ctr;
+        }
+      }
+      ctx->Xi.c[n] ^= out[i] = in[i] ^ ctx->EKi.c[n];
+      n = (n + 1) % 16;
+      if (n == 0) {
+        GCM_MUL(ctx, Xi);
+      }
+    }
+
+    ctx->mres = n;
+    return 1;
+  }
+#if defined(GHASH) && defined(GHASH_CHUNK)
+  while (len >= GHASH_CHUNK) {
+    size_t j = GHASH_CHUNK;
+
+    while (j) {
+      size_t *out_t = (size_t *)out;
+      const size_t *in_t = (const size_t *)in;
+
+      (*block)(ctx->Yi.c, ctx->EKi.c, key);
+      ++ctr;
+      if (is_endian.little) {
+        PUTU32(ctx->Yi.c + 12, ctr);
+      } else {
+        ctx->Yi.d[3] = ctr;
+      }
+      for (i = 0; i < 16 / sizeof(size_t); ++i) {
+        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+      }
+      out += 16;
+      in += 16;
+      j -= 16;
+    }
+    GHASH(ctx, out - GHASH_CHUNK, GHASH_CHUNK);
+    len -= GHASH_CHUNK;
+  }
+  if ((i = (len & (size_t) - 16))) {
+    size_t j = i;
+
+    while (len >= 16) {
+      size_t *out_t = (size_t *)out;
+      const size_t *in_t = (const size_t *)in;
+
+      (*block)(ctx->Yi.c, ctx->EKi.c, key);
+      ++ctr;
+      if (is_endian.little) {
+        PUTU32(ctx->Yi.c + 12, ctr);
+      } else {
+        ctx->Yi.d[3] = ctr;
+      }
+      for (i = 0; i < 16 / sizeof(size_t); ++i) {
+        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+      }
+      out += 16;
+      in += 16;
+      len -= 16;
+    }
+    GHASH(ctx, out - j, j);
+  }
+#else
+  while (len >= 16) {
+    size_t *out_t = (size_t *)out;
+    const size_t *in_t = (const size_t *)in;
+
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    for (i = 0; i < 16 / sizeof(size_t); ++i) {
+      ctx->Xi.t[i] ^= out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+    }
+    GCM_MUL(ctx, Xi);
+    out += 16;
+    in += 16;
+    len -= 16;
+  }
+#endif
+  if (len) {
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    while (len--) {
+      ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
+      ++n;
+    }
+  }
+
+  ctx->mres = n;
+  return 1;
+}
+
+int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const unsigned char *in,
+                          unsigned char *out, size_t len) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+  unsigned int n, ctr;
+  size_t i;
+  uint64_t mlen = ctx->len.u[1];
+  block128_f block = ctx->block;
+  void *key = ctx->key;
+#ifdef GCM_FUNCREF_4BIT
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+#ifdef GHASH
+  void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                      size_t len) = ctx->ghash;
+#endif
+#endif
+
+  mlen += len;
+  if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) {
+    return 0;
+  }
+  ctx->len.u[1] = mlen;
+
+  if (ctx->ares) {
+    /* First call to decrypt finalizes GHASH(AAD) */
+    GCM_MUL(ctx, Xi);
+    ctx->ares = 0;
+  }
+
+  if (is_endian.little) {
+    ctr = GETU32(ctx->Yi.c + 12);
+  } else {
+    ctr = ctx->Yi.d[3];
+  }
+
+  n = ctx->mres;
+  if (n) {
+    while (n && len) {
+      uint8_t c = *(in++);
+      *(out++) = c ^ ctx->EKi.c[n];
+      ctx->Xi.c[n] ^= c;
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->mres = n;
+      return 1;
+    }
+  }
+  if (STRICT_ALIGNMENT && ((size_t)in | (size_t)out) % sizeof(size_t) != 0) {
+    for (i = 0; i < len; ++i) {
+      uint8_t c;
+      if (n == 0) {
+        (*block)(ctx->Yi.c, ctx->EKi.c, key);
+        ++ctr;
+        if (is_endian.little) {
+          PUTU32(ctx->Yi.c + 12, ctr);
+        } else {
+          ctx->Yi.d[3] = ctr;
+        }
+      }
+      c = in[i];
+      out[i] = c ^ ctx->EKi.c[n];
+      ctx->Xi.c[n] ^= c;
+      n = (n + 1) % 16;
+      if (n == 0) {
+        GCM_MUL(ctx, Xi);
+      }
+    }
+
+    ctx->mres = n;
+    return 1;
+  }
+#if defined(GHASH) && defined(GHASH_CHUNK)
+  while (len >= GHASH_CHUNK) {
+    size_t j = GHASH_CHUNK;
+
+    GHASH(ctx, in, GHASH_CHUNK);
+    while (j) {
+      size_t *out_t = (size_t *)out;
+      const size_t *in_t = (const size_t *)in;
+
+      (*block)(ctx->Yi.c, ctx->EKi.c, key);
+      ++ctr;
+      if (is_endian.little) {
+        PUTU32(ctx->Yi.c + 12, ctr);
+      } else {
+        ctx->Yi.d[3] = ctr;
+      }
+      for (i = 0; i < 16 / sizeof(size_t); ++i) {
+        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+      }
+      out += 16;
+      in += 16;
+      j -= 16;
+    }
+    len -= GHASH_CHUNK;
+  }
+  if ((i = (len & (size_t) - 16))) {
+    GHASH(ctx, in, i);
+    while (len >= 16) {
+      size_t *out_t = (size_t *)out;
+      const size_t *in_t = (const size_t *)in;
+
+      (*block)(ctx->Yi.c, ctx->EKi.c, key);
+      ++ctr;
+      if (is_endian.little) {
+        PUTU32(ctx->Yi.c + 12, ctr);
+      } else {
+        ctx->Yi.d[3] = ctr;
+      }
+      for (i = 0; i < 16 / sizeof(size_t); ++i) {
+        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+      }
+      out += 16;
+      in += 16;
+      len -= 16;
+    }
+  }
+#else
+  while (len >= 16) {
+    size_t *out_t = (size_t *)out;
+    const size_t *in_t = (const size_t *)in;
+
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    for (i = 0; i < 16 / sizeof(size_t); ++i) {
+      size_t c = in_t[i];
+      out_t[i] = c ^ ctx->EKi.t[i];
+      ctx->Xi.t[i] ^= c;
+    }
+    GCM_MUL(ctx, Xi);
+    out += 16;
+    in += 16;
+    len -= 16;
+  }
+#endif
+  if (len) {
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    while (len--) {
+      uint8_t c = in[n];
+      ctx->Xi.c[n] ^= c;
+      out[n] = c ^ ctx->EKi.c[n];
+      ++n;
+    }
+  }
+
+  ctx->mres = n;
+  return 1;
+}
+
+int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const uint8_t *in,
+                                uint8_t *out, size_t len, ctr128_f stream) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+  unsigned int n, ctr;
+  size_t i;
+  uint64_t mlen = ctx->len.u[1];
+  void *key = ctx->key;
+#ifdef GCM_FUNCREF_4BIT
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+#ifdef GHASH
+  void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                      size_t len) = ctx->ghash;
+#endif
+#endif
+
+  mlen += len;
+  if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) {
+    return 0;
+  }
+  ctx->len.u[1] = mlen;
+
+  if (ctx->ares) {
+    /* First call to encrypt finalizes GHASH(AAD) */
+    GCM_MUL(ctx, Xi);
+    ctx->ares = 0;
+  }
+
+  if (is_endian.little) {
+    ctr = GETU32(ctx->Yi.c + 12);
+  } else {
+    ctr = ctx->Yi.d[3];
+  }
+
+  n = ctx->mres;
+  if (n) {
+    while (n && len) {
+      ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->mres = n;
+      return 1;
+    }
+  }
+#if defined(GHASH)
+  while (len >= GHASH_CHUNK) {
+    (*stream)(in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
+    ctr += GHASH_CHUNK / 16;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    GHASH(ctx, out, GHASH_CHUNK);
+    out += GHASH_CHUNK;
+    in += GHASH_CHUNK;
+    len -= GHASH_CHUNK;
+  }
+#endif
+  if ((i = (len & (size_t) - 16))) {
+    size_t j = i / 16;
+
+    (*stream)(in, out, j, key, ctx->Yi.c);
+    ctr += (unsigned int)j;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    in += i;
+    len -= i;
+#if defined(GHASH)
+    GHASH(ctx, out, i);
+    out += i;
+#else
+    while (j--) {
+      for (i = 0; i < 16; ++i) {
+        ctx->Xi.c[i] ^= out[i];
+      }
+      GCM_MUL(ctx, Xi);
+      out += 16;
+    }
+#endif
+  }
+  if (len) {
+    (*ctx->block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    if (is_endian.little) {
+      PUTU32(ctx->Yi.c + 12, ctr);
+    } else {
+      ctx->Yi.d[3] = ctr;
+    }
+    while (len--) {
+      ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
+      ++n;
+    }
+  }
+
+  ctx->mres = n;
+  return 1;
+}
+
+int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const uint8_t *in,
+                                uint8_t *out, size_t len,
+                                ctr128_f stream) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+  unsigned int n, ctr;
+  size_t i;
+  uint64_t mlen = ctx->len.u[1];
+  void *key = ctx->key;
+#ifdef GCM_FUNCREF_4BIT
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+#ifdef GHASH
+  void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                      size_t len) = ctx->ghash;
+#endif
+#endif
+
+  mlen += len;
+  if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) {
+    return 0;
+  }
+  ctx->len.u[1] = mlen;
+
+  if (ctx->ares) {
+    /* First call to decrypt finalizes GHASH(AAD) */
+    GCM_MUL(ctx, Xi);
+    ctx->ares = 0;
+  }
+
+  if (is_endian.little) {
+    ctr = GETU32(ctx->Yi.c + 12);
+  } else {
+    ctr = ctx->Yi.d[3];
+  }
+
+  n = ctx->mres;
+  if (n) {
+    while (n && len) {
+      uint8_t c = *(in++);
+      *(out++) = c ^ ctx->EKi.c[n];
+      ctx->Xi.c[n] ^= c;
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->mres = n;
+      return 1;
+    }
+  }
+#if defined(GHASH)
+  while (len >= GHASH_CHUNK) {
+    GHASH(ctx, in, GHASH_CHUNK);
+    (*stream)(in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
+    ctr += GHASH_CHUNK / 16;
+    if (is_endian.little)
+      PUTU32(ctx->Yi.c + 12, ctr);
+    else
+      ctx->Yi.d[3] = ctr;
+    out += GHASH_CHUNK;
+    in += GHASH_CHUNK;
+    len -= GHASH_CHUNK;
+  }
+#endif
+  if ((i = (len & (size_t) - 16))) {
+    size_t j = i / 16;
+
+#if defined(GHASH)
+    GHASH(ctx, in, i);
+#else
+    while (j--) {
+      size_t k;
+      for (k = 0; k < 16; ++k)
+        ctx->Xi.c[k] ^= in[k];
+      GCM_MUL(ctx, Xi);
+      in += 16;
+    }
+    j = i / 16;
+    in -= i;
+#endif
+    (*stream)(in, out, j, key, ctx->Yi.c);
+    ctr += (unsigned int)j;
+    if (is_endian.little)
+      PUTU32(ctx->Yi.c + 12, ctr);
+    else
+      ctx->Yi.d[3] = ctr;
+    out += i;
+    in += i;
+    len -= i;
+  }
+  if (len) {
+    (*ctx->block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    if (is_endian.little)
+      PUTU32(ctx->Yi.c + 12, ctr);
+    else
+      ctx->Yi.d[3] = ctr;
+    while (len--) {
+      uint8_t c = in[n];
+      ctx->Xi.c[n] ^= c;
+      out[n] = c ^ ctx->EKi.c[n];
+      ++n;
+    }
+  }
+
+  ctx->mres = n;
+  return 1;
+}
+
+int CRYPTO_gcm128_finish(GCM128_CONTEXT *ctx, const uint8_t *tag, size_t len) {
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+  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;
+#endif
+
+  if (ctx->mres || ctx->ares) {
+    GCM_MUL(ctx, Xi);
+  }
+
+  if (is_endian.little) {
+#ifdef BSWAP8
+    alen = BSWAP8(alen);
+    clen = BSWAP8(clen);
+#else
+    uint8_t *p = ctx->len.c;
+
+    ctx->len.u[0] = alen;
+    ctx->len.u[1] = clen;
+
+    alen = (uint64_t)GETU32(p) << 32 | GETU32(p + 4);
+    clen = (uint64_t)GETU32(p + 8) << 32 | GETU32(p + 12);
+#endif
+  }
+
+  ctx->Xi.u[0] ^= alen;
+  ctx->Xi.u[1] ^= clen;
+  GCM_MUL(ctx, Xi);
+
+  ctx->Xi.u[0] ^= ctx->EK0.u[0];
+  ctx->Xi.u[1] ^= ctx->EK0.u[1];
+
+  if (tag && len <= sizeof(ctx->Xi)) {
+    return CRYPTO_memcmp(ctx->Xi.c, tag, len) == 0;
+  } else {
+    return 0;
+  }
+}
+
+void CRYPTO_gcm128_tag(GCM128_CONTEXT *ctx, unsigned char *tag, size_t len) {
+  CRYPTO_gcm128_finish(ctx, NULL, 0);
+  memcpy(tag, ctx->Xi.c, len <= sizeof(ctx->Xi.c) ? len : sizeof(ctx->Xi.c));
+}
+
+void CRYPTO_gcm128_release(GCM128_CONTEXT *ctx) {
+  if (ctx) {
+    OPENSSL_cleanse(ctx, sizeof(*ctx));
+    OPENSSL_free(ctx);
+  }
+}
diff --git a/crypto/modes/gcm_test.c b/crypto/modes/gcm_test.c
new file mode 100644
index 0000000..8c52b85
--- /dev/null
+++ b/crypto/modes/gcm_test.c
@@ -0,0 +1,431 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <stdio.h>
+
+#include <openssl/aes.h>
+#include <openssl/mem.h>
+#include <openssl/modes.h>
+
+#include "internal.h"
+
+
+struct test_case {
+  const char *key;
+  const char *plaintext;
+  const char *additional_data;
+  const char *nonce;
+  const char *ciphertext;
+  const char *tag;
+};
+
+static const struct test_case test_cases[] = {
+  {
+    "00000000000000000000000000000000",
+    NULL,
+    NULL,
+    "000000000000000000000000",
+    NULL,
+    "58e2fccefa7e3061367f1d57a4e7455a",
+  },
+  {
+    "00000000000000000000000000000000",
+    "00000000000000000000000000000000",
+    NULL,
+    "000000000000000000000000",
+    "0388dace60b6a392f328c2b971b2fe78",
+    "ab6e47d42cec13bdf53a67b21257bddf",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+    NULL,
+    "cafebabefacedbaddecaf888",
+    "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+    "4d5c2af327cd64a62cf35abd2ba6fab4",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbaddecaf888",
+    "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+    "5bc94fbc3221a5db94fae95ae7121a47",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+    "3612d2e79e3b0785561be14aaca2fccb",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+    "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+    "619cc5aefffe0bfa462af43c1699d050",
+  },
+  {
+    "000000000000000000000000000000000000000000000000",
+    NULL,
+    NULL,
+    "000000000000000000000000",
+    NULL,
+    "cd33b28ac773f74ba00ed1f312572435",
+  },
+  {
+    "000000000000000000000000000000000000000000000000",
+    "00000000000000000000000000000000",
+    NULL,
+    "000000000000000000000000",
+    "98e7247c07f0fe411c267e4384b0f600",
+    "2ff58d80033927ab8ef4d4587514f0fb",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+    NULL,
+    "cafebabefacedbaddecaf888",
+    "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+    "9924a7c8587336bfb118024db8674a14",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbaddecaf888",
+    "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+    "2519498e80f1478f37ba55bd6d27618c",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+    "65dcc57fcf623a24094fcca40d3533f8",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+    "65dcc57fcf623a24094fcca40d3533f8",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+    "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+    "dcf566ff291c25bbb8568fc3d376a6d9",
+  },
+  {
+    "0000000000000000000000000000000000000000000000000000000000000000",
+    NULL,
+    NULL,
+    "000000000000000000000000",
+    NULL,
+    "530f8afbc74536b9a963b4f1c4cb738b",
+  },
+  {
+    "0000000000000000000000000000000000000000000000000000000000000000",
+    "00000000000000000000000000000000",
+    NULL,
+    "000000000000000000000000",
+    "cea7403d4d606b6e074ec5d3baf39d18",
+    "d0d1c8a799996bf0265b98b5d48ab919",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+    NULL,
+    "cafebabefacedbaddecaf888",
+    "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+    "b094dac5d93471bdec1a502270e3cc6c",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbaddecaf888",
+    "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+    "76fc6ece0f4e1768cddf8853bb2d551b",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+    "3a337dbf46a792c45e454913fe2ea8f2",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+    "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+    "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+  },
+  {
+    "00000000000000000000000000000000",
+    NULL,
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+    "000000000000000000000000",
+    NULL,
+    "5fea793a2d6f974d37e68e0cb8ff9492",
+  },
+  {
+    "00000000000000000000000000000000",
+    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+    NULL,
+    /* This nonce results in 0xfff in counter LSB. */
+    "ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+    "56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606872ca10dee15b3249b1a1b958f23134c4bccb7d03200bce420a2f8eb66dcf3644d1423c1b5699003c13ecef4bf38a3b60eedc34033bac1902783dc6d89e2e774188a439c7ebcc0672dbda4ddcfb2794613b0be41315ef778708a70ee7d75165c",
+    "8b307f6b33286d0ab026a9ed3fe1e85f",
+  },
+};
+
+static int from_hex(uint8_t *out, char in) {
+  if (in >= '0' && in <= '9') {
+    *out = in - '0';
+    return 1;
+  }
+  if (in >= 'a' && in <= 'f') {
+    *out = in - 'a' + 10;
+    return 1;
+  }
+  if (in >= 'A' && in <= 'F') {
+    *out = in - 'A' + 10;
+    return 1;
+  }
+
+  return 0;
+}
+
+static int decode_hex(uint8_t **out, size_t *out_len, const char *in,
+                      unsigned test_num, const char *description) {
+  uint8_t *buf = NULL;
+  size_t i;
+
+  if (in == NULL) {
+    *out = NULL;
+    *out_len = 0;
+    return 1;
+  }
+
+  size_t len = strlen(in);
+  if (len & 1) {
+    fprintf(stderr, "%u: Odd-length %s input.\n", test_num, description);
+    goto err;
+  }
+
+  buf = OPENSSL_malloc(len / 2);
+  if (buf == NULL) {
+    fprintf(stderr, "%u: malloc failure.\n", test_num);
+    goto err;
+  }
+
+  for (i = 0; i < len; i += 2) {
+    uint8_t v, v2;
+    if (!from_hex(&v, in[i]) ||
+        !from_hex(&v2, in[i+1])) {
+      fprintf(stderr, "%u: invalid hex digit in %s around offset %u.\n",
+              test_num, description, (unsigned)i);
+      goto err;
+    }
+    buf[i/2] = (v << 4) | v2;
+  }
+
+  *out = buf;
+  *out_len = len/2;
+  return 1;
+
+err:
+  if (buf) {
+    OPENSSL_free(buf);
+  }
+  return 0;
+}
+
+void hexdump(const char *msg, const void *in, size_t len) {
+  const uint8_t *data = in;
+  size_t i;
+
+  fprintf(stderr, "%s: ", msg);
+  for (i = 0; i < len; i++) {
+    fprintf(stderr, "%02x", data[i]);
+  }
+  fprintf(stderr, "\n");
+}
+
+static int run_test_case(unsigned test_num, const struct test_case *test) {
+  size_t key_len, plaintext_len, additional_data_len, nonce_len, ciphertext_len,
+      tag_len;
+  uint8_t *key = NULL, *plaintext = NULL, *additional_data = NULL,
+          *nonce = NULL, *ciphertext = NULL, *tag = NULL, *out = NULL;
+  int ret = 0;
+  AES_KEY aes_key;
+  GCM128_CONTEXT ctx;
+
+  if (!decode_hex(&key, &key_len, test->key, test_num, "key") ||
+      !decode_hex(&plaintext, &plaintext_len, test->plaintext, test_num,
+                  "plaintext") ||
+      !decode_hex(&additional_data, &additional_data_len, test->additional_data,
+                  test_num, "additional_data") ||
+      !decode_hex(&nonce, &nonce_len, test->nonce, test_num, "nonce") ||
+      !decode_hex(&ciphertext, &ciphertext_len, test->ciphertext, test_num,
+                  "ciphertext") ||
+      !decode_hex(&tag, &tag_len, test->tag, test_num, "tag")) {
+    goto out;
+  }
+
+  if (plaintext_len != ciphertext_len) {
+    fprintf(stderr, "%u: plaintext and ciphertext have differing lengths.\n",
+            test_num);
+    goto out;
+  }
+
+  if (key_len != 16 && key_len != 24 && key_len != 32) {
+    fprintf(stderr, "%u: bad key length.\n", test_num);
+    goto out;
+  }
+
+  if (tag_len != 16) {
+    fprintf(stderr, "%u: bad tag length.\n", test_num);
+    goto out;
+  }
+
+  out = OPENSSL_malloc(plaintext_len);
+  if (AES_set_encrypt_key(key, key_len*8, &aes_key)) {
+    fprintf(stderr, "%u: AES_set_encrypt_key failed.\n", test_num);
+    goto out;
+  }
+
+  CRYPTO_gcm128_init(&ctx, &aes_key, (block128_f) AES_encrypt);
+  CRYPTO_gcm128_setiv(&ctx, nonce, nonce_len);
+  memset(out, 0, plaintext_len);
+  if (additional_data) {
+    CRYPTO_gcm128_aad(&ctx, additional_data, additional_data_len);
+  }
+  if (plaintext) {
+    CRYPTO_gcm128_encrypt(&ctx, plaintext, out, plaintext_len);
+  }
+  if (!CRYPTO_gcm128_finish(&ctx, tag, tag_len) ||
+      (ciphertext && memcmp(out, ciphertext, plaintext_len) != 0)) {
+    fprintf(stderr, "%u: encrypt failed.\n", test_num);
+    hexdump("got ", out, plaintext_len);
+    hexdump("want", ciphertext, plaintext_len);
+    goto out;
+  }
+
+  CRYPTO_gcm128_setiv(&ctx, nonce, nonce_len);
+  memset(out, 0, plaintext_len);
+  if (additional_data) {
+    CRYPTO_gcm128_aad(&ctx, additional_data, additional_data_len);
+  }
+  if (ciphertext) {
+    CRYPTO_gcm128_decrypt(&ctx, ciphertext, out, plaintext_len);
+  }
+  if (!CRYPTO_gcm128_finish(&ctx, tag, tag_len)) {
+    fprintf(stderr, "%u: decrypt failed.\n", test_num);
+    goto out;
+  }
+  if (plaintext && memcmp(out, plaintext, plaintext_len)) {
+    fprintf(stderr, "%u: plaintext doesn't match.\n", test_num);
+    goto out;
+  }
+
+  ret = 1;
+
+out:
+  if (key) {
+    OPENSSL_free(key);
+  }
+  if (plaintext) {
+    OPENSSL_free(plaintext);
+  }
+  if (additional_data) {
+    OPENSSL_free(additional_data);
+  }
+  if (nonce) {
+    OPENSSL_free(nonce);
+  }
+  if (ciphertext) {
+    OPENSSL_free(ciphertext);
+  }
+  if (tag) {
+    OPENSSL_free(tag);
+  }
+  if (out) {
+    OPENSSL_free(out);
+  }
+  return ret;
+}
+
+int main() {
+  int ret = 0;
+  unsigned i;
+
+  for (i = 0; i < sizeof(test_cases) / sizeof(struct test_case); i++) {
+    if (!run_test_case(i, &test_cases[i])) {
+      ret = 1;
+    }
+  }
+
+  if (ret == 0) {
+    printf("PASS\n");
+  }
+
+  return ret;
+}
diff --git a/crypto/modes/internal.h b/crypto/modes/internal.h
new file mode 100644
index 0000000..4fa0ec6
--- /dev/null
+++ b/crypto/modes/internal.h
@@ -0,0 +1,201 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#ifndef OPENSSL_HEADER_MODES_INTERNAL_H
+#define OPENSSL_HEADER_MODES_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define asm __asm__
+
+#define STRICT_ALIGNMENT 1
+#if defined(OPENSSL_X86_64) || defined(OPENSSL_X86) || defined(OPENSSL_AARCH64)
+#undef STRICT_ALIGNMENT
+#define STRICT_ALIGNMENT 0
+#endif
+
+#if !defined(PEDANTIC) && !defined(OPENSSL_NO_ASM)
+#if defined(__GNUC__) && __GNUC__ >= 2
+#if defined(OPENSSL_X86_64)
+#define BSWAP8(x)                 \
+  ({                              \
+    uint64_t ret = (x);           \
+    asm("bswapq %0" : "+r"(ret)); \
+    ret;                          \
+  })
+#define BSWAP4(x)                 \
+  ({                              \
+    uint32_t ret = (x);           \
+    asm("bswapl %0" : "+r"(ret)); \
+    ret;                          \
+  })
+#elif defined(OPENSSL_X86)
+#define BSWAP8(x)                                     \
+  ({                                                  \
+    uint32_t lo = (uint64_t)(x) >> 32, hi = (x);      \
+    asm("bswapl %0; bswapl %1" : "+r"(hi), "+r"(lo)); \
+    (uint64_t) hi << 32 | lo;                         \
+  })
+#define BSWAP4(x)                 \
+  ({                              \
+    uint32_t ret = (x);           \
+    asm("bswapl %0" : "+r"(ret)); \
+    ret;                          \
+  })
+#elif defined(OPENSSL_AARCH64)
+#define BSWAP8(x)                          \
+  ({                                       \
+    uint64_t ret;                          \
+    asm("rev %0,%1" : "=r"(ret) : "r"(x)); \
+    ret;                                   \
+  })
+#define BSWAP4(x)                            \
+  ({                                         \
+    uint32_t ret;                            \
+    asm("rev %w0,%w1" : "=r"(ret) : "r"(x)); \
+    ret;                                     \
+  })
+#elif defined(OPENSSL_ARM) && !defined(STRICT_ALIGNMENT)
+#define BSWAP8(x)                                     \
+  ({                                                  \
+    uint32_t lo = (uint64_t)(x) >> 32, hi = (x);      \
+    asm("rev %0,%0; rev %1,%1" : "+r"(hi), "+r"(lo)); \
+    (uint64_t) hi << 32 | lo;                         \
+  })
+#define BSWAP4(x)                                      \
+  ({                                                   \
+    uint32_t ret;                                      \
+    asm("rev %0,%1" : "=r"(ret) : "r"((uint32_t)(x))); \
+    ret;                                               \
+  })
+#endif
+#elif defined(_MSC_VER)
+#if _MSC_VER >= 1300
+#pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
+#define BSWAP8(x) _byteswap_uint64((uint64_t)(x))
+#define BSWAP4(x) _byteswap_ulong((uint32_t)(x))
+#elif defined(OPENSSL_X86)
+__inline uint32_t _bswap4(uint32_t val) {
+  _asm mov eax, val
+  _asm bswap eax
+}
+#define BSWAP4(x) _bswap4(x)
+#endif
+#endif
+#endif
+
+#if defined(BSWAP4) && !defined(STRICT_ALIGNMENT)
+#define GETU32(p) BSWAP4(*(const uint32_t *)(p))
+#define PUTU32(p, v) *(uint32_t *)(p) = BSWAP4(v)
+#else
+#define GETU32(p) \
+  ((uint32_t)(p)[0] << 24 | (uint32_t)(p)[1] << 16 | (uint32_t)(p)[2] << 8 | (uint32_t)(p)[3])
+#define PUTU32(p, v)                                   \
+  ((p)[0] = (uint8_t)((v) >> 24), (p)[1] = (uint8_t)((v) >> 16), \
+   (p)[2] = (uint8_t)((v) >> 8), (p)[3] = (uint8_t)(v))
+#endif
+
+
+/* GCM definitions */
+typedef struct { uint64_t hi,lo; } u128;
+
+struct gcm128_context {
+  /* Following 6 names follow names in GCM specification */
+  union {
+    uint64_t u[2];
+    uint32_t d[4];
+    uint8_t c[16];
+    size_t t[16 / sizeof(size_t)];
+  } Yi, EKi, EK0, len, Xi, H;
+
+  /* Relative position of Xi, H and pre-computed Htable is used in some
+   * assembler modules, i.e. don't change the order! */
+  u128 Htable[16];
+  void (*gmult)(uint64_t Xi[2], const u128 Htable[16]);
+  void (*ghash)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
+                size_t len);
+
+  unsigned int mres, ares;
+  block128_f block;
+  void *key;
+};
+
+struct xts128_context {
+  void *key1, *key2;
+  block128_f block1, block2;
+};
+
+struct ccm128_context {
+  union {
+    uint64_t u[2];
+    uint8_t c[16];
+  } nonce, cmac;
+  uint64_t blocks;
+  block128_f block;
+  void *key;
+};
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+#define U64(C) C##UI64
+#elif defined(__arch64__)
+#define U64(C) C##UL
+#else
+#define U64(C) C##ULL
+#endif
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_MODES_INTERNAL_H */
diff --git a/crypto/modes/modes.h b/crypto/modes/modes.h
new file mode 100644
index 0000000..c3a11ba
--- /dev/null
+++ b/crypto/modes/modes.h
@@ -0,0 +1,217 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#ifndef OPENSSL_HEADER_MODES_H
+#define OPENSSL_HEADER_MODES_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* modes.h contains functions that implement various block-cipher modes. */
+
+
+/* block128_f is the type of a 128-bit, block cipher. */
+typedef void (*block128_f)(const uint8_t in[16], uint8_t out[16],
+                           const void *key);
+
+
+/* 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 int *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 int *num, ctr128_f ctr);
+
+
+/* GCM. */
+
+typedef struct gcm128_context GCM128_CONTEXT;
+
+/* CRYPTO_gcm128_new allocates a fresh |GCM128_CONTEXT| and calls
+ * |CRYPTO_gcm128_init|. It returns the new context, or NULL on error. */
+GCM128_CONTEXT *CRYPTO_gcm128_new(void *key, block128_f block);
+
+/* CRYPTO_gcm128_init initialises |ctx| to use |block| (typically AES) with the
+ * given key. */
+void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, void *key, block128_f block);
+
+/* CRYPTO_gcm128_setiv sets the IV (nonce) for |ctx|. */
+void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const uint8_t *iv, size_t len);
+
+/* CRYPTO_gcm128_aad sets the authenticated data for an instance of GCM. This
+ * must be called before and data is encrypted. It returns one on success and
+ * zero otherwise. */
+int CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx, const uint8_t *aad, size_t len);
+
+/* CRYPTO_gcm128_encrypt encrypts |len| bytes from |in| to |out|. It returns
+ * one on success and zero otherwise. */
+int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const uint8_t *in, uint8_t *out,
+                          size_t len);
+
+/* CRYPTO_gcm128_decrypt decrypts |len| bytes from |in| to |out|. It returns
+ * one on success and zero otherwise. */
+int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, 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
+ * |CRYPTO_ctr128_encrypt_ctr32|. It returns one on success and zero
+ * otherwise. */
+int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const uint8_t *in,
+                                uint8_t *out, size_t len, ctr128_f stream);
+
+/* CRYPTO_gcm128_decrypt_ctr32 decrypts |len| bytes from |in| to |out| using a
+ * CTR function that only handles the bottom 32 bits of the nonce, like
+ * |CRYPTO_ctr128_encrypt_ctr32|. It returns one on success and zero
+ * otherwise. */
+int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const uint8_t *in,
+                                uint8_t *out, size_t len, ctr128_f stream);
+
+/* CRYPTO_gcm128_finish calculates the authenticator and compares it against
+ * |len| bytes of |tag|. It returns one on success and zero otherwise. */
+int CRYPTO_gcm128_finish(GCM128_CONTEXT *ctx, const uint8_t *tag, size_t len);
+
+/* CRYPTO_gcm128_tag calculates the authenticator and copies it into |tag|. The
+ * minimum of |len| and 16 bytes are copied into |tag|. */
+void CRYPTO_gcm128_tag(GCM128_CONTEXT *ctx, uint8_t *tag, size_t len);
+
+/* CRYPTO_gcm128_release clears and frees |ctx|. */
+void CRYPTO_gcm128_release(GCM128_CONTEXT *ctx);
+
+
+/* CBC. */
+
+/* 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);
+
+/* 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);
+
+/* 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);
+
+
+/* OFB. */
+
+/* CRYPTO_ofb128_encrypt encrypts (or decrypts, it's the same with OFB mode)
+ * |len| bytes from |in| to |out| using |block| in OFB mode. There's no
+ * 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],
+                           int *num, block128_f block);
+
+
+/* CFB. */
+
+/* CRYPTO_cfb128_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
+ * from |in| to |out| using |block| in CFB mode. There's no 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_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const void *key, uint8_t ivec[16], int *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], int *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], int *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],
+                                   block128_f block);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_MODES_H */
diff --git a/crypto/modes/ofb.c b/crypto/modes/ofb.c
new file mode 100644
index 0000000..455c069
--- /dev/null
+++ b/crypto/modes/ofb.c
@@ -0,0 +1,106 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+#include <openssl/modes.h>
+
+#include <assert.h>
+
+#include "internal.h"
+
+
+void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const void *key, uint8_t ivec[16], int *num,
+                           block128_f block) {
+  unsigned int n;
+  size_t l=0;
+
+  assert(in && out && key && ivec && num);
+  assert((16 % sizeof(size_t)) == 0);
+
+  n = *num;
+
+  while (n && len) {
+    *(out++) = *(in++) ^ ivec[n];
+    --len;
+    n = (n + 1) % 16;
+  }
+
+  if (STRICT_ALIGNMENT &&
+      ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+    while (l < len) {
+      if (n == 0) {
+        (*block)(ivec, ivec, key);
+      }
+      out[l] = in[l] ^ ivec[n];
+      ++l;
+      n = (n + 1) % 16;
+    }
+
+    *num = n;
+    return;
+  }
+
+  while (len >= 16) {
+    (*block)(ivec, ivec, key);
+    for (; n < 16; n += sizeof(size_t)) {
+      *(size_t *)(out + n) = *(size_t *)(in + n) ^ *(size_t *)(ivec + n);
+    }
+    len -= 16;
+    out += 16;
+    in += 16;
+    n = 0;
+  }
+  if (len) {
+    (*block)(ivec, ivec, key);
+    while (len--) {
+      out[n] = in[n] ^ ivec[n];
+      ++n;
+    }
+  }
+  *num = n;
+}
diff --git a/crypto/obj/CMakeLists.txt b/crypto/obj/CMakeLists.txt
new file mode 100644
index 0000000..d812b37
--- /dev/null
+++ b/crypto/obj/CMakeLists.txt
@@ -0,0 +1,11 @@
+include_directories(. .. ../../include)
+
+add_library(
+	obj
+
+	OBJECT
+
+	obj.c
+	obj_xref.c
+	obj_error.c
+)
diff --git a/crypto/obj/obj.c b/crypto/obj/obj.c
new file mode 100644
index 0000000..970346d
--- /dev/null
+++ b/crypto/obj/obj.c
@@ -0,0 +1,658 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/obj.h>
+
+#include <limits.h>
+
+#include <openssl/asn1.h>
+#include <openssl/buf.h>
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/thread.h>
+
+#include "obj_dat.h"
+
+/* These globals are protected by CRYPTO_LOCK_OBJ. */
+static LHASH_OF(ASN1_OBJECT) *global_added_by_data = NULL;
+static LHASH_OF(ASN1_OBJECT) *global_added_by_nid = NULL;
+static LHASH_OF(ASN1_OBJECT) *global_added_by_short_name = NULL;
+static LHASH_OF(ASN1_OBJECT) *global_added_by_long_name = NULL;
+
+static unsigned global_next_nid = NUM_NID;
+
+static int obj_next_nid() {
+  int ret;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_OBJ);
+  ret = global_next_nid++;
+  CRYPTO_w_unlock(CRYPTO_LOCK_OBJ);
+
+  return ret;
+}
+
+ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *o) {
+  ASN1_OBJECT *r;
+  unsigned char *data = NULL;
+  char *sn = NULL, *ln = NULL;
+
+  if (o == NULL) {
+    return NULL;
+  }
+
+  if (!(o->flags & ASN1_OBJECT_FLAG_DYNAMIC)) {
+    /* TODO(fork): this is a little dangerous. */
+    return (ASN1_OBJECT *)o;
+  }
+
+  r = ASN1_OBJECT_new();
+  if (r == NULL) {
+    OPENSSL_PUT_ERROR(OBJ, OBJ_dup, ERR_R_ASN1_LIB);
+    return NULL;
+  }
+  r->ln = r->sn = NULL;
+
+  data = OPENSSL_malloc(o->length);
+  if (data == NULL) {
+    goto err;
+  }
+  if (o->data != NULL) {
+    memcpy(data, o->data, o->length);
+  }
+
+  /* once data is attached to an object, it remains const */
+  r->data = data;
+  r->length = o->length;
+  r->nid = o->nid;
+
+  if (o->ln != NULL) {
+    ln = OPENSSL_strdup(o->ln);
+    if (ln == NULL) {
+      goto err;
+    }
+  }
+
+  if (o->sn != NULL) {
+    sn = OPENSSL_strdup(o->sn);
+    if (sn) {
+      goto err;
+    }
+  }
+
+  r->sn = sn;
+  r->ln = ln;
+
+  r->flags =
+      o->flags | (ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
+                  ASN1_OBJECT_FLAG_DYNAMIC_DATA);
+  return r;
+
+err:
+  OPENSSL_PUT_ERROR(OBJ, OBJ_dup, ERR_R_MALLOC_FAILURE);
+  if (ln != NULL) {
+    OPENSSL_free(ln);
+  }
+  if (sn != NULL) {
+    OPENSSL_free(sn);
+  }
+  if (data != NULL) {
+    OPENSSL_free(data);
+  }
+  OPENSSL_free(r);
+  return NULL;
+}
+
+int OBJ_cmp(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
+  int ret;
+
+  ret = a->length - b->length;
+  if (ret) {
+    return ret;
+  }
+  return memcmp(a->data, b->data, a->length);
+}
+
+/* nids_cmp is called to search the kNIDsInOIDOrder array. The |key| argument
+ * is an |ASN1_OBJECT|* that we're looking for and |element| is a pointer to an
+ * unsigned int in the array. */
+static int obj_cmp(const void *key, const void *element) {
+  int j;
+  unsigned nid = *((unsigned*) element);
+  const ASN1_OBJECT *a = key;
+  const ASN1_OBJECT *b = &kObjects[nid];
+
+  j = a->length - b->length;
+  if (j) {
+    return j;
+  }
+  return memcmp(a->data, b->data, a->length);
+}
+
+int OBJ_obj2nid(const ASN1_OBJECT *obj) {
+  const unsigned int *nid_ptr;
+
+  if (obj == NULL) {
+    return NID_undef;
+  }
+
+  if (obj->nid != 0) {
+    return obj->nid;
+  }
+
+  CRYPTO_r_lock(CRYPTO_LOCK_OBJ);
+  if (global_added_by_data != NULL) {
+    ASN1_OBJECT *match;
+
+    match = lh_ASN1_OBJECT_retrieve(global_added_by_data, obj);
+    if (match != NULL) {
+      CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+      return match->nid;
+    }
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+
+  nid_ptr = bsearch(obj, kNIDsInOIDOrder, NUM_OBJ, sizeof(unsigned), obj_cmp);
+  if (nid_ptr == NULL) {
+    return NID_undef;
+  }
+
+  return kObjects[*nid_ptr].nid;
+}
+
+int OBJ_cbs2nid(const CBS *cbs) {
+  ASN1_OBJECT obj;
+  memset(&obj, 0, sizeof(obj));
+  obj.data = CBS_data(cbs);
+  obj.length = CBS_len(cbs);
+
+  return OBJ_obj2nid(&obj);
+}
+
+/* short_name_cmp is called to search the kNIDsInShortNameOrder array. The
+ * |key| argument is name that we're looking for and |element| is a pointer to
+ * an unsigned int in the array. */
+static int short_name_cmp(const void *key, const void *element) {
+  const char *name = (const char *) key;
+  unsigned nid = *((unsigned*) element);
+
+  return strcmp(name, kObjects[nid].sn);
+}
+
+int OBJ_sn2nid(const char *short_name) {
+  const unsigned int *nid_ptr;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_OBJ);
+  if (global_added_by_short_name != NULL) {
+    ASN1_OBJECT *match, template;
+
+    template.sn = short_name;
+    match = lh_ASN1_OBJECT_retrieve(global_added_by_short_name, &template);
+    if (match != NULL) {
+      CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+      return match->nid;
+    }
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+
+  nid_ptr = bsearch(short_name, kNIDsInShortNameOrder, NUM_SN, sizeof(unsigned), short_name_cmp);
+  if (nid_ptr == NULL) {
+    return NID_undef;
+  }
+
+  return kObjects[*nid_ptr].nid;
+}
+
+/* long_name_cmp is called to search the kNIDsInLongNameOrder array. The
+ * |key| argument is name that we're looking for and |element| is a pointer to
+ * an unsigned int in the array. */
+static int long_name_cmp(const void *key, const void *element) {
+  const char *name = (const char *) key;
+  unsigned nid = *((unsigned*) element);
+
+  return strcmp(name, kObjects[nid].ln);
+}
+
+int OBJ_ln2nid(const char *long_name) {
+  const unsigned int *nid_ptr;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_OBJ);
+  if (global_added_by_long_name != NULL) {
+    ASN1_OBJECT *match, template;
+
+    template.ln = long_name;
+    match = lh_ASN1_OBJECT_retrieve(global_added_by_long_name, &template);
+    if (match != NULL) {
+      CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+      return match->nid;
+    }
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+
+  nid_ptr = bsearch(long_name, kNIDsInLongNameOrder, NUM_LN, sizeof(unsigned), long_name_cmp);
+  if (nid_ptr == NULL) {
+    return NID_undef;
+  }
+
+  return kObjects[*nid_ptr].nid;
+}
+
+int OBJ_txt2nid(const char *s) {
+  ASN1_OBJECT *obj;
+  int nid;
+
+  obj = OBJ_txt2obj(s, 0 /* search names */);
+  nid = OBJ_obj2nid(obj);
+  ASN1_OBJECT_free(obj);
+  return nid;
+}
+
+const ASN1_OBJECT *OBJ_nid2obj(int nid) {
+  if (nid >= 0 && nid < NUM_NID) {
+    if (nid != NID_undef && kObjects[nid].nid == NID_undef) {
+      goto err;
+    }
+    return &kObjects[nid];
+  }
+
+  CRYPTO_r_lock(CRYPTO_LOCK_OBJ);
+  if (global_added_by_nid != NULL) {
+    ASN1_OBJECT *match, template;
+
+    template.nid = nid;
+    match = lh_ASN1_OBJECT_retrieve(global_added_by_nid, &template);
+    if (match != NULL) {
+      CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+      return match;
+    }
+  }
+  CRYPTO_r_unlock(CRYPTO_LOCK_OBJ);
+
+err:
+  OPENSSL_PUT_ERROR(OBJ, OBJ_nid2obj, OBJ_R_UNKNOWN_NID);
+  return NULL;
+}
+
+const char *OBJ_nid2sn(int nid) {
+  const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
+  if (obj == NULL) {
+    return NULL;
+  }
+
+  return obj->sn;
+}
+
+const char *OBJ_nid2ln(int nid) {
+  const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
+  if (obj == NULL) {
+    return NULL;
+  }
+
+  return obj->ln;
+}
+
+ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
+  int nid = NID_undef;
+  ASN1_OBJECT *op = NULL;
+  unsigned char *buf;
+  unsigned char *p;
+  const unsigned char *bufp;
+  int contents_len, total_len;
+
+  if (!dont_search_names) {
+    nid = OBJ_sn2nid(s);
+    if (nid == NID_undef) {
+      nid = OBJ_ln2nid(s);
+    }
+
+    if (nid != NID_undef) {
+      return (ASN1_OBJECT*) OBJ_nid2obj(nid);
+    }
+  }
+
+  /* Work out size of content octets */
+  contents_len = a2d_ASN1_OBJECT(NULL, 0, s, -1);
+  if (contents_len <= 0) {
+    return NULL;
+  }
+  /* Work out total size */
+  total_len = ASN1_object_size(0, contents_len, V_ASN1_OBJECT);
+
+  buf = OPENSSL_malloc(total_len);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(OBJ, OBJ_txt2obj, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  p = buf;
+  /* Write out tag+length */
+  ASN1_put_object(&p, 0, contents_len, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
+  /* Write out contents */
+  a2d_ASN1_OBJECT(p, contents_len, s, -1);
+
+  bufp = buf;
+  op = d2i_ASN1_OBJECT(NULL, &bufp, total_len);
+  OPENSSL_free(buf);
+
+  return op;
+}
+
+int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj, int dont_return_name) {
+  int i, n = 0, len, nid, first, use_bn;
+  BIGNUM *bl;
+  unsigned long l;
+  const unsigned char *p;
+  char tbuf[DECIMAL_SIZE(i) + DECIMAL_SIZE(l) + 2];
+
+  if (out && out_len > 0) {
+    out[0] = 0;
+  }
+
+  if (obj == NULL || obj->data == NULL) {
+    return 0;
+  }
+
+  if (!dont_return_name && (nid = OBJ_obj2nid(obj)) != NID_undef) {
+    const char *s;
+    s = OBJ_nid2ln(nid);
+    if (s == NULL) {
+      s = OBJ_nid2sn(nid);
+    }
+    if (s) {
+      if (out) {
+        BUF_strlcpy(out, s, out_len);
+      }
+      return strlen(s);
+    }
+  }
+
+  len = obj->length;
+  p = obj->data;
+
+  first = 1;
+  bl = NULL;
+
+  while (len > 0) {
+    l = 0;
+    use_bn = 0;
+    for (;;) {
+      unsigned char c = *p++;
+      len--;
+      if (len == 0 && (c & 0x80)) {
+        goto err;
+      }
+      if (use_bn) {
+        if (!BN_add_word(bl, c & 0x7f)) {
+          goto err;
+        }
+      } else {
+        l |= c & 0x7f;
+      }
+      if (!(c & 0x80)) {
+        break;
+      }
+      if (!use_bn && (l > (ULONG_MAX >> 7L))) {
+        if (!bl && !(bl = BN_new())) {
+          goto err;
+        }
+        if (!BN_set_word(bl, l)) {
+          goto err;
+        }
+        use_bn = 1;
+      }
+      if (use_bn) {
+        if (!BN_lshift(bl, bl, 7)) {
+          goto err;
+        }
+      } else {
+        l <<= 7L;
+      }
+    }
+
+    if (first) {
+      first = 0;
+      if (l >= 80) {
+        i = 2;
+        if (use_bn) {
+          if (!BN_sub_word(bl, 80)) {
+            goto err;
+          }
+        } else {
+          l -= 80;
+        }
+      } else {
+        i = (int)(l / 40);
+        l -= (long)(i * 40);
+      }
+      if (out && out_len > 1) {
+        *out++ = i + '0';
+        *out = '0';
+        out_len--;
+      }
+      n++;
+    }
+
+    if (use_bn) {
+      char *bndec;
+      bndec = BN_bn2dec(bl);
+      if (!bndec) {
+        goto err;
+      }
+      i = strlen(bndec);
+      if (out) {
+        if (out_len > 1) {
+          *out++ = '.';
+          *out = 0;
+          out_len--;
+        }
+        BUF_strlcpy(out, bndec, out_len);
+        if (i > out_len) {
+          out += out_len;
+          out_len = 0;
+        } else {
+          out += i;
+          out_len -= i;
+        }
+      }
+      n++;
+      n += i;
+      OPENSSL_free(bndec);
+    } else {
+      BIO_snprintf(tbuf, sizeof(tbuf), ".%lu", l);
+      i = strlen(tbuf);
+      if (out && out_len > 0) {
+        BUF_strlcpy(out, tbuf, out_len);
+        if (i > out_len) {
+          out += out_len;
+          out_len = 0;
+        } else {
+          out += i;
+          out_len -= i;
+        }
+      }
+      n += i;
+    }
+  }
+
+  if (bl) {
+    BN_free(bl);
+  }
+  return n;
+
+err:
+  if (bl) {
+    BN_free(bl);
+  }
+  return -1;
+}
+
+static uint32_t hash_nid(const ASN1_OBJECT *obj) {
+  return obj->nid;
+}
+
+static int cmp_nid(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
+  return a->nid - b->nid;
+}
+
+static uint32_t hash_data(const ASN1_OBJECT *obj) {
+  return OPENSSL_hash32(obj->data, obj->length);
+}
+
+static int cmp_data(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
+  int i = a->length - b->length;
+  if (i) {
+    return i;
+  }
+  return memcmp(a->data, b->data, a->length);
+}
+
+static uint32_t hash_short_name(const ASN1_OBJECT *obj) {
+  return lh_strhash(obj->sn);
+}
+
+static int cmp_short_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
+  return strcmp(a->sn, b->sn);
+}
+
+static uint32_t hash_long_name(const ASN1_OBJECT *obj) {
+  return lh_strhash(obj->ln);
+}
+
+static int cmp_long_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
+  return strcmp(a->ln, b->ln);
+}
+
+/* obj_add_object inserts |obj| into the various global hashes for run-time
+ * added objects. It returns one on success or zero otherwise. */
+static int obj_add_object(ASN1_OBJECT *obj) {
+  int ok;
+  ASN1_OBJECT *old_object;
+
+  obj->flags &= ~(ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
+                  ASN1_OBJECT_FLAG_DYNAMIC_DATA);
+
+  CRYPTO_w_lock(CRYPTO_LOCK_OBJ);
+  if (global_added_by_nid == NULL) {
+    global_added_by_nid = lh_ASN1_OBJECT_new(hash_nid, cmp_nid);
+    global_added_by_data = lh_ASN1_OBJECT_new(hash_data, cmp_data);
+    global_added_by_short_name = lh_ASN1_OBJECT_new(hash_short_name, cmp_short_name);
+    global_added_by_long_name = lh_ASN1_OBJECT_new(hash_long_name, cmp_long_name);
+  }
+
+  /* We don't pay attention to |old_object| (which contains any previous object
+   * that was evicted from the hashes) because we don't have a reference count
+   * on ASN1_OBJECT values. Also, we should never have duplicates nids and so
+   * should always have objects in |global_added_by_nid|. */
+
+  ok = lh_ASN1_OBJECT_insert(global_added_by_nid, &old_object, obj);
+  if (obj->length != 0 && obj->data != NULL) {
+    ok &= lh_ASN1_OBJECT_insert(global_added_by_data, &old_object, obj);
+  }
+  if (obj->sn != NULL) {
+    ok &= lh_ASN1_OBJECT_insert(global_added_by_short_name, &old_object, obj);
+  }
+  if (obj->ln != NULL) {
+    ok &= lh_ASN1_OBJECT_insert(global_added_by_long_name, &old_object, obj);
+  }
+  CRYPTO_w_unlock(CRYPTO_LOCK_OBJ);
+
+  return ok;
+}
+
+int OBJ_create(const char *oid, const char *short_name, const char *long_name) {
+  int ret = NID_undef;
+  ASN1_OBJECT *op = NULL;
+  unsigned char *buf = NULL;
+  int len;
+
+  len = a2d_ASN1_OBJECT(NULL, 0, oid, -1);
+  if (len <= 0) {
+    goto err;
+  }
+
+  buf = OPENSSL_malloc(len);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(OBJ, OBJ_create, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  len = a2d_ASN1_OBJECT(buf, len, oid, -1);
+  if (len == 0) {
+    goto err;
+  }
+
+  op = (ASN1_OBJECT *)ASN1_OBJECT_create(obj_next_nid(), buf, len, short_name,
+                                         long_name);
+  if (op == NULL) {
+    goto err;
+  }
+
+  if (obj_add_object(op)) {
+    ret = op->nid;
+  }
+  op = NULL;
+
+err:
+  if (op != NULL) {
+    ASN1_OBJECT_free(op);
+  }
+  if (buf != NULL) {
+    OPENSSL_free(buf);
+  }
+
+  return ret;
+}
diff --git a/crypto/obj/obj.h b/crypto/obj/obj.h
new file mode 100644
index 0000000..7c9772a
--- /dev/null
+++ b/crypto/obj/obj.h
@@ -0,0 +1,193 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_OBJECTS_H
+#define OPENSSL_HEADER_OBJECTS_H
+
+#include <openssl/base.h>
+
+#include <openssl/obj_mac.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* The objects library deals with the registration and indexing of ASN.1 object
+ * identifiers. These values are often written as a dotted sequence of numbers,
+ * e.g. 1.2.840.113549.1.9.16.3.9.
+ *
+ * Internally, OpenSSL likes to deal with these values by numbering them with
+ * numbers called "nids". OpenSSL has a large, built-in database of common
+ * object identifiers and also has both short and long names for them.
+ *
+ * This library provides functions for translating between object identifiers,
+ * nids, short names and long names.
+ *
+ * The nid values should not be used outside of a single process: they are not
+ * stable identifiers. */
+
+
+/* Basic operations. */
+
+/* OBJ_dup returns a duplicate copy of |obj| or NULL on allocation failure. */
+ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *obj);
+
+/* OBJ_cmp returns a value less than, equal to or greater than zero if |a| is
+ * less than, equal to or greater than |b|, respectively. */
+int OBJ_cmp(const ASN1_OBJECT *a, const ASN1_OBJECT *b);
+
+
+/* Looking up nids. */
+
+/* OBJ_obj2nid returns the nid corresponding to |obj|, or |NID_undef| if no
+ * such object is known. */
+int OBJ_obj2nid(const ASN1_OBJECT *obj);
+
+/* OBJ_cbs2nid returns the nid corresponding to the DER data in |cbs|, or
+ * |NID_undef| if no such object is known. */
+int OBJ_cbs2nid(const CBS *cbs);
+
+/* OBJ_sn2nid returns the nid corresponding to |short_name|, or |NID_undef| if
+ * no such short name is known. */
+int OBJ_sn2nid(const char *short_name);
+
+/* OBJ_ln2nid returns the nid corresponding to |long_name|, or |NID_undef| if
+ * no such long name is known. */
+int OBJ_ln2nid(const char *long_name);
+
+/* OBJ_txt2nid returns the nid corresponding to |s|, which may be a short name,
+ * long name, or an ASCII string containing a dotted sequence of numbers. It
+ * returns the nid or NID_undef if unknown. */
+int OBJ_txt2nid(const char *s);
+
+
+/* Getting information about nids. */
+
+/* OBJ_nid2obj returns the ASN1_OBJECT corresponding to |nid|, or NULL if |nid|
+ * is unknown. */
+const ASN1_OBJECT *OBJ_nid2obj(int nid);
+
+/* OBJ_nid2sn returns the short name for |nid|, or NULL if |nid| is unknown. */
+const char *OBJ_nid2sn(int nid);
+
+/* OBJ_nid2sn returns the long name for |nid|, or NULL if |nid| is unknown. */
+const char *OBJ_nid2ln(int nid);
+
+
+/* Dealing with textual representations of object identifiers. */
+
+/* OBJ_txt2obj returns an ASN1_OBJECT for the textual respresentation in |s|.
+ * If |dont_search_names| is zero, then |s| will be matched against the long
+ * and short names of a known objects to find a match. Otherwise |s| must
+ * contain an ASCII string with a dotted sequence of numbers. The resulting
+ * object need not be previously known. It returns NULL on error. */
+ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names);
+
+/* OBJ_obj2txt converts |obj| to a textual representation. If
+ * |dont_return_name| is zero then |obj| will be matched against known objects
+ * and the long (preferably) or short name will be used if found. Otherwise
+ * |obj| will be converted into a dotted sequence of integers. If |out| is not
+ * NULL, then at most |out_len| bytes of the textual form will be written
+ * there. If |out_len| is at least one, then string written to |out| will
+ * always be NUL terminated. It returns the number of characters that could
+ * have been written, not including the final NUL, or -1 on error. */
+int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
+                int dont_return_name);
+
+
+/* Adding objects at runtime. */
+
+/* OBJ_create adds a known object and returns the nid of the new object, or
+ * NID_undef on error. */
+int OBJ_create(const char *oid, const char *short_name, const char *long_name);
+
+
+/* Handling signature algorithm identifiers.
+ *
+ * Some NIDs (e.g. sha256WithRSAEncryption) specify both a digest algorithm and
+ * a public key algorithm. The following functions map between pairs of digest
+ * and public-key algorithms and the NIDs that specify their combination.
+ *
+ * Sometimes the combination NID leaves the digest unspecified (e.g.
+ * rsassaPss). In these cases, the digest NID is |NID_undef|. */
+
+/* OBJ_find_sigid_algs finds the digest and public-key NIDs that correspond to
+ * the signing algorithm |sign_nid|. If successful, it sets |*out_digest_nid|
+ * and |*out_pkey_nid| and returns one. Otherwise it returns zero. Any of
+ * |out_digest_nid| or |out_pkey_nid| can be NULL if the caller doesn't need
+ * that output value. */
+int OBJ_find_sigid_algs(int sign_nid, int *out_digest_nid, int *out_pkey_nid);
+
+/* OBJ_find_sigid_by_algs finds the signature NID that corresponds to the
+ * combination of |digest_nid| and |pkey_nid|. If success, it sets
+ * |*out_sign_nid| and returns one. Otherwise it returns zero. The
+ * |out_sign_nid| argument can be NULL if the caller only wishes to learn
+ * whether the combination is valid. */
+int OBJ_find_sigid_by_algs(int *out_sign_nid, int digest_nid, int pkey_nid);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define OBJ_F_OBJ_txt2obj 100
+#define OBJ_F_OBJ_create 101
+#define OBJ_F_OBJ_dup 102
+#define OBJ_F_OBJ_nid2obj 103
+#define OBJ_R_UNKNOWN_NID 100
+
+#endif  /* OPENSSL_HEADER_OBJECTS_H */
diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h
new file mode 100644
index 0000000..3863f5d
--- /dev/null
+++ b/crypto/obj/obj_dat.h
@@ -0,0 +1,5275 @@
+/* crypto/objects/obj_dat.h */
+
+/* THIS FILE IS GENERATED FROM objects.h by obj_dat.pl via the
+ * following command:
+ * perl obj_dat.pl obj_mac.h obj_dat.h
+ */
+
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#define NUM_NID 951
+#define NUM_SN 941
+#define NUM_LN 941
+#define NUM_OBJ 883
+
+static const unsigned char lvalues[6188]={
+0x00,                                        /* [  0] OBJ_undef */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,               /* [  1] OBJ_rsadsi */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,          /* [  7] OBJ_pkcs */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x02,     /* [ 14] OBJ_md2 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x05,     /* [ 22] OBJ_md5 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x04,     /* [ 30] OBJ_rc4 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,/* [ 38] OBJ_rsaEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x02,/* [ 47] OBJ_md2WithRSAEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,/* [ 56] OBJ_md5WithRSAEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x01,/* [ 65] OBJ_pbeWithMD2AndDES_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x03,/* [ 74] OBJ_pbeWithMD5AndDES_CBC */
+0x55,                                        /* [ 83] OBJ_X500 */
+0x55,0x04,                                   /* [ 84] OBJ_X509 */
+0x55,0x04,0x03,                              /* [ 86] OBJ_commonName */
+0x55,0x04,0x06,                              /* [ 89] OBJ_countryName */
+0x55,0x04,0x07,                              /* [ 92] OBJ_localityName */
+0x55,0x04,0x08,                              /* [ 95] OBJ_stateOrProvinceName */
+0x55,0x04,0x0A,                              /* [ 98] OBJ_organizationName */
+0x55,0x04,0x0B,                              /* [101] OBJ_organizationalUnitName */
+0x55,0x08,0x01,0x01,                         /* [104] OBJ_rsa */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,     /* [108] OBJ_pkcs7 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x01,/* [116] OBJ_pkcs7_data */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x02,/* [125] OBJ_pkcs7_signed */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x03,/* [134] OBJ_pkcs7_enveloped */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x04,/* [143] OBJ_pkcs7_signedAndEnveloped */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x05,/* [152] OBJ_pkcs7_digest */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x06,/* [161] OBJ_pkcs7_encrypted */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x03,     /* [170] OBJ_pkcs3 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x03,0x01,/* [178] OBJ_dhKeyAgreement */
+0x2B,0x0E,0x03,0x02,0x06,                    /* [187] OBJ_des_ecb */
+0x2B,0x0E,0x03,0x02,0x09,                    /* [192] OBJ_des_cfb64 */
+0x2B,0x0E,0x03,0x02,0x07,                    /* [197] OBJ_des_cbc */
+0x2B,0x0E,0x03,0x02,0x11,                    /* [202] OBJ_des_ede_ecb */
+0x2B,0x06,0x01,0x04,0x01,0x81,0x3C,0x07,0x01,0x01,0x02,/* [207] OBJ_idea_cbc */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x02,     /* [218] OBJ_rc2_cbc */
+0x2B,0x0E,0x03,0x02,0x12,                    /* [226] OBJ_sha */
+0x2B,0x0E,0x03,0x02,0x0F,                    /* [231] OBJ_shaWithRSAEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x07,     /* [236] OBJ_des_ede3_cbc */
+0x2B,0x0E,0x03,0x02,0x08,                    /* [244] OBJ_des_ofb64 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,     /* [249] OBJ_pkcs9 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x01,/* [257] OBJ_pkcs9_emailAddress */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x02,/* [266] OBJ_pkcs9_unstructuredName */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x03,/* [275] OBJ_pkcs9_contentType */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x04,/* [284] OBJ_pkcs9_messageDigest */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x05,/* [293] OBJ_pkcs9_signingTime */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x06,/* [302] OBJ_pkcs9_countersignature */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x07,/* [311] OBJ_pkcs9_challengePassword */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x08,/* [320] OBJ_pkcs9_unstructuredAddress */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x09,/* [329] OBJ_pkcs9_extCertAttributes */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,          /* [338] OBJ_netscape */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,     /* [345] OBJ_netscape_cert_extension */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x02,     /* [353] OBJ_netscape_data_type */
+0x2B,0x0E,0x03,0x02,0x1A,                    /* [361] OBJ_sha1 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,/* [366] OBJ_sha1WithRSAEncryption */
+0x2B,0x0E,0x03,0x02,0x0D,                    /* [375] OBJ_dsaWithSHA */
+0x2B,0x0E,0x03,0x02,0x0C,                    /* [380] OBJ_dsa_2 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0B,/* [385] OBJ_pbeWithSHA1AndRC2_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0C,/* [394] OBJ_id_pbkdf2 */
+0x2B,0x0E,0x03,0x02,0x1B,                    /* [403] OBJ_dsaWithSHA1_2 */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,/* [408] OBJ_netscape_cert_type */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x02,/* [417] OBJ_netscape_base_url */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x03,/* [426] OBJ_netscape_revocation_url */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x04,/* [435] OBJ_netscape_ca_revocation_url */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x07,/* [444] OBJ_netscape_renewal_url */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x08,/* [453] OBJ_netscape_ca_policy_url */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x0C,/* [462] OBJ_netscape_ssl_server_name */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x0D,/* [471] OBJ_netscape_comment */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x02,0x05,/* [480] OBJ_netscape_cert_sequence */
+0x55,0x1D,                                   /* [489] OBJ_id_ce */
+0x55,0x1D,0x0E,                              /* [491] OBJ_subject_key_identifier */
+0x55,0x1D,0x0F,                              /* [494] OBJ_key_usage */
+0x55,0x1D,0x10,                              /* [497] OBJ_private_key_usage_period */
+0x55,0x1D,0x11,                              /* [500] OBJ_subject_alt_name */
+0x55,0x1D,0x12,                              /* [503] OBJ_issuer_alt_name */
+0x55,0x1D,0x13,                              /* [506] OBJ_basic_constraints */
+0x55,0x1D,0x14,                              /* [509] OBJ_crl_number */
+0x55,0x1D,0x20,                              /* [512] OBJ_certificate_policies */
+0x55,0x1D,0x23,                              /* [515] OBJ_authority_key_identifier */
+0x2B,0x06,0x01,0x04,0x01,0x97,0x55,0x01,0x02,/* [518] OBJ_bf_cbc */
+0x55,0x08,0x03,0x65,                         /* [527] OBJ_mdc2 */
+0x55,0x08,0x03,0x64,                         /* [531] OBJ_mdc2WithRSA */
+0x55,0x04,0x2A,                              /* [535] OBJ_givenName */
+0x55,0x04,0x04,                              /* [538] OBJ_surname */
+0x55,0x04,0x2B,                              /* [541] OBJ_initials */
+0x55,0x1D,0x1F,                              /* [544] OBJ_crl_distribution_points */
+0x2B,0x0E,0x03,0x02,0x03,                    /* [547] OBJ_md5WithRSA */
+0x55,0x04,0x05,                              /* [552] OBJ_serialNumber */
+0x55,0x04,0x0C,                              /* [555] OBJ_title */
+0x55,0x04,0x0D,                              /* [558] OBJ_description */
+0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x0A,/* [561] OBJ_cast5_cbc */
+0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x0C,/* [570] OBJ_pbeWithMD5AndCast5_CBC */
+0x2A,0x86,0x48,0xCE,0x38,0x04,0x03,          /* [579] OBJ_dsaWithSHA1 */
+0x2B,0x0E,0x03,0x02,0x1D,                    /* [586] OBJ_sha1WithRSA */
+0x2A,0x86,0x48,0xCE,0x38,0x04,0x01,          /* [591] OBJ_dsa */
+0x2B,0x24,0x03,0x02,0x01,                    /* [598] OBJ_ripemd160 */
+0x2B,0x24,0x03,0x03,0x01,0x02,               /* [603] OBJ_ripemd160WithRSA */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x08,     /* [609] OBJ_rc5_cbc */
+0x29,0x01,0x01,0x85,0x1A,0x01,               /* [617] OBJ_rle_compression */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x08,/* [623] OBJ_zlib_compression */
+0x55,0x1D,0x25,                              /* [634] OBJ_ext_key_usage */
+0x2B,0x06,0x01,0x05,0x05,0x07,               /* [637] OBJ_id_pkix */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,          /* [643] OBJ_id_kp */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,     /* [650] OBJ_server_auth */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x02,     /* [658] OBJ_client_auth */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x03,     /* [666] OBJ_code_sign */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x04,     /* [674] OBJ_email_protect */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x08,     /* [682] OBJ_time_stamp */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x15,/* [690] OBJ_ms_code_ind */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x16,/* [700] OBJ_ms_code_com */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0A,0x03,0x01,/* [710] OBJ_ms_ctl_sign */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0A,0x03,0x03,/* [720] OBJ_ms_sgc */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0A,0x03,0x04,/* [730] OBJ_ms_efs */
+0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x04,0x01,/* [740] OBJ_ns_sgc */
+0x55,0x1D,0x1B,                              /* [749] OBJ_delta_crl */
+0x55,0x1D,0x15,                              /* [752] OBJ_crl_reason */
+0x55,0x1D,0x18,                              /* [755] OBJ_invalidity_date */
+0x2B,0x65,0x01,0x04,0x01,                    /* [758] OBJ_sxnet */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x01,/* [763] OBJ_pbe_WithSHA1And128BitRC4 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x02,/* [773] OBJ_pbe_WithSHA1And40BitRC4 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x03,/* [783] OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x04,/* [793] OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x05,/* [803] OBJ_pbe_WithSHA1And128BitRC2_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x06,/* [813] OBJ_pbe_WithSHA1And40BitRC2_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x01,/* [823] OBJ_keyBag */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x02,/* [834] OBJ_pkcs8ShroudedKeyBag */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x03,/* [845] OBJ_certBag */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x04,/* [856] OBJ_crlBag */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x05,/* [867] OBJ_secretBag */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x06,/* [878] OBJ_safeContentsBag */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x14,/* [889] OBJ_friendlyName */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x15,/* [898] OBJ_localKeyID */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x16,0x01,/* [907] OBJ_x509Certificate */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x16,0x02,/* [917] OBJ_sdsiCertificate */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x17,0x01,/* [927] OBJ_x509Crl */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0D,/* [937] OBJ_pbes2 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0E,/* [946] OBJ_pbmac1 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x07,     /* [955] OBJ_hmacWithSHA1 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,     /* [963] OBJ_id_qt_cps */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x02,     /* [971] OBJ_id_qt_unotice */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x0F,/* [979] OBJ_SMIMECapabilities */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x04,/* [988] OBJ_pbeWithMD2AndRC2_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x06,/* [997] OBJ_pbeWithMD5AndRC2_CBC */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0A,/* [1006] OBJ_pbeWithSHA1AndDES_CBC */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0E,/* [1015] OBJ_ms_ext_req */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x0E,/* [1025] OBJ_ext_req */
+0x55,0x04,0x29,                              /* [1034] OBJ_name */
+0x55,0x04,0x2E,                              /* [1037] OBJ_dnQualifier */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,          /* [1040] OBJ_id_pe */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,          /* [1047] OBJ_id_ad */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x01,     /* [1054] OBJ_info_access */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,     /* [1062] OBJ_ad_OCSP */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x02,     /* [1070] OBJ_ad_ca_issuers */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x09,     /* [1078] OBJ_OCSP_sign */
+0x28,                                        /* [1086] OBJ_iso */
+0x2A,                                        /* [1087] OBJ_member_body */
+0x2A,0x86,0x48,                              /* [1088] OBJ_ISO_US */
+0x2A,0x86,0x48,0xCE,0x38,                    /* [1091] OBJ_X9_57 */
+0x2A,0x86,0x48,0xCE,0x38,0x04,               /* [1096] OBJ_X9cm */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,     /* [1102] OBJ_pkcs1 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,     /* [1110] OBJ_pkcs5 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,/* [1118] OBJ_SMIME */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,/* [1127] OBJ_id_smime_mod */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,/* [1137] OBJ_id_smime_ct */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,/* [1147] OBJ_id_smime_aa */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,/* [1157] OBJ_id_smime_alg */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x04,/* [1167] OBJ_id_smime_cd */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x05,/* [1177] OBJ_id_smime_spq */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,/* [1187] OBJ_id_smime_cti */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x01,/* [1197] OBJ_id_smime_mod_cms */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x02,/* [1208] OBJ_id_smime_mod_ess */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x03,/* [1219] OBJ_id_smime_mod_oid */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x04,/* [1230] OBJ_id_smime_mod_msg_v3 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x05,/* [1241] OBJ_id_smime_mod_ets_eSignature_88 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x06,/* [1252] OBJ_id_smime_mod_ets_eSignature_97 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x07,/* [1263] OBJ_id_smime_mod_ets_eSigPolicy_88 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x08,/* [1274] OBJ_id_smime_mod_ets_eSigPolicy_97 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x01,/* [1285] OBJ_id_smime_ct_receipt */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x02,/* [1296] OBJ_id_smime_ct_authData */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x03,/* [1307] OBJ_id_smime_ct_publishCert */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x04,/* [1318] OBJ_id_smime_ct_TSTInfo */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x05,/* [1329] OBJ_id_smime_ct_TDTInfo */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x06,/* [1340] OBJ_id_smime_ct_contentInfo */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x07,/* [1351] OBJ_id_smime_ct_DVCSRequestData */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x08,/* [1362] OBJ_id_smime_ct_DVCSResponseData */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x01,/* [1373] OBJ_id_smime_aa_receiptRequest */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x02,/* [1384] OBJ_id_smime_aa_securityLabel */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x03,/* [1395] OBJ_id_smime_aa_mlExpandHistory */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x04,/* [1406] OBJ_id_smime_aa_contentHint */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x05,/* [1417] OBJ_id_smime_aa_msgSigDigest */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x06,/* [1428] OBJ_id_smime_aa_encapContentType */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x07,/* [1439] OBJ_id_smime_aa_contentIdentifier */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x08,/* [1450] OBJ_id_smime_aa_macValue */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x09,/* [1461] OBJ_id_smime_aa_equivalentLabels */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0A,/* [1472] OBJ_id_smime_aa_contentReference */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0B,/* [1483] OBJ_id_smime_aa_encrypKeyPref */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0C,/* [1494] OBJ_id_smime_aa_signingCertificate */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0D,/* [1505] OBJ_id_smime_aa_smimeEncryptCerts */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0E,/* [1516] OBJ_id_smime_aa_timeStampToken */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0F,/* [1527] OBJ_id_smime_aa_ets_sigPolicyId */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x10,/* [1538] OBJ_id_smime_aa_ets_commitmentType */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x11,/* [1549] OBJ_id_smime_aa_ets_signerLocation */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x12,/* [1560] OBJ_id_smime_aa_ets_signerAttr */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x13,/* [1571] OBJ_id_smime_aa_ets_otherSigCert */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x14,/* [1582] OBJ_id_smime_aa_ets_contentTimestamp */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x15,/* [1593] OBJ_id_smime_aa_ets_CertificateRefs */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x16,/* [1604] OBJ_id_smime_aa_ets_RevocationRefs */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x17,/* [1615] OBJ_id_smime_aa_ets_certValues */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x18,/* [1626] OBJ_id_smime_aa_ets_revocationValues */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x19,/* [1637] OBJ_id_smime_aa_ets_escTimeStamp */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1A,/* [1648] OBJ_id_smime_aa_ets_certCRLTimestamp */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1B,/* [1659] OBJ_id_smime_aa_ets_archiveTimeStamp */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1C,/* [1670] OBJ_id_smime_aa_signatureType */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1D,/* [1681] OBJ_id_smime_aa_dvcs_dvc */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x01,/* [1692] OBJ_id_smime_alg_ESDHwith3DES */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x02,/* [1703] OBJ_id_smime_alg_ESDHwithRC2 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x03,/* [1714] OBJ_id_smime_alg_3DESwrap */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x04,/* [1725] OBJ_id_smime_alg_RC2wrap */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x05,/* [1736] OBJ_id_smime_alg_ESDH */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x06,/* [1747] OBJ_id_smime_alg_CMS3DESwrap */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x07,/* [1758] OBJ_id_smime_alg_CMSRC2wrap */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x04,0x01,/* [1769] OBJ_id_smime_cd_ldap */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x05,0x01,/* [1780] OBJ_id_smime_spq_ets_sqt_uri */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x05,0x02,/* [1791] OBJ_id_smime_spq_ets_sqt_unotice */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x01,/* [1802] OBJ_id_smime_cti_ets_proofOfOrigin */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x02,/* [1813] OBJ_id_smime_cti_ets_proofOfReceipt */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x03,/* [1824] OBJ_id_smime_cti_ets_proofOfDelivery */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x04,/* [1835] OBJ_id_smime_cti_ets_proofOfSender */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x05,/* [1846] OBJ_id_smime_cti_ets_proofOfApproval */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x06,/* [1857] OBJ_id_smime_cti_ets_proofOfCreation */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x04,     /* [1868] OBJ_md4 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,          /* [1876] OBJ_id_pkix_mod */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x02,          /* [1883] OBJ_id_qt */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,          /* [1890] OBJ_id_it */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,          /* [1897] OBJ_id_pkip */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x06,          /* [1904] OBJ_id_alg */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,          /* [1911] OBJ_id_cmc */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x08,          /* [1918] OBJ_id_on */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x09,          /* [1925] OBJ_id_pda */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,          /* [1932] OBJ_id_aca */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0B,          /* [1939] OBJ_id_qcs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,          /* [1946] OBJ_id_cct */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x01,     /* [1953] OBJ_id_pkix1_explicit_88 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x02,     /* [1961] OBJ_id_pkix1_implicit_88 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x03,     /* [1969] OBJ_id_pkix1_explicit_93 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x04,     /* [1977] OBJ_id_pkix1_implicit_93 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x05,     /* [1985] OBJ_id_mod_crmf */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x06,     /* [1993] OBJ_id_mod_cmc */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x07,     /* [2001] OBJ_id_mod_kea_profile_88 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x08,     /* [2009] OBJ_id_mod_kea_profile_93 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x09,     /* [2017] OBJ_id_mod_cmp */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0A,     /* [2025] OBJ_id_mod_qualified_cert_88 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0B,     /* [2033] OBJ_id_mod_qualified_cert_93 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0C,     /* [2041] OBJ_id_mod_attribute_cert */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0D,     /* [2049] OBJ_id_mod_timestamp_protocol */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0E,     /* [2057] OBJ_id_mod_ocsp */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0F,     /* [2065] OBJ_id_mod_dvcs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x10,     /* [2073] OBJ_id_mod_cmp2000 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x02,     /* [2081] OBJ_biometricInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x03,     /* [2089] OBJ_qcStatements */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x04,     /* [2097] OBJ_ac_auditEntity */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x05,     /* [2105] OBJ_ac_targeting */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x06,     /* [2113] OBJ_aaControls */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x07,     /* [2121] OBJ_sbgp_ipAddrBlock */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x08,     /* [2129] OBJ_sbgp_autonomousSysNum */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x09,     /* [2137] OBJ_sbgp_routerIdentifier */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x03,     /* [2145] OBJ_textNotice */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x05,     /* [2153] OBJ_ipsecEndSystem */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x06,     /* [2161] OBJ_ipsecTunnel */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x07,     /* [2169] OBJ_ipsecUser */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x0A,     /* [2177] OBJ_dvcs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x01,     /* [2185] OBJ_id_it_caProtEncCert */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x02,     /* [2193] OBJ_id_it_signKeyPairTypes */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x03,     /* [2201] OBJ_id_it_encKeyPairTypes */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x04,     /* [2209] OBJ_id_it_preferredSymmAlg */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x05,     /* [2217] OBJ_id_it_caKeyUpdateInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x06,     /* [2225] OBJ_id_it_currentCRL */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x07,     /* [2233] OBJ_id_it_unsupportedOIDs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x08,     /* [2241] OBJ_id_it_subscriptionRequest */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x09,     /* [2249] OBJ_id_it_subscriptionResponse */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0A,     /* [2257] OBJ_id_it_keyPairParamReq */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0B,     /* [2265] OBJ_id_it_keyPairParamRep */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0C,     /* [2273] OBJ_id_it_revPassphrase */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0D,     /* [2281] OBJ_id_it_implicitConfirm */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0E,     /* [2289] OBJ_id_it_confirmWaitTime */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0F,     /* [2297] OBJ_id_it_origPKIMessage */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,     /* [2305] OBJ_id_regCtrl */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x02,     /* [2313] OBJ_id_regInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x01,/* [2321] OBJ_id_regCtrl_regToken */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x02,/* [2330] OBJ_id_regCtrl_authenticator */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x03,/* [2339] OBJ_id_regCtrl_pkiPublicationInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x04,/* [2348] OBJ_id_regCtrl_pkiArchiveOptions */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x05,/* [2357] OBJ_id_regCtrl_oldCertID */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x06,/* [2366] OBJ_id_regCtrl_protocolEncrKey */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x02,0x01,/* [2375] OBJ_id_regInfo_utf8Pairs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x02,0x02,/* [2384] OBJ_id_regInfo_certReq */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x01,     /* [2393] OBJ_id_alg_des40 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x02,     /* [2401] OBJ_id_alg_noSignature */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x03,     /* [2409] OBJ_id_alg_dh_sig_hmac_sha1 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x04,     /* [2417] OBJ_id_alg_dh_pop */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x01,     /* [2425] OBJ_id_cmc_statusInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x02,     /* [2433] OBJ_id_cmc_identification */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x03,     /* [2441] OBJ_id_cmc_identityProof */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x04,     /* [2449] OBJ_id_cmc_dataReturn */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x05,     /* [2457] OBJ_id_cmc_transactionId */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x06,     /* [2465] OBJ_id_cmc_senderNonce */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x07,     /* [2473] OBJ_id_cmc_recipientNonce */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x08,     /* [2481] OBJ_id_cmc_addExtensions */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x09,     /* [2489] OBJ_id_cmc_encryptedPOP */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x0A,     /* [2497] OBJ_id_cmc_decryptedPOP */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x0B,     /* [2505] OBJ_id_cmc_lraPOPWitness */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x0F,     /* [2513] OBJ_id_cmc_getCert */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x10,     /* [2521] OBJ_id_cmc_getCRL */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x11,     /* [2529] OBJ_id_cmc_revokeRequest */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x12,     /* [2537] OBJ_id_cmc_regInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x13,     /* [2545] OBJ_id_cmc_responseInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x15,     /* [2553] OBJ_id_cmc_queryPending */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x16,     /* [2561] OBJ_id_cmc_popLinkRandom */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x17,     /* [2569] OBJ_id_cmc_popLinkWitness */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x18,     /* [2577] OBJ_id_cmc_confirmCertAcceptance */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x08,0x01,     /* [2585] OBJ_id_on_personalData */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x01,     /* [2593] OBJ_id_pda_dateOfBirth */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x02,     /* [2601] OBJ_id_pda_placeOfBirth */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x03,     /* [2609] OBJ_id_pda_gender */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x04,     /* [2617] OBJ_id_pda_countryOfCitizenship */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x05,     /* [2625] OBJ_id_pda_countryOfResidence */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x01,     /* [2633] OBJ_id_aca_authenticationInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x02,     /* [2641] OBJ_id_aca_accessIdentity */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x03,     /* [2649] OBJ_id_aca_chargingIdentity */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x04,     /* [2657] OBJ_id_aca_group */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x05,     /* [2665] OBJ_id_aca_role */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0B,0x01,     /* [2673] OBJ_id_qcs_pkixQCSyntax_v1 */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,0x01,     /* [2681] OBJ_id_cct_crs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,0x02,     /* [2689] OBJ_id_cct_PKIData */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,0x03,     /* [2697] OBJ_id_cct_PKIResponse */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x03,     /* [2705] OBJ_ad_timeStamping */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x04,     /* [2713] OBJ_ad_dvcs */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x01,/* [2721] OBJ_id_pkix_OCSP_basic */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x02,/* [2730] OBJ_id_pkix_OCSP_Nonce */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x03,/* [2739] OBJ_id_pkix_OCSP_CrlID */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x04,/* [2748] OBJ_id_pkix_OCSP_acceptableResponses */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x05,/* [2757] OBJ_id_pkix_OCSP_noCheck */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x06,/* [2766] OBJ_id_pkix_OCSP_archiveCutoff */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x07,/* [2775] OBJ_id_pkix_OCSP_serviceLocator */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x08,/* [2784] OBJ_id_pkix_OCSP_extendedStatus */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x09,/* [2793] OBJ_id_pkix_OCSP_valid */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x0A,/* [2802] OBJ_id_pkix_OCSP_path */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x0B,/* [2811] OBJ_id_pkix_OCSP_trustRoot */
+0x2B,0x0E,0x03,0x02,                         /* [2820] OBJ_algorithm */
+0x2B,0x0E,0x03,0x02,0x0B,                    /* [2824] OBJ_rsaSignature */
+0x55,0x08,                                   /* [2829] OBJ_X500algorithms */
+0x2B,                                        /* [2831] OBJ_org */
+0x2B,0x06,                                   /* [2832] OBJ_dod */
+0x2B,0x06,0x01,                              /* [2834] OBJ_iana */
+0x2B,0x06,0x01,0x01,                         /* [2837] OBJ_Directory */
+0x2B,0x06,0x01,0x02,                         /* [2841] OBJ_Management */
+0x2B,0x06,0x01,0x03,                         /* [2845] OBJ_Experimental */
+0x2B,0x06,0x01,0x04,                         /* [2849] OBJ_Private */
+0x2B,0x06,0x01,0x05,                         /* [2853] OBJ_Security */
+0x2B,0x06,0x01,0x06,                         /* [2857] OBJ_SNMPv2 */
+0x2B,0x06,0x01,0x07,                         /* [2861] OBJ_Mail */
+0x2B,0x06,0x01,0x04,0x01,                    /* [2865] OBJ_Enterprises */
+0x2B,0x06,0x01,0x04,0x01,0x8B,0x3A,0x82,0x58,/* [2870] OBJ_dcObject */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x19,/* [2879] OBJ_domainComponent */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x0D,/* [2889] OBJ_Domain */
+0x00,                                        /* [2899] OBJ_joint_iso_ccitt */
+0x55,0x01,0x05,                              /* [2900] OBJ_selected_attribute_types */
+0x55,0x01,0x05,0x37,                         /* [2903] OBJ_clearance */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x03,/* [2907] OBJ_md4WithRSAEncryption */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0A,     /* [2916] OBJ_ac_proxying */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0B,     /* [2924] OBJ_sinfo_access */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x06,     /* [2932] OBJ_id_aca_encAttrs */
+0x55,0x04,0x48,                              /* [2940] OBJ_role */
+0x55,0x1D,0x24,                              /* [2943] OBJ_policy_constraints */
+0x55,0x1D,0x37,                              /* [2946] OBJ_target_information */
+0x55,0x1D,0x38,                              /* [2949] OBJ_no_rev_avail */
+0x00,                                        /* [2952] OBJ_ccitt */
+0x2A,0x86,0x48,0xCE,0x3D,                    /* [2953] OBJ_ansi_X9_62 */
+0x2A,0x86,0x48,0xCE,0x3D,0x01,0x01,          /* [2958] OBJ_X9_62_prime_field */
+0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,          /* [2965] OBJ_X9_62_characteristic_two_field */
+0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,          /* [2972] OBJ_X9_62_id_ecPublicKey */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x01,     /* [2979] OBJ_X9_62_prime192v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x02,     /* [2987] OBJ_X9_62_prime192v2 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x03,     /* [2995] OBJ_X9_62_prime192v3 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x04,     /* [3003] OBJ_X9_62_prime239v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x05,     /* [3011] OBJ_X9_62_prime239v2 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x06,     /* [3019] OBJ_X9_62_prime239v3 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07,     /* [3027] OBJ_X9_62_prime256v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x01,          /* [3035] OBJ_ecdsa_with_SHA1 */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x11,0x01,/* [3042] OBJ_ms_csp_name */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x01,/* [3051] OBJ_aes_128_ecb */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x02,/* [3060] OBJ_aes_128_cbc */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x03,/* [3069] OBJ_aes_128_ofb128 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x04,/* [3078] OBJ_aes_128_cfb128 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x15,/* [3087] OBJ_aes_192_ecb */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x16,/* [3096] OBJ_aes_192_cbc */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x17,/* [3105] OBJ_aes_192_ofb128 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x18,/* [3114] OBJ_aes_192_cfb128 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x29,/* [3123] OBJ_aes_256_ecb */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2A,/* [3132] OBJ_aes_256_cbc */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2B,/* [3141] OBJ_aes_256_ofb128 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2C,/* [3150] OBJ_aes_256_cfb128 */
+0x55,0x1D,0x17,                              /* [3159] OBJ_hold_instruction_code */
+0x2A,0x86,0x48,0xCE,0x38,0x02,0x01,          /* [3162] OBJ_hold_instruction_none */
+0x2A,0x86,0x48,0xCE,0x38,0x02,0x02,          /* [3169] OBJ_hold_instruction_call_issuer */
+0x2A,0x86,0x48,0xCE,0x38,0x02,0x03,          /* [3176] OBJ_hold_instruction_reject */
+0x09,                                        /* [3183] OBJ_data */
+0x09,0x92,0x26,                              /* [3184] OBJ_pss */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,          /* [3187] OBJ_ucl */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,     /* [3194] OBJ_pilot */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,/* [3202] OBJ_pilotAttributeType */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x03,/* [3211] OBJ_pilotAttributeSyntax */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,/* [3220] OBJ_pilotObjectClass */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x0A,/* [3229] OBJ_pilotGroups */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x03,0x04,/* [3238] OBJ_iA5StringSyntax */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x03,0x05,/* [3248] OBJ_caseIgnoreIA5StringSyntax */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x03,/* [3258] OBJ_pilotObject */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x04,/* [3268] OBJ_pilotPerson */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x05,/* [3278] OBJ_account */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x06,/* [3288] OBJ_document */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x07,/* [3298] OBJ_room */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x09,/* [3308] OBJ_documentSeries */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x0E,/* [3318] OBJ_rFC822localPart */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x0F,/* [3328] OBJ_dNSDomain */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x11,/* [3338] OBJ_domainRelatedObject */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x12,/* [3348] OBJ_friendlyCountry */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x13,/* [3358] OBJ_simpleSecurityObject */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x14,/* [3368] OBJ_pilotOrganization */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x15,/* [3378] OBJ_pilotDSA */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x16,/* [3388] OBJ_qualityLabelledData */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01,/* [3398] OBJ_userId */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x02,/* [3408] OBJ_textEncodedORAddress */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x03,/* [3418] OBJ_rfc822Mailbox */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x04,/* [3428] OBJ_info */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x05,/* [3438] OBJ_favouriteDrink */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x06,/* [3448] OBJ_roomNumber */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x07,/* [3458] OBJ_photo */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x08,/* [3468] OBJ_userClass */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x09,/* [3478] OBJ_host */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0A,/* [3488] OBJ_manager */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0B,/* [3498] OBJ_documentIdentifier */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0C,/* [3508] OBJ_documentTitle */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0D,/* [3518] OBJ_documentVersion */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0E,/* [3528] OBJ_documentAuthor */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0F,/* [3538] OBJ_documentLocation */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x14,/* [3548] OBJ_homeTelephoneNumber */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x15,/* [3558] OBJ_secretary */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x16,/* [3568] OBJ_otherMailbox */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x17,/* [3578] OBJ_lastModifiedTime */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x18,/* [3588] OBJ_lastModifiedBy */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1A,/* [3598] OBJ_aRecord */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1B,/* [3608] OBJ_pilotAttributeType27 */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1C,/* [3618] OBJ_mXRecord */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1D,/* [3628] OBJ_nSRecord */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1E,/* [3638] OBJ_sOARecord */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1F,/* [3648] OBJ_cNAMERecord */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x25,/* [3658] OBJ_associatedDomain */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x26,/* [3668] OBJ_associatedName */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x27,/* [3678] OBJ_homePostalAddress */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x28,/* [3688] OBJ_personalTitle */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x29,/* [3698] OBJ_mobileTelephoneNumber */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2A,/* [3708] OBJ_pagerTelephoneNumber */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2B,/* [3718] OBJ_friendlyCountryName */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2D,/* [3728] OBJ_organizationalStatus */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2E,/* [3738] OBJ_janetMailbox */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2F,/* [3748] OBJ_mailPreferenceOption */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x30,/* [3758] OBJ_buildingName */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x31,/* [3768] OBJ_dSAQuality */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x32,/* [3778] OBJ_singleLevelQuality */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x33,/* [3788] OBJ_subtreeMinimumQuality */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x34,/* [3798] OBJ_subtreeMaximumQuality */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x35,/* [3808] OBJ_personalSignature */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x36,/* [3818] OBJ_dITRedirect */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x37,/* [3828] OBJ_audio */
+0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x38,/* [3838] OBJ_documentPublisher */
+0x55,0x04,0x2D,                              /* [3848] OBJ_x500UniqueIdentifier */
+0x2B,0x06,0x01,0x07,0x01,                    /* [3851] OBJ_mime_mhs */
+0x2B,0x06,0x01,0x07,0x01,0x01,               /* [3856] OBJ_mime_mhs_headings */
+0x2B,0x06,0x01,0x07,0x01,0x02,               /* [3862] OBJ_mime_mhs_bodies */
+0x2B,0x06,0x01,0x07,0x01,0x01,0x01,          /* [3868] OBJ_id_hex_partial_message */
+0x2B,0x06,0x01,0x07,0x01,0x01,0x02,          /* [3875] OBJ_id_hex_multipart_message */
+0x55,0x04,0x2C,                              /* [3882] OBJ_generationQualifier */
+0x55,0x04,0x41,                              /* [3885] OBJ_pseudonym */
+0x67,0x2A,                                   /* [3888] OBJ_id_set */
+0x67,0x2A,0x00,                              /* [3890] OBJ_set_ctype */
+0x67,0x2A,0x01,                              /* [3893] OBJ_set_msgExt */
+0x67,0x2A,0x03,                              /* [3896] OBJ_set_attr */
+0x67,0x2A,0x05,                              /* [3899] OBJ_set_policy */
+0x67,0x2A,0x07,                              /* [3902] OBJ_set_certExt */
+0x67,0x2A,0x08,                              /* [3905] OBJ_set_brand */
+0x67,0x2A,0x00,0x00,                         /* [3908] OBJ_setct_PANData */
+0x67,0x2A,0x00,0x01,                         /* [3912] OBJ_setct_PANToken */
+0x67,0x2A,0x00,0x02,                         /* [3916] OBJ_setct_PANOnly */
+0x67,0x2A,0x00,0x03,                         /* [3920] OBJ_setct_OIData */
+0x67,0x2A,0x00,0x04,                         /* [3924] OBJ_setct_PI */
+0x67,0x2A,0x00,0x05,                         /* [3928] OBJ_setct_PIData */
+0x67,0x2A,0x00,0x06,                         /* [3932] OBJ_setct_PIDataUnsigned */
+0x67,0x2A,0x00,0x07,                         /* [3936] OBJ_setct_HODInput */
+0x67,0x2A,0x00,0x08,                         /* [3940] OBJ_setct_AuthResBaggage */
+0x67,0x2A,0x00,0x09,                         /* [3944] OBJ_setct_AuthRevReqBaggage */
+0x67,0x2A,0x00,0x0A,                         /* [3948] OBJ_setct_AuthRevResBaggage */
+0x67,0x2A,0x00,0x0B,                         /* [3952] OBJ_setct_CapTokenSeq */
+0x67,0x2A,0x00,0x0C,                         /* [3956] OBJ_setct_PInitResData */
+0x67,0x2A,0x00,0x0D,                         /* [3960] OBJ_setct_PI_TBS */
+0x67,0x2A,0x00,0x0E,                         /* [3964] OBJ_setct_PResData */
+0x67,0x2A,0x00,0x10,                         /* [3968] OBJ_setct_AuthReqTBS */
+0x67,0x2A,0x00,0x11,                         /* [3972] OBJ_setct_AuthResTBS */
+0x67,0x2A,0x00,0x12,                         /* [3976] OBJ_setct_AuthResTBSX */
+0x67,0x2A,0x00,0x13,                         /* [3980] OBJ_setct_AuthTokenTBS */
+0x67,0x2A,0x00,0x14,                         /* [3984] OBJ_setct_CapTokenData */
+0x67,0x2A,0x00,0x15,                         /* [3988] OBJ_setct_CapTokenTBS */
+0x67,0x2A,0x00,0x16,                         /* [3992] OBJ_setct_AcqCardCodeMsg */
+0x67,0x2A,0x00,0x17,                         /* [3996] OBJ_setct_AuthRevReqTBS */
+0x67,0x2A,0x00,0x18,                         /* [4000] OBJ_setct_AuthRevResData */
+0x67,0x2A,0x00,0x19,                         /* [4004] OBJ_setct_AuthRevResTBS */
+0x67,0x2A,0x00,0x1A,                         /* [4008] OBJ_setct_CapReqTBS */
+0x67,0x2A,0x00,0x1B,                         /* [4012] OBJ_setct_CapReqTBSX */
+0x67,0x2A,0x00,0x1C,                         /* [4016] OBJ_setct_CapResData */
+0x67,0x2A,0x00,0x1D,                         /* [4020] OBJ_setct_CapRevReqTBS */
+0x67,0x2A,0x00,0x1E,                         /* [4024] OBJ_setct_CapRevReqTBSX */
+0x67,0x2A,0x00,0x1F,                         /* [4028] OBJ_setct_CapRevResData */
+0x67,0x2A,0x00,0x20,                         /* [4032] OBJ_setct_CredReqTBS */
+0x67,0x2A,0x00,0x21,                         /* [4036] OBJ_setct_CredReqTBSX */
+0x67,0x2A,0x00,0x22,                         /* [4040] OBJ_setct_CredResData */
+0x67,0x2A,0x00,0x23,                         /* [4044] OBJ_setct_CredRevReqTBS */
+0x67,0x2A,0x00,0x24,                         /* [4048] OBJ_setct_CredRevReqTBSX */
+0x67,0x2A,0x00,0x25,                         /* [4052] OBJ_setct_CredRevResData */
+0x67,0x2A,0x00,0x26,                         /* [4056] OBJ_setct_PCertReqData */
+0x67,0x2A,0x00,0x27,                         /* [4060] OBJ_setct_PCertResTBS */
+0x67,0x2A,0x00,0x28,                         /* [4064] OBJ_setct_BatchAdminReqData */
+0x67,0x2A,0x00,0x29,                         /* [4068] OBJ_setct_BatchAdminResData */
+0x67,0x2A,0x00,0x2A,                         /* [4072] OBJ_setct_CardCInitResTBS */
+0x67,0x2A,0x00,0x2B,                         /* [4076] OBJ_setct_MeAqCInitResTBS */
+0x67,0x2A,0x00,0x2C,                         /* [4080] OBJ_setct_RegFormResTBS */
+0x67,0x2A,0x00,0x2D,                         /* [4084] OBJ_setct_CertReqData */
+0x67,0x2A,0x00,0x2E,                         /* [4088] OBJ_setct_CertReqTBS */
+0x67,0x2A,0x00,0x2F,                         /* [4092] OBJ_setct_CertResData */
+0x67,0x2A,0x00,0x30,                         /* [4096] OBJ_setct_CertInqReqTBS */
+0x67,0x2A,0x00,0x31,                         /* [4100] OBJ_setct_ErrorTBS */
+0x67,0x2A,0x00,0x32,                         /* [4104] OBJ_setct_PIDualSignedTBE */
+0x67,0x2A,0x00,0x33,                         /* [4108] OBJ_setct_PIUnsignedTBE */
+0x67,0x2A,0x00,0x34,                         /* [4112] OBJ_setct_AuthReqTBE */
+0x67,0x2A,0x00,0x35,                         /* [4116] OBJ_setct_AuthResTBE */
+0x67,0x2A,0x00,0x36,                         /* [4120] OBJ_setct_AuthResTBEX */
+0x67,0x2A,0x00,0x37,                         /* [4124] OBJ_setct_AuthTokenTBE */
+0x67,0x2A,0x00,0x38,                         /* [4128] OBJ_setct_CapTokenTBE */
+0x67,0x2A,0x00,0x39,                         /* [4132] OBJ_setct_CapTokenTBEX */
+0x67,0x2A,0x00,0x3A,                         /* [4136] OBJ_setct_AcqCardCodeMsgTBE */
+0x67,0x2A,0x00,0x3B,                         /* [4140] OBJ_setct_AuthRevReqTBE */
+0x67,0x2A,0x00,0x3C,                         /* [4144] OBJ_setct_AuthRevResTBE */
+0x67,0x2A,0x00,0x3D,                         /* [4148] OBJ_setct_AuthRevResTBEB */
+0x67,0x2A,0x00,0x3E,                         /* [4152] OBJ_setct_CapReqTBE */
+0x67,0x2A,0x00,0x3F,                         /* [4156] OBJ_setct_CapReqTBEX */
+0x67,0x2A,0x00,0x40,                         /* [4160] OBJ_setct_CapResTBE */
+0x67,0x2A,0x00,0x41,                         /* [4164] OBJ_setct_CapRevReqTBE */
+0x67,0x2A,0x00,0x42,                         /* [4168] OBJ_setct_CapRevReqTBEX */
+0x67,0x2A,0x00,0x43,                         /* [4172] OBJ_setct_CapRevResTBE */
+0x67,0x2A,0x00,0x44,                         /* [4176] OBJ_setct_CredReqTBE */
+0x67,0x2A,0x00,0x45,                         /* [4180] OBJ_setct_CredReqTBEX */
+0x67,0x2A,0x00,0x46,                         /* [4184] OBJ_setct_CredResTBE */
+0x67,0x2A,0x00,0x47,                         /* [4188] OBJ_setct_CredRevReqTBE */
+0x67,0x2A,0x00,0x48,                         /* [4192] OBJ_setct_CredRevReqTBEX */
+0x67,0x2A,0x00,0x49,                         /* [4196] OBJ_setct_CredRevResTBE */
+0x67,0x2A,0x00,0x4A,                         /* [4200] OBJ_setct_BatchAdminReqTBE */
+0x67,0x2A,0x00,0x4B,                         /* [4204] OBJ_setct_BatchAdminResTBE */
+0x67,0x2A,0x00,0x4C,                         /* [4208] OBJ_setct_RegFormReqTBE */
+0x67,0x2A,0x00,0x4D,                         /* [4212] OBJ_setct_CertReqTBE */
+0x67,0x2A,0x00,0x4E,                         /* [4216] OBJ_setct_CertReqTBEX */
+0x67,0x2A,0x00,0x4F,                         /* [4220] OBJ_setct_CertResTBE */
+0x67,0x2A,0x00,0x50,                         /* [4224] OBJ_setct_CRLNotificationTBS */
+0x67,0x2A,0x00,0x51,                         /* [4228] OBJ_setct_CRLNotificationResTBS */
+0x67,0x2A,0x00,0x52,                         /* [4232] OBJ_setct_BCIDistributionTBS */
+0x67,0x2A,0x01,0x01,                         /* [4236] OBJ_setext_genCrypt */
+0x67,0x2A,0x01,0x03,                         /* [4240] OBJ_setext_miAuth */
+0x67,0x2A,0x01,0x04,                         /* [4244] OBJ_setext_pinSecure */
+0x67,0x2A,0x01,0x05,                         /* [4248] OBJ_setext_pinAny */
+0x67,0x2A,0x01,0x07,                         /* [4252] OBJ_setext_track2 */
+0x67,0x2A,0x01,0x08,                         /* [4256] OBJ_setext_cv */
+0x67,0x2A,0x05,0x00,                         /* [4260] OBJ_set_policy_root */
+0x67,0x2A,0x07,0x00,                         /* [4264] OBJ_setCext_hashedRoot */
+0x67,0x2A,0x07,0x01,                         /* [4268] OBJ_setCext_certType */
+0x67,0x2A,0x07,0x02,                         /* [4272] OBJ_setCext_merchData */
+0x67,0x2A,0x07,0x03,                         /* [4276] OBJ_setCext_cCertRequired */
+0x67,0x2A,0x07,0x04,                         /* [4280] OBJ_setCext_tunneling */
+0x67,0x2A,0x07,0x05,                         /* [4284] OBJ_setCext_setExt */
+0x67,0x2A,0x07,0x06,                         /* [4288] OBJ_setCext_setQualf */
+0x67,0x2A,0x07,0x07,                         /* [4292] OBJ_setCext_PGWYcapabilities */
+0x67,0x2A,0x07,0x08,                         /* [4296] OBJ_setCext_TokenIdentifier */
+0x67,0x2A,0x07,0x09,                         /* [4300] OBJ_setCext_Track2Data */
+0x67,0x2A,0x07,0x0A,                         /* [4304] OBJ_setCext_TokenType */
+0x67,0x2A,0x07,0x0B,                         /* [4308] OBJ_setCext_IssuerCapabilities */
+0x67,0x2A,0x03,0x00,                         /* [4312] OBJ_setAttr_Cert */
+0x67,0x2A,0x03,0x01,                         /* [4316] OBJ_setAttr_PGWYcap */
+0x67,0x2A,0x03,0x02,                         /* [4320] OBJ_setAttr_TokenType */
+0x67,0x2A,0x03,0x03,                         /* [4324] OBJ_setAttr_IssCap */
+0x67,0x2A,0x03,0x00,0x00,                    /* [4328] OBJ_set_rootKeyThumb */
+0x67,0x2A,0x03,0x00,0x01,                    /* [4333] OBJ_set_addPolicy */
+0x67,0x2A,0x03,0x02,0x01,                    /* [4338] OBJ_setAttr_Token_EMV */
+0x67,0x2A,0x03,0x02,0x02,                    /* [4343] OBJ_setAttr_Token_B0Prime */
+0x67,0x2A,0x03,0x03,0x03,                    /* [4348] OBJ_setAttr_IssCap_CVM */
+0x67,0x2A,0x03,0x03,0x04,                    /* [4353] OBJ_setAttr_IssCap_T2 */
+0x67,0x2A,0x03,0x03,0x05,                    /* [4358] OBJ_setAttr_IssCap_Sig */
+0x67,0x2A,0x03,0x03,0x03,0x01,               /* [4363] OBJ_setAttr_GenCryptgrm */
+0x67,0x2A,0x03,0x03,0x04,0x01,               /* [4369] OBJ_setAttr_T2Enc */
+0x67,0x2A,0x03,0x03,0x04,0x02,               /* [4375] OBJ_setAttr_T2cleartxt */
+0x67,0x2A,0x03,0x03,0x05,0x01,               /* [4381] OBJ_setAttr_TokICCsig */
+0x67,0x2A,0x03,0x03,0x05,0x02,               /* [4387] OBJ_setAttr_SecDevSig */
+0x67,0x2A,0x08,0x01,                         /* [4393] OBJ_set_brand_IATA_ATA */
+0x67,0x2A,0x08,0x1E,                         /* [4397] OBJ_set_brand_Diners */
+0x67,0x2A,0x08,0x22,                         /* [4401] OBJ_set_brand_AmericanExpress */
+0x67,0x2A,0x08,0x23,                         /* [4405] OBJ_set_brand_JCB */
+0x67,0x2A,0x08,0x04,                         /* [4409] OBJ_set_brand_Visa */
+0x67,0x2A,0x08,0x05,                         /* [4413] OBJ_set_brand_MasterCard */
+0x67,0x2A,0x08,0xAE,0x7B,                    /* [4417] OBJ_set_brand_Novus */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x0A,     /* [4422] OBJ_des_cdmf */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x06,/* [4430] OBJ_rsaOAEPEncryptionSET */
+0x00,                                        /* [4439] OBJ_itu_t */
+0x50,                                        /* [4440] OBJ_joint_iso_itu_t */
+0x67,                                        /* [4441] OBJ_international_organizations */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x14,0x02,0x02,/* [4442] OBJ_ms_smartcard_login */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x14,0x02,0x03,/* [4452] OBJ_ms_upn */
+0x55,0x04,0x09,                              /* [4462] OBJ_streetAddress */
+0x55,0x04,0x11,                              /* [4465] OBJ_postalCode */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x15,          /* [4468] OBJ_id_ppl */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0E,     /* [4475] OBJ_proxyCertInfo */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x15,0x00,     /* [4483] OBJ_id_ppl_anyLanguage */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x15,0x01,     /* [4491] OBJ_id_ppl_inheritAll */
+0x55,0x1D,0x1E,                              /* [4499] OBJ_name_constraints */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x15,0x02,     /* [4502] OBJ_Independent */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,/* [4510] OBJ_sha256WithRSAEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,/* [4519] OBJ_sha384WithRSAEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0D,/* [4528] OBJ_sha512WithRSAEncryption */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0E,/* [4537] OBJ_sha224WithRSAEncryption */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,/* [4546] OBJ_sha256 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,/* [4555] OBJ_sha384 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,/* [4564] OBJ_sha512 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04,/* [4573] OBJ_sha224 */
+0x2B,                                        /* [4582] OBJ_identified_organization */
+0x2B,0x81,0x04,                              /* [4583] OBJ_certicom_arc */
+0x67,0x2B,                                   /* [4586] OBJ_wap */
+0x67,0x2B,0x01,                              /* [4588] OBJ_wap_wsg */
+0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,     /* [4591] OBJ_X9_62_id_characteristic_two_basis */
+0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,0x01,/* [4599] OBJ_X9_62_onBasis */
+0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,0x02,/* [4608] OBJ_X9_62_tpBasis */
+0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,0x03,/* [4617] OBJ_X9_62_ppBasis */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x01,     /* [4626] OBJ_X9_62_c2pnb163v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x02,     /* [4634] OBJ_X9_62_c2pnb163v2 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x03,     /* [4642] OBJ_X9_62_c2pnb163v3 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x04,     /* [4650] OBJ_X9_62_c2pnb176v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x05,     /* [4658] OBJ_X9_62_c2tnb191v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x06,     /* [4666] OBJ_X9_62_c2tnb191v2 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x07,     /* [4674] OBJ_X9_62_c2tnb191v3 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x08,     /* [4682] OBJ_X9_62_c2onb191v4 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x09,     /* [4690] OBJ_X9_62_c2onb191v5 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0A,     /* [4698] OBJ_X9_62_c2pnb208w1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0B,     /* [4706] OBJ_X9_62_c2tnb239v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0C,     /* [4714] OBJ_X9_62_c2tnb239v2 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0D,     /* [4722] OBJ_X9_62_c2tnb239v3 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0E,     /* [4730] OBJ_X9_62_c2onb239v4 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0F,     /* [4738] OBJ_X9_62_c2onb239v5 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x10,     /* [4746] OBJ_X9_62_c2pnb272w1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x11,     /* [4754] OBJ_X9_62_c2pnb304w1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x12,     /* [4762] OBJ_X9_62_c2tnb359v1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x13,     /* [4770] OBJ_X9_62_c2pnb368w1 */
+0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x14,     /* [4778] OBJ_X9_62_c2tnb431r1 */
+0x2B,0x81,0x04,0x00,0x06,                    /* [4786] OBJ_secp112r1 */
+0x2B,0x81,0x04,0x00,0x07,                    /* [4791] OBJ_secp112r2 */
+0x2B,0x81,0x04,0x00,0x1C,                    /* [4796] OBJ_secp128r1 */
+0x2B,0x81,0x04,0x00,0x1D,                    /* [4801] OBJ_secp128r2 */
+0x2B,0x81,0x04,0x00,0x09,                    /* [4806] OBJ_secp160k1 */
+0x2B,0x81,0x04,0x00,0x08,                    /* [4811] OBJ_secp160r1 */
+0x2B,0x81,0x04,0x00,0x1E,                    /* [4816] OBJ_secp160r2 */
+0x2B,0x81,0x04,0x00,0x1F,                    /* [4821] OBJ_secp192k1 */
+0x2B,0x81,0x04,0x00,0x20,                    /* [4826] OBJ_secp224k1 */
+0x2B,0x81,0x04,0x00,0x21,                    /* [4831] OBJ_secp224r1 */
+0x2B,0x81,0x04,0x00,0x0A,                    /* [4836] OBJ_secp256k1 */
+0x2B,0x81,0x04,0x00,0x22,                    /* [4841] OBJ_secp384r1 */
+0x2B,0x81,0x04,0x00,0x23,                    /* [4846] OBJ_secp521r1 */
+0x2B,0x81,0x04,0x00,0x04,                    /* [4851] OBJ_sect113r1 */
+0x2B,0x81,0x04,0x00,0x05,                    /* [4856] OBJ_sect113r2 */
+0x2B,0x81,0x04,0x00,0x16,                    /* [4861] OBJ_sect131r1 */
+0x2B,0x81,0x04,0x00,0x17,                    /* [4866] OBJ_sect131r2 */
+0x2B,0x81,0x04,0x00,0x01,                    /* [4871] OBJ_sect163k1 */
+0x2B,0x81,0x04,0x00,0x02,                    /* [4876] OBJ_sect163r1 */
+0x2B,0x81,0x04,0x00,0x0F,                    /* [4881] OBJ_sect163r2 */
+0x2B,0x81,0x04,0x00,0x18,                    /* [4886] OBJ_sect193r1 */
+0x2B,0x81,0x04,0x00,0x19,                    /* [4891] OBJ_sect193r2 */
+0x2B,0x81,0x04,0x00,0x1A,                    /* [4896] OBJ_sect233k1 */
+0x2B,0x81,0x04,0x00,0x1B,                    /* [4901] OBJ_sect233r1 */
+0x2B,0x81,0x04,0x00,0x03,                    /* [4906] OBJ_sect239k1 */
+0x2B,0x81,0x04,0x00,0x10,                    /* [4911] OBJ_sect283k1 */
+0x2B,0x81,0x04,0x00,0x11,                    /* [4916] OBJ_sect283r1 */
+0x2B,0x81,0x04,0x00,0x24,                    /* [4921] OBJ_sect409k1 */
+0x2B,0x81,0x04,0x00,0x25,                    /* [4926] OBJ_sect409r1 */
+0x2B,0x81,0x04,0x00,0x26,                    /* [4931] OBJ_sect571k1 */
+0x2B,0x81,0x04,0x00,0x27,                    /* [4936] OBJ_sect571r1 */
+0x67,0x2B,0x01,0x04,0x01,                    /* [4941] OBJ_wap_wsg_idm_ecid_wtls1 */
+0x67,0x2B,0x01,0x04,0x03,                    /* [4946] OBJ_wap_wsg_idm_ecid_wtls3 */
+0x67,0x2B,0x01,0x04,0x04,                    /* [4951] OBJ_wap_wsg_idm_ecid_wtls4 */
+0x67,0x2B,0x01,0x04,0x05,                    /* [4956] OBJ_wap_wsg_idm_ecid_wtls5 */
+0x67,0x2B,0x01,0x04,0x06,                    /* [4961] OBJ_wap_wsg_idm_ecid_wtls6 */
+0x67,0x2B,0x01,0x04,0x07,                    /* [4966] OBJ_wap_wsg_idm_ecid_wtls7 */
+0x67,0x2B,0x01,0x04,0x08,                    /* [4971] OBJ_wap_wsg_idm_ecid_wtls8 */
+0x67,0x2B,0x01,0x04,0x09,                    /* [4976] OBJ_wap_wsg_idm_ecid_wtls9 */
+0x67,0x2B,0x01,0x04,0x0A,                    /* [4981] OBJ_wap_wsg_idm_ecid_wtls10 */
+0x67,0x2B,0x01,0x04,0x0B,                    /* [4986] OBJ_wap_wsg_idm_ecid_wtls11 */
+0x67,0x2B,0x01,0x04,0x0C,                    /* [4991] OBJ_wap_wsg_idm_ecid_wtls12 */
+0x55,0x1D,0x20,0x00,                         /* [4996] OBJ_any_policy */
+0x55,0x1D,0x21,                              /* [5000] OBJ_policy_mappings */
+0x55,0x1D,0x36,                              /* [5003] OBJ_inhibit_any_policy */
+0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x01,0x02,/* [5006] OBJ_camellia_128_cbc */
+0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x01,0x03,/* [5017] OBJ_camellia_192_cbc */
+0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x01,0x04,/* [5028] OBJ_camellia_256_cbc */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x01,     /* [5039] OBJ_camellia_128_ecb */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x15,     /* [5047] OBJ_camellia_192_ecb */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x29,     /* [5055] OBJ_camellia_256_ecb */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x04,     /* [5063] OBJ_camellia_128_cfb128 */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x18,     /* [5071] OBJ_camellia_192_cfb128 */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x2C,     /* [5079] OBJ_camellia_256_cfb128 */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x03,     /* [5087] OBJ_camellia_128_ofb128 */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x17,     /* [5095] OBJ_camellia_192_ofb128 */
+0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x2B,     /* [5103] OBJ_camellia_256_ofb128 */
+0x55,0x1D,0x09,                              /* [5111] OBJ_subject_directory_attributes */
+0x55,0x1D,0x1C,                              /* [5114] OBJ_issuing_distribution_point */
+0x55,0x1D,0x1D,                              /* [5117] OBJ_certificate_issuer */
+0x2A,0x83,0x1A,0x8C,0x9A,0x44,               /* [5120] OBJ_kisa */
+0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x03,     /* [5126] OBJ_seed_ecb */
+0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x04,     /* [5134] OBJ_seed_cbc */
+0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x06,     /* [5142] OBJ_seed_ofb128 */
+0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x05,     /* [5150] OBJ_seed_cfb128 */
+0x2B,0x06,0x01,0x05,0x05,0x08,0x01,0x01,     /* [5158] OBJ_hmac_md5 */
+0x2B,0x06,0x01,0x05,0x05,0x08,0x01,0x02,     /* [5166] OBJ_hmac_sha1 */
+0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x0D,/* [5174] OBJ_id_PasswordBasedMAC */
+0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x1E,/* [5183] OBJ_id_DHBasedMac */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x10,     /* [5192] OBJ_id_it_suppLangTags */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x05,     /* [5200] OBJ_caRepository */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x09,/* [5208] OBJ_id_smime_ct_compressedData */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x1B,/* [5219] OBJ_id_ct_asciiTextWithCRLF */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x05,/* [5230] OBJ_id_aes128_wrap */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x19,/* [5239] OBJ_id_aes192_wrap */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2D,/* [5248] OBJ_id_aes256_wrap */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x02,          /* [5257] OBJ_ecdsa_with_Recommended */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,          /* [5264] OBJ_ecdsa_with_Specified */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x01,     /* [5271] OBJ_ecdsa_with_SHA224 */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,     /* [5279] OBJ_ecdsa_with_SHA256 */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,     /* [5287] OBJ_ecdsa_with_SHA384 */
+0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x04,     /* [5295] OBJ_ecdsa_with_SHA512 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x06,     /* [5303] OBJ_hmacWithMD5 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x08,     /* [5311] OBJ_hmacWithSHA224 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x09,     /* [5319] OBJ_hmacWithSHA256 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0A,     /* [5327] OBJ_hmacWithSHA384 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0B,     /* [5335] OBJ_hmacWithSHA512 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x01,/* [5343] OBJ_dsa_with_SHA224 */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x02,/* [5352] OBJ_dsa_with_SHA256 */
+0x28,0xCF,0x06,0x03,0x00,0x37,               /* [5361] OBJ_whirlpool */
+0x2A,0x85,0x03,0x02,0x02,                    /* [5367] OBJ_cryptopro */
+0x2A,0x85,0x03,0x02,0x09,                    /* [5372] OBJ_cryptocom */
+0x2A,0x85,0x03,0x02,0x02,0x03,               /* [5377] OBJ_id_GostR3411_94_with_GostR3410_2001 */
+0x2A,0x85,0x03,0x02,0x02,0x04,               /* [5383] OBJ_id_GostR3411_94_with_GostR3410_94 */
+0x2A,0x85,0x03,0x02,0x02,0x09,               /* [5389] OBJ_id_GostR3411_94 */
+0x2A,0x85,0x03,0x02,0x02,0x0A,               /* [5395] OBJ_id_HMACGostR3411_94 */
+0x2A,0x85,0x03,0x02,0x02,0x13,               /* [5401] OBJ_id_GostR3410_2001 */
+0x2A,0x85,0x03,0x02,0x02,0x14,               /* [5407] OBJ_id_GostR3410_94 */
+0x2A,0x85,0x03,0x02,0x02,0x15,               /* [5413] OBJ_id_Gost28147_89 */
+0x2A,0x85,0x03,0x02,0x02,0x16,               /* [5419] OBJ_id_Gost28147_89_MAC */
+0x2A,0x85,0x03,0x02,0x02,0x17,               /* [5425] OBJ_id_GostR3411_94_prf */
+0x2A,0x85,0x03,0x02,0x02,0x62,               /* [5431] OBJ_id_GostR3410_2001DH */
+0x2A,0x85,0x03,0x02,0x02,0x63,               /* [5437] OBJ_id_GostR3410_94DH */
+0x2A,0x85,0x03,0x02,0x02,0x0E,0x01,          /* [5443] OBJ_id_Gost28147_89_CryptoPro_KeyMeshing */
+0x2A,0x85,0x03,0x02,0x02,0x0E,0x00,          /* [5450] OBJ_id_Gost28147_89_None_KeyMeshing */
+0x2A,0x85,0x03,0x02,0x02,0x1E,0x00,          /* [5457] OBJ_id_GostR3411_94_TestParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1E,0x01,          /* [5464] OBJ_id_GostR3411_94_CryptoProParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x00,          /* [5471] OBJ_id_Gost28147_89_TestParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x01,          /* [5478] OBJ_id_Gost28147_89_CryptoPro_A_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x02,          /* [5485] OBJ_id_Gost28147_89_CryptoPro_B_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x03,          /* [5492] OBJ_id_Gost28147_89_CryptoPro_C_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x04,          /* [5499] OBJ_id_Gost28147_89_CryptoPro_D_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x05,          /* [5506] OBJ_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x06,          /* [5513] OBJ_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x1F,0x07,          /* [5520] OBJ_id_Gost28147_89_CryptoPro_RIC_1_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x20,0x00,          /* [5527] OBJ_id_GostR3410_94_TestParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x20,0x02,          /* [5534] OBJ_id_GostR3410_94_CryptoPro_A_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x20,0x03,          /* [5541] OBJ_id_GostR3410_94_CryptoPro_B_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x20,0x04,          /* [5548] OBJ_id_GostR3410_94_CryptoPro_C_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x20,0x05,          /* [5555] OBJ_id_GostR3410_94_CryptoPro_D_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x21,0x01,          /* [5562] OBJ_id_GostR3410_94_CryptoPro_XchA_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x21,0x02,          /* [5569] OBJ_id_GostR3410_94_CryptoPro_XchB_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x21,0x03,          /* [5576] OBJ_id_GostR3410_94_CryptoPro_XchC_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x23,0x00,          /* [5583] OBJ_id_GostR3410_2001_TestParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x23,0x01,          /* [5590] OBJ_id_GostR3410_2001_CryptoPro_A_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x23,0x02,          /* [5597] OBJ_id_GostR3410_2001_CryptoPro_B_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x23,0x03,          /* [5604] OBJ_id_GostR3410_2001_CryptoPro_C_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x24,0x00,          /* [5611] OBJ_id_GostR3410_2001_CryptoPro_XchA_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x24,0x01,          /* [5618] OBJ_id_GostR3410_2001_CryptoPro_XchB_ParamSet */
+0x2A,0x85,0x03,0x02,0x02,0x14,0x01,          /* [5625] OBJ_id_GostR3410_94_a */
+0x2A,0x85,0x03,0x02,0x02,0x14,0x02,          /* [5632] OBJ_id_GostR3410_94_aBis */
+0x2A,0x85,0x03,0x02,0x02,0x14,0x03,          /* [5639] OBJ_id_GostR3410_94_b */
+0x2A,0x85,0x03,0x02,0x02,0x14,0x04,          /* [5646] OBJ_id_GostR3410_94_bBis */
+0x2A,0x85,0x03,0x02,0x09,0x01,0x06,0x01,     /* [5653] OBJ_id_Gost28147_89_cc */
+0x2A,0x85,0x03,0x02,0x09,0x01,0x05,0x03,     /* [5661] OBJ_id_GostR3410_94_cc */
+0x2A,0x85,0x03,0x02,0x09,0x01,0x05,0x04,     /* [5669] OBJ_id_GostR3410_2001_cc */
+0x2A,0x85,0x03,0x02,0x09,0x01,0x03,0x03,     /* [5677] OBJ_id_GostR3411_94_with_GostR3410_94_cc */
+0x2A,0x85,0x03,0x02,0x09,0x01,0x03,0x04,     /* [5685] OBJ_id_GostR3411_94_with_GostR3410_2001_cc */
+0x2A,0x85,0x03,0x02,0x09,0x01,0x08,0x01,     /* [5693] OBJ_id_GostR3410_2001_ParamSet_cc */
+0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x11,0x02,/* [5701] OBJ_LocalKeySet */
+0x55,0x1D,0x2E,                              /* [5710] OBJ_freshest_crl */
+0x2B,0x06,0x01,0x05,0x05,0x07,0x08,0x03,     /* [5713] OBJ_id_on_permanentIdentifier */
+0x55,0x04,0x0E,                              /* [5721] OBJ_searchGuide */
+0x55,0x04,0x0F,                              /* [5724] OBJ_businessCategory */
+0x55,0x04,0x10,                              /* [5727] OBJ_postalAddress */
+0x55,0x04,0x12,                              /* [5730] OBJ_postOfficeBox */
+0x55,0x04,0x13,                              /* [5733] OBJ_physicalDeliveryOfficeName */
+0x55,0x04,0x14,                              /* [5736] OBJ_telephoneNumber */
+0x55,0x04,0x15,                              /* [5739] OBJ_telexNumber */
+0x55,0x04,0x16,                              /* [5742] OBJ_teletexTerminalIdentifier */
+0x55,0x04,0x17,                              /* [5745] OBJ_facsimileTelephoneNumber */
+0x55,0x04,0x18,                              /* [5748] OBJ_x121Address */
+0x55,0x04,0x19,                              /* [5751] OBJ_internationaliSDNNumber */
+0x55,0x04,0x1A,                              /* [5754] OBJ_registeredAddress */
+0x55,0x04,0x1B,                              /* [5757] OBJ_destinationIndicator */
+0x55,0x04,0x1C,                              /* [5760] OBJ_preferredDeliveryMethod */
+0x55,0x04,0x1D,                              /* [5763] OBJ_presentationAddress */
+0x55,0x04,0x1E,                              /* [5766] OBJ_supportedApplicationContext */
+0x55,0x04,0x1F,                              /* [5769] OBJ_member */
+0x55,0x04,0x20,                              /* [5772] OBJ_owner */
+0x55,0x04,0x21,                              /* [5775] OBJ_roleOccupant */
+0x55,0x04,0x22,                              /* [5778] OBJ_seeAlso */
+0x55,0x04,0x23,                              /* [5781] OBJ_userPassword */
+0x55,0x04,0x24,                              /* [5784] OBJ_userCertificate */
+0x55,0x04,0x25,                              /* [5787] OBJ_cACertificate */
+0x55,0x04,0x26,                              /* [5790] OBJ_authorityRevocationList */
+0x55,0x04,0x27,                              /* [5793] OBJ_certificateRevocationList */
+0x55,0x04,0x28,                              /* [5796] OBJ_crossCertificatePair */
+0x55,0x04,0x2F,                              /* [5799] OBJ_enhancedSearchGuide */
+0x55,0x04,0x30,                              /* [5802] OBJ_protocolInformation */
+0x55,0x04,0x31,                              /* [5805] OBJ_distinguishedName */
+0x55,0x04,0x32,                              /* [5808] OBJ_uniqueMember */
+0x55,0x04,0x33,                              /* [5811] OBJ_houseIdentifier */
+0x55,0x04,0x34,                              /* [5814] OBJ_supportedAlgorithms */
+0x55,0x04,0x35,                              /* [5817] OBJ_deltaRevocationList */
+0x55,0x04,0x36,                              /* [5820] OBJ_dmdName */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x09,/* [5823] OBJ_id_alg_PWRI_KEK */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x06,/* [5834] OBJ_aes_128_gcm */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x07,/* [5843] OBJ_aes_128_ccm */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x08,/* [5852] OBJ_id_aes128_wrap_pad */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x1A,/* [5861] OBJ_aes_192_gcm */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x1B,/* [5870] OBJ_aes_192_ccm */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x1C,/* [5879] OBJ_id_aes192_wrap_pad */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2E,/* [5888] OBJ_aes_256_gcm */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2F,/* [5897] OBJ_aes_256_ccm */
+0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x30,/* [5906] OBJ_id_aes256_wrap_pad */
+0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x03,0x02,/* [5915] OBJ_id_camellia128_wrap */
+0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x03,0x03,/* [5926] OBJ_id_camellia192_wrap */
+0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x03,0x04,/* [5937] OBJ_id_camellia256_wrap */
+0x55,0x1D,0x25,0x00,                         /* [5948] OBJ_anyExtendedKeyUsage */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x08,/* [5952] OBJ_mgf1 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0A,/* [5961] OBJ_rsassaPss */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x07,/* [5970] OBJ_rsaesOaep */
+0x2A,0x86,0x48,0xCE,0x3E,0x02,0x01,          /* [5979] OBJ_dhpublicnumber */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x01,/* [5986] OBJ_brainpoolP160r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x02,/* [5995] OBJ_brainpoolP160t1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x03,/* [6004] OBJ_brainpoolP192r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x04,/* [6013] OBJ_brainpoolP192t1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x05,/* [6022] OBJ_brainpoolP224r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x06,/* [6031] OBJ_brainpoolP224t1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07,/* [6040] OBJ_brainpoolP256r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x08,/* [6049] OBJ_brainpoolP256t1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x09,/* [6058] OBJ_brainpoolP320r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0A,/* [6067] OBJ_brainpoolP320t1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0B,/* [6076] OBJ_brainpoolP384r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0C,/* [6085] OBJ_brainpoolP384t1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0D,/* [6094] OBJ_brainpoolP512r1 */
+0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0E,/* [6103] OBJ_brainpoolP512t1 */
+0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x09,/* [6112] OBJ_pSpecified */
+0x2B,0x81,0x05,0x10,0x86,0x48,0x3F,0x00,0x02,/* [6121] OBJ_dhSinglePass_stdDH_sha1kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0B,0x00,               /* [6130] OBJ_dhSinglePass_stdDH_sha224kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0B,0x01,               /* [6136] OBJ_dhSinglePass_stdDH_sha256kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0B,0x02,               /* [6142] OBJ_dhSinglePass_stdDH_sha384kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0B,0x03,               /* [6148] OBJ_dhSinglePass_stdDH_sha512kdf_scheme */
+0x2B,0x81,0x05,0x10,0x86,0x48,0x3F,0x00,0x03,/* [6154] OBJ_dhSinglePass_cofactorDH_sha1kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0E,0x00,               /* [6163] OBJ_dhSinglePass_cofactorDH_sha224kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0E,0x01,               /* [6169] OBJ_dhSinglePass_cofactorDH_sha256kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0E,0x02,               /* [6175] OBJ_dhSinglePass_cofactorDH_sha384kdf_scheme */
+0x2B,0x81,0x04,0x01,0x0E,0x03,               /* [6181] OBJ_dhSinglePass_cofactorDH_sha512kdf_scheme */
+};
+
+static const ASN1_OBJECT kObjects[NUM_NID]={
+{"UNDEF","undefined",NID_undef,1,&(lvalues[0]),0},
+{"rsadsi","RSA Data Security, Inc.",NID_rsadsi,6,&(lvalues[1]),0},
+{"pkcs","RSA Data Security, Inc. PKCS",NID_pkcs,7,&(lvalues[7]),0},
+{"MD2","md2",NID_md2,8,&(lvalues[14]),0},
+{"MD5","md5",NID_md5,8,&(lvalues[22]),0},
+{"RC4","rc4",NID_rc4,8,&(lvalues[30]),0},
+{"rsaEncryption","rsaEncryption",NID_rsaEncryption,9,&(lvalues[38]),0},
+{"RSA-MD2","md2WithRSAEncryption",NID_md2WithRSAEncryption,9,
+	&(lvalues[47]),0},
+{"RSA-MD5","md5WithRSAEncryption",NID_md5WithRSAEncryption,9,
+	&(lvalues[56]),0},
+{"PBE-MD2-DES","pbeWithMD2AndDES-CBC",NID_pbeWithMD2AndDES_CBC,9,
+	&(lvalues[65]),0},
+{"PBE-MD5-DES","pbeWithMD5AndDES-CBC",NID_pbeWithMD5AndDES_CBC,9,
+	&(lvalues[74]),0},
+{"X500","directory services (X.500)",NID_X500,1,&(lvalues[83]),0},
+{"X509","X509",NID_X509,2,&(lvalues[84]),0},
+{"CN","commonName",NID_commonName,3,&(lvalues[86]),0},
+{"C","countryName",NID_countryName,3,&(lvalues[89]),0},
+{"L","localityName",NID_localityName,3,&(lvalues[92]),0},
+{"ST","stateOrProvinceName",NID_stateOrProvinceName,3,&(lvalues[95]),0},
+{"O","organizationName",NID_organizationName,3,&(lvalues[98]),0},
+{"OU","organizationalUnitName",NID_organizationalUnitName,3,
+	&(lvalues[101]),0},
+{"RSA","rsa",NID_rsa,4,&(lvalues[104]),0},
+{"pkcs7","pkcs7",NID_pkcs7,8,&(lvalues[108]),0},
+{"pkcs7-data","pkcs7-data",NID_pkcs7_data,9,&(lvalues[116]),0},
+{"pkcs7-signedData","pkcs7-signedData",NID_pkcs7_signed,9,
+	&(lvalues[125]),0},
+{"pkcs7-envelopedData","pkcs7-envelopedData",NID_pkcs7_enveloped,9,
+	&(lvalues[134]),0},
+{"pkcs7-signedAndEnvelopedData","pkcs7-signedAndEnvelopedData",
+	NID_pkcs7_signedAndEnveloped,9,&(lvalues[143]),0},
+{"pkcs7-digestData","pkcs7-digestData",NID_pkcs7_digest,9,
+	&(lvalues[152]),0},
+{"pkcs7-encryptedData","pkcs7-encryptedData",NID_pkcs7_encrypted,9,
+	&(lvalues[161]),0},
+{"pkcs3","pkcs3",NID_pkcs3,8,&(lvalues[170]),0},
+{"dhKeyAgreement","dhKeyAgreement",NID_dhKeyAgreement,9,
+	&(lvalues[178]),0},
+{"DES-ECB","des-ecb",NID_des_ecb,5,&(lvalues[187]),0},
+{"DES-CFB","des-cfb",NID_des_cfb64,5,&(lvalues[192]),0},
+{"DES-CBC","des-cbc",NID_des_cbc,5,&(lvalues[197]),0},
+{"DES-EDE","des-ede",NID_des_ede_ecb,5,&(lvalues[202]),0},
+{"DES-EDE3","des-ede3",NID_des_ede3_ecb,0,NULL,0},
+{"IDEA-CBC","idea-cbc",NID_idea_cbc,11,&(lvalues[207]),0},
+{"IDEA-CFB","idea-cfb",NID_idea_cfb64,0,NULL,0},
+{"IDEA-ECB","idea-ecb",NID_idea_ecb,0,NULL,0},
+{"RC2-CBC","rc2-cbc",NID_rc2_cbc,8,&(lvalues[218]),0},
+{"RC2-ECB","rc2-ecb",NID_rc2_ecb,0,NULL,0},
+{"RC2-CFB","rc2-cfb",NID_rc2_cfb64,0,NULL,0},
+{"RC2-OFB","rc2-ofb",NID_rc2_ofb64,0,NULL,0},
+{"SHA","sha",NID_sha,5,&(lvalues[226]),0},
+{"RSA-SHA","shaWithRSAEncryption",NID_shaWithRSAEncryption,5,
+	&(lvalues[231]),0},
+{"DES-EDE-CBC","des-ede-cbc",NID_des_ede_cbc,0,NULL,0},
+{"DES-EDE3-CBC","des-ede3-cbc",NID_des_ede3_cbc,8,&(lvalues[236]),0},
+{"DES-OFB","des-ofb",NID_des_ofb64,5,&(lvalues[244]),0},
+{"IDEA-OFB","idea-ofb",NID_idea_ofb64,0,NULL,0},
+{"pkcs9","pkcs9",NID_pkcs9,8,&(lvalues[249]),0},
+{"emailAddress","emailAddress",NID_pkcs9_emailAddress,9,
+	&(lvalues[257]),0},
+{"unstructuredName","unstructuredName",NID_pkcs9_unstructuredName,9,
+	&(lvalues[266]),0},
+{"contentType","contentType",NID_pkcs9_contentType,9,&(lvalues[275]),0},
+{"messageDigest","messageDigest",NID_pkcs9_messageDigest,9,
+	&(lvalues[284]),0},
+{"signingTime","signingTime",NID_pkcs9_signingTime,9,&(lvalues[293]),0},
+{"countersignature","countersignature",NID_pkcs9_countersignature,9,
+	&(lvalues[302]),0},
+{"challengePassword","challengePassword",NID_pkcs9_challengePassword,
+	9,&(lvalues[311]),0},
+{"unstructuredAddress","unstructuredAddress",
+	NID_pkcs9_unstructuredAddress,9,&(lvalues[320]),0},
+{"extendedCertificateAttributes","extendedCertificateAttributes",
+	NID_pkcs9_extCertAttributes,9,&(lvalues[329]),0},
+{"Netscape","Netscape Communications Corp.",NID_netscape,7,
+	&(lvalues[338]),0},
+{"nsCertExt","Netscape Certificate Extension",
+	NID_netscape_cert_extension,8,&(lvalues[345]),0},
+{"nsDataType","Netscape Data Type",NID_netscape_data_type,8,
+	&(lvalues[353]),0},
+{"DES-EDE-CFB","des-ede-cfb",NID_des_ede_cfb64,0,NULL,0},
+{"DES-EDE3-CFB","des-ede3-cfb",NID_des_ede3_cfb64,0,NULL,0},
+{"DES-EDE-OFB","des-ede-ofb",NID_des_ede_ofb64,0,NULL,0},
+{"DES-EDE3-OFB","des-ede3-ofb",NID_des_ede3_ofb64,0,NULL,0},
+{"SHA1","sha1",NID_sha1,5,&(lvalues[361]),0},
+{"RSA-SHA1","sha1WithRSAEncryption",NID_sha1WithRSAEncryption,9,
+	&(lvalues[366]),0},
+{"DSA-SHA","dsaWithSHA",NID_dsaWithSHA,5,&(lvalues[375]),0},
+{"DSA-old","dsaEncryption-old",NID_dsa_2,5,&(lvalues[380]),0},
+{"PBE-SHA1-RC2-64","pbeWithSHA1AndRC2-CBC",NID_pbeWithSHA1AndRC2_CBC,
+	9,&(lvalues[385]),0},
+{"PBKDF2","PBKDF2",NID_id_pbkdf2,9,&(lvalues[394]),0},
+{"DSA-SHA1-old","dsaWithSHA1-old",NID_dsaWithSHA1_2,5,&(lvalues[403]),0},
+{"nsCertType","Netscape Cert Type",NID_netscape_cert_type,9,
+	&(lvalues[408]),0},
+{"nsBaseUrl","Netscape Base Url",NID_netscape_base_url,9,
+	&(lvalues[417]),0},
+{"nsRevocationUrl","Netscape Revocation Url",
+	NID_netscape_revocation_url,9,&(lvalues[426]),0},
+{"nsCaRevocationUrl","Netscape CA Revocation Url",
+	NID_netscape_ca_revocation_url,9,&(lvalues[435]),0},
+{"nsRenewalUrl","Netscape Renewal Url",NID_netscape_renewal_url,9,
+	&(lvalues[444]),0},
+{"nsCaPolicyUrl","Netscape CA Policy Url",NID_netscape_ca_policy_url,
+	9,&(lvalues[453]),0},
+{"nsSslServerName","Netscape SSL Server Name",
+	NID_netscape_ssl_server_name,9,&(lvalues[462]),0},
+{"nsComment","Netscape Comment",NID_netscape_comment,9,&(lvalues[471]),0},
+{"nsCertSequence","Netscape Certificate Sequence",
+	NID_netscape_cert_sequence,9,&(lvalues[480]),0},
+{"DESX-CBC","desx-cbc",NID_desx_cbc,0,NULL,0},
+{"id-ce","id-ce",NID_id_ce,2,&(lvalues[489]),0},
+{"subjectKeyIdentifier","X509v3 Subject Key Identifier",
+	NID_subject_key_identifier,3,&(lvalues[491]),0},
+{"keyUsage","X509v3 Key Usage",NID_key_usage,3,&(lvalues[494]),0},
+{"privateKeyUsagePeriod","X509v3 Private Key Usage Period",
+	NID_private_key_usage_period,3,&(lvalues[497]),0},
+{"subjectAltName","X509v3 Subject Alternative Name",
+	NID_subject_alt_name,3,&(lvalues[500]),0},
+{"issuerAltName","X509v3 Issuer Alternative Name",NID_issuer_alt_name,
+	3,&(lvalues[503]),0},
+{"basicConstraints","X509v3 Basic Constraints",NID_basic_constraints,
+	3,&(lvalues[506]),0},
+{"crlNumber","X509v3 CRL Number",NID_crl_number,3,&(lvalues[509]),0},
+{"certificatePolicies","X509v3 Certificate Policies",
+	NID_certificate_policies,3,&(lvalues[512]),0},
+{"authorityKeyIdentifier","X509v3 Authority Key Identifier",
+	NID_authority_key_identifier,3,&(lvalues[515]),0},
+{"BF-CBC","bf-cbc",NID_bf_cbc,9,&(lvalues[518]),0},
+{"BF-ECB","bf-ecb",NID_bf_ecb,0,NULL,0},
+{"BF-CFB","bf-cfb",NID_bf_cfb64,0,NULL,0},
+{"BF-OFB","bf-ofb",NID_bf_ofb64,0,NULL,0},
+{"MDC2","mdc2",NID_mdc2,4,&(lvalues[527]),0},
+{"RSA-MDC2","mdc2WithRSA",NID_mdc2WithRSA,4,&(lvalues[531]),0},
+{"RC4-40","rc4-40",NID_rc4_40,0,NULL,0},
+{"RC2-40-CBC","rc2-40-cbc",NID_rc2_40_cbc,0,NULL,0},
+{"GN","givenName",NID_givenName,3,&(lvalues[535]),0},
+{"SN","surname",NID_surname,3,&(lvalues[538]),0},
+{"initials","initials",NID_initials,3,&(lvalues[541]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"crlDistributionPoints","X509v3 CRL Distribution Points",
+	NID_crl_distribution_points,3,&(lvalues[544]),0},
+{"RSA-NP-MD5","md5WithRSA",NID_md5WithRSA,5,&(lvalues[547]),0},
+{"serialNumber","serialNumber",NID_serialNumber,3,&(lvalues[552]),0},
+{"title","title",NID_title,3,&(lvalues[555]),0},
+{"description","description",NID_description,3,&(lvalues[558]),0},
+{"CAST5-CBC","cast5-cbc",NID_cast5_cbc,9,&(lvalues[561]),0},
+{"CAST5-ECB","cast5-ecb",NID_cast5_ecb,0,NULL,0},
+{"CAST5-CFB","cast5-cfb",NID_cast5_cfb64,0,NULL,0},
+{"CAST5-OFB","cast5-ofb",NID_cast5_ofb64,0,NULL,0},
+{"pbeWithMD5AndCast5CBC","pbeWithMD5AndCast5CBC",
+	NID_pbeWithMD5AndCast5_CBC,9,&(lvalues[570]),0},
+{"DSA-SHA1","dsaWithSHA1",NID_dsaWithSHA1,7,&(lvalues[579]),0},
+{"MD5-SHA1","md5-sha1",NID_md5_sha1,0,NULL,0},
+{"RSA-SHA1-2","sha1WithRSA",NID_sha1WithRSA,5,&(lvalues[586]),0},
+{"DSA","dsaEncryption",NID_dsa,7,&(lvalues[591]),0},
+{"RIPEMD160","ripemd160",NID_ripemd160,5,&(lvalues[598]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"RSA-RIPEMD160","ripemd160WithRSA",NID_ripemd160WithRSA,6,
+	&(lvalues[603]),0},
+{"RC5-CBC","rc5-cbc",NID_rc5_cbc,8,&(lvalues[609]),0},
+{"RC5-ECB","rc5-ecb",NID_rc5_ecb,0,NULL,0},
+{"RC5-CFB","rc5-cfb",NID_rc5_cfb64,0,NULL,0},
+{"RC5-OFB","rc5-ofb",NID_rc5_ofb64,0,NULL,0},
+{"RLE","run length compression",NID_rle_compression,6,&(lvalues[617]),0},
+{"ZLIB","zlib compression",NID_zlib_compression,11,&(lvalues[623]),0},
+{"extendedKeyUsage","X509v3 Extended Key Usage",NID_ext_key_usage,3,
+	&(lvalues[634]),0},
+{"PKIX","PKIX",NID_id_pkix,6,&(lvalues[637]),0},
+{"id-kp","id-kp",NID_id_kp,7,&(lvalues[643]),0},
+{"serverAuth","TLS Web Server Authentication",NID_server_auth,8,
+	&(lvalues[650]),0},
+{"clientAuth","TLS Web Client Authentication",NID_client_auth,8,
+	&(lvalues[658]),0},
+{"codeSigning","Code Signing",NID_code_sign,8,&(lvalues[666]),0},
+{"emailProtection","E-mail Protection",NID_email_protect,8,
+	&(lvalues[674]),0},
+{"timeStamping","Time Stamping",NID_time_stamp,8,&(lvalues[682]),0},
+{"msCodeInd","Microsoft Individual Code Signing",NID_ms_code_ind,10,
+	&(lvalues[690]),0},
+{"msCodeCom","Microsoft Commercial Code Signing",NID_ms_code_com,10,
+	&(lvalues[700]),0},
+{"msCTLSign","Microsoft Trust List Signing",NID_ms_ctl_sign,10,
+	&(lvalues[710]),0},
+{"msSGC","Microsoft Server Gated Crypto",NID_ms_sgc,10,&(lvalues[720]),0},
+{"msEFS","Microsoft Encrypted File System",NID_ms_efs,10,
+	&(lvalues[730]),0},
+{"nsSGC","Netscape Server Gated Crypto",NID_ns_sgc,9,&(lvalues[740]),0},
+{"deltaCRL","X509v3 Delta CRL Indicator",NID_delta_crl,3,
+	&(lvalues[749]),0},
+{"CRLReason","X509v3 CRL Reason Code",NID_crl_reason,3,&(lvalues[752]),0},
+{"invalidityDate","Invalidity Date",NID_invalidity_date,3,
+	&(lvalues[755]),0},
+{"SXNetID","Strong Extranet ID",NID_sxnet,5,&(lvalues[758]),0},
+{"PBE-SHA1-RC4-128","pbeWithSHA1And128BitRC4",
+	NID_pbe_WithSHA1And128BitRC4,10,&(lvalues[763]),0},
+{"PBE-SHA1-RC4-40","pbeWithSHA1And40BitRC4",
+	NID_pbe_WithSHA1And40BitRC4,10,&(lvalues[773]),0},
+{"PBE-SHA1-3DES","pbeWithSHA1And3-KeyTripleDES-CBC",
+	NID_pbe_WithSHA1And3_Key_TripleDES_CBC,10,&(lvalues[783]),0},
+{"PBE-SHA1-2DES","pbeWithSHA1And2-KeyTripleDES-CBC",
+	NID_pbe_WithSHA1And2_Key_TripleDES_CBC,10,&(lvalues[793]),0},
+{"PBE-SHA1-RC2-128","pbeWithSHA1And128BitRC2-CBC",
+	NID_pbe_WithSHA1And128BitRC2_CBC,10,&(lvalues[803]),0},
+{"PBE-SHA1-RC2-40","pbeWithSHA1And40BitRC2-CBC",
+	NID_pbe_WithSHA1And40BitRC2_CBC,10,&(lvalues[813]),0},
+{"keyBag","keyBag",NID_keyBag,11,&(lvalues[823]),0},
+{"pkcs8ShroudedKeyBag","pkcs8ShroudedKeyBag",NID_pkcs8ShroudedKeyBag,
+	11,&(lvalues[834]),0},
+{"certBag","certBag",NID_certBag,11,&(lvalues[845]),0},
+{"crlBag","crlBag",NID_crlBag,11,&(lvalues[856]),0},
+{"secretBag","secretBag",NID_secretBag,11,&(lvalues[867]),0},
+{"safeContentsBag","safeContentsBag",NID_safeContentsBag,11,
+	&(lvalues[878]),0},
+{"friendlyName","friendlyName",NID_friendlyName,9,&(lvalues[889]),0},
+{"localKeyID","localKeyID",NID_localKeyID,9,&(lvalues[898]),0},
+{"x509Certificate","x509Certificate",NID_x509Certificate,10,
+	&(lvalues[907]),0},
+{"sdsiCertificate","sdsiCertificate",NID_sdsiCertificate,10,
+	&(lvalues[917]),0},
+{"x509Crl","x509Crl",NID_x509Crl,10,&(lvalues[927]),0},
+{"PBES2","PBES2",NID_pbes2,9,&(lvalues[937]),0},
+{"PBMAC1","PBMAC1",NID_pbmac1,9,&(lvalues[946]),0},
+{"hmacWithSHA1","hmacWithSHA1",NID_hmacWithSHA1,8,&(lvalues[955]),0},
+{"id-qt-cps","Policy Qualifier CPS",NID_id_qt_cps,8,&(lvalues[963]),0},
+{"id-qt-unotice","Policy Qualifier User Notice",NID_id_qt_unotice,8,
+	&(lvalues[971]),0},
+{"RC2-64-CBC","rc2-64-cbc",NID_rc2_64_cbc,0,NULL,0},
+{"SMIME-CAPS","S/MIME Capabilities",NID_SMIMECapabilities,9,
+	&(lvalues[979]),0},
+{"PBE-MD2-RC2-64","pbeWithMD2AndRC2-CBC",NID_pbeWithMD2AndRC2_CBC,9,
+	&(lvalues[988]),0},
+{"PBE-MD5-RC2-64","pbeWithMD5AndRC2-CBC",NID_pbeWithMD5AndRC2_CBC,9,
+	&(lvalues[997]),0},
+{"PBE-SHA1-DES","pbeWithSHA1AndDES-CBC",NID_pbeWithSHA1AndDES_CBC,9,
+	&(lvalues[1006]),0},
+{"msExtReq","Microsoft Extension Request",NID_ms_ext_req,10,
+	&(lvalues[1015]),0},
+{"extReq","Extension Request",NID_ext_req,9,&(lvalues[1025]),0},
+{"name","name",NID_name,3,&(lvalues[1034]),0},
+{"dnQualifier","dnQualifier",NID_dnQualifier,3,&(lvalues[1037]),0},
+{"id-pe","id-pe",NID_id_pe,7,&(lvalues[1040]),0},
+{"id-ad","id-ad",NID_id_ad,7,&(lvalues[1047]),0},
+{"authorityInfoAccess","Authority Information Access",NID_info_access,
+	8,&(lvalues[1054]),0},
+{"OCSP","OCSP",NID_ad_OCSP,8,&(lvalues[1062]),0},
+{"caIssuers","CA Issuers",NID_ad_ca_issuers,8,&(lvalues[1070]),0},
+{"OCSPSigning","OCSP Signing",NID_OCSP_sign,8,&(lvalues[1078]),0},
+{"ISO","iso",NID_iso,1,&(lvalues[1086]),0},
+{"member-body","ISO Member Body",NID_member_body,1,&(lvalues[1087]),0},
+{"ISO-US","ISO US Member Body",NID_ISO_US,3,&(lvalues[1088]),0},
+{"X9-57","X9.57",NID_X9_57,5,&(lvalues[1091]),0},
+{"X9cm","X9.57 CM ?",NID_X9cm,6,&(lvalues[1096]),0},
+{"pkcs1","pkcs1",NID_pkcs1,8,&(lvalues[1102]),0},
+{"pkcs5","pkcs5",NID_pkcs5,8,&(lvalues[1110]),0},
+{"SMIME","S/MIME",NID_SMIME,9,&(lvalues[1118]),0},
+{"id-smime-mod","id-smime-mod",NID_id_smime_mod,10,&(lvalues[1127]),0},
+{"id-smime-ct","id-smime-ct",NID_id_smime_ct,10,&(lvalues[1137]),0},
+{"id-smime-aa","id-smime-aa",NID_id_smime_aa,10,&(lvalues[1147]),0},
+{"id-smime-alg","id-smime-alg",NID_id_smime_alg,10,&(lvalues[1157]),0},
+{"id-smime-cd","id-smime-cd",NID_id_smime_cd,10,&(lvalues[1167]),0},
+{"id-smime-spq","id-smime-spq",NID_id_smime_spq,10,&(lvalues[1177]),0},
+{"id-smime-cti","id-smime-cti",NID_id_smime_cti,10,&(lvalues[1187]),0},
+{"id-smime-mod-cms","id-smime-mod-cms",NID_id_smime_mod_cms,11,
+	&(lvalues[1197]),0},
+{"id-smime-mod-ess","id-smime-mod-ess",NID_id_smime_mod_ess,11,
+	&(lvalues[1208]),0},
+{"id-smime-mod-oid","id-smime-mod-oid",NID_id_smime_mod_oid,11,
+	&(lvalues[1219]),0},
+{"id-smime-mod-msg-v3","id-smime-mod-msg-v3",NID_id_smime_mod_msg_v3,
+	11,&(lvalues[1230]),0},
+{"id-smime-mod-ets-eSignature-88","id-smime-mod-ets-eSignature-88",
+	NID_id_smime_mod_ets_eSignature_88,11,&(lvalues[1241]),0},
+{"id-smime-mod-ets-eSignature-97","id-smime-mod-ets-eSignature-97",
+	NID_id_smime_mod_ets_eSignature_97,11,&(lvalues[1252]),0},
+{"id-smime-mod-ets-eSigPolicy-88","id-smime-mod-ets-eSigPolicy-88",
+	NID_id_smime_mod_ets_eSigPolicy_88,11,&(lvalues[1263]),0},
+{"id-smime-mod-ets-eSigPolicy-97","id-smime-mod-ets-eSigPolicy-97",
+	NID_id_smime_mod_ets_eSigPolicy_97,11,&(lvalues[1274]),0},
+{"id-smime-ct-receipt","id-smime-ct-receipt",NID_id_smime_ct_receipt,
+	11,&(lvalues[1285]),0},
+{"id-smime-ct-authData","id-smime-ct-authData",
+	NID_id_smime_ct_authData,11,&(lvalues[1296]),0},
+{"id-smime-ct-publishCert","id-smime-ct-publishCert",
+	NID_id_smime_ct_publishCert,11,&(lvalues[1307]),0},
+{"id-smime-ct-TSTInfo","id-smime-ct-TSTInfo",NID_id_smime_ct_TSTInfo,
+	11,&(lvalues[1318]),0},
+{"id-smime-ct-TDTInfo","id-smime-ct-TDTInfo",NID_id_smime_ct_TDTInfo,
+	11,&(lvalues[1329]),0},
+{"id-smime-ct-contentInfo","id-smime-ct-contentInfo",
+	NID_id_smime_ct_contentInfo,11,&(lvalues[1340]),0},
+{"id-smime-ct-DVCSRequestData","id-smime-ct-DVCSRequestData",
+	NID_id_smime_ct_DVCSRequestData,11,&(lvalues[1351]),0},
+{"id-smime-ct-DVCSResponseData","id-smime-ct-DVCSResponseData",
+	NID_id_smime_ct_DVCSResponseData,11,&(lvalues[1362]),0},
+{"id-smime-aa-receiptRequest","id-smime-aa-receiptRequest",
+	NID_id_smime_aa_receiptRequest,11,&(lvalues[1373]),0},
+{"id-smime-aa-securityLabel","id-smime-aa-securityLabel",
+	NID_id_smime_aa_securityLabel,11,&(lvalues[1384]),0},
+{"id-smime-aa-mlExpandHistory","id-smime-aa-mlExpandHistory",
+	NID_id_smime_aa_mlExpandHistory,11,&(lvalues[1395]),0},
+{"id-smime-aa-contentHint","id-smime-aa-contentHint",
+	NID_id_smime_aa_contentHint,11,&(lvalues[1406]),0},
+{"id-smime-aa-msgSigDigest","id-smime-aa-msgSigDigest",
+	NID_id_smime_aa_msgSigDigest,11,&(lvalues[1417]),0},
+{"id-smime-aa-encapContentType","id-smime-aa-encapContentType",
+	NID_id_smime_aa_encapContentType,11,&(lvalues[1428]),0},
+{"id-smime-aa-contentIdentifier","id-smime-aa-contentIdentifier",
+	NID_id_smime_aa_contentIdentifier,11,&(lvalues[1439]),0},
+{"id-smime-aa-macValue","id-smime-aa-macValue",
+	NID_id_smime_aa_macValue,11,&(lvalues[1450]),0},
+{"id-smime-aa-equivalentLabels","id-smime-aa-equivalentLabels",
+	NID_id_smime_aa_equivalentLabels,11,&(lvalues[1461]),0},
+{"id-smime-aa-contentReference","id-smime-aa-contentReference",
+	NID_id_smime_aa_contentReference,11,&(lvalues[1472]),0},
+{"id-smime-aa-encrypKeyPref","id-smime-aa-encrypKeyPref",
+	NID_id_smime_aa_encrypKeyPref,11,&(lvalues[1483]),0},
+{"id-smime-aa-signingCertificate","id-smime-aa-signingCertificate",
+	NID_id_smime_aa_signingCertificate,11,&(lvalues[1494]),0},
+{"id-smime-aa-smimeEncryptCerts","id-smime-aa-smimeEncryptCerts",
+	NID_id_smime_aa_smimeEncryptCerts,11,&(lvalues[1505]),0},
+{"id-smime-aa-timeStampToken","id-smime-aa-timeStampToken",
+	NID_id_smime_aa_timeStampToken,11,&(lvalues[1516]),0},
+{"id-smime-aa-ets-sigPolicyId","id-smime-aa-ets-sigPolicyId",
+	NID_id_smime_aa_ets_sigPolicyId,11,&(lvalues[1527]),0},
+{"id-smime-aa-ets-commitmentType","id-smime-aa-ets-commitmentType",
+	NID_id_smime_aa_ets_commitmentType,11,&(lvalues[1538]),0},
+{"id-smime-aa-ets-signerLocation","id-smime-aa-ets-signerLocation",
+	NID_id_smime_aa_ets_signerLocation,11,&(lvalues[1549]),0},
+{"id-smime-aa-ets-signerAttr","id-smime-aa-ets-signerAttr",
+	NID_id_smime_aa_ets_signerAttr,11,&(lvalues[1560]),0},
+{"id-smime-aa-ets-otherSigCert","id-smime-aa-ets-otherSigCert",
+	NID_id_smime_aa_ets_otherSigCert,11,&(lvalues[1571]),0},
+{"id-smime-aa-ets-contentTimestamp",
+	"id-smime-aa-ets-contentTimestamp",
+	NID_id_smime_aa_ets_contentTimestamp,11,&(lvalues[1582]),0},
+{"id-smime-aa-ets-CertificateRefs","id-smime-aa-ets-CertificateRefs",
+	NID_id_smime_aa_ets_CertificateRefs,11,&(lvalues[1593]),0},
+{"id-smime-aa-ets-RevocationRefs","id-smime-aa-ets-RevocationRefs",
+	NID_id_smime_aa_ets_RevocationRefs,11,&(lvalues[1604]),0},
+{"id-smime-aa-ets-certValues","id-smime-aa-ets-certValues",
+	NID_id_smime_aa_ets_certValues,11,&(lvalues[1615]),0},
+{"id-smime-aa-ets-revocationValues",
+	"id-smime-aa-ets-revocationValues",
+	NID_id_smime_aa_ets_revocationValues,11,&(lvalues[1626]),0},
+{"id-smime-aa-ets-escTimeStamp","id-smime-aa-ets-escTimeStamp",
+	NID_id_smime_aa_ets_escTimeStamp,11,&(lvalues[1637]),0},
+{"id-smime-aa-ets-certCRLTimestamp",
+	"id-smime-aa-ets-certCRLTimestamp",
+	NID_id_smime_aa_ets_certCRLTimestamp,11,&(lvalues[1648]),0},
+{"id-smime-aa-ets-archiveTimeStamp",
+	"id-smime-aa-ets-archiveTimeStamp",
+	NID_id_smime_aa_ets_archiveTimeStamp,11,&(lvalues[1659]),0},
+{"id-smime-aa-signatureType","id-smime-aa-signatureType",
+	NID_id_smime_aa_signatureType,11,&(lvalues[1670]),0},
+{"id-smime-aa-dvcs-dvc","id-smime-aa-dvcs-dvc",
+	NID_id_smime_aa_dvcs_dvc,11,&(lvalues[1681]),0},
+{"id-smime-alg-ESDHwith3DES","id-smime-alg-ESDHwith3DES",
+	NID_id_smime_alg_ESDHwith3DES,11,&(lvalues[1692]),0},
+{"id-smime-alg-ESDHwithRC2","id-smime-alg-ESDHwithRC2",
+	NID_id_smime_alg_ESDHwithRC2,11,&(lvalues[1703]),0},
+{"id-smime-alg-3DESwrap","id-smime-alg-3DESwrap",
+	NID_id_smime_alg_3DESwrap,11,&(lvalues[1714]),0},
+{"id-smime-alg-RC2wrap","id-smime-alg-RC2wrap",
+	NID_id_smime_alg_RC2wrap,11,&(lvalues[1725]),0},
+{"id-smime-alg-ESDH","id-smime-alg-ESDH",NID_id_smime_alg_ESDH,11,
+	&(lvalues[1736]),0},
+{"id-smime-alg-CMS3DESwrap","id-smime-alg-CMS3DESwrap",
+	NID_id_smime_alg_CMS3DESwrap,11,&(lvalues[1747]),0},
+{"id-smime-alg-CMSRC2wrap","id-smime-alg-CMSRC2wrap",
+	NID_id_smime_alg_CMSRC2wrap,11,&(lvalues[1758]),0},
+{"id-smime-cd-ldap","id-smime-cd-ldap",NID_id_smime_cd_ldap,11,
+	&(lvalues[1769]),0},
+{"id-smime-spq-ets-sqt-uri","id-smime-spq-ets-sqt-uri",
+	NID_id_smime_spq_ets_sqt_uri,11,&(lvalues[1780]),0},
+{"id-smime-spq-ets-sqt-unotice","id-smime-spq-ets-sqt-unotice",
+	NID_id_smime_spq_ets_sqt_unotice,11,&(lvalues[1791]),0},
+{"id-smime-cti-ets-proofOfOrigin","id-smime-cti-ets-proofOfOrigin",
+	NID_id_smime_cti_ets_proofOfOrigin,11,&(lvalues[1802]),0},
+{"id-smime-cti-ets-proofOfReceipt","id-smime-cti-ets-proofOfReceipt",
+	NID_id_smime_cti_ets_proofOfReceipt,11,&(lvalues[1813]),0},
+{"id-smime-cti-ets-proofOfDelivery",
+	"id-smime-cti-ets-proofOfDelivery",
+	NID_id_smime_cti_ets_proofOfDelivery,11,&(lvalues[1824]),0},
+{"id-smime-cti-ets-proofOfSender","id-smime-cti-ets-proofOfSender",
+	NID_id_smime_cti_ets_proofOfSender,11,&(lvalues[1835]),0},
+{"id-smime-cti-ets-proofOfApproval",
+	"id-smime-cti-ets-proofOfApproval",
+	NID_id_smime_cti_ets_proofOfApproval,11,&(lvalues[1846]),0},
+{"id-smime-cti-ets-proofOfCreation",
+	"id-smime-cti-ets-proofOfCreation",
+	NID_id_smime_cti_ets_proofOfCreation,11,&(lvalues[1857]),0},
+{"MD4","md4",NID_md4,8,&(lvalues[1868]),0},
+{"id-pkix-mod","id-pkix-mod",NID_id_pkix_mod,7,&(lvalues[1876]),0},
+{"id-qt","id-qt",NID_id_qt,7,&(lvalues[1883]),0},
+{"id-it","id-it",NID_id_it,7,&(lvalues[1890]),0},
+{"id-pkip","id-pkip",NID_id_pkip,7,&(lvalues[1897]),0},
+{"id-alg","id-alg",NID_id_alg,7,&(lvalues[1904]),0},
+{"id-cmc","id-cmc",NID_id_cmc,7,&(lvalues[1911]),0},
+{"id-on","id-on",NID_id_on,7,&(lvalues[1918]),0},
+{"id-pda","id-pda",NID_id_pda,7,&(lvalues[1925]),0},
+{"id-aca","id-aca",NID_id_aca,7,&(lvalues[1932]),0},
+{"id-qcs","id-qcs",NID_id_qcs,7,&(lvalues[1939]),0},
+{"id-cct","id-cct",NID_id_cct,7,&(lvalues[1946]),0},
+{"id-pkix1-explicit-88","id-pkix1-explicit-88",
+	NID_id_pkix1_explicit_88,8,&(lvalues[1953]),0},
+{"id-pkix1-implicit-88","id-pkix1-implicit-88",
+	NID_id_pkix1_implicit_88,8,&(lvalues[1961]),0},
+{"id-pkix1-explicit-93","id-pkix1-explicit-93",
+	NID_id_pkix1_explicit_93,8,&(lvalues[1969]),0},
+{"id-pkix1-implicit-93","id-pkix1-implicit-93",
+	NID_id_pkix1_implicit_93,8,&(lvalues[1977]),0},
+{"id-mod-crmf","id-mod-crmf",NID_id_mod_crmf,8,&(lvalues[1985]),0},
+{"id-mod-cmc","id-mod-cmc",NID_id_mod_cmc,8,&(lvalues[1993]),0},
+{"id-mod-kea-profile-88","id-mod-kea-profile-88",
+	NID_id_mod_kea_profile_88,8,&(lvalues[2001]),0},
+{"id-mod-kea-profile-93","id-mod-kea-profile-93",
+	NID_id_mod_kea_profile_93,8,&(lvalues[2009]),0},
+{"id-mod-cmp","id-mod-cmp",NID_id_mod_cmp,8,&(lvalues[2017]),0},
+{"id-mod-qualified-cert-88","id-mod-qualified-cert-88",
+	NID_id_mod_qualified_cert_88,8,&(lvalues[2025]),0},
+{"id-mod-qualified-cert-93","id-mod-qualified-cert-93",
+	NID_id_mod_qualified_cert_93,8,&(lvalues[2033]),0},
+{"id-mod-attribute-cert","id-mod-attribute-cert",
+	NID_id_mod_attribute_cert,8,&(lvalues[2041]),0},
+{"id-mod-timestamp-protocol","id-mod-timestamp-protocol",
+	NID_id_mod_timestamp_protocol,8,&(lvalues[2049]),0},
+{"id-mod-ocsp","id-mod-ocsp",NID_id_mod_ocsp,8,&(lvalues[2057]),0},
+{"id-mod-dvcs","id-mod-dvcs",NID_id_mod_dvcs,8,&(lvalues[2065]),0},
+{"id-mod-cmp2000","id-mod-cmp2000",NID_id_mod_cmp2000,8,
+	&(lvalues[2073]),0},
+{"biometricInfo","Biometric Info",NID_biometricInfo,8,&(lvalues[2081]),0},
+{"qcStatements","qcStatements",NID_qcStatements,8,&(lvalues[2089]),0},
+{"ac-auditEntity","ac-auditEntity",NID_ac_auditEntity,8,
+	&(lvalues[2097]),0},
+{"ac-targeting","ac-targeting",NID_ac_targeting,8,&(lvalues[2105]),0},
+{"aaControls","aaControls",NID_aaControls,8,&(lvalues[2113]),0},
+{"sbgp-ipAddrBlock","sbgp-ipAddrBlock",NID_sbgp_ipAddrBlock,8,
+	&(lvalues[2121]),0},
+{"sbgp-autonomousSysNum","sbgp-autonomousSysNum",
+	NID_sbgp_autonomousSysNum,8,&(lvalues[2129]),0},
+{"sbgp-routerIdentifier","sbgp-routerIdentifier",
+	NID_sbgp_routerIdentifier,8,&(lvalues[2137]),0},
+{"textNotice","textNotice",NID_textNotice,8,&(lvalues[2145]),0},
+{"ipsecEndSystem","IPSec End System",NID_ipsecEndSystem,8,
+	&(lvalues[2153]),0},
+{"ipsecTunnel","IPSec Tunnel",NID_ipsecTunnel,8,&(lvalues[2161]),0},
+{"ipsecUser","IPSec User",NID_ipsecUser,8,&(lvalues[2169]),0},
+{"DVCS","dvcs",NID_dvcs,8,&(lvalues[2177]),0},
+{"id-it-caProtEncCert","id-it-caProtEncCert",NID_id_it_caProtEncCert,
+	8,&(lvalues[2185]),0},
+{"id-it-signKeyPairTypes","id-it-signKeyPairTypes",
+	NID_id_it_signKeyPairTypes,8,&(lvalues[2193]),0},
+{"id-it-encKeyPairTypes","id-it-encKeyPairTypes",
+	NID_id_it_encKeyPairTypes,8,&(lvalues[2201]),0},
+{"id-it-preferredSymmAlg","id-it-preferredSymmAlg",
+	NID_id_it_preferredSymmAlg,8,&(lvalues[2209]),0},
+{"id-it-caKeyUpdateInfo","id-it-caKeyUpdateInfo",
+	NID_id_it_caKeyUpdateInfo,8,&(lvalues[2217]),0},
+{"id-it-currentCRL","id-it-currentCRL",NID_id_it_currentCRL,8,
+	&(lvalues[2225]),0},
+{"id-it-unsupportedOIDs","id-it-unsupportedOIDs",
+	NID_id_it_unsupportedOIDs,8,&(lvalues[2233]),0},
+{"id-it-subscriptionRequest","id-it-subscriptionRequest",
+	NID_id_it_subscriptionRequest,8,&(lvalues[2241]),0},
+{"id-it-subscriptionResponse","id-it-subscriptionResponse",
+	NID_id_it_subscriptionResponse,8,&(lvalues[2249]),0},
+{"id-it-keyPairParamReq","id-it-keyPairParamReq",
+	NID_id_it_keyPairParamReq,8,&(lvalues[2257]),0},
+{"id-it-keyPairParamRep","id-it-keyPairParamRep",
+	NID_id_it_keyPairParamRep,8,&(lvalues[2265]),0},
+{"id-it-revPassphrase","id-it-revPassphrase",NID_id_it_revPassphrase,
+	8,&(lvalues[2273]),0},
+{"id-it-implicitConfirm","id-it-implicitConfirm",
+	NID_id_it_implicitConfirm,8,&(lvalues[2281]),0},
+{"id-it-confirmWaitTime","id-it-confirmWaitTime",
+	NID_id_it_confirmWaitTime,8,&(lvalues[2289]),0},
+{"id-it-origPKIMessage","id-it-origPKIMessage",
+	NID_id_it_origPKIMessage,8,&(lvalues[2297]),0},
+{"id-regCtrl","id-regCtrl",NID_id_regCtrl,8,&(lvalues[2305]),0},
+{"id-regInfo","id-regInfo",NID_id_regInfo,8,&(lvalues[2313]),0},
+{"id-regCtrl-regToken","id-regCtrl-regToken",NID_id_regCtrl_regToken,
+	9,&(lvalues[2321]),0},
+{"id-regCtrl-authenticator","id-regCtrl-authenticator",
+	NID_id_regCtrl_authenticator,9,&(lvalues[2330]),0},
+{"id-regCtrl-pkiPublicationInfo","id-regCtrl-pkiPublicationInfo",
+	NID_id_regCtrl_pkiPublicationInfo,9,&(lvalues[2339]),0},
+{"id-regCtrl-pkiArchiveOptions","id-regCtrl-pkiArchiveOptions",
+	NID_id_regCtrl_pkiArchiveOptions,9,&(lvalues[2348]),0},
+{"id-regCtrl-oldCertID","id-regCtrl-oldCertID",
+	NID_id_regCtrl_oldCertID,9,&(lvalues[2357]),0},
+{"id-regCtrl-protocolEncrKey","id-regCtrl-protocolEncrKey",
+	NID_id_regCtrl_protocolEncrKey,9,&(lvalues[2366]),0},
+{"id-regInfo-utf8Pairs","id-regInfo-utf8Pairs",
+	NID_id_regInfo_utf8Pairs,9,&(lvalues[2375]),0},
+{"id-regInfo-certReq","id-regInfo-certReq",NID_id_regInfo_certReq,9,
+	&(lvalues[2384]),0},
+{"id-alg-des40","id-alg-des40",NID_id_alg_des40,8,&(lvalues[2393]),0},
+{"id-alg-noSignature","id-alg-noSignature",NID_id_alg_noSignature,8,
+	&(lvalues[2401]),0},
+{"id-alg-dh-sig-hmac-sha1","id-alg-dh-sig-hmac-sha1",
+	NID_id_alg_dh_sig_hmac_sha1,8,&(lvalues[2409]),0},
+{"id-alg-dh-pop","id-alg-dh-pop",NID_id_alg_dh_pop,8,&(lvalues[2417]),0},
+{"id-cmc-statusInfo","id-cmc-statusInfo",NID_id_cmc_statusInfo,8,
+	&(lvalues[2425]),0},
+{"id-cmc-identification","id-cmc-identification",
+	NID_id_cmc_identification,8,&(lvalues[2433]),0},
+{"id-cmc-identityProof","id-cmc-identityProof",
+	NID_id_cmc_identityProof,8,&(lvalues[2441]),0},
+{"id-cmc-dataReturn","id-cmc-dataReturn",NID_id_cmc_dataReturn,8,
+	&(lvalues[2449]),0},
+{"id-cmc-transactionId","id-cmc-transactionId",
+	NID_id_cmc_transactionId,8,&(lvalues[2457]),0},
+{"id-cmc-senderNonce","id-cmc-senderNonce",NID_id_cmc_senderNonce,8,
+	&(lvalues[2465]),0},
+{"id-cmc-recipientNonce","id-cmc-recipientNonce",
+	NID_id_cmc_recipientNonce,8,&(lvalues[2473]),0},
+{"id-cmc-addExtensions","id-cmc-addExtensions",
+	NID_id_cmc_addExtensions,8,&(lvalues[2481]),0},
+{"id-cmc-encryptedPOP","id-cmc-encryptedPOP",NID_id_cmc_encryptedPOP,
+	8,&(lvalues[2489]),0},
+{"id-cmc-decryptedPOP","id-cmc-decryptedPOP",NID_id_cmc_decryptedPOP,
+	8,&(lvalues[2497]),0},
+{"id-cmc-lraPOPWitness","id-cmc-lraPOPWitness",
+	NID_id_cmc_lraPOPWitness,8,&(lvalues[2505]),0},
+{"id-cmc-getCert","id-cmc-getCert",NID_id_cmc_getCert,8,
+	&(lvalues[2513]),0},
+{"id-cmc-getCRL","id-cmc-getCRL",NID_id_cmc_getCRL,8,&(lvalues[2521]),0},
+{"id-cmc-revokeRequest","id-cmc-revokeRequest",
+	NID_id_cmc_revokeRequest,8,&(lvalues[2529]),0},
+{"id-cmc-regInfo","id-cmc-regInfo",NID_id_cmc_regInfo,8,
+	&(lvalues[2537]),0},
+{"id-cmc-responseInfo","id-cmc-responseInfo",NID_id_cmc_responseInfo,
+	8,&(lvalues[2545]),0},
+{"id-cmc-queryPending","id-cmc-queryPending",NID_id_cmc_queryPending,
+	8,&(lvalues[2553]),0},
+{"id-cmc-popLinkRandom","id-cmc-popLinkRandom",
+	NID_id_cmc_popLinkRandom,8,&(lvalues[2561]),0},
+{"id-cmc-popLinkWitness","id-cmc-popLinkWitness",
+	NID_id_cmc_popLinkWitness,8,&(lvalues[2569]),0},
+{"id-cmc-confirmCertAcceptance","id-cmc-confirmCertAcceptance",
+	NID_id_cmc_confirmCertAcceptance,8,&(lvalues[2577]),0},
+{"id-on-personalData","id-on-personalData",NID_id_on_personalData,8,
+	&(lvalues[2585]),0},
+{"id-pda-dateOfBirth","id-pda-dateOfBirth",NID_id_pda_dateOfBirth,8,
+	&(lvalues[2593]),0},
+{"id-pda-placeOfBirth","id-pda-placeOfBirth",NID_id_pda_placeOfBirth,
+	8,&(lvalues[2601]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"id-pda-gender","id-pda-gender",NID_id_pda_gender,8,&(lvalues[2609]),0},
+{"id-pda-countryOfCitizenship","id-pda-countryOfCitizenship",
+	NID_id_pda_countryOfCitizenship,8,&(lvalues[2617]),0},
+{"id-pda-countryOfResidence","id-pda-countryOfResidence",
+	NID_id_pda_countryOfResidence,8,&(lvalues[2625]),0},
+{"id-aca-authenticationInfo","id-aca-authenticationInfo",
+	NID_id_aca_authenticationInfo,8,&(lvalues[2633]),0},
+{"id-aca-accessIdentity","id-aca-accessIdentity",
+	NID_id_aca_accessIdentity,8,&(lvalues[2641]),0},
+{"id-aca-chargingIdentity","id-aca-chargingIdentity",
+	NID_id_aca_chargingIdentity,8,&(lvalues[2649]),0},
+{"id-aca-group","id-aca-group",NID_id_aca_group,8,&(lvalues[2657]),0},
+{"id-aca-role","id-aca-role",NID_id_aca_role,8,&(lvalues[2665]),0},
+{"id-qcs-pkixQCSyntax-v1","id-qcs-pkixQCSyntax-v1",
+	NID_id_qcs_pkixQCSyntax_v1,8,&(lvalues[2673]),0},
+{"id-cct-crs","id-cct-crs",NID_id_cct_crs,8,&(lvalues[2681]),0},
+{"id-cct-PKIData","id-cct-PKIData",NID_id_cct_PKIData,8,
+	&(lvalues[2689]),0},
+{"id-cct-PKIResponse","id-cct-PKIResponse",NID_id_cct_PKIResponse,8,
+	&(lvalues[2697]),0},
+{"ad_timestamping","AD Time Stamping",NID_ad_timeStamping,8,
+	&(lvalues[2705]),0},
+{"AD_DVCS","ad dvcs",NID_ad_dvcs,8,&(lvalues[2713]),0},
+{"basicOCSPResponse","Basic OCSP Response",NID_id_pkix_OCSP_basic,9,
+	&(lvalues[2721]),0},
+{"Nonce","OCSP Nonce",NID_id_pkix_OCSP_Nonce,9,&(lvalues[2730]),0},
+{"CrlID","OCSP CRL ID",NID_id_pkix_OCSP_CrlID,9,&(lvalues[2739]),0},
+{"acceptableResponses","Acceptable OCSP Responses",
+	NID_id_pkix_OCSP_acceptableResponses,9,&(lvalues[2748]),0},
+{"noCheck","OCSP No Check",NID_id_pkix_OCSP_noCheck,9,&(lvalues[2757]),0},
+{"archiveCutoff","OCSP Archive Cutoff",NID_id_pkix_OCSP_archiveCutoff,
+	9,&(lvalues[2766]),0},
+{"serviceLocator","OCSP Service Locator",
+	NID_id_pkix_OCSP_serviceLocator,9,&(lvalues[2775]),0},
+{"extendedStatus","Extended OCSP Status",
+	NID_id_pkix_OCSP_extendedStatus,9,&(lvalues[2784]),0},
+{"valid","valid",NID_id_pkix_OCSP_valid,9,&(lvalues[2793]),0},
+{"path","path",NID_id_pkix_OCSP_path,9,&(lvalues[2802]),0},
+{"trustRoot","Trust Root",NID_id_pkix_OCSP_trustRoot,9,
+	&(lvalues[2811]),0},
+{"algorithm","algorithm",NID_algorithm,4,&(lvalues[2820]),0},
+{"rsaSignature","rsaSignature",NID_rsaSignature,5,&(lvalues[2824]),0},
+{"X500algorithms","directory services - algorithms",
+	NID_X500algorithms,2,&(lvalues[2829]),0},
+{"ORG","org",NID_org,1,&(lvalues[2831]),0},
+{"DOD","dod",NID_dod,2,&(lvalues[2832]),0},
+{"IANA","iana",NID_iana,3,&(lvalues[2834]),0},
+{"directory","Directory",NID_Directory,4,&(lvalues[2837]),0},
+{"mgmt","Management",NID_Management,4,&(lvalues[2841]),0},
+{"experimental","Experimental",NID_Experimental,4,&(lvalues[2845]),0},
+{"private","Private",NID_Private,4,&(lvalues[2849]),0},
+{"security","Security",NID_Security,4,&(lvalues[2853]),0},
+{"snmpv2","SNMPv2",NID_SNMPv2,4,&(lvalues[2857]),0},
+{"Mail","Mail",NID_Mail,4,&(lvalues[2861]),0},
+{"enterprises","Enterprises",NID_Enterprises,5,&(lvalues[2865]),0},
+{"dcobject","dcObject",NID_dcObject,9,&(lvalues[2870]),0},
+{"DC","domainComponent",NID_domainComponent,10,&(lvalues[2879]),0},
+{"domain","Domain",NID_Domain,10,&(lvalues[2889]),0},
+{"NULL","NULL",NID_joint_iso_ccitt,1,&(lvalues[2899]),0},
+{"selected-attribute-types","Selected Attribute Types",
+	NID_selected_attribute_types,3,&(lvalues[2900]),0},
+{"clearance","clearance",NID_clearance,4,&(lvalues[2903]),0},
+{"RSA-MD4","md4WithRSAEncryption",NID_md4WithRSAEncryption,9,
+	&(lvalues[2907]),0},
+{"ac-proxying","ac-proxying",NID_ac_proxying,8,&(lvalues[2916]),0},
+{"subjectInfoAccess","Subject Information Access",NID_sinfo_access,8,
+	&(lvalues[2924]),0},
+{"id-aca-encAttrs","id-aca-encAttrs",NID_id_aca_encAttrs,8,
+	&(lvalues[2932]),0},
+{"role","role",NID_role,3,&(lvalues[2940]),0},
+{"policyConstraints","X509v3 Policy Constraints",
+	NID_policy_constraints,3,&(lvalues[2943]),0},
+{"targetInformation","X509v3 AC Targeting",NID_target_information,3,
+	&(lvalues[2946]),0},
+{"noRevAvail","X509v3 No Revocation Available",NID_no_rev_avail,3,
+	&(lvalues[2949]),0},
+{"NULL","NULL",NID_ccitt,1,&(lvalues[2952]),0},
+{"ansi-X9-62","ANSI X9.62",NID_ansi_X9_62,5,&(lvalues[2953]),0},
+{"prime-field","prime-field",NID_X9_62_prime_field,7,&(lvalues[2958]),0},
+{"characteristic-two-field","characteristic-two-field",
+	NID_X9_62_characteristic_two_field,7,&(lvalues[2965]),0},
+{"id-ecPublicKey","id-ecPublicKey",NID_X9_62_id_ecPublicKey,7,
+	&(lvalues[2972]),0},
+{"prime192v1","prime192v1",NID_X9_62_prime192v1,8,&(lvalues[2979]),0},
+{"prime192v2","prime192v2",NID_X9_62_prime192v2,8,&(lvalues[2987]),0},
+{"prime192v3","prime192v3",NID_X9_62_prime192v3,8,&(lvalues[2995]),0},
+{"prime239v1","prime239v1",NID_X9_62_prime239v1,8,&(lvalues[3003]),0},
+{"prime239v2","prime239v2",NID_X9_62_prime239v2,8,&(lvalues[3011]),0},
+{"prime239v3","prime239v3",NID_X9_62_prime239v3,8,&(lvalues[3019]),0},
+{"prime256v1","prime256v1",NID_X9_62_prime256v1,8,&(lvalues[3027]),0},
+{"ecdsa-with-SHA1","ecdsa-with-SHA1",NID_ecdsa_with_SHA1,7,
+	&(lvalues[3035]),0},
+{"CSPName","Microsoft CSP Name",NID_ms_csp_name,9,&(lvalues[3042]),0},
+{"AES-128-ECB","aes-128-ecb",NID_aes_128_ecb,9,&(lvalues[3051]),0},
+{"AES-128-CBC","aes-128-cbc",NID_aes_128_cbc,9,&(lvalues[3060]),0},
+{"AES-128-OFB","aes-128-ofb",NID_aes_128_ofb128,9,&(lvalues[3069]),0},
+{"AES-128-CFB","aes-128-cfb",NID_aes_128_cfb128,9,&(lvalues[3078]),0},
+{"AES-192-ECB","aes-192-ecb",NID_aes_192_ecb,9,&(lvalues[3087]),0},
+{"AES-192-CBC","aes-192-cbc",NID_aes_192_cbc,9,&(lvalues[3096]),0},
+{"AES-192-OFB","aes-192-ofb",NID_aes_192_ofb128,9,&(lvalues[3105]),0},
+{"AES-192-CFB","aes-192-cfb",NID_aes_192_cfb128,9,&(lvalues[3114]),0},
+{"AES-256-ECB","aes-256-ecb",NID_aes_256_ecb,9,&(lvalues[3123]),0},
+{"AES-256-CBC","aes-256-cbc",NID_aes_256_cbc,9,&(lvalues[3132]),0},
+{"AES-256-OFB","aes-256-ofb",NID_aes_256_ofb128,9,&(lvalues[3141]),0},
+{"AES-256-CFB","aes-256-cfb",NID_aes_256_cfb128,9,&(lvalues[3150]),0},
+{"holdInstructionCode","Hold Instruction Code",
+	NID_hold_instruction_code,3,&(lvalues[3159]),0},
+{"holdInstructionNone","Hold Instruction None",
+	NID_hold_instruction_none,7,&(lvalues[3162]),0},
+{"holdInstructionCallIssuer","Hold Instruction Call Issuer",
+	NID_hold_instruction_call_issuer,7,&(lvalues[3169]),0},
+{"holdInstructionReject","Hold Instruction Reject",
+	NID_hold_instruction_reject,7,&(lvalues[3176]),0},
+{"data","data",NID_data,1,&(lvalues[3183]),0},
+{"pss","pss",NID_pss,3,&(lvalues[3184]),0},
+{"ucl","ucl",NID_ucl,7,&(lvalues[3187]),0},
+{"pilot","pilot",NID_pilot,8,&(lvalues[3194]),0},
+{"pilotAttributeType","pilotAttributeType",NID_pilotAttributeType,9,
+	&(lvalues[3202]),0},
+{"pilotAttributeSyntax","pilotAttributeSyntax",
+	NID_pilotAttributeSyntax,9,&(lvalues[3211]),0},
+{"pilotObjectClass","pilotObjectClass",NID_pilotObjectClass,9,
+	&(lvalues[3220]),0},
+{"pilotGroups","pilotGroups",NID_pilotGroups,9,&(lvalues[3229]),0},
+{"iA5StringSyntax","iA5StringSyntax",NID_iA5StringSyntax,10,
+	&(lvalues[3238]),0},
+{"caseIgnoreIA5StringSyntax","caseIgnoreIA5StringSyntax",
+	NID_caseIgnoreIA5StringSyntax,10,&(lvalues[3248]),0},
+{"pilotObject","pilotObject",NID_pilotObject,10,&(lvalues[3258]),0},
+{"pilotPerson","pilotPerson",NID_pilotPerson,10,&(lvalues[3268]),0},
+{"account","account",NID_account,10,&(lvalues[3278]),0},
+{"document","document",NID_document,10,&(lvalues[3288]),0},
+{"room","room",NID_room,10,&(lvalues[3298]),0},
+{"documentSeries","documentSeries",NID_documentSeries,10,
+	&(lvalues[3308]),0},
+{"rFC822localPart","rFC822localPart",NID_rFC822localPart,10,
+	&(lvalues[3318]),0},
+{"dNSDomain","dNSDomain",NID_dNSDomain,10,&(lvalues[3328]),0},
+{"domainRelatedObject","domainRelatedObject",NID_domainRelatedObject,
+	10,&(lvalues[3338]),0},
+{"friendlyCountry","friendlyCountry",NID_friendlyCountry,10,
+	&(lvalues[3348]),0},
+{"simpleSecurityObject","simpleSecurityObject",
+	NID_simpleSecurityObject,10,&(lvalues[3358]),0},
+{"pilotOrganization","pilotOrganization",NID_pilotOrganization,10,
+	&(lvalues[3368]),0},
+{"pilotDSA","pilotDSA",NID_pilotDSA,10,&(lvalues[3378]),0},
+{"qualityLabelledData","qualityLabelledData",NID_qualityLabelledData,
+	10,&(lvalues[3388]),0},
+{"UID","userId",NID_userId,10,&(lvalues[3398]),0},
+{"textEncodedORAddress","textEncodedORAddress",
+	NID_textEncodedORAddress,10,&(lvalues[3408]),0},
+{"mail","rfc822Mailbox",NID_rfc822Mailbox,10,&(lvalues[3418]),0},
+{"info","info",NID_info,10,&(lvalues[3428]),0},
+{"favouriteDrink","favouriteDrink",NID_favouriteDrink,10,
+	&(lvalues[3438]),0},
+{"roomNumber","roomNumber",NID_roomNumber,10,&(lvalues[3448]),0},
+{"photo","photo",NID_photo,10,&(lvalues[3458]),0},
+{"userClass","userClass",NID_userClass,10,&(lvalues[3468]),0},
+{"host","host",NID_host,10,&(lvalues[3478]),0},
+{"manager","manager",NID_manager,10,&(lvalues[3488]),0},
+{"documentIdentifier","documentIdentifier",NID_documentIdentifier,10,
+	&(lvalues[3498]),0},
+{"documentTitle","documentTitle",NID_documentTitle,10,&(lvalues[3508]),0},
+{"documentVersion","documentVersion",NID_documentVersion,10,
+	&(lvalues[3518]),0},
+{"documentAuthor","documentAuthor",NID_documentAuthor,10,
+	&(lvalues[3528]),0},
+{"documentLocation","documentLocation",NID_documentLocation,10,
+	&(lvalues[3538]),0},
+{"homeTelephoneNumber","homeTelephoneNumber",NID_homeTelephoneNumber,
+	10,&(lvalues[3548]),0},
+{"secretary","secretary",NID_secretary,10,&(lvalues[3558]),0},
+{"otherMailbox","otherMailbox",NID_otherMailbox,10,&(lvalues[3568]),0},
+{"lastModifiedTime","lastModifiedTime",NID_lastModifiedTime,10,
+	&(lvalues[3578]),0},
+{"lastModifiedBy","lastModifiedBy",NID_lastModifiedBy,10,
+	&(lvalues[3588]),0},
+{"aRecord","aRecord",NID_aRecord,10,&(lvalues[3598]),0},
+{"pilotAttributeType27","pilotAttributeType27",
+	NID_pilotAttributeType27,10,&(lvalues[3608]),0},
+{"mXRecord","mXRecord",NID_mXRecord,10,&(lvalues[3618]),0},
+{"nSRecord","nSRecord",NID_nSRecord,10,&(lvalues[3628]),0},
+{"sOARecord","sOARecord",NID_sOARecord,10,&(lvalues[3638]),0},
+{"cNAMERecord","cNAMERecord",NID_cNAMERecord,10,&(lvalues[3648]),0},
+{"associatedDomain","associatedDomain",NID_associatedDomain,10,
+	&(lvalues[3658]),0},
+{"associatedName","associatedName",NID_associatedName,10,
+	&(lvalues[3668]),0},
+{"homePostalAddress","homePostalAddress",NID_homePostalAddress,10,
+	&(lvalues[3678]),0},
+{"personalTitle","personalTitle",NID_personalTitle,10,&(lvalues[3688]),0},
+{"mobileTelephoneNumber","mobileTelephoneNumber",
+	NID_mobileTelephoneNumber,10,&(lvalues[3698]),0},
+{"pagerTelephoneNumber","pagerTelephoneNumber",
+	NID_pagerTelephoneNumber,10,&(lvalues[3708]),0},
+{"friendlyCountryName","friendlyCountryName",NID_friendlyCountryName,
+	10,&(lvalues[3718]),0},
+{"organizationalStatus","organizationalStatus",
+	NID_organizationalStatus,10,&(lvalues[3728]),0},
+{"janetMailbox","janetMailbox",NID_janetMailbox,10,&(lvalues[3738]),0},
+{"mailPreferenceOption","mailPreferenceOption",
+	NID_mailPreferenceOption,10,&(lvalues[3748]),0},
+{"buildingName","buildingName",NID_buildingName,10,&(lvalues[3758]),0},
+{"dSAQuality","dSAQuality",NID_dSAQuality,10,&(lvalues[3768]),0},
+{"singleLevelQuality","singleLevelQuality",NID_singleLevelQuality,10,
+	&(lvalues[3778]),0},
+{"subtreeMinimumQuality","subtreeMinimumQuality",
+	NID_subtreeMinimumQuality,10,&(lvalues[3788]),0},
+{"subtreeMaximumQuality","subtreeMaximumQuality",
+	NID_subtreeMaximumQuality,10,&(lvalues[3798]),0},
+{"personalSignature","personalSignature",NID_personalSignature,10,
+	&(lvalues[3808]),0},
+{"dITRedirect","dITRedirect",NID_dITRedirect,10,&(lvalues[3818]),0},
+{"audio","audio",NID_audio,10,&(lvalues[3828]),0},
+{"documentPublisher","documentPublisher",NID_documentPublisher,10,
+	&(lvalues[3838]),0},
+{"x500UniqueIdentifier","x500UniqueIdentifier",
+	NID_x500UniqueIdentifier,3,&(lvalues[3848]),0},
+{"mime-mhs","MIME MHS",NID_mime_mhs,5,&(lvalues[3851]),0},
+{"mime-mhs-headings","mime-mhs-headings",NID_mime_mhs_headings,6,
+	&(lvalues[3856]),0},
+{"mime-mhs-bodies","mime-mhs-bodies",NID_mime_mhs_bodies,6,
+	&(lvalues[3862]),0},
+{"id-hex-partial-message","id-hex-partial-message",
+	NID_id_hex_partial_message,7,&(lvalues[3868]),0},
+{"id-hex-multipart-message","id-hex-multipart-message",
+	NID_id_hex_multipart_message,7,&(lvalues[3875]),0},
+{"generationQualifier","generationQualifier",NID_generationQualifier,
+	3,&(lvalues[3882]),0},
+{"pseudonym","pseudonym",NID_pseudonym,3,&(lvalues[3885]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"id-set","Secure Electronic Transactions",NID_id_set,2,
+	&(lvalues[3888]),0},
+{"set-ctype","content types",NID_set_ctype,3,&(lvalues[3890]),0},
+{"set-msgExt","message extensions",NID_set_msgExt,3,&(lvalues[3893]),0},
+{"set-attr","set-attr",NID_set_attr,3,&(lvalues[3896]),0},
+{"set-policy","set-policy",NID_set_policy,3,&(lvalues[3899]),0},
+{"set-certExt","certificate extensions",NID_set_certExt,3,
+	&(lvalues[3902]),0},
+{"set-brand","set-brand",NID_set_brand,3,&(lvalues[3905]),0},
+{"setct-PANData","setct-PANData",NID_setct_PANData,4,&(lvalues[3908]),0},
+{"setct-PANToken","setct-PANToken",NID_setct_PANToken,4,
+	&(lvalues[3912]),0},
+{"setct-PANOnly","setct-PANOnly",NID_setct_PANOnly,4,&(lvalues[3916]),0},
+{"setct-OIData","setct-OIData",NID_setct_OIData,4,&(lvalues[3920]),0},
+{"setct-PI","setct-PI",NID_setct_PI,4,&(lvalues[3924]),0},
+{"setct-PIData","setct-PIData",NID_setct_PIData,4,&(lvalues[3928]),0},
+{"setct-PIDataUnsigned","setct-PIDataUnsigned",
+	NID_setct_PIDataUnsigned,4,&(lvalues[3932]),0},
+{"setct-HODInput","setct-HODInput",NID_setct_HODInput,4,
+	&(lvalues[3936]),0},
+{"setct-AuthResBaggage","setct-AuthResBaggage",
+	NID_setct_AuthResBaggage,4,&(lvalues[3940]),0},
+{"setct-AuthRevReqBaggage","setct-AuthRevReqBaggage",
+	NID_setct_AuthRevReqBaggage,4,&(lvalues[3944]),0},
+{"setct-AuthRevResBaggage","setct-AuthRevResBaggage",
+	NID_setct_AuthRevResBaggage,4,&(lvalues[3948]),0},
+{"setct-CapTokenSeq","setct-CapTokenSeq",NID_setct_CapTokenSeq,4,
+	&(lvalues[3952]),0},
+{"setct-PInitResData","setct-PInitResData",NID_setct_PInitResData,4,
+	&(lvalues[3956]),0},
+{"setct-PI-TBS","setct-PI-TBS",NID_setct_PI_TBS,4,&(lvalues[3960]),0},
+{"setct-PResData","setct-PResData",NID_setct_PResData,4,
+	&(lvalues[3964]),0},
+{"setct-AuthReqTBS","setct-AuthReqTBS",NID_setct_AuthReqTBS,4,
+	&(lvalues[3968]),0},
+{"setct-AuthResTBS","setct-AuthResTBS",NID_setct_AuthResTBS,4,
+	&(lvalues[3972]),0},
+{"setct-AuthResTBSX","setct-AuthResTBSX",NID_setct_AuthResTBSX,4,
+	&(lvalues[3976]),0},
+{"setct-AuthTokenTBS","setct-AuthTokenTBS",NID_setct_AuthTokenTBS,4,
+	&(lvalues[3980]),0},
+{"setct-CapTokenData","setct-CapTokenData",NID_setct_CapTokenData,4,
+	&(lvalues[3984]),0},
+{"setct-CapTokenTBS","setct-CapTokenTBS",NID_setct_CapTokenTBS,4,
+	&(lvalues[3988]),0},
+{"setct-AcqCardCodeMsg","setct-AcqCardCodeMsg",
+	NID_setct_AcqCardCodeMsg,4,&(lvalues[3992]),0},
+{"setct-AuthRevReqTBS","setct-AuthRevReqTBS",NID_setct_AuthRevReqTBS,
+	4,&(lvalues[3996]),0},
+{"setct-AuthRevResData","setct-AuthRevResData",
+	NID_setct_AuthRevResData,4,&(lvalues[4000]),0},
+{"setct-AuthRevResTBS","setct-AuthRevResTBS",NID_setct_AuthRevResTBS,
+	4,&(lvalues[4004]),0},
+{"setct-CapReqTBS","setct-CapReqTBS",NID_setct_CapReqTBS,4,
+	&(lvalues[4008]),0},
+{"setct-CapReqTBSX","setct-CapReqTBSX",NID_setct_CapReqTBSX,4,
+	&(lvalues[4012]),0},
+{"setct-CapResData","setct-CapResData",NID_setct_CapResData,4,
+	&(lvalues[4016]),0},
+{"setct-CapRevReqTBS","setct-CapRevReqTBS",NID_setct_CapRevReqTBS,4,
+	&(lvalues[4020]),0},
+{"setct-CapRevReqTBSX","setct-CapRevReqTBSX",NID_setct_CapRevReqTBSX,
+	4,&(lvalues[4024]),0},
+{"setct-CapRevResData","setct-CapRevResData",NID_setct_CapRevResData,
+	4,&(lvalues[4028]),0},
+{"setct-CredReqTBS","setct-CredReqTBS",NID_setct_CredReqTBS,4,
+	&(lvalues[4032]),0},
+{"setct-CredReqTBSX","setct-CredReqTBSX",NID_setct_CredReqTBSX,4,
+	&(lvalues[4036]),0},
+{"setct-CredResData","setct-CredResData",NID_setct_CredResData,4,
+	&(lvalues[4040]),0},
+{"setct-CredRevReqTBS","setct-CredRevReqTBS",NID_setct_CredRevReqTBS,
+	4,&(lvalues[4044]),0},
+{"setct-CredRevReqTBSX","setct-CredRevReqTBSX",
+	NID_setct_CredRevReqTBSX,4,&(lvalues[4048]),0},
+{"setct-CredRevResData","setct-CredRevResData",
+	NID_setct_CredRevResData,4,&(lvalues[4052]),0},
+{"setct-PCertReqData","setct-PCertReqData",NID_setct_PCertReqData,4,
+	&(lvalues[4056]),0},
+{"setct-PCertResTBS","setct-PCertResTBS",NID_setct_PCertResTBS,4,
+	&(lvalues[4060]),0},
+{"setct-BatchAdminReqData","setct-BatchAdminReqData",
+	NID_setct_BatchAdminReqData,4,&(lvalues[4064]),0},
+{"setct-BatchAdminResData","setct-BatchAdminResData",
+	NID_setct_BatchAdminResData,4,&(lvalues[4068]),0},
+{"setct-CardCInitResTBS","setct-CardCInitResTBS",
+	NID_setct_CardCInitResTBS,4,&(lvalues[4072]),0},
+{"setct-MeAqCInitResTBS","setct-MeAqCInitResTBS",
+	NID_setct_MeAqCInitResTBS,4,&(lvalues[4076]),0},
+{"setct-RegFormResTBS","setct-RegFormResTBS",NID_setct_RegFormResTBS,
+	4,&(lvalues[4080]),0},
+{"setct-CertReqData","setct-CertReqData",NID_setct_CertReqData,4,
+	&(lvalues[4084]),0},
+{"setct-CertReqTBS","setct-CertReqTBS",NID_setct_CertReqTBS,4,
+	&(lvalues[4088]),0},
+{"setct-CertResData","setct-CertResData",NID_setct_CertResData,4,
+	&(lvalues[4092]),0},
+{"setct-CertInqReqTBS","setct-CertInqReqTBS",NID_setct_CertInqReqTBS,
+	4,&(lvalues[4096]),0},
+{"setct-ErrorTBS","setct-ErrorTBS",NID_setct_ErrorTBS,4,
+	&(lvalues[4100]),0},
+{"setct-PIDualSignedTBE","setct-PIDualSignedTBE",
+	NID_setct_PIDualSignedTBE,4,&(lvalues[4104]),0},
+{"setct-PIUnsignedTBE","setct-PIUnsignedTBE",NID_setct_PIUnsignedTBE,
+	4,&(lvalues[4108]),0},
+{"setct-AuthReqTBE","setct-AuthReqTBE",NID_setct_AuthReqTBE,4,
+	&(lvalues[4112]),0},
+{"setct-AuthResTBE","setct-AuthResTBE",NID_setct_AuthResTBE,4,
+	&(lvalues[4116]),0},
+{"setct-AuthResTBEX","setct-AuthResTBEX",NID_setct_AuthResTBEX,4,
+	&(lvalues[4120]),0},
+{"setct-AuthTokenTBE","setct-AuthTokenTBE",NID_setct_AuthTokenTBE,4,
+	&(lvalues[4124]),0},
+{"setct-CapTokenTBE","setct-CapTokenTBE",NID_setct_CapTokenTBE,4,
+	&(lvalues[4128]),0},
+{"setct-CapTokenTBEX","setct-CapTokenTBEX",NID_setct_CapTokenTBEX,4,
+	&(lvalues[4132]),0},
+{"setct-AcqCardCodeMsgTBE","setct-AcqCardCodeMsgTBE",
+	NID_setct_AcqCardCodeMsgTBE,4,&(lvalues[4136]),0},
+{"setct-AuthRevReqTBE","setct-AuthRevReqTBE",NID_setct_AuthRevReqTBE,
+	4,&(lvalues[4140]),0},
+{"setct-AuthRevResTBE","setct-AuthRevResTBE",NID_setct_AuthRevResTBE,
+	4,&(lvalues[4144]),0},
+{"setct-AuthRevResTBEB","setct-AuthRevResTBEB",
+	NID_setct_AuthRevResTBEB,4,&(lvalues[4148]),0},
+{"setct-CapReqTBE","setct-CapReqTBE",NID_setct_CapReqTBE,4,
+	&(lvalues[4152]),0},
+{"setct-CapReqTBEX","setct-CapReqTBEX",NID_setct_CapReqTBEX,4,
+	&(lvalues[4156]),0},
+{"setct-CapResTBE","setct-CapResTBE",NID_setct_CapResTBE,4,
+	&(lvalues[4160]),0},
+{"setct-CapRevReqTBE","setct-CapRevReqTBE",NID_setct_CapRevReqTBE,4,
+	&(lvalues[4164]),0},
+{"setct-CapRevReqTBEX","setct-CapRevReqTBEX",NID_setct_CapRevReqTBEX,
+	4,&(lvalues[4168]),0},
+{"setct-CapRevResTBE","setct-CapRevResTBE",NID_setct_CapRevResTBE,4,
+	&(lvalues[4172]),0},
+{"setct-CredReqTBE","setct-CredReqTBE",NID_setct_CredReqTBE,4,
+	&(lvalues[4176]),0},
+{"setct-CredReqTBEX","setct-CredReqTBEX",NID_setct_CredReqTBEX,4,
+	&(lvalues[4180]),0},
+{"setct-CredResTBE","setct-CredResTBE",NID_setct_CredResTBE,4,
+	&(lvalues[4184]),0},
+{"setct-CredRevReqTBE","setct-CredRevReqTBE",NID_setct_CredRevReqTBE,
+	4,&(lvalues[4188]),0},
+{"setct-CredRevReqTBEX","setct-CredRevReqTBEX",
+	NID_setct_CredRevReqTBEX,4,&(lvalues[4192]),0},
+{"setct-CredRevResTBE","setct-CredRevResTBE",NID_setct_CredRevResTBE,
+	4,&(lvalues[4196]),0},
+{"setct-BatchAdminReqTBE","setct-BatchAdminReqTBE",
+	NID_setct_BatchAdminReqTBE,4,&(lvalues[4200]),0},
+{"setct-BatchAdminResTBE","setct-BatchAdminResTBE",
+	NID_setct_BatchAdminResTBE,4,&(lvalues[4204]),0},
+{"setct-RegFormReqTBE","setct-RegFormReqTBE",NID_setct_RegFormReqTBE,
+	4,&(lvalues[4208]),0},
+{"setct-CertReqTBE","setct-CertReqTBE",NID_setct_CertReqTBE,4,
+	&(lvalues[4212]),0},
+{"setct-CertReqTBEX","setct-CertReqTBEX",NID_setct_CertReqTBEX,4,
+	&(lvalues[4216]),0},
+{"setct-CertResTBE","setct-CertResTBE",NID_setct_CertResTBE,4,
+	&(lvalues[4220]),0},
+{"setct-CRLNotificationTBS","setct-CRLNotificationTBS",
+	NID_setct_CRLNotificationTBS,4,&(lvalues[4224]),0},
+{"setct-CRLNotificationResTBS","setct-CRLNotificationResTBS",
+	NID_setct_CRLNotificationResTBS,4,&(lvalues[4228]),0},
+{"setct-BCIDistributionTBS","setct-BCIDistributionTBS",
+	NID_setct_BCIDistributionTBS,4,&(lvalues[4232]),0},
+{"setext-genCrypt","generic cryptogram",NID_setext_genCrypt,4,
+	&(lvalues[4236]),0},
+{"setext-miAuth","merchant initiated auth",NID_setext_miAuth,4,
+	&(lvalues[4240]),0},
+{"setext-pinSecure","setext-pinSecure",NID_setext_pinSecure,4,
+	&(lvalues[4244]),0},
+{"setext-pinAny","setext-pinAny",NID_setext_pinAny,4,&(lvalues[4248]),0},
+{"setext-track2","setext-track2",NID_setext_track2,4,&(lvalues[4252]),0},
+{"setext-cv","additional verification",NID_setext_cv,4,
+	&(lvalues[4256]),0},
+{"set-policy-root","set-policy-root",NID_set_policy_root,4,
+	&(lvalues[4260]),0},
+{"setCext-hashedRoot","setCext-hashedRoot",NID_setCext_hashedRoot,4,
+	&(lvalues[4264]),0},
+{"setCext-certType","setCext-certType",NID_setCext_certType,4,
+	&(lvalues[4268]),0},
+{"setCext-merchData","setCext-merchData",NID_setCext_merchData,4,
+	&(lvalues[4272]),0},
+{"setCext-cCertRequired","setCext-cCertRequired",
+	NID_setCext_cCertRequired,4,&(lvalues[4276]),0},
+{"setCext-tunneling","setCext-tunneling",NID_setCext_tunneling,4,
+	&(lvalues[4280]),0},
+{"setCext-setExt","setCext-setExt",NID_setCext_setExt,4,
+	&(lvalues[4284]),0},
+{"setCext-setQualf","setCext-setQualf",NID_setCext_setQualf,4,
+	&(lvalues[4288]),0},
+{"setCext-PGWYcapabilities","setCext-PGWYcapabilities",
+	NID_setCext_PGWYcapabilities,4,&(lvalues[4292]),0},
+{"setCext-TokenIdentifier","setCext-TokenIdentifier",
+	NID_setCext_TokenIdentifier,4,&(lvalues[4296]),0},
+{"setCext-Track2Data","setCext-Track2Data",NID_setCext_Track2Data,4,
+	&(lvalues[4300]),0},
+{"setCext-TokenType","setCext-TokenType",NID_setCext_TokenType,4,
+	&(lvalues[4304]),0},
+{"setCext-IssuerCapabilities","setCext-IssuerCapabilities",
+	NID_setCext_IssuerCapabilities,4,&(lvalues[4308]),0},
+{"setAttr-Cert","setAttr-Cert",NID_setAttr_Cert,4,&(lvalues[4312]),0},
+{"setAttr-PGWYcap","payment gateway capabilities",NID_setAttr_PGWYcap,
+	4,&(lvalues[4316]),0},
+{"setAttr-TokenType","setAttr-TokenType",NID_setAttr_TokenType,4,
+	&(lvalues[4320]),0},
+{"setAttr-IssCap","issuer capabilities",NID_setAttr_IssCap,4,
+	&(lvalues[4324]),0},
+{"set-rootKeyThumb","set-rootKeyThumb",NID_set_rootKeyThumb,5,
+	&(lvalues[4328]),0},
+{"set-addPolicy","set-addPolicy",NID_set_addPolicy,5,&(lvalues[4333]),0},
+{"setAttr-Token-EMV","setAttr-Token-EMV",NID_setAttr_Token_EMV,5,
+	&(lvalues[4338]),0},
+{"setAttr-Token-B0Prime","setAttr-Token-B0Prime",
+	NID_setAttr_Token_B0Prime,5,&(lvalues[4343]),0},
+{"setAttr-IssCap-CVM","setAttr-IssCap-CVM",NID_setAttr_IssCap_CVM,5,
+	&(lvalues[4348]),0},
+{"setAttr-IssCap-T2","setAttr-IssCap-T2",NID_setAttr_IssCap_T2,5,
+	&(lvalues[4353]),0},
+{"setAttr-IssCap-Sig","setAttr-IssCap-Sig",NID_setAttr_IssCap_Sig,5,
+	&(lvalues[4358]),0},
+{"setAttr-GenCryptgrm","generate cryptogram",NID_setAttr_GenCryptgrm,
+	6,&(lvalues[4363]),0},
+{"setAttr-T2Enc","encrypted track 2",NID_setAttr_T2Enc,6,
+	&(lvalues[4369]),0},
+{"setAttr-T2cleartxt","cleartext track 2",NID_setAttr_T2cleartxt,6,
+	&(lvalues[4375]),0},
+{"setAttr-TokICCsig","ICC or token signature",NID_setAttr_TokICCsig,6,
+	&(lvalues[4381]),0},
+{"setAttr-SecDevSig","secure device signature",NID_setAttr_SecDevSig,
+	6,&(lvalues[4387]),0},
+{"set-brand-IATA-ATA","set-brand-IATA-ATA",NID_set_brand_IATA_ATA,4,
+	&(lvalues[4393]),0},
+{"set-brand-Diners","set-brand-Diners",NID_set_brand_Diners,4,
+	&(lvalues[4397]),0},
+{"set-brand-AmericanExpress","set-brand-AmericanExpress",
+	NID_set_brand_AmericanExpress,4,&(lvalues[4401]),0},
+{"set-brand-JCB","set-brand-JCB",NID_set_brand_JCB,4,&(lvalues[4405]),0},
+{"set-brand-Visa","set-brand-Visa",NID_set_brand_Visa,4,
+	&(lvalues[4409]),0},
+{"set-brand-MasterCard","set-brand-MasterCard",
+	NID_set_brand_MasterCard,4,&(lvalues[4413]),0},
+{"set-brand-Novus","set-brand-Novus",NID_set_brand_Novus,5,
+	&(lvalues[4417]),0},
+{"DES-CDMF","des-cdmf",NID_des_cdmf,8,&(lvalues[4422]),0},
+{"rsaOAEPEncryptionSET","rsaOAEPEncryptionSET",
+	NID_rsaOAEPEncryptionSET,9,&(lvalues[4430]),0},
+{"ITU-T","itu-t",NID_itu_t,1,&(lvalues[4439]),0},
+{"JOINT-ISO-ITU-T","joint-iso-itu-t",NID_joint_iso_itu_t,1,
+	&(lvalues[4440]),0},
+{"international-organizations","International Organizations",
+	NID_international_organizations,1,&(lvalues[4441]),0},
+{"msSmartcardLogin","Microsoft Smartcardlogin",NID_ms_smartcard_login,
+	10,&(lvalues[4442]),0},
+{"msUPN","Microsoft Universal Principal Name",NID_ms_upn,10,
+	&(lvalues[4452]),0},
+{"AES-128-CFB1","aes-128-cfb1",NID_aes_128_cfb1,0,NULL,0},
+{"AES-192-CFB1","aes-192-cfb1",NID_aes_192_cfb1,0,NULL,0},
+{"AES-256-CFB1","aes-256-cfb1",NID_aes_256_cfb1,0,NULL,0},
+{"AES-128-CFB8","aes-128-cfb8",NID_aes_128_cfb8,0,NULL,0},
+{"AES-192-CFB8","aes-192-cfb8",NID_aes_192_cfb8,0,NULL,0},
+{"AES-256-CFB8","aes-256-cfb8",NID_aes_256_cfb8,0,NULL,0},
+{"DES-CFB1","des-cfb1",NID_des_cfb1,0,NULL,0},
+{"DES-CFB8","des-cfb8",NID_des_cfb8,0,NULL,0},
+{"DES-EDE3-CFB1","des-ede3-cfb1",NID_des_ede3_cfb1,0,NULL,0},
+{"DES-EDE3-CFB8","des-ede3-cfb8",NID_des_ede3_cfb8,0,NULL,0},
+{"street","streetAddress",NID_streetAddress,3,&(lvalues[4462]),0},
+{"postalCode","postalCode",NID_postalCode,3,&(lvalues[4465]),0},
+{"id-ppl","id-ppl",NID_id_ppl,7,&(lvalues[4468]),0},
+{"proxyCertInfo","Proxy Certificate Information",NID_proxyCertInfo,8,
+	&(lvalues[4475]),0},
+{"id-ppl-anyLanguage","Any language",NID_id_ppl_anyLanguage,8,
+	&(lvalues[4483]),0},
+{"id-ppl-inheritAll","Inherit all",NID_id_ppl_inheritAll,8,
+	&(lvalues[4491]),0},
+{"nameConstraints","X509v3 Name Constraints",NID_name_constraints,3,
+	&(lvalues[4499]),0},
+{"id-ppl-independent","Independent",NID_Independent,8,&(lvalues[4502]),0},
+{"RSA-SHA256","sha256WithRSAEncryption",NID_sha256WithRSAEncryption,9,
+	&(lvalues[4510]),0},
+{"RSA-SHA384","sha384WithRSAEncryption",NID_sha384WithRSAEncryption,9,
+	&(lvalues[4519]),0},
+{"RSA-SHA512","sha512WithRSAEncryption",NID_sha512WithRSAEncryption,9,
+	&(lvalues[4528]),0},
+{"RSA-SHA224","sha224WithRSAEncryption",NID_sha224WithRSAEncryption,9,
+	&(lvalues[4537]),0},
+{"SHA256","sha256",NID_sha256,9,&(lvalues[4546]),0},
+{"SHA384","sha384",NID_sha384,9,&(lvalues[4555]),0},
+{"SHA512","sha512",NID_sha512,9,&(lvalues[4564]),0},
+{"SHA224","sha224",NID_sha224,9,&(lvalues[4573]),0},
+{"identified-organization","identified-organization",
+	NID_identified_organization,1,&(lvalues[4582]),0},
+{"certicom-arc","certicom-arc",NID_certicom_arc,3,&(lvalues[4583]),0},
+{"wap","wap",NID_wap,2,&(lvalues[4586]),0},
+{"wap-wsg","wap-wsg",NID_wap_wsg,3,&(lvalues[4588]),0},
+{"id-characteristic-two-basis","id-characteristic-two-basis",
+	NID_X9_62_id_characteristic_two_basis,8,&(lvalues[4591]),0},
+{"onBasis","onBasis",NID_X9_62_onBasis,9,&(lvalues[4599]),0},
+{"tpBasis","tpBasis",NID_X9_62_tpBasis,9,&(lvalues[4608]),0},
+{"ppBasis","ppBasis",NID_X9_62_ppBasis,9,&(lvalues[4617]),0},
+{"c2pnb163v1","c2pnb163v1",NID_X9_62_c2pnb163v1,8,&(lvalues[4626]),0},
+{"c2pnb163v2","c2pnb163v2",NID_X9_62_c2pnb163v2,8,&(lvalues[4634]),0},
+{"c2pnb163v3","c2pnb163v3",NID_X9_62_c2pnb163v3,8,&(lvalues[4642]),0},
+{"c2pnb176v1","c2pnb176v1",NID_X9_62_c2pnb176v1,8,&(lvalues[4650]),0},
+{"c2tnb191v1","c2tnb191v1",NID_X9_62_c2tnb191v1,8,&(lvalues[4658]),0},
+{"c2tnb191v2","c2tnb191v2",NID_X9_62_c2tnb191v2,8,&(lvalues[4666]),0},
+{"c2tnb191v3","c2tnb191v3",NID_X9_62_c2tnb191v3,8,&(lvalues[4674]),0},
+{"c2onb191v4","c2onb191v4",NID_X9_62_c2onb191v4,8,&(lvalues[4682]),0},
+{"c2onb191v5","c2onb191v5",NID_X9_62_c2onb191v5,8,&(lvalues[4690]),0},
+{"c2pnb208w1","c2pnb208w1",NID_X9_62_c2pnb208w1,8,&(lvalues[4698]),0},
+{"c2tnb239v1","c2tnb239v1",NID_X9_62_c2tnb239v1,8,&(lvalues[4706]),0},
+{"c2tnb239v2","c2tnb239v2",NID_X9_62_c2tnb239v2,8,&(lvalues[4714]),0},
+{"c2tnb239v3","c2tnb239v3",NID_X9_62_c2tnb239v3,8,&(lvalues[4722]),0},
+{"c2onb239v4","c2onb239v4",NID_X9_62_c2onb239v4,8,&(lvalues[4730]),0},
+{"c2onb239v5","c2onb239v5",NID_X9_62_c2onb239v5,8,&(lvalues[4738]),0},
+{"c2pnb272w1","c2pnb272w1",NID_X9_62_c2pnb272w1,8,&(lvalues[4746]),0},
+{"c2pnb304w1","c2pnb304w1",NID_X9_62_c2pnb304w1,8,&(lvalues[4754]),0},
+{"c2tnb359v1","c2tnb359v1",NID_X9_62_c2tnb359v1,8,&(lvalues[4762]),0},
+{"c2pnb368w1","c2pnb368w1",NID_X9_62_c2pnb368w1,8,&(lvalues[4770]),0},
+{"c2tnb431r1","c2tnb431r1",NID_X9_62_c2tnb431r1,8,&(lvalues[4778]),0},
+{"secp112r1","secp112r1",NID_secp112r1,5,&(lvalues[4786]),0},
+{"secp112r2","secp112r2",NID_secp112r2,5,&(lvalues[4791]),0},
+{"secp128r1","secp128r1",NID_secp128r1,5,&(lvalues[4796]),0},
+{"secp128r2","secp128r2",NID_secp128r2,5,&(lvalues[4801]),0},
+{"secp160k1","secp160k1",NID_secp160k1,5,&(lvalues[4806]),0},
+{"secp160r1","secp160r1",NID_secp160r1,5,&(lvalues[4811]),0},
+{"secp160r2","secp160r2",NID_secp160r2,5,&(lvalues[4816]),0},
+{"secp192k1","secp192k1",NID_secp192k1,5,&(lvalues[4821]),0},
+{"secp224k1","secp224k1",NID_secp224k1,5,&(lvalues[4826]),0},
+{"secp224r1","secp224r1",NID_secp224r1,5,&(lvalues[4831]),0},
+{"secp256k1","secp256k1",NID_secp256k1,5,&(lvalues[4836]),0},
+{"secp384r1","secp384r1",NID_secp384r1,5,&(lvalues[4841]),0},
+{"secp521r1","secp521r1",NID_secp521r1,5,&(lvalues[4846]),0},
+{"sect113r1","sect113r1",NID_sect113r1,5,&(lvalues[4851]),0},
+{"sect113r2","sect113r2",NID_sect113r2,5,&(lvalues[4856]),0},
+{"sect131r1","sect131r1",NID_sect131r1,5,&(lvalues[4861]),0},
+{"sect131r2","sect131r2",NID_sect131r2,5,&(lvalues[4866]),0},
+{"sect163k1","sect163k1",NID_sect163k1,5,&(lvalues[4871]),0},
+{"sect163r1","sect163r1",NID_sect163r1,5,&(lvalues[4876]),0},
+{"sect163r2","sect163r2",NID_sect163r2,5,&(lvalues[4881]),0},
+{"sect193r1","sect193r1",NID_sect193r1,5,&(lvalues[4886]),0},
+{"sect193r2","sect193r2",NID_sect193r2,5,&(lvalues[4891]),0},
+{"sect233k1","sect233k1",NID_sect233k1,5,&(lvalues[4896]),0},
+{"sect233r1","sect233r1",NID_sect233r1,5,&(lvalues[4901]),0},
+{"sect239k1","sect239k1",NID_sect239k1,5,&(lvalues[4906]),0},
+{"sect283k1","sect283k1",NID_sect283k1,5,&(lvalues[4911]),0},
+{"sect283r1","sect283r1",NID_sect283r1,5,&(lvalues[4916]),0},
+{"sect409k1","sect409k1",NID_sect409k1,5,&(lvalues[4921]),0},
+{"sect409r1","sect409r1",NID_sect409r1,5,&(lvalues[4926]),0},
+{"sect571k1","sect571k1",NID_sect571k1,5,&(lvalues[4931]),0},
+{"sect571r1","sect571r1",NID_sect571r1,5,&(lvalues[4936]),0},
+{"wap-wsg-idm-ecid-wtls1","wap-wsg-idm-ecid-wtls1",
+	NID_wap_wsg_idm_ecid_wtls1,5,&(lvalues[4941]),0},
+{"wap-wsg-idm-ecid-wtls3","wap-wsg-idm-ecid-wtls3",
+	NID_wap_wsg_idm_ecid_wtls3,5,&(lvalues[4946]),0},
+{"wap-wsg-idm-ecid-wtls4","wap-wsg-idm-ecid-wtls4",
+	NID_wap_wsg_idm_ecid_wtls4,5,&(lvalues[4951]),0},
+{"wap-wsg-idm-ecid-wtls5","wap-wsg-idm-ecid-wtls5",
+	NID_wap_wsg_idm_ecid_wtls5,5,&(lvalues[4956]),0},
+{"wap-wsg-idm-ecid-wtls6","wap-wsg-idm-ecid-wtls6",
+	NID_wap_wsg_idm_ecid_wtls6,5,&(lvalues[4961]),0},
+{"wap-wsg-idm-ecid-wtls7","wap-wsg-idm-ecid-wtls7",
+	NID_wap_wsg_idm_ecid_wtls7,5,&(lvalues[4966]),0},
+{"wap-wsg-idm-ecid-wtls8","wap-wsg-idm-ecid-wtls8",
+	NID_wap_wsg_idm_ecid_wtls8,5,&(lvalues[4971]),0},
+{"wap-wsg-idm-ecid-wtls9","wap-wsg-idm-ecid-wtls9",
+	NID_wap_wsg_idm_ecid_wtls9,5,&(lvalues[4976]),0},
+{"wap-wsg-idm-ecid-wtls10","wap-wsg-idm-ecid-wtls10",
+	NID_wap_wsg_idm_ecid_wtls10,5,&(lvalues[4981]),0},
+{"wap-wsg-idm-ecid-wtls11","wap-wsg-idm-ecid-wtls11",
+	NID_wap_wsg_idm_ecid_wtls11,5,&(lvalues[4986]),0},
+{"wap-wsg-idm-ecid-wtls12","wap-wsg-idm-ecid-wtls12",
+	NID_wap_wsg_idm_ecid_wtls12,5,&(lvalues[4991]),0},
+{"anyPolicy","X509v3 Any Policy",NID_any_policy,4,&(lvalues[4996]),0},
+{"policyMappings","X509v3 Policy Mappings",NID_policy_mappings,3,
+	&(lvalues[5000]),0},
+{"inhibitAnyPolicy","X509v3 Inhibit Any Policy",
+	NID_inhibit_any_policy,3,&(lvalues[5003]),0},
+{"Oakley-EC2N-3","ipsec3",NID_ipsec3,0,NULL,0},
+{"Oakley-EC2N-4","ipsec4",NID_ipsec4,0,NULL,0},
+{"CAMELLIA-128-CBC","camellia-128-cbc",NID_camellia_128_cbc,11,
+	&(lvalues[5006]),0},
+{"CAMELLIA-192-CBC","camellia-192-cbc",NID_camellia_192_cbc,11,
+	&(lvalues[5017]),0},
+{"CAMELLIA-256-CBC","camellia-256-cbc",NID_camellia_256_cbc,11,
+	&(lvalues[5028]),0},
+{"CAMELLIA-128-ECB","camellia-128-ecb",NID_camellia_128_ecb,8,
+	&(lvalues[5039]),0},
+{"CAMELLIA-192-ECB","camellia-192-ecb",NID_camellia_192_ecb,8,
+	&(lvalues[5047]),0},
+{"CAMELLIA-256-ECB","camellia-256-ecb",NID_camellia_256_ecb,8,
+	&(lvalues[5055]),0},
+{"CAMELLIA-128-CFB","camellia-128-cfb",NID_camellia_128_cfb128,8,
+	&(lvalues[5063]),0},
+{"CAMELLIA-192-CFB","camellia-192-cfb",NID_camellia_192_cfb128,8,
+	&(lvalues[5071]),0},
+{"CAMELLIA-256-CFB","camellia-256-cfb",NID_camellia_256_cfb128,8,
+	&(lvalues[5079]),0},
+{"CAMELLIA-128-CFB1","camellia-128-cfb1",NID_camellia_128_cfb1,0,NULL,0},
+{"CAMELLIA-192-CFB1","camellia-192-cfb1",NID_camellia_192_cfb1,0,NULL,0},
+{"CAMELLIA-256-CFB1","camellia-256-cfb1",NID_camellia_256_cfb1,0,NULL,0},
+{"CAMELLIA-128-CFB8","camellia-128-cfb8",NID_camellia_128_cfb8,0,NULL,0},
+{"CAMELLIA-192-CFB8","camellia-192-cfb8",NID_camellia_192_cfb8,0,NULL,0},
+{"CAMELLIA-256-CFB8","camellia-256-cfb8",NID_camellia_256_cfb8,0,NULL,0},
+{"CAMELLIA-128-OFB","camellia-128-ofb",NID_camellia_128_ofb128,8,
+	&(lvalues[5087]),0},
+{"CAMELLIA-192-OFB","camellia-192-ofb",NID_camellia_192_ofb128,8,
+	&(lvalues[5095]),0},
+{"CAMELLIA-256-OFB","camellia-256-ofb",NID_camellia_256_ofb128,8,
+	&(lvalues[5103]),0},
+{"subjectDirectoryAttributes","X509v3 Subject Directory Attributes",
+	NID_subject_directory_attributes,3,&(lvalues[5111]),0},
+{"issuingDistributionPoint","X509v3 Issuing Distrubution Point",
+	NID_issuing_distribution_point,3,&(lvalues[5114]),0},
+{"certificateIssuer","X509v3 Certificate Issuer",
+	NID_certificate_issuer,3,&(lvalues[5117]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"KISA","kisa",NID_kisa,6,&(lvalues[5120]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"SEED-ECB","seed-ecb",NID_seed_ecb,8,&(lvalues[5126]),0},
+{"SEED-CBC","seed-cbc",NID_seed_cbc,8,&(lvalues[5134]),0},
+{"SEED-OFB","seed-ofb",NID_seed_ofb128,8,&(lvalues[5142]),0},
+{"SEED-CFB","seed-cfb",NID_seed_cfb128,8,&(lvalues[5150]),0},
+{"HMAC-MD5","hmac-md5",NID_hmac_md5,8,&(lvalues[5158]),0},
+{"HMAC-SHA1","hmac-sha1",NID_hmac_sha1,8,&(lvalues[5166]),0},
+{"id-PasswordBasedMAC","password based MAC",NID_id_PasswordBasedMAC,9,
+	&(lvalues[5174]),0},
+{"id-DHBasedMac","Diffie-Hellman based MAC",NID_id_DHBasedMac,9,
+	&(lvalues[5183]),0},
+{"id-it-suppLangTags","id-it-suppLangTags",NID_id_it_suppLangTags,8,
+	&(lvalues[5192]),0},
+{"caRepository","CA Repository",NID_caRepository,8,&(lvalues[5200]),0},
+{"id-smime-ct-compressedData","id-smime-ct-compressedData",
+	NID_id_smime_ct_compressedData,11,&(lvalues[5208]),0},
+{"id-ct-asciiTextWithCRLF","id-ct-asciiTextWithCRLF",
+	NID_id_ct_asciiTextWithCRLF,11,&(lvalues[5219]),0},
+{"id-aes128-wrap","id-aes128-wrap",NID_id_aes128_wrap,9,
+	&(lvalues[5230]),0},
+{"id-aes192-wrap","id-aes192-wrap",NID_id_aes192_wrap,9,
+	&(lvalues[5239]),0},
+{"id-aes256-wrap","id-aes256-wrap",NID_id_aes256_wrap,9,
+	&(lvalues[5248]),0},
+{"ecdsa-with-Recommended","ecdsa-with-Recommended",
+	NID_ecdsa_with_Recommended,7,&(lvalues[5257]),0},
+{"ecdsa-with-Specified","ecdsa-with-Specified",
+	NID_ecdsa_with_Specified,7,&(lvalues[5264]),0},
+{"ecdsa-with-SHA224","ecdsa-with-SHA224",NID_ecdsa_with_SHA224,8,
+	&(lvalues[5271]),0},
+{"ecdsa-with-SHA256","ecdsa-with-SHA256",NID_ecdsa_with_SHA256,8,
+	&(lvalues[5279]),0},
+{"ecdsa-with-SHA384","ecdsa-with-SHA384",NID_ecdsa_with_SHA384,8,
+	&(lvalues[5287]),0},
+{"ecdsa-with-SHA512","ecdsa-with-SHA512",NID_ecdsa_with_SHA512,8,
+	&(lvalues[5295]),0},
+{"hmacWithMD5","hmacWithMD5",NID_hmacWithMD5,8,&(lvalues[5303]),0},
+{"hmacWithSHA224","hmacWithSHA224",NID_hmacWithSHA224,8,
+	&(lvalues[5311]),0},
+{"hmacWithSHA256","hmacWithSHA256",NID_hmacWithSHA256,8,
+	&(lvalues[5319]),0},
+{"hmacWithSHA384","hmacWithSHA384",NID_hmacWithSHA384,8,
+	&(lvalues[5327]),0},
+{"hmacWithSHA512","hmacWithSHA512",NID_hmacWithSHA512,8,
+	&(lvalues[5335]),0},
+{"dsa_with_SHA224","dsa_with_SHA224",NID_dsa_with_SHA224,9,
+	&(lvalues[5343]),0},
+{"dsa_with_SHA256","dsa_with_SHA256",NID_dsa_with_SHA256,9,
+	&(lvalues[5352]),0},
+{"whirlpool","whirlpool",NID_whirlpool,6,&(lvalues[5361]),0},
+{"cryptopro","cryptopro",NID_cryptopro,5,&(lvalues[5367]),0},
+{"cryptocom","cryptocom",NID_cryptocom,5,&(lvalues[5372]),0},
+{"id-GostR3411-94-with-GostR3410-2001",
+	"GOST R 34.11-94 with GOST R 34.10-2001",
+	NID_id_GostR3411_94_with_GostR3410_2001,6,&(lvalues[5377]),0},
+{"id-GostR3411-94-with-GostR3410-94",
+	"GOST R 34.11-94 with GOST R 34.10-94",
+	NID_id_GostR3411_94_with_GostR3410_94,6,&(lvalues[5383]),0},
+{"md_gost94","GOST R 34.11-94",NID_id_GostR3411_94,6,&(lvalues[5389]),0},
+{"id-HMACGostR3411-94","HMAC GOST 34.11-94",NID_id_HMACGostR3411_94,6,
+	&(lvalues[5395]),0},
+{"gost2001","GOST R 34.10-2001",NID_id_GostR3410_2001,6,
+	&(lvalues[5401]),0},
+{"gost94","GOST R 34.10-94",NID_id_GostR3410_94,6,&(lvalues[5407]),0},
+{"gost89","GOST 28147-89",NID_id_Gost28147_89,6,&(lvalues[5413]),0},
+{"gost89-cnt","gost89-cnt",NID_gost89_cnt,0,NULL,0},
+{"gost-mac","GOST 28147-89 MAC",NID_id_Gost28147_89_MAC,6,
+	&(lvalues[5419]),0},
+{"prf-gostr3411-94","GOST R 34.11-94 PRF",NID_id_GostR3411_94_prf,6,
+	&(lvalues[5425]),0},
+{"id-GostR3410-2001DH","GOST R 34.10-2001 DH",NID_id_GostR3410_2001DH,
+	6,&(lvalues[5431]),0},
+{"id-GostR3410-94DH","GOST R 34.10-94 DH",NID_id_GostR3410_94DH,6,
+	&(lvalues[5437]),0},
+{"id-Gost28147-89-CryptoPro-KeyMeshing",
+	"id-Gost28147-89-CryptoPro-KeyMeshing",
+	NID_id_Gost28147_89_CryptoPro_KeyMeshing,7,&(lvalues[5443]),0},
+{"id-Gost28147-89-None-KeyMeshing","id-Gost28147-89-None-KeyMeshing",
+	NID_id_Gost28147_89_None_KeyMeshing,7,&(lvalues[5450]),0},
+{"id-GostR3411-94-TestParamSet","id-GostR3411-94-TestParamSet",
+	NID_id_GostR3411_94_TestParamSet,7,&(lvalues[5457]),0},
+{"id-GostR3411-94-CryptoProParamSet",
+	"id-GostR3411-94-CryptoProParamSet",
+	NID_id_GostR3411_94_CryptoProParamSet,7,&(lvalues[5464]),0},
+{"id-Gost28147-89-TestParamSet","id-Gost28147-89-TestParamSet",
+	NID_id_Gost28147_89_TestParamSet,7,&(lvalues[5471]),0},
+{"id-Gost28147-89-CryptoPro-A-ParamSet",
+	"id-Gost28147-89-CryptoPro-A-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_A_ParamSet,7,&(lvalues[5478]),0},
+{"id-Gost28147-89-CryptoPro-B-ParamSet",
+	"id-Gost28147-89-CryptoPro-B-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_B_ParamSet,7,&(lvalues[5485]),0},
+{"id-Gost28147-89-CryptoPro-C-ParamSet",
+	"id-Gost28147-89-CryptoPro-C-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_C_ParamSet,7,&(lvalues[5492]),0},
+{"id-Gost28147-89-CryptoPro-D-ParamSet",
+	"id-Gost28147-89-CryptoPro-D-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_D_ParamSet,7,&(lvalues[5499]),0},
+{"id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet",
+	"id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet,7,&(lvalues[5506]),
+	0},
+{"id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet",
+	"id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet,7,&(lvalues[5513]),
+	0},
+{"id-Gost28147-89-CryptoPro-RIC-1-ParamSet",
+	"id-Gost28147-89-CryptoPro-RIC-1-ParamSet",
+	NID_id_Gost28147_89_CryptoPro_RIC_1_ParamSet,7,&(lvalues[5520]),0},
+{"id-GostR3410-94-TestParamSet","id-GostR3410-94-TestParamSet",
+	NID_id_GostR3410_94_TestParamSet,7,&(lvalues[5527]),0},
+{"id-GostR3410-94-CryptoPro-A-ParamSet",
+	"id-GostR3410-94-CryptoPro-A-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_A_ParamSet,7,&(lvalues[5534]),0},
+{"id-GostR3410-94-CryptoPro-B-ParamSet",
+	"id-GostR3410-94-CryptoPro-B-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_B_ParamSet,7,&(lvalues[5541]),0},
+{"id-GostR3410-94-CryptoPro-C-ParamSet",
+	"id-GostR3410-94-CryptoPro-C-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_C_ParamSet,7,&(lvalues[5548]),0},
+{"id-GostR3410-94-CryptoPro-D-ParamSet",
+	"id-GostR3410-94-CryptoPro-D-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_D_ParamSet,7,&(lvalues[5555]),0},
+{"id-GostR3410-94-CryptoPro-XchA-ParamSet",
+	"id-GostR3410-94-CryptoPro-XchA-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_XchA_ParamSet,7,&(lvalues[5562]),0},
+{"id-GostR3410-94-CryptoPro-XchB-ParamSet",
+	"id-GostR3410-94-CryptoPro-XchB-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_XchB_ParamSet,7,&(lvalues[5569]),0},
+{"id-GostR3410-94-CryptoPro-XchC-ParamSet",
+	"id-GostR3410-94-CryptoPro-XchC-ParamSet",
+	NID_id_GostR3410_94_CryptoPro_XchC_ParamSet,7,&(lvalues[5576]),0},
+{"id-GostR3410-2001-TestParamSet","id-GostR3410-2001-TestParamSet",
+	NID_id_GostR3410_2001_TestParamSet,7,&(lvalues[5583]),0},
+{"id-GostR3410-2001-CryptoPro-A-ParamSet",
+	"id-GostR3410-2001-CryptoPro-A-ParamSet",
+	NID_id_GostR3410_2001_CryptoPro_A_ParamSet,7,&(lvalues[5590]),0},
+{"id-GostR3410-2001-CryptoPro-B-ParamSet",
+	"id-GostR3410-2001-CryptoPro-B-ParamSet",
+	NID_id_GostR3410_2001_CryptoPro_B_ParamSet,7,&(lvalues[5597]),0},
+{"id-GostR3410-2001-CryptoPro-C-ParamSet",
+	"id-GostR3410-2001-CryptoPro-C-ParamSet",
+	NID_id_GostR3410_2001_CryptoPro_C_ParamSet,7,&(lvalues[5604]),0},
+{"id-GostR3410-2001-CryptoPro-XchA-ParamSet",
+	"id-GostR3410-2001-CryptoPro-XchA-ParamSet",
+	NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet,7,&(lvalues[5611]),0},
+	
+{"id-GostR3410-2001-CryptoPro-XchB-ParamSet",
+	"id-GostR3410-2001-CryptoPro-XchB-ParamSet",
+	NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet,7,&(lvalues[5618]),0},
+	
+{"id-GostR3410-94-a","id-GostR3410-94-a",NID_id_GostR3410_94_a,7,
+	&(lvalues[5625]),0},
+{"id-GostR3410-94-aBis","id-GostR3410-94-aBis",
+	NID_id_GostR3410_94_aBis,7,&(lvalues[5632]),0},
+{"id-GostR3410-94-b","id-GostR3410-94-b",NID_id_GostR3410_94_b,7,
+	&(lvalues[5639]),0},
+{"id-GostR3410-94-bBis","id-GostR3410-94-bBis",
+	NID_id_GostR3410_94_bBis,7,&(lvalues[5646]),0},
+{"id-Gost28147-89-cc","GOST 28147-89 Cryptocom ParamSet",
+	NID_id_Gost28147_89_cc,8,&(lvalues[5653]),0},
+{"gost94cc","GOST 34.10-94 Cryptocom",NID_id_GostR3410_94_cc,8,
+	&(lvalues[5661]),0},
+{"gost2001cc","GOST 34.10-2001 Cryptocom",NID_id_GostR3410_2001_cc,8,
+	&(lvalues[5669]),0},
+{"id-GostR3411-94-with-GostR3410-94-cc",
+	"GOST R 34.11-94 with GOST R 34.10-94 Cryptocom",
+	NID_id_GostR3411_94_with_GostR3410_94_cc,8,&(lvalues[5677]),0},
+{"id-GostR3411-94-with-GostR3410-2001-cc",
+	"GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom",
+	NID_id_GostR3411_94_with_GostR3410_2001_cc,8,&(lvalues[5685]),0},
+{"id-GostR3410-2001-ParamSet-cc",
+	"GOST R 3410-2001 Parameter Set Cryptocom",
+	NID_id_GostR3410_2001_ParamSet_cc,8,&(lvalues[5693]),0},
+{"HMAC","hmac",NID_hmac,0,NULL,0},
+{"LocalKeySet","Microsoft Local Key set",NID_LocalKeySet,9,
+	&(lvalues[5701]),0},
+{"freshestCRL","X509v3 Freshest CRL",NID_freshest_crl,3,
+	&(lvalues[5710]),0},
+{"id-on-permanentIdentifier","Permanent Identifier",
+	NID_id_on_permanentIdentifier,8,&(lvalues[5713]),0},
+{"searchGuide","searchGuide",NID_searchGuide,3,&(lvalues[5721]),0},
+{"businessCategory","businessCategory",NID_businessCategory,3,
+	&(lvalues[5724]),0},
+{"postalAddress","postalAddress",NID_postalAddress,3,&(lvalues[5727]),0},
+{"postOfficeBox","postOfficeBox",NID_postOfficeBox,3,&(lvalues[5730]),0},
+{"physicalDeliveryOfficeName","physicalDeliveryOfficeName",
+	NID_physicalDeliveryOfficeName,3,&(lvalues[5733]),0},
+{"telephoneNumber","telephoneNumber",NID_telephoneNumber,3,
+	&(lvalues[5736]),0},
+{"telexNumber","telexNumber",NID_telexNumber,3,&(lvalues[5739]),0},
+{"teletexTerminalIdentifier","teletexTerminalIdentifier",
+	NID_teletexTerminalIdentifier,3,&(lvalues[5742]),0},
+{"facsimileTelephoneNumber","facsimileTelephoneNumber",
+	NID_facsimileTelephoneNumber,3,&(lvalues[5745]),0},
+{"x121Address","x121Address",NID_x121Address,3,&(lvalues[5748]),0},
+{"internationaliSDNNumber","internationaliSDNNumber",
+	NID_internationaliSDNNumber,3,&(lvalues[5751]),0},
+{"registeredAddress","registeredAddress",NID_registeredAddress,3,
+	&(lvalues[5754]),0},
+{"destinationIndicator","destinationIndicator",
+	NID_destinationIndicator,3,&(lvalues[5757]),0},
+{"preferredDeliveryMethod","preferredDeliveryMethod",
+	NID_preferredDeliveryMethod,3,&(lvalues[5760]),0},
+{"presentationAddress","presentationAddress",NID_presentationAddress,
+	3,&(lvalues[5763]),0},
+{"supportedApplicationContext","supportedApplicationContext",
+	NID_supportedApplicationContext,3,&(lvalues[5766]),0},
+{"member","member",NID_member,3,&(lvalues[5769]),0},
+{"owner","owner",NID_owner,3,&(lvalues[5772]),0},
+{"roleOccupant","roleOccupant",NID_roleOccupant,3,&(lvalues[5775]),0},
+{"seeAlso","seeAlso",NID_seeAlso,3,&(lvalues[5778]),0},
+{"userPassword","userPassword",NID_userPassword,3,&(lvalues[5781]),0},
+{"userCertificate","userCertificate",NID_userCertificate,3,
+	&(lvalues[5784]),0},
+{"cACertificate","cACertificate",NID_cACertificate,3,&(lvalues[5787]),0},
+{"authorityRevocationList","authorityRevocationList",
+	NID_authorityRevocationList,3,&(lvalues[5790]),0},
+{"certificateRevocationList","certificateRevocationList",
+	NID_certificateRevocationList,3,&(lvalues[5793]),0},
+{"crossCertificatePair","crossCertificatePair",
+	NID_crossCertificatePair,3,&(lvalues[5796]),0},
+{"enhancedSearchGuide","enhancedSearchGuide",NID_enhancedSearchGuide,
+	3,&(lvalues[5799]),0},
+{"protocolInformation","protocolInformation",NID_protocolInformation,
+	3,&(lvalues[5802]),0},
+{"distinguishedName","distinguishedName",NID_distinguishedName,3,
+	&(lvalues[5805]),0},
+{"uniqueMember","uniqueMember",NID_uniqueMember,3,&(lvalues[5808]),0},
+{"houseIdentifier","houseIdentifier",NID_houseIdentifier,3,
+	&(lvalues[5811]),0},
+{"supportedAlgorithms","supportedAlgorithms",NID_supportedAlgorithms,
+	3,&(lvalues[5814]),0},
+{"deltaRevocationList","deltaRevocationList",NID_deltaRevocationList,
+	3,&(lvalues[5817]),0},
+{"dmdName","dmdName",NID_dmdName,3,&(lvalues[5820]),0},
+{"id-alg-PWRI-KEK","id-alg-PWRI-KEK",NID_id_alg_PWRI_KEK,11,
+	&(lvalues[5823]),0},
+{"CMAC","cmac",NID_cmac,0,NULL,0},
+{"id-aes128-GCM","aes-128-gcm",NID_aes_128_gcm,9,&(lvalues[5834]),0},
+{"id-aes128-CCM","aes-128-ccm",NID_aes_128_ccm,9,&(lvalues[5843]),0},
+{"id-aes128-wrap-pad","id-aes128-wrap-pad",NID_id_aes128_wrap_pad,9,
+	&(lvalues[5852]),0},
+{"id-aes192-GCM","aes-192-gcm",NID_aes_192_gcm,9,&(lvalues[5861]),0},
+{"id-aes192-CCM","aes-192-ccm",NID_aes_192_ccm,9,&(lvalues[5870]),0},
+{"id-aes192-wrap-pad","id-aes192-wrap-pad",NID_id_aes192_wrap_pad,9,
+	&(lvalues[5879]),0},
+{"id-aes256-GCM","aes-256-gcm",NID_aes_256_gcm,9,&(lvalues[5888]),0},
+{"id-aes256-CCM","aes-256-ccm",NID_aes_256_ccm,9,&(lvalues[5897]),0},
+{"id-aes256-wrap-pad","id-aes256-wrap-pad",NID_id_aes256_wrap_pad,9,
+	&(lvalues[5906]),0},
+{"AES-128-CTR","aes-128-ctr",NID_aes_128_ctr,0,NULL,0},
+{"AES-192-CTR","aes-192-ctr",NID_aes_192_ctr,0,NULL,0},
+{"AES-256-CTR","aes-256-ctr",NID_aes_256_ctr,0,NULL,0},
+{"id-camellia128-wrap","id-camellia128-wrap",NID_id_camellia128_wrap,
+	11,&(lvalues[5915]),0},
+{"id-camellia192-wrap","id-camellia192-wrap",NID_id_camellia192_wrap,
+	11,&(lvalues[5926]),0},
+{"id-camellia256-wrap","id-camellia256-wrap",NID_id_camellia256_wrap,
+	11,&(lvalues[5937]),0},
+{"anyExtendedKeyUsage","Any Extended Key Usage",
+	NID_anyExtendedKeyUsage,4,&(lvalues[5948]),0},
+{"MGF1","mgf1",NID_mgf1,9,&(lvalues[5952]),0},
+{"RSASSA-PSS","rsassaPss",NID_rsassaPss,9,&(lvalues[5961]),0},
+{"AES-128-XTS","aes-128-xts",NID_aes_128_xts,0,NULL,0},
+{"AES-256-XTS","aes-256-xts",NID_aes_256_xts,0,NULL,0},
+{"RC4-HMAC-MD5","rc4-hmac-md5",NID_rc4_hmac_md5,0,NULL,0},
+{"AES-128-CBC-HMAC-SHA1","aes-128-cbc-hmac-sha1",
+	NID_aes_128_cbc_hmac_sha1,0,NULL,0},
+{"AES-192-CBC-HMAC-SHA1","aes-192-cbc-hmac-sha1",
+	NID_aes_192_cbc_hmac_sha1,0,NULL,0},
+{"AES-256-CBC-HMAC-SHA1","aes-256-cbc-hmac-sha1",
+	NID_aes_256_cbc_hmac_sha1,0,NULL,0},
+{"RSAES-OAEP","rsaesOaep",NID_rsaesOaep,9,&(lvalues[5970]),0},
+{"dhpublicnumber","X9.42 DH",NID_dhpublicnumber,7,&(lvalues[5979]),0},
+{"brainpoolP160r1","brainpoolP160r1",NID_brainpoolP160r1,9,
+	&(lvalues[5986]),0},
+{"brainpoolP160t1","brainpoolP160t1",NID_brainpoolP160t1,9,
+	&(lvalues[5995]),0},
+{"brainpoolP192r1","brainpoolP192r1",NID_brainpoolP192r1,9,
+	&(lvalues[6004]),0},
+{"brainpoolP192t1","brainpoolP192t1",NID_brainpoolP192t1,9,
+	&(lvalues[6013]),0},
+{"brainpoolP224r1","brainpoolP224r1",NID_brainpoolP224r1,9,
+	&(lvalues[6022]),0},
+{"brainpoolP224t1","brainpoolP224t1",NID_brainpoolP224t1,9,
+	&(lvalues[6031]),0},
+{"brainpoolP256r1","brainpoolP256r1",NID_brainpoolP256r1,9,
+	&(lvalues[6040]),0},
+{"brainpoolP256t1","brainpoolP256t1",NID_brainpoolP256t1,9,
+	&(lvalues[6049]),0},
+{"brainpoolP320r1","brainpoolP320r1",NID_brainpoolP320r1,9,
+	&(lvalues[6058]),0},
+{"brainpoolP320t1","brainpoolP320t1",NID_brainpoolP320t1,9,
+	&(lvalues[6067]),0},
+{"brainpoolP384r1","brainpoolP384r1",NID_brainpoolP384r1,9,
+	&(lvalues[6076]),0},
+{"brainpoolP384t1","brainpoolP384t1",NID_brainpoolP384t1,9,
+	&(lvalues[6085]),0},
+{"brainpoolP512r1","brainpoolP512r1",NID_brainpoolP512r1,9,
+	&(lvalues[6094]),0},
+{"brainpoolP512t1","brainpoolP512t1",NID_brainpoolP512t1,9,
+	&(lvalues[6103]),0},
+{"PSPECIFIED","pSpecified",NID_pSpecified,9,&(lvalues[6112]),0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{NULL,NULL,NID_undef,0,NULL,0},
+{"dhSinglePass-stdDH-sha1kdf-scheme",
+	"dhSinglePass-stdDH-sha1kdf-scheme",
+	NID_dhSinglePass_stdDH_sha1kdf_scheme,9,&(lvalues[6121]),0},
+{"dhSinglePass-stdDH-sha224kdf-scheme",
+	"dhSinglePass-stdDH-sha224kdf-scheme",
+	NID_dhSinglePass_stdDH_sha224kdf_scheme,6,&(lvalues[6130]),0},
+{"dhSinglePass-stdDH-sha256kdf-scheme",
+	"dhSinglePass-stdDH-sha256kdf-scheme",
+	NID_dhSinglePass_stdDH_sha256kdf_scheme,6,&(lvalues[6136]),0},
+{"dhSinglePass-stdDH-sha384kdf-scheme",
+	"dhSinglePass-stdDH-sha384kdf-scheme",
+	NID_dhSinglePass_stdDH_sha384kdf_scheme,6,&(lvalues[6142]),0},
+{"dhSinglePass-stdDH-sha512kdf-scheme",
+	"dhSinglePass-stdDH-sha512kdf-scheme",
+	NID_dhSinglePass_stdDH_sha512kdf_scheme,6,&(lvalues[6148]),0},
+{"dhSinglePass-cofactorDH-sha1kdf-scheme",
+	"dhSinglePass-cofactorDH-sha1kdf-scheme",
+	NID_dhSinglePass_cofactorDH_sha1kdf_scheme,9,&(lvalues[6154]),0},
+{"dhSinglePass-cofactorDH-sha224kdf-scheme",
+	"dhSinglePass-cofactorDH-sha224kdf-scheme",
+	NID_dhSinglePass_cofactorDH_sha224kdf_scheme,6,&(lvalues[6163]),0},
+{"dhSinglePass-cofactorDH-sha256kdf-scheme",
+	"dhSinglePass-cofactorDH-sha256kdf-scheme",
+	NID_dhSinglePass_cofactorDH_sha256kdf_scheme,6,&(lvalues[6169]),0},
+{"dhSinglePass-cofactorDH-sha384kdf-scheme",
+	"dhSinglePass-cofactorDH-sha384kdf-scheme",
+	NID_dhSinglePass_cofactorDH_sha384kdf_scheme,6,&(lvalues[6175]),0},
+{"dhSinglePass-cofactorDH-sha512kdf-scheme",
+	"dhSinglePass-cofactorDH-sha512kdf-scheme",
+	NID_dhSinglePass_cofactorDH_sha512kdf_scheme,6,&(lvalues[6181]),0},
+{"dh-std-kdf","dh-std-kdf",NID_dh_std_kdf,0,NULL,0},
+{"dh-cofactor-kdf","dh-cofactor-kdf",NID_dh_cofactor_kdf,0,NULL,0},
+};
+
+static const unsigned int kNIDsInShortNameOrder[NUM_SN]={
+364,	/* "AD_DVCS" */
+419,	/* "AES-128-CBC" */
+916,	/* "AES-128-CBC-HMAC-SHA1" */
+421,	/* "AES-128-CFB" */
+650,	/* "AES-128-CFB1" */
+653,	/* "AES-128-CFB8" */
+904,	/* "AES-128-CTR" */
+418,	/* "AES-128-ECB" */
+420,	/* "AES-128-OFB" */
+913,	/* "AES-128-XTS" */
+423,	/* "AES-192-CBC" */
+917,	/* "AES-192-CBC-HMAC-SHA1" */
+425,	/* "AES-192-CFB" */
+651,	/* "AES-192-CFB1" */
+654,	/* "AES-192-CFB8" */
+905,	/* "AES-192-CTR" */
+422,	/* "AES-192-ECB" */
+424,	/* "AES-192-OFB" */
+427,	/* "AES-256-CBC" */
+918,	/* "AES-256-CBC-HMAC-SHA1" */
+429,	/* "AES-256-CFB" */
+652,	/* "AES-256-CFB1" */
+655,	/* "AES-256-CFB8" */
+906,	/* "AES-256-CTR" */
+426,	/* "AES-256-ECB" */
+428,	/* "AES-256-OFB" */
+914,	/* "AES-256-XTS" */
+91,	/* "BF-CBC" */
+93,	/* "BF-CFB" */
+92,	/* "BF-ECB" */
+94,	/* "BF-OFB" */
+14,	/* "C" */
+751,	/* "CAMELLIA-128-CBC" */
+757,	/* "CAMELLIA-128-CFB" */
+760,	/* "CAMELLIA-128-CFB1" */
+763,	/* "CAMELLIA-128-CFB8" */
+754,	/* "CAMELLIA-128-ECB" */
+766,	/* "CAMELLIA-128-OFB" */
+752,	/* "CAMELLIA-192-CBC" */
+758,	/* "CAMELLIA-192-CFB" */
+761,	/* "CAMELLIA-192-CFB1" */
+764,	/* "CAMELLIA-192-CFB8" */
+755,	/* "CAMELLIA-192-ECB" */
+767,	/* "CAMELLIA-192-OFB" */
+753,	/* "CAMELLIA-256-CBC" */
+759,	/* "CAMELLIA-256-CFB" */
+762,	/* "CAMELLIA-256-CFB1" */
+765,	/* "CAMELLIA-256-CFB8" */
+756,	/* "CAMELLIA-256-ECB" */
+768,	/* "CAMELLIA-256-OFB" */
+108,	/* "CAST5-CBC" */
+110,	/* "CAST5-CFB" */
+109,	/* "CAST5-ECB" */
+111,	/* "CAST5-OFB" */
+894,	/* "CMAC" */
+13,	/* "CN" */
+141,	/* "CRLReason" */
+417,	/* "CSPName" */
+367,	/* "CrlID" */
+391,	/* "DC" */
+31,	/* "DES-CBC" */
+643,	/* "DES-CDMF" */
+30,	/* "DES-CFB" */
+656,	/* "DES-CFB1" */
+657,	/* "DES-CFB8" */
+29,	/* "DES-ECB" */
+32,	/* "DES-EDE" */
+43,	/* "DES-EDE-CBC" */
+60,	/* "DES-EDE-CFB" */
+62,	/* "DES-EDE-OFB" */
+33,	/* "DES-EDE3" */
+44,	/* "DES-EDE3-CBC" */
+61,	/* "DES-EDE3-CFB" */
+658,	/* "DES-EDE3-CFB1" */
+659,	/* "DES-EDE3-CFB8" */
+63,	/* "DES-EDE3-OFB" */
+45,	/* "DES-OFB" */
+80,	/* "DESX-CBC" */
+380,	/* "DOD" */
+116,	/* "DSA" */
+66,	/* "DSA-SHA" */
+113,	/* "DSA-SHA1" */
+70,	/* "DSA-SHA1-old" */
+67,	/* "DSA-old" */
+297,	/* "DVCS" */
+99,	/* "GN" */
+855,	/* "HMAC" */
+780,	/* "HMAC-MD5" */
+781,	/* "HMAC-SHA1" */
+381,	/* "IANA" */
+34,	/* "IDEA-CBC" */
+35,	/* "IDEA-CFB" */
+36,	/* "IDEA-ECB" */
+46,	/* "IDEA-OFB" */
+181,	/* "ISO" */
+183,	/* "ISO-US" */
+645,	/* "ITU-T" */
+646,	/* "JOINT-ISO-ITU-T" */
+773,	/* "KISA" */
+15,	/* "L" */
+856,	/* "LocalKeySet" */
+ 3,	/* "MD2" */
+257,	/* "MD4" */
+ 4,	/* "MD5" */
+114,	/* "MD5-SHA1" */
+95,	/* "MDC2" */
+911,	/* "MGF1" */
+388,	/* "Mail" */
+393,	/* "NULL" */
+404,	/* "NULL" */
+57,	/* "Netscape" */
+366,	/* "Nonce" */
+17,	/* "O" */
+178,	/* "OCSP" */
+180,	/* "OCSPSigning" */
+379,	/* "ORG" */
+18,	/* "OU" */
+749,	/* "Oakley-EC2N-3" */
+750,	/* "Oakley-EC2N-4" */
+ 9,	/* "PBE-MD2-DES" */
+168,	/* "PBE-MD2-RC2-64" */
+10,	/* "PBE-MD5-DES" */
+169,	/* "PBE-MD5-RC2-64" */
+147,	/* "PBE-SHA1-2DES" */
+146,	/* "PBE-SHA1-3DES" */
+170,	/* "PBE-SHA1-DES" */
+148,	/* "PBE-SHA1-RC2-128" */
+149,	/* "PBE-SHA1-RC2-40" */
+68,	/* "PBE-SHA1-RC2-64" */
+144,	/* "PBE-SHA1-RC4-128" */
+145,	/* "PBE-SHA1-RC4-40" */
+161,	/* "PBES2" */
+69,	/* "PBKDF2" */
+162,	/* "PBMAC1" */
+127,	/* "PKIX" */
+935,	/* "PSPECIFIED" */
+98,	/* "RC2-40-CBC" */
+166,	/* "RC2-64-CBC" */
+37,	/* "RC2-CBC" */
+39,	/* "RC2-CFB" */
+38,	/* "RC2-ECB" */
+40,	/* "RC2-OFB" */
+ 5,	/* "RC4" */
+97,	/* "RC4-40" */
+915,	/* "RC4-HMAC-MD5" */
+120,	/* "RC5-CBC" */
+122,	/* "RC5-CFB" */
+121,	/* "RC5-ECB" */
+123,	/* "RC5-OFB" */
+117,	/* "RIPEMD160" */
+124,	/* "RLE" */
+19,	/* "RSA" */
+ 7,	/* "RSA-MD2" */
+396,	/* "RSA-MD4" */
+ 8,	/* "RSA-MD5" */
+96,	/* "RSA-MDC2" */
+104,	/* "RSA-NP-MD5" */
+119,	/* "RSA-RIPEMD160" */
+42,	/* "RSA-SHA" */
+65,	/* "RSA-SHA1" */
+115,	/* "RSA-SHA1-2" */
+671,	/* "RSA-SHA224" */
+668,	/* "RSA-SHA256" */
+669,	/* "RSA-SHA384" */
+670,	/* "RSA-SHA512" */
+919,	/* "RSAES-OAEP" */
+912,	/* "RSASSA-PSS" */
+777,	/* "SEED-CBC" */
+779,	/* "SEED-CFB" */
+776,	/* "SEED-ECB" */
+778,	/* "SEED-OFB" */
+41,	/* "SHA" */
+64,	/* "SHA1" */
+675,	/* "SHA224" */
+672,	/* "SHA256" */
+673,	/* "SHA384" */
+674,	/* "SHA512" */
+188,	/* "SMIME" */
+167,	/* "SMIME-CAPS" */
+100,	/* "SN" */
+16,	/* "ST" */
+143,	/* "SXNetID" */
+458,	/* "UID" */
+ 0,	/* "UNDEF" */
+11,	/* "X500" */
+378,	/* "X500algorithms" */
+12,	/* "X509" */
+184,	/* "X9-57" */
+185,	/* "X9cm" */
+125,	/* "ZLIB" */
+478,	/* "aRecord" */
+289,	/* "aaControls" */
+287,	/* "ac-auditEntity" */
+397,	/* "ac-proxying" */
+288,	/* "ac-targeting" */
+368,	/* "acceptableResponses" */
+446,	/* "account" */
+363,	/* "ad_timestamping" */
+376,	/* "algorithm" */
+405,	/* "ansi-X9-62" */
+910,	/* "anyExtendedKeyUsage" */
+746,	/* "anyPolicy" */
+370,	/* "archiveCutoff" */
+484,	/* "associatedDomain" */
+485,	/* "associatedName" */
+501,	/* "audio" */
+177,	/* "authorityInfoAccess" */
+90,	/* "authorityKeyIdentifier" */
+882,	/* "authorityRevocationList" */
+87,	/* "basicConstraints" */
+365,	/* "basicOCSPResponse" */
+285,	/* "biometricInfo" */
+921,	/* "brainpoolP160r1" */
+922,	/* "brainpoolP160t1" */
+923,	/* "brainpoolP192r1" */
+924,	/* "brainpoolP192t1" */
+925,	/* "brainpoolP224r1" */
+926,	/* "brainpoolP224t1" */
+927,	/* "brainpoolP256r1" */
+928,	/* "brainpoolP256t1" */
+929,	/* "brainpoolP320r1" */
+930,	/* "brainpoolP320t1" */
+931,	/* "brainpoolP384r1" */
+932,	/* "brainpoolP384t1" */
+933,	/* "brainpoolP512r1" */
+934,	/* "brainpoolP512t1" */
+494,	/* "buildingName" */
+860,	/* "businessCategory" */
+691,	/* "c2onb191v4" */
+692,	/* "c2onb191v5" */
+697,	/* "c2onb239v4" */
+698,	/* "c2onb239v5" */
+684,	/* "c2pnb163v1" */
+685,	/* "c2pnb163v2" */
+686,	/* "c2pnb163v3" */
+687,	/* "c2pnb176v1" */
+693,	/* "c2pnb208w1" */
+699,	/* "c2pnb272w1" */
+700,	/* "c2pnb304w1" */
+702,	/* "c2pnb368w1" */
+688,	/* "c2tnb191v1" */
+689,	/* "c2tnb191v2" */
+690,	/* "c2tnb191v3" */
+694,	/* "c2tnb239v1" */
+695,	/* "c2tnb239v2" */
+696,	/* "c2tnb239v3" */
+701,	/* "c2tnb359v1" */
+703,	/* "c2tnb431r1" */
+881,	/* "cACertificate" */
+483,	/* "cNAMERecord" */
+179,	/* "caIssuers" */
+785,	/* "caRepository" */
+443,	/* "caseIgnoreIA5StringSyntax" */
+152,	/* "certBag" */
+677,	/* "certicom-arc" */
+771,	/* "certificateIssuer" */
+89,	/* "certificatePolicies" */
+883,	/* "certificateRevocationList" */
+54,	/* "challengePassword" */
+407,	/* "characteristic-two-field" */
+395,	/* "clearance" */
+130,	/* "clientAuth" */
+131,	/* "codeSigning" */
+50,	/* "contentType" */
+53,	/* "countersignature" */
+153,	/* "crlBag" */
+103,	/* "crlDistributionPoints" */
+88,	/* "crlNumber" */
+884,	/* "crossCertificatePair" */
+806,	/* "cryptocom" */
+805,	/* "cryptopro" */
+500,	/* "dITRedirect" */
+451,	/* "dNSDomain" */
+495,	/* "dSAQuality" */
+434,	/* "data" */
+390,	/* "dcobject" */
+140,	/* "deltaCRL" */
+891,	/* "deltaRevocationList" */
+107,	/* "description" */
+871,	/* "destinationIndicator" */
+950,	/* "dh-cofactor-kdf" */
+949,	/* "dh-std-kdf" */
+28,	/* "dhKeyAgreement" */
+944,	/* "dhSinglePass-cofactorDH-sha1kdf-scheme" */
+945,	/* "dhSinglePass-cofactorDH-sha224kdf-scheme" */
+946,	/* "dhSinglePass-cofactorDH-sha256kdf-scheme" */
+947,	/* "dhSinglePass-cofactorDH-sha384kdf-scheme" */
+948,	/* "dhSinglePass-cofactorDH-sha512kdf-scheme" */
+939,	/* "dhSinglePass-stdDH-sha1kdf-scheme" */
+940,	/* "dhSinglePass-stdDH-sha224kdf-scheme" */
+941,	/* "dhSinglePass-stdDH-sha256kdf-scheme" */
+942,	/* "dhSinglePass-stdDH-sha384kdf-scheme" */
+943,	/* "dhSinglePass-stdDH-sha512kdf-scheme" */
+920,	/* "dhpublicnumber" */
+382,	/* "directory" */
+887,	/* "distinguishedName" */
+892,	/* "dmdName" */
+174,	/* "dnQualifier" */
+447,	/* "document" */
+471,	/* "documentAuthor" */
+468,	/* "documentIdentifier" */
+472,	/* "documentLocation" */
+502,	/* "documentPublisher" */
+449,	/* "documentSeries" */
+469,	/* "documentTitle" */
+470,	/* "documentVersion" */
+392,	/* "domain" */
+452,	/* "domainRelatedObject" */
+802,	/* "dsa_with_SHA224" */
+803,	/* "dsa_with_SHA256" */
+791,	/* "ecdsa-with-Recommended" */
+416,	/* "ecdsa-with-SHA1" */
+793,	/* "ecdsa-with-SHA224" */
+794,	/* "ecdsa-with-SHA256" */
+795,	/* "ecdsa-with-SHA384" */
+796,	/* "ecdsa-with-SHA512" */
+792,	/* "ecdsa-with-Specified" */
+48,	/* "emailAddress" */
+132,	/* "emailProtection" */
+885,	/* "enhancedSearchGuide" */
+389,	/* "enterprises" */
+384,	/* "experimental" */
+172,	/* "extReq" */
+56,	/* "extendedCertificateAttributes" */
+126,	/* "extendedKeyUsage" */
+372,	/* "extendedStatus" */
+867,	/* "facsimileTelephoneNumber" */
+462,	/* "favouriteDrink" */
+857,	/* "freshestCRL" */
+453,	/* "friendlyCountry" */
+490,	/* "friendlyCountryName" */
+156,	/* "friendlyName" */
+509,	/* "generationQualifier" */
+815,	/* "gost-mac" */
+811,	/* "gost2001" */
+851,	/* "gost2001cc" */
+813,	/* "gost89" */
+814,	/* "gost89-cnt" */
+812,	/* "gost94" */
+850,	/* "gost94cc" */
+797,	/* "hmacWithMD5" */
+163,	/* "hmacWithSHA1" */
+798,	/* "hmacWithSHA224" */
+799,	/* "hmacWithSHA256" */
+800,	/* "hmacWithSHA384" */
+801,	/* "hmacWithSHA512" */
+432,	/* "holdInstructionCallIssuer" */
+430,	/* "holdInstructionCode" */
+431,	/* "holdInstructionNone" */
+433,	/* "holdInstructionReject" */
+486,	/* "homePostalAddress" */
+473,	/* "homeTelephoneNumber" */
+466,	/* "host" */
+889,	/* "houseIdentifier" */
+442,	/* "iA5StringSyntax" */
+783,	/* "id-DHBasedMac" */
+824,	/* "id-Gost28147-89-CryptoPro-A-ParamSet" */
+825,	/* "id-Gost28147-89-CryptoPro-B-ParamSet" */
+826,	/* "id-Gost28147-89-CryptoPro-C-ParamSet" */
+827,	/* "id-Gost28147-89-CryptoPro-D-ParamSet" */
+819,	/* "id-Gost28147-89-CryptoPro-KeyMeshing" */
+829,	/* "id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet" */
+828,	/* "id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet" */
+830,	/* "id-Gost28147-89-CryptoPro-RIC-1-ParamSet" */
+820,	/* "id-Gost28147-89-None-KeyMeshing" */
+823,	/* "id-Gost28147-89-TestParamSet" */
+849,	/* "id-Gost28147-89-cc" */
+840,	/* "id-GostR3410-2001-CryptoPro-A-ParamSet" */
+841,	/* "id-GostR3410-2001-CryptoPro-B-ParamSet" */
+842,	/* "id-GostR3410-2001-CryptoPro-C-ParamSet" */
+843,	/* "id-GostR3410-2001-CryptoPro-XchA-ParamSet" */
+844,	/* "id-GostR3410-2001-CryptoPro-XchB-ParamSet" */
+854,	/* "id-GostR3410-2001-ParamSet-cc" */
+839,	/* "id-GostR3410-2001-TestParamSet" */
+817,	/* "id-GostR3410-2001DH" */
+832,	/* "id-GostR3410-94-CryptoPro-A-ParamSet" */
+833,	/* "id-GostR3410-94-CryptoPro-B-ParamSet" */
+834,	/* "id-GostR3410-94-CryptoPro-C-ParamSet" */
+835,	/* "id-GostR3410-94-CryptoPro-D-ParamSet" */
+836,	/* "id-GostR3410-94-CryptoPro-XchA-ParamSet" */
+837,	/* "id-GostR3410-94-CryptoPro-XchB-ParamSet" */
+838,	/* "id-GostR3410-94-CryptoPro-XchC-ParamSet" */
+831,	/* "id-GostR3410-94-TestParamSet" */
+845,	/* "id-GostR3410-94-a" */
+846,	/* "id-GostR3410-94-aBis" */
+847,	/* "id-GostR3410-94-b" */
+848,	/* "id-GostR3410-94-bBis" */
+818,	/* "id-GostR3410-94DH" */
+822,	/* "id-GostR3411-94-CryptoProParamSet" */
+821,	/* "id-GostR3411-94-TestParamSet" */
+807,	/* "id-GostR3411-94-with-GostR3410-2001" */
+853,	/* "id-GostR3411-94-with-GostR3410-2001-cc" */
+808,	/* "id-GostR3411-94-with-GostR3410-94" */
+852,	/* "id-GostR3411-94-with-GostR3410-94-cc" */
+810,	/* "id-HMACGostR3411-94" */
+782,	/* "id-PasswordBasedMAC" */
+266,	/* "id-aca" */
+355,	/* "id-aca-accessIdentity" */
+354,	/* "id-aca-authenticationInfo" */
+356,	/* "id-aca-chargingIdentity" */
+399,	/* "id-aca-encAttrs" */
+357,	/* "id-aca-group" */
+358,	/* "id-aca-role" */
+176,	/* "id-ad" */
+896,	/* "id-aes128-CCM" */
+895,	/* "id-aes128-GCM" */
+788,	/* "id-aes128-wrap" */
+897,	/* "id-aes128-wrap-pad" */
+899,	/* "id-aes192-CCM" */
+898,	/* "id-aes192-GCM" */
+789,	/* "id-aes192-wrap" */
+900,	/* "id-aes192-wrap-pad" */
+902,	/* "id-aes256-CCM" */
+901,	/* "id-aes256-GCM" */
+790,	/* "id-aes256-wrap" */
+903,	/* "id-aes256-wrap-pad" */
+262,	/* "id-alg" */
+893,	/* "id-alg-PWRI-KEK" */
+323,	/* "id-alg-des40" */
+326,	/* "id-alg-dh-pop" */
+325,	/* "id-alg-dh-sig-hmac-sha1" */
+324,	/* "id-alg-noSignature" */
+907,	/* "id-camellia128-wrap" */
+908,	/* "id-camellia192-wrap" */
+909,	/* "id-camellia256-wrap" */
+268,	/* "id-cct" */
+361,	/* "id-cct-PKIData" */
+362,	/* "id-cct-PKIResponse" */
+360,	/* "id-cct-crs" */
+81,	/* "id-ce" */
+680,	/* "id-characteristic-two-basis" */
+263,	/* "id-cmc" */
+334,	/* "id-cmc-addExtensions" */
+346,	/* "id-cmc-confirmCertAcceptance" */
+330,	/* "id-cmc-dataReturn" */
+336,	/* "id-cmc-decryptedPOP" */
+335,	/* "id-cmc-encryptedPOP" */
+339,	/* "id-cmc-getCRL" */
+338,	/* "id-cmc-getCert" */
+328,	/* "id-cmc-identification" */
+329,	/* "id-cmc-identityProof" */
+337,	/* "id-cmc-lraPOPWitness" */
+344,	/* "id-cmc-popLinkRandom" */
+345,	/* "id-cmc-popLinkWitness" */
+343,	/* "id-cmc-queryPending" */
+333,	/* "id-cmc-recipientNonce" */
+341,	/* "id-cmc-regInfo" */
+342,	/* "id-cmc-responseInfo" */
+340,	/* "id-cmc-revokeRequest" */
+332,	/* "id-cmc-senderNonce" */
+327,	/* "id-cmc-statusInfo" */
+331,	/* "id-cmc-transactionId" */
+787,	/* "id-ct-asciiTextWithCRLF" */
+408,	/* "id-ecPublicKey" */
+508,	/* "id-hex-multipart-message" */
+507,	/* "id-hex-partial-message" */
+260,	/* "id-it" */
+302,	/* "id-it-caKeyUpdateInfo" */
+298,	/* "id-it-caProtEncCert" */
+311,	/* "id-it-confirmWaitTime" */
+303,	/* "id-it-currentCRL" */
+300,	/* "id-it-encKeyPairTypes" */
+310,	/* "id-it-implicitConfirm" */
+308,	/* "id-it-keyPairParamRep" */
+307,	/* "id-it-keyPairParamReq" */
+312,	/* "id-it-origPKIMessage" */
+301,	/* "id-it-preferredSymmAlg" */
+309,	/* "id-it-revPassphrase" */
+299,	/* "id-it-signKeyPairTypes" */
+305,	/* "id-it-subscriptionRequest" */
+306,	/* "id-it-subscriptionResponse" */
+784,	/* "id-it-suppLangTags" */
+304,	/* "id-it-unsupportedOIDs" */
+128,	/* "id-kp" */
+280,	/* "id-mod-attribute-cert" */
+274,	/* "id-mod-cmc" */
+277,	/* "id-mod-cmp" */
+284,	/* "id-mod-cmp2000" */
+273,	/* "id-mod-crmf" */
+283,	/* "id-mod-dvcs" */
+275,	/* "id-mod-kea-profile-88" */
+276,	/* "id-mod-kea-profile-93" */
+282,	/* "id-mod-ocsp" */
+278,	/* "id-mod-qualified-cert-88" */
+279,	/* "id-mod-qualified-cert-93" */
+281,	/* "id-mod-timestamp-protocol" */
+264,	/* "id-on" */
+858,	/* "id-on-permanentIdentifier" */
+347,	/* "id-on-personalData" */
+265,	/* "id-pda" */
+352,	/* "id-pda-countryOfCitizenship" */
+353,	/* "id-pda-countryOfResidence" */
+348,	/* "id-pda-dateOfBirth" */
+351,	/* "id-pda-gender" */
+349,	/* "id-pda-placeOfBirth" */
+175,	/* "id-pe" */
+261,	/* "id-pkip" */
+258,	/* "id-pkix-mod" */
+269,	/* "id-pkix1-explicit-88" */
+271,	/* "id-pkix1-explicit-93" */
+270,	/* "id-pkix1-implicit-88" */
+272,	/* "id-pkix1-implicit-93" */
+662,	/* "id-ppl" */
+664,	/* "id-ppl-anyLanguage" */
+667,	/* "id-ppl-independent" */
+665,	/* "id-ppl-inheritAll" */
+267,	/* "id-qcs" */
+359,	/* "id-qcs-pkixQCSyntax-v1" */
+259,	/* "id-qt" */
+164,	/* "id-qt-cps" */
+165,	/* "id-qt-unotice" */
+313,	/* "id-regCtrl" */
+316,	/* "id-regCtrl-authenticator" */
+319,	/* "id-regCtrl-oldCertID" */
+318,	/* "id-regCtrl-pkiArchiveOptions" */
+317,	/* "id-regCtrl-pkiPublicationInfo" */
+320,	/* "id-regCtrl-protocolEncrKey" */
+315,	/* "id-regCtrl-regToken" */
+314,	/* "id-regInfo" */
+322,	/* "id-regInfo-certReq" */
+321,	/* "id-regInfo-utf8Pairs" */
+512,	/* "id-set" */
+191,	/* "id-smime-aa" */
+215,	/* "id-smime-aa-contentHint" */
+218,	/* "id-smime-aa-contentIdentifier" */
+221,	/* "id-smime-aa-contentReference" */
+240,	/* "id-smime-aa-dvcs-dvc" */
+217,	/* "id-smime-aa-encapContentType" */
+222,	/* "id-smime-aa-encrypKeyPref" */
+220,	/* "id-smime-aa-equivalentLabels" */
+232,	/* "id-smime-aa-ets-CertificateRefs" */
+233,	/* "id-smime-aa-ets-RevocationRefs" */
+238,	/* "id-smime-aa-ets-archiveTimeStamp" */
+237,	/* "id-smime-aa-ets-certCRLTimestamp" */
+234,	/* "id-smime-aa-ets-certValues" */
+227,	/* "id-smime-aa-ets-commitmentType" */
+231,	/* "id-smime-aa-ets-contentTimestamp" */
+236,	/* "id-smime-aa-ets-escTimeStamp" */
+230,	/* "id-smime-aa-ets-otherSigCert" */
+235,	/* "id-smime-aa-ets-revocationValues" */
+226,	/* "id-smime-aa-ets-sigPolicyId" */
+229,	/* "id-smime-aa-ets-signerAttr" */
+228,	/* "id-smime-aa-ets-signerLocation" */
+219,	/* "id-smime-aa-macValue" */
+214,	/* "id-smime-aa-mlExpandHistory" */
+216,	/* "id-smime-aa-msgSigDigest" */
+212,	/* "id-smime-aa-receiptRequest" */
+213,	/* "id-smime-aa-securityLabel" */
+239,	/* "id-smime-aa-signatureType" */
+223,	/* "id-smime-aa-signingCertificate" */
+224,	/* "id-smime-aa-smimeEncryptCerts" */
+225,	/* "id-smime-aa-timeStampToken" */
+192,	/* "id-smime-alg" */
+243,	/* "id-smime-alg-3DESwrap" */
+246,	/* "id-smime-alg-CMS3DESwrap" */
+247,	/* "id-smime-alg-CMSRC2wrap" */
+245,	/* "id-smime-alg-ESDH" */
+241,	/* "id-smime-alg-ESDHwith3DES" */
+242,	/* "id-smime-alg-ESDHwithRC2" */
+244,	/* "id-smime-alg-RC2wrap" */
+193,	/* "id-smime-cd" */
+248,	/* "id-smime-cd-ldap" */
+190,	/* "id-smime-ct" */
+210,	/* "id-smime-ct-DVCSRequestData" */
+211,	/* "id-smime-ct-DVCSResponseData" */
+208,	/* "id-smime-ct-TDTInfo" */
+207,	/* "id-smime-ct-TSTInfo" */
+205,	/* "id-smime-ct-authData" */
+786,	/* "id-smime-ct-compressedData" */
+209,	/* "id-smime-ct-contentInfo" */
+206,	/* "id-smime-ct-publishCert" */
+204,	/* "id-smime-ct-receipt" */
+195,	/* "id-smime-cti" */
+255,	/* "id-smime-cti-ets-proofOfApproval" */
+256,	/* "id-smime-cti-ets-proofOfCreation" */
+253,	/* "id-smime-cti-ets-proofOfDelivery" */
+251,	/* "id-smime-cti-ets-proofOfOrigin" */
+252,	/* "id-smime-cti-ets-proofOfReceipt" */
+254,	/* "id-smime-cti-ets-proofOfSender" */
+189,	/* "id-smime-mod" */
+196,	/* "id-smime-mod-cms" */
+197,	/* "id-smime-mod-ess" */
+202,	/* "id-smime-mod-ets-eSigPolicy-88" */
+203,	/* "id-smime-mod-ets-eSigPolicy-97" */
+200,	/* "id-smime-mod-ets-eSignature-88" */
+201,	/* "id-smime-mod-ets-eSignature-97" */
+199,	/* "id-smime-mod-msg-v3" */
+198,	/* "id-smime-mod-oid" */
+194,	/* "id-smime-spq" */
+250,	/* "id-smime-spq-ets-sqt-unotice" */
+249,	/* "id-smime-spq-ets-sqt-uri" */
+676,	/* "identified-organization" */
+461,	/* "info" */
+748,	/* "inhibitAnyPolicy" */
+101,	/* "initials" */
+647,	/* "international-organizations" */
+869,	/* "internationaliSDNNumber" */
+142,	/* "invalidityDate" */
+294,	/* "ipsecEndSystem" */
+295,	/* "ipsecTunnel" */
+296,	/* "ipsecUser" */
+86,	/* "issuerAltName" */
+770,	/* "issuingDistributionPoint" */
+492,	/* "janetMailbox" */
+150,	/* "keyBag" */
+83,	/* "keyUsage" */
+477,	/* "lastModifiedBy" */
+476,	/* "lastModifiedTime" */
+157,	/* "localKeyID" */
+480,	/* "mXRecord" */
+460,	/* "mail" */
+493,	/* "mailPreferenceOption" */
+467,	/* "manager" */
+809,	/* "md_gost94" */
+875,	/* "member" */
+182,	/* "member-body" */
+51,	/* "messageDigest" */
+383,	/* "mgmt" */
+504,	/* "mime-mhs" */
+506,	/* "mime-mhs-bodies" */
+505,	/* "mime-mhs-headings" */
+488,	/* "mobileTelephoneNumber" */
+136,	/* "msCTLSign" */
+135,	/* "msCodeCom" */
+134,	/* "msCodeInd" */
+138,	/* "msEFS" */
+171,	/* "msExtReq" */
+137,	/* "msSGC" */
+648,	/* "msSmartcardLogin" */
+649,	/* "msUPN" */
+481,	/* "nSRecord" */
+173,	/* "name" */
+666,	/* "nameConstraints" */
+369,	/* "noCheck" */
+403,	/* "noRevAvail" */
+72,	/* "nsBaseUrl" */
+76,	/* "nsCaPolicyUrl" */
+74,	/* "nsCaRevocationUrl" */
+58,	/* "nsCertExt" */
+79,	/* "nsCertSequence" */
+71,	/* "nsCertType" */
+78,	/* "nsComment" */
+59,	/* "nsDataType" */
+75,	/* "nsRenewalUrl" */
+73,	/* "nsRevocationUrl" */
+139,	/* "nsSGC" */
+77,	/* "nsSslServerName" */
+681,	/* "onBasis" */
+491,	/* "organizationalStatus" */
+475,	/* "otherMailbox" */
+876,	/* "owner" */
+489,	/* "pagerTelephoneNumber" */
+374,	/* "path" */
+112,	/* "pbeWithMD5AndCast5CBC" */
+499,	/* "personalSignature" */
+487,	/* "personalTitle" */
+464,	/* "photo" */
+863,	/* "physicalDeliveryOfficeName" */
+437,	/* "pilot" */
+439,	/* "pilotAttributeSyntax" */
+438,	/* "pilotAttributeType" */
+479,	/* "pilotAttributeType27" */
+456,	/* "pilotDSA" */
+441,	/* "pilotGroups" */
+444,	/* "pilotObject" */
+440,	/* "pilotObjectClass" */
+455,	/* "pilotOrganization" */
+445,	/* "pilotPerson" */
+ 2,	/* "pkcs" */
+186,	/* "pkcs1" */
+27,	/* "pkcs3" */
+187,	/* "pkcs5" */
+20,	/* "pkcs7" */
+21,	/* "pkcs7-data" */
+25,	/* "pkcs7-digestData" */
+26,	/* "pkcs7-encryptedData" */
+23,	/* "pkcs7-envelopedData" */
+24,	/* "pkcs7-signedAndEnvelopedData" */
+22,	/* "pkcs7-signedData" */
+151,	/* "pkcs8ShroudedKeyBag" */
+47,	/* "pkcs9" */
+401,	/* "policyConstraints" */
+747,	/* "policyMappings" */
+862,	/* "postOfficeBox" */
+861,	/* "postalAddress" */
+661,	/* "postalCode" */
+683,	/* "ppBasis" */
+872,	/* "preferredDeliveryMethod" */
+873,	/* "presentationAddress" */
+816,	/* "prf-gostr3411-94" */
+406,	/* "prime-field" */
+409,	/* "prime192v1" */
+410,	/* "prime192v2" */
+411,	/* "prime192v3" */
+412,	/* "prime239v1" */
+413,	/* "prime239v2" */
+414,	/* "prime239v3" */
+415,	/* "prime256v1" */
+385,	/* "private" */
+84,	/* "privateKeyUsagePeriod" */
+886,	/* "protocolInformation" */
+663,	/* "proxyCertInfo" */
+510,	/* "pseudonym" */
+435,	/* "pss" */
+286,	/* "qcStatements" */
+457,	/* "qualityLabelledData" */
+450,	/* "rFC822localPart" */
+870,	/* "registeredAddress" */
+400,	/* "role" */
+877,	/* "roleOccupant" */
+448,	/* "room" */
+463,	/* "roomNumber" */
+ 6,	/* "rsaEncryption" */
+644,	/* "rsaOAEPEncryptionSET" */
+377,	/* "rsaSignature" */
+ 1,	/* "rsadsi" */
+482,	/* "sOARecord" */
+155,	/* "safeContentsBag" */
+291,	/* "sbgp-autonomousSysNum" */
+290,	/* "sbgp-ipAddrBlock" */
+292,	/* "sbgp-routerIdentifier" */
+159,	/* "sdsiCertificate" */
+859,	/* "searchGuide" */
+704,	/* "secp112r1" */
+705,	/* "secp112r2" */
+706,	/* "secp128r1" */
+707,	/* "secp128r2" */
+708,	/* "secp160k1" */
+709,	/* "secp160r1" */
+710,	/* "secp160r2" */
+711,	/* "secp192k1" */
+712,	/* "secp224k1" */
+713,	/* "secp224r1" */
+714,	/* "secp256k1" */
+715,	/* "secp384r1" */
+716,	/* "secp521r1" */
+154,	/* "secretBag" */
+474,	/* "secretary" */
+717,	/* "sect113r1" */
+718,	/* "sect113r2" */
+719,	/* "sect131r1" */
+720,	/* "sect131r2" */
+721,	/* "sect163k1" */
+722,	/* "sect163r1" */
+723,	/* "sect163r2" */
+724,	/* "sect193r1" */
+725,	/* "sect193r2" */
+726,	/* "sect233k1" */
+727,	/* "sect233r1" */
+728,	/* "sect239k1" */
+729,	/* "sect283k1" */
+730,	/* "sect283r1" */
+731,	/* "sect409k1" */
+732,	/* "sect409r1" */
+733,	/* "sect571k1" */
+734,	/* "sect571r1" */
+386,	/* "security" */
+878,	/* "seeAlso" */
+394,	/* "selected-attribute-types" */
+105,	/* "serialNumber" */
+129,	/* "serverAuth" */
+371,	/* "serviceLocator" */
+625,	/* "set-addPolicy" */
+515,	/* "set-attr" */
+518,	/* "set-brand" */
+638,	/* "set-brand-AmericanExpress" */
+637,	/* "set-brand-Diners" */
+636,	/* "set-brand-IATA-ATA" */
+639,	/* "set-brand-JCB" */
+641,	/* "set-brand-MasterCard" */
+642,	/* "set-brand-Novus" */
+640,	/* "set-brand-Visa" */
+517,	/* "set-certExt" */
+513,	/* "set-ctype" */
+514,	/* "set-msgExt" */
+516,	/* "set-policy" */
+607,	/* "set-policy-root" */
+624,	/* "set-rootKeyThumb" */
+620,	/* "setAttr-Cert" */
+631,	/* "setAttr-GenCryptgrm" */
+623,	/* "setAttr-IssCap" */
+628,	/* "setAttr-IssCap-CVM" */
+630,	/* "setAttr-IssCap-Sig" */
+629,	/* "setAttr-IssCap-T2" */
+621,	/* "setAttr-PGWYcap" */
+635,	/* "setAttr-SecDevSig" */
+632,	/* "setAttr-T2Enc" */
+633,	/* "setAttr-T2cleartxt" */
+634,	/* "setAttr-TokICCsig" */
+627,	/* "setAttr-Token-B0Prime" */
+626,	/* "setAttr-Token-EMV" */
+622,	/* "setAttr-TokenType" */
+619,	/* "setCext-IssuerCapabilities" */
+615,	/* "setCext-PGWYcapabilities" */
+616,	/* "setCext-TokenIdentifier" */
+618,	/* "setCext-TokenType" */
+617,	/* "setCext-Track2Data" */
+611,	/* "setCext-cCertRequired" */
+609,	/* "setCext-certType" */
+608,	/* "setCext-hashedRoot" */
+610,	/* "setCext-merchData" */
+613,	/* "setCext-setExt" */
+614,	/* "setCext-setQualf" */
+612,	/* "setCext-tunneling" */
+540,	/* "setct-AcqCardCodeMsg" */
+576,	/* "setct-AcqCardCodeMsgTBE" */
+570,	/* "setct-AuthReqTBE" */
+534,	/* "setct-AuthReqTBS" */
+527,	/* "setct-AuthResBaggage" */
+571,	/* "setct-AuthResTBE" */
+572,	/* "setct-AuthResTBEX" */
+535,	/* "setct-AuthResTBS" */
+536,	/* "setct-AuthResTBSX" */
+528,	/* "setct-AuthRevReqBaggage" */
+577,	/* "setct-AuthRevReqTBE" */
+541,	/* "setct-AuthRevReqTBS" */
+529,	/* "setct-AuthRevResBaggage" */
+542,	/* "setct-AuthRevResData" */
+578,	/* "setct-AuthRevResTBE" */
+579,	/* "setct-AuthRevResTBEB" */
+543,	/* "setct-AuthRevResTBS" */
+573,	/* "setct-AuthTokenTBE" */
+537,	/* "setct-AuthTokenTBS" */
+600,	/* "setct-BCIDistributionTBS" */
+558,	/* "setct-BatchAdminReqData" */
+592,	/* "setct-BatchAdminReqTBE" */
+559,	/* "setct-BatchAdminResData" */
+593,	/* "setct-BatchAdminResTBE" */
+599,	/* "setct-CRLNotificationResTBS" */
+598,	/* "setct-CRLNotificationTBS" */
+580,	/* "setct-CapReqTBE" */
+581,	/* "setct-CapReqTBEX" */
+544,	/* "setct-CapReqTBS" */
+545,	/* "setct-CapReqTBSX" */
+546,	/* "setct-CapResData" */
+582,	/* "setct-CapResTBE" */
+583,	/* "setct-CapRevReqTBE" */
+584,	/* "setct-CapRevReqTBEX" */
+547,	/* "setct-CapRevReqTBS" */
+548,	/* "setct-CapRevReqTBSX" */
+549,	/* "setct-CapRevResData" */
+585,	/* "setct-CapRevResTBE" */
+538,	/* "setct-CapTokenData" */
+530,	/* "setct-CapTokenSeq" */
+574,	/* "setct-CapTokenTBE" */
+575,	/* "setct-CapTokenTBEX" */
+539,	/* "setct-CapTokenTBS" */
+560,	/* "setct-CardCInitResTBS" */
+566,	/* "setct-CertInqReqTBS" */
+563,	/* "setct-CertReqData" */
+595,	/* "setct-CertReqTBE" */
+596,	/* "setct-CertReqTBEX" */
+564,	/* "setct-CertReqTBS" */
+565,	/* "setct-CertResData" */
+597,	/* "setct-CertResTBE" */
+586,	/* "setct-CredReqTBE" */
+587,	/* "setct-CredReqTBEX" */
+550,	/* "setct-CredReqTBS" */
+551,	/* "setct-CredReqTBSX" */
+552,	/* "setct-CredResData" */
+588,	/* "setct-CredResTBE" */
+589,	/* "setct-CredRevReqTBE" */
+590,	/* "setct-CredRevReqTBEX" */
+553,	/* "setct-CredRevReqTBS" */
+554,	/* "setct-CredRevReqTBSX" */
+555,	/* "setct-CredRevResData" */
+591,	/* "setct-CredRevResTBE" */
+567,	/* "setct-ErrorTBS" */
+526,	/* "setct-HODInput" */
+561,	/* "setct-MeAqCInitResTBS" */
+522,	/* "setct-OIData" */
+519,	/* "setct-PANData" */
+521,	/* "setct-PANOnly" */
+520,	/* "setct-PANToken" */
+556,	/* "setct-PCertReqData" */
+557,	/* "setct-PCertResTBS" */
+523,	/* "setct-PI" */
+532,	/* "setct-PI-TBS" */
+524,	/* "setct-PIData" */
+525,	/* "setct-PIDataUnsigned" */
+568,	/* "setct-PIDualSignedTBE" */
+569,	/* "setct-PIUnsignedTBE" */
+531,	/* "setct-PInitResData" */
+533,	/* "setct-PResData" */
+594,	/* "setct-RegFormReqTBE" */
+562,	/* "setct-RegFormResTBS" */
+606,	/* "setext-cv" */
+601,	/* "setext-genCrypt" */
+602,	/* "setext-miAuth" */
+604,	/* "setext-pinAny" */
+603,	/* "setext-pinSecure" */
+605,	/* "setext-track2" */
+52,	/* "signingTime" */
+454,	/* "simpleSecurityObject" */
+496,	/* "singleLevelQuality" */
+387,	/* "snmpv2" */
+660,	/* "street" */
+85,	/* "subjectAltName" */
+769,	/* "subjectDirectoryAttributes" */
+398,	/* "subjectInfoAccess" */
+82,	/* "subjectKeyIdentifier" */
+498,	/* "subtreeMaximumQuality" */
+497,	/* "subtreeMinimumQuality" */
+890,	/* "supportedAlgorithms" */
+874,	/* "supportedApplicationContext" */
+402,	/* "targetInformation" */
+864,	/* "telephoneNumber" */
+866,	/* "teletexTerminalIdentifier" */
+865,	/* "telexNumber" */
+459,	/* "textEncodedORAddress" */
+293,	/* "textNotice" */
+133,	/* "timeStamping" */
+106,	/* "title" */
+682,	/* "tpBasis" */
+375,	/* "trustRoot" */
+436,	/* "ucl" */
+888,	/* "uniqueMember" */
+55,	/* "unstructuredAddress" */
+49,	/* "unstructuredName" */
+880,	/* "userCertificate" */
+465,	/* "userClass" */
+879,	/* "userPassword" */
+373,	/* "valid" */
+678,	/* "wap" */
+679,	/* "wap-wsg" */
+735,	/* "wap-wsg-idm-ecid-wtls1" */
+743,	/* "wap-wsg-idm-ecid-wtls10" */
+744,	/* "wap-wsg-idm-ecid-wtls11" */
+745,	/* "wap-wsg-idm-ecid-wtls12" */
+736,	/* "wap-wsg-idm-ecid-wtls3" */
+737,	/* "wap-wsg-idm-ecid-wtls4" */
+738,	/* "wap-wsg-idm-ecid-wtls5" */
+739,	/* "wap-wsg-idm-ecid-wtls6" */
+740,	/* "wap-wsg-idm-ecid-wtls7" */
+741,	/* "wap-wsg-idm-ecid-wtls8" */
+742,	/* "wap-wsg-idm-ecid-wtls9" */
+804,	/* "whirlpool" */
+868,	/* "x121Address" */
+503,	/* "x500UniqueIdentifier" */
+158,	/* "x509Certificate" */
+160,	/* "x509Crl" */
+};
+
+static const unsigned int kNIDsInLongNameOrder[NUM_LN]={
+363,	/* "AD Time Stamping" */
+405,	/* "ANSI X9.62" */
+368,	/* "Acceptable OCSP Responses" */
+910,	/* "Any Extended Key Usage" */
+664,	/* "Any language" */
+177,	/* "Authority Information Access" */
+365,	/* "Basic OCSP Response" */
+285,	/* "Biometric Info" */
+179,	/* "CA Issuers" */
+785,	/* "CA Repository" */
+131,	/* "Code Signing" */
+783,	/* "Diffie-Hellman based MAC" */
+382,	/* "Directory" */
+392,	/* "Domain" */
+132,	/* "E-mail Protection" */
+389,	/* "Enterprises" */
+384,	/* "Experimental" */
+372,	/* "Extended OCSP Status" */
+172,	/* "Extension Request" */
+813,	/* "GOST 28147-89" */
+849,	/* "GOST 28147-89 Cryptocom ParamSet" */
+815,	/* "GOST 28147-89 MAC" */
+851,	/* "GOST 34.10-2001 Cryptocom" */
+850,	/* "GOST 34.10-94 Cryptocom" */
+811,	/* "GOST R 34.10-2001" */
+817,	/* "GOST R 34.10-2001 DH" */
+812,	/* "GOST R 34.10-94" */
+818,	/* "GOST R 34.10-94 DH" */
+809,	/* "GOST R 34.11-94" */
+816,	/* "GOST R 34.11-94 PRF" */
+807,	/* "GOST R 34.11-94 with GOST R 34.10-2001" */
+853,	/* "GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom" */
+808,	/* "GOST R 34.11-94 with GOST R 34.10-94" */
+852,	/* "GOST R 34.11-94 with GOST R 34.10-94 Cryptocom" */
+854,	/* "GOST R 3410-2001 Parameter Set Cryptocom" */
+810,	/* "HMAC GOST 34.11-94" */
+432,	/* "Hold Instruction Call Issuer" */
+430,	/* "Hold Instruction Code" */
+431,	/* "Hold Instruction None" */
+433,	/* "Hold Instruction Reject" */
+634,	/* "ICC or token signature" */
+294,	/* "IPSec End System" */
+295,	/* "IPSec Tunnel" */
+296,	/* "IPSec User" */
+182,	/* "ISO Member Body" */
+183,	/* "ISO US Member Body" */
+667,	/* "Independent" */
+665,	/* "Inherit all" */
+647,	/* "International Organizations" */
+142,	/* "Invalidity Date" */
+504,	/* "MIME MHS" */
+388,	/* "Mail" */
+383,	/* "Management" */
+417,	/* "Microsoft CSP Name" */
+135,	/* "Microsoft Commercial Code Signing" */
+138,	/* "Microsoft Encrypted File System" */
+171,	/* "Microsoft Extension Request" */
+134,	/* "Microsoft Individual Code Signing" */
+856,	/* "Microsoft Local Key set" */
+137,	/* "Microsoft Server Gated Crypto" */
+648,	/* "Microsoft Smartcardlogin" */
+136,	/* "Microsoft Trust List Signing" */
+649,	/* "Microsoft Universal Principal Name" */
+393,	/* "NULL" */
+404,	/* "NULL" */
+72,	/* "Netscape Base Url" */
+76,	/* "Netscape CA Policy Url" */
+74,	/* "Netscape CA Revocation Url" */
+71,	/* "Netscape Cert Type" */
+58,	/* "Netscape Certificate Extension" */
+79,	/* "Netscape Certificate Sequence" */
+78,	/* "Netscape Comment" */
+57,	/* "Netscape Communications Corp." */
+59,	/* "Netscape Data Type" */
+75,	/* "Netscape Renewal Url" */
+73,	/* "Netscape Revocation Url" */
+77,	/* "Netscape SSL Server Name" */
+139,	/* "Netscape Server Gated Crypto" */
+178,	/* "OCSP" */
+370,	/* "OCSP Archive Cutoff" */
+367,	/* "OCSP CRL ID" */
+369,	/* "OCSP No Check" */
+366,	/* "OCSP Nonce" */
+371,	/* "OCSP Service Locator" */
+180,	/* "OCSP Signing" */
+161,	/* "PBES2" */
+69,	/* "PBKDF2" */
+162,	/* "PBMAC1" */
+127,	/* "PKIX" */
+858,	/* "Permanent Identifier" */
+164,	/* "Policy Qualifier CPS" */
+165,	/* "Policy Qualifier User Notice" */
+385,	/* "Private" */
+663,	/* "Proxy Certificate Information" */
+ 1,	/* "RSA Data Security, Inc." */
+ 2,	/* "RSA Data Security, Inc. PKCS" */
+188,	/* "S/MIME" */
+167,	/* "S/MIME Capabilities" */
+387,	/* "SNMPv2" */
+512,	/* "Secure Electronic Transactions" */
+386,	/* "Security" */
+394,	/* "Selected Attribute Types" */
+143,	/* "Strong Extranet ID" */
+398,	/* "Subject Information Access" */
+130,	/* "TLS Web Client Authentication" */
+129,	/* "TLS Web Server Authentication" */
+133,	/* "Time Stamping" */
+375,	/* "Trust Root" */
+12,	/* "X509" */
+402,	/* "X509v3 AC Targeting" */
+746,	/* "X509v3 Any Policy" */
+90,	/* "X509v3 Authority Key Identifier" */
+87,	/* "X509v3 Basic Constraints" */
+103,	/* "X509v3 CRL Distribution Points" */
+88,	/* "X509v3 CRL Number" */
+141,	/* "X509v3 CRL Reason Code" */
+771,	/* "X509v3 Certificate Issuer" */
+89,	/* "X509v3 Certificate Policies" */
+140,	/* "X509v3 Delta CRL Indicator" */
+126,	/* "X509v3 Extended Key Usage" */
+857,	/* "X509v3 Freshest CRL" */
+748,	/* "X509v3 Inhibit Any Policy" */
+86,	/* "X509v3 Issuer Alternative Name" */
+770,	/* "X509v3 Issuing Distrubution Point" */
+83,	/* "X509v3 Key Usage" */
+666,	/* "X509v3 Name Constraints" */
+403,	/* "X509v3 No Revocation Available" */
+401,	/* "X509v3 Policy Constraints" */
+747,	/* "X509v3 Policy Mappings" */
+84,	/* "X509v3 Private Key Usage Period" */
+85,	/* "X509v3 Subject Alternative Name" */
+769,	/* "X509v3 Subject Directory Attributes" */
+82,	/* "X509v3 Subject Key Identifier" */
+920,	/* "X9.42 DH" */
+184,	/* "X9.57" */
+185,	/* "X9.57 CM ?" */
+478,	/* "aRecord" */
+289,	/* "aaControls" */
+287,	/* "ac-auditEntity" */
+397,	/* "ac-proxying" */
+288,	/* "ac-targeting" */
+446,	/* "account" */
+364,	/* "ad dvcs" */
+606,	/* "additional verification" */
+419,	/* "aes-128-cbc" */
+916,	/* "aes-128-cbc-hmac-sha1" */
+896,	/* "aes-128-ccm" */
+421,	/* "aes-128-cfb" */
+650,	/* "aes-128-cfb1" */
+653,	/* "aes-128-cfb8" */
+904,	/* "aes-128-ctr" */
+418,	/* "aes-128-ecb" */
+895,	/* "aes-128-gcm" */
+420,	/* "aes-128-ofb" */
+913,	/* "aes-128-xts" */
+423,	/* "aes-192-cbc" */
+917,	/* "aes-192-cbc-hmac-sha1" */
+899,	/* "aes-192-ccm" */
+425,	/* "aes-192-cfb" */
+651,	/* "aes-192-cfb1" */
+654,	/* "aes-192-cfb8" */
+905,	/* "aes-192-ctr" */
+422,	/* "aes-192-ecb" */
+898,	/* "aes-192-gcm" */
+424,	/* "aes-192-ofb" */
+427,	/* "aes-256-cbc" */
+918,	/* "aes-256-cbc-hmac-sha1" */
+902,	/* "aes-256-ccm" */
+429,	/* "aes-256-cfb" */
+652,	/* "aes-256-cfb1" */
+655,	/* "aes-256-cfb8" */
+906,	/* "aes-256-ctr" */
+426,	/* "aes-256-ecb" */
+901,	/* "aes-256-gcm" */
+428,	/* "aes-256-ofb" */
+914,	/* "aes-256-xts" */
+376,	/* "algorithm" */
+484,	/* "associatedDomain" */
+485,	/* "associatedName" */
+501,	/* "audio" */
+882,	/* "authorityRevocationList" */
+91,	/* "bf-cbc" */
+93,	/* "bf-cfb" */
+92,	/* "bf-ecb" */
+94,	/* "bf-ofb" */
+921,	/* "brainpoolP160r1" */
+922,	/* "brainpoolP160t1" */
+923,	/* "brainpoolP192r1" */
+924,	/* "brainpoolP192t1" */
+925,	/* "brainpoolP224r1" */
+926,	/* "brainpoolP224t1" */
+927,	/* "brainpoolP256r1" */
+928,	/* "brainpoolP256t1" */
+929,	/* "brainpoolP320r1" */
+930,	/* "brainpoolP320t1" */
+931,	/* "brainpoolP384r1" */
+932,	/* "brainpoolP384t1" */
+933,	/* "brainpoolP512r1" */
+934,	/* "brainpoolP512t1" */
+494,	/* "buildingName" */
+860,	/* "businessCategory" */
+691,	/* "c2onb191v4" */
+692,	/* "c2onb191v5" */
+697,	/* "c2onb239v4" */
+698,	/* "c2onb239v5" */
+684,	/* "c2pnb163v1" */
+685,	/* "c2pnb163v2" */
+686,	/* "c2pnb163v3" */
+687,	/* "c2pnb176v1" */
+693,	/* "c2pnb208w1" */
+699,	/* "c2pnb272w1" */
+700,	/* "c2pnb304w1" */
+702,	/* "c2pnb368w1" */
+688,	/* "c2tnb191v1" */
+689,	/* "c2tnb191v2" */
+690,	/* "c2tnb191v3" */
+694,	/* "c2tnb239v1" */
+695,	/* "c2tnb239v2" */
+696,	/* "c2tnb239v3" */
+701,	/* "c2tnb359v1" */
+703,	/* "c2tnb431r1" */
+881,	/* "cACertificate" */
+483,	/* "cNAMERecord" */
+751,	/* "camellia-128-cbc" */
+757,	/* "camellia-128-cfb" */
+760,	/* "camellia-128-cfb1" */
+763,	/* "camellia-128-cfb8" */
+754,	/* "camellia-128-ecb" */
+766,	/* "camellia-128-ofb" */
+752,	/* "camellia-192-cbc" */
+758,	/* "camellia-192-cfb" */
+761,	/* "camellia-192-cfb1" */
+764,	/* "camellia-192-cfb8" */
+755,	/* "camellia-192-ecb" */
+767,	/* "camellia-192-ofb" */
+753,	/* "camellia-256-cbc" */
+759,	/* "camellia-256-cfb" */
+762,	/* "camellia-256-cfb1" */
+765,	/* "camellia-256-cfb8" */
+756,	/* "camellia-256-ecb" */
+768,	/* "camellia-256-ofb" */
+443,	/* "caseIgnoreIA5StringSyntax" */
+108,	/* "cast5-cbc" */
+110,	/* "cast5-cfb" */
+109,	/* "cast5-ecb" */
+111,	/* "cast5-ofb" */
+152,	/* "certBag" */
+677,	/* "certicom-arc" */
+517,	/* "certificate extensions" */
+883,	/* "certificateRevocationList" */
+54,	/* "challengePassword" */
+407,	/* "characteristic-two-field" */
+395,	/* "clearance" */
+633,	/* "cleartext track 2" */
+894,	/* "cmac" */
+13,	/* "commonName" */
+513,	/* "content types" */
+50,	/* "contentType" */
+53,	/* "countersignature" */
+14,	/* "countryName" */
+153,	/* "crlBag" */
+884,	/* "crossCertificatePair" */
+806,	/* "cryptocom" */
+805,	/* "cryptopro" */
+500,	/* "dITRedirect" */
+451,	/* "dNSDomain" */
+495,	/* "dSAQuality" */
+434,	/* "data" */
+390,	/* "dcObject" */
+891,	/* "deltaRevocationList" */
+31,	/* "des-cbc" */
+643,	/* "des-cdmf" */
+30,	/* "des-cfb" */
+656,	/* "des-cfb1" */
+657,	/* "des-cfb8" */
+29,	/* "des-ecb" */
+32,	/* "des-ede" */
+43,	/* "des-ede-cbc" */
+60,	/* "des-ede-cfb" */
+62,	/* "des-ede-ofb" */
+33,	/* "des-ede3" */
+44,	/* "des-ede3-cbc" */
+61,	/* "des-ede3-cfb" */
+658,	/* "des-ede3-cfb1" */
+659,	/* "des-ede3-cfb8" */
+63,	/* "des-ede3-ofb" */
+45,	/* "des-ofb" */
+107,	/* "description" */
+871,	/* "destinationIndicator" */
+80,	/* "desx-cbc" */
+950,	/* "dh-cofactor-kdf" */
+949,	/* "dh-std-kdf" */
+28,	/* "dhKeyAgreement" */
+944,	/* "dhSinglePass-cofactorDH-sha1kdf-scheme" */
+945,	/* "dhSinglePass-cofactorDH-sha224kdf-scheme" */
+946,	/* "dhSinglePass-cofactorDH-sha256kdf-scheme" */
+947,	/* "dhSinglePass-cofactorDH-sha384kdf-scheme" */
+948,	/* "dhSinglePass-cofactorDH-sha512kdf-scheme" */
+939,	/* "dhSinglePass-stdDH-sha1kdf-scheme" */
+940,	/* "dhSinglePass-stdDH-sha224kdf-scheme" */
+941,	/* "dhSinglePass-stdDH-sha256kdf-scheme" */
+942,	/* "dhSinglePass-stdDH-sha384kdf-scheme" */
+943,	/* "dhSinglePass-stdDH-sha512kdf-scheme" */
+11,	/* "directory services (X.500)" */
+378,	/* "directory services - algorithms" */
+887,	/* "distinguishedName" */
+892,	/* "dmdName" */
+174,	/* "dnQualifier" */
+447,	/* "document" */
+471,	/* "documentAuthor" */
+468,	/* "documentIdentifier" */
+472,	/* "documentLocation" */
+502,	/* "documentPublisher" */
+449,	/* "documentSeries" */
+469,	/* "documentTitle" */
+470,	/* "documentVersion" */
+380,	/* "dod" */
+391,	/* "domainComponent" */
+452,	/* "domainRelatedObject" */
+116,	/* "dsaEncryption" */
+67,	/* "dsaEncryption-old" */
+66,	/* "dsaWithSHA" */
+113,	/* "dsaWithSHA1" */
+70,	/* "dsaWithSHA1-old" */
+802,	/* "dsa_with_SHA224" */
+803,	/* "dsa_with_SHA256" */
+297,	/* "dvcs" */
+791,	/* "ecdsa-with-Recommended" */
+416,	/* "ecdsa-with-SHA1" */
+793,	/* "ecdsa-with-SHA224" */
+794,	/* "ecdsa-with-SHA256" */
+795,	/* "ecdsa-with-SHA384" */
+796,	/* "ecdsa-with-SHA512" */
+792,	/* "ecdsa-with-Specified" */
+48,	/* "emailAddress" */
+632,	/* "encrypted track 2" */
+885,	/* "enhancedSearchGuide" */
+56,	/* "extendedCertificateAttributes" */
+867,	/* "facsimileTelephoneNumber" */
+462,	/* "favouriteDrink" */
+453,	/* "friendlyCountry" */
+490,	/* "friendlyCountryName" */
+156,	/* "friendlyName" */
+631,	/* "generate cryptogram" */
+509,	/* "generationQualifier" */
+601,	/* "generic cryptogram" */
+99,	/* "givenName" */
+814,	/* "gost89-cnt" */
+855,	/* "hmac" */
+780,	/* "hmac-md5" */
+781,	/* "hmac-sha1" */
+797,	/* "hmacWithMD5" */
+163,	/* "hmacWithSHA1" */
+798,	/* "hmacWithSHA224" */
+799,	/* "hmacWithSHA256" */
+800,	/* "hmacWithSHA384" */
+801,	/* "hmacWithSHA512" */
+486,	/* "homePostalAddress" */
+473,	/* "homeTelephoneNumber" */
+466,	/* "host" */
+889,	/* "houseIdentifier" */
+442,	/* "iA5StringSyntax" */
+381,	/* "iana" */
+824,	/* "id-Gost28147-89-CryptoPro-A-ParamSet" */
+825,	/* "id-Gost28147-89-CryptoPro-B-ParamSet" */
+826,	/* "id-Gost28147-89-CryptoPro-C-ParamSet" */
+827,	/* "id-Gost28147-89-CryptoPro-D-ParamSet" */
+819,	/* "id-Gost28147-89-CryptoPro-KeyMeshing" */
+829,	/* "id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet" */
+828,	/* "id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet" */
+830,	/* "id-Gost28147-89-CryptoPro-RIC-1-ParamSet" */
+820,	/* "id-Gost28147-89-None-KeyMeshing" */
+823,	/* "id-Gost28147-89-TestParamSet" */
+840,	/* "id-GostR3410-2001-CryptoPro-A-ParamSet" */
+841,	/* "id-GostR3410-2001-CryptoPro-B-ParamSet" */
+842,	/* "id-GostR3410-2001-CryptoPro-C-ParamSet" */
+843,	/* "id-GostR3410-2001-CryptoPro-XchA-ParamSet" */
+844,	/* "id-GostR3410-2001-CryptoPro-XchB-ParamSet" */
+839,	/* "id-GostR3410-2001-TestParamSet" */
+832,	/* "id-GostR3410-94-CryptoPro-A-ParamSet" */
+833,	/* "id-GostR3410-94-CryptoPro-B-ParamSet" */
+834,	/* "id-GostR3410-94-CryptoPro-C-ParamSet" */
+835,	/* "id-GostR3410-94-CryptoPro-D-ParamSet" */
+836,	/* "id-GostR3410-94-CryptoPro-XchA-ParamSet" */
+837,	/* "id-GostR3410-94-CryptoPro-XchB-ParamSet" */
+838,	/* "id-GostR3410-94-CryptoPro-XchC-ParamSet" */
+831,	/* "id-GostR3410-94-TestParamSet" */
+845,	/* "id-GostR3410-94-a" */
+846,	/* "id-GostR3410-94-aBis" */
+847,	/* "id-GostR3410-94-b" */
+848,	/* "id-GostR3410-94-bBis" */
+822,	/* "id-GostR3411-94-CryptoProParamSet" */
+821,	/* "id-GostR3411-94-TestParamSet" */
+266,	/* "id-aca" */
+355,	/* "id-aca-accessIdentity" */
+354,	/* "id-aca-authenticationInfo" */
+356,	/* "id-aca-chargingIdentity" */
+399,	/* "id-aca-encAttrs" */
+357,	/* "id-aca-group" */
+358,	/* "id-aca-role" */
+176,	/* "id-ad" */
+788,	/* "id-aes128-wrap" */
+897,	/* "id-aes128-wrap-pad" */
+789,	/* "id-aes192-wrap" */
+900,	/* "id-aes192-wrap-pad" */
+790,	/* "id-aes256-wrap" */
+903,	/* "id-aes256-wrap-pad" */
+262,	/* "id-alg" */
+893,	/* "id-alg-PWRI-KEK" */
+323,	/* "id-alg-des40" */
+326,	/* "id-alg-dh-pop" */
+325,	/* "id-alg-dh-sig-hmac-sha1" */
+324,	/* "id-alg-noSignature" */
+907,	/* "id-camellia128-wrap" */
+908,	/* "id-camellia192-wrap" */
+909,	/* "id-camellia256-wrap" */
+268,	/* "id-cct" */
+361,	/* "id-cct-PKIData" */
+362,	/* "id-cct-PKIResponse" */
+360,	/* "id-cct-crs" */
+81,	/* "id-ce" */
+680,	/* "id-characteristic-two-basis" */
+263,	/* "id-cmc" */
+334,	/* "id-cmc-addExtensions" */
+346,	/* "id-cmc-confirmCertAcceptance" */
+330,	/* "id-cmc-dataReturn" */
+336,	/* "id-cmc-decryptedPOP" */
+335,	/* "id-cmc-encryptedPOP" */
+339,	/* "id-cmc-getCRL" */
+338,	/* "id-cmc-getCert" */
+328,	/* "id-cmc-identification" */
+329,	/* "id-cmc-identityProof" */
+337,	/* "id-cmc-lraPOPWitness" */
+344,	/* "id-cmc-popLinkRandom" */
+345,	/* "id-cmc-popLinkWitness" */
+343,	/* "id-cmc-queryPending" */
+333,	/* "id-cmc-recipientNonce" */
+341,	/* "id-cmc-regInfo" */
+342,	/* "id-cmc-responseInfo" */
+340,	/* "id-cmc-revokeRequest" */
+332,	/* "id-cmc-senderNonce" */
+327,	/* "id-cmc-statusInfo" */
+331,	/* "id-cmc-transactionId" */
+787,	/* "id-ct-asciiTextWithCRLF" */
+408,	/* "id-ecPublicKey" */
+508,	/* "id-hex-multipart-message" */
+507,	/* "id-hex-partial-message" */
+260,	/* "id-it" */
+302,	/* "id-it-caKeyUpdateInfo" */
+298,	/* "id-it-caProtEncCert" */
+311,	/* "id-it-confirmWaitTime" */
+303,	/* "id-it-currentCRL" */
+300,	/* "id-it-encKeyPairTypes" */
+310,	/* "id-it-implicitConfirm" */
+308,	/* "id-it-keyPairParamRep" */
+307,	/* "id-it-keyPairParamReq" */
+312,	/* "id-it-origPKIMessage" */
+301,	/* "id-it-preferredSymmAlg" */
+309,	/* "id-it-revPassphrase" */
+299,	/* "id-it-signKeyPairTypes" */
+305,	/* "id-it-subscriptionRequest" */
+306,	/* "id-it-subscriptionResponse" */
+784,	/* "id-it-suppLangTags" */
+304,	/* "id-it-unsupportedOIDs" */
+128,	/* "id-kp" */
+280,	/* "id-mod-attribute-cert" */
+274,	/* "id-mod-cmc" */
+277,	/* "id-mod-cmp" */
+284,	/* "id-mod-cmp2000" */
+273,	/* "id-mod-crmf" */
+283,	/* "id-mod-dvcs" */
+275,	/* "id-mod-kea-profile-88" */
+276,	/* "id-mod-kea-profile-93" */
+282,	/* "id-mod-ocsp" */
+278,	/* "id-mod-qualified-cert-88" */
+279,	/* "id-mod-qualified-cert-93" */
+281,	/* "id-mod-timestamp-protocol" */
+264,	/* "id-on" */
+347,	/* "id-on-personalData" */
+265,	/* "id-pda" */
+352,	/* "id-pda-countryOfCitizenship" */
+353,	/* "id-pda-countryOfResidence" */
+348,	/* "id-pda-dateOfBirth" */
+351,	/* "id-pda-gender" */
+349,	/* "id-pda-placeOfBirth" */
+175,	/* "id-pe" */
+261,	/* "id-pkip" */
+258,	/* "id-pkix-mod" */
+269,	/* "id-pkix1-explicit-88" */
+271,	/* "id-pkix1-explicit-93" */
+270,	/* "id-pkix1-implicit-88" */
+272,	/* "id-pkix1-implicit-93" */
+662,	/* "id-ppl" */
+267,	/* "id-qcs" */
+359,	/* "id-qcs-pkixQCSyntax-v1" */
+259,	/* "id-qt" */
+313,	/* "id-regCtrl" */
+316,	/* "id-regCtrl-authenticator" */
+319,	/* "id-regCtrl-oldCertID" */
+318,	/* "id-regCtrl-pkiArchiveOptions" */
+317,	/* "id-regCtrl-pkiPublicationInfo" */
+320,	/* "id-regCtrl-protocolEncrKey" */
+315,	/* "id-regCtrl-regToken" */
+314,	/* "id-regInfo" */
+322,	/* "id-regInfo-certReq" */
+321,	/* "id-regInfo-utf8Pairs" */
+191,	/* "id-smime-aa" */
+215,	/* "id-smime-aa-contentHint" */
+218,	/* "id-smime-aa-contentIdentifier" */
+221,	/* "id-smime-aa-contentReference" */
+240,	/* "id-smime-aa-dvcs-dvc" */
+217,	/* "id-smime-aa-encapContentType" */
+222,	/* "id-smime-aa-encrypKeyPref" */
+220,	/* "id-smime-aa-equivalentLabels" */
+232,	/* "id-smime-aa-ets-CertificateRefs" */
+233,	/* "id-smime-aa-ets-RevocationRefs" */
+238,	/* "id-smime-aa-ets-archiveTimeStamp" */
+237,	/* "id-smime-aa-ets-certCRLTimestamp" */
+234,	/* "id-smime-aa-ets-certValues" */
+227,	/* "id-smime-aa-ets-commitmentType" */
+231,	/* "id-smime-aa-ets-contentTimestamp" */
+236,	/* "id-smime-aa-ets-escTimeStamp" */
+230,	/* "id-smime-aa-ets-otherSigCert" */
+235,	/* "id-smime-aa-ets-revocationValues" */
+226,	/* "id-smime-aa-ets-sigPolicyId" */
+229,	/* "id-smime-aa-ets-signerAttr" */
+228,	/* "id-smime-aa-ets-signerLocation" */
+219,	/* "id-smime-aa-macValue" */
+214,	/* "id-smime-aa-mlExpandHistory" */
+216,	/* "id-smime-aa-msgSigDigest" */
+212,	/* "id-smime-aa-receiptRequest" */
+213,	/* "id-smime-aa-securityLabel" */
+239,	/* "id-smime-aa-signatureType" */
+223,	/* "id-smime-aa-signingCertificate" */
+224,	/* "id-smime-aa-smimeEncryptCerts" */
+225,	/* "id-smime-aa-timeStampToken" */
+192,	/* "id-smime-alg" */
+243,	/* "id-smime-alg-3DESwrap" */
+246,	/* "id-smime-alg-CMS3DESwrap" */
+247,	/* "id-smime-alg-CMSRC2wrap" */
+245,	/* "id-smime-alg-ESDH" */
+241,	/* "id-smime-alg-ESDHwith3DES" */
+242,	/* "id-smime-alg-ESDHwithRC2" */
+244,	/* "id-smime-alg-RC2wrap" */
+193,	/* "id-smime-cd" */
+248,	/* "id-smime-cd-ldap" */
+190,	/* "id-smime-ct" */
+210,	/* "id-smime-ct-DVCSRequestData" */
+211,	/* "id-smime-ct-DVCSResponseData" */
+208,	/* "id-smime-ct-TDTInfo" */
+207,	/* "id-smime-ct-TSTInfo" */
+205,	/* "id-smime-ct-authData" */
+786,	/* "id-smime-ct-compressedData" */
+209,	/* "id-smime-ct-contentInfo" */
+206,	/* "id-smime-ct-publishCert" */
+204,	/* "id-smime-ct-receipt" */
+195,	/* "id-smime-cti" */
+255,	/* "id-smime-cti-ets-proofOfApproval" */
+256,	/* "id-smime-cti-ets-proofOfCreation" */
+253,	/* "id-smime-cti-ets-proofOfDelivery" */
+251,	/* "id-smime-cti-ets-proofOfOrigin" */
+252,	/* "id-smime-cti-ets-proofOfReceipt" */
+254,	/* "id-smime-cti-ets-proofOfSender" */
+189,	/* "id-smime-mod" */
+196,	/* "id-smime-mod-cms" */
+197,	/* "id-smime-mod-ess" */
+202,	/* "id-smime-mod-ets-eSigPolicy-88" */
+203,	/* "id-smime-mod-ets-eSigPolicy-97" */
+200,	/* "id-smime-mod-ets-eSignature-88" */
+201,	/* "id-smime-mod-ets-eSignature-97" */
+199,	/* "id-smime-mod-msg-v3" */
+198,	/* "id-smime-mod-oid" */
+194,	/* "id-smime-spq" */
+250,	/* "id-smime-spq-ets-sqt-unotice" */
+249,	/* "id-smime-spq-ets-sqt-uri" */
+34,	/* "idea-cbc" */
+35,	/* "idea-cfb" */
+36,	/* "idea-ecb" */
+46,	/* "idea-ofb" */
+676,	/* "identified-organization" */
+461,	/* "info" */
+101,	/* "initials" */
+869,	/* "internationaliSDNNumber" */
+749,	/* "ipsec3" */
+750,	/* "ipsec4" */
+181,	/* "iso" */
+623,	/* "issuer capabilities" */
+645,	/* "itu-t" */
+492,	/* "janetMailbox" */
+646,	/* "joint-iso-itu-t" */
+150,	/* "keyBag" */
+773,	/* "kisa" */
+477,	/* "lastModifiedBy" */
+476,	/* "lastModifiedTime" */
+157,	/* "localKeyID" */
+15,	/* "localityName" */
+480,	/* "mXRecord" */
+493,	/* "mailPreferenceOption" */
+467,	/* "manager" */
+ 3,	/* "md2" */
+ 7,	/* "md2WithRSAEncryption" */
+257,	/* "md4" */
+396,	/* "md4WithRSAEncryption" */
+ 4,	/* "md5" */
+114,	/* "md5-sha1" */
+104,	/* "md5WithRSA" */
+ 8,	/* "md5WithRSAEncryption" */
+95,	/* "mdc2" */
+96,	/* "mdc2WithRSA" */
+875,	/* "member" */
+602,	/* "merchant initiated auth" */
+514,	/* "message extensions" */
+51,	/* "messageDigest" */
+911,	/* "mgf1" */
+506,	/* "mime-mhs-bodies" */
+505,	/* "mime-mhs-headings" */
+488,	/* "mobileTelephoneNumber" */
+481,	/* "nSRecord" */
+173,	/* "name" */
+681,	/* "onBasis" */
+379,	/* "org" */
+17,	/* "organizationName" */
+491,	/* "organizationalStatus" */
+18,	/* "organizationalUnitName" */
+475,	/* "otherMailbox" */
+876,	/* "owner" */
+935,	/* "pSpecified" */
+489,	/* "pagerTelephoneNumber" */
+782,	/* "password based MAC" */
+374,	/* "path" */
+621,	/* "payment gateway capabilities" */
+ 9,	/* "pbeWithMD2AndDES-CBC" */
+168,	/* "pbeWithMD2AndRC2-CBC" */
+112,	/* "pbeWithMD5AndCast5CBC" */
+10,	/* "pbeWithMD5AndDES-CBC" */
+169,	/* "pbeWithMD5AndRC2-CBC" */
+148,	/* "pbeWithSHA1And128BitRC2-CBC" */
+144,	/* "pbeWithSHA1And128BitRC4" */
+147,	/* "pbeWithSHA1And2-KeyTripleDES-CBC" */
+146,	/* "pbeWithSHA1And3-KeyTripleDES-CBC" */
+149,	/* "pbeWithSHA1And40BitRC2-CBC" */
+145,	/* "pbeWithSHA1And40BitRC4" */
+170,	/* "pbeWithSHA1AndDES-CBC" */
+68,	/* "pbeWithSHA1AndRC2-CBC" */
+499,	/* "personalSignature" */
+487,	/* "personalTitle" */
+464,	/* "photo" */
+863,	/* "physicalDeliveryOfficeName" */
+437,	/* "pilot" */
+439,	/* "pilotAttributeSyntax" */
+438,	/* "pilotAttributeType" */
+479,	/* "pilotAttributeType27" */
+456,	/* "pilotDSA" */
+441,	/* "pilotGroups" */
+444,	/* "pilotObject" */
+440,	/* "pilotObjectClass" */
+455,	/* "pilotOrganization" */
+445,	/* "pilotPerson" */
+186,	/* "pkcs1" */
+27,	/* "pkcs3" */
+187,	/* "pkcs5" */
+20,	/* "pkcs7" */
+21,	/* "pkcs7-data" */
+25,	/* "pkcs7-digestData" */
+26,	/* "pkcs7-encryptedData" */
+23,	/* "pkcs7-envelopedData" */
+24,	/* "pkcs7-signedAndEnvelopedData" */
+22,	/* "pkcs7-signedData" */
+151,	/* "pkcs8ShroudedKeyBag" */
+47,	/* "pkcs9" */
+862,	/* "postOfficeBox" */
+861,	/* "postalAddress" */
+661,	/* "postalCode" */
+683,	/* "ppBasis" */
+872,	/* "preferredDeliveryMethod" */
+873,	/* "presentationAddress" */
+406,	/* "prime-field" */
+409,	/* "prime192v1" */
+410,	/* "prime192v2" */
+411,	/* "prime192v3" */
+412,	/* "prime239v1" */
+413,	/* "prime239v2" */
+414,	/* "prime239v3" */
+415,	/* "prime256v1" */
+886,	/* "protocolInformation" */
+510,	/* "pseudonym" */
+435,	/* "pss" */
+286,	/* "qcStatements" */
+457,	/* "qualityLabelledData" */
+450,	/* "rFC822localPart" */
+98,	/* "rc2-40-cbc" */
+166,	/* "rc2-64-cbc" */
+37,	/* "rc2-cbc" */
+39,	/* "rc2-cfb" */
+38,	/* "rc2-ecb" */
+40,	/* "rc2-ofb" */
+ 5,	/* "rc4" */
+97,	/* "rc4-40" */
+915,	/* "rc4-hmac-md5" */
+120,	/* "rc5-cbc" */
+122,	/* "rc5-cfb" */
+121,	/* "rc5-ecb" */
+123,	/* "rc5-ofb" */
+870,	/* "registeredAddress" */
+460,	/* "rfc822Mailbox" */
+117,	/* "ripemd160" */
+119,	/* "ripemd160WithRSA" */
+400,	/* "role" */
+877,	/* "roleOccupant" */
+448,	/* "room" */
+463,	/* "roomNumber" */
+19,	/* "rsa" */
+ 6,	/* "rsaEncryption" */
+644,	/* "rsaOAEPEncryptionSET" */
+377,	/* "rsaSignature" */
+919,	/* "rsaesOaep" */
+912,	/* "rsassaPss" */
+124,	/* "run length compression" */
+482,	/* "sOARecord" */
+155,	/* "safeContentsBag" */
+291,	/* "sbgp-autonomousSysNum" */
+290,	/* "sbgp-ipAddrBlock" */
+292,	/* "sbgp-routerIdentifier" */
+159,	/* "sdsiCertificate" */
+859,	/* "searchGuide" */
+704,	/* "secp112r1" */
+705,	/* "secp112r2" */
+706,	/* "secp128r1" */
+707,	/* "secp128r2" */
+708,	/* "secp160k1" */
+709,	/* "secp160r1" */
+710,	/* "secp160r2" */
+711,	/* "secp192k1" */
+712,	/* "secp224k1" */
+713,	/* "secp224r1" */
+714,	/* "secp256k1" */
+715,	/* "secp384r1" */
+716,	/* "secp521r1" */
+154,	/* "secretBag" */
+474,	/* "secretary" */
+717,	/* "sect113r1" */
+718,	/* "sect113r2" */
+719,	/* "sect131r1" */
+720,	/* "sect131r2" */
+721,	/* "sect163k1" */
+722,	/* "sect163r1" */
+723,	/* "sect163r2" */
+724,	/* "sect193r1" */
+725,	/* "sect193r2" */
+726,	/* "sect233k1" */
+727,	/* "sect233r1" */
+728,	/* "sect239k1" */
+729,	/* "sect283k1" */
+730,	/* "sect283r1" */
+731,	/* "sect409k1" */
+732,	/* "sect409r1" */
+733,	/* "sect571k1" */
+734,	/* "sect571r1" */
+635,	/* "secure device signature" */
+878,	/* "seeAlso" */
+777,	/* "seed-cbc" */
+779,	/* "seed-cfb" */
+776,	/* "seed-ecb" */
+778,	/* "seed-ofb" */
+105,	/* "serialNumber" */
+625,	/* "set-addPolicy" */
+515,	/* "set-attr" */
+518,	/* "set-brand" */
+638,	/* "set-brand-AmericanExpress" */
+637,	/* "set-brand-Diners" */
+636,	/* "set-brand-IATA-ATA" */
+639,	/* "set-brand-JCB" */
+641,	/* "set-brand-MasterCard" */
+642,	/* "set-brand-Novus" */
+640,	/* "set-brand-Visa" */
+516,	/* "set-policy" */
+607,	/* "set-policy-root" */
+624,	/* "set-rootKeyThumb" */
+620,	/* "setAttr-Cert" */
+628,	/* "setAttr-IssCap-CVM" */
+630,	/* "setAttr-IssCap-Sig" */
+629,	/* "setAttr-IssCap-T2" */
+627,	/* "setAttr-Token-B0Prime" */
+626,	/* "setAttr-Token-EMV" */
+622,	/* "setAttr-TokenType" */
+619,	/* "setCext-IssuerCapabilities" */
+615,	/* "setCext-PGWYcapabilities" */
+616,	/* "setCext-TokenIdentifier" */
+618,	/* "setCext-TokenType" */
+617,	/* "setCext-Track2Data" */
+611,	/* "setCext-cCertRequired" */
+609,	/* "setCext-certType" */
+608,	/* "setCext-hashedRoot" */
+610,	/* "setCext-merchData" */
+613,	/* "setCext-setExt" */
+614,	/* "setCext-setQualf" */
+612,	/* "setCext-tunneling" */
+540,	/* "setct-AcqCardCodeMsg" */
+576,	/* "setct-AcqCardCodeMsgTBE" */
+570,	/* "setct-AuthReqTBE" */
+534,	/* "setct-AuthReqTBS" */
+527,	/* "setct-AuthResBaggage" */
+571,	/* "setct-AuthResTBE" */
+572,	/* "setct-AuthResTBEX" */
+535,	/* "setct-AuthResTBS" */
+536,	/* "setct-AuthResTBSX" */
+528,	/* "setct-AuthRevReqBaggage" */
+577,	/* "setct-AuthRevReqTBE" */
+541,	/* "setct-AuthRevReqTBS" */
+529,	/* "setct-AuthRevResBaggage" */
+542,	/* "setct-AuthRevResData" */
+578,	/* "setct-AuthRevResTBE" */
+579,	/* "setct-AuthRevResTBEB" */
+543,	/* "setct-AuthRevResTBS" */
+573,	/* "setct-AuthTokenTBE" */
+537,	/* "setct-AuthTokenTBS" */
+600,	/* "setct-BCIDistributionTBS" */
+558,	/* "setct-BatchAdminReqData" */
+592,	/* "setct-BatchAdminReqTBE" */
+559,	/* "setct-BatchAdminResData" */
+593,	/* "setct-BatchAdminResTBE" */
+599,	/* "setct-CRLNotificationResTBS" */
+598,	/* "setct-CRLNotificationTBS" */
+580,	/* "setct-CapReqTBE" */
+581,	/* "setct-CapReqTBEX" */
+544,	/* "setct-CapReqTBS" */
+545,	/* "setct-CapReqTBSX" */
+546,	/* "setct-CapResData" */
+582,	/* "setct-CapResTBE" */
+583,	/* "setct-CapRevReqTBE" */
+584,	/* "setct-CapRevReqTBEX" */
+547,	/* "setct-CapRevReqTBS" */
+548,	/* "setct-CapRevReqTBSX" */
+549,	/* "setct-CapRevResData" */
+585,	/* "setct-CapRevResTBE" */
+538,	/* "setct-CapTokenData" */
+530,	/* "setct-CapTokenSeq" */
+574,	/* "setct-CapTokenTBE" */
+575,	/* "setct-CapTokenTBEX" */
+539,	/* "setct-CapTokenTBS" */
+560,	/* "setct-CardCInitResTBS" */
+566,	/* "setct-CertInqReqTBS" */
+563,	/* "setct-CertReqData" */
+595,	/* "setct-CertReqTBE" */
+596,	/* "setct-CertReqTBEX" */
+564,	/* "setct-CertReqTBS" */
+565,	/* "setct-CertResData" */
+597,	/* "setct-CertResTBE" */
+586,	/* "setct-CredReqTBE" */
+587,	/* "setct-CredReqTBEX" */
+550,	/* "setct-CredReqTBS" */
+551,	/* "setct-CredReqTBSX" */
+552,	/* "setct-CredResData" */
+588,	/* "setct-CredResTBE" */
+589,	/* "setct-CredRevReqTBE" */
+590,	/* "setct-CredRevReqTBEX" */
+553,	/* "setct-CredRevReqTBS" */
+554,	/* "setct-CredRevReqTBSX" */
+555,	/* "setct-CredRevResData" */
+591,	/* "setct-CredRevResTBE" */
+567,	/* "setct-ErrorTBS" */
+526,	/* "setct-HODInput" */
+561,	/* "setct-MeAqCInitResTBS" */
+522,	/* "setct-OIData" */
+519,	/* "setct-PANData" */
+521,	/* "setct-PANOnly" */
+520,	/* "setct-PANToken" */
+556,	/* "setct-PCertReqData" */
+557,	/* "setct-PCertResTBS" */
+523,	/* "setct-PI" */
+532,	/* "setct-PI-TBS" */
+524,	/* "setct-PIData" */
+525,	/* "setct-PIDataUnsigned" */
+568,	/* "setct-PIDualSignedTBE" */
+569,	/* "setct-PIUnsignedTBE" */
+531,	/* "setct-PInitResData" */
+533,	/* "setct-PResData" */
+594,	/* "setct-RegFormReqTBE" */
+562,	/* "setct-RegFormResTBS" */
+604,	/* "setext-pinAny" */
+603,	/* "setext-pinSecure" */
+605,	/* "setext-track2" */
+41,	/* "sha" */
+64,	/* "sha1" */
+115,	/* "sha1WithRSA" */
+65,	/* "sha1WithRSAEncryption" */
+675,	/* "sha224" */
+671,	/* "sha224WithRSAEncryption" */
+672,	/* "sha256" */
+668,	/* "sha256WithRSAEncryption" */
+673,	/* "sha384" */
+669,	/* "sha384WithRSAEncryption" */
+674,	/* "sha512" */
+670,	/* "sha512WithRSAEncryption" */
+42,	/* "shaWithRSAEncryption" */
+52,	/* "signingTime" */
+454,	/* "simpleSecurityObject" */
+496,	/* "singleLevelQuality" */
+16,	/* "stateOrProvinceName" */
+660,	/* "streetAddress" */
+498,	/* "subtreeMaximumQuality" */
+497,	/* "subtreeMinimumQuality" */
+890,	/* "supportedAlgorithms" */
+874,	/* "supportedApplicationContext" */
+100,	/* "surname" */
+864,	/* "telephoneNumber" */
+866,	/* "teletexTerminalIdentifier" */
+865,	/* "telexNumber" */
+459,	/* "textEncodedORAddress" */
+293,	/* "textNotice" */
+106,	/* "title" */
+682,	/* "tpBasis" */
+436,	/* "ucl" */
+ 0,	/* "undefined" */
+888,	/* "uniqueMember" */
+55,	/* "unstructuredAddress" */
+49,	/* "unstructuredName" */
+880,	/* "userCertificate" */
+465,	/* "userClass" */
+458,	/* "userId" */
+879,	/* "userPassword" */
+373,	/* "valid" */
+678,	/* "wap" */
+679,	/* "wap-wsg" */
+735,	/* "wap-wsg-idm-ecid-wtls1" */
+743,	/* "wap-wsg-idm-ecid-wtls10" */
+744,	/* "wap-wsg-idm-ecid-wtls11" */
+745,	/* "wap-wsg-idm-ecid-wtls12" */
+736,	/* "wap-wsg-idm-ecid-wtls3" */
+737,	/* "wap-wsg-idm-ecid-wtls4" */
+738,	/* "wap-wsg-idm-ecid-wtls5" */
+739,	/* "wap-wsg-idm-ecid-wtls6" */
+740,	/* "wap-wsg-idm-ecid-wtls7" */
+741,	/* "wap-wsg-idm-ecid-wtls8" */
+742,	/* "wap-wsg-idm-ecid-wtls9" */
+804,	/* "whirlpool" */
+868,	/* "x121Address" */
+503,	/* "x500UniqueIdentifier" */
+158,	/* "x509Certificate" */
+160,	/* "x509Crl" */
+125,	/* "zlib compression" */
+};
+
+static const unsigned int kNIDsInOIDOrder[NUM_OBJ]={
+ 0,	/* OBJ_undef                        0 */
+393,	/* OBJ_joint_iso_ccitt              OBJ_joint_iso_itu_t */
+404,	/* OBJ_ccitt                        OBJ_itu_t */
+645,	/* OBJ_itu_t                        0 */
+434,	/* OBJ_data                         0 9 */
+181,	/* OBJ_iso                          1 */
+182,	/* OBJ_member_body                  1 2 */
+379,	/* OBJ_org                          1 3 */
+676,	/* OBJ_identified_organization      1 3 */
+646,	/* OBJ_joint_iso_itu_t              2 */
+11,	/* OBJ_X500                         2 5 */
+647,	/* OBJ_international_organizations  2 23 */
+380,	/* OBJ_dod                          1 3 6 */
+12,	/* OBJ_X509                         2 5 4 */
+378,	/* OBJ_X500algorithms               2 5 8 */
+81,	/* OBJ_id_ce                        2 5 29 */
+512,	/* OBJ_id_set                       2 23 42 */
+678,	/* OBJ_wap                          2 23 43 */
+435,	/* OBJ_pss                          0 9 2342 */
+183,	/* OBJ_ISO_US                       1 2 840 */
+381,	/* OBJ_iana                         1 3 6 1 */
+677,	/* OBJ_certicom_arc                 1 3 132 */
+394,	/* OBJ_selected_attribute_types     2 5 1 5 */
+13,	/* OBJ_commonName                   2 5 4 3 */
+100,	/* OBJ_surname                      2 5 4 4 */
+105,	/* OBJ_serialNumber                 2 5 4 5 */
+14,	/* OBJ_countryName                  2 5 4 6 */
+15,	/* OBJ_localityName                 2 5 4 7 */
+16,	/* OBJ_stateOrProvinceName          2 5 4 8 */
+660,	/* OBJ_streetAddress                2 5 4 9 */
+17,	/* OBJ_organizationName             2 5 4 10 */
+18,	/* OBJ_organizationalUnitName       2 5 4 11 */
+106,	/* OBJ_title                        2 5 4 12 */
+107,	/* OBJ_description                  2 5 4 13 */
+859,	/* OBJ_searchGuide                  2 5 4 14 */
+860,	/* OBJ_businessCategory             2 5 4 15 */
+861,	/* OBJ_postalAddress                2 5 4 16 */
+661,	/* OBJ_postalCode                   2 5 4 17 */
+862,	/* OBJ_postOfficeBox                2 5 4 18 */
+863,	/* OBJ_physicalDeliveryOfficeName   2 5 4 19 */
+864,	/* OBJ_telephoneNumber              2 5 4 20 */
+865,	/* OBJ_telexNumber                  2 5 4 21 */
+866,	/* OBJ_teletexTerminalIdentifier    2 5 4 22 */
+867,	/* OBJ_facsimileTelephoneNumber     2 5 4 23 */
+868,	/* OBJ_x121Address                  2 5 4 24 */
+869,	/* OBJ_internationaliSDNNumber      2 5 4 25 */
+870,	/* OBJ_registeredAddress            2 5 4 26 */
+871,	/* OBJ_destinationIndicator         2 5 4 27 */
+872,	/* OBJ_preferredDeliveryMethod      2 5 4 28 */
+873,	/* OBJ_presentationAddress          2 5 4 29 */
+874,	/* OBJ_supportedApplicationContext  2 5 4 30 */
+875,	/* OBJ_member                       2 5 4 31 */
+876,	/* OBJ_owner                        2 5 4 32 */
+877,	/* OBJ_roleOccupant                 2 5 4 33 */
+878,	/* OBJ_seeAlso                      2 5 4 34 */
+879,	/* OBJ_userPassword                 2 5 4 35 */
+880,	/* OBJ_userCertificate              2 5 4 36 */
+881,	/* OBJ_cACertificate                2 5 4 37 */
+882,	/* OBJ_authorityRevocationList      2 5 4 38 */
+883,	/* OBJ_certificateRevocationList    2 5 4 39 */
+884,	/* OBJ_crossCertificatePair         2 5 4 40 */
+173,	/* OBJ_name                         2 5 4 41 */
+99,	/* OBJ_givenName                    2 5 4 42 */
+101,	/* OBJ_initials                     2 5 4 43 */
+509,	/* OBJ_generationQualifier          2 5 4 44 */
+503,	/* OBJ_x500UniqueIdentifier         2 5 4 45 */
+174,	/* OBJ_dnQualifier                  2 5 4 46 */
+885,	/* OBJ_enhancedSearchGuide          2 5 4 47 */
+886,	/* OBJ_protocolInformation          2 5 4 48 */
+887,	/* OBJ_distinguishedName            2 5 4 49 */
+888,	/* OBJ_uniqueMember                 2 5 4 50 */
+889,	/* OBJ_houseIdentifier              2 5 4 51 */
+890,	/* OBJ_supportedAlgorithms          2 5 4 52 */
+891,	/* OBJ_deltaRevocationList          2 5 4 53 */
+892,	/* OBJ_dmdName                      2 5 4 54 */
+510,	/* OBJ_pseudonym                    2 5 4 65 */
+400,	/* OBJ_role                         2 5 4 72 */
+769,	/* OBJ_subject_directory_attributes 2 5 29 9 */
+82,	/* OBJ_subject_key_identifier       2 5 29 14 */
+83,	/* OBJ_key_usage                    2 5 29 15 */
+84,	/* OBJ_private_key_usage_period     2 5 29 16 */
+85,	/* OBJ_subject_alt_name             2 5 29 17 */
+86,	/* OBJ_issuer_alt_name              2 5 29 18 */
+87,	/* OBJ_basic_constraints            2 5 29 19 */
+88,	/* OBJ_crl_number                   2 5 29 20 */
+141,	/* OBJ_crl_reason                   2 5 29 21 */
+430,	/* OBJ_hold_instruction_code        2 5 29 23 */
+142,	/* OBJ_invalidity_date              2 5 29 24 */
+140,	/* OBJ_delta_crl                    2 5 29 27 */
+770,	/* OBJ_issuing_distribution_point   2 5 29 28 */
+771,	/* OBJ_certificate_issuer           2 5 29 29 */
+666,	/* OBJ_name_constraints             2 5 29 30 */
+103,	/* OBJ_crl_distribution_points      2 5 29 31 */
+89,	/* OBJ_certificate_policies         2 5 29 32 */
+747,	/* OBJ_policy_mappings              2 5 29 33 */
+90,	/* OBJ_authority_key_identifier     2 5 29 35 */
+401,	/* OBJ_policy_constraints           2 5 29 36 */
+126,	/* OBJ_ext_key_usage                2 5 29 37 */
+857,	/* OBJ_freshest_crl                 2 5 29 46 */
+748,	/* OBJ_inhibit_any_policy           2 5 29 54 */
+402,	/* OBJ_target_information           2 5 29 55 */
+403,	/* OBJ_no_rev_avail                 2 5 29 56 */
+513,	/* OBJ_set_ctype                    2 23 42 0 */
+514,	/* OBJ_set_msgExt                   2 23 42 1 */
+515,	/* OBJ_set_attr                     2 23 42 3 */
+516,	/* OBJ_set_policy                   2 23 42 5 */
+517,	/* OBJ_set_certExt                  2 23 42 7 */
+518,	/* OBJ_set_brand                    2 23 42 8 */
+679,	/* OBJ_wap_wsg                      2 23 43 1 */
+382,	/* OBJ_Directory                    1 3 6 1 1 */
+383,	/* OBJ_Management                   1 3 6 1 2 */
+384,	/* OBJ_Experimental                 1 3 6 1 3 */
+385,	/* OBJ_Private                      1 3 6 1 4 */
+386,	/* OBJ_Security                     1 3 6 1 5 */
+387,	/* OBJ_SNMPv2                       1 3 6 1 6 */
+388,	/* OBJ_Mail                         1 3 6 1 7 */
+376,	/* OBJ_algorithm                    1 3 14 3 2 */
+395,	/* OBJ_clearance                    2 5 1 5 55 */
+19,	/* OBJ_rsa                          2 5 8 1 1 */
+96,	/* OBJ_mdc2WithRSA                  2 5 8 3 100 */
+95,	/* OBJ_mdc2                         2 5 8 3 101 */
+746,	/* OBJ_any_policy                   2 5 29 32 0 */
+910,	/* OBJ_anyExtendedKeyUsage          2 5 29 37 0 */
+519,	/* OBJ_setct_PANData                2 23 42 0 0 */
+520,	/* OBJ_setct_PANToken               2 23 42 0 1 */
+521,	/* OBJ_setct_PANOnly                2 23 42 0 2 */
+522,	/* OBJ_setct_OIData                 2 23 42 0 3 */
+523,	/* OBJ_setct_PI                     2 23 42 0 4 */
+524,	/* OBJ_setct_PIData                 2 23 42 0 5 */
+525,	/* OBJ_setct_PIDataUnsigned         2 23 42 0 6 */
+526,	/* OBJ_setct_HODInput               2 23 42 0 7 */
+527,	/* OBJ_setct_AuthResBaggage         2 23 42 0 8 */
+528,	/* OBJ_setct_AuthRevReqBaggage      2 23 42 0 9 */
+529,	/* OBJ_setct_AuthRevResBaggage      2 23 42 0 10 */
+530,	/* OBJ_setct_CapTokenSeq            2 23 42 0 11 */
+531,	/* OBJ_setct_PInitResData           2 23 42 0 12 */
+532,	/* OBJ_setct_PI_TBS                 2 23 42 0 13 */
+533,	/* OBJ_setct_PResData               2 23 42 0 14 */
+534,	/* OBJ_setct_AuthReqTBS             2 23 42 0 16 */
+535,	/* OBJ_setct_AuthResTBS             2 23 42 0 17 */
+536,	/* OBJ_setct_AuthResTBSX            2 23 42 0 18 */
+537,	/* OBJ_setct_AuthTokenTBS           2 23 42 0 19 */
+538,	/* OBJ_setct_CapTokenData           2 23 42 0 20 */
+539,	/* OBJ_setct_CapTokenTBS            2 23 42 0 21 */
+540,	/* OBJ_setct_AcqCardCodeMsg         2 23 42 0 22 */
+541,	/* OBJ_setct_AuthRevReqTBS          2 23 42 0 23 */
+542,	/* OBJ_setct_AuthRevResData         2 23 42 0 24 */
+543,	/* OBJ_setct_AuthRevResTBS          2 23 42 0 25 */
+544,	/* OBJ_setct_CapReqTBS              2 23 42 0 26 */
+545,	/* OBJ_setct_CapReqTBSX             2 23 42 0 27 */
+546,	/* OBJ_setct_CapResData             2 23 42 0 28 */
+547,	/* OBJ_setct_CapRevReqTBS           2 23 42 0 29 */
+548,	/* OBJ_setct_CapRevReqTBSX          2 23 42 0 30 */
+549,	/* OBJ_setct_CapRevResData          2 23 42 0 31 */
+550,	/* OBJ_setct_CredReqTBS             2 23 42 0 32 */
+551,	/* OBJ_setct_CredReqTBSX            2 23 42 0 33 */
+552,	/* OBJ_setct_CredResData            2 23 42 0 34 */
+553,	/* OBJ_setct_CredRevReqTBS          2 23 42 0 35 */
+554,	/* OBJ_setct_CredRevReqTBSX         2 23 42 0 36 */
+555,	/* OBJ_setct_CredRevResData         2 23 42 0 37 */
+556,	/* OBJ_setct_PCertReqData           2 23 42 0 38 */
+557,	/* OBJ_setct_PCertResTBS            2 23 42 0 39 */
+558,	/* OBJ_setct_BatchAdminReqData      2 23 42 0 40 */
+559,	/* OBJ_setct_BatchAdminResData      2 23 42 0 41 */
+560,	/* OBJ_setct_CardCInitResTBS        2 23 42 0 42 */
+561,	/* OBJ_setct_MeAqCInitResTBS        2 23 42 0 43 */
+562,	/* OBJ_setct_RegFormResTBS          2 23 42 0 44 */
+563,	/* OBJ_setct_CertReqData            2 23 42 0 45 */
+564,	/* OBJ_setct_CertReqTBS             2 23 42 0 46 */
+565,	/* OBJ_setct_CertResData            2 23 42 0 47 */
+566,	/* OBJ_setct_CertInqReqTBS          2 23 42 0 48 */
+567,	/* OBJ_setct_ErrorTBS               2 23 42 0 49 */
+568,	/* OBJ_setct_PIDualSignedTBE        2 23 42 0 50 */
+569,	/* OBJ_setct_PIUnsignedTBE          2 23 42 0 51 */
+570,	/* OBJ_setct_AuthReqTBE             2 23 42 0 52 */
+571,	/* OBJ_setct_AuthResTBE             2 23 42 0 53 */
+572,	/* OBJ_setct_AuthResTBEX            2 23 42 0 54 */
+573,	/* OBJ_setct_AuthTokenTBE           2 23 42 0 55 */
+574,	/* OBJ_setct_CapTokenTBE            2 23 42 0 56 */
+575,	/* OBJ_setct_CapTokenTBEX           2 23 42 0 57 */
+576,	/* OBJ_setct_AcqCardCodeMsgTBE      2 23 42 0 58 */
+577,	/* OBJ_setct_AuthRevReqTBE          2 23 42 0 59 */
+578,	/* OBJ_setct_AuthRevResTBE          2 23 42 0 60 */
+579,	/* OBJ_setct_AuthRevResTBEB         2 23 42 0 61 */
+580,	/* OBJ_setct_CapReqTBE              2 23 42 0 62 */
+581,	/* OBJ_setct_CapReqTBEX             2 23 42 0 63 */
+582,	/* OBJ_setct_CapResTBE              2 23 42 0 64 */
+583,	/* OBJ_setct_CapRevReqTBE           2 23 42 0 65 */
+584,	/* OBJ_setct_CapRevReqTBEX          2 23 42 0 66 */
+585,	/* OBJ_setct_CapRevResTBE           2 23 42 0 67 */
+586,	/* OBJ_setct_CredReqTBE             2 23 42 0 68 */
+587,	/* OBJ_setct_CredReqTBEX            2 23 42 0 69 */
+588,	/* OBJ_setct_CredResTBE             2 23 42 0 70 */
+589,	/* OBJ_setct_CredRevReqTBE          2 23 42 0 71 */
+590,	/* OBJ_setct_CredRevReqTBEX         2 23 42 0 72 */
+591,	/* OBJ_setct_CredRevResTBE          2 23 42 0 73 */
+592,	/* OBJ_setct_BatchAdminReqTBE       2 23 42 0 74 */
+593,	/* OBJ_setct_BatchAdminResTBE       2 23 42 0 75 */
+594,	/* OBJ_setct_RegFormReqTBE          2 23 42 0 76 */
+595,	/* OBJ_setct_CertReqTBE             2 23 42 0 77 */
+596,	/* OBJ_setct_CertReqTBEX            2 23 42 0 78 */
+597,	/* OBJ_setct_CertResTBE             2 23 42 0 79 */
+598,	/* OBJ_setct_CRLNotificationTBS     2 23 42 0 80 */
+599,	/* OBJ_setct_CRLNotificationResTBS  2 23 42 0 81 */
+600,	/* OBJ_setct_BCIDistributionTBS     2 23 42 0 82 */
+601,	/* OBJ_setext_genCrypt              2 23 42 1 1 */
+602,	/* OBJ_setext_miAuth                2 23 42 1 3 */
+603,	/* OBJ_setext_pinSecure             2 23 42 1 4 */
+604,	/* OBJ_setext_pinAny                2 23 42 1 5 */
+605,	/* OBJ_setext_track2                2 23 42 1 7 */
+606,	/* OBJ_setext_cv                    2 23 42 1 8 */
+620,	/* OBJ_setAttr_Cert                 2 23 42 3 0 */
+621,	/* OBJ_setAttr_PGWYcap              2 23 42 3 1 */
+622,	/* OBJ_setAttr_TokenType            2 23 42 3 2 */
+623,	/* OBJ_setAttr_IssCap               2 23 42 3 3 */
+607,	/* OBJ_set_policy_root              2 23 42 5 0 */
+608,	/* OBJ_setCext_hashedRoot           2 23 42 7 0 */
+609,	/* OBJ_setCext_certType             2 23 42 7 1 */
+610,	/* OBJ_setCext_merchData            2 23 42 7 2 */
+611,	/* OBJ_setCext_cCertRequired        2 23 42 7 3 */
+612,	/* OBJ_setCext_tunneling            2 23 42 7 4 */
+613,	/* OBJ_setCext_setExt               2 23 42 7 5 */
+614,	/* OBJ_setCext_setQualf             2 23 42 7 6 */
+615,	/* OBJ_setCext_PGWYcapabilities     2 23 42 7 7 */
+616,	/* OBJ_setCext_TokenIdentifier      2 23 42 7 8 */
+617,	/* OBJ_setCext_Track2Data           2 23 42 7 9 */
+618,	/* OBJ_setCext_TokenType            2 23 42 7 10 */
+619,	/* OBJ_setCext_IssuerCapabilities   2 23 42 7 11 */
+636,	/* OBJ_set_brand_IATA_ATA           2 23 42 8 1 */
+640,	/* OBJ_set_brand_Visa               2 23 42 8 4 */
+641,	/* OBJ_set_brand_MasterCard         2 23 42 8 5 */
+637,	/* OBJ_set_brand_Diners             2 23 42 8 30 */
+638,	/* OBJ_set_brand_AmericanExpress    2 23 42 8 34 */
+639,	/* OBJ_set_brand_JCB                2 23 42 8 35 */
+805,	/* OBJ_cryptopro                    1 2 643 2 2 */
+806,	/* OBJ_cryptocom                    1 2 643 2 9 */
+184,	/* OBJ_X9_57                        1 2 840 10040 */
+405,	/* OBJ_ansi_X9_62                   1 2 840 10045 */
+389,	/* OBJ_Enterprises                  1 3 6 1 4 1 */
+504,	/* OBJ_mime_mhs                     1 3 6 1 7 1 */
+104,	/* OBJ_md5WithRSA                   1 3 14 3 2 3 */
+29,	/* OBJ_des_ecb                      1 3 14 3 2 6 */
+31,	/* OBJ_des_cbc                      1 3 14 3 2 7 */
+45,	/* OBJ_des_ofb64                    1 3 14 3 2 8 */
+30,	/* OBJ_des_cfb64                    1 3 14 3 2 9 */
+377,	/* OBJ_rsaSignature                 1 3 14 3 2 11 */
+67,	/* OBJ_dsa_2                        1 3 14 3 2 12 */
+66,	/* OBJ_dsaWithSHA                   1 3 14 3 2 13 */
+42,	/* OBJ_shaWithRSAEncryption         1 3 14 3 2 15 */
+32,	/* OBJ_des_ede_ecb                  1 3 14 3 2 17 */
+41,	/* OBJ_sha                          1 3 14 3 2 18 */
+64,	/* OBJ_sha1                         1 3 14 3 2 26 */
+70,	/* OBJ_dsaWithSHA1_2                1 3 14 3 2 27 */
+115,	/* OBJ_sha1WithRSA                  1 3 14 3 2 29 */
+117,	/* OBJ_ripemd160                    1 3 36 3 2 1 */
+143,	/* OBJ_sxnet                        1 3 101 1 4 1 */
+721,	/* OBJ_sect163k1                    1 3 132 0 1 */
+722,	/* OBJ_sect163r1                    1 3 132 0 2 */
+728,	/* OBJ_sect239k1                    1 3 132 0 3 */
+717,	/* OBJ_sect113r1                    1 3 132 0 4 */
+718,	/* OBJ_sect113r2                    1 3 132 0 5 */
+704,	/* OBJ_secp112r1                    1 3 132 0 6 */
+705,	/* OBJ_secp112r2                    1 3 132 0 7 */
+709,	/* OBJ_secp160r1                    1 3 132 0 8 */
+708,	/* OBJ_secp160k1                    1 3 132 0 9 */
+714,	/* OBJ_secp256k1                    1 3 132 0 10 */
+723,	/* OBJ_sect163r2                    1 3 132 0 15 */
+729,	/* OBJ_sect283k1                    1 3 132 0 16 */
+730,	/* OBJ_sect283r1                    1 3 132 0 17 */
+719,	/* OBJ_sect131r1                    1 3 132 0 22 */
+720,	/* OBJ_sect131r2                    1 3 132 0 23 */
+724,	/* OBJ_sect193r1                    1 3 132 0 24 */
+725,	/* OBJ_sect193r2                    1 3 132 0 25 */
+726,	/* OBJ_sect233k1                    1 3 132 0 26 */
+727,	/* OBJ_sect233r1                    1 3 132 0 27 */
+706,	/* OBJ_secp128r1                    1 3 132 0 28 */
+707,	/* OBJ_secp128r2                    1 3 132 0 29 */
+710,	/* OBJ_secp160r2                    1 3 132 0 30 */
+711,	/* OBJ_secp192k1                    1 3 132 0 31 */
+712,	/* OBJ_secp224k1                    1 3 132 0 32 */
+713,	/* OBJ_secp224r1                    1 3 132 0 33 */
+715,	/* OBJ_secp384r1                    1 3 132 0 34 */
+716,	/* OBJ_secp521r1                    1 3 132 0 35 */
+731,	/* OBJ_sect409k1                    1 3 132 0 36 */
+732,	/* OBJ_sect409r1                    1 3 132 0 37 */
+733,	/* OBJ_sect571k1                    1 3 132 0 38 */
+734,	/* OBJ_sect571r1                    1 3 132 0 39 */
+624,	/* OBJ_set_rootKeyThumb             2 23 42 3 0 0 */
+625,	/* OBJ_set_addPolicy                2 23 42 3 0 1 */
+626,	/* OBJ_setAttr_Token_EMV            2 23 42 3 2 1 */
+627,	/* OBJ_setAttr_Token_B0Prime        2 23 42 3 2 2 */
+628,	/* OBJ_setAttr_IssCap_CVM           2 23 42 3 3 3 */
+629,	/* OBJ_setAttr_IssCap_T2            2 23 42 3 3 4 */
+630,	/* OBJ_setAttr_IssCap_Sig           2 23 42 3 3 5 */
+642,	/* OBJ_set_brand_Novus              2 23 42 8 6011 */
+735,	/* OBJ_wap_wsg_idm_ecid_wtls1       2 23 43 1 4 1 */
+736,	/* OBJ_wap_wsg_idm_ecid_wtls3       2 23 43 1 4 3 */
+737,	/* OBJ_wap_wsg_idm_ecid_wtls4       2 23 43 1 4 4 */
+738,	/* OBJ_wap_wsg_idm_ecid_wtls5       2 23 43 1 4 5 */
+739,	/* OBJ_wap_wsg_idm_ecid_wtls6       2 23 43 1 4 6 */
+740,	/* OBJ_wap_wsg_idm_ecid_wtls7       2 23 43 1 4 7 */
+741,	/* OBJ_wap_wsg_idm_ecid_wtls8       2 23 43 1 4 8 */
+742,	/* OBJ_wap_wsg_idm_ecid_wtls9       2 23 43 1 4 9 */
+743,	/* OBJ_wap_wsg_idm_ecid_wtls10      2 23 43 1 4 10 */
+744,	/* OBJ_wap_wsg_idm_ecid_wtls11      2 23 43 1 4 11 */
+745,	/* OBJ_wap_wsg_idm_ecid_wtls12      2 23 43 1 4 12 */
+804,	/* OBJ_whirlpool                    1 0 10118 3 0 55 */
+124,	/* OBJ_rle_compression              1 1 1 1 666 1 */
+773,	/* OBJ_kisa                         1 2 410 200004 */
+807,	/* OBJ_id_GostR3411_94_with_GostR3410_2001 1 2 643 2 2 3 */
+808,	/* OBJ_id_GostR3411_94_with_GostR3410_94 1 2 643 2 2 4 */
+809,	/* OBJ_id_GostR3411_94              1 2 643 2 2 9 */
+810,	/* OBJ_id_HMACGostR3411_94          1 2 643 2 2 10 */
+811,	/* OBJ_id_GostR3410_2001            1 2 643 2 2 19 */
+812,	/* OBJ_id_GostR3410_94              1 2 643 2 2 20 */
+813,	/* OBJ_id_Gost28147_89              1 2 643 2 2 21 */
+815,	/* OBJ_id_Gost28147_89_MAC          1 2 643 2 2 22 */
+816,	/* OBJ_id_GostR3411_94_prf          1 2 643 2 2 23 */
+817,	/* OBJ_id_GostR3410_2001DH          1 2 643 2 2 98 */
+818,	/* OBJ_id_GostR3410_94DH            1 2 643 2 2 99 */
+ 1,	/* OBJ_rsadsi                       1 2 840 113549 */
+185,	/* OBJ_X9cm                         1 2 840 10040 4 */
+127,	/* OBJ_id_pkix                      1 3 6 1 5 5 7 */
+505,	/* OBJ_mime_mhs_headings            1 3 6 1 7 1 1 */
+506,	/* OBJ_mime_mhs_bodies              1 3 6 1 7 1 2 */
+119,	/* OBJ_ripemd160WithRSA             1 3 36 3 3 1 2 */
+940,	/* OBJ_dhSinglePass_stdDH_sha224kdf_scheme 1 3 132 1 11 0 */
+941,	/* OBJ_dhSinglePass_stdDH_sha256kdf_scheme 1 3 132 1 11 1 */
+942,	/* OBJ_dhSinglePass_stdDH_sha384kdf_scheme 1 3 132 1 11 2 */
+943,	/* OBJ_dhSinglePass_stdDH_sha512kdf_scheme 1 3 132 1 11 3 */
+945,	/* OBJ_dhSinglePass_cofactorDH_sha224kdf_scheme 1 3 132 1 14 0 */
+946,	/* OBJ_dhSinglePass_cofactorDH_sha256kdf_scheme 1 3 132 1 14 1 */
+947,	/* OBJ_dhSinglePass_cofactorDH_sha384kdf_scheme 1 3 132 1 14 2 */
+948,	/* OBJ_dhSinglePass_cofactorDH_sha512kdf_scheme 1 3 132 1 14 3 */
+631,	/* OBJ_setAttr_GenCryptgrm          2 23 42 3 3 3 1 */
+632,	/* OBJ_setAttr_T2Enc                2 23 42 3 3 4 1 */
+633,	/* OBJ_setAttr_T2cleartxt           2 23 42 3 3 4 2 */
+634,	/* OBJ_setAttr_TokICCsig            2 23 42 3 3 5 1 */
+635,	/* OBJ_setAttr_SecDevSig            2 23 42 3 3 5 2 */
+436,	/* OBJ_ucl                          0 9 2342 19200300 */
+820,	/* OBJ_id_Gost28147_89_None_KeyMeshing 1 2 643 2 2 14 0 */
+819,	/* OBJ_id_Gost28147_89_CryptoPro_KeyMeshing 1 2 643 2 2 14 1 */
+845,	/* OBJ_id_GostR3410_94_a            1 2 643 2 2 20 1 */
+846,	/* OBJ_id_GostR3410_94_aBis         1 2 643 2 2 20 2 */
+847,	/* OBJ_id_GostR3410_94_b            1 2 643 2 2 20 3 */
+848,	/* OBJ_id_GostR3410_94_bBis         1 2 643 2 2 20 4 */
+821,	/* OBJ_id_GostR3411_94_TestParamSet 1 2 643 2 2 30 0 */
+822,	/* OBJ_id_GostR3411_94_CryptoProParamSet 1 2 643 2 2 30 1 */
+823,	/* OBJ_id_Gost28147_89_TestParamSet 1 2 643 2 2 31 0 */
+824,	/* OBJ_id_Gost28147_89_CryptoPro_A_ParamSet 1 2 643 2 2 31 1 */
+825,	/* OBJ_id_Gost28147_89_CryptoPro_B_ParamSet 1 2 643 2 2 31 2 */
+826,	/* OBJ_id_Gost28147_89_CryptoPro_C_ParamSet 1 2 643 2 2 31 3 */
+827,	/* OBJ_id_Gost28147_89_CryptoPro_D_ParamSet 1 2 643 2 2 31 4 */
+828,	/* OBJ_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet 1 2 643 2 2 31 5 */
+829,	/* OBJ_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet 1 2 643 2 2 31 6 */
+830,	/* OBJ_id_Gost28147_89_CryptoPro_RIC_1_ParamSet 1 2 643 2 2 31 7 */
+831,	/* OBJ_id_GostR3410_94_TestParamSet 1 2 643 2 2 32 0 */
+832,	/* OBJ_id_GostR3410_94_CryptoPro_A_ParamSet 1 2 643 2 2 32 2 */
+833,	/* OBJ_id_GostR3410_94_CryptoPro_B_ParamSet 1 2 643 2 2 32 3 */
+834,	/* OBJ_id_GostR3410_94_CryptoPro_C_ParamSet 1 2 643 2 2 32 4 */
+835,	/* OBJ_id_GostR3410_94_CryptoPro_D_ParamSet 1 2 643 2 2 32 5 */
+836,	/* OBJ_id_GostR3410_94_CryptoPro_XchA_ParamSet 1 2 643 2 2 33 1 */
+837,	/* OBJ_id_GostR3410_94_CryptoPro_XchB_ParamSet 1 2 643 2 2 33 2 */
+838,	/* OBJ_id_GostR3410_94_CryptoPro_XchC_ParamSet 1 2 643 2 2 33 3 */
+839,	/* OBJ_id_GostR3410_2001_TestParamSet 1 2 643 2 2 35 0 */
+840,	/* OBJ_id_GostR3410_2001_CryptoPro_A_ParamSet 1 2 643 2 2 35 1 */
+841,	/* OBJ_id_GostR3410_2001_CryptoPro_B_ParamSet 1 2 643 2 2 35 2 */
+842,	/* OBJ_id_GostR3410_2001_CryptoPro_C_ParamSet 1 2 643 2 2 35 3 */
+843,	/* OBJ_id_GostR3410_2001_CryptoPro_XchA_ParamSet 1 2 643 2 2 36 0 */
+844,	/* OBJ_id_GostR3410_2001_CryptoPro_XchB_ParamSet 1 2 643 2 2 36 1 */
+ 2,	/* OBJ_pkcs                         1 2 840 113549 1 */
+431,	/* OBJ_hold_instruction_none        1 2 840 10040 2 1 */
+432,	/* OBJ_hold_instruction_call_issuer 1 2 840 10040 2 2 */
+433,	/* OBJ_hold_instruction_reject      1 2 840 10040 2 3 */
+116,	/* OBJ_dsa                          1 2 840 10040 4 1 */
+113,	/* OBJ_dsaWithSHA1                  1 2 840 10040 4 3 */
+406,	/* OBJ_X9_62_prime_field            1 2 840 10045 1 1 */
+407,	/* OBJ_X9_62_characteristic_two_field 1 2 840 10045 1 2 */
+408,	/* OBJ_X9_62_id_ecPublicKey         1 2 840 10045 2 1 */
+416,	/* OBJ_ecdsa_with_SHA1              1 2 840 10045 4 1 */
+791,	/* OBJ_ecdsa_with_Recommended       1 2 840 10045 4 2 */
+792,	/* OBJ_ecdsa_with_Specified         1 2 840 10045 4 3 */
+920,	/* OBJ_dhpublicnumber               1 2 840 10046 2 1 */
+258,	/* OBJ_id_pkix_mod                  1 3 6 1 5 5 7 0 */
+175,	/* OBJ_id_pe                        1 3 6 1 5 5 7 1 */
+259,	/* OBJ_id_qt                        1 3 6 1 5 5 7 2 */
+128,	/* OBJ_id_kp                        1 3 6 1 5 5 7 3 */
+260,	/* OBJ_id_it                        1 3 6 1 5 5 7 4 */
+261,	/* OBJ_id_pkip                      1 3 6 1 5 5 7 5 */
+262,	/* OBJ_id_alg                       1 3 6 1 5 5 7 6 */
+263,	/* OBJ_id_cmc                       1 3 6 1 5 5 7 7 */
+264,	/* OBJ_id_on                        1 3 6 1 5 5 7 8 */
+265,	/* OBJ_id_pda                       1 3 6 1 5 5 7 9 */
+266,	/* OBJ_id_aca                       1 3 6 1 5 5 7 10 */
+267,	/* OBJ_id_qcs                       1 3 6 1 5 5 7 11 */
+268,	/* OBJ_id_cct                       1 3 6 1 5 5 7 12 */
+662,	/* OBJ_id_ppl                       1 3 6 1 5 5 7 21 */
+176,	/* OBJ_id_ad                        1 3 6 1 5 5 7 48 */
+507,	/* OBJ_id_hex_partial_message       1 3 6 1 7 1 1 1 */
+508,	/* OBJ_id_hex_multipart_message     1 3 6 1 7 1 1 2 */
+57,	/* OBJ_netscape                     2 16 840 1 113730 */
+754,	/* OBJ_camellia_128_ecb             0 3 4401 5 3 1 9 1 */
+766,	/* OBJ_camellia_128_ofb128          0 3 4401 5 3 1 9 3 */
+757,	/* OBJ_camellia_128_cfb128          0 3 4401 5 3 1 9 4 */
+755,	/* OBJ_camellia_192_ecb             0 3 4401 5 3 1 9 21 */
+767,	/* OBJ_camellia_192_ofb128          0 3 4401 5 3 1 9 23 */
+758,	/* OBJ_camellia_192_cfb128          0 3 4401 5 3 1 9 24 */
+756,	/* OBJ_camellia_256_ecb             0 3 4401 5 3 1 9 41 */
+768,	/* OBJ_camellia_256_ofb128          0 3 4401 5 3 1 9 43 */
+759,	/* OBJ_camellia_256_cfb128          0 3 4401 5 3 1 9 44 */
+437,	/* OBJ_pilot                        0 9 2342 19200300 100 */
+776,	/* OBJ_seed_ecb                     1 2 410 200004 1 3 */
+777,	/* OBJ_seed_cbc                     1 2 410 200004 1 4 */
+779,	/* OBJ_seed_cfb128                  1 2 410 200004 1 5 */
+778,	/* OBJ_seed_ofb128                  1 2 410 200004 1 6 */
+852,	/* OBJ_id_GostR3411_94_with_GostR3410_94_cc 1 2 643 2 9 1 3 3 */
+853,	/* OBJ_id_GostR3411_94_with_GostR3410_2001_cc 1 2 643 2 9 1 3 4 */
+850,	/* OBJ_id_GostR3410_94_cc           1 2 643 2 9 1 5 3 */
+851,	/* OBJ_id_GostR3410_2001_cc         1 2 643 2 9 1 5 4 */
+849,	/* OBJ_id_Gost28147_89_cc           1 2 643 2 9 1 6 1 */
+854,	/* OBJ_id_GostR3410_2001_ParamSet_cc 1 2 643 2 9 1 8 1 */
+186,	/* OBJ_pkcs1                        1 2 840 113549 1 1 */
+27,	/* OBJ_pkcs3                        1 2 840 113549 1 3 */
+187,	/* OBJ_pkcs5                        1 2 840 113549 1 5 */
+20,	/* OBJ_pkcs7                        1 2 840 113549 1 7 */
+47,	/* OBJ_pkcs9                        1 2 840 113549 1 9 */
+ 3,	/* OBJ_md2                          1 2 840 113549 2 2 */
+257,	/* OBJ_md4                          1 2 840 113549 2 4 */
+ 4,	/* OBJ_md5                          1 2 840 113549 2 5 */
+797,	/* OBJ_hmacWithMD5                  1 2 840 113549 2 6 */
+163,	/* OBJ_hmacWithSHA1                 1 2 840 113549 2 7 */
+798,	/* OBJ_hmacWithSHA224               1 2 840 113549 2 8 */
+799,	/* OBJ_hmacWithSHA256               1 2 840 113549 2 9 */
+800,	/* OBJ_hmacWithSHA384               1 2 840 113549 2 10 */
+801,	/* OBJ_hmacWithSHA512               1 2 840 113549 2 11 */
+37,	/* OBJ_rc2_cbc                      1 2 840 113549 3 2 */
+ 5,	/* OBJ_rc4                          1 2 840 113549 3 4 */
+44,	/* OBJ_des_ede3_cbc                 1 2 840 113549 3 7 */
+120,	/* OBJ_rc5_cbc                      1 2 840 113549 3 8 */
+643,	/* OBJ_des_cdmf                     1 2 840 113549 3 10 */
+680,	/* OBJ_X9_62_id_characteristic_two_basis 1 2 840 10045 1 2 3 */
+684,	/* OBJ_X9_62_c2pnb163v1             1 2 840 10045 3 0 1 */
+685,	/* OBJ_X9_62_c2pnb163v2             1 2 840 10045 3 0 2 */
+686,	/* OBJ_X9_62_c2pnb163v3             1 2 840 10045 3 0 3 */
+687,	/* OBJ_X9_62_c2pnb176v1             1 2 840 10045 3 0 4 */
+688,	/* OBJ_X9_62_c2tnb191v1             1 2 840 10045 3 0 5 */
+689,	/* OBJ_X9_62_c2tnb191v2             1 2 840 10045 3 0 6 */
+690,	/* OBJ_X9_62_c2tnb191v3             1 2 840 10045 3 0 7 */
+691,	/* OBJ_X9_62_c2onb191v4             1 2 840 10045 3 0 8 */
+692,	/* OBJ_X9_62_c2onb191v5             1 2 840 10045 3 0 9 */
+693,	/* OBJ_X9_62_c2pnb208w1             1 2 840 10045 3 0 10 */
+694,	/* OBJ_X9_62_c2tnb239v1             1 2 840 10045 3 0 11 */
+695,	/* OBJ_X9_62_c2tnb239v2             1 2 840 10045 3 0 12 */
+696,	/* OBJ_X9_62_c2tnb239v3             1 2 840 10045 3 0 13 */
+697,	/* OBJ_X9_62_c2onb239v4             1 2 840 10045 3 0 14 */
+698,	/* OBJ_X9_62_c2onb239v5             1 2 840 10045 3 0 15 */
+699,	/* OBJ_X9_62_c2pnb272w1             1 2 840 10045 3 0 16 */
+700,	/* OBJ_X9_62_c2pnb304w1             1 2 840 10045 3 0 17 */
+701,	/* OBJ_X9_62_c2tnb359v1             1 2 840 10045 3 0 18 */
+702,	/* OBJ_X9_62_c2pnb368w1             1 2 840 10045 3 0 19 */
+703,	/* OBJ_X9_62_c2tnb431r1             1 2 840 10045 3 0 20 */
+409,	/* OBJ_X9_62_prime192v1             1 2 840 10045 3 1 1 */
+410,	/* OBJ_X9_62_prime192v2             1 2 840 10045 3 1 2 */
+411,	/* OBJ_X9_62_prime192v3             1 2 840 10045 3 1 3 */
+412,	/* OBJ_X9_62_prime239v1             1 2 840 10045 3 1 4 */
+413,	/* OBJ_X9_62_prime239v2             1 2 840 10045 3 1 5 */
+414,	/* OBJ_X9_62_prime239v3             1 2 840 10045 3 1 6 */
+415,	/* OBJ_X9_62_prime256v1             1 2 840 10045 3 1 7 */
+793,	/* OBJ_ecdsa_with_SHA224            1 2 840 10045 4 3 1 */
+794,	/* OBJ_ecdsa_with_SHA256            1 2 840 10045 4 3 2 */
+795,	/* OBJ_ecdsa_with_SHA384            1 2 840 10045 4 3 3 */
+796,	/* OBJ_ecdsa_with_SHA512            1 2 840 10045 4 3 4 */
+269,	/* OBJ_id_pkix1_explicit_88         1 3 6 1 5 5 7 0 1 */
+270,	/* OBJ_id_pkix1_implicit_88         1 3 6 1 5 5 7 0 2 */
+271,	/* OBJ_id_pkix1_explicit_93         1 3 6 1 5 5 7 0 3 */
+272,	/* OBJ_id_pkix1_implicit_93         1 3 6 1 5 5 7 0 4 */
+273,	/* OBJ_id_mod_crmf                  1 3 6 1 5 5 7 0 5 */
+274,	/* OBJ_id_mod_cmc                   1 3 6 1 5 5 7 0 6 */
+275,	/* OBJ_id_mod_kea_profile_88        1 3 6 1 5 5 7 0 7 */
+276,	/* OBJ_id_mod_kea_profile_93        1 3 6 1 5 5 7 0 8 */
+277,	/* OBJ_id_mod_cmp                   1 3 6 1 5 5 7 0 9 */
+278,	/* OBJ_id_mod_qualified_cert_88     1 3 6 1 5 5 7 0 10 */
+279,	/* OBJ_id_mod_qualified_cert_93     1 3 6 1 5 5 7 0 11 */
+280,	/* OBJ_id_mod_attribute_cert        1 3 6 1 5 5 7 0 12 */
+281,	/* OBJ_id_mod_timestamp_protocol    1 3 6 1 5 5 7 0 13 */
+282,	/* OBJ_id_mod_ocsp                  1 3 6 1 5 5 7 0 14 */
+283,	/* OBJ_id_mod_dvcs                  1 3 6 1 5 5 7 0 15 */
+284,	/* OBJ_id_mod_cmp2000               1 3 6 1 5 5 7 0 16 */
+177,	/* OBJ_info_access                  1 3 6 1 5 5 7 1 1 */
+285,	/* OBJ_biometricInfo                1 3 6 1 5 5 7 1 2 */
+286,	/* OBJ_qcStatements                 1 3 6 1 5 5 7 1 3 */
+287,	/* OBJ_ac_auditEntity               1 3 6 1 5 5 7 1 4 */
+288,	/* OBJ_ac_targeting                 1 3 6 1 5 5 7 1 5 */
+289,	/* OBJ_aaControls                   1 3 6 1 5 5 7 1 6 */
+290,	/* OBJ_sbgp_ipAddrBlock             1 3 6 1 5 5 7 1 7 */
+291,	/* OBJ_sbgp_autonomousSysNum        1 3 6 1 5 5 7 1 8 */
+292,	/* OBJ_sbgp_routerIdentifier        1 3 6 1 5 5 7 1 9 */
+397,	/* OBJ_ac_proxying                  1 3 6 1 5 5 7 1 10 */
+398,	/* OBJ_sinfo_access                 1 3 6 1 5 5 7 1 11 */
+663,	/* OBJ_proxyCertInfo                1 3 6 1 5 5 7 1 14 */
+164,	/* OBJ_id_qt_cps                    1 3 6 1 5 5 7 2 1 */
+165,	/* OBJ_id_qt_unotice                1 3 6 1 5 5 7 2 2 */
+293,	/* OBJ_textNotice                   1 3 6 1 5 5 7 2 3 */
+129,	/* OBJ_server_auth                  1 3 6 1 5 5 7 3 1 */
+130,	/* OBJ_client_auth                  1 3 6 1 5 5 7 3 2 */
+131,	/* OBJ_code_sign                    1 3 6 1 5 5 7 3 3 */
+132,	/* OBJ_email_protect                1 3 6 1 5 5 7 3 4 */
+294,	/* OBJ_ipsecEndSystem               1 3 6 1 5 5 7 3 5 */
+295,	/* OBJ_ipsecTunnel                  1 3 6 1 5 5 7 3 6 */
+296,	/* OBJ_ipsecUser                    1 3 6 1 5 5 7 3 7 */
+133,	/* OBJ_time_stamp                   1 3 6 1 5 5 7 3 8 */
+180,	/* OBJ_OCSP_sign                    1 3 6 1 5 5 7 3 9 */
+297,	/* OBJ_dvcs                         1 3 6 1 5 5 7 3 10 */
+298,	/* OBJ_id_it_caProtEncCert          1 3 6 1 5 5 7 4 1 */
+299,	/* OBJ_id_it_signKeyPairTypes       1 3 6 1 5 5 7 4 2 */
+300,	/* OBJ_id_it_encKeyPairTypes        1 3 6 1 5 5 7 4 3 */
+301,	/* OBJ_id_it_preferredSymmAlg       1 3 6 1 5 5 7 4 4 */
+302,	/* OBJ_id_it_caKeyUpdateInfo        1 3 6 1 5 5 7 4 5 */
+303,	/* OBJ_id_it_currentCRL             1 3 6 1 5 5 7 4 6 */
+304,	/* OBJ_id_it_unsupportedOIDs        1 3 6 1 5 5 7 4 7 */
+305,	/* OBJ_id_it_subscriptionRequest    1 3 6 1 5 5 7 4 8 */
+306,	/* OBJ_id_it_subscriptionResponse   1 3 6 1 5 5 7 4 9 */
+307,	/* OBJ_id_it_keyPairParamReq        1 3 6 1 5 5 7 4 10 */
+308,	/* OBJ_id_it_keyPairParamRep        1 3 6 1 5 5 7 4 11 */
+309,	/* OBJ_id_it_revPassphrase          1 3 6 1 5 5 7 4 12 */
+310,	/* OBJ_id_it_implicitConfirm        1 3 6 1 5 5 7 4 13 */
+311,	/* OBJ_id_it_confirmWaitTime        1 3 6 1 5 5 7 4 14 */
+312,	/* OBJ_id_it_origPKIMessage         1 3 6 1 5 5 7 4 15 */
+784,	/* OBJ_id_it_suppLangTags           1 3 6 1 5 5 7 4 16 */
+313,	/* OBJ_id_regCtrl                   1 3 6 1 5 5 7 5 1 */
+314,	/* OBJ_id_regInfo                   1 3 6 1 5 5 7 5 2 */
+323,	/* OBJ_id_alg_des40                 1 3 6 1 5 5 7 6 1 */
+324,	/* OBJ_id_alg_noSignature           1 3 6 1 5 5 7 6 2 */
+325,	/* OBJ_id_alg_dh_sig_hmac_sha1      1 3 6 1 5 5 7 6 3 */
+326,	/* OBJ_id_alg_dh_pop                1 3 6 1 5 5 7 6 4 */
+327,	/* OBJ_id_cmc_statusInfo            1 3 6 1 5 5 7 7 1 */
+328,	/* OBJ_id_cmc_identification        1 3 6 1 5 5 7 7 2 */
+329,	/* OBJ_id_cmc_identityProof         1 3 6 1 5 5 7 7 3 */
+330,	/* OBJ_id_cmc_dataReturn            1 3 6 1 5 5 7 7 4 */
+331,	/* OBJ_id_cmc_transactionId         1 3 6 1 5 5 7 7 5 */
+332,	/* OBJ_id_cmc_senderNonce           1 3 6 1 5 5 7 7 6 */
+333,	/* OBJ_id_cmc_recipientNonce        1 3 6 1 5 5 7 7 7 */
+334,	/* OBJ_id_cmc_addExtensions         1 3 6 1 5 5 7 7 8 */
+335,	/* OBJ_id_cmc_encryptedPOP          1 3 6 1 5 5 7 7 9 */
+336,	/* OBJ_id_cmc_decryptedPOP          1 3 6 1 5 5 7 7 10 */
+337,	/* OBJ_id_cmc_lraPOPWitness         1 3 6 1 5 5 7 7 11 */
+338,	/* OBJ_id_cmc_getCert               1 3 6 1 5 5 7 7 15 */
+339,	/* OBJ_id_cmc_getCRL                1 3 6 1 5 5 7 7 16 */
+340,	/* OBJ_id_cmc_revokeRequest         1 3 6 1 5 5 7 7 17 */
+341,	/* OBJ_id_cmc_regInfo               1 3 6 1 5 5 7 7 18 */
+342,	/* OBJ_id_cmc_responseInfo          1 3 6 1 5 5 7 7 19 */
+343,	/* OBJ_id_cmc_queryPending          1 3 6 1 5 5 7 7 21 */
+344,	/* OBJ_id_cmc_popLinkRandom         1 3 6 1 5 5 7 7 22 */
+345,	/* OBJ_id_cmc_popLinkWitness        1 3 6 1 5 5 7 7 23 */
+346,	/* OBJ_id_cmc_confirmCertAcceptance 1 3 6 1 5 5 7 7 24 */
+347,	/* OBJ_id_on_personalData           1 3 6 1 5 5 7 8 1 */
+858,	/* OBJ_id_on_permanentIdentifier    1 3 6 1 5 5 7 8 3 */
+348,	/* OBJ_id_pda_dateOfBirth           1 3 6 1 5 5 7 9 1 */
+349,	/* OBJ_id_pda_placeOfBirth          1 3 6 1 5 5 7 9 2 */
+351,	/* OBJ_id_pda_gender                1 3 6 1 5 5 7 9 3 */
+352,	/* OBJ_id_pda_countryOfCitizenship  1 3 6 1 5 5 7 9 4 */
+353,	/* OBJ_id_pda_countryOfResidence    1 3 6 1 5 5 7 9 5 */
+354,	/* OBJ_id_aca_authenticationInfo    1 3 6 1 5 5 7 10 1 */
+355,	/* OBJ_id_aca_accessIdentity        1 3 6 1 5 5 7 10 2 */
+356,	/* OBJ_id_aca_chargingIdentity      1 3 6 1 5 5 7 10 3 */
+357,	/* OBJ_id_aca_group                 1 3 6 1 5 5 7 10 4 */
+358,	/* OBJ_id_aca_role                  1 3 6 1 5 5 7 10 5 */
+399,	/* OBJ_id_aca_encAttrs              1 3 6 1 5 5 7 10 6 */
+359,	/* OBJ_id_qcs_pkixQCSyntax_v1       1 3 6 1 5 5 7 11 1 */
+360,	/* OBJ_id_cct_crs                   1 3 6 1 5 5 7 12 1 */
+361,	/* OBJ_id_cct_PKIData               1 3 6 1 5 5 7 12 2 */
+362,	/* OBJ_id_cct_PKIResponse           1 3 6 1 5 5 7 12 3 */
+664,	/* OBJ_id_ppl_anyLanguage           1 3 6 1 5 5 7 21 0 */
+665,	/* OBJ_id_ppl_inheritAll            1 3 6 1 5 5 7 21 1 */
+667,	/* OBJ_Independent                  1 3 6 1 5 5 7 21 2 */
+178,	/* OBJ_ad_OCSP                      1 3 6 1 5 5 7 48 1 */
+179,	/* OBJ_ad_ca_issuers                1 3 6 1 5 5 7 48 2 */
+363,	/* OBJ_ad_timeStamping              1 3 6 1 5 5 7 48 3 */
+364,	/* OBJ_ad_dvcs                      1 3 6 1 5 5 7 48 4 */
+785,	/* OBJ_caRepository                 1 3 6 1 5 5 7 48 5 */
+780,	/* OBJ_hmac_md5                     1 3 6 1 5 5 8 1 1 */
+781,	/* OBJ_hmac_sha1                    1 3 6 1 5 5 8 1 2 */
+58,	/* OBJ_netscape_cert_extension      2 16 840 1 113730 1 */
+59,	/* OBJ_netscape_data_type           2 16 840 1 113730 2 */
+438,	/* OBJ_pilotAttributeType           0 9 2342 19200300 100 1 */
+439,	/* OBJ_pilotAttributeSyntax         0 9 2342 19200300 100 3 */
+440,	/* OBJ_pilotObjectClass             0 9 2342 19200300 100 4 */
+441,	/* OBJ_pilotGroups                  0 9 2342 19200300 100 10 */
+108,	/* OBJ_cast5_cbc                    1 2 840 113533 7 66 10 */
+112,	/* OBJ_pbeWithMD5AndCast5_CBC       1 2 840 113533 7 66 12 */
+782,	/* OBJ_id_PasswordBasedMAC          1 2 840 113533 7 66 13 */
+783,	/* OBJ_id_DHBasedMac                1 2 840 113533 7 66 30 */
+ 6,	/* OBJ_rsaEncryption                1 2 840 113549 1 1 1 */
+ 7,	/* OBJ_md2WithRSAEncryption         1 2 840 113549 1 1 2 */
+396,	/* OBJ_md4WithRSAEncryption         1 2 840 113549 1 1 3 */
+ 8,	/* OBJ_md5WithRSAEncryption         1 2 840 113549 1 1 4 */
+65,	/* OBJ_sha1WithRSAEncryption        1 2 840 113549 1 1 5 */
+644,	/* OBJ_rsaOAEPEncryptionSET         1 2 840 113549 1 1 6 */
+919,	/* OBJ_rsaesOaep                    1 2 840 113549 1 1 7 */
+911,	/* OBJ_mgf1                         1 2 840 113549 1 1 8 */
+935,	/* OBJ_pSpecified                   1 2 840 113549 1 1 9 */
+912,	/* OBJ_rsassaPss                    1 2 840 113549 1 1 10 */
+668,	/* OBJ_sha256WithRSAEncryption      1 2 840 113549 1 1 11 */
+669,	/* OBJ_sha384WithRSAEncryption      1 2 840 113549 1 1 12 */
+670,	/* OBJ_sha512WithRSAEncryption      1 2 840 113549 1 1 13 */
+671,	/* OBJ_sha224WithRSAEncryption      1 2 840 113549 1 1 14 */
+28,	/* OBJ_dhKeyAgreement               1 2 840 113549 1 3 1 */
+ 9,	/* OBJ_pbeWithMD2AndDES_CBC         1 2 840 113549 1 5 1 */
+10,	/* OBJ_pbeWithMD5AndDES_CBC         1 2 840 113549 1 5 3 */
+168,	/* OBJ_pbeWithMD2AndRC2_CBC         1 2 840 113549 1 5 4 */
+169,	/* OBJ_pbeWithMD5AndRC2_CBC         1 2 840 113549 1 5 6 */
+170,	/* OBJ_pbeWithSHA1AndDES_CBC        1 2 840 113549 1 5 10 */
+68,	/* OBJ_pbeWithSHA1AndRC2_CBC        1 2 840 113549 1 5 11 */
+69,	/* OBJ_id_pbkdf2                    1 2 840 113549 1 5 12 */
+161,	/* OBJ_pbes2                        1 2 840 113549 1 5 13 */
+162,	/* OBJ_pbmac1                       1 2 840 113549 1 5 14 */
+21,	/* OBJ_pkcs7_data                   1 2 840 113549 1 7 1 */
+22,	/* OBJ_pkcs7_signed                 1 2 840 113549 1 7 2 */
+23,	/* OBJ_pkcs7_enveloped              1 2 840 113549 1 7 3 */
+24,	/* OBJ_pkcs7_signedAndEnveloped     1 2 840 113549 1 7 4 */
+25,	/* OBJ_pkcs7_digest                 1 2 840 113549 1 7 5 */
+26,	/* OBJ_pkcs7_encrypted              1 2 840 113549 1 7 6 */
+48,	/* OBJ_pkcs9_emailAddress           1 2 840 113549 1 9 1 */
+49,	/* OBJ_pkcs9_unstructuredName       1 2 840 113549 1 9 2 */
+50,	/* OBJ_pkcs9_contentType            1 2 840 113549 1 9 3 */
+51,	/* OBJ_pkcs9_messageDigest          1 2 840 113549 1 9 4 */
+52,	/* OBJ_pkcs9_signingTime            1 2 840 113549 1 9 5 */
+53,	/* OBJ_pkcs9_countersignature       1 2 840 113549 1 9 6 */
+54,	/* OBJ_pkcs9_challengePassword      1 2 840 113549 1 9 7 */
+55,	/* OBJ_pkcs9_unstructuredAddress    1 2 840 113549 1 9 8 */
+56,	/* OBJ_pkcs9_extCertAttributes      1 2 840 113549 1 9 9 */
+172,	/* OBJ_ext_req                      1 2 840 113549 1 9 14 */
+167,	/* OBJ_SMIMECapabilities            1 2 840 113549 1 9 15 */
+188,	/* OBJ_SMIME                        1 2 840 113549 1 9 16 */
+156,	/* OBJ_friendlyName                 1 2 840 113549 1 9 20 */
+157,	/* OBJ_localKeyID                   1 2 840 113549 1 9 21 */
+681,	/* OBJ_X9_62_onBasis                1 2 840 10045 1 2 3 1 */
+682,	/* OBJ_X9_62_tpBasis                1 2 840 10045 1 2 3 2 */
+683,	/* OBJ_X9_62_ppBasis                1 2 840 10045 1 2 3 3 */
+417,	/* OBJ_ms_csp_name                  1 3 6 1 4 1 311 17 1 */
+856,	/* OBJ_LocalKeySet                  1 3 6 1 4 1 311 17 2 */
+390,	/* OBJ_dcObject                     1 3 6 1 4 1 1466 344 */
+91,	/* OBJ_bf_cbc                       1 3 6 1 4 1 3029 1 2 */
+315,	/* OBJ_id_regCtrl_regToken          1 3 6 1 5 5 7 5 1 1 */
+316,	/* OBJ_id_regCtrl_authenticator     1 3 6 1 5 5 7 5 1 2 */
+317,	/* OBJ_id_regCtrl_pkiPublicationInfo 1 3 6 1 5 5 7 5 1 3 */
+318,	/* OBJ_id_regCtrl_pkiArchiveOptions 1 3 6 1 5 5 7 5 1 4 */
+319,	/* OBJ_id_regCtrl_oldCertID         1 3 6 1 5 5 7 5 1 5 */
+320,	/* OBJ_id_regCtrl_protocolEncrKey   1 3 6 1 5 5 7 5 1 6 */
+321,	/* OBJ_id_regInfo_utf8Pairs         1 3 6 1 5 5 7 5 2 1 */
+322,	/* OBJ_id_regInfo_certReq           1 3 6 1 5 5 7 5 2 2 */
+365,	/* OBJ_id_pkix_OCSP_basic           1 3 6 1 5 5 7 48 1 1 */
+366,	/* OBJ_id_pkix_OCSP_Nonce           1 3 6 1 5 5 7 48 1 2 */
+367,	/* OBJ_id_pkix_OCSP_CrlID           1 3 6 1 5 5 7 48 1 3 */
+368,	/* OBJ_id_pkix_OCSP_acceptableResponses 1 3 6 1 5 5 7 48 1 4 */
+369,	/* OBJ_id_pkix_OCSP_noCheck         1 3 6 1 5 5 7 48 1 5 */
+370,	/* OBJ_id_pkix_OCSP_archiveCutoff   1 3 6 1 5 5 7 48 1 6 */
+371,	/* OBJ_id_pkix_OCSP_serviceLocator  1 3 6 1 5 5 7 48 1 7 */
+372,	/* OBJ_id_pkix_OCSP_extendedStatus  1 3 6 1 5 5 7 48 1 8 */
+373,	/* OBJ_id_pkix_OCSP_valid           1 3 6 1 5 5 7 48 1 9 */
+374,	/* OBJ_id_pkix_OCSP_path            1 3 6 1 5 5 7 48 1 10 */
+375,	/* OBJ_id_pkix_OCSP_trustRoot       1 3 6 1 5 5 7 48 1 11 */
+921,	/* OBJ_brainpoolP160r1              1 3 36 3 3 2 8 1 1 1 */
+922,	/* OBJ_brainpoolP160t1              1 3 36 3 3 2 8 1 1 2 */
+923,	/* OBJ_brainpoolP192r1              1 3 36 3 3 2 8 1 1 3 */
+924,	/* OBJ_brainpoolP192t1              1 3 36 3 3 2 8 1 1 4 */
+925,	/* OBJ_brainpoolP224r1              1 3 36 3 3 2 8 1 1 5 */
+926,	/* OBJ_brainpoolP224t1              1 3 36 3 3 2 8 1 1 6 */
+927,	/* OBJ_brainpoolP256r1              1 3 36 3 3 2 8 1 1 7 */
+928,	/* OBJ_brainpoolP256t1              1 3 36 3 3 2 8 1 1 8 */
+929,	/* OBJ_brainpoolP320r1              1 3 36 3 3 2 8 1 1 9 */
+930,	/* OBJ_brainpoolP320t1              1 3 36 3 3 2 8 1 1 10 */
+931,	/* OBJ_brainpoolP384r1              1 3 36 3 3 2 8 1 1 11 */
+932,	/* OBJ_brainpoolP384t1              1 3 36 3 3 2 8 1 1 12 */
+933,	/* OBJ_brainpoolP512r1              1 3 36 3 3 2 8 1 1 13 */
+934,	/* OBJ_brainpoolP512t1              1 3 36 3 3 2 8 1 1 14 */
+939,	/* OBJ_dhSinglePass_stdDH_sha1kdf_scheme 1 3 133 16 840 63 0 2 */
+944,	/* OBJ_dhSinglePass_cofactorDH_sha1kdf_scheme 1 3 133 16 840 63 0 3 */
+418,	/* OBJ_aes_128_ecb                  2 16 840 1 101 3 4 1 1 */
+419,	/* OBJ_aes_128_cbc                  2 16 840 1 101 3 4 1 2 */
+420,	/* OBJ_aes_128_ofb128               2 16 840 1 101 3 4 1 3 */
+421,	/* OBJ_aes_128_cfb128               2 16 840 1 101 3 4 1 4 */
+788,	/* OBJ_id_aes128_wrap               2 16 840 1 101 3 4 1 5 */
+895,	/* OBJ_aes_128_gcm                  2 16 840 1 101 3 4 1 6 */
+896,	/* OBJ_aes_128_ccm                  2 16 840 1 101 3 4 1 7 */
+897,	/* OBJ_id_aes128_wrap_pad           2 16 840 1 101 3 4 1 8 */
+422,	/* OBJ_aes_192_ecb                  2 16 840 1 101 3 4 1 21 */
+423,	/* OBJ_aes_192_cbc                  2 16 840 1 101 3 4 1 22 */
+424,	/* OBJ_aes_192_ofb128               2 16 840 1 101 3 4 1 23 */
+425,	/* OBJ_aes_192_cfb128               2 16 840 1 101 3 4 1 24 */
+789,	/* OBJ_id_aes192_wrap               2 16 840 1 101 3 4 1 25 */
+898,	/* OBJ_aes_192_gcm                  2 16 840 1 101 3 4 1 26 */
+899,	/* OBJ_aes_192_ccm                  2 16 840 1 101 3 4 1 27 */
+900,	/* OBJ_id_aes192_wrap_pad           2 16 840 1 101 3 4 1 28 */
+426,	/* OBJ_aes_256_ecb                  2 16 840 1 101 3 4 1 41 */
+427,	/* OBJ_aes_256_cbc                  2 16 840 1 101 3 4 1 42 */
+428,	/* OBJ_aes_256_ofb128               2 16 840 1 101 3 4 1 43 */
+429,	/* OBJ_aes_256_cfb128               2 16 840 1 101 3 4 1 44 */
+790,	/* OBJ_id_aes256_wrap               2 16 840 1 101 3 4 1 45 */
+901,	/* OBJ_aes_256_gcm                  2 16 840 1 101 3 4 1 46 */
+902,	/* OBJ_aes_256_ccm                  2 16 840 1 101 3 4 1 47 */
+903,	/* OBJ_id_aes256_wrap_pad           2 16 840 1 101 3 4 1 48 */
+672,	/* OBJ_sha256                       2 16 840 1 101 3 4 2 1 */
+673,	/* OBJ_sha384                       2 16 840 1 101 3 4 2 2 */
+674,	/* OBJ_sha512                       2 16 840 1 101 3 4 2 3 */
+675,	/* OBJ_sha224                       2 16 840 1 101 3 4 2 4 */
+802,	/* OBJ_dsa_with_SHA224              2 16 840 1 101 3 4 3 1 */
+803,	/* OBJ_dsa_with_SHA256              2 16 840 1 101 3 4 3 2 */
+71,	/* OBJ_netscape_cert_type           2 16 840 1 113730 1 1 */
+72,	/* OBJ_netscape_base_url            2 16 840 1 113730 1 2 */
+73,	/* OBJ_netscape_revocation_url      2 16 840 1 113730 1 3 */
+74,	/* OBJ_netscape_ca_revocation_url   2 16 840 1 113730 1 4 */
+75,	/* OBJ_netscape_renewal_url         2 16 840 1 113730 1 7 */
+76,	/* OBJ_netscape_ca_policy_url       2 16 840 1 113730 1 8 */
+77,	/* OBJ_netscape_ssl_server_name     2 16 840 1 113730 1 12 */
+78,	/* OBJ_netscape_comment             2 16 840 1 113730 1 13 */
+79,	/* OBJ_netscape_cert_sequence       2 16 840 1 113730 2 5 */
+139,	/* OBJ_ns_sgc                       2 16 840 1 113730 4 1 */
+458,	/* OBJ_userId                       0 9 2342 19200300 100 1 1 */
+459,	/* OBJ_textEncodedORAddress         0 9 2342 19200300 100 1 2 */
+460,	/* OBJ_rfc822Mailbox                0 9 2342 19200300 100 1 3 */
+461,	/* OBJ_info                         0 9 2342 19200300 100 1 4 */
+462,	/* OBJ_favouriteDrink               0 9 2342 19200300 100 1 5 */
+463,	/* OBJ_roomNumber                   0 9 2342 19200300 100 1 6 */
+464,	/* OBJ_photo                        0 9 2342 19200300 100 1 7 */
+465,	/* OBJ_userClass                    0 9 2342 19200300 100 1 8 */
+466,	/* OBJ_host                         0 9 2342 19200300 100 1 9 */
+467,	/* OBJ_manager                      0 9 2342 19200300 100 1 10 */
+468,	/* OBJ_documentIdentifier           0 9 2342 19200300 100 1 11 */
+469,	/* OBJ_documentTitle                0 9 2342 19200300 100 1 12 */
+470,	/* OBJ_documentVersion              0 9 2342 19200300 100 1 13 */
+471,	/* OBJ_documentAuthor               0 9 2342 19200300 100 1 14 */
+472,	/* OBJ_documentLocation             0 9 2342 19200300 100 1 15 */
+473,	/* OBJ_homeTelephoneNumber          0 9 2342 19200300 100 1 20 */
+474,	/* OBJ_secretary                    0 9 2342 19200300 100 1 21 */
+475,	/* OBJ_otherMailbox                 0 9 2342 19200300 100 1 22 */
+476,	/* OBJ_lastModifiedTime             0 9 2342 19200300 100 1 23 */
+477,	/* OBJ_lastModifiedBy               0 9 2342 19200300 100 1 24 */
+391,	/* OBJ_domainComponent              0 9 2342 19200300 100 1 25 */
+478,	/* OBJ_aRecord                      0 9 2342 19200300 100 1 26 */
+479,	/* OBJ_pilotAttributeType27         0 9 2342 19200300 100 1 27 */
+480,	/* OBJ_mXRecord                     0 9 2342 19200300 100 1 28 */
+481,	/* OBJ_nSRecord                     0 9 2342 19200300 100 1 29 */
+482,	/* OBJ_sOARecord                    0 9 2342 19200300 100 1 30 */
+483,	/* OBJ_cNAMERecord                  0 9 2342 19200300 100 1 31 */
+484,	/* OBJ_associatedDomain             0 9 2342 19200300 100 1 37 */
+485,	/* OBJ_associatedName               0 9 2342 19200300 100 1 38 */
+486,	/* OBJ_homePostalAddress            0 9 2342 19200300 100 1 39 */
+487,	/* OBJ_personalTitle                0 9 2342 19200300 100 1 40 */
+488,	/* OBJ_mobileTelephoneNumber        0 9 2342 19200300 100 1 41 */
+489,	/* OBJ_pagerTelephoneNumber         0 9 2342 19200300 100 1 42 */
+490,	/* OBJ_friendlyCountryName          0 9 2342 19200300 100 1 43 */
+491,	/* OBJ_organizationalStatus         0 9 2342 19200300 100 1 45 */
+492,	/* OBJ_janetMailbox                 0 9 2342 19200300 100 1 46 */
+493,	/* OBJ_mailPreferenceOption         0 9 2342 19200300 100 1 47 */
+494,	/* OBJ_buildingName                 0 9 2342 19200300 100 1 48 */
+495,	/* OBJ_dSAQuality                   0 9 2342 19200300 100 1 49 */
+496,	/* OBJ_singleLevelQuality           0 9 2342 19200300 100 1 50 */
+497,	/* OBJ_subtreeMinimumQuality        0 9 2342 19200300 100 1 51 */
+498,	/* OBJ_subtreeMaximumQuality        0 9 2342 19200300 100 1 52 */
+499,	/* OBJ_personalSignature            0 9 2342 19200300 100 1 53 */
+500,	/* OBJ_dITRedirect                  0 9 2342 19200300 100 1 54 */
+501,	/* OBJ_audio                        0 9 2342 19200300 100 1 55 */
+502,	/* OBJ_documentPublisher            0 9 2342 19200300 100 1 56 */
+442,	/* OBJ_iA5StringSyntax              0 9 2342 19200300 100 3 4 */
+443,	/* OBJ_caseIgnoreIA5StringSyntax    0 9 2342 19200300 100 3 5 */
+444,	/* OBJ_pilotObject                  0 9 2342 19200300 100 4 3 */
+445,	/* OBJ_pilotPerson                  0 9 2342 19200300 100 4 4 */
+446,	/* OBJ_account                      0 9 2342 19200300 100 4 5 */
+447,	/* OBJ_document                     0 9 2342 19200300 100 4 6 */
+448,	/* OBJ_room                         0 9 2342 19200300 100 4 7 */
+449,	/* OBJ_documentSeries               0 9 2342 19200300 100 4 9 */
+392,	/* OBJ_Domain                       0 9 2342 19200300 100 4 13 */
+450,	/* OBJ_rFC822localPart              0 9 2342 19200300 100 4 14 */
+451,	/* OBJ_dNSDomain                    0 9 2342 19200300 100 4 15 */
+452,	/* OBJ_domainRelatedObject          0 9 2342 19200300 100 4 17 */
+453,	/* OBJ_friendlyCountry              0 9 2342 19200300 100 4 18 */
+454,	/* OBJ_simpleSecurityObject         0 9 2342 19200300 100 4 19 */
+455,	/* OBJ_pilotOrganization            0 9 2342 19200300 100 4 20 */
+456,	/* OBJ_pilotDSA                     0 9 2342 19200300 100 4 21 */
+457,	/* OBJ_qualityLabelledData          0 9 2342 19200300 100 4 22 */
+189,	/* OBJ_id_smime_mod                 1 2 840 113549 1 9 16 0 */
+190,	/* OBJ_id_smime_ct                  1 2 840 113549 1 9 16 1 */
+191,	/* OBJ_id_smime_aa                  1 2 840 113549 1 9 16 2 */
+192,	/* OBJ_id_smime_alg                 1 2 840 113549 1 9 16 3 */
+193,	/* OBJ_id_smime_cd                  1 2 840 113549 1 9 16 4 */
+194,	/* OBJ_id_smime_spq                 1 2 840 113549 1 9 16 5 */
+195,	/* OBJ_id_smime_cti                 1 2 840 113549 1 9 16 6 */
+158,	/* OBJ_x509Certificate              1 2 840 113549 1 9 22 1 */
+159,	/* OBJ_sdsiCertificate              1 2 840 113549 1 9 22 2 */
+160,	/* OBJ_x509Crl                      1 2 840 113549 1 9 23 1 */
+144,	/* OBJ_pbe_WithSHA1And128BitRC4     1 2 840 113549 1 12 1 1 */
+145,	/* OBJ_pbe_WithSHA1And40BitRC4      1 2 840 113549 1 12 1 2 */
+146,	/* OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC 1 2 840 113549 1 12 1 3 */
+147,	/* OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC 1 2 840 113549 1 12 1 4 */
+148,	/* OBJ_pbe_WithSHA1And128BitRC2_CBC 1 2 840 113549 1 12 1 5 */
+149,	/* OBJ_pbe_WithSHA1And40BitRC2_CBC  1 2 840 113549 1 12 1 6 */
+171,	/* OBJ_ms_ext_req                   1 3 6 1 4 1 311 2 1 14 */
+134,	/* OBJ_ms_code_ind                  1 3 6 1 4 1 311 2 1 21 */
+135,	/* OBJ_ms_code_com                  1 3 6 1 4 1 311 2 1 22 */
+136,	/* OBJ_ms_ctl_sign                  1 3 6 1 4 1 311 10 3 1 */
+137,	/* OBJ_ms_sgc                       1 3 6 1 4 1 311 10 3 3 */
+138,	/* OBJ_ms_efs                       1 3 6 1 4 1 311 10 3 4 */
+648,	/* OBJ_ms_smartcard_login           1 3 6 1 4 1 311 20 2 2 */
+649,	/* OBJ_ms_upn                       1 3 6 1 4 1 311 20 2 3 */
+751,	/* OBJ_camellia_128_cbc             1 2 392 200011 61 1 1 1 2 */
+752,	/* OBJ_camellia_192_cbc             1 2 392 200011 61 1 1 1 3 */
+753,	/* OBJ_camellia_256_cbc             1 2 392 200011 61 1 1 1 4 */
+907,	/* OBJ_id_camellia128_wrap          1 2 392 200011 61 1 1 3 2 */
+908,	/* OBJ_id_camellia192_wrap          1 2 392 200011 61 1 1 3 3 */
+909,	/* OBJ_id_camellia256_wrap          1 2 392 200011 61 1 1 3 4 */
+196,	/* OBJ_id_smime_mod_cms             1 2 840 113549 1 9 16 0 1 */
+197,	/* OBJ_id_smime_mod_ess             1 2 840 113549 1 9 16 0 2 */
+198,	/* OBJ_id_smime_mod_oid             1 2 840 113549 1 9 16 0 3 */
+199,	/* OBJ_id_smime_mod_msg_v3          1 2 840 113549 1 9 16 0 4 */
+200,	/* OBJ_id_smime_mod_ets_eSignature_88 1 2 840 113549 1 9 16 0 5 */
+201,	/* OBJ_id_smime_mod_ets_eSignature_97 1 2 840 113549 1 9 16 0 6 */
+202,	/* OBJ_id_smime_mod_ets_eSigPolicy_88 1 2 840 113549 1 9 16 0 7 */
+203,	/* OBJ_id_smime_mod_ets_eSigPolicy_97 1 2 840 113549 1 9 16 0 8 */
+204,	/* OBJ_id_smime_ct_receipt          1 2 840 113549 1 9 16 1 1 */
+205,	/* OBJ_id_smime_ct_authData         1 2 840 113549 1 9 16 1 2 */
+206,	/* OBJ_id_smime_ct_publishCert      1 2 840 113549 1 9 16 1 3 */
+207,	/* OBJ_id_smime_ct_TSTInfo          1 2 840 113549 1 9 16 1 4 */
+208,	/* OBJ_id_smime_ct_TDTInfo          1 2 840 113549 1 9 16 1 5 */
+209,	/* OBJ_id_smime_ct_contentInfo      1 2 840 113549 1 9 16 1 6 */
+210,	/* OBJ_id_smime_ct_DVCSRequestData  1 2 840 113549 1 9 16 1 7 */
+211,	/* OBJ_id_smime_ct_DVCSResponseData 1 2 840 113549 1 9 16 1 8 */
+786,	/* OBJ_id_smime_ct_compressedData   1 2 840 113549 1 9 16 1 9 */
+787,	/* OBJ_id_ct_asciiTextWithCRLF      1 2 840 113549 1 9 16 1 27 */
+212,	/* OBJ_id_smime_aa_receiptRequest   1 2 840 113549 1 9 16 2 1 */
+213,	/* OBJ_id_smime_aa_securityLabel    1 2 840 113549 1 9 16 2 2 */
+214,	/* OBJ_id_smime_aa_mlExpandHistory  1 2 840 113549 1 9 16 2 3 */
+215,	/* OBJ_id_smime_aa_contentHint      1 2 840 113549 1 9 16 2 4 */
+216,	/* OBJ_id_smime_aa_msgSigDigest     1 2 840 113549 1 9 16 2 5 */
+217,	/* OBJ_id_smime_aa_encapContentType 1 2 840 113549 1 9 16 2 6 */
+218,	/* OBJ_id_smime_aa_contentIdentifier 1 2 840 113549 1 9 16 2 7 */
+219,	/* OBJ_id_smime_aa_macValue         1 2 840 113549 1 9 16 2 8 */
+220,	/* OBJ_id_smime_aa_equivalentLabels 1 2 840 113549 1 9 16 2 9 */
+221,	/* OBJ_id_smime_aa_contentReference 1 2 840 113549 1 9 16 2 10 */
+222,	/* OBJ_id_smime_aa_encrypKeyPref    1 2 840 113549 1 9 16 2 11 */
+223,	/* OBJ_id_smime_aa_signingCertificate 1 2 840 113549 1 9 16 2 12 */
+224,	/* OBJ_id_smime_aa_smimeEncryptCerts 1 2 840 113549 1 9 16 2 13 */
+225,	/* OBJ_id_smime_aa_timeStampToken   1 2 840 113549 1 9 16 2 14 */
+226,	/* OBJ_id_smime_aa_ets_sigPolicyId  1 2 840 113549 1 9 16 2 15 */
+227,	/* OBJ_id_smime_aa_ets_commitmentType 1 2 840 113549 1 9 16 2 16 */
+228,	/* OBJ_id_smime_aa_ets_signerLocation 1 2 840 113549 1 9 16 2 17 */
+229,	/* OBJ_id_smime_aa_ets_signerAttr   1 2 840 113549 1 9 16 2 18 */
+230,	/* OBJ_id_smime_aa_ets_otherSigCert 1 2 840 113549 1 9 16 2 19 */
+231,	/* OBJ_id_smime_aa_ets_contentTimestamp 1 2 840 113549 1 9 16 2 20 */
+232,	/* OBJ_id_smime_aa_ets_CertificateRefs 1 2 840 113549 1 9 16 2 21 */
+233,	/* OBJ_id_smime_aa_ets_RevocationRefs 1 2 840 113549 1 9 16 2 22 */
+234,	/* OBJ_id_smime_aa_ets_certValues   1 2 840 113549 1 9 16 2 23 */
+235,	/* OBJ_id_smime_aa_ets_revocationValues 1 2 840 113549 1 9 16 2 24 */
+236,	/* OBJ_id_smime_aa_ets_escTimeStamp 1 2 840 113549 1 9 16 2 25 */
+237,	/* OBJ_id_smime_aa_ets_certCRLTimestamp 1 2 840 113549 1 9 16 2 26 */
+238,	/* OBJ_id_smime_aa_ets_archiveTimeStamp 1 2 840 113549 1 9 16 2 27 */
+239,	/* OBJ_id_smime_aa_signatureType    1 2 840 113549 1 9 16 2 28 */
+240,	/* OBJ_id_smime_aa_dvcs_dvc         1 2 840 113549 1 9 16 2 29 */
+241,	/* OBJ_id_smime_alg_ESDHwith3DES    1 2 840 113549 1 9 16 3 1 */
+242,	/* OBJ_id_smime_alg_ESDHwithRC2     1 2 840 113549 1 9 16 3 2 */
+243,	/* OBJ_id_smime_alg_3DESwrap        1 2 840 113549 1 9 16 3 3 */
+244,	/* OBJ_id_smime_alg_RC2wrap         1 2 840 113549 1 9 16 3 4 */
+245,	/* OBJ_id_smime_alg_ESDH            1 2 840 113549 1 9 16 3 5 */
+246,	/* OBJ_id_smime_alg_CMS3DESwrap     1 2 840 113549 1 9 16 3 6 */
+247,	/* OBJ_id_smime_alg_CMSRC2wrap      1 2 840 113549 1 9 16 3 7 */
+125,	/* OBJ_zlib_compression             1 2 840 113549 1 9 16 3 8 */
+893,	/* OBJ_id_alg_PWRI_KEK              1 2 840 113549 1 9 16 3 9 */
+248,	/* OBJ_id_smime_cd_ldap             1 2 840 113549 1 9 16 4 1 */
+249,	/* OBJ_id_smime_spq_ets_sqt_uri     1 2 840 113549 1 9 16 5 1 */
+250,	/* OBJ_id_smime_spq_ets_sqt_unotice 1 2 840 113549 1 9 16 5 2 */
+251,	/* OBJ_id_smime_cti_ets_proofOfOrigin 1 2 840 113549 1 9 16 6 1 */
+252,	/* OBJ_id_smime_cti_ets_proofOfReceipt 1 2 840 113549 1 9 16 6 2 */
+253,	/* OBJ_id_smime_cti_ets_proofOfDelivery 1 2 840 113549 1 9 16 6 3 */
+254,	/* OBJ_id_smime_cti_ets_proofOfSender 1 2 840 113549 1 9 16 6 4 */
+255,	/* OBJ_id_smime_cti_ets_proofOfApproval 1 2 840 113549 1 9 16 6 5 */
+256,	/* OBJ_id_smime_cti_ets_proofOfCreation 1 2 840 113549 1 9 16 6 6 */
+150,	/* OBJ_keyBag                       1 2 840 113549 1 12 10 1 1 */
+151,	/* OBJ_pkcs8ShroudedKeyBag          1 2 840 113549 1 12 10 1 2 */
+152,	/* OBJ_certBag                      1 2 840 113549 1 12 10 1 3 */
+153,	/* OBJ_crlBag                       1 2 840 113549 1 12 10 1 4 */
+154,	/* OBJ_secretBag                    1 2 840 113549 1 12 10 1 5 */
+155,	/* OBJ_safeContentsBag              1 2 840 113549 1 12 10 1 6 */
+34,	/* OBJ_idea_cbc                     1 3 6 1 4 1 188 7 1 1 2 */
+};
+
diff --git a/crypto/obj/obj_dat.pl b/crypto/obj/obj_dat.pl
new file mode 100644
index 0000000..227bfb1
--- /dev/null
+++ b/crypto/obj/obj_dat.pl
@@ -0,0 +1,303 @@
+#!/usr/local/bin/perl
+
+# fixes bug in floating point emulation on sparc64 when
+# this script produces off-by-one output on sparc64
+use integer;
+
+sub obj_cmp
+	{
+	local(@a,@b,$_,$r);
+
+	$A=$obj_len{$obj{$nid{$a}}};
+	$B=$obj_len{$obj{$nid{$b}}};
+
+	$r=($A-$B);
+	return($r) if $r != 0;
+
+	$A=$obj_der{$obj{$nid{$a}}};
+	$B=$obj_der{$obj{$nid{$b}}};
+
+	return($A cmp $B);
+	}
+
+sub expand_obj
+	{
+	local(*v)=@_;
+	local($k,$d);
+	local($i);
+
+	do	{
+		$i=0;
+		foreach $k (keys %v)
+			{
+			if (($v{$k} =~ s/(OBJ_[^,]+),/$v{$1},/))
+				{ $i++; }
+			}
+		} while($i);
+	foreach $k (keys %v)
+		{
+		@a=split(/,/,$v{$k});
+		$objn{$k}=$#a+1;
+		}
+	return(%objn);
+	}
+
+open (IN,"$ARGV[0]") || die "Can't open input file $ARGV[0]";
+open (OUT,">$ARGV[1]") || die "Can't open output file $ARGV[1]";
+
+while (<IN>)
+	{
+	next unless /^\#define\s+(\S+)\s+(.*)$/;
+	$v=$1;
+	$d=$2;
+	$d =~ s/^\"//;
+	$d =~ s/\"$//;
+	if ($v =~ /^SN_(.*)$/)
+		{
+		if(defined $snames{$d})
+			{
+			print "WARNING: Duplicate short name \"$d\"\n";
+			}
+		else 
+			{ $snames{$d} = "X"; }
+		$sn{$1}=$d;
+		}
+	elsif ($v =~ /^LN_(.*)$/)
+		{
+		if(defined $lnames{$d})
+			{
+			print "WARNING: Duplicate long name \"$d\"\n";
+			}
+		else 
+			{ $lnames{$d} = "X"; }
+		$ln{$1}=$d;
+		}
+	elsif ($v =~ /^NID_(.*)$/)
+		{ $nid{$d}=$1; }
+	elsif ($v =~ /^OBJ_(.*)$/)
+		{
+		$obj{$1}=$v;
+		$objd{$v}=$d;
+		}
+	}
+close IN;
+
+%ob=&expand_obj(*objd);
+
+@a=sort { $a <=> $b } keys %nid;
+$n=$a[$#a]+1;
+
+@lvalues=();
+$lvalues=0;
+
+for ($i=0; $i<$n; $i++)
+	{
+	if (!defined($nid{$i}))
+		{
+		push(@out,"{NULL,NULL,NID_undef,0,NULL,0},\n");
+		}
+	else
+		{
+		$sn=defined($sn{$nid{$i}})?"$sn{$nid{$i}}":"NULL";
+		$ln=defined($ln{$nid{$i}})?"$ln{$nid{$i}}":"NULL";
+
+		if ($sn eq "NULL") {
+			$sn=$ln;
+			$sn{$nid{$i}} = $ln;
+		}
+
+		if ($ln eq "NULL") {
+			$ln=$sn;
+			$ln{$nid{$i}} = $sn;
+		}
+			
+		$out ="{";
+		$out.="\"$sn\"";
+		$out.=","."\"$ln\"";
+		$out.=",NID_$nid{$i},";
+		if (defined($obj{$nid{$i}}))
+			{
+			$v=$objd{$obj{$nid{$i}}};
+			$v =~ s/L//g;
+			$v =~ s/,/ /g;
+			$r=&der_it($v);
+			$z="";
+			$length=0;
+			foreach (unpack("C*",$r))
+				{
+				$z.=sprintf("0x%02X,",$_);
+				$length++;
+				}
+			$obj_der{$obj{$nid{$i}}}=$z;
+			$obj_len{$obj{$nid{$i}}}=$length;
+
+			push(@lvalues,sprintf("%-45s/* [%3d] %s */\n",
+				$z,$lvalues,$obj{$nid{$i}}));
+			$out.="$length,&(lvalues[$lvalues]),0";
+			$lvalues+=$length;
+			}
+		else
+			{
+			$out.="0,NULL,0";
+			}
+		$out.="},\n";
+		push(@out,$out);
+		}
+	}
+
+@a=grep(defined($sn{$nid{$_}}),0 .. $n);
+foreach (sort { $sn{$nid{$a}} cmp $sn{$nid{$b}} } @a)
+	{
+	push(@sn,sprintf("%2d,\t/* \"$sn{$nid{$_}}\" */\n",$_));
+	}
+
+@a=grep(defined($ln{$nid{$_}}),0 .. $n);
+foreach (sort { $ln{$nid{$a}} cmp $ln{$nid{$b}} } @a)
+	{
+	push(@ln,sprintf("%2d,\t/* \"$ln{$nid{$_}}\" */\n",$_));
+	}
+
+@a=grep(defined($obj{$nid{$_}}),0 .. $n);
+foreach (sort obj_cmp @a)
+	{
+	$m=$obj{$nid{$_}};
+	$v=$objd{$m};
+	$v =~ s/L//g;
+	$v =~ s/,/ /g;
+	push(@ob,sprintf("%2d,\t/* %-32s %s */\n",$_,$m,$v));
+	}
+
+print OUT <<'EOF';
+/* THIS FILE IS GENERATED FROM objects.h by obj_dat.pl via the
+ * following command:
+ * perl obj_dat.pl obj_mac.h obj_dat.h */
+
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+EOF
+
+printf OUT "#define NUM_NID %d\n",$n;
+printf OUT "#define NUM_SN %d\n",$#sn+1;
+printf OUT "#define NUM_LN %d\n",$#ln+1;
+printf OUT "#define NUM_OBJ %d\n\n",$#ob+1;
+
+printf OUT "static const unsigned char lvalues[%d]={\n",$lvalues+1;
+print OUT @lvalues;
+print OUT "};\n\n";
+
+printf OUT "static const ASN1_OBJECT kObjects[NUM_NID]={\n";
+foreach (@out)
+	{
+	if (length($_) > 75)
+		{
+		$out="";
+		foreach (split(/,/))
+			{
+			$t=$out.$_.",";
+			if (length($t) > 70)
+				{
+				print OUT "$out\n";
+				$t="\t$_,";
+				}
+			$out=$t;
+			}
+		chop $out;
+		print OUT "$out";
+		}
+	else
+		{ print OUT $_; }
+	}
+print  OUT "};\n\n";
+
+printf OUT "static const unsigned int kNIDsInShortNameOrder[NUM_SN]={\n";
+print  OUT @sn;
+print  OUT "};\n\n";
+
+printf OUT "static const unsigned int kNIDsInLongNameOrder[NUM_LN]={\n";
+print  OUT @ln;
+print  OUT "};\n\n";
+
+printf OUT "static const unsigned int kNIDsInOIDOrder[NUM_OBJ]={\n";
+print  OUT @ob;
+print  OUT "};\n\n";
+
+close OUT;
+
+sub der_it
+	{
+	local($v)=@_;
+	local(@a,$i,$ret,@r);
+
+	@a=split(/\s+/,$v);
+	$ret.=pack("C*",$a[0]*40+$a[1]);
+	shift @a;
+	shift @a;
+	foreach (@a)
+		{
+		@r=();
+		$t=0;
+		while ($_ >= 128)
+			{
+			$x=$_%128;
+			$_/=128;
+			push(@r,((($t++)?0x80:0)|$x));
+			}
+		push(@r,((($t++)?0x80:0)|$_));
+		$ret.=pack("C*",reverse(@r));
+		}
+	return($ret);
+	}
diff --git a/crypto/obj/obj_error.c b/crypto/obj/obj_error.c
new file mode 100644
index 0000000..c98f10f
--- /dev/null
+++ b/crypto/obj/obj_error.c
@@ -0,0 +1,26 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "obj.h"
+
+const ERR_STRING_DATA OBJ_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_OBJ, OBJ_F_OBJ_create, 0), "OBJ_create"},
+  {ERR_PACK(ERR_LIB_OBJ, OBJ_F_OBJ_dup, 0), "OBJ_dup"},
+  {ERR_PACK(ERR_LIB_OBJ, OBJ_F_OBJ_nid2obj, 0), "OBJ_nid2obj"},
+  {ERR_PACK(ERR_LIB_OBJ, OBJ_F_OBJ_txt2obj, 0), "OBJ_txt2obj"},
+  {ERR_PACK(ERR_LIB_OBJ, 0, OBJ_R_UNKNOWN_NID), "UNKNOWN_NID"},
+  {0, NULL},
+};
diff --git a/crypto/obj/obj_mac.h b/crypto/obj/obj_mac.h
new file mode 100644
index 0000000..b5ad56d
--- /dev/null
+++ b/crypto/obj/obj_mac.h
@@ -0,0 +1,4148 @@
+/* crypto/objects/obj_mac.h */
+
+/* THIS FILE IS GENERATED FROM objects.txt by objects.pl via the
+ * following command:
+ * perl objects.pl objects.txt obj_mac.num obj_mac.h
+ */
+
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#define SN_undef			"UNDEF"
+#define LN_undef			"undefined"
+#define NID_undef			0
+#define OBJ_undef			0L
+
+#define SN_itu_t		"ITU-T"
+#define LN_itu_t		"itu-t"
+#define NID_itu_t		645
+#define OBJ_itu_t		0L
+
+#define NID_ccitt		404
+#define OBJ_ccitt		OBJ_itu_t
+
+#define SN_iso		"ISO"
+#define LN_iso		"iso"
+#define NID_iso		181
+#define OBJ_iso		1L
+
+#define SN_joint_iso_itu_t		"JOINT-ISO-ITU-T"
+#define LN_joint_iso_itu_t		"joint-iso-itu-t"
+#define NID_joint_iso_itu_t		646
+#define OBJ_joint_iso_itu_t		2L
+
+#define NID_joint_iso_ccitt		393
+#define OBJ_joint_iso_ccitt		OBJ_joint_iso_itu_t
+
+#define SN_member_body		"member-body"
+#define LN_member_body		"ISO Member Body"
+#define NID_member_body		182
+#define OBJ_member_body		OBJ_iso,2L
+
+#define SN_identified_organization		"identified-organization"
+#define NID_identified_organization		676
+#define OBJ_identified_organization		OBJ_iso,3L
+
+#define SN_hmac_md5		"HMAC-MD5"
+#define LN_hmac_md5		"hmac-md5"
+#define NID_hmac_md5		780
+#define OBJ_hmac_md5		OBJ_identified_organization,6L,1L,5L,5L,8L,1L,1L
+
+#define SN_hmac_sha1		"HMAC-SHA1"
+#define LN_hmac_sha1		"hmac-sha1"
+#define NID_hmac_sha1		781
+#define OBJ_hmac_sha1		OBJ_identified_organization,6L,1L,5L,5L,8L,1L,2L
+
+#define SN_certicom_arc		"certicom-arc"
+#define NID_certicom_arc		677
+#define OBJ_certicom_arc		OBJ_identified_organization,132L
+
+#define SN_international_organizations		"international-organizations"
+#define LN_international_organizations		"International Organizations"
+#define NID_international_organizations		647
+#define OBJ_international_organizations		OBJ_joint_iso_itu_t,23L
+
+#define SN_wap		"wap"
+#define NID_wap		678
+#define OBJ_wap		OBJ_international_organizations,43L
+
+#define SN_wap_wsg		"wap-wsg"
+#define NID_wap_wsg		679
+#define OBJ_wap_wsg		OBJ_wap,1L
+
+#define SN_selected_attribute_types		"selected-attribute-types"
+#define LN_selected_attribute_types		"Selected Attribute Types"
+#define NID_selected_attribute_types		394
+#define OBJ_selected_attribute_types		OBJ_joint_iso_itu_t,5L,1L,5L
+
+#define SN_clearance		"clearance"
+#define NID_clearance		395
+#define OBJ_clearance		OBJ_selected_attribute_types,55L
+
+#define SN_ISO_US		"ISO-US"
+#define LN_ISO_US		"ISO US Member Body"
+#define NID_ISO_US		183
+#define OBJ_ISO_US		OBJ_member_body,840L
+
+#define SN_X9_57		"X9-57"
+#define LN_X9_57		"X9.57"
+#define NID_X9_57		184
+#define OBJ_X9_57		OBJ_ISO_US,10040L
+
+#define SN_X9cm		"X9cm"
+#define LN_X9cm		"X9.57 CM ?"
+#define NID_X9cm		185
+#define OBJ_X9cm		OBJ_X9_57,4L
+
+#define SN_dsa		"DSA"
+#define LN_dsa		"dsaEncryption"
+#define NID_dsa		116
+#define OBJ_dsa		OBJ_X9cm,1L
+
+#define SN_dsaWithSHA1		"DSA-SHA1"
+#define LN_dsaWithSHA1		"dsaWithSHA1"
+#define NID_dsaWithSHA1		113
+#define OBJ_dsaWithSHA1		OBJ_X9cm,3L
+
+#define SN_ansi_X9_62		"ansi-X9-62"
+#define LN_ansi_X9_62		"ANSI X9.62"
+#define NID_ansi_X9_62		405
+#define OBJ_ansi_X9_62		OBJ_ISO_US,10045L
+
+#define OBJ_X9_62_id_fieldType		OBJ_ansi_X9_62,1L
+
+#define SN_X9_62_prime_field		"prime-field"
+#define NID_X9_62_prime_field		406
+#define OBJ_X9_62_prime_field		OBJ_X9_62_id_fieldType,1L
+
+#define SN_X9_62_characteristic_two_field		"characteristic-two-field"
+#define NID_X9_62_characteristic_two_field		407
+#define OBJ_X9_62_characteristic_two_field		OBJ_X9_62_id_fieldType,2L
+
+#define SN_X9_62_id_characteristic_two_basis		"id-characteristic-two-basis"
+#define NID_X9_62_id_characteristic_two_basis		680
+#define OBJ_X9_62_id_characteristic_two_basis		OBJ_X9_62_characteristic_two_field,3L
+
+#define SN_X9_62_onBasis		"onBasis"
+#define NID_X9_62_onBasis		681
+#define OBJ_X9_62_onBasis		OBJ_X9_62_id_characteristic_two_basis,1L
+
+#define SN_X9_62_tpBasis		"tpBasis"
+#define NID_X9_62_tpBasis		682
+#define OBJ_X9_62_tpBasis		OBJ_X9_62_id_characteristic_two_basis,2L
+
+#define SN_X9_62_ppBasis		"ppBasis"
+#define NID_X9_62_ppBasis		683
+#define OBJ_X9_62_ppBasis		OBJ_X9_62_id_characteristic_two_basis,3L
+
+#define OBJ_X9_62_id_publicKeyType		OBJ_ansi_X9_62,2L
+
+#define SN_X9_62_id_ecPublicKey		"id-ecPublicKey"
+#define NID_X9_62_id_ecPublicKey		408
+#define OBJ_X9_62_id_ecPublicKey		OBJ_X9_62_id_publicKeyType,1L
+
+#define OBJ_X9_62_ellipticCurve		OBJ_ansi_X9_62,3L
+
+#define OBJ_X9_62_c_TwoCurve		OBJ_X9_62_ellipticCurve,0L
+
+#define SN_X9_62_c2pnb163v1		"c2pnb163v1"
+#define NID_X9_62_c2pnb163v1		684
+#define OBJ_X9_62_c2pnb163v1		OBJ_X9_62_c_TwoCurve,1L
+
+#define SN_X9_62_c2pnb163v2		"c2pnb163v2"
+#define NID_X9_62_c2pnb163v2		685
+#define OBJ_X9_62_c2pnb163v2		OBJ_X9_62_c_TwoCurve,2L
+
+#define SN_X9_62_c2pnb163v3		"c2pnb163v3"
+#define NID_X9_62_c2pnb163v3		686
+#define OBJ_X9_62_c2pnb163v3		OBJ_X9_62_c_TwoCurve,3L
+
+#define SN_X9_62_c2pnb176v1		"c2pnb176v1"
+#define NID_X9_62_c2pnb176v1		687
+#define OBJ_X9_62_c2pnb176v1		OBJ_X9_62_c_TwoCurve,4L
+
+#define SN_X9_62_c2tnb191v1		"c2tnb191v1"
+#define NID_X9_62_c2tnb191v1		688
+#define OBJ_X9_62_c2tnb191v1		OBJ_X9_62_c_TwoCurve,5L
+
+#define SN_X9_62_c2tnb191v2		"c2tnb191v2"
+#define NID_X9_62_c2tnb191v2		689
+#define OBJ_X9_62_c2tnb191v2		OBJ_X9_62_c_TwoCurve,6L
+
+#define SN_X9_62_c2tnb191v3		"c2tnb191v3"
+#define NID_X9_62_c2tnb191v3		690
+#define OBJ_X9_62_c2tnb191v3		OBJ_X9_62_c_TwoCurve,7L
+
+#define SN_X9_62_c2onb191v4		"c2onb191v4"
+#define NID_X9_62_c2onb191v4		691
+#define OBJ_X9_62_c2onb191v4		OBJ_X9_62_c_TwoCurve,8L
+
+#define SN_X9_62_c2onb191v5		"c2onb191v5"
+#define NID_X9_62_c2onb191v5		692
+#define OBJ_X9_62_c2onb191v5		OBJ_X9_62_c_TwoCurve,9L
+
+#define SN_X9_62_c2pnb208w1		"c2pnb208w1"
+#define NID_X9_62_c2pnb208w1		693
+#define OBJ_X9_62_c2pnb208w1		OBJ_X9_62_c_TwoCurve,10L
+
+#define SN_X9_62_c2tnb239v1		"c2tnb239v1"
+#define NID_X9_62_c2tnb239v1		694
+#define OBJ_X9_62_c2tnb239v1		OBJ_X9_62_c_TwoCurve,11L
+
+#define SN_X9_62_c2tnb239v2		"c2tnb239v2"
+#define NID_X9_62_c2tnb239v2		695
+#define OBJ_X9_62_c2tnb239v2		OBJ_X9_62_c_TwoCurve,12L
+
+#define SN_X9_62_c2tnb239v3		"c2tnb239v3"
+#define NID_X9_62_c2tnb239v3		696
+#define OBJ_X9_62_c2tnb239v3		OBJ_X9_62_c_TwoCurve,13L
+
+#define SN_X9_62_c2onb239v4		"c2onb239v4"
+#define NID_X9_62_c2onb239v4		697
+#define OBJ_X9_62_c2onb239v4		OBJ_X9_62_c_TwoCurve,14L
+
+#define SN_X9_62_c2onb239v5		"c2onb239v5"
+#define NID_X9_62_c2onb239v5		698
+#define OBJ_X9_62_c2onb239v5		OBJ_X9_62_c_TwoCurve,15L
+
+#define SN_X9_62_c2pnb272w1		"c2pnb272w1"
+#define NID_X9_62_c2pnb272w1		699
+#define OBJ_X9_62_c2pnb272w1		OBJ_X9_62_c_TwoCurve,16L
+
+#define SN_X9_62_c2pnb304w1		"c2pnb304w1"
+#define NID_X9_62_c2pnb304w1		700
+#define OBJ_X9_62_c2pnb304w1		OBJ_X9_62_c_TwoCurve,17L
+
+#define SN_X9_62_c2tnb359v1		"c2tnb359v1"
+#define NID_X9_62_c2tnb359v1		701
+#define OBJ_X9_62_c2tnb359v1		OBJ_X9_62_c_TwoCurve,18L
+
+#define SN_X9_62_c2pnb368w1		"c2pnb368w1"
+#define NID_X9_62_c2pnb368w1		702
+#define OBJ_X9_62_c2pnb368w1		OBJ_X9_62_c_TwoCurve,19L
+
+#define SN_X9_62_c2tnb431r1		"c2tnb431r1"
+#define NID_X9_62_c2tnb431r1		703
+#define OBJ_X9_62_c2tnb431r1		OBJ_X9_62_c_TwoCurve,20L
+
+#define OBJ_X9_62_primeCurve		OBJ_X9_62_ellipticCurve,1L
+
+#define SN_X9_62_prime192v1		"prime192v1"
+#define NID_X9_62_prime192v1		409
+#define OBJ_X9_62_prime192v1		OBJ_X9_62_primeCurve,1L
+
+#define SN_X9_62_prime192v2		"prime192v2"
+#define NID_X9_62_prime192v2		410
+#define OBJ_X9_62_prime192v2		OBJ_X9_62_primeCurve,2L
+
+#define SN_X9_62_prime192v3		"prime192v3"
+#define NID_X9_62_prime192v3		411
+#define OBJ_X9_62_prime192v3		OBJ_X9_62_primeCurve,3L
+
+#define SN_X9_62_prime239v1		"prime239v1"
+#define NID_X9_62_prime239v1		412
+#define OBJ_X9_62_prime239v1		OBJ_X9_62_primeCurve,4L
+
+#define SN_X9_62_prime239v2		"prime239v2"
+#define NID_X9_62_prime239v2		413
+#define OBJ_X9_62_prime239v2		OBJ_X9_62_primeCurve,5L
+
+#define SN_X9_62_prime239v3		"prime239v3"
+#define NID_X9_62_prime239v3		414
+#define OBJ_X9_62_prime239v3		OBJ_X9_62_primeCurve,6L
+
+#define SN_X9_62_prime256v1		"prime256v1"
+#define NID_X9_62_prime256v1		415
+#define OBJ_X9_62_prime256v1		OBJ_X9_62_primeCurve,7L
+
+#define OBJ_X9_62_id_ecSigType		OBJ_ansi_X9_62,4L
+
+#define SN_ecdsa_with_SHA1		"ecdsa-with-SHA1"
+#define NID_ecdsa_with_SHA1		416
+#define OBJ_ecdsa_with_SHA1		OBJ_X9_62_id_ecSigType,1L
+
+#define SN_ecdsa_with_Recommended		"ecdsa-with-Recommended"
+#define NID_ecdsa_with_Recommended		791
+#define OBJ_ecdsa_with_Recommended		OBJ_X9_62_id_ecSigType,2L
+
+#define SN_ecdsa_with_Specified		"ecdsa-with-Specified"
+#define NID_ecdsa_with_Specified		792
+#define OBJ_ecdsa_with_Specified		OBJ_X9_62_id_ecSigType,3L
+
+#define SN_ecdsa_with_SHA224		"ecdsa-with-SHA224"
+#define NID_ecdsa_with_SHA224		793
+#define OBJ_ecdsa_with_SHA224		OBJ_ecdsa_with_Specified,1L
+
+#define SN_ecdsa_with_SHA256		"ecdsa-with-SHA256"
+#define NID_ecdsa_with_SHA256		794
+#define OBJ_ecdsa_with_SHA256		OBJ_ecdsa_with_Specified,2L
+
+#define SN_ecdsa_with_SHA384		"ecdsa-with-SHA384"
+#define NID_ecdsa_with_SHA384		795
+#define OBJ_ecdsa_with_SHA384		OBJ_ecdsa_with_Specified,3L
+
+#define SN_ecdsa_with_SHA512		"ecdsa-with-SHA512"
+#define NID_ecdsa_with_SHA512		796
+#define OBJ_ecdsa_with_SHA512		OBJ_ecdsa_with_Specified,4L
+
+#define OBJ_secg_ellipticCurve		OBJ_certicom_arc,0L
+
+#define SN_secp112r1		"secp112r1"
+#define NID_secp112r1		704
+#define OBJ_secp112r1		OBJ_secg_ellipticCurve,6L
+
+#define SN_secp112r2		"secp112r2"
+#define NID_secp112r2		705
+#define OBJ_secp112r2		OBJ_secg_ellipticCurve,7L
+
+#define SN_secp128r1		"secp128r1"
+#define NID_secp128r1		706
+#define OBJ_secp128r1		OBJ_secg_ellipticCurve,28L
+
+#define SN_secp128r2		"secp128r2"
+#define NID_secp128r2		707
+#define OBJ_secp128r2		OBJ_secg_ellipticCurve,29L
+
+#define SN_secp160k1		"secp160k1"
+#define NID_secp160k1		708
+#define OBJ_secp160k1		OBJ_secg_ellipticCurve,9L
+
+#define SN_secp160r1		"secp160r1"
+#define NID_secp160r1		709
+#define OBJ_secp160r1		OBJ_secg_ellipticCurve,8L
+
+#define SN_secp160r2		"secp160r2"
+#define NID_secp160r2		710
+#define OBJ_secp160r2		OBJ_secg_ellipticCurve,30L
+
+#define SN_secp192k1		"secp192k1"
+#define NID_secp192k1		711
+#define OBJ_secp192k1		OBJ_secg_ellipticCurve,31L
+
+#define SN_secp224k1		"secp224k1"
+#define NID_secp224k1		712
+#define OBJ_secp224k1		OBJ_secg_ellipticCurve,32L
+
+#define SN_secp224r1		"secp224r1"
+#define NID_secp224r1		713
+#define OBJ_secp224r1		OBJ_secg_ellipticCurve,33L
+
+#define SN_secp256k1		"secp256k1"
+#define NID_secp256k1		714
+#define OBJ_secp256k1		OBJ_secg_ellipticCurve,10L
+
+#define SN_secp384r1		"secp384r1"
+#define NID_secp384r1		715
+#define OBJ_secp384r1		OBJ_secg_ellipticCurve,34L
+
+#define SN_secp521r1		"secp521r1"
+#define NID_secp521r1		716
+#define OBJ_secp521r1		OBJ_secg_ellipticCurve,35L
+
+#define SN_sect113r1		"sect113r1"
+#define NID_sect113r1		717
+#define OBJ_sect113r1		OBJ_secg_ellipticCurve,4L
+
+#define SN_sect113r2		"sect113r2"
+#define NID_sect113r2		718
+#define OBJ_sect113r2		OBJ_secg_ellipticCurve,5L
+
+#define SN_sect131r1		"sect131r1"
+#define NID_sect131r1		719
+#define OBJ_sect131r1		OBJ_secg_ellipticCurve,22L
+
+#define SN_sect131r2		"sect131r2"
+#define NID_sect131r2		720
+#define OBJ_sect131r2		OBJ_secg_ellipticCurve,23L
+
+#define SN_sect163k1		"sect163k1"
+#define NID_sect163k1		721
+#define OBJ_sect163k1		OBJ_secg_ellipticCurve,1L
+
+#define SN_sect163r1		"sect163r1"
+#define NID_sect163r1		722
+#define OBJ_sect163r1		OBJ_secg_ellipticCurve,2L
+
+#define SN_sect163r2		"sect163r2"
+#define NID_sect163r2		723
+#define OBJ_sect163r2		OBJ_secg_ellipticCurve,15L
+
+#define SN_sect193r1		"sect193r1"
+#define NID_sect193r1		724
+#define OBJ_sect193r1		OBJ_secg_ellipticCurve,24L
+
+#define SN_sect193r2		"sect193r2"
+#define NID_sect193r2		725
+#define OBJ_sect193r2		OBJ_secg_ellipticCurve,25L
+
+#define SN_sect233k1		"sect233k1"
+#define NID_sect233k1		726
+#define OBJ_sect233k1		OBJ_secg_ellipticCurve,26L
+
+#define SN_sect233r1		"sect233r1"
+#define NID_sect233r1		727
+#define OBJ_sect233r1		OBJ_secg_ellipticCurve,27L
+
+#define SN_sect239k1		"sect239k1"
+#define NID_sect239k1		728
+#define OBJ_sect239k1		OBJ_secg_ellipticCurve,3L
+
+#define SN_sect283k1		"sect283k1"
+#define NID_sect283k1		729
+#define OBJ_sect283k1		OBJ_secg_ellipticCurve,16L
+
+#define SN_sect283r1		"sect283r1"
+#define NID_sect283r1		730
+#define OBJ_sect283r1		OBJ_secg_ellipticCurve,17L
+
+#define SN_sect409k1		"sect409k1"
+#define NID_sect409k1		731
+#define OBJ_sect409k1		OBJ_secg_ellipticCurve,36L
+
+#define SN_sect409r1		"sect409r1"
+#define NID_sect409r1		732
+#define OBJ_sect409r1		OBJ_secg_ellipticCurve,37L
+
+#define SN_sect571k1		"sect571k1"
+#define NID_sect571k1		733
+#define OBJ_sect571k1		OBJ_secg_ellipticCurve,38L
+
+#define SN_sect571r1		"sect571r1"
+#define NID_sect571r1		734
+#define OBJ_sect571r1		OBJ_secg_ellipticCurve,39L
+
+#define OBJ_wap_wsg_idm_ecid		OBJ_wap_wsg,4L
+
+#define SN_wap_wsg_idm_ecid_wtls1		"wap-wsg-idm-ecid-wtls1"
+#define NID_wap_wsg_idm_ecid_wtls1		735
+#define OBJ_wap_wsg_idm_ecid_wtls1		OBJ_wap_wsg_idm_ecid,1L
+
+#define SN_wap_wsg_idm_ecid_wtls3		"wap-wsg-idm-ecid-wtls3"
+#define NID_wap_wsg_idm_ecid_wtls3		736
+#define OBJ_wap_wsg_idm_ecid_wtls3		OBJ_wap_wsg_idm_ecid,3L
+
+#define SN_wap_wsg_idm_ecid_wtls4		"wap-wsg-idm-ecid-wtls4"
+#define NID_wap_wsg_idm_ecid_wtls4		737
+#define OBJ_wap_wsg_idm_ecid_wtls4		OBJ_wap_wsg_idm_ecid,4L
+
+#define SN_wap_wsg_idm_ecid_wtls5		"wap-wsg-idm-ecid-wtls5"
+#define NID_wap_wsg_idm_ecid_wtls5		738
+#define OBJ_wap_wsg_idm_ecid_wtls5		OBJ_wap_wsg_idm_ecid,5L
+
+#define SN_wap_wsg_idm_ecid_wtls6		"wap-wsg-idm-ecid-wtls6"
+#define NID_wap_wsg_idm_ecid_wtls6		739
+#define OBJ_wap_wsg_idm_ecid_wtls6		OBJ_wap_wsg_idm_ecid,6L
+
+#define SN_wap_wsg_idm_ecid_wtls7		"wap-wsg-idm-ecid-wtls7"
+#define NID_wap_wsg_idm_ecid_wtls7		740
+#define OBJ_wap_wsg_idm_ecid_wtls7		OBJ_wap_wsg_idm_ecid,7L
+
+#define SN_wap_wsg_idm_ecid_wtls8		"wap-wsg-idm-ecid-wtls8"
+#define NID_wap_wsg_idm_ecid_wtls8		741
+#define OBJ_wap_wsg_idm_ecid_wtls8		OBJ_wap_wsg_idm_ecid,8L
+
+#define SN_wap_wsg_idm_ecid_wtls9		"wap-wsg-idm-ecid-wtls9"
+#define NID_wap_wsg_idm_ecid_wtls9		742
+#define OBJ_wap_wsg_idm_ecid_wtls9		OBJ_wap_wsg_idm_ecid,9L
+
+#define SN_wap_wsg_idm_ecid_wtls10		"wap-wsg-idm-ecid-wtls10"
+#define NID_wap_wsg_idm_ecid_wtls10		743
+#define OBJ_wap_wsg_idm_ecid_wtls10		OBJ_wap_wsg_idm_ecid,10L
+
+#define SN_wap_wsg_idm_ecid_wtls11		"wap-wsg-idm-ecid-wtls11"
+#define NID_wap_wsg_idm_ecid_wtls11		744
+#define OBJ_wap_wsg_idm_ecid_wtls11		OBJ_wap_wsg_idm_ecid,11L
+
+#define SN_wap_wsg_idm_ecid_wtls12		"wap-wsg-idm-ecid-wtls12"
+#define NID_wap_wsg_idm_ecid_wtls12		745
+#define OBJ_wap_wsg_idm_ecid_wtls12		OBJ_wap_wsg_idm_ecid,12L
+
+#define SN_cast5_cbc		"CAST5-CBC"
+#define LN_cast5_cbc		"cast5-cbc"
+#define NID_cast5_cbc		108
+#define OBJ_cast5_cbc		OBJ_ISO_US,113533L,7L,66L,10L
+
+#define SN_cast5_ecb		"CAST5-ECB"
+#define LN_cast5_ecb		"cast5-ecb"
+#define NID_cast5_ecb		109
+
+#define SN_cast5_cfb64		"CAST5-CFB"
+#define LN_cast5_cfb64		"cast5-cfb"
+#define NID_cast5_cfb64		110
+
+#define SN_cast5_ofb64		"CAST5-OFB"
+#define LN_cast5_ofb64		"cast5-ofb"
+#define NID_cast5_ofb64		111
+
+#define LN_pbeWithMD5AndCast5_CBC		"pbeWithMD5AndCast5CBC"
+#define NID_pbeWithMD5AndCast5_CBC		112
+#define OBJ_pbeWithMD5AndCast5_CBC		OBJ_ISO_US,113533L,7L,66L,12L
+
+#define SN_id_PasswordBasedMAC		"id-PasswordBasedMAC"
+#define LN_id_PasswordBasedMAC		"password based MAC"
+#define NID_id_PasswordBasedMAC		782
+#define OBJ_id_PasswordBasedMAC		OBJ_ISO_US,113533L,7L,66L,13L
+
+#define SN_id_DHBasedMac		"id-DHBasedMac"
+#define LN_id_DHBasedMac		"Diffie-Hellman based MAC"
+#define NID_id_DHBasedMac		783
+#define OBJ_id_DHBasedMac		OBJ_ISO_US,113533L,7L,66L,30L
+
+#define SN_rsadsi		"rsadsi"
+#define LN_rsadsi		"RSA Data Security, Inc."
+#define NID_rsadsi		1
+#define OBJ_rsadsi		OBJ_ISO_US,113549L
+
+#define SN_pkcs		"pkcs"
+#define LN_pkcs		"RSA Data Security, Inc. PKCS"
+#define NID_pkcs		2
+#define OBJ_pkcs		OBJ_rsadsi,1L
+
+#define SN_pkcs1		"pkcs1"
+#define NID_pkcs1		186
+#define OBJ_pkcs1		OBJ_pkcs,1L
+
+#define LN_rsaEncryption		"rsaEncryption"
+#define NID_rsaEncryption		6
+#define OBJ_rsaEncryption		OBJ_pkcs1,1L
+
+#define SN_md2WithRSAEncryption		"RSA-MD2"
+#define LN_md2WithRSAEncryption		"md2WithRSAEncryption"
+#define NID_md2WithRSAEncryption		7
+#define OBJ_md2WithRSAEncryption		OBJ_pkcs1,2L
+
+#define SN_md4WithRSAEncryption		"RSA-MD4"
+#define LN_md4WithRSAEncryption		"md4WithRSAEncryption"
+#define NID_md4WithRSAEncryption		396
+#define OBJ_md4WithRSAEncryption		OBJ_pkcs1,3L
+
+#define SN_md5WithRSAEncryption		"RSA-MD5"
+#define LN_md5WithRSAEncryption		"md5WithRSAEncryption"
+#define NID_md5WithRSAEncryption		8
+#define OBJ_md5WithRSAEncryption		OBJ_pkcs1,4L
+
+#define SN_sha1WithRSAEncryption		"RSA-SHA1"
+#define LN_sha1WithRSAEncryption		"sha1WithRSAEncryption"
+#define NID_sha1WithRSAEncryption		65
+#define OBJ_sha1WithRSAEncryption		OBJ_pkcs1,5L
+
+#define SN_rsaesOaep		"RSAES-OAEP"
+#define LN_rsaesOaep		"rsaesOaep"
+#define NID_rsaesOaep		919
+#define OBJ_rsaesOaep		OBJ_pkcs1,7L
+
+#define SN_mgf1		"MGF1"
+#define LN_mgf1		"mgf1"
+#define NID_mgf1		911
+#define OBJ_mgf1		OBJ_pkcs1,8L
+
+#define SN_pSpecified		"PSPECIFIED"
+#define LN_pSpecified		"pSpecified"
+#define NID_pSpecified		935
+#define OBJ_pSpecified		OBJ_pkcs1,9L
+
+#define SN_rsassaPss		"RSASSA-PSS"
+#define LN_rsassaPss		"rsassaPss"
+#define NID_rsassaPss		912
+#define OBJ_rsassaPss		OBJ_pkcs1,10L
+
+#define SN_sha256WithRSAEncryption		"RSA-SHA256"
+#define LN_sha256WithRSAEncryption		"sha256WithRSAEncryption"
+#define NID_sha256WithRSAEncryption		668
+#define OBJ_sha256WithRSAEncryption		OBJ_pkcs1,11L
+
+#define SN_sha384WithRSAEncryption		"RSA-SHA384"
+#define LN_sha384WithRSAEncryption		"sha384WithRSAEncryption"
+#define NID_sha384WithRSAEncryption		669
+#define OBJ_sha384WithRSAEncryption		OBJ_pkcs1,12L
+
+#define SN_sha512WithRSAEncryption		"RSA-SHA512"
+#define LN_sha512WithRSAEncryption		"sha512WithRSAEncryption"
+#define NID_sha512WithRSAEncryption		670
+#define OBJ_sha512WithRSAEncryption		OBJ_pkcs1,13L
+
+#define SN_sha224WithRSAEncryption		"RSA-SHA224"
+#define LN_sha224WithRSAEncryption		"sha224WithRSAEncryption"
+#define NID_sha224WithRSAEncryption		671
+#define OBJ_sha224WithRSAEncryption		OBJ_pkcs1,14L
+
+#define SN_pkcs3		"pkcs3"
+#define NID_pkcs3		27
+#define OBJ_pkcs3		OBJ_pkcs,3L
+
+#define LN_dhKeyAgreement		"dhKeyAgreement"
+#define NID_dhKeyAgreement		28
+#define OBJ_dhKeyAgreement		OBJ_pkcs3,1L
+
+#define SN_pkcs5		"pkcs5"
+#define NID_pkcs5		187
+#define OBJ_pkcs5		OBJ_pkcs,5L
+
+#define SN_pbeWithMD2AndDES_CBC		"PBE-MD2-DES"
+#define LN_pbeWithMD2AndDES_CBC		"pbeWithMD2AndDES-CBC"
+#define NID_pbeWithMD2AndDES_CBC		9
+#define OBJ_pbeWithMD2AndDES_CBC		OBJ_pkcs5,1L
+
+#define SN_pbeWithMD5AndDES_CBC		"PBE-MD5-DES"
+#define LN_pbeWithMD5AndDES_CBC		"pbeWithMD5AndDES-CBC"
+#define NID_pbeWithMD5AndDES_CBC		10
+#define OBJ_pbeWithMD5AndDES_CBC		OBJ_pkcs5,3L
+
+#define SN_pbeWithMD2AndRC2_CBC		"PBE-MD2-RC2-64"
+#define LN_pbeWithMD2AndRC2_CBC		"pbeWithMD2AndRC2-CBC"
+#define NID_pbeWithMD2AndRC2_CBC		168
+#define OBJ_pbeWithMD2AndRC2_CBC		OBJ_pkcs5,4L
+
+#define SN_pbeWithMD5AndRC2_CBC		"PBE-MD5-RC2-64"
+#define LN_pbeWithMD5AndRC2_CBC		"pbeWithMD5AndRC2-CBC"
+#define NID_pbeWithMD5AndRC2_CBC		169
+#define OBJ_pbeWithMD5AndRC2_CBC		OBJ_pkcs5,6L
+
+#define SN_pbeWithSHA1AndDES_CBC		"PBE-SHA1-DES"
+#define LN_pbeWithSHA1AndDES_CBC		"pbeWithSHA1AndDES-CBC"
+#define NID_pbeWithSHA1AndDES_CBC		170
+#define OBJ_pbeWithSHA1AndDES_CBC		OBJ_pkcs5,10L
+
+#define SN_pbeWithSHA1AndRC2_CBC		"PBE-SHA1-RC2-64"
+#define LN_pbeWithSHA1AndRC2_CBC		"pbeWithSHA1AndRC2-CBC"
+#define NID_pbeWithSHA1AndRC2_CBC		68
+#define OBJ_pbeWithSHA1AndRC2_CBC		OBJ_pkcs5,11L
+
+#define LN_id_pbkdf2		"PBKDF2"
+#define NID_id_pbkdf2		69
+#define OBJ_id_pbkdf2		OBJ_pkcs5,12L
+
+#define LN_pbes2		"PBES2"
+#define NID_pbes2		161
+#define OBJ_pbes2		OBJ_pkcs5,13L
+
+#define LN_pbmac1		"PBMAC1"
+#define NID_pbmac1		162
+#define OBJ_pbmac1		OBJ_pkcs5,14L
+
+#define SN_pkcs7		"pkcs7"
+#define NID_pkcs7		20
+#define OBJ_pkcs7		OBJ_pkcs,7L
+
+#define LN_pkcs7_data		"pkcs7-data"
+#define NID_pkcs7_data		21
+#define OBJ_pkcs7_data		OBJ_pkcs7,1L
+
+#define LN_pkcs7_signed		"pkcs7-signedData"
+#define NID_pkcs7_signed		22
+#define OBJ_pkcs7_signed		OBJ_pkcs7,2L
+
+#define LN_pkcs7_enveloped		"pkcs7-envelopedData"
+#define NID_pkcs7_enveloped		23
+#define OBJ_pkcs7_enveloped		OBJ_pkcs7,3L
+
+#define LN_pkcs7_signedAndEnveloped		"pkcs7-signedAndEnvelopedData"
+#define NID_pkcs7_signedAndEnveloped		24
+#define OBJ_pkcs7_signedAndEnveloped		OBJ_pkcs7,4L
+
+#define LN_pkcs7_digest		"pkcs7-digestData"
+#define NID_pkcs7_digest		25
+#define OBJ_pkcs7_digest		OBJ_pkcs7,5L
+
+#define LN_pkcs7_encrypted		"pkcs7-encryptedData"
+#define NID_pkcs7_encrypted		26
+#define OBJ_pkcs7_encrypted		OBJ_pkcs7,6L
+
+#define SN_pkcs9		"pkcs9"
+#define NID_pkcs9		47
+#define OBJ_pkcs9		OBJ_pkcs,9L
+
+#define LN_pkcs9_emailAddress		"emailAddress"
+#define NID_pkcs9_emailAddress		48
+#define OBJ_pkcs9_emailAddress		OBJ_pkcs9,1L
+
+#define LN_pkcs9_unstructuredName		"unstructuredName"
+#define NID_pkcs9_unstructuredName		49
+#define OBJ_pkcs9_unstructuredName		OBJ_pkcs9,2L
+
+#define LN_pkcs9_contentType		"contentType"
+#define NID_pkcs9_contentType		50
+#define OBJ_pkcs9_contentType		OBJ_pkcs9,3L
+
+#define LN_pkcs9_messageDigest		"messageDigest"
+#define NID_pkcs9_messageDigest		51
+#define OBJ_pkcs9_messageDigest		OBJ_pkcs9,4L
+
+#define LN_pkcs9_signingTime		"signingTime"
+#define NID_pkcs9_signingTime		52
+#define OBJ_pkcs9_signingTime		OBJ_pkcs9,5L
+
+#define LN_pkcs9_countersignature		"countersignature"
+#define NID_pkcs9_countersignature		53
+#define OBJ_pkcs9_countersignature		OBJ_pkcs9,6L
+
+#define LN_pkcs9_challengePassword		"challengePassword"
+#define NID_pkcs9_challengePassword		54
+#define OBJ_pkcs9_challengePassword		OBJ_pkcs9,7L
+
+#define LN_pkcs9_unstructuredAddress		"unstructuredAddress"
+#define NID_pkcs9_unstructuredAddress		55
+#define OBJ_pkcs9_unstructuredAddress		OBJ_pkcs9,8L
+
+#define LN_pkcs9_extCertAttributes		"extendedCertificateAttributes"
+#define NID_pkcs9_extCertAttributes		56
+#define OBJ_pkcs9_extCertAttributes		OBJ_pkcs9,9L
+
+#define SN_ext_req		"extReq"
+#define LN_ext_req		"Extension Request"
+#define NID_ext_req		172
+#define OBJ_ext_req		OBJ_pkcs9,14L
+
+#define SN_SMIMECapabilities		"SMIME-CAPS"
+#define LN_SMIMECapabilities		"S/MIME Capabilities"
+#define NID_SMIMECapabilities		167
+#define OBJ_SMIMECapabilities		OBJ_pkcs9,15L
+
+#define SN_SMIME		"SMIME"
+#define LN_SMIME		"S/MIME"
+#define NID_SMIME		188
+#define OBJ_SMIME		OBJ_pkcs9,16L
+
+#define SN_id_smime_mod		"id-smime-mod"
+#define NID_id_smime_mod		189
+#define OBJ_id_smime_mod		OBJ_SMIME,0L
+
+#define SN_id_smime_ct		"id-smime-ct"
+#define NID_id_smime_ct		190
+#define OBJ_id_smime_ct		OBJ_SMIME,1L
+
+#define SN_id_smime_aa		"id-smime-aa"
+#define NID_id_smime_aa		191
+#define OBJ_id_smime_aa		OBJ_SMIME,2L
+
+#define SN_id_smime_alg		"id-smime-alg"
+#define NID_id_smime_alg		192
+#define OBJ_id_smime_alg		OBJ_SMIME,3L
+
+#define SN_id_smime_cd		"id-smime-cd"
+#define NID_id_smime_cd		193
+#define OBJ_id_smime_cd		OBJ_SMIME,4L
+
+#define SN_id_smime_spq		"id-smime-spq"
+#define NID_id_smime_spq		194
+#define OBJ_id_smime_spq		OBJ_SMIME,5L
+
+#define SN_id_smime_cti		"id-smime-cti"
+#define NID_id_smime_cti		195
+#define OBJ_id_smime_cti		OBJ_SMIME,6L
+
+#define SN_id_smime_mod_cms		"id-smime-mod-cms"
+#define NID_id_smime_mod_cms		196
+#define OBJ_id_smime_mod_cms		OBJ_id_smime_mod,1L
+
+#define SN_id_smime_mod_ess		"id-smime-mod-ess"
+#define NID_id_smime_mod_ess		197
+#define OBJ_id_smime_mod_ess		OBJ_id_smime_mod,2L
+
+#define SN_id_smime_mod_oid		"id-smime-mod-oid"
+#define NID_id_smime_mod_oid		198
+#define OBJ_id_smime_mod_oid		OBJ_id_smime_mod,3L
+
+#define SN_id_smime_mod_msg_v3		"id-smime-mod-msg-v3"
+#define NID_id_smime_mod_msg_v3		199
+#define OBJ_id_smime_mod_msg_v3		OBJ_id_smime_mod,4L
+
+#define SN_id_smime_mod_ets_eSignature_88		"id-smime-mod-ets-eSignature-88"
+#define NID_id_smime_mod_ets_eSignature_88		200
+#define OBJ_id_smime_mod_ets_eSignature_88		OBJ_id_smime_mod,5L
+
+#define SN_id_smime_mod_ets_eSignature_97		"id-smime-mod-ets-eSignature-97"
+#define NID_id_smime_mod_ets_eSignature_97		201
+#define OBJ_id_smime_mod_ets_eSignature_97		OBJ_id_smime_mod,6L
+
+#define SN_id_smime_mod_ets_eSigPolicy_88		"id-smime-mod-ets-eSigPolicy-88"
+#define NID_id_smime_mod_ets_eSigPolicy_88		202
+#define OBJ_id_smime_mod_ets_eSigPolicy_88		OBJ_id_smime_mod,7L
+
+#define SN_id_smime_mod_ets_eSigPolicy_97		"id-smime-mod-ets-eSigPolicy-97"
+#define NID_id_smime_mod_ets_eSigPolicy_97		203
+#define OBJ_id_smime_mod_ets_eSigPolicy_97		OBJ_id_smime_mod,8L
+
+#define SN_id_smime_ct_receipt		"id-smime-ct-receipt"
+#define NID_id_smime_ct_receipt		204
+#define OBJ_id_smime_ct_receipt		OBJ_id_smime_ct,1L
+
+#define SN_id_smime_ct_authData		"id-smime-ct-authData"
+#define NID_id_smime_ct_authData		205
+#define OBJ_id_smime_ct_authData		OBJ_id_smime_ct,2L
+
+#define SN_id_smime_ct_publishCert		"id-smime-ct-publishCert"
+#define NID_id_smime_ct_publishCert		206
+#define OBJ_id_smime_ct_publishCert		OBJ_id_smime_ct,3L
+
+#define SN_id_smime_ct_TSTInfo		"id-smime-ct-TSTInfo"
+#define NID_id_smime_ct_TSTInfo		207
+#define OBJ_id_smime_ct_TSTInfo		OBJ_id_smime_ct,4L
+
+#define SN_id_smime_ct_TDTInfo		"id-smime-ct-TDTInfo"
+#define NID_id_smime_ct_TDTInfo		208
+#define OBJ_id_smime_ct_TDTInfo		OBJ_id_smime_ct,5L
+
+#define SN_id_smime_ct_contentInfo		"id-smime-ct-contentInfo"
+#define NID_id_smime_ct_contentInfo		209
+#define OBJ_id_smime_ct_contentInfo		OBJ_id_smime_ct,6L
+
+#define SN_id_smime_ct_DVCSRequestData		"id-smime-ct-DVCSRequestData"
+#define NID_id_smime_ct_DVCSRequestData		210
+#define OBJ_id_smime_ct_DVCSRequestData		OBJ_id_smime_ct,7L
+
+#define SN_id_smime_ct_DVCSResponseData		"id-smime-ct-DVCSResponseData"
+#define NID_id_smime_ct_DVCSResponseData		211
+#define OBJ_id_smime_ct_DVCSResponseData		OBJ_id_smime_ct,8L
+
+#define SN_id_smime_ct_compressedData		"id-smime-ct-compressedData"
+#define NID_id_smime_ct_compressedData		786
+#define OBJ_id_smime_ct_compressedData		OBJ_id_smime_ct,9L
+
+#define SN_id_ct_asciiTextWithCRLF		"id-ct-asciiTextWithCRLF"
+#define NID_id_ct_asciiTextWithCRLF		787
+#define OBJ_id_ct_asciiTextWithCRLF		OBJ_id_smime_ct,27L
+
+#define SN_id_smime_aa_receiptRequest		"id-smime-aa-receiptRequest"
+#define NID_id_smime_aa_receiptRequest		212
+#define OBJ_id_smime_aa_receiptRequest		OBJ_id_smime_aa,1L
+
+#define SN_id_smime_aa_securityLabel		"id-smime-aa-securityLabel"
+#define NID_id_smime_aa_securityLabel		213
+#define OBJ_id_smime_aa_securityLabel		OBJ_id_smime_aa,2L
+
+#define SN_id_smime_aa_mlExpandHistory		"id-smime-aa-mlExpandHistory"
+#define NID_id_smime_aa_mlExpandHistory		214
+#define OBJ_id_smime_aa_mlExpandHistory		OBJ_id_smime_aa,3L
+
+#define SN_id_smime_aa_contentHint		"id-smime-aa-contentHint"
+#define NID_id_smime_aa_contentHint		215
+#define OBJ_id_smime_aa_contentHint		OBJ_id_smime_aa,4L
+
+#define SN_id_smime_aa_msgSigDigest		"id-smime-aa-msgSigDigest"
+#define NID_id_smime_aa_msgSigDigest		216
+#define OBJ_id_smime_aa_msgSigDigest		OBJ_id_smime_aa,5L
+
+#define SN_id_smime_aa_encapContentType		"id-smime-aa-encapContentType"
+#define NID_id_smime_aa_encapContentType		217
+#define OBJ_id_smime_aa_encapContentType		OBJ_id_smime_aa,6L
+
+#define SN_id_smime_aa_contentIdentifier		"id-smime-aa-contentIdentifier"
+#define NID_id_smime_aa_contentIdentifier		218
+#define OBJ_id_smime_aa_contentIdentifier		OBJ_id_smime_aa,7L
+
+#define SN_id_smime_aa_macValue		"id-smime-aa-macValue"
+#define NID_id_smime_aa_macValue		219
+#define OBJ_id_smime_aa_macValue		OBJ_id_smime_aa,8L
+
+#define SN_id_smime_aa_equivalentLabels		"id-smime-aa-equivalentLabels"
+#define NID_id_smime_aa_equivalentLabels		220
+#define OBJ_id_smime_aa_equivalentLabels		OBJ_id_smime_aa,9L
+
+#define SN_id_smime_aa_contentReference		"id-smime-aa-contentReference"
+#define NID_id_smime_aa_contentReference		221
+#define OBJ_id_smime_aa_contentReference		OBJ_id_smime_aa,10L
+
+#define SN_id_smime_aa_encrypKeyPref		"id-smime-aa-encrypKeyPref"
+#define NID_id_smime_aa_encrypKeyPref		222
+#define OBJ_id_smime_aa_encrypKeyPref		OBJ_id_smime_aa,11L
+
+#define SN_id_smime_aa_signingCertificate		"id-smime-aa-signingCertificate"
+#define NID_id_smime_aa_signingCertificate		223
+#define OBJ_id_smime_aa_signingCertificate		OBJ_id_smime_aa,12L
+
+#define SN_id_smime_aa_smimeEncryptCerts		"id-smime-aa-smimeEncryptCerts"
+#define NID_id_smime_aa_smimeEncryptCerts		224
+#define OBJ_id_smime_aa_smimeEncryptCerts		OBJ_id_smime_aa,13L
+
+#define SN_id_smime_aa_timeStampToken		"id-smime-aa-timeStampToken"
+#define NID_id_smime_aa_timeStampToken		225
+#define OBJ_id_smime_aa_timeStampToken		OBJ_id_smime_aa,14L
+
+#define SN_id_smime_aa_ets_sigPolicyId		"id-smime-aa-ets-sigPolicyId"
+#define NID_id_smime_aa_ets_sigPolicyId		226
+#define OBJ_id_smime_aa_ets_sigPolicyId		OBJ_id_smime_aa,15L
+
+#define SN_id_smime_aa_ets_commitmentType		"id-smime-aa-ets-commitmentType"
+#define NID_id_smime_aa_ets_commitmentType		227
+#define OBJ_id_smime_aa_ets_commitmentType		OBJ_id_smime_aa,16L
+
+#define SN_id_smime_aa_ets_signerLocation		"id-smime-aa-ets-signerLocation"
+#define NID_id_smime_aa_ets_signerLocation		228
+#define OBJ_id_smime_aa_ets_signerLocation		OBJ_id_smime_aa,17L
+
+#define SN_id_smime_aa_ets_signerAttr		"id-smime-aa-ets-signerAttr"
+#define NID_id_smime_aa_ets_signerAttr		229
+#define OBJ_id_smime_aa_ets_signerAttr		OBJ_id_smime_aa,18L
+
+#define SN_id_smime_aa_ets_otherSigCert		"id-smime-aa-ets-otherSigCert"
+#define NID_id_smime_aa_ets_otherSigCert		230
+#define OBJ_id_smime_aa_ets_otherSigCert		OBJ_id_smime_aa,19L
+
+#define SN_id_smime_aa_ets_contentTimestamp		"id-smime-aa-ets-contentTimestamp"
+#define NID_id_smime_aa_ets_contentTimestamp		231
+#define OBJ_id_smime_aa_ets_contentTimestamp		OBJ_id_smime_aa,20L
+
+#define SN_id_smime_aa_ets_CertificateRefs		"id-smime-aa-ets-CertificateRefs"
+#define NID_id_smime_aa_ets_CertificateRefs		232
+#define OBJ_id_smime_aa_ets_CertificateRefs		OBJ_id_smime_aa,21L
+
+#define SN_id_smime_aa_ets_RevocationRefs		"id-smime-aa-ets-RevocationRefs"
+#define NID_id_smime_aa_ets_RevocationRefs		233
+#define OBJ_id_smime_aa_ets_RevocationRefs		OBJ_id_smime_aa,22L
+
+#define SN_id_smime_aa_ets_certValues		"id-smime-aa-ets-certValues"
+#define NID_id_smime_aa_ets_certValues		234
+#define OBJ_id_smime_aa_ets_certValues		OBJ_id_smime_aa,23L
+
+#define SN_id_smime_aa_ets_revocationValues		"id-smime-aa-ets-revocationValues"
+#define NID_id_smime_aa_ets_revocationValues		235
+#define OBJ_id_smime_aa_ets_revocationValues		OBJ_id_smime_aa,24L
+
+#define SN_id_smime_aa_ets_escTimeStamp		"id-smime-aa-ets-escTimeStamp"
+#define NID_id_smime_aa_ets_escTimeStamp		236
+#define OBJ_id_smime_aa_ets_escTimeStamp		OBJ_id_smime_aa,25L
+
+#define SN_id_smime_aa_ets_certCRLTimestamp		"id-smime-aa-ets-certCRLTimestamp"
+#define NID_id_smime_aa_ets_certCRLTimestamp		237
+#define OBJ_id_smime_aa_ets_certCRLTimestamp		OBJ_id_smime_aa,26L
+
+#define SN_id_smime_aa_ets_archiveTimeStamp		"id-smime-aa-ets-archiveTimeStamp"
+#define NID_id_smime_aa_ets_archiveTimeStamp		238
+#define OBJ_id_smime_aa_ets_archiveTimeStamp		OBJ_id_smime_aa,27L
+
+#define SN_id_smime_aa_signatureType		"id-smime-aa-signatureType"
+#define NID_id_smime_aa_signatureType		239
+#define OBJ_id_smime_aa_signatureType		OBJ_id_smime_aa,28L
+
+#define SN_id_smime_aa_dvcs_dvc		"id-smime-aa-dvcs-dvc"
+#define NID_id_smime_aa_dvcs_dvc		240
+#define OBJ_id_smime_aa_dvcs_dvc		OBJ_id_smime_aa,29L
+
+#define SN_id_smime_alg_ESDHwith3DES		"id-smime-alg-ESDHwith3DES"
+#define NID_id_smime_alg_ESDHwith3DES		241
+#define OBJ_id_smime_alg_ESDHwith3DES		OBJ_id_smime_alg,1L
+
+#define SN_id_smime_alg_ESDHwithRC2		"id-smime-alg-ESDHwithRC2"
+#define NID_id_smime_alg_ESDHwithRC2		242
+#define OBJ_id_smime_alg_ESDHwithRC2		OBJ_id_smime_alg,2L
+
+#define SN_id_smime_alg_3DESwrap		"id-smime-alg-3DESwrap"
+#define NID_id_smime_alg_3DESwrap		243
+#define OBJ_id_smime_alg_3DESwrap		OBJ_id_smime_alg,3L
+
+#define SN_id_smime_alg_RC2wrap		"id-smime-alg-RC2wrap"
+#define NID_id_smime_alg_RC2wrap		244
+#define OBJ_id_smime_alg_RC2wrap		OBJ_id_smime_alg,4L
+
+#define SN_id_smime_alg_ESDH		"id-smime-alg-ESDH"
+#define NID_id_smime_alg_ESDH		245
+#define OBJ_id_smime_alg_ESDH		OBJ_id_smime_alg,5L
+
+#define SN_id_smime_alg_CMS3DESwrap		"id-smime-alg-CMS3DESwrap"
+#define NID_id_smime_alg_CMS3DESwrap		246
+#define OBJ_id_smime_alg_CMS3DESwrap		OBJ_id_smime_alg,6L
+
+#define SN_id_smime_alg_CMSRC2wrap		"id-smime-alg-CMSRC2wrap"
+#define NID_id_smime_alg_CMSRC2wrap		247
+#define OBJ_id_smime_alg_CMSRC2wrap		OBJ_id_smime_alg,7L
+
+#define SN_id_alg_PWRI_KEK		"id-alg-PWRI-KEK"
+#define NID_id_alg_PWRI_KEK		893
+#define OBJ_id_alg_PWRI_KEK		OBJ_id_smime_alg,9L
+
+#define SN_id_smime_cd_ldap		"id-smime-cd-ldap"
+#define NID_id_smime_cd_ldap		248
+#define OBJ_id_smime_cd_ldap		OBJ_id_smime_cd,1L
+
+#define SN_id_smime_spq_ets_sqt_uri		"id-smime-spq-ets-sqt-uri"
+#define NID_id_smime_spq_ets_sqt_uri		249
+#define OBJ_id_smime_spq_ets_sqt_uri		OBJ_id_smime_spq,1L
+
+#define SN_id_smime_spq_ets_sqt_unotice		"id-smime-spq-ets-sqt-unotice"
+#define NID_id_smime_spq_ets_sqt_unotice		250
+#define OBJ_id_smime_spq_ets_sqt_unotice		OBJ_id_smime_spq,2L
+
+#define SN_id_smime_cti_ets_proofOfOrigin		"id-smime-cti-ets-proofOfOrigin"
+#define NID_id_smime_cti_ets_proofOfOrigin		251
+#define OBJ_id_smime_cti_ets_proofOfOrigin		OBJ_id_smime_cti,1L
+
+#define SN_id_smime_cti_ets_proofOfReceipt		"id-smime-cti-ets-proofOfReceipt"
+#define NID_id_smime_cti_ets_proofOfReceipt		252
+#define OBJ_id_smime_cti_ets_proofOfReceipt		OBJ_id_smime_cti,2L
+
+#define SN_id_smime_cti_ets_proofOfDelivery		"id-smime-cti-ets-proofOfDelivery"
+#define NID_id_smime_cti_ets_proofOfDelivery		253
+#define OBJ_id_smime_cti_ets_proofOfDelivery		OBJ_id_smime_cti,3L
+
+#define SN_id_smime_cti_ets_proofOfSender		"id-smime-cti-ets-proofOfSender"
+#define NID_id_smime_cti_ets_proofOfSender		254
+#define OBJ_id_smime_cti_ets_proofOfSender		OBJ_id_smime_cti,4L
+
+#define SN_id_smime_cti_ets_proofOfApproval		"id-smime-cti-ets-proofOfApproval"
+#define NID_id_smime_cti_ets_proofOfApproval		255
+#define OBJ_id_smime_cti_ets_proofOfApproval		OBJ_id_smime_cti,5L
+
+#define SN_id_smime_cti_ets_proofOfCreation		"id-smime-cti-ets-proofOfCreation"
+#define NID_id_smime_cti_ets_proofOfCreation		256
+#define OBJ_id_smime_cti_ets_proofOfCreation		OBJ_id_smime_cti,6L
+
+#define LN_friendlyName		"friendlyName"
+#define NID_friendlyName		156
+#define OBJ_friendlyName		OBJ_pkcs9,20L
+
+#define LN_localKeyID		"localKeyID"
+#define NID_localKeyID		157
+#define OBJ_localKeyID		OBJ_pkcs9,21L
+
+#define SN_ms_csp_name		"CSPName"
+#define LN_ms_csp_name		"Microsoft CSP Name"
+#define NID_ms_csp_name		417
+#define OBJ_ms_csp_name		1L,3L,6L,1L,4L,1L,311L,17L,1L
+
+#define SN_LocalKeySet		"LocalKeySet"
+#define LN_LocalKeySet		"Microsoft Local Key set"
+#define NID_LocalKeySet		856
+#define OBJ_LocalKeySet		1L,3L,6L,1L,4L,1L,311L,17L,2L
+
+#define OBJ_certTypes		OBJ_pkcs9,22L
+
+#define LN_x509Certificate		"x509Certificate"
+#define NID_x509Certificate		158
+#define OBJ_x509Certificate		OBJ_certTypes,1L
+
+#define LN_sdsiCertificate		"sdsiCertificate"
+#define NID_sdsiCertificate		159
+#define OBJ_sdsiCertificate		OBJ_certTypes,2L
+
+#define OBJ_crlTypes		OBJ_pkcs9,23L
+
+#define LN_x509Crl		"x509Crl"
+#define NID_x509Crl		160
+#define OBJ_x509Crl		OBJ_crlTypes,1L
+
+#define OBJ_pkcs12		OBJ_pkcs,12L
+
+#define OBJ_pkcs12_pbeids		OBJ_pkcs12,1L
+
+#define SN_pbe_WithSHA1And128BitRC4		"PBE-SHA1-RC4-128"
+#define LN_pbe_WithSHA1And128BitRC4		"pbeWithSHA1And128BitRC4"
+#define NID_pbe_WithSHA1And128BitRC4		144
+#define OBJ_pbe_WithSHA1And128BitRC4		OBJ_pkcs12_pbeids,1L
+
+#define SN_pbe_WithSHA1And40BitRC4		"PBE-SHA1-RC4-40"
+#define LN_pbe_WithSHA1And40BitRC4		"pbeWithSHA1And40BitRC4"
+#define NID_pbe_WithSHA1And40BitRC4		145
+#define OBJ_pbe_WithSHA1And40BitRC4		OBJ_pkcs12_pbeids,2L
+
+#define SN_pbe_WithSHA1And3_Key_TripleDES_CBC		"PBE-SHA1-3DES"
+#define LN_pbe_WithSHA1And3_Key_TripleDES_CBC		"pbeWithSHA1And3-KeyTripleDES-CBC"
+#define NID_pbe_WithSHA1And3_Key_TripleDES_CBC		146
+#define OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC		OBJ_pkcs12_pbeids,3L
+
+#define SN_pbe_WithSHA1And2_Key_TripleDES_CBC		"PBE-SHA1-2DES"
+#define LN_pbe_WithSHA1And2_Key_TripleDES_CBC		"pbeWithSHA1And2-KeyTripleDES-CBC"
+#define NID_pbe_WithSHA1And2_Key_TripleDES_CBC		147
+#define OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC		OBJ_pkcs12_pbeids,4L
+
+#define SN_pbe_WithSHA1And128BitRC2_CBC		"PBE-SHA1-RC2-128"
+#define LN_pbe_WithSHA1And128BitRC2_CBC		"pbeWithSHA1And128BitRC2-CBC"
+#define NID_pbe_WithSHA1And128BitRC2_CBC		148
+#define OBJ_pbe_WithSHA1And128BitRC2_CBC		OBJ_pkcs12_pbeids,5L
+
+#define SN_pbe_WithSHA1And40BitRC2_CBC		"PBE-SHA1-RC2-40"
+#define LN_pbe_WithSHA1And40BitRC2_CBC		"pbeWithSHA1And40BitRC2-CBC"
+#define NID_pbe_WithSHA1And40BitRC2_CBC		149
+#define OBJ_pbe_WithSHA1And40BitRC2_CBC		OBJ_pkcs12_pbeids,6L
+
+#define OBJ_pkcs12_Version1		OBJ_pkcs12,10L
+
+#define OBJ_pkcs12_BagIds		OBJ_pkcs12_Version1,1L
+
+#define LN_keyBag		"keyBag"
+#define NID_keyBag		150
+#define OBJ_keyBag		OBJ_pkcs12_BagIds,1L
+
+#define LN_pkcs8ShroudedKeyBag		"pkcs8ShroudedKeyBag"
+#define NID_pkcs8ShroudedKeyBag		151
+#define OBJ_pkcs8ShroudedKeyBag		OBJ_pkcs12_BagIds,2L
+
+#define LN_certBag		"certBag"
+#define NID_certBag		152
+#define OBJ_certBag		OBJ_pkcs12_BagIds,3L
+
+#define LN_crlBag		"crlBag"
+#define NID_crlBag		153
+#define OBJ_crlBag		OBJ_pkcs12_BagIds,4L
+
+#define LN_secretBag		"secretBag"
+#define NID_secretBag		154
+#define OBJ_secretBag		OBJ_pkcs12_BagIds,5L
+
+#define LN_safeContentsBag		"safeContentsBag"
+#define NID_safeContentsBag		155
+#define OBJ_safeContentsBag		OBJ_pkcs12_BagIds,6L
+
+#define SN_md2		"MD2"
+#define LN_md2		"md2"
+#define NID_md2		3
+#define OBJ_md2		OBJ_rsadsi,2L,2L
+
+#define SN_md4		"MD4"
+#define LN_md4		"md4"
+#define NID_md4		257
+#define OBJ_md4		OBJ_rsadsi,2L,4L
+
+#define SN_md5		"MD5"
+#define LN_md5		"md5"
+#define NID_md5		4
+#define OBJ_md5		OBJ_rsadsi,2L,5L
+
+#define SN_md5_sha1		"MD5-SHA1"
+#define LN_md5_sha1		"md5-sha1"
+#define NID_md5_sha1		114
+
+#define LN_hmacWithMD5		"hmacWithMD5"
+#define NID_hmacWithMD5		797
+#define OBJ_hmacWithMD5		OBJ_rsadsi,2L,6L
+
+#define LN_hmacWithSHA1		"hmacWithSHA1"
+#define NID_hmacWithSHA1		163
+#define OBJ_hmacWithSHA1		OBJ_rsadsi,2L,7L
+
+#define LN_hmacWithSHA224		"hmacWithSHA224"
+#define NID_hmacWithSHA224		798
+#define OBJ_hmacWithSHA224		OBJ_rsadsi,2L,8L
+
+#define LN_hmacWithSHA256		"hmacWithSHA256"
+#define NID_hmacWithSHA256		799
+#define OBJ_hmacWithSHA256		OBJ_rsadsi,2L,9L
+
+#define LN_hmacWithSHA384		"hmacWithSHA384"
+#define NID_hmacWithSHA384		800
+#define OBJ_hmacWithSHA384		OBJ_rsadsi,2L,10L
+
+#define LN_hmacWithSHA512		"hmacWithSHA512"
+#define NID_hmacWithSHA512		801
+#define OBJ_hmacWithSHA512		OBJ_rsadsi,2L,11L
+
+#define SN_rc2_cbc		"RC2-CBC"
+#define LN_rc2_cbc		"rc2-cbc"
+#define NID_rc2_cbc		37
+#define OBJ_rc2_cbc		OBJ_rsadsi,3L,2L
+
+#define SN_rc2_ecb		"RC2-ECB"
+#define LN_rc2_ecb		"rc2-ecb"
+#define NID_rc2_ecb		38
+
+#define SN_rc2_cfb64		"RC2-CFB"
+#define LN_rc2_cfb64		"rc2-cfb"
+#define NID_rc2_cfb64		39
+
+#define SN_rc2_ofb64		"RC2-OFB"
+#define LN_rc2_ofb64		"rc2-ofb"
+#define NID_rc2_ofb64		40
+
+#define SN_rc2_40_cbc		"RC2-40-CBC"
+#define LN_rc2_40_cbc		"rc2-40-cbc"
+#define NID_rc2_40_cbc		98
+
+#define SN_rc2_64_cbc		"RC2-64-CBC"
+#define LN_rc2_64_cbc		"rc2-64-cbc"
+#define NID_rc2_64_cbc		166
+
+#define SN_rc4		"RC4"
+#define LN_rc4		"rc4"
+#define NID_rc4		5
+#define OBJ_rc4		OBJ_rsadsi,3L,4L
+
+#define SN_rc4_40		"RC4-40"
+#define LN_rc4_40		"rc4-40"
+#define NID_rc4_40		97
+
+#define SN_des_ede3_cbc		"DES-EDE3-CBC"
+#define LN_des_ede3_cbc		"des-ede3-cbc"
+#define NID_des_ede3_cbc		44
+#define OBJ_des_ede3_cbc		OBJ_rsadsi,3L,7L
+
+#define SN_rc5_cbc		"RC5-CBC"
+#define LN_rc5_cbc		"rc5-cbc"
+#define NID_rc5_cbc		120
+#define OBJ_rc5_cbc		OBJ_rsadsi,3L,8L
+
+#define SN_rc5_ecb		"RC5-ECB"
+#define LN_rc5_ecb		"rc5-ecb"
+#define NID_rc5_ecb		121
+
+#define SN_rc5_cfb64		"RC5-CFB"
+#define LN_rc5_cfb64		"rc5-cfb"
+#define NID_rc5_cfb64		122
+
+#define SN_rc5_ofb64		"RC5-OFB"
+#define LN_rc5_ofb64		"rc5-ofb"
+#define NID_rc5_ofb64		123
+
+#define SN_ms_ext_req		"msExtReq"
+#define LN_ms_ext_req		"Microsoft Extension Request"
+#define NID_ms_ext_req		171
+#define OBJ_ms_ext_req		1L,3L,6L,1L,4L,1L,311L,2L,1L,14L
+
+#define SN_ms_code_ind		"msCodeInd"
+#define LN_ms_code_ind		"Microsoft Individual Code Signing"
+#define NID_ms_code_ind		134
+#define OBJ_ms_code_ind		1L,3L,6L,1L,4L,1L,311L,2L,1L,21L
+
+#define SN_ms_code_com		"msCodeCom"
+#define LN_ms_code_com		"Microsoft Commercial Code Signing"
+#define NID_ms_code_com		135
+#define OBJ_ms_code_com		1L,3L,6L,1L,4L,1L,311L,2L,1L,22L
+
+#define SN_ms_ctl_sign		"msCTLSign"
+#define LN_ms_ctl_sign		"Microsoft Trust List Signing"
+#define NID_ms_ctl_sign		136
+#define OBJ_ms_ctl_sign		1L,3L,6L,1L,4L,1L,311L,10L,3L,1L
+
+#define SN_ms_sgc		"msSGC"
+#define LN_ms_sgc		"Microsoft Server Gated Crypto"
+#define NID_ms_sgc		137
+#define OBJ_ms_sgc		1L,3L,6L,1L,4L,1L,311L,10L,3L,3L
+
+#define SN_ms_efs		"msEFS"
+#define LN_ms_efs		"Microsoft Encrypted File System"
+#define NID_ms_efs		138
+#define OBJ_ms_efs		1L,3L,6L,1L,4L,1L,311L,10L,3L,4L
+
+#define SN_ms_smartcard_login		"msSmartcardLogin"
+#define LN_ms_smartcard_login		"Microsoft Smartcardlogin"
+#define NID_ms_smartcard_login		648
+#define OBJ_ms_smartcard_login		1L,3L,6L,1L,4L,1L,311L,20L,2L,2L
+
+#define SN_ms_upn		"msUPN"
+#define LN_ms_upn		"Microsoft Universal Principal Name"
+#define NID_ms_upn		649
+#define OBJ_ms_upn		1L,3L,6L,1L,4L,1L,311L,20L,2L,3L
+
+#define SN_idea_cbc		"IDEA-CBC"
+#define LN_idea_cbc		"idea-cbc"
+#define NID_idea_cbc		34
+#define OBJ_idea_cbc		1L,3L,6L,1L,4L,1L,188L,7L,1L,1L,2L
+
+#define SN_idea_ecb		"IDEA-ECB"
+#define LN_idea_ecb		"idea-ecb"
+#define NID_idea_ecb		36
+
+#define SN_idea_cfb64		"IDEA-CFB"
+#define LN_idea_cfb64		"idea-cfb"
+#define NID_idea_cfb64		35
+
+#define SN_idea_ofb64		"IDEA-OFB"
+#define LN_idea_ofb64		"idea-ofb"
+#define NID_idea_ofb64		46
+
+#define SN_bf_cbc		"BF-CBC"
+#define LN_bf_cbc		"bf-cbc"
+#define NID_bf_cbc		91
+#define OBJ_bf_cbc		1L,3L,6L,1L,4L,1L,3029L,1L,2L
+
+#define SN_bf_ecb		"BF-ECB"
+#define LN_bf_ecb		"bf-ecb"
+#define NID_bf_ecb		92
+
+#define SN_bf_cfb64		"BF-CFB"
+#define LN_bf_cfb64		"bf-cfb"
+#define NID_bf_cfb64		93
+
+#define SN_bf_ofb64		"BF-OFB"
+#define LN_bf_ofb64		"bf-ofb"
+#define NID_bf_ofb64		94
+
+#define SN_id_pkix		"PKIX"
+#define NID_id_pkix		127
+#define OBJ_id_pkix		1L,3L,6L,1L,5L,5L,7L
+
+#define SN_id_pkix_mod		"id-pkix-mod"
+#define NID_id_pkix_mod		258
+#define OBJ_id_pkix_mod		OBJ_id_pkix,0L
+
+#define SN_id_pe		"id-pe"
+#define NID_id_pe		175
+#define OBJ_id_pe		OBJ_id_pkix,1L
+
+#define SN_id_qt		"id-qt"
+#define NID_id_qt		259
+#define OBJ_id_qt		OBJ_id_pkix,2L
+
+#define SN_id_kp		"id-kp"
+#define NID_id_kp		128
+#define OBJ_id_kp		OBJ_id_pkix,3L
+
+#define SN_id_it		"id-it"
+#define NID_id_it		260
+#define OBJ_id_it		OBJ_id_pkix,4L
+
+#define SN_id_pkip		"id-pkip"
+#define NID_id_pkip		261
+#define OBJ_id_pkip		OBJ_id_pkix,5L
+
+#define SN_id_alg		"id-alg"
+#define NID_id_alg		262
+#define OBJ_id_alg		OBJ_id_pkix,6L
+
+#define SN_id_cmc		"id-cmc"
+#define NID_id_cmc		263
+#define OBJ_id_cmc		OBJ_id_pkix,7L
+
+#define SN_id_on		"id-on"
+#define NID_id_on		264
+#define OBJ_id_on		OBJ_id_pkix,8L
+
+#define SN_id_pda		"id-pda"
+#define NID_id_pda		265
+#define OBJ_id_pda		OBJ_id_pkix,9L
+
+#define SN_id_aca		"id-aca"
+#define NID_id_aca		266
+#define OBJ_id_aca		OBJ_id_pkix,10L
+
+#define SN_id_qcs		"id-qcs"
+#define NID_id_qcs		267
+#define OBJ_id_qcs		OBJ_id_pkix,11L
+
+#define SN_id_cct		"id-cct"
+#define NID_id_cct		268
+#define OBJ_id_cct		OBJ_id_pkix,12L
+
+#define SN_id_ppl		"id-ppl"
+#define NID_id_ppl		662
+#define OBJ_id_ppl		OBJ_id_pkix,21L
+
+#define SN_id_ad		"id-ad"
+#define NID_id_ad		176
+#define OBJ_id_ad		OBJ_id_pkix,48L
+
+#define SN_id_pkix1_explicit_88		"id-pkix1-explicit-88"
+#define NID_id_pkix1_explicit_88		269
+#define OBJ_id_pkix1_explicit_88		OBJ_id_pkix_mod,1L
+
+#define SN_id_pkix1_implicit_88		"id-pkix1-implicit-88"
+#define NID_id_pkix1_implicit_88		270
+#define OBJ_id_pkix1_implicit_88		OBJ_id_pkix_mod,2L
+
+#define SN_id_pkix1_explicit_93		"id-pkix1-explicit-93"
+#define NID_id_pkix1_explicit_93		271
+#define OBJ_id_pkix1_explicit_93		OBJ_id_pkix_mod,3L
+
+#define SN_id_pkix1_implicit_93		"id-pkix1-implicit-93"
+#define NID_id_pkix1_implicit_93		272
+#define OBJ_id_pkix1_implicit_93		OBJ_id_pkix_mod,4L
+
+#define SN_id_mod_crmf		"id-mod-crmf"
+#define NID_id_mod_crmf		273
+#define OBJ_id_mod_crmf		OBJ_id_pkix_mod,5L
+
+#define SN_id_mod_cmc		"id-mod-cmc"
+#define NID_id_mod_cmc		274
+#define OBJ_id_mod_cmc		OBJ_id_pkix_mod,6L
+
+#define SN_id_mod_kea_profile_88		"id-mod-kea-profile-88"
+#define NID_id_mod_kea_profile_88		275
+#define OBJ_id_mod_kea_profile_88		OBJ_id_pkix_mod,7L
+
+#define SN_id_mod_kea_profile_93		"id-mod-kea-profile-93"
+#define NID_id_mod_kea_profile_93		276
+#define OBJ_id_mod_kea_profile_93		OBJ_id_pkix_mod,8L
+
+#define SN_id_mod_cmp		"id-mod-cmp"
+#define NID_id_mod_cmp		277
+#define OBJ_id_mod_cmp		OBJ_id_pkix_mod,9L
+
+#define SN_id_mod_qualified_cert_88		"id-mod-qualified-cert-88"
+#define NID_id_mod_qualified_cert_88		278
+#define OBJ_id_mod_qualified_cert_88		OBJ_id_pkix_mod,10L
+
+#define SN_id_mod_qualified_cert_93		"id-mod-qualified-cert-93"
+#define NID_id_mod_qualified_cert_93		279
+#define OBJ_id_mod_qualified_cert_93		OBJ_id_pkix_mod,11L
+
+#define SN_id_mod_attribute_cert		"id-mod-attribute-cert"
+#define NID_id_mod_attribute_cert		280
+#define OBJ_id_mod_attribute_cert		OBJ_id_pkix_mod,12L
+
+#define SN_id_mod_timestamp_protocol		"id-mod-timestamp-protocol"
+#define NID_id_mod_timestamp_protocol		281
+#define OBJ_id_mod_timestamp_protocol		OBJ_id_pkix_mod,13L
+
+#define SN_id_mod_ocsp		"id-mod-ocsp"
+#define NID_id_mod_ocsp		282
+#define OBJ_id_mod_ocsp		OBJ_id_pkix_mod,14L
+
+#define SN_id_mod_dvcs		"id-mod-dvcs"
+#define NID_id_mod_dvcs		283
+#define OBJ_id_mod_dvcs		OBJ_id_pkix_mod,15L
+
+#define SN_id_mod_cmp2000		"id-mod-cmp2000"
+#define NID_id_mod_cmp2000		284
+#define OBJ_id_mod_cmp2000		OBJ_id_pkix_mod,16L
+
+#define SN_info_access		"authorityInfoAccess"
+#define LN_info_access		"Authority Information Access"
+#define NID_info_access		177
+#define OBJ_info_access		OBJ_id_pe,1L
+
+#define SN_biometricInfo		"biometricInfo"
+#define LN_biometricInfo		"Biometric Info"
+#define NID_biometricInfo		285
+#define OBJ_biometricInfo		OBJ_id_pe,2L
+
+#define SN_qcStatements		"qcStatements"
+#define NID_qcStatements		286
+#define OBJ_qcStatements		OBJ_id_pe,3L
+
+#define SN_ac_auditEntity		"ac-auditEntity"
+#define NID_ac_auditEntity		287
+#define OBJ_ac_auditEntity		OBJ_id_pe,4L
+
+#define SN_ac_targeting		"ac-targeting"
+#define NID_ac_targeting		288
+#define OBJ_ac_targeting		OBJ_id_pe,5L
+
+#define SN_aaControls		"aaControls"
+#define NID_aaControls		289
+#define OBJ_aaControls		OBJ_id_pe,6L
+
+#define SN_sbgp_ipAddrBlock		"sbgp-ipAddrBlock"
+#define NID_sbgp_ipAddrBlock		290
+#define OBJ_sbgp_ipAddrBlock		OBJ_id_pe,7L
+
+#define SN_sbgp_autonomousSysNum		"sbgp-autonomousSysNum"
+#define NID_sbgp_autonomousSysNum		291
+#define OBJ_sbgp_autonomousSysNum		OBJ_id_pe,8L
+
+#define SN_sbgp_routerIdentifier		"sbgp-routerIdentifier"
+#define NID_sbgp_routerIdentifier		292
+#define OBJ_sbgp_routerIdentifier		OBJ_id_pe,9L
+
+#define SN_ac_proxying		"ac-proxying"
+#define NID_ac_proxying		397
+#define OBJ_ac_proxying		OBJ_id_pe,10L
+
+#define SN_sinfo_access		"subjectInfoAccess"
+#define LN_sinfo_access		"Subject Information Access"
+#define NID_sinfo_access		398
+#define OBJ_sinfo_access		OBJ_id_pe,11L
+
+#define SN_proxyCertInfo		"proxyCertInfo"
+#define LN_proxyCertInfo		"Proxy Certificate Information"
+#define NID_proxyCertInfo		663
+#define OBJ_proxyCertInfo		OBJ_id_pe,14L
+
+#define SN_id_qt_cps		"id-qt-cps"
+#define LN_id_qt_cps		"Policy Qualifier CPS"
+#define NID_id_qt_cps		164
+#define OBJ_id_qt_cps		OBJ_id_qt,1L
+
+#define SN_id_qt_unotice		"id-qt-unotice"
+#define LN_id_qt_unotice		"Policy Qualifier User Notice"
+#define NID_id_qt_unotice		165
+#define OBJ_id_qt_unotice		OBJ_id_qt,2L
+
+#define SN_textNotice		"textNotice"
+#define NID_textNotice		293
+#define OBJ_textNotice		OBJ_id_qt,3L
+
+#define SN_server_auth		"serverAuth"
+#define LN_server_auth		"TLS Web Server Authentication"
+#define NID_server_auth		129
+#define OBJ_server_auth		OBJ_id_kp,1L
+
+#define SN_client_auth		"clientAuth"
+#define LN_client_auth		"TLS Web Client Authentication"
+#define NID_client_auth		130
+#define OBJ_client_auth		OBJ_id_kp,2L
+
+#define SN_code_sign		"codeSigning"
+#define LN_code_sign		"Code Signing"
+#define NID_code_sign		131
+#define OBJ_code_sign		OBJ_id_kp,3L
+
+#define SN_email_protect		"emailProtection"
+#define LN_email_protect		"E-mail Protection"
+#define NID_email_protect		132
+#define OBJ_email_protect		OBJ_id_kp,4L
+
+#define SN_ipsecEndSystem		"ipsecEndSystem"
+#define LN_ipsecEndSystem		"IPSec End System"
+#define NID_ipsecEndSystem		294
+#define OBJ_ipsecEndSystem		OBJ_id_kp,5L
+
+#define SN_ipsecTunnel		"ipsecTunnel"
+#define LN_ipsecTunnel		"IPSec Tunnel"
+#define NID_ipsecTunnel		295
+#define OBJ_ipsecTunnel		OBJ_id_kp,6L
+
+#define SN_ipsecUser		"ipsecUser"
+#define LN_ipsecUser		"IPSec User"
+#define NID_ipsecUser		296
+#define OBJ_ipsecUser		OBJ_id_kp,7L
+
+#define SN_time_stamp		"timeStamping"
+#define LN_time_stamp		"Time Stamping"
+#define NID_time_stamp		133
+#define OBJ_time_stamp		OBJ_id_kp,8L
+
+#define SN_OCSP_sign		"OCSPSigning"
+#define LN_OCSP_sign		"OCSP Signing"
+#define NID_OCSP_sign		180
+#define OBJ_OCSP_sign		OBJ_id_kp,9L
+
+#define SN_dvcs		"DVCS"
+#define LN_dvcs		"dvcs"
+#define NID_dvcs		297
+#define OBJ_dvcs		OBJ_id_kp,10L
+
+#define SN_id_it_caProtEncCert		"id-it-caProtEncCert"
+#define NID_id_it_caProtEncCert		298
+#define OBJ_id_it_caProtEncCert		OBJ_id_it,1L
+
+#define SN_id_it_signKeyPairTypes		"id-it-signKeyPairTypes"
+#define NID_id_it_signKeyPairTypes		299
+#define OBJ_id_it_signKeyPairTypes		OBJ_id_it,2L
+
+#define SN_id_it_encKeyPairTypes		"id-it-encKeyPairTypes"
+#define NID_id_it_encKeyPairTypes		300
+#define OBJ_id_it_encKeyPairTypes		OBJ_id_it,3L
+
+#define SN_id_it_preferredSymmAlg		"id-it-preferredSymmAlg"
+#define NID_id_it_preferredSymmAlg		301
+#define OBJ_id_it_preferredSymmAlg		OBJ_id_it,4L
+
+#define SN_id_it_caKeyUpdateInfo		"id-it-caKeyUpdateInfo"
+#define NID_id_it_caKeyUpdateInfo		302
+#define OBJ_id_it_caKeyUpdateInfo		OBJ_id_it,5L
+
+#define SN_id_it_currentCRL		"id-it-currentCRL"
+#define NID_id_it_currentCRL		303
+#define OBJ_id_it_currentCRL		OBJ_id_it,6L
+
+#define SN_id_it_unsupportedOIDs		"id-it-unsupportedOIDs"
+#define NID_id_it_unsupportedOIDs		304
+#define OBJ_id_it_unsupportedOIDs		OBJ_id_it,7L
+
+#define SN_id_it_subscriptionRequest		"id-it-subscriptionRequest"
+#define NID_id_it_subscriptionRequest		305
+#define OBJ_id_it_subscriptionRequest		OBJ_id_it,8L
+
+#define SN_id_it_subscriptionResponse		"id-it-subscriptionResponse"
+#define NID_id_it_subscriptionResponse		306
+#define OBJ_id_it_subscriptionResponse		OBJ_id_it,9L
+
+#define SN_id_it_keyPairParamReq		"id-it-keyPairParamReq"
+#define NID_id_it_keyPairParamReq		307
+#define OBJ_id_it_keyPairParamReq		OBJ_id_it,10L
+
+#define SN_id_it_keyPairParamRep		"id-it-keyPairParamRep"
+#define NID_id_it_keyPairParamRep		308
+#define OBJ_id_it_keyPairParamRep		OBJ_id_it,11L
+
+#define SN_id_it_revPassphrase		"id-it-revPassphrase"
+#define NID_id_it_revPassphrase		309
+#define OBJ_id_it_revPassphrase		OBJ_id_it,12L
+
+#define SN_id_it_implicitConfirm		"id-it-implicitConfirm"
+#define NID_id_it_implicitConfirm		310
+#define OBJ_id_it_implicitConfirm		OBJ_id_it,13L
+
+#define SN_id_it_confirmWaitTime		"id-it-confirmWaitTime"
+#define NID_id_it_confirmWaitTime		311
+#define OBJ_id_it_confirmWaitTime		OBJ_id_it,14L
+
+#define SN_id_it_origPKIMessage		"id-it-origPKIMessage"
+#define NID_id_it_origPKIMessage		312
+#define OBJ_id_it_origPKIMessage		OBJ_id_it,15L
+
+#define SN_id_it_suppLangTags		"id-it-suppLangTags"
+#define NID_id_it_suppLangTags		784
+#define OBJ_id_it_suppLangTags		OBJ_id_it,16L
+
+#define SN_id_regCtrl		"id-regCtrl"
+#define NID_id_regCtrl		313
+#define OBJ_id_regCtrl		OBJ_id_pkip,1L
+
+#define SN_id_regInfo		"id-regInfo"
+#define NID_id_regInfo		314
+#define OBJ_id_regInfo		OBJ_id_pkip,2L
+
+#define SN_id_regCtrl_regToken		"id-regCtrl-regToken"
+#define NID_id_regCtrl_regToken		315
+#define OBJ_id_regCtrl_regToken		OBJ_id_regCtrl,1L
+
+#define SN_id_regCtrl_authenticator		"id-regCtrl-authenticator"
+#define NID_id_regCtrl_authenticator		316
+#define OBJ_id_regCtrl_authenticator		OBJ_id_regCtrl,2L
+
+#define SN_id_regCtrl_pkiPublicationInfo		"id-regCtrl-pkiPublicationInfo"
+#define NID_id_regCtrl_pkiPublicationInfo		317
+#define OBJ_id_regCtrl_pkiPublicationInfo		OBJ_id_regCtrl,3L
+
+#define SN_id_regCtrl_pkiArchiveOptions		"id-regCtrl-pkiArchiveOptions"
+#define NID_id_regCtrl_pkiArchiveOptions		318
+#define OBJ_id_regCtrl_pkiArchiveOptions		OBJ_id_regCtrl,4L
+
+#define SN_id_regCtrl_oldCertID		"id-regCtrl-oldCertID"
+#define NID_id_regCtrl_oldCertID		319
+#define OBJ_id_regCtrl_oldCertID		OBJ_id_regCtrl,5L
+
+#define SN_id_regCtrl_protocolEncrKey		"id-regCtrl-protocolEncrKey"
+#define NID_id_regCtrl_protocolEncrKey		320
+#define OBJ_id_regCtrl_protocolEncrKey		OBJ_id_regCtrl,6L
+
+#define SN_id_regInfo_utf8Pairs		"id-regInfo-utf8Pairs"
+#define NID_id_regInfo_utf8Pairs		321
+#define OBJ_id_regInfo_utf8Pairs		OBJ_id_regInfo,1L
+
+#define SN_id_regInfo_certReq		"id-regInfo-certReq"
+#define NID_id_regInfo_certReq		322
+#define OBJ_id_regInfo_certReq		OBJ_id_regInfo,2L
+
+#define SN_id_alg_des40		"id-alg-des40"
+#define NID_id_alg_des40		323
+#define OBJ_id_alg_des40		OBJ_id_alg,1L
+
+#define SN_id_alg_noSignature		"id-alg-noSignature"
+#define NID_id_alg_noSignature		324
+#define OBJ_id_alg_noSignature		OBJ_id_alg,2L
+
+#define SN_id_alg_dh_sig_hmac_sha1		"id-alg-dh-sig-hmac-sha1"
+#define NID_id_alg_dh_sig_hmac_sha1		325
+#define OBJ_id_alg_dh_sig_hmac_sha1		OBJ_id_alg,3L
+
+#define SN_id_alg_dh_pop		"id-alg-dh-pop"
+#define NID_id_alg_dh_pop		326
+#define OBJ_id_alg_dh_pop		OBJ_id_alg,4L
+
+#define SN_id_cmc_statusInfo		"id-cmc-statusInfo"
+#define NID_id_cmc_statusInfo		327
+#define OBJ_id_cmc_statusInfo		OBJ_id_cmc,1L
+
+#define SN_id_cmc_identification		"id-cmc-identification"
+#define NID_id_cmc_identification		328
+#define OBJ_id_cmc_identification		OBJ_id_cmc,2L
+
+#define SN_id_cmc_identityProof		"id-cmc-identityProof"
+#define NID_id_cmc_identityProof		329
+#define OBJ_id_cmc_identityProof		OBJ_id_cmc,3L
+
+#define SN_id_cmc_dataReturn		"id-cmc-dataReturn"
+#define NID_id_cmc_dataReturn		330
+#define OBJ_id_cmc_dataReturn		OBJ_id_cmc,4L
+
+#define SN_id_cmc_transactionId		"id-cmc-transactionId"
+#define NID_id_cmc_transactionId		331
+#define OBJ_id_cmc_transactionId		OBJ_id_cmc,5L
+
+#define SN_id_cmc_senderNonce		"id-cmc-senderNonce"
+#define NID_id_cmc_senderNonce		332
+#define OBJ_id_cmc_senderNonce		OBJ_id_cmc,6L
+
+#define SN_id_cmc_recipientNonce		"id-cmc-recipientNonce"
+#define NID_id_cmc_recipientNonce		333
+#define OBJ_id_cmc_recipientNonce		OBJ_id_cmc,7L
+
+#define SN_id_cmc_addExtensions		"id-cmc-addExtensions"
+#define NID_id_cmc_addExtensions		334
+#define OBJ_id_cmc_addExtensions		OBJ_id_cmc,8L
+
+#define SN_id_cmc_encryptedPOP		"id-cmc-encryptedPOP"
+#define NID_id_cmc_encryptedPOP		335
+#define OBJ_id_cmc_encryptedPOP		OBJ_id_cmc,9L
+
+#define SN_id_cmc_decryptedPOP		"id-cmc-decryptedPOP"
+#define NID_id_cmc_decryptedPOP		336
+#define OBJ_id_cmc_decryptedPOP		OBJ_id_cmc,10L
+
+#define SN_id_cmc_lraPOPWitness		"id-cmc-lraPOPWitness"
+#define NID_id_cmc_lraPOPWitness		337
+#define OBJ_id_cmc_lraPOPWitness		OBJ_id_cmc,11L
+
+#define SN_id_cmc_getCert		"id-cmc-getCert"
+#define NID_id_cmc_getCert		338
+#define OBJ_id_cmc_getCert		OBJ_id_cmc,15L
+
+#define SN_id_cmc_getCRL		"id-cmc-getCRL"
+#define NID_id_cmc_getCRL		339
+#define OBJ_id_cmc_getCRL		OBJ_id_cmc,16L
+
+#define SN_id_cmc_revokeRequest		"id-cmc-revokeRequest"
+#define NID_id_cmc_revokeRequest		340
+#define OBJ_id_cmc_revokeRequest		OBJ_id_cmc,17L
+
+#define SN_id_cmc_regInfo		"id-cmc-regInfo"
+#define NID_id_cmc_regInfo		341
+#define OBJ_id_cmc_regInfo		OBJ_id_cmc,18L
+
+#define SN_id_cmc_responseInfo		"id-cmc-responseInfo"
+#define NID_id_cmc_responseInfo		342
+#define OBJ_id_cmc_responseInfo		OBJ_id_cmc,19L
+
+#define SN_id_cmc_queryPending		"id-cmc-queryPending"
+#define NID_id_cmc_queryPending		343
+#define OBJ_id_cmc_queryPending		OBJ_id_cmc,21L
+
+#define SN_id_cmc_popLinkRandom		"id-cmc-popLinkRandom"
+#define NID_id_cmc_popLinkRandom		344
+#define OBJ_id_cmc_popLinkRandom		OBJ_id_cmc,22L
+
+#define SN_id_cmc_popLinkWitness		"id-cmc-popLinkWitness"
+#define NID_id_cmc_popLinkWitness		345
+#define OBJ_id_cmc_popLinkWitness		OBJ_id_cmc,23L
+
+#define SN_id_cmc_confirmCertAcceptance		"id-cmc-confirmCertAcceptance"
+#define NID_id_cmc_confirmCertAcceptance		346
+#define OBJ_id_cmc_confirmCertAcceptance		OBJ_id_cmc,24L
+
+#define SN_id_on_personalData		"id-on-personalData"
+#define NID_id_on_personalData		347
+#define OBJ_id_on_personalData		OBJ_id_on,1L
+
+#define SN_id_on_permanentIdentifier		"id-on-permanentIdentifier"
+#define LN_id_on_permanentIdentifier		"Permanent Identifier"
+#define NID_id_on_permanentIdentifier		858
+#define OBJ_id_on_permanentIdentifier		OBJ_id_on,3L
+
+#define SN_id_pda_dateOfBirth		"id-pda-dateOfBirth"
+#define NID_id_pda_dateOfBirth		348
+#define OBJ_id_pda_dateOfBirth		OBJ_id_pda,1L
+
+#define SN_id_pda_placeOfBirth		"id-pda-placeOfBirth"
+#define NID_id_pda_placeOfBirth		349
+#define OBJ_id_pda_placeOfBirth		OBJ_id_pda,2L
+
+#define SN_id_pda_gender		"id-pda-gender"
+#define NID_id_pda_gender		351
+#define OBJ_id_pda_gender		OBJ_id_pda,3L
+
+#define SN_id_pda_countryOfCitizenship		"id-pda-countryOfCitizenship"
+#define NID_id_pda_countryOfCitizenship		352
+#define OBJ_id_pda_countryOfCitizenship		OBJ_id_pda,4L
+
+#define SN_id_pda_countryOfResidence		"id-pda-countryOfResidence"
+#define NID_id_pda_countryOfResidence		353
+#define OBJ_id_pda_countryOfResidence		OBJ_id_pda,5L
+
+#define SN_id_aca_authenticationInfo		"id-aca-authenticationInfo"
+#define NID_id_aca_authenticationInfo		354
+#define OBJ_id_aca_authenticationInfo		OBJ_id_aca,1L
+
+#define SN_id_aca_accessIdentity		"id-aca-accessIdentity"
+#define NID_id_aca_accessIdentity		355
+#define OBJ_id_aca_accessIdentity		OBJ_id_aca,2L
+
+#define SN_id_aca_chargingIdentity		"id-aca-chargingIdentity"
+#define NID_id_aca_chargingIdentity		356
+#define OBJ_id_aca_chargingIdentity		OBJ_id_aca,3L
+
+#define SN_id_aca_group		"id-aca-group"
+#define NID_id_aca_group		357
+#define OBJ_id_aca_group		OBJ_id_aca,4L
+
+#define SN_id_aca_role		"id-aca-role"
+#define NID_id_aca_role		358
+#define OBJ_id_aca_role		OBJ_id_aca,5L
+
+#define SN_id_aca_encAttrs		"id-aca-encAttrs"
+#define NID_id_aca_encAttrs		399
+#define OBJ_id_aca_encAttrs		OBJ_id_aca,6L
+
+#define SN_id_qcs_pkixQCSyntax_v1		"id-qcs-pkixQCSyntax-v1"
+#define NID_id_qcs_pkixQCSyntax_v1		359
+#define OBJ_id_qcs_pkixQCSyntax_v1		OBJ_id_qcs,1L
+
+#define SN_id_cct_crs		"id-cct-crs"
+#define NID_id_cct_crs		360
+#define OBJ_id_cct_crs		OBJ_id_cct,1L
+
+#define SN_id_cct_PKIData		"id-cct-PKIData"
+#define NID_id_cct_PKIData		361
+#define OBJ_id_cct_PKIData		OBJ_id_cct,2L
+
+#define SN_id_cct_PKIResponse		"id-cct-PKIResponse"
+#define NID_id_cct_PKIResponse		362
+#define OBJ_id_cct_PKIResponse		OBJ_id_cct,3L
+
+#define SN_id_ppl_anyLanguage		"id-ppl-anyLanguage"
+#define LN_id_ppl_anyLanguage		"Any language"
+#define NID_id_ppl_anyLanguage		664
+#define OBJ_id_ppl_anyLanguage		OBJ_id_ppl,0L
+
+#define SN_id_ppl_inheritAll		"id-ppl-inheritAll"
+#define LN_id_ppl_inheritAll		"Inherit all"
+#define NID_id_ppl_inheritAll		665
+#define OBJ_id_ppl_inheritAll		OBJ_id_ppl,1L
+
+#define SN_Independent		"id-ppl-independent"
+#define LN_Independent		"Independent"
+#define NID_Independent		667
+#define OBJ_Independent		OBJ_id_ppl,2L
+
+#define SN_ad_OCSP		"OCSP"
+#define LN_ad_OCSP		"OCSP"
+#define NID_ad_OCSP		178
+#define OBJ_ad_OCSP		OBJ_id_ad,1L
+
+#define SN_ad_ca_issuers		"caIssuers"
+#define LN_ad_ca_issuers		"CA Issuers"
+#define NID_ad_ca_issuers		179
+#define OBJ_ad_ca_issuers		OBJ_id_ad,2L
+
+#define SN_ad_timeStamping		"ad_timestamping"
+#define LN_ad_timeStamping		"AD Time Stamping"
+#define NID_ad_timeStamping		363
+#define OBJ_ad_timeStamping		OBJ_id_ad,3L
+
+#define SN_ad_dvcs		"AD_DVCS"
+#define LN_ad_dvcs		"ad dvcs"
+#define NID_ad_dvcs		364
+#define OBJ_ad_dvcs		OBJ_id_ad,4L
+
+#define SN_caRepository		"caRepository"
+#define LN_caRepository		"CA Repository"
+#define NID_caRepository		785
+#define OBJ_caRepository		OBJ_id_ad,5L
+
+#define OBJ_id_pkix_OCSP		OBJ_ad_OCSP
+
+#define SN_id_pkix_OCSP_basic		"basicOCSPResponse"
+#define LN_id_pkix_OCSP_basic		"Basic OCSP Response"
+#define NID_id_pkix_OCSP_basic		365
+#define OBJ_id_pkix_OCSP_basic		OBJ_id_pkix_OCSP,1L
+
+#define SN_id_pkix_OCSP_Nonce		"Nonce"
+#define LN_id_pkix_OCSP_Nonce		"OCSP Nonce"
+#define NID_id_pkix_OCSP_Nonce		366
+#define OBJ_id_pkix_OCSP_Nonce		OBJ_id_pkix_OCSP,2L
+
+#define SN_id_pkix_OCSP_CrlID		"CrlID"
+#define LN_id_pkix_OCSP_CrlID		"OCSP CRL ID"
+#define NID_id_pkix_OCSP_CrlID		367
+#define OBJ_id_pkix_OCSP_CrlID		OBJ_id_pkix_OCSP,3L
+
+#define SN_id_pkix_OCSP_acceptableResponses		"acceptableResponses"
+#define LN_id_pkix_OCSP_acceptableResponses		"Acceptable OCSP Responses"
+#define NID_id_pkix_OCSP_acceptableResponses		368
+#define OBJ_id_pkix_OCSP_acceptableResponses		OBJ_id_pkix_OCSP,4L
+
+#define SN_id_pkix_OCSP_noCheck		"noCheck"
+#define LN_id_pkix_OCSP_noCheck		"OCSP No Check"
+#define NID_id_pkix_OCSP_noCheck		369
+#define OBJ_id_pkix_OCSP_noCheck		OBJ_id_pkix_OCSP,5L
+
+#define SN_id_pkix_OCSP_archiveCutoff		"archiveCutoff"
+#define LN_id_pkix_OCSP_archiveCutoff		"OCSP Archive Cutoff"
+#define NID_id_pkix_OCSP_archiveCutoff		370
+#define OBJ_id_pkix_OCSP_archiveCutoff		OBJ_id_pkix_OCSP,6L
+
+#define SN_id_pkix_OCSP_serviceLocator		"serviceLocator"
+#define LN_id_pkix_OCSP_serviceLocator		"OCSP Service Locator"
+#define NID_id_pkix_OCSP_serviceLocator		371
+#define OBJ_id_pkix_OCSP_serviceLocator		OBJ_id_pkix_OCSP,7L
+
+#define SN_id_pkix_OCSP_extendedStatus		"extendedStatus"
+#define LN_id_pkix_OCSP_extendedStatus		"Extended OCSP Status"
+#define NID_id_pkix_OCSP_extendedStatus		372
+#define OBJ_id_pkix_OCSP_extendedStatus		OBJ_id_pkix_OCSP,8L
+
+#define SN_id_pkix_OCSP_valid		"valid"
+#define NID_id_pkix_OCSP_valid		373
+#define OBJ_id_pkix_OCSP_valid		OBJ_id_pkix_OCSP,9L
+
+#define SN_id_pkix_OCSP_path		"path"
+#define NID_id_pkix_OCSP_path		374
+#define OBJ_id_pkix_OCSP_path		OBJ_id_pkix_OCSP,10L
+
+#define SN_id_pkix_OCSP_trustRoot		"trustRoot"
+#define LN_id_pkix_OCSP_trustRoot		"Trust Root"
+#define NID_id_pkix_OCSP_trustRoot		375
+#define OBJ_id_pkix_OCSP_trustRoot		OBJ_id_pkix_OCSP,11L
+
+#define SN_algorithm		"algorithm"
+#define LN_algorithm		"algorithm"
+#define NID_algorithm		376
+#define OBJ_algorithm		1L,3L,14L,3L,2L
+
+#define SN_md5WithRSA		"RSA-NP-MD5"
+#define LN_md5WithRSA		"md5WithRSA"
+#define NID_md5WithRSA		104
+#define OBJ_md5WithRSA		OBJ_algorithm,3L
+
+#define SN_des_ecb		"DES-ECB"
+#define LN_des_ecb		"des-ecb"
+#define NID_des_ecb		29
+#define OBJ_des_ecb		OBJ_algorithm,6L
+
+#define SN_des_cbc		"DES-CBC"
+#define LN_des_cbc		"des-cbc"
+#define NID_des_cbc		31
+#define OBJ_des_cbc		OBJ_algorithm,7L
+
+#define SN_des_ofb64		"DES-OFB"
+#define LN_des_ofb64		"des-ofb"
+#define NID_des_ofb64		45
+#define OBJ_des_ofb64		OBJ_algorithm,8L
+
+#define SN_des_cfb64		"DES-CFB"
+#define LN_des_cfb64		"des-cfb"
+#define NID_des_cfb64		30
+#define OBJ_des_cfb64		OBJ_algorithm,9L
+
+#define SN_rsaSignature		"rsaSignature"
+#define NID_rsaSignature		377
+#define OBJ_rsaSignature		OBJ_algorithm,11L
+
+#define SN_dsa_2		"DSA-old"
+#define LN_dsa_2		"dsaEncryption-old"
+#define NID_dsa_2		67
+#define OBJ_dsa_2		OBJ_algorithm,12L
+
+#define SN_dsaWithSHA		"DSA-SHA"
+#define LN_dsaWithSHA		"dsaWithSHA"
+#define NID_dsaWithSHA		66
+#define OBJ_dsaWithSHA		OBJ_algorithm,13L
+
+#define SN_shaWithRSAEncryption		"RSA-SHA"
+#define LN_shaWithRSAEncryption		"shaWithRSAEncryption"
+#define NID_shaWithRSAEncryption		42
+#define OBJ_shaWithRSAEncryption		OBJ_algorithm,15L
+
+#define SN_des_ede_ecb		"DES-EDE"
+#define LN_des_ede_ecb		"des-ede"
+#define NID_des_ede_ecb		32
+#define OBJ_des_ede_ecb		OBJ_algorithm,17L
+
+#define SN_des_ede3_ecb		"DES-EDE3"
+#define LN_des_ede3_ecb		"des-ede3"
+#define NID_des_ede3_ecb		33
+
+#define SN_des_ede_cbc		"DES-EDE-CBC"
+#define LN_des_ede_cbc		"des-ede-cbc"
+#define NID_des_ede_cbc		43
+
+#define SN_des_ede_cfb64		"DES-EDE-CFB"
+#define LN_des_ede_cfb64		"des-ede-cfb"
+#define NID_des_ede_cfb64		60
+
+#define SN_des_ede3_cfb64		"DES-EDE3-CFB"
+#define LN_des_ede3_cfb64		"des-ede3-cfb"
+#define NID_des_ede3_cfb64		61
+
+#define SN_des_ede_ofb64		"DES-EDE-OFB"
+#define LN_des_ede_ofb64		"des-ede-ofb"
+#define NID_des_ede_ofb64		62
+
+#define SN_des_ede3_ofb64		"DES-EDE3-OFB"
+#define LN_des_ede3_ofb64		"des-ede3-ofb"
+#define NID_des_ede3_ofb64		63
+
+#define SN_desx_cbc		"DESX-CBC"
+#define LN_desx_cbc		"desx-cbc"
+#define NID_desx_cbc		80
+
+#define SN_sha		"SHA"
+#define LN_sha		"sha"
+#define NID_sha		41
+#define OBJ_sha		OBJ_algorithm,18L
+
+#define SN_sha1		"SHA1"
+#define LN_sha1		"sha1"
+#define NID_sha1		64
+#define OBJ_sha1		OBJ_algorithm,26L
+
+#define SN_dsaWithSHA1_2		"DSA-SHA1-old"
+#define LN_dsaWithSHA1_2		"dsaWithSHA1-old"
+#define NID_dsaWithSHA1_2		70
+#define OBJ_dsaWithSHA1_2		OBJ_algorithm,27L
+
+#define SN_sha1WithRSA		"RSA-SHA1-2"
+#define LN_sha1WithRSA		"sha1WithRSA"
+#define NID_sha1WithRSA		115
+#define OBJ_sha1WithRSA		OBJ_algorithm,29L
+
+#define SN_ripemd160		"RIPEMD160"
+#define LN_ripemd160		"ripemd160"
+#define NID_ripemd160		117
+#define OBJ_ripemd160		1L,3L,36L,3L,2L,1L
+
+#define SN_ripemd160WithRSA		"RSA-RIPEMD160"
+#define LN_ripemd160WithRSA		"ripemd160WithRSA"
+#define NID_ripemd160WithRSA		119
+#define OBJ_ripemd160WithRSA		1L,3L,36L,3L,3L,1L,2L
+
+#define SN_sxnet		"SXNetID"
+#define LN_sxnet		"Strong Extranet ID"
+#define NID_sxnet		143
+#define OBJ_sxnet		1L,3L,101L,1L,4L,1L
+
+#define SN_X500		"X500"
+#define LN_X500		"directory services (X.500)"
+#define NID_X500		11
+#define OBJ_X500		2L,5L
+
+#define SN_X509		"X509"
+#define NID_X509		12
+#define OBJ_X509		OBJ_X500,4L
+
+#define SN_commonName		"CN"
+#define LN_commonName		"commonName"
+#define NID_commonName		13
+#define OBJ_commonName		OBJ_X509,3L
+
+#define SN_surname		"SN"
+#define LN_surname		"surname"
+#define NID_surname		100
+#define OBJ_surname		OBJ_X509,4L
+
+#define LN_serialNumber		"serialNumber"
+#define NID_serialNumber		105
+#define OBJ_serialNumber		OBJ_X509,5L
+
+#define SN_countryName		"C"
+#define LN_countryName		"countryName"
+#define NID_countryName		14
+#define OBJ_countryName		OBJ_X509,6L
+
+#define SN_localityName		"L"
+#define LN_localityName		"localityName"
+#define NID_localityName		15
+#define OBJ_localityName		OBJ_X509,7L
+
+#define SN_stateOrProvinceName		"ST"
+#define LN_stateOrProvinceName		"stateOrProvinceName"
+#define NID_stateOrProvinceName		16
+#define OBJ_stateOrProvinceName		OBJ_X509,8L
+
+#define SN_streetAddress		"street"
+#define LN_streetAddress		"streetAddress"
+#define NID_streetAddress		660
+#define OBJ_streetAddress		OBJ_X509,9L
+
+#define SN_organizationName		"O"
+#define LN_organizationName		"organizationName"
+#define NID_organizationName		17
+#define OBJ_organizationName		OBJ_X509,10L
+
+#define SN_organizationalUnitName		"OU"
+#define LN_organizationalUnitName		"organizationalUnitName"
+#define NID_organizationalUnitName		18
+#define OBJ_organizationalUnitName		OBJ_X509,11L
+
+#define SN_title		"title"
+#define LN_title		"title"
+#define NID_title		106
+#define OBJ_title		OBJ_X509,12L
+
+#define LN_description		"description"
+#define NID_description		107
+#define OBJ_description		OBJ_X509,13L
+
+#define LN_searchGuide		"searchGuide"
+#define NID_searchGuide		859
+#define OBJ_searchGuide		OBJ_X509,14L
+
+#define LN_businessCategory		"businessCategory"
+#define NID_businessCategory		860
+#define OBJ_businessCategory		OBJ_X509,15L
+
+#define LN_postalAddress		"postalAddress"
+#define NID_postalAddress		861
+#define OBJ_postalAddress		OBJ_X509,16L
+
+#define LN_postalCode		"postalCode"
+#define NID_postalCode		661
+#define OBJ_postalCode		OBJ_X509,17L
+
+#define LN_postOfficeBox		"postOfficeBox"
+#define NID_postOfficeBox		862
+#define OBJ_postOfficeBox		OBJ_X509,18L
+
+#define LN_physicalDeliveryOfficeName		"physicalDeliveryOfficeName"
+#define NID_physicalDeliveryOfficeName		863
+#define OBJ_physicalDeliveryOfficeName		OBJ_X509,19L
+
+#define LN_telephoneNumber		"telephoneNumber"
+#define NID_telephoneNumber		864
+#define OBJ_telephoneNumber		OBJ_X509,20L
+
+#define LN_telexNumber		"telexNumber"
+#define NID_telexNumber		865
+#define OBJ_telexNumber		OBJ_X509,21L
+
+#define LN_teletexTerminalIdentifier		"teletexTerminalIdentifier"
+#define NID_teletexTerminalIdentifier		866
+#define OBJ_teletexTerminalIdentifier		OBJ_X509,22L
+
+#define LN_facsimileTelephoneNumber		"facsimileTelephoneNumber"
+#define NID_facsimileTelephoneNumber		867
+#define OBJ_facsimileTelephoneNumber		OBJ_X509,23L
+
+#define LN_x121Address		"x121Address"
+#define NID_x121Address		868
+#define OBJ_x121Address		OBJ_X509,24L
+
+#define LN_internationaliSDNNumber		"internationaliSDNNumber"
+#define NID_internationaliSDNNumber		869
+#define OBJ_internationaliSDNNumber		OBJ_X509,25L
+
+#define LN_registeredAddress		"registeredAddress"
+#define NID_registeredAddress		870
+#define OBJ_registeredAddress		OBJ_X509,26L
+
+#define LN_destinationIndicator		"destinationIndicator"
+#define NID_destinationIndicator		871
+#define OBJ_destinationIndicator		OBJ_X509,27L
+
+#define LN_preferredDeliveryMethod		"preferredDeliveryMethod"
+#define NID_preferredDeliveryMethod		872
+#define OBJ_preferredDeliveryMethod		OBJ_X509,28L
+
+#define LN_presentationAddress		"presentationAddress"
+#define NID_presentationAddress		873
+#define OBJ_presentationAddress		OBJ_X509,29L
+
+#define LN_supportedApplicationContext		"supportedApplicationContext"
+#define NID_supportedApplicationContext		874
+#define OBJ_supportedApplicationContext		OBJ_X509,30L
+
+#define SN_member		"member"
+#define NID_member		875
+#define OBJ_member		OBJ_X509,31L
+
+#define SN_owner		"owner"
+#define NID_owner		876
+#define OBJ_owner		OBJ_X509,32L
+
+#define LN_roleOccupant		"roleOccupant"
+#define NID_roleOccupant		877
+#define OBJ_roleOccupant		OBJ_X509,33L
+
+#define SN_seeAlso		"seeAlso"
+#define NID_seeAlso		878
+#define OBJ_seeAlso		OBJ_X509,34L
+
+#define LN_userPassword		"userPassword"
+#define NID_userPassword		879
+#define OBJ_userPassword		OBJ_X509,35L
+
+#define LN_userCertificate		"userCertificate"
+#define NID_userCertificate		880
+#define OBJ_userCertificate		OBJ_X509,36L
+
+#define LN_cACertificate		"cACertificate"
+#define NID_cACertificate		881
+#define OBJ_cACertificate		OBJ_X509,37L
+
+#define LN_authorityRevocationList		"authorityRevocationList"
+#define NID_authorityRevocationList		882
+#define OBJ_authorityRevocationList		OBJ_X509,38L
+
+#define LN_certificateRevocationList		"certificateRevocationList"
+#define NID_certificateRevocationList		883
+#define OBJ_certificateRevocationList		OBJ_X509,39L
+
+#define LN_crossCertificatePair		"crossCertificatePair"
+#define NID_crossCertificatePair		884
+#define OBJ_crossCertificatePair		OBJ_X509,40L
+
+#define SN_name		"name"
+#define LN_name		"name"
+#define NID_name		173
+#define OBJ_name		OBJ_X509,41L
+
+#define SN_givenName		"GN"
+#define LN_givenName		"givenName"
+#define NID_givenName		99
+#define OBJ_givenName		OBJ_X509,42L
+
+#define SN_initials		"initials"
+#define LN_initials		"initials"
+#define NID_initials		101
+#define OBJ_initials		OBJ_X509,43L
+
+#define LN_generationQualifier		"generationQualifier"
+#define NID_generationQualifier		509
+#define OBJ_generationQualifier		OBJ_X509,44L
+
+#define LN_x500UniqueIdentifier		"x500UniqueIdentifier"
+#define NID_x500UniqueIdentifier		503
+#define OBJ_x500UniqueIdentifier		OBJ_X509,45L
+
+#define SN_dnQualifier		"dnQualifier"
+#define LN_dnQualifier		"dnQualifier"
+#define NID_dnQualifier		174
+#define OBJ_dnQualifier		OBJ_X509,46L
+
+#define LN_enhancedSearchGuide		"enhancedSearchGuide"
+#define NID_enhancedSearchGuide		885
+#define OBJ_enhancedSearchGuide		OBJ_X509,47L
+
+#define LN_protocolInformation		"protocolInformation"
+#define NID_protocolInformation		886
+#define OBJ_protocolInformation		OBJ_X509,48L
+
+#define LN_distinguishedName		"distinguishedName"
+#define NID_distinguishedName		887
+#define OBJ_distinguishedName		OBJ_X509,49L
+
+#define LN_uniqueMember		"uniqueMember"
+#define NID_uniqueMember		888
+#define OBJ_uniqueMember		OBJ_X509,50L
+
+#define LN_houseIdentifier		"houseIdentifier"
+#define NID_houseIdentifier		889
+#define OBJ_houseIdentifier		OBJ_X509,51L
+
+#define LN_supportedAlgorithms		"supportedAlgorithms"
+#define NID_supportedAlgorithms		890
+#define OBJ_supportedAlgorithms		OBJ_X509,52L
+
+#define LN_deltaRevocationList		"deltaRevocationList"
+#define NID_deltaRevocationList		891
+#define OBJ_deltaRevocationList		OBJ_X509,53L
+
+#define SN_dmdName		"dmdName"
+#define NID_dmdName		892
+#define OBJ_dmdName		OBJ_X509,54L
+
+#define LN_pseudonym		"pseudonym"
+#define NID_pseudonym		510
+#define OBJ_pseudonym		OBJ_X509,65L
+
+#define SN_role		"role"
+#define LN_role		"role"
+#define NID_role		400
+#define OBJ_role		OBJ_X509,72L
+
+#define SN_X500algorithms		"X500algorithms"
+#define LN_X500algorithms		"directory services - algorithms"
+#define NID_X500algorithms		378
+#define OBJ_X500algorithms		OBJ_X500,8L
+
+#define SN_rsa		"RSA"
+#define LN_rsa		"rsa"
+#define NID_rsa		19
+#define OBJ_rsa		OBJ_X500algorithms,1L,1L
+
+#define SN_mdc2WithRSA		"RSA-MDC2"
+#define LN_mdc2WithRSA		"mdc2WithRSA"
+#define NID_mdc2WithRSA		96
+#define OBJ_mdc2WithRSA		OBJ_X500algorithms,3L,100L
+
+#define SN_mdc2		"MDC2"
+#define LN_mdc2		"mdc2"
+#define NID_mdc2		95
+#define OBJ_mdc2		OBJ_X500algorithms,3L,101L
+
+#define SN_id_ce		"id-ce"
+#define NID_id_ce		81
+#define OBJ_id_ce		OBJ_X500,29L
+
+#define SN_subject_directory_attributes		"subjectDirectoryAttributes"
+#define LN_subject_directory_attributes		"X509v3 Subject Directory Attributes"
+#define NID_subject_directory_attributes		769
+#define OBJ_subject_directory_attributes		OBJ_id_ce,9L
+
+#define SN_subject_key_identifier		"subjectKeyIdentifier"
+#define LN_subject_key_identifier		"X509v3 Subject Key Identifier"
+#define NID_subject_key_identifier		82
+#define OBJ_subject_key_identifier		OBJ_id_ce,14L
+
+#define SN_key_usage		"keyUsage"
+#define LN_key_usage		"X509v3 Key Usage"
+#define NID_key_usage		83
+#define OBJ_key_usage		OBJ_id_ce,15L
+
+#define SN_private_key_usage_period		"privateKeyUsagePeriod"
+#define LN_private_key_usage_period		"X509v3 Private Key Usage Period"
+#define NID_private_key_usage_period		84
+#define OBJ_private_key_usage_period		OBJ_id_ce,16L
+
+#define SN_subject_alt_name		"subjectAltName"
+#define LN_subject_alt_name		"X509v3 Subject Alternative Name"
+#define NID_subject_alt_name		85
+#define OBJ_subject_alt_name		OBJ_id_ce,17L
+
+#define SN_issuer_alt_name		"issuerAltName"
+#define LN_issuer_alt_name		"X509v3 Issuer Alternative Name"
+#define NID_issuer_alt_name		86
+#define OBJ_issuer_alt_name		OBJ_id_ce,18L
+
+#define SN_basic_constraints		"basicConstraints"
+#define LN_basic_constraints		"X509v3 Basic Constraints"
+#define NID_basic_constraints		87
+#define OBJ_basic_constraints		OBJ_id_ce,19L
+
+#define SN_crl_number		"crlNumber"
+#define LN_crl_number		"X509v3 CRL Number"
+#define NID_crl_number		88
+#define OBJ_crl_number		OBJ_id_ce,20L
+
+#define SN_crl_reason		"CRLReason"
+#define LN_crl_reason		"X509v3 CRL Reason Code"
+#define NID_crl_reason		141
+#define OBJ_crl_reason		OBJ_id_ce,21L
+
+#define SN_invalidity_date		"invalidityDate"
+#define LN_invalidity_date		"Invalidity Date"
+#define NID_invalidity_date		142
+#define OBJ_invalidity_date		OBJ_id_ce,24L
+
+#define SN_delta_crl		"deltaCRL"
+#define LN_delta_crl		"X509v3 Delta CRL Indicator"
+#define NID_delta_crl		140
+#define OBJ_delta_crl		OBJ_id_ce,27L
+
+#define SN_issuing_distribution_point		"issuingDistributionPoint"
+#define LN_issuing_distribution_point		"X509v3 Issuing Distrubution Point"
+#define NID_issuing_distribution_point		770
+#define OBJ_issuing_distribution_point		OBJ_id_ce,28L
+
+#define SN_certificate_issuer		"certificateIssuer"
+#define LN_certificate_issuer		"X509v3 Certificate Issuer"
+#define NID_certificate_issuer		771
+#define OBJ_certificate_issuer		OBJ_id_ce,29L
+
+#define SN_name_constraints		"nameConstraints"
+#define LN_name_constraints		"X509v3 Name Constraints"
+#define NID_name_constraints		666
+#define OBJ_name_constraints		OBJ_id_ce,30L
+
+#define SN_crl_distribution_points		"crlDistributionPoints"
+#define LN_crl_distribution_points		"X509v3 CRL Distribution Points"
+#define NID_crl_distribution_points		103
+#define OBJ_crl_distribution_points		OBJ_id_ce,31L
+
+#define SN_certificate_policies		"certificatePolicies"
+#define LN_certificate_policies		"X509v3 Certificate Policies"
+#define NID_certificate_policies		89
+#define OBJ_certificate_policies		OBJ_id_ce,32L
+
+#define SN_any_policy		"anyPolicy"
+#define LN_any_policy		"X509v3 Any Policy"
+#define NID_any_policy		746
+#define OBJ_any_policy		OBJ_certificate_policies,0L
+
+#define SN_policy_mappings		"policyMappings"
+#define LN_policy_mappings		"X509v3 Policy Mappings"
+#define NID_policy_mappings		747
+#define OBJ_policy_mappings		OBJ_id_ce,33L
+
+#define SN_authority_key_identifier		"authorityKeyIdentifier"
+#define LN_authority_key_identifier		"X509v3 Authority Key Identifier"
+#define NID_authority_key_identifier		90
+#define OBJ_authority_key_identifier		OBJ_id_ce,35L
+
+#define SN_policy_constraints		"policyConstraints"
+#define LN_policy_constraints		"X509v3 Policy Constraints"
+#define NID_policy_constraints		401
+#define OBJ_policy_constraints		OBJ_id_ce,36L
+
+#define SN_ext_key_usage		"extendedKeyUsage"
+#define LN_ext_key_usage		"X509v3 Extended Key Usage"
+#define NID_ext_key_usage		126
+#define OBJ_ext_key_usage		OBJ_id_ce,37L
+
+#define SN_freshest_crl		"freshestCRL"
+#define LN_freshest_crl		"X509v3 Freshest CRL"
+#define NID_freshest_crl		857
+#define OBJ_freshest_crl		OBJ_id_ce,46L
+
+#define SN_inhibit_any_policy		"inhibitAnyPolicy"
+#define LN_inhibit_any_policy		"X509v3 Inhibit Any Policy"
+#define NID_inhibit_any_policy		748
+#define OBJ_inhibit_any_policy		OBJ_id_ce,54L
+
+#define SN_target_information		"targetInformation"
+#define LN_target_information		"X509v3 AC Targeting"
+#define NID_target_information		402
+#define OBJ_target_information		OBJ_id_ce,55L
+
+#define SN_no_rev_avail		"noRevAvail"
+#define LN_no_rev_avail		"X509v3 No Revocation Available"
+#define NID_no_rev_avail		403
+#define OBJ_no_rev_avail		OBJ_id_ce,56L
+
+#define SN_anyExtendedKeyUsage		"anyExtendedKeyUsage"
+#define LN_anyExtendedKeyUsage		"Any Extended Key Usage"
+#define NID_anyExtendedKeyUsage		910
+#define OBJ_anyExtendedKeyUsage		OBJ_ext_key_usage,0L
+
+#define SN_netscape		"Netscape"
+#define LN_netscape		"Netscape Communications Corp."
+#define NID_netscape		57
+#define OBJ_netscape		2L,16L,840L,1L,113730L
+
+#define SN_netscape_cert_extension		"nsCertExt"
+#define LN_netscape_cert_extension		"Netscape Certificate Extension"
+#define NID_netscape_cert_extension		58
+#define OBJ_netscape_cert_extension		OBJ_netscape,1L
+
+#define SN_netscape_data_type		"nsDataType"
+#define LN_netscape_data_type		"Netscape Data Type"
+#define NID_netscape_data_type		59
+#define OBJ_netscape_data_type		OBJ_netscape,2L
+
+#define SN_netscape_cert_type		"nsCertType"
+#define LN_netscape_cert_type		"Netscape Cert Type"
+#define NID_netscape_cert_type		71
+#define OBJ_netscape_cert_type		OBJ_netscape_cert_extension,1L
+
+#define SN_netscape_base_url		"nsBaseUrl"
+#define LN_netscape_base_url		"Netscape Base Url"
+#define NID_netscape_base_url		72
+#define OBJ_netscape_base_url		OBJ_netscape_cert_extension,2L
+
+#define SN_netscape_revocation_url		"nsRevocationUrl"
+#define LN_netscape_revocation_url		"Netscape Revocation Url"
+#define NID_netscape_revocation_url		73
+#define OBJ_netscape_revocation_url		OBJ_netscape_cert_extension,3L
+
+#define SN_netscape_ca_revocation_url		"nsCaRevocationUrl"
+#define LN_netscape_ca_revocation_url		"Netscape CA Revocation Url"
+#define NID_netscape_ca_revocation_url		74
+#define OBJ_netscape_ca_revocation_url		OBJ_netscape_cert_extension,4L
+
+#define SN_netscape_renewal_url		"nsRenewalUrl"
+#define LN_netscape_renewal_url		"Netscape Renewal Url"
+#define NID_netscape_renewal_url		75
+#define OBJ_netscape_renewal_url		OBJ_netscape_cert_extension,7L
+
+#define SN_netscape_ca_policy_url		"nsCaPolicyUrl"
+#define LN_netscape_ca_policy_url		"Netscape CA Policy Url"
+#define NID_netscape_ca_policy_url		76
+#define OBJ_netscape_ca_policy_url		OBJ_netscape_cert_extension,8L
+
+#define SN_netscape_ssl_server_name		"nsSslServerName"
+#define LN_netscape_ssl_server_name		"Netscape SSL Server Name"
+#define NID_netscape_ssl_server_name		77
+#define OBJ_netscape_ssl_server_name		OBJ_netscape_cert_extension,12L
+
+#define SN_netscape_comment		"nsComment"
+#define LN_netscape_comment		"Netscape Comment"
+#define NID_netscape_comment		78
+#define OBJ_netscape_comment		OBJ_netscape_cert_extension,13L
+
+#define SN_netscape_cert_sequence		"nsCertSequence"
+#define LN_netscape_cert_sequence		"Netscape Certificate Sequence"
+#define NID_netscape_cert_sequence		79
+#define OBJ_netscape_cert_sequence		OBJ_netscape_data_type,5L
+
+#define SN_ns_sgc		"nsSGC"
+#define LN_ns_sgc		"Netscape Server Gated Crypto"
+#define NID_ns_sgc		139
+#define OBJ_ns_sgc		OBJ_netscape,4L,1L
+
+#define SN_org		"ORG"
+#define LN_org		"org"
+#define NID_org		379
+#define OBJ_org		OBJ_iso,3L
+
+#define SN_dod		"DOD"
+#define LN_dod		"dod"
+#define NID_dod		380
+#define OBJ_dod		OBJ_org,6L
+
+#define SN_iana		"IANA"
+#define LN_iana		"iana"
+#define NID_iana		381
+#define OBJ_iana		OBJ_dod,1L
+
+#define OBJ_internet		OBJ_iana
+
+#define SN_Directory		"directory"
+#define LN_Directory		"Directory"
+#define NID_Directory		382
+#define OBJ_Directory		OBJ_internet,1L
+
+#define SN_Management		"mgmt"
+#define LN_Management		"Management"
+#define NID_Management		383
+#define OBJ_Management		OBJ_internet,2L
+
+#define SN_Experimental		"experimental"
+#define LN_Experimental		"Experimental"
+#define NID_Experimental		384
+#define OBJ_Experimental		OBJ_internet,3L
+
+#define SN_Private		"private"
+#define LN_Private		"Private"
+#define NID_Private		385
+#define OBJ_Private		OBJ_internet,4L
+
+#define SN_Security		"security"
+#define LN_Security		"Security"
+#define NID_Security		386
+#define OBJ_Security		OBJ_internet,5L
+
+#define SN_SNMPv2		"snmpv2"
+#define LN_SNMPv2		"SNMPv2"
+#define NID_SNMPv2		387
+#define OBJ_SNMPv2		OBJ_internet,6L
+
+#define LN_Mail		"Mail"
+#define NID_Mail		388
+#define OBJ_Mail		OBJ_internet,7L
+
+#define SN_Enterprises		"enterprises"
+#define LN_Enterprises		"Enterprises"
+#define NID_Enterprises		389
+#define OBJ_Enterprises		OBJ_Private,1L
+
+#define SN_dcObject		"dcobject"
+#define LN_dcObject		"dcObject"
+#define NID_dcObject		390
+#define OBJ_dcObject		OBJ_Enterprises,1466L,344L
+
+#define SN_mime_mhs		"mime-mhs"
+#define LN_mime_mhs		"MIME MHS"
+#define NID_mime_mhs		504
+#define OBJ_mime_mhs		OBJ_Mail,1L
+
+#define SN_mime_mhs_headings		"mime-mhs-headings"
+#define LN_mime_mhs_headings		"mime-mhs-headings"
+#define NID_mime_mhs_headings		505
+#define OBJ_mime_mhs_headings		OBJ_mime_mhs,1L
+
+#define SN_mime_mhs_bodies		"mime-mhs-bodies"
+#define LN_mime_mhs_bodies		"mime-mhs-bodies"
+#define NID_mime_mhs_bodies		506
+#define OBJ_mime_mhs_bodies		OBJ_mime_mhs,2L
+
+#define SN_id_hex_partial_message		"id-hex-partial-message"
+#define LN_id_hex_partial_message		"id-hex-partial-message"
+#define NID_id_hex_partial_message		507
+#define OBJ_id_hex_partial_message		OBJ_mime_mhs_headings,1L
+
+#define SN_id_hex_multipart_message		"id-hex-multipart-message"
+#define LN_id_hex_multipart_message		"id-hex-multipart-message"
+#define NID_id_hex_multipart_message		508
+#define OBJ_id_hex_multipart_message		OBJ_mime_mhs_headings,2L
+
+#define SN_rle_compression		"RLE"
+#define LN_rle_compression		"run length compression"
+#define NID_rle_compression		124
+#define OBJ_rle_compression		1L,1L,1L,1L,666L,1L
+
+#define SN_zlib_compression		"ZLIB"
+#define LN_zlib_compression		"zlib compression"
+#define NID_zlib_compression		125
+#define OBJ_zlib_compression		OBJ_id_smime_alg,8L
+
+#define OBJ_csor		2L,16L,840L,1L,101L,3L
+
+#define OBJ_nistAlgorithms		OBJ_csor,4L
+
+#define OBJ_aes		OBJ_nistAlgorithms,1L
+
+#define SN_aes_128_ecb		"AES-128-ECB"
+#define LN_aes_128_ecb		"aes-128-ecb"
+#define NID_aes_128_ecb		418
+#define OBJ_aes_128_ecb		OBJ_aes,1L
+
+#define SN_aes_128_cbc		"AES-128-CBC"
+#define LN_aes_128_cbc		"aes-128-cbc"
+#define NID_aes_128_cbc		419
+#define OBJ_aes_128_cbc		OBJ_aes,2L
+
+#define SN_aes_128_ofb128		"AES-128-OFB"
+#define LN_aes_128_ofb128		"aes-128-ofb"
+#define NID_aes_128_ofb128		420
+#define OBJ_aes_128_ofb128		OBJ_aes,3L
+
+#define SN_aes_128_cfb128		"AES-128-CFB"
+#define LN_aes_128_cfb128		"aes-128-cfb"
+#define NID_aes_128_cfb128		421
+#define OBJ_aes_128_cfb128		OBJ_aes,4L
+
+#define SN_id_aes128_wrap		"id-aes128-wrap"
+#define NID_id_aes128_wrap		788
+#define OBJ_id_aes128_wrap		OBJ_aes,5L
+
+#define SN_aes_128_gcm		"id-aes128-GCM"
+#define LN_aes_128_gcm		"aes-128-gcm"
+#define NID_aes_128_gcm		895
+#define OBJ_aes_128_gcm		OBJ_aes,6L
+
+#define SN_aes_128_ccm		"id-aes128-CCM"
+#define LN_aes_128_ccm		"aes-128-ccm"
+#define NID_aes_128_ccm		896
+#define OBJ_aes_128_ccm		OBJ_aes,7L
+
+#define SN_id_aes128_wrap_pad		"id-aes128-wrap-pad"
+#define NID_id_aes128_wrap_pad		897
+#define OBJ_id_aes128_wrap_pad		OBJ_aes,8L
+
+#define SN_aes_192_ecb		"AES-192-ECB"
+#define LN_aes_192_ecb		"aes-192-ecb"
+#define NID_aes_192_ecb		422
+#define OBJ_aes_192_ecb		OBJ_aes,21L
+
+#define SN_aes_192_cbc		"AES-192-CBC"
+#define LN_aes_192_cbc		"aes-192-cbc"
+#define NID_aes_192_cbc		423
+#define OBJ_aes_192_cbc		OBJ_aes,22L
+
+#define SN_aes_192_ofb128		"AES-192-OFB"
+#define LN_aes_192_ofb128		"aes-192-ofb"
+#define NID_aes_192_ofb128		424
+#define OBJ_aes_192_ofb128		OBJ_aes,23L
+
+#define SN_aes_192_cfb128		"AES-192-CFB"
+#define LN_aes_192_cfb128		"aes-192-cfb"
+#define NID_aes_192_cfb128		425
+#define OBJ_aes_192_cfb128		OBJ_aes,24L
+
+#define SN_id_aes192_wrap		"id-aes192-wrap"
+#define NID_id_aes192_wrap		789
+#define OBJ_id_aes192_wrap		OBJ_aes,25L
+
+#define SN_aes_192_gcm		"id-aes192-GCM"
+#define LN_aes_192_gcm		"aes-192-gcm"
+#define NID_aes_192_gcm		898
+#define OBJ_aes_192_gcm		OBJ_aes,26L
+
+#define SN_aes_192_ccm		"id-aes192-CCM"
+#define LN_aes_192_ccm		"aes-192-ccm"
+#define NID_aes_192_ccm		899
+#define OBJ_aes_192_ccm		OBJ_aes,27L
+
+#define SN_id_aes192_wrap_pad		"id-aes192-wrap-pad"
+#define NID_id_aes192_wrap_pad		900
+#define OBJ_id_aes192_wrap_pad		OBJ_aes,28L
+
+#define SN_aes_256_ecb		"AES-256-ECB"
+#define LN_aes_256_ecb		"aes-256-ecb"
+#define NID_aes_256_ecb		426
+#define OBJ_aes_256_ecb		OBJ_aes,41L
+
+#define SN_aes_256_cbc		"AES-256-CBC"
+#define LN_aes_256_cbc		"aes-256-cbc"
+#define NID_aes_256_cbc		427
+#define OBJ_aes_256_cbc		OBJ_aes,42L
+
+#define SN_aes_256_ofb128		"AES-256-OFB"
+#define LN_aes_256_ofb128		"aes-256-ofb"
+#define NID_aes_256_ofb128		428
+#define OBJ_aes_256_ofb128		OBJ_aes,43L
+
+#define SN_aes_256_cfb128		"AES-256-CFB"
+#define LN_aes_256_cfb128		"aes-256-cfb"
+#define NID_aes_256_cfb128		429
+#define OBJ_aes_256_cfb128		OBJ_aes,44L
+
+#define SN_id_aes256_wrap		"id-aes256-wrap"
+#define NID_id_aes256_wrap		790
+#define OBJ_id_aes256_wrap		OBJ_aes,45L
+
+#define SN_aes_256_gcm		"id-aes256-GCM"
+#define LN_aes_256_gcm		"aes-256-gcm"
+#define NID_aes_256_gcm		901
+#define OBJ_aes_256_gcm		OBJ_aes,46L
+
+#define SN_aes_256_ccm		"id-aes256-CCM"
+#define LN_aes_256_ccm		"aes-256-ccm"
+#define NID_aes_256_ccm		902
+#define OBJ_aes_256_ccm		OBJ_aes,47L
+
+#define SN_id_aes256_wrap_pad		"id-aes256-wrap-pad"
+#define NID_id_aes256_wrap_pad		903
+#define OBJ_id_aes256_wrap_pad		OBJ_aes,48L
+
+#define SN_aes_128_cfb1		"AES-128-CFB1"
+#define LN_aes_128_cfb1		"aes-128-cfb1"
+#define NID_aes_128_cfb1		650
+
+#define SN_aes_192_cfb1		"AES-192-CFB1"
+#define LN_aes_192_cfb1		"aes-192-cfb1"
+#define NID_aes_192_cfb1		651
+
+#define SN_aes_256_cfb1		"AES-256-CFB1"
+#define LN_aes_256_cfb1		"aes-256-cfb1"
+#define NID_aes_256_cfb1		652
+
+#define SN_aes_128_cfb8		"AES-128-CFB8"
+#define LN_aes_128_cfb8		"aes-128-cfb8"
+#define NID_aes_128_cfb8		653
+
+#define SN_aes_192_cfb8		"AES-192-CFB8"
+#define LN_aes_192_cfb8		"aes-192-cfb8"
+#define NID_aes_192_cfb8		654
+
+#define SN_aes_256_cfb8		"AES-256-CFB8"
+#define LN_aes_256_cfb8		"aes-256-cfb8"
+#define NID_aes_256_cfb8		655
+
+#define SN_aes_128_ctr		"AES-128-CTR"
+#define LN_aes_128_ctr		"aes-128-ctr"
+#define NID_aes_128_ctr		904
+
+#define SN_aes_192_ctr		"AES-192-CTR"
+#define LN_aes_192_ctr		"aes-192-ctr"
+#define NID_aes_192_ctr		905
+
+#define SN_aes_256_ctr		"AES-256-CTR"
+#define LN_aes_256_ctr		"aes-256-ctr"
+#define NID_aes_256_ctr		906
+
+#define SN_aes_128_xts		"AES-128-XTS"
+#define LN_aes_128_xts		"aes-128-xts"
+#define NID_aes_128_xts		913
+
+#define SN_aes_256_xts		"AES-256-XTS"
+#define LN_aes_256_xts		"aes-256-xts"
+#define NID_aes_256_xts		914
+
+#define SN_des_cfb1		"DES-CFB1"
+#define LN_des_cfb1		"des-cfb1"
+#define NID_des_cfb1		656
+
+#define SN_des_cfb8		"DES-CFB8"
+#define LN_des_cfb8		"des-cfb8"
+#define NID_des_cfb8		657
+
+#define SN_des_ede3_cfb1		"DES-EDE3-CFB1"
+#define LN_des_ede3_cfb1		"des-ede3-cfb1"
+#define NID_des_ede3_cfb1		658
+
+#define SN_des_ede3_cfb8		"DES-EDE3-CFB8"
+#define LN_des_ede3_cfb8		"des-ede3-cfb8"
+#define NID_des_ede3_cfb8		659
+
+#define OBJ_nist_hashalgs		OBJ_nistAlgorithms,2L
+
+#define SN_sha256		"SHA256"
+#define LN_sha256		"sha256"
+#define NID_sha256		672
+#define OBJ_sha256		OBJ_nist_hashalgs,1L
+
+#define SN_sha384		"SHA384"
+#define LN_sha384		"sha384"
+#define NID_sha384		673
+#define OBJ_sha384		OBJ_nist_hashalgs,2L
+
+#define SN_sha512		"SHA512"
+#define LN_sha512		"sha512"
+#define NID_sha512		674
+#define OBJ_sha512		OBJ_nist_hashalgs,3L
+
+#define SN_sha224		"SHA224"
+#define LN_sha224		"sha224"
+#define NID_sha224		675
+#define OBJ_sha224		OBJ_nist_hashalgs,4L
+
+#define OBJ_dsa_with_sha2		OBJ_nistAlgorithms,3L
+
+#define SN_dsa_with_SHA224		"dsa_with_SHA224"
+#define NID_dsa_with_SHA224		802
+#define OBJ_dsa_with_SHA224		OBJ_dsa_with_sha2,1L
+
+#define SN_dsa_with_SHA256		"dsa_with_SHA256"
+#define NID_dsa_with_SHA256		803
+#define OBJ_dsa_with_SHA256		OBJ_dsa_with_sha2,2L
+
+#define SN_hold_instruction_code		"holdInstructionCode"
+#define LN_hold_instruction_code		"Hold Instruction Code"
+#define NID_hold_instruction_code		430
+#define OBJ_hold_instruction_code		OBJ_id_ce,23L
+
+#define OBJ_holdInstruction		OBJ_X9_57,2L
+
+#define SN_hold_instruction_none		"holdInstructionNone"
+#define LN_hold_instruction_none		"Hold Instruction None"
+#define NID_hold_instruction_none		431
+#define OBJ_hold_instruction_none		OBJ_holdInstruction,1L
+
+#define SN_hold_instruction_call_issuer		"holdInstructionCallIssuer"
+#define LN_hold_instruction_call_issuer		"Hold Instruction Call Issuer"
+#define NID_hold_instruction_call_issuer		432
+#define OBJ_hold_instruction_call_issuer		OBJ_holdInstruction,2L
+
+#define SN_hold_instruction_reject		"holdInstructionReject"
+#define LN_hold_instruction_reject		"Hold Instruction Reject"
+#define NID_hold_instruction_reject		433
+#define OBJ_hold_instruction_reject		OBJ_holdInstruction,3L
+
+#define SN_data		"data"
+#define NID_data		434
+#define OBJ_data		OBJ_itu_t,9L
+
+#define SN_pss		"pss"
+#define NID_pss		435
+#define OBJ_pss		OBJ_data,2342L
+
+#define SN_ucl		"ucl"
+#define NID_ucl		436
+#define OBJ_ucl		OBJ_pss,19200300L
+
+#define SN_pilot		"pilot"
+#define NID_pilot		437
+#define OBJ_pilot		OBJ_ucl,100L
+
+#define LN_pilotAttributeType		"pilotAttributeType"
+#define NID_pilotAttributeType		438
+#define OBJ_pilotAttributeType		OBJ_pilot,1L
+
+#define LN_pilotAttributeSyntax		"pilotAttributeSyntax"
+#define NID_pilotAttributeSyntax		439
+#define OBJ_pilotAttributeSyntax		OBJ_pilot,3L
+
+#define LN_pilotObjectClass		"pilotObjectClass"
+#define NID_pilotObjectClass		440
+#define OBJ_pilotObjectClass		OBJ_pilot,4L
+
+#define LN_pilotGroups		"pilotGroups"
+#define NID_pilotGroups		441
+#define OBJ_pilotGroups		OBJ_pilot,10L
+
+#define LN_iA5StringSyntax		"iA5StringSyntax"
+#define NID_iA5StringSyntax		442
+#define OBJ_iA5StringSyntax		OBJ_pilotAttributeSyntax,4L
+
+#define LN_caseIgnoreIA5StringSyntax		"caseIgnoreIA5StringSyntax"
+#define NID_caseIgnoreIA5StringSyntax		443
+#define OBJ_caseIgnoreIA5StringSyntax		OBJ_pilotAttributeSyntax,5L
+
+#define LN_pilotObject		"pilotObject"
+#define NID_pilotObject		444
+#define OBJ_pilotObject		OBJ_pilotObjectClass,3L
+
+#define LN_pilotPerson		"pilotPerson"
+#define NID_pilotPerson		445
+#define OBJ_pilotPerson		OBJ_pilotObjectClass,4L
+
+#define SN_account		"account"
+#define NID_account		446
+#define OBJ_account		OBJ_pilotObjectClass,5L
+
+#define SN_document		"document"
+#define NID_document		447
+#define OBJ_document		OBJ_pilotObjectClass,6L
+
+#define SN_room		"room"
+#define NID_room		448
+#define OBJ_room		OBJ_pilotObjectClass,7L
+
+#define LN_documentSeries		"documentSeries"
+#define NID_documentSeries		449
+#define OBJ_documentSeries		OBJ_pilotObjectClass,9L
+
+#define SN_Domain		"domain"
+#define LN_Domain		"Domain"
+#define NID_Domain		392
+#define OBJ_Domain		OBJ_pilotObjectClass,13L
+
+#define LN_rFC822localPart		"rFC822localPart"
+#define NID_rFC822localPart		450
+#define OBJ_rFC822localPart		OBJ_pilotObjectClass,14L
+
+#define LN_dNSDomain		"dNSDomain"
+#define NID_dNSDomain		451
+#define OBJ_dNSDomain		OBJ_pilotObjectClass,15L
+
+#define LN_domainRelatedObject		"domainRelatedObject"
+#define NID_domainRelatedObject		452
+#define OBJ_domainRelatedObject		OBJ_pilotObjectClass,17L
+
+#define LN_friendlyCountry		"friendlyCountry"
+#define NID_friendlyCountry		453
+#define OBJ_friendlyCountry		OBJ_pilotObjectClass,18L
+
+#define LN_simpleSecurityObject		"simpleSecurityObject"
+#define NID_simpleSecurityObject		454
+#define OBJ_simpleSecurityObject		OBJ_pilotObjectClass,19L
+
+#define LN_pilotOrganization		"pilotOrganization"
+#define NID_pilotOrganization		455
+#define OBJ_pilotOrganization		OBJ_pilotObjectClass,20L
+
+#define LN_pilotDSA		"pilotDSA"
+#define NID_pilotDSA		456
+#define OBJ_pilotDSA		OBJ_pilotObjectClass,21L
+
+#define LN_qualityLabelledData		"qualityLabelledData"
+#define NID_qualityLabelledData		457
+#define OBJ_qualityLabelledData		OBJ_pilotObjectClass,22L
+
+#define SN_userId		"UID"
+#define LN_userId		"userId"
+#define NID_userId		458
+#define OBJ_userId		OBJ_pilotAttributeType,1L
+
+#define LN_textEncodedORAddress		"textEncodedORAddress"
+#define NID_textEncodedORAddress		459
+#define OBJ_textEncodedORAddress		OBJ_pilotAttributeType,2L
+
+#define SN_rfc822Mailbox		"mail"
+#define LN_rfc822Mailbox		"rfc822Mailbox"
+#define NID_rfc822Mailbox		460
+#define OBJ_rfc822Mailbox		OBJ_pilotAttributeType,3L
+
+#define SN_info		"info"
+#define NID_info		461
+#define OBJ_info		OBJ_pilotAttributeType,4L
+
+#define LN_favouriteDrink		"favouriteDrink"
+#define NID_favouriteDrink		462
+#define OBJ_favouriteDrink		OBJ_pilotAttributeType,5L
+
+#define LN_roomNumber		"roomNumber"
+#define NID_roomNumber		463
+#define OBJ_roomNumber		OBJ_pilotAttributeType,6L
+
+#define SN_photo		"photo"
+#define NID_photo		464
+#define OBJ_photo		OBJ_pilotAttributeType,7L
+
+#define LN_userClass		"userClass"
+#define NID_userClass		465
+#define OBJ_userClass		OBJ_pilotAttributeType,8L
+
+#define SN_host		"host"
+#define NID_host		466
+#define OBJ_host		OBJ_pilotAttributeType,9L
+
+#define SN_manager		"manager"
+#define NID_manager		467
+#define OBJ_manager		OBJ_pilotAttributeType,10L
+
+#define LN_documentIdentifier		"documentIdentifier"
+#define NID_documentIdentifier		468
+#define OBJ_documentIdentifier		OBJ_pilotAttributeType,11L
+
+#define LN_documentTitle		"documentTitle"
+#define NID_documentTitle		469
+#define OBJ_documentTitle		OBJ_pilotAttributeType,12L
+
+#define LN_documentVersion		"documentVersion"
+#define NID_documentVersion		470
+#define OBJ_documentVersion		OBJ_pilotAttributeType,13L
+
+#define LN_documentAuthor		"documentAuthor"
+#define NID_documentAuthor		471
+#define OBJ_documentAuthor		OBJ_pilotAttributeType,14L
+
+#define LN_documentLocation		"documentLocation"
+#define NID_documentLocation		472
+#define OBJ_documentLocation		OBJ_pilotAttributeType,15L
+
+#define LN_homeTelephoneNumber		"homeTelephoneNumber"
+#define NID_homeTelephoneNumber		473
+#define OBJ_homeTelephoneNumber		OBJ_pilotAttributeType,20L
+
+#define SN_secretary		"secretary"
+#define NID_secretary		474
+#define OBJ_secretary		OBJ_pilotAttributeType,21L
+
+#define LN_otherMailbox		"otherMailbox"
+#define NID_otherMailbox		475
+#define OBJ_otherMailbox		OBJ_pilotAttributeType,22L
+
+#define LN_lastModifiedTime		"lastModifiedTime"
+#define NID_lastModifiedTime		476
+#define OBJ_lastModifiedTime		OBJ_pilotAttributeType,23L
+
+#define LN_lastModifiedBy		"lastModifiedBy"
+#define NID_lastModifiedBy		477
+#define OBJ_lastModifiedBy		OBJ_pilotAttributeType,24L
+
+#define SN_domainComponent		"DC"
+#define LN_domainComponent		"domainComponent"
+#define NID_domainComponent		391
+#define OBJ_domainComponent		OBJ_pilotAttributeType,25L
+
+#define LN_aRecord		"aRecord"
+#define NID_aRecord		478
+#define OBJ_aRecord		OBJ_pilotAttributeType,26L
+
+#define LN_pilotAttributeType27		"pilotAttributeType27"
+#define NID_pilotAttributeType27		479
+#define OBJ_pilotAttributeType27		OBJ_pilotAttributeType,27L
+
+#define LN_mXRecord		"mXRecord"
+#define NID_mXRecord		480
+#define OBJ_mXRecord		OBJ_pilotAttributeType,28L
+
+#define LN_nSRecord		"nSRecord"
+#define NID_nSRecord		481
+#define OBJ_nSRecord		OBJ_pilotAttributeType,29L
+
+#define LN_sOARecord		"sOARecord"
+#define NID_sOARecord		482
+#define OBJ_sOARecord		OBJ_pilotAttributeType,30L
+
+#define LN_cNAMERecord		"cNAMERecord"
+#define NID_cNAMERecord		483
+#define OBJ_cNAMERecord		OBJ_pilotAttributeType,31L
+
+#define LN_associatedDomain		"associatedDomain"
+#define NID_associatedDomain		484
+#define OBJ_associatedDomain		OBJ_pilotAttributeType,37L
+
+#define LN_associatedName		"associatedName"
+#define NID_associatedName		485
+#define OBJ_associatedName		OBJ_pilotAttributeType,38L
+
+#define LN_homePostalAddress		"homePostalAddress"
+#define NID_homePostalAddress		486
+#define OBJ_homePostalAddress		OBJ_pilotAttributeType,39L
+
+#define LN_personalTitle		"personalTitle"
+#define NID_personalTitle		487
+#define OBJ_personalTitle		OBJ_pilotAttributeType,40L
+
+#define LN_mobileTelephoneNumber		"mobileTelephoneNumber"
+#define NID_mobileTelephoneNumber		488
+#define OBJ_mobileTelephoneNumber		OBJ_pilotAttributeType,41L
+
+#define LN_pagerTelephoneNumber		"pagerTelephoneNumber"
+#define NID_pagerTelephoneNumber		489
+#define OBJ_pagerTelephoneNumber		OBJ_pilotAttributeType,42L
+
+#define LN_friendlyCountryName		"friendlyCountryName"
+#define NID_friendlyCountryName		490
+#define OBJ_friendlyCountryName		OBJ_pilotAttributeType,43L
+
+#define LN_organizationalStatus		"organizationalStatus"
+#define NID_organizationalStatus		491
+#define OBJ_organizationalStatus		OBJ_pilotAttributeType,45L
+
+#define LN_janetMailbox		"janetMailbox"
+#define NID_janetMailbox		492
+#define OBJ_janetMailbox		OBJ_pilotAttributeType,46L
+
+#define LN_mailPreferenceOption		"mailPreferenceOption"
+#define NID_mailPreferenceOption		493
+#define OBJ_mailPreferenceOption		OBJ_pilotAttributeType,47L
+
+#define LN_buildingName		"buildingName"
+#define NID_buildingName		494
+#define OBJ_buildingName		OBJ_pilotAttributeType,48L
+
+#define LN_dSAQuality		"dSAQuality"
+#define NID_dSAQuality		495
+#define OBJ_dSAQuality		OBJ_pilotAttributeType,49L
+
+#define LN_singleLevelQuality		"singleLevelQuality"
+#define NID_singleLevelQuality		496
+#define OBJ_singleLevelQuality		OBJ_pilotAttributeType,50L
+
+#define LN_subtreeMinimumQuality		"subtreeMinimumQuality"
+#define NID_subtreeMinimumQuality		497
+#define OBJ_subtreeMinimumQuality		OBJ_pilotAttributeType,51L
+
+#define LN_subtreeMaximumQuality		"subtreeMaximumQuality"
+#define NID_subtreeMaximumQuality		498
+#define OBJ_subtreeMaximumQuality		OBJ_pilotAttributeType,52L
+
+#define LN_personalSignature		"personalSignature"
+#define NID_personalSignature		499
+#define OBJ_personalSignature		OBJ_pilotAttributeType,53L
+
+#define LN_dITRedirect		"dITRedirect"
+#define NID_dITRedirect		500
+#define OBJ_dITRedirect		OBJ_pilotAttributeType,54L
+
+#define SN_audio		"audio"
+#define NID_audio		501
+#define OBJ_audio		OBJ_pilotAttributeType,55L
+
+#define LN_documentPublisher		"documentPublisher"
+#define NID_documentPublisher		502
+#define OBJ_documentPublisher		OBJ_pilotAttributeType,56L
+
+#define SN_id_set		"id-set"
+#define LN_id_set		"Secure Electronic Transactions"
+#define NID_id_set		512
+#define OBJ_id_set		OBJ_international_organizations,42L
+
+#define SN_set_ctype		"set-ctype"
+#define LN_set_ctype		"content types"
+#define NID_set_ctype		513
+#define OBJ_set_ctype		OBJ_id_set,0L
+
+#define SN_set_msgExt		"set-msgExt"
+#define LN_set_msgExt		"message extensions"
+#define NID_set_msgExt		514
+#define OBJ_set_msgExt		OBJ_id_set,1L
+
+#define SN_set_attr		"set-attr"
+#define NID_set_attr		515
+#define OBJ_set_attr		OBJ_id_set,3L
+
+#define SN_set_policy		"set-policy"
+#define NID_set_policy		516
+#define OBJ_set_policy		OBJ_id_set,5L
+
+#define SN_set_certExt		"set-certExt"
+#define LN_set_certExt		"certificate extensions"
+#define NID_set_certExt		517
+#define OBJ_set_certExt		OBJ_id_set,7L
+
+#define SN_set_brand		"set-brand"
+#define NID_set_brand		518
+#define OBJ_set_brand		OBJ_id_set,8L
+
+#define SN_setct_PANData		"setct-PANData"
+#define NID_setct_PANData		519
+#define OBJ_setct_PANData		OBJ_set_ctype,0L
+
+#define SN_setct_PANToken		"setct-PANToken"
+#define NID_setct_PANToken		520
+#define OBJ_setct_PANToken		OBJ_set_ctype,1L
+
+#define SN_setct_PANOnly		"setct-PANOnly"
+#define NID_setct_PANOnly		521
+#define OBJ_setct_PANOnly		OBJ_set_ctype,2L
+
+#define SN_setct_OIData		"setct-OIData"
+#define NID_setct_OIData		522
+#define OBJ_setct_OIData		OBJ_set_ctype,3L
+
+#define SN_setct_PI		"setct-PI"
+#define NID_setct_PI		523
+#define OBJ_setct_PI		OBJ_set_ctype,4L
+
+#define SN_setct_PIData		"setct-PIData"
+#define NID_setct_PIData		524
+#define OBJ_setct_PIData		OBJ_set_ctype,5L
+
+#define SN_setct_PIDataUnsigned		"setct-PIDataUnsigned"
+#define NID_setct_PIDataUnsigned		525
+#define OBJ_setct_PIDataUnsigned		OBJ_set_ctype,6L
+
+#define SN_setct_HODInput		"setct-HODInput"
+#define NID_setct_HODInput		526
+#define OBJ_setct_HODInput		OBJ_set_ctype,7L
+
+#define SN_setct_AuthResBaggage		"setct-AuthResBaggage"
+#define NID_setct_AuthResBaggage		527
+#define OBJ_setct_AuthResBaggage		OBJ_set_ctype,8L
+
+#define SN_setct_AuthRevReqBaggage		"setct-AuthRevReqBaggage"
+#define NID_setct_AuthRevReqBaggage		528
+#define OBJ_setct_AuthRevReqBaggage		OBJ_set_ctype,9L
+
+#define SN_setct_AuthRevResBaggage		"setct-AuthRevResBaggage"
+#define NID_setct_AuthRevResBaggage		529
+#define OBJ_setct_AuthRevResBaggage		OBJ_set_ctype,10L
+
+#define SN_setct_CapTokenSeq		"setct-CapTokenSeq"
+#define NID_setct_CapTokenSeq		530
+#define OBJ_setct_CapTokenSeq		OBJ_set_ctype,11L
+
+#define SN_setct_PInitResData		"setct-PInitResData"
+#define NID_setct_PInitResData		531
+#define OBJ_setct_PInitResData		OBJ_set_ctype,12L
+
+#define SN_setct_PI_TBS		"setct-PI-TBS"
+#define NID_setct_PI_TBS		532
+#define OBJ_setct_PI_TBS		OBJ_set_ctype,13L
+
+#define SN_setct_PResData		"setct-PResData"
+#define NID_setct_PResData		533
+#define OBJ_setct_PResData		OBJ_set_ctype,14L
+
+#define SN_setct_AuthReqTBS		"setct-AuthReqTBS"
+#define NID_setct_AuthReqTBS		534
+#define OBJ_setct_AuthReqTBS		OBJ_set_ctype,16L
+
+#define SN_setct_AuthResTBS		"setct-AuthResTBS"
+#define NID_setct_AuthResTBS		535
+#define OBJ_setct_AuthResTBS		OBJ_set_ctype,17L
+
+#define SN_setct_AuthResTBSX		"setct-AuthResTBSX"
+#define NID_setct_AuthResTBSX		536
+#define OBJ_setct_AuthResTBSX		OBJ_set_ctype,18L
+
+#define SN_setct_AuthTokenTBS		"setct-AuthTokenTBS"
+#define NID_setct_AuthTokenTBS		537
+#define OBJ_setct_AuthTokenTBS		OBJ_set_ctype,19L
+
+#define SN_setct_CapTokenData		"setct-CapTokenData"
+#define NID_setct_CapTokenData		538
+#define OBJ_setct_CapTokenData		OBJ_set_ctype,20L
+
+#define SN_setct_CapTokenTBS		"setct-CapTokenTBS"
+#define NID_setct_CapTokenTBS		539
+#define OBJ_setct_CapTokenTBS		OBJ_set_ctype,21L
+
+#define SN_setct_AcqCardCodeMsg		"setct-AcqCardCodeMsg"
+#define NID_setct_AcqCardCodeMsg		540
+#define OBJ_setct_AcqCardCodeMsg		OBJ_set_ctype,22L
+
+#define SN_setct_AuthRevReqTBS		"setct-AuthRevReqTBS"
+#define NID_setct_AuthRevReqTBS		541
+#define OBJ_setct_AuthRevReqTBS		OBJ_set_ctype,23L
+
+#define SN_setct_AuthRevResData		"setct-AuthRevResData"
+#define NID_setct_AuthRevResData		542
+#define OBJ_setct_AuthRevResData		OBJ_set_ctype,24L
+
+#define SN_setct_AuthRevResTBS		"setct-AuthRevResTBS"
+#define NID_setct_AuthRevResTBS		543
+#define OBJ_setct_AuthRevResTBS		OBJ_set_ctype,25L
+
+#define SN_setct_CapReqTBS		"setct-CapReqTBS"
+#define NID_setct_CapReqTBS		544
+#define OBJ_setct_CapReqTBS		OBJ_set_ctype,26L
+
+#define SN_setct_CapReqTBSX		"setct-CapReqTBSX"
+#define NID_setct_CapReqTBSX		545
+#define OBJ_setct_CapReqTBSX		OBJ_set_ctype,27L
+
+#define SN_setct_CapResData		"setct-CapResData"
+#define NID_setct_CapResData		546
+#define OBJ_setct_CapResData		OBJ_set_ctype,28L
+
+#define SN_setct_CapRevReqTBS		"setct-CapRevReqTBS"
+#define NID_setct_CapRevReqTBS		547
+#define OBJ_setct_CapRevReqTBS		OBJ_set_ctype,29L
+
+#define SN_setct_CapRevReqTBSX		"setct-CapRevReqTBSX"
+#define NID_setct_CapRevReqTBSX		548
+#define OBJ_setct_CapRevReqTBSX		OBJ_set_ctype,30L
+
+#define SN_setct_CapRevResData		"setct-CapRevResData"
+#define NID_setct_CapRevResData		549
+#define OBJ_setct_CapRevResData		OBJ_set_ctype,31L
+
+#define SN_setct_CredReqTBS		"setct-CredReqTBS"
+#define NID_setct_CredReqTBS		550
+#define OBJ_setct_CredReqTBS		OBJ_set_ctype,32L
+
+#define SN_setct_CredReqTBSX		"setct-CredReqTBSX"
+#define NID_setct_CredReqTBSX		551
+#define OBJ_setct_CredReqTBSX		OBJ_set_ctype,33L
+
+#define SN_setct_CredResData		"setct-CredResData"
+#define NID_setct_CredResData		552
+#define OBJ_setct_CredResData		OBJ_set_ctype,34L
+
+#define SN_setct_CredRevReqTBS		"setct-CredRevReqTBS"
+#define NID_setct_CredRevReqTBS		553
+#define OBJ_setct_CredRevReqTBS		OBJ_set_ctype,35L
+
+#define SN_setct_CredRevReqTBSX		"setct-CredRevReqTBSX"
+#define NID_setct_CredRevReqTBSX		554
+#define OBJ_setct_CredRevReqTBSX		OBJ_set_ctype,36L
+
+#define SN_setct_CredRevResData		"setct-CredRevResData"
+#define NID_setct_CredRevResData		555
+#define OBJ_setct_CredRevResData		OBJ_set_ctype,37L
+
+#define SN_setct_PCertReqData		"setct-PCertReqData"
+#define NID_setct_PCertReqData		556
+#define OBJ_setct_PCertReqData		OBJ_set_ctype,38L
+
+#define SN_setct_PCertResTBS		"setct-PCertResTBS"
+#define NID_setct_PCertResTBS		557
+#define OBJ_setct_PCertResTBS		OBJ_set_ctype,39L
+
+#define SN_setct_BatchAdminReqData		"setct-BatchAdminReqData"
+#define NID_setct_BatchAdminReqData		558
+#define OBJ_setct_BatchAdminReqData		OBJ_set_ctype,40L
+
+#define SN_setct_BatchAdminResData		"setct-BatchAdminResData"
+#define NID_setct_BatchAdminResData		559
+#define OBJ_setct_BatchAdminResData		OBJ_set_ctype,41L
+
+#define SN_setct_CardCInitResTBS		"setct-CardCInitResTBS"
+#define NID_setct_CardCInitResTBS		560
+#define OBJ_setct_CardCInitResTBS		OBJ_set_ctype,42L
+
+#define SN_setct_MeAqCInitResTBS		"setct-MeAqCInitResTBS"
+#define NID_setct_MeAqCInitResTBS		561
+#define OBJ_setct_MeAqCInitResTBS		OBJ_set_ctype,43L
+
+#define SN_setct_RegFormResTBS		"setct-RegFormResTBS"
+#define NID_setct_RegFormResTBS		562
+#define OBJ_setct_RegFormResTBS		OBJ_set_ctype,44L
+
+#define SN_setct_CertReqData		"setct-CertReqData"
+#define NID_setct_CertReqData		563
+#define OBJ_setct_CertReqData		OBJ_set_ctype,45L
+
+#define SN_setct_CertReqTBS		"setct-CertReqTBS"
+#define NID_setct_CertReqTBS		564
+#define OBJ_setct_CertReqTBS		OBJ_set_ctype,46L
+
+#define SN_setct_CertResData		"setct-CertResData"
+#define NID_setct_CertResData		565
+#define OBJ_setct_CertResData		OBJ_set_ctype,47L
+
+#define SN_setct_CertInqReqTBS		"setct-CertInqReqTBS"
+#define NID_setct_CertInqReqTBS		566
+#define OBJ_setct_CertInqReqTBS		OBJ_set_ctype,48L
+
+#define SN_setct_ErrorTBS		"setct-ErrorTBS"
+#define NID_setct_ErrorTBS		567
+#define OBJ_setct_ErrorTBS		OBJ_set_ctype,49L
+
+#define SN_setct_PIDualSignedTBE		"setct-PIDualSignedTBE"
+#define NID_setct_PIDualSignedTBE		568
+#define OBJ_setct_PIDualSignedTBE		OBJ_set_ctype,50L
+
+#define SN_setct_PIUnsignedTBE		"setct-PIUnsignedTBE"
+#define NID_setct_PIUnsignedTBE		569
+#define OBJ_setct_PIUnsignedTBE		OBJ_set_ctype,51L
+
+#define SN_setct_AuthReqTBE		"setct-AuthReqTBE"
+#define NID_setct_AuthReqTBE		570
+#define OBJ_setct_AuthReqTBE		OBJ_set_ctype,52L
+
+#define SN_setct_AuthResTBE		"setct-AuthResTBE"
+#define NID_setct_AuthResTBE		571
+#define OBJ_setct_AuthResTBE		OBJ_set_ctype,53L
+
+#define SN_setct_AuthResTBEX		"setct-AuthResTBEX"
+#define NID_setct_AuthResTBEX		572
+#define OBJ_setct_AuthResTBEX		OBJ_set_ctype,54L
+
+#define SN_setct_AuthTokenTBE		"setct-AuthTokenTBE"
+#define NID_setct_AuthTokenTBE		573
+#define OBJ_setct_AuthTokenTBE		OBJ_set_ctype,55L
+
+#define SN_setct_CapTokenTBE		"setct-CapTokenTBE"
+#define NID_setct_CapTokenTBE		574
+#define OBJ_setct_CapTokenTBE		OBJ_set_ctype,56L
+
+#define SN_setct_CapTokenTBEX		"setct-CapTokenTBEX"
+#define NID_setct_CapTokenTBEX		575
+#define OBJ_setct_CapTokenTBEX		OBJ_set_ctype,57L
+
+#define SN_setct_AcqCardCodeMsgTBE		"setct-AcqCardCodeMsgTBE"
+#define NID_setct_AcqCardCodeMsgTBE		576
+#define OBJ_setct_AcqCardCodeMsgTBE		OBJ_set_ctype,58L
+
+#define SN_setct_AuthRevReqTBE		"setct-AuthRevReqTBE"
+#define NID_setct_AuthRevReqTBE		577
+#define OBJ_setct_AuthRevReqTBE		OBJ_set_ctype,59L
+
+#define SN_setct_AuthRevResTBE		"setct-AuthRevResTBE"
+#define NID_setct_AuthRevResTBE		578
+#define OBJ_setct_AuthRevResTBE		OBJ_set_ctype,60L
+
+#define SN_setct_AuthRevResTBEB		"setct-AuthRevResTBEB"
+#define NID_setct_AuthRevResTBEB		579
+#define OBJ_setct_AuthRevResTBEB		OBJ_set_ctype,61L
+
+#define SN_setct_CapReqTBE		"setct-CapReqTBE"
+#define NID_setct_CapReqTBE		580
+#define OBJ_setct_CapReqTBE		OBJ_set_ctype,62L
+
+#define SN_setct_CapReqTBEX		"setct-CapReqTBEX"
+#define NID_setct_CapReqTBEX		581
+#define OBJ_setct_CapReqTBEX		OBJ_set_ctype,63L
+
+#define SN_setct_CapResTBE		"setct-CapResTBE"
+#define NID_setct_CapResTBE		582
+#define OBJ_setct_CapResTBE		OBJ_set_ctype,64L
+
+#define SN_setct_CapRevReqTBE		"setct-CapRevReqTBE"
+#define NID_setct_CapRevReqTBE		583
+#define OBJ_setct_CapRevReqTBE		OBJ_set_ctype,65L
+
+#define SN_setct_CapRevReqTBEX		"setct-CapRevReqTBEX"
+#define NID_setct_CapRevReqTBEX		584
+#define OBJ_setct_CapRevReqTBEX		OBJ_set_ctype,66L
+
+#define SN_setct_CapRevResTBE		"setct-CapRevResTBE"
+#define NID_setct_CapRevResTBE		585
+#define OBJ_setct_CapRevResTBE		OBJ_set_ctype,67L
+
+#define SN_setct_CredReqTBE		"setct-CredReqTBE"
+#define NID_setct_CredReqTBE		586
+#define OBJ_setct_CredReqTBE		OBJ_set_ctype,68L
+
+#define SN_setct_CredReqTBEX		"setct-CredReqTBEX"
+#define NID_setct_CredReqTBEX		587
+#define OBJ_setct_CredReqTBEX		OBJ_set_ctype,69L
+
+#define SN_setct_CredResTBE		"setct-CredResTBE"
+#define NID_setct_CredResTBE		588
+#define OBJ_setct_CredResTBE		OBJ_set_ctype,70L
+
+#define SN_setct_CredRevReqTBE		"setct-CredRevReqTBE"
+#define NID_setct_CredRevReqTBE		589
+#define OBJ_setct_CredRevReqTBE		OBJ_set_ctype,71L
+
+#define SN_setct_CredRevReqTBEX		"setct-CredRevReqTBEX"
+#define NID_setct_CredRevReqTBEX		590
+#define OBJ_setct_CredRevReqTBEX		OBJ_set_ctype,72L
+
+#define SN_setct_CredRevResTBE		"setct-CredRevResTBE"
+#define NID_setct_CredRevResTBE		591
+#define OBJ_setct_CredRevResTBE		OBJ_set_ctype,73L
+
+#define SN_setct_BatchAdminReqTBE		"setct-BatchAdminReqTBE"
+#define NID_setct_BatchAdminReqTBE		592
+#define OBJ_setct_BatchAdminReqTBE		OBJ_set_ctype,74L
+
+#define SN_setct_BatchAdminResTBE		"setct-BatchAdminResTBE"
+#define NID_setct_BatchAdminResTBE		593
+#define OBJ_setct_BatchAdminResTBE		OBJ_set_ctype,75L
+
+#define SN_setct_RegFormReqTBE		"setct-RegFormReqTBE"
+#define NID_setct_RegFormReqTBE		594
+#define OBJ_setct_RegFormReqTBE		OBJ_set_ctype,76L
+
+#define SN_setct_CertReqTBE		"setct-CertReqTBE"
+#define NID_setct_CertReqTBE		595
+#define OBJ_setct_CertReqTBE		OBJ_set_ctype,77L
+
+#define SN_setct_CertReqTBEX		"setct-CertReqTBEX"
+#define NID_setct_CertReqTBEX		596
+#define OBJ_setct_CertReqTBEX		OBJ_set_ctype,78L
+
+#define SN_setct_CertResTBE		"setct-CertResTBE"
+#define NID_setct_CertResTBE		597
+#define OBJ_setct_CertResTBE		OBJ_set_ctype,79L
+
+#define SN_setct_CRLNotificationTBS		"setct-CRLNotificationTBS"
+#define NID_setct_CRLNotificationTBS		598
+#define OBJ_setct_CRLNotificationTBS		OBJ_set_ctype,80L
+
+#define SN_setct_CRLNotificationResTBS		"setct-CRLNotificationResTBS"
+#define NID_setct_CRLNotificationResTBS		599
+#define OBJ_setct_CRLNotificationResTBS		OBJ_set_ctype,81L
+
+#define SN_setct_BCIDistributionTBS		"setct-BCIDistributionTBS"
+#define NID_setct_BCIDistributionTBS		600
+#define OBJ_setct_BCIDistributionTBS		OBJ_set_ctype,82L
+
+#define SN_setext_genCrypt		"setext-genCrypt"
+#define LN_setext_genCrypt		"generic cryptogram"
+#define NID_setext_genCrypt		601
+#define OBJ_setext_genCrypt		OBJ_set_msgExt,1L
+
+#define SN_setext_miAuth		"setext-miAuth"
+#define LN_setext_miAuth		"merchant initiated auth"
+#define NID_setext_miAuth		602
+#define OBJ_setext_miAuth		OBJ_set_msgExt,3L
+
+#define SN_setext_pinSecure		"setext-pinSecure"
+#define NID_setext_pinSecure		603
+#define OBJ_setext_pinSecure		OBJ_set_msgExt,4L
+
+#define SN_setext_pinAny		"setext-pinAny"
+#define NID_setext_pinAny		604
+#define OBJ_setext_pinAny		OBJ_set_msgExt,5L
+
+#define SN_setext_track2		"setext-track2"
+#define NID_setext_track2		605
+#define OBJ_setext_track2		OBJ_set_msgExt,7L
+
+#define SN_setext_cv		"setext-cv"
+#define LN_setext_cv		"additional verification"
+#define NID_setext_cv		606
+#define OBJ_setext_cv		OBJ_set_msgExt,8L
+
+#define SN_set_policy_root		"set-policy-root"
+#define NID_set_policy_root		607
+#define OBJ_set_policy_root		OBJ_set_policy,0L
+
+#define SN_setCext_hashedRoot		"setCext-hashedRoot"
+#define NID_setCext_hashedRoot		608
+#define OBJ_setCext_hashedRoot		OBJ_set_certExt,0L
+
+#define SN_setCext_certType		"setCext-certType"
+#define NID_setCext_certType		609
+#define OBJ_setCext_certType		OBJ_set_certExt,1L
+
+#define SN_setCext_merchData		"setCext-merchData"
+#define NID_setCext_merchData		610
+#define OBJ_setCext_merchData		OBJ_set_certExt,2L
+
+#define SN_setCext_cCertRequired		"setCext-cCertRequired"
+#define NID_setCext_cCertRequired		611
+#define OBJ_setCext_cCertRequired		OBJ_set_certExt,3L
+
+#define SN_setCext_tunneling		"setCext-tunneling"
+#define NID_setCext_tunneling		612
+#define OBJ_setCext_tunneling		OBJ_set_certExt,4L
+
+#define SN_setCext_setExt		"setCext-setExt"
+#define NID_setCext_setExt		613
+#define OBJ_setCext_setExt		OBJ_set_certExt,5L
+
+#define SN_setCext_setQualf		"setCext-setQualf"
+#define NID_setCext_setQualf		614
+#define OBJ_setCext_setQualf		OBJ_set_certExt,6L
+
+#define SN_setCext_PGWYcapabilities		"setCext-PGWYcapabilities"
+#define NID_setCext_PGWYcapabilities		615
+#define OBJ_setCext_PGWYcapabilities		OBJ_set_certExt,7L
+
+#define SN_setCext_TokenIdentifier		"setCext-TokenIdentifier"
+#define NID_setCext_TokenIdentifier		616
+#define OBJ_setCext_TokenIdentifier		OBJ_set_certExt,8L
+
+#define SN_setCext_Track2Data		"setCext-Track2Data"
+#define NID_setCext_Track2Data		617
+#define OBJ_setCext_Track2Data		OBJ_set_certExt,9L
+
+#define SN_setCext_TokenType		"setCext-TokenType"
+#define NID_setCext_TokenType		618
+#define OBJ_setCext_TokenType		OBJ_set_certExt,10L
+
+#define SN_setCext_IssuerCapabilities		"setCext-IssuerCapabilities"
+#define NID_setCext_IssuerCapabilities		619
+#define OBJ_setCext_IssuerCapabilities		OBJ_set_certExt,11L
+
+#define SN_setAttr_Cert		"setAttr-Cert"
+#define NID_setAttr_Cert		620
+#define OBJ_setAttr_Cert		OBJ_set_attr,0L
+
+#define SN_setAttr_PGWYcap		"setAttr-PGWYcap"
+#define LN_setAttr_PGWYcap		"payment gateway capabilities"
+#define NID_setAttr_PGWYcap		621
+#define OBJ_setAttr_PGWYcap		OBJ_set_attr,1L
+
+#define SN_setAttr_TokenType		"setAttr-TokenType"
+#define NID_setAttr_TokenType		622
+#define OBJ_setAttr_TokenType		OBJ_set_attr,2L
+
+#define SN_setAttr_IssCap		"setAttr-IssCap"
+#define LN_setAttr_IssCap		"issuer capabilities"
+#define NID_setAttr_IssCap		623
+#define OBJ_setAttr_IssCap		OBJ_set_attr,3L
+
+#define SN_set_rootKeyThumb		"set-rootKeyThumb"
+#define NID_set_rootKeyThumb		624
+#define OBJ_set_rootKeyThumb		OBJ_setAttr_Cert,0L
+
+#define SN_set_addPolicy		"set-addPolicy"
+#define NID_set_addPolicy		625
+#define OBJ_set_addPolicy		OBJ_setAttr_Cert,1L
+
+#define SN_setAttr_Token_EMV		"setAttr-Token-EMV"
+#define NID_setAttr_Token_EMV		626
+#define OBJ_setAttr_Token_EMV		OBJ_setAttr_TokenType,1L
+
+#define SN_setAttr_Token_B0Prime		"setAttr-Token-B0Prime"
+#define NID_setAttr_Token_B0Prime		627
+#define OBJ_setAttr_Token_B0Prime		OBJ_setAttr_TokenType,2L
+
+#define SN_setAttr_IssCap_CVM		"setAttr-IssCap-CVM"
+#define NID_setAttr_IssCap_CVM		628
+#define OBJ_setAttr_IssCap_CVM		OBJ_setAttr_IssCap,3L
+
+#define SN_setAttr_IssCap_T2		"setAttr-IssCap-T2"
+#define NID_setAttr_IssCap_T2		629
+#define OBJ_setAttr_IssCap_T2		OBJ_setAttr_IssCap,4L
+
+#define SN_setAttr_IssCap_Sig		"setAttr-IssCap-Sig"
+#define NID_setAttr_IssCap_Sig		630
+#define OBJ_setAttr_IssCap_Sig		OBJ_setAttr_IssCap,5L
+
+#define SN_setAttr_GenCryptgrm		"setAttr-GenCryptgrm"
+#define LN_setAttr_GenCryptgrm		"generate cryptogram"
+#define NID_setAttr_GenCryptgrm		631
+#define OBJ_setAttr_GenCryptgrm		OBJ_setAttr_IssCap_CVM,1L
+
+#define SN_setAttr_T2Enc		"setAttr-T2Enc"
+#define LN_setAttr_T2Enc		"encrypted track 2"
+#define NID_setAttr_T2Enc		632
+#define OBJ_setAttr_T2Enc		OBJ_setAttr_IssCap_T2,1L
+
+#define SN_setAttr_T2cleartxt		"setAttr-T2cleartxt"
+#define LN_setAttr_T2cleartxt		"cleartext track 2"
+#define NID_setAttr_T2cleartxt		633
+#define OBJ_setAttr_T2cleartxt		OBJ_setAttr_IssCap_T2,2L
+
+#define SN_setAttr_TokICCsig		"setAttr-TokICCsig"
+#define LN_setAttr_TokICCsig		"ICC or token signature"
+#define NID_setAttr_TokICCsig		634
+#define OBJ_setAttr_TokICCsig		OBJ_setAttr_IssCap_Sig,1L
+
+#define SN_setAttr_SecDevSig		"setAttr-SecDevSig"
+#define LN_setAttr_SecDevSig		"secure device signature"
+#define NID_setAttr_SecDevSig		635
+#define OBJ_setAttr_SecDevSig		OBJ_setAttr_IssCap_Sig,2L
+
+#define SN_set_brand_IATA_ATA		"set-brand-IATA-ATA"
+#define NID_set_brand_IATA_ATA		636
+#define OBJ_set_brand_IATA_ATA		OBJ_set_brand,1L
+
+#define SN_set_brand_Diners		"set-brand-Diners"
+#define NID_set_brand_Diners		637
+#define OBJ_set_brand_Diners		OBJ_set_brand,30L
+
+#define SN_set_brand_AmericanExpress		"set-brand-AmericanExpress"
+#define NID_set_brand_AmericanExpress		638
+#define OBJ_set_brand_AmericanExpress		OBJ_set_brand,34L
+
+#define SN_set_brand_JCB		"set-brand-JCB"
+#define NID_set_brand_JCB		639
+#define OBJ_set_brand_JCB		OBJ_set_brand,35L
+
+#define SN_set_brand_Visa		"set-brand-Visa"
+#define NID_set_brand_Visa		640
+#define OBJ_set_brand_Visa		OBJ_set_brand,4L
+
+#define SN_set_brand_MasterCard		"set-brand-MasterCard"
+#define NID_set_brand_MasterCard		641
+#define OBJ_set_brand_MasterCard		OBJ_set_brand,5L
+
+#define SN_set_brand_Novus		"set-brand-Novus"
+#define NID_set_brand_Novus		642
+#define OBJ_set_brand_Novus		OBJ_set_brand,6011L
+
+#define SN_des_cdmf		"DES-CDMF"
+#define LN_des_cdmf		"des-cdmf"
+#define NID_des_cdmf		643
+#define OBJ_des_cdmf		OBJ_rsadsi,3L,10L
+
+#define SN_rsaOAEPEncryptionSET		"rsaOAEPEncryptionSET"
+#define NID_rsaOAEPEncryptionSET		644
+#define OBJ_rsaOAEPEncryptionSET		OBJ_rsadsi,1L,1L,6L
+
+#define SN_ipsec3		"Oakley-EC2N-3"
+#define LN_ipsec3		"ipsec3"
+#define NID_ipsec3		749
+
+#define SN_ipsec4		"Oakley-EC2N-4"
+#define LN_ipsec4		"ipsec4"
+#define NID_ipsec4		750
+
+#define SN_whirlpool		"whirlpool"
+#define NID_whirlpool		804
+#define OBJ_whirlpool		OBJ_iso,0L,10118L,3L,0L,55L
+
+#define SN_cryptopro		"cryptopro"
+#define NID_cryptopro		805
+#define OBJ_cryptopro		OBJ_member_body,643L,2L,2L
+
+#define SN_cryptocom		"cryptocom"
+#define NID_cryptocom		806
+#define OBJ_cryptocom		OBJ_member_body,643L,2L,9L
+
+#define SN_id_GostR3411_94_with_GostR3410_2001		"id-GostR3411-94-with-GostR3410-2001"
+#define LN_id_GostR3411_94_with_GostR3410_2001		"GOST R 34.11-94 with GOST R 34.10-2001"
+#define NID_id_GostR3411_94_with_GostR3410_2001		807
+#define OBJ_id_GostR3411_94_with_GostR3410_2001		OBJ_cryptopro,3L
+
+#define SN_id_GostR3411_94_with_GostR3410_94		"id-GostR3411-94-with-GostR3410-94"
+#define LN_id_GostR3411_94_with_GostR3410_94		"GOST R 34.11-94 with GOST R 34.10-94"
+#define NID_id_GostR3411_94_with_GostR3410_94		808
+#define OBJ_id_GostR3411_94_with_GostR3410_94		OBJ_cryptopro,4L
+
+#define SN_id_GostR3411_94		"md_gost94"
+#define LN_id_GostR3411_94		"GOST R 34.11-94"
+#define NID_id_GostR3411_94		809
+#define OBJ_id_GostR3411_94		OBJ_cryptopro,9L
+
+#define SN_id_HMACGostR3411_94		"id-HMACGostR3411-94"
+#define LN_id_HMACGostR3411_94		"HMAC GOST 34.11-94"
+#define NID_id_HMACGostR3411_94		810
+#define OBJ_id_HMACGostR3411_94		OBJ_cryptopro,10L
+
+#define SN_id_GostR3410_2001		"gost2001"
+#define LN_id_GostR3410_2001		"GOST R 34.10-2001"
+#define NID_id_GostR3410_2001		811
+#define OBJ_id_GostR3410_2001		OBJ_cryptopro,19L
+
+#define SN_id_GostR3410_94		"gost94"
+#define LN_id_GostR3410_94		"GOST R 34.10-94"
+#define NID_id_GostR3410_94		812
+#define OBJ_id_GostR3410_94		OBJ_cryptopro,20L
+
+#define SN_id_Gost28147_89		"gost89"
+#define LN_id_Gost28147_89		"GOST 28147-89"
+#define NID_id_Gost28147_89		813
+#define OBJ_id_Gost28147_89		OBJ_cryptopro,21L
+
+#define SN_gost89_cnt		"gost89-cnt"
+#define NID_gost89_cnt		814
+
+#define SN_id_Gost28147_89_MAC		"gost-mac"
+#define LN_id_Gost28147_89_MAC		"GOST 28147-89 MAC"
+#define NID_id_Gost28147_89_MAC		815
+#define OBJ_id_Gost28147_89_MAC		OBJ_cryptopro,22L
+
+#define SN_id_GostR3411_94_prf		"prf-gostr3411-94"
+#define LN_id_GostR3411_94_prf		"GOST R 34.11-94 PRF"
+#define NID_id_GostR3411_94_prf		816
+#define OBJ_id_GostR3411_94_prf		OBJ_cryptopro,23L
+
+#define SN_id_GostR3410_2001DH		"id-GostR3410-2001DH"
+#define LN_id_GostR3410_2001DH		"GOST R 34.10-2001 DH"
+#define NID_id_GostR3410_2001DH		817
+#define OBJ_id_GostR3410_2001DH		OBJ_cryptopro,98L
+
+#define SN_id_GostR3410_94DH		"id-GostR3410-94DH"
+#define LN_id_GostR3410_94DH		"GOST R 34.10-94 DH"
+#define NID_id_GostR3410_94DH		818
+#define OBJ_id_GostR3410_94DH		OBJ_cryptopro,99L
+
+#define SN_id_Gost28147_89_CryptoPro_KeyMeshing		"id-Gost28147-89-CryptoPro-KeyMeshing"
+#define NID_id_Gost28147_89_CryptoPro_KeyMeshing		819
+#define OBJ_id_Gost28147_89_CryptoPro_KeyMeshing		OBJ_cryptopro,14L,1L
+
+#define SN_id_Gost28147_89_None_KeyMeshing		"id-Gost28147-89-None-KeyMeshing"
+#define NID_id_Gost28147_89_None_KeyMeshing		820
+#define OBJ_id_Gost28147_89_None_KeyMeshing		OBJ_cryptopro,14L,0L
+
+#define SN_id_GostR3411_94_TestParamSet		"id-GostR3411-94-TestParamSet"
+#define NID_id_GostR3411_94_TestParamSet		821
+#define OBJ_id_GostR3411_94_TestParamSet		OBJ_cryptopro,30L,0L
+
+#define SN_id_GostR3411_94_CryptoProParamSet		"id-GostR3411-94-CryptoProParamSet"
+#define NID_id_GostR3411_94_CryptoProParamSet		822
+#define OBJ_id_GostR3411_94_CryptoProParamSet		OBJ_cryptopro,30L,1L
+
+#define SN_id_Gost28147_89_TestParamSet		"id-Gost28147-89-TestParamSet"
+#define NID_id_Gost28147_89_TestParamSet		823
+#define OBJ_id_Gost28147_89_TestParamSet		OBJ_cryptopro,31L,0L
+
+#define SN_id_Gost28147_89_CryptoPro_A_ParamSet		"id-Gost28147-89-CryptoPro-A-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_A_ParamSet		824
+#define OBJ_id_Gost28147_89_CryptoPro_A_ParamSet		OBJ_cryptopro,31L,1L
+
+#define SN_id_Gost28147_89_CryptoPro_B_ParamSet		"id-Gost28147-89-CryptoPro-B-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_B_ParamSet		825
+#define OBJ_id_Gost28147_89_CryptoPro_B_ParamSet		OBJ_cryptopro,31L,2L
+
+#define SN_id_Gost28147_89_CryptoPro_C_ParamSet		"id-Gost28147-89-CryptoPro-C-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_C_ParamSet		826
+#define OBJ_id_Gost28147_89_CryptoPro_C_ParamSet		OBJ_cryptopro,31L,3L
+
+#define SN_id_Gost28147_89_CryptoPro_D_ParamSet		"id-Gost28147-89-CryptoPro-D-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_D_ParamSet		827
+#define OBJ_id_Gost28147_89_CryptoPro_D_ParamSet		OBJ_cryptopro,31L,4L
+
+#define SN_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet		"id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet		828
+#define OBJ_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet		OBJ_cryptopro,31L,5L
+
+#define SN_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet		"id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet		829
+#define OBJ_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet		OBJ_cryptopro,31L,6L
+
+#define SN_id_Gost28147_89_CryptoPro_RIC_1_ParamSet		"id-Gost28147-89-CryptoPro-RIC-1-ParamSet"
+#define NID_id_Gost28147_89_CryptoPro_RIC_1_ParamSet		830
+#define OBJ_id_Gost28147_89_CryptoPro_RIC_1_ParamSet		OBJ_cryptopro,31L,7L
+
+#define SN_id_GostR3410_94_TestParamSet		"id-GostR3410-94-TestParamSet"
+#define NID_id_GostR3410_94_TestParamSet		831
+#define OBJ_id_GostR3410_94_TestParamSet		OBJ_cryptopro,32L,0L
+
+#define SN_id_GostR3410_94_CryptoPro_A_ParamSet		"id-GostR3410-94-CryptoPro-A-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_A_ParamSet		832
+#define OBJ_id_GostR3410_94_CryptoPro_A_ParamSet		OBJ_cryptopro,32L,2L
+
+#define SN_id_GostR3410_94_CryptoPro_B_ParamSet		"id-GostR3410-94-CryptoPro-B-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_B_ParamSet		833
+#define OBJ_id_GostR3410_94_CryptoPro_B_ParamSet		OBJ_cryptopro,32L,3L
+
+#define SN_id_GostR3410_94_CryptoPro_C_ParamSet		"id-GostR3410-94-CryptoPro-C-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_C_ParamSet		834
+#define OBJ_id_GostR3410_94_CryptoPro_C_ParamSet		OBJ_cryptopro,32L,4L
+
+#define SN_id_GostR3410_94_CryptoPro_D_ParamSet		"id-GostR3410-94-CryptoPro-D-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_D_ParamSet		835
+#define OBJ_id_GostR3410_94_CryptoPro_D_ParamSet		OBJ_cryptopro,32L,5L
+
+#define SN_id_GostR3410_94_CryptoPro_XchA_ParamSet		"id-GostR3410-94-CryptoPro-XchA-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_XchA_ParamSet		836
+#define OBJ_id_GostR3410_94_CryptoPro_XchA_ParamSet		OBJ_cryptopro,33L,1L
+
+#define SN_id_GostR3410_94_CryptoPro_XchB_ParamSet		"id-GostR3410-94-CryptoPro-XchB-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_XchB_ParamSet		837
+#define OBJ_id_GostR3410_94_CryptoPro_XchB_ParamSet		OBJ_cryptopro,33L,2L
+
+#define SN_id_GostR3410_94_CryptoPro_XchC_ParamSet		"id-GostR3410-94-CryptoPro-XchC-ParamSet"
+#define NID_id_GostR3410_94_CryptoPro_XchC_ParamSet		838
+#define OBJ_id_GostR3410_94_CryptoPro_XchC_ParamSet		OBJ_cryptopro,33L,3L
+
+#define SN_id_GostR3410_2001_TestParamSet		"id-GostR3410-2001-TestParamSet"
+#define NID_id_GostR3410_2001_TestParamSet		839
+#define OBJ_id_GostR3410_2001_TestParamSet		OBJ_cryptopro,35L,0L
+
+#define SN_id_GostR3410_2001_CryptoPro_A_ParamSet		"id-GostR3410-2001-CryptoPro-A-ParamSet"
+#define NID_id_GostR3410_2001_CryptoPro_A_ParamSet		840
+#define OBJ_id_GostR3410_2001_CryptoPro_A_ParamSet		OBJ_cryptopro,35L,1L
+
+#define SN_id_GostR3410_2001_CryptoPro_B_ParamSet		"id-GostR3410-2001-CryptoPro-B-ParamSet"
+#define NID_id_GostR3410_2001_CryptoPro_B_ParamSet		841
+#define OBJ_id_GostR3410_2001_CryptoPro_B_ParamSet		OBJ_cryptopro,35L,2L
+
+#define SN_id_GostR3410_2001_CryptoPro_C_ParamSet		"id-GostR3410-2001-CryptoPro-C-ParamSet"
+#define NID_id_GostR3410_2001_CryptoPro_C_ParamSet		842
+#define OBJ_id_GostR3410_2001_CryptoPro_C_ParamSet		OBJ_cryptopro,35L,3L
+
+#define SN_id_GostR3410_2001_CryptoPro_XchA_ParamSet		"id-GostR3410-2001-CryptoPro-XchA-ParamSet"
+#define NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet		843
+#define OBJ_id_GostR3410_2001_CryptoPro_XchA_ParamSet		OBJ_cryptopro,36L,0L
+
+#define SN_id_GostR3410_2001_CryptoPro_XchB_ParamSet		"id-GostR3410-2001-CryptoPro-XchB-ParamSet"
+#define NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet		844
+#define OBJ_id_GostR3410_2001_CryptoPro_XchB_ParamSet		OBJ_cryptopro,36L,1L
+
+#define SN_id_GostR3410_94_a		"id-GostR3410-94-a"
+#define NID_id_GostR3410_94_a		845
+#define OBJ_id_GostR3410_94_a		OBJ_id_GostR3410_94,1L
+
+#define SN_id_GostR3410_94_aBis		"id-GostR3410-94-aBis"
+#define NID_id_GostR3410_94_aBis		846
+#define OBJ_id_GostR3410_94_aBis		OBJ_id_GostR3410_94,2L
+
+#define SN_id_GostR3410_94_b		"id-GostR3410-94-b"
+#define NID_id_GostR3410_94_b		847
+#define OBJ_id_GostR3410_94_b		OBJ_id_GostR3410_94,3L
+
+#define SN_id_GostR3410_94_bBis		"id-GostR3410-94-bBis"
+#define NID_id_GostR3410_94_bBis		848
+#define OBJ_id_GostR3410_94_bBis		OBJ_id_GostR3410_94,4L
+
+#define SN_id_Gost28147_89_cc		"id-Gost28147-89-cc"
+#define LN_id_Gost28147_89_cc		"GOST 28147-89 Cryptocom ParamSet"
+#define NID_id_Gost28147_89_cc		849
+#define OBJ_id_Gost28147_89_cc		OBJ_cryptocom,1L,6L,1L
+
+#define SN_id_GostR3410_94_cc		"gost94cc"
+#define LN_id_GostR3410_94_cc		"GOST 34.10-94 Cryptocom"
+#define NID_id_GostR3410_94_cc		850
+#define OBJ_id_GostR3410_94_cc		OBJ_cryptocom,1L,5L,3L
+
+#define SN_id_GostR3410_2001_cc		"gost2001cc"
+#define LN_id_GostR3410_2001_cc		"GOST 34.10-2001 Cryptocom"
+#define NID_id_GostR3410_2001_cc		851
+#define OBJ_id_GostR3410_2001_cc		OBJ_cryptocom,1L,5L,4L
+
+#define SN_id_GostR3411_94_with_GostR3410_94_cc		"id-GostR3411-94-with-GostR3410-94-cc"
+#define LN_id_GostR3411_94_with_GostR3410_94_cc		"GOST R 34.11-94 with GOST R 34.10-94 Cryptocom"
+#define NID_id_GostR3411_94_with_GostR3410_94_cc		852
+#define OBJ_id_GostR3411_94_with_GostR3410_94_cc		OBJ_cryptocom,1L,3L,3L
+
+#define SN_id_GostR3411_94_with_GostR3410_2001_cc		"id-GostR3411-94-with-GostR3410-2001-cc"
+#define LN_id_GostR3411_94_with_GostR3410_2001_cc		"GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom"
+#define NID_id_GostR3411_94_with_GostR3410_2001_cc		853
+#define OBJ_id_GostR3411_94_with_GostR3410_2001_cc		OBJ_cryptocom,1L,3L,4L
+
+#define SN_id_GostR3410_2001_ParamSet_cc		"id-GostR3410-2001-ParamSet-cc"
+#define LN_id_GostR3410_2001_ParamSet_cc		"GOST R 3410-2001 Parameter Set Cryptocom"
+#define NID_id_GostR3410_2001_ParamSet_cc		854
+#define OBJ_id_GostR3410_2001_ParamSet_cc		OBJ_cryptocom,1L,8L,1L
+
+#define SN_camellia_128_cbc		"CAMELLIA-128-CBC"
+#define LN_camellia_128_cbc		"camellia-128-cbc"
+#define NID_camellia_128_cbc		751
+#define OBJ_camellia_128_cbc		1L,2L,392L,200011L,61L,1L,1L,1L,2L
+
+#define SN_camellia_192_cbc		"CAMELLIA-192-CBC"
+#define LN_camellia_192_cbc		"camellia-192-cbc"
+#define NID_camellia_192_cbc		752
+#define OBJ_camellia_192_cbc		1L,2L,392L,200011L,61L,1L,1L,1L,3L
+
+#define SN_camellia_256_cbc		"CAMELLIA-256-CBC"
+#define LN_camellia_256_cbc		"camellia-256-cbc"
+#define NID_camellia_256_cbc		753
+#define OBJ_camellia_256_cbc		1L,2L,392L,200011L,61L,1L,1L,1L,4L
+
+#define SN_id_camellia128_wrap		"id-camellia128-wrap"
+#define NID_id_camellia128_wrap		907
+#define OBJ_id_camellia128_wrap		1L,2L,392L,200011L,61L,1L,1L,3L,2L
+
+#define SN_id_camellia192_wrap		"id-camellia192-wrap"
+#define NID_id_camellia192_wrap		908
+#define OBJ_id_camellia192_wrap		1L,2L,392L,200011L,61L,1L,1L,3L,3L
+
+#define SN_id_camellia256_wrap		"id-camellia256-wrap"
+#define NID_id_camellia256_wrap		909
+#define OBJ_id_camellia256_wrap		1L,2L,392L,200011L,61L,1L,1L,3L,4L
+
+#define OBJ_ntt_ds		0L,3L,4401L,5L
+
+#define OBJ_camellia		OBJ_ntt_ds,3L,1L,9L
+
+#define SN_camellia_128_ecb		"CAMELLIA-128-ECB"
+#define LN_camellia_128_ecb		"camellia-128-ecb"
+#define NID_camellia_128_ecb		754
+#define OBJ_camellia_128_ecb		OBJ_camellia,1L
+
+#define SN_camellia_128_ofb128		"CAMELLIA-128-OFB"
+#define LN_camellia_128_ofb128		"camellia-128-ofb"
+#define NID_camellia_128_ofb128		766
+#define OBJ_camellia_128_ofb128		OBJ_camellia,3L
+
+#define SN_camellia_128_cfb128		"CAMELLIA-128-CFB"
+#define LN_camellia_128_cfb128		"camellia-128-cfb"
+#define NID_camellia_128_cfb128		757
+#define OBJ_camellia_128_cfb128		OBJ_camellia,4L
+
+#define SN_camellia_192_ecb		"CAMELLIA-192-ECB"
+#define LN_camellia_192_ecb		"camellia-192-ecb"
+#define NID_camellia_192_ecb		755
+#define OBJ_camellia_192_ecb		OBJ_camellia,21L
+
+#define SN_camellia_192_ofb128		"CAMELLIA-192-OFB"
+#define LN_camellia_192_ofb128		"camellia-192-ofb"
+#define NID_camellia_192_ofb128		767
+#define OBJ_camellia_192_ofb128		OBJ_camellia,23L
+
+#define SN_camellia_192_cfb128		"CAMELLIA-192-CFB"
+#define LN_camellia_192_cfb128		"camellia-192-cfb"
+#define NID_camellia_192_cfb128		758
+#define OBJ_camellia_192_cfb128		OBJ_camellia,24L
+
+#define SN_camellia_256_ecb		"CAMELLIA-256-ECB"
+#define LN_camellia_256_ecb		"camellia-256-ecb"
+#define NID_camellia_256_ecb		756
+#define OBJ_camellia_256_ecb		OBJ_camellia,41L
+
+#define SN_camellia_256_ofb128		"CAMELLIA-256-OFB"
+#define LN_camellia_256_ofb128		"camellia-256-ofb"
+#define NID_camellia_256_ofb128		768
+#define OBJ_camellia_256_ofb128		OBJ_camellia,43L
+
+#define SN_camellia_256_cfb128		"CAMELLIA-256-CFB"
+#define LN_camellia_256_cfb128		"camellia-256-cfb"
+#define NID_camellia_256_cfb128		759
+#define OBJ_camellia_256_cfb128		OBJ_camellia,44L
+
+#define SN_camellia_128_cfb1		"CAMELLIA-128-CFB1"
+#define LN_camellia_128_cfb1		"camellia-128-cfb1"
+#define NID_camellia_128_cfb1		760
+
+#define SN_camellia_192_cfb1		"CAMELLIA-192-CFB1"
+#define LN_camellia_192_cfb1		"camellia-192-cfb1"
+#define NID_camellia_192_cfb1		761
+
+#define SN_camellia_256_cfb1		"CAMELLIA-256-CFB1"
+#define LN_camellia_256_cfb1		"camellia-256-cfb1"
+#define NID_camellia_256_cfb1		762
+
+#define SN_camellia_128_cfb8		"CAMELLIA-128-CFB8"
+#define LN_camellia_128_cfb8		"camellia-128-cfb8"
+#define NID_camellia_128_cfb8		763
+
+#define SN_camellia_192_cfb8		"CAMELLIA-192-CFB8"
+#define LN_camellia_192_cfb8		"camellia-192-cfb8"
+#define NID_camellia_192_cfb8		764
+
+#define SN_camellia_256_cfb8		"CAMELLIA-256-CFB8"
+#define LN_camellia_256_cfb8		"camellia-256-cfb8"
+#define NID_camellia_256_cfb8		765
+
+#define SN_kisa		"KISA"
+#define LN_kisa		"kisa"
+#define NID_kisa		773
+#define OBJ_kisa		OBJ_member_body,410L,200004L
+
+#define SN_seed_ecb		"SEED-ECB"
+#define LN_seed_ecb		"seed-ecb"
+#define NID_seed_ecb		776
+#define OBJ_seed_ecb		OBJ_kisa,1L,3L
+
+#define SN_seed_cbc		"SEED-CBC"
+#define LN_seed_cbc		"seed-cbc"
+#define NID_seed_cbc		777
+#define OBJ_seed_cbc		OBJ_kisa,1L,4L
+
+#define SN_seed_cfb128		"SEED-CFB"
+#define LN_seed_cfb128		"seed-cfb"
+#define NID_seed_cfb128		779
+#define OBJ_seed_cfb128		OBJ_kisa,1L,5L
+
+#define SN_seed_ofb128		"SEED-OFB"
+#define LN_seed_ofb128		"seed-ofb"
+#define NID_seed_ofb128		778
+#define OBJ_seed_ofb128		OBJ_kisa,1L,6L
+
+#define SN_hmac		"HMAC"
+#define LN_hmac		"hmac"
+#define NID_hmac		855
+
+#define SN_cmac		"CMAC"
+#define LN_cmac		"cmac"
+#define NID_cmac		894
+
+#define SN_rc4_hmac_md5		"RC4-HMAC-MD5"
+#define LN_rc4_hmac_md5		"rc4-hmac-md5"
+#define NID_rc4_hmac_md5		915
+
+#define SN_aes_128_cbc_hmac_sha1		"AES-128-CBC-HMAC-SHA1"
+#define LN_aes_128_cbc_hmac_sha1		"aes-128-cbc-hmac-sha1"
+#define NID_aes_128_cbc_hmac_sha1		916
+
+#define SN_aes_192_cbc_hmac_sha1		"AES-192-CBC-HMAC-SHA1"
+#define LN_aes_192_cbc_hmac_sha1		"aes-192-cbc-hmac-sha1"
+#define NID_aes_192_cbc_hmac_sha1		917
+
+#define SN_aes_256_cbc_hmac_sha1		"AES-256-CBC-HMAC-SHA1"
+#define LN_aes_256_cbc_hmac_sha1		"aes-256-cbc-hmac-sha1"
+#define NID_aes_256_cbc_hmac_sha1		918
+
+#define SN_dhpublicnumber		"dhpublicnumber"
+#define LN_dhpublicnumber		"X9.42 DH"
+#define NID_dhpublicnumber		920
+#define OBJ_dhpublicnumber		OBJ_ISO_US,10046L,2L,1L
+
+#define SN_brainpoolP160r1		"brainpoolP160r1"
+#define NID_brainpoolP160r1		921
+#define OBJ_brainpoolP160r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,1L
+
+#define SN_brainpoolP160t1		"brainpoolP160t1"
+#define NID_brainpoolP160t1		922
+#define OBJ_brainpoolP160t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,2L
+
+#define SN_brainpoolP192r1		"brainpoolP192r1"
+#define NID_brainpoolP192r1		923
+#define OBJ_brainpoolP192r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,3L
+
+#define SN_brainpoolP192t1		"brainpoolP192t1"
+#define NID_brainpoolP192t1		924
+#define OBJ_brainpoolP192t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,4L
+
+#define SN_brainpoolP224r1		"brainpoolP224r1"
+#define NID_brainpoolP224r1		925
+#define OBJ_brainpoolP224r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,5L
+
+#define SN_brainpoolP224t1		"brainpoolP224t1"
+#define NID_brainpoolP224t1		926
+#define OBJ_brainpoolP224t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,6L
+
+#define SN_brainpoolP256r1		"brainpoolP256r1"
+#define NID_brainpoolP256r1		927
+#define OBJ_brainpoolP256r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,7L
+
+#define SN_brainpoolP256t1		"brainpoolP256t1"
+#define NID_brainpoolP256t1		928
+#define OBJ_brainpoolP256t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,8L
+
+#define SN_brainpoolP320r1		"brainpoolP320r1"
+#define NID_brainpoolP320r1		929
+#define OBJ_brainpoolP320r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,9L
+
+#define SN_brainpoolP320t1		"brainpoolP320t1"
+#define NID_brainpoolP320t1		930
+#define OBJ_brainpoolP320t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,10L
+
+#define SN_brainpoolP384r1		"brainpoolP384r1"
+#define NID_brainpoolP384r1		931
+#define OBJ_brainpoolP384r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,11L
+
+#define SN_brainpoolP384t1		"brainpoolP384t1"
+#define NID_brainpoolP384t1		932
+#define OBJ_brainpoolP384t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,12L
+
+#define SN_brainpoolP512r1		"brainpoolP512r1"
+#define NID_brainpoolP512r1		933
+#define OBJ_brainpoolP512r1		1L,3L,36L,3L,3L,2L,8L,1L,1L,13L
+
+#define SN_brainpoolP512t1		"brainpoolP512t1"
+#define NID_brainpoolP512t1		934
+#define OBJ_brainpoolP512t1		1L,3L,36L,3L,3L,2L,8L,1L,1L,14L
+
+#define OBJ_x9_63_scheme		1L,3L,133L,16L,840L,63L,0L
+
+#define OBJ_secg_scheme		OBJ_certicom_arc,1L
+
+#define SN_dhSinglePass_stdDH_sha1kdf_scheme		"dhSinglePass-stdDH-sha1kdf-scheme"
+#define NID_dhSinglePass_stdDH_sha1kdf_scheme		939
+#define OBJ_dhSinglePass_stdDH_sha1kdf_scheme		OBJ_x9_63_scheme,2L
+
+#define SN_dhSinglePass_stdDH_sha224kdf_scheme		"dhSinglePass-stdDH-sha224kdf-scheme"
+#define NID_dhSinglePass_stdDH_sha224kdf_scheme		940
+#define OBJ_dhSinglePass_stdDH_sha224kdf_scheme		OBJ_secg_scheme,11L,0L
+
+#define SN_dhSinglePass_stdDH_sha256kdf_scheme		"dhSinglePass-stdDH-sha256kdf-scheme"
+#define NID_dhSinglePass_stdDH_sha256kdf_scheme		941
+#define OBJ_dhSinglePass_stdDH_sha256kdf_scheme		OBJ_secg_scheme,11L,1L
+
+#define SN_dhSinglePass_stdDH_sha384kdf_scheme		"dhSinglePass-stdDH-sha384kdf-scheme"
+#define NID_dhSinglePass_stdDH_sha384kdf_scheme		942
+#define OBJ_dhSinglePass_stdDH_sha384kdf_scheme		OBJ_secg_scheme,11L,2L
+
+#define SN_dhSinglePass_stdDH_sha512kdf_scheme		"dhSinglePass-stdDH-sha512kdf-scheme"
+#define NID_dhSinglePass_stdDH_sha512kdf_scheme		943
+#define OBJ_dhSinglePass_stdDH_sha512kdf_scheme		OBJ_secg_scheme,11L,3L
+
+#define SN_dhSinglePass_cofactorDH_sha1kdf_scheme		"dhSinglePass-cofactorDH-sha1kdf-scheme"
+#define NID_dhSinglePass_cofactorDH_sha1kdf_scheme		944
+#define OBJ_dhSinglePass_cofactorDH_sha1kdf_scheme		OBJ_x9_63_scheme,3L
+
+#define SN_dhSinglePass_cofactorDH_sha224kdf_scheme		"dhSinglePass-cofactorDH-sha224kdf-scheme"
+#define NID_dhSinglePass_cofactorDH_sha224kdf_scheme		945
+#define OBJ_dhSinglePass_cofactorDH_sha224kdf_scheme		OBJ_secg_scheme,14L,0L
+
+#define SN_dhSinglePass_cofactorDH_sha256kdf_scheme		"dhSinglePass-cofactorDH-sha256kdf-scheme"
+#define NID_dhSinglePass_cofactorDH_sha256kdf_scheme		946
+#define OBJ_dhSinglePass_cofactorDH_sha256kdf_scheme		OBJ_secg_scheme,14L,1L
+
+#define SN_dhSinglePass_cofactorDH_sha384kdf_scheme		"dhSinglePass-cofactorDH-sha384kdf-scheme"
+#define NID_dhSinglePass_cofactorDH_sha384kdf_scheme		947
+#define OBJ_dhSinglePass_cofactorDH_sha384kdf_scheme		OBJ_secg_scheme,14L,2L
+
+#define SN_dhSinglePass_cofactorDH_sha512kdf_scheme		"dhSinglePass-cofactorDH-sha512kdf-scheme"
+#define NID_dhSinglePass_cofactorDH_sha512kdf_scheme		948
+#define OBJ_dhSinglePass_cofactorDH_sha512kdf_scheme		OBJ_secg_scheme,14L,3L
+
+#define SN_dh_std_kdf		"dh-std-kdf"
+#define NID_dh_std_kdf		949
+
+#define SN_dh_cofactor_kdf		"dh-cofactor-kdf"
+#define NID_dh_cofactor_kdf		950
+
diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num
new file mode 100644
index 0000000..dbca56a
--- /dev/null
+++ b/crypto/obj/obj_mac.num
@@ -0,0 +1,938 @@
+itu_t		1
+iso		2
+joint_iso_itu_t		3
+member_body		4
+identified_organization		5
+hmac_md5		6
+hmac_sha1		7
+certicom_arc		8
+international_organizations		9
+wap		10
+wap_wsg		11
+selected_attribute_types		12
+clearance		13
+ISO_US		14
+X9_57		15
+X9cm		16
+dsa		17
+dsaWithSHA1		18
+ansi_X9_62		19
+X9_62_prime_field		20
+X9_62_characteristic_two_field		21
+X9_62_id_characteristic_two_basis		22
+X9_62_onBasis		23
+X9_62_tpBasis		24
+X9_62_ppBasis		25
+X9_62_id_ecPublicKey		26
+X9_62_c2pnb163v1		27
+X9_62_c2pnb163v2		28
+X9_62_c2pnb163v3		29
+X9_62_c2pnb176v1		30
+X9_62_c2tnb191v1		31
+X9_62_c2tnb191v2		32
+X9_62_c2tnb191v3		33
+X9_62_c2onb191v4		34
+X9_62_c2onb191v5		35
+X9_62_c2pnb208w1		36
+X9_62_c2tnb239v1		37
+X9_62_c2tnb239v2		38
+X9_62_c2tnb239v3		39
+X9_62_c2onb239v4		40
+X9_62_c2onb239v5		41
+X9_62_c2pnb272w1		42
+X9_62_c2pnb304w1		43
+X9_62_c2tnb359v1		44
+X9_62_c2pnb368w1		45
+X9_62_c2tnb431r1		46
+X9_62_prime192v1		47
+X9_62_prime192v2		48
+X9_62_prime192v3		49
+X9_62_prime239v1		50
+X9_62_prime239v2		51
+X9_62_prime239v3		52
+X9_62_prime256v1		53
+ecdsa_with_SHA1		54
+ecdsa_with_Recommended		55
+ecdsa_with_Specified		56
+ecdsa_with_SHA224		57
+ecdsa_with_SHA256		58
+ecdsa_with_SHA384		59
+ecdsa_with_SHA512		60
+secp112r1		61
+secp112r2		62
+secp128r1		63
+secp128r2		64
+secp160k1		65
+secp160r1		66
+secp160r2		67
+secp192k1		68
+secp224k1		69
+secp224r1		70
+secp256k1		71
+secp384r1		72
+secp521r1		73
+sect113r1		74
+sect113r2		75
+sect131r1		76
+sect131r2		77
+sect163k1		78
+sect163r1		79
+sect163r2		80
+sect193r1		81
+sect193r2		82
+sect233k1		83
+sect233r1		84
+sect239k1		85
+sect283k1		86
+sect283r1		87
+sect409k1		88
+sect409r1		89
+sect571k1		90
+sect571r1		91
+wap_wsg_idm_ecid_wtls1		92
+wap_wsg_idm_ecid_wtls3		93
+wap_wsg_idm_ecid_wtls4		94
+wap_wsg_idm_ecid_wtls5		95
+wap_wsg_idm_ecid_wtls6		96
+wap_wsg_idm_ecid_wtls7		97
+wap_wsg_idm_ecid_wtls8		98
+wap_wsg_idm_ecid_wtls9		99
+wap_wsg_idm_ecid_wtls10		100
+wap_wsg_idm_ecid_wtls11		101
+wap_wsg_idm_ecid_wtls12		102
+cast5_cbc		103
+cast5_ecb		104
+cast5_cfb64		105
+cast5_ofb64		106
+pbeWithMD5AndCast5_CBC		107
+id_PasswordBasedMAC		108
+id_DHBasedMac		109
+rsadsi		110
+pkcs		111
+pkcs1		112
+rsaEncryption		113
+md2WithRSAEncryption		114
+md4WithRSAEncryption		115
+md5WithRSAEncryption		116
+sha1WithRSAEncryption		117
+rsaesOaep		118
+mgf1		119
+pSpecified		120
+rsassaPss		121
+sha256WithRSAEncryption		122
+sha384WithRSAEncryption		123
+sha512WithRSAEncryption		124
+sha224WithRSAEncryption		125
+pkcs3		126
+dhKeyAgreement		127
+pkcs5		128
+pbeWithMD2AndDES_CBC		129
+pbeWithMD5AndDES_CBC		130
+pbeWithMD2AndRC2_CBC		131
+pbeWithMD5AndRC2_CBC		132
+pbeWithSHA1AndDES_CBC		133
+pbeWithSHA1AndRC2_CBC		134
+id_pbkdf2		135
+pbes2		136
+pbmac1		137
+pkcs7		138
+pkcs7_data		139
+pkcs7_signed		140
+pkcs7_enveloped		141
+pkcs7_signedAndEnveloped		142
+pkcs7_digest		143
+pkcs7_encrypted		144
+pkcs9		145
+pkcs9_emailAddress		146
+pkcs9_unstructuredName		147
+pkcs9_contentType		148
+pkcs9_messageDigest		149
+pkcs9_signingTime		150
+pkcs9_countersignature		151
+pkcs9_challengePassword		152
+pkcs9_unstructuredAddress		153
+pkcs9_extCertAttributes		154
+ext_req		155
+SMIMECapabilities		156
+SMIME		157
+id_smime_mod		158
+id_smime_ct		159
+id_smime_aa		160
+id_smime_alg		161
+id_smime_cd		162
+id_smime_spq		163
+id_smime_cti		164
+id_smime_mod_cms		165
+id_smime_mod_ess		166
+id_smime_mod_oid		167
+id_smime_mod_msg_v3		168
+id_smime_mod_ets_eSignature_88		169
+id_smime_mod_ets_eSignature_97		170
+id_smime_mod_ets_eSigPolicy_88		171
+id_smime_mod_ets_eSigPolicy_97		172
+id_smime_ct_receipt		173
+id_smime_ct_authData		174
+id_smime_ct_publishCert		175
+id_smime_ct_TSTInfo		176
+id_smime_ct_TDTInfo		177
+id_smime_ct_contentInfo		178
+id_smime_ct_DVCSRequestData		179
+id_smime_ct_DVCSResponseData		180
+id_smime_ct_compressedData		181
+id_ct_asciiTextWithCRLF		182
+id_smime_aa_receiptRequest		183
+id_smime_aa_securityLabel		184
+id_smime_aa_mlExpandHistory		185
+id_smime_aa_contentHint		186
+id_smime_aa_msgSigDigest		187
+id_smime_aa_encapContentType		188
+id_smime_aa_contentIdentifier		189
+id_smime_aa_macValue		190
+id_smime_aa_equivalentLabels		191
+id_smime_aa_contentReference		192
+id_smime_aa_encrypKeyPref		193
+id_smime_aa_signingCertificate		194
+id_smime_aa_smimeEncryptCerts		195
+id_smime_aa_timeStampToken		196
+id_smime_aa_ets_sigPolicyId		197
+id_smime_aa_ets_commitmentType		198
+id_smime_aa_ets_signerLocation		199
+id_smime_aa_ets_signerAttr		200
+id_smime_aa_ets_otherSigCert		201
+id_smime_aa_ets_contentTimestamp		202
+id_smime_aa_ets_CertificateRefs		203
+id_smime_aa_ets_RevocationRefs		204
+id_smime_aa_ets_certValues		205
+id_smime_aa_ets_revocationValues		206
+id_smime_aa_ets_escTimeStamp		207
+id_smime_aa_ets_certCRLTimestamp		208
+id_smime_aa_ets_archiveTimeStamp		209
+id_smime_aa_signatureType		210
+id_smime_aa_dvcs_dvc		211
+id_smime_alg_ESDHwith3DES		212
+id_smime_alg_ESDHwithRC2		213
+id_smime_alg_3DESwrap		214
+id_smime_alg_RC2wrap		215
+id_smime_alg_ESDH		216
+id_smime_alg_CMS3DESwrap		217
+id_smime_alg_CMSRC2wrap		218
+id_alg_PWRI_KEK		219
+id_smime_cd_ldap		220
+id_smime_spq_ets_sqt_uri		221
+id_smime_spq_ets_sqt_unotice		222
+id_smime_cti_ets_proofOfOrigin		223
+id_smime_cti_ets_proofOfReceipt		224
+id_smime_cti_ets_proofOfDelivery		225
+id_smime_cti_ets_proofOfSender		226
+id_smime_cti_ets_proofOfApproval		227
+id_smime_cti_ets_proofOfCreation		228
+friendlyName		229
+localKeyID		230
+ms_csp_name		231
+LocalKeySet		232
+x509Certificate		233
+sdsiCertificate		234
+x509Crl		235
+pbe_WithSHA1And128BitRC4		236
+pbe_WithSHA1And40BitRC4		237
+pbe_WithSHA1And3_Key_TripleDES_CBC		238
+pbe_WithSHA1And2_Key_TripleDES_CBC		239
+pbe_WithSHA1And128BitRC2_CBC		240
+pbe_WithSHA1And40BitRC2_CBC		241
+keyBag		242
+pkcs8ShroudedKeyBag		243
+certBag		244
+crlBag		245
+secretBag		246
+safeContentsBag		247
+md2		248
+md4		249
+md5		250
+md5_sha1		251
+hmacWithMD5		252
+hmacWithSHA1		253
+hmacWithSHA224		254
+hmacWithSHA256		255
+hmacWithSHA384		256
+hmacWithSHA512		257
+rc2_cbc		258
+rc2_ecb		259
+rc2_cfb64		260
+rc2_ofb64		261
+rc2_40_cbc		262
+rc2_64_cbc		263
+rc4		264
+rc4_40		265
+des_ede3_cbc		266
+rc5_cbc		267
+rc5_ecb		268
+rc5_cfb64		269
+rc5_ofb64		270
+ms_ext_req		271
+ms_code_ind		272
+ms_code_com		273
+ms_ctl_sign		274
+ms_sgc		275
+ms_efs		276
+ms_smartcard_login		277
+ms_upn		278
+idea_cbc		279
+idea_ecb		280
+idea_cfb64		281
+idea_ofb64		282
+bf_cbc		283
+bf_ecb		284
+bf_cfb64		285
+bf_ofb64		286
+id_pkix		287
+id_pkix_mod		288
+id_pe		289
+id_qt		290
+id_kp		291
+id_it		292
+id_pkip		293
+id_alg		294
+id_cmc		295
+id_on		296
+id_pda		297
+id_aca		298
+id_qcs		299
+id_cct		300
+id_ppl		301
+id_ad		302
+id_pkix1_explicit_88		303
+id_pkix1_implicit_88		304
+id_pkix1_explicit_93		305
+id_pkix1_implicit_93		306
+id_mod_crmf		307
+id_mod_cmc		308
+id_mod_kea_profile_88		309
+id_mod_kea_profile_93		310
+id_mod_cmp		311
+id_mod_qualified_cert_88		312
+id_mod_qualified_cert_93		313
+id_mod_attribute_cert		314
+id_mod_timestamp_protocol		315
+id_mod_ocsp		316
+id_mod_dvcs		317
+id_mod_cmp2000		318
+info_access		319
+biometricInfo		320
+qcStatements		321
+ac_auditEntity		322
+ac_targeting		323
+aaControls		324
+sbgp_ipAddrBlock		325
+sbgp_autonomousSysNum		326
+sbgp_routerIdentifier		327
+ac_proxying		328
+sinfo_access		329
+proxyCertInfo		330
+id_qt_cps		331
+id_qt_unotice		332
+textNotice		333
+server_auth		334
+client_auth		335
+code_sign		336
+email_protect		337
+ipsecEndSystem		338
+ipsecTunnel		339
+ipsecUser		340
+time_stamp		341
+OCSP_sign		342
+dvcs		343
+id_it_caProtEncCert		344
+id_it_signKeyPairTypes		345
+id_it_encKeyPairTypes		346
+id_it_preferredSymmAlg		347
+id_it_caKeyUpdateInfo		348
+id_it_currentCRL		349
+id_it_unsupportedOIDs		350
+id_it_subscriptionRequest		351
+id_it_subscriptionResponse		352
+id_it_keyPairParamReq		353
+id_it_keyPairParamRep		354
+id_it_revPassphrase		355
+id_it_implicitConfirm		356
+id_it_confirmWaitTime		357
+id_it_origPKIMessage		358
+id_it_suppLangTags		359
+id_regCtrl		360
+id_regInfo		361
+id_regCtrl_regToken		362
+id_regCtrl_authenticator		363
+id_regCtrl_pkiPublicationInfo		364
+id_regCtrl_pkiArchiveOptions		365
+id_regCtrl_oldCertID		366
+id_regCtrl_protocolEncrKey		367
+id_regInfo_utf8Pairs		368
+id_regInfo_certReq		369
+id_alg_des40		370
+id_alg_noSignature		371
+id_alg_dh_sig_hmac_sha1		372
+id_alg_dh_pop		373
+id_cmc_statusInfo		374
+id_cmc_identification		375
+id_cmc_identityProof		376
+id_cmc_dataReturn		377
+id_cmc_transactionId		378
+id_cmc_senderNonce		379
+id_cmc_recipientNonce		380
+id_cmc_addExtensions		381
+id_cmc_encryptedPOP		382
+id_cmc_decryptedPOP		383
+id_cmc_lraPOPWitness		384
+id_cmc_getCert		385
+id_cmc_getCRL		386
+id_cmc_revokeRequest		387
+id_cmc_regInfo		388
+id_cmc_responseInfo		389
+id_cmc_queryPending		390
+id_cmc_popLinkRandom		391
+id_cmc_popLinkWitness		392
+id_cmc_confirmCertAcceptance		393
+id_on_personalData		394
+id_on_permanentIdentifier		395
+id_pda_dateOfBirth		396
+id_pda_placeOfBirth		397
+id_pda_gender		398
+id_pda_countryOfCitizenship		399
+id_pda_countryOfResidence		400
+id_aca_authenticationInfo		401
+id_aca_accessIdentity		402
+id_aca_chargingIdentity		403
+id_aca_group		404
+id_aca_role		405
+id_aca_encAttrs		406
+id_qcs_pkixQCSyntax_v1		407
+id_cct_crs		408
+id_cct_PKIData		409
+id_cct_PKIResponse		410
+id_ppl_anyLanguage		411
+id_ppl_inheritAll		412
+Independent		413
+ad_OCSP		414
+ad_ca_issuers		415
+ad_timeStamping		416
+ad_dvcs		417
+caRepository		418
+id_pkix_OCSP_basic		419
+id_pkix_OCSP_Nonce		420
+id_pkix_OCSP_CrlID		421
+id_pkix_OCSP_acceptableResponses		422
+id_pkix_OCSP_noCheck		423
+id_pkix_OCSP_archiveCutoff		424
+id_pkix_OCSP_serviceLocator		425
+id_pkix_OCSP_extendedStatus		426
+id_pkix_OCSP_valid		427
+id_pkix_OCSP_path		428
+id_pkix_OCSP_trustRoot		429
+algorithm		430
+md5WithRSA		431
+des_ecb		432
+des_cbc		433
+des_ofb64		434
+des_cfb64		435
+rsaSignature		436
+dsa_2		437
+dsaWithSHA		438
+shaWithRSAEncryption		439
+des_ede_ecb		440
+des_ede3_ecb		441
+des_ede_cbc		442
+des_ede_cfb64		443
+des_ede3_cfb64		444
+des_ede_ofb64		445
+des_ede3_ofb64		446
+desx_cbc		447
+sha		448
+sha1		449
+dsaWithSHA1_2		450
+sha1WithRSA		451
+ripemd160		452
+ripemd160WithRSA		453
+sxnet		454
+X500		455
+X509		456
+commonName		457
+surname		458
+serialNumber		459
+countryName		460
+localityName		461
+stateOrProvinceName		462
+streetAddress		463
+organizationName		464
+organizationalUnitName		465
+title		466
+description		467
+searchGuide		468
+businessCategory		469
+postalAddress		470
+postalCode		471
+postOfficeBox		472
+physicalDeliveryOfficeName		473
+telephoneNumber		474
+telexNumber		475
+teletexTerminalIdentifier		476
+facsimileTelephoneNumber		477
+x121Address		478
+internationaliSDNNumber		479
+registeredAddress		480
+destinationIndicator		481
+preferredDeliveryMethod		482
+presentationAddress		483
+supportedApplicationContext		484
+member		485
+owner		486
+roleOccupant		487
+seeAlso		488
+userPassword		489
+userCertificate		490
+cACertificate		491
+authorityRevocationList		492
+certificateRevocationList		493
+crossCertificatePair		494
+name		495
+givenName		496
+initials		497
+generationQualifier		498
+x500UniqueIdentifier		499
+dnQualifier		500
+enhancedSearchGuide		501
+protocolInformation		502
+distinguishedName		503
+uniqueMember		504
+houseIdentifier		505
+supportedAlgorithms		506
+deltaRevocationList		507
+dmdName		508
+pseudonym		509
+role		510
+X500algorithms		511
+rsa		512
+mdc2WithRSA		513
+mdc2		514
+id_ce		515
+subject_directory_attributes		516
+subject_key_identifier		517
+key_usage		518
+private_key_usage_period		519
+subject_alt_name		520
+issuer_alt_name		521
+basic_constraints		522
+crl_number		523
+crl_reason		524
+invalidity_date		525
+delta_crl		526
+issuing_distribution_point		527
+certificate_issuer		528
+name_constraints		529
+crl_distribution_points		530
+certificate_policies		531
+any_policy		532
+policy_mappings		533
+authority_key_identifier		534
+policy_constraints		535
+ext_key_usage		536
+freshest_crl		537
+inhibit_any_policy		538
+target_information		539
+no_rev_avail		540
+anyExtendedKeyUsage		541
+netscape		542
+netscape_cert_extension		543
+netscape_data_type		544
+netscape_cert_type		545
+netscape_base_url		546
+netscape_revocation_url		547
+netscape_ca_revocation_url		548
+netscape_renewal_url		549
+netscape_ca_policy_url		550
+netscape_ssl_server_name		551
+netscape_comment		552
+netscape_cert_sequence		553
+ns_sgc		554
+org		555
+dod		556
+iana		557
+Directory		558
+Management		559
+Experimental		560
+Private		561
+Security		562
+SNMPv2		563
+Mail		564
+Enterprises		565
+dcObject		566
+mime_mhs		567
+mime_mhs_headings		568
+mime_mhs_bodies		569
+id_hex_partial_message		570
+id_hex_multipart_message		571
+rle_compression		572
+zlib_compression		573
+aes_128_ecb		574
+aes_128_cbc		575
+aes_128_ofb128		576
+aes_128_cfb128		577
+id_aes128_wrap		578
+aes_128_gcm		579
+aes_128_ccm		580
+id_aes128_wrap_pad		581
+aes_192_ecb		582
+aes_192_cbc		583
+aes_192_ofb128		584
+aes_192_cfb128		585
+id_aes192_wrap		586
+aes_192_gcm		587
+aes_192_ccm		588
+id_aes192_wrap_pad		589
+aes_256_ecb		590
+aes_256_cbc		591
+aes_256_ofb128		592
+aes_256_cfb128		593
+id_aes256_wrap		594
+aes_256_gcm		595
+aes_256_ccm		596
+id_aes256_wrap_pad		597
+aes_128_cfb1		598
+aes_192_cfb1		599
+aes_256_cfb1		600
+aes_128_cfb8		601
+aes_192_cfb8		602
+aes_256_cfb8		603
+aes_128_ctr		604
+aes_192_ctr		605
+aes_256_ctr		606
+aes_128_xts		607
+aes_256_xts		608
+des_cfb1		609
+des_cfb8		610
+des_ede3_cfb1		611
+des_ede3_cfb8		612
+sha256		613
+sha384		614
+sha512		615
+sha224		616
+dsa_with_SHA224		617
+dsa_with_SHA256		618
+hold_instruction_code		619
+hold_instruction_none		620
+hold_instruction_call_issuer		621
+hold_instruction_reject		622
+data		623
+pss		624
+ucl		625
+pilot		626
+pilotAttributeType		627
+pilotAttributeSyntax		628
+pilotObjectClass		629
+pilotGroups		630
+iA5StringSyntax		631
+caseIgnoreIA5StringSyntax		632
+pilotObject		633
+pilotPerson		634
+account		635
+document		636
+room		637
+documentSeries		638
+Domain		639
+rFC822localPart		640
+dNSDomain		641
+domainRelatedObject		642
+friendlyCountry		643
+simpleSecurityObject		644
+pilotOrganization		645
+pilotDSA		646
+qualityLabelledData		647
+userId		648
+textEncodedORAddress		649
+rfc822Mailbox		650
+info		651
+favouriteDrink		652
+roomNumber		653
+photo		654
+userClass		655
+host		656
+manager		657
+documentIdentifier		658
+documentTitle		659
+documentVersion		660
+documentAuthor		661
+documentLocation		662
+homeTelephoneNumber		663
+secretary		664
+otherMailbox		665
+lastModifiedTime		666
+lastModifiedBy		667
+domainComponent		668
+aRecord		669
+pilotAttributeType27		670
+mXRecord		671
+nSRecord		672
+sOARecord		673
+cNAMERecord		674
+associatedDomain		675
+associatedName		676
+homePostalAddress		677
+personalTitle		678
+mobileTelephoneNumber		679
+pagerTelephoneNumber		680
+friendlyCountryName		681
+organizationalStatus		682
+janetMailbox		683
+mailPreferenceOption		684
+buildingName		685
+dSAQuality		686
+singleLevelQuality		687
+subtreeMinimumQuality		688
+subtreeMaximumQuality		689
+personalSignature		690
+dITRedirect		691
+audio		692
+documentPublisher		693
+id_set		694
+set_ctype		695
+set_msgExt		696
+set_attr		697
+set_policy		698
+set_certExt		699
+set_brand		700
+setct_PANData		701
+setct_PANToken		702
+setct_PANOnly		703
+setct_OIData		704
+setct_PI		705
+setct_PIData		706
+setct_PIDataUnsigned		707
+setct_HODInput		708
+setct_AuthResBaggage		709
+setct_AuthRevReqBaggage		710
+setct_AuthRevResBaggage		711
+setct_CapTokenSeq		712
+setct_PInitResData		713
+setct_PI_TBS		714
+setct_PResData		715
+setct_AuthReqTBS		716
+setct_AuthResTBS		717
+setct_AuthResTBSX		718
+setct_AuthTokenTBS		719
+setct_CapTokenData		720
+setct_CapTokenTBS		721
+setct_AcqCardCodeMsg		722
+setct_AuthRevReqTBS		723
+setct_AuthRevResData		724
+setct_AuthRevResTBS		725
+setct_CapReqTBS		726
+setct_CapReqTBSX		727
+setct_CapResData		728
+setct_CapRevReqTBS		729
+setct_CapRevReqTBSX		730
+setct_CapRevResData		731
+setct_CredReqTBS		732
+setct_CredReqTBSX		733
+setct_CredResData		734
+setct_CredRevReqTBS		735
+setct_CredRevReqTBSX		736
+setct_CredRevResData		737
+setct_PCertReqData		738
+setct_PCertResTBS		739
+setct_BatchAdminReqData		740
+setct_BatchAdminResData		741
+setct_CardCInitResTBS		742
+setct_MeAqCInitResTBS		743
+setct_RegFormResTBS		744
+setct_CertReqData		745
+setct_CertReqTBS		746
+setct_CertResData		747
+setct_CertInqReqTBS		748
+setct_ErrorTBS		749
+setct_PIDualSignedTBE		750
+setct_PIUnsignedTBE		751
+setct_AuthReqTBE		752
+setct_AuthResTBE		753
+setct_AuthResTBEX		754
+setct_AuthTokenTBE		755
+setct_CapTokenTBE		756
+setct_CapTokenTBEX		757
+setct_AcqCardCodeMsgTBE		758
+setct_AuthRevReqTBE		759
+setct_AuthRevResTBE		760
+setct_AuthRevResTBEB		761
+setct_CapReqTBE		762
+setct_CapReqTBEX		763
+setct_CapResTBE		764
+setct_CapRevReqTBE		765
+setct_CapRevReqTBEX		766
+setct_CapRevResTBE		767
+setct_CredReqTBE		768
+setct_CredReqTBEX		769
+setct_CredResTBE		770
+setct_CredRevReqTBE		771
+setct_CredRevReqTBEX		772
+setct_CredRevResTBE		773
+setct_BatchAdminReqTBE		774
+setct_BatchAdminResTBE		775
+setct_RegFormReqTBE		776
+setct_CertReqTBE		777
+setct_CertReqTBEX		778
+setct_CertResTBE		779
+setct_CRLNotificationTBS		780
+setct_CRLNotificationResTBS		781
+setct_BCIDistributionTBS		782
+setext_genCrypt		783
+setext_miAuth		784
+setext_pinSecure		785
+setext_pinAny		786
+setext_track2		787
+setext_cv		788
+set_policy_root		789
+setCext_hashedRoot		790
+setCext_certType		791
+setCext_merchData		792
+setCext_cCertRequired		793
+setCext_tunneling		794
+setCext_setExt		795
+setCext_setQualf		796
+setCext_PGWYcapabilities		797
+setCext_TokenIdentifier		798
+setCext_Track2Data		799
+setCext_TokenType		800
+setCext_IssuerCapabilities		801
+setAttr_Cert		802
+setAttr_PGWYcap		803
+setAttr_TokenType		804
+setAttr_IssCap		805
+set_rootKeyThumb		806
+set_addPolicy		807
+setAttr_Token_EMV		808
+setAttr_Token_B0Prime		809
+setAttr_IssCap_CVM		810
+setAttr_IssCap_T2		811
+setAttr_IssCap_Sig		812
+setAttr_GenCryptgrm		813
+setAttr_T2Enc		814
+setAttr_T2cleartxt		815
+setAttr_TokICCsig		816
+setAttr_SecDevSig		817
+set_brand_IATA_ATA		818
+set_brand_Diners		819
+set_brand_AmericanExpress		820
+set_brand_JCB		821
+set_brand_Visa		822
+set_brand_MasterCard		823
+set_brand_Novus		824
+des_cdmf		825
+rsaOAEPEncryptionSET		826
+ipsec3		827
+ipsec4		828
+whirlpool		829
+cryptopro		830
+cryptocom		831
+id_GostR3411_94_with_GostR3410_2001		832
+id_GostR3411_94_with_GostR3410_94		833
+id_GostR3411_94		834
+id_HMACGostR3411_94		835
+id_GostR3410_2001		836
+id_GostR3410_94		837
+id_Gost28147_89		838
+gost89_cnt		839
+id_Gost28147_89_MAC		840
+id_GostR3411_94_prf		841
+id_GostR3410_2001DH		842
+id_GostR3410_94DH		843
+id_Gost28147_89_CryptoPro_KeyMeshing		844
+id_Gost28147_89_None_KeyMeshing		845
+id_GostR3411_94_TestParamSet		846
+id_GostR3411_94_CryptoProParamSet		847
+id_Gost28147_89_TestParamSet		848
+id_Gost28147_89_CryptoPro_A_ParamSet		849
+id_Gost28147_89_CryptoPro_B_ParamSet		850
+id_Gost28147_89_CryptoPro_C_ParamSet		851
+id_Gost28147_89_CryptoPro_D_ParamSet		852
+id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet		853
+id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet		854
+id_Gost28147_89_CryptoPro_RIC_1_ParamSet		855
+id_GostR3410_94_TestParamSet		856
+id_GostR3410_94_CryptoPro_A_ParamSet		857
+id_GostR3410_94_CryptoPro_B_ParamSet		858
+id_GostR3410_94_CryptoPro_C_ParamSet		859
+id_GostR3410_94_CryptoPro_D_ParamSet		860
+id_GostR3410_94_CryptoPro_XchA_ParamSet		861
+id_GostR3410_94_CryptoPro_XchB_ParamSet		862
+id_GostR3410_94_CryptoPro_XchC_ParamSet		863
+id_GostR3410_2001_TestParamSet		864
+id_GostR3410_2001_CryptoPro_A_ParamSet		865
+id_GostR3410_2001_CryptoPro_B_ParamSet		866
+id_GostR3410_2001_CryptoPro_C_ParamSet		867
+id_GostR3410_2001_CryptoPro_XchA_ParamSet		868
+id_GostR3410_2001_CryptoPro_XchB_ParamSet		869
+id_GostR3410_94_a		870
+id_GostR3410_94_aBis		871
+id_GostR3410_94_b		872
+id_GostR3410_94_bBis		873
+id_Gost28147_89_cc		874
+id_GostR3410_94_cc		875
+id_GostR3410_2001_cc		876
+id_GostR3411_94_with_GostR3410_94_cc		877
+id_GostR3411_94_with_GostR3410_2001_cc		878
+id_GostR3410_2001_ParamSet_cc		879
+camellia_128_cbc		880
+camellia_192_cbc		881
+camellia_256_cbc		882
+id_camellia128_wrap		883
+id_camellia192_wrap		884
+id_camellia256_wrap		885
+camellia_128_ecb		886
+camellia_128_ofb128		887
+camellia_128_cfb128		888
+camellia_192_ecb		889
+camellia_192_ofb128		890
+camellia_192_cfb128		891
+camellia_256_ecb		892
+camellia_256_ofb128		893
+camellia_256_cfb128		894
+camellia_128_cfb1		895
+camellia_192_cfb1		896
+camellia_256_cfb1		897
+camellia_128_cfb8		898
+camellia_192_cfb8		899
+camellia_256_cfb8		900
+kisa		901
+seed_ecb		902
+seed_cbc		903
+seed_cfb128		904
+seed_ofb128		905
+hmac		906
+cmac		907
+rc4_hmac_md5		908
+aes_128_cbc_hmac_sha1		909
+aes_192_cbc_hmac_sha1		910
+aes_256_cbc_hmac_sha1		911
+dhpublicnumber		912
+brainpoolP160r1		913
+brainpoolP160t1		914
+brainpoolP192r1		915
+brainpoolP192t1		916
+brainpoolP224r1		917
+brainpoolP224t1		918
+brainpoolP256r1		919
+brainpoolP256t1		920
+brainpoolP320r1		921
+brainpoolP320t1		922
+brainpoolP384r1		923
+brainpoolP384t1		924
+brainpoolP512r1		925
+brainpoolP512t1		926
+dhSinglePass_stdDH_sha1kdf_scheme		927
+dhSinglePass_stdDH_sha224kdf_scheme		928
+dhSinglePass_stdDH_sha256kdf_scheme		929
+dhSinglePass_stdDH_sha384kdf_scheme		930
+dhSinglePass_stdDH_sha512kdf_scheme		931
+dhSinglePass_cofactorDH_sha1kdf_scheme		932
+dhSinglePass_cofactorDH_sha224kdf_scheme		933
+dhSinglePass_cofactorDH_sha256kdf_scheme		934
+dhSinglePass_cofactorDH_sha384kdf_scheme		935
+dhSinglePass_cofactorDH_sha512kdf_scheme		936
+dh_std_kdf		937
+dh_cofactor_kdf		938
diff --git a/crypto/obj/obj_xref.c b/crypto/obj/obj_xref.c
new file mode 100644
index 0000000..70babea
--- /dev/null
+++ b/crypto/obj/obj_xref.c
@@ -0,0 +1,124 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/obj.h>
+
+#include <stdlib.h>
+
+#include "obj_xref.h"
+
+
+static int nid_triple_cmp_by_sign_id(const void *in_a, const void *in_b) {
+  const nid_triple *a = in_a;
+  const nid_triple *b = in_b;
+
+  return a->sign_id - b->sign_id;
+}
+
+int OBJ_find_sigid_algs(int sign_nid, int *out_digest_nid, int *out_pkey_nid) {
+  nid_triple key;
+  const nid_triple *triple;
+
+  key.sign_id = sign_nid;
+
+  triple = bsearch(&key, sigoid_srt, sizeof(sigoid_srt) / sizeof(nid_triple),
+                   sizeof(nid_triple), nid_triple_cmp_by_sign_id);
+
+  if (triple == NULL) {
+    return 0;
+  }
+  if (out_digest_nid) {
+    *out_digest_nid = triple->hash_id;
+  }
+  if (out_pkey_nid) {
+    *out_pkey_nid = triple->pkey_id;
+  }
+
+  return 1;
+}
+
+static int nid_triple_cmp_by_digest_and_hash(const void *in_a,
+                                             const void *in_b) {
+  const nid_triple *a = *((nid_triple**) in_a);
+  const nid_triple *b = *((nid_triple**) in_b);
+
+  int ret = a->hash_id - b->hash_id;
+  if (ret) {
+    return ret;
+  }
+  return a->pkey_id - b->pkey_id;
+}
+
+int OBJ_find_sigid_by_algs(int *out_sign_nid, int digest_nid, int pkey_nid) {
+  nid_triple key, *pkey;
+  const nid_triple **triple;
+
+  key.hash_id = digest_nid;
+  key.pkey_id = pkey_nid;
+  pkey = &key;
+
+  triple = bsearch(&pkey, sigoid_srt_xref,
+                   sizeof(sigoid_srt_xref) / sizeof(nid_triple *),
+                   sizeof(nid_triple *), nid_triple_cmp_by_digest_and_hash);
+
+  if (triple == NULL) {
+    return 0;
+  }
+  if (out_sign_nid) {
+    *out_sign_nid = (*triple)->sign_id;
+  }
+  return 1;
+}
diff --git a/crypto/obj/obj_xref.h b/crypto/obj/obj_xref.h
new file mode 100644
index 0000000..cfd628a
--- /dev/null
+++ b/crypto/obj/obj_xref.h
@@ -0,0 +1,97 @@
+/* AUTOGENERATED BY objxref.pl, DO NOT EDIT */
+
+typedef struct
+	{
+	int sign_id;
+	int hash_id;
+	int pkey_id;
+	} nid_triple;
+
+static const nid_triple sigoid_srt[] =
+	{
+	{NID_md2WithRSAEncryption, NID_md2, NID_rsaEncryption},
+	{NID_md5WithRSAEncryption, NID_md5, NID_rsaEncryption},
+	{NID_shaWithRSAEncryption, NID_sha, NID_rsaEncryption},
+	{NID_sha1WithRSAEncryption, NID_sha1, NID_rsaEncryption},
+	{NID_dsaWithSHA, NID_sha, NID_dsa},
+	{NID_dsaWithSHA1_2, NID_sha1, NID_dsa_2},
+	{NID_mdc2WithRSA, NID_mdc2, NID_rsaEncryption},
+	{NID_md5WithRSA, NID_md5, NID_rsa},
+	{NID_dsaWithSHA1, NID_sha1, NID_dsa},
+	{NID_sha1WithRSA, NID_sha1, NID_rsa},
+	{NID_ripemd160WithRSA, NID_ripemd160, NID_rsaEncryption},
+	{NID_md4WithRSAEncryption, NID_md4, NID_rsaEncryption},
+	{NID_ecdsa_with_SHA1, NID_sha1, NID_X9_62_id_ecPublicKey},
+	{NID_sha256WithRSAEncryption, NID_sha256, NID_rsaEncryption},
+	{NID_sha384WithRSAEncryption, NID_sha384, NID_rsaEncryption},
+	{NID_sha512WithRSAEncryption, NID_sha512, NID_rsaEncryption},
+	{NID_sha224WithRSAEncryption, NID_sha224, NID_rsaEncryption},
+	{NID_ecdsa_with_Recommended, NID_undef, NID_X9_62_id_ecPublicKey},
+	{NID_ecdsa_with_Specified, NID_undef, NID_X9_62_id_ecPublicKey},
+	{NID_ecdsa_with_SHA224, NID_sha224, NID_X9_62_id_ecPublicKey},
+	{NID_ecdsa_with_SHA256, NID_sha256, NID_X9_62_id_ecPublicKey},
+	{NID_ecdsa_with_SHA384, NID_sha384, NID_X9_62_id_ecPublicKey},
+	{NID_ecdsa_with_SHA512, NID_sha512, NID_X9_62_id_ecPublicKey},
+	{NID_dsa_with_SHA224, NID_sha224, NID_dsa},
+	{NID_dsa_with_SHA256, NID_sha256, NID_dsa},
+	{NID_id_GostR3411_94_with_GostR3410_2001, NID_id_GostR3411_94, NID_id_GostR3410_2001},
+	{NID_id_GostR3411_94_with_GostR3410_94, NID_id_GostR3411_94, NID_id_GostR3410_94},
+	{NID_id_GostR3411_94_with_GostR3410_94_cc, NID_id_GostR3411_94, NID_id_GostR3410_94_cc},
+	{NID_id_GostR3411_94_with_GostR3410_2001_cc, NID_id_GostR3411_94, NID_id_GostR3410_2001_cc},
+	{NID_rsassaPss, NID_undef, NID_rsaEncryption},
+	{NID_dhSinglePass_stdDH_sha1kdf_scheme, NID_sha1, NID_dh_std_kdf},
+	{NID_dhSinglePass_stdDH_sha224kdf_scheme, NID_sha224, NID_dh_std_kdf},
+	{NID_dhSinglePass_stdDH_sha256kdf_scheme, NID_sha256, NID_dh_std_kdf},
+	{NID_dhSinglePass_stdDH_sha384kdf_scheme, NID_sha384, NID_dh_std_kdf},
+	{NID_dhSinglePass_stdDH_sha512kdf_scheme, NID_sha512, NID_dh_std_kdf},
+	{NID_dhSinglePass_cofactorDH_sha1kdf_scheme, NID_sha1, NID_dh_cofactor_kdf},
+	{NID_dhSinglePass_cofactorDH_sha224kdf_scheme, NID_sha224, NID_dh_cofactor_kdf},
+	{NID_dhSinglePass_cofactorDH_sha256kdf_scheme, NID_sha256, NID_dh_cofactor_kdf},
+	{NID_dhSinglePass_cofactorDH_sha384kdf_scheme, NID_sha384, NID_dh_cofactor_kdf},
+	{NID_dhSinglePass_cofactorDH_sha512kdf_scheme, NID_sha512, NID_dh_cofactor_kdf},
+	};
+
+static const nid_triple * const sigoid_srt_xref[] =
+	{
+	&sigoid_srt[29],
+	&sigoid_srt[17],
+	&sigoid_srt[18],
+	&sigoid_srt[0],
+	&sigoid_srt[1],
+	&sigoid_srt[7],
+	&sigoid_srt[2],
+	&sigoid_srt[4],
+	&sigoid_srt[3],
+	&sigoid_srt[9],
+	&sigoid_srt[5],
+	&sigoid_srt[8],
+	&sigoid_srt[12],
+	&sigoid_srt[30],
+	&sigoid_srt[35],
+	&sigoid_srt[6],
+	&sigoid_srt[10],
+	&sigoid_srt[11],
+	&sigoid_srt[13],
+	&sigoid_srt[24],
+	&sigoid_srt[20],
+	&sigoid_srt[32],
+	&sigoid_srt[37],
+	&sigoid_srt[14],
+	&sigoid_srt[21],
+	&sigoid_srt[33],
+	&sigoid_srt[38],
+	&sigoid_srt[15],
+	&sigoid_srt[22],
+	&sigoid_srt[34],
+	&sigoid_srt[39],
+	&sigoid_srt[16],
+	&sigoid_srt[23],
+	&sigoid_srt[19],
+	&sigoid_srt[31],
+	&sigoid_srt[36],
+	&sigoid_srt[25],
+	&sigoid_srt[26],
+	&sigoid_srt[27],
+	&sigoid_srt[28],
+	};
+
diff --git a/crypto/obj/obj_xref.pl b/crypto/obj/obj_xref.pl
new file mode 100644
index 0000000..731d3ae
--- /dev/null
+++ b/crypto/obj/obj_xref.pl
@@ -0,0 +1,107 @@
+#!/usr/local/bin/perl
+
+use strict;
+
+my %xref_tbl;
+my %oid_tbl;
+
+my ($mac_file, $xref_file) = @ARGV;
+
+open(IN, $mac_file) || die "Can't open $mac_file";
+
+# Read in OID nid values for a lookup table.
+
+while (<IN>)
+	{
+	chomp;
+	my ($name, $num) = /^(\S+)\s+(\S+)$/;
+	$oid_tbl{$name} = $num;
+	}
+close IN;
+
+open(IN, $xref_file) || die "Can't open $xref_file";
+
+my $ln = 1;
+
+while (<IN>)
+	{
+	chomp;
+	s/#.*$//;
+	next if (/^\S*$/);
+	my ($xr, $p1, $p2) = /^(\S+)\s+(\S+)\s+(\S+)/;
+	check_oid($xr);
+	check_oid($p1);
+	check_oid($p2);
+	$xref_tbl{$xr} = [$p1, $p2, $ln];
+	}
+
+my @xrkeys = keys %xref_tbl;
+
+my @srt1 = sort { $oid_tbl{$a} <=> $oid_tbl{$b}} @xrkeys;
+
+for(my $i = 0; $i <= $#srt1; $i++)
+	{
+	$xref_tbl{$srt1[$i]}[2] = $i;
+	}
+
+my @srt2 = sort
+	{
+	my$ap1 = $oid_tbl{$xref_tbl{$a}[0]};
+	my$bp1 = $oid_tbl{$xref_tbl{$b}[0]};
+	return $ap1 - $bp1 if ($ap1 != $bp1);
+	my$ap2 = $oid_tbl{$xref_tbl{$a}[1]};
+	my$bp2 = $oid_tbl{$xref_tbl{$b}[1]};
+
+	return $ap2 - $bp2;
+	} @xrkeys;
+
+my $pname = $0;
+
+$pname =~ s|^.[^/]/||;
+
+print <<EOF;
+/* AUTOGENERATED BY $pname, DO NOT EDIT */
+
+typedef struct
+	{
+	int sign_id;
+	int hash_id;
+	int pkey_id;
+	} nid_triple;
+
+static const nid_triple sigoid_srt[] =
+	{
+EOF
+
+foreach (@srt1)
+	{
+	my $xr = $_;
+	my ($p1, $p2) = @{$xref_tbl{$_}};
+	print "\t{NID_$xr, NID_$p1, NID_$p2},\n";
+	}
+
+print "\t};";
+print <<EOF;
+
+
+static const nid_triple * const sigoid_srt_xref[] =
+	{
+EOF
+
+foreach (@srt2)
+	{
+	my $x = $xref_tbl{$_}[2];
+	print "\t\&sigoid_srt\[$x\],\n";
+	}
+
+print "\t};\n\n";
+
+sub check_oid
+	{
+	my ($chk) = @_;
+	if (!exists $oid_tbl{$chk})
+		{
+		die "Not Found \"$chk\"\n";
+		}
+	}
+
diff --git a/crypto/obj/obj_xref.txt b/crypto/obj/obj_xref.txt
new file mode 100644
index 0000000..19c9422
--- /dev/null
+++ b/crypto/obj/obj_xref.txt
@@ -0,0 +1,58 @@
+# OID cross reference table.
+# Links signatures OIDs to their corresponding public key algorithms
+# and digests.
+
+md2WithRSAEncryption	md2	rsaEncryption
+md5WithRSAEncryption	md5	rsaEncryption
+shaWithRSAEncryption	sha	rsaEncryption
+sha1WithRSAEncryption	sha1	rsaEncryption
+md4WithRSAEncryption	md4	rsaEncryption
+sha256WithRSAEncryption sha256	rsaEncryption
+sha384WithRSAEncryption	sha384	rsaEncryption
+sha512WithRSAEncryption	sha512	rsaEncryption
+sha224WithRSAEncryption	sha224	rsaEncryption
+mdc2WithRSA		mdc2	rsaEncryption
+ripemd160WithRSA	ripemd160 rsaEncryption
+# For PSS the digest algorithm can vary and depends on the included
+# AlgorithmIdentifier. The digest "undef" indicates the public key
+# method should handle this explicitly.
+rsassaPss		undef	rsaEncryption
+
+# Alternative deprecated OIDs. By using the older "rsa" OID this
+# type will be recognized by not normally used.
+
+md5WithRSA		md5	rsa
+sha1WithRSA		sha1	rsa
+
+dsaWithSHA		sha	dsa
+dsaWithSHA1		sha1	dsa
+
+dsaWithSHA1_2		sha1	dsa_2
+
+ecdsa_with_SHA1		sha1	X9_62_id_ecPublicKey
+ecdsa_with_SHA224	sha224	X9_62_id_ecPublicKey
+ecdsa_with_SHA256	sha256	X9_62_id_ecPublicKey
+ecdsa_with_SHA384	sha384	X9_62_id_ecPublicKey
+ecdsa_with_SHA512	sha512	X9_62_id_ecPublicKey
+ecdsa_with_Recommended	undef	X9_62_id_ecPublicKey
+ecdsa_with_Specified	undef	X9_62_id_ecPublicKey
+
+dsa_with_SHA224		sha224	dsa
+dsa_with_SHA256		sha256	dsa
+
+id_GostR3411_94_with_GostR3410_2001	id_GostR3411_94 id_GostR3410_2001
+id_GostR3411_94_with_GostR3410_94	id_GostR3411_94 id_GostR3410_94
+id_GostR3411_94_with_GostR3410_94_cc	id_GostR3411_94 id_GostR3410_94_cc
+id_GostR3411_94_with_GostR3410_2001_cc	id_GostR3411_94 id_GostR3410_2001_cc
+# ECDH KDFs and their corresponding message digests and schemes
+dhSinglePass_stdDH_sha1kdf_scheme		sha1	dh_std_kdf
+dhSinglePass_stdDH_sha224kdf_scheme		sha224	dh_std_kdf
+dhSinglePass_stdDH_sha256kdf_scheme		sha256	dh_std_kdf
+dhSinglePass_stdDH_sha384kdf_scheme		sha384	dh_std_kdf
+dhSinglePass_stdDH_sha512kdf_scheme		sha512	dh_std_kdf
+
+dhSinglePass_cofactorDH_sha1kdf_scheme		sha1	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha224kdf_scheme	sha224	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha256kdf_scheme	sha256	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha384kdf_scheme	sha384	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha512kdf_scheme	sha512	dh_cofactor_kdf
diff --git a/crypto/obj/objects.pl b/crypto/obj/objects.pl
new file mode 100644
index 0000000..4b8b394
--- /dev/null
+++ b/crypto/obj/objects.pl
@@ -0,0 +1,228 @@
+#!/usr/local/bin/perl
+
+open (NUMIN,"$ARGV[1]") || die "Can't open number file $ARGV[1]";
+$max_nid=0;
+$o=0;
+while(<NUMIN>)
+	{
+	chop;
+	$o++;
+	s/#.*$//;
+	next if /^\s*$/;
+	$_ = 'X'.$_;
+	($Cname,$mynum) = split;
+	$Cname =~ s/^X//;
+	if (defined($nidn{$mynum}))
+		{ die "$ARGV[1]:$o:There's already an object with NID ",$mynum," on line ",$order{$mynum},"\n"; }
+	if (defined($nid{$Cname}))
+		{ die "$ARGV[1]:$o:There's already an object with name ",$Cname," on line ",$order{$nid{$Cname}},"\n"; }
+	$nid{$Cname} = $mynum;
+	$nidn{$mynum} = $Cname;
+	$order{$mynum} = $o;
+	$max_nid = $mynum if $mynum > $max_nid;
+	}
+close NUMIN;
+
+open (IN,"$ARGV[0]") || die "Can't open input file $ARGV[0]";
+$Cname="";
+$o=0;
+while (<IN>)
+	{
+	chop;
+	$o++;
+        if (/^!module\s+(.*)$/)
+		{
+		$module = $1."-";
+		$module =~ s/\./_/g;
+		$module =~ s/-/_/g;
+		}
+        if (/^!global$/)
+		{ $module = ""; }
+	if (/^!Cname\s+(.*)$/)
+		{ $Cname = $1; }
+	if (/^!Alias\s+(.+?)\s+(.*)$/)
+		{
+		$Cname = $module.$1;
+		$myoid = $2;
+		$myoid = &process_oid($myoid);
+		$Cname =~ s/-/_/g;
+		$ordern{$o} = $Cname;
+		$order{$Cname} = $o;
+		$obj{$Cname} = $myoid;
+		$_ = "";
+		$Cname = "";
+		}
+	s/!.*$//;
+	s/#.*$//;
+	next if /^\s*$/;
+	($myoid,$mysn,$myln) = split ':';
+	$mysn =~ s/^\s*//;
+	$mysn =~ s/\s*$//;
+	$myln =~ s/^\s*//;
+	$myln =~ s/\s*$//;
+	$myoid =~ s/^\s*//;
+	$myoid =~ s/\s*$//;
+	if ($myoid ne "")
+		{
+		$myoid = &process_oid($myoid);
+		}
+
+	if ($Cname eq "" && !($myln =~ / /))
+		{
+		$Cname = $myln;
+		$Cname =~ s/\./_/g;
+		$Cname =~ s/-/_/g;
+		if ($Cname ne "" && defined($ln{$module.$Cname}))
+			{ die "objects.txt:$o:There's already an object with long name ",$ln{$module.$Cname}," on line ",$order{$module.$Cname},"\n"; }
+		}
+	if ($Cname eq "")
+		{
+		$Cname = $mysn;
+		$Cname =~ s/-/_/g;
+		if ($Cname ne "" && defined($sn{$module.$Cname}))
+			{ die "objects.txt:$o:There's already an object with short name ",$sn{$module.$Cname}," on line ",$order{$module.$Cname},"\n"; }
+		}
+	if ($Cname eq "")
+		{
+		$Cname = $myln;
+		$Cname =~ s/-/_/g;
+		$Cname =~ s/\./_/g;
+		$Cname =~ s/ /_/g;
+		if ($Cname ne "" && defined($ln{$module.$Cname}))
+			{ die "objects.txt:$o:There's already an object with long name ",$ln{$module.$Cname}," on line ",$order{$module.$Cname},"\n"; }
+		}
+	$Cname =~ s/\./_/g;
+	$Cname =~ s/-/_/g;
+	$Cname = $module.$Cname;
+	$ordern{$o} = $Cname;
+	$order{$Cname} = $o;
+	$sn{$Cname} = $mysn;
+	$ln{$Cname} = $myln;
+	$obj{$Cname} = $myoid;
+	if (!defined($nid{$Cname}))
+		{
+		$max_nid++;
+		$nid{$Cname} = $max_nid;
+		$nidn{$max_nid} = $Cname;
+print STDERR "Added OID $Cname\n";
+		}
+	$Cname="";
+	}
+close IN;
+
+open (NUMOUT,">$ARGV[1]") || die "Can't open output file $ARGV[1]";
+foreach (sort { $a <=> $b } keys %nidn)
+	{
+	print NUMOUT $nidn{$_},"\t\t",$_,"\n";
+	}
+close NUMOUT;
+
+open (OUT,">$ARGV[2]") || die "Can't open output file $ARGV[2]";
+print OUT <<'EOF';
+/* THIS FILE IS GENERATED FROM objects.txt by objects.pl via the
+ * following command:
+ * perl objects.pl objects.txt obj_mac.num obj_mac.h */
+
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#define SN_undef			"UNDEF"
+#define LN_undef			"undefined"
+#define NID_undef			0
+#define OBJ_undef			0L
+
+EOF
+
+foreach (sort { $a <=> $b } keys %ordern)
+	{
+	$Cname=$ordern{$_};
+	print OUT "#define SN_",$Cname,"\t\t\"",$sn{$Cname},"\"\n" if $sn{$Cname} ne "";
+	print OUT "#define LN_",$Cname,"\t\t\"",$ln{$Cname},"\"\n" if $ln{$Cname} ne "";
+	print OUT "#define NID_",$Cname,"\t\t",$nid{$Cname},"\n" if $nid{$Cname} ne "";
+	print OUT "#define OBJ_",$Cname,"\t\t",$obj{$Cname},"\n" if $obj{$Cname} ne "";
+	print OUT "\n";
+	}
+
+close OUT;
+
+sub process_oid
+	{
+	local($oid)=@_;
+	local(@a,$oid_pref);
+
+	@a = split(/\s+/,$myoid);
+	$pref_oid = "";
+	$pref_sep = "";
+	if (!($a[0] =~ /^[0-9]+$/))
+		{
+		$a[0] =~ s/-/_/g;
+		if (!defined($obj{$a[0]}))
+			{ die "$ARGV[0]:$o:Undefined identifier ",$a[0],"\n"; }
+		$pref_oid = "OBJ_" . $a[0];
+		$pref_sep = ",";
+		shift @a;
+		}
+	$oids = join('L,',@a) . "L";
+	if ($oids ne "L")
+		{
+		$oids = $pref_oid . $pref_sep . $oids;
+		}
+	else
+		{
+		$oids = $pref_oid;
+		}
+	return($oids);
+	}
diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt
new file mode 100644
index 0000000..aeffc6c
--- /dev/null
+++ b/crypto/obj/objects.txt
@@ -0,0 +1,1334 @@
+# CCITT was renamed to ITU-T quite some time ago
+0			: ITU-T			: itu-t
+!Alias ccitt itu-t
+
+1			: ISO			: iso
+
+2			: JOINT-ISO-ITU-T	: joint-iso-itu-t
+!Alias joint-iso-ccitt joint-iso-itu-t
+
+iso 2			: member-body		: ISO Member Body
+
+iso 3			: identified-organization
+
+# HMAC OIDs
+identified-organization 6 1 5 5 8 1 1	: HMAC-MD5	: hmac-md5
+identified-organization 6 1 5 5 8 1 2	: HMAC-SHA1	: hmac-sha1
+
+identified-organization 132	: certicom-arc
+
+joint-iso-itu-t 23	: international-organizations	: International Organizations
+
+international-organizations 43	: wap
+wap 1			: wap-wsg
+
+joint-iso-itu-t 5 1 5	: selected-attribute-types	: Selected Attribute Types
+
+selected-attribute-types 55	: clearance
+
+member-body 840		: ISO-US		: ISO US Member Body
+ISO-US 10040		: X9-57			: X9.57
+X9-57 4			: X9cm			: X9.57 CM ?
+
+!Cname dsa
+X9cm 1			: DSA			: dsaEncryption
+X9cm 3			: DSA-SHA1		: dsaWithSHA1
+
+
+ISO-US 10045		: ansi-X9-62		: ANSI X9.62
+!module X9-62
+!Alias id-fieldType ansi-X9-62 1
+X9-62_id-fieldType 1		: prime-field
+X9-62_id-fieldType 2		: characteristic-two-field
+X9-62_characteristic-two-field 3 : id-characteristic-two-basis
+X9-62_id-characteristic-two-basis 1 : onBasis
+X9-62_id-characteristic-two-basis 2 : tpBasis
+X9-62_id-characteristic-two-basis 3 : ppBasis
+!Alias id-publicKeyType ansi-X9-62 2
+X9-62_id-publicKeyType 1	: id-ecPublicKey
+!Alias ellipticCurve ansi-X9-62 3
+!Alias c-TwoCurve X9-62_ellipticCurve 0
+X9-62_c-TwoCurve 1		: c2pnb163v1
+X9-62_c-TwoCurve 2		: c2pnb163v2
+X9-62_c-TwoCurve 3		: c2pnb163v3
+X9-62_c-TwoCurve 4		: c2pnb176v1
+X9-62_c-TwoCurve 5		: c2tnb191v1
+X9-62_c-TwoCurve 6		: c2tnb191v2
+X9-62_c-TwoCurve 7		: c2tnb191v3
+X9-62_c-TwoCurve 8		: c2onb191v4
+X9-62_c-TwoCurve 9		: c2onb191v5
+X9-62_c-TwoCurve 10		: c2pnb208w1
+X9-62_c-TwoCurve 11		: c2tnb239v1
+X9-62_c-TwoCurve 12		: c2tnb239v2
+X9-62_c-TwoCurve 13		: c2tnb239v3
+X9-62_c-TwoCurve 14		: c2onb239v4
+X9-62_c-TwoCurve 15		: c2onb239v5
+X9-62_c-TwoCurve 16		: c2pnb272w1
+X9-62_c-TwoCurve 17		: c2pnb304w1
+X9-62_c-TwoCurve 18		: c2tnb359v1
+X9-62_c-TwoCurve 19		: c2pnb368w1
+X9-62_c-TwoCurve 20		: c2tnb431r1
+!Alias primeCurve X9-62_ellipticCurve 1
+X9-62_primeCurve 1	 	: prime192v1
+X9-62_primeCurve 2	 	: prime192v2
+X9-62_primeCurve 3	 	: prime192v3
+X9-62_primeCurve 4	 	: prime239v1
+X9-62_primeCurve 5	 	: prime239v2
+X9-62_primeCurve 6	 	: prime239v3
+X9-62_primeCurve 7	 	: prime256v1
+!Alias id-ecSigType ansi-X9-62 4
+!global
+X9-62_id-ecSigType 1		: ecdsa-with-SHA1
+X9-62_id-ecSigType 2		: ecdsa-with-Recommended
+X9-62_id-ecSigType 3		: ecdsa-with-Specified
+ecdsa-with-Specified 1		: ecdsa-with-SHA224
+ecdsa-with-Specified 2		: ecdsa-with-SHA256
+ecdsa-with-Specified 3		: ecdsa-with-SHA384
+ecdsa-with-Specified 4		: ecdsa-with-SHA512
+
+# SECG curve OIDs from "SEC 2: Recommended Elliptic Curve Domain Parameters"
+# (http://www.secg.org/)
+!Alias secg_ellipticCurve certicom-arc 0
+# SECG prime curves OIDs
+secg-ellipticCurve 6		: secp112r1
+secg-ellipticCurve 7		: secp112r2
+secg-ellipticCurve 28		: secp128r1
+secg-ellipticCurve 29		: secp128r2
+secg-ellipticCurve 9		: secp160k1
+secg-ellipticCurve 8		: secp160r1
+secg-ellipticCurve 30		: secp160r2
+secg-ellipticCurve 31		: secp192k1
+# NOTE: the curve secp192r1 is the same as prime192v1 defined above
+#       and is therefore omitted
+secg-ellipticCurve 32		: secp224k1
+secg-ellipticCurve 33		: secp224r1
+secg-ellipticCurve 10		: secp256k1
+# NOTE: the curve secp256r1 is the same as prime256v1 defined above
+#       and is therefore omitted
+secg-ellipticCurve 34		: secp384r1
+secg-ellipticCurve 35		: secp521r1
+# SECG characteristic two curves OIDs
+secg-ellipticCurve 4		: sect113r1
+secg-ellipticCurve 5		: sect113r2
+secg-ellipticCurve 22		: sect131r1
+secg-ellipticCurve 23		: sect131r2
+secg-ellipticCurve 1		: sect163k1
+secg-ellipticCurve 2		: sect163r1
+secg-ellipticCurve 15		: sect163r2
+secg-ellipticCurve 24		: sect193r1
+secg-ellipticCurve 25		: sect193r2
+secg-ellipticCurve 26		: sect233k1
+secg-ellipticCurve 27		: sect233r1
+secg-ellipticCurve 3		: sect239k1
+secg-ellipticCurve 16		: sect283k1
+secg-ellipticCurve 17		: sect283r1
+secg-ellipticCurve 36		: sect409k1
+secg-ellipticCurve 37		: sect409r1
+secg-ellipticCurve 38		: sect571k1
+secg-ellipticCurve 39		: sect571r1
+
+# WAP/TLS curve OIDs (http://www.wapforum.org/)
+!Alias wap-wsg-idm-ecid wap-wsg 4
+wap-wsg-idm-ecid 1	: wap-wsg-idm-ecid-wtls1
+wap-wsg-idm-ecid 3	: wap-wsg-idm-ecid-wtls3
+wap-wsg-idm-ecid 4	: wap-wsg-idm-ecid-wtls4
+wap-wsg-idm-ecid 5	: wap-wsg-idm-ecid-wtls5
+wap-wsg-idm-ecid 6	: wap-wsg-idm-ecid-wtls6
+wap-wsg-idm-ecid 7	: wap-wsg-idm-ecid-wtls7
+wap-wsg-idm-ecid 8	: wap-wsg-idm-ecid-wtls8
+wap-wsg-idm-ecid 9	: wap-wsg-idm-ecid-wtls9
+wap-wsg-idm-ecid 10	: wap-wsg-idm-ecid-wtls10
+wap-wsg-idm-ecid 11	: wap-wsg-idm-ecid-wtls11
+wap-wsg-idm-ecid 12	: wap-wsg-idm-ecid-wtls12
+
+
+ISO-US 113533 7 66 10	: CAST5-CBC		: cast5-cbc
+			: CAST5-ECB		: cast5-ecb
+!Cname cast5-cfb64
+			: CAST5-CFB		: cast5-cfb
+!Cname cast5-ofb64
+			: CAST5-OFB		: cast5-ofb
+!Cname pbeWithMD5AndCast5-CBC
+ISO-US 113533 7 66 12	:			: pbeWithMD5AndCast5CBC
+
+# Macs for CMP and CRMF
+ISO-US 113533 7 66 13	: id-PasswordBasedMAC	: password based MAC
+ISO-US 113533 7 66 30	: id-DHBasedMac		: Diffie-Hellman based MAC
+
+ISO-US 113549		: rsadsi		: RSA Data Security, Inc.
+
+rsadsi 1		: pkcs			: RSA Data Security, Inc. PKCS
+
+pkcs 1			: pkcs1
+pkcs1 1			:			: rsaEncryption
+pkcs1 2			: RSA-MD2		: md2WithRSAEncryption
+pkcs1 3			: RSA-MD4		: md4WithRSAEncryption
+pkcs1 4			: RSA-MD5		: md5WithRSAEncryption
+pkcs1 5			: RSA-SHA1		: sha1WithRSAEncryption
+# According to PKCS #1 version 2.1
+pkcs1 7			: RSAES-OAEP		: rsaesOaep
+pkcs1 8			: MGF1			: mgf1
+pkcs1 9			: PSPECIFIED		: pSpecified
+pkcs1 10		: RSASSA-PSS		: rsassaPss
+
+pkcs1 11		: RSA-SHA256		: sha256WithRSAEncryption
+pkcs1 12		: RSA-SHA384		: sha384WithRSAEncryption
+pkcs1 13		: RSA-SHA512		: sha512WithRSAEncryption
+pkcs1 14		: RSA-SHA224		: sha224WithRSAEncryption
+
+pkcs 3			: pkcs3
+pkcs3 1			:			: dhKeyAgreement
+
+pkcs 5			: pkcs5
+pkcs5 1			: PBE-MD2-DES		: pbeWithMD2AndDES-CBC
+pkcs5 3			: PBE-MD5-DES		: pbeWithMD5AndDES-CBC
+pkcs5 4			: PBE-MD2-RC2-64	: pbeWithMD2AndRC2-CBC
+pkcs5 6			: PBE-MD5-RC2-64	: pbeWithMD5AndRC2-CBC
+pkcs5 10		: PBE-SHA1-DES		: pbeWithSHA1AndDES-CBC
+pkcs5 11		: PBE-SHA1-RC2-64	: pbeWithSHA1AndRC2-CBC
+!Cname id_pbkdf2
+pkcs5 12		:			: PBKDF2
+!Cname pbes2
+pkcs5 13		:			: PBES2
+!Cname pbmac1
+pkcs5 14		:			: PBMAC1
+
+pkcs 7			: pkcs7
+pkcs7 1			:			: pkcs7-data
+!Cname pkcs7-signed
+pkcs7 2			:			: pkcs7-signedData
+!Cname pkcs7-enveloped
+pkcs7 3			:			: pkcs7-envelopedData
+!Cname pkcs7-signedAndEnveloped
+pkcs7 4			:			: pkcs7-signedAndEnvelopedData
+!Cname pkcs7-digest
+pkcs7 5			:			: pkcs7-digestData
+!Cname pkcs7-encrypted
+pkcs7 6			:			: pkcs7-encryptedData
+
+pkcs 9			: pkcs9
+!module pkcs9
+pkcs9 1			: 			: emailAddress
+pkcs9 2			:			: unstructuredName
+pkcs9 3			:			: contentType
+pkcs9 4			:			: messageDigest
+pkcs9 5			:			: signingTime
+pkcs9 6			:			: countersignature
+pkcs9 7			:			: challengePassword
+pkcs9 8			:			: unstructuredAddress
+!Cname extCertAttributes
+pkcs9 9			:			: extendedCertificateAttributes
+!global
+
+!Cname ext-req
+pkcs9 14		: extReq		: Extension Request
+
+!Cname SMIMECapabilities
+pkcs9 15		: SMIME-CAPS		: S/MIME Capabilities
+
+# S/MIME
+!Cname SMIME
+pkcs9 16		: SMIME			: S/MIME
+SMIME 0			: id-smime-mod
+SMIME 1			: id-smime-ct
+SMIME 2			: id-smime-aa
+SMIME 3			: id-smime-alg
+SMIME 4			: id-smime-cd
+SMIME 5			: id-smime-spq
+SMIME 6			: id-smime-cti
+
+# S/MIME Modules
+id-smime-mod 1		: id-smime-mod-cms
+id-smime-mod 2		: id-smime-mod-ess
+id-smime-mod 3		: id-smime-mod-oid
+id-smime-mod 4		: id-smime-mod-msg-v3
+id-smime-mod 5		: id-smime-mod-ets-eSignature-88
+id-smime-mod 6		: id-smime-mod-ets-eSignature-97
+id-smime-mod 7		: id-smime-mod-ets-eSigPolicy-88
+id-smime-mod 8		: id-smime-mod-ets-eSigPolicy-97
+
+# S/MIME Content Types
+id-smime-ct 1		: id-smime-ct-receipt
+id-smime-ct 2		: id-smime-ct-authData
+id-smime-ct 3		: id-smime-ct-publishCert
+id-smime-ct 4		: id-smime-ct-TSTInfo
+id-smime-ct 5		: id-smime-ct-TDTInfo
+id-smime-ct 6		: id-smime-ct-contentInfo
+id-smime-ct 7		: id-smime-ct-DVCSRequestData
+id-smime-ct 8		: id-smime-ct-DVCSResponseData
+id-smime-ct 9		: id-smime-ct-compressedData
+id-smime-ct 27		: id-ct-asciiTextWithCRLF
+
+# S/MIME Attributes
+id-smime-aa 1		: id-smime-aa-receiptRequest
+id-smime-aa 2		: id-smime-aa-securityLabel
+id-smime-aa 3		: id-smime-aa-mlExpandHistory
+id-smime-aa 4		: id-smime-aa-contentHint
+id-smime-aa 5		: id-smime-aa-msgSigDigest
+# obsolete
+id-smime-aa 6		: id-smime-aa-encapContentType
+id-smime-aa 7		: id-smime-aa-contentIdentifier
+# obsolete
+id-smime-aa 8		: id-smime-aa-macValue
+id-smime-aa 9		: id-smime-aa-equivalentLabels
+id-smime-aa 10		: id-smime-aa-contentReference
+id-smime-aa 11		: id-smime-aa-encrypKeyPref
+id-smime-aa 12		: id-smime-aa-signingCertificate
+id-smime-aa 13		: id-smime-aa-smimeEncryptCerts
+id-smime-aa 14		: id-smime-aa-timeStampToken
+id-smime-aa 15		: id-smime-aa-ets-sigPolicyId
+id-smime-aa 16		: id-smime-aa-ets-commitmentType
+id-smime-aa 17		: id-smime-aa-ets-signerLocation
+id-smime-aa 18		: id-smime-aa-ets-signerAttr
+id-smime-aa 19		: id-smime-aa-ets-otherSigCert
+id-smime-aa 20		: id-smime-aa-ets-contentTimestamp
+id-smime-aa 21		: id-smime-aa-ets-CertificateRefs
+id-smime-aa 22		: id-smime-aa-ets-RevocationRefs
+id-smime-aa 23		: id-smime-aa-ets-certValues
+id-smime-aa 24		: id-smime-aa-ets-revocationValues
+id-smime-aa 25		: id-smime-aa-ets-escTimeStamp
+id-smime-aa 26		: id-smime-aa-ets-certCRLTimestamp
+id-smime-aa 27		: id-smime-aa-ets-archiveTimeStamp
+id-smime-aa 28		: id-smime-aa-signatureType
+id-smime-aa 29		: id-smime-aa-dvcs-dvc
+
+# S/MIME Algorithm Identifiers
+# obsolete
+id-smime-alg 1		: id-smime-alg-ESDHwith3DES
+# obsolete
+id-smime-alg 2		: id-smime-alg-ESDHwithRC2
+# obsolete
+id-smime-alg 3		: id-smime-alg-3DESwrap
+# obsolete
+id-smime-alg 4		: id-smime-alg-RC2wrap
+id-smime-alg 5		: id-smime-alg-ESDH
+id-smime-alg 6		: id-smime-alg-CMS3DESwrap
+id-smime-alg 7		: id-smime-alg-CMSRC2wrap
+id-smime-alg 9		: id-alg-PWRI-KEK
+
+# S/MIME Certificate Distribution
+id-smime-cd 1		: id-smime-cd-ldap
+
+# S/MIME Signature Policy Qualifier
+id-smime-spq 1		: id-smime-spq-ets-sqt-uri
+id-smime-spq 2		: id-smime-spq-ets-sqt-unotice
+
+# S/MIME Commitment Type Identifier
+id-smime-cti 1		: id-smime-cti-ets-proofOfOrigin
+id-smime-cti 2		: id-smime-cti-ets-proofOfReceipt
+id-smime-cti 3		: id-smime-cti-ets-proofOfDelivery
+id-smime-cti 4		: id-smime-cti-ets-proofOfSender
+id-smime-cti 5		: id-smime-cti-ets-proofOfApproval
+id-smime-cti 6		: id-smime-cti-ets-proofOfCreation
+
+pkcs9 20		:			: friendlyName
+pkcs9 21		:			: localKeyID
+!Cname ms-csp-name
+1 3 6 1 4 1 311 17 1	: CSPName		: Microsoft CSP Name
+1 3 6 1 4 1 311 17 2	: LocalKeySet		: Microsoft Local Key set
+!Alias certTypes pkcs9 22
+certTypes 1		:			: x509Certificate
+certTypes 2		:			: sdsiCertificate
+!Alias crlTypes pkcs9 23
+crlTypes 1		:			: x509Crl
+
+!Alias pkcs12 pkcs 12
+!Alias pkcs12-pbeids pkcs12 1
+
+!Cname pbe-WithSHA1And128BitRC4
+pkcs12-pbeids 1		: PBE-SHA1-RC4-128	: pbeWithSHA1And128BitRC4
+!Cname pbe-WithSHA1And40BitRC4
+pkcs12-pbeids 2		: PBE-SHA1-RC4-40	: pbeWithSHA1And40BitRC4
+!Cname pbe-WithSHA1And3_Key_TripleDES-CBC
+pkcs12-pbeids 3		: PBE-SHA1-3DES		: pbeWithSHA1And3-KeyTripleDES-CBC
+!Cname pbe-WithSHA1And2_Key_TripleDES-CBC
+pkcs12-pbeids 4		: PBE-SHA1-2DES		: pbeWithSHA1And2-KeyTripleDES-CBC
+!Cname pbe-WithSHA1And128BitRC2-CBC
+pkcs12-pbeids 5		: PBE-SHA1-RC2-128	: pbeWithSHA1And128BitRC2-CBC
+!Cname pbe-WithSHA1And40BitRC2-CBC
+pkcs12-pbeids 6		: PBE-SHA1-RC2-40	: pbeWithSHA1And40BitRC2-CBC
+
+!Alias pkcs12-Version1 pkcs12 10
+!Alias pkcs12-BagIds pkcs12-Version1 1
+pkcs12-BagIds 1		:			: keyBag
+pkcs12-BagIds 2		:			: pkcs8ShroudedKeyBag
+pkcs12-BagIds 3		:			: certBag
+pkcs12-BagIds 4		:			: crlBag
+pkcs12-BagIds 5		:			: secretBag
+pkcs12-BagIds 6		:			: safeContentsBag
+
+rsadsi 2 2		: MD2			: md2
+rsadsi 2 4		: MD4			: md4
+rsadsi 2 5		: MD5			: md5
+			: MD5-SHA1		: md5-sha1
+rsadsi 2 6		:			: hmacWithMD5
+rsadsi 2 7		:			: hmacWithSHA1
+
+# From RFC4231
+rsadsi 2 8		:			: hmacWithSHA224
+rsadsi 2 9		:			: hmacWithSHA256
+rsadsi 2 10		:			: hmacWithSHA384
+rsadsi 2 11		:			: hmacWithSHA512
+
+rsadsi 3 2		: RC2-CBC		: rc2-cbc
+			: RC2-ECB		: rc2-ecb
+!Cname rc2-cfb64
+			: RC2-CFB		: rc2-cfb
+!Cname rc2-ofb64
+			: RC2-OFB		: rc2-ofb
+			: RC2-40-CBC		: rc2-40-cbc
+			: RC2-64-CBC		: rc2-64-cbc
+rsadsi 3 4		: RC4			: rc4
+			: RC4-40		: rc4-40
+rsadsi 3 7		: DES-EDE3-CBC		: des-ede3-cbc
+rsadsi 3 8		: RC5-CBC		: rc5-cbc
+			: RC5-ECB		: rc5-ecb
+!Cname rc5-cfb64
+			: RC5-CFB		: rc5-cfb
+!Cname rc5-ofb64
+			: RC5-OFB		: rc5-ofb
+
+!Cname ms-ext-req
+1 3 6 1 4 1 311 2 1 14	: msExtReq		: Microsoft Extension Request
+!Cname ms-code-ind
+1 3 6 1 4 1 311 2 1 21	: msCodeInd		: Microsoft Individual Code Signing
+!Cname ms-code-com
+1 3 6 1 4 1 311 2 1 22	: msCodeCom		: Microsoft Commercial Code Signing
+!Cname ms-ctl-sign
+1 3 6 1 4 1 311 10 3 1	: msCTLSign		: Microsoft Trust List Signing
+!Cname ms-sgc
+1 3 6 1 4 1 311 10 3 3	: msSGC			: Microsoft Server Gated Crypto
+!Cname ms-efs
+1 3 6 1 4 1 311 10 3 4	: msEFS			: Microsoft Encrypted File System
+!Cname ms-smartcard-login
+1 3 6 1 4 1 311 20 2 2	: msSmartcardLogin	: Microsoft Smartcardlogin
+!Cname ms-upn
+1 3 6 1 4 1 311 20 2 3	: msUPN			: Microsoft Universal Principal Name
+
+1 3 6 1 4 1 188 7 1 1 2	: IDEA-CBC		: idea-cbc
+			: IDEA-ECB		: idea-ecb
+!Cname idea-cfb64
+			: IDEA-CFB		: idea-cfb
+!Cname idea-ofb64
+			: IDEA-OFB		: idea-ofb
+
+1 3 6 1 4 1 3029 1 2	: BF-CBC		: bf-cbc
+			: BF-ECB		: bf-ecb
+!Cname bf-cfb64
+			: BF-CFB		: bf-cfb
+!Cname bf-ofb64
+			: BF-OFB		: bf-ofb
+
+!Cname id-pkix
+1 3 6 1 5 5 7		: PKIX
+
+# PKIX Arcs
+id-pkix 0		: id-pkix-mod
+id-pkix 1		: id-pe
+id-pkix 2		: id-qt
+id-pkix 3		: id-kp
+id-pkix 4		: id-it
+id-pkix 5		: id-pkip
+id-pkix 6		: id-alg
+id-pkix 7		: id-cmc
+id-pkix 8		: id-on
+id-pkix 9		: id-pda
+id-pkix 10		: id-aca
+id-pkix 11		: id-qcs
+id-pkix 12		: id-cct
+id-pkix 21		: id-ppl
+id-pkix 48		: id-ad
+
+# PKIX Modules
+id-pkix-mod 1		: id-pkix1-explicit-88
+id-pkix-mod 2		: id-pkix1-implicit-88
+id-pkix-mod 3		: id-pkix1-explicit-93
+id-pkix-mod 4		: id-pkix1-implicit-93
+id-pkix-mod 5		: id-mod-crmf
+id-pkix-mod 6		: id-mod-cmc
+id-pkix-mod 7		: id-mod-kea-profile-88
+id-pkix-mod 8		: id-mod-kea-profile-93
+id-pkix-mod 9		: id-mod-cmp
+id-pkix-mod 10		: id-mod-qualified-cert-88
+id-pkix-mod 11		: id-mod-qualified-cert-93
+id-pkix-mod 12		: id-mod-attribute-cert
+id-pkix-mod 13		: id-mod-timestamp-protocol
+id-pkix-mod 14		: id-mod-ocsp
+id-pkix-mod 15		: id-mod-dvcs
+id-pkix-mod 16		: id-mod-cmp2000
+
+# PKIX Private Extensions
+!Cname info-access
+id-pe 1			: authorityInfoAccess	: Authority Information Access
+id-pe 2			: biometricInfo		: Biometric Info
+id-pe 3			: qcStatements
+id-pe 4			: ac-auditEntity
+id-pe 5			: ac-targeting
+id-pe 6			: aaControls
+id-pe 7			: sbgp-ipAddrBlock
+id-pe 8			: sbgp-autonomousSysNum
+id-pe 9			: sbgp-routerIdentifier
+id-pe 10		: ac-proxying
+!Cname sinfo-access
+id-pe 11		: subjectInfoAccess	: Subject Information Access
+id-pe 14		: proxyCertInfo		: Proxy Certificate Information
+
+# PKIX policyQualifiers for Internet policy qualifiers
+id-qt 1			: id-qt-cps		: Policy Qualifier CPS
+id-qt 2			: id-qt-unotice		: Policy Qualifier User Notice
+id-qt 3			: textNotice
+
+# PKIX key purpose identifiers
+!Cname server-auth
+id-kp 1			: serverAuth		: TLS Web Server Authentication
+!Cname client-auth
+id-kp 2			: clientAuth		: TLS Web Client Authentication
+!Cname code-sign
+id-kp 3			: codeSigning		: Code Signing
+!Cname email-protect
+id-kp 4			: emailProtection	: E-mail Protection
+id-kp 5			: ipsecEndSystem	: IPSec End System
+id-kp 6			: ipsecTunnel		: IPSec Tunnel
+id-kp 7			: ipsecUser		: IPSec User
+!Cname time-stamp
+id-kp 8			: timeStamping		: Time Stamping
+# From OCSP spec RFC2560
+!Cname OCSP-sign
+id-kp 9			: OCSPSigning		: OCSP Signing
+id-kp 10		: DVCS			: dvcs
+
+# CMP information types
+id-it 1			: id-it-caProtEncCert
+id-it 2			: id-it-signKeyPairTypes
+id-it 3			: id-it-encKeyPairTypes
+id-it 4			: id-it-preferredSymmAlg
+id-it 5			: id-it-caKeyUpdateInfo
+id-it 6			: id-it-currentCRL
+id-it 7			: id-it-unsupportedOIDs
+# obsolete
+id-it 8			: id-it-subscriptionRequest
+# obsolete
+id-it 9			: id-it-subscriptionResponse
+id-it 10		: id-it-keyPairParamReq
+id-it 11		: id-it-keyPairParamRep
+id-it 12		: id-it-revPassphrase
+id-it 13		: id-it-implicitConfirm
+id-it 14		: id-it-confirmWaitTime
+id-it 15		: id-it-origPKIMessage
+id-it 16		: id-it-suppLangTags
+
+# CRMF registration
+id-pkip 1		: id-regCtrl
+id-pkip 2		: id-regInfo
+
+# CRMF registration controls
+id-regCtrl 1		: id-regCtrl-regToken
+id-regCtrl 2		: id-regCtrl-authenticator
+id-regCtrl 3		: id-regCtrl-pkiPublicationInfo
+id-regCtrl 4		: id-regCtrl-pkiArchiveOptions
+id-regCtrl 5		: id-regCtrl-oldCertID
+id-regCtrl 6		: id-regCtrl-protocolEncrKey
+
+# CRMF registration information
+id-regInfo 1		: id-regInfo-utf8Pairs
+id-regInfo 2		: id-regInfo-certReq
+
+# algorithms
+id-alg 1		: id-alg-des40
+id-alg 2		: id-alg-noSignature
+id-alg 3		: id-alg-dh-sig-hmac-sha1
+id-alg 4		: id-alg-dh-pop
+
+# CMC controls
+id-cmc 1		: id-cmc-statusInfo
+id-cmc 2		: id-cmc-identification
+id-cmc 3		: id-cmc-identityProof
+id-cmc 4		: id-cmc-dataReturn
+id-cmc 5		: id-cmc-transactionId
+id-cmc 6		: id-cmc-senderNonce
+id-cmc 7		: id-cmc-recipientNonce
+id-cmc 8		: id-cmc-addExtensions
+id-cmc 9		: id-cmc-encryptedPOP
+id-cmc 10		: id-cmc-decryptedPOP
+id-cmc 11		: id-cmc-lraPOPWitness
+id-cmc 15		: id-cmc-getCert
+id-cmc 16		: id-cmc-getCRL
+id-cmc 17		: id-cmc-revokeRequest
+id-cmc 18		: id-cmc-regInfo
+id-cmc 19		: id-cmc-responseInfo
+id-cmc 21		: id-cmc-queryPending
+id-cmc 22		: id-cmc-popLinkRandom
+id-cmc 23		: id-cmc-popLinkWitness
+id-cmc 24		: id-cmc-confirmCertAcceptance 
+
+# other names
+id-on 1			: id-on-personalData
+id-on 3			: id-on-permanentIdentifier : Permanent Identifier
+
+# personal data attributes
+id-pda 1		: id-pda-dateOfBirth
+id-pda 2		: id-pda-placeOfBirth
+id-pda 3		: id-pda-gender
+id-pda 4		: id-pda-countryOfCitizenship
+id-pda 5		: id-pda-countryOfResidence
+
+# attribute certificate attributes
+id-aca 1		: id-aca-authenticationInfo
+id-aca 2		: id-aca-accessIdentity
+id-aca 3		: id-aca-chargingIdentity
+id-aca 4		: id-aca-group
+# attention : the following seems to be obsolete, replace by 'role'
+id-aca 5		: id-aca-role
+id-aca 6		: id-aca-encAttrs
+
+# qualified certificate statements
+id-qcs 1		: id-qcs-pkixQCSyntax-v1
+
+# CMC content types
+id-cct 1		: id-cct-crs
+id-cct 2		: id-cct-PKIData
+id-cct 3		: id-cct-PKIResponse
+
+# Predefined Proxy Certificate policy languages
+id-ppl 0		: id-ppl-anyLanguage	: Any language
+id-ppl 1		: id-ppl-inheritAll	: Inherit all
+id-ppl 2		: id-ppl-independent	: Independent
+
+# access descriptors for authority info access extension
+!Cname ad-OCSP
+id-ad 1			: OCSP			: OCSP
+!Cname ad-ca-issuers
+id-ad 2			: caIssuers		: CA Issuers
+!Cname ad-timeStamping
+id-ad 3			: ad_timestamping	: AD Time Stamping
+!Cname ad-dvcs
+id-ad 4			: AD_DVCS		: ad dvcs
+id-ad 5			: caRepository		: CA Repository
+
+
+!Alias id-pkix-OCSP ad-OCSP
+!module id-pkix-OCSP
+!Cname basic
+id-pkix-OCSP 1		: basicOCSPResponse	: Basic OCSP Response
+id-pkix-OCSP 2		: Nonce			: OCSP Nonce
+id-pkix-OCSP 3		: CrlID			: OCSP CRL ID
+id-pkix-OCSP 4		: acceptableResponses	: Acceptable OCSP Responses
+id-pkix-OCSP 5		: noCheck		: OCSP No Check
+id-pkix-OCSP 6		: archiveCutoff		: OCSP Archive Cutoff
+id-pkix-OCSP 7		: serviceLocator	: OCSP Service Locator
+id-pkix-OCSP 8		: extendedStatus	: Extended OCSP Status
+id-pkix-OCSP 9		: valid
+id-pkix-OCSP 10		: path
+id-pkix-OCSP 11		: trustRoot		: Trust Root
+!global
+
+1 3 14 3 2		: algorithm		: algorithm
+algorithm 3		: RSA-NP-MD5		: md5WithRSA
+algorithm 6		: DES-ECB		: des-ecb
+algorithm 7		: DES-CBC		: des-cbc
+!Cname des-ofb64
+algorithm 8		: DES-OFB		: des-ofb
+!Cname des-cfb64
+algorithm 9		: DES-CFB		: des-cfb
+algorithm 11		: rsaSignature
+!Cname dsa-2
+algorithm 12		: DSA-old		: dsaEncryption-old
+algorithm 13		: DSA-SHA		: dsaWithSHA
+algorithm 15		: RSA-SHA		: shaWithRSAEncryption
+!Cname des-ede-ecb
+algorithm 17		: DES-EDE		: des-ede
+!Cname des-ede3-ecb
+			: DES-EDE3		: des-ede3
+			: DES-EDE-CBC		: des-ede-cbc
+!Cname des-ede-cfb64
+			: DES-EDE-CFB		: des-ede-cfb
+!Cname des-ede3-cfb64
+			: DES-EDE3-CFB		: des-ede3-cfb
+!Cname des-ede-ofb64
+			: DES-EDE-OFB		: des-ede-ofb
+!Cname des-ede3-ofb64
+			: DES-EDE3-OFB		: des-ede3-ofb
+			: DESX-CBC		: desx-cbc
+algorithm 18		: SHA			: sha
+algorithm 26		: SHA1			: sha1
+!Cname dsaWithSHA1-2
+algorithm 27		: DSA-SHA1-old		: dsaWithSHA1-old
+algorithm 29		: RSA-SHA1-2		: sha1WithRSA
+
+1 3 36 3 2 1		: RIPEMD160		: ripemd160
+1 3 36 3 3 1 2		: RSA-RIPEMD160		: ripemd160WithRSA
+
+!Cname sxnet
+1 3 101 1 4 1		: SXNetID		: Strong Extranet ID
+
+2 5			: X500			: directory services (X.500)
+
+X500 4			: X509
+X509 3			: CN			: commonName
+X509 4			: SN			: surname
+X509 5			: 			: serialNumber
+X509 6			: C			: countryName
+X509 7			: L			: localityName
+X509 8			: ST			: stateOrProvinceName
+X509 9			: street		: streetAddress
+X509 10			: O			: organizationName
+X509 11			: OU			: organizationalUnitName
+X509 12			: title			: title
+X509 13			: 			: description
+X509 14			: 			: searchGuide
+X509 15			: 			: businessCategory
+X509 16			: 			: postalAddress
+X509 17			: 			: postalCode
+X509 18			: 			: postOfficeBox
+X509 19			: 			: physicalDeliveryOfficeName
+X509 20			: 			: telephoneNumber
+X509 21			: 			: telexNumber
+X509 22			: 			: teletexTerminalIdentifier
+X509 23			: 			: facsimileTelephoneNumber
+X509 24			: 			: x121Address
+X509 25			: 			: internationaliSDNNumber
+X509 26			: 			: registeredAddress
+X509 27			: 			: destinationIndicator
+X509 28			: 			: preferredDeliveryMethod
+X509 29			: 			: presentationAddress
+X509 30			: 			: supportedApplicationContext
+X509 31			: member		:
+X509 32			: owner			:
+X509 33			: 			: roleOccupant
+X509 34			: seeAlso		:
+X509 35			: 			: userPassword
+X509 36			: 			: userCertificate
+X509 37			: 			: cACertificate
+X509 38			: 			: authorityRevocationList
+X509 39			: 			: certificateRevocationList
+X509 40			: 			: crossCertificatePair
+X509 41			: name			: name
+X509 42			: GN			: givenName
+X509 43			: initials		: initials
+X509 44			: 			: generationQualifier
+X509 45			: 			: x500UniqueIdentifier
+X509 46			: dnQualifier		: dnQualifier
+X509 47			: 			: enhancedSearchGuide
+X509 48			: 			: protocolInformation
+X509 49			: 			: distinguishedName
+X509 50			: 			: uniqueMember
+X509 51			: 			: houseIdentifier
+X509 52			: 			: supportedAlgorithms
+X509 53			: 			: deltaRevocationList
+X509 54			: dmdName		:
+X509 65			:			: pseudonym
+X509 72			: role			: role
+
+X500 8			: X500algorithms	: directory services - algorithms
+X500algorithms 1 1	: RSA			: rsa
+X500algorithms 3 100	: RSA-MDC2		: mdc2WithRSA
+X500algorithms 3 101	: MDC2			: mdc2
+
+X500 29			: id-ce
+!Cname subject-directory-attributes
+id-ce 9			: subjectDirectoryAttributes : X509v3 Subject Directory Attributes
+!Cname subject-key-identifier
+id-ce 14		: subjectKeyIdentifier	: X509v3 Subject Key Identifier
+!Cname key-usage
+id-ce 15		: keyUsage		: X509v3 Key Usage
+!Cname private-key-usage-period
+id-ce 16		: privateKeyUsagePeriod	: X509v3 Private Key Usage Period
+!Cname subject-alt-name
+id-ce 17		: subjectAltName	: X509v3 Subject Alternative Name
+!Cname issuer-alt-name
+id-ce 18		: issuerAltName		: X509v3 Issuer Alternative Name
+!Cname basic-constraints
+id-ce 19		: basicConstraints	: X509v3 Basic Constraints
+!Cname crl-number
+id-ce 20		: crlNumber		: X509v3 CRL Number
+!Cname crl-reason
+id-ce 21		: CRLReason		: X509v3 CRL Reason Code
+!Cname invalidity-date
+id-ce 24		: invalidityDate	: Invalidity Date
+!Cname delta-crl
+id-ce 27		: deltaCRL		: X509v3 Delta CRL Indicator
+!Cname issuing-distribution-point
+id-ce 28		: issuingDistributionPoint : X509v3 Issuing Distrubution Point
+!Cname certificate-issuer
+id-ce 29		: certificateIssuer	: X509v3 Certificate Issuer
+!Cname name-constraints
+id-ce 30		: nameConstraints	: X509v3 Name Constraints
+!Cname crl-distribution-points
+id-ce 31		: crlDistributionPoints	: X509v3 CRL Distribution Points
+!Cname certificate-policies
+id-ce 32		: certificatePolicies	: X509v3 Certificate Policies
+!Cname any-policy
+certificate-policies 0	: anyPolicy		: X509v3 Any Policy
+!Cname policy-mappings
+id-ce 33		: policyMappings	: X509v3 Policy Mappings
+!Cname authority-key-identifier
+id-ce 35		: authorityKeyIdentifier : X509v3 Authority Key Identifier
+!Cname policy-constraints
+id-ce 36		: policyConstraints	: X509v3 Policy Constraints
+!Cname ext-key-usage
+id-ce 37		: extendedKeyUsage	: X509v3 Extended Key Usage
+!Cname freshest-crl
+id-ce 46		: freshestCRL		: X509v3 Freshest CRL
+!Cname inhibit-any-policy
+id-ce 54		: inhibitAnyPolicy	: X509v3 Inhibit Any Policy
+!Cname target-information
+id-ce 55		: targetInformation	: X509v3 AC Targeting
+!Cname no-rev-avail
+id-ce 56		: noRevAvail		: X509v3 No Revocation Available
+
+# From RFC5280
+ext-key-usage 0		: anyExtendedKeyUsage	: Any Extended Key Usage
+
+
+!Cname netscape
+2 16 840 1 113730	: Netscape		: Netscape Communications Corp.
+!Cname netscape-cert-extension
+netscape 1		: nsCertExt		: Netscape Certificate Extension
+!Cname netscape-data-type
+netscape 2		: nsDataType		: Netscape Data Type
+!Cname netscape-cert-type
+netscape-cert-extension 1 : nsCertType		: Netscape Cert Type
+!Cname netscape-base-url
+netscape-cert-extension 2 : nsBaseUrl		: Netscape Base Url
+!Cname netscape-revocation-url
+netscape-cert-extension 3 : nsRevocationUrl	: Netscape Revocation Url
+!Cname netscape-ca-revocation-url
+netscape-cert-extension 4 : nsCaRevocationUrl	: Netscape CA Revocation Url
+!Cname netscape-renewal-url
+netscape-cert-extension 7 : nsRenewalUrl	: Netscape Renewal Url
+!Cname netscape-ca-policy-url
+netscape-cert-extension 8 : nsCaPolicyUrl	: Netscape CA Policy Url
+!Cname netscape-ssl-server-name
+netscape-cert-extension 12 : nsSslServerName	: Netscape SSL Server Name
+!Cname netscape-comment
+netscape-cert-extension 13 : nsComment		: Netscape Comment
+!Cname netscape-cert-sequence
+netscape-data-type 5	: nsCertSequence	: Netscape Certificate Sequence
+!Cname ns-sgc
+netscape 4 1		: nsSGC			: Netscape Server Gated Crypto
+
+# iso(1)
+iso 3			: ORG			: org
+org 6			: DOD			: dod
+dod 1			: IANA			: iana
+!Alias internet iana
+
+internet 1		: directory		: Directory
+internet 2		: mgmt			: Management
+internet 3		: experimental		: Experimental
+internet 4		: private		: Private
+internet 5		: security		: Security
+internet 6		: snmpv2		: SNMPv2
+# Documents refer to "internet 7" as "mail". This however leads to ambiguities
+# with RFC2798, Section 9.1.3, where "mail" is defined as the short name for
+# rfc822Mailbox. The short name is therefore here left out for a reason.
+# Subclasses of "mail", e.g. "MIME MHS" don't consitute a problem, as
+# references are realized via long name "Mail" (with capital M).
+internet 7		:			: Mail
+
+Private 1		: enterprises		: Enterprises
+
+# RFC 2247
+Enterprises 1466 344	: dcobject		: dcObject
+
+# RFC 1495
+Mail 1			: mime-mhs		: MIME MHS
+mime-mhs 1		: mime-mhs-headings	: mime-mhs-headings
+mime-mhs 2		: mime-mhs-bodies	: mime-mhs-bodies
+mime-mhs-headings 1	: id-hex-partial-message : id-hex-partial-message
+mime-mhs-headings 2	: id-hex-multipart-message : id-hex-multipart-message
+
+# What the hell are these OIDs, really?
+!Cname rle-compression
+1 1 1 1 666 1		: RLE			: run length compression
+!Cname zlib-compression
+id-smime-alg 8		: ZLIB			: zlib compression
+
+# AES aka Rijndael
+
+!Alias csor 2 16 840 1 101 3
+!Alias nistAlgorithms csor 4
+!Alias aes nistAlgorithms 1
+
+aes 1			: AES-128-ECB		: aes-128-ecb
+aes 2			: AES-128-CBC		: aes-128-cbc
+!Cname aes-128-ofb128
+aes 3			: AES-128-OFB		: aes-128-ofb
+!Cname aes-128-cfb128
+aes 4			: AES-128-CFB		: aes-128-cfb
+aes 5			: id-aes128-wrap
+aes 6			: id-aes128-GCM		: aes-128-gcm
+aes 7			: id-aes128-CCM		: aes-128-ccm
+aes 8			: id-aes128-wrap-pad
+
+aes 21			: AES-192-ECB		: aes-192-ecb
+aes 22			: AES-192-CBC		: aes-192-cbc
+!Cname aes-192-ofb128
+aes 23			: AES-192-OFB		: aes-192-ofb
+!Cname aes-192-cfb128
+aes 24			: AES-192-CFB		: aes-192-cfb
+aes 25			: id-aes192-wrap
+aes 26			: id-aes192-GCM		: aes-192-gcm
+aes 27			: id-aes192-CCM		: aes-192-ccm
+aes 28			: id-aes192-wrap-pad
+
+aes 41			: AES-256-ECB		: aes-256-ecb
+aes 42			: AES-256-CBC		: aes-256-cbc
+!Cname aes-256-ofb128
+aes 43			: AES-256-OFB		: aes-256-ofb
+!Cname aes-256-cfb128
+aes 44			: AES-256-CFB		: aes-256-cfb
+aes 45			: id-aes256-wrap
+aes 46			: id-aes256-GCM		: aes-256-gcm
+aes 47			: id-aes256-CCM		: aes-256-ccm
+aes 48			: id-aes256-wrap-pad
+
+# There are no OIDs for these modes...
+
+			: AES-128-CFB1		: aes-128-cfb1
+			: AES-192-CFB1		: aes-192-cfb1
+			: AES-256-CFB1		: aes-256-cfb1
+			: AES-128-CFB8		: aes-128-cfb8
+			: AES-192-CFB8		: aes-192-cfb8
+			: AES-256-CFB8		: aes-256-cfb8
+			: AES-128-CTR		: aes-128-ctr
+			: AES-192-CTR		: aes-192-ctr
+			: AES-256-CTR		: aes-256-ctr
+			: AES-128-XTS		: aes-128-xts
+			: AES-256-XTS		: aes-256-xts
+			: DES-CFB1		: des-cfb1
+			: DES-CFB8		: des-cfb8
+			: DES-EDE3-CFB1		: des-ede3-cfb1
+			: DES-EDE3-CFB8		: des-ede3-cfb8
+
+# OIDs for SHA224, SHA256, SHA385 and SHA512, according to x9.84.
+!Alias nist_hashalgs nistAlgorithms 2
+nist_hashalgs 1		: SHA256		: sha256
+nist_hashalgs 2		: SHA384		: sha384
+nist_hashalgs 3		: SHA512		: sha512
+nist_hashalgs 4		: SHA224		: sha224
+
+# OIDs for dsa-with-sha224 and dsa-with-sha256
+!Alias dsa_with_sha2 nistAlgorithms 3
+dsa_with_sha2 1		: dsa_with_SHA224
+dsa_with_sha2 2		: dsa_with_SHA256
+
+# Hold instruction CRL entry extension
+!Cname hold-instruction-code
+id-ce 23		: holdInstructionCode	: Hold Instruction Code
+!Alias holdInstruction	X9-57 2
+!Cname hold-instruction-none
+holdInstruction 1	: holdInstructionNone	: Hold Instruction None
+!Cname hold-instruction-call-issuer
+holdInstruction 2	: holdInstructionCallIssuer : Hold Instruction Call Issuer
+!Cname hold-instruction-reject
+holdInstruction 3	: holdInstructionReject	: Hold Instruction Reject
+
+# OID's from ITU-T.  Most of this is defined in RFC 1274.  A couple of
+# them are also mentioned in RFC 2247
+itu-t 9			: data
+data 2342		: pss
+pss 19200300		: ucl
+ucl 100			: pilot
+pilot 1			:			: pilotAttributeType
+pilot 3			:			: pilotAttributeSyntax
+pilot 4			:			: pilotObjectClass
+pilot 10		:			: pilotGroups
+pilotAttributeSyntax 4	:			: iA5StringSyntax
+pilotAttributeSyntax 5	:			: caseIgnoreIA5StringSyntax
+pilotObjectClass 3	:			: pilotObject
+pilotObjectClass 4	:			: pilotPerson
+pilotObjectClass 5	: account
+pilotObjectClass 6	: document
+pilotObjectClass 7	: room
+pilotObjectClass 9	:			: documentSeries
+pilotObjectClass 13	: domain		: Domain
+pilotObjectClass 14	:			: rFC822localPart
+pilotObjectClass 15	:			: dNSDomain
+pilotObjectClass 17	:			: domainRelatedObject
+pilotObjectClass 18	:			: friendlyCountry
+pilotObjectClass 19	:			: simpleSecurityObject
+pilotObjectClass 20	:			: pilotOrganization
+pilotObjectClass 21	:			: pilotDSA
+pilotObjectClass 22	:			: qualityLabelledData
+pilotAttributeType 1	: UID			: userId
+pilotAttributeType 2	:			: textEncodedORAddress
+pilotAttributeType 3	: mail			: rfc822Mailbox
+pilotAttributeType 4	: info
+pilotAttributeType 5	:			: favouriteDrink
+pilotAttributeType 6	:			: roomNumber
+pilotAttributeType 7	: photo
+pilotAttributeType 8	:			: userClass
+pilotAttributeType 9	: host
+pilotAttributeType 10	: manager
+pilotAttributeType 11	:			: documentIdentifier
+pilotAttributeType 12	:			: documentTitle
+pilotAttributeType 13	:			: documentVersion
+pilotAttributeType 14	:			: documentAuthor
+pilotAttributeType 15	:			: documentLocation
+pilotAttributeType 20	:			: homeTelephoneNumber
+pilotAttributeType 21	: secretary
+pilotAttributeType 22	:			: otherMailbox
+pilotAttributeType 23	:			: lastModifiedTime
+pilotAttributeType 24	:			: lastModifiedBy
+pilotAttributeType 25	: DC			: domainComponent
+pilotAttributeType 26	:			: aRecord
+pilotAttributeType 27	:			: pilotAttributeType27
+pilotAttributeType 28	:			: mXRecord
+pilotAttributeType 29	:			: nSRecord
+pilotAttributeType 30	:			: sOARecord
+pilotAttributeType 31	:			: cNAMERecord
+pilotAttributeType 37	:			: associatedDomain
+pilotAttributeType 38	:			: associatedName
+pilotAttributeType 39	:			: homePostalAddress
+pilotAttributeType 40	:			: personalTitle
+pilotAttributeType 41	:			: mobileTelephoneNumber
+pilotAttributeType 42	:			: pagerTelephoneNumber
+pilotAttributeType 43	:			: friendlyCountryName
+# The following clashes with 2.5.4.45, so commented away
+#pilotAttributeType 44	: uid			: uniqueIdentifier
+pilotAttributeType 45	:			: organizationalStatus
+pilotAttributeType 46	:			: janetMailbox
+pilotAttributeType 47	:			: mailPreferenceOption
+pilotAttributeType 48	:			: buildingName
+pilotAttributeType 49	:			: dSAQuality
+pilotAttributeType 50	:			: singleLevelQuality
+pilotAttributeType 51	:			: subtreeMinimumQuality
+pilotAttributeType 52	:			: subtreeMaximumQuality
+pilotAttributeType 53	:			: personalSignature
+pilotAttributeType 54	:			: dITRedirect
+pilotAttributeType 55	: audio
+pilotAttributeType 56	:			: documentPublisher
+
+international-organizations 42	: id-set	: Secure Electronic Transactions
+
+id-set 0		: set-ctype		: content types
+id-set 1		: set-msgExt		: message extensions
+id-set 3		: set-attr
+id-set 5		: set-policy
+id-set 7		: set-certExt		: certificate extensions
+id-set 8		: set-brand
+
+set-ctype 0		: setct-PANData
+set-ctype 1		: setct-PANToken
+set-ctype 2		: setct-PANOnly
+set-ctype 3		: setct-OIData
+set-ctype 4		: setct-PI
+set-ctype 5		: setct-PIData
+set-ctype 6		: setct-PIDataUnsigned
+set-ctype 7		: setct-HODInput
+set-ctype 8		: setct-AuthResBaggage
+set-ctype 9		: setct-AuthRevReqBaggage
+set-ctype 10		: setct-AuthRevResBaggage
+set-ctype 11		: setct-CapTokenSeq
+set-ctype 12		: setct-PInitResData
+set-ctype 13		: setct-PI-TBS
+set-ctype 14		: setct-PResData
+set-ctype 16		: setct-AuthReqTBS
+set-ctype 17		: setct-AuthResTBS
+set-ctype 18		: setct-AuthResTBSX
+set-ctype 19		: setct-AuthTokenTBS
+set-ctype 20		: setct-CapTokenData
+set-ctype 21		: setct-CapTokenTBS
+set-ctype 22		: setct-AcqCardCodeMsg
+set-ctype 23		: setct-AuthRevReqTBS
+set-ctype 24		: setct-AuthRevResData
+set-ctype 25		: setct-AuthRevResTBS
+set-ctype 26		: setct-CapReqTBS
+set-ctype 27		: setct-CapReqTBSX
+set-ctype 28		: setct-CapResData
+set-ctype 29		: setct-CapRevReqTBS
+set-ctype 30		: setct-CapRevReqTBSX
+set-ctype 31		: setct-CapRevResData
+set-ctype 32		: setct-CredReqTBS
+set-ctype 33		: setct-CredReqTBSX
+set-ctype 34		: setct-CredResData
+set-ctype 35		: setct-CredRevReqTBS
+set-ctype 36		: setct-CredRevReqTBSX
+set-ctype 37		: setct-CredRevResData
+set-ctype 38		: setct-PCertReqData
+set-ctype 39		: setct-PCertResTBS
+set-ctype 40		: setct-BatchAdminReqData
+set-ctype 41		: setct-BatchAdminResData
+set-ctype 42		: setct-CardCInitResTBS
+set-ctype 43		: setct-MeAqCInitResTBS
+set-ctype 44		: setct-RegFormResTBS
+set-ctype 45		: setct-CertReqData
+set-ctype 46		: setct-CertReqTBS
+set-ctype 47		: setct-CertResData
+set-ctype 48		: setct-CertInqReqTBS
+set-ctype 49		: setct-ErrorTBS
+set-ctype 50		: setct-PIDualSignedTBE
+set-ctype 51		: setct-PIUnsignedTBE
+set-ctype 52		: setct-AuthReqTBE
+set-ctype 53		: setct-AuthResTBE
+set-ctype 54		: setct-AuthResTBEX
+set-ctype 55		: setct-AuthTokenTBE
+set-ctype 56		: setct-CapTokenTBE
+set-ctype 57		: setct-CapTokenTBEX
+set-ctype 58		: setct-AcqCardCodeMsgTBE
+set-ctype 59		: setct-AuthRevReqTBE
+set-ctype 60		: setct-AuthRevResTBE
+set-ctype 61		: setct-AuthRevResTBEB
+set-ctype 62		: setct-CapReqTBE
+set-ctype 63		: setct-CapReqTBEX
+set-ctype 64		: setct-CapResTBE
+set-ctype 65		: setct-CapRevReqTBE
+set-ctype 66		: setct-CapRevReqTBEX
+set-ctype 67		: setct-CapRevResTBE
+set-ctype 68		: setct-CredReqTBE
+set-ctype 69		: setct-CredReqTBEX
+set-ctype 70		: setct-CredResTBE
+set-ctype 71		: setct-CredRevReqTBE
+set-ctype 72		: setct-CredRevReqTBEX
+set-ctype 73		: setct-CredRevResTBE
+set-ctype 74		: setct-BatchAdminReqTBE
+set-ctype 75		: setct-BatchAdminResTBE
+set-ctype 76		: setct-RegFormReqTBE
+set-ctype 77		: setct-CertReqTBE
+set-ctype 78		: setct-CertReqTBEX
+set-ctype 79		: setct-CertResTBE
+set-ctype 80		: setct-CRLNotificationTBS
+set-ctype 81		: setct-CRLNotificationResTBS
+set-ctype 82		: setct-BCIDistributionTBS
+
+set-msgExt 1		: setext-genCrypt	: generic cryptogram
+set-msgExt 3		: setext-miAuth		: merchant initiated auth
+set-msgExt 4		: setext-pinSecure
+set-msgExt 5		: setext-pinAny
+set-msgExt 7		: setext-track2
+set-msgExt 8		: setext-cv		: additional verification
+
+set-policy 0		: set-policy-root
+
+set-certExt 0		: setCext-hashedRoot
+set-certExt 1		: setCext-certType
+set-certExt 2		: setCext-merchData
+set-certExt 3		: setCext-cCertRequired
+set-certExt 4		: setCext-tunneling
+set-certExt 5		: setCext-setExt
+set-certExt 6		: setCext-setQualf
+set-certExt 7		: setCext-PGWYcapabilities
+set-certExt 8		: setCext-TokenIdentifier
+set-certExt 9		: setCext-Track2Data
+set-certExt 10		: setCext-TokenType
+set-certExt 11		: setCext-IssuerCapabilities
+
+set-attr 0		: setAttr-Cert
+set-attr 1		: setAttr-PGWYcap	: payment gateway capabilities
+set-attr 2		: setAttr-TokenType
+set-attr 3		: setAttr-IssCap	: issuer capabilities
+
+setAttr-Cert 0		: set-rootKeyThumb
+setAttr-Cert 1		: set-addPolicy
+
+setAttr-TokenType 1	: setAttr-Token-EMV
+setAttr-TokenType 2	: setAttr-Token-B0Prime
+
+setAttr-IssCap 3	: setAttr-IssCap-CVM
+setAttr-IssCap 4	: setAttr-IssCap-T2
+setAttr-IssCap 5	: setAttr-IssCap-Sig
+
+setAttr-IssCap-CVM 1	: setAttr-GenCryptgrm	: generate cryptogram
+setAttr-IssCap-T2 1	: setAttr-T2Enc		: encrypted track 2
+setAttr-IssCap-T2 2	: setAttr-T2cleartxt	: cleartext track 2
+
+setAttr-IssCap-Sig 1	: setAttr-TokICCsig	: ICC or token signature
+setAttr-IssCap-Sig 2	: setAttr-SecDevSig	: secure device signature
+
+set-brand 1		: set-brand-IATA-ATA
+set-brand 30		: set-brand-Diners
+set-brand 34		: set-brand-AmericanExpress
+set-brand 35		: set-brand-JCB
+set-brand 4		: set-brand-Visa
+set-brand 5		: set-brand-MasterCard
+set-brand 6011		: set-brand-Novus
+
+rsadsi 3 10		: DES-CDMF		: des-cdmf
+rsadsi 1 1 6		: rsaOAEPEncryptionSET
+
+			: Oakley-EC2N-3		: ipsec3
+			: Oakley-EC2N-4		: ipsec4
+
+iso 0 10118 3 0 55	: whirlpool
+
+# GOST OIDs
+
+member-body 643 2 2	: cryptopro
+member-body 643 2 9	: cryptocom
+
+cryptopro 3		: id-GostR3411-94-with-GostR3410-2001 : GOST R 34.11-94 with GOST R 34.10-2001
+cryptopro 4		: id-GostR3411-94-with-GostR3410-94 : GOST R 34.11-94 with GOST R 34.10-94
+!Cname id-GostR3411-94
+cryptopro 9		: md_gost94		: GOST R 34.11-94
+cryptopro 10		: id-HMACGostR3411-94	: HMAC GOST 34.11-94
+!Cname id-GostR3410-2001
+cryptopro 19		: gost2001	: GOST R 34.10-2001
+!Cname id-GostR3410-94
+cryptopro 20		: gost94	: GOST R 34.10-94
+!Cname id-Gost28147-89
+cryptopro 21		: gost89 		: GOST 28147-89
+			: gost89-cnt
+!Cname id-Gost28147-89-MAC
+cryptopro 22		: gost-mac	: GOST 28147-89 MAC
+!Cname id-GostR3411-94-prf
+cryptopro 23		: prf-gostr3411-94	: GOST R 34.11-94 PRF
+cryptopro 98		: id-GostR3410-2001DH	: GOST R 34.10-2001 DH
+cryptopro 99		: id-GostR3410-94DH	: GOST R 34.10-94 DH
+
+cryptopro 14 1		: id-Gost28147-89-CryptoPro-KeyMeshing
+cryptopro 14 0		: id-Gost28147-89-None-KeyMeshing
+
+# GOST parameter set OIDs
+
+cryptopro 30 0		: id-GostR3411-94-TestParamSet
+cryptopro 30 1		: id-GostR3411-94-CryptoProParamSet
+
+cryptopro 31 0		: id-Gost28147-89-TestParamSet
+cryptopro 31 1		: id-Gost28147-89-CryptoPro-A-ParamSet
+cryptopro 31 2		: id-Gost28147-89-CryptoPro-B-ParamSet
+cryptopro 31 3		: id-Gost28147-89-CryptoPro-C-ParamSet
+cryptopro 31 4		: id-Gost28147-89-CryptoPro-D-ParamSet
+cryptopro 31 5		: id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet
+cryptopro 31 6		: id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet
+cryptopro 31 7		: id-Gost28147-89-CryptoPro-RIC-1-ParamSet
+
+cryptopro 32 0		: id-GostR3410-94-TestParamSet
+cryptopro 32 2		: id-GostR3410-94-CryptoPro-A-ParamSet
+cryptopro 32 3		: id-GostR3410-94-CryptoPro-B-ParamSet
+cryptopro 32 4		: id-GostR3410-94-CryptoPro-C-ParamSet
+cryptopro 32 5		: id-GostR3410-94-CryptoPro-D-ParamSet
+
+cryptopro 33 1		: id-GostR3410-94-CryptoPro-XchA-ParamSet
+cryptopro 33 2		: id-GostR3410-94-CryptoPro-XchB-ParamSet
+cryptopro 33 3		: id-GostR3410-94-CryptoPro-XchC-ParamSet
+
+cryptopro 35 0		: id-GostR3410-2001-TestParamSet
+cryptopro 35 1		: id-GostR3410-2001-CryptoPro-A-ParamSet
+cryptopro 35 2		: id-GostR3410-2001-CryptoPro-B-ParamSet
+cryptopro 35 3		: id-GostR3410-2001-CryptoPro-C-ParamSet
+
+cryptopro 36 0		: id-GostR3410-2001-CryptoPro-XchA-ParamSet
+cryptopro 36 1		: id-GostR3410-2001-CryptoPro-XchB-ParamSet
+
+id-GostR3410-94 1	: id-GostR3410-94-a
+id-GostR3410-94 2	: id-GostR3410-94-aBis
+id-GostR3410-94 3	: id-GostR3410-94-b
+id-GostR3410-94 4	: id-GostR3410-94-bBis
+
+# Cryptocom LTD GOST OIDs
+
+cryptocom 1 6 1		: id-Gost28147-89-cc	: GOST 28147-89 Cryptocom ParamSet
+!Cname id-GostR3410-94-cc
+cryptocom 1 5 3		: gost94cc	: GOST 34.10-94 Cryptocom
+!Cname id-GostR3410-2001-cc
+cryptocom 1 5 4		: gost2001cc	: GOST 34.10-2001 Cryptocom
+
+cryptocom 1 3 3		: id-GostR3411-94-with-GostR3410-94-cc : GOST R 34.11-94 with GOST R 34.10-94 Cryptocom
+cryptocom 1 3 4		: id-GostR3411-94-with-GostR3410-2001-cc : GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom
+
+cryptocom 1 8 1		: id-GostR3410-2001-ParamSet-cc : GOST R 3410-2001 Parameter Set Cryptocom
+
+# Definitions for Camellia cipher - CBC MODE
+
+1 2 392 200011 61 1 1 1 2 : CAMELLIA-128-CBC		: camellia-128-cbc
+1 2 392 200011 61 1 1 1 3 : CAMELLIA-192-CBC		: camellia-192-cbc
+1 2 392 200011 61 1 1 1 4 : CAMELLIA-256-CBC		: camellia-256-cbc
+1 2 392 200011 61 1 1 3 2 : id-camellia128-wrap
+1 2 392 200011 61 1 1 3 3 : id-camellia192-wrap
+1 2 392 200011 61 1 1 3 4 : id-camellia256-wrap
+
+# Definitions for Camellia cipher - ECB, CFB, OFB MODE
+
+!Alias ntt-ds 0 3 4401 5
+!Alias camellia ntt-ds 3 1 9 
+
+camellia 1		: CAMELLIA-128-ECB		: camellia-128-ecb
+!Cname camellia-128-ofb128
+camellia 3		: CAMELLIA-128-OFB		: camellia-128-ofb
+!Cname camellia-128-cfb128
+camellia 4		: CAMELLIA-128-CFB		: camellia-128-cfb
+
+camellia 21		: CAMELLIA-192-ECB		: camellia-192-ecb
+!Cname camellia-192-ofb128
+camellia 23		: CAMELLIA-192-OFB		: camellia-192-ofb
+!Cname camellia-192-cfb128
+camellia 24		: CAMELLIA-192-CFB		: camellia-192-cfb
+
+camellia 41		: CAMELLIA-256-ECB		: camellia-256-ecb
+!Cname camellia-256-ofb128
+camellia 43		: CAMELLIA-256-OFB		: camellia-256-ofb
+!Cname camellia-256-cfb128
+camellia 44		: CAMELLIA-256-CFB		: camellia-256-cfb
+
+# There are no OIDs for these modes...
+
+			: CAMELLIA-128-CFB1		: camellia-128-cfb1
+			: CAMELLIA-192-CFB1		: camellia-192-cfb1
+			: CAMELLIA-256-CFB1		: camellia-256-cfb1
+			: CAMELLIA-128-CFB8		: camellia-128-cfb8
+			: CAMELLIA-192-CFB8		: camellia-192-cfb8
+			: CAMELLIA-256-CFB8		: camellia-256-cfb8
+
+# Definitions for SEED cipher - ECB, CBC, OFB mode
+
+member-body 410 200004  : KISA          : kisa
+kisa 1 3                : SEED-ECB      : seed-ecb
+kisa 1 4                : SEED-CBC      : seed-cbc
+!Cname seed-cfb128
+kisa 1 5                : SEED-CFB      : seed-cfb
+!Cname seed-ofb128
+kisa 1 6                : SEED-OFB      : seed-ofb
+
+# There is no OID that just denotes "HMAC" oddly enough...
+
+			: HMAC				: hmac
+# Nor CMAC either
+			: CMAC				: cmac
+
+# Synthetic composite ciphersuites
+			: RC4-HMAC-MD5			: rc4-hmac-md5
+			: AES-128-CBC-HMAC-SHA1		: aes-128-cbc-hmac-sha1
+			: AES-192-CBC-HMAC-SHA1		: aes-192-cbc-hmac-sha1
+			: AES-256-CBC-HMAC-SHA1		: aes-256-cbc-hmac-sha1
+
+ISO-US 10046 2 1	: dhpublicnumber		: X9.42 DH
+
+# RFC 5639 curve OIDs (see http://www.ietf.org/rfc/rfc5639.txt)
+# versionOne OBJECT IDENTIFIER ::= {
+# iso(1) identifified-organization(3) teletrust(36) algorithm(3)
+# signature-algorithm(3) ecSign(2) ecStdCurvesAndGeneration(8)
+# ellipticCurve(1) 1 }
+1 3 36 3 3 2 8 1 1 1 : brainpoolP160r1
+1 3 36 3 3 2 8 1 1 2 : brainpoolP160t1
+1 3 36 3 3 2 8 1 1 3 : brainpoolP192r1
+1 3 36 3 3 2 8 1 1 4 : brainpoolP192t1
+1 3 36 3 3 2 8 1 1 5 : brainpoolP224r1
+1 3 36 3 3 2 8 1 1 6 : brainpoolP224t1
+1 3 36 3 3 2 8 1 1 7 : brainpoolP256r1
+1 3 36 3 3 2 8 1 1 8 : brainpoolP256t1
+1 3 36 3 3 2 8 1 1 9 : brainpoolP320r1
+1 3 36 3 3 2 8 1 1 10 : brainpoolP320t1
+1 3 36 3 3 2 8 1 1 11 : brainpoolP384r1
+1 3 36 3 3 2 8 1 1 12 : brainpoolP384t1
+1 3 36 3 3 2 8 1 1 13 : brainpoolP512r1
+1 3 36 3 3 2 8 1 1 14 : brainpoolP512t1            
+
+# ECDH schemes from RFC5753
+!Alias x9-63-scheme 1 3 133 16 840 63 0
+!Alias secg-scheme certicom-arc 1
+
+x9-63-scheme 2   : dhSinglePass-stdDH-sha1kdf-scheme
+secg-scheme 11 0 : dhSinglePass-stdDH-sha224kdf-scheme
+secg-scheme 11 1 : dhSinglePass-stdDH-sha256kdf-scheme
+secg-scheme 11 2 : dhSinglePass-stdDH-sha384kdf-scheme
+secg-scheme 11 3 : dhSinglePass-stdDH-sha512kdf-scheme
+
+x9-63-scheme 3   : dhSinglePass-cofactorDH-sha1kdf-scheme
+secg-scheme 14 0 : dhSinglePass-cofactorDH-sha224kdf-scheme
+secg-scheme 14 1 : dhSinglePass-cofactorDH-sha256kdf-scheme
+secg-scheme 14 2 : dhSinglePass-cofactorDH-sha384kdf-scheme
+secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
+# NIDs for use with lookup tables.
+                 : dh-std-kdf
+                 : dh-cofactor-kdf
diff --git a/crypto/pem/CMakeLists.txt b/crypto/pem/CMakeLists.txt
new file mode 100644
index 0000000..a24d330
--- /dev/null
+++ b/crypto/pem/CMakeLists.txt
@@ -0,0 +1,17 @@
+include_directories(. .. ../../include)
+
+add_library(
+	pem
+
+	OBJECT
+
+	pem_all.c
+	pem_error.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/crypto/pem/pem.h b/crypto/pem/pem.h
new file mode 100644
index 0000000..469fbd2
--- /dev/null
+++ b/crypto/pem/pem.h
@@ -0,0 +1,615 @@
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_PEM_H
+#define OPENSSL_HEADER_PEM_H
+
+#include <openssl/base64.h>
+#include <openssl/bio.h>
+#include <openssl/cipher.h>
+#include <openssl/digest.h>
+#include <openssl/evp.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+#define PEM_BUFSIZE		1024
+
+#define PEM_OBJ_UNDEF		0
+#define PEM_OBJ_X509		1
+#define PEM_OBJ_X509_REQ	2
+#define PEM_OBJ_CRL		3
+#define PEM_OBJ_SSL_SESSION	4
+#define PEM_OBJ_PRIV_KEY	10
+#define PEM_OBJ_PRIV_RSA	11
+#define PEM_OBJ_PRIV_DSA	12
+#define PEM_OBJ_PRIV_DH		13
+#define PEM_OBJ_PUB_RSA		14
+#define PEM_OBJ_PUB_DSA		15
+#define PEM_OBJ_PUB_DH		16
+#define PEM_OBJ_DHPARAMS	17
+#define PEM_OBJ_DSAPARAMS	18
+#define PEM_OBJ_PRIV_RSA_PUBLIC	19
+#define PEM_OBJ_PRIV_ECDSA	20
+#define PEM_OBJ_PUB_ECDSA	21
+#define PEM_OBJ_ECPARAMETERS	22
+
+#define PEM_ERROR		30
+#define PEM_DEK_DES_CBC         40
+#define PEM_DEK_IDEA_CBC        45
+#define PEM_DEK_DES_EDE         50
+#define PEM_DEK_DES_ECB         60
+#define PEM_DEK_RSA             70
+#define PEM_DEK_RSA_MD2         80
+#define PEM_DEK_RSA_MD5         90
+
+#define PEM_MD_MD2		NID_md2
+#define PEM_MD_MD5		NID_md5
+#define PEM_MD_SHA		NID_sha
+#define PEM_MD_MD2_RSA		NID_md2WithRSAEncryption
+#define PEM_MD_MD5_RSA		NID_md5WithRSAEncryption
+#define PEM_MD_SHA_RSA		NID_sha1WithRSAEncryption
+
+#define PEM_STRING_X509_OLD	"X509 CERTIFICATE"
+#define PEM_STRING_X509		"CERTIFICATE"
+#define PEM_STRING_X509_PAIR	"CERTIFICATE PAIR"
+#define PEM_STRING_X509_TRUSTED	"TRUSTED CERTIFICATE"
+#define PEM_STRING_X509_REQ_OLD	"NEW CERTIFICATE REQUEST"
+#define PEM_STRING_X509_REQ	"CERTIFICATE REQUEST"
+#define PEM_STRING_X509_CRL	"X509 CRL"
+#define PEM_STRING_EVP_PKEY	"ANY PRIVATE KEY"
+#define PEM_STRING_PUBLIC	"PUBLIC KEY"
+#define PEM_STRING_RSA		"RSA PRIVATE KEY"
+#define PEM_STRING_RSA_PUBLIC	"RSA PUBLIC KEY"
+#define PEM_STRING_DSA		"DSA PRIVATE KEY"
+#define PEM_STRING_DSA_PUBLIC	"DSA PUBLIC KEY"
+#define PEM_STRING_PKCS7	"PKCS7"
+#define PEM_STRING_PKCS7_SIGNED	"PKCS #7 SIGNED DATA"
+#define PEM_STRING_PKCS8	"ENCRYPTED PRIVATE KEY"
+#define PEM_STRING_PKCS8INF	"PRIVATE KEY"
+#define PEM_STRING_DHPARAMS	"DH PARAMETERS"
+#define PEM_STRING_DHXPARAMS	"X9.42 DH PARAMETERS"
+#define PEM_STRING_SSL_SESSION	"SSL SESSION PARAMETERS"
+#define PEM_STRING_DSAPARAMS	"DSA PARAMETERS"
+#define PEM_STRING_ECDSA_PUBLIC "ECDSA PUBLIC KEY"
+#define PEM_STRING_ECPARAMETERS "EC PARAMETERS"
+#define PEM_STRING_ECPRIVATEKEY	"EC PRIVATE KEY"
+#define PEM_STRING_PARAMETERS	"PARAMETERS"
+#define PEM_STRING_CMS		"CMS"
+
+  /* Note that this structure is initialised by PEM_SealInit and cleaned up
+     by PEM_SealFinal (at least for now) */
+typedef struct PEM_Encode_Seal_st
+	{
+	EVP_ENCODE_CTX encode;
+	EVP_MD_CTX md;
+	EVP_CIPHER_CTX cipher;
+	} PEM_ENCODE_SEAL_CTX;
+
+/* enc_type is one off */
+#define PEM_TYPE_ENCRYPTED      10
+#define PEM_TYPE_MIC_ONLY       20
+#define PEM_TYPE_MIC_CLEAR      30
+#define PEM_TYPE_CLEAR		40
+
+typedef struct pem_recip_st
+	{
+	char *name;
+	X509_NAME *dn;
+
+	int cipher;
+	int key_enc;
+	/*	char iv[8]; unused and wrong size */
+	} PEM_USER;
+
+typedef struct pem_ctx_st
+	{
+	int type;		/* what type of object */
+
+	struct	{
+		int version;	
+		int mode;		
+		} proc_type;
+
+	char *domain;
+
+	struct	{
+		int cipher;
+	/* unused, and wrong size
+	   unsigned char iv[8]; */
+		} DEK_info;
+		
+	PEM_USER *originator;
+
+	int num_recipient;
+	PEM_USER **recipient;
+
+	/* XXX(ben): don#t think this is used! 
+		STACK *x509_chain;	/ * certificate chain */
+	EVP_MD *md;		/* signature type */
+
+	int md_enc;		/* is the md encrypted or not? */
+	int md_len;		/* length of md_data */
+	char *md_data;		/* message digest, could be pkey encrypted */
+
+	EVP_CIPHER *dec;	/* date encryption cipher */
+	int key_len;		/* key length */
+	unsigned char *key;	/* key */
+	/* unused, and wrong size
+	   unsigned char iv[8]; */
+
+	
+	int  data_enc;		/* is the data encrypted */
+	int data_len;
+	unsigned char *data;
+	} PEM_CTX;
+
+/* These macros make the PEM_read/PEM_write functions easier to maintain and
+ * write. Now they are all implemented with either:
+ * IMPLEMENT_PEM_rw(...) or IMPLEMENT_PEM_rw_cb(...)
+ */
+
+#ifdef OPENSSL_NO_FP_API
+
+#define IMPLEMENT_PEM_read_fp(name, type, str, asn1) /**/
+#define IMPLEMENT_PEM_write_fp(name, type, str, asn1) /**/
+#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1) /**/
+#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) /**/
+#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1) /**/
+
+#else
+
+#define IMPLEMENT_PEM_read_fp(name, type, str, asn1) \
+type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u)\
+{ \
+return PEM_ASN1_read((d2i_of_void *)d2i_##asn1, str,fp,(void **)x,cb,u); \
+} 
+
+#define IMPLEMENT_PEM_write_fp(name, type, str, asn1) \
+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_const(name, type, str, asn1) \
+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_cb_fp(name, type, str, asn1) \
+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) \
+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); \
+	}
+
+#endif
+
+#define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
+type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\
+{ \
+return PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
+}
+
+#define IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
+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_const(name, type, str, asn1) \
+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_cb_bio(name, type, str, asn1) \
+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_const(name, type, str, asn1) \
+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(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_fp(name, type, str, asn1) 
+
+#define IMPLEMENT_PEM_write_const(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_bio_const(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_fp_const(name, type, str, asn1) 
+
+#define IMPLEMENT_PEM_write_cb(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) 
+
+#define IMPLEMENT_PEM_write_cb_const(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1) 
+
+#define IMPLEMENT_PEM_read(name, type, str, asn1) \
+	IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
+	IMPLEMENT_PEM_read_fp(name, type, str, asn1) 
+
+#define IMPLEMENT_PEM_rw(name, type, str, asn1) \
+	IMPLEMENT_PEM_read(name, type, str, asn1) \
+	IMPLEMENT_PEM_write(name, type, str, asn1)
+
+#define IMPLEMENT_PEM_rw_const(name, type, str, asn1) \
+	IMPLEMENT_PEM_read(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_const(name, type, str, asn1)
+
+#define IMPLEMENT_PEM_rw_cb(name, type, str, asn1) \
+	IMPLEMENT_PEM_read(name, type, str, asn1) \
+	IMPLEMENT_PEM_write_cb(name, type, str, asn1)
+
+/* These are the same except they are for the declarations */
+
+#if defined(OPENSSL_NO_FP_API)
+
+#define DECLARE_PEM_read_fp(name, type) /**/
+#define DECLARE_PEM_write_fp(name, type) /**/
+#define DECLARE_PEM_write_cb_fp(name, type) /**/
+
+#else
+
+#define DECLARE_PEM_read_fp(name, type) \
+	type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u);
+
+#define DECLARE_PEM_write_fp(name, type) \
+	int PEM_write_##name(FILE *fp, type *x);
+
+#define DECLARE_PEM_write_fp_const(name, type) \
+	int PEM_write_##name(FILE *fp, const type *x);
+
+#define DECLARE_PEM_write_cb_fp(name, type) \
+	int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \
+	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u);
+
+#endif
+
+#ifndef OPENSSL_NO_BIO
+#define DECLARE_PEM_read_bio(name, type) \
+	type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u);
+
+#define DECLARE_PEM_write_bio(name, type) \
+	int PEM_write_bio_##name(BIO *bp, type *x);
+
+#define DECLARE_PEM_write_bio_const(name, type) \
+	int PEM_write_bio_##name(BIO *bp, const type *x);
+
+#define DECLARE_PEM_write_cb_bio(name, type) \
+	int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \
+	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u);
+
+#else
+
+#define DECLARE_PEM_read_bio(name, type) /**/
+#define DECLARE_PEM_write_bio(name, type) /**/
+#define DECLARE_PEM_write_bio_const(name, type) /**/
+#define DECLARE_PEM_write_cb_bio(name, type) /**/
+
+#endif
+
+#define DECLARE_PEM_write(name, type) \
+	DECLARE_PEM_write_bio(name, type) \
+	DECLARE_PEM_write_fp(name, type) 
+
+#define DECLARE_PEM_write_const(name, type) \
+	DECLARE_PEM_write_bio_const(name, type) \
+	DECLARE_PEM_write_fp_const(name, type)
+
+#define DECLARE_PEM_write_cb(name, type) \
+	DECLARE_PEM_write_cb_bio(name, type) \
+	DECLARE_PEM_write_cb_fp(name, type) 
+
+#define DECLARE_PEM_read(name, type) \
+	DECLARE_PEM_read_bio(name, type) \
+	DECLARE_PEM_read_fp(name, type)
+
+#define DECLARE_PEM_rw(name, type) \
+	DECLARE_PEM_read(name, type) \
+	DECLARE_PEM_write(name, type)
+
+#define DECLARE_PEM_rw_const(name, type) \
+	DECLARE_PEM_read(name, type) \
+	DECLARE_PEM_write_const(name, type)
+
+#define DECLARE_PEM_rw_cb(name, type) \
+	DECLARE_PEM_read(name, type) \
+	DECLARE_PEM_write_cb(name, type)
+
+#if 1
+/* "userdata": new with OpenSSL 0.9.4 */
+typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata);
+#else
+/* OpenSSL 0.9.3, 0.9.3a */
+typedef int pem_password_cb(char *buf, int size, int rwflag);
+#endif
+
+int	PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher);
+int	PEM_do_header (EVP_CIPHER_INFO *cipher, unsigned char *data,long *len,
+	pem_password_cb *callback,void *u);
+
+#ifndef OPENSSL_NO_BIO
+int	PEM_read_bio(BIO *bp, char **name, char **header,
+		unsigned char **data,long *len);
+int	PEM_write_bio(BIO *bp,const char *name, const char *hdr,
+		      const unsigned char *data, long len);
+int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm, const char *name, BIO *bp,
+	     pem_password_cb *cb, void *u);
+void *	PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp,
+			  void **x, pem_password_cb *cb, void *u);
+int	PEM_ASN1_write_bio(i2d_of_void *i2d,const char *name,BIO *bp, void *x,
+			   const EVP_CIPHER *enc,unsigned char *kstr,int klen,
+			   pem_password_cb *cb, void *u);
+
+STACK_OF(X509_INFO) *	PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk, pem_password_cb *cb, void *u);
+int	PEM_X509_INFO_write_bio(BIO *bp,X509_INFO *xi, EVP_CIPHER *enc,
+		unsigned char *kstr, int klen, pem_password_cb *cd, void *u);
+#endif
+
+int	PEM_read(FILE *fp, char **name, char **header,
+		unsigned char **data,long *len);
+int	PEM_write(FILE *fp, const char *name, const char *hdr,
+		  const unsigned char *data, long len);
+void *  PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
+		      pem_password_cb *cb, void *u);
+int	PEM_ASN1_write(i2d_of_void *i2d,const char *name,FILE *fp,
+		       void *x,const EVP_CIPHER *enc,unsigned char *kstr,
+		       int klen,pem_password_cb *callback, void *u);
+STACK_OF(X509_INFO) *	PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
+	pem_password_cb *cb, void *u);
+
+int	PEM_SealInit(PEM_ENCODE_SEAL_CTX *ctx, EVP_CIPHER *type,
+		EVP_MD *md_type, unsigned char **ek, int *ekl,
+		unsigned char *iv, EVP_PKEY **pubk, int npubk);
+void	PEM_SealUpdate(PEM_ENCODE_SEAL_CTX *ctx, unsigned char *out, int *outl,
+		unsigned char *in, int inl);
+int	PEM_SealFinal(PEM_ENCODE_SEAL_CTX *ctx, unsigned char *sig,int *sigl,
+		unsigned char *out, int *outl, EVP_PKEY *priv);
+
+void    PEM_SignInit(EVP_MD_CTX *ctx, EVP_MD *type);
+void    PEM_SignUpdate(EVP_MD_CTX *ctx,unsigned char *d,unsigned int cnt);
+int	PEM_SignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,
+		unsigned int *siglen, EVP_PKEY *pkey);
+
+int	PEM_def_callback(char *buf, int num, int w, void *key);
+void	PEM_proc_type(char *buf, int type);
+void	PEM_dek_info(char *buf, const char *type, int len, char *str);
+
+
+DECLARE_PEM_rw(X509, X509)
+
+DECLARE_PEM_rw(X509_AUX, X509)
+
+DECLARE_PEM_rw(X509_CERT_PAIR, X509_CERT_PAIR)
+
+DECLARE_PEM_rw(X509_REQ, X509_REQ)
+DECLARE_PEM_write(X509_REQ_NEW, X509_REQ)
+
+DECLARE_PEM_rw(X509_CRL, X509_CRL)
+
+/* DECLARE_PEM_rw(PKCS7, PKCS7) */
+
+DECLARE_PEM_rw(NETSCAPE_CERT_SEQUENCE, NETSCAPE_CERT_SEQUENCE)
+
+DECLARE_PEM_rw(PKCS8, X509_SIG)
+
+DECLARE_PEM_rw(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO)
+
+#ifndef OPENSSL_NO_RSA
+
+DECLARE_PEM_rw_cb(RSAPrivateKey, RSA)
+
+DECLARE_PEM_rw_const(RSAPublicKey, RSA)
+DECLARE_PEM_rw(RSA_PUBKEY, RSA)
+
+#endif
+
+#ifndef OPENSSL_NO_DSA
+
+DECLARE_PEM_rw_cb(DSAPrivateKey, DSA)
+
+DECLARE_PEM_rw(DSA_PUBKEY, DSA)
+
+DECLARE_PEM_rw_const(DSAparams, DSA)
+
+#endif
+
+#ifndef OPENSSL_NO_EC
+DECLARE_PEM_rw_const(ECPKParameters, EC_GROUP)
+DECLARE_PEM_rw_cb(ECPrivateKey, EC_KEY)
+DECLARE_PEM_rw(EC_PUBKEY, EC_KEY)
+#endif
+
+#ifndef OPENSSL_NO_DH
+
+DECLARE_PEM_rw_const(DHparams, DH)
+DECLARE_PEM_write_const(DHxparams, DH)
+
+#endif
+
+DECLARE_PEM_rw_cb(PrivateKey, EVP_PKEY)
+
+DECLARE_PEM_rw(PUBKEY, EVP_PKEY)
+
+int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u);
+int PEM_write_bio_PKCS8PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *,
+                                  char *, int, pem_password_cb *, void *);
+int i2d_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u);
+int i2d_PKCS8PrivateKey_nid_bio(BIO *bp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u);
+EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u);
+
+int i2d_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u);
+int i2d_PKCS8PrivateKey_nid_fp(FILE *fp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u);
+int PEM_write_PKCS8PrivateKey_nid(FILE *fp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u);
+
+EVP_PKEY *d2i_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, void *u);
+
+int PEM_write_PKCS8PrivateKey(FILE *fp,EVP_PKEY *x,const EVP_CIPHER *enc,
+			      char *kstr,int klen, pem_password_cb *cd, void *u);
+
+EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x);
+int PEM_write_bio_Parameters(BIO *bp, EVP_PKEY *x);
+
+
+EVP_PKEY *b2i_PrivateKey(const unsigned char **in, long length);
+EVP_PKEY *b2i_PublicKey(const unsigned char **in, long length);
+EVP_PKEY *b2i_PrivateKey_bio(BIO *in);
+EVP_PKEY *b2i_PublicKey_bio(BIO *in);
+int i2b_PrivateKey_bio(BIO *out, EVP_PKEY *pk);
+int i2b_PublicKey_bio(BIO *out, EVP_PKEY *pk);
+#ifndef OPENSSL_NO_RC4
+EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u);
+int i2b_PVK_bio(BIO *out, EVP_PKEY *pk, int enclevel,
+		pem_password_cb *cb, void *u);
+#endif
+
+
+void ERR_load_PEM_strings(void);
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#define PEM_F_PEM_read_bio_DHparams 100
+#define PEM_F_load_iv 101
+#define PEM_F_PEM_write 102
+#define PEM_F_do_pk8pkey_fp 103
+#define PEM_F_PEM_read_PrivateKey 104
+#define PEM_F_PEM_read_DHparams 105
+#define PEM_F_PEM_ASN1_read_bio 106
+#define PEM_F_PEM_ASN1_read 107
+#define PEM_F_PEM_get_EVP_CIPHER_INFO 108
+#define PEM_F_PEM_X509_INFO_read 109
+#define PEM_F_PEM_read_bio_Parameters 110
+#define PEM_F_PEM_read 111
+#define PEM_F_PEM_X509_INFO_read_bio 112
+#define PEM_F_PEM_X509_INFO_write_bio 113
+#define PEM_F_PEM_ASN1_write 114
+#define PEM_F_d2i_PKCS8PrivateKey_bio 115
+#define PEM_F_d2i_PKCS8PrivateKey_fp 116
+#define PEM_F_PEM_read_bio_PrivateKey 117
+#define PEM_F_PEM_write_PrivateKey 118
+#define PEM_F_PEM_ASN1_write_bio 119
+#define PEM_F_PEM_do_header 120
+#define PEM_F_PEM_write_bio 121
+#define PEM_F_do_pk8pkey 122
+#define PEM_F_PEM_read_bio 123
+#define PEM_R_NO_START_LINE 100
+#define PEM_R_NOT_PROC_TYPE 101
+#define PEM_R_SHORT_HEADER 102
+#define PEM_R_BAD_IV_CHARS 103
+#define PEM_R_ERROR_CONVERTING_PRIVATE_KEY 104
+#define PEM_R_BAD_END_LINE 105
+#define PEM_R_CIPHER_IS_NULL 106
+#define PEM_R_BAD_MAGIC_NUMBER 107
+#define PEM_R_BAD_DECRYPT 108
+#define PEM_R_UNSUPPORTED_ENCRYPTION 109
+#define PEM_R_PVK_DATA_TOO_SHORT 110
+#define PEM_R_PROBLEMS_GETTING_PASSWORD 111
+#define PEM_R_KEYBLOB_HEADER_PARSE_ERROR 112
+#define PEM_R_BIO_WRITE_FAILURE 113
+#define PEM_R_INCONSISTENT_HEADER 114
+#define PEM_R_PUBLIC_KEY_NO_RSA 115
+#define PEM_R_EXPECTING_PUBLIC_KEY_BLOB 116
+#define PEM_R_KEYBLOB_TOO_SHORT 117
+#define PEM_R_BAD_BASE64_DECODE 118
+#define PEM_R_READ_KEY 119
+#define PEM_R_BAD_PASSWORD_READ 120
+#define PEM_R_UNSUPPORTED_KEY_COMPONENTS 121
+#define PEM_R_UNSUPPORTED_CIPHER 122
+#define PEM_R_NOT_ENCRYPTED 123
+#define PEM_R_NOT_DEK_INFO 124
+#define PEM_R_BAD_VERSION_NUMBER 125
+#define PEM_R_EXPECTING_PRIVATE_KEY_BLOB 126
+#define PEM_R_PVK_TOO_SHORT 127
+
+#endif  /* OPENSSL_HEADER_PEM_H */
diff --git a/crypto/pem/pem_all.c b/crypto/pem/pem_all.c
new file mode 100644
index 0000000..ccdc2e8
--- /dev/null
+++ b/crypto/pem/pem_all.c
@@ -0,0 +1,442 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/bio.h>
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+/*#include <openssl/pkcs7.h> */
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+
+static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa);
+static DSA *pkey_get_dsa(EVP_PKEY *key, DSA **dsa);
+static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey);
+
+
+IMPLEMENT_PEM_rw(X509_REQ, X509_REQ, PEM_STRING_X509_REQ, X509_REQ)
+
+IMPLEMENT_PEM_write(X509_REQ_NEW, X509_REQ, PEM_STRING_X509_REQ_OLD, X509_REQ)
+
+IMPLEMENT_PEM_rw(X509_CRL, X509_CRL, PEM_STRING_X509_CRL, X509_CRL)
+
+
+#ifndef OPENSSL_NO_RSA
+
+/* We treat RSA or DSA private keys as a special case.
+ *
+ * For private keys we read in an EVP_PKEY structure with
+ * PEM_read_bio_PrivateKey() and extract the relevant private
+ * key: this means can handle "traditional" and PKCS#8 formats
+ * transparently.
+ */
+
+static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa)
+{
+	RSA *rtmp;
+	if(!key) return NULL;
+	rtmp = EVP_PKEY_get1_RSA(key);
+	EVP_PKEY_free(key);
+	if(!rtmp) return NULL;
+	if(rsa) {
+		RSA_free(*rsa);
+		*rsa = rtmp;
+	}
+	return rtmp;
+}
+
+RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb,
+								void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+	return pkey_get_rsa(pktmp, rsa);
+}
+
+#ifndef OPENSSL_NO_FP_API
+
+RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb,
+								void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+	return pkey_get_rsa(pktmp, rsa);
+}
+
+#endif
+
+#ifdef OPENSSL_FIPS
+
+int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+{
+	if (FIPS_mode())
+		{
+		EVP_PKEY *k;
+		int ret;
+		k = EVP_PKEY_new();
+		if (!k)
+			return 0;
+		EVP_PKEY_set1_RSA(k, x);
+
+		ret = PEM_write_bio_PrivateKey(bp, k, enc, kstr, klen, cb, u);
+		EVP_PKEY_free(k);
+		return ret;
+		}
+	else
+		return PEM_ASN1_write_bio((i2d_of_void *)i2d_RSAPrivateKey,
+					PEM_STRING_RSA,bp,x,enc,kstr,klen,cb,u);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int PEM_write_RSAPrivateKey(FILE *fp, RSA *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+{
+	if (FIPS_mode())
+		{
+		EVP_PKEY *k;
+		int ret;
+		k = EVP_PKEY_new();
+		if (!k)
+			return 0;
+
+		EVP_PKEY_set1_RSA(k, x);
+
+		ret = PEM_write_PrivateKey(fp, k, enc, kstr, klen, cb, u);
+		EVP_PKEY_free(k);
+		return ret;
+		}
+	else
+		return PEM_ASN1_write((i2d_of_void *)i2d_RSAPrivateKey,
+					PEM_STRING_RSA,fp,x,enc,kstr,klen,cb,u);
+}
+#endif
+
+#else
+
+IMPLEMENT_PEM_write_cb_const(RSAPrivateKey, RSA, PEM_STRING_RSA, RSAPrivateKey)
+
+#endif
+
+IMPLEMENT_PEM_rw_const(RSAPublicKey, RSA, PEM_STRING_RSA_PUBLIC, RSAPublicKey)
+IMPLEMENT_PEM_rw(RSA_PUBKEY, RSA, PEM_STRING_PUBLIC, RSA_PUBKEY)
+
+#endif
+
+#ifndef OPENSSL_NO_DSA
+
+static DSA *pkey_get_dsa(EVP_PKEY *key, DSA **dsa)
+{
+	DSA *dtmp;
+	if(!key) return NULL;
+	dtmp = EVP_PKEY_get1_DSA(key);
+	EVP_PKEY_free(key);
+	if(!dtmp) return NULL;
+	if(dsa) {
+		DSA_free(*dsa);
+		*dsa = dtmp;
+	}
+	return dtmp;
+}
+
+DSA *PEM_read_bio_DSAPrivateKey(BIO *bp, DSA **dsa, pem_password_cb *cb,
+								void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+	return pkey_get_dsa(pktmp, dsa);	/* will free pktmp */
+}
+
+#ifdef OPENSSL_FIPS
+
+int PEM_write_bio_DSAPrivateKey(BIO *bp, DSA *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+{
+	if (FIPS_mode())
+		{
+		EVP_PKEY *k;
+		int ret;
+		k = EVP_PKEY_new();
+		if (!k)
+			return 0;
+		EVP_PKEY_set1_DSA(k, x);
+
+		ret = PEM_write_bio_PrivateKey(bp, k, enc, kstr, klen, cb, u);
+		EVP_PKEY_free(k);
+		return ret;
+		}
+	else
+		return PEM_ASN1_write_bio((i2d_of_void *)i2d_DSAPrivateKey,
+					PEM_STRING_DSA,bp,x,enc,kstr,klen,cb,u);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int PEM_write_DSAPrivateKey(FILE *fp, DSA *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+{
+	if (FIPS_mode())
+		{
+		EVP_PKEY *k;
+		int ret;
+		k = EVP_PKEY_new();
+		if (!k)
+			return 0;
+		EVP_PKEY_set1_DSA(k, x);
+		ret = PEM_write_PrivateKey(fp, k, enc, kstr, klen, cb, u);
+		EVP_PKEY_free(k);
+		return ret;
+		}
+	else
+		return PEM_ASN1_write((i2d_of_void *)i2d_DSAPrivateKey,
+					PEM_STRING_DSA,fp,x,enc,kstr,klen,cb,u);
+}
+#endif
+
+#else
+
+IMPLEMENT_PEM_write_cb_const(DSAPrivateKey, DSA, PEM_STRING_DSA, DSAPrivateKey)
+
+#endif
+
+IMPLEMENT_PEM_rw(DSA_PUBKEY, DSA, PEM_STRING_PUBLIC, DSA_PUBKEY)
+
+#ifndef OPENSSL_NO_FP_API
+
+DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **dsa, pem_password_cb *cb,
+								void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+	return pkey_get_dsa(pktmp, dsa);	/* will free pktmp */
+}
+
+#endif
+
+IMPLEMENT_PEM_rw_const(DSAparams, DSA, PEM_STRING_DSAPARAMS, DSAparams)
+
+#endif
+
+
+#ifndef OPENSSL_NO_EC
+static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey)
+{
+	EC_KEY *dtmp;
+	if(!key) return NULL;
+	dtmp = EVP_PKEY_get1_EC_KEY(key);
+	EVP_PKEY_free(key);
+	if(!dtmp) return NULL;
+	if(eckey) 
+	{
+ 		EC_KEY_free(*eckey);
+		*eckey = dtmp;
+	}
+	return dtmp;
+}
+
+EC_KEY *PEM_read_bio_ECPrivateKey(BIO *bp, EC_KEY **key, pem_password_cb *cb,
+							void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+	return pkey_get_eckey(pktmp, key);	/* will free pktmp */
+}
+
+/* TODO(fork): remove this code? */
+/* IMPLEMENT_PEM_rw_const(ECPKParameters, EC_GROUP, PEM_STRING_ECPARAMETERS, ECPKParameters) */
+
+
+
+#ifdef OPENSSL_FIPS
+
+int PEM_write_bio_ECPrivateKey(BIO *bp, EC_KEY *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+{
+	if (FIPS_mode())
+		{
+		EVP_PKEY *k;
+		int ret;
+		k = EVP_PKEY_new();
+		if (!k)
+			return 0;
+		EVP_PKEY_set1_EC_KEY(k, x);
+
+		ret = PEM_write_bio_PrivateKey(bp, k, enc, kstr, klen, cb, u);
+		EVP_PKEY_free(k);
+		return ret;
+		}
+	else
+		return PEM_ASN1_write_bio((i2d_of_void *)i2d_ECPrivateKey,
+						PEM_STRING_ECPRIVATEKEY,
+						bp,x,enc,kstr,klen,cb,u);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int PEM_write_ECPrivateKey(FILE *fp, EC_KEY *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+{
+	if (FIPS_mode())
+		{
+		EVP_PKEY *k;
+		int ret;
+		k = EVP_PKEY_new();
+		if (!k)
+			return 0;
+		EVP_PKEY_set1_EC_KEY(k, x);
+		ret = PEM_write_PrivateKey(fp, k, enc, kstr, klen, cb, u);
+		EVP_PKEY_free(k);
+		return ret;
+		}
+	else
+		return PEM_ASN1_write((i2d_of_void *)i2d_ECPrivateKey,
+						PEM_STRING_ECPRIVATEKEY,
+						fp,x,enc,kstr,klen,cb,u);
+}
+#endif
+
+#else
+
+IMPLEMENT_PEM_write_cb(ECPrivateKey, EC_KEY, PEM_STRING_ECPRIVATEKEY, ECPrivateKey)
+
+#endif
+
+IMPLEMENT_PEM_rw(EC_PUBKEY, EC_KEY, PEM_STRING_PUBLIC, EC_PUBKEY)
+
+#ifndef OPENSSL_NO_FP_API
+ 
+EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
+ 								void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+	return pkey_get_eckey(pktmp, eckey);	/* will free pktmp */
+}
+
+#endif
+
+#endif
+
+#ifndef OPENSSL_NO_DH
+
+IMPLEMENT_PEM_write_const(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
+
+/* TODO(fork): remove this code? */
+/* IMPLEMENT_PEM_write_const(DHxparams, DH, PEM_STRING_DHXPARAMS, DHxparams) */
+
+#endif
+
+IMPLEMENT_PEM_rw(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
diff --git a/crypto/pem/pem_error.c b/crypto/pem/pem_error.c
new file mode 100644
index 0000000..99c9b32
--- /dev/null
+++ b/crypto/pem/pem_error.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "pem.h"
+
+const ERR_STRING_DATA PEM_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_ASN1_read, 0), "PEM_ASN1_read"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_ASN1_read_bio, 0), "PEM_ASN1_read_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_ASN1_write, 0), "PEM_ASN1_write"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_ASN1_write_bio, 0), "PEM_ASN1_write_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_X509_INFO_read, 0), "PEM_X509_INFO_read"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_X509_INFO_read_bio, 0), "PEM_X509_INFO_read_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_X509_INFO_write_bio, 0), "PEM_X509_INFO_write_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_do_header, 0), "PEM_do_header"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_get_EVP_CIPHER_INFO, 0), "PEM_get_EVP_CIPHER_INFO"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read, 0), "PEM_read"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read_DHparams, 0), "PEM_read_DHparams"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read_PrivateKey, 0), "PEM_read_PrivateKey"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read_bio, 0), "PEM_read_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read_bio_DHparams, 0), "PEM_read_bio_DHparams"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read_bio_Parameters, 0), "PEM_read_bio_Parameters"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_read_bio_PrivateKey, 0), "PEM_read_bio_PrivateKey"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_write, 0), "PEM_write"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_write_PrivateKey, 0), "PEM_write_PrivateKey"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_write_bio, 0), "PEM_write_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_d2i_PKCS8PrivateKey_bio, 0), "d2i_PKCS8PrivateKey_bio"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_d2i_PKCS8PrivateKey_fp, 0), "d2i_PKCS8PrivateKey_fp"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_do_pk8pkey, 0), "do_pk8pkey"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_do_pk8pkey_fp, 0), "do_pk8pkey_fp"},
+  {ERR_PACK(ERR_LIB_PEM, PEM_F_load_iv, 0), "load_iv"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_BASE64_DECODE), "BAD_BASE64_DECODE"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_DECRYPT), "BAD_DECRYPT"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_END_LINE), "BAD_END_LINE"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_IV_CHARS), "BAD_IV_CHARS"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_MAGIC_NUMBER), "BAD_MAGIC_NUMBER"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_PASSWORD_READ), "BAD_PASSWORD_READ"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_VERSION_NUMBER), "BAD_VERSION_NUMBER"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BIO_WRITE_FAILURE), "BIO_WRITE_FAILURE"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_CIPHER_IS_NULL), "CIPHER_IS_NULL"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_ERROR_CONVERTING_PRIVATE_KEY), "ERROR_CONVERTING_PRIVATE_KEY"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_PRIVATE_KEY_BLOB), "EXPECTING_PRIVATE_KEY_BLOB"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_PUBLIC_KEY_BLOB), "EXPECTING_PUBLIC_KEY_BLOB"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_INCONSISTENT_HEADER), "INCONSISTENT_HEADER"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_KEYBLOB_HEADER_PARSE_ERROR), "KEYBLOB_HEADER_PARSE_ERROR"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_KEYBLOB_TOO_SHORT), "KEYBLOB_TOO_SHORT"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_NOT_DEK_INFO), "NOT_DEK_INFO"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_NOT_ENCRYPTED), "NOT_ENCRYPTED"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_NOT_PROC_TYPE), "NOT_PROC_TYPE"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_NO_START_LINE), "NO_START_LINE"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_PROBLEMS_GETTING_PASSWORD), "PROBLEMS_GETTING_PASSWORD"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_PUBLIC_KEY_NO_RSA), "PUBLIC_KEY_NO_RSA"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_PVK_DATA_TOO_SHORT), "PVK_DATA_TOO_SHORT"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_PVK_TOO_SHORT), "PVK_TOO_SHORT"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_READ_KEY), "READ_KEY"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_SHORT_HEADER), "SHORT_HEADER"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_UNSUPPORTED_CIPHER), "UNSUPPORTED_CIPHER"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_UNSUPPORTED_ENCRYPTION), "UNSUPPORTED_ENCRYPTION"},
+  {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_UNSUPPORTED_KEY_COMPONENTS), "UNSUPPORTED_KEY_COMPONENTS"},
+  {0, NULL},
+};
diff --git a/crypto/pem/pem_info.c b/crypto/pem/pem_info.c
new file mode 100644
index 0000000..9536e49
--- /dev/null
+++ b/crypto/pem/pem_info.c
@@ -0,0 +1,409 @@
+/* crypto/pem/pem_info.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include <openssl/pem.h>
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <openssl/buf.h>
+#include <openssl/dsa.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+
+#ifndef OPENSSL_NO_FP_API
+STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk, pem_password_cb *cb, void *u)
+	{
+        BIO *b;
+        STACK_OF(X509_INFO) *ret;
+
+        if ((b=BIO_new(BIO_s_file())) == NULL)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_X509_INFO_read, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_X509_INFO_read_bio(b,sk,cb,u);
+        BIO_free(b);
+        return(ret);
+	}
+#endif
+
+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;
+	char *name=NULL,*header=NULL;
+	void *pp;
+	unsigned char *data=NULL;
+	const unsigned char *p;
+	long len,error=0;
+	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)
+			{
+			OPENSSL_PUT_ERROR(PEM, PEM_X509_INFO_read_bio, ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+		}
+	else
+		ret=sk;
+
+	if ((xi=X509_INFO_new()) == 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)
+				{
+				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);
+			}
+		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);
+			}
+		else
+#ifndef OPENSSL_NO_RSA
+			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
+#endif
+#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
+#ifndef OPENSSL_NO_EC
+ 			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
+#endif
+			{
+			d2i=NULL;
+			pp=NULL;
+			}
+
+		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, PEM_X509_INFO_read_bio, ERR_R_ASN1_LIB);
+						goto err;
+						}
+					}
+				else if (d2i(pp,&p,len) == NULL)
+					{
+					OPENSSL_PUT_ERROR(PEM, PEM_X509_INFO_read_bio, ERR_R_ASN1_LIB);
+					goto err;
+					}
+				}
+			else
+				{ /* encrypted RSA data */
+				if (!PEM_get_EVP_CIPHER_INFO(header,
+					&xi->enc_cipher)) goto err;
+				xi->enc_data=(char *)data;
+				xi->enc_len=(int)len;
+				data=NULL;
+				}
+			}
+		else	{
+			/* unknown */
+			}
+		if (name != NULL) OPENSSL_free(name);
+		if (header != NULL) OPENSSL_free(header);
+		if (data != NULL) 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)) goto err;
+		xi=NULL;
+		}
+	ok=1;
+err:
+	if (xi != NULL) X509_INFO_free(xi);
+	if (!ok)
+		{
+		for (i=0; ((int)i)<sk_X509_INFO_num(ret); i++)
+			{
+			xi=sk_X509_INFO_value(ret,i);
+			X509_INFO_free(xi);
+			}
+		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);
+	}
+
+
+/* A TJH addition */
+int PEM_X509_INFO_write_bio(BIO *bp, X509_INFO *xi, EVP_CIPHER *enc,
+	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u)
+	{
+	EVP_CIPHER_CTX ctx;
+	int i,ret=0;
+	unsigned char *data=NULL;
+	const char *objstr=NULL;
+	char buf[PEM_BUFSIZE];
+	unsigned char *iv=NULL;
+	unsigned iv_len = 0;
+	
+	if (enc != NULL)
+		{
+		iv_len = EVP_CIPHER_iv_length(enc);
+		objstr=OBJ_nid2sn(EVP_CIPHER_nid(enc));
+		if (objstr == NULL)
+			{
+			OPENSSL_PUT_ERROR(PEM, PEM_X509_INFO_write_bio, PEM_R_UNSUPPORTED_CIPHER);
+			goto err;
+			}
+		}
+
+	/* now for the fun part ... if we have a private key then 
+	 * we have to be able to handle a not-yet-decrypted key
+	 * being written out correctly ... if it is decrypted or
+	 * it is non-encrypted then we use the base code
+	 */
+	if (xi->x_pkey!=NULL)
+		{
+		if ( (xi->enc_data!=NULL) && (xi->enc_len>0) )
+			{
+			if (enc == NULL)
+				{
+				OPENSSL_PUT_ERROR(PEM, PEM_X509_INFO_write_bio, PEM_R_CIPHER_IS_NULL);
+				goto err;
+				}
+
+			/* copy from weirdo names into more normal things */
+			iv=xi->enc_cipher.iv;
+			data=(unsigned char *)xi->enc_data;
+			i=xi->enc_len;
+
+			/* we take the encryption data from the
+			 * internal stuff rather than what the
+			 * user has passed us ... as we have to 
+			 * match exactly for some strange reason
+			 */
+			objstr=OBJ_nid2sn(
+				EVP_CIPHER_nid(xi->enc_cipher.cipher));
+			if (objstr == NULL)
+				{
+				OPENSSL_PUT_ERROR(PEM, PEM_X509_INFO_write_bio, PEM_R_UNSUPPORTED_CIPHER);
+				goto err;
+				}
+
+			/* create the right magic header stuff */
+			assert(strlen(objstr)+23+2*iv_len+13 <= sizeof buf);
+			buf[0]='\0';
+			PEM_proc_type(buf,PEM_TYPE_ENCRYPTED);
+			PEM_dek_info(buf,objstr,iv_len,(char *)iv);
+
+			/* use the normal code to write things out */
+			i=PEM_write_bio(bp,PEM_STRING_RSA,buf,data,i);
+			if (i <= 0) goto err;
+			}
+		else
+			{
+			/* Add DSA/DH */
+#ifndef OPENSSL_NO_RSA
+			/* normal optionally encrypted stuff */
+			if (PEM_write_bio_RSAPrivateKey(bp,
+				xi->x_pkey->dec_pkey->pkey.rsa,
+				enc,kstr,klen,cb,u)<=0)
+				goto err;
+#endif
+			}
+		}
+
+	/* if we have a certificate then write it out now */
+	if ((xi->x509 != NULL) && (PEM_write_bio_X509(bp,xi->x509) <= 0))
+		goto err;
+
+	/* we are ignoring anything else that is loaded into the X509_INFO
+	 * structure for the moment ... as I don't need it so I'm not
+	 * coding it here and Eric can do it when this makes it into the
+	 * base library --tjh
+	 */
+
+	ret=1;
+
+err:
+	OPENSSL_cleanse((char *)&ctx,sizeof(ctx));
+	OPENSSL_cleanse(buf,PEM_BUFSIZE);
+	return(ret);
+	}
diff --git a/crypto/pem/pem_lib.c b/crypto/pem/pem_lib.c
new file mode 100644
index 0000000..23efd0a
--- /dev/null
+++ b/crypto/pem/pem_lib.c
@@ -0,0 +1,825 @@
+/* crypto/pem/pem_lib.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include <openssl/base64.h>
+#include <openssl/buf.h>
+#include <openssl/des.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include "../evp/internal.h"
+
+
+#define MIN_LENGTH	4
+
+static int load_iv(char **fromp,unsigned char *to, int num);
+static int check_pem(const char *nm, const char *name);
+int pem_check_suffix(const char *pem_str, const char *suffix);
+
+void PEM_proc_type(char *buf, int type)
+	{
+	const char *str;
+
+	if (type == PEM_TYPE_ENCRYPTED)
+		str="ENCRYPTED";
+	else if (type == PEM_TYPE_MIC_CLEAR)
+		str="MIC-CLEAR";
+	else if (type == PEM_TYPE_MIC_ONLY)
+		str="MIC-ONLY";
+	else
+		str="BAD-TYPE";
+		
+	BUF_strlcat(buf,"Proc-Type: 4,",PEM_BUFSIZE);
+	BUF_strlcat(buf,str,PEM_BUFSIZE);
+	BUF_strlcat(buf,"\n",PEM_BUFSIZE);
+	}
+
+void PEM_dek_info(char *buf, const char *type, int len, char *str)
+	{
+	static const unsigned char map[17]="0123456789ABCDEF";
+	long i;
+	int j;
+
+	BUF_strlcat(buf,"DEK-Info: ",PEM_BUFSIZE);
+	BUF_strlcat(buf,type,PEM_BUFSIZE);
+	BUF_strlcat(buf,",",PEM_BUFSIZE);
+	j=strlen(buf);
+	if (j + (len * 2) + 1 > PEM_BUFSIZE)
+        	return;
+	for (i=0; i<len; i++)
+		{
+		buf[j+i*2]  =map[(str[i]>>4)&0x0f];
+		buf[j+i*2+1]=map[(str[i]   )&0x0f];
+		}
+	buf[j+i*2]='\n';
+	buf[j+i*2+1]='\0';
+	}
+
+#ifndef OPENSSL_NO_FP_API
+void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
+		    pem_password_cb *cb, void *u)
+	{
+        BIO *b;
+        void *ret;
+
+        if ((b=BIO_new(BIO_s_file())) == NULL)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_ASN1_read, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_ASN1_read_bio(d2i,name,b,x,cb,u);
+        BIO_free(b);
+        return(ret);
+	}
+#endif
+
+static int check_pem(const char *nm, const char *name)
+{
+	/* Normal matching nm and name */
+	if (!strcmp(nm,name)) return 1;
+
+	/* Make PEM_STRING_EVP_PKEY match any private key */
+
+	if(!strcmp(name,PEM_STRING_EVP_PKEY))
+		{
+		int slen;
+		const EVP_PKEY_ASN1_METHOD *ameth;
+		if(!strcmp(nm,PEM_STRING_PKCS8))
+			return 1;
+		if(!strcmp(nm,PEM_STRING_PKCS8INF))
+			return 1;
+		slen = pem_check_suffix(nm, "PRIVATE KEY"); 
+		if (slen > 0)
+			{
+			/* NB: ENGINE implementations wont contain
+			 * a deprecated old private key decode function
+			 * so don't look for them.
+			 */
+			ameth = EVP_PKEY_asn1_find_str(NULL, nm, slen);
+			if (ameth && ameth->old_priv_decode)
+				return 1;
+			}
+		return 0;
+		}
+
+	if(!strcmp(name,PEM_STRING_PARAMETERS))
+		{
+		int slen;
+		const EVP_PKEY_ASN1_METHOD *ameth;
+		slen = pem_check_suffix(nm, "PARAMETERS"); 
+		if (slen > 0)
+			{
+			ENGINE *e;
+			ameth = EVP_PKEY_asn1_find_str(&e, nm, slen);
+			if (ameth)
+				{
+				int r;
+				if (ameth->param_decode)
+					r = 1;
+				else
+					r = 0;
+				return r;
+				}
+			}
+		return 0;
+		}
+	/* If reading DH parameters handle X9.42 DH format too */
+	if(!strcmp(nm,PEM_STRING_DHXPARAMS) &&
+		!strcmp(name,PEM_STRING_DHPARAMS)) return 1;
+
+	/* Permit older strings */
+
+	if(!strcmp(nm,PEM_STRING_X509_OLD) &&
+		!strcmp(name,PEM_STRING_X509)) return 1;
+
+	if(!strcmp(nm,PEM_STRING_X509_REQ_OLD) &&
+		!strcmp(name,PEM_STRING_X509_REQ)) return 1;
+
+	/* Allow normal certs to be read as trusted certs */
+	if(!strcmp(nm,PEM_STRING_X509) &&
+		!strcmp(name,PEM_STRING_X509_TRUSTED)) return 1;
+
+	if(!strcmp(nm,PEM_STRING_X509_OLD) &&
+		!strcmp(name,PEM_STRING_X509_TRUSTED)) return 1;
+
+	/* Some CAs use PKCS#7 with CERTIFICATE headers */
+	if(!strcmp(nm, PEM_STRING_X509) &&
+		!strcmp(name, PEM_STRING_PKCS7)) return 1;
+
+	if(!strcmp(nm, PEM_STRING_PKCS7_SIGNED) &&
+		!strcmp(name, PEM_STRING_PKCS7)) return 1;
+
+#ifndef OPENSSL_NO_CMS
+	if(!strcmp(nm, PEM_STRING_X509) &&
+		!strcmp(name, PEM_STRING_CMS)) return 1;
+	/* Allow CMS to be read from PKCS#7 headers */
+	if(!strcmp(nm, PEM_STRING_PKCS7) &&
+		!strcmp(name, PEM_STRING_CMS)) return 1;
+#endif
+
+	return 0;
+}
+
+int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm, const char *name, BIO *bp,
+	     pem_password_cb *cb, void *u)
+	{
+	EVP_CIPHER_INFO cipher;
+	char *nm=NULL,*header=NULL;
+	unsigned char *data=NULL;
+	long len;
+	int ret = 0;
+
+	for (;;)
+		{
+		if (!PEM_read_bio(bp,&nm,&header,&data,&len)) {
+			if(ERR_GET_REASON(ERR_peek_error()) ==
+				PEM_R_NO_START_LINE)
+				ERR_add_error_data(2, "Expecting: ", name);
+			return 0;
+		}
+		if(check_pem(nm, name)) break;
+		OPENSSL_free(nm);
+		OPENSSL_free(header);
+		OPENSSL_free(data);
+		}
+	if (!PEM_get_EVP_CIPHER_INFO(header,&cipher)) goto err;
+	if (!PEM_do_header(&cipher,data,&len,cb,u)) goto err;
+
+	*pdata = data;
+	*plen = len;
+
+	if (pnm)
+		*pnm = nm;
+
+	ret = 1;
+
+err:
+	if (!ret || !pnm) OPENSSL_free(nm);
+	OPENSSL_free(header);
+	if (!ret) OPENSSL_free(data);
+	return ret;
+	}
+
+#ifndef OPENSSL_NO_FP_API
+int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp,
+		   void *x, const EVP_CIPHER *enc, unsigned char *kstr,
+		   int klen, pem_password_cb *callback, void *u)
+        {
+        BIO *b;
+        int ret;
+
+        if ((b=BIO_new(BIO_s_file())) == NULL)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_ASN1_write, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_ASN1_write_bio(i2d,name,b,x,enc,kstr,klen,callback,u);
+        BIO_free(b);
+        return(ret);
+        }
+#endif
+
+int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
+		       void *x, const EVP_CIPHER *enc, unsigned char *kstr,
+		       int klen, pem_password_cb *callback, void *u)
+	{
+	EVP_CIPHER_CTX ctx;
+	int dsize=0,i,j,ret=0;
+	unsigned char *p,*data=NULL;
+	const char *objstr=NULL;
+	char buf[PEM_BUFSIZE];
+	unsigned char key[EVP_MAX_KEY_LENGTH];
+	unsigned char iv[EVP_MAX_IV_LENGTH];
+	
+	if (enc != NULL)
+		{
+		objstr=OBJ_nid2sn(EVP_CIPHER_nid(enc));
+		if (objstr == NULL)
+			{
+			OPENSSL_PUT_ERROR(PEM, PEM_ASN1_write_bio, PEM_R_UNSUPPORTED_CIPHER);
+			goto err;
+			}
+		}
+
+	if ((dsize=i2d(x,NULL)) < 0)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_ASN1_write_bio, ERR_R_ASN1_LIB);
+		dsize=0;
+		goto err;
+		}
+	/* dzise + 8 bytes are needed */
+	/* actually it needs the cipher block size extra... */
+	data=(unsigned char *)OPENSSL_malloc((unsigned int)dsize+20);
+	if (data == NULL)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_ASN1_write_bio, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	p=data;
+	i=i2d(x,&p);
+
+	if (enc != NULL)
+		{
+		const unsigned iv_len = EVP_CIPHER_iv_length(enc);
+
+		if (kstr == NULL)
+			{
+			klen = 0;
+			if (callback)
+				klen=(*callback)(buf,PEM_BUFSIZE,1,u);
+			if (klen <= 0)
+				{
+				OPENSSL_PUT_ERROR(PEM, PEM_ASN1_write_bio, PEM_R_READ_KEY);
+				goto err;
+				}
+#ifdef CHARSET_EBCDIC
+			/* Convert the pass phrase from EBCDIC */
+			ebcdic2ascii(buf, buf, klen);
+#endif
+			kstr=(unsigned char *)buf;
+			}
+		assert(iv_len <= (int)sizeof(iv));
+		if (RAND_pseudo_bytes(iv,iv_len) < 0) /* Generate a salt */
+			goto err;
+		/* The 'iv' is used as the iv and as a salt.  It is
+		 * NOT taken from the BytesToKey function */
+		if (!EVP_BytesToKey(enc,EVP_md5(),iv,kstr,klen,1,key,NULL))
+			goto err;
+
+		if (kstr == (unsigned char *)buf) OPENSSL_cleanse(buf,PEM_BUFSIZE);
+
+		assert(strlen(objstr)+23+2*iv_len+13 <= sizeof buf);
+
+		buf[0]='\0';
+		PEM_proc_type(buf,PEM_TYPE_ENCRYPTED);
+		PEM_dek_info(buf,objstr,iv_len,(char *)iv);
+		/* k=strlen(buf); */
+
+		EVP_CIPHER_CTX_init(&ctx);
+		ret = 1;
+		if (!EVP_EncryptInit_ex(&ctx,enc,NULL,key,iv)
+			|| !EVP_EncryptUpdate(&ctx,data,&j,data,i)
+			|| !EVP_EncryptFinal_ex(&ctx,&(data[j]),&i))
+			ret = 0;
+		EVP_CIPHER_CTX_cleanup(&ctx);
+		if (ret == 0)
+			goto err;
+		i+=j;
+		}
+	else
+		{
+		ret=1;
+		buf[0]='\0';
+		}
+	i=PEM_write_bio(bp,name,buf,data,i);
+	if (i <= 0) ret=0;
+err:
+	OPENSSL_cleanse(key,sizeof(key));
+	OPENSSL_cleanse(iv,sizeof(iv));
+	OPENSSL_cleanse((char *)&ctx,sizeof(ctx));
+	OPENSSL_cleanse(buf,PEM_BUFSIZE);
+	if (data != NULL)
+		{
+		OPENSSL_cleanse(data,(unsigned int)dsize);
+		OPENSSL_free(data);
+		}
+	return(ret);
+	}
+
+int PEM_do_header(EVP_CIPHER_INFO *cipher, unsigned char *data, long *plen,
+	     pem_password_cb *callback,void *u)
+	{
+	int i,j,o,klen;
+	long len;
+	EVP_CIPHER_CTX ctx;
+	unsigned char key[EVP_MAX_KEY_LENGTH];
+	char buf[PEM_BUFSIZE];
+
+	len= *plen;
+
+	if (cipher->cipher == NULL) return(1);
+
+	klen = 0;
+	if (callback)
+		klen=callback(buf,PEM_BUFSIZE,0,u);
+	if (klen <= 0)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_do_header, PEM_R_BAD_PASSWORD_READ);
+		return(0);
+		}
+#ifdef CHARSET_EBCDIC
+	/* Convert the pass phrase from EBCDIC */
+	ebcdic2ascii(buf, buf, klen);
+#endif
+
+	if (!EVP_BytesToKey(cipher->cipher,EVP_md5(),&(cipher->iv[0]),
+		(unsigned char *)buf,klen,1,key,NULL))
+		return 0;
+
+	j=(int)len;
+	EVP_CIPHER_CTX_init(&ctx);
+	o = EVP_DecryptInit_ex(&ctx,cipher->cipher,NULL, key,&(cipher->iv[0]));
+	if (o)
+		o = EVP_DecryptUpdate(&ctx,data,&i,data,j);
+	if (o)
+		o = EVP_DecryptFinal_ex(&ctx,&(data[i]),&j);
+	EVP_CIPHER_CTX_cleanup(&ctx);
+	OPENSSL_cleanse((char *)buf,sizeof(buf));
+	OPENSSL_cleanse((char *)key,sizeof(key));
+	if (!o)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_do_header, PEM_R_BAD_DECRYPT);
+		return(0);
+		}
+	j+=i;
+	*plen=j;
+	return(1);
+	}
+
+static const EVP_CIPHER* cipher_by_name(const char *name) {
+  if (strcmp(name, "DES-CBC") == 0) {
+    return EVP_des_cbc();
+  } else if (strcmp(name, "AES-128-CBC") == 0) {
+    return EVP_aes_128_cbc();
+  } else if (strcmp(name,  "AES-256-CBC") == 0) {
+    return EVP_aes_256_cbc();
+  } else {
+    return NULL;
+  }
+}
+
+int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher)
+	{
+	const EVP_CIPHER *enc=NULL;
+	char *p,c;
+	char **header_pp = &header;
+
+	cipher->cipher=NULL;
+	if ((header == NULL) || (*header == '\0') || (*header == '\n'))
+		return(1);
+	if (strncmp(header,"Proc-Type: ",11) != 0)
+		{ OPENSSL_PUT_ERROR(PEM, PEM_get_EVP_CIPHER_INFO, PEM_R_NOT_PROC_TYPE); return(0); }
+	header+=11;
+	if (*header != '4') return(0); header++;
+	if (*header != ',') return(0); header++;
+	if (strncmp(header,"ENCRYPTED",9) != 0)
+		{ OPENSSL_PUT_ERROR(PEM, PEM_get_EVP_CIPHER_INFO, PEM_R_NOT_ENCRYPTED); return(0); }
+	for (; (*header != '\n') && (*header != '\0'); header++)
+		;
+	if (*header == '\0')
+		{ OPENSSL_PUT_ERROR(PEM, PEM_get_EVP_CIPHER_INFO, PEM_R_SHORT_HEADER); return(0); }
+	header++;
+	if (strncmp(header,"DEK-Info: ",10) != 0)
+		{ OPENSSL_PUT_ERROR(PEM, PEM_get_EVP_CIPHER_INFO, PEM_R_NOT_DEK_INFO); return(0); }
+	header+=10;
+
+	p=header;
+	for (;;)
+		{
+		c= *header;
+#ifndef CHARSET_EBCDIC
+		if (!(	((c >= 'A') && (c <= 'Z')) || (c == '-') ||
+			((c >= '0') && (c <= '9'))))
+			break;
+#else
+		if (!(	isupper(c) || (c == '-') ||
+			isdigit(c)))
+			break;
+#endif
+		header++;
+		}
+	*header='\0';
+	cipher->cipher=enc=cipher_by_name(p);
+	*header=c;
+	header++;
+
+	if (enc == NULL)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_get_EVP_CIPHER_INFO, PEM_R_UNSUPPORTED_ENCRYPTION);
+		return(0);
+		}
+	if (!load_iv(header_pp,&(cipher->iv[0]),EVP_CIPHER_iv_length(enc)))
+		return(0);
+
+	return(1);
+	}
+
+static int load_iv(char **fromp, unsigned char *to, int num)
+	{
+	int v,i;
+	char *from;
+
+	from= *fromp;
+	for (i=0; i<num; i++) to[i]=0;
+	num*=2;
+	for (i=0; i<num; i++)
+		{
+		if ((*from >= '0') && (*from <= '9'))
+			v= *from-'0';
+		else if ((*from >= 'A') && (*from <= 'F'))
+			v= *from-'A'+10;
+		else if ((*from >= 'a') && (*from <= 'f'))
+			v= *from-'a'+10;
+		else
+			{
+			OPENSSL_PUT_ERROR(PEM, load_iv, PEM_R_BAD_IV_CHARS);
+			return(0);
+			}
+		from++;
+		to[i/2]|=v<<(long)((!(i&1))*4);
+		}
+
+	*fromp=from;
+	return(1);
+	}
+
+#ifndef OPENSSL_NO_FP_API
+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)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_write, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_write_bio(b, name, header, data,len);
+        BIO_free(b);
+        return(ret);
+        }
+#endif
+
+int PEM_write_bio(BIO *bp, const char *name, const char *header,
+		  const unsigned char *data, long len)
+	{
+	int nlen,n,i,j,outl;
+	unsigned char *buf = NULL;
+	EVP_ENCODE_CTX ctx;
+	int reason=ERR_R_BUF_LIB;
+	
+	EVP_EncodeInit(&ctx);
+	nlen=strlen(name);
+
+	if (	(BIO_write(bp,"-----BEGIN ",11) != 11) ||
+		(BIO_write(bp,name,nlen) != nlen) ||
+		(BIO_write(bp,"-----\n",6) != 6))
+		goto err;
+		
+	i=strlen(header);
+	if (i > 0)
+		{
+		if (	(BIO_write(bp,header,i) != i) ||
+			(BIO_write(bp,"\n",1) != 1))
+			goto err;
+		}
+
+	buf = OPENSSL_malloc(PEM_BUFSIZE*8);
+	if (buf == NULL)
+		{
+		reason=ERR_R_MALLOC_FAILURE;
+		goto err;
+		}
+
+	i=j=0;
+	while (len > 0)
+		{
+		n=(int)((len>(PEM_BUFSIZE*5))?(PEM_BUFSIZE*5):len);
+		EVP_EncodeUpdate(&ctx,buf,&outl,&(data[j]),n);
+		if ((outl) && (BIO_write(bp,(char *)buf,outl) != outl))
+			goto err;
+		i+=outl;
+		len-=n;
+		j+=n;
+		}
+	EVP_EncodeFinal(&ctx,buf,&outl);
+	if ((outl > 0) && (BIO_write(bp,(char *)buf,outl) != outl)) goto err;
+	OPENSSL_cleanse(buf, PEM_BUFSIZE*8);
+	OPENSSL_free(buf);
+	buf = NULL;
+	if (	(BIO_write(bp,"-----END ",9) != 9) ||
+		(BIO_write(bp,name,nlen) != nlen) ||
+		(BIO_write(bp,"-----\n",6) != 6))
+		goto err;
+	return(i+outl);
+err:
+	if (buf) {
+		OPENSSL_cleanse(buf, PEM_BUFSIZE*8);
+		OPENSSL_free(buf);
+	}
+	OPENSSL_PUT_ERROR(PEM, PEM_write_bio, reason);
+	return(0);
+	}
+
+#ifndef OPENSSL_NO_FP_API
+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)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_read, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_read_bio(b, name, header, data,len);
+        BIO_free(b);
+        return(ret);
+        }
+#endif
+
+int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
+	     long *len)
+	{
+	EVP_ENCODE_CTX ctx;
+	int end=0,i,k,bl=0,hl=0,nohead=0;
+	char buf[256];
+	BUF_MEM *nameB;
+	BUF_MEM *headerB;
+	BUF_MEM *dataB,*tmpB;
+	
+	nameB=BUF_MEM_new();
+	headerB=BUF_MEM_new();
+	dataB=BUF_MEM_new();
+	if ((nameB == NULL) || (headerB == NULL) || (dataB == NULL))
+		{
+		BUF_MEM_free(nameB);
+		BUF_MEM_free(headerB);
+		BUF_MEM_free(dataB);
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio, ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+
+	buf[254]='\0';
+	for (;;)
+		{
+		i=BIO_gets(bp,buf,254);
+
+		if (i <= 0)
+			{
+			OPENSSL_PUT_ERROR(PEM, PEM_read_bio, PEM_R_NO_START_LINE);
+			goto err;
+			}
+
+		while ((i >= 0) && (buf[i] <= ' ')) i--;
+		buf[++i]='\n'; buf[++i]='\0';
+
+		if (strncmp(buf,"-----BEGIN ",11) == 0)
+			{
+			i=strlen(&(buf[11]));
+
+			if (strncmp(&(buf[11+i-6]),"-----\n",6) != 0)
+				continue;
+			if (!BUF_MEM_grow(nameB,i+9))
+				{
+				OPENSSL_PUT_ERROR(PEM, PEM_read_bio, ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+			memcpy(nameB->data,&(buf[11]),i-6);
+			nameB->data[i-6]='\0';
+			break;
+			}
+		}
+	hl=0;
+	if (!BUF_MEM_grow(headerB,256))
+		{ OPENSSL_PUT_ERROR(PEM, PEM_read_bio, ERR_R_MALLOC_FAILURE); goto err; }
+	headerB->data[0]='\0';
+	for (;;)
+		{
+		i=BIO_gets(bp,buf,254);
+		if (i <= 0) break;
+
+		while ((i >= 0) && (buf[i] <= ' ')) i--;
+		buf[++i]='\n'; buf[++i]='\0';
+
+		if (buf[0] == '\n') break;
+		if (!BUF_MEM_grow(headerB,hl+i+9))
+			{ OPENSSL_PUT_ERROR(PEM, PEM_read_bio, ERR_R_MALLOC_FAILURE); goto err; }
+		if (strncmp(buf,"-----END ",9) == 0)
+			{
+			nohead=1;
+			break;
+			}
+		memcpy(&(headerB->data[hl]),buf,i);
+		headerB->data[hl+i]='\0';
+		hl+=i;
+		}
+
+	bl=0;
+	if (!BUF_MEM_grow(dataB,1024))
+		{ OPENSSL_PUT_ERROR(PEM, PEM_read_bio, ERR_R_MALLOC_FAILURE); goto err; }
+	dataB->data[0]='\0';
+	if (!nohead)
+		{
+		for (;;)
+			{
+			i=BIO_gets(bp,buf,254);
+			if (i <= 0) break;
+
+			while ((i >= 0) && (buf[i] <= ' ')) i--;
+			buf[++i]='\n'; buf[++i]='\0';
+
+			if (i != 65) end=1;
+			if (strncmp(buf,"-----END ",9) == 0)
+				break;
+			if (i > 65) break;
+			if (!BUF_MEM_grow_clean(dataB,i+bl+9))
+				{
+				OPENSSL_PUT_ERROR(PEM, PEM_read_bio, ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+			memcpy(&(dataB->data[bl]),buf,i);
+			dataB->data[bl+i]='\0';
+			bl+=i;
+			if (end)
+				{
+				buf[0]='\0';
+				i=BIO_gets(bp,buf,254);
+				if (i <= 0) break;
+
+				while ((i >= 0) && (buf[i] <= ' ')) i--;
+				buf[++i]='\n'; buf[++i]='\0';
+
+				break;
+				}
+			}
+		}
+	else
+		{
+		tmpB=headerB;
+		headerB=dataB;
+		dataB=tmpB;
+		bl=hl;
+		}
+	i=strlen(nameB->data);
+	if (	(strncmp(buf,"-----END ",9) != 0) ||
+		(strncmp(nameB->data,&(buf[9]),i) != 0) ||
+		(strncmp(&(buf[9+i]),"-----\n",6) != 0))
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio, PEM_R_BAD_END_LINE);
+		goto err;
+		}
+
+	EVP_DecodeInit(&ctx);
+	i=EVP_DecodeUpdate(&ctx,
+		(unsigned char *)dataB->data,&bl,
+		(unsigned char *)dataB->data,bl);
+	if (i < 0)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio, PEM_R_BAD_BASE64_DECODE);
+		goto err;
+		}
+	i=EVP_DecodeFinal(&ctx,(unsigned char *)&(dataB->data[bl]),&k);
+	if (i < 0)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio, PEM_R_BAD_BASE64_DECODE);
+		goto err;
+		}
+	bl+=k;
+
+	if (bl == 0) goto err;
+	*name=nameB->data;
+	*header=headerB->data;
+	*data=(unsigned char *)dataB->data;
+	*len=bl;
+	OPENSSL_free(nameB);
+	OPENSSL_free(headerB);
+	OPENSSL_free(dataB);
+	return(1);
+err:
+	BUF_MEM_free(nameB);
+	BUF_MEM_free(headerB);
+	BUF_MEM_free(dataB);
+	return(0);
+	}
+
+/* Check pem string and return prefix length.
+ * If for example the pem_str == "RSA PRIVATE KEY" and suffix = "PRIVATE KEY"
+ * the return value is 3 for the string "RSA".
+ */
+
+int pem_check_suffix(const char *pem_str, const char *suffix)
+	{
+	int pem_len = strlen(pem_str);
+	int suffix_len = strlen(suffix);
+	const char *p;
+	if (suffix_len + 1 >= pem_len)
+		return 0;
+	p = pem_str + pem_len - suffix_len;
+	if (strcmp(p, suffix))
+		return 0;
+	p--;
+	if (*p != ' ')
+		return 0;
+	return p - pem_str;
+	}
+
diff --git a/crypto/pem/pem_oth.c b/crypto/pem/pem_oth.c
new file mode 100644
index 0000000..20d12b6
--- /dev/null
+++ b/crypto/pem/pem_oth.c
@@ -0,0 +1,89 @@
+/* crypto/pem/pem_oth.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/pem.h>
+
+#include <stdio.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+
+/* Handle 'other' PEMs: not private keys */
+
+void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x,
+			pem_password_cb *cb, void *u)
+	{
+	const unsigned char *p=NULL;
+	unsigned char *data=NULL;
+	long len;
+	char *ret=NULL;
+
+	if (!PEM_bytes_read_bio(&data, &len, NULL, name, bp, cb, u))
+		return NULL;
+	p = data;
+	ret=d2i(x,&p,len);
+	if (ret == NULL)
+		OPENSSL_PUT_ERROR(PEM, PEM_ASN1_read_bio, ERR_R_ASN1_LIB);
+	OPENSSL_free(data);
+	return ret;
+	}
diff --git a/crypto/pem/pem_pk8.c b/crypto/pem/pem_pk8.c
new file mode 100644
index 0000000..18cfb92
--- /dev/null
+++ b/crypto/pem/pem_pk8.c
@@ -0,0 +1,243 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/pem.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/pkcs8.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+
+static int do_pk8pkey(BIO *bp, EVP_PKEY *x, int isder,
+				int nid, const EVP_CIPHER *enc,
+				char *kstr, int klen,
+				pem_password_cb *cb, void *u);
+static int do_pk8pkey_fp(FILE *bp, EVP_PKEY *x, int isder,
+				int nid, const EVP_CIPHER *enc,
+				char *kstr, int klen,
+				pem_password_cb *cb, void *u);
+
+/* These functions write a private key in PKCS#8 format: it is a "drop in"
+ * replacement for PEM_write_bio_PrivateKey() and friends. As usual if 'enc'
+ * is NULL then it uses the unencrypted private key form. The 'nid' versions
+ * uses PKCS#5 v1.5 PBE algorithms whereas the others use PKCS#5 v2.0.
+ */
+
+int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey(bp, x, 0, nid, NULL, kstr, klen, cb, u);
+}
+
+int PEM_write_bio_PKCS8PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey(bp, x, 0, -1, enc, kstr, klen, cb, u);
+}
+
+int i2d_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey(bp, x, 1, -1, enc, kstr, klen, cb, u);
+}
+
+int i2d_PKCS8PrivateKey_nid_bio(BIO *bp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey(bp, x, 1, nid, NULL, kstr, klen, cb, u);
+}
+
+static int do_pk8pkey(BIO *bp, EVP_PKEY *x, int isder, int nid, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	X509_SIG *p8;
+	PKCS8_PRIV_KEY_INFO *p8inf;
+	char buf[PEM_BUFSIZE];
+	int ret;
+	if(!(p8inf = EVP_PKEY2PKCS8(x))) {
+		OPENSSL_PUT_ERROR(PEM, do_pk8pkey, PEM_R_ERROR_CONVERTING_PRIVATE_KEY);
+		return 0;
+	}
+	if(enc || (nid != -1)) {
+		if(!kstr) {
+			klen = 0;
+			if (cb)
+				klen = cb(buf, PEM_BUFSIZE, 1, u);
+			if(klen <= 0) {
+				OPENSSL_PUT_ERROR(PEM, do_pk8pkey, PEM_R_READ_KEY);
+				PKCS8_PRIV_KEY_INFO_free(p8inf);
+				return 0;
+			}
+				
+			kstr = buf;
+		}
+		p8 = PKCS8_encrypt(nid, enc, kstr, klen, NULL, 0, 0, p8inf);
+		if(kstr == buf) OPENSSL_cleanse(buf, klen);
+		PKCS8_PRIV_KEY_INFO_free(p8inf);
+		if(isder) ret = i2d_PKCS8_bio(bp, p8);
+		else ret = PEM_write_bio_PKCS8(bp, p8);
+		X509_SIG_free(p8);
+		return ret;
+	} else {
+		if(isder) ret = i2d_PKCS8_PRIV_KEY_INFO_bio(bp, p8inf);
+		else ret = PEM_write_bio_PKCS8_PRIV_KEY_INFO(bp, p8inf);
+		PKCS8_PRIV_KEY_INFO_free(p8inf);
+		return ret;
+	}
+}
+
+EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u)
+{
+	PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+	X509_SIG *p8 = NULL;
+	int klen;
+	EVP_PKEY *ret;
+	char psbuf[PEM_BUFSIZE];
+	p8 = d2i_PKCS8_bio(bp, NULL);
+	if(!p8) return NULL;
+
+	klen = 0;
+	if (cb)
+		klen=cb(psbuf,PEM_BUFSIZE,0,u);
+	if (klen <= 0) {
+		OPENSSL_PUT_ERROR(PEM, d2i_PKCS8PrivateKey_bio, PEM_R_BAD_PASSWORD_READ);
+		X509_SIG_free(p8);
+		return NULL;	
+	}
+	p8inf = PKCS8_decrypt(p8, psbuf, klen);
+	X509_SIG_free(p8);
+	if(!p8inf) return NULL;
+	ret = EVP_PKCS82PKEY(p8inf);
+	PKCS8_PRIV_KEY_INFO_free(p8inf);
+	if(!ret) return NULL;
+	if(x) {
+		if(*x) EVP_PKEY_free(*x);
+		*x = ret;
+	}
+	return ret;
+}
+
+#ifndef OPENSSL_NO_FP_API
+
+int i2d_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey_fp(fp, x, 1, -1, enc, kstr, klen, cb, u);
+}
+
+int i2d_PKCS8PrivateKey_nid_fp(FILE *fp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey_fp(fp, x, 1, nid, NULL, kstr, klen, cb, u);
+}
+
+int PEM_write_PKCS8PrivateKey_nid(FILE *fp, EVP_PKEY *x, int nid,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey_fp(fp, x, 0, nid, NULL, kstr, klen, cb, u);
+}
+
+int PEM_write_PKCS8PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
+			      char *kstr, int klen, pem_password_cb *cb, void *u)
+{
+	return do_pk8pkey_fp(fp, x, 0, -1, enc, kstr, klen, cb, u);
+}
+
+static int do_pk8pkey_fp(FILE *fp, EVP_PKEY *x, int isder, int nid, const EVP_CIPHER *enc,
+				  char *kstr, int klen,
+				  pem_password_cb *cb, void *u)
+{
+	BIO *bp;
+	int ret;
+	if(!(bp = BIO_new_fp(fp, BIO_NOCLOSE))) {
+		OPENSSL_PUT_ERROR(PEM, do_pk8pkey_fp, ERR_R_BUF_LIB);
+                return(0);
+	}
+	ret = do_pk8pkey(bp, x, isder, nid, enc, kstr, klen, cb, u);
+	BIO_free(bp);
+	return ret;
+}
+
+EVP_PKEY *d2i_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, void *u)
+{
+	BIO *bp;
+	EVP_PKEY *ret;
+	if(!(bp = BIO_new_fp(fp, BIO_NOCLOSE))) {
+		OPENSSL_PUT_ERROR(PEM, d2i_PKCS8PrivateKey_fp, ERR_R_BUF_LIB);
+                return NULL;
+	}
+	ret = d2i_PKCS8PrivateKey_bio(bp, x, cb, u);
+	BIO_free(bp);
+	return ret;
+}
+
+#endif
+
+IMPLEMENT_PEM_rw(PKCS8, X509_SIG, PEM_STRING_PKCS8, X509_SIG)
+IMPLEMENT_PEM_rw(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO, PEM_STRING_PKCS8INF,
+							 PKCS8_PRIV_KEY_INFO)
diff --git a/crypto/pem/pem_pkey.c b/crypto/pem/pem_pkey.c
new file mode 100644
index 0000000..7900f4b
--- /dev/null
+++ b/crypto/pem/pem_pkey.c
@@ -0,0 +1,313 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/pem.h>
+
+#include <stdio.h>
+
+#include <openssl/buf.h>
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/pkcs8.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include "../evp/internal.h"
+
+
+int pem_check_suffix(const char *pem_str, const char *suffix);
+
+EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u)
+	{
+	char *nm=NULL;
+	const unsigned char *p=NULL;
+	unsigned char *data=NULL;
+	long len;
+	int slen;
+	EVP_PKEY *ret=NULL;
+
+	if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_EVP_PKEY, bp, cb, u))
+		return NULL;
+	p = data;
+
+	if (strcmp(nm,PEM_STRING_PKCS8INF) == 0) {
+		PKCS8_PRIV_KEY_INFO *p8inf;
+		p8inf=d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, len);
+		if(!p8inf) goto p8err;
+		ret = EVP_PKCS82PKEY(p8inf);
+		if(x) {
+			if(*x) EVP_PKEY_free((EVP_PKEY *)*x);
+			*x = ret;
+		}
+		PKCS8_PRIV_KEY_INFO_free(p8inf);
+	} else if (strcmp(nm,PEM_STRING_PKCS8) == 0) {
+		PKCS8_PRIV_KEY_INFO *p8inf;
+		X509_SIG *p8;
+		int klen;
+		char psbuf[PEM_BUFSIZE];
+		p8 = d2i_X509_SIG(NULL, &p, len);
+		if(!p8) goto p8err;
+
+		klen = 0;
+		if (cb) klen=cb(psbuf,PEM_BUFSIZE,0,u);
+		if (klen <= 0) {
+			OPENSSL_PUT_ERROR(PEM, PEM_read_bio_PrivateKey, PEM_R_BAD_PASSWORD_READ);
+			X509_SIG_free(p8);
+			goto err;
+		}
+		p8inf = PKCS8_decrypt(p8, psbuf, klen);
+		X509_SIG_free(p8);
+		if(!p8inf) goto p8err;
+		ret = EVP_PKCS82PKEY(p8inf);
+		if(x) {
+			if(*x) EVP_PKEY_free((EVP_PKEY *)*x);
+			*x = ret;
+		}
+		PKCS8_PRIV_KEY_INFO_free(p8inf);
+	} else if ((slen = pem_check_suffix(nm, "PRIVATE KEY")) > 0)
+		{
+		const EVP_PKEY_ASN1_METHOD *ameth;
+		ameth = EVP_PKEY_asn1_find_str(NULL, nm, slen);
+		if (!ameth || !ameth->old_priv_decode)
+			goto p8err;
+		ret=d2i_PrivateKey(ameth->pkey_id,x,&p,len);
+		}
+p8err:
+	if (ret == NULL)
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio_PrivateKey, ERR_R_ASN1_LIB);
+
+err:
+	OPENSSL_free(nm);
+	OPENSSL_cleanse(data, len);
+	OPENSSL_free(data);
+	return(ret);
+	}
+
+int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                                               unsigned char *kstr, int klen,
+                                               pem_password_cb *cb, void *u)
+	{
+	char pem_str[80];
+	if (!x->ameth || x->ameth->priv_encode)
+		return PEM_write_bio_PKCS8PrivateKey(bp, x, enc,
+							(char *)kstr, klen,
+							cb, u);
+
+	BIO_snprintf(pem_str, 80, "%s PRIVATE KEY", x->ameth->pem_str);
+	return PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey,
+				pem_str,bp,x,enc,kstr,klen,cb,u);
+	}
+
+static const int public_key_type_from_str(const char *name, size_t len) {
+  if (len == 3 && memcmp(name, "RSA", 3) == 0) {
+    return EVP_PKEY_RSA;
+  } else if (len == 2 && memcmp(name, "DH", 2) == 0) {
+    return EVP_PKEY_DH;
+  } else if (len == 2 && memcmp(name, "EC", 2) == 0) {
+    return EVP_PKEY_EC;
+  }
+  return NID_undef;
+}
+
+static int set_pkey_type_from_str(EVP_PKEY *pkey, const char *name, size_t len) {
+  int nid = public_key_type_from_str(name, len);
+  if (nid == NID_undef) {
+    return 0;
+  }
+  return EVP_PKEY_set_type(pkey, nid);
+}
+
+EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x)
+	{
+	char *nm=NULL;
+	const unsigned char *p=NULL;
+	unsigned char *data=NULL;
+	long len;
+	int slen;
+	EVP_PKEY *ret=NULL;
+
+	if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_PARAMETERS,
+								bp, 0, NULL))
+		return NULL;
+	p = data;
+
+	if ((slen = pem_check_suffix(nm, "PARAMETERS")) > 0)
+		{
+		ret = EVP_PKEY_new();
+		if (!ret)
+			goto err;
+		if (!set_pkey_type_from_str(ret, nm, slen)
+			|| !ret->ameth->param_decode
+			|| !ret->ameth->param_decode(ret, &p, len))
+			{
+			EVP_PKEY_free(ret);
+			ret = NULL;
+			goto err;
+			}
+		if(x)
+			{
+			if(*x) EVP_PKEY_free((EVP_PKEY *)*x);
+			*x = ret;
+			}
+		}
+err:
+	if (ret == NULL)
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio_Parameters, ERR_R_ASN1_LIB);
+	OPENSSL_free(nm);
+	OPENSSL_free(data);
+	return(ret);
+	}
+
+int PEM_write_bio_Parameters(BIO *bp, EVP_PKEY *x)
+	{
+	char pem_str[80];
+	if (!x->ameth || !x->ameth->param_encode)
+		return 0;
+
+	BIO_snprintf(pem_str, 80, "%s PARAMETERS", x->ameth->pem_str);
+	return PEM_ASN1_write_bio(
+		(i2d_of_void *)x->ameth->param_encode,
+				pem_str,bp,x,NULL,NULL,0,0,NULL);
+	}
+
+#ifndef OPENSSL_NO_FP_API
+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)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_read_PrivateKey, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_read_bio_PrivateKey(b,x,cb,u);
+        BIO_free(b);
+        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)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_write_PrivateKey, ERR_R_BUF_LIB);
+                return 0;
+		}
+        ret=PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
+        BIO_free(b);
+        return ret;
+	}
+
+#endif
+
+#ifndef OPENSSL_NO_DH
+
+/* Transparently read in PKCS#3 or X9.42 DH parameters */
+
+DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u)
+	{
+	char *nm=NULL;
+	const unsigned char *p=NULL;
+	unsigned char *data=NULL;
+	long len;
+	DH *ret=NULL;
+
+	if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_DHPARAMS,
+								bp, cb, u))
+		return NULL;
+	p = data;
+
+        /* TODO(fork): remove? */
+	/*if (!strcmp(nm, PEM_STRING_DHXPARAMS))
+		ret = d2i_DHxparams(x, &p, len);
+	else */
+		ret = d2i_DHparams(x, &p, len);
+
+	if (ret == NULL)
+		OPENSSL_PUT_ERROR(PEM, PEM_read_bio_DHparams, ERR_R_ASN1_LIB);
+	OPENSSL_free(nm);
+	OPENSSL_free(data);
+	return ret;
+	}
+
+#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)
+		{
+		OPENSSL_PUT_ERROR(PEM, PEM_read_DHparams, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=PEM_read_bio_DHparams(b,x,cb,u);
+        BIO_free(b);
+        return(ret);
+	}
+#endif
+
+#endif
diff --git a/crypto/pem/pem_x509.c b/crypto/pem/pem_x509.c
new file mode 100644
index 0000000..f463047
--- /dev/null
+++ b/crypto/pem/pem_x509.c
@@ -0,0 +1,65 @@
+/* pem_x509.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2001.
+ */
+/* ====================================================================
+ * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+
+IMPLEMENT_PEM_rw(X509, X509, PEM_STRING_X509, X509)
diff --git a/crypto/pem/pem_xaux.c b/crypto/pem/pem_xaux.c
new file mode 100644
index 0000000..8dabd41
--- /dev/null
+++ b/crypto/pem/pem_xaux.c
@@ -0,0 +1,66 @@
+/* pem_xaux.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2001.
+ */
+/* ====================================================================
+ * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+
+IMPLEMENT_PEM_rw(X509_AUX, X509, PEM_STRING_X509_TRUSTED, X509_AUX)
+IMPLEMENT_PEM_rw(X509_CERT_PAIR, X509_CERT_PAIR, PEM_STRING_X509_PAIR, X509_CERT_PAIR)
diff --git a/crypto/perlasm/cbc.pl b/crypto/perlasm/cbc.pl
new file mode 100644
index 0000000..24561e7
--- /dev/null
+++ b/crypto/perlasm/cbc.pl
@@ -0,0 +1,349 @@
+#!/usr/local/bin/perl
+
+# void des_ncbc_encrypt(input, output, length, schedule, ivec, enc)
+# des_cblock (*input);
+# des_cblock (*output);
+# long length;
+# des_key_schedule schedule;
+# des_cblock (*ivec);
+# int enc;
+#
+# calls 
+# des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
+#
+
+#&cbc("des_ncbc_encrypt","des_encrypt",0);
+#&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",
+#	1,4,5,3,5,-1);
+#&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",
+#	0,4,5,3,5,-1);
+#&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",
+#	0,6,7,3,4,5);
+#
+# When doing a cipher that needs bigendian order,
+# for encrypt, the iv is kept in bigendian form,
+# while for decrypt, it is kept in little endian.
+sub cbc
+	{
+	local($name,$enc_func,$dec_func,$swap,$iv_off,$enc_off,$p1,$p2,$p3)=@_;
+	# name is the function name
+	# enc_func and dec_func and the functions to call for encrypt/decrypt
+	# swap is true if byte order needs to be reversed
+	# iv_off is parameter number for the iv 
+	# enc_off is parameter number for the encrypt/decrypt flag
+	# p1,p2,p3 are the offsets for parameters to be passed to the
+	# underlying calls.
+
+	&function_begin_B($name,"");
+	&comment("");
+
+	$in="esi";
+	$out="edi";
+	$count="ebp";
+
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	$data_off=4;
+	$data_off+=4 if ($p1 > 0);
+	$data_off+=4 if ($p2 > 0);
+	$data_off+=4 if ($p3 > 0);
+
+	&mov($count,	&wparam(2));	# length
+
+	&comment("getting iv ptr from parameter $iv_off");
+	&mov("ebx",	&wparam($iv_off));	# Get iv ptr
+
+	&mov($in,	&DWP(0,"ebx","",0));#	iv[0]
+	&mov($out,	&DWP(4,"ebx","",0));#	iv[1]
+
+	&push($out);
+	&push($in);
+	&push($out);	# used in decrypt for iv[1]
+	&push($in);	# used in decrypt for iv[0]
+
+	&mov("ebx",	"esp");		# This is the address of tin[2]
+
+	&mov($in,	&wparam(0));	# in
+	&mov($out,	&wparam(1));	# out
+
+	# We have loaded them all, how lets push things
+	&comment("getting encrypt flag from parameter $enc_off");
+	&mov("ecx",	&wparam($enc_off));	# Get enc flag
+	if ($p3 > 0)
+		{
+		&comment("get and push parameter $p3");
+		if ($enc_off != $p3)
+			{ &mov("eax",	&wparam($p3)); &push("eax"); }
+		else	{ &push("ecx"); }
+		}
+	if ($p2 > 0)
+		{
+		&comment("get and push parameter $p2");
+		if ($enc_off != $p2)
+			{ &mov("eax",	&wparam($p2)); &push("eax"); }
+		else	{ &push("ecx"); }
+		}
+	if ($p1 > 0)
+		{
+		&comment("get and push parameter $p1");
+		if ($enc_off != $p1)
+			{ &mov("eax",	&wparam($p1)); &push("eax"); }
+		else	{ &push("ecx"); }
+		}
+	&push("ebx");		# push data/iv
+
+	&cmp("ecx",0);
+	&jz(&label("decrypt"));
+
+	&and($count,0xfffffff8);
+	&mov("eax",	&DWP($data_off,"esp","",0));	# load iv[0]
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));	# load iv[1]
+
+	&jz(&label("encrypt_finish"));
+
+	#############################################################
+
+	&set_label("encrypt_loop");
+	# encrypt start 
+	# "eax" and "ebx" hold iv (or the last cipher text)
+
+	&mov("ecx",	&DWP(0,$in,"",0));	# load first 4 bytes
+	&mov("edx",	&DWP(4,$in,"",0));	# second 4 bytes
+
+	&xor("eax",	"ecx");
+	&xor("ebx",	"edx");
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put in array for call
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($enc_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP(0,$out,"",0),"eax");
+	&mov(&DWP(4,$out,"",0),"ebx");
+
+	# eax and ebx are the next iv.
+
+	&add($in,	8);
+	&add($out,	8);
+
+	&sub($count,	8);
+	&jnz(&label("encrypt_loop"));
+
+###################################################################3
+	&set_label("encrypt_finish");
+	&mov($count,	&wparam(2));	# length
+	&and($count,	7);
+	&jz(&label("finish"));
+	&call(&label("PIC_point"));
+&set_label("PIC_point");
+	&blindpop("edx");
+	&lea("ecx",&DWP(&label("cbc_enc_jmp_table")."-".&label("PIC_point"),"edx"));
+	&mov($count,&DWP(0,"ecx",$count,4));
+	&add($count,"edx");
+	&xor("ecx","ecx");
+	&xor("edx","edx");
+	#&mov($count,&DWP(&label("cbc_enc_jmp_table"),"",$count,4));
+	&jmp_ptr($count);
+
+&set_label("ej7");
+	&movb(&HB("edx"),	&BP(6,$in,"",0));
+	&shl("edx",8);
+&set_label("ej6");
+	&movb(&HB("edx"),	&BP(5,$in,"",0));
+&set_label("ej5");
+	&movb(&LB("edx"),	&BP(4,$in,"",0));
+&set_label("ej4");
+	&mov("ecx",		&DWP(0,$in,"",0));
+	&jmp(&label("ejend"));
+&set_label("ej3");
+	&movb(&HB("ecx"),	&BP(2,$in,"",0));
+	&shl("ecx",8);
+&set_label("ej2");
+	&movb(&HB("ecx"),	&BP(1,$in,"",0));
+&set_label("ej1");
+	&movb(&LB("ecx"),	&BP(0,$in,"",0));
+&set_label("ejend");
+
+	&xor("eax",	"ecx");
+	&xor("ebx",	"edx");
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put in array for call
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($enc_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP(0,$out,"",0),"eax");
+	&mov(&DWP(4,$out,"",0),"ebx");
+
+	&jmp(&label("finish"));
+
+	#############################################################
+	#############################################################
+	&set_label("decrypt",1);
+	# decrypt start 
+	&and($count,0xfffffff8);
+	# The next 2 instructions are only for if the jz is taken
+	&mov("eax",	&DWP($data_off+8,"esp","",0));	# get iv[0]
+	&mov("ebx",	&DWP($data_off+12,"esp","",0));	# get iv[1]
+	&jz(&label("decrypt_finish"));
+
+	&set_label("decrypt_loop");
+	&mov("eax",	&DWP(0,$in,"",0));	# load first 4 bytes
+	&mov("ebx",	&DWP(4,$in,"",0));	# second 4 bytes
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put back
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($dec_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));	# get return
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));	#
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov("ecx",	&DWP($data_off+8,"esp","",0));	# get iv[0]
+	&mov("edx",	&DWP($data_off+12,"esp","",0));	# get iv[1]
+
+	&xor("ecx",	"eax");
+	&xor("edx",	"ebx");
+
+	&mov("eax",	&DWP(0,$in,"",0));	# get old cipher text,
+	&mov("ebx",	&DWP(4,$in,"",0));	# next iv actually
+
+	&mov(&DWP(0,$out,"",0),"ecx");
+	&mov(&DWP(4,$out,"",0),"edx");
+
+	&mov(&DWP($data_off+8,"esp","",0),	"eax");	# save iv
+	&mov(&DWP($data_off+12,"esp","",0),	"ebx");	#
+
+	&add($in,	8);
+	&add($out,	8);
+
+	&sub($count,	8);
+	&jnz(&label("decrypt_loop"));
+############################ ENDIT #######################3
+	&set_label("decrypt_finish");
+	&mov($count,	&wparam(2));	# length
+	&and($count,	7);
+	&jz(&label("finish"));
+
+	&mov("eax",	&DWP(0,$in,"",0));	# load first 4 bytes
+	&mov("ebx",	&DWP(4,$in,"",0));	# second 4 bytes
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put back
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($dec_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));	# get return
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));	#
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov("ecx",	&DWP($data_off+8,"esp","",0));	# get iv[0]
+	&mov("edx",	&DWP($data_off+12,"esp","",0));	# get iv[1]
+
+	&xor("ecx",	"eax");
+	&xor("edx",	"ebx");
+
+	# this is for when we exit
+	&mov("eax",	&DWP(0,$in,"",0));	# get old cipher text,
+	&mov("ebx",	&DWP(4,$in,"",0));	# next iv actually
+
+&set_label("dj7");
+	&rotr("edx",	16);
+	&movb(&BP(6,$out,"",0),	&LB("edx"));
+	&shr("edx",16);
+&set_label("dj6");
+	&movb(&BP(5,$out,"",0),	&HB("edx"));
+&set_label("dj5");
+	&movb(&BP(4,$out,"",0),	&LB("edx"));
+&set_label("dj4");
+	&mov(&DWP(0,$out,"",0),	"ecx");
+	&jmp(&label("djend"));
+&set_label("dj3");
+	&rotr("ecx",	16);
+	&movb(&BP(2,$out,"",0),	&LB("ecx"));
+	&shl("ecx",16);
+&set_label("dj2");
+	&movb(&BP(1,$in,"",0),	&HB("ecx"));
+&set_label("dj1");
+	&movb(&BP(0,$in,"",0),	&LB("ecx"));
+&set_label("djend");
+
+	# final iv is still in eax:ebx
+	&jmp(&label("finish"));
+
+
+############################ FINISH #######################3
+	&set_label("finish",1);
+	&mov("ecx",	&wparam($iv_off));	# Get iv ptr
+
+	#################################################
+	$total=16+4;
+	$total+=4 if ($p1 > 0);
+	$total+=4 if ($p2 > 0);
+	$total+=4 if ($p3 > 0);
+	&add("esp",$total);
+
+	&mov(&DWP(0,"ecx","",0),	"eax");	# save iv
+	&mov(&DWP(4,"ecx","",0),	"ebx");	# save iv
+
+	&function_end_A($name);
+
+	&align(64);
+	&set_label("cbc_enc_jmp_table");
+	&data_word("0");
+	&data_word(&label("ej1")."-".&label("PIC_point"));
+	&data_word(&label("ej2")."-".&label("PIC_point"));
+	&data_word(&label("ej3")."-".&label("PIC_point"));
+	&data_word(&label("ej4")."-".&label("PIC_point"));
+	&data_word(&label("ej5")."-".&label("PIC_point"));
+	&data_word(&label("ej6")."-".&label("PIC_point"));
+	&data_word(&label("ej7")."-".&label("PIC_point"));
+	# not used
+	#&set_label("cbc_dec_jmp_table",1);
+	#&data_word("0");
+	#&data_word(&label("dj1")."-".&label("PIC_point"));
+	#&data_word(&label("dj2")."-".&label("PIC_point"));
+	#&data_word(&label("dj3")."-".&label("PIC_point"));
+	#&data_word(&label("dj4")."-".&label("PIC_point"));
+	#&data_word(&label("dj5")."-".&label("PIC_point"));
+	#&data_word(&label("dj6")."-".&label("PIC_point"));
+	#&data_word(&label("dj7")."-".&label("PIC_point"));
+	&align(64);
+
+	&function_end_B($name);
+	
+	}
+
+1;
diff --git a/crypto/perlasm/ppc-xlate.pl b/crypto/perlasm/ppc-xlate.pl
new file mode 100755
index 0000000..3ed7bd9
--- /dev/null
+++ b/crypto/perlasm/ppc-xlate.pl
@@ -0,0 +1,161 @@
+#!/usr/bin/env perl
+
+# PowerPC assembler distiller by <appro>.
+
+my $flavour = shift;
+my $output = shift;
+open STDOUT,">$output" || die "can't open $output: $!";
+
+my %GLOBALS;
+my $dotinlocallabels=($flavour=~/linux/)?1:0;
+
+################################################################
+# directives which need special treatment on different platforms
+################################################################
+my $globl = sub {
+    my $junk = shift;
+    my $name = shift;
+    my $global = \$GLOBALS{$name};
+    my $ret;
+
+    $name =~ s|^[\.\_]||;
+ 
+    SWITCH: for ($flavour) {
+	/aix/		&& do { $name = ".$name";
+				last;
+			      };
+	/osx/		&& do { $name = "_$name";
+				last;
+			      };
+	/linux.*32/	&& do {	$ret .= ".globl	$name\n";
+				$ret .= ".type	$name,\@function";
+				last;
+			      };
+	/linux.*64/	&& do {	$ret .= ".globl	$name\n";
+				$ret .= ".type	$name,\@function\n";
+				$ret .= ".section	\".opd\",\"aw\"\n";
+				$ret .= ".align	3\n";
+				$ret .= "$name:\n";
+				$ret .= ".quad	.$name,.TOC.\@tocbase,0\n";
+				$ret .= ".previous\n";
+
+				$name = ".$name";
+				last;
+			      };
+    }
+
+    $ret = ".globl	$name" if (!$ret);
+    $$global = $name;
+    $ret;
+};
+my $text = sub {
+    ($flavour =~ /aix/) ? ".csect" : ".text";
+};
+my $machine = sub {
+    my $junk = shift;
+    my $arch = shift;
+    if ($flavour =~ /osx/)
+    {	$arch =~ s/\"//g;
+	$arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any");
+    }
+    ".machine	$arch";
+};
+my $size = sub {
+    if ($flavour =~ /linux/)
+    {	shift;
+	my $name = shift; $name =~ s|^[\.\_]||;
+	my $ret  = ".size	$name,.-".($flavour=~/64/?".":"").$name;
+	$ret .= "\n.size	.$name,.-.$name" if ($flavour=~/64/);
+	$ret;
+    }
+    else
+    {	"";	}
+};
+my $asciz = sub {
+    shift;
+    my $line = join(",",@_);
+    if ($line =~ /^"(.*)"$/)
+    {	".byte	" . join(",",unpack("C*",$1),0) . "\n.align	2";	}
+    else
+    {	"";	}
+};
+
+################################################################
+# simplified mnemonics not handled by at least one assembler
+################################################################
+my $cmplw = sub {
+    my $f = shift;
+    my $cr = 0; $cr = shift if ($#_>1);
+    # Some out-of-date 32-bit GNU assembler just can't handle cmplw...
+    ($flavour =~ /linux.*32/) ?
+	"	.long	".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 :
+	"	cmplw	".join(',',$cr,@_);
+};
+my $bdnz = sub {
+    my $f = shift;
+    my $bo = $f=~/[\+\-]/ ? 16+9 : 16;	# optional "to be taken" hint
+    "	bc	$bo,0,".shift;
+} if ($flavour!~/linux/);
+my $bltlr = sub {
+    my $f = shift;
+    my $bo = $f=~/\-/ ? 12+2 : 12;	# optional "not to be taken" hint
+    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
+	"	.long	".sprintf "0x%x",19<<26|$bo<<21|16<<1 :
+	"	bclr	$bo,0";
+};
+my $bnelr = sub {
+    my $f = shift;
+    my $bo = $f=~/\-/ ? 4+2 : 4;	# optional "not to be taken" hint
+    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
+	"	.long	".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 :
+	"	bclr	$bo,2";
+};
+my $beqlr = sub {
+    my $f = shift;
+    my $bo = $f=~/-/ ? 12+2 : 12;	# optional "not to be taken" hint
+    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
+	"	.long	".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 :
+	"	bclr	$bo,2";
+};
+# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two
+# arguments is 64, with "operand out of range" error.
+my $extrdi = sub {
+    my ($f,$ra,$rs,$n,$b) = @_;
+    $b = ($b+$n)&63; $n = 64-$n;
+    "	rldicl	$ra,$rs,$b,$n";
+};
+
+while($line=<>) {
+
+    $line =~ s|[#!;].*$||;	# get rid of asm-style comments...
+    $line =~ s|/\*.*\*/||;	# ... and C-style comments...
+    $line =~ s|^\s+||;		# ... and skip white spaces in beginning...
+    $line =~ s|\s+$||;		# ... and at the end
+
+    {
+	$line =~ s|\b\.L(\w+)|L$1|g;	# common denominator for Locallabel
+	$line =~ s|\bL(\w+)|\.L$1|g	if ($dotinlocallabels);
+    }
+
+    {
+	$line =~ s|(^[\.\w]+)\:\s*||;
+	my $label = $1;
+	printf "%s:",($GLOBALS{$label} or $label) if ($label);
+    }
+
+    {
+	$line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||;
+	my $c = $1; $c = "\t" if ($c eq "");
+	my $mnemonic = $2;
+	my $f = $3;
+	my $opcode = eval("\$$mnemonic");
+	$line =~ s|\bc?[rf]([0-9]+)\b|$1|g if ($c ne "." and $flavour !~ /osx/);
+	if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(',',$line)); }
+	elsif ($mnemonic)           { $line = $c.$mnemonic.$f."\t".$line; }
+    }
+
+    print $line if ($line);
+    print "\n";
+}
+
+close STDOUT;
diff --git a/crypto/perlasm/readme b/crypto/perlasm/readme
new file mode 100644
index 0000000..f02bbee
--- /dev/null
+++ b/crypto/perlasm/readme
@@ -0,0 +1,124 @@
+The perl scripts in this directory are my 'hack' to generate
+multiple different assembler formats via the one origional script.
+
+The way to use this library is to start with adding the path to this directory
+and then include it.
+
+push(@INC,"perlasm","../../perlasm");
+require "x86asm.pl";
+
+The first thing we do is setup the file and type of assember
+
+&asm_init($ARGV[0],$0);
+
+The first argument is the 'type'.  Currently
+'cpp', 'sol', 'a.out', 'elf' or 'win32'.
+Argument 2 is the file name.
+
+The reciprocal function is
+&asm_finish() which should be called at the end.
+
+There are 2 main 'packages'. x86ms.pl, which is the microsoft assembler,
+and x86unix.pl which is the unix (gas) version.
+
+Functions of interest are:
+&external_label("des_SPtrans");	declare and external variable
+&LB(reg);			Low byte for a register
+&HB(reg);			High byte for a register
+&BP(off,base,index,scale)	Byte pointer addressing
+&DWP(off,base,index,scale)	Word pointer addressing
+&stack_push(num)		Basically a 'sub esp, num*4' with extra
+&stack_pop(num)			inverse of stack_push
+&function_begin(name,extra)	Start a function with pushing of
+				edi, esi, ebx and ebp.  extra is extra win32
+				external info that may be required.
+&function_begin_B(name,extra)	Same as norma function_begin but no pushing.
+&function_end(name)		Call at end of function.
+&function_end_A(name)		Standard pop and ret, for use inside functions
+&function_end_B(name)		Call at end but with poping or 'ret'.
+&swtmp(num)			Address on stack temp word.
+&wparam(num)			Parameter number num, that was push
+				in C convention.  This all works over pushes
+				and pops.
+&comment("hello there")		Put in a comment.
+&label("loop")			Refer to a label, normally a jmp target.
+&set_label("loop")		Set a label at this point.
+&data_word(word)		Put in a word of data.
+
+So how does this all hold together?  Given
+
+int calc(int len, int *data)
+	{
+	int i,j=0;
+
+	for (i=0; i<len; i++)
+		{
+		j+=other(data[i]);
+		}
+	}
+
+So a very simple version of this function could be coded as
+
+	push(@INC,"perlasm","../../perlasm");
+	require "x86asm.pl";
+	
+	&asm_init($ARGV[0],"cacl.pl");
+
+	&external_label("other");
+
+	$tmp1=	"eax";
+	$j=	"edi";
+	$data=	"esi";
+	$i=	"ebp";
+
+	&comment("a simple function");
+	&function_begin("calc");
+	&mov(	$data,		&wparam(1)); # data
+	&xor(	$j,		$j);
+	&xor(	$i,		$i);
+
+	&set_label("loop");
+	&cmp(	$i,		&wparam(0));
+	&jge(	&label("end"));
+
+	&mov(	$tmp1,		&DWP(0,$data,$i,4));
+	&push(	$tmp1);
+	&call(	"other");
+	&add(	$j,		"eax");
+	&pop(	$tmp1);
+	&inc(	$i);
+	&jmp(	&label("loop"));
+
+	&set_label("end");
+	&mov(	"eax",		$j);
+
+	&function_end("calc");
+
+	&asm_finish();
+
+The above example is very very unoptimised but gives an idea of how
+things work.
+
+There is also a cbc mode function generator in cbc.pl
+
+&cbc(	$name,
+	$encrypt_function_name,
+	$decrypt_function_name,
+	$true_if_byte_swap_needed,
+	$parameter_number_for_iv,
+	$parameter_number_for_encrypt_flag,
+	$first_parameter_to_pass,
+	$second_parameter_to_pass,
+	$third_parameter_to_pass);
+
+So for example, given
+void BF_encrypt(BF_LONG *data,BF_KEY *key);
+void BF_decrypt(BF_LONG *data,BF_KEY *key);
+void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length,
+        BF_KEY *ks, unsigned char *iv, int enc);
+
+&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",1,4,5,3,-1,-1);
+
+&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",0,4,5,3,5,-1);
+&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",0,6,7,3,4,5);
+
diff --git a/crypto/perlasm/sparcv9_modes.pl b/crypto/perlasm/sparcv9_modes.pl
new file mode 100644
index 0000000..6b47bb1
--- /dev/null
+++ b/crypto/perlasm/sparcv9_modes.pl
@@ -0,0 +1,1680 @@
+#!/usr/bin/env perl
+
+# Specific modes implementations for SPARC Architecture 2011. There
+# is T4 dependency though, an ASI value that is not specified in the
+# Architecture Manual. But as SPARC universe is rather monocultural,
+# we imply that processor capable of executing crypto instructions
+# can handle the ASI in question as well. This means that we ought to
+# keep eyes open when new processors emerge...
+#
+# As for above mentioned ASI. It's so called "block initializing
+# store" which cancels "read" in "read-update-write" on cache lines.
+# This is "cooperative" optimization, as it reduces overall pressure
+# on memory interface. Benefits can't be observed/quantified with
+# usual benchmarks, on the contrary you can notice that single-thread
+# performance for parallelizable modes is ~1.5% worse for largest
+# block sizes [though few percent better for not so long ones]. All
+# this based on suggestions from David Miller.
+
+sub asm_init {		# to be called with @ARGV as argument
+    for (@_)		{ $::abibits=64 if (/\-m64/ || /\-xarch\=v9/); }
+    if ($::abibits==64)	{ $::bias=2047; $::frame=192; $::size_t_cc="%xcc"; }
+    else		{ $::bias=0;    $::frame=112; $::size_t_cc="%icc"; }
+}
+
+# unified interface
+my ($inp,$out,$len,$key,$ivec)=map("%i$_",(0..5));
+# local variables
+my ($ileft,$iright,$ooff,$omask,$ivoff,$blk_init)=map("%l$_",(0..7));
+
+sub alg_cbc_encrypt_implement {
+my ($alg,$bits) = @_;
+
+$::code.=<<___;
+.globl	${alg}${bits}_t4_cbc_encrypt
+.align	32
+${alg}${bits}_t4_cbc_encrypt:
+	save		%sp, -$::frame, %sp
+	sub		$inp, $out, $blk_init	! $inp!=$out
+___
+$::code.=<<___ if (!$::evp);
+	andcc		$ivec, 7, $ivoff
+	alignaddr	$ivec, %g0, $ivec
+
+	ldd		[$ivec + 0], %f0	! load ivec
+	bz,pt		%icc, 1f
+	ldd		[$ivec + 8], %f2
+	ldd		[$ivec + 16], %f4
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+1:
+___
+$::code.=<<___ if ($::evp);
+	ld		[$ivec + 0], %f0
+	ld		[$ivec + 4], %f1
+	ld		[$ivec + 8], %f2
+	ld		[$ivec + 12], %f3
+___
+$::code.=<<___;
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	call		_${alg}${bits}_load_enckey
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		64, $iright
+	mov		0xff, $omask
+	sub		$iright, $ileft, $iright
+	and		$out, 7, $ooff
+	cmp		$len, 127
+	movrnz		$ooff, 0, $blk_init		! if (	$out&7 ||
+	movleu		$::size_t_cc, 0, $blk_init	!	$len<128 ||
+	brnz,pn		$blk_init, .L${bits}cbc_enc_blk	!	$inp==$out)
+	srl		$omask, $ooff, $omask
+
+	alignaddrl	$out, %g0, $out
+	srlx		$len, 4, $len
+	prefetch	[$out], 22
+
+.L${bits}_cbc_enc_loop:
+	ldx		[$inp + 0], %o0
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 8], %o1
+
+	ldx		[$inp + 16], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+4:
+	xor		%g4, %o0, %o0		! ^= rk[0]
+	xor		%g5, %o1, %o1
+	movxtod		%o0, %f12
+	movxtod		%o1, %f14
+
+	fxor		%f12, %f0, %f0		! ^= ivec
+	fxor		%f14, %f2, %f2
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 16+63], 20
+	call		_${alg}${bits}_encrypt_1x
+	add		$inp, 16, $inp
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 1, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	brnz,pt		$len, .L${bits}_cbc_enc_loop
+	add		$out, 16, $out
+___
+$::code.=<<___ if ($::evp);
+	st		%f0, [$ivec + 0]
+	st		%f1, [$ivec + 4]
+	st		%f2, [$ivec + 8]
+	st		%f3, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, 3f
+	nop
+
+	std		%f0, [$ivec + 0]	! write out ivec
+	std		%f2, [$ivec + 8]
+___
+$::code.=<<___;
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f4		! handle unaligned output
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+
+	stda		%f4, [$out + $omask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $omask, $omask
+	stda		%f8, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_cbc_enc_loop+4
+	orn		%g0, $omask, $omask
+___
+$::code.=<<___ if ($::evp);
+	st		%f0, [$ivec + 0]
+	st		%f1, [$ivec + 4]
+	st		%f2, [$ivec + 8]
+	st		%f3, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, 3f
+	nop
+
+	std		%f0, [$ivec + 0]	! write out ivec
+	std		%f2, [$ivec + 8]
+	ret
+	restore
+
+.align	16
+3:	alignaddrl	$ivec, $ivoff, %g0	! handle unaligned ivec
+	mov		0xff, $omask
+	srl		$omask, $ivoff, $omask
+	faligndata	%f0, %f0, %f4
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+	stda		%f4, [$ivec + $omask]0xc0
+	std		%f6, [$ivec + 8]
+	add		$ivec, 16, $ivec
+	orn		%g0, $omask, $omask
+	stda		%f8, [$ivec + $omask]0xc0
+___
+$::code.=<<___;
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}cbc_enc_blk:
+	add	$out, $len, $blk_init
+	and	$blk_init, 63, $blk_init	! tail
+	sub	$len, $blk_init, $len
+	add	$blk_init, 15, $blk_init	! round up to 16n
+	srlx	$len, 4, $len
+	srl	$blk_init, 4, $blk_init
+
+.L${bits}_cbc_enc_blk_loop:
+	ldx		[$inp + 0], %o0
+	brz,pt		$ileft, 5f
+	ldx		[$inp + 8], %o1
+
+	ldx		[$inp + 16], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+5:
+	xor		%g4, %o0, %o0		! ^= rk[0]
+	xor		%g5, %o1, %o1
+	movxtod		%o0, %f12
+	movxtod		%o1, %f14
+
+	fxor		%f12, %f0, %f0		! ^= ivec
+	fxor		%f14, %f2, %f2
+	prefetch	[$inp + 16+63], 20
+	call		_${alg}${bits}_encrypt_1x
+	add		$inp, 16, $inp
+	sub		$len, 1, $len
+		
+	stda		%f0, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f2, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	brnz,pt		$len, .L${bits}_cbc_enc_blk_loop
+	add		$out, 8, $out
+
+	membar		#StoreLoad|#StoreStore
+	brnz,pt		$blk_init, .L${bits}_cbc_enc_loop
+	mov		$blk_init, $len
+___
+$::code.=<<___ if ($::evp);
+	st		%f0, [$ivec + 0]
+	st		%f1, [$ivec + 4]
+	st		%f2, [$ivec + 8]
+	st		%f3, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, 3b
+	nop
+
+	std		%f0, [$ivec + 0]	! write out ivec
+	std		%f2, [$ivec + 8]
+___
+$::code.=<<___;
+	ret
+	restore
+.type	${alg}${bits}_t4_cbc_encrypt,#function
+.size	${alg}${bits}_t4_cbc_encrypt,.-${alg}${bits}_t4_cbc_encrypt
+___
+}
+
+sub alg_cbc_decrypt_implement {
+my ($alg,$bits) = @_;
+
+$::code.=<<___;
+.globl	${alg}${bits}_t4_cbc_decrypt
+.align	32
+${alg}${bits}_t4_cbc_decrypt:
+	save		%sp, -$::frame, %sp
+	sub		$inp, $out, $blk_init	! $inp!=$out
+___
+$::code.=<<___ if (!$::evp);
+	andcc		$ivec, 7, $ivoff
+	alignaddr	$ivec, %g0, $ivec
+
+	ldd		[$ivec + 0], %f12	! load ivec
+	bz,pt		%icc, 1f
+	ldd		[$ivec + 8], %f14
+	ldd		[$ivec + 16], %f0
+	faligndata	%f12, %f14, %f12
+	faligndata	%f14, %f0, %f14
+1:
+___
+$::code.=<<___ if ($::evp);
+	ld		[$ivec + 0], %f12	! load ivec
+	ld		[$ivec + 4], %f13
+	ld		[$ivec + 8], %f14
+	ld		[$ivec + 12], %f15
+___
+$::code.=<<___;
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	call		_${alg}${bits}_load_deckey
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		64, $iright
+	mov		0xff, $omask
+	sub		$iright, $ileft, $iright
+	and		$out, 7, $ooff
+	cmp		$len, 255
+	movrnz		$ooff, 0, $blk_init		! if (	$out&7 ||
+	movleu		$::size_t_cc, 0, $blk_init	!	$len<256 ||
+	brnz,pn		$blk_init, .L${bits}cbc_dec_blk	!	$inp==$out)
+	srl		$omask, $ooff, $omask
+
+	andcc		$len, 16, %g0		! is number of blocks even?
+	srlx		$len, 4, $len
+	alignaddrl	$out, %g0, $out
+	bz		%icc, .L${bits}_cbc_dec_loop2x
+	prefetch	[$out], 22
+.L${bits}_cbc_dec_loop:
+	ldx		[$inp + 0], %o0
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 8], %o1
+
+	ldx		[$inp + 16], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+4:
+	xor		%g4, %o0, %o2		! ^= rk[0]
+	xor		%g5, %o1, %o3
+	movxtod		%o2, %f0
+	movxtod		%o3, %f2
+
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 16+63], 20
+	call		_${alg}${bits}_decrypt_1x
+	add		$inp, 16, $inp
+
+	fxor		%f12, %f0, %f0		! ^= ivec
+	fxor		%f14, %f2, %f2
+	movxtod		%o0, %f12
+	movxtod		%o1, %f14
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 1, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	brnz,pt		$len, .L${bits}_cbc_dec_loop2x
+	add		$out, 16, $out
+___
+$::code.=<<___ if ($::evp);
+	st		%f12, [$ivec + 0]
+	st		%f13, [$ivec + 4]
+	st		%f14, [$ivec + 8]
+	st		%f15, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, .L${bits}_cbc_dec_unaligned_ivec
+	nop
+
+	std		%f12, [$ivec + 0]	! write out ivec
+	std		%f14, [$ivec + 8]
+___
+$::code.=<<___;
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f4		! handle unaligned output
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+
+	stda		%f4, [$out + $omask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $omask, $omask
+	stda		%f8, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_cbc_dec_loop2x+4
+	orn		%g0, $omask, $omask
+___
+$::code.=<<___ if ($::evp);
+	st		%f12, [$ivec + 0]
+	st		%f13, [$ivec + 4]
+	st		%f14, [$ivec + 8]
+	st		%f15, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, .L${bits}_cbc_dec_unaligned_ivec
+	nop
+
+	std		%f12, [$ivec + 0]	! write out ivec
+	std		%f14, [$ivec + 8]
+___
+$::code.=<<___;
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}_cbc_dec_loop2x:
+	ldx		[$inp + 0], %o0
+	ldx		[$inp + 8], %o1
+	ldx		[$inp + 16], %o2
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 24], %o3
+
+	ldx		[$inp + 32], %o4
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	or		%g1, %o0, %o0
+	sllx		%o1, $ileft, %o1
+	srlx		%o2, $iright, %g1
+	or		%g1, %o1, %o1
+	sllx		%o2, $ileft, %o2
+	srlx		%o3, $iright, %g1
+	or		%g1, %o2, %o2
+	sllx		%o3, $ileft, %o3
+	srlx		%o4, $iright, %o4
+	or		%o4, %o3, %o3
+4:
+	xor		%g4, %o0, %o4		! ^= rk[0]
+	xor		%g5, %o1, %o5
+	movxtod		%o4, %f0
+	movxtod		%o5, %f2
+	xor		%g4, %o2, %o4
+	xor		%g5, %o3, %o5
+	movxtod		%o4, %f4
+	movxtod		%o5, %f6
+
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 32+63], 20
+	call		_${alg}${bits}_decrypt_2x
+	add		$inp, 32, $inp
+
+	movxtod		%o0, %f8
+	movxtod		%o1, %f10
+	fxor		%f12, %f0, %f0		! ^= ivec
+	fxor		%f14, %f2, %f2
+	movxtod		%o2, %f12
+	movxtod		%o3, %f14
+	fxor		%f8, %f4, %f4
+	fxor		%f10, %f6, %f6
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 2, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	std		%f4, [$out + 16]
+	std		%f6, [$out + 24]
+	brnz,pt		$len, .L${bits}_cbc_dec_loop2x
+	add		$out, 32, $out
+___
+$::code.=<<___ if ($::evp);
+	st		%f12, [$ivec + 0]
+	st		%f13, [$ivec + 4]
+	st		%f14, [$ivec + 8]
+	st		%f15, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, .L${bits}_cbc_dec_unaligned_ivec
+	nop
+
+	std		%f12, [$ivec + 0]	! write out ivec
+	std		%f14, [$ivec + 8]
+___
+$::code.=<<___;
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f8		! handle unaligned output
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+	faligndata	%f4, %f6, %f4
+	faligndata	%f6, %f6, %f6
+	stda		%f8, [$out + $omask]0xc0	! partial store
+	std		%f0, [$out + 8]
+	std		%f2, [$out + 16]
+	std		%f4, [$out + 24]
+	add		$out, 32, $out
+	orn		%g0, $omask, $omask
+	stda		%f6, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_cbc_dec_loop2x+4
+	orn		%g0, $omask, $omask
+___
+$::code.=<<___ if ($::evp);
+	st		%f12, [$ivec + 0]
+	st		%f13, [$ivec + 4]
+	st		%f14, [$ivec + 8]
+	st		%f15, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, .L${bits}_cbc_dec_unaligned_ivec
+	nop
+
+	std		%f12, [$ivec + 0]	! write out ivec
+	std		%f14, [$ivec + 8]
+	ret
+	restore
+
+.align	16
+.L${bits}_cbc_dec_unaligned_ivec:
+	alignaddrl	$ivec, $ivoff, %g0	! handle unaligned ivec
+	mov		0xff, $omask
+	srl		$omask, $ivoff, $omask
+	faligndata	%f12, %f12, %f0
+	faligndata	%f12, %f14, %f2
+	faligndata	%f14, %f14, %f4
+	stda		%f0, [$ivec + $omask]0xc0
+	std		%f2, [$ivec + 8]
+	add		$ivec, 16, $ivec
+	orn		%g0, $omask, $omask
+	stda		%f4, [$ivec + $omask]0xc0
+___
+$::code.=<<___;
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}cbc_dec_blk:
+	add	$out, $len, $blk_init
+	and	$blk_init, 63, $blk_init	! tail
+	sub	$len, $blk_init, $len
+	add	$blk_init, 15, $blk_init	! round up to 16n
+	srlx	$len, 4, $len
+	srl	$blk_init, 4, $blk_init
+	sub	$len, 1, $len
+	add	$blk_init, 1, $blk_init
+
+.L${bits}_cbc_dec_blk_loop2x:
+	ldx		[$inp + 0], %o0
+	ldx		[$inp + 8], %o1
+	ldx		[$inp + 16], %o2
+	brz,pt		$ileft, 5f
+	ldx		[$inp + 24], %o3
+
+	ldx		[$inp + 32], %o4
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	or		%g1, %o0, %o0
+	sllx		%o1, $ileft, %o1
+	srlx		%o2, $iright, %g1
+	or		%g1, %o1, %o1
+	sllx		%o2, $ileft, %o2
+	srlx		%o3, $iright, %g1
+	or		%g1, %o2, %o2
+	sllx		%o3, $ileft, %o3
+	srlx		%o4, $iright, %o4
+	or		%o4, %o3, %o3
+5:
+	xor		%g4, %o0, %o4		! ^= rk[0]
+	xor		%g5, %o1, %o5
+	movxtod		%o4, %f0
+	movxtod		%o5, %f2
+	xor		%g4, %o2, %o4
+	xor		%g5, %o3, %o5
+	movxtod		%o4, %f4
+	movxtod		%o5, %f6
+
+	prefetch	[$inp + 32+63], 20
+	call		_${alg}${bits}_decrypt_2x
+	add		$inp, 32, $inp
+	subcc		$len, 2, $len
+
+	movxtod		%o0, %f8
+	movxtod		%o1, %f10
+	fxor		%f12, %f0, %f0		! ^= ivec
+	fxor		%f14, %f2, %f2
+	movxtod		%o2, %f12
+	movxtod		%o3, %f14
+	fxor		%f8, %f4, %f4
+	fxor		%f10, %f6, %f6
+
+	stda		%f0, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f2, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f4, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f6, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	bgu,pt		$::size_t_cc, .L${bits}_cbc_dec_blk_loop2x
+	add		$out, 8, $out
+
+	add		$blk_init, $len, $len
+	andcc		$len, 1, %g0		! is number of blocks even?
+	membar		#StoreLoad|#StoreStore
+	bnz,pt		%icc, .L${bits}_cbc_dec_loop
+	srl		$len, 0, $len
+	brnz,pn		$len, .L${bits}_cbc_dec_loop2x
+	nop
+___
+$::code.=<<___ if ($::evp);
+	st		%f12, [$ivec + 0]	! write out ivec
+	st		%f13, [$ivec + 4]
+	st		%f14, [$ivec + 8]
+	st		%f15, [$ivec + 12]
+___
+$::code.=<<___ if (!$::evp);
+	brnz,pn		$ivoff, 3b
+	nop
+
+	std		%f12, [$ivec + 0]	! write out ivec
+	std		%f14, [$ivec + 8]
+___
+$::code.=<<___;
+	ret
+	restore
+.type	${alg}${bits}_t4_cbc_decrypt,#function
+.size	${alg}${bits}_t4_cbc_decrypt,.-${alg}${bits}_t4_cbc_decrypt
+___
+}
+
+sub alg_ctr32_implement {
+my ($alg,$bits) = @_;
+
+$::code.=<<___;
+.globl	${alg}${bits}_t4_ctr32_encrypt
+.align	32
+${alg}${bits}_t4_ctr32_encrypt:
+	save		%sp, -$::frame, %sp
+
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	call		_${alg}${bits}_load_enckey
+	sllx		$len, 4, $len
+
+	ld		[$ivec + 0], %l4	! counter
+	ld		[$ivec + 4], %l5
+	ld		[$ivec + 8], %l6
+	ld		[$ivec + 12], %l7
+
+	sllx		%l4, 32, %o5
+	or		%l5, %o5, %o5
+	sllx		%l6, 32, %g1
+	xor		%o5, %g4, %g4		! ^= rk[0]
+	xor		%g1, %g5, %g5
+	movxtod		%g4, %f14		! most significant 64 bits
+
+	sub		$inp, $out, $blk_init	! $inp!=$out
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		64, $iright
+	mov		0xff, $omask
+	sub		$iright, $ileft, $iright
+	and		$out, 7, $ooff
+	cmp		$len, 255
+	movrnz		$ooff, 0, $blk_init		! if (	$out&7 ||
+	movleu		$::size_t_cc, 0, $blk_init	!	$len<256 ||
+	brnz,pn		$blk_init, .L${bits}_ctr32_blk	!	$inp==$out)
+	srl		$omask, $ooff, $omask
+
+	andcc		$len, 16, %g0		! is number of blocks even?
+	alignaddrl	$out, %g0, $out
+	bz		%icc, .L${bits}_ctr32_loop2x
+	srlx		$len, 4, $len
+.L${bits}_ctr32_loop:
+	ldx		[$inp + 0], %o0
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 8], %o1
+
+	ldx		[$inp + 16], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+4:
+	xor		%g5, %l7, %g1		! ^= rk[0]
+	add		%l7, 1, %l7
+	movxtod		%g1, %f2
+	srl		%l7, 0, %l7		! clruw
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 16+63], 20
+___
+$::code.=<<___ if ($alg eq "aes");
+	aes_eround01	%f16, %f14, %f2, %f4
+	aes_eround23	%f18, %f14, %f2, %f2
+___
+$::code.=<<___ if ($alg eq "cmll");
+	camellia_f	%f16, %f2, %f14, %f2
+	camellia_f	%f18, %f14, %f2, %f0
+___
+$::code.=<<___;
+	call		_${alg}${bits}_encrypt_1x+8
+	add		$inp, 16, $inp
+
+	movxtod		%o0, %f10
+	movxtod		%o1, %f12
+	fxor		%f10, %f0, %f0		! ^= inp
+	fxor		%f12, %f2, %f2
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 1, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	brnz,pt		$len, .L${bits}_ctr32_loop2x
+	add		$out, 16, $out
+
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f4		! handle unaligned output
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+	stda		%f4, [$out + $omask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $omask, $omask
+	stda		%f8, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_ctr32_loop2x+4
+	orn		%g0, $omask, $omask
+
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}_ctr32_loop2x:
+	ldx		[$inp + 0], %o0
+	ldx		[$inp + 8], %o1
+	ldx		[$inp + 16], %o2
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 24], %o3
+
+	ldx		[$inp + 32], %o4
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	or		%g1, %o0, %o0
+	sllx		%o1, $ileft, %o1
+	srlx		%o2, $iright, %g1
+	or		%g1, %o1, %o1
+	sllx		%o2, $ileft, %o2
+	srlx		%o3, $iright, %g1
+	or		%g1, %o2, %o2
+	sllx		%o3, $ileft, %o3
+	srlx		%o4, $iright, %o4
+	or		%o4, %o3, %o3
+4:
+	xor		%g5, %l7, %g1		! ^= rk[0]
+	add		%l7, 1, %l7
+	movxtod		%g1, %f2
+	srl		%l7, 0, %l7		! clruw
+	xor		%g5, %l7, %g1
+	add		%l7, 1, %l7
+	movxtod		%g1, %f6
+	srl		%l7, 0, %l7		! clruw
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 32+63], 20
+___
+$::code.=<<___ if ($alg eq "aes");
+	aes_eround01	%f16, %f14, %f2, %f8
+	aes_eround23	%f18, %f14, %f2, %f2
+	aes_eround01	%f16, %f14, %f6, %f10
+	aes_eround23	%f18, %f14, %f6, %f6
+___
+$::code.=<<___ if ($alg eq "cmll");
+	camellia_f	%f16, %f2, %f14, %f2
+	camellia_f	%f16, %f6, %f14, %f6
+	camellia_f	%f18, %f14, %f2, %f0
+	camellia_f	%f18, %f14, %f6, %f4
+___
+$::code.=<<___;
+	call		_${alg}${bits}_encrypt_2x+16
+	add		$inp, 32, $inp
+
+	movxtod		%o0, %f8
+	movxtod		%o1, %f10
+	movxtod		%o2, %f12
+	fxor		%f8, %f0, %f0		! ^= inp
+	movxtod		%o3, %f8
+	fxor		%f10, %f2, %f2
+	fxor		%f12, %f4, %f4
+	fxor		%f8, %f6, %f6
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 2, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	std		%f4, [$out + 16]
+	std		%f6, [$out + 24]
+	brnz,pt		$len, .L${bits}_ctr32_loop2x
+	add		$out, 32, $out
+
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f8		! handle unaligned output
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+	faligndata	%f4, %f6, %f4
+	faligndata	%f6, %f6, %f6
+
+	stda		%f8, [$out + $omask]0xc0	! partial store
+	std		%f0, [$out + 8]
+	std		%f2, [$out + 16]
+	std		%f4, [$out + 24]
+	add		$out, 32, $out
+	orn		%g0, $omask, $omask
+	stda		%f6, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_ctr32_loop2x+4
+	orn		%g0, $omask, $omask
+
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}_ctr32_blk:
+	add	$out, $len, $blk_init
+	and	$blk_init, 63, $blk_init	! tail
+	sub	$len, $blk_init, $len
+	add	$blk_init, 15, $blk_init	! round up to 16n
+	srlx	$len, 4, $len
+	srl	$blk_init, 4, $blk_init
+	sub	$len, 1, $len
+	add	$blk_init, 1, $blk_init
+
+.L${bits}_ctr32_blk_loop2x:
+	ldx		[$inp + 0], %o0
+	ldx		[$inp + 8], %o1
+	ldx		[$inp + 16], %o2
+	brz,pt		$ileft, 5f
+	ldx		[$inp + 24], %o3
+
+	ldx		[$inp + 32], %o4
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	or		%g1, %o0, %o0
+	sllx		%o1, $ileft, %o1
+	srlx		%o2, $iright, %g1
+	or		%g1, %o1, %o1
+	sllx		%o2, $ileft, %o2
+	srlx		%o3, $iright, %g1
+	or		%g1, %o2, %o2
+	sllx		%o3, $ileft, %o3
+	srlx		%o4, $iright, %o4
+	or		%o4, %o3, %o3
+5:
+	xor		%g5, %l7, %g1		! ^= rk[0]
+	add		%l7, 1, %l7
+	movxtod		%g1, %f2
+	srl		%l7, 0, %l7		! clruw
+	xor		%g5, %l7, %g1
+	add		%l7, 1, %l7
+	movxtod		%g1, %f6
+	srl		%l7, 0, %l7		! clruw
+	prefetch	[$inp + 32+63], 20
+___
+$::code.=<<___ if ($alg eq "aes");
+	aes_eround01	%f16, %f14, %f2, %f8
+	aes_eround23	%f18, %f14, %f2, %f2
+	aes_eround01	%f16, %f14, %f6, %f10
+	aes_eround23	%f18, %f14, %f6, %f6
+___
+$::code.=<<___ if ($alg eq "cmll");
+	camellia_f	%f16, %f2, %f14, %f2
+	camellia_f	%f16, %f6, %f14, %f6
+	camellia_f	%f18, %f14, %f2, %f0
+	camellia_f	%f18, %f14, %f6, %f4
+___
+$::code.=<<___;
+	call		_${alg}${bits}_encrypt_2x+16
+	add		$inp, 32, $inp
+	subcc		$len, 2, $len
+
+	movxtod		%o0, %f8
+	movxtod		%o1, %f10
+	movxtod		%o2, %f12
+	fxor		%f8, %f0, %f0		! ^= inp
+	movxtod		%o3, %f8
+	fxor		%f10, %f2, %f2
+	fxor		%f12, %f4, %f4
+	fxor		%f8, %f6, %f6
+
+	stda		%f0, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f2, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f4, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f6, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	bgu,pt		$::size_t_cc, .L${bits}_ctr32_blk_loop2x
+	add		$out, 8, $out
+
+	add		$blk_init, $len, $len
+	andcc		$len, 1, %g0		! is number of blocks even?
+	membar		#StoreLoad|#StoreStore
+	bnz,pt		%icc, .L${bits}_ctr32_loop
+	srl		$len, 0, $len
+	brnz,pn		$len, .L${bits}_ctr32_loop2x
+	nop
+
+	ret
+	restore
+.type	${alg}${bits}_t4_ctr32_encrypt,#function
+.size	${alg}${bits}_t4_ctr32_encrypt,.-${alg}${bits}_t4_ctr32_encrypt
+___
+}
+
+sub alg_xts_implement {
+my ($alg,$bits,$dir) = @_;
+my ($inp,$out,$len,$key1,$key2,$ivec)=map("%i$_",(0..5));
+my $rem=$ivec;
+
+$::code.=<<___;
+.globl	${alg}${bits}_t4_xts_${dir}crypt
+.align	32
+${alg}${bits}_t4_xts_${dir}crypt:
+	save		%sp, -$::frame-16, %sp
+
+	mov		$ivec, %o0
+	add		%fp, $::bias-16, %o1
+	call		${alg}_t4_encrypt
+	mov		$key2, %o2
+
+	add		%fp, $::bias-16, %l7
+	ldxa		[%l7]0x88, %g2
+	add		%fp, $::bias-8, %l7
+	ldxa		[%l7]0x88, %g3		! %g3:%g2 is tweak
+
+	sethi		%hi(0x76543210), %l7
+	or		%l7, %lo(0x76543210), %l7
+	bmask		%l7, %g0, %g0		! byte swap mask
+
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	call		_${alg}${bits}_load_${dir}ckey
+	and		$len, 15,  $rem
+	and		$len, -16, $len
+___
+$code.=<<___ if ($dir eq "de");
+	mov		0, %l7
+	movrnz		$rem, 16,  %l7
+	sub		$len, %l7, $len
+___
+$code.=<<___;
+
+	sub		$inp, $out, $blk_init	! $inp!=$out
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		64, $iright
+	mov		0xff, $omask
+	sub		$iright, $ileft, $iright
+	and		$out, 7, $ooff
+	cmp		$len, 255
+	movrnz		$ooff, 0, $blk_init		! if (	$out&7 ||
+	movleu		$::size_t_cc, 0, $blk_init	!	$len<256 ||
+	brnz,pn		$blk_init, .L${bits}_xts_${dir}blk !	$inp==$out)
+	srl		$omask, $ooff, $omask
+
+	andcc		$len, 16, %g0		! is number of blocks even?
+___
+$code.=<<___ if ($dir eq "de");
+	brz,pn		$len, .L${bits}_xts_${dir}steal
+___
+$code.=<<___;
+	alignaddrl	$out, %g0, $out
+	bz		%icc, .L${bits}_xts_${dir}loop2x
+	srlx		$len, 4, $len
+.L${bits}_xts_${dir}loop:
+	ldx		[$inp + 0], %o0
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 8], %o1
+
+	ldx		[$inp + 16], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+4:
+	movxtod		%g2, %f12
+	movxtod		%g3, %f14
+	bshuffle	%f12, %f12, %f12
+	bshuffle	%f14, %f14, %f14
+
+	xor		%g4, %o0, %o0		! ^= rk[0]
+	xor		%g5, %o1, %o1
+	movxtod		%o0, %f0
+	movxtod		%o1, %f2
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 16+63], 20
+	call		_${alg}${bits}_${dir}crypt_1x
+	add		$inp, 16, $inp
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+
+	srax		%g3, 63, %l7		! next tweak value
+	addcc		%g2, %g2, %g2
+	and		%l7, 0x87, %l7
+	addxc		%g3, %g3, %g3
+	xor		%l7, %g2, %g2
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 1, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	brnz,pt		$len, .L${bits}_xts_${dir}loop2x
+	add		$out, 16, $out
+
+	brnz,pn		$rem, .L${bits}_xts_${dir}steal
+	nop
+
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f4		! handle unaligned output
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+	stda		%f4, [$out + $omask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $omask, $omask
+	stda		%f8, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_xts_${dir}loop2x+4
+	orn		%g0, $omask, $omask
+
+	brnz,pn		$rem, .L${bits}_xts_${dir}steal
+	nop
+
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}_xts_${dir}loop2x:
+	ldx		[$inp + 0], %o0
+	ldx		[$inp + 8], %o1
+	ldx		[$inp + 16], %o2
+	brz,pt		$ileft, 4f
+	ldx		[$inp + 24], %o3
+
+	ldx		[$inp + 32], %o4
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	or		%g1, %o0, %o0
+	sllx		%o1, $ileft, %o1
+	srlx		%o2, $iright, %g1
+	or		%g1, %o1, %o1
+	sllx		%o2, $ileft, %o2
+	srlx		%o3, $iright, %g1
+	or		%g1, %o2, %o2
+	sllx		%o3, $ileft, %o3
+	srlx		%o4, $iright, %o4
+	or		%o4, %o3, %o3
+4:
+	movxtod		%g2, %f12
+	movxtod		%g3, %f14
+	bshuffle	%f12, %f12, %f12
+	bshuffle	%f14, %f14, %f14
+
+	srax		%g3, 63, %l7		! next tweak value
+	addcc		%g2, %g2, %g2
+	and		%l7, 0x87, %l7
+	addxc		%g3, %g3, %g3
+	xor		%l7, %g2, %g2
+
+	movxtod		%g2, %f8
+	movxtod		%g3, %f10
+	bshuffle	%f8,  %f8,  %f8
+	bshuffle	%f10, %f10, %f10
+
+	xor		%g4, %o0, %o0		! ^= rk[0]
+	xor		%g5, %o1, %o1
+	xor		%g4, %o2, %o2		! ^= rk[0]
+	xor		%g5, %o3, %o3
+	movxtod		%o0, %f0
+	movxtod		%o1, %f2
+	movxtod		%o2, %f4
+	movxtod		%o3, %f6
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+	fxor		%f8,  %f4, %f4		! ^= tweak[0]
+	fxor		%f10, %f6, %f6
+
+	prefetch	[$out + 63], 22
+	prefetch	[$inp + 32+63], 20
+	call		_${alg}${bits}_${dir}crypt_2x
+	add		$inp, 32, $inp
+
+	movxtod		%g2, %f8
+	movxtod		%g3, %f10
+
+	srax		%g3, 63, %l7		! next tweak value
+	addcc		%g2, %g2, %g2
+	and		%l7, 0x87, %l7
+	addxc		%g3, %g3, %g3
+	xor		%l7, %g2, %g2
+
+	bshuffle	%f8,  %f8,  %f8
+	bshuffle	%f10, %f10, %f10
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+	fxor		%f8,  %f4, %f4
+	fxor		%f10, %f6, %f6
+
+	brnz,pn		$ooff, 2f
+	sub		$len, 2, $len
+		
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	std		%f4, [$out + 16]
+	std		%f6, [$out + 24]
+	brnz,pt		$len, .L${bits}_xts_${dir}loop2x
+	add		$out, 32, $out
+
+	fsrc2		%f4, %f0
+	fsrc2		%f6, %f2
+	brnz,pn		$rem, .L${bits}_xts_${dir}steal
+	nop
+
+	ret
+	restore
+
+.align	16
+2:	ldxa		[$inp]0x82, %o0		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f8		! handle unaligned output
+	faligndata	%f0, %f2, %f10
+	faligndata	%f2, %f4, %f12
+	faligndata	%f4, %f6, %f14
+	faligndata	%f6, %f6, %f0
+
+	stda		%f8, [$out + $omask]0xc0	! partial store
+	std		%f10, [$out + 8]
+	std		%f12, [$out + 16]
+	std		%f14, [$out + 24]
+	add		$out, 32, $out
+	orn		%g0, $omask, $omask
+	stda		%f0, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .L${bits}_xts_${dir}loop2x+4
+	orn		%g0, $omask, $omask
+
+	fsrc2		%f4, %f0
+	fsrc2		%f6, %f2
+	brnz,pn		$rem, .L${bits}_xts_${dir}steal
+	nop
+
+	ret
+	restore
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+.align	32
+.L${bits}_xts_${dir}blk:
+	add	$out, $len, $blk_init
+	and	$blk_init, 63, $blk_init	! tail
+	sub	$len, $blk_init, $len
+	add	$blk_init, 15, $blk_init	! round up to 16n
+	srlx	$len, 4, $len
+	srl	$blk_init, 4, $blk_init
+	sub	$len, 1, $len
+	add	$blk_init, 1, $blk_init
+
+.L${bits}_xts_${dir}blk2x:
+	ldx		[$inp + 0], %o0
+	ldx		[$inp + 8], %o1
+	ldx		[$inp + 16], %o2
+	brz,pt		$ileft, 5f
+	ldx		[$inp + 24], %o3
+
+	ldx		[$inp + 32], %o4
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	or		%g1, %o0, %o0
+	sllx		%o1, $ileft, %o1
+	srlx		%o2, $iright, %g1
+	or		%g1, %o1, %o1
+	sllx		%o2, $ileft, %o2
+	srlx		%o3, $iright, %g1
+	or		%g1, %o2, %o2
+	sllx		%o3, $ileft, %o3
+	srlx		%o4, $iright, %o4
+	or		%o4, %o3, %o3
+5:
+	movxtod		%g2, %f12
+	movxtod		%g3, %f14
+	bshuffle	%f12, %f12, %f12
+	bshuffle	%f14, %f14, %f14
+
+	srax		%g3, 63, %l7		! next tweak value
+	addcc		%g2, %g2, %g2
+	and		%l7, 0x87, %l7
+	addxc		%g3, %g3, %g3
+	xor		%l7, %g2, %g2
+
+	movxtod		%g2, %f8
+	movxtod		%g3, %f10
+	bshuffle	%f8,  %f8,  %f8
+	bshuffle	%f10, %f10, %f10
+
+	xor		%g4, %o0, %o0		! ^= rk[0]
+	xor		%g5, %o1, %o1
+	xor		%g4, %o2, %o2		! ^= rk[0]
+	xor		%g5, %o3, %o3
+	movxtod		%o0, %f0
+	movxtod		%o1, %f2
+	movxtod		%o2, %f4
+	movxtod		%o3, %f6
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+	fxor		%f8,  %f4, %f4		! ^= tweak[0]
+	fxor		%f10, %f6, %f6
+
+	prefetch	[$inp + 32+63], 20
+	call		_${alg}${bits}_${dir}crypt_2x
+	add		$inp, 32, $inp
+
+	movxtod		%g2, %f8
+	movxtod		%g3, %f10
+
+	srax		%g3, 63, %l7		! next tweak value
+	addcc		%g2, %g2, %g2
+	and		%l7, 0x87, %l7
+	addxc		%g3, %g3, %g3
+	xor		%l7, %g2, %g2
+
+	bshuffle	%f8,  %f8,  %f8
+	bshuffle	%f10, %f10, %f10
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+	fxor		%f8,  %f4, %f4
+	fxor		%f10, %f6, %f6
+
+	stda		%f0, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f2, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f4, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	add		$out, 8, $out
+	stda		%f6, [$out]0xe2		! ASI_BLK_INIT, T4-specific
+	bgu,pt		$::size_t_cc, .L${bits}_xts_${dir}blk2x
+	add		$out, 8, $out
+
+	add		$blk_init, $len, $len
+	andcc		$len, 1, %g0		! is number of blocks even?
+	membar		#StoreLoad|#StoreStore
+	bnz,pt		%icc, .L${bits}_xts_${dir}loop
+	srl		$len, 0, $len
+	brnz,pn		$len, .L${bits}_xts_${dir}loop2x
+	nop
+
+	fsrc2		%f4, %f0
+	fsrc2		%f6, %f2
+	brnz,pn		$rem, .L${bits}_xts_${dir}steal
+	nop
+
+	ret
+	restore
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+___
+$code.=<<___ if ($dir eq "en");
+.align	32
+.L${bits}_xts_${dir}steal:
+	std		%f0, [%fp + $::bias-16]	! copy of output
+	std		%f2, [%fp + $::bias-8]
+
+	srl		$ileft, 3, $ileft
+	add		%fp, $::bias-16, %l7
+	add		$inp, $ileft, $inp	! original $inp+$len&-15
+	add		$out, $ooff, $out	! original $out+$len&-15
+	mov		0, $ileft
+	nop					! align
+
+.L${bits}_xts_${dir}stealing:
+	ldub		[$inp + $ileft], %o0
+	ldub		[%l7  + $ileft], %o1
+	dec		$rem
+	stb		%o0, [%l7  + $ileft]
+	stb		%o1, [$out + $ileft]
+	brnz		$rem, .L${bits}_xts_${dir}stealing
+	inc		$ileft
+
+	mov		%l7, $inp
+	sub		$out, 16, $out
+	mov		0, $ileft
+	sub		$out, $ooff, $out
+	ba		.L${bits}_xts_${dir}loop	! one more time
+	mov		1, $len				! $rem is 0
+___
+$code.=<<___ if ($dir eq "de");
+.align	32
+.L${bits}_xts_${dir}steal:
+	ldx		[$inp + 0], %o0
+	brz,pt		$ileft, 8f
+	ldx		[$inp + 8], %o1
+
+	ldx		[$inp + 16], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+8:
+	srax		%g3, 63, %l7		! next tweak value
+	addcc		%g2, %g2, %o2
+	and		%l7, 0x87, %l7
+	addxc		%g3, %g3, %o3
+	xor		%l7, %o2, %o2
+
+	movxtod		%o2, %f12
+	movxtod		%o3, %f14
+	bshuffle	%f12, %f12, %f12
+	bshuffle	%f14, %f14, %f14
+
+	xor		%g4, %o0, %o0		! ^= rk[0]
+	xor		%g5, %o1, %o1
+	movxtod		%o0, %f0
+	movxtod		%o1, %f2
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+
+	call		_${alg}${bits}_${dir}crypt_1x
+	add		$inp, 16, $inp
+
+	fxor		%f12, %f0, %f0		! ^= tweak[0]
+	fxor		%f14, %f2, %f2
+
+	std		%f0, [%fp + $::bias-16]
+	std		%f2, [%fp + $::bias-8]
+
+	srl		$ileft, 3, $ileft
+	add		%fp, $::bias-16, %l7
+	add		$inp, $ileft, $inp	! original $inp+$len&-15
+	add		$out, $ooff, $out	! original $out+$len&-15
+	mov		0, $ileft
+	add		$out, 16, $out
+	nop					! align
+
+.L${bits}_xts_${dir}stealing:
+	ldub		[$inp + $ileft], %o0
+	ldub		[%l7  + $ileft], %o1
+	dec		$rem
+	stb		%o0, [%l7  + $ileft]
+	stb		%o1, [$out + $ileft]
+	brnz		$rem, .L${bits}_xts_${dir}stealing
+	inc		$ileft
+
+	mov		%l7, $inp
+	sub		$out, 16, $out
+	mov		0, $ileft
+	sub		$out, $ooff, $out
+	ba		.L${bits}_xts_${dir}loop	! one more time
+	mov		1, $len				! $rem is 0
+___
+$code.=<<___;
+	ret
+	restore
+.type	${alg}${bits}_t4_xts_${dir}crypt,#function
+.size	${alg}${bits}_t4_xts_${dir}crypt,.-${alg}${bits}_t4_xts_${dir}crypt
+___
+}
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extentions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %visopf = (	"faligndata"	=> 0x048,
+		"bshuffle"	=> 0x04c,
+		"fnot2"		=> 0x066,
+		"fxor"		=> 0x06c,
+		"fsrc2"		=> 0x078	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"addxc"		=> 0x011,
+		"addxccc"	=> 0x013,
+		"umulxhi"	=> 0x016,
+		"alignaddr"	=> 0x018,
+		"bmask"		=> 0x019,
+		"alignaddrl"	=> 0x01a	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unaes_round {	# 4-argument instructions
+my ($mnemonic,$rs1,$rs2,$rs3,$rd)=@_;
+my ($ref,$opf);
+my %aesopf = (	"aes_eround01"	=> 0,
+		"aes_eround23"	=> 1,
+		"aes_dround01"	=> 2,
+		"aes_dround23"	=> 3,
+		"aes_eround01_l"=> 4,
+		"aes_eround23_l"=> 5,
+		"aes_dround01_l"=> 6,
+		"aes_dround23_l"=> 7,
+		"aes_kexpand1"	=> 8	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rs3,$rd";
+
+    if (defined($opf=$aesopf{$mnemonic})) {
+	$rs3 = ($rs3 =~ /%f([0-6]*[02468])/) ? (($1|$1>>5)&31) : $rs3;
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x19<<19|$rs1<<14|$rs3<<9|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unaes_kexpand {	# 3-argument instructions
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %aesopf = (	"aes_kexpand0"	=> 0x130,
+		"aes_kexpand2"	=> 0x131	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if (defined($opf=$aesopf{$mnemonic})) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub uncamellia_f {	# 4-argument instructions
+my ($mnemonic,$rs1,$rs2,$rs3,$rd)=@_;
+my ($ref,$opf);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rs3,$rd";
+
+    if (1) {
+	$rs3 = ($rs3 =~ /%f([0-6]*[02468])/) ? (($1|$1>>5)&31) : $rs3;
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x19<<19|$rs1<<14|$rs3<<9|0xc<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub uncamellia3 {	# 3-argument instructions
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %cmllopf = (	"camellia_fl"	=> 0x13c,
+		"camellia_fli"	=> 0x13d	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if (defined($opf=$cmllopf{$mnemonic})) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unmovxtox {		# 2-argument instructions
+my ($mnemonic,$rs,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24, "f" => 0 );
+my ($ref,$opf);
+my %movxopf = (	"movdtox"	=> 0x110,
+		"movstouw"	=> 0x111,
+		"movstosw"	=> 0x113,
+		"movxtod"	=> 0x118,
+		"movwtos"	=> 0x119	);
+
+    $ref = "$mnemonic\t$rs,$rd";
+
+    if (defined($opf=$movxopf{$mnemonic})) {
+	foreach ($rs,$rd) {
+	    return $ref if (!/%([fgoli])([0-9]{1,2})/);
+	    $_=$bias{$1}+$2;
+	    if ($2>=32) {
+		return $ref if ($2&1);
+		# re-encode for upper double register addressing
+		$_=($2|$2>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$opf<<5|$rs,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub undes {
+my ($mnemonic)=shift;
+my @args=@_;
+my ($ref,$opf);
+my %desopf = (	"des_round"	=> 0b1001,
+		"des_ip"	=> 0b100110100,
+		"des_iip"	=> 0b100110101,
+		"des_kexpand"	=> 0b100110110	);
+
+    $ref = "$mnemonic\t".join(",",@_);
+
+    if (defined($opf=$desopf{$mnemonic})) {	# 4-arg
+	if ($mnemonic eq "des_round") {
+	    foreach (@args[0..3]) {
+		return $ref if (!/%f([0-9]{1,2})/);
+		$_=$1;
+		if ($1>=32) {
+		    return $ref if ($1&1);
+		    # re-encode for upper double register addressing
+		    $_=($1|$1>>5)&31;
+		}
+	    }
+	    return  sprintf ".word\t0x%08x !%s",
+			    2<<30|0b011001<<19|$opf<<5|$args[0]<<14|$args[1]|$args[2]<<9|$args[3]<<25,
+			    $ref;
+	} elsif ($mnemonic eq "des_kexpand") {	# 3-arg
+	    foreach (@args[0..2]) {
+		return $ref if (!/(%f)?([0-9]{1,2})/);
+		$_=$2;
+		if ($2>=32) {
+		    return $ref if ($2&1);
+		    # re-encode for upper double register addressing
+		    $_=($2|$2>>5)&31;
+		}
+	    }
+	    return  sprintf ".word\t0x%08x !%s",
+			    2<<30|0b110110<<19|$opf<<5|$args[0]<<14|$args[1]|$args[2]<<25,
+			    $ref;
+	} else {				# 2-arg
+	    foreach (@args[0..1]) {
+		return $ref if (!/%f([0-9]{1,2})/);
+		$_=$1;
+		if ($1>=32) {
+		    return $ref if ($2&1);
+		    # re-encode for upper double register addressing
+		    $_=($1|$1>>5)&31;
+		}
+	    }
+	    return  sprintf ".word\t0x%08x !%s",
+			    2<<30|0b110110<<19|$opf<<5|$args[0]<<14|$args[1]<<25,
+			    $ref;
+	}
+    } else {
+	return $ref;
+    }
+}
+
+sub emit_assembler {
+    foreach (split("\n",$::code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(f[a-z]+2[sd]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})\s*$/$1\t%f0,$2,$3/go;
+
+	s/\b(aes_[edk][^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*([%fx0-9]+),\s*(%f[0-9]{1,2})/
+		&unaes_round($1,$2,$3,$4,$5)
+	 /geo or
+	s/\b(aes_kexpand[02])\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unaes_kexpand($1,$2,$3,$4)
+	 /geo or
+	s/\b(camellia_f)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*([%fx0-9]+),\s*(%f[0-9]{1,2})/
+		&uncamellia_f($1,$2,$3,$4,$5)
+	 /geo or
+	s/\b(camellia_[^s]+)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&uncamellia3($1,$2,$3,$4)
+	 /geo or
+	s/\b(des_\w+)\s+(%f[0-9]{1,2}),\s*([%fx0-9]+)(?:,\s*(%f[0-9]{1,2})(?:,\s*(%f[0-9]{1,2}))?)?/
+		&undes($1,$2,$3,$4,$5)
+	 /geo or
+	s/\b(mov[ds]to\w+)\s+(%f[0-9]{1,2}),\s*(%[goli][0-7])/
+		&unmovxtox($1,$2,$3)
+	 /geo or
+	s/\b(mov[xw]to[ds])\s+(%[goli][0-7]),\s*(%f[0-9]{1,2})/
+		&unmovxtox($1,$2,$3)
+	 /geo or
+	s/\b([fb][^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+	 /geo or
+	s/\b(umulxhi|bmask|addxc[c]{0,2}|alignaddr[l]*)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+	 /geo;
+
+	print $_,"\n";
+    }
+}
+
+1;
diff --git a/crypto/perlasm/x86_64-xlate.pl b/crypto/perlasm/x86_64-xlate.pl
new file mode 100755
index 0000000..24ff1b4
--- /dev/null
+++ b/crypto/perlasm/x86_64-xlate.pl
@@ -0,0 +1,1151 @@
+#!/usr/bin/env perl
+
+# Ascetic x86_64 AT&T to MASM/NASM assembler translator by <appro>.
+#
+# Why AT&T to MASM and not vice versa? Several reasons. Because AT&T
+# format is way easier to parse. Because it's simpler to "gear" from
+# Unix ABI to Windows one [see cross-reference "card" at the end of
+# file]. Because Linux targets were available first...
+#
+# In addition the script also "distills" code suitable for GNU
+# assembler, so that it can be compiled with more rigid assemblers,
+# such as Solaris /usr/ccs/bin/as.
+#
+# This translator is not designed to convert *arbitrary* assembler
+# code from AT&T format to MASM one. It's designed to convert just
+# enough to provide for dual-ABI OpenSSL modules development...
+# There *are* limitations and you might have to modify your assembler
+# code or this script to achieve the desired result...
+#
+# Currently recognized limitations:
+#
+# - can't use multiple ops per line;
+#
+# Dual-ABI styling rules.
+#
+# 1. Adhere to Unix register and stack layout [see cross-reference
+#    ABI "card" at the end for explanation].
+# 2. Forget about "red zone," stick to more traditional blended
+#    stack frame allocation. If volatile storage is actually required
+#    that is. If not, just leave the stack as is.
+# 3. Functions tagged with ".type name,@function" get crafted with
+#    unified Win64 prologue and epilogue automatically. If you want
+#    to take care of ABI differences yourself, tag functions as
+#    ".type name,@abi-omnipotent" instead.
+# 4. To optimize the Win64 prologue you can specify number of input
+#    arguments as ".type name,@function,N." Keep in mind that if N is
+#    larger than 6, then you *have to* write "abi-omnipotent" code,
+#    because >6 cases can't be addressed with unified prologue.
+# 5. Name local labels as .L*, do *not* use dynamic labels such as 1:
+#    (sorry about latter).
+# 6. Don't use [or hand-code with .byte] "rep ret." "ret" mnemonic is
+#    required to identify the spots, where to inject Win64 epilogue!
+#    But on the pros, it's then prefixed with rep automatically:-)
+# 7. Stick to explicit ip-relative addressing. If you have to use
+#    GOTPCREL addressing, stick to mov symbol@GOTPCREL(%rip),%r??.
+#    Both are recognized and translated to proper Win64 addressing
+#    modes. To support legacy code a synthetic directive, .picmeup,
+#    is implemented. It puts address of the *next* instruction into
+#    target register, e.g.:
+#
+#		.picmeup	%rax
+#		lea		.Label-.(%rax),%rax
+#
+# 8. In order to provide for structured exception handling unified
+#    Win64 prologue copies %rsp value to %rax. For further details
+#    see SEH paragraph at the end.
+# 9. .init segment is allowed to contain calls to functions only.
+# a. If function accepts more than 4 arguments *and* >4th argument
+#    is declared as non 64-bit value, do clear its upper part.
+
+my $flavour = shift;
+my $output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+open STDOUT,">$output" || die "can't open $output: $!"
+	if (defined($output));
+
+my $gas=1;	$gas=0 if ($output =~ /\.asm$/);
+my $elf=1;	$elf=0 if (!$gas);
+my $win64=0;
+my $prefix="";
+my $decor=".L";
+
+my $masmref=8 + 50727*2**-32;	# 8.00.50727 shipped with VS2005
+my $masm=0;
+my $PTR=" PTR";
+
+my $nasmref=2.03;
+my $nasm=0;
+
+if    ($flavour eq "mingw64")	{ $gas=1; $elf=0; $win64=1;
+				  $prefix=`echo __USER_LABEL_PREFIX__ | $ENV{CC} -E -P -`;
+				  chomp($prefix);
+				}
+elsif ($flavour eq "macosx")	{ $gas=1; $elf=0; $prefix="_"; $decor="L\$"; }
+elsif ($flavour eq "masm")	{ $gas=0; $elf=0; $masm=$masmref; $win64=1; $decor="\$L\$"; }
+elsif ($flavour eq "nasm")	{ $gas=0; $elf=0; $nasm=$nasmref; $win64=1; $decor="\$L\$"; $PTR=""; }
+elsif (!$gas)
+{   if ($ENV{ASM} =~ m/nasm/ && `nasm -v` =~ m/version ([0-9]+)\.([0-9]+)/i)
+    {	$nasm = $1 + $2*0.01; $PTR="";  }
+    elsif (`ml64 2>&1` =~ m/Version ([0-9]+)\.([0-9]+)(\.([0-9]+))?/)
+    {	$masm = $1 + $2*2**-16 + $4*2**-32;   }
+    die "no assembler found on %PATH" if (!($nasm || $masm));
+    $win64=1;
+    $elf=0;
+    $decor="\$L\$";
+}
+
+my $current_segment;
+my $current_function;
+my %globals;
+
+{ package opcode;	# pick up opcodes
+    sub re {
+	my	$self = shift;	# single instance in enough...
+	local	*line = shift;
+	undef	$ret;
+
+	if ($line =~ /^([a-z][a-z0-9]*)/i) {
+	    $self->{op} = $1;
+	    $ret = $self;
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+
+	    undef $self->{sz};
+	    undef $self->{arg_sz};
+	    if ($self->{op} =~ /^(movz)x?([bw]).*/) {	# movz is pain...
+		$self->{op} = $1;
+		$self->{sz} = $2;
+	    } elsif ($self->{op} =~ /call|jmp/) {
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /^p/ && $' !~ /^(ush|op|insrw)/) { # SSEn
+		$self->{sz} = "";
+	    } elsif ($self->{op} eq "vpbroadcastq") {
+		$self->{arg_sz} = "q";
+	    } elsif ($self->{op} eq "vpbroadcastb") {
+		$self->{arg_sz} = "b";
+	    } elsif ($self->{op} =~ /^vinserti128/) {
+		$self->{arg_sz} = "x";
+	    } elsif ($self->{op} =~ /^v/) { # VEX
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /movq/ && $line =~ /%xmm/) {
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /([a-z]{3,})([qlwb])$/) {
+		$self->{op} = $1;
+		$self->{sz} = $2;
+	    }
+	}
+	$ret;
+    }
+    sub size {
+	my $self = shift;
+	my $sz   = shift;
+	$self->{sz} = $sz if (defined($sz) && !defined($self->{sz}));
+	$self->{sz};
+    }
+    sub arg_size {
+	my $self = shift;
+	$self->{arg_sz};
+    }
+    sub out {
+	my $self = shift;
+	if ($gas) {
+	    if ($self->{op} eq "movz") {	# movz is pain...
+		sprintf "%s%s%s",$self->{op},$self->{sz},shift;
+	    } elsif ($self->{op} =~ /^set/) { 
+		"$self->{op}";
+	    } elsif ($self->{op} eq "ret") {
+		my $epilogue = "";
+		if ($win64 && $current_function->{abi} eq "svr4") {
+		    $epilogue = "movq	8(%rsp),%rdi\n\t" .
+				"movq	16(%rsp),%rsi\n\t";
+		}
+	    	$epilogue . ".byte	0xf3,0xc3";
+	    } elsif ($self->{op} eq "call" && !$elf && $current_segment eq ".init") {
+		".p2align\t3\n\t.quad";
+	    } else {
+		"$self->{op}$self->{sz}";
+	    }
+	} else {
+	    $self->{op} =~ s/^movz/movzx/;
+	    if ($self->{op} eq "ret") {
+		$self->{op} = "";
+		if ($win64 && $current_function->{abi} eq "svr4") {
+		    $self->{op} = "mov	rdi,QWORD${PTR}[8+rsp]\t;WIN64 epilogue\n\t".
+				  "mov	rsi,QWORD${PTR}[16+rsp]\n\t";
+	    	}
+		$self->{op} .= "DB\t0F3h,0C3h\t\t;repret";
+	    } elsif ($self->{op} =~ /^(pop|push)f/) {
+		$self->{op} .= $self->{sz};
+	    } elsif ($self->{op} eq "call" && $current_segment eq ".CRT\$XCU") {
+		$self->{op} = "\tDQ";
+	    } 
+	    $self->{op};
+	}
+    }
+    sub mnemonic {
+	my $self=shift;
+	my $op=shift;
+	$self->{op}=$op if (defined($op));
+	$self->{op};
+    }
+}
+{ package const;	# pick up constants, which start with $
+    sub re {
+	my	$self = shift;	# single instance in enough...
+	local	*line = shift;
+	undef	$ret;
+
+	if ($line =~ /^\$([^,]+)/) {
+	    $self->{value} = $1;
+	    $ret = $self;
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+	}
+	$ret;
+    }
+    sub out {
+    	my $self = shift;
+
+	if ($gas) {
+	    # Solaris /usr/ccs/bin/as can't handle multiplications
+	    # in $self->{value}
+	    $self->{value} =~ s/(?<![\w\$\.])(0x?[0-9a-f]+)/oct($1)/egi;
+	    $self->{value} =~ s/([0-9]+\s*[\*\/\%]\s*[0-9]+)/eval($1)/eg;
+	    sprintf "\$%s",$self->{value};
+	} else {
+	    $self->{value} =~ s/(0b[0-1]+)/oct($1)/eig;
+	    $self->{value} =~ s/0x([0-9a-f]+)/0$1h/ig if ($masm);
+	    sprintf "%s",$self->{value};
+	}
+    }
+}
+{ package ea;		# pick up effective addresses: expr(%reg,%reg,scale)
+    sub re {
+	my	$self = shift;	# single instance in enough...
+	local	*line = shift;
+	undef	$ret;
+
+	# optional * ---vvv--- appears in indirect jmp/call
+	if ($line =~ /^(\*?)([^\(,]*)\(([%\w,]+)\)/) {
+	    $self->{asterisk} = $1;
+	    $self->{label} = $2;
+	    ($self->{base},$self->{index},$self->{scale})=split(/,/,$3);
+	    $self->{scale} = 1 if (!defined($self->{scale}));
+	    $ret = $self;
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+
+	    if ($win64 && $self->{label} =~ s/\@GOTPCREL//) {
+		die if (opcode->mnemonic() ne "mov");
+		opcode->mnemonic("lea");
+	    }
+	    $self->{base}  =~ s/^%//;
+	    $self->{index} =~ s/^%// if (defined($self->{index}));
+	}
+	$ret;
+    }
+    sub size {}
+    sub out {
+    	my $self = shift;
+	my $sz = shift;
+
+	$self->{label} =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei;
+	$self->{label} =~ s/\.L/$decor/g;
+
+	# Silently convert all EAs to 64-bit. This is required for
+	# elder GNU assembler and results in more compact code,
+	# *but* most importantly AES module depends on this feature!
+	$self->{index} =~ s/^[er](.?[0-9xpi])[d]?$/r\1/;
+	$self->{base}  =~ s/^[er](.?[0-9xpi])[d]?$/r\1/;
+
+	# Solaris /usr/ccs/bin/as can't handle multiplications
+	# in $self->{label}, new gas requires sign extension...
+	use integer;
+	$self->{label} =~ s/(?<![\w\$\.])(0x?[0-9a-f]+)/oct($1)/egi;
+	$self->{label} =~ s/\b([0-9]+\s*[\*\/\%]\s*[0-9]+)\b/eval($1)/eg;
+	$self->{label} =~ s/\b([0-9]+)\b/$1<<32>>32/eg;
+
+	if (!$self->{label} && $self->{index} && $self->{scale}==1 &&
+	    $self->{base} =~ /(rbp|r13)/) {
+		$self->{base} = $self->{index}; $self->{index} = $1;
+	}
+
+	if ($gas) {
+	    $self->{label} =~ s/^___imp_/__imp__/   if ($flavour eq "mingw64");
+
+	    if (defined($self->{index})) {
+		sprintf "%s%s(%s,%%%s,%d)",$self->{asterisk},
+					$self->{label},
+					$self->{base}?"%$self->{base}":"",
+					$self->{index},$self->{scale};
+	    } else {
+		sprintf "%s%s(%%%s)",	$self->{asterisk},$self->{label},$self->{base};
+	    }
+	} else {
+	    %szmap = (	b=>"BYTE$PTR", w=>"WORD$PTR", l=>"DWORD$PTR",
+	    		q=>"QWORD$PTR",o=>"OWORD$PTR",x=>"XMMWORD$PTR",
+			y=>"YMMWORD$PTR" );
+
+	    $self->{label} =~ s/\./\$/g;
+	    $self->{label} =~ s/(?<![\w\$\.])0x([0-9a-f]+)/0$1h/ig;
+	    $self->{label} = "($self->{label})" if ($self->{label} =~ /[\*\+\-\/]/);
+	    $sz="q" if ($self->{asterisk} || opcode->mnemonic() =~ /^v?movq$/);
+	    $sz="l" if (opcode->mnemonic() =~ /^v?movd$/);
+
+	    if (defined($self->{index})) {
+		sprintf "%s[%s%s*%d%s]",$szmap{$sz},
+					$self->{label}?"$self->{label}+":"",
+					$self->{index},$self->{scale},
+					$self->{base}?"+$self->{base}":"";
+	    } elsif ($self->{base} eq "rip") {
+		sprintf "%s[%s]",$szmap{$sz},$self->{label};
+	    } else {
+		sprintf "%s[%s%s]",$szmap{$sz},
+					$self->{label}?"$self->{label}+":"",
+					$self->{base};
+	    }
+	}
+    }
+}
+{ package register;	# pick up registers, which start with %.
+    sub re {
+	my	$class = shift;	# muliple instances...
+	my	$self = {};
+	local	*line = shift;
+	undef	$ret;
+
+	# optional * ---vvv--- appears in indirect jmp/call
+	if ($line =~ /^(\*?)%(\w+)/) {
+	    bless $self,$class;
+	    $self->{asterisk} = $1;
+	    $self->{value} = $2;
+	    $ret = $self;
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+	}
+	$ret;
+    }
+    sub size {
+	my	$self = shift;
+	undef	$ret;
+
+	if    ($self->{value} =~ /^r[\d]+b$/i)	{ $ret="b"; }
+	elsif ($self->{value} =~ /^r[\d]+w$/i)	{ $ret="w"; }
+	elsif ($self->{value} =~ /^r[\d]+d$/i)	{ $ret="l"; }
+	elsif ($self->{value} =~ /^r[\w]+$/i)	{ $ret="q"; }
+	elsif ($self->{value} =~ /^[a-d][hl]$/i){ $ret="b"; }
+	elsif ($self->{value} =~ /^[\w]{2}l$/i)	{ $ret="b"; }
+	elsif ($self->{value} =~ /^[\w]{2}$/i)	{ $ret="w"; }
+	elsif ($self->{value} =~ /^e[a-z]{2}$/i){ $ret="l"; }
+
+	$ret;
+    }
+    sub out {
+    	my $self = shift;
+	if ($gas)	{ sprintf "%s%%%s",$self->{asterisk},$self->{value}; }
+	else		{ $self->{value}; }
+    }
+}
+{ package label;	# pick up labels, which end with :
+    sub re {
+	my	$self = shift;	# single instance is enough...
+	local	*line = shift;
+	undef	$ret;
+
+	if ($line =~ /(^[\.\w]+)\:/) {
+	    $self->{value} = $1;
+	    $ret = $self;
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+
+	    $self->{value} =~ s/^\.L/$decor/;
+	}
+	$ret;
+    }
+    sub out {
+	my $self = shift;
+
+	if ($gas) {
+	    my $func = ($globals{$self->{value}} or $self->{value}) . ":";
+	    if ($win64	&&
+			$current_function->{name} eq $self->{value} &&
+			$current_function->{abi} eq "svr4") {
+		$func .= "\n";
+		$func .= "	movq	%rdi,8(%rsp)\n";
+		$func .= "	movq	%rsi,16(%rsp)\n";
+		$func .= "	movq	%rsp,%rax\n";
+		$func .= "${decor}SEH_begin_$current_function->{name}:\n";
+		my $narg = $current_function->{narg};
+		$narg=6 if (!defined($narg));
+		$func .= "	movq	%rcx,%rdi\n" if ($narg>0);
+		$func .= "	movq	%rdx,%rsi\n" if ($narg>1);
+		$func .= "	movq	%r8,%rdx\n"  if ($narg>2);
+		$func .= "	movq	%r9,%rcx\n"  if ($narg>3);
+		$func .= "	movq	40(%rsp),%r8\n" if ($narg>4);
+		$func .= "	movq	48(%rsp),%r9\n" if ($narg>5);
+	    }
+	    $func;
+	} elsif ($self->{value} ne "$current_function->{name}") {
+	    $self->{value} .= ":" if ($masm && $ret!~m/^\$/);
+	    $self->{value} . ":";
+	} elsif ($win64 && $current_function->{abi} eq "svr4") {
+	    my $func =	"$current_function->{name}" .
+			($nasm ? ":" : "\tPROC $current_function->{scope}") .
+			"\n";
+	    $func .= "	mov	QWORD${PTR}[8+rsp],rdi\t;WIN64 prologue\n";
+	    $func .= "	mov	QWORD${PTR}[16+rsp],rsi\n";
+	    $func .= "	mov	rax,rsp\n";
+	    $func .= "${decor}SEH_begin_$current_function->{name}:";
+	    $func .= ":" if ($masm);
+	    $func .= "\n";
+	    my $narg = $current_function->{narg};
+	    $narg=6 if (!defined($narg));
+	    $func .= "	mov	rdi,rcx\n" if ($narg>0);
+	    $func .= "	mov	rsi,rdx\n" if ($narg>1);
+	    $func .= "	mov	rdx,r8\n"  if ($narg>2);
+	    $func .= "	mov	rcx,r9\n"  if ($narg>3);
+	    $func .= "	mov	r8,QWORD${PTR}[40+rsp]\n" if ($narg>4);
+	    $func .= "	mov	r9,QWORD${PTR}[48+rsp]\n" if ($narg>5);
+	    $func .= "\n";
+	} else {
+	   "$current_function->{name}".
+			($nasm ? ":" : "\tPROC $current_function->{scope}");
+	}
+    }
+}
+{ package expr;		# pick up expressioins
+    sub re {
+	my	$self = shift;	# single instance is enough...
+	local	*line = shift;
+	undef	$ret;
+
+	if ($line =~ /(^[^,]+)/) {
+	    $self->{value} = $1;
+	    $ret = $self;
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+
+	    $self->{value} =~ s/\@PLT// if (!$elf);
+	    $self->{value} =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei;
+	    $self->{value} =~ s/\.L/$decor/g;
+	}
+	$ret;
+    }
+    sub out {
+	my $self = shift;
+	if ($nasm && opcode->mnemonic()=~m/^j(?![re]cxz)/) {
+	    "NEAR ".$self->{value};
+	} else {
+	    $self->{value};
+	}
+    }
+}
+{ package directive;	# pick up directives, which start with .
+    sub re {
+	my	$self = shift;	# single instance is enough...
+	local	*line = shift;
+	undef	$ret;
+	my	$dir;
+	my	%opcode =	# lea 2f-1f(%rip),%dst; 1: nop; 2:
+		(	"%rax"=>0x01058d48,	"%rcx"=>0x010d8d48,
+			"%rdx"=>0x01158d48,	"%rbx"=>0x011d8d48,
+			"%rsp"=>0x01258d48,	"%rbp"=>0x012d8d48,
+			"%rsi"=>0x01358d48,	"%rdi"=>0x013d8d48,
+			"%r8" =>0x01058d4c,	"%r9" =>0x010d8d4c,
+			"%r10"=>0x01158d4c,	"%r11"=>0x011d8d4c,
+			"%r12"=>0x01258d4c,	"%r13"=>0x012d8d4c,
+			"%r14"=>0x01358d4c,	"%r15"=>0x013d8d4c	);
+
+	if ($line =~ /^\s*(\.\w+)/) {
+	    $dir = $1;
+	    $ret = $self;
+	    undef $self->{value};
+	    $line = substr($line,@+[0]); $line =~ s/^\s+//;
+
+	    SWITCH: for ($dir) {
+		/\.picmeup/ && do { if ($line =~ /(%r[\w]+)/i) {
+			    		$dir="\t.long";
+					$line=sprintf "0x%x,0x90000000",$opcode{$1};
+				    }
+				    last;
+				  };
+		/\.global|\.globl|\.extern/
+			    && do { $globals{$line} = $prefix . $line;
+				    $line = $globals{$line} if ($prefix);
+				    last;
+				  };
+		/\.type/    && do { ($sym,$type,$narg) = split(',',$line);
+				    if ($type eq "\@function") {
+					undef $current_function;
+					$current_function->{name} = $sym;
+					$current_function->{abi}  = "svr4";
+					$current_function->{narg} = $narg;
+					$current_function->{scope} = defined($globals{$sym})?"PUBLIC":"PRIVATE";
+				    } elsif ($type eq "\@abi-omnipotent") {
+					undef $current_function;
+					$current_function->{name} = $sym;
+					$current_function->{scope} = defined($globals{$sym})?"PUBLIC":"PRIVATE";
+				    }
+				    $line =~ s/\@abi\-omnipotent/\@function/;
+				    $line =~ s/\@function.*/\@function/;
+				    last;
+				  };
+		/\.asciz/   && do { if ($line =~ /^"(.*)"$/) {
+					$dir  = ".byte";
+					$line = join(",",unpack("C*",$1),0);
+				    }
+				    last;
+				  };
+		/\.rva|\.long|\.quad/
+			    && do { $line =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei;
+				    $line =~ s/\.L/$decor/g;
+				    last;
+				  };
+	    }
+
+	    if ($gas) {
+		$self->{value} = $dir . "\t" . $line;
+
+		if ($dir =~ /\.extern/) {
+		    $self->{value} = ""; # swallow extern
+		} elsif (!$elf && $dir =~ /\.type/) {
+		    $self->{value} = "";
+		    $self->{value} = ".def\t" . ($globals{$1} or $1) . ";\t" .
+				(defined($globals{$1})?".scl 2;":".scl 3;") .
+				"\t.type 32;\t.endef"
+				if ($win64 && $line =~ /([^,]+),\@function/);
+		} elsif (!$elf && $dir =~ /\.size/) {
+		    $self->{value} = "";
+		    if (defined($current_function)) {
+			$self->{value} .= "${decor}SEH_end_$current_function->{name}:"
+				if ($win64 && $current_function->{abi} eq "svr4");
+			undef $current_function;
+		    }
+		} elsif (!$elf && $dir =~ /\.align/) {
+		    $self->{value} = ".p2align\t" . (log($line)/log(2));
+		} elsif ($dir eq ".section") {
+		    $current_segment=$line;
+		    if (!$elf && $current_segment eq ".init") {
+			if	($flavour eq "macosx")	{ $self->{value} = ".mod_init_func"; }
+			elsif	($flavour eq "mingw64")	{ $self->{value} = ".section\t.ctors"; }
+		    }
+		} elsif ($dir =~ /\.(text|data)/) {
+		    $current_segment=".$1";
+		} elsif ($dir =~ /\.hidden/) {
+		    if    ($flavour eq "macosx")  { $self->{value} = ".private_extern\t$prefix$line"; }
+		    elsif ($flavour eq "mingw64") { $self->{value} = ""; }
+		} elsif ($dir =~ /\.comm/) {
+		    $self->{value} = "$dir\t$prefix$line";
+		    $self->{value} =~ s|,([0-9]+),([0-9]+)$|",$1,".log($2)/log(2)|e if ($flavour eq "macosx");
+		}
+		$line = "";
+		return $self;
+	    }
+
+	    # non-gas case or nasm/masm
+	    SWITCH: for ($dir) {
+		/\.text/    && do { my $v=undef;
+				    if ($nasm) {
+					$v="section	.text code align=64\n";
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$current_segment = ".text\$";
+					$v.="$current_segment\tSEGMENT ";
+					$v.=$masm>=$masmref ? "ALIGN(64)" : "PAGE";
+					$v.=" 'CODE'";
+				    }
+				    $self->{value} = $v;
+				    last;
+				  };
+		/\.data/    && do { my $v=undef;
+				    if ($nasm) {
+					$v="section	.data data align=8\n";
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$current_segment = "_DATA";
+					$v.="$current_segment\tSEGMENT";
+				    }
+				    $self->{value} = $v;
+				    last;
+				  };
+		/\.section/ && do { my $v=undef;
+				    $line =~ s/([^,]*).*/$1/;
+				    $line = ".CRT\$XCU" if ($line eq ".init");
+				    if ($nasm) {
+					$v="section	$line";
+					if ($line=~/\.([px])data/) {
+					    $v.=" rdata align=";
+					    $v.=$1 eq "p"? 4 : 8;
+					} elsif ($line=~/\.CRT\$/i) {
+					    $v.=" rdata align=8";
+					}
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$v.="$line\tSEGMENT";
+					if ($line=~/\.([px])data/) {
+					    $v.=" READONLY";
+					    $v.=" ALIGN(".($1 eq "p" ? 4 : 8).")" if ($masm>=$masmref);
+					} elsif ($line=~/\.CRT\$/i) {
+					    $v.=" READONLY ";
+					    $v.=$masm>=$masmref ? "ALIGN(8)" : "DWORD";
+					}
+				    }
+				    $current_segment = $line;
+				    $self->{value} = $v;
+				    last;
+				  };
+		/\.extern/  && do { $self->{value}  = "EXTERN\t".$line;
+				    $self->{value} .= ":NEAR" if ($masm);
+				    last;
+				  };
+		/\.globl|.global/
+			    && do { $self->{value}  = $masm?"PUBLIC":"global";
+				    $self->{value} .= "\t".$line;
+				    last;
+				  };
+		/\.size/    && do { if (defined($current_function)) {
+					undef $self->{value};
+					if ($current_function->{abi} eq "svr4") {
+					    $self->{value}="${decor}SEH_end_$current_function->{name}:";
+					    $self->{value}.=":\n" if($masm);
+					}
+					$self->{value}.="$current_function->{name}\tENDP" if($masm && $current_function->{name});
+					undef $current_function;
+				    }
+				    last;
+				  };
+		/\.align/   && do { $self->{value} = "ALIGN\t".$line; last; };
+		/\.(value|long|rva|quad)/
+			    && do { my $sz  = substr($1,0,1);
+				    my @arr = split(/,\s*/,$line);
+				    my $last = pop(@arr);
+				    my $conv = sub  {	my $var=shift;
+							$var=~s/^(0b[0-1]+)/oct($1)/eig;
+							$var=~s/^0x([0-9a-f]+)/0$1h/ig if ($masm);
+							if ($sz eq "D" && ($current_segment=~/.[px]data/ || $dir eq ".rva"))
+							{ $var=~s/([_a-z\$\@][_a-z0-9\$\@]*)/$nasm?"$1 wrt ..imagebase":"imagerel $1"/egi; }
+							$var;
+						    };  
+
+				    $sz =~ tr/bvlrq/BWDDQ/;
+				    $self->{value} = "\tD$sz\t";
+				    for (@arr) { $self->{value} .= &$conv($_).","; }
+				    $self->{value} .= &$conv($last);
+				    last;
+				  };
+		/\.byte/    && do { my @str=split(/,\s*/,$line);
+				    map(s/(0b[0-1]+)/oct($1)/eig,@str);
+				    map(s/0x([0-9a-f]+)/0$1h/ig,@str) if ($masm);	
+				    while ($#str>15) {
+					$self->{value}.="DB\t"
+						.join(",",@str[0..15])."\n";
+					foreach (0..15) { shift @str; }
+				    }
+				    $self->{value}.="DB\t"
+						.join(",",@str) if (@str);
+				    last;
+				  };
+		/\.comm/    && do { my @str=split(/,\s*/,$line);
+				    my $v=undef;
+				    if ($nasm) {
+					$v.="common	$prefix@str[0] @str[1]";
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$current_segment = "_DATA";
+					$v.="$current_segment\tSEGMENT\n";
+					$v.="COMM	@str[0]:DWORD:".@str[1]/4;
+				    }
+				    $self->{value} = $v;
+				    last;
+				  };
+	    }
+	    $line = "";
+	}
+
+	$ret;
+    }
+    sub out {
+	my $self = shift;
+	$self->{value};
+    }
+}
+
+sub rex {
+ local *opcode=shift;
+ my ($dst,$src,$rex)=@_;
+
+   $rex|=0x04 if($dst>=8);
+   $rex|=0x01 if($src>=8);
+   push @opcode,($rex|0x40) if ($rex);
+}
+
+# older gas and ml64 don't handle SSE>2 instructions
+my %regrm = (	"%eax"=>0, "%ecx"=>1, "%edx"=>2, "%ebx"=>3,
+		"%esp"=>4, "%ebp"=>5, "%esi"=>6, "%edi"=>7	);
+
+my $movq = sub {	# elderly gas can't handle inter-register movq
+  my $arg = shift;
+  my @opcode=(0x66);
+    if ($arg =~ /%xmm([0-9]+),\s*%r(\w+)/) {
+	my ($src,$dst)=($1,$2);
+	if ($dst !~ /[0-9]+/)	{ $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,$src,$dst,0x8);
+	push @opcode,0x0f,0x7e;
+	push @opcode,0xc0|(($src&7)<<3)|($dst&7);	# ModR/M
+	@opcode;
+    } elsif ($arg =~ /%r(\w+),\s*%xmm([0-9]+)/) {
+	my ($src,$dst)=($2,$1);
+	if ($dst !~ /[0-9]+/)	{ $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,$src,$dst,0x8);
+	push @opcode,0x0f,0x6e;
+	push @opcode,0xc0|(($src&7)<<3)|($dst&7);	# ModR/M
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pextrd = sub {
+    if (shift =~ /\$([0-9]+),\s*%xmm([0-9]+),\s*(%\w+)/) {
+      my @opcode=(0x66);
+	$imm=$1;
+	$src=$2;
+	$dst=$3;
+	if ($dst =~ /%r([0-9]+)d/)	{ $dst = $1; }
+	elsif ($dst =~ /%e/)		{ $dst = $regrm{$dst}; }
+	rex(\@opcode,$src,$dst);
+	push @opcode,0x0f,0x3a,0x16;
+	push @opcode,0xc0|(($src&7)<<3)|($dst&7);	# ModR/M
+	push @opcode,$imm;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pinsrd = sub {
+    if (shift =~ /\$([0-9]+),\s*(%\w+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	$imm=$1;
+	$src=$2;
+	$dst=$3;
+	if ($src =~ /%r([0-9]+)/)	{ $src = $1; }
+	elsif ($src =~ /%e/)		{ $src = $regrm{$src}; }
+	rex(\@opcode,$dst,$src);
+	push @opcode,0x0f,0x3a,0x22;
+	push @opcode,0xc0|(($dst&7)<<3)|($src&7);	# ModR/M
+	push @opcode,$imm;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pshufb = sub {
+    if (shift =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	rex(\@opcode,$2,$1);
+	push @opcode,0x0f,0x38,0x00;
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $palignr = sub {
+    if (shift =~ /\$([0-9]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x3a,0x0f;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	push @opcode,$1;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pclmulqdq = sub {
+    if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x3a,0x44;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $rdrand = sub {
+    if (shift =~ /%[er](\w+)/) {
+      my @opcode=();
+      my $dst=$1;
+	if ($dst !~ /[0-9]+/) { $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,0,$1,8);
+	push @opcode,0x0f,0xc7,0xf0|($dst&7);
+	@opcode;
+    } else {
+	();
+    }
+};
+
+sub rxb {
+ local *opcode=shift;
+ my ($dst,$src1,$src2,$rxb)=@_;
+
+   $rxb|=0x7<<5;
+   $rxb&=~(0x04<<5) if($dst>=8);
+   $rxb&=~(0x01<<5) if($src1>=8);
+   $rxb&=~(0x02<<5) if($src2>=8);
+   push @opcode,$rxb;
+}
+
+my $vprotd = sub {
+    if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x8f);
+	rxb(\@opcode,$3,$2,-1,0x08);
+	push @opcode,0x78,0xc2;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $vprotq = sub {
+    if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x8f);
+	rxb(\@opcode,$3,$2,-1,0x08);
+	push @opcode,0x78,0xc3;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+if ($nasm) {
+    print <<___;
+default	rel
+%define XMMWORD
+___
+} elsif ($masm) {
+    print <<___;
+OPTION	DOTNAME
+___
+}
+
+print STDOUT "#if defined(__x86_64__)\n" if ($gas);
+
+while($line=<>) {
+
+    chomp($line);
+
+    $line =~ s|[#!].*$||;	# get rid of asm-style comments...
+    $line =~ s|/\*.*\*/||;	# ... and C-style comments...
+    $line =~ s|^\s+||;		# ... and skip white spaces in beginning
+    $line =~ s|\s+$||;		# ... and at the end
+
+    undef $label;
+    undef $opcode;
+    undef @args;
+
+    if ($label=label->re(\$line))	{ print $label->out(); }
+
+    if (directive->re(\$line)) {
+	printf "%s",directive->out();
+    } elsif ($opcode=opcode->re(\$line)) {
+	my $asm = eval("\$".$opcode->mnemonic());
+	undef @bytes;
+	
+	if ((ref($asm) eq 'CODE') && scalar(@bytes=&$asm($line))) {
+	    print $gas?".byte\t":"DB\t",join(',',@bytes),"\n";
+	    next;
+	}
+
+	ARGUMENT: while (1) {
+	my $arg;
+
+	if ($arg=register->re(\$line))	{ opcode->size($arg->size()); }
+	elsif ($arg=const->re(\$line))	{ }
+	elsif ($arg=ea->re(\$line))	{ }
+	elsif ($arg=expr->re(\$line))	{ }
+	else				{ last ARGUMENT; }
+
+	push @args,$arg;
+
+	last ARGUMENT if ($line !~ /^,/);
+
+	$line =~ s/^,\s*//;
+	} # ARGUMENT:
+
+	if ($#args>=0) {
+	    my $insn;
+	    my $sz=opcode->size();
+	    my $arg_sz=opcode->arg_size();
+
+	    if ($gas) {
+		$insn = $opcode->out($#args>=1?$args[$#args]->size():$sz);
+		@args = map($_->out($sz),@args);
+		printf "\t%s\t%s",$insn,join(",",@args);
+	    } else {
+		$insn = $opcode->out();
+		foreach (@args) {
+		    my $arg = $_->out();
+		    # $insn.=$sz compensates for movq, pinsrw, ...
+		    if ($arg =~ /^xmm[0-9]+$/) { $insn.=$sz; $sz="x" if(!$sz); last; }
+		    if ($arg =~ /^ymm[0-9]+$/) { $insn.=$sz; $sz="y" if(!$sz); last; }
+		    if ($arg =~ /^mm[0-9]+$/)  { $insn.=$sz; $sz="q" if(!$sz); last; }
+		}
+		$sz=$arg_sz if($arg_sz);
+		@args = reverse(@args);
+		undef $sz if ($nasm && $opcode->mnemonic() eq "lea");
+
+		if ($insn eq "movq" && $#args == 1 && $args[0]->out($sz) eq "xmm0" && $args[1]->out($sz) eq "rax") {
+		    # I have no clue why MASM can't parse this instruction.
+		    printf "DB 66h, 48h, 0fh, 6eh, 0c0h";
+		} else {
+		    printf "\t%s\t%s",$insn,join(",",map($_->out($sz),@args));
+		}
+	    }
+	} else {
+	    printf "\t%s",$opcode->out();
+	}
+    }
+
+    print $line,"\n";
+}
+
+print "\n$current_segment\tENDS\n"	if ($current_segment && $masm);
+print "END\n"				if ($masm);
+print "#endif"				if ($gas);
+
+
+close STDOUT;
+
+#################################################
+# Cross-reference x86_64 ABI "card"
+#
+# 		Unix		Win64
+# %rax		*		*
+# %rbx		-		-
+# %rcx		#4		#1
+# %rdx		#3		#2
+# %rsi		#2		-
+# %rdi		#1		-
+# %rbp		-		-
+# %rsp		-		-
+# %r8		#5		#3
+# %r9		#6		#4
+# %r10		*		*
+# %r11		*		*
+# %r12		-		-
+# %r13		-		-
+# %r14		-		-
+# %r15		-		-
+# 
+# (*)	volatile register
+# (-)	preserved by callee
+# (#)	Nth argument, volatile
+#
+# In Unix terms top of stack is argument transfer area for arguments
+# which could not be accomodated in registers. Or in other words 7th
+# [integer] argument resides at 8(%rsp) upon function entry point.
+# 128 bytes above %rsp constitute a "red zone" which is not touched
+# by signal handlers and can be used as temporal storage without
+# allocating a frame.
+#
+# In Win64 terms N*8 bytes on top of stack is argument transfer area,
+# which belongs to/can be overwritten by callee. N is the number of
+# arguments passed to callee, *but* not less than 4! This means that
+# upon function entry point 5th argument resides at 40(%rsp), as well
+# as that 32 bytes from 8(%rsp) can always be used as temporal
+# storage [without allocating a frame]. One can actually argue that
+# one can assume a "red zone" above stack pointer under Win64 as well.
+# Point is that at apparently no occasion Windows kernel would alter
+# the area above user stack pointer in true asynchronous manner...
+#
+# All the above means that if assembler programmer adheres to Unix
+# register and stack layout, but disregards the "red zone" existense,
+# it's possible to use following prologue and epilogue to "gear" from
+# Unix to Win64 ABI in leaf functions with not more than 6 arguments.
+#
+# omnipotent_function:
+# ifdef WIN64
+#	movq	%rdi,8(%rsp)
+#	movq	%rsi,16(%rsp)
+#	movq	%rcx,%rdi	; if 1st argument is actually present
+#	movq	%rdx,%rsi	; if 2nd argument is actually ...
+#	movq	%r8,%rdx	; if 3rd argument is ...
+#	movq	%r9,%rcx	; if 4th argument ...
+#	movq	40(%rsp),%r8	; if 5th ...
+#	movq	48(%rsp),%r9	; if 6th ...
+# endif
+#	...
+# ifdef WIN64
+#	movq	8(%rsp),%rdi
+#	movq	16(%rsp),%rsi
+# endif
+#	ret
+#
+#################################################
+# Win64 SEH, Structured Exception Handling.
+#
+# Unlike on Unix systems(*) lack of Win64 stack unwinding information
+# has undesired side-effect at run-time: if an exception is raised in
+# assembler subroutine such as those in question (basically we're
+# referring to segmentation violations caused by malformed input
+# parameters), the application is briskly terminated without invoking
+# any exception handlers, most notably without generating memory dump
+# or any user notification whatsoever. This poses a problem. It's
+# possible to address it by registering custom language-specific
+# handler that would restore processor context to the state at
+# subroutine entry point and return "exception is not handled, keep
+# unwinding" code. Writing such handler can be a challenge... But it's
+# doable, though requires certain coding convention. Consider following
+# snippet:
+#
+# .type	function,@function
+# function:
+#	movq	%rsp,%rax	# copy rsp to volatile register
+#	pushq	%r15		# save non-volatile registers
+#	pushq	%rbx
+#	pushq	%rbp
+#	movq	%rsp,%r11
+#	subq	%rdi,%r11	# prepare [variable] stack frame
+#	andq	$-64,%r11
+#	movq	%rax,0(%r11)	# check for exceptions
+#	movq	%r11,%rsp	# allocate [variable] stack frame
+#	movq	%rax,0(%rsp)	# save original rsp value
+# magic_point:
+#	...
+#	movq	0(%rsp),%rcx	# pull original rsp value
+#	movq	-24(%rcx),%rbp	# restore non-volatile registers
+#	movq	-16(%rcx),%rbx
+#	movq	-8(%rcx),%r15
+#	movq	%rcx,%rsp	# restore original rsp
+#	ret
+# .size function,.-function
+#
+# The key is that up to magic_point copy of original rsp value remains
+# in chosen volatile register and no non-volatile register, except for
+# rsp, is modified. While past magic_point rsp remains constant till
+# the very end of the function. In this case custom language-specific
+# exception handler would look like this:
+#
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+# {	ULONG64 *rsp = (ULONG64 *)context->Rax;
+#	if (context->Rip >= magic_point)
+#	{   rsp = ((ULONG64 **)context->Rsp)[0];
+#	    context->Rbp = rsp[-3];
+#	    context->Rbx = rsp[-2];
+#	    context->R15 = rsp[-1];
+#	}
+#	context->Rsp = (ULONG64)rsp;
+#	context->Rdi = rsp[1];
+#	context->Rsi = rsp[2];
+#
+#	memcpy (disp->ContextRecord,context,sizeof(CONTEXT));
+#	RtlVirtualUnwind(UNW_FLAG_NHANDLER,disp->ImageBase,
+#		dips->ControlPc,disp->FunctionEntry,disp->ContextRecord,
+#		&disp->HandlerData,&disp->EstablisherFrame,NULL);
+#	return ExceptionContinueSearch;
+# }
+#
+# It's appropriate to implement this handler in assembler, directly in
+# function's module. In order to do that one has to know members'
+# offsets in CONTEXT and DISPATCHER_CONTEXT structures and some constant
+# values. Here they are:
+#
+#	CONTEXT.Rax				120
+#	CONTEXT.Rcx				128
+#	CONTEXT.Rdx				136
+#	CONTEXT.Rbx				144
+#	CONTEXT.Rsp				152
+#	CONTEXT.Rbp				160
+#	CONTEXT.Rsi				168
+#	CONTEXT.Rdi				176
+#	CONTEXT.R8				184
+#	CONTEXT.R9				192
+#	CONTEXT.R10				200
+#	CONTEXT.R11				208
+#	CONTEXT.R12				216
+#	CONTEXT.R13				224
+#	CONTEXT.R14				232
+#	CONTEXT.R15				240
+#	CONTEXT.Rip				248
+#	CONTEXT.Xmm6				512
+#	sizeof(CONTEXT)				1232
+#	DISPATCHER_CONTEXT.ControlPc		0
+#	DISPATCHER_CONTEXT.ImageBase		8
+#	DISPATCHER_CONTEXT.FunctionEntry	16
+#	DISPATCHER_CONTEXT.EstablisherFrame	24
+#	DISPATCHER_CONTEXT.TargetIp		32
+#	DISPATCHER_CONTEXT.ContextRecord	40
+#	DISPATCHER_CONTEXT.LanguageHandler	48
+#	DISPATCHER_CONTEXT.HandlerData		56
+#	UNW_FLAG_NHANDLER			0
+#	ExceptionContinueSearch			1
+#
+# In order to tie the handler to the function one has to compose
+# couple of structures: one for .xdata segment and one for .pdata.
+#
+# UNWIND_INFO structure for .xdata segment would be
+#
+# function_unwind_info:
+#	.byte	9,0,0,0
+#	.rva	handler
+#
+# This structure designates exception handler for a function with
+# zero-length prologue, no stack frame or frame register.
+#
+# To facilitate composing of .pdata structures, auto-generated "gear"
+# prologue copies rsp value to rax and denotes next instruction with
+# .LSEH_begin_{function_name} label. This essentially defines the SEH
+# styling rule mentioned in the beginning. Position of this label is
+# chosen in such manner that possible exceptions raised in the "gear"
+# prologue would be accounted to caller and unwound from latter's frame.
+# End of function is marked with respective .LSEH_end_{function_name}
+# label. To summarize, .pdata segment would contain
+#
+#	.rva	.LSEH_begin_function
+#	.rva	.LSEH_end_function
+#	.rva	function_unwind_info
+#
+# Reference to functon_unwind_info from .xdata segment is the anchor.
+# In case you wonder why references are 32-bit .rvas and not 64-bit
+# .quads. References put into these two segments are required to be
+# *relative* to the base address of the current binary module, a.k.a.
+# image base. No Win64 module, be it .exe or .dll, can be larger than
+# 2GB and thus such relative references can be and are accommodated in
+# 32 bits.
+#
+# Having reviewed the example function code, one can argue that "movq
+# %rsp,%rax" above is redundant. It is not! Keep in mind that on Unix
+# rax would contain an undefined value. If this "offends" you, use
+# another register and refrain from modifying rax till magic_point is
+# reached, i.e. as if it was a non-volatile register. If more registers
+# are required prior [variable] frame setup is completed, note that
+# nobody says that you can have only one "magic point." You can
+# "liberate" non-volatile registers by denoting last stack off-load
+# instruction and reflecting it in finer grade unwind logic in handler.
+# After all, isn't it why it's called *language-specific* handler...
+#
+# Attentive reader can notice that exceptions would be mishandled in
+# auto-generated "gear" epilogue. Well, exception effectively can't
+# occur there, because if memory area used by it was subject to
+# segmentation violation, then it would be raised upon call to the
+# function (and as already mentioned be accounted to caller, which is
+# not a problem). If you're still not comfortable, then define tail
+# "magic point" just prior ret instruction and have handler treat it...
+#
+# (*)	Note that we're talking about run-time, not debug-time. Lack of
+#	unwind information makes debugging hard on both Windows and
+#	Unix. "Unlike" referes to the fact that on Unix signal handler
+#	will always be invoked, core dumped and appropriate exit code
+#	returned to parent (for user notification).
diff --git a/crypto/perlasm/x86asm.pl b/crypto/perlasm/x86asm.pl
new file mode 100644
index 0000000..7236bea
--- /dev/null
+++ b/crypto/perlasm/x86asm.pl
@@ -0,0 +1,290 @@
+#!/usr/bin/env perl
+
+# require 'x86asm.pl';
+# &asm_init(<flavor>,"des-586.pl"[,$i386only]);
+# &function_begin("foo");
+# ...
+# &function_end("foo");
+# &asm_finish
+
+$out=();
+$i386=0;
+
+# AUTOLOAD is this context has quite unpleasant side effect, namely
+# that typos in function calls effectively go to assembler output,
+# but on the pros side we don't have to implement one subroutine per
+# each opcode...
+sub ::AUTOLOAD
+{ my $opcode = $AUTOLOAD;
+
+    die "more than 4 arguments passed to $opcode" if ($#_>3);
+
+    $opcode =~ s/.*:://;
+    if    ($opcode =~ /^push/) { $stack+=4; }
+    elsif ($opcode =~ /^pop/)  { $stack-=4; }
+
+    &generic($opcode,@_) or die "undefined subroutine \&$AUTOLOAD";
+}
+
+sub ::emit
+{ my $opcode=shift;
+
+    if ($#_==-1)    { push(@out,"\t$opcode\n");				}
+    else            { push(@out,"\t$opcode\t".join(',',@_)."\n");	}
+}
+
+sub ::LB
+{   $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'low byte'";
+  $1."l";
+}
+sub ::HB
+{   $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'high byte'";
+  $1."h";
+}
+sub ::stack_push{ my $num=$_[0]*4; $stack+=$num; &sub("esp",$num);	}
+sub ::stack_pop	{ my $num=$_[0]*4; $stack-=$num; &add("esp",$num);	}
+sub ::blindpop	{ &pop($_[0]); $stack+=4;				}
+sub ::wparam	{ &DWP($stack+4*$_[0],"esp");				}
+sub ::swtmp	{ &DWP(4*$_[0],"esp");					}
+
+sub ::bswap
+{   if ($i386)	# emulate bswap for i386
+    {	&comment("bswap @_");
+	&xchg(&HB(@_),&LB(@_));
+	&ror (@_,16);
+	&xchg(&HB(@_),&LB(@_));
+    }
+    else
+    {	&generic("bswap",@_);	}
+}
+# These are made-up opcodes introduced over the years essentially
+# by ignorance, just alias them to real ones...
+sub ::movb	{ &mov(@_);	}
+sub ::xorb	{ &xor(@_);	}
+sub ::rotl	{ &rol(@_);	}
+sub ::rotr	{ &ror(@_);	}
+sub ::exch	{ &xchg(@_);	}
+sub ::halt	{ &hlt;		}
+sub ::movz	{ &movzx(@_);	}
+sub ::pushf	{ &pushfd;	}
+sub ::popf	{ &popfd;	}
+
+# 3 argument instructions
+sub ::movq
+{ my($p1,$p2,$optimize)=@_;
+
+    if ($optimize && $p1=~/^mm[0-7]$/ && $p2=~/^mm[0-7]$/)
+    # movq between mmx registers can sink Intel CPUs
+    {	&::pshufw($p1,$p2,0xe4);		}
+    else
+    {	&::generic("movq",@_);			}
+}
+
+# SSE>2 instructions
+my %regrm = (	"eax"=>0, "ecx"=>1, "edx"=>2, "ebx"=>3,
+		"esp"=>4, "ebp"=>5, "esi"=>6, "edi"=>7	);
+sub ::pextrd
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /(e[a-dsd][ixp]):xmm([0-7])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x16,0xc0|($2<<3)|$regrm{$1},$imm);	}
+    else
+    {	&::generic("pextrd",@_);		}
+}
+
+sub ::pinsrd
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):(e[a-dsd][ixp])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x22,0xc0|($1<<3)|$regrm{$2},$imm);	}
+    else
+    {	&::generic("pinsrd",@_);		}
+}
+
+sub ::pshufb
+{ my($dst,$src)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x66,0x0f,0x38,0x00,0xc0|($1<<3)|$2);	}
+    else
+    {	&::generic("pshufb",@_);		}
+}
+
+sub ::palignr
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x0f,0xc0|($1<<3)|$2,$imm);	}
+    else
+    {	&::generic("palignr",@_);		}
+}
+
+sub ::pclmulqdq
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x44,0xc0|($1<<3)|$2,$imm);	}
+    else
+    {	&::generic("pclmulqdq",@_);		}
+}
+
+sub ::rdrand
+{ my ($dst)=@_;
+    if ($dst =~ /(e[a-dsd][ixp])/)
+    {	&::data_byte(0x0f,0xc7,0xf0|$regrm{$dst});	}
+    else
+    {	&::generic("rdrand",@_);	}
+}
+
+sub rxb {
+ local *opcode=shift;
+ my ($dst,$src1,$src2,$rxb)=@_;
+
+   $rxb|=0x7<<5;
+   $rxb&=~(0x04<<5) if($dst>=8);
+   $rxb&=~(0x01<<5) if($src1>=8);
+   $rxb&=~(0x02<<5) if($src2>=8);
+   push @opcode,$rxb;
+}
+
+sub ::vprotd
+{ my $args=join(',',@_);
+    if ($args =~ /xmm([0-7]),xmm([0-7]),([x0-9a-f]+)/)
+    { my @opcode=(0x8f);
+	rxb(\@opcode,$1,$2,-1,0x08);
+	push @opcode,0x78,0xc2;
+	push @opcode,0xc0|($2&7)|(($1&7)<<3);		# ModR/M
+	my $c=$3;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	&::data_byte(@opcode);
+    }
+    else
+    {	&::generic("vprotd",@_);	}
+}
+
+# label management
+$lbdecor="L";		# local label decoration, set by package
+$label="000";
+
+sub ::islabel		# see is argument is a known label
+{ my $i;
+    foreach $i (values %label) { return $i if ($i eq $_[0]); }
+  $label{$_[0]};	# can be undef
+}
+
+sub ::label		# instantiate a function-scope label
+{   if (!defined($label{$_[0]}))
+    {	$label{$_[0]}="${lbdecor}${label}${_[0]}"; $label++;   }
+  $label{$_[0]};
+}
+
+sub ::LABEL		# instantiate a file-scope label
+{   $label{$_[0]}=$_[1] if (!defined($label{$_[0]}));
+  $label{$_[0]};
+}
+
+sub ::static_label	{ &::LABEL($_[0],$lbdecor.$_[0]); }
+
+sub ::set_label_B	{ push(@out,"@_:\n"); }
+sub ::set_label
+{ my $label=&::label($_[0]);
+    &::align($_[1]) if ($_[1]>1);
+    &::set_label_B($label);
+  $label;
+}
+
+sub ::wipe_labels	# wipes function-scope labels
+{   foreach $i (keys %label)
+    {	delete $label{$i} if ($label{$i} =~ /^\Q${lbdecor}\E[0-9]{3}/);	}
+}
+
+# subroutine management
+sub ::function_begin
+{   &function_begin_B(@_);
+    $stack=4;
+    &push("ebp");
+    &push("ebx");
+    &push("esi");
+    &push("edi");
+}
+
+sub ::function_end
+{   &pop("edi");
+    &pop("esi");
+    &pop("ebx");
+    &pop("ebp");
+    &ret();
+    &function_end_B(@_);
+    $stack=0;
+    &wipe_labels();
+}
+
+sub ::function_end_A
+{   &pop("edi");
+    &pop("esi");
+    &pop("ebx");
+    &pop("ebp");
+    &ret();
+    $stack+=16;	# readjust esp as if we didn't pop anything
+}
+
+sub ::asciz
+{ my @str=unpack("C*",shift);
+    push @str,0;
+    while ($#str>15) {
+	&data_byte(@str[0..15]);
+	foreach (0..15) { shift @str; }
+    }
+    &data_byte(@str) if (@str);
+}
+
+sub ::asm_finish
+{   &file_end();
+    print "#if defined(__i386__)\n";
+    print @out;
+    print "#endif";
+}
+
+sub ::asm_init
+{ my ($type,$fn,$cpu)=@_;
+
+    $filename=$fn;
+    $i386=$cpu;
+
+    $elf=$cpp=$coff=$aout=$macosx=$win32=$netware=$mwerks=$android=0;
+    if    (($type eq "elf"))
+    {	$elf=1;			require "x86gas.pl";	}
+    elsif (($type eq "a\.out"))
+    {	$aout=1;		require "x86gas.pl";	}
+    elsif (($type eq "coff" or $type eq "gaswin"))
+    {	$coff=1;		require "x86gas.pl";	}
+    elsif (($type eq "win32n"))
+    {	$win32=1;		require "x86nasm.pl";	}
+    elsif (($type eq "nw-nasm"))
+    {	$netware=1;		require "x86nasm.pl";	}
+    #elsif (($type eq "nw-mwasm"))
+    #{	$netware=1; $mwerks=1;	require "x86nasm.pl";	}
+    elsif (($type eq "win32"))
+    {	$win32=1;		require "x86masm.pl";	}
+    elsif (($type eq "macosx"))
+    {	$aout=1; $macosx=1;	require "x86gas.pl";	}
+    elsif (($type eq "android"))
+    {	$elf=1; $android=1;	require "x86gas.pl";	}
+    else
+    {	print STDERR <<"EOF";
+Pick one target type from
+	elf	- Linux, FreeBSD, Solaris x86, etc.
+	a.out	- DJGPP, elder OpenBSD, etc.
+	coff	- GAS/COFF such as Win32 targets
+	win32n	- Windows 95/Windows NT NASM format
+	nw-nasm - NetWare NASM format
+	macosx	- Mac OS X
+EOF
+	exit(1);
+    }
+
+    $pic=0;
+    for (@ARGV) { $pic=1 if (/\-[fK]PIC/i); }
+
+    $filename =~ s/\.pl$//;
+    &file($filename);
+}
+
+sub ::hidden {}
+
+1;
diff --git a/crypto/perlasm/x86gas.pl b/crypto/perlasm/x86gas.pl
new file mode 100644
index 0000000..8bf6cef
--- /dev/null
+++ b/crypto/perlasm/x86gas.pl
@@ -0,0 +1,258 @@
+#!/usr/bin/env perl
+
+package x86gas;
+
+*out=\@::out;
+
+$::lbdecor=$::aout?"L":".L";		# local label decoration
+$nmdecor=($::aout or $::coff)?"_":"";	# external name decoration
+
+$initseg="";
+
+$align=16;
+$align=log($align)/log(2) if ($::aout);
+$com_start="#" if ($::aout or $::coff);
+
+sub opsize()
+{ my $reg=shift;
+    if    ($reg =~ m/^%e/o)		{ "l"; }
+    elsif ($reg =~ m/^%[a-d][hl]$/o)	{ "b"; }
+    elsif ($reg =~ m/^%[xm]/o)		{ undef; }
+    else				{ "w"; }
+}
+
+# swap arguments;
+# expand opcode with size suffix;
+# prefix numeric constants with $;
+sub ::generic
+{ my($opcode,@arg)=@_;
+  my($suffix,$dst,$src);
+
+    @arg=reverse(@arg);
+
+    for (@arg)
+    {	s/^(\*?)(e?[a-dsixphl]{2})$/$1%$2/o;	# gp registers
+	s/^([xy]?mm[0-7])$/%$1/o;		# xmm/mmx registers
+	s/^(\-?[0-9]+)$/\$$1/o;			# constants
+	s/^(\-?0x[0-9a-f]+)$/\$$1/o;		# constants
+    }
+
+    $dst = $arg[$#arg]		if ($#arg>=0);
+    $src = $arg[$#arg-1]	if ($#arg>=1);
+    if    ($dst =~ m/^%/o)	{ $suffix=&opsize($dst); }
+    elsif ($src =~ m/^%/o)	{ $suffix=&opsize($src); }
+    else			{ $suffix="l";           }
+    undef $suffix if ($dst =~ m/^%[xm]/o || $src =~ m/^%[xm]/o);
+
+    if ($#_==0)				{ &::emit($opcode);		}
+    elsif ($#_==1 && $opcode =~ m/^(call|clflush|j|loop|set)/o)
+					{ &::emit($opcode,@arg);	}
+    else				{ &::emit($opcode.$suffix,@arg);}
+
+  1;
+}
+#
+# opcodes not covered by ::generic above, mostly inconsistent namings...
+#
+sub ::movzx	{ &::movzb(@_);			}
+sub ::pushfd	{ &::pushfl;			}
+sub ::popfd	{ &::popfl;			}
+sub ::cpuid	{ &::emit(".byte\t0x0f,0xa2");	}
+sub ::rdtsc	{ &::emit(".byte\t0x0f,0x31");	}
+
+sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
+sub ::call_ptr	{ &::generic("call","*$_[0]");	}
+sub ::jmp_ptr	{ &::generic("jmp","*$_[0]");	}
+
+*::bswap = sub	{ &::emit("bswap","%$_[0]");	} if (!$::i386);
+
+sub ::DWP
+{ my($addr,$reg1,$reg2,$idx)=@_;
+  my $ret="";
+
+    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }
+
+    $addr =~ s/^\s+//;
+    # prepend global references with optional underscore
+    $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;
+
+    $reg1 = "%$reg1" if ($reg1);
+    $reg2 = "%$reg2" if ($reg2);
+
+    $ret .= $addr if (($addr ne "") && ($addr ne 0));
+
+    if ($reg2)
+    {	$idx!= 0 or $idx=1;
+	$ret .= "($reg1,$reg2,$idx)";
+    }
+    elsif ($reg1)
+    {	$ret .= "($reg1)";	}
+
+  $ret;
+}
+sub ::QWP	{ &::DWP(@_);	}
+sub ::BP	{ &::DWP(@_);	}
+sub ::WP	{ &::DWP(@_);	}
+sub ::BC	{ @_;		}
+sub ::DWC	{ @_;		}
+
+sub ::file
+{   push(@out,".file\t\"$_[0].S\"\n.text\n");	}
+
+sub ::function_begin_B
+{ my $func=shift;
+  my $global=($func !~ /^_/);
+  my $begin="${::lbdecor}_${func}_begin";
+
+    &::LABEL($func,$global?"$begin":"$nmdecor$func");
+    $func=$nmdecor.$func;
+
+    push(@out,".globl\t$func\n")	if ($global);
+    if ($::coff)
+    {	push(@out,".def\t$func;\t.scl\t".(3-$global).";\t.type\t32;\t.endef\n"); }
+    elsif (($::aout and !$::pic) or $::macosx)
+    { }
+    else
+    {	push(@out,".type	$func,\@function\n"); }
+    push(@out,".align\t$align\n");
+    push(@out,"$func:\n");
+    push(@out,"$begin:\n")		if ($global);
+    $::stack=4;
+}
+
+sub ::function_end_B
+{ my $func=shift;
+    push(@out,".size\t$nmdecor$func,.-".&::LABEL($func)."\n") if ($::elf);
+    $::stack=0;
+    &::wipe_labels();
+}
+
+sub ::comment
+	{
+	if (!defined($com_start) or $::elf)
+		{	# Regarding $::elf above...
+			# GNU and SVR4 as'es use different comment delimiters,
+		push(@out,"\n");	# so we just skip ELF comments...
+		return;
+		}
+	foreach (@_)
+		{
+		if (/^\s*$/)
+			{ push(@out,"\n"); }
+		else
+			{ push(@out,"\t$com_start $_ $com_end\n"); }
+		}
+	}
+
+sub ::external_label
+{   foreach(@_) { &::LABEL($_,$nmdecor.$_); }   }
+
+sub ::public_label
+{   push(@out,".globl\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   }
+
+sub ::file_end
+{   if ($::macosx)
+    {	if (%non_lazy_ptr)
+    	{   push(@out,".section __IMPORT,__pointers,non_lazy_symbol_pointers\n");
+	    foreach $i (keys %non_lazy_ptr)
+	    {	push(@out,"$non_lazy_ptr{$i}:\n.indirect_symbol\t$i\n.long\t0\n");   }
+	}
+    }
+    if (0 && grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out) {
+	my $tmp=".comm\t${nmdecor}OPENSSL_ia32cap_P,16";
+	if ($::macosx)	{ push (@out,"$tmp,2\n"); }
+	elsif ($::elf)	{ push (@out,"$tmp,4\n"); }
+	else		{ push (@out,"$tmp\n"); }
+    }
+    push(@out,$initseg) if ($initseg);
+}
+
+sub ::data_byte	{   push(@out,".byte\t".join(',',@_)."\n");   }
+sub ::data_short{   push(@out,".value\t".join(',',@_)."\n");  }
+sub ::data_word {   push(@out,".long\t".join(',',@_)."\n");   }
+
+sub ::align
+{ my $val=$_[0];
+    if ($::aout)
+    {	$val=int(log($val)/log(2));
+	$val.=",0x90";
+    }
+    push(@out,".align\t$val\n");
+}
+
+sub ::picmeup
+{ my($dst,$sym,$base,$reflabel)=@_;
+
+    if (defined($base) && $sym eq "OPENSSL_ia32cap_P" && !$::macosx)
+    {	&::lea($dst,&::DWP("$sym-$reflabel",$base));	}
+    elsif (($::pic && ($::elf || $::aout)) || $::macosx)
+    {	if (!defined($base))
+	{   &::call(&::label("PIC_me_up"));
+	    &::set_label("PIC_me_up");
+	    &::blindpop($dst);
+	    $base=$dst;
+	    $reflabel=&::label("PIC_me_up");
+	}
+	if ($::macosx)
+	{   my $indirect=&::static_label("$nmdecor$sym\$non_lazy_ptr");
+	    &::mov($dst,&::DWP("$indirect-$reflabel",$base));
+	    $non_lazy_ptr{"$nmdecor$sym"}=$indirect;
+	}
+	else
+	{   &::lea($dst,&::DWP("_GLOBAL_OFFSET_TABLE_+[.-$reflabel]",
+			    $base));
+	    &::mov($dst,&::DWP("$sym\@GOT",$dst));
+	}
+    }
+    else
+    {	&::lea($dst,&::DWP($sym));	}
+}
+
+sub ::initseg
+{ my $f=$nmdecor.shift;
+
+    if ($::android)
+    {	$initseg.=<<___;
+.section	.init_array
+.align	4
+.long	$f
+___
+    }
+    elsif ($::elf)
+    {	$initseg.=<<___;
+.section	.init
+	call	$f
+___
+    }
+    elsif ($::coff)
+    {   $initseg.=<<___;	# applies to both Cygwin and Mingw
+.section	.ctors
+.long	$f
+___
+    }
+    elsif ($::macosx)
+    {	$initseg.=<<___;
+.mod_init_func
+.align 2
+.long   $f
+___
+    }
+    elsif ($::aout)
+    {	my $ctor="${nmdecor}_GLOBAL_\$I\$$f";
+	$initseg.=".text\n";
+	$initseg.=".type	$ctor,\@function\n" if ($::pic);
+	$initseg.=<<___;	# OpenBSD way...
+.globl	$ctor
+.align	2
+$ctor:
+	jmp	$f
+___
+    }
+}
+
+sub ::dataseg
+{   push(@out,".data\n");   }
+
+*::hidden = sub { push(@out,".hidden\t$nmdecor$_[0]\n"); } if ($::elf);
+
+1;
diff --git a/crypto/perlasm/x86masm.pl b/crypto/perlasm/x86masm.pl
new file mode 100644
index 0000000..1741342
--- /dev/null
+++ b/crypto/perlasm/x86masm.pl
@@ -0,0 +1,200 @@
+#!/usr/bin/env perl
+
+package x86masm;
+
+*out=\@::out;
+
+$::lbdecor="\$L";	# local label decoration
+$nmdecor="_";		# external name decoration
+
+$initseg="";
+$segment="";
+
+sub ::generic
+{ my ($opcode,@arg)=@_;
+
+    # fix hexadecimal constants
+    for (@arg) { s/(?<![\w\$\.])0x([0-9a-f]+)/0$1h/oi; }
+
+    if ($opcode =~ /lea/ && @arg[1] =~ s/.*PTR\s+(\(.*\))$/OFFSET $1/)	# no []
+    {	$opcode="mov";	}
+    elsif ($opcode !~ /movq/)
+    {	# fix xmm references
+	$arg[0] =~ s/\b[A-Z]+WORD\s+PTR/XMMWORD PTR/i if ($arg[1]=~/\bxmm[0-7]\b/i);
+	$arg[1] =~ s/\b[A-Z]+WORD\s+PTR/XMMWORD PTR/i if ($arg[0]=~/\bxmm[0-7]\b/i);
+    }
+
+    &::emit($opcode,@arg);
+  1;
+}
+#
+# opcodes not covered by ::generic above, mostly inconsistent namings...
+#
+sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
+sub ::call_ptr	{ &::emit("call",@_);	}
+sub ::jmp_ptr	{ &::emit("jmp",@_);	}
+sub ::lock	{ &::data_byte(0xf0);	}
+
+sub get_mem
+{ my($size,$addr,$reg1,$reg2,$idx)=@_;
+  my($post,$ret);
+
+    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }
+
+    $ret .= "$size PTR " if ($size ne "");
+
+    $addr =~ s/^\s+//;
+    # prepend global references with optional underscore
+    $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;
+    # put address arithmetic expression in parenthesis
+    $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/);
+
+    if (($addr ne "") && ($addr ne 0))
+    {	if ($addr !~ /^-/)	{ $ret .= "$addr";  }
+	else			{ $post=$addr;      }
+    }
+    $ret .= "[";
+
+    if ($reg2 ne "")
+    {	$idx!=0 or $idx=1;
+	$ret .= "$reg2*$idx";
+	$ret .= "+$reg1" if ($reg1 ne "");
+    }
+    else
+    {	$ret .= "$reg1";   }
+
+    $ret .= "$post]";
+    $ret =~ s/\+\]/]/; # in case $addr was the only argument
+    $ret =~ s/\[\s*\]//;
+
+  $ret;
+}
+sub ::BP	{ &get_mem("BYTE",@_);  }
+sub ::WP	{ &get_mem("WORD",@_);	}
+sub ::DWP	{ &get_mem("DWORD",@_); }
+sub ::QWP	{ &get_mem("QWORD",@_); }
+sub ::BC	{ "@_";  }
+sub ::DWC	{ "@_"; }
+
+sub ::file
+{ my $tmp=<<___;
+TITLE	$_[0].asm
+IF \@Version LT 800
+ECHO MASM version 8.00 or later is strongly recommended.
+ENDIF
+.486
+.MODEL	FLAT
+OPTION	DOTNAME
+IF \@Version LT 800
+.text\$	SEGMENT PAGE 'CODE'
+ELSE
+.text\$	SEGMENT ALIGN(64) 'CODE'
+ENDIF
+___
+    push(@out,$tmp);
+    $segment = ".text\$";
+}
+
+sub ::function_begin_B
+{ my $func=shift;
+  my $global=($func !~ /^_/);
+  my $begin="${::lbdecor}_${func}_begin";
+
+    &::LABEL($func,$global?"$begin":"$nmdecor$func");
+    $func="ALIGN\t16\n".$nmdecor.$func."\tPROC";
+
+    if ($global)    { $func.=" PUBLIC\n${begin}::\n"; }
+    else	    { $func.=" PRIVATE\n";            }
+    push(@out,$func);
+    $::stack=4;
+}
+sub ::function_end_B
+{ my $func=shift;
+
+    push(@out,"$nmdecor$func ENDP\n");
+    $::stack=0;
+    &::wipe_labels();
+}
+
+sub ::file_end
+{ my $xmmheader=<<___;
+.686
+.XMM
+IF \@Version LT 800
+XMMWORD STRUCT 16
+DQ	2 dup (?)
+XMMWORD	ENDS
+ENDIF
+___
+    if (grep {/\b[x]?mm[0-7]\b/i} @out) {
+	grep {s/\.[3-7]86/$xmmheader/} @out;
+    }
+
+    push(@out,"$segment	ENDS\n");
+
+    if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out)
+    {	my $comm=<<___;
+.bss	SEGMENT 'BSS'
+COMM	${nmdecor}OPENSSL_ia32cap_P:DWORD:4
+.bss	ENDS
+___
+	# comment out OPENSSL_ia32cap_P declarations
+	grep {s/(^EXTERN\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out;
+	push (@out,$comm);
+    }
+    push (@out,$initseg) if ($initseg);
+    push (@out,"END\n");
+}
+
+sub ::comment {   foreach (@_) { push(@out,"\t; $_\n"); }   }
+
+*::set_label_B = sub
+{ my $l=shift; push(@out,$l.($l=~/^\Q${::lbdecor}\E[0-9]{3}/?":\n":"::\n")); };
+
+sub ::external_label
+{   foreach(@_)
+    {	push(@out, "EXTERN\t".&::LABEL($_,$nmdecor.$_).":NEAR\n");   }
+}
+
+sub ::public_label
+{   push(@out,"PUBLIC\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   }
+
+sub ::data_byte
+{   push(@out,("DB\t").join(',',@_)."\n");	}
+
+sub ::data_short
+{   push(@out,("DW\t").join(',',@_)."\n");	}
+
+sub ::data_word
+{   push(@out,("DD\t").join(',',@_)."\n");	}
+
+sub ::align
+{   push(@out,"ALIGN\t$_[0]\n");	}
+
+sub ::picmeup
+{ my($dst,$sym)=@_;
+    &::lea($dst,&::DWP($sym));
+}
+
+sub ::initseg
+{ my $f=$nmdecor.shift;
+
+    $initseg.=<<___;
+.CRT\$XCU	SEGMENT DWORD PUBLIC 'DATA'
+EXTERN	$f:NEAR
+DD	$f
+.CRT\$XCU	ENDS
+___
+}
+
+sub ::dataseg
+{   push(@out,"$segment\tENDS\n_DATA\tSEGMENT\n"); $segment="_DATA";   }
+
+sub ::safeseh
+{ my $nm=shift;
+    push(@out,"IF \@Version GE 710\n");
+    push(@out,".SAFESEH	".&::LABEL($nm,$nmdecor.$nm)."\n");
+    push(@out,"ENDIF\n");
+}
+
+1;
diff --git a/crypto/perlasm/x86nasm.pl b/crypto/perlasm/x86nasm.pl
new file mode 100644
index 0000000..5d92f60
--- /dev/null
+++ b/crypto/perlasm/x86nasm.pl
@@ -0,0 +1,179 @@
+#!/usr/bin/env perl
+
+package x86nasm;
+
+*out=\@::out;
+
+$::lbdecor="L\$";		# local label decoration
+$nmdecor=$::netware?"":"_";	# external name decoration
+$drdecor=$::mwerks?".":"";	# directive decoration
+
+$initseg="";
+
+sub ::generic
+{ my $opcode=shift;
+  my $tmp;
+
+    if (!$::mwerks)
+    {   if    ($opcode =~ m/^j/o && $#_==0) # optimize jumps
+	{   $_[0] = "NEAR $_[0]";   	}
+	elsif ($opcode eq "lea" && $#_==1)  # wipe storage qualifier from lea
+	{   $_[1] =~ s/^[^\[]*\[/\[/o;	}
+	elsif ($opcode eq "clflush" && $#_==0)
+	{   $_[0] =~ s/^[^\[]*\[/\[/o;	}
+    }
+    &::emit($opcode,@_);
+  1;
+}
+#
+# opcodes not covered by ::generic above, mostly inconsistent namings...
+#
+sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
+sub ::call_ptr	{ &::emit("call",@_);	}
+sub ::jmp_ptr	{ &::emit("jmp",@_);	}
+
+sub get_mem
+{ my($size,$addr,$reg1,$reg2,$idx)=@_;
+  my($post,$ret);
+
+    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }
+
+    if ($size ne "")
+    {	$ret .= "$size";
+	$ret .= " PTR" if ($::mwerks);
+	$ret .= " ";
+    }
+    $ret .= "[";
+
+    $addr =~ s/^\s+//;
+    # prepend global references with optional underscore
+    $addr =~ s/^([^\+\-0-9][^\+\-]*)/::islabel($1) or "$nmdecor$1"/ige;
+    # put address arithmetic expression in parenthesis
+    $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/);
+
+    if (($addr ne "") && ($addr ne 0))
+    {	if ($addr !~ /^-/)	{ $ret .= "$addr+"; }
+	else			{ $post=$addr;      }
+    }
+
+    if ($reg2 ne "")
+    {	$idx!=0 or $idx=1;
+	$ret .= "$reg2*$idx";
+	$ret .= "+$reg1" if ($reg1 ne "");
+    }
+    else
+    {	$ret .= "$reg1";   }
+
+    $ret .= "$post]";
+    $ret =~ s/\+\]/]/; # in case $addr was the only argument
+
+  $ret;
+}
+sub ::BP	{ &get_mem("BYTE",@_);  }
+sub ::DWP	{ &get_mem("DWORD",@_); }
+sub ::WP	{ &get_mem("WORD",@_);	}
+sub ::QWP	{ &get_mem("",@_);      }
+sub ::BC	{ (($::mwerks)?"":"BYTE ")."@_";  }
+sub ::DWC	{ (($::mwerks)?"":"DWORD ")."@_"; }
+
+sub ::file
+{   if ($::mwerks)	{ push(@out,".section\t.text,64\n"); }
+    else
+    { my $tmp=<<___;
+%ifidn __OUTPUT_FORMAT__,obj
+section	code	use32 class=code align=64
+%elifidn __OUTPUT_FORMAT__,win32
+\$\@feat.00 equ 1
+section	.text	code align=64
+%else
+section	.text	code
+%endif
+___
+	push(@out,$tmp);
+    }
+}
+
+sub ::function_begin_B
+{ my $func=shift;
+  my $global=($func !~ /^_/);
+  my $begin="${::lbdecor}_${func}_begin";
+
+    $begin =~ s/^\@/./ if ($::mwerks);	# the torture never stops
+
+    &::LABEL($func,$global?"$begin":"$nmdecor$func");
+    $func=$nmdecor.$func;
+
+    push(@out,"${drdecor}global	$func\n")	if ($global);
+    push(@out,"${drdecor}align	16\n");
+    push(@out,"$func:\n");
+    push(@out,"$begin:\n")			if ($global);
+    $::stack=4;
+}
+
+sub ::function_end_B
+{   $::stack=0;
+    &::wipe_labels();
+}
+
+sub ::file_end
+{   if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out)
+    {	my $comm=<<___;
+${drdecor}segment	.bss
+${drdecor}common	${nmdecor}OPENSSL_ia32cap_P 16
+___
+	# comment out OPENSSL_ia32cap_P declarations
+	grep {s/(^extern\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out;
+	push (@out,$comm)
+    }
+    push (@out,$initseg) if ($initseg);		
+}
+
+sub ::comment {   foreach (@_) { push(@out,"\t; $_\n"); }   }
+
+sub ::external_label
+{   foreach(@_)
+    {	push(@out,"${drdecor}extern\t".&::LABEL($_,$nmdecor.$_)."\n");   }
+}
+
+sub ::public_label
+{   push(@out,"${drdecor}global\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");  }
+
+sub ::data_byte
+{   push(@out,(($::mwerks)?".byte\t":"db\t").join(',',@_)."\n");	}
+sub ::data_short
+{   push(@out,(($::mwerks)?".word\t":"dw\t").join(',',@_)."\n");	}
+sub ::data_word
+{   push(@out,(($::mwerks)?".long\t":"dd\t").join(',',@_)."\n");	}
+
+sub ::align
+{   push(@out,"${drdecor}align\t$_[0]\n");	}
+
+sub ::picmeup
+{ my($dst,$sym)=@_;
+    &::lea($dst,&::DWP($sym));
+}
+
+sub ::initseg
+{ my $f=$nmdecor.shift;
+    if ($::win32)
+    {	$initseg=<<___;
+segment	.CRT\$XCU data align=4
+extern	$f
+dd	$f
+___
+    }
+}
+
+sub ::dataseg
+{   if ($mwerks)	{ push(@out,".section\t.data,4\n");   }
+    else		{ push(@out,"section\t.data align=4\n"); }
+}
+
+sub ::safeseh
+{ my $nm=shift;
+    push(@out,"%if	__NASM_VERSION_ID__ >= 0x02030000\n");
+    push(@out,"safeseh	".&::LABEL($nm,$nmdecor.$nm)."\n");
+    push(@out,"%endif\n");
+}
+
+1;
diff --git a/crypto/pkcs8/CMakeLists.txt b/crypto/pkcs8/CMakeLists.txt
new file mode 100644
index 0000000..b89af85
--- /dev/null
+++ b/crypto/pkcs8/CMakeLists.txt
@@ -0,0 +1,13 @@
+include_directories(. .. ../../include)
+
+add_library(
+	pkcs8
+
+	OBJECT
+
+	pkcs8.c
+	p8_pkey.c
+	p5_pbe.c
+	p5_pbev2.c
+	pkcs8_error.c
+)
diff --git a/crypto/pkcs8/internal.h b/crypto/pkcs8/internal.h
new file mode 100644
index 0000000..44ca4f7
--- /dev/null
+++ b/crypto/pkcs8/internal.h
@@ -0,0 +1,74 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_PKCS8_INTERNAL_H
+#define OPENSSL_HEADER_PKCS8_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define PKCS5_DEFAULT_ITERATIONS 2048
+#define PKCS5_SALT_LEN 8
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_PKCS8_INTERNAL_H */
diff --git a/crypto/pkcs8/p5_pbe.c b/crypto/pkcs8/p5_pbe.c
new file mode 100644
index 0000000..9cdff4c
--- /dev/null
+++ b/crypto/pkcs8/p5_pbe.c
@@ -0,0 +1,148 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/pkcs8.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+/* PKCS#5 password based encryption structure */
+
+ASN1_SEQUENCE(PBEPARAM) = {
+	ASN1_SIMPLE(PBEPARAM, salt, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(PBEPARAM, iter, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(PBEPARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBEPARAM)
+
+
+/* Set an algorithm identifier for a PKCS#5 PBE algorithm */
+
+int PKCS5_pbe_set0_algor(X509_ALGOR *algor, int alg, int iter,
+				const unsigned char *salt, int saltlen)
+	{
+	PBEPARAM *pbe=NULL;
+	ASN1_STRING *pbe_str=NULL;
+	unsigned char *sstr;
+
+	pbe = PBEPARAM_new();
+	if (!pbe)
+		{
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe_set0_algor, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	if(iter <= 0)
+		iter = PKCS5_DEFAULT_ITERATIONS;
+	if (!ASN1_INTEGER_set(pbe->iter, iter))
+		{
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe_set0_algor, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	if (!saltlen)
+		saltlen = PKCS5_SALT_LEN;
+	if (!ASN1_STRING_set(pbe->salt, NULL, saltlen))
+		{
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe_set0_algor, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	sstr = ASN1_STRING_data(pbe->salt);
+	if (salt)
+		memcpy(sstr, salt, saltlen);
+	else if (RAND_pseudo_bytes(sstr, saltlen) < 0)
+		goto err;
+
+	if(!ASN1_item_pack(pbe, ASN1_ITEM_rptr(PBEPARAM), &pbe_str))
+		{
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe_set0_algor, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	PBEPARAM_free(pbe);
+	pbe = NULL;
+
+	if (X509_ALGOR_set0(algor, OBJ_nid2obj(alg), V_ASN1_SEQUENCE, pbe_str))
+		return 1;
+
+err:
+	if (pbe != NULL)
+		PBEPARAM_free(pbe);
+	if (pbe_str != NULL)
+		ASN1_STRING_free(pbe_str);
+	return 0;
+	}
+
+/* Return an algorithm identifier for a PKCS#5 PBE algorithm */
+
+X509_ALGOR *PKCS5_pbe_set(int alg, int iter,
+				const unsigned char *salt, int saltlen)
+	{
+	X509_ALGOR *ret;
+	ret = X509_ALGOR_new();
+	if (!ret)
+		{
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe_set, ERR_R_MALLOC_FAILURE);
+		return NULL;
+		}
+
+	if (PKCS5_pbe_set0_algor(ret, alg, iter, salt, saltlen)) 
+		return ret;
+
+	X509_ALGOR_free(ret);
+	return NULL;
+	}
diff --git a/crypto/pkcs8/p5_pbev2.c b/crypto/pkcs8/p5_pbev2.c
new file mode 100644
index 0000000..66746df
--- /dev/null
+++ b/crypto/pkcs8/p5_pbev2.c
@@ -0,0 +1,298 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999-2004.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <openssl/asn1t.h>
+#include <openssl/cipher.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/pkcs8.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+/* PKCS#5 v2.0 password based encryption structures */
+
+ASN1_SEQUENCE(PBE2PARAM) = {
+	ASN1_SIMPLE(PBE2PARAM, keyfunc, X509_ALGOR),
+	ASN1_SIMPLE(PBE2PARAM, encryption, X509_ALGOR)
+} ASN1_SEQUENCE_END(PBE2PARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBE2PARAM)
+
+ASN1_SEQUENCE(PBKDF2PARAM) = {
+	ASN1_SIMPLE(PBKDF2PARAM, salt, ASN1_ANY),
+	ASN1_SIMPLE(PBKDF2PARAM, iter, ASN1_INTEGER),
+	ASN1_OPT(PBKDF2PARAM, keylength, ASN1_INTEGER),
+	ASN1_OPT(PBKDF2PARAM, prf, X509_ALGOR)
+} ASN1_SEQUENCE_END(PBKDF2PARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBKDF2PARAM);
+
+static int ASN1_TYPE_set_octetstring(ASN1_TYPE *a, unsigned char *data, int len)
+	{
+	ASN1_STRING *os;
+
+	if ((os=M_ASN1_OCTET_STRING_new()) == NULL) return(0);
+	if (!M_ASN1_OCTET_STRING_set(os,data,len)) return(0);
+	ASN1_TYPE_set(a,V_ASN1_OCTET_STRING,os);
+	return(1);
+	}
+
+static int param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+	{
+	unsigned iv_len;
+
+	iv_len = EVP_CIPHER_CTX_iv_length(c);
+	return ASN1_TYPE_set_octetstring(type, c->oiv, iv_len);
+	}
+
+/* Return an algorithm identifier for a PKCS#5 v2.0 PBE algorithm:
+ * yes I know this is horrible!
+ *
+ * Extended version to allow application supplied PRF NID and IV. */
+
+X509_ALGOR *PKCS5_pbe2_set_iv(const EVP_CIPHER *cipher, int iter,
+				 unsigned char *salt, int saltlen,
+				 unsigned char *aiv, int prf_nid)
+{
+	X509_ALGOR *scheme = NULL, *kalg = NULL, *ret = NULL;
+	int alg_nid, keylen;
+	EVP_CIPHER_CTX ctx;
+	unsigned char iv[EVP_MAX_IV_LENGTH];
+	PBE2PARAM *pbe2 = NULL;
+	const ASN1_OBJECT *obj;
+
+	alg_nid = EVP_CIPHER_nid(cipher);
+	if(alg_nid == NID_undef) {
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe2_set_iv, PKCS8_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
+		goto err;
+	}
+	obj = OBJ_nid2obj(alg_nid);
+
+	if(!(pbe2 = PBE2PARAM_new())) goto merr;
+
+	/* Setup the AlgorithmIdentifier for the encryption scheme */
+	scheme = pbe2->encryption;
+
+	scheme->algorithm = (ASN1_OBJECT*) obj;
+	if(!(scheme->parameter = ASN1_TYPE_new())) goto merr;
+
+	/* Create random IV */
+	if (EVP_CIPHER_iv_length(cipher))
+		{
+		if (aiv)
+			memcpy(iv, aiv, EVP_CIPHER_iv_length(cipher));
+		else if (RAND_pseudo_bytes(iv, EVP_CIPHER_iv_length(cipher)) < 0)
+  			goto err;
+		}
+
+	EVP_CIPHER_CTX_init(&ctx);
+
+	/* Dummy cipherinit to just setup the IV, and PRF */
+	if (!EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, iv, 0))
+		goto err;
+	if(param_to_asn1(&ctx, scheme->parameter) < 0) {
+		OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe2_set_iv, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
+		EVP_CIPHER_CTX_cleanup(&ctx);
+		goto err;
+	}
+	/* If prf NID unspecified see if cipher has a preference.
+	 * An error is OK here: just means use default PRF.
+	 */
+	if ((prf_nid == -1) && 
+	EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_PBE_PRF_NID, 0, &prf_nid) <= 0)
+		{
+		ERR_clear_error();
+		prf_nid = NID_hmacWithSHA1;
+		}
+	EVP_CIPHER_CTX_cleanup(&ctx);
+
+	/* If its RC2 then we'd better setup the key length */
+
+	if(alg_nid == NID_rc2_cbc)
+		keylen = EVP_CIPHER_key_length(cipher);
+	else
+		keylen = -1;
+
+	/* Setup keyfunc */
+
+	X509_ALGOR_free(pbe2->keyfunc);
+
+	pbe2->keyfunc = PKCS5_pbkdf2_set(iter, salt, saltlen, prf_nid, keylen);
+
+	if (!pbe2->keyfunc)
+		goto merr;
+
+	/* Now set up top level AlgorithmIdentifier */
+
+	if(!(ret = X509_ALGOR_new())) goto merr;
+	if(!(ret->parameter = ASN1_TYPE_new())) goto merr;
+
+	ret->algorithm = (ASN1_OBJECT*) OBJ_nid2obj(NID_pbes2);
+
+	/* Encode PBE2PARAM into parameter */
+
+	if(!ASN1_item_pack(pbe2, ASN1_ITEM_rptr(PBE2PARAM),
+				 &ret->parameter->value.sequence)) goto merr;
+	ret->parameter->type = V_ASN1_SEQUENCE;
+
+	PBE2PARAM_free(pbe2);
+	pbe2 = NULL;
+
+	return ret;
+
+	merr:
+	OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbe2_set_iv, ERR_R_MALLOC_FAILURE);
+
+	err:
+	PBE2PARAM_free(pbe2);
+	/* Note 'scheme' is freed as part of pbe2 */
+	X509_ALGOR_free(kalg);
+	X509_ALGOR_free(ret);
+
+	return NULL;
+
+}
+
+X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter,
+				 unsigned char *salt, int saltlen)
+	{
+	return PKCS5_pbe2_set_iv(cipher, iter, salt, saltlen, NULL, -1);
+	}
+
+X509_ALGOR *PKCS5_pbkdf2_set(int iter, unsigned char *salt, int saltlen,
+				int prf_nid, int keylen)
+	{
+	X509_ALGOR *keyfunc = NULL;
+	PBKDF2PARAM *kdf = NULL;
+	ASN1_OCTET_STRING *osalt = NULL;
+
+	if(!(kdf = PBKDF2PARAM_new()))
+		goto merr;
+	if(!(osalt = M_ASN1_OCTET_STRING_new()))
+		goto merr;
+
+	kdf->salt->value.octet_string = osalt;
+	kdf->salt->type = V_ASN1_OCTET_STRING;
+
+	if (!saltlen)
+		saltlen = PKCS5_SALT_LEN;
+	if (!(osalt->data = OPENSSL_malloc (saltlen)))
+		goto merr;
+
+	osalt->length = saltlen;
+
+	if (salt)
+		memcpy (osalt->data, salt, saltlen);
+	else if (RAND_pseudo_bytes (osalt->data, saltlen) < 0)
+		goto merr;
+
+	if(iter <= 0)
+		iter = PKCS5_DEFAULT_ITERATIONS;
+
+	if(!ASN1_INTEGER_set(kdf->iter, iter))
+		goto merr;
+
+	/* If have a key len set it up */
+
+	if(keylen > 0) 
+		{
+		if(!(kdf->keylength = M_ASN1_INTEGER_new()))
+			goto merr;
+		if(!ASN1_INTEGER_set (kdf->keylength, keylen))
+			goto merr;
+		}
+
+	/* prf can stay NULL if we are using hmacWithSHA1 */
+	if (prf_nid > 0 && prf_nid != NID_hmacWithSHA1)
+		{
+		kdf->prf = X509_ALGOR_new();
+		if (!kdf->prf)
+			goto merr;
+		X509_ALGOR_set0(kdf->prf, OBJ_nid2obj(prf_nid),
+					V_ASN1_NULL, NULL);
+		}
+
+	/* Finally setup the keyfunc structure */
+
+	keyfunc = X509_ALGOR_new();
+	if (!keyfunc)
+		goto merr;
+
+	keyfunc->algorithm = (ASN1_OBJECT*) OBJ_nid2obj(NID_id_pbkdf2);
+
+	/* Encode PBKDF2PARAM into parameter of pbe2 */
+
+	if(!(keyfunc->parameter = ASN1_TYPE_new()))
+		goto merr;
+
+	if(!ASN1_item_pack(kdf, ASN1_ITEM_rptr(PBKDF2PARAM),
+			 &keyfunc->parameter->value.sequence))
+		goto merr;
+	keyfunc->parameter->type = V_ASN1_SEQUENCE;
+
+	PBKDF2PARAM_free(kdf);
+	return keyfunc;
+
+	merr:
+	OPENSSL_PUT_ERROR(PKCS8, PKCS5_pbkdf2_set, ERR_R_MALLOC_FAILURE);
+	PBKDF2PARAM_free(kdf);
+	X509_ALGOR_free(keyfunc);
+	return NULL;
+	}
+
diff --git a/crypto/pkcs8/p8_pkey.c b/crypto/pkcs8/p8_pkey.c
new file mode 100644
index 0000000..9095ffd
--- /dev/null
+++ b/crypto/pkcs8/p8_pkey.c
@@ -0,0 +1,84 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
+
+/* Minor tweak to operation: zero private key data */
+static int pkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+							void *exarg)
+{
+	/* Since the structure must still be valid use ASN1_OP_FREE_PRE */
+	if(operation == ASN1_OP_FREE_PRE) {
+		PKCS8_PRIV_KEY_INFO *key = (PKCS8_PRIV_KEY_INFO *)*pval;
+		if (key->pkey->value.octet_string)
+		OPENSSL_cleanse(key->pkey->value.octet_string->data,
+			key->pkey->value.octet_string->length);
+	}
+	return 1;
+}
+
+ASN1_SEQUENCE_cb(PKCS8_PRIV_KEY_INFO, pkey_cb) = {
+	ASN1_SIMPLE(PKCS8_PRIV_KEY_INFO, version, ASN1_INTEGER),
+	ASN1_SIMPLE(PKCS8_PRIV_KEY_INFO, pkeyalg, X509_ALGOR),
+	ASN1_SIMPLE(PKCS8_PRIV_KEY_INFO, pkey, ASN1_ANY),
+	ASN1_IMP_SET_OF_OPT(PKCS8_PRIV_KEY_INFO, attributes, X509_ATTRIBUTE, 0)
+} ASN1_SEQUENCE_END_cb(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS8_PRIV_KEY_INFO)
diff --git a/crypto/pkcs8/pkcs8.c b/crypto/pkcs8/pkcs8.c
new file mode 100644
index 0000000..310af83
--- /dev/null
+++ b/crypto/pkcs8/pkcs8.c
@@ -0,0 +1,580 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/pkcs8.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bn.h>
+#include <openssl/cipher.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
+
+#include "../evp/internal.h"
+
+
+#define PKCS12_KEY_ID 1
+#define PKCS12_IV_ID 2
+
+static int ascii_to_ucs2(const char *ascii, size_t ascii_len,
+                              uint8_t **out, size_t *out_len) {
+  uint8_t *unitmp;
+  size_t ulen, i;
+
+  ulen = ascii_len * 2 + 2;
+  if (ulen < ascii_len) {
+    return 0;
+  }
+  unitmp = OPENSSL_malloc(ulen);
+  if (unitmp == NULL) {
+    return 0;
+  }
+  for (i = 0; i < ulen - 2; i += 2) {
+    unitmp[i] = 0;
+    unitmp[i + 1] = ascii[i >> 1];
+  }
+
+  /* Make result double null terminated */
+  unitmp[ulen - 2] = 0;
+  unitmp[ulen - 1] = 0;
+  *out_len = ulen;
+  *out = unitmp;
+  return 1;
+}
+
+static int pkcs12_key_gen_uni(uint8_t *pass, size_t pass_len, uint8_t *salt,
+                              size_t salt_len, int id, int iterations,
+                              size_t out_len, uint8_t *out,
+                              const EVP_MD *md_type) {
+  uint8_t *B, *D, *I, *p, *Ai;
+  int Slen, Plen, Ilen, Ijlen;
+  int i, j, v;
+  size_t u;
+  int ret = 0;
+  BIGNUM *Ij, *Bpl1; /* These hold Ij and B + 1 */
+  EVP_MD_CTX ctx;
+
+  EVP_MD_CTX_init(&ctx);
+  v = EVP_MD_block_size(md_type);
+  u = EVP_MD_size(md_type);
+  D = OPENSSL_malloc(v);
+  Ai = OPENSSL_malloc(u);
+  B = OPENSSL_malloc(v + 1);
+  Slen = v * ((salt_len + v - 1) / v);
+  if (pass_len)
+    Plen = v * ((pass_len + v - 1) / v);
+  else
+    Plen = 0;
+  Ilen = Slen + Plen;
+  I = OPENSSL_malloc(Ilen);
+  Ij = BN_new();
+  Bpl1 = BN_new();
+  if (!D || !Ai || !B || !I || !Ij || !Bpl1)
+    goto err;
+  for (i = 0; i < v; i++)
+    D[i] = id;
+  p = I;
+  for (i = 0; i < Slen; i++)
+    *p++ = salt[i % salt_len];
+  for (i = 0; i < Plen; i++)
+    *p++ = pass[i % pass_len];
+  for (;;) {
+    if (!EVP_DigestInit_ex(&ctx, md_type, NULL) ||
+        !EVP_DigestUpdate(&ctx, D, v) ||
+        !EVP_DigestUpdate(&ctx, I, Ilen) ||
+        !EVP_DigestFinal_ex(&ctx, Ai, NULL)) {
+      goto err;
+    }
+    for (j = 1; j < iterations; j++) {
+      if (!EVP_DigestInit_ex(&ctx, md_type, NULL) ||
+          !EVP_DigestUpdate(&ctx, Ai, u) ||
+          !EVP_DigestFinal_ex(&ctx, Ai, NULL)) {
+        goto err;
+      }
+    }
+    memcpy(out, Ai, out_len < u ? out_len : u);
+    if (u >= out_len) {
+      ret = 1;
+      goto end;
+    }
+    out_len -= u;
+    out += u;
+    for (j = 0; j < v; j++)
+      B[j] = Ai[j % u];
+    /* Work out B + 1 first then can use B as tmp space */
+    if (!BN_bin2bn(B, v, Bpl1))
+      goto err;
+    if (!BN_add_word(Bpl1, 1))
+      goto err;
+    for (j = 0; j < Ilen; j += v) {
+      if (!BN_bin2bn(I + j, v, Ij))
+        goto err;
+      if (!BN_add(Ij, Ij, Bpl1))
+        goto err;
+      if (!BN_bn2bin(Ij, B))
+        goto err;
+      Ijlen = BN_num_bytes(Ij);
+      /* If more than 2^(v*8) - 1 cut off MSB */
+      if (Ijlen > v) {
+        if (!BN_bn2bin(Ij, B))
+          goto err;
+        memcpy(I + j, B + 1, v);
+        /* If less than v bytes pad with zeroes */
+      } else if (Ijlen < v) {
+        memset(I + j, 0, v - Ijlen);
+        if (!BN_bn2bin(Ij, I + j + v - Ijlen))
+          goto err;
+      } else if (!BN_bn2bin(Ij, I + j)) {
+        goto err;
+      }
+    }
+  }
+
+err:
+  OPENSSL_PUT_ERROR(PKCS8, pkcs12_key_gen_uni, ERR_R_MALLOC_FAILURE);
+
+end:
+  OPENSSL_free(Ai);
+  OPENSSL_free(B);
+  OPENSSL_free(D);
+  OPENSSL_free(I);
+  BN_free(Ij);
+  BN_free(Bpl1);
+  EVP_MD_CTX_cleanup(&ctx);
+
+  return ret;
+}
+
+static int pkcs12_key_gen_asc(const char *pass, size_t pass_len, uint8_t *salt,
+                              size_t salt_len, int id, int iterations,
+                              int out_len, uint8_t *out,
+                              const EVP_MD *md_type) {
+  int ret;
+  uint8_t *ucs2_pass = NULL;
+  size_t ucs2_pass_len = 0;
+
+  if (pass && !ascii_to_ucs2(pass, pass_len, &ucs2_pass, &ucs2_pass_len)) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_key_gen_asc, PKCS8_R_DECODE_ERROR);
+    return 0;
+  }
+  ret = pkcs12_key_gen_uni(ucs2_pass, ucs2_pass_len, salt, salt_len, id,
+                           iterations, out_len, out, md_type);
+
+  if (ucs2_pass) {
+    OPENSSL_cleanse(ucs2_pass, ucs2_pass_len);
+    OPENSSL_free(ucs2_pass);
+  }
+
+  return ret;
+}
+
+static int pkcs12_pbe_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass,
+                               size_t pass_len, ASN1_TYPE *param,
+                               const EVP_CIPHER *cipher, const EVP_MD *md,
+                               int is_encrypt) {
+  PBEPARAM *pbe;
+  int salt_len, iterations, ret;
+  uint8_t *salt;
+  const uint8_t *pbuf;
+  uint8_t key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
+
+  /* Extract useful info from parameter */
+  if (param == NULL || param->type != V_ASN1_SEQUENCE ||
+      param->value.sequence == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_pbe_keyivgen, PKCS8_R_DECODE_ERROR);
+    return 0;
+  }
+
+  pbuf = param->value.sequence->data;
+  pbe = d2i_PBEPARAM(NULL, &pbuf, param->value.sequence->length);
+  if (pbe == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_pbe_keyivgen, PKCS8_R_DECODE_ERROR);
+    return 0;
+  }
+
+  if (!pbe->iter) {
+    iterations = 1;
+  } else {
+    iterations = ASN1_INTEGER_get(pbe->iter);
+  }
+  salt = pbe->salt->data;
+  salt_len = pbe->salt->length;
+  if (!pkcs12_key_gen_asc(pass, pass_len, salt, salt_len, PKCS12_KEY_ID,
+                          iterations, EVP_CIPHER_key_length(cipher), key, md)) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_pbe_keyivgen, PKCS8_R_KEY_GEN_ERROR);
+    PBEPARAM_free(pbe);
+    return 0;
+  }
+  if (!pkcs12_key_gen_asc(pass, pass_len, salt, salt_len, PKCS12_IV_ID,
+                          iterations, EVP_CIPHER_iv_length(cipher), iv, md)) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_pbe_keyivgen, PKCS8_R_KEY_GEN_ERROR);
+    PBEPARAM_free(pbe);
+    return 0;
+  }
+  PBEPARAM_free(pbe);
+  ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, is_encrypt);
+  OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
+  OPENSSL_cleanse(iv, EVP_MAX_IV_LENGTH);
+  return ret;
+}
+
+typedef int (*keygen_func)(EVP_CIPHER_CTX *ctx, const char *pass,
+                           size_t pass_len, ASN1_TYPE *param,
+                           const EVP_CIPHER *cipher, const EVP_MD *md,
+                           int is_encrypt);
+
+struct pbe_suite {
+  int pbe_nid;
+  int cipher_nid;
+  int md_nid;
+  keygen_func keygen;
+};
+
+static const struct pbe_suite kBuiltinPBE[] = {
+    {
+     NID_pbe_WithSHA1And128BitRC4, NID_rc4, NID_sha1, pkcs12_pbe_keyivgen,
+    },
+    {
+     NID_pbe_WithSHA1And3_Key_TripleDES_CBC, NID_des_ede3_cbc, NID_sha1,
+     pkcs12_pbe_keyivgen,
+    },
+};
+
+static int pbe_cipher_init(ASN1_OBJECT *pbe_obj, const char *pass,
+                           size_t pass_len, ASN1_TYPE *param,
+                           EVP_CIPHER_CTX *ctx, int is_encrypt) {
+  const EVP_CIPHER *cipher;
+  const EVP_MD *md;
+  unsigned i;
+
+  const struct pbe_suite *suite = NULL;
+  const int pbe_nid = OBJ_obj2nid(pbe_obj);
+
+  for (i = 0; i < sizeof(kBuiltinPBE) / sizeof(struct pbe_suite); i++) {
+    suite = &kBuiltinPBE[i];
+    if (suite->pbe_nid == pbe_nid) {
+      break;
+    }
+  }
+
+  if (suite == NULL) {
+    char obj_str[80];
+    OPENSSL_PUT_ERROR(PKCS8, pbe_cipher_init, PKCS8_R_UNKNOWN_ALGORITHM);
+    if (!pbe_obj) {
+      strncpy(obj_str, "NULL", sizeof(obj_str));
+    } else {
+      i2t_ASN1_OBJECT(obj_str, sizeof(obj_str), pbe_obj);
+    }
+    ERR_add_error_data(2, "TYPE=", obj_str);
+    return 0;
+  }
+
+  if (suite->cipher_nid == -1) {
+    cipher = NULL;
+  } else {
+    cipher = EVP_get_cipherbynid(suite->cipher_nid);
+    if (!cipher) {
+      OPENSSL_PUT_ERROR(PKCS8, pbe_cipher_init, PKCS8_R_UNKNOWN_CIPHER);
+      return 0;
+    }
+  }
+
+  if (suite->md_nid == -1) {
+    md = NULL;
+  } else {
+    md = EVP_get_digestbynid(suite->md_nid);
+    if (!md) {
+      OPENSSL_PUT_ERROR(PKCS8, pbe_cipher_init, PKCS8_R_UNKNOWN_DIGEST);
+      return 0;
+    }
+  }
+
+  if (!suite->keygen(ctx, pass, pass_len, param, cipher, md, is_encrypt)) {
+    OPENSSL_PUT_ERROR(PKCS8, pbe_cipher_init, PKCS8_R_KEYGEN_FAILURE);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int pbe_crypt(const X509_ALGOR *algor, const char *pass, size_t pass_len,
+                     uint8_t *in, size_t in_len, uint8_t **out, size_t *out_len,
+                     int is_encrypt) {
+  uint8_t *buf;
+  int n, ret = 0;
+  EVP_CIPHER_CTX ctx;
+  unsigned block_size;
+
+  EVP_CIPHER_CTX_init(&ctx);
+
+  if (!pbe_cipher_init(algor->algorithm, pass, pass_len, algor->parameter, &ctx,
+                       is_encrypt)) {
+    OPENSSL_PUT_ERROR(PKCS8, pbe_crypt, PKCS8_R_UNKNOWN_CIPHER_ALGORITHM);
+    return 0;
+  }
+  block_size = EVP_CIPHER_CTX_block_size(&ctx);
+
+  if (in_len + block_size < in_len) {
+    OPENSSL_PUT_ERROR(PKCS8, pbe_crypt, PKCS8_R_TOO_LONG);
+    goto err;
+  }
+
+  buf = OPENSSL_malloc(in_len + block_size);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, pbe_crypt, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (!EVP_CipherUpdate(&ctx, buf, &n, in, in_len)) {
+    OPENSSL_free(buf);
+    OPENSSL_PUT_ERROR(PKCS8, pbe_crypt, ERR_R_EVP_LIB);
+    goto err;
+  }
+  *out_len = n;
+
+  if (!EVP_CipherFinal_ex(&ctx, buf + n, &n)) {
+    OPENSSL_free(buf);
+    OPENSSL_PUT_ERROR(PKCS8, pbe_crypt, ERR_R_EVP_LIB);
+    goto err;
+  }
+  *out_len += n;
+  *out = buf;
+  ret = 1;
+
+err:
+  EVP_CIPHER_CTX_cleanup(&ctx);
+  return ret;
+}
+
+static void *pkcs12_item_decrypt_d2i(X509_ALGOR *algor, const ASN1_ITEM *it,
+                                     const char *pass, size_t pass_len,
+                                     ASN1_OCTET_STRING *oct) {
+  uint8_t *out;
+  const uint8_t *p;
+  void *ret;
+  size_t out_len;
+
+  if (!pbe_crypt(algor, pass, pass_len, oct->data, oct->length, &out, &out_len,
+                 0 /* decrypt */)) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_item_decrypt_d2i, PKCS8_R_CRYPT_ERROR);
+    return NULL;
+  }
+  p = out;
+  ret = ASN1_item_d2i(NULL, &p, out_len, it);
+  OPENSSL_cleanse(out, out_len);
+  if (!ret) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_item_decrypt_d2i, PKCS8_R_DECODE_ERROR);
+  }
+  OPENSSL_free(out);
+  return ret;
+}
+
+PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
+                                   int pass_len) {
+  if (pass && pass_len == -1) {
+    pass_len = strlen(pass);
+  }
+  return pkcs12_item_decrypt_d2i(pkcs8->algor,
+                                 ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), pass,
+                                 pass_len, pkcs8->digest);
+}
+
+static ASN1_OCTET_STRING *pkcs12_item_i2d_encrypt(X509_ALGOR *algor,
+                                                  const ASN1_ITEM *it,
+                                                  const char *pass,
+                                                  size_t passlen, void *obj) {
+  ASN1_OCTET_STRING *oct;
+  uint8_t *in = NULL;
+  int in_len;
+  size_t crypt_len;
+
+  oct = M_ASN1_OCTET_STRING_new();
+  if (oct == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_item_i2d_encrypt, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  in_len = ASN1_item_i2d(obj, &in, it);
+  if (!in) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_item_i2d_encrypt, PKCS8_R_ENCODE_ERROR);
+    return NULL;
+  }
+  if (!pbe_crypt(algor, pass, passlen, in, in_len, &oct->data, &crypt_len,
+                 1 /* encrypt */)) {
+    OPENSSL_PUT_ERROR(PKCS8, pkcs12_item_i2d_encrypt, PKCS8_R_ENCRYPT_ERROR);
+    OPENSSL_free(in);
+    return NULL;
+  }
+  oct->length = crypt_len;
+  OPENSSL_cleanse(in, in_len);
+  OPENSSL_free(in);
+  return oct;
+}
+
+X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
+                        int pass_len, uint8_t *salt, size_t salt_len,
+                        int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
+  X509_SIG *pkcs8 = NULL;
+  X509_ALGOR *pbe;
+
+  if (pass && pass_len == -1) {
+    pass_len = strlen(pass);
+  }
+
+  pkcs8 = X509_SIG_new();
+  if (pkcs8 == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_encrypt, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (pbe_nid == -1) {
+    pbe = PKCS5_pbe2_set(cipher, iterations, salt, salt_len);
+  } else {
+    pbe = PKCS5_pbe_set(pbe_nid, iterations, salt, salt_len);
+  }
+
+  if (!pbe) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_encrypt, ERR_R_ASN1_LIB);
+    goto err;
+  }
+
+  X509_ALGOR_free(pkcs8->algor);
+  pkcs8->algor = pbe;
+  M_ASN1_OCTET_STRING_free(pkcs8->digest);
+  pkcs8->digest = pkcs12_item_i2d_encrypt(
+      pbe, ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), pass, pass_len, p8inf);
+  if (!pkcs8->digest) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_encrypt, PKCS8_R_ENCRYPT_ERROR);
+    goto err;
+  }
+
+  return pkcs8;
+
+err:
+  X509_SIG_free(pkcs8);
+  return NULL;
+}
+
+EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *p8) {
+  EVP_PKEY *pkey = NULL;
+  ASN1_OBJECT *algoid;
+  char obj_tmp[80];
+
+  if (!PKCS8_pkey_get0(&algoid, NULL, NULL, NULL, p8))
+    return NULL;
+
+  pkey = EVP_PKEY_new();
+  if (pkey == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, EVP_PKCS82PKEY, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  if (!EVP_PKEY_set_type(pkey, OBJ_obj2nid(algoid))) {
+    OPENSSL_PUT_ERROR(PKCS8, EVP_PKCS82PKEY,
+                      PKCS8_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM);
+    i2t_ASN1_OBJECT(obj_tmp, 80, algoid);
+    ERR_add_error_data(2, "TYPE=", obj_tmp);
+    goto error;
+  }
+
+  if (pkey->ameth->priv_decode) {
+    if (!pkey->ameth->priv_decode(pkey, p8)) {
+      OPENSSL_PUT_ERROR(PKCS8, EVP_PKCS82PKEY, PKCS8_R_PRIVATE_KEY_DECODE_ERROR);
+      goto error;
+    }
+  } else {
+    OPENSSL_PUT_ERROR(PKCS8, EVP_PKCS82PKEY, PKCS8_R_METHOD_NOT_SUPPORTED);
+    goto error;
+  }
+
+  return pkey;
+
+error:
+  EVP_PKEY_free(pkey);
+  return NULL;
+}
+
+PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey) {
+  PKCS8_PRIV_KEY_INFO *p8;
+
+  p8 = PKCS8_PRIV_KEY_INFO_new();
+  if (p8 == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, EVP_PKEY2PKCS8, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  p8->broken = PKCS8_OK;
+
+  if (pkey->ameth) {
+    if (pkey->ameth->priv_encode) {
+      if (!pkey->ameth->priv_encode(p8, pkey)) {
+        OPENSSL_PUT_ERROR(PKCS8, EVP_PKEY2PKCS8,
+                          PKCS8_R_PRIVATE_KEY_ENCODE_ERROR);
+        goto error;
+      }
+    } else {
+      OPENSSL_PUT_ERROR(PKCS8, EVP_PKEY2PKCS8, PKCS8_R_METHOD_NOT_SUPPORTED);
+      goto error;
+    }
+  } else {
+    OPENSSL_PUT_ERROR(PKCS8, EVP_PKEY2PKCS8,
+                      PKCS8_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM);
+    goto error;
+  }
+  return p8;
+
+error:
+  PKCS8_PRIV_KEY_INFO_free(p8);
+  return NULL;
+}
diff --git a/crypto/pkcs8/pkcs8.h b/crypto/pkcs8/pkcs8.h
new file mode 100644
index 0000000..9c4f60c
--- /dev/null
+++ b/crypto/pkcs8/pkcs8.h
@@ -0,0 +1,112 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#ifndef OPENSSL_HEADER_PKCS8_H
+#define OPENSSL_HEADER_PKCS8_H
+
+#include <openssl/base.h>
+
+#include <openssl/x509.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
+                        int pass_len, uint8_t *salt, size_t salt_len, int iterations,
+                        PKCS8_PRIV_KEY_INFO *p8inf);
+
+PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
+                                   int pass_len);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define PKCS8_F_PKCS8_encrypt 100
+#define PKCS8_F_EVP_PKEY2PKCS8 101
+#define PKCS8_F_EVP_PKCS82PKEY 102
+#define PKCS8_F_PKCS5_pbe_set0_algor 103
+#define PKCS8_F_pbe_crypt 104
+#define PKCS8_F_pkcs12_item_decrypt_d2i 105
+#define PKCS8_F_PKCS5_pbe_set 106
+#define PKCS8_F_pkcs12_key_gen_uni 107
+#define PKCS8_F_pkcs12_key_gen_asc 108
+#define PKCS8_F_pkcs12_pbe_keyivgen 109
+#define PKCS8_F_pbe_cipher_init 110
+#define PKCS8_F_pkcs12_item_i2d_encrypt 111
+#define PKCS8_F_PKCS5_pbe2_set_iv 112
+#define PKCS8_F_PKCS5_pbkdf2_set 113
+#define PKCS8_R_ERROR_SETTING_CIPHER_PARAMS 100
+#define PKCS8_R_PRIVATE_KEY_ENCODE_ERROR 101
+#define PKCS8_R_UNKNOWN_ALGORITHM 102
+#define PKCS8_R_UNKNOWN_CIPHER 103
+#define PKCS8_R_UNKNOWN_DIGEST 104
+#define PKCS8_R_ENCODE_ERROR 105
+#define PKCS8_R_DECODE_ERROR 106
+#define PKCS8_R_ENCRYPT_ERROR 107
+#define PKCS8_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM 108
+#define PKCS8_R_PRIVATE_KEY_DECODE_ERROR 109
+#define PKCS8_R_UNKNOWN_CIPHER_ALGORITHM 110
+#define PKCS8_R_KEYGEN_FAILURE 111
+#define PKCS8_R_TOO_LONG 112
+#define PKCS8_R_CRYPT_ERROR 113
+#define PKCS8_R_METHOD_NOT_SUPPORTED 114
+#define PKCS8_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER 115
+#define PKCS8_R_KEY_GEN_ERROR 116
+
+#endif  /* OPENSSL_HEADER_PKCS8_H */
diff --git a/crypto/pkcs8/pkcs8_error.c b/crypto/pkcs8/pkcs8_error.c
new file mode 100644
index 0000000..02129f0
--- /dev/null
+++ b/crypto/pkcs8/pkcs8_error.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "pkcs8.h"
+
+const ERR_STRING_DATA PKCS8_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_EVP_PKCS82PKEY, 0), "EVP_PKCS82PKEY"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_EVP_PKEY2PKCS8, 0), "EVP_PKEY2PKCS8"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_PKCS5_pbe2_set_iv, 0), "PKCS5_pbe2_set_iv"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_PKCS5_pbe_set, 0), "PKCS5_pbe_set"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_PKCS5_pbe_set0_algor, 0), "PKCS5_pbe_set0_algor"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_PKCS5_pbkdf2_set, 0), "PKCS5_pbkdf2_set"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_PKCS8_encrypt, 0), "PKCS8_encrypt"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pbe_cipher_init, 0), "pbe_cipher_init"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pbe_crypt, 0), "pbe_crypt"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pkcs12_item_decrypt_d2i, 0), "pkcs12_item_decrypt_d2i"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pkcs12_item_i2d_encrypt, 0), "pkcs12_item_i2d_encrypt"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pkcs12_key_gen_asc, 0), "pkcs12_key_gen_asc"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pkcs12_key_gen_uni, 0), "pkcs12_key_gen_uni"},
+  {ERR_PACK(ERR_LIB_PKCS8, PKCS8_F_pkcs12_pbe_keyivgen, 0), "pkcs12_pbe_keyivgen"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER), "CIPHER_HAS_NO_OBJECT_IDENTIFIER"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_CRYPT_ERROR), "CRYPT_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_DECODE_ERROR), "DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_ENCODE_ERROR), "ENCODE_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_ENCRYPT_ERROR), "ENCRYPT_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS), "ERROR_SETTING_CIPHER_PARAMS"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_KEYGEN_FAILURE), "KEYGEN_FAILURE"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_KEY_GEN_ERROR), "KEY_GEN_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_METHOD_NOT_SUPPORTED), "METHOD_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_PRIVATE_KEY_DECODE_ERROR), "PRIVATE_KEY_DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_PRIVATE_KEY_ENCODE_ERROR), "PRIVATE_KEY_ENCODE_ERROR"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_TOO_LONG), "TOO_LONG"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_UNKNOWN_ALGORITHM), "UNKNOWN_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_UNKNOWN_CIPHER), "UNKNOWN_CIPHER"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_UNKNOWN_CIPHER_ALGORITHM), "UNKNOWN_CIPHER_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_UNKNOWN_DIGEST), "UNKNOWN_DIGEST"},
+  {ERR_PACK(ERR_LIB_PKCS8, 0, PKCS8_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM), "UNSUPPORTED_PRIVATE_KEY_ALGORITHM"},
+  {0, NULL},
+};
diff --git a/crypto/rand/CMakeLists.txt b/crypto/rand/CMakeLists.txt
new file mode 100644
index 0000000..eb24613
--- /dev/null
+++ b/crypto/rand/CMakeLists.txt
@@ -0,0 +1,11 @@
+include_directories(. .. ../../include)
+
+add_library(
+	rand
+
+	OBJECT
+
+	rand.c
+	urandom.c
+	windows.c
+)
diff --git a/crypto/rand/rand.c b/crypto/rand/rand.c
new file mode 100644
index 0000000..6780b6c
--- /dev/null
+++ b/crypto/rand/rand.c
@@ -0,0 +1,28 @@
+/* Copyright (c) 2014, 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>
+
+
+int RAND_pseudo_bytes(uint8_t *buf, size_t len) {
+  return RAND_bytes(buf, len);
+}
+
+void RAND_seed(const void *buf, int num) {}
+
+void RAND_add(const void *buf, int num, double entropy) {}
+
+int RAND_poll(void) {
+  return 1;
+}
diff --git a/crypto/rand/rand.h b/crypto/rand/rand.h
new file mode 100644
index 0000000..62e1037
--- /dev/null
+++ b/crypto/rand/rand.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2014, 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_RAND_H
+#define OPENSSL_HEADER_RAND_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* RAND_bytes writes |len| bytes of random data to |buf|. It returns one on
+ * success and zero on otherwise. */
+int RAND_bytes(uint8_t *buf, size_t len);
+
+/* RAND_cleanup frees any resources used by the RNG. This is not safe if other
+ * threads might still be calling |RAND_bytes|. */
+void RAND_cleanup();
+
+
+/* Deprecated functions */
+
+/* RAND_pseudo_bytes is a wrapper around |RAND_bytes|. */
+int RAND_pseudo_bytes(uint8_t *buf, size_t len);
+
+/* RAND_seed does nothing. */
+void RAND_seed(const void *buf, int num);
+
+/* RAND_add does nothing. */
+void RAND_add(const void *buf, int num, double entropy);
+
+/* RAND_poll returns one. */
+int RAND_poll(void);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_RAND_H */
diff --git a/crypto/rand/urandom.c b/crypto/rand/urandom.c
new file mode 100644
index 0000000..44fe1d6
--- /dev/null
+++ b/crypto/rand/urandom.c
@@ -0,0 +1,241 @@
+/* Copyright (c) 2014, 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>
+
+#if !defined(OPENSSL_WINDOWS)
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/thread.h>
+#include <openssl/mem.h>
+
+
+/* This file implements a PRNG by reading from /dev/urandom, optionally with a
+ * fork-safe buffer.
+ *
+ * If buffering is enabled then it maintains a global, linked list of buffers.
+ * Threads which need random bytes grab a buffer from the list under a lock and
+ * copy out the bytes that they need. In the rare case that the buffer is
+ * empty, it's refilled from /dev/urandom outside of the lock.
+ *
+ * Large requests are always serviced from /dev/urandom directly.
+ *
+ * Each buffer contains the PID of the process that created it and it's tested
+ * against the current PID each time. Thus processes that fork will discard all
+ * the buffers filled by the parent process. There are two problems with this:
+ *
+ * 1) glibc maintains a cache of the current PID+PPID and, if this cache isn't
+ *    correctly invalidated, the getpid() will continue to believe that
+ *    it's the old process. Glibc depends on the glibc wrappers for fork,
+ *    vfork and clone being used in order to invalidate the getpid() cache.
+ *
+ * 2) If a process forks, dies and then its child forks, it's possible that
+ *    the third process will end up with the same PID as the original process.
+ *    If the second process never used any random values then this will mean
+ *    that the third process has stale, cached values and won't notice.
+ */
+
+/* BUF_SIZE is intended to be a 4K allocation with malloc overhead. struct
+ * rand_buffer also fits in this space and the remainder is entropy. */
+#define BUF_SIZE (4096 - 16)
+
+/* rand_buffer contains unused, random bytes. These structures form a linked
+ * list via the |next| pointer, which is NULL in the final element. */
+struct rand_buffer {
+  size_t used; /* used contains the number of bytes of |rand| that have
+                  been consumed. */
+  struct rand_buffer *next;
+  pid_t pid; /* pid contains the pid at the time that the buffer was
+                created so that data is not duplicated after a fork. */
+  pid_t ppid; /* ppid contains the parent pid in order to try and reduce
+                 the possibility of duplicated PID confusing the
+                 detection of a fork. */
+  uint8_t rand[];
+};
+
+/* rand_bytes_per_buf is the number of actual entropy bytes in a buffer. */
+static const size_t rand_bytes_per_buf = BUF_SIZE - sizeof(struct rand_buffer);
+
+/* list_head is the start of a global, linked-list of rand_buffer objects. It's
+ * protected by CRYPTO_LOCK_RAND. */
+static struct rand_buffer *list_head;
+
+/* urandom_fd is a file descriptor to /dev/urandom. It's protected by
+ * CRYPTO_LOCK_RAND. */
+static int urandom_fd = -2;
+
+/* urandom_buffering controls whether buffering is enabled (1) or not (0). This
+ * is protected by CRYPTO_LOCK_RAND. */
+static int urandom_buffering = 0;
+
+/* urandom_get_fd_locked returns a file descriptor to /dev/urandom. The caller
+ * of this function must hold CRYPTO_LOCK_RAND. */
+static int urandom_get_fd_locked() {
+  if (urandom_fd != -2)
+    return urandom_fd;
+
+  urandom_fd = open("/dev/urandom", O_RDONLY | O_NOCTTY);
+  return urandom_fd;
+}
+
+/* RAND_cleanup frees all buffers, closes any cached file descriptor
+ * and resets the global state. */
+void RAND_cleanup(void) {
+  struct rand_buffer *cur;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_RAND);
+  while ((cur = list_head)) {
+    list_head = cur->next;
+    OPENSSL_free(cur);
+  }
+  if (urandom_fd >= 0) {
+    close(urandom_fd);
+  }
+  urandom_fd = -2;
+  list_head = NULL;
+  CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+}
+
+/* read_full reads exactly |len| bytes from |fd| into |out| and returns 1. In
+ * the case of an error it returns 0. */
+static char read_full(int fd, uint8_t *out, size_t len) {
+  ssize_t r;
+
+  while (len > 0) {
+    do {
+      r = read(fd, out, len);
+    } while (r == -1 && errno == EINTR);
+
+    if (r <= 0) {
+      return 0;
+    }
+    out += r;
+    len -= r;
+  }
+
+  return 1;
+}
+
+/* urandom_rand_pseudo_bytes puts |num| random bytes into |out|. It returns
+ * one on success and zero otherwise. */
+int RAND_bytes(uint8_t *out, size_t requested) {
+  int fd;
+  struct rand_buffer *buf;
+  size_t todo;
+  pid_t pid, ppid;
+
+  if (requested == 0) {
+    return 1;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_RAND);
+  fd = urandom_get_fd_locked();
+
+  if (fd < 0) {
+    CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+    abort();
+    return 0;
+  }
+
+  /* If buffering is not enabled, or if the request is large, then the
+   * result comes directly from urandom. */
+  if (!urandom_buffering || requested > BUF_SIZE / 2) {
+    CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+    if (!read_full(fd, out, requested)) {
+      abort();
+      return 0;
+    }
+    return 1;
+  }
+
+  pid = getpid();
+  ppid = getppid();
+
+  for (;;) {
+    buf = list_head;
+    if (buf && buf->pid == pid && buf->ppid == ppid &&
+        rand_bytes_per_buf - buf->used >= requested) {
+      memcpy(out, &buf->rand[buf->used], requested);
+      buf->used += requested;
+      CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+      return 1;
+    }
+
+    /* If we don't immediately have enough entropy with the correct
+     * PID, remove the buffer from the list in order to gain
+     * exclusive access and unlock. */
+    if (buf) {
+      list_head = buf->next;
+    }
+    CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+
+    if (!buf) {
+      buf = (struct rand_buffer *)OPENSSL_malloc(BUF_SIZE);
+      /* The buffer doesn't contain any random bytes yet
+       * so we mark it as fully used so that it will be
+       * filled below. */
+      buf->used = rand_bytes_per_buf;
+      buf->next = NULL;
+      buf->pid = pid;
+      buf->ppid = ppid;
+    }
+
+    if (buf->pid == pid && buf->ppid == ppid) {
+      break;
+    }
+
+    /* We have forked and so cannot use these bytes as they
+     * may have been used in another process. */
+    OPENSSL_free(buf);
+    CRYPTO_w_lock(CRYPTO_LOCK_RAND);
+  }
+
+  while (requested > 0) {
+    todo = rand_bytes_per_buf - buf->used;
+    if (todo > requested) {
+      todo = requested;
+    }
+    memcpy(out, &buf->rand[buf->used], todo);
+    requested -= todo;
+    out += todo;
+    buf->used += todo;
+
+    if (buf->used < rand_bytes_per_buf) {
+      break;
+    }
+
+    if (!read_full(fd, buf->rand, rand_bytes_per_buf)) {
+      OPENSSL_free(buf);
+      abort();
+      return 0;
+    }
+
+    buf->used = 0;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_RAND);
+  assert(list_head != buf);
+  buf->next = list_head;
+  list_head = buf;
+  CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+  return 1;
+}
+
+#endif  /* !OPENSSL_WINDOWS */
diff --git a/crypto/rand/windows.c b/crypto/rand/windows.c
new file mode 100644
index 0000000..967dd9b
--- /dev/null
+++ b/crypto/rand/windows.c
@@ -0,0 +1,71 @@
+/* Copyright (c) 2014, 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 <openssl/thread.h>
+
+
+#if defined(OPENSSL_WINDOWS)
+
+#include <stdlib.h>
+#include <Windows.h>
+#include <Wincrypt.h>
+
+static char global_provider_init;
+static HCRYPTPROV global_provider;
+
+void RAND_cleanup(void) {
+  CRYPTO_w_lock(CRYPTO_LOCK_RAND);
+  CryptReleaseContext(global_provider, 0);
+  global_provider_init = 0;
+  CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+}
+
+int RAND_bytes(uint8_t *out, size_t requested) {
+  HCRYPTPROV provider = 0;
+  int ok;
+
+  CRYPTO_r_lock(CRYPTO_LOCK_RAND);
+  if (!global_provider_init) {
+    CRYPTO_r_unlock(CRYPTO_LOCK_RAND);
+    CRYPTO_w_lock(CRYPTO_LOCK_RAND);
+    if (!global_provider_init) {
+      if (CryptAcquireContext(&global_provider, NULL, NULL, PROV_RSA_FULL,
+                              CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+        global_provider_init = 1;
+      }
+    }
+    CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+    CRYPTO_r_lock(CRYPTO_LOCK_RAND);
+  }
+
+  ok = global_provider_init;
+  provider = global_provider;
+  CRYPTO_r_unlock(CRYPTO_LOCK_RAND);
+
+  if (!ok) {
+    abort();
+    return ok;
+  }
+
+  if (TRUE != CryptGenRandom(provider, requested, out)) {
+    abort();
+    return 0;
+  }
+
+  return 1;
+}
+
+#endif  /* OPENSSL_WINDOWS */
diff --git a/crypto/rc4/CMakeLists.txt b/crypto/rc4/CMakeLists.txt
new file mode 100644
index 0000000..0072832
--- /dev/null
+++ b/crypto/rc4/CMakeLists.txt
@@ -0,0 +1,29 @@
+include_directories(. .. ../../include)
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		RC4_ARCH_SOURCES
+
+		rc4-x86_64.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		RC4_ARCH_SOURCES
+
+		rc4-586.${ASM_EXT}
+	)
+endif()
+
+add_library(
+	rc4
+
+	OBJECT
+
+	rc4.c
+	${RC4_ARCH_SOURCES}
+)
+
+perlasm(rc4-x86_64.${ASM_EXT} asm/rc4-x86_64.pl)
+perlasm(rc4-586.${ASM_EXT} asm/rc4-586.pl)
diff --git a/crypto/rc4/asm/rc4-586.pl b/crypto/rc4/asm/rc4-586.pl
new file mode 100644
index 0000000..84f1a79
--- /dev/null
+++ b/crypto/rc4/asm/rc4-586.pl
@@ -0,0 +1,410 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# [Re]written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# At some point it became apparent that the original SSLeay RC4
+# assembler implementation performs suboptimally on latest IA-32
+# microarchitectures. After re-tuning performance has changed as
+# following:
+#
+# Pentium	-10%
+# Pentium III	+12%
+# AMD		+50%(*)
+# P4		+250%(**)
+#
+# (*)	This number is actually a trade-off:-) It's possible to
+#	achieve	+72%, but at the cost of -48% off PIII performance.
+#	In other words code performing further 13% faster on AMD
+#	would perform almost 2 times slower on Intel PIII...
+#	For reference! This code delivers ~80% of rc4-amd64.pl
+#	performance on the same Opteron machine.
+# (**)	This number requires compressed key schedule set up by
+#	RC4_set_key [see commentary below for further details].
+#
+#					<appro@fy.chalmers.se>
+
+# May 2011
+#
+# Optimize for Core2 and Westmere [and incidentally Opteron]. Current
+# performance in cycles per processed byte (less is better) and
+# improvement relative to previous version of this module is:
+#
+# Pentium	10.2			# original numbers
+# Pentium III	7.8(*)
+# Intel P4	7.5
+#
+# Opteron	6.1/+20%		# new MMX numbers
+# Core2		5.3/+67%(**)
+# Westmere	5.1/+94%(**)
+# Sandy Bridge	5.0/+8%
+# Atom		12.6/+6%
+#
+# (*)	PIII can actually deliver 6.6 cycles per byte with MMX code,
+#	but this specific code performs poorly on Core2. And vice
+#	versa, below MMX/SSE code delivering 5.8/7.1 on Core2 performs
+#	poorly on PIII, at 8.0/14.5:-( As PIII is not a "hot" CPU
+#	[anymore], I chose to discard PIII-specific code path and opt
+#	for original IALU-only code, which is why MMX/SSE code path
+#	is guarded by SSE2 bit (see below), not MMX/SSE.
+# (**)	Performance vs. block size on Core2 and Westmere had a maximum
+#	at ... 64 bytes block size. And it was quite a maximum, 40-60%
+#	in comparison to largest 8KB block size. Above improvement
+#	coefficients are for the largest block size.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"rc4-586.pl");
+
+$xx="eax";
+$yy="ebx";
+$tx="ecx";
+$ty="edx";
+$inp="esi";
+$out="ebp";
+$dat="edi";
+
+sub RC4_loop {
+  my $i=shift;
+  my $func = ($i==0)?*mov:*or;
+
+	&add	(&LB($yy),&LB($tx));
+	&mov	($ty,&DWP(0,$dat,$yy,4));
+	&mov	(&DWP(0,$dat,$yy,4),$tx);
+	&mov	(&DWP(0,$dat,$xx,4),$ty);
+	&add	($ty,$tx);
+	&inc	(&LB($xx));
+	&and	($ty,0xff);
+	&ror	($out,8)	if ($i!=0);
+	if ($i<3) {
+	  &mov	($tx,&DWP(0,$dat,$xx,4));
+	} else {
+	  &mov	($tx,&wparam(3));	# reload [re-biased] out
+	}
+	&$func	($out,&DWP(0,$dat,$ty,4));
+}
+
+if ($alt=0) {
+  # >20% faster on Atom and Sandy Bridge[!], 8% faster on Opteron,
+  # but ~40% slower on Core2 and Westmere... Attempt to add movz
+  # brings down Opteron by 25%, Atom and Sandy Bridge by 15%, yet
+  # on Core2 with movz it's almost 20% slower than below alternative
+  # code... Yes, it's a total mess...
+  my @XX=($xx,$out);
+  $RC4_loop_mmx = sub {		# SSE actually...
+    my $i=shift;
+    my $j=$i<=0?0:$i>>1;
+    my $mm=$i<=0?"mm0":"mm".($i&1);
+
+	&add	(&LB($yy),&LB($tx));
+	&lea	(@XX[1],&DWP(1,@XX[0]));
+	&pxor	("mm2","mm0")				if ($i==0);
+	&psllq	("mm1",8)				if ($i==0);
+	&and	(@XX[1],0xff);
+	&pxor	("mm0","mm0")				if ($i<=0);
+	&mov	($ty,&DWP(0,$dat,$yy,4));
+	&mov	(&DWP(0,$dat,$yy,4),$tx);
+	&pxor	("mm1","mm2")				if ($i==0);
+	&mov	(&DWP(0,$dat,$XX[0],4),$ty);
+	&add	(&LB($ty),&LB($tx));
+	&movd	(@XX[0],"mm7")				if ($i==0);
+	&mov	($tx,&DWP(0,$dat,@XX[1],4));
+	&pxor	("mm1","mm1")				if ($i==1);
+	&movq	("mm2",&QWP(0,$inp))			if ($i==1);
+	&movq	(&QWP(-8,(@XX[0],$inp)),"mm1")		if ($i==0);
+	&pinsrw	($mm,&DWP(0,$dat,$ty,4),$j);
+
+	push	(@XX,shift(@XX))			if ($i>=0);
+  }
+} else {
+  # Using pinsrw here improves performane on Intel CPUs by 2-3%, but
+  # brings down AMD by 7%...
+  $RC4_loop_mmx = sub {
+    my $i=shift;
+
+	&add	(&LB($yy),&LB($tx));
+	&psllq	("mm1",8*(($i-1)&7))			if (abs($i)!=1);
+	&mov	($ty,&DWP(0,$dat,$yy,4));
+	&mov	(&DWP(0,$dat,$yy,4),$tx);
+	&mov	(&DWP(0,$dat,$xx,4),$ty);
+	&inc	($xx);
+	&add	($ty,$tx);
+	&movz	($xx,&LB($xx));				# (*)
+	&movz	($ty,&LB($ty));				# (*)
+	&pxor	("mm2",$i==1?"mm0":"mm1")		if ($i>=0);
+	&movq	("mm0",&QWP(0,$inp))			if ($i<=0);
+	&movq	(&QWP(-8,($out,$inp)),"mm2")		if ($i==0);
+	&mov	($tx,&DWP(0,$dat,$xx,4));
+	&movd	($i>0?"mm1":"mm2",&DWP(0,$dat,$ty,4));
+
+	# (*)	This is the key to Core2 and Westmere performance.
+	#	Whithout movz out-of-order execution logic confuses
+	#	itself and fails to reorder loads and stores. Problem
+	#	appears to be fixed in Sandy Bridge...
+  }
+}
+
+&external_label("OPENSSL_ia32cap_P");
+
+# void RC4(RC4_KEY *key,size_t len,const unsigned char *inp,unsigned char *out);
+&function_begin("RC4");
+	&mov	($dat,&wparam(0));	# load key schedule pointer
+	&mov	($ty, &wparam(1));	# load len
+	&mov	($inp,&wparam(2));	# load inp
+	&mov	($out,&wparam(3));	# load out
+
+	&xor	($xx,$xx);		# avoid partial register stalls
+	&xor	($yy,$yy);
+
+	&cmp	($ty,0);		# safety net
+	&je	(&label("abort"));
+
+	&mov	(&LB($xx),&BP(0,$dat));	# load key->x
+	&mov	(&LB($yy),&BP(4,$dat));	# load key->y
+	&add	($dat,8);
+
+	&lea	($tx,&DWP(0,$inp,$ty));
+	&sub	($out,$inp);		# re-bias out
+	&mov	(&wparam(1),$tx);	# save input+len
+
+	&inc	(&LB($xx));
+
+	# detect compressed key schedule...
+	&cmp	(&DWP(256,$dat),-1);
+	&je	(&label("RC4_CHAR"));
+
+	&mov	($tx,&DWP(0,$dat,$xx,4));
+
+	&and	($ty,-4);		# how many 4-byte chunks?
+	&jz	(&label("loop1"));
+
+	&test	($ty,-8);
+	&mov	(&wparam(3),$out);	# $out as accumulator in these loops
+	&jz	(&label("go4loop4"));
+
+	&picmeup($out,"OPENSSL_ia32cap_P");
+	&bt	(&DWP(0,$out),26);	# check SSE2 bit [could have been MMX]
+	&jnc	(&label("go4loop4"));
+
+	&mov	($out,&wparam(3))	if (!$alt);
+	&movd	("mm7",&wparam(3))	if ($alt);
+	&and	($ty,-8);
+	&lea	($ty,&DWP(-8,$inp,$ty));
+	&mov	(&DWP(-4,$dat),$ty);	# save input+(len/8)*8-8
+
+	&$RC4_loop_mmx(-1);
+	&jmp(&label("loop_mmx_enter"));
+
+	&set_label("loop_mmx",16);
+		&$RC4_loop_mmx(0);
+	&set_label("loop_mmx_enter");
+		for 	($i=1;$i<8;$i++) { &$RC4_loop_mmx($i); }
+		&mov	($ty,$yy);
+		&xor	($yy,$yy);		# this is second key to Core2
+		&mov	(&LB($yy),&LB($ty));	# and Westmere performance...
+		&cmp	($inp,&DWP(-4,$dat));
+		&lea	($inp,&DWP(8,$inp));
+	&jb	(&label("loop_mmx"));
+
+    if ($alt) {
+	&movd	($out,"mm7");
+	&pxor	("mm2","mm0");
+	&psllq	("mm1",8);
+	&pxor	("mm1","mm2");
+	&movq	(&QWP(-8,$out,$inp),"mm1");
+    } else {
+	&psllq	("mm1",56);
+	&pxor	("mm2","mm1");
+	&movq	(&QWP(-8,$out,$inp),"mm2");
+    }
+	&emms	();
+
+	&cmp	($inp,&wparam(1));	# compare to input+len
+	&je	(&label("done"));
+	&jmp	(&label("loop1"));
+
+&set_label("go4loop4",16);
+	&lea	($ty,&DWP(-4,$inp,$ty));
+	&mov	(&wparam(2),$ty);	# save input+(len/4)*4-4
+
+	&set_label("loop4");
+		for ($i=0;$i<4;$i++) { RC4_loop($i); }
+		&ror	($out,8);
+		&xor	($out,&DWP(0,$inp));
+		&cmp	($inp,&wparam(2));	# compare to input+(len/4)*4-4
+		&mov	(&DWP(0,$tx,$inp),$out);# $tx holds re-biased out here
+		&lea	($inp,&DWP(4,$inp));
+		&mov	($tx,&DWP(0,$dat,$xx,4));
+	&jb	(&label("loop4"));
+
+	&cmp	($inp,&wparam(1));	# compare to input+len
+	&je	(&label("done"));
+	&mov	($out,&wparam(3));	# restore $out
+
+	&set_label("loop1",16);
+		&add	(&LB($yy),&LB($tx));
+		&mov	($ty,&DWP(0,$dat,$yy,4));
+		&mov	(&DWP(0,$dat,$yy,4),$tx);
+		&mov	(&DWP(0,$dat,$xx,4),$ty);
+		&add	($ty,$tx);
+		&inc	(&LB($xx));
+		&and	($ty,0xff);
+		&mov	($ty,&DWP(0,$dat,$ty,4));
+		&xor	(&LB($ty),&BP(0,$inp));
+		&lea	($inp,&DWP(1,$inp));
+		&mov	($tx,&DWP(0,$dat,$xx,4));
+		&cmp	($inp,&wparam(1));	# compare to input+len
+		&mov	(&BP(-1,$out,$inp),&LB($ty));
+	&jb	(&label("loop1"));
+
+	&jmp	(&label("done"));
+
+# this is essentially Intel P4 specific codepath...
+&set_label("RC4_CHAR",16);
+	&movz	($tx,&BP(0,$dat,$xx));
+	# strangely enough unrolled loop performs over 20% slower...
+	&set_label("cloop1");
+		&add	(&LB($yy),&LB($tx));
+		&movz	($ty,&BP(0,$dat,$yy));
+		&mov	(&BP(0,$dat,$yy),&LB($tx));
+		&mov	(&BP(0,$dat,$xx),&LB($ty));
+		&add	(&LB($ty),&LB($tx));
+		&movz	($ty,&BP(0,$dat,$ty));
+		&add	(&LB($xx),1);
+		&xor	(&LB($ty),&BP(0,$inp));
+		&lea	($inp,&DWP(1,$inp));
+		&movz	($tx,&BP(0,$dat,$xx));
+		&cmp	($inp,&wparam(1));
+		&mov	(&BP(-1,$out,$inp),&LB($ty));
+	&jb	(&label("cloop1"));
+
+&set_label("done");
+	&dec	(&LB($xx));
+	&mov	(&DWP(-4,$dat),$yy);		# save key->y
+	&mov	(&BP(-8,$dat),&LB($xx));	# save key->x
+&set_label("abort");
+&function_end("RC4");
+
+########################################################################
+
+$inp="esi";
+$out="edi";
+$idi="ebp";
+$ido="ecx";
+$idx="edx";
+
+# void RC4_set_key(RC4_KEY *key,int len,const unsigned char *data);
+&function_begin("RC4_set_key");
+	&mov	($out,&wparam(0));		# load key
+	&mov	($idi,&wparam(1));		# load len
+	&mov	($inp,&wparam(2));		# load data
+	&picmeup($idx,"OPENSSL_ia32cap_P");
+
+	&lea	($out,&DWP(2*4,$out));		# &key->data
+	&lea	($inp,&DWP(0,$inp,$idi));	# $inp to point at the end
+	&neg	($idi);
+	&xor	("eax","eax");
+	&mov	(&DWP(-4,$out),$idi);		# borrow key->y
+
+	&bt	(&DWP(0,$idx),20);		# check for bit#20
+	&jc	(&label("c1stloop"));
+
+&set_label("w1stloop",16);
+	&mov	(&DWP(0,$out,"eax",4),"eax");	# key->data[i]=i;
+	&add	(&LB("eax"),1);			# i++;
+	&jnc	(&label("w1stloop"));
+
+	&xor	($ido,$ido);
+	&xor	($idx,$idx);
+
+&set_label("w2ndloop",16);
+	&mov	("eax",&DWP(0,$out,$ido,4));
+	&add	(&LB($idx),&BP(0,$inp,$idi));
+	&add	(&LB($idx),&LB("eax"));
+	&add	($idi,1);
+	&mov	("ebx",&DWP(0,$out,$idx,4));
+	&jnz	(&label("wnowrap"));
+	  &mov	($idi,&DWP(-4,$out));
+	&set_label("wnowrap");
+	&mov	(&DWP(0,$out,$idx,4),"eax");
+	&mov	(&DWP(0,$out,$ido,4),"ebx");
+	&add	(&LB($ido),1);
+	&jnc	(&label("w2ndloop"));
+&jmp	(&label("exit"));
+
+# Unlike all other x86 [and x86_64] implementations, Intel P4 core
+# [including EM64T] was found to perform poorly with above "32-bit" key
+# schedule, a.k.a. RC4_INT. Performance improvement for IA-32 hand-coded
+# assembler turned out to be 3.5x if re-coded for compressed 8-bit one,
+# a.k.a. RC4_CHAR! It's however inappropriate to just switch to 8-bit
+# schedule for x86[_64], because non-P4 implementations suffer from
+# significant performance losses then, e.g. PIII exhibits >2x
+# deterioration, and so does Opteron. In order to assure optimal
+# all-round performance, we detect P4 at run-time and set up compressed
+# key schedule, which is recognized by RC4 procedure.
+
+&set_label("c1stloop",16);
+	&mov	(&BP(0,$out,"eax"),&LB("eax"));	# key->data[i]=i;
+	&add	(&LB("eax"),1);			# i++;
+	&jnc	(&label("c1stloop"));
+
+	&xor	($ido,$ido);
+	&xor	($idx,$idx);
+	&xor	("ebx","ebx");
+
+&set_label("c2ndloop",16);
+	&mov	(&LB("eax"),&BP(0,$out,$ido));
+	&add	(&LB($idx),&BP(0,$inp,$idi));
+	&add	(&LB($idx),&LB("eax"));
+	&add	($idi,1);
+	&mov	(&LB("ebx"),&BP(0,$out,$idx));
+	&jnz	(&label("cnowrap"));
+	  &mov	($idi,&DWP(-4,$out));
+	&set_label("cnowrap");
+	&mov	(&BP(0,$out,$idx),&LB("eax"));
+	&mov	(&BP(0,$out,$ido),&LB("ebx"));
+	&add	(&LB($ido),1);
+	&jnc	(&label("c2ndloop"));
+
+	&mov	(&DWP(256,$out),-1);		# mark schedule as compressed
+
+&set_label("exit");
+	&xor	("eax","eax");
+	&mov	(&DWP(-8,$out),"eax");		# key->x=0;
+	&mov	(&DWP(-4,$out),"eax");		# key->y=0;
+&function_end("RC4_set_key");
+
+# const char *RC4_options(void);
+&function_begin_B("RC4_options");
+	&call	(&label("pic_point"));
+&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("opts")."-".&label("pic_point"),"eax"));
+	&picmeup("edx","OPENSSL_ia32cap_P");
+	&mov	("edx",&DWP(0,"edx"));
+	&bt	("edx",20);
+	&jc	(&label("1xchar"));
+	&bt	("edx",26);
+	&jnc	(&label("ret"));
+	&add	("eax",25);
+	&ret	();
+&set_label("1xchar");
+	&add	("eax",12);
+&set_label("ret");
+	&ret	();
+&set_label("opts",64);
+&asciz	("rc4(4x,int)");
+&asciz	("rc4(1x,char)");
+&asciz	("rc4(8x,mmx)");
+&asciz	("RC4 for x86, CRYPTOGAMS by <appro\@openssl.org>");
+&align	(64);
+&function_end_B("RC4_options");
+
+&asm_finish();
+
diff --git a/crypto/rc4/asm/rc4-x86_64.pl b/crypto/rc4/asm/rc4-x86_64.pl
new file mode 100644
index 0000000..c7da8fa
--- /dev/null
+++ b/crypto/rc4/asm/rc4-x86_64.pl
@@ -0,0 +1,677 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# July 2004
+#
+# 2.22x RC4 tune-up:-) It should be noted though that my hand [as in
+# "hand-coded assembler"] doesn't stand for the whole improvement
+# coefficient. It turned out that eliminating RC4_CHAR from config
+# line results in ~40% improvement (yes, even for C implementation).
+# Presumably it has everything to do with AMD cache architecture and
+# RAW or whatever penalties. Once again! The module *requires* config
+# line *without* RC4_CHAR! As for coding "secret," I bet on partial
+# register arithmetics. For example instead of 'inc %r8; and $255,%r8'
+# I simply 'inc %r8b'. Even though optimization manual discourages
+# to operate on partial registers, it turned out to be the best bet.
+# At least for AMD... How IA32E would perform remains to be seen...
+
+# November 2004
+#
+# As was shown by Marc Bevand reordering of couple of load operations
+# results in even higher performance gain of 3.3x:-) At least on
+# Opteron... For reference, 1x in this case is RC4_CHAR C-code
+# compiled with gcc 3.3.2, which performs at ~54MBps per 1GHz clock.
+# Latter means that if you want to *estimate* what to expect from
+# *your* Opteron, then multiply 54 by 3.3 and clock frequency in GHz.
+
+# November 2004
+#
+# Intel P4 EM64T core was found to run the AMD64 code really slow...
+# The only way to achieve comparable performance on P4 was to keep
+# RC4_CHAR. Kind of ironic, huh? As it's apparently impossible to
+# compose blended code, which would perform even within 30% marginal
+# on either AMD and Intel platforms, I implement both cases. See
+# rc4_skey.c for further details...
+
+# April 2005
+#
+# P4 EM64T core appears to be "allergic" to 64-bit inc/dec. Replacing 
+# those with add/sub results in 50% performance improvement of folded
+# loop...
+
+# May 2005
+#
+# As was shown by Zou Nanhai loop unrolling can improve Intel EM64T
+# performance by >30% [unlike P4 32-bit case that is]. But this is
+# provided that loads are reordered even more aggressively! Both code
+# pathes, AMD64 and EM64T, reorder loads in essentially same manner
+# as my IA-64 implementation. On Opteron this resulted in modest 5%
+# improvement [I had to test it], while final Intel P4 performance
+# achieves respectful 432MBps on 2.8GHz processor now. For reference.
+# If executed on Xeon, current RC4_CHAR code-path is 2.7x faster than
+# RC4_INT code-path. While if executed on Opteron, it's only 25%
+# slower than the RC4_INT one [meaning that if CPU µ-arch detection
+# is not implemented, then this final RC4_CHAR code-path should be
+# preferred, as it provides better *all-round* performance].
+
+# March 2007
+#
+# Intel Core2 was observed to perform poorly on both code paths:-( It
+# apparently suffers from some kind of partial register stall, which
+# occurs in 64-bit mode only [as virtually identical 32-bit loop was
+# observed to outperform 64-bit one by almost 50%]. Adding two movzb to
+# cloop1 boosts its performance by 80%! This loop appears to be optimal
+# fit for Core2 and therefore the code was modified to skip cloop8 on
+# this CPU.
+
+# May 2010
+#
+# Intel Westmere was observed to perform suboptimally. Adding yet
+# another movzb to cloop1 improved performance by almost 50%! Core2
+# performance is improved too, but nominally...
+
+# May 2011
+#
+# The only code path that was not modified is P4-specific one. Non-P4
+# Intel code path optimization is heavily based on submission by Maxim
+# Perminov, Maxim Locktyukhin and Jim Guilford of Intel. I've used
+# some of the ideas even in attempt to optmize the original RC4_INT
+# code path... Current performance in cycles per processed byte (less
+# is better) and improvement coefficients relative to previous
+# version of this module are:
+#
+# Opteron	5.3/+0%(*)
+# P4		6.5
+# Core2		6.2/+15%(**)
+# Westmere	4.2/+60%
+# Sandy Bridge	4.2/+120%
+# Atom		9.3/+80%
+#
+# (*)	But corresponding loop has less instructions, which should have
+#	positive effect on upcoming Bulldozer, which has one less ALU.
+#	For reference, Intel code runs at 6.8 cpb rate on Opteron.
+# (**)	Note that Core2 result is ~15% lower than corresponding result
+#	for 32-bit code, meaning that it's possible to improve it,
+#	but more than likely at the cost of the others (see rc4-586.pl
+#	to get the idea)...
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$dat="%rdi";	    # arg1
+$len="%rsi";	    # arg2
+$inp="%rdx";	    # arg3
+$out="%rcx";	    # arg4
+
+{
+$code=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+.globl	RC4
+.type	RC4,\@function,4
+.align	16
+RC4:	or	$len,$len
+	jne	.Lentry
+	ret
+.Lentry:
+	push	%rbx
+	push	%r12
+	push	%r13
+.Lprologue:
+	mov	$len,%r11
+	mov	$inp,%r12
+	mov	$out,%r13
+___
+my $len="%r11";		# reassign input arguments
+my $inp="%r12";
+my $out="%r13";
+
+my @XX=("%r10","%rsi");
+my @TX=("%rax","%rbx");
+my $YY="%rcx";
+my $TY="%rdx";
+
+$code.=<<___;
+	xor	$XX[0],$XX[0]
+	xor	$YY,$YY
+
+	lea	8($dat),$dat
+	mov	-8($dat),$XX[0]#b
+	mov	-4($dat),$YY#b
+	cmpl	\$-1,256($dat)
+	je	.LRC4_CHAR
+	mov	OPENSSL_ia32cap_P(%rip),%r8d
+	xor	$TX[1],$TX[1]
+	inc	$XX[0]#b
+	sub	$XX[0],$TX[1]
+	sub	$inp,$out
+	movl	($dat,$XX[0],4),$TX[0]#d
+	test	\$-16,$len
+	jz	.Lloop1
+	bt	\$30,%r8d	# Intel CPU?
+	jc	.Lintel
+	and	\$7,$TX[1]
+	lea	1($XX[0]),$XX[1]
+	jz	.Loop8
+	sub	$TX[1],$len
+.Loop8_warmup:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($inp),$TY#b
+	movb	$TY#b,($out,$inp)
+	lea	1($inp),$inp
+	dec	$TX[1]
+	jnz	.Loop8_warmup
+
+	lea	1($XX[0]),$XX[1]
+	jmp	.Loop8
+.align	16
+.Loop8:
+___
+for ($i=0;$i<8;$i++) {
+$code.=<<___ if ($i==7);
+	add	\$8,$XX[1]#b
+___
+$code.=<<___;
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	`4*($i==7?-1:$i)`($dat,$XX[1],4),$TX[1]#d
+	ror	\$8,%r8				# ror is redundant when $i=0
+	movl	$TY#d,4*$i($dat,$XX[0],4)
+	add	$TX[0]#b,$TY#b
+	movb	($dat,$TY,4),%r8b
+___
+push(@TX,shift(@TX)); #push(@XX,shift(@XX));	# "rotate" registers
+}
+$code.=<<___;
+	add	\$8,$XX[0]#b
+	ror	\$8,%r8
+	sub	\$8,$len
+
+	xor	($inp),%r8
+	mov	%r8,($out,$inp)
+	lea	8($inp),$inp
+
+	test	\$-8,$len
+	jnz	.Loop8
+	cmp	\$0,$len
+	jne	.Lloop1
+	jmp	.Lexit
+
+.align	16
+.Lintel:
+	test	\$-32,$len
+	jz	.Lloop1
+	and	\$15,$TX[1]
+	jz	.Loop16_is_hot
+	sub	$TX[1],$len
+.Loop16_warmup:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($inp),$TY#b
+	movb	$TY#b,($out,$inp)
+	lea	1($inp),$inp
+	dec	$TX[1]
+	jnz	.Loop16_warmup
+
+	mov	$YY,$TX[1]
+	xor	$YY,$YY
+	mov	$TX[1]#b,$YY#b
+
+.Loop16_is_hot:
+	lea	($dat,$XX[0],4),$XX[1]
+___
+sub RC4_loop {
+  my $i=shift;
+  my $j=$i<0?0:$i;
+  my $xmm="%xmm".($j&1);
+
+    $code.="	add	\$16,$XX[0]#b\n"		if ($i==15);
+    $code.="	movdqu	($inp),%xmm2\n"			if ($i==15);
+    $code.="	add	$TX[0]#b,$YY#b\n"		if ($i<=0);
+    $code.="	movl	($dat,$YY,4),$TY#d\n";
+    $code.="	pxor	%xmm0,%xmm2\n"			if ($i==0);
+    $code.="	psllq	\$8,%xmm1\n"			if ($i==0);
+    $code.="	pxor	$xmm,$xmm\n"			if ($i<=1);
+    $code.="	movl	$TX[0]#d,($dat,$YY,4)\n";
+    $code.="	add	$TY#b,$TX[0]#b\n";
+    $code.="	movl	`4*($j+1)`($XX[1]),$TX[1]#d\n"	if ($i<15);
+    $code.="	movz	$TX[0]#b,$TX[0]#d\n";
+    $code.="	movl	$TY#d,4*$j($XX[1])\n";
+    $code.="	pxor	%xmm1,%xmm2\n"			if ($i==0);
+    $code.="	lea	($dat,$XX[0],4),$XX[1]\n"	if ($i==15);
+    $code.="	add	$TX[1]#b,$YY#b\n"		if ($i<15);
+    $code.="	pinsrw	\$`($j>>1)&7`,($dat,$TX[0],4),$xmm\n";
+    $code.="	movdqu	%xmm2,($out,$inp)\n"		if ($i==0);
+    $code.="	lea	16($inp),$inp\n"		if ($i==0);
+    $code.="	movl	($XX[1]),$TX[1]#d\n"		if ($i==15);
+}
+	RC4_loop(-1);
+$code.=<<___;
+	jmp	.Loop16_enter
+.align	16
+.Loop16:
+___
+
+for ($i=0;$i<16;$i++) {
+    $code.=".Loop16_enter:\n"		if ($i==1);
+	RC4_loop($i);
+	push(@TX,shift(@TX)); 		# "rotate" registers
+}
+$code.=<<___;
+	mov	$YY,$TX[1]
+	xor	$YY,$YY			# keyword to partial register
+	sub	\$16,$len
+	mov	$TX[1]#b,$YY#b
+	test	\$-16,$len
+	jnz	.Loop16
+
+	psllq	\$8,%xmm1
+	pxor	%xmm0,%xmm2
+	pxor	%xmm1,%xmm2
+	movdqu	%xmm2,($out,$inp)
+	lea	16($inp),$inp
+
+	cmp	\$0,$len
+	jne	.Lloop1
+	jmp	.Lexit
+
+.align	16
+.Lloop1:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($inp),$TY#b
+	movb	$TY#b,($out,$inp)
+	lea	1($inp),$inp
+	dec	$len
+	jnz	.Lloop1
+	jmp	.Lexit
+
+.align	16
+.LRC4_CHAR:
+	add	\$1,$XX[0]#b
+	movzb	($dat,$XX[0]),$TX[0]#d
+	test	\$-8,$len
+	jz	.Lcloop1
+	jmp	.Lcloop8
+.align	16
+.Lcloop8:
+	mov	($inp),%r8d
+	mov	4($inp),%r9d
+___
+# unroll 2x4-wise, because 64-bit rotates kill Intel P4...
+for ($i=0;$i<4;$i++) {
+$code.=<<___;
+	add	$TX[0]#b,$YY#b
+	lea	1($XX[0]),$XX[1]
+	movzb	($dat,$YY),$TY#d
+	movzb	$XX[1]#b,$XX[1]#d
+	movzb	($dat,$XX[1]),$TX[1]#d
+	movb	$TX[0]#b,($dat,$YY)
+	cmp	$XX[1],$YY
+	movb	$TY#b,($dat,$XX[0])
+	jne	.Lcmov$i			# Intel cmov is sloooow...
+	mov	$TX[0],$TX[1]
+.Lcmov$i:
+	add	$TX[0]#b,$TY#b
+	xor	($dat,$TY),%r8b
+	ror	\$8,%r8d
+___
+push(@TX,shift(@TX)); push(@XX,shift(@XX));	# "rotate" registers
+}
+for ($i=4;$i<8;$i++) {
+$code.=<<___;
+	add	$TX[0]#b,$YY#b
+	lea	1($XX[0]),$XX[1]
+	movzb	($dat,$YY),$TY#d
+	movzb	$XX[1]#b,$XX[1]#d
+	movzb	($dat,$XX[1]),$TX[1]#d
+	movb	$TX[0]#b,($dat,$YY)
+	cmp	$XX[1],$YY
+	movb	$TY#b,($dat,$XX[0])
+	jne	.Lcmov$i			# Intel cmov is sloooow...
+	mov	$TX[0],$TX[1]
+.Lcmov$i:
+	add	$TX[0]#b,$TY#b
+	xor	($dat,$TY),%r9b
+	ror	\$8,%r9d
+___
+push(@TX,shift(@TX)); push(@XX,shift(@XX));	# "rotate" registers
+}
+$code.=<<___;
+	lea	-8($len),$len
+	mov	%r8d,($out)
+	lea	8($inp),$inp
+	mov	%r9d,4($out)
+	lea	8($out),$out
+
+	test	\$-8,$len
+	jnz	.Lcloop8
+	cmp	\$0,$len
+	jne	.Lcloop1
+	jmp	.Lexit
+___
+$code.=<<___;
+.align	16
+.Lcloop1:
+	add	$TX[0]#b,$YY#b
+	movzb	$YY#b,$YY#d
+	movzb	($dat,$YY),$TY#d
+	movb	$TX[0]#b,($dat,$YY)
+	movb	$TY#b,($dat,$XX[0])
+	add	$TX[0]#b,$TY#b
+	add	\$1,$XX[0]#b
+	movzb	$TY#b,$TY#d
+	movzb	$XX[0]#b,$XX[0]#d
+	movzb	($dat,$TY),$TY#d
+	movzb	($dat,$XX[0]),$TX[0]#d
+	xorb	($inp),$TY#b
+	lea	1($inp),$inp
+	movb	$TY#b,($out)
+	lea	1($out),$out
+	sub	\$1,$len
+	jnz	.Lcloop1
+	jmp	.Lexit
+
+.align	16
+.Lexit:
+	sub	\$1,$XX[0]#b
+	movl	$XX[0]#d,-8($dat)
+	movl	$YY#d,-4($dat)
+
+	mov	(%rsp),%r13
+	mov	8(%rsp),%r12
+	mov	16(%rsp),%rbx
+	add	\$24,%rsp
+.Lepilogue:
+	ret
+.size	RC4,.-RC4
+___
+}
+
+$idx="%r8";
+$ido="%r9";
+
+$code.=<<___;
+.globl	RC4_set_key
+.type	RC4_set_key,\@function,3
+.align	16
+RC4_set_key:
+	lea	8($dat),$dat
+	lea	($inp,$len),$inp
+	neg	$len
+	mov	$len,%rcx
+	xor	%eax,%eax
+	xor	$ido,$ido
+	xor	%r10,%r10
+	xor	%r11,%r11
+
+	mov	OPENSSL_ia32cap_P(%rip),$idx#d
+	bt	\$20,$idx#d	# RC4_CHAR?
+	jc	.Lc1stloop
+	jmp	.Lw1stloop
+
+.align	16
+.Lw1stloop:
+	mov	%eax,($dat,%rax,4)
+	add	\$1,%al
+	jnc	.Lw1stloop
+
+	xor	$ido,$ido
+	xor	$idx,$idx
+.align	16
+.Lw2ndloop:
+	mov	($dat,$ido,4),%r10d
+	add	($inp,$len,1),$idx#b
+	add	%r10b,$idx#b
+	add	\$1,$len
+	mov	($dat,$idx,4),%r11d
+	cmovz	%rcx,$len
+	mov	%r10d,($dat,$idx,4)
+	mov	%r11d,($dat,$ido,4)
+	add	\$1,$ido#b
+	jnc	.Lw2ndloop
+	jmp	.Lexit_key
+
+.align	16
+.Lc1stloop:
+	mov	%al,($dat,%rax)
+	add	\$1,%al
+	jnc	.Lc1stloop
+
+	xor	$ido,$ido
+	xor	$idx,$idx
+.align	16
+.Lc2ndloop:
+	mov	($dat,$ido),%r10b
+	add	($inp,$len),$idx#b
+	add	%r10b,$idx#b
+	add	\$1,$len
+	mov	($dat,$idx),%r11b
+	jnz	.Lcnowrap
+	mov	%rcx,$len
+.Lcnowrap:
+	mov	%r10b,($dat,$idx)
+	mov	%r11b,($dat,$ido)
+	add	\$1,$ido#b
+	jnc	.Lc2ndloop
+	movl	\$-1,256($dat)
+
+.align	16
+.Lexit_key:
+	xor	%eax,%eax
+	mov	%eax,-8($dat)
+	mov	%eax,-4($dat)
+	ret
+.size	RC4_set_key,.-RC4_set_key
+
+.globl	RC4_options
+.type	RC4_options,\@abi-omnipotent
+.align	16
+RC4_options:
+	lea	.Lopts(%rip),%rax
+	mov	OPENSSL_ia32cap_P(%rip),%edx
+	bt	\$20,%edx
+	jc	.L8xchar
+	bt	\$30,%edx
+	jnc	.Ldone
+	add	\$25,%rax
+	ret
+.L8xchar:
+	add	\$12,%rax
+.Ldone:
+	ret
+.align	64
+.Lopts:
+.asciz	"rc4(8x,int)"
+.asciz	"rc4(8x,char)"
+.asciz	"rc4(16x,int)"
+.asciz	"RC4 for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+.align	64
+.size	RC4_options,.-RC4_options
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	stream_se_handler,\@abi-omnipotent
+.align	16
+stream_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	24(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%r12
+	mov	-24(%rax),%r13
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	stream_se_handler,.-stream_se_handler
+
+.type	key_se_handler,\@abi-omnipotent
+.align	16
+key_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	152($context),%rax	# pull context->Rsp
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+.Lcommon_seh_exit:
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	key_se_handler,.-key_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_RC4
+	.rva	.LSEH_end_RC4
+	.rva	.LSEH_info_RC4
+
+	.rva	.LSEH_begin_RC4_set_key
+	.rva	.LSEH_end_RC4_set_key
+	.rva	.LSEH_info_RC4_set_key
+
+.section	.xdata
+.align	8
+.LSEH_info_RC4:
+	.byte	9,0,0,0
+	.rva	stream_se_handler
+.LSEH_info_RC4_set_key:
+	.byte	9,0,0,0
+	.rva	key_se_handler
+___
+}
+
+sub reg_part {
+my ($reg,$conv)=@_;
+    if ($reg =~ /%r[0-9]+/)	{ $reg .= $conv; }
+    elsif ($conv eq "b")	{ $reg =~ s/%[er]([^x]+)x?/%$1l/;	}
+    elsif ($conv eq "w")	{ $reg =~ s/%[er](.+)/%$1/;		}
+    elsif ($conv eq "d")	{ $reg =~ s/%[er](.+)/%e$1/;		}
+    return $reg;
+}
+
+$code =~ s/(%[a-z0-9]+)#([bwd])/reg_part($1,$2)/gem;
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/rc4/rc4.c b/crypto/rc4/rc4.c
new file mode 100644
index 0000000..a93f96e
--- /dev/null
+++ b/crypto/rc4/rc4.c
@@ -0,0 +1,348 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/rc4.h>
+
+
+#if defined(OPENSSL_NO_ASM) || \
+    (!defined(OPENSSL_X86_64) && !defined(OPENSSL_X86))
+
+#define RC4_CHUNK unsigned long
+#define RC4_INT uint32_t
+
+
+/* RC4 as implemented from a posting from
+ * Newsgroups: sci.crypt
+ * From: sterndark@netcom.com (David Sterndark)
+ * Subject: RC4 Algorithm revealed.
+ * Message-ID: <sternCvKL4B.Hyy@netcom.com>
+ * Date: Wed, 14 Sep 1994 06:35:31 GMT */
+
+void RC4(RC4_KEY *key, size_t len, const uint8_t *in, uint8_t *out) {
+  register RC4_INT *d;
+  register RC4_INT x, y, tx, ty;
+  size_t i;
+
+  x = key->x;
+  y = key->y;
+  d = key->data;
+
+#if defined(RC4_CHUNK)
+/* The original reason for implementing this(*) was the fact that
+ * pre-21164a Alpha CPUs don't have byte load/store instructions
+ * and e.g. a byte store has to be done with 64-bit load, shift,
+ * and, or and finally 64-bit store. Peaking data and operating
+ * at natural word size made it possible to reduce amount of
+ * instructions as well as to perform early read-ahead without
+ * suffering from RAW (read-after-write) hazard. This resulted
+ * in ~40%(**) performance improvement on 21064 box with gcc.
+ * But it's not only Alpha users who win here:-) Thanks to the
+ * early-n-wide read-ahead this implementation also exhibits
+ * >40% speed-up on SPARC and 20-30% on 64-bit MIPS (depending
+ * on sizeof(RC4_INT)).
+ *
+ * (*)	"this" means code which recognizes the case when input
+ *	and output pointers appear to be aligned at natural CPU
+ *	word boundary
+ * (**)	i.e. according to 'apps/openssl speed rc4' benchmark,
+ *	crypto/rc4/rc4speed.c exhibits almost 70% speed-up...
+ *
+ * Cavets.
+ *
+ * - RC4_CHUNK="unsigned long long" should be a #1 choice for
+ *   UltraSPARC. Unfortunately gcc generates very slow code
+ *   (2.5-3 times slower than one generated by Sun's WorkShop
+ *   C) and therefore gcc (at least 2.95 and earlier) should
+ *   always be told that RC4_CHUNK="unsigned long".
+ *
+ *					<appro@fy.chalmers.se> */
+
+#define RC4_STEP                                                             \
+  (x = (x + 1) & 0xff, tx = d[x], y = (tx + y) & 0xff, ty = d[y], d[y] = tx, \
+   d[x] = ty, (RC4_CHUNK)d[(tx + ty) & 0xff])
+
+  if ((((size_t)in & (sizeof(RC4_CHUNK) - 1)) |
+       ((size_t)out & (sizeof(RC4_CHUNK) - 1))) == 0) {
+    RC4_CHUNK ichunk, otp;
+    const union {
+      long one;
+      char little;
+    } is_endian = {1};
+
+    /* I reckon we can afford to implement both endian
+     * cases and to decide which way to take at run-time
+     * because the machine code appears to be very compact
+     * and redundant 1-2KB is perfectly tolerable (i.e.
+     * in case the compiler fails to eliminate it:-). By
+     * suggestion from Terrel Larson <terr@terralogic.net>
+     * who also stands for the is_endian union:-)
+     *
+     * Special notes.
+     *
+     * - is_endian is declared automatic as doing otherwise
+     *   (declaring static) prevents gcc from eliminating
+     *   the redundant code;
+     * - compilers (those I've tried) don't seem to have
+     *   problems eliminating either the operators guarded
+     *   by "if (sizeof(RC4_CHUNK)==8)" or the condition
+     *   expressions themselves so I've got 'em to replace
+     *   corresponding #ifdefs from the previous version;
+     * - I chose to let the redundant switch cases when
+     *   sizeof(RC4_CHUNK)!=8 be (were also #ifdefed
+     *   before);
+     * - in case you wonder "&(sizeof(RC4_CHUNK)*8-1)" in
+     *   [LB]ESHFT guards against "shift is out of range"
+     *   warnings when sizeof(RC4_CHUNK)!=8
+     *
+     *			<appro@fy.chalmers.se> */
+    if (!is_endian.little) { /* BIG-ENDIAN CASE */
+#define BESHFT(c) \
+  (((sizeof(RC4_CHUNK) - (c) - 1) * 8) & (sizeof(RC4_CHUNK) * 8 - 1))
+      for (; len & (0 - sizeof(RC4_CHUNK)); len -= sizeof(RC4_CHUNK)) {
+        ichunk = *(RC4_CHUNK *)in;
+        otp = RC4_STEP << BESHFT(0);
+        otp |= RC4_STEP << BESHFT(1);
+        otp |= RC4_STEP << BESHFT(2);
+        otp |= RC4_STEP << BESHFT(3);
+        if (sizeof(RC4_CHUNK) == 8) {
+          otp |= RC4_STEP << BESHFT(4);
+          otp |= RC4_STEP << BESHFT(5);
+          otp |= RC4_STEP << BESHFT(6);
+          otp |= RC4_STEP << BESHFT(7);
+        }
+        *(RC4_CHUNK *)out = otp ^ ichunk;
+        in += sizeof(RC4_CHUNK);
+        out += sizeof(RC4_CHUNK);
+      }
+      if (len) {
+        RC4_CHUNK mask = (RC4_CHUNK) - 1, ochunk;
+
+        ichunk = *(RC4_CHUNK *)in;
+        ochunk = *(RC4_CHUNK *)out;
+        otp = 0;
+        i = BESHFT(0);
+        mask <<= (sizeof(RC4_CHUNK) - len) << 3;
+        switch (len & (sizeof(RC4_CHUNK) - 1)) {
+          case 7:
+            otp = RC4_STEP << i, i -= 8;
+          case 6:
+            otp |= RC4_STEP << i, i -= 8;
+          case 5:
+            otp |= RC4_STEP << i, i -= 8;
+          case 4:
+            otp |= RC4_STEP << i, i -= 8;
+          case 3:
+            otp |= RC4_STEP << i, i -= 8;
+          case 2:
+            otp |= RC4_STEP << i, i -= 8;
+          case 1:
+            otp |= RC4_STEP << i, i -= 8;
+        }
+        ochunk &= ~mask;
+        ochunk |= (otp ^ ichunk) & mask;
+        *(RC4_CHUNK *)out = ochunk;
+      }
+      key->x = x;
+      key->y = y;
+      return;
+    } else { /* LITTLE-ENDIAN CASE */
+#define LESHFT(c) (((c) * 8) & (sizeof(RC4_CHUNK) * 8 - 1))
+      for (; len & (0 - sizeof(RC4_CHUNK)); len -= sizeof(RC4_CHUNK)) {
+        ichunk = *(RC4_CHUNK *)in;
+        otp = RC4_STEP;
+        otp |= RC4_STEP << 8;
+        otp |= RC4_STEP << 16;
+        otp |= RC4_STEP << 24;
+        if (sizeof(RC4_CHUNK) == 8) {
+          otp |= RC4_STEP << LESHFT(4);
+          otp |= RC4_STEP << LESHFT(5);
+          otp |= RC4_STEP << LESHFT(6);
+          otp |= RC4_STEP << LESHFT(7);
+        }
+        *(RC4_CHUNK *)out = otp ^ ichunk;
+        in += sizeof(RC4_CHUNK);
+        out += sizeof(RC4_CHUNK);
+      }
+      if (len) {
+        RC4_CHUNK mask = (RC4_CHUNK) - 1, ochunk;
+
+        ichunk = *(RC4_CHUNK *)in;
+        ochunk = *(RC4_CHUNK *)out;
+        otp = 0;
+        i = 0;
+        mask >>= (sizeof(RC4_CHUNK) - len) << 3;
+        switch (len & (sizeof(RC4_CHUNK) - 1)) {
+          case 7:
+            otp = RC4_STEP, i += 8;
+          case 6:
+            otp |= RC4_STEP << i, i += 8;
+          case 5:
+            otp |= RC4_STEP << i, i += 8;
+          case 4:
+            otp |= RC4_STEP << i, i += 8;
+          case 3:
+            otp |= RC4_STEP << i, i += 8;
+          case 2:
+            otp |= RC4_STEP << i, i += 8;
+          case 1:
+            otp |= RC4_STEP << i, i += 8;
+        }
+        ochunk &= ~mask;
+        ochunk |= (otp ^ ichunk) & mask;
+        *(RC4_CHUNK *)out = ochunk;
+      }
+      key->x = x;
+      key->y = y;
+      return;
+    }
+  }
+#endif
+#define LOOP(in, out)   \
+  x = ((x + 1) & 0xff); \
+  tx = d[x];            \
+  y = (tx + y) & 0xff;  \
+  d[x] = ty = d[y];     \
+  d[y] = tx;            \
+  (out) = d[(tx + ty) & 0xff] ^ (in);
+
+#ifndef RC4_INDEX
+#define RC4_LOOP(a, b, i) LOOP(*((a)++), *((b)++))
+#else
+#define RC4_LOOP(a, b, i) LOOP(a[i], b[i])
+#endif
+
+  i = len >> 3;
+  if (i) {
+    for (;;) {
+      RC4_LOOP(in, out, 0);
+      RC4_LOOP(in, out, 1);
+      RC4_LOOP(in, out, 2);
+      RC4_LOOP(in, out, 3);
+      RC4_LOOP(in, out, 4);
+      RC4_LOOP(in, out, 5);
+      RC4_LOOP(in, out, 6);
+      RC4_LOOP(in, out, 7);
+#ifdef RC4_INDEX
+      in += 8;
+      out += 8;
+#endif
+      if (--i == 0)
+        break;
+    }
+  }
+  i = len & 0x07;
+  if (i) {
+    for (;;) {
+      RC4_LOOP(in, out, 0);
+      if (--i == 0)
+        break;
+      RC4_LOOP(in, out, 1);
+      if (--i == 0)
+        break;
+      RC4_LOOP(in, out, 2);
+      if (--i == 0)
+        break;
+      RC4_LOOP(in, out, 3);
+      if (--i == 0)
+        break;
+      RC4_LOOP(in, out, 4);
+      if (--i == 0)
+        break;
+      RC4_LOOP(in, out, 5);
+      if (--i == 0)
+        break;
+      RC4_LOOP(in, out, 6);
+      if (--i == 0)
+        break;
+    }
+  }
+  key->x = x;
+  key->y = y;
+}
+
+void RC4_set_key(RC4_KEY *rc4key, unsigned len, const uint8_t *key) {
+  register RC4_INT tmp;
+  register int id1, id2;
+  register RC4_INT *d;
+  unsigned int i;
+
+  d = &rc4key->data[0];
+  rc4key->x = 0;
+  rc4key->y = 0;
+  id1 = id2 = 0;
+
+#define SK_LOOP(d, n)                     \
+  {                                       \
+    tmp = d[(n)];                         \
+    id2 = (key[id1] + tmp + id2) & 0xff; \
+    if (++id1 == len)                     \
+      id1 = 0;                            \
+    d[(n)] = d[id2];                      \
+    d[id2] = tmp;                         \
+  }
+
+  for (i = 0; i < 256; i++) {
+    d[i] = i;
+  }
+  for (i = 0; i < 256; i += 4) {
+    SK_LOOP(d, i + 0);
+    SK_LOOP(d, i + 1);
+    SK_LOOP(d, i + 2);
+    SK_LOOP(d, i + 3);
+  }
+}
+
+#endif
diff --git a/crypto/rc4/rc4.h b/crypto/rc4/rc4.h
new file mode 100644
index 0000000..4b87e8b
--- /dev/null
+++ b/crypto/rc4/rc4.h
@@ -0,0 +1,88 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_RC4_H
+#define OPENSSL_HEADER_RC4_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+typedef struct rc4_key_st {
+  uint32_t x, y;
+  /* data is sometimes used as an array of 32-bit values and sometimes as 8-bit
+   * values, depending on the platform. */
+  uint32_t data[256];
+} RC4_KEY;
+
+
+/* RC4_set_key performs an RC4 key schedule and initialises |rc4key| with |len|
+ * bytes of key material from |key|. */
+void RC4_set_key(RC4_KEY *rc4key, unsigned len, const uint8_t *key);
+
+/* RC4 encrypts (or decrypts, it's the same with RC4) |len| bytes from |in| to
+ * |out|. */
+void RC4(RC4_KEY *key, size_t len, const uint8_t *in, uint8_t *out);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_RC4_H */
diff --git a/crypto/rsa/CMakeLists.txt b/crypto/rsa/CMakeLists.txt
new file mode 100644
index 0000000..82e6c50
--- /dev/null
+++ b/crypto/rsa/CMakeLists.txt
@@ -0,0 +1,22 @@
+include_directories(. .. ../../include)
+
+add_library(
+	rsa
+
+	OBJECT
+
+	rsa.c
+	rsa_impl.c
+	blinding.c
+	padding.c
+	rsa_asn1.c
+	rsa_error.c
+)
+
+add_executable(
+	rsa_test
+
+	rsa_test.c
+)
+
+target_link_libraries(rsa_test crypto)
diff --git a/crypto/rsa/blinding.c b/crypto/rsa/blinding.c
new file mode 100644
index 0000000..04f672a
--- /dev/null
+++ b/crypto/rsa/blinding.c
@@ -0,0 +1,468 @@
+/* ====================================================================
+ * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/rsa.h>
+
+#include <openssl/bn.h>
+#include <openssl/mem.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+
+#define BN_BLINDING_COUNTER 32
+
+struct bn_blinding_st {
+  BIGNUM *A;
+  BIGNUM *Ai;
+  BIGNUM *e;
+  BIGNUM *mod; /* just a reference */
+  CRYPTO_THREADID tid;
+  int counter;
+  unsigned long flags;
+  BN_MONT_CTX *m_ctx;
+  int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                    const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+};
+
+BN_BLINDING *BN_BLINDING_new(const BIGNUM *A, const BIGNUM *Ai, BIGNUM *mod) {
+  BN_BLINDING *ret = NULL;
+
+  ret = (BN_BLINDING*) OPENSSL_malloc(sizeof(BN_BLINDING));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(RSA, BN_BLINDING_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  memset(ret, 0, sizeof(BN_BLINDING));
+  if (A != NULL) {
+    ret->A = BN_dup(A);
+    if (ret->A == NULL) {
+      goto err;
+    }
+  }
+  if (Ai != NULL) {
+    ret->Ai = BN_dup(Ai);
+    if (ret->Ai == NULL) {
+      goto err;
+    }
+  }
+
+  /* save a copy of mod in the BN_BLINDING structure */
+  ret->mod = BN_dup(mod);
+  if (ret->mod == NULL) {
+    goto err;
+  }
+  if (BN_get_flags(mod, BN_FLG_CONSTTIME) != 0) {
+    BN_set_flags(ret->mod, BN_FLG_CONSTTIME);
+  }
+
+  /* Set the counter to the special value -1
+   * to indicate that this is never-used fresh blinding
+   * that does not need updating before first use. */
+  ret->counter = -1;
+  CRYPTO_THREADID_current(&ret->tid);
+  return ret;
+
+err:
+  if (ret != NULL) {
+    BN_BLINDING_free(ret);
+  }
+  return NULL;
+}
+
+void BN_BLINDING_free(BN_BLINDING *r) {
+  if (r == NULL) {
+    return;
+  }
+
+  if (r->A != NULL)
+    BN_free(r->A);
+  if (r->Ai != NULL)
+    BN_free(r->Ai);
+  if (r->e != NULL)
+    BN_free(r->e);
+  if (r->mod != NULL)
+    BN_free(r->mod);
+  OPENSSL_free(r);
+}
+
+int BN_BLINDING_update(BN_BLINDING *b, BN_CTX *ctx) {
+  int ret = 0;
+
+  if (b->A == NULL || b->Ai == NULL) {
+    OPENSSL_PUT_ERROR(RSA, BN_BLINDING_update, RSA_R_BN_NOT_INITIALIZED);
+    goto err;
+  }
+
+  if (b->counter == -1) {
+    b->counter = 0;
+  }
+
+  if (++b->counter == BN_BLINDING_COUNTER && b->e != NULL &&
+      !(b->flags & BN_BLINDING_NO_RECREATE)) {
+    /* re-create blinding parameters */
+    if (!BN_BLINDING_create_param(b, NULL, NULL, ctx, NULL, NULL)) {
+      goto err;
+    }
+  } else if (!(b->flags & BN_BLINDING_NO_UPDATE)) {
+    if (!BN_mod_mul(b->A, b->A, b->A, b->mod, ctx)) {
+      goto err;
+    }
+    if (!BN_mod_mul(b->Ai, b->Ai, b->Ai, b->mod, ctx)) {
+      goto err;
+    }
+  }
+
+  ret = 1;
+
+err:
+  if (b->counter == BN_BLINDING_COUNTER) {
+    b->counter = 0;
+  }
+  return ret;
+}
+
+int BN_BLINDING_convert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx) {
+  return BN_BLINDING_convert_ex(n, NULL, b, ctx);
+}
+
+int BN_BLINDING_convert_ex(BIGNUM *n, BIGNUM *r, BN_BLINDING *b, BN_CTX *ctx) {
+  int ret = 1;
+
+  if (b->A == NULL || b->Ai == NULL) {
+    OPENSSL_PUT_ERROR(RSA, BN_BLINDING_convert_ex, RSA_R_BN_NOT_INITIALIZED);
+    return 0;
+  }
+
+  if (b->counter == -1) {
+    /* Fresh blinding, doesn't need updating. */
+    b->counter = 0;
+  } else if (!BN_BLINDING_update(b, ctx)) {
+    return 0;
+  }
+
+  if (r != NULL) {
+    if (!BN_copy(r, b->Ai)) {
+      ret = 0;
+    }
+  }
+
+  if (!BN_mod_mul(n, n, b->A, b->mod, ctx)) {
+    ret = 0;
+  }
+
+  return ret;
+}
+
+int BN_BLINDING_invert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx) {
+  return BN_BLINDING_invert_ex(n, NULL, b, ctx);
+}
+
+int BN_BLINDING_invert_ex(BIGNUM *n, const BIGNUM *r, BN_BLINDING *b,
+                          BN_CTX *ctx) {
+  int ret;
+
+  if (r != NULL) {
+    ret = BN_mod_mul(n, n, r, b->mod, ctx);
+  } else {
+    if (b->Ai == NULL) {
+      OPENSSL_PUT_ERROR(RSA, BN_BLINDING_invert_ex, RSA_R_BN_NOT_INITIALIZED);
+      return 0;
+    }
+    ret = BN_mod_mul(n, n, b->Ai, b->mod, ctx);
+  }
+
+  return ret;
+}
+
+CRYPTO_THREADID *BN_BLINDING_thread_id(BN_BLINDING *b) { return &b->tid; }
+
+unsigned long BN_BLINDING_get_flags(const BN_BLINDING *b) { return b->flags; }
+
+void BN_BLINDING_set_flags(BN_BLINDING *b, unsigned long flags) {
+  b->flags = flags;
+}
+
+BN_BLINDING *BN_BLINDING_create_param(
+    BN_BLINDING *b, const BIGNUM *e, BIGNUM *m, BN_CTX *ctx,
+    int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                      const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx),
+    BN_MONT_CTX *m_ctx) {
+  int retry_counter = 32;
+  BN_BLINDING *ret = NULL;
+
+  if (b == NULL) {
+    ret = BN_BLINDING_new(NULL, NULL, m);
+  } else {
+    ret = b;
+  }
+
+  if (ret == NULL) {
+    goto err;
+  }
+
+  if (ret->A == NULL && (ret->A = BN_new()) == NULL) {
+    goto err;
+  }
+  if (ret->Ai == NULL && (ret->Ai = BN_new()) == NULL) {
+    goto err;
+  }
+
+  if (e != NULL) {
+    if (ret->e != NULL) {
+      BN_free(ret->e);
+    }
+    ret->e = BN_dup(e);
+  }
+  if (ret->e == NULL) {
+    goto err;
+  }
+
+  if (bn_mod_exp != NULL) {
+    ret->bn_mod_exp = bn_mod_exp;
+  }
+  if (m_ctx != NULL) {
+    ret->m_ctx = m_ctx;
+  }
+
+  do {
+    if (!BN_rand_range(ret->A, ret->mod)) {
+      goto err;
+    }
+    if (BN_mod_inverse(ret->Ai, ret->A, ret->mod, ctx) == NULL) {
+      /* this should almost never happen for good RSA keys */
+      uint32_t error = ERR_peek_last_error();
+      if (ERR_GET_REASON(error) == BN_R_NO_INVERSE) {
+        if (retry_counter-- == 0) {
+          OPENSSL_PUT_ERROR(RSA, BN_BLINDING_create_param,
+                            RSA_R_TOO_MANY_ITERATIONS);
+          goto err;
+        }
+        ERR_clear_error();
+      } else {
+        goto err;
+      }
+    } else {
+      break;
+    }
+  } while (1);
+
+  if (ret->bn_mod_exp != NULL && ret->m_ctx != NULL) {
+    if (!ret->bn_mod_exp(ret->A, ret->A, ret->e, ret->mod, ctx, ret->m_ctx)) {
+      goto err;
+    }
+  } else {
+    if (!BN_mod_exp(ret->A, ret->A, ret->e, ret->mod, ctx)) {
+      goto err;
+    }
+  }
+
+  return ret;
+
+err:
+  if (b == NULL && ret != NULL) {
+    BN_BLINDING_free(ret);
+    ret = NULL;
+  }
+
+  return ret;
+}
+
+static BIGNUM *rsa_get_public_exp(const BIGNUM *d, const BIGNUM *p,
+                                  const BIGNUM *q, BN_CTX *ctx) {
+  BIGNUM *ret = NULL, *r0, *r1, *r2;
+
+  if (d == NULL || p == NULL || q == NULL) {
+    return NULL;
+  }
+
+  BN_CTX_start(ctx);
+  r0 = BN_CTX_get(ctx);
+  r1 = BN_CTX_get(ctx);
+  r2 = BN_CTX_get(ctx);
+  if (r2 == NULL) {
+    goto err;
+  }
+
+  if (!BN_sub(r1, p, BN_value_one())) {
+    goto err;
+  }
+  if (!BN_sub(r2, q, BN_value_one())) {
+    goto err;
+  }
+  if (!BN_mul(r0, r1, r2, ctx)) {
+    goto err;
+  }
+
+  ret = BN_mod_inverse(NULL, d, r0, ctx);
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+BN_BLINDING *rsa_setup_blinding(RSA *rsa, BN_CTX *in_ctx) {
+  BIGNUM local_n;
+  BIGNUM *e, *n;
+  BN_CTX *ctx;
+  BN_BLINDING *ret = NULL;
+
+  if (in_ctx == NULL) {
+    ctx = BN_CTX_new();
+    if (ctx == NULL) {
+      return 0;
+    }
+  } else {
+    ctx = in_ctx;
+  }
+
+  BN_CTX_start(ctx);
+  e = BN_CTX_get(ctx);
+  if (e == NULL) {
+    OPENSSL_PUT_ERROR(RSA, rsa_setup_blinding, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (rsa->e == NULL) {
+    e = rsa_get_public_exp(rsa->d, rsa->p, rsa->q, ctx);
+    if (e == NULL) {
+      OPENSSL_PUT_ERROR(RSA, rsa_setup_blinding, RSA_R_NO_PUBLIC_EXPONENT);
+      goto err;
+    }
+  } else {
+    e = rsa->e;
+  }
+
+  n = &local_n;
+  BN_with_flags(n, rsa->n, BN_FLG_CONSTTIME);
+
+  if (rsa->flags & RSA_FLAG_CACHE_PUBLIC) {
+    if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n,
+                                ctx)) {
+      goto err;
+    }
+  }
+
+  ret = BN_BLINDING_create_param(NULL, e, n, ctx, rsa->meth->bn_mod_exp,
+                                 rsa->_method_mod_n);
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(RSA, rsa_setup_blinding, ERR_R_BN_LIB);
+    goto err;
+  }
+  CRYPTO_THREADID_current(BN_BLINDING_thread_id(ret));
+
+err:
+  BN_CTX_end(ctx);
+  if (in_ctx == NULL) {
+    BN_CTX_free(ctx);
+  }
+  if (rsa->e == NULL) {
+    BN_free(e);
+  }
+
+  return ret;
+}
diff --git a/crypto/rsa/internal.h b/crypto/rsa/internal.h
new file mode 100644
index 0000000..f697940
--- /dev/null
+++ b/crypto/rsa/internal.h
@@ -0,0 +1,157 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_RSA_INTERNAL_H
+#define OPENSSL_HEADER_RSA_INTERNAL_H
+
+#include <openssl/base.h>
+
+#include <openssl/asn1.h>
+#include <openssl/thread.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define RSA_PKCS1_PADDING_SIZE 11
+
+
+/* BN_BLINDING flags */
+#define BN_BLINDING_NO_UPDATE 0x00000001
+#define BN_BLINDING_NO_RECREATE 0x00000002
+
+BN_BLINDING *BN_BLINDING_new(const BIGNUM *A, const BIGNUM *Ai, BIGNUM *mod);
+void BN_BLINDING_free(BN_BLINDING *b);
+int BN_BLINDING_update(BN_BLINDING *b, BN_CTX *ctx);
+int BN_BLINDING_convert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx);
+int BN_BLINDING_invert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx);
+int BN_BLINDING_convert_ex(BIGNUM *n, BIGNUM *r, BN_BLINDING *b, BN_CTX *);
+int BN_BLINDING_invert_ex(BIGNUM *n, const BIGNUM *r, BN_BLINDING *b, BN_CTX *);
+CRYPTO_THREADID *BN_BLINDING_thread_id(BN_BLINDING *);
+unsigned long BN_BLINDING_get_flags(const BN_BLINDING *);
+void BN_BLINDING_set_flags(BN_BLINDING *, unsigned long);
+BN_BLINDING *BN_BLINDING_create_param(
+    BN_BLINDING *b, const BIGNUM *e, BIGNUM *m, BN_CTX *ctx,
+    int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                      const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx),
+    BN_MONT_CTX *m_ctx);
+BN_BLINDING *rsa_setup_blinding(RSA *rsa, BN_CTX *in_ctx);
+
+
+int RSA_padding_add_PKCS1_type_1(uint8_t *to, unsigned to_len,
+                                 const uint8_t *from, unsigned from_len);
+int RSA_padding_check_PKCS1_type_1(uint8_t *to, unsigned to_len,
+                                   const uint8_t *from, unsigned from_len,
+                                   unsigned rsa_len);
+int RSA_padding_add_PKCS1_type_2(uint8_t *to, unsigned to_len,
+                                 const uint8_t *from, unsigned from_len);
+int RSA_padding_check_PKCS1_type_2(uint8_t *to, unsigned to_len,
+                                   const uint8_t *from, unsigned from_len,
+                                   unsigned rsa_len);
+int RSA_padding_add_PKCS1_OAEP_mgf1(uint8_t *to, unsigned to_len,
+                                    const uint8_t *from, unsigned from_len,
+                                    const uint8_t *param, unsigned plen,
+                                    const EVP_MD *md, const EVP_MD *mgf1md);
+int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *to, unsigned to_len,
+                                      const uint8_t *from, unsigned from_len,
+                                      unsigned num, const uint8_t *param,
+                                      unsigned plen, const EVP_MD *md,
+                                      const EVP_MD *mgf1md);
+int RSA_padding_add_PKCS1_OAEP(uint8_t *to, unsigned to_len,
+                               const uint8_t *from, unsigned from_len,
+                               const uint8_t *p, unsigned pl);
+int RSA_padding_check_PKCS1_OAEP(uint8_t *to, unsigned to_len,
+                                 const uint8_t *from, unsigned from_len,
+                                 unsigned rsa_len, const uint8_t *p, unsigned pl);
+int RSA_padding_add_SSLv23(uint8_t *to, unsigned to_len, const uint8_t *from,
+                           unsigned from_len);
+int RSA_padding_check_SSLv23(uint8_t *to, unsigned to_len, const uint8_t *from,
+                             unsigned from_len, unsigned rsa_len);
+int RSA_padding_add_none(uint8_t *to, unsigned to_len, const uint8_t *from,
+                         unsigned from_len);
+int RSA_padding_check_none(uint8_t *to, unsigned to_len, const uint8_t *from,
+                           unsigned from_len, unsigned rsa_len);
+
+/* RSA_verify_PKCS1_PSS_mgf1 */
+int RSA_verify_PKCS1_PSS_mgf1(RSA *rsa, const uint8_t *mHash,
+                              const EVP_MD *Hash, const EVP_MD *mgf1Hash,
+                              const uint8_t *EM, int sLen);
+
+int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, uint8_t *EM, const uint8_t *mHash,
+                                   const EVP_MD *Hash, const EVP_MD *mgf1Hash,
+                                   int sLen);
+
+typedef struct rsa_pss_params_st {
+  X509_ALGOR *hashAlgorithm;
+  X509_ALGOR *maskGenAlgorithm;
+  ASN1_INTEGER *saltLength;
+  ASN1_INTEGER *trailerField;
+} RSA_PSS_PARAMS;
+
+DECLARE_ASN1_FUNCTIONS(RSA_PSS_PARAMS)
+
+typedef struct rsa_oaep_params_st {
+  X509_ALGOR *hashFunc;
+  X509_ALGOR *maskGenFunc;
+  X509_ALGOR *pSourceFunc;
+} RSA_OAEP_PARAMS;
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_RSA_INTERNAL_H */
diff --git a/crypto/rsa/padding.c b/crypto/rsa/padding.c
new file mode 100644
index 0000000..203f4fb
--- /dev/null
+++ b/crypto/rsa/padding.c
@@ -0,0 +1,827 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2005.
+ */
+/* ====================================================================
+ * Copyright (c) 2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/rsa.h>
+
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include "internal.h"
+
+/* TODO(fork): don't the check functions have to be constant time? */
+
+int RSA_padding_add_PKCS1_type_1(uint8_t *to, unsigned tlen,
+                                 const uint8_t *from, unsigned flen) {
+  unsigned j;
+  uint8_t *p;
+
+  if (flen > (tlen - RSA_PKCS1_PADDING_SIZE)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_type_1,
+                      RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  p = (uint8_t *)to;
+
+  *(p++) = 0;
+  *(p++) = 1; /* Private Key BT (Block Type) */
+
+  /* pad out with 0xff data */
+  j = tlen - 3 - flen;
+  memset(p, 0xff, j);
+  p += j;
+  *(p++) = 0;
+  memcpy(p, from, (unsigned int)flen);
+  return 1;
+}
+
+int RSA_padding_check_PKCS1_type_1(uint8_t *to, unsigned tlen, const uint8_t *from,
+                                   unsigned flen, unsigned num) {
+  unsigned i, j;
+  const uint8_t *p;
+
+  if (flen == 0) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_1,
+                      RSA_R_EMPTY_PUBLIC_KEY);
+    return -1;
+  }
+
+  p = from;
+  if ((num != (flen + 1)) || (*(p++) != 1)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_1,
+                      RSA_R_BLOCK_TYPE_IS_NOT_01);
+    return -1;
+  }
+
+  /* scan over padding data */
+  j = flen - 1; /* one for type. */
+  for (i = 0; i < j; i++) {
+    if (*p != 0xff) /* should decrypt to 0xff */
+    {
+      if (*p == 0) {
+        p++;
+        break;
+      } else {
+        OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_1,
+                          RSA_R_BAD_FIXED_HEADER_DECRYPT);
+        return -1;
+      }
+    }
+    p++;
+  }
+
+  if (i == j) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_1,
+                      RSA_R_NULL_BEFORE_BLOCK_MISSING);
+    return -1;
+  }
+
+  if (i < 8) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_1,
+                      RSA_R_BAD_PAD_BYTE_COUNT);
+    return -1;
+  }
+  i++; /* Skip over the '\0' */
+  j -= i;
+  if (j > tlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_1,
+                      RSA_R_DATA_TOO_LARGE);
+    return -1;
+  }
+  memcpy(to, p, j);
+
+  return j;
+}
+
+int RSA_padding_add_PKCS1_type_2(uint8_t *to, unsigned tlen,
+                                 const uint8_t *from, unsigned flen) {
+  unsigned i, j;
+  uint8_t *p;
+
+  if (flen > (tlen - 11)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_type_2,
+                      RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  p = (unsigned char *)to;
+
+  *(p++) = 0;
+  *(p++) = 2; /* Public Key BT (Block Type) */
+
+  /* pad out with non-zero random data */
+  j = tlen - 3 - flen;
+
+  if (RAND_pseudo_bytes(p, j) <= 0) {
+    return 0;
+  }
+
+  for (i = 0; i < j; i++) {
+    if (*p == 0) {
+      do {
+        if (RAND_pseudo_bytes(p, 1) <= 0) {
+          return 0;
+        }
+      } while (*p == 0);
+    }
+    p++;
+  }
+
+  *(p++) = 0;
+
+  memcpy(p, from, (unsigned int)flen);
+  return 1;
+}
+
+int RSA_padding_check_PKCS1_type_2(uint8_t *to, unsigned tlen, const uint8_t *from,
+                                   unsigned flen, unsigned num) {
+  unsigned i, j;
+  const uint8_t *p;
+
+  if (flen == 0) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
+                      RSA_R_EMPTY_PUBLIC_KEY);
+    return -1;
+  }
+
+  p = from;
+  if ((num != (flen + 1)) || (*(p++) != 2)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
+                      RSA_R_BLOCK_TYPE_IS_NOT_02);
+    return -1;
+  }
+
+  /* scan over padding data */
+  j = flen - 1; /* one for type. */
+  for (i = 0; i < j; i++) {
+    if (*(p++) == 0) {
+      break;
+    }
+  }
+
+  if (i == j) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
+                      RSA_R_NULL_BEFORE_BLOCK_MISSING);
+    return -1;
+  }
+
+  if (i < 8) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
+                      RSA_R_BAD_PAD_BYTE_COUNT);
+    return -1;
+  }
+  i++; /* Skip over the '\0' */
+  j -= i;
+  if (j > tlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
+                      RSA_R_DATA_TOO_LARGE);
+    return -1;
+  }
+  memcpy(to, p, (unsigned int)j);
+
+  return j;
+}
+
+int RSA_padding_add_none(uint8_t *to, unsigned tlen, const uint8_t *from, unsigned flen) {
+  if (flen > tlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_none,
+                      RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  if (flen < tlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_none,
+                      RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  memcpy(to, from, (unsigned int)flen);
+  return 1;
+}
+
+int RSA_padding_check_none(uint8_t *to, unsigned tlen, const uint8_t *from,
+                           unsigned flen, unsigned num) {
+  if (flen > tlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_none, RSA_R_DATA_TOO_LARGE);
+    return -1;
+  }
+
+  memset(to, 0, tlen - flen);
+  memcpy(to + tlen - flen, from, flen);
+  return tlen;
+}
+
+int RSA_padding_add_SSLv23(uint8_t *to, unsigned tlen, const uint8_t *from,
+                           unsigned flen) {
+  unsigned i, j;
+  uint8_t *p;
+
+  if (flen > (tlen - 11)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_SSLv23,
+                      RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  p = to;
+
+  *(p++) = 0;
+  *(p++) = 2; /* Public Key BT (Block Type) */
+
+  /* pad out with non-zero random data */
+  j = tlen - 3 - 8 - flen;
+
+  if (RAND_pseudo_bytes(p, j) <= 0) {
+    return 0;
+  }
+
+  for (i = 0; i < j; i++) {
+    if (*p == '\0') {
+      do {
+        if (RAND_pseudo_bytes(p, 1) <= 0)
+          return 0;
+      } while (*p == '\0');
+    }
+    p++;
+  }
+
+  memset(p, 3, 8);
+  p += 8;
+  *(p++) = '\0';
+
+  memcpy(p, from, flen);
+  return 1;
+}
+
+int RSA_padding_check_SSLv23(uint8_t *to, unsigned tlen, const uint8_t *from,
+                             unsigned flen, unsigned num) {
+  unsigned i, j, k;
+  const uint8_t *p;
+
+  p = from;
+  if (flen < 10) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_SSLv23, RSA_R_DATA_TOO_SMALL);
+    return -1;
+  }
+  if ((num != (flen + 1)) || (*(p++) != 02)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_SSLv23,
+                      RSA_R_BLOCK_TYPE_IS_NOT_02);
+    return -1;
+  }
+
+  /* scan over padding data */
+  j = flen - 1; /* one for type */
+  for (i = 0; i < j; i++) {
+    if (*(p++) == 0) {
+      break;
+    }
+  }
+
+  if ((i == j) || (i < 8)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_SSLv23,
+                      RSA_R_NULL_BEFORE_BLOCK_MISSING);
+    return -1;
+  }
+
+  for (k = -9; k < -1; k++) {
+    if (p[k] != 0x03) {
+      break;
+    }
+  }
+
+  if (k == -1) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_SSLv23,
+                      RSA_R_SSLV3_ROLLBACK_ATTACK);
+    return -1;
+  }
+
+  i++; /* Skip over the '\0' */
+  j -= i;
+  if (j > tlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_SSLv23, RSA_R_DATA_TOO_LARGE);
+    return -1;
+  }
+  memcpy(to, p, (unsigned int)j);
+
+  return j;
+}
+
+int PKCS1_MGF1(uint8_t *mask, unsigned len, const uint8_t *seed,
+               unsigned seedlen, const EVP_MD *dgst) {
+  unsigned outlen = 0;
+  uint32_t i;
+  uint8_t cnt[4];
+  EVP_MD_CTX c;
+  uint8_t md[EVP_MAX_MD_SIZE];
+  unsigned mdlen;
+  int ret = -1;
+
+  EVP_MD_CTX_init(&c);
+  mdlen = EVP_MD_size(dgst);
+
+  for (i = 0; outlen < len; i++) {
+    cnt[0] = (uint8_t)((i >> 24) & 255);
+    cnt[1] = (uint8_t)((i >> 16) & 255);
+    cnt[2] = (uint8_t)((i >> 8)) & 255;
+    cnt[3] = (uint8_t)(i & 255);
+    if (!EVP_DigestInit_ex(&c, dgst, NULL) ||
+        !EVP_DigestUpdate(&c, seed, seedlen) || !EVP_DigestUpdate(&c, cnt, 4)) {
+      goto err;
+    }
+
+    if (outlen + mdlen <= len) {
+      if (!EVP_DigestFinal_ex(&c, mask + outlen, NULL)) {
+        goto err;
+      }
+      outlen += mdlen;
+    } else {
+      if (!EVP_DigestFinal_ex(&c, md, NULL)) {
+        goto err;
+      }
+      memcpy(mask + outlen, md, len - outlen);
+      outlen = len;
+    }
+  }
+  ret = 0;
+
+err:
+  EVP_MD_CTX_cleanup(&c);
+  return ret;
+}
+
+int RSA_padding_add_PKCS1_OAEP_mgf1(uint8_t *to, unsigned tlen,
+                                    const uint8_t *from, unsigned flen,
+                                    const uint8_t *param, unsigned plen,
+                                    const EVP_MD *md, const EVP_MD *mgf1md) {
+  unsigned i, emlen = tlen - 1, mdlen;
+  uint8_t *db, *seed;
+  uint8_t *dbmask = NULL, seedmask[SHA_DIGEST_LENGTH];
+  int ret = 0;
+
+  if (md == NULL) {
+    md = EVP_sha1();
+  }
+  if (mgf1md == NULL) {
+    mgf1md = md;
+  }
+
+  mdlen = EVP_MD_size(md);
+
+  if (flen > emlen - 2 * mdlen - 1) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_OAEP_mgf1,
+                      RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  if (emlen < 2 * mdlen + 1) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_OAEP_mgf1,
+                      RSA_R_KEY_SIZE_TOO_SMALL);
+    return 0;
+  }
+
+  to[0] = 0;
+  seed = to + 1;
+  db = to + mdlen + 1;
+
+  if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL)) {
+    return 0;
+  }
+  memset(db + mdlen, 0, emlen - flen - 2 * mdlen - 1);
+  db[emlen - flen - mdlen - 1] = 0x01;
+  memcpy(db + emlen - flen - mdlen, from, (unsigned int)flen);
+  if (RAND_pseudo_bytes(seed, mdlen) <= 0) {
+    return 0;
+  }
+
+  dbmask = OPENSSL_malloc(emlen - mdlen);
+  if (dbmask == NULL) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_OAEP_mgf1,
+                      ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  if (PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md) < 0) {
+    goto out;
+  }
+  for (i = 0; i < emlen - mdlen; i++) {
+    db[i] ^= dbmask[i];
+  }
+
+  if (PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md) < 0) {
+    goto out;
+  }
+  for (i = 0; i < SHA_DIGEST_LENGTH; i++) {
+    seed[i] ^= seedmask[i];
+  }
+  ret = 1;
+
+out:
+  if (dbmask != NULL) {
+    OPENSSL_free(dbmask);
+  }
+  return ret;
+}
+
+int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *to, unsigned tlen,
+                                      const uint8_t *from, unsigned flen,
+                                      unsigned num, const uint8_t *param,
+                                      unsigned plen, const EVP_MD *md,
+                                      const EVP_MD *mgf1md) {
+  unsigned i, dblen, mlen = -1, bad, mdlen;
+  const uint8_t *maskeddb;
+  unsigned lzero;
+  uint8_t *db = NULL, seed[SHA_DIGEST_LENGTH], phash[SHA_DIGEST_LENGTH];
+  uint8_t *padded_from;
+
+  if (md == NULL) {
+    md = EVP_sha1();
+  }
+  if (mgf1md == NULL) {
+    mgf1md = md;
+  }
+
+  mdlen = EVP_MD_size(md);
+
+  if (--num < 2 * mdlen + 1) {
+    /* 'num' is the length of the modulus, i.e. does not depend on the
+     * particular ciphertext. */
+    goto decoding_err;
+  }
+
+  /* TODO(fork): this code differs significantly between 1.0.1 and 1.0.2. We
+   * need to understand why and pick the best one. */
+
+  /* lzero is the number of leading zeros. We must not leak in the case
+   * that this is negative. See James H. Manger, "A Chosen Ciphertext
+   * Attack on RSA Optimal Asymmetric Encryption Padding (OAEP) [...]",
+   * CRYPTO 2001). */
+  lzero = num - flen;
+  /* If lzero is negative then the MSB will be set and this arithmetic
+   * right shift will set bad to all ones. Otherwise it'll be all
+   * zeros. */
+  bad = ((int)lzero) >> (sizeof(int) * 8 - 1);
+  lzero &= ~bad;
+  flen = (bad & num) | (~bad & flen);
+
+  dblen = num - mdlen;
+  db = OPENSSL_malloc(dblen + num);
+  if (db == NULL) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_OAEP_mgf1,
+                      ERR_R_MALLOC_FAILURE);
+    return -1;
+  }
+
+  /* Always do this zero-padding copy (even when lzero == 0) to avoid
+   * leaking timing info about the value of lzero. This sadly leaks
+   * side-channel information, but it's not possible to have a fixed
+   * memory access pattern since we can't read out of the bounds of
+   * |from|. */
+  padded_from = db + dblen;
+  memset(padded_from, 0, num);
+  memcpy(padded_from + lzero, from, flen);
+
+  maskeddb = padded_from + mdlen;
+
+  if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md)) {
+    return -1;
+  }
+  for (i = 0; i < mdlen; i++) {
+    seed[i] ^= padded_from[i];
+  }
+
+  if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md)) {
+    return -1;
+  }
+  for (i = 0; i < dblen; i++) {
+    db[i] ^= maskeddb[i];
+  }
+
+  if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL)) {
+    return -1;
+  }
+
+  if (CRYPTO_memcmp(db, phash, mdlen) != 0 || bad) {
+    goto decoding_err;
+  } else {
+    /* At this point we consider timing side-channels to be moot
+     * because the plaintext contained the correct phash. */
+    for (i = mdlen; i < dblen; i++) {
+      if (db[i] != 0x00) {
+        break;
+      }
+    }
+
+    if (i == dblen || db[i] != 0x01) {
+      goto decoding_err;
+    } else {
+      /* everything looks OK */
+
+      mlen = dblen - ++i;
+      if (tlen < mlen) {
+        OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_OAEP_mgf1,
+                          RSA_R_DATA_TOO_LARGE);
+        mlen = -1;
+      } else {
+        memcpy(to, db + i, mlen);
+      }
+    }
+  }
+
+  OPENSSL_free(db);
+  return mlen;
+
+decoding_err:
+  /* to avoid chosen ciphertext attacks, the error message should not reveal
+   * which kind of decoding error happened */
+  OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_OAEP_mgf1,
+                    RSA_R_OAEP_DECODING_ERROR);
+  if (db != NULL) {
+    OPENSSL_free(db);
+  }
+  return -1;
+}
+
+int RSA_padding_add_PKCS1_OAEP(uint8_t *to, unsigned tlen,
+                               const uint8_t *from, unsigned flen,
+                               const uint8_t *param, unsigned plen) {
+  return RSA_padding_add_PKCS1_OAEP_mgf1(to, tlen, from, flen, param, plen,
+                                         NULL, NULL);
+}
+
+int RSA_padding_check_PKCS1_OAEP(uint8_t *to, unsigned tlen,
+                                 const uint8_t *from, unsigned flen,
+                                 unsigned num, const uint8_t *param,
+                                 unsigned plen) {
+  return RSA_padding_check_PKCS1_OAEP_mgf1(to, tlen, from, flen, num, param,
+                                           plen, NULL, NULL);
+}
+
+static const unsigned char zeroes[] = {0,0,0,0,0,0,0,0};
+
+int RSA_verify_PKCS1_PSS_mgf1(RSA *rsa, const uint8_t *mHash,
+                              const EVP_MD *Hash, const EVP_MD *mgf1Hash,
+                              const uint8_t *EM, int sLen) {
+  int i;
+  int ret = 0;
+  int maskedDBLen, MSBits, emLen;
+  size_t hLen;
+  const uint8_t *H;
+  uint8_t *DB = NULL;
+  EVP_MD_CTX ctx;
+  uint8_t H_[EVP_MAX_MD_SIZE];
+  EVP_MD_CTX_init(&ctx);
+
+  if (mgf1Hash == NULL) {
+    mgf1Hash = Hash;
+  }
+
+  hLen = EVP_MD_size(Hash);
+
+  /* Negative sLen has special meanings:
+   *	-1	sLen == hLen
+   *	-2	salt length is autorecovered from signature
+   *	-N	reserved */
+  if (sLen == -1) {
+    sLen = hLen;
+  } else if (sLen == -2) {
+    sLen = -2;
+  } else if (sLen < -2) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1, RSA_R_SLEN_CHECK_FAILED);
+    goto err;
+  }
+
+  MSBits = (BN_num_bits(rsa->n) - 1) & 0x7;
+  emLen = RSA_size(rsa);
+  if (EM[0] & (0xFF << MSBits)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1,
+                      RSA_R_FIRST_OCTET_INVALID);
+    goto err;
+  }
+  if (MSBits == 0) {
+    EM++;
+    emLen--;
+  }
+  if (emLen < ((int)hLen + sLen + 2)) {
+    /* sLen can be small negative */
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1, RSA_R_DATA_TOO_LARGE);
+    goto err;
+  }
+  if (EM[emLen - 1] != 0xbc) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1, RSA_R_LAST_OCTET_INVALID);
+    goto err;
+  }
+  maskedDBLen = emLen - hLen - 1;
+  H = EM + maskedDBLen;
+  DB = OPENSSL_malloc(maskedDBLen);
+  if (!DB) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  if (PKCS1_MGF1(DB, maskedDBLen, H, hLen, mgf1Hash) < 0) {
+    goto err;
+  }
+  for (i = 0; i < maskedDBLen; i++) {
+    DB[i] ^= EM[i];
+  }
+  if (MSBits) {
+    DB[0] &= 0xFF >> (8 - MSBits);
+  }
+  for (i = 0; DB[i] == 0 && i < (maskedDBLen - 1); i++)
+    ;
+  if (DB[i++] != 0x1) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1,
+                      RSA_R_SLEN_RECOVERY_FAILED);
+    goto err;
+  }
+  if (sLen >= 0 && (maskedDBLen - i) != sLen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1, RSA_R_SLEN_CHECK_FAILED);
+    goto err;
+  }
+  if (!EVP_DigestInit_ex(&ctx, Hash, NULL) ||
+      !EVP_DigestUpdate(&ctx, zeroes, sizeof zeroes) ||
+      !EVP_DigestUpdate(&ctx, mHash, hLen)) {
+    goto err;
+  }
+  if (maskedDBLen - i) {
+    if (!EVP_DigestUpdate(&ctx, DB + i, maskedDBLen - i)) {
+      goto err;
+    }
+  }
+  if (!EVP_DigestFinal_ex(&ctx, H_, NULL)) {
+    goto err;
+  }
+  if (memcmp(H_, H, hLen)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify_PKCS1_PSS_mgf1, RSA_R_BAD_SIGNATURE);
+    ret = 0;
+  } else {
+    ret = 1;
+  }
+
+err:
+  if (DB) {
+    OPENSSL_free(DB);
+  }
+  EVP_MD_CTX_cleanup(&ctx);
+
+  return ret;
+}
+
+int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, unsigned char *EM,
+                                   const unsigned char *mHash,
+                                   const EVP_MD *Hash, const EVP_MD *mgf1Hash,
+                                   int sLen) {
+  int i;
+  int ret = 0;
+  int maskedDBLen, MSBits, emLen;
+  size_t hLen;
+  unsigned char *H, *salt = NULL, *p;
+  EVP_MD_CTX ctx;
+
+  if (mgf1Hash == NULL) {
+    mgf1Hash = Hash;
+  }
+
+  hLen = EVP_MD_size(Hash);
+
+  /* Negative sLen has special meanings:
+   *	-1	sLen == hLen
+   *	-2	salt length is maximized
+   *	-N	reserved */
+  if (sLen == -1) {
+    sLen = hLen;
+  } else if (sLen == -2) {
+    sLen = -2;
+  } else if (sLen < -2) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_PSS_mgf1,
+                      RSA_R_SLEN_CHECK_FAILED);
+    goto err;
+  }
+
+  MSBits = (BN_num_bits(rsa->n) - 1) & 0x7;
+  emLen = RSA_size(rsa);
+  if (MSBits == 0) {
+    *EM++ = 0;
+    emLen--;
+  }
+  if (sLen == -2) {
+    sLen = emLen - hLen - 2;
+  } else if (emLen < (hLen + sLen + 2)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_PSS_mgf1,
+                      RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    goto err;
+  }
+  if (sLen > 0) {
+    salt = OPENSSL_malloc(sLen);
+    if (!salt) {
+      OPENSSL_PUT_ERROR(RSA, RSA_padding_add_PKCS1_PSS_mgf1,
+                        ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    if (RAND_pseudo_bytes(salt, sLen) <= 0) {
+      goto err;
+    }
+  }
+  maskedDBLen = emLen - hLen - 1;
+  H = EM + maskedDBLen;
+  EVP_MD_CTX_init(&ctx);
+  if (!EVP_DigestInit_ex(&ctx, Hash, NULL) ||
+      !EVP_DigestUpdate(&ctx, zeroes, sizeof zeroes) ||
+      !EVP_DigestUpdate(&ctx, mHash, hLen)) {
+    goto err;
+  }
+  if (sLen && !EVP_DigestUpdate(&ctx, salt, sLen)) {
+    goto err;
+  }
+  if (!EVP_DigestFinal_ex(&ctx, H, NULL)) {
+    goto err;
+  }
+  EVP_MD_CTX_cleanup(&ctx);
+
+  /* Generate dbMask in place then perform XOR on it */
+  if (PKCS1_MGF1(EM, maskedDBLen, H, hLen, mgf1Hash)) {
+    goto err;
+  }
+
+  p = EM;
+
+  /* Initial PS XORs with all zeroes which is a NOP so just update
+   * pointer. Note from a test above this value is guaranteed to
+   * be non-negative. */
+  p += emLen - sLen - hLen - 2;
+  *p++ ^= 0x1;
+  if (sLen > 0) {
+    for (i = 0; i < sLen; i++) {
+      *p++ ^= salt[i];
+    }
+  }
+  if (MSBits) {
+    EM[0] &= 0xFF >> (8 - MSBits);
+  }
+
+  /* H is already in place so just set final 0xbc */
+
+  EM[emLen - 1] = 0xbc;
+
+  ret = 1;
+
+err:
+  if (salt) {
+    OPENSSL_free(salt);
+  }
+
+  return ret;
+}
diff --git a/crypto/rsa/rsa.c b/crypto/rsa/rsa.c
new file mode 100644
index 0000000..061bc49
--- /dev/null
+++ b/crypto/rsa/rsa.c
@@ -0,0 +1,488 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/rsa.h>
+
+#include <openssl/bn.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/ex_data.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+extern const RSA_METHOD RSA_default_method;
+
+RSA *RSA_new(void) { return RSA_new_method(NULL); }
+
+RSA *RSA_new_method(const ENGINE *engine) {
+  RSA *rsa = (RSA *)OPENSSL_malloc(sizeof(RSA));
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(RSA, RSA_new_method, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(rsa, 0, sizeof(RSA));
+
+  if (engine) {
+    rsa->meth = ENGINE_get_RSA_method(engine);
+  }
+
+  if (rsa->meth == NULL) {
+    rsa->meth = (RSA_METHOD*) &RSA_default_method;
+  }
+  METHOD_ref(rsa->meth);
+
+  rsa->references = 1;
+  rsa->flags = rsa->meth->flags;
+
+  if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_RSA, rsa, &rsa->ex_data)) {
+    METHOD_unref(rsa->meth);
+    OPENSSL_free(rsa);
+    return NULL;
+  }
+
+  if (rsa->meth->init && !rsa->meth->init(rsa)) {
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_RSA, rsa, &rsa->ex_data);
+    METHOD_unref(rsa->meth);
+    OPENSSL_free(rsa);
+    return NULL;
+  }
+
+  return rsa;
+}
+
+void RSA_free(RSA *rsa) {
+  unsigned u;
+
+  if (rsa == NULL) {
+    return;
+  }
+
+  if (CRYPTO_add(&rsa->references, -1, CRYPTO_LOCK_RSA) > 0) {
+    return;
+  }
+
+  if (rsa->meth->finish) {
+    rsa->meth->finish(rsa);
+  }
+  METHOD_unref(rsa->meth);
+
+  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DSA, rsa, &rsa->ex_data);
+
+  if (rsa->n != NULL)
+    BN_clear_free(rsa->n);
+  if (rsa->e != NULL)
+    BN_clear_free(rsa->e);
+  if (rsa->d != NULL)
+    BN_clear_free(rsa->d);
+  if (rsa->p != NULL)
+    BN_clear_free(rsa->p);
+  if (rsa->q != NULL)
+    BN_clear_free(rsa->q);
+  if (rsa->dmp1 != NULL)
+    BN_clear_free(rsa->dmp1);
+  if (rsa->dmq1 != NULL)
+    BN_clear_free(rsa->dmq1);
+  if (rsa->iqmp != NULL)
+    BN_clear_free(rsa->iqmp);
+  for (u = 0; u < rsa->num_blindings; u++) {
+    BN_BLINDING_free(rsa->blindings[u]);
+  }
+  if (rsa->blindings != NULL)
+    OPENSSL_free(rsa->blindings);
+  if (rsa->blindings_inuse != NULL)
+    OPENSSL_free(rsa->blindings_inuse);
+  OPENSSL_free(rsa);
+}
+
+int RSA_up_ref(RSA *rsa) {
+  CRYPTO_add(&rsa->references, 1, CRYPTO_LOCK_RSA);
+  return 1;
+}
+
+int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
+  if (rsa->meth->keygen) {
+    return rsa->meth->keygen(rsa, bits, e_value, cb);
+  }
+
+  return RSA_default_method.keygen(rsa, bits, e_value, cb);
+}
+
+int RSA_encrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                const uint8_t *in, size_t in_len, int padding) {
+  if (rsa->meth->encrypt) {
+    return rsa->meth->encrypt(rsa, out_len, out, max_out, in, in_len, padding);
+  }
+
+  return RSA_default_method.encrypt(rsa, out_len, out, max_out, in, in_len,
+                                    padding);
+}
+
+int RSA_public_encrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                       int padding) {
+  size_t out_len;
+
+  if (!RSA_encrypt(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  return out_len;
+}
+
+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) {
+  if (rsa->meth->sign_raw) {
+    return rsa->meth->sign_raw(rsa, out_len, out, max_out, in, in_len, padding);
+  }
+
+  return RSA_default_method.sign_raw(rsa, out_len, out, max_out, in, in_len,
+                                     padding);
+}
+
+int RSA_private_encrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                        int padding) {
+  size_t out_len;
+
+  if (!RSA_sign_raw(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  return out_len;
+}
+
+int RSA_decrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                const uint8_t *in, size_t in_len, int padding) {
+  if (rsa->meth->decrypt) {
+    return rsa->meth->decrypt(rsa, out_len, out, max_out, in, in_len, padding);
+  }
+
+  return RSA_default_method.decrypt(rsa, out_len, out, max_out, in, in_len,
+                                    padding);
+}
+
+int RSA_private_decrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                        int padding) {
+  size_t out_len;
+
+  if (!RSA_decrypt(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  return out_len;
+}
+
+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) {
+  if (rsa->meth->verify_raw) {
+    return rsa->meth->verify_raw(rsa, out_len, out, max_out, in, in_len, padding);
+  }
+
+  return RSA_default_method.verify_raw(rsa, out_len, out, max_out, in, in_len,
+                                       padding);
+}
+
+int RSA_public_decrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                       int padding) {
+  size_t out_len;
+
+  if (!RSA_verify_raw(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  return out_len;
+}
+
+unsigned RSA_size(const RSA *rsa) {
+  return BN_num_bytes(rsa->n);
+}
+
+int RSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                         CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
+  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_RSA, argl, argp, new_func,
+                                 dup_func, free_func);
+}
+
+int RSA_set_ex_data(RSA *d, int idx, void *arg) {
+  return CRYPTO_set_ex_data(&d->ex_data, idx, arg);
+}
+
+void *RSA_get_ex_data(const RSA *d, int idx) {
+  return CRYPTO_get_ex_data(&d->ex_data, idx);
+}
+
+/* SSL_SIG_LENGTH is the size of an SSL/TLS (prior to TLS 1.2) signature: it's
+ * the length of an MD5 and SHA1 hash. */
+static const unsigned SSL_SIG_LENGTH = 36;
+
+/* pkcs1_sig_prefix contains the ASN.1, DER encoded prefix for a hash that is
+ * to be signed with PKCS#1. */
+struct pkcs1_sig_prefix {
+  /* nid identifies the hash function. */
+  int nid;
+  /* len is the number of bytes of |bytes| which are valid. */
+  uint8_t len;
+  /* bytes contains the DER bytes. */
+  uint8_t bytes[19];
+};
+
+/* kPKCS1SigPrefixes contains the ASN.1 prefixes for PKCS#1 signatures with
+ * different hash functions. */
+static const struct pkcs1_sig_prefix kPKCS1SigPrefixes[] = {
+    {
+     NID_md5,
+     18,
+     {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+      0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
+    },
+    {
+     NID_sha1,
+     15,
+     {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05,
+      0x00, 0x04, 0x14},
+    },
+    {
+     NID_sha224,
+     19,
+     {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+      0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
+    },
+    {
+     NID_sha256,
+     19,
+     {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+      0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
+    },
+    {
+     NID_sha384,
+     19,
+     {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+      0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
+    },
+    {
+     NID_sha512,
+     19,
+     {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+      0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
+    },
+    {
+     NID_ripemd160,
+     14,
+     {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31,
+      0x04, 0x14},
+    },
+    {
+     NID_undef, 0, {0},
+    },
+};
+
+/* TODO(fork): mostly new code, needs careful review. */
+
+/* pkcs1_prefixed_msg builds a PKCS#1, prefixed version of |msg| for the given
+ * hash function and sets |out_msg| to point to it. On successful return,
+ * |*out_msg| may be allocated memory and, if so, |*is_alloced| will be 1. */
+static int pkcs1_prefixed_msg(uint8_t **out_msg, size_t *out_msg_len,
+                              int *is_alloced, int hash_nid, const uint8_t *msg,
+                              size_t msg_len) {
+  unsigned i;
+  const uint8_t* prefix = NULL;
+  unsigned prefix_len;
+  uint8_t *signed_msg;
+  unsigned signed_msg_len;
+
+  if (hash_nid == NID_md5_sha1) {
+    /* Special case: SSL signature, just check the length. */
+    if (msg_len != SSL_SIG_LENGTH) {
+      OPENSSL_PUT_ERROR(RSA, RSA_sign, RSA_R_INVALID_MESSAGE_LENGTH);
+      return 0;
+    }
+
+    *out_msg = (uint8_t*) msg;
+    *out_msg_len = SSL_SIG_LENGTH;
+    *is_alloced = 0;
+    return 1;
+  }
+
+  for (i = 0; kPKCS1SigPrefixes[i].nid != NID_undef; i++) {
+    const struct pkcs1_sig_prefix *sig_prefix = &kPKCS1SigPrefixes[i];
+    if (sig_prefix->nid == hash_nid) {
+      prefix = sig_prefix->bytes;
+      prefix_len = sig_prefix->len;
+      break;
+    }
+  }
+
+  if (prefix == NULL) {
+    OPENSSL_PUT_ERROR(RSA, RSA_sign, RSA_R_UNKNOWN_ALGORITHM_TYPE);
+    return 0;
+  }
+
+  signed_msg_len = prefix_len + msg_len;
+  if (signed_msg_len < prefix_len) {
+    OPENSSL_PUT_ERROR(RSA, RSA_sign, RSA_R_TOO_LONG);
+    return 0;
+  }
+
+  signed_msg = OPENSSL_malloc(signed_msg_len);
+  if (!signed_msg) {
+    OPENSSL_PUT_ERROR(RSA, RSA_sign, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  memcpy(signed_msg, prefix, prefix_len);
+  memcpy(signed_msg + prefix_len, msg, msg_len);
+
+  *out_msg = signed_msg;
+  *out_msg_len = signed_msg_len;
+  *is_alloced = 1;
+
+  return 1;
+}
+
+int RSA_sign(int hash_nid, const uint8_t *in, unsigned in_len, uint8_t *out,
+             unsigned *out_len, RSA *rsa) {
+  const unsigned rsa_size = RSA_size(rsa);
+  int ret = 0;
+  uint8_t *signed_msg;
+  size_t signed_msg_len;
+  int signed_msg_is_alloced = 0;
+  size_t size_t_out_len;
+
+  if (rsa->meth->sign) {
+    return rsa->meth->sign(hash_nid, in, in_len, out, out_len, rsa);
+  }
+
+  if (!pkcs1_prefixed_msg(&signed_msg, &signed_msg_len, &signed_msg_is_alloced,
+                          hash_nid, in, in_len)) {
+    return 0;
+  }
+
+  if (rsa_size < RSA_PKCS1_PADDING_SIZE ||
+      signed_msg_len > rsa_size - RSA_PKCS1_PADDING_SIZE) {
+    OPENSSL_PUT_ERROR(RSA, RSA_sign, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY);
+    goto finish;
+  }
+
+  if (RSA_sign_raw(rsa, &size_t_out_len, out, rsa_size, signed_msg,
+                   signed_msg_len, RSA_PKCS1_PADDING)) {
+    *out_len = size_t_out_len;
+    ret = 1;
+  }
+
+finish:
+  if (signed_msg_is_alloced) {
+    OPENSSL_free(signed_msg);
+  }
+  return ret;
+}
+
+int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len,
+               const uint8_t *sig, size_t sig_len, RSA *rsa) {
+  const size_t rsa_size = RSA_size(rsa);
+  uint8_t *buf = NULL;
+  int ret = 0;
+  uint8_t *signed_msg = NULL;
+  size_t signed_msg_len, len;
+  int signed_msg_is_alloced = 0;
+
+  if (rsa->meth->verify) {
+    return rsa->meth->verify(hash_nid, msg, msg_len, sig, sig_len, rsa);
+  }
+
+  if (sig_len != rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify, RSA_R_WRONG_SIGNATURE_LENGTH);
+    return 0;
+  }
+
+  if (hash_nid == NID_md5_sha1 && msg_len != SSL_SIG_LENGTH) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify, RSA_R_INVALID_MESSAGE_LENGTH);
+    return 0;
+  }
+
+  buf = OPENSSL_malloc(rsa_size);
+  if (!buf) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  if (!RSA_verify_raw(rsa, &len, buf, rsa_size, sig, sig_len,
+                      RSA_PKCS1_PADDING)) {
+    goto out;
+  }
+
+  if (!pkcs1_prefixed_msg(&signed_msg, &signed_msg_len, &signed_msg_is_alloced,
+                          hash_nid, msg, msg_len)) {
+    goto out;
+  }
+
+  if (len != signed_msg_len || CRYPTO_memcmp(buf, signed_msg, len) != 0) {
+    OPENSSL_PUT_ERROR(RSA, RSA_verify, RSA_R_BAD_SIGNATURE);
+    goto out;
+  }
+
+  ret = 1;
+
+out:
+  if (buf != NULL) {
+    OPENSSL_free(buf);
+  }
+  if (signed_msg_is_alloced) {
+    OPENSSL_free(signed_msg);
+  }
+  return ret;
+}
diff --git a/crypto/rsa/rsa.h b/crypto/rsa/rsa.h
new file mode 100644
index 0000000..27bc04a
--- /dev/null
+++ b/crypto/rsa/rsa.h
@@ -0,0 +1,459 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_RSA_H
+#define OPENSSL_HEADER_RSA_H
+
+#include <openssl/base.h>
+
+#include <openssl/engine.h>
+#include <openssl/ex_data.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* rsa.h contains functions for handling encryption and signature using RSA. */
+
+
+/* Allocation and destruction. */
+
+/* RSA_new returns a new, empty RSA object or NULL on error. */
+RSA *RSA_new(void);
+
+/* RSA_new_method acts the same as |DH_new| but takes an explicit |ENGINE|. */
+RSA *RSA_new_method(const ENGINE *engine);
+
+/* RSA_free decrements the reference count of |rsa| and frees it if the
+ * reference count drops to zero. */
+void RSA_free(RSA *rsa);
+
+/* RSA_up_ref increments the reference count of |rsa|. */
+int RSA_up_ref(RSA *rsa);
+
+
+/* Key generation. */
+
+/* RSA_generate_key_ex generates a new RSA key where the modulus has size
+ * |bits| and the public exponent is |e|. If unsure, |RSA_F4| is a good value
+ * for |e|. If |cb| is not NULL then it is called during the key generation
+ * process. In addition to the calls documented for |BN_generate_prime_ex|, it
+ * is called with event=2 when the n'th prime is rejected as unsuitable and
+ * with event=3 when a suitable value for |p| is found. */
+int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
+
+
+/* Encryption / Decryption */
+
+/* Padding types for encryption. */
+#define RSA_PKCS1_PADDING 1
+#define RSA_SSLV23_PADDING 2
+#define RSA_NO_PADDING 3
+#define RSA_PKCS1_OAEP_PADDING 4
+/* RSA_PKCS1_PSS_PADDING can only be used via the EVP interface. */
+#define RSA_PKCS1_PSS_PADDING 6
+
+/* RSA_encrypt encrypts |in_len| bytes from |in| to the public key from |rsa|
+ * and writes, at most, |max_out| bytes of encrypted data to |out|. The
+ * |max_out| argument must be, at least, |RSA_size| in order to ensure success.
+ *
+ * It returns 1 on success or zero on error.
+ *
+ * The |padding| argument must be one of the |RSA_*_PADDING| values. If in
+ * doubt, |RSA_PKCS1_PADDING| is the most common but |RSA_PKCS1_OAEP_PADDING|
+ * is the most secure. */
+int RSA_encrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                const uint8_t *in, size_t in_len, int padding);
+
+/* RSA_decrypt decrypts |in_len| bytes from |in| with the private key from
+ * |rsa| and writes, at most, |max_out| bytes of plaintext to |out|. The
+ * |max_out| argument must be, at least, |RSA_size| in order to ensure success.
+ *
+ * It returns 1 on success or zero on error.
+ *
+ * The |padding| argument must be one of the |RSA_*_PADDING| values. If in
+ * doubt, |RSA_PKCS1_PADDING| is the most common but |RSA_PKCS1_OAEP_PADDING|
+ * is the most secure. */
+int RSA_decrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                const uint8_t *in, size_t in_len, int padding);
+
+/* RSA_public_encrypt encrypts |flen| bytes from |from| to the public key in
+ * |rsa| and writes the encrypted data to |to|. The |to| buffer must have at
+ * least |RSA_size| bytes of space. It returns the number of bytes written, or
+ * -1 on error. The |padding| argument must be one of the |RSA_*_PADDING|
+ * values. If in doubt, |RSA_PKCS1_PADDING| is the most common but
+ * |RSA_PKCS1_OAEP_PADDING| is the most secure.
+ *
+ * WARNING: this function is dangerous because it breaks the usual return value
+ * convention. Use |RSA_encrypt| instead. */
+int RSA_public_encrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                       int padding);
+
+/* RSA_private_decrypt decrypts |flen| bytes from |from| with the public key in
+ * |rsa| and writes the plaintext to |to|. The |to| buffer must have at
+ * least |RSA_size| bytes of space. It returns the number of bytes written, or
+ * -1 on error. The |padding| argument must be one of the |RSA_*_PADDING|
+ * values. If in doubt, |RSA_PKCS1_PADDING| is the most common but
+ * |RSA_PKCS1_OAEP_PADDING| is the most secure.
+ *
+ * WARNING: this function is dangerous because it breaks the usual return value
+ * convention. Use |RSA_decrypt| instead. */
+int RSA_private_decrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                        int padding);
+
+
+/* Signing / Verification */
+
+/* RSA_sign signs |in_len| bytes of digest from |in| with |rsa| and writes, at
+ * most, |RSA_size(rsa)| bytes to |out|. On successful return, the actual
+ * number of bytes written is written to |*out_len|.
+ *
+ * The |hash_nid| argument identifies the hash function used to calculate |in|
+ * and is embedded in the resulting signature. For example, it might be
+ * |NID_sha256|.
+ *
+ * It returns 1 on success and zero on error. */
+int RSA_sign(int hash_nid, const uint8_t *in, unsigned int in_len, uint8_t *out,
+             unsigned int *out_len, RSA *rsa);
+
+/* RSA_sign_raw signs |in_len| bytes from |in| with the public key from |rsa|
+ * and writes, at most, |max_out| bytes of encrypted data to |out|. The
+ * |max_out| argument must be, at least, |RSA_size| in order to ensure success.
+ *
+ * It returns 1 on success or zero on error.
+ *
+ * The |padding| argument must be one of the |RSA_*_PADDING| values. If in
+ * doubt, |RSA_PKCS1_PADDING| is the most common. */
+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);
+
+/* RSA_verify verifies that |sig_len| bytes from |sig| are a valid, PKCS#1
+ * signature of |msg_len| bytes at |msg| by |rsa|.
+ *
+ * The |hash_nid| argument identifies the hash function used to calculate |in|
+ * and is embedded in the resulting signature in order to prevent hash
+ * confusion attacks. For example, it might be |NID_sha256|.
+ *
+ * It returns one if the signature is valid and zero otherwise.
+ *
+ * WARNING: this differs from the original, OpenSSL function which additionally
+ * returned -1 on error. */
+int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len,
+               const uint8_t *sig, size_t sig_len, RSA *rsa);
+
+/* RSA_verify_raw verifies |in_len| bytes of signature from |in| using the
+ * public key from |rsa| and writes, at most, |max_out| bytes of plaintext to
+ * |out|. The |max_out| argument must be, at least, |RSA_size| in order to
+ * ensure success.
+ *
+ * It returns 1 on success or zero on error.
+ *
+ * The |padding| argument must be one of the |RSA_*_PADDING| values. If in
+ * doubt, |RSA_PKCS1_PADDING| is the most common. */
+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);
+
+/* RSA_private_encrypt encrypts |flen| bytes from |from| with the private key in
+ * |rsa| and writes the encrypted data to |to|. The |to| buffer must have at
+ * least |RSA_size| bytes of space. It returns the number of bytes written, or
+ * -1 on error. The |padding| argument must be one of the |RSA_*_PADDING|
+ * values. If in doubt, |RSA_PKCS1_PADDING| is the most common.
+ *
+ * WARNING: this function is dangerous because it breaks the usual return value
+ * convention. Use |RSA_sign_raw| instead. */
+int RSA_private_encrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                        int padding);
+
+/* RSA_private_encrypt verifies |flen| bytes of signature from |from| using the
+ * public key in |rsa| and writes the plaintext to |to|. The |to| buffer must
+ * have at least |RSA_size| bytes of space. It returns the number of bytes
+ * written, or -1 on error. The |padding| argument must be one of the
+ * |RSA_*_PADDING| values. If in doubt, |RSA_PKCS1_PADDING| is the most common.
+ *
+ * WARNING: this function is dangerous because it breaks the usual return value
+ * convention. Use |RSA_verify_raw| instead. */
+int RSA_public_decrypt(int flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                       int padding);
+
+
+/* Utility functions. */
+
+/* RSA_size returns the number of bytes in the modulus, which is also the size
+ * of a signature of encrypted value using |rsa|. */
+unsigned RSA_size(const RSA *rsa);
+
+/* RSAPublicKey_dup allocates a fresh |RSA| and copies the private key from
+ * |rsa| into it. It returns the fresh |RSA| object, or NULL on error. */
+RSA *RSAPublicKey_dup(const RSA *rsa);
+
+/* RSAPrivateKey_dup allocates a fresh |RSA| and copies the private key from
+ * |rsa| into it. It returns the fresh |RSA| object, or NULL on error. */
+RSA *RSAPrivateKey_dup(const RSA *rsa);
+
+
+/* ASN.1 functions. */
+
+/* d2i_RSAPublicKey parses an ASN.1, DER-encoded, RSA public key from |len|
+ * bytes at |*inp|. If |out| is not NULL then, on exit, a pointer to the result
+ * is in |*out|. If |*out| is already non-NULL on entry then the result is
+ * written directly into |*out|, otherwise a fresh |RSA| is allocated. On
+ * successful exit, |*inp| is advanced past the DER structure. It returns the
+ * result or NULL on error. */
+RSA *d2i_RSAPublicKey(RSA **out, const uint8_t **inp, long len);
+
+/* i2d_RSAPublicKey marshals |in| to an ASN.1, DER structure. If |outp| is not
+ * NULL then the result is written to |*outp| and |*outp| is advanced just past
+ * the output. It returns the number of bytes in the result, whether written or
+ * not, or a negative value on error. */
+int i2d_RSAPublicKey(const RSA *in, uint8_t **outp);
+
+/* d2i_RSAPrivateKey parses an ASN.1, DER-encoded, RSA private key from |len|
+ * bytes at |*inp|. If |out| is not NULL then, on exit, a pointer to the result
+ * is in |*out|. If |*out| is already non-NULL on entry then the result is
+ * written directly into |*out|, otherwise a fresh |RSA| is allocated. On
+ * successful exit, |*inp| is advanced past the DER structure. It returns the
+ * result or NULL on error. */
+RSA *d2i_RSAPrivateKey(RSA **out, const uint8_t **inp, long len);
+
+/* i2d_RSAPrivateKey marshals |in| to an ASN.1, DER structure. If |outp| is not
+ * NULL then the result is written to |*outp| and |*outp| is advanced just past
+ * the output. It returns the number of bytes in the result, whether written or
+ * not, or a negative value on error. */
+int i2d_RSAPrivateKey(const RSA *in, uint8_t **outp);
+
+
+/* ex_data functions.
+ *
+ * These functions are wrappers. See |ex_data.h| for details. */
+
+int RSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+                         CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int RSA_set_ex_data(RSA *r, int idx, void *arg);
+void *RSA_get_ex_data(const RSA *r, int idx);
+
+
+/* RSA_FLAG_CACHE_PUBLIC causes a precomputed Montgomery context to be created,
+ * on demand, for the public key operations. */
+#define RSA_FLAG_CACHE_PUBLIC 2
+
+/* RSA_FLAG_CACHE_PRIVATE causes a precomputed Montgomery context to be
+ * created, on demand, for the private key operations. */
+#define RSA_FLAG_CACHE_PRIVATE 4
+
+/* RSA_FLAG_NO_BLINDING disables blinding of private operations. */
+#define RSA_FLAG_NO_BLINDING 8
+
+/* RSA_FLAG_EXT_PKEY means that private key operations will be handled by
+ * |mod_exp| and that they do not depend on the private key components being
+ * present: for example a key stored in external hardware. */
+#define RSA_FLAG_EXT_PKEY 0x20
+
+/* RSA_FLAG_SIGN_VER causes the |sign| and |verify| functions of |rsa_meth_st|
+ * to be called when set. */
+#define RSA_FLAG_SIGN_VER 0x40
+
+
+/* RSA public exponent values. */
+
+#define RSA_3 0x3
+#define RSA_F4 0x10001
+
+
+struct rsa_meth_st {
+  struct openssl_method_common_st common;
+
+  void *app_data;
+
+  int (*init)(RSA *rsa);
+  int (*finish)(RSA *rsa);
+
+  int (*sign)(int type, const uint8_t *m, unsigned int m_length,
+              uint8_t *sigret, unsigned int *siglen, const RSA *rsa);
+
+  int (*verify)(int dtype, const uint8_t *m, unsigned int m_length,
+                const uint8_t *sigbuf, unsigned int siglen, const RSA *rsa);
+
+
+  /* These functions mirror the |RSA_*| functions of the same name. */
+  int (*encrypt)(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                 const uint8_t *in, size_t in_len, int padding);
+  int (*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);
+
+  int (*decrypt)(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                 const uint8_t *in, size_t in_len, int padding);
+  int (*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);
+
+  int (*mod_exp)(BIGNUM *r0, const BIGNUM *I, RSA *rsa,
+                 BN_CTX *ctx); /* Can be null */
+  int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                    const BIGNUM *m, BN_CTX *ctx,
+                    BN_MONT_CTX *m_ctx);
+
+  int flags;
+
+  int (*keygen)(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
+};
+
+
+/* Private functions. */
+
+typedef struct bn_blinding_st BN_BLINDING;
+
+struct rsa_st {
+  /* version is only used during ASN.1 (de)serialisation. */
+  long version;
+  RSA_METHOD *meth;
+
+  BIGNUM *n;
+  BIGNUM *e;
+  BIGNUM *d;
+  BIGNUM *p;
+  BIGNUM *q;
+  BIGNUM *dmp1;
+  BIGNUM *dmq1;
+  BIGNUM *iqmp;
+  /* be careful using this if the RSA structure is shared */
+  CRYPTO_EX_DATA ex_data;
+  int references;
+  int flags;
+
+  /* Used to cache montgomery values */
+  BN_MONT_CTX *_method_mod_n;
+  BN_MONT_CTX *_method_mod_p;
+  BN_MONT_CTX *_method_mod_q;
+
+  /* num_blindings contains the size of the |blindings| and |blindings_inuse|
+   * arrays. This member and the |blindings_inuse| array are protected by
+   * CRYPTO_LOCK_RSA_BLINDING. */
+  unsigned num_blindings;
+  /* blindings is an array of BN_BLINDING structures that can be reserved by a
+   * thread by locking CRYPTO_LOCK_RSA_BLINDING and changing the corresponding
+   * element in |blindings_inuse| from 0 to 1. */
+  BN_BLINDING **blindings;
+  unsigned char *blindings_inuse;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#define RSA_F_RSA_padding_check_none 100
+#define RSA_F_RSA_padding_add_none 101
+#define RSA_F_RSA_padding_check_PKCS1_OAEP_mgf1 102
+#define RSA_F_RSA_verify_PKCS1_PSS_mgf1 103
+#define RSA_F_RSA_padding_add_PKCS1_PSS_mgf1 104
+#define RSA_F_RSA_verify 105
+#define RSA_F_rsa_setup_blinding 106
+#define RSA_F_verify_raw 107
+#define RSA_F_RSA_padding_add_PKCS1_type_1 108
+#define RSA_F_keygen 109
+#define RSA_F_RSA_padding_add_PKCS1_OAEP_mgf1 110
+#define RSA_F_pkcs1_prefixed_msg 111
+#define RSA_F_BN_BLINDING_update 112
+#define RSA_F_RSA_padding_check_SSLv23 113
+#define RSA_F_RSA_padding_add_SSLv23 114
+#define RSA_F_BN_BLINDING_new 115
+#define RSA_F_RSA_padding_add_PKCS1_type_2 116
+#define RSA_F_BN_BLINDING_convert_ex 117
+#define RSA_F_BN_BLINDING_invert_ex 118
+#define RSA_F_encrypt 119
+#define RSA_F_sign_raw 120
+#define RSA_F_RSA_new_method 121
+#define RSA_F_RSA_padding_check_PKCS1_type_1 122
+#define RSA_F_RSA_sign 123
+#define RSA_F_BN_BLINDING_create_param 124
+#define RSA_F_decrypt 125
+#define RSA_F_RSA_padding_check_PKCS1_type_2 126
+#define RSA_R_INVALID_MESSAGE_LENGTH 100
+#define RSA_R_DATA_GREATER_THAN_MOD_LEN 101
+#define RSA_R_NO_PUBLIC_EXPONENT 102
+#define RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE 103
+#define RSA_R_BLOCK_TYPE_IS_NOT_01 104
+#define RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE 105
+#define RSA_R_UNKNOWN_PADDING_TYPE 106
+#define RSA_R_TOO_MANY_ITERATIONS 107
+#define RSA_R_SLEN_RECOVERY_FAILED 108
+#define RSA_R_WRONG_SIGNATURE_LENGTH 109
+#define RSA_R_MODULUS_TOO_LARGE 110
+#define RSA_R_NULL_BEFORE_BLOCK_MISSING 111
+#define RSA_R_DATA_TOO_LARGE 112
+#define RSA_R_OUTPUT_BUFFER_TOO_SMALL 113
+#define RSA_R_SLEN_CHECK_FAILED 114
+#define RSA_R_FIRST_OCTET_INVALID 115
+#define RSA_R_BAD_E_VALUE 116
+#define RSA_R_DATA_TOO_LARGE_FOR_MODULUS 117
+#define RSA_R_EMPTY_PUBLIC_KEY 118
+#define RSA_R_BAD_PAD_BYTE_COUNT 119
+#define RSA_R_OAEP_DECODING_ERROR 120
+#define RSA_R_TOO_LONG 121
+#define RSA_R_BAD_FIXED_HEADER_DECRYPT 122
+#define RSA_R_DATA_TOO_SMALL 123
+#define RSA_R_UNKNOWN_ALGORITHM_TYPE 124
+#define RSA_R_PADDING_CHECK_FAILED 125
+#define RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD 126
+#define RSA_R_BLOCK_TYPE_IS_NOT_02 127
+#define RSA_R_LAST_OCTET_INVALID 128
+#define RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY 129
+#define RSA_R_SSLV3_ROLLBACK_ATTACK 130
+#define RSA_R_KEY_SIZE_TOO_SMALL 131
+#define RSA_R_BAD_SIGNATURE 132
+#define RSA_R_BN_NOT_INITIALIZED 133
+
+#endif  /* OPENSSL_HEADER_RSA_H */
diff --git a/crypto/rsa/rsa_asn1.c b/crypto/rsa/rsa_asn1.c
new file mode 100644
index 0000000..924cb8a
--- /dev/null
+++ b/crypto/rsa/rsa_asn1.c
@@ -0,0 +1,125 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2000.
+ */
+/* ====================================================================
+ * Copyright (c) 2000-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/rsa.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+#include "internal.h"
+
+
+/* Override the default free and new methods */
+static int rsa_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                  void *exarg) {
+  if (operation == ASN1_OP_NEW_PRE) {
+    *pval = (ASN1_VALUE *)RSA_new();
+    if (*pval) {
+      return 2;
+    }
+    return 0;
+  } else if (operation == ASN1_OP_FREE_PRE) {
+    RSA_free((RSA *)*pval);
+    *pval = NULL;
+    return 2;
+  }
+  return 1;
+}
+
+ASN1_SEQUENCE_cb(RSAPrivateKey, rsa_cb) = {
+  ASN1_SIMPLE(RSA, version, LONG),
+  ASN1_SIMPLE(RSA, n, BIGNUM),
+  ASN1_SIMPLE(RSA, e, BIGNUM),
+  ASN1_SIMPLE(RSA, d, BIGNUM),
+  ASN1_SIMPLE(RSA, p, BIGNUM),
+  ASN1_SIMPLE(RSA, q, BIGNUM),
+  ASN1_SIMPLE(RSA, dmp1, BIGNUM),
+  ASN1_SIMPLE(RSA, dmq1, BIGNUM),
+  ASN1_SIMPLE(RSA, iqmp, BIGNUM),
+} ASN1_SEQUENCE_END_cb(RSA, RSAPrivateKey);
+
+ASN1_SEQUENCE_cb(RSAPublicKey, rsa_cb) = {
+    ASN1_SIMPLE(RSA, n, BIGNUM),
+    ASN1_SIMPLE(RSA, e, BIGNUM),
+} ASN1_SEQUENCE_END_cb(RSA, RSAPublicKey);
+
+ASN1_SEQUENCE(RSA_PSS_PARAMS) = {
+  ASN1_EXP_OPT(RSA_PSS_PARAMS, hashAlgorithm, X509_ALGOR,0),
+  ASN1_EXP_OPT(RSA_PSS_PARAMS, maskGenAlgorithm, X509_ALGOR,1),
+  ASN1_EXP_OPT(RSA_PSS_PARAMS, saltLength, ASN1_INTEGER,2),
+  ASN1_EXP_OPT(RSA_PSS_PARAMS, trailerField, ASN1_INTEGER,3),
+} ASN1_SEQUENCE_END(RSA_PSS_PARAMS);
+
+IMPLEMENT_ASN1_FUNCTIONS(RSA_PSS_PARAMS);
+
+ASN1_SEQUENCE(RSA_OAEP_PARAMS) = {
+  ASN1_EXP_OPT(RSA_OAEP_PARAMS, hashFunc, X509_ALGOR, 0),
+  ASN1_EXP_OPT(RSA_OAEP_PARAMS, maskGenFunc, X509_ALGOR, 1),
+  ASN1_EXP_OPT(RSA_OAEP_PARAMS, pSourceFunc, X509_ALGOR, 2),
+} ASN1_SEQUENCE_END(RSA_OAEP_PARAMS);
+
+IMPLEMENT_ASN1_FUNCTIONS(RSA_OAEP_PARAMS);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(RSA, RSAPrivateKey, RSAPrivateKey);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(RSA, RSAPublicKey, RSAPublicKey);
+
+RSA *RSAPublicKey_dup(const RSA *rsa) {
+  return ASN1_item_dup(ASN1_ITEM_rptr(RSAPublicKey), (RSA *) rsa);
+}
+
+RSA *RSAPrivateKey_dup(const RSA *rsa) {
+  return ASN1_item_dup(ASN1_ITEM_rptr(RSAPrivateKey), (RSA *) rsa);
+}
diff --git a/crypto/rsa/rsa_error.c b/crypto/rsa/rsa_error.c
new file mode 100644
index 0000000..d8b10d5
--- /dev/null
+++ b/crypto/rsa/rsa_error.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "rsa.h"
+
+const ERR_STRING_DATA RSA_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_BN_BLINDING_convert_ex, 0), "BN_BLINDING_convert_ex"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_BN_BLINDING_create_param, 0), "BN_BLINDING_create_param"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_BN_BLINDING_invert_ex, 0), "BN_BLINDING_invert_ex"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_BN_BLINDING_new, 0), "BN_BLINDING_new"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_BN_BLINDING_update, 0), "BN_BLINDING_update"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_new_method, 0), "RSA_new_method"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_add_PKCS1_OAEP_mgf1, 0), "RSA_padding_add_PKCS1_OAEP_mgf1"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_add_PKCS1_PSS_mgf1, 0), "RSA_padding_add_PKCS1_PSS_mgf1"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_add_PKCS1_type_1, 0), "RSA_padding_add_PKCS1_type_1"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_add_PKCS1_type_2, 0), "RSA_padding_add_PKCS1_type_2"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_add_SSLv23, 0), "RSA_padding_add_SSLv23"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_add_none, 0), "RSA_padding_add_none"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_PKCS1_OAEP_mgf1, 0), "RSA_padding_check_PKCS1_OAEP_mgf1"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_PKCS1_type_1, 0), "RSA_padding_check_PKCS1_type_1"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_PKCS1_type_2, 0), "RSA_padding_check_PKCS1_type_2"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_SSLv23, 0), "RSA_padding_check_SSLv23"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_none, 0), "RSA_padding_check_none"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_sign, 0), "RSA_sign"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_verify, 0), "RSA_verify"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_verify_PKCS1_PSS_mgf1, 0), "RSA_verify_PKCS1_PSS_mgf1"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_decrypt, 0), "decrypt"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_encrypt, 0), "encrypt"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_keygen, 0), "keygen"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_pkcs1_prefixed_msg, 0), "pkcs1_prefixed_msg"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_rsa_setup_blinding, 0), "rsa_setup_blinding"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_sign_raw, 0), "sign_raw"},
+  {ERR_PACK(ERR_LIB_RSA, RSA_F_verify_raw, 0), "verify_raw"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_E_VALUE), "BAD_E_VALUE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_FIXED_HEADER_DECRYPT), "BAD_FIXED_HEADER_DECRYPT"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_PAD_BYTE_COUNT), "BAD_PAD_BYTE_COUNT"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_SIGNATURE), "BAD_SIGNATURE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BLOCK_TYPE_IS_NOT_01), "BLOCK_TYPE_IS_NOT_01"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BLOCK_TYPE_IS_NOT_02), "BLOCK_TYPE_IS_NOT_02"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BN_NOT_INITIALIZED), "BN_NOT_INITIALIZED"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_GREATER_THAN_MOD_LEN), "DATA_GREATER_THAN_MOD_LEN"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE), "DATA_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE), "DATA_TOO_LARGE_FOR_KEY_SIZE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_MODULUS), "DATA_TOO_LARGE_FOR_MODULUS"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_SMALL), "DATA_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE), "DATA_TOO_SMALL_FOR_KEY_SIZE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY), "DIGEST_TOO_BIG_FOR_RSA_KEY"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_EMPTY_PUBLIC_KEY), "EMPTY_PUBLIC_KEY"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_FIRST_OCTET_INVALID), "FIRST_OCTET_INVALID"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_INVALID_MESSAGE_LENGTH), "INVALID_MESSAGE_LENGTH"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_KEY_SIZE_TOO_SMALL), "KEY_SIZE_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_LAST_OCTET_INVALID), "LAST_OCTET_INVALID"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_MODULUS_TOO_LARGE), "MODULUS_TOO_LARGE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_NO_PUBLIC_EXPONENT), "NO_PUBLIC_EXPONENT"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_NULL_BEFORE_BLOCK_MISSING), "NULL_BEFORE_BLOCK_MISSING"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_OAEP_DECODING_ERROR), "OAEP_DECODING_ERROR"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_OUTPUT_BUFFER_TOO_SMALL), "OUTPUT_BUFFER_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_PADDING_CHECK_FAILED), "PADDING_CHECK_FAILED"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SLEN_CHECK_FAILED), "SLEN_CHECK_FAILED"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SLEN_RECOVERY_FAILED), "SLEN_RECOVERY_FAILED"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SSLV3_ROLLBACK_ATTACK), "SSLV3_ROLLBACK_ATTACK"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD), "THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_TOO_LONG), "TOO_LONG"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_TOO_MANY_ITERATIONS), "TOO_MANY_ITERATIONS"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_UNKNOWN_ALGORITHM_TYPE), "UNKNOWN_ALGORITHM_TYPE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_UNKNOWN_PADDING_TYPE), "UNKNOWN_PADDING_TYPE"},
+  {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_WRONG_SIGNATURE_LENGTH), "WRONG_SIGNATURE_LENGTH"},
+  {0, NULL},
+};
diff --git a/crypto/rsa/rsa_impl.c b/crypto/rsa/rsa_impl.c
new file mode 100644
index 0000000..e7aed9f
--- /dev/null
+++ b/crypto/rsa/rsa_impl.c
@@ -0,0 +1,1006 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/rsa.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+#define OPENSSL_RSA_MAX_MODULUS_BITS 16384
+#define OPENSSL_RSA_SMALL_MODULUS_BITS 3072
+#define OPENSSL_RSA_MAX_PUBEXP_BITS \
+  64 /* exponent limit enforced for "large" modulus only */
+
+
+static int finish(RSA *rsa) {
+  if (rsa->_method_mod_n != NULL) {
+    BN_MONT_CTX_free(rsa->_method_mod_n);
+  }
+  if (rsa->_method_mod_p != NULL) {
+    BN_MONT_CTX_free(rsa->_method_mod_p);
+  }
+  if (rsa->_method_mod_q != NULL) {
+    BN_MONT_CTX_free(rsa->_method_mod_q);
+  }
+
+  return 1;
+}
+
+static int encrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                   const uint8_t *in, size_t in_len, int padding) {
+  const unsigned rsa_size = RSA_size(rsa);
+  unsigned i, j, k;
+  BIGNUM *f, *result;
+  uint8_t *buf = NULL;
+  BN_CTX *ctx = NULL;
+  int ret = 0;
+
+  if (rsa_size > OPENSSL_RSA_MAX_MODULUS_BITS) {
+    OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_MODULUS_TOO_LARGE);
+    return 0;
+  }
+
+  if (max_out < rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (BN_ucmp(rsa->n, rsa->e) <= 0) {
+    OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_BAD_E_VALUE);
+    return 0;
+  }
+
+  /* for large moduli, enforce exponent limit */
+  if (BN_num_bits(rsa->n) > OPENSSL_RSA_SMALL_MODULUS_BITS &&
+      BN_num_bits(rsa->e) > OPENSSL_RSA_MAX_PUBEXP_BITS) {
+    OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_BAD_E_VALUE);
+    return 0;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+  BN_CTX_start(ctx);
+  f = BN_CTX_get(ctx);
+  result = BN_CTX_get(ctx);
+  buf = OPENSSL_malloc(rsa_size);
+  if (!f || !result || !buf) {
+    OPENSSL_PUT_ERROR(RSA, encrypt, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  switch (padding) {
+    case RSA_PKCS1_PADDING:
+      i = RSA_padding_add_PKCS1_type_2(buf, rsa_size, in, in_len);
+      break;
+    case RSA_PKCS1_OAEP_PADDING:
+      i = RSA_padding_add_PKCS1_OAEP(buf, rsa_size, in, in_len, NULL, 0);
+      break;
+    case RSA_SSLV23_PADDING:
+      i = RSA_padding_add_SSLv23(buf, rsa_size, in, in_len);
+      break;
+    case RSA_NO_PADDING:
+      i = RSA_padding_add_none(buf, rsa_size, in, in_len);
+      break;
+    default:
+      OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_UNKNOWN_PADDING_TYPE);
+      goto err;
+  }
+
+  if (i <= 0) {
+    goto err;
+  }
+
+  if (BN_bin2bn(buf, rsa_size, f) == NULL) {
+    goto err;
+  }
+
+  if (BN_ucmp(f, rsa->n) >= 0) {
+    /* usually the padding functions would catch this */
+    OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+    goto err;
+  }
+
+  if (rsa->flags & RSA_FLAG_CACHE_PUBLIC) {
+    if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n,
+                                ctx)) {
+      goto err;
+    }
+  }
+
+  if (!rsa->meth->bn_mod_exp(result, f, rsa->e, rsa->n, ctx, rsa->_method_mod_n)) {
+    goto err;
+  }
+
+  /* put in leading 0 bytes if the number is less than the length of the
+   * modulus */
+  j = BN_num_bytes(result);
+  i = BN_bn2bin(result, &(out[rsa_size - j]));
+  for (k = 0; k < rsa_size - i; k++) {
+    out[k] = 0;
+  }
+
+  *out_len = rsa_size;
+  ret = 1;
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  if (buf != NULL) {
+    OPENSSL_cleanse(buf, rsa_size);
+    OPENSSL_free(buf);
+  }
+
+  return ret;
+}
+
+/* MAX_BLINDINGS_PER_RSA defines the maximum number of cached BN_BLINDINGs per
+ * RSA*. Then this limit is exceeded, BN_BLINDING objects will be created and
+ * destroyed as needed. */
+#define MAX_BLINDINGS_PER_RSA 1024
+
+/* rsa_blinding_get returns a BN_BLINDING to use with |rsa|. It does this by
+ * allocating one of the cached BN_BLINDING objects in |rsa->blindings|. If
+ * none are free, the cache will be extended by a extra element and the new
+ * BN_BLINDING is returned.
+ *
+ * On success, the index of the assigned BN_BLINDING is written to
+ * |*index_used| and must be passed to |rsa_blinding_release| when finished. */
+static BN_BLINDING *rsa_blinding_get(RSA *rsa, unsigned *index_used,
+                                     BN_CTX *ctx) {
+  BN_BLINDING *ret = NULL;
+  BN_BLINDING **new_blindings;
+  uint8_t *new_blindings_inuse;
+  char overflow = 0;
+
+  CRYPTO_w_lock(CRYPTO_LOCK_RSA_BLINDING);
+  if (rsa->num_blindings > 0) {
+    unsigned i, starting_index;
+    CRYPTO_THREADID threadid;
+
+    /* We start searching the array at a value based on the
+     * threadid in order to try avoid bouncing the BN_BLINDING
+     * values around different threads. It's harmless if
+     * threadid.val is always set to zero. */
+    CRYPTO_THREADID_current(&threadid);
+    starting_index = threadid.val % rsa->num_blindings;
+
+    for (i = starting_index;;) {
+      if (rsa->blindings_inuse[i] == 0) {
+        rsa->blindings_inuse[i] = 1;
+        ret = rsa->blindings[i];
+        *index_used = i;
+        break;
+      }
+      i++;
+      if (i == rsa->num_blindings) {
+        i = 0;
+      }
+      if (i == starting_index) {
+        break;
+      }
+    }
+  }
+
+  if (ret != NULL) {
+    CRYPTO_w_unlock(CRYPTO_LOCK_RSA_BLINDING);
+    return ret;
+  }
+
+  overflow = rsa->num_blindings >= MAX_BLINDINGS_PER_RSA;
+
+  /* We didn't find a free BN_BLINDING to use so increase the length of
+   * the arrays by one and use the newly created element. */
+
+  CRYPTO_w_unlock(CRYPTO_LOCK_RSA_BLINDING);
+  ret = rsa_setup_blinding(rsa, ctx);
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  if (overflow) {
+    /* We cannot add any more cached BN_BLINDINGs so we use |ret|
+     * and mark it for destruction in |rsa_blinding_release|. */
+    *index_used = MAX_BLINDINGS_PER_RSA;
+    return ret;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_RSA_BLINDING);
+
+  new_blindings =
+      OPENSSL_malloc(sizeof(BN_BLINDING *) * (rsa->num_blindings + 1));
+  if (new_blindings == NULL) {
+    goto err1;
+  }
+  memcpy(new_blindings, rsa->blindings,
+         sizeof(BN_BLINDING *) * rsa->num_blindings);
+  new_blindings[rsa->num_blindings] = ret;
+
+  new_blindings_inuse = OPENSSL_malloc(rsa->num_blindings + 1);
+  if (new_blindings_inuse == NULL) {
+    goto err2;
+  }
+  memcpy(new_blindings_inuse, rsa->blindings_inuse, rsa->num_blindings);
+  new_blindings_inuse[rsa->num_blindings] = 1;
+  *index_used = rsa->num_blindings;
+
+  if (rsa->blindings != NULL) {
+    OPENSSL_free(rsa->blindings);
+  }
+  rsa->blindings = new_blindings;
+  if (rsa->blindings_inuse != NULL) {
+    OPENSSL_free(rsa->blindings_inuse);
+  }
+  rsa->blindings_inuse = new_blindings_inuse;
+  rsa->num_blindings++;
+
+  CRYPTO_w_unlock(CRYPTO_LOCK_RSA_BLINDING);
+  return ret;
+
+err2:
+  OPENSSL_free(new_blindings);
+
+err1:
+  CRYPTO_w_unlock(CRYPTO_LOCK_RSA_BLINDING);
+  BN_BLINDING_free(ret);
+  return NULL;
+}
+
+/* rsa_blinding_release marks the cached BN_BLINDING at the given index as free
+ * for other threads to use. */
+static void rsa_blinding_release(RSA *rsa, BN_BLINDING *blinding,
+                                 unsigned blinding_index) {
+  if (blinding_index == MAX_BLINDINGS_PER_RSA) {
+    /* This blinding wasn't cached. */
+    BN_BLINDING_free(blinding);
+    return;
+  }
+
+  CRYPTO_w_lock(CRYPTO_LOCK_RSA_BLINDING);
+  rsa->blindings_inuse[blinding_index] = 0;
+  CRYPTO_w_unlock(CRYPTO_LOCK_RSA_BLINDING);
+}
+
+/* signing */
+static int 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) {
+  const unsigned rsa_size = RSA_size(rsa);
+  BIGNUM *f, *result;
+  unsigned i, j, k;
+  uint8_t *buf = NULL;
+  BN_CTX *ctx = NULL;
+  unsigned blinding_index = 0;
+  BN_BLINDING *blinding = NULL;
+  int ret = 0;
+
+  if (max_out < rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  f = BN_CTX_get(ctx);
+  result = BN_CTX_get(ctx);
+  buf = OPENSSL_malloc(rsa_size);
+  if (!f || !result || !buf) {
+    OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  switch (padding) {
+    case RSA_PKCS1_PADDING:
+      i = RSA_padding_add_PKCS1_type_1(buf, rsa_size, in, in_len);
+      break;
+    case RSA_NO_PADDING:
+      i = RSA_padding_add_none(buf, rsa_size, in, in_len);
+      break;
+    default:
+      OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_UNKNOWN_PADDING_TYPE);
+      goto err;
+  }
+  if (i <= 0) {
+    goto err;
+  }
+
+  if (BN_bin2bn(buf, rsa_size, f) == NULL) {
+    goto err;
+  }
+
+  if (BN_ucmp(f, rsa->n) >= 0) {
+    /* usually the padding functions would catch this */
+    OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+    goto err;
+  }
+
+  if (!(rsa->flags & RSA_FLAG_NO_BLINDING)) {
+    blinding = rsa_blinding_get(rsa, &blinding_index, ctx);
+    if (blinding == NULL) {
+      OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+    if (!BN_BLINDING_convert_ex(f, NULL, blinding, ctx)) {
+      goto err;
+    }
+  }
+
+  if ((rsa->flags & RSA_FLAG_EXT_PKEY) ||
+      ((rsa->p != NULL) && (rsa->q != NULL) && (rsa->dmp1 != NULL) &&
+       (rsa->dmq1 != NULL) && (rsa->iqmp != NULL))) {
+    if (!rsa->meth->mod_exp(result, f, rsa, ctx)) {
+      goto err;
+    }
+  } else {
+    BIGNUM local_d;
+    BIGNUM *d = NULL;
+
+    BN_init(&local_d);
+    d = &local_d;
+    BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+    if (rsa->flags & RSA_FLAG_CACHE_PUBLIC) {
+      if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n,
+                                  ctx)) {
+        goto err;
+      }
+    }
+
+    if (!rsa->meth->bn_mod_exp(result, f, d, rsa->n, ctx, rsa->_method_mod_n)) {
+      goto err;
+    }
+  }
+
+  if (blinding) {
+    if (!BN_BLINDING_invert_ex(result, NULL, blinding, ctx)) {
+      goto err;
+    }
+  }
+
+  /* put in leading 0 bytes if the number is less than the
+   * length of the modulus */
+  j = BN_num_bytes(result);
+  i = BN_bn2bin(result, &(out[rsa_size - j]));
+  for (k = 0; k < rsa_size - i; k++) {
+    out[k] = 0;
+  }
+
+  *out_len = rsa_size;
+  ret = 1;
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  if (buf != NULL) {
+    OPENSSL_cleanse(buf, rsa_size);
+    OPENSSL_free(buf);
+  }
+  if (blinding != NULL) {
+    rsa_blinding_release(rsa, blinding, blinding_index);
+  }
+
+  return ret;
+}
+
+static int decrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
+                   const uint8_t *in, size_t in_len, int padding) {
+  const unsigned rsa_size = RSA_size(rsa);
+  unsigned j;
+  BIGNUM *f, *result;
+  int r = -1;
+  uint8_t *buf = NULL;
+  BN_CTX *ctx = NULL;
+  unsigned blinding_index;
+  BN_BLINDING *blinding = NULL;
+  int ret = 0;
+
+  if (max_out < rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  f = BN_CTX_get(ctx);
+  result = BN_CTX_get(ctx);
+  buf = OPENSSL_malloc(rsa_size);
+  if (!f || !result || !buf) {
+    OPENSSL_PUT_ERROR(RSA, decrypt, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  /* This check was for equality but PGP does evil things
+   * and chops off the top '0' bytes.
+   * TODO(fork): investigate this. */
+  if (in_len > rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_DATA_GREATER_THAN_MOD_LEN);
+    goto err;
+  }
+
+  /* make data into a big number */
+  if (BN_bin2bn(in, (int)in_len, f) == NULL) {
+    goto err;
+  }
+
+  if (BN_ucmp(f, rsa->n) >= 0) {
+    OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+    goto err;
+  }
+
+  if (!(rsa->flags & RSA_FLAG_NO_BLINDING)) {
+    blinding = rsa_blinding_get(rsa, &blinding_index, ctx);
+    if (blinding == NULL) {
+      OPENSSL_PUT_ERROR(RSA, decrypt, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+    if (!BN_BLINDING_convert_ex(f, NULL, blinding, ctx)) {
+      goto err;
+    }
+  }
+
+  /* do the decrypt */
+  if ((rsa->flags & RSA_FLAG_EXT_PKEY) ||
+      ((rsa->p != NULL) && (rsa->q != NULL) && (rsa->dmp1 != NULL) &&
+       (rsa->dmq1 != NULL) && (rsa->iqmp != NULL))) {
+    if (!rsa->meth->mod_exp(result, f, rsa, ctx)) {
+      goto err;
+    }
+  } else {
+    BIGNUM local_d;
+    BIGNUM *d = NULL;
+
+    d = &local_d;
+    BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+    if (rsa->flags & RSA_FLAG_CACHE_PUBLIC) {
+      if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n,
+                                  ctx)) {
+        goto err;
+      }
+    }
+    if (!rsa->meth->bn_mod_exp(result, f, d, rsa->n, ctx, rsa->_method_mod_n)) {
+      goto err;
+    }
+  }
+
+  if (blinding) {
+    if (!BN_BLINDING_invert_ex(result, NULL, blinding, ctx)) {
+      goto err;
+    }
+  }
+
+  j = BN_bn2bin(result, buf); /* j is only used with no-padding mode */
+
+  switch (padding) {
+    case RSA_PKCS1_PADDING:
+      r = RSA_padding_check_PKCS1_type_2(out, rsa_size, buf, j, rsa_size);
+      break;
+    case RSA_PKCS1_OAEP_PADDING:
+      r = RSA_padding_check_PKCS1_OAEP(out, rsa_size, buf, j, rsa_size, NULL, 0);
+      break;
+    case RSA_SSLV23_PADDING:
+      r = RSA_padding_check_SSLv23(out, rsa_size, buf, j, rsa_size);
+      break;
+    case RSA_NO_PADDING:
+      r = RSA_padding_check_none(out, rsa_size, buf, j, rsa_size);
+      break;
+    default:
+      OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_UNKNOWN_PADDING_TYPE);
+      goto err;
+  }
+
+  if (r < 0) {
+    OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_PADDING_CHECK_FAILED);
+  } else {
+    *out_len = r;
+    ret = 1;
+  }
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  if (buf != NULL) {
+    OPENSSL_cleanse(buf, rsa_size);
+    OPENSSL_free(buf);
+  }
+  if (blinding != NULL) {
+    rsa_blinding_release(rsa, blinding, blinding_index);
+  }
+
+  return ret;
+}
+
+static int 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) {
+  const unsigned rsa_size = RSA_size(rsa);
+  BIGNUM *f, *result;
+  int ret = 0;
+  int i, r = -1;
+  uint8_t *buf = NULL;
+  BN_CTX *ctx = NULL;
+
+  if (BN_num_bits(rsa->n) > OPENSSL_RSA_MAX_MODULUS_BITS) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_MODULUS_TOO_LARGE);
+    return 0;
+  }
+
+  if (BN_ucmp(rsa->n, rsa->e) <= 0) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_BAD_E_VALUE);
+    return 0;
+  }
+
+  if (max_out < rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  /* for large moduli, enforce exponent limit */
+  if (BN_num_bits(rsa->n) > OPENSSL_RSA_SMALL_MODULUS_BITS &&
+      BN_num_bits(rsa->e) > OPENSSL_RSA_MAX_PUBEXP_BITS) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_BAD_E_VALUE);
+    return 0;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+  BN_CTX_start(ctx);
+  f = BN_CTX_get(ctx);
+  result = BN_CTX_get(ctx);
+  buf = OPENSSL_malloc(rsa_size);
+  if (!f || !result || !buf) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  /* This check was for equality but PGP does evil things
+   * and chops off the top '0' bytes.
+   * TODO(fork): investigate */
+  if (in_len > rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_DATA_GREATER_THAN_MOD_LEN);
+    goto err;
+  }
+
+  if (BN_bin2bn(in, in_len, f) == NULL) {
+    goto err;
+  }
+
+  if (BN_ucmp(f, rsa->n) >= 0) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+    goto err;
+  }
+
+  if (rsa->flags & RSA_FLAG_CACHE_PUBLIC) {
+    if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n,
+                                ctx)) {
+      goto err;
+    }
+  }
+
+  if (!rsa->meth->bn_mod_exp(result, f, rsa->e, rsa->n, ctx,
+                             rsa->_method_mod_n)) {
+    goto err;
+  }
+
+  i = BN_bn2bin(result, buf);
+
+  switch (padding) {
+    case RSA_PKCS1_PADDING:
+      r = RSA_padding_check_PKCS1_type_1(out, rsa_size, buf, i, rsa_size);
+      break;
+    case RSA_NO_PADDING:
+      r = RSA_padding_check_none(out, rsa_size, buf, i, rsa_size);
+      break;
+    default:
+      OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_UNKNOWN_PADDING_TYPE);
+      goto err;
+  }
+
+  if (r < 0) {
+    OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_PADDING_CHECK_FAILED);
+  } else {
+    *out_len = r;
+    ret = 1;
+  }
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  if (buf != NULL) {
+    OPENSSL_cleanse(buf, rsa_size);
+    OPENSSL_free(buf);
+  }
+  return ret;
+}
+
+static int mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) {
+  BIGNUM *r1, *m1, *vrfy;
+  BIGNUM local_dmp1, local_dmq1, local_c, local_r1;
+  BIGNUM *dmp1, *dmq1, *c, *pr1;
+  int ret = 0;
+
+  BN_CTX_start(ctx);
+  r1 = BN_CTX_get(ctx);
+  m1 = BN_CTX_get(ctx);
+  vrfy = BN_CTX_get(ctx);
+
+  {
+    BIGNUM local_p, local_q;
+    BIGNUM *p = NULL, *q = NULL;
+
+    /* Make sure BN_mod_inverse in Montgomery intialization uses the
+     * BN_FLG_CONSTTIME flag (unless RSA_FLAG_NO_CONSTTIME is set) */
+    BN_init(&local_p);
+    p = &local_p;
+    BN_with_flags(p, rsa->p, BN_FLG_CONSTTIME);
+
+    BN_init(&local_q);
+    q = &local_q;
+    BN_with_flags(q, rsa->q, BN_FLG_CONSTTIME);
+
+    if (rsa->flags & RSA_FLAG_CACHE_PRIVATE) {
+      if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_p, CRYPTO_LOCK_RSA, p, ctx)) {
+        goto err;
+      }
+      if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_q, CRYPTO_LOCK_RSA, q, ctx)) {
+        goto err;
+      }
+    }
+  }
+
+  if (rsa->flags & RSA_FLAG_CACHE_PUBLIC) {
+    if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n,
+                                ctx)) {
+      goto err;
+    }
+  }
+
+  /* compute I mod q */
+  c = &local_c;
+  BN_with_flags(c, I, BN_FLG_CONSTTIME);
+  if (!BN_mod(r1, c, rsa->q, ctx)) {
+    goto err;
+  }
+
+  /* compute r1^dmq1 mod q */
+  dmq1 = &local_dmq1;
+  BN_with_flags(dmq1, rsa->dmq1, BN_FLG_CONSTTIME);
+  if (!rsa->meth->bn_mod_exp(m1, r1, dmq1, rsa->q, ctx, rsa->_method_mod_q)) {
+    goto err;
+  }
+
+  /* compute I mod p */
+  c = &local_c;
+  BN_with_flags(c, I, BN_FLG_CONSTTIME);
+  if (!BN_mod(r1, c, rsa->p, ctx)) {
+    goto err;
+  }
+
+  /* compute r1^dmp1 mod p */
+  dmp1 = &local_dmp1;
+  BN_with_flags(dmp1, rsa->dmp1, BN_FLG_CONSTTIME);
+  if (!rsa->meth->bn_mod_exp(r0, r1, dmp1, rsa->p, ctx, rsa->_method_mod_p)) {
+    goto err;
+  }
+
+  if (!BN_sub(r0, r0, m1)) {
+    goto err;
+  }
+  /* This will help stop the size of r0 increasing, which does
+   * affect the multiply if it optimised for a power of 2 size */
+  if (BN_is_negative(r0)) {
+    if (!BN_add(r0, r0, rsa->p)) {
+      goto err;
+    }
+  }
+
+  if (!BN_mul(r1, r0, rsa->iqmp, ctx)) {
+    goto err;
+  }
+
+  /* Turn BN_FLG_CONSTTIME flag on before division operation */
+  pr1 = &local_r1;
+  BN_with_flags(pr1, r1, BN_FLG_CONSTTIME);
+
+  if (!BN_mod(r0, pr1, rsa->p, ctx)) {
+    goto err;
+  }
+
+  /* If p < q it is occasionally possible for the correction of
+   * adding 'p' if r0 is negative above to leave the result still
+   * negative. This can break the private key operations: the following
+   * second correction should *always* correct this rare occurrence.
+   * This will *never* happen with OpenSSL generated keys because
+   * they ensure p > q [steve] */
+  if (BN_is_negative(r0)) {
+    if (!BN_add(r0, r0, rsa->p)) {
+      goto err;
+    }
+  }
+  if (!BN_mul(r1, r0, rsa->q, ctx)) {
+    goto err;
+  }
+  if (!BN_add(r0, r1, m1)) {
+    goto err;
+  }
+
+  if (rsa->e && rsa->n) {
+    if (!rsa->meth->bn_mod_exp(vrfy, r0, rsa->e, rsa->n, ctx,
+                               rsa->_method_mod_n)) {
+      goto err;
+    }
+    /* If 'I' was greater than (or equal to) rsa->n, the operation
+     * will be equivalent to using 'I mod n'. However, the result of
+     * the verify will *always* be less than 'n' so we don't check
+     * for absolute equality, just congruency. */
+    if (!BN_sub(vrfy, vrfy, I)) {
+      goto err;
+    }
+    if (!BN_mod(vrfy, vrfy, rsa->n, ctx)) {
+      goto err;
+    }
+    if (BN_is_negative(vrfy)) {
+      if (!BN_add(vrfy, vrfy, rsa->n)) {
+        goto err;
+      }
+    }
+    if (!BN_is_zero(vrfy)) {
+      /* 'I' and 'vrfy' aren't congruent mod n. Don't leak
+       * miscalculated CRT output, just do a raw (slower)
+       * mod_exp and return that instead. */
+
+      BIGNUM local_d;
+      BIGNUM *d = NULL;
+
+      d = &local_d;
+      BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+      if (!rsa->meth->bn_mod_exp(r0, I, d, rsa->n, ctx, rsa->_method_mod_n)) {
+        goto err;
+      }
+    }
+  }
+  ret = 1;
+
+err:
+  BN_CTX_end(ctx);
+  return ret;
+}
+
+static int keygen(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
+  BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL, *r3 = NULL, *tmp;
+  BIGNUM local_r0, local_d, local_p;
+  BIGNUM *pr0, *d, *p;
+  int bitsp, bitsq, ok = -1, n = 0;
+  BN_CTX *ctx = NULL;
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+  BN_CTX_start(ctx);
+  r0 = BN_CTX_get(ctx);
+  r1 = BN_CTX_get(ctx);
+  r2 = BN_CTX_get(ctx);
+  r3 = BN_CTX_get(ctx);
+  if (r3 == NULL) {
+    goto err;
+  }
+
+  bitsp = (bits + 1) / 2;
+  bitsq = bits - bitsp;
+
+  /* We need the RSA components non-NULL */
+  if (!rsa->n && ((rsa->n = BN_new()) == NULL))
+    goto err;
+  if (!rsa->d && ((rsa->d = BN_new()) == NULL))
+    goto err;
+  if (!rsa->e && ((rsa->e = BN_new()) == NULL))
+    goto err;
+  if (!rsa->p && ((rsa->p = BN_new()) == NULL))
+    goto err;
+  if (!rsa->q && ((rsa->q = BN_new()) == NULL))
+    goto err;
+  if (!rsa->dmp1 && ((rsa->dmp1 = BN_new()) == NULL))
+    goto err;
+  if (!rsa->dmq1 && ((rsa->dmq1 = BN_new()) == NULL))
+    goto err;
+  if (!rsa->iqmp && ((rsa->iqmp = BN_new()) == NULL))
+    goto err;
+
+  BN_copy(rsa->e, e_value);
+
+  /* generate p and q */
+  for (;;) {
+    if (!BN_generate_prime_ex(rsa->p, bitsp, 0, NULL, NULL, cb))
+      goto err;
+    if (!BN_sub(r2, rsa->p, BN_value_one()))
+      goto err;
+    if (!BN_gcd(r1, r2, rsa->e, ctx))
+      goto err;
+    if (BN_is_one(r1))
+      break;
+    if (!BN_GENCB_call(cb, 2, n++))
+      goto err;
+  }
+  if (!BN_GENCB_call(cb, 3, 0))
+    goto err;
+  for (;;) {
+    /* When generating ridiculously small keys, we can get stuck
+     * continually regenerating the same prime values. Check for
+     * this and bail if it happens 3 times. */
+    unsigned int degenerate = 0;
+    do {
+      if (!BN_generate_prime_ex(rsa->q, bitsq, 0, NULL, NULL, cb))
+        goto err;
+    } while ((BN_cmp(rsa->p, rsa->q) == 0) && (++degenerate < 3));
+    if (degenerate == 3) {
+      ok = 0; /* we set our own err */
+      OPENSSL_PUT_ERROR(RSA, keygen, RSA_R_KEY_SIZE_TOO_SMALL);
+      goto err;
+    }
+    if (!BN_sub(r2, rsa->q, BN_value_one()))
+      goto err;
+    if (!BN_gcd(r1, r2, rsa->e, ctx))
+      goto err;
+    if (BN_is_one(r1))
+      break;
+    if (!BN_GENCB_call(cb, 2, n++))
+      goto err;
+  }
+  if (!BN_GENCB_call(cb, 3, 1))
+    goto err;
+  if (BN_cmp(rsa->p, rsa->q) < 0) {
+    tmp = rsa->p;
+    rsa->p = rsa->q;
+    rsa->q = tmp;
+  }
+
+  /* calculate n */
+  if (!BN_mul(rsa->n, rsa->p, rsa->q, ctx))
+    goto err;
+
+  /* calculate d */
+  if (!BN_sub(r1, rsa->p, BN_value_one()))
+    goto err; /* p-1 */
+  if (!BN_sub(r2, rsa->q, BN_value_one()))
+    goto err; /* q-1 */
+  if (!BN_mul(r0, r1, r2, ctx))
+    goto err; /* (p-1)(q-1) */
+  pr0 = &local_r0;
+  BN_with_flags(pr0, r0, BN_FLG_CONSTTIME);
+  if (!BN_mod_inverse(rsa->d, rsa->e, pr0, ctx))
+    goto err; /* d */
+
+  /* set up d for correct BN_FLG_CONSTTIME flag */
+  d = &local_d;
+  BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+  /* calculate d mod (p-1) */
+  if (!BN_mod(rsa->dmp1, d, r1, ctx))
+    goto err;
+
+  /* calculate d mod (q-1) */
+  if (!BN_mod(rsa->dmq1, d, r2, ctx))
+    goto err;
+
+  /* calculate inverse of q mod p */
+  p = &local_p;
+  BN_with_flags(p, rsa->p, BN_FLG_CONSTTIME);
+
+  if (!BN_mod_inverse(rsa->iqmp, rsa->q, p, ctx))
+    goto err;
+
+  ok = 1;
+
+err:
+  if (ok == -1) {
+    OPENSSL_PUT_ERROR(RSA, keygen, ERR_LIB_BN);
+    ok = 0;
+  }
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+
+  return ok;
+}
+
+const struct rsa_meth_st RSA_default_method = {
+  {
+    0 /* references */,
+    1 /* is_static */,
+  },
+  NULL /* app_data */,
+
+  NULL /* init */,
+  finish,
+
+  NULL /* sign */,
+  NULL /* verify */,
+
+  encrypt,
+  sign_raw,
+  decrypt,
+  verify_raw,
+
+  mod_exp /* mod_exp */,
+  BN_mod_exp_mont /* bn_mod_exp */,
+
+  RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE,
+
+  keygen,
+};
diff --git a/crypto/rsa/rsa_test.c b/crypto/rsa/rsa_test.c
new file mode 100644
index 0000000..e8d6d20
--- /dev/null
+++ b/crypto/rsa/rsa_test.c
@@ -0,0 +1,391 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/rsa.h>
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+
+
+#define SetKey                                              \
+  key->n = BN_bin2bn(n, sizeof(n) - 1, key->n);             \
+  key->e = BN_bin2bn(e, sizeof(e) - 1, key->e);             \
+  key->d = BN_bin2bn(d, sizeof(d) - 1, key->d);             \
+  key->p = BN_bin2bn(p, sizeof(p) - 1, key->p);             \
+  key->q = BN_bin2bn(q, sizeof(q) - 1, key->q);             \
+  key->dmp1 = BN_bin2bn(dmp1, sizeof(dmp1) - 1, key->dmp1); \
+  key->dmq1 = BN_bin2bn(dmq1, sizeof(dmq1) - 1, key->dmq1); \
+  key->iqmp = BN_bin2bn(iqmp, sizeof(iqmp) - 1, key->iqmp); \
+  memcpy(c, ctext_ex, sizeof(ctext_ex) - 1);                \
+  return (sizeof(ctext_ex) - 1);
+
+static int key1(RSA *key, unsigned char *c) {
+  static unsigned char n[] =
+      "\x00\xAA\x36\xAB\xCE\x88\xAC\xFD\xFF\x55\x52\x3C\x7F\xC4\x52\x3F"
+      "\x90\xEF\xA0\x0D\xF3\x77\x4A\x25\x9F\x2E\x62\xB4\xC5\xD9\x9C\xB5"
+      "\xAD\xB3\x00\xA0\x28\x5E\x53\x01\x93\x0E\x0C\x70\xFB\x68\x76\x93"
+      "\x9C\xE6\x16\xCE\x62\x4A\x11\xE0\x08\x6D\x34\x1E\xBC\xAC\xA0\xA1"
+      "\xF5";
+
+  static unsigned char e[] = "\x11";
+
+  static unsigned char d[] =
+      "\x0A\x03\x37\x48\x62\x64\x87\x69\x5F\x5F\x30\xBC\x38\xB9\x8B\x44"
+      "\xC2\xCD\x2D\xFF\x43\x40\x98\xCD\x20\xD8\xA1\x38\xD0\x90\xBF\x64"
+      "\x79\x7C\x3F\xA7\xA2\xCD\xCB\x3C\xD1\xE0\xBD\xBA\x26\x54\xB4\xF9"
+      "\xDF\x8E\x8A\xE5\x9D\x73\x3D\x9F\x33\xB3\x01\x62\x4A\xFD\x1D\x51";
+
+  static unsigned char p[] =
+      "\x00\xD8\x40\xB4\x16\x66\xB4\x2E\x92\xEA\x0D\xA3\xB4\x32\x04\xB5"
+      "\xCF\xCE\x33\x52\x52\x4D\x04\x16\xA5\xA4\x41\xE7\x00\xAF\x46\x12"
+      "\x0D";
+
+  static unsigned char q[] =
+      "\x00\xC9\x7F\xB1\xF0\x27\xF4\x53\xF6\x34\x12\x33\xEA\xAA\xD1\xD9"
+      "\x35\x3F\x6C\x42\xD0\x88\x66\xB1\xD0\x5A\x0F\x20\x35\x02\x8B\x9D"
+      "\x89";
+
+  static unsigned char dmp1[] =
+      "\x59\x0B\x95\x72\xA2\xC2\xA9\xC4\x06\x05\x9D\xC2\xAB\x2F\x1D\xAF"
+      "\xEB\x7E\x8B\x4F\x10\xA7\x54\x9E\x8E\xED\xF5\xB4\xFC\xE0\x9E\x05";
+
+  static unsigned char dmq1[] =
+      "\x00\x8E\x3C\x05\x21\xFE\x15\xE0\xEA\x06\xA3\x6F\xF0\xF1\x0C\x99"
+      "\x52\xC3\x5B\x7A\x75\x14\xFD\x32\x38\xB8\x0A\xAD\x52\x98\x62\x8D"
+      "\x51";
+
+  static unsigned char iqmp[] =
+      "\x36\x3F\xF7\x18\x9D\xA8\xE9\x0B\x1D\x34\x1F\x71\xD0\x9B\x76\xA8"
+      "\xA9\x43\xE1\x1D\x10\xB2\x4D\x24\x9F\x2D\xEA\xFE\xF8\x0C\x18\x26";
+
+  static unsigned char ctext_ex[] =
+      "\x1b\x8f\x05\xf9\xca\x1a\x79\x52\x6e\x53\xf3\xcc\x51\x4f\xdb\x89"
+      "\x2b\xfb\x91\x93\x23\x1e\x78\xb9\x92\xe6\x8d\x50\xa4\x80\xcb\x52"
+      "\x33\x89\x5c\x74\x95\x8d\x5d\x02\xab\x8c\x0f\xd0\x40\xeb\x58\x44"
+      "\xb0\x05\xc3\x9e\xd8\x27\x4a\x9d\xbf\xa8\x06\x71\x40\x94\x39\xd2";
+
+  SetKey;
+}
+
+static int key2(RSA *key, unsigned char *c) {
+  static unsigned char n[] =
+      "\x00\xA3\x07\x9A\x90\xDF\x0D\xFD\x72\xAC\x09\x0C\xCC\x2A\x78\xB8"
+      "\x74\x13\x13\x3E\x40\x75\x9C\x98\xFA\xF8\x20\x4F\x35\x8A\x0B\x26"
+      "\x3C\x67\x70\xE7\x83\xA9\x3B\x69\x71\xB7\x37\x79\xD2\x71\x7B\xE8"
+      "\x34\x77\xCF";
+
+  static unsigned char e[] = "\x3";
+
+  static unsigned char d[] =
+      "\x6C\xAF\xBC\x60\x94\xB3\xFE\x4C\x72\xB0\xB3\x32\xC6\xFB\x25\xA2"
+      "\xB7\x62\x29\x80\x4E\x68\x65\xFC\xA4\x5A\x74\xDF\x0F\x8F\xB8\x41"
+      "\x3B\x52\xC0\xD0\xE5\x3D\x9B\x59\x0F\xF1\x9B\xE7\x9F\x49\xDD\x21"
+      "\xE5\xEB";
+
+  static unsigned char p[] =
+      "\x00\xCF\x20\x35\x02\x8B\x9D\x86\x98\x40\xB4\x16\x66\xB4\x2E\x92"
+      "\xEA\x0D\xA3\xB4\x32\x04\xB5\xCF\xCE\x91";
+
+  static unsigned char q[] =
+      "\x00\xC9\x7F\xB1\xF0\x27\xF4\x53\xF6\x34\x12\x33\xEA\xAA\xD1\xD9"
+      "\x35\x3F\x6C\x42\xD0\x88\x66\xB1\xD0\x5F";
+
+  static unsigned char dmp1[] =
+      "\x00\x8A\x15\x78\xAC\x5D\x13\xAF\x10\x2B\x22\xB9\x99\xCD\x74\x61"
+      "\xF1\x5E\x6D\x22\xCC\x03\x23\xDF\xDF\x0B";
+
+  static unsigned char dmq1[] =
+      "\x00\x86\x55\x21\x4A\xC5\x4D\x8D\x4E\xCD\x61\x77\xF1\xC7\x36\x90"
+      "\xCE\x2A\x48\x2C\x8B\x05\x99\xCB\xE0\x3F";
+
+  static unsigned char iqmp[] =
+      "\x00\x83\xEF\xEF\xB8\xA9\xA4\x0D\x1D\xB6\xED\x98\xAD\x84\xED\x13"
+      "\x35\xDC\xC1\x08\xF3\x22\xD0\x57\xCF\x8D";
+
+  static unsigned char ctext_ex[] =
+      "\x14\xbd\xdd\x28\xc9\x83\x35\x19\x23\x80\xe8\xe5\x49\xb1\x58\x2a"
+      "\x8b\x40\xb4\x48\x6d\x03\xa6\xa5\x31\x1f\x1f\xd5\xf0\xa1\x80\xe4"
+      "\x17\x53\x03\x29\xa9\x34\x90\x74\xb1\x52\x13\x54\x29\x08\x24\x52"
+      "\x62\x51";
+
+  SetKey;
+}
+
+static int key3(RSA *key, unsigned char *c) {
+  static unsigned char n[] =
+      "\x00\xBB\xF8\x2F\x09\x06\x82\xCE\x9C\x23\x38\xAC\x2B\x9D\xA8\x71"
+      "\xF7\x36\x8D\x07\xEE\xD4\x10\x43\xA4\x40\xD6\xB6\xF0\x74\x54\xF5"
+      "\x1F\xB8\xDF\xBA\xAF\x03\x5C\x02\xAB\x61\xEA\x48\xCE\xEB\x6F\xCD"
+      "\x48\x76\xED\x52\x0D\x60\xE1\xEC\x46\x19\x71\x9D\x8A\x5B\x8B\x80"
+      "\x7F\xAF\xB8\xE0\xA3\xDF\xC7\x37\x72\x3E\xE6\xB4\xB7\xD9\x3A\x25"
+      "\x84\xEE\x6A\x64\x9D\x06\x09\x53\x74\x88\x34\xB2\x45\x45\x98\x39"
+      "\x4E\xE0\xAA\xB1\x2D\x7B\x61\xA5\x1F\x52\x7A\x9A\x41\xF6\xC1\x68"
+      "\x7F\xE2\x53\x72\x98\xCA\x2A\x8F\x59\x46\xF8\xE5\xFD\x09\x1D\xBD"
+      "\xCB";
+
+  static unsigned char e[] = "\x11";
+
+  static unsigned char d[] =
+      "\x00\xA5\xDA\xFC\x53\x41\xFA\xF2\x89\xC4\xB9\x88\xDB\x30\xC1\xCD"
+      "\xF8\x3F\x31\x25\x1E\x06\x68\xB4\x27\x84\x81\x38\x01\x57\x96\x41"
+      "\xB2\x94\x10\xB3\xC7\x99\x8D\x6B\xC4\x65\x74\x5E\x5C\x39\x26\x69"
+      "\xD6\x87\x0D\xA2\xC0\x82\xA9\x39\xE3\x7F\xDC\xB8\x2E\xC9\x3E\xDA"
+      "\xC9\x7F\xF3\xAD\x59\x50\xAC\xCF\xBC\x11\x1C\x76\xF1\xA9\x52\x94"
+      "\x44\xE5\x6A\xAF\x68\xC5\x6C\x09\x2C\xD3\x8D\xC3\xBE\xF5\xD2\x0A"
+      "\x93\x99\x26\xED\x4F\x74\xA1\x3E\xDD\xFB\xE1\xA1\xCE\xCC\x48\x94"
+      "\xAF\x94\x28\xC2\xB7\xB8\x88\x3F\xE4\x46\x3A\x4B\xC8\x5B\x1C\xB3"
+      "\xC1";
+
+  static unsigned char p[] =
+      "\x00\xEE\xCF\xAE\x81\xB1\xB9\xB3\xC9\x08\x81\x0B\x10\xA1\xB5\x60"
+      "\x01\x99\xEB\x9F\x44\xAE\xF4\xFD\xA4\x93\xB8\x1A\x9E\x3D\x84\xF6"
+      "\x32\x12\x4E\xF0\x23\x6E\x5D\x1E\x3B\x7E\x28\xFA\xE7\xAA\x04\x0A"
+      "\x2D\x5B\x25\x21\x76\x45\x9D\x1F\x39\x75\x41\xBA\x2A\x58\xFB\x65"
+      "\x99";
+
+  static unsigned char q[] =
+      "\x00\xC9\x7F\xB1\xF0\x27\xF4\x53\xF6\x34\x12\x33\xEA\xAA\xD1\xD9"
+      "\x35\x3F\x6C\x42\xD0\x88\x66\xB1\xD0\x5A\x0F\x20\x35\x02\x8B\x9D"
+      "\x86\x98\x40\xB4\x16\x66\xB4\x2E\x92\xEA\x0D\xA3\xB4\x32\x04\xB5"
+      "\xCF\xCE\x33\x52\x52\x4D\x04\x16\xA5\xA4\x41\xE7\x00\xAF\x46\x15"
+      "\x03";
+
+  static unsigned char dmp1[] =
+      "\x54\x49\x4C\xA6\x3E\xBA\x03\x37\xE4\xE2\x40\x23\xFC\xD6\x9A\x5A"
+      "\xEB\x07\xDD\xDC\x01\x83\xA4\xD0\xAC\x9B\x54\xB0\x51\xF2\xB1\x3E"
+      "\xD9\x49\x09\x75\xEA\xB7\x74\x14\xFF\x59\xC1\xF7\x69\x2E\x9A\x2E"
+      "\x20\x2B\x38\xFC\x91\x0A\x47\x41\x74\xAD\xC9\x3C\x1F\x67\xC9\x81";
+
+  static unsigned char dmq1[] =
+      "\x47\x1E\x02\x90\xFF\x0A\xF0\x75\x03\x51\xB7\xF8\x78\x86\x4C\xA9"
+      "\x61\xAD\xBD\x3A\x8A\x7E\x99\x1C\x5C\x05\x56\xA9\x4C\x31\x46\xA7"
+      "\xF9\x80\x3F\x8F\x6F\x8A\xE3\x42\xE9\x31\xFD\x8A\xE4\x7A\x22\x0D"
+      "\x1B\x99\xA4\x95\x84\x98\x07\xFE\x39\xF9\x24\x5A\x98\x36\xDA\x3D";
+
+  static unsigned char iqmp[] =
+      "\x00\xB0\x6C\x4F\xDA\xBB\x63\x01\x19\x8D\x26\x5B\xDB\xAE\x94\x23"
+      "\xB3\x80\xF2\x71\xF7\x34\x53\x88\x50\x93\x07\x7F\xCD\x39\xE2\x11"
+      "\x9F\xC9\x86\x32\x15\x4F\x58\x83\xB1\x67\xA9\x67\xBF\x40\x2B\x4E"
+      "\x9E\x2E\x0F\x96\x56\xE6\x98\xEA\x36\x66\xED\xFB\x25\x79\x80\x39"
+      "\xF7";
+
+  static unsigned char ctext_ex[] =
+      "\xb8\x24\x6b\x56\xa6\xed\x58\x81\xae\xb5\x85\xd9\xa2\x5b\x2a\xd7"
+      "\x90\xc4\x17\xe0\x80\x68\x1b\xf1\xac\x2b\xc3\xde\xb6\x9d\x8b\xce"
+      "\xf0\xc4\x36\x6f\xec\x40\x0a\xf0\x52\xa7\x2e\x9b\x0e\xff\xb5\xb3"
+      "\xf2\xf1\x92\xdb\xea\xca\x03\xc1\x27\x40\x05\x71\x13\xbf\x1f\x06"
+      "\x69\xac\x22\xe9\xf3\xa7\x85\x2e\x3c\x15\xd9\x13\xca\xb0\xb8\x86"
+      "\x3a\x95\xc9\x92\x94\xce\x86\x74\x21\x49\x54\x61\x03\x46\xf4\xd4"
+      "\x74\xb2\x6f\x7c\x48\xb4\x2e\xe6\x8e\x1f\x57\x2a\x1f\xc4\x02\x6a"
+      "\xc4\x56\xb4\xf5\x9f\x7b\x62\x1e\xa1\xb9\xd8\x8f\x64\x20\x2f\xb1";
+
+  SetKey;
+}
+
+static int test_only_d_given() {
+  RSA *key = RSA_new();
+  uint8_t buf[64];
+  unsigned buf_len = sizeof(buf);
+  const uint8_t kDummyHash[16] = {0};
+  int ret = 0;
+
+  if (!BN_hex2bn(&key->n,
+                 "00e77bbf3889d4ef36a9a25d4d69f3f632eb4362214c74517da6d6aeaa9bd"
+                 "09ac42b26621cd88f3a6eb013772fc3bf9f83914b6467231c630202c35b3e"
+                 "5808c659") ||
+      !BN_hex2bn(&key->e, "010001") ||
+      !BN_hex2bn(&key->d,
+                 "0365db9eb6d73b53b015c40cd8db4de7dd7035c68b5ac1bf786d7a4ee2cea"
+                 "316eaeca21a73ac365e58713195f2ae9849348525ca855386b6d028e437a9"
+                 "495a01") ||
+      RSA_size(key) > sizeof(buf)) {
+    goto err;
+  }
+
+  if (!RSA_sign(NID_md5, kDummyHash, sizeof(kDummyHash), buf, &buf_len, key)) {
+    fprintf(stderr, "RSA_sign failed with only d given.\n");
+    BIO_print_errors_fp(stderr);
+    goto err;
+  }
+
+  if (!RSA_verify(NID_md5, kDummyHash, sizeof(kDummyHash), buf, buf_len, key)) {
+    fprintf(stderr, "RSA_verify failed with only d given.\n");
+    BIO_print_errors_fp(stderr);
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  RSA_free(key);
+  return ret;
+}
+
+int main(int argc, char *argv[]) {
+  int err = 0;
+  int v;
+  RSA *key;
+  unsigned char ptext[256];
+  unsigned char ctext[256];
+  static unsigned char ptext_ex[] = "\x54\x85\x9b\x34\x2c\x49\xea\x2a";
+  unsigned char ctext_ex[256];
+  int plen;
+  int clen = 0;
+  int num;
+  int n;
+
+  plen = sizeof(ptext_ex) - 1;
+
+  for (v = 0; v < 3; v++) {
+    key = RSA_new();
+    switch (v) {
+      case 0:
+        clen = key1(key, ctext_ex);
+        break;
+      case 1:
+        clen = key2(key, ctext_ex);
+        break;
+      case 2:
+        clen = key3(key, ctext_ex);
+        break;
+      default:
+        abort();
+        return 1;
+    }
+
+    num = RSA_public_encrypt(plen, ptext_ex, ctext, key, RSA_PKCS1_PADDING);
+    if (num != clen) {
+      printf("PKCS#1 v1.5 encryption failed!\n");
+      err = 1;
+      goto oaep;
+    }
+
+    num = RSA_private_decrypt(num, ctext, ptext, key, RSA_PKCS1_PADDING);
+    if (num != plen || memcmp(ptext, ptext_ex, num) != 0) {
+      printf("PKCS#1 v1.5 decryption failed!\n");
+      err = 1;
+    } else {
+      printf("PKCS #1 v1.5 encryption/decryption ok\n");
+    }
+
+  oaep:
+    ERR_clear_error();
+    num =
+        RSA_public_encrypt(plen, ptext_ex, ctext, key, RSA_PKCS1_OAEP_PADDING);
+    if (num == -1) {
+      printf("No OAEP support\n");
+      goto next;
+    }
+    if (num != clen) {
+      printf("OAEP encryption failed!\n");
+      err = 1;
+      goto next;
+    }
+
+    num = RSA_private_decrypt(num, ctext, ptext, key, RSA_PKCS1_OAEP_PADDING);
+    if (num != plen || memcmp(ptext, ptext_ex, num) != 0) {
+      printf("OAEP decryption (encrypted data) failed!\n");
+      err = 1;
+    } else if (memcmp(ctext, ctext_ex, num) == 0) {
+      printf("OAEP test vector %d passed!\n", v);
+    }
+
+    /* Different ciphertexts (rsa_oaep.c without -DPKCS_TESTVECT).
+       Try decrypting ctext_ex */
+
+    num =
+        RSA_private_decrypt(clen, ctext_ex, ptext, key, RSA_PKCS1_OAEP_PADDING);
+
+    if (num != plen || memcmp(ptext, ptext_ex, num) != 0) {
+      printf("OAEP decryption (test vector data) failed!\n");
+      err = 1;
+    } else {
+      printf("OAEP encryption/decryption ok\n");
+    }
+
+    /* Try decrypting corrupted ciphertexts */
+    for (n = 0; n < clen; ++n) {
+      int b;
+      unsigned char saved = ctext[n];
+      for (b = 0; b < 256; ++b) {
+        if (b == saved)
+          continue;
+        ctext[n] = b;
+        num =
+            RSA_private_decrypt(num, ctext, ptext, key, RSA_PKCS1_OAEP_PADDING);
+        if (num > 0) {
+          printf("Corrupt data decrypted!\n");
+          err = 1;
+        }
+      }
+    }
+
+  next:
+    RSA_free(key);
+  }
+
+  if (err != 0 ||
+      !test_only_d_given()) {
+    err = 1;
+  }
+
+  if (err == 0) {
+    printf("PASS\n");
+  }
+  return err;
+}
diff --git a/crypto/sha/CMakeLists.txt b/crypto/sha/CMakeLists.txt
new file mode 100644
index 0000000..848c048
--- /dev/null
+++ b/crypto/sha/CMakeLists.txt
@@ -0,0 +1,61 @@
+include_directories(. .. ../../include)
+
+if (${ARCH} STREQUAL "x86_64")
+	set(
+		SHA_ARCH_SOURCES
+
+		sha1-x86_64.${ASM_EXT}
+		sha256-x86_64.${ASM_EXT}
+		sha512-x86_64.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "x86")
+	set(
+		SHA_ARCH_SOURCES
+
+		sha1-586.${ASM_EXT}
+		sha256-586.${ASM_EXT}
+		sha512-586.${ASM_EXT}
+	)
+endif()
+
+if (${ARCH} STREQUAL "arm")
+	set(
+		SHA_ARCH_SOURCES
+
+		sha1-armv4-large.${ASM_EXT}
+		sha256-armv4.${ASM_EXT}
+		sha512-armv4.${ASM_EXT}
+	)
+endif()
+
+add_library(
+	sha
+
+	OBJECT
+
+	sha1.c
+	sha256.c
+	sha512.c
+
+	${SHA_ARCH_SOURCES}
+)
+
+perlasm(sha1-x86_64.${ASM_EXT} asm/sha1-x86_64.pl)
+perlasm(sha256-x86_64.${ASM_EXT} asm/sha512-x86_64.pl sha256)
+perlasm(sha512-x86_64.${ASM_EXT} asm/sha512-x86_64.pl sha512)
+perlasm(sha1-586.${ASM_EXT} asm/sha1-586.pl)
+perlasm(sha256-586.${ASM_EXT} asm/sha256-586.pl)
+perlasm(sha512-586.${ASM_EXT} asm/sha512-586.pl)
+perlasm(sha1-armv4-large.${ASM_EXT} asm/sha1-armv4-large.pl)
+perlasm(sha256-armv4.${ASM_EXT} asm/sha256-armv4.pl)
+perlasm(sha512-armv4.${ASM_EXT} asm/sha512-armv4.pl)
+
+add_executable(
+	sha1_test
+
+	sha1_test.c
+)
+
+target_link_libraries(sha1_test crypto)
diff --git a/crypto/sha/asm/sha1-586.pl b/crypto/sha/asm/sha1-586.pl
new file mode 100644
index 0000000..632dbbe
--- /dev/null
+++ b/crypto/sha/asm/sha1-586.pl
@@ -0,0 +1,1266 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# [Re]written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# "[Re]written" was achieved in two major overhauls. In 2004 BODY_*
+# functions were re-implemented to address P4 performance issue [see
+# commentary below], and in 2006 the rest was rewritten in order to
+# gain freedom to liberate licensing terms.
+
+# January, September 2004.
+#
+# It was noted that Intel IA-32 C compiler generates code which
+# performs ~30% *faster* on P4 CPU than original *hand-coded*
+# SHA1 assembler implementation. To address this problem (and
+# prove that humans are still better than machines:-), the
+# original code was overhauled, which resulted in following
+# performance changes:
+#
+#		compared with original	compared with Intel cc
+#		assembler impl.		generated code
+# Pentium	-16%			+48%
+# PIII/AMD	+8%			+16%
+# P4		+85%(!)			+45%
+#
+# As you can see Pentium came out as looser:-( Yet I reckoned that
+# improvement on P4 outweights the loss and incorporate this
+# re-tuned code to 0.9.7 and later.
+# ----------------------------------------------------------------
+#					<appro@fy.chalmers.se>
+
+# August 2009.
+#
+# George Spelvin has tipped that F_40_59(b,c,d) can be rewritten as
+# '(c&d) + (b&(c^d))', which allows to accumulate partial results
+# and lighten "pressure" on scratch registers. This resulted in
+# >12% performance improvement on contemporary AMD cores (with no
+# degradation on other CPUs:-). Also, the code was revised to maximize
+# "distance" between instructions producing input to 'lea' instruction
+# and the 'lea' instruction itself, which is essential for Intel Atom
+# core and resulted in ~15% improvement.
+
+# October 2010.
+#
+# Add SSSE3, Supplemental[!] SSE3, implementation. The idea behind it
+# is to offload message schedule denoted by Wt in NIST specification,
+# or Xupdate in OpenSSL source, to SIMD unit. The idea is not novel,
+# and in SSE2 context was first explored by Dean Gaudet in 2004, see
+# http://arctic.org/~dean/crypto/sha1.html. Since then several things
+# have changed that made it interesting again:
+#
+# a) XMM units became faster and wider;
+# b) instruction set became more versatile;
+# c) an important observation was made by Max Locktykhin, which made
+#    it possible to reduce amount of instructions required to perform
+#    the operation in question, for further details see
+#    http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/.
+
+# April 2011.
+#
+# Add AVX code path, probably most controversial... The thing is that
+# switch to AVX alone improves performance by as little as 4% in
+# comparison to SSSE3 code path. But below result doesn't look like
+# 4% improvement... Trouble is that Sandy Bridge decodes 'ro[rl]' as
+# pair of µ-ops, and it's the additional µ-ops, two per round, that
+# make it run slower than Core2 and Westmere. But 'sh[rl]d' is decoded
+# as single µ-op by Sandy Bridge and it's replacing 'ro[rl]' with
+# equivalent 'sh[rl]d' that is responsible for the impressive 5.1
+# cycles per processed byte. But 'sh[rl]d' is not something that used
+# to be fast, nor does it appear to be fast in upcoming Bulldozer
+# [according to its optimization manual]. Which is why AVX code path
+# is guarded by *both* AVX and synthetic bit denoting Intel CPUs.
+# One can argue that it's unfair to AMD, but without 'sh[rl]d' it
+# makes no sense to keep the AVX code path. If somebody feels that
+# strongly, it's probably more appropriate to discuss possibility of
+# using vector rotate XOP on AMD...
+
+######################################################################
+# Current performance is summarized in following table. Numbers are
+# CPU clock cycles spent to process single byte (less is better).
+#
+#		x86		SSSE3		AVX
+# Pentium	15.7		-
+# PIII		11.5		-
+# P4		10.6		-
+# AMD K8	7.1		-
+# Core2		7.3		6.0/+22%	-
+# Atom		12.5		9.3(*)/+35%	-
+# Westmere	7.3		5.5/+33%	-
+# Sandy Bridge	8.8		6.2/+40%	5.1(**)/+73%
+# Ivy Bridge	7.2		4.8/+51%	4.7(**)/+53%
+# Bulldozer	11.6		6.0/+92%
+# VIA Nano	10.6		7.4/+43%
+#
+# (*)	Loop is 1056 instructions long and expected result is ~8.25.
+#	It remains mystery [to me] why ILP is limited to 1.7.
+#
+# (**)	As per above comment, the result is for AVX *plus* sh[rl]d.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"sha1-586.pl",$ARGV[$#ARGV] eq "386");
+
+$xmm=$ymm=0;
+for (@ARGV) { $xmm=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+$ymm=1 if ($xmm &&
+		`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+			=~ /GNU assembler version ([2-9]\.[0-9]+)/ &&
+		$1>=2.19);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && $ARGV[0] eq "win32n" && 
+		`nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/ &&
+		$1>=2.03);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && $ARGV[0] eq "win32" &&
+		`ml 2>&1` =~ /Version ([0-9]+)\./ &&
+		$1>=10);	# first version supporting AVX
+
+&external_label("OPENSSL_ia32cap_P") if ($xmm);
+
+
+$A="eax";
+$B="ebx";
+$C="ecx";
+$D="edx";
+$E="edi";
+$T="esi";
+$tmp1="ebp";
+
+@V=($A,$B,$C,$D,$E,$T);
+
+$alt=0;	# 1 denotes alternative IALU implementation, which performs
+	# 8% *worse* on P4, same on Westmere and Atom, 2% better on
+	# Sandy Bridge...
+
+sub BODY_00_15
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+
+	&comment("00_15 $n");
+
+	&mov($f,$c);			# f to hold F_00_19(b,c,d)
+	 if ($n==0)  { &mov($tmp1,$a); }
+	 else        { &mov($a,$tmp1); }
+	&rotl($tmp1,5);			# tmp1=ROTATE(a,5)
+	 &xor($f,$d);
+	&add($tmp1,$e);			# tmp1+=e;
+	 &mov($e,&swtmp($n%16));	# e becomes volatile and is loaded
+	 				# with xi, also note that e becomes
+					# f in next round...
+	&and($f,$b);
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &xor($f,$d);			# f holds F_00_19(b,c,d)
+	&lea($tmp1,&DWP(0x5a827999,$tmp1,$e));	# tmp1+=K_00_19+xi
+
+	if ($n==15) { &mov($e,&swtmp(($n+1)%16));# pre-fetch f for next round
+		      &add($f,$tmp1); }	# f+=tmp1
+	else        { &add($tmp1,$f); }	# f becomes a in next round
+	&mov($tmp1,$a)			if ($alt && $n==15);
+	}
+
+sub BODY_16_19
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+
+	&comment("16_19 $n");
+
+if ($alt) {
+	&xor($c,$d);
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&and($tmp1,$c);			# tmp1 to hold F_00_19(b,c,d), b&=c^d
+	 &xor($f,&swtmp(($n+8)%16));
+	&xor($tmp1,$d);			# tmp1=F_00_19(b,c,d)
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &add($e,$tmp1);		# e+=F_00_19(b,c,d)
+	&xor($c,$d);			# restore $c
+	 &mov($tmp1,$a);		# b in next round
+	&rotr($b,$n==16?2:7);		# b=ROTATE(b,30)
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&rotl($a,5);			# ROTATE(a,5)
+	 &lea($f,&DWP(0x5a827999,$f,$e));# f+=F_00_19(b,c,d)+e
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$a);			# f+=ROTATE(a,5)
+} else {
+	&mov($tmp1,$c);			# tmp1 to hold F_00_19(b,c,d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$d);
+	 &xor($f,&swtmp(($n+8)%16));
+	&and($tmp1,$b);
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &xor($tmp1,$d);		# tmp1=F_00_19(b,c,d)
+	&add($e,$tmp1);			# e+=F_00_19(b,c,d)
+	 &mov($tmp1,$a);
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&rotl($tmp1,5);			# ROTATE(a,5)
+	 &lea($f,&DWP(0x5a827999,$f,$e));# f+=F_00_19(b,c,d)+e
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$tmp1);		# f+=ROTATE(a,5)
+}
+	}
+
+sub BODY_20_39
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+	local $K=($n<40)?0x6ed9eba1:0xca62c1d6;
+
+	&comment("20_39 $n");
+
+if ($alt) {
+	&xor($tmp1,$c);			# tmp1 to hold F_20_39(b,c,d), b^=c
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$d);			# tmp1 holds F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+8)%16));
+	&add($e,$tmp1);			# e+=F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &mov($tmp1,$a);		# b in next round
+	&rotr($b,7);			# b=ROTATE(b,30)
+	 &mov(&swtmp($n%16),$f)		if($n<77);# xi=f
+	&rotl($a,5);			# ROTATE(a,5)
+	 &xor($b,$c)			if($n==39);# warm up for BODY_40_59
+	&and($tmp1,$b)			if($n==39);
+	 &lea($f,&DWP($K,$f,$e));	# f+=e+K_XX_YY
+	&mov($e,&swtmp(($n+1)%16))	if($n<79);# pre-fetch f for next round
+	 &add($f,$a);			# f+=ROTATE(a,5)
+	&rotr($a,5)			if ($n==79);
+} else {
+	&mov($tmp1,$b);			# tmp1 to hold F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$c);
+	 &xor($f,&swtmp(($n+8)%16));
+	&xor($tmp1,$d);			# tmp1 holds F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &add($e,$tmp1);		# e+=F_20_39(b,c,d)
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &mov($tmp1,$a);
+	&rotl($tmp1,5);			# ROTATE(a,5)
+	 &mov(&swtmp($n%16),$f) if($n<77);# xi=f
+	&lea($f,&DWP($K,$f,$e));	# f+=e+K_XX_YY
+	 &mov($e,&swtmp(($n+1)%16)) if($n<79);# pre-fetch f for next round
+	&add($f,$tmp1);			# f+=ROTATE(a,5)
+}
+	}
+
+sub BODY_40_59
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+
+	&comment("40_59 $n");
+
+if ($alt) {
+	&add($e,$tmp1);			# e+=b&(c^d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&mov($tmp1,$d);
+	 &xor($f,&swtmp(($n+8)%16));
+	&xor($c,$d);			# restore $c
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &and($tmp1,$c);
+	&rotr($b,7);			# b=ROTATE(b,30)
+	 &add($e,$tmp1);		# e+=c&d
+	&mov($tmp1,$a);			# b in next round
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&rotl($a,5);			# ROTATE(a,5)
+	 &xor($b,$c)			if ($n<59);
+	&and($tmp1,$b)			if ($n<59);# tmp1 to hold F_40_59(b,c,d)
+	 &lea($f,&DWP(0x8f1bbcdc,$f,$e));# f+=K_40_59+e+(b&(c^d))
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$a);			# f+=ROTATE(a,5)
+} else {
+	&mov($tmp1,$c);			# tmp1 to hold F_40_59(b,c,d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$d);
+	 &xor($f,&swtmp(($n+8)%16));
+	&and($tmp1,$b);
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &add($tmp1,$e);		# b&(c^d)+=e
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &mov($e,$a);			# e becomes volatile
+	&rotl($e,5);			# ROTATE(a,5)
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&lea($f,&DWP(0x8f1bbcdc,$f,$tmp1));# f+=K_40_59+e+(b&(c^d))
+	 &mov($tmp1,$c);
+	&add($f,$e);			# f+=ROTATE(a,5)
+	 &and($tmp1,$d);
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$tmp1);		# f+=c&d
+}
+	}
+
+&function_begin("sha1_block_data_order");
+if ($xmm) {
+  &static_label("ssse3_shortcut");
+  &static_label("avx_shortcut")		if ($ymm);
+  &static_label("K_XX_XX");
+
+	&call	(&label("pic_point"));	# make it PIC!
+  &set_label("pic_point");
+	&blindpop($tmp1);
+	&picmeup($T,"OPENSSL_ia32cap_P",$tmp1,&label("pic_point"));
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+
+	&mov	($A,&DWP(0,$T));
+	&mov	($D,&DWP(4,$T));
+	&test	($D,1<<9);		# check SSSE3 bit
+	&jz	(&label("x86"));
+	&test	($A,1<<24);		# check FXSR bit
+	&jz	(&label("x86"));
+	if ($ymm) {
+		&and	($D,1<<28);		# mask AVX bit
+		&and	($A,1<<30);		# mask "Intel CPU" bit
+		&or	($A,$D);
+		&cmp	($A,1<<28|1<<30);
+		&je	(&label("avx_shortcut"));
+	}
+	&jmp	(&label("ssse3_shortcut"));
+  &set_label("x86",16);
+}
+	&mov($tmp1,&wparam(0));	# SHA_CTX *c
+	&mov($T,&wparam(1));	# const void *input
+	&mov($A,&wparam(2));	# size_t num
+	&stack_push(16+3);	# allocate X[16]
+	&shl($A,6);
+	&add($A,$T);
+	&mov(&wparam(2),$A);	# pointer beyond the end of input
+	&mov($E,&DWP(16,$tmp1));# pre-load E
+	&jmp(&label("loop"));
+
+&set_label("loop",16);
+
+	# copy input chunk to X, but reversing byte order!
+	for ($i=0; $i<16; $i+=4)
+		{
+		&mov($A,&DWP(4*($i+0),$T));
+		&mov($B,&DWP(4*($i+1),$T));
+		&mov($C,&DWP(4*($i+2),$T));
+		&mov($D,&DWP(4*($i+3),$T));
+		&bswap($A);
+		&bswap($B);
+		&bswap($C);
+		&bswap($D);
+		&mov(&swtmp($i+0),$A);
+		&mov(&swtmp($i+1),$B);
+		&mov(&swtmp($i+2),$C);
+		&mov(&swtmp($i+3),$D);
+		}
+	&mov(&wparam(1),$T);	# redundant in 1st spin
+
+	&mov($A,&DWP(0,$tmp1));	# load SHA_CTX
+	&mov($B,&DWP(4,$tmp1));
+	&mov($C,&DWP(8,$tmp1));
+	&mov($D,&DWP(12,$tmp1));
+	# E is pre-loaded
+
+	for($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<20;$i++)	{ &BODY_16_19($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+
+	(($V[5] eq $D) and ($V[0] eq $E)) or die;	# double-check
+
+	&mov($tmp1,&wparam(0));	# re-load SHA_CTX*
+	&mov($D,&wparam(1));	# D is last "T" and is discarded
+
+	&add($E,&DWP(0,$tmp1));	# E is last "A"...
+	&add($T,&DWP(4,$tmp1));
+	&add($A,&DWP(8,$tmp1));
+	&add($B,&DWP(12,$tmp1));
+	&add($C,&DWP(16,$tmp1));
+
+	&mov(&DWP(0,$tmp1),$E);	# update SHA_CTX
+	 &add($D,64);		# advance input pointer
+	&mov(&DWP(4,$tmp1),$T);
+	 &cmp($D,&wparam(2));	# have we reached the end yet?
+	&mov(&DWP(8,$tmp1),$A);
+	 &mov($E,$C);		# C is last "E" which needs to be "pre-loaded"
+	&mov(&DWP(12,$tmp1),$B);
+	 &mov($T,$D);		# input pointer
+	&mov(&DWP(16,$tmp1),$C);
+	&jb(&label("loop"));
+
+	&stack_pop(16+3);
+&function_end("sha1_block_data_order");
+
+if ($xmm) {
+######################################################################
+# The SSSE3 implementation.
+#
+# %xmm[0-7] are used as ring @X[] buffer containing quadruples of last
+# 32 elements of the message schedule or Xupdate outputs. First 4
+# quadruples are simply byte-swapped input, next 4 are calculated
+# according to method originally suggested by Dean Gaudet (modulo
+# being implemented in SSSE3). Once 8 quadruples or 32 elements are
+# collected, it switches to routine proposed by Max Locktyukhin.
+#
+# Calculations inevitably require temporary reqisters, and there are
+# no %xmm registers left to spare. For this reason part of the ring
+# buffer, X[2..4] to be specific, is offloaded to 3 quadriples ring
+# buffer on the stack. Keep in mind that X[2] is alias X[-6], X[3] -
+# X[-5], and X[4] - X[-4]...
+#
+# Another notable optimization is aggressive stack frame compression
+# aiming to minimize amount of 9-byte instructions...
+#
+# Yet another notable optimization is "jumping" $B variable. It means
+# that there is no register permanently allocated for $B value. This
+# allowed to eliminate one instruction from body_20_39...
+#
+my $Xi=4;			# 4xSIMD Xupdate round, start pre-seeded
+my @X=map("xmm$_",(4..7,0..3));	# pre-seeded for $Xi=4
+my @V=($A,$B,$C,$D,$E);
+my $j=0;			# hash round
+my $rx=0;
+my @T=($T,$tmp1);
+my $inp;
+
+my $_rol=sub { &rol(@_) };
+my $_ror=sub { &ror(@_) };
+
+&function_begin("_sha1_block_data_order_ssse3");
+	&call	(&label("pic_point"));	# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tmp1);
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+&set_label("ssse3_shortcut");
+
+	&movdqa	(@X[3],&QWP(0,$tmp1));		# K_00_19
+	&movdqa	(@X[4],&QWP(16,$tmp1));		# K_20_39
+	&movdqa	(@X[5],&QWP(32,$tmp1));		# K_40_59
+	&movdqa	(@X[6],&QWP(48,$tmp1));		# K_60_79
+	&movdqa	(@X[2],&QWP(64,$tmp1));		# pbswap mask
+
+	&mov	($E,&wparam(0));		# load argument block
+	&mov	($inp=@T[1],&wparam(1));
+	&mov	($D,&wparam(2));
+	&mov	(@T[0],"esp");
+
+	# stack frame layout
+	#
+	# +0	X[0]+K	X[1]+K	X[2]+K	X[3]+K	# XMM->IALU xfer area
+	#	X[4]+K	X[5]+K	X[6]+K	X[7]+K
+	#	X[8]+K	X[9]+K	X[10]+K	X[11]+K
+	#	X[12]+K	X[13]+K	X[14]+K	X[15]+K
+	#
+	# +64	X[0]	X[1]	X[2]	X[3]	# XMM->XMM backtrace area
+	#	X[4]	X[5]	X[6]	X[7]
+	#	X[8]	X[9]	X[10]	X[11]	# even borrowed for K_00_19
+	#
+	# +112	K_20_39	K_20_39	K_20_39	K_20_39	# constants
+	#	K_40_59	K_40_59	K_40_59	K_40_59
+	#	K_60_79	K_60_79	K_60_79	K_60_79
+	#	K_00_19	K_00_19	K_00_19	K_00_19
+	#	pbswap mask
+	#
+	# +192	ctx				# argument block
+	# +196	inp
+	# +200	end
+	# +204	esp
+	&sub	("esp",208);
+	&and	("esp",-64);
+
+	&movdqa	(&QWP(112+0,"esp"),@X[4]);	# copy constants
+	&movdqa	(&QWP(112+16,"esp"),@X[5]);
+	&movdqa	(&QWP(112+32,"esp"),@X[6]);
+	&shl	($D,6);				# len*64
+	&movdqa	(&QWP(112+48,"esp"),@X[3]);
+	&add	($D,$inp);			# end of input
+	&movdqa	(&QWP(112+64,"esp"),@X[2]);
+	&add	($inp,64);
+	&mov	(&DWP(192+0,"esp"),$E);		# save argument block
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&mov	(&DWP(192+8,"esp"),$D);
+	&mov	(&DWP(192+12,"esp"),@T[0]);	# save original %esp
+
+	&mov	($A,&DWP(0,$E));		# load context
+	&mov	($B,&DWP(4,$E));
+	&mov	($C,&DWP(8,$E));
+	&mov	($D,&DWP(12,$E));
+	&mov	($E,&DWP(16,$E));
+	&mov	(@T[0],$B);			# magic seed
+
+	&movdqu	(@X[-4&7],&QWP(-64,$inp));	# load input to %xmm[0-3]
+	&movdqu	(@X[-3&7],&QWP(-48,$inp));
+	&movdqu	(@X[-2&7],&QWP(-32,$inp));
+	&movdqu	(@X[-1&7],&QWP(-16,$inp));
+	&pshufb	(@X[-4&7],@X[2]);		# byte swap
+	&pshufb	(@X[-3&7],@X[2]);
+	&pshufb	(@X[-2&7],@X[2]);
+	&movdqa	(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+	&pshufb	(@X[-1&7],@X[2]);
+	&paddd	(@X[-4&7],@X[3]);		# add K_00_19
+	&paddd	(@X[-3&7],@X[3]);
+	&paddd	(@X[-2&7],@X[3]);
+	&movdqa	(&QWP(0,"esp"),@X[-4&7]);	# X[]+K xfer to IALU
+	&psubd	(@X[-4&7],@X[3]);		# restore X[]
+	&movdqa	(&QWP(0+16,"esp"),@X[-3&7]);
+	&psubd	(@X[-3&7],@X[3]);
+	&movdqa	(&QWP(0+32,"esp"),@X[-2&7]);
+	&mov	(@T[1],$C);
+	&psubd	(@X[-2&7],@X[3]);
+	&xor	(@T[1],$D);
+	&movdqa	(@X[0],@X[-3&7]);
+	&and	(@T[0],@T[1]);
+	&jmp	(&label("loop"));
+
+######################################################################
+# SSE instruction sequence is first broken to groups of indepentent
+# instructions, independent in respect to their inputs and shifter
+# (not all architectures have more than one). Then IALU instructions
+# are "knitted in" between the SSE groups. Distance is maintained for
+# SSE latency of 2 in hope that it fits better upcoming AMD Bulldozer
+# [which allegedly also implements SSSE3]...
+#
+# Temporary registers usage. X[2] is volatile at the entry and at the
+# end is restored from backtrace ring buffer. X[3] is expected to
+# contain current K_XX_XX constant and is used to caclulate X[-1]+K
+# from previous round, it becomes volatile the moment the value is
+# saved to stack for transfer to IALU. X[4] becomes volatile whenever
+# X[-4] is accumulated and offloaded to backtrace ring buffer, at the
+# end it is loaded with next K_XX_XX [which becomes X[3] in next
+# round]...
+#
+sub Xupdate_ssse3_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&palignr(@X[0],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	&movdqa	(@X[2],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &paddd	(@X[3],@X[-1&7]);
+	  &movdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psrldq	(@X[2],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[2],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[2]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&movdqa	(@X[4],@X[0]);
+	&movdqa	(@X[2],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslldq	(@X[4],12);		# "X[0]"<<96, extract one dword
+	&paddd	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@X[2],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(@X[3],@X[4]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@X[4],30);
+	&por	(@X[0],@X[2]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if ($Xi>5);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslld	(@X[3],2);
+	&pxor	(@X[0],@X[4]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(@X[4],&QWP(112-16+16*(($Xi)/5),"esp"));	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[3]);		# "X[0]"^=("X[0]"<<96)<<<2
+	  &movdqa	(@X[1],@X[-2&7])	if ($Xi<7);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_ssse3_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&movdqa	(@X[2],@X[-1&7])	if ($Xi==8);
+	 eval(shift(@insns));		# body_20_39
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	&palignr(@X[2],@X[-2&7],8);	# compose "X[-6]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&pxor	(@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	  &movdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);	# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 if ($Xi%5) {
+	  &movdqa	(@X[4],@X[3]);	# "perpetuate" K_XX_XX...
+	 } else {			# ... or load next one
+	  &movdqa	(@X[4],&QWP(112-16+16*($Xi/5),"esp"));
+	 }
+	  &paddd	(@X[3],@X[-1&7]);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[2]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&movdqa	(@X[2],@X[0]);
+	  &movdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&pslld	(@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	&psrld	(@X[2],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&por	(@X[0],@X[2]);		# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	  &movdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if($Xi<19);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	  &movdqa	(@X[3],@X[0])	if ($Xi<19);
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_ssse3_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &paddd	(@X[3],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &movdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&mov	($inp=@T[1],&DWP(192+4,"esp"));
+	&cmp	($inp,&DWP(192+8,"esp"));
+	&je	(&label("done"));
+
+	&movdqa	(@X[3],&QWP(112+48,"esp"));	# K_00_19
+	&movdqa	(@X[2],&QWP(112+64,"esp"));	# pbswap mask
+	&movdqu	(@X[-4&7],&QWP(0,$inp));	# load input
+	&movdqu	(@X[-3&7],&QWP(16,$inp));
+	&movdqu	(@X[-2&7],&QWP(32,$inp));
+	&movdqu	(@X[-1&7],&QWP(48,$inp));
+	&add	($inp,64);
+	&pshufb	(@X[-4&7],@X[2]);		# byte swap
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&movdqa	(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+
+  $Xi=0;
+}
+
+sub Xloop_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pshufb	(@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddd	(@X[($Xi-4)&7],@X[3]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(&QWP(0+16*$Xi,"esp"),@X[($Xi-4)&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psubd	(@X[($Xi-4)&7],@X[3]);
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+sub body_00_19 () {	# ((c^d)&b)^d
+	# on start @T[0]=(c^d)&b
+	return &body_20_39()	if ($rx==19);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&$_ror	($b,$j?7:2);',	# $b>>>2
+	'&xor	(@T[0],$d);',
+	'&mov	(@T[1],$a);',	# $b in next round
+
+	'&add	($e,&DWP(4*($j&15),"esp"));',	# X[]+K xfer
+	'&xor	($b,$c);',	# $c^$d for next round
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&and	(@T[1],$b);',	# ($b&($c^$d)) for next round
+
+	'&xor	($b,$c);',	# restore $b
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_20_39 () {	# b^d^c
+	# on entry @T[0]=b^d
+	return &body_40_59()	if ($rx==39);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,&DWP(4*($j&15),"esp"));',	# X[]+K xfer
+	'&xor	(@T[0],$d)	if($j==19);'.
+	'&xor	(@T[0],$c)	if($j> 19);',	# ($b^$d^$c)
+	'&mov	(@T[1],$a);',	# $b in next round
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&xor	(@T[1],$c)	if ($j< 79);',	# $b^$d for next round
+
+	'&$_ror	($b,7);',	# $b>>>2
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_40_59 () {	# ((b^c)&(c^d))^c
+	# on entry @T[0]=(b^c), (c^=d)
+	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,&DWP(4*($j&15),"esp"));',	# X[]+K xfer
+	'&and	(@T[0],$c)	if ($j>=40);',	# (b^c)&(c^d)
+	'&xor	($c,$d)		if ($j>=40);',	# restore $c
+
+	'&$_ror	($b,7);',	# $b>>>2
+	'&mov	(@T[1],$a);',	# $b for next round
+	'&xor	(@T[0],$c);',
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&xor	(@T[1],$c)	if ($j==59);'.
+	'&xor	(@T[1],$b)	if ($j< 59);',	# b^c for next round
+
+	'&xor	($b,$c)		if ($j< 59);',	# c^d for next round
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+&set_label("loop",16);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xuplast_ssse3_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	(&DWP(8,@T[1]),$C);
+	&mov	($B,$C);
+	&mov	(&DWP(12,@T[1]),$D);
+	&xor	($B,$D);
+	&mov	(&DWP(16,@T[1]),$E);
+	&and	($B,@T[0]);
+	&movdqa	(@X[0],@X[-3&7]);
+	&xchg	($B,@T[0]);
+
+	&jmp	(&label("loop"));
+
+&set_label("done",16);		$j=$saved_j; @V=@saved_V;
+
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&mov	("esp",&DWP(192+12,"esp"));	# restore %esp
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	(&DWP(8,@T[1]),$C);
+	&mov	(&DWP(12,@T[1]),$D);
+	&mov	(&DWP(16,@T[1]),$E);
+
+&function_end("_sha1_block_data_order_ssse3");
+
+$rx=0;	# reset
+
+if ($ymm) {
+my $Xi=4;			# 4xSIMD Xupdate round, start pre-seeded
+my @X=map("xmm$_",(4..7,0..3));	# pre-seeded for $Xi=4
+my @V=($A,$B,$C,$D,$E);
+my $j=0;			# hash round
+my @T=($T,$tmp1);
+my $inp;
+
+my $_rol=sub { &shld(@_[0],@_) };
+my $_ror=sub { &shrd(@_[0],@_) };
+
+&function_begin("_sha1_block_data_order_avx");
+	&call	(&label("pic_point"));	# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tmp1);
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+&set_label("avx_shortcut");
+	&vzeroall();
+
+	&vmovdqa(@X[3],&QWP(0,$tmp1));		# K_00_19
+	&vmovdqa(@X[4],&QWP(16,$tmp1));		# K_20_39
+	&vmovdqa(@X[5],&QWP(32,$tmp1));		# K_40_59
+	&vmovdqa(@X[6],&QWP(48,$tmp1));		# K_60_79
+	&vmovdqa(@X[2],&QWP(64,$tmp1));		# pbswap mask
+
+	&mov	($E,&wparam(0));		# load argument block
+	&mov	($inp=@T[1],&wparam(1));
+	&mov	($D,&wparam(2));
+	&mov	(@T[0],"esp");
+
+	# stack frame layout
+	#
+	# +0	X[0]+K	X[1]+K	X[2]+K	X[3]+K	# XMM->IALU xfer area
+	#	X[4]+K	X[5]+K	X[6]+K	X[7]+K
+	#	X[8]+K	X[9]+K	X[10]+K	X[11]+K
+	#	X[12]+K	X[13]+K	X[14]+K	X[15]+K
+	#
+	# +64	X[0]	X[1]	X[2]	X[3]	# XMM->XMM backtrace area
+	#	X[4]	X[5]	X[6]	X[7]
+	#	X[8]	X[9]	X[10]	X[11]	# even borrowed for K_00_19
+	#
+	# +112	K_20_39	K_20_39	K_20_39	K_20_39	# constants
+	#	K_40_59	K_40_59	K_40_59	K_40_59
+	#	K_60_79	K_60_79	K_60_79	K_60_79
+	#	K_00_19	K_00_19	K_00_19	K_00_19
+	#	pbswap mask
+	#
+	# +192	ctx				# argument block
+	# +196	inp
+	# +200	end
+	# +204	esp
+	&sub	("esp",208);
+	&and	("esp",-64);
+
+	&vmovdqa(&QWP(112+0,"esp"),@X[4]);	# copy constants
+	&vmovdqa(&QWP(112+16,"esp"),@X[5]);
+	&vmovdqa(&QWP(112+32,"esp"),@X[6]);
+	&shl	($D,6);				# len*64
+	&vmovdqa(&QWP(112+48,"esp"),@X[3]);
+	&add	($D,$inp);			# end of input
+	&vmovdqa(&QWP(112+64,"esp"),@X[2]);
+	&add	($inp,64);
+	&mov	(&DWP(192+0,"esp"),$E);		# save argument block
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&mov	(&DWP(192+8,"esp"),$D);
+	&mov	(&DWP(192+12,"esp"),@T[0]);	# save original %esp
+
+	&mov	($A,&DWP(0,$E));		# load context
+	&mov	($B,&DWP(4,$E));
+	&mov	($C,&DWP(8,$E));
+	&mov	($D,&DWP(12,$E));
+	&mov	($E,&DWP(16,$E));
+	&mov	(@T[0],$B);			# magic seed
+
+	&vmovdqu(@X[-4&7],&QWP(-64,$inp));	# load input to %xmm[0-3]
+	&vmovdqu(@X[-3&7],&QWP(-48,$inp));
+	&vmovdqu(@X[-2&7],&QWP(-32,$inp));
+	&vmovdqu(@X[-1&7],&QWP(-16,$inp));
+	&vpshufb(@X[-4&7],@X[-4&7],@X[2]);	# byte swap
+	&vpshufb(@X[-3&7],@X[-3&7],@X[2]);
+	&vpshufb(@X[-2&7],@X[-2&7],@X[2]);
+	&vmovdqa(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+	&vpshufb(@X[-1&7],@X[-1&7],@X[2]);
+	&vpaddd	(@X[0],@X[-4&7],@X[3]);		# add K_00_19
+	&vpaddd	(@X[1],@X[-3&7],@X[3]);
+	&vpaddd	(@X[2],@X[-2&7],@X[3]);
+	&vmovdqa(&QWP(0,"esp"),@X[0]);		# X[]+K xfer to IALU
+	&mov	(@T[1],$C);
+	&vmovdqa(&QWP(0+16,"esp"),@X[1]);
+	&xor	(@T[1],$D);
+	&vmovdqa(&QWP(0+32,"esp"),@X[2]);
+	&and	(@T[0],@T[1]);
+	&jmp	(&label("loop"));
+
+sub Xupdate_avx_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vpaddd	(@X[3],@X[3],@X[-1&7]);
+	  &vmovdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpsrldq(@X[2],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[2],@X[2],@X[-2&7]);		# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[2]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@X[2],@X[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@X[4],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@X[3],@X[4],30);
+	&vpor	(@X[0],@X[0],@X[2]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@X[4],@X[4],2);
+	  &vmovdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if ($Xi>5);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[3]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[4]);		# "X[0]"^=("X[0]"<<96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(@X[4],&QWP(112-16+16*(($Xi)/5),"esp"));	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@X[2],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	  &vmovdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);	# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 if ($Xi%5) {
+	  &vmovdqa	(@X[4],@X[3]);	# "perpetuate" K_XX_XX...
+	 } else {			# ... or load next one
+	  &vmovdqa	(@X[4],&QWP(112-16+16*($Xi/5),"esp"));
+	 }
+	  &vpaddd	(@X[3],@X[3],@X[-1&7]);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[2]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpsrld	(@X[2],@X[0],30);
+	  &vmovdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@X[2]);	# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	  &vmovdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if($Xi<19);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_avx_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &vpaddd	(@X[3],@X[3],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vmovdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&mov	($inp=@T[1],&DWP(192+4,"esp"));
+	&cmp	($inp,&DWP(192+8,"esp"));
+	&je	(&label("done"));
+
+	&vmovdqa(@X[3],&QWP(112+48,"esp"));	# K_00_19
+	&vmovdqa(@X[2],&QWP(112+64,"esp"));	# pbswap mask
+	&vmovdqu(@X[-4&7],&QWP(0,$inp));	# load input
+	&vmovdqu(@X[-3&7],&QWP(16,$inp));
+	&vmovdqu(@X[-2&7],&QWP(32,$inp));
+	&vmovdqu(@X[-1&7],&QWP(48,$inp));
+	&add	($inp,64);
+	&vpshufb(@X[-4&7],@X[-4&7],@X[2]);		# byte swap
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&vmovdqa(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+
+  $Xi=0;
+}
+
+sub Xloop_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpshufb	(@X[($Xi-3)&7],@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpaddd	(@X[$Xi&7],@X[($Xi-4)&7],@X[3]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqa	(&QWP(0+16*$Xi,"esp"),@X[$Xi&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+&set_label("loop",16);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xuplast_avx_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	($B,$C);
+	&mov	(&DWP(8,@T[1]),$C);
+	&xor	($B,$D);
+	&mov	(&DWP(12,@T[1]),$D);
+	&and	($B,@T[0]);
+	&mov	(&DWP(16,@T[1]),$E);
+	&xchg	($B,@T[0]);
+
+	&jmp	(&label("loop"));
+
+&set_label("done",16);		$j=$saved_j; @V=@saved_V;
+
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+
+	&vzeroall();
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&mov	("esp",&DWP(192+12,"esp"));	# restore %esp
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	(&DWP(8,@T[1]),$C);
+	&mov	(&DWP(12,@T[1]),$D);
+	&mov	(&DWP(16,@T[1]),$E);
+&function_end("_sha1_block_data_order_avx");
+}
+&set_label("K_XX_XX",64);
+&data_word(0x5a827999,0x5a827999,0x5a827999,0x5a827999);	# K_00_19
+&data_word(0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1);	# K_20_39
+&data_word(0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc);	# K_40_59
+&data_word(0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6);	# K_60_79
+&data_word(0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f);	# pbswap mask
+}
+&asciz("SHA1 block transform for x86, CRYPTOGAMS by <appro\@openssl.org>");
+
+&asm_finish();
diff --git a/crypto/sha/asm/sha1-armv4-large.pl b/crypto/sha/asm/sha1-armv4-large.pl
new file mode 100644
index 0000000..6643731
--- /dev/null
+++ b/crypto/sha/asm/sha1-armv4-large.pl
@@ -0,0 +1,251 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# sha1_block procedure for ARMv4.
+#
+# January 2007.
+
+# Size/performance trade-off
+# ====================================================================
+# impl		size in bytes	comp cycles[*]	measured performance
+# ====================================================================
+# thumb		304		3212		4420
+# armv4-small	392/+29%	1958/+64%	2250/+96%
+# armv4-compact	740/+89%	1552/+26%	1840/+22%
+# armv4-large	1420/+92%	1307/+19%	1370/+34%[***]
+# full unroll	~5100/+260%	~1260/+4%	~1300/+5%
+# ====================================================================
+# thumb		= same as 'small' but in Thumb instructions[**] and
+#		  with recurring code in two private functions;
+# small		= detached Xload/update, loops are folded;
+# compact	= detached Xload/update, 5x unroll;
+# large		= interleaved Xload/update, 5x unroll;
+# full unroll	= interleaved Xload/update, full unroll, estimated[!];
+#
+# [*]	Manually counted instructions in "grand" loop body. Measured
+#	performance is affected by prologue and epilogue overhead,
+#	i-cache availability, branch penalties, etc.
+# [**]	While each Thumb instruction is twice smaller, they are not as
+#	diverse as ARM ones: e.g., there are only two arithmetic
+#	instructions with 3 arguments, no [fixed] rotate, addressing
+#	modes are limited. As result it takes more instructions to do
+#	the same job in Thumb, therefore the code is never twice as
+#	small and always slower.
+# [***]	which is also ~35% better than compiler generated code. Dual-
+#	issue Cortex A8 core was measured to process input block in
+#	~990 cycles.
+
+# August 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 13% improvement on
+# Cortex A8 core and in absolute terms ~870 cycles per input block
+# [or 13.6 cycles per byte].
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 10%
+# improvement on Cortex A8 core and 12.2 cycles per byte.
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$ctx="r0";
+$inp="r1";
+$len="r2";
+$a="r3";
+$b="r4";
+$c="r5";
+$d="r6";
+$e="r7";
+$K="r8";
+$t0="r9";
+$t1="r10";
+$t2="r11";
+$t3="r12";
+$Xi="r14";
+@V=($a,$b,$c,$d,$e);
+
+sub Xupdate {
+my ($a,$b,$c,$d,$e,$opt1,$opt2)=@_;
+$code.=<<___;
+	ldr	$t0,[$Xi,#15*4]
+	ldr	$t1,[$Xi,#13*4]
+	ldr	$t2,[$Xi,#7*4]
+	add	$e,$K,$e,ror#2			@ E+=K_xx_xx
+	ldr	$t3,[$Xi,#2*4]
+	eor	$t0,$t0,$t1
+	eor	$t2,$t2,$t3			@ 1 cycle stall
+	eor	$t1,$c,$d			@ F_xx_xx
+	mov	$t0,$t0,ror#31
+	add	$e,$e,$a,ror#27			@ E+=ROR(A,27)
+	eor	$t0,$t0,$t2,ror#31
+	str	$t0,[$Xi,#-4]!
+	$opt1					@ F_xx_xx
+	$opt2					@ F_xx_xx
+	add	$e,$e,$t0			@ E+=X[i]
+___
+}
+
+sub BODY_00_15 {
+my ($a,$b,$c,$d,$e)=@_;
+$code.=<<___;
+#if __ARM_ARCH__<7
+	ldrb	$t1,[$inp,#2]
+	ldrb	$t0,[$inp,#3]
+	ldrb	$t2,[$inp,#1]
+	add	$e,$K,$e,ror#2			@ E+=K_00_19
+	ldrb	$t3,[$inp],#4
+	orr	$t0,$t0,$t1,lsl#8
+	eor	$t1,$c,$d			@ F_xx_xx
+	orr	$t0,$t0,$t2,lsl#16
+	add	$e,$e,$a,ror#27			@ E+=ROR(A,27)
+	orr	$t0,$t0,$t3,lsl#24
+#else
+	ldr	$t0,[$inp],#4			@ handles unaligned
+	add	$e,$K,$e,ror#2			@ E+=K_00_19
+	eor	$t1,$c,$d			@ F_xx_xx
+	add	$e,$e,$a,ror#27			@ E+=ROR(A,27)
+#ifdef __ARMEL__
+	rev	$t0,$t0				@ byte swap
+#endif
+#endif
+	and	$t1,$b,$t1,ror#2
+	add	$e,$e,$t0			@ E+=X[i]
+	eor	$t1,$t1,$d,ror#2		@ F_00_19(B,C,D)
+	str	$t0,[$Xi,#-4]!
+	add	$e,$e,$t1			@ E+=F_00_19(B,C,D)
+___
+}
+
+sub BODY_16_19 {
+my ($a,$b,$c,$d,$e)=@_;
+	&Xupdate(@_,"and $t1,$b,$t1,ror#2");
+$code.=<<___;
+	eor	$t1,$t1,$d,ror#2		@ F_00_19(B,C,D)
+	add	$e,$e,$t1			@ E+=F_00_19(B,C,D)
+___
+}
+
+sub BODY_20_39 {
+my ($a,$b,$c,$d,$e)=@_;
+	&Xupdate(@_,"eor $t1,$b,$t1,ror#2");
+$code.=<<___;
+	add	$e,$e,$t1			@ E+=F_20_39(B,C,D)
+___
+}
+
+sub BODY_40_59 {
+my ($a,$b,$c,$d,$e)=@_;
+	&Xupdate(@_,"and $t1,$b,$t1,ror#2","and $t2,$c,$d");
+$code.=<<___;
+	add	$e,$e,$t1			@ E+=F_40_59(B,C,D)
+	add	$e,$e,$t2,ror#2
+___
+}
+
+$code=<<___;
+#if defined(__arm__)
+#include "arm_arch.h"
+
+.text
+
+.global	sha1_block_data_order
+.type	sha1_block_data_order,%function
+
+.align	2
+sha1_block_data_order:
+	stmdb	sp!,{r4-r12,lr}
+	add	$len,$inp,$len,lsl#6	@ $len to point at the end of $inp
+	ldmia	$ctx,{$a,$b,$c,$d,$e}
+.Lloop:
+	ldr	$K,.LK_00_19
+	mov	$Xi,sp
+	sub	sp,sp,#15*4
+	mov	$c,$c,ror#30
+	mov	$d,$d,ror#30
+	mov	$e,$e,ror#30		@ [6]
+.L_00_15:
+___
+for($i=0;$i<5;$i++) {
+	&BODY_00_15(@V);	unshift(@V,pop(@V));
+}
+$code.=<<___;
+	teq	$Xi,sp
+	bne	.L_00_15		@ [((11+4)*5+2)*3]
+	sub	sp,sp,#25*4
+___
+	&BODY_00_15(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+$code.=<<___;
+
+	ldr	$K,.LK_20_39		@ [+15+16*4]
+	cmn	sp,#0			@ [+3], clear carry to denote 20_39
+.L_20_39_or_60_79:
+___
+for($i=0;$i<5;$i++) {
+	&BODY_20_39(@V);	unshift(@V,pop(@V));
+}
+$code.=<<___;
+	teq	$Xi,sp			@ preserve carry
+	bne	.L_20_39_or_60_79	@ [+((12+3)*5+2)*4]
+	bcs	.L_done			@ [+((12+3)*5+2)*4], spare 300 bytes
+
+	ldr	$K,.LK_40_59
+	sub	sp,sp,#20*4		@ [+2]
+.L_40_59:
+___
+for($i=0;$i<5;$i++) {
+	&BODY_40_59(@V);	unshift(@V,pop(@V));
+}
+$code.=<<___;
+	teq	$Xi,sp
+	bne	.L_40_59		@ [+((12+5)*5+2)*4]
+
+	ldr	$K,.LK_60_79
+	sub	sp,sp,#20*4
+	cmp	sp,#0			@ set carry to denote 60_79
+	b	.L_20_39_or_60_79	@ [+4], spare 300 bytes
+.L_done:
+	add	sp,sp,#80*4		@ "deallocate" stack frame
+	ldmia	$ctx,{$K,$t0,$t1,$t2,$t3}
+	add	$a,$K,$a
+	add	$b,$t0,$b
+	add	$c,$t1,$c,ror#2
+	add	$d,$t2,$d,ror#2
+	add	$e,$t3,$e,ror#2
+	stmia	$ctx,{$a,$b,$c,$d,$e}
+	teq	$inp,$len
+	bne	.Lloop			@ [+18], total 1307
+
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.align	2
+.LK_00_19:	.word	0x5a827999
+.LK_20_39:	.word	0x6ed9eba1
+.LK_40_59:	.word	0x8f1bbcdc
+.LK_60_79:	.word	0xca62c1d6
+.size	sha1_block_data_order,.-sha1_block_data_order
+.asciz	"SHA1 block transform for ARMv4, CRYPTOGAMS by <appro\@openssl.org>"
+.align	2
+
+#endif
+___
+
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+print $code;
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha1-x86_64.pl b/crypto/sha/asm/sha1-x86_64.pl
new file mode 100644
index 0000000..194b5ff
--- /dev/null
+++ b/crypto/sha/asm/sha1-x86_64.pl
@@ -0,0 +1,1837 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# sha1_block procedure for x86_64.
+#
+# It was brought to my attention that on EM64T compiler-generated code
+# was far behind 32-bit assembler implementation. This is unlike on
+# Opteron where compiler-generated code was only 15% behind 32-bit
+# assembler, which originally made it hard to motivate the effort.
+# There was suggestion to mechanically translate 32-bit code, but I
+# dismissed it, reasoning that x86_64 offers enough register bank
+# capacity to fully utilize SHA-1 parallelism. Therefore this fresh
+# implementation:-) However! While 64-bit code does perform better
+# on Opteron, I failed to beat 32-bit assembler on EM64T core. Well,
+# x86_64 does offer larger *addressable* bank, but out-of-order core
+# reaches for even more registers through dynamic aliasing, and EM64T
+# core must have managed to run-time optimize even 32-bit code just as
+# good as 64-bit one. Performance improvement is summarized in the
+# following table:
+#
+#		gcc 3.4		32-bit asm	cycles/byte
+# Opteron	+45%		+20%		6.8
+# Xeon P4	+65%		+0%		9.9
+# Core2		+60%		+10%		7.0
+
+# August 2009.
+#
+# The code was revised to minimize code size and to maximize
+# "distance" between instructions producing input to 'lea'
+# instruction and the 'lea' instruction itself, which is essential
+# for Intel Atom core.
+
+# October 2010.
+#
+# Add SSSE3, Supplemental[!] SSE3, implementation. The idea behind it
+# is to offload message schedule denoted by Wt in NIST specification,
+# or Xupdate in OpenSSL source, to SIMD unit. See sha1-586.pl module
+# for background and implementation details. The only difference from
+# 32-bit code is that 64-bit code doesn't have to spill @X[] elements
+# to free temporary registers.
+
+# April 2011.
+#
+# Add AVX code path. See sha1-586.pl for further information.
+
+# May 2013.
+#
+# Add AVX2+BMI code path. Initial attempt (utilizing BMI instructions
+# and loading pair of consecutive blocks to 256-bit %ymm registers)
+# did not provide impressive performance improvement till a crucial
+# hint regarding the number of Xupdate iterations to pre-compute in
+# advance was provided by Ilya Albrekht of Intel Corp.
+
+######################################################################
+# Current performance is summarized in following table. Numbers are
+# CPU clock cycles spent to process single byte (less is better).
+#
+#		x86_64		SSSE3		AVX[2]
+# P4		9.8		-
+# Opteron	6.65		-
+# Core2		6.70		6.05/+11%	-
+# Westmere	7.08		5.44/+30%	-
+# Sandy Bridge	7.93		6.16/+28%	4.99/+59%
+# Ivy Bridge	6.30		4.63/+36%	4.60/+37%
+# Haswell	5.98		4.12/+45%	3.57/+67%
+# Bulldozer	10.9		5.95/+82%
+# VIA Nano	10.2		7.46/+37%
+# Atom		11.0		9.61/+14%
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$ctx="%rdi";	# 1st arg
+$inp="%rsi";	# 2nd arg
+$num="%rdx";	# 3rd arg
+
+# reassign arguments in order to produce more compact code
+$ctx="%r8";
+$inp="%r9";
+$num="%r10";
+
+$t0="%eax";
+$t1="%ebx";
+$t2="%ecx";
+@xi=("%edx","%ebp");
+$A="%esi";
+$B="%edi";
+$C="%r11d";
+$D="%r12d";
+$E="%r13d";
+
+@V=($A,$B,$C,$D,$E);
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i==0);
+	mov	`4*$i`($inp),$xi[0]
+	bswap	$xi[0]
+	mov	$xi[0],`4*$i`(%rsp)
+___
+$code.=<<___ if ($i<15);
+	mov	$c,$t0
+	mov	`4*$j`($inp),$xi[1]
+	mov	$a,$t2
+	xor	$d,$t0
+	bswap	$xi[1]
+	rol	\$5,$t2
+	lea	0x5a827999($xi[0],$e),$e
+	and	$b,$t0
+	mov	$xi[1],`4*$j`(%rsp)
+	add	$t2,$e
+	xor	$d,$t0
+	rol	\$30,$b
+	add	$t0,$e
+___
+$code.=<<___ if ($i>=15);
+	mov	`4*($j%16)`(%rsp),$xi[1]
+	mov	$c,$t0
+	mov	$a,$t2
+	xor	`4*(($j+2)%16)`(%rsp),$xi[1]
+	xor	$d,$t0
+	rol	\$5,$t2
+	xor	`4*(($j+8)%16)`(%rsp),$xi[1]
+	and	$b,$t0
+	lea	0x5a827999($xi[0],$e),$e
+	xor	`4*(($j+13)%16)`(%rsp),$xi[1]
+	xor	$d,$t0
+	rol	\$1,$xi[1]
+	add	$t2,$e
+	rol	\$30,$b
+	mov	$xi[1],`4*($j%16)`(%rsp)
+	add	$t0,$e
+___
+unshift(@xi,pop(@xi));
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+my $K=($i<40)?0x6ed9eba1:0xca62c1d6;
+$code.=<<___ if ($i<79);
+	mov	`4*($j%16)`(%rsp),$xi[1]
+	mov	$c,$t0
+	mov	$a,$t2
+	xor	`4*(($j+2)%16)`(%rsp),$xi[1]
+	xor	$b,$t0
+	rol	\$5,$t2
+	lea	$K($xi[0],$e),$e
+	xor	`4*(($j+8)%16)`(%rsp),$xi[1]
+	xor	$d,$t0
+	add	$t2,$e
+	xor	`4*(($j+13)%16)`(%rsp),$xi[1]
+	rol	\$30,$b
+	add	$t0,$e
+	rol	\$1,$xi[1]
+___
+$code.=<<___ if ($i<76);
+	mov	$xi[1],`4*($j%16)`(%rsp)
+___
+$code.=<<___ if ($i==79);
+	mov	$c,$t0
+	mov	$a,$t2
+	xor	$b,$t0
+	lea	$K($xi[0],$e),$e
+	rol	\$5,$t2
+	xor	$d,$t0
+	add	$t2,$e
+	rol	\$30,$b
+	add	$t0,$e
+___
+unshift(@xi,pop(@xi));
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___;
+	mov	`4*($j%16)`(%rsp),$xi[1]
+	mov	$c,$t0
+	mov	$c,$t1
+	xor	`4*(($j+2)%16)`(%rsp),$xi[1]
+	and	$d,$t0
+	mov	$a,$t2
+	xor	`4*(($j+8)%16)`(%rsp),$xi[1]
+	xor	$d,$t1
+	lea	0x8f1bbcdc($xi[0],$e),$e
+	rol	\$5,$t2
+	xor	`4*(($j+13)%16)`(%rsp),$xi[1]
+	add	$t0,$e
+	and	$b,$t1
+	rol	\$1,$xi[1]
+	add	$t1,$e
+	rol	\$30,$b
+	mov	$xi[1],`4*($j%16)`(%rsp)
+	add	$t2,$e
+___
+unshift(@xi,pop(@xi));
+}
+
+$code.=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+.globl	sha1_block_data_order
+.type	sha1_block_data_order,\@function,3
+.align	16
+sha1_block_data_order:
+	mov	OPENSSL_ia32cap_P+0(%rip),%r9d
+	mov	OPENSSL_ia32cap_P+4(%rip),%r8d
+	mov	OPENSSL_ia32cap_P+8(%rip),%r10d
+	test	\$`1<<9`,%r8d		# check SSSE3 bit
+	jz	.Lialu
+___
+$code.=<<___ if ($avx>1);
+	and	\$`1<<3|1<<5|1<<8`,%r10d	# check AVX2+BMI1+BMI2
+	cmp	\$`1<<3|1<<5|1<<8`,%r10d
+	je	_avx2_shortcut
+___
+$code.=<<___ if ($avx);
+	and	\$`1<<28`,%r8d		# mask AVX bit
+	and	\$`1<<30`,%r9d		# mask "Intel CPU" bit
+	or	%r9d,%r8d
+	cmp	\$`1<<28|1<<30`,%r8d
+	je	_avx_shortcut
+___
+$code.=<<___;
+	jmp	_ssse3_shortcut
+
+.align	16
+.Lialu:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	mov	%rsp,%r11
+	mov	%rdi,$ctx	# reassigned argument
+	sub	\$`8+16*4`,%rsp
+	mov	%rsi,$inp	# reassigned argument
+	and	\$-64,%rsp
+	mov	%rdx,$num	# reassigned argument
+	mov	%r11,`16*4`(%rsp)
+.Lprologue:
+
+	mov	0($ctx),$A
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	16($ctx),$E
+	jmp	.Lloop
+
+.align	16
+.Lloop:
+___
+for($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	add	0($ctx),$A
+	add	4($ctx),$B
+	add	8($ctx),$C
+	add	12($ctx),$D
+	add	16($ctx),$E
+	mov	$A,0($ctx)
+	mov	$B,4($ctx)
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+
+	sub	\$1,$num
+	lea	`16*4`($inp),$inp
+	jnz	.Lloop
+
+	mov	`16*4`(%rsp),%rsi
+	mov	(%rsi),%r13
+	mov	8(%rsi),%r12
+	mov	16(%rsi),%rbp
+	mov	24(%rsi),%rbx
+	lea	32(%rsi),%rsp
+.Lepilogue:
+	ret
+.size	sha1_block_data_order,.-sha1_block_data_order
+___
+{{{
+my $Xi=4;
+my @X=map("%xmm$_",(4..7,0..3));
+my @Tx=map("%xmm$_",(8..10));
+my $Kx="%xmm11";
+my @V=($A,$B,$C,$D,$E)=("%eax","%ebx","%ecx","%edx","%ebp");	# size optimization
+my @T=("%esi","%edi");
+my $j=0;
+my $rx=0;
+my $K_XX_XX="%r11";
+
+my $_rol=sub { &rol(@_) };
+my $_ror=sub { &ror(@_) };
+
+{ my $sn;
+sub align32() {
+  ++$sn;
+$code.=<<___;
+	jmp	.Lalign32_$sn	# see "Decoded ICache" in manual
+.align	32
+.Lalign32_$sn:
+___
+}
+}
+
+$code.=<<___;
+.type	sha1_block_data_order_ssse3,\@function,3
+.align	16
+sha1_block_data_order_ssse3:
+_ssse3_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13		# redundant, done to share Win64 SE handler
+	push	%r14
+	lea	`-64-($win64?6*16:0)`(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,-40-6*16(%rax)
+	movaps	%xmm7,-40-5*16(%rax)
+	movaps	%xmm8,-40-4*16(%rax)
+	movaps	%xmm9,-40-3*16(%rax)
+	movaps	%xmm10,-40-2*16(%rax)
+	movaps	%xmm11,-40-1*16(%rax)
+.Lprologue_ssse3:
+___
+$code.=<<___;
+	mov	%rax,%r14	# original %rsp
+	and	\$-64,%rsp
+	mov	%rdi,$ctx	# reassigned argument
+	mov	%rsi,$inp	# reassigned argument
+	mov	%rdx,$num	# reassigned argument
+
+	shl	\$6,$num
+	add	$inp,$num
+	lea	K_XX_XX+64(%rip),$K_XX_XX
+
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	movdqa	64($K_XX_XX),@X[2]	# pbswap mask
+	movdqa	-64($K_XX_XX),@Tx[1]	# K_00_19
+	movdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	movdqu	16($inp),@X[-3&7]
+	movdqu	32($inp),@X[-2&7]
+	movdqu	48($inp),@X[-1&7]
+	pshufb	@X[2],@X[-4&7]		# byte swap
+	add	\$64,$inp
+	pshufb	@X[2],@X[-3&7]
+	pshufb	@X[2],@X[-2&7]
+	pshufb	@X[2],@X[-1&7]
+	paddd	@Tx[1],@X[-4&7]		# add K_00_19
+	paddd	@Tx[1],@X[-3&7]
+	paddd	@Tx[1],@X[-2&7]
+	movdqa	@X[-4&7],0(%rsp)	# X[]+K xfer to IALU
+	psubd	@Tx[1],@X[-4&7]		# restore X[]
+	movdqa	@X[-3&7],16(%rsp)
+	psubd	@Tx[1],@X[-3&7]
+	movdqa	@X[-2&7],32(%rsp)
+	psubd	@Tx[1],@X[-2&7]
+	jmp	.Loop_ssse3
+___
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+sub Xupdate_ssse3_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&movdqa	(@X[0],@X[-3&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(@Tx[0],@X[-1&7]);
+	&palignr(@X[0],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psrldq	(@Tx[0],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&movdqa	(@Tx[2],@X[0]);
+	&movdqa	(@Tx[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslldq	(@Tx[2],12);		# "X[0]"<<96, extract one dword
+	&paddd	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@Tx[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(@Tx[1],@Tx[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@Tx[2],30);
+	&por	(@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslld	(@Tx[1],2);
+	&pxor	(@X[0],@Tx[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(@Tx[2],eval(2*16*(($Xi)/5)-64)."($K_XX_XX)");	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[1]);		# "X[0]"^=("X[0]">>96)<<<2
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+		push(@Tx,shift(@Tx));
+}
+
+sub Xupdate_ssse3_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&movdqa	(@Tx[0],@X[-1&7])	if ($Xi==8);
+	 eval(shift(@insns));		# body_20_39
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	&palignr(@Tx[0],@X[-2&7],8);	# compose "X[-6]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&pxor	(@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns))	if (@insns[0] !~ /&ro[rl]/);
+	if ($Xi%5) {
+	  &movdqa	(@Tx[2],@Tx[1]);# "perpetuate" K_XX_XX...
+	} else {			# ... or load next one
+	  &movdqa	(@Tx[2],eval(2*16*($Xi/5)-64)."($K_XX_XX)");
+	}
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&movdqa	(@Tx[0],@X[0]);
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&pslld	(@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	&psrld	(@Tx[0],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&por	(@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	  &movdqa	(@Tx[1],@X[0])	if ($Xi<19);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+		push(@Tx,shift(@Tx));
+}
+
+sub Xuplast_ssse3_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&cmp	($inp,$num);
+	&je	(".Ldone_ssse3");
+
+	unshift(@Tx,pop(@Tx));
+
+	&movdqa	(@X[2],"64($K_XX_XX)");		# pbswap mask
+	&movdqa	(@Tx[1],"-64($K_XX_XX)");	# K_00_19
+	&movdqu	(@X[-4&7],"0($inp)");		# load input
+	&movdqu	(@X[-3&7],"16($inp)");
+	&movdqu	(@X[-2&7],"32($inp)");
+	&movdqu	(@X[-1&7],"48($inp)");
+	&pshufb	(@X[-4&7],@X[2]);		# byte swap
+	&add	($inp,64);
+
+  $Xi=0;
+}
+
+sub Xloop_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pshufb	(@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddd	(@X[($Xi-4)&7],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(eval(16*$Xi)."(%rsp)",@X[($Xi-4)&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psubd	(@X[($Xi-4)&7],@Tx[1]);
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+sub body_00_19 () {	# ((c^d)&b)^d
+	# on start @T[0]=(c^d)&b
+	return &body_20_39() if ($rx==19); $rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&$_ror	($b,$j?7:2)',	# $b>>>2
+	'&xor	(@T[0],$d)',
+	'&mov	(@T[1],$a)',	# $b for next round
+
+	'&add	($e,eval(4*($j&15))."(%rsp)")',	# X[]+K xfer
+	'&xor	($b,$c)',	# $c^$d for next round
+
+	'&$_rol	($a,5)',
+	'&add	($e,@T[0])',
+	'&and	(@T[1],$b)',	# ($b&($c^$d)) for next round
+
+	'&xor	($b,$c)',	# restore $b
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_20_39 () {	# b^d^c
+	# on entry @T[0]=b^d
+	return &body_40_59() if ($rx==39); $rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,eval(4*($j&15))."(%rsp)")',	# X[]+K xfer
+	'&xor	(@T[0],$d)	if($j==19);'.
+	'&xor	(@T[0],$c)	if($j> 19)',	# ($b^$d^$c)
+	'&mov	(@T[1],$a)',	# $b for next round
+
+	'&$_rol	($a,5)',
+	'&add	($e,@T[0])',
+	'&xor	(@T[1],$c)	if ($j< 79)',	# $b^$d for next round
+
+	'&$_ror	($b,7)',	# $b>>>2
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_40_59 () {	# ((b^c)&(c^d))^c
+	# on entry @T[0]=(b^c), (c^=d)
+	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,eval(4*($j&15))."(%rsp)")',	# X[]+K xfer
+	'&and	(@T[0],$c)	if ($j>=40)',	# (b^c)&(c^d)
+	'&xor	($c,$d)		if ($j>=40)',	# restore $c
+
+	'&$_ror	($b,7)',	# $b>>>2
+	'&mov	(@T[1],$a)',	# $b for next round
+	'&xor	(@T[0],$c)',
+
+	'&$_rol	($a,5)',
+	'&add	($e,@T[0])',
+	'&xor	(@T[1],$c)	if ($j==59);'.
+	'&xor	(@T[1],$b)	if ($j< 59)',	# b^c for next round
+
+	'&xor	($b,$c)		if ($j< 59)',	# c^d for next round
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+$code.=<<___;
+.align	16
+.Loop_ssse3:
+___
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xuplast_ssse3_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_ssse3
+
+.align	16
+.Ldone_ssse3:
+___
+				$j=$saved_j; @V=@saved_V;
+
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-40-6*16(%r14),%xmm6
+	movaps	-40-5*16(%r14),%xmm7
+	movaps	-40-4*16(%r14),%xmm8
+	movaps	-40-3*16(%r14),%xmm9
+	movaps	-40-2*16(%r14),%xmm10
+	movaps	-40-1*16(%r14),%xmm11
+___
+$code.=<<___;
+	lea	(%r14),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue_ssse3:
+	ret
+.size	sha1_block_data_order_ssse3,.-sha1_block_data_order_ssse3
+___
+
+if ($avx) {
+$Xi=4;				# reset variables
+@X=map("%xmm$_",(4..7,0..3));
+@Tx=map("%xmm$_",(8..10));
+$j=0;
+$rx=0;
+
+my $done_avx_label=".Ldone_avx";
+
+my $_rol=sub { &shld(@_[0],@_) };
+my $_ror=sub { &shrd(@_[0],@_) };
+
+$code.=<<___;
+.type	sha1_block_data_order_avx,\@function,3
+.align	16
+sha1_block_data_order_avx:
+_avx_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13		# redundant, done to share Win64 SE handler
+	push	%r14
+	lea	`-64-($win64?6*16:0)`(%rsp),%rsp
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	vmovaps	%xmm6,-40-6*16(%rax)
+	vmovaps	%xmm7,-40-5*16(%rax)
+	vmovaps	%xmm8,-40-4*16(%rax)
+	vmovaps	%xmm9,-40-3*16(%rax)
+	vmovaps	%xmm10,-40-2*16(%rax)
+	vmovaps	%xmm11,-40-1*16(%rax)
+.Lprologue_avx:
+___
+$code.=<<___;
+	mov	%rax,%r14	# original %rsp
+	and	\$-64,%rsp
+	mov	%rdi,$ctx	# reassigned argument
+	mov	%rsi,$inp	# reassigned argument
+	mov	%rdx,$num	# reassigned argument
+
+	shl	\$6,$num
+	add	$inp,$num
+	lea	K_XX_XX+64(%rip),$K_XX_XX
+
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	vmovdqa	64($K_XX_XX),@X[2]	# pbswap mask
+	vmovdqa	-64($K_XX_XX),$Kx	# K_00_19
+	vmovdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	vmovdqu	16($inp),@X[-3&7]
+	vmovdqu	32($inp),@X[-2&7]
+	vmovdqu	48($inp),@X[-1&7]
+	vpshufb	@X[2],@X[-4&7],@X[-4&7]	# byte swap
+	add	\$64,$inp
+	vpshufb	@X[2],@X[-3&7],@X[-3&7]
+	vpshufb	@X[2],@X[-2&7],@X[-2&7]
+	vpshufb	@X[2],@X[-1&7],@X[-1&7]
+	vpaddd	$Kx,@X[-4&7],@X[0]	# add K_00_19
+	vpaddd	$Kx,@X[-3&7],@X[1]
+	vpaddd	$Kx,@X[-2&7],@X[2]
+	vmovdqa	@X[0],0(%rsp)		# X[]+K xfer to IALU
+	vmovdqa	@X[1],16(%rsp)
+	vmovdqa	@X[2],32(%rsp)
+	jmp	.Loop_avx
+___
+
+sub Xupdate_avx_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@Tx[2],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[1],@Tx[2],30);
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@Tx[2],@Tx[2],2);
+	&vpxor	(@X[0],@X[0],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[2]);		# "X[0]"^=("X[0]">>96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	($Kx,eval(2*16*(($Xi)/5)-64)."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@Tx[0],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);		# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns))	if (@insns[0] !~ /&ro[rl]/);
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	  &vmovdqa	($Kx,eval(2*16*($Xi/5)-64)."($K_XX_XX)")	if ($Xi%5==0);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpsrld	(@Tx[0],@X[0],30);
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_avx_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&cmp	($inp,$num);
+	&je	($done_avx_label);
+
+	&vmovdqa(@X[2],"64($K_XX_XX)");		# pbswap mask
+	&vmovdqa($Kx,"-64($K_XX_XX)");		# K_00_19
+	&vmovdqu(@X[-4&7],"0($inp)");		# load input
+	&vmovdqu(@X[-3&7],"16($inp)");
+	&vmovdqu(@X[-2&7],"32($inp)");
+	&vmovdqu(@X[-1&7],"48($inp)");
+	&vpshufb(@X[-4&7],@X[-4&7],@X[2]);	# byte swap
+	&add	($inp,64);
+
+  $Xi=0;
+}
+
+sub Xloop_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpshufb(@X[($Xi-3)&7],@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpaddd	(@X[$Xi&7],@X[($Xi-4)&7],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqa(eval(16*$Xi)."(%rsp)",@X[$Xi&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+$code.=<<___;
+.align	16
+.Loop_avx:
+___
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xuplast_avx_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_avx
+
+.align	16
+$done_avx_label:
+___
+				$j=$saved_j; @V=@saved_V;
+
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+
+$code.=<<___;
+	vzeroupper
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-40-6*16(%r14),%xmm6
+	movaps	-40-5*16(%r14),%xmm7
+	movaps	-40-4*16(%r14),%xmm8
+	movaps	-40-3*16(%r14),%xmm9
+	movaps	-40-2*16(%r14),%xmm10
+	movaps	-40-1*16(%r14),%xmm11
+___
+$code.=<<___;
+	lea	(%r14),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue_avx:
+	ret
+.size	sha1_block_data_order_avx,.-sha1_block_data_order_avx
+___
+
+if ($avx>1) {
+use integer;
+$Xi=4;					# reset variables
+@X=map("%ymm$_",(4..7,0..3));
+@Tx=map("%ymm$_",(8..10));
+$Kx="%ymm11";
+$j=0;
+
+my @ROTX=("%eax","%ebp","%ebx","%ecx","%edx","%esi");
+my ($a5,$t0)=("%r12d","%edi");
+
+my ($A,$F,$B,$C,$D,$E)=@ROTX;
+my $rx=0;
+my $frame="%r13";
+
+$code.=<<___;
+.type	sha1_block_data_order_avx2,\@function,3
+.align	16
+sha1_block_data_order_avx2:
+_avx2_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	lea	-6*16(%rsp),%rsp
+	vmovaps	%xmm6,-40-6*16(%rax)
+	vmovaps	%xmm7,-40-5*16(%rax)
+	vmovaps	%xmm8,-40-4*16(%rax)
+	vmovaps	%xmm9,-40-3*16(%rax)
+	vmovaps	%xmm10,-40-2*16(%rax)
+	vmovaps	%xmm11,-40-1*16(%rax)
+.Lprologue_avx2:
+___
+$code.=<<___;
+	mov	%rax,%r14		# original %rsp
+	mov	%rdi,$ctx		# reassigned argument
+	mov	%rsi,$inp		# reassigned argument
+	mov	%rdx,$num		# reassigned argument
+
+	lea	-640(%rsp),%rsp
+	shl	\$6,$num
+	 lea	64($inp),$frame
+	and	\$-128,%rsp
+	add	$inp,$num
+	lea	K_XX_XX+64(%rip),$K_XX_XX
+
+	mov	0($ctx),$A		# load context
+	 cmp	$num,$frame
+	 cmovae	$inp,$frame		# next or same block
+	mov	4($ctx),$F
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	16($ctx),$E
+	vmovdqu	64($K_XX_XX),@X[2]	# pbswap mask
+
+	vmovdqu		($inp),%xmm0
+	vmovdqu		16($inp),%xmm1
+	vmovdqu		32($inp),%xmm2
+	vmovdqu		48($inp),%xmm3
+	lea		64($inp),$inp
+	vinserti128	\$1,($frame),@X[-4&7],@X[-4&7]
+	vinserti128	\$1,16($frame),@X[-3&7],@X[-3&7]
+	vpshufb		@X[2],@X[-4&7],@X[-4&7]
+	vinserti128	\$1,32($frame),@X[-2&7],@X[-2&7]
+	vpshufb		@X[2],@X[-3&7],@X[-3&7]
+	vinserti128	\$1,48($frame),@X[-1&7],@X[-1&7]
+	vpshufb		@X[2],@X[-2&7],@X[-2&7]
+	vmovdqu		-64($K_XX_XX),$Kx	# K_00_19
+	vpshufb		@X[2],@X[-1&7],@X[-1&7]
+
+	vpaddd	$Kx,@X[-4&7],@X[0]	# add K_00_19
+	vpaddd	$Kx,@X[-3&7],@X[1]
+	vmovdqu	@X[0],0(%rsp)		# X[]+K xfer to IALU
+	vpaddd	$Kx,@X[-2&7],@X[2]
+	vmovdqu	@X[1],32(%rsp)
+	vpaddd	$Kx,@X[-1&7],@X[3]
+	vmovdqu	@X[2],64(%rsp)
+	vmovdqu	@X[3],96(%rsp)
+___
+for (;$Xi<8;$Xi++) {	# Xupdate_avx2_16_31
+    use integer;
+
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	&vpsrld	(@Tx[0],@X[0],31);
+	&vmovdqu($Kx,eval(2*16*(($Xi)/5)-64)."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	&vpslldq(@Tx[2],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	&vpsrld	(@Tx[1],@Tx[2],30);
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	&vpslld	(@Tx[2],@Tx[2],2);
+	&vpxor	(@X[0],@X[0],@Tx[1]);
+	&vpxor	(@X[0],@X[0],@Tx[2]);		# "X[0]"^=("X[0]">>96)<<<2
+	&vpaddd	(@Tx[1],@X[0],$Kx);
+	&vmovdqu("32*$Xi(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+
+	push(@X,shift(@X));	# "rotate" X[]
+}
+$code.=<<___;
+	lea	128(%rsp),$frame
+	jmp	.Loop_avx2
+.align	32
+.Loop_avx2:
+	rorx	\$2,$F,$B
+	andn	$D,$F,$t0
+	and	$C,$F
+	xor	$t0,$F
+___
+sub bodyx_00_19 () {	# 8 instructions, 3 cycles critical path
+	# at start $f=(b&c)^(~b&d), $b>>>=2
+	return &bodyx_20_39() if ($rx==19); $rx++;
+	(
+	'($a,$f,$b,$c,$d,$e)=@ROTX;'.
+
+	'&add	($e,((32*($j/4)+4*($j%4))%256-128)."($frame)");'.	# e+=X[i]+K
+	 '&lea	($frame,"256($frame)")	if ($j%32==31);',
+	'&andn	($t0,$a,$c)',			# ~b&d for next round
+
+	'&add	($e,$f)',			# e+=(b&c)^(~b&d)
+	'&rorx	($a5,$a,27)',			# a<<<5
+	'&rorx	($f,$a,2)',			# b>>>2 for next round
+	'&and	($a,$b)',			# b&c for next round
+
+	'&add	($e,$a5)',			# e+=a<<<5
+	'&xor	($a,$t0);'.			# f=(b&c)^(~b&d) for next round
+
+	'unshift(@ROTX,pop(@ROTX)); $j++;'
+	)
+}
+
+sub bodyx_20_39 () {	# 7 instructions, 2 cycles critical path
+	# on entry $f=b^c^d, $b>>>=2
+	return &bodyx_40_59() if ($rx==39); $rx++;
+	(
+	'($a,$f,$b,$c,$d,$e)=@ROTX;'.
+
+	'&add	($e,((32*($j/4)+4*($j%4))%256-128)."($frame)");'.	# e+=X[i]+K
+	 '&lea	($frame,"256($frame)")	if ($j%32==31);',
+
+	'&lea	($e,"($e,$f)")',		# e+=b^c^d
+	'&rorx	($a5,$a,27)',			# a<<<5
+	'&rorx	($f,$a,2)	if ($j<79)',	# b>>>2 in next round
+	'&xor	($a,$b)		if ($j<79)',	# b^c for next round
+
+	'&add	($e,$a5)',			# e+=a<<<5
+	'&xor	($a,$c)		if ($j<79);'.	# f=b^c^d for next round
+
+	'unshift(@ROTX,pop(@ROTX)); $j++;'
+	)
+}
+
+sub bodyx_40_59 () {	# 10 instructions, 3 cycles critical path
+	# on entry $f=((b^c)&(c^d)), $b>>>=2
+	$rx++;
+	(
+	'($a,$f,$b,$c,$d,$e)=@ROTX;'.
+
+	'&add	($e,((32*($j/4)+4*($j%4))%256-128)."($frame)");'.	# e+=X[i]+K
+	 '&lea	($frame,"256($frame)")	if ($j%32==31);',
+	'&xor	($f,$c)		if ($j>39)',	# (b^c)&(c^d)^c
+	'&mov	($t0,$b)	if ($j<59)',	# count on zero latency
+	'&xor	($t0,$c)	if ($j<59)',	# c^d for next round
+
+	'&lea	($e,"($e,$f)")',		# e+=(b^c)&(c^d)^c
+	'&rorx	($a5,$a,27)',			# a<<<5
+	'&rorx	($f,$a,2)',			# b>>>2 in next round
+	'&xor	($a,$b)',			# b^c for next round
+
+	'&add	($e,$a5)',			# e+=a<<<5
+	'&and	($a,$t0)	if ($j< 59);'.	# f=(b^c)&(c^d) for next round
+	'&xor	($a,$c)		if ($j==59);'.	# f=b^c^d for next round
+
+	'unshift(@ROTX,pop(@ROTX)); $j++;'
+	)
+}
+
+sub Xupdate_avx2_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body,&$body);	# 35 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],31);
+	&vmovdqu($Kx,eval(2*16*(($Xi)/5)-64)."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@Tx[2],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[1],@Tx[2],30);
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@Tx[2],@Tx[2],2);
+	&vpxor	(@X[0],@X[0],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[2]);		# "X[0]"^=("X[0]">>96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpaddd	(@Tx[1],@X[0],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqu(eval(32*($Xi))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+	$Xi++;
+	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx2_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body,&$body);	# 35 to 50 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@Tx[0],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);		# "X[0]"^="X[-28]"
+	&vmovdqu($Kx,eval(2*16*($Xi/5)-64)."($K_XX_XX)")	if ($Xi%5==0);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],30);
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	#&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpaddd	(@Tx[1],@X[0],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vmovdqu("32*$Xi(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+	$Xi++;
+	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xloop_avx2()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 foreach (@insns) { eval; }
+}
+
+	&align32();
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+
+	&align32();
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+
+	&Xloop_avx2(\&bodyx_20_39);
+	&Xloop_avx2(\&bodyx_20_39);
+	&Xloop_avx2(\&bodyx_20_39);
+	&Xloop_avx2(\&bodyx_20_39);
+
+$code.=<<___;
+	lea	128($inp),$frame
+	lea	128($inp),%rdi			# borrow $t0
+	cmp	$num,$frame
+	cmovae	$inp,$frame			# next or previous block
+
+	# output is d-e-[a]-f-b-c => A=d,F=e,C=f,D=b,E=c
+	add	0($ctx),@ROTX[0]		# update context
+	add	4($ctx),@ROTX[1]
+	add	8($ctx),@ROTX[3]
+	mov	@ROTX[0],0($ctx)
+	add	12($ctx),@ROTX[4]
+	mov	@ROTX[1],4($ctx)
+	 mov	@ROTX[0],$A			# A=d
+	add	16($ctx),@ROTX[5]
+	 mov	@ROTX[3],$a5
+	mov	@ROTX[3],8($ctx)
+	 mov	@ROTX[4],$D			# D=b
+	 #xchg	@ROTX[5],$F			# F=c, C=f
+	mov	@ROTX[4],12($ctx)
+	 mov	@ROTX[1],$F			# F=e
+	mov	@ROTX[5],16($ctx)
+	#mov	$F,16($ctx)
+	 mov	@ROTX[5],$E			# E=c
+	 mov	$a5,$C				# C=f
+	 #xchg	$F,$E				# E=c, F=e
+
+	cmp	$num,$inp
+	je	.Ldone_avx2
+___
+
+$Xi=4;				# reset variables
+@X=map("%ymm$_",(4..7,0..3));
+
+$code.=<<___;
+	vmovdqu	64($K_XX_XX),@X[2]		# pbswap mask
+	cmp	$num,%rdi			# borrowed $t0
+	ja	.Last_avx2
+
+	vmovdqu		-64(%rdi),%xmm0		# low part of @X[-4&7]
+	vmovdqu		-48(%rdi),%xmm1
+	vmovdqu		-32(%rdi),%xmm2
+	vmovdqu		-16(%rdi),%xmm3
+	vinserti128	\$1,0($frame),@X[-4&7],@X[-4&7]
+	vinserti128	\$1,16($frame),@X[-3&7],@X[-3&7]
+	vinserti128	\$1,32($frame),@X[-2&7],@X[-2&7]
+	vinserti128	\$1,48($frame),@X[-1&7],@X[-1&7]
+	jmp	.Last_avx2
+
+.align	32
+.Last_avx2:
+	lea	128+16(%rsp),$frame
+	rorx	\$2,$F,$B
+	andn	$D,$F,$t0
+	and	$C,$F
+	xor	$t0,$F
+	sub	\$-128,$inp
+___
+	$rx=$j=0;	@ROTX=($A,$F,$B,$C,$D,$E);
+
+	&Xloop_avx2	(\&bodyx_00_19);
+	&Xloop_avx2	(\&bodyx_00_19);
+	&Xloop_avx2	(\&bodyx_00_19);
+	&Xloop_avx2	(\&bodyx_00_19);
+
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vmovdqu	($Kx,"-64($K_XX_XX)");		# K_00_19
+	  &vpshufb	(@X[-4&7],@X[-4&7],@X[2]);	# byte swap
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vpshufb	(@X[-3&7],@X[-3&7],@X[2]);
+	  &vpaddd	(@Tx[0],@X[-4&7],$Kx);		# add K_00_19
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vmovdqu	("0(%rsp)",@Tx[0]);
+	  &vpshufb	(@X[-2&7],@X[-2&7],@X[2]);
+	  &vpaddd	(@Tx[1],@X[-3&7],$Kx);
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vmovdqu	("32(%rsp)",@Tx[1]);
+	  &vpshufb	(@X[-1&7],@X[-1&7],@X[2]);
+	  &vpaddd	(@X[2],@X[-2&7],$Kx);
+
+	&Xloop_avx2	(\&bodyx_40_59);
+	&align32	();
+	  &vmovdqu	("64(%rsp)",@X[2]);
+	  &vpaddd	(@X[3],@X[-1&7],$Kx);
+	&Xloop_avx2	(\&bodyx_40_59);
+	  &vmovdqu	("96(%rsp)",@X[3]);
+	&Xloop_avx2	(\&bodyx_40_59);
+	&Xupdate_avx2_16_31(\&bodyx_40_59);
+
+	&Xupdate_avx2_16_31(\&bodyx_20_39);
+	&Xupdate_avx2_16_31(\&bodyx_20_39);
+	&Xupdate_avx2_16_31(\&bodyx_20_39);
+	&Xloop_avx2	(\&bodyx_20_39);
+
+$code.=<<___;
+	lea	128(%rsp),$frame
+
+	# output is d-e-[a]-f-b-c => A=d,F=e,C=f,D=b,E=c
+	add	0($ctx),@ROTX[0]		# update context
+	add	4($ctx),@ROTX[1]
+	add	8($ctx),@ROTX[3]
+	mov	@ROTX[0],0($ctx)
+	add	12($ctx),@ROTX[4]
+	mov	@ROTX[1],4($ctx)
+	 mov	@ROTX[0],$A			# A=d
+	add	16($ctx),@ROTX[5]
+	 mov	@ROTX[3],$a5
+	mov	@ROTX[3],8($ctx)
+	 mov	@ROTX[4],$D			# D=b
+	 #xchg	@ROTX[5],$F			# F=c, C=f
+	mov	@ROTX[4],12($ctx)
+	 mov	@ROTX[1],$F			# F=e
+	mov	@ROTX[5],16($ctx)
+	#mov	$F,16($ctx)
+	 mov	@ROTX[5],$E			# E=c
+	 mov	$a5,$C				# C=f
+	 #xchg	$F,$E				# E=c, F=e
+
+	cmp	$num,$inp
+	jbe	.Loop_avx2
+
+.Ldone_avx2:
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-40-6*16(%r14),%xmm6
+	movaps	-40-5*16(%r14),%xmm7
+	movaps	-40-4*16(%r14),%xmm8
+	movaps	-40-3*16(%r14),%xmm9
+	movaps	-40-2*16(%r14),%xmm10
+	movaps	-40-1*16(%r14),%xmm11
+___
+$code.=<<___;
+	lea	(%r14),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue_avx2:
+	ret
+.size	sha1_block_data_order_avx2,.-sha1_block_data_order_avx2
+___
+}
+}
+$code.=<<___;
+.align	64
+K_XX_XX:
+.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap mask
+.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap mask
+___
+}}}
+$code.=<<___;
+.asciz	"SHA1 block transform for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lcommon_seh_tail
+
+	mov	`16*4`(%rax),%rax	# pull saved stack pointer
+	lea	32(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+
+	jmp	.Lcommon_seh_tail
+.size	se_handler,.-se_handler
+
+.type	ssse3_handler,\@abi-omnipotent
+.align	16
+ssse3_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	232($context),%rax	# pull context->R14
+
+	lea	-40-6*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$12,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore cotnext->R12
+	mov	%r13,224($context)	# restore cotnext->R13
+	mov	%r14,232($context)	# restore cotnext->R14
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	ssse3_handler,.-ssse3_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_sha1_block_data_order
+	.rva	.LSEH_end_sha1_block_data_order
+	.rva	.LSEH_info_sha1_block_data_order
+	.rva	.LSEH_begin_sha1_block_data_order_ssse3
+	.rva	.LSEH_end_sha1_block_data_order_ssse3
+	.rva	.LSEH_info_sha1_block_data_order_ssse3
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_sha1_block_data_order_avx
+	.rva	.LSEH_end_sha1_block_data_order_avx
+	.rva	.LSEH_info_sha1_block_data_order_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_sha1_block_data_order_avx2
+	.rva	.LSEH_end_sha1_block_data_order_avx2
+	.rva	.LSEH_info_sha1_block_data_order_avx2
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_sha1_block_data_order:
+	.byte	9,0,0,0
+	.rva	se_handler
+.LSEH_info_sha1_block_data_order_ssse3:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_ssse3,.Lepilogue_ssse3	# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_sha1_block_data_order_avx:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_sha1_block_data_order_avx2:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+}
+
+####################################################################
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha256-586.pl b/crypto/sha/asm/sha256-586.pl
new file mode 100644
index 0000000..bd48b63
--- /dev/null
+++ b/crypto/sha/asm/sha256-586.pl
@@ -0,0 +1,1125 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA256 block transform for x86. September 2007.
+#
+# Performance improvement over compiler generated code varies from
+# 10% to 40% [see below]. Not very impressive on some µ-archs, but
+# it's 5 times smaller and optimizies amount of writes.
+#
+# May 2012.
+#
+# Optimization including two of Pavel Semjanov's ideas, alternative
+# Maj and full unroll, resulted in ~20-25% improvement on most CPUs,
+# ~7% on Pentium, ~40% on Atom. As fully unrolled loop body is almost
+# 15x larger, 8KB vs. 560B, it's fired only for longer inputs. But not
+# on P4, where it kills performance, nor Sandy Bridge, where folded
+# loop is approximately as fast...
+#
+# June 2012.
+#
+# Add AMD XOP-specific code path, >30% improvement on Bulldozer over
+# May version, >60% over original. Add AVX+shrd code path, >25%
+# improvement on Sandy Bridge over May version, 60% over original.
+#
+# May 2013.
+#
+# Replace AMD XOP code path with SSSE3 to cover more processors.
+# (Biggest improvement coefficient is on upcoming Atom Silvermont,
+# not shown.) Add AVX+BMI code path.
+#
+# Performance in clock cycles per processed byte (less is better):
+#
+#		gcc	icc	x86 asm(*)	SIMD	x86_64 asm(**)	
+# Pentium	46	57	40/38		-	-
+# PIII		36	33	27/24		-	-
+# P4		41	38	28		-	17.3
+# AMD K8	27	25	19/15.5		-	14.9
+# Core2		26	23	18/15.6		14.3	13.8
+# Westmere	27	-	19/15.7		13.4	12.3
+# Sandy Bridge	25	-	15.9		12.4	11.6
+# Ivy Bridge	24	-	15.0		11.4	10.3
+# Haswell	22	-	13.9		9.46	7.80
+# Bulldozer	36	-	27/22		17.0	13.6
+# VIA Nano	36	-	25/22		16.8	16.5
+# Atom		50	-	30/25		21.9	18.9
+#
+# (*)	numbers after slash are for unrolled loop, where applicable;
+# (**)	x86_64 assembly performance is presented for reference
+#	purposes, results are best-available;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"sha512-586.pl",$ARGV[$#ARGV] eq "386");
+
+$xmm=$avx=0;
+for (@ARGV) { $xmm=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+if ($xmm &&	`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+			=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if ($xmm && !$avx && $ARGV[0] eq "win32n" &&
+		`nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.03) + ($1>=2.10);
+}
+
+if ($xmm && !$avx && $ARGV[0] eq "win32" &&
+		`ml 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+$unroll_after = 64*4;	# If pre-evicted from L1P cache first spin of
+			# fully unrolled loop was measured to run about
+			# 3-4x slower. If slowdown coefficient is N and
+			# unrolled loop is m times faster, then you break
+			# even at (N-1)/(m-1) blocks. Then it needs to be
+			# adjusted for probability of code being evicted,
+			# code size/cache size=1/4. Typical m is 1.15...
+
+$A="eax";
+$E="edx";
+$T="ebx";
+$Aoff=&DWP(4,"esp");
+$Boff=&DWP(8,"esp");
+$Coff=&DWP(12,"esp");
+$Doff=&DWP(16,"esp");
+$Eoff=&DWP(20,"esp");
+$Foff=&DWP(24,"esp");
+$Goff=&DWP(28,"esp");
+$Hoff=&DWP(32,"esp");
+$Xoff=&DWP(36,"esp");
+$K256="ebp";
+
+sub BODY_16_63() {
+	&mov	($T,"ecx");			# "ecx" is preloaded
+	 &mov	("esi",&DWP(4*(9+15+16-14),"esp"));
+	&ror	("ecx",18-7);
+	 &mov	("edi","esi");
+	&ror	("esi",19-17);
+	 &xor	("ecx",$T);
+	 &shr	($T,3);
+	&ror	("ecx",7);
+	 &xor	("esi","edi");
+	 &xor	($T,"ecx");			# T = sigma0(X[-15])
+	&ror	("esi",17);
+	 &add	($T,&DWP(4*(9+15+16),"esp"));	# T += X[-16]
+	&shr	("edi",10);
+	 &add	($T,&DWP(4*(9+15+16-9),"esp"));	# T += X[-7]
+	#&xor	("edi","esi")			# sigma1(X[-2])
+	# &add	($T,"edi");			# T += sigma1(X[-2])
+	# &mov	(&DWP(4*(9+15),"esp"),$T);	# save X[0]
+
+	&BODY_00_15(1);
+}
+sub BODY_00_15() {
+    my $in_16_63=shift;
+
+	&mov	("ecx",$E);
+	 &xor	("edi","esi")			if ($in_16_63);	# sigma1(X[-2])
+	 &mov	("esi",$Foff);
+	&ror	("ecx",25-11);
+	 &add	($T,"edi")			if ($in_16_63);	# T += sigma1(X[-2])
+	 &mov	("edi",$Goff);
+	&xor	("ecx",$E);
+	 &xor	("esi","edi");
+	 &mov	($T,&DWP(4*(9+15),"esp"))	if (!$in_16_63);
+	 &mov	(&DWP(4*(9+15),"esp"),$T)	if ($in_16_63);	# save X[0]
+	&ror	("ecx",11-6);
+	 &and	("esi",$E);
+	 &mov	($Eoff,$E);		# modulo-scheduled
+	&xor	($E,"ecx");
+	 &add	($T,$Hoff);		# T += h
+	 &xor	("esi","edi");		# Ch(e,f,g)
+	&ror	($E,6);			# Sigma1(e)
+	 &mov	("ecx",$A);
+	 &add	($T,"esi");		# T += Ch(e,f,g)
+
+	&ror	("ecx",22-13);
+	 &add	($T,$E);		# T += Sigma1(e)
+	 &mov	("edi",$Boff);
+	&xor	("ecx",$A);
+	 &mov	($Aoff,$A);		# modulo-scheduled
+	 &lea	("esp",&DWP(-4,"esp"));
+	&ror	("ecx",13-2);
+	 &mov	("esi",&DWP(0,$K256));
+	&xor	("ecx",$A);
+	 &mov	($E,$Eoff);		# e in next iteration, d in this one
+	 &xor	($A,"edi");		# a ^= b
+	&ror	("ecx",2);		# Sigma0(a)
+
+	 &add	($T,"esi");		# T+= K[i]
+	 &mov	(&DWP(0,"esp"),$A);	# (b^c) in next round
+	&add	($E,$T);		# d += T
+	 &and	($A,&DWP(4,"esp"));	# a &= (b^c)
+	&add	($T,"ecx");		# T += Sigma0(a)
+	 &xor	($A,"edi");		# h = Maj(a,b,c) = Ch(a^b,c,b)
+	 &mov	("ecx",&DWP(4*(9+15+16-1),"esp"))	if ($in_16_63);	# preload T
+	&add	($K256,4);
+	 &add	($A,$T);		# h += T
+}
+
+&external_label("OPENSSL_ia32cap_P")		if (!$i386);
+
+&function_begin("sha256_block_data_order");
+	&mov	("esi",wparam(0));	# ctx
+	&mov	("edi",wparam(1));	# inp
+	&mov	("eax",wparam(2));	# num
+	&mov	("ebx","esp");		# saved sp
+
+	&call	(&label("pic_point"));	# make it PIC!
+&set_label("pic_point");
+	&blindpop($K256);
+	&lea	($K256,&DWP(&label("K256")."-".&label("pic_point"),$K256));
+
+	&sub	("esp",16);
+	&and	("esp",-64);
+
+	&shl	("eax",6);
+	&add	("eax","edi");
+	&mov	(&DWP(0,"esp"),"esi");	# ctx
+	&mov	(&DWP(4,"esp"),"edi");	# inp
+	&mov	(&DWP(8,"esp"),"eax");	# inp+num*128
+	&mov	(&DWP(12,"esp"),"ebx");	# saved sp
+						if (!$i386) {
+	&picmeup("edx","OPENSSL_ia32cap_P",$K256,&label("K256"));
+	&mov	("ecx",&DWP(0,"edx"));
+	&mov	("ebx",&DWP(4,"edx"));
+	&test	("ecx",1<<20);		# check for P4
+	&jnz	(&label("loop"));
+	&and	("ecx",1<<30);		# mask "Intel CPU" bit
+	&and	("ebx",1<<28|1<<9);	# mask AVX and SSSE3 bits
+	&or	("ecx","ebx");
+	&and	("ecx",1<<28|1<<30);
+	&cmp	("ecx",1<<28|1<<30);
+					if ($xmm) {
+	&je	(&label("AVX"))		if ($avx);
+	&test	("ebx",1<<9);		# check for SSSE3
+	&jnz	(&label("SSSE3"));
+					} else {
+	&je	(&label("loop_shrd"));
+					}
+						if ($unroll_after) {
+	&sub	("eax","edi");
+	&cmp	("eax",$unroll_after);
+	&jae	(&label("unrolled"));
+						} }
+	&jmp	(&label("loop"));
+
+sub COMPACT_LOOP() {
+my $suffix=shift;
+
+&set_label("loop$suffix",$suffix?32:16);
+    # copy input block to stack reversing byte and dword order
+    for($i=0;$i<4;$i++) {
+	&mov	("eax",&DWP($i*16+0,"edi"));
+	&mov	("ebx",&DWP($i*16+4,"edi"));
+	&mov	("ecx",&DWP($i*16+8,"edi"));
+	&bswap	("eax");
+	&mov	("edx",&DWP($i*16+12,"edi"));
+	&bswap	("ebx");
+	&push	("eax");
+	&bswap	("ecx");
+	&push	("ebx");
+	&bswap	("edx");
+	&push	("ecx");
+	&push	("edx");
+    }
+	&add	("edi",64);
+	&lea	("esp",&DWP(-4*9,"esp"));# place for A,B,C,D,E,F,G,H
+	&mov	(&DWP(4*(9+16)+4,"esp"),"edi");
+
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($A,&DWP(0,"esi"));
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	# &mov	($Aoff,$A);
+	&mov	($Boff,"ebx");
+	&xor	("ebx","ecx");
+	&mov	($Coff,"ecx");
+	&mov	($Doff,"edi");
+	&mov	(&DWP(0,"esp"),"ebx");	# magic
+	&mov	($E,&DWP(16,"esi"));	
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("edi",&DWP(28,"esi"));
+	# &mov	($Eoff,$E);
+	&mov	($Foff,"ebx");
+	&mov	($Goff,"ecx");
+	&mov	($Hoff,"edi");
+
+&set_label("00_15$suffix",16);
+
+	&BODY_00_15();
+
+	&cmp	("esi",0xc19bf174);
+	&jne	(&label("00_15$suffix"));
+
+	&mov	("ecx",&DWP(4*(9+15+16-1),"esp"));	# preloaded in BODY_00_15(1)
+	&jmp	(&label("16_63$suffix"));
+
+&set_label("16_63$suffix",16);
+
+	&BODY_16_63();
+
+	&cmp	("esi",0xc67178f2);
+	&jne	(&label("16_63$suffix"));
+
+	&mov	("esi",&DWP(4*(9+16+64)+0,"esp"));#ctx
+	# &mov	($A,$Aoff);
+	&mov	("ebx",$Boff);
+	# &mov	("edi",$Coff);
+	&mov	("ecx",$Doff);
+	&add	($A,&DWP(0,"esi"));
+	&add	("ebx",&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$A);
+	&mov	(&DWP(4,"esi"),"ebx");
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	# &mov	($E,$Eoff);
+	&mov	("eax",$Foff);
+	&mov	("ebx",$Goff);
+	&mov	("ecx",$Hoff);
+	&mov	("edi",&DWP(4*(9+16+64)+4,"esp"));#inp
+	&add	($E,&DWP(16,"esi"));
+	&add	("eax",&DWP(20,"esi"));
+	&add	("ebx",&DWP(24,"esi"));
+	&add	("ecx",&DWP(28,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"eax");
+	&mov	(&DWP(24,"esi"),"ebx");
+	&mov	(&DWP(28,"esi"),"ecx");
+
+	&lea	("esp",&DWP(4*(9+16+64),"esp"));# destroy frame
+	&sub	($K256,4*64);			# rewind K
+
+	&cmp	("edi",&DWP(8,"esp"));		# are we done yet?
+	&jb	(&label("loop$suffix"));
+}
+	&COMPACT_LOOP();
+	&mov	("esp",&DWP(12,"esp"));		# restore sp
+&function_end_A();
+						if (!$i386 && !$xmm) {
+	# ~20% improvement on Sandy Bridge
+	local *ror = sub { &shrd(@_[0],@_) };
+	&COMPACT_LOOP("_shrd");
+	&mov	("esp",&DWP(12,"esp"));		# restore sp
+&function_end_A();
+						}
+
+&set_label("K256",64);	# Yes! I keep it in the code segment!
+@K256=(	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,
+	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
+	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,
+	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,
+	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
+	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,
+	0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,
+	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
+	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2	);
+&data_word(@K256);
+&data_word(0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f);	# byte swap mask
+&asciz("SHA256 block transform for x86, CRYPTOGAMS by <appro\@openssl.org>");
+
+($a,$b,$c,$d,$e,$f,$g,$h)=(0..7);	# offsets
+sub off { &DWP(4*(((shift)-$i)&7),"esp"); }
+
+if (!$i386 && $unroll_after) {
+my @AH=($A,$K256);
+
+&set_label("unrolled",16);
+	&lea	("esp",&DWP(-96,"esp"));
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("ebx",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");		# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"ebx");
+	&mov	($E,&DWP(16,"esi"));	
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"ebx");
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&jmp	(&label("grand_loop"));
+
+&set_label("grand_loop",16);
+    # copy input block to stack reversing byte order
+    for($i=0;$i<5;$i++) {
+	&mov	("ebx",&DWP(12*$i+0,"edi"));
+	&mov	("ecx",&DWP(12*$i+4,"edi"));
+	&bswap	("ebx");
+	&mov	("esi",&DWP(12*$i+8,"edi"));
+	&bswap	("ecx");
+	&mov	(&DWP(32+12*$i+0,"esp"),"ebx");
+	&bswap	("esi");
+	&mov	(&DWP(32+12*$i+4,"esp"),"ecx");
+	&mov	(&DWP(32+12*$i+8,"esp"),"esi");
+    }
+	&mov	("ebx",&DWP($i*12,"edi"));
+	&add	("edi",64);
+	&bswap	("ebx");
+	&mov	(&DWP(96+4,"esp"),"edi");
+	&mov	(&DWP(32+12*$i,"esp"),"ebx");
+
+    my ($t1,$t2) = ("ecx","esi");
+
+    for ($i=0;$i<64;$i++) {
+
+      if ($i>=16) {
+	&mov	($T,$t1);			# $t1 is preloaded
+	# &mov	($t2,&DWP(32+4*(($i+14)&15),"esp"));
+	&ror	($t1,18-7);
+	 &mov	("edi",$t2);
+	&ror	($t2,19-17);
+	 &xor	($t1,$T);
+	 &shr	($T,3);
+	&ror	($t1,7);
+	 &xor	($t2,"edi");
+	 &xor	($T,$t1);			# T = sigma0(X[-15])
+	&ror	($t2,17);
+	 &add	($T,&DWP(32+4*($i&15),"esp"));	# T += X[-16]
+	&shr	("edi",10);
+	 &add	($T,&DWP(32+4*(($i+9)&15),"esp"));	# T += X[-7]
+	#&xor	("edi",$t2)			# sigma1(X[-2])
+	# &add	($T,"edi");			# T += sigma1(X[-2])
+	# &mov	(&DWP(4*(9+15),"esp"),$T);	# save X[0]
+      }
+	&mov	($t1,$E);
+	 &xor	("edi",$t2)			if ($i>=16);	# sigma1(X[-2])
+	 &mov	($t2,&off($f));
+	&ror	($E,25-11);
+	 &add	($T,"edi")			if ($i>=16);	# T += sigma1(X[-2])
+	 &mov	("edi",&off($g));
+	&xor	($E,$t1);
+	 &mov	($T,&DWP(32+4*($i&15),"esp"))	if ($i<16);	# X[i]
+	 &mov	(&DWP(32+4*($i&15),"esp"),$T)	if ($i>=16 && $i<62);	# save X[0]
+	 &xor	($t2,"edi");
+	&ror	($E,11-6);
+	 &and	($t2,$t1);
+	 &mov	(&off($e),$t1);		# save $E, modulo-scheduled
+	&xor	($E,$t1);
+	 &add	($T,&off($h));		# T += h
+	 &xor	("edi",$t2);		# Ch(e,f,g)
+	&ror	($E,6);			# Sigma1(e)
+	 &mov	($t1,$AH[0]);
+	 &add	($T,"edi");		# T += Ch(e,f,g)
+
+	&ror	($t1,22-13);
+	 &mov	($t2,$AH[0]);
+	 &mov	("edi",&off($b));
+	&xor	($t1,$AH[0]);
+	 &mov	(&off($a),$AH[0]);	# save $A, modulo-scheduled
+	 &xor	($AH[0],"edi");		# a ^= b, (b^c) in next round
+	&ror	($t1,13-2);
+	 &and	($AH[1],$AH[0]);	# (b^c) &= (a^b)
+	 &lea	($E,&DWP(@K256[$i],$T,$E));	# T += Sigma1(1)+K[i]
+	&xor	($t1,$t2);
+	 &xor	($AH[1],"edi");		# h = Maj(a,b,c) = Ch(a^b,c,b)
+	 &mov	($t2,&DWP(32+4*(($i+2)&15),"esp"))	if ($i>=15 && $i<63);
+	&ror	($t1,2);		# Sigma0(a)
+
+	 &add	($AH[1],$E);		# h += T
+	 &add	($E,&off($d));		# d += T
+	&add	($AH[1],$t1);		# h += Sigma0(a)
+	 &mov	($t1,&DWP(32+4*(($i+15)&15),"esp"))	if ($i>=15 && $i<63);
+
+	@AH = reverse(@AH);		# rotate(a,h)
+	($t1,$t2) = ($t2,$t1);		# rotate(t1,t2)
+    }
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");		# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ebx",&DWP(24,"esp"));
+	&mov	("ecx",&DWP(28,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ebx",&DWP(24,"esi"));
+	&add	("ecx",&DWP(28,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	&mov	(&DWP(24,"esi"),"ebx");
+	&mov	(&DWP(28,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	 &mov	(&DWP(24,"esp"),"ebx");
+	 &mov	(&DWP(28,"esp"),"ecx");
+
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_loop"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+&function_end_A();
+}
+						if (!$i386 && $xmm) {{{
+my @X = map("xmm$_",(0..3));
+my ($t0,$t1,$t2,$t3) = map("xmm$_",(4..7));
+my @AH = ($A,$T);
+
+&set_label("SSSE3",32);
+	&lea	("esp",&DWP(-96,"esp"));
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");			# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"edi");
+	&mov	($E,&DWP(16,"esi"));
+	&mov	("edi",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&movdqa	($t3,&QWP(256,$K256));
+	&jmp	(&label("grand_ssse3"));
+
+&set_label("grand_ssse3",16);
+	# load input, reverse byte order, add K256[0..15], save to stack
+	&movdqu	(@X[0],&QWP(0,"edi"));
+	&movdqu	(@X[1],&QWP(16,"edi"));
+	&movdqu	(@X[2],&QWP(32,"edi"));
+	&movdqu	(@X[3],&QWP(48,"edi"));
+	&add	("edi",64);
+	&pshufb	(@X[0],$t3);
+	&mov	(&DWP(96+4,"esp"),"edi");
+	&pshufb	(@X[1],$t3);
+	&movdqa	($t0,&QWP(0,$K256));
+	&pshufb	(@X[2],$t3);
+	&movdqa	($t1,&QWP(16,$K256));
+	&paddd	($t0,@X[0]);
+	&pshufb	(@X[3],$t3);
+	&movdqa	($t2,&QWP(32,$K256));
+	&paddd	($t1,@X[1]);
+	&movdqa	($t3,&QWP(48,$K256));
+	&movdqa	(&QWP(32+0,"esp"),$t0);
+	&paddd	($t2,@X[2]);
+	&movdqa	(&QWP(32+16,"esp"),$t1);
+	&paddd	($t3,@X[3]);
+	&movdqa	(&QWP(32+32,"esp"),$t2);
+	&movdqa	(&QWP(32+48,"esp"),$t3);
+	&jmp	(&label("ssse3_00_47"));
+
+&set_label("ssse3_00_47",16);
+	&add		($K256,64);
+
+sub SSSE3_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 120 instructions
+
+	  eval(shift(@insns));
+	&movdqa		($t0,@X[1]);
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	&movdqa		($t3,@X[3]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&palignr	($t0,@X[0],4);		# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &palignr	($t3,@X[2],4);		# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t1,$t0);
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	&movdqa		($t2,$t0);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&psrld		($t0,3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &paddd		(@X[0],$t3);		# X[0..3] += X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&psrld		($t2,7);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[3],0b11111010);	# X[14..15]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pslld		($t1,32-18);
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&psrld		($t2,18-7);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pslld		($t1,18-7);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t1);		# sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrld		($t3,10);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&paddd		(@X[0],$t0);		# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrlq		($t2,17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrlq		($t2,19-17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,$t3,0b10000000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &psrldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		(@X[0],$t3);		# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[0],0b01010000);	# X[16..17]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));			# @
+	 &psrld		($t3,10);
+	  eval(shift(@insns));
+	 &psrlq		($t2,17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrlq		($t2,19-17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,$t3,0b00001000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&movdqa		($t2,&QWP(16*$j,$K256));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pslldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&paddd		(@X[0],$t3);		# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		($t2,@X[0]);
+	  eval(shift(@insns));			# @
+
+	foreach (@insns) { eval; }		# remaining instructions
+
+	&movdqa		(&QWP(32+16*$j,"esp"),$t2);
+}
+
+sub body_00_15 () {
+	(
+	'&mov	("ecx",$E);',
+	'&ror	($E,25-11);',
+	 '&mov	("esi",&off($f));',
+	'&xor	($E,"ecx");',
+	 '&mov	("edi",&off($g));',
+	 '&xor	("esi","edi");',
+	'&ror	($E,11-6);',
+	 '&and	("esi","ecx");',
+	 '&mov	(&off($e),"ecx");',	# save $E, modulo-scheduled
+	'&xor	($E,"ecx");',
+	 '&xor	("edi","esi");',	# Ch(e,f,g)
+	'&ror	($E,6);',		# T = Sigma1(e)
+	 '&mov	("ecx",$AH[0]);',
+	 '&add	($E,"edi");',		# T += Ch(e,f,g)
+	 '&mov	("edi",&off($b));',
+	'&mov	("esi",$AH[0]);',
+
+	'&ror	("ecx",22-13);',
+	 '&mov	(&off($a),$AH[0]);',	# save $A, modulo-scheduled
+	'&xor	("ecx",$AH[0]);',
+	 '&xor	($AH[0],"edi");',	# a ^= b, (b^c) in next round
+	 '&add	($E,&off($h));',	# T += h
+	'&ror	("ecx",13-2);',
+	 '&and	($AH[1],$AH[0]);',	# (b^c) &= (a^b)
+	'&xor	("ecx","esi");',
+	 '&add	($E,&DWP(32+4*($i&15),"esp"));',	# T += K[i]+X[i]
+	 '&xor	($AH[1],"edi");',	# h = Maj(a,b,c) = Ch(a^b,c,b)
+	'&ror	("ecx",2);',		# Sigma0(a)
+
+	 '&add	($AH[1],$E);',		# h += T
+	 '&add	($E,&off($d));',	# d += T
+	'&add	($AH[1],"ecx");'.	# h += Sigma0(a)
+
+	'@AH = reverse(@AH); $i++;'	# rotate(a,h)
+	);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&SSSE3_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));		# rotate(@X)
+    }
+	&cmp	(&DWP(16*$j,$K256),0x00010203);
+	&jne	(&label("ssse3_00_47"));
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");			# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ecx",&DWP(24,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ecx",&DWP(24,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(28,"esp"));
+	&mov	(&DWP(24,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	&add	("edi",&DWP(28,"esi"));
+	 &mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esi"),"edi");
+	 &mov	(&DWP(28,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+
+	&movdqa	($t3,&QWP(64,$K256));
+	&sub	($K256,3*64);			# rewind K
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_ssse3"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+&function_end_A();
+						if ($avx) {
+&set_label("AVX",32);
+						if ($avx>1) {
+	&mov	("edx",&DWP(8,"edx"));
+	&and	("edx",1<<8|1<<3);		# check for BMI2+BMI1
+	&cmp	("edx",1<<8|1<<3);
+	&je	(&label("AVX_BMI"));
+						}
+	&lea	("esp",&DWP(-96,"esp"));
+	&vzeroall	();
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");			# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"edi");
+	&mov	($E,&DWP(16,"esi"));
+	&mov	("edi",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&vmovdqa	($t3,&QWP(256,$K256));
+	&jmp	(&label("grand_avx"));
+
+&set_label("grand_avx",32);
+	# load input, reverse byte order, add K256[0..15], save to stack
+	&vmovdqu	(@X[0],&QWP(0,"edi"));
+	&vmovdqu	(@X[1],&QWP(16,"edi"));
+	&vmovdqu	(@X[2],&QWP(32,"edi"));
+	&vmovdqu	(@X[3],&QWP(48,"edi"));
+	&add		("edi",64);
+	&vpshufb	(@X[0],@X[0],$t3);
+	&mov		(&DWP(96+4,"esp"),"edi");
+	&vpshufb	(@X[1],@X[1],$t3);
+	&vpshufb	(@X[2],@X[2],$t3);
+	&vpaddd		($t0,@X[0],&QWP(0,$K256));
+	&vpshufb	(@X[3],@X[3],$t3);
+	&vpaddd		($t1,@X[1],&QWP(16,$K256));
+	&vpaddd		($t2,@X[2],&QWP(32,$K256));
+	&vpaddd		($t3,@X[3],&QWP(48,$K256));
+	&vmovdqa	(&QWP(32+0,"esp"),$t0);
+	&vmovdqa	(&QWP(32+16,"esp"),$t1);
+	&vmovdqa	(&QWP(32+32,"esp"),$t2);
+	&vmovdqa	(&QWP(32+48,"esp"),$t3);
+	&jmp		(&label("avx_00_47"));
+
+&set_label("avx_00_47",16);
+	&add		($K256,64);
+
+sub Xupdate_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],4);',	# X[1..4]
+	 '&vpalignr	($t3,@X[3],@X[2],4);',	# X[9..12]
+	'&vpsrld	($t2,$t0,7);',
+	 '&vpaddd	(@X[0],@X[0],$t3);',	# X[0..3] += X[9..16]
+	'&vpsrld	($t3,$t0,3);',
+	'&vpslld	($t1,$t0,14);',
+	'&vpxor		($t0,$t3,$t2);',
+	 '&vpshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&vpsrld	($t2,$t2,18-7);',
+	'&vpxor		($t0,$t0,$t1);',
+	'&vpslld	($t1,$t1,25-14);',
+	'&vpxor		($t0,$t0,$t2);',
+	 '&vpsrld	($t2,$t3,10);',
+	'&vpxor		($t0,$t0,$t1);',	# sigma0(X[1..4])
+	 '&vpsrlq	($t1,$t3,17);',
+	'&vpaddd	(@X[0],@X[0],$t0);',	# X[0..3] += sigma0(X[1..4])
+	 '&vpxor	($t2,$t2,$t1);',
+	 '&vpsrlq	($t3,$t3,19);',
+	 '&vpxor	($t2,$t2,$t3);',	# sigma1(X[14..15]
+	 '&vpshufd	($t3,$t2,0b10000100);',
+	'&vpsrldq	($t3,$t3,8);',
+	'&vpaddd	(@X[0],@X[0],$t3);',	# X[0..1] += sigma1(X[14..15])
+	 '&vpshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&vpsrld	($t2,$t3,10);',
+	 '&vpsrlq	($t1,$t3,17);',
+	 '&vpxor	($t2,$t2,$t1);',
+	 '&vpsrlq	($t3,$t3,19);',
+	 '&vpxor	($t2,$t2,$t3);',	# sigma1(X[16..17]
+	 '&vpshufd	($t3,$t2,0b11101000);',
+	'&vpslldq	($t3,$t3,8);',
+	'&vpaddd	(@X[0],@X[0],$t3);'	# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+local *ror = sub { &shrd(@_[0],@_) };
+sub AVX_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 120 instructions
+my $insn;
+
+	foreach (Xupdate_AVX()) {		# 31 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval($insn = shift(@insns));
+	    eval(shift(@insns)) if ($insn =~ /rorx/ && @insns[0] =~ /rorx/);
+	}
+	&vpaddd		($t2,@X[0],&QWP(16*$j,$K256));
+	foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(&QWP(32+16*$j,"esp"),$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));		# rotate(@X)
+    }
+	&cmp	(&DWP(16*$j,$K256),0x00010203);
+	&jne	(&label("avx_00_47"));
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");			# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ecx",&DWP(24,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ecx",&DWP(24,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(28,"esp"));
+	&mov	(&DWP(24,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	&add	("edi",&DWP(28,"esi"));
+	 &mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esi"),"edi");
+	 &mov	(&DWP(28,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+
+	&vmovdqa	($t3,&QWP(64,$K256));
+	&sub	($K256,3*64);			# rewind K
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_avx"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+	&vzeroall	();
+&function_end_A();
+						if ($avx>1) {
+sub bodyx_00_15 () {			# +10%
+	(
+	'&rorx	("ecx",$E,6)',
+	'&rorx	("esi",$E,11)',
+	 '&mov	(&off($e),$E)',		# save $E, modulo-scheduled
+	'&rorx	("edi",$E,25)',
+	'&xor	("ecx","esi")',
+	 '&andn	("esi",$E,&off($g))',
+	'&xor	("ecx","edi")',		# Sigma1(e)
+	 '&and	($E,&off($f))',
+	 '&mov	(&off($a),$AH[0]);',	# save $A, modulo-scheduled
+	 '&or	($E,"esi")',		# T = Ch(e,f,g)
+
+	'&rorx	("edi",$AH[0],2)',
+	'&rorx	("esi",$AH[0],13)',
+	 '&lea	($E,&DWP(0,$E,"ecx"))',	# T += Sigma1(e)
+	'&rorx	("ecx",$AH[0],22)',
+	'&xor	("esi","edi")',
+	 '&mov	("edi",&off($b))',
+	'&xor	("ecx","esi")',		# Sigma0(a)
+
+	 '&xor	($AH[0],"edi")',	# a ^= b, (b^c) in next round
+	 '&add	($E,&off($h))',		# T += h
+	 '&and	($AH[1],$AH[0])',	# (b^c) &= (a^b)
+	 '&add	($E,&DWP(32+4*($i&15),"esp"))',	# T += K[i]+X[i]
+	 '&xor	($AH[1],"edi")',	# h = Maj(a,b,c) = Ch(a^b,c,b)
+
+	 '&add	("ecx",$E)',		# h += T
+	 '&add	($E,&off($d))',		# d += T
+	'&lea	($AH[1],&DWP(0,$AH[1],"ecx"));'.	# h += Sigma0(a)
+
+	'@AH = reverse(@AH); $i++;'	# rotate(a,h)
+	);
+}
+
+&set_label("AVX_BMI",32);
+	&lea	("esp",&DWP(-96,"esp"));
+	&vzeroall	();
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");			# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"edi");
+	&mov	($E,&DWP(16,"esi"));
+	&mov	("edi",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&vmovdqa	($t3,&QWP(256,$K256));
+	&jmp	(&label("grand_avx_bmi"));
+
+&set_label("grand_avx_bmi",32);
+	# load input, reverse byte order, add K256[0..15], save to stack
+	&vmovdqu	(@X[0],&QWP(0,"edi"));
+	&vmovdqu	(@X[1],&QWP(16,"edi"));
+	&vmovdqu	(@X[2],&QWP(32,"edi"));
+	&vmovdqu	(@X[3],&QWP(48,"edi"));
+	&add		("edi",64);
+	&vpshufb	(@X[0],@X[0],$t3);
+	&mov		(&DWP(96+4,"esp"),"edi");
+	&vpshufb	(@X[1],@X[1],$t3);
+	&vpshufb	(@X[2],@X[2],$t3);
+	&vpaddd		($t0,@X[0],&QWP(0,$K256));
+	&vpshufb	(@X[3],@X[3],$t3);
+	&vpaddd		($t1,@X[1],&QWP(16,$K256));
+	&vpaddd		($t2,@X[2],&QWP(32,$K256));
+	&vpaddd		($t3,@X[3],&QWP(48,$K256));
+	&vmovdqa	(&QWP(32+0,"esp"),$t0);
+	&vmovdqa	(&QWP(32+16,"esp"),$t1);
+	&vmovdqa	(&QWP(32+32,"esp"),$t2);
+	&vmovdqa	(&QWP(32+48,"esp"),$t3);
+	&jmp		(&label("avx_bmi_00_47"));
+
+&set_label("avx_bmi_00_47",16);
+	&add		($K256,64);
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));		# rotate(@X)
+    }
+	&cmp	(&DWP(16*$j,$K256),0x00010203);
+	&jne	(&label("avx_bmi_00_47"));
+
+    for ($i=0; $i<16; ) {
+	foreach(bodyx_00_15()) { eval; }
+    }
+
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");			# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ecx",&DWP(24,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ecx",&DWP(24,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(28,"esp"));
+	&mov	(&DWP(24,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	&add	("edi",&DWP(28,"esi"));
+	 &mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esi"),"edi");
+	 &mov	(&DWP(28,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+
+	&vmovdqa	($t3,&QWP(64,$K256));
+	&sub	($K256,3*64);			# rewind K
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_avx_bmi"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+	&vzeroall	();
+&function_end_A();
+						}
+						}
+						}}}
+&function_end_B("sha256_block_data_order");
+
+&asm_finish();
diff --git a/crypto/sha/asm/sha256-armv4.pl b/crypto/sha/asm/sha256-armv4.pl
new file mode 100644
index 0000000..1b2a098
--- /dev/null
+++ b/crypto/sha/asm/sha256-armv4.pl
@@ -0,0 +1,540 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA256 block procedure for ARMv4. May 2007.
+
+# Performance is ~2x better than gcc 3.4 generated code and in "abso-
+# lute" terms is ~2250 cycles per 64-byte block or ~35 cycles per
+# byte [on single-issue Xscale PXA250 core].
+
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 22% improvement on
+# Cortex A8 core and ~20 cycles per processed byte.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 16%
+# improvement on Cortex A8 core and ~15.4 cycles per processed byte.
+
+# September 2013.
+#
+# Add NEON implementation. On Cortex A8 it was measured to process one
+# byte in 12.5 cycles or 23% faster than integer-only code. Snapdragon
+# S4 does it in 12.5 cycles too, but it's 50% faster than integer-only
+# code (meaning that latter performs sub-optimally, nothing was done
+# about it).
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$ctx="r0";	$t0="r0";
+$inp="r1";	$t4="r1";
+$len="r2";	$t1="r2";
+$T1="r3";	$t3="r3";
+$A="r4";
+$B="r5";
+$C="r6";
+$D="r7";
+$E="r8";
+$F="r9";
+$G="r10";
+$H="r11";
+@V=($A,$B,$C,$D,$E,$F,$G,$H);
+$t2="r12";
+$Ktbl="r14";
+
+@Sigma0=( 2,13,22);
+@Sigma1=( 6,11,25);
+@sigma0=( 7,18, 3);
+@sigma1=(17,19,10);
+
+sub BODY_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___ if ($i<16);
+#if __ARM_ARCH__>=7
+	@ ldr	$t1,[$inp],#4			@ $i
+# if $i==15
+	str	$inp,[sp,#17*4]			@ make room for $t4
+# endif
+	eor	$t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`
+	add	$a,$a,$t2			@ h+=Maj(a,b,c) from the past
+	eor	$t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]`	@ Sigma1(e)
+	rev	$t1,$t1
+#else
+	@ ldrb	$t1,[$inp,#3]			@ $i
+	add	$a,$a,$t2			@ h+=Maj(a,b,c) from the past
+	ldrb	$t2,[$inp,#2]
+	ldrb	$t0,[$inp,#1]
+	orr	$t1,$t1,$t2,lsl#8
+	ldrb	$t2,[$inp],#4
+	orr	$t1,$t1,$t0,lsl#16
+# if $i==15
+	str	$inp,[sp,#17*4]			@ make room for $t4
+# endif
+	eor	$t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`
+	orr	$t1,$t1,$t2,lsl#24
+	eor	$t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]`	@ Sigma1(e)
+#endif
+___
+$code.=<<___;
+	ldr	$t2,[$Ktbl],#4			@ *K256++
+	add	$h,$h,$t1			@ h+=X[i]
+	str	$t1,[sp,#`$i%16`*4]
+	eor	$t1,$f,$g
+	add	$h,$h,$t0,ror#$Sigma1[0]	@ h+=Sigma1(e)
+	and	$t1,$t1,$e
+	add	$h,$h,$t2			@ h+=K256[i]
+	eor	$t1,$t1,$g			@ Ch(e,f,g)
+	eor	$t0,$a,$a,ror#`$Sigma0[1]-$Sigma0[0]`
+	add	$h,$h,$t1			@ h+=Ch(e,f,g)
+#if $i==31
+	and	$t2,$t2,#0xff
+	cmp	$t2,#0xf2			@ done?
+#endif
+#if $i<15
+# if __ARM_ARCH__>=7
+	ldr	$t1,[$inp],#4			@ prefetch
+# else
+	ldrb	$t1,[$inp,#3]
+# endif
+	eor	$t2,$a,$b			@ a^b, b^c in next round
+#else
+	ldr	$t1,[sp,#`($i+2)%16`*4]		@ from future BODY_16_xx
+	eor	$t2,$a,$b			@ a^b, b^c in next round
+	ldr	$t4,[sp,#`($i+15)%16`*4]	@ from future BODY_16_xx
+#endif
+	eor	$t0,$t0,$a,ror#`$Sigma0[2]-$Sigma0[0]`	@ Sigma0(a)
+	and	$t3,$t3,$t2			@ (b^c)&=(a^b)
+	add	$d,$d,$h			@ d+=h
+	eor	$t3,$t3,$b			@ Maj(a,b,c)
+	add	$h,$h,$t0,ror#$Sigma0[0]	@ h+=Sigma0(a)
+	@ add	$h,$h,$t3			@ h+=Maj(a,b,c)
+___
+	($t2,$t3)=($t3,$t2);
+}
+
+sub BODY_16_XX {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___;
+	@ ldr	$t1,[sp,#`($i+1)%16`*4]		@ $i
+	@ ldr	$t4,[sp,#`($i+14)%16`*4]
+	mov	$t0,$t1,ror#$sigma0[0]
+	add	$a,$a,$t2			@ h+=Maj(a,b,c) from the past
+	mov	$t2,$t4,ror#$sigma1[0]
+	eor	$t0,$t0,$t1,ror#$sigma0[1]
+	eor	$t2,$t2,$t4,ror#$sigma1[1]
+	eor	$t0,$t0,$t1,lsr#$sigma0[2]	@ sigma0(X[i+1])
+	ldr	$t1,[sp,#`($i+0)%16`*4]
+	eor	$t2,$t2,$t4,lsr#$sigma1[2]	@ sigma1(X[i+14])
+	ldr	$t4,[sp,#`($i+9)%16`*4]
+
+	add	$t2,$t2,$t0
+	eor	$t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`	@ from BODY_00_15
+	add	$t1,$t1,$t2
+	eor	$t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]`	@ Sigma1(e)
+	add	$t1,$t1,$t4			@ X[i]
+___
+	&BODY_00_15(@_);
+}
+
+$code=<<___;
+#if defined(__arm__)
+#include "arm_arch.h"
+
+.text
+.code	32
+
+.type	K256,%object
+.align	5
+K256:
+.word	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+.word	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+.word	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+.word	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+.word	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+.word	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+.word	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+.word	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+.word	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+.word	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+.word	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+.word	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+.word	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+.word	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+.word	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+.word	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+.size	K256,.-K256
+.word	0				@ terminator
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-sha256_block_data_order
+.align	5
+
+.global	sha256_block_data_order
+.type	sha256_block_data_order,%function
+sha256_block_data_order:
+	sub	r3,pc,#8		@ sha256_block_data_order
+	add	$len,$inp,$len,lsl#6	@ len to point at the end of inp
+#if __ARM_ARCH__>=7
+	ldr	r12,.LOPENSSL_armcap
+	ldr	r12,[r3,r12]		@ OPENSSL_armcap_P
+	tst	r12,#1
+	bne	.LNEON
+#endif
+	stmdb	sp!,{$ctx,$inp,$len,r4-r11,lr}
+	ldmia	$ctx,{$A,$B,$C,$D,$E,$F,$G,$H}
+	sub	$Ktbl,r3,#256+32	@ K256
+	sub	sp,sp,#16*4		@ alloca(X[16])
+.Loop:
+# if __ARM_ARCH__>=7
+	ldr	$t1,[$inp],#4
+# else
+	ldrb	$t1,[$inp,#3]
+# endif
+	eor	$t3,$B,$C		@ magic
+	eor	$t2,$t2,$t2
+___
+for($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=".Lrounds_16_xx:\n";
+for (;$i<32;$i++)	{ &BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	ldreq	$t3,[sp,#16*4]		@ pull ctx
+	bne	.Lrounds_16_xx
+
+	add	$A,$A,$t2		@ h+=Maj(a,b,c) from the past
+	ldr	$t0,[$t3,#0]
+	ldr	$t1,[$t3,#4]
+	ldr	$t2,[$t3,#8]
+	add	$A,$A,$t0
+	ldr	$t0,[$t3,#12]
+	add	$B,$B,$t1
+	ldr	$t1,[$t3,#16]
+	add	$C,$C,$t2
+	ldr	$t2,[$t3,#20]
+	add	$D,$D,$t0
+	ldr	$t0,[$t3,#24]
+	add	$E,$E,$t1
+	ldr	$t1,[$t3,#28]
+	add	$F,$F,$t2
+	ldr	$inp,[sp,#17*4]		@ pull inp
+	ldr	$t2,[sp,#18*4]		@ pull inp+len
+	add	$G,$G,$t0
+	add	$H,$H,$t1
+	stmia	$t3,{$A,$B,$C,$D,$E,$F,$G,$H}
+	cmp	$inp,$t2
+	sub	$Ktbl,$Ktbl,#256	@ rewind Ktbl
+	bne	.Loop
+
+	add	sp,sp,#`16+3`*4	@ destroy frame
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r11,pc}
+#else
+	ldmia	sp!,{r4-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+___
+######################################################################
+# NEON stuff
+#
+{{{
+my @X=map("q$_",(0..3));
+my ($T0,$T1,$T2,$T3,$T4,$T5)=("q8","q9","q10","q11","d24","d25");
+my $Xfer=$t4;
+my $j=0;
+
+sub Dlo()   { shift=~m|q([1]?[0-9])|?"d".($1*2):"";     }
+sub Dhi()   { shift=~m|q([1]?[0-9])|?"d".($1*2+1):"";   }
+
+sub AUTOLOAD()          # thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
+  my $arg = pop;
+    $arg = "#$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',@_,$arg)."\n";
+}
+
+sub Xupdate()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+	&vext_8		($T0,@X[0],@X[1],4);	# X[1..4]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vext_8		($T1,@X[2],@X[3],4);	# X[9..12]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	($T2,$T0,$sigma0[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(@X[0],@X[0],$T1);	# X[0..3] += X[9..12]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	($T1,$T0,$sigma0[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vsli_32	($T2,$T0,32-$sigma0[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	($T3,$T0,$sigma0[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		($T1,$T1,$T2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vsli_32	($T3,$T0,32-$sigma0[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dhi(@X[3]),$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		($T1,$T1,$T3);		# sigma0(X[1..4])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dhi(@X[3]),32-$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T5,&Dhi(@X[3]),$sigma1[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(@X[0],@X[0],$T1);	# X[0..3] += sigma0(X[1..4])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dhi(@X[3]),$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dhi(@X[3]),32-$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);		# sigma1(X[14..15])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(&Dlo(@X[0]),&Dlo(@X[0]),$T5);# X[0..1] += sigma1(X[14..15])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dlo(@X[0]),$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dlo(@X[0]),32-$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T5,&Dlo(@X[0]),$sigma1[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dlo(@X[0]),$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vld1_32	("{$T0}","[$Ktbl,:128]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dlo(@X[0]),32-$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);		# sigma1(X[16..17])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(&Dhi(@X[0]),&Dhi(@X[0]),$T5);# X[2..3] += sigma1(X[16..17])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	($T0,$T0,@X[0]);
+	 while($#insns>=2) { eval(shift(@insns)); }
+	&vst1_32	("{$T0}","[$Xfer,:128]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	push(@X,shift(@X));		# "rotate" X[]
+}
+
+sub Xpreload()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vld1_32	("{$T0}","[$Ktbl,:128]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vrev32_8	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	($T0,$T0,@X[0]);
+	 foreach (@insns) { eval; }	# remaining instructions
+	&vst1_32	("{$T0}","[$Xfer,:128]!");
+
+	push(@X,shift(@X));		# "rotate" X[]
+}
+
+sub body_00_15 () {
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@V;'.
+	'&add	($h,$h,$t1)',			# h+=X[i]+K[i]
+	'&eor	($t1,$f,$g)',
+	'&eor	($t0,$e,$e,"ror#".($Sigma1[1]-$Sigma1[0]))',
+	'&add	($a,$a,$t2)',			# h+=Maj(a,b,c) from the past
+	'&and	($t1,$t1,$e)',
+	'&eor	($t2,$t0,$e,"ror#".($Sigma1[2]-$Sigma1[0]))',	# Sigma1(e)
+	'&eor	($t0,$a,$a,"ror#".($Sigma0[1]-$Sigma0[0]))',
+	'&eor	($t1,$t1,$g)',			# Ch(e,f,g)
+	'&add	($h,$h,$t2,"ror#$Sigma1[0]")',	# h+=Sigma1(e)
+	'&eor	($t2,$a,$b)',			# a^b, b^c in next round
+	'&eor	($t0,$t0,$a,"ror#".($Sigma0[2]-$Sigma0[0]))',	# Sigma0(a)
+	'&add	($h,$h,$t1)',			# h+=Ch(e,f,g)
+	'&ldr	($t1,sprintf "[sp,#%d]",4*(($j+1)&15))	if (($j&15)!=15);'.
+	'&ldr	($t1,"[$Ktbl]")				if ($j==15);'.
+	'&ldr	($t1,"[sp,#64]")			if ($j==31)',
+	'&and	($t3,$t3,$t2)',			# (b^c)&=(a^b)
+	'&add	($d,$d,$h)',			# d+=h
+	'&add	($h,$h,$t0,"ror#$Sigma0[0]");'.	# h+=Sigma0(a)
+	'&eor	($t3,$t3,$b)',			# Maj(a,b,c)
+	'$j++;	unshift(@V,pop(@V)); ($t2,$t3)=($t3,$t2);'
+	)
+}
+
+$code.=<<___;
+#if __ARM_ARCH__>=7
+.fpu	neon
+.align	4
+.LNEON:
+	stmdb	sp!,{r4-r12,lr}
+
+	mov	$t2,sp
+	sub	sp,sp,#16*4+16		@ alloca
+	sub	$Ktbl,r3,#256+32	@ K256
+	bic	sp,sp,#15		@ align for 128-bit stores
+
+	vld1.8		{@X[0]},[$inp]!
+	vld1.8		{@X[1]},[$inp]!
+	vld1.8		{@X[2]},[$inp]!
+	vld1.8		{@X[3]},[$inp]!
+	vld1.32		{$T0},[$Ktbl,:128]!
+	vld1.32		{$T1},[$Ktbl,:128]!
+	vld1.32		{$T2},[$Ktbl,:128]!
+	vld1.32		{$T3},[$Ktbl,:128]!
+	vrev32.8	@X[0],@X[0]		@ yes, even on
+	str		$ctx,[sp,#64]
+	vrev32.8	@X[1],@X[1]		@ big-endian
+	str		$inp,[sp,#68]
+	mov		$Xfer,sp
+	vrev32.8	@X[2],@X[2]
+	str		$len,[sp,#72]
+	vrev32.8	@X[3],@X[3]
+	str		$t2,[sp,#76]		@ save original sp
+	vadd.i32	$T0,$T0,@X[0]
+	vadd.i32	$T1,$T1,@X[1]
+	vst1.32		{$T0},[$Xfer,:128]!
+	vadd.i32	$T2,$T2,@X[2]
+	vst1.32		{$T1},[$Xfer,:128]!
+	vadd.i32	$T3,$T3,@X[3]
+	vst1.32		{$T2},[$Xfer,:128]!
+	vst1.32		{$T3},[$Xfer,:128]!
+
+	ldmia		$ctx,{$A-$H}
+	sub		$Xfer,$Xfer,#64
+	ldr		$t1,[sp,#0]
+	eor		$t2,$t2,$t2
+	eor		$t3,$B,$C
+	b		.L_00_48
+
+.align	4
+.L_00_48:
+___
+	&Xupdate(\&body_00_15);
+	&Xupdate(\&body_00_15);
+	&Xupdate(\&body_00_15);
+	&Xupdate(\&body_00_15);
+$code.=<<___;
+	teq	$t1,#0				@ check for K256 terminator
+	ldr	$t1,[sp,#0]
+	sub	$Xfer,$Xfer,#64
+	bne	.L_00_48
+
+	ldr		$inp,[sp,#68]
+	ldr		$t0,[sp,#72]
+	sub		$Ktbl,$Ktbl,#256	@ rewind $Ktbl
+	teq		$inp,$t0
+	subeq		$inp,$inp,#64		@ avoid SEGV
+	vld1.8		{@X[0]},[$inp]!		@ load next input block
+	vld1.8		{@X[1]},[$inp]!
+	vld1.8		{@X[2]},[$inp]!
+	vld1.8		{@X[3]},[$inp]!
+	strne		$inp,[sp,#68]
+	mov		$Xfer,sp
+___
+	&Xpreload(\&body_00_15);
+	&Xpreload(\&body_00_15);
+	&Xpreload(\&body_00_15);
+	&Xpreload(\&body_00_15);
+$code.=<<___;
+	ldr	$t0,[$t1,#0]
+	add	$A,$A,$t2			@ h+=Maj(a,b,c) from the past
+	ldr	$t2,[$t1,#4]
+	ldr	$t3,[$t1,#8]
+	ldr	$t4,[$t1,#12]
+	add	$A,$A,$t0			@ accumulate
+	ldr	$t0,[$t1,#16]
+	add	$B,$B,$t2
+	ldr	$t2,[$t1,#20]
+	add	$C,$C,$t3
+	ldr	$t3,[$t1,#24]
+	add	$D,$D,$t4
+	ldr	$t4,[$t1,#28]
+	add	$E,$E,$t0
+	str	$A,[$t1],#4
+	add	$F,$F,$t2
+	str	$B,[$t1],#4
+	add	$G,$G,$t3
+	str	$C,[$t1],#4
+	add	$H,$H,$t4
+	str	$D,[$t1],#4
+	stmia	$t1,{$E-$H}
+
+	movne	$Xfer,sp
+	ldrne	$t1,[sp,#0]
+	eorne	$t2,$t2,$t2
+	ldreq	sp,[sp,#76]			@ restore original sp
+	eorne	$t3,$B,$C
+	bne	.L_00_48
+
+	ldmia	sp!,{r4-r12,pc}
+#endif
+___
+}}}
+$code.=<<___;
+.size   sha256_block_data_order,.-sha256_block_data_order
+.asciz  "SHA256 block transform for ARMv4/NEON, CRYPTOGAMS by <appro\@openssl.org>"
+.align	2
+.comm   OPENSSL_armcap_P,4,4
+
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+print $code;
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha512-586.pl b/crypto/sha/asm/sha512-586.pl
new file mode 100644
index 0000000..9fc7929
--- /dev/null
+++ b/crypto/sha/asm/sha512-586.pl
@@ -0,0 +1,910 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA512 block transform for x86. September 2007.
+#
+# May 2013.
+#
+# Add SSSE3 code path, 20-25% improvement [over original SSE2 code].
+#
+# Performance in clock cycles per processed byte (less is better):
+#
+#		gcc	icc	x86 asm	SIMD(*)	x86_64(**)
+# Pentium	100	97	61	-	-
+# PIII		75	77	56	-	-
+# P4		116	95	82	34.6	30.8
+# AMD K8	54	55	36	20.7	9.57
+# Core2		66	57	40	15.9	9.97
+# Westmere	70	-	38	12.2	9.58
+# Sandy Bridge	58	-	35	11.9	11.2
+# Ivy Bridge	50	-	33	11.5	8.17
+# Haswell	46	-	29	11.3	7.66
+# Bulldozer	121	-	50	14.0	13.5
+# VIA Nano	91	-	52	33	14.7
+# Atom		126	-	68	48(***)	14.7
+#
+# (*)	whichever best applicable.
+# (**)	x86_64 assembler performance is presented for reference
+#	purposes, the results are for integer-only code.
+# (***)	paddq is increadibly slow on Atom.
+#
+# IALU code-path is optimized for elder Pentiums. On vanilla Pentium
+# performance improvement over compiler generated code reaches ~60%,
+# while on PIII - ~35%. On newer µ-archs improvement varies from 15%
+# to 50%, but it's less important as they are expected to execute SSE2
+# code-path, which is commonly ~2-3x faster [than compiler generated
+# code]. SSE2 code-path is as fast as original sha512-sse2.pl, even
+# though it does not use 128-bit operations. The latter means that
+# SSE2-aware kernel is no longer required to execute the code. Another
+# difference is that new code optimizes amount of writes, but at the
+# cost of increased data cache "footprint" by 1/2KB.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+&asm_init($ARGV[0],"sha512-586.pl",$ARGV[$#ARGV] eq "386");
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+$Tlo=&DWP(0,"esp");	$Thi=&DWP(4,"esp");
+$Alo=&DWP(8,"esp");	$Ahi=&DWP(8+4,"esp");
+$Blo=&DWP(16,"esp");	$Bhi=&DWP(16+4,"esp");
+$Clo=&DWP(24,"esp");	$Chi=&DWP(24+4,"esp");
+$Dlo=&DWP(32,"esp");	$Dhi=&DWP(32+4,"esp");
+$Elo=&DWP(40,"esp");	$Ehi=&DWP(40+4,"esp");
+$Flo=&DWP(48,"esp");	$Fhi=&DWP(48+4,"esp");
+$Glo=&DWP(56,"esp");	$Ghi=&DWP(56+4,"esp");
+$Hlo=&DWP(64,"esp");	$Hhi=&DWP(64+4,"esp");
+$K512="ebp";
+
+$Asse2=&QWP(0,"esp");
+$Bsse2=&QWP(8,"esp");
+$Csse2=&QWP(16,"esp");
+$Dsse2=&QWP(24,"esp");
+$Esse2=&QWP(32,"esp");
+$Fsse2=&QWP(40,"esp");
+$Gsse2=&QWP(48,"esp");
+$Hsse2=&QWP(56,"esp");
+
+$A="mm0";	# B-D and
+$E="mm4";	# F-H are commonly loaded to respectively mm1-mm3 and
+		# mm5-mm7, but it's done on on-demand basis...
+$BxC="mm2";	# ... except for B^C
+
+sub BODY_00_15_sse2 {
+    my $phase=shift;
+
+	#&movq	("mm5",$Fsse2);			# load f
+	#&movq	("mm6",$Gsse2);			# load g
+
+	&movq	("mm1",$E);			# %mm1 is sliding right
+	 &pxor	("mm5","mm6");			# f^=g
+	&psrlq	("mm1",14);
+	 &movq	($Esse2,$E);			# modulo-scheduled save e
+	 &pand	("mm5",$E);			# f&=e
+	&psllq	($E,23);			# $E is sliding left
+	 &movq	($A,"mm3")			if ($phase<2);
+	 &movq	(&QWP(8*9,"esp"),"mm7")		# save X[i]
+	&movq	("mm3","mm1");			# %mm3 is T1
+	 &psrlq	("mm1",4);
+	 &pxor	("mm5","mm6");			# Ch(e,f,g)
+	&pxor	("mm3",$E);
+	 &psllq	($E,23);
+	&pxor	("mm3","mm1");
+	 &movq	($Asse2,$A);			# modulo-scheduled save a
+	 &paddq	("mm7","mm5");			# X[i]+=Ch(e,f,g)
+	&pxor	("mm3",$E);
+	 &psrlq	("mm1",23);
+	 &paddq	("mm7",$Hsse2);			# X[i]+=h
+	&pxor	("mm3","mm1");
+	 &psllq	($E,4);
+	 &paddq	("mm7",QWP(0,$K512));		# X[i]+=K512[i]
+	&pxor	("mm3",$E);			# T1=Sigma1_512(e)
+
+	 &movq	($E,$Dsse2);			# e = load d, e in next round
+	&paddq	("mm3","mm7");			# T1+=X[i]
+	 &movq	("mm5",$A);			# %mm5 is sliding right
+	 &psrlq	("mm5",28);
+	&paddq	($E,"mm3");			# d += T1
+	 &movq	("mm6",$A);			# %mm6 is sliding left
+	 &movq	("mm7","mm5");
+	 &psllq	("mm6",25);
+	&movq	("mm1",$Bsse2);			# load b
+	 &psrlq	("mm5",6);
+	 &pxor	("mm7","mm6");
+	&sub	("esp",8);
+	 &psllq	("mm6",5);
+	 &pxor	("mm7","mm5");
+	&pxor	($A,"mm1");			# a^b, b^c in next round
+	 &psrlq	("mm5",5);
+	 &pxor	("mm7","mm6");
+	&pand	($BxC,$A);			# (b^c)&(a^b)
+	 &psllq	("mm6",6);
+	 &pxor	("mm7","mm5");
+	&pxor	($BxC,"mm1");			# [h=]Maj(a,b,c)
+	 &pxor	("mm6","mm7");			# Sigma0_512(a)
+	 &movq	("mm7",&QWP(8*(9+16-1),"esp"))	if ($phase!=0);	# pre-fetch
+	 &movq	("mm5",$Fsse2)			if ($phase==0);	# load f
+
+    if ($phase>1) {
+	&paddq	($BxC,"mm6");			# h+=Sigma0(a)
+	 &add	($K512,8);
+	#&paddq	($BxC,"mm3");			# h+=T1
+
+	($A,$BxC) = ($BxC,$A);			# rotate registers
+    } else {
+	&paddq	("mm3",$BxC);			# T1+=Maj(a,b,c)
+	 &movq	($BxC,$A);
+	 &add	($K512,8);
+	&paddq	("mm3","mm6");			# T1+=Sigma0(a)
+	 &movq	("mm6",$Gsse2)			if ($phase==0);	# load g
+	#&movq	($A,"mm3");			# h=T1
+    }
+}
+
+sub BODY_00_15_x86 {
+	#define Sigma1(x)	(ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+	#	LO		lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+	#	HI		hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+	&mov	("ecx",$Elo);
+	&mov	("edx",$Ehi);
+	&mov	("esi","ecx");
+
+	&shr	("ecx",9);	# lo>>9
+	&mov	("edi","edx");
+	&shr	("edx",9);	# hi>>9
+	&mov	("ebx","ecx");
+	&shl	("esi",14);	# lo<<14
+	&mov	("eax","edx");
+	&shl	("edi",14);	# hi<<14
+	&xor	("ebx","esi");
+
+	&shr	("ecx",14-9);	# lo>>14
+	&xor	("eax","edi");
+	&shr	("edx",14-9);	# hi>>14
+	&xor	("eax","ecx");
+	&shl	("esi",18-14);	# lo<<18
+	&xor	("ebx","edx");
+	&shl	("edi",18-14);	# hi<<18
+	&xor	("ebx","esi");
+
+	&shr	("ecx",18-14);	# lo>>18
+	&xor	("eax","edi");
+	&shr	("edx",18-14);	# hi>>18
+	&xor	("eax","ecx");
+	&shl	("esi",23-18);	# lo<<23
+	&xor	("ebx","edx");
+	&shl	("edi",23-18);	# hi<<23
+	&xor	("eax","esi");
+	&xor	("ebx","edi");			# T1 = Sigma1(e)
+
+	&mov	("ecx",$Flo);
+	&mov	("edx",$Fhi);
+	&mov	("esi",$Glo);
+	&mov	("edi",$Ghi);
+	 &add	("eax",$Hlo);
+	 &adc	("ebx",$Hhi);			# T1 += h
+	&xor	("ecx","esi");
+	&xor	("edx","edi");
+	&and	("ecx",$Elo);
+	&and	("edx",$Ehi);
+	 &add	("eax",&DWP(8*(9+15)+0,"esp"));
+	 &adc	("ebx",&DWP(8*(9+15)+4,"esp"));	# T1 += X[0]
+	&xor	("ecx","esi");
+	&xor	("edx","edi");			# Ch(e,f,g) = (f^g)&e)^g
+
+	&mov	("esi",&DWP(0,$K512));
+	&mov	("edi",&DWP(4,$K512));		# K[i]
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# T1 += Ch(e,f,g)
+	&mov	("ecx",$Dlo);
+	&mov	("edx",$Dhi);
+	&add	("eax","esi");
+	&adc	("ebx","edi");			# T1 += K[i]
+	&mov	($Tlo,"eax");
+	&mov	($Thi,"ebx");			# put T1 away
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# d += T1
+
+	#define Sigma0(x)	(ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+	#	LO		lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+	#	HI		hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+	&mov	("ecx",$Alo);
+	&mov	("edx",$Ahi);
+	&mov	($Dlo,"eax");
+	&mov	($Dhi,"ebx");
+	&mov	("esi","ecx");
+
+	&shr	("ecx",2);	# lo>>2
+	&mov	("edi","edx");
+	&shr	("edx",2);	# hi>>2
+	&mov	("ebx","ecx");
+	&shl	("esi",4);	# lo<<4
+	&mov	("eax","edx");
+	&shl	("edi",4);	# hi<<4
+	&xor	("ebx","esi");
+
+	&shr	("ecx",7-2);	# lo>>7
+	&xor	("eax","edi");
+	&shr	("edx",7-2);	# hi>>7
+	&xor	("ebx","ecx");
+	&shl	("esi",25-4);	# lo<<25
+	&xor	("eax","edx");
+	&shl	("edi",25-4);	# hi<<25
+	&xor	("eax","esi");
+
+	&shr	("ecx",28-7);	# lo>>28
+	&xor	("ebx","edi");
+	&shr	("edx",28-7);	# hi>>28
+	&xor	("eax","ecx");
+	&shl	("esi",30-25);	# lo<<30
+	&xor	("ebx","edx");
+	&shl	("edi",30-25);	# hi<<30
+	&xor	("eax","esi");
+	&xor	("ebx","edi");			# Sigma0(a)
+
+	&mov	("ecx",$Alo);
+	&mov	("edx",$Ahi);
+	&mov	("esi",$Blo);
+	&mov	("edi",$Bhi);
+	&add	("eax",$Tlo);
+	&adc	("ebx",$Thi);			# T1 = Sigma0(a)+T1
+	&or	("ecx","esi");
+	&or	("edx","edi");
+	&and	("ecx",$Clo);
+	&and	("edx",$Chi);
+	&and	("esi",$Alo);
+	&and	("edi",$Ahi);
+	&or	("ecx","esi");
+	&or	("edx","edi");			# Maj(a,b,c) = ((a|b)&c)|(a&b)
+
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# T1 += Maj(a,b,c)
+	&mov	($Tlo,"eax");
+	&mov	($Thi,"ebx");
+
+	&mov	(&LB("edx"),&BP(0,$K512));	# pre-fetch LSB of *K
+	&sub	("esp",8);
+	&lea	($K512,&DWP(8,$K512));		# K++
+}
+
+
+&function_begin("sha512_block_data_order");
+	&mov	("esi",wparam(0));	# ctx
+	&mov	("edi",wparam(1));	# inp
+	&mov	("eax",wparam(2));	# num
+	&mov	("ebx","esp");		# saved sp
+
+	&call	(&label("pic_point"));	# make it PIC!
+&set_label("pic_point");
+	&blindpop($K512);
+	&lea	($K512,&DWP(&label("K512")."-".&label("pic_point"),$K512));
+
+	&sub	("esp",16);
+	&and	("esp",-64);
+
+	&shl	("eax",7);
+	&add	("eax","edi");
+	&mov	(&DWP(0,"esp"),"esi");	# ctx
+	&mov	(&DWP(4,"esp"),"edi");	# inp
+	&mov	(&DWP(8,"esp"),"eax");	# inp+num*128
+	&mov	(&DWP(12,"esp"),"ebx");	# saved sp
+
+if ($sse2) {
+	&picmeup("edx","OPENSSL_ia32cap_P",$K512,&label("K512"));
+	&mov	("ecx",&DWP(0,"edx"));
+	&test	("ecx",1<<26);
+	&jz	(&label("loop_x86"));
+
+	&mov	("edx",&DWP(4,"edx"));
+
+	# load ctx->h[0-7]
+	&movq	($A,&QWP(0,"esi"));
+	 &and	("ecx",1<<24);		# XMM registers availability
+	&movq	("mm1",&QWP(8,"esi"));
+	 &and	("edx",1<<9);		# SSSE3 bit
+	&movq	($BxC,&QWP(16,"esi"));
+	 &or	("ecx","edx");
+	&movq	("mm3",&QWP(24,"esi"));
+	&movq	($E,&QWP(32,"esi"));
+	&movq	("mm5",&QWP(40,"esi"));
+	&movq	("mm6",&QWP(48,"esi"));
+	&movq	("mm7",&QWP(56,"esi"));
+	&cmp	("ecx",1<<24|1<<9);
+	&je	(&label("SSSE3"));
+	&sub	("esp",8*10);
+	&jmp	(&label("loop_sse2"));
+
+&set_label("loop_sse2",16);
+	#&movq	($Asse2,$A);
+	&movq	($Bsse2,"mm1");
+	&movq	($Csse2,$BxC);
+	&movq	($Dsse2,"mm3");
+	#&movq	($Esse2,$E);
+	&movq	($Fsse2,"mm5");
+	&movq	($Gsse2,"mm6");
+	&pxor	($BxC,"mm1");			# magic
+	&movq	($Hsse2,"mm7");
+	&movq	("mm3",$A);			# magic
+
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ebx",&DWP(4,"edi"));
+	&add	("edi",8);
+	&mov	("edx",15);			# counter
+	&bswap	("eax");
+	&bswap	("ebx");
+	&jmp	(&label("00_14_sse2"));
+
+&set_label("00_14_sse2",16);
+	&movd	("mm1","eax");
+	&mov	("eax",&DWP(0,"edi"));
+	&movd	("mm7","ebx");
+	&mov	("ebx",&DWP(4,"edi"));
+	&add	("edi",8);
+	&bswap	("eax");
+	&bswap	("ebx");
+	&punpckldq("mm7","mm1");
+
+	&BODY_00_15_sse2();
+
+	&dec	("edx");
+	&jnz	(&label("00_14_sse2"));
+
+	&movd	("mm1","eax");
+	&movd	("mm7","ebx");
+	&punpckldq("mm7","mm1");
+
+	&BODY_00_15_sse2(1);
+
+	&pxor	($A,$A);			# A is in %mm3
+	&mov	("edx",32);			# counter
+	&jmp	(&label("16_79_sse2"));
+
+&set_label("16_79_sse2",16);
+    for ($j=0;$j<2;$j++) {			# 2x unroll
+	#&movq	("mm7",&QWP(8*(9+16-1),"esp"));	# prefetched in BODY_00_15 
+	&movq	("mm5",&QWP(8*(9+16-14),"esp"));
+	&movq	("mm1","mm7");
+	&psrlq	("mm7",1);
+	 &movq	("mm6","mm5");
+	 &psrlq	("mm5",6);
+	&psllq	("mm1",56);
+	 &paddq	($A,"mm3");			# from BODY_00_15
+	 &movq	("mm3","mm7");
+	&psrlq	("mm7",7-1);
+	 &pxor	("mm3","mm1");
+	 &psllq	("mm1",63-56);
+	&pxor	("mm3","mm7");
+	 &psrlq	("mm7",8-7);
+	&pxor	("mm3","mm1");
+	 &movq	("mm1","mm5");
+	 &psrlq	("mm5",19-6);
+	&pxor	("mm7","mm3");			# sigma0
+
+	 &psllq	("mm6",3);
+	 &pxor	("mm1","mm5");
+	&paddq	("mm7",&QWP(8*(9+16),"esp"));
+	 &pxor	("mm1","mm6");
+	 &psrlq	("mm5",61-19);
+	&paddq	("mm7",&QWP(8*(9+16-9),"esp"));
+	 &pxor	("mm1","mm5");
+	 &psllq	("mm6",45-3);
+	&movq	("mm5",$Fsse2);			# load f
+	 &pxor	("mm1","mm6");			# sigma1
+	&movq	("mm6",$Gsse2);			# load g
+
+	&paddq	("mm7","mm1");			# X[i]
+	#&movq	(&QWP(8*9,"esp"),"mm7");	# moved to BODY_00_15
+
+	&BODY_00_15_sse2(2);
+    }
+	&dec	("edx");
+	&jnz	(&label("16_79_sse2"));
+
+	#&movq	($A,$Asse2);
+	&paddq	($A,"mm3");			# from BODY_00_15
+	&movq	("mm1",$Bsse2);
+	#&movq	($BxC,$Csse2);
+	&movq	("mm3",$Dsse2);
+	#&movq	($E,$Esse2);
+	&movq	("mm5",$Fsse2);
+	&movq	("mm6",$Gsse2);
+	&movq	("mm7",$Hsse2);
+
+	&pxor	($BxC,"mm1");			# de-magic
+	&paddq	($A,&QWP(0,"esi"));
+	&paddq	("mm1",&QWP(8,"esi"));
+	&paddq	($BxC,&QWP(16,"esi"));
+	&paddq	("mm3",&QWP(24,"esi"));
+	&paddq	($E,&QWP(32,"esi"));
+	&paddq	("mm5",&QWP(40,"esi"));
+	&paddq	("mm6",&QWP(48,"esi"));
+	&paddq	("mm7",&QWP(56,"esi"));
+
+	&mov	("eax",8*80);
+	&movq	(&QWP(0,"esi"),$A);
+	&movq	(&QWP(8,"esi"),"mm1");
+	&movq	(&QWP(16,"esi"),$BxC);
+	&movq	(&QWP(24,"esi"),"mm3");
+	&movq	(&QWP(32,"esi"),$E);
+	&movq	(&QWP(40,"esi"),"mm5");
+	&movq	(&QWP(48,"esi"),"mm6");
+	&movq	(&QWP(56,"esi"),"mm7");
+
+	&lea	("esp",&DWP(0,"esp","eax"));	# destroy frame
+	&sub	($K512,"eax");			# rewind K
+
+	&cmp	("edi",&DWP(8*10+8,"esp"));	# are we done yet?
+	&jb	(&label("loop_sse2"));
+
+	&mov	("esp",&DWP(8*10+12,"esp"));	# restore sp
+	&emms	();
+&function_end_A();
+
+&set_label("SSSE3",32);
+{ my ($cnt,$frame)=("ecx","edx");
+  my @X=map("xmm$_",(0..7));
+  my $j;
+  my $i=0;
+
+	&lea	($frame,&DWP(-64,"esp"));
+	&sub	("esp",256);
+
+	# fixed stack frame layout
+	#
+	# +0	A B C D E F G H		# backing store
+	# +64	X[0]+K[i] .. X[15]+K[i]	# XMM->MM xfer area
+	# +192				# XMM off-load ring buffer
+	# +256				# saved parameters
+
+	&movdqa		(@X[1],&QWP(80*8,$K512));		# byte swap mask
+	&movdqu		(@X[0],&QWP(0,"edi"));
+	&pshufb		(@X[0],@X[1]);
+    for ($j=0;$j<8;$j++) {
+	&movdqa		(&QWP(16*(($j-1)%4),$frame),@X[3])	if ($j>4); # off-load
+	&movdqa		(@X[3],&QWP(16*($j%8),$K512));
+	&movdqa		(@X[2],@X[1])				if ($j<7); # perpetuate byte swap mask
+	&movdqu		(@X[1],&QWP(16*($j+1),"edi"))		if ($j<7); # next input
+	&movdqa		(@X[1],&QWP(16*(($j+1)%4),$frame))	if ($j==7);# restore @X[0]
+	&paddq		(@X[3],@X[0]);
+	&pshufb		(@X[1],@X[2])				if ($j<7);
+	&movdqa		(&QWP(16*($j%8)-128,$frame),@X[3]);	# xfer X[i]+K[i]
+
+	push(@X,shift(@X));					# rotate(@X)
+    }
+	#&jmp		(&label("loop_ssse3"));
+	&nop		();
+
+&set_label("loop_ssse3",32);
+	&movdqa		(@X[2],&QWP(16*(($j+1)%4),$frame));	# pre-restore @X[1]
+	&movdqa		(&QWP(16*(($j-1)%4),$frame),@X[3]);	# off-load @X[3]
+	&lea		($K512,&DWP(16*8,$K512));
+
+	#&movq	($Asse2,$A);			# off-load A-H
+	&movq	($Bsse2,"mm1");
+	 &mov	("ebx","edi");
+	&movq	($Csse2,$BxC);
+	 &lea	("edi",&DWP(128,"edi"));	# advance input
+	&movq	($Dsse2,"mm3");
+	 &cmp	("edi","eax");
+	#&movq	($Esse2,$E);
+	&movq	($Fsse2,"mm5");
+	 &cmovb	("ebx","edi");
+	&movq	($Gsse2,"mm6");
+	 &mov	("ecx",4);			# loop counter
+	&pxor	($BxC,"mm1");			# magic
+	&movq	($Hsse2,"mm7");
+	&pxor	("mm3","mm3");			# magic
+
+	&jmp		(&label("00_47_ssse3"));
+
+sub BODY_00_15_ssse3 {		# "phase-less" copy of BODY_00_15_sse2
+	(
+	'&movq	("mm1",$E)',				# %mm1 is sliding right
+	'&movq	("mm7",&QWP(((-8*$i)%128)-128,$frame))',# X[i]+K[i]
+	 '&pxor	("mm5","mm6")',				# f^=g
+	'&psrlq	("mm1",14)',
+	 '&movq	(&QWP(8*($i+4)%64,"esp"),$E)',		# modulo-scheduled save e
+	 '&pand	("mm5",$E)',				# f&=e
+	'&psllq	($E,23)',				# $E is sliding left
+	'&paddq	($A,"mm3")',				# [h+=Maj(a,b,c)]
+	'&movq	("mm3","mm1")',				# %mm3 is T1
+	 '&psrlq("mm1",4)',
+	 '&pxor	("mm5","mm6")',				# Ch(e,f,g)
+	'&pxor	("mm3",$E)',
+	 '&psllq($E,23)',
+	'&pxor	("mm3","mm1")',
+	 '&movq	(&QWP(8*$i%64,"esp"),$A)',		# modulo-scheduled save a
+	 '&paddq("mm7","mm5")',				# X[i]+=Ch(e,f,g)
+	'&pxor	("mm3",$E)',
+	 '&psrlq("mm1",23)',
+	 '&paddq("mm7",&QWP(8*($i+7)%64,"esp"))',	# X[i]+=h
+	'&pxor	("mm3","mm1")',
+	 '&psllq($E,4)',
+	'&pxor	("mm3",$E)',				# T1=Sigma1_512(e)
+
+	 '&movq	($E,&QWP(8*($i+3)%64,"esp"))',		# e = load d, e in next round
+	'&paddq	("mm3","mm7")',				# T1+=X[i]
+	 '&movq	("mm5",$A)',				# %mm5 is sliding right
+	 '&psrlq("mm5",28)',
+	'&paddq	($E,"mm3")',				# d += T1
+	 '&movq	("mm6",$A)',				# %mm6 is sliding left
+	 '&movq	("mm7","mm5")',
+	 '&psllq("mm6",25)',
+	'&movq	("mm1",&QWP(8*($i+1)%64,"esp"))',	# load b
+	 '&psrlq("mm5",6)',
+	 '&pxor	("mm7","mm6")',
+	 '&psllq("mm6",5)',
+	 '&pxor	("mm7","mm5")',
+	'&pxor	($A,"mm1")',				# a^b, b^c in next round
+	 '&psrlq("mm5",5)',
+	 '&pxor	("mm7","mm6")',
+	'&pand	($BxC,$A)',				# (b^c)&(a^b)
+	 '&psllq("mm6",6)',
+	 '&pxor	("mm7","mm5")',
+	'&pxor	($BxC,"mm1")',				# [h=]Maj(a,b,c)
+	 '&pxor	("mm6","mm7")',				# Sigma0_512(a)
+	 '&movq	("mm5",&QWP(8*($i+5-1)%64,"esp"))',	# pre-load f
+	'&paddq	($BxC,"mm6")',				# h+=Sigma0(a)
+	 '&movq	("mm6",&QWP(8*($i+6-1)%64,"esp"))',	# pre-load g
+
+	'($A,$BxC) = ($BxC,$A); $i--;'
+	);
+}
+
+&set_label("00_47_ssse3",32);
+
+    for(;$j<16;$j++) {
+	my ($t0,$t2,$t1)=@X[2..4];
+	my @insns = (&BODY_00_15_ssse3(),&BODY_00_15_ssse3());
+
+	&movdqa		($t2,@X[5]);
+	&movdqa		(@X[1],$t0);			# restore @X[1]
+	&palignr	($t0,@X[0],8);			# X[1..2]
+	&movdqa		(&QWP(16*($j%4),$frame),@X[4]);	# off-load @X[4]
+	 &palignr	($t2,@X[4],8);			# X[9..10]
+
+	&movdqa		($t1,$t0);
+	&psrlq		($t0,7);
+	 &paddq		(@X[0],$t2);			# X[0..1] += X[9..10]
+	&movdqa		($t2,$t1);
+	&psrlq		($t1,1);
+	&psllq		($t2,64-8);
+	&pxor		($t0,$t1);
+	&psrlq		($t1,8-1);
+	&pxor		($t0,$t2);
+	&psllq		($t2,8-1);
+	&pxor		($t0,$t1);
+	 &movdqa	($t1,@X[7]);
+	&pxor		($t0,$t2);			# sigma0(X[1..2])
+	 &movdqa	($t2,@X[7]);
+	 &psrlq		($t1,6);
+	&paddq		(@X[0],$t0);			# X[0..1] += sigma0(X[1..2])
+
+	&movdqa		($t0,@X[7]);
+	&psrlq		($t2,19);
+	&psllq		($t0,64-61);
+	&pxor		($t1,$t2);
+	&psrlq		($t2,61-19);
+	&pxor		($t1,$t0);
+	&psllq		($t0,61-19);
+	&pxor		($t1,$t2);
+	&movdqa		($t2,&QWP(16*(($j+2)%4),$frame));# pre-restore @X[1]
+	&pxor		($t1,$t0);			# sigma0(X[1..2])
+	&movdqa		($t0,&QWP(16*($j%8),$K512));
+	 eval(shift(@insns));
+	&paddq		(@X[0],$t1);			# X[0..1] += sigma0(X[14..15])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddq		($t0,@X[0]);
+	 foreach(@insns) { eval; }
+	&movdqa		(&QWP(16*($j%8)-128,$frame),$t0);# xfer X[i]+K[i]
+
+	push(@X,shift(@X));				# rotate(@X)
+    }
+	&lea		($K512,&DWP(16*8,$K512));
+	&dec		("ecx");
+	&jnz		(&label("00_47_ssse3"));
+
+	&movdqa		(@X[1],&QWP(0,$K512));		# byte swap mask
+	&lea		($K512,&DWP(-80*8,$K512));	# rewind
+	&movdqu		(@X[0],&QWP(0,"ebx"));
+	&pshufb		(@X[0],@X[1]);
+
+    for ($j=0;$j<8;$j++) {	# load next or same block
+	my @insns = (&BODY_00_15_ssse3(),&BODY_00_15_ssse3());
+
+	&movdqa		(&QWP(16*(($j-1)%4),$frame),@X[3])	if ($j>4); # off-load
+	&movdqa		(@X[3],&QWP(16*($j%8),$K512));
+	&movdqa		(@X[2],@X[1])				if ($j<7); # perpetuate byte swap mask
+	&movdqu		(@X[1],&QWP(16*($j+1),"ebx"))		if ($j<7); # next input
+	&movdqa		(@X[1],&QWP(16*(($j+1)%4),$frame))	if ($j==7);# restore @X[0]
+	&paddq		(@X[3],@X[0]);
+	&pshufb		(@X[1],@X[2])				if ($j<7);
+	 foreach(@insns) { eval; }
+	&movdqa		(&QWP(16*($j%8)-128,$frame),@X[3]);# xfer X[i]+K[i]
+
+	push(@X,shift(@X));				# rotate(@X)
+    }
+
+	#&movq	($A,$Asse2);			# load A-H
+	&movq	("mm1",$Bsse2);
+	&paddq	($A,"mm3");			# from BODY_00_15
+	#&movq	($BxC,$Csse2);
+	&movq	("mm3",$Dsse2);
+	#&movq	($E,$Esse2);
+	#&movq	("mm5",$Fsse2);
+	#&movq	("mm6",$Gsse2);
+	&movq	("mm7",$Hsse2);
+
+	&pxor	($BxC,"mm1");			# de-magic
+	&paddq	($A,&QWP(0,"esi"));
+	&paddq	("mm1",&QWP(8,"esi"));
+	&paddq	($BxC,&QWP(16,"esi"));
+	&paddq	("mm3",&QWP(24,"esi"));
+	&paddq	($E,&QWP(32,"esi"));
+	&paddq	("mm5",&QWP(40,"esi"));
+	&paddq	("mm6",&QWP(48,"esi"));
+	&paddq	("mm7",&QWP(56,"esi"));
+
+	&movq	(&QWP(0,"esi"),$A);
+	&movq	(&QWP(8,"esi"),"mm1");
+	&movq	(&QWP(16,"esi"),$BxC);
+	&movq	(&QWP(24,"esi"),"mm3");
+	&movq	(&QWP(32,"esi"),$E);
+	&movq	(&QWP(40,"esi"),"mm5");
+	&movq	(&QWP(48,"esi"),"mm6");
+	&movq	(&QWP(56,"esi"),"mm7");
+
+    	&cmp	("edi","eax")			# are we done yet?
+	&jb	(&label("loop_ssse3"));
+
+	&mov	("esp",&DWP(64+12,$frame));	# restore sp
+	&emms	();
+}
+&function_end_A();
+}
+&set_label("loop_x86",16);
+    # copy input block to stack reversing byte and qword order
+    for ($i=0;$i<8;$i++) {
+	&mov	("eax",&DWP($i*16+0,"edi"));
+	&mov	("ebx",&DWP($i*16+4,"edi"));
+	&mov	("ecx",&DWP($i*16+8,"edi"));
+	&mov	("edx",&DWP($i*16+12,"edi"));
+	&bswap	("eax");
+	&bswap	("ebx");
+	&bswap	("ecx");
+	&bswap	("edx");
+	&push	("eax");
+	&push	("ebx");
+	&push	("ecx");
+	&push	("edx");
+    }
+	&add	("edi",128);
+	&sub	("esp",9*8);		# place for T,A,B,C,D,E,F,G,H
+	&mov	(&DWP(8*(9+16)+4,"esp"),"edi");
+
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&lea	("edi",&DWP(8,"esp"));
+	&mov	("ecx",16);
+	&data_word(0xA5F3F689);		# rep movsd
+
+&set_label("00_15_x86",16);
+	&BODY_00_15_x86();
+
+	&cmp	(&LB("edx"),0x94);
+	&jne	(&label("00_15_x86"));
+
+&set_label("16_79_x86",16);
+	#define sigma0(x)	(ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+	#	LO		lo>>1^hi<<31  ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
+	#	HI		hi>>1^lo<<31  ^ hi>>8^lo<<24 ^ hi>>7
+	&mov	("ecx",&DWP(8*(9+15+16-1)+0,"esp"));
+	&mov	("edx",&DWP(8*(9+15+16-1)+4,"esp"));
+	&mov	("esi","ecx");
+
+	&shr	("ecx",1);	# lo>>1
+	&mov	("edi","edx");
+	&shr	("edx",1);	# hi>>1
+	&mov	("eax","ecx");
+	&shl	("esi",24);	# lo<<24
+	&mov	("ebx","edx");
+	&shl	("edi",24);	# hi<<24
+	&xor	("ebx","esi");
+
+	&shr	("ecx",7-1);	# lo>>7
+	&xor	("eax","edi");
+	&shr	("edx",7-1);	# hi>>7
+	&xor	("eax","ecx");
+	&shl	("esi",31-24);	# lo<<31
+	&xor	("ebx","edx");
+	&shl	("edi",25-24);	# hi<<25
+	&xor	("ebx","esi");
+
+	&shr	("ecx",8-7);	# lo>>8
+	&xor	("eax","edi");
+	&shr	("edx",8-7);	# hi>>8
+	&xor	("eax","ecx");
+	&shl	("edi",31-25);	# hi<<31
+	&xor	("ebx","edx");
+	&xor	("eax","edi");			# T1 = sigma0(X[-15])
+
+	&mov	(&DWP(0,"esp"),"eax");
+	&mov	(&DWP(4,"esp"),"ebx");		# put T1 away
+
+	#define sigma1(x)	(ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+	#	LO		lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
+	#	HI		hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
+	&mov	("ecx",&DWP(8*(9+15+16-14)+0,"esp"));
+	&mov	("edx",&DWP(8*(9+15+16-14)+4,"esp"));
+	&mov	("esi","ecx");
+
+	&shr	("ecx",6);	# lo>>6
+	&mov	("edi","edx");
+	&shr	("edx",6);	# hi>>6
+	&mov	("eax","ecx");
+	&shl	("esi",3);	# lo<<3
+	&mov	("ebx","edx");
+	&shl	("edi",3);	# hi<<3
+	&xor	("eax","esi");
+
+	&shr	("ecx",19-6);	# lo>>19
+	&xor	("ebx","edi");
+	&shr	("edx",19-6);	# hi>>19
+	&xor	("eax","ecx");
+	&shl	("esi",13-3);	# lo<<13
+	&xor	("ebx","edx");
+	&shl	("edi",13-3);	# hi<<13
+	&xor	("ebx","esi");
+
+	&shr	("ecx",29-19);	# lo>>29
+	&xor	("eax","edi");
+	&shr	("edx",29-19);	# hi>>29
+	&xor	("ebx","ecx");
+	&shl	("edi",26-13);	# hi<<26
+	&xor	("eax","edx");
+	&xor	("eax","edi");			# sigma1(X[-2])
+
+	&mov	("ecx",&DWP(8*(9+15+16)+0,"esp"));
+	&mov	("edx",&DWP(8*(9+15+16)+4,"esp"));
+	&add	("eax",&DWP(0,"esp"));
+	&adc	("ebx",&DWP(4,"esp"));		# T1 = sigma1(X[-2])+T1
+	&mov	("esi",&DWP(8*(9+15+16-9)+0,"esp"));
+	&mov	("edi",&DWP(8*(9+15+16-9)+4,"esp"));
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# T1 += X[-16]
+	&add	("eax","esi");
+	&adc	("ebx","edi");			# T1 += X[-7]
+	&mov	(&DWP(8*(9+15)+0,"esp"),"eax");
+	&mov	(&DWP(8*(9+15)+4,"esp"),"ebx");	# save X[0]
+
+	&BODY_00_15_x86();
+
+	&cmp	(&LB("edx"),0x17);
+	&jne	(&label("16_79_x86"));
+
+	&mov	("esi",&DWP(8*(9+16+80)+0,"esp"));# ctx
+	&mov	("edi",&DWP(8*(9+16+80)+4,"esp"));# inp
+    for($i=0;$i<4;$i++) {
+	&mov	("eax",&DWP($i*16+0,"esi"));
+	&mov	("ebx",&DWP($i*16+4,"esi"));
+	&mov	("ecx",&DWP($i*16+8,"esi"));
+	&mov	("edx",&DWP($i*16+12,"esi"));
+	&add	("eax",&DWP(8+($i*16)+0,"esp"));
+	&adc	("ebx",&DWP(8+($i*16)+4,"esp"));
+	&mov	(&DWP($i*16+0,"esi"),"eax");
+	&mov	(&DWP($i*16+4,"esi"),"ebx");
+	&add	("ecx",&DWP(8+($i*16)+8,"esp"));
+	&adc	("edx",&DWP(8+($i*16)+12,"esp"));
+	&mov	(&DWP($i*16+8,"esi"),"ecx");
+	&mov	(&DWP($i*16+12,"esi"),"edx");
+    }
+	&add	("esp",8*(9+16+80));		# destroy frame
+	&sub	($K512,8*80);			# rewind K
+
+	&cmp	("edi",&DWP(8,"esp"));		# are we done yet?
+	&jb	(&label("loop_x86"));
+
+	&mov	("esp",&DWP(12,"esp"));		# restore sp
+&function_end_A();
+
+&set_label("K512",64);	# Yes! I keep it in the code segment!
+	&data_word(0xd728ae22,0x428a2f98);	# u64
+	&data_word(0x23ef65cd,0x71374491);	# u64
+	&data_word(0xec4d3b2f,0xb5c0fbcf);	# u64
+	&data_word(0x8189dbbc,0xe9b5dba5);	# u64
+	&data_word(0xf348b538,0x3956c25b);	# u64
+	&data_word(0xb605d019,0x59f111f1);	# u64
+	&data_word(0xaf194f9b,0x923f82a4);	# u64
+	&data_word(0xda6d8118,0xab1c5ed5);	# u64
+	&data_word(0xa3030242,0xd807aa98);	# u64
+	&data_word(0x45706fbe,0x12835b01);	# u64
+	&data_word(0x4ee4b28c,0x243185be);	# u64
+	&data_word(0xd5ffb4e2,0x550c7dc3);	# u64
+	&data_word(0xf27b896f,0x72be5d74);	# u64
+	&data_word(0x3b1696b1,0x80deb1fe);	# u64
+	&data_word(0x25c71235,0x9bdc06a7);	# u64
+	&data_word(0xcf692694,0xc19bf174);	# u64
+	&data_word(0x9ef14ad2,0xe49b69c1);	# u64
+	&data_word(0x384f25e3,0xefbe4786);	# u64
+	&data_word(0x8b8cd5b5,0x0fc19dc6);	# u64
+	&data_word(0x77ac9c65,0x240ca1cc);	# u64
+	&data_word(0x592b0275,0x2de92c6f);	# u64
+	&data_word(0x6ea6e483,0x4a7484aa);	# u64
+	&data_word(0xbd41fbd4,0x5cb0a9dc);	# u64
+	&data_word(0x831153b5,0x76f988da);	# u64
+	&data_word(0xee66dfab,0x983e5152);	# u64
+	&data_word(0x2db43210,0xa831c66d);	# u64
+	&data_word(0x98fb213f,0xb00327c8);	# u64
+	&data_word(0xbeef0ee4,0xbf597fc7);	# u64
+	&data_word(0x3da88fc2,0xc6e00bf3);	# u64
+	&data_word(0x930aa725,0xd5a79147);	# u64
+	&data_word(0xe003826f,0x06ca6351);	# u64
+	&data_word(0x0a0e6e70,0x14292967);	# u64
+	&data_word(0x46d22ffc,0x27b70a85);	# u64
+	&data_word(0x5c26c926,0x2e1b2138);	# u64
+	&data_word(0x5ac42aed,0x4d2c6dfc);	# u64
+	&data_word(0x9d95b3df,0x53380d13);	# u64
+	&data_word(0x8baf63de,0x650a7354);	# u64
+	&data_word(0x3c77b2a8,0x766a0abb);	# u64
+	&data_word(0x47edaee6,0x81c2c92e);	# u64
+	&data_word(0x1482353b,0x92722c85);	# u64
+	&data_word(0x4cf10364,0xa2bfe8a1);	# u64
+	&data_word(0xbc423001,0xa81a664b);	# u64
+	&data_word(0xd0f89791,0xc24b8b70);	# u64
+	&data_word(0x0654be30,0xc76c51a3);	# u64
+	&data_word(0xd6ef5218,0xd192e819);	# u64
+	&data_word(0x5565a910,0xd6990624);	# u64
+	&data_word(0x5771202a,0xf40e3585);	# u64
+	&data_word(0x32bbd1b8,0x106aa070);	# u64
+	&data_word(0xb8d2d0c8,0x19a4c116);	# u64
+	&data_word(0x5141ab53,0x1e376c08);	# u64
+	&data_word(0xdf8eeb99,0x2748774c);	# u64
+	&data_word(0xe19b48a8,0x34b0bcb5);	# u64
+	&data_word(0xc5c95a63,0x391c0cb3);	# u64
+	&data_word(0xe3418acb,0x4ed8aa4a);	# u64
+	&data_word(0x7763e373,0x5b9cca4f);	# u64
+	&data_word(0xd6b2b8a3,0x682e6ff3);	# u64
+	&data_word(0x5defb2fc,0x748f82ee);	# u64
+	&data_word(0x43172f60,0x78a5636f);	# u64
+	&data_word(0xa1f0ab72,0x84c87814);	# u64
+	&data_word(0x1a6439ec,0x8cc70208);	# u64
+	&data_word(0x23631e28,0x90befffa);	# u64
+	&data_word(0xde82bde9,0xa4506ceb);	# u64
+	&data_word(0xb2c67915,0xbef9a3f7);	# u64
+	&data_word(0xe372532b,0xc67178f2);	# u64
+	&data_word(0xea26619c,0xca273ece);	# u64
+	&data_word(0x21c0c207,0xd186b8c7);	# u64
+	&data_word(0xcde0eb1e,0xeada7dd6);	# u64
+	&data_word(0xee6ed178,0xf57d4f7f);	# u64
+	&data_word(0x72176fba,0x06f067aa);	# u64
+	&data_word(0xa2c898a6,0x0a637dc5);	# u64
+	&data_word(0xbef90dae,0x113f9804);	# u64
+	&data_word(0x131c471b,0x1b710b35);	# u64
+	&data_word(0x23047d84,0x28db77f5);	# u64
+	&data_word(0x40c72493,0x32caab7b);	# u64
+	&data_word(0x15c9bebc,0x3c9ebe0a);	# u64
+	&data_word(0x9c100d4c,0x431d67c4);	# u64
+	&data_word(0xcb3e42b6,0x4cc5d4be);	# u64
+	&data_word(0xfc657e2a,0x597f299c);	# u64
+	&data_word(0x3ad6faec,0x5fcb6fab);	# u64
+	&data_word(0x4a475817,0x6c44198c);	# u64
+
+	&data_word(0x04050607,0x00010203);	# byte swap
+	&data_word(0x0c0d0e0f,0x08090a0b);	# mask
+&function_end_B("sha512_block_data_order");
+&asciz("SHA512 block transform for x86, CRYPTOGAMS by <appro\@openssl.org>");
+
+&asm_finish();
diff --git a/crypto/sha/asm/sha512-armv4.pl b/crypto/sha/asm/sha512-armv4.pl
new file mode 100644
index 0000000..ae2cbfc
--- /dev/null
+++ b/crypto/sha/asm/sha512-armv4.pl
@@ -0,0 +1,604 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA512 block procedure for ARMv4. September 2007.
+
+# This code is ~4.5 (four and a half) times faster than code generated
+# by gcc 3.4 and it spends ~72 clock cycles per byte [on single-issue
+# Xscale PXA250 core].
+#
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 6% improvement on
+# Cortex A8 core and ~40 cycles per processed byte.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 7%
+# improvement on Coxtex A8 core and ~38 cycles per byte.
+
+# March 2011.
+#
+# Add NEON implementation. On Cortex A8 it was measured to process
+# one byte in 23.3 cycles or ~60% faster than integer-only code.
+
+# August 2012.
+#
+# Improve NEON performance by 12% on Snapdragon S4. In absolute
+# terms it's 22.6 cycles per byte, which is disappointing result.
+# Technical writers asserted that 3-way S4 pipeline can sustain
+# multiple NEON instructions per cycle, but dual NEON issue could
+# not be observed, and for NEON-only sequences IPC(*) was found to
+# be limited by 1:-( 0.33 and 0.66 were measured for sequences with
+# ILPs(*) of 1 and 2 respectively. This in turn means that you can
+# even find yourself striving, as I did here, for achieving IPC
+# adequate to one delivered by Cortex A8 [for reference, it's
+# 0.5 for ILP of 1, and 1 for higher ILPs].
+#
+# (*) ILP, instruction-level parallelism, how many instructions
+#     *can* execute at the same time. IPC, instructions per cycle,
+#     indicates how many instructions actually execute.
+
+# Byte order [in]dependence. =========================================
+#
+# Originally caller was expected to maintain specific *dword* order in
+# h[0-7], namely with most significant dword at *lower* address, which
+# was reflected in below two parameters as 0 and 4. Now caller is
+# expected to maintain native byte order for whole 64-bit values.
+$hi="HI";
+$lo="LO";
+# ====================================================================
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$ctx="r0";	# parameter block
+$inp="r1";
+$len="r2";
+
+$Tlo="r3";
+$Thi="r4";
+$Alo="r5";
+$Ahi="r6";
+$Elo="r7";
+$Ehi="r8";
+$t0="r9";
+$t1="r10";
+$t2="r11";
+$t3="r12";
+############	r13 is stack pointer
+$Ktbl="r14";
+############	r15 is program counter
+
+$Aoff=8*0;
+$Boff=8*1;
+$Coff=8*2;
+$Doff=8*3;
+$Eoff=8*4;
+$Foff=8*5;
+$Goff=8*6;
+$Hoff=8*7;
+$Xoff=8*8;
+
+sub BODY_00_15() {
+my $magic = shift;
+$code.=<<___;
+	@ Sigma1(x)	(ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+	@ LO		lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+	@ HI		hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+	mov	$t0,$Elo,lsr#14
+	str	$Tlo,[sp,#$Xoff+0]
+	mov	$t1,$Ehi,lsr#14
+	str	$Thi,[sp,#$Xoff+4]
+	eor	$t0,$t0,$Ehi,lsl#18
+	ldr	$t2,[sp,#$Hoff+0]	@ h.lo
+	eor	$t1,$t1,$Elo,lsl#18
+	ldr	$t3,[sp,#$Hoff+4]	@ h.hi
+	eor	$t0,$t0,$Elo,lsr#18
+	eor	$t1,$t1,$Ehi,lsr#18
+	eor	$t0,$t0,$Ehi,lsl#14
+	eor	$t1,$t1,$Elo,lsl#14
+	eor	$t0,$t0,$Ehi,lsr#9
+	eor	$t1,$t1,$Elo,lsr#9
+	eor	$t0,$t0,$Elo,lsl#23
+	eor	$t1,$t1,$Ehi,lsl#23	@ Sigma1(e)
+	adds	$Tlo,$Tlo,$t0
+	ldr	$t0,[sp,#$Foff+0]	@ f.lo
+	adc	$Thi,$Thi,$t1		@ T += Sigma1(e)
+	ldr	$t1,[sp,#$Foff+4]	@ f.hi
+	adds	$Tlo,$Tlo,$t2
+	ldr	$t2,[sp,#$Goff+0]	@ g.lo
+	adc	$Thi,$Thi,$t3		@ T += h
+	ldr	$t3,[sp,#$Goff+4]	@ g.hi
+
+	eor	$t0,$t0,$t2
+	str	$Elo,[sp,#$Eoff+0]
+	eor	$t1,$t1,$t3
+	str	$Ehi,[sp,#$Eoff+4]
+	and	$t0,$t0,$Elo
+	str	$Alo,[sp,#$Aoff+0]
+	and	$t1,$t1,$Ehi
+	str	$Ahi,[sp,#$Aoff+4]
+	eor	$t0,$t0,$t2
+	ldr	$t2,[$Ktbl,#$lo]	@ K[i].lo
+	eor	$t1,$t1,$t3		@ Ch(e,f,g)
+	ldr	$t3,[$Ktbl,#$hi]	@ K[i].hi
+
+	adds	$Tlo,$Tlo,$t0
+	ldr	$Elo,[sp,#$Doff+0]	@ d.lo
+	adc	$Thi,$Thi,$t1		@ T += Ch(e,f,g)
+	ldr	$Ehi,[sp,#$Doff+4]	@ d.hi
+	adds	$Tlo,$Tlo,$t2
+	and	$t0,$t2,#0xff
+	adc	$Thi,$Thi,$t3		@ T += K[i]
+	adds	$Elo,$Elo,$Tlo
+	ldr	$t2,[sp,#$Boff+0]	@ b.lo
+	adc	$Ehi,$Ehi,$Thi		@ d += T
+	teq	$t0,#$magic
+
+	ldr	$t3,[sp,#$Coff+0]	@ c.lo
+	orreq	$Ktbl,$Ktbl,#1
+	@ Sigma0(x)	(ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+	@ LO		lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+	@ HI		hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+	mov	$t0,$Alo,lsr#28
+	mov	$t1,$Ahi,lsr#28
+	eor	$t0,$t0,$Ahi,lsl#4
+	eor	$t1,$t1,$Alo,lsl#4
+	eor	$t0,$t0,$Ahi,lsr#2
+	eor	$t1,$t1,$Alo,lsr#2
+	eor	$t0,$t0,$Alo,lsl#30
+	eor	$t1,$t1,$Ahi,lsl#30
+	eor	$t0,$t0,$Ahi,lsr#7
+	eor	$t1,$t1,$Alo,lsr#7
+	eor	$t0,$t0,$Alo,lsl#25
+	eor	$t1,$t1,$Ahi,lsl#25	@ Sigma0(a)
+	adds	$Tlo,$Tlo,$t0
+	and	$t0,$Alo,$t2
+	adc	$Thi,$Thi,$t1		@ T += Sigma0(a)
+
+	ldr	$t1,[sp,#$Boff+4]	@ b.hi
+	orr	$Alo,$Alo,$t2
+	ldr	$t2,[sp,#$Coff+4]	@ c.hi
+	and	$Alo,$Alo,$t3
+	and	$t3,$Ahi,$t1
+	orr	$Ahi,$Ahi,$t1
+	orr	$Alo,$Alo,$t0		@ Maj(a,b,c).lo
+	and	$Ahi,$Ahi,$t2
+	adds	$Alo,$Alo,$Tlo
+	orr	$Ahi,$Ahi,$t3		@ Maj(a,b,c).hi
+	sub	sp,sp,#8
+	adc	$Ahi,$Ahi,$Thi		@ h += T
+	tst	$Ktbl,#1
+	add	$Ktbl,$Ktbl,#8
+___
+}
+$code=<<___;
+#if defined(__arm__)
+#include "arm_arch.h"
+#ifdef __ARMEL__
+# define LO 0
+# define HI 4
+# define WORD64(hi0,lo0,hi1,lo1)	.word	lo0,hi0, lo1,hi1
+#else
+# define HI 0
+# define LO 4
+# define WORD64(hi0,lo0,hi1,lo1)	.word	hi0,lo0, hi1,lo1
+#endif
+
+.text
+.code	32
+.type	K512,%object
+.align	5
+K512:
+WORD64(0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd)
+WORD64(0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc)
+WORD64(0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019)
+WORD64(0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118)
+WORD64(0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe)
+WORD64(0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2)
+WORD64(0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1)
+WORD64(0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694)
+WORD64(0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3)
+WORD64(0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65)
+WORD64(0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483)
+WORD64(0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5)
+WORD64(0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210)
+WORD64(0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4)
+WORD64(0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725)
+WORD64(0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70)
+WORD64(0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926)
+WORD64(0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df)
+WORD64(0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8)
+WORD64(0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b)
+WORD64(0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001)
+WORD64(0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30)
+WORD64(0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910)
+WORD64(0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8)
+WORD64(0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53)
+WORD64(0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8)
+WORD64(0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb)
+WORD64(0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3)
+WORD64(0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60)
+WORD64(0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec)
+WORD64(0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9)
+WORD64(0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b)
+WORD64(0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207)
+WORD64(0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178)
+WORD64(0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6)
+WORD64(0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b)
+WORD64(0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493)
+WORD64(0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c)
+WORD64(0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a)
+WORD64(0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817)
+.size	K512,.-K512
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-sha512_block_data_order
+.skip	32-4
+
+.global	sha512_block_data_order
+.type	sha512_block_data_order,%function
+sha512_block_data_order:
+	sub	r3,pc,#8		@ sha512_block_data_order
+	add	$len,$inp,$len,lsl#7	@ len to point at the end of inp
+#if __ARM_ARCH__>=7
+	ldr	r12,.LOPENSSL_armcap
+	ldr	r12,[r3,r12]		@ OPENSSL_armcap_P
+	tst	r12,#1
+	bne	.LNEON
+#endif
+	stmdb	sp!,{r4-r12,lr}
+	sub	$Ktbl,r3,#672		@ K512
+	sub	sp,sp,#9*8
+
+	ldr	$Elo,[$ctx,#$Eoff+$lo]
+	ldr	$Ehi,[$ctx,#$Eoff+$hi]
+	ldr	$t0, [$ctx,#$Goff+$lo]
+	ldr	$t1, [$ctx,#$Goff+$hi]
+	ldr	$t2, [$ctx,#$Hoff+$lo]
+	ldr	$t3, [$ctx,#$Hoff+$hi]
+.Loop:
+	str	$t0, [sp,#$Goff+0]
+	str	$t1, [sp,#$Goff+4]
+	str	$t2, [sp,#$Hoff+0]
+	str	$t3, [sp,#$Hoff+4]
+	ldr	$Alo,[$ctx,#$Aoff+$lo]
+	ldr	$Ahi,[$ctx,#$Aoff+$hi]
+	ldr	$Tlo,[$ctx,#$Boff+$lo]
+	ldr	$Thi,[$ctx,#$Boff+$hi]
+	ldr	$t0, [$ctx,#$Coff+$lo]
+	ldr	$t1, [$ctx,#$Coff+$hi]
+	ldr	$t2, [$ctx,#$Doff+$lo]
+	ldr	$t3, [$ctx,#$Doff+$hi]
+	str	$Tlo,[sp,#$Boff+0]
+	str	$Thi,[sp,#$Boff+4]
+	str	$t0, [sp,#$Coff+0]
+	str	$t1, [sp,#$Coff+4]
+	str	$t2, [sp,#$Doff+0]
+	str	$t3, [sp,#$Doff+4]
+	ldr	$Tlo,[$ctx,#$Foff+$lo]
+	ldr	$Thi,[$ctx,#$Foff+$hi]
+	str	$Tlo,[sp,#$Foff+0]
+	str	$Thi,[sp,#$Foff+4]
+
+.L00_15:
+#if __ARM_ARCH__<7
+	ldrb	$Tlo,[$inp,#7]
+	ldrb	$t0, [$inp,#6]
+	ldrb	$t1, [$inp,#5]
+	ldrb	$t2, [$inp,#4]
+	ldrb	$Thi,[$inp,#3]
+	ldrb	$t3, [$inp,#2]
+	orr	$Tlo,$Tlo,$t0,lsl#8
+	ldrb	$t0, [$inp,#1]
+	orr	$Tlo,$Tlo,$t1,lsl#16
+	ldrb	$t1, [$inp],#8
+	orr	$Tlo,$Tlo,$t2,lsl#24
+	orr	$Thi,$Thi,$t3,lsl#8
+	orr	$Thi,$Thi,$t0,lsl#16
+	orr	$Thi,$Thi,$t1,lsl#24
+#else
+	ldr	$Tlo,[$inp,#4]
+	ldr	$Thi,[$inp],#8
+#ifdef __ARMEL__
+	rev	$Tlo,$Tlo
+	rev	$Thi,$Thi
+#endif
+#endif
+___
+	&BODY_00_15(0x94);
+$code.=<<___;
+	tst	$Ktbl,#1
+	beq	.L00_15
+	ldr	$t0,[sp,#`$Xoff+8*(16-1)`+0]
+	ldr	$t1,[sp,#`$Xoff+8*(16-1)`+4]
+	bic	$Ktbl,$Ktbl,#1
+.L16_79:
+	@ sigma0(x)	(ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+	@ LO		lo>>1^hi<<31  ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
+	@ HI		hi>>1^lo<<31  ^ hi>>8^lo<<24 ^ hi>>7
+	mov	$Tlo,$t0,lsr#1
+	ldr	$t2,[sp,#`$Xoff+8*(16-14)`+0]
+	mov	$Thi,$t1,lsr#1
+	ldr	$t3,[sp,#`$Xoff+8*(16-14)`+4]
+	eor	$Tlo,$Tlo,$t1,lsl#31
+	eor	$Thi,$Thi,$t0,lsl#31
+	eor	$Tlo,$Tlo,$t0,lsr#8
+	eor	$Thi,$Thi,$t1,lsr#8
+	eor	$Tlo,$Tlo,$t1,lsl#24
+	eor	$Thi,$Thi,$t0,lsl#24
+	eor	$Tlo,$Tlo,$t0,lsr#7
+	eor	$Thi,$Thi,$t1,lsr#7
+	eor	$Tlo,$Tlo,$t1,lsl#25
+
+	@ sigma1(x)	(ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+	@ LO		lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
+	@ HI		hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
+	mov	$t0,$t2,lsr#19
+	mov	$t1,$t3,lsr#19
+	eor	$t0,$t0,$t3,lsl#13
+	eor	$t1,$t1,$t2,lsl#13
+	eor	$t0,$t0,$t3,lsr#29
+	eor	$t1,$t1,$t2,lsr#29
+	eor	$t0,$t0,$t2,lsl#3
+	eor	$t1,$t1,$t3,lsl#3
+	eor	$t0,$t0,$t2,lsr#6
+	eor	$t1,$t1,$t3,lsr#6
+	ldr	$t2,[sp,#`$Xoff+8*(16-9)`+0]
+	eor	$t0,$t0,$t3,lsl#26
+
+	ldr	$t3,[sp,#`$Xoff+8*(16-9)`+4]
+	adds	$Tlo,$Tlo,$t0
+	ldr	$t0,[sp,#`$Xoff+8*16`+0]
+	adc	$Thi,$Thi,$t1
+
+	ldr	$t1,[sp,#`$Xoff+8*16`+4]
+	adds	$Tlo,$Tlo,$t2
+	adc	$Thi,$Thi,$t3
+	adds	$Tlo,$Tlo,$t0
+	adc	$Thi,$Thi,$t1
+___
+	&BODY_00_15(0x17);
+$code.=<<___;
+	ldreq	$t0,[sp,#`$Xoff+8*(16-1)`+0]
+	ldreq	$t1,[sp,#`$Xoff+8*(16-1)`+4]
+	beq	.L16_79
+	bic	$Ktbl,$Ktbl,#1
+
+	ldr	$Tlo,[sp,#$Boff+0]
+	ldr	$Thi,[sp,#$Boff+4]
+	ldr	$t0, [$ctx,#$Aoff+$lo]
+	ldr	$t1, [$ctx,#$Aoff+$hi]
+	ldr	$t2, [$ctx,#$Boff+$lo]
+	ldr	$t3, [$ctx,#$Boff+$hi]
+	adds	$t0,$Alo,$t0
+	str	$t0, [$ctx,#$Aoff+$lo]
+	adc	$t1,$Ahi,$t1
+	str	$t1, [$ctx,#$Aoff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Boff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Boff+$hi]
+
+	ldr	$Alo,[sp,#$Coff+0]
+	ldr	$Ahi,[sp,#$Coff+4]
+	ldr	$Tlo,[sp,#$Doff+0]
+	ldr	$Thi,[sp,#$Doff+4]
+	ldr	$t0, [$ctx,#$Coff+$lo]
+	ldr	$t1, [$ctx,#$Coff+$hi]
+	ldr	$t2, [$ctx,#$Doff+$lo]
+	ldr	$t3, [$ctx,#$Doff+$hi]
+	adds	$t0,$Alo,$t0
+	str	$t0, [$ctx,#$Coff+$lo]
+	adc	$t1,$Ahi,$t1
+	str	$t1, [$ctx,#$Coff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Doff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Doff+$hi]
+
+	ldr	$Tlo,[sp,#$Foff+0]
+	ldr	$Thi,[sp,#$Foff+4]
+	ldr	$t0, [$ctx,#$Eoff+$lo]
+	ldr	$t1, [$ctx,#$Eoff+$hi]
+	ldr	$t2, [$ctx,#$Foff+$lo]
+	ldr	$t3, [$ctx,#$Foff+$hi]
+	adds	$Elo,$Elo,$t0
+	str	$Elo,[$ctx,#$Eoff+$lo]
+	adc	$Ehi,$Ehi,$t1
+	str	$Ehi,[$ctx,#$Eoff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Foff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Foff+$hi]
+
+	ldr	$Alo,[sp,#$Goff+0]
+	ldr	$Ahi,[sp,#$Goff+4]
+	ldr	$Tlo,[sp,#$Hoff+0]
+	ldr	$Thi,[sp,#$Hoff+4]
+	ldr	$t0, [$ctx,#$Goff+$lo]
+	ldr	$t1, [$ctx,#$Goff+$hi]
+	ldr	$t2, [$ctx,#$Hoff+$lo]
+	ldr	$t3, [$ctx,#$Hoff+$hi]
+	adds	$t0,$Alo,$t0
+	str	$t0, [$ctx,#$Goff+$lo]
+	adc	$t1,$Ahi,$t1
+	str	$t1, [$ctx,#$Goff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Hoff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Hoff+$hi]
+
+	add	sp,sp,#640
+	sub	$Ktbl,$Ktbl,#640
+
+	teq	$inp,$len
+	bne	.Loop
+
+	add	sp,sp,#8*9		@ destroy frame
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+___
+
+{
+my @Sigma0=(28,34,39);
+my @Sigma1=(14,18,41);
+my @sigma0=(1, 8, 7);
+my @sigma1=(19,61,6);
+
+my $Ktbl="r3";
+my $cnt="r12";	# volatile register known as ip, intra-procedure-call scratch
+
+my @X=map("d$_",(0..15));
+my @V=($A,$B,$C,$D,$E,$F,$G,$H)=map("d$_",(16..23));
+
+sub NEON_00_15() {
+my $i=shift;
+my ($a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my ($t0,$t1,$t2,$T1,$K,$Ch,$Maj)=map("d$_",(24..31));	# temps
+
+$code.=<<___ if ($i<16 || $i&1);
+	vshr.u64	$t0,$e,#@Sigma1[0]	@ $i
+#if $i<16
+	vld1.64		{@X[$i%16]},[$inp]!	@ handles unaligned
+#endif
+	vshr.u64	$t1,$e,#@Sigma1[1]
+#if $i>0
+	 vadd.i64	$a,$Maj			@ h+=Maj from the past
+#endif
+	vshr.u64	$t2,$e,#@Sigma1[2]
+___
+$code.=<<___;
+	vld1.64		{$K},[$Ktbl,:64]!	@ K[i++]
+	vsli.64		$t0,$e,#`64-@Sigma1[0]`
+	vsli.64		$t1,$e,#`64-@Sigma1[1]`
+	vmov		$Ch,$e
+	vsli.64		$t2,$e,#`64-@Sigma1[2]`
+#if $i<16 && defined(__ARMEL__)
+	vrev64.8	@X[$i],@X[$i]
+#endif
+	veor		$t1,$t0
+	vbsl		$Ch,$f,$g		@ Ch(e,f,g)
+	vshr.u64	$t0,$a,#@Sigma0[0]
+	veor		$t2,$t1			@ Sigma1(e)
+	vadd.i64	$T1,$Ch,$h
+	vshr.u64	$t1,$a,#@Sigma0[1]
+	vsli.64		$t0,$a,#`64-@Sigma0[0]`
+	vadd.i64	$T1,$t2
+	vshr.u64	$t2,$a,#@Sigma0[2]
+	vadd.i64	$K,@X[$i%16]
+	vsli.64		$t1,$a,#`64-@Sigma0[1]`
+	veor		$Maj,$a,$b
+	vsli.64		$t2,$a,#`64-@Sigma0[2]`
+	veor		$h,$t0,$t1
+	vadd.i64	$T1,$K
+	vbsl		$Maj,$c,$b		@ Maj(a,b,c)
+	veor		$h,$t2			@ Sigma0(a)
+	vadd.i64	$d,$T1
+	vadd.i64	$Maj,$T1
+	@ vadd.i64	$h,$Maj
+___
+}
+
+sub NEON_16_79() {
+my $i=shift;
+
+if ($i&1)	{ &NEON_00_15($i,@_); return; }
+
+# 2x-vectorized, therefore runs every 2nd round
+my @X=map("q$_",(0..7));			# view @X as 128-bit vector
+my ($t0,$t1,$s0,$s1) = map("q$_",(12..15));	# temps
+my ($d0,$d1,$d2) = map("d$_",(24..26));		# temps from NEON_00_15
+my $e=@_[4];					# $e from NEON_00_15
+$i /= 2;
+$code.=<<___;
+	vshr.u64	$t0,@X[($i+7)%8],#@sigma1[0]
+	vshr.u64	$t1,@X[($i+7)%8],#@sigma1[1]
+	 vadd.i64	@_[0],d30			@ h+=Maj from the past
+	vshr.u64	$s1,@X[($i+7)%8],#@sigma1[2]
+	vsli.64		$t0,@X[($i+7)%8],#`64-@sigma1[0]`
+	vext.8		$s0,@X[$i%8],@X[($i+1)%8],#8	@ X[i+1]
+	vsli.64		$t1,@X[($i+7)%8],#`64-@sigma1[1]`
+	veor		$s1,$t0
+	vshr.u64	$t0,$s0,#@sigma0[0]
+	veor		$s1,$t1				@ sigma1(X[i+14])
+	vshr.u64	$t1,$s0,#@sigma0[1]
+	vadd.i64	@X[$i%8],$s1
+	vshr.u64	$s1,$s0,#@sigma0[2]
+	vsli.64		$t0,$s0,#`64-@sigma0[0]`
+	vsli.64		$t1,$s0,#`64-@sigma0[1]`
+	vext.8		$s0,@X[($i+4)%8],@X[($i+5)%8],#8	@ X[i+9]
+	veor		$s1,$t0
+	vshr.u64	$d0,$e,#@Sigma1[0]		@ from NEON_00_15
+	vadd.i64	@X[$i%8],$s0
+	vshr.u64	$d1,$e,#@Sigma1[1]		@ from NEON_00_15
+	veor		$s1,$t1				@ sigma0(X[i+1])
+	vshr.u64	$d2,$e,#@Sigma1[2]		@ from NEON_00_15
+	vadd.i64	@X[$i%8],$s1
+___
+	&NEON_00_15(2*$i,@_);
+}
+
+$code.=<<___;
+#if __ARM_ARCH__>=7
+.fpu	neon
+
+.align	4
+.LNEON:
+	dmb				@ errata #451034 on early Cortex A8
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+	sub	$Ktbl,r3,#672		@ K512
+	vldmia	$ctx,{$A-$H}		@ load context
+.Loop_neon:
+___
+for($i=0;$i<16;$i++)	{ &NEON_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	mov		$cnt,#4
+.L16_79_neon:
+	subs		$cnt,#1
+___
+for(;$i<32;$i++)	{ &NEON_16_79($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	bne		.L16_79_neon
+
+	 vadd.i64	$A,d30		@ h+=Maj from the past
+	vldmia		$ctx,{d24-d31}	@ load context to temp
+	vadd.i64	q8,q12		@ vectorized accumulate
+	vadd.i64	q9,q13
+	vadd.i64	q10,q14
+	vadd.i64	q11,q15
+	vstmia		$ctx,{$A-$H}	@ save context
+	teq		$inp,$len
+	sub		$Ktbl,#640	@ rewind K512
+	bne		.Loop_neon
+
+	vldmia	sp!,{d8-d15}		@ epilogue
+	bx	lr
+#endif
+___
+}
+$code.=<<___;
+.size	sha512_block_data_order,.-sha512_block_data_order
+.asciz	"SHA512 block transform for ARMv4/NEON, CRYPTOGAMS by <appro\@openssl.org>"
+.align	2
+.comm	OPENSSL_armcap_P,4,4
+
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+print $code;
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha512-x86_64.pl b/crypto/sha/asm/sha512-x86_64.pl
new file mode 100644
index 0000000..0c1285f
--- /dev/null
+++ b/crypto/sha/asm/sha512-x86_64.pl
@@ -0,0 +1,2152 @@
+#!/usr/bin/env perl
+#
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. Rights for redistribution and usage in source and binary
+# forms are granted according to the OpenSSL license.
+# ====================================================================
+#
+# sha256/512_block procedure for x86_64.
+#
+# 40% improvement over compiler-generated code on Opteron. On EM64T
+# sha256 was observed to run >80% faster and sha512 - >40%. No magical
+# tricks, just straight implementation... I really wonder why gcc
+# [being armed with inline assembler] fails to generate as fast code.
+# The only thing which is cool about this module is that it's very
+# same instruction sequence used for both SHA-256 and SHA-512. In
+# former case the instructions operate on 32-bit operands, while in
+# latter - on 64-bit ones. All I had to do is to get one flavor right,
+# the other one passed the test right away:-)
+#
+# sha256_block runs in ~1005 cycles on Opteron, which gives you
+# asymptotic performance of 64*1000/1005=63.7MBps times CPU clock
+# frequency in GHz. sha512_block runs in ~1275 cycles, which results
+# in 128*1000/1275=100MBps per GHz. Is there room for improvement?
+# Well, if you compare it to IA-64 implementation, which maintains
+# X[16] in register bank[!], tends to 4 instructions per CPU clock
+# cycle and runs in 1003 cycles, 1275 is very good result for 3-way
+# issue Opteron pipeline and X[16] maintained in memory. So that *if*
+# there is a way to improve it, *then* the only way would be to try to
+# offload X[16] updates to SSE unit, but that would require "deeper"
+# loop unroll, which in turn would naturally cause size blow-up, not
+# to mention increased complexity! And once again, only *if* it's
+# actually possible to noticeably improve overall ILP, instruction
+# level parallelism, on a given CPU implementation in this case.
+#
+# Special note on Intel EM64T. While Opteron CPU exhibits perfect
+# perfromance ratio of 1.5 between 64- and 32-bit flavors [see above],
+# [currently available] EM64T CPUs apparently are far from it. On the
+# contrary, 64-bit version, sha512_block, is ~30% *slower* than 32-bit
+# sha256_block:-( This is presumably because 64-bit shifts/rotates
+# apparently are not atomic instructions, but implemented in microcode.
+#
+# May 2012.
+#
+# Optimization including one of Pavel Semjanov's ideas, alternative
+# Maj, resulted in >=5% improvement on most CPUs, +20% SHA256 and
+# unfortunately -2% SHA512 on P4 [which nobody should care about
+# that much].
+#
+# June 2012.
+#
+# Add SIMD code paths, see below for improvement coefficients. SSSE3
+# code path was not attempted for SHA512, because improvement is not
+# estimated to be high enough, noticeably less than 9%, to justify
+# the effort, not on pre-AVX processors. [Obviously with exclusion
+# for VIA Nano, but it has SHA512 instruction that is faster and
+# should be used instead.] For reference, corresponding estimated
+# upper limit for improvement for SSSE3 SHA256 is 28%. The fact that
+# higher coefficients are observed on VIA Nano and Bulldozer has more
+# to do with specifics of their architecture [which is topic for
+# separate discussion].
+#
+# November 2012.
+#
+# Add AVX2 code path. Two consecutive input blocks are loaded to
+# 256-bit %ymm registers, with data from first block to least
+# significant 128-bit halves and data from second to most significant.
+# The data is then processed with same SIMD instruction sequence as
+# for AVX, but with %ymm as operands. Side effect is increased stack
+# frame, 448 additional bytes in SHA256 and 1152 in SHA512.
+
+######################################################################
+# Current performance in cycles per processed byte (less is better):
+#
+#		SHA256	SSSE3       AVX/XOP(*)	    SHA512  AVX/XOP(*)
+#
+# AMD K8	14.9	-	    -		    9.57    -
+# P4		17.3	-	    -		    30.8    -
+# Core 2	15.6	13.8(+13%)  -		    9.97    -
+# Westmere	14.8	12.3(+19%)  -		    9.58    -
+# Sandy Bridge	17.4	14.2(+23%)  11.6(+50%(**))  11.2    8.10(+38%(**))
+# Ivy Bridge	12.6	10.5(+20%)  10.3(+22%)	    8.17    7.22(+13%)
+# Haswell	12.2	9.28(+31%)  7.80(+56%)	    7.66    5.40(+42%)
+# Bulldozer	21.1	13.6(+54%)  13.6(+54%(***)) 13.5    8.58(+57%)
+# VIA Nano	23.0	16.5(+39%)  -		    14.7    -
+# Atom		23.0	18.9(+22%)  -		    14.7    -
+#
+# (*)	whichever best applicable;
+# (**)	switch from ror to shrd stands for fair share of improvement;
+# (***)	execution time is fully determined by remaining integer-only
+#	part, body_00_15; reducing the amount of SIMD instructions
+#	below certain limit makes no difference/sense; to conserve
+#	space SHA256 XOP code path is therefore omitted;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+open OUT,"| \"$^X\" $xlate $flavour";
+*STDOUT=*OUT;
+
+if ($output =~ /512/) {
+	$func="sha512_block_data_order";
+	$TABLE="K512";
+	$SZ=8;
+	@ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%rax","%rbx","%rcx","%rdx",
+					"%r8", "%r9", "%r10","%r11");
+	($T1,$a0,$a1,$a2,$a3)=("%r12","%r13","%r14","%r15","%rdi");
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=(1,  8, 7);
+	@sigma1=(19,61, 6);
+	$rounds=80;
+} else {
+	$func="sha256_block_data_order";
+	$TABLE="K256";
+	$SZ=4;
+	@ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%eax","%ebx","%ecx","%edx",
+					"%r8d","%r9d","%r10d","%r11d");
+	($T1,$a0,$a1,$a2,$a3)=("%r12d","%r13d","%r14d","%r15d","%edi");
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+}
+
+$ctx="%rdi";	# 1st arg, zapped by $a3
+$inp="%rsi";	# 2nd arg
+$Tbl="%rbp";
+
+$_ctx="16*$SZ+0*8(%rsp)";
+$_inp="16*$SZ+1*8(%rsp)";
+$_end="16*$SZ+2*8(%rsp)";
+$_rsp="16*$SZ+3*8(%rsp)";
+$framesz="16*$SZ+4*8";
+
+
+sub ROUND_00_15()
+{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+  my $STRIDE=$SZ;
+     $STRIDE += 16 if ($i%(16/$SZ)==(16/$SZ-1));
+
+$code.=<<___;
+	ror	\$`$Sigma1[2]-$Sigma1[1]`,$a0
+	mov	$f,$a2
+
+	xor	$e,$a0
+	ror	\$`$Sigma0[2]-$Sigma0[1]`,$a1
+	xor	$g,$a2			# f^g
+
+	mov	$T1,`$SZ*($i&0xf)`(%rsp)
+	xor	$a,$a1
+	and	$e,$a2			# (f^g)&e
+
+	ror	\$`$Sigma1[1]-$Sigma1[0]`,$a0
+	add	$h,$T1			# T1+=h
+	xor	$g,$a2			# Ch(e,f,g)=((f^g)&e)^g
+
+	ror	\$`$Sigma0[1]-$Sigma0[0]`,$a1
+	xor	$e,$a0
+	add	$a2,$T1			# T1+=Ch(e,f,g)
+
+	mov	$a,$a2
+	add	($Tbl),$T1		# T1+=K[round]
+	xor	$a,$a1
+
+	xor	$b,$a2			# a^b, b^c in next round
+	ror	\$$Sigma1[0],$a0	# Sigma1(e)
+	mov	$b,$h
+
+	and	$a2,$a3
+	ror	\$$Sigma0[0],$a1	# Sigma0(a)
+	add	$a0,$T1			# T1+=Sigma1(e)
+
+	xor	$a3,$h			# h=Maj(a,b,c)=Ch(a^b,c,b)
+	add	$T1,$d			# d+=T1
+	add	$T1,$h			# h+=T1
+
+	lea	$STRIDE($Tbl),$Tbl	# round++
+___
+$code.=<<___ if ($i<15);
+	add	$a1,$h			# h+=Sigma0(a)
+___
+	($a2,$a3) = ($a3,$a2);
+}
+
+sub ROUND_16_XX()
+{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___;
+	mov	`$SZ*(($i+1)&0xf)`(%rsp),$a0
+	mov	`$SZ*(($i+14)&0xf)`(%rsp),$a2
+
+	mov	$a0,$T1
+	ror	\$`$sigma0[1]-$sigma0[0]`,$a0
+	add	$a1,$a			# modulo-scheduled h+=Sigma0(a)
+	mov	$a2,$a1
+	ror	\$`$sigma1[1]-$sigma1[0]`,$a2
+
+	xor	$T1,$a0
+	shr	\$$sigma0[2],$T1
+	ror	\$$sigma0[0],$a0
+	xor	$a1,$a2
+	shr	\$$sigma1[2],$a1
+
+	ror	\$$sigma1[0],$a2
+	xor	$a0,$T1			# sigma0(X[(i+1)&0xf])
+	xor	$a1,$a2			# sigma1(X[(i+14)&0xf])
+	add	`$SZ*(($i+9)&0xf)`(%rsp),$T1
+
+	add	`$SZ*($i&0xf)`(%rsp),$T1
+	mov	$e,$a0
+	add	$a2,$T1
+	mov	$a,$a1
+___
+	&ROUND_00_15(@_);
+}
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+.globl	$func
+.type	$func,\@function,3
+.align	16
+$func:
+___
+$code.=<<___ if ($SZ==4 || $avx);
+	lea	OPENSSL_ia32cap_P(%rip),%r11
+	mov	0(%r11),%r9d
+	mov	4(%r11),%r10d
+	mov	8(%r11),%r11d
+___
+$code.=<<___ if ($avx && $SZ==8);
+	test	\$`1<<11`,%r10d		# check for XOP
+	jnz	.Lxop_shortcut
+___
+$code.=<<___ if ($avx>1);
+	and	\$`1<<8|1<<5|1<<3`,%r11d	# check for BMI2+AVX2+BMI1
+	cmp	\$`1<<8|1<<5|1<<3`,%r11d
+	je	.Lavx2_shortcut
+___
+$code.=<<___ if ($avx);
+	and	\$`1<<30`,%r9d		# mask "Intel CPU" bit
+	and	\$`1<<28|1<<9`,%r10d	# mask AVX and SSSE3 bits
+	or	%r9d,%r10d
+	cmp	\$`1<<28|1<<9|1<<30`,%r10d
+	je	.Lavx_shortcut
+___
+$code.=<<___ if ($SZ==4);
+	test	\$`1<<9`,%r10d
+	jnz	.Lssse3_shortcut
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$$framesz,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+.Lprologue:
+
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+	jmp	.Lloop
+
+.align	16
+.Lloop:
+	mov	$B,$a3
+	lea	$TABLE(%rip),$Tbl
+	xor	$C,$a3			# magic
+___
+	for($i=0;$i<16;$i++) {
+		$code.="	mov	$SZ*$i($inp),$T1\n";
+		$code.="	mov	@ROT[4],$a0\n";
+		$code.="	mov	@ROT[0],$a1\n";
+		$code.="	bswap	$T1\n";
+		&ROUND_00_15($i,@ROT);
+		unshift(@ROT,pop(@ROT));
+	}
+$code.=<<___;
+	jmp	.Lrounds_16_xx
+.align	16
+.Lrounds_16_xx:
+___
+	for(;$i<32;$i++) {
+		&ROUND_16_XX($i,@ROT);
+		unshift(@ROT,pop(@ROT));
+	}
+
+$code.=<<___;
+	cmpb	\$0,`$SZ-1`($Tbl)
+	jnz	.Lrounds_16_xx
+
+	mov	$_ctx,$ctx
+	add	$a1,$A			# modulo-scheduled h+=Sigma0(a)
+	lea	16*$SZ($inp),$inp
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop
+
+	mov	$_rsp,%rsi
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue:
+	ret
+.size	$func,.-$func
+___
+
+if ($SZ==4) {
+$code.=<<___;
+.align	64
+.type	$TABLE,\@object
+$TABLE:
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
+	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
+	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
+	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
+	.asciz	"SHA256 block transform for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+___
+} else {
+$code.=<<___;
+.align	64
+.type	$TABLE,\@object
+$TABLE:
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+
+	.quad	0x0001020304050607,0x08090a0b0c0d0e0f
+	.quad	0x0001020304050607,0x08090a0b0c0d0e0f
+	.asciz	"SHA512 block transform for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
+___
+}
+
+######################################################################
+# SIMD code paths
+#
+{{{
+
+my $a4=$T1;
+my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+sub body_00_15 () {
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.
+
+	'&ror	($a0,$Sigma1[2]-$Sigma1[1])',
+	'&mov	($a,$a1)',
+	'&mov	($a4,$f)',
+
+	'&ror	($a1,$Sigma0[2]-$Sigma0[1])',
+	'&xor	($a0,$e)',
+	'&xor	($a4,$g)',			# f^g
+
+	'&ror	($a0,$Sigma1[1]-$Sigma1[0])',
+	'&xor	($a1,$a)',
+	'&and	($a4,$e)',			# (f^g)&e
+
+	'&xor	($a0,$e)',
+	'&add	($h,$SZ*($i&15)."(%rsp)")',	# h+=X[i]+K[i]
+	'&mov	($a2,$a)',
+
+	'&xor	($a4,$g)',			# Ch(e,f,g)=((f^g)&e)^g
+	'&ror	($a1,$Sigma0[1]-$Sigma0[0])',
+	'&xor	($a2,$b)',			# a^b, b^c in next round
+
+	'&add	($h,$a4)',			# h+=Ch(e,f,g)
+	'&ror	($a0,$Sigma1[0])',		# Sigma1(e)
+	'&and	($a3,$a2)',			# (b^c)&(a^b)
+
+	'&xor	($a1,$a)',
+	'&add	($h,$a0)',			# h+=Sigma1(e)
+	'&xor	($a3,$b)',			# Maj(a,b,c)=Ch(a^b,c,b)
+
+	'&ror	($a1,$Sigma0[0])',		# Sigma0(a)
+	'&add	($d,$h)',			# d+=h
+	'&add	($h,$a3)',			# h+=Maj(a,b,c)
+
+	'&mov	($a0,$d)',
+	'&add	($a1,$h);'.			# h+=Sigma0(a)
+	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
+	);
+}
+
+######################################################################
+# SSSE3 code path
+#
+if ($SZ==4) {	# SHA256 only
+my @X = map("%xmm$_",(0..3));
+my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%xmm$_",(4..9));
+
+$code.=<<___;
+.type	${func}_ssse3,\@function,3
+.align	64
+${func}_ssse3:
+.Lssse3_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$`$framesz+$win64*16*4`,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___;
+.Lprologue_ssse3:
+
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+___
+
+$code.=<<___;
+	#movdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
+	#movdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
+	jmp	.Lloop_ssse3
+.align	16
+.Lloop_ssse3:
+	movdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	movdqu	0x00($inp),@X[0]
+	movdqu	0x10($inp),@X[1]
+	movdqu	0x20($inp),@X[2]
+	movdqu	0x30($inp),@X[3]
+	pshufb	$t3,@X[0]
+	lea	$TABLE(%rip),$Tbl
+	pshufb	$t3,@X[1]
+	movdqa	0x00($Tbl),$t0
+	pshufb	$t3,@X[2]
+	movdqa	0x20($Tbl),$t1
+	paddd	@X[0],$t0
+	movdqa	0x40($Tbl),$t2
+	pshufb	$t3,@X[3]
+	movdqa	0x60($Tbl),$t3
+	paddd	@X[1],$t1
+	paddd	@X[2],$t2
+	paddd	@X[3],$t3
+	movdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	movdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	movdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	movdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lssse3_00_47
+
+.align	16
+.Lssse3_00_47:
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+___
+sub Xupdate_256_SSSE3 () {
+	(
+	'&movdqa	($t0,@X[1]);',
+	'&movdqa	($t3,@X[3])',
+	'&palignr	($t0,@X[0],$SZ)',	# X[1..4]
+	 '&palignr	($t3,@X[2],$SZ);',	# X[9..12]
+	'&movdqa	($t1,$t0)',
+	'&movdqa	($t2,$t0);',
+	'&psrld		($t0,$sigma0[2])',
+	 '&paddd	(@X[0],$t3);',		# X[0..3] += X[9..12]
+	'&psrld		($t2,$sigma0[0])',
+	 '&pshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&pslld		($t1,8*$SZ-$sigma0[1]);'.
+	'&pxor		($t0,$t2)',
+	'&psrld		($t2,$sigma0[1]-$sigma0[0]);'.
+	'&pxor		($t0,$t1)',
+	'&pslld		($t1,$sigma0[1]-$sigma0[0]);'.
+	'&pxor		($t0,$t2);',
+	 '&movdqa	($t2,$t3)',
+	'&pxor		($t0,$t1);',		# sigma0(X[1..4])
+	 '&psrld	($t3,$sigma1[2])',
+	'&paddd		(@X[0],$t0);',		# X[0..3] += sigma0(X[1..4])
+	 '&psrlq	($t2,$sigma1[0])',
+	 '&pxor		($t3,$t2);',
+	 '&psrlq	($t2,$sigma1[1]-$sigma1[0])',
+	 '&pxor		($t3,$t2)',
+	 '&pshufb	($t3,$t4)',		# sigma1(X[14..15])
+	'&paddd		(@X[0],$t3)',		# X[0..1] += sigma1(X[14..15])
+	 '&pshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&movdqa	($t2,$t3);',
+	 '&psrld	($t3,$sigma1[2])',
+	 '&psrlq	($t2,$sigma1[0])',
+	 '&pxor		($t3,$t2);',
+	 '&psrlq	($t2,$sigma1[1]-$sigma1[0])',
+	 '&pxor		($t3,$t2);',
+	'&movdqa	($t2,16*2*$j."($Tbl)")',
+	 '&pshufb	($t3,$t5)',
+	'&paddd		(@X[0],$t3)'		# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+sub SSSE3_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+    if (0) {
+	foreach (Xupdate_256_SSSE3()) {		# 36 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+    } else {			# squeeze extra 4% on Westmere and 19% on Atom
+	  eval(shift(@insns));	#@
+	&movdqa		($t0,@X[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t3,@X[3]);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	&palignr	($t0,@X[0],$SZ);	# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &palignr	($t3,@X[2],$SZ);	# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&movdqa		($t1,$t0);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t2,$t0);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	&psrld		($t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &paddd		(@X[0],$t3);		# X[0..3] += X[9..12]
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	&psrld		($t2,$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[3],0b11111010);	# X[4..15]
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&pslld		($t1,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&psrld		($t2,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	&pxor		($t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pslld		($t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pxor		($t0,$t1);		# sigma0(X[1..4])
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrld		($t3,$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		(@X[0],$t0);		# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &psrlq		($t2,$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 &psrlq		($t2,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 #&pshufb	($t3,$t4);		# sigma1(X[14..15])
+	 &pshufd	($t3,$t3,0b10000000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&paddd		(@X[0],$t3);		# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[0],0b01010000);	# X[16..17]
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrld		($t3,$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 &psrlq		($t2,$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &psrlq		($t2,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 #&pshufb	($t3,$t5);
+	 &pshufd	($t3,$t3,0b00001000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t2,16*2*$j."($Tbl)");
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &pslldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		(@X[0],$t3);		# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+    }
+	&paddd		($t2,@X[0]);
+	  foreach (@insns) { eval; }		# remaining instructions
+	&movdqa		(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&SSSE3_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lssse3_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+$code.=<<___;
+	mov	$_ctx,$ctx
+	mov	$a1,$A
+
+	add	$SZ*0($ctx),$A
+	lea	16*$SZ($inp),$inp
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop_ssse3
+
+	mov	$_rsp,%rsi
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_ssse3:
+	ret
+.size	${func}_ssse3,.-${func}_ssse3
+___
+}
+
+if ($avx) {{
+######################################################################
+# XOP code path
+#
+if ($SZ==8) {	# SHA512 only
+$code.=<<___;
+.type	${func}_xop,\@function,3
+.align	64
+${func}_xop:
+.Lxop_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$`$framesz+$win64*16*($SZ==4?4:6)`,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	%xmm10,16*$SZ+96(%rsp)
+	movaps	%xmm11,16*$SZ+112(%rsp)
+___
+$code.=<<___;
+.Lprologue_xop:
+
+	vzeroupper
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+	jmp	.Lloop_xop
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%xmm$_",(0..3));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(4..7));
+
+$code.=<<___;
+.align	16
+.Lloop_xop:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[0],@X[0]
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[1],@X[1]
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lxop_00_47
+
+.align	16
+.Lxop_00_47:
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+___
+sub XOP_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+	&vpalignr	($t0,@X[1],@X[0],$SZ);	# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpalignr	($t3,@X[3],@X[2],$SZ);	# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotd		($t1,$t0,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrld		($t0,$t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpaddd	(@X[0],@X[0],$t3);	# X[0..3] += X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotd		($t2,$t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t3,@X[3],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t2);		# sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrld	($t2,@X[3],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t0);	# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrldq	($t3,$t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t3);	# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t3,@X[0],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrld	($t2,@X[0],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpslldq	($t3,$t3,8);		# 22 instructions
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t3);	# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&XOP_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lxop_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+					} else {	# SHA512
+    my @X = map("%xmm$_",(0..7));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(8..11));
+
+$code.=<<___;
+.align	16
+.Lloop_xop:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vpshufb	$t3,@X[0],@X[0]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[1],@X[1]
+	vmovdqu	0x40($inp),@X[4]
+	vpshufb	$t3,@X[2],@X[2]
+	vmovdqu	0x50($inp),@X[5]
+	vpshufb	$t3,@X[3],@X[3]
+	vmovdqu	0x60($inp),@X[6]
+	vpshufb	$t3,@X[4],@X[4]
+	vmovdqu	0x70($inp),@X[7]
+	vpshufb	$t3,@X[5],@X[5]
+	vpaddq	-0x80($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[6],@X[6]
+	vpaddq	-0x60($Tbl),@X[1],$t1
+	vpshufb	$t3,@X[7],@X[7]
+	vpaddq	-0x40($Tbl),@X[2],$t2
+	vpaddq	-0x20($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	vpaddq	0x00($Tbl),@X[4],$t0
+	vmovdqa	$t1,0x10(%rsp)
+	vpaddq	0x20($Tbl),@X[5],$t1
+	vmovdqa	$t2,0x20(%rsp)
+	vpaddq	0x40($Tbl),@X[6],$t2
+	vmovdqa	$t3,0x30(%rsp)
+	vpaddq	0x60($Tbl),@X[7],$t3
+	vmovdqa	$t0,0x40(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x50(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x60(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x70(%rsp)
+	mov	$E,$a0
+	jmp	.Lxop_00_47
+
+.align	16
+.Lxop_00_47:
+	add	\$16*2*$SZ,$Tbl
+___
+sub XOP_512_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body);			# 52 instructions
+
+	&vpalignr	($t0,@X[1],@X[0],$SZ);	# X[1..2]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpalignr	($t3,@X[5],@X[4],$SZ);	# X[9..10]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotq		($t1,$t0,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrlq		($t0,$t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpaddq	(@X[0],@X[0],$t3);	# X[0..1] += X[9..10]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotq		($t2,$t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotq	($t3,@X[7],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t2);		# sigma0(X[1..2])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrlq	($t2,@X[7],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddq		(@X[0],@X[0],$t0);	# X[0..1] += sigma0(X[1..2])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotq	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddq		(@X[0],@X[0],$t3);	# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<8; $j++) {
+	&XOP_512_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ-0x80."($Tbl)",0);
+	&jne	(".Lxop_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+}
+$code.=<<___;
+	mov	$_ctx,$ctx
+	mov	$a1,$A
+
+	add	$SZ*0($ctx),$A
+	lea	16*$SZ($inp),$inp
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop_xop
+
+	mov	$_rsp,%rsi
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	16*$SZ+96(%rsp),%xmm10
+	movaps	16*$SZ+112(%rsp),%xmm11
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_xop:
+	ret
+.size	${func}_xop,.-${func}_xop
+___
+}
+######################################################################
+# AVX+shrd code path
+#
+local *ror = sub { &shrd(@_[0],@_) };
+
+$code.=<<___;
+.type	${func}_avx,\@function,3
+.align	64
+${func}_avx:
+.Lavx_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$`$framesz+$win64*16*($SZ==4?4:6)`,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	%xmm10,16*$SZ+96(%rsp)
+	movaps	%xmm11,16*$SZ+112(%rsp)
+___
+$code.=<<___;
+.Lprologue_avx:
+
+	vzeroupper
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%xmm$_",(0..3));
+    my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%xmm$_",(4..9));
+
+$code.=<<___;
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
+	jmp	.Lloop_avx
+.align	16
+.Lloop_avx:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[0],@X[0]
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[1],@X[1]
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lavx_00_47
+
+.align	16
+.Lavx_00_47:
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+___
+sub Xupdate_256_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..4]
+	 '&vpalignr	($t3,@X[3],@X[2],$SZ)',	# X[9..12]
+	'&vpsrld	($t2,$t0,$sigma0[0]);',
+	 '&vpaddd	(@X[0],@X[0],$t3)',	# X[0..3] += X[9..12]
+	'&vpsrld	($t3,$t0,$sigma0[2])',
+	'&vpslld	($t1,$t0,8*$SZ-$sigma0[1]);',
+	'&vpxor		($t0,$t3,$t2)',
+	 '&vpshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&vpsrld	($t2,$t2,$sigma0[1]-$sigma0[0]);',
+	'&vpxor		($t0,$t0,$t1)',
+	'&vpslld	($t1,$t1,$sigma0[1]-$sigma0[0]);',
+	'&vpxor		($t0,$t0,$t2)',
+	 '&vpsrld	($t2,$t3,$sigma1[2]);',
+	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..4])
+	 '&vpsrlq	($t3,$t3,$sigma1[0]);',
+	'&vpaddd	(@X[0],@X[0],$t0)',	# X[0..3] += sigma0(X[1..4])
+	 '&vpxor	($t2,$t2,$t3);',
+	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3)',
+	 '&vpshufb	($t2,$t2,$t4)',		# sigma1(X[14..15])
+	'&vpaddd	(@X[0],@X[0],$t2)',	# X[0..1] += sigma1(X[14..15])
+	 '&vpshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&vpsrld	($t2,$t3,$sigma1[2])',
+	 '&vpsrlq	($t3,$t3,$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3);',
+	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3)',
+	 '&vpshufb	($t2,$t2,$t5)',
+	'&vpaddd	(@X[0],@X[0],$t2)'	# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+sub AVX_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+	foreach (Xupdate_256_AVX()) {		# 29 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lavx_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+					} else {	# SHA512
+    my @X = map("%xmm$_",(0..7));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(8..11));
+
+$code.=<<___;
+	jmp	.Lloop_avx
+.align	16
+.Lloop_avx:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vpshufb	$t3,@X[0],@X[0]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[1],@X[1]
+	vmovdqu	0x40($inp),@X[4]
+	vpshufb	$t3,@X[2],@X[2]
+	vmovdqu	0x50($inp),@X[5]
+	vpshufb	$t3,@X[3],@X[3]
+	vmovdqu	0x60($inp),@X[6]
+	vpshufb	$t3,@X[4],@X[4]
+	vmovdqu	0x70($inp),@X[7]
+	vpshufb	$t3,@X[5],@X[5]
+	vpaddq	-0x80($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[6],@X[6]
+	vpaddq	-0x60($Tbl),@X[1],$t1
+	vpshufb	$t3,@X[7],@X[7]
+	vpaddq	-0x40($Tbl),@X[2],$t2
+	vpaddq	-0x20($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	vpaddq	0x00($Tbl),@X[4],$t0
+	vmovdqa	$t1,0x10(%rsp)
+	vpaddq	0x20($Tbl),@X[5],$t1
+	vmovdqa	$t2,0x20(%rsp)
+	vpaddq	0x40($Tbl),@X[6],$t2
+	vmovdqa	$t3,0x30(%rsp)
+	vpaddq	0x60($Tbl),@X[7],$t3
+	vmovdqa	$t0,0x40(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x50(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x60(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x70(%rsp)
+	mov	$E,$a0
+	jmp	.Lavx_00_47
+
+.align	16
+.Lavx_00_47:
+	add	\$16*2*$SZ,$Tbl
+___
+sub Xupdate_512_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..2]
+	 '&vpalignr	($t3,@X[5],@X[4],$SZ)',	# X[9..10]
+	'&vpsrlq	($t2,$t0,$sigma0[0])',
+	 '&vpaddq	(@X[0],@X[0],$t3);',	# X[0..1] += X[9..10]
+	'&vpsrlq	($t3,$t0,$sigma0[2])',
+	'&vpsllq	($t1,$t0,8*$SZ-$sigma0[1]);',
+	 '&vpxor	($t0,$t3,$t2)',
+	'&vpsrlq	($t2,$t2,$sigma0[1]-$sigma0[0]);',
+	 '&vpxor	($t0,$t0,$t1)',
+	'&vpsllq	($t1,$t1,$sigma0[1]-$sigma0[0]);',
+	 '&vpxor	($t0,$t0,$t2)',
+	 '&vpsrlq	($t3,@X[7],$sigma1[2]);',
+	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..2])
+	 '&vpsllq	($t2,@X[7],8*$SZ-$sigma1[1]);',
+	'&vpaddq	(@X[0],@X[0],$t0)',	# X[0..1] += sigma0(X[1..2])
+	 '&vpsrlq	($t1,@X[7],$sigma1[0]);',
+	 '&vpxor	($t3,$t3,$t2)',
+	 '&vpsllq	($t2,$t2,$sigma1[1]-$sigma1[0]);',
+	 '&vpxor	($t3,$t3,$t1)',
+	 '&vpsrlq	($t1,$t1,$sigma1[1]-$sigma1[0]);',
+	 '&vpxor	($t3,$t3,$t2)',
+	 '&vpxor	($t3,$t3,$t1)',		# sigma1(X[14..15])
+	'&vpaddq	(@X[0],@X[0],$t3)',	# X[0..1] += sigma1(X[14..15])
+	);
+}
+
+sub AVX_512_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body);			# 52 instructions
+
+	foreach (Xupdate_512_AVX()) {		# 23 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<8; $j++) {
+	&AVX_512_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ-0x80."($Tbl)",0);
+	&jne	(".Lavx_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+}
+$code.=<<___;
+	mov	$_ctx,$ctx
+	mov	$a1,$A
+
+	add	$SZ*0($ctx),$A
+	lea	16*$SZ($inp),$inp
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop_avx
+
+	mov	$_rsp,%rsi
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	16*$SZ+96(%rsp),%xmm10
+	movaps	16*$SZ+112(%rsp),%xmm11
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx:
+	ret
+.size	${func}_avx,.-${func}_avx
+___
+
+if ($avx>1) {{
+######################################################################
+# AVX2+BMI code path
+#
+my $a5=$SZ==4?"%esi":"%rsi";	# zap $inp 
+my $PUSH8=8*2*$SZ;
+use integer;
+
+sub bodyx_00_15 () {
+	# at start $a1 should be zero, $a3 - $b^$c and $a4 copy of $f
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.
+
+	'&add	($h,(32*($i/(16/$SZ))+$SZ*($i%(16/$SZ)))%$PUSH8.$base)',    # h+=X[i]+K[i]
+	'&and	($a4,$e)',		# f&e
+	'&rorx	($a0,$e,$Sigma1[2])',
+	'&rorx	($a2,$e,$Sigma1[1])',
+
+	'&lea	($a,"($a,$a1)")',	# h+=Sigma0(a) from the past
+	'&lea	($h,"($h,$a4)")',
+	'&andn	($a4,$e,$g)',		# ~e&g
+	'&xor	($a0,$a2)',
+
+	'&rorx	($a1,$e,$Sigma1[0])',
+	'&lea	($h,"($h,$a4)")',	# h+=Ch(e,f,g)=(e&f)+(~e&g)
+	'&xor	($a0,$a1)',		# Sigma1(e)
+	'&mov	($a2,$a)',
+
+	'&rorx	($a4,$a,$Sigma0[2])',
+	'&lea	($h,"($h,$a0)")',	# h+=Sigma1(e)
+	'&xor	($a2,$b)',		# a^b, b^c in next round
+	'&rorx	($a1,$a,$Sigma0[1])',
+
+	'&rorx	($a0,$a,$Sigma0[0])',
+	'&lea	($d,"($d,$h)")',	# d+=h
+	'&and	($a3,$a2)',		# (b^c)&(a^b)
+	'&xor	($a1,$a4)',
+
+	'&xor	($a3,$b)',		# Maj(a,b,c)=Ch(a^b,c,b)
+	'&xor	($a1,$a0)',		# Sigma0(a)
+	'&lea	($h,"($h,$a3)");'.	# h+=Maj(a,b,c)
+	'&mov	($a4,$e)',		# copy of f in future
+
+	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
+	);
+	# and at the finish one has to $a+=$a1
+}
+
+$code.=<<___;
+.type	${func}_avx2,\@function,3
+.align	64
+${func}_avx2:
+.Lavx2_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	sub	\$`2*$SZ*$rounds+4*8+$win64*16*($SZ==4?4:6)`,%rsp
+	shl	\$4,%rdx		# num*16
+	and	\$-256*$SZ,%rsp		# align stack frame
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	add	\$`2*$SZ*($rounds-8)`,%rsp
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	%xmm10,16*$SZ+96(%rsp)
+	movaps	%xmm11,16*$SZ+112(%rsp)
+___
+$code.=<<___;
+.Lprologue_avx2:
+
+	vzeroupper
+	sub	\$-16*$SZ,$inp		# inp++, size optimization
+	mov	$SZ*0($ctx),$A
+	mov	$inp,%r12		# borrow $T1
+	mov	$SZ*1($ctx),$B
+	cmp	%rdx,$inp		# $_end
+	mov	$SZ*2($ctx),$C
+	cmove	%rsp,%r12		# next block or random data
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%ymm$_",(0..3));
+    my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%ymm$_",(4..9));
+
+$code.=<<___;
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
+	jmp	.Loop_avx2
+.align	16
+.Loop_avx2:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	-16*$SZ+0($inp),%xmm0
+	vmovdqu	-16*$SZ+16($inp),%xmm1
+	vmovdqu	-16*$SZ+32($inp),%xmm2
+	vmovdqu	-16*$SZ+48($inp),%xmm3
+	#mov		$inp,$_inp	# offload $inp
+	vinserti128	\$1,(%r12),@X[0],@X[0]
+	vinserti128	\$1,16(%r12),@X[1],@X[1]
+	vpshufb		$t3,@X[0],@X[0]
+	vinserti128	\$1,32(%r12),@X[2],@X[2]
+	vpshufb		$t3,@X[1],@X[1]
+	vinserti128	\$1,48(%r12),@X[3],@X[3]
+
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	xor	$a1,$a1
+	vmovdqa	$t1,0x20(%rsp)
+	lea	-$PUSH8(%rsp),%rsp
+	mov	$B,$a3
+	vmovdqa	$t2,0x00(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x20(%rsp)
+	mov	$F,$a4
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+	jmp	.Lavx2_00_47
+
+.align	16
+.Lavx2_00_47:
+___
+
+sub AVX2_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 96 instructions
+my $base = "+2*$PUSH8(%rsp)";
+
+	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%2)==0);
+	foreach (Xupdate_256_AVX()) {		# 29 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX2_256_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&lea	($Tbl,16*2*$SZ."($Tbl)");
+	&cmpb	(($SZ-1)."($Tbl)",0);
+	&jne	(".Lavx2_00_47");
+
+    for ($i=0; $i<16; ) {
+	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+					} else {	# SHA512
+    my @X = map("%ymm$_",(0..7));
+    my ($t0,$t1,$t2,$t3) = map("%ymm$_",(8..11));
+
+$code.=<<___;
+	jmp	.Loop_avx2
+.align	16
+.Loop_avx2:
+	vmovdqu	-16*$SZ($inp),%xmm0
+	vmovdqu	-16*$SZ+16($inp),%xmm1
+	vmovdqu	-16*$SZ+32($inp),%xmm2
+	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
+	vmovdqu	-16*$SZ+48($inp),%xmm3
+	vmovdqu	-16*$SZ+64($inp),%xmm4
+	vmovdqu	-16*$SZ+80($inp),%xmm5
+	vmovdqu	-16*$SZ+96($inp),%xmm6
+	vmovdqu	-16*$SZ+112($inp),%xmm7
+	#mov	$inp,$_inp	# offload $inp
+	vmovdqa	`$SZ*2*$rounds-0x80`($Tbl),$t2
+	vinserti128	\$1,(%r12),@X[0],@X[0]
+	vinserti128	\$1,16(%r12),@X[1],@X[1]
+	 vpshufb	$t2,@X[0],@X[0]
+	vinserti128	\$1,32(%r12),@X[2],@X[2]
+	 vpshufb	$t2,@X[1],@X[1]
+	vinserti128	\$1,48(%r12),@X[3],@X[3]
+	 vpshufb	$t2,@X[2],@X[2]
+	vinserti128	\$1,64(%r12),@X[4],@X[4]
+	 vpshufb	$t2,@X[3],@X[3]
+	vinserti128	\$1,80(%r12),@X[5],@X[5]
+	 vpshufb	$t2,@X[4],@X[4]
+	vinserti128	\$1,96(%r12),@X[6],@X[6]
+	 vpshufb	$t2,@X[5],@X[5]
+	vinserti128	\$1,112(%r12),@X[7],@X[7]
+
+	vpaddq	-0x80($Tbl),@X[0],$t0
+	vpshufb	$t2,@X[6],@X[6]
+	vpaddq	-0x60($Tbl),@X[1],$t1
+	vpshufb	$t2,@X[7],@X[7]
+	vpaddq	-0x40($Tbl),@X[2],$t2
+	vpaddq	-0x20($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	vpaddq	0x00($Tbl),@X[4],$t0
+	vmovdqa	$t1,0x20(%rsp)
+	vpaddq	0x20($Tbl),@X[5],$t1
+	vmovdqa	$t2,0x40(%rsp)
+	vpaddq	0x40($Tbl),@X[6],$t2
+	vmovdqa	$t3,0x60(%rsp)
+	lea	-$PUSH8(%rsp),%rsp
+	vpaddq	0x60($Tbl),@X[7],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	xor	$a1,$a1
+	vmovdqa	$t1,0x20(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x40(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x60(%rsp)
+	mov	$F,$a4
+	add	\$16*2*$SZ,$Tbl
+	jmp	.Lavx2_00_47
+
+.align	16
+.Lavx2_00_47:
+___
+
+sub AVX2_512_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body);			# 48 instructions
+my $base = "+2*$PUSH8(%rsp)";
+
+	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%4)==0);
+	foreach (Xupdate_512_AVX()) {		# 23 instructions
+	    eval;
+	    if ($_ !~ /\;$/) {
+		eval(shift(@insns));
+		eval(shift(@insns));
+		eval(shift(@insns));
+	    }
+	}
+	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<8; $j++) {
+	&AVX2_512_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&lea	($Tbl,16*2*$SZ."($Tbl)");
+	&cmpb	(($SZ-1-0x80)."($Tbl)",0);
+	&jne	(".Lavx2_00_47");
+
+    for ($i=0; $i<16; ) {
+	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+}
+$code.=<<___;
+	mov	`2*$SZ*$rounds`(%rsp),$ctx	# $_ctx
+	add	$a1,$A
+	#mov	`2*$SZ*$rounds+8`(%rsp),$inp	# $_inp
+	lea	`2*$SZ*($rounds-8)`(%rsp),$Tbl
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+
+	cmp	`$PUSH8+2*8`($Tbl),$inp	# $_end
+	je	.Ldone_avx2
+
+	xor	$a1,$a1
+	mov	$B,$a3
+	xor	$C,$a3			# magic
+	mov	$F,$a4
+	jmp	.Lower_avx2
+.align	16
+.Lower_avx2:
+___
+    for ($i=0; $i<8; ) {
+	my $base="+16($Tbl)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+$code.=<<___;
+	lea	-$PUSH8($Tbl),$Tbl
+	cmp	%rsp,$Tbl
+	jae	.Lower_avx2
+
+	mov	`2*$SZ*$rounds`(%rsp),$ctx	# $_ctx
+	add	$a1,$A
+	#mov	`2*$SZ*$rounds+8`(%rsp),$inp	# $_inp
+	lea	`2*$SZ*($rounds-8)`(%rsp),%rsp
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	lea	`2*16*$SZ`($inp),$inp	# inp+=2
+	add	$SZ*6($ctx),$G
+	mov	$inp,%r12
+	add	$SZ*7($ctx),$H
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	cmove	%rsp,%r12		# next block or stale data
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+
+	jbe	.Loop_avx2
+	lea	(%rsp),$Tbl
+
+.Ldone_avx2:
+	lea	($Tbl),%rsp
+	mov	$_rsp,%rsi
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	16*$SZ+96(%rsp),%xmm10
+	movaps	16*$SZ+112(%rsp),%xmm11
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx2:
+	ret
+.size	${func}_avx2,.-${func}_avx2
+___
+}}
+}}}}}
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HanderlData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<prologue label
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+___
+$code.=<<___ if ($avx>1);
+	lea	.Lavx2_shortcut(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<avx2_shortcut
+	jb	.Lnot_in_avx2
+
+	and	\$-256*$SZ,%rax
+	add	\$`2*$SZ*($rounds-8)`,%rax
+.Lnot_in_avx2:
+___
+$code.=<<___;
+	mov	%rax,%rsi		# put aside Rsp
+	mov	16*$SZ+3*8(%rax),%rax	# pull $_rsp
+	lea	48(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx
+	jb	.Lin_prologue		# non-AVX code
+
+	lea	16*$SZ+4*8(%rsi),%rsi	# Xmm6- save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$`$SZ==4?8:12`,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_$func
+	.rva	.LSEH_end_$func
+	.rva	.LSEH_info_$func
+___
+$code.=<<___ if ($SZ==4);
+	.rva	.LSEH_begin_${func}_ssse3
+	.rva	.LSEH_end_${func}_ssse3
+	.rva	.LSEH_info_${func}_ssse3
+___
+$code.=<<___ if ($avx && $SZ==8);
+	.rva	.LSEH_begin_${func}_xop
+	.rva	.LSEH_end_${func}_xop
+	.rva	.LSEH_info_${func}_xop
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_${func}_avx
+	.rva	.LSEH_end_${func}_avx
+	.rva	.LSEH_info_${func}_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_${func}_avx2
+	.rva	.LSEH_end_${func}_avx2
+	.rva	.LSEH_info_${func}_avx2
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_$func:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue,.Lepilogue			# HandlerData[]
+___
+$code.=<<___ if ($SZ==4);
+.LSEH_info_${func}_ssse3:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_ssse3,.Lepilogue_ssse3	# HandlerData[]
+___
+$code.=<<___ if ($avx && $SZ==8);
+.LSEH_info_${func}_xop:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_xop,.Lepilogue_xop		# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_${func}_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_${func}_avx2:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/sha/sha.h b/crypto/sha/sha.h
new file mode 100644
index 0000000..2eda284
--- /dev/null
+++ b/crypto/sha/sha.h
@@ -0,0 +1,237 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_SHA_H
+#define OPENSSL_HEADER_SHA_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* SHA-1. */
+
+
+/* SHA_CBLOCK is the block size of SHA-1. */
+#define SHA_CBLOCK 64
+
+/* SHA_DIGEST_LENGTH is the length of a SHA-1 digest. */
+#define SHA_DIGEST_LENGTH 20
+
+/* TODO(fork): remove */
+#define SHA_LBLOCK 16
+#define SHA_LONG uint32_t
+
+/* SHA1_Init initialises |sha| and returns one. */
+int SHA1_Init(SHA_CTX *sha);
+
+/* SHA1_Update adds |len| bytes from |data| to |sha| and returns one. */
+int SHA1_Update(SHA_CTX *sha, const void *data, size_t len);
+
+/* SHA1_Final adds the final padding to |sha| and writes the resulting digest
+ * to |md|, which must have at least |SHA_DIGEST_LENGTH| bytes of space. It
+ * returns one. */
+int SHA1_Final(uint8_t *md, SHA_CTX *sha);
+
+/* SHA1 writes the digest of |len| bytes from |data| to |out| and returns
+ * |out|. There must be at least |SHA_DIGEST_LENGTH| bytes of space in
+ * |out|. */
+uint8_t *SHA1(const uint8_t *data, size_t len, uint8_t *out);
+
+/* SHA1_Transform is a low-level function that performs a single, SHA-1 block
+ * transformation using the state from |sha| and 64 bytes from |block|. */
+void SHA1_Transform(SHA_CTX *sha, const uint8_t *block);
+
+struct sha_state_st {
+  uint32_t h0, h1, h2, h3, h4;
+  uint32_t Nl, Nh;
+  uint32_t data[16];
+  unsigned int num;
+};
+
+
+/* SHA-224. */
+
+/* SHA224_CBLOCK is the block size of SHA-224. */
+#define SHA224_CBLOCK 64
+
+/* SHA224_DIGEST_LENGTH is the length of a SHA-224 digest. */
+#define SHA224_DIGEST_LENGTH 28
+
+/* SHA224_Init initialises |sha| and returns 1. */
+int SHA224_Init(SHA256_CTX *sha);
+
+/* SHA224_Update adds |len| bytes from |data| to |sha|. */
+int SHA224_Update(SHA256_CTX *sha, const void *data, size_t len);
+
+/* SHA224_Final adds the final padding to |sha| and writes the resulting digest
+ * to |md|, which must have at least |SHA_DIGEST_LENGTH| bytes of space. */
+int SHA224_Final(uint8_t *md, SHA256_CTX *sha);
+
+/* SHA224 writes the digest of |len| bytes from |data| to |out| and returns
+ * |out|. There must be at least |SHA_DIGEST_LENGTH| bytes of space in
+ * |out|. */
+uint8_t *SHA224(const uint8_t *data, size_t len, uint8_t *out);
+
+
+/* SHA-256. */
+
+/* SHA256_CBLOCK is the block size of SHA-256. */
+#define SHA256_CBLOCK 64
+
+/* SHA256_DIGEST_LENGTH is the length of a SHA-256 digest. */
+#define SHA256_DIGEST_LENGTH 32
+
+/* SHA256_Init initialises |sha| and returns 1. */
+int SHA256_Init(SHA256_CTX *sha);
+
+/* SHA256_Update adds |len| bytes from |data| to |sha|. */
+int SHA256_Update(SHA256_CTX *sha, const void *data, size_t len);
+
+/* SHA256_Final adds the final padding to |sha| and writes the resulting digest
+ * to |md|, which must have at least |SHA_DIGEST_LENGTH| bytes of space. */
+int SHA256_Final(uint8_t *md, SHA256_CTX *sha);
+
+/* SHA256 writes the digest of |len| bytes from |data| to |out| and returns
+ * |out|. There must be at least |SHA_DIGEST_LENGTH| bytes of space in
+ * |out|. */
+uint8_t *SHA256(const uint8_t *data, size_t len ,uint8_t *out);
+
+/* SHA256_Transform is a low-level function that performs a single, SHA-1 block
+ * transformation using the state from |sha| and 64 bytes from |block|. */
+void SHA256_Transform(SHA256_CTX *sha, const uint8_t *data);
+
+struct sha256_state_st {
+  uint32_t h[8];
+  uint32_t Nl, Nh;
+  uint32_t data[16];
+  unsigned int num, md_len;
+};
+
+
+/* SHA-384. */
+
+/* SHA384_CBLOCK is the block size of SHA-384. */
+#define SHA384_CBLOCK 128
+
+/* SHA384_DIGEST_LENGTH is the length of a SHA-384 digest. */
+#define SHA384_DIGEST_LENGTH 48
+
+/* SHA384_Init initialises |sha| and returns 1. */
+int SHA384_Init(SHA512_CTX *sha);
+
+/* SHA384_Update adds |len| bytes from |data| to |sha|. */
+int SHA384_Update(SHA512_CTX *sha, const void *data, size_t len);
+
+/* SHA384_Final adds the final padding to |sha| and writes the resulting digest
+ * to |md|, which must have at least |SHA_DIGEST_LENGTH| bytes of space. */
+int SHA384_Final(uint8_t *md, SHA512_CTX *sha);
+
+/* SHA384 writes the digest of |len| bytes from |data| to |out| and returns
+ * |out|. There must be at least |SHA_DIGEST_LENGTH| bytes of space in
+ * |out|. */
+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-1 block
+ * transformation using the state from |sha| and 64 bytes from |block|. */
+void SHA384_Transform(SHA512_CTX *sha, const uint8_t *data);
+
+
+/* SHA-512. */
+
+/* SHA512_CBLOCK is the block size of SHA-512. */
+#define SHA512_CBLOCK 128
+
+/* SHA512_DIGEST_LENGTH is the length of a SHA-512 digest. */
+#define SHA512_DIGEST_LENGTH 64
+
+/* SHA512_Init initialises |sha| and returns 1. */
+int SHA512_Init(SHA512_CTX *sha);
+
+/* SHA512_Update adds |len| bytes from |data| to |sha|. */
+int SHA512_Update(SHA512_CTX *sha, const void *data, size_t len);
+
+/* SHA512_Final adds the final padding to |sha| and writes the resulting digest
+ * to |md|, which must have at least |SHA_DIGEST_LENGTH| bytes of space. */
+int SHA512_Final(uint8_t *md, SHA512_CTX *sha);
+
+/* SHA512 writes the digest of |len| bytes from |data| to |out| and returns
+ * |out|. There must be at least |SHA_DIGEST_LENGTH| bytes of space in
+ * |out|. */
+uint8_t *SHA512(const uint8_t *data, size_t len ,uint8_t *out);
+
+/* SHA512_Transform is a low-level function that performs a single, SHA-1 block
+ * transformation using the state from |sha| and 64 bytes from |block|. */
+void SHA512_Transform(SHA512_CTX *sha, const uint8_t *data);
+
+struct sha512_state_st {
+  uint64_t h[8];
+  uint64_t Nl, Nh;
+  union {
+    uint64_t d[16];
+    uint8_t p[64];
+  } u;
+  unsigned int num, md_len;
+};
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_SHA_H */
diff --git a/crypto/sha/sha1.c b/crypto/sha/sha1.c
new file mode 100644
index 0000000..3148739
--- /dev/null
+++ b/crypto/sha/sha1.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/sha.h>
+
+#include <string.h>
+
+#include <openssl/mem.h>
+
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64) || defined(OPENSSL_ARM))
+#define SHA1_ASM
+#endif
+
+int SHA1_Init(SHA_CTX *sha) {
+  memset(sha, 0, sizeof(SHA_CTX));
+  sha->h0 = 0x67452301UL;
+  sha->h1 = 0xefcdab89UL;
+  sha->h2 = 0x98badcfeUL;
+  sha->h3 = 0x10325476UL;
+  sha->h4 = 0xc3d2e1f0UL;
+  return 1;
+}
+
+uint8_t *SHA1(const uint8_t *data, size_t len, uint8_t *out) {
+  SHA_CTX ctx;
+  static uint8_t buf[SHA_DIGEST_LENGTH];
+
+  /* TODO(fork): remove this static buffer. */
+  if (out == NULL) {
+    out = buf;
+  }
+  if (!SHA1_Init(&ctx)) {
+    return NULL;
+  }
+  SHA1_Update(&ctx, data, len);
+  SHA1_Final(out, &ctx);
+  OPENSSL_cleanse(&ctx, sizeof(ctx));
+  return out;
+}
+
+#define DATA_ORDER_IS_BIG_ENDIAN
+
+#define HASH_LONG               uint32_t
+#define HASH_CTX                SHA_CTX
+#define HASH_CBLOCK             64
+#define HASH_MAKE_STRING(c, s) \
+  do {                         \
+    unsigned long ll;          \
+    ll = (c)->h0;              \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->h1;              \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->h2;              \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->h3;              \
+    (void) HOST_l2c(ll, (s));  \
+    ll = (c)->h4;              \
+    (void) HOST_l2c(ll, (s));  \
+  } while (0)
+
+#define HASH_UPDATE SHA1_Update
+#define HASH_TRANSFORM SHA1_Transform
+#define HASH_FINAL SHA1_Final
+#define HASH_BLOCK_DATA_ORDER sha1_block_data_order
+#define Xupdate(a, ix, ia, ib, ic, id) \
+  ((a) = (ia ^ ib ^ ic ^ id), ix = (a) = ROTATE((a), 1))
+
+#ifndef SHA1_ASM
+static
+#endif
+void sha1_block_data_order(SHA_CTX *c, const void *p, size_t num);
+
+#include "../digest/md32_common.h"
+
+#define K_00_19 0x5a827999UL
+#define K_20_39 0x6ed9eba1UL
+#define K_40_59 0x8f1bbcdcUL
+#define K_60_79 0xca62c1d6UL
+
+/* As  pointed out by Wei Dai <weidai@eskimo.com>, F() below can be simplified
+ * to the code in F_00_19.  Wei attributes these optimisations to Peter
+ * Gutmann's SHS code, and he attributes it to Rich Schroeppel. #define
+ * F(x,y,z) (((x) & (y))  |  ((~(x)) & (z))) I've just become aware of another
+ * tweak to be made, again from Wei Dai, in F_40_59, (x&a)|(y&a) -> (x|y)&a */
+#define F_00_19(b, c, d) ((((c) ^ (d)) & (b)) ^ (d))
+#define F_20_39(b, c, d) ((b) ^ (c) ^ (d))
+#define F_40_59(b, c, d) (((b) & (c)) | (((b) | (c)) & (d)))
+#define F_60_79(b, c, d) F_20_39(b, c, d)
+
+#define BODY_00_15(i, a, b, c, d, e, f, xi)                           \
+  (f) = xi + (e) + K_00_19 + ROTATE((a), 5) + F_00_19((b), (c), (d)); \
+  (b) = ROTATE((b), 30);
+
+#define BODY_16_19(i, a, b, c, d, e, f, xi, xa, xb, xc, xd)       \
+  Xupdate(f, xi, xa, xb, xc, xd);                                 \
+  (f) += (e) + K_00_19 + ROTATE((a), 5) + F_00_19((b), (c), (d)); \
+  (b) = ROTATE((b), 30);
+
+#define BODY_20_31(i, a, b, c, d, e, f, xi, xa, xb, xc, xd)       \
+  Xupdate(f, xi, xa, xb, xc, xd);                                 \
+  (f) += (e) + K_20_39 + ROTATE((a), 5) + F_20_39((b), (c), (d)); \
+  (b) = ROTATE((b), 30);
+
+#define BODY_32_39(i, a, b, c, d, e, f, xa, xb, xc, xd)           \
+  Xupdate(f, xa, xa, xb, xc, xd);                                 \
+  (f) += (e) + K_20_39 + ROTATE((a), 5) + F_20_39((b), (c), (d)); \
+  (b) = ROTATE((b), 30);
+
+#define BODY_40_59(i, a, b, c, d, e, f, xa, xb, xc, xd)           \
+  Xupdate(f, xa, xa, xb, xc, xd);                                 \
+  (f) += (e) + K_40_59 + ROTATE((a), 5) + F_40_59((b), (c), (d)); \
+  (b) = ROTATE((b), 30);
+
+#define BODY_60_79(i, a, b, c, d, e, f, xa, xb, xc, xd)               \
+  Xupdate(f, xa, xa, xb, xc, xd);                                     \
+  (f) = xa + (e) + K_60_79 + ROTATE((a), 5) + F_60_79((b), (c), (d)); \
+  (b) = ROTATE((b), 30);
+
+#ifdef X
+#undef X
+#endif
+
+/* Originally X was an array. As it's automatic it's natural
+* to expect RISC compiler to accomodate at least part of it in
+* the register bank, isn't it? Unfortunately not all compilers
+* "find" this expectation reasonable:-( On order to make such
+* compilers generate better code I replace X[] with a bunch of
+* X0, X1, etc. See the function body below...
+*					<appro@fy.chalmers.se> */
+#define X(i)	XX##i
+
+#if !defined(SHA1_ASM)
+static void HASH_BLOCK_DATA_ORDER(SHA_CTX *c, const void *p, size_t num) {
+  const uint8_t *data = p;
+  register unsigned MD32_REG_T A, B, C, D, E, T, l;
+  unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10,
+      XX11, XX12, XX13, XX14, XX15;
+
+  A = c->h0;
+  B = c->h1;
+  C = c->h2;
+  D = c->h3;
+  E = c->h4;
+
+  for (;;) {
+    const union {
+      long one;
+      char little;
+    } is_endian = {1};
+
+    if (!is_endian.little && ((size_t)p % 4) == 0) {
+      const uint32_t *W = (const uint32_t *)data;
+
+      X(0) = W[0];
+      X(1) = W[1];
+      BODY_00_15(0, A, B, C, D, E, T, X(0));
+      X(2) = W[2];
+      BODY_00_15(1, T, A, B, C, D, E, X(1));
+      X(3) = W[3];
+      BODY_00_15(2, E, T, A, B, C, D, X(2));
+      X(4) = W[4];
+      BODY_00_15(3, D, E, T, A, B, C, X(3));
+      X(5) = W[5];
+      BODY_00_15(4, C, D, E, T, A, B, X(4));
+      X(6) = W[6];
+      BODY_00_15(5, B, C, D, E, T, A, X(5));
+      X(7) = W[7];
+      BODY_00_15(6, A, B, C, D, E, T, X(6));
+      X(8) = W[8];
+      BODY_00_15(7, T, A, B, C, D, E, X(7));
+      X(9) = W[9];
+      BODY_00_15(8, E, T, A, B, C, D, X(8));
+      X(10) = W[10];
+      BODY_00_15(9, D, E, T, A, B, C, X(9));
+      X(11) = W[11];
+      BODY_00_15(10, C, D, E, T, A, B, X(10));
+      X(12) = W[12];
+      BODY_00_15(11, B, C, D, E, T, A, X(11));
+      X(13) = W[13];
+      BODY_00_15(12, A, B, C, D, E, T, X(12));
+      X(14) = W[14];
+      BODY_00_15(13, T, A, B, C, D, E, X(13));
+      X(15) = W[15];
+      BODY_00_15(14, E, T, A, B, C, D, X(14));
+      BODY_00_15(15, D, E, T, A, B, C, X(15));
+
+      data += HASH_CBLOCK;
+    } else {
+      (void)HOST_c2l(data, l);
+      X(0) = l;
+      (void)HOST_c2l(data, l);
+      X(1) = l;
+      BODY_00_15(0, A, B, C, D, E, T, X(0));
+      (void)HOST_c2l(data, l);
+      X(2) = l;
+      BODY_00_15(1, T, A, B, C, D, E, X(1));
+      (void)HOST_c2l(data, l);
+      X(3) = l;
+      BODY_00_15(2, E, T, A, B, C, D, X(2));
+      (void)HOST_c2l(data, l);
+      X(4) = l;
+      BODY_00_15(3, D, E, T, A, B, C, X(3));
+      (void)HOST_c2l(data, l);
+      X(5) = l;
+      BODY_00_15(4, C, D, E, T, A, B, X(4));
+      (void)HOST_c2l(data, l);
+      X(6) = l;
+      BODY_00_15(5, B, C, D, E, T, A, X(5));
+      (void)HOST_c2l(data, l);
+      X(7) = l;
+      BODY_00_15(6, A, B, C, D, E, T, X(6));
+      (void)HOST_c2l(data, l);
+      X(8) = l;
+      BODY_00_15(7, T, A, B, C, D, E, X(7));
+      (void)HOST_c2l(data, l);
+      X(9) = l;
+      BODY_00_15(8, E, T, A, B, C, D, X(8));
+      (void)HOST_c2l(data, l);
+      X(10) = l;
+      BODY_00_15(9, D, E, T, A, B, C, X(9));
+      (void)HOST_c2l(data, l);
+      X(11) = l;
+      BODY_00_15(10, C, D, E, T, A, B, X(10));
+      (void)HOST_c2l(data, l);
+      X(12) = l;
+      BODY_00_15(11, B, C, D, E, T, A, X(11));
+      (void)HOST_c2l(data, l);
+      X(13) = l;
+      BODY_00_15(12, A, B, C, D, E, T, X(12));
+      (void)HOST_c2l(data, l);
+      X(14) = l;
+      BODY_00_15(13, T, A, B, C, D, E, X(13));
+      (void)HOST_c2l(data, l);
+      X(15) = l;
+      BODY_00_15(14, E, T, A, B, C, D, X(14));
+      BODY_00_15(15, D, E, T, A, B, C, X(15));
+    }
+
+    BODY_16_19(16, C, D, E, T, A, B, X(0), X(0), X(2), X(8), X(13));
+    BODY_16_19(17, B, C, D, E, T, A, X(1), X(1), X(3), X(9), X(14));
+    BODY_16_19(18, A, B, C, D, E, T, X(2), X(2), X(4), X(10), X(15));
+    BODY_16_19(19, T, A, B, C, D, E, X(3), X(3), X(5), X(11), X(0));
+
+    BODY_20_31(20, E, T, A, B, C, D, X(4), X(4), X(6), X(12), X(1));
+    BODY_20_31(21, D, E, T, A, B, C, X(5), X(5), X(7), X(13), X(2));
+    BODY_20_31(22, C, D, E, T, A, B, X(6), X(6), X(8), X(14), X(3));
+    BODY_20_31(23, B, C, D, E, T, A, X(7), X(7), X(9), X(15), X(4));
+    BODY_20_31(24, A, B, C, D, E, T, X(8), X(8), X(10), X(0), X(5));
+    BODY_20_31(25, T, A, B, C, D, E, X(9), X(9), X(11), X(1), X(6));
+    BODY_20_31(26, E, T, A, B, C, D, X(10), X(10), X(12), X(2), X(7));
+    BODY_20_31(27, D, E, T, A, B, C, X(11), X(11), X(13), X(3), X(8));
+    BODY_20_31(28, C, D, E, T, A, B, X(12), X(12), X(14), X(4), X(9));
+    BODY_20_31(29, B, C, D, E, T, A, X(13), X(13), X(15), X(5), X(10));
+    BODY_20_31(30, A, B, C, D, E, T, X(14), X(14), X(0), X(6), X(11));
+    BODY_20_31(31, T, A, B, C, D, E, X(15), X(15), X(1), X(7), X(12));
+
+    BODY_32_39(32, E, T, A, B, C, D, X(0), X(2), X(8), X(13));
+    BODY_32_39(33, D, E, T, A, B, C, X(1), X(3), X(9), X(14));
+    BODY_32_39(34, C, D, E, T, A, B, X(2), X(4), X(10), X(15));
+    BODY_32_39(35, B, C, D, E, T, A, X(3), X(5), X(11), X(0));
+    BODY_32_39(36, A, B, C, D, E, T, X(4), X(6), X(12), X(1));
+    BODY_32_39(37, T, A, B, C, D, E, X(5), X(7), X(13), X(2));
+    BODY_32_39(38, E, T, A, B, C, D, X(6), X(8), X(14), X(3));
+    BODY_32_39(39, D, E, T, A, B, C, X(7), X(9), X(15), X(4));
+
+    BODY_40_59(40, C, D, E, T, A, B, X(8), X(10), X(0), X(5));
+    BODY_40_59(41, B, C, D, E, T, A, X(9), X(11), X(1), X(6));
+    BODY_40_59(42, A, B, C, D, E, T, X(10), X(12), X(2), X(7));
+    BODY_40_59(43, T, A, B, C, D, E, X(11), X(13), X(3), X(8));
+    BODY_40_59(44, E, T, A, B, C, D, X(12), X(14), X(4), X(9));
+    BODY_40_59(45, D, E, T, A, B, C, X(13), X(15), X(5), X(10));
+    BODY_40_59(46, C, D, E, T, A, B, X(14), X(0), X(6), X(11));
+    BODY_40_59(47, B, C, D, E, T, A, X(15), X(1), X(7), X(12));
+    BODY_40_59(48, A, B, C, D, E, T, X(0), X(2), X(8), X(13));
+    BODY_40_59(49, T, A, B, C, D, E, X(1), X(3), X(9), X(14));
+    BODY_40_59(50, E, T, A, B, C, D, X(2), X(4), X(10), X(15));
+    BODY_40_59(51, D, E, T, A, B, C, X(3), X(5), X(11), X(0));
+    BODY_40_59(52, C, D, E, T, A, B, X(4), X(6), X(12), X(1));
+    BODY_40_59(53, B, C, D, E, T, A, X(5), X(7), X(13), X(2));
+    BODY_40_59(54, A, B, C, D, E, T, X(6), X(8), X(14), X(3));
+    BODY_40_59(55, T, A, B, C, D, E, X(7), X(9), X(15), X(4));
+    BODY_40_59(56, E, T, A, B, C, D, X(8), X(10), X(0), X(5));
+    BODY_40_59(57, D, E, T, A, B, C, X(9), X(11), X(1), X(6));
+    BODY_40_59(58, C, D, E, T, A, B, X(10), X(12), X(2), X(7));
+    BODY_40_59(59, B, C, D, E, T, A, X(11), X(13), X(3), X(8));
+
+    BODY_60_79(60, A, B, C, D, E, T, X(12), X(14), X(4), X(9));
+    BODY_60_79(61, T, A, B, C, D, E, X(13), X(15), X(5), X(10));
+    BODY_60_79(62, E, T, A, B, C, D, X(14), X(0), X(6), X(11));
+    BODY_60_79(63, D, E, T, A, B, C, X(15), X(1), X(7), X(12));
+    BODY_60_79(64, C, D, E, T, A, B, X(0), X(2), X(8), X(13));
+    BODY_60_79(65, B, C, D, E, T, A, X(1), X(3), X(9), X(14));
+    BODY_60_79(66, A, B, C, D, E, T, X(2), X(4), X(10), X(15));
+    BODY_60_79(67, T, A, B, C, D, E, X(3), X(5), X(11), X(0));
+    BODY_60_79(68, E, T, A, B, C, D, X(4), X(6), X(12), X(1));
+    BODY_60_79(69, D, E, T, A, B, C, X(5), X(7), X(13), X(2));
+    BODY_60_79(70, C, D, E, T, A, B, X(6), X(8), X(14), X(3));
+    BODY_60_79(71, B, C, D, E, T, A, X(7), X(9), X(15), X(4));
+    BODY_60_79(72, A, B, C, D, E, T, X(8), X(10), X(0), X(5));
+    BODY_60_79(73, T, A, B, C, D, E, X(9), X(11), X(1), X(6));
+    BODY_60_79(74, E, T, A, B, C, D, X(10), X(12), X(2), X(7));
+    BODY_60_79(75, D, E, T, A, B, C, X(11), X(13), X(3), X(8));
+    BODY_60_79(76, C, D, E, T, A, B, X(12), X(14), X(4), X(9));
+    BODY_60_79(77, B, C, D, E, T, A, X(13), X(15), X(5), X(10));
+    BODY_60_79(78, A, B, C, D, E, T, X(14), X(0), X(6), X(11));
+    BODY_60_79(79, T, A, B, C, D, E, X(15), X(1), X(7), X(12));
+
+    c->h0 = (c->h0 + E) & 0xffffffffL;
+    c->h1 = (c->h1 + T) & 0xffffffffL;
+    c->h2 = (c->h2 + A) & 0xffffffffL;
+    c->h3 = (c->h3 + B) & 0xffffffffL;
+    c->h4 = (c->h4 + C) & 0xffffffffL;
+
+    if (--num == 0)
+      break;
+
+    A = c->h0;
+    B = c->h1;
+    C = c->h2;
+    D = c->h3;
+    E = c->h4;
+  }
+}
+#endif
diff --git a/crypto/sha/sha1_test.c b/crypto/sha/sha1_test.c
new file mode 100644
index 0000000..72ef9e1
--- /dev/null
+++ b/crypto/sha/sha1_test.c
@@ -0,0 +1,125 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/sha.h>
+#include <openssl/digest.h>
+
+
+static const char *const test[] = {
+    "abc", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", NULL, };
+
+static const char *const expected[] = {
+    "a9993e364706816aba3e25717850c26c9cd0d89d",
+    "84983e441c3bd26ebaae4aa1f95129e5e54670f1", };
+
+static int test_incremental() {
+  EVP_MD_CTX ctx;
+  char buf[1000];
+  uint8_t md[SHA_DIGEST_LENGTH];
+  char md_hex[sizeof(md) * 2 + 1];
+  size_t i;
+  static const char expected[] = "34aa973cd4c4daa4f61eeb2bdbad27316534016f";
+
+  memset(buf, 'a', sizeof(buf));
+  EVP_MD_CTX_init(&ctx);
+  EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL);
+  for (i = 0; i < 1000; i++) {
+    EVP_DigestUpdate(&ctx, buf, sizeof(buf));
+  }
+  EVP_DigestFinal_ex(&ctx, md, NULL);
+  EVP_MD_CTX_cleanup(&ctx);
+
+  for (i = 0; i < sizeof(md); i++) {
+    sprintf(&md_hex[i * 2], "%02x", md[i]);
+  }
+
+  if (strcmp(md_hex, expected) != 0) {
+    fprintf(stderr, "test_incremental: got %s, wanted %s\n", md_hex, expected);
+    return 0;
+  }
+
+  return 1;
+}
+
+int main(int argc, char **argv) {
+  size_t i, j;
+  uint8_t md[SHA_DIGEST_LENGTH];
+  char md_hex[sizeof(md) * 2 + 1];
+  int ok = 1;
+
+  for (i = 0; test[i] != NULL; i++) {
+    EVP_Digest(test[i], strlen(test[i]), md, NULL, EVP_sha1(), NULL);
+    for (j = 0; j < sizeof(md); j++) {
+      sprintf(&md_hex[j * 2], "%02x", md[j]);
+    }
+
+    if (strcmp(md_hex, expected[i]) != 0) {
+      fprintf(stderr, "#%u: got %s, wanted %s\n", (unsigned)i, md_hex,
+              expected[i]);
+      ok = 0;
+    }
+  }
+
+  ok &= test_incremental();
+
+  if (ok) {
+    printf("PASS\n");
+  }
+
+  return ok ? 0 : 1;
+}
diff --git a/crypto/sha/sha256.c b/crypto/sha/sha256.c
new file mode 100644
index 0000000..0685784
--- /dev/null
+++ b/crypto/sha/sha256.c
@@ -0,0 +1,365 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/sha.h>
+
+#include <string.h>
+
+#include <openssl/mem.h>
+
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64) || defined(OPENSSL_ARM))
+#define SHA256_ASM
+#endif
+
+int SHA224_Init(SHA256_CTX *sha) {
+  memset(sha, 0, sizeof(SHA256_CTX));
+  sha->h[0] = 0xc1059ed8UL;
+  sha->h[1] = 0x367cd507UL;
+  sha->h[2] = 0x3070dd17UL;
+  sha->h[3] = 0xf70e5939UL;
+  sha->h[4] = 0xffc00b31UL;
+  sha->h[5] = 0x68581511UL;
+  sha->h[6] = 0x64f98fa7UL;
+  sha->h[7] = 0xbefa4fa4UL;
+  sha->md_len = SHA224_DIGEST_LENGTH;
+  return 1;
+}
+
+int SHA256_Init(SHA256_CTX *sha) {
+  memset(sha, 0, sizeof(SHA256_CTX));
+  sha->h[0] = 0x6a09e667UL;
+  sha->h[1] = 0xbb67ae85UL;
+  sha->h[2] = 0x3c6ef372UL;
+  sha->h[3] = 0xa54ff53aUL;
+  sha->h[4] = 0x510e527fUL;
+  sha->h[5] = 0x9b05688cUL;
+  sha->h[6] = 0x1f83d9abUL;
+  sha->h[7] = 0x5be0cd19UL;
+  sha->md_len = SHA256_DIGEST_LENGTH;
+  return 1;
+}
+
+uint8_t *SHA224(const uint8_t *data, size_t len, uint8_t *out) {
+  SHA256_CTX ctx;
+  static uint8_t buf[SHA224_DIGEST_LENGTH];
+
+  /* TODO(fork): remove this static buffer. */
+  if (out == NULL) {
+    out = buf;
+  }
+  SHA224_Init(&ctx);
+  SHA256_Update(&ctx, data, len);
+  SHA256_Final(out, &ctx);
+  OPENSSL_cleanse(&ctx, sizeof(ctx));
+  return out;
+}
+
+uint8_t *SHA256(const uint8_t *data, size_t len, uint8_t *out) {
+  SHA256_CTX ctx;
+  static uint8_t buf[SHA256_DIGEST_LENGTH];
+
+  /* TODO(fork): remove this static buffer. */
+  if (out == NULL) {
+    out = buf;
+  }
+  SHA256_Init(&ctx);
+  SHA256_Update(&ctx, data, len);
+  SHA256_Final(out, &ctx);
+  OPENSSL_cleanse(&ctx, sizeof(ctx));
+  return out;
+}
+
+int SHA224_Update(SHA256_CTX *ctx, const void *data, size_t len) {
+  return SHA256_Update(ctx, data, len);
+}
+
+int SHA224_Final(uint8_t *md, SHA256_CTX *ctx) {
+  return SHA256_Final(md, ctx);
+}
+
+#define DATA_ORDER_IS_BIG_ENDIAN
+
+#define HASH_LONG uint32_t
+#define HASH_CTX SHA256_CTX
+#define HASH_CBLOCK 64
+
+/* Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
+ * default: case below covers for it. It's not clear however if it's permitted
+ * to truncate to amount of bytes not divisible by 4. I bet not, but if it is,
+ * then default: case shall be extended. For reference. Idea behind separate
+ * cases for pre-defined lenghts is to let the compiler decide if it's
+ * appropriate to unroll small loops. */
+#define HASH_MAKE_STRING(c, s)                              \
+  do {                                                      \
+    unsigned long ll;                                       \
+    unsigned int nn;                                        \
+    switch ((c)->md_len) {                                  \
+      case SHA224_DIGEST_LENGTH:                            \
+        for (nn = 0; nn < SHA224_DIGEST_LENGTH / 4; nn++) { \
+          ll = (c)->h[nn];                                  \
+          (void) HOST_l2c(ll, (s));                         \
+        }                                                   \
+        break;                                              \
+      case SHA256_DIGEST_LENGTH:                            \
+        for (nn = 0; nn < SHA256_DIGEST_LENGTH / 4; nn++) { \
+          ll = (c)->h[nn];                                  \
+          (void) HOST_l2c(ll, (s));                         \
+        }                                                   \
+        break;                                              \
+      default:                                              \
+        if ((c)->md_len > SHA256_DIGEST_LENGTH)             \
+          return 0;                                         \
+        for (nn = 0; nn < (c)->md_len / 4; nn++) {          \
+          ll = (c)->h[nn];                                  \
+          (void) HOST_l2c(ll, (s));                         \
+        }                                                   \
+        break;                                              \
+    }                                                       \
+  } while (0)
+
+
+#define HASH_UPDATE SHA256_Update
+#define HASH_TRANSFORM SHA256_Transform
+#define HASH_FINAL SHA256_Final
+#define HASH_BLOCK_DATA_ORDER sha256_block_data_order
+#ifndef SHA256_ASM
+static
+#endif
+void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num);
+
+#include "../digest/md32_common.h"
+
+#ifndef SHA256_ASM
+static const HASH_LONG K256[64] = {
+    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+    0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+    0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+    0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+    0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+    0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+    0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+    0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+    0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+    0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL};
+
+/* FIPS specification refers to right rotations, while our ROTATE macro
+ * is left one. This is why you might notice that rotation coefficients
+ * differ from those observed in FIPS document by 32-N... */
+#define Sigma0(x) (ROTATE((x), 30) ^ ROTATE((x), 19) ^ ROTATE((x), 10))
+#define Sigma1(x) (ROTATE((x), 26) ^ ROTATE((x), 21) ^ ROTATE((x), 7))
+#define sigma0(x) (ROTATE((x), 25) ^ ROTATE((x), 14) ^ ((x) >> 3))
+#define sigma1(x) (ROTATE((x), 15) ^ ROTATE((x), 13) ^ ((x) >> 10))
+
+#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+#define ROUND_00_15(i, a, b, c, d, e, f, g, h)   \
+  do {                                           \
+    T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i]; \
+    h = Sigma0(a) + Maj(a, b, c);                \
+    d += T1;                                     \
+    h += T1;                                     \
+  } while (0)
+
+#define ROUND_16_63(i, a, b, c, d, e, f, g, h, X)      \
+  do {                                                 \
+    s0 = X[(i + 1) & 0x0f];                            \
+    s0 = sigma0(s0);                                   \
+    s1 = X[(i + 14) & 0x0f];                           \
+    s1 = sigma1(s1);                                   \
+    T1 = X[(i) & 0x0f] += s0 + s1 + X[(i + 9) & 0x0f]; \
+    ROUND_00_15(i, a, b, c, d, e, f, g, h);            \
+  } while (0)
+
+static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
+                                    size_t num) {
+  unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1;
+  HASH_LONG X[16];
+  int i;
+  const uint8_t *data = in;
+  const union {
+    long one;
+    char little;
+  } is_endian = {1};
+
+  while (num--) {
+    a = ctx->h[0];
+    b = ctx->h[1];
+    c = ctx->h[2];
+    d = ctx->h[3];
+    e = ctx->h[4];
+    f = ctx->h[5];
+    g = ctx->h[6];
+    h = ctx->h[7];
+
+    if (!is_endian.little && sizeof(HASH_LONG) == 4 && ((size_t)in % 4) == 0) {
+      const HASH_LONG *W = (const HASH_LONG *)data;
+
+      T1 = X[0] = W[0];
+      ROUND_00_15(0, a, b, c, d, e, f, g, h);
+      T1 = X[1] = W[1];
+      ROUND_00_15(1, h, a, b, c, d, e, f, g);
+      T1 = X[2] = W[2];
+      ROUND_00_15(2, g, h, a, b, c, d, e, f);
+      T1 = X[3] = W[3];
+      ROUND_00_15(3, f, g, h, a, b, c, d, e);
+      T1 = X[4] = W[4];
+      ROUND_00_15(4, e, f, g, h, a, b, c, d);
+      T1 = X[5] = W[5];
+      ROUND_00_15(5, d, e, f, g, h, a, b, c);
+      T1 = X[6] = W[6];
+      ROUND_00_15(6, c, d, e, f, g, h, a, b);
+      T1 = X[7] = W[7];
+      ROUND_00_15(7, b, c, d, e, f, g, h, a);
+      T1 = X[8] = W[8];
+      ROUND_00_15(8, a, b, c, d, e, f, g, h);
+      T1 = X[9] = W[9];
+      ROUND_00_15(9, h, a, b, c, d, e, f, g);
+      T1 = X[10] = W[10];
+      ROUND_00_15(10, g, h, a, b, c, d, e, f);
+      T1 = X[11] = W[11];
+      ROUND_00_15(11, f, g, h, a, b, c, d, e);
+      T1 = X[12] = W[12];
+      ROUND_00_15(12, e, f, g, h, a, b, c, d);
+      T1 = X[13] = W[13];
+      ROUND_00_15(13, d, e, f, g, h, a, b, c);
+      T1 = X[14] = W[14];
+      ROUND_00_15(14, c, d, e, f, g, h, a, b);
+      T1 = X[15] = W[15];
+      ROUND_00_15(15, b, c, d, e, f, g, h, a);
+
+      data += HASH_CBLOCK;
+    } else {
+      HASH_LONG l;
+
+      HOST_c2l(data, l);
+      T1 = X[0] = l;
+      ROUND_00_15(0, a, b, c, d, e, f, g, h);
+      HOST_c2l(data, l);
+      T1 = X[1] = l;
+      ROUND_00_15(1, h, a, b, c, d, e, f, g);
+      HOST_c2l(data, l);
+      T1 = X[2] = l;
+      ROUND_00_15(2, g, h, a, b, c, d, e, f);
+      HOST_c2l(data, l);
+      T1 = X[3] = l;
+      ROUND_00_15(3, f, g, h, a, b, c, d, e);
+      HOST_c2l(data, l);
+      T1 = X[4] = l;
+      ROUND_00_15(4, e, f, g, h, a, b, c, d);
+      HOST_c2l(data, l);
+      T1 = X[5] = l;
+      ROUND_00_15(5, d, e, f, g, h, a, b, c);
+      HOST_c2l(data, l);
+      T1 = X[6] = l;
+      ROUND_00_15(6, c, d, e, f, g, h, a, b);
+      HOST_c2l(data, l);
+      T1 = X[7] = l;
+      ROUND_00_15(7, b, c, d, e, f, g, h, a);
+      HOST_c2l(data, l);
+      T1 = X[8] = l;
+      ROUND_00_15(8, a, b, c, d, e, f, g, h);
+      HOST_c2l(data, l);
+      T1 = X[9] = l;
+      ROUND_00_15(9, h, a, b, c, d, e, f, g);
+      HOST_c2l(data, l);
+      T1 = X[10] = l;
+      ROUND_00_15(10, g, h, a, b, c, d, e, f);
+      HOST_c2l(data, l);
+      T1 = X[11] = l;
+      ROUND_00_15(11, f, g, h, a, b, c, d, e);
+      HOST_c2l(data, l);
+      T1 = X[12] = l;
+      ROUND_00_15(12, e, f, g, h, a, b, c, d);
+      HOST_c2l(data, l);
+      T1 = X[13] = l;
+      ROUND_00_15(13, d, e, f, g, h, a, b, c);
+      HOST_c2l(data, l);
+      T1 = X[14] = l;
+      ROUND_00_15(14, c, d, e, f, g, h, a, b);
+      HOST_c2l(data, l);
+      T1 = X[15] = l;
+      ROUND_00_15(15, b, c, d, e, f, g, h, a);
+    }
+
+    for (i = 16; i < 64; i += 8) {
+      ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X);
+      ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X);
+      ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X);
+      ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X);
+      ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X);
+      ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X);
+      ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X);
+      ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X);
+    }
+
+    ctx->h[0] += a;
+    ctx->h[1] += b;
+    ctx->h[2] += c;
+    ctx->h[3] += d;
+    ctx->h[4] += e;
+    ctx->h[5] += f;
+    ctx->h[6] += g;
+    ctx->h[7] += h;
+  }
+}
+
+#endif /* SHA256_ASM */
diff --git a/crypto/sha/sha512.c b/crypto/sha/sha512.c
new file mode 100644
index 0000000..dcaac6f
--- /dev/null
+++ b/crypto/sha/sha512.c
@@ -0,0 +1,590 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/sha.h>
+
+#include <string.h>
+
+#include <openssl/mem.h>
+
+
+/* IMPLEMENTATION NOTES.
+ *
+ * As you might have noticed 32-bit hash algorithms:
+ *
+ * - permit SHA_LONG to be wider than 32-bit (case on CRAY);
+ * - optimized versions implement two transform functions: one operating
+ *   on [aligned] data in host byte order and one - on data in input
+ *   stream byte order;
+ * - share common byte-order neutral collector and padding function
+ *   implementations, ../md32_common.h;
+ *
+ * Neither of the above applies to this SHA-512 implementations. Reasons
+ * [in reverse order] are:
+ *
+ * - it's the only 64-bit hash algorithm for the moment of this writing,
+ *   there is no need for common collector/padding implementation [yet];
+ * - by supporting only one transform function [which operates on
+ *   *aligned* data in input stream byte order, big-endian in this case]
+ *   we minimize burden of maintenance in two ways: a) collector/padding
+ *   function is simpler; b) only one transform function to stare at;
+ * - SHA_LONG64 is required to be exactly 64-bit in order to be able to
+ *   apply a number of optimizations to mitigate potential performance
+ *   penalties caused by previous design decision; */
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64) || defined(OPENSSL_ARM))
+#define SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
+#define SHA512_ASM
+#endif
+
+int SHA384_Init(SHA512_CTX *sha) {
+  sha->h[0] = 0xcbbb9d5dc1059ed8;
+  sha->h[1] = 0x629a292a367cd507;
+  sha->h[2] = 0x9159015a3070dd17;
+  sha->h[3] = 0x152fecd8f70e5939;
+  sha->h[4] = 0x67332667ffc00b31;
+  sha->h[5] = 0x8eb44a8768581511;
+  sha->h[6] = 0xdb0c2e0d64f98fa7;
+  sha->h[7] = 0x47b5481dbefa4fa4;
+
+  sha->Nl = 0;
+  sha->Nh = 0;
+  sha->num = 0;
+  sha->md_len = SHA384_DIGEST_LENGTH;
+  return 1;
+}
+
+
+int SHA512_Init(SHA512_CTX *sha) {
+  sha->h[0] = 0x6a09e667f3bcc908;
+  sha->h[1] = 0xbb67ae8584caa73b;
+  sha->h[2] = 0x3c6ef372fe94f82b;
+  sha->h[3] = 0xa54ff53a5f1d36f1;
+  sha->h[4] = 0x510e527fade682d1;
+  sha->h[5] = 0x9b05688c2b3e6c1f;
+  sha->h[6] = 0x1f83d9abfb41bd6b;
+  sha->h[7] = 0x5be0cd19137e2179;
+
+  sha->Nl = 0;
+  sha->Nh = 0;
+  sha->num = 0;
+  sha->md_len = SHA512_DIGEST_LENGTH;
+  return 1;
+}
+
+uint8_t *SHA384(const uint8_t *data, size_t len, uint8_t *out) {
+  SHA512_CTX ctx;
+  static uint8_t buf[SHA384_DIGEST_LENGTH];
+
+  /* TODO(fork): remove this static buffer. */
+  if (out == NULL) {
+    out = buf;
+  }
+
+  SHA384_Init(&ctx);
+  SHA512_Update(&ctx, data, len);
+  SHA512_Final(out, &ctx);
+  OPENSSL_cleanse(&ctx, sizeof(ctx));
+  return out;
+}
+
+uint8_t *SHA512(const uint8_t *data, size_t len, uint8_t *out) {
+  SHA512_CTX ctx;
+  static uint8_t buf[SHA512_DIGEST_LENGTH];
+
+  /* TODO(fork): remove this static buffer. */
+  if (out == NULL) {
+    out = buf;
+  }
+  SHA512_Init(&ctx);
+  SHA512_Update(&ctx, data, len);
+  SHA512_Final(out, &ctx);
+  OPENSSL_cleanse(&ctx, sizeof(ctx));
+  return out;
+}
+
+#if !defined(SHA512_ASM)
+static
+#endif
+void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num);
+
+
+int SHA384_Final(unsigned char *md, SHA512_CTX *sha) {
+  return SHA512_Final(md, sha);
+}
+
+int SHA384_Update(SHA512_CTX *sha, const void *data, size_t len) {
+  return SHA512_Update(sha, data, len);
+}
+
+void SHA512_Transform(SHA512_CTX *c, const unsigned char *data) {
+#ifndef SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
+  if ((size_t)data % sizeof(c->u.d[0]) != 0) {
+    memcpy(c->u.p, data, sizeof(c->u.p));
+    data = c->u.p;
+  }
+#endif
+  sha512_block_data_order(c, data, 1);
+}
+
+int SHA512_Update(SHA512_CTX *c, const void *in_data, size_t len) {
+  uint64_t l;
+  uint8_t *p = c->u.p;
+  const uint8_t *data = (const uint8_t *)in_data;
+
+  if (len == 0)
+    return 1;
+
+  l = (c->Nl + (((uint64_t)len) << 3)) & 0xffffffffffffffff;
+  if (l < c->Nl) {
+    c->Nh++;
+  }
+  if (sizeof(len) >= 8) {
+    c->Nh += (((uint64_t)len) >> 61);
+  }
+  c->Nl = l;
+
+  if (c->num != 0) {
+    size_t n = sizeof(c->u) - c->num;
+
+    if (len < n) {
+      memcpy(p + c->num, data, len);
+      c->num += (unsigned int)len;
+      return 1;
+    } else {
+      memcpy(p + c->num, data, n), c->num = 0;
+      len -= n;
+      data += n;
+      sha512_block_data_order(c, p, 1);
+    }
+  }
+
+  if (len >= sizeof(c->u)) {
+#ifndef SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
+    if ((size_t)data % sizeof(c->u.d[0]) != 0)
+      while (len >= sizeof(c->u))
+        memcpy(p, data, sizeof(c->u)), sha512_block_data_order(c, p, 1),
+            len -= sizeof(c->u), data += sizeof(c->u);
+    else
+#endif
+      sha512_block_data_order(c, data, len / sizeof(c->u)), data += len,
+          len %= sizeof(c->u), data -= len;
+  }
+
+  if (len != 0) {
+    memcpy(p, data, len);
+    c->num = (int)len;
+  }
+
+  return 1;
+}
+
+int SHA512_Final(unsigned char *md, SHA512_CTX *sha) {
+  uint8_t *p = (uint8_t *)sha->u.p;
+  size_t n = sha->num;
+
+  p[n] = 0x80; /* There always is a room for one */
+  n++;
+  if (n > (sizeof(sha->u) - 16)) {
+    memset(p + n, 0, sizeof(sha->u) - n);
+    n = 0;
+    sha512_block_data_order(sha, p, 1);
+  }
+
+  memset(p + n, 0, sizeof(sha->u) - 16 - n);
+  p[sizeof(sha->u) - 1] = (uint8_t)(sha->Nl);
+  p[sizeof(sha->u) - 2] = (uint8_t)(sha->Nl >> 8);
+  p[sizeof(sha->u) - 3] = (uint8_t)(sha->Nl >> 16);
+  p[sizeof(sha->u) - 4] = (uint8_t)(sha->Nl >> 24);
+  p[sizeof(sha->u) - 5] = (uint8_t)(sha->Nl >> 32);
+  p[sizeof(sha->u) - 6] = (uint8_t)(sha->Nl >> 40);
+  p[sizeof(sha->u) - 7] = (uint8_t)(sha->Nl >> 48);
+  p[sizeof(sha->u) - 8] = (uint8_t)(sha->Nl >> 56);
+  p[sizeof(sha->u) - 9] = (uint8_t)(sha->Nh);
+  p[sizeof(sha->u) - 10] = (uint8_t)(sha->Nh >> 8);
+  p[sizeof(sha->u) - 11] = (uint8_t)(sha->Nh >> 16);
+  p[sizeof(sha->u) - 12] = (uint8_t)(sha->Nh >> 24);
+  p[sizeof(sha->u) - 13] = (uint8_t)(sha->Nh >> 32);
+  p[sizeof(sha->u) - 14] = (uint8_t)(sha->Nh >> 40);
+  p[sizeof(sha->u) - 15] = (uint8_t)(sha->Nh >> 48);
+  p[sizeof(sha->u) - 16] = (uint8_t)(sha->Nh >> 56);
+
+  sha512_block_data_order(sha, p, 1);
+
+  if (md == 0) {
+    return 0;
+  }
+
+  switch (sha->md_len) {
+    /* Let compiler decide if it's appropriate to unroll... */
+    case SHA384_DIGEST_LENGTH:
+      for (n = 0; n < SHA384_DIGEST_LENGTH / 8; n++) {
+        uint64_t t = sha->h[n];
+
+        *(md++) = (uint8_t)(t >> 56);
+        *(md++) = (uint8_t)(t >> 48);
+        *(md++) = (uint8_t)(t >> 40);
+        *(md++) = (uint8_t)(t >> 32);
+        *(md++) = (uint8_t)(t >> 24);
+        *(md++) = (uint8_t)(t >> 16);
+        *(md++) = (uint8_t)(t >> 8);
+        *(md++) = (uint8_t)(t);
+      }
+      break;
+    case SHA512_DIGEST_LENGTH:
+      for (n = 0; n < SHA512_DIGEST_LENGTH / 8; n++) {
+        uint64_t t = sha->h[n];
+
+        *(md++) = (uint8_t)(t >> 56);
+        *(md++) = (uint8_t)(t >> 48);
+        *(md++) = (uint8_t)(t >> 40);
+        *(md++) = (uint8_t)(t >> 32);
+        *(md++) = (uint8_t)(t >> 24);
+        *(md++) = (uint8_t)(t >> 16);
+        *(md++) = (uint8_t)(t >> 8);
+        *(md++) = (uint8_t)(t);
+      }
+      break;
+    /* ... as well as make sure md_len is not abused. */
+    default:
+      return 0;
+  }
+
+  return 1;
+}
+
+#ifndef SHA512_ASM
+static const uint64_t K512[80] = {
+    0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f,
+    0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019,
+    0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242,
+    0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+    0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
+    0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3,
+    0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275,
+    0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+    0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f,
+    0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
+    0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc,
+    0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+    0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6,
+    0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001,
+    0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
+    0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+    0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99,
+    0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb,
+    0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc,
+    0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+    0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915,
+    0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207,
+    0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba,
+    0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+    0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
+    0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
+    0x5fcb6fab3ad6faec, 0x6c44198c4a475817};
+
+#if defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64) || defined(__x86_64__)
+#define ROTR(a, n)                                         \
+  ({                                                       \
+    uint64_t ret;                                          \
+    asm("rorq %1,%0" : "=r"(ret) : "J"(n), "0"(a) : "cc"); \
+    ret;                                                   \
+  })
+#define PULL64(x)                               \
+  ({                                            \
+    uint64_t ret = *((const uint64_t *)(&(x))); \
+    asm("bswapq	%0" : "=r"(ret) : "0"(ret));    \
+    ret;                                        \
+  })
+#elif(defined(__i386) || defined(__i386__))
+#define PULL64(x)                                                         \
+  ({                                                                      \
+    const unsigned int *p = (const unsigned int *)(&(x));                 \
+    unsigned int hi = p[0], lo = p[1];                                    \
+    asm("bswapl %0; bswapl %1;" : "=r"(lo), "=r"(hi) : "0"(lo), "1"(hi)); \
+    ((uint64_t)hi) << 32 | lo;                                            \
+  })
+#elif(defined(_ARCH_PPC) && defined(__64BIT__)) || defined(_ARCH_PPC64)
+#define ROTR(a, n)                                       \
+  ({                                                     \
+    uint64_t ret;                                        \
+    asm("rotrdi %0,%1,%2" : "=r"(ret) : "r"(a), "K"(n)); \
+    ret;                                                 \
+  })
+#elif defined(__aarch64__)
+#define ROTR(a, n)                                    \
+  ({                                                  \
+    uint64_t ret;                                     \
+    asm("ror %0,%1,%2" : "=r"(ret) : "r"(a), "I"(n)); \
+    ret;                                              \
+  })
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+    __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define PULL64(x)                                                       \
+  ({                                                                    \
+    uint64_t ret;                                                       \
+    asm("rev	%0,%1" : "=r"(ret) : "r"(*((const uint64_t *)(&(x))))); \
+    ret;                                                                \
+  })
+#endif
+#endif
+#elif defined(_MSC_VER)
+#if defined(_WIN64) /* applies to both IA-64 and AMD64 */
+#pragma intrinsic(_rotr64)
+#define ROTR(a, n) _rotr64((a), n)
+#endif
+#if defined(_M_IX86) && !defined(OPENSSL_NO_ASM)
+static uint64_t __fastcall __pull64be(const void *x) {
+  _asm mov edx, [ecx + 0]
+  _asm mov eax, [ecx + 4]
+  _asm bswap edx
+  _asm bswap eax
+}
+#define PULL64(x) __pull64be(&(x))
+#if _MSC_VER <= 1200
+#pragma inline_depth(0)
+#endif
+#endif
+#endif
+
+#ifndef PULL64
+#define B(x, j) \
+  (((uint64_t)(*(((const unsigned char *)(&x)) + j))) << ((7 - j) * 8))
+#define PULL64(x)                                                        \
+  (B(x, 0) | B(x, 1) | B(x, 2) | B(x, 3) | B(x, 4) | B(x, 5) | B(x, 6) | \
+   B(x, 7))
+#endif
+
+#ifndef ROTR
+#define ROTR(x, s) (((x) >> s) | (x) << (64 - s))
+#endif
+
+#define Sigma0(x) (ROTR((x), 28) ^ ROTR((x), 34) ^ ROTR((x), 39))
+#define Sigma1(x) (ROTR((x), 14) ^ ROTR((x), 18) ^ ROTR((x), 41))
+#define sigma0(x) (ROTR((x), 1) ^ ROTR((x), 8) ^ ((x) >> 7))
+#define sigma1(x) (ROTR((x), 19) ^ ROTR((x), 61) ^ ((x) >> 6))
+
+#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+
+#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
+/*
+ * This code should give better results on 32-bit CPU with less than
+ * ~24 registers, both size and performance wise...
+ */
+static void sha512_block_data_order(SHA512_CTX *ctx, const void *in,
+                                    size_t num) {
+  const uint64_t *W = in;
+  uint64_t A, E, T;
+  uint64_t X[9 + 80], *F;
+  int i;
+
+  while (num--) {
+    F = X + 80;
+    A = ctx->h[0];
+    F[1] = ctx->h[1];
+    F[2] = ctx->h[2];
+    F[3] = ctx->h[3];
+    E = ctx->h[4];
+    F[5] = ctx->h[5];
+    F[6] = ctx->h[6];
+    F[7] = ctx->h[7];
+
+    for (i = 0; i < 16; i++, F--) {
+      T = PULL64(W[i]);
+      F[0] = A;
+      F[4] = E;
+      F[8] = T;
+      T += F[7] + Sigma1(E) + Ch(E, F[5], F[6]) + K512[i];
+      E = F[3] + T;
+      A = T + Sigma0(A) + Maj(A, F[1], F[2]);
+    }
+
+    for (; i < 80; i++, F--) {
+      T = sigma0(F[8 + 16 - 1]);
+      T += sigma1(F[8 + 16 - 14]);
+      T += F[8 + 16] + F[8 + 16 - 9];
+
+      F[0] = A;
+      F[4] = E;
+      F[8] = T;
+      T += F[7] + Sigma1(E) + Ch(E, F[5], F[6]) + K512[i];
+      E = F[3] + T;
+      A = T + Sigma0(A) + Maj(A, F[1], F[2]);
+    }
+
+    ctx->h[0] += A;
+    ctx->h[1] += F[1];
+    ctx->h[2] += F[2];
+    ctx->h[3] += F[3];
+    ctx->h[4] += E;
+    ctx->h[5] += F[5];
+    ctx->h[6] += F[6];
+    ctx->h[7] += F[7];
+
+    W += 16;
+  }
+}
+
+#else
+
+#define ROUND_00_15(i, a, b, c, d, e, f, g, h)   \
+  do {                                           \
+    T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i]; \
+    h = Sigma0(a) + Maj(a, b, c);                \
+    d += T1;                                     \
+    h += T1;                                     \
+  } while (0)
+
+#define ROUND_16_80(i, j, a, b, c, d, e, f, g, h, X)   \
+  do {                                                 \
+    s0 = X[(j + 1) & 0x0f];                            \
+    s0 = sigma0(s0);                                   \
+    s1 = X[(j + 14) & 0x0f];                           \
+    s1 = sigma1(s1);                                   \
+    T1 = X[(j) & 0x0f] += s0 + s1 + X[(j + 9) & 0x0f]; \
+    ROUND_00_15(i + j, a, b, c, d, e, f, g, h);        \
+  } while (0)
+
+static void sha512_block_data_order(SHA512_CTX *ctx, const void *in,
+                                    size_t num) {
+  const uint64_t *W = in;
+  uint64_t a, b, c, d, e, f, g, h, s0, s1, T1;
+  uint64_t X[16];
+  int i;
+
+  while (num--) {
+
+    a = ctx->h[0];
+    b = ctx->h[1];
+    c = ctx->h[2];
+    d = ctx->h[3];
+    e = ctx->h[4];
+    f = ctx->h[5];
+    g = ctx->h[6];
+    h = ctx->h[7];
+
+    T1 = X[0] = PULL64(W[0]);
+    ROUND_00_15(0, a, b, c, d, e, f, g, h);
+    T1 = X[1] = PULL64(W[1]);
+    ROUND_00_15(1, h, a, b, c, d, e, f, g);
+    T1 = X[2] = PULL64(W[2]);
+    ROUND_00_15(2, g, h, a, b, c, d, e, f);
+    T1 = X[3] = PULL64(W[3]);
+    ROUND_00_15(3, f, g, h, a, b, c, d, e);
+    T1 = X[4] = PULL64(W[4]);
+    ROUND_00_15(4, e, f, g, h, a, b, c, d);
+    T1 = X[5] = PULL64(W[5]);
+    ROUND_00_15(5, d, e, f, g, h, a, b, c);
+    T1 = X[6] = PULL64(W[6]);
+    ROUND_00_15(6, c, d, e, f, g, h, a, b);
+    T1 = X[7] = PULL64(W[7]);
+    ROUND_00_15(7, b, c, d, e, f, g, h, a);
+    T1 = X[8] = PULL64(W[8]);
+    ROUND_00_15(8, a, b, c, d, e, f, g, h);
+    T1 = X[9] = PULL64(W[9]);
+    ROUND_00_15(9, h, a, b, c, d, e, f, g);
+    T1 = X[10] = PULL64(W[10]);
+    ROUND_00_15(10, g, h, a, b, c, d, e, f);
+    T1 = X[11] = PULL64(W[11]);
+    ROUND_00_15(11, f, g, h, a, b, c, d, e);
+    T1 = X[12] = PULL64(W[12]);
+    ROUND_00_15(12, e, f, g, h, a, b, c, d);
+    T1 = X[13] = PULL64(W[13]);
+    ROUND_00_15(13, d, e, f, g, h, a, b, c);
+    T1 = X[14] = PULL64(W[14]);
+    ROUND_00_15(14, c, d, e, f, g, h, a, b);
+    T1 = X[15] = PULL64(W[15]);
+    ROUND_00_15(15, b, c, d, e, f, g, h, a);
+
+    for (i = 16; i < 80; i += 16) {
+      ROUND_16_80(i, 0, a, b, c, d, e, f, g, h, X);
+      ROUND_16_80(i, 1, h, a, b, c, d, e, f, g, X);
+      ROUND_16_80(i, 2, g, h, a, b, c, d, e, f, X);
+      ROUND_16_80(i, 3, f, g, h, a, b, c, d, e, X);
+      ROUND_16_80(i, 4, e, f, g, h, a, b, c, d, X);
+      ROUND_16_80(i, 5, d, e, f, g, h, a, b, c, X);
+      ROUND_16_80(i, 6, c, d, e, f, g, h, a, b, X);
+      ROUND_16_80(i, 7, b, c, d, e, f, g, h, a, X);
+      ROUND_16_80(i, 8, a, b, c, d, e, f, g, h, X);
+      ROUND_16_80(i, 9, h, a, b, c, d, e, f, g, X);
+      ROUND_16_80(i, 10, g, h, a, b, c, d, e, f, X);
+      ROUND_16_80(i, 11, f, g, h, a, b, c, d, e, X);
+      ROUND_16_80(i, 12, e, f, g, h, a, b, c, d, X);
+      ROUND_16_80(i, 13, d, e, f, g, h, a, b, c, X);
+      ROUND_16_80(i, 14, c, d, e, f, g, h, a, b, X);
+      ROUND_16_80(i, 15, b, c, d, e, f, g, h, a, X);
+    }
+
+    ctx->h[0] += a;
+    ctx->h[1] += b;
+    ctx->h[2] += c;
+    ctx->h[3] += d;
+    ctx->h[4] += e;
+    ctx->h[5] += f;
+    ctx->h[6] += g;
+    ctx->h[7] += h;
+
+    W += 16;
+  }
+}
+
+#endif
+
+#endif /* SHA512_ASM */
diff --git a/crypto/stack/CMakeLists.txt b/crypto/stack/CMakeLists.txt
new file mode 100644
index 0000000..62645c5
--- /dev/null
+++ b/crypto/stack/CMakeLists.txt
@@ -0,0 +1,9 @@
+include_directories(. .. ../../include)
+
+add_library(
+	stack
+
+	OBJECT
+
+	stack.c
+)
diff --git a/crypto/stack/make_macros.sh b/crypto/stack/make_macros.sh
new file mode 100644
index 0000000..84ca478
--- /dev/null
+++ b/crypto/stack/make_macros.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+cat > stack_macros.h << EOF
+/* Copyright (c) 2014, 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(IN_STACK_H)
+#error "Don't include this file directly. Include stack.h."
+#endif
+
+EOF
+
+output_stack () {
+  type=$1
+  star=$2
+
+  cat >> stack_macros.h << EOF
+/* ${type} */
+#define sk_${type}_new(comp)\\
+  ((STACK_OF(${type})*) sk_new(CHECKED_CAST(stack_cmp_func, int (*) (const ${type} *${star}a, const ${type} *${star}b), comp)))
+
+#define sk_${type}_new_null()\\
+  ((STACK_OF(${type})*) sk_new_null())
+
+#define sk_${type}_num(sk)\\
+  sk_num(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk))
+
+#define sk_${type}_zero(sk)\\
+  sk_zero(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk));
+
+#define sk_${type}_value(sk, i)\\
+  ((${type}${star}) sk_value(CHECKED_CAST(_STACK*, const STACK_OF(${type})*, sk), (i)))
+
+#define sk_${type}_set(sk, i, p)\\
+  ((${type}${star}) sk_set(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), (i), CHECKED_CAST(void*, ${type}${star}, p)))
+
+#define sk_${type}_free(sk)\\
+  sk_free(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk))
+
+#define sk_${type}_pop_free(sk, free_func)\\
+  sk_pop_free(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), CHECKED_CAST(void (*) (void*), void (*) (${type}${star}), free_func))
+
+#define sk_${type}_insert(sk, p, where)\\
+  sk_insert(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), CHECKED_CAST(void*, ${type}${star}, p), (where))
+
+#define sk_${type}_delete(sk, where)\\
+  ((${type}${star}) sk_delete(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), (where)))
+
+#define sk_${type}_delete_ptr(sk, p)\\
+  ((${type}${star}) sk_delete_ptr(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), CHECKED_CAST(void*, ${type}${star}, p)))
+
+#define sk_${type}_find(sk, out_index, p)\\
+  sk_find(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), (out_index), CHECKED_CAST(void*, ${type}${star}, p))
+
+#define sk_${type}_shift(sk)\\
+  ((${type}${star}) sk_shift(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk)))
+
+#define sk_${type}_push(sk, p)\\
+  sk_push(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), CHECKED_CAST(void*, ${type}${star}, p))
+
+#define sk_${type}_pop(sk)\\
+  ((${type}${star}) sk_pop(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk)))
+
+#define sk_${type}_dup(sk)\\
+  ((STACK_OF(${type})*) sk_dup(CHECKED_CAST(_STACK*, const STACK_OF(${type})*, sk)))
+
+#define sk_${type}_sort(sk)\\
+  sk_sort(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk))
+
+#define sk_${type}_is_sorted(sk)\\
+  sk_is_sorted(CHECKED_CAST(_STACK*, const STACK_OF(${type})*, sk))
+
+#define sk_${type}_set_cmp_func(sk, comp)\\
+  ((int (*) (const ${type} **a, const ${type} **b)) sk_set_cmp_func(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), CHECKED_CAST(stack_cmp_func, int (*) (const ${type} **a, const ${type} **b), comp)))
+
+
+EOF
+}
+
+stack_types=$(cat stack.h | grep '^ \* STACK_OF:' | sed -e 's/.*STACK_OF://' -e 's/ .*//')
+special_stack_types=$(cat stack.h | grep '^ \* SPECIAL_STACK_OF:' | sed -e 's/.*SPECIAL_STACK_OF://' -e 's/ .*//')
+
+for type in $stack_types; do
+  echo Stack of ${type}
+  output_stack "${type}" "*"
+done
+
+for type in $special_stack_types; do
+  echo Stack of ${type}
+  output_stack "${type}" ""
+done
+
+clang-format -i stack_macros.h
diff --git a/crypto/stack/stack.c b/crypto/stack/stack.c
new file mode 100644
index 0000000..074b1e1
--- /dev/null
+++ b/crypto/stack/stack.c
@@ -0,0 +1,360 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include "stack.h"
+
+#include <openssl/mem.h>
+
+/* kMinSize is the number of pointers that will be initially allocated in a new
+ * stack. */
+static const size_t kMinSize = 4;
+
+_STACK *sk_new(stack_cmp_func comp) {
+  _STACK *ret;
+
+  ret = OPENSSL_malloc(sizeof(_STACK));
+  if (ret == NULL) {
+    goto err;
+  }
+  memset(ret, 0, sizeof(_STACK));
+
+  ret->data = OPENSSL_malloc(sizeof(void *) * kMinSize);
+  if (ret->data == NULL) {
+    goto err;
+  }
+
+  memset(ret->data, 0, sizeof(void *) * kMinSize);
+
+  ret->comp = comp;
+  ret->num_alloc = kMinSize;
+
+  return ret;
+
+err:
+  if (ret) {
+    OPENSSL_free(ret);
+  }
+  return NULL;
+}
+
+_STACK *sk_new_null(void) { return sk_new(NULL); }
+
+size_t sk_num(const _STACK *sk) {
+  if (sk == NULL) {
+    return 0;
+  }
+  return sk->num;
+}
+
+void sk_zero(_STACK *sk) {
+  if (sk == NULL || sk->num == 0) {
+    return;
+  }
+  memset(sk->data, 0, sizeof(void*) * sk->num);
+  sk->num = 0;
+  sk->sorted = 0;
+}
+
+void *sk_value(const _STACK *sk, size_t i) {
+  if (!sk || i >= sk->num) {
+    return NULL;
+  }
+  return sk->data[i];
+}
+
+void *sk_set(_STACK *sk, size_t i, void *value) {
+  if (!sk || i >= sk->num) {
+    return NULL;
+  }
+  return sk->data[i] = value;
+}
+
+void sk_free(_STACK *sk) {
+  if (sk == NULL) {
+    return;
+  }
+  OPENSSL_free(sk->data);
+  OPENSSL_free(sk);
+}
+
+void sk_pop_free(_STACK *sk, void (*func)(void *)) {
+  size_t i;
+
+  if (sk == NULL) {
+    return;
+  }
+
+  for (i = 0; i < sk->num; i++) {
+    if (sk->data[i] != NULL) {
+      func(sk->data[i]);
+    }
+  }
+  sk_free(sk);
+}
+
+size_t sk_insert(_STACK *sk, void *p, size_t where) {
+  if (sk == NULL) {
+    return 0;
+  }
+
+  if (sk->num_alloc <= sk->num + 1) {
+    /* Attempt to double the size of the array. */
+    size_t new_alloc = sk->num_alloc << 1;
+    size_t alloc_size = new_alloc * sizeof(void *);
+    void **data;
+
+    /* If the doubling overflowed, try to increment. */
+    if (new_alloc < sk->num_alloc || alloc_size / sizeof(void *) != new_alloc) {
+      new_alloc = sk->num_alloc + 1;
+      alloc_size = new_alloc * sizeof(void *);
+    }
+
+    /* If the increment also overflowed, fail. */
+    if (new_alloc < sk->num_alloc || alloc_size / sizeof(void *) != new_alloc) {
+      return 0;
+    }
+
+    data = OPENSSL_realloc(sk->data, alloc_size);
+    if (data == NULL) {
+      return 0;
+    }
+
+    sk->data = data;
+    sk->num_alloc = new_alloc;
+  }
+
+  if (where >= sk->num) {
+    sk->data[sk->num] = p;
+  } else {
+    memmove(&sk->data[where + 1], &sk->data[where],
+            sizeof(void *) * (sk->num - where));
+    sk->data[where] = p;
+  }
+
+  sk->num++;
+  sk->sorted = 0;
+
+  return sk->num;
+}
+
+void *sk_delete(_STACK *sk, size_t where) {
+  void *ret;
+
+  if (!sk || where >= sk->num) {
+    return NULL;
+  }
+
+  ret = sk->data[where];
+
+  if (where != sk->num - 1) {
+    memmove(&sk->data[where], &sk->data[where + 1],
+            sizeof(void *) * (sk->num - where - 1));
+  }
+
+  sk->num--;
+  return ret;
+}
+
+void *sk_delete_ptr(_STACK *sk, void *p) {
+  size_t i;
+
+  if (sk == NULL) {
+    return NULL;
+  }
+
+  for (i = 0; i < sk->num; i++) {
+    if (sk->data[i] == p) {
+      return sk_delete(sk, i);
+    }
+  }
+
+  return NULL;
+}
+
+int sk_find(_STACK *sk, size_t *out_index, void *p) {
+  const void *const *r;
+  size_t i;
+  int (*comp_func)(const void *,const void *);
+
+  if (sk == NULL) {
+    return -1;
+  }
+
+  if (sk->comp == NULL) {
+    /* Use pointer equality when no comparison function has been set. */
+    for (i = 0; i < sk->num; i++) {
+      if (sk->data[i] == p) {
+        if (out_index) {
+          *out_index = i;
+        }
+        return 1;
+      }
+    }
+    return 0;
+  }
+
+  if (p == NULL) {
+    return 0;
+  }
+
+  sk_sort(sk);
+
+  /* sk->comp is a function that takes pointers to pointers to elements, but
+   * qsort and bsearch take a comparison function that just takes pointers to
+   * elements. However, since we're passing an array of pointers to
+   * qsort/bsearch, we can just cast the comparison function and everything
+   * works. */
+  comp_func=(int (*)(const void *,const void *))(sk->comp);
+  r = bsearch(&p, sk->data, sk->num, sizeof(void *), comp_func);
+  if (r == NULL) {
+    return 0;
+  }
+  i = ((void **)r) - sk->data;
+  /* This function always returns the first result. */
+  while (i > 0 && sk->comp((const void**) &p, (const void**) &sk->data[i-1]) == 0) {
+    i--;
+  }
+  if (out_index) {
+    *out_index = i;
+  }
+  return 1;
+}
+
+void *sk_shift(_STACK *sk) {
+  if (sk == NULL) {
+    return NULL;
+  }
+  if (sk->num == 0) {
+    return NULL;
+  }
+  return sk_delete(sk, 0);
+}
+
+size_t sk_push(_STACK *sk, void *p) { return (sk_insert(sk, p, sk->num)); }
+
+void *sk_pop(_STACK *sk) {
+  if (sk == NULL) {
+    return NULL;
+  }
+  if (sk->num == 0) {
+    return NULL;
+  }
+  return sk_delete(sk, sk->num - 1);
+}
+
+_STACK *sk_dup(const _STACK *sk) {
+  _STACK *ret;
+  void **s;
+
+  if (sk == NULL) {
+    return NULL;
+  }
+
+  ret = sk_new(sk->comp);
+  if (ret == NULL) {
+    goto err;
+  }
+
+  s = (void **)OPENSSL_realloc(ret->data, sizeof(void *) * sk->num_alloc);
+  if (s == NULL) {
+    goto err;
+  }
+  ret->data = s;
+
+  ret->num = sk->num;
+  memcpy(ret->data, sk->data, sizeof(void *) * sk->num);
+  ret->sorted = sk->sorted;
+  ret->num_alloc = sk->num_alloc;
+  ret->comp = sk->comp;
+  return ret;
+
+err:
+  if (ret) {
+    sk_free(ret);
+  }
+  return NULL;
+}
+
+void sk_sort(_STACK *sk) {
+  int (*comp_func)(const void *,const void *);
+
+  if (sk == NULL || sk->sorted) {
+    return;
+  }
+
+  /* See the comment in sk_find about this cast. */
+  comp_func = (int (*)(const void *, const void *))(sk->comp);
+  qsort(sk->data, sk->num, sizeof(void *), comp_func);
+  sk->sorted = 1;
+}
+
+int sk_is_sorted(const _STACK *sk) {
+  if (!sk) {
+    return 1;
+  }
+  return sk->sorted;
+}
+
+stack_cmp_func sk_set_cmp_func(_STACK *sk, stack_cmp_func comp) {
+  stack_cmp_func old = sk->comp;
+
+  if (sk->comp != comp) {
+    sk->sorted = 0;
+  }
+  sk->comp = comp;
+
+  return old;
+}
diff --git a/crypto/stack/stack.h b/crypto/stack/stack.h
new file mode 100644
index 0000000..53bf167
--- /dev/null
+++ b/crypto/stack/stack.h
@@ -0,0 +1,291 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_STACK_H
+#define OPENSSL_HEADER_STACK_H
+
+#include <openssl/base.h>
+
+#include <openssl/type_check.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* A stack, in OpenSSL, is an array of pointers. They are the most commonly
+ * used collection object.
+ *
+ * This file defines macros for type safe use of the stack functions. A stack
+ * of a specific type of object has type |STACK_OF(type)|. This can be defined
+ * (once) with |DEFINE_STACK_OF(type)| and declared where needed with
+ * |DECLARE_STACK_OF(type)|. For example:
+ *
+ *   struct foo {
+ *     int bar;
+ *   };
+ *
+ *   DEFINE_STACK_OF(struct foo);
+ *
+ * Although note that the stack will contain /pointers/ to |foo|.
+ *
+ * A macro will be defined for each of the sk_* functions below. For
+ * STACK_OF(foo), the macros would be sk_foo_new, sk_foo_pop etc. */
+
+
+/* stack_cmp_func is a comparision 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 comparision function. */
+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
+ * directly, rather the wrapper macros should be used. */
+typedef struct stack_st {
+  /* num contains the number of valid pointers in |data|. */
+  size_t num;
+  void **data;
+  /* sorted is non-zero if the values pointed to by |data| are in ascending
+   * order, based on |comp|. */
+  size_t sorted;
+  /* num_alloc contains the number of pointers allocated in the buffer pointed
+   * to by |data|, which may be larger than |num|. */
+  size_t num_alloc;
+  /* comp is an optional comparision function. */
+  stack_cmp_func comp;
+} _STACK;
+
+
+#define STACK_OF(type) struct stack_st_##type
+
+#define DEFINE_STACK_OF(type) \
+STACK_OF(type) {\
+  _STACK stack; \
+};
+
+#define DECLARE_STACK_OF(type) STACK_OF(type);
+
+/* The make_macros.sh script in this directory parses the following lines and
+ * generates the stack_macros.h file that contains macros for the following
+ * types of stacks:
+ *
+ * STACK_OF:ACCESS_DESCRIPTION
+ * STACK_OF:ASN1_ADB_TABLE
+ * STACK_OF:ASN1_GENERALSTRING
+ * STACK_OF:ASN1_INTEGER
+ * STACK_OF:ASN1_OBJECT
+ * STACK_OF:ASN1_STRING_TABLE
+ * STACK_OF:ASN1_TYPE
+ * STACK_OF:ASN1_VALUE
+ * STACK_OF:BIO
+ * STACK_OF:BY_DIR_ENTRY
+ * STACK_OF:BY_DIR_HASH
+ * STACK_OF:CONF_VALUE
+ * STACK_OF:CRYPTO_EX_DATA_FUNCS
+ * STACK_OF:DIST_POINT
+ * STACK_OF:GENERAL_NAME
+ * STACK_OF:GENERAL_NAMES
+ * STACK_OF:GENERAL_SUBTREE
+ * STACK_OF:MIME_HEADER
+ * STACK_OF:PKCS7_SIGNER_INFO
+ * STACK_OF:PKCS7_RECIP_INFO
+ * STACK_OF:POLICYINFO
+ * STACK_OF:POLICYQUALINFO
+ * STACK_OF:POLICY_MAPPING
+ * STACK_OF:SRTP_PROTECTION_PROFILE
+ * STACK_OF:SSL_CIPHER
+ * STACK_OF:SSL_COMP
+ * STACK_OF:STACK_OF_X509_NAME_ENTRY
+ * STACK_OF:SXNETID
+ * STACK_OF:X509
+ * STACK_OF:X509V3_EXT_METHOD
+ * STACK_OF:X509_ALGOR
+ * STACK_OF:X509_ATTRIBUTE
+ * STACK_OF:X509_CRL
+ * STACK_OF:X509_EXTENSION
+ * STACK_OF:X509_INFO
+ * STACK_OF:X509_LOOKUP
+ * STACK_OF:X509_NAME
+ * STACK_OF:X509_NAME_ENTRY
+ * STACK_OF:X509_NAME_ENTRY
+ * STACK_OF:X509_OBJECT
+ * STACK_OF:X509_POLICY_DATA
+ * STACK_OF:X509_POLICY_NODE
+ * STACK_OF:X509_PURPOSE
+ * STACK_OF:X509_REVOKED
+ * STACK_OF:X509_TRUST
+ * STACK_OF:X509_VERIFY_PARAM
+ * STACK_OF:void */
+
+
+/* Some stacks are special because, although we would like STACK_OF(char *),
+ * that would actually be a stack of pointers to char*, but we just want to
+ * point to the string directly. In this case we call them "special" and use
+ * |DEFINE_SPECIAL_STACK_OF(type)| */
+#define DEFINE_SPECIAL_STACK_OF(type, inner)             \
+  STACK_OF(type) { _STACK special_stack; };              \
+  OPENSSL_COMPILE_ASSERT(sizeof(type) == sizeof(void *), \
+                         special_stack_of_non_pointer_##type);
+
+typedef char *OPENSSL_STRING;
+
+DEFINE_SPECIAL_STACK_OF(OPENSSL_STRING, char)
+DEFINE_SPECIAL_STACK_OF(OPENSSL_BLOCK, uint8_t)
+
+/* The make_macros.sh script in this directory parses the following lines and
+ * generates the stack_macros.h file that contains macros for the following
+ * types of stacks:
+ *
+ * SPECIAL_STACK_OF:OPENSSL_STRING
+ * SPECIAL_STACK_OF:OPENSSL_BLOCK */
+
+#define IN_STACK_H
+#include <openssl/stack_macros.h>
+#undef IN_STACK_H
+
+
+/* These are the raw stack functions, you shouldn't be using them. Rather you
+ * should be using the type stack macros implemented above. */
+
+/* sk_new creates a new, empty stack with the given comparision function, which
+ * may be zero. It returns the new stack or NULL on allocation failure. */
+_STACK *sk_new(stack_cmp_func comp);
+
+/* sk_new_null creates a new, empty stack. It returns the new stack or NULL on
+ * allocation failure. */
+_STACK *sk_new_null(void);
+
+/* sk_num returns the number of elements in |s|. */
+size_t sk_num(const _STACK *sk);
+
+/* sk_zero resets |sk| to the empty state but does nothing to free the
+ * individual elements themselves. */
+void sk_zero(_STACK *sk);
+
+/* sk_value returns the |i|th pointer in |sk|, or NULL if |i| is out of
+ * range. */
+void *sk_value(const _STACK *sk, size_t i);
+
+/* sk_set sets the |i|th pointer in |sk| to |p| and returns |p|. If |i| is out
+ * of range, it returns NULL. */
+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|. */
+void sk_free(_STACK *sk);
+
+/* sk_pop_free calls |free_func| on each element in the stack and then frees
+ * the stack itself. */
+void sk_pop_free(_STACK *sk, void (*free_func)(void *));
+
+/* 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
+ * error. */
+size_t sk_insert(_STACK *sk, void *p, size_t where);
+
+/* sk_delete removes the pointer at index |where|, moving other elements down
+ * if needed. It returns the removed pointer, or NULL if |where| is out of
+ * range. */
+void *sk_delete(_STACK *sk, size_t where);
+
+/* 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. */
+void *sk_delete_ptr(_STACK *sk, void *p);
+
+/* sk_find returns the first value in the stack equal to |p|. If a comparision
+ * function has been set on the stack, then equality is defined by it and the
+ * stack will be sorted if need be so that a binary search can be used.
+ * Otherwise pointer equality is used. If a matching element is found, its
+ * index is written to |*out_index| (if |out_index| is not NULL) and one is
+ * returned. Otherwise zero is returned. */
+int sk_find(_STACK *sk, size_t *out_index, void *p);
+
+/* sk_shift removes and returns the first element in the stack, or returns NULL
+ * if the stack is empty. */
+void *sk_shift(_STACK *sk);
+
+/* sk_push appends |p| to the stack and returns the length of the new stack, or
+ * 0 on allocation failure. */
+size_t sk_push(_STACK *sk, void *p);
+
+/* sk_pop returns and removes the last element on the stack, or NULL if the
+ * stack is empty. */
+void *sk_pop(_STACK *sk);
+
+/* sk_dup performs a shallow copy of a stack and returns the new stack, or NULL
+ * on error. */
+_STACK *sk_dup(const _STACK *sk);
+
+/* sk_sort sorts the elements of |sk| into ascending order based on the
+ * comparison function. The stack maintains a |sorted| flag and sorting an
+ * already sorted stack is a no-op. */
+void sk_sort(_STACK *sk);
+
+/* sk_is_sorted returns one if |sk| is known to be sorted and zero
+ * otherwise. */
+int sk_is_sorted(const _STACK *sk);
+
+/* sk_set_cmp_func sets the comparison function to be used by |sk| and returns
+ * the previous one. */
+stack_cmp_func sk_set_cmp_func(_STACK *sk, stack_cmp_func comp);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_STACK_H */
diff --git a/crypto/stack/stack_macros.h b/crypto/stack/stack_macros.h
new file mode 100644
index 0000000..52924c8
--- /dev/null
+++ b/crypto/stack/stack_macros.h
@@ -0,0 +1,3731 @@
+/* Copyright (c) 2014, 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(IN_STACK_H)
+#error "Don't include this file directly. Include stack.h."
+#endif
+
+/* ACCESS_DESCRIPTION */
+#define sk_ACCESS_DESCRIPTION_new(comp)                                    \
+  ((STACK_OF(ACCESS_DESCRIPTION) *)sk_new(CHECKED_CAST(                    \
+      stack_cmp_func,                                                      \
+      int (*)(const ACCESS_DESCRIPTION **a, const ACCESS_DESCRIPTION **b), \
+      comp)))
+
+#define sk_ACCESS_DESCRIPTION_new_null() \
+  ((STACK_OF(ACCESS_DESCRIPTION) *)sk_new_null())
+
+#define sk_ACCESS_DESCRIPTION_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk))
+
+#define sk_ACCESS_DESCRIPTION_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk));
+
+#define sk_ACCESS_DESCRIPTION_value(sk, i) \
+  ((ACCESS_DESCRIPTION *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk), (i)))
+
+#define sk_ACCESS_DESCRIPTION_set(sk, i, p)                            \
+  ((ACCESS_DESCRIPTION *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk), (i), \
+      CHECKED_CAST(void *, ACCESS_DESCRIPTION *, p)))
+
+#define sk_ACCESS_DESCRIPTION_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk))
+
+#define sk_ACCESS_DESCRIPTION_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(ACCESS_DESCRIPTION *), \
+                           free_func))
+
+#define sk_ACCESS_DESCRIPTION_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk), \
+            CHECKED_CAST(void *, ACCESS_DESCRIPTION *, p), (where))
+
+#define sk_ACCESS_DESCRIPTION_delete(sk, where) \
+  ((ACCESS_DESCRIPTION *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk), (where)))
+
+#define sk_ACCESS_DESCRIPTION_delete_ptr(sk, p)                   \
+  ((ACCESS_DESCRIPTION *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk), \
+      CHECKED_CAST(void *, ACCESS_DESCRIPTION *, p)))
+
+#define sk_ACCESS_DESCRIPTION_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk), \
+          (out_index), CHECKED_CAST(void *, ACCESS_DESCRIPTION *, p))
+
+#define sk_ACCESS_DESCRIPTION_shift(sk) \
+  ((ACCESS_DESCRIPTION *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk)))
+
+#define sk_ACCESS_DESCRIPTION_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk), \
+          CHECKED_CAST(void *, ACCESS_DESCRIPTION *, p))
+
+#define sk_ACCESS_DESCRIPTION_pop(sk) \
+  ((ACCESS_DESCRIPTION *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk)))
+
+#define sk_ACCESS_DESCRIPTION_dup(sk)      \
+  ((STACK_OF(ACCESS_DESCRIPTION) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk)))
+
+#define sk_ACCESS_DESCRIPTION_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk))
+
+#define sk_ACCESS_DESCRIPTION_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk))
+
+#define sk_ACCESS_DESCRIPTION_set_cmp_func(sk, comp)                       \
+  ((int (*)(const ACCESS_DESCRIPTION **a, const ACCESS_DESCRIPTION **b))   \
+   sk_set_cmp_func(                                                        \
+       CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const ACCESS_DESCRIPTION **a,  \
+                                            const ACCESS_DESCRIPTION **b), \
+                    comp)))
+
+/* ASN1_ADB_TABLE */
+#define sk_ASN1_ADB_TABLE_new(comp)                 \
+  ((STACK_OF(ASN1_ADB_TABLE) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                               \
+      int (*)(const ASN1_ADB_TABLE **a, const ASN1_ADB_TABLE **b), comp)))
+
+#define sk_ASN1_ADB_TABLE_new_null() ((STACK_OF(ASN1_ADB_TABLE) *)sk_new_null())
+
+#define sk_ASN1_ADB_TABLE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk))
+
+#define sk_ASN1_ADB_TABLE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk));
+
+#define sk_ASN1_ADB_TABLE_value(sk, i) \
+  ((ASN1_ADB_TABLE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk), (i)))
+
+#define sk_ASN1_ADB_TABLE_set(sk, i, p)                            \
+  ((ASN1_ADB_TABLE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), (i), \
+      CHECKED_CAST(void *, ASN1_ADB_TABLE *, p)))
+
+#define sk_ASN1_ADB_TABLE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk))
+
+#define sk_ASN1_ADB_TABLE_pop_free(sk, free_func)             \
+  sk_pop_free(                                                \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(ASN1_ADB_TABLE *), free_func))
+
+#define sk_ASN1_ADB_TABLE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), \
+            CHECKED_CAST(void *, ASN1_ADB_TABLE *, p), (where))
+
+#define sk_ASN1_ADB_TABLE_delete(sk, where) \
+  ((ASN1_ADB_TABLE *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), (where)))
+
+#define sk_ASN1_ADB_TABLE_delete_ptr(sk, p)                   \
+  ((ASN1_ADB_TABLE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), \
+      CHECKED_CAST(void *, ASN1_ADB_TABLE *, p)))
+
+#define sk_ASN1_ADB_TABLE_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), (out_index), \
+          CHECKED_CAST(void *, ASN1_ADB_TABLE *, p))
+
+#define sk_ASN1_ADB_TABLE_shift(sk) \
+  ((ASN1_ADB_TABLE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk)))
+
+#define sk_ASN1_ADB_TABLE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk), \
+          CHECKED_CAST(void *, ASN1_ADB_TABLE *, p))
+
+#define sk_ASN1_ADB_TABLE_pop(sk) \
+  ((ASN1_ADB_TABLE *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk)))
+
+#define sk_ASN1_ADB_TABLE_dup(sk)      \
+  ((STACK_OF(ASN1_ADB_TABLE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk)))
+
+#define sk_ASN1_ADB_TABLE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk))
+
+#define sk_ASN1_ADB_TABLE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk))
+
+#define sk_ASN1_ADB_TABLE_set_cmp_func(sk, comp)                       \
+  ((int (*)(const ASN1_ADB_TABLE **a, const ASN1_ADB_TABLE **b))       \
+   sk_set_cmp_func(                                                    \
+       CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const ASN1_ADB_TABLE **a,  \
+                                            const ASN1_ADB_TABLE **b), \
+                    comp)))
+
+/* ASN1_GENERALSTRING */
+#define sk_ASN1_GENERALSTRING_new(comp)                                    \
+  ((STACK_OF(ASN1_GENERALSTRING) *)sk_new(CHECKED_CAST(                    \
+      stack_cmp_func,                                                      \
+      int (*)(const ASN1_GENERALSTRING **a, const ASN1_GENERALSTRING **b), \
+      comp)))
+
+#define sk_ASN1_GENERALSTRING_new_null() \
+  ((STACK_OF(ASN1_GENERALSTRING) *)sk_new_null())
+
+#define sk_ASN1_GENERALSTRING_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk))
+
+#define sk_ASN1_GENERALSTRING_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk));
+
+#define sk_ASN1_GENERALSTRING_value(sk, i) \
+  ((ASN1_GENERALSTRING *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk), (i)))
+
+#define sk_ASN1_GENERALSTRING_set(sk, i, p)                            \
+  ((ASN1_GENERALSTRING *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk), (i), \
+      CHECKED_CAST(void *, ASN1_GENERALSTRING *, p)))
+
+#define sk_ASN1_GENERALSTRING_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk))
+
+#define sk_ASN1_GENERALSTRING_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(ASN1_GENERALSTRING *), \
+                           free_func))
+
+#define sk_ASN1_GENERALSTRING_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk), \
+            CHECKED_CAST(void *, ASN1_GENERALSTRING *, p), (where))
+
+#define sk_ASN1_GENERALSTRING_delete(sk, where) \
+  ((ASN1_GENERALSTRING *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk), (where)))
+
+#define sk_ASN1_GENERALSTRING_delete_ptr(sk, p)                   \
+  ((ASN1_GENERALSTRING *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk), \
+      CHECKED_CAST(void *, ASN1_GENERALSTRING *, p)))
+
+#define sk_ASN1_GENERALSTRING_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk), \
+          (out_index), CHECKED_CAST(void *, ASN1_GENERALSTRING *, p))
+
+#define sk_ASN1_GENERALSTRING_shift(sk) \
+  ((ASN1_GENERALSTRING *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk)))
+
+#define sk_ASN1_GENERALSTRING_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk), \
+          CHECKED_CAST(void *, ASN1_GENERALSTRING *, p))
+
+#define sk_ASN1_GENERALSTRING_pop(sk) \
+  ((ASN1_GENERALSTRING *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk)))
+
+#define sk_ASN1_GENERALSTRING_dup(sk)      \
+  ((STACK_OF(ASN1_GENERALSTRING) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk)))
+
+#define sk_ASN1_GENERALSTRING_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk))
+
+#define sk_ASN1_GENERALSTRING_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk))
+
+#define sk_ASN1_GENERALSTRING_set_cmp_func(sk, comp)                       \
+  ((int (*)(const ASN1_GENERALSTRING **a, const ASN1_GENERALSTRING **b))   \
+   sk_set_cmp_func(                                                        \
+       CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const ASN1_GENERALSTRING **a,  \
+                                            const ASN1_GENERALSTRING **b), \
+                    comp)))
+
+/* ASN1_INTEGER */
+#define sk_ASN1_INTEGER_new(comp)                                              \
+  ((STACK_OF(ASN1_INTEGER) *)sk_new(CHECKED_CAST(                              \
+      stack_cmp_func, int (*)(const ASN1_INTEGER **a, const ASN1_INTEGER **b), \
+      comp)))
+
+#define sk_ASN1_INTEGER_new_null() ((STACK_OF(ASN1_INTEGER) *)sk_new_null())
+
+#define sk_ASN1_INTEGER_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk))
+
+#define sk_ASN1_INTEGER_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk));
+
+#define sk_ASN1_INTEGER_value(sk, i) \
+  ((ASN1_INTEGER *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_INTEGER) *, sk), (i)))
+
+#define sk_ASN1_INTEGER_set(sk, i, p)                            \
+  ((ASN1_INTEGER *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), (i), \
+      CHECKED_CAST(void *, ASN1_INTEGER *, p)))
+
+#define sk_ASN1_INTEGER_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk))
+
+#define sk_ASN1_INTEGER_pop_free(sk, free_func)             \
+  sk_pop_free(                                              \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(ASN1_INTEGER *), free_func))
+
+#define sk_ASN1_INTEGER_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), \
+            CHECKED_CAST(void *, ASN1_INTEGER *, p), (where))
+
+#define sk_ASN1_INTEGER_delete(sk, where) \
+  ((ASN1_INTEGER *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), (where)))
+
+#define sk_ASN1_INTEGER_delete_ptr(sk, p)                   \
+  ((ASN1_INTEGER *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), \
+      CHECKED_CAST(void *, ASN1_INTEGER *, p)))
+
+#define sk_ASN1_INTEGER_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), (out_index), \
+          CHECKED_CAST(void *, ASN1_INTEGER *, p))
+
+#define sk_ASN1_INTEGER_shift(sk) \
+  ((ASN1_INTEGER *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk)))
+
+#define sk_ASN1_INTEGER_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk), \
+          CHECKED_CAST(void *, ASN1_INTEGER *, p))
+
+#define sk_ASN1_INTEGER_pop(sk) \
+  ((ASN1_INTEGER *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk)))
+
+#define sk_ASN1_INTEGER_dup(sk)      \
+  ((STACK_OF(ASN1_INTEGER) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_INTEGER) *, sk)))
+
+#define sk_ASN1_INTEGER_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk))
+
+#define sk_ASN1_INTEGER_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_INTEGER) *, sk))
+
+#define sk_ASN1_INTEGER_set_cmp_func(sk, comp)                               \
+  ((int (*)(const ASN1_INTEGER **a, const ASN1_INTEGER **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk),                  \
+      CHECKED_CAST(stack_cmp_func,                                           \
+                   int (*)(const ASN1_INTEGER **a, const ASN1_INTEGER **b),  \
+                   comp)))
+
+/* ASN1_OBJECT */
+#define sk_ASN1_OBJECT_new(comp)                                             \
+  ((STACK_OF(ASN1_OBJECT) *)sk_new(CHECKED_CAST(                             \
+      stack_cmp_func, int (*)(const ASN1_OBJECT **a, const ASN1_OBJECT **b), \
+      comp)))
+
+#define sk_ASN1_OBJECT_new_null() ((STACK_OF(ASN1_OBJECT) *)sk_new_null())
+
+#define sk_ASN1_OBJECT_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk))
+
+#define sk_ASN1_OBJECT_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk));
+
+#define sk_ASN1_OBJECT_value(sk, i) \
+  ((ASN1_OBJECT *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_OBJECT) *, sk), (i)))
+
+#define sk_ASN1_OBJECT_set(sk, i, p)                                          \
+  ((ASN1_OBJECT *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), \
+                         (i), CHECKED_CAST(void *, ASN1_OBJECT *, p)))
+
+#define sk_ASN1_OBJECT_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk))
+
+#define sk_ASN1_OBJECT_pop_free(sk, free_func)             \
+  sk_pop_free(                                             \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(ASN1_OBJECT *), free_func))
+
+#define sk_ASN1_OBJECT_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), \
+            CHECKED_CAST(void *, ASN1_OBJECT *, p), (where))
+
+#define sk_ASN1_OBJECT_delete(sk, where) \
+  ((ASN1_OBJECT *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), (where)))
+
+#define sk_ASN1_OBJECT_delete_ptr(sk, p)                   \
+  ((ASN1_OBJECT *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), \
+      CHECKED_CAST(void *, ASN1_OBJECT *, p)))
+
+#define sk_ASN1_OBJECT_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), (out_index), \
+          CHECKED_CAST(void *, ASN1_OBJECT *, p))
+
+#define sk_ASN1_OBJECT_shift(sk) \
+  ((ASN1_OBJECT *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk)))
+
+#define sk_ASN1_OBJECT_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), \
+          CHECKED_CAST(void *, ASN1_OBJECT *, p))
+
+#define sk_ASN1_OBJECT_pop(sk) \
+  ((ASN1_OBJECT *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk)))
+
+#define sk_ASN1_OBJECT_dup(sk)      \
+  ((STACK_OF(ASN1_OBJECT) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_OBJECT) *, sk)))
+
+#define sk_ASN1_OBJECT_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk))
+
+#define sk_ASN1_OBJECT_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_OBJECT) *, sk))
+
+#define sk_ASN1_OBJECT_set_cmp_func(sk, comp)                              \
+  ((int (*)(const ASN1_OBJECT **a, const ASN1_OBJECT **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk),                 \
+      CHECKED_CAST(stack_cmp_func,                                         \
+                   int (*)(const ASN1_OBJECT **a, const ASN1_OBJECT **b),  \
+                   comp)))
+
+/* ASN1_STRING_TABLE */
+#define sk_ASN1_STRING_TABLE_new(comp)                                   \
+  ((STACK_OF(ASN1_STRING_TABLE) *)sk_new(CHECKED_CAST(                   \
+      stack_cmp_func,                                                    \
+      int (*)(const ASN1_STRING_TABLE **a, const ASN1_STRING_TABLE **b), \
+      comp)))
+
+#define sk_ASN1_STRING_TABLE_new_null() \
+  ((STACK_OF(ASN1_STRING_TABLE) *)sk_new_null())
+
+#define sk_ASN1_STRING_TABLE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk))
+
+#define sk_ASN1_STRING_TABLE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk));
+
+#define sk_ASN1_STRING_TABLE_value(sk, i) \
+  ((ASN1_STRING_TABLE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk), (i)))
+
+#define sk_ASN1_STRING_TABLE_set(sk, i, p)                            \
+  ((ASN1_STRING_TABLE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk), (i), \
+      CHECKED_CAST(void *, ASN1_STRING_TABLE *, p)))
+
+#define sk_ASN1_STRING_TABLE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk))
+
+#define sk_ASN1_STRING_TABLE_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(ASN1_STRING_TABLE *), \
+                           free_func))
+
+#define sk_ASN1_STRING_TABLE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk), \
+            CHECKED_CAST(void *, ASN1_STRING_TABLE *, p), (where))
+
+#define sk_ASN1_STRING_TABLE_delete(sk, where) \
+  ((ASN1_STRING_TABLE *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk), (where)))
+
+#define sk_ASN1_STRING_TABLE_delete_ptr(sk, p)                   \
+  ((ASN1_STRING_TABLE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk), \
+      CHECKED_CAST(void *, ASN1_STRING_TABLE *, p)))
+
+#define sk_ASN1_STRING_TABLE_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk), \
+          (out_index), CHECKED_CAST(void *, ASN1_STRING_TABLE *, p))
+
+#define sk_ASN1_STRING_TABLE_shift(sk) \
+  ((ASN1_STRING_TABLE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk)))
+
+#define sk_ASN1_STRING_TABLE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk), \
+          CHECKED_CAST(void *, ASN1_STRING_TABLE *, p))
+
+#define sk_ASN1_STRING_TABLE_pop(sk) \
+  ((ASN1_STRING_TABLE *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk)))
+
+#define sk_ASN1_STRING_TABLE_dup(sk)      \
+  ((STACK_OF(ASN1_STRING_TABLE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk)))
+
+#define sk_ASN1_STRING_TABLE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk))
+
+#define sk_ASN1_STRING_TABLE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk))
+
+#define sk_ASN1_STRING_TABLE_set_cmp_func(sk, comp)                       \
+  ((int (*)(const ASN1_STRING_TABLE **a, const ASN1_STRING_TABLE **b))    \
+   sk_set_cmp_func(                                                       \
+       CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const ASN1_STRING_TABLE **a,  \
+                                            const ASN1_STRING_TABLE **b), \
+                    comp)))
+
+/* ASN1_TYPE */
+#define sk_ASN1_TYPE_new(comp)     \
+  ((STACK_OF(ASN1_TYPE) *)sk_new(  \
+      CHECKED_CAST(stack_cmp_func, \
+                   int (*)(const ASN1_TYPE **a, const ASN1_TYPE **b), comp)))
+
+#define sk_ASN1_TYPE_new_null() ((STACK_OF(ASN1_TYPE) *)sk_new_null())
+
+#define sk_ASN1_TYPE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk))
+
+#define sk_ASN1_TYPE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk));
+
+#define sk_ASN1_TYPE_value(sk, i) \
+  ((ASN1_TYPE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_TYPE) *, sk), (i)))
+
+#define sk_ASN1_TYPE_set(sk, i, p)                                             \
+  ((ASN1_TYPE *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), (i), \
+                       CHECKED_CAST(void *, ASN1_TYPE *, p)))
+
+#define sk_ASN1_TYPE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk))
+
+#define sk_ASN1_TYPE_pop_free(sk, free_func)             \
+  sk_pop_free(                                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(ASN1_TYPE *), free_func))
+
+#define sk_ASN1_TYPE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), \
+            CHECKED_CAST(void *, ASN1_TYPE *, p), (where))
+
+#define sk_ASN1_TYPE_delete(sk, where)                                       \
+  ((ASN1_TYPE *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), \
+                          (where)))
+
+#define sk_ASN1_TYPE_delete_ptr(sk, p)                   \
+  ((ASN1_TYPE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), \
+      CHECKED_CAST(void *, ASN1_TYPE *, p)))
+
+#define sk_ASN1_TYPE_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), (out_index), \
+          CHECKED_CAST(void *, ASN1_TYPE *, p))
+
+#define sk_ASN1_TYPE_shift(sk) \
+  ((ASN1_TYPE *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk)))
+
+#define sk_ASN1_TYPE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), \
+          CHECKED_CAST(void *, ASN1_TYPE *, p))
+
+#define sk_ASN1_TYPE_pop(sk) \
+  ((ASN1_TYPE *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk)))
+
+#define sk_ASN1_TYPE_dup(sk)      \
+  ((STACK_OF(ASN1_TYPE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_TYPE) *, sk)))
+
+#define sk_ASN1_TYPE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk))
+
+#define sk_ASN1_TYPE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_TYPE) *, sk))
+
+#define sk_ASN1_TYPE_set_cmp_func(sk, comp)                            \
+  ((int (*)(const ASN1_TYPE **a, const ASN1_TYPE **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk),               \
+      CHECKED_CAST(stack_cmp_func,                                     \
+                   int (*)(const ASN1_TYPE **a, const ASN1_TYPE **b), comp)))
+
+/* ASN1_VALUE */
+#define sk_ASN1_VALUE_new(comp)                                            \
+  ((STACK_OF(ASN1_VALUE) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const ASN1_VALUE **a, const ASN1_VALUE **b), \
+      comp)))
+
+#define sk_ASN1_VALUE_new_null() ((STACK_OF(ASN1_VALUE) *)sk_new_null())
+
+#define sk_ASN1_VALUE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk))
+
+#define sk_ASN1_VALUE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk));
+
+#define sk_ASN1_VALUE_value(sk, i) \
+  ((ASN1_VALUE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_VALUE) *, sk), (i)))
+
+#define sk_ASN1_VALUE_set(sk, i, p)                                         \
+  ((ASN1_VALUE *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
+                        (i), CHECKED_CAST(void *, ASN1_VALUE *, p)))
+
+#define sk_ASN1_VALUE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk))
+
+#define sk_ASN1_VALUE_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(ASN1_VALUE *), free_func))
+
+#define sk_ASN1_VALUE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
+            CHECKED_CAST(void *, ASN1_VALUE *, p), (where))
+
+#define sk_ASN1_VALUE_delete(sk, where)                                        \
+  ((ASN1_VALUE *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
+                           (where)))
+
+#define sk_ASN1_VALUE_delete_ptr(sk, p)                   \
+  ((ASN1_VALUE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
+      CHECKED_CAST(void *, ASN1_VALUE *, p)))
+
+#define sk_ASN1_VALUE_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), (out_index), \
+          CHECKED_CAST(void *, ASN1_VALUE *, p))
+
+#define sk_ASN1_VALUE_shift(sk) \
+  ((ASN1_VALUE *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk)))
+
+#define sk_ASN1_VALUE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
+          CHECKED_CAST(void *, ASN1_VALUE *, p))
+
+#define sk_ASN1_VALUE_pop(sk) \
+  ((ASN1_VALUE *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk)))
+
+#define sk_ASN1_VALUE_dup(sk)      \
+  ((STACK_OF(ASN1_VALUE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(ASN1_VALUE) *, sk)))
+
+#define sk_ASN1_VALUE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk))
+
+#define sk_ASN1_VALUE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_VALUE) *, sk))
+
+#define sk_ASN1_VALUE_set_cmp_func(sk, comp)                             \
+  ((int (*)(const ASN1_VALUE **a, const ASN1_VALUE **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const ASN1_VALUE **a, const ASN1_VALUE **b),  \
+                   comp)))
+
+/* BIO */
+#define sk_BIO_new(comp)                 \
+  ((STACK_OF(BIO) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func, int (*)(const BIO **a, const BIO **b), comp)))
+
+#define sk_BIO_new_null() ((STACK_OF(BIO) *)sk_new_null())
+
+#define sk_BIO_num(sk) sk_num(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk))
+
+#define sk_BIO_zero(sk) sk_zero(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk));
+
+#define sk_BIO_value(sk, i) \
+  ((BIO *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(BIO) *, sk), (i)))
+
+#define sk_BIO_set(sk, i, p)                                       \
+  ((BIO *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), (i), \
+                 CHECKED_CAST(void *, BIO *, p)))
+
+#define sk_BIO_free(sk) sk_free(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk))
+
+#define sk_BIO_pop_free(sk, free_func)                     \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), \
+              CHECKED_CAST(void (*)(void *), void (*)(BIO *), free_func))
+
+#define sk_BIO_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), \
+            CHECKED_CAST(void *, BIO *, p), (where))
+
+#define sk_BIO_delete(sk, where) \
+  ((BIO *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), (where)))
+
+#define sk_BIO_delete_ptr(sk, p)                                     \
+  ((BIO *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), \
+                        CHECKED_CAST(void *, BIO *, p)))
+
+#define sk_BIO_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), (out_index), \
+          CHECKED_CAST(void *, BIO *, p))
+
+#define sk_BIO_shift(sk) \
+  ((BIO *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk)))
+
+#define sk_BIO_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), \
+          CHECKED_CAST(void *, BIO *, p))
+
+#define sk_BIO_pop(sk) \
+  ((BIO *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk)))
+
+#define sk_BIO_dup(sk) \
+  ((STACK_OF(BIO) *)sk_dup(CHECKED_CAST(_STACK *, const STACK_OF(BIO) *, sk)))
+
+#define sk_BIO_sort(sk) sk_sort(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk))
+
+#define sk_BIO_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(BIO) *, sk))
+
+#define sk_BIO_set_cmp_func(sk, comp)                                     \
+  ((int (*)(const BIO **a, const BIO **b))sk_set_cmp_func(                \
+      CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk),                        \
+      CHECKED_CAST(stack_cmp_func, int (*)(const BIO **a, const BIO **b), \
+                   comp)))
+
+/* BY_DIR_ENTRY */
+#define sk_BY_DIR_ENTRY_new(comp)                                              \
+  ((STACK_OF(BY_DIR_ENTRY) *)sk_new(CHECKED_CAST(                              \
+      stack_cmp_func, int (*)(const BY_DIR_ENTRY **a, const BY_DIR_ENTRY **b), \
+      comp)))
+
+#define sk_BY_DIR_ENTRY_new_null() ((STACK_OF(BY_DIR_ENTRY) *)sk_new_null())
+
+#define sk_BY_DIR_ENTRY_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk))
+
+#define sk_BY_DIR_ENTRY_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk));
+
+#define sk_BY_DIR_ENTRY_value(sk, i) \
+  ((BY_DIR_ENTRY *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk), (i)))
+
+#define sk_BY_DIR_ENTRY_set(sk, i, p)                            \
+  ((BY_DIR_ENTRY *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), (i), \
+      CHECKED_CAST(void *, BY_DIR_ENTRY *, p)))
+
+#define sk_BY_DIR_ENTRY_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk))
+
+#define sk_BY_DIR_ENTRY_pop_free(sk, free_func)             \
+  sk_pop_free(                                              \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(BY_DIR_ENTRY *), free_func))
+
+#define sk_BY_DIR_ENTRY_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), \
+            CHECKED_CAST(void *, BY_DIR_ENTRY *, p), (where))
+
+#define sk_BY_DIR_ENTRY_delete(sk, where) \
+  ((BY_DIR_ENTRY *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), (where)))
+
+#define sk_BY_DIR_ENTRY_delete_ptr(sk, p)                   \
+  ((BY_DIR_ENTRY *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), \
+      CHECKED_CAST(void *, BY_DIR_ENTRY *, p)))
+
+#define sk_BY_DIR_ENTRY_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), (out_index), \
+          CHECKED_CAST(void *, BY_DIR_ENTRY *, p))
+
+#define sk_BY_DIR_ENTRY_shift(sk) \
+  ((BY_DIR_ENTRY *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk)))
+
+#define sk_BY_DIR_ENTRY_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk), \
+          CHECKED_CAST(void *, BY_DIR_ENTRY *, p))
+
+#define sk_BY_DIR_ENTRY_pop(sk) \
+  ((BY_DIR_ENTRY *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk)))
+
+#define sk_BY_DIR_ENTRY_dup(sk)      \
+  ((STACK_OF(BY_DIR_ENTRY) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk)))
+
+#define sk_BY_DIR_ENTRY_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk))
+
+#define sk_BY_DIR_ENTRY_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk))
+
+#define sk_BY_DIR_ENTRY_set_cmp_func(sk, comp)                               \
+  ((int (*)(const BY_DIR_ENTRY **a, const BY_DIR_ENTRY **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk),                  \
+      CHECKED_CAST(stack_cmp_func,                                           \
+                   int (*)(const BY_DIR_ENTRY **a, const BY_DIR_ENTRY **b),  \
+                   comp)))
+
+/* BY_DIR_HASH */
+#define sk_BY_DIR_HASH_new(comp)                                             \
+  ((STACK_OF(BY_DIR_HASH) *)sk_new(CHECKED_CAST(                             \
+      stack_cmp_func, int (*)(const BY_DIR_HASH **a, const BY_DIR_HASH **b), \
+      comp)))
+
+#define sk_BY_DIR_HASH_new_null() ((STACK_OF(BY_DIR_HASH) *)sk_new_null())
+
+#define sk_BY_DIR_HASH_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk))
+
+#define sk_BY_DIR_HASH_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk));
+
+#define sk_BY_DIR_HASH_value(sk, i) \
+  ((BY_DIR_HASH *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_HASH) *, sk), (i)))
+
+#define sk_BY_DIR_HASH_set(sk, i, p)                                          \
+  ((BY_DIR_HASH *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), \
+                         (i), CHECKED_CAST(void *, BY_DIR_HASH *, p)))
+
+#define sk_BY_DIR_HASH_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk))
+
+#define sk_BY_DIR_HASH_pop_free(sk, free_func)             \
+  sk_pop_free(                                             \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(BY_DIR_HASH *), free_func))
+
+#define sk_BY_DIR_HASH_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), \
+            CHECKED_CAST(void *, BY_DIR_HASH *, p), (where))
+
+#define sk_BY_DIR_HASH_delete(sk, where) \
+  ((BY_DIR_HASH *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), (where)))
+
+#define sk_BY_DIR_HASH_delete_ptr(sk, p)                   \
+  ((BY_DIR_HASH *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), \
+      CHECKED_CAST(void *, BY_DIR_HASH *, p)))
+
+#define sk_BY_DIR_HASH_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), (out_index), \
+          CHECKED_CAST(void *, BY_DIR_HASH *, p))
+
+#define sk_BY_DIR_HASH_shift(sk) \
+  ((BY_DIR_HASH *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk)))
+
+#define sk_BY_DIR_HASH_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), \
+          CHECKED_CAST(void *, BY_DIR_HASH *, p))
+
+#define sk_BY_DIR_HASH_pop(sk) \
+  ((BY_DIR_HASH *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk)))
+
+#define sk_BY_DIR_HASH_dup(sk)      \
+  ((STACK_OF(BY_DIR_HASH) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_HASH) *, sk)))
+
+#define sk_BY_DIR_HASH_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk))
+
+#define sk_BY_DIR_HASH_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_HASH) *, sk))
+
+#define sk_BY_DIR_HASH_set_cmp_func(sk, comp)                              \
+  ((int (*)(const BY_DIR_HASH **a, const BY_DIR_HASH **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk),                 \
+      CHECKED_CAST(stack_cmp_func,                                         \
+                   int (*)(const BY_DIR_HASH **a, const BY_DIR_HASH **b),  \
+                   comp)))
+
+/* CONF_VALUE */
+#define sk_CONF_VALUE_new(comp)                                            \
+  ((STACK_OF(CONF_VALUE) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const CONF_VALUE **a, const CONF_VALUE **b), \
+      comp)))
+
+#define sk_CONF_VALUE_new_null() ((STACK_OF(CONF_VALUE) *)sk_new_null())
+
+#define sk_CONF_VALUE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk))
+
+#define sk_CONF_VALUE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk));
+
+#define sk_CONF_VALUE_value(sk, i) \
+  ((CONF_VALUE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(CONF_VALUE) *, sk), (i)))
+
+#define sk_CONF_VALUE_set(sk, i, p)                                         \
+  ((CONF_VALUE *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
+                        (i), CHECKED_CAST(void *, CONF_VALUE *, p)))
+
+#define sk_CONF_VALUE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk))
+
+#define sk_CONF_VALUE_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(CONF_VALUE *), free_func))
+
+#define sk_CONF_VALUE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
+            CHECKED_CAST(void *, CONF_VALUE *, p), (where))
+
+#define sk_CONF_VALUE_delete(sk, where)                                        \
+  ((CONF_VALUE *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
+                           (where)))
+
+#define sk_CONF_VALUE_delete_ptr(sk, p)                   \
+  ((CONF_VALUE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
+      CHECKED_CAST(void *, CONF_VALUE *, p)))
+
+#define sk_CONF_VALUE_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), (out_index), \
+          CHECKED_CAST(void *, CONF_VALUE *, p))
+
+#define sk_CONF_VALUE_shift(sk) \
+  ((CONF_VALUE *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk)))
+
+#define sk_CONF_VALUE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
+          CHECKED_CAST(void *, CONF_VALUE *, p))
+
+#define sk_CONF_VALUE_pop(sk) \
+  ((CONF_VALUE *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk)))
+
+#define sk_CONF_VALUE_dup(sk)      \
+  ((STACK_OF(CONF_VALUE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(CONF_VALUE) *, sk)))
+
+#define sk_CONF_VALUE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk))
+
+#define sk_CONF_VALUE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(CONF_VALUE) *, sk))
+
+#define sk_CONF_VALUE_set_cmp_func(sk, comp)                             \
+  ((int (*)(const CONF_VALUE **a, const CONF_VALUE **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const CONF_VALUE **a, const CONF_VALUE **b),  \
+                   comp)))
+
+/* CRYPTO_EX_DATA_FUNCS */
+#define sk_CRYPTO_EX_DATA_FUNCS_new(comp)                                      \
+  ((STACK_OF(CRYPTO_EX_DATA_FUNCS) *)sk_new(CHECKED_CAST(                      \
+      stack_cmp_func,                                                          \
+      int (*)(const CRYPTO_EX_DATA_FUNCS **a, const CRYPTO_EX_DATA_FUNCS **b), \
+      comp)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_new_null() \
+  ((STACK_OF(CRYPTO_EX_DATA_FUNCS) *)sk_new_null())
+
+#define sk_CRYPTO_EX_DATA_FUNCS_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk));
+
+#define sk_CRYPTO_EX_DATA_FUNCS_value(sk, i)                              \
+  ((CRYPTO_EX_DATA_FUNCS *)sk_value(                                      \
+      CHECKED_CAST(_STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), \
+      (i)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_set(sk, i, p)                            \
+  ((CRYPTO_EX_DATA_FUNCS *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), (i), \
+      CHECKED_CAST(void *, CRYPTO_EX_DATA_FUNCS *, p)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(CRYPTO_EX_DATA_FUNCS *), \
+                           free_func))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), \
+            CHECKED_CAST(void *, CRYPTO_EX_DATA_FUNCS *, p), (where))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_delete(sk, where) \
+  ((CRYPTO_EX_DATA_FUNCS *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), (where)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_delete_ptr(sk, p)                   \
+  ((CRYPTO_EX_DATA_FUNCS *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), \
+      CHECKED_CAST(void *, CRYPTO_EX_DATA_FUNCS *, p)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), \
+          (out_index), CHECKED_CAST(void *, CRYPTO_EX_DATA_FUNCS *, p))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_shift(sk) \
+  ((CRYPTO_EX_DATA_FUNCS *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), \
+          CHECKED_CAST(void *, CRYPTO_EX_DATA_FUNCS *, p))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_pop(sk) \
+  ((CRYPTO_EX_DATA_FUNCS *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_dup(sk)      \
+  ((STACK_OF(CRYPTO_EX_DATA_FUNCS) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk)))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_is_sorted(sk) \
+  sk_is_sorted(                               \
+      CHECKED_CAST(_STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
+
+#define sk_CRYPTO_EX_DATA_FUNCS_set_cmp_func(sk, comp)                       \
+  ((int (*)(const CRYPTO_EX_DATA_FUNCS **a, const CRYPTO_EX_DATA_FUNCS **b)) \
+   sk_set_cmp_func(                                                          \
+       CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const CRYPTO_EX_DATA_FUNCS **a,  \
+                                            const CRYPTO_EX_DATA_FUNCS **b), \
+                    comp)))
+
+/* DIST_POINT */
+#define sk_DIST_POINT_new(comp)                                            \
+  ((STACK_OF(DIST_POINT) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const DIST_POINT **a, const DIST_POINT **b), \
+      comp)))
+
+#define sk_DIST_POINT_new_null() ((STACK_OF(DIST_POINT) *)sk_new_null())
+
+#define sk_DIST_POINT_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk))
+
+#define sk_DIST_POINT_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk));
+
+#define sk_DIST_POINT_value(sk, i) \
+  ((DIST_POINT *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(DIST_POINT) *, sk), (i)))
+
+#define sk_DIST_POINT_set(sk, i, p)                                         \
+  ((DIST_POINT *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
+                        (i), CHECKED_CAST(void *, DIST_POINT *, p)))
+
+#define sk_DIST_POINT_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk))
+
+#define sk_DIST_POINT_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(DIST_POINT *), free_func))
+
+#define sk_DIST_POINT_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
+            CHECKED_CAST(void *, DIST_POINT *, p), (where))
+
+#define sk_DIST_POINT_delete(sk, where)                                        \
+  ((DIST_POINT *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
+                           (where)))
+
+#define sk_DIST_POINT_delete_ptr(sk, p)                   \
+  ((DIST_POINT *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
+      CHECKED_CAST(void *, DIST_POINT *, p)))
+
+#define sk_DIST_POINT_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), (out_index), \
+          CHECKED_CAST(void *, DIST_POINT *, p))
+
+#define sk_DIST_POINT_shift(sk) \
+  ((DIST_POINT *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk)))
+
+#define sk_DIST_POINT_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
+          CHECKED_CAST(void *, DIST_POINT *, p))
+
+#define sk_DIST_POINT_pop(sk) \
+  ((DIST_POINT *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk)))
+
+#define sk_DIST_POINT_dup(sk)      \
+  ((STACK_OF(DIST_POINT) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(DIST_POINT) *, sk)))
+
+#define sk_DIST_POINT_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk))
+
+#define sk_DIST_POINT_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(DIST_POINT) *, sk))
+
+#define sk_DIST_POINT_set_cmp_func(sk, comp)                             \
+  ((int (*)(const DIST_POINT **a, const DIST_POINT **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const DIST_POINT **a, const DIST_POINT **b),  \
+                   comp)))
+
+/* GENERAL_NAME */
+#define sk_GENERAL_NAME_new(comp)                                              \
+  ((STACK_OF(GENERAL_NAME) *)sk_new(CHECKED_CAST(                              \
+      stack_cmp_func, int (*)(const GENERAL_NAME **a, const GENERAL_NAME **b), \
+      comp)))
+
+#define sk_GENERAL_NAME_new_null() ((STACK_OF(GENERAL_NAME) *)sk_new_null())
+
+#define sk_GENERAL_NAME_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk))
+
+#define sk_GENERAL_NAME_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk));
+
+#define sk_GENERAL_NAME_value(sk, i) \
+  ((GENERAL_NAME *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAME) *, sk), (i)))
+
+#define sk_GENERAL_NAME_set(sk, i, p)                            \
+  ((GENERAL_NAME *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), (i), \
+      CHECKED_CAST(void *, GENERAL_NAME *, p)))
+
+#define sk_GENERAL_NAME_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk))
+
+#define sk_GENERAL_NAME_pop_free(sk, free_func)             \
+  sk_pop_free(                                              \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(GENERAL_NAME *), free_func))
+
+#define sk_GENERAL_NAME_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), \
+            CHECKED_CAST(void *, GENERAL_NAME *, p), (where))
+
+#define sk_GENERAL_NAME_delete(sk, where) \
+  ((GENERAL_NAME *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), (where)))
+
+#define sk_GENERAL_NAME_delete_ptr(sk, p)                   \
+  ((GENERAL_NAME *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), \
+      CHECKED_CAST(void *, GENERAL_NAME *, p)))
+
+#define sk_GENERAL_NAME_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), (out_index), \
+          CHECKED_CAST(void *, GENERAL_NAME *, p))
+
+#define sk_GENERAL_NAME_shift(sk) \
+  ((GENERAL_NAME *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk)))
+
+#define sk_GENERAL_NAME_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk), \
+          CHECKED_CAST(void *, GENERAL_NAME *, p))
+
+#define sk_GENERAL_NAME_pop(sk) \
+  ((GENERAL_NAME *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk)))
+
+#define sk_GENERAL_NAME_dup(sk)      \
+  ((STACK_OF(GENERAL_NAME) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAME) *, sk)))
+
+#define sk_GENERAL_NAME_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk))
+
+#define sk_GENERAL_NAME_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAME) *, sk))
+
+#define sk_GENERAL_NAME_set_cmp_func(sk, comp)                               \
+  ((int (*)(const GENERAL_NAME **a, const GENERAL_NAME **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk),                  \
+      CHECKED_CAST(stack_cmp_func,                                           \
+                   int (*)(const GENERAL_NAME **a, const GENERAL_NAME **b),  \
+                   comp)))
+
+/* GENERAL_NAMES */
+#define sk_GENERAL_NAMES_new(comp)                 \
+  ((STACK_OF(GENERAL_NAMES) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                              \
+      int (*)(const GENERAL_NAMES **a, const GENERAL_NAMES **b), comp)))
+
+#define sk_GENERAL_NAMES_new_null() ((STACK_OF(GENERAL_NAMES) *)sk_new_null())
+
+#define sk_GENERAL_NAMES_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk))
+
+#define sk_GENERAL_NAMES_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk));
+
+#define sk_GENERAL_NAMES_value(sk, i) \
+  ((GENERAL_NAMES *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAMES) *, sk), (i)))
+
+#define sk_GENERAL_NAMES_set(sk, i, p)                            \
+  ((GENERAL_NAMES *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), (i), \
+      CHECKED_CAST(void *, GENERAL_NAMES *, p)))
+
+#define sk_GENERAL_NAMES_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk))
+
+#define sk_GENERAL_NAMES_pop_free(sk, free_func)             \
+  sk_pop_free(                                               \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(GENERAL_NAMES *), free_func))
+
+#define sk_GENERAL_NAMES_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), \
+            CHECKED_CAST(void *, GENERAL_NAMES *, p), (where))
+
+#define sk_GENERAL_NAMES_delete(sk, where) \
+  ((GENERAL_NAMES *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), (where)))
+
+#define sk_GENERAL_NAMES_delete_ptr(sk, p)                   \
+  ((GENERAL_NAMES *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), \
+      CHECKED_CAST(void *, GENERAL_NAMES *, p)))
+
+#define sk_GENERAL_NAMES_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), (out_index), \
+          CHECKED_CAST(void *, GENERAL_NAMES *, p))
+
+#define sk_GENERAL_NAMES_shift(sk) \
+  ((GENERAL_NAMES *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk)))
+
+#define sk_GENERAL_NAMES_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk), \
+          CHECKED_CAST(void *, GENERAL_NAMES *, p))
+
+#define sk_GENERAL_NAMES_pop(sk) \
+  ((GENERAL_NAMES *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk)))
+
+#define sk_GENERAL_NAMES_dup(sk)      \
+  ((STACK_OF(GENERAL_NAMES) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAMES) *, sk)))
+
+#define sk_GENERAL_NAMES_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk))
+
+#define sk_GENERAL_NAMES_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAMES) *, sk))
+
+#define sk_GENERAL_NAMES_set_cmp_func(sk, comp)                                \
+  ((int (*)(const GENERAL_NAMES **a, const GENERAL_NAMES **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk),                   \
+      CHECKED_CAST(stack_cmp_func,                                             \
+                   int (*)(const GENERAL_NAMES **a, const GENERAL_NAMES **b),  \
+                   comp)))
+
+/* GENERAL_SUBTREE */
+#define sk_GENERAL_SUBTREE_new(comp)                 \
+  ((STACK_OF(GENERAL_SUBTREE) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                                \
+      int (*)(const GENERAL_SUBTREE **a, const GENERAL_SUBTREE **b), comp)))
+
+#define sk_GENERAL_SUBTREE_new_null() \
+  ((STACK_OF(GENERAL_SUBTREE) *)sk_new_null())
+
+#define sk_GENERAL_SUBTREE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk))
+
+#define sk_GENERAL_SUBTREE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk));
+
+#define sk_GENERAL_SUBTREE_value(sk, i) \
+  ((GENERAL_SUBTREE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk), (i)))
+
+#define sk_GENERAL_SUBTREE_set(sk, i, p)                            \
+  ((GENERAL_SUBTREE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), (i), \
+      CHECKED_CAST(void *, GENERAL_SUBTREE *, p)))
+
+#define sk_GENERAL_SUBTREE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk))
+
+#define sk_GENERAL_SUBTREE_pop_free(sk, free_func)             \
+  sk_pop_free(                                                 \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(GENERAL_SUBTREE *), free_func))
+
+#define sk_GENERAL_SUBTREE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), \
+            CHECKED_CAST(void *, GENERAL_SUBTREE *, p), (where))
+
+#define sk_GENERAL_SUBTREE_delete(sk, where) \
+  ((GENERAL_SUBTREE *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), (where)))
+
+#define sk_GENERAL_SUBTREE_delete_ptr(sk, p)                   \
+  ((GENERAL_SUBTREE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), \
+      CHECKED_CAST(void *, GENERAL_SUBTREE *, p)))
+
+#define sk_GENERAL_SUBTREE_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), \
+          (out_index), CHECKED_CAST(void *, GENERAL_SUBTREE *, p))
+
+#define sk_GENERAL_SUBTREE_shift(sk) \
+  ((GENERAL_SUBTREE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk)))
+
+#define sk_GENERAL_SUBTREE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk), \
+          CHECKED_CAST(void *, GENERAL_SUBTREE *, p))
+
+#define sk_GENERAL_SUBTREE_pop(sk) \
+  ((GENERAL_SUBTREE *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk)))
+
+#define sk_GENERAL_SUBTREE_dup(sk)      \
+  ((STACK_OF(GENERAL_SUBTREE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk)))
+
+#define sk_GENERAL_SUBTREE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk))
+
+#define sk_GENERAL_SUBTREE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk))
+
+#define sk_GENERAL_SUBTREE_set_cmp_func(sk, comp)                       \
+  ((int (*)(const GENERAL_SUBTREE **a, const GENERAL_SUBTREE **b))      \
+   sk_set_cmp_func(                                                     \
+       CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const GENERAL_SUBTREE **a,  \
+                                            const GENERAL_SUBTREE **b), \
+                    comp)))
+
+/* MIME_HEADER */
+#define sk_MIME_HEADER_new(comp)                                             \
+  ((STACK_OF(MIME_HEADER) *)sk_new(CHECKED_CAST(                             \
+      stack_cmp_func, int (*)(const MIME_HEADER **a, const MIME_HEADER **b), \
+      comp)))
+
+#define sk_MIME_HEADER_new_null() ((STACK_OF(MIME_HEADER) *)sk_new_null())
+
+#define sk_MIME_HEADER_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk))
+
+#define sk_MIME_HEADER_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk));
+
+#define sk_MIME_HEADER_value(sk, i) \
+  ((MIME_HEADER *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(MIME_HEADER) *, sk), (i)))
+
+#define sk_MIME_HEADER_set(sk, i, p)                                          \
+  ((MIME_HEADER *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), \
+                         (i), CHECKED_CAST(void *, MIME_HEADER *, p)))
+
+#define sk_MIME_HEADER_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk))
+
+#define sk_MIME_HEADER_pop_free(sk, free_func)             \
+  sk_pop_free(                                             \
+      CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(MIME_HEADER *), free_func))
+
+#define sk_MIME_HEADER_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), \
+            CHECKED_CAST(void *, MIME_HEADER *, p), (where))
+
+#define sk_MIME_HEADER_delete(sk, where) \
+  ((MIME_HEADER *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), (where)))
+
+#define sk_MIME_HEADER_delete_ptr(sk, p)                   \
+  ((MIME_HEADER *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), \
+      CHECKED_CAST(void *, MIME_HEADER *, p)))
+
+#define sk_MIME_HEADER_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), (out_index), \
+          CHECKED_CAST(void *, MIME_HEADER *, p))
+
+#define sk_MIME_HEADER_shift(sk) \
+  ((MIME_HEADER *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk)))
+
+#define sk_MIME_HEADER_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk), \
+          CHECKED_CAST(void *, MIME_HEADER *, p))
+
+#define sk_MIME_HEADER_pop(sk) \
+  ((MIME_HEADER *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk)))
+
+#define sk_MIME_HEADER_dup(sk)      \
+  ((STACK_OF(MIME_HEADER) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(MIME_HEADER) *, sk)))
+
+#define sk_MIME_HEADER_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk))
+
+#define sk_MIME_HEADER_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(MIME_HEADER) *, sk))
+
+#define sk_MIME_HEADER_set_cmp_func(sk, comp)                              \
+  ((int (*)(const MIME_HEADER **a, const MIME_HEADER **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(MIME_HEADER) *, sk),                 \
+      CHECKED_CAST(stack_cmp_func,                                         \
+                   int (*)(const MIME_HEADER **a, const MIME_HEADER **b),  \
+                   comp)))
+
+/* PKCS7_SIGNER_INFO */
+#define sk_PKCS7_SIGNER_INFO_new(comp)                                   \
+  ((STACK_OF(PKCS7_SIGNER_INFO) *)sk_new(CHECKED_CAST(                   \
+      stack_cmp_func,                                                    \
+      int (*)(const PKCS7_SIGNER_INFO **a, const PKCS7_SIGNER_INFO **b), \
+      comp)))
+
+#define sk_PKCS7_SIGNER_INFO_new_null() \
+  ((STACK_OF(PKCS7_SIGNER_INFO) *)sk_new_null())
+
+#define sk_PKCS7_SIGNER_INFO_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk))
+
+#define sk_PKCS7_SIGNER_INFO_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk));
+
+#define sk_PKCS7_SIGNER_INFO_value(sk, i) \
+  ((PKCS7_SIGNER_INFO *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(PKCS7_SIGNER_INFO) *, sk), (i)))
+
+#define sk_PKCS7_SIGNER_INFO_set(sk, i, p)                            \
+  ((PKCS7_SIGNER_INFO *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk), (i), \
+      CHECKED_CAST(void *, PKCS7_SIGNER_INFO *, p)))
+
+#define sk_PKCS7_SIGNER_INFO_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk))
+
+#define sk_PKCS7_SIGNER_INFO_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(PKCS7_SIGNER_INFO *), \
+                           free_func))
+
+#define sk_PKCS7_SIGNER_INFO_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk), \
+            CHECKED_CAST(void *, PKCS7_SIGNER_INFO *, p), (where))
+
+#define sk_PKCS7_SIGNER_INFO_delete(sk, where) \
+  ((PKCS7_SIGNER_INFO *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk), (where)))
+
+#define sk_PKCS7_SIGNER_INFO_delete_ptr(sk, p)                   \
+  ((PKCS7_SIGNER_INFO *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk), \
+      CHECKED_CAST(void *, PKCS7_SIGNER_INFO *, p)))
+
+#define sk_PKCS7_SIGNER_INFO_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk), \
+          (out_index), CHECKED_CAST(void *, PKCS7_SIGNER_INFO *, p))
+
+#define sk_PKCS7_SIGNER_INFO_shift(sk) \
+  ((PKCS7_SIGNER_INFO *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk)))
+
+#define sk_PKCS7_SIGNER_INFO_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk), \
+          CHECKED_CAST(void *, PKCS7_SIGNER_INFO *, p))
+
+#define sk_PKCS7_SIGNER_INFO_pop(sk) \
+  ((PKCS7_SIGNER_INFO *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk)))
+
+#define sk_PKCS7_SIGNER_INFO_dup(sk)      \
+  ((STACK_OF(PKCS7_SIGNER_INFO) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(PKCS7_SIGNER_INFO) *, sk)))
+
+#define sk_PKCS7_SIGNER_INFO_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk))
+
+#define sk_PKCS7_SIGNER_INFO_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(PKCS7_SIGNER_INFO) *, sk))
+
+#define sk_PKCS7_SIGNER_INFO_set_cmp_func(sk, comp)                       \
+  ((int (*)(const PKCS7_SIGNER_INFO **a, const PKCS7_SIGNER_INFO **b))    \
+   sk_set_cmp_func(                                                       \
+       CHECKED_CAST(_STACK *, STACK_OF(PKCS7_SIGNER_INFO) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const PKCS7_SIGNER_INFO **a,  \
+                                            const PKCS7_SIGNER_INFO **b), \
+                    comp)))
+
+/* PKCS7_RECIP_INFO */
+#define sk_PKCS7_RECIP_INFO_new(comp)                 \
+  ((STACK_OF(PKCS7_RECIP_INFO) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                                 \
+      int (*)(const PKCS7_RECIP_INFO **a, const PKCS7_RECIP_INFO **b), comp)))
+
+#define sk_PKCS7_RECIP_INFO_new_null() \
+  ((STACK_OF(PKCS7_RECIP_INFO) *)sk_new_null())
+
+#define sk_PKCS7_RECIP_INFO_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk))
+
+#define sk_PKCS7_RECIP_INFO_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk));
+
+#define sk_PKCS7_RECIP_INFO_value(sk, i) \
+  ((PKCS7_RECIP_INFO *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(PKCS7_RECIP_INFO) *, sk), (i)))
+
+#define sk_PKCS7_RECIP_INFO_set(sk, i, p)                            \
+  ((PKCS7_RECIP_INFO *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), (i), \
+      CHECKED_CAST(void *, PKCS7_RECIP_INFO *, p)))
+
+#define sk_PKCS7_RECIP_INFO_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk))
+
+#define sk_PKCS7_RECIP_INFO_pop_free(sk, free_func)             \
+  sk_pop_free(                                                  \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(PKCS7_RECIP_INFO *), free_func))
+
+#define sk_PKCS7_RECIP_INFO_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), \
+            CHECKED_CAST(void *, PKCS7_RECIP_INFO *, p), (where))
+
+#define sk_PKCS7_RECIP_INFO_delete(sk, where) \
+  ((PKCS7_RECIP_INFO *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), (where)))
+
+#define sk_PKCS7_RECIP_INFO_delete_ptr(sk, p)                   \
+  ((PKCS7_RECIP_INFO *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), \
+      CHECKED_CAST(void *, PKCS7_RECIP_INFO *, p)))
+
+#define sk_PKCS7_RECIP_INFO_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), \
+          (out_index), CHECKED_CAST(void *, PKCS7_RECIP_INFO *, p))
+
+#define sk_PKCS7_RECIP_INFO_shift(sk) \
+  ((PKCS7_RECIP_INFO *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk)))
+
+#define sk_PKCS7_RECIP_INFO_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk), \
+          CHECKED_CAST(void *, PKCS7_RECIP_INFO *, p))
+
+#define sk_PKCS7_RECIP_INFO_pop(sk) \
+  ((PKCS7_RECIP_INFO *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk)))
+
+#define sk_PKCS7_RECIP_INFO_dup(sk)      \
+  ((STACK_OF(PKCS7_RECIP_INFO) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(PKCS7_RECIP_INFO) *, sk)))
+
+#define sk_PKCS7_RECIP_INFO_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk))
+
+#define sk_PKCS7_RECIP_INFO_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(PKCS7_RECIP_INFO) *, sk))
+
+#define sk_PKCS7_RECIP_INFO_set_cmp_func(sk, comp)                       \
+  ((int (*)(const PKCS7_RECIP_INFO **a, const PKCS7_RECIP_INFO **b))     \
+   sk_set_cmp_func(                                                      \
+       CHECKED_CAST(_STACK *, STACK_OF(PKCS7_RECIP_INFO) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const PKCS7_RECIP_INFO **a,  \
+                                            const PKCS7_RECIP_INFO **b), \
+                    comp)))
+
+/* POLICYINFO */
+#define sk_POLICYINFO_new(comp)                                            \
+  ((STACK_OF(POLICYINFO) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const POLICYINFO **a, const POLICYINFO **b), \
+      comp)))
+
+#define sk_POLICYINFO_new_null() ((STACK_OF(POLICYINFO) *)sk_new_null())
+
+#define sk_POLICYINFO_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk))
+
+#define sk_POLICYINFO_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk));
+
+#define sk_POLICYINFO_value(sk, i) \
+  ((POLICYINFO *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(POLICYINFO) *, sk), (i)))
+
+#define sk_POLICYINFO_set(sk, i, p)                                         \
+  ((POLICYINFO *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
+                        (i), CHECKED_CAST(void *, POLICYINFO *, p)))
+
+#define sk_POLICYINFO_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk))
+
+#define sk_POLICYINFO_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(POLICYINFO *), free_func))
+
+#define sk_POLICYINFO_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
+            CHECKED_CAST(void *, POLICYINFO *, p), (where))
+
+#define sk_POLICYINFO_delete(sk, where)                                        \
+  ((POLICYINFO *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
+                           (where)))
+
+#define sk_POLICYINFO_delete_ptr(sk, p)                   \
+  ((POLICYINFO *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
+      CHECKED_CAST(void *, POLICYINFO *, p)))
+
+#define sk_POLICYINFO_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), (out_index), \
+          CHECKED_CAST(void *, POLICYINFO *, p))
+
+#define sk_POLICYINFO_shift(sk) \
+  ((POLICYINFO *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk)))
+
+#define sk_POLICYINFO_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
+          CHECKED_CAST(void *, POLICYINFO *, p))
+
+#define sk_POLICYINFO_pop(sk) \
+  ((POLICYINFO *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk)))
+
+#define sk_POLICYINFO_dup(sk)      \
+  ((STACK_OF(POLICYINFO) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(POLICYINFO) *, sk)))
+
+#define sk_POLICYINFO_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk))
+
+#define sk_POLICYINFO_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(POLICYINFO) *, sk))
+
+#define sk_POLICYINFO_set_cmp_func(sk, comp)                             \
+  ((int (*)(const POLICYINFO **a, const POLICYINFO **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const POLICYINFO **a, const POLICYINFO **b),  \
+                   comp)))
+
+/* POLICYQUALINFO */
+#define sk_POLICYQUALINFO_new(comp)                 \
+  ((STACK_OF(POLICYQUALINFO) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                               \
+      int (*)(const POLICYQUALINFO **a, const POLICYQUALINFO **b), comp)))
+
+#define sk_POLICYQUALINFO_new_null() ((STACK_OF(POLICYQUALINFO) *)sk_new_null())
+
+#define sk_POLICYQUALINFO_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk))
+
+#define sk_POLICYQUALINFO_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk));
+
+#define sk_POLICYQUALINFO_value(sk, i) \
+  ((POLICYQUALINFO *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(POLICYQUALINFO) *, sk), (i)))
+
+#define sk_POLICYQUALINFO_set(sk, i, p)                            \
+  ((POLICYQUALINFO *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), (i), \
+      CHECKED_CAST(void *, POLICYQUALINFO *, p)))
+
+#define sk_POLICYQUALINFO_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk))
+
+#define sk_POLICYQUALINFO_pop_free(sk, free_func)             \
+  sk_pop_free(                                                \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(POLICYQUALINFO *), free_func))
+
+#define sk_POLICYQUALINFO_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), \
+            CHECKED_CAST(void *, POLICYQUALINFO *, p), (where))
+
+#define sk_POLICYQUALINFO_delete(sk, where) \
+  ((POLICYQUALINFO *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), (where)))
+
+#define sk_POLICYQUALINFO_delete_ptr(sk, p)                   \
+  ((POLICYQUALINFO *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), \
+      CHECKED_CAST(void *, POLICYQUALINFO *, p)))
+
+#define sk_POLICYQUALINFO_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), (out_index), \
+          CHECKED_CAST(void *, POLICYQUALINFO *, p))
+
+#define sk_POLICYQUALINFO_shift(sk) \
+  ((POLICYQUALINFO *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk)))
+
+#define sk_POLICYQUALINFO_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk), \
+          CHECKED_CAST(void *, POLICYQUALINFO *, p))
+
+#define sk_POLICYQUALINFO_pop(sk) \
+  ((POLICYQUALINFO *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk)))
+
+#define sk_POLICYQUALINFO_dup(sk)      \
+  ((STACK_OF(POLICYQUALINFO) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(POLICYQUALINFO) *, sk)))
+
+#define sk_POLICYQUALINFO_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk))
+
+#define sk_POLICYQUALINFO_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(POLICYQUALINFO) *, sk))
+
+#define sk_POLICYQUALINFO_set_cmp_func(sk, comp)                       \
+  ((int (*)(const POLICYQUALINFO **a, const POLICYQUALINFO **b))       \
+   sk_set_cmp_func(                                                    \
+       CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const POLICYQUALINFO **a,  \
+                                            const POLICYQUALINFO **b), \
+                    comp)))
+
+/* POLICY_MAPPING */
+#define sk_POLICY_MAPPING_new(comp)                 \
+  ((STACK_OF(POLICY_MAPPING) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                               \
+      int (*)(const POLICY_MAPPING **a, const POLICY_MAPPING **b), comp)))
+
+#define sk_POLICY_MAPPING_new_null() ((STACK_OF(POLICY_MAPPING) *)sk_new_null())
+
+#define sk_POLICY_MAPPING_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk))
+
+#define sk_POLICY_MAPPING_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk));
+
+#define sk_POLICY_MAPPING_value(sk, i) \
+  ((POLICY_MAPPING *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(POLICY_MAPPING) *, sk), (i)))
+
+#define sk_POLICY_MAPPING_set(sk, i, p)                            \
+  ((POLICY_MAPPING *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), (i), \
+      CHECKED_CAST(void *, POLICY_MAPPING *, p)))
+
+#define sk_POLICY_MAPPING_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk))
+
+#define sk_POLICY_MAPPING_pop_free(sk, free_func)             \
+  sk_pop_free(                                                \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(POLICY_MAPPING *), free_func))
+
+#define sk_POLICY_MAPPING_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), \
+            CHECKED_CAST(void *, POLICY_MAPPING *, p), (where))
+
+#define sk_POLICY_MAPPING_delete(sk, where) \
+  ((POLICY_MAPPING *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), (where)))
+
+#define sk_POLICY_MAPPING_delete_ptr(sk, p)                   \
+  ((POLICY_MAPPING *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), \
+      CHECKED_CAST(void *, POLICY_MAPPING *, p)))
+
+#define sk_POLICY_MAPPING_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), (out_index), \
+          CHECKED_CAST(void *, POLICY_MAPPING *, p))
+
+#define sk_POLICY_MAPPING_shift(sk) \
+  ((POLICY_MAPPING *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk)))
+
+#define sk_POLICY_MAPPING_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk), \
+          CHECKED_CAST(void *, POLICY_MAPPING *, p))
+
+#define sk_POLICY_MAPPING_pop(sk) \
+  ((POLICY_MAPPING *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk)))
+
+#define sk_POLICY_MAPPING_dup(sk)      \
+  ((STACK_OF(POLICY_MAPPING) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(POLICY_MAPPING) *, sk)))
+
+#define sk_POLICY_MAPPING_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk))
+
+#define sk_POLICY_MAPPING_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(POLICY_MAPPING) *, sk))
+
+#define sk_POLICY_MAPPING_set_cmp_func(sk, comp)                       \
+  ((int (*)(const POLICY_MAPPING **a, const POLICY_MAPPING **b))       \
+   sk_set_cmp_func(                                                    \
+       CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const POLICY_MAPPING **a,  \
+                                            const POLICY_MAPPING **b), \
+                    comp)))
+
+/* SRTP_PROTECTION_PROFILE */
+#define sk_SRTP_PROTECTION_PROFILE_new(comp)                                   \
+  ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_new(                                \
+      CHECKED_CAST(stack_cmp_func, int (*)(const SRTP_PROTECTION_PROFILE **a,  \
+                                           const SRTP_PROTECTION_PROFILE **b), \
+                   comp)))
+
+#define sk_SRTP_PROTECTION_PROFILE_new_null() \
+  ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_new_null())
+
+#define sk_SRTP_PROTECTION_PROFILE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
+
+#define sk_SRTP_PROTECTION_PROFILE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk));
+
+#define sk_SRTP_PROTECTION_PROFILE_value(sk, i)                              \
+  ((SRTP_PROTECTION_PROFILE *)sk_value(                                      \
+      CHECKED_CAST(_STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+      (i)))
+
+#define sk_SRTP_PROTECTION_PROFILE_set(sk, i, p)                            \
+  ((SRTP_PROTECTION_PROFILE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), (i), \
+      CHECKED_CAST(void *, SRTP_PROTECTION_PROFILE *, p)))
+
+#define sk_SRTP_PROTECTION_PROFILE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
+
+#define sk_SRTP_PROTECTION_PROFILE_pop_free(sk, free_func)                     \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+              CHECKED_CAST(void (*)(void *),                                   \
+                           void (*)(SRTP_PROTECTION_PROFILE *), free_func))
+
+#define sk_SRTP_PROTECTION_PROFILE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+            CHECKED_CAST(void *, SRTP_PROTECTION_PROFILE *, p), (where))
+
+#define sk_SRTP_PROTECTION_PROFILE_delete(sk, where)                   \
+  ((SRTP_PROTECTION_PROFILE *)sk_delete(                               \
+      CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+      (where)))
+
+#define sk_SRTP_PROTECTION_PROFILE_delete_ptr(sk, p)                   \
+  ((SRTP_PROTECTION_PROFILE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+      CHECKED_CAST(void *, SRTP_PROTECTION_PROFILE *, p)))
+
+#define sk_SRTP_PROTECTION_PROFILE_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+          (out_index), CHECKED_CAST(void *, SRTP_PROTECTION_PROFILE *, p))
+
+#define sk_SRTP_PROTECTION_PROFILE_shift(sk) \
+  ((SRTP_PROTECTION_PROFILE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk)))
+
+#define sk_SRTP_PROTECTION_PROFILE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+          CHECKED_CAST(void *, SRTP_PROTECTION_PROFILE *, p))
+
+#define sk_SRTP_PROTECTION_PROFILE_pop(sk) \
+  ((SRTP_PROTECTION_PROFILE *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk)))
+
+#define sk_SRTP_PROTECTION_PROFILE_dup(sk)      \
+  ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk)))
+
+#define sk_SRTP_PROTECTION_PROFILE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
+
+#define sk_SRTP_PROTECTION_PROFILE_is_sorted(sk) \
+  sk_is_sorted(                                  \
+      CHECKED_CAST(_STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
+
+#define sk_SRTP_PROTECTION_PROFILE_set_cmp_func(sk, comp)               \
+  ((int (*)(const SRTP_PROTECTION_PROFILE **a,                          \
+            const SRTP_PROTECTION_PROFILE **b))                         \
+   sk_set_cmp_func(                                                     \
+       CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+       CHECKED_CAST(stack_cmp_func,                                     \
+                    int (*)(const SRTP_PROTECTION_PROFILE **a,          \
+                            const SRTP_PROTECTION_PROFILE **b),         \
+                    comp)))
+
+/* SSL_CIPHER */
+#define sk_SSL_CIPHER_new(comp)                                            \
+  ((STACK_OF(SSL_CIPHER) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const SSL_CIPHER **a, const SSL_CIPHER **b), \
+      comp)))
+
+#define sk_SSL_CIPHER_new_null() ((STACK_OF(SSL_CIPHER) *)sk_new_null())
+
+#define sk_SSL_CIPHER_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk))
+
+#define sk_SSL_CIPHER_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk));
+
+#define sk_SSL_CIPHER_value(sk, i) \
+  ((SSL_CIPHER *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(SSL_CIPHER) *, sk), (i)))
+
+#define sk_SSL_CIPHER_set(sk, i, p)                                         \
+  ((SSL_CIPHER *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), \
+                        (i), CHECKED_CAST(void *, SSL_CIPHER *, p)))
+
+#define sk_SSL_CIPHER_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk))
+
+#define sk_SSL_CIPHER_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(SSL_CIPHER *), free_func))
+
+#define sk_SSL_CIPHER_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), \
+            CHECKED_CAST(void *, SSL_CIPHER *, p), (where))
+
+#define sk_SSL_CIPHER_delete(sk, where)                                        \
+  ((SSL_CIPHER *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), \
+                           (where)))
+
+#define sk_SSL_CIPHER_delete_ptr(sk, p)                   \
+  ((SSL_CIPHER *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), \
+      CHECKED_CAST(void *, SSL_CIPHER *, p)))
+
+#define sk_SSL_CIPHER_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), (out_index), \
+          CHECKED_CAST(void *, SSL_CIPHER *, p))
+
+#define sk_SSL_CIPHER_shift(sk) \
+  ((SSL_CIPHER *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk)))
+
+#define sk_SSL_CIPHER_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk), \
+          CHECKED_CAST(void *, SSL_CIPHER *, p))
+
+#define sk_SSL_CIPHER_pop(sk) \
+  ((SSL_CIPHER *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk)))
+
+#define sk_SSL_CIPHER_dup(sk)      \
+  ((STACK_OF(SSL_CIPHER) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(SSL_CIPHER) *, sk)))
+
+#define sk_SSL_CIPHER_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk))
+
+#define sk_SSL_CIPHER_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(SSL_CIPHER) *, sk))
+
+#define sk_SSL_CIPHER_set_cmp_func(sk, comp)                             \
+  ((int (*)(const SSL_CIPHER **a, const SSL_CIPHER **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const SSL_CIPHER **a, const SSL_CIPHER **b),  \
+                   comp)))
+
+/* SSL_COMP */
+#define sk_SSL_COMP_new(comp)                 \
+  ((STACK_OF(SSL_COMP) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func, int (*)(const SSL_COMP **a, const SSL_COMP **b), comp)))
+
+#define sk_SSL_COMP_new_null() ((STACK_OF(SSL_COMP) *)sk_new_null())
+
+#define sk_SSL_COMP_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk))
+
+#define sk_SSL_COMP_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk));
+
+#define sk_SSL_COMP_value(sk, i) \
+  ((SSL_COMP *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(SSL_COMP) *, sk), (i)))
+
+#define sk_SSL_COMP_set(sk, i, p)                                            \
+  ((SSL_COMP *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), (i), \
+                      CHECKED_CAST(void *, SSL_COMP *, p)))
+
+#define sk_SSL_COMP_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk))
+
+#define sk_SSL_COMP_pop_free(sk, free_func)                     \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), \
+              CHECKED_CAST(void (*)(void *), void (*)(SSL_COMP *), free_func))
+
+#define sk_SSL_COMP_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), \
+            CHECKED_CAST(void *, SSL_COMP *, p), (where))
+
+#define sk_SSL_COMP_delete(sk, where)                                      \
+  ((SSL_COMP *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), \
+                         (where)))
+
+#define sk_SSL_COMP_delete_ptr(sk, p)                                          \
+  ((SSL_COMP *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), \
+                             CHECKED_CAST(void *, SSL_COMP *, p)))
+
+#define sk_SSL_COMP_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), (out_index), \
+          CHECKED_CAST(void *, SSL_COMP *, p))
+
+#define sk_SSL_COMP_shift(sk) \
+  ((SSL_COMP *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk)))
+
+#define sk_SSL_COMP_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), \
+          CHECKED_CAST(void *, SSL_COMP *, p))
+
+#define sk_SSL_COMP_pop(sk) \
+  ((SSL_COMP *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk)))
+
+#define sk_SSL_COMP_dup(sk)      \
+  ((STACK_OF(SSL_COMP) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(SSL_COMP) *, sk)))
+
+#define sk_SSL_COMP_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk))
+
+#define sk_SSL_COMP_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(SSL_COMP) *, sk))
+
+#define sk_SSL_COMP_set_cmp_func(sk, comp)                           \
+  ((int (*)(const SSL_COMP **a, const SSL_COMP **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk),              \
+      CHECKED_CAST(stack_cmp_func,                                   \
+                   int (*)(const SSL_COMP **a, const SSL_COMP **b), comp)))
+
+/* STACK_OF_X509_NAME_ENTRY */
+#define sk_STACK_OF_X509_NAME_ENTRY_new(comp)                      \
+  ((STACK_OF(STACK_OF_X509_NAME_ENTRY) *)sk_new(CHECKED_CAST(      \
+      stack_cmp_func, int (*)(const STACK_OF_X509_NAME_ENTRY **a,  \
+                              const STACK_OF_X509_NAME_ENTRY **b), \
+      comp)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_new_null() \
+  ((STACK_OF(STACK_OF_X509_NAME_ENTRY) *)sk_new_null())
+
+#define sk_STACK_OF_X509_NAME_ENTRY_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk));
+
+#define sk_STACK_OF_X509_NAME_ENTRY_value(sk, i)                              \
+  ((STACK_OF_X509_NAME_ENTRY *)sk_value(                                      \
+      CHECKED_CAST(_STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+      (i)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_set(sk, i, p)                            \
+  ((STACK_OF_X509_NAME_ENTRY *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), (i), \
+      CHECKED_CAST(void *, STACK_OF_X509_NAME_ENTRY *, p)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_pop_free(sk, free_func)                \
+  sk_pop_free(                                                             \
+      CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk),    \
+      CHECKED_CAST(void (*)(void *), void (*)(STACK_OF_X509_NAME_ENTRY *), \
+                   free_func))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+            CHECKED_CAST(void *, STACK_OF_X509_NAME_ENTRY *, p), (where))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_delete(sk, where)                   \
+  ((STACK_OF_X509_NAME_ENTRY *)sk_delete(                               \
+      CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+      (where)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_delete_ptr(sk, p)                   \
+  ((STACK_OF_X509_NAME_ENTRY *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+      CHECKED_CAST(void *, STACK_OF_X509_NAME_ENTRY *, p)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+          (out_index), CHECKED_CAST(void *, STACK_OF_X509_NAME_ENTRY *, p))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_shift(sk) \
+  ((STACK_OF_X509_NAME_ENTRY *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+          CHECKED_CAST(void *, STACK_OF_X509_NAME_ENTRY *, p))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_pop(sk) \
+  ((STACK_OF_X509_NAME_ENTRY *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_dup(sk)      \
+  ((STACK_OF(STACK_OF_X509_NAME_ENTRY) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk)))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_is_sorted(sk) \
+  sk_is_sorted(                                   \
+      CHECKED_CAST(_STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
+
+#define sk_STACK_OF_X509_NAME_ENTRY_set_cmp_func(sk, comp)               \
+  ((int (*)(const STACK_OF_X509_NAME_ENTRY **a,                          \
+            const STACK_OF_X509_NAME_ENTRY **b))                         \
+   sk_set_cmp_func(                                                      \
+       CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+       CHECKED_CAST(stack_cmp_func,                                      \
+                    int (*)(const STACK_OF_X509_NAME_ENTRY **a,          \
+                            const STACK_OF_X509_NAME_ENTRY **b),         \
+                    comp)))
+
+/* SXNETID */
+#define sk_SXNETID_new(comp)                 \
+  ((STACK_OF(SXNETID) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func, int (*)(const SXNETID **a, const SXNETID **b), comp)))
+
+#define sk_SXNETID_new_null() ((STACK_OF(SXNETID) *)sk_new_null())
+
+#define sk_SXNETID_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk))
+
+#define sk_SXNETID_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk));
+
+#define sk_SXNETID_value(sk, i)                                               \
+  ((SXNETID *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(SXNETID) *, sk), \
+                       (i)))
+
+#define sk_SXNETID_set(sk, i, p)                                           \
+  ((SXNETID *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), (i), \
+                     CHECKED_CAST(void *, SXNETID *, p)))
+
+#define sk_SXNETID_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk))
+
+#define sk_SXNETID_pop_free(sk, free_func)                     \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), \
+              CHECKED_CAST(void (*)(void *), void (*)(SXNETID *), free_func))
+
+#define sk_SXNETID_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), \
+            CHECKED_CAST(void *, SXNETID *, p), (where))
+
+#define sk_SXNETID_delete(sk, where)                                     \
+  ((SXNETID *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), \
+                        (where)))
+
+#define sk_SXNETID_delete_ptr(sk, p)                                         \
+  ((SXNETID *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), \
+                            CHECKED_CAST(void *, SXNETID *, p)))
+
+#define sk_SXNETID_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), (out_index), \
+          CHECKED_CAST(void *, SXNETID *, p))
+
+#define sk_SXNETID_shift(sk) \
+  ((SXNETID *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk)))
+
+#define sk_SXNETID_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), \
+          CHECKED_CAST(void *, SXNETID *, p))
+
+#define sk_SXNETID_pop(sk) \
+  ((SXNETID *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk)))
+
+#define sk_SXNETID_dup(sk)      \
+  ((STACK_OF(SXNETID) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(SXNETID) *, sk)))
+
+#define sk_SXNETID_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk))
+
+#define sk_SXNETID_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(SXNETID) *, sk))
+
+#define sk_SXNETID_set_cmp_func(sk, comp)                          \
+  ((int (*)(const SXNETID **a, const SXNETID **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk),             \
+      CHECKED_CAST(stack_cmp_func,                                 \
+                   int (*)(const SXNETID **a, const SXNETID **b), comp)))
+
+/* X509 */
+#define sk_X509_new(comp)                 \
+  ((STACK_OF(X509) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func, int (*)(const X509 **a, const X509 **b), comp)))
+
+#define sk_X509_new_null() ((STACK_OF(X509) *)sk_new_null())
+
+#define sk_X509_num(sk) sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk))
+
+#define sk_X509_zero(sk) sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk));
+
+#define sk_X509_value(sk, i) \
+  ((X509 *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(X509) *, sk), (i)))
+
+#define sk_X509_set(sk, i, p)                                        \
+  ((X509 *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), (i), \
+                  CHECKED_CAST(void *, X509 *, p)))
+
+#define sk_X509_free(sk) sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk))
+
+#define sk_X509_pop_free(sk, free_func)                     \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), \
+              CHECKED_CAST(void (*)(void *), void (*)(X509 *), free_func))
+
+#define sk_X509_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), \
+            CHECKED_CAST(void *, X509 *, p), (where))
+
+#define sk_X509_delete(sk, where) \
+  ((X509 *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), (where)))
+
+#define sk_X509_delete_ptr(sk, p)                                      \
+  ((X509 *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), \
+                         CHECKED_CAST(void *, X509 *, p)))
+
+#define sk_X509_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509 *, p))
+
+#define sk_X509_shift(sk) \
+  ((X509 *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk)))
+
+#define sk_X509_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), \
+          CHECKED_CAST(void *, X509 *, p))
+
+#define sk_X509_pop(sk) \
+  ((X509 *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk)))
+
+#define sk_X509_dup(sk) \
+  ((STACK_OF(X509) *)sk_dup(CHECKED_CAST(_STACK *, const STACK_OF(X509) *, sk)))
+
+#define sk_X509_sort(sk) sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk))
+
+#define sk_X509_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509) *, sk))
+
+#define sk_X509_set_cmp_func(sk, comp)                                      \
+  ((int (*)(const X509 **a, const X509 **b))sk_set_cmp_func(                \
+      CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk),                         \
+      CHECKED_CAST(stack_cmp_func, int (*)(const X509 **a, const X509 **b), \
+                   comp)))
+
+/* X509V3_EXT_METHOD */
+#define sk_X509V3_EXT_METHOD_new(comp)                                   \
+  ((STACK_OF(X509V3_EXT_METHOD) *)sk_new(CHECKED_CAST(                   \
+      stack_cmp_func,                                                    \
+      int (*)(const X509V3_EXT_METHOD **a, const X509V3_EXT_METHOD **b), \
+      comp)))
+
+#define sk_X509V3_EXT_METHOD_new_null() \
+  ((STACK_OF(X509V3_EXT_METHOD) *)sk_new_null())
+
+#define sk_X509V3_EXT_METHOD_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk))
+
+#define sk_X509V3_EXT_METHOD_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk));
+
+#define sk_X509V3_EXT_METHOD_value(sk, i) \
+  ((X509V3_EXT_METHOD *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk), (i)))
+
+#define sk_X509V3_EXT_METHOD_set(sk, i, p)                            \
+  ((X509V3_EXT_METHOD *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk), (i), \
+      CHECKED_CAST(void *, X509V3_EXT_METHOD *, p)))
+
+#define sk_X509V3_EXT_METHOD_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk))
+
+#define sk_X509V3_EXT_METHOD_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(X509V3_EXT_METHOD *), \
+                           free_func))
+
+#define sk_X509V3_EXT_METHOD_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk), \
+            CHECKED_CAST(void *, X509V3_EXT_METHOD *, p), (where))
+
+#define sk_X509V3_EXT_METHOD_delete(sk, where) \
+  ((X509V3_EXT_METHOD *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk), (where)))
+
+#define sk_X509V3_EXT_METHOD_delete_ptr(sk, p)                   \
+  ((X509V3_EXT_METHOD *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk), \
+      CHECKED_CAST(void *, X509V3_EXT_METHOD *, p)))
+
+#define sk_X509V3_EXT_METHOD_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk), \
+          (out_index), CHECKED_CAST(void *, X509V3_EXT_METHOD *, p))
+
+#define sk_X509V3_EXT_METHOD_shift(sk) \
+  ((X509V3_EXT_METHOD *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk)))
+
+#define sk_X509V3_EXT_METHOD_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk), \
+          CHECKED_CAST(void *, X509V3_EXT_METHOD *, p))
+
+#define sk_X509V3_EXT_METHOD_pop(sk) \
+  ((X509V3_EXT_METHOD *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk)))
+
+#define sk_X509V3_EXT_METHOD_dup(sk)      \
+  ((STACK_OF(X509V3_EXT_METHOD) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk)))
+
+#define sk_X509V3_EXT_METHOD_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk))
+
+#define sk_X509V3_EXT_METHOD_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk))
+
+#define sk_X509V3_EXT_METHOD_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509V3_EXT_METHOD **a, const X509V3_EXT_METHOD **b))    \
+   sk_set_cmp_func(                                                       \
+       CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509V3_EXT_METHOD **a,  \
+                                            const X509V3_EXT_METHOD **b), \
+                    comp)))
+
+/* X509_ALGOR */
+#define sk_X509_ALGOR_new(comp)                                            \
+  ((STACK_OF(X509_ALGOR) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const X509_ALGOR **a, const X509_ALGOR **b), \
+      comp)))
+
+#define sk_X509_ALGOR_new_null() ((STACK_OF(X509_ALGOR) *)sk_new_null())
+
+#define sk_X509_ALGOR_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk))
+
+#define sk_X509_ALGOR_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk));
+
+#define sk_X509_ALGOR_value(sk, i) \
+  ((X509_ALGOR *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_ALGOR) *, sk), (i)))
+
+#define sk_X509_ALGOR_set(sk, i, p)                                         \
+  ((X509_ALGOR *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
+                        (i), CHECKED_CAST(void *, X509_ALGOR *, p)))
+
+#define sk_X509_ALGOR_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk))
+
+#define sk_X509_ALGOR_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_ALGOR *), free_func))
+
+#define sk_X509_ALGOR_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
+            CHECKED_CAST(void *, X509_ALGOR *, p), (where))
+
+#define sk_X509_ALGOR_delete(sk, where)                                        \
+  ((X509_ALGOR *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
+                           (where)))
+
+#define sk_X509_ALGOR_delete_ptr(sk, p)                   \
+  ((X509_ALGOR *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
+      CHECKED_CAST(void *, X509_ALGOR *, p)))
+
+#define sk_X509_ALGOR_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_ALGOR *, p))
+
+#define sk_X509_ALGOR_shift(sk) \
+  ((X509_ALGOR *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk)))
+
+#define sk_X509_ALGOR_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
+          CHECKED_CAST(void *, X509_ALGOR *, p))
+
+#define sk_X509_ALGOR_pop(sk) \
+  ((X509_ALGOR *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk)))
+
+#define sk_X509_ALGOR_dup(sk)      \
+  ((STACK_OF(X509_ALGOR) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_ALGOR) *, sk)))
+
+#define sk_X509_ALGOR_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk))
+
+#define sk_X509_ALGOR_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_ALGOR) *, sk))
+
+#define sk_X509_ALGOR_set_cmp_func(sk, comp)                             \
+  ((int (*)(const X509_ALGOR **a, const X509_ALGOR **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const X509_ALGOR **a, const X509_ALGOR **b),  \
+                   comp)))
+
+/* X509_ATTRIBUTE */
+#define sk_X509_ATTRIBUTE_new(comp)                 \
+  ((STACK_OF(X509_ATTRIBUTE) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                               \
+      int (*)(const X509_ATTRIBUTE **a, const X509_ATTRIBUTE **b), comp)))
+
+#define sk_X509_ATTRIBUTE_new_null() ((STACK_OF(X509_ATTRIBUTE) *)sk_new_null())
+
+#define sk_X509_ATTRIBUTE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk))
+
+#define sk_X509_ATTRIBUTE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk));
+
+#define sk_X509_ATTRIBUTE_value(sk, i) \
+  ((X509_ATTRIBUTE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk), (i)))
+
+#define sk_X509_ATTRIBUTE_set(sk, i, p)                            \
+  ((X509_ATTRIBUTE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), (i), \
+      CHECKED_CAST(void *, X509_ATTRIBUTE *, p)))
+
+#define sk_X509_ATTRIBUTE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk))
+
+#define sk_X509_ATTRIBUTE_pop_free(sk, free_func)             \
+  sk_pop_free(                                                \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_ATTRIBUTE *), free_func))
+
+#define sk_X509_ATTRIBUTE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), \
+            CHECKED_CAST(void *, X509_ATTRIBUTE *, p), (where))
+
+#define sk_X509_ATTRIBUTE_delete(sk, where) \
+  ((X509_ATTRIBUTE *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), (where)))
+
+#define sk_X509_ATTRIBUTE_delete_ptr(sk, p)                   \
+  ((X509_ATTRIBUTE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), \
+      CHECKED_CAST(void *, X509_ATTRIBUTE *, p)))
+
+#define sk_X509_ATTRIBUTE_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_ATTRIBUTE *, p))
+
+#define sk_X509_ATTRIBUTE_shift(sk) \
+  ((X509_ATTRIBUTE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk)))
+
+#define sk_X509_ATTRIBUTE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk), \
+          CHECKED_CAST(void *, X509_ATTRIBUTE *, p))
+
+#define sk_X509_ATTRIBUTE_pop(sk) \
+  ((X509_ATTRIBUTE *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk)))
+
+#define sk_X509_ATTRIBUTE_dup(sk)      \
+  ((STACK_OF(X509_ATTRIBUTE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk)))
+
+#define sk_X509_ATTRIBUTE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk))
+
+#define sk_X509_ATTRIBUTE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk))
+
+#define sk_X509_ATTRIBUTE_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_ATTRIBUTE **a, const X509_ATTRIBUTE **b))       \
+   sk_set_cmp_func(                                                    \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_ATTRIBUTE **a,  \
+                                            const X509_ATTRIBUTE **b), \
+                    comp)))
+
+/* X509_CRL */
+#define sk_X509_CRL_new(comp)                 \
+  ((STACK_OF(X509_CRL) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func, int (*)(const X509_CRL **a, const X509_CRL **b), comp)))
+
+#define sk_X509_CRL_new_null() ((STACK_OF(X509_CRL) *)sk_new_null())
+
+#define sk_X509_CRL_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk))
+
+#define sk_X509_CRL_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk));
+
+#define sk_X509_CRL_value(sk, i) \
+  ((X509_CRL *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_CRL) *, sk), (i)))
+
+#define sk_X509_CRL_set(sk, i, p)                                            \
+  ((X509_CRL *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), (i), \
+                      CHECKED_CAST(void *, X509_CRL *, p)))
+
+#define sk_X509_CRL_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk))
+
+#define sk_X509_CRL_pop_free(sk, free_func)                     \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), \
+              CHECKED_CAST(void (*)(void *), void (*)(X509_CRL *), free_func))
+
+#define sk_X509_CRL_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), \
+            CHECKED_CAST(void *, X509_CRL *, p), (where))
+
+#define sk_X509_CRL_delete(sk, where)                                      \
+  ((X509_CRL *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), \
+                         (where)))
+
+#define sk_X509_CRL_delete_ptr(sk, p)                                          \
+  ((X509_CRL *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), \
+                             CHECKED_CAST(void *, X509_CRL *, p)))
+
+#define sk_X509_CRL_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_CRL *, p))
+
+#define sk_X509_CRL_shift(sk) \
+  ((X509_CRL *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk)))
+
+#define sk_X509_CRL_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), \
+          CHECKED_CAST(void *, X509_CRL *, p))
+
+#define sk_X509_CRL_pop(sk) \
+  ((X509_CRL *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk)))
+
+#define sk_X509_CRL_dup(sk)      \
+  ((STACK_OF(X509_CRL) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_CRL) *, sk)))
+
+#define sk_X509_CRL_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk))
+
+#define sk_X509_CRL_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_CRL) *, sk))
+
+#define sk_X509_CRL_set_cmp_func(sk, comp)                           \
+  ((int (*)(const X509_CRL **a, const X509_CRL **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk),              \
+      CHECKED_CAST(stack_cmp_func,                                   \
+                   int (*)(const X509_CRL **a, const X509_CRL **b), comp)))
+
+/* X509_EXTENSION */
+#define sk_X509_EXTENSION_new(comp)                 \
+  ((STACK_OF(X509_EXTENSION) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                               \
+      int (*)(const X509_EXTENSION **a, const X509_EXTENSION **b), comp)))
+
+#define sk_X509_EXTENSION_new_null() ((STACK_OF(X509_EXTENSION) *)sk_new_null())
+
+#define sk_X509_EXTENSION_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk))
+
+#define sk_X509_EXTENSION_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk));
+
+#define sk_X509_EXTENSION_value(sk, i) \
+  ((X509_EXTENSION *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_EXTENSION) *, sk), (i)))
+
+#define sk_X509_EXTENSION_set(sk, i, p)                            \
+  ((X509_EXTENSION *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), (i), \
+      CHECKED_CAST(void *, X509_EXTENSION *, p)))
+
+#define sk_X509_EXTENSION_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk))
+
+#define sk_X509_EXTENSION_pop_free(sk, free_func)             \
+  sk_pop_free(                                                \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_EXTENSION *), free_func))
+
+#define sk_X509_EXTENSION_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), \
+            CHECKED_CAST(void *, X509_EXTENSION *, p), (where))
+
+#define sk_X509_EXTENSION_delete(sk, where) \
+  ((X509_EXTENSION *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), (where)))
+
+#define sk_X509_EXTENSION_delete_ptr(sk, p)                   \
+  ((X509_EXTENSION *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), \
+      CHECKED_CAST(void *, X509_EXTENSION *, p)))
+
+#define sk_X509_EXTENSION_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_EXTENSION *, p))
+
+#define sk_X509_EXTENSION_shift(sk) \
+  ((X509_EXTENSION *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk)))
+
+#define sk_X509_EXTENSION_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk), \
+          CHECKED_CAST(void *, X509_EXTENSION *, p))
+
+#define sk_X509_EXTENSION_pop(sk) \
+  ((X509_EXTENSION *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk)))
+
+#define sk_X509_EXTENSION_dup(sk)      \
+  ((STACK_OF(X509_EXTENSION) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_EXTENSION) *, sk)))
+
+#define sk_X509_EXTENSION_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk))
+
+#define sk_X509_EXTENSION_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_EXTENSION) *, sk))
+
+#define sk_X509_EXTENSION_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_EXTENSION **a, const X509_EXTENSION **b))       \
+   sk_set_cmp_func(                                                    \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_EXTENSION **a,  \
+                                            const X509_EXTENSION **b), \
+                    comp)))
+
+/* X509_INFO */
+#define sk_X509_INFO_new(comp)     \
+  ((STACK_OF(X509_INFO) *)sk_new(  \
+      CHECKED_CAST(stack_cmp_func, \
+                   int (*)(const X509_INFO **a, const X509_INFO **b), comp)))
+
+#define sk_X509_INFO_new_null() ((STACK_OF(X509_INFO) *)sk_new_null())
+
+#define sk_X509_INFO_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk))
+
+#define sk_X509_INFO_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk));
+
+#define sk_X509_INFO_value(sk, i) \
+  ((X509_INFO *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_INFO) *, sk), (i)))
+
+#define sk_X509_INFO_set(sk, i, p)                                             \
+  ((X509_INFO *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), (i), \
+                       CHECKED_CAST(void *, X509_INFO *, p)))
+
+#define sk_X509_INFO_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk))
+
+#define sk_X509_INFO_pop_free(sk, free_func)             \
+  sk_pop_free(                                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_INFO *), free_func))
+
+#define sk_X509_INFO_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), \
+            CHECKED_CAST(void *, X509_INFO *, p), (where))
+
+#define sk_X509_INFO_delete(sk, where)                                       \
+  ((X509_INFO *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), \
+                          (where)))
+
+#define sk_X509_INFO_delete_ptr(sk, p)                   \
+  ((X509_INFO *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), \
+      CHECKED_CAST(void *, X509_INFO *, p)))
+
+#define sk_X509_INFO_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_INFO *, p))
+
+#define sk_X509_INFO_shift(sk) \
+  ((X509_INFO *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk)))
+
+#define sk_X509_INFO_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), \
+          CHECKED_CAST(void *, X509_INFO *, p))
+
+#define sk_X509_INFO_pop(sk) \
+  ((X509_INFO *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk)))
+
+#define sk_X509_INFO_dup(sk)      \
+  ((STACK_OF(X509_INFO) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_INFO) *, sk)))
+
+#define sk_X509_INFO_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk))
+
+#define sk_X509_INFO_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_INFO) *, sk))
+
+#define sk_X509_INFO_set_cmp_func(sk, comp)                            \
+  ((int (*)(const X509_INFO **a, const X509_INFO **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk),               \
+      CHECKED_CAST(stack_cmp_func,                                     \
+                   int (*)(const X509_INFO **a, const X509_INFO **b), comp)))
+
+/* X509_LOOKUP */
+#define sk_X509_LOOKUP_new(comp)                                             \
+  ((STACK_OF(X509_LOOKUP) *)sk_new(CHECKED_CAST(                             \
+      stack_cmp_func, int (*)(const X509_LOOKUP **a, const X509_LOOKUP **b), \
+      comp)))
+
+#define sk_X509_LOOKUP_new_null() ((STACK_OF(X509_LOOKUP) *)sk_new_null())
+
+#define sk_X509_LOOKUP_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk))
+
+#define sk_X509_LOOKUP_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk));
+
+#define sk_X509_LOOKUP_value(sk, i) \
+  ((X509_LOOKUP *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_LOOKUP) *, sk), (i)))
+
+#define sk_X509_LOOKUP_set(sk, i, p)                                          \
+  ((X509_LOOKUP *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), \
+                         (i), CHECKED_CAST(void *, X509_LOOKUP *, p)))
+
+#define sk_X509_LOOKUP_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk))
+
+#define sk_X509_LOOKUP_pop_free(sk, free_func)             \
+  sk_pop_free(                                             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_LOOKUP *), free_func))
+
+#define sk_X509_LOOKUP_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), \
+            CHECKED_CAST(void *, X509_LOOKUP *, p), (where))
+
+#define sk_X509_LOOKUP_delete(sk, where) \
+  ((X509_LOOKUP *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), (where)))
+
+#define sk_X509_LOOKUP_delete_ptr(sk, p)                   \
+  ((X509_LOOKUP *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), \
+      CHECKED_CAST(void *, X509_LOOKUP *, p)))
+
+#define sk_X509_LOOKUP_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_LOOKUP *, p))
+
+#define sk_X509_LOOKUP_shift(sk) \
+  ((X509_LOOKUP *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk)))
+
+#define sk_X509_LOOKUP_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), \
+          CHECKED_CAST(void *, X509_LOOKUP *, p))
+
+#define sk_X509_LOOKUP_pop(sk) \
+  ((X509_LOOKUP *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk)))
+
+#define sk_X509_LOOKUP_dup(sk)      \
+  ((STACK_OF(X509_LOOKUP) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_LOOKUP) *, sk)))
+
+#define sk_X509_LOOKUP_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk))
+
+#define sk_X509_LOOKUP_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_LOOKUP) *, sk))
+
+#define sk_X509_LOOKUP_set_cmp_func(sk, comp)                              \
+  ((int (*)(const X509_LOOKUP **a, const X509_LOOKUP **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk),                 \
+      CHECKED_CAST(stack_cmp_func,                                         \
+                   int (*)(const X509_LOOKUP **a, const X509_LOOKUP **b),  \
+                   comp)))
+
+/* X509_NAME */
+#define sk_X509_NAME_new(comp)     \
+  ((STACK_OF(X509_NAME) *)sk_new(  \
+      CHECKED_CAST(stack_cmp_func, \
+                   int (*)(const X509_NAME **a, const X509_NAME **b), comp)))
+
+#define sk_X509_NAME_new_null() ((STACK_OF(X509_NAME) *)sk_new_null())
+
+#define sk_X509_NAME_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk))
+
+#define sk_X509_NAME_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk));
+
+#define sk_X509_NAME_value(sk, i) \
+  ((X509_NAME *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME) *, sk), (i)))
+
+#define sk_X509_NAME_set(sk, i, p)                                             \
+  ((X509_NAME *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), (i), \
+                       CHECKED_CAST(void *, X509_NAME *, p)))
+
+#define sk_X509_NAME_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk))
+
+#define sk_X509_NAME_pop_free(sk, free_func)             \
+  sk_pop_free(                                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_NAME *), free_func))
+
+#define sk_X509_NAME_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), \
+            CHECKED_CAST(void *, X509_NAME *, p), (where))
+
+#define sk_X509_NAME_delete(sk, where)                                       \
+  ((X509_NAME *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), \
+                          (where)))
+
+#define sk_X509_NAME_delete_ptr(sk, p)                   \
+  ((X509_NAME *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), \
+      CHECKED_CAST(void *, X509_NAME *, p)))
+
+#define sk_X509_NAME_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_NAME *, p))
+
+#define sk_X509_NAME_shift(sk) \
+  ((X509_NAME *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk)))
+
+#define sk_X509_NAME_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), \
+          CHECKED_CAST(void *, X509_NAME *, p))
+
+#define sk_X509_NAME_pop(sk) \
+  ((X509_NAME *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk)))
+
+#define sk_X509_NAME_dup(sk)      \
+  ((STACK_OF(X509_NAME) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME) *, sk)))
+
+#define sk_X509_NAME_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk))
+
+#define sk_X509_NAME_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME) *, sk))
+
+#define sk_X509_NAME_set_cmp_func(sk, comp)                            \
+  ((int (*)(const X509_NAME **a, const X509_NAME **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk),               \
+      CHECKED_CAST(stack_cmp_func,                                     \
+                   int (*)(const X509_NAME **a, const X509_NAME **b), comp)))
+
+/* X509_NAME_ENTRY */
+#define sk_X509_NAME_ENTRY_new(comp)                 \
+  ((STACK_OF(X509_NAME_ENTRY) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                                \
+      int (*)(const X509_NAME_ENTRY **a, const X509_NAME_ENTRY **b), comp)))
+
+#define sk_X509_NAME_ENTRY_new_null() \
+  ((STACK_OF(X509_NAME_ENTRY) *)sk_new_null())
+
+#define sk_X509_NAME_ENTRY_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk));
+
+#define sk_X509_NAME_ENTRY_value(sk, i) \
+  ((X509_NAME_ENTRY *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk), (i)))
+
+#define sk_X509_NAME_ENTRY_set(sk, i, p)                            \
+  ((X509_NAME_ENTRY *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), (i), \
+      CHECKED_CAST(void *, X509_NAME_ENTRY *, p)))
+
+#define sk_X509_NAME_ENTRY_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_pop_free(sk, free_func)             \
+  sk_pop_free(                                                 \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_NAME_ENTRY *), free_func))
+
+#define sk_X509_NAME_ENTRY_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+            CHECKED_CAST(void *, X509_NAME_ENTRY *, p), (where))
+
+#define sk_X509_NAME_ENTRY_delete(sk, where) \
+  ((X509_NAME_ENTRY *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), (where)))
+
+#define sk_X509_NAME_ENTRY_delete_ptr(sk, p)                   \
+  ((X509_NAME_ENTRY *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+      CHECKED_CAST(void *, X509_NAME_ENTRY *, p)))
+
+#define sk_X509_NAME_ENTRY_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+          (out_index), CHECKED_CAST(void *, X509_NAME_ENTRY *, p))
+
+#define sk_X509_NAME_ENTRY_shift(sk) \
+  ((X509_NAME_ENTRY *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk)))
+
+#define sk_X509_NAME_ENTRY_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+          CHECKED_CAST(void *, X509_NAME_ENTRY *, p))
+
+#define sk_X509_NAME_ENTRY_pop(sk) \
+  ((X509_NAME_ENTRY *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk)))
+
+#define sk_X509_NAME_ENTRY_dup(sk)      \
+  ((STACK_OF(X509_NAME_ENTRY) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk)))
+
+#define sk_X509_NAME_ENTRY_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_NAME_ENTRY **a, const X509_NAME_ENTRY **b))      \
+   sk_set_cmp_func(                                                     \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_NAME_ENTRY **a,  \
+                                            const X509_NAME_ENTRY **b), \
+                    comp)))
+
+/* X509_NAME_ENTRY */
+#define sk_X509_NAME_ENTRY_new(comp)                 \
+  ((STACK_OF(X509_NAME_ENTRY) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                                \
+      int (*)(const X509_NAME_ENTRY **a, const X509_NAME_ENTRY **b), comp)))
+
+#define sk_X509_NAME_ENTRY_new_null() \
+  ((STACK_OF(X509_NAME_ENTRY) *)sk_new_null())
+
+#define sk_X509_NAME_ENTRY_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk));
+
+#define sk_X509_NAME_ENTRY_value(sk, i) \
+  ((X509_NAME_ENTRY *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk), (i)))
+
+#define sk_X509_NAME_ENTRY_set(sk, i, p)                            \
+  ((X509_NAME_ENTRY *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), (i), \
+      CHECKED_CAST(void *, X509_NAME_ENTRY *, p)))
+
+#define sk_X509_NAME_ENTRY_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_pop_free(sk, free_func)             \
+  sk_pop_free(                                                 \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_NAME_ENTRY *), free_func))
+
+#define sk_X509_NAME_ENTRY_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+            CHECKED_CAST(void *, X509_NAME_ENTRY *, p), (where))
+
+#define sk_X509_NAME_ENTRY_delete(sk, where) \
+  ((X509_NAME_ENTRY *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), (where)))
+
+#define sk_X509_NAME_ENTRY_delete_ptr(sk, p)                   \
+  ((X509_NAME_ENTRY *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+      CHECKED_CAST(void *, X509_NAME_ENTRY *, p)))
+
+#define sk_X509_NAME_ENTRY_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+          (out_index), CHECKED_CAST(void *, X509_NAME_ENTRY *, p))
+
+#define sk_X509_NAME_ENTRY_shift(sk) \
+  ((X509_NAME_ENTRY *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk)))
+
+#define sk_X509_NAME_ENTRY_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk), \
+          CHECKED_CAST(void *, X509_NAME_ENTRY *, p))
+
+#define sk_X509_NAME_ENTRY_pop(sk) \
+  ((X509_NAME_ENTRY *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk)))
+
+#define sk_X509_NAME_ENTRY_dup(sk)      \
+  ((STACK_OF(X509_NAME_ENTRY) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk)))
+
+#define sk_X509_NAME_ENTRY_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk))
+
+#define sk_X509_NAME_ENTRY_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_NAME_ENTRY **a, const X509_NAME_ENTRY **b))      \
+   sk_set_cmp_func(                                                     \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_NAME_ENTRY **a,  \
+                                            const X509_NAME_ENTRY **b), \
+                    comp)))
+
+/* X509_OBJECT */
+#define sk_X509_OBJECT_new(comp)                                             \
+  ((STACK_OF(X509_OBJECT) *)sk_new(CHECKED_CAST(                             \
+      stack_cmp_func, int (*)(const X509_OBJECT **a, const X509_OBJECT **b), \
+      comp)))
+
+#define sk_X509_OBJECT_new_null() ((STACK_OF(X509_OBJECT) *)sk_new_null())
+
+#define sk_X509_OBJECT_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk))
+
+#define sk_X509_OBJECT_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk));
+
+#define sk_X509_OBJECT_value(sk, i) \
+  ((X509_OBJECT *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_OBJECT) *, sk), (i)))
+
+#define sk_X509_OBJECT_set(sk, i, p)                                          \
+  ((X509_OBJECT *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), \
+                         (i), CHECKED_CAST(void *, X509_OBJECT *, p)))
+
+#define sk_X509_OBJECT_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk))
+
+#define sk_X509_OBJECT_pop_free(sk, free_func)             \
+  sk_pop_free(                                             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_OBJECT *), free_func))
+
+#define sk_X509_OBJECT_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), \
+            CHECKED_CAST(void *, X509_OBJECT *, p), (where))
+
+#define sk_X509_OBJECT_delete(sk, where) \
+  ((X509_OBJECT *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), (where)))
+
+#define sk_X509_OBJECT_delete_ptr(sk, p)                   \
+  ((X509_OBJECT *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), \
+      CHECKED_CAST(void *, X509_OBJECT *, p)))
+
+#define sk_X509_OBJECT_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_OBJECT *, p))
+
+#define sk_X509_OBJECT_shift(sk) \
+  ((X509_OBJECT *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk)))
+
+#define sk_X509_OBJECT_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), \
+          CHECKED_CAST(void *, X509_OBJECT *, p))
+
+#define sk_X509_OBJECT_pop(sk) \
+  ((X509_OBJECT *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk)))
+
+#define sk_X509_OBJECT_dup(sk)      \
+  ((STACK_OF(X509_OBJECT) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_OBJECT) *, sk)))
+
+#define sk_X509_OBJECT_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk))
+
+#define sk_X509_OBJECT_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_OBJECT) *, sk))
+
+#define sk_X509_OBJECT_set_cmp_func(sk, comp)                              \
+  ((int (*)(const X509_OBJECT **a, const X509_OBJECT **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk),                 \
+      CHECKED_CAST(stack_cmp_func,                                         \
+                   int (*)(const X509_OBJECT **a, const X509_OBJECT **b),  \
+                   comp)))
+
+/* X509_POLICY_DATA */
+#define sk_X509_POLICY_DATA_new(comp)                 \
+  ((STACK_OF(X509_POLICY_DATA) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                                 \
+      int (*)(const X509_POLICY_DATA **a, const X509_POLICY_DATA **b), comp)))
+
+#define sk_X509_POLICY_DATA_new_null() \
+  ((STACK_OF(X509_POLICY_DATA) *)sk_new_null())
+
+#define sk_X509_POLICY_DATA_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk))
+
+#define sk_X509_POLICY_DATA_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk));
+
+#define sk_X509_POLICY_DATA_value(sk, i) \
+  ((X509_POLICY_DATA *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_DATA) *, sk), (i)))
+
+#define sk_X509_POLICY_DATA_set(sk, i, p)                            \
+  ((X509_POLICY_DATA *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), (i), \
+      CHECKED_CAST(void *, X509_POLICY_DATA *, p)))
+
+#define sk_X509_POLICY_DATA_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk))
+
+#define sk_X509_POLICY_DATA_pop_free(sk, free_func)             \
+  sk_pop_free(                                                  \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_POLICY_DATA *), free_func))
+
+#define sk_X509_POLICY_DATA_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), \
+            CHECKED_CAST(void *, X509_POLICY_DATA *, p), (where))
+
+#define sk_X509_POLICY_DATA_delete(sk, where) \
+  ((X509_POLICY_DATA *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), (where)))
+
+#define sk_X509_POLICY_DATA_delete_ptr(sk, p)                   \
+  ((X509_POLICY_DATA *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), \
+      CHECKED_CAST(void *, X509_POLICY_DATA *, p)))
+
+#define sk_X509_POLICY_DATA_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), \
+          (out_index), CHECKED_CAST(void *, X509_POLICY_DATA *, p))
+
+#define sk_X509_POLICY_DATA_shift(sk) \
+  ((X509_POLICY_DATA *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk)))
+
+#define sk_X509_POLICY_DATA_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk), \
+          CHECKED_CAST(void *, X509_POLICY_DATA *, p))
+
+#define sk_X509_POLICY_DATA_pop(sk) \
+  ((X509_POLICY_DATA *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk)))
+
+#define sk_X509_POLICY_DATA_dup(sk)      \
+  ((STACK_OF(X509_POLICY_DATA) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_DATA) *, sk)))
+
+#define sk_X509_POLICY_DATA_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk))
+
+#define sk_X509_POLICY_DATA_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_DATA) *, sk))
+
+#define sk_X509_POLICY_DATA_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_POLICY_DATA **a, const X509_POLICY_DATA **b))     \
+   sk_set_cmp_func(                                                      \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_POLICY_DATA **a,  \
+                                            const X509_POLICY_DATA **b), \
+                    comp)))
+
+/* X509_POLICY_NODE */
+#define sk_X509_POLICY_NODE_new(comp)                 \
+  ((STACK_OF(X509_POLICY_NODE) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                                 \
+      int (*)(const X509_POLICY_NODE **a, const X509_POLICY_NODE **b), comp)))
+
+#define sk_X509_POLICY_NODE_new_null() \
+  ((STACK_OF(X509_POLICY_NODE) *)sk_new_null())
+
+#define sk_X509_POLICY_NODE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk))
+
+#define sk_X509_POLICY_NODE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk));
+
+#define sk_X509_POLICY_NODE_value(sk, i) \
+  ((X509_POLICY_NODE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_NODE) *, sk), (i)))
+
+#define sk_X509_POLICY_NODE_set(sk, i, p)                            \
+  ((X509_POLICY_NODE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), (i), \
+      CHECKED_CAST(void *, X509_POLICY_NODE *, p)))
+
+#define sk_X509_POLICY_NODE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk))
+
+#define sk_X509_POLICY_NODE_pop_free(sk, free_func)             \
+  sk_pop_free(                                                  \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_POLICY_NODE *), free_func))
+
+#define sk_X509_POLICY_NODE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), \
+            CHECKED_CAST(void *, X509_POLICY_NODE *, p), (where))
+
+#define sk_X509_POLICY_NODE_delete(sk, where) \
+  ((X509_POLICY_NODE *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), (where)))
+
+#define sk_X509_POLICY_NODE_delete_ptr(sk, p)                   \
+  ((X509_POLICY_NODE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), \
+      CHECKED_CAST(void *, X509_POLICY_NODE *, p)))
+
+#define sk_X509_POLICY_NODE_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), \
+          (out_index), CHECKED_CAST(void *, X509_POLICY_NODE *, p))
+
+#define sk_X509_POLICY_NODE_shift(sk) \
+  ((X509_POLICY_NODE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk)))
+
+#define sk_X509_POLICY_NODE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk), \
+          CHECKED_CAST(void *, X509_POLICY_NODE *, p))
+
+#define sk_X509_POLICY_NODE_pop(sk) \
+  ((X509_POLICY_NODE *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk)))
+
+#define sk_X509_POLICY_NODE_dup(sk)      \
+  ((STACK_OF(X509_POLICY_NODE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_NODE) *, sk)))
+
+#define sk_X509_POLICY_NODE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk))
+
+#define sk_X509_POLICY_NODE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_NODE) *, sk))
+
+#define sk_X509_POLICY_NODE_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_POLICY_NODE **a, const X509_POLICY_NODE **b))     \
+   sk_set_cmp_func(                                                      \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_POLICY_NODE **a,  \
+                                            const X509_POLICY_NODE **b), \
+                    comp)))
+
+/* X509_PURPOSE */
+#define sk_X509_PURPOSE_new(comp)                                              \
+  ((STACK_OF(X509_PURPOSE) *)sk_new(CHECKED_CAST(                              \
+      stack_cmp_func, int (*)(const X509_PURPOSE **a, const X509_PURPOSE **b), \
+      comp)))
+
+#define sk_X509_PURPOSE_new_null() ((STACK_OF(X509_PURPOSE) *)sk_new_null())
+
+#define sk_X509_PURPOSE_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk))
+
+#define sk_X509_PURPOSE_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk));
+
+#define sk_X509_PURPOSE_value(sk, i) \
+  ((X509_PURPOSE *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_PURPOSE) *, sk), (i)))
+
+#define sk_X509_PURPOSE_set(sk, i, p)                            \
+  ((X509_PURPOSE *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), (i), \
+      CHECKED_CAST(void *, X509_PURPOSE *, p)))
+
+#define sk_X509_PURPOSE_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk))
+
+#define sk_X509_PURPOSE_pop_free(sk, free_func)             \
+  sk_pop_free(                                              \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_PURPOSE *), free_func))
+
+#define sk_X509_PURPOSE_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), \
+            CHECKED_CAST(void *, X509_PURPOSE *, p), (where))
+
+#define sk_X509_PURPOSE_delete(sk, where) \
+  ((X509_PURPOSE *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), (where)))
+
+#define sk_X509_PURPOSE_delete_ptr(sk, p)                   \
+  ((X509_PURPOSE *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), \
+      CHECKED_CAST(void *, X509_PURPOSE *, p)))
+
+#define sk_X509_PURPOSE_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_PURPOSE *, p))
+
+#define sk_X509_PURPOSE_shift(sk) \
+  ((X509_PURPOSE *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk)))
+
+#define sk_X509_PURPOSE_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk), \
+          CHECKED_CAST(void *, X509_PURPOSE *, p))
+
+#define sk_X509_PURPOSE_pop(sk) \
+  ((X509_PURPOSE *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk)))
+
+#define sk_X509_PURPOSE_dup(sk)      \
+  ((STACK_OF(X509_PURPOSE) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_PURPOSE) *, sk)))
+
+#define sk_X509_PURPOSE_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk))
+
+#define sk_X509_PURPOSE_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_PURPOSE) *, sk))
+
+#define sk_X509_PURPOSE_set_cmp_func(sk, comp)                               \
+  ((int (*)(const X509_PURPOSE **a, const X509_PURPOSE **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk),                  \
+      CHECKED_CAST(stack_cmp_func,                                           \
+                   int (*)(const X509_PURPOSE **a, const X509_PURPOSE **b),  \
+                   comp)))
+
+/* X509_REVOKED */
+#define sk_X509_REVOKED_new(comp)                                              \
+  ((STACK_OF(X509_REVOKED) *)sk_new(CHECKED_CAST(                              \
+      stack_cmp_func, int (*)(const X509_REVOKED **a, const X509_REVOKED **b), \
+      comp)))
+
+#define sk_X509_REVOKED_new_null() ((STACK_OF(X509_REVOKED) *)sk_new_null())
+
+#define sk_X509_REVOKED_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk))
+
+#define sk_X509_REVOKED_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk));
+
+#define sk_X509_REVOKED_value(sk, i) \
+  ((X509_REVOKED *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_REVOKED) *, sk), (i)))
+
+#define sk_X509_REVOKED_set(sk, i, p)                            \
+  ((X509_REVOKED *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), (i), \
+      CHECKED_CAST(void *, X509_REVOKED *, p)))
+
+#define sk_X509_REVOKED_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk))
+
+#define sk_X509_REVOKED_pop_free(sk, free_func)             \
+  sk_pop_free(                                              \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_REVOKED *), free_func))
+
+#define sk_X509_REVOKED_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), \
+            CHECKED_CAST(void *, X509_REVOKED *, p), (where))
+
+#define sk_X509_REVOKED_delete(sk, where) \
+  ((X509_REVOKED *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), (where)))
+
+#define sk_X509_REVOKED_delete_ptr(sk, p)                   \
+  ((X509_REVOKED *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), \
+      CHECKED_CAST(void *, X509_REVOKED *, p)))
+
+#define sk_X509_REVOKED_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_REVOKED *, p))
+
+#define sk_X509_REVOKED_shift(sk) \
+  ((X509_REVOKED *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk)))
+
+#define sk_X509_REVOKED_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk), \
+          CHECKED_CAST(void *, X509_REVOKED *, p))
+
+#define sk_X509_REVOKED_pop(sk) \
+  ((X509_REVOKED *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk)))
+
+#define sk_X509_REVOKED_dup(sk)      \
+  ((STACK_OF(X509_REVOKED) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_REVOKED) *, sk)))
+
+#define sk_X509_REVOKED_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk))
+
+#define sk_X509_REVOKED_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_REVOKED) *, sk))
+
+#define sk_X509_REVOKED_set_cmp_func(sk, comp)                               \
+  ((int (*)(const X509_REVOKED **a, const X509_REVOKED **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk),                  \
+      CHECKED_CAST(stack_cmp_func,                                           \
+                   int (*)(const X509_REVOKED **a, const X509_REVOKED **b),  \
+                   comp)))
+
+/* X509_TRUST */
+#define sk_X509_TRUST_new(comp)                                            \
+  ((STACK_OF(X509_TRUST) *)sk_new(CHECKED_CAST(                            \
+      stack_cmp_func, int (*)(const X509_TRUST **a, const X509_TRUST **b), \
+      comp)))
+
+#define sk_X509_TRUST_new_null() ((STACK_OF(X509_TRUST) *)sk_new_null())
+
+#define sk_X509_TRUST_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk))
+
+#define sk_X509_TRUST_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk));
+
+#define sk_X509_TRUST_value(sk, i) \
+  ((X509_TRUST *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_TRUST) *, sk), (i)))
+
+#define sk_X509_TRUST_set(sk, i, p)                                         \
+  ((X509_TRUST *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
+                        (i), CHECKED_CAST(void *, X509_TRUST *, p)))
+
+#define sk_X509_TRUST_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk))
+
+#define sk_X509_TRUST_pop_free(sk, free_func)             \
+  sk_pop_free(                                            \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(X509_TRUST *), free_func))
+
+#define sk_X509_TRUST_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
+            CHECKED_CAST(void *, X509_TRUST *, p), (where))
+
+#define sk_X509_TRUST_delete(sk, where)                                        \
+  ((X509_TRUST *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
+                           (where)))
+
+#define sk_X509_TRUST_delete_ptr(sk, p)                   \
+  ((X509_TRUST *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
+      CHECKED_CAST(void *, X509_TRUST *, p)))
+
+#define sk_X509_TRUST_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), (out_index), \
+          CHECKED_CAST(void *, X509_TRUST *, p))
+
+#define sk_X509_TRUST_shift(sk) \
+  ((X509_TRUST *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk)))
+
+#define sk_X509_TRUST_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
+          CHECKED_CAST(void *, X509_TRUST *, p))
+
+#define sk_X509_TRUST_pop(sk) \
+  ((X509_TRUST *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk)))
+
+#define sk_X509_TRUST_dup(sk)      \
+  ((STACK_OF(X509_TRUST) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_TRUST) *, sk)))
+
+#define sk_X509_TRUST_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk))
+
+#define sk_X509_TRUST_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_TRUST) *, sk))
+
+#define sk_X509_TRUST_set_cmp_func(sk, comp)                             \
+  ((int (*)(const X509_TRUST **a, const X509_TRUST **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk),                \
+      CHECKED_CAST(stack_cmp_func,                                       \
+                   int (*)(const X509_TRUST **a, const X509_TRUST **b),  \
+                   comp)))
+
+/* X509_VERIFY_PARAM */
+#define sk_X509_VERIFY_PARAM_new(comp)                                   \
+  ((STACK_OF(X509_VERIFY_PARAM) *)sk_new(CHECKED_CAST(                   \
+      stack_cmp_func,                                                    \
+      int (*)(const X509_VERIFY_PARAM **a, const X509_VERIFY_PARAM **b), \
+      comp)))
+
+#define sk_X509_VERIFY_PARAM_new_null() \
+  ((STACK_OF(X509_VERIFY_PARAM) *)sk_new_null())
+
+#define sk_X509_VERIFY_PARAM_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk))
+
+#define sk_X509_VERIFY_PARAM_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk));
+
+#define sk_X509_VERIFY_PARAM_value(sk, i) \
+  ((X509_VERIFY_PARAM *)sk_value(         \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk), (i)))
+
+#define sk_X509_VERIFY_PARAM_set(sk, i, p)                            \
+  ((X509_VERIFY_PARAM *)sk_set(                                       \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk), (i), \
+      CHECKED_CAST(void *, X509_VERIFY_PARAM *, p)))
+
+#define sk_X509_VERIFY_PARAM_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk))
+
+#define sk_X509_VERIFY_PARAM_pop_free(sk, free_func)                        \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk),    \
+              CHECKED_CAST(void (*)(void *), void (*)(X509_VERIFY_PARAM *), \
+                           free_func))
+
+#define sk_X509_VERIFY_PARAM_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk), \
+            CHECKED_CAST(void *, X509_VERIFY_PARAM *, p), (where))
+
+#define sk_X509_VERIFY_PARAM_delete(sk, where) \
+  ((X509_VERIFY_PARAM *)sk_delete(             \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk), (where)))
+
+#define sk_X509_VERIFY_PARAM_delete_ptr(sk, p)                   \
+  ((X509_VERIFY_PARAM *)sk_delete_ptr(                           \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk), \
+      CHECKED_CAST(void *, X509_VERIFY_PARAM *, p)))
+
+#define sk_X509_VERIFY_PARAM_find(sk, out_index, p)                  \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk), \
+          (out_index), CHECKED_CAST(void *, X509_VERIFY_PARAM *, p))
+
+#define sk_X509_VERIFY_PARAM_shift(sk) \
+  ((X509_VERIFY_PARAM *)sk_shift(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk)))
+
+#define sk_X509_VERIFY_PARAM_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk), \
+          CHECKED_CAST(void *, X509_VERIFY_PARAM *, p))
+
+#define sk_X509_VERIFY_PARAM_pop(sk) \
+  ((X509_VERIFY_PARAM *)sk_pop(      \
+      CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk)))
+
+#define sk_X509_VERIFY_PARAM_dup(sk)      \
+  ((STACK_OF(X509_VERIFY_PARAM) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk)))
+
+#define sk_X509_VERIFY_PARAM_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk))
+
+#define sk_X509_VERIFY_PARAM_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk))
+
+#define sk_X509_VERIFY_PARAM_set_cmp_func(sk, comp)                       \
+  ((int (*)(const X509_VERIFY_PARAM **a, const X509_VERIFY_PARAM **b))    \
+   sk_set_cmp_func(                                                       \
+       CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const X509_VERIFY_PARAM **a,  \
+                                            const X509_VERIFY_PARAM **b), \
+                    comp)))
+
+/* void */
+#define sk_void_new(comp)                \
+  ((STACK_OF(void)*)sk_new(CHECKED_CAST( \
+      stack_cmp_func, int (*)(const void **a, const void **b), comp)))
+
+#define sk_void_new_null() ((STACK_OF(void)*)sk_new_null())
+
+#define sk_void_num(sk) sk_num(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk))
+
+#define sk_void_zero(sk) sk_zero(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk));
+
+#define sk_void_value(sk, i) \
+  ((void *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(void)*, sk), (i)))
+
+#define sk_void_set(sk, i, p)                                       \
+  ((void *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), (i), \
+                  CHECKED_CAST(void *, void *, p)))
+
+#define sk_void_free(sk) sk_free(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk))
+
+#define sk_void_pop_free(sk, free_func)                    \
+  sk_pop_free(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), \
+              CHECKED_CAST(void (*)(void *), void (*)(void *), free_func))
+
+#define sk_void_insert(sk, p, where)                     \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), \
+            CHECKED_CAST(void *, void *, p), (where))
+
+#define sk_void_delete(sk, where) \
+  ((void *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), (where)))
+
+#define sk_void_delete_ptr(sk, p)                                     \
+  ((void *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), \
+                         CHECKED_CAST(void *, void *, p)))
+
+#define sk_void_find(sk, out_index, p)                              \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), (out_index), \
+          CHECKED_CAST(void *, void *, p))
+
+#define sk_void_shift(sk) \
+  ((void *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk)))
+
+#define sk_void_push(sk, p)                            \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), \
+          CHECKED_CAST(void *, void *, p))
+
+#define sk_void_pop(sk) \
+  ((void *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk)))
+
+#define sk_void_dup(sk) \
+  ((STACK_OF(void)*)sk_dup(CHECKED_CAST(_STACK *, const STACK_OF(void)*, sk)))
+
+#define sk_void_sort(sk) sk_sort(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk))
+
+#define sk_void_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(void)*, sk))
+
+#define sk_void_set_cmp_func(sk, comp)                                      \
+  ((int (*)(const void **a, const void **b))sk_set_cmp_func(                \
+      CHECKED_CAST(_STACK *, STACK_OF(void)*, sk),                          \
+      CHECKED_CAST(stack_cmp_func, int (*)(const void **a, const void **b), \
+                   comp)))
+
+/* OPENSSL_STRING */
+#define sk_OPENSSL_STRING_new(comp)                 \
+  ((STACK_OF(OPENSSL_STRING) *)sk_new(CHECKED_CAST( \
+      stack_cmp_func,                               \
+      int (*)(const OPENSSL_STRING *a, const OPENSSL_STRING *b), comp)))
+
+#define sk_OPENSSL_STRING_new_null() ((STACK_OF(OPENSSL_STRING) *)sk_new_null())
+
+#define sk_OPENSSL_STRING_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk))
+
+#define sk_OPENSSL_STRING_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk));
+
+#define sk_OPENSSL_STRING_value(sk, i) \
+  ((OPENSSL_STRING)sk_value(           \
+      CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_STRING) *, sk), (i)))
+
+#define sk_OPENSSL_STRING_set(sk, i, p)                            \
+  ((OPENSSL_STRING)sk_set(                                         \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), (i), \
+      CHECKED_CAST(void *, OPENSSL_STRING, p)))
+
+#define sk_OPENSSL_STRING_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk))
+
+#define sk_OPENSSL_STRING_pop_free(sk, free_func)             \
+  sk_pop_free(                                                \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(OPENSSL_STRING), free_func))
+
+#define sk_OPENSSL_STRING_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), \
+            CHECKED_CAST(void *, OPENSSL_STRING, p), (where))
+
+#define sk_OPENSSL_STRING_delete(sk, where) \
+  ((OPENSSL_STRING)sk_delete(               \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), (where)))
+
+#define sk_OPENSSL_STRING_delete_ptr(sk, p)                   \
+  ((OPENSSL_STRING)sk_delete_ptr(                             \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), \
+      CHECKED_CAST(void *, OPENSSL_STRING, p)))
+
+#define sk_OPENSSL_STRING_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), (out_index), \
+          CHECKED_CAST(void *, OPENSSL_STRING, p))
+
+#define sk_OPENSSL_STRING_shift(sk) \
+  ((OPENSSL_STRING)sk_shift(        \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk)))
+
+#define sk_OPENSSL_STRING_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk), \
+          CHECKED_CAST(void *, OPENSSL_STRING, p))
+
+#define sk_OPENSSL_STRING_pop(sk) \
+  ((OPENSSL_STRING)sk_pop(        \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk)))
+
+#define sk_OPENSSL_STRING_dup(sk)      \
+  ((STACK_OF(OPENSSL_STRING) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_STRING) *, sk)))
+
+#define sk_OPENSSL_STRING_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk))
+
+#define sk_OPENSSL_STRING_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_STRING) *, sk))
+
+#define sk_OPENSSL_STRING_set_cmp_func(sk, comp)                       \
+  ((int (*)(const OPENSSL_STRING **a, const OPENSSL_STRING **b))       \
+   sk_set_cmp_func(                                                    \
+       CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk),         \
+       CHECKED_CAST(stack_cmp_func, int (*)(const OPENSSL_STRING **a,  \
+                                            const OPENSSL_STRING **b), \
+                    comp)))
+
+/* OPENSSL_BLOCK */
+#define sk_OPENSSL_BLOCK_new(comp)                                             \
+  ((STACK_OF(OPENSSL_BLOCK) *)sk_new(CHECKED_CAST(                             \
+      stack_cmp_func, int (*)(const OPENSSL_BLOCK *a, const OPENSSL_BLOCK *b), \
+      comp)))
+
+#define sk_OPENSSL_BLOCK_new_null() ((STACK_OF(OPENSSL_BLOCK) *)sk_new_null())
+
+#define sk_OPENSSL_BLOCK_num(sk) \
+  sk_num(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk))
+
+#define sk_OPENSSL_BLOCK_zero(sk) \
+  sk_zero(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk));
+
+#define sk_OPENSSL_BLOCK_value(sk, i) \
+  ((OPENSSL_BLOCK)sk_value(           \
+      CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk), (i)))
+
+#define sk_OPENSSL_BLOCK_set(sk, i, p)                            \
+  ((OPENSSL_BLOCK)sk_set(                                         \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), (i), \
+      CHECKED_CAST(void *, OPENSSL_BLOCK, p)))
+
+#define sk_OPENSSL_BLOCK_free(sk) \
+  sk_free(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk))
+
+#define sk_OPENSSL_BLOCK_pop_free(sk, free_func)             \
+  sk_pop_free(                                               \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), \
+      CHECKED_CAST(void (*)(void *), void (*)(OPENSSL_BLOCK), free_func))
+
+#define sk_OPENSSL_BLOCK_insert(sk, p, where)                      \
+  sk_insert(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), \
+            CHECKED_CAST(void *, OPENSSL_BLOCK, p), (where))
+
+#define sk_OPENSSL_BLOCK_delete(sk, where) \
+  ((OPENSSL_BLOCK)sk_delete(               \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), (where)))
+
+#define sk_OPENSSL_BLOCK_delete_ptr(sk, p)                   \
+  ((OPENSSL_BLOCK)sk_delete_ptr(                             \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), \
+      CHECKED_CAST(void *, OPENSSL_BLOCK, p)))
+
+#define sk_OPENSSL_BLOCK_find(sk, out_index, p)                               \
+  sk_find(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), (out_index), \
+          CHECKED_CAST(void *, OPENSSL_BLOCK, p))
+
+#define sk_OPENSSL_BLOCK_shift(sk) \
+  ((OPENSSL_BLOCK)sk_shift(        \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk)))
+
+#define sk_OPENSSL_BLOCK_push(sk, p)                             \
+  sk_push(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk), \
+          CHECKED_CAST(void *, OPENSSL_BLOCK, p))
+
+#define sk_OPENSSL_BLOCK_pop(sk) \
+  ((OPENSSL_BLOCK)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk)))
+
+#define sk_OPENSSL_BLOCK_dup(sk)      \
+  ((STACK_OF(OPENSSL_BLOCK) *)sk_dup( \
+      CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk)))
+
+#define sk_OPENSSL_BLOCK_sort(sk) \
+  sk_sort(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk))
+
+#define sk_OPENSSL_BLOCK_is_sorted(sk) \
+  sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk))
+
+#define sk_OPENSSL_BLOCK_set_cmp_func(sk, comp)                                \
+  ((int (*)(const OPENSSL_BLOCK **a, const OPENSSL_BLOCK **b))sk_set_cmp_func( \
+      CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk),                   \
+      CHECKED_CAST(stack_cmp_func,                                             \
+                   int (*)(const OPENSSL_BLOCK **a, const OPENSSL_BLOCK **b),  \
+                   comp)))
diff --git a/crypto/thread.c b/crypto/thread.c
new file mode 100644
index 0000000..1c9926c
--- /dev/null
+++ b/crypto/thread.c
@@ -0,0 +1,195 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/thread.h>
+
+#include <errno.h>
+
+#include <openssl/mem.h>
+#include <openssl/type_check.h>
+
+/* lock_names contains the names of all the locks defined in thread.h. */
+static const char *const lock_names[] = {
+    "<<ERROR>>",    "err",          "ex_data",       "x509",
+    "x509_info",    "x509_pkey",    "x509_crl",      "x509_req",
+    "dsa",          "rsa",          "evp_pkey",      "x509_store",
+    "ssl_ctx",      "ssl_cert",     "ssl_session",   "ssl_sess_cert",
+    "ssl",          "ssl_method",   "rand",          "rand2",
+    "debug_malloc", "BIO",          "gethostbyname", "getservbyname",
+    "readdir",      "RSA_blinding", "dh",            "debug_malloc2",
+    "dso",          "dynlock",      "engine",        "ui",
+    "ecdsa",        "ec",           "ecdh",          "bn",
+    "ec_pre_comp",  "store",        "comp",          "fips",
+    "fips2",        "obj",
+};
+
+OPENSSL_COMPILE_ASSERT(CRYPTO_NUM_LOCKS ==
+                           sizeof(lock_names) / sizeof(lock_names[0]),
+                       CRYPTO_NUM_LOCKS_inconsistent);
+
+static void (*locking_callback)(int mode, int lock_num, const char *file,
+                                int line) = 0;
+static int (*add_lock_callback)(int *pointer, int amount, int lock_num,
+                                const char *file, int line) = 0;
+static void (*threadid_callback)(CRYPTO_THREADID *) = 0;
+
+
+int CRYPTO_num_locks(void) { return CRYPTO_NUM_LOCKS; }
+
+void CRYPTO_set_locking_callback(void (*func)(int mode, int lock_num,
+                                              const char *file, int line)) {
+  locking_callback = func;
+}
+
+void CRYPTO_set_add_lock_callback(int (*func)(int *num, int mount, int lock_num,
+                                              const char *file, int line)) {
+  add_lock_callback = func;
+}
+
+const char *CRYPTO_get_lock_name(int lock_num) {
+  if (lock_num >= 0 && lock_num < CRYPTO_NUM_LOCKS) {
+    return lock_names[lock_num];
+  } else {
+    return "ERROR";
+  }
+}
+
+int CRYPTO_THREADID_set_callback(void (*func)(CRYPTO_THREADID *)) {
+  if (threadid_callback) {
+    return 0;
+  }
+  threadid_callback = func;
+  return 1;
+}
+
+void CRYPTO_THREADID_set_numeric(CRYPTO_THREADID *id, unsigned long val) {
+  memset(id, 0, sizeof(*id));
+  id->val = val;
+}
+
+void CRYPTO_THREADID_set_pointer(CRYPTO_THREADID *id, void *ptr) {
+  memset(id, 0, sizeof(*id));
+  id->ptr = ptr;
+}
+
+void (*CRYPTO_get_locking_callback(void))(int mode, int lock_num,
+                                          const char *file, int line) {
+  return locking_callback;
+}
+
+int (*CRYPTO_get_add_lock_callback(void))(int *num, int mount, int lock_num,
+                                          const char *file, int line) {
+  return add_lock_callback;
+}
+
+void CRYPTO_lock(int mode, int lock_num, const char *file, int line) {
+  if (locking_callback != NULL) {
+    locking_callback(mode, lock_num, file, line);
+  }
+}
+
+int CRYPTO_add_lock(int *pointer, int amount, int lock_num, const char *file,
+                    int line) {
+  int ret = 0;
+
+  if (add_lock_callback != NULL) {
+    ret = add_lock_callback(pointer, amount, lock_num, file, line);
+  } else {
+    CRYPTO_lock(CRYPTO_LOCK | CRYPTO_WRITE, lock_num, file, line);
+    ret = *pointer + amount;
+    *pointer = ret;
+    CRYPTO_lock(CRYPTO_UNLOCK | CRYPTO_WRITE, lock_num, file, line);
+  }
+
+  return ret;
+}
+
+void CRYPTO_THREADID_current(CRYPTO_THREADID *id) {
+  if (threadid_callback) {
+    threadid_callback(id);
+    return;
+  }
+
+#if defined(OPENSSL_SYS_WIN32)
+  CRYPTO_THREADID_set_numeric(id, (unsigned long)GetCurrentThreadId());
+#else
+  /* For everything else, default to using the address of 'errno' */
+  CRYPTO_THREADID_set_pointer(id, (void *)&errno);
+#endif
+}
+
+int CRYPTO_THREADID_cmp(const CRYPTO_THREADID *a, const CRYPTO_THREADID *b) {
+  return memcmp(a, b, sizeof(*a));
+}
+
+void CRYPTO_THREADID_cpy(CRYPTO_THREADID *dest, const CRYPTO_THREADID *src) {
+  memcpy(dest, src, sizeof(*src));
+}
+
+uint32_t CRYPTO_THREADID_hash(const CRYPTO_THREADID *id) {
+  return OPENSSL_hash32(id, sizeof(CRYPTO_THREADID));
+}
+
+void CRYPTO_set_id_callback(unsigned long (*func)(void)) {}
+
+void CRYPTO_set_dynlock_create_callback(struct CRYPTO_dynlock_value *(
+    *dyn_create_function)(const char *file, int line)) {}
+
+void CRYPTO_set_dynlock_lock_callback(void (*dyn_lock_function)(
+    int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)) {}
+
+void CRYPTO_set_dynlock_destroy_callback(void (*dyn_destroy_function)(
+    struct CRYPTO_dynlock_value *l, const char *file, int line)) {}
diff --git a/crypto/thread.h b/crypto/thread.h
new file mode 100644
index 0000000..ef06f1e
--- /dev/null
+++ b/crypto/thread.h
@@ -0,0 +1,270 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_THREAD_H
+#define OPENSSL_HEADER_THREAD_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Functions to support multithreading.
+ *
+ * OpenSSL can safely be used in multi-threaded applications provided that at
+ * least two callback functions are set with |CRYPTO_set_locking_callback| and
+ * |CRYPTO_THREADID_set_callback|.
+ *
+ * The locking callback performs mutual exclusion. Rather than using a single
+ * lock for all, shared data-structures, OpenSSL requires that the locking
+ * callback support a fixed (at run-time) number of different locks, given by
+ * |CRYPTO_num_locks|.
+ *
+ * The thread ID callback is called to record the currently executing thread's
+ * identifier in a |CRYPTO_THREADID| structure. If this callback is not
+ * provided then the address of |errno| is used as the thread identifier. This
+ * is sufficient only if the system has a thread-local |errno| value. */
+
+
+/* CRYPTO_num_locks returns the number of static locks that the callback
+ * function passed to |CRYPTO_set_locking_callback| must be able to handle. */
+int CRYPTO_num_locks(void);
+
+/* CRYPTO_set_locking_callback sets a callback function that implements locking
+ * on behalf of OpenSSL. The callback is called whenever OpenSSL needs to lock
+ * or unlock a lock, and locks are specified as a number between zero and
+ * |CRYPTO_num_locks()-1|.
+ *
+ * The mode argument to the callback is a bitwise-OR of either CRYPTO_LOCK or
+ * CRYPTO_UNLOCK, to denote the action, and CRYPTO_READ or CRYPTO_WRITE, to
+ * indicate the type of lock. The |file| and |line| arguments give the location
+ * in the OpenSSL source where the locking action originated. */
+void CRYPTO_set_locking_callback(void (*func)(int mode, int lock_num,
+                                              const char *file, int line));
+
+/* CRYPTO_set_add_lock_callback sets an optional callback which is used when
+ * OpenSSL needs to add a fixed amount to an integer. For example, this is used
+ * when maintaining reference counts. Normally the reference counts are
+ * maintained by performing the addition under a lock but, if this callback
+ * has been set, the application is free to implement the operation using
+ * faster methods (i.e. atomic operations).
+ *
+ * The callback is given a pointer to the integer to be altered (|num|), the
+ * amount to add to the integer (|amount|, which may be negative), the number
+ * of the lock which would have been taken to protect the operation and the
+ * position in the OpenSSL code where the operation originated. */
+void CRYPTO_set_add_lock_callback(int (*func)(int *num, int amount,
+                                              int lock_num, const char *file,
+                                              int line));
+
+/* CRYPTO_get_lock_name returns the name of the lock given by |lock_num|. This
+ * can be used in a locking callback for debugging purposes. */
+const char *CRYPTO_get_lock_name(int lock_num);
+
+
+/* CRYPTO_THREADID identifies a thread in a multithreaded program. This
+ * structure should not be used directly. Rather applications should use
+ * |CRYPTO_THREADID_set_numeric| and |CRYPTO_THREADID_set_pointer|. */
+typedef struct crypto_threadid_st {
+  void *ptr;
+  unsigned long val;
+} CRYPTO_THREADID;
+
+/* CRYPTO_THREADID_set_callback sets a callback function that stores an
+ * identifier of the currently executing thread into |threadid|. The
+ * CRYPTO_THREADID structure should not be accessed directly. Rather one of
+ * |CRYPTO_THREADID_set_numeric| or |CRYPTO_THREADID_set_pointer| should be
+ * used depending on whether thread IDs are numbers or pointers on the host
+ * system. */
+int CRYPTO_THREADID_set_callback(
+    void (*threadid_func)(CRYPTO_THREADID *threadid));
+
+void CRYPTO_THREADID_set_numeric(CRYPTO_THREADID *id, unsigned long val);
+void CRYPTO_THREADID_set_pointer(CRYPTO_THREADID *id, void *ptr);
+
+
+/* Private functions: */
+
+/* CRYPTO_get_locking_callback returns the callback, if any, that was most
+ * recently set using |CRYPTO_set_locking_callback|. */
+void (*CRYPTO_get_locking_callback(void))(int mode, int lock_num,
+                                          const char *file, int line);
+
+/* CRYPTO_get_add_lock_callback returns the callback, if any, that was most
+ * recently set using |CRYPTO_set_add_lock_callback|. */
+int (*CRYPTO_get_add_lock_callback(void))(int *num, int amount, int lock_num,
+                                          const char *file, int line);
+
+/* CRYPTO_lock locks or unlocks the lock specified by |lock_num| (one of
+ * |CRYPTO_LOCK_*|). Don't call this directly, rather use one of the
+ * CRYPTO_[rw]_(un)lock macros. */
+void CRYPTO_lock(int mode, int lock_num, const char *file, int line);
+
+/* CRYPTO_add_lock adds |amount| to |*pointer|, protected by the lock specified
+ * by |lock_num|. It returns the new value of |*pointer|. Don't call this
+ * function directly, rather use the |CRYPTO_add_lock| macro.
+ *
+ * TODO(fork): rename to CRYPTO_add_locked. */
+int CRYPTO_add_lock(int *pointer, int amount, int lock_num, const char *file,
+                    int line);
+
+
+/* CRYPTO_THREADID_current stores the current thread identifier in |id|. */
+void CRYPTO_THREADID_current(CRYPTO_THREADID *id);
+
+/* CRYPTO_THREADID_cmp returns < 0, 0 or > 0 if |a| is less than, equal to or
+ * greater than |b|, respectively. */
+int CRYPTO_THREADID_cmp(const CRYPTO_THREADID *a, const CRYPTO_THREADID *b);
+
+/* CRYPTO_THREADID_cpy sets |*dest| equal to |*src|. */
+void CRYPTO_THREADID_cpy(CRYPTO_THREADID *dest, const CRYPTO_THREADID *src);
+
+/* CRYPTO_THREADID_hash returns a hash of the numeric value of |id|. */
+uint32_t CRYPTO_THREADID_hash(const CRYPTO_THREADID *id);
+
+/* These are the locks used by OpenSSL. These values should match up with the
+ * table in thread.c. */
+#define CRYPTO_LOCK_ERR 1
+#define CRYPTO_LOCK_EX_DATA 2
+#define CRYPTO_LOCK_X509 3
+#define CRYPTO_LOCK_X509_INFO 4
+#define CRYPTO_LOCK_X509_PKEY 5
+#define CRYPTO_LOCK_X509_CRL 6
+#define CRYPTO_LOCK_X509_REQ 7
+#define CRYPTO_LOCK_DSA 8
+#define CRYPTO_LOCK_RSA 9
+#define CRYPTO_LOCK_EVP_PKEY 10
+#define CRYPTO_LOCK_X509_STORE 11
+#define CRYPTO_LOCK_SSL_CTX 12
+#define CRYPTO_LOCK_SSL_CERT 13
+#define CRYPTO_LOCK_SSL_SESSION 14
+#define CRYPTO_LOCK_SSL_SESS_CERT 15
+#define CRYPTO_LOCK_SSL 16
+#define CRYPTO_LOCK_SSL_METHOD 17
+#define CRYPTO_LOCK_RAND 18
+#define CRYPTO_LOCK_RAND2 19
+#define CRYPTO_LOCK_MALLOC 20
+#define CRYPTO_LOCK_BIO 21
+#define CRYPTO_LOCK_GETHOSTBYNAME 22
+#define CRYPTO_LOCK_GETSERVBYNAME 23
+#define CRYPTO_LOCK_READDIR 24
+#define CRYPTO_LOCK_RSA_BLINDING 25
+#define CRYPTO_LOCK_DH 26
+#define CRYPTO_LOCK_MALLOC2 27
+#define CRYPTO_LOCK_DSO 28
+#define CRYPTO_LOCK_DYNLOCK 29
+#define CRYPTO_LOCK_ENGINE 30
+#define CRYPTO_LOCK_UI 31
+#define CRYPTO_LOCK_ECDSA 32
+#define CRYPTO_LOCK_EC 33
+#define CRYPTO_LOCK_ECDH 34
+#define CRYPTO_LOCK_BN 35
+#define CRYPTO_LOCK_EC_PRE_COMP 36
+#define CRYPTO_LOCK_STORE 37
+#define CRYPTO_LOCK_COMP 38
+#define CRYPTO_LOCK_FIPS 39
+#define CRYPTO_LOCK_FIPS2 40
+#define CRYPTO_LOCK_OBJ 40
+#define CRYPTO_NUM_LOCKS 42
+
+#define CRYPTO_LOCK 1
+#define CRYPTO_UNLOCK 2
+#define CRYPTO_READ 4
+#define CRYPTO_WRITE 8
+
+#define CRYPTO_w_lock(lock_num) \
+  CRYPTO_lock(CRYPTO_LOCK | CRYPTO_WRITE, lock_num, __FILE__, __LINE__)
+#define CRYPTO_w_unlock(lock_num) \
+  CRYPTO_lock(CRYPTO_UNLOCK | CRYPTO_WRITE, lock_num, __FILE__, __LINE__)
+#define CRYPTO_r_lock(lock_num) \
+  CRYPTO_lock(CRYPTO_LOCK | CRYPTO_READ, lock_num, __FILE__, __LINE__)
+#define CRYPTO_r_unlock(lock_num) \
+  CRYPTO_lock(CRYPTO_UNLOCK | CRYPTO_READ, lock_num, __FILE__, __LINE__)
+#define CRYPTO_add(addr, amount, lock_num) \
+  CRYPTO_add_lock(addr, amount, lock_num, __FILE__, __LINE__)
+
+
+/* Private functions.
+ *
+ * Some old code calls these functions and so no-op implementations are
+ * provided.
+ *
+ * TODO(fork): cleanup callers and remove. */
+
+void CRYPTO_set_id_callback(unsigned long (*func)(void));
+
+typedef struct {
+  int references;
+  struct CRYPTO_dynlock_value *data;
+} CRYPTO_dynlock;
+
+void CRYPTO_set_dynlock_create_callback(struct CRYPTO_dynlock_value *(
+    *dyn_create_function)(const char *file, int line));
+
+void CRYPTO_set_dynlock_lock_callback(void (*dyn_lock_function)(
+    int mode, struct CRYPTO_dynlock_value *l, const char *file, int line));
+
+void CRYPTO_set_dynlock_destroy_callback(void (*dyn_destroy_function)(
+    struct CRYPTO_dynlock_value *l, const char *file, int line));
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_THREAD_H */
diff --git a/crypto/time_support.c b/crypto/time_support.c
new file mode 100644
index 0000000..5c6e9c0
--- /dev/null
+++ b/crypto/time_support.c
@@ -0,0 +1,198 @@
+/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL
+ * project 2001.
+ * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2008.
+ */
+/* ====================================================================
+ * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#define _BSD_SOURCE /* for gmtime_r */
+
+#include <openssl/time_support.h>
+
+#define SECS_PER_DAY (24 * 60 * 60)
+
+struct tm *OPENSSL_gmtime(const time_t *time, struct tm *result) {
+#if defined(OPENSSL_WINDOWS)
+  if (_gmtime_s(result, time)) {
+    return NULL;
+  }
+  return result;
+#else
+  return gmtime_r(time, result);
+#endif
+}
+
+/* Convert date to and from julian day Uses Fliegel & Van Flandern algorithm */
+static long date_to_julian(int y, int m, int d) {
+  return (1461 * (y + 4800 + (m - 14) / 12)) / 4 +
+         (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 -
+         (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075;
+}
+
+static void julian_to_date(long jd, int *y, int *m, int *d) {
+  long L = jd + 68569;
+  long n = (4 * L) / 146097;
+  long i, j;
+
+  L = L - (146097 * n + 3) / 4;
+  i = (4000 * (L + 1)) / 1461001;
+  L = L - (1461 * i) / 4 + 31;
+  j = (80 * L) / 2447;
+  *d = L - (2447 * j) / 80;
+  L = j / 11;
+  *m = j + 2 - (12 * L);
+  *y = 100 * (n - 49) + i + L;
+}
+
+/* Convert tm structure and offset into julian day and seconds */
+static int julian_adj(const struct tm *tm, int off_day, long offset_sec,
+                      long *pday, int *psec) {
+  int offset_hms, offset_day;
+  long time_jd;
+  int time_year, time_month, time_day;
+  /* split offset into days and day seconds */
+  offset_day = offset_sec / SECS_PER_DAY;
+  /* Avoid sign issues with % operator */
+  offset_hms = offset_sec - (offset_day * SECS_PER_DAY);
+  offset_day += off_day;
+  /* Add current time seconds to offset */
+  offset_hms += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+  /* Adjust day seconds if overflow */
+  if (offset_hms >= SECS_PER_DAY) {
+    offset_day++;
+    offset_hms -= SECS_PER_DAY;
+  } else if (offset_hms < 0) {
+    offset_day--;
+    offset_hms += SECS_PER_DAY;
+  }
+
+  /* Convert date of time structure into a Julian day number. */
+
+  time_year = tm->tm_year + 1900;
+  time_month = tm->tm_mon + 1;
+  time_day = tm->tm_mday;
+
+  time_jd = date_to_julian(time_year, time_month, time_day);
+
+  /* Work out Julian day of new date */
+  time_jd += offset_day;
+
+  if (time_jd < 0)
+    return 0;
+
+  *pday = time_jd;
+  *psec = offset_hms;
+  return 1;
+}
+
+int OPENSSL_gmtime_adj(struct tm *tm, int off_day, long offset_sec) {
+  int time_sec, time_year, time_month, time_day;
+  long time_jd;
+
+  /* Convert time and offset into julian day and seconds */
+  if (!julian_adj(tm, off_day, offset_sec, &time_jd, &time_sec))
+    return 0;
+
+  /* Convert Julian day back to date */
+
+  julian_to_date(time_jd, &time_year, &time_month, &time_day);
+
+  if (time_year < 1900 || time_year > 9999)
+    return 0;
+
+  /* Update tm structure */
+
+  tm->tm_year = time_year - 1900;
+  tm->tm_mon = time_month - 1;
+  tm->tm_mday = time_day;
+
+  tm->tm_hour = time_sec / 3600;
+  tm->tm_min = (time_sec / 60) % 60;
+  tm->tm_sec = time_sec % 60;
+
+  return 1;
+}
+
+int OPENSSL_gmtime_diff(int *pday, int *psec, const struct tm *from,
+                        const struct tm *to) {
+  int from_sec, to_sec, diff_sec;
+  long from_jd, to_jd, diff_day;
+
+  if (!julian_adj(from, 0, 0, &from_jd, &from_sec)) {
+    return 0;
+  }
+  if (!julian_adj(to, 0, 0, &to_jd, &to_sec)) {
+    return 0;
+  }
+
+  diff_day = to_jd - from_jd;
+  diff_sec = to_sec - from_sec;
+  /* Adjust differences so both positive or both negative */
+  if (diff_day > 0 && diff_sec < 0) {
+    diff_day--;
+    diff_sec += SECS_PER_DAY;
+  }
+  if (diff_day < 0 && diff_sec > 0) {
+    diff_day++;
+    diff_sec -= SECS_PER_DAY;
+  }
+
+  if (pday) {
+    *pday = (int)diff_day;
+  }
+  if (psec) {
+    *psec = diff_sec;
+  }
+
+  return 1;
+}
diff --git a/crypto/time_support.h b/crypto/time_support.h
new file mode 100644
index 0000000..051c691
--- /dev/null
+++ b/crypto/time_support.h
@@ -0,0 +1,88 @@
+/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL
+ * project 2001.
+ * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2008.
+ */
+/* ====================================================================
+ * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef OPENSSL_HEADER_TIME_H
+#define OPENSSL_HEADER_TIME_H
+
+#include <openssl/base.h>
+
+#include <time.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* OPENSSL_gmtime wraps |gmtime_r|. See the manual page for that function. */
+struct tm *OPENSSL_gmtime(const time_t *timer, struct tm *result);
+
+/* OPENSSL_gmtime_adj updates |tm| by adding |offset_day| days and |offset_sec|
+ * seconds. */
+int OPENSSL_gmtime_adj(struct tm *tm, int offset_day, long offset_sec);
+
+/* OPENSSL_gmtime_diff calculates the difference between |from| and |to| and
+ * outputs the difference as a number of days and seconds in |*out_days| and
+ * |*out_secs|. */
+int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from,
+                        const struct tm *to);
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_TIME_H */
diff --git a/crypto/type_check.h b/crypto/type_check.h
new file mode 100644
index 0000000..dd59151
--- /dev/null
+++ b/crypto/type_check.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_TYPE_CHECK_H
+#define OPENSSL_HEADER_TYPE_CHECK_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* This header file contains some common macros for enforcing type checking.
+ * Several, common OpenSSL structures (i.e. stack and lhash) operate on void
+ * pointers, but we wish to have type checking when they are used with a
+ * specific type. */
+
+/* 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))
+
+#define OPENSSL_COMPILE_ASSERT(cond, msg) \
+  typedef char OPENSSL_COMPILE_ASSERT_##msg[((cond) ? 1 : -1)]
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_TYPE_CHECK_H */
diff --git a/crypto/x509/CMakeLists.txt b/crypto/x509/CMakeLists.txt
new file mode 100644
index 0000000..9017d76
--- /dev/null
+++ b/crypto/x509/CMakeLists.txt
@@ -0,0 +1,57 @@
+include_directories(. .. ../../include)
+
+add_library(
+	x509
+
+	OBJECT
+
+	a_digest.c
+	a_sign.c
+	a_strex.c
+	a_verify.c
+	asn1_gen.c
+	by_dir.c
+	by_file.c
+	i2d_pr.c
+	pkcs7.c
+	t_crl.c
+	t_x509.c
+	t_x509a.c
+	x509.c
+	x509_att.c
+	x509_cmp.c
+	x509_d2.c
+	x509_def.c
+	x509_error.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
+	x509type.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/crypto/x509/a_digest.c b/crypto/x509/a_digest.c
new file mode 100644
index 0000000..9411dbc
--- /dev/null
+++ b/crypto/x509/a_digest.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <openssl/asn1.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+int ASN1_digest(i2d_of_void *i2d, const EVP_MD *type, char *data,
+		unsigned char *md, unsigned int *len)
+	{
+	int i;
+	unsigned char *str,*p;
+
+	i=i2d(data,NULL);
+	if ((str=(unsigned char *)OPENSSL_malloc(i)) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, ASN1_digest, ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+	p=str;
+	i2d(data,&p);
+
+	if (!EVP_Digest(str, i, md, len, type, NULL))
+		return 0;
+	OPENSSL_free(str);
+	return(1);
+	}
+
+int ASN1_item_digest(const ASN1_ITEM *it, const EVP_MD *type, void *asn,
+		unsigned char *md, unsigned int *len)
+	{
+	int i;
+	unsigned char *str = NULL;
+
+	i=ASN1_item_i2d(asn,&str, it);
+	if (!str) return(0);
+
+	if (!EVP_Digest(str, i, md, len, type, NULL))
+		return 0;
+	OPENSSL_free(str);
+	return(1);
+	}
diff --git a/crypto/x509/a_sign.c b/crypto/x509/a_sign.c
new file mode 100644
index 0000000..d35efe1
--- /dev/null
+++ b/crypto/x509/a_sign.c
@@ -0,0 +1,272 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "../evp/internal.h"
+
+
+/* TODO(fork): this code appears to be dead. */
+#if 0
+#ifndef NO_ASN1_OLD
+
+int ASN1_sign(i2d_of_void *i2d, X509_ALGOR *algor1, X509_ALGOR *algor2,
+	      ASN1_BIT_STRING *signature, char *data, EVP_PKEY *pkey,
+	      const EVP_MD *type)
+	{
+	EVP_MD_CTX ctx;
+	unsigned char *p,*buf_in=NULL,*buf_out=NULL;
+	int i,inl=0,outl=0,outll=0;
+	X509_ALGOR *a;
+
+	EVP_MD_CTX_init(&ctx);
+	for (i=0; i<2; i++)
+		{
+		if (i == 0)
+			a=algor1;
+		else
+			a=algor2;
+		if (a == NULL) continue;
+                if (type->pkey_type == NID_dsaWithSHA1)
+			{
+			/* special case: RFC 2459 tells us to omit 'parameters'
+			 * with id-dsa-with-sha1 */
+			ASN1_TYPE_free(a->parameter);
+			a->parameter = NULL;
+			}
+		else if ((a->parameter == NULL) || 
+			(a->parameter->type != V_ASN1_NULL))
+			{
+			ASN1_TYPE_free(a->parameter);
+			if ((a->parameter=ASN1_TYPE_new()) == NULL) goto err;
+			a->parameter->type=V_ASN1_NULL;
+			}
+		ASN1_OBJECT_free(a->algorithm);
+		a->algorithm=OBJ_nid2obj(type->pkey_type);
+		if (a->algorithm == NULL)
+			{
+			OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_UNKNOWN_OBJECT_TYPE);
+			goto err;
+			}
+		if (a->algorithm->length == 0)
+			{
+			OPENSSL_PUT_ERROR(ASN1, XXX, ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD);
+			goto err;
+			}
+		}
+	inl=i2d(data,NULL);
+	buf_in=(unsigned char *)OPENSSL_malloc((unsigned int)inl);
+	outll=outl=EVP_PKEY_size(pkey);
+	buf_out=(unsigned char *)OPENSSL_malloc((unsigned int)outl);
+	if ((buf_in == NULL) || (buf_out == NULL))
+		{
+		outl=0;
+		OPENSSL_PUT_ERROR(ASN1, XXX, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	p=buf_in;
+
+	i2d(data,&p);
+	if (!EVP_SignInit_ex(&ctx,type, NULL)
+		|| !EVP_SignUpdate(&ctx,(unsigned char *)buf_in,inl)
+		|| !EVP_SignFinal(&ctx,(unsigned char *)buf_out,
+			(unsigned int *)&outl,pkey))
+		{
+		outl=0;
+		OPENSSL_PUT_ERROR(ASN1, XXX, ERR_R_EVP_LIB);
+		goto err;
+		}
+	if (signature->data != NULL) OPENSSL_free(signature->data);
+	signature->data=buf_out;
+	buf_out=NULL;
+	signature->length=outl;
+	/* In the interests of compatibility, I'll make sure that
+	 * the bit string has a 'not-used bits' value of 0
+	 */
+	signature->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
+	signature->flags|=ASN1_STRING_FLAG_BITS_LEFT;
+err:
+	EVP_MD_CTX_cleanup(&ctx);
+	if (buf_in != NULL)
+		{ OPENSSL_cleanse((char *)buf_in,(unsigned int)inl); OPENSSL_free(buf_in); }
+	if (buf_out != NULL)
+		{ OPENSSL_cleanse((char *)buf_out,outll); OPENSSL_free(buf_out); }
+	return(outl);
+	}
+
+#endif
+#endif
+
+int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1, X509_ALGOR *algor2,
+	     ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey,
+	     const EVP_MD *type)
+	{
+	EVP_MD_CTX ctx;
+	EVP_MD_CTX_init(&ctx);
+	if (!EVP_DigestSignInit(&ctx, NULL, type, NULL, pkey))
+		{
+		EVP_MD_CTX_cleanup(&ctx);
+		return 0;
+		}
+	return ASN1_item_sign_ctx(it, algor1, algor2, signature, asn, &ctx);
+	}
+		
+
+int ASN1_item_sign_ctx(const ASN1_ITEM *it,
+		X509_ALGOR *algor1, X509_ALGOR *algor2,
+	     	ASN1_BIT_STRING *signature, void *asn, EVP_MD_CTX *ctx)
+	{
+	const EVP_MD *type;
+	EVP_PKEY *pkey;
+	unsigned char *buf_in=NULL,*buf_out=NULL;
+	size_t inl=0,outl=0,outll=0;
+	int signid, paramtype;
+	int rv;
+
+	type = EVP_MD_CTX_md(ctx);
+	pkey = EVP_PKEY_CTX_get0_pkey(ctx->pctx);
+
+	if (!type || !pkey)
+		{
+		OPENSSL_PUT_ERROR(X509, ASN1_item_sign_ctx, X509_R_CONTEXT_NOT_INITIALISED);
+		return 0;
+		}
+
+	if (pkey->ameth->item_sign)
+		{
+		rv = pkey->ameth->item_sign(ctx, it, asn, algor1, algor2,
+						signature);
+		if (rv == 1)
+			outl = signature->length;
+		/* Return value meanings:
+		 * <=0: error.
+		 *   1: method does everything.
+		 *   2: carry on as normal.
+		 *   3: ASN1 method sets algorithm identifiers: just sign.
+		 */
+		if (rv <= 0)
+			OPENSSL_PUT_ERROR(X509, ASN1_item_sign_ctx, ERR_R_EVP_LIB);
+		if (rv <= 1)
+			goto err;
+		}
+	else
+		rv = 2;
+
+	if (rv == 2)
+		{
+		/* TODO(fork): EVP_MD_FLAG_PKEY_METHOD_SIGNATURE seems to mean
+		 * "is SHA". */
+		if (!pkey->ameth ||
+			!OBJ_find_sigid_by_algs(&signid,
+						EVP_MD_type(type),
+						pkey->ameth->pkey_id))
+			{
+			OPENSSL_PUT_ERROR(X509, ASN1_item_sign_ctx, X509_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED);
+			return 0;
+			}
+
+		if (pkey->ameth->pkey_flags & ASN1_PKEY_SIGPARAM_NULL)
+			paramtype = V_ASN1_NULL;
+		else
+			paramtype = V_ASN1_UNDEF;
+
+		if (algor1)
+			X509_ALGOR_set0(algor1, OBJ_nid2obj(signid), paramtype, NULL);
+		if (algor2)
+			X509_ALGOR_set0(algor2, OBJ_nid2obj(signid), paramtype, NULL);
+
+		}
+
+	inl=ASN1_item_i2d(asn,&buf_in, it);
+	outll=outl=EVP_PKEY_size(pkey);
+	buf_out=OPENSSL_malloc((unsigned int)outl);
+	if ((buf_in == NULL) || (buf_out == NULL))
+		{
+		outl=0;
+		OPENSSL_PUT_ERROR(X509, ASN1_item_sign_ctx, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	if (!EVP_DigestSignUpdate(ctx, buf_in, inl)
+		|| !EVP_DigestSignFinal(ctx, buf_out, &outl))
+		{
+		outl=0;
+		OPENSSL_PUT_ERROR(X509, ASN1_item_sign_ctx, ERR_R_EVP_LIB);
+		goto err;
+		}
+	if (signature->data != NULL) OPENSSL_free(signature->data);
+	signature->data=buf_out;
+	buf_out=NULL;
+	signature->length=outl;
+	/* In the interests of compatibility, I'll make sure that
+	 * the bit string has a 'not-used bits' value of 0
+	 */
+	signature->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
+	signature->flags|=ASN1_STRING_FLAG_BITS_LEFT;
+err:
+	EVP_MD_CTX_cleanup(ctx);
+	if (buf_in != NULL)
+		{ OPENSSL_cleanse((char *)buf_in,(unsigned int)inl); OPENSSL_free(buf_in); }
+	if (buf_out != NULL)
+		{ OPENSSL_cleanse((char *)buf_out,outll); OPENSSL_free(buf_out); }
+	return(outl);
+	}
diff --git a/crypto/x509/a_strex.c b/crypto/x509/a_strex.c
new file mode 100644
index 0000000..92b61ac
--- /dev/null
+++ b/crypto/x509/a_strex.c
@@ -0,0 +1,561 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <openssl/asn1.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "charmap.h"
+
+
+/* ASN1_STRING_print_ex() and X509_NAME_print_ex().
+ * Enhanced string and name printing routines handling
+ * multibyte characters, RFC2253 and a host of other
+ * options.
+ */
+
+
+#define CHARTYPE_BS_ESC		(ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
+
+#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
+		  ASN1_STRFLGS_ESC_QUOTE | \
+		  ASN1_STRFLGS_ESC_CTRL | \
+		  ASN1_STRFLGS_ESC_MSB)
+
+
+static int send_bio_chars(void *arg, const void *buf, int len)
+{
+	if(!arg) return 1;
+	if(BIO_write(arg, buf, len) != len) return 0;
+	return 1;
+}
+
+static int send_fp_chars(void *arg, const void *buf, int len)
+{
+	if(!arg) return 1;
+	if(fwrite(buf, 1, len, arg) != (unsigned int)len) return 0;
+	return 1;
+}
+
+typedef int char_io(void *arg, const void *buf, int len);
+
+/* This function handles display of
+ * strings, one character at a time.
+ * It is passed an unsigned long for each
+ * character because it could come from 2 or even
+ * 4 byte forms.
+ */
+
+#define HEX_SIZE(type) (sizeof(type)*2)
+
+static int do_esc_char(unsigned long c, unsigned char flags, char *do_quotes, char_io *io_ch, void *arg)
+{
+	unsigned char chflgs, chtmp;
+	char tmphex[HEX_SIZE(long)+3];
+
+	if(c > 0xffffffffL)
+		return -1;
+	if(c > 0xffff) {
+		BIO_snprintf(tmphex, sizeof tmphex, "\\W%08lX", c);
+		if(!io_ch(arg, tmphex, 10)) return -1;
+		return 10;
+	}
+	if(c > 0xff) {
+		BIO_snprintf(tmphex, sizeof tmphex, "\\U%04lX", c);
+		if(!io_ch(arg, tmphex, 6)) return -1;
+		return 6;
+	}
+	chtmp = (unsigned char)c;
+	if(chtmp > 0x7f) chflgs = flags & ASN1_STRFLGS_ESC_MSB;
+	else chflgs = char_type[chtmp] & flags;
+	if(chflgs & CHARTYPE_BS_ESC) {
+		/* If we don't escape with quotes, signal we need quotes */
+		if(chflgs & ASN1_STRFLGS_ESC_QUOTE) {
+			if(do_quotes) *do_quotes = 1;
+			if(!io_ch(arg, &chtmp, 1)) return -1;
+			return 1;
+		}
+		if(!io_ch(arg, "\\", 1)) return -1;
+		if(!io_ch(arg, &chtmp, 1)) return -1;
+		return 2;
+	}
+	if(chflgs & (ASN1_STRFLGS_ESC_CTRL|ASN1_STRFLGS_ESC_MSB)) {
+		BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
+		if(!io_ch(arg, tmphex, 3)) return -1;
+		return 3;
+	}
+	/* If we get this far and do any escaping at all must escape 
+	 * the escape character itself: backslash.
+	 */
+	if (chtmp == '\\' && flags & ESC_FLAGS) {
+		if(!io_ch(arg, "\\\\", 2)) return -1;
+		return 2;
+	}
+	if(!io_ch(arg, &chtmp, 1)) return -1;
+	return 1;
+}
+
+#define BUF_TYPE_WIDTH_MASK	0x7
+#define BUF_TYPE_CONVUTF8	0x8
+
+/* This function sends each character in a buffer to
+ * do_esc_char(). It interprets the content formats
+ * and converts to or from UTF8 as appropriate.
+ */
+
+static int do_buf(unsigned char *buf, int buflen,
+			int type, unsigned char flags, char *quotes, char_io *io_ch, void *arg)
+{
+	int i, outlen, len;
+	unsigned char orflags, *p, *q;
+	unsigned long c;
+	p = buf;
+	q = buf + buflen;
+	outlen = 0;
+	while(p != q) {
+		if(p == buf && flags & ASN1_STRFLGS_ESC_2253) orflags = CHARTYPE_FIRST_ESC_2253;
+		else orflags = 0;
+		switch(type & BUF_TYPE_WIDTH_MASK) {
+			case 4:
+			c = ((unsigned long)*p++) << 24;
+			c |= ((unsigned long)*p++) << 16;
+			c |= ((unsigned long)*p++) << 8;
+			c |= *p++;
+			break;
+
+			case 2:
+			c = ((unsigned long)*p++) << 8;
+			c |= *p++;
+			break;
+
+			case 1:
+			c = *p++;
+			break;
+			
+			case 0:
+			i = UTF8_getc(p, buflen, &c);
+			if(i < 0) return -1;	/* Invalid UTF8String */
+			p += i;
+			break;
+			default:
+			return -1;	/* invalid width */
+		}
+		if (p == q && flags & ASN1_STRFLGS_ESC_2253) orflags = CHARTYPE_LAST_ESC_2253;
+		if(type & BUF_TYPE_CONVUTF8) {
+			unsigned char utfbuf[6];
+			int utflen;
+			utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
+			for(i = 0; i < utflen; i++) {
+				/* We don't need to worry about setting orflags correctly
+				 * because if utflen==1 its value will be correct anyway 
+				 * otherwise each character will be > 0x7f and so the 
+				 * character will never be escaped on first and last.
+				 */
+				len = do_esc_char(utfbuf[i], (unsigned char)(flags | orflags), quotes, io_ch, arg);
+				if(len < 0) return -1;
+				outlen += len;
+			}
+		} else {
+			len = do_esc_char(c, (unsigned char)(flags | orflags), quotes, io_ch, arg);
+			if(len < 0) return -1;
+			outlen += len;
+		}
+	}
+	return outlen;
+}
+
+/* This function hex dumps a buffer of characters */
+
+static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf, int buflen)
+{
+	static const char hexdig[] = "0123456789ABCDEF";
+	unsigned char *p, *q;
+	char hextmp[2];
+	if(arg) {
+		p = buf;
+		q = buf + buflen;
+		while(p != q) {
+			hextmp[0] = hexdig[*p >> 4];
+			hextmp[1] = hexdig[*p & 0xf];
+			if(!io_ch(arg, hextmp, 2)) return -1;
+			p++;
+		}
+	}
+	return buflen << 1;
+}
+
+/* "dump" a string. This is done when the type is unknown,
+ * or the flags request it. We can either dump the content
+ * octets or the entire DER encoding. This uses the RFC2253
+ * #01234 format.
+ */
+
+static int do_dump(unsigned long lflags, char_io *io_ch, void *arg, ASN1_STRING *str)
+{
+	/* Placing the ASN1_STRING in a temp ASN1_TYPE allows
+	 * the DER encoding to readily obtained
+	 */
+	ASN1_TYPE t;
+	unsigned char *der_buf, *p;
+	int outlen, der_len;
+
+	if(!io_ch(arg, "#", 1)) return -1;
+	/* If we don't dump DER encoding just dump content octets */
+	if(!(lflags & ASN1_STRFLGS_DUMP_DER)) {
+		outlen = do_hex_dump(io_ch, arg, str->data, str->length);
+		if(outlen < 0) return -1;
+		return outlen + 1;
+	}
+	t.type = str->type;
+	t.value.ptr = (char *)str;
+	der_len = i2d_ASN1_TYPE(&t, NULL);
+	der_buf = OPENSSL_malloc(der_len);
+	if(!der_buf) return -1;
+	p = der_buf;
+	i2d_ASN1_TYPE(&t, &p);
+	outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
+	OPENSSL_free(der_buf);
+	if(outlen < 0) return -1;
+	return outlen + 1;
+}
+
+/* Lookup table to convert tags to character widths,
+ * 0 = UTF8 encoded, -1 is used for non string types
+ * otherwise it is the number of bytes per character
+ */
+
+static const signed char tag2nbyte[] = {
+	-1, -1, -1, -1, -1,	/* 0-4 */
+	-1, -1, -1, -1, -1,	/* 5-9 */
+	-1, -1, 0, -1,		/* 10-13 */
+	-1, -1, -1, -1,		/* 15-17 */
+	-1, 1, 1,		/* 18-20 */
+	-1, 1, 1, 1,		/* 21-24 */
+	-1, 1, -1,		/* 25-27 */
+	4, -1, 2		/* 28-30 */
+};
+
+/* This is the main function, print out an
+ * ASN1_STRING taking note of various escape
+ * and display options. Returns number of
+ * characters written or -1 if an error
+ * occurred.
+ */
+
+static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, ASN1_STRING *str)
+{
+	int outlen, len;
+	int type;
+	char quotes;
+	unsigned char flags;
+	quotes = 0;
+	/* Keep a copy of escape flags */
+	flags = (unsigned char)(lflags & ESC_FLAGS);
+
+	type = str->type;
+
+	outlen = 0;
+
+
+	if(lflags & ASN1_STRFLGS_SHOW_TYPE) {
+		const char *tagname;
+		tagname = ASN1_tag2str(type);
+		outlen += strlen(tagname);
+		if(!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1)) return -1; 
+		outlen++;
+	}
+
+	/* Decide what to do with type, either dump content or display it */
+
+	/* Dump everything */
+	if(lflags & ASN1_STRFLGS_DUMP_ALL) type = -1;
+	/* Ignore the string type */
+	else if(lflags & ASN1_STRFLGS_IGNORE_TYPE) type = 1;
+	else {
+		/* Else determine width based on type */
+		if((type > 0) && (type < 31)) type = tag2nbyte[type];
+		else type = -1;
+		if((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN)) type = 1;
+	}
+
+	if(type == -1) {
+		len = do_dump(lflags, io_ch, arg, str);
+		if(len < 0) return -1;
+		outlen += len;
+		return outlen;
+	}
+
+	if(lflags & ASN1_STRFLGS_UTF8_CONVERT) {
+		/* Note: if string is UTF8 and we want
+		 * to convert to UTF8 then we just interpret
+		 * it as 1 byte per character to avoid converting
+		 * twice.
+		 */
+		if(!type) type = 1;
+		else type |= BUF_TYPE_CONVUTF8;
+	}
+
+	len = do_buf(str->data, str->length, type, flags, &quotes, io_ch, NULL);
+	if(len < 0) return -1;
+	outlen += len;
+	if(quotes) outlen += 2;
+	if(!arg) return outlen;
+	if(quotes && !io_ch(arg, "\"", 1)) return -1;
+	if(do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0)
+		return -1;
+	if(quotes && !io_ch(arg, "\"", 1)) return -1;
+	return outlen;
+}
+
+/* Used for line indenting: print 'indent' spaces */
+
+static int do_indent(char_io *io_ch, void *arg, int indent)
+{
+	int i;
+	for(i = 0; i < indent; i++)
+			if(!io_ch(arg, " ", 1)) return 0;
+	return 1;
+}
+
+#define FN_WIDTH_LN	25
+#define FN_WIDTH_SN	10
+
+static int do_name_ex(char_io *io_ch, void *arg, X509_NAME *n,
+				int indent, unsigned long flags)
+{
+	int i, prev = -1, orflags, cnt;
+	int fn_opt, fn_nid;
+	ASN1_OBJECT *fn;
+	ASN1_STRING *val;
+	X509_NAME_ENTRY *ent;
+	char objtmp[80];
+	const char *objbuf;
+	int outlen, len;
+	char *sep_dn, *sep_mv, *sep_eq;
+	int sep_dn_len, sep_mv_len, sep_eq_len;
+	if(indent < 0) indent = 0;
+	outlen = indent;
+	if(!do_indent(io_ch, arg, indent)) return -1;
+	switch (flags & XN_FLAG_SEP_MASK)
+	{
+		case XN_FLAG_SEP_MULTILINE:
+		sep_dn = "\n";
+		sep_dn_len = 1;
+		sep_mv = " + ";
+		sep_mv_len = 3;
+		break;
+
+		case XN_FLAG_SEP_COMMA_PLUS:
+		sep_dn = ",";
+		sep_dn_len = 1;
+		sep_mv = "+";
+		sep_mv_len = 1;
+		indent = 0;
+		break;
+
+		case XN_FLAG_SEP_CPLUS_SPC:
+		sep_dn = ", ";
+		sep_dn_len = 2;
+		sep_mv = " + ";
+		sep_mv_len = 3;
+		indent = 0;
+		break;
+
+		case XN_FLAG_SEP_SPLUS_SPC:
+		sep_dn = "; ";
+		sep_dn_len = 2;
+		sep_mv = " + ";
+		sep_mv_len = 3;
+		indent = 0;
+		break;
+
+		default:
+		return -1;
+	}
+
+	if(flags & XN_FLAG_SPC_EQ) {
+		sep_eq = " = ";
+		sep_eq_len = 3;
+	} else {
+		sep_eq = "=";
+		sep_eq_len = 1;
+	}
+
+	fn_opt = flags & XN_FLAG_FN_MASK;
+
+	cnt = X509_NAME_entry_count(n);	
+	for(i = 0; i < cnt; i++) {
+		if(flags & XN_FLAG_DN_REV)
+				ent = X509_NAME_get_entry(n, cnt - i - 1);
+		else ent = X509_NAME_get_entry(n, i);
+		if(prev != -1) {
+			if(prev == ent->set) {
+				if(!io_ch(arg, sep_mv, sep_mv_len)) return -1;
+				outlen += sep_mv_len;
+			} else {
+				if(!io_ch(arg, sep_dn, sep_dn_len)) return -1;
+				outlen += sep_dn_len;
+				if(!do_indent(io_ch, arg, indent)) return -1;
+				outlen += indent;
+			}
+		}
+		prev = ent->set;
+		fn = X509_NAME_ENTRY_get_object(ent);
+		val = X509_NAME_ENTRY_get_data(ent);
+		fn_nid = OBJ_obj2nid(fn);
+		if(fn_opt != XN_FLAG_FN_NONE) {
+			int objlen, fld_len;
+			if((fn_opt == XN_FLAG_FN_OID) || (fn_nid==NID_undef) ) {
+				OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
+				fld_len = 0; /* XXX: what should this be? */
+				objbuf = objtmp;
+			} else {
+				if(fn_opt == XN_FLAG_FN_SN) {
+					fld_len = FN_WIDTH_SN;
+					objbuf = OBJ_nid2sn(fn_nid);
+				} else if(fn_opt == XN_FLAG_FN_LN) {
+					fld_len = FN_WIDTH_LN;
+					objbuf = OBJ_nid2ln(fn_nid);
+				} else {
+					fld_len = 0; /* XXX: what should this be? */
+					objbuf = "";
+				}
+			}
+			objlen = strlen(objbuf);
+			if(!io_ch(arg, objbuf, objlen)) return -1;
+			if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
+				if (!do_indent(io_ch, arg, fld_len - objlen)) return -1;
+				outlen += fld_len - objlen;
+			}
+			if(!io_ch(arg, sep_eq, sep_eq_len)) return -1;
+			outlen += objlen + sep_eq_len;
+		}
+		/* If the field name is unknown then fix up the DER dump
+		 * flag. We might want to limit this further so it will
+ 		 * DER dump on anything other than a few 'standard' fields.
+		 */
+		if((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS)) 
+					orflags = ASN1_STRFLGS_DUMP_ALL;
+		else orflags = 0;
+     
+		len = do_print_ex(io_ch, arg, flags | orflags, val);
+		if(len < 0) return -1;
+		outlen += len;
+	}
+	return outlen;
+}
+
+/* Wrappers round the main functions */
+
+int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent, unsigned long flags)
+{
+	if(flags == XN_FLAG_COMPAT)
+		return X509_NAME_print(out, nm, indent);
+	return do_name_ex(send_bio_chars, out, nm, indent, flags);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent, unsigned long flags)
+{
+	if(flags == XN_FLAG_COMPAT)
+		{
+		BIO *btmp;
+		int ret;
+		btmp = BIO_new_fp(fp, BIO_NOCLOSE);
+		if(!btmp) return -1;
+		ret = X509_NAME_print(btmp, nm, indent);
+		BIO_free(btmp);
+		return ret;
+		}
+	return do_name_ex(send_fp_chars, fp, nm, indent, flags);
+}
+#endif
+
+int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags)
+{
+	return do_print_ex(send_bio_chars, out, flags, str);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags)
+{
+	return do_print_ex(send_fp_chars, fp, flags, str);
+}
+#endif
+
+/* Utility function: convert any string type to UTF8, returns number of bytes
+ * in output string or a negative error code
+ */
+
+int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in)
+{
+	ASN1_STRING stmp, *str = &stmp;
+	int mbflag, type, ret;
+	if(!in) return -1;
+	type = in->type;
+	if((type < 0) || (type > 30)) return -1;
+	mbflag = tag2nbyte[type];
+	if(mbflag == -1) return -1;
+	mbflag |= MBSTRING_FLAG;
+	stmp.data = NULL;
+	stmp.length = 0;
+	ret = ASN1_mbstring_copy(&str, in->data, in->length, mbflag, B_ASN1_UTF8STRING);
+	if(ret < 0) return ret;
+	*out = stmp.data;
+	return stmp.length;
+}
diff --git a/crypto/x509/a_verify.c b/crypto/x509/a_verify.c
new file mode 100644
index 0000000..2bece38
--- /dev/null
+++ b/crypto/x509/a_verify.c
@@ -0,0 +1,169 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "../evp/internal.h"
+
+
+int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a,
+		ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey)
+	{
+	EVP_MD_CTX ctx;
+	unsigned char *buf_in=NULL;
+	int ret= -1,inl;
+	const EVP_PKEY_ASN1_METHOD *ameth;
+
+	int mdnid, pknid;
+
+	EVP_MD_CTX_init(&ctx);
+
+	/* Convert signature OID into digest and public key OIDs */
+	if (!OBJ_find_sigid_algs(OBJ_obj2nid(a->algorithm), &mdnid, &pknid))
+		{
+		OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
+		goto err;
+		}
+	if (mdnid == NID_undef)
+		{
+		if (!pkey->ameth || !pkey->ameth->item_verify)
+			{
+			OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
+			goto err;
+			}
+		ret = pkey->ameth->item_verify(&ctx, it, asn, a,
+							signature, pkey);
+		/* Return value of 2 means carry on, anything else means we
+		 * exit straight away: either a fatal error of the underlying
+		 * verification routine handles all verification.
+		 */
+		if (ret != 2)
+			goto err;
+		ret = -1;
+		}
+	else
+		{
+		const EVP_MD *type;
+		type=EVP_get_digestbynid(mdnid);
+		if (type == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
+			goto err;
+			}
+
+		/* Check public key OID matches public key type */
+		ameth = EVP_PKEY_asn1_find(NULL, pknid);
+		if (ameth == NULL || ameth->pkey_id != pkey->ameth->pkey_id)
+			{
+			OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ASN1_R_WRONG_PUBLIC_KEY_TYPE);
+			goto err;
+			}
+
+		if (!EVP_DigestVerifyInit(&ctx, NULL, type, NULL, pkey))
+			{
+			OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ERR_R_EVP_LIB);
+			ret=0;
+			goto err;
+			}
+
+		}
+
+	inl = ASN1_item_i2d(asn, &buf_in, it);
+	
+	if (buf_in == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	if (!EVP_DigestVerifyUpdate(&ctx,buf_in,inl))
+		{
+		OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ERR_R_EVP_LIB);
+		ret=0;
+		goto err;
+		}
+
+	OPENSSL_cleanse(buf_in,(unsigned int)inl);
+	OPENSSL_free(buf_in);
+
+	if (EVP_DigestVerifyFinal(&ctx,signature->data,
+			(size_t)signature->length) <= 0)
+		{
+		OPENSSL_PUT_ERROR(X509, ASN1_item_verify, ERR_R_EVP_LIB);
+		ret=0;
+		goto err;
+		}
+	/* we don't need to zero the 'ctx' because we just checked
+	 * public information */
+	/* memset(&ctx,0,sizeof(ctx)); */
+	ret=1;
+err:
+	EVP_MD_CTX_cleanup(&ctx);
+	return(ret);
+	}
+
diff --git a/crypto/x509/asn1_gen.c b/crypto/x509/asn1_gen.c
new file mode 100644
index 0000000..bbdc1a3
--- /dev/null
+++ b/crypto/x509/asn1_gen.c
@@ -0,0 +1,857 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+#define ASN1_GEN_FLAG		0x10000
+#define ASN1_GEN_FLAG_IMP	(ASN1_GEN_FLAG|1)
+#define ASN1_GEN_FLAG_EXP	(ASN1_GEN_FLAG|2)
+#define ASN1_GEN_FLAG_TAG	(ASN1_GEN_FLAG|3)
+#define ASN1_GEN_FLAG_BITWRAP	(ASN1_GEN_FLAG|4)
+#define ASN1_GEN_FLAG_OCTWRAP	(ASN1_GEN_FLAG|5)
+#define ASN1_GEN_FLAG_SEQWRAP	(ASN1_GEN_FLAG|6)
+#define ASN1_GEN_FLAG_SETWRAP	(ASN1_GEN_FLAG|7)
+#define ASN1_GEN_FLAG_FORMAT	(ASN1_GEN_FLAG|8)
+
+#define ASN1_GEN_STR(str,val)	{str, sizeof(str) - 1, val}
+
+#define ASN1_FLAG_EXP_MAX	20
+
+/* Input formats */
+
+/* ASCII: default */
+#define ASN1_GEN_FORMAT_ASCII	1
+/* UTF8 */
+#define ASN1_GEN_FORMAT_UTF8	2
+/* Hex */
+#define ASN1_GEN_FORMAT_HEX	3
+/* List of bits */
+#define ASN1_GEN_FORMAT_BITLIST	4
+
+
+struct tag_name_st
+	{
+	const char *strnam;
+	int len;
+	int tag;
+	};
+
+typedef struct
+	{
+	int exp_tag;
+	int exp_class;
+	int exp_constructed;
+	int exp_pad;
+	long exp_len;
+	} tag_exp_type;
+
+typedef struct
+	{
+	int imp_tag;
+	int imp_class;
+	int utype;
+	int format;
+	const char *str;
+	tag_exp_type exp_list[ASN1_FLAG_EXP_MAX];
+	int exp_count;
+	} tag_exp_arg;
+
+static int bitstr_cb(const char *elem, int len, void *bitstr);
+static int asn1_cb(const char *elem, int len, void *bitstr);
+static int append_exp(tag_exp_arg *arg, int exp_tag, int exp_class, int exp_constructed, int exp_pad, int imp_ok);
+static int parse_tagging(const char *vstart, int vlen, int *ptag, int *pclass);
+static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf);
+static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype);
+static int asn1_str2tag(const char *tagstr, int len);
+
+ASN1_TYPE *ASN1_generate_nconf(char *str, CONF *nconf)
+	{
+	X509V3_CTX cnf;
+
+	if (!nconf)
+		return ASN1_generate_v3(str, NULL);
+
+	X509V3_set_nconf(&cnf, nconf);
+	return ASN1_generate_v3(str, &cnf);
+	}
+
+ASN1_TYPE *ASN1_generate_v3(char *str, X509V3_CTX *cnf)
+	{
+	ASN1_TYPE *ret;
+	tag_exp_arg asn1_tags;
+	tag_exp_type *etmp;
+
+	int i, len;
+
+	unsigned char *orig_der = NULL, *new_der = NULL;
+	const unsigned char *cpy_start;
+	unsigned char *p;
+	const unsigned char *cp;
+	int cpy_len;
+	long hdr_len;
+	int hdr_constructed = 0, hdr_tag, hdr_class;
+	int r;
+
+	asn1_tags.imp_tag = -1;
+	asn1_tags.imp_class = -1;
+	asn1_tags.format = ASN1_GEN_FORMAT_ASCII;
+	asn1_tags.exp_count = 0;
+	if (CONF_parse_list(str, ',', 1, asn1_cb, &asn1_tags) != 0)
+		return NULL;
+
+	if ((asn1_tags.utype == V_ASN1_SEQUENCE) || (asn1_tags.utype == V_ASN1_SET))
+		{
+		if (!cnf)
+			{
+			OPENSSL_PUT_ERROR(X509, ASN1_generate_v3, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG);
+			return NULL;
+			}
+		ret = asn1_multi(asn1_tags.utype, asn1_tags.str, cnf);
+		}
+	else
+		ret = asn1_str2type(asn1_tags.str, asn1_tags.format, asn1_tags.utype);
+
+	if (!ret)
+		return NULL;
+
+	/* If no tagging return base type */
+	if ((asn1_tags.imp_tag == -1) && (asn1_tags.exp_count == 0))
+		return ret;
+
+	/* Generate the encoding */
+	cpy_len = i2d_ASN1_TYPE(ret, &orig_der);
+	ASN1_TYPE_free(ret);
+	ret = NULL;
+	/* Set point to start copying for modified encoding */
+	cpy_start = orig_der;
+
+	/* Do we need IMPLICIT tagging? */
+	if (asn1_tags.imp_tag != -1)
+		{
+		/* If IMPLICIT we will replace the underlying tag */
+		/* Skip existing tag+len */
+		r = ASN1_get_object(&cpy_start, &hdr_len, &hdr_tag, &hdr_class, cpy_len);
+		if (r & 0x80)
+			goto err;
+		/* Update copy length */
+		cpy_len -= cpy_start - orig_der;
+		/* For IMPLICIT tagging the length should match the
+		 * original length and constructed flag should be
+		 * consistent.
+		 */
+		if (r & 0x1)
+			{
+			/* Indefinite length constructed */
+			hdr_constructed = 2;
+			hdr_len = 0;
+			}
+		else
+			/* Just retain constructed flag */
+			hdr_constructed = r & V_ASN1_CONSTRUCTED;
+		/* Work out new length with IMPLICIT tag: ignore constructed
+		 * because it will mess up if indefinite length
+		 */
+		len = ASN1_object_size(0, hdr_len, asn1_tags.imp_tag);
+		}
+	else
+		len = cpy_len;
+
+	/* Work out length in any EXPLICIT, starting from end */
+
+	for(i = 0, etmp = asn1_tags.exp_list + asn1_tags.exp_count - 1; i < asn1_tags.exp_count; i++, etmp--)
+		{
+		/* Content length: number of content octets + any padding */
+		len += etmp->exp_pad;
+		etmp->exp_len = len;
+		/* Total object length: length including new header */
+		len = ASN1_object_size(0, len, etmp->exp_tag);
+		}
+
+	/* Allocate buffer for new encoding */
+
+	new_der = OPENSSL_malloc(len);
+	if (!new_der)
+		goto err;
+
+	/* Generate tagged encoding */
+
+	p = new_der;
+
+	/* Output explicit tags first */
+
+	for (i = 0, etmp = asn1_tags.exp_list; i < asn1_tags.exp_count; i++, etmp++)
+		{
+		ASN1_put_object(&p, etmp->exp_constructed, etmp->exp_len,
+					etmp->exp_tag, etmp->exp_class);
+		if (etmp->exp_pad)
+			*p++ = 0;
+		}
+
+	/* If IMPLICIT, output tag */
+
+	if (asn1_tags.imp_tag != -1)
+		{
+		if (asn1_tags.imp_class == V_ASN1_UNIVERSAL 
+		    && (asn1_tags.imp_tag == V_ASN1_SEQUENCE
+		     || asn1_tags.imp_tag == V_ASN1_SET) )
+			hdr_constructed = V_ASN1_CONSTRUCTED;
+		ASN1_put_object(&p, hdr_constructed, hdr_len,
+					asn1_tags.imp_tag, asn1_tags.imp_class);
+		}
+
+	/* Copy across original encoding */
+	memcpy(p, cpy_start, cpy_len);
+
+	cp = new_der;
+
+	/* Obtain new ASN1_TYPE structure */
+	ret = d2i_ASN1_TYPE(NULL, &cp, len);
+
+	err:
+	if (orig_der)
+		OPENSSL_free(orig_der);
+	if (new_der)
+		OPENSSL_free(new_der);
+
+	return ret;
+
+	}
+
+static int asn1_cb(const char *elem, int len, void *bitstr)
+	{
+	tag_exp_arg *arg = bitstr;
+	int i;
+	int utype;
+	int vlen = 0;
+	const char *p, *vstart = NULL;
+
+	int tmp_tag, tmp_class;
+
+	for(i = 0, p = elem; i < len; p++, i++)
+		{
+		/* Look for the ':' in name value pairs */
+		if (*p == ':')
+			{
+			vstart = p + 1;
+			vlen = len - (vstart - elem);
+			len = p - elem;
+			break;
+			}
+		}
+
+	utype = asn1_str2tag(elem, len);
+
+	if (utype == -1)
+		{
+		OPENSSL_PUT_ERROR(X509, asn1_cb, ASN1_R_UNKNOWN_TAG);
+		ERR_add_error_data(2, "tag=", elem);
+		return -1;
+		}
+
+	/* If this is not a modifier mark end of string and exit */
+	if (!(utype & ASN1_GEN_FLAG))
+		{
+		arg->utype = utype;
+		arg->str = vstart;
+		/* If no value and not end of string, error */
+		if (!vstart && elem[len])
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_cb, ASN1_R_MISSING_VALUE);
+			return -1;
+			}
+		return 0;
+		}
+
+	switch(utype)
+		{
+
+		case ASN1_GEN_FLAG_IMP:
+		/* Check for illegal multiple IMPLICIT tagging */
+		if (arg->imp_tag != -1)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_cb, ASN1_R_ILLEGAL_NESTED_TAGGING);
+			return -1;
+			}
+		if (!parse_tagging(vstart, vlen, &arg->imp_tag, &arg->imp_class))
+			return -1;
+		break;
+
+		case ASN1_GEN_FLAG_EXP:
+
+		if (!parse_tagging(vstart, vlen, &tmp_tag, &tmp_class))
+			return -1;
+		if (!append_exp(arg, tmp_tag, tmp_class, 1, 0, 0))
+			return -1;
+		break;
+
+		case ASN1_GEN_FLAG_SEQWRAP:
+		if (!append_exp(arg, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL, 1, 0, 1))
+			return -1;
+		break;
+
+		case ASN1_GEN_FLAG_SETWRAP:
+		if (!append_exp(arg, V_ASN1_SET, V_ASN1_UNIVERSAL, 1, 0, 1))
+			return -1;
+		break;
+
+		case ASN1_GEN_FLAG_BITWRAP:
+		if (!append_exp(arg, V_ASN1_BIT_STRING, V_ASN1_UNIVERSAL, 0, 1, 1))
+			return -1;
+		break;
+
+		case ASN1_GEN_FLAG_OCTWRAP:
+		if (!append_exp(arg, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL, 0, 0, 1))
+			return -1;
+		break;
+
+		case ASN1_GEN_FLAG_FORMAT:
+		if (!strncmp(vstart, "ASCII", 5))
+			arg->format = ASN1_GEN_FORMAT_ASCII;
+		else if (!strncmp(vstart, "UTF8", 4))
+			arg->format = ASN1_GEN_FORMAT_UTF8;
+		else if (!strncmp(vstart, "HEX", 3))
+			arg->format = ASN1_GEN_FORMAT_HEX;
+		else if (!strncmp(vstart, "BITLIST", 3))
+			arg->format = ASN1_GEN_FORMAT_BITLIST;
+		else
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_cb, ASN1_R_UNKOWN_FORMAT);
+			return -1;
+			}
+		break;
+
+		}
+
+	return 1;
+
+	}
+
+static int parse_tagging(const char *vstart, int vlen, int *ptag, int *pclass)
+	{
+	char erch[2];
+	long tag_num;
+	char *eptr;
+	if (!vstart)
+		return 0;
+	tag_num = strtoul(vstart, &eptr, 10);
+	/* Check we haven't gone past max length: should be impossible */
+	if (eptr && *eptr && (eptr > vstart + vlen))
+		return 0;
+	if (tag_num < 0)
+		{
+		OPENSSL_PUT_ERROR(X509, parse_tagging, ASN1_R_INVALID_NUMBER);
+		return 0;
+		}
+	*ptag = tag_num;
+	/* If we have non numeric characters, parse them */
+	if (eptr)
+		vlen -= eptr - vstart;
+	else 
+		vlen = 0;
+	if (vlen)
+		{
+		switch (*eptr)
+			{
+
+			case 'U':
+			*pclass = V_ASN1_UNIVERSAL;
+			break;
+
+			case 'A':
+			*pclass = V_ASN1_APPLICATION;
+			break;
+
+			case 'P':
+			*pclass = V_ASN1_PRIVATE;
+			break;
+
+			case 'C':
+			*pclass = V_ASN1_CONTEXT_SPECIFIC;
+			break;
+
+			default:
+			erch[0] = *eptr;
+			erch[1] = 0;
+			OPENSSL_PUT_ERROR(X509, parse_tagging, ASN1_R_INVALID_MODIFIER);
+			ERR_add_error_data(2, "Char=", erch);
+			return 0;
+			break;
+
+			}
+		}
+	else
+		*pclass = V_ASN1_CONTEXT_SPECIFIC;
+
+	return 1;
+
+	}
+
+/* Handle multiple types: SET and SEQUENCE */
+
+static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf)
+	{
+	ASN1_TYPE *ret = NULL;
+	STACK_OF(ASN1_TYPE) *sk = NULL;
+	STACK_OF(CONF_VALUE) *sect = NULL;
+	unsigned char *der = NULL;
+	int derlen;
+	size_t i;
+	sk = sk_ASN1_TYPE_new_null();
+	if (!sk)
+		goto bad;
+	if (section)
+		{
+		if (!cnf)
+			goto bad;
+		sect = X509V3_get_section(cnf, (char *)section);
+		if (!sect)
+			goto bad;
+		for (i = 0; i < sk_CONF_VALUE_num(sect); i++)
+			{
+			ASN1_TYPE *typ = ASN1_generate_v3(sk_CONF_VALUE_value(sect, i)->value, cnf);
+			if (!typ)
+				goto bad;
+			if (!sk_ASN1_TYPE_push(sk, typ))
+				goto bad;
+			}
+		}
+
+	/* Now we has a STACK of the components, convert to the correct form */
+
+	if (utype == V_ASN1_SET)
+		derlen = i2d_ASN1_SET_ANY(sk, &der);
+	else
+		derlen = i2d_ASN1_SEQUENCE_ANY(sk, &der);
+
+	if (derlen < 0)
+		goto bad;
+
+	if (!(ret = ASN1_TYPE_new()))
+		goto bad;
+
+	if (!(ret->value.asn1_string = ASN1_STRING_type_new(utype)))
+		goto bad;
+
+	ret->type = utype;
+
+	ret->value.asn1_string->data = der;
+	ret->value.asn1_string->length = derlen;
+
+	der = NULL;
+
+	bad:
+
+	if (der)
+		OPENSSL_free(der);
+
+	if (sk)
+		sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free);
+	if (sect)
+		X509V3_section_free(cnf, sect);
+
+	return ret;
+	}
+
+static int append_exp(tag_exp_arg *arg, int exp_tag, int exp_class, int exp_constructed, int exp_pad, int imp_ok)
+	{
+	tag_exp_type *exp_tmp;
+	/* Can only have IMPLICIT if permitted */
+	if ((arg->imp_tag != -1) && !imp_ok)
+		{
+		OPENSSL_PUT_ERROR(X509, append_exp, ASN1_R_ILLEGAL_IMPLICIT_TAG);
+		return 0;
+		}
+
+	if (arg->exp_count == ASN1_FLAG_EXP_MAX)
+		{
+		OPENSSL_PUT_ERROR(X509, append_exp, ASN1_R_DEPTH_EXCEEDED);
+		return 0;
+		}
+
+	exp_tmp = &arg->exp_list[arg->exp_count++];
+
+	/* If IMPLICIT set tag to implicit value then
+	 * reset implicit tag since it has been used.
+	 */
+	if (arg->imp_tag != -1)
+		{
+		exp_tmp->exp_tag = arg->imp_tag;
+		exp_tmp->exp_class = arg->imp_class;
+		arg->imp_tag = -1;
+		arg->imp_class = -1;
+		}
+	else
+		{
+		exp_tmp->exp_tag = exp_tag;
+		exp_tmp->exp_class = exp_class;
+		}
+	exp_tmp->exp_constructed = exp_constructed;
+	exp_tmp->exp_pad = exp_pad;
+
+	return 1;
+	}
+
+
+static int asn1_str2tag(const char *tagstr, int len)
+	{
+	unsigned int i;
+	static const struct tag_name_st *tntmp, tnst [] = {
+		ASN1_GEN_STR("BOOL", V_ASN1_BOOLEAN),
+		ASN1_GEN_STR("BOOLEAN", V_ASN1_BOOLEAN),
+		ASN1_GEN_STR("NULL", V_ASN1_NULL),
+		ASN1_GEN_STR("INT", V_ASN1_INTEGER),
+		ASN1_GEN_STR("INTEGER", V_ASN1_INTEGER),
+		ASN1_GEN_STR("ENUM", V_ASN1_ENUMERATED),
+		ASN1_GEN_STR("ENUMERATED", V_ASN1_ENUMERATED),
+		ASN1_GEN_STR("OID", V_ASN1_OBJECT),
+		ASN1_GEN_STR("OBJECT", V_ASN1_OBJECT),
+		ASN1_GEN_STR("UTCTIME", V_ASN1_UTCTIME),
+		ASN1_GEN_STR("UTC", V_ASN1_UTCTIME),
+		ASN1_GEN_STR("GENERALIZEDTIME", V_ASN1_GENERALIZEDTIME),
+		ASN1_GEN_STR("GENTIME", V_ASN1_GENERALIZEDTIME),
+		ASN1_GEN_STR("OCT", V_ASN1_OCTET_STRING),
+		ASN1_GEN_STR("OCTETSTRING", V_ASN1_OCTET_STRING),
+		ASN1_GEN_STR("BITSTR", V_ASN1_BIT_STRING),
+		ASN1_GEN_STR("BITSTRING", V_ASN1_BIT_STRING),
+		ASN1_GEN_STR("UNIVERSALSTRING", V_ASN1_UNIVERSALSTRING),
+		ASN1_GEN_STR("UNIV", V_ASN1_UNIVERSALSTRING),
+		ASN1_GEN_STR("IA5", V_ASN1_IA5STRING),
+		ASN1_GEN_STR("IA5STRING", V_ASN1_IA5STRING),
+		ASN1_GEN_STR("UTF8", V_ASN1_UTF8STRING),
+		ASN1_GEN_STR("UTF8String", V_ASN1_UTF8STRING),
+		ASN1_GEN_STR("BMP", V_ASN1_BMPSTRING),
+		ASN1_GEN_STR("BMPSTRING", V_ASN1_BMPSTRING),
+		ASN1_GEN_STR("VISIBLESTRING", V_ASN1_VISIBLESTRING),
+		ASN1_GEN_STR("VISIBLE", V_ASN1_VISIBLESTRING),
+		ASN1_GEN_STR("PRINTABLESTRING", V_ASN1_PRINTABLESTRING),
+		ASN1_GEN_STR("PRINTABLE", V_ASN1_PRINTABLESTRING),
+		ASN1_GEN_STR("T61", V_ASN1_T61STRING),
+		ASN1_GEN_STR("T61STRING", V_ASN1_T61STRING),
+		ASN1_GEN_STR("TELETEXSTRING", V_ASN1_T61STRING),
+		ASN1_GEN_STR("GeneralString", V_ASN1_GENERALSTRING),
+		ASN1_GEN_STR("GENSTR", V_ASN1_GENERALSTRING),
+		ASN1_GEN_STR("NUMERIC", V_ASN1_NUMERICSTRING),
+		ASN1_GEN_STR("NUMERICSTRING", V_ASN1_NUMERICSTRING),
+
+		/* Special cases */
+		ASN1_GEN_STR("SEQUENCE", V_ASN1_SEQUENCE),
+		ASN1_GEN_STR("SEQ", V_ASN1_SEQUENCE),
+		ASN1_GEN_STR("SET", V_ASN1_SET),
+		/* type modifiers */
+		/* Explicit tag */
+		ASN1_GEN_STR("EXP", ASN1_GEN_FLAG_EXP),
+		ASN1_GEN_STR("EXPLICIT", ASN1_GEN_FLAG_EXP),
+		/* Implicit tag */
+		ASN1_GEN_STR("IMP", ASN1_GEN_FLAG_IMP),
+		ASN1_GEN_STR("IMPLICIT", ASN1_GEN_FLAG_IMP),
+		/* OCTET STRING wrapper */
+		ASN1_GEN_STR("OCTWRAP", ASN1_GEN_FLAG_OCTWRAP),
+		/* SEQUENCE wrapper */
+		ASN1_GEN_STR("SEQWRAP", ASN1_GEN_FLAG_SEQWRAP),
+		/* SET wrapper */
+		ASN1_GEN_STR("SETWRAP", ASN1_GEN_FLAG_SETWRAP),
+		/* BIT STRING wrapper */
+		ASN1_GEN_STR("BITWRAP", ASN1_GEN_FLAG_BITWRAP),
+		ASN1_GEN_STR("FORM", ASN1_GEN_FLAG_FORMAT),
+		ASN1_GEN_STR("FORMAT", ASN1_GEN_FLAG_FORMAT),
+	};
+
+	if (len == -1)
+		len = strlen(tagstr);
+	
+	tntmp = tnst;	
+	for (i = 0; i < sizeof(tnst) / sizeof(struct tag_name_st); i++, tntmp++)
+		{
+		if ((len == tntmp->len) && !strncmp(tntmp->strnam, tagstr, len))
+			return tntmp->tag;
+		}
+	
+	return -1;
+	}
+
+static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype)
+	{
+	ASN1_TYPE *atmp = NULL;
+
+	CONF_VALUE vtmp;
+
+	unsigned char *rdata;
+	long rdlen;
+
+	int no_unused = 1;
+
+	if (!(atmp = ASN1_TYPE_new()))
+		{
+		OPENSSL_PUT_ERROR(X509, asn1_str2type, ERR_R_MALLOC_FAILURE);
+		return NULL;
+		}
+
+	if (!str)
+		str = "";
+
+	switch(utype)
+		{
+
+		case V_ASN1_NULL:
+		if (str && *str)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_NULL_VALUE);
+			goto bad_form;
+			}
+		break;
+		
+		case V_ASN1_BOOLEAN:
+		if (format != ASN1_GEN_FORMAT_ASCII)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_NOT_ASCII_FORMAT);
+			goto bad_form;
+			}
+		vtmp.name = NULL;
+		vtmp.section = NULL;
+		vtmp.value = (char *)str;
+		if (!X509V3_get_value_bool(&vtmp, &atmp->value.boolean))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_BOOLEAN);
+			goto bad_str;
+			}
+		break;
+
+		case V_ASN1_INTEGER:
+		case V_ASN1_ENUMERATED:
+		if (format != ASN1_GEN_FORMAT_ASCII)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_INTEGER_NOT_ASCII_FORMAT);
+			goto bad_form;
+			}
+		if (!(atmp->value.integer = s2i_ASN1_INTEGER(NULL, (char *)str)))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_INTEGER);
+			goto bad_str;
+			}
+		break;
+
+		case V_ASN1_OBJECT:
+		if (format != ASN1_GEN_FORMAT_ASCII)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_OBJECT_NOT_ASCII_FORMAT);
+			goto bad_form;
+			}
+		if (!(atmp->value.object = OBJ_txt2obj(str, 0)))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_OBJECT);
+			goto bad_str;
+			}
+		break;
+
+		case V_ASN1_UTCTIME:
+		case V_ASN1_GENERALIZEDTIME:
+		if (format != ASN1_GEN_FORMAT_ASCII)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_TIME_NOT_ASCII_FORMAT);
+			goto bad_form;
+			}
+		if (!(atmp->value.asn1_string = ASN1_STRING_new()))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ERR_R_MALLOC_FAILURE);
+			goto bad_str;
+			}
+		if (!ASN1_STRING_set(atmp->value.asn1_string, str, -1))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ERR_R_MALLOC_FAILURE);
+			goto bad_str;
+			}
+		atmp->value.asn1_string->type = utype;
+		if (!ASN1_TIME_check(atmp->value.asn1_string))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_TIME_VALUE);
+			goto bad_str;
+			}
+
+		break;
+
+		case V_ASN1_BMPSTRING:
+		case V_ASN1_PRINTABLESTRING:
+		case V_ASN1_IA5STRING:
+		case V_ASN1_T61STRING:
+		case V_ASN1_UTF8STRING:
+		case V_ASN1_VISIBLESTRING:
+		case V_ASN1_UNIVERSALSTRING:
+		case V_ASN1_GENERALSTRING:
+		case V_ASN1_NUMERICSTRING:
+
+		if (format == ASN1_GEN_FORMAT_ASCII)
+			format = MBSTRING_ASC;
+		else if (format == ASN1_GEN_FORMAT_UTF8)
+			format = MBSTRING_UTF8;
+		else
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_FORMAT);
+			goto bad_form;
+			}
+
+
+		if (ASN1_mbstring_copy(&atmp->value.asn1_string, (unsigned char *)str,
+						-1, format, ASN1_tag2bit(utype)) <= 0)
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ERR_R_MALLOC_FAILURE);
+			goto bad_str;
+			}
+		
+
+		break;
+
+		case V_ASN1_BIT_STRING:
+
+		case V_ASN1_OCTET_STRING:
+
+		if (!(atmp->value.asn1_string = ASN1_STRING_new()))
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ERR_R_MALLOC_FAILURE);
+			goto bad_form;
+			}
+
+		if (format == ASN1_GEN_FORMAT_HEX)
+			{
+
+			if (!(rdata = string_to_hex((char *)str, &rdlen)))
+				{
+				OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_HEX);
+				goto bad_str;
+				}
+
+			atmp->value.asn1_string->data = rdata;
+			atmp->value.asn1_string->length = rdlen;
+			atmp->value.asn1_string->type = utype;
+
+			}
+		else if (format == ASN1_GEN_FORMAT_ASCII)
+			ASN1_STRING_set(atmp->value.asn1_string, str, -1);
+		else if ((format == ASN1_GEN_FORMAT_BITLIST) && (utype == V_ASN1_BIT_STRING))
+			{
+			if (!CONF_parse_list(str, ',', 1, bitstr_cb, atmp->value.bit_string))
+				{
+				OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_LIST_ERROR);
+				goto bad_str;
+				}
+			no_unused = 0;
+			
+			}
+		else 
+			{
+			OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_ILLEGAL_BITSTRING_FORMAT);
+			goto bad_form;
+			}
+
+		if ((utype == V_ASN1_BIT_STRING) && no_unused)
+			{
+			atmp->value.asn1_string->flags
+				&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
+        		atmp->value.asn1_string->flags
+				|= ASN1_STRING_FLAG_BITS_LEFT;
+			}
+
+
+		break;
+
+		default:
+		OPENSSL_PUT_ERROR(X509, asn1_str2type, ASN1_R_UNSUPPORTED_TYPE);
+		goto bad_str;
+		break;
+		}
+
+
+	atmp->type = utype;
+	return atmp;
+
+
+	bad_str:
+	ERR_add_error_data(2, "string=", str);
+	bad_form:
+
+	ASN1_TYPE_free(atmp);
+	return NULL;
+
+	}
+
+static int bitstr_cb(const char *elem, int len, void *bitstr)
+	{
+	long bitnum;
+	char *eptr;
+	if (!elem)
+		return 0;
+	bitnum = strtoul(elem, &eptr, 10);
+	if (eptr && *eptr && (eptr != elem + len))
+		return 0;
+	if (bitnum < 0)
+		{
+		OPENSSL_PUT_ERROR(X509, bitstr_cb, ASN1_R_INVALID_NUMBER);
+		return 0;
+		}
+	if (!ASN1_BIT_STRING_set_bit(bitstr, bitnum, 1))
+		{
+		OPENSSL_PUT_ERROR(X509, bitstr_cb, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	return 1;
+	}
+
diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c
new file mode 100644
index 0000000..8b8d0d4
--- /dev/null
+++ b/crypto/x509/by_dir.c
@@ -0,0 +1,472 @@
+/* crypto/x509/by_dir.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
+
+
+typedef struct lookup_dir_hashes_st
+	{
+	unsigned long hash;
+	int suffix;
+	} BY_DIR_HASH;
+
+typedef struct lookup_dir_entry_st
+	{
+	char *dir;
+	int dir_type;
+	STACK_OF(BY_DIR_HASH) *hashes;
+	} BY_DIR_ENTRY;
+
+typedef struct lookup_dir_st
+	{
+	BUF_MEM *buffer;
+	STACK_OF(BY_DIR_ENTRY) *dirs;
+	} BY_DIR;
+
+DECLARE_STACK_OF(BY_DIR_HASH)
+DECLARE_STACK_OF(BY_DIR_ENTRY)
+
+static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
+	char **ret);
+static int new_dir(X509_LOOKUP *lu);
+static void free_dir(X509_LOOKUP *lu);
+static int add_cert_dir(BY_DIR *ctx,const char *dir,int type);
+static int get_cert_by_subject(X509_LOOKUP *xl,int type,X509_NAME *name,
+	X509_OBJECT *ret);
+X509_LOOKUP_METHOD x509_dir_lookup=
+	{
+	"Load certs from files in a directory",
+	new_dir,		/* new */
+	free_dir,		/* free */
+	NULL, 			/* init */
+	NULL,			/* shutdown */
+	dir_ctrl,		/* ctrl */
+	get_cert_by_subject,	/* get_by_subject */
+	NULL,			/* get_by_issuer_serial */
+	NULL,			/* get_by_fingerprint */
+	NULL,			/* get_by_alias */
+	};
+
+X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void)
+	{
+	return(&x509_dir_lookup);
+	}
+
+static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
+	     char **retp)
+	{
+	int ret=0;
+	BY_DIR *ld;
+	char *dir = NULL;
+
+	ld=(BY_DIR *)ctx->method_data;
+
+	switch (cmd)
+		{
+	case X509_L_ADD_DIR:
+		if (argl == X509_FILETYPE_DEFAULT)
+			{
+			dir=(char *)getenv(X509_get_default_cert_dir_env());
+			if (dir)
+				ret=add_cert_dir(ld,dir,X509_FILETYPE_PEM);
+			else
+				ret=add_cert_dir(ld,X509_get_default_cert_dir(),
+					X509_FILETYPE_PEM);
+			if (!ret)
+				{
+				OPENSSL_PUT_ERROR(X509, dir_ctrl, X509_R_LOADING_CERT_DIR);
+				}
+			}
+		else
+			ret=add_cert_dir(ld,argp,(int)argl);
+		break;
+		}
+	return(ret);
+	}
+
+static int new_dir(X509_LOOKUP *lu)
+	{
+	BY_DIR *a;
+
+	if ((a=(BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL)
+		return(0);
+	if ((a->buffer=BUF_MEM_new()) == NULL)
+		{
+		OPENSSL_free(a);
+		return(0);
+		}
+	a->dirs=NULL;
+	lu->method_data=(char *)a;
+	return(1);
+	}
+
+static void by_dir_hash_free(BY_DIR_HASH *hash)
+	{
+	OPENSSL_free(hash);
+	}
+
+static int by_dir_hash_cmp(const BY_DIR_HASH **a,
+			const BY_DIR_HASH **b)
+	{
+	if ((*a)->hash > (*b)->hash)
+		return 1;
+	if ((*a)->hash < (*b)->hash)
+		return -1;
+	return 0;
+	}
+
+static void by_dir_entry_free(BY_DIR_ENTRY *ent)
+	{
+	if (ent->dir)
+		OPENSSL_free(ent->dir);
+	if (ent->hashes)
+		sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
+	OPENSSL_free(ent);
+	}
+
+static void free_dir(X509_LOOKUP *lu)
+	{
+	BY_DIR *a;
+
+	a=(BY_DIR *)lu->method_data;
+	if (a->dirs != NULL)
+		sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
+	if (a->buffer != NULL)
+		BUF_MEM_free(a->buffer);
+	OPENSSL_free(a);
+	}
+
+static int add_cert_dir(BY_DIR *ctx, const char *dir, int type)
+	{
+	int j,len;
+	const char *s,*ss,*p;
+
+	if (dir == NULL || !*dir)
+	    {
+	    OPENSSL_PUT_ERROR(X509, add_cert_dir, X509_R_INVALID_DIRECTORY);
+	    return 0;
+	    }
+
+	s=dir;
+	p=s;
+	for (;;p++)
+		{
+		if ((*p == ':') || (*p == '\0'))
+			{
+			BY_DIR_ENTRY *ent;
+			ss=s;
+			s=p+1;
+			len=(int)(p-ss);
+			if (len == 0) continue;
+			for (j=0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++)
+				{
+				ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
+				if (strlen(ent->dir) == (size_t)len &&
+				    strncmp(ent->dir,ss,(unsigned int)len) == 0)
+					break;
+				}
+			if (j < sk_BY_DIR_ENTRY_num(ctx->dirs))
+				continue;
+			if (ctx->dirs == NULL)
+				{
+				ctx->dirs = sk_BY_DIR_ENTRY_new_null();
+				if (!ctx->dirs)
+					{
+					OPENSSL_PUT_ERROR(X509, add_cert_dir, ERR_R_MALLOC_FAILURE);
+					return 0;
+					}
+				}
+			ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY));
+			if (!ent)
+				return 0;
+			ent->dir_type = type;
+			ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
+			ent->dir = OPENSSL_malloc((unsigned int)len+1);
+			if (!ent->dir || !ent->hashes)
+				{
+				by_dir_entry_free(ent);
+				return 0;
+				}
+			strncpy(ent->dir,ss,(unsigned int)len);
+			ent->dir[len] = '\0';
+			if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent))
+				{
+				by_dir_entry_free(ent);
+				return 0;
+				}
+			}
+		if (*p == '\0')
+			break;
+		}
+	return 1;
+	}
+
+static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
+	     X509_OBJECT *ret)
+	{
+	BY_DIR *ctx;
+	union	{
+		struct	{
+			X509 st_x509;
+			X509_CINF st_x509_cinf;
+			} x509;
+		struct	{
+			X509_CRL st_crl;
+			X509_CRL_INFO st_crl_info;
+			} crl;
+		} data;
+	int ok=0;
+	int i,j,k;
+	unsigned long h;
+	BUF_MEM *b=NULL;
+	X509_OBJECT stmp,*tmp;
+	const char *postfix="";
+
+	if (name == NULL) return(0);
+
+	stmp.type=type;
+	if (type == X509_LU_X509)
+		{
+		data.x509.st_x509.cert_info= &data.x509.st_x509_cinf;
+		data.x509.st_x509_cinf.subject=name;
+		stmp.data.x509= &data.x509.st_x509;
+		postfix="";
+		}
+	else if (type == X509_LU_CRL)
+		{
+		data.crl.st_crl.crl= &data.crl.st_crl_info;
+		data.crl.st_crl_info.issuer=name;
+		stmp.data.crl= &data.crl.st_crl;
+		postfix="r";
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(X509, get_cert_by_subject, X509_R_WRONG_LOOKUP_TYPE);
+		goto finish;
+		}
+
+	if ((b=BUF_MEM_new()) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, get_cert_by_subject, ERR_R_BUF_LIB);
+		goto finish;
+		}
+	
+	ctx=(BY_DIR *)xl->method_data;
+
+	h=X509_NAME_hash(name);
+	for (i=0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++)
+		{
+		BY_DIR_ENTRY *ent;
+		size_t idx;
+		BY_DIR_HASH htmp, *hent;
+		ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
+		j=strlen(ent->dir)+1+8+6+1+1;
+		if (!BUF_MEM_grow(b,j))
+			{
+			OPENSSL_PUT_ERROR(X509, get_cert_by_subject, ERR_R_MALLOC_FAILURE);
+			goto finish;
+			}
+		if (type == X509_LU_CRL && ent->hashes)
+			{
+			htmp.hash = h;
+			CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
+			if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp))
+				{
+				hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
+				k = hent->suffix;
+				}
+			else
+				{
+				hent = NULL;
+				k=0;
+				}
+			CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
+			}
+		else
+			{
+			k = 0;
+			hent = NULL;
+			}
+		for (;;)
+			{
+			char c = '/';
+#ifdef OPENSSL_SYS_VMS
+			c = ent->dir[strlen(ent->dir)-1];
+			if (c != ':' && c != '>' && c != ']')
+				{
+				/* If no separator is present, we assume the
+				   directory specifier is a logical name, and
+				   add a colon.  We really should use better
+				   VMS routines for merging things like this,
+				   but this will do for now...
+				   -- Richard Levitte */
+				c = ':';
+				}
+			else
+				{
+				c = '\0';
+				}
+#endif
+			if (c == '\0')
+				{
+				/* This is special.  When c == '\0', no
+				   directory separator should be added. */
+				BIO_snprintf(b->data,b->max,
+					"%s%08lx.%s%d",ent->dir,h,
+					postfix,k);
+				}
+			else
+				{
+				BIO_snprintf(b->data,b->max,
+					"%s%c%08lx.%s%d",ent->dir,c,h,
+					postfix,k);
+				}
+#ifndef OPENSSL_NO_POSIX_IO
+#ifdef _WIN32
+#define stat _stat
+#endif
+			{
+			struct stat st;
+			if (stat(b->data,&st) < 0)
+				break;
+			}
+#endif
+			/* found one. */
+			if (type == X509_LU_X509)
+				{
+				if ((X509_load_cert_file(xl,b->data,
+					ent->dir_type)) == 0)
+					break;
+				}
+			else if (type == X509_LU_CRL)
+				{
+				if ((X509_load_crl_file(xl,b->data,
+					ent->dir_type)) == 0)
+					break;
+				}
+			/* else case will caught higher up */
+			k++;
+			}
+
+		/* we have added it to the cache so now pull
+		 * it out again */
+		CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+		tmp = NULL;
+		if (sk_X509_OBJECT_find(xl->store_ctx->objs, &idx, &stmp)) {
+			tmp=sk_X509_OBJECT_value(xl->store_ctx->objs,idx);
+		}
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+
+		/* If a CRL, update the last file suffix added for this */
+
+		if (type == X509_LU_CRL)
+			{
+			CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+			/* Look for entry again in case another thread added
+			 * an entry first.
+			 */
+			if (!hent)
+				{
+				htmp.hash = h;
+				if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp))
+					hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
+				}
+			if (!hent)
+				{
+				hent = OPENSSL_malloc(sizeof(BY_DIR_HASH));
+				hent->hash = h;
+				hent->suffix = k;
+				if (!sk_BY_DIR_HASH_push(ent->hashes, hent))
+					{
+					CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+					OPENSSL_free(hent);
+					ok = 0;
+					goto finish;
+					}
+				}
+			else if (hent->suffix < k)
+				hent->suffix = k;
+
+			CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+			}
+
+		if (tmp != NULL)
+			{
+			ok=1;
+			ret->type=tmp->type;
+			memcpy(&ret->data,&tmp->data,sizeof(ret->data));
+			/* If we were going to up the reference count,
+			 * we would need to do it on a perl 'type'
+			 * basis */
+	/*		CRYPTO_add(&tmp->data.x509->references,1,
+				CRYPTO_LOCK_X509);*/
+			goto finish;
+			}
+		}
+finish:
+	if (b != NULL) BUF_MEM_free(b);
+	return(ok);
+	}
diff --git a/crypto/x509/by_file.c b/crypto/x509/by_file.c
new file mode 100644
index 0000000..2649631
--- /dev/null
+++ b/crypto/x509/by_file.c
@@ -0,0 +1,292 @@
+/* crypto/x509/by_file.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/lhash.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+#ifndef OPENSSL_NO_STDIO
+
+static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
+	long argl, char **ret);
+X509_LOOKUP_METHOD x509_file_lookup=
+	{
+	"Load file into cache",
+	NULL,		/* new */
+	NULL,		/* free */
+	NULL, 		/* init */
+	NULL,		/* shutdown */
+	by_file_ctrl,	/* ctrl */
+	NULL,		/* get_by_subject */
+	NULL,		/* get_by_issuer_serial */
+	NULL,		/* get_by_fingerprint */
+	NULL,		/* get_by_alias */
+	};
+
+X509_LOOKUP_METHOD *X509_LOOKUP_file(void)
+	{
+	return(&x509_file_lookup);
+	}
+
+static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
+	     char **ret)
+	{
+	int ok=0;
+	char *file;
+
+	switch (cmd)
+		{
+	case X509_L_FILE_LOAD:
+		if (argl == X509_FILETYPE_DEFAULT)
+			{
+			file = (char *)getenv(X509_get_default_cert_file_env());
+			if (file)
+				ok = (X509_load_cert_crl_file(ctx,file,
+					      X509_FILETYPE_PEM) != 0);
+
+			else
+				ok = (X509_load_cert_crl_file(ctx,X509_get_default_cert_file(),
+					      X509_FILETYPE_PEM) != 0);
+
+			if (!ok)
+				{
+				OPENSSL_PUT_ERROR(X509, by_file_ctrl, X509_R_LOADING_DEFAULTS);
+				}
+			}
+		else
+			{
+			if(argl == X509_FILETYPE_PEM)
+				ok = (X509_load_cert_crl_file(ctx,argp,
+					X509_FILETYPE_PEM) != 0);
+			else
+				ok = (X509_load_cert_file(ctx,argp,(int)argl) != 0);
+			}
+		break;
+		}
+	return(ok);
+	}
+
+int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type)
+	{
+	int ret=0;
+	BIO *in=NULL;
+	int i,count=0;
+	X509 *x=NULL;
+
+	if (file == NULL) return(1);
+	in=BIO_new(BIO_s_file());
+
+	if ((in == NULL) || (BIO_read_filename(in,file) <= 0))
+		{
+		OPENSSL_PUT_ERROR(X509, X509_load_cert_file, ERR_R_SYS_LIB);
+		goto err;
+		}
+
+	if (type == X509_FILETYPE_PEM)
+		{
+		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))
+					{
+					ERR_clear_error();
+					break;
+					}
+				else
+					{
+					OPENSSL_PUT_ERROR(X509, X509_load_cert_file, ERR_R_PEM_LIB);
+					goto err;
+					}
+				}
+			i=X509_STORE_add_cert(ctx->store_ctx,x);
+			if (!i) goto err;
+			count++;
+			X509_free(x);
+			x=NULL;
+			}
+		ret=count;
+		}
+	else if (type == X509_FILETYPE_ASN1)
+		{
+		x=d2i_X509_bio(in,NULL);
+		if (x == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_load_cert_file, ERR_R_ASN1_LIB);
+			goto err;
+			}
+		i=X509_STORE_add_cert(ctx->store_ctx,x);
+		if (!i) goto err;
+		ret=i;
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(X509, X509_load_cert_file, X509_R_BAD_X509_FILETYPE);
+		goto err;
+		}
+err:
+	if (x != NULL) X509_free(x);
+	if (in != NULL) BIO_free(in);
+	return(ret);
+	}
+
+int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type)
+	{
+	int ret=0;
+	BIO *in=NULL;
+	int i,count=0;
+	X509_CRL *x=NULL;
+
+	if (file == NULL) return(1);
+	in=BIO_new(BIO_s_file());
+
+	if ((in == NULL) || (BIO_read_filename(in,file) <= 0))
+		{
+		OPENSSL_PUT_ERROR(X509, X509_load_crl_file, ERR_R_SYS_LIB);
+		goto err;
+		}
+
+	if (type == X509_FILETYPE_PEM)
+		{
+		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))
+					{
+					ERR_clear_error();
+					break;
+					}
+				else
+					{
+					OPENSSL_PUT_ERROR(X509, X509_load_crl_file, ERR_R_PEM_LIB);
+					goto err;
+					}
+				}
+			i=X509_STORE_add_crl(ctx->store_ctx,x);
+			if (!i) goto err;
+			count++;
+			X509_CRL_free(x);
+			x=NULL;
+			}
+		ret=count;
+		}
+	else if (type == X509_FILETYPE_ASN1)
+		{
+		x=d2i_X509_CRL_bio(in,NULL);
+		if (x == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_load_crl_file, ERR_R_ASN1_LIB);
+			goto err;
+			}
+		i=X509_STORE_add_crl(ctx->store_ctx,x);
+		if (!i) goto err;
+		ret=i;
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(X509, X509_load_crl_file, X509_R_BAD_X509_FILETYPE);
+		goto err;
+		}
+err:
+	if (x != NULL) X509_CRL_free(x);
+	if (in != NULL) BIO_free(in);
+	return(ret);
+	}
+
+int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type)
+{
+	STACK_OF(X509_INFO) *inf;
+	X509_INFO *itmp;
+	BIO *in;
+	size_t i;
+	int count = 0;
+	if(type != X509_FILETYPE_PEM)
+		return X509_load_cert_file(ctx, file, type);
+	in = BIO_new_file(file, "r");
+	if(!in) {
+		OPENSSL_PUT_ERROR(X509, X509_load_cert_crl_file, ERR_R_SYS_LIB);
+		return 0;
+	}
+	inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
+	BIO_free(in);
+	if(!inf) {
+		OPENSSL_PUT_ERROR(X509, X509_load_cert_crl_file, ERR_R_PEM_LIB);
+		return 0;
+	}
+	for(i = 0; i < sk_X509_INFO_num(inf); i++) {
+		itmp = sk_X509_INFO_value(inf, i);
+		if(itmp->x509) {
+			X509_STORE_add_cert(ctx->store_ctx, itmp->x509);
+			count++;
+		}
+		if(itmp->crl) {
+			X509_STORE_add_crl(ctx->store_ctx, itmp->crl);
+			count++;
+		}
+	}
+	sk_X509_INFO_pop_free(inf, X509_INFO_free);
+	return count;
+}
+
+#endif /* OPENSSL_NO_STDIO */
diff --git a/crypto/x509/charmap.h b/crypto/x509/charmap.h
new file mode 100644
index 0000000..b55e638
--- /dev/null
+++ b/crypto/x509/charmap.h
@@ -0,0 +1,15 @@
+/* Auto generated with chartype.pl script.
+ * Mask of various character properties
+ */
+
+static const unsigned char char_type[] = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+120, 0, 1,40, 0, 0, 0,16,16,16, 0,25,25,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16, 9, 9,16, 9,16,
+ 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16, 0, 1, 0, 0, 0,
+ 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 2
+};
+
diff --git a/crypto/x509/i2d_pr.c b/crypto/x509/i2d_pr.c
new file mode 100644
index 0000000..8896565
--- /dev/null
+++ b/crypto/x509/i2d_pr.c
@@ -0,0 +1,83 @@
+/* crypto/asn1/i2d_pr.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "../evp/internal.h"
+
+
+int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
+	{
+	if (a->ameth && a->ameth->old_priv_encode)
+		{
+		return a->ameth->old_priv_encode(a, pp);
+		}
+	if (a->ameth && a->ameth->priv_encode) {
+		PKCS8_PRIV_KEY_INFO *p8 = EVP_PKEY2PKCS8((EVP_PKEY*)a);
+		int ret = i2d_PKCS8_PRIV_KEY_INFO(p8,pp);
+		PKCS8_PRIV_KEY_INFO_free(p8);
+		return ret;
+	}
+	OPENSSL_PUT_ERROR(X509, i2d_PrivateKey, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+	return -1;
+	}
+
diff --git a/crypto/x509/pkcs7.c b/crypto/x509/pkcs7.c
new file mode 100644
index 0000000..8bbb393
--- /dev/null
+++ b/crypto/x509/pkcs7.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2014, 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/x509.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+
+
+int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
+  /* See https://tools.ietf.org/html/rfc2315#section-9.1 */
+  CBS content_info, content_type, wrapped_signed_data, signed_data,
+      version_bytes, certificates;
+  int nid;
+  const size_t initial_certs_len = sk_X509_num(out_certs);
+
+  if (!CBS_get_asn1_ber(cbs, &content_info, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) {
+    return 0;
+  }
+
+  nid = OBJ_cbs2nid(&content_type);
+  if (nid != NID_pkcs7_signed) {
+    OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
+                      X509_R_NOT_PKCS7_SIGNED_DATA);
+    return 0;
+  }
+
+  if (!CBS_get_asn1_ber(&content_info, &wrapped_signed_data,
+                        CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+      !CBS_get_asn1_ber(&wrapped_signed_data, &signed_data,
+                        CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1_ber(&signed_data, &version_bytes, CBS_ASN1_INTEGER) ||
+      !CBS_get_asn1_ber(&signed_data, NULL /* digests */, CBS_ASN1_SET) ||
+      !CBS_get_asn1_ber(&signed_data, NULL /* content */, CBS_ASN1_SEQUENCE)) {
+    return 0;
+  }
+
+  if (CBS_len(&version_bytes) < 1 || CBS_data(&version_bytes)[0] == 0) {
+    OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
+                      X509_R_BAD_PKCS7_VERSION);
+    return 0;
+  }
+
+  if (!CBS_get_asn1_ber(&signed_data, &certificates,
+                        CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
+    OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
+                      X509_R_NO_CERTIFICATES_INCLUDED);
+    return 0;
+  }
+
+  while (CBS_len(&certificates) > 0) {
+    CBS cert;
+    X509 *x509;
+    const uint8_t *inp;
+
+    if (!CBS_get_asn1_element(&certificates, &cert, CBS_ASN1_SEQUENCE)) {
+      goto err;
+    }
+
+    inp = CBS_data(&cert);
+    x509 = d2i_X509(NULL, &inp, CBS_len(&cert));
+    if (!x509) {
+      goto err;
+    }
+
+    if (inp != CBS_data(&cert) + CBS_len(&cert)) {
+      /* This suggests a disconnect between the two ASN.1 parsers. */
+      goto err;
+    }
+
+    sk_X509_push(out_certs, x509);
+  }
+
+  return 1;
+
+err:
+  while (sk_X509_num(out_certs) != initial_certs_len) {
+    X509 *x509 = sk_X509_pop(out_certs);
+    X509_free(x509);
+  }
+
+  return 0;
+}
diff --git a/crypto/x509/t_crl.c b/crypto/x509/t_crl.c
new file mode 100644
index 0000000..f2b2ee2
--- /dev/null
+++ b/crypto/x509/t_crl.c
@@ -0,0 +1,130 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+#ifndef OPENSSL_NO_FP_API
+int X509_CRL_print_fp(FILE *fp, X509_CRL *x)
+        {
+        BIO *b;
+        int ret;
+
+        if ((b=BIO_new(BIO_s_file())) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_CRL_print_fp, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=X509_CRL_print(b, x);
+        BIO_free(b);
+        return(ret);
+        }
+#endif
+
+int X509_CRL_print(BIO *out, X509_CRL *x)
+{
+	STACK_OF(X509_REVOKED) *rev;
+	X509_REVOKED *r;
+	long l;
+	size_t i;
+	char *p;
+
+	BIO_printf(out, "Certificate Revocation List (CRL):\n");
+	l = X509_CRL_get_version(x);
+	BIO_printf(out, "%8sVersion %lu (0x%lx)\n", "", l+1, l);
+	i = OBJ_obj2nid(x->sig_alg->algorithm);
+	X509_signature_print(out, x->sig_alg, NULL);
+	p=X509_NAME_oneline(X509_CRL_get_issuer(x),NULL,0);
+	BIO_printf(out,"%8sIssuer: %s\n","",p);
+	OPENSSL_free(p);
+	BIO_printf(out,"%8sLast Update: ","");
+	ASN1_TIME_print(out,X509_CRL_get_lastUpdate(x));
+	BIO_printf(out,"\n%8sNext Update: ","");
+	if (X509_CRL_get_nextUpdate(x))
+		 ASN1_TIME_print(out,X509_CRL_get_nextUpdate(x));
+	else BIO_printf(out,"NONE");
+	BIO_printf(out,"\n");
+
+	X509V3_extensions_print(out, "CRL extensions",
+						x->crl->extensions, 0, 8);
+
+	rev = X509_CRL_get_REVOKED(x);
+
+	if(sk_X509_REVOKED_num(rev) > 0)
+	    BIO_printf(out, "Revoked Certificates:\n");
+	else BIO_printf(out, "No Revoked Certificates.\n");
+
+	for(i = 0; i < sk_X509_REVOKED_num(rev); i++) {
+		r = sk_X509_REVOKED_value(rev, i);
+		BIO_printf(out,"    Serial Number: ");
+		i2a_ASN1_INTEGER(out,r->serialNumber);
+		BIO_printf(out,"\n        Revocation Date: ");
+		ASN1_TIME_print(out,r->revocationDate);
+		BIO_printf(out,"\n");
+		X509V3_extensions_print(out, "CRL entry extensions",
+						r->extensions, 0, 8);
+	}
+	X509_signature_print(out, x->sig_alg, x->signature);
+
+	return 1;
+
+}
diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c
new file mode 100644
index 0000000..91d4ac7
--- /dev/null
+++ b/crypto/x509/t_x509.c
@@ -0,0 +1,516 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "../evp/internal.h"
+
+#ifndef OPENSSL_NO_FP_API
+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)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_print_ex_fp, ERR_R_BUF_LIB);
+                return(0);
+		}
+        BIO_set_fp(b,fp,BIO_NOCLOSE);
+        ret=X509_print_ex(b, x, nmflag, cflag);
+        BIO_free(b);
+        return(ret);
+        }
+
+int X509_print_fp(FILE *fp, X509 *x)
+	{
+	return X509_print_ex_fp(fp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+	}
+#endif
+
+int X509_print(BIO *bp, X509 *x)
+{
+	return X509_print_ex(bp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+}
+
+int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, unsigned long cflag)
+	{
+	long l;
+	int ret=0,i;
+	char *m=NULL,mlch = ' ';
+	int nmindent = 0;
+	X509_CINF *ci;
+	ASN1_INTEGER *bs;
+	EVP_PKEY *pkey=NULL;
+	const char *neg;
+
+	if((nmflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) {
+			mlch = '\n';
+			nmindent = 12;
+	}
+
+	if(nmflags == X509_FLAG_COMPAT)
+		nmindent = 16;
+
+	ci=x->cert_info;
+	if(!(cflag & X509_FLAG_NO_HEADER))
+		{
+		if (BIO_write(bp,"Certificate:\n",13) <= 0) goto err;
+		if (BIO_write(bp,"    Data:\n",10) <= 0) goto err;
+		}
+	if(!(cflag & X509_FLAG_NO_VERSION))
+		{
+		l=X509_get_version(x);
+		if (BIO_printf(bp,"%8sVersion: %lu (0x%lx)\n","",l+1,l) <= 0) goto err;
+		}
+	if(!(cflag & X509_FLAG_NO_SERIAL))
+		{
+
+		if (BIO_write(bp,"        Serial Number:",22) <= 0) goto err;
+
+		bs=X509_get_serialNumber(x);
+		if (bs->length <= (int)sizeof(long))
+			{
+			l=ASN1_INTEGER_get(bs);
+			if (bs->type == V_ASN1_NEG_INTEGER)
+				{
+				l= -l;
+				neg="-";
+				}
+			else
+				neg="";
+			if (BIO_printf(bp," %s%lu (%s0x%lx)\n",neg,l,neg,l) <= 0)
+				goto err;
+			}
+		else
+			{
+			neg=(bs->type == V_ASN1_NEG_INTEGER)?" (Negative)":"";
+			if (BIO_printf(bp,"\n%12s%s","",neg) <= 0) goto err;
+
+			for (i=0; i<bs->length; i++)
+				{
+				if (BIO_printf(bp,"%02x%c",bs->data[i],
+					((i+1 == bs->length)?'\n':':')) <= 0)
+					goto err;
+				}
+			}
+
+		}
+
+	if(!(cflag & X509_FLAG_NO_SIGNAME))
+		{
+		if(X509_signature_print(bp, x->sig_alg, NULL) <= 0)
+			goto err;
+#if 0
+		if (BIO_printf(bp,"%8sSignature Algorithm: ","") <= 0) 
+			goto err;
+		if (i2a_ASN1_OBJECT(bp, ci->signature->algorithm) <= 0)
+			goto err;
+		if (BIO_puts(bp, "\n") <= 0)
+			goto err;
+#endif
+		}
+
+	if(!(cflag & X509_FLAG_NO_ISSUER))
+		{
+		if (BIO_printf(bp,"        Issuer:%c",mlch) <= 0) goto err;
+		if (X509_NAME_print_ex(bp,X509_get_issuer_name(x),nmindent, nmflags) < 0) goto err;
+		if (BIO_write(bp,"\n",1) <= 0) goto err;
+		}
+	if(!(cflag & X509_FLAG_NO_VALIDITY))
+		{
+		if (BIO_write(bp,"        Validity\n",17) <= 0) goto err;
+		if (BIO_write(bp,"            Not Before: ",24) <= 0) goto err;
+		if (!ASN1_TIME_print(bp,X509_get_notBefore(x))) goto err;
+		if (BIO_write(bp,"\n            Not After : ",25) <= 0) goto err;
+		if (!ASN1_TIME_print(bp,X509_get_notAfter(x))) goto err;
+		if (BIO_write(bp,"\n",1) <= 0) goto err;
+		}
+	if(!(cflag & X509_FLAG_NO_SUBJECT))
+		{
+		if (BIO_printf(bp,"        Subject:%c",mlch) <= 0) goto err;
+		if (X509_NAME_print_ex(bp,X509_get_subject_name(x),nmindent, nmflags) < 0) goto err;
+		if (BIO_write(bp,"\n",1) <= 0) goto err;
+		}
+	if(!(cflag & X509_FLAG_NO_PUBKEY))
+		{
+		if (BIO_write(bp,"        Subject Public Key Info:\n",33) <= 0)
+			goto err;
+		if (BIO_printf(bp,"%12sPublic Key Algorithm: ","") <= 0)
+			goto err;
+		if (i2a_ASN1_OBJECT(bp, ci->key->algor->algorithm) <= 0)
+			goto err;
+		if (BIO_puts(bp, "\n") <= 0)
+			goto err;
+
+		pkey=X509_get_pubkey(x);
+		if (pkey == NULL)
+			{
+			BIO_printf(bp,"%12sUnable to load Public Key\n","");
+			BIO_print_errors(bp);
+			}
+		else
+			{
+			EVP_PKEY_print_public(bp, pkey, 16, NULL);
+			EVP_PKEY_free(pkey);
+			}
+		}
+
+	if(!(cflag & X509_FLAG_NO_IDS))
+		{
+		if (ci->issuerUID)
+			{
+			if (BIO_printf(bp,"%8sIssuer Unique ID: ","") <= 0) 
+				goto err;
+			if (!X509_signature_dump(bp, ci->issuerUID, 12))
+				goto err;
+			}
+		if (ci->subjectUID)
+			{
+			if (BIO_printf(bp,"%8sSubject Unique ID: ","") <= 0) 
+				goto err;
+			if (!X509_signature_dump(bp, ci->subjectUID, 12))
+				goto err;
+			}
+		}
+
+	if (!(cflag & X509_FLAG_NO_EXTENSIONS))
+		X509V3_extensions_print(bp, "X509v3 extensions",
+					ci->extensions, cflag, 8);
+
+	if(!(cflag & X509_FLAG_NO_SIGDUMP))
+		{
+		if(X509_signature_print(bp, x->sig_alg, x->signature) <= 0) goto err;
+		}
+	if(!(cflag & X509_FLAG_NO_AUX))
+		{
+		if (!X509_CERT_AUX_print(bp, x->aux, 0)) goto err;
+		}
+	ret=1;
+err:
+	if (m != NULL) OPENSSL_free(m);
+	return(ret);
+	}
+
+int X509_ocspid_print (BIO *bp, X509 *x)
+	{
+	unsigned char *der=NULL ;
+	unsigned char *dertmp;
+	int derlen;
+	int i;
+	unsigned char SHA1md[SHA_DIGEST_LENGTH];
+
+	/* display the hash of the subject as it would appear
+	   in OCSP requests */
+	if (BIO_printf(bp,"        Subject OCSP hash: ") <= 0)
+		goto err;
+	derlen = i2d_X509_NAME(x->cert_info->subject, NULL);
+	if ((der = dertmp = (unsigned char *)OPENSSL_malloc (derlen)) == NULL)
+		goto err;
+	i2d_X509_NAME(x->cert_info->subject, &dertmp);
+
+	if (!EVP_Digest(der, derlen, SHA1md, NULL, EVP_sha1(), NULL))
+		goto err;
+	for (i=0; i < SHA_DIGEST_LENGTH; i++)
+		{
+		if (BIO_printf(bp,"%02X",SHA1md[i]) <= 0) goto err;
+		}
+	OPENSSL_free (der);
+	der=NULL;
+
+	/* display the hash of the public key as it would appear
+	   in OCSP requests */
+	if (BIO_printf(bp,"\n        Public key OCSP hash: ") <= 0)
+		goto err;
+
+	if (!EVP_Digest(x->cert_info->key->public_key->data,
+			x->cert_info->key->public_key->length,
+			SHA1md, NULL, EVP_sha1(), NULL))
+		goto err;
+	for (i=0; i < SHA_DIGEST_LENGTH; i++)
+		{
+		if (BIO_printf(bp,"%02X",SHA1md[i]) <= 0)
+			goto err;
+		}
+	BIO_printf(bp,"\n");
+
+	return (1);
+err:
+	if (der != NULL) OPENSSL_free(der);
+	return(0);
+	}
+
+int X509_signature_print(BIO *bp, X509_ALGOR *sigalg, ASN1_STRING *sig)
+{
+	int sig_nid;
+	if (BIO_puts(bp,"    Signature Algorithm: ") <= 0) return 0;
+	if (i2a_ASN1_OBJECT(bp, sigalg->algorithm) <= 0) return 0;
+
+	sig_nid = OBJ_obj2nid(sigalg->algorithm);
+	if (sig_nid != NID_undef)
+		{
+		int pkey_nid, dig_nid;
+		const EVP_PKEY_ASN1_METHOD *ameth;
+		if (OBJ_find_sigid_algs(sig_nid, &dig_nid, &pkey_nid))
+			{
+			ameth = EVP_PKEY_asn1_find(NULL, pkey_nid);
+			if (ameth && ameth->sig_print)
+				return ameth->sig_print(bp, sigalg, sig, 9, 0);
+			}
+		}
+	if (sig)
+		return X509_signature_dump(bp, sig, 9);
+	else if (BIO_puts(bp, "\n") <= 0)
+		return 0;
+	return 1;
+}
+
+int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
+	{
+	int i,n;
+	char buf[80];
+	const char *p;
+
+	if (v == NULL) return(0);
+	n=0;
+	p=(const char *)v->data;
+	for (i=0; i<v->length; i++)
+		{
+		if ((p[i] > '~') || ((p[i] < ' ') &&
+			(p[i] != '\n') && (p[i] != '\r')))
+			buf[n]='.';
+		else
+			buf[n]=p[i];
+		n++;
+		if (n >= 80)
+			{
+			if (BIO_write(bp,buf,n) <= 0)
+				return(0);
+			n=0;
+			}
+		}
+	if (n > 0)
+		if (BIO_write(bp,buf,n) <= 0)
+			return(0);
+	return(1);
+	}
+
+int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
+{
+	if(tm->type == V_ASN1_UTCTIME) return ASN1_UTCTIME_print(bp, tm);
+	if(tm->type == V_ASN1_GENERALIZEDTIME)
+				return ASN1_GENERALIZEDTIME_print(bp, tm);
+	BIO_write(bp,"Bad time value",14);
+	return(0);
+}
+
+static const char *mon[12]=
+    {
+    "Jan","Feb","Mar","Apr","May","Jun",
+    "Jul","Aug","Sep","Oct","Nov","Dec"
+    };
+
+int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
+	{
+	char *v;
+	int gmt=0;
+	int i;
+	int y=0,M=0,d=0,h=0,m=0,s=0;
+	char *f = NULL;
+	int f_len = 0;
+
+	i=tm->length;
+	v=(char *)tm->data;
+
+	if (i < 12) goto err;
+	if (v[i-1] == 'Z') gmt=1;
+	for (i=0; i<12; i++)
+		if ((v[i] > '9') || (v[i] < '0')) goto err;
+	y= (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0');
+	M= (v[4]-'0')*10+(v[5]-'0');
+	if ((M > 12) || (M < 1)) goto err;
+	d= (v[6]-'0')*10+(v[7]-'0');
+	h= (v[8]-'0')*10+(v[9]-'0');
+	m=  (v[10]-'0')*10+(v[11]-'0');
+	if (tm->length >= 14 &&
+	    (v[12] >= '0') && (v[12] <= '9') &&
+	    (v[13] >= '0') && (v[13] <= '9'))
+		{
+		s=  (v[12]-'0')*10+(v[13]-'0');
+		/* Check for fractions of seconds. */
+		if (tm->length >= 15 && v[14] == '.')
+			{
+			int l = tm->length;
+			f = &v[14];	/* The decimal point. */
+			f_len = 1;
+			while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
+				++f_len;
+			}
+		}
+
+	if (BIO_printf(bp,"%s %2d %02d:%02d:%02d%.*s %d%s",
+		mon[M-1],d,h,m,s,f_len,f,y,(gmt)?" GMT":"") <= 0)
+		return(0);
+	else
+		return(1);
+err:
+	BIO_write(bp,"Bad time value",14);
+	return(0);
+	}
+
+int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm)
+	{
+	const char *v;
+	int gmt=0;
+	int i;
+	int y=0,M=0,d=0,h=0,m=0,s=0;
+
+	i=tm->length;
+	v=(const char *)tm->data;
+
+	if (i < 10) goto err;
+	if (v[i-1] == 'Z') gmt=1;
+	for (i=0; i<10; i++)
+		if ((v[i] > '9') || (v[i] < '0')) goto err;
+	y= (v[0]-'0')*10+(v[1]-'0');
+	if (y < 50) y+=100;
+	M= (v[2]-'0')*10+(v[3]-'0');
+	if ((M > 12) || (M < 1)) goto err;
+	d= (v[4]-'0')*10+(v[5]-'0');
+	h= (v[6]-'0')*10+(v[7]-'0');
+	m=  (v[8]-'0')*10+(v[9]-'0');
+	if (tm->length >=12 &&
+	    (v[10] >= '0') && (v[10] <= '9') &&
+	    (v[11] >= '0') && (v[11] <= '9'))
+		s=  (v[10]-'0')*10+(v[11]-'0');
+
+	if (BIO_printf(bp,"%s %2d %02d:%02d:%02d %d%s",
+		mon[M-1],d,h,m,s,y+1900,(gmt)?" GMT":"") <= 0)
+		return(0);
+	else
+		return(1);
+err:
+	BIO_write(bp,"Bad time value",14);
+	return(0);
+	}
+
+int X509_NAME_print(BIO *bp, X509_NAME *name, int obase)
+	{
+	char *s,*c,*b;
+	int ret=0,l,i;
+
+	l=80-2-obase;
+
+	b=X509_NAME_oneline(name,NULL,0);
+	if (!*b)
+		{
+		OPENSSL_free(b);
+		return 1;
+		}
+	s=b+1; /* skip the first slash */
+
+	c=s;
+	for (;;)
+		{
+#ifndef CHARSET_EBCDIC
+		if (	((*s == '/') &&
+				((s[1] >= 'A') && (s[1] <= 'Z') && (
+					(s[2] == '=') ||
+					((s[2] >= 'A') && (s[2] <= 'Z') &&
+					(s[3] == '='))
+				 ))) ||
+			(*s == '\0'))
+#else
+		if (	((*s == '/') &&
+				(isupper(s[1]) && (
+					(s[2] == '=') ||
+					(isupper(s[2]) &&
+					(s[3] == '='))
+				 ))) ||
+			(*s == '\0'))
+#endif
+			{
+			i=s-c;
+			if (BIO_write(bp,c,i) != i) goto err;
+			c=s+1;	/* skip following slash */
+			if (*s != '\0')
+				{
+				if (BIO_write(bp,", ",2) != 2) goto err;
+				}
+			l--;
+			}
+		if (*s == '\0') break;
+		s++;
+		l--;
+		}
+	
+	ret=1;
+	if (0)
+		{
+err:
+		OPENSSL_PUT_ERROR(X509, X509_NAME_print, ERR_R_BUF_LIB);
+		}
+	OPENSSL_free(b);
+	return(ret);
+	}
diff --git a/crypto/x509/t_x509a.c b/crypto/x509/t_x509a.c
new file mode 100644
index 0000000..7667268
--- /dev/null
+++ b/crypto/x509/t_x509a.c
@@ -0,0 +1,109 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+/* X509_CERT_AUX and string set routines */
+
+int X509_CERT_AUX_print(BIO *out, X509_CERT_AUX *aux, int indent)
+{
+	char oidstr[80], first;
+	size_t i;
+	int j;
+	if(!aux) return 1;
+	if(aux->trust) {
+		first = 1;
+		BIO_printf(out, "%*sTrusted Uses:\n%*s",
+						indent, "", indent + 2, "");
+		for(i = 0; i < sk_ASN1_OBJECT_num(aux->trust); i++) {
+			if(!first) BIO_puts(out, ", ");
+			else first = 0;
+			OBJ_obj2txt(oidstr, sizeof oidstr,
+				sk_ASN1_OBJECT_value(aux->trust, i), 0);
+			BIO_puts(out, oidstr);
+		}
+		BIO_puts(out, "\n");
+	} else BIO_printf(out, "%*sNo Trusted Uses.\n", indent, "");
+	if(aux->reject) {
+		first = 1;
+		BIO_printf(out, "%*sRejected Uses:\n%*s",
+						indent, "", indent + 2, "");
+		for(i = 0; i < sk_ASN1_OBJECT_num(aux->reject); i++) {
+			if(!first) BIO_puts(out, ", ");
+			else first = 0;
+			OBJ_obj2txt(oidstr, sizeof oidstr,
+				sk_ASN1_OBJECT_value(aux->reject, i), 0);
+			BIO_puts(out, oidstr);
+		}
+		BIO_puts(out, "\n");
+	} else BIO_printf(out, "%*sNo Rejected Uses.\n", indent, "");
+	if(aux->alias) BIO_printf(out, "%*sAlias: %s\n", indent, "",
+							aux->alias->data);
+	if(aux->keyid) {
+		BIO_printf(out, "%*sKey Id: ", indent, "");
+		for(j = 0; j < aux->keyid->length; j++)
+			BIO_printf(out, "%s%02X",
+				j ? ":" : "",
+				aux->keyid->data[j]);
+		BIO_write(out,"\n",1);
+	}
+	return 1;
+}
diff --git a/crypto/x509/vpm_int.h b/crypto/x509/vpm_int.h
new file mode 100644
index 0000000..af99821
--- /dev/null
+++ b/crypto/x509/vpm_int.h
@@ -0,0 +1,69 @@
+/* vpm_int.h */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2013.
+ */
+/* ====================================================================
+ * Copyright (c) 2013 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/* internal only structure to hold additional X509_VERIFY_PARAM data */
+
+struct X509_VERIFY_PARAM_ID_st
+	{
+	unsigned char *host;	/* If not NULL hostname to match */
+	size_t hostlen;
+	unsigned char *email;	/* If not NULL email address to match */
+	size_t emaillen;
+	unsigned char *ip;	/* If not NULL IP address to match */
+	size_t iplen;		/* Length of IP address */
+	};
diff --git a/crypto/x509/x509.c b/crypto/x509/x509.c
new file mode 100644
index 0000000..31f9e1e
--- /dev/null
+++ b/crypto/x509/x509.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <openssl/bio.h>
+#include <openssl/mem.h>
+
+
+int PKCS8_pkey_set0(PKCS8_PRIV_KEY_INFO *priv, ASN1_OBJECT *aobj, int version,
+                    int ptype, void *pval, uint8_t *penc, int penclen) {
+  uint8_t **ppenc = NULL;
+  if (version >= 0) {
+    if (!ASN1_INTEGER_set(priv->version, version)) {
+      return 0;
+    }
+  }
+
+  if (penc) {
+    int pmtype;
+    ASN1_OCTET_STRING *oct;
+
+    oct = ASN1_OCTET_STRING_new();
+    if (!oct) {
+      return 0;
+    }
+    oct->data = penc;
+    ppenc = &oct->data;
+    oct->length = penclen;
+    if (priv->broken == PKCS8_NO_OCTET) {
+      pmtype = V_ASN1_SEQUENCE;
+    } else {
+      pmtype = V_ASN1_OCTET_STRING;
+    }
+    ASN1_TYPE_set(priv->pkey, pmtype, oct);
+  }
+
+  if (!X509_ALGOR_set0(priv->pkeyalg, aobj, ptype, pval)) {
+    /* If call fails do not swallow 'enc' */
+    if (ppenc) {
+      *ppenc = NULL;
+    }
+    return 0;
+  }
+
+  return 1;
+}
+
+int PKCS8_pkey_get0(ASN1_OBJECT **ppkalg, const uint8_t **pk, int *ppklen,
+                    X509_ALGOR **pa, PKCS8_PRIV_KEY_INFO *p8) {
+  if (ppkalg) {
+    *ppkalg = p8->pkeyalg->algorithm;
+  }
+
+  if (p8->pkey->type == V_ASN1_OCTET_STRING) {
+    p8->broken = PKCS8_OK;
+    if (pk) {
+      *pk = p8->pkey->value.octet_string->data;
+      *ppklen = p8->pkey->value.octet_string->length;
+    }
+  } else if (p8->pkey->type == V_ASN1_SEQUENCE) {
+    p8->broken = PKCS8_NO_OCTET;
+    if (pk) {
+      *pk = p8->pkey->value.sequence->data;
+      *ppklen = p8->pkey->value.sequence->length;
+    }
+  } else {
+    return 0;
+  }
+
+  if (pa) {
+    *pa = p8->pkeyalg;
+  }
+  return 1;
+}
+
+int X509_signature_dump(BIO *bp, const ASN1_STRING *sig, int indent) {
+  const uint8_t *s;
+  int i, n;
+
+  n = sig->length;
+  s = sig->data;
+  for (i = 0; i < n; i++) {
+    if ((i % 18) == 0) {
+      if (BIO_write(bp, "\n", 1) <= 0 ||
+          BIO_indent(bp, indent, indent) <= 0) {
+        return 0;
+      }
+    }
+    if (BIO_printf(bp, "%02x%s", s[i], ((i + 1) == n) ? "" : ":") <= 0) {
+      return 0;
+    }
+  }
+  if (BIO_write(bp, "\n", 1) != 1) {
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h
new file mode 100644
index 0000000..c4e742e
--- /dev/null
+++ b/crypto/x509/x509.h
@@ -0,0 +1,1332 @@
+/* crypto/x509/x509.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECDH support in OpenSSL originally developed by 
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#ifndef HEADER_X509_H
+#define HEADER_X509_H
+
+#include <openssl/base.h>
+
+#include <time.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/cipher.h>
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <openssl/stack.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef OPENSSL_SYS_WIN32
+/* Under Win32 these are defined in wincrypt.h */
+#undef X509_NAME
+#undef X509_CERT_PAIR
+#undef X509_EXTENSIONS
+#endif
+
+#define X509_FILETYPE_PEM	1
+#define X509_FILETYPE_ASN1	2
+#define X509_FILETYPE_DEFAULT	3
+
+#define X509v3_KU_DIGITAL_SIGNATURE	0x0080
+#define X509v3_KU_NON_REPUDIATION	0x0040
+#define X509v3_KU_KEY_ENCIPHERMENT	0x0020
+#define X509v3_KU_DATA_ENCIPHERMENT	0x0010
+#define X509v3_KU_KEY_AGREEMENT		0x0008
+#define X509v3_KU_KEY_CERT_SIGN		0x0004
+#define X509v3_KU_CRL_SIGN		0x0002
+#define X509v3_KU_ENCIPHER_ONLY		0x0001
+#define X509v3_KU_DECIPHER_ONLY		0x8000
+#define X509v3_KU_UNDEF			0xffff
+
+typedef struct X509_objects_st
+	{
+	int nid;
+	int (*a2i)(void);
+	int (*i2a)(void);
+	} X509_OBJECTS;
+
+DECLARE_ASN1_SET_OF(X509_ALGOR)
+
+typedef STACK_OF(X509_ALGOR) X509_ALGORS;
+
+typedef struct X509_val_st
+	{
+	ASN1_TIME *notBefore;
+	ASN1_TIME *notAfter;
+	} X509_VAL;
+
+struct X509_pubkey_st
+	{
+	X509_ALGOR *algor;
+	ASN1_BIT_STRING *public_key;
+	EVP_PKEY *pkey;
+	};
+
+typedef struct X509_sig_st
+	{
+	X509_ALGOR *algor;
+	ASN1_OCTET_STRING *digest;
+	} X509_SIG;
+
+typedef struct X509_name_entry_st
+	{
+	ASN1_OBJECT *object;
+	ASN1_STRING *value;
+	int set;
+	int size; 	/* temp variable */
+	} X509_NAME_ENTRY;
+
+DECLARE_STACK_OF(X509_NAME_ENTRY)
+DECLARE_ASN1_SET_OF(X509_NAME_ENTRY)
+
+/* we always keep X509_NAMEs in 2 forms. */
+struct X509_name_st
+	{
+	STACK_OF(X509_NAME_ENTRY) *entries;
+	int modified;	/* true if 'bytes' needs to be built */
+#ifndef OPENSSL_NO_BUFFER
+	BUF_MEM *bytes;
+#else
+	char *bytes;
+#endif
+/*	unsigned long hash; Keep the hash around for lookups */
+	unsigned char *canon_enc;
+	int canon_enclen;
+	} /* X509_NAME */;
+
+DECLARE_STACK_OF(X509_NAME)
+
+#define X509_EX_V_NETSCAPE_HACK		0x8000
+#define X509_EX_V_INIT			0x0001
+typedef struct X509_extension_st
+	{
+	ASN1_OBJECT *object;
+	ASN1_BOOLEAN critical;
+	ASN1_OCTET_STRING *value;
+	} X509_EXTENSION;
+
+typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
+
+DECLARE_STACK_OF(X509_EXTENSION)
+DECLARE_ASN1_SET_OF(X509_EXTENSION)
+
+/* a sequence of these are used */
+typedef struct x509_attributes_st
+	{
+	ASN1_OBJECT *object;
+	int single; /* 0 for a set, 1 for a single item (which is wrong) */
+	union	{
+		char		*ptr;
+/* 0 */		STACK_OF(ASN1_TYPE) *set;
+/* 1 */		ASN1_TYPE	*single;
+		} value;
+	} X509_ATTRIBUTE;
+
+DECLARE_STACK_OF(X509_ATTRIBUTE)
+DECLARE_ASN1_SET_OF(X509_ATTRIBUTE)
+
+
+typedef struct X509_req_info_st
+	{
+	ASN1_ENCODING enc;
+	ASN1_INTEGER *version;
+	X509_NAME *subject;
+	X509_PUBKEY *pubkey;
+	/*  d=2 hl=2 l=  0 cons: cont: 00 */
+	STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
+	} X509_REQ_INFO;
+
+typedef struct X509_req_st
+	{
+	X509_REQ_INFO *req_info;
+	X509_ALGOR *sig_alg;
+	ASN1_BIT_STRING *signature;
+	int references;
+	} X509_REQ;
+
+typedef struct x509_cinf_st
+	{
+	ASN1_INTEGER *version;		/* [ 0 ] default of v1 */
+	ASN1_INTEGER *serialNumber;
+	X509_ALGOR *signature;
+	X509_NAME *issuer;
+	X509_VAL *validity;
+	X509_NAME *subject;
+	X509_PUBKEY *key;
+	ASN1_BIT_STRING *issuerUID;		/* [ 1 ] optional in v2 */
+	ASN1_BIT_STRING *subjectUID;		/* [ 2 ] optional in v2 */
+	STACK_OF(X509_EXTENSION) *extensions;	/* [ 3 ] optional in v3 */
+	ASN1_ENCODING enc;
+	} X509_CINF;
+
+/* This stuff is certificate "auxiliary info"
+ * it contains details which are useful in certificate
+ * stores and databases. When used this is tagged onto
+ * the end of the certificate itself
+ */
+
+typedef struct x509_cert_aux_st
+	{
+	STACK_OF(ASN1_OBJECT) *trust;		/* trusted uses */
+	STACK_OF(ASN1_OBJECT) *reject;		/* rejected uses */
+	ASN1_UTF8STRING *alias;			/* "friendly name" */
+	ASN1_OCTET_STRING *keyid;		/* key id of private key */
+	STACK_OF(X509_ALGOR) *other;		/* other unspecified info */
+	} X509_CERT_AUX;
+
+struct x509_st
+	{
+	X509_CINF *cert_info;
+	X509_ALGOR *sig_alg;
+	ASN1_BIT_STRING *signature;
+	int valid;
+	int references;
+	char *name;
+	CRYPTO_EX_DATA ex_data;
+	/* These contain copies of various extension values */
+	long ex_pathlen;
+	long ex_pcpathlen;
+	unsigned long ex_flags;
+	unsigned long ex_kusage;
+	unsigned long ex_xkusage;
+	unsigned long ex_nscert;
+	ASN1_OCTET_STRING *skid;
+	AUTHORITY_KEYID *akid;
+	X509_POLICY_CACHE *policy_cache;
+	STACK_OF(DIST_POINT) *crldp;
+	STACK_OF(GENERAL_NAME) *altname;
+	NAME_CONSTRAINTS *nc;
+#ifndef OPENSSL_NO_SHA
+	unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+#endif
+	X509_CERT_AUX *aux;
+	} /* X509 */;
+
+DECLARE_STACK_OF(X509)
+DECLARE_ASN1_SET_OF(X509)
+
+/* This is used for a table of trust checking functions */
+
+typedef struct x509_trust_st {
+	int trust;
+	int flags;
+	int (*check_trust)(struct x509_trust_st *, X509 *, int);
+	char *name;
+	int arg1;
+	void *arg2;
+} X509_TRUST;
+
+DECLARE_STACK_OF(X509_TRUST)
+
+typedef struct x509_cert_pair_st {
+	X509 *forward;
+	X509 *reverse;
+} X509_CERT_PAIR;
+
+/* standard trust ids */
+
+#define X509_TRUST_DEFAULT	-1	/* Only valid in purpose settings */
+
+#define X509_TRUST_COMPAT	1
+#define X509_TRUST_SSL_CLIENT	2
+#define X509_TRUST_SSL_SERVER	3
+#define X509_TRUST_EMAIL	4
+#define X509_TRUST_OBJECT_SIGN	5
+#define X509_TRUST_OCSP_SIGN	6
+#define X509_TRUST_OCSP_REQUEST	7
+#define X509_TRUST_TSA		8
+
+/* Keep these up to date! */
+#define X509_TRUST_MIN		1
+#define X509_TRUST_MAX		8
+
+
+/* trust_flags values */
+#define	X509_TRUST_DYNAMIC 	1
+#define	X509_TRUST_DYNAMIC_NAME	2
+
+/* check_trust return codes */
+
+#define X509_TRUST_TRUSTED	1
+#define X509_TRUST_REJECTED	2
+#define X509_TRUST_UNTRUSTED	3
+
+/* Flags for X509_print_ex() */
+
+#define	X509_FLAG_COMPAT		0
+#define	X509_FLAG_NO_HEADER		1L
+#define	X509_FLAG_NO_VERSION		(1L << 1)
+#define	X509_FLAG_NO_SERIAL		(1L << 2)
+#define	X509_FLAG_NO_SIGNAME		(1L << 3)
+#define	X509_FLAG_NO_ISSUER		(1L << 4)
+#define	X509_FLAG_NO_VALIDITY		(1L << 5)
+#define	X509_FLAG_NO_SUBJECT		(1L << 6)
+#define	X509_FLAG_NO_PUBKEY		(1L << 7)
+#define	X509_FLAG_NO_EXTENSIONS		(1L << 8)
+#define	X509_FLAG_NO_SIGDUMP		(1L << 9)
+#define	X509_FLAG_NO_AUX		(1L << 10)
+#define	X509_FLAG_NO_ATTRIBUTES		(1L << 11)
+#define	X509_FLAG_NO_IDS		(1L << 12)
+
+/* Flags specific to X509_NAME_print_ex() */	
+
+/* The field separator information */
+
+#define XN_FLAG_SEP_MASK	(0xf << 16)
+
+#define XN_FLAG_COMPAT		0		/* Traditional SSLeay: use old X509_NAME_print */
+#define XN_FLAG_SEP_COMMA_PLUS	(1 << 16)	/* RFC2253 ,+ */
+#define XN_FLAG_SEP_CPLUS_SPC	(2 << 16)	/* ,+ spaced: more readable */
+#define XN_FLAG_SEP_SPLUS_SPC	(3 << 16)	/* ;+ spaced */
+#define XN_FLAG_SEP_MULTILINE	(4 << 16)	/* One line per field */
+
+#define XN_FLAG_DN_REV		(1 << 20)	/* Reverse DN order */
+
+/* How the field name is shown */
+
+#define XN_FLAG_FN_MASK		(0x3 << 21)
+
+#define XN_FLAG_FN_SN		0		/* Object short name */
+#define XN_FLAG_FN_LN		(1 << 21)	/* Object long name */
+#define XN_FLAG_FN_OID		(2 << 21)	/* Always use OIDs */
+#define XN_FLAG_FN_NONE		(3 << 21)	/* No field names */
+
+#define XN_FLAG_SPC_EQ		(1 << 23)	/* Put spaces round '=' */
+
+/* This determines if we dump fields we don't recognise:
+ * RFC2253 requires this.
+ */
+
+#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24)
+
+#define XN_FLAG_FN_ALIGN	(1 << 25)	/* Align field names to 20 characters */
+
+/* Complete set of RFC2253 flags */
+
+#define XN_FLAG_RFC2253 (ASN1_STRFLGS_RFC2253 | \
+			XN_FLAG_SEP_COMMA_PLUS | \
+			XN_FLAG_DN_REV | \
+			XN_FLAG_FN_SN | \
+			XN_FLAG_DUMP_UNKNOWN_FIELDS)
+
+/* readable oneline form */
+
+#define XN_FLAG_ONELINE (ASN1_STRFLGS_RFC2253 | \
+			ASN1_STRFLGS_ESC_QUOTE | \
+			XN_FLAG_SEP_CPLUS_SPC | \
+			XN_FLAG_SPC_EQ | \
+			XN_FLAG_FN_SN)
+
+/* readable multiline form */
+
+#define XN_FLAG_MULTILINE (ASN1_STRFLGS_ESC_CTRL | \
+			ASN1_STRFLGS_ESC_MSB | \
+			XN_FLAG_SEP_MULTILINE | \
+			XN_FLAG_SPC_EQ | \
+			XN_FLAG_FN_LN | \
+			XN_FLAG_FN_ALIGN)
+
+struct x509_revoked_st
+	{
+	ASN1_INTEGER *serialNumber;
+	ASN1_TIME *revocationDate;
+	STACK_OF(X509_EXTENSION) /* optional */ *extensions;
+	/* Set up if indirect CRL */
+	STACK_OF(GENERAL_NAME) *issuer;
+	/* Revocation reason */
+	int reason;
+	int sequence; /* load sequence */
+	};
+
+DECLARE_STACK_OF(X509_REVOKED)
+DECLARE_ASN1_SET_OF(X509_REVOKED)
+
+typedef struct X509_crl_info_st
+	{
+	ASN1_INTEGER *version;
+	X509_ALGOR *sig_alg;
+	X509_NAME *issuer;
+	ASN1_TIME *lastUpdate;
+	ASN1_TIME *nextUpdate;
+	STACK_OF(X509_REVOKED) *revoked;
+	STACK_OF(X509_EXTENSION) /* [0] */ *extensions;
+	ASN1_ENCODING enc;
+	} X509_CRL_INFO;
+
+struct X509_crl_st
+	{
+	/* actual signature */
+	X509_CRL_INFO *crl;
+	X509_ALGOR *sig_alg;
+	ASN1_BIT_STRING *signature;
+	int references;
+	int flags;
+	/* Copies of various extensions */
+	AUTHORITY_KEYID *akid;
+	ISSUING_DIST_POINT *idp;
+	/* Convenient breakdown of IDP */
+	int idp_flags;
+	int idp_reasons;
+	/* CRL and base CRL numbers for delta processing */
+	ASN1_INTEGER *crl_number;
+	ASN1_INTEGER *base_crl_number;
+#ifndef OPENSSL_NO_SHA
+	unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+#endif
+	STACK_OF(GENERAL_NAMES) *issuers;
+	const X509_CRL_METHOD *meth;
+	void *meth_data;
+	} /* X509_CRL */;
+
+DECLARE_STACK_OF(X509_CRL)
+DECLARE_ASN1_SET_OF(X509_CRL)
+
+typedef struct private_key_st
+	{
+	int version;
+	/* The PKCS#8 data types */
+	X509_ALGOR *enc_algor;
+	ASN1_OCTET_STRING *enc_pkey;	/* encrypted pub key */
+
+	/* When decrypted, the following will not be NULL */
+	EVP_PKEY *dec_pkey;
+
+	/* used to encrypt and decrypt */
+	int key_length;
+	char *key_data;
+	int key_free;	/* true if we should auto free key_data */
+
+	/* expanded version of 'enc_algor' */
+	EVP_CIPHER_INFO cipher;
+
+	int references;
+	} X509_PKEY;
+
+#ifndef OPENSSL_NO_EVP
+typedef struct X509_info_st
+	{
+	X509 *x509;
+	X509_CRL *crl;
+	X509_PKEY *x_pkey;
+
+	EVP_CIPHER_INFO enc_cipher;
+	int enc_len;
+	char *enc_data;
+
+	int references;
+	} X509_INFO;
+
+DECLARE_STACK_OF(X509_INFO)
+#endif
+
+/* The next 2 structures and their 8 routines were sent to me by
+ * Pat Richard <patr@x509.com> and are used to manipulate
+ * Netscapes spki structures - useful if you are writing a CA web page
+ */
+typedef struct Netscape_spkac_st
+	{
+	X509_PUBKEY *pubkey;
+	ASN1_IA5STRING *challenge;	/* challenge sent in atlas >= PR2 */
+	} NETSCAPE_SPKAC;
+
+typedef struct Netscape_spki_st
+	{
+	NETSCAPE_SPKAC *spkac;	/* signed public key and challenge */
+	X509_ALGOR *sig_algor;
+	ASN1_BIT_STRING *signature;
+	} NETSCAPE_SPKI;
+
+/* Netscape certificate sequence structure */
+typedef struct Netscape_certificate_sequence
+	{
+	ASN1_OBJECT *type;
+	STACK_OF(X509) *certs;
+	} NETSCAPE_CERT_SEQUENCE;
+
+/* Unused (and iv length is wrong)
+typedef struct CBCParameter_st
+	{
+	unsigned char iv[8];
+	} CBC_PARAM;
+*/
+
+/* Password based encryption structure */
+
+typedef struct PBEPARAM_st {
+ASN1_OCTET_STRING *salt;
+ASN1_INTEGER *iter;
+} PBEPARAM;
+
+/* Password based encryption V2 structures */
+
+typedef struct PBE2PARAM_st {
+X509_ALGOR *keyfunc;
+X509_ALGOR *encryption;
+} PBE2PARAM;
+
+typedef struct PBKDF2PARAM_st {
+ASN1_TYPE *salt;	/* Usually OCTET STRING but could be anything */
+ASN1_INTEGER *iter;
+ASN1_INTEGER *keylength;
+X509_ALGOR *prf;
+} PBKDF2PARAM;
+
+
+/* PKCS#8 private key info structure */
+
+struct pkcs8_priv_key_info_st
+        {
+        int broken;     /* Flag for various broken formats */
+#define PKCS8_OK		0
+#define PKCS8_NO_OCTET		1
+#define PKCS8_EMBEDDED_PARAM	2
+#define PKCS8_NS_DB		3
+#define PKCS8_NEG_PRIVKEY	4
+        ASN1_INTEGER *version;
+        X509_ALGOR *pkeyalg;
+        ASN1_TYPE *pkey; /* Should be OCTET STRING but some are broken */
+        STACK_OF(X509_ATTRIBUTE) *attributes;
+        };
+
+#ifdef  __cplusplus
+}
+#endif
+
+#include <openssl/x509_vfy.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#define X509_EXT_PACK_UNKNOWN	1
+#define X509_EXT_PACK_STRING	2
+
+#define		X509_get_version(x) ASN1_INTEGER_get((x)->cert_info->version)
+/* #define	X509_get_serialNumber(x) ((x)->cert_info->serialNumber) */
+#define		X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
+#define		X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
+#define		X509_extract_key(x)	X509_get_pubkey(x) /*****/
+#define		X509_REQ_get_version(x) ASN1_INTEGER_get((x)->req_info->version)
+#define		X509_REQ_get_subject_name(x) ((x)->req_info->subject)
+#define		X509_REQ_extract_key(a)	X509_REQ_get_pubkey(a)
+#define		X509_name_cmp(a,b)	X509_NAME_cmp((a),(b))
+#define		X509_get_signature_type(x) EVP_PKEY_type(OBJ_obj2nid((x)->sig_alg->algorithm))
+
+#define		X509_CRL_get_version(x) ASN1_INTEGER_get((x)->crl->version)
+#define 	X509_CRL_get_lastUpdate(x) ((x)->crl->lastUpdate)
+#define 	X509_CRL_get_nextUpdate(x) ((x)->crl->nextUpdate)
+#define		X509_CRL_get_issuer(x) ((x)->crl->issuer)
+#define		X509_CRL_get_REVOKED(x) ((x)->crl->revoked)
+
+void X509_CRL_set_default_method(const X509_CRL_METHOD *meth);
+X509_CRL_METHOD *X509_CRL_METHOD_new(
+	int (*crl_init)(X509_CRL *crl),
+	int (*crl_free)(X509_CRL *crl),
+	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
+				ASN1_INTEGER *ser, X509_NAME *issuer),
+	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk));
+void X509_CRL_METHOD_free(X509_CRL_METHOD *m);
+
+void X509_CRL_set_meth_data(X509_CRL *crl, void *dat);
+void *X509_CRL_get_meth_data(X509_CRL *crl);
+
+/* This one is only used so that a binary form can output, as in
+ * i2d_X509_NAME(X509_get_X509_PUBKEY(x),&buf) */
+#define 	X509_get_X509_PUBKEY(x) ((x)->cert_info->key)
+
+
+const char *X509_verify_cert_error_string(long n);
+
+#ifndef OPENSSL_NO_EVP
+int X509_verify(X509 *a, EVP_PKEY *r);
+
+int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r);
+int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r);
+int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r);
+
+NETSCAPE_SPKI * NETSCAPE_SPKI_b64_decode(const char *str, int len);
+char * NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *x);
+EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *x);
+int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *x, EVP_PKEY *pkey);
+
+int NETSCAPE_SPKI_print(BIO *out, NETSCAPE_SPKI *spki);
+
+int X509_signature_dump(BIO *bp,const ASN1_STRING *sig, int indent);
+int X509_signature_print(BIO *bp,X509_ALGOR *alg, ASN1_STRING *sig);
+
+int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md);
+int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx);
+/* int X509_http_nbio(OCSP_REQ_CTX *rctx, X509 **pcert); */
+int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md);
+int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx);
+int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md);
+int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx);
+/* int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl); */
+int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md);
+
+int X509_pubkey_digest(const X509 *data,const EVP_MD *type,
+		unsigned char *md, unsigned int *len);
+int X509_digest(const X509 *data,const EVP_MD *type,
+		unsigned char *md, unsigned int *len);
+int X509_CRL_digest(const X509_CRL *data,const EVP_MD *type,
+		unsigned char *md, unsigned int *len);
+int X509_REQ_digest(const X509_REQ *data,const EVP_MD *type,
+		unsigned char *md, unsigned int *len);
+int X509_NAME_digest(const X509_NAME *data,const EVP_MD *type,
+		unsigned char *md, unsigned int *len);
+#endif
+
+#ifndef OPENSSL_NO_FP_API
+X509 *d2i_X509_fp(FILE *fp, X509 **x509);
+int i2d_X509_fp(FILE *fp,X509 *x509);
+X509_CRL *d2i_X509_CRL_fp(FILE *fp,X509_CRL **crl);
+int i2d_X509_CRL_fp(FILE *fp,X509_CRL *crl);
+X509_REQ *d2i_X509_REQ_fp(FILE *fp,X509_REQ **req);
+int i2d_X509_REQ_fp(FILE *fp,X509_REQ *req);
+#ifndef OPENSSL_NO_RSA
+RSA *d2i_RSAPrivateKey_fp(FILE *fp,RSA **rsa);
+int i2d_RSAPrivateKey_fp(FILE *fp,RSA *rsa);
+RSA *d2i_RSAPublicKey_fp(FILE *fp,RSA **rsa);
+int i2d_RSAPublicKey_fp(FILE *fp,RSA *rsa);
+RSA *d2i_RSA_PUBKEY_fp(FILE *fp,RSA **rsa);
+int i2d_RSA_PUBKEY_fp(FILE *fp,RSA *rsa);
+#endif
+#ifndef OPENSSL_NO_DSA
+DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa);
+int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa);
+DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa);
+int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa);
+#endif
+#ifndef OPENSSL_NO_EC
+EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey);
+int   i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey);
+EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey);
+int   i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey);
+#endif
+X509_SIG *d2i_PKCS8_fp(FILE *fp,X509_SIG **p8);
+int i2d_PKCS8_fp(FILE *fp,X509_SIG *p8);
+PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
+						PKCS8_PRIV_KEY_INFO **p8inf);
+int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,PKCS8_PRIV_KEY_INFO *p8inf);
+int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key);
+int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey);
+EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a);
+int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey);
+EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a);
+#endif
+
+#ifndef OPENSSL_NO_BIO
+X509 *d2i_X509_bio(BIO *bp,X509 **x509);
+int i2d_X509_bio(BIO *bp,X509 *x509);
+X509_CRL *d2i_X509_CRL_bio(BIO *bp,X509_CRL **crl);
+int i2d_X509_CRL_bio(BIO *bp,X509_CRL *crl);
+X509_REQ *d2i_X509_REQ_bio(BIO *bp,X509_REQ **req);
+int i2d_X509_REQ_bio(BIO *bp,X509_REQ *req);
+#ifndef OPENSSL_NO_RSA
+RSA *d2i_RSAPrivateKey_bio(BIO *bp,RSA **rsa);
+int i2d_RSAPrivateKey_bio(BIO *bp,RSA *rsa);
+RSA *d2i_RSAPublicKey_bio(BIO *bp,RSA **rsa);
+int i2d_RSAPublicKey_bio(BIO *bp,RSA *rsa);
+RSA *d2i_RSA_PUBKEY_bio(BIO *bp,RSA **rsa);
+int i2d_RSA_PUBKEY_bio(BIO *bp,RSA *rsa);
+#endif
+#ifndef OPENSSL_NO_DSA
+DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa);
+int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa);
+DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa);
+int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa);
+#endif
+#ifndef OPENSSL_NO_EC
+EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey);
+int   i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *eckey);
+EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey);
+int   i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey);
+#endif
+X509_SIG *d2i_PKCS8_bio(BIO *bp,X509_SIG **p8);
+int i2d_PKCS8_bio(BIO *bp,X509_SIG *p8);
+PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,
+						PKCS8_PRIV_KEY_INFO **p8inf);
+int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,PKCS8_PRIV_KEY_INFO *p8inf);
+int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key);
+int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey);
+EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a);
+int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey);
+EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a);
+#endif
+
+X509 *X509_dup(X509 *x509);
+X509_ATTRIBUTE *X509_ATTRIBUTE_dup(X509_ATTRIBUTE *xa);
+X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *ex);
+X509_CRL *X509_CRL_dup(X509_CRL *crl);
+X509_REVOKED *X509_REVOKED_dup(X509_REVOKED *rev);
+X509_REQ *X509_REQ_dup(X509_REQ *req);
+X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn);
+int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype, void *pval);
+void X509_ALGOR_get0(ASN1_OBJECT **paobj, int *pptype, void **ppval,
+						X509_ALGOR *algor);
+void X509_ALGOR_set_md(X509_ALGOR *alg, const EVP_MD *md);
+
+X509_NAME *X509_NAME_dup(X509_NAME *xn);
+X509_NAME_ENTRY *X509_NAME_ENTRY_dup(X509_NAME_ENTRY *ne);
+
+int		X509_cmp_time(const ASN1_TIME *s, time_t *t);
+int		X509_cmp_current_time(const ASN1_TIME *s);
+ASN1_TIME *	X509_time_adj(ASN1_TIME *s, long adj, time_t *t);
+ASN1_TIME *	X509_time_adj_ex(ASN1_TIME *s,
+				int offset_day, long offset_sec, time_t *t);
+ASN1_TIME *	X509_gmtime_adj(ASN1_TIME *s, long adj);
+
+const char *	X509_get_default_cert_area(void );
+const char *	X509_get_default_cert_dir(void );
+const char *	X509_get_default_cert_file(void );
+const char *	X509_get_default_cert_dir_env(void );
+const char *	X509_get_default_cert_file_env(void );
+const char *	X509_get_default_private_dir(void );
+
+X509_REQ *	X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md);
+X509 *		X509_REQ_to_X509(X509_REQ *r, int days,EVP_PKEY *pkey);
+
+DECLARE_ASN1_ENCODE_FUNCTIONS(X509_ALGORS, X509_ALGORS, X509_ALGORS)
+DECLARE_ASN1_FUNCTIONS(X509_VAL)
+
+DECLARE_ASN1_FUNCTIONS(X509_PUBKEY)
+
+int		X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey);
+EVP_PKEY *	X509_PUBKEY_get(X509_PUBKEY *key);
+int		X509_get_pubkey_parameters(EVP_PKEY *pkey,
+					   STACK_OF(X509) *chain);
+int		i2d_PUBKEY(const EVP_PKEY *a,unsigned char **pp);
+EVP_PKEY *	d2i_PUBKEY(EVP_PKEY **a,const unsigned char **pp,
+			long length);
+#ifndef OPENSSL_NO_RSA
+int		i2d_RSA_PUBKEY(const RSA *a,unsigned char **pp);
+RSA *		d2i_RSA_PUBKEY(RSA **a,const unsigned char **pp,
+			long length);
+#endif
+#ifndef OPENSSL_NO_DSA
+int		i2d_DSA_PUBKEY(const DSA *a,unsigned char **pp);
+DSA *		d2i_DSA_PUBKEY(DSA **a,const unsigned char **pp,
+			long length);
+#endif
+#ifndef OPENSSL_NO_EC
+int		i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp);
+EC_KEY 		*d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp,
+			long length);
+#endif
+
+DECLARE_ASN1_FUNCTIONS(X509_SIG)
+DECLARE_ASN1_FUNCTIONS(X509_REQ_INFO)
+DECLARE_ASN1_FUNCTIONS(X509_REQ)
+
+DECLARE_ASN1_FUNCTIONS(X509_ATTRIBUTE)
+X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value);
+
+DECLARE_ASN1_FUNCTIONS(X509_EXTENSION)
+DECLARE_ASN1_ENCODE_FUNCTIONS(X509_EXTENSIONS, X509_EXTENSIONS, X509_EXTENSIONS)
+
+DECLARE_ASN1_FUNCTIONS(X509_NAME_ENTRY)
+
+DECLARE_ASN1_FUNCTIONS(X509_NAME)
+
+int		X509_NAME_set(X509_NAME **xn, X509_NAME *name);
+
+DECLARE_ASN1_FUNCTIONS(X509_CINF)
+
+DECLARE_ASN1_FUNCTIONS(X509)
+DECLARE_ASN1_FUNCTIONS(X509_CERT_AUX)
+
+DECLARE_ASN1_FUNCTIONS(X509_CERT_PAIR)
+
+int X509_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+	     CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int X509_set_ex_data(X509 *r, int idx, void *arg);
+void *X509_get_ex_data(X509 *r, int idx);
+int		i2d_X509_AUX(X509 *a,unsigned char **pp);
+X509 *		d2i_X509_AUX(X509 **a,const unsigned char **pp,long length);
+
+void X509_get0_signature(ASN1_BIT_STRING **psig, X509_ALGOR **palg,
+								const X509 *x);
+int X509_get_signature_nid(const X509 *x);
+
+int X509_alias_set1(X509 *x, unsigned char *name, int len);
+int X509_keyid_set1(X509 *x, unsigned char *id, int len);
+unsigned char * X509_alias_get0(X509 *x, int *len);
+unsigned char * X509_keyid_get0(X509 *x, int *len);
+int (*X509_TRUST_set_default(int (*trust)(int , X509 *, int)))(int, X509 *, int);
+int X509_TRUST_set(int *t, int trust);
+int X509_add1_trust_object(X509 *x, ASN1_OBJECT *obj);
+int X509_add1_reject_object(X509 *x, ASN1_OBJECT *obj);
+void X509_trust_clear(X509 *x);
+void X509_reject_clear(X509 *x);
+
+DECLARE_ASN1_FUNCTIONS(X509_REVOKED)
+DECLARE_ASN1_FUNCTIONS(X509_CRL_INFO)
+DECLARE_ASN1_FUNCTIONS(X509_CRL)
+
+int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
+int X509_CRL_get0_by_serial(X509_CRL *crl,
+		X509_REVOKED **ret, ASN1_INTEGER *serial);
+int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x);
+
+X509_PKEY *	X509_PKEY_new(void );
+void		X509_PKEY_free(X509_PKEY *a);
+int		i2d_X509_PKEY(X509_PKEY *a,unsigned char **pp);
+X509_PKEY *	d2i_X509_PKEY(X509_PKEY **a,const unsigned char **pp,long length);
+
+DECLARE_ASN1_FUNCTIONS(NETSCAPE_SPKI)
+DECLARE_ASN1_FUNCTIONS(NETSCAPE_SPKAC)
+DECLARE_ASN1_FUNCTIONS(NETSCAPE_CERT_SEQUENCE)
+
+#ifndef OPENSSL_NO_EVP
+X509_INFO *	X509_INFO_new(void);
+void		X509_INFO_free(X509_INFO *a);
+char *		X509_NAME_oneline(X509_NAME *a,char *buf,int size);
+
+int ASN1_verify(i2d_of_void *i2d, X509_ALGOR *algor1,
+		ASN1_BIT_STRING *signature,char *data,EVP_PKEY *pkey);
+
+int ASN1_digest(i2d_of_void *i2d,const EVP_MD *type,char *data,
+		unsigned char *md,unsigned int *len);
+
+int ASN1_sign(i2d_of_void *i2d, X509_ALGOR *algor1,
+	      X509_ALGOR *algor2, ASN1_BIT_STRING *signature,
+	      char *data,EVP_PKEY *pkey, const EVP_MD *type);
+
+int ASN1_item_digest(const ASN1_ITEM *it,const EVP_MD *type,void *data,
+	unsigned char *md,unsigned int *len);
+
+int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *algor1,
+	ASN1_BIT_STRING *signature,void *data,EVP_PKEY *pkey);
+
+int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1, X509_ALGOR *algor2,
+	ASN1_BIT_STRING *signature,
+	void *data, EVP_PKEY *pkey, const EVP_MD *type);
+int ASN1_item_sign_ctx(const ASN1_ITEM *it,
+		X509_ALGOR *algor1, X509_ALGOR *algor2,
+	     	ASN1_BIT_STRING *signature, void *asn, EVP_MD_CTX *ctx);
+#endif
+
+int 		X509_set_version(X509 *x,long version);
+int 		X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial);
+ASN1_INTEGER *	X509_get_serialNumber(X509 *x);
+int 		X509_set_issuer_name(X509 *x, X509_NAME *name);
+X509_NAME *	X509_get_issuer_name(X509 *a);
+int 		X509_set_subject_name(X509 *x, X509_NAME *name);
+X509_NAME *	X509_get_subject_name(X509 *a);
+int 		X509_set_notBefore(X509 *x, const ASN1_TIME *tm);
+int 		X509_set_notAfter(X509 *x, const ASN1_TIME *tm);
+int 		X509_set_pubkey(X509 *x, EVP_PKEY *pkey);
+EVP_PKEY *	X509_get_pubkey(X509 *x);
+ASN1_BIT_STRING * X509_get0_pubkey_bitstr(const X509 *x);
+int		X509_certificate_type(X509 *x,EVP_PKEY *pubkey /* optional */);
+
+int		X509_REQ_set_version(X509_REQ *x,long version);
+int		X509_REQ_set_subject_name(X509_REQ *req,X509_NAME *name);
+int		X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
+EVP_PKEY *	X509_REQ_get_pubkey(X509_REQ *req);
+int		X509_REQ_extension_nid(int nid);
+int *		X509_REQ_get_extension_nids(void);
+void		X509_REQ_set_extension_nids(int *nids);
+STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req);
+int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts,
+				int nid);
+int X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts);
+int X509_REQ_get_attr_count(const X509_REQ *req);
+int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid,
+			  int lastpos);
+int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, ASN1_OBJECT *obj,
+			  int lastpos);
+X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc);
+X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc);
+int X509_REQ_add1_attr(X509_REQ *req, X509_ATTRIBUTE *attr);
+int X509_REQ_add1_attr_by_OBJ(X509_REQ *req,
+			const ASN1_OBJECT *obj, int type,
+			const unsigned char *bytes, int len);
+int X509_REQ_add1_attr_by_NID(X509_REQ *req,
+			int nid, int type,
+			const unsigned char *bytes, int len);
+int X509_REQ_add1_attr_by_txt(X509_REQ *req,
+			const char *attrname, int type,
+			const unsigned char *bytes, int len);
+
+int X509_CRL_set_version(X509_CRL *x, long version);
+int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name);
+int X509_CRL_set_lastUpdate(X509_CRL *x, const ASN1_TIME *tm);
+int X509_CRL_set_nextUpdate(X509_CRL *x, const ASN1_TIME *tm);
+int X509_CRL_sort(X509_CRL *crl);
+
+int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial);
+int X509_REVOKED_set_revocationDate(X509_REVOKED *r, ASN1_TIME *tm);
+
+X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer,
+			EVP_PKEY *skey, const EVP_MD *md, unsigned int flags);
+
+int		X509_REQ_check_private_key(X509_REQ *x509,EVP_PKEY *pkey);
+
+int		X509_check_private_key(X509 *x509,EVP_PKEY *pkey);
+int 		X509_chain_check_suiteb(int *perror_depth,
+						X509 *x, STACK_OF(X509) *chain,
+						unsigned long flags);
+int 		X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk,
+						unsigned long flags);
+STACK_OF(X509) *X509_chain_up_ref(STACK_OF(X509) *chain);
+
+int		X509_issuer_and_serial_cmp(const X509 *a, const X509 *b);
+unsigned long	X509_issuer_and_serial_hash(X509 *a);
+
+int		X509_issuer_name_cmp(const X509 *a, const X509 *b);
+unsigned long	X509_issuer_name_hash(X509 *a);
+
+int		X509_subject_name_cmp(const X509 *a, const X509 *b);
+unsigned long	X509_subject_name_hash(X509 *x);
+
+#ifndef OPENSSL_NO_MD5
+unsigned long	X509_issuer_name_hash_old(X509 *a);
+unsigned long	X509_subject_name_hash_old(X509 *x);
+#endif
+
+int		X509_cmp(const X509 *a, const X509 *b);
+int		X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b);
+unsigned long	X509_NAME_hash(X509_NAME *x);
+unsigned long	X509_NAME_hash_old(X509_NAME *x);
+
+int		X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b);
+int		X509_CRL_match(const X509_CRL *a, const X509_CRL *b);
+#ifndef OPENSSL_NO_FP_API
+int		X509_print_ex_fp(FILE *bp,X509 *x, unsigned long nmflag, unsigned long cflag);
+int		X509_print_fp(FILE *bp,X509 *x);
+int		X509_CRL_print_fp(FILE *bp,X509_CRL *x);
+int		X509_REQ_print_fp(FILE *bp,X509_REQ *req);
+int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent, unsigned long flags);
+#endif
+
+#ifndef OPENSSL_NO_BIO
+int		X509_NAME_print(BIO *bp, X509_NAME *name, int obase);
+int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent, unsigned long flags);
+int		X509_print_ex(BIO *bp,X509 *x, unsigned long nmflag, unsigned long cflag);
+int		X509_print(BIO *bp,X509 *x);
+int		X509_ocspid_print(BIO *bp,X509 *x);
+int		X509_CERT_AUX_print(BIO *bp,X509_CERT_AUX *x, int indent);
+int		X509_CRL_print(BIO *bp,X509_CRL *x);
+int		X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflag, unsigned long cflag);
+int		X509_REQ_print(BIO *bp,X509_REQ *req);
+#endif
+
+int 		X509_NAME_entry_count(X509_NAME *name);
+int 		X509_NAME_get_text_by_NID(X509_NAME *name, int nid,
+			char *buf,int len);
+int		X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
+			char *buf,int len);
+
+/* NOTE: you should be passsing -1, not 0 as lastpos.  The functions that use
+ * lastpos, search after that position on. */
+int 		X509_NAME_get_index_by_NID(X509_NAME *name,int nid,int lastpos);
+int 		X509_NAME_get_index_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
+			int lastpos);
+X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *name, int loc);
+X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name, int loc);
+int 		X509_NAME_add_entry(X509_NAME *name,X509_NAME_ENTRY *ne,
+			int loc, int set);
+int X509_NAME_add_entry_by_OBJ(X509_NAME *name, ASN1_OBJECT *obj, int type,
+			unsigned char *bytes, int len, int loc, int set);
+int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid, int type,
+			unsigned char *bytes, int len, int loc, int set);
+X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(X509_NAME_ENTRY **ne,
+		const char *field, int type, const unsigned char *bytes, int len);
+X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(X509_NAME_ENTRY **ne, int nid,
+			int type,unsigned char *bytes, int len);
+int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type,
+			const unsigned char *bytes, int len, int loc, int set);
+X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(X509_NAME_ENTRY **ne,
+			const ASN1_OBJECT *obj, int type,const unsigned char *bytes,
+			int len);
+int 		X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *ne,
+			const ASN1_OBJECT *obj);
+int 		X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type,
+			const unsigned char *bytes, int len);
+ASN1_OBJECT *	X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *ne);
+ASN1_STRING *	X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *ne);
+
+int		X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x);
+int		X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x,
+				      int nid, int lastpos);
+int		X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *x,
+				      const ASN1_OBJECT *obj,int lastpos);
+int		X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *x,
+					   int crit, int lastpos);
+X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc);
+X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x, int loc);
+STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
+					 X509_EXTENSION *ex, int loc);
+
+int		X509_get_ext_count(X509 *x);
+int		X509_get_ext_by_NID(X509 *x, int nid, int lastpos);
+int		X509_get_ext_by_OBJ(X509 *x,ASN1_OBJECT *obj,int lastpos);
+int		X509_get_ext_by_critical(X509 *x, int crit, int lastpos);
+X509_EXTENSION *X509_get_ext(X509 *x, int loc);
+X509_EXTENSION *X509_delete_ext(X509 *x, int loc);
+int		X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
+void	*	X509_get_ext_d2i(X509 *x, int nid, int *crit, int *idx);
+int		X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
+							unsigned long flags);
+
+int		X509_CRL_get_ext_count(X509_CRL *x);
+int		X509_CRL_get_ext_by_NID(X509_CRL *x, int nid, int lastpos);
+int		X509_CRL_get_ext_by_OBJ(X509_CRL *x,ASN1_OBJECT *obj,int lastpos);
+int		X509_CRL_get_ext_by_critical(X509_CRL *x, int crit, int lastpos);
+X509_EXTENSION *X509_CRL_get_ext(X509_CRL *x, int loc);
+X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc);
+int		X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc);
+void	*	X509_CRL_get_ext_d2i(X509_CRL *x, int nid, int *crit, int *idx);
+int		X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit,
+							unsigned long flags);
+
+int		X509_REVOKED_get_ext_count(X509_REVOKED *x);
+int		X509_REVOKED_get_ext_by_NID(X509_REVOKED *x, int nid, int lastpos);
+int		X509_REVOKED_get_ext_by_OBJ(X509_REVOKED *x,ASN1_OBJECT *obj,int lastpos);
+int		X509_REVOKED_get_ext_by_critical(X509_REVOKED *x, int crit, int lastpos);
+X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *x, int loc);
+X509_EXTENSION *X509_REVOKED_delete_ext(X509_REVOKED *x, int loc);
+int		X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex, int loc);
+void	*	X509_REVOKED_get_ext_d2i(X509_REVOKED *x, int nid, int *crit, int *idx);
+int		X509_REVOKED_add1_ext_i2d(X509_REVOKED *x, int nid, void *value, int crit,
+							unsigned long flags);
+
+X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
+			int nid, int crit, ASN1_OCTET_STRING *data);
+X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **ex,
+			const ASN1_OBJECT *obj,int crit,ASN1_OCTET_STRING *data);
+int		X509_EXTENSION_set_object(X509_EXTENSION *ex,const ASN1_OBJECT *obj);
+int		X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit);
+int		X509_EXTENSION_set_data(X509_EXTENSION *ex,
+			ASN1_OCTET_STRING *data);
+ASN1_OBJECT *	X509_EXTENSION_get_object(X509_EXTENSION *ex);
+ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
+int		X509_EXTENSION_get_critical(X509_EXTENSION *ex);
+
+int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x);
+int X509at_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid,
+			  int lastpos);
+int X509at_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk, const ASN1_OBJECT *obj,
+			  int lastpos);
+X509_ATTRIBUTE *X509at_get_attr(const STACK_OF(X509_ATTRIBUTE) *x, int loc);
+X509_ATTRIBUTE *X509at_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc);
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x,
+					 X509_ATTRIBUTE *attr);
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_OBJ(STACK_OF(X509_ATTRIBUTE) **x,
+			const ASN1_OBJECT *obj, int type,
+			const unsigned char *bytes, int len);
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_NID(STACK_OF(X509_ATTRIBUTE) **x,
+			int nid, int type,
+			const unsigned char *bytes, int len);
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE) **x,
+			const char *attrname, int type,
+			const unsigned char *bytes, int len);
+void *X509at_get0_data_by_OBJ(STACK_OF(X509_ATTRIBUTE) *x,
+				ASN1_OBJECT *obj, int lastpos, int type);
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid,
+	     int atrtype, const void *data, int len);
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr,
+	     const ASN1_OBJECT *obj, int atrtype, const void *data, int len);
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(X509_ATTRIBUTE **attr,
+		const char *atrname, int type, const unsigned char *bytes, int len);
+int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, const ASN1_OBJECT *obj);
+int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, const void *data, int len);
+void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx,
+					int atrtype, void *data);
+int X509_ATTRIBUTE_count(X509_ATTRIBUTE *attr);
+ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr);
+ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *attr, int idx);
+
+int EVP_PKEY_get_attr_count(const EVP_PKEY *key);
+int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *key, int nid,
+			  int lastpos);
+int EVP_PKEY_get_attr_by_OBJ(const EVP_PKEY *key, ASN1_OBJECT *obj,
+			  int lastpos);
+X509_ATTRIBUTE *EVP_PKEY_get_attr(const EVP_PKEY *key, int loc);
+X509_ATTRIBUTE *EVP_PKEY_delete_attr(EVP_PKEY *key, int loc);
+int EVP_PKEY_add1_attr(EVP_PKEY *key, X509_ATTRIBUTE *attr);
+int EVP_PKEY_add1_attr_by_OBJ(EVP_PKEY *key,
+			const ASN1_OBJECT *obj, int type,
+			const unsigned char *bytes, int len);
+int EVP_PKEY_add1_attr_by_NID(EVP_PKEY *key,
+			int nid, int type,
+			const unsigned char *bytes, int len);
+int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *key,
+			const char *attrname, int type,
+			const unsigned char *bytes, int len);
+
+int		X509_verify_cert(X509_STORE_CTX *ctx);
+
+/* lookup a cert from a X509 STACK */
+X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk,X509_NAME *name,
+				     ASN1_INTEGER *serial);
+X509 *X509_find_by_subject(STACK_OF(X509) *sk,X509_NAME *name);
+
+DECLARE_ASN1_FUNCTIONS(PBEPARAM)
+DECLARE_ASN1_FUNCTIONS(PBE2PARAM)
+DECLARE_ASN1_FUNCTIONS(PBKDF2PARAM)
+
+int PKCS5_pbe_set0_algor(X509_ALGOR *algor, int alg, int iter,
+				const unsigned char *salt, int saltlen);
+
+X509_ALGOR *PKCS5_pbe_set(int alg, int iter,
+				const unsigned char *salt, int saltlen);
+X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter,
+					 unsigned char *salt, int saltlen);
+X509_ALGOR *PKCS5_pbe2_set_iv(const EVP_CIPHER *cipher, int iter,
+				 unsigned char *salt, int saltlen,
+				 unsigned char *aiv, int prf_nid);
+
+X509_ALGOR *PKCS5_pbkdf2_set(int iter, unsigned char *salt, int saltlen,
+				int prf_nid, int keylen);
+
+/* PKCS#8 utilities */
+
+DECLARE_ASN1_FUNCTIONS(PKCS8_PRIV_KEY_INFO)
+
+EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *p8);
+PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey);
+PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8_broken(EVP_PKEY *pkey, int broken);
+PKCS8_PRIV_KEY_INFO *PKCS8_set_broken(PKCS8_PRIV_KEY_INFO *p8, int broken);
+
+int PKCS8_pkey_set0(PKCS8_PRIV_KEY_INFO *priv, ASN1_OBJECT *aobj,
+			int version, int ptype, void *pval,
+				unsigned char *penc, int penclen);
+int PKCS8_pkey_get0(ASN1_OBJECT **ppkalg,
+		const unsigned char **pk, int *ppklen,
+		X509_ALGOR **pa,
+		PKCS8_PRIV_KEY_INFO *p8);
+
+int X509_PUBKEY_set0_param(X509_PUBKEY *pub, const ASN1_OBJECT *aobj,
+					int ptype, void *pval,
+					unsigned char *penc, int penclen);
+int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg,
+		const unsigned char **pk, int *ppklen,
+		X509_ALGOR **pa,
+		X509_PUBKEY *pub);
+
+int X509_check_trust(X509 *x, int id, int flags);
+int X509_TRUST_get_count(void);
+X509_TRUST * X509_TRUST_get0(int idx);
+int X509_TRUST_get_by_id(int id);
+int X509_TRUST_add(int id, int flags, int (*ck)(X509_TRUST *, X509 *, int),
+					char *name, int arg1, void *arg2);
+void X509_TRUST_cleanup(void);
+int X509_TRUST_get_flags(X509_TRUST *xp);
+char *X509_TRUST_get0_name(X509_TRUST *xp);
+int X509_TRUST_get_trust(X509_TRUST *xp);
+
+/* PKCS7_get_certificates parses a PKCS#7, SignedData structure from |cbs| and
+ * appends the included certificates to |out_certs|. It returns one on success
+ * and zero on error. */
+int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs);
+
+
+/* EVP_PK values indicate the algorithm of the public key in a certificate. */
+
+#define EVP_PK_RSA	0x0001
+#define EVP_PK_DSA	0x0002
+#define EVP_PK_DH	0x0004
+#define EVP_PK_EC	0x0008
+
+/* EVP_PKS values indicate the algorithm used to sign a certificate. */
+
+#define EVP_PKS_RSA 0x0100
+#define EVP_PKS_DSA 0x0200
+#define EVP_PKS_EC 0x0400
+
+/* EVP_PKT values are flags that define what public-key operations can be
+ * performed with the public key from a certificate. */
+
+/* EVP_PKT_SIGN indicates that the public key can be used for signing. */
+#define EVP_PKT_SIGN 0x0010
+/* EVP_PKT_ENC indicates that a session key can be encrypted to the public
+ * key. */
+#define EVP_PKT_ENC 0x0020
+/* EVP_PKT_EXCH indicates that key-agreement can be performed. */
+#define EVP_PKT_EXCH 0x0040
+/* EVP_PKT_EXP indicates that key is weak (i.e. "export"). */
+#define EVP_PKT_EXP 0x1000
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#define X509_F_x509_name_ex_new 100
+#define X509_F_X509_EXTENSION_create_by_NID 101
+#define X509_F_X509_load_crl_file 102
+#define X509_F_X509_TRUST_set 103
+#define X509_F_X509_EXTENSION_create_by_OBJ 104
+#define X509_F_by_file_ctrl 105
+#define X509_F_X509_load_cert_crl_file 106
+#define X509_F_X509_CRL_add0_revoked 107
+#define X509_F_bitstr_cb 108
+#define X509_F_X509_STORE_CTX_new 109
+#define X509_F_X509_REQ_to_X509 110
+#define X509_F_X509v3_add_ext 111
+#define X509_F_ASN1_sign 112
+#define X509_F_asn1_str2type 113
+#define X509_F_i2d_RSA_PUBKEY 114
+#define X509_F_ASN1_item_sign_ctx 115
+#define X509_F_x509_name_encode 116
+#define X509_F_d2i_X509_PKEY 117
+#define X509_F_ASN1_generate_v3 118
+#define X509_F_dir_ctrl 119
+#define X509_F_X509_print_ex_fp 120
+#define X509_F_X509_ATTRIBUTE_get0_data 121
+#define X509_F_X509_NAME_oneline 122
+#define X509_F_X509_CRL_print_fp 123
+#define X509_F_X509_STORE_CTX_get1_issuer 124
+#define X509_F_add_cert_dir 125
+#define X509_F_PKCS7_get_certificates 126
+#define X509_F_X509_ATTRIBUTE_create_by_NID 127
+#define X509_F_X509_ATTRIBUTE_set1_data 128
+#define X509_F_X509_STORE_CTX_init 129
+#define X509_F_NETSCAPE_SPKI_b64_decode 130
+#define X509_F_X509_NAME_print 131
+#define X509_F_x509_name_ex_d2i 132
+#define X509_F_X509_PKEY_new 133
+#define X509_F_X509_STORE_add_cert 134
+#define X509_F_parse_tagging 135
+#define X509_F_check_policy 136
+#define X509_F_ASN1_digest 137
+#define X509_F_X509_load_cert_file 138
+#define X509_F_X509_ATTRIBUTE_create_by_txt 139
+#define X509_F_X509_PUBKEY_set 140
+#define X509_F_X509_PUBKEY_get 141
+#define X509_F_get_cert_by_subject 142
+#define X509_F_X509_NAME_add_entry 143
+#define X509_F_X509at_add1_attr 144
+#define X509_F_X509_check_private_key 145
+#define X509_F_append_exp 146
+#define X509_F_i2d_EC_PUBKEY 147
+#define X509_F_X509_INFO_new 148
+#define X509_F_X509_STORE_CTX_purpose_inherit 149
+#define X509_F_NETSCAPE_SPKI_b64_encode 150
+#define X509_F_X509_to_X509_REQ 151
+#define X509_F_X509_NAME_ENTRY_create_by_txt 152
+#define X509_F_X509_NAME_ENTRY_set_object 153
+#define X509_F_asn1_cb 154
+#define X509_F_X509_verify_cert 155
+#define X509_F_X509_CRL_diff 156
+#define X509_F_i2d_PrivateKey 157
+#define X509_F_X509_REQ_check_private_key 158
+#define X509_F_X509_STORE_add_crl 159
+#define X509_F_X509_get_pubkey_parameters 160
+#define X509_F_ASN1_item_verify 161
+#define X509_F_X509_ATTRIBUTE_create_by_OBJ 162
+#define X509_F_i2d_DSA_PUBKEY 163
+#define X509_F_X509_TRUST_add 164
+#define X509_F_X509_NAME_ENTRY_create_by_NID 165
+#define X509_R_NO_CERT_SET_FOR_US_TO_VERIFY 100
+#define X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN 101
+#define X509_R_METHOD_NOT_SUPPORTED 102
+#define X509_R_UNSUPPORTED_ALGORITHM 103
+#define X509_R_CRL_VERIFY_FAILURE 104
+#define X509_R_BASE64_DECODE_ERROR 105
+#define X509_R_INVALID_TRUST 106
+#define X509_R_UNKNOWN_NID 107
+#define X509_R_INVALID_DIRECTORY 108
+#define X509_R_KEY_VALUES_MISMATCH 109
+#define X509_R_CERT_ALREADY_IN_HASH_TABLE 110
+#define X509_R_PUBLIC_KEY_DECODE_ERROR 111
+#define X509_R_NOT_PKCS7_SIGNED_DATA 112
+#define X509_R_PUBLIC_KEY_ENCODE_ERROR 113
+#define X509_R_LOADING_CERT_DIR 114
+#define X509_R_WRONG_TYPE 115
+#define X509_R_UNKNOWN_PURPOSE_ID 116
+#define X509_R_NEWER_CRL_NOT_NEWER 117
+#define X509_R_UNKNOWN_TRUST_ID 118
+#define X509_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED 119
+#define X509_R_KEY_TYPE_MISMATCH 120
+#define X509_R_UNKNOWN_KEY_TYPE 121
+#define X509_R_BAD_X509_FILETYPE 122
+#define X509_R_ISSUER_MISMATCH 123
+#define X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY 124
+#define X509_R_WRONG_LOOKUP_TYPE 125
+#define X509_R_CONTEXT_NOT_INITIALISED 126
+#define X509_R_CANT_CHECK_DH_KEY 127
+#define X509_R_NO_CERTIFICATES_INCLUDED 128
+#define X509_R_INVALID_FIELD_NAME 129
+#define X509_R_SHOULD_RETRY 130
+#define X509_R_NO_CRL_NUMBER 131
+#define X509_R_IDP_MISMATCH 132
+#define X509_R_LOADING_DEFAULTS 133
+#define X509_R_BAD_PKCS7_VERSION 134
+#define X509_R_CRL_ALREADY_DELTA 135
+#define X509_R_ERR_ASN1_LIB 136
+#define X509_R_AKID_MISMATCH 137
+
+#endif
diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c
new file mode 100644
index 0000000..3613c35
--- /dev/null
+++ b/crypto/x509/x509_att.c
@@ -0,0 +1,353 @@
+/* crypto/x509/x509_att.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+
+
+int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x)
+{
+	return sk_X509_ATTRIBUTE_num(x);
+}
+
+int X509at_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid,
+			  int lastpos)
+{
+	const ASN1_OBJECT *obj;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL) return(-2);
+	return(X509at_get_attr_by_OBJ(x,obj,lastpos));
+}
+
+int X509at_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk, const ASN1_OBJECT *obj,
+			  int lastpos)
+{
+	int n;
+	X509_ATTRIBUTE *ex;
+
+	if (sk == NULL) return(-1);
+	lastpos++;
+	if (lastpos < 0)
+		lastpos=0;
+	n=sk_X509_ATTRIBUTE_num(sk);
+	for ( ; lastpos < n; lastpos++)
+		{
+		ex=sk_X509_ATTRIBUTE_value(sk,lastpos);
+		if (OBJ_cmp(ex->object,obj) == 0)
+			return(lastpos);
+		}
+	return(-1);
+}
+
+X509_ATTRIBUTE *X509at_get_attr(const STACK_OF(X509_ATTRIBUTE) *x, int loc)
+{
+	if (x == NULL || loc < 0 || sk_X509_ATTRIBUTE_num(x) <= (size_t) loc)
+		return NULL;
+	else
+		return sk_X509_ATTRIBUTE_value(x,loc);
+}
+
+X509_ATTRIBUTE *X509at_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc)
+{
+	X509_ATTRIBUTE *ret;
+
+	if (x == NULL || loc < 0 || sk_X509_ATTRIBUTE_num(x) <= (size_t) loc)
+		return(NULL);
+	ret=sk_X509_ATTRIBUTE_delete(x,loc);
+	return(ret);
+}
+
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x,
+					 X509_ATTRIBUTE *attr)
+{
+	X509_ATTRIBUTE *new_attr=NULL;
+	STACK_OF(X509_ATTRIBUTE) *sk=NULL;
+
+	if (x == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509at_add1_attr, ERR_R_PASSED_NULL_PARAMETER);
+		goto err2;
+		} 
+
+	if (*x == NULL)
+		{
+		if ((sk=sk_X509_ATTRIBUTE_new_null()) == NULL)
+			goto err;
+		}
+	else
+		sk= *x;
+
+	if ((new_attr=X509_ATTRIBUTE_dup(attr)) == NULL)
+		goto err2;
+	if (!sk_X509_ATTRIBUTE_push(sk,new_attr))
+		goto err;
+	if (*x == NULL)
+		*x=sk;
+	return(sk);
+err:
+	OPENSSL_PUT_ERROR(X509, X509at_add1_attr, ERR_R_MALLOC_FAILURE);
+err2:
+	if (new_attr != NULL) X509_ATTRIBUTE_free(new_attr);
+	if (sk != NULL) sk_X509_ATTRIBUTE_free(sk);
+	return(NULL);
+}
+
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_OBJ(STACK_OF(X509_ATTRIBUTE) **x,
+			const ASN1_OBJECT *obj, int type,
+			const unsigned char *bytes, int len)
+{
+	X509_ATTRIBUTE *attr;
+	STACK_OF(X509_ATTRIBUTE) *ret;
+	attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, type, bytes, len);
+	if(!attr) return 0;
+	ret = X509at_add1_attr(x, attr);
+	X509_ATTRIBUTE_free(attr);
+	return ret;
+}
+
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_NID(STACK_OF(X509_ATTRIBUTE) **x,
+			int nid, int type,
+			const unsigned char *bytes, int len)
+{
+	X509_ATTRIBUTE *attr;
+	STACK_OF(X509_ATTRIBUTE) *ret;
+	attr = X509_ATTRIBUTE_create_by_NID(NULL, nid, type, bytes, len);
+	if(!attr) return 0;
+	ret = X509at_add1_attr(x, attr);
+	X509_ATTRIBUTE_free(attr);
+	return ret;
+}
+
+STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE) **x,
+			const char *attrname, int type,
+			const unsigned char *bytes, int len)
+{
+	X509_ATTRIBUTE *attr;
+	STACK_OF(X509_ATTRIBUTE) *ret;
+	attr = X509_ATTRIBUTE_create_by_txt(NULL, attrname, type, bytes, len);
+	if(!attr) return 0;
+	ret = X509at_add1_attr(x, attr);
+	X509_ATTRIBUTE_free(attr);
+	return ret;
+}
+
+void *X509at_get0_data_by_OBJ(STACK_OF(X509_ATTRIBUTE) *x,
+				ASN1_OBJECT *obj, int lastpos, int type)
+{
+	int i;
+	X509_ATTRIBUTE *at;
+	i = X509at_get_attr_by_OBJ(x, obj, lastpos);
+	if (i == -1)
+		return NULL;
+	if ((lastpos <= -2) && (X509at_get_attr_by_OBJ(x, obj, i) != -1))
+		return NULL;
+	at = X509at_get_attr(x, i);
+	if (lastpos <= -3 && (X509_ATTRIBUTE_count(at) != 1))
+		return NULL;
+	return X509_ATTRIBUTE_get0_data(at, 0, type, NULL);
+}
+
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid,
+	     int atrtype, const void *data, int len)
+{
+	const ASN1_OBJECT *obj;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_ATTRIBUTE_create_by_NID, X509_R_UNKNOWN_NID);
+		return(NULL);
+		}
+	return X509_ATTRIBUTE_create_by_OBJ(attr,obj,atrtype,data,len);
+}
+
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr,
+	     const ASN1_OBJECT *obj, int atrtype, const void *data, int len)
+{
+	X509_ATTRIBUTE *ret;
+
+	if ((attr == NULL) || (*attr == NULL))
+		{
+		if ((ret=X509_ATTRIBUTE_new()) == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_ATTRIBUTE_create_by_OBJ, ERR_R_MALLOC_FAILURE);
+			return(NULL);
+			}
+		}
+	else
+		ret= *attr;
+
+	if (!X509_ATTRIBUTE_set1_object(ret,obj))
+		goto err;
+	if (!X509_ATTRIBUTE_set1_data(ret,atrtype,data,len))
+		goto err;
+
+	if ((attr != NULL) && (*attr == NULL)) *attr=ret;
+	return(ret);
+err:
+	if ((attr == NULL) || (ret != *attr))
+		X509_ATTRIBUTE_free(ret);
+	return(NULL);
+}
+
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(X509_ATTRIBUTE **attr,
+		const char *atrname, int type, const unsigned char *bytes, int len)
+	{
+	ASN1_OBJECT *obj;
+	X509_ATTRIBUTE *nattr;
+
+	obj=OBJ_txt2obj(atrname, 0);
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_ATTRIBUTE_create_by_txt, X509_R_INVALID_FIELD_NAME);
+		ERR_add_error_data(2, "name=", atrname);
+		return(NULL);
+		}
+	nattr = X509_ATTRIBUTE_create_by_OBJ(attr,obj,type,bytes,len);
+	ASN1_OBJECT_free(obj);
+	return nattr;
+	}
+
+int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, const ASN1_OBJECT *obj)
+{
+	if ((attr == NULL) || (obj == NULL))
+		return(0);
+	ASN1_OBJECT_free(attr->object);
+	attr->object=OBJ_dup(obj);
+	return(1);
+}
+
+int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, const void *data, int len)
+{
+	ASN1_TYPE *ttmp;
+	ASN1_STRING *stmp = NULL;
+	int atype = 0;
+	if (!attr) return 0;
+	if(attrtype & MBSTRING_FLAG) {
+		stmp = ASN1_STRING_set_by_NID(NULL, data, len, attrtype,
+						OBJ_obj2nid(attr->object));
+		if(!stmp) {
+			OPENSSL_PUT_ERROR(X509, X509_ATTRIBUTE_set1_data, ERR_R_ASN1_LIB);
+			return 0;
+		}
+		atype = stmp->type;
+	} else if (len != -1){
+		if(!(stmp = ASN1_STRING_type_new(attrtype))) goto err;
+		if(!ASN1_STRING_set(stmp, data, len)) goto err;
+		atype = attrtype;
+	}
+	if(!(attr->value.set = sk_ASN1_TYPE_new_null())) goto err;
+	attr->single = 0;
+	/* This is a bit naughty because the attribute should really have
+	 * at least one value but some types use and zero length SET and
+	 * require this.
+	 */
+	if (attrtype == 0)
+		return 1;
+	if(!(ttmp = ASN1_TYPE_new())) goto err;
+	if ((len == -1) && !(attrtype & MBSTRING_FLAG))
+		{
+		if (!ASN1_TYPE_set1(ttmp, attrtype, data))
+			goto err;
+		}
+	else
+		ASN1_TYPE_set(ttmp, atype, stmp);
+	if(!sk_ASN1_TYPE_push(attr->value.set, ttmp)) goto err;
+	return 1;
+	err:
+	OPENSSL_PUT_ERROR(X509, X509_ATTRIBUTE_set1_data, ERR_R_MALLOC_FAILURE);
+	return 0;
+}
+
+int X509_ATTRIBUTE_count(X509_ATTRIBUTE *attr)
+{
+	if(!attr->single) return sk_ASN1_TYPE_num(attr->value.set);
+	if(attr->value.single) return 1;
+	return 0;
+}
+
+ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr)
+{
+	if (attr == NULL) return(NULL);
+	return(attr->object);
+}
+
+void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx,
+					int atrtype, void *data)
+{
+	ASN1_TYPE *ttmp;
+	ttmp = X509_ATTRIBUTE_get0_type(attr, idx);
+	if(!ttmp) return NULL;
+	if(atrtype != ASN1_TYPE_get(ttmp)){
+		OPENSSL_PUT_ERROR(X509, X509_ATTRIBUTE_get0_data, X509_R_WRONG_TYPE);
+		return NULL;
+	}
+	return ttmp->value.ptr;
+}
+
+ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *attr, int idx)
+{
+	if (attr == NULL) return(NULL);
+	if(idx >= X509_ATTRIBUTE_count(attr)) return NULL;
+	if(!attr->single) return sk_ASN1_TYPE_value(attr->value.set, idx);
+	else return attr->value.single;
+}
diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c
new file mode 100644
index 0000000..e626fcf
--- /dev/null
+++ b/crypto/x509/x509_cmp.c
@@ -0,0 +1,515 @@
+/* crypto/x509/x509_cmp.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b)
+	{
+	int i;
+	X509_CINF *ai,*bi;
+
+	ai=a->cert_info;
+	bi=b->cert_info;
+	i=M_ASN1_INTEGER_cmp(ai->serialNumber,bi->serialNumber);
+	if (i) return(i);
+	return(X509_NAME_cmp(ai->issuer,bi->issuer));
+	}
+
+#ifndef OPENSSL_NO_MD5
+unsigned long X509_issuer_and_serial_hash(X509 *a)
+	{
+	unsigned long ret=0;
+	EVP_MD_CTX ctx;
+	unsigned char md[16];
+	char *f;
+
+	EVP_MD_CTX_init(&ctx);
+	f=X509_NAME_oneline(a->cert_info->issuer,NULL,0);
+	if (!EVP_DigestInit_ex(&ctx, EVP_md5(), NULL))
+		goto err;
+	if (!EVP_DigestUpdate(&ctx,(unsigned char *)f,strlen(f)))
+		goto err;
+	OPENSSL_free(f);
+	if(!EVP_DigestUpdate(&ctx,(unsigned char *)a->cert_info->serialNumber->data,
+		(unsigned long)a->cert_info->serialNumber->length))
+		goto err;
+	if (!EVP_DigestFinal_ex(&ctx,&(md[0]),NULL))
+		goto err;
+	ret=(	((unsigned long)md[0]     )|((unsigned long)md[1]<<8L)|
+		((unsigned long)md[2]<<16L)|((unsigned long)md[3]<<24L)
+		)&0xffffffffL;
+	err:
+	EVP_MD_CTX_cleanup(&ctx);
+	return(ret);
+	}
+#endif
+	
+int X509_issuer_name_cmp(const X509 *a, const X509 *b)
+	{
+	return(X509_NAME_cmp(a->cert_info->issuer,b->cert_info->issuer));
+	}
+
+int X509_subject_name_cmp(const X509 *a, const X509 *b)
+	{
+	return(X509_NAME_cmp(a->cert_info->subject,b->cert_info->subject));
+	}
+
+int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b)
+	{
+	return(X509_NAME_cmp(a->crl->issuer,b->crl->issuer));
+	}
+
+#ifndef OPENSSL_NO_SHA
+int X509_CRL_match(const X509_CRL *a, const X509_CRL *b)
+	{
+	return memcmp(a->sha1_hash, b->sha1_hash, 20);
+	}
+#endif
+
+X509_NAME *X509_get_issuer_name(X509 *a)
+	{
+	return(a->cert_info->issuer);
+	}
+
+unsigned long X509_issuer_name_hash(X509 *x)
+	{
+	return(X509_NAME_hash(x->cert_info->issuer));
+	}
+
+#ifndef OPENSSL_NO_MD5
+unsigned long X509_issuer_name_hash_old(X509 *x)
+	{
+	return(X509_NAME_hash_old(x->cert_info->issuer));
+	}
+#endif
+
+X509_NAME *X509_get_subject_name(X509 *a)
+	{
+	return(a->cert_info->subject);
+	}
+
+ASN1_INTEGER *X509_get_serialNumber(X509 *a)
+	{
+	return(a->cert_info->serialNumber);
+	}
+
+unsigned long X509_subject_name_hash(X509 *x)
+	{
+	return(X509_NAME_hash(x->cert_info->subject));
+	}
+
+#ifndef OPENSSL_NO_MD5
+unsigned long X509_subject_name_hash_old(X509 *x)
+	{
+	return(X509_NAME_hash_old(x->cert_info->subject));
+	}
+#endif
+
+#ifndef OPENSSL_NO_SHA
+/* Compare two certificates: they must be identical for
+ * this to work. NB: Although "cmp" operations are generally
+ * prototyped to take "const" arguments (eg. for use in
+ * STACKs), the way X509 handling is - these operations may
+ * involve ensuring the hashes are up-to-date and ensuring
+ * certain cert information is cached. So this is the point
+ * where the "depth-first" constification tree has to halt
+ * with an evil cast.
+ */
+int X509_cmp(const X509 *a, const X509 *b)
+{
+	int rv;
+	/* ensure hash is valid */
+	X509_check_purpose((X509 *)a, -1, 0);
+	X509_check_purpose((X509 *)b, -1, 0);
+
+	rv = memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH);
+	if (rv)
+		return rv;
+	/* Check for match against stored encoding too */
+	if (!a->cert_info->enc.modified && !b->cert_info->enc.modified)
+		{
+		rv = (int)(a->cert_info->enc.len - b->cert_info->enc.len);
+		if (rv)
+			return rv;
+		return memcmp(a->cert_info->enc.enc, b->cert_info->enc.enc,
+				a->cert_info->enc.len);
+		}
+	return rv;
+}
+#endif
+
+
+int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b)
+	{
+	int ret;
+
+	/* Ensure canonical encoding is present and up to date */
+
+	if (!a->canon_enc || a->modified)
+		{
+		ret = i2d_X509_NAME((X509_NAME *)a, NULL);
+		if (ret < 0)
+			return -2;
+		}
+
+	if (!b->canon_enc || b->modified)
+		{
+		ret = i2d_X509_NAME((X509_NAME *)b, NULL);
+		if (ret < 0)
+			return -2;
+		}
+
+	ret = a->canon_enclen - b->canon_enclen;
+
+	if (ret)
+		return ret;
+
+	return memcmp(a->canon_enc, b->canon_enc, a->canon_enclen);
+
+	}
+
+unsigned long X509_NAME_hash(X509_NAME *x)
+	{
+	unsigned long ret=0;
+	unsigned char md[SHA_DIGEST_LENGTH];
+
+	/* Make sure X509_NAME structure contains valid cached encoding */
+	i2d_X509_NAME(x,NULL);
+	if (!EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, EVP_sha1(),
+		NULL))
+		return 0;
+
+	ret=(	((unsigned long)md[0]     )|((unsigned long)md[1]<<8L)|
+		((unsigned long)md[2]<<16L)|((unsigned long)md[3]<<24L)
+		)&0xffffffffL;
+	return(ret);
+	}
+
+
+#ifndef OPENSSL_NO_MD5
+/* I now DER encode the name and hash it.  Since I cache the DER encoding,
+ * this is reasonably efficient. */
+
+unsigned long X509_NAME_hash_old(X509_NAME *x)
+	{
+	EVP_MD_CTX md_ctx;
+	unsigned long ret=0;
+	unsigned char md[16];
+
+	/* Make sure X509_NAME structure contains valid cached encoding */
+	i2d_X509_NAME(x,NULL);
+	EVP_MD_CTX_init(&md_ctx);
+	/* EVP_MD_CTX_set_flags(&md_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); */
+	if (EVP_DigestInit_ex(&md_ctx, EVP_md5(), NULL)
+	    && EVP_DigestUpdate(&md_ctx, x->bytes->data, x->bytes->length)
+	    && EVP_DigestFinal_ex(&md_ctx,md,NULL))
+		ret=(((unsigned long)md[0]     )|((unsigned long)md[1]<<8L)|
+		     ((unsigned long)md[2]<<16L)|((unsigned long)md[3]<<24L)
+		     )&0xffffffffL;
+	EVP_MD_CTX_cleanup(&md_ctx);
+
+	return(ret);
+	}
+#endif
+
+/* Search a stack of X509 for a match */
+X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk, X509_NAME *name,
+		ASN1_INTEGER *serial)
+	{
+	size_t i;
+	X509_CINF cinf;
+	X509 x,*x509=NULL;
+
+	if(!sk) return NULL;
+
+	x.cert_info= &cinf;
+	cinf.serialNumber=serial;
+	cinf.issuer=name;
+
+	for (i=0; i<sk_X509_num(sk); i++)
+		{
+		x509=sk_X509_value(sk,i);
+		if (X509_issuer_and_serial_cmp(x509,&x) == 0)
+			return(x509);
+		}
+	return(NULL);
+	}
+
+X509 *X509_find_by_subject(STACK_OF(X509) *sk, X509_NAME *name)
+	{
+	X509 *x509;
+	size_t i;
+
+	for (i=0; i<sk_X509_num(sk); i++)
+		{
+		x509=sk_X509_value(sk,i);
+		if (X509_NAME_cmp(X509_get_subject_name(x509),name) == 0)
+			return(x509);
+		}
+	return(NULL);
+	}
+
+EVP_PKEY *X509_get_pubkey(X509 *x)
+	{
+	if ((x == NULL) || (x->cert_info == NULL))
+		return(NULL);
+	return(X509_PUBKEY_get(x->cert_info->key));
+	}
+
+ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x)
+	{
+	if(!x) return NULL;
+	return x->cert_info->key->public_key;
+	}
+
+
+int X509_check_private_key(X509 *x, EVP_PKEY *k)
+	{
+	EVP_PKEY *xk;
+	int ret;
+
+	xk=X509_get_pubkey(x);
+
+	if (xk)
+		ret = EVP_PKEY_cmp(xk, k);
+	else
+		ret = -2;
+
+	switch (ret)
+		{
+	case 1:
+		break;
+	case 0:
+		OPENSSL_PUT_ERROR(X509, X509_check_private_key, X509_R_KEY_VALUES_MISMATCH);
+		break;
+	case -1:
+		OPENSSL_PUT_ERROR(X509, X509_check_private_key, X509_R_KEY_TYPE_MISMATCH);
+		break;
+	case -2:
+	        OPENSSL_PUT_ERROR(X509, X509_check_private_key, X509_R_UNKNOWN_KEY_TYPE);
+		}
+	if (xk)
+		EVP_PKEY_free(xk);
+	if (ret > 0)
+		return 1;
+	return 0;
+	}
+
+/* Check a suite B algorithm is permitted: pass in a public key and
+ * the NID of its signature (or 0 if no signature). The pflags is
+ * a pointer to a flags field which must contain the suite B verification
+ * flags.
+ */
+
+#ifndef OPENSSL_NO_EC
+
+static int check_suite_b(EVP_PKEY *pkey, int sign_nid, unsigned long *pflags)
+	{
+	const EC_GROUP *grp = NULL;
+	int curve_nid;
+	if (pkey && pkey->type == EVP_PKEY_EC)
+		grp = EC_KEY_get0_group(pkey->pkey.ec);
+	if (!grp)
+		return X509_V_ERR_SUITE_B_INVALID_ALGORITHM;
+	curve_nid = EC_GROUP_get_curve_name(grp);
+	/* Check curve is consistent with LOS */
+	if (curve_nid == NID_secp384r1) /* P-384 */
+		{
+		/* Check signature algorithm is consistent with
+		 * curve.
+		 */
+		if (sign_nid != -1 && sign_nid != NID_ecdsa_with_SHA384)
+			return X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM;
+		if (!(*pflags & X509_V_FLAG_SUITEB_192_LOS))
+			return X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED;
+		/* If we encounter P-384 we cannot use P-256 later */
+		*pflags &= ~X509_V_FLAG_SUITEB_128_LOS_ONLY;
+		}
+	else if (curve_nid == NID_X9_62_prime256v1) /* P-256 */
+		{
+		if (sign_nid != -1 && sign_nid != NID_ecdsa_with_SHA256)
+			return X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM;
+		if (!(*pflags & X509_V_FLAG_SUITEB_128_LOS_ONLY))
+			return X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED;
+		}
+	else
+		return X509_V_ERR_SUITE_B_INVALID_CURVE;
+
+	return X509_V_OK;
+	}
+
+int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain,
+							unsigned long flags)
+	{
+	int rv, sign_nid;
+	size_t i;
+	EVP_PKEY *pk = NULL;
+	unsigned long tflags;
+	if (!(flags & X509_V_FLAG_SUITEB_128_LOS))
+		return X509_V_OK;
+	tflags = flags;
+	/* If no EE certificate passed in must be first in chain */
+	if (x == NULL)
+		{
+		x = sk_X509_value(chain, 0);
+		i = 1;
+		}
+	else
+		i = 0;
+
+	if (X509_get_version(x) != 2)
+		{
+		rv = X509_V_ERR_SUITE_B_INVALID_VERSION;
+		/* Correct error depth */
+		i = 0;
+		goto end;
+		}
+
+	pk = X509_get_pubkey(x);
+	/* Check EE key only */
+	rv = check_suite_b(pk, -1, &tflags);
+	if (rv != X509_V_OK)
+		{
+		/* Correct error depth */
+		i = 0;
+		goto end;
+		}
+	for(; i < sk_X509_num(chain); i++)
+		{
+		sign_nid = X509_get_signature_nid(x);
+		x = sk_X509_value(chain, i);
+		if (X509_get_version(x) != 2)
+			{
+			rv = X509_V_ERR_SUITE_B_INVALID_VERSION;
+			goto end;
+			}
+		EVP_PKEY_free(pk);
+		pk = X509_get_pubkey(x);
+		rv = check_suite_b(pk, sign_nid, &tflags);
+		if (rv != X509_V_OK)
+			goto end;
+		}
+
+	/* Final check: root CA signature */
+	rv = check_suite_b(pk, X509_get_signature_nid(x), &tflags);
+	end:
+	if (pk)
+		EVP_PKEY_free(pk);
+	if (rv != X509_V_OK)
+		{
+		/* Invalid signature or LOS errors are for previous cert */
+		if ((rv == X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM
+		    || rv == X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED) && i)
+			i--;
+		/* If we have LOS error and flags changed then we are signing
+		 * P-384 with P-256. Use more meaninggul error.
+		 */
+		if (rv == X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED && flags != tflags)
+			rv = X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256;
+		if (perror_depth)
+			*perror_depth = i;
+		}
+	return rv;
+	}
+
+int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk, unsigned long flags)
+	{
+	int sign_nid;
+	if (!(flags & X509_V_FLAG_SUITEB_128_LOS))
+		return X509_V_OK;
+	sign_nid = OBJ_obj2nid(crl->crl->sig_alg->algorithm);
+	return check_suite_b(pk, sign_nid, &flags);
+	}
+
+#else
+int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain,
+							unsigned long flags)
+	{
+	return 0;
+	}
+
+int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk, unsigned long flags)
+	{
+	return 0;
+	}
+
+#endif
+/* Not strictly speaking an "up_ref" as a STACK doesn't have a reference
+ * count but it has the same effect by duping the STACK and upping the ref
+ * of each X509 structure.
+ */
+STACK_OF(X509) *X509_chain_up_ref(STACK_OF(X509) *chain)
+	{
+	STACK_OF(X509) *ret;
+	size_t i;
+	ret = sk_X509_dup(chain);
+	for (i = 0; i < sk_X509_num(ret); i++)
+		{
+		X509 *x = sk_X509_value(ret, i);
+		CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+		}
+	return ret;
+	}
diff --git a/crypto/x509/x509_d2.c b/crypto/x509/x509_d2.c
new file mode 100644
index 0000000..2161d85
--- /dev/null
+++ b/crypto/x509/x509_d2.c
@@ -0,0 +1,105 @@
+/* crypto/x509/x509_d2.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/err.h>
+#include <openssl/x509.h>
+
+
+#ifndef OPENSSL_NO_STDIO
+int X509_STORE_set_default_paths(X509_STORE *ctx)
+	{
+	X509_LOOKUP *lookup;
+
+	lookup=X509_STORE_add_lookup(ctx,X509_LOOKUP_file());
+	if (lookup == NULL) return(0);
+	X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
+
+	lookup=X509_STORE_add_lookup(ctx,X509_LOOKUP_hash_dir());
+	if (lookup == NULL) return(0);
+	X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
+	
+	/* clear any errors */
+	ERR_clear_error();
+
+	return(1);
+	}
+
+int X509_STORE_load_locations(X509_STORE *ctx, const char *file,
+		const char *path)
+	{
+	X509_LOOKUP *lookup;
+
+	if (file != NULL)
+		{
+		lookup=X509_STORE_add_lookup(ctx,X509_LOOKUP_file());
+		if (lookup == NULL) return(0);
+		if (X509_LOOKUP_load_file(lookup,file,X509_FILETYPE_PEM) != 1)
+		    return(0);
+		}
+	if (path != NULL)
+		{
+		lookup=X509_STORE_add_lookup(ctx,X509_LOOKUP_hash_dir());
+		if (lookup == NULL) return(0);
+		if (X509_LOOKUP_add_dir(lookup,path,X509_FILETYPE_PEM) != 1)
+		    return(0);
+		}
+	if ((path == NULL) && (file == NULL))
+		return(0);
+	return(1);
+	}
+
+#endif
diff --git a/crypto/x509/x509_def.c b/crypto/x509/x509_def.c
new file mode 100644
index 0000000..dbae289
--- /dev/null
+++ b/crypto/x509/x509_def.c
@@ -0,0 +1,88 @@
+/* crypto/x509/x509_def.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+
+/* TODO(fork): cleanup */
+
+#define OPENSSLDIR "/etc/ssl"
+#define X509_CERT_AREA		OPENSSLDIR
+#define X509_CERT_DIR		OPENSSLDIR "/certs"
+#define X509_CERT_FILE		OPENSSLDIR "/cert.pem"
+#define X509_PRIVATE_DIR	OPENSSLDIR "/private"
+#define X509_CERT_DIR_EVP        "SSL_CERT_DIR"
+#define X509_CERT_FILE_EVP       "SSL_CERT_FILE"
+
+const char *X509_get_default_private_dir(void)
+	{ return(X509_PRIVATE_DIR); }
+	
+const char *X509_get_default_cert_area(void)
+	{ return(X509_CERT_AREA); }
+
+const char *X509_get_default_cert_dir(void)
+	{ return(X509_CERT_DIR); }
+
+const char *X509_get_default_cert_file(void)
+	{ return(X509_CERT_FILE); }
+
+const char *X509_get_default_cert_dir_env(void)
+	{ return(X509_CERT_DIR_EVP); }
+
+const char *X509_get_default_cert_file_env(void)
+	{ return(X509_CERT_FILE_EVP); }
+
diff --git a/crypto/x509/x509_error.c b/crypto/x509/x509_error.c
new file mode 100644
index 0000000..53d2faf
--- /dev/null
+++ b/crypto/x509/x509_error.c
@@ -0,0 +1,125 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "x509.h"
+
+const ERR_STRING_DATA X509_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_X509, X509_F_ASN1_digest, 0), "ASN1_digest"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_ASN1_generate_v3, 0), "ASN1_generate_v3"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_ASN1_item_sign_ctx, 0), "ASN1_item_sign_ctx"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_ASN1_item_verify, 0), "ASN1_item_verify"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_ASN1_sign, 0), "ASN1_sign"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_NETSCAPE_SPKI_b64_decode, 0), "NETSCAPE_SPKI_b64_decode"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_NETSCAPE_SPKI_b64_encode, 0), "NETSCAPE_SPKI_b64_encode"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_PKCS7_get_certificates, 0), "PKCS7_get_certificates"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_create_by_NID, 0), "X509_ATTRIBUTE_create_by_NID"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_create_by_OBJ, 0), "X509_ATTRIBUTE_create_by_OBJ"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_create_by_txt, 0), "X509_ATTRIBUTE_create_by_txt"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_get0_data, 0), "X509_ATTRIBUTE_get0_data"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_set1_data, 0), "X509_ATTRIBUTE_set1_data"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_CRL_add0_revoked, 0), "X509_CRL_add0_revoked"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_CRL_diff, 0), "X509_CRL_diff"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_CRL_print_fp, 0), "X509_CRL_print_fp"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_EXTENSION_create_by_NID, 0), "X509_EXTENSION_create_by_NID"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_EXTENSION_create_by_OBJ, 0), "X509_EXTENSION_create_by_OBJ"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_INFO_new, 0), "X509_INFO_new"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ENTRY_create_by_NID, 0), "X509_NAME_ENTRY_create_by_NID"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ENTRY_create_by_txt, 0), "X509_NAME_ENTRY_create_by_txt"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ENTRY_set_object, 0), "X509_NAME_ENTRY_set_object"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_add_entry, 0), "X509_NAME_add_entry"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_oneline, 0), "X509_NAME_oneline"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_print, 0), "X509_NAME_print"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_PKEY_new, 0), "X509_PKEY_new"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_PUBKEY_get, 0), "X509_PUBKEY_get"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_PUBKEY_set, 0), "X509_PUBKEY_set"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_REQ_check_private_key, 0), "X509_REQ_check_private_key"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_REQ_to_X509, 0), "X509_REQ_to_X509"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_get1_issuer, 0), "X509_STORE_CTX_get1_issuer"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_init, 0), "X509_STORE_CTX_init"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_new, 0), "X509_STORE_CTX_new"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_purpose_inherit, 0), "X509_STORE_CTX_purpose_inherit"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_add_cert, 0), "X509_STORE_add_cert"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_add_crl, 0), "X509_STORE_add_crl"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_TRUST_add, 0), "X509_TRUST_add"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_TRUST_set, 0), "X509_TRUST_set"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_check_private_key, 0), "X509_check_private_key"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_get_pubkey_parameters, 0), "X509_get_pubkey_parameters"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_load_cert_crl_file, 0), "X509_load_cert_crl_file"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_load_cert_file, 0), "X509_load_cert_file"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_load_crl_file, 0), "X509_load_crl_file"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_print_ex_fp, 0), "X509_print_ex_fp"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_to_X509_REQ, 0), "X509_to_X509_REQ"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509_verify_cert, 0), "X509_verify_cert"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509at_add1_attr, 0), "X509at_add1_attr"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_X509v3_add_ext, 0), "X509v3_add_ext"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_add_cert_dir, 0), "add_cert_dir"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_append_exp, 0), "append_exp"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_asn1_cb, 0), "asn1_cb"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_asn1_str2type, 0), "asn1_str2type"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_bitstr_cb, 0), "bitstr_cb"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_by_file_ctrl, 0), "by_file_ctrl"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_check_policy, 0), "check_policy"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_d2i_X509_PKEY, 0), "d2i_X509_PKEY"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_dir_ctrl, 0), "dir_ctrl"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_get_cert_by_subject, 0), "get_cert_by_subject"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_i2d_DSA_PUBKEY, 0), "i2d_DSA_PUBKEY"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_i2d_EC_PUBKEY, 0), "i2d_EC_PUBKEY"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_i2d_PrivateKey, 0), "i2d_PrivateKey"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_i2d_RSA_PUBKEY, 0), "i2d_RSA_PUBKEY"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_parse_tagging, 0), "parse_tagging"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_x509_name_encode, 0), "x509_name_encode"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_x509_name_ex_d2i, 0), "x509_name_ex_d2i"},
+  {ERR_PACK(ERR_LIB_X509, X509_F_x509_name_ex_new, 0), "x509_name_ex_new"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_AKID_MISMATCH), "AKID_MISMATCH"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_BAD_PKCS7_VERSION), "BAD_PKCS7_VERSION"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_BAD_X509_FILETYPE), "BAD_X509_FILETYPE"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_BASE64_DECODE_ERROR), "BASE64_DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_CANT_CHECK_DH_KEY), "CANT_CHECK_DH_KEY"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_CERT_ALREADY_IN_HASH_TABLE), "CERT_ALREADY_IN_HASH_TABLE"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_CONTEXT_NOT_INITIALISED), "CONTEXT_NOT_INITIALISED"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_CRL_ALREADY_DELTA), "CRL_ALREADY_DELTA"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_CRL_VERIFY_FAILURE), "CRL_VERIFY_FAILURE"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED), "DIGEST_AND_KEY_TYPE_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_ERR_ASN1_LIB), "ERR_ASN1_LIB"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_IDP_MISMATCH), "IDP_MISMATCH"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_DIRECTORY), "INVALID_DIRECTORY"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_FIELD_NAME), "INVALID_FIELD_NAME"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_TRUST), "INVALID_TRUST"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_ISSUER_MISMATCH), "ISSUER_MISMATCH"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_KEY_TYPE_MISMATCH), "KEY_TYPE_MISMATCH"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_KEY_VALUES_MISMATCH), "KEY_VALUES_MISMATCH"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_LOADING_CERT_DIR), "LOADING_CERT_DIR"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_LOADING_DEFAULTS), "LOADING_DEFAULTS"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_METHOD_NOT_SUPPORTED), "METHOD_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_NEWER_CRL_NOT_NEWER), "NEWER_CRL_NOT_NEWER"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_NOT_PKCS7_SIGNED_DATA), "NOT_PKCS7_SIGNED_DATA"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_NO_CERTIFICATES_INCLUDED), "NO_CERTIFICATES_INCLUDED"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY), "NO_CERT_SET_FOR_US_TO_VERIFY"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_NO_CRL_NUMBER), "NO_CRL_NUMBER"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_PUBLIC_KEY_DECODE_ERROR), "PUBLIC_KEY_DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_PUBLIC_KEY_ENCODE_ERROR), "PUBLIC_KEY_ENCODE_ERROR"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_SHOULD_RETRY), "SHOULD_RETRY"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN), "UNABLE_TO_FIND_PARAMETERS_IN_CHAIN"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY), "UNABLE_TO_GET_CERTS_PUBLIC_KEY"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_KEY_TYPE), "UNKNOWN_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_NID), "UNKNOWN_NID"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_PURPOSE_ID), "UNKNOWN_PURPOSE_ID"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_TRUST_ID), "UNKNOWN_TRUST_ID"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNSUPPORTED_ALGORITHM), "UNSUPPORTED_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_WRONG_LOOKUP_TYPE), "WRONG_LOOKUP_TYPE"},
+  {ERR_PACK(ERR_LIB_X509, 0, X509_R_WRONG_TYPE), "WRONG_TYPE"},
+  {0, NULL},
+};
diff --git a/crypto/x509/x509_ext.c b/crypto/x509/x509_ext.c
new file mode 100644
index 0000000..2f1e0c5
--- /dev/null
+++ b/crypto/x509/x509_ext.c
@@ -0,0 +1,206 @@
+/* crypto/x509/x509_ext.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+int X509_CRL_get_ext_count(X509_CRL *x)
+	{
+	return(X509v3_get_ext_count(x->crl->extensions));
+	}
+
+int X509_CRL_get_ext_by_NID(X509_CRL *x, int nid, int lastpos)
+	{
+	return(X509v3_get_ext_by_NID(x->crl->extensions,nid,lastpos));
+	}
+
+int X509_CRL_get_ext_by_OBJ(X509_CRL *x, ASN1_OBJECT *obj, int lastpos)
+	{
+	return(X509v3_get_ext_by_OBJ(x->crl->extensions,obj,lastpos));
+	}
+
+int X509_CRL_get_ext_by_critical(X509_CRL *x, int crit, int lastpos)
+	{
+	return(X509v3_get_ext_by_critical(x->crl->extensions,crit,lastpos));
+	}
+
+X509_EXTENSION *X509_CRL_get_ext(X509_CRL *x, int loc)
+	{
+	return(X509v3_get_ext(x->crl->extensions,loc));
+	}
+
+X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc)
+	{
+	return(X509v3_delete_ext(x->crl->extensions,loc));
+	}
+
+void *X509_CRL_get_ext_d2i(X509_CRL *x, int nid, int *crit, int *idx)
+{
+	return X509V3_get_d2i(x->crl->extensions, nid, crit, idx);
+}
+
+int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit,
+							unsigned long flags)
+{
+	return X509V3_add1_i2d(&x->crl->extensions, nid, value, crit, flags);
+}
+
+int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc)
+	{
+	return(X509v3_add_ext(&(x->crl->extensions),ex,loc) != NULL);
+	}
+
+int X509_get_ext_count(X509 *x)
+	{
+	return(X509v3_get_ext_count(x->cert_info->extensions));
+	}
+
+int X509_get_ext_by_NID(X509 *x, int nid, int lastpos)
+	{
+	return(X509v3_get_ext_by_NID(x->cert_info->extensions,nid,lastpos));
+	}
+
+int X509_get_ext_by_OBJ(X509 *x, ASN1_OBJECT *obj, int lastpos)
+	{
+	return(X509v3_get_ext_by_OBJ(x->cert_info->extensions,obj,lastpos));
+	}
+
+int X509_get_ext_by_critical(X509 *x, int crit, int lastpos)
+	{
+	return(X509v3_get_ext_by_critical(x->cert_info->extensions,crit,lastpos));
+	}
+
+X509_EXTENSION *X509_get_ext(X509 *x, int loc)
+	{
+	return(X509v3_get_ext(x->cert_info->extensions,loc));
+	}
+
+X509_EXTENSION *X509_delete_ext(X509 *x, int loc)
+	{
+	return(X509v3_delete_ext(x->cert_info->extensions,loc));
+	}
+
+int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc)
+	{
+	return(X509v3_add_ext(&(x->cert_info->extensions),ex,loc) != NULL);
+	}
+
+void *X509_get_ext_d2i(X509 *x, int nid, int *crit, int *idx)
+{
+	return X509V3_get_d2i(x->cert_info->extensions, nid, crit, idx);
+}
+
+int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
+							unsigned long flags)
+{
+	return X509V3_add1_i2d(&x->cert_info->extensions, nid, value, crit,
+							flags);
+}
+
+int X509_REVOKED_get_ext_count(X509_REVOKED *x)
+	{
+	return(X509v3_get_ext_count(x->extensions));
+	}
+
+int X509_REVOKED_get_ext_by_NID(X509_REVOKED *x, int nid, int lastpos)
+	{
+	return(X509v3_get_ext_by_NID(x->extensions,nid,lastpos));
+	}
+
+int X509_REVOKED_get_ext_by_OBJ(X509_REVOKED *x, ASN1_OBJECT *obj,
+	     int lastpos)
+	{
+	return(X509v3_get_ext_by_OBJ(x->extensions,obj,lastpos));
+	}
+
+int X509_REVOKED_get_ext_by_critical(X509_REVOKED *x, int crit, int lastpos)
+	{
+	return(X509v3_get_ext_by_critical(x->extensions,crit,lastpos));
+	}
+
+X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *x, int loc)
+	{
+	return(X509v3_get_ext(x->extensions,loc));
+	}
+
+X509_EXTENSION *X509_REVOKED_delete_ext(X509_REVOKED *x, int loc)
+	{
+	return(X509v3_delete_ext(x->extensions,loc));
+	}
+
+int X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex, int loc)
+	{
+	return(X509v3_add_ext(&(x->extensions),ex,loc) != NULL);
+	}
+
+void *X509_REVOKED_get_ext_d2i(X509_REVOKED *x, int nid, int *crit, int *idx)
+{
+	return X509V3_get_d2i(x->extensions, nid, crit, idx);
+}
+
+int X509_REVOKED_add1_ext_i2d(X509_REVOKED *x, int nid, void *value, int crit,
+							unsigned long flags)
+{
+	return X509V3_add1_i2d(&x->extensions, nid, value, crit, flags);
+}
+
+IMPLEMENT_ASN1_SET_OF(X509_EXTENSION)
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
new file mode 100644
index 0000000..3cf2d81
--- /dev/null
+++ b/crypto/x509/x509_lu.c
@@ -0,0 +1,742 @@
+/* crypto/x509/x509_lu.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/err.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method)
+	{
+	X509_LOOKUP *ret;
+
+	ret=(X509_LOOKUP *)OPENSSL_malloc(sizeof(X509_LOOKUP));
+	if (ret == NULL) return NULL;
+
+	ret->init=0;
+	ret->skip=0;
+	ret->method=method;
+	ret->method_data=NULL;
+	ret->store_ctx=NULL;
+	if ((method->new_item != NULL) && !method->new_item(ret))
+		{
+		OPENSSL_free(ret);
+		return NULL;
+		}
+	return ret;
+	}
+
+void X509_LOOKUP_free(X509_LOOKUP *ctx)
+	{
+	if (ctx == NULL) return;
+	if (	(ctx->method != NULL) &&
+		(ctx->method->free != NULL))
+		(*ctx->method->free)(ctx);
+	OPENSSL_free(ctx);
+	}
+
+int X509_LOOKUP_init(X509_LOOKUP *ctx)
+	{
+	if (ctx->method == NULL) return 0;
+	if (ctx->method->init != NULL)
+		return ctx->method->init(ctx);
+	else
+		return 1;
+	}
+
+int X509_LOOKUP_shutdown(X509_LOOKUP *ctx)
+	{
+	if (ctx->method == NULL) return 0;
+	if (ctx->method->shutdown != NULL)
+		return ctx->method->shutdown(ctx);
+	else
+		return 1;
+	}
+
+int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
+	     char **ret)
+	{
+	if (ctx->method == NULL) return -1;
+	if (ctx->method->ctrl != NULL)
+		return ctx->method->ctrl(ctx,cmd,argc,argl,ret);
+	else
+		return 1;
+	}
+
+int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, int type, X509_NAME *name,
+	     X509_OBJECT *ret)
+	{
+	if ((ctx->method == NULL) || (ctx->method->get_by_subject == NULL))
+		return X509_LU_FAIL;
+	if (ctx->skip) return 0;
+	return ctx->method->get_by_subject(ctx,type,name,ret);
+	}
+
+int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, int type, X509_NAME *name,
+	     ASN1_INTEGER *serial, X509_OBJECT *ret)
+	{
+	if ((ctx->method == NULL) ||
+		(ctx->method->get_by_issuer_serial == NULL))
+		return X509_LU_FAIL;
+	return ctx->method->get_by_issuer_serial(ctx,type,name,serial,ret);
+	}
+
+int X509_LOOKUP_by_fingerprint(X509_LOOKUP *ctx, int type,
+	     unsigned char *bytes, int len, X509_OBJECT *ret)
+	{
+	if ((ctx->method == NULL) || (ctx->method->get_by_fingerprint == NULL))
+		return X509_LU_FAIL;
+	return ctx->method->get_by_fingerprint(ctx,type,bytes,len,ret);
+	}
+
+int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, int type, char *str, int len,
+	     X509_OBJECT *ret)
+	{
+	if ((ctx->method == NULL) || (ctx->method->get_by_alias == NULL))
+		return X509_LU_FAIL;
+	return ctx->method->get_by_alias(ctx,type,str,len,ret);
+	}
+
+  
+static int x509_object_cmp(const X509_OBJECT **a, const X509_OBJECT **b)
+  	{
+ 	int ret;
+
+ 	ret=((*a)->type - (*b)->type);
+ 	if (ret) return ret;
+ 	switch ((*a)->type)
+ 		{
+ 	case X509_LU_X509:
+ 		ret=X509_subject_name_cmp((*a)->data.x509,(*b)->data.x509);
+ 		break;
+ 	case X509_LU_CRL:
+ 		ret=X509_CRL_cmp((*a)->data.crl,(*b)->data.crl);
+ 		break;
+	default:
+		/* abort(); */
+		return 0;
+		}
+	return ret;
+	}
+
+X509_STORE *X509_STORE_new(void)
+	{
+	X509_STORE *ret;
+
+	if ((ret=(X509_STORE *)OPENSSL_malloc(sizeof(X509_STORE))) == NULL)
+		return NULL;
+	ret->objs = sk_X509_OBJECT_new(x509_object_cmp);
+	ret->cache=1;
+	ret->get_cert_methods=sk_X509_LOOKUP_new_null();
+	ret->verify=0;
+	ret->verify_cb=0;
+
+	if ((ret->param = X509_VERIFY_PARAM_new()) == NULL)
+		return NULL;
+
+	ret->get_issuer = 0;
+	ret->check_issued = 0;
+	ret->check_revocation = 0;
+	ret->get_crl = 0;
+	ret->check_crl = 0;
+	ret->cert_crl = 0;
+	ret->lookup_certs = 0;
+	ret->lookup_crls = 0;
+	ret->cleanup = 0;
+
+	if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE, ret, &ret->ex_data))
+		{
+		sk_X509_OBJECT_free(ret->objs);
+		OPENSSL_free(ret);
+		return NULL;
+		}
+
+	ret->references=1;
+	return ret;
+	}
+
+static void cleanup(X509_OBJECT *a)
+	{
+	if (a->type == X509_LU_X509)
+		{
+		X509_free(a->data.x509);
+		}
+	else if (a->type == X509_LU_CRL)
+		{
+		X509_CRL_free(a->data.crl);
+		}
+	else
+		{
+		/* abort(); */
+		}
+
+	OPENSSL_free(a);
+	}
+
+void X509_STORE_free(X509_STORE *vfy)
+	{
+	int i;
+	size_t j;
+	STACK_OF(X509_LOOKUP) *sk;
+	X509_LOOKUP *lu;
+
+	if (vfy == NULL)
+	    return;
+
+	i=CRYPTO_add(&vfy->references,-1,CRYPTO_LOCK_X509_STORE);
+#ifdef REF_PRINT
+	REF_PRINT("X509_STORE",vfy);
+#endif
+	if (i > 0) return;
+#ifdef REF_CHECK
+	if (i < 0)
+		{
+		fprintf(stderr,"X509_STORE_free, bad reference count\n");
+		abort(); /* ok */
+		}
+#endif
+
+	sk=vfy->get_cert_methods;
+	for (j=0; j<sk_X509_LOOKUP_num(sk); j++)
+		{
+		lu=sk_X509_LOOKUP_value(sk,j);
+		X509_LOOKUP_shutdown(lu);
+		X509_LOOKUP_free(lu);
+		}
+	sk_X509_LOOKUP_free(sk);
+	sk_X509_OBJECT_pop_free(vfy->objs, cleanup);
+
+	CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE, vfy, &vfy->ex_data);
+	if (vfy->param)
+		X509_VERIFY_PARAM_free(vfy->param);
+	OPENSSL_free(vfy);
+	}
+
+X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m)
+	{
+	size_t i;
+	STACK_OF(X509_LOOKUP) *sk;
+	X509_LOOKUP *lu;
+
+	sk=v->get_cert_methods;
+	for (i=0; i<sk_X509_LOOKUP_num(sk); i++)
+		{
+		lu=sk_X509_LOOKUP_value(sk,i);
+		if (m == lu->method)
+			{
+			return lu;
+			}
+		}
+	/* a new one */
+	lu=X509_LOOKUP_new(m);
+	if (lu == NULL)
+		return NULL;
+	else
+		{
+		lu->store_ctx=v;
+		if (sk_X509_LOOKUP_push(v->get_cert_methods,lu))
+			return lu;
+		else
+			{
+			X509_LOOKUP_free(lu);
+			return NULL;
+			}
+		}
+	}
+
+int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
+	     X509_OBJECT *ret)
+	{
+	X509_STORE *ctx=vs->ctx;
+	X509_LOOKUP *lu;
+	X509_OBJECT stmp,*tmp;
+	int i,j;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+	tmp=X509_OBJECT_retrieve_by_subject(ctx->objs,type,name);
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+	if (tmp == NULL || type == X509_LU_CRL)
+		{
+		for (i=vs->current_method; i<(int)sk_X509_LOOKUP_num(ctx->get_cert_methods); i++)
+			{
+			lu=sk_X509_LOOKUP_value(ctx->get_cert_methods,i);
+			j=X509_LOOKUP_by_subject(lu,type,name,&stmp);
+			if (j < 0)
+				{
+				vs->current_method=j;
+				return j;
+				}
+			else if (j)
+				{
+				tmp= &stmp;
+				break;
+				}
+			}
+		vs->current_method=0;
+		if (tmp == NULL)
+			return 0;
+		}
+
+/*	if (ret->data.ptr != NULL)
+		X509_OBJECT_free_contents(ret); */
+
+	ret->type=tmp->type;
+	ret->data.ptr=tmp->data.ptr;
+
+	X509_OBJECT_up_ref_count(ret);
+
+	return 1;
+	}
+
+int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
+	{
+	X509_OBJECT *obj;
+	int ret=1;
+
+	if (x == NULL) return 0;
+	obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_STORE_add_cert, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	obj->type=X509_LU_X509;
+	obj->data.x509=x;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+
+	X509_OBJECT_up_ref_count(obj);
+
+	if (X509_OBJECT_retrieve_match(ctx->objs, obj))
+		{
+		X509_OBJECT_free_contents(obj);
+		OPENSSL_free(obj);
+		OPENSSL_PUT_ERROR(X509, X509_STORE_add_cert, X509_R_CERT_ALREADY_IN_HASH_TABLE);
+		ret=0;
+		} 
+	else sk_X509_OBJECT_push(ctx->objs, obj);
+
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+	return ret;
+	}
+
+int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
+	{
+	X509_OBJECT *obj;
+	int ret=1;
+
+	if (x == NULL) return 0;
+	obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_STORE_add_crl, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	obj->type=X509_LU_CRL;
+	obj->data.crl=x;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+
+	X509_OBJECT_up_ref_count(obj);
+
+	if (X509_OBJECT_retrieve_match(ctx->objs, obj))
+		{
+		X509_OBJECT_free_contents(obj);
+		OPENSSL_free(obj);
+		OPENSSL_PUT_ERROR(X509, X509_STORE_add_crl, X509_R_CERT_ALREADY_IN_HASH_TABLE);
+		ret=0;
+		}
+	else sk_X509_OBJECT_push(ctx->objs, obj);
+
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+	return ret;
+	}
+
+void X509_OBJECT_up_ref_count(X509_OBJECT *a)
+	{
+	switch (a->type)
+		{
+	case X509_LU_X509:
+		CRYPTO_add(&a->data.x509->references,1,CRYPTO_LOCK_X509);
+		break;
+	case X509_LU_CRL:
+		CRYPTO_add(&a->data.crl->references,1,CRYPTO_LOCK_X509_CRL);
+		break;
+		}
+	}
+
+void X509_OBJECT_free_contents(X509_OBJECT *a)
+	{
+	switch (a->type)
+		{
+	case X509_LU_X509:
+		X509_free(a->data.x509);
+		break;
+	case X509_LU_CRL:
+		X509_CRL_free(a->data.crl);
+		break;
+		}
+	}
+
+static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, int type,
+	     X509_NAME *name, int *pnmatch)
+	{
+	X509_OBJECT stmp;
+	X509 x509_s;
+	X509_CINF cinf_s;
+	X509_CRL crl_s;
+	X509_CRL_INFO crl_info_s;
+	size_t idx;
+
+	stmp.type=type;
+	switch (type)
+		{
+	case X509_LU_X509:
+		stmp.data.x509= &x509_s;
+		x509_s.cert_info= &cinf_s;
+		cinf_s.subject=name;
+		break;
+	case X509_LU_CRL:
+		stmp.data.crl= &crl_s;
+		crl_s.crl= &crl_info_s;
+		crl_info_s.issuer=name;
+		break;
+	default:
+		/* abort(); */
+		return -1;
+		}
+
+	idx = -1;
+	if (sk_X509_OBJECT_find(h, &idx, &stmp) && pnmatch)
+		{
+		int tidx;
+		const X509_OBJECT *tobj, *pstmp;
+		*pnmatch = 1;
+		pstmp = &stmp;
+		for (tidx = idx + 1; tidx < (int)sk_X509_OBJECT_num(h); tidx++)
+			{
+			tobj = sk_X509_OBJECT_value(h, tidx);
+			if (x509_object_cmp(&tobj, &pstmp))
+				break;
+			(*pnmatch)++;
+			}
+		}
+
+	return idx;
+	}
+
+
+int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type,
+	     X509_NAME *name)
+	{
+	return x509_object_idx_cnt(h, type, name, NULL);
+	}
+
+X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h, int type,
+	     X509_NAME *name)
+	{
+	int idx;
+	idx = X509_OBJECT_idx_by_subject(h, type, name);
+	if (idx==-1) return NULL;
+	return sk_X509_OBJECT_value(h, idx);
+	}
+
+STACK_OF(X509)* X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
+	{
+	int i, idx, cnt;
+	STACK_OF(X509) *sk;
+	X509 *x;
+	X509_OBJECT *obj;
+	sk = sk_X509_new_null();
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+	idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt);
+	if (idx < 0)
+		{
+		/* Nothing found in cache: do lookup to possibly add new
+		 * objects to cache
+		 */
+		X509_OBJECT xobj;
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+		if (!X509_STORE_get_by_subject(ctx, X509_LU_X509, nm, &xobj))
+			{
+			sk_X509_free(sk);
+			return NULL;
+			}
+		X509_OBJECT_free_contents(&xobj);
+		CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+		idx = x509_object_idx_cnt(ctx->ctx->objs,X509_LU_X509,nm, &cnt);
+		if (idx < 0)
+			{
+			CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+			sk_X509_free(sk);
+			return NULL;
+			}
+		}
+	for (i = 0; i < cnt; i++, idx++)
+		{
+		obj = sk_X509_OBJECT_value(ctx->ctx->objs, idx);
+		x = obj->data.x509;
+		CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+		if (!sk_X509_push(sk, x))
+			{
+			CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+			X509_free(x);
+			sk_X509_pop_free(sk, X509_free);
+			return NULL;
+			}
+		}
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+	return sk;
+
+	}
+
+STACK_OF(X509_CRL)* X509_STORE_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm)
+	{
+	int i, idx, cnt;
+	STACK_OF(X509_CRL) *sk;
+	X509_CRL *x;
+	X509_OBJECT *obj, xobj;
+	sk = sk_X509_CRL_new_null();
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+	/* Check cache first */
+	idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_CRL, nm, &cnt);
+
+	/* Always do lookup to possibly add new CRLs to cache
+	 */
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+	if (!X509_STORE_get_by_subject(ctx, X509_LU_CRL, nm, &xobj))
+		{
+		sk_X509_CRL_free(sk);
+		return NULL;
+		}
+	X509_OBJECT_free_contents(&xobj);
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+	idx = x509_object_idx_cnt(ctx->ctx->objs,X509_LU_CRL, nm, &cnt);
+	if (idx < 0)
+		{
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+		sk_X509_CRL_free(sk);
+		return NULL;
+		}
+
+	for (i = 0; i < cnt; i++, idx++)
+		{
+		obj = sk_X509_OBJECT_value(ctx->ctx->objs, idx);
+		x = obj->data.crl;
+		CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509_CRL);
+		if (!sk_X509_CRL_push(sk, x))
+			{
+			CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+			X509_CRL_free(x);
+			sk_X509_CRL_pop_free(sk, X509_CRL_free);
+			return NULL;
+			}
+		}
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+	return sk;
+	}
+
+X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, X509_OBJECT *x)
+	{
+	size_t idx, i;
+	X509_OBJECT *obj;
+
+	if (!sk_X509_OBJECT_find(h, &idx, x)) {
+		return NULL;
+	}
+	if ((x->type != X509_LU_X509) && (x->type != X509_LU_CRL))
+		return sk_X509_OBJECT_value(h, idx);
+	for (i = idx; i < sk_X509_OBJECT_num(h); i++)
+		{
+		obj = sk_X509_OBJECT_value(h, i);
+		if (x509_object_cmp((const X509_OBJECT **)&obj, (const X509_OBJECT **)&x))
+			return NULL;
+		if (x->type == X509_LU_X509)
+			{
+			if (!X509_cmp(obj->data.x509, x->data.x509))
+				return obj;
+			}
+		else if (x->type == X509_LU_CRL)
+			{
+			if (!X509_CRL_match(obj->data.crl, x->data.crl))
+				return obj;
+			}
+		else
+			return obj;
+		}
+	return NULL;
+	}
+
+
+/* Try to get issuer certificate from store. Due to limitations
+ * of the API this can only retrieve a single certificate matching
+ * a given subject name. However it will fill the cache with all
+ * matching certificates, so we can examine the cache for all
+ * matches.
+ *
+ * Return values are:
+ *  1 lookup successful.
+ *  0 certificate not found.
+ * -1 some other error.
+ */
+int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
+	{
+	X509_NAME *xn;
+	X509_OBJECT obj, *pobj;
+	int ok, idx, ret;
+	size_t i;
+	xn=X509_get_issuer_name(x);
+	ok=X509_STORE_get_by_subject(ctx,X509_LU_X509,xn,&obj);
+	if (ok != X509_LU_X509)
+		{
+		if (ok == X509_LU_RETRY)
+			{
+			X509_OBJECT_free_contents(&obj);
+			OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_get1_issuer, X509_R_SHOULD_RETRY);
+			return -1;
+			}
+		else if (ok != X509_LU_FAIL)
+			{
+			X509_OBJECT_free_contents(&obj);
+			/* not good :-(, break anyway */
+			return -1;
+			}
+		return 0;
+		}
+	/* If certificate matches all OK */
+	if (ctx->check_issued(ctx, x, obj.data.x509))
+		{
+		*issuer = obj.data.x509;
+		return 1;
+		}
+	X509_OBJECT_free_contents(&obj);
+
+	/* Else find index of first cert accepted by 'check_issued' */
+	ret = 0;
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+	idx = X509_OBJECT_idx_by_subject(ctx->ctx->objs, X509_LU_X509, xn);
+	if (idx != -1) /* should be true as we've had at least one match */
+		{
+		/* Look through all matching certs for suitable issuer */
+		for (i = idx; i < sk_X509_OBJECT_num(ctx->ctx->objs); i++)
+			{
+			pobj = sk_X509_OBJECT_value(ctx->ctx->objs, i);
+			/* See if we've run past the matches */
+			if (pobj->type != X509_LU_X509)
+				break;
+			if (X509_NAME_cmp(xn, X509_get_subject_name(pobj->data.x509)))
+				break;
+			if (ctx->check_issued(ctx, x, pobj->data.x509))
+				{
+				*issuer = pobj->data.x509;
+				X509_OBJECT_up_ref_count(pobj);
+				ret = 1;
+				break;
+				}
+			}
+		}
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+	return ret;
+	}
+
+int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags)
+	{
+	return X509_VERIFY_PARAM_set_flags(ctx->param, flags);
+	}
+
+int X509_STORE_set_depth(X509_STORE *ctx, int depth)
+	{
+	X509_VERIFY_PARAM_set_depth(ctx->param, depth);
+	return 1;
+	}
+
+int X509_STORE_set_purpose(X509_STORE *ctx, int purpose)
+	{
+	return X509_VERIFY_PARAM_set_purpose(ctx->param, purpose);
+	}
+
+int X509_STORE_set_trust(X509_STORE *ctx, int trust)
+	{
+	return X509_VERIFY_PARAM_set_trust(ctx->param, trust);
+	}
+
+int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *param)
+	{
+	return X509_VERIFY_PARAM_set1(ctx->param, param);
+	}
+
+void X509_STORE_set_verify_cb(X509_STORE *ctx,
+				  int (*verify_cb)(int, X509_STORE_CTX *))
+	{
+	ctx->verify_cb = verify_cb;
+	}
+
+void X509_STORE_set_lookup_crls_cb(X509_STORE *ctx,
+		STACK_OF(X509_CRL)* (*cb)(X509_STORE_CTX *ctx, X509_NAME *nm))
+	{
+	ctx->lookup_crls = cb;
+	}
+
+X509_STORE *X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx)
+	{
+	return ctx->ctx;
+	}
diff --git a/crypto/x509/x509_obj.c b/crypto/x509/x509_obj.c
new file mode 100644
index 0000000..434108c
--- /dev/null
+++ b/crypto/x509/x509_obj.c
@@ -0,0 +1,226 @@
+/* crypto/x509/x509_obj.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+char *X509_NAME_oneline(X509_NAME *a, char *buf, int len)
+	{
+	X509_NAME_ENTRY *ne;
+	size_t i;
+	int n,lold,l,l1,l2,num,j,type;
+	const char *s;
+	char *p;
+	unsigned char *q;
+	BUF_MEM *b=NULL;
+	static const char hex[17]="0123456789ABCDEF";
+	int gs_doit[4];
+	char tmp_buf[80];
+#ifdef CHARSET_EBCDIC
+	char ebcdic_buf[1024];
+#endif
+
+	if (buf == NULL)
+		{
+		if ((b=BUF_MEM_new()) == NULL) goto err;
+		if (!BUF_MEM_grow(b,200)) goto err;
+		b->data[0]='\0';
+		len=200;
+		}
+	if (a == NULL)
+	    {
+	    if(b)
+		{
+		buf=b->data;
+		OPENSSL_free(b);
+		}
+	    strncpy(buf,"NO X509_NAME",len);
+	    buf[len-1]='\0';
+	    return buf;
+	    }
+
+	len--; /* space for '\0' */
+	l=0;
+	for (i=0; i<sk_X509_NAME_ENTRY_num(a->entries); i++)
+		{
+		ne=sk_X509_NAME_ENTRY_value(a->entries,i);
+		n=OBJ_obj2nid(ne->object);
+		if ((n == NID_undef) || ((s=OBJ_nid2sn(n)) == NULL))
+			{
+			i2t_ASN1_OBJECT(tmp_buf,sizeof(tmp_buf),ne->object);
+			s=tmp_buf;
+			}
+		l1=strlen(s);
+
+		type=ne->value->type;
+		num=ne->value->length;
+		q=ne->value->data;
+#ifdef CHARSET_EBCDIC
+                if (type == V_ASN1_GENERALSTRING ||
+		    type == V_ASN1_VISIBLESTRING ||
+		    type == V_ASN1_PRINTABLESTRING ||
+		    type == V_ASN1_TELETEXSTRING ||
+		    type == V_ASN1_VISIBLESTRING ||
+		    type == V_ASN1_IA5STRING) {
+                        ascii2ebcdic(ebcdic_buf, q,
+				     (num > sizeof ebcdic_buf)
+				     ? sizeof ebcdic_buf : num);
+                        q=ebcdic_buf;
+		}
+#endif
+
+		if ((type == V_ASN1_GENERALSTRING) && ((num%4) == 0))
+			{
+			gs_doit[0]=gs_doit[1]=gs_doit[2]=gs_doit[3]=0;
+			for (j=0; j<num; j++)
+				if (q[j] != 0) gs_doit[j&3]=1;
+
+			if (gs_doit[0]|gs_doit[1]|gs_doit[2])
+				gs_doit[0]=gs_doit[1]=gs_doit[2]=gs_doit[3]=1;
+			else
+				{
+				gs_doit[0]=gs_doit[1]=gs_doit[2]=0;
+				gs_doit[3]=1;
+				}
+			}
+		else
+			gs_doit[0]=gs_doit[1]=gs_doit[2]=gs_doit[3]=1;
+
+		for (l2=j=0; j<num; j++)
+			{
+			if (!gs_doit[j&3]) continue;
+			l2++;
+#ifndef CHARSET_EBCDIC
+			if ((q[j] < ' ') || (q[j] > '~')) l2+=3;
+#else
+			if ((os_toascii[q[j]] < os_toascii[' ']) ||
+			    (os_toascii[q[j]] > os_toascii['~'])) l2+=3;
+#endif
+			}
+
+		lold=l;
+		l+=1+l1+1+l2;
+		if (b != NULL)
+			{
+			if (!BUF_MEM_grow(b,l+1)) goto err;
+			p= &(b->data[lold]);
+			}
+		else if (l > len)
+			{
+			break;
+			}
+		else
+			p= &(buf[lold]);
+		*(p++)='/';
+		memcpy(p,s,(unsigned int)l1); p+=l1;
+		*(p++)='=';
+
+#ifndef CHARSET_EBCDIC /* q was assigned above already. */
+		q=ne->value->data;
+#endif
+
+		for (j=0; j<num; j++)
+			{
+			if (!gs_doit[j&3]) continue;
+#ifndef CHARSET_EBCDIC
+			n=q[j];
+			if ((n < ' ') || (n > '~'))
+				{
+				*(p++)='\\';
+				*(p++)='x';
+				*(p++)=hex[(n>>4)&0x0f];
+				*(p++)=hex[n&0x0f];
+				}
+			else
+				*(p++)=n;
+#else
+			n=os_toascii[q[j]];
+			if ((n < os_toascii[' ']) ||
+			    (n > os_toascii['~']))
+				{
+				*(p++)='\\';
+				*(p++)='x';
+				*(p++)=hex[(n>>4)&0x0f];
+				*(p++)=hex[n&0x0f];
+				}
+			else
+				*(p++)=q[j];
+#endif
+			}
+		*p='\0';
+		}
+	if (b != NULL)
+		{
+		p=b->data;
+		OPENSSL_free(b);
+		}
+	else
+		p=buf;
+	if (i == 0)
+		*p = '\0';
+	return(p);
+err:
+	OPENSSL_PUT_ERROR(X509, X509_NAME_oneline, ERR_R_MALLOC_FAILURE);
+	if (b != NULL) BUF_MEM_free(b);
+	return(NULL);
+	}
+
diff --git a/crypto/x509/x509_r2x.c b/crypto/x509/x509_r2x.c
new file mode 100644
index 0000000..3c8e9c0
--- /dev/null
+++ b/crypto/x509/x509_r2x.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_PKEY *pkey)
+	{
+	X509 *ret=NULL;
+	X509_CINF *xi=NULL;
+	X509_NAME *xn;
+
+	if ((ret=X509_new()) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_REQ_to_X509, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	/* duplicate the request */
+	xi=ret->cert_info;
+
+	if (sk_X509_ATTRIBUTE_num(r->req_info->attributes) != 0)
+		{
+		if ((xi->version=M_ASN1_INTEGER_new()) == NULL) goto err;
+		if (!ASN1_INTEGER_set(xi->version,2)) goto err;
+/*		xi->extensions=ri->attributes; <- bad, should not ever be done
+		ri->attributes=NULL; */
+		}
+
+	xn=X509_REQ_get_subject_name(r);
+	if (X509_set_subject_name(ret,X509_NAME_dup(xn)) == 0)
+		goto err;
+	if (X509_set_issuer_name(ret,X509_NAME_dup(xn)) == 0)
+		goto err;
+
+	if (X509_gmtime_adj(xi->validity->notBefore,0) == NULL)
+		goto err;
+	if (X509_gmtime_adj(xi->validity->notAfter,(long)60*60*24*days) == NULL)
+		goto err;
+
+	X509_set_pubkey(ret,X509_REQ_get_pubkey(r));
+
+	if (!X509_sign(ret,pkey,EVP_md5()))
+		goto err;
+	if (0)
+		{
+err:
+		X509_free(ret);
+		ret=NULL;
+		}
+	return(ret);
+	}
+
diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c
new file mode 100644
index 0000000..ccaf729
--- /dev/null
+++ b/crypto/x509/x509_req.c
@@ -0,0 +1,316 @@
+/* crypto/x509/x509_req.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+
+X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
+	{
+	X509_REQ *ret;
+	X509_REQ_INFO *ri;
+	int i;
+	EVP_PKEY *pktmp;
+
+	ret=X509_REQ_new();
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_to_X509_REQ, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	ri=ret->req_info;
+
+	ri->version->length=1;
+	ri->version->data=(unsigned char *)OPENSSL_malloc(1);
+	if (ri->version->data == NULL) goto err;
+	ri->version->data[0]=0; /* version == 0 */
+
+	if (!X509_REQ_set_subject_name(ret,X509_get_subject_name(x)))
+		goto err;
+
+	pktmp = X509_get_pubkey(x);
+	i=X509_REQ_set_pubkey(ret,pktmp);
+	EVP_PKEY_free(pktmp);
+	if (!i) goto err;
+
+	if (pkey != NULL)
+		{
+		if (!X509_REQ_sign(ret,pkey,md))
+			goto err;
+		}
+	return(ret);
+err:
+	X509_REQ_free(ret);
+	return(NULL);
+	}
+
+EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req)
+	{
+	if ((req == NULL) || (req->req_info == NULL))
+		return(NULL);
+	return(X509_PUBKEY_get(req->req_info->pubkey));
+	}
+
+int X509_REQ_check_private_key(X509_REQ *x, EVP_PKEY *k)
+	{
+	EVP_PKEY *xk=NULL;
+	int ok=0;
+
+	xk=X509_REQ_get_pubkey(x);
+	switch (EVP_PKEY_cmp(xk, k))
+		{
+	case 1:
+		ok=1;
+		break;
+	case 0:
+		OPENSSL_PUT_ERROR(X509, X509_REQ_check_private_key, X509_R_KEY_VALUES_MISMATCH);
+		break;
+	case -1:
+		OPENSSL_PUT_ERROR(X509, X509_REQ_check_private_key, X509_R_KEY_TYPE_MISMATCH);
+		break;
+	case -2:
+#ifndef OPENSSL_NO_EC
+		if (k->type == EVP_PKEY_EC)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_REQ_check_private_key, ERR_R_EC_LIB);
+			break;
+			}
+#endif
+#ifndef OPENSSL_NO_DH
+		if (k->type == EVP_PKEY_DH)
+			{
+			/* No idea */
+			OPENSSL_PUT_ERROR(X509, X509_REQ_check_private_key, X509_R_CANT_CHECK_DH_KEY);
+			break;
+			}
+#endif
+	        OPENSSL_PUT_ERROR(X509, X509_REQ_check_private_key, X509_R_UNKNOWN_KEY_TYPE);
+		}
+
+	EVP_PKEY_free(xk);
+	return(ok);
+	}
+
+/* It seems several organisations had the same idea of including a list of
+ * extensions in a certificate request. There are at least two OIDs that are
+ * used and there may be more: so the list is configurable.
+ */
+
+static int ext_nid_list[] = { NID_ext_req, NID_ms_ext_req, NID_undef};
+
+static int *ext_nids = ext_nid_list;
+
+int X509_REQ_extension_nid(int req_nid)
+{
+	int i, nid;
+	for(i = 0; ; i++) {
+		nid = ext_nids[i];
+		if(nid == NID_undef) return 0;
+		else if (req_nid == nid) return 1;
+	}
+}
+
+int *X509_REQ_get_extension_nids(void)
+{
+	return ext_nids;
+}
+	
+void X509_REQ_set_extension_nids(int *nids)
+{
+	ext_nids = nids;
+}
+
+STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req)
+	{
+	X509_ATTRIBUTE *attr;
+	ASN1_TYPE *ext = NULL;
+	int idx, *pnid;
+	const unsigned char *p;
+
+	if ((req == NULL) || (req->req_info == NULL) || !ext_nids)
+		return(NULL);
+	for (pnid = ext_nids; *pnid != NID_undef; pnid++)
+		{
+		idx = X509_REQ_get_attr_by_NID(req, *pnid, -1);
+		if (idx == -1)
+			continue;
+		attr = X509_REQ_get_attr(req, idx);
+		if(attr->single) ext = attr->value.single;
+		else if(sk_ASN1_TYPE_num(attr->value.set))
+			ext = sk_ASN1_TYPE_value(attr->value.set, 0);
+		break;
+		}
+	if(!ext || (ext->type != V_ASN1_SEQUENCE))
+		return NULL;
+	p = ext->value.sequence->data;
+	return (STACK_OF(X509_EXTENSION) *)
+		ASN1_item_d2i(NULL, &p, ext->value.sequence->length,
+				ASN1_ITEM_rptr(X509_EXTENSIONS));
+}
+
+/* Add a STACK_OF extensions to a certificate request: allow alternative OIDs
+ * in case we want to create a non standard one.
+ */
+
+int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts,
+				int nid)
+{
+	ASN1_TYPE *at = NULL;
+	X509_ATTRIBUTE *attr = NULL;
+	if(!(at = ASN1_TYPE_new()) ||
+		!(at->value.sequence = ASN1_STRING_new())) goto err;
+
+	at->type = V_ASN1_SEQUENCE;
+	/* Generate encoding of extensions */
+	at->value.sequence->length = 
+			ASN1_item_i2d((ASN1_VALUE *)exts,
+				&at->value.sequence->data,
+				ASN1_ITEM_rptr(X509_EXTENSIONS));
+	if(!(attr = X509_ATTRIBUTE_new())) goto err;
+	if(!(attr->value.set = sk_ASN1_TYPE_new_null())) goto err;
+	if(!sk_ASN1_TYPE_push(attr->value.set, at)) goto err;
+	at = NULL;
+	attr->single = 0;
+	attr->object = (ASN1_OBJECT*) OBJ_nid2obj(nid);
+	if (!req->req_info->attributes)
+		{
+		if (!(req->req_info->attributes = sk_X509_ATTRIBUTE_new_null()))
+			goto err;
+		}
+	if(!sk_X509_ATTRIBUTE_push(req->req_info->attributes, attr)) goto err;
+	return 1;
+	err:
+	X509_ATTRIBUTE_free(attr);
+	ASN1_TYPE_free(at);
+	return 0;
+}
+/* This is the normal usage: use the "official" OID */
+int X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts)
+{
+	return X509_REQ_add_extensions_nid(req, exts, NID_ext_req);
+}
+
+/* Request attribute functions */
+
+int X509_REQ_get_attr_count(const X509_REQ *req)
+{
+	return X509at_get_attr_count(req->req_info->attributes);
+}
+
+int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid,
+			  int lastpos)
+{
+	return X509at_get_attr_by_NID(req->req_info->attributes, nid, lastpos);
+}
+
+int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, ASN1_OBJECT *obj,
+			  int lastpos)
+{
+	return X509at_get_attr_by_OBJ(req->req_info->attributes, obj, lastpos);
+}
+
+X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc)
+{
+	return X509at_get_attr(req->req_info->attributes, loc);
+}
+
+X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc)
+{
+	return X509at_delete_attr(req->req_info->attributes, loc);
+}
+
+int X509_REQ_add1_attr(X509_REQ *req, X509_ATTRIBUTE *attr)
+{
+	if(X509at_add1_attr(&req->req_info->attributes, attr)) return 1;
+	return 0;
+}
+
+int X509_REQ_add1_attr_by_OBJ(X509_REQ *req,
+			const ASN1_OBJECT *obj, int type,
+			const unsigned char *bytes, int len)
+{
+	if(X509at_add1_attr_by_OBJ(&req->req_info->attributes, obj,
+				type, bytes, len)) return 1;
+	return 0;
+}
+
+int X509_REQ_add1_attr_by_NID(X509_REQ *req,
+			int nid, int type,
+			const unsigned char *bytes, int len)
+{
+	if(X509at_add1_attr_by_NID(&req->req_info->attributes, nid,
+				type, bytes, len)) return 1;
+	return 0;
+}
+
+int X509_REQ_add1_attr_by_txt(X509_REQ *req,
+			const char *attrname, int type,
+			const unsigned char *bytes, int len)
+{
+	if(X509at_add1_attr_by_txt(&req->req_info->attributes, attrname,
+				type, bytes, len)) return 1;
+	return 0;
+}
diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c
new file mode 100644
index 0000000..4288571
--- /dev/null
+++ b/crypto/x509/x509_set.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/cipher.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+int X509_set_version(X509 *x, long version)
+	{
+	if (x == NULL) return(0);
+	if (x->cert_info->version == NULL)
+		{
+		if ((x->cert_info->version=M_ASN1_INTEGER_new()) == NULL)
+			return(0);
+		}
+	return(ASN1_INTEGER_set(x->cert_info->version,version));
+	}
+
+int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial)
+	{
+	ASN1_INTEGER *in;
+
+	if (x == NULL) return(0);
+	in=x->cert_info->serialNumber;
+	if (in != serial)
+		{
+		in=M_ASN1_INTEGER_dup(serial);
+		if (in != NULL)
+			{
+			M_ASN1_INTEGER_free(x->cert_info->serialNumber);
+			x->cert_info->serialNumber=in;
+			}
+		}
+	return(in != NULL);
+	}
+
+int X509_set_issuer_name(X509 *x, X509_NAME *name)
+	{
+	if ((x == NULL) || (x->cert_info == NULL)) return(0);
+	return(X509_NAME_set(&x->cert_info->issuer,name));
+	}
+
+int X509_set_subject_name(X509 *x, X509_NAME *name)
+	{
+	if ((x == NULL) || (x->cert_info == NULL)) return(0);
+	return(X509_NAME_set(&x->cert_info->subject,name));
+	}
+
+int X509_set_notBefore(X509 *x, const ASN1_TIME *tm)
+	{
+	ASN1_TIME *in;
+
+	if ((x == NULL) || (x->cert_info->validity == NULL)) return(0);
+	in=x->cert_info->validity->notBefore;
+	if (in != tm)
+		{
+		in=M_ASN1_TIME_dup(tm);
+		if (in != NULL)
+			{
+			M_ASN1_TIME_free(x->cert_info->validity->notBefore);
+			x->cert_info->validity->notBefore=in;
+			}
+		}
+	return(in != NULL);
+	}
+
+int X509_set_notAfter(X509 *x, const ASN1_TIME *tm)
+	{
+	ASN1_TIME *in;
+
+	if ((x == NULL) || (x->cert_info->validity == NULL)) return(0);
+	in=x->cert_info->validity->notAfter;
+	if (in != tm)
+		{
+		in=M_ASN1_TIME_dup(tm);
+		if (in != NULL)
+			{
+			M_ASN1_TIME_free(x->cert_info->validity->notAfter);
+			x->cert_info->validity->notAfter=in;
+			}
+		}
+	return(in != NULL);
+	}
+
+int X509_set_pubkey(X509 *x, EVP_PKEY *pkey)
+	{
+	if ((x == NULL) || (x->cert_info == NULL)) return(0);
+	return(X509_PUBKEY_set(&(x->cert_info->key),pkey));
+	}
+
+
+
diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c
new file mode 100644
index 0000000..e55b75b
--- /dev/null
+++ b/crypto/x509/x509_trs.c
@@ -0,0 +1,295 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999. */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static int tr_cmp(const X509_TRUST **a,
+		const X509_TRUST **b);
+static void trtable_free(X509_TRUST *p);
+
+static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags);
+static int trust_1oid(X509_TRUST *trust, X509 *x, int flags);
+static int trust_compat(X509_TRUST *trust, X509 *x, int flags);
+
+static int obj_trust(int id, X509 *x, int flags);
+static int (*default_trust)(int id, X509 *x, int flags) = obj_trust;
+
+/* WARNING: the following table should be kept in order of trust
+ * and without any gaps so we can just subtract the minimum trust
+ * value to get an index into the table
+ */
+
+static X509_TRUST trstandard[] = {
+{X509_TRUST_COMPAT, 0, trust_compat, "compatible", 0, NULL},
+{X509_TRUST_SSL_CLIENT, 0, trust_1oidany, "SSL Client", NID_client_auth, NULL},
+{X509_TRUST_SSL_SERVER, 0, trust_1oidany, "SSL Server", NID_server_auth, NULL},
+{X509_TRUST_EMAIL, 0, trust_1oidany, "S/MIME email", NID_email_protect, NULL},
+{X509_TRUST_OBJECT_SIGN, 0, trust_1oidany, "Object Signer", NID_code_sign, NULL},
+{X509_TRUST_OCSP_SIGN, 0, trust_1oid, "OCSP responder", NID_OCSP_sign, NULL},
+{X509_TRUST_OCSP_REQUEST, 0, trust_1oid, "OCSP request", NID_ad_OCSP, NULL},
+{X509_TRUST_TSA, 0, trust_1oidany, "TSA server", NID_time_stamp, NULL}
+};
+
+#define X509_TRUST_COUNT	(sizeof(trstandard)/sizeof(X509_TRUST))
+
+static STACK_OF(X509_TRUST) *trtable = NULL;
+
+static int tr_cmp(const X509_TRUST **a,
+		const X509_TRUST **b)
+{
+	return (*a)->trust - (*b)->trust;
+}
+
+int (*X509_TRUST_set_default(int (*trust)(int , X509 *, int)))(int, X509 *, int)
+{
+	int (*oldtrust)(int , X509 *, int);
+	oldtrust = default_trust;
+	default_trust = trust;
+	return oldtrust;
+}
+
+
+int X509_check_trust(X509 *x, int id, int flags)
+{
+	X509_TRUST *pt;
+	int idx;
+	if(id == -1) return 1;
+	/* We get this as a default value */
+	if (id == 0)
+		{
+		int rv;
+		rv = obj_trust(NID_anyExtendedKeyUsage, x, 0);
+		if (rv != X509_TRUST_UNTRUSTED)
+			return rv;
+		return trust_compat(NULL, x, 0);
+		}
+	idx = X509_TRUST_get_by_id(id);
+	if(idx == -1) return default_trust(id, x, flags);
+	pt = X509_TRUST_get0(idx);
+	return pt->check_trust(pt, x, flags);
+}
+
+int X509_TRUST_get_count(void)
+{
+	if(!trtable) return X509_TRUST_COUNT;
+	return sk_X509_TRUST_num(trtable) + X509_TRUST_COUNT;
+}
+
+X509_TRUST * X509_TRUST_get0(int idx)
+{
+	if(idx < 0) return NULL;
+	if(idx < (int)X509_TRUST_COUNT) return trstandard + idx;
+	return sk_X509_TRUST_value(trtable, idx - X509_TRUST_COUNT);
+}
+
+int X509_TRUST_get_by_id(int id)
+{
+	X509_TRUST tmp;
+	size_t idx;
+
+	if((id >= X509_TRUST_MIN) && (id <= X509_TRUST_MAX))
+				 return id - X509_TRUST_MIN;
+	tmp.trust = id;
+	if(!trtable) return -1;
+	if (!sk_X509_TRUST_find(trtable, &idx, &tmp)) {
+		return -1;
+	}
+	return idx + X509_TRUST_COUNT;
+}
+
+int X509_TRUST_set(int *t, int trust)
+{
+	if(X509_TRUST_get_by_id(trust) == -1) {
+		OPENSSL_PUT_ERROR(X509, X509_TRUST_set, X509_R_INVALID_TRUST);
+		return 0;
+	}
+	*t = trust;
+	return 1;
+}
+
+int X509_TRUST_add(int id, int flags, int (*ck)(X509_TRUST *, X509 *, int),
+					char *name, int arg1, void *arg2)
+{
+	int idx;
+	X509_TRUST *trtmp;
+	/* This is set according to what we change: application can't set it */
+	flags &= ~X509_TRUST_DYNAMIC;
+	/* This will always be set for application modified trust entries */
+	flags |= X509_TRUST_DYNAMIC_NAME;
+	/* Get existing entry if any */
+	idx = X509_TRUST_get_by_id(id);
+	/* Need a new entry */
+	if(idx == -1) {
+		if(!(trtmp = OPENSSL_malloc(sizeof(X509_TRUST)))) {
+			OPENSSL_PUT_ERROR(X509, X509_TRUST_add, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+		trtmp->flags = X509_TRUST_DYNAMIC;
+	} else trtmp = X509_TRUST_get0(idx);
+
+	/* OPENSSL_free existing name if dynamic */
+	if(trtmp->flags & X509_TRUST_DYNAMIC_NAME) OPENSSL_free(trtmp->name);
+	/* dup supplied name */
+	if(!(trtmp->name = BUF_strdup(name))) {
+		OPENSSL_PUT_ERROR(X509, X509_TRUST_add, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	/* Keep the dynamic flag of existing entry */
+	trtmp->flags &= X509_TRUST_DYNAMIC;
+	/* Set all other flags */
+	trtmp->flags |= flags;
+
+	trtmp->trust = id;
+	trtmp->check_trust = ck;
+	trtmp->arg1 = arg1;
+	trtmp->arg2 = arg2;
+
+	/* If its a new entry manage the dynamic table */
+	if(idx == -1) {
+		if(!trtable && !(trtable = sk_X509_TRUST_new(tr_cmp))) {
+			OPENSSL_PUT_ERROR(X509, X509_TRUST_add, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+		if (!sk_X509_TRUST_push(trtable, trtmp)) {
+			OPENSSL_PUT_ERROR(X509, X509_TRUST_add, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static void trtable_free(X509_TRUST *p)
+	{
+	if(!p) return;
+	if (p->flags & X509_TRUST_DYNAMIC) 
+		{
+		if (p->flags & X509_TRUST_DYNAMIC_NAME)
+			OPENSSL_free(p->name);
+		OPENSSL_free(p);
+		}
+	}
+
+void X509_TRUST_cleanup(void)
+{
+	unsigned int i;
+	for(i = 0; i < X509_TRUST_COUNT; i++) trtable_free(trstandard + i);
+	sk_X509_TRUST_pop_free(trtable, trtable_free);
+	trtable = NULL;
+}
+
+int X509_TRUST_get_flags(X509_TRUST *xp)
+{
+	return xp->flags;
+}
+
+char *X509_TRUST_get0_name(X509_TRUST *xp)
+{
+	return xp->name;
+}
+
+int X509_TRUST_get_trust(X509_TRUST *xp)
+{
+	return xp->trust;
+}
+
+static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags)
+{
+	if(x->aux && (x->aux->trust || x->aux->reject))
+		return obj_trust(trust->arg1, x, flags);
+	/* we don't have any trust settings: for compatibility
+	 * we return trusted if it is self signed
+	 */
+	return trust_compat(trust, x, flags);
+}
+
+static int trust_1oid(X509_TRUST *trust, X509 *x, int flags)
+{
+	if(x->aux) return obj_trust(trust->arg1, x, flags);
+	return X509_TRUST_UNTRUSTED;
+}
+
+static int trust_compat(X509_TRUST *trust, X509 *x, int flags)
+{
+	X509_check_purpose(x, -1, 0);
+	if(x->ex_flags & EXFLAG_SS) return X509_TRUST_TRUSTED;
+	else return X509_TRUST_UNTRUSTED;
+}
+
+static int obj_trust(int id, X509 *x, int flags)
+{
+	ASN1_OBJECT *obj;
+	size_t i;
+	X509_CERT_AUX *ax;
+	ax = x->aux;
+	if(!ax) return X509_TRUST_UNTRUSTED;
+	if(ax->reject) {
+		for(i = 0; i < sk_ASN1_OBJECT_num(ax->reject); i++) {
+			obj = sk_ASN1_OBJECT_value(ax->reject, i);
+			if(OBJ_obj2nid(obj) == id) return X509_TRUST_REJECTED;
+		}
+	}	
+	if(ax->trust) {
+		for(i = 0; i < sk_ASN1_OBJECT_num(ax->trust); i++) {
+			obj = sk_ASN1_OBJECT_value(ax->trust, i);
+			if(OBJ_obj2nid(obj) == id) return X509_TRUST_TRUSTED;
+		}
+	}
+	return X509_TRUST_UNTRUSTED;
+}
+
diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c
new file mode 100644
index 0000000..c286710
--- /dev/null
+++ b/crypto/x509/x509_txt.c
@@ -0,0 +1,209 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/buf.h>
+#include <openssl/cipher.h>
+#include <openssl/evp.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+const char *X509_verify_cert_error_string(long n)
+	{
+	static char buf[100];
+
+	switch ((int)n)
+		{
+	case X509_V_OK:
+		return("ok");
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+		return("unable to get issuer certificate");
+	case X509_V_ERR_UNABLE_TO_GET_CRL:
+		return("unable to get certificate CRL");
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+		return("unable to decrypt certificate's signature");
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+		return("unable to decrypt CRL's signature");
+	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+		return("unable to decode issuer public key");
+	case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+		return("certificate signature failure");
+	case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+		return("CRL signature failure");
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+		return("certificate is not yet valid");
+	case X509_V_ERR_CRL_NOT_YET_VALID:
+		return("CRL is not yet valid");
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+		return("certificate has expired");
+	case X509_V_ERR_CRL_HAS_EXPIRED:
+		return("CRL has expired");
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+		return("format error in certificate's notBefore field");
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+		return("format error in certificate's notAfter field");
+	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+		return("format error in CRL's lastUpdate field");
+	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+		return("format error in CRL's nextUpdate field");
+	case X509_V_ERR_OUT_OF_MEM:
+		return("out of memory");
+	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+		return("self signed certificate");
+	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+		return("self signed certificate in certificate chain");
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+		return("unable to get local issuer certificate");
+	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+		return("unable to verify the first certificate");
+	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+		return("certificate chain too long");
+	case X509_V_ERR_CERT_REVOKED:
+		return("certificate revoked");
+	case X509_V_ERR_INVALID_CA:
+		return ("invalid CA certificate");
+	case X509_V_ERR_INVALID_NON_CA:
+		return ("invalid non-CA certificate (has CA markings)");
+	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+		return ("path length constraint exceeded");
+	case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
+		return("proxy path length constraint exceeded");
+	case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED:
+		return("proxy certificates not allowed, please set the appropriate flag");
+	case X509_V_ERR_INVALID_PURPOSE:
+		return ("unsupported certificate purpose");
+	case X509_V_ERR_CERT_UNTRUSTED:
+		return ("certificate not trusted");
+	case X509_V_ERR_CERT_REJECTED:
+		return ("certificate rejected");
+	case X509_V_ERR_APPLICATION_VERIFICATION:
+		return("application verification failure");
+	case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+		return("subject issuer mismatch");
+	case X509_V_ERR_AKID_SKID_MISMATCH:
+		return("authority and subject key identifier mismatch");
+	case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+		return("authority and issuer serial number mismatch");
+	case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+		return("key usage does not include certificate signing");
+	case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+		return("unable to get CRL issuer certificate");
+	case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
+		return("unhandled critical extension");
+	case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
+		return("key usage does not include CRL signing");
+	case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
+		return("key usage does not include digital signature");
+	case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
+		return("unhandled critical CRL extension");
+	case X509_V_ERR_INVALID_EXTENSION:
+		return("invalid or inconsistent certificate extension");
+	case X509_V_ERR_INVALID_POLICY_EXTENSION:
+		return("invalid or inconsistent certificate policy extension");
+	case X509_V_ERR_NO_EXPLICIT_POLICY:
+		return("no explicit policy");
+	case X509_V_ERR_DIFFERENT_CRL_SCOPE:
+	return("Different CRL scope");
+	case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE:
+	return("Unsupported extension feature");
+ 	case X509_V_ERR_UNNESTED_RESOURCE:
+ 		return("RFC 3779 resource not subset of parent's resources");
+
+	case X509_V_ERR_PERMITTED_VIOLATION:
+		return("permitted subtree violation");
+	case X509_V_ERR_EXCLUDED_VIOLATION:
+		return("excluded subtree violation");
+	case X509_V_ERR_SUBTREE_MINMAX:
+		return("name constraints minimum and maximum not supported");
+	case X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:
+		return("unsupported name constraint type");
+	case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX:
+		return("unsupported or invalid name constraint syntax");
+	case X509_V_ERR_UNSUPPORTED_NAME_SYNTAX:
+		return("unsupported or invalid name syntax");
+	case X509_V_ERR_CRL_PATH_VALIDATION_ERROR:
+		return("CRL path validation error");
+
+	case X509_V_ERR_SUITE_B_INVALID_VERSION:
+		return("Suite B: certificate version invalid");
+	case X509_V_ERR_SUITE_B_INVALID_ALGORITHM:
+		return("Suite B: invalid public key algorithm");
+	case X509_V_ERR_SUITE_B_INVALID_CURVE:
+		return("Suite B: invalid ECC curve");
+	case X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM:
+		return("Suite B: invalid signature algorithm");
+	case X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED:
+		return("Suite B: curve not allowed for this LOS");
+	case X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256:
+		return("Suite B: cannot sign P-384 with P-256");
+
+	case X509_V_ERR_HOSTNAME_MISMATCH:
+		return("Hostname mismatch");
+	case X509_V_ERR_EMAIL_MISMATCH:
+		return("Email address mismatch");
+	case X509_V_ERR_IP_ADDRESS_MISMATCH:
+		return("IP address mismatch");
+
+	default:
+		BIO_snprintf(buf,sizeof buf,"error number %ld",n);
+		return(buf);
+		}
+	}
+
+
diff --git a/crypto/x509/x509_v3.c b/crypto/x509/x509_v3.c
new file mode 100644
index 0000000..95fe729
--- /dev/null
+++ b/crypto/x509/x509_v3.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x)
+	{
+	if (x == NULL) return(0);
+	return(sk_X509_EXTENSION_num(x));
+	}
+
+int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x, int nid,
+			  int lastpos)
+	{
+	const ASN1_OBJECT *obj;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL) return(-2);
+	return(X509v3_get_ext_by_OBJ(x,obj,lastpos));
+	}
+
+int X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *sk, const ASN1_OBJECT *obj,
+			  int lastpos)
+	{
+	int n;
+	X509_EXTENSION *ex;
+
+	if (sk == NULL) return(-1);
+	lastpos++;
+	if (lastpos < 0)
+		lastpos=0;
+	n=sk_X509_EXTENSION_num(sk);
+	for ( ; lastpos < n; lastpos++)
+		{
+		ex=sk_X509_EXTENSION_value(sk,lastpos);
+		if (OBJ_cmp(ex->object,obj) == 0)
+			return(lastpos);
+		}
+	return(-1);
+	}
+
+int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *sk, int crit,
+			       int lastpos)
+	{
+	int n;
+	X509_EXTENSION *ex;
+
+	if (sk == NULL) return(-1);
+	lastpos++;
+	if (lastpos < 0)
+		lastpos=0;
+	n=sk_X509_EXTENSION_num(sk);
+	for ( ; lastpos < n; lastpos++)
+		{
+		ex=sk_X509_EXTENSION_value(sk,lastpos);
+		if (	((ex->critical > 0) && crit) ||
+			((ex->critical <= 0) && !crit))
+			return(lastpos);
+		}
+	return(-1);
+	}
+
+X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc)
+	{
+	if (x == NULL || loc < 0 || sk_X509_EXTENSION_num(x) <= (size_t) loc)
+		return NULL;
+	else
+		return sk_X509_EXTENSION_value(x,loc);
+	}
+
+X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x, int loc)
+	{
+	X509_EXTENSION *ret;
+
+	if (x == NULL || loc < 0 || sk_X509_EXTENSION_num(x) <= (size_t) loc)
+		return(NULL);
+	ret=sk_X509_EXTENSION_delete(x,loc);
+	return(ret);
+	}
+
+STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
+					 X509_EXTENSION *ex, int loc)
+	{
+	X509_EXTENSION *new_ex=NULL;
+	int n;
+	STACK_OF(X509_EXTENSION) *sk=NULL;
+
+	if (x == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509v3_add_ext, ERR_R_PASSED_NULL_PARAMETER);
+		goto err2;
+		}
+
+	if (*x == NULL)
+		{
+		if ((sk=sk_X509_EXTENSION_new_null()) == NULL)
+			goto err;
+		}
+	else
+		sk= *x;
+
+	n=sk_X509_EXTENSION_num(sk);
+	if (loc > n) loc=n;
+	else if (loc < 0) loc=n;
+
+	if ((new_ex=X509_EXTENSION_dup(ex)) == NULL)
+		goto err2;
+	if (!sk_X509_EXTENSION_insert(sk,new_ex,loc))
+		goto err;
+	if (*x == NULL)
+		*x=sk;
+	return(sk);
+err:
+	OPENSSL_PUT_ERROR(X509, X509v3_add_ext, ERR_R_MALLOC_FAILURE);
+err2:
+	if (new_ex != NULL) X509_EXTENSION_free(new_ex);
+	if (sk != NULL) sk_X509_EXTENSION_free(sk);
+	return(NULL);
+	}
+
+X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, int nid,
+	     int crit, ASN1_OCTET_STRING *data)
+	{
+	const ASN1_OBJECT *obj;
+	X509_EXTENSION *ret;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_EXTENSION_create_by_NID, X509_R_UNKNOWN_NID);
+		return(NULL);
+		}
+	ret=X509_EXTENSION_create_by_OBJ(ex,obj,crit,data);
+	return(ret);
+	}
+
+X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **ex,
+	     const ASN1_OBJECT *obj, int crit, ASN1_OCTET_STRING *data)
+	{
+	X509_EXTENSION *ret;
+
+	if ((ex == NULL) || (*ex == NULL))
+		{
+		if ((ret=X509_EXTENSION_new()) == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_EXTENSION_create_by_OBJ, ERR_R_MALLOC_FAILURE);
+			return(NULL);
+			}
+		}
+	else
+		ret= *ex;
+
+	if (!X509_EXTENSION_set_object(ret,obj))
+		goto err;
+	if (!X509_EXTENSION_set_critical(ret,crit))
+		goto err;
+	if (!X509_EXTENSION_set_data(ret,data))
+		goto err;
+	
+	if ((ex != NULL) && (*ex == NULL)) *ex=ret;
+	return(ret);
+err:
+	if ((ex == NULL) || (ret != *ex))
+		X509_EXTENSION_free(ret);
+	return(NULL);
+	}
+
+int X509_EXTENSION_set_object(X509_EXTENSION *ex, const ASN1_OBJECT *obj)
+	{
+	if ((ex == NULL) || (obj == NULL))
+		return(0);
+	ASN1_OBJECT_free(ex->object);
+	ex->object=OBJ_dup(obj);
+	return(1);
+	}
+
+int X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit)
+	{
+	if (ex == NULL) return(0);
+	ex->critical=(crit)?0xFF:-1;
+	return(1);
+	}
+
+int X509_EXTENSION_set_data(X509_EXTENSION *ex, ASN1_OCTET_STRING *data)
+	{
+	int i;
+
+	if (ex == NULL) return(0);
+	i=M_ASN1_OCTET_STRING_set(ex->value,data->data,data->length);
+	if (!i) return(0);
+	return(1);
+	}
+
+ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex)
+	{
+	if (ex == NULL) return(NULL);
+	return(ex->object);
+	}
+
+ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ex)
+	{
+	if (ex == NULL) return(NULL);
+	return(ex->value);
+	}
+
+int X509_EXTENSION_get_critical(X509_EXTENSION *ex)
+	{
+	if (ex == NULL) return(0);
+	if(ex->critical > 0) return 1;
+	return 0;
+	}
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
new file mode 100644
index 0000000..ce2087f
--- /dev/null
+++ b/crypto/x509/x509_vfy.c
@@ -0,0 +1,2457 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <time.h>
+
+#include <openssl/asn1.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "vpm_int.h"
+
+/* CRL score values */
+
+/* No unhandled critical extensions */
+
+#define CRL_SCORE_NOCRITICAL	0x100
+
+/* certificate is within CRL scope */
+
+#define CRL_SCORE_SCOPE		0x080
+
+/* CRL times valid */
+
+#define CRL_SCORE_TIME		0x040
+
+/* Issuer name matches certificate */
+
+#define CRL_SCORE_ISSUER_NAME	0x020
+
+/* If this score or above CRL is probably valid */
+
+#define CRL_SCORE_VALID (CRL_SCORE_NOCRITICAL|CRL_SCORE_TIME|CRL_SCORE_SCOPE)
+
+/* CRL issuer is certificate issuer */
+
+#define CRL_SCORE_ISSUER_CERT	0x018
+
+/* CRL issuer is on certificate path */
+
+#define CRL_SCORE_SAME_PATH	0x008
+
+/* CRL issuer matches CRL AKID */
+
+#define CRL_SCORE_AKID		0x004
+
+/* Have a delta CRL with valid times */
+
+#define CRL_SCORE_TIME_DELTA	0x002
+
+static int null_callback(int ok,X509_STORE_CTX *e);
+static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer);
+static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x);
+static int check_chain_extensions(X509_STORE_CTX *ctx);
+static int check_name_constraints(X509_STORE_CTX *ctx);
+static int check_id(X509_STORE_CTX *ctx);
+static int check_trust(X509_STORE_CTX *ctx);
+static int check_revocation(X509_STORE_CTX *ctx);
+static int check_cert(X509_STORE_CTX *ctx);
+static int check_policy(X509_STORE_CTX *ctx);
+
+static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer,
+			unsigned int *preasons,
+			X509_CRL *crl, X509 *x);
+static int get_crl_delta(X509_STORE_CTX *ctx,
+				X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x);
+static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, int *pcrl_score,
+			X509_CRL *base, STACK_OF(X509_CRL) *crls);
+static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl,
+				X509 **pissuer, int *pcrl_score);
+static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score,
+				unsigned int *preasons);
+static int check_crl_path(X509_STORE_CTX *ctx, X509 *x);
+static int check_crl_chain(X509_STORE_CTX *ctx,
+			STACK_OF(X509) *cert_path,
+			STACK_OF(X509) *crl_path);
+
+static int internal_verify(X509_STORE_CTX *ctx);
+const char X509_version[]="X.509";
+
+
+static int null_callback(int ok, X509_STORE_CTX *e)
+	{
+	return ok;
+	}
+
+#if 0
+static int x509_subject_cmp(X509 **a, X509 **b)
+	{
+	return X509_subject_name_cmp(*a,*b);
+	}
+#endif
+
+/* Given a certificate try and find an exact match in the store */
+
+static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x)
+	{
+	STACK_OF(X509) *certs;
+	X509 *xtmp = NULL;
+	size_t i;
+	/* Lookup all certs with matching subject name */
+	certs = ctx->lookup_certs(ctx, X509_get_subject_name(x));
+	if (certs == NULL)
+		return NULL;
+	/* Look for exact match */
+	for (i = 0; i < sk_X509_num(certs); i++)
+		{
+		xtmp = sk_X509_value(certs, i);
+		if (!X509_cmp(xtmp, x))
+			break;
+		}
+	if (i < sk_X509_num(certs))
+		CRYPTO_add(&xtmp->references,1,CRYPTO_LOCK_X509);
+	else
+		xtmp = NULL;
+	sk_X509_pop_free(certs, X509_free);
+	return xtmp;
+	}
+
+int X509_verify_cert(X509_STORE_CTX *ctx)
+	{
+	X509 *x,*xtmp,*chain_ss=NULL;
+	int bad_chain = 0;
+	X509_VERIFY_PARAM *param = ctx->param;
+	int depth,i,ok=0;
+	int num;
+	int (*cb)(int xok,X509_STORE_CTX *xctx);
+	STACK_OF(X509) *sktmp=NULL;
+	if (ctx->cert == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_verify_cert, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY);
+		return -1;
+		}
+
+	cb=ctx->verify_cb;
+
+	/* first we make sure the chain we are going to build is
+	 * present and that the first entry is in place */
+	if (ctx->chain == NULL)
+		{
+		if (	((ctx->chain=sk_X509_new_null()) == NULL) ||
+			(!sk_X509_push(ctx->chain,ctx->cert)))
+			{
+			OPENSSL_PUT_ERROR(X509, X509_verify_cert, ERR_R_MALLOC_FAILURE);
+			goto end;
+			}
+		CRYPTO_add(&ctx->cert->references,1,CRYPTO_LOCK_X509);
+		ctx->last_untrusted=1;
+		}
+
+	/* We use a temporary STACK so we can chop and hack at it */
+	if (ctx->untrusted != NULL
+	    && (sktmp=sk_X509_dup(ctx->untrusted)) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_verify_cert, ERR_R_MALLOC_FAILURE);
+		goto end;
+		}
+
+	num=sk_X509_num(ctx->chain);
+	x=sk_X509_value(ctx->chain,num-1);
+	depth=param->depth;
+
+
+	for (;;)
+		{
+		/* If we have enough, we break */
+		if (depth < num) break; /* FIXME: If this happens, we should take
+		                         * note of it and, if appropriate, use the
+		                         * X509_V_ERR_CERT_CHAIN_TOO_LONG error
+		                         * code later.
+		                         */
+
+		/* If we are self signed, we break */
+		if (ctx->check_issued(ctx, x,x)) break;
+
+		/* If asked see if we can find issuer in trusted store first */
+		if (ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST)
+			{
+			ok = ctx->get_issuer(&xtmp, ctx, x);
+			if (ok < 0)
+				return ok;
+			/* If successful for now free up cert so it
+			 * will be picked up again later.
+			 */
+			if (ok > 0)
+				{
+				X509_free(xtmp);
+				break;
+				}
+			}
+
+		/* If we were passed a cert chain, use it first */
+		if (ctx->untrusted != NULL)
+			{
+			xtmp=find_issuer(ctx, sktmp,x);
+			if (xtmp != NULL)
+				{
+				if (!sk_X509_push(ctx->chain,xtmp))
+					{
+					OPENSSL_PUT_ERROR(X509, X509_verify_cert, ERR_R_MALLOC_FAILURE);
+					goto end;
+					}
+				CRYPTO_add(&xtmp->references,1,CRYPTO_LOCK_X509);
+				(void)sk_X509_delete_ptr(sktmp,xtmp);
+				ctx->last_untrusted++;
+				x=xtmp;
+				num++;
+				/* reparse the full chain for
+				 * the next one */
+				continue;
+				}
+			}
+		break;
+		}
+
+	/* at this point, chain should contain a list of untrusted
+	 * certificates.  We now need to add at least one trusted one,
+	 * if possible, otherwise we complain. */
+
+	/* Examine last certificate in chain and see if it
+ 	 * is self signed.
+ 	 */
+
+	i=sk_X509_num(ctx->chain);
+	x=sk_X509_value(ctx->chain,i-1);
+	if (ctx->check_issued(ctx, x, x))
+		{
+		/* we have a self signed certificate */
+		if (sk_X509_num(ctx->chain) == 1)
+			{
+			/* We have a single self signed certificate: see if
+			 * we can find it in the store. We must have an exact
+			 * match to avoid possible impersonation.
+			 */
+			ok = ctx->get_issuer(&xtmp, ctx, x);
+			if ((ok <= 0) || X509_cmp(x, xtmp)) 
+				{
+				ctx->error=X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
+				ctx->current_cert=x;
+				ctx->error_depth=i-1;
+				if (ok == 1) X509_free(xtmp);
+				bad_chain = 1;
+				ok=cb(0,ctx);
+				if (!ok) goto end;
+				}
+			else 
+				{
+				/* We have a match: replace certificate with store version
+				 * so we get any trust settings.
+				 */
+				X509_free(x);
+				x = xtmp;
+				(void)sk_X509_set(ctx->chain, i - 1, x);
+				ctx->last_untrusted=0;
+				}
+			}
+		else
+			{
+			/* extract and save self signed certificate for later use */
+			chain_ss=sk_X509_pop(ctx->chain);
+			ctx->last_untrusted--;
+			num--;
+			x=sk_X509_value(ctx->chain,num-1);
+			}
+		}
+
+	/* We now lookup certs from the certificate store */
+	for (;;)
+		{
+		/* If we have enough, we break */
+		if (depth < num) break;
+
+		/* If we are self signed, we break */
+		if (ctx->check_issued(ctx,x,x)) break;
+
+		ok = ctx->get_issuer(&xtmp, ctx, x);
+
+		if (ok < 0) return ok;
+		if (ok == 0) break;
+
+		x = xtmp;
+		if (!sk_X509_push(ctx->chain,x))
+			{
+			X509_free(xtmp);
+			OPENSSL_PUT_ERROR(X509, X509_verify_cert, ERR_R_MALLOC_FAILURE);
+			return 0;
+			}
+		num++;
+		}
+
+	/* we now have our chain, lets check it... */
+
+	i = check_trust(ctx);
+
+	/* If explicitly rejected error */
+	if (i == X509_TRUST_REJECTED)
+		goto end;
+	/* If not explicitly trusted then indicate error */
+	if (i != X509_TRUST_TRUSTED)
+		{
+		if ((chain_ss == NULL) || !ctx->check_issued(ctx, x, chain_ss))
+			{
+			if (ctx->last_untrusted >= num)
+				ctx->error=X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY;
+			else
+				ctx->error=X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+			ctx->current_cert=x;
+			}
+		else
+			{
+
+			sk_X509_push(ctx->chain,chain_ss);
+			num++;
+			ctx->last_untrusted=num;
+			ctx->current_cert=chain_ss;
+			ctx->error=X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+			chain_ss=NULL;
+			}
+
+		ctx->error_depth=num-1;
+		bad_chain = 1;
+		ok=cb(0,ctx);
+		if (!ok) goto end;
+		}
+
+	/* We have the chain complete: now we need to check its purpose */
+	ok = check_chain_extensions(ctx);
+
+	if (!ok) goto end;
+
+	/* Check name constraints */
+
+	ok = check_name_constraints(ctx);
+	
+	if (!ok) goto end;
+
+	ok = check_id(ctx);
+
+	if (!ok) goto end;
+
+	/* We may as well copy down any DSA parameters that are required */
+	X509_get_pubkey_parameters(NULL,ctx->chain);
+
+	/* Check revocation status: we do this after copying parameters
+	 * because they may be needed for CRL signature verification.
+	 */
+
+	ok = ctx->check_revocation(ctx);
+	if(!ok) goto end;
+
+	i = X509_chain_check_suiteb(&ctx->error_depth, NULL, ctx->chain,
+							ctx->param->flags);
+	if (i != X509_V_OK)
+		{
+		ctx->error = i;
+		ctx->current_cert = sk_X509_value(ctx->chain, ctx->error_depth);
+		ok = cb(0, ctx);
+		if (!ok)
+			goto end;
+		}
+
+	/* At this point, we have a chain and need to verify it */
+	if (ctx->verify != NULL)
+		ok=ctx->verify(ctx);
+	else
+		ok=internal_verify(ctx);
+	if(!ok) goto end;
+
+	/* If we get this far evaluate policies */
+	if (!bad_chain && (ctx->param->flags & X509_V_FLAG_POLICY_CHECK))
+		ok = ctx->check_policy(ctx);
+	if(!ok) goto end;
+	if (0)
+		{
+end:
+		X509_get_pubkey_parameters(NULL,ctx->chain);
+		}
+	if (sktmp != NULL) sk_X509_free(sktmp);
+	if (chain_ss != NULL) X509_free(chain_ss);
+	return ok;
+	}
+
+
+/* Given a STACK_OF(X509) find the issuer of cert (if any)
+ */
+
+static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
+{
+	size_t i;
+	X509 *issuer;
+	for (i = 0; i < sk_X509_num(sk); i++)
+		{
+		issuer = sk_X509_value(sk, i);
+		if (ctx->check_issued(ctx, x, issuer))
+			return issuer;
+		}
+	return NULL;
+}
+
+/* Given a possible certificate and issuer check them */
+
+static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer)
+{
+	int ret;
+	ret = X509_check_issued(issuer, x);
+	if (ret == X509_V_OK)
+		return 1;
+	/* If we haven't asked for issuer errors don't set ctx */
+	if (!(ctx->param->flags & X509_V_FLAG_CB_ISSUER_CHECK))
+		return 0;
+
+	ctx->error = ret;
+	ctx->current_cert = x;
+	ctx->current_issuer = issuer;
+	return ctx->verify_cb(0, ctx);
+	return 0;
+}
+
+/* Alternative lookup method: look from a STACK stored in other_ctx */
+
+static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
+{
+	*issuer = find_issuer(ctx, ctx->other_ctx, x);
+	if (*issuer)
+		{
+		CRYPTO_add(&(*issuer)->references,1,CRYPTO_LOCK_X509);
+		return 1;
+		}
+	else
+		return 0;
+}
+	
+
+/* Check a certificate chains extensions for consistency
+ * with the supplied purpose
+ */
+
+static int check_chain_extensions(X509_STORE_CTX *ctx)
+{
+#ifdef OPENSSL_NO_CHAIN_VERIFY
+	return 1;
+#else
+	int i, ok=0, must_be_ca, plen = 0;
+	X509 *x;
+	int (*cb)(int xok,X509_STORE_CTX *xctx);
+	int proxy_path_length = 0;
+	int purpose;
+	int allow_proxy_certs;
+	cb=ctx->verify_cb;
+
+	/* must_be_ca can have 1 of 3 values:
+	   -1: we accept both CA and non-CA certificates, to allow direct
+	       use of self-signed certificates (which are marked as CA).
+	   0:  we only accept non-CA certificates.  This is currently not
+	       used, but the possibility is present for future extensions.
+	   1:  we only accept CA certificates.  This is currently used for
+	       all certificates in the chain except the leaf certificate.
+	*/
+	must_be_ca = -1;
+
+	/* CRL path validation */
+	if (ctx->parent)
+		{
+		allow_proxy_certs = 0;
+		purpose = X509_PURPOSE_CRL_SIGN;
+		}
+	else
+		{
+		allow_proxy_certs =
+			!!(ctx->param->flags & X509_V_FLAG_ALLOW_PROXY_CERTS);
+		/* A hack to keep people who don't want to modify their
+		   software happy */
+		if (getenv("OPENSSL_ALLOW_PROXY_CERTS"))
+			allow_proxy_certs = 1;
+		purpose = ctx->param->purpose;
+		}
+
+	/* Check all untrusted certificates */
+	for (i = 0; i < ctx->last_untrusted; i++)
+		{
+		int ret;
+		x = sk_X509_value(ctx->chain, i);
+		if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL)
+			&& (x->ex_flags & EXFLAG_CRITICAL))
+			{
+			ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION;
+			ctx->error_depth = i;
+			ctx->current_cert = x;
+			ok=cb(0,ctx);
+			if (!ok) goto end;
+			}
+		if (!allow_proxy_certs && (x->ex_flags & EXFLAG_PROXY))
+			{
+			ctx->error = X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED;
+			ctx->error_depth = i;
+			ctx->current_cert = x;
+			ok=cb(0,ctx);
+			if (!ok) goto end;
+			}
+		ret = X509_check_ca(x);
+		switch(must_be_ca)
+			{
+		case -1:
+			if ((ctx->param->flags & X509_V_FLAG_X509_STRICT)
+				&& (ret != 1) && (ret != 0))
+				{
+				ret = 0;
+				ctx->error = X509_V_ERR_INVALID_CA;
+				}
+			else
+				ret = 1;
+			break;
+		case 0:
+			if (ret != 0)
+				{
+				ret = 0;
+				ctx->error = X509_V_ERR_INVALID_NON_CA;
+				}
+			else
+				ret = 1;
+			break;
+		default:
+			if ((ret == 0)
+				|| ((ctx->param->flags & X509_V_FLAG_X509_STRICT)
+					&& (ret != 1)))
+				{
+				ret = 0;
+				ctx->error = X509_V_ERR_INVALID_CA;
+				}
+			else
+				ret = 1;
+			break;
+			}
+		if (ret == 0)
+			{
+			ctx->error_depth = i;
+			ctx->current_cert = x;
+			ok=cb(0,ctx);
+			if (!ok) goto end;
+			}
+		if (ctx->param->purpose > 0)
+			{
+			ret = X509_check_purpose(x, purpose, must_be_ca > 0);
+			if ((ret == 0)
+				|| ((ctx->param->flags & X509_V_FLAG_X509_STRICT)
+					&& (ret != 1)))
+				{
+				ctx->error = X509_V_ERR_INVALID_PURPOSE;
+				ctx->error_depth = i;
+				ctx->current_cert = x;
+				ok=cb(0,ctx);
+				if (!ok) goto end;
+				}
+			}
+		/* Check pathlen if not self issued */
+		if ((i > 1) && !(x->ex_flags & EXFLAG_SI)
+			   && (x->ex_pathlen != -1)
+			   && (plen > (x->ex_pathlen + proxy_path_length + 1)))
+			{
+			ctx->error = X509_V_ERR_PATH_LENGTH_EXCEEDED;
+			ctx->error_depth = i;
+			ctx->current_cert = x;
+			ok=cb(0,ctx);
+			if (!ok) goto end;
+			}
+		/* Increment path length if not self issued */
+		if (!(x->ex_flags & EXFLAG_SI))
+			plen++;
+		/* If this certificate is a proxy certificate, the next
+		   certificate must be another proxy certificate or a EE
+		   certificate.  If not, the next certificate must be a
+		   CA certificate.  */
+		if (x->ex_flags & EXFLAG_PROXY)
+			{
+			if (x->ex_pcpathlen != -1 && i > x->ex_pcpathlen)
+				{
+				ctx->error =
+					X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED;
+				ctx->error_depth = i;
+				ctx->current_cert = x;
+				ok=cb(0,ctx);
+				if (!ok) goto end;
+				}
+			proxy_path_length++;
+			must_be_ca = 0;
+			}
+		else
+			must_be_ca = 1;
+		}
+	ok = 1;
+ end:
+	return ok;
+#endif
+}
+
+static int check_name_constraints(X509_STORE_CTX *ctx)
+	{
+	X509 *x;
+	int i, j, rv;
+	/* Check name constraints for all certificates */
+	for (i = sk_X509_num(ctx->chain) - 1; i >= 0; i--)
+		{
+		x = sk_X509_value(ctx->chain, i);
+		/* Ignore self issued certs unless last in chain */
+		if (i && (x->ex_flags & EXFLAG_SI))
+			continue;
+		/* Check against constraints for all certificates higher in
+		 * chain including trust anchor. Trust anchor not strictly
+		 * speaking needed but if it includes constraints it is to be
+		 * assumed it expects them to be obeyed.
+		 */
+		for (j = sk_X509_num(ctx->chain) - 1; j > i; j--)
+			{
+			NAME_CONSTRAINTS *nc = sk_X509_value(ctx->chain, j)->nc;
+			if (nc)
+				{
+				rv = NAME_CONSTRAINTS_check(x, nc);
+				if (rv != X509_V_OK)
+					{
+					ctx->error = rv;
+					ctx->error_depth = i;
+					ctx->current_cert = x;
+					if (!ctx->verify_cb(0,ctx))
+						return 0;
+					}
+				}
+			}
+		}
+	return 1;
+	}
+
+static int check_id_error(X509_STORE_CTX *ctx, int errcode)
+	{
+	ctx->error = errcode;
+	ctx->current_cert = ctx->cert;
+	ctx->error_depth = 0;
+	return ctx->verify_cb(0, ctx);
+	}
+
+static int check_id(X509_STORE_CTX *ctx)
+	{
+	X509_VERIFY_PARAM *vpm = ctx->param;
+	X509_VERIFY_PARAM_ID *id = vpm->id;
+	X509 *x = ctx->cert;
+	if (id->host && !X509_check_host(x, id->host, id->hostlen, 0))
+		{
+		if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
+			return 0;
+		}
+	if (id->email && !X509_check_email(x, id->email, id->emaillen, 0))
+		{
+		if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH))
+			return 0;
+		}
+	if (id->ip && !X509_check_ip(x, id->ip, id->iplen, 0))
+		{
+		if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH))
+			return 0;
+		}
+	return 1;
+	}
+
+static int check_trust(X509_STORE_CTX *ctx)
+{
+	size_t i;
+	int ok;
+	X509 *x = NULL;
+	int (*cb)(int xok,X509_STORE_CTX *xctx);
+	cb=ctx->verify_cb;
+	/* Check all trusted certificates in chain */
+	for (i = ctx->last_untrusted; i < sk_X509_num(ctx->chain); i++)
+		{
+		x = sk_X509_value(ctx->chain, i);
+		ok = X509_check_trust(x, ctx->param->trust, 0);
+		/* If explicitly trusted return trusted */
+		if (ok == X509_TRUST_TRUSTED)
+			return X509_TRUST_TRUSTED;
+		/* If explicitly rejected notify callback and reject if
+		 * not overridden.
+		 */
+		if (ok == X509_TRUST_REJECTED)
+			{
+			ctx->error_depth = i;
+			ctx->current_cert = x;
+			ctx->error = X509_V_ERR_CERT_REJECTED;
+			ok = cb(0, ctx);
+			if (!ok)
+				return X509_TRUST_REJECTED;
+			}
+		}
+	/* If we accept partial chains and have at least one trusted
+	 * certificate return success.
+	 */
+	if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN)
+		{
+		X509 *mx;
+		if (ctx->last_untrusted < (int) sk_X509_num(ctx->chain))
+			return X509_TRUST_TRUSTED;
+		x = sk_X509_value(ctx->chain, 0);
+		mx = lookup_cert_match(ctx, x);
+		if (mx)
+			{
+			(void)sk_X509_set(ctx->chain, 0, mx);
+			X509_free(x);
+			ctx->last_untrusted = 0;
+			return X509_TRUST_TRUSTED;
+			}
+		}
+
+	/* If no trusted certs in chain at all return untrusted and
+	 * allow standard (no issuer cert) etc errors to be indicated.
+	 */
+	return X509_TRUST_UNTRUSTED;
+}
+
+static int check_revocation(X509_STORE_CTX *ctx)
+	{
+	int i, last, ok;
+	if (!(ctx->param->flags & X509_V_FLAG_CRL_CHECK))
+		return 1;
+	if (ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL)
+		last = sk_X509_num(ctx->chain) - 1;
+	else
+		{
+		/* If checking CRL paths this isn't the EE certificate */
+		if (ctx->parent)
+			return 1;
+		last = 0;
+		}
+	for(i = 0; i <= last; i++)
+		{
+		ctx->error_depth = i;
+		ok = check_cert(ctx);
+		if (!ok) return ok;
+		}
+	return 1;
+	}
+
+static int check_cert(X509_STORE_CTX *ctx)
+	{
+	X509_CRL *crl = NULL, *dcrl = NULL;
+	X509 *x;
+	int ok, cnum;
+	unsigned int last_reasons;
+	cnum = ctx->error_depth;
+	x = sk_X509_value(ctx->chain, cnum);
+	ctx->current_cert = x;
+	ctx->current_issuer = NULL;
+	ctx->current_crl_score = 0;
+	ctx->current_reasons = 0;
+	while (ctx->current_reasons != CRLDP_ALL_REASONS)
+		{
+		last_reasons = ctx->current_reasons;
+		/* Try to retrieve relevant CRL */
+		if (ctx->get_crl)
+			ok = ctx->get_crl(ctx, &crl, x);
+		else
+			ok = get_crl_delta(ctx, &crl, &dcrl, x);
+		/* If error looking up CRL, nothing we can do except
+		 * notify callback
+		 */
+		if(!ok)
+			{
+			ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL;
+			ok = ctx->verify_cb(0, ctx);
+			goto err;
+			}
+		ctx->current_crl = crl;
+		ok = ctx->check_crl(ctx, crl);
+		if (!ok)
+			goto err;
+
+		if (dcrl)
+			{
+			ok = ctx->check_crl(ctx, dcrl);
+			if (!ok)
+				goto err;
+			ok = ctx->cert_crl(ctx, dcrl, x);
+			if (!ok)
+				goto err;
+			}
+		else
+			ok = 1;
+
+		/* Don't look in full CRL if delta reason is removefromCRL */
+		if (ok != 2)
+			{
+			ok = ctx->cert_crl(ctx, crl, x);
+			if (!ok)
+				goto err;
+			}
+
+		X509_CRL_free(crl);
+		X509_CRL_free(dcrl);
+		crl = NULL;
+		dcrl = NULL;
+		/* If reasons not updated we wont get anywhere by
+		 * another iteration, so exit loop.
+		 */
+		if (last_reasons == ctx->current_reasons)
+			{
+			ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL;
+			ok = ctx->verify_cb(0, ctx);
+			goto err;
+			}
+		}
+	err:
+	X509_CRL_free(crl);
+	X509_CRL_free(dcrl);
+
+	ctx->current_crl = NULL;
+	return ok;
+
+	}
+
+/* Check CRL times against values in X509_STORE_CTX */
+
+static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify)
+	{
+	time_t *ptime;
+	int i;
+	if (notify)
+		ctx->current_crl = crl;
+	if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME)
+		ptime = &ctx->param->check_time;
+	else
+		ptime = NULL;
+
+	i=X509_cmp_time(X509_CRL_get_lastUpdate(crl), ptime);
+	if (i == 0)
+		{
+		if (!notify)
+			return 0;
+		ctx->error=X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD;
+		if (!ctx->verify_cb(0, ctx))
+			return 0;
+		}
+
+	if (i > 0)
+		{
+		if (!notify)
+			return 0;
+		ctx->error=X509_V_ERR_CRL_NOT_YET_VALID;
+		if (!ctx->verify_cb(0, ctx))
+			return 0;
+		}
+
+	if(X509_CRL_get_nextUpdate(crl))
+		{
+		i=X509_cmp_time(X509_CRL_get_nextUpdate(crl), ptime);
+
+		if (i == 0)
+			{
+			if (!notify)
+				return 0;
+			ctx->error=X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD;
+			if (!ctx->verify_cb(0, ctx))
+				return 0;
+			}
+		/* Ignore expiry of base CRL is delta is valid */
+		if ((i < 0) && !(ctx->current_crl_score & CRL_SCORE_TIME_DELTA))
+			{
+			if (!notify)
+				return 0;
+			ctx->error=X509_V_ERR_CRL_HAS_EXPIRED;
+			if (!ctx->verify_cb(0, ctx))
+				return 0;
+			}
+		}
+
+	if (notify)
+		ctx->current_crl = NULL;
+
+	return 1;
+	}
+
+static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl,
+			X509 **pissuer, int *pscore, unsigned int *preasons,
+			STACK_OF(X509_CRL) *crls)
+	{
+	int crl_score, best_score = *pscore;
+	size_t i;
+	unsigned int reasons, best_reasons = 0;
+	X509 *x = ctx->current_cert;
+	X509_CRL *crl, *best_crl = NULL;
+	X509 *crl_issuer = NULL, *best_crl_issuer = NULL;
+
+	for (i = 0; i < sk_X509_CRL_num(crls); i++)
+		{
+		crl = sk_X509_CRL_value(crls, i);
+		reasons = *preasons;
+		crl_score = get_crl_score(ctx, &crl_issuer, &reasons, crl, x);
+
+		if (crl_score > best_score)
+			{
+			best_crl = crl;
+			best_crl_issuer = crl_issuer;
+			best_score = crl_score;
+			best_reasons = reasons;
+			}
+		}
+
+	if (best_crl)
+		{
+		if (*pcrl)
+			X509_CRL_free(*pcrl);
+		*pcrl = best_crl;
+		*pissuer = best_crl_issuer;
+		*pscore = best_score;
+		*preasons = best_reasons;
+		CRYPTO_add(&best_crl->references, 1, CRYPTO_LOCK_X509_CRL);
+		if (*pdcrl)
+			{
+			X509_CRL_free(*pdcrl);
+			*pdcrl = NULL;
+			}
+		get_delta_sk(ctx, pdcrl, pscore, best_crl, crls);
+		}
+
+	if (best_score >= CRL_SCORE_VALID)
+		return 1;
+
+	return 0;
+	}
+
+/* Compare two CRL extensions for delta checking purposes. They should be
+ * both present or both absent. If both present all fields must be identical.
+ */
+
+static int crl_extension_match(X509_CRL *a, X509_CRL *b, int nid)
+	{
+	ASN1_OCTET_STRING *exta, *extb;
+	int i;
+	i = X509_CRL_get_ext_by_NID(a, nid, -1);
+	if (i >= 0)
+		{
+		/* Can't have multiple occurrences */
+		if (X509_CRL_get_ext_by_NID(a, nid, i) != -1)
+			return 0;
+		exta = X509_EXTENSION_get_data(X509_CRL_get_ext(a, i));
+		}
+	else
+		exta = NULL;
+
+	i = X509_CRL_get_ext_by_NID(b, nid, -1);
+
+	if (i >= 0)
+		{
+
+		if (X509_CRL_get_ext_by_NID(b, nid, i) != -1)
+			return 0;
+		extb = X509_EXTENSION_get_data(X509_CRL_get_ext(b, i));
+		}
+	else
+		extb = NULL;
+
+	if (!exta && !extb)
+		return 1;
+
+	if (!exta || !extb)
+		return 0;
+
+
+	if (ASN1_OCTET_STRING_cmp(exta, extb))
+		return 0;
+
+	return 1;
+	}
+
+/* See if a base and delta are compatible */
+
+static int check_delta_base(X509_CRL *delta, X509_CRL *base)
+	{
+	/* Delta CRL must be a delta */
+	if (!delta->base_crl_number)
+			return 0;
+	/* Base must have a CRL number */
+	if (!base->crl_number)
+			return 0;
+	/* Issuer names must match */
+	if (X509_NAME_cmp(X509_CRL_get_issuer(base),
+				X509_CRL_get_issuer(delta)))
+		return 0;
+	/* AKID and IDP must match */
+	if (!crl_extension_match(delta, base, NID_authority_key_identifier))
+			return 0;
+	if (!crl_extension_match(delta, base, NID_issuing_distribution_point))
+			return 0;
+	/* Delta CRL base number must not exceed Full CRL number. */
+	if (ASN1_INTEGER_cmp(delta->base_crl_number, base->crl_number) > 0)
+			return 0;
+	/* Delta CRL number must exceed full CRL number */
+	if (ASN1_INTEGER_cmp(delta->crl_number, base->crl_number) > 0)
+			return 1;
+	return 0;
+	}
+
+/* For a given base CRL find a delta... maybe extend to delta scoring
+ * or retrieve a chain of deltas...
+ */
+
+static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, int *pscore,
+			X509_CRL *base, STACK_OF(X509_CRL) *crls)
+	{
+	X509_CRL *delta;
+	size_t i;
+	if (!(ctx->param->flags & X509_V_FLAG_USE_DELTAS))
+		return;
+	if (!((ctx->current_cert->ex_flags | base->flags) & EXFLAG_FRESHEST))
+		return;
+	for (i = 0; i < sk_X509_CRL_num(crls); i++)
+		{
+		delta = sk_X509_CRL_value(crls, i);
+		if (check_delta_base(delta, base))
+			{
+			if (check_crl_time(ctx, delta, 0))
+				*pscore |= CRL_SCORE_TIME_DELTA;
+			CRYPTO_add(&delta->references, 1, CRYPTO_LOCK_X509_CRL);
+			*dcrl = delta;
+			return;
+			}
+		}
+	*dcrl = NULL;
+	}
+
+/* For a given CRL return how suitable it is for the supplied certificate 'x'.
+ * The return value is a mask of several criteria.
+ * If the issuer is not the certificate issuer this is returned in *pissuer.
+ * The reasons mask is also used to determine if the CRL is suitable: if
+ * no new reasons the CRL is rejected, otherwise reasons is updated.
+ */
+
+static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer,
+			unsigned int *preasons,
+			X509_CRL *crl, X509 *x)
+	{
+
+	int crl_score = 0;
+	unsigned int tmp_reasons = *preasons, crl_reasons;
+
+	/* First see if we can reject CRL straight away */
+
+	/* Invalid IDP cannot be processed */
+	if (crl->idp_flags & IDP_INVALID)
+		return 0;
+	/* Reason codes or indirect CRLs need extended CRL support */
+	if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT))
+		{
+		if (crl->idp_flags & (IDP_INDIRECT | IDP_REASONS))
+			return 0;
+		}
+	else if (crl->idp_flags & IDP_REASONS)
+		{
+		/* If no new reasons reject */
+		if (!(crl->idp_reasons & ~tmp_reasons))
+			return 0;
+		}
+	/* Don't process deltas at this stage */
+	else if (crl->base_crl_number)
+		return 0;
+	/* If issuer name doesn't match certificate need indirect CRL */
+	if (X509_NAME_cmp(X509_get_issuer_name(x), X509_CRL_get_issuer(crl)))
+		{
+		if (!(crl->idp_flags & IDP_INDIRECT))
+			return 0;
+		}
+	else
+		crl_score |= CRL_SCORE_ISSUER_NAME;
+
+	if (!(crl->flags & EXFLAG_CRITICAL))
+		crl_score |= CRL_SCORE_NOCRITICAL;
+
+	/* Check expiry */
+	if (check_crl_time(ctx, crl, 0))
+		crl_score |= CRL_SCORE_TIME;
+
+	/* Check authority key ID and locate certificate issuer */
+	crl_akid_check(ctx, crl, pissuer, &crl_score);
+
+	/* If we can't locate certificate issuer at this point forget it */
+
+	if (!(crl_score & CRL_SCORE_AKID))
+		return 0;
+
+	/* Check cert for matching CRL distribution points */
+
+	if (crl_crldp_check(x, crl, crl_score, &crl_reasons))
+		{
+		/* If no new reasons reject */
+		if (!(crl_reasons & ~tmp_reasons))
+			return 0;
+		tmp_reasons |= crl_reasons;
+		crl_score |= CRL_SCORE_SCOPE;
+		}
+
+	*preasons = tmp_reasons;
+
+	return crl_score;
+
+	}
+
+static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl,
+				X509 **pissuer, int *pcrl_score)
+	{
+	X509 *crl_issuer = NULL;
+	X509_NAME *cnm = X509_CRL_get_issuer(crl);
+	int cidx = ctx->error_depth;
+	size_t i;
+
+	if (cidx != sk_X509_num(ctx->chain) - 1)
+		cidx++;
+
+	crl_issuer = sk_X509_value(ctx->chain, cidx);
+
+	if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK)
+		{
+		if (*pcrl_score & CRL_SCORE_ISSUER_NAME)
+			{
+			*pcrl_score |= CRL_SCORE_AKID|CRL_SCORE_ISSUER_CERT;
+			*pissuer = crl_issuer;
+			return;
+			}
+		}
+
+	for (cidx++; cidx < (int) sk_X509_num(ctx->chain); cidx++)
+		{
+		crl_issuer = sk_X509_value(ctx->chain, cidx);
+		if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm))
+			continue;
+		if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK)
+			{
+			*pcrl_score |= CRL_SCORE_AKID|CRL_SCORE_SAME_PATH;
+			*pissuer = crl_issuer;
+			return;
+			}
+		}
+
+	/* Anything else needs extended CRL support */
+
+	if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT))
+		return;
+
+	/* Otherwise the CRL issuer is not on the path. Look for it in the
+	 * set of untrusted certificates.
+	 */
+	for (i = 0; i < sk_X509_num(ctx->untrusted); i++)
+		{
+		crl_issuer = sk_X509_value(ctx->untrusted, i);
+		if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm))
+			continue;
+		if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK)
+			{
+			*pissuer = crl_issuer;
+			*pcrl_score |= CRL_SCORE_AKID;
+			return;
+			}
+		}
+	}
+
+/* Check the path of a CRL issuer certificate. This creates a new
+ * X509_STORE_CTX and populates it with most of the parameters from the
+ * parent. This could be optimised somewhat since a lot of path checking
+ * will be duplicated by the parent, but this will rarely be used in 
+ * practice.
+ */
+
+static int check_crl_path(X509_STORE_CTX *ctx, X509 *x)
+	{
+	X509_STORE_CTX crl_ctx;
+	int ret;
+	/* Don't allow recursive CRL path validation */
+	if (ctx->parent)
+		return 0;
+	if (!X509_STORE_CTX_init(&crl_ctx, ctx->ctx, x, ctx->untrusted))
+		return -1;
+
+	crl_ctx.crls = ctx->crls;
+	/* Copy verify params across */
+	X509_STORE_CTX_set0_param(&crl_ctx, ctx->param);
+
+	crl_ctx.parent = ctx;
+	crl_ctx.verify_cb = ctx->verify_cb;
+
+	/* Verify CRL issuer */
+	ret = X509_verify_cert(&crl_ctx);
+
+	if (ret <= 0)
+		goto err;
+
+	/* Check chain is acceptable */
+
+	ret = check_crl_chain(ctx, ctx->chain, crl_ctx.chain);
+	err:
+	X509_STORE_CTX_cleanup(&crl_ctx);
+	return ret;
+	}
+
+/* RFC3280 says nothing about the relationship between CRL path
+ * and certificate path, which could lead to situations where a
+ * certificate could be revoked or validated by a CA not authorised
+ * to do so. RFC5280 is more strict and states that the two paths must
+ * end in the same trust anchor, though some discussions remain...
+ * until this is resolved we use the RFC5280 version
+ */
+
+static int check_crl_chain(X509_STORE_CTX *ctx,
+			STACK_OF(X509) *cert_path,
+			STACK_OF(X509) *crl_path)
+	{
+	X509 *cert_ta, *crl_ta;
+	cert_ta = sk_X509_value(cert_path, sk_X509_num(cert_path) - 1);
+	crl_ta = sk_X509_value(crl_path, sk_X509_num(crl_path) - 1);
+	if (!X509_cmp(cert_ta, crl_ta))
+		return 1;
+	return 0;
+	}
+
+/* Check for match between two dist point names: three separate cases.
+ * 1. Both are relative names and compare X509_NAME types.
+ * 2. One full, one relative. Compare X509_NAME to GENERAL_NAMES.
+ * 3. Both are full names and compare two GENERAL_NAMES.
+ * 4. One is NULL: automatic match.
+ */
+
+
+static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b)
+	{
+	X509_NAME *nm = NULL;
+	GENERAL_NAMES *gens = NULL;
+	GENERAL_NAME *gena, *genb;
+	size_t i, j;
+	if (!a || !b)
+		return 1;
+	if (a->type == 1)
+		{
+		if (!a->dpname)
+			return 0;
+		/* Case 1: two X509_NAME */
+		if (b->type == 1)
+			{
+			if (!b->dpname)
+				return 0;
+			if (!X509_NAME_cmp(a->dpname, b->dpname))
+				return 1;
+			else
+				return 0;
+			}
+		/* Case 2: set name and GENERAL_NAMES appropriately */
+		nm = a->dpname;
+		gens = b->name.fullname;
+		}
+	else if (b->type == 1)
+		{
+		if (!b->dpname)
+			return 0;
+		/* Case 2: set name and GENERAL_NAMES appropriately */
+		gens = a->name.fullname;
+		nm = b->dpname;
+		}
+
+	/* Handle case 2 with one GENERAL_NAMES and one X509_NAME */
+	if (nm)
+		{
+		for (i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+			{
+			gena = sk_GENERAL_NAME_value(gens, i);	
+			if (gena->type != GEN_DIRNAME)
+				continue;
+			if (!X509_NAME_cmp(nm, gena->d.directoryName))
+				return 1;
+			}
+		return 0;
+		}
+
+	/* Else case 3: two GENERAL_NAMES */
+
+	for (i = 0; i < sk_GENERAL_NAME_num(a->name.fullname); i++)
+		{
+		gena = sk_GENERAL_NAME_value(a->name.fullname, i);
+		for (j = 0; j < sk_GENERAL_NAME_num(b->name.fullname); j++)
+			{
+			genb = sk_GENERAL_NAME_value(b->name.fullname, j);
+			if (!GENERAL_NAME_cmp(gena, genb))
+				return 1;
+			}
+		}
+
+	return 0;
+
+	}
+
+static int crldp_check_crlissuer(DIST_POINT *dp, X509_CRL *crl, int crl_score)
+	{
+	size_t i;
+	X509_NAME *nm = X509_CRL_get_issuer(crl);
+	/* If no CRLissuer return is successful iff don't need a match */
+	if (!dp->CRLissuer)
+		return !!(crl_score & CRL_SCORE_ISSUER_NAME);
+	for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++)
+		{
+		GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i);
+		if (gen->type != GEN_DIRNAME)
+			continue;
+		if (!X509_NAME_cmp(gen->d.directoryName, nm))
+			return 1;
+		}
+	return 0;
+	}
+
+/* Check CRLDP and IDP */
+
+static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score,
+				unsigned int *preasons)
+	{
+	size_t i;
+	if (crl->idp_flags & IDP_ONLYATTR)
+		return 0;
+	if (x->ex_flags & EXFLAG_CA)
+		{
+		if (crl->idp_flags & IDP_ONLYUSER)
+			return 0;
+		}
+	else
+		{
+		if (crl->idp_flags & IDP_ONLYCA)
+			return 0;
+		}
+	*preasons = crl->idp_reasons;
+	for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++)
+		{
+		DIST_POINT *dp = sk_DIST_POINT_value(x->crldp, i);
+		if (crldp_check_crlissuer(dp, crl, crl_score))
+			{
+			if (!crl->idp ||
+			     idp_check_dp(dp->distpoint, crl->idp->distpoint))
+				{
+				*preasons &= dp->dp_reasons;
+				return 1;
+				}
+			}
+		}
+	if ((!crl->idp || !crl->idp->distpoint) && (crl_score & CRL_SCORE_ISSUER_NAME))
+		return 1;
+	return 0;
+	}
+
+/* Retrieve CRL corresponding to current certificate.
+ * If deltas enabled try to find a delta CRL too
+ */
+	
+static int get_crl_delta(X509_STORE_CTX *ctx,
+				X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x)
+	{
+	int ok;
+	X509 *issuer = NULL;
+	int crl_score = 0;
+	unsigned int reasons;
+	X509_CRL *crl = NULL, *dcrl = NULL;
+	STACK_OF(X509_CRL) *skcrl;
+	X509_NAME *nm = X509_get_issuer_name(x);
+	reasons = ctx->current_reasons;
+	ok = get_crl_sk(ctx, &crl, &dcrl, 
+				&issuer, &crl_score, &reasons, ctx->crls);
+
+	if (ok)
+		goto done;
+
+	/* Lookup CRLs from store */
+
+	skcrl = ctx->lookup_crls(ctx, nm);
+
+	/* If no CRLs found and a near match from get_crl_sk use that */
+	if (!skcrl && crl)
+		goto done;
+
+	get_crl_sk(ctx, &crl, &dcrl, &issuer, &crl_score, &reasons, skcrl);
+
+	sk_X509_CRL_pop_free(skcrl, X509_CRL_free);
+
+	done:
+
+	/* If we got any kind of CRL use it and return success */
+	if (crl)
+		{
+		ctx->current_issuer = issuer;
+		ctx->current_crl_score = crl_score;
+		ctx->current_reasons = reasons;
+		*pcrl = crl;
+		*pdcrl = dcrl;
+		return 1;
+		}
+
+	return 0;
+	}
+
+/* Check CRL validity */
+static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl)
+	{
+	X509 *issuer = NULL;
+	EVP_PKEY *ikey = NULL;
+	int ok = 0, chnum, cnum;
+	cnum = ctx->error_depth;
+	chnum = sk_X509_num(ctx->chain) - 1;
+	/* if we have an alternative CRL issuer cert use that */
+	if (ctx->current_issuer)
+		issuer = ctx->current_issuer;
+
+	/* Else find CRL issuer: if not last certificate then issuer
+	 * is next certificate in chain.
+	 */
+	else if (cnum < chnum)
+		issuer = sk_X509_value(ctx->chain, cnum + 1);
+	else
+		{
+		issuer = sk_X509_value(ctx->chain, chnum);
+		/* If not self signed, can't check signature */
+		if(!ctx->check_issued(ctx, issuer, issuer))
+			{
+			ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER;
+			ok = ctx->verify_cb(0, ctx);
+			if(!ok) goto err;
+			}
+		}
+
+	if(issuer)
+		{
+		/* Skip most tests for deltas because they have already
+		 * been done
+		 */
+		if (!crl->base_crl_number)
+			{
+			/* Check for cRLSign bit if keyUsage present */
+			if ((issuer->ex_flags & EXFLAG_KUSAGE) &&
+				!(issuer->ex_kusage & KU_CRL_SIGN))
+				{
+				ctx->error = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN;
+				ok = ctx->verify_cb(0, ctx);
+				if(!ok) goto err;
+				}
+
+			if (!(ctx->current_crl_score & CRL_SCORE_SCOPE))
+				{
+				ctx->error = X509_V_ERR_DIFFERENT_CRL_SCOPE;
+				ok = ctx->verify_cb(0, ctx);
+				if(!ok) goto err;
+				}
+
+			if (!(ctx->current_crl_score & CRL_SCORE_SAME_PATH))
+				{
+				if (check_crl_path(ctx, ctx->current_issuer) <= 0)
+					{
+					ctx->error = X509_V_ERR_CRL_PATH_VALIDATION_ERROR;
+					ok = ctx->verify_cb(0, ctx);
+					if(!ok) goto err;
+					}
+				}
+
+			if (crl->idp_flags & IDP_INVALID)
+				{
+				ctx->error = X509_V_ERR_INVALID_EXTENSION;
+				ok = ctx->verify_cb(0, ctx);
+				if(!ok) goto err;
+				}
+
+
+			}
+
+		if (!(ctx->current_crl_score & CRL_SCORE_TIME))
+			{
+			ok = check_crl_time(ctx, crl, 1);
+			if (!ok)
+				goto err;
+			}
+
+		/* Attempt to get issuer certificate public key */
+		ikey = X509_get_pubkey(issuer);
+
+		if(!ikey)
+			{
+			ctx->error=X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+			ok = ctx->verify_cb(0, ctx);
+			if (!ok) goto err;
+			}
+		else
+			{
+			int rv;
+			rv = X509_CRL_check_suiteb(crl, ikey, ctx->param->flags);
+			if (rv != X509_V_OK)
+				{
+				ctx->error=rv;
+				ok = ctx->verify_cb(0, ctx);
+				if (!ok)
+					goto err;
+				}
+			/* Verify CRL signature */
+			if(X509_CRL_verify(crl, ikey) <= 0)
+				{
+				ctx->error=X509_V_ERR_CRL_SIGNATURE_FAILURE;
+				ok = ctx->verify_cb(0, ctx);
+				if (!ok) goto err;
+				}
+			}
+		}
+
+	ok = 1;
+
+	err:
+	EVP_PKEY_free(ikey);
+	return ok;
+	}
+
+/* Check certificate against CRL */
+static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x)
+	{
+	int ok;
+	X509_REVOKED *rev;
+	/* The rules changed for this... previously if a CRL contained
+	 * unhandled critical extensions it could still be used to indicate
+	 * a certificate was revoked. This has since been changed since 
+	 * critical extension can change the meaning of CRL entries.
+	 */
+	if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL)
+		&& (crl->flags & EXFLAG_CRITICAL))
+		{
+		ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION;
+		ok = ctx->verify_cb(0, ctx);
+		if(!ok)
+			return 0;
+		}
+	/* Look for serial number of certificate in CRL
+	 * If found make sure reason is not removeFromCRL.
+	 */
+	if (X509_CRL_get0_by_cert(crl, &rev, x))
+		{
+		if (rev->reason == CRL_REASON_REMOVE_FROM_CRL)
+			return 2;
+		ctx->error = X509_V_ERR_CERT_REVOKED;
+		ok = ctx->verify_cb(0, ctx);
+		if (!ok)
+			return 0;
+		}
+
+	return 1;
+	}
+
+static int check_policy(X509_STORE_CTX *ctx)
+	{
+	int ret;
+	if (ctx->parent)
+		return 1;
+	ret = X509_policy_check(&ctx->tree, &ctx->explicit_policy, ctx->chain,
+				ctx->param->policies, ctx->param->flags);
+	if (ret == 0)
+		{
+		OPENSSL_PUT_ERROR(X509, check_policy, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	/* Invalid or inconsistent extensions */
+	if (ret == -1)
+		{
+		/* Locate certificates with bad extensions and notify
+		 * callback.
+		 */
+		X509 *x;
+		size_t i;
+		for (i = 1; i < sk_X509_num(ctx->chain); i++)
+			{
+			x = sk_X509_value(ctx->chain, i);
+			if (!(x->ex_flags & EXFLAG_INVALID_POLICY))
+				continue;
+			ctx->current_cert = x;
+			ctx->error = X509_V_ERR_INVALID_POLICY_EXTENSION;
+			if(!ctx->verify_cb(0, ctx))
+				return 0;
+			}
+		return 1;
+		}
+	if (ret == -2)
+		{
+		ctx->current_cert = NULL;
+		ctx->error = X509_V_ERR_NO_EXPLICIT_POLICY;
+		return ctx->verify_cb(0, ctx);
+		}
+
+	if (ctx->param->flags & X509_V_FLAG_NOTIFY_POLICY)
+		{
+		ctx->current_cert = NULL;
+		ctx->error = X509_V_OK;
+		if (!ctx->verify_cb(2, ctx))
+			return 0;
+		}
+
+	return 1;
+	}
+
+static int check_cert_time(X509_STORE_CTX *ctx, X509 *x)
+	{
+	time_t *ptime;
+	int i;
+
+	if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME)
+		ptime = &ctx->param->check_time;
+	else
+		ptime = NULL;
+
+	i=X509_cmp_time(X509_get_notBefore(x), ptime);
+	if (i == 0)
+		{
+		ctx->error=X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD;
+		ctx->current_cert=x;
+		if (!ctx->verify_cb(0, ctx))
+			return 0;
+		}
+
+	if (i > 0)
+		{
+		ctx->error=X509_V_ERR_CERT_NOT_YET_VALID;
+		ctx->current_cert=x;
+		if (!ctx->verify_cb(0, ctx))
+			return 0;
+		}
+
+	i=X509_cmp_time(X509_get_notAfter(x), ptime);
+	if (i == 0)
+		{
+		ctx->error=X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD;
+		ctx->current_cert=x;
+		if (!ctx->verify_cb(0, ctx))
+			return 0;
+		}
+
+	if (i < 0)
+		{
+		ctx->error=X509_V_ERR_CERT_HAS_EXPIRED;
+		ctx->current_cert=x;
+		if (!ctx->verify_cb(0, ctx))
+			return 0;
+		}
+
+	return 1;
+	}
+
+static int internal_verify(X509_STORE_CTX *ctx)
+	{
+	int ok=0,n;
+	X509 *xs,*xi;
+	EVP_PKEY *pkey=NULL;
+	int (*cb)(int xok,X509_STORE_CTX *xctx);
+
+	cb=ctx->verify_cb;
+
+	n=sk_X509_num(ctx->chain);
+	ctx->error_depth=n-1;
+	n--;
+	xi=sk_X509_value(ctx->chain,n);
+
+	if (ctx->check_issued(ctx, xi, xi))
+		xs=xi;
+	else
+		{
+		if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN && n == 0)
+			{
+			xs = xi;
+			goto check_cert;
+			}
+		if (n <= 0)
+			{
+			ctx->error=X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE;
+			ctx->current_cert=xi;
+			ok=cb(0,ctx);
+			goto end;
+			}
+		else
+			{
+			n--;
+			ctx->error_depth=n;
+			xs=sk_X509_value(ctx->chain,n);
+			}
+		}
+
+/*	ctx->error=0;  not needed */
+	while (n >= 0)
+		{
+		ctx->error_depth=n;
+
+		/* Skip signature check for self signed certificates unless
+		 * explicitly asked for. It doesn't add any security and
+		 * just wastes time.
+		 */
+		if (!xs->valid && (xs != xi || (ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)))
+			{
+			if ((pkey=X509_get_pubkey(xi)) == NULL)
+				{
+				ctx->error=X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+				ctx->current_cert=xi;
+				ok=(*cb)(0,ctx);
+				if (!ok) goto end;
+				}
+			else if (X509_verify(xs,pkey) <= 0)
+				{
+				ctx->error=X509_V_ERR_CERT_SIGNATURE_FAILURE;
+				ctx->current_cert=xs;
+				ok=(*cb)(0,ctx);
+				if (!ok)
+					{
+					EVP_PKEY_free(pkey);
+					goto end;
+					}
+				}
+			EVP_PKEY_free(pkey);
+			pkey=NULL;
+			}
+
+		xs->valid = 1;
+
+		check_cert:
+		ok = check_cert_time(ctx, xs);
+		if (!ok)
+			goto end;
+
+		/* The last error (if any) is still in the error value */
+		ctx->current_issuer=xi;
+		ctx->current_cert=xs;
+		ok=(*cb)(1,ctx);
+		if (!ok) goto end;
+
+		n--;
+		if (n >= 0)
+			{
+			xi=xs;
+			xs=sk_X509_value(ctx->chain,n);
+			}
+		}
+	ok=1;
+end:
+	return ok;
+	}
+
+int X509_cmp_current_time(const ASN1_TIME *ctm)
+{
+	return X509_cmp_time(ctm, NULL);
+}
+
+int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time)
+	{
+	char *str;
+	ASN1_TIME atm;
+	long offset;
+	char buff1[24],buff2[24],*p;
+	int i,j;
+
+	p=buff1;
+	i=ctm->length;
+	str=(char *)ctm->data;
+	if (ctm->type == V_ASN1_UTCTIME)
+		{
+		if ((i < 11) || (i > 17)) return 0;
+		memcpy(p,str,10);
+		p+=10;
+		str+=10;
+		}
+	else
+		{
+		if (i < 13) return 0;
+		memcpy(p,str,12);
+		p+=12;
+		str+=12;
+		}
+
+	if ((*str == 'Z') || (*str == '-') || (*str == '+'))
+		{ *(p++)='0'; *(p++)='0'; }
+	else
+		{ 
+		*(p++)= *(str++);
+		*(p++)= *(str++);
+		/* Skip any fractional seconds... */
+		if (*str == '.')
+			{
+			str++;
+			while ((*str >= '0') && (*str <= '9')) str++;
+			}
+		
+		}
+	*(p++)='Z';
+	*(p++)='\0';
+
+	if (*str == 'Z')
+		offset=0;
+	else
+		{
+		if ((*str != '+') && (*str != '-'))
+			return 0;
+		offset=((str[1]-'0')*10+(str[2]-'0'))*60;
+		offset+=(str[3]-'0')*10+(str[4]-'0');
+		if (*str == '-')
+			offset= -offset;
+		}
+	atm.type=ctm->type;
+	atm.flags = 0;
+	atm.length=sizeof(buff2);
+	atm.data=(unsigned char *)buff2;
+
+	if (X509_time_adj(&atm, offset*60, cmp_time) == NULL)
+		return 0;
+
+	if (ctm->type == V_ASN1_UTCTIME)
+		{
+		i=(buff1[0]-'0')*10+(buff1[1]-'0');
+		if (i < 50) i+=100; /* cf. RFC 2459 */
+		j=(buff2[0]-'0')*10+(buff2[1]-'0');
+		if (j < 50) j+=100;
+
+		if (i < j) return -1;
+		if (i > j) return 1;
+		}
+	i=strcmp(buff1,buff2);
+	if (i == 0) /* wait a second then return younger :-) */
+		return -1;
+	else
+		return i;
+	}
+
+ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj)
+{
+	return X509_time_adj(s, adj, NULL);
+}
+
+ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec, time_t *in_tm)
+	{
+	return X509_time_adj_ex(s, 0, offset_sec, in_tm);
+	}
+
+ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s,
+				int offset_day, long offset_sec, time_t *in_tm)
+	{
+	time_t t;
+
+	if (in_tm) t = *in_tm;
+	else time(&t);
+
+	if (s && !(s->flags & ASN1_STRING_FLAG_MSTRING))
+		{
+		if (s->type == V_ASN1_UTCTIME)
+			return ASN1_UTCTIME_adj(s,t, offset_day, offset_sec);
+		if (s->type == V_ASN1_GENERALIZEDTIME)
+			return ASN1_GENERALIZEDTIME_adj(s, t, offset_day,
+								offset_sec);
+		}
+	return ASN1_TIME_adj(s, t, offset_day, offset_sec);
+	}
+
+int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain)
+	{
+	EVP_PKEY *ktmp=NULL,*ktmp2;
+	size_t i,j;
+
+	if ((pkey != NULL) && !EVP_PKEY_missing_parameters(pkey)) return 1;
+
+	for (i=0; i<sk_X509_num(chain); i++)
+		{
+		ktmp=X509_get_pubkey(sk_X509_value(chain,i));
+		if (ktmp == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_get_pubkey_parameters, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY);
+			return 0;
+			}
+		if (!EVP_PKEY_missing_parameters(ktmp))
+			break;
+		else
+			{
+			EVP_PKEY_free(ktmp);
+			ktmp=NULL;
+			}
+		}
+	if (ktmp == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_get_pubkey_parameters, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN);
+		return 0;
+		}
+
+	/* first, populate the other certs */
+	for (j=i-1; j < i; j--)
+		{
+		ktmp2=X509_get_pubkey(sk_X509_value(chain,j));
+		EVP_PKEY_copy_parameters(ktmp2,ktmp);
+		EVP_PKEY_free(ktmp2);
+		}
+	
+	if (pkey != NULL) EVP_PKEY_copy_parameters(pkey,ktmp);
+	EVP_PKEY_free(ktmp);
+	return 1;
+	}
+
+/* Make a delta CRL as the diff between two full CRLs */
+
+X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer,
+			EVP_PKEY *skey, const EVP_MD *md, unsigned int flags)
+	{
+	X509_CRL *crl = NULL;
+	int i;
+	size_t j;
+	STACK_OF(X509_REVOKED) *revs = NULL;
+	/* CRLs can't be delta already */
+	if (base->base_crl_number || newer->base_crl_number)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_CRL_ALREADY_DELTA);
+			return NULL;
+			}
+	/* Base and new CRL must have a CRL number */
+	if (!base->crl_number || !newer->crl_number)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_NO_CRL_NUMBER);
+			return NULL;
+			}
+	/* Issuer names must match */
+	if (X509_NAME_cmp(X509_CRL_get_issuer(base),
+				X509_CRL_get_issuer(newer)))
+			{
+			OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_ISSUER_MISMATCH);
+			return NULL;
+			}
+	/* AKID and IDP must match */
+	if (!crl_extension_match(base, newer, NID_authority_key_identifier))
+			{
+			OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_AKID_MISMATCH);
+			return NULL;
+			}
+	if (!crl_extension_match(base, newer, NID_issuing_distribution_point))
+			{
+			OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_IDP_MISMATCH);
+			return NULL;
+			}
+	/* Newer CRL number must exceed full CRL number */
+	if (ASN1_INTEGER_cmp(newer->crl_number, base->crl_number) <= 0)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_NEWER_CRL_NOT_NEWER);
+			return NULL;
+			}
+	/* CRLs must verify */
+	if (skey && (X509_CRL_verify(base, skey) <= 0 ||
+			X509_CRL_verify(newer, skey) <= 0))
+		{
+		OPENSSL_PUT_ERROR(X509, X509_CRL_diff, X509_R_CRL_VERIFY_FAILURE);
+		return NULL;
+		}
+	/* Create new CRL */
+	crl = X509_CRL_new();
+	if (!crl || !X509_CRL_set_version(crl, 1))
+		goto memerr;
+	/* Set issuer name */
+	if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer)))
+		goto memerr;
+
+	if (!X509_CRL_set_lastUpdate(crl, X509_CRL_get_lastUpdate(newer)))
+		goto memerr;
+	if (!X509_CRL_set_nextUpdate(crl, X509_CRL_get_nextUpdate(newer)))
+		goto memerr;
+
+	/* Set base CRL number: must be critical */
+
+	if (!X509_CRL_add1_ext_i2d(crl, NID_delta_crl, base->crl_number, 1, 0))
+		goto memerr;
+
+	/* Copy extensions across from newest CRL to delta: this will set
+	 * CRL number to correct value too.
+	 */
+
+	for (i = 0; i < X509_CRL_get_ext_count(newer); i++)
+		{
+		X509_EXTENSION *ext;
+		ext = X509_CRL_get_ext(newer, i);
+		if (!X509_CRL_add_ext(crl, ext, -1))
+			goto memerr;
+		}
+
+	/* Go through revoked entries, copying as needed */
+
+	revs = X509_CRL_get_REVOKED(newer);
+
+	for (j = 0; j < sk_X509_REVOKED_num(revs); j++)
+		{
+		X509_REVOKED *rvn, *rvtmp;
+		rvn = sk_X509_REVOKED_value(revs, j);
+		/* Add only if not also in base.
+		 * TODO: need something cleverer here for some more complex
+		 * CRLs covering multiple CAs.
+		 */
+		if (!X509_CRL_get0_by_serial(base, &rvtmp, rvn->serialNumber))
+			{
+			rvtmp = X509_REVOKED_dup(rvn);
+			if (!rvtmp)
+				goto memerr;
+			if (!X509_CRL_add0_revoked(crl, rvtmp))
+				{
+				X509_REVOKED_free(rvtmp);
+				goto memerr;
+				}
+			}
+		}
+	/* TODO: optionally prune deleted entries */
+
+	if (skey && md && !X509_CRL_sign(crl, skey, md))
+		goto memerr;
+	
+	return crl;
+
+	memerr:
+	OPENSSL_PUT_ERROR(X509, X509_CRL_diff, ERR_R_MALLOC_FAILURE);
+	if (crl)
+		X509_CRL_free(crl);
+	return NULL;
+	}
+
+int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+	     CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
+	{
+	/* This function is (usually) called only once, by
+	 * SSL_get_ex_data_X509_STORE_CTX_idx (ssl/ssl_cert.c). */
+	return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE_CTX, argl, argp,
+			new_func, dup_func, free_func);
+	}
+
+int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx, int idx, void *data)
+	{
+	return CRYPTO_set_ex_data(&ctx->ex_data,idx,data);
+	}
+
+void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx)
+	{
+	return CRYPTO_get_ex_data(&ctx->ex_data,idx);
+	}
+
+int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx)
+	{
+	return ctx->error;
+	}
+
+void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int err)
+	{
+	ctx->error=err;
+	}
+
+int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx)
+	{
+	return ctx->error_depth;
+	}
+
+X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx)
+	{
+	return ctx->current_cert;
+	}
+
+STACK_OF(X509) *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx)
+	{
+	return ctx->chain;
+	}
+
+STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx)
+	{
+	size_t i;
+	X509 *x;
+	STACK_OF(X509) *chain;
+	if (!ctx->chain || !(chain = sk_X509_dup(ctx->chain))) return NULL;
+	for (i = 0; i < sk_X509_num(chain); i++)
+		{
+		x = sk_X509_value(chain, i);
+		CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+		}
+	return chain;
+	}
+
+X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx)
+	{
+	return ctx->current_issuer;
+	}
+
+X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx)
+	{
+	return ctx->current_crl;
+	}
+
+X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx)
+	{
+	return ctx->parent;
+	}
+
+void X509_STORE_CTX_set_cert(X509_STORE_CTX *ctx, X509 *x)
+	{
+	ctx->cert=x;
+	}
+
+void X509_STORE_CTX_set_chain(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
+	{
+	ctx->untrusted=sk;
+	}
+
+void X509_STORE_CTX_set0_crls(X509_STORE_CTX *ctx, STACK_OF(X509_CRL) *sk)
+	{
+	ctx->crls=sk;
+	}
+
+int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose)
+	{
+	return X509_STORE_CTX_purpose_inherit(ctx, 0, purpose, 0);
+	}
+
+int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust)
+	{
+	return X509_STORE_CTX_purpose_inherit(ctx, 0, 0, trust);
+	}
+
+/* This function is used to set the X509_STORE_CTX purpose and trust
+ * values. This is intended to be used when another structure has its
+ * own trust and purpose values which (if set) will be inherited by
+ * the ctx. If they aren't set then we will usually have a default
+ * purpose in mind which should then be used to set the trust value.
+ * An example of this is SSL use: an SSL structure will have its own
+ * purpose and trust settings which the application can set: if they
+ * aren't set then we use the default of SSL client/server.
+ */
+
+int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,
+				int purpose, int trust)
+{
+	int idx;
+	/* If purpose not set use default */
+	if (!purpose) purpose = def_purpose;
+	/* If we have a purpose then check it is valid */
+	if (purpose)
+		{
+		X509_PURPOSE *ptmp;
+		idx = X509_PURPOSE_get_by_id(purpose);
+		if (idx == -1)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_purpose_inherit, X509_R_UNKNOWN_PURPOSE_ID);
+			return 0;
+			}
+		ptmp = X509_PURPOSE_get0(idx);
+		if (ptmp->trust == X509_TRUST_DEFAULT)
+			{
+			idx = X509_PURPOSE_get_by_id(def_purpose);
+			if (idx == -1)
+				{
+				OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_purpose_inherit, X509_R_UNKNOWN_PURPOSE_ID);
+				return 0;
+				}
+			ptmp = X509_PURPOSE_get0(idx);
+			}
+		/* If trust not set then get from purpose default */
+		if (!trust) trust = ptmp->trust;
+		}
+	if (trust)
+		{
+		idx = X509_TRUST_get_by_id(trust);
+		if (idx == -1)
+			{
+			OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_purpose_inherit, X509_R_UNKNOWN_TRUST_ID);
+			return 0;
+			}
+		}
+
+	if (purpose && !ctx->param->purpose) ctx->param->purpose = purpose;
+	if (trust && !ctx->param->trust) ctx->param->trust = trust;
+	return 1;
+}
+
+X509_STORE_CTX *X509_STORE_CTX_new(void)
+{
+	X509_STORE_CTX *ctx;
+	ctx = (X509_STORE_CTX *)OPENSSL_malloc(sizeof(X509_STORE_CTX));
+	if (!ctx)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_new, ERR_R_MALLOC_FAILURE);
+		return NULL;
+		}
+	memset(ctx, 0, sizeof(X509_STORE_CTX));
+	return ctx;
+}
+
+void X509_STORE_CTX_free(X509_STORE_CTX *ctx)
+{
+	X509_STORE_CTX_cleanup(ctx);
+	OPENSSL_free(ctx);
+}
+
+int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
+	     STACK_OF(X509) *chain)
+	{
+	int ret = 1;
+	ctx->ctx=store;
+	ctx->current_method=0;
+	ctx->cert=x509;
+	ctx->untrusted=chain;
+	ctx->crls = NULL;
+	ctx->last_untrusted=0;
+	ctx->other_ctx=NULL;
+	ctx->valid=0;
+	ctx->chain=NULL;
+	ctx->error=0;
+	ctx->explicit_policy=0;
+	ctx->error_depth=0;
+	ctx->current_cert=NULL;
+	ctx->current_issuer=NULL;
+	ctx->current_crl=NULL;
+	ctx->current_crl_score=0;
+	ctx->current_reasons=0;
+	ctx->tree = NULL;
+	ctx->parent = NULL;
+
+	ctx->param = X509_VERIFY_PARAM_new();
+
+	if (!ctx->param)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_init, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+
+	/* Inherit callbacks and flags from X509_STORE if not set
+	 * use defaults.
+	 */
+
+
+	if (store)
+		ret = X509_VERIFY_PARAM_inherit(ctx->param, store->param);
+	else
+		ctx->param->inh_flags |= X509_VP_FLAG_DEFAULT|X509_VP_FLAG_ONCE;
+
+	if (store)
+		{
+		ctx->verify_cb = store->verify_cb;
+		ctx->cleanup = store->cleanup;
+		}
+	else
+		ctx->cleanup = 0;
+
+	if (ret)
+		ret = X509_VERIFY_PARAM_inherit(ctx->param,
+					X509_VERIFY_PARAM_lookup("default"));
+
+	if (ret == 0)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_init, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+
+	if (store && store->check_issued)
+		ctx->check_issued = store->check_issued;
+	else
+		ctx->check_issued = check_issued;
+
+	if (store && store->get_issuer)
+		ctx->get_issuer = store->get_issuer;
+	else
+		ctx->get_issuer = X509_STORE_CTX_get1_issuer;
+
+	if (store && store->verify_cb)
+		ctx->verify_cb = store->verify_cb;
+	else
+		ctx->verify_cb = null_callback;
+
+	if (store && store->verify)
+		ctx->verify = store->verify;
+	else
+		ctx->verify = internal_verify;
+
+	if (store && store->check_revocation)
+		ctx->check_revocation = store->check_revocation;
+	else
+		ctx->check_revocation = check_revocation;
+
+	if (store && store->get_crl)
+		ctx->get_crl = store->get_crl;
+	else
+		ctx->get_crl = NULL;
+
+	if (store && store->check_crl)
+		ctx->check_crl = store->check_crl;
+	else
+		ctx->check_crl = check_crl;
+
+	if (store && store->cert_crl)
+		ctx->cert_crl = store->cert_crl;
+	else
+		ctx->cert_crl = cert_crl;
+
+	if (store && store->lookup_certs)
+		ctx->lookup_certs = store->lookup_certs;
+	else
+		ctx->lookup_certs = X509_STORE_get1_certs;
+
+	if (store && store->lookup_crls)
+		ctx->lookup_crls = store->lookup_crls;
+	else
+		ctx->lookup_crls = X509_STORE_get1_crls;
+
+	ctx->check_policy = check_policy;
+
+
+	/* This memset() can't make any sense anyway, so it's removed. As
+	 * X509_STORE_CTX_cleanup does a proper "free" on the ex_data, we put a
+	 * corresponding "new" here and remove this bogus initialisation. */
+	/* memset(&(ctx->ex_data),0,sizeof(CRYPTO_EX_DATA)); */
+	if(!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx,
+				&(ctx->ex_data)))
+		{
+		OPENSSL_free(ctx);
+		OPENSSL_PUT_ERROR(X509, X509_STORE_CTX_init, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	return 1;
+	}
+
+/* Set alternative lookup method: just a STACK of trusted certificates.
+ * This avoids X509_STORE nastiness where it isn't needed.
+ */
+
+void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
+{
+	ctx->other_ctx = sk;
+	ctx->get_issuer = get_issuer_sk;
+}
+
+void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx)
+	{
+	if (ctx->cleanup) ctx->cleanup(ctx);
+	if (ctx->param != NULL)
+		{
+		if (ctx->parent == NULL)
+			X509_VERIFY_PARAM_free(ctx->param);
+		ctx->param=NULL;
+		}
+	if (ctx->tree != NULL)
+		{
+		X509_policy_tree_free(ctx->tree);
+		ctx->tree=NULL;
+		}
+	if (ctx->chain != NULL)
+		{
+		sk_X509_pop_free(ctx->chain,X509_free);
+		ctx->chain=NULL;
+		}
+	CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, &(ctx->ex_data));
+	memset(&ctx->ex_data,0,sizeof(CRYPTO_EX_DATA));
+	}
+
+void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth)
+	{
+	X509_VERIFY_PARAM_set_depth(ctx->param, depth);
+	}
+
+void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags)
+	{
+	X509_VERIFY_PARAM_set_flags(ctx->param, flags);
+	}
+
+void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags, time_t t)
+	{
+	X509_VERIFY_PARAM_set_time(ctx->param, t);
+	}
+
+void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx,
+				  int (*verify_cb)(int, X509_STORE_CTX *))
+	{
+	ctx->verify_cb=verify_cb;
+	}
+
+X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx)
+	{
+	return ctx->tree;
+	}
+
+int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx)
+	{
+	return ctx->explicit_policy;
+	}
+
+int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name)
+	{
+	const X509_VERIFY_PARAM *param;
+	param = X509_VERIFY_PARAM_lookup(name);
+	if (!param)
+		return 0;
+	return X509_VERIFY_PARAM_inherit(ctx->param, param);
+	}
+
+X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx)
+	{
+	return ctx->param;
+	}
+
+void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param)
+	{
+	if (ctx->param)
+		X509_VERIFY_PARAM_free(ctx->param);
+	ctx->param = param;
+	}
+
+IMPLEMENT_ASN1_SET_OF(X509)
+IMPLEMENT_ASN1_SET_OF(X509_ATTRIBUTE)
diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h
new file mode 100644
index 0000000..6054250
--- /dev/null
+++ b/crypto/x509/x509_vfy.h
@@ -0,0 +1,605 @@
+/* crypto/x509/x509_vfy.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_X509_H
+#include <openssl/x509.h>
+/* openssl/x509.h ends up #include-ing this file at about the only
+ * appropriate moment. */
+#endif
+
+#ifndef HEADER_X509_VFY_H
+#define HEADER_X509_VFY_H
+
+#include <openssl/bio.h>
+#include <openssl/lhash.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if 0
+/* Outer object */
+typedef struct x509_hash_dir_st
+	{
+	int num_dirs;
+	char **dirs;
+	int *dirs_type;
+	int num_dirs_alloced;
+	} X509_HASH_DIR_CTX;
+#endif
+
+typedef struct x509_file_st
+	{
+	int num_paths;	/* number of paths to files or directories */
+	int num_alloced;
+	char **paths;	/* the list of paths or directories */
+	int *path_type;
+	} X509_CERT_FILE_CTX;
+
+/*******************************/
+/*
+SSL_CTX -> X509_STORE    
+		-> X509_LOOKUP
+			->X509_LOOKUP_METHOD
+		-> X509_LOOKUP
+			->X509_LOOKUP_METHOD
+ 
+SSL	-> X509_STORE_CTX
+		->X509_STORE    
+
+The X509_STORE holds the tables etc for verification stuff.
+A X509_STORE_CTX is used while validating a single certificate.
+The X509_STORE has X509_LOOKUPs for looking up certs.
+The X509_STORE then calls a function to actually verify the
+certificate chain.
+*/
+
+#define X509_LU_RETRY		-1
+#define X509_LU_FAIL		0
+#define X509_LU_X509		1
+#define X509_LU_CRL		2
+#define X509_LU_PKEY		3
+
+typedef struct x509_object_st
+	{
+	/* one of the above types */
+	int type;
+	union	{
+		char *ptr;
+		X509 *x509;
+		X509_CRL *crl;
+		EVP_PKEY *pkey;
+		} data;
+	} X509_OBJECT;
+
+typedef struct x509_lookup_st X509_LOOKUP;
+
+DECLARE_STACK_OF(X509_LOOKUP)
+DECLARE_STACK_OF(X509_OBJECT)
+
+/* This is a static that defines the function interface */
+typedef struct x509_lookup_method_st
+	{
+	const char *name;
+	int (*new_item)(X509_LOOKUP *ctx);
+	void (*free)(X509_LOOKUP *ctx);
+	int (*init)(X509_LOOKUP *ctx);
+	int (*shutdown)(X509_LOOKUP *ctx);
+	int (*ctrl)(X509_LOOKUP *ctx,int cmd,const char *argc,long argl,
+			char **ret);
+	int (*get_by_subject)(X509_LOOKUP *ctx,int type,X509_NAME *name,
+			      X509_OBJECT *ret);
+	int (*get_by_issuer_serial)(X509_LOOKUP *ctx,int type,X509_NAME *name,
+				    ASN1_INTEGER *serial,X509_OBJECT *ret);
+	int (*get_by_fingerprint)(X509_LOOKUP *ctx,int type,
+				  unsigned char *bytes,int len,
+				  X509_OBJECT *ret);
+	int (*get_by_alias)(X509_LOOKUP *ctx,int type,char *str,int len,
+			    X509_OBJECT *ret);
+	} X509_LOOKUP_METHOD;
+
+typedef struct X509_VERIFY_PARAM_ID_st X509_VERIFY_PARAM_ID;
+
+/* This structure hold all parameters associated with a verify operation
+ * by including an X509_VERIFY_PARAM structure in related structures the
+ * parameters used can be customized
+ */
+
+typedef struct X509_VERIFY_PARAM_st
+	{
+	char *name;
+	time_t check_time;	/* Time to use */
+	unsigned long inh_flags; /* Inheritance flags */
+	unsigned long flags;	/* Various verify flags */
+	int purpose;		/* purpose to check untrusted certificates */
+	int trust;		/* trust setting to check */
+	int depth;		/* Verify depth */
+	STACK_OF(ASN1_OBJECT) *policies;	/* Permissible policies */
+	X509_VERIFY_PARAM_ID *id;	/* opaque ID data */
+	} X509_VERIFY_PARAM;
+
+DECLARE_STACK_OF(X509_VERIFY_PARAM)
+
+/* This is used to hold everything.  It is used for all certificate
+ * validation.  Once we have a certificate chain, the 'verify'
+ * function is then called to actually check the cert chain. */
+struct x509_store_st
+	{
+	/* The following is a cache of trusted certs */
+	int cache; 	/* if true, stash any hits */
+	STACK_OF(X509_OBJECT) *objs;	/* Cache of all objects */
+
+	/* These are external lookup methods */
+	STACK_OF(X509_LOOKUP) *get_cert_methods;
+
+	X509_VERIFY_PARAM *param;
+
+	/* Callbacks for various operations */
+	int (*verify)(X509_STORE_CTX *ctx);	/* called to verify a certificate */
+	int (*verify_cb)(int ok,X509_STORE_CTX *ctx);	/* error callback */
+	int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);	/* get issuers cert from ctx */
+	int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */
+	int (*check_revocation)(X509_STORE_CTX *ctx); /* Check revocation status of chain */
+	int (*get_crl)(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); /* retrieve CRL */
+	int (*check_crl)(X509_STORE_CTX *ctx, X509_CRL *crl); /* Check CRL validity */
+	int (*cert_crl)(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); /* Check certificate against CRL */
+	STACK_OF(X509) * (*lookup_certs)(X509_STORE_CTX *ctx, X509_NAME *nm);
+	STACK_OF(X509_CRL) * (*lookup_crls)(X509_STORE_CTX *ctx, X509_NAME *nm);
+	int (*cleanup)(X509_STORE_CTX *ctx);
+
+	CRYPTO_EX_DATA ex_data;
+	int references;
+	} /* X509_STORE */;
+
+int X509_STORE_set_depth(X509_STORE *store, int depth);
+
+#define X509_STORE_set_verify_cb_func(ctx,func) ((ctx)->verify_cb=(func))
+#define X509_STORE_set_verify_func(ctx,func)	((ctx)->verify=(func))
+
+/* This is the functions plus an instance of the local variables. */
+struct x509_lookup_st
+	{
+	int init;			/* have we been started */
+	int skip;			/* don't use us. */
+	X509_LOOKUP_METHOD *method;	/* the functions */
+	char *method_data;		/* method data */
+
+	X509_STORE *store_ctx;	/* who owns us */
+	} /* X509_LOOKUP */;
+
+/* This is a used when verifying cert chains.  Since the
+ * gathering of the cert chain can take some time (and have to be
+ * 'retried', this needs to be kept and passed around. */
+struct x509_store_ctx_st      /* X509_STORE_CTX */
+	{
+	X509_STORE *ctx;
+	int current_method;	/* used when looking up certs */
+
+	/* The following are set by the caller */
+	X509 *cert;		/* The cert to check */
+	STACK_OF(X509) *untrusted;	/* chain of X509s - untrusted - passed in */
+	STACK_OF(X509_CRL) *crls;	/* set of CRLs passed in */
+
+	X509_VERIFY_PARAM *param;
+	void *other_ctx;	/* Other info for use with get_issuer() */
+
+	/* Callbacks for various operations */
+	int (*verify)(X509_STORE_CTX *ctx);	/* called to verify a certificate */
+	int (*verify_cb)(int ok,X509_STORE_CTX *ctx);		/* error callback */
+	int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);	/* get issuers cert from ctx */
+	int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */
+	int (*check_revocation)(X509_STORE_CTX *ctx); /* Check revocation status of chain */
+	int (*get_crl)(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); /* retrieve CRL */
+	int (*check_crl)(X509_STORE_CTX *ctx, X509_CRL *crl); /* Check CRL validity */
+	int (*cert_crl)(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); /* Check certificate against CRL */
+	int (*check_policy)(X509_STORE_CTX *ctx);
+	STACK_OF(X509) * (*lookup_certs)(X509_STORE_CTX *ctx, X509_NAME *nm);
+	STACK_OF(X509_CRL) * (*lookup_crls)(X509_STORE_CTX *ctx, X509_NAME *nm);
+	int (*cleanup)(X509_STORE_CTX *ctx);
+
+	/* The following is built up */
+	int valid;		/* if 0, rebuild chain */
+	int last_untrusted;	/* index of last untrusted cert */
+	STACK_OF(X509) *chain; 		/* chain of X509s - built up and trusted */
+	X509_POLICY_TREE *tree;	/* Valid policy tree */
+
+	int explicit_policy;	/* Require explicit policy value */
+
+	/* When something goes wrong, this is why */
+	int error_depth;
+	int error;
+	X509 *current_cert;
+	X509 *current_issuer;	/* cert currently being tested as valid issuer */
+	X509_CRL *current_crl;	/* current CRL */
+
+	int current_crl_score;  /* score of current CRL */
+	unsigned int current_reasons;  /* Reason mask */
+
+	X509_STORE_CTX *parent; /* For CRL path validation: parent context */
+
+	CRYPTO_EX_DATA ex_data;
+	} /* X509_STORE_CTX */;
+
+void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth);
+
+#define X509_STORE_CTX_set_app_data(ctx,data) \
+	X509_STORE_CTX_set_ex_data(ctx,0,data)
+#define X509_STORE_CTX_get_app_data(ctx) \
+	X509_STORE_CTX_get_ex_data(ctx,0)
+
+#define X509_L_FILE_LOAD	1
+#define X509_L_ADD_DIR		2
+
+#define X509_LOOKUP_load_file(x,name,type) \
+		X509_LOOKUP_ctrl((x),X509_L_FILE_LOAD,(name),(long)(type),NULL)
+
+#define X509_LOOKUP_add_dir(x,name,type) \
+		X509_LOOKUP_ctrl((x),X509_L_ADD_DIR,(name),(long)(type),NULL)
+
+#define		X509_V_OK					0
+/* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */
+
+#define		X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT		2
+#define		X509_V_ERR_UNABLE_TO_GET_CRL			3
+#define		X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE	4
+#define		X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE	5
+#define		X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY	6
+#define		X509_V_ERR_CERT_SIGNATURE_FAILURE		7
+#define		X509_V_ERR_CRL_SIGNATURE_FAILURE		8
+#define		X509_V_ERR_CERT_NOT_YET_VALID			9
+#define		X509_V_ERR_CERT_HAS_EXPIRED			10
+#define		X509_V_ERR_CRL_NOT_YET_VALID			11
+#define		X509_V_ERR_CRL_HAS_EXPIRED			12
+#define		X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD	13
+#define		X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD	14
+#define		X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD	15
+#define		X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD	16
+#define		X509_V_ERR_OUT_OF_MEM				17
+#define		X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT		18
+#define		X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN		19
+#define		X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY	20
+#define		X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE	21
+#define		X509_V_ERR_CERT_CHAIN_TOO_LONG			22
+#define		X509_V_ERR_CERT_REVOKED				23
+#define		X509_V_ERR_INVALID_CA				24
+#define		X509_V_ERR_PATH_LENGTH_EXCEEDED			25
+#define		X509_V_ERR_INVALID_PURPOSE			26
+#define		X509_V_ERR_CERT_UNTRUSTED			27
+#define		X509_V_ERR_CERT_REJECTED			28
+/* These are 'informational' when looking for issuer cert */
+#define		X509_V_ERR_SUBJECT_ISSUER_MISMATCH		29
+#define		X509_V_ERR_AKID_SKID_MISMATCH			30
+#define		X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH		31
+#define		X509_V_ERR_KEYUSAGE_NO_CERTSIGN			32
+
+#define		X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER		33
+#define		X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION		34
+#define		X509_V_ERR_KEYUSAGE_NO_CRL_SIGN			35
+#define		X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION	36
+#define		X509_V_ERR_INVALID_NON_CA			37
+#define		X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED		38
+#define		X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE	39
+#define		X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED	40
+
+#define		X509_V_ERR_INVALID_EXTENSION			41
+#define		X509_V_ERR_INVALID_POLICY_EXTENSION		42
+#define		X509_V_ERR_NO_EXPLICIT_POLICY			43
+#define		X509_V_ERR_DIFFERENT_CRL_SCOPE			44
+#define		X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE	45
+
+#define		X509_V_ERR_UNNESTED_RESOURCE			46
+
+#define		X509_V_ERR_PERMITTED_VIOLATION			47
+#define		X509_V_ERR_EXCLUDED_VIOLATION			48
+#define		X509_V_ERR_SUBTREE_MINMAX			49
+#define		X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE		51
+#define		X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX	52
+#define		X509_V_ERR_UNSUPPORTED_NAME_SYNTAX		53
+#define		X509_V_ERR_CRL_PATH_VALIDATION_ERROR		54
+
+/* Suite B mode algorithm violation */
+#define		X509_V_ERR_SUITE_B_INVALID_VERSION		56
+#define		X509_V_ERR_SUITE_B_INVALID_ALGORITHM		57
+#define		X509_V_ERR_SUITE_B_INVALID_CURVE		58
+#define		X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM	59
+#define		X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED		60
+#define		X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256	61
+
+/* Host, email and IP check errors */
+#define		X509_V_ERR_HOSTNAME_MISMATCH			62
+#define		X509_V_ERR_EMAIL_MISMATCH			63
+#define		X509_V_ERR_IP_ADDRESS_MISMATCH			64
+
+/* The application is not happy */
+#define		X509_V_ERR_APPLICATION_VERIFICATION		50
+
+/* Certificate verify flags */
+
+/* Send issuer+subject checks to verify_cb */
+#define	X509_V_FLAG_CB_ISSUER_CHECK		0x1
+/* Use check time instead of current time */
+#define	X509_V_FLAG_USE_CHECK_TIME		0x2
+/* Lookup CRLs */
+#define	X509_V_FLAG_CRL_CHECK			0x4
+/* Lookup CRLs for whole chain */
+#define	X509_V_FLAG_CRL_CHECK_ALL		0x8
+/* Ignore unhandled critical extensions */
+#define	X509_V_FLAG_IGNORE_CRITICAL		0x10
+/* Disable workarounds for broken certificates */
+#define	X509_V_FLAG_X509_STRICT			0x20
+/* Enable proxy certificate validation */
+#define	X509_V_FLAG_ALLOW_PROXY_CERTS		0x40
+/* Enable policy checking */
+#define X509_V_FLAG_POLICY_CHECK		0x80
+/* Policy variable require-explicit-policy */
+#define X509_V_FLAG_EXPLICIT_POLICY		0x100
+/* Policy variable inhibit-any-policy */
+#define	X509_V_FLAG_INHIBIT_ANY			0x200
+/* Policy variable inhibit-policy-mapping */
+#define X509_V_FLAG_INHIBIT_MAP			0x400
+/* Notify callback that policy is OK */
+#define X509_V_FLAG_NOTIFY_POLICY		0x800
+/* Extended CRL features such as indirect CRLs, alternate CRL signing keys */
+#define X509_V_FLAG_EXTENDED_CRL_SUPPORT	0x1000
+/* Delta CRL support */
+#define X509_V_FLAG_USE_DELTAS			0x2000
+/* Check selfsigned CA signature */
+#define X509_V_FLAG_CHECK_SS_SIGNATURE		0x4000
+/* Use trusted store first */
+#define X509_V_FLAG_TRUSTED_FIRST		0x8000
+/* Suite B 128 bit only mode: not normally used */
+#define X509_V_FLAG_SUITEB_128_LOS_ONLY		0x10000
+/* Suite B 192 bit only mode */
+#define X509_V_FLAG_SUITEB_192_LOS		0x20000
+/* Suite B 128 bit mode allowing 192 bit algorithms */
+#define X509_V_FLAG_SUITEB_128_LOS		0x30000
+
+/* Allow partial chains if at least one certificate is in trusted store */
+#define X509_V_FLAG_PARTIAL_CHAIN		0x80000
+
+#define X509_VP_FLAG_DEFAULT			0x1
+#define X509_VP_FLAG_OVERWRITE			0x2
+#define X509_VP_FLAG_RESET_FLAGS		0x4
+#define X509_VP_FLAG_LOCKED			0x8
+#define X509_VP_FLAG_ONCE			0x10
+
+/* Internal use: mask of policy related options */
+#define X509_V_FLAG_POLICY_MASK (X509_V_FLAG_POLICY_CHECK \
+				| X509_V_FLAG_EXPLICIT_POLICY \
+				| X509_V_FLAG_INHIBIT_ANY \
+				| X509_V_FLAG_INHIBIT_MAP)
+
+int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type,
+	     X509_NAME *name);
+X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h,int type,X509_NAME *name);
+X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, X509_OBJECT *x);
+void X509_OBJECT_up_ref_count(X509_OBJECT *a);
+void X509_OBJECT_free_contents(X509_OBJECT *a);
+X509_STORE *X509_STORE_new(void );
+void X509_STORE_free(X509_STORE *v);
+
+STACK_OF(X509)* X509_STORE_get1_certs(X509_STORE_CTX *st, X509_NAME *nm);
+STACK_OF(X509_CRL)* X509_STORE_get1_crls(X509_STORE_CTX *st, X509_NAME *nm);
+int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags);
+int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
+int X509_STORE_set_trust(X509_STORE *ctx, int trust);
+int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *pm);
+
+void X509_STORE_set_verify_cb(X509_STORE *ctx,
+				  int (*verify_cb)(int, X509_STORE_CTX *));
+
+void X509_STORE_set_lookup_crls_cb(X509_STORE *ctx,
+		STACK_OF(X509_CRL)* (*cb)(X509_STORE_CTX *ctx, X509_NAME *nm));
+
+X509_STORE_CTX *X509_STORE_CTX_new(void);
+
+int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
+
+void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
+int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
+			 X509 *x509, STACK_OF(X509) *chain);
+void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk);
+void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx);
+
+X509_STORE *X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx);
+
+X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m);
+
+X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void);
+X509_LOOKUP_METHOD *X509_LOOKUP_file(void);
+
+int X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
+int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x);
+
+int X509_STORE_get_by_subject(X509_STORE_CTX *vs,int type,X509_NAME *name,
+	X509_OBJECT *ret);
+
+int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
+	long argl, char **ret);
+
+#ifndef OPENSSL_NO_STDIO
+int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type);
+int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type);
+int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type);
+#endif
+
+
+X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method);
+void X509_LOOKUP_free(X509_LOOKUP *ctx);
+int X509_LOOKUP_init(X509_LOOKUP *ctx);
+int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, int type, X509_NAME *name,
+	X509_OBJECT *ret);
+int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, int type, X509_NAME *name,
+	ASN1_INTEGER *serial, X509_OBJECT *ret);
+int X509_LOOKUP_by_fingerprint(X509_LOOKUP *ctx, int type,
+	unsigned char *bytes, int len, X509_OBJECT *ret);
+int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, int type, char *str,
+	int len, X509_OBJECT *ret);
+int X509_LOOKUP_shutdown(X509_LOOKUP *ctx);
+
+#ifndef OPENSSL_NO_STDIO
+int	X509_STORE_load_locations (X509_STORE *ctx,
+		const char *file, const char *dir);
+int	X509_STORE_set_default_paths(X509_STORE *ctx);
+#endif
+
+int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+	CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int	X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx,int idx,void *data);
+void *	X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx,int idx);
+int	X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
+void	X509_STORE_CTX_set_error(X509_STORE_CTX *ctx,int s);
+int	X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
+X509 *	X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
+X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx);
+X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx);
+X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx);
+STACK_OF(X509) *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
+STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx);
+void	X509_STORE_CTX_set_cert(X509_STORE_CTX *c,X509 *x);
+void	X509_STORE_CTX_set_chain(X509_STORE_CTX *c,STACK_OF(X509) *sk);
+void	X509_STORE_CTX_set0_crls(X509_STORE_CTX *c,STACK_OF(X509_CRL) *sk);
+int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
+int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust);
+int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,
+				int purpose, int trust);
+void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags);
+void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags,
+								time_t t);
+void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx,
+				  int (*verify_cb)(int, X509_STORE_CTX *));
+  
+X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx);
+int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx);
+
+X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx);
+void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param);
+int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name);
+
+/* X509_VERIFY_PARAM functions */
+
+X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void);
+void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param);
+int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *to,
+						const X509_VERIFY_PARAM *from);
+int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, 
+						const X509_VERIFY_PARAM *from);
+int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name);
+int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags);
+int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param,
+							unsigned long flags);
+unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param);
+int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose);
+int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust);
+void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth);
+void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t);
+int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param,
+						ASN1_OBJECT *policy);
+int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, 
+					STACK_OF(ASN1_OBJECT) *policies);
+
+int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
+				const unsigned char *name, size_t namelen);
+int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
+				const unsigned char *email, size_t emaillen);
+int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
+					const unsigned char *ip, size_t iplen);
+int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc);
+
+int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param);
+const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param);
+
+int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param);
+int X509_VERIFY_PARAM_get_count(void);
+const X509_VERIFY_PARAM *X509_VERIFY_PARAM_get0(int id);
+const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name);
+void X509_VERIFY_PARAM_table_cleanup(void);
+
+int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
+			STACK_OF(X509) *certs,
+			STACK_OF(ASN1_OBJECT) *policy_oids,
+			unsigned int flags);
+
+void X509_policy_tree_free(X509_POLICY_TREE *tree);
+
+int X509_policy_tree_level_count(const X509_POLICY_TREE *tree);
+X509_POLICY_LEVEL *
+	X509_policy_tree_get0_level(const X509_POLICY_TREE *tree, int i);
+
+STACK_OF(X509_POLICY_NODE) *
+	X509_policy_tree_get0_policies(const X509_POLICY_TREE *tree);
+
+STACK_OF(X509_POLICY_NODE) *
+	X509_policy_tree_get0_user_policies(const X509_POLICY_TREE *tree);
+
+int X509_policy_level_node_count(X509_POLICY_LEVEL *level);
+
+X509_POLICY_NODE *X509_policy_level_get0_node(X509_POLICY_LEVEL *level, int i);
+
+const ASN1_OBJECT *X509_policy_node_get0_policy(const X509_POLICY_NODE *node);
+
+STACK_OF(POLICYQUALINFO) *
+	X509_policy_node_get0_qualifiers(const X509_POLICY_NODE *node);
+const X509_POLICY_NODE *
+	X509_policy_node_get0_parent(const X509_POLICY_NODE *node);
+
+#ifdef  __cplusplus
+}
+#endif
+#endif
+
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
new file mode 100644
index 0000000..25b0987
--- /dev/null
+++ b/crypto/x509/x509_vpm.c
@@ -0,0 +1,578 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004. */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/buf.h>
+#include <openssl/lhash.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "vpm_int.h"
+
+/* X509_VERIFY_PARAM functions */
+
+static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
+	{
+	X509_VERIFY_PARAM_ID *paramid;
+	if (!param)
+		return;
+	param->name = NULL;
+	param->purpose = 0;
+	param->trust = 0;
+	/*param->inh_flags = X509_VP_FLAG_DEFAULT;*/
+	param->inh_flags = 0;
+	param->flags = 0;
+	param->depth = -1;
+	if (param->policies)
+		{
+		sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free);
+		param->policies = NULL;
+		}
+	paramid = param->id;
+	if (paramid->host)
+		{
+		OPENSSL_free(paramid->host);
+		paramid->host = NULL;
+		paramid->hostlen = 0;
+		}
+	if (paramid->email)
+		{
+		OPENSSL_free(paramid->email);
+		paramid->email = NULL;
+		paramid->emaillen = 0;
+		}
+	if (paramid->ip)
+		{
+		OPENSSL_free(paramid->ip);
+		paramid->ip = NULL;
+		paramid->iplen = 0;
+		}
+
+	}
+
+X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void)
+	{
+	X509_VERIFY_PARAM *param;
+	X509_VERIFY_PARAM_ID *paramid;
+	param = OPENSSL_malloc(sizeof(X509_VERIFY_PARAM));
+	if (!param)
+		return NULL;
+	paramid = OPENSSL_malloc(sizeof(X509_VERIFY_PARAM));
+	if (!paramid)
+		{
+		OPENSSL_free(param);
+		return NULL;
+		}
+	memset(param, 0, sizeof(X509_VERIFY_PARAM));
+	memset(paramid, 0, sizeof(X509_VERIFY_PARAM_ID));
+	param->id = paramid;
+	x509_verify_param_zero(param);
+	return param;
+	}
+
+void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param)
+	{
+	x509_verify_param_zero(param);
+	OPENSSL_free(param->id);
+	OPENSSL_free(param);
+	}
+
+/* This function determines how parameters are "inherited" from one structure
+ * to another. There are several different ways this can happen.
+ *
+ * 1. If a child structure needs to have its values initialized from a parent
+ *    they are simply copied across. For example SSL_CTX copied to SSL.
+ * 2. If the structure should take on values only if they are currently unset.
+ *    For example the values in an SSL structure will take appropriate value
+ *    for SSL servers or clients but only if the application has not set new
+ *    ones.
+ *
+ * The "inh_flags" field determines how this function behaves. 
+ *
+ * Normally any values which are set in the default are not copied from the
+ * destination and verify flags are ORed together.
+ *
+ * If X509_VP_FLAG_DEFAULT is set then anything set in the source is copied
+ * to the destination. Effectively the values in "to" become default values
+ * which will be used only if nothing new is set in "from".
+ *
+ * If X509_VP_FLAG_OVERWRITE is set then all value are copied across whether
+ * they are set or not. Flags is still Ored though.
+ *
+ * If X509_VP_FLAG_RESET_FLAGS is set then the flags value is copied instead
+ * of ORed.
+ *
+ * If X509_VP_FLAG_LOCKED is set then no values are copied.
+ *
+ * If X509_VP_FLAG_ONCE is set then the current inh_flags setting is zeroed
+ * after the next call.
+ */
+
+/* Macro to test if a field should be copied from src to dest */
+
+#define test_x509_verify_param_copy(field, def) \
+	(to_overwrite || \
+		((src->field != def) && (to_default || (dest->field == def))))
+
+/* As above but for ID fields */
+
+#define test_x509_verify_param_copy_id(idf, def) \
+	test_x509_verify_param_copy(id->idf, def)
+
+/* Macro to test and copy a field if necessary */
+
+#define x509_verify_param_copy(field, def) \
+	if (test_x509_verify_param_copy(field, def)) \
+		dest->field = src->field
+
+
+int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
+						const X509_VERIFY_PARAM *src)
+	{
+	unsigned long inh_flags;
+	int to_default, to_overwrite;
+	X509_VERIFY_PARAM_ID *id;
+	if (!src)
+		return 1;
+	id = src->id;
+	inh_flags = dest->inh_flags | src->inh_flags;
+
+	if (inh_flags & X509_VP_FLAG_ONCE)
+		dest->inh_flags = 0;
+
+	if (inh_flags & X509_VP_FLAG_LOCKED)
+		return 1;
+
+	if (inh_flags & X509_VP_FLAG_DEFAULT)
+		to_default = 1;
+	else
+		to_default = 0;
+
+	if (inh_flags & X509_VP_FLAG_OVERWRITE)
+		to_overwrite = 1;
+	else
+		to_overwrite = 0;
+
+	x509_verify_param_copy(purpose, 0);
+	x509_verify_param_copy(trust, 0);
+	x509_verify_param_copy(depth, -1);
+
+	/* If overwrite or check time not set, copy across */
+
+	if (to_overwrite || !(dest->flags & X509_V_FLAG_USE_CHECK_TIME))
+		{
+		dest->check_time = src->check_time;
+		dest->flags &= ~X509_V_FLAG_USE_CHECK_TIME;
+		/* Don't need to copy flag: that is done below */
+		}
+
+	if (inh_flags & X509_VP_FLAG_RESET_FLAGS)
+		dest->flags = 0;
+
+	dest->flags |= src->flags;
+
+	if (test_x509_verify_param_copy(policies, NULL))
+		{
+		if (!X509_VERIFY_PARAM_set1_policies(dest, src->policies))
+			return 0;
+		}
+
+	if (test_x509_verify_param_copy_id(host, NULL))
+		{
+		if (!X509_VERIFY_PARAM_set1_host(dest, id->host, id->hostlen))
+			return 0;
+		}
+
+	if (test_x509_verify_param_copy_id(email, NULL))
+		{
+		if (!X509_VERIFY_PARAM_set1_email(dest, id->email, id->emaillen))
+			return 0;
+		}
+
+	if (test_x509_verify_param_copy_id(ip, NULL))
+		{
+		if (!X509_VERIFY_PARAM_set1_ip(dest, id->ip, id->iplen))
+			return 0;
+		}
+
+	return 1;
+	}
+
+int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to,
+						const X509_VERIFY_PARAM *from)
+	{
+	unsigned long save_flags = to->inh_flags;
+	int ret;
+	to->inh_flags |= X509_VP_FLAG_DEFAULT;
+	ret = X509_VERIFY_PARAM_inherit(to, from);
+	to->inh_flags = save_flags;
+	return ret;
+	}
+
+static int int_x509_param_set1(unsigned char **pdest, size_t *pdestlen,
+				const unsigned char *src, size_t srclen)
+	{
+	void *tmp;
+	if (src)
+		{
+		if (srclen == 0)
+			{
+			tmp = BUF_strdup((char *)src);
+			srclen = strlen((char *)src);
+			}
+		else
+			tmp = BUF_memdup(src, srclen);
+		if (!tmp)
+			return 0;
+		}
+	else
+		{
+		tmp = NULL;
+		srclen = 0;
+		}
+	if (*pdest)
+		OPENSSL_free(*pdest);
+	*pdest = tmp;
+	if (pdestlen)
+		*pdestlen = srclen;
+	return 1;
+	}
+
+int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name)
+	{
+	if (param->name)
+		OPENSSL_free(param->name);
+	param->name = BUF_strdup(name);
+	if (param->name)
+		return 1;
+	return 0;
+	}
+
+int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags)
+	{
+	param->flags |= flags;
+	if (flags & X509_V_FLAG_POLICY_MASK)
+		param->flags |= X509_V_FLAG_POLICY_CHECK;
+	return 1;
+	}
+
+int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, unsigned long flags)
+	{
+	param->flags &= ~flags;
+	return 1;
+	}
+
+unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param)
+	{
+	return param->flags;
+	}
+
+int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose)
+	{
+	return X509_PURPOSE_set(&param->purpose, purpose);
+	}
+
+int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust)
+	{
+	return X509_TRUST_set(&param->trust, trust);
+	}
+
+void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth)
+	{
+	param->depth = depth;
+	}
+
+void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t)
+	{
+	param->check_time = t;
+	param->flags |= X509_V_FLAG_USE_CHECK_TIME;
+	}
+
+int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, ASN1_OBJECT *policy)
+	{
+	if (!param->policies)
+		{
+		param->policies = sk_ASN1_OBJECT_new_null();
+		if (!param->policies)
+			return 0;
+		}
+	if (!sk_ASN1_OBJECT_push(param->policies, policy))
+		return 0;
+	return 1;
+	}
+
+int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, 
+					STACK_OF(ASN1_OBJECT) *policies)
+	{
+	size_t i;
+	ASN1_OBJECT *oid, *doid;
+	if (!param)
+		return 0;
+	if (param->policies)
+		sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free);
+
+	if (!policies)
+		{
+		param->policies = NULL;
+		return 1;
+		}
+
+	param->policies = sk_ASN1_OBJECT_new_null();
+	if (!param->policies)
+		return 0;
+
+	for (i = 0; i < sk_ASN1_OBJECT_num(policies); i++)
+		{
+		oid = sk_ASN1_OBJECT_value(policies, i);
+		doid = OBJ_dup(oid);
+		if (!doid)
+			return 0;
+		if (!sk_ASN1_OBJECT_push(param->policies, doid))
+			{
+			ASN1_OBJECT_free(doid);
+			return 0;
+			}
+		}
+	param->flags |= X509_V_FLAG_POLICY_CHECK;
+	return 1;
+	}
+
+int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
+				const unsigned char *name, size_t namelen)
+	{
+	return int_x509_param_set1(&param->id->host, &param->id->hostlen,
+					name, namelen);
+	}
+
+int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
+				const unsigned char *email, size_t emaillen)
+	{
+	return int_x509_param_set1(&param->id->email, &param->id->emaillen,
+					email, emaillen);
+	}
+
+int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
+					const unsigned char *ip, size_t iplen)
+	{
+	if (iplen != 0 && iplen != 4 && iplen != 16)
+		return 0;
+	return int_x509_param_set1(&param->id->ip, &param->id->iplen, ip, iplen);
+	}
+
+int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc)
+	{
+	unsigned char ipout[16];
+	int iplen;
+	iplen = a2i_ipadd(ipout, ipasc);
+	if (iplen == 0)
+		return 0;
+	return X509_VERIFY_PARAM_set1_ip(param, ipout, (size_t)iplen);
+	}
+
+int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param)
+	{
+	return param->depth;
+	}
+
+const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param)
+	{
+	return param->name;
+	}
+
+static X509_VERIFY_PARAM_ID _empty_id = {NULL, 0, NULL, 0, NULL, 0};
+
+#define vpm_empty_id (X509_VERIFY_PARAM_ID *)&_empty_id
+
+/* Default verify parameters: these are used for various
+ * applications and can be overridden by the user specified table.
+ * NB: the 'name' field *must* be in alphabetical order because it
+ * will be searched using OBJ_search.
+ */
+
+static const X509_VERIFY_PARAM default_table[] = {
+	{
+	"default",	/* X509 default parameters */
+	0,		/* Check time */
+	0,		/* internal flags */
+	0,		/* flags */
+	0,		/* purpose */
+	0,		/* trust */
+	100,		/* depth */
+	NULL,		/* policies */
+	vpm_empty_id
+	},
+	{
+	"pkcs7",			/* S/MIME sign parameters */
+	0,				/* Check time */
+	0,				/* internal flags */
+	0,				/* flags */
+	X509_PURPOSE_SMIME_SIGN,	/* purpose */
+	X509_TRUST_EMAIL,		/* trust */
+	-1,				/* depth */
+	NULL,				/* policies */
+	vpm_empty_id
+	},
+	{
+	"smime_sign",			/* S/MIME sign parameters */
+	0,				/* Check time */
+	0,				/* internal flags */
+	0,				/* flags */
+	X509_PURPOSE_SMIME_SIGN,	/* purpose */
+	X509_TRUST_EMAIL,		/* trust */
+	-1,				/* depth */
+	NULL,				/* policies */
+	vpm_empty_id
+	},
+	{
+	"ssl_client",			/* SSL/TLS client parameters */
+	0,				/* Check time */
+	0,				/* internal flags */
+	0,				/* flags */
+	X509_PURPOSE_SSL_CLIENT,	/* purpose */
+	X509_TRUST_SSL_CLIENT,		/* trust */
+	-1,				/* depth */
+	NULL,				/* policies */
+	vpm_empty_id
+	},
+	{
+	"ssl_server",			/* SSL/TLS server parameters */
+	0,				/* Check time */
+	0,				/* internal flags */
+	0,				/* flags */
+	X509_PURPOSE_SSL_SERVER,	/* purpose */
+	X509_TRUST_SSL_SERVER,		/* trust */
+	-1,				/* depth */
+	NULL,				/* policies */
+	vpm_empty_id
+	}};
+
+static STACK_OF(X509_VERIFY_PARAM) *param_table = NULL;
+
+static int param_cmp(const X509_VERIFY_PARAM **a,
+			const X509_VERIFY_PARAM **b)
+	{
+	return strcmp((*a)->name, (*b)->name);
+	}
+
+int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param)
+	{
+	X509_VERIFY_PARAM *ptmp;
+	if (!param_table)
+		{
+		param_table = sk_X509_VERIFY_PARAM_new(param_cmp);
+		if (!param_table)
+			return 0;
+		}
+	else
+		{
+		size_t idx;
+
+		if (sk_X509_VERIFY_PARAM_find(param_table, &idx, param))
+			{
+			ptmp = sk_X509_VERIFY_PARAM_value(param_table, idx);
+			X509_VERIFY_PARAM_free(ptmp);
+			(void)sk_X509_VERIFY_PARAM_delete(param_table, idx);
+			}
+		}
+	if (!sk_X509_VERIFY_PARAM_push(param_table, param))
+		return 0;
+	return 1;
+	}
+
+int X509_VERIFY_PARAM_get_count(void)
+	{
+	int num = sizeof(default_table)/sizeof(X509_VERIFY_PARAM);
+	if (param_table)
+		num += sk_X509_VERIFY_PARAM_num(param_table);
+	return num;
+	}
+
+const X509_VERIFY_PARAM *X509_VERIFY_PARAM_get0(int id)
+	{
+	int num = sizeof(default_table)/sizeof(X509_VERIFY_PARAM);
+	if (id < num)
+		return default_table + id;
+	return sk_X509_VERIFY_PARAM_value(param_table, id - num);
+	}
+
+const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name)
+	{
+	X509_VERIFY_PARAM pm;
+	unsigned i, limit;
+
+	pm.name = (char *)name;
+	if (param_table)
+		{
+		size_t idx;
+		if (sk_X509_VERIFY_PARAM_find(param_table, &idx, &pm))
+			return sk_X509_VERIFY_PARAM_value(param_table, idx);
+		}
+
+	limit = sizeof(default_table)/sizeof(X509_VERIFY_PARAM);
+	for (i = 0; i < limit; i++) {
+		if (strcmp(default_table[i].name, name) == 0) {
+			return &default_table[i];
+		}
+	}
+	return NULL;
+	}
+
+void X509_VERIFY_PARAM_table_cleanup(void)
+	{
+	if (param_table)
+		sk_X509_VERIFY_PARAM_pop_free(param_table,
+						X509_VERIFY_PARAM_free);
+	param_table = NULL;
+	}
diff --git a/crypto/x509/x509cset.c b/crypto/x509/x509cset.c
new file mode 100644
index 0000000..b526c69
--- /dev/null
+++ b/crypto/x509/x509cset.c
@@ -0,0 +1,165 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2001. */
+/* ====================================================================
+ * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/asn1.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+int X509_CRL_set_version(X509_CRL *x, long version)
+	{
+	if (x == NULL) return(0);
+	if (x->crl->version == NULL)
+		{
+		if ((x->crl->version=M_ASN1_INTEGER_new()) == NULL)
+			return(0);
+		}
+	return(ASN1_INTEGER_set(x->crl->version,version));
+	}
+
+int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name)
+	{
+	if ((x == NULL) || (x->crl == NULL)) return(0);
+	return(X509_NAME_set(&x->crl->issuer,name));
+	}
+
+
+int X509_CRL_set_lastUpdate(X509_CRL *x, const ASN1_TIME *tm)
+	{
+	ASN1_TIME *in;
+
+	if (x == NULL) return(0);
+	in=x->crl->lastUpdate;
+	if (in != tm)
+		{
+		in=M_ASN1_TIME_dup(tm);
+		if (in != NULL)
+			{
+			M_ASN1_TIME_free(x->crl->lastUpdate);
+			x->crl->lastUpdate=in;
+			}
+		}
+	return(in != NULL);
+	}
+
+int X509_CRL_set_nextUpdate(X509_CRL *x, const ASN1_TIME *tm)
+	{
+	ASN1_TIME *in;
+
+	if (x == NULL) return(0);
+	in=x->crl->nextUpdate;
+	if (in != tm)
+		{
+		in=M_ASN1_TIME_dup(tm);
+		if (in != NULL)
+			{
+			M_ASN1_TIME_free(x->crl->nextUpdate);
+			x->crl->nextUpdate=in;
+			}
+		}
+	return(in != NULL);
+	}
+
+int X509_CRL_sort(X509_CRL *c)
+	{
+	size_t i;
+	X509_REVOKED *r;
+	/* sort the data so it will be written in serial
+	 * number order */
+	sk_X509_REVOKED_sort(c->crl->revoked);
+	for (i=0; i<sk_X509_REVOKED_num(c->crl->revoked); i++)
+		{
+		r=sk_X509_REVOKED_value(c->crl->revoked,i);
+		r->sequence=i;
+		}
+	c->crl->enc.modified = 1;
+	return 1;
+	}
+
+int X509_REVOKED_set_revocationDate(X509_REVOKED *x, ASN1_TIME *tm)
+	{
+	ASN1_TIME *in;
+
+	if (x == NULL) return(0);
+	in=x->revocationDate;
+	if (in != tm)
+		{
+		in=M_ASN1_TIME_dup(tm);
+		if (in != NULL)
+			{
+			M_ASN1_TIME_free(x->revocationDate);
+			x->revocationDate=in;
+			}
+		}
+	return(in != NULL);
+	}
+
+int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial)
+	{
+	ASN1_INTEGER *in;
+
+	if (x == NULL) return(0);
+	in=x->serialNumber;
+	if (in != serial)
+		{
+		in=M_ASN1_INTEGER_dup(serial);
+		if (in != NULL)
+			{
+			M_ASN1_INTEGER_free(x->serialNumber);
+			x->serialNumber=in;
+			}
+		}
+	return(in != NULL);
+	}
diff --git a/crypto/x509/x509name.c b/crypto/x509/x509name.c
new file mode 100644
index 0000000..4a5ea17
--- /dev/null
+++ b/crypto/x509/x509name.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+
+
+int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len)
+	{
+	const ASN1_OBJECT *obj;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL) return(-1);
+	return(X509_NAME_get_text_by_OBJ(name,obj,buf,len));
+	}
+
+int X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, char *buf,
+	     int len)
+	{
+	int i;
+	ASN1_STRING *data;
+
+	i=X509_NAME_get_index_by_OBJ(name,obj,-1);
+	if (i < 0) return(-1);
+	data=X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
+	i=(data->length > (len-1))?(len-1):data->length;
+	if (buf == NULL) return(data->length);
+	memcpy(buf,data->data,i);
+	buf[i]='\0';
+	return(i);
+	}
+
+int X509_NAME_entry_count(X509_NAME *name)
+	{
+	if (name == NULL) return(0);
+	return(sk_X509_NAME_ENTRY_num(name->entries));
+	}
+
+int X509_NAME_get_index_by_NID(X509_NAME *name, int nid, int lastpos)
+	{
+	const ASN1_OBJECT *obj;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL) return(-2);
+	return(X509_NAME_get_index_by_OBJ(name,obj,lastpos));
+	}
+
+/* NOTE: you should be passsing -1, not 0 as lastpos */
+int X509_NAME_get_index_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
+	     int lastpos)
+	{
+	int n;
+	X509_NAME_ENTRY *ne;
+	STACK_OF(X509_NAME_ENTRY) *sk;
+
+	if (name == NULL) return(-1);
+	if (lastpos < 0)
+		lastpos= -1;
+	sk=name->entries;
+	n=sk_X509_NAME_ENTRY_num(sk);
+	for (lastpos++; lastpos < n; lastpos++)
+		{
+		ne=sk_X509_NAME_ENTRY_value(sk,lastpos);
+		if (OBJ_cmp(ne->object,obj) == 0)
+			return(lastpos);
+		}
+	return(-1);
+	}
+
+X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *name, int loc)
+	{
+	if(name == NULL || loc < 0 || sk_X509_NAME_ENTRY_num(name->entries) <= (size_t) loc)
+		return(NULL);
+	else
+		return(sk_X509_NAME_ENTRY_value(name->entries,loc));
+	}
+
+X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name, int loc)
+	{
+	X509_NAME_ENTRY *ret;
+	int i,n,set_prev,set_next;
+	STACK_OF(X509_NAME_ENTRY) *sk;
+
+	if (name == NULL || loc < 0 || sk_X509_NAME_ENTRY_num(name->entries) <= (size_t) loc)
+		return(NULL);
+	sk=name->entries;
+	ret=sk_X509_NAME_ENTRY_delete(sk,loc);
+	n=sk_X509_NAME_ENTRY_num(sk);
+	name->modified=1;
+	if (loc == n) return(ret);
+
+	/* else we need to fixup the set field */
+	if (loc != 0)
+		set_prev=(sk_X509_NAME_ENTRY_value(sk,loc-1))->set;
+	else
+		set_prev=ret->set-1;
+	set_next=sk_X509_NAME_ENTRY_value(sk,loc)->set;
+
+	/* set_prev is the previous set
+	 * set is the current set
+	 * set_next is the following
+	 * prev  1 1	1 1	1 1	1 1
+	 * set   1	1	2	2
+	 * next  1 1	2 2	2 2	3 2
+	 * so basically only if prev and next differ by 2, then
+	 * re-number down by 1 */
+	if (set_prev+1 < set_next)
+		for (i=loc; i<n; i++)
+			sk_X509_NAME_ENTRY_value(sk,i)->set--;
+	return(ret);
+	}
+
+int X509_NAME_add_entry_by_OBJ(X509_NAME *name, ASN1_OBJECT *obj, int type,
+			unsigned char *bytes, int len, int loc, int set)
+{
+	X509_NAME_ENTRY *ne;
+	int ret;
+	ne = X509_NAME_ENTRY_create_by_OBJ(NULL, obj, type, bytes, len);
+	if(!ne) return 0;
+	ret = X509_NAME_add_entry(name, ne, loc, set);
+	X509_NAME_ENTRY_free(ne);
+	return ret;
+}
+
+int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid, int type,
+			unsigned char *bytes, int len, int loc, int set)
+{
+	X509_NAME_ENTRY *ne;
+	int ret;
+	ne = X509_NAME_ENTRY_create_by_NID(NULL, nid, type, bytes, len);
+	if(!ne) return 0;
+	ret = X509_NAME_add_entry(name, ne, loc, set);
+	X509_NAME_ENTRY_free(ne);
+	return ret;
+}
+
+int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type,
+			const unsigned char *bytes, int len, int loc, int set)
+{
+	X509_NAME_ENTRY *ne;
+	int ret;
+	ne = X509_NAME_ENTRY_create_by_txt(NULL, field, type, bytes, len);
+	if(!ne) return 0;
+	ret = X509_NAME_add_entry(name, ne, loc, set);
+	X509_NAME_ENTRY_free(ne);
+	return ret;
+}
+
+/* if set is -1, append to previous set, 0 'a new one', and 1,
+ * prepend to the guy we are about to stomp on. */
+int X509_NAME_add_entry(X509_NAME *name, X509_NAME_ENTRY *ne, int loc,
+	     int set)
+	{
+	X509_NAME_ENTRY *new_name=NULL;
+	int n,i,inc;
+	STACK_OF(X509_NAME_ENTRY) *sk;
+
+	if (name == NULL) return(0);
+	sk=name->entries;
+	n=sk_X509_NAME_ENTRY_num(sk);
+	if (loc > n) loc=n;
+	else if (loc < 0) loc=n;
+
+	name->modified=1;
+
+	if (set == -1)
+		{
+		if (loc == 0)
+			{
+			set=0;
+			inc=1;
+			}
+		else
+			{
+			set=sk_X509_NAME_ENTRY_value(sk,loc-1)->set;
+			inc=0;
+			}
+		}
+	else /* if (set >= 0) */
+		{
+		if (loc >= n)
+			{
+			if (loc != 0)
+				set=sk_X509_NAME_ENTRY_value(sk,loc-1)->set+1;
+			else
+				set=0;
+			}
+		else
+			set=sk_X509_NAME_ENTRY_value(sk,loc)->set;
+		inc=(set == 0)?1:0;
+		}
+
+	if ((new_name=X509_NAME_ENTRY_dup(ne)) == NULL)
+		goto err;
+	new_name->set=set;
+	if (!sk_X509_NAME_ENTRY_insert(sk,new_name,loc))
+		{
+		OPENSSL_PUT_ERROR(X509, X509_NAME_add_entry, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+	if (inc)
+		{
+		n=sk_X509_NAME_ENTRY_num(sk);
+		for (i=loc+1; i<n; i++)
+			sk_X509_NAME_ENTRY_value(sk,i-1)->set+=1;
+		}	
+	return(1);
+err:
+	if (new_name != NULL)
+		X509_NAME_ENTRY_free(new_name);
+	return(0);
+	}
+
+X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(X509_NAME_ENTRY **ne,
+		const char *field, int type, const unsigned char *bytes, int len)
+	{
+	ASN1_OBJECT *obj;
+	X509_NAME_ENTRY *nentry;
+
+	obj=OBJ_txt2obj(field, 0);
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_NAME_ENTRY_create_by_txt, X509_R_INVALID_FIELD_NAME);
+		ERR_add_error_data(2, "name=", field);
+		return(NULL);
+		}
+	nentry = X509_NAME_ENTRY_create_by_OBJ(ne,obj,type,bytes,len);
+	ASN1_OBJECT_free(obj);
+	return nentry;
+	}
+
+X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(X509_NAME_ENTRY **ne, int nid,
+	     int type, unsigned char *bytes, int len)
+	{
+	const ASN1_OBJECT *obj;
+	X509_NAME_ENTRY *nentry;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_NAME_ENTRY_create_by_NID, X509_R_UNKNOWN_NID);
+		return(NULL);
+		}
+	nentry = X509_NAME_ENTRY_create_by_OBJ(ne,obj,type,bytes,len);
+	/* TODO(fork): remove this? */
+	/* ASN1_OBJECT_free(obj); */
+	return nentry;
+	}
+
+X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(X509_NAME_ENTRY **ne,
+	     const ASN1_OBJECT *obj, int type, const unsigned char *bytes, int len)
+	{
+	X509_NAME_ENTRY *ret;
+
+	if ((ne == NULL) || (*ne == NULL))
+		{
+		if ((ret=X509_NAME_ENTRY_new()) == NULL)
+			return(NULL);
+		}
+	else
+		ret= *ne;
+
+	if (!X509_NAME_ENTRY_set_object(ret,obj))
+		goto err;
+	if (!X509_NAME_ENTRY_set_data(ret,type,bytes,len))
+		goto err;
+
+	if ((ne != NULL) && (*ne == NULL)) *ne=ret;
+	return(ret);
+err:
+	if ((ne == NULL) || (ret != *ne))
+		X509_NAME_ENTRY_free(ret);
+	return(NULL);
+	}
+
+int X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *ne, const ASN1_OBJECT *obj)
+	{
+	if ((ne == NULL) || (obj == NULL))
+		{
+		OPENSSL_PUT_ERROR(X509, X509_NAME_ENTRY_set_object, ERR_R_PASSED_NULL_PARAMETER);
+		return(0);
+		}
+	ASN1_OBJECT_free(ne->object);
+	ne->object=OBJ_dup(obj);
+	return((ne->object == NULL)?0:1);
+	}
+
+int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type,
+	     const unsigned char *bytes, int len)
+	{
+	int i;
+
+	if ((ne == NULL) || ((bytes == NULL) && (len != 0))) return(0);
+	if((type > 0) && (type & MBSTRING_FLAG)) 
+		return ASN1_STRING_set_by_NID(&ne->value, bytes,
+						len, type,
+					OBJ_obj2nid(ne->object)) ? 1 : 0;
+	if (len < 0) len=strlen((const char *)bytes);
+	i=ASN1_STRING_set(ne->value,bytes,len);
+	if (!i) return(0);
+	if (type != V_ASN1_UNDEF)
+		{
+		if (type == V_ASN1_APP_CHOOSE)
+			ne->value->type=ASN1_PRINTABLE_type(bytes,len);
+		else
+			ne->value->type=type;
+		}
+	return(1);
+	}
+
+ASN1_OBJECT *X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *ne)
+	{
+	if (ne == NULL) return(NULL);
+	return(ne->object);
+	}
+
+ASN1_STRING *X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *ne)
+	{
+	if (ne == NULL) return(NULL);
+	return(ne->value);
+	}
+
diff --git a/crypto/x509/x509rset.c b/crypto/x509/x509rset.c
new file mode 100644
index 0000000..dbab555
--- /dev/null
+++ b/crypto/x509/x509rset.c
@@ -0,0 +1,80 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+int X509_REQ_set_version(X509_REQ *x, long version)
+	{
+	if (x == NULL) return(0);
+	return(ASN1_INTEGER_set(x->req_info->version,version));
+	}
+
+int X509_REQ_set_subject_name(X509_REQ *x, X509_NAME *name)
+	{
+	if ((x == NULL) || (x->req_info == NULL)) return(0);
+	return(X509_NAME_set(&x->req_info->subject,name));
+	}
+
+int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey)
+	{
+	if ((x == NULL) || (x->req_info == NULL)) return(0);
+	return(X509_PUBKEY_set(&x->req_info->pubkey,pkey));
+	}
+
diff --git a/crypto/x509/x509spki.c b/crypto/x509/x509spki.c
new file mode 100644
index 0000000..ffadcb4
--- /dev/null
+++ b/crypto/x509/x509spki.c
@@ -0,0 +1,118 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999. */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/base64.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
+
+
+int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *x, EVP_PKEY *pkey)
+{
+	if ((x == NULL) || (x->spkac == NULL)) return(0);
+	return(X509_PUBKEY_set(&(x->spkac->pubkey),pkey));
+}
+
+EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *x)
+{
+	if ((x == NULL) || (x->spkac == NULL))
+		return(NULL);
+	return(X509_PUBKEY_get(x->spkac->pubkey));
+}
+
+/* Load a Netscape SPKI from a base64 encoded string */
+
+NETSCAPE_SPKI * NETSCAPE_SPKI_b64_decode(const char *str, int len)
+{
+	unsigned char *spki_der;
+	const unsigned char *p;
+	int spki_len;
+	NETSCAPE_SPKI *spki;
+	if(len <= 0) len = strlen(str);
+	if (!(spki_der = OPENSSL_malloc(len + 1))) {
+		OPENSSL_PUT_ERROR(X509, NETSCAPE_SPKI_b64_decode, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	spki_len = EVP_DecodeBlock(spki_der, (const unsigned char *)str, len);
+	if(spki_len < 0) {
+		OPENSSL_PUT_ERROR(X509, NETSCAPE_SPKI_b64_decode, X509_R_BASE64_DECODE_ERROR);
+		OPENSSL_free(spki_der);
+		return NULL;
+	}
+	p = spki_der;
+	spki = d2i_NETSCAPE_SPKI(NULL, &p, spki_len);
+	OPENSSL_free(spki_der);
+	return spki;
+}
+
+/* Generate a base64 encoded string from an SPKI */
+
+char * NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *spki)
+{
+	unsigned char *der_spki, *p;
+	char *b64_str;
+	int der_len;
+	der_len = i2d_NETSCAPE_SPKI(spki, NULL);
+	der_spki = OPENSSL_malloc(der_len);
+	b64_str = OPENSSL_malloc(der_len * 2);
+	if(!der_spki || !b64_str) {
+		OPENSSL_PUT_ERROR(X509, NETSCAPE_SPKI_b64_encode, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	p = der_spki;
+	i2d_NETSCAPE_SPKI(spki, &p);
+	EVP_EncodeBlock((unsigned char *)b64_str, der_spki, der_len);
+	OPENSSL_free(der_spki);
+	return b64_str;
+}
diff --git a/crypto/x509/x509type.c b/crypto/x509/x509type.c
new file mode 100644
index 0000000..e7c7935
--- /dev/null
+++ b/crypto/x509/x509type.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+int X509_certificate_type(X509 *x, EVP_PKEY *pkey)
+	{
+	EVP_PKEY *pk;
+	int ret=0,i;
+
+	if (x == NULL) return(0);
+
+	if (pkey == NULL)
+		pk=X509_get_pubkey(x);
+	else
+		pk=pkey;
+
+	if (pk == NULL) return(0);
+
+	switch (pk->type)
+		{
+	case EVP_PKEY_RSA:
+		ret=EVP_PK_RSA|EVP_PKT_SIGN;
+/*		if (!sign only extension) */
+			ret|=EVP_PKT_ENC;
+	break;
+	case EVP_PKEY_DSA:
+		ret=EVP_PK_DSA|EVP_PKT_SIGN;
+		break;
+	case EVP_PKEY_EC:
+		ret=EVP_PK_EC|EVP_PKT_SIGN|EVP_PKT_EXCH;
+		break;
+	case EVP_PKEY_DH:
+		ret=EVP_PK_DH|EVP_PKT_EXCH;
+		break;	
+	case NID_id_GostR3410_94:
+	case NID_id_GostR3410_2001:
+		ret=EVP_PKT_EXCH|EVP_PKT_SIGN;
+		break;
+	default:
+		break;
+		}
+
+	i=OBJ_obj2nid(x->sig_alg->algorithm);
+	if (i && OBJ_find_sigid_algs(i, NULL, &i))
+		{
+
+		switch (i)
+			{
+		case NID_rsaEncryption:
+		case NID_rsa:
+			ret|=EVP_PKS_RSA;
+			break;
+		case NID_dsa:
+		case NID_dsa_2:
+			ret|=EVP_PKS_DSA;
+			break;
+		case NID_X9_62_id_ecPublicKey:
+			ret|=EVP_PKS_EC;
+			break;
+		default:
+			break;
+			}
+		}
+
+	if (EVP_PKEY_size(pk) <= 1024/8)/* /8 because it's 1024 bits we look
+					   for, not bytes */
+		ret|=EVP_PKT_EXP;
+	if(pkey==NULL) EVP_PKEY_free(pk);
+	return(ret);
+	}
+
diff --git a/crypto/x509/x_algor.c b/crypto/x509/x_algor.c
new file mode 100644
index 0000000..238b0f3
--- /dev/null
+++ b/crypto/x509/x_algor.c
@@ -0,0 +1,142 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2000. */
+/* ====================================================================
+ * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/x509.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/digest.h>
+#include <openssl/obj.h>
+
+
+ASN1_SEQUENCE(X509_ALGOR) = {
+	ASN1_SIMPLE(X509_ALGOR, algorithm, ASN1_OBJECT),
+	ASN1_OPT(X509_ALGOR, parameter, ASN1_ANY)
+} ASN1_SEQUENCE_END(X509_ALGOR)
+
+ASN1_ITEM_TEMPLATE(X509_ALGORS) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, algorithms, X509_ALGOR)
+ASN1_ITEM_TEMPLATE_END(X509_ALGORS)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_ALGOR)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(X509_ALGORS, X509_ALGORS, X509_ALGORS)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_ALGOR)
+
+IMPLEMENT_ASN1_SET_OF(X509_ALGOR)
+
+int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype, void *pval)
+	{
+	if (!alg)
+		return 0;
+	if (ptype != V_ASN1_UNDEF)
+		{
+		if (alg->parameter == NULL)
+			alg->parameter = ASN1_TYPE_new();
+		if (alg->parameter == NULL)
+			return 0;
+		}
+	if (alg)
+		{
+		if (alg->algorithm)
+			ASN1_OBJECT_free(alg->algorithm);
+		alg->algorithm = (ASN1_OBJECT*) aobj;
+		}
+	if (ptype == 0)
+		return 1;	
+	if (ptype == V_ASN1_UNDEF)
+		{
+		if (alg->parameter)
+			{
+			ASN1_TYPE_free(alg->parameter);
+			alg->parameter = NULL;
+			}
+		}
+	else
+		ASN1_TYPE_set(alg->parameter, ptype, pval);
+	return 1;
+	}
+
+void X509_ALGOR_get0(ASN1_OBJECT **paobj, int *pptype, void **ppval,
+						X509_ALGOR *algor)
+	{
+	if (paobj)
+		*paobj = algor->algorithm;
+	if (pptype)
+		{
+		if (algor->parameter == NULL)
+			{
+			*pptype = V_ASN1_UNDEF;
+			return;
+			}
+		else
+			*pptype = algor->parameter->type;
+		if (ppval)
+			*ppval = algor->parameter->value.ptr;
+		}
+	}
+
+/* Set up an X509_ALGOR DigestAlgorithmIdentifier from an EVP_MD */
+
+void X509_ALGOR_set_md(X509_ALGOR *alg, const EVP_MD *md)
+	{
+	int param_type;
+
+	if (EVP_MD_flags(md) & EVP_MD_FLAG_DIGALGID_ABSENT)
+		param_type = V_ASN1_UNDEF;
+	else
+		param_type = V_ASN1_NULL;
+
+	X509_ALGOR_set0(alg, OBJ_nid2obj(EVP_MD_type(md)), param_type, NULL);
+
+	}
diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c
new file mode 100644
index 0000000..49e35e5
--- /dev/null
+++ b/crypto/x509/x_all.c
@@ -0,0 +1,547 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/dsa.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+
+
+extern const ASN1_ITEM RSAPrivateKey_it;
+extern const ASN1_ITEM RSAPublicKey_it;
+
+int X509_verify(X509 *a, EVP_PKEY *r)
+	{
+	return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF),a->sig_alg,
+		a->signature,a->cert_info,r));
+	}
+
+int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r)
+	{
+	return( ASN1_item_verify(ASN1_ITEM_rptr(X509_REQ_INFO),
+		a->sig_alg,a->signature,a->req_info,r));
+	}
+
+int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
+	{
+	x->cert_info->enc.modified = 1;
+	return(ASN1_item_sign(ASN1_ITEM_rptr(X509_CINF), x->cert_info->signature,
+		x->sig_alg, x->signature, x->cert_info,pkey,md));
+	}
+
+int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx)
+	{
+	x->cert_info->enc.modified = 1;
+	return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CINF),
+		x->cert_info->signature,
+		x->sig_alg, x->signature, x->cert_info, ctx);
+	}
+
+/* TODO(fork)
+int X509_http_nbio(OCSP_REQ_CTX *rctx, X509 **pcert)
+	{
+	return OCSP_REQ_CTX_nbio_d2i(rctx,
+				(ASN1_VALUE **)pcert, ASN1_ITEM_rptr(X509));
+	}
+*/
+
+int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md)
+	{
+	return(ASN1_item_sign(ASN1_ITEM_rptr(X509_REQ_INFO),x->sig_alg, NULL,
+		x->signature, x->req_info,pkey,md));
+	}
+
+int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx)
+	{
+	return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_REQ_INFO),
+		x->sig_alg, NULL, x->signature, x->req_info, ctx);
+	}
+
+int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md)
+	{
+	x->crl->enc.modified = 1;
+	return(ASN1_item_sign(ASN1_ITEM_rptr(X509_CRL_INFO),x->crl->sig_alg,
+		x->sig_alg, x->signature, x->crl,pkey,md));
+	}
+
+int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx)
+	{
+	x->crl->enc.modified = 1;
+	return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CRL_INFO),
+		x->crl->sig_alg, x->sig_alg, x->signature, x->crl, ctx);
+	}
+
+/* TODO(fork)
+int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl)
+	{
+	return OCSP_REQ_CTX_nbio_d2i(rctx,
+				(ASN1_VALUE **)pcrl, ASN1_ITEM_rptr(X509_CRL));
+	}
+*/
+
+int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md)
+	{
+	return(ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), x->sig_algor,NULL,
+		x->signature, x->spkac,pkey,md));
+	}
+
+#ifndef OPENSSL_NO_FP_API
+X509 *d2i_X509_fp(FILE *fp, X509 **x509)
+	{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509), fp, x509);
+	}
+
+int i2d_X509_fp(FILE *fp, X509 *x509)
+	{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509), fp, x509);
+	}
+#endif
+
+X509 *d2i_X509_bio(BIO *bp, X509 **x509)
+	{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509), bp, x509);
+	}
+
+int i2d_X509_bio(BIO *bp, X509 *x509)
+	{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509), bp, x509);
+	}
+
+#ifndef OPENSSL_NO_FP_API
+X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl)
+	{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl);
+	}
+
+int i2d_X509_CRL_fp(FILE *fp, X509_CRL *crl)
+	{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl);
+	}
+#endif
+
+X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl)
+	{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl);
+	}
+
+int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl)
+	{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl);
+	}
+
+/* TODO(fork) */
+#if 0
+#ifndef OPENSSL_NO_FP_API
+PKCS7 *d2i_PKCS7_fp(FILE *fp, PKCS7 **p7)
+	{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(PKCS7), fp, p7);
+	}
+
+int i2d_PKCS7_fp(FILE *fp, PKCS7 *p7)
+	{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(PKCS7), fp, p7);
+	}
+#endif
+
+PKCS7 *d2i_PKCS7_bio(BIO *bp, PKCS7 **p7)
+	{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKCS7), bp, p7);
+	}
+
+int i2d_PKCS7_bio(BIO *bp, PKCS7 *p7)
+	{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(PKCS7), bp, p7);
+	}
+#endif
+
+#ifndef OPENSSL_NO_FP_API
+X509_REQ *d2i_X509_REQ_fp(FILE *fp, X509_REQ **req)
+	{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_REQ), fp, req);
+	}
+
+int i2d_X509_REQ_fp(FILE *fp, X509_REQ *req)
+	{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_REQ), fp, req);
+	}
+#endif
+
+X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req)
+	{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_REQ), bp, req);
+	}
+
+int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req)
+	{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_REQ), bp, req);
+	}
+
+#ifndef OPENSSL_NO_RSA
+
+#ifndef OPENSSL_NO_FP_API
+RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa)
+	{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(RSAPrivateKey), fp, rsa);
+	}
+
+int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa)
+	{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(RSAPrivateKey), fp, rsa);
+	}
+
+RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa)
+	{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(RSAPublicKey), fp, rsa);
+	}
+
+
+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);
+	}
+
+int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa)
+	{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(RSAPublicKey), fp, rsa);
+	}
+
+int i2d_RSA_PUBKEY_fp(FILE *fp, RSA *rsa)
+	{
+	return ASN1_i2d_fp((I2D_OF_const(void))i2d_RSA_PUBKEY,fp,rsa);
+	}
+#endif
+
+RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa)
+	{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa);
+	}
+
+int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa)
+	{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa);
+	}
+
+RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa)
+	{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa);
+	}
+
+
+RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa)
+	{
+	return ASN1_d2i_bio_of(RSA,RSA_new,d2i_RSA_PUBKEY,bp,rsa);
+	}
+
+int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa)
+	{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa);
+	}
+
+int i2d_RSA_PUBKEY_bio(BIO *bp, RSA *rsa)
+	{
+	return ASN1_i2d_bio_of_const(RSA,i2d_RSA_PUBKEY,bp,rsa);
+	}
+#endif
+
+#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);
+	}
+
+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);
+	}
+#endif
+
+DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa)
+	{
+	return ASN1_d2i_bio_of(DSA,DSA_new,d2i_DSAPrivateKey,bp,dsa
+);
+	}
+
+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);
+	}
+
+#endif
+
+#ifndef OPENSSL_NO_EC
+#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);
+	}
+  
+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);
+	}
+#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);
+	}
+
+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);
+	}
+#endif
+
+
+int X509_pubkey_digest(const X509 *data, const EVP_MD *type, unsigned char *md,
+	     unsigned int *len)
+	{
+	ASN1_BIT_STRING *key;
+	key = X509_get0_pubkey_bitstr(data);
+	if(!key) return 0;
+	return EVP_Digest(key->data, key->length, md, len, type, NULL);
+	}
+
+int X509_digest(const X509 *data, const EVP_MD *type, unsigned char *md,
+	     unsigned int *len)
+	{
+	return(ASN1_item_digest(ASN1_ITEM_rptr(X509),type,(char *)data,md,len));
+	}
+
+int X509_CRL_digest(const X509_CRL *data, const EVP_MD *type, unsigned char *md,
+	     unsigned int *len)
+	{
+	return(ASN1_item_digest(ASN1_ITEM_rptr(X509_CRL),type,(char *)data,md,len));
+	}
+
+int X509_REQ_digest(const X509_REQ *data, const EVP_MD *type, unsigned char *md,
+	     unsigned int *len)
+	{
+	return(ASN1_item_digest(ASN1_ITEM_rptr(X509_REQ),type,(char *)data,md,len));
+	}
+
+int X509_NAME_digest(const X509_NAME *data, const EVP_MD *type, unsigned char *md,
+	     unsigned int *len)
+	{
+	return(ASN1_item_digest(ASN1_ITEM_rptr(X509_NAME),type,(char *)data,md,len));
+	}
+
+#if 0 /* TODO(fork): remove */
+int PKCS7_ISSUER_AND_SERIAL_digest(PKCS7_ISSUER_AND_SERIAL *data, const EVP_MD *type,
+	     unsigned char *md, unsigned int *len)
+	{
+	return(ASN1_item_digest(ASN1_ITEM_rptr(PKCS7_ISSUER_AND_SERIAL),type,
+		(char *)data,md,len));
+	}
+#endif
+
+#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);
+	}
+#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);
+	}
+
+#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);
+	}
+
+int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key)
+	{
+	PKCS8_PRIV_KEY_INFO *p8inf;
+	int ret;
+	p8inf = EVP_PKEY2PKCS8(key);
+	if(!p8inf) return 0;
+	ret = i2d_PKCS8_PRIV_KEY_INFO_fp(fp, p8inf);
+	PKCS8_PRIV_KEY_INFO_free(p8inf);
+	return ret;
+	}
+
+int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey)
+	{
+	return ASN1_i2d_fp_of_const(EVP_PKEY,i2d_PrivateKey,fp,pkey);
+	}
+
+EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a)
+{
+	return ASN1_d2i_fp_of(EVP_PKEY,EVP_PKEY_new,d2i_AutoPrivateKey,fp,a);
+}
+
+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);
+	}
+
+int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key)
+	{
+	PKCS8_PRIV_KEY_INFO *p8inf;
+	int ret;
+	p8inf = EVP_PKEY2PKCS8(key);
+	if(!p8inf) return 0;
+	ret = i2d_PKCS8_PRIV_KEY_INFO_bio(bp, p8inf);
+	PKCS8_PRIV_KEY_INFO_free(p8inf);
+	return ret;
+	}
+#endif
+
+int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey)
+	{
+	return ASN1_i2d_bio_of_const(EVP_PKEY,i2d_PrivateKey,bp,pkey);
+	}
+
+EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a)
+	{
+	return ASN1_d2i_bio_of(EVP_PKEY,EVP_PKEY_new,d2i_AutoPrivateKey,bp,a);
+	}
+
+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);
+	}
diff --git a/crypto/x509/x_attrib.c b/crypto/x509/x_attrib.c
new file mode 100644
index 0000000..c460a70
--- /dev/null
+++ b/crypto/x509/x_attrib.c
@@ -0,0 +1,117 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/obj.h>
+
+
+/* X509_ATTRIBUTE: this has the following form:
+ *
+ * typedef struct x509_attributes_st
+ *	{
+ *	ASN1_OBJECT *object;
+ *	int single;
+ *	union	{
+ *		char		*ptr;
+ * 		STACK_OF(ASN1_TYPE) *set;
+ * 		ASN1_TYPE	*single;
+ *		} value;
+ *	} X509_ATTRIBUTE;
+ *
+ * this needs some extra thought because the CHOICE type is
+ * merged with the main structure and because the value can
+ * be anything at all we *must* try the SET OF first because
+ * the ASN1_ANY type will swallow anything including the whole
+ * SET OF structure.
+ */
+
+ASN1_CHOICE(X509_ATTRIBUTE_SET) = {
+	ASN1_SET_OF(X509_ATTRIBUTE, value.set, ASN1_ANY),
+	ASN1_SIMPLE(X509_ATTRIBUTE, value.single, ASN1_ANY)
+} ASN1_CHOICE_END_selector(X509_ATTRIBUTE, X509_ATTRIBUTE_SET, single)
+
+ASN1_SEQUENCE(X509_ATTRIBUTE) = {
+	ASN1_SIMPLE(X509_ATTRIBUTE, object, ASN1_OBJECT),
+	/* CHOICE type merged with parent */
+	ASN1_EX_COMBINE(0, 0, X509_ATTRIBUTE_SET)
+} ASN1_SEQUENCE_END(X509_ATTRIBUTE)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_ATTRIBUTE)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_ATTRIBUTE)
+
+X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value)
+	{
+	X509_ATTRIBUTE *ret=NULL;
+	ASN1_TYPE *val=NULL;
+
+	if ((ret=X509_ATTRIBUTE_new()) == NULL)
+		return(NULL);
+        /* TODO(fork): const correctness. */
+	ret->object=(ASN1_OBJECT*) OBJ_nid2obj(nid);
+	ret->single=0;
+	if ((ret->value.set=sk_ASN1_TYPE_new_null()) == NULL) goto err;
+	if ((val=ASN1_TYPE_new()) == NULL) goto err;
+	if (!sk_ASN1_TYPE_push(ret->value.set,val)) goto err;
+
+	ASN1_TYPE_set(val,atrtype,value);
+	return(ret);
+err:
+	if (ret != NULL) X509_ATTRIBUTE_free(ret);
+	if (val != NULL) ASN1_TYPE_free(val);
+	return(NULL);
+	}
diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c
new file mode 100644
index 0000000..2d74ff5
--- /dev/null
+++ b/crypto/x509/x_crl.c
@@ -0,0 +1,530 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "../asn1/asn1_locl.h"
+
+
+static int X509_REVOKED_cmp(const X509_REVOKED **a,
+				const X509_REVOKED **b);
+static void setup_idp(X509_CRL *crl, ISSUING_DIST_POINT *idp);
+
+ASN1_SEQUENCE(X509_REVOKED) = {
+	ASN1_SIMPLE(X509_REVOKED,serialNumber, ASN1_INTEGER),
+	ASN1_SIMPLE(X509_REVOKED,revocationDate, ASN1_TIME),
+	ASN1_SEQUENCE_OF_OPT(X509_REVOKED,extensions, X509_EXTENSION)
+} ASN1_SEQUENCE_END(X509_REVOKED)
+
+static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r);
+static int def_crl_lookup(X509_CRL *crl,
+		X509_REVOKED **ret, ASN1_INTEGER *serial, X509_NAME *issuer);
+
+static X509_CRL_METHOD int_crl_meth =
+	{
+	0,
+	0,0,
+	def_crl_lookup,
+	def_crl_verify
+	};
+
+static const X509_CRL_METHOD *default_crl_method = &int_crl_meth;
+
+/* The X509_CRL_INFO structure needs a bit of customisation.
+ * Since we cache the original encoding the signature wont be affected by
+ * reordering of the revoked field.
+ */
+static int crl_inf_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+								void *exarg)
+{
+	X509_CRL_INFO *a = (X509_CRL_INFO *)*pval;
+
+	if(!a || !a->revoked) return 1;
+	switch(operation) {
+		/* Just set cmp function here. We don't sort because that
+		 * would affect the output of X509_CRL_print().
+		 */
+		case ASN1_OP_D2I_POST:
+		(void)sk_X509_REVOKED_set_cmp_func(a->revoked,X509_REVOKED_cmp);
+		break;
+	}
+	return 1;
+}
+
+
+ASN1_SEQUENCE_enc(X509_CRL_INFO, enc, crl_inf_cb) = {
+	ASN1_OPT(X509_CRL_INFO, version, ASN1_INTEGER),
+	ASN1_SIMPLE(X509_CRL_INFO, sig_alg, X509_ALGOR),
+	ASN1_SIMPLE(X509_CRL_INFO, issuer, X509_NAME),
+	ASN1_SIMPLE(X509_CRL_INFO, lastUpdate, ASN1_TIME),
+	ASN1_OPT(X509_CRL_INFO, nextUpdate, ASN1_TIME),
+	ASN1_SEQUENCE_OF_OPT(X509_CRL_INFO, revoked, X509_REVOKED),
+	ASN1_EXP_SEQUENCE_OF_OPT(X509_CRL_INFO, extensions, X509_EXTENSION, 0)
+} ASN1_SEQUENCE_END_enc(X509_CRL_INFO, X509_CRL_INFO)
+
+/* Set CRL entry issuer according to CRL certificate issuer extension.
+ * Check for unhandled critical CRL entry extensions.
+ */
+
+static int crl_set_issuers(X509_CRL *crl)
+	{
+
+	size_t i, k;
+	int j;
+	GENERAL_NAMES *gens, *gtmp;
+	STACK_OF(X509_REVOKED) *revoked;
+
+	revoked = X509_CRL_get_REVOKED(crl);
+
+	gens = NULL;
+	for (i = 0; i < sk_X509_REVOKED_num(revoked); i++)
+		{
+		X509_REVOKED *rev = sk_X509_REVOKED_value(revoked, i);
+		STACK_OF(X509_EXTENSION) *exts;
+		ASN1_ENUMERATED *reason;
+		X509_EXTENSION *ext;
+		gtmp = X509_REVOKED_get_ext_d2i(rev, 
+						NID_certificate_issuer,
+						&j, NULL);
+		if (!gtmp && (j != -1))
+			{
+			crl->flags |= EXFLAG_INVALID;
+			return 1;
+			}
+
+		if (gtmp)
+			{
+			gens = gtmp;
+			if (!crl->issuers)
+				{
+				crl->issuers = sk_GENERAL_NAMES_new_null();
+				if (!crl->issuers)
+					return 0;
+				}
+			if (!sk_GENERAL_NAMES_push(crl->issuers, gtmp))
+				return 0;
+			}
+		rev->issuer = gens;
+
+		reason = X509_REVOKED_get_ext_d2i(rev, NID_crl_reason,
+								&j, NULL);
+		if (!reason && (j != -1))
+			{
+			crl->flags |= EXFLAG_INVALID;
+			return 1;
+			}
+
+		if (reason)
+			{
+			rev->reason = ASN1_ENUMERATED_get(reason);
+			ASN1_ENUMERATED_free(reason);
+			}
+		else
+			rev->reason = CRL_REASON_NONE;	
+
+		/* Check for critical CRL entry extensions */
+
+		exts = rev->extensions;
+
+		for (k = 0; k < sk_X509_EXTENSION_num(exts); k++)
+			{
+			ext = sk_X509_EXTENSION_value(exts, k);
+			if (ext->critical > 0)
+				{
+				if (OBJ_obj2nid(ext->object) ==
+					NID_certificate_issuer)
+					continue;
+				crl->flags |= EXFLAG_CRITICAL;
+				break;
+				}
+			}
+
+
+		}
+
+	return 1;
+
+	}
+
+/* The X509_CRL structure needs a bit of customisation. Cache some extensions
+ * and hash of the whole CRL.
+ */
+static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+								void *exarg)
+	{
+	X509_CRL *crl = (X509_CRL *)*pval;
+	STACK_OF(X509_EXTENSION) *exts;
+	X509_EXTENSION *ext;
+	size_t idx;
+
+	switch(operation)
+		{
+		case ASN1_OP_NEW_POST:
+		crl->idp = NULL;
+		crl->akid = NULL;
+		crl->flags = 0;
+		crl->idp_flags = 0;
+		crl->idp_reasons = CRLDP_ALL_REASONS;
+		crl->meth = default_crl_method;
+		crl->meth_data = NULL;
+		crl->issuers = NULL;
+		crl->crl_number = NULL;
+		crl->base_crl_number = NULL;
+		break;
+
+		case ASN1_OP_D2I_POST:
+#ifndef OPENSSL_NO_SHA
+		X509_CRL_digest(crl, EVP_sha1(), crl->sha1_hash, NULL);
+#endif
+		crl->idp = X509_CRL_get_ext_d2i(crl,
+				NID_issuing_distribution_point, NULL, NULL);
+		if (crl->idp)
+			setup_idp(crl, crl->idp);
+
+		crl->akid = X509_CRL_get_ext_d2i(crl,
+				NID_authority_key_identifier, NULL, NULL);	
+
+		crl->crl_number = X509_CRL_get_ext_d2i(crl,
+				NID_crl_number, NULL, NULL);	
+
+		crl->base_crl_number = X509_CRL_get_ext_d2i(crl,
+				NID_delta_crl, NULL, NULL);	
+		/* Delta CRLs must have CRL number */
+		if (crl->base_crl_number && !crl->crl_number)
+			crl->flags |= EXFLAG_INVALID;
+
+		/* See if we have any unhandled critical CRL extensions and 
+		 * indicate this in a flag. We only currently handle IDP so
+		 * anything else critical sets the flag.
+		 *
+		 * This code accesses the X509_CRL structure directly:
+		 * applications shouldn't do this.
+		 */
+
+		exts = crl->crl->extensions;
+
+		for (idx = 0; idx < sk_X509_EXTENSION_num(exts); idx++)
+			{
+			int nid;
+			ext = sk_X509_EXTENSION_value(exts, idx);
+			nid = OBJ_obj2nid(ext->object);
+			if (nid == NID_freshest_crl)
+				crl->flags |= EXFLAG_FRESHEST;
+			if (ext->critical > 0)
+				{
+				/* We handle IDP and deltas */
+				if ((nid == NID_issuing_distribution_point)
+					|| (nid == NID_delta_crl))
+					break;;
+				crl->flags |= EXFLAG_CRITICAL;
+				break;
+				}
+			}
+
+
+		if (!crl_set_issuers(crl))
+			return 0;
+
+		if (crl->meth->crl_init)
+			{
+			if (crl->meth->crl_init(crl) == 0)
+				return 0;
+			}
+		break;
+
+		case ASN1_OP_FREE_POST:
+		if (crl->meth->crl_free)
+			{
+			if (!crl->meth->crl_free(crl))
+				return 0;
+			}
+		if (crl->akid)
+			AUTHORITY_KEYID_free(crl->akid);
+		if (crl->idp)
+			ISSUING_DIST_POINT_free(crl->idp);
+		ASN1_INTEGER_free(crl->crl_number);
+		ASN1_INTEGER_free(crl->base_crl_number);
+		sk_GENERAL_NAMES_pop_free(crl->issuers, GENERAL_NAMES_free);
+		break;
+		}
+	return 1;
+	}
+
+/* Convert IDP into a more convenient form */
+
+static void setup_idp(X509_CRL *crl, ISSUING_DIST_POINT *idp)
+	{
+	int idp_only = 0;
+	/* Set various flags according to IDP */
+	crl->idp_flags |= IDP_PRESENT;
+	if (idp->onlyuser > 0)
+		{
+		idp_only++;
+		crl->idp_flags |= IDP_ONLYUSER;
+		}
+	if (idp->onlyCA > 0)
+		{
+		idp_only++;
+		crl->idp_flags |= IDP_ONLYCA;
+		}
+	if (idp->onlyattr > 0)
+		{
+		idp_only++;
+		crl->idp_flags |= IDP_ONLYATTR;
+		}
+
+	if (idp_only > 1)
+		crl->idp_flags |= IDP_INVALID;
+
+	if (idp->indirectCRL > 0)
+		crl->idp_flags |= IDP_INDIRECT;
+
+	if (idp->onlysomereasons)
+		{
+		crl->idp_flags |= IDP_REASONS;
+		if (idp->onlysomereasons->length > 0)
+			crl->idp_reasons = idp->onlysomereasons->data[0];
+		if (idp->onlysomereasons->length > 1)
+			crl->idp_reasons |=
+				(idp->onlysomereasons->data[1] << 8);
+		crl->idp_reasons &= CRLDP_ALL_REASONS;
+		}
+
+	DIST_POINT_set_dpname(idp->distpoint, X509_CRL_get_issuer(crl));
+	}
+
+ASN1_SEQUENCE_ref(X509_CRL, crl_cb, CRYPTO_LOCK_X509_CRL) = {
+	ASN1_SIMPLE(X509_CRL, crl, X509_CRL_INFO),
+	ASN1_SIMPLE(X509_CRL, sig_alg, X509_ALGOR),
+	ASN1_SIMPLE(X509_CRL, signature, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END_ref(X509_CRL, X509_CRL)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_REVOKED)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_REVOKED)
+IMPLEMENT_ASN1_FUNCTIONS(X509_CRL_INFO)
+IMPLEMENT_ASN1_FUNCTIONS(X509_CRL)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_CRL)
+
+static int X509_REVOKED_cmp(const X509_REVOKED **a,
+			const X509_REVOKED **b)
+	{
+	return(ASN1_STRING_cmp(
+		(ASN1_STRING *)(*a)->serialNumber,
+		(ASN1_STRING *)(*b)->serialNumber));
+	}
+
+int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev)
+{
+	X509_CRL_INFO *inf;
+	inf = crl->crl;
+	if(!inf->revoked)
+		inf->revoked = sk_X509_REVOKED_new(X509_REVOKED_cmp);
+	if(!inf->revoked || !sk_X509_REVOKED_push(inf->revoked, rev)) {
+		OPENSSL_PUT_ERROR(X509, X509_CRL_add0_revoked, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	inf->enc.modified = 1;
+	return 1;
+}
+
+int X509_CRL_verify(X509_CRL *crl, EVP_PKEY *r)
+	{
+	if (crl->meth->crl_verify)
+		return crl->meth->crl_verify(crl, r);
+	return 0;
+	}
+
+int X509_CRL_get0_by_serial(X509_CRL *crl,
+		X509_REVOKED **ret, ASN1_INTEGER *serial)
+	{
+	if (crl->meth->crl_lookup)
+		return crl->meth->crl_lookup(crl, ret, serial, NULL);
+	return 0;
+	}
+
+int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x)
+	{
+	if (crl->meth->crl_lookup)
+		return crl->meth->crl_lookup(crl, ret,
+						X509_get_serialNumber(x),
+						X509_get_issuer_name(x));
+	return 0;
+	}
+
+static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r)
+	{
+	return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CRL_INFO),
+		crl->sig_alg, crl->signature,crl->crl,r));
+	}
+
+static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm,
+						X509_REVOKED *rev)
+	{
+	size_t i;
+
+	if (!rev->issuer)
+		{
+		if (!nm)
+			return 1;
+		if (!X509_NAME_cmp(nm, X509_CRL_get_issuer(crl)))
+			return 1;
+		return 0;
+		}
+
+	if (!nm)
+		nm = X509_CRL_get_issuer(crl);
+
+	for (i = 0; i < sk_GENERAL_NAME_num(rev->issuer); i++)
+		{
+		GENERAL_NAME *gen = sk_GENERAL_NAME_value(rev->issuer, i);
+		if (gen->type != GEN_DIRNAME)
+			continue;
+		if (!X509_NAME_cmp(nm, gen->d.directoryName))
+			return 1;
+		}
+	return 0;
+
+	}
+
+static int def_crl_lookup(X509_CRL *crl,
+		X509_REVOKED **ret, ASN1_INTEGER *serial, X509_NAME *issuer)
+	{
+	X509_REVOKED rtmp, *rev;
+	size_t idx;
+	rtmp.serialNumber = serial;
+	/* Sort revoked into serial number order if not already sorted.
+	 * Do this under a lock to avoid race condition.
+ 	 */
+	if (!sk_X509_REVOKED_is_sorted(crl->crl->revoked))
+		{
+		CRYPTO_w_lock(CRYPTO_LOCK_X509_CRL);
+		sk_X509_REVOKED_sort(crl->crl->revoked);
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509_CRL);
+		}
+	if (!sk_X509_REVOKED_find(crl->crl->revoked, &idx, &rtmp))
+		return 0;
+	/* Need to look for matching name */
+	for(;idx < sk_X509_REVOKED_num(crl->crl->revoked); idx++)
+		{
+		rev = sk_X509_REVOKED_value(crl->crl->revoked, idx);
+		if (ASN1_INTEGER_cmp(rev->serialNumber, serial))
+			return 0;
+		if (crl_revoked_issuer_match(crl, issuer, rev))
+			{
+			if (ret)
+				*ret = rev;
+			if (rev->reason == CRL_REASON_REMOVE_FROM_CRL)
+				return 2;
+			return 1;
+			}
+		}
+	return 0;
+	}
+
+void X509_CRL_set_default_method(const X509_CRL_METHOD *meth)
+	{
+	if (meth == NULL)
+		default_crl_method = &int_crl_meth;
+	else 
+		default_crl_method = meth;
+	}
+
+X509_CRL_METHOD *X509_CRL_METHOD_new(
+	int (*crl_init)(X509_CRL *crl),
+	int (*crl_free)(X509_CRL *crl),
+	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
+				ASN1_INTEGER *ser, X509_NAME *issuer),
+	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk))
+	{
+	X509_CRL_METHOD *m;
+	m = OPENSSL_malloc(sizeof(X509_CRL_METHOD));
+	if (!m)
+		return NULL;
+	m->crl_init = crl_init;
+	m->crl_free = crl_free;
+	m->crl_lookup = crl_lookup;
+	m->crl_verify = crl_verify;
+	m->flags = X509_CRL_METHOD_DYNAMIC;
+	return m;
+	}
+
+void X509_CRL_METHOD_free(X509_CRL_METHOD *m)
+	{
+	if (!(m->flags & X509_CRL_METHOD_DYNAMIC))
+		return;
+	OPENSSL_free(m);
+	}
+
+void X509_CRL_set_meth_data(X509_CRL *crl, void *dat)
+	{
+	crl->meth_data = dat;
+	}
+
+void *X509_CRL_get_meth_data(X509_CRL *crl)
+	{
+	return crl->meth_data;
+	}
+
+IMPLEMENT_ASN1_SET_OF(X509_REVOKED)
+IMPLEMENT_ASN1_SET_OF(X509_CRL)
diff --git a/crypto/x509/x_exten.c b/crypto/x509/x_exten.c
new file mode 100644
index 0000000..cf64c84
--- /dev/null
+++ b/crypto/x509/x_exten.c
@@ -0,0 +1,75 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/cipher.h>
+#include <openssl/x509.h>
+
+
+ASN1_SEQUENCE(X509_EXTENSION) = {
+	ASN1_SIMPLE(X509_EXTENSION, object, ASN1_OBJECT),
+	ASN1_OPT(X509_EXTENSION, critical, ASN1_BOOLEAN),
+	ASN1_SIMPLE(X509_EXTENSION, value, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(X509_EXTENSION)
+
+ASN1_ITEM_TEMPLATE(X509_EXTENSIONS) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Extension, X509_EXTENSION)
+ASN1_ITEM_TEMPLATE_END(X509_EXTENSIONS)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_EXTENSION)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(X509_EXTENSIONS, X509_EXTENSIONS, X509_EXTENSIONS)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_EXTENSION)
diff --git a/crypto/x509/x_info.c b/crypto/x509/x_info.c
new file mode 100644
index 0000000..8047c71
--- /dev/null
+++ b/crypto/x509/x_info.c
@@ -0,0 +1,110 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+
+X509_INFO *X509_INFO_new(void)
+	{
+	X509_INFO *ret=NULL;
+
+	ret=(X509_INFO *)OPENSSL_malloc(sizeof(X509_INFO));
+	if (ret == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_INFO_new, ERR_R_MALLOC_FAILURE);
+		return(NULL);
+		}
+ 
+        ret->enc_cipher.cipher=NULL;
+        ret->enc_len=0;
+        ret->enc_data=NULL;
+ 
+	ret->references=1;
+	ret->x509=NULL;
+	ret->crl=NULL;
+	ret->x_pkey=NULL;
+	return(ret);
+	}
+
+void X509_INFO_free(X509_INFO *x)
+	{
+	int i;
+
+	if (x == NULL) return;
+
+	i=CRYPTO_add(&x->references,-1,CRYPTO_LOCK_X509_INFO);
+#ifdef REF_PRINT
+	REF_PRINT("X509_INFO",x);
+#endif
+	if (i > 0) return;
+#ifdef REF_CHECK
+	if (i < 0)
+		{
+		fprintf(stderr,"X509_INFO_free, bad reference count\n");
+		abort();
+		}
+#endif
+
+	if (x->x509 != NULL) X509_free(x->x509);
+	if (x->crl != NULL) X509_CRL_free(x->crl);
+	if (x->x_pkey != NULL) X509_PKEY_free(x->x_pkey);
+	if (x->enc_data != NULL) OPENSSL_free(x->enc_data);
+	OPENSSL_free(x);
+	}
diff --git a/crypto/x509/x_name.c b/crypto/x509/x_name.c
new file mode 100644
index 0000000..56eeaf4
--- /dev/null
+++ b/crypto/x509/x_name.c
@@ -0,0 +1,527 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <ctype.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+
+#include "../asn1/asn1_locl.h"
+
+
+typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY;
+DECLARE_STACK_OF(STACK_OF_X509_NAME_ENTRY)
+
+static int x509_name_ex_d2i(ASN1_VALUE **val,
+				const unsigned char **in, long len,
+				const ASN1_ITEM *it,
+				int tag, int aclass, char opt, ASN1_TLC *ctx);
+
+static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out,
+				const ASN1_ITEM *it, int tag, int aclass);
+static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it);
+static void x509_name_ex_free(ASN1_VALUE **val, const ASN1_ITEM *it);
+
+static int x509_name_encode(X509_NAME *a);
+static int x509_name_canon(X509_NAME *a);
+static int asn1_string_canon(ASN1_STRING *out, ASN1_STRING *in);
+static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname,
+			  unsigned char **in);
+
+
+static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval,
+						int indent,
+						const char *fname, 
+						const ASN1_PCTX *pctx);
+
+ASN1_SEQUENCE(X509_NAME_ENTRY) = {
+	ASN1_SIMPLE(X509_NAME_ENTRY, object, ASN1_OBJECT),
+	ASN1_SIMPLE(X509_NAME_ENTRY, value, ASN1_PRINTABLE)
+} ASN1_SEQUENCE_END(X509_NAME_ENTRY)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_NAME_ENTRY)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME_ENTRY)
+
+/* For the "Name" type we need a SEQUENCE OF { SET OF X509_NAME_ENTRY }
+ * so declare two template wrappers for this
+ */
+
+ASN1_ITEM_TEMPLATE(X509_NAME_ENTRIES) =
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF, 0, RDNS, X509_NAME_ENTRY)
+ASN1_ITEM_TEMPLATE_END(X509_NAME_ENTRIES)
+
+ASN1_ITEM_TEMPLATE(X509_NAME_INTERNAL) =
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Name, X509_NAME_ENTRIES)
+ASN1_ITEM_TEMPLATE_END(X509_NAME_INTERNAL)
+
+/* Normally that's where it would end: we'd have two nested STACK structures
+ * representing the ASN1. Unfortunately X509_NAME uses a completely different
+ * form and caches encodings so we have to process the internal form and convert
+ * to the external form.
+ */
+
+const ASN1_EXTERN_FUNCS x509_name_ff = {
+	NULL,
+	x509_name_ex_new,
+	x509_name_ex_free,
+	0,	/* Default clear behaviour is OK */
+	x509_name_ex_d2i,
+	x509_name_ex_i2d,
+	x509_name_ex_print
+};
+
+IMPLEMENT_EXTERN_ASN1(X509_NAME, V_ASN1_SEQUENCE, x509_name_ff) 
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_NAME)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME)
+
+static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it)
+{
+	X509_NAME *ret = NULL;
+	ret = OPENSSL_malloc(sizeof(X509_NAME));
+	if(!ret) goto memerr;
+	if ((ret->entries=sk_X509_NAME_ENTRY_new_null()) == NULL)
+		goto memerr;
+	if((ret->bytes = BUF_MEM_new()) == NULL) goto memerr;
+	ret->canon_enc = NULL;
+	ret->canon_enclen = 0;
+	ret->modified=1;
+	*val = (ASN1_VALUE *)ret;
+	return 1;
+
+ memerr:
+	OPENSSL_PUT_ERROR(X509, x509_name_ex_new, ERR_R_MALLOC_FAILURE);
+	if (ret)
+		{
+		if (ret->entries)
+			sk_X509_NAME_ENTRY_free(ret->entries);
+		OPENSSL_free(ret);
+		}
+	return 0;
+}
+
+static void x509_name_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+	X509_NAME *a;
+	if(!pval || !*pval)
+	    return;
+	a = (X509_NAME *)*pval;
+
+	BUF_MEM_free(a->bytes);
+	sk_X509_NAME_ENTRY_pop_free(a->entries,X509_NAME_ENTRY_free);
+	if (a->canon_enc)
+		OPENSSL_free(a->canon_enc);
+	OPENSSL_free(a);
+	*pval = NULL;
+}
+
+static int x509_name_ex_d2i(ASN1_VALUE **val,
+			const unsigned char **in, long len, const ASN1_ITEM *it,
+				int tag, int aclass, char opt, ASN1_TLC *ctx)
+{
+	const unsigned char *p = *in, *q;
+	union { STACK_OF(STACK_OF_X509_NAME_ENTRY) *s;
+		ASN1_VALUE *a; } intname = {NULL};
+	union { X509_NAME *x; ASN1_VALUE *a; } nm = {NULL};
+	size_t i, j;
+	int ret;
+	STACK_OF(X509_NAME_ENTRY) *entries;
+	X509_NAME_ENTRY *entry;
+	q = p;
+
+	/* Get internal representation of Name */
+	ret = ASN1_item_ex_d2i(&intname.a,
+			       &p, len, ASN1_ITEM_rptr(X509_NAME_INTERNAL),
+			       tag, aclass, opt, ctx);
+	
+	if(ret <= 0) return ret;
+
+	if(*val) x509_name_ex_free(val, NULL);
+	if(!x509_name_ex_new(&nm.a, NULL)) goto err;
+	/* We've decoded it: now cache encoding */
+	if(!BUF_MEM_grow(nm.x->bytes, p - q)) goto err;
+	memcpy(nm.x->bytes->data, q, p - q);
+
+	/* Convert internal representation to X509_NAME structure */
+	for(i = 0; i < sk_STACK_OF_X509_NAME_ENTRY_num(intname.s); i++) {
+		entries = sk_STACK_OF_X509_NAME_ENTRY_value(intname.s, i);
+		for(j = 0; j < sk_X509_NAME_ENTRY_num(entries); j++) {
+			entry = sk_X509_NAME_ENTRY_value(entries, j);
+			entry->set = i;
+			if(!sk_X509_NAME_ENTRY_push(nm.x->entries, entry))
+				goto err;
+		}
+		sk_X509_NAME_ENTRY_free(entries);
+	}
+	sk_STACK_OF_X509_NAME_ENTRY_free(intname.s);
+	ret = x509_name_canon(nm.x);
+	if (!ret)
+		goto err;
+	nm.x->modified = 0;
+	*val = nm.a;
+	*in = p;
+	return ret;
+err:
+        if (nm.x != NULL)
+		X509_NAME_free(nm.x);
+	OPENSSL_PUT_ERROR(X509, x509_name_ex_d2i, ERR_R_ASN1_LIB);
+	return 0;
+}
+
+static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass)
+{
+	int ret;
+	X509_NAME *a = (X509_NAME *)*val;
+	if(a->modified) {
+		ret = x509_name_encode(a);
+		if(ret < 0)
+			return ret;
+		ret = x509_name_canon(a);
+		if(ret < 0)
+			return ret;
+	}
+	ret = a->bytes->length;
+	if(out != NULL) {
+		memcpy(*out,a->bytes->data,ret);
+		*out+=ret;
+	}
+	return ret;
+}
+
+static void local_sk_X509_NAME_ENTRY_free(STACK_OF(X509_NAME_ENTRY) *ne)
+	{
+	sk_X509_NAME_ENTRY_free(ne);
+	}
+
+static void local_sk_X509_NAME_ENTRY_pop_free(STACK_OF(X509_NAME_ENTRY) *ne)
+	{
+	sk_X509_NAME_ENTRY_pop_free(ne, X509_NAME_ENTRY_free);
+	}
+
+static int x509_name_encode(X509_NAME *a)
+{
+	union { STACK_OF(STACK_OF_X509_NAME_ENTRY) *s;
+		ASN1_VALUE *a; } intname = {NULL};
+	int len;
+	unsigned char *p;
+	STACK_OF(X509_NAME_ENTRY) *entries = NULL;
+	X509_NAME_ENTRY *entry;
+	int set = -1;
+	size_t i;
+	intname.s = sk_STACK_OF_X509_NAME_ENTRY_new_null();
+	if(!intname.s) goto memerr;
+	for(i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) {
+		entry = sk_X509_NAME_ENTRY_value(a->entries, i);
+		if(entry->set != set) {
+			entries = sk_X509_NAME_ENTRY_new_null();
+			if(!entries) goto memerr;
+			if(!sk_STACK_OF_X509_NAME_ENTRY_push(intname.s,
+							     entries))
+				goto memerr;
+			set = entry->set;
+		}
+		if(!sk_X509_NAME_ENTRY_push(entries, entry)) goto memerr;
+	}
+	len = ASN1_item_ex_i2d(&intname.a, NULL,
+			       ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1);
+	if (!BUF_MEM_grow(a->bytes,len)) goto memerr;
+	p=(unsigned char *)a->bytes->data;
+	ASN1_item_ex_i2d(&intname.a,
+			 &p, ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1);
+	sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s,
+					     local_sk_X509_NAME_ENTRY_free);
+	a->modified = 0;
+	return len;
+memerr:
+	sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s,
+					     local_sk_X509_NAME_ENTRY_free);
+	OPENSSL_PUT_ERROR(X509, x509_name_encode, ERR_R_MALLOC_FAILURE);
+	return -1;
+}
+
+static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval,
+						int indent,
+						const char *fname, 
+						const ASN1_PCTX *pctx)
+	{
+	if (X509_NAME_print_ex(out, (X509_NAME *)*pval,
+					indent, pctx->nm_flags) <= 0)
+		return 0;
+	return 2;
+	}
+
+/* This function generates the canonical encoding of the Name structure.
+ * In it all strings are converted to UTF8, leading, trailing and
+ * multiple spaces collapsed, converted to lower case and the leading
+ * SEQUENCE header removed.
+ *
+ * In future we could also normalize the UTF8 too.
+ *
+ * By doing this comparison of Name structures can be rapidly
+ * perfomed by just using memcmp() of the canonical encoding.
+ * By omitting the leading SEQUENCE name constraints of type
+ * dirName can also be checked with a simple memcmp().
+ */
+
+static int x509_name_canon(X509_NAME *a)
+	{
+	unsigned char *p;
+	STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname = NULL;
+	STACK_OF(X509_NAME_ENTRY) *entries = NULL;
+	X509_NAME_ENTRY *entry, *tmpentry = NULL;
+	int set = -1, ret = 0;
+	size_t i;
+
+	if (a->canon_enc)
+		{
+		OPENSSL_free(a->canon_enc);
+		a->canon_enc = NULL;
+		}
+	/* Special case: empty X509_NAME => null encoding */
+	if (sk_X509_NAME_ENTRY_num(a->entries) == 0)
+		{
+		a->canon_enclen = 0;
+		return 1;
+		}
+	intname = sk_STACK_OF_X509_NAME_ENTRY_new_null();
+	if(!intname)
+		goto err;
+	for(i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++)
+		{
+		entry = sk_X509_NAME_ENTRY_value(a->entries, i);
+		if(entry->set != set)
+			{
+			entries = sk_X509_NAME_ENTRY_new_null();
+			if(!entries)
+				goto err;
+			if(!sk_STACK_OF_X509_NAME_ENTRY_push(intname, entries))
+				goto err;
+			set = entry->set;
+			}
+		tmpentry = X509_NAME_ENTRY_new();
+		tmpentry->object = OBJ_dup(entry->object);
+		if (!asn1_string_canon(tmpentry->value, entry->value))
+			goto err;
+		if(!sk_X509_NAME_ENTRY_push(entries, tmpentry))
+			goto err;
+		tmpentry = NULL;
+		}
+
+	/* Finally generate encoding */
+
+	a->canon_enclen = i2d_name_canon(intname, NULL);
+
+	p = OPENSSL_malloc(a->canon_enclen);
+
+	if (!p)
+		goto err;
+
+	a->canon_enc = p;
+
+	i2d_name_canon(intname, &p);
+
+	ret = 1;
+
+	err:
+
+	if (tmpentry)
+		X509_NAME_ENTRY_free(tmpentry);
+	if (intname)
+		sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname,
+					local_sk_X509_NAME_ENTRY_pop_free);
+	return ret;
+	}
+
+/* Bitmap of all the types of string that will be canonicalized. */
+
+#define ASN1_MASK_CANON	\
+	(B_ASN1_UTF8STRING | B_ASN1_BMPSTRING | B_ASN1_UNIVERSALSTRING \
+	| B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_IA5STRING \
+	| B_ASN1_VISIBLESTRING)
+	
+
+static int asn1_string_canon(ASN1_STRING *out, ASN1_STRING *in)
+	{
+	unsigned char *to, *from;
+	int len, i;
+
+	/* If type not in bitmask just copy string across */
+	if (!(ASN1_tag2bit(in->type) & ASN1_MASK_CANON))
+		{
+		if (!ASN1_STRING_copy(out, in))
+			return 0;
+		return 1;
+		}
+
+	out->type = V_ASN1_UTF8STRING;
+	out->length = ASN1_STRING_to_UTF8(&out->data, in);
+	if (out->length == -1)
+		return 0;
+
+	to = out->data;
+	from = to;
+
+	len = out->length;
+
+	/* Convert string in place to canonical form.
+	 * Ultimately we may need to handle a wider range of characters
+	 * but for now ignore anything with MSB set and rely on the
+	 * isspace() and tolower() functions.
+	 */
+
+	/* Ignore leading spaces */
+	while((len > 0) && !(*from & 0x80) && isspace(*from))
+		{
+		from++;
+		len--;
+		}
+
+	to = from + len - 1;
+
+	/* Ignore trailing spaces */
+	while ((len > 0) && !(*to & 0x80) && isspace(*to))
+		{
+		to--;
+		len--;
+		}
+
+	to = out->data;
+
+	i = 0;
+	while(i < len)
+		{
+		/* If MSB set just copy across */
+		if (*from & 0x80)
+			{
+			*to++ = *from++;
+			i++;
+			}
+		/* Collapse multiple spaces */
+		else if (isspace(*from))
+			{
+			/* Copy one space across */
+			*to++ = ' ';
+			/* Ignore subsequent spaces. Note: don't need to
+			 * check len here because we know the last 
+			 * character is a non-space so we can't overflow.
+			 */
+			do
+				{
+				from++;
+				i++;
+				}
+			while(!(*from & 0x80) && isspace(*from));
+			}
+		else
+			{
+			*to++ = tolower(*from);
+			from++;
+			i++;
+			}
+		}
+
+	out->length = to - out->data;
+
+	return 1;
+
+	}
+
+static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) *_intname,
+			  unsigned char **in)
+	{
+	int len, ltmp;
+	size_t i;
+	ASN1_VALUE *v;
+	STACK_OF(ASN1_VALUE) *intname = (STACK_OF(ASN1_VALUE) *)_intname;
+
+	len = 0;
+	for (i = 0; i < sk_ASN1_VALUE_num(intname); i++)
+		{
+		v = sk_ASN1_VALUE_value(intname, i);
+		ltmp = ASN1_item_ex_i2d(&v, in,
+			ASN1_ITEM_rptr(X509_NAME_ENTRIES), -1, -1);
+		if (ltmp < 0)
+			return ltmp;
+		len += ltmp;
+		}
+	return len;
+	}
+
+int X509_NAME_set(X509_NAME **xn, X509_NAME *name)
+	{
+	X509_NAME *in;
+
+	if (!xn || !name) return(0);
+
+	if (*xn != name)
+		{
+		in=X509_NAME_dup(name);
+		if (in != NULL)
+			{
+			X509_NAME_free(*xn);
+			*xn=in;
+			}
+		}
+	return(*xn != NULL);
+	}
+	
+IMPLEMENT_ASN1_SET_OF(X509_NAME_ENTRY)
diff --git a/crypto/x509/x_pkey.c b/crypto/x509/x_pkey.c
new file mode 100644
index 0000000..b7d29f1
--- /dev/null
+++ b/crypto/x509/x_pkey.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/mem.h>
+#include <openssl/err.h>
+
+
+OPENSSL_DECLARE_ERROR_FUNCTION(X509, d2i_X509_PKEY);
+OPENSSL_DECLARE_ERROR_FUNCTION(X509, X509_PKEY_new);
+
+/* need to implement */
+int i2d_X509_PKEY(X509_PKEY *a, unsigned char **pp)
+	{
+	return(0);
+	}
+
+X509_PKEY *d2i_X509_PKEY(X509_PKEY **a, const unsigned char **pp, long length)
+	{
+	int i;
+	M_ASN1_D2I_vars(a,X509_PKEY *,X509_PKEY_new);
+
+	M_ASN1_D2I_Init();
+	M_ASN1_D2I_start_sequence();
+	M_ASN1_D2I_get_x(X509_ALGOR,ret->enc_algor,d2i_X509_ALGOR);
+	M_ASN1_D2I_get_x(ASN1_OCTET_STRING,ret->enc_pkey,d2i_ASN1_OCTET_STRING);
+
+	ret->cipher.cipher=NULL;
+
+        /* TODO(fork): fix */
+	/*ret->cipher.cipher=EVP_get_cipherbyname(
+		OBJ_nid2ln(OBJ_obj2nid(ret->enc_algor->algorithm)));*/
+
+	if (ret->cipher.cipher == NULL)
+		{
+		c.error=ASN1_R_UNSUPPORTED_CIPHER;
+		c.line=__LINE__;
+		goto err;
+		}
+	if (ret->enc_algor->parameter->type == V_ASN1_OCTET_STRING) 
+		{
+		i=ret->enc_algor->parameter->value.octet_string->length;
+		if (i > EVP_MAX_IV_LENGTH)
+			{
+			c.error=ASN1_R_IV_TOO_LARGE;
+			c.line=__LINE__;
+			goto err;
+			}
+		memcpy(ret->cipher.iv,
+			ret->enc_algor->parameter->value.octet_string->data,i);
+		}
+	else
+		memset(ret->cipher.iv,0,EVP_MAX_IV_LENGTH);
+	M_ASN1_D2I_Finish(a,X509_PKEY_free,X509_F_d2i_X509_PKEY);
+	}
+
+X509_PKEY *X509_PKEY_new(void)
+	{
+	X509_PKEY *ret=NULL;
+	ASN1_CTX c;
+
+	M_ASN1_New_Malloc(ret,X509_PKEY);
+	ret->version=0;
+	M_ASN1_New(ret->enc_algor,X509_ALGOR_new);
+	M_ASN1_New(ret->enc_pkey,M_ASN1_OCTET_STRING_new);
+	ret->dec_pkey=NULL;
+	ret->key_length=0;
+	ret->key_data=NULL;
+	ret->key_free=0;
+	ret->cipher.cipher=NULL;
+	memset(ret->cipher.iv,0,EVP_MAX_IV_LENGTH);
+	ret->references=1;
+	return(ret);
+	M_ASN1_New_Error(X509_F_X509_PKEY_new);
+	}
+
+void X509_PKEY_free(X509_PKEY *x)
+	{
+	int i;
+
+	if (x == NULL) return;
+
+	i=CRYPTO_add(&x->references,-1,CRYPTO_LOCK_X509_PKEY);
+	if (i > 0) return;
+
+	if (x->enc_algor != NULL) X509_ALGOR_free(x->enc_algor);
+	if (x->enc_pkey != NULL) M_ASN1_OCTET_STRING_free(x->enc_pkey);
+	if (x->dec_pkey != NULL)EVP_PKEY_free(x->dec_pkey);
+	if ((x->key_data != NULL) && (x->key_free)) OPENSSL_free(x->key_data);
+	OPENSSL_free(x);
+	}
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
new file mode 100644
index 0000000..df1d4af
--- /dev/null
+++ b/crypto/x509/x_pubkey.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "../evp/internal.h"
+
+
+/* Minor tweak to operation: free up EVP_PKEY */
+static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+			void *exarg)
+	{
+	if (operation == ASN1_OP_FREE_POST)
+		{
+		X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval;
+		EVP_PKEY_free(pubkey->pkey);
+		}
+	return 1;
+	}
+
+ASN1_SEQUENCE_cb(X509_PUBKEY, pubkey_cb) = {
+	ASN1_SIMPLE(X509_PUBKEY, algor, X509_ALGOR),
+	ASN1_SIMPLE(X509_PUBKEY, public_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END_cb(X509_PUBKEY, X509_PUBKEY)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_PUBKEY)
+
+int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey)
+	{
+	X509_PUBKEY *pk=NULL;
+
+	if (x == NULL) return(0);
+
+	if ((pk=X509_PUBKEY_new()) == NULL) goto error;
+
+	if (pkey->ameth)
+		{
+		if (pkey->ameth->pub_encode)
+			{
+			if (!pkey->ameth->pub_encode(pk, pkey))
+				{
+				OPENSSL_PUT_ERROR(X509, X509_PUBKEY_set, X509_R_PUBLIC_KEY_ENCODE_ERROR);
+				goto error;
+				}
+			}
+		else
+			{
+			OPENSSL_PUT_ERROR(X509, X509_PUBKEY_set, X509_R_METHOD_NOT_SUPPORTED);
+			goto error;
+			}
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(X509, X509_PUBKEY_set, X509_R_UNSUPPORTED_ALGORITHM);
+		goto error;
+		}
+
+	if (*x != NULL)
+		X509_PUBKEY_free(*x);
+
+	*x=pk;
+
+	return 1;
+error:
+	if (pk != NULL) X509_PUBKEY_free(pk);
+	return 0;
+	}
+
+EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key)
+	{
+	EVP_PKEY *ret=NULL;
+
+	if (key == NULL) goto error;
+
+	if (key->pkey != NULL)
+		{
+		CRYPTO_add(&key->pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+		return key->pkey;
+		}
+
+	if (key->public_key == NULL) goto error;
+
+	if ((ret = EVP_PKEY_new()) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, X509_PUBKEY_get, ERR_R_MALLOC_FAILURE);
+		goto error;
+		}
+
+	if (!EVP_PKEY_set_type(ret, OBJ_obj2nid(key->algor->algorithm)))
+		{
+		OPENSSL_PUT_ERROR(X509, X509_PUBKEY_get, X509_R_UNSUPPORTED_ALGORITHM);
+		goto error;
+		}
+
+	if (ret->ameth->pub_decode)
+		{
+		if (!ret->ameth->pub_decode(ret, key))
+			{
+			OPENSSL_PUT_ERROR(X509, X509_PUBKEY_get, X509_R_PUBLIC_KEY_DECODE_ERROR);
+			goto error;
+			}
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(X509, X509_PUBKEY_get, X509_R_METHOD_NOT_SUPPORTED);
+		goto error;
+		}
+
+	/* Check to see if another thread set key->pkey first */
+	CRYPTO_w_lock(CRYPTO_LOCK_EVP_PKEY);
+	if (key->pkey)
+		{
+		CRYPTO_w_unlock(CRYPTO_LOCK_EVP_PKEY);
+		EVP_PKEY_free(ret);
+		ret = key->pkey;
+		}
+	else
+		{
+		key->pkey = ret;
+		CRYPTO_w_unlock(CRYPTO_LOCK_EVP_PKEY);
+		}
+	CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_EVP_PKEY);
+
+	return ret;
+
+	error:
+	if (ret != NULL)
+		EVP_PKEY_free(ret);
+	return(NULL);
+	}
+
+/* Now two pseudo ASN1 routines that take an EVP_PKEY structure
+ * and encode or decode as X509_PUBKEY
+ */
+
+EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp,
+	     long length)
+	{
+	X509_PUBKEY *xpk;
+	EVP_PKEY *pktmp;
+	xpk = d2i_X509_PUBKEY(NULL, pp, length);
+	if(!xpk) return NULL;
+	pktmp = X509_PUBKEY_get(xpk);
+	X509_PUBKEY_free(xpk);
+	if(!pktmp) return NULL;
+	if(a)
+		{
+		EVP_PKEY_free(*a);
+		*a = pktmp;
+		}
+	return pktmp;
+	}
+
+int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
+	{
+	X509_PUBKEY *xpk=NULL;
+	int ret;
+	if(!a) return 0;
+	if(!X509_PUBKEY_set(&xpk, (EVP_PKEY*) a)) return 0;
+	ret = i2d_X509_PUBKEY(xpk, pp);
+	X509_PUBKEY_free(xpk);
+	return ret;
+	}
+
+/* The following are equivalents but which return RSA and DSA
+ * keys
+ */
+#ifndef OPENSSL_NO_RSA
+RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp,
+	     long length)
+	{
+	EVP_PKEY *pkey;
+	RSA *key;
+	const unsigned char *q;
+	q = *pp;
+	pkey = d2i_PUBKEY(NULL, &q, length);
+	if (!pkey) return NULL;
+	key = EVP_PKEY_get1_RSA(pkey);
+	EVP_PKEY_free(pkey);
+	if (!key) return NULL;
+	*pp = q;
+	if (a)
+		{
+		RSA_free(*a);
+		*a = key;
+		}
+	return key;
+	}
+
+int i2d_RSA_PUBKEY(const RSA *a, unsigned char **pp)
+	{
+	EVP_PKEY *pktmp;
+	int ret;
+	if (!a) return 0;
+	pktmp = EVP_PKEY_new();
+	if (!pktmp)
+		{
+		OPENSSL_PUT_ERROR(X509, i2d_RSA_PUBKEY, ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	EVP_PKEY_set1_RSA(pktmp, (RSA*) a);
+	ret = i2d_PUBKEY(pktmp, pp);
+	EVP_PKEY_free(pktmp);
+	return ret;
+	}
+#endif
+
+#ifndef OPENSSL_NO_DSA
+DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp,
+	     long length)
+	{
+	EVP_PKEY *pkey;
+	DSA *key;
+	const unsigned char *q;
+	q = *pp;
+	pkey = d2i_PUBKEY(NULL, &q, length);
+	if (!pkey) return NULL;
+	key = EVP_PKEY_get1_DSA(pkey);
+	EVP_PKEY_free(pkey);
+	if (!key) return NULL;
+	*pp = q;
+	if (a)
+		{
+		DSA_free(*a);
+		*a = key;
+		}
+	return key;
+	}
+
+int i2d_DSA_PUBKEY(const DSA *a, unsigned char **pp)
+	{
+	EVP_PKEY *pktmp;
+	int ret;
+	if(!a) return 0;
+	pktmp = EVP_PKEY_new();
+	if(!pktmp)
+		{
+		OPENSSL_PUT_ERROR(X509, i2d_DSA_PUBKEY,  ERR_R_MALLOC_FAILURE);
+		return 0;
+		}
+	EVP_PKEY_set1_DSA(pktmp, (DSA*) a);
+	ret = i2d_PUBKEY(pktmp, pp);
+	EVP_PKEY_free(pktmp);
+	return ret;
+	}
+#endif
+
+#ifndef OPENSSL_NO_EC
+EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, long length)
+	{
+	EVP_PKEY *pkey;
+	EC_KEY *key;
+	const unsigned char *q;
+	q = *pp;
+	pkey = d2i_PUBKEY(NULL, &q, length);
+	if (!pkey) return(NULL);
+	key = EVP_PKEY_get1_EC_KEY(pkey);
+	EVP_PKEY_free(pkey);
+	if (!key)  return(NULL);
+	*pp = q;
+	if (a)
+		{
+		EC_KEY_free(*a);
+		*a = key;
+		}
+	return(key);
+	}
+
+int i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp)
+	{
+	EVP_PKEY *pktmp;
+	int ret;
+	if (!a)	return(0);
+	if ((pktmp = EVP_PKEY_new()) == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509, i2d_EC_PUBKEY,  ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+	EVP_PKEY_set1_EC_KEY(pktmp, (EC_KEY*) a);
+	ret = i2d_PUBKEY(pktmp, pp);
+	EVP_PKEY_free(pktmp);
+	return(ret);
+	}
+#endif
+
+int X509_PUBKEY_set0_param(X509_PUBKEY *pub, const ASN1_OBJECT *aobj,
+					int ptype, void *pval,
+					unsigned char *penc, int penclen)
+	{
+	if (!X509_ALGOR_set0(pub->algor, aobj, ptype, pval))
+		return 0;
+	if (penc)
+		{
+		if (pub->public_key->data)
+			OPENSSL_free(pub->public_key->data);
+		pub->public_key->data = penc;
+		pub->public_key->length = penclen;
+  		/* Set number of unused bits to zero */
+		pub->public_key->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
+		pub->public_key->flags|=ASN1_STRING_FLAG_BITS_LEFT;
+		}
+	return 1;
+	}
+
+int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg,
+		const unsigned char **pk, int *ppklen,
+		X509_ALGOR **pa,
+		X509_PUBKEY *pub)
+	{
+	if (ppkalg)
+		*ppkalg = pub->algor->algorithm;
+	if (pk)
+		{
+		*pk = pub->public_key->data;
+		*ppklen = pub->public_key->length;
+		}
+	if (pa)
+		*pa = pub->algor;
+	return 1;
+	}
diff --git a/crypto/x509/x_req.c b/crypto/x509/x_req.c
new file mode 100644
index 0000000..8bf4613
--- /dev/null
+++ b/crypto/x509/x_req.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/thread.h>
+#include <openssl/x509.h>
+
+/* X509_REQ_INFO is handled in an unusual way to get round
+ * invalid encodings. Some broken certificate requests don't
+ * encode the attributes field if it is empty. This is in
+ * violation of PKCS#10 but we need to tolerate it. We do
+ * this by making the attributes field OPTIONAL then using
+ * the callback to initialise it to an empty STACK. 
+ *
+ * This means that the field will be correctly encoded unless
+ * we NULL out the field.
+ *
+ * As a result we no longer need the req_kludge field because
+ * the information is now contained in the attributes field:
+ * 1. If it is NULL then it's the invalid omission.
+ * 2. If it is empty it is the correct encoding.
+ * 3. If it is not empty then some attributes are present.
+ *
+ */
+
+static int rinf_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+							void *exarg)
+{
+	X509_REQ_INFO *rinf = (X509_REQ_INFO *)*pval;
+
+	if(operation == ASN1_OP_NEW_POST) {
+		rinf->attributes = sk_X509_ATTRIBUTE_new_null();
+		if(!rinf->attributes) return 0;
+	}
+	return 1;
+}
+
+ASN1_SEQUENCE_enc(X509_REQ_INFO, enc, rinf_cb) = {
+	ASN1_SIMPLE(X509_REQ_INFO, version, ASN1_INTEGER),
+	ASN1_SIMPLE(X509_REQ_INFO, subject, X509_NAME),
+	ASN1_SIMPLE(X509_REQ_INFO, pubkey, X509_PUBKEY),
+	/* This isn't really OPTIONAL but it gets round invalid
+	 * encodings
+	 */
+	ASN1_IMP_SET_OF_OPT(X509_REQ_INFO, attributes, X509_ATTRIBUTE, 0)
+} ASN1_SEQUENCE_END_enc(X509_REQ_INFO, X509_REQ_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_REQ_INFO)
+
+ASN1_SEQUENCE_ref(X509_REQ, 0, CRYPTO_LOCK_X509_REQ) = {
+	ASN1_SIMPLE(X509_REQ, req_info, X509_REQ_INFO),
+	ASN1_SIMPLE(X509_REQ, sig_alg, X509_ALGOR),
+	ASN1_SIMPLE(X509_REQ, signature, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END_ref(X509_REQ, X509_REQ)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_REQ)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_REQ)
diff --git a/crypto/x509/x_sig.c b/crypto/x509/x_sig.c
new file mode 100644
index 0000000..fabdb67
--- /dev/null
+++ b/crypto/x509/x_sig.c
@@ -0,0 +1,69 @@
+/* crypto/asn1/x_sig.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+
+
+ASN1_SEQUENCE(X509_SIG) = {
+	ASN1_SIMPLE(X509_SIG, algor, X509_ALGOR),
+	ASN1_SIMPLE(X509_SIG, digest, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(X509_SIG)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_SIG)
diff --git a/crypto/x509/x_spki.c b/crypto/x509/x_spki.c
new file mode 100644
index 0000000..35bf246
--- /dev/null
+++ b/crypto/x509/x_spki.c
@@ -0,0 +1,78 @@
+/* crypto/asn1/x_spki.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+ /* This module was send to me my Pat Richards <patr@x509.com> who
+  * wrote it.  It is under my Copyright with his permission. */
+
+#include <openssl/x509.h>
+#include <openssl/asn1t.h>
+
+
+ASN1_SEQUENCE(NETSCAPE_SPKAC) = {
+	ASN1_SIMPLE(NETSCAPE_SPKAC, pubkey, X509_PUBKEY),
+	ASN1_SIMPLE(NETSCAPE_SPKAC, challenge, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(NETSCAPE_SPKAC)
+
+IMPLEMENT_ASN1_FUNCTIONS(NETSCAPE_SPKAC)
+
+ASN1_SEQUENCE(NETSCAPE_SPKI) = {
+	ASN1_SIMPLE(NETSCAPE_SPKI, spkac, NETSCAPE_SPKAC),
+	ASN1_SIMPLE(NETSCAPE_SPKI, sig_algor, X509_ALGOR),
+	ASN1_SIMPLE(NETSCAPE_SPKI, signature, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(NETSCAPE_SPKI)
+
+IMPLEMENT_ASN1_FUNCTIONS(NETSCAPE_SPKI)
diff --git a/crypto/x509/x_val.c b/crypto/x509/x_val.c
new file mode 100644
index 0000000..26200ee
--- /dev/null
+++ b/crypto/x509/x_val.c
@@ -0,0 +1,69 @@
+/* crypto/asn1/x_val.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+
+
+ASN1_SEQUENCE(X509_VAL) = {
+	ASN1_SIMPLE(X509_VAL, notBefore, ASN1_TIME),
+	ASN1_SIMPLE(X509_VAL, notAfter, ASN1_TIME)
+} ASN1_SEQUENCE_END(X509_VAL)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_VAL)
diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c
new file mode 100644
index 0000000..712cff4
--- /dev/null
+++ b/crypto/x509/x_x509.c
@@ -0,0 +1,203 @@
+/* crypto/asn1/x_x509.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/thread.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+ASN1_SEQUENCE_enc(X509_CINF, enc, 0) = {
+	ASN1_EXP_OPT(X509_CINF, version, ASN1_INTEGER, 0),
+	ASN1_SIMPLE(X509_CINF, serialNumber, ASN1_INTEGER),
+	ASN1_SIMPLE(X509_CINF, signature, X509_ALGOR),
+	ASN1_SIMPLE(X509_CINF, issuer, X509_NAME),
+	ASN1_SIMPLE(X509_CINF, validity, X509_VAL),
+	ASN1_SIMPLE(X509_CINF, subject, X509_NAME),
+	ASN1_SIMPLE(X509_CINF, key, X509_PUBKEY),
+	ASN1_IMP_OPT(X509_CINF, issuerUID, ASN1_BIT_STRING, 1),
+	ASN1_IMP_OPT(X509_CINF, subjectUID, ASN1_BIT_STRING, 2),
+	ASN1_EXP_SEQUENCE_OF_OPT(X509_CINF, extensions, X509_EXTENSION, 3)
+} ASN1_SEQUENCE_END_enc(X509_CINF, X509_CINF)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_CINF)
+/* X509 top level structure needs a bit of customisation */
+
+extern void policy_cache_free(X509_POLICY_CACHE *cache);
+
+static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+								void *exarg)
+{
+	X509 *ret = (X509 *)*pval;
+
+	switch(operation) {
+
+		case ASN1_OP_NEW_POST:
+		ret->valid=0;
+		ret->name = NULL;
+		ret->ex_flags = 0;
+		ret->ex_pathlen = -1;
+		ret->skid = NULL;
+		ret->akid = NULL;
+		ret->aux = NULL;
+		ret->crldp = NULL;
+		CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data);
+		break;
+
+		case ASN1_OP_D2I_POST:
+		if (ret->name != NULL) OPENSSL_free(ret->name);
+		ret->name=X509_NAME_oneline(ret->cert_info->subject,NULL,0);
+		break;
+
+		case ASN1_OP_FREE_POST:
+		CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data);
+		X509_CERT_AUX_free(ret->aux);
+		ASN1_OCTET_STRING_free(ret->skid);
+		AUTHORITY_KEYID_free(ret->akid);
+		CRL_DIST_POINTS_free(ret->crldp);
+		policy_cache_free(ret->policy_cache);
+		GENERAL_NAMES_free(ret->altname);
+		NAME_CONSTRAINTS_free(ret->nc);
+
+		if (ret->name != NULL) OPENSSL_free(ret->name);
+		break;
+
+	}
+
+	return 1;
+
+}
+
+ASN1_SEQUENCE_ref(X509, x509_cb, CRYPTO_LOCK_X509) = {
+	ASN1_SIMPLE(X509, cert_info, X509_CINF),
+	ASN1_SIMPLE(X509, sig_alg, X509_ALGOR),
+	ASN1_SIMPLE(X509, signature, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END_ref(X509, X509)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509)
+
+int X509_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+	     CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
+        {
+	return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509, argl, argp,
+				new_func, dup_func, free_func);
+        }
+
+int X509_set_ex_data(X509 *r, int idx, void *arg)
+	{
+	return(CRYPTO_set_ex_data(&r->ex_data,idx,arg));
+	}
+
+void *X509_get_ex_data(X509 *r, int idx)
+	{
+	return(CRYPTO_get_ex_data(&r->ex_data,idx));
+	}
+
+/* X509_AUX ASN1 routines. X509_AUX is the name given to
+ * a certificate with extra info tagged on the end. Since these
+ * functions set how a certificate is trusted they should only
+ * be used when the certificate comes from a reliable source
+ * such as local storage.
+ *
+ */
+
+X509 *d2i_X509_AUX(X509 **a, const unsigned char **pp, long length)
+{
+	const unsigned char *q;
+	X509 *ret;
+	/* Save start position */
+	q = *pp;
+	ret = d2i_X509(a, pp, length);
+	/* If certificate unreadable then forget it */
+	if(!ret) return NULL;
+	/* update length */
+	length -= *pp - q;
+	if(!length) return ret;
+	if(!d2i_X509_CERT_AUX(&ret->aux, pp, length)) goto err;
+	return ret;
+	err:
+	X509_free(ret);
+	return NULL;
+}
+
+int i2d_X509_AUX(X509 *a, unsigned char **pp)
+{
+	int length;
+	length = i2d_X509(a, pp);
+	if(a) length += i2d_X509_CERT_AUX(a->aux, pp);
+	return length;
+}
+
+void X509_get0_signature(ASN1_BIT_STRING **psig, X509_ALGOR **palg,
+				const X509 *x)
+	{
+	if (psig)
+		*psig = x->signature;
+	if (palg)
+		*palg = x->sig_alg;
+	}
+
+int X509_get_signature_nid(const X509 *x)
+	{
+	return OBJ_obj2nid(x->sig_alg->algorithm);
+	}
diff --git a/crypto/x509/x_x509a.c b/crypto/x509/x_x509a.c
new file mode 100644
index 0000000..e13204b
--- /dev/null
+++ b/crypto/x509/x_x509a.c
@@ -0,0 +1,177 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999. */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/evp.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+
+/* X509_CERT_AUX routines. These are used to encode additional
+ * user modifiable data about a certificate. This data is
+ * appended to the X509 encoding when the *_X509_AUX routines
+ * are used. This means that the "traditional" X509 routines
+ * will simply ignore the extra data. */
+
+static X509_CERT_AUX *aux_get(X509 *x);
+
+ASN1_SEQUENCE(X509_CERT_AUX) = {
+	ASN1_SEQUENCE_OF_OPT(X509_CERT_AUX, trust, ASN1_OBJECT),
+	ASN1_IMP_SEQUENCE_OF_OPT(X509_CERT_AUX, reject, ASN1_OBJECT, 0),
+	ASN1_OPT(X509_CERT_AUX, alias, ASN1_UTF8STRING),
+	ASN1_OPT(X509_CERT_AUX, keyid, ASN1_OCTET_STRING),
+	ASN1_IMP_SEQUENCE_OF_OPT(X509_CERT_AUX, other, X509_ALGOR, 1)
+} ASN1_SEQUENCE_END(X509_CERT_AUX)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_CERT_AUX)
+
+static X509_CERT_AUX *aux_get(X509 *x)
+{
+	if(!x) return NULL;
+	if(!x->aux && !(x->aux = X509_CERT_AUX_new())) return NULL;
+	return x->aux;
+}
+
+int X509_alias_set1(X509 *x, unsigned char *name, int len)
+{
+	X509_CERT_AUX *aux;
+	if (!name)
+		{
+		if (!x || !x->aux || !x->aux->alias)
+			return 1;
+		ASN1_UTF8STRING_free(x->aux->alias);
+		x->aux->alias = NULL;
+		return 1;
+		}
+	if(!(aux = aux_get(x))) return 0;
+	if(!aux->alias && !(aux->alias = ASN1_UTF8STRING_new())) return 0;
+	return ASN1_STRING_set(aux->alias, name, len);
+}
+
+int X509_keyid_set1(X509 *x, unsigned char *id, int len)
+{
+	X509_CERT_AUX *aux;
+	if (!id)
+		{
+		if (!x || !x->aux || !x->aux->keyid)
+			return 1;
+		ASN1_OCTET_STRING_free(x->aux->keyid);
+		x->aux->keyid = NULL;
+		return 1;
+		}
+	if(!(aux = aux_get(x))) return 0;
+	if(!aux->keyid && !(aux->keyid = ASN1_OCTET_STRING_new())) return 0;
+	return ASN1_STRING_set(aux->keyid, id, len);
+}
+
+unsigned char *X509_alias_get0(X509 *x, int *len)
+{
+	if(!x->aux || !x->aux->alias) return NULL;
+	if(len) *len = x->aux->alias->length;
+	return x->aux->alias->data;
+}
+
+unsigned char *X509_keyid_get0(X509 *x, int *len)
+{
+	if(!x->aux || !x->aux->keyid) return NULL;
+	if(len) *len = x->aux->keyid->length;
+	return x->aux->keyid->data;
+}
+
+int X509_add1_trust_object(X509 *x, ASN1_OBJECT *obj)
+{
+	X509_CERT_AUX *aux;
+	ASN1_OBJECT *objtmp;
+	if(!(objtmp = OBJ_dup(obj))) return 0;
+	if(!(aux = aux_get(x))) return 0;
+	if(!aux->trust
+		&& !(aux->trust = sk_ASN1_OBJECT_new_null())) return 0;
+	return sk_ASN1_OBJECT_push(aux->trust, objtmp);
+}
+
+int X509_add1_reject_object(X509 *x, ASN1_OBJECT *obj)
+{
+	X509_CERT_AUX *aux;
+	ASN1_OBJECT *objtmp;
+	if(!(objtmp = OBJ_dup(obj))) return 0;
+	if(!(aux = aux_get(x))) return 0;
+	if(!aux->reject
+		&& !(aux->reject = sk_ASN1_OBJECT_new_null())) return 0;
+	return sk_ASN1_OBJECT_push(aux->reject, objtmp);
+}
+
+void X509_trust_clear(X509 *x)
+{
+	if(x->aux && x->aux->trust) {
+		sk_ASN1_OBJECT_pop_free(x->aux->trust, ASN1_OBJECT_free);
+		x->aux->trust = NULL;
+	}
+}
+
+void X509_reject_clear(X509 *x)
+{
+	if(x->aux && x->aux->reject) {
+		sk_ASN1_OBJECT_pop_free(x->aux->reject, ASN1_OBJECT_free);
+		x->aux->reject = NULL;
+	}
+}
+
+ASN1_SEQUENCE(X509_CERT_PAIR) = {
+	ASN1_EXP_OPT(X509_CERT_PAIR, forward, X509, 0),
+	ASN1_EXP_OPT(X509_CERT_PAIR, reverse, X509, 1)
+} ASN1_SEQUENCE_END(X509_CERT_PAIR)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_CERT_PAIR)
diff --git a/crypto/x509v3/CMakeLists.txt b/crypto/x509v3/CMakeLists.txt
new file mode 100644
index 0000000..8a76259
--- /dev/null
+++ b/crypto/x509v3/CMakeLists.txt
@@ -0,0 +1,60 @@
+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_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
+	x509v3_error.c
+)
+
+add_executable(
+	v3name_test
+
+	v3nametest.c
+)
+
+add_executable(
+	tab_test
+
+	tabtest.c
+)
+
+target_link_libraries(v3name_test crypto)
+target_link_libraries(tab_test crypto)
diff --git a/crypto/x509v3/ext_dat.h b/crypto/x509v3/ext_dat.h
new file mode 100644
index 0000000..e7d0052
--- /dev/null
+++ b/crypto/x509v3/ext_dat.h
@@ -0,0 +1,129 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+/* This file contains a table of "standard" extensions */
+
+extern X509V3_EXT_METHOD v3_bcons, v3_nscert, v3_key_usage, v3_ext_ku;
+extern X509V3_EXT_METHOD v3_pkey_usage_period, v3_sxnet, v3_info, v3_sinfo;
+extern X509V3_EXT_METHOD v3_ns_ia5_list[], v3_alt[], v3_skey_id, v3_akey_id;
+extern X509V3_EXT_METHOD v3_crl_num, v3_crl_reason, v3_crl_invdate;
+extern X509V3_EXT_METHOD v3_delta_crl, v3_cpols, v3_crld, v3_freshest_crl;
+extern X509V3_EXT_METHOD v3_ocsp_nonce, v3_ocsp_accresp, v3_ocsp_acutoff;
+extern X509V3_EXT_METHOD v3_ocsp_crlid, v3_ocsp_nocheck, v3_ocsp_serviceloc;
+extern X509V3_EXT_METHOD v3_crl_hold, v3_pci;
+extern X509V3_EXT_METHOD v3_policy_mappings, v3_policy_constraints;
+extern X509V3_EXT_METHOD v3_name_constraints, v3_inhibit_anyp, v3_idp;
+extern X509V3_EXT_METHOD v3_addr, v3_asid;
+
+/* This table will be searched using OBJ_bsearch so it *must* kept in
+ * order of the ext_nid values.
+ */
+
+/* TODO(fork): OCSP support */
+#define OPENSSL_NO_OCSP
+
+static const X509V3_EXT_METHOD *standard_exts[] = {
+&v3_nscert,
+&v3_ns_ia5_list[0],
+&v3_ns_ia5_list[1],
+&v3_ns_ia5_list[2],
+&v3_ns_ia5_list[3],
+&v3_ns_ia5_list[4],
+&v3_ns_ia5_list[5],
+&v3_ns_ia5_list[6],
+&v3_skey_id,
+&v3_key_usage,
+&v3_pkey_usage_period,
+&v3_alt[0],
+&v3_alt[1],
+&v3_bcons,
+&v3_crl_num,
+&v3_cpols,
+&v3_akey_id,
+&v3_crld,
+&v3_ext_ku,
+&v3_delta_crl,
+&v3_crl_reason,
+#ifndef OPENSSL_NO_OCSP
+&v3_crl_invdate,
+#endif
+&v3_sxnet,
+&v3_info,
+#ifndef OPENSSL_NO_OCSP
+&v3_ocsp_nonce,
+&v3_ocsp_crlid,
+&v3_ocsp_accresp,
+&v3_ocsp_nocheck,
+&v3_ocsp_acutoff,
+&v3_ocsp_serviceloc,
+#endif
+&v3_sinfo,
+&v3_policy_constraints,
+#ifndef OPENSSL_NO_OCSP
+&v3_crl_hold,
+#endif
+&v3_pci,
+&v3_name_constraints,
+&v3_policy_mappings,
+&v3_inhibit_anyp,
+&v3_idp,
+&v3_alt[2],
+&v3_freshest_crl,
+};
+
+/* Number of standard extensions */
+
+#define STANDARD_EXTENSION_COUNT (sizeof(standard_exts)/sizeof(X509V3_EXT_METHOD *))
+
diff --git a/crypto/x509v3/pcy_cache.c b/crypto/x509v3/pcy_cache.c
new file mode 100644
index 0000000..5d59c00
--- /dev/null
+++ b/crypto/x509v3/pcy_cache.c
@@ -0,0 +1,286 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/thread.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+
+static int policy_data_cmp(const X509_POLICY_DATA **a,
+				const X509_POLICY_DATA **b);
+static int policy_cache_set_int(long *out, ASN1_INTEGER *value);
+
+/* Set cache entry according to CertificatePolicies extension.
+ * Note: this destroys the passed CERTIFICATEPOLICIES structure.
+ */
+
+static int policy_cache_create(X509 *x,
+			CERTIFICATEPOLICIES *policies, int crit)
+	{
+	size_t i;
+	int ret = 0;
+	X509_POLICY_CACHE *cache = x->policy_cache;
+	X509_POLICY_DATA *data = NULL;
+	POLICYINFO *policy;
+	if (sk_POLICYINFO_num(policies) == 0)
+		goto bad_policy;
+	cache->data = sk_X509_POLICY_DATA_new(policy_data_cmp);
+	if (!cache->data)
+		goto bad_policy;
+	for (i = 0; i < sk_POLICYINFO_num(policies); i++)
+		{
+		policy = sk_POLICYINFO_value(policies, i);
+		data = policy_data_new(policy, NULL, crit);
+		if (!data)
+			goto bad_policy;
+		/* Duplicate policy OIDs are illegal: reject if matches
+		 * found.
+		 */
+		if (OBJ_obj2nid(data->valid_policy) == NID_any_policy)
+			{
+			if (cache->anyPolicy)
+				{
+				ret = -1;
+				goto bad_policy;
+				}
+			cache->anyPolicy = data;
+			}
+		else if (sk_X509_POLICY_DATA_find(cache->data, NULL, data))
+			{
+			ret = -1;
+			goto bad_policy;
+			}
+		else if (!sk_X509_POLICY_DATA_push(cache->data, data))
+			goto bad_policy;
+		data = NULL;
+		}
+	ret = 1;
+	bad_policy:
+	if (ret == -1)
+		x->ex_flags |= EXFLAG_INVALID_POLICY;
+	if (data)
+		policy_data_free(data);
+	sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
+	if (ret <= 0)
+		{
+		sk_X509_POLICY_DATA_pop_free(cache->data, policy_data_free);
+		cache->data = NULL;
+		}
+	return ret;
+	}
+
+	
+static int policy_cache_new(X509 *x)
+	{
+	X509_POLICY_CACHE *cache;
+	ASN1_INTEGER *ext_any = NULL;
+	POLICY_CONSTRAINTS *ext_pcons = NULL;
+	CERTIFICATEPOLICIES *ext_cpols = NULL;
+	POLICY_MAPPINGS *ext_pmaps = NULL;
+	int i;
+	cache = OPENSSL_malloc(sizeof(X509_POLICY_CACHE));
+	if (!cache)
+		return 0;
+	cache->anyPolicy = NULL;
+	cache->data = NULL;
+	cache->any_skip = -1;
+	cache->explicit_skip = -1;
+	cache->map_skip = -1;
+
+	x->policy_cache = cache;
+
+	/* Handle requireExplicitPolicy *first*. Need to process this
+	 * even if we don't have any policies.
+	 */
+	ext_pcons = X509_get_ext_d2i(x, NID_policy_constraints, &i, NULL);
+
+	if (!ext_pcons)
+		{
+		if (i != -1)
+			goto bad_cache;
+		}
+	else
+		{
+		if (!ext_pcons->requireExplicitPolicy
+			&& !ext_pcons->inhibitPolicyMapping)
+			goto bad_cache;
+		if (!policy_cache_set_int(&cache->explicit_skip,
+			ext_pcons->requireExplicitPolicy))
+			goto bad_cache;
+		if (!policy_cache_set_int(&cache->map_skip,
+			ext_pcons->inhibitPolicyMapping))
+			goto bad_cache;
+		}
+
+	/* Process CertificatePolicies */
+
+	ext_cpols = X509_get_ext_d2i(x, NID_certificate_policies, &i, NULL);
+	/* If no CertificatePolicies extension or problem decoding then
+	 * there is no point continuing because the valid policies will be
+	 * NULL.
+	 */
+	if (!ext_cpols)
+		{
+		/* If not absent some problem with extension */
+		if (i != -1)
+			goto bad_cache;
+		return 1;
+		}
+
+	i = policy_cache_create(x, ext_cpols, i);
+
+	/* NB: ext_cpols freed by policy_cache_set_policies */
+
+	if (i <= 0)
+		return i;
+
+	ext_pmaps = X509_get_ext_d2i(x, NID_policy_mappings, &i, NULL);
+
+	if (!ext_pmaps)
+		{
+		/* If not absent some problem with extension */
+		if (i != -1)
+			goto bad_cache;
+		}
+	else
+		{
+		i = policy_cache_set_mapping(x, ext_pmaps);
+		if (i <= 0)
+			goto bad_cache;
+		}
+
+	ext_any = X509_get_ext_d2i(x, NID_inhibit_any_policy, &i, NULL);
+
+	if (!ext_any)
+		{
+		if (i != -1)
+			goto bad_cache;
+		}
+	else if (!policy_cache_set_int(&cache->any_skip, ext_any))
+			goto bad_cache;
+
+	if (0)
+		{
+		bad_cache:
+		x->ex_flags |= EXFLAG_INVALID_POLICY;
+		}
+
+	if(ext_pcons)
+		POLICY_CONSTRAINTS_free(ext_pcons);
+
+	if (ext_any)
+		ASN1_INTEGER_free(ext_any);
+
+	return 1;
+
+	
+}
+
+void policy_cache_free(X509_POLICY_CACHE *cache)
+	{
+	if (!cache)
+		return;
+	if (cache->anyPolicy)
+		policy_data_free(cache->anyPolicy);
+	if (cache->data)
+		sk_X509_POLICY_DATA_pop_free(cache->data, policy_data_free);
+	OPENSSL_free(cache);
+	}
+
+const X509_POLICY_CACHE *policy_cache_set(X509 *x)
+	{
+
+	if (x->policy_cache == NULL)
+		{
+		CRYPTO_w_lock(CRYPTO_LOCK_X509);
+			policy_cache_new(x);
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+		}
+
+	return x->policy_cache;
+
+	}
+
+X509_POLICY_DATA *policy_cache_find_data(const X509_POLICY_CACHE *cache,
+						const ASN1_OBJECT *id)
+	{
+	size_t idx;
+	X509_POLICY_DATA tmp;
+
+	tmp.valid_policy = (ASN1_OBJECT *)id;
+	if (!sk_X509_POLICY_DATA_find(cache->data, &idx, &tmp))
+		return NULL;
+	return sk_X509_POLICY_DATA_value(cache->data, idx);
+	}
+
+static int policy_data_cmp(const X509_POLICY_DATA **a,
+				const X509_POLICY_DATA **b)
+	{
+	return OBJ_cmp((*a)->valid_policy, (*b)->valid_policy);
+	}
+
+static int policy_cache_set_int(long *out, ASN1_INTEGER *value)
+	{
+	if (value == NULL)
+		return 1;
+	if (value->type == V_ASN1_NEG_INTEGER)
+		return 0;
+	*out = ASN1_INTEGER_get(value);
+	return 1;
+	}
diff --git a/crypto/x509v3/pcy_data.c b/crypto/x509v3/pcy_data.c
new file mode 100644
index 0000000..cd45dca
--- /dev/null
+++ b/crypto/x509v3/pcy_data.c
@@ -0,0 +1,137 @@
+/* pcy_data.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+
+/* Policy Node routines */
+
+void policy_data_free(X509_POLICY_DATA *data)
+	{
+	ASN1_OBJECT_free(data->valid_policy);
+	/* Don't free qualifiers if shared */
+	if (!(data->flags & POLICY_DATA_FLAG_SHARED_QUALIFIERS))
+		sk_POLICYQUALINFO_pop_free(data->qualifier_set,
+					POLICYQUALINFO_free);
+	sk_ASN1_OBJECT_pop_free(data->expected_policy_set, ASN1_OBJECT_free);
+	OPENSSL_free(data);
+	}
+
+/* Create a data based on an existing policy. If 'id' is NULL use the
+ * oid in the policy, otherwise use 'id'. This behaviour covers the two
+ * types of data in RFC3280: data with from a CertificatePolcies extension
+ * and additional data with just the qualifiers of anyPolicy and ID from
+ * another source.
+ */
+
+X509_POLICY_DATA *policy_data_new(POLICYINFO *policy,
+					const ASN1_OBJECT *cid, int crit)
+	{
+	X509_POLICY_DATA *ret;
+	ASN1_OBJECT *id;
+	if (!policy && !cid)
+		return NULL;
+	if (cid)
+		{
+		id = OBJ_dup(cid);
+		if (!id)
+			return NULL;
+		}
+	else
+		id = NULL;
+	ret = OPENSSL_malloc(sizeof(X509_POLICY_DATA));
+	if (!ret)
+		return NULL;
+	ret->expected_policy_set = sk_ASN1_OBJECT_new_null();
+	if (!ret->expected_policy_set)
+		{
+		OPENSSL_free(ret);
+		if (id)
+			ASN1_OBJECT_free(id);
+		return NULL;
+		}
+
+	if (crit)
+		ret->flags = POLICY_DATA_FLAG_CRITICAL;
+	else
+		ret->flags = 0;
+
+	if (id)
+		ret->valid_policy = id;
+	else
+		{
+		ret->valid_policy = policy->policyid;
+		policy->policyid = NULL;
+		}
+
+	if (policy)
+		{
+		ret->qualifier_set = policy->qualifiers;
+		policy->qualifiers = NULL;
+		}
+	else
+		ret->qualifier_set = NULL;
+
+	return ret;
+	}
+
diff --git a/crypto/x509v3/pcy_int.h b/crypto/x509v3/pcy_int.h
new file mode 100644
index 0000000..ccff928
--- /dev/null
+++ b/crypto/x509v3/pcy_int.h
@@ -0,0 +1,212 @@
+/* pcy_int.h */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+
+typedef struct X509_POLICY_DATA_st X509_POLICY_DATA;
+
+DECLARE_STACK_OF(X509_POLICY_DATA)
+
+/* Internal structures */
+
+/* This structure and the field names correspond to the Policy 'node' of
+ * RFC3280. NB this structure contains no pointers to parent or child
+ * data: X509_POLICY_NODE contains that. This means that the main policy data
+ * can be kept static and cached with the certificate.
+ */
+
+struct X509_POLICY_DATA_st
+	{
+	unsigned int flags;
+	/* Policy OID and qualifiers for this data */
+	ASN1_OBJECT *valid_policy;
+	STACK_OF(POLICYQUALINFO) *qualifier_set;
+	STACK_OF(ASN1_OBJECT) *expected_policy_set;
+	};
+
+/* X509_POLICY_DATA flags values */
+
+/* This flag indicates the structure has been mapped using a policy mapping
+ * extension. If policy mapping is not active its references get deleted. 
+ */
+
+#define POLICY_DATA_FLAG_MAPPED			0x1
+
+/* This flag indicates the data doesn't correspond to a policy in Certificate
+ * Policies: it has been mapped to any policy.
+ */
+
+#define POLICY_DATA_FLAG_MAPPED_ANY		0x2
+
+/* AND with flags to see if any mapping has occurred */
+
+#define POLICY_DATA_FLAG_MAP_MASK		0x3
+
+/* qualifiers are shared and shouldn't be freed */
+
+#define POLICY_DATA_FLAG_SHARED_QUALIFIERS	0x4
+
+/* Parent node is an extra node and should be freed */
+
+#define POLICY_DATA_FLAG_EXTRA_NODE		0x8
+
+/* Corresponding CertificatePolicies is critical */
+
+#define POLICY_DATA_FLAG_CRITICAL		0x10
+
+/* This structure is cached with a certificate */
+
+struct X509_POLICY_CACHE_st {
+	/* anyPolicy data or NULL if no anyPolicy */
+	X509_POLICY_DATA *anyPolicy;
+	/* other policy data */
+	STACK_OF(X509_POLICY_DATA) *data;
+	/* If InhibitAnyPolicy present this is its value or -1 if absent. */
+	long any_skip;
+	/* If policyConstraints and requireExplicitPolicy present this is its
+	 * value or -1 if absent.
+	 */
+	long explicit_skip;
+	/* If policyConstraints and policyMapping present this is its
+	 * value or -1 if absent.
+         */
+	long map_skip;
+	};
+
+/*#define POLICY_CACHE_FLAG_CRITICAL		POLICY_DATA_FLAG_CRITICAL*/
+
+/* This structure represents the relationship between nodes */
+
+struct X509_POLICY_NODE_st
+	{
+	/* node data this refers to */
+	const X509_POLICY_DATA *data;
+	/* Parent node */
+	X509_POLICY_NODE *parent;
+	/* Number of child nodes */
+	int nchild;
+	};
+
+struct X509_POLICY_LEVEL_st
+	{
+	/* Cert for this level */
+	X509 *cert;
+	/* nodes at this level */
+	STACK_OF(X509_POLICY_NODE) *nodes;
+	/* anyPolicy node */
+	X509_POLICY_NODE *anyPolicy;
+	/* Extra data */
+	/*STACK_OF(X509_POLICY_DATA) *extra_data;*/
+	unsigned int flags;
+	};
+
+struct X509_POLICY_TREE_st
+	{
+	/* This is the tree 'level' data */
+	X509_POLICY_LEVEL *levels;
+	int nlevel;
+	/* Extra policy data when additional nodes (not from the certificate)
+	 * are required.
+	 */
+	STACK_OF(X509_POLICY_DATA) *extra_data;
+	/* This is the authority constained policy set */
+	STACK_OF(X509_POLICY_NODE) *auth_policies;
+	STACK_OF(X509_POLICY_NODE) *user_policies;
+	unsigned int flags;
+	};
+
+/* Set if anyPolicy present in user policies */
+#define POLICY_FLAG_ANY_POLICY		0x2
+
+/* Useful macros */
+
+#define node_data_critical(data) (data->flags & POLICY_DATA_FLAG_CRITICAL)
+#define node_critical(node) node_data_critical(node->data)
+
+/* Internal functions */
+
+X509_POLICY_DATA *policy_data_new(POLICYINFO *policy, const ASN1_OBJECT *id,
+								int crit);
+void policy_data_free(X509_POLICY_DATA *data);
+
+X509_POLICY_DATA *policy_cache_find_data(const X509_POLICY_CACHE *cache,
+							const ASN1_OBJECT *id);
+int policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps);
+
+
+STACK_OF(X509_POLICY_NODE) *policy_node_cmp_new(void);
+
+void policy_cache_init(void);
+
+void policy_cache_free(X509_POLICY_CACHE *cache);
+
+X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level,
+					const X509_POLICY_NODE *parent,	
+					const ASN1_OBJECT *id);
+
+X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk,
+						const ASN1_OBJECT *id);
+
+X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
+			const X509_POLICY_DATA *data,
+			X509_POLICY_NODE *parent,
+			X509_POLICY_TREE *tree);
+void policy_node_free(X509_POLICY_NODE *node);
+int policy_node_match(const X509_POLICY_LEVEL *lvl,
+		      const X509_POLICY_NODE *node, const ASN1_OBJECT *oid);
+
+const X509_POLICY_CACHE *policy_cache_set(X509 *x);
diff --git a/crypto/x509v3/pcy_lib.c b/crypto/x509v3/pcy_lib.c
new file mode 100644
index 0000000..16be2f0
--- /dev/null
+++ b/crypto/x509v3/pcy_lib.c
@@ -0,0 +1,165 @@
+/* pcy_lib.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+
+/* accessor functions */
+
+/* X509_POLICY_TREE stuff */
+
+int X509_policy_tree_level_count(const X509_POLICY_TREE *tree)
+	{
+	if (!tree)
+		return 0;
+	return tree->nlevel;
+	}
+
+X509_POLICY_LEVEL *
+	X509_policy_tree_get0_level(const X509_POLICY_TREE *tree, int i)
+	{
+	if (!tree || (i < 0) || (i >= tree->nlevel))
+		return NULL;
+	return tree->levels + i;
+	}
+
+STACK_OF(X509_POLICY_NODE) *
+		X509_policy_tree_get0_policies(const X509_POLICY_TREE *tree)
+	{
+	if (!tree)
+		return NULL;
+	return tree->auth_policies;
+	}
+
+STACK_OF(X509_POLICY_NODE) *
+	X509_policy_tree_get0_user_policies(const X509_POLICY_TREE *tree)
+	{
+	if (!tree)
+		return NULL;
+	if (tree->flags & POLICY_FLAG_ANY_POLICY)
+		return tree->auth_policies;
+	else
+		return tree->user_policies;
+	}
+
+/* X509_POLICY_LEVEL stuff */
+
+int X509_policy_level_node_count(X509_POLICY_LEVEL *level)
+	{
+	int n;
+	if (!level)
+		return 0;
+	if (level->anyPolicy)
+		n = 1;
+	else
+		n = 0;
+	if (level->nodes)
+		n += sk_X509_POLICY_NODE_num(level->nodes);
+	return n;
+	}
+
+X509_POLICY_NODE *X509_policy_level_get0_node(X509_POLICY_LEVEL *level, int i)
+	{
+	if (!level)
+		return NULL;
+	if (level->anyPolicy)
+		{
+		if (i == 0)
+			return level->anyPolicy;
+		i--;
+		}
+	return sk_X509_POLICY_NODE_value(level->nodes, i);
+	}
+
+/* X509_POLICY_NODE stuff */
+
+const ASN1_OBJECT *X509_policy_node_get0_policy(const X509_POLICY_NODE *node)
+	{
+	if (!node)
+		return NULL;
+	return node->data->valid_policy;
+	}
+
+#if 0
+int X509_policy_node_get_critical(const X509_POLICY_NODE *node)
+	{
+	if (node_critical(node))
+		return 1;
+	return 0;
+	}
+#endif
+
+STACK_OF(POLICYQUALINFO) *
+		X509_policy_node_get0_qualifiers(const X509_POLICY_NODE *node)
+	{
+	if (!node)
+		return NULL;
+	return node->data->qualifier_set;
+	}
+
+const X509_POLICY_NODE *
+		X509_policy_node_get0_parent(const X509_POLICY_NODE *node)
+	{
+	if (!node)
+		return NULL;
+	return node->parent;
+	}
+
+
diff --git a/crypto/x509v3/pcy_map.c b/crypto/x509v3/pcy_map.c
new file mode 100644
index 0000000..2b8307b
--- /dev/null
+++ b/crypto/x509v3/pcy_map.c
@@ -0,0 +1,133 @@
+/* pcy_map.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+
+/* Set policy mapping entries in cache.
+ * Note: this modifies the passed POLICY_MAPPINGS structure
+ */
+
+int policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps)
+	{
+	POLICY_MAPPING *map;
+	X509_POLICY_DATA *data;
+	X509_POLICY_CACHE *cache = x->policy_cache;
+	size_t i;
+	int ret = 0;
+	if (sk_POLICY_MAPPING_num(maps) == 0)
+		{
+		ret = -1;
+		goto bad_mapping;
+		}
+	for (i = 0; i < sk_POLICY_MAPPING_num(maps); i++)
+		{
+		map = sk_POLICY_MAPPING_value(maps, i);
+		/* Reject if map to or from anyPolicy */
+		if ((OBJ_obj2nid(map->subjectDomainPolicy) == NID_any_policy)
+		   || (OBJ_obj2nid(map->issuerDomainPolicy) == NID_any_policy))
+			{
+			ret = -1;
+			goto bad_mapping;
+			}
+
+		/* Attempt to find matching policy data */
+		data = policy_cache_find_data(cache, map->issuerDomainPolicy);
+		/* If we don't have anyPolicy can't map */
+		if (!data && !cache->anyPolicy)
+			continue;
+
+		/* Create a NODE from anyPolicy */
+		if (!data)
+			{
+			data = policy_data_new(NULL, map->issuerDomainPolicy,
+					cache->anyPolicy->flags
+						& POLICY_DATA_FLAG_CRITICAL);
+			if (!data)
+				goto bad_mapping;
+			data->qualifier_set = cache->anyPolicy->qualifier_set;
+			/*map->issuerDomainPolicy = NULL;*/
+			data->flags |= POLICY_DATA_FLAG_MAPPED_ANY;
+			data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
+			if (!sk_X509_POLICY_DATA_push(cache->data, data))
+				{
+				policy_data_free(data);
+				goto bad_mapping;
+				}
+			}
+		else
+			data->flags |= POLICY_DATA_FLAG_MAPPED;
+		if (!sk_ASN1_OBJECT_push(data->expected_policy_set, 
+						map->subjectDomainPolicy))
+			goto bad_mapping;
+		map->subjectDomainPolicy = NULL;
+
+		}
+
+	ret = 1;
+	bad_mapping:
+	if (ret == -1)
+		x->ex_flags |= EXFLAG_INVALID_POLICY;
+	sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free);
+	return ret;
+
+	}
diff --git a/crypto/x509v3/pcy_node.c b/crypto/x509v3/pcy_node.c
new file mode 100644
index 0000000..55cc203
--- /dev/null
+++ b/crypto/x509v3/pcy_node.c
@@ -0,0 +1,197 @@
+/* pcy_node.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/asn1.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+
+static int node_cmp(const X509_POLICY_NODE **a,
+			const X509_POLICY_NODE **b)
+	{
+	return OBJ_cmp((*a)->data->valid_policy, (*b)->data->valid_policy);
+	}
+
+STACK_OF(X509_POLICY_NODE) *policy_node_cmp_new(void)
+	{
+	return sk_X509_POLICY_NODE_new(node_cmp);
+	}
+
+X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *nodes,
+					const ASN1_OBJECT *id)
+	{
+	X509_POLICY_DATA n;
+	X509_POLICY_NODE l;
+	size_t idx;
+
+	n.valid_policy = (ASN1_OBJECT *)id;
+	l.data = &n;
+
+	if (!sk_X509_POLICY_NODE_find(nodes, &idx, &l))
+		return NULL;
+
+	return sk_X509_POLICY_NODE_value(nodes, idx);
+
+	}
+
+X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level,
+					const X509_POLICY_NODE *parent,	
+					const ASN1_OBJECT *id)
+	{
+	X509_POLICY_NODE *node;
+	size_t i;
+	for (i = 0; i < sk_X509_POLICY_NODE_num(level->nodes); i++)
+		{
+		node = sk_X509_POLICY_NODE_value(level->nodes, i);
+		if (node->parent == parent)
+			{
+			if (!OBJ_cmp(node->data->valid_policy, id))
+				return node;
+			}
+		}
+	return NULL;
+	}
+
+X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
+			const X509_POLICY_DATA *data,
+			X509_POLICY_NODE *parent,
+			X509_POLICY_TREE *tree)
+	{
+	X509_POLICY_NODE *node;
+	node = OPENSSL_malloc(sizeof(X509_POLICY_NODE));
+	if (!node)
+		return NULL;
+	node->data = data;
+	node->parent = parent;
+	node->nchild = 0;
+	if (level)
+		{
+		if (OBJ_obj2nid(data->valid_policy) == NID_any_policy)
+			{
+			if (level->anyPolicy)
+				goto node_error;
+			level->anyPolicy = node;
+			}
+		else
+			{
+
+			if (!level->nodes)
+				level->nodes = policy_node_cmp_new();
+			if (!level->nodes)
+				goto node_error;
+			if (!sk_X509_POLICY_NODE_push(level->nodes, node))
+				goto node_error;
+			}
+		}
+
+	if (tree)
+		{
+		if (!tree->extra_data)
+			 tree->extra_data = sk_X509_POLICY_DATA_new_null();
+		if (!tree->extra_data)
+			goto node_error;
+		if (!sk_X509_POLICY_DATA_push(tree->extra_data, data))
+			goto node_error;
+		}
+
+	if (parent)
+		parent->nchild++;
+
+	return node;
+
+	node_error:
+	policy_node_free(node);
+	return 0;
+
+	}
+
+void policy_node_free(X509_POLICY_NODE *node)
+	{
+	OPENSSL_free(node);
+	}
+
+/* See if a policy node matches a policy OID. If mapping enabled look through
+ * expected policy set otherwise just valid policy.
+ */
+
+int policy_node_match(const X509_POLICY_LEVEL *lvl,
+		      const X509_POLICY_NODE *node, const ASN1_OBJECT *oid)
+	{
+	size_t i;
+	ASN1_OBJECT *policy_oid;
+	const X509_POLICY_DATA *x = node->data;
+
+	if (	    (lvl->flags & X509_V_FLAG_INHIBIT_MAP)
+		|| !(x->flags & POLICY_DATA_FLAG_MAP_MASK))
+		{
+		if (!OBJ_cmp(x->valid_policy, oid))
+			return 1;
+		return 0;
+		}
+
+	for (i = 0; i < sk_ASN1_OBJECT_num(x->expected_policy_set); i++)
+		{
+		policy_oid = sk_ASN1_OBJECT_value(x->expected_policy_set, i);
+		if (!OBJ_cmp(policy_oid, oid))
+			return 1;
+		}
+	return 0;
+
+	}
diff --git a/crypto/x509v3/pcy_tree.c b/crypto/x509v3/pcy_tree.c
new file mode 100644
index 0000000..dbaa498
--- /dev/null
+++ b/crypto/x509v3/pcy_tree.c
@@ -0,0 +1,877 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2004.
+ */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/thread.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+
+/* Enable this to print out the complete policy tree at various point during
+ * evaluation.
+ */
+
+/*#define OPENSSL_POLICY_DEBUG*/
+
+#ifdef OPENSSL_POLICY_DEBUG
+
+static void expected_print(BIO *err, X509_POLICY_LEVEL *lev,
+				X509_POLICY_NODE *node, int indent)
+	{
+	if (	    (lev->flags & X509_V_FLAG_INHIBIT_MAP)
+		|| !(node->data->flags & POLICY_DATA_FLAG_MAP_MASK))
+		BIO_puts(err, "  Not Mapped\n");
+	else
+		{
+		int i;
+		STACK_OF(ASN1_OBJECT) *pset = node->data->expected_policy_set;
+		ASN1_OBJECT *oid;
+		BIO_puts(err, "  Expected: ");
+		for (i = 0; i < sk_ASN1_OBJECT_num(pset); i++)
+			{
+			oid = sk_ASN1_OBJECT_value(pset, i);
+			if (i)
+				BIO_puts(err, ", ");
+			i2a_ASN1_OBJECT(err, oid);
+			}
+		BIO_puts(err, "\n");
+		}
+	}
+
+static void tree_print(char *str, X509_POLICY_TREE *tree,
+			X509_POLICY_LEVEL *curr)
+	{
+	X509_POLICY_LEVEL *plev;
+	X509_POLICY_NODE *node;
+	int i;
+	BIO *err;
+	err = BIO_new_fp(stderr, BIO_NOCLOSE);
+	if (!curr)
+		curr = tree->levels + tree->nlevel;
+	else
+		curr++;
+	BIO_printf(err, "Level print after %s\n", str);
+	BIO_printf(err, "Printing Up to Level %ld\n", curr - tree->levels);
+	for (plev = tree->levels; plev != curr; plev++)
+		{
+		BIO_printf(err, "Level %ld, flags = %x\n",
+				plev - tree->levels, plev->flags);
+		for (i = 0; i < sk_X509_POLICY_NODE_num(plev->nodes); i++)
+			{
+			node = sk_X509_POLICY_NODE_value(plev->nodes, i);
+			X509_POLICY_NODE_print(err, node, 2);
+			expected_print(err, plev, node, 2);
+			BIO_printf(err, "  Flags: %x\n", node->data->flags);
+			}
+		if (plev->anyPolicy)
+			X509_POLICY_NODE_print(err, plev->anyPolicy, 2);
+		}
+
+	BIO_free(err);
+
+	}
+#else
+
+#define tree_print(a,b,c) /* */
+
+#endif
+
+/* Initialize policy tree. Return values:
+ *  0 Some internal error occured.
+ * -1 Inconsistent or invalid extensions in certificates.
+ *  1 Tree initialized OK.
+ *  2 Policy tree is empty.
+ *  5 Tree OK and requireExplicitPolicy true.
+ *  6 Tree empty and requireExplicitPolicy true.
+ */
+
+static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
+			unsigned int flags)
+	{
+	X509_POLICY_TREE *tree;
+	X509_POLICY_LEVEL *level;
+	const X509_POLICY_CACHE *cache;
+	X509_POLICY_DATA *data = NULL;
+	X509 *x;
+	int ret = 1;
+	int i, n;
+	int explicit_policy;
+	int any_skip;
+	int map_skip;
+	*ptree = NULL;
+	n = sk_X509_num(certs);
+
+#if 0
+	/* Disable policy mapping for now... */
+	flags |= X509_V_FLAG_INHIBIT_MAP;
+#endif
+
+	if (flags & X509_V_FLAG_EXPLICIT_POLICY)
+		explicit_policy = 0;
+	else
+		explicit_policy = n + 1;
+
+	if (flags & X509_V_FLAG_INHIBIT_ANY)
+		any_skip = 0;
+	else
+		any_skip = n + 1;
+
+	if (flags & X509_V_FLAG_INHIBIT_MAP)
+		map_skip = 0;
+	else
+		map_skip = n + 1;
+
+	/* Can't do anything with just a trust anchor */
+	if (n == 1)
+		return 1;
+	/* First setup policy cache in all certificates apart from the
+	 * trust anchor. Note any bad cache results on the way. Also can
+	 * calculate explicit_policy value at this point.
+	 */
+	for (i = n - 2; i >= 0; i--)
+		{
+		x = sk_X509_value(certs, i);
+		X509_check_purpose(x, -1, -1);
+		cache = policy_cache_set(x);
+		/* If cache NULL something bad happened: return immediately */
+		if (cache == NULL)
+			return 0;
+		/* If inconsistent extensions keep a note of it but continue */
+		if (x->ex_flags & EXFLAG_INVALID_POLICY)
+			ret = -1;
+		/* Otherwise if we have no data (hence no CertificatePolicies)
+		 * and haven't already set an inconsistent code note it.
+		 */
+		else if ((ret == 1) && !cache->data)
+			ret = 2;
+		if (explicit_policy > 0)
+			{
+			if (!(x->ex_flags & EXFLAG_SI))
+				explicit_policy--;
+			if ((cache->explicit_skip != -1)
+				&& (cache->explicit_skip < explicit_policy))
+				explicit_policy = cache->explicit_skip;
+			}
+		}
+
+	if (ret != 1)
+		{
+		if (ret == 2 && !explicit_policy)
+			return 6;
+		return ret;
+		}
+
+
+	/* If we get this far initialize the tree */
+
+	tree = OPENSSL_malloc(sizeof(X509_POLICY_TREE));
+
+	if (!tree)
+		return 0;
+
+	tree->flags = 0;
+	tree->levels = OPENSSL_malloc(sizeof(X509_POLICY_LEVEL) * n);
+	tree->nlevel = 0;
+	tree->extra_data = NULL;
+	tree->auth_policies = NULL;
+	tree->user_policies = NULL;
+
+	if (!tree->levels)
+		{
+		OPENSSL_free(tree);
+		return 0;
+		}
+
+	memset(tree->levels, 0, n * sizeof(X509_POLICY_LEVEL));
+
+	tree->nlevel = n;
+
+	level = tree->levels;
+
+	/* Root data: initialize to anyPolicy */
+
+	data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0);
+
+	if (!data || !level_add_node(level, data, NULL, tree))
+		goto bad_tree;
+
+	for (i = n - 2; i >= 0; i--)
+		{
+		level++;
+		x = sk_X509_value(certs, i);
+		cache = policy_cache_set(x);
+		CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+		level->cert = x;
+
+		if (!cache->anyPolicy)
+				level->flags |= X509_V_FLAG_INHIBIT_ANY;
+
+		/* Determine inhibit any and inhibit map flags */
+		if (any_skip == 0)
+			{
+			/* Any matching allowed if certificate is self
+			 * issued and not the last in the chain.
+			 */
+			if (!(x->ex_flags & EXFLAG_SI) || (i == 0))
+				level->flags |= X509_V_FLAG_INHIBIT_ANY;
+			}
+		else
+			{
+			if (!(x->ex_flags & EXFLAG_SI))
+				any_skip--;
+			if ((cache->any_skip >= 0)
+				&& (cache->any_skip < any_skip))
+				any_skip = cache->any_skip;
+			}
+
+		if (map_skip == 0)
+			level->flags |= X509_V_FLAG_INHIBIT_MAP;
+		else
+			{
+			if (!(x->ex_flags & EXFLAG_SI))
+				map_skip--;
+			if ((cache->map_skip >= 0)
+				&& (cache->map_skip < map_skip))
+				map_skip = cache->map_skip;
+			}
+
+		}
+
+	*ptree = tree;
+
+	if (explicit_policy)
+		return 1;
+	else
+		return 5;
+
+	bad_tree:
+
+	X509_policy_tree_free(tree);
+
+	return 0;
+
+	}
+
+static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
+				const X509_POLICY_DATA *data)
+	{
+	X509_POLICY_LEVEL *last = curr - 1;
+	X509_POLICY_NODE *node;
+	int matched = 0;
+	size_t i;
+	/* Iterate through all in nodes linking matches */
+	for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++)
+		{
+		node = sk_X509_POLICY_NODE_value(last->nodes, i);
+		if (policy_node_match(last, node, data->valid_policy))
+			{
+			if (!level_add_node(curr, data, node, NULL))
+				return 0;
+			matched = 1;
+			}
+		}
+	if (!matched && last->anyPolicy)
+		{
+		if (!level_add_node(curr, data, last->anyPolicy, NULL))
+			return 0;
+		}
+	return 1;
+	}
+
+/* This corresponds to RFC3280 6.1.3(d)(1):
+ * link any data from CertificatePolicies onto matching parent
+ * or anyPolicy if no match.
+ */
+
+static int tree_link_nodes(X509_POLICY_LEVEL *curr,
+				const X509_POLICY_CACHE *cache)
+	{
+	size_t i;
+	X509_POLICY_DATA *data;
+
+	for (i = 0; i < sk_X509_POLICY_DATA_num(cache->data); i++)
+		{
+		data = sk_X509_POLICY_DATA_value(cache->data, i);
+		/* If a node is mapped any it doesn't have a corresponding
+		 * CertificatePolicies entry. 
+		 * However such an identical node would be created
+		 * if anyPolicy matching is enabled because there would be
+		 * no match with the parent valid_policy_set. So we create
+		 * link because then it will have the mapping flags
+		 * right and we can prune it later.
+		 */
+#if 0
+		if ((data->flags & POLICY_DATA_FLAG_MAPPED_ANY)
+			&& !(curr->flags & X509_V_FLAG_INHIBIT_ANY))
+			continue;
+#endif
+		/* Look for matching nodes in previous level */
+		if (!tree_link_matching_nodes(curr, data))
+				return 0;
+		}
+	return 1;
+	}
+
+/* This corresponds to RFC3280 6.1.3(d)(2):
+ * Create new data for any unmatched policies in the parent and link
+ * to anyPolicy.
+ */
+
+static int tree_add_unmatched(X509_POLICY_LEVEL *curr,
+			const X509_POLICY_CACHE *cache,
+			const ASN1_OBJECT *id,
+			X509_POLICY_NODE *node,
+			X509_POLICY_TREE *tree)
+	{
+	X509_POLICY_DATA *data;
+	if (id == NULL)
+		id = node->data->valid_policy;
+	/* Create a new node with qualifiers from anyPolicy and
+	 * id from unmatched node.
+	 */
+	data = policy_data_new(NULL, id, node_critical(node));
+
+	if (data == NULL)
+		return 0;
+	/* Curr may not have anyPolicy */
+	data->qualifier_set = cache->anyPolicy->qualifier_set;
+	data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
+	if (!level_add_node(curr, data, node, tree))
+		{
+		policy_data_free(data);
+		return 0;
+		}
+
+	return 1;
+	}
+
+static int tree_link_unmatched(X509_POLICY_LEVEL *curr,
+			const X509_POLICY_CACHE *cache,
+			X509_POLICY_NODE *node,
+			X509_POLICY_TREE *tree)
+	{
+	const X509_POLICY_LEVEL *last = curr - 1;
+	size_t i;
+
+	if (	    (last->flags & X509_V_FLAG_INHIBIT_MAP)
+		|| !(node->data->flags & POLICY_DATA_FLAG_MAPPED))
+		{
+		/* If no policy mapping: matched if one child present */
+		if (node->nchild)
+			return 1;
+		if (!tree_add_unmatched(curr, cache, NULL, node, tree))
+			return 0;
+		/* Add it */
+		}
+	else
+		{
+		/* If mapping: matched if one child per expected policy set */
+		STACK_OF(ASN1_OBJECT) *expset = node->data->expected_policy_set;
+		if (node->nchild == sk_ASN1_OBJECT_num(expset))
+			return 1;
+		/* Locate unmatched nodes */
+		for (i = 0; i < sk_ASN1_OBJECT_num(expset); i++)
+			{
+			ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(expset, i);
+			if (level_find_node(curr, node, oid))
+				continue;
+			if (!tree_add_unmatched(curr, cache, oid, node, tree))
+				return 0;
+			}
+
+		}
+
+	return 1;
+
+	}
+
+static int tree_link_any(X509_POLICY_LEVEL *curr,
+			const X509_POLICY_CACHE *cache,
+			X509_POLICY_TREE *tree)
+	{
+	size_t i;
+	/*X509_POLICY_DATA *data;*/
+	X509_POLICY_NODE *node;
+	X509_POLICY_LEVEL *last = curr - 1;
+
+	for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++)
+		{
+		node = sk_X509_POLICY_NODE_value(last->nodes, i);
+
+		if (!tree_link_unmatched(curr, cache, node, tree))
+			return 0;
+
+#if 0
+
+		/* Skip any node with any children: we only want unmathced
+		 * nodes.
+		 *
+		 * Note: need something better for policy mapping
+		 * because each node may have multiple children 
+		 */
+		if (node->nchild)
+			continue;
+
+		/* Create a new node with qualifiers from anyPolicy and
+		 * id from unmatched node.
+		 */
+		data = policy_data_new(NULL, node->data->valid_policy, 
+						node_critical(node));
+
+		if (data == NULL)
+			return 0;
+		/* Curr may not have anyPolicy */
+		data->qualifier_set = cache->anyPolicy->qualifier_set;
+		data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
+		if (!level_add_node(curr, data, node, tree))
+			{
+			policy_data_free(data);
+			return 0;
+			}
+
+#endif
+
+		}
+	/* Finally add link to anyPolicy */
+	if (last->anyPolicy)
+		{
+		if (!level_add_node(curr, cache->anyPolicy,
+						last->anyPolicy, NULL))
+			return 0;
+		}
+	return 1;
+	}
+
+/* Prune the tree: delete any child mapped child data on the current level
+ * then proceed up the tree deleting any data with no children. If we ever
+ * have no data on a level we can halt because the tree will be empty.
+ */
+
+static int tree_prune(X509_POLICY_TREE *tree, X509_POLICY_LEVEL *curr)
+	{
+	STACK_OF(X509_POLICY_NODE) *nodes;
+	X509_POLICY_NODE *node;
+	int i;
+	nodes = curr->nodes;
+	if (curr->flags & X509_V_FLAG_INHIBIT_MAP)
+		{
+		for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--)
+			{
+			node = sk_X509_POLICY_NODE_value(nodes, i);
+			/* Delete any mapped data: see RFC3280 XXXX */
+			if (node->data->flags & POLICY_DATA_FLAG_MAP_MASK)
+				{
+				node->parent->nchild--;
+				OPENSSL_free(node);
+				(void)sk_X509_POLICY_NODE_delete(nodes,i);
+				}
+			}
+		}
+
+	for(;;)	{
+		--curr;
+		nodes = curr->nodes;
+		for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--)
+			{
+			node = sk_X509_POLICY_NODE_value(nodes, i);
+			if (node->nchild == 0)
+				{
+				node->parent->nchild--;
+				OPENSSL_free(node);
+				(void)sk_X509_POLICY_NODE_delete(nodes, i);
+				}
+			}
+		if (curr->anyPolicy && !curr->anyPolicy->nchild)
+			{
+			if (curr->anyPolicy->parent)
+				curr->anyPolicy->parent->nchild--;
+			OPENSSL_free(curr->anyPolicy);
+			curr->anyPolicy = NULL;
+			}
+		if (curr == tree->levels)
+			{
+			/* If we zapped anyPolicy at top then tree is empty */
+			if (!curr->anyPolicy)
+					return 2;
+			return 1;
+			}
+		}
+
+	return 1;
+
+	}
+
+static int tree_add_auth_node(STACK_OF(X509_POLICY_NODE) **pnodes,
+						 X509_POLICY_NODE *pcy)
+	{
+	if (!*pnodes)
+		{
+		*pnodes = policy_node_cmp_new();
+		if (!*pnodes)
+			return 0;
+		}
+	else if (sk_X509_POLICY_NODE_find(*pnodes, NULL, pcy))
+		return 1;
+
+	if (!sk_X509_POLICY_NODE_push(*pnodes, pcy))
+		return 0;
+
+	return 1;
+
+	}
+
+/* Calculate the authority set based on policy tree.
+ * The 'pnodes' parameter is used as a store for the set of policy nodes
+ * used to calculate the user set. If the authority set is not anyPolicy
+ * then pnodes will just point to the authority set. If however the authority
+ * set is anyPolicy then the set of valid policies (other than anyPolicy)
+ * is store in pnodes. The return value of '2' is used in this case to indicate
+ * that pnodes should be freed.
+ */
+
+static int tree_calculate_authority_set(X509_POLICY_TREE *tree,
+					STACK_OF(X509_POLICY_NODE) **pnodes)
+	{
+	X509_POLICY_LEVEL *curr;
+	X509_POLICY_NODE *node, *anyptr;
+	STACK_OF(X509_POLICY_NODE) **addnodes;
+	int i;
+	size_t j;
+	curr = tree->levels + tree->nlevel - 1;
+
+	/* If last level contains anyPolicy set is anyPolicy */
+	if (curr->anyPolicy)
+		{
+		if (!tree_add_auth_node(&tree->auth_policies, curr->anyPolicy))
+			return 0;
+		addnodes = pnodes;
+		}
+	else
+		/* Add policies to authority set */
+		addnodes = &tree->auth_policies;
+
+	curr = tree->levels;
+	for (i = 1; i < tree->nlevel; i++)
+		{
+		/* If no anyPolicy node on this this level it can't
+		 * appear on lower levels so end search.
+		 */
+		if (!(anyptr = curr->anyPolicy))
+			break;
+		curr++;
+		for (j = 0; j < sk_X509_POLICY_NODE_num(curr->nodes); j++)
+			{
+			node = sk_X509_POLICY_NODE_value(curr->nodes, j);
+			if ((node->parent == anyptr)
+				&& !tree_add_auth_node(addnodes, node))
+					return 0;
+			}
+		}
+
+	if (addnodes == pnodes)
+		return 2;
+
+	*pnodes = tree->auth_policies;
+
+	return 1;
+	}
+
+static int tree_calculate_user_set(X509_POLICY_TREE *tree,
+				STACK_OF(ASN1_OBJECT) *policy_oids,
+				STACK_OF(X509_POLICY_NODE) *auth_nodes)
+	{
+	size_t i;
+	X509_POLICY_NODE *node;
+	ASN1_OBJECT *oid;
+
+	X509_POLICY_NODE *anyPolicy;
+	X509_POLICY_DATA *extra;
+
+	/* Check if anyPolicy present in authority constrained policy set:
+	 * this will happen if it is a leaf node.
+	 */
+
+	if (sk_ASN1_OBJECT_num(policy_oids) <= 0)
+		return 1;
+
+	anyPolicy = tree->levels[tree->nlevel - 1].anyPolicy;
+
+	for (i = 0; i < sk_ASN1_OBJECT_num(policy_oids); i++)
+		{
+		oid = sk_ASN1_OBJECT_value(policy_oids, i);
+		if (OBJ_obj2nid(oid) == NID_any_policy)
+			{
+			tree->flags |= POLICY_FLAG_ANY_POLICY;
+			return 1;
+			}
+		}
+
+	for (i = 0; i < sk_ASN1_OBJECT_num(policy_oids); i++)
+		{
+		oid = sk_ASN1_OBJECT_value(policy_oids, i);
+		node = tree_find_sk(auth_nodes, oid);
+		if (!node)
+			{
+			if (!anyPolicy)
+				continue;
+			/* Create a new node with policy ID from user set
+			 * and qualifiers from anyPolicy.
+			 */
+			extra = policy_data_new(NULL, oid,
+						node_critical(anyPolicy));
+			if (!extra)
+				return 0;
+			extra->qualifier_set = anyPolicy->data->qualifier_set;
+			extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS
+						| POLICY_DATA_FLAG_EXTRA_NODE;
+			node = level_add_node(NULL, extra, anyPolicy->parent,
+						tree);
+			}
+		if (!tree->user_policies)
+			{
+			tree->user_policies = sk_X509_POLICY_NODE_new_null();
+			if (!tree->user_policies)
+				return 1;
+			}
+		if (!sk_X509_POLICY_NODE_push(tree->user_policies, node))
+			return 0;
+		}
+	return 1;
+
+	}
+
+static int tree_evaluate(X509_POLICY_TREE *tree)
+	{
+	int ret, i;
+	X509_POLICY_LEVEL *curr = tree->levels + 1;
+	const X509_POLICY_CACHE *cache;
+
+	for(i = 1; i < tree->nlevel; i++, curr++)
+		{
+		cache = policy_cache_set(curr->cert);
+		if (!tree_link_nodes(curr, cache))
+			return 0;
+
+		if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY)
+			&& !tree_link_any(curr, cache, tree))
+			return 0;
+	tree_print("before tree_prune()", tree, curr);
+		ret = tree_prune(tree, curr);
+		if (ret != 1)
+			return ret;
+		}
+
+	return 1;
+
+	}
+
+static void exnode_free(X509_POLICY_NODE *node)
+	{
+	if (node->data && (node->data->flags & POLICY_DATA_FLAG_EXTRA_NODE))
+		OPENSSL_free(node);
+	}
+
+
+void X509_policy_tree_free(X509_POLICY_TREE *tree)
+	{
+	X509_POLICY_LEVEL *curr;
+	int i;
+
+	if (!tree)
+		return;
+
+	sk_X509_POLICY_NODE_free(tree->auth_policies);
+	sk_X509_POLICY_NODE_pop_free(tree->user_policies, exnode_free);
+
+	for(i = 0, curr = tree->levels; i < tree->nlevel; i++, curr++)
+		{
+		if (curr->cert)
+			X509_free(curr->cert);
+		if (curr->nodes)
+			sk_X509_POLICY_NODE_pop_free(curr->nodes,
+						policy_node_free);
+		if (curr->anyPolicy)
+			policy_node_free(curr->anyPolicy);
+		}
+
+	if (tree->extra_data)
+		sk_X509_POLICY_DATA_pop_free(tree->extra_data,
+						policy_data_free);
+
+	OPENSSL_free(tree->levels);
+	OPENSSL_free(tree);
+
+	}
+
+/* Application policy checking function.
+ * Return codes:
+ *  0 	Internal Error.
+ *  1   Successful.
+ * -1   One or more certificates contain invalid or inconsistent extensions
+ * -2	User constrained policy set empty and requireExplicit true.
+ */
+
+int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
+			STACK_OF(X509) *certs,
+			STACK_OF(ASN1_OBJECT) *policy_oids,
+			unsigned int flags)
+	{
+	int ret;
+	X509_POLICY_TREE *tree = NULL;
+	STACK_OF(X509_POLICY_NODE) *nodes, *auth_nodes = NULL;
+	*ptree = NULL;
+
+	*pexplicit_policy = 0;
+	ret = tree_init(&tree, certs, flags);
+
+	switch (ret)
+		{
+
+		/* Tree empty requireExplicit False: OK */
+		case 2:
+		return 1;
+
+		/* Some internal error */
+		case -1:
+		return -1;
+
+		/* Some internal error */
+		case 0:
+		return 0;
+
+		/* Tree empty requireExplicit True: Error */
+
+		case 6:
+		*pexplicit_policy = 1;
+		return -2;
+
+		/* Tree OK requireExplicit True: OK and continue */
+		case 5:
+		*pexplicit_policy = 1;
+		break;
+
+		/* Tree OK: continue */
+
+		case 1:
+		if (!tree)
+			/*
+			 * tree_init() returns success and a null tree
+			 * if it's just looking at a trust anchor.
+			 * I'm not sure that returning success here is
+			 * correct, but I'm sure that reporting this
+			 * as an internal error which our caller
+			 * interprets as a malloc failure is wrong.
+			 */
+			return 1;
+		break;
+		}
+
+	if (!tree) goto error;
+	ret = tree_evaluate(tree);
+
+	tree_print("tree_evaluate()", tree, NULL);
+
+	if (ret <= 0)
+		goto error;
+
+	/* Return value 2 means tree empty */
+	if (ret == 2)
+		{
+		X509_policy_tree_free(tree);
+		if (*pexplicit_policy)
+			return -2;
+		else
+			return 1;
+		}
+
+	/* Tree is not empty: continue */
+
+	ret = tree_calculate_authority_set(tree, &auth_nodes);
+
+	if (!ret)
+		goto error;
+
+	if (!tree_calculate_user_set(tree, policy_oids, auth_nodes))
+		goto error;
+	
+	if (ret == 2)
+		sk_X509_POLICY_NODE_free(auth_nodes);
+
+	if (tree)
+		*ptree = tree;
+
+	if (*pexplicit_policy)
+		{
+		nodes = X509_policy_tree_get0_user_policies(tree);
+		if (sk_X509_POLICY_NODE_num(nodes) <= 0)
+			return -2;
+		}
+
+	return 1;
+
+	error:
+
+	X509_policy_tree_free(tree);
+
+	return 0;
+
+	}
+
diff --git a/crypto/x509v3/tabtest.c b/crypto/x509v3/tabtest.c
new file mode 100644
index 0000000..aa27c1e
--- /dev/null
+++ b/crypto/x509v3/tabtest.c
@@ -0,0 +1,92 @@
+/* tabtest.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/* Simple program to check the ext_dat.h is correct and print out
+ * problems if it is not.
+ */
+
+#include <stdio.h>
+
+#include <openssl/x509v3.h>
+
+#include "ext_dat.h"
+
+int main()
+{
+	int i, prev = -1, bad = 0;
+	const X509V3_EXT_METHOD **tmp;
+	i = sizeof(standard_exts) / sizeof(X509V3_EXT_METHOD *);
+	if(i != STANDARD_EXTENSION_COUNT)
+		fprintf(stderr, "Extension number invalid expecting %d\n", i);
+	tmp = standard_exts;
+	for(i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++) {
+		if((*tmp)->ext_nid < prev) bad = 1;
+		prev = (*tmp)->ext_nid;
+		
+	}
+	if(bad) {
+		tmp = standard_exts;
+		fprintf(stderr, "Extensions out of order!\n");
+		for(i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++)
+		printf("%d : %s\n", (*tmp)->ext_nid, OBJ_nid2sn((*tmp)->ext_nid));
+		return 1;
+	} else {
+		printf("PASS\n");
+		return 0;
+	}
+}
diff --git a/crypto/x509v3/v3_akey.c b/crypto/x509v3/v3_akey.c
new file mode 100644
index 0000000..67607d9
--- /dev/null
+++ b/crypto/x509v3/v3_akey.c
@@ -0,0 +1,211 @@
+/* v3_akey.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
+			AUTHORITY_KEYID *akeyid, STACK_OF(CONF_VALUE) *extlist);
+static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
+			X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *values);
+
+const X509V3_EXT_METHOD v3_akey_id =
+	{
+	NID_authority_key_identifier,
+	X509V3_EXT_MULTILINE, ASN1_ITEM_ref(AUTHORITY_KEYID),
+	0,0,0,0,
+	0,0,
+	(X509V3_EXT_I2V)i2v_AUTHORITY_KEYID,
+	(X509V3_EXT_V2I)v2i_AUTHORITY_KEYID,
+	0,0,
+	NULL
+	};
+
+static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
+	     AUTHORITY_KEYID *akeyid, STACK_OF(CONF_VALUE) *extlist)
+{
+	char *tmp;
+	if(akeyid->keyid) {
+		tmp = hex_to_string(akeyid->keyid->data, akeyid->keyid->length);
+		X509V3_add_value("keyid", tmp, &extlist);
+		OPENSSL_free(tmp);
+	}
+	if(akeyid->issuer) 
+		extlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist);
+	if(akeyid->serial) {
+		tmp = hex_to_string(akeyid->serial->data,
+						 akeyid->serial->length);
+		X509V3_add_value("serial", tmp, &extlist);
+		OPENSSL_free(tmp);
+	}
+	return extlist;
+}
+
+/* Currently two options:
+ * keyid: use the issuers subject keyid, the value 'always' means its is
+ * an error if the issuer certificate doesn't have a key id.
+ * issuer: use the issuers cert issuer and serial number. The default is
+ * to only use this if keyid is not present. With the option 'always'
+ * this is always included.
+ */
+
+static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
+	     X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *values)
+	{
+	char keyid=0, issuer=0;
+	size_t i;
+	int j;
+	CONF_VALUE *cnf;
+	ASN1_OCTET_STRING *ikeyid = NULL;
+	X509_NAME *isname = NULL;
+	GENERAL_NAMES * gens = NULL;
+	GENERAL_NAME *gen = NULL;
+	ASN1_INTEGER *serial = NULL;
+	X509_EXTENSION *ext;
+	X509 *cert;
+	AUTHORITY_KEYID *akeyid;
+
+	for(i = 0; i < sk_CONF_VALUE_num(values); i++)
+		{
+		cnf = sk_CONF_VALUE_value(values, i);
+		if(!strcmp(cnf->name, "keyid"))
+			{
+			keyid = 1;
+			if(cnf->value && !strcmp(cnf->value, "always"))
+				keyid = 2;
+			}
+		else if(!strcmp(cnf->name, "issuer"))
+			{
+			issuer = 1;
+			if(cnf->value && !strcmp(cnf->value, "always"))
+				issuer = 2;
+			}
+		else
+			{
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_KEYID, X509V3_R_UNKNOWN_OPTION);
+			ERR_add_error_data(2, "name=", cnf->name);
+			return NULL;
+			}
+		}
+
+	if(!ctx || !ctx->issuer_cert)
+		{
+		if(ctx && (ctx->flags==CTX_TEST))
+			return AUTHORITY_KEYID_new();
+		OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_KEYID, X509V3_R_NO_ISSUER_CERTIFICATE);
+		return NULL;
+		}
+
+	cert = ctx->issuer_cert;
+
+	if(keyid)
+		{
+		j = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1);
+		if((j >= 0)  && (ext = X509_get_ext(cert, j)))
+			ikeyid = X509V3_EXT_d2i(ext);
+		if(keyid==2 && !ikeyid)
+			{
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_KEYID, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID);
+			return NULL;
+			}
+		}
+
+	if((issuer && !ikeyid) || (issuer == 2))
+		{
+		isname = X509_NAME_dup(X509_get_issuer_name(cert));
+		serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(cert));
+		if(!isname || !serial)
+			{
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_KEYID, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS);
+			goto err;
+			}
+		}
+
+	if(!(akeyid = AUTHORITY_KEYID_new())) goto err;
+
+	if(isname)
+		{
+		if(!(gens = sk_GENERAL_NAME_new_null())
+			|| !(gen = GENERAL_NAME_new())
+			|| !sk_GENERAL_NAME_push(gens, gen))
+			{
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_KEYID, ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+		gen->type = GEN_DIRNAME;
+		gen->d.dirn = isname;
+		}
+
+	akeyid->issuer = gens;
+	akeyid->serial = serial;
+	akeyid->keyid = ikeyid;
+
+	return akeyid;
+
+ err:
+	X509_NAME_free(isname);
+	M_ASN1_INTEGER_free(serial);
+	M_ASN1_OCTET_STRING_free(ikeyid);
+	return NULL;
+	}
diff --git a/crypto/x509v3/v3_akeya.c b/crypto/x509v3/v3_akeya.c
new file mode 100644
index 0000000..8b72cca
--- /dev/null
+++ b/crypto/x509v3/v3_akeya.c
@@ -0,0 +1,71 @@
+/* v3_akey_asn1.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/x509v3.h>
+
+
+ASN1_SEQUENCE(AUTHORITY_KEYID) = {
+	ASN1_IMP_OPT(AUTHORITY_KEYID, keyid, ASN1_OCTET_STRING, 0),
+	ASN1_IMP_SEQUENCE_OF_OPT(AUTHORITY_KEYID, issuer, GENERAL_NAME, 1),
+	ASN1_IMP_OPT(AUTHORITY_KEYID, serial, ASN1_INTEGER, 2)
+} ASN1_SEQUENCE_END(AUTHORITY_KEYID)
+
+IMPLEMENT_ASN1_FUNCTIONS(AUTHORITY_KEYID)
diff --git a/crypto/x509v3/v3_alt.c b/crypto/x509v3/v3_alt.c
new file mode 100644
index 0000000..54e0f0a
--- /dev/null
+++ b/crypto/x509v3/v3_alt.c
@@ -0,0 +1,617 @@
+/* v3_alt.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static GENERAL_NAMES *v2i_subject_alt(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+static GENERAL_NAMES *v2i_issuer_alt(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p);
+static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens);
+static int do_othername(GENERAL_NAME *gen, char *value, X509V3_CTX *ctx);
+static int do_dirname(GENERAL_NAME *gen, char *value, X509V3_CTX *ctx);
+
+const X509V3_EXT_METHOD v3_alt[] = {
+{ NID_subject_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES),
+0,0,0,0,
+0,0,
+(X509V3_EXT_I2V)i2v_GENERAL_NAMES,
+(X509V3_EXT_V2I)v2i_subject_alt,
+NULL, NULL, NULL},
+
+{ NID_issuer_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES),
+0,0,0,0,
+0,0,
+(X509V3_EXT_I2V)i2v_GENERAL_NAMES,
+(X509V3_EXT_V2I)v2i_issuer_alt,
+NULL, NULL, NULL},
+
+{ NID_certificate_issuer, 0, ASN1_ITEM_ref(GENERAL_NAMES),
+0,0,0,0,
+0,0,
+(X509V3_EXT_I2V)i2v_GENERAL_NAMES,
+NULL, NULL, NULL, NULL},
+};
+
+STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(X509V3_EXT_METHOD *method,
+		GENERAL_NAMES *gens, STACK_OF(CONF_VALUE) *ret)
+{
+	size_t i;
+	GENERAL_NAME *gen;
+	for(i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
+		gen = sk_GENERAL_NAME_value(gens, i);
+		ret = i2v_GENERAL_NAME(method, gen, ret);
+	}
+	if(!ret) return sk_CONF_VALUE_new_null();
+	return ret;
+}
+
+STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method,
+				GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret)
+{
+	unsigned char *p;
+	char oline[256], htmp[5];
+	int i;
+	switch (gen->type)
+	{
+		case GEN_OTHERNAME:
+		X509V3_add_value("othername","<unsupported>", &ret);
+		break;
+
+		case GEN_X400:
+		X509V3_add_value("X400Name","<unsupported>", &ret);
+		break;
+
+		case GEN_EDIPARTY:
+		X509V3_add_value("EdiPartyName","<unsupported>", &ret);
+		break;
+
+		case GEN_EMAIL:
+		X509V3_add_value_uchar("email",gen->d.ia5->data, &ret);
+		break;
+
+		case GEN_DNS:
+		X509V3_add_value_uchar("DNS",gen->d.ia5->data, &ret);
+		break;
+
+		case GEN_URI:
+		X509V3_add_value_uchar("URI",gen->d.ia5->data, &ret);
+		break;
+
+		case GEN_DIRNAME:
+		X509_NAME_oneline(gen->d.dirn, oline, 256);
+		X509V3_add_value("DirName",oline, &ret);
+		break;
+
+		case GEN_IPADD:
+		p = gen->d.ip->data;
+		if(gen->d.ip->length == 4)
+			BIO_snprintf(oline, sizeof oline,
+				     "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+		else if(gen->d.ip->length == 16)
+			{
+			oline[0] = 0;
+			for (i = 0; i < 8; i++)
+				{
+				BIO_snprintf(htmp, sizeof htmp,
+					     "%X", p[0] << 8 | p[1]);
+				p += 2;
+				strcat(oline, htmp);
+				if (i != 7)
+					strcat(oline, ":");
+				}
+			}
+		else
+			{
+			X509V3_add_value("IP Address","<invalid>", &ret);
+			break;
+			}
+		X509V3_add_value("IP Address",oline, &ret);
+		break;
+
+		case GEN_RID:
+		i2t_ASN1_OBJECT(oline, 256, gen->d.rid);
+		X509V3_add_value("Registered ID",oline, &ret);
+		break;
+	}
+	return ret;
+}
+
+int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen)
+{
+	unsigned char *p;
+	int i;
+	switch (gen->type)
+	{
+		case GEN_OTHERNAME:
+		BIO_printf(out, "othername:<unsupported>");
+		break;
+
+		case GEN_X400:
+		BIO_printf(out, "X400Name:<unsupported>");
+		break;
+
+		case GEN_EDIPARTY:
+		/* Maybe fix this: it is supported now */
+		BIO_printf(out, "EdiPartyName:<unsupported>");
+		break;
+
+		case GEN_EMAIL:
+		BIO_printf(out, "email:%s",gen->d.ia5->data);
+		break;
+
+		case GEN_DNS:
+		BIO_printf(out, "DNS:%s",gen->d.ia5->data);
+		break;
+
+		case GEN_URI:
+		BIO_printf(out, "URI:%s",gen->d.ia5->data);
+		break;
+
+		case GEN_DIRNAME:
+		BIO_printf(out, "DirName: ");
+		X509_NAME_print_ex(out, gen->d.dirn, 0, XN_FLAG_ONELINE);
+		break;
+
+		case GEN_IPADD:
+		p = gen->d.ip->data;
+		if(gen->d.ip->length == 4)
+			BIO_printf(out, "IP Address:%d.%d.%d.%d",
+						p[0], p[1], p[2], p[3]);
+		else if(gen->d.ip->length == 16)
+			{
+			BIO_printf(out, "IP Address");
+			for (i = 0; i < 8; i++)
+				{
+				BIO_printf(out, ":%X", p[0] << 8 | p[1]);
+				p += 2;
+				}
+			BIO_puts(out, "\n");
+			}
+		else
+			{
+			BIO_printf(out,"IP Address:<invalid>");
+			break;
+			}
+		break;
+
+		case GEN_RID:
+		BIO_printf(out, "Registered ID");
+		i2a_ASN1_OBJECT(out, gen->d.rid);
+		break;
+	}
+	return 1;
+}
+
+static GENERAL_NAMES *v2i_issuer_alt(X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	GENERAL_NAMES *gens = NULL;
+	CONF_VALUE *cnf;
+	size_t i;
+	if(!(gens = sk_GENERAL_NAME_new_null())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_issuer_alt, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		cnf = sk_CONF_VALUE_value(nval, i);
+		if(!name_cmp(cnf->name, "issuer") && cnf->value &&
+						!strcmp(cnf->value, "copy")) {
+			if(!copy_issuer(ctx, gens)) goto err;
+		} else {
+			GENERAL_NAME *gen;
+			if(!(gen = v2i_GENERAL_NAME(method, ctx, cnf)))
+								 goto err; 
+			sk_GENERAL_NAME_push(gens, gen);
+		}
+	}
+	return gens;
+	err:
+	sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+	return NULL;
+}
+
+/* Append subject altname of issuer to issuer alt name of subject */
+
+static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens)
+{
+	GENERAL_NAMES *ialt;
+	GENERAL_NAME *gen;
+	X509_EXTENSION *ext;
+	int i;
+	size_t j;
+	if(ctx && (ctx->flags == CTX_TEST)) return 1;
+	if(!ctx || !ctx->issuer_cert) {
+		OPENSSL_PUT_ERROR(X509V3, copy_issuer, X509V3_R_NO_ISSUER_DETAILS);
+		goto err;
+	}
+        i = X509_get_ext_by_NID(ctx->issuer_cert, NID_subject_alt_name, -1);
+	if(i < 0) return 1;
+        if(!(ext = X509_get_ext(ctx->issuer_cert, i)) ||
+                        !(ialt = X509V3_EXT_d2i(ext)) ) {
+		OPENSSL_PUT_ERROR(X509V3, copy_issuer, X509V3_R_ISSUER_DECODE_ERROR);
+		goto err;
+	}
+
+	for(j = 0; j < sk_GENERAL_NAME_num(ialt); j++) {
+		gen = sk_GENERAL_NAME_value(ialt, j);
+		if(!sk_GENERAL_NAME_push(gens, gen)) {
+			OPENSSL_PUT_ERROR(X509V3, copy_issuer, ERR_R_MALLOC_FAILURE);
+			goto err;
+		}
+	}
+	sk_GENERAL_NAME_free(ialt);
+
+	return 1;
+		
+	err:
+	return 0;
+	
+}
+
+static GENERAL_NAMES *v2i_subject_alt(X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	GENERAL_NAMES *gens = NULL;
+	CONF_VALUE *cnf;
+	size_t i;
+	if(!(gens = sk_GENERAL_NAME_new_null())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_subject_alt, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		cnf = sk_CONF_VALUE_value(nval, i);
+		if(!name_cmp(cnf->name, "email") && cnf->value &&
+						!strcmp(cnf->value, "copy")) {
+			if(!copy_email(ctx, gens, 0)) goto err;
+		} else if(!name_cmp(cnf->name, "email") && cnf->value &&
+						!strcmp(cnf->value, "move")) {
+			if(!copy_email(ctx, gens, 1)) goto err;
+		} else {
+			GENERAL_NAME *gen;
+			if(!(gen = v2i_GENERAL_NAME(method, ctx, cnf)))
+								 goto err; 
+			sk_GENERAL_NAME_push(gens, gen);
+		}
+	}
+	return gens;
+	err:
+	sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+	return NULL;
+}
+
+/* Copy any email addresses in a certificate or request to 
+ * GENERAL_NAMES
+ */
+
+static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p)
+{
+	X509_NAME *nm;
+	ASN1_IA5STRING *email = NULL;
+	X509_NAME_ENTRY *ne;
+	GENERAL_NAME *gen = NULL;
+	int i;
+	if(ctx != NULL && ctx->flags == CTX_TEST)
+		return 1;
+	if(!ctx || (!ctx->subject_cert && !ctx->subject_req)) {
+		OPENSSL_PUT_ERROR(X509V3, copy_email, X509V3_R_NO_SUBJECT_DETAILS);
+		goto err;
+	}
+	/* Find the subject name */
+	if(ctx->subject_cert) nm = X509_get_subject_name(ctx->subject_cert);
+	else nm = X509_REQ_get_subject_name(ctx->subject_req);
+
+	/* Now add any email address(es) to STACK */
+	i = -1;
+	while((i = X509_NAME_get_index_by_NID(nm,
+					 NID_pkcs9_emailAddress, i)) >= 0) {
+		ne = X509_NAME_get_entry(nm, i);
+		email = M_ASN1_IA5STRING_dup(X509_NAME_ENTRY_get_data(ne));
+                if (move_p)
+                        {
+                        X509_NAME_delete_entry(nm, i);
+			X509_NAME_ENTRY_free(ne);
+                        i--;
+                        }
+		if(!email || !(gen = GENERAL_NAME_new())) {
+			OPENSSL_PUT_ERROR(X509V3, copy_email, ERR_R_MALLOC_FAILURE);
+			goto err;
+		}
+		gen->d.ia5 = email;
+		email = NULL;
+		gen->type = GEN_EMAIL;
+		if(!sk_GENERAL_NAME_push(gens, gen)) {
+			OPENSSL_PUT_ERROR(X509V3, copy_email, ERR_R_MALLOC_FAILURE);
+			goto err;
+		}
+		gen = NULL;
+	}
+
+	
+	return 1;
+		
+	err:
+	GENERAL_NAME_free(gen);
+	M_ASN1_IA5STRING_free(email);
+	return 0;
+	
+}
+
+GENERAL_NAMES *v2i_GENERAL_NAMES(const X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	GENERAL_NAME *gen;
+	GENERAL_NAMES *gens = NULL;
+	CONF_VALUE *cnf;
+	size_t i;
+	if(!(gens = sk_GENERAL_NAME_new_null())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_GENERAL_NAMES, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		cnf = sk_CONF_VALUE_value(nval, i);
+		if(!(gen = v2i_GENERAL_NAME(method, ctx, cnf))) goto err; 
+		sk_GENERAL_NAME_push(gens, gen);
+	}
+	return gens;
+	err:
+	sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+	return NULL;
+}
+
+GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+			       CONF_VALUE *cnf)
+	{
+	return v2i_GENERAL_NAME_ex(NULL, method, ctx, cnf, 0);
+	}
+
+GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out,
+			       const X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+			       int gen_type, char *value, int is_nc)
+	{
+	char is_string = 0;
+	GENERAL_NAME *gen = NULL;
+
+	if(!value)
+		{
+		OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, X509V3_R_MISSING_VALUE);
+		return NULL;
+		}
+
+	if (out)
+		gen = out;
+	else
+		{
+		gen = GENERAL_NAME_new();
+		if(gen == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, ERR_R_MALLOC_FAILURE);
+			return NULL;
+			}
+		}
+
+	switch (gen_type)
+		{
+		case GEN_URI:
+		case GEN_EMAIL:
+		case GEN_DNS:
+		is_string = 1;
+		break;
+		
+		case GEN_RID:
+		{
+		ASN1_OBJECT *obj;
+		if(!(obj = OBJ_txt2obj(value,0)))
+			{
+			OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, X509V3_R_BAD_OBJECT);
+			ERR_add_error_data(2, "value=", value);
+			goto err;
+			}
+		gen->d.rid = obj;
+		}
+		break;
+
+		case GEN_IPADD:
+		if (is_nc)
+			gen->d.ip = a2i_IPADDRESS_NC(value);
+		else
+			gen->d.ip = a2i_IPADDRESS(value);
+		if(gen->d.ip == NULL)
+			{
+			OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, X509V3_R_BAD_IP_ADDRESS);
+			ERR_add_error_data(2, "value=", value);
+			goto err;
+			}
+		break;
+
+		case GEN_DIRNAME:
+		if (!do_dirname(gen, value, ctx))
+			{
+			OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, X509V3_R_DIRNAME_ERROR);
+			goto err;
+			}
+		break;
+
+		case GEN_OTHERNAME:
+		if (!do_othername(gen, value, ctx))
+			{
+			OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, X509V3_R_OTHERNAME_ERROR);
+			goto err;
+			}
+		break;
+		default:
+		OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, X509V3_R_UNSUPPORTED_TYPE);
+		goto err;
+		}
+
+	if(is_string)
+		{
+		if(!(gen->d.ia5 = M_ASN1_IA5STRING_new()) ||
+			      !ASN1_STRING_set(gen->d.ia5, (unsigned char*)value,
+					       strlen(value)))
+			{
+			OPENSSL_PUT_ERROR(X509V3, a2i_GENERAL_NAME, ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+		}
+
+	gen->type = gen_type;
+
+	return gen;
+
+	err:
+	if (!out)
+		GENERAL_NAME_free(gen);
+	return NULL;
+	}
+
+GENERAL_NAME *v2i_GENERAL_NAME_ex(GENERAL_NAME *out,
+				  const X509V3_EXT_METHOD *method,
+				  X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc)
+	{
+	int type;
+
+	char *name, *value;
+
+	name = cnf->name;
+	value = cnf->value;
+
+	if(!value)
+		{
+		OPENSSL_PUT_ERROR(X509V3, v2i_GENERAL_NAME_ex, X509V3_R_MISSING_VALUE);
+		return NULL;
+		}
+
+	if(!name_cmp(name, "email"))
+		type = GEN_EMAIL;
+	else if(!name_cmp(name, "URI"))
+		type = GEN_URI;
+	else if(!name_cmp(name, "DNS"))
+		type = GEN_DNS;
+	else if(!name_cmp(name, "RID"))
+		type = GEN_RID;
+	else if(!name_cmp(name, "IP"))
+		type = GEN_IPADD;
+	else if(!name_cmp(name, "dirName"))
+		type = GEN_DIRNAME;
+	else if(!name_cmp(name, "otherName"))
+		type = GEN_OTHERNAME;
+	else
+		{
+		OPENSSL_PUT_ERROR(X509V3, v2i_GENERAL_NAME_ex, X509V3_R_UNSUPPORTED_OPTION);
+		ERR_add_error_data(2, "name=", name);
+		return NULL;
+		}
+
+	return a2i_GENERAL_NAME(out, method, ctx, type, value, is_nc);
+
+	}
+
+static int do_othername(GENERAL_NAME *gen, char *value, X509V3_CTX *ctx)
+	{
+	char *objtmp = NULL, *p;
+	int objlen;
+	if (!(p = strchr(value, ';')))
+		return 0;
+	if (!(gen->d.otherName = OTHERNAME_new()))
+		return 0;
+	/* Free this up because we will overwrite it.
+	 * no need to free type_id because it is static
+	 */
+	ASN1_TYPE_free(gen->d.otherName->value);
+	if (!(gen->d.otherName->value = ASN1_generate_v3(p + 1, ctx)))
+		return 0;
+	objlen = p - value;
+	objtmp = OPENSSL_malloc(objlen + 1);
+	strncpy(objtmp, value, objlen);
+	objtmp[objlen] = 0;
+	gen->d.otherName->type_id = OBJ_txt2obj(objtmp, 0);
+	OPENSSL_free(objtmp);	
+	if (!gen->d.otherName->type_id)
+		return 0;
+	return 1;
+	}
+
+static int do_dirname(GENERAL_NAME *gen, char *value, X509V3_CTX *ctx)
+	{
+	int ret;
+	STACK_OF(CONF_VALUE) *sk;
+	X509_NAME *nm;
+	if (!(nm = X509_NAME_new()))
+		return 0;
+	sk = X509V3_get_section(ctx, value);
+	if (!sk)
+		{
+		OPENSSL_PUT_ERROR(X509V3, do_dirname, X509V3_R_SECTION_NOT_FOUND);
+		ERR_add_error_data(2, "section=", value);
+		X509_NAME_free(nm);
+		return 0;
+		}
+	/* FIXME: should allow other character types... */
+	ret = X509V3_NAME_from_section(nm, sk, MBSTRING_ASC);
+	if (!ret)
+		X509_NAME_free(nm);
+	gen->d.dirn = nm;
+	X509V3_section_free(ctx, sk);
+		
+	return ret;
+	}
diff --git a/crypto/x509v3/v3_bcons.c b/crypto/x509v3/v3_bcons.c
new file mode 100644
index 0000000..881b922
--- /dev/null
+++ b/crypto/x509v3/v3_bcons.c
@@ -0,0 +1,125 @@
+/* v3_bcons.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static STACK_OF(CONF_VALUE) *i2v_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, BASIC_CONSTRAINTS *bcons, STACK_OF(CONF_VALUE) *extlist);
+static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *values);
+
+const X509V3_EXT_METHOD v3_bcons = {
+NID_basic_constraints, 0,
+ASN1_ITEM_ref(BASIC_CONSTRAINTS),
+0,0,0,0,
+0,0,
+(X509V3_EXT_I2V)i2v_BASIC_CONSTRAINTS,
+(X509V3_EXT_V2I)v2i_BASIC_CONSTRAINTS,
+NULL,NULL,
+NULL
+};
+
+ASN1_SEQUENCE(BASIC_CONSTRAINTS) = {
+	ASN1_OPT(BASIC_CONSTRAINTS, ca, ASN1_FBOOLEAN),
+	ASN1_OPT(BASIC_CONSTRAINTS, pathlen, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(BASIC_CONSTRAINTS)
+
+IMPLEMENT_ASN1_FUNCTIONS(BASIC_CONSTRAINTS)
+
+
+static STACK_OF(CONF_VALUE) *i2v_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method,
+	     BASIC_CONSTRAINTS *bcons, STACK_OF(CONF_VALUE) *extlist)
+{
+	X509V3_add_value_bool("CA", bcons->ca, &extlist);
+	X509V3_add_value_int("pathlen", bcons->pathlen, &extlist);
+	return extlist;
+}
+
+static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method,
+	     X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *values)
+{
+	BASIC_CONSTRAINTS *bcons=NULL;
+	CONF_VALUE *val;
+	size_t i;
+	if(!(bcons = BASIC_CONSTRAINTS_new())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_BASIC_CONSTRAINTS, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(values); i++) {
+		val = sk_CONF_VALUE_value(values, i);
+		if(!strcmp(val->name, "CA")) {
+			if(!X509V3_get_value_bool(val, &bcons->ca)) goto err;
+		} else if(!strcmp(val->name, "pathlen")) {
+			if(!X509V3_get_value_int(val, &bcons->pathlen)) goto err;
+		} else {
+			OPENSSL_PUT_ERROR(X509V3, v2i_BASIC_CONSTRAINTS, X509V3_R_INVALID_NAME);
+			X509V3_conf_err(val);
+			goto err;
+		}
+	}
+	return bcons;
+	err:
+	BASIC_CONSTRAINTS_free(bcons);
+	return NULL;
+}
+
diff --git a/crypto/x509v3/v3_bitst.c b/crypto/x509v3/v3_bitst.c
new file mode 100644
index 0000000..dd389f0
--- /dev/null
+++ b/crypto/x509v3/v3_bitst.c
@@ -0,0 +1,140 @@
+/* v3_bitst.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static BIT_STRING_BITNAME ns_cert_type_table[] = {
+{0, "SSL Client", "client"},
+{1, "SSL Server", "server"},
+{2, "S/MIME", "email"},
+{3, "Object Signing", "objsign"},
+{4, "Unused", "reserved"},
+{5, "SSL CA", "sslCA"},
+{6, "S/MIME CA", "emailCA"},
+{7, "Object Signing CA", "objCA"},
+{-1, NULL, NULL}
+};
+
+static BIT_STRING_BITNAME key_usage_type_table[] = {
+{0, "Digital Signature", "digitalSignature"},
+{1, "Non Repudiation", "nonRepudiation"},
+{2, "Key Encipherment", "keyEncipherment"},
+{3, "Data Encipherment", "dataEncipherment"},
+{4, "Key Agreement", "keyAgreement"},
+{5, "Certificate Sign", "keyCertSign"},
+{6, "CRL Sign", "cRLSign"},
+{7, "Encipher Only", "encipherOnly"},
+{8, "Decipher Only", "decipherOnly"},
+{-1, NULL, NULL}
+};
+
+
+
+const X509V3_EXT_METHOD v3_nscert = EXT_BITSTRING(NID_netscape_cert_type, ns_cert_type_table);
+const X509V3_EXT_METHOD v3_key_usage = EXT_BITSTRING(NID_key_usage, key_usage_type_table);
+
+STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method,
+	     ASN1_BIT_STRING *bits, STACK_OF(CONF_VALUE) *ret)
+{
+	BIT_STRING_BITNAME *bnam;
+	for(bnam =method->usr_data; bnam->lname; bnam++) {
+		if(ASN1_BIT_STRING_get_bit(bits, bnam->bitnum)) 
+			X509V3_add_value(bnam->lname, NULL, &ret);
+	}
+	return ret;
+}
+	
+ASN1_BIT_STRING *v2i_ASN1_BIT_STRING(X509V3_EXT_METHOD *method,
+	     X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	CONF_VALUE *val;
+	ASN1_BIT_STRING *bs;
+	size_t i;
+	BIT_STRING_BITNAME *bnam;
+	if(!(bs = M_ASN1_BIT_STRING_new())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_ASN1_BIT_STRING, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		val = sk_CONF_VALUE_value(nval, i);
+		for(bnam = method->usr_data; bnam->lname; bnam++) {
+			if(!strcmp(bnam->sname, val->name) ||
+				!strcmp(bnam->lname, val->name) ) {
+				if(!ASN1_BIT_STRING_set_bit(bs, bnam->bitnum, 1)) {
+					OPENSSL_PUT_ERROR(X509V3, v2i_ASN1_BIT_STRING, ERR_R_MALLOC_FAILURE);
+					M_ASN1_BIT_STRING_free(bs);
+					return NULL;
+				}
+				break;
+			}
+		}
+		if(!bnam->lname) {
+			OPENSSL_PUT_ERROR(X509V3, v2i_ASN1_BIT_STRING, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT);
+			X509V3_conf_err(val);
+			M_ASN1_BIT_STRING_free(bs);
+			return NULL;
+		}
+	}
+	return bs;
+}
+	
+
diff --git a/crypto/x509v3/v3_conf.c b/crypto/x509v3/v3_conf.c
new file mode 100644
index 0000000..9ee4809
--- /dev/null
+++ b/crypto/x509v3/v3_conf.c
@@ -0,0 +1,528 @@
+/* v3_conf.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+/* extension creation utilities */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+static int v3_check_critical(char **value);
+static int v3_check_generic(char **value);
+static X509_EXTENSION *do_ext_nconf(CONF *conf, X509V3_CTX *ctx, int ext_nid, int crit, char *value);
+static X509_EXTENSION *v3_generic_extension(const char *ext, char *value, int crit, int type, X509V3_CTX *ctx);
+static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, int ext_nid,
+				  int crit, void *ext_struc);
+static unsigned char *generic_asn1(char *value, X509V3_CTX *ctx, long *ext_len);
+/* CONF *conf:  Config file    */
+/* char *name:  Name    */
+/* char *value:  Value    */
+X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, char *name,
+				 char *value)
+	{
+	int crit;
+	int ext_type;
+	X509_EXTENSION *ret;
+	crit = v3_check_critical(&value);
+	if ((ext_type = v3_check_generic(&value))) 
+		return v3_generic_extension(name, value, crit, ext_type, ctx);
+	ret = do_ext_nconf(conf, ctx, OBJ_sn2nid(name), crit, value);
+	if (!ret)
+		{
+		OPENSSL_PUT_ERROR(X509V3, X509V3_EXT_nconf, X509V3_R_ERROR_IN_EXTENSION);
+		ERR_add_error_data(4,"name=", name, ", value=", value);
+		}
+	return ret;
+	}
+
+/* CONF *conf:  Config file    */
+/* char *value:  Value    */
+X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid,
+				     char *value)
+	{
+	int crit;
+	int ext_type;
+	crit = v3_check_critical(&value);
+	if ((ext_type = v3_check_generic(&value))) 
+		return v3_generic_extension(OBJ_nid2sn(ext_nid),
+						 value, crit, ext_type, ctx);
+	return do_ext_nconf(conf, ctx, ext_nid, crit, value);
+	}
+
+/* CONF *conf:  Config file    */
+/* char *value:  Value    */
+static X509_EXTENSION *do_ext_nconf(CONF *conf, X509V3_CTX *ctx, int ext_nid,
+				    int crit, char *value)
+	{
+	const X509V3_EXT_METHOD *method;
+	X509_EXTENSION *ext;
+	STACK_OF(CONF_VALUE) *nval;
+	void *ext_struc;
+	if (ext_nid == NID_undef)
+		{
+		OPENSSL_PUT_ERROR(X509V3, do_ext_nconf, X509V3_R_UNKNOWN_EXTENSION_NAME);
+		return NULL;
+		}
+	if (!(method = X509V3_EXT_get_nid(ext_nid)))
+		{
+		OPENSSL_PUT_ERROR(X509V3, do_ext_nconf, X509V3_R_UNKNOWN_EXTENSION);
+		return NULL;
+		}
+	/* Now get internal extension representation based on type */
+	if (method->v2i)
+		{
+		if(*value == '@') nval = NCONF_get_section(conf, value + 1);
+		else nval = X509V3_parse_list(value);
+		if(sk_CONF_VALUE_num(nval) <= 0)
+			{
+			OPENSSL_PUT_ERROR(X509V3, do_ext_nconf, X509V3_R_INVALID_EXTENSION_STRING);
+			ERR_add_error_data(4, "name=", OBJ_nid2sn(ext_nid), ",section=", value);
+			return NULL;
+			}
+		ext_struc = method->v2i(method, ctx, nval);
+		if(*value != '@') sk_CONF_VALUE_pop_free(nval,
+							 X509V3_conf_free);
+		if(!ext_struc) return NULL;
+		}
+	else if(method->s2i)
+		{
+		if(!(ext_struc = method->s2i(method, ctx, value))) return NULL;
+		}
+	else if(method->r2i)
+		{
+		if(!ctx->db || !ctx->db_meth)
+			{
+			OPENSSL_PUT_ERROR(X509V3, do_ext_nconf, X509V3_R_NO_CONFIG_DATABASE);
+			return NULL;
+			}
+		if(!(ext_struc = method->r2i(method, ctx, value))) return NULL;
+		}
+	else
+		{
+		OPENSSL_PUT_ERROR(X509V3, do_ext_nconf, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED);
+		ERR_add_error_data(2, "name=", OBJ_nid2sn(ext_nid));
+		return NULL;
+		}
+
+	ext  = do_ext_i2d(method, ext_nid, crit, ext_struc);
+	if(method->it) ASN1_item_free(ext_struc, ASN1_ITEM_ptr(method->it));
+	else method->ext_free(ext_struc);
+	return ext;
+
+	}
+
+static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, int ext_nid,
+				  int crit, void *ext_struc)
+	{
+	unsigned char *ext_der;
+	int ext_len;
+	ASN1_OCTET_STRING *ext_oct;
+	X509_EXTENSION *ext;
+	/* Convert internal representation to DER */
+	if (method->it)
+		{
+		ext_der = NULL;
+		ext_len = ASN1_item_i2d(ext_struc, &ext_der, ASN1_ITEM_ptr(method->it));
+		if (ext_len < 0) goto merr;
+		}
+	 else
+		{
+		unsigned char *p;
+		ext_len = method->i2d(ext_struc, NULL);
+		if(!(ext_der = OPENSSL_malloc(ext_len))) goto merr;
+		p = ext_der;
+		method->i2d(ext_struc, &p);
+		}
+	if (!(ext_oct = M_ASN1_OCTET_STRING_new())) goto merr;
+	ext_oct->data = ext_der;
+	ext_oct->length = ext_len;
+
+	ext = X509_EXTENSION_create_by_NID(NULL, ext_nid, crit, ext_oct);
+	if (!ext) goto merr;
+	M_ASN1_OCTET_STRING_free(ext_oct);
+
+	return ext;
+
+	merr:
+	OPENSSL_PUT_ERROR(X509V3, do_ext_i2d, ERR_R_MALLOC_FAILURE);
+	return NULL;
+
+	}
+
+/* Given an internal structure, nid and critical flag create an extension */
+
+X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc)
+	{
+	const X509V3_EXT_METHOD *method;
+	if (!(method = X509V3_EXT_get_nid(ext_nid))) {
+		OPENSSL_PUT_ERROR(X509V3, X509V3_EXT_i2d, X509V3_R_UNKNOWN_EXTENSION);
+		return NULL;
+	}
+	return do_ext_i2d(method, ext_nid, crit, ext_struc);
+}
+
+/* Check the extension string for critical flag */
+static int v3_check_critical(char **value)
+{
+	char *p = *value;
+	if ((strlen(p) < 9) || strncmp(p, "critical,", 9)) return 0;
+	p+=9;
+	while(isspace((unsigned char)*p)) p++;
+	*value = p;
+	return 1;
+}
+
+/* Check extension string for generic extension and return the type */
+static int v3_check_generic(char **value)
+{
+	int gen_type = 0;
+	char *p = *value;
+	if ((strlen(p) >= 4) && !strncmp(p, "DER:", 4))
+		{
+		p+=4;
+		gen_type = 1;
+		}
+	else if ((strlen(p) >= 5) && !strncmp(p, "ASN1:", 5))
+		{
+		p+=5;
+		gen_type = 2;
+		}
+	else
+		return 0;
+
+	while (isspace((unsigned char)*p)) p++;
+	*value = p;
+	return gen_type;
+}
+
+/* Create a generic extension: for now just handle DER type */
+static X509_EXTENSION *v3_generic_extension(const char *ext, char *value,
+					    int crit, int gen_type,
+					    X509V3_CTX *ctx)
+	{
+	unsigned char *ext_der=NULL;
+	long ext_len;
+	ASN1_OBJECT *obj=NULL;
+	ASN1_OCTET_STRING *oct=NULL;
+	X509_EXTENSION *extension=NULL;
+	if (!(obj = OBJ_txt2obj(ext, 0)))
+		{
+		OPENSSL_PUT_ERROR(X509V3, v3_generic_extension, X509V3_R_EXTENSION_NAME_ERROR);
+		ERR_add_error_data(2, "name=", ext);
+		goto err;
+		}
+
+	if (gen_type == 1)
+		ext_der = string_to_hex(value, &ext_len);
+	else if (gen_type == 2)
+		ext_der = generic_asn1(value, ctx, &ext_len);
+
+	if (ext_der == NULL)
+		{
+		OPENSSL_PUT_ERROR(X509V3, v3_generic_extension, X509V3_R_EXTENSION_VALUE_ERROR);
+		ERR_add_error_data(2, "value=", value);
+		goto err;
+		}
+
+	if (!(oct = M_ASN1_OCTET_STRING_new()))
+		{
+		OPENSSL_PUT_ERROR(X509V3, v3_generic_extension, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	oct->data = ext_der;
+	oct->length = ext_len;
+	ext_der = NULL;
+
+	extension = X509_EXTENSION_create_by_OBJ(NULL, obj, crit, oct);
+
+	err:
+	ASN1_OBJECT_free(obj);
+	M_ASN1_OCTET_STRING_free(oct);
+	if(ext_der) OPENSSL_free(ext_der);
+	return extension;
+
+	}
+
+static unsigned char *generic_asn1(char *value, X509V3_CTX *ctx, long *ext_len)
+	{
+	ASN1_TYPE *typ;
+	unsigned char *ext_der = NULL;
+	typ = ASN1_generate_v3(value, ctx);
+	if (typ == NULL)
+		return NULL;
+	*ext_len = i2d_ASN1_TYPE(typ, &ext_der);
+	ASN1_TYPE_free(typ);
+	return ext_der;
+	}
+
+/* This is the main function: add a bunch of extensions based on a config file
+ * section to an extension STACK.
+ */
+
+
+int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, char *section,
+			    STACK_OF(X509_EXTENSION) **sk)
+	{
+	X509_EXTENSION *ext;
+	STACK_OF(CONF_VALUE) *nval;
+	CONF_VALUE *val;	
+	size_t i;
+	if (!(nval = NCONF_get_section(conf, section))) return 0;
+	for (i = 0; i < sk_CONF_VALUE_num(nval); i++)
+		{
+		val = sk_CONF_VALUE_value(nval, i);
+		if (!(ext = X509V3_EXT_nconf(conf, ctx, val->name, val->value)))
+								return 0;
+		if (sk) X509v3_add_ext(sk, ext, -1);
+		X509_EXTENSION_free(ext);
+		}
+	return 1;
+	}
+
+/* Convenience functions to add extensions to a certificate, CRL and request */
+
+int X509V3_EXT_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section,
+			 X509 *cert)
+	{
+	STACK_OF(X509_EXTENSION) **sk = NULL;
+	if (cert)
+		sk = &cert->cert_info->extensions;
+	return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
+	}
+
+/* Same as above but for a CRL */
+
+int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section,
+			     X509_CRL *crl)
+	{
+	STACK_OF(X509_EXTENSION) **sk = NULL;
+	if (crl)
+		sk = &crl->crl->extensions;
+	return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
+	}
+
+/* Add extensions to certificate request */
+
+int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section,
+	     X509_REQ *req)
+	{
+	STACK_OF(X509_EXTENSION) *extlist = NULL, **sk = NULL;
+	int i;
+	if (req)
+		sk = &extlist;
+	i = X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
+	if (!i || !sk)
+		return i;
+	i = X509_REQ_add_extensions(req, extlist);
+	sk_X509_EXTENSION_pop_free(extlist, X509_EXTENSION_free);
+	return i;
+	}
+
+/* Config database functions */
+
+char * X509V3_get_string(X509V3_CTX *ctx, char *name, char *section)
+	{
+	if(!ctx->db || !ctx->db_meth || !ctx->db_meth->get_string)
+		{
+		OPENSSL_PUT_ERROR(X509V3, X509V3_get_string, X509V3_R_OPERATION_NOT_DEFINED);
+		return NULL;
+		}
+	if (ctx->db_meth->get_string)
+			return ctx->db_meth->get_string(ctx->db, name, section);
+	return NULL;
+	}
+
+STACK_OF(CONF_VALUE) * X509V3_get_section(X509V3_CTX *ctx, char *section)
+	{
+	if(!ctx->db || !ctx->db_meth || !ctx->db_meth->get_section)
+		{
+		OPENSSL_PUT_ERROR(X509V3, X509V3_get_section, X509V3_R_OPERATION_NOT_DEFINED);
+		return NULL;
+		}
+	if (ctx->db_meth->get_section)
+			return ctx->db_meth->get_section(ctx->db, section);
+	return NULL;
+	}
+
+void X509V3_string_free(X509V3_CTX *ctx, char *str)
+	{
+	if (!str) return;
+	if (ctx->db_meth->free_string)
+			ctx->db_meth->free_string(ctx->db, str);
+	}
+
+void X509V3_section_free(X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *section)
+	{
+	if (!section) return;
+	if (ctx->db_meth->free_section)
+			ctx->db_meth->free_section(ctx->db, section);
+	}
+
+static char *nconf_get_string(void *db, char *section, char *value)
+	{
+        /* TODO(fork): this should return a const value. */
+	return (char *) NCONF_get_string(db, section, value);
+	}
+
+static STACK_OF(CONF_VALUE) *nconf_get_section(void *db, char *section)
+	{
+	return NCONF_get_section(db, section);
+	}
+
+static X509V3_CONF_METHOD nconf_method = {
+nconf_get_string,
+nconf_get_section,
+NULL,
+NULL
+};
+
+void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf)
+	{
+	ctx->db_meth = &nconf_method;
+	ctx->db = conf;
+	}
+
+void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subj, X509_REQ *req,
+		    X509_CRL *crl, int flags)
+	{
+	ctx->issuer_cert = issuer;
+	ctx->subject_cert = subj;
+	ctx->crl = crl;
+	ctx->subject_req = req;
+	ctx->flags = flags;
+	}
+
+/* TODO(fork): remove */
+#if 0
+/* Old conf compatibility functions */
+
+X509_EXTENSION *X509V3_EXT_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+				char *name, char *value)
+	{
+	CONF ctmp;
+	CONF_set_nconf(&ctmp, conf);
+	return X509V3_EXT_nconf(&ctmp, ctx, name, value);
+	}
+
+/* LHASH *conf:  Config file    */
+/* char *value:  Value    */
+X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+				    int ext_nid, char *value)
+	{
+	CONF ctmp;
+	CONF_set_nconf(&ctmp, conf);
+	return X509V3_EXT_nconf_nid(&ctmp, ctx, ext_nid, value);
+	}
+
+static char *conf_lhash_get_string(void *db, char *section, char *value)
+	{
+	return CONF_get_string(db, section, value);
+	}
+
+static STACK_OF(CONF_VALUE) *conf_lhash_get_section(void *db, char *section)
+	{
+	return CONF_get_section(db, section);
+	}
+
+static X509V3_CONF_METHOD conf_lhash_method = {
+conf_lhash_get_string,
+conf_lhash_get_section,
+NULL,
+NULL
+};
+
+void X509V3_set_conf_lhash(X509V3_CTX *ctx, LHASH_OF(CONF_VALUE) *lhash)
+	{
+	ctx->db_meth = &conf_lhash_method;
+	ctx->db = lhash;
+	}
+
+int X509V3_EXT_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+			char *section, X509 *cert)
+	{
+	CONF ctmp;
+	CONF_set_nconf(&ctmp, conf);
+	return X509V3_EXT_add_nconf(&ctmp, ctx, section, cert);
+	}
+
+/* Same as above but for a CRL */
+
+int X509V3_EXT_CRL_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+			    char *section, X509_CRL *crl)
+	{
+	CONF ctmp;
+	CONF_set_nconf(&ctmp, conf);
+	return X509V3_EXT_CRL_add_nconf(&ctmp, ctx, section, crl);
+	}
+
+/* Add extensions to certificate request */
+
+int X509V3_EXT_REQ_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+			    char *section, X509_REQ *req)
+	{
+	CONF ctmp;
+	CONF_set_nconf(&ctmp, conf);
+	return X509V3_EXT_REQ_add_nconf(&ctmp, ctx, section, req);
+	}
+#endif
diff --git a/crypto/x509v3/v3_cpols.c b/crypto/x509v3/v3_cpols.c
new file mode 100644
index 0000000..b3d6fef
--- /dev/null
+++ b/crypto/x509v3/v3_cpols.c
@@ -0,0 +1,460 @@
+/* v3_cpols.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/stack.h>
+#include <openssl/x509v3.h>
+
+#include "pcy_int.h"
+
+/* Certificate policies extension support: this one is a bit complex... */
+
+static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, BIO *out, int indent);
+static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, char *value);
+static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, int indent);
+static void print_notice(BIO *out, USERNOTICE *notice, int indent);
+static POLICYINFO *policy_section(X509V3_CTX *ctx,
+				 STACK_OF(CONF_VALUE) *polstrs, int ia5org);
+static POLICYQUALINFO *notice_section(X509V3_CTX *ctx,
+					STACK_OF(CONF_VALUE) *unot, int ia5org);
+static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos);
+
+const X509V3_EXT_METHOD v3_cpols = {
+NID_certificate_policies, 0,ASN1_ITEM_ref(CERTIFICATEPOLICIES),
+0,0,0,0,
+0,0,
+0,0,
+(X509V3_EXT_I2R)i2r_certpol,
+(X509V3_EXT_R2I)r2i_certpol,
+NULL
+};
+
+ASN1_ITEM_TEMPLATE(CERTIFICATEPOLICIES) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CERTIFICATEPOLICIES, POLICYINFO)
+ASN1_ITEM_TEMPLATE_END(CERTIFICATEPOLICIES)
+
+IMPLEMENT_ASN1_FUNCTIONS(CERTIFICATEPOLICIES)
+
+ASN1_SEQUENCE(POLICYINFO) = {
+	ASN1_SIMPLE(POLICYINFO, policyid, ASN1_OBJECT),
+	ASN1_SEQUENCE_OF_OPT(POLICYINFO, qualifiers, POLICYQUALINFO)
+} ASN1_SEQUENCE_END(POLICYINFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(POLICYINFO)
+
+ASN1_ADB_TEMPLATE(policydefault) = ASN1_SIMPLE(POLICYQUALINFO, d.other, ASN1_ANY);
+
+ASN1_ADB(POLICYQUALINFO) = {
+	ADB_ENTRY(NID_id_qt_cps, ASN1_SIMPLE(POLICYQUALINFO, d.cpsuri, ASN1_IA5STRING)),
+	ADB_ENTRY(NID_id_qt_unotice, ASN1_SIMPLE(POLICYQUALINFO, d.usernotice, USERNOTICE))
+} ASN1_ADB_END(POLICYQUALINFO, 0, pqualid, 0, &policydefault_tt, NULL);
+
+ASN1_SEQUENCE(POLICYQUALINFO) = {
+	ASN1_SIMPLE(POLICYQUALINFO, pqualid, ASN1_OBJECT),
+	ASN1_ADB_OBJECT(POLICYQUALINFO)
+} ASN1_SEQUENCE_END(POLICYQUALINFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(POLICYQUALINFO)
+
+ASN1_SEQUENCE(USERNOTICE) = {
+	ASN1_OPT(USERNOTICE, noticeref, NOTICEREF),
+	ASN1_OPT(USERNOTICE, exptext, DISPLAYTEXT)
+} ASN1_SEQUENCE_END(USERNOTICE)
+
+IMPLEMENT_ASN1_FUNCTIONS(USERNOTICE)
+
+ASN1_SEQUENCE(NOTICEREF) = {
+	ASN1_SIMPLE(NOTICEREF, organization, DISPLAYTEXT),
+	ASN1_SEQUENCE_OF(NOTICEREF, noticenos, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(NOTICEREF)
+
+IMPLEMENT_ASN1_FUNCTIONS(NOTICEREF)
+
+static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method,
+		X509V3_CTX *ctx, char *value)
+{
+	STACK_OF(POLICYINFO) *pols = NULL;
+	char *pstr;
+	POLICYINFO *pol;
+	ASN1_OBJECT *pobj;
+	STACK_OF(CONF_VALUE) *vals;
+	CONF_VALUE *cnf;
+	size_t i;
+	int ia5org;
+	pols = sk_POLICYINFO_new_null();
+	if (pols == NULL) {
+		OPENSSL_PUT_ERROR(X509V3, r2i_certpol, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	vals =  X509V3_parse_list(value);
+	if (vals == NULL) {
+		OPENSSL_PUT_ERROR(X509V3, r2i_certpol, ERR_R_X509V3_LIB);
+		goto err;
+	}
+	ia5org = 0;
+	for(i = 0; i < sk_CONF_VALUE_num(vals); i++) {
+		cnf = sk_CONF_VALUE_value(vals, i);
+		if(cnf->value || !cnf->name ) {
+			OPENSSL_PUT_ERROR(X509V3, r2i_certpol, X509V3_R_INVALID_POLICY_IDENTIFIER);
+			X509V3_conf_err(cnf);
+			goto err;
+		}
+		pstr = cnf->name;
+		if(!strcmp(pstr,"ia5org")) {
+			ia5org = 1;
+			continue;
+		} else if(*pstr == '@') {
+			STACK_OF(CONF_VALUE) *polsect;
+			polsect = X509V3_get_section(ctx, pstr + 1);
+			if(!polsect) {
+				OPENSSL_PUT_ERROR(X509V3, r2i_certpol, X509V3_R_INVALID_SECTION);
+
+				X509V3_conf_err(cnf);
+				goto err;
+			}
+			pol = policy_section(ctx, polsect, ia5org);
+			X509V3_section_free(ctx, polsect);
+			if(!pol) goto err;
+		} else {
+			if(!(pobj = OBJ_txt2obj(cnf->name, 0))) {
+				OPENSSL_PUT_ERROR(X509V3, r2i_certpol, X509V3_R_INVALID_OBJECT_IDENTIFIER);
+				X509V3_conf_err(cnf);
+				goto err;
+			}
+			pol = POLICYINFO_new();
+			pol->policyid = pobj;
+		}
+		if (!sk_POLICYINFO_push(pols, pol)){
+			POLICYINFO_free(pol);
+			OPENSSL_PUT_ERROR(X509V3, r2i_certpol, ERR_R_MALLOC_FAILURE);
+			goto err;
+		}
+	}
+	sk_CONF_VALUE_pop_free(vals, X509V3_conf_free);
+	return pols;
+	err:
+	sk_CONF_VALUE_pop_free(vals, X509V3_conf_free);
+	sk_POLICYINFO_pop_free(pols, POLICYINFO_free);
+	return NULL;
+}
+
+static POLICYINFO *policy_section(X509V3_CTX *ctx,
+				STACK_OF(CONF_VALUE) *polstrs, int ia5org)
+{
+	size_t i;
+	CONF_VALUE *cnf;
+	POLICYINFO *pol;
+	POLICYQUALINFO *qual;
+	if(!(pol = POLICYINFO_new())) goto merr;
+	for(i = 0; i < sk_CONF_VALUE_num(polstrs); i++) {
+		cnf = sk_CONF_VALUE_value(polstrs, i);
+		if(!strcmp(cnf->name, "policyIdentifier")) {
+			ASN1_OBJECT *pobj;
+			if(!(pobj = OBJ_txt2obj(cnf->value, 0))) {
+				OPENSSL_PUT_ERROR(X509V3, policy_section, X509V3_R_INVALID_OBJECT_IDENTIFIER);
+				X509V3_conf_err(cnf);
+				goto err;
+			}
+			pol->policyid = pobj;
+
+		} else if(!name_cmp(cnf->name, "CPS")) {
+			if(!pol->qualifiers) pol->qualifiers =
+						 sk_POLICYQUALINFO_new_null();
+			if(!(qual = POLICYQUALINFO_new())) goto merr;
+			if(!sk_POLICYQUALINFO_push(pol->qualifiers, qual))
+								 goto merr;
+                        /* TODO(fork): const correctness */
+			qual->pqualid = (ASN1_OBJECT*) OBJ_nid2obj(NID_id_qt_cps);
+			qual->d.cpsuri = M_ASN1_IA5STRING_new();
+			if(!ASN1_STRING_set(qual->d.cpsuri, cnf->value,
+						 strlen(cnf->value))) goto merr;
+		} else if(!name_cmp(cnf->name, "userNotice")) {
+			STACK_OF(CONF_VALUE) *unot;
+			if(*cnf->value != '@') {
+				OPENSSL_PUT_ERROR(X509V3, policy_section, X509V3_R_EXPECTED_A_SECTION_NAME);
+				X509V3_conf_err(cnf);
+				goto err;
+			}
+			unot = X509V3_get_section(ctx, cnf->value + 1);
+			if(!unot) {
+				OPENSSL_PUT_ERROR(X509V3, policy_section, X509V3_R_INVALID_SECTION);
+
+				X509V3_conf_err(cnf);
+				goto err;
+			}
+			qual = notice_section(ctx, unot, ia5org);
+			X509V3_section_free(ctx, unot);
+			if(!qual) goto err;
+			if(!pol->qualifiers) pol->qualifiers =
+						 sk_POLICYQUALINFO_new_null();
+			if(!sk_POLICYQUALINFO_push(pol->qualifiers, qual))
+								 goto merr;
+		} else {
+			OPENSSL_PUT_ERROR(X509V3, policy_section, X509V3_R_INVALID_OPTION);
+
+			X509V3_conf_err(cnf);
+			goto err;
+		}
+	}
+	if(!pol->policyid) {
+		OPENSSL_PUT_ERROR(X509V3, policy_section, X509V3_R_NO_POLICY_IDENTIFIER);
+		goto err;
+	}
+
+	return pol;
+
+	merr:
+	OPENSSL_PUT_ERROR(X509V3, policy_section, ERR_R_MALLOC_FAILURE);
+
+	err:
+	POLICYINFO_free(pol);
+	return NULL;
+	
+	
+}
+
+static POLICYQUALINFO *notice_section(X509V3_CTX *ctx,
+					STACK_OF(CONF_VALUE) *unot, int ia5org)
+{
+	size_t i;
+	int ret;
+	CONF_VALUE *cnf;
+	USERNOTICE *not;
+	POLICYQUALINFO *qual;
+	if(!(qual = POLICYQUALINFO_new())) goto merr;
+        /* TODO(fork): const correctness */
+	qual->pqualid = (ASN1_OBJECT *) OBJ_nid2obj(NID_id_qt_unotice);
+	if(!(not = USERNOTICE_new())) goto merr;
+	qual->d.usernotice = not;
+	for(i = 0; i < sk_CONF_VALUE_num(unot); i++) {
+		cnf = sk_CONF_VALUE_value(unot, i);
+		if(!strcmp(cnf->name, "explicitText")) {
+			not->exptext = M_ASN1_VISIBLESTRING_new();
+			if(!ASN1_STRING_set(not->exptext, cnf->value,
+						 strlen(cnf->value))) goto merr;
+		} else if(!strcmp(cnf->name, "organization")) {
+			NOTICEREF *nref;
+			if(!not->noticeref) {
+				if(!(nref = NOTICEREF_new())) goto merr;
+				not->noticeref = nref;
+			} else nref = not->noticeref;
+			if(ia5org) nref->organization->type = V_ASN1_IA5STRING;
+			else nref->organization->type = V_ASN1_VISIBLESTRING;
+			if(!ASN1_STRING_set(nref->organization, cnf->value,
+						 strlen(cnf->value))) goto merr;
+		} else if(!strcmp(cnf->name, "noticeNumbers")) {
+			NOTICEREF *nref;
+			STACK_OF(CONF_VALUE) *nos;
+			if(!not->noticeref) {
+				if(!(nref = NOTICEREF_new())) goto merr;
+				not->noticeref = nref;
+			} else nref = not->noticeref;
+			nos = X509V3_parse_list(cnf->value);
+			if(!nos || !sk_CONF_VALUE_num(nos)) {
+				OPENSSL_PUT_ERROR(X509V3, notice_section, X509V3_R_INVALID_NUMBERS);
+				X509V3_conf_err(cnf);
+				goto err;
+			}
+			ret = nref_nos(nref->noticenos, nos);
+			sk_CONF_VALUE_pop_free(nos, X509V3_conf_free);
+			if (!ret)
+				goto err;
+		} else {
+			OPENSSL_PUT_ERROR(X509V3, notice_section, X509V3_R_INVALID_OPTION);
+			X509V3_conf_err(cnf);
+			goto err;
+		}
+	}
+
+	if(not->noticeref && 
+	      (!not->noticeref->noticenos || !not->noticeref->organization)) {
+			OPENSSL_PUT_ERROR(X509V3, notice_section, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS);
+			goto err;
+	}
+
+	return qual;
+
+	merr:
+	OPENSSL_PUT_ERROR(X509V3, notice_section, ERR_R_MALLOC_FAILURE);
+
+	err:
+	POLICYQUALINFO_free(qual);
+	return NULL;
+}
+
+static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos)
+{
+	CONF_VALUE *cnf;
+	ASN1_INTEGER *aint;
+
+	size_t i;
+
+	for(i = 0; i < sk_CONF_VALUE_num(nos); i++) {
+		cnf = sk_CONF_VALUE_value(nos, i);
+		if(!(aint = s2i_ASN1_INTEGER(NULL, cnf->name))) {
+			OPENSSL_PUT_ERROR(X509V3, nref_nos, X509V3_R_INVALID_NUMBER);
+			goto err;
+		}
+		if(!sk_ASN1_INTEGER_push(nnums, aint)) goto merr;
+	}
+	return 1;
+
+	merr:
+	OPENSSL_PUT_ERROR(X509V3, nref_nos, ERR_R_MALLOC_FAILURE);
+
+	err:
+	sk_ASN1_INTEGER_pop_free(nnums, ASN1_STRING_free);
+	return 0;
+}
+
+
+static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol,
+		BIO *out, int indent)
+{
+	size_t i;
+	POLICYINFO *pinfo;
+	/* First print out the policy OIDs */
+	for(i = 0; i < sk_POLICYINFO_num(pol); i++) {
+		pinfo = sk_POLICYINFO_value(pol, i);
+		BIO_printf(out, "%*sPolicy: ", indent, "");
+		i2a_ASN1_OBJECT(out, pinfo->policyid);
+		BIO_puts(out, "\n");
+		if(pinfo->qualifiers)
+			 print_qualifiers(out, pinfo->qualifiers, indent + 2);
+	}
+	return 1;
+}
+
+static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals,
+		int indent)
+{
+	POLICYQUALINFO *qualinfo;
+	size_t i;
+	for(i = 0; i < sk_POLICYQUALINFO_num(quals); i++) {
+		qualinfo = sk_POLICYQUALINFO_value(quals, i);
+		switch(OBJ_obj2nid(qualinfo->pqualid))
+		{
+			case NID_id_qt_cps:
+			BIO_printf(out, "%*sCPS: %s\n", indent, "",
+						qualinfo->d.cpsuri->data);
+			break;
+		
+			case NID_id_qt_unotice:
+			BIO_printf(out, "%*sUser Notice:\n", indent, "");
+			print_notice(out, qualinfo->d.usernotice, indent + 2);
+			break;
+
+			default:
+			BIO_printf(out, "%*sUnknown Qualifier: ",
+							 indent + 2, "");
+			
+			i2a_ASN1_OBJECT(out, qualinfo->pqualid);
+			BIO_puts(out, "\n");
+			break;
+		}
+	}
+}
+
+static void print_notice(BIO *out, USERNOTICE *notice, int indent)
+{
+	size_t i;
+	if(notice->noticeref) {
+		NOTICEREF *ref;
+		ref = notice->noticeref;
+		BIO_printf(out, "%*sOrganization: %s\n", indent, "",
+						 ref->organization->data);
+		BIO_printf(out, "%*sNumber%s: ", indent, "",
+			   sk_ASN1_INTEGER_num(ref->noticenos) > 1 ? "s" : "");
+		for(i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) {
+			ASN1_INTEGER *num;
+			char *tmp;
+			num = sk_ASN1_INTEGER_value(ref->noticenos, i);
+			if(i) BIO_puts(out, ", ");
+			tmp = i2s_ASN1_INTEGER(NULL, num);
+			BIO_puts(out, tmp);
+			OPENSSL_free(tmp);
+		}
+		BIO_puts(out, "\n");
+	}
+	if(notice->exptext)
+		BIO_printf(out, "%*sExplicit Text: %s\n", indent, "",
+							 notice->exptext->data);
+}
+
+void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent)
+	{
+	const X509_POLICY_DATA *dat = node->data;
+
+	BIO_printf(out, "%*sPolicy: ", indent, "");
+			
+	i2a_ASN1_OBJECT(out, dat->valid_policy);
+	BIO_puts(out, "\n");
+	BIO_printf(out, "%*s%s\n", indent + 2, "",
+		node_data_critical(dat) ? "Critical" : "Non Critical");
+	if (dat->qualifier_set)
+		print_qualifiers(out, dat->qualifier_set, indent + 2);
+	else
+		BIO_printf(out, "%*sNo Qualifiers\n", indent + 2, "");
+	}
diff --git a/crypto/x509v3/v3_crld.c b/crypto/x509v3/v3_crld.c
new file mode 100644
index 0000000..e2bd2ad
--- /dev/null
+++ b/crypto/x509v3/v3_crld.c
@@ -0,0 +1,615 @@
+/* v3_crld.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static void *v2i_crld(const X509V3_EXT_METHOD *method,
+		      X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out,
+		     int indent);
+
+const X509V3_EXT_METHOD v3_crld =
+	{
+	NID_crl_distribution_points, 0, ASN1_ITEM_ref(CRL_DIST_POINTS),
+	0,0,0,0,
+	0,0,
+	0,
+	v2i_crld,
+	i2r_crldp,0,
+	NULL
+	};
+
+const X509V3_EXT_METHOD v3_freshest_crl =
+	{
+	NID_freshest_crl, 0, ASN1_ITEM_ref(CRL_DIST_POINTS),
+	0,0,0,0,
+	0,0,
+	0,
+	v2i_crld,
+	i2r_crldp,0,
+	NULL
+	};
+
+static STACK_OF(GENERAL_NAME) *gnames_from_sectname(X509V3_CTX *ctx, char *sect)
+	{
+	STACK_OF(CONF_VALUE) *gnsect;
+	STACK_OF(GENERAL_NAME) *gens;
+	if (*sect == '@')
+		gnsect = X509V3_get_section(ctx, sect + 1);
+	else
+		gnsect = X509V3_parse_list(sect);
+	if (!gnsect)
+		{
+		OPENSSL_PUT_ERROR(X509V3, gnames_from_sectname, X509V3_R_SECTION_NOT_FOUND);
+		return NULL;
+		}
+	gens = v2i_GENERAL_NAMES(NULL, ctx, gnsect);
+	if (*sect == '@')
+		X509V3_section_free(ctx, gnsect);
+	else
+		sk_CONF_VALUE_pop_free(gnsect, X509V3_conf_free);
+	return gens;
+	}
+
+static int set_dist_point_name(DIST_POINT_NAME **pdp, X509V3_CTX *ctx,
+							CONF_VALUE *cnf)
+	{
+	STACK_OF(GENERAL_NAME) *fnm = NULL;
+	STACK_OF(X509_NAME_ENTRY) *rnm = NULL;
+	if (!strncmp(cnf->name, "fullname", 9))
+		{
+		fnm = gnames_from_sectname(ctx, cnf->value);
+		if (!fnm)
+			goto err;
+		}
+	else if (!strcmp(cnf->name, "relativename"))
+		{
+		int ret;
+		STACK_OF(CONF_VALUE) *dnsect;
+		X509_NAME *nm;
+		nm = X509_NAME_new();
+		if (!nm)
+			return -1;
+		dnsect = X509V3_get_section(ctx, cnf->value);
+		if (!dnsect)
+			{
+			OPENSSL_PUT_ERROR(X509V3, set_dist_point_name, X509V3_R_SECTION_NOT_FOUND);
+			return -1;
+			}
+		ret = X509V3_NAME_from_section(nm, dnsect, MBSTRING_ASC);
+		X509V3_section_free(ctx, dnsect);
+		rnm = nm->entries;
+		nm->entries = NULL;
+		X509_NAME_free(nm);
+		if (!ret || sk_X509_NAME_ENTRY_num(rnm) <= 0)
+			goto err;
+		/* Since its a name fragment can't have more than one
+		 * RDNSequence
+		 */
+		if (sk_X509_NAME_ENTRY_value(rnm,
+				sk_X509_NAME_ENTRY_num(rnm) - 1)->set)
+			{
+			OPENSSL_PUT_ERROR(X509V3, set_dist_point_name, X509V3_R_INVALID_MULTIPLE_RDNS);
+			goto err;
+			}
+		}
+	else
+		return 0;
+
+	if (*pdp)
+		{
+		OPENSSL_PUT_ERROR(X509V3, set_dist_point_name, X509V3_R_DISTPOINT_ALREADY_SET);
+		goto err;
+		}
+
+	*pdp = DIST_POINT_NAME_new();
+	if (!*pdp)
+		goto err;
+	if (fnm)
+		{
+		(*pdp)->type = 0;
+		(*pdp)->name.fullname = fnm;
+		}
+	else
+		{
+		(*pdp)->type = 1;
+		(*pdp)->name.relativename = rnm;
+		}
+
+	return 1;
+		
+	err:
+	if (fnm)
+		sk_GENERAL_NAME_pop_free(fnm, GENERAL_NAME_free);
+	if (rnm)
+		sk_X509_NAME_ENTRY_pop_free(rnm, X509_NAME_ENTRY_free);
+	return -1;
+	}
+
+static const BIT_STRING_BITNAME reason_flags[] = {
+{0, "Unused", "unused"},
+{1, "Key Compromise", "keyCompromise"},
+{2, "CA Compromise", "CACompromise"},
+{3, "Affiliation Changed", "affiliationChanged"},
+{4, "Superseded", "superseded"},
+{5, "Cessation Of Operation", "cessationOfOperation"},
+{6, "Certificate Hold", "certificateHold"},
+{7, "Privilege Withdrawn", "privilegeWithdrawn"},
+{8, "AA Compromise", "AACompromise"},
+{-1, NULL, NULL}
+};
+
+static int set_reasons(ASN1_BIT_STRING **preas, char *value)
+	{
+	STACK_OF(CONF_VALUE) *rsk = NULL;
+	const BIT_STRING_BITNAME *pbn;
+	const char *bnam;
+	size_t i;
+	int ret = 0;
+	rsk = X509V3_parse_list(value);
+	if (!rsk)
+		return 0;
+	if (*preas)
+		return 0;
+	for (i = 0; i < sk_CONF_VALUE_num(rsk); i++)
+		{
+		bnam = sk_CONF_VALUE_value(rsk, i)->name;
+		if (!*preas)
+			{
+			*preas = ASN1_BIT_STRING_new();
+			if (!*preas)
+				goto err;
+			}
+		for (pbn = reason_flags; pbn->lname; pbn++)
+			{
+			if (!strcmp(pbn->sname, bnam))
+				{
+				if (!ASN1_BIT_STRING_set_bit(*preas,
+							pbn->bitnum, 1))
+					goto err;
+				break;
+				}
+			}
+		if (!pbn->lname)
+			goto err;
+		}
+	ret = 1;
+
+	err:
+	sk_CONF_VALUE_pop_free(rsk, X509V3_conf_free);
+	return ret;
+	}
+
+static int print_reasons(BIO *out, const char *rname,
+			ASN1_BIT_STRING *rflags, int indent)
+	{
+	int first = 1;
+	const BIT_STRING_BITNAME *pbn;
+	BIO_printf(out, "%*s%s:\n%*s", indent, "", rname, indent + 2, "");
+	for (pbn = reason_flags; pbn->lname; pbn++)
+		{
+		if (ASN1_BIT_STRING_get_bit(rflags, pbn->bitnum))
+			{
+			if (first)
+				first = 0;
+			else
+				BIO_puts(out, ", ");
+			BIO_puts(out, pbn->lname);
+			}
+		}
+	if (first)
+		BIO_puts(out, "<EMPTY>\n");
+	else
+		BIO_puts(out, "\n");
+	return 1;
+	}
+
+static DIST_POINT *crldp_from_section(X509V3_CTX *ctx,
+						STACK_OF(CONF_VALUE) *nval)
+	{
+	size_t i;
+	CONF_VALUE *cnf;
+	DIST_POINT *point = NULL;
+	point = DIST_POINT_new();
+	if (!point)
+		goto err;
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++)
+		{
+		int ret;
+		cnf = sk_CONF_VALUE_value(nval, i);
+		ret = set_dist_point_name(&point->distpoint, ctx, cnf);
+		if (ret > 0)
+			continue;
+		if (ret < 0)
+			goto err;
+		if (!strcmp(cnf->name, "reasons"))
+			{
+			if (!set_reasons(&point->reasons, cnf->value))
+				goto err;
+			}
+		else if (!strcmp(cnf->name, "CRLissuer"))
+			{
+			point->CRLissuer =
+				gnames_from_sectname(ctx, cnf->value);
+			if (!point->CRLissuer)
+				goto err;
+			}
+		}
+
+	return point;
+			
+
+	err:
+	if (point)
+		DIST_POINT_free(point);
+	return NULL;
+	}
+
+static void *v2i_crld(const X509V3_EXT_METHOD *method,
+		      X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+	{
+	STACK_OF(DIST_POINT) *crld = NULL;
+	GENERAL_NAMES *gens = NULL;
+	GENERAL_NAME *gen = NULL;
+	CONF_VALUE *cnf;
+	size_t i;
+	if(!(crld = sk_DIST_POINT_new_null())) goto merr;
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		DIST_POINT *point;
+		cnf = sk_CONF_VALUE_value(nval, i);
+		if (!cnf->value)
+			{
+			STACK_OF(CONF_VALUE) *dpsect;
+			dpsect = X509V3_get_section(ctx, cnf->name);
+			if (!dpsect)
+				goto err;
+			point = crldp_from_section(ctx, dpsect);
+			X509V3_section_free(ctx, dpsect);
+			if (!point)
+				goto err;
+			if(!sk_DIST_POINT_push(crld, point))
+				{
+				DIST_POINT_free(point);
+				goto merr;
+				}
+			}
+		else
+			{
+			if(!(gen = v2i_GENERAL_NAME(method, ctx, cnf)))
+				goto err; 
+			if(!(gens = GENERAL_NAMES_new()))
+				goto merr;
+			if(!sk_GENERAL_NAME_push(gens, gen))
+				goto merr;
+			gen = NULL;
+			if(!(point = DIST_POINT_new()))
+				goto merr;
+			if(!sk_DIST_POINT_push(crld, point))
+				{
+				DIST_POINT_free(point);
+				goto merr;
+				}
+			if(!(point->distpoint = DIST_POINT_NAME_new()))
+				goto merr;
+			point->distpoint->name.fullname = gens;
+			point->distpoint->type = 0;
+			gens = NULL;
+			}
+	}
+	return crld;
+
+	merr:
+	OPENSSL_PUT_ERROR(X509V3, v2i_crld, ERR_R_MALLOC_FAILURE);
+	err:
+	GENERAL_NAME_free(gen);
+	GENERAL_NAMES_free(gens);
+	sk_DIST_POINT_pop_free(crld, DIST_POINT_free);
+	return NULL;
+}
+
+IMPLEMENT_ASN1_SET_OF(DIST_POINT)
+
+static int dpn_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+								void *exarg)
+	{
+	DIST_POINT_NAME *dpn = (DIST_POINT_NAME *)*pval;
+
+	switch(operation)
+		{
+		case ASN1_OP_NEW_POST:
+		dpn->dpname = NULL;
+		break;
+
+		case ASN1_OP_FREE_POST:
+		if (dpn->dpname)
+			X509_NAME_free(dpn->dpname);
+		break;
+		}
+	return 1;
+	}
+
+
+ASN1_CHOICE_cb(DIST_POINT_NAME, dpn_cb) = {
+	ASN1_IMP_SEQUENCE_OF(DIST_POINT_NAME, name.fullname, GENERAL_NAME, 0),
+	ASN1_IMP_SET_OF(DIST_POINT_NAME, name.relativename, X509_NAME_ENTRY, 1)
+} ASN1_CHOICE_END_cb(DIST_POINT_NAME, DIST_POINT_NAME, type)
+
+
+IMPLEMENT_ASN1_FUNCTIONS(DIST_POINT_NAME)
+
+ASN1_SEQUENCE(DIST_POINT) = {
+	ASN1_EXP_OPT(DIST_POINT, distpoint, DIST_POINT_NAME, 0),
+	ASN1_IMP_OPT(DIST_POINT, reasons, ASN1_BIT_STRING, 1),
+	ASN1_IMP_SEQUENCE_OF_OPT(DIST_POINT, CRLissuer, GENERAL_NAME, 2)
+} ASN1_SEQUENCE_END(DIST_POINT)
+
+IMPLEMENT_ASN1_FUNCTIONS(DIST_POINT)
+
+ASN1_ITEM_TEMPLATE(CRL_DIST_POINTS) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CRLDistributionPoints, DIST_POINT)
+ASN1_ITEM_TEMPLATE_END(CRL_DIST_POINTS)
+
+IMPLEMENT_ASN1_FUNCTIONS(CRL_DIST_POINTS)
+
+ASN1_SEQUENCE(ISSUING_DIST_POINT) = {
+	ASN1_EXP_OPT(ISSUING_DIST_POINT, distpoint, DIST_POINT_NAME, 0),
+	ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyuser, ASN1_FBOOLEAN, 1),
+	ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyCA, ASN1_FBOOLEAN, 2),
+	ASN1_IMP_OPT(ISSUING_DIST_POINT, onlysomereasons, ASN1_BIT_STRING, 3),
+	ASN1_IMP_OPT(ISSUING_DIST_POINT, indirectCRL, ASN1_FBOOLEAN, 4),
+	ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyattr, ASN1_FBOOLEAN, 5)
+} ASN1_SEQUENCE_END(ISSUING_DIST_POINT)
+
+IMPLEMENT_ASN1_FUNCTIONS(ISSUING_DIST_POINT)
+
+static int i2r_idp(const X509V3_EXT_METHOD *method, void *pidp, BIO *out,
+		   int indent);
+static void *v2i_idp(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+		     STACK_OF(CONF_VALUE) *nval);
+
+const X509V3_EXT_METHOD v3_idp =
+	{
+	NID_issuing_distribution_point, X509V3_EXT_MULTILINE,
+	ASN1_ITEM_ref(ISSUING_DIST_POINT),
+	0,0,0,0,
+	0,0,
+	0,
+	v2i_idp,
+	i2r_idp,0,
+	NULL
+	};
+
+static void *v2i_idp(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+		     STACK_OF(CONF_VALUE) *nval)
+	{
+	ISSUING_DIST_POINT *idp = NULL;
+	CONF_VALUE *cnf;
+	char *name, *val;
+	size_t i;
+	int ret;
+	idp = ISSUING_DIST_POINT_new();
+	if (!idp)
+		goto merr;
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++)
+		{
+		cnf = sk_CONF_VALUE_value(nval, i);
+		name = cnf->name;
+		val = cnf->value;
+		ret = set_dist_point_name(&idp->distpoint, ctx, cnf);
+		if (ret > 0)
+			continue;
+		if (ret < 0)
+			goto err;
+		if (!strcmp(name, "onlyuser"))
+			{
+			if (!X509V3_get_value_bool(cnf, &idp->onlyuser))
+				goto err;
+			}
+		else if (!strcmp(name, "onlyCA"))
+			{
+			if (!X509V3_get_value_bool(cnf, &idp->onlyCA))
+				goto err;
+			}
+		else if (!strcmp(name, "onlyAA"))
+			{
+			if (!X509V3_get_value_bool(cnf, &idp->onlyattr))
+				goto err;
+			}
+		else if (!strcmp(name, "indirectCRL"))
+			{
+			if (!X509V3_get_value_bool(cnf, &idp->indirectCRL))
+				goto err;
+			}
+		else if (!strcmp(name, "onlysomereasons"))
+			{
+			if (!set_reasons(&idp->onlysomereasons, val))
+				goto err;
+			}
+		else
+			{
+                        OPENSSL_PUT_ERROR(X509V3, v2i_idp, X509V3_R_INVALID_NAME);
+                        X509V3_conf_err(cnf);
+                        goto err;
+			}
+		}
+	return idp;
+
+	merr:
+	OPENSSL_PUT_ERROR(X509V3, v2i_idp, ERR_R_MALLOC_FAILURE);
+	err:
+	ISSUING_DIST_POINT_free(idp);
+	return NULL;
+	}
+
+static int print_gens(BIO *out, STACK_OF(GENERAL_NAME) *gens, int indent)
+	{
+	size_t i;
+	for (i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+		{
+		BIO_printf(out, "%*s", indent + 2, "");
+		GENERAL_NAME_print(out, sk_GENERAL_NAME_value(gens, i));
+		BIO_puts(out, "\n");
+		}
+	return 1;
+	}
+
+static int print_distpoint(BIO *out, DIST_POINT_NAME *dpn, int indent)
+	{
+	if (dpn->type == 0)
+		{
+		BIO_printf(out, "%*sFull Name:\n", indent, "");
+		print_gens(out, dpn->name.fullname, indent);
+		}
+	else
+		{
+		X509_NAME ntmp;
+		ntmp.entries = dpn->name.relativename;
+		BIO_printf(out, "%*sRelative Name:\n%*s",
+						indent, "", indent + 2, "");
+		X509_NAME_print_ex(out, &ntmp, 0, XN_FLAG_ONELINE);
+		BIO_puts(out, "\n");
+		}
+	return 1;
+	}
+
+static int i2r_idp(const X509V3_EXT_METHOD *method, void *pidp, BIO *out,
+		   int indent)
+	{
+	ISSUING_DIST_POINT *idp = pidp;
+	if (idp->distpoint)
+		print_distpoint(out, idp->distpoint, indent);
+	if (idp->onlyuser > 0)
+		BIO_printf(out, "%*sOnly User Certificates\n", indent, "");
+	if (idp->onlyCA > 0)
+		BIO_printf(out, "%*sOnly CA Certificates\n", indent, "");
+	if (idp->indirectCRL > 0)
+		BIO_printf(out, "%*sIndirect CRL\n", indent, "");
+	if (idp->onlysomereasons)
+		print_reasons(out, "Only Some Reasons", 
+				idp->onlysomereasons, indent);
+	if (idp->onlyattr > 0)
+		BIO_printf(out, "%*sOnly Attribute Certificates\n", indent, "");
+	if (!idp->distpoint && (idp->onlyuser <= 0) && (idp->onlyCA <= 0)
+		&& (idp->indirectCRL <= 0) && !idp->onlysomereasons
+		&& (idp->onlyattr <= 0))
+		BIO_printf(out, "%*s<EMPTY>\n", indent, "");
+		
+	return 1;
+	}
+
+static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out,
+		     int indent)
+	{
+	STACK_OF(DIST_POINT) *crld = pcrldp;
+	DIST_POINT *point;
+	size_t i;
+	for(i = 0; i < sk_DIST_POINT_num(crld); i++)
+		{
+		BIO_puts(out, "\n");
+		point = sk_DIST_POINT_value(crld, i);
+		if(point->distpoint)
+			print_distpoint(out, point->distpoint, indent);
+		if(point->reasons) 
+			print_reasons(out, "Reasons", point->reasons,
+								indent);
+		if(point->CRLissuer)
+			{
+			BIO_printf(out, "%*sCRL Issuer:\n", indent, "");
+			print_gens(out, point->CRLissuer, indent);
+			}
+		}
+	return 1;
+	}
+
+int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname)
+	{
+	size_t i;
+	STACK_OF(X509_NAME_ENTRY) *frag;
+	X509_NAME_ENTRY *ne;
+	if (!dpn || (dpn->type != 1))
+		return 1;
+	frag = dpn->name.relativename;
+	dpn->dpname = X509_NAME_dup(iname);
+	if (!dpn->dpname)
+		return 0;
+	for (i = 0; i < sk_X509_NAME_ENTRY_num(frag); i++)
+		{
+		ne = sk_X509_NAME_ENTRY_value(frag, i);
+		if (!X509_NAME_add_entry(dpn->dpname, ne, -1, i ? 0 : 1))
+			{
+			X509_NAME_free(dpn->dpname);
+			dpn->dpname = NULL;
+			return 0;
+			}
+		}
+	/* generate cached encoding of name */
+	if (i2d_X509_NAME(dpn->dpname, NULL) < 0)
+		{
+		X509_NAME_free(dpn->dpname);
+		dpn->dpname = NULL;
+		return 0;
+		}
+	return 1;
+	}
diff --git a/crypto/x509v3/v3_enum.c b/crypto/x509v3/v3_enum.c
new file mode 100644
index 0000000..d6a1b8b
--- /dev/null
+++ b/crypto/x509v3/v3_enum.c
@@ -0,0 +1,98 @@
+/* v3_enum.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/buf.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static ENUMERATED_NAMES crl_reasons[] = {
+{CRL_REASON_UNSPECIFIED, 	 "Unspecified", "unspecified"},
+{CRL_REASON_KEY_COMPROMISE,	 "Key Compromise", "keyCompromise"},
+{CRL_REASON_CA_COMPROMISE,	 "CA Compromise", "CACompromise"},
+{CRL_REASON_AFFILIATION_CHANGED, "Affiliation Changed", "affiliationChanged"},
+{CRL_REASON_SUPERSEDED, 	 "Superseded", "superseded"},
+{CRL_REASON_CESSATION_OF_OPERATION,
+			"Cessation Of Operation", "cessationOfOperation"},
+{CRL_REASON_CERTIFICATE_HOLD,	 "Certificate Hold", "certificateHold"},
+{CRL_REASON_REMOVE_FROM_CRL,	 "Remove From CRL", "removeFromCRL"},
+{CRL_REASON_PRIVILEGE_WITHDRAWN, "Privilege Withdrawn", "privilegeWithdrawn"},
+{CRL_REASON_AA_COMPROMISE,	 "AA Compromise", "AACompromise"},
+{-1, NULL, NULL}
+};
+
+const X509V3_EXT_METHOD v3_crl_reason = { 
+NID_crl_reason, 0, ASN1_ITEM_ref(ASN1_ENUMERATED),
+0,0,0,0,
+(X509V3_EXT_I2S)i2s_ASN1_ENUMERATED_TABLE,
+0,
+0,0,0,0,
+crl_reasons};
+
+
+char *i2s_ASN1_ENUMERATED_TABLE(X509V3_EXT_METHOD *method,
+	     ASN1_ENUMERATED *e)
+{
+	ENUMERATED_NAMES *enam;
+	long strval;
+	strval = ASN1_ENUMERATED_get(e);
+	for(enam = method->usr_data; enam->lname; enam++) {
+		if(strval == enam->bitnum) return BUF_strdup(enam->lname);
+	}
+	return i2s_ASN1_ENUMERATED(method, e);
+}
diff --git a/crypto/x509v3/v3_extku.c b/crypto/x509v3/v3_extku.c
new file mode 100644
index 0000000..f4b8af8
--- /dev/null
+++ b/crypto/x509v3/v3_extku.c
@@ -0,0 +1,145 @@
+/* v3_extku.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static void *v2i_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method,
+				    X509V3_CTX *ctx,
+				    STACK_OF(CONF_VALUE) *nval);
+static STACK_OF(CONF_VALUE) *i2v_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method,
+		void *eku, STACK_OF(CONF_VALUE) *extlist);
+
+const X509V3_EXT_METHOD v3_ext_ku = {
+	NID_ext_key_usage, 0,
+	ASN1_ITEM_ref(EXTENDED_KEY_USAGE),
+	0,0,0,0,
+	0,0,
+	i2v_EXTENDED_KEY_USAGE,
+	v2i_EXTENDED_KEY_USAGE,
+	0,0,
+	NULL
+};
+
+/* NB OCSP acceptable responses also is a SEQUENCE OF OBJECT */
+const X509V3_EXT_METHOD v3_ocsp_accresp = {
+	NID_id_pkix_OCSP_acceptableResponses, 0,
+	ASN1_ITEM_ref(EXTENDED_KEY_USAGE),
+	0,0,0,0,
+	0,0,
+	i2v_EXTENDED_KEY_USAGE,
+	v2i_EXTENDED_KEY_USAGE,
+	0,0,
+	NULL
+};
+
+ASN1_ITEM_TEMPLATE(EXTENDED_KEY_USAGE) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, EXTENDED_KEY_USAGE, ASN1_OBJECT)
+ASN1_ITEM_TEMPLATE_END(EXTENDED_KEY_USAGE)
+
+IMPLEMENT_ASN1_FUNCTIONS(EXTENDED_KEY_USAGE)
+
+static STACK_OF(CONF_VALUE) *
+  i2v_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method, void *a,
+			 STACK_OF(CONF_VALUE) *ext_list)
+{
+	EXTENDED_KEY_USAGE *eku = a;
+	size_t i;
+	ASN1_OBJECT *obj;
+	char obj_tmp[80];
+	for(i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
+		obj = sk_ASN1_OBJECT_value(eku, i);
+		i2t_ASN1_OBJECT(obj_tmp, 80, obj);
+		X509V3_add_value(NULL, obj_tmp, &ext_list);
+	}
+	return ext_list;
+}
+
+static void *v2i_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method,
+				    X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	EXTENDED_KEY_USAGE *extku;
+	char *extval;
+	ASN1_OBJECT *objtmp;
+	CONF_VALUE *val;
+	size_t i;
+
+	if(!(extku = sk_ASN1_OBJECT_new_null())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_EXTENDED_KEY_USAGE, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		val = sk_CONF_VALUE_value(nval, i);
+		if(val->value) extval = val->value;
+		else extval = val->name;
+		if(!(objtmp = OBJ_txt2obj(extval, 0))) {
+			sk_ASN1_OBJECT_pop_free(extku, ASN1_OBJECT_free);
+			OPENSSL_PUT_ERROR(X509V3, v2i_EXTENDED_KEY_USAGE, X509V3_R_INVALID_OBJECT_IDENTIFIER);
+			X509V3_conf_err(val);
+			return NULL;
+		}
+		sk_ASN1_OBJECT_push(extku, objtmp);
+	}
+	return extku;
+}
diff --git a/crypto/x509v3/v3_genn.c b/crypto/x509v3/v3_genn.c
new file mode 100644
index 0000000..8b0a68d
--- /dev/null
+++ b/crypto/x509v3/v3_genn.c
@@ -0,0 +1,252 @@
+/* v3_genn.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+ASN1_SEQUENCE(OTHERNAME) = {
+	ASN1_SIMPLE(OTHERNAME, type_id, ASN1_OBJECT),
+	/* Maybe have a true ANY DEFINED BY later */
+	ASN1_EXP(OTHERNAME, value, ASN1_ANY, 0)
+} ASN1_SEQUENCE_END(OTHERNAME)
+
+IMPLEMENT_ASN1_FUNCTIONS(OTHERNAME)
+
+ASN1_SEQUENCE(EDIPARTYNAME) = {
+	ASN1_IMP_OPT(EDIPARTYNAME, nameAssigner, DIRECTORYSTRING, 0),
+	ASN1_IMP_OPT(EDIPARTYNAME, partyName, DIRECTORYSTRING, 1)
+} ASN1_SEQUENCE_END(EDIPARTYNAME)
+
+IMPLEMENT_ASN1_FUNCTIONS(EDIPARTYNAME)
+
+ASN1_CHOICE(GENERAL_NAME) = {
+	ASN1_IMP(GENERAL_NAME, d.otherName, OTHERNAME, GEN_OTHERNAME),
+	ASN1_IMP(GENERAL_NAME, d.rfc822Name, ASN1_IA5STRING, GEN_EMAIL),
+	ASN1_IMP(GENERAL_NAME, d.dNSName, ASN1_IA5STRING, GEN_DNS),
+	/* Don't decode this */
+	ASN1_IMP(GENERAL_NAME, d.x400Address, ASN1_SEQUENCE, GEN_X400),
+	/* X509_NAME is a CHOICE type so use EXPLICIT */
+	ASN1_EXP(GENERAL_NAME, d.directoryName, X509_NAME, GEN_DIRNAME),
+	ASN1_IMP(GENERAL_NAME, d.ediPartyName, EDIPARTYNAME, GEN_EDIPARTY),
+	ASN1_IMP(GENERAL_NAME, d.uniformResourceIdentifier, ASN1_IA5STRING, GEN_URI),
+	ASN1_IMP(GENERAL_NAME, d.iPAddress, ASN1_OCTET_STRING, GEN_IPADD),
+	ASN1_IMP(GENERAL_NAME, d.registeredID, ASN1_OBJECT, GEN_RID)
+} ASN1_CHOICE_END(GENERAL_NAME)
+
+IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAME)
+
+ASN1_ITEM_TEMPLATE(GENERAL_NAMES) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, GeneralNames, GENERAL_NAME)
+ASN1_ITEM_TEMPLATE_END(GENERAL_NAMES)
+
+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);
+	}
+
+/* Returns 0 if they are equal, != 0 otherwise. */
+int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b)
+	{
+	int result = -1;
+
+	if (!a || !b || a->type != b->type) return -1;
+	switch(a->type)
+		{
+	case GEN_X400:
+	case GEN_EDIPARTY:
+		result = ASN1_TYPE_cmp(a->d.other, b->d.other);
+		break;
+
+	case GEN_OTHERNAME:
+		result = OTHERNAME_cmp(a->d.otherName, b->d.otherName);
+		break;
+
+	case GEN_EMAIL:
+	case GEN_DNS:
+	case GEN_URI:
+		result = ASN1_STRING_cmp(a->d.ia5, b->d.ia5);
+		break;
+
+	case GEN_DIRNAME:
+		result = X509_NAME_cmp(a->d.dirn, b->d.dirn);
+		break;
+
+	case GEN_IPADD:
+		result = ASN1_OCTET_STRING_cmp(a->d.ip, b->d.ip);
+		break;
+	
+	case GEN_RID:
+		result = OBJ_cmp(a->d.rid, b->d.rid);
+		break;
+		}
+	return result;
+	}
+
+/* Returns 0 if they are equal, != 0 otherwise. */
+int OTHERNAME_cmp(OTHERNAME *a, OTHERNAME *b)
+	{
+	int result = -1;
+
+	if (!a || !b) return -1;
+	/* Check their type first. */
+	if ((result = OBJ_cmp(a->type_id, b->type_id)) != 0)
+		return result;
+	/* Check the value. */
+	result = ASN1_TYPE_cmp(a->value, b->value);
+	return result;
+	}
+
+void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, void *value)
+	{
+	switch(type)
+		{
+	case GEN_X400:
+	case GEN_EDIPARTY:
+		a->d.other = value;
+		break;
+
+	case GEN_OTHERNAME:
+		a->d.otherName = value;
+		break;
+
+	case GEN_EMAIL:
+	case GEN_DNS:
+	case GEN_URI:
+		a->d.ia5 = value;
+		break;
+
+	case GEN_DIRNAME:
+		a->d.dirn = value;
+		break;
+
+	case GEN_IPADD:
+		a->d.ip = value;
+		break;
+	
+	case GEN_RID:
+		a->d.rid = value;
+		break;
+		}
+	a->type = type;
+	}
+
+void *GENERAL_NAME_get0_value(GENERAL_NAME *a, int *ptype)
+	{
+	if (ptype)
+		*ptype = a->type;
+	switch(a->type)
+		{
+	case GEN_X400:
+	case GEN_EDIPARTY:
+		return a->d.other;
+
+	case GEN_OTHERNAME:
+		return a->d.otherName;
+
+	case GEN_EMAIL:
+	case GEN_DNS:
+	case GEN_URI:
+		return a->d.ia5;
+
+	case GEN_DIRNAME:
+		return a->d.dirn;
+
+	case GEN_IPADD:
+		return a->d.ip;
+	
+	case GEN_RID:
+		return a->d.rid;
+
+	default:
+		return NULL;
+		}
+	}
+
+int GENERAL_NAME_set0_othername(GENERAL_NAME *gen,
+				ASN1_OBJECT *oid, ASN1_TYPE *value)
+	{
+	OTHERNAME *oth;
+	oth = OTHERNAME_new();
+	if (!oth)
+		return 0;
+	oth->type_id = oid;
+	oth->value = value;
+	GENERAL_NAME_set0_value(gen, GEN_OTHERNAME, oth);
+	return 1;
+	}
+
+int GENERAL_NAME_get0_otherName(GENERAL_NAME *gen, 
+				ASN1_OBJECT **poid, ASN1_TYPE **pvalue)
+	{
+	if (gen->type != GEN_OTHERNAME)
+		return 0;
+	if (poid)
+		*poid = gen->d.otherName->type_id;
+	if (pvalue)
+		*pvalue = gen->d.otherName->value;
+	return 1;
+	}
+
diff --git a/crypto/x509v3/v3_ia5.c b/crypto/x509v3/v3_ia5.c
new file mode 100644
index 0000000..04ef05c
--- /dev/null
+++ b/crypto/x509v3/v3_ia5.c
@@ -0,0 +1,120 @@
+/* v3_ia5.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static char *i2s_ASN1_IA5STRING(X509V3_EXT_METHOD *method, ASN1_IA5STRING *ia5);
+static ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, char *str);
+const X509V3_EXT_METHOD v3_ns_ia5_list[] = { 
+EXT_IA5STRING(NID_netscape_base_url),
+EXT_IA5STRING(NID_netscape_revocation_url),
+EXT_IA5STRING(NID_netscape_ca_revocation_url),
+EXT_IA5STRING(NID_netscape_renewal_url),
+EXT_IA5STRING(NID_netscape_ca_policy_url),
+EXT_IA5STRING(NID_netscape_ssl_server_name),
+EXT_IA5STRING(NID_netscape_comment),
+EXT_END
+};
+
+
+static char *i2s_ASN1_IA5STRING(X509V3_EXT_METHOD *method,
+	     ASN1_IA5STRING *ia5)
+{
+	char *tmp;
+	if(!ia5 || !ia5->length) return NULL;
+	if(!(tmp = OPENSSL_malloc(ia5->length + 1))) {
+		OPENSSL_PUT_ERROR(X509V3, i2s_ASN1_IA5STRING, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	memcpy(tmp, ia5->data, ia5->length);
+	tmp[ia5->length] = 0;
+	return tmp;
+}
+
+static ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method,
+	     X509V3_CTX *ctx, char *str)
+{
+	ASN1_IA5STRING *ia5;
+	if(!str) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_ASN1_IA5STRING, X509V3_R_INVALID_NULL_ARGUMENT);
+		return NULL;
+	}
+	if(!(ia5 = M_ASN1_IA5STRING_new())) goto err;
+	if(!ASN1_STRING_set((ASN1_STRING *)ia5, (unsigned char*)str,
+			    strlen(str))) {
+		M_ASN1_IA5STRING_free(ia5);
+		goto err;
+	}
+#ifdef CHARSET_EBCDIC
+        ebcdic2ascii(ia5->data, ia5->data, ia5->length);
+#endif /*CHARSET_EBCDIC*/
+	return ia5;
+	err:
+	OPENSSL_PUT_ERROR(X509V3, s2i_ASN1_IA5STRING, ERR_R_MALLOC_FAILURE);
+	return NULL;
+}
+
diff --git a/crypto/x509v3/v3_info.c b/crypto/x509v3/v3_info.c
new file mode 100644
index 0000000..4ef744b
--- /dev/null
+++ b/crypto/x509v3/v3_info.c
@@ -0,0 +1,199 @@
+/* v3_info.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/buf.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD *method,
+				AUTHORITY_INFO_ACCESS *ainfo,
+						STACK_OF(CONF_VALUE) *ret);
+static AUTHORITY_INFO_ACCESS *v2i_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+
+const X509V3_EXT_METHOD v3_info =
+{ NID_info_access, X509V3_EXT_MULTILINE, ASN1_ITEM_ref(AUTHORITY_INFO_ACCESS),
+0,0,0,0,
+0,0,
+(X509V3_EXT_I2V)i2v_AUTHORITY_INFO_ACCESS,
+(X509V3_EXT_V2I)v2i_AUTHORITY_INFO_ACCESS,
+0,0,
+NULL};
+
+const X509V3_EXT_METHOD v3_sinfo =
+{ NID_sinfo_access, X509V3_EXT_MULTILINE, ASN1_ITEM_ref(AUTHORITY_INFO_ACCESS),
+0,0,0,0,
+0,0,
+(X509V3_EXT_I2V)i2v_AUTHORITY_INFO_ACCESS,
+(X509V3_EXT_V2I)v2i_AUTHORITY_INFO_ACCESS,
+0,0,
+NULL};
+
+ASN1_SEQUENCE(ACCESS_DESCRIPTION) = {
+	ASN1_SIMPLE(ACCESS_DESCRIPTION, method, ASN1_OBJECT),
+	ASN1_SIMPLE(ACCESS_DESCRIPTION, location, GENERAL_NAME)
+} ASN1_SEQUENCE_END(ACCESS_DESCRIPTION)
+
+IMPLEMENT_ASN1_FUNCTIONS(ACCESS_DESCRIPTION)
+
+ASN1_ITEM_TEMPLATE(AUTHORITY_INFO_ACCESS) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, GeneralNames, ACCESS_DESCRIPTION)
+ASN1_ITEM_TEMPLATE_END(AUTHORITY_INFO_ACCESS)
+
+IMPLEMENT_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS)
+
+static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD *method,
+				AUTHORITY_INFO_ACCESS *ainfo,
+						STACK_OF(CONF_VALUE) *ret)
+{
+	ACCESS_DESCRIPTION *desc;
+	size_t i;
+	int nlen;
+	char objtmp[80], *ntmp;
+	CONF_VALUE *vtmp;
+	for(i = 0; i < sk_ACCESS_DESCRIPTION_num(ainfo); i++) {
+		desc = sk_ACCESS_DESCRIPTION_value(ainfo, i);
+		ret = i2v_GENERAL_NAME(method, desc->location, ret);
+		if(!ret) break;
+		vtmp = sk_CONF_VALUE_value(ret, i);
+		i2t_ASN1_OBJECT(objtmp, sizeof objtmp, desc->method);
+		nlen = strlen(objtmp) + strlen(vtmp->name) + 5;
+		ntmp = OPENSSL_malloc(nlen);
+		if(!ntmp) {
+			OPENSSL_PUT_ERROR(X509V3, i2v_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE);
+			return NULL;
+		}
+		BUF_strlcpy(ntmp, objtmp, nlen);
+		BUF_strlcat(ntmp, " - ", nlen);
+		BUF_strlcat(ntmp, vtmp->name, nlen);
+		OPENSSL_free(vtmp->name);
+		vtmp->name = ntmp;
+		
+	}
+	if(!ret) return sk_CONF_VALUE_new_null();
+	return ret;
+}
+
+static AUTHORITY_INFO_ACCESS *v2i_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	AUTHORITY_INFO_ACCESS *ainfo = NULL;
+	CONF_VALUE *cnf, ctmp;
+	ACCESS_DESCRIPTION *acc;
+	size_t i;
+	int objlen;
+	char *objtmp, *ptmp;
+	if(!(ainfo = sk_ACCESS_DESCRIPTION_new_null())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		cnf = sk_CONF_VALUE_value(nval, i);
+		if(!(acc = ACCESS_DESCRIPTION_new())
+			|| !sk_ACCESS_DESCRIPTION_push(ainfo, acc)) {
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE);
+			goto err;
+		}
+		ptmp = strchr(cnf->name, ';');
+		if(!ptmp) {
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_INFO_ACCESS, X509V3_R_INVALID_SYNTAX);
+			goto err;
+		}
+		objlen = ptmp - cnf->name;
+		ctmp.name = ptmp + 1;
+		ctmp.value = cnf->value;
+		if(!v2i_GENERAL_NAME_ex(acc->location, method, ctx, &ctmp, 0))
+								 goto err; 
+		if(!(objtmp = OPENSSL_malloc(objlen + 1))) {
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE);
+			goto err;
+		}
+		strncpy(objtmp, cnf->name, objlen);
+		objtmp[objlen] = 0;
+		acc->method = OBJ_txt2obj(objtmp, 0);
+		if(!acc->method) {
+			OPENSSL_PUT_ERROR(X509V3, v2i_AUTHORITY_INFO_ACCESS, X509V3_R_BAD_OBJECT);
+			ERR_add_error_data(2, "value=", objtmp);
+			OPENSSL_free(objtmp);
+			goto err;
+		}
+		OPENSSL_free(objtmp);
+
+	}
+	return ainfo;
+	err:
+	sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free);
+	return NULL;
+}
+
+int i2a_ACCESS_DESCRIPTION(BIO *bp, ACCESS_DESCRIPTION* a)
+        {
+	i2a_ASN1_OBJECT(bp, a->method);
+#ifdef UNDEF
+	i2a_GENERAL_NAME(bp, a->location);
+#endif
+	return 2;
+	}
diff --git a/crypto/x509v3/v3_int.c b/crypto/x509v3/v3_int.c
new file mode 100644
index 0000000..8ca23bd
--- /dev/null
+++ b/crypto/x509v3/v3_int.c
@@ -0,0 +1,87 @@
+/* v3_int.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+const X509V3_EXT_METHOD v3_crl_num = { 
+	NID_crl_number, 0, ASN1_ITEM_ref(ASN1_INTEGER),
+	0,0,0,0,
+	(X509V3_EXT_I2S)i2s_ASN1_INTEGER,
+	0,
+	0,0,0,0, NULL};
+
+const X509V3_EXT_METHOD v3_delta_crl = { 
+	NID_delta_crl, 0, ASN1_ITEM_ref(ASN1_INTEGER),
+	0,0,0,0,
+	(X509V3_EXT_I2S)i2s_ASN1_INTEGER,
+	0,
+	0,0,0,0, NULL};
+
+static void * s2i_asn1_int(X509V3_EXT_METHOD *meth, X509V3_CTX *ctx, char *value)
+	{
+	return s2i_ASN1_INTEGER(meth, value);
+	}
+
+const X509V3_EXT_METHOD v3_inhibit_anyp = { 
+	NID_inhibit_any_policy, 0, ASN1_ITEM_ref(ASN1_INTEGER),
+	0,0,0,0,
+	(X509V3_EXT_I2S)i2s_ASN1_INTEGER,
+	(X509V3_EXT_S2I)s2i_asn1_int,
+	0,0,0,0, NULL};
diff --git a/crypto/x509v3/v3_lib.c b/crypto/x509v3/v3_lib.c
new file mode 100644
index 0000000..b921499
--- /dev/null
+++ b/crypto/x509v3/v3_lib.c
@@ -0,0 +1,312 @@
+/* v3_lib.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* X509 v3 extension utilities */
+
+#include <stdio.h>
+
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+#include "ext_dat.h"
+static STACK_OF(X509V3_EXT_METHOD) *ext_list = NULL;
+
+static void ext_list_free(X509V3_EXT_METHOD *ext);
+
+static int ext_stack_cmp(const X509V3_EXT_METHOD **a, const X509V3_EXT_METHOD **b)
+{
+	return ((*a)->ext_nid - (*b)->ext_nid);
+}
+
+int X509V3_EXT_add(X509V3_EXT_METHOD *ext)
+{
+	if(!ext_list && !(ext_list = sk_X509V3_EXT_METHOD_new(ext_stack_cmp))) {
+		OPENSSL_PUT_ERROR(X509V3, X509V3_EXT_add, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	if(!sk_X509V3_EXT_METHOD_push(ext_list, ext)) {
+		OPENSSL_PUT_ERROR(X509V3, X509V3_EXT_add, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	return 1;
+}
+
+static int ext_cmp(const void *void_a,
+		   const void *void_b)
+{
+	const X509V3_EXT_METHOD **a = (const X509V3_EXT_METHOD**) void_a;
+	const X509V3_EXT_METHOD **b = (const X509V3_EXT_METHOD**) void_b;
+	return ext_stack_cmp(a, b);
+}
+
+const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid)
+{
+	X509V3_EXT_METHOD tmp;
+	const X509V3_EXT_METHOD *t = &tmp, * const *ret;
+	size_t idx;
+
+	if(nid < 0) return NULL;
+	tmp.ext_nid = nid;
+	ret = bsearch(&t, standard_exts, STANDARD_EXTENSION_COUNT, sizeof(X509V3_EXT_METHOD*), ext_cmp);
+	if(ret) return *ret;
+	if(!ext_list) return NULL;
+
+	if (!sk_X509V3_EXT_METHOD_find(ext_list, &idx, &tmp))
+		return NULL;
+	return sk_X509V3_EXT_METHOD_value(ext_list, idx);
+}
+
+const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *ext)
+{
+	int nid;
+	if((nid = OBJ_obj2nid(ext->object)) == NID_undef) return NULL;
+	return X509V3_EXT_get_nid(nid);
+}
+
+
+int X509V3_EXT_add_list(X509V3_EXT_METHOD *extlist)
+{
+	for(;extlist->ext_nid!=-1;extlist++) 
+			if(!X509V3_EXT_add(extlist)) return 0;
+	return 1;
+}
+
+int X509V3_EXT_add_alias(int nid_to, int nid_from)
+{
+	const X509V3_EXT_METHOD *ext;
+	X509V3_EXT_METHOD *tmpext;
+
+	if(!(ext = X509V3_EXT_get_nid(nid_from))) {
+		OPENSSL_PUT_ERROR(X509V3, X509V3_EXT_add_alias, X509V3_R_EXTENSION_NOT_FOUND);
+		return 0;
+	}
+	if(!(tmpext = (X509V3_EXT_METHOD *)OPENSSL_malloc(sizeof(X509V3_EXT_METHOD)))) {
+		OPENSSL_PUT_ERROR(X509V3, X509V3_EXT_add_alias, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	*tmpext = *ext;
+	tmpext->ext_nid = nid_to;
+	tmpext->ext_flags |= X509V3_EXT_DYNAMIC;
+	return X509V3_EXT_add(tmpext);
+}
+
+void X509V3_EXT_cleanup(void)
+{
+	sk_X509V3_EXT_METHOD_pop_free(ext_list, ext_list_free);
+	ext_list = NULL;
+}
+
+static void ext_list_free(X509V3_EXT_METHOD *ext)
+{
+	if(ext->ext_flags & X509V3_EXT_DYNAMIC) OPENSSL_free(ext);
+}
+
+/* Legacy function: we don't need to add standard extensions
+ * any more because they are now kept in ext_dat.h.
+ */
+
+int X509V3_add_standard_extensions(void)
+{
+	return 1;
+}
+
+/* Return an extension internal structure */
+
+void *X509V3_EXT_d2i(X509_EXTENSION *ext)
+{
+	const X509V3_EXT_METHOD *method;
+	const unsigned char *p;
+
+	if(!(method = X509V3_EXT_get(ext))) return NULL;
+	p = ext->value->data;
+	if(method->it) return ASN1_item_d2i(NULL, &p, ext->value->length, ASN1_ITEM_ptr(method->it));
+	return method->d2i(NULL, &p, ext->value->length);
+}
+
+/* Get critical flag and decoded version of extension from a NID.
+ * The "idx" variable returns the last found extension and can
+ * be used to retrieve multiple extensions of the same NID.
+ * However multiple extensions with the same NID is usually
+ * due to a badly encoded certificate so if idx is NULL we
+ * choke if multiple extensions exist.
+ * The "crit" variable is set to the critical value.
+ * The return value is the decoded extension or NULL on
+ * error. The actual error can have several different causes,
+ * the value of *crit reflects the cause:
+ * >= 0, extension found but not decoded (reflects critical value).
+ * -1 extension not found.
+ * -2 extension occurs more than once.
+ */
+
+void *X509V3_get_d2i(STACK_OF(X509_EXTENSION) *x, int nid, int *crit, int *idx)
+{
+	int lastpos;
+	size_t i;
+	X509_EXTENSION *ex, *found_ex = NULL;
+	if(!x) {
+		if(idx) *idx = -1;
+		if(crit) *crit = -1;
+		return NULL;
+	}
+	if(idx) lastpos = *idx + 1;
+	else lastpos = 0;
+	if(lastpos < 0) lastpos = 0;
+	for(i = lastpos; i < sk_X509_EXTENSION_num(x); i++)
+	{
+		ex = sk_X509_EXTENSION_value(x, i);
+		if(OBJ_obj2nid(ex->object) == nid) {
+			if(idx) {
+				*idx = i;
+				found_ex = ex;
+				break;
+			} else if(found_ex) {
+				/* Found more than one */
+				if(crit) *crit = -2;
+				return NULL;
+			}
+			found_ex = ex;
+		}
+	}
+	if(found_ex) {
+		/* Found it */
+		if(crit) *crit = X509_EXTENSION_get_critical(found_ex);
+		return X509V3_EXT_d2i(found_ex);
+	}
+
+	/* Extension not found */
+	if(idx) *idx = -1;
+	if(crit) *crit = -1;
+	return NULL;
+}
+
+/* This function is a general extension append, replace and delete utility.
+ * The precise operation is governed by the 'flags' value. The 'crit' and
+ * 'value' arguments (if relevant) are the extensions internal structure.
+ */
+
+int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value,
+					int crit, unsigned long flags)
+{
+	int extidx = -1;
+	int errcode;
+	X509_EXTENSION *ext, *extmp;
+	unsigned long ext_op = flags & X509V3_ADD_OP_MASK;
+
+	/* If appending we don't care if it exists, otherwise
+	 * look for existing extension.
+	 */
+	if(ext_op != X509V3_ADD_APPEND)
+		extidx = X509v3_get_ext_by_NID(*x, nid, -1);
+
+	/* See if extension exists */
+	if(extidx >= 0) {
+		/* If keep existing, nothing to do */
+		if(ext_op == X509V3_ADD_KEEP_EXISTING)
+			return 1;
+		/* If default then its an error */
+		if(ext_op == X509V3_ADD_DEFAULT) {
+			errcode = X509V3_R_EXTENSION_EXISTS;
+			goto err;
+		}
+		/* If delete, just delete it */
+		if(ext_op == X509V3_ADD_DELETE) {
+			if(!sk_X509_EXTENSION_delete(*x, extidx)) return -1;
+			return 1;
+		}
+	} else {
+		/* If replace existing or delete, error since 
+		 * extension must exist
+		 */
+		if((ext_op == X509V3_ADD_REPLACE_EXISTING) ||
+		   (ext_op == X509V3_ADD_DELETE)) {
+			errcode = X509V3_R_EXTENSION_NOT_FOUND;
+			goto err;
+		}
+	}
+
+	/* If we get this far then we have to create an extension:
+	 * could have some flags for alternative encoding schemes...
+	 */
+
+	ext = X509V3_EXT_i2d(nid, crit, value);
+
+	if(!ext) {
+		OPENSSL_PUT_ERROR(X509V3, X509V3_add1_i2d, X509V3_R_ERROR_CREATING_EXTENSION);
+		return 0;
+	}
+
+	/* If extension exists replace it.. */
+	if(extidx >= 0) {
+		extmp = sk_X509_EXTENSION_value(*x, extidx);
+		X509_EXTENSION_free(extmp);
+		if(!sk_X509_EXTENSION_set(*x, extidx, ext)) return -1;
+		return 1;
+	}
+
+	if(!*x && !(*x = sk_X509_EXTENSION_new_null())) return -1;
+	if(!sk_X509_EXTENSION_push(*x, ext)) return -1;
+
+	return 1;
+
+	err:
+	if(!(flags & X509V3_ADD_SILENT))
+		OPENSSL_PUT_ERROR(X509V3, X509V3_add1_i2d, errcode);
+	return 0;
+}
diff --git a/crypto/x509v3/v3_ncons.c b/crypto/x509v3/v3_ncons.c
new file mode 100644
index 0000000..a50a725
--- /dev/null
+++ b/crypto/x509v3/v3_ncons.c
@@ -0,0 +1,514 @@
+/* v3_ncons.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <strings.h>
+#endif
+
+
+static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
+				  X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, 
+				void *a, BIO *bp, int ind);
+static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
+				   STACK_OF(GENERAL_SUBTREE) *trees,
+				   BIO *bp, int ind, char *name);
+static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
+
+static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
+static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen);
+static int nc_dn(X509_NAME *sub, X509_NAME *nm);
+static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
+static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
+static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
+
+const X509V3_EXT_METHOD v3_name_constraints = {
+	NID_name_constraints, 0,
+	ASN1_ITEM_ref(NAME_CONSTRAINTS),
+	0,0,0,0,
+	0,0,
+	0, v2i_NAME_CONSTRAINTS,
+	i2r_NAME_CONSTRAINTS,0,
+	NULL
+};
+
+ASN1_SEQUENCE(GENERAL_SUBTREE) = {
+	ASN1_SIMPLE(GENERAL_SUBTREE, base, GENERAL_NAME),
+	ASN1_IMP_OPT(GENERAL_SUBTREE, minimum, ASN1_INTEGER, 0),
+	ASN1_IMP_OPT(GENERAL_SUBTREE, maximum, ASN1_INTEGER, 1)
+} ASN1_SEQUENCE_END(GENERAL_SUBTREE)
+
+ASN1_SEQUENCE(NAME_CONSTRAINTS) = {
+	ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, permittedSubtrees,
+							GENERAL_SUBTREE, 0),
+	ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, excludedSubtrees,
+							GENERAL_SUBTREE, 1),
+} ASN1_SEQUENCE_END(NAME_CONSTRAINTS)
+	
+
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE)
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS)
+
+static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
+				  X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+	{
+	size_t i;
+	CONF_VALUE tval, *val;
+	STACK_OF(GENERAL_SUBTREE) **ptree = NULL;
+	NAME_CONSTRAINTS *ncons = NULL;
+	GENERAL_SUBTREE *sub = NULL;
+	ncons = NAME_CONSTRAINTS_new();
+	if (!ncons)
+		goto memerr;
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++)
+		{
+		val = sk_CONF_VALUE_value(nval, i);
+		if (!strncmp(val->name, "permitted", 9) && val->name[9])
+			{
+			ptree = &ncons->permittedSubtrees;
+			tval.name = val->name + 10;
+			}
+		else if (!strncmp(val->name, "excluded", 8) && val->name[8])
+			{
+			ptree = &ncons->excludedSubtrees;
+			tval.name = val->name + 9;
+			}
+		else
+			{
+			OPENSSL_PUT_ERROR(X509V3, v2i_NAME_CONSTRAINTS, X509V3_R_INVALID_SYNTAX);
+			goto err;
+			}
+		tval.value = val->value;
+		sub = GENERAL_SUBTREE_new();
+		if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1))
+			goto err;
+		if (!*ptree)
+			*ptree = sk_GENERAL_SUBTREE_new_null();
+		if (!*ptree || !sk_GENERAL_SUBTREE_push(*ptree, sub))
+			goto memerr;
+		sub = NULL;
+		}
+
+	return ncons;
+
+	memerr:
+	OPENSSL_PUT_ERROR(X509V3, v2i_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE);
+	err:
+	if (ncons)
+		NAME_CONSTRAINTS_free(ncons);
+	if (sub)
+		GENERAL_SUBTREE_free(sub);
+
+	return NULL;
+	}
+			
+
+	
+
+static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
+				BIO *bp, int ind)
+	{
+	NAME_CONSTRAINTS *ncons = a;
+	do_i2r_name_constraints(method, ncons->permittedSubtrees,
+					bp, ind, "Permitted");
+	do_i2r_name_constraints(method, ncons->excludedSubtrees,
+					bp, ind, "Excluded");
+	return 1;
+	}
+
+static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
+				   STACK_OF(GENERAL_SUBTREE) *trees,
+				   BIO *bp, int ind, char *name)
+	{
+	GENERAL_SUBTREE *tree;
+	size_t i;
+	if (sk_GENERAL_SUBTREE_num(trees) > 0)
+		BIO_printf(bp, "%*s%s:\n", ind, "", name);
+	for(i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++)
+		{
+		tree = sk_GENERAL_SUBTREE_value(trees, i);
+		BIO_printf(bp, "%*s", ind + 2, "");
+		if (tree->base->type == GEN_IPADD)
+			print_nc_ipadd(bp, tree->base->d.ip);
+		else
+			GENERAL_NAME_print(bp, tree->base);
+		BIO_puts(bp, "\n");
+		}
+	return 1;
+	}
+
+static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
+	{
+	int i, len;
+	unsigned char *p;
+	p = ip->data;
+	len = ip->length;
+	BIO_puts(bp, "IP:");
+	if(len == 8)
+		{
+		BIO_printf(bp, "%d.%d.%d.%d/%d.%d.%d.%d",
+				p[0], p[1], p[2], p[3],
+				p[4], p[5], p[6], p[7]);
+		}
+	else if(len == 32)
+		{
+		for (i = 0; i < 16; i++)
+			{
+			BIO_printf(bp, "%X", p[0] << 8 | p[1]);
+			p += 2;
+			if (i == 7)
+				BIO_puts(bp, "/");
+			else if (i != 15)
+				BIO_puts(bp, ":");
+			}
+		}
+	else
+		BIO_printf(bp, "IP Address:<invalid>");
+	return 1;
+	}
+
+/* Check a certificate conforms to a specified set of constraints.
+ * Return values:
+ *  X509_V_OK: All constraints obeyed.
+ *  X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
+ *  X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
+ *  X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
+ *  X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:  Unsupported constraint type.
+ *  X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
+ *  X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
+
+ */
+
+int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
+	{
+	int r, i;
+	size_t j;
+	X509_NAME *nm;
+
+	nm = X509_get_subject_name(x);
+
+	if (X509_NAME_entry_count(nm) > 0)
+		{
+		GENERAL_NAME gntmp;
+		gntmp.type = GEN_DIRNAME;
+		gntmp.d.directoryName = nm;
+
+		r = nc_match(&gntmp, nc);
+
+		if (r != X509_V_OK)
+			return r;
+
+		gntmp.type = GEN_EMAIL;
+
+
+		/* Process any email address attributes in subject name */
+
+		for (i = -1;;)
+			{
+			X509_NAME_ENTRY *ne;
+			i = X509_NAME_get_index_by_NID(nm,
+						       NID_pkcs9_emailAddress,
+						       i);
+			if (i == -1)
+				break;
+			ne = X509_NAME_get_entry(nm, i);
+			gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
+			if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING)
+				return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+
+			r = nc_match(&gntmp, nc);
+
+			if (r != X509_V_OK)
+				return r;
+			}
+		
+		}
+
+	for (j = 0; j < sk_GENERAL_NAME_num(x->altname); j++)
+		{
+		GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, j);
+		r = nc_match(gen, nc);
+		if (r != X509_V_OK)
+			return r;
+		}
+
+	return X509_V_OK;
+
+	}
+
+static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
+	{
+	GENERAL_SUBTREE *sub;
+	int r, match = 0;
+	size_t i;
+
+	/* Permitted subtrees: if any subtrees exist of matching the type
+	 * at least one subtree must match.
+	 */
+
+	for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++)
+		{
+		sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
+		if (gen->type != sub->base->type)
+			continue;
+		if (sub->minimum || sub->maximum)
+			return X509_V_ERR_SUBTREE_MINMAX;
+		/* If we already have a match don't bother trying any more */
+		if (match == 2)
+			continue;
+		if (match == 0)
+			match = 1;
+		r = nc_match_single(gen, sub->base);
+		if (r == X509_V_OK)
+			match = 2;
+		else if (r != X509_V_ERR_PERMITTED_VIOLATION)
+			return r;
+		}
+
+	if (match == 1)
+		return X509_V_ERR_PERMITTED_VIOLATION;
+
+	/* Excluded subtrees: must not match any of these */
+
+	for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++)
+		{
+		sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
+		if (gen->type != sub->base->type)
+			continue;
+		if (sub->minimum || sub->maximum)
+			return X509_V_ERR_SUBTREE_MINMAX;
+
+		r = nc_match_single(gen, sub->base);
+		if (r == X509_V_OK)
+			return X509_V_ERR_EXCLUDED_VIOLATION;
+		else if (r != X509_V_ERR_PERMITTED_VIOLATION)
+			return r;
+
+		}
+
+	return X509_V_OK;
+
+	}
+
+static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base)
+	{
+	switch(base->type)
+		{
+		case GEN_DIRNAME:
+		return nc_dn(gen->d.directoryName, base->d.directoryName);
+
+		case GEN_DNS:
+		return nc_dns(gen->d.dNSName, base->d.dNSName);
+
+		case GEN_EMAIL:
+		return nc_email(gen->d.rfc822Name, base->d.rfc822Name);
+
+		case GEN_URI:
+		return nc_uri(gen->d.uniformResourceIdentifier,
+					base->d.uniformResourceIdentifier);
+
+		default:
+		return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
+		}
+
+	}
+
+/* directoryName name constraint matching.
+ * The canonical encoding of X509_NAME makes this comparison easy. It is
+ * matched if the subtree is a subset of the name.
+ */
+
+static int nc_dn(X509_NAME *nm, X509_NAME *base)
+	{
+	/* Ensure canonical encodings are up to date.  */
+	if (nm->modified && i2d_X509_NAME(nm, NULL) < 0)
+		return X509_V_ERR_OUT_OF_MEM;
+	if (base->modified && i2d_X509_NAME(base, NULL) < 0)
+		return X509_V_ERR_OUT_OF_MEM;
+	if (base->canon_enclen > nm->canon_enclen)
+		return X509_V_ERR_PERMITTED_VIOLATION;
+	if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen))
+		return X509_V_ERR_PERMITTED_VIOLATION;
+	return X509_V_OK;
+	}
+
+static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
+	{
+	char *baseptr = (char *)base->data;
+	char *dnsptr = (char *)dns->data;
+	/* Empty matches everything */
+	if (!*baseptr)
+		return X509_V_OK;
+	/* Otherwise can add zero or more components on the left so
+	 * compare RHS and if dns is longer and expect '.' as preceding
+	 * character.
+	 */
+	if (dns->length > base->length)
+		{
+		dnsptr += dns->length - base->length;
+		if (dnsptr[-1] != '.')
+			return X509_V_ERR_PERMITTED_VIOLATION;
+		}
+
+	if (strcasecmp(baseptr, dnsptr))
+			return X509_V_ERR_PERMITTED_VIOLATION;
+
+	return X509_V_OK;
+
+	}
+
+static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
+	{
+	const char *baseptr = (char *)base->data;
+	const char *emlptr = (char *)eml->data;
+
+	const char *baseat = strchr(baseptr, '@');
+	const char *emlat = strchr(emlptr, '@');
+	if (!emlat)
+		return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+	/* Special case: inital '.' is RHS match */
+	if (!baseat && (*baseptr == '.'))
+		{
+		if (eml->length > base->length)
+			{
+			emlptr += eml->length - base->length;
+			if (!strcasecmp(baseptr, emlptr))
+				return X509_V_OK;
+			}
+		return X509_V_ERR_PERMITTED_VIOLATION;
+		}
+
+	/* If we have anything before '@' match local part */
+
+	if (baseat)
+		{
+		if (baseat != baseptr)
+			{
+			if ((baseat - baseptr) != (emlat - emlptr))
+				return X509_V_ERR_PERMITTED_VIOLATION;
+			/* Case sensitive match of local part */
+			if (strncmp(baseptr, emlptr, emlat - emlptr))
+				return X509_V_ERR_PERMITTED_VIOLATION;
+			}
+		/* Position base after '@' */
+		baseptr = baseat + 1;
+		}
+	emlptr = emlat + 1;
+	/* Just have hostname left to match: case insensitive */
+	if (strcasecmp(baseptr, emlptr))
+		return X509_V_ERR_PERMITTED_VIOLATION;
+
+	return X509_V_OK;
+
+	}
+
+static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
+	{
+	const char *baseptr = (char *)base->data;
+	const char *hostptr = (char *)uri->data;
+	const char *p = strchr(hostptr, ':');
+	int hostlen;
+	/* Check for foo:// and skip past it */
+	if (!p || (p[1] != '/') || (p[2] != '/'))
+		return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+	hostptr = p + 3;
+
+	/* Determine length of hostname part of URI */
+
+	/* Look for a port indicator as end of hostname first */
+
+	p = strchr(hostptr, ':');
+	/* Otherwise look for trailing slash */
+	if (!p)
+		p = strchr(hostptr, '/');
+
+	if (!p)
+		hostlen = strlen(hostptr);
+	else
+		hostlen = p - hostptr;
+
+	if (hostlen == 0)
+		return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+
+	/* Special case: inital '.' is RHS match */
+	if (*baseptr == '.')
+		{
+		if (hostlen > base->length)
+			{
+			p = hostptr + hostlen - base->length;
+			if (!strncasecmp(p, baseptr, base->length))
+				return X509_V_OK;
+			}
+		return X509_V_ERR_PERMITTED_VIOLATION;
+		}
+
+	if ((base->length != (int)hostlen) || strncasecmp(hostptr, baseptr, hostlen))
+		return X509_V_ERR_PERMITTED_VIOLATION;
+
+	return X509_V_OK;
+
+	}
diff --git a/crypto/x509v3/v3_pci.c b/crypto/x509v3/v3_pci.c
new file mode 100644
index 0000000..6f0b1c8
--- /dev/null
+++ b/crypto/x509v3/v3_pci.c
@@ -0,0 +1,333 @@
+/* v3_pci.c -*- mode:C; c-file-style: "eay" -*- */
+/* Contributed to the OpenSSL Project 2004
+ * by Richard Levitte (richard@levitte.org)
+ */
+/* Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *ext,
+	BIO *out, int indent);
+static PROXY_CERT_INFO_EXTENSION *r2i_pci(X509V3_EXT_METHOD *method,
+	X509V3_CTX *ctx, char *str);
+
+const X509V3_EXT_METHOD v3_pci =
+	{ NID_proxyCertInfo, 0, ASN1_ITEM_ref(PROXY_CERT_INFO_EXTENSION),
+	  0,0,0,0,
+	  0,0,
+	  NULL, NULL,
+	  (X509V3_EXT_I2R)i2r_pci,
+	  (X509V3_EXT_R2I)r2i_pci,
+	  NULL,
+	};
+
+static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *pci,
+	BIO *out, int indent)
+	{
+	BIO_printf(out, "%*sPath Length Constraint: ", indent, "");
+	if (pci->pcPathLengthConstraint)
+	  i2a_ASN1_INTEGER(out, pci->pcPathLengthConstraint);
+	else
+	  BIO_printf(out, "infinite");
+	BIO_puts(out, "\n");
+	BIO_printf(out, "%*sPolicy Language: ", indent, "");
+	i2a_ASN1_OBJECT(out, pci->proxyPolicy->policyLanguage);
+	BIO_puts(out, "\n");
+	if (pci->proxyPolicy->policy && pci->proxyPolicy->policy->data)
+	  BIO_printf(out, "%*sPolicy Text: %s\n", indent, "",
+		     pci->proxyPolicy->policy->data);
+	return 1;
+	}
+
+static int process_pci_value(CONF_VALUE *val,
+	ASN1_OBJECT **language, ASN1_INTEGER **pathlen,
+	ASN1_OCTET_STRING **policy)
+	{
+	int free_policy = 0;
+
+	if (strcmp(val->name, "language") == 0)
+		{
+		if (*language)
+			{
+			OPENSSL_PUT_ERROR(X509V3, process_pci_value, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED);
+			X509V3_conf_err(val);
+			return 0;
+			}
+		if (!(*language = OBJ_txt2obj(val->value, 0)))
+			{
+			OPENSSL_PUT_ERROR(X509V3, process_pci_value, X509V3_R_INVALID_OBJECT_IDENTIFIER);
+			X509V3_conf_err(val);
+			return 0;
+			}
+		}
+	else if (strcmp(val->name, "pathlen") == 0)
+		{
+		if (*pathlen)
+			{
+			OPENSSL_PUT_ERROR(X509V3, process_pci_value, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED);
+			X509V3_conf_err(val);
+			return 0;
+			}
+		if (!X509V3_get_value_int(val, pathlen))
+			{
+			OPENSSL_PUT_ERROR(X509V3, process_pci_value, X509V3_R_POLICY_PATH_LENGTH);
+			X509V3_conf_err(val);
+			return 0;
+			}
+		}
+	else if (strcmp(val->name, "policy") == 0)
+		{
+		unsigned char *tmp_data = NULL;
+		long val_len;
+		if (!*policy)
+			{
+			*policy = ASN1_OCTET_STRING_new();
+			if (!*policy)
+				{
+				OPENSSL_PUT_ERROR(X509V3, process_pci_value, ERR_R_MALLOC_FAILURE);
+				X509V3_conf_err(val);
+				return 0;
+				}
+			free_policy = 1;
+			}
+		if (strncmp(val->value, "hex:", 4) == 0)
+			{
+			unsigned char *tmp_data2 =
+				string_to_hex(val->value + 4, &val_len);
+
+			if (!tmp_data2) 
+				{
+				OPENSSL_PUT_ERROR(X509V3, process_pci_value, X509V3_R_ILLEGAL_HEX_DIGIT);
+				X509V3_conf_err(val);
+				goto err;
+				}
+
+			tmp_data = OPENSSL_realloc((*policy)->data,
+				(*policy)->length + val_len + 1);
+			if (tmp_data)
+				{
+				(*policy)->data = tmp_data;
+				memcpy(&(*policy)->data[(*policy)->length],
+					tmp_data2, val_len);
+				(*policy)->length += val_len;
+				(*policy)->data[(*policy)->length] = '\0';
+				}
+			else
+				{
+				OPENSSL_free(tmp_data2);
+				/* realloc failure implies the original data space is b0rked too! */
+				(*policy)->data = NULL;
+				(*policy)->length = 0;
+				OPENSSL_PUT_ERROR(X509V3, process_pci_value, ERR_R_MALLOC_FAILURE);
+				X509V3_conf_err(val);
+				goto err;
+				}
+			OPENSSL_free(tmp_data2);
+			}
+		else if (strncmp(val->value, "file:", 5) == 0)
+			{
+			unsigned char buf[2048];
+			int n;
+			BIO *b = BIO_new_file(val->value + 5, "r");
+			if (!b)
+				{
+				OPENSSL_PUT_ERROR(X509V3, process_pci_value, ERR_R_BIO_LIB);
+				X509V3_conf_err(val);
+				goto err;
+				}
+			while((n = BIO_read(b, buf, sizeof(buf))) > 0
+				|| (n == 0 && BIO_should_retry(b)))
+				{
+				if (!n) continue;
+
+				tmp_data = OPENSSL_realloc((*policy)->data,
+					(*policy)->length + n + 1);
+
+				if (!tmp_data)
+					break;
+
+				(*policy)->data = tmp_data;
+				memcpy(&(*policy)->data[(*policy)->length],
+					buf, n);
+				(*policy)->length += n;
+				(*policy)->data[(*policy)->length] = '\0';
+				}
+			BIO_free_all(b);
+
+			if (n < 0)
+				{
+				OPENSSL_PUT_ERROR(X509V3, process_pci_value, ERR_R_BIO_LIB);
+				X509V3_conf_err(val);
+				goto err;
+				}
+			}
+		else if (strncmp(val->value, "text:", 5) == 0)
+			{
+			val_len = strlen(val->value + 5);
+			tmp_data = OPENSSL_realloc((*policy)->data,
+				(*policy)->length + val_len + 1);
+			if (tmp_data)
+				{
+				(*policy)->data = tmp_data;
+				memcpy(&(*policy)->data[(*policy)->length],
+					val->value + 5, val_len);
+				(*policy)->length += val_len;
+				(*policy)->data[(*policy)->length] = '\0';
+				}
+			else
+				{
+				/* realloc failure implies the original data space is b0rked too! */
+				(*policy)->data = NULL;
+				(*policy)->length = 0;
+				OPENSSL_PUT_ERROR(X509V3, process_pci_value, ERR_R_MALLOC_FAILURE);
+				X509V3_conf_err(val);
+				goto err;
+				}
+			}
+		else
+			{
+			OPENSSL_PUT_ERROR(X509V3, process_pci_value, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG);
+			X509V3_conf_err(val);
+			goto err;
+			}
+		if (!tmp_data)
+			{
+			OPENSSL_PUT_ERROR(X509V3, process_pci_value, ERR_R_MALLOC_FAILURE);
+			X509V3_conf_err(val);
+			goto err;
+			}
+		}
+	return 1;
+err:
+	if (free_policy)
+		{
+		ASN1_OCTET_STRING_free(*policy);
+		*policy = NULL;
+		}
+	return 0;
+	}
+
+static PROXY_CERT_INFO_EXTENSION *r2i_pci(X509V3_EXT_METHOD *method,
+	X509V3_CTX *ctx, char *value)
+	{
+	PROXY_CERT_INFO_EXTENSION *pci = NULL;
+	STACK_OF(CONF_VALUE) *vals;
+	ASN1_OBJECT *language = NULL;
+	ASN1_INTEGER *pathlen = NULL;
+	ASN1_OCTET_STRING *policy = NULL;
+	size_t i, j;
+	int nid;
+
+	vals = X509V3_parse_list(value);
+	for (i = 0; i < sk_CONF_VALUE_num(vals); i++)
+		{
+		CONF_VALUE *cnf = sk_CONF_VALUE_value(vals, i);
+		if (!cnf->name || (*cnf->name != '@' && !cnf->value))
+			{
+			OPENSSL_PUT_ERROR(X509V3, r2i_pci, X509V3_R_INVALID_PROXY_POLICY_SETTING);
+			X509V3_conf_err(cnf);
+			goto err;
+			}
+		if (*cnf->name == '@')
+			{
+			STACK_OF(CONF_VALUE) *sect;
+			int success_p = 1;
+
+			sect = X509V3_get_section(ctx, cnf->name + 1);
+			if (!sect)
+				{
+				OPENSSL_PUT_ERROR(X509V3, r2i_pci, X509V3_R_INVALID_SECTION);
+				X509V3_conf_err(cnf);
+				goto err;
+				}
+			for (j = 0; success_p && j < sk_CONF_VALUE_num(sect); j++)
+				{
+				success_p =
+					process_pci_value(sk_CONF_VALUE_value(sect, j),
+						&language, &pathlen, &policy);
+				}
+			X509V3_section_free(ctx, sect);
+			if (!success_p)
+				goto err;
+			}
+		else
+			{
+			if (!process_pci_value(cnf,
+					&language, &pathlen, &policy))
+				{
+				X509V3_conf_err(cnf);
+				goto err;
+				}
+			}
+		}
+
+	/* Language is mandatory */
+	if (!language)
+		{
+		OPENSSL_PUT_ERROR(X509V3, r2i_pci, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED);
+		goto err;
+		}
+	nid = OBJ_obj2nid(language);
+	if ((nid == NID_Independent || nid == NID_id_ppl_inheritAll) && policy)
+		{
+		OPENSSL_PUT_ERROR(X509V3, r2i_pci, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY);
+		goto err;
+		}
+
+	pci = PROXY_CERT_INFO_EXTENSION_new();
+	if (!pci)
+		{
+		OPENSSL_PUT_ERROR(X509V3, r2i_pci, ERR_R_MALLOC_FAILURE);
+		goto err;
+		}
+
+	pci->proxyPolicy->policyLanguage = language; language = NULL;
+	pci->proxyPolicy->policy = policy; policy = NULL;
+	pci->pcPathLengthConstraint = pathlen; pathlen = NULL;
+	goto end;
+err:
+	if (language) { ASN1_OBJECT_free(language); language = NULL; }
+	if (pathlen) { ASN1_INTEGER_free(pathlen); pathlen = NULL; }
+	if (policy) { ASN1_OCTET_STRING_free(policy); policy = NULL; }
+	if (pci) { PROXY_CERT_INFO_EXTENSION_free(pci); pci = NULL; }
+end:
+	sk_CONF_VALUE_pop_free(vals, X509V3_conf_free);
+	return pci;
+	}
diff --git a/crypto/x509v3/v3_pcia.c b/crypto/x509v3/v3_pcia.c
new file mode 100644
index 0000000..04842bf
--- /dev/null
+++ b/crypto/x509v3/v3_pcia.c
@@ -0,0 +1,56 @@
+/* v3_pcia.c -*- mode:C; c-file-style: "eay" -*- */
+/* Contributed to the OpenSSL Project 2004
+ * by Richard Levitte (richard@levitte.org)
+ */
+/* Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+
+
+ASN1_SEQUENCE(PROXY_POLICY) =
+	{
+	ASN1_SIMPLE(PROXY_POLICY,policyLanguage,ASN1_OBJECT),
+	ASN1_OPT(PROXY_POLICY,policy,ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(PROXY_POLICY)
+
+IMPLEMENT_ASN1_FUNCTIONS(PROXY_POLICY)
+
+ASN1_SEQUENCE(PROXY_CERT_INFO_EXTENSION) =
+	{
+	ASN1_OPT(PROXY_CERT_INFO_EXTENSION,pcPathLengthConstraint,ASN1_INTEGER),
+	ASN1_SIMPLE(PROXY_CERT_INFO_EXTENSION,proxyPolicy,PROXY_POLICY)
+} ASN1_SEQUENCE_END(PROXY_CERT_INFO_EXTENSION)
+
+IMPLEMENT_ASN1_FUNCTIONS(PROXY_CERT_INFO_EXTENSION)
diff --git a/crypto/x509v3/v3_pcons.c b/crypto/x509v3/v3_pcons.c
new file mode 100644
index 0000000..dac732c
--- /dev/null
+++ b/crypto/x509v3/v3_pcons.c
@@ -0,0 +1,141 @@
+/* v3_pcons.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static STACK_OF(CONF_VALUE) *
+i2v_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *bcons,
+		       STACK_OF(CONF_VALUE) *extlist);
+static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method,
+				    X509V3_CTX *ctx,
+				    STACK_OF(CONF_VALUE) *values);
+
+const X509V3_EXT_METHOD v3_policy_constraints = {
+NID_policy_constraints, 0,
+ASN1_ITEM_ref(POLICY_CONSTRAINTS),
+0,0,0,0,
+0,0,
+i2v_POLICY_CONSTRAINTS,
+v2i_POLICY_CONSTRAINTS,
+NULL,NULL,
+NULL
+};
+
+ASN1_SEQUENCE(POLICY_CONSTRAINTS) = {
+	ASN1_IMP_OPT(POLICY_CONSTRAINTS, requireExplicitPolicy, ASN1_INTEGER,0),
+	ASN1_IMP_OPT(POLICY_CONSTRAINTS, inhibitPolicyMapping, ASN1_INTEGER,1)
+} ASN1_SEQUENCE_END(POLICY_CONSTRAINTS)
+
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(POLICY_CONSTRAINTS)
+
+
+static STACK_OF(CONF_VALUE) *
+i2v_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
+		       STACK_OF(CONF_VALUE) *extlist)
+{
+	POLICY_CONSTRAINTS *pcons = a;
+	X509V3_add_value_int("Require Explicit Policy",
+			pcons->requireExplicitPolicy, &extlist);
+	X509V3_add_value_int("Inhibit Policy Mapping",
+			pcons->inhibitPolicyMapping, &extlist);
+	return extlist;
+}
+
+static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method,
+				    X509V3_CTX *ctx,
+				    STACK_OF(CONF_VALUE) *values)
+{
+	POLICY_CONSTRAINTS *pcons=NULL;
+	CONF_VALUE *val;
+	size_t i;
+	if(!(pcons = POLICY_CONSTRAINTS_new())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_CONSTRAINTS, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(values); i++) {
+		val = sk_CONF_VALUE_value(values, i);
+		if(!strcmp(val->name, "requireExplicitPolicy")) {
+			if(!X509V3_get_value_int(val,
+				&pcons->requireExplicitPolicy)) goto err;
+		} else if(!strcmp(val->name, "inhibitPolicyMapping")) {
+			if(!X509V3_get_value_int(val,
+				&pcons->inhibitPolicyMapping)) goto err;
+		} else {
+			OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_CONSTRAINTS, X509V3_R_INVALID_NAME);
+			X509V3_conf_err(val);
+			goto err;
+		}
+	}
+	if (!pcons->inhibitPolicyMapping && !pcons->requireExplicitPolicy) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_CONSTRAINTS, X509V3_R_ILLEGAL_EMPTY_EXTENSION);
+		goto err;
+	}
+
+	return pcons;
+	err:
+	POLICY_CONSTRAINTS_free(pcons);
+	return NULL;
+}
+
diff --git a/crypto/x509v3/v3_pku.c b/crypto/x509v3/v3_pku.c
new file mode 100644
index 0000000..445eda6
--- /dev/null
+++ b/crypto/x509v3/v3_pku.c
@@ -0,0 +1,109 @@
+/* v3_pku.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static int i2r_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, PKEY_USAGE_PERIOD *usage, BIO *out, int indent);
+/*
+static PKEY_USAGE_PERIOD *v2i_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *values);
+*/
+const X509V3_EXT_METHOD v3_pkey_usage_period = {
+NID_private_key_usage_period, 0, ASN1_ITEM_ref(PKEY_USAGE_PERIOD),
+0,0,0,0,
+0,0,0,0,
+(X509V3_EXT_I2R)i2r_PKEY_USAGE_PERIOD, NULL,
+NULL
+};
+
+ASN1_SEQUENCE(PKEY_USAGE_PERIOD) = {
+	ASN1_IMP_OPT(PKEY_USAGE_PERIOD, notBefore, ASN1_GENERALIZEDTIME, 0),
+	ASN1_IMP_OPT(PKEY_USAGE_PERIOD, notAfter, ASN1_GENERALIZEDTIME, 1)
+} ASN1_SEQUENCE_END(PKEY_USAGE_PERIOD)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKEY_USAGE_PERIOD)
+
+static int i2r_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method,
+	     PKEY_USAGE_PERIOD *usage, BIO *out, int indent)
+{
+	BIO_printf(out, "%*s", indent, "");
+	if(usage->notBefore) {
+		BIO_write(out, "Not Before: ", 12);
+		ASN1_GENERALIZEDTIME_print(out, usage->notBefore);
+		if(usage->notAfter) BIO_write(out, ", ", 2);
+	}
+	if(usage->notAfter) {
+		BIO_write(out, "Not After: ", 11);
+		ASN1_GENERALIZEDTIME_print(out, usage->notAfter);
+	}
+	return 1;
+}
+
+/*
+static PKEY_USAGE_PERIOD *v2i_PKEY_USAGE_PERIOD(method, ctx, values)
+X509V3_EXT_METHOD *method;
+X509V3_CTX *ctx;
+STACK_OF(CONF_VALUE) *values;
+{
+return NULL;
+}
+*/
diff --git a/crypto/x509v3/v3_pmaps.c b/crypto/x509v3/v3_pmaps.c
new file mode 100644
index 0000000..fbc169d
--- /dev/null
+++ b/crypto/x509v3/v3_pmaps.c
@@ -0,0 +1,156 @@
+/* v3_pmaps.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static void *v2i_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+static STACK_OF(CONF_VALUE) *
+i2v_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method, void *pmps,
+		    STACK_OF(CONF_VALUE) *extlist);
+
+const X509V3_EXT_METHOD v3_policy_mappings = {
+	NID_policy_mappings, 0,
+	ASN1_ITEM_ref(POLICY_MAPPINGS),
+	0,0,0,0,
+	0,0,
+	i2v_POLICY_MAPPINGS,
+	v2i_POLICY_MAPPINGS,
+	0,0,
+	NULL
+};
+
+ASN1_SEQUENCE(POLICY_MAPPING) = {
+	ASN1_SIMPLE(POLICY_MAPPING, issuerDomainPolicy, ASN1_OBJECT),
+	ASN1_SIMPLE(POLICY_MAPPING, subjectDomainPolicy, ASN1_OBJECT)
+} ASN1_SEQUENCE_END(POLICY_MAPPING)
+
+ASN1_ITEM_TEMPLATE(POLICY_MAPPINGS) = 
+	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, POLICY_MAPPINGS,
+								POLICY_MAPPING)
+ASN1_ITEM_TEMPLATE_END(POLICY_MAPPINGS)
+
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(POLICY_MAPPING)
+
+
+static STACK_OF(CONF_VALUE) *
+i2v_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method, void *a,
+		    STACK_OF(CONF_VALUE) *ext_list)
+{
+	POLICY_MAPPINGS *pmaps = a;
+	POLICY_MAPPING *pmap;
+	size_t i;
+	char obj_tmp1[80];
+	char obj_tmp2[80];
+	for(i = 0; i < sk_POLICY_MAPPING_num(pmaps); i++) {
+		pmap = sk_POLICY_MAPPING_value(pmaps, i);
+		i2t_ASN1_OBJECT(obj_tmp1, 80, pmap->issuerDomainPolicy);
+		i2t_ASN1_OBJECT(obj_tmp2, 80, pmap->subjectDomainPolicy);
+		X509V3_add_value(obj_tmp1, obj_tmp2, &ext_list);
+	}
+	return ext_list;
+}
+
+static void *v2i_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
+{
+	POLICY_MAPPINGS *pmaps;
+	POLICY_MAPPING *pmap;
+	ASN1_OBJECT *obj1, *obj2;
+	CONF_VALUE *val;
+	size_t i;
+
+	if(!(pmaps = sk_POLICY_MAPPING_new_null())) {
+		OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_MAPPINGS, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		val = sk_CONF_VALUE_value(nval, i);
+		if(!val->value || !val->name) {
+			sk_POLICY_MAPPING_pop_free(pmaps, POLICY_MAPPING_free);
+			OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_MAPPINGS, X509V3_R_INVALID_OBJECT_IDENTIFIER);
+			X509V3_conf_err(val);
+			return NULL;
+		}
+		obj1 = OBJ_txt2obj(val->name, 0);
+		obj2 = OBJ_txt2obj(val->value, 0);
+		if(!obj1 || !obj2) {
+			sk_POLICY_MAPPING_pop_free(pmaps, POLICY_MAPPING_free);
+			OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_MAPPINGS, X509V3_R_INVALID_OBJECT_IDENTIFIER);
+			X509V3_conf_err(val);
+			return NULL;
+		}
+		pmap = POLICY_MAPPING_new();
+		if (!pmap) {
+			sk_POLICY_MAPPING_pop_free(pmaps, POLICY_MAPPING_free);
+			OPENSSL_PUT_ERROR(X509V3, v2i_POLICY_MAPPINGS, ERR_R_MALLOC_FAILURE);
+			return NULL;
+		}
+		pmap->issuerDomainPolicy = obj1;
+		pmap->subjectDomainPolicy = obj2;
+		sk_POLICY_MAPPING_push(pmaps, pmap);
+	}
+	return pmaps;
+}
diff --git a/crypto/x509v3/v3_prn.c b/crypto/x509v3/v3_prn.c
new file mode 100644
index 0000000..4376e0f
--- /dev/null
+++ b/crypto/x509v3/v3_prn.c
@@ -0,0 +1,237 @@
+/* v3_prn.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+/* X509 v3 extension utilities */
+
+#include <stdio.h>
+
+#include <openssl/bio.h>
+#include <openssl/conf.h>
+#include <openssl/mem.h>
+#include <openssl/x509v3.h>
+
+
+/* Extension printing routines */
+
+static int unknown_ext_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, int indent, int supported);
+
+/* Print out a name+value stack */
+
+void X509V3_EXT_val_prn(BIO *out, STACK_OF(CONF_VALUE) *val, int indent, int ml)
+{
+	size_t i;
+	CONF_VALUE *nval;
+	if(!val) return;
+	if(!ml || !sk_CONF_VALUE_num(val)) {
+		BIO_printf(out, "%*s", indent, "");
+		if(!sk_CONF_VALUE_num(val)) BIO_puts(out, "<EMPTY>\n");
+	}
+	for(i = 0; i < sk_CONF_VALUE_num(val); i++) {
+		if(ml) BIO_printf(out, "%*s", indent, "");
+		else if(i > 0) BIO_printf(out, ", ");
+		nval = sk_CONF_VALUE_value(val, i);
+		if(!nval->name) BIO_puts(out, nval->value);
+		else if(!nval->value) BIO_puts(out, nval->name);
+#ifndef CHARSET_EBCDIC
+		else BIO_printf(out, "%s:%s", nval->name, nval->value);
+#else
+		else {
+			int len;
+			char *tmp;
+			len = strlen(nval->value)+1;
+			tmp = OPENSSL_malloc(len);
+			if (tmp)
+			{
+				ascii2ebcdic(tmp, nval->value, len);
+				BIO_printf(out, "%s:%s", nval->name, tmp);
+				OPENSSL_free(tmp);
+			}
+		}
+#endif
+		if(ml) BIO_puts(out, "\n");
+	}
+}
+
+/* Main routine: print out a general extension */
+
+int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, int indent)
+{
+	void *ext_str = NULL;
+	char *value = NULL;
+	const unsigned char *p;
+	const X509V3_EXT_METHOD *method;	
+	STACK_OF(CONF_VALUE) *nval = NULL;
+	int ok = 1;
+
+	if(!(method = X509V3_EXT_get(ext)))
+		return unknown_ext_print(out, ext, flag, indent, 0);
+	p = ext->value->data;
+	if(method->it) ext_str = ASN1_item_d2i(NULL, &p, ext->value->length, ASN1_ITEM_ptr(method->it));
+	else ext_str = method->d2i(NULL, &p, ext->value->length);
+
+	if(!ext_str) return unknown_ext_print(out, ext, flag, indent, 1);
+
+	if(method->i2s) {
+		if(!(value = method->i2s(method, ext_str))) {
+			ok = 0;
+			goto err;
+		}
+#ifndef CHARSET_EBCDIC
+		BIO_printf(out, "%*s%s", indent, "", value);
+#else
+		{
+			int len;
+			char *tmp;
+			len = strlen(value)+1;
+			tmp = OPENSSL_malloc(len);
+			if (tmp)
+			{
+				ascii2ebcdic(tmp, value, len);
+				BIO_printf(out, "%*s%s", indent, "", tmp);
+				OPENSSL_free(tmp);
+			}
+		}
+#endif
+	} else if(method->i2v) {
+		if(!(nval = method->i2v(method, ext_str, NULL))) {
+			ok = 0;
+			goto err;
+		}
+		X509V3_EXT_val_prn(out, nval, indent,
+				 method->ext_flags & X509V3_EXT_MULTILINE);
+	} else if(method->i2r) {
+		if(!method->i2r(method, ext_str, out, indent)) ok = 0;
+	} else ok = 0;
+
+	err:
+		sk_CONF_VALUE_pop_free(nval, X509V3_conf_free);
+		if(value) OPENSSL_free(value);
+		if(method->it) ASN1_item_free(ext_str, ASN1_ITEM_ptr(method->it));
+		else method->ext_free(ext_str);
+		return ok;
+}
+
+int X509V3_extensions_print(BIO *bp, char *title, STACK_OF(X509_EXTENSION) *exts, unsigned long flag, int indent)
+{
+	size_t i;
+	int j;
+
+	if(sk_X509_EXTENSION_num(exts) <= 0) return 1;
+
+	if(title) 
+		{
+		BIO_printf(bp,"%*s%s:\n",indent, "", title);
+		indent += 4;
+		}
+
+	for (i=0; i<sk_X509_EXTENSION_num(exts); i++)
+		{
+		ASN1_OBJECT *obj;
+		X509_EXTENSION *ex;
+		ex=sk_X509_EXTENSION_value(exts, i);
+		if (indent && BIO_printf(bp,"%*s",indent, "") <= 0) return 0;
+		obj=X509_EXTENSION_get_object(ex);
+		i2a_ASN1_OBJECT(bp,obj);
+		j=X509_EXTENSION_get_critical(ex);
+		if (BIO_printf(bp,": %s\n",j?"critical":"") <= 0)
+			return 0;
+		if(!X509V3_EXT_print(bp, ex, flag, indent + 4))
+			{
+			BIO_printf(bp, "%*s", indent + 4, "");
+			M_ASN1_OCTET_STRING_print(bp,ex->value);
+			}
+		if (BIO_write(bp,"\n",1) <= 0) return 0;
+		}
+	return 1;
+}
+
+static int unknown_ext_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, int indent, int supported)
+{
+	switch(flag & X509V3_EXT_UNKNOWN_MASK) {
+
+		case X509V3_EXT_DEFAULT:
+		return 0;
+
+		case X509V3_EXT_ERROR_UNKNOWN:
+		if(supported)
+			BIO_printf(out, "%*s<Parse Error>", indent, "");
+		else
+			BIO_printf(out, "%*s<Not Supported>", indent, "");
+		return 1;
+
+		case X509V3_EXT_PARSE_UNKNOWN:
+			return ASN1_parse_dump(out,
+				ext->value->data, ext->value->length, indent, -1);
+		case X509V3_EXT_DUMP_UNKNOWN:
+			return BIO_hexdump(out, ext->value->data, ext->value->length, indent);
+
+		default:
+		return 1;
+	}
+}
+	
+
+#ifndef OPENSSL_NO_FP_API
+int X509V3_EXT_print_fp(FILE *fp, X509_EXTENSION *ext, int flag, int indent)
+{
+	BIO *bio_tmp;
+	int ret;
+	if(!(bio_tmp = BIO_new_fp(fp, BIO_NOCLOSE))) return 0;
+	ret = X509V3_EXT_print(bio_tmp, ext, flag, indent);
+	BIO_free(bio_tmp);
+	return ret;
+}
+#endif
diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c
new file mode 100644
index 0000000..8f7279c
--- /dev/null
+++ b/crypto/x509v3/v3_purp.c
@@ -0,0 +1,767 @@
+/* v3_purp.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2001.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <stdio.h>
+
+#include <openssl/buf.h>
+#include <openssl/err.h>
+#include <openssl/digest.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+
+
+static void x509v3_cache_extensions(X509 *x);
+
+static int check_ssl_ca(const X509 *x);
+static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int purpose_smime(const X509 *x, int ca);
+static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca);
+
+static int xp_cmp(const X509_PURPOSE **a, const X509_PURPOSE **b);
+static void xptable_free(X509_PURPOSE *p);
+
+static X509_PURPOSE xstandard[] = {
+	{X509_PURPOSE_SSL_CLIENT, X509_TRUST_SSL_CLIENT, 0, check_purpose_ssl_client, "SSL client", "sslclient", NULL},
+	{X509_PURPOSE_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, check_purpose_ssl_server, "SSL server", "sslserver", NULL},
+	{X509_PURPOSE_NS_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, check_purpose_ns_ssl_server, "Netscape SSL server", "nssslserver", NULL},
+	{X509_PURPOSE_SMIME_SIGN, X509_TRUST_EMAIL, 0, check_purpose_smime_sign, "S/MIME signing", "smimesign", NULL},
+	{X509_PURPOSE_SMIME_ENCRYPT, X509_TRUST_EMAIL, 0, check_purpose_smime_encrypt, "S/MIME encryption", "smimeencrypt", NULL},
+	{X509_PURPOSE_CRL_SIGN, X509_TRUST_COMPAT, 0, check_purpose_crl_sign, "CRL signing", "crlsign", NULL},
+	{X509_PURPOSE_ANY, X509_TRUST_DEFAULT, 0, no_check, "Any Purpose", "any", NULL},
+	{X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, ocsp_helper, "OCSP helper", "ocsphelper", NULL},
+	{X509_PURPOSE_TIMESTAMP_SIGN, X509_TRUST_TSA, 0, check_purpose_timestamp_sign, "Time Stamp signing", "timestampsign", NULL},
+};
+
+#define X509_PURPOSE_COUNT (sizeof(xstandard)/sizeof(X509_PURPOSE))
+
+static STACK_OF(X509_PURPOSE) *xptable = NULL;
+
+static int xp_cmp(const X509_PURPOSE **a, const X509_PURPOSE **b)
+{
+	return (*a)->purpose - (*b)->purpose;
+}
+
+/* As much as I'd like to make X509_check_purpose use a "const" X509*
+ * I really can't because it does recalculate hashes and do other non-const
+ * things. */
+int X509_check_purpose(X509 *x, int id, int ca)
+{
+	int idx;
+	const X509_PURPOSE *pt;
+	if(!(x->ex_flags & EXFLAG_SET)) {
+		CRYPTO_w_lock(CRYPTO_LOCK_X509);
+		x509v3_cache_extensions(x);
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+	}
+	if(id == -1) return 1;
+	idx = X509_PURPOSE_get_by_id(id);
+	if(idx == -1) return -1;
+	pt = X509_PURPOSE_get0(idx);
+	return pt->check_purpose(pt, x, ca);
+}
+
+int X509_PURPOSE_set(int *p, int purpose)
+{
+	if(X509_PURPOSE_get_by_id(purpose) == -1) {
+		OPENSSL_PUT_ERROR(X509V3, X509_PURPOSE_set, X509V3_R_INVALID_PURPOSE);
+		return 0;
+	}
+	*p = purpose;
+	return 1;
+}
+
+int X509_PURPOSE_get_count(void)
+{
+	if(!xptable) return X509_PURPOSE_COUNT;
+	return sk_X509_PURPOSE_num(xptable) + X509_PURPOSE_COUNT;
+}
+
+X509_PURPOSE * X509_PURPOSE_get0(int idx)
+{
+	if(idx < 0) return NULL;
+	if(idx < (int)X509_PURPOSE_COUNT) return xstandard + idx;
+	return sk_X509_PURPOSE_value(xptable, idx - X509_PURPOSE_COUNT);
+}
+
+int X509_PURPOSE_get_by_sname(char *sname)
+{
+	int i;
+	X509_PURPOSE *xptmp;
+	for(i = 0; i < X509_PURPOSE_get_count(); i++) {
+		xptmp = X509_PURPOSE_get0(i);
+		if(!strcmp(xptmp->sname, sname)) return i;
+	}
+	return -1;
+}
+
+int X509_PURPOSE_get_by_id(int purpose)
+{
+	X509_PURPOSE tmp;
+	size_t idx;
+
+	if((purpose >= X509_PURPOSE_MIN) && (purpose <= X509_PURPOSE_MAX))
+		return purpose - X509_PURPOSE_MIN;
+	tmp.purpose = purpose;
+	if(!xptable) return -1;
+
+	if (!sk_X509_PURPOSE_find(xptable, &idx, &tmp))
+		return -1;
+	return idx + X509_PURPOSE_COUNT;
+}
+
+int X509_PURPOSE_add(int id, int trust, int flags,
+			int (*ck)(const X509_PURPOSE *, const X509 *, int),
+					char *name, char *sname, void *arg)
+{
+	int idx;
+	X509_PURPOSE *ptmp;
+	/* This is set according to what we change: application can't set it */
+	flags &= ~X509_PURPOSE_DYNAMIC;
+	/* This will always be set for application modified trust entries */
+	flags |= X509_PURPOSE_DYNAMIC_NAME;
+	/* Get existing entry if any */
+	idx = X509_PURPOSE_get_by_id(id);
+	/* Need a new entry */
+	if(idx == -1) {
+		if(!(ptmp = OPENSSL_malloc(sizeof(X509_PURPOSE)))) {
+			OPENSSL_PUT_ERROR(X509V3, X509_PURPOSE_add, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+		ptmp->flags = X509_PURPOSE_DYNAMIC;
+	} else ptmp = X509_PURPOSE_get0(idx);
+
+	/* OPENSSL_free existing name if dynamic */
+	if(ptmp->flags & X509_PURPOSE_DYNAMIC_NAME) {
+		OPENSSL_free(ptmp->name);
+		OPENSSL_free(ptmp->sname);
+	}
+	/* dup supplied name */
+	ptmp->name = BUF_strdup(name);
+	ptmp->sname = BUF_strdup(sname);
+	if(!ptmp->name || !ptmp->sname) {
+		OPENSSL_PUT_ERROR(X509V3, X509_PURPOSE_add, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	/* Keep the dynamic flag of existing entry */
+	ptmp->flags &= X509_PURPOSE_DYNAMIC;
+	/* Set all other flags */
+	ptmp->flags |= flags;
+
+	ptmp->purpose = id;
+	ptmp->trust = trust;
+	ptmp->check_purpose = ck;
+	ptmp->usr_data = arg;
+
+	/* If its a new entry manage the dynamic table */
+	if(idx == -1) {
+		if(!xptable && !(xptable = sk_X509_PURPOSE_new(xp_cmp))) {
+			OPENSSL_PUT_ERROR(X509V3, X509_PURPOSE_add, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+		if (!sk_X509_PURPOSE_push(xptable, ptmp)) {
+			OPENSSL_PUT_ERROR(X509V3, X509_PURPOSE_add, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static void xptable_free(X509_PURPOSE *p)
+	{
+	if(!p) return;
+	if (p->flags & X509_PURPOSE_DYNAMIC) 
+		{
+		if (p->flags & X509_PURPOSE_DYNAMIC_NAME) {
+			OPENSSL_free(p->name);
+			OPENSSL_free(p->sname);
+		}
+		OPENSSL_free(p);
+		}
+	}
+
+void X509_PURPOSE_cleanup(void)
+{
+	unsigned int i;
+	sk_X509_PURPOSE_pop_free(xptable, xptable_free);
+	for(i = 0; i < X509_PURPOSE_COUNT; i++) xptable_free(xstandard + i);
+	xptable = NULL;
+}
+
+int X509_PURPOSE_get_id(X509_PURPOSE *xp)
+{
+	return xp->purpose;
+}
+
+char *X509_PURPOSE_get0_name(X509_PURPOSE *xp)
+{
+	return xp->name;
+}
+
+char *X509_PURPOSE_get0_sname(X509_PURPOSE *xp)
+{
+	return xp->sname;
+}
+
+int X509_PURPOSE_get_trust(X509_PURPOSE *xp)
+{
+	return xp->trust;
+}
+
+static int nid_cmp(const void *void_a, const void *void_b)
+	{
+	const int *a = void_a, *b = void_b;
+
+	return *a - *b;
+	}
+
+int X509_supported_extension(X509_EXTENSION *ex)
+	{
+	/* This table is a list of the NIDs of supported extensions:
+	 * that is those which are used by the verify process. If
+	 * an extension is critical and doesn't appear in this list
+	 * then the verify process will normally reject the certificate.
+	 * The list must be kept in numerical order because it will be
+	 * searched using bsearch.
+	 */
+
+	static const int supported_nids[] = {
+		NID_netscape_cert_type, /* 71 */
+        	NID_key_usage,		/* 83 */
+		NID_subject_alt_name,	/* 85 */
+		NID_basic_constraints,	/* 87 */
+		NID_certificate_policies, /* 89 */
+        	NID_ext_key_usage,	/* 126 */
+		NID_policy_constraints,	/* 401 */
+		NID_proxyCertInfo,	/* 663 */
+		NID_name_constraints,	/* 666 */
+		NID_policy_mappings,	/* 747 */
+		NID_inhibit_any_policy	/* 748 */
+	};
+
+	int ex_nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex));
+
+	if (ex_nid == NID_undef) 
+		return 0;
+
+	if (bsearch(&ex_nid, supported_nids, sizeof(supported_nids)/sizeof(int), sizeof(int), nid_cmp) != NULL)
+		return 1;
+	return 0;
+	}
+
+static void setup_dp(X509 *x, DIST_POINT *dp)
+	{
+	X509_NAME *iname = NULL;
+	size_t i;
+	if (dp->reasons)
+		{
+		if (dp->reasons->length > 0)
+			dp->dp_reasons = dp->reasons->data[0];
+		if (dp->reasons->length > 1)
+			dp->dp_reasons |= (dp->reasons->data[1] << 8);
+		dp->dp_reasons &= CRLDP_ALL_REASONS;
+		}
+	else
+		dp->dp_reasons = CRLDP_ALL_REASONS;
+	if (!dp->distpoint || (dp->distpoint->type != 1))
+		return;
+	for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++)
+		{
+		GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i);
+		if (gen->type == GEN_DIRNAME)
+			{
+			iname = gen->d.directoryName;
+			break;
+			}
+		}
+	if (!iname)
+		iname = X509_get_issuer_name(x);
+
+	DIST_POINT_set_dpname(dp->distpoint, iname);
+
+	}
+
+static void setup_crldp(X509 *x)
+	{
+	size_t i;
+	x->crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL);
+	for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++)
+		setup_dp(x, sk_DIST_POINT_value(x->crldp, i));
+	}
+
+static void x509v3_cache_extensions(X509 *x)
+{
+	BASIC_CONSTRAINTS *bs;
+	PROXY_CERT_INFO_EXTENSION *pci;
+	ASN1_BIT_STRING *usage;
+	ASN1_BIT_STRING *ns;
+	EXTENDED_KEY_USAGE *extusage;
+	X509_EXTENSION *ex;
+	size_t i;
+	int j;
+	if(x->ex_flags & EXFLAG_SET) return;
+#ifndef OPENSSL_NO_SHA
+	X509_digest(x, EVP_sha1(), x->sha1_hash, NULL);
+#endif
+	/* Does subject name match issuer ? */
+	if(!X509_NAME_cmp(X509_get_subject_name(x), X509_get_issuer_name(x)))
+			 x->ex_flags |= EXFLAG_SI;
+	/* V1 should mean no extensions ... */
+	if(!X509_get_version(x)) x->ex_flags |= EXFLAG_V1;
+	/* Handle basic constraints */
+	if((bs=X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL))) {
+		if(bs->ca) x->ex_flags |= EXFLAG_CA;
+		if(bs->pathlen) {
+			if((bs->pathlen->type == V_ASN1_NEG_INTEGER)
+						|| !bs->ca) {
+				x->ex_flags |= EXFLAG_INVALID;
+				x->ex_pathlen = 0;
+			} else x->ex_pathlen = ASN1_INTEGER_get(bs->pathlen);
+		} else x->ex_pathlen = -1;
+		BASIC_CONSTRAINTS_free(bs);
+		x->ex_flags |= EXFLAG_BCONS;
+	}
+	/* Handle proxy certificates */
+	if((pci=X509_get_ext_d2i(x, NID_proxyCertInfo, NULL, NULL))) {
+		if (x->ex_flags & EXFLAG_CA
+		    || X509_get_ext_by_NID(x, NID_subject_alt_name, 0) >= 0
+		    || X509_get_ext_by_NID(x, NID_issuer_alt_name, 0) >= 0) {
+			x->ex_flags |= EXFLAG_INVALID;
+		}
+		if (pci->pcPathLengthConstraint) {
+			x->ex_pcpathlen =
+				ASN1_INTEGER_get(pci->pcPathLengthConstraint);
+		} else x->ex_pcpathlen = -1;
+		PROXY_CERT_INFO_EXTENSION_free(pci);
+		x->ex_flags |= EXFLAG_PROXY;
+	}
+	/* Handle key usage */
+	if((usage=X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) {
+		if(usage->length > 0) {
+			x->ex_kusage = usage->data[0];
+			if(usage->length > 1) 
+				x->ex_kusage |= usage->data[1] << 8;
+		} else x->ex_kusage = 0;
+		x->ex_flags |= EXFLAG_KUSAGE;
+		ASN1_BIT_STRING_free(usage);
+	}
+	x->ex_xkusage = 0;
+	if((extusage=X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL))) {
+		x->ex_flags |= EXFLAG_XKUSAGE;
+		for(i = 0; i < sk_ASN1_OBJECT_num(extusage); i++) {
+			switch(OBJ_obj2nid(sk_ASN1_OBJECT_value(extusage,i))) {
+				case NID_server_auth:
+				x->ex_xkusage |= XKU_SSL_SERVER;
+				break;
+
+				case NID_client_auth:
+				x->ex_xkusage |= XKU_SSL_CLIENT;
+				break;
+
+				case NID_email_protect:
+				x->ex_xkusage |= XKU_SMIME;
+				break;
+
+				case NID_code_sign:
+				x->ex_xkusage |= XKU_CODE_SIGN;
+				break;
+
+				case NID_ms_sgc:
+				case NID_ns_sgc:
+				x->ex_xkusage |= XKU_SGC;
+				break;
+
+				case NID_OCSP_sign:
+				x->ex_xkusage |= XKU_OCSP_SIGN;
+				break;
+
+				case NID_time_stamp:
+				x->ex_xkusage |= XKU_TIMESTAMP;
+				break;
+
+				case NID_dvcs:
+				x->ex_xkusage |= XKU_DVCS;
+				break;
+
+				case NID_anyExtendedKeyUsage:
+				x->ex_xkusage |= XKU_ANYEKU;
+				break;
+			}
+		}
+		sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free);
+	}
+
+	if((ns=X509_get_ext_d2i(x, NID_netscape_cert_type, NULL, NULL))) {
+		if(ns->length > 0) x->ex_nscert = ns->data[0];
+		else x->ex_nscert = 0;
+		x->ex_flags |= EXFLAG_NSCERT;
+		ASN1_BIT_STRING_free(ns);
+	}
+	x->skid =X509_get_ext_d2i(x, NID_subject_key_identifier, NULL, NULL);
+	x->akid =X509_get_ext_d2i(x, NID_authority_key_identifier, NULL, NULL);
+	x->altname = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+	x->nc = X509_get_ext_d2i(x, NID_name_constraints, &j, NULL);
+	if (!x->nc && (j != -1))
+		x->ex_flags |= EXFLAG_INVALID;
+	setup_crldp(x);
+
+	for (j = 0; j < X509_get_ext_count(x); j++)
+		{
+		ex = X509_get_ext(x, j);
+		if (OBJ_obj2nid(X509_EXTENSION_get_object(ex))
+					== NID_freshest_crl)
+			x->ex_flags |= EXFLAG_FRESHEST;
+		if (!X509_EXTENSION_get_critical(ex))
+			continue;
+		if (!X509_supported_extension(ex))
+			{
+			x->ex_flags |= EXFLAG_CRITICAL;
+			break;
+			}
+		}
+	x->ex_flags |= EXFLAG_SET;
+}
+
+/* CA checks common to all purposes
+ * return codes:
+ * 0 not a CA
+ * 1 is a CA
+ * 2 basicConstraints absent so "maybe" a CA
+ * 3 basicConstraints absent but self signed V1.
+ * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
+ */
+
+#define V1_ROOT (EXFLAG_V1|EXFLAG_SS)
+#define ku_reject(x, usage) \
+	(((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
+#define xku_reject(x, usage) \
+	(((x)->ex_flags & EXFLAG_XKUSAGE) && !((x)->ex_xkusage & (usage)))
+#define ns_reject(x, usage) \
+	(((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage)))
+
+static int check_ca(const X509 *x)
+{
+	/* keyUsage if present should allow cert signing */
+	if(ku_reject(x, KU_KEY_CERT_SIGN)) return 0;
+	if(x->ex_flags & EXFLAG_BCONS) {
+		if(x->ex_flags & EXFLAG_CA) return 1;
+		/* If basicConstraints says not a CA then say so */
+		else return 0;
+	} else {
+		/* we support V1 roots for...  uh, I don't really know why. */
+		if((x->ex_flags & V1_ROOT) == V1_ROOT) return 3;
+		/* If key usage present it must have certSign so tolerate it */
+		else if (x->ex_flags & EXFLAG_KUSAGE) return 4;
+		/* Older certificates could have Netscape-specific CA types */
+		else if (x->ex_flags & EXFLAG_NSCERT
+			 && x->ex_nscert & NS_ANY_CA) return 5;
+		/* can this still be regarded a CA certificate?  I doubt it */
+		return 0;
+	}
+}
+
+int X509_check_ca(X509 *x)
+{
+	if(!(x->ex_flags & EXFLAG_SET)) {
+		CRYPTO_w_lock(CRYPTO_LOCK_X509);
+		x509v3_cache_extensions(x);
+		CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+	}
+
+	return check_ca(x);
+}
+
+/* Check SSL CA: common checks for SSL client and server */
+static int check_ssl_ca(const X509 *x)
+{
+	int ca_ret;
+	ca_ret = check_ca(x);
+	if(!ca_ret) return 0;
+	/* check nsCertType if present */
+	if(ca_ret != 5 || x->ex_nscert & NS_SSL_CA) return ca_ret;
+	else return 0;
+}
+
+
+static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	if(xku_reject(x,XKU_SSL_CLIENT)) return 0;
+	if(ca) return check_ssl_ca(x);
+	/* We need to do digital signatures or key agreement */
+	if(ku_reject(x,KU_DIGITAL_SIGNATURE|KU_KEY_AGREEMENT)) return 0;
+	/* nsCertType if present should allow SSL client use */	
+	if(ns_reject(x, NS_SSL_CLIENT)) return 0;
+	return 1;
+}
+/* Key usage needed for TLS/SSL server: digital signature, encipherment or
+ * key agreement. The ssl code can check this more thoroughly for individual
+ * key types.
+ */
+#define KU_TLS \
+	KU_DIGITAL_SIGNATURE|KU_KEY_ENCIPHERMENT|KU_KEY_AGREEMENT
+
+static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	if(xku_reject(x,XKU_SSL_SERVER|XKU_SGC)) return 0;
+	if(ca) return check_ssl_ca(x);
+
+	if(ns_reject(x, NS_SSL_SERVER)) return 0;
+	if(ku_reject(x, KU_TLS)) return 0;
+	
+	return 1;
+
+}
+
+static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	int ret;
+	ret = check_purpose_ssl_server(xp, x, ca);
+	if(!ret || ca) return ret;
+	/* We need to encipher or Netscape complains */
+	if(ku_reject(x, KU_KEY_ENCIPHERMENT)) return 0;
+	return ret;
+}
+
+/* common S/MIME checks */
+static int purpose_smime(const X509 *x, int ca)
+{
+	if(xku_reject(x,XKU_SMIME)) return 0;
+	if(ca) {
+		int ca_ret;
+		ca_ret = check_ca(x);
+		if(!ca_ret) return 0;
+		/* check nsCertType if present */
+		if(ca_ret != 5 || x->ex_nscert & NS_SMIME_CA) return ca_ret;
+		else return 0;
+	}
+	if(x->ex_flags & EXFLAG_NSCERT) {
+		if(x->ex_nscert & NS_SMIME) return 1;
+		/* Workaround for some buggy certificates */
+		if(x->ex_nscert & NS_SSL_CLIENT) return 2;
+		return 0;
+	}
+	return 1;
+}
+
+static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	int ret;
+	ret = purpose_smime(x, ca);
+	if(!ret || ca) return ret;
+	if(ku_reject(x, KU_DIGITAL_SIGNATURE|KU_NON_REPUDIATION)) return 0;
+	return ret;
+}
+
+static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	int ret;
+	ret = purpose_smime(x, ca);
+	if(!ret || ca) return ret;
+	if(ku_reject(x, KU_KEY_ENCIPHERMENT)) return 0;
+	return ret;
+}
+
+static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	if(ca) {
+		int ca_ret;
+		if((ca_ret = check_ca(x)) != 2) return ca_ret;
+		else return 0;
+	}
+	if(ku_reject(x, KU_CRL_SIGN)) return 0;
+	return 1;
+}
+
+/* OCSP helper: this is *not* a full OCSP check. It just checks that
+ * each CA is valid. Additional checks must be made on the chain.
+ */
+
+static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	/* Must be a valid CA.  Should we really support the "I don't know"
+	   value (2)? */
+	if(ca) return check_ca(x);
+	/* leaf certificate is checked in OCSP_verify() */
+	return 1;
+}
+
+static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x,
+					int ca)
+{
+	int i_ext;
+
+	/* If ca is true we must return if this is a valid CA certificate. */
+	if (ca) return check_ca(x);
+
+	/* 
+	 * Check the optional key usage field:
+	 * if Key Usage is present, it must be one of digitalSignature 
+	 * and/or nonRepudiation (other values are not consistent and shall
+	 * be rejected).
+	 */
+	if ((x->ex_flags & EXFLAG_KUSAGE)
+	    && ((x->ex_kusage & ~(KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)) ||
+		!(x->ex_kusage & (KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE))))
+		return 0;
+
+	/* Only time stamp key usage is permitted and it's required. */
+	if (!(x->ex_flags & EXFLAG_XKUSAGE) || x->ex_xkusage != XKU_TIMESTAMP)
+		return 0;
+
+	/* Extended Key Usage MUST be critical */
+	i_ext = X509_get_ext_by_NID((X509 *) x, NID_ext_key_usage, 0);
+	if (i_ext >= 0)
+		{
+		X509_EXTENSION *ext = X509_get_ext((X509 *) x, i_ext);
+		if (!X509_EXTENSION_get_critical(ext))
+			return 0;
+		}
+
+	return 1;
+}
+
+static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+	return 1;
+}
+
+/* Various checks to see if one certificate issued the second.
+ * This can be used to prune a set of possible issuer certificates
+ * which have been looked up using some simple method such as by
+ * subject name.
+ * These are:
+ * 1. Check issuer_name(subject) == subject_name(issuer)
+ * 2. If akid(subject) exists check it matches issuer
+ * 3. If key_usage(issuer) exists check it supports certificate signing
+ * returns 0 for OK, positive for reason for mismatch, reasons match
+ * codes for X509_verify_cert()
+ */
+
+int X509_check_issued(X509 *issuer, X509 *subject)
+{
+	if(X509_NAME_cmp(X509_get_subject_name(issuer),
+			X509_get_issuer_name(subject)))
+				return X509_V_ERR_SUBJECT_ISSUER_MISMATCH;
+	x509v3_cache_extensions(issuer);
+	x509v3_cache_extensions(subject);
+
+	if(subject->akid)
+		{
+		int ret = X509_check_akid(issuer, subject->akid);
+		if (ret != X509_V_OK)
+			return ret;
+		}
+
+	if(subject->ex_flags & EXFLAG_PROXY)
+		{
+		if(ku_reject(issuer, KU_DIGITAL_SIGNATURE))
+			return X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE;
+		}
+	else if(ku_reject(issuer, KU_KEY_CERT_SIGN))
+		return X509_V_ERR_KEYUSAGE_NO_CERTSIGN;
+	return X509_V_OK;
+}
+
+int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid)
+	{
+
+	if(!akid)
+		return X509_V_OK;
+
+	/* Check key ids (if present) */
+	if(akid->keyid && issuer->skid &&
+		 ASN1_OCTET_STRING_cmp(akid->keyid, issuer->skid) )
+				return X509_V_ERR_AKID_SKID_MISMATCH;
+	/* Check serial number */
+	if(akid->serial &&
+		ASN1_INTEGER_cmp(X509_get_serialNumber(issuer), akid->serial))
+				return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH;
+	/* Check issuer name */
+	if(akid->issuer)
+		{
+		/* Ugh, for some peculiar reason AKID includes
+		 * SEQUENCE OF GeneralName. So look for a DirName.
+		 * There may be more than one but we only take any
+		 * notice of the first.
+		 */
+		GENERAL_NAMES *gens;
+		GENERAL_NAME *gen;
+		X509_NAME *nm = NULL;
+		size_t i;
+		gens = akid->issuer;
+		for(i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+			{
+			gen = sk_GENERAL_NAME_value(gens, i);
+			if(gen->type == GEN_DIRNAME)
+				{
+				nm = gen->d.dirn;
+				break;
+				}
+			}
+		if(nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer)))
+			return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH;
+		}
+	return X509_V_OK;
+	}
+
diff --git a/crypto/x509v3/v3_skey.c b/crypto/x509v3/v3_skey.c
new file mode 100644
index 0000000..116679a
--- /dev/null
+++ b/crypto/x509v3/v3_skey.c
@@ -0,0 +1,147 @@
+/* v3_skey.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+
+#include <stdio.h>
+
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, char *str);
+const X509V3_EXT_METHOD v3_skey_id = { 
+NID_subject_key_identifier, 0, ASN1_ITEM_ref(ASN1_OCTET_STRING),
+0,0,0,0,
+(X509V3_EXT_I2S)i2s_ASN1_OCTET_STRING,
+(X509V3_EXT_S2I)s2i_skey_id,
+0,0,0,0,
+NULL};
+
+char *i2s_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method,
+	     ASN1_OCTET_STRING *oct)
+{
+	return hex_to_string(oct->data, oct->length);
+}
+
+ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method,
+	     X509V3_CTX *ctx, char *str)
+{
+	ASN1_OCTET_STRING *oct;
+	long length;
+
+	if(!(oct = M_ASN1_OCTET_STRING_new())) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_ASN1_OCTET_STRING, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if(!(oct->data = string_to_hex(str, &length))) {
+		M_ASN1_OCTET_STRING_free(oct);
+		return NULL;
+	}
+
+	oct->length = length;
+
+	return oct;
+
+}
+
+static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method,
+	     X509V3_CTX *ctx, char *str)
+{
+	ASN1_OCTET_STRING *oct;
+	ASN1_BIT_STRING *pk;
+	unsigned char pkey_dig[EVP_MAX_MD_SIZE];
+	unsigned int diglen;
+
+	if(strcmp(str, "hash")) return s2i_ASN1_OCTET_STRING(method, ctx, str);
+
+	if(!(oct = M_ASN1_OCTET_STRING_new())) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_skey_id, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if(ctx && (ctx->flags == CTX_TEST)) return oct;
+
+	if(!ctx || (!ctx->subject_req && !ctx->subject_cert)) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_skey_id, X509V3_R_NO_PUBLIC_KEY);
+		goto err;
+	}
+
+	if(ctx->subject_req) 
+		pk = ctx->subject_req->req_info->pubkey->public_key;
+	else pk = ctx->subject_cert->cert_info->key->public_key;
+
+	if(!pk) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_skey_id, X509V3_R_NO_PUBLIC_KEY);
+		goto err;
+	}
+
+	if (!EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL))
+		goto err;
+
+	if(!M_ASN1_OCTET_STRING_set(oct, pkey_dig, diglen)) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_skey_id, ERR_R_MALLOC_FAILURE);
+		goto err;
+	}
+
+	return oct;
+	
+	err:
+	M_ASN1_OCTET_STRING_free(oct);
+	return NULL;
+}
diff --git a/crypto/x509v3/v3_sxnet.c b/crypto/x509v3/v3_sxnet.c
new file mode 100644
index 0000000..7b8e3c5
--- /dev/null
+++ b/crypto/x509v3/v3_sxnet.c
@@ -0,0 +1,265 @@
+/* v3_sxnet.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+/* Support for Thawte strong extranet extension */
+
+#define SXNET_TEST
+
+static int sxnet_i2r(X509V3_EXT_METHOD *method, SXNET *sx, BIO *out, int indent);
+#ifdef SXNET_TEST
+static SXNET * sxnet_v2i(X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+						STACK_OF(CONF_VALUE) *nval);
+#endif
+const X509V3_EXT_METHOD v3_sxnet = {
+NID_sxnet, X509V3_EXT_MULTILINE, ASN1_ITEM_ref(SXNET),
+0,0,0,0,
+0,0,
+0, 
+#ifdef SXNET_TEST
+(X509V3_EXT_V2I)sxnet_v2i,
+#else
+0,
+#endif
+(X509V3_EXT_I2R)sxnet_i2r,
+0,
+NULL
+};
+
+ASN1_SEQUENCE(SXNETID) = {
+	ASN1_SIMPLE(SXNETID, zone, ASN1_INTEGER),
+	ASN1_SIMPLE(SXNETID, user, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(SXNETID)
+
+IMPLEMENT_ASN1_FUNCTIONS(SXNETID)
+
+ASN1_SEQUENCE(SXNET) = {
+	ASN1_SIMPLE(SXNET, version, ASN1_INTEGER),
+	ASN1_SEQUENCE_OF(SXNET, ids, SXNETID)
+} ASN1_SEQUENCE_END(SXNET)
+
+IMPLEMENT_ASN1_FUNCTIONS(SXNET)
+
+static int sxnet_i2r(X509V3_EXT_METHOD *method, SXNET *sx, BIO *out,
+	     int indent)
+{
+	long v;
+	char *tmp;
+	SXNETID *id;
+	size_t i;
+	v = ASN1_INTEGER_get(sx->version);
+	BIO_printf(out, "%*sVersion: %ld (0x%lX)", indent, "", v + 1, v);
+	for(i = 0; i < sk_SXNETID_num(sx->ids); i++) {
+		id = sk_SXNETID_value(sx->ids, i);
+		tmp = i2s_ASN1_INTEGER(NULL, id->zone);
+		BIO_printf(out, "\n%*sZone: %s, User: ", indent, "", tmp);
+		OPENSSL_free(tmp);
+		M_ASN1_OCTET_STRING_print(out, id->user);
+	}
+	return 1;
+}
+
+#ifdef SXNET_TEST
+
+/* NBB: this is used for testing only. It should *not* be used for anything
+ * else because it will just take static IDs from the configuration file and
+ * they should really be separate values for each user.
+ */
+
+
+static SXNET * sxnet_v2i(X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+	     STACK_OF(CONF_VALUE) *nval)
+{
+	CONF_VALUE *cnf;
+	SXNET *sx = NULL;
+	size_t i;
+	for(i = 0; i < sk_CONF_VALUE_num(nval); i++) {
+		cnf = sk_CONF_VALUE_value(nval, i);
+		if(!SXNET_add_id_asc(&sx, cnf->name, cnf->value, -1))
+								 return NULL;
+	}
+	return sx;
+}
+		
+	
+#endif
+
+/* Strong Extranet utility functions */
+
+/* Add an id given the zone as an ASCII number */
+
+int SXNET_add_id_asc(SXNET **psx, char *zone, char *user,
+	     int userlen)
+{
+	ASN1_INTEGER *izone = NULL;
+	if(!(izone = s2i_ASN1_INTEGER(NULL, zone))) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_add_id_asc, X509V3_R_ERROR_CONVERTING_ZONE);
+		return 0;
+	}
+	return SXNET_add_id_INTEGER(psx, izone, user, userlen);
+}
+
+/* Add an id given the zone as an unsigned long */
+
+int SXNET_add_id_ulong(SXNET **psx, unsigned long lzone, char *user,
+	     int userlen)
+{
+	ASN1_INTEGER *izone = NULL;
+	if(!(izone = M_ASN1_INTEGER_new()) || !ASN1_INTEGER_set(izone, lzone)) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_add_id_ulong, ERR_R_MALLOC_FAILURE);
+		M_ASN1_INTEGER_free(izone);
+		return 0;
+	}
+	return SXNET_add_id_INTEGER(psx, izone, user, userlen);
+	
+}
+
+/* Add an id given the zone as an ASN1_INTEGER.
+ * Note this version uses the passed integer and doesn't make a copy so don't
+ * free it up afterwards.
+ */
+
+int SXNET_add_id_INTEGER(SXNET **psx, ASN1_INTEGER *zone, char *user,
+	     int userlen)
+{
+	SXNET *sx = NULL;
+	SXNETID *id = NULL;
+	if(!psx || !zone || !user) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_add_id_INTEGER, X509V3_R_INVALID_NULL_ARGUMENT);
+		return 0;
+	}
+	if(userlen == -1) userlen = strlen(user);
+	if(userlen > 64) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_add_id_INTEGER, X509V3_R_USER_TOO_LONG);
+		return 0;
+	}
+	if(!*psx) {
+		if(!(sx = SXNET_new())) goto err;
+		if(!ASN1_INTEGER_set(sx->version, 0)) goto err;
+		*psx = sx;
+	} else sx = *psx;
+	if(SXNET_get_id_INTEGER(sx, zone)) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_add_id_INTEGER, X509V3_R_DUPLICATE_ZONE_ID);
+		return 0;
+	}
+
+	if(!(id = SXNETID_new())) goto err;
+	if(userlen == -1) userlen = strlen(user);
+		
+	if(!M_ASN1_OCTET_STRING_set(id->user, user, userlen)) goto err;
+	if(!sk_SXNETID_push(sx->ids, id)) goto err;
+	id->zone = zone;
+	return 1;
+	
+	err:
+	OPENSSL_PUT_ERROR(X509V3, SXNET_add_id_INTEGER, ERR_R_MALLOC_FAILURE);
+	SXNETID_free(id);
+	SXNET_free(sx);
+	*psx = NULL;
+	return 0;
+}
+
+ASN1_OCTET_STRING *SXNET_get_id_asc(SXNET *sx, char *zone)
+{
+	ASN1_INTEGER *izone = NULL;
+	ASN1_OCTET_STRING *oct;
+	if(!(izone = s2i_ASN1_INTEGER(NULL, zone))) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_get_id_asc, X509V3_R_ERROR_CONVERTING_ZONE);
+		return NULL;
+	}
+	oct = SXNET_get_id_INTEGER(sx, izone);
+	M_ASN1_INTEGER_free(izone);
+	return oct;
+}
+
+ASN1_OCTET_STRING *SXNET_get_id_ulong(SXNET *sx, unsigned long lzone)
+{
+	ASN1_INTEGER *izone = NULL;
+	ASN1_OCTET_STRING *oct;
+	if(!(izone = M_ASN1_INTEGER_new()) || !ASN1_INTEGER_set(izone, lzone)) {
+		OPENSSL_PUT_ERROR(X509V3, SXNET_get_id_ulong, ERR_R_MALLOC_FAILURE);
+		M_ASN1_INTEGER_free(izone);
+		return NULL;
+	}
+	oct = SXNET_get_id_INTEGER(sx, izone);
+	M_ASN1_INTEGER_free(izone);
+	return oct;
+}
+
+ASN1_OCTET_STRING *SXNET_get_id_INTEGER(SXNET *sx, ASN1_INTEGER *zone)
+{
+	SXNETID *id;
+	size_t i;
+	for(i = 0; i < sk_SXNETID_num(sx->ids); i++) {
+		id = sk_SXNETID_value(sx->ids, i);
+		if(!M_ASN1_INTEGER_cmp(id->zone, zone)) return id->user;
+	}
+	return NULL;
+}
+
+IMPLEMENT_ASN1_SET_OF(SXNETID)
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c
new file mode 100644
index 0000000..a4df78c
--- /dev/null
+++ b/crypto/x509v3/v3_utl.c
@@ -0,0 +1,1181 @@
+/* v3_utl.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* X509 v3 extension utilities */
+
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509v3.h>
+
+
+static char *strip_spaces(char *name);
+static int sk_strcmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b);
+static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, GENERAL_NAMES *gens);
+static void str_free(OPENSSL_STRING str);
+static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email);
+
+static int ipv4_from_asc(unsigned char *v4, const char *in);
+static int ipv6_from_asc(unsigned char *v6, const char *in);
+static int ipv6_cb(const char *elem, int len, void *usr);
+static int ipv6_hex(unsigned char *out, const char *in, int inlen);
+
+/* Add a CONF_VALUE name value pair to stack */
+
+int X509V3_add_value(const char *name, const char *value,
+						STACK_OF(CONF_VALUE) **extlist)
+{
+	CONF_VALUE *vtmp = NULL;
+	char *tname = NULL, *tvalue = NULL;
+	if(name && !(tname = BUF_strdup(name))) goto err;
+	if(value && !(tvalue = BUF_strdup(value))) goto err;
+	if(!(vtmp = (CONF_VALUE *)OPENSSL_malloc(sizeof(CONF_VALUE)))) goto err;
+	if(!*extlist && !(*extlist = sk_CONF_VALUE_new_null())) goto err;
+	vtmp->section = NULL;
+	vtmp->name = tname;
+	vtmp->value = tvalue;
+	if(!sk_CONF_VALUE_push(*extlist, vtmp)) goto err;
+	return 1;
+	err:
+	OPENSSL_PUT_ERROR(X509V3, X509V3_add_value, ERR_R_MALLOC_FAILURE);
+	if(vtmp) OPENSSL_free(vtmp);
+	if(tname) OPENSSL_free(tname);
+	if(tvalue) OPENSSL_free(tvalue);
+	return 0;
+}
+
+int X509V3_add_value_uchar(const char *name, const unsigned char *value,
+			   STACK_OF(CONF_VALUE) **extlist)
+    {
+    return X509V3_add_value(name,(const char *)value,extlist);
+    }
+
+/* Free function for STACK_OF(CONF_VALUE) */
+
+void X509V3_conf_free(CONF_VALUE *conf)
+{
+	if(!conf) return;
+	if(conf->name) OPENSSL_free(conf->name);
+	if(conf->value) OPENSSL_free(conf->value);
+	if(conf->section) OPENSSL_free(conf->section);
+	OPENSSL_free(conf);
+}
+
+int X509V3_add_value_bool(const char *name, int asn1_bool,
+						STACK_OF(CONF_VALUE) **extlist)
+{
+	if(asn1_bool) return X509V3_add_value(name, "TRUE", extlist);
+	return X509V3_add_value(name, "FALSE", extlist);
+}
+
+int X509V3_add_value_bool_nf(char *name, int asn1_bool,
+						STACK_OF(CONF_VALUE) **extlist)
+{
+	if(asn1_bool) return X509V3_add_value(name, "TRUE", extlist);
+	return 1;
+}
+
+
+char *i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *method, ASN1_ENUMERATED *a)
+{
+	BIGNUM *bntmp = NULL;
+	char *strtmp = NULL;
+	if(!a) return NULL;
+	if(!(bntmp = ASN1_ENUMERATED_to_BN(a, NULL)) ||
+	    !(strtmp = BN_bn2dec(bntmp)) )
+		OPENSSL_PUT_ERROR(X509V3, i2s_ASN1_ENUMERATED, ERR_R_MALLOC_FAILURE);
+	BN_free(bntmp);
+	return strtmp;
+}
+
+char *i2s_ASN1_INTEGER(X509V3_EXT_METHOD *method, ASN1_INTEGER *a)
+{
+	BIGNUM *bntmp = NULL;
+	char *strtmp = NULL;
+	if(!a) return NULL;
+	if(!(bntmp = ASN1_INTEGER_to_BN(a, NULL)) ||
+	    !(strtmp = BN_bn2dec(bntmp)) )
+		OPENSSL_PUT_ERROR(X509V3, i2s_ASN1_INTEGER, ERR_R_MALLOC_FAILURE);
+	BN_free(bntmp);
+	return strtmp;
+}
+
+ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *method, char *value)
+{
+	BIGNUM *bn = NULL;
+	ASN1_INTEGER *aint;
+	int isneg, ishex;
+	int ret;
+	if (!value) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_ASN1_INTEGER, X509V3_R_INVALID_NULL_VALUE);
+		return 0;
+	}
+	bn = BN_new();
+	if (value[0] == '-') {
+		value++;
+		isneg = 1;
+	} else isneg = 0;
+
+	if (value[0] == '0' && ((value[1] == 'x') || (value[1] == 'X'))) {
+		value += 2;
+		ishex = 1;
+	} else ishex = 0;
+
+	if (ishex) ret = BN_hex2bn(&bn, value);
+	else ret = BN_dec2bn(&bn, value);
+
+	if (!ret || value[ret]) {
+		BN_free(bn);
+		OPENSSL_PUT_ERROR(X509V3, s2i_ASN1_INTEGER, X509V3_R_BN_DEC2BN_ERROR);
+		return 0;
+	}
+
+	if (isneg && BN_is_zero(bn)) isneg = 0;
+
+	aint = BN_to_ASN1_INTEGER(bn, NULL);
+	BN_free(bn);
+	if (!aint) {
+		OPENSSL_PUT_ERROR(X509V3, s2i_ASN1_INTEGER, X509V3_R_BN_TO_ASN1_INTEGER_ERROR);
+		return 0;
+	}
+	if (isneg) aint->type |= V_ASN1_NEG;
+	return aint;
+}
+
+int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint,
+	     STACK_OF(CONF_VALUE) **extlist)
+{
+	char *strtmp;
+	int ret;
+	if(!aint) return 1;
+	if(!(strtmp = i2s_ASN1_INTEGER(NULL, aint))) return 0;
+	ret = X509V3_add_value(name, strtmp, extlist);
+	OPENSSL_free(strtmp);
+	return ret;
+}
+
+int X509V3_get_value_bool(CONF_VALUE *value, int *asn1_bool)
+{
+	char *btmp;
+	if(!(btmp = value->value)) goto err;
+	if(!strcmp(btmp, "TRUE") || !strcmp(btmp, "true")
+		 || !strcmp(btmp, "Y") || !strcmp(btmp, "y")
+		|| !strcmp(btmp, "YES") || !strcmp(btmp, "yes")) {
+		*asn1_bool = 0xff;
+		return 1;
+	} else if(!strcmp(btmp, "FALSE") || !strcmp(btmp, "false")
+		 || !strcmp(btmp, "N") || !strcmp(btmp, "n")
+		|| !strcmp(btmp, "NO") || !strcmp(btmp, "no")) {
+		*asn1_bool = 0;
+		return 1;
+	}
+	err:
+	OPENSSL_PUT_ERROR(X509V3, X509V3_get_value_bool, X509V3_R_INVALID_BOOLEAN_STRING);
+	X509V3_conf_err(value);
+	return 0;
+}
+
+int X509V3_get_value_int(CONF_VALUE *value, ASN1_INTEGER **aint)
+{
+	ASN1_INTEGER *itmp;
+	if(!(itmp = s2i_ASN1_INTEGER(NULL, value->value))) {
+		X509V3_conf_err(value);
+		return 0;
+	}
+	*aint = itmp;
+	return 1;
+}
+
+#define HDR_NAME	1
+#define HDR_VALUE	2
+
+/*#define DEBUG*/
+
+STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line)
+{
+	char *p, *q, c;
+	char *ntmp, *vtmp;
+	STACK_OF(CONF_VALUE) *values = NULL;
+	char *linebuf;
+	int state;
+	/* We are going to modify the line so copy it first */
+	linebuf = BUF_strdup(line);
+	state = HDR_NAME;
+	ntmp = NULL;
+	/* Go through all characters */
+	for(p = linebuf, q = linebuf; (c = *p) && (c!='\r') && (c!='\n'); p++) {
+
+		switch(state) {
+			case HDR_NAME:
+			if(c == ':') {
+				state = HDR_VALUE;
+				*p = 0;
+				ntmp = strip_spaces(q);
+				if(!ntmp) {
+					OPENSSL_PUT_ERROR(X509V3, X509V3_parse_list, X509V3_R_INVALID_NULL_NAME);
+					goto err;
+				}
+				q = p + 1;
+			} else if(c == ',') {
+				*p = 0;
+				ntmp = strip_spaces(q);
+				q = p + 1;
+#if 0
+				printf("%s\n", ntmp);
+#endif
+				if(!ntmp) {
+					OPENSSL_PUT_ERROR(X509V3, X509V3_parse_list, X509V3_R_INVALID_NULL_NAME);
+					goto err;
+				}
+				X509V3_add_value(ntmp, NULL, &values);
+			}
+			break ;
+
+			case HDR_VALUE:
+			if(c == ',') {
+				state = HDR_NAME;
+				*p = 0;
+				vtmp = strip_spaces(q);
+#if 0
+				printf("%s\n", ntmp);
+#endif
+				if(!vtmp) {
+					OPENSSL_PUT_ERROR(X509V3, X509V3_parse_list, X509V3_R_INVALID_NULL_VALUE);
+					goto err;
+				}
+				X509V3_add_value(ntmp, vtmp, &values);
+				ntmp = NULL;
+				q = p + 1;
+			}
+
+		}
+	}
+
+	if(state == HDR_VALUE) {
+		vtmp = strip_spaces(q);
+#if 0
+		printf("%s=%s\n", ntmp, vtmp);
+#endif
+		if(!vtmp) {
+			OPENSSL_PUT_ERROR(X509V3, X509V3_parse_list, X509V3_R_INVALID_NULL_VALUE);
+			goto err;
+		}
+		X509V3_add_value(ntmp, vtmp, &values);
+	} else {
+		ntmp = strip_spaces(q);
+#if 0
+		printf("%s\n", ntmp);
+#endif
+		if(!ntmp) {
+			OPENSSL_PUT_ERROR(X509V3, X509V3_parse_list, X509V3_R_INVALID_NULL_NAME);
+			goto err;
+		}
+		X509V3_add_value(ntmp, NULL, &values);
+	}
+OPENSSL_free(linebuf);
+return values;
+
+err:
+OPENSSL_free(linebuf);
+sk_CONF_VALUE_pop_free(values, X509V3_conf_free);
+return NULL;
+
+}
+
+/* Delete leading and trailing spaces from a string */
+static char *strip_spaces(char *name)
+{
+	char *p, *q;
+	/* Skip over leading spaces */
+	p = name;
+	while(*p && isspace((unsigned char)*p)) p++;
+	if(!*p) return NULL;
+	q = p + strlen(p) - 1;
+	while((q != p) && isspace((unsigned char)*q)) q--;
+	if(p != q) q[1] = 0;
+	if(!*p) return NULL;
+	return p;
+}
+
+/* hex string utilities */
+
+/* Given a buffer of length 'len' return a OPENSSL_malloc'ed string with its
+ * hex representation
+ * @@@ (Contents of buffer are always kept in ASCII, also on EBCDIC machines)
+ */
+
+char *hex_to_string(const unsigned char *buffer, long len)
+{
+	char *tmp, *q;
+	const unsigned char *p;
+	int i;
+	const static char hexdig[] = "0123456789ABCDEF";
+	if(!buffer || !len) return NULL;
+	if(!(tmp = OPENSSL_malloc(len * 3 + 1))) {
+		OPENSSL_PUT_ERROR(X509V3, hex_to_string, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	q = tmp;
+	for(i = 0, p = buffer; i < len; i++,p++) {
+		*q++ = hexdig[(*p >> 4) & 0xf];
+		*q++ = hexdig[*p & 0xf];
+		*q++ = ':';
+	}
+	q[-1] = 0;
+#ifdef CHARSET_EBCDIC
+	ebcdic2ascii(tmp, tmp, q - tmp - 1);
+#endif
+
+	return tmp;
+}
+
+/* Give a string of hex digits convert to
+ * a buffer
+ */
+
+unsigned char *string_to_hex(const char *str, long *len)
+{
+	unsigned char *hexbuf, *q;
+	unsigned char ch, cl, *p;
+	if(!str) {
+		OPENSSL_PUT_ERROR(X509V3, string_to_hex, X509V3_R_INVALID_NULL_ARGUMENT);
+		return NULL;
+	}
+	if(!(hexbuf = OPENSSL_malloc(strlen(str) >> 1))) goto err;
+	for(p = (unsigned char *)str, q = hexbuf; *p;) {
+		ch = *p++;
+#ifdef CHARSET_EBCDIC
+		ch = os_toebcdic[ch];
+#endif
+		if(ch == ':') continue;
+		cl = *p++;
+#ifdef CHARSET_EBCDIC
+		cl = os_toebcdic[cl];
+#endif
+		if(!cl) {
+			OPENSSL_PUT_ERROR(X509V3, string_to_hex, X509V3_R_ODD_NUMBER_OF_DIGITS);
+			OPENSSL_free(hexbuf);
+			return NULL;
+		}
+		if(isupper(ch)) ch = tolower(ch);
+		if(isupper(cl)) cl = tolower(cl);
+
+		if((ch >= '0') && (ch <= '9')) ch -= '0';
+		else if ((ch >= 'a') && (ch <= 'f')) ch -= 'a' - 10;
+		else goto badhex;
+
+		if((cl >= '0') && (cl <= '9')) cl -= '0';
+		else if ((cl >= 'a') && (cl <= 'f')) cl -= 'a' - 10;
+		else goto badhex;
+
+		*q++ = (ch << 4) | cl;
+	}
+
+	if(len) *len = q - hexbuf;
+
+	return hexbuf;
+
+	err:
+	if(hexbuf) OPENSSL_free(hexbuf);
+	OPENSSL_PUT_ERROR(X509V3, string_to_hex, ERR_R_MALLOC_FAILURE);
+	return NULL;
+
+	badhex:
+	OPENSSL_free(hexbuf);
+	OPENSSL_PUT_ERROR(X509V3, string_to_hex, X509V3_R_ILLEGAL_HEX_DIGIT);
+	return NULL;
+
+}
+
+/* V2I name comparison function: returns zero if 'name' matches
+ * cmp or cmp.*
+ */
+
+int name_cmp(const char *name, const char *cmp)
+{
+	int len, ret;
+	char c;
+	len = strlen(cmp);
+	if((ret = strncmp(name, cmp, len))) return ret;
+	c = name[len];
+	if(!c || (c=='.')) return 0;
+	return 1;
+}
+
+static int sk_strcmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b)
+{
+	return strcmp(*a, *b);
+}
+
+STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x)
+{
+	GENERAL_NAMES *gens;
+	STACK_OF(OPENSSL_STRING) *ret;
+
+	gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+	ret = get_email(X509_get_subject_name(x), gens);
+	sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+	return ret;
+}
+
+STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x)
+{
+	AUTHORITY_INFO_ACCESS *info;
+	STACK_OF(OPENSSL_STRING) *ret = NULL;
+	size_t i;
+
+	info = X509_get_ext_d2i(x, NID_info_access, NULL, NULL);
+	if (!info)
+		return NULL;
+	for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++)
+		{
+		ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i);
+		if (OBJ_obj2nid(ad->method) == NID_ad_OCSP)
+			{
+			if (ad->location->type == GEN_URI)
+				{
+				if (!append_ia5(&ret, ad->location->d.uniformResourceIdentifier))
+					break;
+				}
+			}
+		}
+	AUTHORITY_INFO_ACCESS_free(info);
+	return ret;
+}
+
+STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x)
+{
+	GENERAL_NAMES *gens;
+	STACK_OF(X509_EXTENSION) *exts;
+	STACK_OF(OPENSSL_STRING) *ret;
+
+	exts = X509_REQ_get_extensions(x);
+	gens = X509V3_get_d2i(exts, NID_subject_alt_name, NULL, NULL);
+	ret = get_email(X509_REQ_get_subject_name(x), gens);
+	sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+	sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+	return ret;
+}
+
+
+static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, GENERAL_NAMES *gens)
+{
+	STACK_OF(OPENSSL_STRING) *ret = NULL;
+	X509_NAME_ENTRY *ne;
+	ASN1_IA5STRING *email;
+	GENERAL_NAME *gen;
+	int i;
+	size_t j;
+	/* Now add any email address(es) to STACK */
+	i = -1;
+	/* First supplied X509_NAME */
+	while((i = X509_NAME_get_index_by_NID(name,
+					 NID_pkcs9_emailAddress, i)) >= 0) {
+		ne = X509_NAME_get_entry(name, i);
+		email = X509_NAME_ENTRY_get_data(ne);
+		if(!append_ia5(&ret, email)) return NULL;
+	}
+	for(j = 0; j < sk_GENERAL_NAME_num(gens); j++)
+	{
+		gen = sk_GENERAL_NAME_value(gens, j);
+		if(gen->type != GEN_EMAIL) continue;
+		if(!append_ia5(&ret, gen->d.ia5)) return NULL;
+	}
+	return ret;
+}
+
+static void str_free(OPENSSL_STRING str)
+{
+	OPENSSL_free(str);
+}
+
+static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email)
+{
+	char *emtmp;
+	/* First some sanity checks */
+	if(email->type != V_ASN1_IA5STRING) return 1;
+	if(!email->data || !email->length) return 1;
+	if(!*sk) *sk = sk_OPENSSL_STRING_new(sk_strcmp);
+	if(!*sk) return 0;
+	/* Don't add duplicates */
+	if(sk_OPENSSL_STRING_find(*sk, NULL, (char *)email->data)) return 1;
+	emtmp = BUF_strdup((char *)email->data);
+	if(!emtmp || !sk_OPENSSL_STRING_push(*sk, emtmp)) {
+		X509_email_free(*sk);
+		*sk = NULL;
+		return 0;
+	}
+	return 1;
+}
+
+void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
+{
+	sk_OPENSSL_STRING_pop_free(sk, str_free);
+}
+
+typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
+			const unsigned char *subject, size_t subject_len);
+
+/* Compare while ASCII ignoring case. */
+static int equal_nocase(const unsigned char *pattern, size_t pattern_len,
+			const unsigned char *subject, size_t subject_len)
+	{
+	if (pattern_len != subject_len)
+		return 0;
+	while (pattern_len)
+		{
+		unsigned char l = *pattern;
+		unsigned char r = *subject;
+		/* The pattern must not contain NUL characters. */
+		if (l == 0)
+			return 0;
+		if (l != r)
+			{
+			if ('A' <= l && l <= 'Z')
+				l = (l - 'A') + 'a';
+			if ('A' <= r && r <= 'Z')
+				r = (r - 'A') + 'a';
+			if (l != r)
+				return 0;
+			}
+		++pattern;
+		++subject;
+		--pattern_len;
+		}
+	return 1;
+	}
+
+/* Compare using memcmp. */
+static int equal_case(const unsigned char *pattern, size_t pattern_len,
+		      const unsigned char *subject, size_t subject_len)
+{
+	/* The pattern must not contain NUL characters. */
+	if (memchr(pattern, '\0', pattern_len) != NULL)
+		return 0;
+	if (pattern_len != subject_len)
+		return 0;
+	return !memcmp(pattern, subject, pattern_len);
+}
+
+/* RFC 5280, section 7.5, requires that only the domain is compared in
+   a case-insensitive manner. */
+static int equal_email(const unsigned char *a, size_t a_len,
+		       const unsigned char *b, size_t b_len)
+	{
+	size_t i = a_len;
+	if (a_len != b_len)
+		return 0;
+	/* We search backwards for the '@' character, so that we do
+	   not have to deal with quoted local-parts.  The domain part
+	   is compared in a case-insensitive manner. */
+	while (i > 0)
+		{
+		--i;
+		if (a[i] == '@' || b[i] == '@')
+			{
+			if (!equal_nocase(a + i, a_len - i,
+					  b + i, a_len - i))
+				return 0;
+			break;
+			}
+		}
+	if (i == 0)
+		i = a_len;
+	return equal_case(a, i, b, i);
+	}
+
+/* Compare the prefix and suffix with the subject, and check that the
+   characters in-between are valid. */
+static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
+			  const unsigned char *suffix, size_t suffix_len,
+			  const unsigned char *subject, size_t subject_len)
+	{
+	const unsigned char *wildcard_start;
+	const unsigned char *wildcard_end;
+	const unsigned char *p;
+	if (subject_len < prefix_len + suffix_len)
+		return 0;
+	if (!equal_nocase(prefix, prefix_len, subject, prefix_len))
+		return 0;
+	wildcard_start = subject + prefix_len;
+	wildcard_end = subject + (subject_len - suffix_len);
+	if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len))
+		return 0;
+	/* The wildcard must match at least one character. */
+	if (wildcard_start == wildcard_end)
+		return 0;
+	/* Check that the part matched by the wildcard contains only
+	   permitted characters and only matches a single label. */
+	for (p = wildcard_start; p != wildcard_end; ++p)
+		if (!(('0' <= *p && *p <= '9') ||
+		      ('A' <= *p && *p <= 'Z') ||
+		      ('a' <= *p && *p <= 'z') ||
+		      *p == '-'))
+			return 0;
+	return 1;
+	}
+
+/* Checks if the memory region consistens of [0-9A-Za-z.-]. */
+static int valid_domain_characters(const unsigned char *p, size_t len)
+	{
+	while (len)
+		{
+		if (!(('0' <= *p && *p <= '9') ||
+		      ('A' <= *p && *p <= 'Z') ||
+		      ('a' <= *p && *p <= 'z') ||
+		      *p == '-' || *p == '.'))
+			return 0;
+		++p;
+		--len;
+		}
+	return 1;
+	}
+
+/* Find the '*' in a wildcard pattern.  If no such character is found
+   or the pattern is otherwise invalid, returns NULL. */
+static const unsigned char *wildcard_find_star(const unsigned char *pattern,
+					       size_t pattern_len)
+	{
+	const unsigned char *star = memchr(pattern, '*', pattern_len);
+	size_t dot_count = 0;
+	const unsigned char *suffix_start;
+	size_t suffix_length;
+	if (star == NULL)
+		return NULL;
+	suffix_start = star + 1;
+	suffix_length = (pattern + pattern_len) - (star + 1);
+	if (!(valid_domain_characters(pattern, star - pattern) &&
+	      valid_domain_characters(suffix_start, suffix_length)))
+		return NULL;
+	/* Check that the suffix matches at least two labels. */
+	while (suffix_length)
+		{
+		if (*suffix_start == '.')
+			++dot_count;
+		++suffix_start;
+		--suffix_length;
+		}
+	if (dot_count < 2)
+		return NULL;
+	return star;
+	}
+
+/* Compare using wildcards. */
+static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
+			  const unsigned char *subject, size_t subject_len)
+	{
+	const unsigned char *star = wildcard_find_star(pattern, pattern_len);
+	if (star == NULL)
+		return equal_nocase(pattern, pattern_len,
+				    subject, subject_len);
+	return wildcard_match(pattern, star - pattern,
+			      star + 1, (pattern + pattern_len) - star - 1,
+			      subject, subject_len);
+	}
+
+/* Compare an ASN1_STRING to a supplied string. If they match
+ * return 1. If cmp_type > 0 only compare if string matches the
+ * type, otherwise convert it to UTF8.
+ */
+
+static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
+				const unsigned char *b, size_t blen)
+	{
+	if (!a->data || !a->length)
+		return 0;
+	if (cmp_type > 0)
+		{
+		if (cmp_type != a->type)
+			return 0;
+		if (cmp_type == V_ASN1_IA5STRING)
+			return equal(a->data, a->length, b, blen);
+		if (a->length == (int)blen && !memcmp(a->data, b, blen))
+			return 1;
+		else
+			return 0;
+		}
+	else
+		{
+		int astrlen, rv;
+		unsigned char *astr;
+		astrlen = ASN1_STRING_to_UTF8(&astr, a);
+		if (astrlen < 0)
+			return -1;
+		rv = equal(astr, astrlen, b, blen);
+		OPENSSL_free(astr);
+		return rv;
+		}
+	}
+
+static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags, int check_type)
+	{
+	GENERAL_NAMES *gens = NULL;
+	X509_NAME *name = NULL;
+	size_t i;
+	int j;
+	int cnid;
+	int alt_type;
+	equal_fn equal;
+	if (check_type == GEN_EMAIL)
+		{
+		cnid = NID_pkcs9_emailAddress;
+		alt_type = V_ASN1_IA5STRING;
+		equal = equal_email;
+		}
+	else if (check_type == GEN_DNS)
+		{
+		cnid = NID_commonName;
+		alt_type = V_ASN1_IA5STRING;
+		if (flags & X509_CHECK_FLAG_NO_WILDCARDS)
+			equal = equal_nocase;
+		else
+			equal = equal_wildcard;
+		}
+	else
+		{
+		cnid = 0;
+		alt_type = V_ASN1_OCTET_STRING;
+		equal = equal_case;
+		}
+
+	if (chklen == 0)
+		chklen = strlen((const char *)chk);
+
+	gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+	if (gens)
+		{
+		int rv = 0;
+		for (i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+			{
+			GENERAL_NAME *gen;
+			ASN1_STRING *cstr;
+			gen = sk_GENERAL_NAME_value(gens, i);
+			if(gen->type != check_type)
+				continue;
+			if (check_type == GEN_EMAIL)
+				cstr = gen->d.rfc822Name;
+			else if (check_type == GEN_DNS)
+				cstr = gen->d.dNSName;
+			else
+				cstr = gen->d.iPAddress;
+			if (do_check_string(cstr, alt_type, equal, chk, chklen))
+				{
+				rv = 1;
+				break;
+				}
+			}
+		GENERAL_NAMES_free(gens);
+		if (rv)
+			return 1;
+		if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
+			return 0;
+		}
+	j = -1;
+	name = X509_get_subject_name(x);
+	while((j = X509_NAME_get_index_by_NID(name, cnid, j)) >= 0)
+		{
+		X509_NAME_ENTRY *ne;
+		ASN1_STRING *str;
+		ne = X509_NAME_get_entry(name, j);
+		str = X509_NAME_ENTRY_get_data(ne);
+		if (do_check_string(str, -1, equal, chk, chklen))
+			return 1;
+		}
+	return 0;
+	}
+
+int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags)
+	{
+	return do_x509_check(x, chk, chklen, flags, GEN_DNS);
+	}
+
+int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags)
+	{
+	return do_x509_check(x, chk, chklen, flags, GEN_EMAIL);
+	}
+
+int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags)
+	{
+	return do_x509_check(x, chk, chklen, flags, GEN_IPADD);
+	}
+
+int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags)
+	{
+	unsigned char ipout[16];
+	int iplen;
+	iplen = a2i_ipadd(ipout, ipasc);
+	if (iplen == 0)
+		return -2;
+	return do_x509_check(x, ipout, (size_t)iplen, flags, GEN_IPADD);
+	}
+
+/* Convert IP addresses both IPv4 and IPv6 into an 
+ * OCTET STRING compatible with RFC3280.
+ */
+
+ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc)
+	{
+	unsigned char ipout[16];
+	ASN1_OCTET_STRING *ret;
+	int iplen;
+
+	/* If string contains a ':' assume IPv6 */
+
+	iplen = a2i_ipadd(ipout, ipasc);
+
+	if (!iplen)
+		return NULL;
+
+	ret = ASN1_OCTET_STRING_new();
+	if (!ret)
+		return NULL;
+	if (!ASN1_OCTET_STRING_set(ret, ipout, iplen))
+		{
+		ASN1_OCTET_STRING_free(ret);
+		return NULL;
+		}
+	return ret;
+	}
+
+ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc)
+	{
+	ASN1_OCTET_STRING *ret = NULL;
+	unsigned char ipout[32];
+	char *iptmp = NULL, *p;
+	int iplen1, iplen2;
+	p = strchr(ipasc,'/');
+	if (!p)
+		return NULL;
+	iptmp = BUF_strdup(ipasc);
+	if (!iptmp)
+		return NULL;
+	p = iptmp + (p - ipasc);
+	*p++ = 0;
+
+	iplen1 = a2i_ipadd(ipout, iptmp);
+
+	if (!iplen1)
+		goto err;
+
+	iplen2 = a2i_ipadd(ipout + iplen1, p);
+
+	OPENSSL_free(iptmp);
+	iptmp = NULL;
+
+	if (!iplen2 || (iplen1 != iplen2))
+		goto err;
+
+	ret = ASN1_OCTET_STRING_new();
+	if (!ret)
+		goto err;
+	if (!ASN1_OCTET_STRING_set(ret, ipout, iplen1 + iplen2))
+		goto err;
+
+	return ret;
+
+	err:
+	if (iptmp)
+		OPENSSL_free(iptmp);
+	if (ret)
+		ASN1_OCTET_STRING_free(ret);
+	return NULL;
+	}
+	
+
+int a2i_ipadd(unsigned char *ipout, const char *ipasc)
+	{
+	/* If string contains a ':' assume IPv6 */
+
+	if (strchr(ipasc, ':'))
+		{
+		if (!ipv6_from_asc(ipout, ipasc))
+			return 0;
+		return 16;
+		}
+	else
+		{
+		if (!ipv4_from_asc(ipout, ipasc))
+			return 0;
+		return 4;
+		}
+	}
+
+static int ipv4_from_asc(unsigned char *v4, const char *in)
+	{
+	int a0, a1, a2, a3;
+	if (sscanf(in, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) != 4)
+		return 0;
+	if ((a0 < 0) || (a0 > 255) || (a1 < 0) || (a1 > 255)
+		|| (a2 < 0) || (a2 > 255) || (a3 < 0) || (a3 > 255))
+		return 0;
+	v4[0] = a0;
+	v4[1] = a1;
+	v4[2] = a2;
+	v4[3] = a3;
+	return 1;
+	}
+
+typedef struct {
+		/* Temporary store for IPV6 output */
+		unsigned char tmp[16];
+		/* Total number of bytes in tmp */
+		int total;
+		/* The position of a zero (corresponding to '::') */
+		int zero_pos;
+		/* Number of zeroes */
+		int zero_cnt;
+	} IPV6_STAT;
+
+
+static int ipv6_from_asc(unsigned char *v6, const char *in)
+	{
+	IPV6_STAT v6stat;
+	v6stat.total = 0;
+	v6stat.zero_pos = -1;
+	v6stat.zero_cnt = 0;
+	/* Treat the IPv6 representation as a list of values
+	 * separated by ':'. The presence of a '::' will parse
+ 	 * as one, two or three zero length elements.
+	 */
+	if (!CONF_parse_list(in, ':', 0, ipv6_cb, &v6stat))
+		return 0;
+
+	/* Now for some sanity checks */
+
+	if (v6stat.zero_pos == -1)
+		{
+		/* If no '::' must have exactly 16 bytes */
+		if (v6stat.total != 16)
+			return 0;
+		}
+	else 
+		{
+		/* If '::' must have less than 16 bytes */
+		if (v6stat.total == 16)
+			return 0;
+		/* More than three zeroes is an error */
+		if (v6stat.zero_cnt > 3)
+			return 0;
+		/* Can only have three zeroes if nothing else present */
+		else if (v6stat.zero_cnt == 3)
+			{
+			if (v6stat.total > 0)
+				return 0;
+			}
+		/* Can only have two zeroes if at start or end */
+		else if (v6stat.zero_cnt == 2)
+			{
+			if ((v6stat.zero_pos != 0)
+				&& (v6stat.zero_pos != v6stat.total))
+				return 0;
+			}
+		else 
+		/* Can only have one zero if *not* start or end */
+			{
+			if ((v6stat.zero_pos == 0)
+				|| (v6stat.zero_pos == v6stat.total))
+				return 0;
+			}
+		}
+
+	/* Format result */
+
+	if (v6stat.zero_pos >= 0)
+		{
+		/* Copy initial part */
+		memcpy(v6, v6stat.tmp, v6stat.zero_pos);
+		/* Zero middle */
+		memset(v6 + v6stat.zero_pos, 0, 16 - v6stat.total);
+		/* Copy final part */
+		if (v6stat.total != v6stat.zero_pos)
+			memcpy(v6 + v6stat.zero_pos + 16 - v6stat.total,
+				v6stat.tmp + v6stat.zero_pos,
+				v6stat.total - v6stat.zero_pos);
+		}
+	else
+		memcpy(v6, v6stat.tmp, 16);
+
+	return 1;
+	}
+
+static int ipv6_cb(const char *elem, int len, void *usr)
+	{
+	IPV6_STAT *s = usr;
+	/* Error if 16 bytes written */
+	if (s->total == 16)
+		return 0;
+	if (len == 0)
+		{
+		/* Zero length element, corresponds to '::' */
+		if (s->zero_pos == -1)
+			s->zero_pos = s->total;
+		/* If we've already got a :: its an error */
+		else if (s->zero_pos != s->total)
+			return 0;
+		s->zero_cnt++;
+		}
+	else 
+		{
+		/* If more than 4 characters could be final a.b.c.d form */
+		if (len > 4)
+			{
+			/* Need at least 4 bytes left */
+			if (s->total > 12)
+				return 0;
+			/* Must be end of string */
+			if (elem[len])
+				return 0;
+			if (!ipv4_from_asc(s->tmp + s->total, elem))
+				return 0;
+			s->total += 4;
+			}
+		else
+			{
+			if (!ipv6_hex(s->tmp + s->total, elem, len))
+				return 0;
+			s->total += 2;
+			}
+		}
+	return 1;
+	}
+
+/* Convert a string of up to 4 hex digits into the corresponding
+ * IPv6 form.
+ */
+
+static int ipv6_hex(unsigned char *out, const char *in, int inlen)
+	{
+	unsigned char c;
+	unsigned int num = 0;
+	if (inlen > 4)
+		return 0;
+	while(inlen--)
+		{
+		c = *in++;
+		num <<= 4;
+		if ((c >= '0') && (c <= '9'))
+			num |= c - '0';
+		else if ((c >= 'A') && (c <= 'F'))
+			num |= c - 'A' + 10;
+		else if ((c >= 'a') && (c <= 'f'))
+			num |=  c - 'a' + 10;
+		else
+			return 0;
+		}
+	out[0] = num >> 8;
+	out[1] = num & 0xff;
+	return 1;
+	}
+
+
+int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE)*dn_sk,
+						unsigned long chtype)
+	{
+	CONF_VALUE *v;
+	int mval;
+	size_t i;
+	char *p, *type;
+	if (!nm)
+		return 0;
+
+	for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++)
+		{
+		v=sk_CONF_VALUE_value(dn_sk,i);
+		type=v->name;
+		/* Skip past any leading X. X: X, etc to allow for
+		 * multiple instances 
+		 */
+		for(p = type; *p ; p++) 
+#ifndef CHARSET_EBCDIC
+			if ((*p == ':') || (*p == ',') || (*p == '.'))
+#else
+			if ((*p == os_toascii[':']) || (*p == os_toascii[',']) || (*p == os_toascii['.']))
+#endif
+				{
+				p++;
+				if(*p) type = p;
+				break;
+				}
+#ifndef CHARSET_EBCDIC
+		if (*type == '+')
+#else
+		if (*type == os_toascii['+'])
+#endif
+			{
+			mval = -1;
+			type++;
+			}
+		else
+			mval = 0;
+		if (!X509_NAME_add_entry_by_txt(nm,type, chtype,
+				(unsigned char *) v->value,-1,-1,mval))
+					return 0;
+
+		}
+	return 1;
+	}
diff --git a/crypto/x509v3/v3nametest.c b/crypto/x509v3/v3nametest.c
new file mode 100644
index 0000000..15d3e10
--- /dev/null
+++ b/crypto/x509v3/v3nametest.c
@@ -0,0 +1,418 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999. */
+/* ====================================================================
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <string.h>
+#include <strings.h>
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+
+static const char *const names[] =
+	{
+	"a", "b", ".", "*", "@",
+	".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..",
+	"@@", "**",
+	"*.com", "*com", "*.*.com", "*com", "com*", "*example.com",
+	"*@example.com", "test@*.example.com",
+	"example.com", "www.example.com", "test.www.example.com",
+	"*.example.com", "*.www.example.com", "test.*.example.com", "www.*.com",
+	"example.net", "xn--rger-koa.example.com",
+	"a.example.com", "b.example.com",
+	"postmaster@example.com", "Postmaster@example.com",
+	"postmaster@EXAMPLE.COM",
+	NULL
+	};
+
+static const char *const exceptions[] =
+	{
+	"set CN: host: [*.example.com] does not match [*.example.com]",
+	"set CN: host: [*.example.com] matches [a.example.com]",
+	"set CN: host: [*.example.com] matches [b.example.com]",
+	"set CN: host: [*.example.com] matches [www.example.com]",
+	"set CN: host: [*.example.com] matches [xn--rger-koa.example.com]",
+	"set CN: host: [test.*.example.com] does not match [test.*.example.com]",
+	"set CN: host: [test.*.example.com] matches [test.www.example.com]",
+	"set CN: host: [*.www.example.com] does not match [*.www.example.com]",
+	"set CN: host: [*.www.example.com] matches [test.www.example.com]",
+	"set emailAddress: email: [postmaster@example.com] does not match [Postmaster@example.com]",
+	"set emailAddress: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]",
+	"set emailAddress: email: [Postmaster@example.com] does not match [postmaster@example.com]",
+	"set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
+	"set dnsName: host: [*.example.com] matches [www.example.com]",
+	"set dnsName: host: [*.example.com] does not match [*.example.com]",
+	"set dnsName: host: [*.example.com] matches [a.example.com]",
+	"set dnsName: host: [*.example.com] matches [b.example.com]",
+	"set dnsName: host: [*.example.com] matches [xn--rger-koa.example.com]",
+	"set dnsName: host: [*.www.example.com] matches [test.www.example.com]",
+	"set dnsName: host: [*.www.example.com] does not match [*.www.example.com]",
+	"set dnsName: host: [test.*.example.com] matches [test.www.example.com]",
+	"set dnsName: host: [test.*.example.com] does not match [test.*.example.com]",
+	"set rfc822Name: email: [postmaster@example.com] does not match [Postmaster@example.com]",
+	"set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@example.com]",
+	"set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
+	"set rfc822Name: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]",
+	NULL
+	};
+
+static int is_exception(const char *msg)
+	{
+	const char *const *p;
+	for (p = exceptions; *p; ++p)
+		if (strcmp(msg, *p) == 0)
+			return 1;
+	return 0;
+	}
+
+static int set_cn(X509 *crt, ...)
+	{
+	int ret = 0;
+	X509_NAME *n = NULL;
+	va_list ap;
+	va_start(ap, crt);
+	n = X509_NAME_new();
+	if (n == NULL)
+		goto out;
+	while (1) {
+		int nid;
+		const char *name;
+		nid = va_arg(ap, int);
+		if (nid == 0)
+			break;
+		name = va_arg(ap, const char *);
+		if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_ASC,
+							(unsigned char *)name,
+						-1, -1, 1))
+			goto out;
+	}
+	if (!X509_set_subject_name(crt, n))
+		goto out;
+	ret = 1;
+ out:
+	X509_NAME_free(n);
+	va_end(ap);
+	return ret;
+	}
+
+/*
+int		X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
+X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
+			int nid, int crit, ASN1_OCTET_STRING *data);
+int		X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
+*/
+
+static int set_altname(X509 *crt, ...)
+	{
+	int ret = 0;
+	GENERAL_NAMES *gens = NULL;
+	GENERAL_NAME *gen = NULL;
+	ASN1_IA5STRING *ia5 = NULL;
+	va_list ap;
+	va_start(ap, crt);
+	gens = sk_GENERAL_NAME_new_null();
+	if (gens == NULL)
+		goto out;
+	while (1) {
+		int type;
+		const char *name;
+		type = va_arg(ap, int);
+		if (type == 0)
+			break;
+		name = va_arg(ap, const char *);
+
+		gen = GENERAL_NAME_new();
+		if (gen == NULL)
+			goto out;
+		ia5 = ASN1_IA5STRING_new();
+		if (ia5 == NULL)
+			goto out;
+		if (!ASN1_STRING_set(ia5, name, -1))
+			goto out;
+		switch (type)
+			{
+			case GEN_EMAIL:
+			case GEN_DNS:
+				GENERAL_NAME_set0_value(gen, type, ia5);
+				ia5 = NULL;
+				break;
+			default:
+				abort();
+			}
+		sk_GENERAL_NAME_push(gens, gen);
+		gen = NULL;
+	}
+	if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0))
+		goto out;
+	ret = 1;
+ out:
+	ASN1_IA5STRING_free(ia5);
+	GENERAL_NAME_free(gen);
+	GENERAL_NAMES_free(gens);
+	va_end(ap);
+	return ret;
+	}
+
+static int set_cn1(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, name, 0);
+	}
+
+
+static int set_cn_and_email(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, name,
+		      NID_pkcs9_emailAddress, "dummy@example.com", 0);
+	}
+
+static int set_cn2(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, "dummy value",
+		      NID_commonName, name, 0);
+	}
+
+static int set_cn3(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, name,
+		      NID_commonName, "dummy value", 0);
+	}
+
+static int set_email1(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, name, 0);
+	}
+
+static int set_email2(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, "dummy@example.com",
+		      NID_pkcs9_emailAddress, name, 0);
+	}
+
+static int set_email3(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, name,
+		      NID_pkcs9_emailAddress, "dummy@example.com", 0);
+	}
+
+static int set_email_and_cn(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, name,
+		      NID_commonName, "www.example.org", 0);
+	}
+
+static int set_altname_dns(X509 *crt, const char *name)
+	{
+	return set_altname(crt, GEN_DNS, name, 0);
+	}
+
+static int set_altname_email(X509 *crt, const char *name)
+	{
+	return set_altname(crt, GEN_EMAIL, name, 0);
+	}
+
+struct set_name_fn
+	{
+	int (*fn)(X509 *, const char *);
+	const char *name;
+	int host;
+	int email;
+	};
+
+static const struct set_name_fn name_fns[] =
+	{
+	{set_cn1, "set CN", 1, 0},
+	{set_cn2, "set CN", 1, 0},
+	{set_cn3, "set CN", 1, 0},
+	{set_cn_and_email, "set CN", 1, 0},
+	{set_email1, "set emailAddress", 0, 1},
+	{set_email2, "set emailAddress", 0, 1},
+	{set_email3, "set emailAddress", 0, 1},
+	{set_email_and_cn, "set emailAddress", 0, 1},
+	{set_altname_dns, "set dnsName", 1, 0},
+	{set_altname_email, "set rfc822Name", 0, 1},
+	{NULL, NULL, 0}
+	};
+
+static X509 *make_cert()
+	{
+	X509 *ret = NULL;
+	X509 *crt = NULL;
+	X509_NAME *issuer = NULL;
+	crt = X509_new();
+	if (crt == NULL)
+		goto out;
+	if (!X509_set_version(crt, 3))
+		goto out;
+	ret = crt;
+	crt = NULL;
+ out:
+	X509_NAME_free(issuer);
+	return ret;
+	}
+
+static int errors;
+
+static void check_message(const struct set_name_fn *fn, const char *op,
+			  const char *nameincert, int match, const char *name)
+	{
+	char msg[1024];
+	if (match < 0)
+		return;
+	BIO_snprintf(msg, sizeof(msg), "%s: %s: [%s] %s [%s]",
+		 fn->name, op, nameincert,
+		 match ? "matches" : "does not match", name);
+	if (is_exception(msg))
+		return;
+	puts(msg);
+	++errors;
+	}
+
+static void run_cert(X509 *crt, const char *nameincert,
+		     const struct set_name_fn *fn)
+	{
+	const char *const *pname = names;
+	while (*pname)
+		{
+		int samename = strcasecmp(nameincert, *pname) == 0;
+		size_t namelen = strlen(*pname);
+		char *name = malloc(namelen);
+		int match, ret;
+		memcpy(name, *pname, namelen);
+
+		ret = X509_check_host(crt, (const unsigned char *)name,
+				      namelen, 0);
+		match = -1;
+		if (ret < 0)
+			{
+			fprintf(stderr, "internal error in X509_check_host");
+			++errors;
+			}
+		else if (fn->host)
+			{
+			if (ret == 1 && !samename)
+				match = 1;
+			if (ret == 0 && samename)
+				match = 0;
+			}
+		else if (ret == 1)
+			match = 1;
+		check_message(fn, "host", nameincert, match, *pname);
+
+		ret = X509_check_host(crt, (const unsigned char *)name,
+				      namelen, X509_CHECK_FLAG_NO_WILDCARDS);
+		match = -1;
+		if (ret < 0)
+			{
+			fprintf(stderr, "internal error in X509_check_host");
+			++errors;
+			}
+		else if (fn->host)
+			{
+			if (ret == 1 && !samename)
+				match = 1;
+			if (ret == 0 && samename)
+				match = 0;
+			}
+		else if (ret == 1)
+			match = 1;
+		check_message(fn, "host-no-wildcards",
+			      nameincert, match, *pname);
+
+		ret = X509_check_email(crt, (const unsigned char *)name,
+				       namelen, 0);
+		match = -1;
+		if (fn->email)
+			{
+			if (ret && !samename)
+				match = 1;
+			if (!ret && samename && strchr(nameincert, '@') != NULL)
+				match = 0;
+			}
+		else if (ret)
+			match = 1;
+		check_message(fn, "email", nameincert, match, *pname);
+		++pname;
+		free(name);
+		}
+	}
+
+int
+main(void)
+	{
+	const struct set_name_fn *pfn = name_fns;
+	while (pfn->name) {
+		const char *const *pname = names;
+		while (*pname)
+			{
+			X509 *crt = make_cert();
+			if (crt == NULL)
+				{
+				fprintf(stderr, "make_cert failed\n");
+				return 1;
+				}
+			if (!pfn->fn(crt, *pname))
+				{
+				fprintf(stderr, "X509 name setting failed\n");
+				return 1;
+				}
+			run_cert(crt, *pname, pfn);
+			X509_free(crt);
+			++pname;
+			}
+		++pfn;
+	}
+	if (errors == 0) {
+	  printf("PASS\n");
+	}
+	return errors > 0 ? 1 : 0;
+	}
diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h
new file mode 100644
index 0000000..8eae9ce
--- /dev/null
+++ b/crypto/x509v3/x509v3.h
@@ -0,0 +1,855 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999. */
+/* ====================================================================
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#ifndef HEADER_X509V3_H
+#define HEADER_X509V3_H
+
+#include <openssl/bio.h>
+#include <openssl/conf.h>
+#include <openssl/x509.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Forward reference */
+struct v3_ext_method;
+struct v3_ext_ctx;
+
+/* Useful typedefs */
+
+typedef void * (*X509V3_EXT_NEW)(void);
+typedef void (*X509V3_EXT_FREE)(void *);
+typedef void * (*X509V3_EXT_D2I)(void *, const unsigned char ** , long);
+typedef int (*X509V3_EXT_I2D)(void *, unsigned char **);
+typedef STACK_OF(CONF_VALUE) *
+  (*X509V3_EXT_I2V)(const struct v3_ext_method *method, void *ext,
+		    STACK_OF(CONF_VALUE) *extlist);
+typedef void * (*X509V3_EXT_V2I)(const struct v3_ext_method *method,
+				 struct v3_ext_ctx *ctx,
+				 STACK_OF(CONF_VALUE) *values);
+typedef char * (*X509V3_EXT_I2S)(const struct v3_ext_method *method, void *ext);
+typedef void * (*X509V3_EXT_S2I)(const struct v3_ext_method *method,
+				 struct v3_ext_ctx *ctx, const char *str);
+typedef int (*X509V3_EXT_I2R)(const struct v3_ext_method *method, void *ext,
+			      BIO *out, int indent);
+typedef void * (*X509V3_EXT_R2I)(const struct v3_ext_method *method,
+				 struct v3_ext_ctx *ctx, const char *str);
+
+/* V3 extension structure */
+
+struct v3_ext_method {
+int ext_nid;
+int ext_flags;
+/* If this is set the following four fields are ignored */
+ASN1_ITEM_EXP *it;
+/* Old style ASN1 calls */
+X509V3_EXT_NEW ext_new;
+X509V3_EXT_FREE ext_free;
+X509V3_EXT_D2I d2i;
+X509V3_EXT_I2D i2d;
+
+/* The following pair is used for string extensions */
+X509V3_EXT_I2S i2s;
+X509V3_EXT_S2I s2i;
+
+/* The following pair is used for multi-valued extensions */
+X509V3_EXT_I2V i2v;
+X509V3_EXT_V2I v2i;
+
+/* The following are used for raw extensions */
+X509V3_EXT_I2R i2r;
+X509V3_EXT_R2I r2i;
+
+void *usr_data;	/* Any extension specific data */
+};
+
+typedef struct X509V3_CONF_METHOD_st {
+char * (*get_string)(void *db, char *section, char *value);
+STACK_OF(CONF_VALUE) * (*get_section)(void *db, char *section);
+void (*free_string)(void *db, char * string);
+void (*free_section)(void *db, STACK_OF(CONF_VALUE) *section);
+} X509V3_CONF_METHOD;
+
+/* Context specific info */
+struct v3_ext_ctx {
+#define CTX_TEST 0x1
+int flags;
+X509 *issuer_cert;
+X509 *subject_cert;
+X509_REQ *subject_req;
+X509_CRL *crl;
+X509V3_CONF_METHOD *db_meth;
+void *db;
+/* Maybe more here */
+};
+
+typedef struct v3_ext_method X509V3_EXT_METHOD;
+
+DECLARE_STACK_OF(X509V3_EXT_METHOD)
+
+/* ext_flags values */
+#define X509V3_EXT_DYNAMIC	0x1
+#define X509V3_EXT_CTX_DEP	0x2
+#define X509V3_EXT_MULTILINE	0x4
+
+typedef BIT_STRING_BITNAME ENUMERATED_NAMES;
+
+typedef struct BASIC_CONSTRAINTS_st {
+int ca;
+ASN1_INTEGER *pathlen;
+} BASIC_CONSTRAINTS;
+
+
+typedef struct PKEY_USAGE_PERIOD_st {
+ASN1_GENERALIZEDTIME *notBefore;
+ASN1_GENERALIZEDTIME *notAfter;
+} PKEY_USAGE_PERIOD;
+
+typedef struct otherName_st {
+ASN1_OBJECT *type_id;
+ASN1_TYPE *value;
+} OTHERNAME;
+
+typedef struct EDIPartyName_st {
+	ASN1_STRING *nameAssigner;
+	ASN1_STRING *partyName;
+} EDIPARTYNAME;
+
+typedef struct GENERAL_NAME_st {
+
+#define GEN_OTHERNAME	0
+#define GEN_EMAIL	1
+#define GEN_DNS		2
+#define GEN_X400	3
+#define GEN_DIRNAME	4
+#define GEN_EDIPARTY	5
+#define GEN_URI		6
+#define GEN_IPADD	7
+#define GEN_RID		8
+
+int type;
+union {
+	char *ptr;
+	OTHERNAME *otherName; /* otherName */
+	ASN1_IA5STRING *rfc822Name;
+	ASN1_IA5STRING *dNSName;
+	ASN1_TYPE *x400Address;
+	X509_NAME *directoryName;
+	EDIPARTYNAME *ediPartyName;
+	ASN1_IA5STRING *uniformResourceIdentifier;
+	ASN1_OCTET_STRING *iPAddress;
+	ASN1_OBJECT *registeredID;
+
+	/* Old names */
+	ASN1_OCTET_STRING *ip; /* iPAddress */
+	X509_NAME *dirn;		/* dirn */
+	ASN1_IA5STRING *ia5;/* rfc822Name, dNSName, uniformResourceIdentifier */
+	ASN1_OBJECT *rid; /* registeredID */
+	ASN1_TYPE *other; /* x400Address */
+} d;
+} GENERAL_NAME;
+
+typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES;
+
+typedef struct ACCESS_DESCRIPTION_st {
+	ASN1_OBJECT *method;
+	GENERAL_NAME *location;
+} ACCESS_DESCRIPTION;
+
+typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS;
+
+typedef STACK_OF(ASN1_OBJECT) EXTENDED_KEY_USAGE;
+
+DECLARE_STACK_OF(GENERAL_NAME)
+DECLARE_ASN1_SET_OF(GENERAL_NAME)
+
+DECLARE_STACK_OF(ACCESS_DESCRIPTION)
+DECLARE_ASN1_SET_OF(ACCESS_DESCRIPTION)
+
+typedef struct DIST_POINT_NAME_st {
+int type;
+union {
+	GENERAL_NAMES *fullname;
+	STACK_OF(X509_NAME_ENTRY) *relativename;
+} name;
+/* If relativename then this contains the full distribution point name */
+X509_NAME *dpname;
+} DIST_POINT_NAME;
+/* All existing reasons */
+#define CRLDP_ALL_REASONS	0x807f
+
+#define CRL_REASON_NONE				-1
+#define CRL_REASON_UNSPECIFIED			0
+#define CRL_REASON_KEY_COMPROMISE		1
+#define CRL_REASON_CA_COMPROMISE		2
+#define CRL_REASON_AFFILIATION_CHANGED		3
+#define CRL_REASON_SUPERSEDED			4
+#define CRL_REASON_CESSATION_OF_OPERATION	5
+#define CRL_REASON_CERTIFICATE_HOLD		6
+#define CRL_REASON_REMOVE_FROM_CRL		8
+#define CRL_REASON_PRIVILEGE_WITHDRAWN		9
+#define CRL_REASON_AA_COMPROMISE		10
+
+struct DIST_POINT_st {
+DIST_POINT_NAME	*distpoint;
+ASN1_BIT_STRING *reasons;
+GENERAL_NAMES *CRLissuer;
+int dp_reasons;
+};
+
+typedef STACK_OF(DIST_POINT) CRL_DIST_POINTS;
+
+DECLARE_STACK_OF(DIST_POINT)
+DECLARE_ASN1_SET_OF(DIST_POINT)
+
+struct AUTHORITY_KEYID_st {
+ASN1_OCTET_STRING *keyid;
+GENERAL_NAMES *issuer;
+ASN1_INTEGER *serial;
+};
+
+/* Strong extranet structures */
+
+typedef struct SXNET_ID_st {
+	ASN1_INTEGER *zone;
+	ASN1_OCTET_STRING *user;
+} SXNETID;
+
+DECLARE_STACK_OF(SXNETID)
+DECLARE_ASN1_SET_OF(SXNETID)
+
+typedef struct SXNET_st {
+	ASN1_INTEGER *version;
+	STACK_OF(SXNETID) *ids;
+} SXNET;
+
+typedef struct NOTICEREF_st {
+	ASN1_STRING *organization;
+	STACK_OF(ASN1_INTEGER) *noticenos;
+} NOTICEREF;
+
+typedef struct USERNOTICE_st {
+	NOTICEREF *noticeref;
+	ASN1_STRING *exptext;
+} USERNOTICE;
+
+typedef struct POLICYQUALINFO_st {
+	ASN1_OBJECT *pqualid;
+	union {
+		ASN1_IA5STRING *cpsuri;
+		USERNOTICE *usernotice;
+		ASN1_TYPE *other;
+	} d;
+} POLICYQUALINFO;
+
+DECLARE_STACK_OF(POLICYQUALINFO)
+DECLARE_ASN1_SET_OF(POLICYQUALINFO)
+
+typedef struct POLICYINFO_st {
+	ASN1_OBJECT *policyid;
+	STACK_OF(POLICYQUALINFO) *qualifiers;
+} POLICYINFO;
+
+typedef STACK_OF(POLICYINFO) CERTIFICATEPOLICIES;
+
+DECLARE_STACK_OF(POLICYINFO)
+DECLARE_ASN1_SET_OF(POLICYINFO)
+
+typedef struct POLICY_MAPPING_st {
+	ASN1_OBJECT *issuerDomainPolicy;
+	ASN1_OBJECT *subjectDomainPolicy;
+} POLICY_MAPPING;
+
+DECLARE_STACK_OF(POLICY_MAPPING)
+
+typedef STACK_OF(POLICY_MAPPING) POLICY_MAPPINGS;
+
+typedef struct GENERAL_SUBTREE_st {
+	GENERAL_NAME *base;
+	ASN1_INTEGER *minimum;
+	ASN1_INTEGER *maximum;
+} GENERAL_SUBTREE;
+
+DECLARE_STACK_OF(GENERAL_SUBTREE)
+
+struct NAME_CONSTRAINTS_st {
+	STACK_OF(GENERAL_SUBTREE) *permittedSubtrees;
+	STACK_OF(GENERAL_SUBTREE) *excludedSubtrees;
+};
+
+typedef struct POLICY_CONSTRAINTS_st {
+	ASN1_INTEGER *requireExplicitPolicy;
+	ASN1_INTEGER *inhibitPolicyMapping;
+} POLICY_CONSTRAINTS;
+
+/* Proxy certificate structures, see RFC 3820 */
+typedef struct PROXY_POLICY_st
+	{
+	ASN1_OBJECT *policyLanguage;
+	ASN1_OCTET_STRING *policy;
+	} PROXY_POLICY;
+
+typedef struct PROXY_CERT_INFO_EXTENSION_st
+	{
+	ASN1_INTEGER *pcPathLengthConstraint;
+	PROXY_POLICY *proxyPolicy;
+	} PROXY_CERT_INFO_EXTENSION;
+
+DECLARE_ASN1_FUNCTIONS(PROXY_POLICY)
+DECLARE_ASN1_FUNCTIONS(PROXY_CERT_INFO_EXTENSION)
+
+struct ISSUING_DIST_POINT_st
+	{
+	DIST_POINT_NAME *distpoint;
+	int onlyuser;
+	int onlyCA;
+	ASN1_BIT_STRING *onlysomereasons;
+	int indirectCRL;
+	int onlyattr;
+	};
+
+/* Values in idp_flags field */
+/* IDP present */
+#define	IDP_PRESENT	0x1
+/* IDP values inconsistent */
+#define IDP_INVALID	0x2
+/* onlyuser true */
+#define	IDP_ONLYUSER	0x4
+/* onlyCA true */
+#define	IDP_ONLYCA	0x8
+/* onlyattr true */
+#define IDP_ONLYATTR	0x10
+/* indirectCRL true */
+#define IDP_INDIRECT	0x20
+/* onlysomereasons present */
+#define IDP_REASONS	0x40
+
+#define X509V3_conf_err(val) ERR_add_error_data(6, "section:", val->section, \
+",name:", val->name, ",value:", val->value);
+
+#define X509V3_set_ctx_test(ctx) \
+			X509V3_set_ctx(ctx, NULL, NULL, NULL, NULL, CTX_TEST)
+#define X509V3_set_ctx_nodb(ctx) (ctx)->db = NULL;
+
+#define EXT_BITSTRING(nid, table) { nid, 0, ASN1_ITEM_ref(ASN1_BIT_STRING), \
+			0,0,0,0, \
+			0,0, \
+			(X509V3_EXT_I2V)i2v_ASN1_BIT_STRING, \
+			(X509V3_EXT_V2I)v2i_ASN1_BIT_STRING, \
+			NULL, NULL, \
+			table}
+
+#define EXT_IA5STRING(nid) { nid, 0, ASN1_ITEM_ref(ASN1_IA5STRING), \
+			0,0,0,0, \
+			(X509V3_EXT_I2S)i2s_ASN1_IA5STRING, \
+			(X509V3_EXT_S2I)s2i_ASN1_IA5STRING, \
+			0,0,0,0, \
+			NULL}
+
+#define EXT_END { -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+
+/* X509_PURPOSE stuff */
+
+#define EXFLAG_BCONS		0x1
+#define EXFLAG_KUSAGE		0x2
+#define EXFLAG_XKUSAGE		0x4
+#define EXFLAG_NSCERT		0x8
+
+#define EXFLAG_CA		0x10
+/* Really self issued not necessarily self signed */
+#define EXFLAG_SI		0x20
+#define EXFLAG_SS		0x20
+#define EXFLAG_V1		0x40
+#define EXFLAG_INVALID		0x80
+#define EXFLAG_SET		0x100
+#define EXFLAG_CRITICAL		0x200
+#define EXFLAG_PROXY		0x400
+
+#define EXFLAG_INVALID_POLICY	0x800
+#define EXFLAG_FRESHEST		0x1000
+
+#define KU_DIGITAL_SIGNATURE	0x0080
+#define KU_NON_REPUDIATION	0x0040
+#define KU_KEY_ENCIPHERMENT	0x0020
+#define KU_DATA_ENCIPHERMENT	0x0010
+#define KU_KEY_AGREEMENT	0x0008
+#define KU_KEY_CERT_SIGN	0x0004
+#define KU_CRL_SIGN		0x0002
+#define KU_ENCIPHER_ONLY	0x0001
+#define KU_DECIPHER_ONLY	0x8000
+
+#define NS_SSL_CLIENT		0x80
+#define NS_SSL_SERVER		0x40
+#define NS_SMIME		0x20
+#define NS_OBJSIGN		0x10
+#define NS_SSL_CA		0x04
+#define NS_SMIME_CA		0x02
+#define NS_OBJSIGN_CA		0x01
+#define NS_ANY_CA		(NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA)
+
+#define XKU_SSL_SERVER		0x1	
+#define XKU_SSL_CLIENT		0x2
+#define XKU_SMIME		0x4
+#define XKU_CODE_SIGN		0x8
+#define XKU_SGC			0x10
+#define XKU_OCSP_SIGN		0x20
+#define XKU_TIMESTAMP		0x40
+#define XKU_DVCS		0x80
+#define XKU_ANYEKU		0x100
+
+#define X509_PURPOSE_DYNAMIC	0x1
+#define X509_PURPOSE_DYNAMIC_NAME	0x2
+
+typedef struct x509_purpose_st {
+	int purpose;
+	int trust;		/* Default trust ID */
+	int flags;
+	int (*check_purpose)(const struct x509_purpose_st *,
+				const X509 *, int);
+	char *name;
+	char *sname;
+	void *usr_data;
+} X509_PURPOSE;
+
+#define X509_PURPOSE_SSL_CLIENT		1
+#define X509_PURPOSE_SSL_SERVER		2
+#define X509_PURPOSE_NS_SSL_SERVER	3
+#define X509_PURPOSE_SMIME_SIGN		4
+#define X509_PURPOSE_SMIME_ENCRYPT	5
+#define X509_PURPOSE_CRL_SIGN		6
+#define X509_PURPOSE_ANY		7
+#define X509_PURPOSE_OCSP_HELPER	8
+#define X509_PURPOSE_TIMESTAMP_SIGN	9
+
+#define X509_PURPOSE_MIN		1
+#define X509_PURPOSE_MAX		9
+
+/* Flags for X509V3_EXT_print() */
+
+#define X509V3_EXT_UNKNOWN_MASK		(0xfL << 16)
+/* Return error for unknown extensions */
+#define X509V3_EXT_DEFAULT		0
+/* Print error for unknown extensions */
+#define X509V3_EXT_ERROR_UNKNOWN	(1L << 16)
+/* ASN1 parse unknown extensions */
+#define X509V3_EXT_PARSE_UNKNOWN	(2L << 16)
+/* BIO_dump unknown extensions */
+#define X509V3_EXT_DUMP_UNKNOWN		(3L << 16)
+
+/* Flags for X509V3_add1_i2d */
+
+#define X509V3_ADD_OP_MASK		0xfL
+#define X509V3_ADD_DEFAULT		0L
+#define X509V3_ADD_APPEND		1L
+#define X509V3_ADD_REPLACE		2L
+#define X509V3_ADD_REPLACE_EXISTING	3L
+#define X509V3_ADD_KEEP_EXISTING	4L
+#define X509V3_ADD_DELETE		5L
+#define X509V3_ADD_SILENT		0x10
+
+DECLARE_STACK_OF(X509_PURPOSE)
+
+DECLARE_ASN1_FUNCTIONS(BASIC_CONSTRAINTS)
+
+DECLARE_ASN1_FUNCTIONS(SXNET)
+DECLARE_ASN1_FUNCTIONS(SXNETID)
+
+int SXNET_add_id_asc(SXNET **psx, char *zone, char *user, int userlen); 
+int SXNET_add_id_ulong(SXNET **psx, unsigned long lzone, char *user, int userlen); 
+int SXNET_add_id_INTEGER(SXNET **psx, ASN1_INTEGER *izone, char *user, int userlen); 
+
+ASN1_OCTET_STRING *SXNET_get_id_asc(SXNET *sx, char *zone);
+ASN1_OCTET_STRING *SXNET_get_id_ulong(SXNET *sx, unsigned long lzone);
+ASN1_OCTET_STRING *SXNET_get_id_INTEGER(SXNET *sx, ASN1_INTEGER *zone);
+
+DECLARE_ASN1_FUNCTIONS(AUTHORITY_KEYID)
+
+DECLARE_ASN1_FUNCTIONS(PKEY_USAGE_PERIOD)
+
+DECLARE_ASN1_FUNCTIONS(GENERAL_NAME)
+GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a);
+int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b);
+
+
+
+ASN1_BIT_STRING *v2i_ASN1_BIT_STRING(X509V3_EXT_METHOD *method,
+				X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method,
+				ASN1_BIT_STRING *bits,
+				STACK_OF(CONF_VALUE) *extlist);
+
+STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method, GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret);
+int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen);
+
+DECLARE_ASN1_FUNCTIONS(GENERAL_NAMES)
+
+STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(X509V3_EXT_METHOD *method,
+		GENERAL_NAMES *gen, STACK_OF(CONF_VALUE) *extlist);
+GENERAL_NAMES *v2i_GENERAL_NAMES(const X509V3_EXT_METHOD *method,
+				 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
+
+DECLARE_ASN1_FUNCTIONS(OTHERNAME)
+DECLARE_ASN1_FUNCTIONS(EDIPARTYNAME)
+int OTHERNAME_cmp(OTHERNAME *a, OTHERNAME *b);
+void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, void *value);
+void *GENERAL_NAME_get0_value(GENERAL_NAME *a, int *ptype);
+int GENERAL_NAME_set0_othername(GENERAL_NAME *gen,
+				ASN1_OBJECT *oid, ASN1_TYPE *value);
+int GENERAL_NAME_get0_otherName(GENERAL_NAME *gen, 
+				ASN1_OBJECT **poid, ASN1_TYPE **pvalue);
+
+char *i2s_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, ASN1_OCTET_STRING *ia5);
+ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, char *str);
+
+DECLARE_ASN1_FUNCTIONS(EXTENDED_KEY_USAGE)
+int i2a_ACCESS_DESCRIPTION(BIO *bp, ACCESS_DESCRIPTION* a);
+
+DECLARE_ASN1_FUNCTIONS(CERTIFICATEPOLICIES)
+DECLARE_ASN1_FUNCTIONS(POLICYINFO)
+DECLARE_ASN1_FUNCTIONS(POLICYQUALINFO)
+DECLARE_ASN1_FUNCTIONS(USERNOTICE)
+DECLARE_ASN1_FUNCTIONS(NOTICEREF)
+
+DECLARE_ASN1_FUNCTIONS(CRL_DIST_POINTS)
+DECLARE_ASN1_FUNCTIONS(DIST_POINT)
+DECLARE_ASN1_FUNCTIONS(DIST_POINT_NAME)
+DECLARE_ASN1_FUNCTIONS(ISSUING_DIST_POINT)
+
+int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname);
+
+int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc);
+
+DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION)
+DECLARE_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS)
+
+DECLARE_ASN1_ITEM(POLICY_MAPPING)
+DECLARE_ASN1_ALLOC_FUNCTIONS(POLICY_MAPPING)
+DECLARE_ASN1_ITEM(POLICY_MAPPINGS)
+
+DECLARE_ASN1_ITEM(GENERAL_SUBTREE)
+DECLARE_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE)
+
+DECLARE_ASN1_ITEM(NAME_CONSTRAINTS)
+DECLARE_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS)
+
+DECLARE_ASN1_ALLOC_FUNCTIONS(POLICY_CONSTRAINTS)
+DECLARE_ASN1_ITEM(POLICY_CONSTRAINTS)
+
+GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out,
+			       const X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+			       int gen_type, char *value, int is_nc);
+
+GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx,
+			       CONF_VALUE *cnf);
+GENERAL_NAME *v2i_GENERAL_NAME_ex(GENERAL_NAME *out,
+				  const X509V3_EXT_METHOD *method,
+				  X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc);
+void X509V3_conf_free(CONF_VALUE *val);
+
+X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid, char *value);
+X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, char *name, char *value);
+int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, char *section, STACK_OF(X509_EXTENSION) **sk);
+int X509V3_EXT_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509 *cert);
+int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_REQ *req);
+int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_CRL *crl);
+
+X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+				    int ext_nid, char *value);
+X509_EXTENSION *X509V3_EXT_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+				char *name, char *value);
+int X509V3_EXT_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+			char *section, X509 *cert);
+int X509V3_EXT_REQ_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+			    char *section, X509_REQ *req);
+int X509V3_EXT_CRL_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
+			    char *section, X509_CRL *crl);
+
+int X509V3_add_value_bool_nf(char *name, int asn1_bool,
+			     STACK_OF(CONF_VALUE) **extlist);
+int X509V3_get_value_bool(CONF_VALUE *value, int *asn1_bool);
+int X509V3_get_value_int(CONF_VALUE *value, ASN1_INTEGER **aint);
+void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf);
+void X509V3_set_conf_lhash(X509V3_CTX *ctx, LHASH_OF(CONF_VALUE) *lhash);
+
+char * X509V3_get_string(X509V3_CTX *ctx, char *name, char *section);
+STACK_OF(CONF_VALUE) * X509V3_get_section(X509V3_CTX *ctx, char *section);
+void X509V3_string_free(X509V3_CTX *ctx, char *str);
+void X509V3_section_free( X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *section);
+void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
+				 X509_REQ *req, X509_CRL *crl, int flags);
+
+int X509V3_add_value(const char *name, const char *value,
+						STACK_OF(CONF_VALUE) **extlist);
+int X509V3_add_value_uchar(const char *name, const unsigned char *value,
+						STACK_OF(CONF_VALUE) **extlist);
+int X509V3_add_value_bool(const char *name, int asn1_bool,
+						STACK_OF(CONF_VALUE) **extlist);
+int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint,
+						STACK_OF(CONF_VALUE) **extlist);
+char * i2s_ASN1_INTEGER(X509V3_EXT_METHOD *meth, ASN1_INTEGER *aint);
+ASN1_INTEGER * s2i_ASN1_INTEGER(X509V3_EXT_METHOD *meth, char *value);
+char * i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *meth, ASN1_ENUMERATED *aint);
+char * i2s_ASN1_ENUMERATED_TABLE(X509V3_EXT_METHOD *meth, ASN1_ENUMERATED *aint);
+int X509V3_EXT_add(X509V3_EXT_METHOD *ext);
+int X509V3_EXT_add_list(X509V3_EXT_METHOD *extlist);
+int X509V3_EXT_add_alias(int nid_to, int nid_from);
+void X509V3_EXT_cleanup(void);
+
+const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *ext);
+const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid);
+int X509V3_add_standard_extensions(void);
+STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line);
+void *X509V3_EXT_d2i(X509_EXTENSION *ext);
+void *X509V3_get_d2i(STACK_OF(X509_EXTENSION) *x, int nid, int *crit, int *idx);
+
+
+X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc);
+int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, int crit, unsigned long flags);
+
+char *hex_to_string(const unsigned char *buffer, long len);
+unsigned char *string_to_hex(const char *str, long *len);
+int name_cmp(const char *name, const char *cmp);
+
+void X509V3_EXT_val_prn(BIO *out, STACK_OF(CONF_VALUE) *val, int indent,
+								 int ml);
+int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, int indent);
+int X509V3_EXT_print_fp(FILE *out, X509_EXTENSION *ext, int flag, int indent);
+
+int X509V3_extensions_print(BIO *out, char *title, STACK_OF(X509_EXTENSION) *exts, unsigned long flag, int indent);
+
+int X509_check_ca(X509 *x);
+int X509_check_purpose(X509 *x, int id, int ca);
+int X509_supported_extension(X509_EXTENSION *ex);
+int X509_PURPOSE_set(int *p, int purpose);
+int X509_check_issued(X509 *issuer, X509 *subject);
+int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid);
+int X509_PURPOSE_get_count(void);
+X509_PURPOSE * X509_PURPOSE_get0(int idx);
+int X509_PURPOSE_get_by_sname(char *sname);
+int X509_PURPOSE_get_by_id(int id);
+int X509_PURPOSE_add(int id, int trust, int flags,
+			int (*ck)(const X509_PURPOSE *, const X509 *, int),
+				char *name, char *sname, void *arg);
+char *X509_PURPOSE_get0_name(X509_PURPOSE *xp);
+char *X509_PURPOSE_get0_sname(X509_PURPOSE *xp);
+int X509_PURPOSE_get_trust(X509_PURPOSE *xp);
+void X509_PURPOSE_cleanup(void);
+int X509_PURPOSE_get_id(X509_PURPOSE *);
+
+STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x);
+STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x);
+void X509_email_free(STACK_OF(OPENSSL_STRING) *sk);
+STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
+/* Flags for X509_check_* functions */
+
+/* Always check subject name for host match even if subject alt names present */
+#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT	0x1
+/* Disable wild-card matching for dnsName fields and common name. */
+#define X509_CHECK_FLAG_NO_WILDCARDS	0x2
+
+int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags);
+int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags);
+int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags);
+int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags);
+
+ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
+ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc);
+int a2i_ipadd(unsigned char *ipout, const char *ipasc);
+int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE)*dn_sk,
+						unsigned long chtype);
+
+void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent);
+DECLARE_STACK_OF(X509_POLICY_NODE)
+
+/* BEGIN ERROR CODES */
+/* The following lines are auto generated by the script mkerr.pl. Any changes
+ * made after this point may be overwritten when the script is next run.
+ */
+void ERR_load_X509V3_strings(void);
+
+
+#ifdef  __cplusplus
+}
+#endif
+#define X509V3_F_do_ext_i2d 100
+#define X509V3_F_v2i_AUTHORITY_KEYID 101
+#define X509V3_F_X509V3_parse_list 102
+#define X509V3_F_SXNET_add_id_asc 103
+#define X509V3_F_SXNET_get_id_ulong 104
+#define X509V3_F_v2i_AUTHORITY_INFO_ACCESS 105
+#define X509V3_F_X509V3_EXT_add 106
+#define X509V3_F_i2s_ASN1_INTEGER 107
+#define X509V3_F_s2i_ASN1_OCTET_STRING 108
+#define X509V3_F_copy_issuer 109
+#define X509V3_F_v2i_subject_alt 110
+#define X509V3_F_copy_email 111
+#define X509V3_F_X509V3_EXT_i2d 112
+#define X509V3_F_v2i_crld 113
+#define X509V3_F_policy_section 114
+#define X509V3_F_a2i_GENERAL_NAME 115
+#define X509V3_F_hex_to_string 116
+#define X509V3_F_i2s_ASN1_IA5STRING 117
+#define X509V3_F_string_to_hex 118
+#define X509V3_F_v3_generic_extension 119
+#define X509V3_F_X509V3_get_section 120
+#define X509V3_F_s2i_skey_id 121
+#define X509V3_F_nref_nos 122
+#define X509V3_F_X509V3_get_value_bool 123
+#define X509V3_F_v2i_NAME_CONSTRAINTS 124
+#define X509V3_F_v2i_POLICY_MAPPINGS 125
+#define X509V3_F_v2i_GENERAL_NAMES 126
+#define X509V3_F_do_dirname 127
+#define X509V3_F_v2i_ASN1_BIT_STRING 128
+#define X509V3_F_SXNET_add_id_ulong 129
+#define X509V3_F_X509V3_EXT_add_alias 130
+#define X509V3_F_X509V3_add1_i2d 131
+#define X509V3_F_r2i_pci 132
+#define X509V3_F_X509V3_get_string 133
+#define X509V3_F_gnames_from_sectname 134
+#define X509V3_F_r2i_certpol 135
+#define X509V3_F_X509V3_add_value 136
+#define X509V3_F_s2i_ASN1_INTEGER 137
+#define X509V3_F_v2i_issuer_alt 138
+#define X509V3_F_v2i_GENERAL_NAME_ex 139
+#define X509V3_F_X509V3_EXT_nconf 140
+#define X509V3_F_v2i_BASIC_CONSTRAINTS 141
+#define X509V3_F_process_pci_value 142
+#define X509V3_F_notice_section 143
+#define X509V3_F_X509_PURPOSE_set 144
+#define X509V3_F_do_ext_nconf 145
+#define X509V3_F_i2s_ASN1_ENUMERATED 146
+#define X509V3_F_s2i_ASN1_IA5STRING 147
+#define X509V3_F_v2i_POLICY_CONSTRAINTS 148
+#define X509V3_F_v2i_EXTENDED_KEY_USAGE 149
+#define X509V3_F_SXNET_get_id_asc 150
+#define X509V3_F_set_dist_point_name 151
+#define X509V3_F_v2i_idp 152
+#define X509V3_F_X509_PURPOSE_add 153
+#define X509V3_F_SXNET_add_id_INTEGER 154
+#define X509V3_F_i2v_AUTHORITY_INFO_ACCESS 155
+#define X509V3_R_INVALID_BOOLEAN_STRING 100
+#define X509V3_R_POLICY_SYNTAX_NOT_CURRENTLY_SUPPORTED 101
+#define X509V3_R_INVALID_NAME 102
+#define X509V3_R_OPERATION_NOT_DEFINED 103
+#define X509V3_R_POLICY_PATH_LENGTH 104
+#define X509V3_R_INVALID_PROXY_POLICY_SETTING 105
+#define X509V3_R_INVALID_ASRANGE 106
+#define X509V3_R_ERROR_CREATING_EXTENSION 107
+#define X509V3_R_ISSUER_DECODE_ERROR 108
+#define X509V3_R_OTHERNAME_ERROR 109
+#define X509V3_R_ILLEGAL_HEX_DIGIT 110
+#define X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED 111
+#define X509V3_R_USER_TOO_LONG 112
+#define X509V3_R_INVALID_INHERITANCE 113
+#define X509V3_R_INVALID_SAFI 114
+#define X509V3_R_INVALID_NULL_VALUE 115
+#define X509V3_R_NO_SUBJECT_DETAILS 116
+#define X509V3_R_BAD_OBJECT 117
+#define X509V3_R_DIRNAME_ERROR 118
+#define X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT 119
+#define X509V3_R_INVALID_EXTENSION_STRING 120
+#define X509V3_R_NEED_ORGANIZATION_AND_NUMBERS 121
+#define X509V3_R_BN_TO_ASN1_INTEGER_ERROR 122
+#define X509V3_R_INVALID_OPTION 123
+#define X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS 124
+#define X509V3_R_INVALID_POLICY_IDENTIFIER 125
+#define X509V3_R_INVALID_PURPOSE 126
+#define X509V3_R_UNKNOWN_EXTENSION 127
+#define X509V3_R_NO_ISSUER_CERTIFICATE 128
+#define X509V3_R_BN_DEC2BN_ERROR 129
+#define X509V3_R_EXPECTED_A_SECTION_NAME 130
+#define X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED 131
+#define X509V3_R_MISSING_VALUE 132
+#define X509V3_R_SECTION_NOT_FOUND 133
+#define X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED 134
+#define X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED 135
+#define X509V3_R_ILLEGAL_EMPTY_EXTENSION 136
+#define X509V3_R_NO_POLICY_IDENTIFIER 137
+#define X509V3_R_NO_ISSUER_DETAILS 138
+#define X509V3_R_NO_CONFIG_DATABASE 139
+#define X509V3_R_INCORRECT_POLICY_SYNTAX_TAG 140
+#define X509V3_R_INVALID_SECTION 141
+#define X509V3_R_INVALID_IPADDRESS 142
+#define X509V3_R_EXTENSION_VALUE_ERROR 143
+#define X509V3_R_UNABLE_TO_GET_ISSUER_KEYID 144
+#define X509V3_R_INVALID_NULL_ARGUMENT 145
+#define X509V3_R_ERROR_IN_EXTENSION 146
+#define X509V3_R_INVALID_NULL_NAME 147
+#define X509V3_R_BAD_IP_ADDRESS 148
+#define X509V3_R_UNSUPPORTED_OPTION 149
+#define X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY 150
+#define X509V3_R_EXTENSION_EXISTS 151
+#define X509V3_R_UNKNOWN_OPTION 152
+#define X509V3_R_ERROR_CONVERTING_ZONE 153
+#define X509V3_R_NO_PUBLIC_KEY 154
+#define X509V3_R_INVALID_MULTIPLE_RDNS 155
+#define X509V3_R_INVALID_SYNTAX 156
+#define X509V3_R_UNKNOWN_EXTENSION_NAME 157
+#define X509V3_R_ODD_NUMBER_OF_DIGITS 158
+#define X509V3_R_DISTPOINT_ALREADY_SET 159
+#define X509V3_R_UNSUPPORTED_TYPE 160
+#define X509V3_R_EXTENSION_NAME_ERROR 161
+#define X509V3_R_INVALID_NUMBERS 162
+#define X509V3_R_INVALID_NUMBER 163
+#define X509V3_R_INVALID_OBJECT_IDENTIFIER 164
+#define X509V3_R_DUPLICATE_ZONE_ID 165
+#define X509V3_R_EXTENSION_NOT_FOUND 166
+#define X509V3_R_INVALID_ASNUMBER 167
+
+#endif
diff --git a/crypto/x509v3/x509v3_error.c b/crypto/x509v3/x509v3_error.c
new file mode 100644
index 0000000..6a8d582
--- /dev/null
+++ b/crypto/x509v3/x509v3_error.c
@@ -0,0 +1,145 @@
+/* Copyright (c) 2014, 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/err.h>
+
+#include "x509v3.h"
+
+const ERR_STRING_DATA X509V3_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_add_id_INTEGER, 0), "SXNET_add_id_INTEGER"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_add_id_asc, 0), "SXNET_add_id_asc"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_add_id_ulong, 0), "SXNET_add_id_ulong"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_get_id_asc, 0), "SXNET_get_id_asc"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_get_id_ulong, 0), "SXNET_get_id_ulong"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_add, 0), "X509V3_EXT_add"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_add_alias, 0), "X509V3_EXT_add_alias"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_i2d, 0), "X509V3_EXT_i2d"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_nconf, 0), "X509V3_EXT_nconf"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_add1_i2d, 0), "X509V3_add1_i2d"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_add_value, 0), "X509V3_add_value"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_get_section, 0), "X509V3_get_section"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_get_string, 0), "X509V3_get_string"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_get_value_bool, 0), "X509V3_get_value_bool"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_parse_list, 0), "X509V3_parse_list"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509_PURPOSE_add, 0), "X509_PURPOSE_add"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509_PURPOSE_set, 0), "X509_PURPOSE_set"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_a2i_GENERAL_NAME, 0), "a2i_GENERAL_NAME"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_copy_email, 0), "copy_email"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_copy_issuer, 0), "copy_issuer"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_do_dirname, 0), "do_dirname"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_do_ext_i2d, 0), "do_ext_i2d"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_do_ext_nconf, 0), "do_ext_nconf"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_gnames_from_sectname, 0), "gnames_from_sectname"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_hex_to_string, 0), "hex_to_string"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_i2s_ASN1_ENUMERATED, 0), "i2s_ASN1_ENUMERATED"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_i2s_ASN1_IA5STRING, 0), "i2s_ASN1_IA5STRING"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_i2s_ASN1_INTEGER, 0), "i2s_ASN1_INTEGER"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_i2v_AUTHORITY_INFO_ACCESS, 0), "i2v_AUTHORITY_INFO_ACCESS"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_notice_section, 0), "notice_section"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_nref_nos, 0), "nref_nos"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_policy_section, 0), "policy_section"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_process_pci_value, 0), "process_pci_value"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_r2i_certpol, 0), "r2i_certpol"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_r2i_pci, 0), "r2i_pci"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_s2i_ASN1_IA5STRING, 0), "s2i_ASN1_IA5STRING"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_s2i_ASN1_INTEGER, 0), "s2i_ASN1_INTEGER"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_s2i_ASN1_OCTET_STRING, 0), "s2i_ASN1_OCTET_STRING"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_s2i_skey_id, 0), "s2i_skey_id"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_set_dist_point_name, 0), "set_dist_point_name"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_string_to_hex, 0), "string_to_hex"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_ASN1_BIT_STRING, 0), "v2i_ASN1_BIT_STRING"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_AUTHORITY_INFO_ACCESS, 0), "v2i_AUTHORITY_INFO_ACCESS"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_AUTHORITY_KEYID, 0), "v2i_AUTHORITY_KEYID"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_BASIC_CONSTRAINTS, 0), "v2i_BASIC_CONSTRAINTS"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_EXTENDED_KEY_USAGE, 0), "v2i_EXTENDED_KEY_USAGE"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_GENERAL_NAMES, 0), "v2i_GENERAL_NAMES"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_GENERAL_NAME_ex, 0), "v2i_GENERAL_NAME_ex"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_NAME_CONSTRAINTS, 0), "v2i_NAME_CONSTRAINTS"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_POLICY_CONSTRAINTS, 0), "v2i_POLICY_CONSTRAINTS"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_POLICY_MAPPINGS, 0), "v2i_POLICY_MAPPINGS"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_crld, 0), "v2i_crld"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_idp, 0), "v2i_idp"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_issuer_alt, 0), "v2i_issuer_alt"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v2i_subject_alt, 0), "v2i_subject_alt"},
+  {ERR_PACK(ERR_LIB_X509V3, X509V3_F_v3_generic_extension, 0), "v3_generic_extension"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BAD_IP_ADDRESS), "BAD_IP_ADDRESS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BAD_OBJECT), "BAD_OBJECT"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BN_DEC2BN_ERROR), "BN_DEC2BN_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BN_TO_ASN1_INTEGER_ERROR), "BN_TO_ASN1_INTEGER_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DIRNAME_ERROR), "DIRNAME_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DISTPOINT_ALREADY_SET), "DISTPOINT_ALREADY_SET"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DUPLICATE_ZONE_ID), "DUPLICATE_ZONE_ID"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_CONVERTING_ZONE), "ERROR_CONVERTING_ZONE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_CREATING_EXTENSION), "ERROR_CREATING_EXTENSION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_IN_EXTENSION), "ERROR_IN_EXTENSION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXPECTED_A_SECTION_NAME), "EXPECTED_A_SECTION_NAME"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_EXISTS), "EXTENSION_EXISTS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_NAME_ERROR), "EXTENSION_NAME_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_NOT_FOUND), "EXTENSION_NOT_FOUND"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED), "EXTENSION_SETTING_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_VALUE_ERROR), "EXTENSION_VALUE_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ILLEGAL_EMPTY_EXTENSION), "ILLEGAL_EMPTY_EXTENSION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ILLEGAL_HEX_DIGIT), "ILLEGAL_HEX_DIGIT"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG), "INCORRECT_POLICY_SYNTAX_TAG"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_ASNUMBER), "INVALID_ASNUMBER"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_ASRANGE), "INVALID_ASRANGE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_BOOLEAN_STRING), "INVALID_BOOLEAN_STRING"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_EXTENSION_STRING), "INVALID_EXTENSION_STRING"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_INHERITANCE), "INVALID_INHERITANCE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_IPADDRESS), "INVALID_IPADDRESS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_MULTIPLE_RDNS), "INVALID_MULTIPLE_RDNS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NAME), "INVALID_NAME"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_ARGUMENT), "INVALID_NULL_ARGUMENT"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_NAME), "INVALID_NULL_NAME"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_VALUE), "INVALID_NULL_VALUE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NUMBER), "INVALID_NUMBER"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NUMBERS), "INVALID_NUMBERS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_OBJECT_IDENTIFIER), "INVALID_OBJECT_IDENTIFIER"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_OPTION), "INVALID_OPTION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_POLICY_IDENTIFIER), "INVALID_POLICY_IDENTIFIER"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_PROXY_POLICY_SETTING), "INVALID_PROXY_POLICY_SETTING"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_PURPOSE), "INVALID_PURPOSE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SAFI), "INVALID_SAFI"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SECTION), "INVALID_SECTION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SYNTAX), "INVALID_SYNTAX"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ISSUER_DECODE_ERROR), "ISSUER_DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_MISSING_VALUE), "MISSING_VALUE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS), "NEED_ORGANIZATION_AND_NUMBERS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_CONFIG_DATABASE), "NO_CONFIG_DATABASE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_ISSUER_CERTIFICATE), "NO_ISSUER_CERTIFICATE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_ISSUER_DETAILS), "NO_ISSUER_DETAILS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_POLICY_IDENTIFIER), "NO_POLICY_IDENTIFIER"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED), "NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_PUBLIC_KEY), "NO_PUBLIC_KEY"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_SUBJECT_DETAILS), "NO_SUBJECT_DETAILS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ODD_NUMBER_OF_DIGITS), "ODD_NUMBER_OF_DIGITS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_OPERATION_NOT_DEFINED), "OPERATION_NOT_DEFINED"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_OTHERNAME_ERROR), "OTHERNAME_ERROR"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED), "POLICY_LANGUAGE_ALREADY_DEFINED"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_PATH_LENGTH), "POLICY_PATH_LENGTH"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED), "POLICY_PATH_LENGTH_ALREADY_DEFINED"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_SYNTAX_NOT_CURRENTLY_SUPPORTED), "POLICY_SYNTAX_NOT_CURRENTLY_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY), "POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_SECTION_NOT_FOUND), "SECTION_NOT_FOUND"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS), "UNABLE_TO_GET_ISSUER_DETAILS"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID), "UNABLE_TO_GET_ISSUER_KEYID"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT), "UNKNOWN_BIT_STRING_ARGUMENT"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_EXTENSION), "UNKNOWN_EXTENSION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_EXTENSION_NAME), "UNKNOWN_EXTENSION_NAME"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_OPTION), "UNKNOWN_OPTION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNSUPPORTED_OPTION), "UNSUPPORTED_OPTION"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNSUPPORTED_TYPE), "UNSUPPORTED_TYPE"},
+  {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_USER_TOO_LONG), "USER_TOO_LONG"},
+  {0, NULL},
+};