aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/axtls/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/lib/axtls/ssl')
-rw-r--r--circuitpython/lib/axtls/ssl/BigIntConfig.in145
-rw-r--r--circuitpython/lib/axtls/ssl/Config.in341
-rw-r--r--circuitpython/lib/axtls/ssl/Makefile125
-rw-r--r--circuitpython/lib/axtls/ssl/asn1.c776
-rw-r--r--circuitpython/lib/axtls/ssl/cert.h54
-rw-r--r--circuitpython/lib/axtls/ssl/crypto_misc.h213
-rw-r--r--circuitpython/lib/axtls/ssl/gen_cert.c370
-rw-r--r--circuitpython/lib/axtls/ssl/loader.c492
-rw-r--r--circuitpython/lib/axtls/ssl/openssl.c318
-rw-r--r--circuitpython/lib/axtls/ssl/os_port.c0
-rw-r--r--circuitpython/lib/axtls/ssl/os_port.h7
-rw-r--r--circuitpython/lib/axtls/ssl/p12.c483
-rw-r--r--circuitpython/lib/axtls/ssl/private_key.h54
-rw-r--r--circuitpython/lib/axtls/ssl/ssl.h539
-rw-r--r--circuitpython/lib/axtls/ssl/test/Makefile100
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.ca_key.pem27
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.cerbin0 -> 786 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.pem19
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.ca_x509_sha256.pem19
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.encrypted.p8bin0 -> 673 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.encrypted_pem.p817
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_1024bin0 -> 609 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_1024.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_2048bin0 -> 1192 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_2048.pem27
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_4096bin0 -> 2347 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_4096.pem51
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_aes128.pem18
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_aes256.pem18
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_device.pem27
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_end_chain.pem27
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca.pem27
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca2.pem27
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.noname.p12bin0 -> 1612 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.unencrypted.p8bin0 -> 635 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.unencrypted_pem.p816
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.withCA.p12bin0 -> 2521 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.withoutCA.p12bin0 -> 1702 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.cerbin0 -> 604 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha256.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha384.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha512.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.cerbin0 -> 736 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.pem18
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.cerbin0 -> 992 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.pem23
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_aes128.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_aes256.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_after.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_before.pem15
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_device.cerbin0 -> 466 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_device.pem34
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain.pem19
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain_bad.pem19
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca.pem19
-rw-r--r--circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca2.pem37
-rw-r--r--circuitpython/lib/axtls/ssl/test/ca-bundle.crt3585
-rw-r--r--circuitpython/lib/axtls/ssl/test/camster_duckdns_org.crt31
-rw-r--r--circuitpython/lib/axtls/ssl/test/comodo.sha384.cer32
-rw-r--r--circuitpython/lib/axtls/ssl/test/deutsche_telecom.x509_cabin0 -> 670 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/equifax.x509_cabin0 -> 646 bytes
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/gnutls.cerbin0 -> 599 bytes
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/header_issue.datbin0 -> 1159 bytes
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/killgnutls.sh2
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/killopenssl.sh2
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/make_certs.sh253
-rw-r--r--circuitpython/lib/axtls/ssl/test/microsoft.x509_cabin0 -> 1046 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/microsoft.x509_ca.pem24
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/ms_iis.cer13
-rw-r--r--circuitpython/lib/axtls/ssl/test/perf_bigint.c234
-rw-r--r--circuitpython/lib/axtls/ssl/test/qualityssl.com.derbin0 -> 1385 bytes
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/socgen.cerbin0 -> 980 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/ssltest.c2589
-rwxr-xr-xcircuitpython/lib/axtls/ssl/test/test_axssl.sh163
-rw-r--r--circuitpython/lib/axtls/ssl/test/thawte.x509_cabin0 -> 811 bytes
-rw-r--r--circuitpython/lib/axtls/ssl/test/verisign.x509_ca.pem16
-rw-r--r--circuitpython/lib/axtls/ssl/test/verisign.x509_my_cert.pem25
-rwxr-xr-xcircuitpython/lib/axtls/ssl/tls1.c2463
-rwxr-xr-xcircuitpython/lib/axtls/ssl/tls1.h329
-rw-r--r--circuitpython/lib/axtls/ssl/tls1_clnt.c546
-rw-r--r--circuitpython/lib/axtls/ssl/tls1_svr.c537
-rw-r--r--circuitpython/lib/axtls/ssl/x509.c921
83 files changed, 16406 insertions, 0 deletions
diff --git a/circuitpython/lib/axtls/ssl/BigIntConfig.in b/circuitpython/lib/axtls/ssl/BigIntConfig.in
new file mode 100644
index 0000000..116ce17
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/BigIntConfig.in
@@ -0,0 +1,145 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/config/Kconfig-language.txt
+#
+
+menu "BigInt Options"
+ depends on !CONFIG_SSL_SKELETON_MODE
+
+choice
+ prompt "Reduction Algorithm"
+ default CONFIG_BIGINT_BARRETT
+
+config CONFIG_BIGINT_CLASSICAL
+ bool "Classical"
+ help
+ Classical uses standard division. It has no limitations and is
+ theoretically the slowest due to the divisions used. For this particular
+ implementation it is surprisingly quite fast.
+
+config CONFIG_BIGINT_MONTGOMERY
+ bool "Montgomery"
+ help
+ Montgomery uses simple addition and multiplication to achieve its
+ performance. It has the limitation that 0 <= x, y < m, and so is not
+ used when CRT is active.
+
+ This option will not be normally selected.
+
+config CONFIG_BIGINT_BARRETT
+ bool "Barrett"
+ help
+ Barrett performs expensive precomputation before reduction and partial
+ multiplies for computational speed.
+
+ It is about 40% faster than Classical/Montgomery with the expense of
+ about 2kB, and so this option is normally selected.
+
+endchoice
+
+config CONFIG_BIGINT_CRT
+ bool "Chinese Remainder Theorem (CRT)"
+ default y
+ help
+ Allow the Chinese Remainder Theorem (CRT) to be used.
+
+ Uses a number of extra coefficients from the private key to improve the
+ performance of a decryption. This feature is one of the most
+ significant performance improvements (it reduces a decryption time by
+ over 3 times).
+
+ This option should be selected.
+
+config CONFIG_BIGINT_KARATSUBA
+ bool "Karatsuba Multiplication"
+ default n
+ help
+ Allow Karasuba multiplication to be used.
+
+ Uses 3 multiplications (plus a number of additions/subtractions)
+ instead of 4. Multiplications are O(N^2) but addition/subtraction
+ is O(N) hence for large numbers is beneficial. For this project, the
+ effect was only useful for 4096 bit keys (for 32 bit processors). For
+ 8 bit processors this option might be a possibility.
+
+ It costs about 2kB to enable it.
+
+config MUL_KARATSUBA_THRESH
+ int "Karatsuba Multiplication Theshold"
+ default 20
+ depends on CONFIG_BIGINT_KARATSUBA
+ help
+ The minimum number of components needed before Karasuba muliplication
+ is used.
+
+ This is very dependent on the speed/implementation of bi_add()/
+ bi_subtract(). There is a bit of trial and error here and will be
+ at a different point for different architectures.
+
+config SQU_KARATSUBA_THRESH
+ int "Karatsuba Square Threshold"
+ default 40
+ depends on CONFIG_BIGINT_KARATSUBA && CONFIG_BIGINT_SQUARE
+ help
+ The minimum number of components needed before Karatsuba squaring
+ is used.
+
+ This is very dependent on the speed/implementation of bi_add()/
+ bi_subtract(). There is a bit of trial and error here and will be
+ at a different point for different architectures.
+
+config CONFIG_BIGINT_SLIDING_WINDOW
+ bool "Sliding Window Exponentiation"
+ default y
+ help
+ Allow Sliding-Window Exponentiation to be used.
+
+ Potentially processes more than 1 bit at a time when doing
+ exponentiation. The sliding-window technique reduces the number of
+ precomputations compared to other precomputed techniques.
+
+ It results in a considerable performance improvement with it enabled
+ (it halves the decryption time) and so should be selected.
+
+config CONFIG_BIGINT_SQUARE
+ bool "Square Algorithm"
+ default y
+ help
+ Allow squaring to be used instead of a multiplication. It uses
+ 1/2 of the standard multiplies to obtain its performance.
+ It gives a 20% speed improvement overall and so should be selected.
+
+config CONFIG_BIGINT_CHECK_ON
+ bool "BigInt Integrity Checking"
+ default n if !CONFIG_DEBUG
+ default y if CONFIG_DEBUG
+ help
+ This is used when developing bigint algorithms. It performs a sanity
+ check on all operations at the expense of speed.
+
+ This option is only selected when developing and should normally be
+ turned off.
+
+choice
+ prompt "Integer Size"
+ default CONFIG_INTEGER_32BIT
+
+config CONFIG_INTEGER_32BIT
+ bool "32"
+ help
+ The native integer size is 32 bits or higher.
+
+
+config CONFIG_INTEGER_16BIT
+ bool "16"
+ help
+ The native integer size is 16 bits.
+
+config CONFIG_INTEGER_8BIT
+ bool "8"
+ help
+ The native integer size is 8 bits.
+
+endchoice
+endmenu
+
diff --git a/circuitpython/lib/axtls/ssl/Config.in b/circuitpython/lib/axtls/ssl/Config.in
new file mode 100644
index 0000000..aac4144
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/Config.in
@@ -0,0 +1,341 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/config/Kconfig-language.txt
+#
+
+menu "SSL Library"
+
+choice
+ prompt "Mode"
+ default CONFIG_SSL_FULL_MODE
+
+config CONFIG_SSL_SERVER_ONLY
+ bool "Server only - no verification"
+ help
+ Enable server functionality (no client functionality).
+ This mode still supports sessions and chaining (which can be turned
+ off in configuration).
+
+ The axssl sample runs with the minimum of features.
+
+ This is the most space efficient of the modes with the library
+ about 45kB in size. Use this mode if you are doing standard SSL server
+ work.
+
+config CONFIG_SSL_CERT_VERIFICATION
+ bool "Server only - with verification"
+ help
+ Enable server functionality with client authentication (no client
+ functionality).
+
+ The axssl sample runs with the "-verify" and "-CAfile" options.
+
+ This mode produces a library about 49kB in size. Use this mode if you
+ have an SSL server which requires client authentication (which is
+ uncommon in browser applications).
+
+
+config CONFIG_SSL_FULL_MODE
+ bool "Client/Server enabled with diagnostics"
+ help
+ Enable client/server functionality including diagnostics. Most of the
+ extra size in this mode is due to the storage of various strings that
+ are used.
+
+ The axssl sample has 3 more options, "-debug", "-state" and "-show-rsa"
+
+ This mode produces a library about 58kB in size. It is suggested that
+ this mode is used only during development, or systems that have more
+ generous memory limits.
+
+ It is the default to demonstrate the features of axTLS.
+
+config CONFIG_SSL_SKELETON_MODE
+ bool "Skeleton mode - the smallest server mode"
+ help
+ This is an experiment to build the smallest library at the expense of
+ features and speed.
+
+ * Server mode only.
+ * The AES cipher is disabled.
+ * No session resumption.
+ * No external keys/certificates are supported.
+ * The bigint library has most of the performance features disabled.
+ * Some other features/API calls may not work.
+
+ This mode produces a library about 37kB in size. The main
+ disadvantage of this mode is speed - it will be much slower than the
+ other build modes.
+
+endchoice
+
+config CONFIG_SSL_ENABLE_SERVER
+ bool "Server enabled"
+ help
+ Enable server functionality.
+
+config CONFIG_SSL_ENABLE_CLIENT
+ bool "Client enabled"
+ help
+ Enable client functionality.
+
+ The axssl sample runs with the "s_client" option enabled.
+
+ This mode produces a library about 51kB in size. Use this mode if you
+ require axTLS to use SSL client functionality (the SSL server code
+ is always enabled).
+
+config CONFIG_SSL_DIAGNOSTICS
+ bool "Diagnostic messages"
+ help
+ Enable support for diagnostics of connection progress and state.
+
+choice
+ prompt "Protocol Preference"
+# depends on !CONFIG_SSL_SKELETON_MODE
+ default CONFIG_SSL_PROT_MEDIUM
+
+config CONFIG_SSL_PROT_LOW
+ bool "Low"
+ help
+ Chooses the cipher in the order of AES128-SHA, AES128-SHA256,
+ AES256-SHA256.
+
+ This will use the fastest cipher(s) but at the expense of security.
+
+config CONFIG_SSL_PROT_MEDIUM
+ bool "Medium"
+ help
+ Chooses the cipher in the order of AES128-SHA256, AES256-SHA256,
+ AES128-SHA
+
+ This mode is a balance between speed and security and is the default.
+
+config CONFIG_SSL_PROT_HIGH
+ bool "High"
+ help
+ Chooses the cipher in the order of AES256-SHA256, AES128-SHA256,
+ AES128-SHA.
+
+ This will use the strongest cipher(s) at the cost of speed.
+
+endchoice
+
+config CONFIG_SSL_AES
+ bool "Enable AES cipher"
+ default y
+ help
+ Enable/disable AES support.
+
+config CONFIG_SSL_USE_DEFAULT_KEY
+ bool "Enable default key"
+# depends on !CONFIG_SSL_SKELETON_MODE
+ default y
+ help
+ Some applications will not require the default private key/certificate
+ that is built in. This is one way to save on a couple of kB's if an
+ external private key/certificate is used.
+
+ The private key is in ssl/private_key.h and the certificate is in
+ ssl/cert.h.
+
+ The advantage of a built-in private key/certificate is that no file
+ system is required for access. Both the certificate and the private
+ key will be automatically loaded on a ssl_ctx_new().
+
+ However this private key/certificate can never be changed (without a
+ code update).
+
+ This mode is enabled by default. Disable this mode if the
+ built-in key/certificate is not used.
+
+config CONFIG_SSL_PRIVATE_KEY_LOCATION
+ string "Private key file location"
+ depends on !CONFIG_SSL_USE_DEFAULT_KEY && !CONFIG_SSL_SKELETON_MODE
+ help
+ The file location of the private key which will be automatically
+ loaded on a ssl_ctx_new().
+
+config CONFIG_SSL_PRIVATE_KEY_PASSWORD
+ string "Private key password"
+ depends on !CONFIG_SSL_USE_DEFAULT_KEY && CONFIG_SSL_HAS_PEM
+ help
+ The password required to decrypt a PEM-encoded password file.
+
+config CONFIG_SSL_X509_CERT_LOCATION
+ string "X.509 certificate file location"
+ depends on !CONFIG_SSL_GENERATE_X509_CERT && !CONFIG_SSL_USE_DEFAULT_KEY && !CONFIG_SSL_SKELETON_MODE
+ help
+ The file location of the X.509 certificate which will be automatically
+ loaded on a ssl_ctx_new().
+
+config CONFIG_SSL_GENERATE_X509_CERT
+ bool "Generate X.509 Certificate"
+ default n
+ help
+ An X.509 certificate can be automatically generated on a
+ ssl_ctx_new(). A private key still needs to be provided (the private
+ key in ss/private_key.h will be used unless
+ CONFIG_SSL_PRIVATE_KEY_LOCATION is set).
+
+ The certificate is generated on the fly, and so a minor start-up time
+ penalty is to be expected. This feature adds around 5kB to the
+ library.
+
+ This feature is disabled by default.
+
+config CONFIG_SSL_X509_COMMON_NAME
+ string "X.509 Common Name"
+ depends on CONFIG_SSL_GENERATE_X509_CERT
+ help
+ The common name for the X.509 certificate. This should be the fully
+ qualified domain name (FQDN), e.g. www.foo.com.
+
+ If this is blank, then this will be value from gethostname() and
+ getdomainname().
+
+config CONFIG_SSL_X509_ORGANIZATION_NAME
+ string "X.509 Organization Name"
+ depends on CONFIG_SSL_GENERATE_X509_CERT
+ help
+ The organization name for the generated X.509 certificate.
+
+ This field is optional.
+
+config CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME
+ string "X.509 Organization Unit Name"
+ depends on CONFIG_SSL_GENERATE_X509_CERT
+ help
+ The organization unit name for the generated X.509 certificate.
+
+ This field is optional.
+
+config CONFIG_SSL_HAS_PEM
+ bool "Enable PEM"
+ default n if !CONFIG_SSL_FULL_MODE
+ default y if CONFIG_SSL_FULL_MODE
+ depends on !CONFIG_SSL_SKELETON_MODE
+ help
+ Enable the use of PEM format for certificates and private keys.
+
+ PEM is not normally needed - PEM files can be converted into DER files
+ quite easily. However they have the convenience of allowing multiple
+ certificates/keys in the same file.
+
+ This feature will add a couple of kB to the library.
+
+ Disable if PEM is not used (which will be in most cases).
+
+config CONFIG_SSL_USE_PKCS12
+ bool "Use PKCS8/PKCS12"
+ default n if !CONFIG_SSL_FULL_MODE
+ default y if CONFIG_SSL_FULL_MODE
+ depends on !CONFIG_SSL_SKELETON_MODE
+ help
+ PKCS#12 certificates combine private keys and certificates together in
+ one file.
+
+ PKCS#8 private keys are also suppported (as it is a subset of PKCS#12).
+
+ The decryption of these certificates uses RC4-128 (and these
+ certificates must be encrypted using this cipher). The actual
+ algorithm is "PBE-SHA1-RC4-128".
+
+ Disable if PKCS#12 is not used (which will be in most cases).
+
+config CONFIG_SSL_EXPIRY_TIME
+ int "Session expiry time (in hours)"
+ depends on !CONFIG_SSL_SKELETON_MODE
+ default 24
+ help
+ The time (in hours) before a session expires.
+
+ A longer time means that the expensive parts of a handshake don't
+ need to be run when a client reconnects later.
+
+ The default is 1 day.
+
+config CONFIG_X509_MAX_CA_CERTS
+ int "Maximum number of certificate authorites"
+ default 150
+# depends on !CONFIG_SSL_SERVER_ONLY && !CONFIG_SSL_SKELETON_MODE
+ help
+ Determines the number of CA's allowed.
+
+ Increase this figure if more trusted sites are allowed. Each
+ certificate adds about 300 bytes (when added).
+
+ The default is to allow the Debian cert bundle to be parsed.
+
+config CONFIG_SSL_MAX_CERTS
+ int "Maximum number of chained certificates"
+ default 3
+ help
+ Determines the number of certificates used in a certificate
+ chain. The chain length must be at least 1.
+
+ Increase this figure if more certificates are to be added to the
+ chain. Each certificate adds about 300 bytes (when added).
+
+ The default is to allow one certificate + 2 certificates in the chain.
+
+config CONFIG_SSL_CTX_MUTEXING
+ bool "Enable SSL_CTX mutexing"
+ default n
+ help
+ Normally mutexing is not required - each SSL_CTX object can deal with
+ many SSL objects (as long as each SSL_CTX object is using a single
+ thread).
+
+ If the SSL_CTX object is not thread safe e.g. the case where a
+ new thread is created for each SSL object, then mutexing is required.
+
+ Select y when a mutex on the SSL_CTX object is required.
+
+config CONFIG_USE_DEV_URANDOM
+ bool "Use /dev/urandom"
+ default y
+ depends on !CONFIG_PLATFORM_WIN32
+ help
+ Use /dev/urandom. Otherwise a custom RNG is used.
+
+ This will be the default on most Linux systems.
+
+config CONFIG_WIN32_USE_CRYPTO_LIB
+ bool "Use Win32 Crypto Library"
+ depends on CONFIG_PLATFORM_WIN32
+ help
+ Microsoft produce a Crypto API which requires the Platform SDK to be
+ installed. It's used for the RNG.
+
+ This will be the default on most Win32 systems.
+
+config CONFIG_OPENSSL_COMPATIBLE
+ bool "Enable openssl API compatibility"
+ default n
+ help
+ To ease the porting of openssl applications, a subset of the openssl
+ API is wrapped around the axTLS API.
+
+ Note: not all the API is implemented, so parts may still break. And
+ it's definitely not 100% compatible.
+
+config CONFIG_PERFORMANCE_TESTING
+ bool "Build the bigint performance test tool"
+ default n
+ depends on CONFIG_SSL_CERT_VERIFICATION
+ help
+ Used for performance testing of bigint.
+
+ This is a testing tool and is normally disabled.
+
+config CONFIG_SSL_TEST
+ bool "Build the SSL testing tool"
+ default n
+ depends on CONFIG_SSL_FULL_MODE && !CONFIG_SSL_GENERATE_X509_CERT
+ help
+ Used for sanity checking the SSL handshaking.
+
+ This is a testing tool and is normally disabled.
+
+endmenu
diff --git a/circuitpython/lib/axtls/ssl/Makefile b/circuitpython/lib/axtls/ssl/Makefile
new file mode 100644
index 0000000..a222884
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/Makefile
@@ -0,0 +1,125 @@
+#
+# Copyright (c) 2007, Cameron Rich
+#
+# 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 axTLS project 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 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.
+#
+
+AXTLS_HOME=..
+
+include $(AXTLS_HOME)/config/.config
+include $(AXTLS_HOME)/config/makefile.conf
+
+all: libs
+ifdef CONFIG_PERFORMANCE_TESTING
+ $(MAKE) -C test
+else
+ifdef CONFIG_SSL_TEST
+ $(MAKE) -C test
+endif
+endif
+
+ifndef CONFIG_PLATFORM_WIN32
+TARGET1=$(AXTLS_HOME)/$(STAGE)/libaxtls.a
+BASETARGET=libaxtls.so
+CRYPTO_PATH=$(AXTLS_HOME)/crypto/
+ifdef CONFIG_PLATFORM_CYGWIN
+TARGET2=$(AXTLS_HOME)/$(STAGE)/libaxtls.dll.a
+else
+TARGET2=$(AXTLS_HOME)/$(STAGE)/$(LIBMINOR)
+endif
+
+# shared library major/minor numbers
+LIBMAJOR=$(BASETARGET).1
+LIBMINOR=$(BASETARGET).1.2
+else
+TARGET1=$(AXTLS_HOME)/$(STAGE)/axtls.lib
+TARGET2=$(AXTLS_HOME)/$(STAGE)/axtls.dll
+STATIC_LIB=$(AXTLS_HOME)/$(STAGE)/axtls.static.lib
+CRYPTO_PATH=$(AXTLS_HOME)\\crypto\\
+endif
+
+libs: $(TARGET1) #$(TARGET2)
+
+CRYPTO_OBJ=\
+ $(CRYPTO_PATH)aes.o \
+ $(CRYPTO_PATH)bigint.o \
+ $(CRYPTO_PATH)crypto_misc.o \
+ $(CRYPTO_PATH)hmac.o \
+ $(CRYPTO_PATH)md5.o \
+ $(CRYPTO_PATH)rc4.o \
+ $(CRYPTO_PATH)rsa.o \
+ $(CRYPTO_PATH)sha1.o \
+ $(CRYPTO_PATH)sha256.o \
+ $(CRYPTO_PATH)sha384.o \
+ $(CRYPTO_PATH)sha512.o
+
+OBJ=\
+ asn1.o \
+ gen_cert.o \
+ loader.o \
+ openssl.o \
+ os_port.o \
+ p12.o \
+ tls1.o \
+ tls1_svr.o \
+ tls1_clnt.o \
+ x509.o
+
+include $(AXTLS_HOME)/config/makefile.post
+
+ifndef CONFIG_PLATFORM_WIN32 # Linux/Unix/Cygwin
+
+$(TARGET1) : $(CRYPTO_OBJ) $(OBJ)
+ $(AR) -rcs $@ $(CRYPTO_OBJ) $(OBJ)
+
+$(TARGET2) : $(CRYPTO_OBJ) $(OBJ)
+ifndef CONFIG_PLATFORM_CYGWIN
+ $(LD) $(LDFLAGS) $(LDSHARED) -Wl,-soname,$(LIBMAJOR) -o $(AXTLS_HOME)/$(STAGE)/$(LIBMINOR) $(CRYPTO_OBJ) $(OBJ)
+ cd $(AXTLS_HOME)/$(STAGE); ln -sf $(LIBMINOR) $(LIBMAJOR); ln -sf $(LIBMAJOR) $(BASETARGET); cd -
+else
+ $(LD) $(LDFLAGS) $(LDSHARED) -o $(AXTLS_HOME)/$(STAGE)/cygaxtls.dll \
+ -Wl,--out-implib=$(AXTLS_HOME)/$(STAGE)/libaxtls.dll.a \
+ -Wl,--export-all-symbols \
+ -Wl,--enable-auto-import $(CRYPTO_OBJ) $(OBJ)
+endif
+
+else # Win32
+CRYPTO_OBJ:=$(CRYPTO_OBJ:.o=.obj)
+
+$(TARGET1) : $(OBJ)
+ $(AR) /out:$@ $(CRYPTO_OBJ) $(OBJ)
+
+$(TARGET2) : $(OBJ)
+ cp $(TARGET1) $(STATIC_LIB)
+ $(LD) $(LDFLAGS) $(LDSHARED) /out:$@ $(CRYPTO_OBJ) $(OBJ)
+
+endif
+
+clean::
+ $(MAKE) -C test clean
+ -@rm -f $(AXTLS_HOME)/$(STAGE)/* *.a $(TARGET1) $(TARGET2)
+
diff --git a/circuitpython/lib/axtls/ssl/asn1.c b/circuitpython/lib/axtls/ssl/asn1.c
new file mode 100644
index 0000000..ed28eb3
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/asn1.c
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2007-2019, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * Some primitive asn methods for extraction ASN.1 data.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "os_port.h"
+#include "crypto.h"
+#include "crypto_misc.h"
+
+/* 1.2.840.113549.1.1 OID prefix - handle the following */
+/* md5WithRSAEncryption(4) */
+/* sha1WithRSAEncryption(5) */
+/* sha256WithRSAEncryption (11) */
+/* sha384WithRSAEncryption (12) */
+/* sha512WithRSAEncryption (13) */
+static const uint8_t sig_oid_prefix[] =
+{
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
+};
+
+/* 1.3.14.3.2.29 SHA1 with RSA signature */
+static const uint8_t sig_sha1WithRSAEncrypt[] =
+{
+ 0x2b, 0x0e, 0x03, 0x02, 0x1d
+};
+
+/* 2.16.840.1.101.3.4.2.1 SHA-256 */
+static const uint8_t sig_sha256[] =
+{
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+/* 2.16.840.1.101.3.4.2.2 SHA-384 */
+static const uint8_t sig_sha384[] =
+{
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+/* 2.16.840.1.101.3.4.2.3 SHA-512 */
+static const uint8_t sig_sha512[] =
+{
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+static const uint8_t sig_subject_alt_name[] =
+{
+ 0x55, 0x1d, 0x11
+};
+
+static const uint8_t sig_basic_constraints[] =
+{
+ 0x55, 0x1d, 0x13
+};
+
+static const uint8_t sig_key_usage[] =
+{
+ 0x55, 0x1d, 0x0f
+};
+
+/* CN, O, OU, L, C, ST */
+static const uint8_t g_dn_types[] = { 3, 10, 11, 7, 6, 8 };
+
+uint32_t get_asn1_length(const uint8_t *buf, int *offset)
+{
+ int i;
+ uint32_t len;
+
+ if (!(buf[*offset] & 0x80)) /* short form */
+ {
+ len = buf[(*offset)++];
+ }
+ else /* long form */
+ {
+ int length_bytes = buf[(*offset)++]&0x7f;
+ if (length_bytes > 4) /* limit number of bytes */
+ return 0;
+
+ len = 0;
+ for (i = 0; i < length_bytes; i++)
+ {
+ len <<= 8;
+ len += buf[(*offset)++];
+ }
+ }
+
+ return len;
+}
+
+/**
+ * Skip the ASN1.1 object type and its length. Get ready to read the object's
+ * data.
+ */
+int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type)
+{
+ if (buf[*offset] != obj_type)
+ return X509_NOT_OK;
+
+ (*offset)++;
+ return get_asn1_length(buf, offset);
+}
+
+/**
+ * Skip over an ASN.1 object type completely. Get ready to read the next
+ * object.
+ */
+int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type)
+{
+ int len;
+
+ if (buf[*offset] != obj_type)
+ return X509_NOT_OK;
+ (*offset)++;
+ len = get_asn1_length(buf, offset);
+ *offset += len;
+ return 0;
+}
+
+/**
+ * Read an integer value for ASN.1 data
+ * Note: This function allocates memory which must be freed by the user.
+ */
+int asn1_get_big_int(const uint8_t *buf, int *offset, uint8_t **object)
+{
+ int len;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0)
+ goto end_big_int;
+
+ if (len > 1 && buf[*offset] == 0x00) /* ignore the negative byte */
+ {
+ len--;
+ (*offset)++;
+ }
+
+ *object = (uint8_t *)malloc(len);
+ memcpy(*object, &buf[*offset], len);
+ *offset += len;
+
+end_big_int:
+ return len;
+}
+
+/**
+ * Read an integer value for ASN.1 data
+ */
+int asn1_get_int(const uint8_t *buf, int *offset, int32_t *val)
+{
+ int res = X509_OK;
+ int len;
+ int i;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0 ||
+ len > sizeof(int32_t))
+ {
+ res = X509_NOT_OK;
+ goto end_int;
+ }
+
+ *val = 0;
+ for (i = 0; i < len; i++)
+ {
+ *val <<= 8;
+ *val |= buf[(*offset)++];
+ }
+
+end_int:
+ return res;
+}
+
+/**
+ * Read an boolean value for ASN.1 data
+ */
+int asn1_get_bool(const uint8_t *buf, int *offset, bool *val)
+{
+ int res = X509_OK;
+
+ if (asn1_next_obj(buf, offset, ASN1_BOOLEAN) != 1)
+ {
+ res = X509_NOT_OK;
+ goto end_bool;
+ }
+
+ /* DER demands that "If the encoding represents the boolean value TRUE,
+ its single contents octet shall have all eight bits set to one."
+ Thus only 0 and 255 are valid encoded values. */
+ *val = buf[(*offset)++] == 0xFF;
+
+end_bool:
+ return res;
+}
+
+/**
+ * Convert an ASN.1 bit string into a 32 bit integer. Used for key usage
+ */
+int asn1_get_bit_string_as_int(const uint8_t *buf, int *offset, uint32_t *val)
+{
+ int res = X509_OK;
+ int len, i;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_BIT_STRING)) < 0 || len > 5)
+ {
+ res = X509_NOT_OK;
+ goto end_bit_string_as_int;
+ }
+
+ /* number of bits left unused in the final byte of content */
+ (*offset)++;
+ len--;
+ *val = 0;
+
+ /* not sure why key usage doesn't used proper DER spec version */
+ for (i = len-1; i >= 0; --i)
+ {
+ *val <<= 8;
+ *val |= buf[(*offset) + i];
+ }
+
+ *offset += len;
+
+end_bit_string_as_int:
+ return res;
+}
+
+/**
+ * Get all the RSA private key specifics from an ASN.1 encoded file
+ */
+int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx)
+{
+ int offset = 7;
+ uint8_t *modulus = NULL, *priv_exp = NULL, *pub_exp = NULL;
+ int mod_len, priv_len, pub_len;
+#ifdef CONFIG_BIGINT_CRT
+ uint8_t *p = NULL, *q = NULL, *dP = NULL, *dQ = NULL, *qInv = NULL;
+ int p_len, q_len, dP_len, dQ_len, qInv_len;
+#endif
+
+ /* not in der format */
+ if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: This is not a valid ASN.1 file\n");
+#endif
+ return X509_INVALID_PRIV_KEY;
+ }
+
+ /* Use the private key to mix up the RNG if possible. */
+ RNG_custom_init(buf, len);
+
+ mod_len = asn1_get_big_int(buf, &offset, &modulus);
+ pub_len = asn1_get_big_int(buf, &offset, &pub_exp);
+ priv_len = asn1_get_big_int(buf, &offset, &priv_exp);
+
+ if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0)
+ return X509_INVALID_PRIV_KEY;
+
+#ifdef CONFIG_BIGINT_CRT
+ p_len = asn1_get_big_int(buf, &offset, &p);
+ q_len = asn1_get_big_int(buf, &offset, &q);
+ dP_len = asn1_get_big_int(buf, &offset, &dP);
+ dQ_len = asn1_get_big_int(buf, &offset, &dQ);
+ qInv_len = asn1_get_big_int(buf, &offset, &qInv);
+
+ if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0)
+ return X509_INVALID_PRIV_KEY;
+
+ RSA_priv_key_new(rsa_ctx,
+ modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len,
+ p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len);
+
+ free(p);
+ free(q);
+ free(dP);
+ free(dQ);
+ free(qInv);
+#else
+ RSA_priv_key_new(rsa_ctx,
+ modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len);
+#endif
+
+ free(modulus);
+ free(priv_exp);
+ free(pub_exp);
+ return X509_OK;
+}
+
+/**
+ * Get the time of a certificate.
+ */
+static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t)
+{
+ int ret = X509_NOT_OK, len, t_offset, abs_year;
+ struct tm tm;
+
+ /* see http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 */
+ if (buf[*offset] == ASN1_UTC_TIME)
+ {
+ (*offset)++;
+
+ len = get_asn1_length(buf, offset);
+ t_offset = *offset;
+
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0');
+
+ /*if (tm.tm_year < 50) */ /* ignore 1951-2050 part of spec */
+ {
+ tm.tm_year += 100;
+ }
+
+ tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1;
+ tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0');
+ tm.tm_hour = (buf[t_offset+6] - '0')*10 + (buf[t_offset+7] - '0');
+ tm.tm_min = (buf[t_offset+8] - '0')*10 + (buf[t_offset+9] - '0');
+ tm.tm_sec = (buf[t_offset+10] - '0')*10 + (buf[t_offset+11] - '0');
+ *t = mktime(&tm);
+ *offset += len;
+ ret = X509_OK;
+ }
+ else if (buf[*offset] == ASN1_GENERALIZED_TIME)
+ {
+ (*offset)++;
+
+ len = get_asn1_length(buf, offset);
+ t_offset = *offset;
+
+ memset(&tm, 0, sizeof(struct tm));
+ abs_year = ((buf[t_offset] - '0')*1000 +
+ (buf[t_offset+1] - '0')*100 + (buf[t_offset+2] - '0')*10 +
+ (buf[t_offset+3] - '0'));
+
+ if (abs_year <= 1901)
+ {
+ tm.tm_year = 1;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ }
+ else
+ {
+ tm.tm_year = abs_year - 1900;
+ tm.tm_mon = (buf[t_offset+4] - '0')*10 +
+ (buf[t_offset+5] - '0') - 1;
+ tm.tm_mday = (buf[t_offset+6] - '0')*10 + (buf[t_offset+7] - '0');
+ tm.tm_hour = (buf[t_offset+8] - '0')*10 + (buf[t_offset+9] - '0');
+ tm.tm_min = (buf[t_offset+10] - '0')*10 + (buf[t_offset+11] - '0');
+ tm.tm_sec = (buf[t_offset+12] - '0')*10 + (buf[t_offset+13] - '0');
+ *t = mktime(&tm);
+ }
+
+ *offset += len;
+ ret = X509_OK;
+ }
+
+ return ret;
+}
+
+/**
+ * Get the version type of a certificate
+ */
+int asn1_version(const uint8_t *cert, int *offset, int *val)
+{
+ (*offset) += 2; /* get past explicit tag */
+ return asn1_get_int(cert, offset, val);
+}
+
+/**
+ * Retrieve the notbefore and notafter certificate times.
+ */
+int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ asn1_get_utc_time(cert, offset, &x509_ctx->not_before) ||
+ asn1_get_utc_time(cert, offset, &x509_ctx->not_after));
+}
+
+/**
+ * Get the components of a distinguished name
+ */
+static int asn1_get_oid_x520(const uint8_t *buf, int *offset)
+{
+ int dn_type = 0;
+ int len;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
+ goto end_oid;
+
+ /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name
+ components we are interested in. */
+ if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04)
+ dn_type = buf[(*offset)++];
+ else
+ {
+ *offset += len; /* skip over it */
+ }
+
+end_oid:
+ return dn_type;
+}
+
+/**
+ * Obtain an ASN.1 printable string type.
+ */
+static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str)
+{
+ int len = X509_NOT_OK;
+ int asn1_type = buf[*offset];
+
+ /* some certs have this awful crud in them for some reason */
+ if (asn1_type != ASN1_PRINTABLE_STR &&
+ asn1_type != ASN1_PRINTABLE_STR2 &&
+ asn1_type != ASN1_TELETEX_STR &&
+ asn1_type != ASN1_IA5_STR &&
+ asn1_type != ASN1_UNICODE_STR)
+ goto end_pnt_str;
+
+ (*offset)++;
+ len = get_asn1_length(buf, offset);
+
+ if (asn1_type == ASN1_UNICODE_STR)
+ {
+ int i;
+ *str = (char *)malloc(len/2+1); /* allow for null */
+
+ for (i = 0; i < len; i += 2)
+ (*str)[i/2] = buf[*offset + i + 1];
+
+ (*str)[len/2] = 0; /* null terminate */
+ }
+ else
+ {
+ *str = (char *)malloc(len+1); /* allow for null */
+ memcpy(*str, &buf[*offset], len);
+ (*str)[len] = 0; /* null terminate */
+ }
+
+ *offset += len;
+
+end_pnt_str:
+ return len;
+}
+
+/**
+ * Get the subject name (or the issuer) of a certificate.
+ */
+int asn1_name(const uint8_t *cert, int *offset, char *dn[])
+{
+ int ret = X509_NOT_OK;
+ int dn_type;
+ char *tmp;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
+ goto end_name;
+
+ while (asn1_next_obj(cert, offset, ASN1_SET) >= 0)
+ {
+ int i, found = 0;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ (dn_type = asn1_get_oid_x520(cert, offset)) < 0)
+ goto end_name;
+
+ tmp = NULL;
+
+ if (asn1_get_printable_str(cert, offset, &tmp) < 0)
+ {
+ free(tmp);
+ goto end_name;
+ }
+
+ /* find the distinguished named type */
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ if (dn_type == g_dn_types[i])
+ {
+ if (dn[i] == NULL)
+ {
+ dn[i] = tmp;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (found == 0) /* not found so get rid of it */
+ {
+ free(tmp);
+ }
+ }
+
+ ret = X509_OK;
+end_name:
+ return ret;
+}
+
+/**
+ * Read the modulus and public exponent of a certificate.
+ */
+int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK, mod_len, pub_len;
+ uint8_t *modulus = NULL, *pub_exp = NULL;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0)
+ goto end_pub_key;
+
+ (*offset)++; /* ignore the padding bit field */
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
+ goto end_pub_key;
+
+ mod_len = asn1_get_big_int(cert, offset, &modulus);
+ pub_len = asn1_get_big_int(cert, offset, &pub_exp);
+
+ RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len);
+
+ free(modulus);
+ free(pub_exp);
+ ret = X509_OK;
+
+end_pub_key:
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Read the signature of the certificate.
+ */
+int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK;
+
+ if (cert[(*offset)++] != ASN1_BIT_STRING)
+ goto end_sig;
+
+ x509_ctx->sig_len = get_asn1_length(cert, offset)-1;
+ (*offset)++; /* ignore bit string padding bits */
+ x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len);
+ memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len);
+ *offset += x509_ctx->sig_len;
+ ret = X509_OK;
+
+end_sig:
+ return ret;
+}
+
+/*
+ * Compare 2 distinguished name components for equality
+ * @return 0 if a match
+ */
+static int asn1_compare_dn_comp(const char *dn1, const char *dn2)
+{
+ int ret;
+
+ if (dn1 == NULL && dn2 == NULL)
+ ret = 0;
+ else
+ ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 1;
+
+ return ret;
+}
+
+/**
+ * Clean up all of the CA certificates.
+ */
+void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx)
+{
+ int i = 0;
+
+ if (ca_cert_ctx == NULL)
+ return;
+
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ x509_free(ca_cert_ctx->cert[i]);
+ ca_cert_ctx->cert[i++] = NULL;
+ }
+
+ free(ca_cert_ctx);
+}
+
+/*
+ * Compare 2 distinguished names for equality
+ * @return 0 if a match
+ */
+int asn1_compare_dn(char * const dn1[], char * const dn2[])
+{
+ int i;
+
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ if (asn1_compare_dn_comp(dn1[i], dn2[i]))
+ return 1;
+ }
+
+ return 0; /* all good */
+}
+
+int asn1_find_oid(const uint8_t* cert, int* offset,
+ const uint8_t* oid, int oid_length)
+{
+ int seqlen;
+ if ((seqlen = asn1_next_obj(cert, offset, ASN1_SEQUENCE))> 0)
+ {
+ int end = *offset + seqlen;
+
+ while (*offset < end)
+ {
+ int type = cert[(*offset)++];
+ int length = get_asn1_length(cert, offset);
+ int noffset = *offset + length;
+
+ if (type == ASN1_SEQUENCE)
+ {
+ type = cert[(*offset)++];
+ length = get_asn1_length(cert, offset);
+
+ if (type == ASN1_OID && length == oid_length &&
+ memcmp(cert + *offset, oid, oid_length) == 0)
+ {
+ *offset += oid_length;
+ return 1;
+ }
+ }
+
+ *offset = noffset;
+ }
+ }
+
+ return 0;
+}
+
+int asn1_is_subject_alt_name(const uint8_t *cert, int offset)
+{
+ if (asn1_find_oid(cert, &offset, sig_subject_alt_name,
+ sizeof(sig_subject_alt_name)))
+ {
+ return offset;
+ }
+
+ return 0;
+}
+
+int asn1_is_basic_constraints(const uint8_t *cert, int offset)
+{
+ if (asn1_find_oid(cert, &offset, sig_basic_constraints,
+ sizeof(sig_basic_constraints)))
+ {
+ return offset;
+ }
+
+ return 0;
+}
+
+int asn1_is_key_usage(const uint8_t *cert, int offset)
+{
+ if (asn1_find_oid(cert, &offset, sig_key_usage,
+ sizeof(sig_key_usage)))
+ {
+ return offset;
+ }
+
+ return 0;
+}
+
+bool asn1_is_critical_ext(const uint8_t *buf, int *offset)
+{
+ /* critical is optional */
+ bool res = false;
+
+ if (asn1_next_obj(buf, offset, ASN1_BOOLEAN) == 1)
+ res = buf[(*offset)++] == 0xFF;
+
+ return res;
+}
+
+#endif /* CONFIG_SSL_CERT_VERIFICATION */
+
+/**
+ * Read the signature type of the certificate. We only support RSA-MD5 and
+ * RSA-SHA1 signature types.
+ */
+int asn1_signature_type(const uint8_t *cert,
+ int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK, len;
+
+ if (cert[(*offset)++] != ASN1_OID)
+ goto end_check_sig;
+
+ len = get_asn1_length(cert, offset);
+
+ if (len == sizeof(sig_sha1WithRSAEncrypt) &&
+ memcmp(sig_sha1WithRSAEncrypt, &cert[*offset],
+ sizeof(sig_sha1WithRSAEncrypt)) == 0)
+ {
+ x509_ctx->sig_type = SIG_TYPE_SHA1;
+ }
+ else if (len == sizeof(sig_sha256) &&
+ memcmp(sig_sha256, &cert[*offset],
+ sizeof(sig_sha256)) == 0)
+ {
+ x509_ctx->sig_type = SIG_TYPE_SHA256;
+ }
+ else if (len == sizeof(sig_sha384) &&
+ memcmp(sig_sha384, &cert[*offset],
+ sizeof(sig_sha384)) == 0)
+ {
+ x509_ctx->sig_type = SIG_TYPE_SHA384;
+ }
+ else if (len == sizeof(sig_sha512) &&
+ memcmp(sig_sha512, &cert[*offset],
+ sizeof(sig_sha512)) == 0)
+ {
+ x509_ctx->sig_type = SIG_TYPE_SHA512;
+ }
+ else
+ {
+ if (memcmp(sig_oid_prefix, &cert[*offset], sizeof(sig_oid_prefix)))
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ int i;
+ printf("invalid digest: ");
+
+ for (i = 0; i < len; i++)
+ printf("%02x ", cert[*offset + i]);
+
+ printf("\n");
+#endif
+ goto end_check_sig; /* unrecognised cert type */
+ }
+
+ x509_ctx->sig_type = cert[*offset + sizeof(sig_oid_prefix)];
+ }
+
+ *offset += len;
+ asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */
+ ret = X509_OK;
+
+end_check_sig:
+ return ret;
+}
+
diff --git a/circuitpython/lib/axtls/ssl/cert.h b/circuitpython/lib/axtls/ssl/cert.h
new file mode 100644
index 0000000..40234b9
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/cert.h
@@ -0,0 +1,54 @@
+unsigned char default_certificate[] = {
+ 0x30, 0x82, 0x02, 0x58, 0x30, 0x82, 0x01, 0x40, 0x02, 0x09, 0x00, 0xa5,
+ 0x2a, 0xc8, 0x78, 0x87, 0xf2, 0xe7, 0xc5, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x34,
+ 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x29, 0x61,
+ 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
+ 0x20, 0x44, 0x6f, 0x64, 0x67, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x32,
+ 0x33, 0x30, 0x32, 0x31, 0x30, 0x34, 0x32, 0x37, 0x5a, 0x17, 0x0d, 0x33,
+ 0x30, 0x30, 0x39, 0x30, 0x38, 0x32, 0x31, 0x30, 0x34, 0x32, 0x37, 0x5a,
+ 0x30, 0x2c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x61, 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65,
+ 0x63, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x81,
+ 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02,
+ 0x81, 0x81, 0x00, 0xbd, 0x0f, 0xd4, 0x42, 0xa8, 0x74, 0x87, 0x54, 0xaa,
+ 0xb9, 0x3a, 0x1f, 0x8b, 0xce, 0xbd, 0xb7, 0x65, 0xfb, 0x40, 0x3d, 0xd0,
+ 0x11, 0x9a, 0x9c, 0xdc, 0x82, 0x7c, 0xea, 0xa8, 0x17, 0xe1, 0x74, 0xf3,
+ 0x05, 0x0e, 0x61, 0xc1, 0xc1, 0x78, 0x8a, 0xb2, 0xba, 0x15, 0x22, 0x5a,
+ 0xff, 0x9b, 0xb8, 0x7a, 0x2e, 0x0f, 0x88, 0xb7, 0x74, 0xde, 0x04, 0x99,
+ 0xa5, 0xa2, 0x99, 0x53, 0x8b, 0xad, 0x78, 0x5a, 0x31, 0xed, 0xbc, 0x01,
+ 0xe7, 0xdf, 0xe9, 0xec, 0x2f, 0xa0, 0x5d, 0x53, 0xf6, 0xe6, 0x8a, 0xa0,
+ 0xc8, 0x6d, 0x41, 0x45, 0x63, 0x23, 0xb3, 0xcf, 0x4e, 0x50, 0x1f, 0x28,
+ 0xdf, 0x36, 0xe2, 0x73, 0xdf, 0xd6, 0xa1, 0xb3, 0x46, 0x4f, 0x6e, 0xbb,
+ 0x0d, 0x9b, 0xef, 0xa8, 0xf9, 0x4c, 0xa5, 0x71, 0xa1, 0x88, 0xdd, 0x07,
+ 0xa9, 0x86, 0x0d, 0x3f, 0xcd, 0x99, 0x23, 0xa2, 0x84, 0x77, 0x0f, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x32, 0xe0, 0x3c, 0x6e, 0x21, 0xe6, 0xa6, 0xf4, 0xb8, 0x10, 0x9f, 0x8a,
+ 0xe6, 0x0b, 0x84, 0x4e, 0x2c, 0xe5, 0x14, 0xca, 0x56, 0x81, 0x3f, 0xc0,
+ 0x2c, 0xa3, 0x39, 0x89, 0x24, 0xce, 0xaf, 0x47, 0x2e, 0x19, 0x62, 0xb2,
+ 0xe4, 0x76, 0x91, 0x25, 0xbc, 0xe1, 0xa8, 0xee, 0x6a, 0x68, 0x3a, 0x77,
+ 0xb9, 0xb2, 0x62, 0x97, 0x0c, 0x25, 0x3c, 0x5e, 0x13, 0x48, 0x87, 0x80,
+ 0xa3, 0x91, 0xd9, 0x2e, 0xe6, 0x92, 0x2b, 0x1c, 0x52, 0x24, 0xb1, 0x77,
+ 0xc6, 0xf6, 0xde, 0xd8, 0x9b, 0xd9, 0x57, 0x37, 0x56, 0x68, 0x17, 0x32,
+ 0x66, 0x01, 0x08, 0x38, 0x08, 0x9a, 0xc1, 0x8c, 0x5e, 0x3f, 0xe7, 0xc9,
+ 0x44, 0xcb, 0x62, 0xb9, 0x48, 0xc7, 0x89, 0xa6, 0xff, 0x8e, 0x7d, 0x3d,
+ 0xe1, 0x46, 0x32, 0x9c, 0x13, 0x06, 0x9a, 0xd1, 0x17, 0xab, 0x3f, 0xa9,
+ 0x90, 0x04, 0x33, 0x2d, 0x3f, 0x81, 0x0a, 0xa5, 0x55, 0xce, 0xb6, 0x95,
+ 0x54, 0xad, 0xf1, 0x4f, 0xa2, 0xca, 0xc3, 0xf6, 0x25, 0x7b, 0x71, 0xd2,
+ 0x68, 0x85, 0xe9, 0x72, 0xb6, 0x99, 0x34, 0x6d, 0xe5, 0x5f, 0xf6, 0x74,
+ 0x1c, 0xb9, 0xa2, 0xda, 0x2b, 0x04, 0xff, 0x82, 0xc5, 0x09, 0x04, 0xc4,
+ 0xba, 0xbc, 0x82, 0x3e, 0xb4, 0x72, 0x18, 0x8e, 0x30, 0x68, 0x48, 0x4a,
+ 0x0d, 0xa7, 0x3d, 0xb5, 0xf4, 0x42, 0x3a, 0x97, 0x60, 0x7d, 0xa8, 0x61,
+ 0x8a, 0x9e, 0x98, 0xc4, 0x7e, 0x65, 0x99, 0xea, 0x7e, 0xca, 0x75, 0xe7,
+ 0xdb, 0x21, 0x5d, 0xce, 0x7c, 0x66, 0x3d, 0x7e, 0xdc, 0x14, 0xfe, 0x55,
+ 0x04, 0x97, 0xa8, 0x64, 0x12, 0xb4, 0xb5, 0x30, 0x48, 0x72, 0xbc, 0xdb,
+ 0xeb, 0x5b, 0x4f, 0xa6, 0xfb, 0x87, 0x01, 0x41, 0x91, 0xec, 0x98, 0x98,
+ 0xf1, 0x4b, 0x38, 0xa2, 0x40, 0xf1, 0x05, 0x90, 0xbb, 0x9b, 0x5d, 0x96,
+ 0xb1, 0x22, 0x6b, 0x50
+};
+unsigned int default_certificate_len = 604;
diff --git a/circuitpython/lib/axtls/ssl/crypto_misc.h b/circuitpython/lib/axtls/ssl/crypto_misc.h
new file mode 100644
index 0000000..21b59cb
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/crypto_misc.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2007-2017, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+*/
+
+/**
+ * @file crypto_misc.h
+ */
+
+#ifndef HEADER_CRYPTO_MISC_H
+#define HEADER_CRYPTO_MISC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include "crypto.h"
+#include "bigint.h"
+
+/**************************************************************************
+ * X509 declarations
+ **************************************************************************/
+#define X509_OK 0
+#define X509_NOT_OK -1
+#define X509_VFY_ERROR_NO_TRUSTED_CERT -2
+#define X509_VFY_ERROR_BAD_SIGNATURE -3
+#define X509_VFY_ERROR_NOT_YET_VALID -4
+#define X509_VFY_ERROR_EXPIRED -5
+#define X509_VFY_ERROR_SELF_SIGNED -6
+#define X509_VFY_ERROR_INVALID_CHAIN -7
+#define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8
+#define X509_INVALID_PRIV_KEY -9
+#define X509_MAX_CERTS -10
+#define X509_VFY_ERROR_BASIC_CONSTRAINT -11
+
+/*
+ * The Distinguished Name
+ */
+#define X509_NUM_DN_TYPES 6
+#define X509_COMMON_NAME 0
+#define X509_ORGANIZATION 1
+#define X509_ORGANIZATIONAL_UNIT 2
+#define X509_LOCATION 3
+#define X509_COUNTRY 4
+#define X509_STATE 5
+
+/*
+ * Key Usage bits
+ */
+#define IS_SET_KEY_USAGE_FLAG(A, B) (A->key_usage & B)
+
+#define KEY_USAGE_DIGITAL_SIGNATURE 0x0080
+#define KEY_USAGE_NON_REPUDIATION 0x0040
+#define KEY_USAGE_KEY_ENCIPHERMENT 0x0020
+#define KEY_USAGE_DATA_ENCIPHERMENT 0x0010
+#define KEY_USAGE_KEY_AGREEMENT 0x0008
+#define KEY_USAGE_KEY_CERT_SIGN 0x0004
+#define KEY_USAGE_CRL_SIGN 0x0002
+#define KEY_USAGE_ENCIPHER_ONLY 0x0001
+#define KEY_USAGE_DECIPHER_ONLY 0x8000
+
+struct _x509_ctx
+{
+ char *ca_cert_dn[X509_NUM_DN_TYPES];
+ char *cert_dn[X509_NUM_DN_TYPES];
+ char **subject_alt_dnsnames;
+ time_t not_before;
+ time_t not_after;
+ uint8_t *signature;
+ RSA_CTX *rsa_ctx;
+ bigint *digest;
+ uint16_t sig_len;
+ uint8_t sig_type;
+ bool basic_constraint_present;
+ bool basic_constraint_is_critical;
+ bool key_usage_present;
+ bool key_usage_is_critical;
+ bool subject_alt_name_present;
+ bool subject_alt_name_is_critical;
+ bool basic_constraint_cA;
+ int basic_constraint_pathLenConstraint;
+ uint32_t key_usage;
+ struct _x509_ctx *next;
+};
+
+typedef struct _x509_ctx X509_CTX;
+
+//#ifdef CONFIG_SSL_CERT_VERIFICATION
+typedef struct
+{
+ X509_CTX *cert[CONFIG_X509_MAX_CA_CERTS];
+} CA_CERT_CTX;
+//#endif
+
+int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx);
+void x509_free(X509_CTX *x509_ctx);
+//#ifdef CONFIG_SSL_CERT_VERIFICATION
+int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert,
+ int *pathLenConstraint);
+//#endif
+//#ifdef CONFIG_SSL_FULL_MODE
+void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx);
+const char * x509_display_error(int error);
+//#endif
+
+/**************************************************************************
+ * ASN1 declarations
+ **************************************************************************/
+#define ASN1_BOOLEAN 0x01
+#define ASN1_INTEGER 0x02
+#define ASN1_BIT_STRING 0x03
+#define ASN1_OCTET_STRING 0x04
+#define ASN1_NULL 0x05
+#define ASN1_PRINTABLE_STR2 0x0C
+#define ASN1_OID 0x06
+#define ASN1_PRINTABLE_STR2 0x0C
+#define ASN1_PRINTABLE_STR 0x13
+#define ASN1_TELETEX_STR 0x14
+#define ASN1_IA5_STR 0x16
+#define ASN1_UTC_TIME 0x17
+#define ASN1_GENERALIZED_TIME 0x18
+#define ASN1_UNICODE_STR 0x1e
+#define ASN1_SEQUENCE 0x30
+#define ASN1_CONTEXT_DNSNAME 0x82
+#define ASN1_SET 0x31
+#define ASN1_V3_DATA 0xa3
+#define ASN1_IMPLICIT_TAG 0x80
+#define ASN1_CONTEXT_DNSNAME 0x82
+#define ASN1_EXPLICIT_TAG 0xa0
+#define ASN1_V3_DATA 0xa3
+
+#define SIG_TYPE_MD5 0x04
+#define SIG_TYPE_SHA1 0x05
+#define SIG_TYPE_SHA256 0x0b
+#define SIG_TYPE_SHA384 0x0c
+#define SIG_TYPE_SHA512 0x0d
+
+uint32_t get_asn1_length(const uint8_t *buf, int *offset);
+int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx);
+int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type);
+int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type);
+int asn1_get_big_int(const uint8_t *buf, int *offset, uint8_t **object);
+int asn1_get_int(const uint8_t *buf, int *offset, int32_t *val);
+int asn1_get_bool(const uint8_t *buf, int *offset, bool *val);
+int asn1_get_bit_string_as_int(const uint8_t *buf, int *offset, uint32_t *val);
+int asn1_version(const uint8_t *cert, int *offset, int *val);
+int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx);
+int asn1_name(const uint8_t *cert, int *offset, char *dn[]);
+int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx);
+int asn1_compare_dn(char * const dn1[], char * const dn2[]);
+int asn1_is_subject_alt_name(const uint8_t *cert, int offset);
+int asn1_is_basic_constraints(const uint8_t *cert, int offset);
+int asn1_is_key_usage(const uint8_t *cert, int offset);
+bool asn1_is_critical_ext(const uint8_t *buf, int *offset);
+#endif /* CONFIG_SSL_CERT_VERIFICATION */
+int asn1_signature_type(const uint8_t *cert,
+ int *offset, X509_CTX *x509_ctx);
+
+/**************************************************************************
+ * MISC declarations
+ **************************************************************************/
+#define SALT_SIZE 8
+
+extern const char * const unsupported_str;
+
+typedef void (*crypt_func)(void *, const uint8_t *, uint8_t *, int);
+typedef void (*hmac_func)(const uint8_t *msg, int length, const uint8_t *key,
+ int key_len, uint8_t *digest);
+
+int get_file(const char *filename, uint8_t **buf);
+
+#if defined(CONFIG_SSL_DIAGNOSTICS) || defined(WIN32) || defined(CONFIG_DEBUG)
+EXP_FUNC void STDCALL print_blob(const char *format, const uint8_t *data, int size, ...);
+#else
+ #define print_blob(...)
+#endif
+
+EXP_FUNC int STDCALL base64_decode(const char *in, int len,
+ uint8_t *out, int *outlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/gen_cert.c b/circuitpython/lib/axtls/ssl/gen_cert.c
new file mode 100644
index 0000000..17ad38e
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/gen_cert.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2007-2014, Cameron Rich
+ *
+ * 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 axTLS project 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 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 "config.h"
+
+#ifdef CONFIG_SSL_GENERATE_X509_CERT
+#include <string.h>
+#include <stdlib.h>
+#include "os_port.h"
+#include "ssl.h"
+
+/**
+ * Generate a basic X.509 certificate
+ */
+
+static uint8_t set_gen_length(int len, uint8_t *buf, int *offset)
+{
+ if (len < 0x80) /* short form */
+ {
+ buf[(*offset)++] = len;
+ return 1;
+ }
+ else /* long form */
+ {
+ int i, length_bytes = 0;
+
+ if (len & 0x00FF0000)
+ length_bytes = 3;
+ else if (len & 0x0000FF00)
+ length_bytes = 2;
+ else if (len & 0x000000FF)
+ length_bytes = 1;
+
+ buf[(*offset)++] = 0x80 + length_bytes;
+
+ for (i = length_bytes-1; i >= 0; i--)
+ {
+ buf[*offset+i] = len & 0xFF;
+ len >>= 8;
+ }
+
+ *offset += length_bytes;
+ return length_bytes+1;
+ }
+}
+
+static int pre_adjust_with_size(uint8_t type,
+ int *seq_offset, uint8_t *buf, int *offset)
+{
+ buf[(*offset)++] = type;
+ *seq_offset = *offset;
+ *offset += 4; /* fill in later */
+ return *offset;
+}
+
+static void adjust_with_size(int seq_size, int seq_start,
+ uint8_t *buf, int *offset)
+{
+ uint8_t seq_byte_size;
+ int orig_seq_size = seq_size;
+ int orig_seq_start = seq_start;
+
+ seq_size = *offset-seq_size;
+ seq_byte_size = set_gen_length(seq_size, buf, &seq_start);
+
+ if (seq_byte_size != 4)
+ {
+ memmove(&buf[orig_seq_start+seq_byte_size],
+ &buf[orig_seq_size], seq_size);
+ *offset -= 4-seq_byte_size;
+ }
+}
+
+static void gen_serial_number(uint8_t *buf, int *offset)
+{
+ static const uint8_t ser_oid[] = { ASN1_INTEGER, 1, 0x7F };
+ memcpy(&buf[*offset], ser_oid , sizeof(ser_oid));
+ *offset += sizeof(ser_oid);
+}
+
+static void gen_signature_alg(uint8_t *buf, int *offset)
+{
+ /* OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5) */
+ static const uint8_t sig_oid[] =
+ {
+ ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ ASN1_NULL, 0x00
+ };
+
+ memcpy(&buf[*offset], sig_oid, sizeof(sig_oid));
+ *offset += sizeof(sig_oid);
+}
+
+static int gen_dn(const char *name, uint8_t dn_type,
+ uint8_t *buf, int *offset)
+{
+ int ret = X509_OK;
+ int name_size = strlen(name);
+
+ if (name_size > 0x70) /* just too big */
+ {
+ ret = X509_NOT_OK;
+ goto error;
+ }
+
+ buf[(*offset)++] = ASN1_SET;
+ set_gen_length(9+name_size, buf, offset);
+ buf[(*offset)++] = ASN1_SEQUENCE;
+ set_gen_length(7+name_size, buf, offset);
+ buf[(*offset)++] = ASN1_OID;
+ buf[(*offset)++] = 3;
+ buf[(*offset)++] = 0x55;
+ buf[(*offset)++] = 0x04;
+ buf[(*offset)++] = dn_type;
+ buf[(*offset)++] = ASN1_PRINTABLE_STR;
+ buf[(*offset)++] = name_size;
+ strcpy((char *)&buf[*offset], name);
+ *offset += name_size;
+
+error:
+ return ret;
+}
+
+static int gen_issuer(const char * dn[], uint8_t *buf, int *offset)
+{
+ int ret = X509_OK;
+ int seq_offset;
+ int seq_size = pre_adjust_with_size(
+ ASN1_SEQUENCE, &seq_offset, buf, offset);
+ char fqdn[128];
+
+ /* we need the common name, so if not configured, work out the fully
+ * qualified domain name */
+ if (dn[X509_COMMON_NAME] == NULL || strlen(dn[X509_COMMON_NAME]) == 0)
+ {
+ int fqdn_len;
+ gethostname(fqdn, sizeof(fqdn));
+ fqdn_len = strlen(fqdn);
+ fqdn[fqdn_len++] = '.';
+
+ if (getdomainname(&fqdn[fqdn_len], sizeof(fqdn)-fqdn_len) < 0)
+ {
+ ret = X509_NOT_OK;
+ goto error;
+ }
+
+ fqdn_len = strlen(fqdn);
+
+ if (fqdn[fqdn_len-1] == '.') /* ensure '.' is not last char */
+ fqdn[fqdn_len-1] = 0;
+
+ dn[X509_COMMON_NAME] = fqdn;
+ }
+
+ if ((ret = gen_dn(dn[X509_COMMON_NAME], 3, buf, offset)))
+ goto error;
+
+ if (dn[X509_ORGANIZATION] != NULL && strlen(dn[X509_ORGANIZATION]) > 0)
+ {
+ if ((ret = gen_dn(dn[X509_ORGANIZATION], 10, buf, offset)))
+ goto error;
+ }
+
+ if (dn[X509_ORGANIZATIONAL_UNIT] != NULL &&
+ strlen(dn[X509_ORGANIZATIONAL_UNIT]) > 0)
+ {
+ if ((ret = gen_dn(dn[X509_ORGANIZATIONAL_UNIT], 11, buf, offset)))
+ goto error;
+ }
+
+ adjust_with_size(seq_size, seq_offset, buf, offset);
+
+error:
+ return ret;
+}
+
+static void gen_utc_time(uint8_t *buf, int *offset)
+{
+ static const uint8_t time_seq[] =
+ {
+ ASN1_SEQUENCE, 30,
+ ASN1_UTC_TIME, 13,
+ '0', '7', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z',
+ ASN1_UTC_TIME, 13, /* make it good for 30 or so years */
+ '3', '8', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z'
+ };
+
+ /* fixed time */
+ memcpy(&buf[*offset], time_seq, sizeof(time_seq));
+ *offset += sizeof(time_seq);
+}
+
+static void gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset)
+{
+ static const uint8_t pub_key_seq[] =
+ {
+ ASN1_INTEGER, 0x03, 0x01, 0x00, 0x01 /* INTEGER 65537 */
+ };
+
+ int seq_offset;
+ int pub_key_size = rsa_ctx->num_octets;
+ uint8_t *block = (uint8_t *)alloca(pub_key_size);
+ int seq_size = pre_adjust_with_size(
+ ASN1_SEQUENCE, &seq_offset, buf, offset);
+ buf[(*offset)++] = ASN1_INTEGER;
+ bi_export(rsa_ctx->bi_ctx, rsa_ctx->m, block, pub_key_size);
+
+ if (*block & 0x80) /* make integer positive */
+ {
+ set_gen_length(pub_key_size+1, buf, offset);
+ buf[(*offset)++] = 0;
+ }
+ else
+ set_gen_length(pub_key_size, buf, offset);
+
+ memcpy(&buf[*offset], block, pub_key_size);
+ *offset += pub_key_size;
+ memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq));
+ *offset += sizeof(pub_key_seq);
+ adjust_with_size(seq_size, seq_offset, buf, offset);
+}
+
+static void gen_pub_key1(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset)
+{
+ int seq_offset;
+ int seq_size = pre_adjust_with_size(
+ ASN1_BIT_STRING, &seq_offset, buf, offset);
+ buf[(*offset)++] = 0; /* bit string is multiple of 8 */
+ gen_pub_key2(rsa_ctx, buf, offset);
+ adjust_with_size(seq_size, seq_offset, buf, offset);
+}
+
+static void gen_pub_key(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset)
+{
+ /* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) */
+ static const uint8_t rsa_enc_oid[] =
+ {
+ ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ ASN1_NULL, 0x00
+ };
+
+ int seq_offset;
+ int seq_size = pre_adjust_with_size(
+ ASN1_SEQUENCE, &seq_offset, buf, offset);
+
+ memcpy(&buf[*offset], rsa_enc_oid, sizeof(rsa_enc_oid));
+ *offset += sizeof(rsa_enc_oid);
+ gen_pub_key1(rsa_ctx, buf, offset);
+ adjust_with_size(seq_size, seq_offset, buf, offset);
+}
+
+static void gen_signature(const RSA_CTX *rsa_ctx, const uint8_t *sha_dgst,
+ uint8_t *buf, int *offset)
+{
+ static const uint8_t asn1_sig[] =
+ {
+ ASN1_SEQUENCE, 0x21, ASN1_SEQUENCE, 0x09, ASN1_OID, 0x05,
+ 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* sha1 (1 3 14 3 2 26) */
+ ASN1_NULL, 0x00, ASN1_OCTET_STRING, 0x14
+ };
+
+ uint8_t *enc_block = (uint8_t *)alloca(rsa_ctx->num_octets);
+ uint8_t *block = (uint8_t *)alloca(sizeof(asn1_sig) + SHA1_SIZE);
+ int sig_size;
+
+ /* add the digest as an embedded asn.1 sequence */
+ memcpy(block, asn1_sig, sizeof(asn1_sig));
+ memcpy(&block[sizeof(asn1_sig)], sha_dgst, SHA1_SIZE);
+
+ sig_size = RSA_encrypt(rsa_ctx, block,
+ sizeof(asn1_sig) + SHA1_SIZE, enc_block, 1);
+
+ buf[(*offset)++] = ASN1_BIT_STRING;
+ set_gen_length(sig_size+1, buf, offset);
+ buf[(*offset)++] = 0; /* bit string is multiple of 8 */
+ memcpy(&buf[*offset], enc_block, sig_size);
+ *offset += sig_size;
+}
+
+static int gen_tbs_cert(const char * dn[],
+ const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset,
+ uint8_t *sha_dgst)
+{
+ int ret = X509_OK;
+ SHA1_CTX sha_ctx;
+ int seq_offset;
+ int begin_tbs = *offset;
+ int seq_size = pre_adjust_with_size(
+ ASN1_SEQUENCE, &seq_offset, buf, offset);
+
+ gen_serial_number(buf, offset);
+ gen_signature_alg(buf, offset);
+
+ /* CA certicate issuer */
+ if ((ret = gen_issuer(dn, buf, offset)))
+ goto error;
+
+ gen_utc_time(buf, offset);
+
+ /* certificate issuer */
+ if ((ret = gen_issuer(dn, buf, offset)))
+ goto error;
+
+ gen_pub_key(rsa_ctx, buf, offset);
+ adjust_with_size(seq_size, seq_offset, buf, offset);
+
+ SHA1_Init(&sha_ctx);
+ SHA1_Update(&sha_ctx, &buf[begin_tbs], *offset-begin_tbs);
+ SHA1_Final(sha_dgst, &sha_ctx);
+
+error:
+ return ret;
+}
+
+/**
+ * Create a new certificate.
+ */
+EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data)
+{
+ int ret = X509_OK, offset = 0, seq_offset;
+ /* allocate enough space to load a new certificate */
+ uint8_t *buf = (uint8_t *)alloca(ssl_ctx->rsa_ctx->num_octets*2 + 512);
+ uint8_t sha_dgst[SHA1_SIZE];
+ int seq_size = pre_adjust_with_size(ASN1_SEQUENCE,
+ &seq_offset, buf, &offset);
+
+ if ((ret = gen_tbs_cert(dn, ssl_ctx->rsa_ctx, buf, &offset, sha_dgst)) < 0)
+ goto error;
+
+ gen_signature_alg(buf, &offset);
+ gen_signature(ssl_ctx->rsa_ctx, sha_dgst, buf, &offset);
+ adjust_with_size(seq_size, seq_offset, buf, &offset);
+ *cert_data = (uint8_t *)malloc(offset); /* create the exact memory for it */
+ memcpy(*cert_data, buf, offset);
+
+error:
+ return ret < 0 ? ret : offset;
+}
+
+#endif
+
diff --git a/circuitpython/lib/axtls/ssl/loader.c b/circuitpython/lib/axtls/ssl/loader.c
new file mode 100644
index 0000000..38ed0bf
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/loader.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2007-2014, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * Load certificates/keys into memory. These can be in many different formats.
+ * PEM support and other formats can be processed here.
+ *
+ * The PEM private keys may be optionally encrypted with AES128 or AES256.
+ * The encrypted PEM keys were generated with something like:
+ *
+ * openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "os_port.h"
+#include "ssl.h"
+
+#if CONFIG_SSL_ENABLE_SERVER
+
+static int do_obj(SSL_CTX *ssl_ctx, int obj_type,
+ SSLObjLoader *ssl_obj, const char *password);
+#ifdef CONFIG_SSL_HAS_PEM
+static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type,
+ SSLObjLoader *ssl_obj, const char *password);
+#endif
+
+/*
+ * Load a file into memory that is in binary DER (or ascii PEM) format.
+ */
+EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type,
+ const char *filename, const char *password)
+{
+#ifndef CONFIG_SSL_SKELETON_MODE
+ static const char * const begin = "-----BEGIN";
+ int ret = SSL_OK;
+ SSLObjLoader *ssl_obj = NULL;
+
+ if (filename == NULL)
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+
+ ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader));
+ ssl_obj->len = get_file(filename, &ssl_obj->buf);
+ if (ssl_obj->len <= 0)
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+
+ /* is the file a PEM file? */
+ if (strstr((char *)ssl_obj->buf, begin) != NULL)
+ {
+#ifdef CONFIG_SSL_HAS_PEM
+ ret = ssl_obj_PEM_load(ssl_ctx, obj_type, ssl_obj, password);
+#else
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("%s", unsupported_str);
+#endif
+ ret = SSL_ERROR_NOT_SUPPORTED;
+#endif
+ }
+ else
+ ret = do_obj(ssl_ctx, obj_type, ssl_obj, password);
+
+error:
+ ssl_obj_free(ssl_obj);
+ return ret;
+#else
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("%s", unsupported_str);
+#endif
+ return SSL_ERROR_NOT_SUPPORTED;
+#endif /* CONFIG_SSL_SKELETON_MODE */
+}
+
+/*
+ * Transfer binary data into the object loader.
+ */
+EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int mem_type,
+ const uint8_t *data, int len, const char *password)
+{
+ int ret;
+ SSLObjLoader *ssl_obj;
+
+ ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader));
+ ssl_obj->buf = (uint8_t *)malloc(len);
+ memcpy(ssl_obj->buf, data, len);
+ ssl_obj->len = len;
+ ret = do_obj(ssl_ctx, mem_type, ssl_obj, password);
+ ssl_obj_free(ssl_obj);
+ return ret;
+}
+
+/*
+ * Actually work out what we are doing
+ */
+static int do_obj(SSL_CTX *ssl_ctx, int obj_type,
+ SSLObjLoader *ssl_obj, const char *password)
+{
+ int ret = SSL_OK;
+
+ switch (obj_type)
+ {
+ case SSL_OBJ_RSA_KEY:
+ ret = add_private_key(ssl_ctx, ssl_obj);
+ break;
+
+ case SSL_OBJ_X509_CERT:
+ ret = add_cert(ssl_ctx, ssl_obj->buf, ssl_obj->len);
+ break;
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ case SSL_OBJ_X509_CACERT:
+ add_cert_auth(ssl_ctx, ssl_obj->buf, ssl_obj->len);
+ break;
+#endif
+
+#ifdef CONFIG_SSL_USE_PKCS12
+ case SSL_OBJ_PKCS8:
+ ret = pkcs8_decode(ssl_ctx, ssl_obj, password);
+ break;
+
+ case SSL_OBJ_PKCS12:
+ ret = pkcs12_decode(ssl_ctx, ssl_obj, password);
+ break;
+#endif
+ default:
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("%s", unsupported_str);
+#endif
+ ret = SSL_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Clean up our mess.
+ */
+void ssl_obj_free(SSLObjLoader *ssl_obj)
+{
+ if (ssl_obj)
+ {
+ free(ssl_obj->buf);
+ free(ssl_obj);
+ }
+}
+
+/*
+ * Support for PEM encoded keys/certificates.
+ */
+#ifdef CONFIG_SSL_HAS_PEM
+
+#define NUM_PEM_TYPES 4
+#define IV_SIZE 16
+#define IS_RSA_PRIVATE_KEY 0
+#define IS_ENCRYPTED_PRIVATE_KEY 1
+#define IS_PRIVATE_KEY 2
+#define IS_CERTIFICATE 3
+
+static const char * const begins[NUM_PEM_TYPES] =
+{
+ "-----BEGIN RSA PRIVATE KEY-----",
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----",
+ "-----BEGIN PRIVATE KEY-----",
+ "-----BEGIN CERTIFICATE-----",
+};
+
+static const char * const ends[NUM_PEM_TYPES] =
+{
+ "-----END RSA PRIVATE KEY-----",
+ "-----END ENCRYPTED PRIVATE KEY-----",
+ "-----END PRIVATE KEY-----",
+ "-----END CERTIFICATE-----",
+};
+
+static const char * const aes_str[2] =
+{
+ "DEK-Info: AES-128-CBC,",
+ "DEK-Info: AES-256-CBC,"
+};
+
+/**
+ * Take a base64 blob of data and decrypt it (using AES) into its
+ * proper ASN.1 form.
+ */
+static int pem_decrypt(const char *where, const char *end,
+ const char *password, SSLObjLoader *ssl_obj)
+{
+ int ret = -1;
+ int is_aes_256 = 0;
+ char *start = NULL;
+ uint8_t iv[IV_SIZE];
+ int i, pem_size;
+ MD5_CTX md5_ctx;
+ AES_CTX aes_ctx;
+ uint8_t key[32]; /* AES256 size */
+
+ if (password == NULL || strlen(password) == 0)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: Need a password for this PEM file\n");
+#endif
+ goto error;
+ }
+
+ if ((start = strstr((const char *)where, aes_str[0]))) /* AES128? */
+ {
+ start += strlen(aes_str[0]);
+ }
+ else if ((start = strstr((const char *)where, aes_str[1]))) /* AES256? */
+ {
+ is_aes_256 = 1;
+ start += strlen(aes_str[1]);
+ }
+ else
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: Unsupported password cipher\n");
+#endif
+ goto error;
+ }
+
+ /* convert from hex to binary - assumes uppercase hex */
+ for (i = 0; i < IV_SIZE; i++)
+ {
+ char c = *start++ - '0';
+ iv[i] = (c > 9 ? c + '0' - 'A' + 10 : c) << 4;
+ c = *start++ - '0';
+ iv[i] += (c > 9 ? c + '0' - 'A' + 10 : c);
+ }
+
+ while (*start == '\r' || *start == '\n')
+ start++;
+
+ /* turn base64 into binary */
+ pem_size = (int)(end-start);
+ if (base64_decode(start, pem_size, ssl_obj->buf, &ssl_obj->len) != 0)
+ goto error;
+
+ /* work out the key */
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, (const uint8_t *)password, strlen(password));
+ MD5_Update(&md5_ctx, iv, SALT_SIZE);
+ MD5_Final(key, &md5_ctx);
+
+ if (is_aes_256)
+ {
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, key, MD5_SIZE);
+ MD5_Update(&md5_ctx, (const uint8_t *)password, strlen(password));
+ MD5_Update(&md5_ctx, iv, SALT_SIZE);
+ MD5_Final(&key[MD5_SIZE], &md5_ctx);
+ }
+
+ /* decrypt using the key/iv */
+ AES_set_key(&aes_ctx, key, iv, is_aes_256 ? AES_MODE_256 : AES_MODE_128);
+ AES_convert_key(&aes_ctx);
+ AES_cbc_decrypt(&aes_ctx, ssl_obj->buf, ssl_obj->buf, ssl_obj->len);
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/**
+ * Take a base64 blob of data and turn it into its proper ASN.1 form.
+ */
+static int new_pem_obj(SSL_CTX *ssl_ctx, int is_cacert, char *where,
+ int remain, const char *password)
+{
+ int ret = SSL_ERROR_BAD_CERTIFICATE;
+ SSLObjLoader *ssl_obj = NULL;
+
+ while (remain > 0)
+ {
+ int i, pem_size, obj_type;
+ char *start = NULL, *end = NULL;
+
+ for (i = 0; i < NUM_PEM_TYPES; i++)
+ {
+ if ((start = strstr(where, begins[i])) &&
+ (end = strstr(where, ends[i])))
+ {
+ remain -= (int)(end-where);
+ start += strlen(begins[i]);
+ pem_size = (int)(end-start);
+
+ ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader));
+
+ /* 4/3 bigger than what we need but so what */
+ ssl_obj->buf = (uint8_t *)calloc(1, pem_size);
+ ssl_obj->len = pem_size;
+
+ if (i == IS_RSA_PRIVATE_KEY &&
+ strstr(start, "Proc-Type:") &&
+ strstr(start, "4,ENCRYPTED"))
+ {
+ /* check for encrypted PEM file */
+ if (pem_decrypt(start, end, password, ssl_obj) < 0)
+ {
+ ret = SSL_ERROR_BAD_CERTIFICATE;
+ goto error;
+ }
+ }
+ else
+ {
+ ssl_obj->len = pem_size;
+ if (base64_decode(start, pem_size,
+ ssl_obj->buf, &ssl_obj->len) != 0)
+ {
+ ret = SSL_ERROR_BAD_CERTIFICATE;
+ goto error;
+ }
+ }
+
+ switch (i)
+ {
+ case IS_RSA_PRIVATE_KEY:
+ obj_type = SSL_OBJ_RSA_KEY;
+ break;
+
+ case IS_ENCRYPTED_PRIVATE_KEY:
+ case IS_PRIVATE_KEY:
+ obj_type = SSL_OBJ_PKCS8;
+ break;
+
+ case IS_CERTIFICATE:
+ obj_type = is_cacert ?
+ SSL_OBJ_X509_CACERT : SSL_OBJ_X509_CERT;
+ break;
+
+ default:
+ ret = SSL_ERROR_BAD_CERTIFICATE;
+ goto error;
+ }
+
+ /* In a format we can now understand - so process it */
+ if ((ret = do_obj(ssl_ctx, obj_type, ssl_obj, password)))
+ goto error;
+
+ end += strlen(ends[i]);
+ remain -= strlen(ends[i]);
+ while (remain > 0 && (*end == '\r' || *end == '\n'))
+ {
+ end++;
+ remain--;
+ }
+
+ where = end;
+ break;
+ }
+ }
+
+ ssl_obj_free(ssl_obj);
+ ssl_obj = NULL;
+ if (start == NULL)
+ break;
+ }
+error:
+ ssl_obj_free(ssl_obj);
+ return ret;
+}
+
+/*
+ * Load a file into memory that is in ASCII PEM format.
+ */
+static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type,
+ SSLObjLoader *ssl_obj, const char *password)
+{
+ char *start;
+
+ /* add a null terminator */
+ ssl_obj->len++;
+ ssl_obj->buf = (uint8_t *)realloc(ssl_obj->buf, ssl_obj->len);
+ ssl_obj->buf[ssl_obj->len-1] = 0;
+ start = (char *)ssl_obj->buf;
+ return new_pem_obj(ssl_ctx, obj_type == SSL_OBJ_X509_CACERT,
+ start, ssl_obj->len, password);
+}
+#endif /* CONFIG_SSL_HAS_PEM */
+
+/**
+ * Load the key/certificates in memory depending on compile-time and user
+ * options.
+ */
+int load_key_certs(SSL_CTX *ssl_ctx)
+{
+ int ret = SSL_OK;
+ uint32_t options = ssl_ctx->options;
+#ifdef CONFIG_SSL_GENERATE_X509_CERT
+ uint8_t *cert_data = NULL;
+ int cert_size;
+ static const char *dn[] =
+ {
+ CONFIG_SSL_X509_COMMON_NAME,
+ CONFIG_SSL_X509_ORGANIZATION_NAME,
+ CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME
+ };
+#endif
+
+ /* do the private key first */
+ if (strlen(CONFIG_SSL_PRIVATE_KEY_LOCATION) > 0)
+ {
+ if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY,
+ CONFIG_SSL_PRIVATE_KEY_LOCATION,
+ CONFIG_SSL_PRIVATE_KEY_PASSWORD)) < 0)
+ goto error;
+ }
+ else if (!(options & SSL_NO_DEFAULT_KEY))
+ {
+#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE)
+ static const /* saves a few more bytes */
+#include "private_key.h"
+
+ ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key,
+ default_private_key_len, NULL);
+#endif
+ }
+
+ /* now load the certificate */
+#ifdef CONFIG_SSL_GENERATE_X509_CERT
+ if ((cert_size = ssl_x509_create(ssl_ctx, 0, dn, &cert_data)) < 0)
+ {
+ ret = cert_size;
+ goto error;
+ }
+
+ ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, cert_data, cert_size, NULL);
+ free(cert_data);
+#else
+ if (strlen(CONFIG_SSL_X509_CERT_LOCATION))
+ {
+ if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT,
+ CONFIG_SSL_X509_CERT_LOCATION, NULL)) < 0)
+ goto error;
+ }
+ else if (!(options & SSL_NO_DEFAULT_KEY))
+ {
+#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE)
+#include "cert.h"
+ ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT,
+ default_certificate, default_certificate_len, NULL);
+#endif
+ }
+#endif
+
+error:
+#ifdef CONFIG_SSL_FULL_MODE
+ if (ret)
+ {
+ printf("Error: Certificate or key not loaded\n");
+ }
+#endif
+
+ return ret;
+
+}
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/openssl.c b/circuitpython/lib/axtls/ssl/openssl.c
new file mode 100644
index 0000000..d0343e5
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/openssl.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2007-2016, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/*
+ * Enable a subset of openssl compatible functions. We don't aim to be 100%
+ * compatible - just to be able to do basic ports etc.
+ *
+ * Only really tested on mini_httpd, so I'm not too sure how extensive this
+ * port is.
+ */
+
+#include "config.h"
+
+#ifdef CONFIG_OPENSSL_COMPATIBLE
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "os_port.h"
+#include "ssl.h"
+
+#define OPENSSL_CTX_ATTR ((OPENSSL_CTX *)ssl_ctx->bonus_attr)
+
+static char *key_password = NULL;
+
+void *SSLv3_server_method(void) { return NULL; }
+void *TLSv1_server_method(void) { return NULL; }
+void *SSLv3_client_method(void) { return NULL; }
+void *TLSv1_client_method(void) { return NULL; }
+
+typedef void * (*ssl_func_type_t)(void);
+typedef void * (*bio_func_type_t)(void);
+
+typedef struct
+{
+ ssl_func_type_t ssl_func_type;
+} OPENSSL_CTX;
+
+SSL_CTX * SSL_CTX_new(ssl_func_type_t meth)
+{
+ SSL_CTX *ssl_ctx = ssl_ctx_new(0, 5);
+ ssl_ctx->bonus_attr = malloc(sizeof(OPENSSL_CTX));
+ OPENSSL_CTX_ATTR->ssl_func_type = meth;
+ return ssl_ctx;
+}
+
+void SSL_CTX_free(SSL_CTX * ssl_ctx)
+{
+ free(ssl_ctx->bonus_attr);
+ ssl_ctx_free(ssl_ctx);
+}
+
+SSL * SSL_new(SSL_CTX *ssl_ctx)
+{
+ SSL *ssl;
+#ifdef CONFIG_SSL_ENABLE_CLIENT
+ ssl_func_type_t ssl_func_type = OPENSSL_CTX_ATTR->ssl_func_type;
+#endif
+
+ ssl = ssl_new(ssl_ctx, -1); /* fd is set later */
+#ifdef CONFIG_SSL_ENABLE_CLIENT
+ if (ssl_func_type == SSLv3_client_method ||
+ ssl_func_type == TLSv1_client_method)
+ {
+ SET_SSL_FLAG(SSL_IS_CLIENT);
+ }
+ else
+#endif
+ {
+ ssl->next_state = HS_CLIENT_HELLO;
+ }
+
+ return ssl;
+}
+
+int SSL_set_fd(SSL *s, int fd)
+{
+ s->client_fd = fd;
+ return 1; /* always succeeds */
+}
+
+int SSL_accept(SSL *ssl)
+{
+ while (ssl_read(ssl, NULL) == SSL_OK)
+ {
+ if (ssl->next_state == HS_CLIENT_HELLO)
+ return 1; /* we're done */
+ }
+
+ return -1;
+}
+
+#ifdef CONFIG_SSL_ENABLE_CLIENT
+int SSL_connect(SSL *ssl)
+{
+ return do_client_connect(ssl) == SSL_OK ? 1 : -1;
+}
+#endif
+
+void SSL_free(SSL *ssl)
+{
+ ssl_free(ssl);
+}
+
+int SSL_read(SSL *ssl, void *buf, int num)
+{
+ uint8_t *read_buf;
+ int ret;
+
+ while ((ret = ssl_read(ssl, &read_buf)) == SSL_OK);
+
+ if (ret > SSL_OK)
+ {
+ memcpy(buf, read_buf, ret > num ? num : ret);
+ }
+
+ return ret;
+}
+
+int SSL_write(SSL *ssl, const void *buf, int num)
+{
+ return ssl_write(ssl, buf, num);
+}
+
+int SSL_CTX_use_certificate_file(SSL_CTX *ssl_ctx, const char *file, int type)
+{
+ return (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, file, NULL) == SSL_OK);
+}
+
+int SSL_CTX_use_PrivateKey_file(SSL_CTX *ssl_ctx, const char *file, int type)
+{
+ return (ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, file, key_password) == SSL_OK);
+}
+
+int SSL_CTX_use_certificate_ASN1(SSL_CTX *ssl_ctx, int len, const uint8_t *d)
+{
+ return (ssl_obj_memory_load(ssl_ctx,
+ SSL_OBJ_X509_CERT, d, len, NULL) == SSL_OK);
+}
+
+int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx,
+ unsigned int sid_ctx_len)
+{
+ return 1;
+}
+
+int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx)
+{
+ return 1;
+}
+
+int SSL_CTX_use_certificate_chain_file(SSL_CTX *ssl_ctx, const char *file)
+{
+ return (ssl_obj_load(ssl_ctx,
+ SSL_OBJ_X509_CERT, file, NULL) == SSL_OK);
+}
+
+int SSL_shutdown(SSL *ssl)
+{
+ return 1;
+}
+
+/*** get/set session ***/
+SSL_SESSION *SSL_get1_session(SSL *ssl)
+{
+ return (SSL_SESSION *)ssl_get_session_id(ssl); /* note: wrong cast */
+}
+
+int SSL_set_session(SSL *ssl, SSL_SESSION *session)
+{
+ memcpy(ssl->session_id, (uint8_t *)session, SSL_SESSION_ID_SIZE);
+ return 1;
+}
+
+void SSL_SESSION_free(SSL_SESSION *session) { }
+/*** end get/set session ***/
+
+long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
+{
+ return 0;
+}
+
+void SSL_CTX_set_verify(SSL_CTX *ctx, int mode,
+ int (*verify_callback)(int, void *)) { }
+
+void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth) { }
+
+int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
+ const char *CApath)
+{
+ return 1;
+}
+
+void *SSL_load_client_CA_file(const char *file)
+{
+ return (void *)file;
+}
+
+void SSL_CTX_set_client_CA_list(SSL_CTX *ssl_ctx, void *file)
+{
+
+ ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, (const char *)file, NULL);
+}
+
+void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, void *cb) { }
+
+void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u)
+{
+ key_password = (char *)u;
+}
+
+int SSL_peek(SSL *ssl, void *buf, int num)
+{
+ memcpy(buf, ssl->bm_data, num);
+ return num;
+}
+
+void SSL_set_bio(SSL *ssl, void *rbio, void *wbio) { }
+
+long SSL_get_verify_result(const SSL *ssl)
+{
+ return ssl_handshake_status(ssl);
+}
+
+int SSL_state(SSL *ssl)
+{
+ return 0x03; // ok state
+}
+
+/** end of could do better list */
+
+void *SSL_get_peer_certificate(const SSL *ssl)
+{
+ return &ssl->ssl_ctx->certs[0];
+}
+
+int SSL_clear(SSL *ssl)
+{
+ return 1;
+}
+
+
+int SSL_CTX_check_private_key(const SSL_CTX *ctx)
+{
+ return 1;
+}
+
+int SSL_CTX_set_cipher_list(SSL *s, const char *str)
+{
+ return 1;
+}
+
+int SSL_get_error(const SSL *ssl, int ret)
+{
+ ssl_display_error(ret);
+ return 0; /* TODO: return proper return code */
+}
+
+void SSL_CTX_set_options(SSL_CTX *ssl_ctx, int option) {}
+int SSL_library_init(void ) { return 1; }
+void SSL_load_error_strings(void ) {}
+void ERR_print_errors_fp(FILE *fp) {}
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+long SSL_CTX_get_timeout(const SSL_CTX *ssl_ctx) {
+ return CONFIG_SSL_EXPIRY_TIME*3600; }
+long SSL_CTX_set_timeout(SSL_CTX *ssl_ctx, long t) {
+ return SSL_CTX_get_timeout(ssl_ctx); }
+#endif
+void BIO_printf(FILE *f, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+}
+
+void* BIO_s_null(void) { return NULL; }
+FILE *BIO_new(bio_func_type_t func)
+{
+ if (func == BIO_s_null)
+ return fopen("/dev/null", "r");
+ else
+ return NULL;
+}
+
+FILE *BIO_new_fp(FILE *stream, int close_flag) { return stream; }
+int BIO_free(FILE *a) { if (a != stdout && a != stderr) fclose(a); return 1; }
+
+
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/os_port.c b/circuitpython/lib/axtls/ssl/os_port.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/os_port.c
diff --git a/circuitpython/lib/axtls/ssl/os_port.h b/circuitpython/lib/axtls/ssl/os_port.h
new file mode 100644
index 0000000..6e8fdd5
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/os_port.h
@@ -0,0 +1,7 @@
+#include "config.h"
+
+#ifdef CONFIG_AXTLS_OS_PORT_H
+#include CONFIG_AXTLS_OS_PORT_H
+#else
+#include "axtls_os_port.h"
+#endif
diff --git a/circuitpython/lib/axtls/ssl/p12.c b/circuitpython/lib/axtls/ssl/p12.c
new file mode 100644
index 0000000..2c16581
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/p12.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2007, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * Process PKCS#8/PKCS#12 keys.
+ *
+ * The decoding of a PKCS#12 key is fairly specific - this code was tested on a
+ * key generated with:
+ *
+ * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem
+ * -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128
+ * -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd
+ *
+ * or with a certificate chain:
+ *
+ * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem
+ * -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe
+ * PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd
+ *
+ * Note that the PBE has to be specified with PBE-SHA1-RC4-128. The
+ * private/public keys/certs have to use RSA encryption. Both the integrity
+ * and privacy passwords are the same.
+ *
+ * The PKCS#8 files were generated with something like:
+ *
+ * PEM format:
+ * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1
+ * PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8
+ *
+ * DER format:
+ * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER
+ * -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "os_port.h"
+#include "ssl.h"
+
+/* all commented out if not used */
+#ifdef CONFIG_SSL_USE_PKCS12
+
+#define BLOCK_SIZE 64
+#define PKCS12_KEY_ID 1
+#define PKCS12_IV_ID 2
+#define PKCS12_MAC_ID 3
+
+static char *make_uni_pass(const char *password, int *uni_pass_len);
+static int p8_decrypt(const char *uni_pass, int uni_pass_len,
+ const uint8_t *salt, int iter,
+ uint8_t *priv_key, int priv_key_len, int id);
+static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key);
+static int get_pbe_params(uint8_t *buf, int *offset,
+ const uint8_t **salt, int *iterations);
+
+/*
+ * Take a raw pkcs8 block and then decrypt it and turn it into a normal key.
+ */
+int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password)
+{
+ uint8_t *buf = ssl_obj->buf;
+ int len, offset = 0;
+ int iterations;
+ int ret = SSL_NOT_OK;
+ uint8_t *version = NULL;
+ const uint8_t *salt;
+ uint8_t *priv_key;
+ int uni_pass_len;
+ char *uni_pass = make_uni_pass(password, &uni_pass_len);
+
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: Invalid p8 ASN.1 file\n");
+#endif
+ goto error;
+ }
+
+ /* unencrypted key? */
+ if (asn1_get_big_int(buf, &offset, &version) > 0 && *version == 0)
+ {
+ ret = p8_add_key(ssl_ctx, buf);
+ goto error;
+ }
+
+ if (get_pbe_params(buf, &offset, &salt, &iterations) < 0)
+ goto error;
+
+ if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
+ goto error;
+
+ priv_key = &buf[offset];
+
+ p8_decrypt(uni_pass, uni_pass_len, salt,
+ iterations, priv_key, len, PKCS12_KEY_ID);
+ ret = p8_add_key(ssl_ctx, priv_key);
+
+error:
+ free(version);
+ free(uni_pass);
+ return ret;
+}
+
+/*
+ * Take the unencrypted pkcs8 and turn it into a private key
+ */
+static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key)
+{
+ uint8_t *buf = priv_key;
+ int len, offset = 0;
+ int ret = SSL_NOT_OK;
+
+ /* Skip the preamble and go straight to the private key.
+ We only support rsaEncryption (1.2.840.113549.1.1.1) */
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 ||
+ asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
+ goto error;
+
+ ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx);
+
+error:
+ return ret;
+}
+
+/*
+ * Create the unicode password
+ */
+static char *make_uni_pass(const char *password, int *uni_pass_len)
+{
+ int pass_len = 0, i;
+ char *uni_pass;
+
+ if (password == NULL)
+ {
+ password = "";
+ }
+
+ uni_pass = (char *)malloc((strlen(password)+1)*2);
+
+ /* modify the password into a unicode version */
+ for (i = 0; i < (int)strlen(password); i++)
+ {
+ uni_pass[pass_len++] = 0;
+ uni_pass[pass_len++] = password[i];
+ }
+
+ uni_pass[pass_len++] = 0; /* null terminate */
+ uni_pass[pass_len++] = 0;
+ *uni_pass_len = pass_len;
+ return uni_pass;
+}
+
+/*
+ * Decrypt a pkcs8 block.
+ */
+static int p8_decrypt(const char *uni_pass, int uni_pass_len,
+ const uint8_t *salt, int iter,
+ uint8_t *priv_key, int priv_key_len, int id)
+{
+ uint8_t p[BLOCK_SIZE*2];
+ uint8_t d[BLOCK_SIZE];
+ uint8_t Ai[SHA1_SIZE];
+ SHA1_CTX sha_ctx;
+ RC4_CTX rc4_ctx;
+ int i;
+
+ for (i = 0; i < BLOCK_SIZE; i++)
+ {
+ p[i] = salt[i % SALT_SIZE];
+ p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len];
+ d[i] = id;
+ }
+
+ /* get the key - no IV since we are using RC4 */
+ SHA1_Init(&sha_ctx);
+ SHA1_Update(&sha_ctx, d, sizeof(d));
+ SHA1_Update(&sha_ctx, p, sizeof(p));
+ SHA1_Final(Ai, &sha_ctx);
+
+ for (i = 1; i < iter; i++)
+ {
+ SHA1_Init(&sha_ctx);
+ SHA1_Update(&sha_ctx, Ai, SHA1_SIZE);
+ SHA1_Final(Ai, &sha_ctx);
+ }
+
+ /* do the decryption */
+ if (id == PKCS12_KEY_ID)
+ {
+ RC4_setup(&rc4_ctx, Ai, 16);
+ RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len);
+ }
+ else /* MAC */
+ memcpy(priv_key, Ai, SHA1_SIZE);
+
+ return 0;
+}
+
+/*
+ * Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s)
+ * and keys.
+ */
+int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password)
+{
+ uint8_t *buf = ssl_obj->buf;
+ int len, iterations, auth_safes_start,
+ auth_safes_end, auth_safes_len, key_offset, offset = 0;
+ int all_certs = 0;
+ uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac;
+ uint8_t key[SHA1_SIZE];
+ uint8_t mac[SHA1_SIZE];
+ const uint8_t *salt;
+ int uni_pass_len, ret = SSL_OK;
+ char *uni_pass = make_uni_pass(password, &uni_pass_len);
+ static const uint8_t pkcs_data[] = /* pkc7 data */
+ { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 };
+ static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */
+ { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 };
+ static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */
+ { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 };
+
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: Invalid p12 ASN.1 file\n");
+#endif
+ goto error;
+ }
+
+ if (asn1_get_big_int(buf, &offset, &version) < 0 || *version != 3)
+ {
+ ret = SSL_ERROR_INVALID_VERSION;
+ goto error;
+ }
+
+ /* remove all the boring pcks7 bits */
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
+ len != sizeof(pkcs_data) ||
+ memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
+ goto error;
+
+ offset += len;
+
+ if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0)
+ goto error;
+
+ /* work out the MAC start/end points (done on AuthSafes) */
+ auth_safes_start = offset;
+ auth_safes_end = offset;
+ if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0)
+ goto error;
+
+ auth_safes_len = auth_safes_end - auth_safes_start;
+ auth_safes = malloc(auth_safes_len);
+
+ memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len);
+
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
+ (len != sizeof(pkcs_encrypted) ||
+ memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted))))
+ goto error;
+
+ offset += len;
+
+ if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
+ len != sizeof(pkcs_data) ||
+ memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
+ goto error;
+
+ offset += len;
+
+ /* work out the salt for the certificate */
+ if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0)
+ goto error;
+
+ /* decrypt the certificate */
+ cert = &buf[offset];
+ if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert,
+ len, PKCS12_KEY_ID)) < 0)
+ goto error;
+
+ offset += len;
+
+ /* load the certificate */
+ key_offset = 0;
+ all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE);
+
+ /* keep going until all certs are loaded */
+ while (key_offset < all_certs)
+ {
+ int cert_offset = key_offset;
+
+ if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 ||
+ asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 ||
+ asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 ||
+ asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 ||
+ asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 ||
+ (len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0)
+ goto error;
+
+ if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0)
+ goto error;
+
+ key_offset = cert_offset;
+ }
+
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
+ len != sizeof(pkcs_data) ||
+ memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
+ goto error;
+
+ offset += len;
+
+ if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
+ (len != sizeof(pkcs8_key_bag)) ||
+ memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag)))
+ goto error;
+
+ offset += len;
+
+ /* work out the salt for the private key */
+ if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ get_pbe_params(buf, &offset, &salt, &iterations) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
+ goto error;
+
+ /* decrypt the private key */
+ cert = &buf[offset];
+ if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert,
+ len, PKCS12_KEY_ID)) < 0)
+ goto error;
+
+ offset += len;
+
+ /* load the private key */
+ if ((ret = p8_add_key(ssl_ctx, cert)) < 0)
+ goto error;
+
+ /* miss out on friendly name, local key id etc */
+ if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0)
+ goto error;
+
+ /* work out the MAC */
+ if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 ||
+ len != SHA1_SIZE)
+ goto error;
+
+ orig_mac = &buf[offset];
+ offset += len;
+
+ /* get the salt */
+ if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8)
+ goto error;
+
+ salt = &buf[offset];
+
+ /* work out what the mac should be */
+ if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations,
+ key, SHA1_SIZE, PKCS12_MAC_ID)) < 0)
+ goto error;
+
+ ssl_hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac);
+
+ if (memcmp(mac, orig_mac, SHA1_SIZE))
+ {
+ ret = SSL_ERROR_INVALID_HMAC;
+ goto error;
+ }
+
+error:
+ free(version);
+ free(uni_pass);
+ free(auth_safes);
+ return ret;
+}
+
+/*
+ * Retrieve the salt/iteration details from a PBE block.
+ */
+static int get_pbe_params(uint8_t *buf, int *offset,
+ const uint8_t **salt, int *iterations)
+{
+ static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4 */
+ { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 };
+
+ int i, len;
+ uint8_t *iter = NULL;
+ int error_code = SSL_ERROR_NOT_SUPPORTED;
+
+ /* Get the PBE type */
+ if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
+ goto error;
+
+ /* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1)
+ which is the only algorithm we support */
+ if (len != sizeof(pbeSH1RC4) ||
+ memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4)))
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n");
+#endif
+ goto error;
+ }
+
+ *offset += len;
+
+ if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 ||
+ (len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 ||
+ len != 8)
+ goto error;
+
+ *salt = &buf[*offset];
+ *offset += len;
+
+ if ((len = asn1_get_big_int(buf, offset, &iter)) < 0)
+ goto error;
+
+ *iterations = 0;
+ for (i = 0; i < len; i++)
+ {
+ (*iterations) <<= 8;
+ (*iterations) += iter[i];
+ }
+
+ free(iter);
+ error_code = SSL_OK; /* got here - we are ok */
+
+error:
+ return error_code;
+}
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/private_key.h b/circuitpython/lib/axtls/ssl/private_key.h
new file mode 100644
index 0000000..f72bee2
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/private_key.h
@@ -0,0 +1,54 @@
+unsigned char default_private_key[] = {
+ 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xbd,
+ 0x0f, 0xd4, 0x42, 0xa8, 0x74, 0x87, 0x54, 0xaa, 0xb9, 0x3a, 0x1f, 0x8b,
+ 0xce, 0xbd, 0xb7, 0x65, 0xfb, 0x40, 0x3d, 0xd0, 0x11, 0x9a, 0x9c, 0xdc,
+ 0x82, 0x7c, 0xea, 0xa8, 0x17, 0xe1, 0x74, 0xf3, 0x05, 0x0e, 0x61, 0xc1,
+ 0xc1, 0x78, 0x8a, 0xb2, 0xba, 0x15, 0x22, 0x5a, 0xff, 0x9b, 0xb8, 0x7a,
+ 0x2e, 0x0f, 0x88, 0xb7, 0x74, 0xde, 0x04, 0x99, 0xa5, 0xa2, 0x99, 0x53,
+ 0x8b, 0xad, 0x78, 0x5a, 0x31, 0xed, 0xbc, 0x01, 0xe7, 0xdf, 0xe9, 0xec,
+ 0x2f, 0xa0, 0x5d, 0x53, 0xf6, 0xe6, 0x8a, 0xa0, 0xc8, 0x6d, 0x41, 0x45,
+ 0x63, 0x23, 0xb3, 0xcf, 0x4e, 0x50, 0x1f, 0x28, 0xdf, 0x36, 0xe2, 0x73,
+ 0xdf, 0xd6, 0xa1, 0xb3, 0x46, 0x4f, 0x6e, 0xbb, 0x0d, 0x9b, 0xef, 0xa8,
+ 0xf9, 0x4c, 0xa5, 0x71, 0xa1, 0x88, 0xdd, 0x07, 0xa9, 0x86, 0x0d, 0x3f,
+ 0xcd, 0x99, 0x23, 0xa2, 0x84, 0x77, 0x0f, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0x02, 0x81, 0x80, 0x26, 0x3f, 0xec, 0x96, 0xab, 0xd4, 0x1f, 0x89, 0x0e,
+ 0x9d, 0x38, 0xd8, 0x27, 0x05, 0xe5, 0xb6, 0x14, 0x08, 0xd7, 0xff, 0x69,
+ 0x78, 0x16, 0x4a, 0xc4, 0x06, 0x16, 0x55, 0xb7, 0x3a, 0x55, 0x9f, 0xbe,
+ 0x86, 0xf8, 0x58, 0xe8, 0xc5, 0x46, 0xa8, 0xf0, 0xed, 0xda, 0xd6, 0xbf,
+ 0x88, 0x55, 0x2d, 0xe6, 0x72, 0x29, 0x2c, 0x64, 0xc9, 0x5d, 0x1d, 0x9b,
+ 0x24, 0x3a, 0x98, 0x40, 0xa1, 0xd2, 0xaf, 0x5c, 0xab, 0x23, 0xe4, 0x33,
+ 0xd0, 0xea, 0x60, 0x52, 0xe7, 0x7a, 0x9e, 0x73, 0x5f, 0x2e, 0x80, 0xd1,
+ 0xdc, 0x6f, 0x47, 0x0f, 0x97, 0x80, 0x36, 0xd2, 0x30, 0x07, 0xdd, 0xd6,
+ 0xd7, 0x15, 0x89, 0x2b, 0x74, 0xd5, 0x7e, 0x8a, 0xbc, 0x63, 0x42, 0x0a,
+ 0xf2, 0x31, 0x29, 0xbf, 0xf9, 0xf9, 0xf0, 0x88, 0x8f, 0x8a, 0xc2, 0x22,
+ 0x6e, 0x15, 0x26, 0xb7, 0x5e, 0x5b, 0x58, 0x44, 0x1c, 0x3b, 0x79, 0x02,
+ 0x41, 0x00, 0xe1, 0xf1, 0xb2, 0xe5, 0xc8, 0x80, 0x93, 0x40, 0x50, 0x74,
+ 0x14, 0xdd, 0xb2, 0xf2, 0x27, 0x5c, 0x0c, 0x3d, 0xc0, 0x5f, 0xee, 0x9c,
+ 0x45, 0x6c, 0x13, 0x00, 0xdf, 0xd0, 0xd9, 0x83, 0xfa, 0x90, 0x2c, 0x84,
+ 0xf2, 0xaa, 0xc2, 0xdd, 0xfb, 0xcf, 0x03, 0x41, 0x88, 0x10, 0xc6, 0xbb,
+ 0x5e, 0xb7, 0xb6, 0x2e, 0xa6, 0x1d, 0xaa, 0xba, 0xfb, 0x4a, 0x72, 0xd8,
+ 0x9a, 0xad, 0x88, 0x0d, 0x6a, 0x15, 0x02, 0x41, 0x00, 0xd6, 0x36, 0x23,
+ 0xf3, 0x5d, 0x77, 0xc8, 0xd3, 0x49, 0xc1, 0x93, 0xfe, 0xca, 0x0d, 0xeb,
+ 0x9b, 0xda, 0xbd, 0x47, 0x28, 0x73, 0x97, 0xa0, 0x50, 0xd7, 0x4c, 0x24,
+ 0xdf, 0x9b, 0x0b, 0x37, 0xae, 0xc3, 0x31, 0xb5, 0x4f, 0x62, 0x08, 0xca,
+ 0xe5, 0xef, 0x97, 0x7b, 0x43, 0xa0, 0xda, 0x2b, 0x1f, 0xbf, 0xa8, 0x08,
+ 0x93, 0xd2, 0x16, 0x1c, 0x89, 0x99, 0xf1, 0xdf, 0x26, 0xd1, 0x42, 0x99,
+ 0x93, 0x02, 0x41, 0x00, 0xb1, 0x41, 0xe4, 0x7e, 0xdf, 0x20, 0xf7, 0xe4,
+ 0xf1, 0xf9, 0x4f, 0xd1, 0x6a, 0x2d, 0x0d, 0xf1, 0xe9, 0xec, 0x9c, 0x3a,
+ 0xe6, 0xc0, 0x94, 0xba, 0x27, 0xe2, 0x7c, 0xb4, 0xa5, 0xa1, 0x23, 0xf6,
+ 0xed, 0xe6, 0x53, 0x56, 0xe2, 0x50, 0x32, 0xd8, 0x02, 0x8e, 0xeb, 0xc7,
+ 0x75, 0x91, 0xd3, 0xca, 0x3e, 0xd4, 0x34, 0x20, 0x7c, 0x2b, 0xfb, 0x2f,
+ 0x3a, 0x10, 0x72, 0xb1, 0x07, 0x56, 0xb6, 0xcd, 0x02, 0x40, 0x1e, 0x3b,
+ 0xf2, 0x03, 0x0d, 0x74, 0x34, 0xb2, 0x2d, 0xbc, 0xd6, 0xc8, 0xa5, 0x78,
+ 0x25, 0x83, 0x0f, 0xf2, 0x9b, 0x32, 0x88, 0x6e, 0x24, 0x40, 0x84, 0xc2,
+ 0xc8, 0x89, 0x8e, 0xf6, 0x9c, 0x5b, 0x5c, 0x4d, 0x8d, 0xcb, 0xb0, 0x88,
+ 0x91, 0x2a, 0xb7, 0x10, 0x68, 0x63, 0x79, 0x36, 0x91, 0xd3, 0x9f, 0x57,
+ 0x76, 0x2e, 0x76, 0xfe, 0x8b, 0xf4, 0x97, 0xf7, 0xdd, 0x89, 0x3b, 0x0b,
+ 0xed, 0x65, 0x02, 0x41, 0x00, 0xb9, 0xaf, 0xbf, 0x09, 0xc9, 0x90, 0x26,
+ 0xf3, 0x72, 0x8b, 0xbf, 0xb3, 0x7c, 0xe7, 0x6f, 0x6f, 0x5b, 0xa3, 0x95,
+ 0xb8, 0x9e, 0x03, 0xb9, 0xcf, 0xa0, 0x53, 0xba, 0x32, 0xc1, 0xd3, 0xad,
+ 0x85, 0xbb, 0x79, 0x48, 0x09, 0xd6, 0x3f, 0x9c, 0xd9, 0x37, 0x91, 0x11,
+ 0x0d, 0x04, 0xd5, 0x3b, 0xca, 0x74, 0x5d, 0x1c, 0x91, 0x8d, 0x3d, 0xf1,
+ 0xf8, 0xf9, 0xbe, 0x35, 0xd7, 0xb2, 0x53, 0x50, 0x1d
+};
+unsigned int default_private_key_len = 609;
diff --git a/circuitpython/lib/axtls/ssl/ssl.h b/circuitpython/lib/axtls/ssl/ssl.h
new file mode 100644
index 0000000..dc47c81
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/ssl.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2007-2016, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * @mainpage axTLS API
+ *
+ * @image html axolotl.jpg
+ *
+ * The axTLS library has features such as:
+ * - The TLSv1 SSL client/server protocol
+ * - No requirement to use any openssl libraries.
+ * - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers.
+ * - RSA encryption/decryption with variable sized keys (up to 4096 bits).
+ * - Certificate chaining and peer authentication.
+ * - Session resumption, session renegotiation.
+ * - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding.
+ * - Highly configurable compile time options.
+ * - Portable across many platforms (written in ANSI C), and has language
+ * bindings in C, C#, VB.NET, Java, Perl and Lua.
+ * - Partial openssl API compatibility (via a wrapper).
+ * - A very small footprint (around 50-60kB for the library in 'server-only'
+ * mode).
+ * - No dependencies on sockets - can use serial connections for example.
+ * - A very simple API - ~ 20 functions/methods.
+ *
+ * A list of these functions/methods are described below.
+ *
+ * @ref c_api
+ *
+ * @ref bigint_api
+ *
+ * @ref csharp_api
+ *
+ * @ref java_api
+ */
+#ifndef HEADER_SSL_H
+#define HEADER_SSL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <time.h>
+
+/* need to predefine before ssl_lib.h gets to it */
+#define SSL_SESSION_ID_SIZE 32
+
+#include "tls1.h"
+
+/* The optional parameters that can be given to the client/server SSL engine */
+#define SSL_CLIENT_AUTHENTICATION 0x00010000
+#define SSL_SERVER_VERIFY_LATER 0x00020000
+#define SSL_NO_DEFAULT_KEY 0x00040000
+#define SSL_DISPLAY_STATES 0x00080000
+#define SSL_DISPLAY_BYTES 0x00100000
+#define SSL_DISPLAY_CERTS 0x00200000
+#define SSL_DISPLAY_RSA 0x00400000
+#define SSL_CONNECT_IN_PARTS 0x00800000
+
+/* errors that can be generated */
+#define SSL_OK 0
+#define SSL_NOT_OK -1
+#define SSL_ERROR_DEAD -2
+#define SSL_CLOSE_NOTIFY -3
+#define SSL_EAGAIN -4
+#define SSL_ERROR_CONN_LOST -256
+#define SSL_ERROR_RECORD_OVERFLOW -257
+#define SSL_ERROR_SOCK_SETUP_FAILURE -258
+#define SSL_ERROR_INVALID_HANDSHAKE -260
+#define SSL_ERROR_INVALID_PROT_MSG -261
+#define SSL_ERROR_INVALID_HMAC -262
+#define SSL_ERROR_INVALID_VERSION -263
+#define SSL_ERROR_UNSUPPORTED_EXTENSION -264
+#define SSL_ERROR_INVALID_SESSION -265
+#define SSL_ERROR_NO_CIPHER -266
+#define SSL_ERROR_INVALID_CERT_HASH_ALG -267
+#define SSL_ERROR_BAD_CERTIFICATE -268
+#define SSL_ERROR_INVALID_KEY -269
+#define SSL_ERROR_FINISHED_INVALID -271
+#define SSL_ERROR_NO_CERT_DEFINED -272
+#define SSL_ERROR_NO_CLIENT_RENOG -273
+#define SSL_ERROR_NOT_SUPPORTED -274
+#define SSL_X509_OFFSET -512
+#define SSL_X509_ERROR(A) (SSL_X509_OFFSET+A)
+
+/* alert types that are recognized */
+#define SSL_ALERT_TYPE_WARNING 1
+#define SLL_ALERT_TYPE_FATAL 2
+
+/* these are all the alerts that are recognized */
+#define SSL_ALERT_CLOSE_NOTIFY 0
+#define SSL_ALERT_UNEXPECTED_MESSAGE 10
+#define SSL_ALERT_BAD_RECORD_MAC 20
+#define SSL_ALERT_RECORD_OVERFLOW 22
+#define SSL_ALERT_HANDSHAKE_FAILURE 40
+#define SSL_ALERT_BAD_CERTIFICATE 42
+#define SSL_ALERT_UNSUPPORTED_CERTIFICATE 43
+#define SSL_ALERT_CERTIFICATE_EXPIRED 45
+#define SSL_ALERT_CERTIFICATE_UNKNOWN 46
+#define SSL_ALERT_ILLEGAL_PARAMETER 47
+#define SSL_ALERT_UNKNOWN_CA 48
+#define SSL_ALERT_DECODE_ERROR 50
+#define SSL_ALERT_DECRYPT_ERROR 51
+#define SSL_ALERT_INVALID_VERSION 70
+#define SSL_ALERT_NO_RENEGOTIATION 100
+#define SSL_ALERT_UNSUPPORTED_EXTENSION 110
+
+/* The ciphers that are supported */
+#define SSL_AES128_SHA 0x2f
+#define SSL_AES256_SHA 0x35
+#define SSL_AES128_SHA256 0x3c
+#define SSL_AES256_SHA256 0x3d
+
+/* build mode ids' */
+#define SSL_BUILD_SKELETON_MODE 0x01
+#define SSL_BUILD_SERVER_ONLY 0x02
+#define SSL_BUILD_ENABLE_VERIFICATION 0x03
+#define SSL_BUILD_ENABLE_CLIENT 0x04
+#define SSL_BUILD_FULL_MODE 0x05
+
+/* offsets to retrieve configuration information */
+#define SSL_BUILD_MODE 0
+#define SSL_MAX_CERT_CFG_OFFSET 1
+#define SSL_MAX_CA_CERT_CFG_OFFSET 2
+#define SSL_HAS_PEM 3
+
+/* default session sizes */
+#define SSL_DEFAULT_SVR_SESS 5
+#define SSL_DEFAULT_CLNT_SESS 1
+
+/* X.509/X.520 distinguished name types */
+#define SSL_X509_CERT_COMMON_NAME 0
+#define SSL_X509_CERT_ORGANIZATION 1
+#define SSL_X509_CERT_ORGANIZATIONAL_NAME 2
+#define SSL_X509_CERT_LOCATION 3
+#define SSL_X509_CERT_COUNTRY 4
+#define SSL_X509_CERT_STATE 5
+#define SSL_X509_CA_CERT_COMMON_NAME 6
+#define SSL_X509_CA_CERT_ORGANIZATION 7
+#define SSL_X509_CA_CERT_ORGANIZATIONAL_NAME 8
+#define SSL_X509_CA_CERT_LOCATION 9
+#define SSL_X509_CA_CERT_COUNTRY 10
+#define SSL_X509_CA_CERT_STATE 11
+
+/* SSL object loader types */
+#define SSL_OBJ_X509_CERT 1
+#define SSL_OBJ_X509_CACERT 2
+#define SSL_OBJ_RSA_KEY 3
+#define SSL_OBJ_PKCS8 4
+#define SSL_OBJ_PKCS12 5
+
+/**
+ * @defgroup c_api Standard C API
+ * @brief The standard interface in C.
+ * @{
+ */
+
+/**
+ * @brief Establish a new client/server context.
+ *
+ * This function is called before any client/server SSL connections are made.
+ *
+ * Each new connection will use the this context's private key and
+ * certificate chain. If a different certificate chain is required, then a
+ * different context needs to be be used.
+ *
+ * There are two threading models supported - a single thread with one
+ * SSL_CTX can support any number of SSL connections - and multiple threads can
+ * support one SSL_CTX object each (the default). But if a single SSL_CTX
+ * object uses many SSL objects in individual threads, then the
+ * CONFIG_SSL_CTX_MUTEXING option needs to be configured.
+ *
+ * @param options [in] Any particular options. At present the options
+ * supported are:
+ * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server
+ * authentication fails. The certificate can be authenticated later with a
+ * call to ssl_verify_cert().
+ * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication
+ * i.e. each handshake will include a "certificate request" message from the
+ * server. Only available if verification has been enabled.
+ * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences
+ * during the handshake.
+ * - SSL_DISPLAY_STATES (full mode build only): Display the state changes
+ * during the handshake.
+ * - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that
+ * are passed during a handshake.
+ * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that
+ * are passed during a handshake.
+ * - SSL_CONNECT_IN_PARTS (client only): To use a non-blocking version of
+ * ssl_client_new().
+ * @param num_sessions [in] The number of sessions to be used for session
+ * caching. If this value is 0, then there is no session caching. This option
+ * is not used in skeleton mode.
+ * @return A client/server context.
+ */
+EXP_FUNC SSL_CTX * STDCALL ssl_ctx_new(uint32_t options, int num_sessions);
+
+/**
+ * @brief Remove a client/server context.
+ *
+ * Frees any used resources used by this context. Each connection will be
+ * sent a "Close Notify" alert (if possible).
+ * @param ssl_ctx [in] The client/server context.
+ */
+EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx);
+
+/**
+ * @brief Allocates new SSL extensions structure and returns pointer to it
+ *
+ * @return ssl_ext Pointer to SSL_EXTENSIONS structure
+ *
+ */
+EXP_FUNC SSL_EXTENSIONS * STDCALL ssl_ext_new(void);
+
+/**
+ * @brief Frees SSL extensions structure
+ *
+ * @param ssl_ext [in] Pointer to SSL_EXTENSION structure
+ *
+ */
+EXP_FUNC void STDCALL ssl_ext_free(SSL_EXTENSIONS *ssl_ext);
+
+/**
+ * @brief (server only) Establish a new SSL connection to an SSL client.
+ *
+ * It is up to the application to establish the logical connection (whether it
+ * is a socket, serial connection etc).
+ * @param ssl_ctx [in] The server context.
+ * @param client_fd [in] The client's file descriptor.
+ * @return An SSL object reference.
+ */
+EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, long client_fd);
+
+/**
+ * @brief (client only) Establish a new SSL connection to an SSL server.
+ *
+ * It is up to the application to establish the initial logical connection
+ * (whether it is a socket, serial connection etc).
+ *
+ * This is a normally a blocking call - it will finish when the handshake is
+ * complete (or has failed). To use in non-blocking mode, set
+ * SSL_CONNECT_IN_PARTS in ssl_ctx_new().
+ * @param ssl_ctx [in] The client context.
+ * @param client_fd [in] The client's file descriptor.
+ * @param session_id [in] A 32 byte session id for session resumption. This
+ * can be null if no session resumption is being used or required. This option
+ * is not used in skeleton mode.
+ * @param sess_id_size The size of the session id (max 32)
+ * @param ssl_ext pointer to a structure with the activated SSL extensions
+ * and their values
+ * @return An SSL object reference. Use ssl_handshake_status() to check
+ * if a handshake succeeded.
+ */
+EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, long client_fd, const uint8_t *session_id, uint8_t sess_id_size, SSL_EXTENSIONS* ssl_ext);
+
+/**
+ * @brief Free any used resources on this connection.
+
+ * A "Close Notify" message is sent on this connection (if possible). It is up
+ * to the application to close the socket or file descriptor.
+ * @param ssl [in] The ssl object reference.
+ */
+EXP_FUNC void STDCALL ssl_free(SSL *ssl);
+
+/**
+ * @brief Read the SSL data stream.
+ * If the socket is non-blocking and data is blocked then SSO_OK will be
+ * returned.
+ * @param ssl [in] An SSL object reference.
+ * @param in_data [out] If the read was successful, a pointer to the read
+ * buffer will be here. Do NOT ever free this memory as this buffer is used in
+ * sucessive calls. If the call was unsuccessful, this value will be null.
+ * @return The number of decrypted bytes:
+ * - if > 0, then the handshaking is complete and we are returning the number
+ * of decrypted bytes.
+ * - SSL_OK if the handshaking stage is successful (but not yet complete).
+ * - < 0 if an error.
+ * @see ssl.h for the error code list.
+ * @note Use in_data before doing any successive ssl calls.
+ */
+EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data);
+
+/**
+ * @brief Write to the SSL data stream.
+ * if the socket is non-blocking and data is blocked then a check is made
+ * to ensure that all data is sent (i.e. blocked mode is forced).
+ * @param ssl [in] An SSL obect reference.
+ * @param out_data [in] The data to be written
+ * @param out_len [in] The number of bytes to be written.
+ * @return The number of bytes sent, or if < 0 if an error.
+ * @see ssl.h for the error code list.
+ */
+EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len);
+
+/**
+ * @brief Find an ssl object based on a file descriptor.
+ *
+ * Goes through the list of SSL objects maintained in a client/server context
+ * to look for a file descriptor match.
+ * @param ssl_ctx [in] The client/server context.
+ * @param client_fd [in] The file descriptor.
+ * @return A reference to the SSL object. Returns null if the object could not
+ * be found.
+ */
+EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, long client_fd);
+
+/**
+ * @brief Get the session id for a handshake.
+ *
+ * This will be a 32 byte sequence and is available after the first
+ * handshaking messages are sent.
+ * @param ssl [in] An SSL object reference.
+ * @return The session id as a 32 byte sequence.
+ * @note A SSLv23 handshake may have only 16 valid bytes.
+ */
+EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl);
+
+/**
+ * @brief Get the session id size for a handshake.
+ *
+ * This will normally be 32 but could be 0 (no session id) or something else.
+ * @param ssl [in] An SSL object reference.
+ * @return The size of the session id.
+ */
+EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl);
+
+/**
+ * @brief Return the cipher id (in the SSL form).
+ * @param ssl [in] An SSL object reference.
+ * @return The cipher id. This will be one of the following:
+ * - SSL_AES128_SHA (0x2f)
+ * - SSL_AES256_SHA (0x35)
+ * - SSL_AES128_SHA256 (0x3c)
+ * - SSL_AES256_SHA256 (0x3d)
+ */
+EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl);
+
+/**
+ * @brief Return the status of the handshake.
+ * @param ssl [in] An SSL object reference.
+ * @return SSL_OK if the handshake is complete and ok.
+ * @see ssl.h for the error code list.
+ */
+EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl);
+
+/**
+ * @brief Retrieve various parameters about the axTLS engine.
+ * @param offset [in] The configuration offset. It will be one of the following:
+ * - SSL_BUILD_MODE The build mode. This will be one of the following:
+ * - SSL_BUILD_SERVER_ONLY (basic server mode)
+ * - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication)
+ * - SSL_BUILD_ENABLE_CLIENT (client/server capabilties)
+ * - SSL_BUILD_FULL_MODE (client/server with diagnostics)
+ * - SSL_BUILD_SKELETON_MODE (skeleton mode)
+ * - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed.
+ * - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed.
+ * - SSL_HAS_PEM 1 if supported
+ * @return The value of the requested parameter.
+ */
+EXP_FUNC int STDCALL ssl_get_config(int offset);
+
+/**
+ * @brief Display why the handshake failed.
+ *
+ * This call is only useful in a 'full mode' build. The output is to stdout.
+ * @param error_code [in] An error code.
+ * @see ssl.h for the error code list.
+ */
+EXP_FUNC void STDCALL ssl_display_error(int error_code);
+
+/**
+ * @brief Authenticate a received certificate.
+ *
+ * This call is usually made by a client after a handshake is complete and the
+ * context is in SSL_SERVER_VERIFY_LATER mode.
+ * @param ssl [in] An SSL object reference.
+ * @return SSL_OK if the certificate is verified.
+ */
+EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl);
+
+/**
+ * @brief Retrieve an X.509 distinguished name component.
+ *
+ * When a handshake is complete and a certificate has been exchanged, then the
+ * details of the remote certificate can be retrieved.
+ *
+ * This will usually be used by a client to check that the server's common
+ * name matches the URL.
+ *
+ * @param ssl [in] An SSL object reference.
+ * @param component [in] one of:
+ * - SSL_X509_CERT_COMMON_NAME
+ * - SSL_X509_CERT_ORGANIZATION
+ * - SSL_X509_CERT_ORGANIZATIONAL_NAME
+ * - SSL_X509_CERT_LOCATION
+ * - SSL_X509_CERT_COUNTRY
+ * - SSL_X509_CERT_STATE
+ * - SSL_X509_CA_CERT_COMMON_NAME
+ * - SSL_X509_CA_CERT_ORGANIZATION
+ * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME
+ * - SSL_X509_CA_CERT_LOCATION
+ * - SSL_X509_CA_CERT_COUNTRY
+ * - SSL_X509_CA_CERT_STATE
+ * @return The appropriate string (or null if not defined)
+ * @note Verification build mode must be enabled.
+ */
+EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component);
+
+/**
+ * @brief Retrieve a Subject Alternative DNSName
+ *
+ * When a handshake is complete and a certificate has been exchanged, then the
+ * details of the remote certificate can be retrieved.
+ *
+ * This will usually be used by a client to check that the server's DNS
+ * name matches the URL.
+ *
+ * @param ssl [in] An SSL object reference.
+ * @param dnsindex [in] The index of the DNS name to retrieve.
+ * @return The appropriate string (or null if not defined)
+ * @note Verification build mode must be enabled.
+ */
+EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int dnsindex);
+
+/**
+ * @brief Force the client to perform its handshake again.
+ *
+ * For a client this involves sending another "client hello" message.
+ * For the server is means sending a "hello request" message.
+ *
+ * This is a blocking call on the client (until the handshake completes).
+ *
+ * @param ssl [in] An SSL object reference.
+ * @return SSL_OK if renegotiation instantiation was ok
+ */
+EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl);
+
+/**
+ * @brief Process a file that is in binary DER or ASCII PEM format.
+ *
+ * These are temporary objects that are used to load private keys,
+ * certificates etc into memory.
+ * @param ssl_ctx [in] The client/server context.
+ * @param obj_type [in] The format of the file. Can be one of:
+ * - SSL_OBJ_X509_CERT (no password required)
+ * - SSL_OBJ_X509_CACERT (no password required)
+ * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported)
+ * - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported)
+ * - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported)
+ *
+ * PEM files are automatically detected (if supported). The object type is
+ * also detected, and so is not relevant for these types of files.
+ * @param filename [in] The location of a file in DER/PEM format.
+ * @param password [in] The password used. Can be null if not required.
+ * @return SSL_OK if all ok
+ * @note Not available in skeleton build mode.
+ */
+EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *filename, const char *password);
+
+/**
+ * @brief Process binary data.
+ *
+ * These are temporary objects that are used to load private keys,
+ * certificates etc into memory.
+ * @param ssl_ctx [in] The client/server context.
+ * @param obj_type [in] The format of the memory data.
+ * @param data [in] The binary data to be loaded.
+ * @param len [in] The amount of data to be loaded.
+ * @param password [in] The password used. Can be null if not required.
+ * @return SSL_OK if all ok
+ * @see ssl_obj_load for more details on obj_type.
+ */
+EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password);
+
+#ifdef CONFIG_SSL_GENERATE_X509_CERT
+/**
+ * @brief Create an X.509 certificate.
+ *
+ * This certificate is a self-signed v1 cert with a fixed start/stop validity
+ * times. It is signed with an internal private key in ssl_ctx.
+ *
+ * @param ssl_ctx [in] The client/server context.
+ * @param options [in] Not used yet.
+ * @param dn [in] An array of distinguished name strings. The array is defined
+ * by:
+ * - SSL_X509_CERT_COMMON_NAME (0)
+ * - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the
+ * hostname will be used.
+ * - SSL_X509_CERT_ORGANIZATION (1)
+ * - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME
+ * will be used.
+ * - SSL_X509_CERT_ORGANIZATIONAL_NAME (2)
+ * - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional.
+ * @param cert_data [out] The certificate as a sequence of bytes.
+ * @return < 0 if an error, or the size of the certificate in bytes.
+ * @note cert_data must be freed when there is no more need for it.
+ */
+EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data);
+#endif
+
+/**
+ * @brief Return the axTLS library version as a string.
+ */
+EXP_FUNC const char * STDCALL ssl_version(void);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/test/Makefile b/circuitpython/lib/axtls/ssl/test/Makefile
new file mode 100644
index 0000000..b1d7538
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/Makefile
@@ -0,0 +1,100 @@
+#
+# Copyright (c) 2007, Cameron Rich
+#
+# 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 axTLS project 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 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.
+#
+
+all:
+
+AXTLS_HOME=../..
+
+include $(AXTLS_HOME)/config/.config
+include $(AXTLS_HOME)/config/makefile.conf
+
+ifdef CONFIG_PERFORMANCE_TESTING
+all: performance
+endif
+
+ifdef CONFIG_SSL_TEST
+all: ssltesting
+endif
+
+include $(AXTLS_HOME)/config/makefile.post
+
+ifndef CONFIG_PLATFORM_WIN32
+performance: $(AXTLS_HOME)/$(STAGE)/perf_bigint
+ssltesting: $(AXTLS_HOME)/$(STAGE)/ssltest
+LIBS=$(AXTLS_HOME)/$(STAGE)
+
+$(AXTLS_HOME)/$(STAGE)/perf_bigint: perf_bigint.o $(LIBS)/libaxtls.a
+ $(LD) $(LDFLAGS) -o $@ $^ -L $(LIBS) -laxtls
+
+$(AXTLS_HOME)/$(STAGE)/ssltest: ssltest.o $(LIBS)/libaxtls.a
+ $(LD) $(LDFLAGS) -o $@ $^ -lpthread -L $(LIBS) -laxtls
+else
+performance: $(AXTLS_HOME)/$(STAGE)/perf_bigint.exe
+ssltesting: $(AXTLS_HOME)/$(STAGE)/ssltest.exe
+
+CRYPTO_PATH="$(AXTLS_INCLUDE)crypto\\"
+AXTLS_SSL_PATH="$(AXTLS_INCLUDE)ssl\\"
+
+CRYPTO_OBJ=\
+ $(CRYPTO_PATH)aes.obj \
+ $(CRYPTO_PATH)bigint.obj \
+ $(CRYPTO_PATH)crypto_misc.obj \
+ $(CRYPTO_PATH)hmac.obj \
+ $(CRYPTO_PATH)md2.obj \
+ $(CRYPTO_PATH)md5.obj \
+ $(CRYPTO_PATH)rc4.obj \
+ $(CRYPTO_PATH)rsa.obj \
+ $(CRYPTO_PATH)sha1.obj \
+ $(CRYPTO_PATH)sha256.obj \
+ $(CRYPTO_PATH)sha384.obj \
+ $(CRYPTO_PATH)sha512.obj
+
+OBJ=\
+ $(AXTLS_SSL_PATH)asn1.obj \
+ $(AXTLS_SSL_PATH)gen_cert.obj \
+ $(AXTLS_SSL_PATH)loader.obj \
+ $(AXTLS_SSL_PATH)openssl.obj \
+ $(AXTLS_SSL_PATH)os_port.obj \
+ $(AXTLS_SSL_PATH)p12.obj \
+ $(AXTLS_SSL_PATH)x509.obj \
+ $(AXTLS_SSL_PATH)tls1.obj \
+ $(AXTLS_SSL_PATH)tls1_svr.obj \
+ $(AXTLS_SSL_PATH)tls1_clnt.obj
+
+$(AXTLS_HOME)/$(STAGE)/perf_bigint.exe: perf_bigint.obj
+ $(LD) $(LDFLAGS) /out:$@ $? $(CRYPTO_OBJ) $(OBJ)
+
+$(AXTLS_HOME)/$(STAGE)/ssltest.exe: ssltest.obj
+ $(LD) $(LDFLAGS) /out:$@ $? $(CRYPTO_OBJ) $(OBJ)
+endif
+
+clean::
+ -@rm -f $(AXTLS_HOME)/$(STAGE)/perf_bigint* $(AXTLS_HOME)/$(STAGE)/ssltest*
+
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.ca_key.pem b/circuitpython/lib/axtls/ssl/test/axTLS.ca_key.pem
new file mode 100644
index 0000000..0c8c19b
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.ca_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAsSMwkpYHlwIsi1oxllJLjAPjjycx9t6Q3BOzXBSoZHc1wA+r
+QVuKHarpe8tYOKlNP8b79ejbCv/1U3NlVkMJJtLZUPtDwTEwUZNEG17ixS438mGI
+1AbHXvwhPaoT24UymEOcbJxAZuClDfzKpN7Cl6OzN7Ox44gHmBCBSnUhRAnJYxD6
+x8wBLw8HdH0bMdoc2+H4KyW31uMGva7C9YlUCPVQGPJsTP1DLRskqy+zO1NERUTO
+XytHeYLlh/X/o+NYVvUf7WKu6qC3I4HK+OZ5OW3E+EaE73oYRmUVDaVavL7bRKEf
+2NSbzowU+VsFEnyXIF7YnRFp0D1yrMzaC49bDQIDAQABAoIBAQCwOrNLUunwKaCJ
+b00gIXW5sfDGbhc+ZUU3Pn5V4NN7SEJ4dt5JYrnxNCWgHLkDfiQ1jFEF4QlzUx0O
+TiMGhCDpuCGueJx66uYIcnvywx7XT1kn0jNfxfK6JBsqDzg8ULL6W2GXiIhmEZ8E
+YHh3OIvec2WMyED1flMXzWvj2M4ksfMgZH290T9BSxzlEj9X7dZu55K4sFtu0XNi
+7uJDB0vF8KfW5gLpwj6pd7i32Cm/Pgpl+7lcqMNpDHo0U2dMyWeH1EmWinS4rHxd
+mGDm0N0qK4BOYAJdasq7HwRjzDZkAPuNqcH0gOzSq3aYCI2tP2E6IvprM/vbqA4I
+ooo+GDJhAoGBAORiphxweuGBRTpSqoi9qEICM2WMC2NZW8bpFyWn79CuALGzvvc+
+Buw8W3LVElByUyTVWBnBQQ1iXZGe9f1x2P9q8R1Bdl0rpePg3J9ff9674VRLrcov
+M85/NUHkj1fJ+2CkCx7KG5BFm3GaAb74e+7MceEW/eErMmCqtNgkR2Z5AoGBAMaO
+O9SnVNd4wgnJAjVgN4fqaXWNzZz6NYWtnFVIcTTO7vU1P9t5NjgcsdF9+SnLA/jV
+Ylo5Pl0fLPKg5QtIvlooZjXB8hgKVjrVGRLFt9TN0tJMeJ//11Bo88MNMOf0OQPg
+i2OMWI3w/oRFJthmpeYWOgXNlLOcoSW1LsnWR0Q1AoGBALAXi+KTq3tiM+FzSb/j
+E+/JSJ28bC9u/7+Pi2RiZxrsfuaFI/H4ZlgRdaVFujhC3e6hfKtnAWRzepfEDAEd
+neXaLAyVo9DUzbS1dQaBGNPA400eiOJCoNxP4t1qgEd9GhB6i4Ry6uvDb8YYq832
+Q4BtLEUUeC38I3y7QnMBDfhpAoGAZ13InA54xqvhKELy2WK7xhAs0rv93MkNcAhP
+qL5L4RgRoqoUEmfp6BBYKh2Qx0cfTD2aNCo04znFppJIazV1k24Qt8+9/vHyrjIe
+GX3BFBIKvNx+t5zzNLNOo66MVVT5EaGmLy7zMwHRHn75mBLoLv5HOpop3c+evQiz
+0POyqjkCgYA2+ql0jS7Qbk7EPyFXftQhHcD/Ld8TKjonShkASoudSEHKwuQGBuit
+ftPsg/E5YfhlRpQB6p00kXb4vZsdpFV38eXkyLuLwg0kbpaLedahIujIENNvWP1w
+89T/ueiafWPqvwu3M7sAkkd/ReAQ0LcPwrsBHlOk0uujq+WV+b+Xfw==
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.cer b/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.cer
new file mode 100644
index 0000000..7bde7b0
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.pem b/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.pem
new file mode 100644
index 0000000..0dfd1f4
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDjCCAfagAwIBAgIJAORglrABpAToMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNV
+BAoTKWF4VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE2MTIzMDIxMDQyN1oXDTMwMDkwODIxMDQyN1owNDEyMDAGA1UEChMpYXhUTFMg
+UHJvamVjdCBEb2RneSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCxIzCSlgeXAiyLWjGWUkuMA+OPJzH23pDcE7Nc
+FKhkdzXAD6tBW4odqul7y1g4qU0/xvv16NsK//VTc2VWQwkm0tlQ+0PBMTBRk0Qb
+XuLFLjfyYYjUBsde/CE9qhPbhTKYQ5xsnEBm4KUN/Mqk3sKXo7M3s7HjiAeYEIFK
+dSFECcljEPrHzAEvDwd0fRsx2hzb4fgrJbfW4wa9rsL1iVQI9VAY8mxM/UMtGySr
+L7M7U0RFRM5fK0d5guWH9f+j41hW9R/tYq7qoLcjgcr45nk5bcT4RoTvehhGZRUN
+pVq8vttEoR/Y1JvOjBT5WwUSfJcgXtidEWnQPXKszNoLj1sNAgMBAAGjIzAhMA8G
+A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IB
+AQByY8+gtxSKO5ASYKArY8Sb4xJw0MNwsosARm17sT/8UTerS9CssUQXkW4LaU3T
+SzFVxRUz9O+74yu87W58IaxGNq80quFkOfpS39eCtbqD2e4Y/JuC30hK/GmhBquD
+gVh3z3hD8+DUXq83T1+9zgjCppLeQfaFejbaJSwQ7+K6gJn5JZNiaHVGN1GO6lT6
+5W3zqep5zU6a4T5UeZqkYyuzfTBbIkgEga0JzPaCdd5JOrwFR3UNWlICOxr7htDI
+FqiTrA0RH2ZNmrmJDcM1pFWlQAPnvDoRLeMpmvWZDnGrWYDzyt0j5G0bIPL7214c
+b5kxOvfGlM4E6v+lMSw/HDrI
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509_sha256.pem b/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509_sha256.pem
new file mode 100644
index 0000000..0771ca6
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.ca_x509_sha256.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDjCCAfagAwIBAgIJAJRCXZlKhZeHMA0GCSqGSIb3DQEBCwUAMDQxMjAwBgNV
+BAoTKWF4VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE2MTIzMDIxMDQyN1oXDTMwMDkwODIxMDQyN1owNDEyMDAGA1UEChMpYXhUTFMg
+UHJvamVjdCBEb2RneSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCxIzCSlgeXAiyLWjGWUkuMA+OPJzH23pDcE7Nc
+FKhkdzXAD6tBW4odqul7y1g4qU0/xvv16NsK//VTc2VWQwkm0tlQ+0PBMTBRk0Qb
+XuLFLjfyYYjUBsde/CE9qhPbhTKYQ5xsnEBm4KUN/Mqk3sKXo7M3s7HjiAeYEIFK
+dSFECcljEPrHzAEvDwd0fRsx2hzb4fgrJbfW4wa9rsL1iVQI9VAY8mxM/UMtGySr
+L7M7U0RFRM5fK0d5guWH9f+j41hW9R/tYq7qoLcjgcr45nk5bcT4RoTvehhGZRUN
+pVq8vttEoR/Y1JvOjBT5WwUSfJcgXtidEWnQPXKszNoLj1sNAgMBAAGjIzAhMA8G
+A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
+AQAm3qU+APMkgTRCWhw0m4+06gc2iGaf7l+3i/Kg6EvDB9js4cI1sqzRpSdqdjTZ
+BwVlIURlB6BnEMCB3hP69IF/oSnqrnIxx2KIeVJt4CCwW1mXKsBwxNMXwHYD3A02
+08eFTRkP0ofn+YBRfUYFkDFebqba4V3NBZtBW497YtQk/ssKAL1xaI0V26M4E/VH
+jkJtk99mcMjfuD8HPoqjTeVBGtY2h0A3j2LQPmOJNdSxv30SJuVjvpG7vejQripq
+9MW0kPMBIRKhBpUBLIthLLxm/2VBe0IHU34kpe60YpQdJqebbldtOG1Uxe4F2JpN
+LfUXs2/UCNpEHS60Xl8UDJXA
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.encrypted.p8 b/circuitpython/lib/axtls/ssl/test/axTLS.encrypted.p8
new file mode 100644
index 0000000..07feaac
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.encrypted.p8
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.encrypted_pem.p8 b/circuitpython/lib/axtls/ssl/test/axTLS.encrypted_pem.p8
new file mode 100644
index 0000000..bc28403
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.encrypted_pem.p8
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICnTAcBgoqhkiG9w0BDAEBMA4ECLZOmsNEeDZnAgIIAASCAnu22JX3BRRr9cTr
+j4x62+iDbrqcwmhQkybM1xShFgWObY3/OQf1NJfH4DO4zGQN45ZN0X9zSUVRu8Dd
+ycHls5qsROJ96PYQ8d1xnuh1C9TWmFiLieM0qUyn35raHcKPQiBl8PYHtNr3+m0K
+bsql6T23hE5ZjVhbRtcRdRd9MsQeTZ6n4mdWPEhzN1+rmSPV8ybCbrtJl3/Dh1v8
+LnswXB92T6WcmHRyRiNsbAXR9SQkOaXFZ/1RUXDNqoRYZjlWPBWdcQf48mzgaDjq
+3RXF1mjbOlX2x4/V4qVwH1qZADlT38fSRk/iVdRfureS+wfOuJgJ/g9Riwa0o41K
+2q+sOEcTOv4rShXRGp/0ckeuwhARDdSMymlSBhgBWdf14qdgxCx0rAXYlRAc/4Ze
+Zl9ErfydwdUfDJoPK+QlGZMF//hFRF+vdO/wJHleLsels4KaqN9v/sB+BrGWaeUC
+ScKMISixquCwfWi2qGiqLQGsxHn0d6ejiMblZR4kTf7dcuWVWGXdf5VZqxLNP8Xg
+zzjjeP59OxHRVH0ytGBUejTehcxWDKq+6ESAuujzVXa0v1+5QzZXsJ8rPyZOINzj
+bdpN4Cr3tZj9ALrNzoC2oG62+OO45U7lg54cV1nyrQ2QiI4XFnt0WA06K1G1JrRW
+a/y6nZkntGybbCpuuIBiWl2FfdcTepctiXcJ0vCYqVNOTT4L2Y1RwajDzALhgb7e
+wOy0eA8gmOmMLNozSlMV7siL0LeE1WlotLwK+AHMU1lmPMqS7jK9nycXtZyg4IKl
+eBAnW052fnKZhT4zNlfJkZqpcp6YWpCU16PWx6L+/FDzFPzswluHyfB72oVqRniI
+2A==
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_1024 b/circuitpython/lib/axtls/ssl/test/axTLS.key_1024
new file mode 100644
index 0000000..e39791e
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_1024
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_1024.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_1024.pem
new file mode 100644
index 0000000..fd8226e
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_1024.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC9D9RCqHSHVKq5Oh+Lzr23ZftAPdARmpzcgnzqqBfhdPMFDmHB
+wXiKsroVIlr/m7h6Lg+It3TeBJmloplTi614WjHtvAHn3+nsL6BdU/bmiqDIbUFF
+YyOzz05QHyjfNuJz39ahs0ZPbrsNm++o+UylcaGI3Qephg0/zZkjooR3DwIDAQAB
+AoGAJj/slqvUH4kOnTjYJwXlthQI1/9peBZKxAYWVbc6VZ++hvhY6MVGqPDt2ta/
+iFUt5nIpLGTJXR2bJDqYQKHSr1yrI+Qz0OpgUud6nnNfLoDR3G9HD5eANtIwB93W
+1xWJK3TVfoq8Y0IK8jEpv/n58IiPisIibhUmt15bWEQcO3kCQQDh8bLlyICTQFB0
+FN2y8idcDD3AX+6cRWwTAN/Q2YP6kCyE8qrC3fvPA0GIEMa7Xre2LqYdqrr7SnLY
+mq2IDWoVAkEA1jYj8113yNNJwZP+yg3rm9q9Ryhzl6BQ10wk35sLN67DMbVPYgjK
+5e+Xe0Og2isfv6gIk9IWHImZ8d8m0UKZkwJBALFB5H7fIPfk8flP0WotDfHp7Jw6
+5sCUuififLSloSP27eZTVuJQMtgCjuvHdZHTyj7UNCB8K/svOhBysQdWts0CQB47
+8gMNdDSyLbzWyKV4JYMP8psyiG4kQITCyImO9pxbXE2Ny7CIkSq3EGhjeTaR059X
+di52/ov0l/fdiTsL7WUCQQC5r78JyZAm83KLv7N8529vW6OVuJ4Duc+gU7oywdOt
+hbt5SAnWP5zZN5ERDQTVO8p0XRyRjT3x+Pm+NdeyU1Ad
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_2048 b/circuitpython/lib/axtls/ssl/test/axTLS.key_2048
new file mode 100644
index 0000000..0098987
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_2048
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_2048.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_2048.pem
new file mode 100644
index 0000000..5ef719d
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA+Vp2PKclcZoKZ3OicQZNA4XpGZbD96Gasb5E66voMPub7l+y
+ln6A9CYveBna0i/cQHRCwpYuzzbHQgOjAAw7rcveiIqm8nJMxA8BFNMJCHv0knDl
+RT3H28xoTt08Z/MCfCYyOXoVsJOS6BSOSdFVyzhfU6nAie5iskYnmKecmhYaFm94
+S8WWJMNgEvwzPyhdmE4rpqUBHRSu3zPgHUnRRIOfui5KdZGzKpaC+i1Gn58MOC4W
+L8kguEMDCEkQD6zxFq0PX5GBgu+zVqCFByX7soxZB027FsmJUN6WNoo3hR/H1QV8
+RdXvgMbT00yymEnYVfn9ZL7nXShKI70Pi5Y11QIDAQABAoIBAQDQQBrrgPUmsW3r
+BIowNwDu5lHNizrTf+ZAeBX7dbEP57NNHCN8yN5OCe4vMfis/kfGlNKEzQT/DlLP
+8VWa3pyhA9kw1AumBIvUWmuexrmOmmeiPiNc9sIJ8edTpjWi4zO6F/RuSGYA+N8C
+cNh9EhXDCaujpewlxjArj6fWOHXzwMewghiGCm/fW0rWri5HCQ7ec+WuHPC2KQGQ
+mRDpkXUsxWzTIMIsMKe3M2jYD+pk4xkJqN1+OV/APmQfLyshkGD4EN/qG1dieMQ0
+e85HxxCxbFETOzip0+pHXMbtxY/ok25/SCdtBXkBUJK1KqAysEZdHtOWmmk4HDaz
+Dx2PBbKpAoGBAP+BT/WXeVgtOv1idyv9RG34kOJCDaROtVk3g5MNLfj9mIklcwEd
+SylhpFxvvrFQAWQIDFPzFkukMs8aIuC0Gm4zxH+8RF45lwhgcXlXP87IiIyW6Bqu
+aglmWAM9UHol3/b2AD1UdpqGe0nvWo0rIqs4pdtGTA5BSSUYeaZXhzFDAoGBAPnW
+GWqDarzSbZM4TWcrx6O+cjLnstxDW+cgSCIrm9iVq2ys+Hn5O03aN9SsklyGxXgo
+ygdGvbYnnPl3ugT139KEiIeUSCBU15WP/NZejpHmtUHduDw1Y6SlBJ9T+p5CSDE+
+2kJoZu1zG7W3eBprZsyqNWtw3Dwc2N+emrF+NF8HAoGBANzErAVFq6if9E+KK/SL
+cvwegXmun0DwbUu4ZuzBv45b+NfPzu4QlKgd4TmpqDhnK7x2I8jJyuLy7p/6Mla7
+5/Z+rnO8hcpwsmqfgozY8Z5HsYzgu46KU77penTaHtZcMYefCZf0ikJ8nrzEnxZJ
+RjxxxwWPWRocGQp/emVbTconAoGAUClmFkr8YIGULvyNuWDOubdNpQ+6z/m87zfo
+bS5Y3vGHA2OshlZ1tNEjwNVuUMndamSMDjGghWXIdDL6OMU7f6yOshHd4qHWWmLM
+2WuVizLfTbb6ejcXNajNBuJHM6hIyaRFG7Gr9NxOM8weeTukzF6ArWyU/aSz4Wxe
+bjz0SNkCgYBVC0xdMPauqO1Ie/Gp6SPrIx9+bDDlwEvnDki/2RN7z5IkmBnRFxPy
+6wqBiMFRxJXI4q3SSSbicb+7TQlK2/Vq/bL6QfjjQM8ZEzpPenYpP/wyMQXdVxfh
+1GFDTzeiAhXTvBsZVj8R64VNhNaNn4naMlC4hqxaZ2KA0wrjTRKR0g==
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_4096 b/circuitpython/lib/axtls/ssl/test/axTLS.key_4096
new file mode 100644
index 0000000..df8a8fd
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_4096
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_4096.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_4096.pem
new file mode 100644
index 0000000..73fb75f
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEAwApOo9f0syXI8DmEkP88eHCgZZ8FumcICcqcFe0OywR9UsXU
+dQhMqKfMLi8Wlco+rNqFcDfsNyAllCDiq89dp/aNj3inCVAoXnaJuiZQ6dX9UtBR
+VEW9cr/z6RjgrZQ9+MkbOx+MXVd44I8jEWVuW2oK1eQzU+FUb12dj45mTaLpL6Bx
+XcXGC7e2KSeO75iWwitMyGmJQirV8fttUNa6VfKu7isxESz5jaUmuSsMZIl6Hjm/
+KxmZxkBuFv1Zm2KkKqItV/WqQYCrngil1I5zJLjHWFNjA634U63yWZo5LWQY8pp5
+wy0stC87yJD70rsszjm3zejtXAVipm27+GWe1bKI6UAVfyG3y+bEAKOGc9b6UpUJ
+7iRPd32DrZCJ1xubVW3Lx+gK5YILsf7fQARPAbc1KbuJ2GJ2v9ihINzkqyT6N6FJ
+ns/BcZnHAXk+KorS03xDMgI/CMKEjoc5YjFY9ioOUvvtpeg2kb0VUqvAHOm4i+DU
+f5pQ82uH16tHVk2YyZqNWNVu8cbYsLAKFYPtyur9EO2TeKhCI+vAjl5FNw3TQAjy
+1N4P7olDpJh1Vkx5q2fbaQvIq1HMovSmGKKvObXjjAoqlwVaiCukkr/djV1Teu1J
+T50aP7tqCOOrBYBaWHO1IOB6xFOSfpAkuZVf4gqHsl2jTiLY6zDhkafJgoECAwEA
+AQKCAgAlKVlyZzXY/PTXV6oJjPqcq96+C3nGSm3Jx0VREOCN9L5zqAim5QZAlMf0
+H/SU4+Ag/uBXiNrTCAt9kKeMa8JJ4HIgU06vhK1rKjEYrpV1yo0M23cBgcVZUT/X
+2ZKQxGEBpZj5Ze95mJWxjsFQenpSgkC6h0BPeQkny8vTndC6MU5Cgx+s77qVReWg
+LSGBx9tUk6B2H8YJ4dQo0Wij/gls3FtxhzYlhrh76nuF1Yi+Y8QX2UDfDEMvlAQ5
+uqj+YqY2AdAYd1eM+WM8X5wHd9FcR817kBdW/PFS8BQ3tppd6ELTn4T0eedurr04
+4KV6b/IJri2dUPetmPUwE4gOV0vW5LP1DSNS+sBD1OrsQ+Ytovht868GLMQ/d6M+
+82IDz2at0lDQ+6JLGBrY2rOXYi22fqGgSg2ErDENgYU1dft1Yc8sAzCTl5WC/Bhg
+IxbfUChsYqURvq7faaqVfb50FcMYUcj/rAXVSLOuix2HUgLsQSj2MTtvuR7+BUgh
+2KEmiCaMJy6CQyIgtOTjhmPqMr5Se/JYi1SRG0SxZpsYhGRPfjC8jg1k/VmRoAmS
+pvqfhqgUMB0vAjfynoxFOZGNZIVrSR+yuC3xaORBW5UAPGvZHS1XzuxdybjpPm74
+0NB1JIc3ylAWcJEWdpnpk8OzZCAhifx0Farrq6Xauk6/ZLtd4QKCAQEA4a0DaHcp
+OqFTudM8kC/N21FuE9U5MRLtJ2HP42iTEAfMi1bpZjO35giqYhfxNJiyTPnWDbkS
+HVvNuSLAG58/Xs0KTuXCEgWuALBFtgg39BrPTT9Kw2CdqCgIpx5ArBwF+3lDfGIH
+lIeoOhR7OKnN8a1/6l7KVOHxV4FMX9aVmom8FKKqjZYcObY86MgLObMtt50v0cKY
+GgNZCy1HLoXHRGJ+3pypZEV0qxbOt2jGAhEpDf/LdX9Ez4AIYK16/a5AvUnNJlvN
+d28LPgTCvPK8Mhz/m+7ms8IfZ2j0hJgm2j9jiMTK0QvyhWq57TfrU+8HWCKzsUfH
+M1Riwt0z8u9HpQKCAQEA2dhE79/lyaGe3lxqJmzxdLG3Khp3wAk4/SM6CP9elN9J
+4kg7UFGIE1AUs73HPwlXmoOyE1r+g6W2CaNMds5qqJKOIIS/89mrGZs68hpTLbdR
+rTrNNeTLlOQcF/txNfawYEtPwAedjIxUFATKDibbDwfe0p3mRZSm/ji0N3n5ehJl
+yDwfaSVsZsoHWm0GUfhMVGwu1OZjAGGpGZIf/tcjMNQ3cKAmqAvRpRrlc51eUw5M
++Z+POgRY2mnrKgTUREFzaLG62umaTVTGC6dn++QLPmFgoFuloSUJNUbRRvIg9d+a
+aatn2eiusBILVarQcHGWdysuMFqxUJ9VHQqCNqI4rQKCAQBjPcZF5kEHO3KqQS5c
+6ejJDaIurpGb9wq7StQ02QPzBLr6e5ngC9ZPHnhu8sBrtMqT9zoehshkiL6LL7Dz
+dLBVbC2gTIFvk3fVba76Qdr5SeDnw3GJQa+TByfm9fLSvPAUilsXE7TpqE5eXCtj
+26hpIzchRdYMRd/v7zg63Q6lCvTezjnaUazP5EgcxfvJv/XWzRT+VWi158r8k0i+
+OK5McFQCaTpEkhagNkNpfHW26vz23woF/ZWw+ki02xU/AaYOl6nTuIM+hmKXP1iz
+5rrD/uSZGhHx8ugEfa8psA9F4qJOvtvB2lMoQKrKmtCt9GtyYrBKwZnkBLP5pXT2
+3CrRAoIBABiIz/LIH6QezLq0Y8wiFuuSnFNkmboKD94Kmp2qzSctIrAWfH+mPxIV
+wc8gf5Es5y3iySp+5A1Fm4PoXVNAGikUIGevK8M175w5rGDZ8CZE8DD3X2dDdl41
+dqiIzA5M0z51HO0+rlLG9y0uAOepHqDJvSGxYN7TSB93mWxqE1vZOJddlhgMe/Hz
+rPJVNxICSe50JK4bqGjBlv7nQy07Y547OGc50kC43AqhRdhIj/gAs1Cl1Mau+KbY
+qQCZfKKXUH0pDydaieNNueRUHVT0MQP8iZpl1/iXKDtU13sLCAVJAqYGBPM4znvL
+/HTQgRs4375aIaCWhkPTPg3AQjwO9x0CggEANIoxDPtty49prkkd8xQQceySEbnf
+CeTljNd1uUEqki5DSJMfXajwuSkUrhZAnSevT7k9BjsAzqHM9sWqo3C3PQpFA2Q/
+seTA7lSIxyKvQ4bt/ZaMMeryCzC/WKH81+F35IoQ616nzJz+A7khHX/+l6ZdS9Ud
+LYmYUEB1PQVd0PFQQ67dnNCJ265Hi54XLez9rzuYbuNFIb+wg4FsjV8NHUQSMow3
+LQLLMO16nqs9lQA17WdTlhyR6fYrovh6h7puGzONesSF0s9uLSyiOQ9UDKenNPPb
+nBdM3Bwq5M72o11IKwTPvq95e09o2eFY0SlqwqLdqeD9lrbh/HWfO9VpWA==
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_aes128.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_aes128.pem
new file mode 100644
index 0000000..470f29b
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_aes128.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,7BFA0887B7740C1BBA458D0362E75F89
+
+aBIhF2zVGRMFZAWg8s11WnxoTQhGabdzchHBxPltnipEgvwOM9FGmYnVIDsFPvyb
+hSA2HgmN972SPeRG4hITzrIXiRZMBOd6Lchz9p2Ui/mqkR6RUfnUBkUtkgxGymSa
+oD7WAmbpNG73+7KX4gV36tevC1dM9lQzbClhhXQL9FH9LFh11cS7ZFa9L/esWHbb
+sxgicR8zG919UEA+XvhLPVXpYD3AVHTcyEHbPWiGY49SIEI+GOcLi6rvxQizcXKY
+pFtmlk/jM06OdZWur5/qLg3HqGP/o8GUjjA6KuAe5SIWVrmlkHCYHj6c1KFLVQJZ
+7qAUNvjVaELXOc3+SE64Fl4OMP/4MiUA5WJ+gcdIKFtX/qksjywzF7goNTsxB1ZX
+8g9JP6m5BF2VS/Woz+9ypekzk7DEKST4Knh8cYBNdz6yYwX73FBb93jk1aPMXv2K
+SzhupTSHyAt7ddvXm+QYXBrklpZgUcuvinrkb7TNYEDcc2h3iDkLMHhI2VW0fiSF
+JcL2DZWLu1UChg+Nkc43+9f8Ao76tjJHdV+IkWfINlKz8cpKthIfOiUSQWYK4Czr
+0E3dyUrljIqFyKmZGokQ2QH+kdMmcm2/R+8NwL4s0Y4k1ylB0mZ5+A55t0ESaUVC
+8NJtoIMrLz2pyhf2ohXMVepcjvR/Id8vNxSlLFResmIwSo4orTZOnsHBlQqm5hJl
+9dr8G0o7n7WVfGbHLlpuPHoKegn1OK5xLbDbAi14HnZIi7/lUnbQHMbFFBjyS5EE
+v+SMbI7JU+5zeJc34WLUTm1zll1ZNpFH/76vlP7tUWxYqVUZfKYHOndqS4b1rAs2
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_aes256.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_aes256.pem
new file mode 100644
index 0000000..5e9c5fd
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_aes256.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,5AA67B48694516B66EECE19E0F3BB048
+
+eXbuyN1kJR84JMhFruS9yeJ/WnyD0V6h9w/6fhEcPfxW0HmFqgIk35IE4c4QYazZ
+aMC0J1S19Yb4kKnRd7j0+92IDBw3B7zeJKjPDrcRDSo8R8K7O7+6MYwcmJJ/DZlC
+yqcaHr3cJEiS4I8NS3EdffffFA37Uf2O4C26b96ewg899YZXmPtai6uOvKbF1A90
+aRV4PdCpJdJq0q69VfoSeQ5p//Wokb4RfDGWTNxpgclYr0SK2F8qfITjCNDbqR+E
+r+6tRsy9VcHVRjswuewU19HOQNQzaPlIiNaoGBGvOVRHLdE0s8b1Czyh3oeuRAZi
+EK/SPEdZ5JMWbtxpKi+rdqFZhW3sGE5T9QItYqAWezScOCcuZw7ivVITWj0HuAJl
+YZnkL4CS2qMKfQ7oz/YBAuShDvfl/W1M2YXt8caoYIox8NWnGaifHbk+eP7sQRE3
+UMA/71gfz4AOUSKOWTNCen+qeAImyRi++6kFOVyAZnmvGAj1INv76xh6tHP9aDN+
+0ROlEMeL+4EyqEeTeRL5+i9xEGtA247hMjtwB6i8SDl2N9RZkIzGOj6GmJpWQhHK
+qRSu+PvoDfSHqfHYXof0rtvCuKj6pAhYgKeL5Ycfc0FtJX6gTvErlKWENUrsJxgV
+RJNcRbltxcOylmdYP2tUe/PxQCSFcYIzFbQbXJDoZ9hMgJRv086fzn/QWiZEx59o
+9r8kKQrYhKjS34Aq13ghFBf/Mr+GKrB8JtO5DnlC4N9AnapVlDgeZn2NmXyVZygA
+mcIq5MTJcZzCdX6PdzNVBsttRfYeItN9M8aP5q5YodBfuLXPOvFhV2J7wc5Bgt4i
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_device.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_device.pem
new file mode 100644
index 0000000..5c9a845
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_device.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvV0Pomdmpxx7s19ue0itPlPusWrTSwofoWEU141dp7cS9eXc
+SnQV5pfvYvvw21HsZHy+xW2cupjQBzvqPtrrqAz4p2E9VHTMuzvDSScj6GU0BUD+
+GQ3qthLkXMfh7pJZFN5DYtVqzSE+ufZlYlfBqsT1BON9eRs2fTf6OnMFApx0ANjJ
+yFElu2cEf3iif/3Z6PF0iUNtKaDNDVvVBoiRa9mUuj4vxnyH4HmyzThp2bXDLMUO
+Viz/WWZs9Nl/QoJduyJZGEii1UM5VS81qQnu2dT1g/E0yPXwtOQUnVgjSMPfW+CA
+yJ1XTdSwVDSsyXApyxxtSEYbM81t3GGvmmCf+QIDAQABAoIBAFFO4COvmlgu1r6S
+P3IYJqsYhukPIWKbGjHE6ZoUTR5ycWW8KPafGbhFjLhHzYeeiY4sMg27nwxQCSLS
+CyaqAX3K9AmKqzbUYAQVCSkj8TscGVYYLgK8Awfi3MMp4Ez78dwQA4cwdAdYOwLG
+VYoAfFvC7iIHPB0AHklt+7eVI5WWsYlOIf9aS6+PDVii86GVoCpBE0eNZjE3JJo7
+hC0ctcjOqSVZRPC4p5KLFI8QkwyEK3vstyIiQOKSCFbZIn5Wi0+d6A9K96WhwssM
+OpmenLv3xgz4zrUkRDbv0cPAU+/e7ZVPCiIbL/Y78qk3lBjUuyztX6fwQ/eDFadw
+BS6LcvECgYEA+Nlwgi1Kzcd46xKG7xryI0mYjHO4cbZMsTcB4FQK5xXkGlg6j+WD
+NAi/LKDJhaKcBKIYDhYi8tY1ye7JSDmyrPfZ0WvQF8K4yprUAqidNIuwOPMASjTW
+CEBgTfMwCATNYNiaQ8eRuTLyknF4I2lrc4Ifby7cYEZrJ6aLGyolvdcCgYEAws4J
+rGQS8W1DmzrL9mIDlSUtMFkTWqhx7nvG4lCYWVGpMByiQwf4DRvcqmXhwPr0syaj
+qE6nu8G3iuOywSx2qrPX1a6NzeoZZybOSL7poaVmnpxgtS1xXyo1Bdc5ZPPOom1T
+apb1QXHIPFVFKsuKsrQvyXfYLFGlQbYh3TGrtq8CgYEAmDqT+95vI0ECNHNp/f0y
+4OlVm53y2AUYF1S6Hhvra3/VwVP1xy80uvEa2dcmUEywOplaM8vQ51KpJvWfRkKd
+jfg01Eqqys5AsxhR16qEOK+3Rq9InxyBThzrjOPWnyEo7jSy8gG0oGGNSI6HWspT
+hB620hINmAub426bLCv1WJMCgYEAkdfBZEAT8o30BH5TfyVIO1v25fB6TfA4Q+yF
+LKBcPtqlSPDXBkosClxmq2fVSS5ZDtsJwZMJfsb8C86G4JrSSOCV4VNqtNPjqtdh
+rxLHRQ7YsjyvJlVcQHwP8Ex+mrbxZ6djwTQ9b36pA4pvWyfBsiK2eCXyQNPrXjPm
+THzIat8CgYAXN7CO+EUKtBil/r3PTj8FpWxvfZiKMbmktiHKD9ntcN3te2hrOWVs
+1SmrIzr8/eBIsh7OGS3LUZTb4wKGtY29N6fTHuoA8pYILeRcJALGZnmrsTyFDzGH
+iXBSkZ94EXpywxWHAEglow028nQ9UNugKk9SIkpg42u4vrqhRitMqw==
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_end_chain.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_end_chain.pem
new file mode 100644
index 0000000..7c1cb6c
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_end_chain.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA7R4S75j5ZfknrzbsAvUnqS997FNk545sGo0YraDQ7CeJXGqe
+DyUTpdwtzBt8H8/axNP1BFdgNEWnF1C1HXVbhqWxq16IlakqZYkYldHJMPQgFa3Y
+uRN4Y0i8sn8A1Fbe69D//57JZr1wt+qxAJWWn0anPGXmR0HlO0At1ACQkKGSE+aj
+TB2TnO7UTsjKRrXPv16FdWTXkJTVHUdVuAs6IfgAKkvQQ5zA8D6IXgjWsToWdFzl
+I8eTDHZqZJiw8gPg7xG0jepN9JU05JRc4NduzeVj/0WT0tdilO0jQP/+8FgikdOe
+DU6wo4/e0Ta5bdZe7apqOBfdDnq3EUnHO1ZQbQIDAQABAoIBADbu47Yse4L7YQ0/
+rRfWUfTpMsQgYd0far4P+Cqpeh1r32/Qp4OctFuVkeqaZ3w7PFSjQj1aPMh/ZoGJ
+ShxkBus/0dSA1yXNBix1wYNcEb9Mn25GU1I1R4vA2y6DK98FrSl2xwgickhiFQ4W
+yiD3huiphq8AcIQLqR679KIL63IF+lMnHmYTrm9/rkGvO/wiW55OMhLvhuR4w7/n
+5g+PMBLF4vEqtN6wEpb5f3Q8ugNCG35ykpgBMFWI6FGGmcZkYgux+xnTweZ9+Xol
+tBQRrq9cY3/ouIrRX4K30e/EcaJN0eA0Cx9WerEHfYO45BWUJGySsxPab9vk6Qep
+uxRnHoECgYEA+uv054HQKGQOZPjgZ9lCqfPB4wQ98T4hNSLgNwAkd85D7UtF6Wb9
+GMsEecJ5aPIQjDN6dTT6Nb45AV4e0XWtQFtxHFXP6SKfyIlf0Zcl0cd8Fvt7CX/e
+ghZF6ndUxHaWtAltALVwo+Fi6LOgEN7+dsBjkAZf994cZNfqrr6B1s0CgYEA8eqY
+u2p/QE7YNfw7naMUKDqgqGzo41IjF915rznYOyuO4hu+zGrL2D+7EeZnLWfSCSxl
+t0uowOzDOKkm6XeungMyJFH0DnzhYEgh6K9AXMi3QF0zfgGrh7hhK5wFEt+a4nOY
+hIAnqhANqISRhOOd0iT2VIt+igQhEQv8XLjAICECgYAVvkKfmQkfpuP0bfiMJzB2
+p6/Ca0iu0fJwt0/0lCeU1iPeuSoaupjuABGoN2jr5iX28DMJWwjfhVdNPgmvnuHf
+dM0NZoY4ro5oAzdxYwac8gtXtn0H6rOuVB3E3ohS6e/PNA3lBNP473vxrDcPnzMv
+uSYngdXpFa8iMe+dKtb3dQKBgQCAzse56q+Mvy5yODZJ7f4amXTXmP27pA1ZdKyI
+90TB5KR0kg9aanbVUsG5ezNuwrvb9I7INPnKl4Yu0ioM35PTQKJfIl/PowChsmaT
+rVSY0qp4E+gJ7Lu3TR44CR/Od87RSnln+5CjBV8wXj3ZQxTSQqoCRDABLseoevhJ
+Knnp4QKBgEHHHpegaCXl7+kaPzD/qD38iN6AmoAya9ZUxCUH55bDmkTFu1ZGgA6S
+87fuXQ2tzwFyOtaxAftPuOkSKMpZYRPJY92SalHA4WyN1pl0MhWMZQiyKOh0QQ7A
+yaHjrLn5wPrhHhkpmYu2L1ZNiN+NIFDuD/j+APCX+wqb9otfbfxk
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca.pem
new file mode 100644
index 0000000..57fef36
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAzhdv9CgZ895+JuF4J5ttgKS8HDKL5uCAYOS2B+o1OkGzYRkS
+I+vA41BpPfE8D8P9lgjcbzAOrOvgYHPZneE3Ix1mhlyPwbnXesC48AahbWXeHFJL
+u3Hr3EfjIVngptIHF0/FXmLtOjcxLpP/mXyUTX7bm0NMhgEvxAX34fPfgM4r6AwP
+YLwxJGl0c4rrb9choDmXp7+72NnjiO/pXzrUkI0VL+fV6U/cT9ro6p6wJYewvfXA
+zqV01B76xf+QhYGp6h/HSZQntTQfA2KIp3H7jhaVXgC/CyyZ/fvx1QUAVCy8pMck
+Fs7YuLtVuhAOZ1KpeZzaQotwRjTEeRkF99iCxwIDAQABAoIBAFNxi+O4hOGHuV42
+tjabKNgIWx2znY+KYJBaqhVET+7ZgS6UPxMKNlwTR7lLvjzH5xnjVpUySQ7cpkmH
+Ppo9AN0X31YRjicq/sL12ytcE+o+b5LaA03Oz2euN5leUaZZrYNTyh7wQQrsI96v
+D7NujIFgFryjoA0118gvfnEfE+SLW5q4m+n7D4cZ2D1nolDnOo2noLhKhMVWT9jp
+UVoa5sO+9/Ap71ElAaet2LawNsKbyjjRuI84544G09zQ+YUfcelADaqtXyYZpSJL
+iWrmHasD1w8NfiHB84y2hWHySoiuTBfVBJFfTjg075TaxBwCtDbzzEe0ecDCVGnb
+gZfkevECgYEA5+5cUFf9LXt7Cwqc/lnuIUYmX7pvTPVVZbdjEzd1MUvu12p01cRN
+QYk4K41LZsQYwDrCk82TFn1+hDbVt27i5r4FPJ9GbkSh4AiqUFDkqpqE/U06ZiUX
+JOosU6laI6MmguWbXAAQuv2OwK7xVA0575HdbsEK8LRP2bSTRUYNFjkCgYEA43qb
+FaKixVC4KOm8dyNYFHB5oldZW0u4ieemum8B+a9wd2BEG8FdxFw6IuGnKyMQ1BWu
+NVwN2wZsHbosmCYxGO7cX4OT37711hdCr4pCGQhQ3gf9eNls4ZjjykZp8EHWQnx8
+SYl7sjQMQUjhfqePKwR396IGx4KSrc/l1rxKYP8CgYEAjYPPJ+bIQFw7s30CVeAh
+gIQBHh/vkZGQTcQb27nW9AFU9nOqXlSsnvRPJaPNAiNcxs4Ts4OX3/0qmRmsRYSP
+RiNjpp24p8eQzdX7tY3mOIKX6saYf4LaIFgSO+n1ahE+ilf296fCjZXw6HjWH2cC
+lr710YJQXpZmsnuP8JDRo2ECgYBrasL+5WydZi+ASldPnuYByNb3HO46GTiMDlKB
+6Ndy8zBVfqTKwnWnurFNNWc+DHHu5En+Mnjse0zkgLx8IFTA5FI13Ckg18i4jwVT
+ZSMvNOkS340G2wz6PrsaEkQGSuCFRsld5Ej/7mn3DhZFO5R0iMipq94tqe/fmbN7
+wjAROwKBgHLl/XNhPTI/ed76ZjmCpbCvQlKZeYLhNbTS4lHvKCuniHKQsPz4BxjB
+fuVCUDH61yQT7WM31zIMfzQZjx9hTgFYYvnptRWj9zIBlIZJEieX3//flvEJ3Qmd
+/tbSoADa/1y+Y58biKkgmM4f4qS9h0AkVe88OCbpm14Xs2u7/4Nu
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca2.pem b/circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca2.pem
new file mode 100644
index 0000000..5a4dbee
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.key_intermediate_ca2.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAqhp+ySEP4Oods+29nF39auqEmJ/qTUqQCz+9iWyOhp6NU5pJ
+2LZiQNxVWIzasFHp9I1cvmf+4fnstMpaFl/TtdCpQnuV+eFIe7alEP6+Pc3YiFrO
+zyO0XnqJcZwmmkpr5omvGcx9FbcrKflzfgmn4ZvGIdgtwkA+ZAj6WkpZHGGpzqLY
+yAFz86fwANeT4I3a90aGwOEo/on+jvJA+O8KARsFx+yspxrfSZtNVTNWFfQfkIIt
+7vOWnevZNAEkMnHxY8jDFq8EACNGDo3r0j+G5imRl8P0hdklpN261OS1hANVM3l2
+t2INNN/UkZjJe3/T7RW30A0VZ3Wk/nz3IgKlDwIDAQABAoIBACV6FurrPNtZ2Vd2
+DqtvzdCLgNE7klybC+dekLzBTRl9vzdnK9PyQu11XdxXlCr6sSfvKTrOIMrazHr8
+hiKd1EAfi9sY7W8TYmvXTsDSz0lAm+9Wym+6txeFudhtBdhCg0lUll6BviFVrM3f
+psFjETjUoC9+uH4ut1BE5huUe9OTmOXxupLWdETcHViMc2VrpnDsNKX/lFIMFSo7
+3JDVojyTA/xdawJgkksCmQyHLICDvqJrIbLaDKxCfEiMKMyjVqgf9qwR33WAJ73e
+ES8XMVtgIce2Zpzx6OITf+gNhS0ekqsxZJ56iiacOVHxpphNKk+BwSj1bBxHLRoo
+narBebkCgYEA1BuLsbfr5lXAmGxE4JG1XrOZ7YtTqQNMYzy03s6fndoITqS+YpHk
+dtPqM/lQ6qByqLbaPZFVEqecIb9EVRoG1m+9rIkAutiWf/vAWwr7w7yIbFID2YBH
+50crmdtbK+1eoml/MkNx2grmgelqtMy5iOzwbPcFzmgXaTBzXvJvZEUCgYEAzU3F
+f3QP0xgbrT0iK6138waegsuPaLoBdq+arlZ0LMzuN7T3KTKiNfkJKWbxoaECmu1p
+PpJsQpG+0cQxyKU1PK9Pz+9qUXaPf/OsRKTcvpiS2kdBFXLPEgGi2JeSoPZxErYM
+WuaqLIM8krYrV0JBRgGv38Pd0u0VaSbQvPx5u0MCgYEAidFoIE6IKf64CJH44w3q
+EiGSt8Va06u/+48bWtZY4kEkOq1Sw0tWbltdhu3NRNaCCdvdzDldVKSxjz/vD3i8
+zqKGVNAkOEO47mnO35kwY0tiPTfBJpbyoXUeAHeGMvGmFtODgU5PcMS6Z9kZq2aG
+e1CxG6waCraZ15BStnPCKx0CgYA2n7Olhp7TPn3WqQZXcq8QdTlleX2tkpfjGTPh
+oNUGOnxDTB3a00L/c0QxxNcTdwB3ciVnZZPyXk7UBwxr4zD39XkZzQyPoijqFU5H
+cUneWD/yXbT+XO6lTtQiJqn3s7pADTnaUbcDYuOR8XA0pkcxti8yLS3u+e+Ra6ds
+MQy+ewKBgDfHrvBhbxyYskxc/xHnSeAZjrKsKoFNmV3/n9XTmms3WAVoCYydIEou
+Pm3p5kDiaiTTqLBpxoa4g2tuaEXBJpgLwxq79Jod4PA1bnip45ERRrk4kRROIaFg
+eHV6U0+PQTrdFhiJkvK7CgNZVr+NLiS1V1pM3x7xI81nAV9Sv4c5
+-----END RSA PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.noname.p12 b/circuitpython/lib/axtls/ssl/test/axTLS.noname.p12
new file mode 100644
index 0000000..0383293
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.noname.p12
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.unencrypted.p8 b/circuitpython/lib/axtls/ssl/test/axTLS.unencrypted.p8
new file mode 100644
index 0000000..df7c2e2
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.unencrypted.p8
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.unencrypted_pem.p8 b/circuitpython/lib/axtls/ssl/test/axTLS.unencrypted_pem.p8
new file mode 100644
index 0000000..dee0ed4
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.unencrypted_pem.p8
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL0P1EKodIdUqrk6
+H4vOvbdl+0A90BGanNyCfOqoF+F08wUOYcHBeIqyuhUiWv+buHouD4i3dN4EmaWi
+mVOLrXhaMe28Aeff6ewvoF1T9uaKoMhtQUVjI7PPTlAfKN824nPf1qGzRk9uuw2b
+76j5TKVxoYjdB6mGDT/NmSOihHcPAgMBAAECgYAmP+yWq9QfiQ6dONgnBeW2FAjX
+/2l4FkrEBhZVtzpVn76G+FjoxUao8O3a1r+IVS3mciksZMldHZskOphAodKvXKsj
+5DPQ6mBS53qec18ugNHcb0cPl4A20jAH3dbXFYkrdNV+irxjQgryMSm/+fnwiI+K
+wiJuFSa3XltYRBw7eQJBAOHxsuXIgJNAUHQU3bLyJ1wMPcBf7pxFbBMA39DZg/qQ
+LITyqsLd+88DQYgQxrtet7Yuph2quvtKctiarYgNahUCQQDWNiPzXXfI00nBk/7K
+Deub2r1HKHOXoFDXTCTfmws3rsMxtU9iCMrl75d7Q6DaKx+/qAiT0hYciZnx3ybR
+QpmTAkEAsUHkft8g9+Tx+U/Rai0N8ensnDrmwJS6J+J8tKWhI/bt5lNW4lAy2AKO
+68d1kdPKPtQ0IHwr+y86EHKxB1a2zQJAHjvyAw10NLItvNbIpXglgw/ymzKIbiRA
+hMLIiY72nFtcTY3LsIiRKrcQaGN5NpHTn1d2Lnb+i/SX992JOwvtZQJBALmvvwnJ
+kCbzcou/s3znb29bo5W4ngO5z6BTujLB062Fu3lICdY/nNk3kRENBNU7ynRdHJGN
+PfH4+b4117JTUB0=
+-----END PRIVATE KEY-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.withCA.p12 b/circuitpython/lib/axtls/ssl/test/axTLS.withCA.p12
new file mode 100644
index 0000000..0a89e73
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.withCA.p12
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.withoutCA.p12 b/circuitpython/lib/axtls/ssl/test/axTLS.withoutCA.p12
new file mode 100644
index 0000000..10e28fa
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.withoutCA.p12
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.cer b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.cer
new file mode 100644
index 0000000..784e26f
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.pem
new file mode 100644
index 0000000..0a5c7a6
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/LnxTANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjdaFw0zMDA5MDgyMTA0MjdaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAvQ/UQqh0h1SquTofi869t2X7QD3QEZqc3IJ86qgX4XTzBQ5hwcF4irK6FSJa
+/5u4ei4PiLd03gSZpaKZU4uteFox7bwB59/p7C+gXVP25oqgyG1BRWMjs89OUB8o
+3zbic9/WobNGT267DZvvqPlMpXGhiN0HqYYNP82ZI6KEdw8CAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAMuA8biHmpvS4EJ+K5guETizlFMpWgT/ALKM5iSTOr0cuGWKy
+5HaRJbzhqO5qaDp3ubJilwwlPF4TSIeAo5HZLuaSKxxSJLF3xvbe2JvZVzdWaBcy
+ZgEIOAiawYxeP+fJRMtiuUjHiab/jn094UYynBMGmtEXqz+pkAQzLT+BCqVVzraV
+VK3xT6LKw/Yle3HSaIXpcraZNG3lX/Z0HLmi2isE/4LFCQTEuryCPrRyGI4waEhK
+Dac9tfRCOpdgfahhip6YxH5lmep+ynXn2yFdznxmPX7cFP5VBJeoZBK0tTBIcrzb
+61tPpvuHAUGR7JiY8Us4okDxBZC7m12WsSJrUA==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha256.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha256.pem
new file mode 100644
index 0000000..b1059b4
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha256.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/LnxjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjdaFw0zMDA5MDgyMTA0MjdaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAvQ/UQqh0h1SquTofi869t2X7QD3QEZqc3IJ86qgX4XTzBQ5hwcF4irK6FSJa
+/5u4ei4PiLd03gSZpaKZU4uteFox7bwB59/p7C+gXVP25oqgyG1BRWMjs89OUB8o
+3zbic9/WobNGT267DZvvqPlMpXGhiN0HqYYNP82ZI6KEdw8CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEAbpbFPGnc74yvFgxKiNGA8+9azns10+KionRirc6g/1X1zBnJ
+7vBXW9aXwUr2y9G3jnmX82eut0YnaJ3xlU5rp2NbGSH43fQd/OvWC+6yDFBeHfJG
+JqbyP9oBkrUSuaXO/svsGUr8z1YVnxvtN7A1NGt+xQmgTyNq2QWg3MSQQwVtNsQt
+84mQqU0BmmyRyi14LOCi2dHxjduXFVgHIcM6XzVL8lxQ1oaabA1mP5u8dzH+n+sT
+uVDMmn6ABDZsnnCo9i7WidPI8pfJ4k6xR5l0wUdcOQeY8ynwUmILBt7lhKhxtAIG
+j9SHuiQYstmr5+6wIBv6LRwjuXNDEwcqdT+o1g==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha384.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha384.pem
new file mode 100644
index 0000000..cbdca1d
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha384.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/LnxzANBgkqhkiG9w0BAQwFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjdaFw0zMDA5MDgyMTA0MjdaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAvQ/UQqh0h1SquTofi869t2X7QD3QEZqc3IJ86qgX4XTzBQ5hwcF4irK6FSJa
+/5u4ei4PiLd03gSZpaKZU4uteFox7bwB59/p7C+gXVP25oqgyG1BRWMjs89OUB8o
+3zbic9/WobNGT267DZvvqPlMpXGhiN0HqYYNP82ZI6KEdw8CAwEAATANBgkqhkiG
+9w0BAQwFAAOCAQEAcVYgGWa/OFtWSpsH55h4iHan1Hj8WMzhvczirWDP8EaG3N1P
+WVo7LQdfhs9OvQwLlacAym3eawwsMe7ODU6Iq2vsbajeKMuF0jb5teghb7dMIiIW
+UIy3zRe9pmOZZpIcwJfuzTMTXMq+UksNfSzgB8mGqnIj+4D3UPXce5KYMvxzAjYU
+HAAFR9+ZkTdpratH/qFlk14DJ4CKlH69/RUpekkXcn3lk1DutfQ15kaTnaFObTUs
+HvGr2pX5PsofeR7DW4NStIMbOwMvTby6thCw8zImI2yyouCv+LkilGxmxEMAV85X
+ZKu5dNZXbJwfOFjkmHEtDfmISsdl8NoQxyz55A==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha512.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha512.pem
new file mode 100644
index 0000000..83648e6
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_1024_sha512.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/LnyDANBgkqhkiG9w0BAQ0FADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjdaFw0zMDA5MDgyMTA0MjdaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAvQ/UQqh0h1SquTofi869t2X7QD3QEZqc3IJ86qgX4XTzBQ5hwcF4irK6FSJa
+/5u4ei4PiLd03gSZpaKZU4uteFox7bwB59/p7C+gXVP25oqgyG1BRWMjs89OUB8o
+3zbic9/WobNGT267DZvvqPlMpXGhiN0HqYYNP82ZI6KEdw8CAwEAATANBgkqhkiG
+9w0BAQ0FAAOCAQEAbrG+mMdCorMaq6VHQC071CdXzqdUGFgXM/qpVjNo3tKjqUbD
+73FJOYYoSgXO7aJPebraaZTdSpgHO2vdJrifUjrJg+2RKDwIzCP853fgi3Nm4n+R
+7uDsd6K/cj4xvcnhIdmQZC3TPIJFujjbMy0Tw9EQ5Zoz5rNwR0Oy++HtWoULOjTe
+xsVsagshFaGajLF6RXoH+caKHIi4HANlWwJvHCBpqQYUyhUCAHUm9Pdo0+h7Viuj
+evzm3SPr5rOisE8xTDU4WPOhwel3lggZWfh6nbDG7+1sWmT4qTtAkv3V1m1o1h+8
+4TeJUOV9QHm88a2UUqWIZocaLxPYB6NThoBNVg==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.cer b/circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.cer
new file mode 100644
index 0000000..45a2912
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.pem
new file mode 100644
index 0000000..95bdb5a
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_2048.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcQCCQClKsh4h/LnyTANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjdaFw0zMDA5MDgyMTA0MjdaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAPladjynJXGaCmdzonEGTQOF6RmWw/ehmrG+ROur6DD7m+5fspZ+gPQm
+L3gZ2tIv3EB0QsKWLs82x0IDowAMO63L3oiKpvJyTMQPARTTCQh79JJw5UU9x9vM
+aE7dPGfzAnwmMjl6FbCTkugUjknRVcs4X1OpwInuYrJGJ5innJoWGhZveEvFliTD
+YBL8Mz8oXZhOK6alAR0Urt8z4B1J0USDn7ouSnWRsyqWgvotRp+fDDguFi/JILhD
+AwhJEA+s8RatD1+RgYLvs1aghQcl+7KMWQdNuxbJiVDeljaKN4Ufx9UFfEXV74DG
+09NMsphJ2FX5/WS+510oSiO9D4uWNdUCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+HKqK24TYW7NvGoIifLaFyaSwAQV/OODpiZmTie3X5RBHR34v9fnBk5qnBf7EZEfC
+uMg/mdMQm2Fst4uBzx9q8PltfcmCaX+/1M0F5nzhEszJ3cDevMBexNl7Q4nfYNTN
+QShJyhSgr9cQ/K48k9IA64RRcRP9DWtSNzt0zzA4UCqlkBvKd6TdcC+rAzOigdhT
+z0e1a9KVfKSxtXxyCAQZyKdsfWlhVyXPWd2urd8IfLfHdiFMKwyyreCCc4tCDjcA
+QcJkv2bfL8Cb4cUd2vtI8kic9zUBFaOWyz9tOicGG2k3SBjN99iQfBAsqbHjtGnj
++dKbDCxXy4udsNINgfE3aA==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.cer b/circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.cer
new file mode 100644
index 0000000..f90fb1c
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.pem
new file mode 100644
index 0000000..e6aaf25
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_4096.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3DCCAsQCCQClKsh4h/LnyjANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjdaFw0zMDA5MDgyMTA0MjdaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+AgoCggIBAMAKTqPX9LMlyPA5hJD/PHhwoGWfBbpnCAnKnBXtDssEfVLF1HUITKin
+zC4vFpXKPqzahXA37DcgJZQg4qvPXaf2jY94pwlQKF52ibomUOnV/VLQUVRFvXK/
+8+kY4K2UPfjJGzsfjF1XeOCPIxFlbltqCtXkM1PhVG9dnY+OZk2i6S+gcV3Fxgu3
+tiknju+YlsIrTMhpiUIq1fH7bVDWulXyru4rMREs+Y2lJrkrDGSJeh45vysZmcZA
+bhb9WZtipCqiLVf1qkGAq54IpdSOcyS4x1hTYwOt+FOt8lmaOS1kGPKaecMtLLQv
+O8iQ+9K7LM45t83o7VwFYqZtu/hlntWyiOlAFX8ht8vmxACjhnPW+lKVCe4kT3d9
+g62Qidcbm1Vty8foCuWCC7H+30AETwG3NSm7idhidr/YoSDc5Ksk+jehSZ7PwXGZ
+xwF5PiqK0tN8QzICPwjChI6HOWIxWPYqDlL77aXoNpG9FVKrwBzpuIvg1H+aUPNr
+h9erR1ZNmMmajVjVbvHG2LCwChWD7crq/RDtk3ioQiPrwI5eRTcN00AI8tTeD+6J
+Q6SYdVZMeatn22kLyKtRzKL0phiirzm144wKKpcFWogrpJK/3Y1dU3rtSU+dGj+7
+agjjqwWAWlhztSDgesRTkn6QJLmVX+IKh7Jdo04i2Osw4ZGnyYKBAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADggEBABPkfLZVIJ5z5cIhmiN5yqjMKotzGMwi7ihiPx8YSgBc
+2grI7Aqyojn/gPBTvKXGRYCGC7aXIBo69RlbnZy0VW1fLqoNo8y6+zLiryCtxsv6
+3orQmHslh2WofzzhkNyazT3WoGvzV4qhzK5KU7vkrFpHcwiLFRglBMP4ruoOq8jY
+4wKDrP3FpgaSpY4NOm7ro7LiYzmVcj6A9Gci6mm55er6ZGX0yI9O1FXOZCxABPJF
+kDEAk4GQScbaEDBZWjFAhkmdwYnE3n6usPnURwp08vDRkd4bGdrMT2KtP5ASNigQ
+vEaYj2pQJLS8Eljwc6nEBeGrZ14fGXXaVh+p9TKrpM8=
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_aes128.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_aes128.pem
new file mode 100644
index 0000000..1eeb601
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_aes128.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/Ln0DANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjhaFw0zMDA5MDgyMTA0MjhaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEA8k4Lr67fner/JtPfbCJe9+RMyq4ErKmXDlxQnXDEcvn/7Mw/CcNQXDPDRTN2
+/pw0Cwb45Pgv0bqTAFPK2sWhp68QMiuyl+lIUGSrgXX69nW7B2cAp4YSNW55M8SQ
+EISL3QOkzl1oHW3iuxxGeuCGuVv0i4gRxhQ1XkHdoGOc4G0CAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAPbTh7YlIYA/XOKC1Laf0SQ0HQU4hC9rd1IowBIyKYT5YlKVM
+VIrmqN7B3ytUZbP3eL9ZO63G5NHmGk/i+RpxF+O0oX09uhIPWHlupZ1LeEHDwsSG
+5OjZ1scU0bbIX/aBNPDL8hU7/TSTtL7NY9939cHE5LVwdSuOqndXl5nPFKxyJjfB
+npworzs+UUwXLAcJSRHTruyRf6fvSfl1NdMabWiR/L7+tzDV0842+HwUsJLUlWFv
+osK0avS8ER8PrFMRnKqGIK416B38ZMjn65G/J5v1j/RSHDAWjp+SYAFHaxiU0gEk
+WOAZlVmG1vVlFk/ubTJ/vd+RAk0QYI+THgwlbQ==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_aes256.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_aes256.pem
new file mode 100644
index 0000000..89fe526
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_aes256.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/Ln0TANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjhaFw0zMDA5MDgyMTA0MjhaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAqn/ZBpw3WLD87W/rVp8rdvg2yoml5CSMfePkcXKCfbWMAkZOJJtMupPoMFC9
+CbQ5EUmSV47BHGTh0xnQK/4s4bc6lPVtkK7dJtFfonCsiTErW0p6z0q7tQfWFEd3
+NGNFv/qVIsB0hJDOJn8x30wYWCOY+gC+53KJzRVcRMoM75sCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAqI4YkmyznQ9lE0+2cSkRX7ELJeKGedGFCCKJZaee5k9R4PBc
+fg97WsEMARSl2WLzAkK3rjSQWslbRxb/u1NYhd2uBdrYGIMXgPyBIjV7sSoI0TkC
+3muhke+W2yZsVp55DoJpfsX9dIfzbM0ICbaD509xuClfraIafO/6VLrw/+BzTTVM
+DECTo1dCheYYXyOJPgdgdcABKKb3WkTLJPkV6SCK0D8xAHpTRR8AtT0xu60JDRoq
+wcKZie02AXOgg3mEQPdvmVDb+cgOL2Cb/nyraS2OVKPRTZfQfvhve0mU3DN/TVDB
+7N9bVd1WNst+bsNIE0SbnYiF/oSdnHGNQxaLGA==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_after.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_after.pem
new file mode 100644
index 0000000..c90d630
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_after.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/Ln0zANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjEy
+MzAyMTA0MjhaFw0xNTEyMzEyMTA0MjhaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAvQ/UQqh0h1SquTofi869t2X7QD3QEZqc3IJ86qgX4XTzBQ5hwcF4irK6FSJa
+/5u4ei4PiLd03gSZpaKZU4uteFox7bwB59/p7C+gXVP25oqgyG1BRWMjs89OUB8o
+3zbic9/WobNGT267DZvvqPlMpXGhiN0HqYYNP82ZI6KEdw8CAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAqk3O/LBqyketDzFqMe7L8nqmM/crpmjZ93KEwhrc7uRKJgXm
+CjfTSm7D43l4xbz/NbHnNvSFnblFoioAQP23opotJV/WutmrGCIjpidLc682GvcP
+DvhlOTAqoVFFWmjL6gj2iKEtIUzxCMI7K/h/znQvk/NrsfvOF0uZtpMoLqqo4o3Y
+V2vogbTEPpr77952JMpsLvJYatwiYo8G7Pc8qdl86mk3tx1Kqn/Pl9CgMgj8tgFi
+tcBRFePe3WvAiK7vnSqz7H4NUXrcv0RTA7f/bUXNWoRUI98GbYKVpxgIY7uFmLF8
+0jb92pWMlA9Ps0fAdMKs98c7dIaJi0buM5Mtuw==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_before.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_before.pem
new file mode 100644
index 0000000..3d65fe6
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_bad_before.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAUACCQClKsh4h/Ln0jANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh
+eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yNDEy
+MzExNDAwMDBaFw0yNTEyMzExNDAwMDBaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl
+Y3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAvQ/UQqh0h1SquTofi869t2X7QD3QEZqc3IJ86qgX4XTzBQ5hwcF4irK6FSJa
+/5u4ei4PiLd03gSZpaKZU4uteFox7bwB59/p7C+gXVP25oqgyG1BRWMjs89OUB8o
+3zbic9/WobNGT267DZvvqPlMpXGhiN0HqYYNP82ZI6KEdw8CAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEALWtzVdl7s7NHPKslDc0NN2N/kqd8V5Ug93s9POLPQV6Cq7fH
+WPzxdU/IcLwocngqDzYEADlKJL1prRIP7IEtclRW109kVf5ge4waL7BIFogX0iII
+WzjSlLTdYFo6ZK/oxwg86+zVA9UbkWbvQwSGE60Hqr8GEaqon09CxYUpFTtOq7qZ
+Ikjjf1Rz/t1AAGXoN2Yls1hm2ONYFuxQq+dBME0sSL7hdhwFoOi/u3Q0QXmg4Z1Y
+xe0J3Pg1mt5nsh4cY0QDNZdSbZz1p4jVY5RxQsoYzgyVYQWBeDJGKs2RN8o1CFko
+EMQ4Bq65fSUs5hPnYGl4iibWmQQgAWkasKZwaw==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_device.cer b/circuitpython/lib/axtls/ssl/test/axTLS.x509_device.cer
new file mode 100644
index 0000000..1dc36cd
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_device.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_device.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_device.pem
new file mode 100644
index 0000000..2ecdabe
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_device.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIICUjCCAbsCCQClKsh4h/LnyzANBgkqhkiG9w0BAQUFADAsMRYwFAYDVQQKEw1h
+eFRMUyBQcm9qZWN0MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTYxMjMwMjEwNDI3
+WhcNMzAwOTA4MjEwNDI3WjArMSkwJwYDVQQKEyBheFRMUyBQcm9qZWN0IERldmlj
+ZSBDZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1d
+D6JnZqcce7NfbntIrT5T7rFq00sKH6FhFNeNXae3EvXl3Ep0FeaX72L78NtR7GR8
+vsVtnLqY0Ac76j7a66gM+KdhPVR0zLs7w0knI+hlNAVA/hkN6rYS5FzH4e6SWRTe
+Q2LVas0hPrn2ZWJXwarE9QTjfXkbNn03+jpzBQKcdADYychRJbtnBH94on/92ejx
+dIlDbSmgzQ1b1QaIkWvZlLo+L8Z8h+B5ss04adm1wyzFDlYs/1lmbPTZf0KCXbsi
+WRhIotVDOVUvNakJ7tnU9YPxNMj18LTkFJ1YI0jD31vggMidV03UsFQ0rMlwKcsc
+bUhGGzPNbdxhr5pgn/kCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBX+beg1jGMSqQd
+6Q/d07mQCLuEwmXmmz7hc0lgp6IumjsxulO2xLiotXie7e3GIRAVgJsd6ysRJKim
+IioOMxcwUMWw0CcqjcwqorczJjsqzW99dzCd3NcDiDxx+7Pye58D8qOLHGtafC9n
+TjQELV6dNIUX5IfH/18jAkPEmFcOUg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDDjCCAfagAwIBAgIJAORglrABpAToMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNV
+BAoTKWF4VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE2MTIzMDIxMDQyN1oXDTMwMDkwODIxMDQyN1owNDEyMDAGA1UEChMpYXhUTFMg
+UHJvamVjdCBEb2RneSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCxIzCSlgeXAiyLWjGWUkuMA+OPJzH23pDcE7Nc
+FKhkdzXAD6tBW4odqul7y1g4qU0/xvv16NsK//VTc2VWQwkm0tlQ+0PBMTBRk0Qb
+XuLFLjfyYYjUBsde/CE9qhPbhTKYQ5xsnEBm4KUN/Mqk3sKXo7M3s7HjiAeYEIFK
+dSFECcljEPrHzAEvDwd0fRsx2hzb4fgrJbfW4wa9rsL1iVQI9VAY8mxM/UMtGySr
+L7M7U0RFRM5fK0d5guWH9f+j41hW9R/tYq7qoLcjgcr45nk5bcT4RoTvehhGZRUN
+pVq8vttEoR/Y1JvOjBT5WwUSfJcgXtidEWnQPXKszNoLj1sNAgMBAAGjIzAhMA8G
+A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IB
+AQByY8+gtxSKO5ASYKArY8Sb4xJw0MNwsosARm17sT/8UTerS9CssUQXkW4LaU3T
+SzFVxRUz9O+74yu87W58IaxGNq80quFkOfpS39eCtbqD2e4Y/JuC30hK/GmhBquD
+gVh3z3hD8+DUXq83T1+9zgjCppLeQfaFejbaJSwQ7+K6gJn5JZNiaHVGN1GO6lT6
+5W3zqep5zU6a4T5UeZqkYyuzfTBbIkgEga0JzPaCdd5JOrwFR3UNWlICOxr7htDI
+FqiTrA0RH2ZNmrmJDcM1pFWlQAPnvDoRLeMpmvWZDnGrWYDzyt0j5G0bIPL7214c
+b5kxOvfGlM4E6v+lMSw/HDrI
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain.pem
new file mode 100644
index 0000000..c0a4eb3
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJDCCAgygAwIBAgIJAKUqyHiH8ufOMA0GCSqGSIb3DQEBCwUAMCgxJjAkBgNV
+BAoTHWF4VExTIFByb2plY3QgSW50ZXJtZWRpYXRlIENBMB4XDTE2MTIzMDIxMDQy
+OFoXDTMwMDkwODIxMDQyOFowLDEWMBQGA1UEChMNYXhUTFMgUHJvamVjdDESMBAG
+A1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+7R4S75j5ZfknrzbsAvUnqS997FNk545sGo0YraDQ7CeJXGqeDyUTpdwtzBt8H8/a
+xNP1BFdgNEWnF1C1HXVbhqWxq16IlakqZYkYldHJMPQgFa3YuRN4Y0i8sn8A1Fbe
+69D//57JZr1wt+qxAJWWn0anPGXmR0HlO0At1ACQkKGSE+ajTB2TnO7UTsjKRrXP
+v16FdWTXkJTVHUdVuAs6IfgAKkvQQ5zA8D6IXgjWsToWdFzlI8eTDHZqZJiw8gPg
+7xG0jepN9JU05JRc4NduzeVj/0WT0tdilO0jQP/+8FgikdOeDU6wo4/e0Ta5bdZe
+7apqOBfdDnq3EUnHO1ZQbQIDAQABo00wSzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB
+/wQEAwIF4DArBgNVHREEJDAigg93d3cuZXhhbXBsZS5uZXSCD3d3dy5leGFtcGxl
+Lm9yZzANBgkqhkiG9w0BAQsFAAOCAQEAbPQ1Y205Ricl1K2ByVa2RHJyYGP0Qj+H
+5mWj4A92N1vAb6uOrliqFYOClYINGsKKC8YDZdyA4c8ZglCKZ2RePwOSdaWIw7wi
+Efhysyq3WLSlo5jEcnkO7o5dV/7V7FX/+65UEXu35ZqaE8ZY++eotmLzccnInYP3
+1e4oaF5hyZIHVNCXnywNvchSkXjN0HhvIpeLTyzC5MQkQMMEOi8EBOgTGAo4UvL9
+eau3YhhpvfnCDlrRB6N79RcTLmJyOj5g1YO/9wn/du+EMwEkKUg5QJHzwUB2+ISp
+57JjKh2BQt0g/XJfRugHQcNtjUu73Ziexpl0qaXjNjb8mvsuV9RX0g==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain_bad.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain_bad.pem
new file mode 100644
index 0000000..743dbc0
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_end_chain_bad.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJjCCAg6gAwIBAgIJAKUqyHiH8ufPMA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV
+BAoTH2F4VExTIFByb2plY3QgSW50ZXJtZWRpYXRlIDIgQ0EwHhcNMTYxMjMwMjEw
+NDI4WhcNMzAwOTA4MjEwNDI4WjAsMRYwFAYDVQQKEw1heFRMUyBQcm9qZWN0MRIw
+EAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDtHhLvmPll+SevNuwC9SepL33sU2TnjmwajRitoNDsJ4lcap4PJROl3C3MG3wf
+z9rE0/UEV2A0RacXULUddVuGpbGrXoiVqSpliRiV0ckw9CAVrdi5E3hjSLyyfwDU
+Vt7r0P//nslmvXC36rEAlZafRqc8ZeZHQeU7QC3UAJCQoZIT5qNMHZOc7tROyMpG
+tc+/XoV1ZNeQlNUdR1W4Czoh+AAqS9BDnMDwPoheCNaxOhZ0XOUjx5MMdmpkmLDy
+A+DvEbSN6k30lTTklFzg127N5WP/RZPS12KU7SNA//7wWCKR054NTrCjj97RNrlt
+1l7tqmo4F90OercRScc7VlBtAgMBAAGjTTBLMAwGA1UdEwEB/wQCMAAwDgYDVR0P
+AQH/BAQDAgXgMCsGA1UdEQQkMCKCD3d3dy5leGFtcGxlLm5ldIIPd3d3LmV4YW1w
+bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBGJydzARx5fNYprptRG9c3pqecGKlX
+ArxbLe+K+83wS427HnWONnuZ5LJKGkuXjIeh1G/2AfKYXJz/SMN0IPYY3ro5Lt1Z
+GOPyn01qcj6OkfyiUPCDIsVFS9V0TVLTBLTMZFa1j6nMHs8Ajmm10Fkf/NQP/CVk
+zEgoktjb+wLqw1iPUGqkVepVtp3wg7VQY6E07SliNp/06Q3ue6xLJnYlKqFhUQf7
+064JyRIH9W/C2WnxWbKRBjsE4gCRnMvKQrCrfR64VohCr6XvygIIufwCG/CLJogn
+4OJMqz1oDkABPgSgjNhOdwnt+3dvxbjQBiRy1ERfNAJLo+V6x6aO1Gqz
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca.pem
new file mode 100644
index 0000000..4fbad81
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAeqgAwIBAgIJAKUqyHiH8ufMMA0GCSqGSIb3DQEBCwUAMDQxMjAwBgNV
+BAoTKWF4VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE2MTIzMDIxMDQyN1oXDTMwMDkwODIxMDQyN1owKDEmMCQGA1UEChMdYXhUTFMg
+UHJvamVjdCBJbnRlcm1lZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDOF2/0KBnz3n4m4Xgnm22ApLwcMovm4IBg5LYH6jU6QbNhGRIj68Dj
+UGk98TwPw/2WCNxvMA6s6+Bgc9md4TcjHWaGXI/Budd6wLjwBqFtZd4cUku7cevc
+R+MhWeCm0gcXT8VeYu06NzEuk/+ZfJRNftubQ0yGAS/EBffh89+AzivoDA9gvDEk
+aXRziutv1yGgOZenv7vY2eOI7+lfOtSQjRUv59XpT9xP2ujqnrAlh7C99cDOpXTU
+HvrF/5CFganqH8dJlCe1NB8DYoincfuOFpVeAL8LLJn9+/HVBQBULLykxyQWzti4
+u1W6EA5nUql5nNpCi3BGNMR5GQX32ILHAgMBAAGjIzAhMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwCwYDVR0PBAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQAYt1Wq1XsZjOWf
+mH9XzI3k8UEtErTkBX5SXH10siY5tYmHzH02jaZVRC1yNrxQevqARhR1yj3daKg3
++innX8foKlYRCxTfFeE/HELcm7rIt5mrIGdGAFn2fnImO5kpXxbPk6IAXRTp6lN8
+QS08Ah1Aaeh0ae2Ruzx2TOnG/IuB7ChliJxepo0KtRw6RN6ETvC2KS0glSf5fPW7
+c18UPbTesecrki12FlaGbPD/sWHOltBIpPXzgef7G8b6CiYqaX0T3T/47RLRwrMm
+SAcbMerJZoyudbLd+OHGtvz5kOmYCoZYkay121MSvtFucHnR3UjEui2lUbL1Qi92
+2L3MUIVn
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca2.pem b/circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca2.pem
new file mode 100644
index 0000000..ea8ab7d
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/axTLS.x509_intermediate_ca2.pem
@@ -0,0 +1,37 @@
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAeGgAwIBAgIJAKUqyHiH8ufNMA0GCSqGSIb3DQEBCwUAMCgxJjAkBgNV
+BAoTHWF4VExTIFByb2plY3QgSW50ZXJtZWRpYXRlIENBMB4XDTE2MTIzMDIxMDQy
+N1oXDTMwMDkwODIxMDQyN1owKjEoMCYGA1UEChMfYXhUTFMgUHJvamVjdCBJbnRl
+cm1lZGlhdGUgMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKoa
+fskhD+DqHbPtvZxd/WrqhJif6k1KkAs/vYlsjoaejVOaSdi2YkDcVViM2rBR6fSN
+XL5n/uH57LTKWhZf07XQqUJ7lfnhSHu2pRD+vj3N2Ihazs8jtF56iXGcJppKa+aJ
+rxnMfRW3Kyn5c34Jp+GbxiHYLcJAPmQI+lpKWRxhqc6i2MgBc/On8ADXk+CN2vdG
+hsDhKP6J/o7yQPjvCgEbBcfsrKca30mbTVUzVhX0H5CCLe7zlp3r2TQBJDJx8WPI
+wxavBAAjRg6N69I/huYpkZfD9IXZJaTdutTktYQDVTN5drdiDTTf1JGYyXt/0+0V
+t9ANFWd1pP589yICpQ8CAwEAAaMkMCIwEgYDVR0TAQH/BAgwBgEB/wIBCjAMBgNV
+HQ8EBQMDBwWAMA0GCSqGSIb3DQEBCwUAA4IBAQBNWDjqxlO0BwMdyxfUs1wDsLiq
+4hOmUFwCv8TuTFsL9aNe7OIJRT8IF2pHw07qyvAxJVEWUCLK/KLq0HrRTzRZO/tK
+eNroHMHS+fBwFuHFp+1RGMK2rHLajxVVB6X0aeLWKrNKrvUQZL+Tx+NZ3dEO62rq
+rANnElCrnyI1bIBKwHdUbOMOHeZAECr7H0KfvaX8F67aln4IwPzCauQM3DFf9h9Z
+FvmxUdVgjRZK4e5he9k4gT/Faom0z/ApnW4DJRF8rpCPWqN872aNpL8NPZuOFtiD
+Gzg5O4wJYx7OWIPa/qcQ0hTbn5waPzC68X448tlJTPiwyeKsYT3JwWqLi6o5
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAeqgAwIBAgIJAKUqyHiH8ufMMA0GCSqGSIb3DQEBCwUAMDQxMjAwBgNV
+BAoTKWF4VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE2MTIzMDIxMDQyN1oXDTMwMDkwODIxMDQyN1owKDEmMCQGA1UEChMdYXhUTFMg
+UHJvamVjdCBJbnRlcm1lZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDOF2/0KBnz3n4m4Xgnm22ApLwcMovm4IBg5LYH6jU6QbNhGRIj68Dj
+UGk98TwPw/2WCNxvMA6s6+Bgc9md4TcjHWaGXI/Budd6wLjwBqFtZd4cUku7cevc
+R+MhWeCm0gcXT8VeYu06NzEuk/+ZfJRNftubQ0yGAS/EBffh89+AzivoDA9gvDEk
+aXRziutv1yGgOZenv7vY2eOI7+lfOtSQjRUv59XpT9xP2ujqnrAlh7C99cDOpXTU
+HvrF/5CFganqH8dJlCe1NB8DYoincfuOFpVeAL8LLJn9+/HVBQBULLykxyQWzti4
+u1W6EA5nUql5nNpCi3BGNMR5GQX32ILHAgMBAAGjIzAhMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwCwYDVR0PBAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQAYt1Wq1XsZjOWf
+mH9XzI3k8UEtErTkBX5SXH10siY5tYmHzH02jaZVRC1yNrxQevqARhR1yj3daKg3
++innX8foKlYRCxTfFeE/HELcm7rIt5mrIGdGAFn2fnImO5kpXxbPk6IAXRTp6lN8
+QS08Ah1Aaeh0ae2Ruzx2TOnG/IuB7ChliJxepo0KtRw6RN6ETvC2KS0glSf5fPW7
+c18UPbTesecrki12FlaGbPD/sWHOltBIpPXzgef7G8b6CiYqaX0T3T/47RLRwrMm
+SAcbMerJZoyudbLd+OHGtvz5kOmYCoZYkay121MSvtFucHnR3UjEui2lUbL1Qi92
+2L3MUIVn
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/ca-bundle.crt b/circuitpython/lib/axtls/ssl/test/ca-bundle.crt
new file mode 100644
index 0000000..0c00239
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/ca-bundle.crt
@@ -0,0 +1,3585 @@
+-----BEGIN CERTIFICATE-----
+MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy
+MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
+A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
+YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD
+VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA
+isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj
+Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50
+QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt
+bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR
+yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID
+AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0
+cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f
+BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj
+cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1
+U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl
+YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos
+SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/
+t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u
+mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb
+K+9A46sd33oqK8n8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIESzCCAzOgAwIBAgIJAJigUTEEXRQpMA0GCSqGSIb3DQEBBQUAMHYxCzAJBgNV
+BAYTAkRFMQ8wDQYDVQQIEwZIZXNzZW4xDjAMBgNVBAcTBUZ1bGRhMRAwDgYDVQQK
+EwdEZWJjb25mMRMwEQYDVQQDEwpEZWJjb25mIENBMR8wHQYJKoZIhvcNAQkBFhBq
+b2VyZ0BkZWJpYW4ub3JnMB4XDTA1MTEwNTE3NTUxNFoXDTE1MTEwMzE3NTUxNFow
+djELMAkGA1UEBhMCREUxDzANBgNVBAgTBkhlc3NlbjEOMAwGA1UEBxMFRnVsZGEx
+EDAOBgNVBAoTB0RlYmNvbmYxEzARBgNVBAMTCkRlYmNvbmYgQ0ExHzAdBgkqhkiG
+9w0BCQEWEGpvZXJnQGRlYmlhbi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCvbOo0SrIwI5IMlsshH8WF3dHB9r9JlSKhMPaybawa1EyvZspMQ3wa
+F5qxNf3Sj+NElEmjseEqvCZiIIzqwerHu0Qw62cDYCdCd2+Wb5m0bPYB5CGHiyU1
+eNP0je42O0YeXG2BvUujN8AviocVo39X2YwNQ0ryy4OaqYgm2pRlbtT2ESbF+SfV
+Y2iqQj/f8ymF+lHo/pz8tbAqxWcqaSiHFAVQJrdqtFhtoodoNiE3q76zJoUkZTXB
+k60Yc3MJSnatZCpnsSBr/D7zpntl0THrUjjtdRWCjQVhqfhM1yZJV+ApbLdheFh0
+ZWlSxdnp25p0q0XYw/7G92ELyFDfBUUNAgMBAAGjgdswgdgwHQYDVR0OBBYEFMuV
+dFNb4mCWUFbcP5LOtxFLrEVTMIGoBgNVHSMEgaAwgZ2AFMuVdFNb4mCWUFbcP5LO
+txFLrEVToXqkeDB2MQswCQYDVQQGEwJERTEPMA0GA1UECBMGSGVzc2VuMQ4wDAYD
+VQQHEwVGdWxkYTEQMA4GA1UEChMHRGViY29uZjETMBEGA1UEAxMKRGViY29uZiBD
+QTEfMB0GCSqGSIb3DQEJARYQam9lcmdAZGViaWFuLm9yZ4IJAJigUTEEXRQpMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGZXxHg4mnkvilRIM1EQfGdY
+S5b/WcyF2MYSTeTvK4aIB6VHwpZoZCnDGj2m2D3CkHT0upAD9o0zM1tdsfncLzV+
+mDT/jNmBtYo4QXx5vEPwvEIcgrWjwk7SyaEUhZjtolTkHB7ACl0oD0r71St4iEPR
+qTUCEXk2E47bg1Fz58wNt/yo2+4iqiRjg1XCH4evkQuhpW+dTZnDyFNqwSYZapOE
+TBA+9zBb6xD1KM2DdY7r4GiyYItN0BKLfuWbh9LXGbl1C+f4P11g+m2MPiavIeCe
+1iazG5pcS3KoTLACsYlEX24TINtg4kcuS81XdllcnsV3Kdts0nIqPj6uhTTZD0k=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIRANAeQJAAAEZSAAAAAQAAAAQwDQYJKoZIhvcNAQEFBQAw
+gYkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJEQzETMBEGA1UEBxMKV2FzaGluZ3Rv
+bjEXMBUGA1UEChMOQUJBLkVDT00sIElOQy4xGTAXBgNVBAMTEEFCQS5FQ09NIFJv
+b3QgQ0ExJDAiBgkqhkiG9w0BCQEWFWFkbWluQGRpZ3NpZ3RydXN0LmNvbTAeFw05
+OTA3MTIxNzMzNTNaFw0wOTA3MDkxNzMzNTNaMIGJMQswCQYDVQQGEwJVUzELMAkG
+A1UECBMCREMxEzARBgNVBAcTCldhc2hpbmd0b24xFzAVBgNVBAoTDkFCQS5FQ09N
+LCBJTkMuMRkwFwYDVQQDExBBQkEuRUNPTSBSb290IENBMSQwIgYJKoZIhvcNAQkB
+FhVhZG1pbkBkaWdzaWd0cnVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCx0xHgeVVDBwhMywVCAOINg0Y95JO6tgbTDVm9PsHOQ2cBiiGo77zM
+0KLMsFWWU4RmBQDaREmA2FQKpSWGlO1jVv9wbKOhGdJ4vmgqRF4vz8wYXke8OrFG
+PR7wuSw0X4x8TAgpnUBV6zx9g9618PeKgw6hTLQ6pbNfWiKX7BmbwQVo/ea3qZGU
+LOR4SCQaJRk665WcOQqKz0Ky8BzVX/tr7WhWezkscjiw7pOp03t3POtxA6k4ShZs
+iSrK2jMTecJVjO2cu/LLWxD4LmE1xilMKtAqY9FlWbT4zfn0AIS2V0KFnTKo+SpU
++/94Qby9cSj0u5C8/5Y0BONFnqFGKECBAgMBAAGjFjAUMBIGA1UdEwEB/wQIMAYB
+Af8CAQgwDQYJKoZIhvcNAQEFBQADggEBAARvJYbk5pYntNlCwNDJALF/VD6Hsm0k
+qS8Kfv2kRLD4VAe9G52dyntQJHsRW0mjpr8SdNWJt7cvmGQlFLdh6X9ggGvTZOir
+vRrWUfrAtF13Gn9kCF55xgVM8XrdTX3O5kh7VNJhkoHWG9YA8A6eKHegTYjHInYZ
+w8eeG6Z3ePhfm1bR8PIXrI6dWeYf/le22V7hXZ9F7GFoGUHhsiAm/lowdiT/QHI8
+eZ98IkirRs3bs4Ysj78FQdPB4xTjQRcm0HyncUwZ6EoPclgxfexgeqMiKL0ZJGA/
+O4dzwGvky663qyVDslUte6sGDnVdNOVdc22esnVApVnJTzFxiNmIf1Q=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
+IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
+MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
+bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
+H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
+uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
+mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
+a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
+E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
+WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
+VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
+Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
+IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
+AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
+YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
+Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
+c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
+mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
+b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
+MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
+VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
+CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
+tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
+dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
+PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
+BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
+ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
+7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
+43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
+eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
+pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
+WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
+b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
+MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
+ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
+BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
+6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
+GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
+dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
+1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
+62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
+BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
+MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
+cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
+b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
+IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
+iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
+GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
+4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
+XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
+b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
+MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
+EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
+BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
+xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
+87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
+2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
+WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
+0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
+A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
+pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
+ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
+aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
+hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
+hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
+dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
+P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
+iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
+xqE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
+bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2
+MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
+ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk
+hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym
+1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW
+OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb
+2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko
+O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU
+AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
+Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb
+LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir
+oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C
+MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
+sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
+bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2
+MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
+ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC
+206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci
+KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2
+JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9
+BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e
+Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B
+PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67
+Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq
+Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
+o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3
++L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj
+YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj
+FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn
+xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2
+LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc
+obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8
+CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe
+IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA
+DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F
+AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX
+Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb
+AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl
+Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
+RY8mkaKO/qk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMCVVMx
+HTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNBbWVyaWNh
+IE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyOTA2MDAwMFoXDTM3MTEyMDE1
+MDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBT0wgVGltZSBXYXJuZXIg
+SW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUgSW5jLjE3MDUGA1UEAxMuQU9M
+IFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnej8Mlo2k06AX3dLm/WpcZuS+U
+0pPlLYnKhHw/EEMbjIt8hFj4JHxIzyr9wBXZGH6EGhfT257XyuTZ16pYUYfw8ItI
+TuLCxFlpMGK2MKKMCxGZYTVtfu/FsRkGIBKOQuHfD5YQUqjPnF+VFNivO3ULMSAf
+RC+iYkGzuxgh28pxPIzstrkNn+9R7017EvILDOGsQI93f7DKeHEMXRZxcKLXwjqF
+zQ6axOAAsNUl6twr5JQtOJyJQVdkKGUZHLZEtMgxa44Be3ZZJX8VHIQIfHNlIAqh
+BC4aMqiaILGcLCFZ5/vP7nAtCMpjPiybkxlqpMKX/7eGV4iFbJ4VFitNLLMCAwEA
+AaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoTYwFsuGkABFgFOxj8jY
+PXy+XxIwHwYDVR0jBBgwFoAUoTYwFsuGkABFgFOxj8jYPXy+XxIwDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQCKIBilvrMvtKaEAEAwKfq0FHNMeUWn
+9nDg6H5kHgqVfGphwu9OH77/yZkfB2FK4V1Mza3u0FIy2VkyvNp5ctZ7CegCgTXT
+Ct8RHcl5oIBN/lrXVtbtDyqvpxh1MwzqwWEFT2qaifKNuZ8u77BfWgDrvq2g+EQF
+Z7zLBO+eZMXpyD8Fv8YvBxzDNnGGyjhmSs3WuEvGbKeXO/oTLW4jYYehY0KswsuX
+n2Fozy1MBJ3XJU8KDk2QixhWqJNIV9xvrr2eZ1d3iVCzvhGbRWeDhhmH05i9CBoW
+H1iCC+GWaQVLjuyDUTEH1dSf/1l7qG6Fz9NLqUmwX7A5KGgOc90lmt4S
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMCVVMx
+HTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNBbWVyaWNh
+IE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAwMFoXDTM3MDkyODIz
+NDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBT0wgVGltZSBXYXJuZXIg
+SW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUgSW5jLjE3MDUGA1UEAxMuQU9M
+IFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ
+7ouZzU9AhqS2TcnZsdw8TQ2FTBVsRotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilb
+m2BPJoPRYxJWSXakFsKlnUWsi4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOY
+xFSMFkpBd4aVdQxHAWZg/BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZ
+YYCLqJV+FNwSbKTQ2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbq
+JS5Gr42whTg0ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fx
+I2rSAG2X+Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETz
+kxmlJ85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
+EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNoKk/S
+Btc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJKg71ZDIM
+gtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1ExMVCgyhwn2RAu
+rda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaAFE9pbQN+nZ8HGEO8txBO
+1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAO/Ouyugu
+h4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0cnAxa8cZmIDJgt43d15Ui47y6mdP
+yXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRFASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q
+7C+qPBR7V8F+GBRn7iTGvboVsNIYvbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKT
+RuidDV29rs4prWPVVRaAMCf/drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/
+ClTluUI8JPu3B5wwn3la5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyB
+M5kYJRF3p+v9WAksmWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQ
+my8YJPamTQr5O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xO
+AU++CrYD062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT
+9Y41xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
+hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOLZ8/5
+fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
+DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
+ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
+VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
+mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
+IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
+mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
+XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
+dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
+jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
+BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
+DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
+9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
+Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
+ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
+R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFajCCBFKgAwIBAgIEPLU9RjANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQKEwli
+ZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEGA1UEAxMq
+YmVUUlVTVGVkIFJvb3QgQ0EtQmFsdGltb3JlIEltcGxlbWVudGF0aW9uMB4XDTAy
+MDQxMTA3Mzg1MVoXDTIyMDQxMTA3Mzg1MVowZjESMBAGA1UEChMJYmVUUlVTVGVk
+MRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAxBgNVBAMTKmJlVFJVU1Rl
+ZCBSb290IENBLUJhbHRpbW9yZSBJbXBsZW1lbnRhdGlvbjCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALx+xDmcjOPWHIb/ymKt4H8wRXqOGrO4x/nRNv8i
+805qX4QQ+2aBw5R5MdKR4XeOGCrDFN5R9U+jK7wYFuK13XneIviCfsuBH/0nLI/6
+l2Qijvj/YaOcGx6Sj8CoCd8JEey3fTGaGuqDIQY8n7pc/5TqarjDa1U0Tz0yH92B
+FODEPM2dMPgwqZfT7syj0B9fHBOB1BirlNFjw55/NZKeX0Tq7PQiXLfoPX2k+Ymp
+kbIq2eszh+6l/ePazIjmiSZuxyuC0F6dWdsU7JGDBcNeDsYq0ATdcT0gTlgn/FP7
+eHgZFLL8kFKJOGJgB7Sg7KxrUNb9uShr71ItOrL/8QFArDcCAwEAAaOCAh4wggIa
+MA8GA1UdEwEB/wQFMAMBAf8wggG1BgNVHSAEggGsMIIBqDCCAaQGDysGAQQBsT4A
+AAEJKIORMTCCAY8wggFIBggrBgEFBQcCAjCCAToaggE2UmVsaWFuY2Ugb24gb3Ig
+dXNlIG9mIHRoaXMgQ2VydGlmaWNhdGUgY3JlYXRlcyBhbiBhY2tub3dsZWRnbWVu
+dCBhbmQgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk
+IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgdGhlIENlcnRpZmljYXRpb24g
+UHJhY3RpY2UgU3RhdGVtZW50IGFuZCB0aGUgUmVseWluZyBQYXJ0eSBBZ3JlZW1l
+bnQsIHdoaWNoIGNhbiBiZSBmb3VuZCBhdCB0aGUgYmVUUlVTVGVkIHdlYiBzaXRl
+LCBodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20vcHJvZHVjdHNfc2VydmljZXMvaW5k
+ZXguaHRtbDBBBggrBgEFBQcCARY1aHR0cDovL3d3dy5iZXRydXN0ZWQuY29tL3By
+b2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWwwHQYDVR0OBBYEFEU9w6nR3D8kVpgc
+cxiIav+DR+22MB8GA1UdIwQYMBaAFEU9w6nR3D8kVpgccxiIav+DR+22MA4GA1Ud
+DwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEASZK8o+6svfoNyYt5hhwjdrCA
+WXf82n+0S9/DZEtqTg6t8n1ZdwWtColzsPq8y9yNAIiPpqCy6qxSJ7+hSHyXEHu6
+7RMdmgduyzFiEuhjA6p9beP4G3YheBufS0OM00mG9htc9i5gFdPp43t1P9ACg9AY
+gkHNZTfqjjJ+vWuZXTARyNtIVBw74acT02pIk/c9jH8F6M7ziCpjBLjqflh8AXtb
+4cV97yHgjQ5dUX2xZ/2jvTg2xvI4hocalmhgRvsoFEdV4aeADGvi6t9NfJBIoDa9
+CReJf8Py05yc493EG931t3GzUwWJBtDLSoDByFOQtTwxiBdQn8nEDovYqAJjDQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFLDCCBBSgAwIBAgIEOU99hzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJX
+VzESMBAGA1UEChMJYmVUUlVTVGVkMRswGQYDVQQDExJiZVRSVVNUZWQgUm9vdCBD
+QXMxGjAYBgNVBAMTEWJlVFJVU1RlZCBSb290IENBMB4XDTAwMDYyMDE0MjEwNFoX
+DTEwMDYyMDEzMjEwNFowWjELMAkGA1UEBhMCV1cxEjAQBgNVBAoTCWJlVFJVU1Rl
+ZDEbMBkGA1UEAxMSYmVUUlVTVGVkIFJvb3QgQ0FzMRowGAYDVQQDExFiZVRSVVNU
+ZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANS0c3oT
+CjhVAb6JVuGUntS+WutKNHUbYSnE4a0IYCF4SP+00PpeQY1hRIfo7clY+vyTmt9P
+6j41ffgzeubx181vSUs9Ty1uDoM6GHh3o8/n9E1z2Jo7Gh2+lVPPIJfCzz4kUmwM
+jmVZxXH/YgmPqsWPzGCgc0rXOD8Vcr+il7dw6K/ifhYGTPWqZCZyByWtNfwYsSbX
+2P8ZDoMbjNx4RWc0PfSvHI3kbWvtILNnmrRhyxdviTX/507AMhLn7uzf/5cwdO2N
+R47rtMNE5qdMf1ZD6Li8tr76g5fmu/vEtpO+GRg+jIG5c4gW9JZDnGdzF5DYCW5j
+rEq2I8QBoa2k5MUCAwEAAaOCAfgwggH0MA8GA1UdEwEB/wQFMAMBAf8wggFZBgNV
+HSAEggFQMIIBTDCCAUgGCisGAQQBsT4BAAAwggE4MIIBAQYIKwYBBQUHAgIwgfQa
+gfFSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1
+bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0
+ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGFuZCBjZXJ0aWZpY2F0aW9uIHBy
+YWN0aWNlIHN0YXRlbWVudCwgd2hpY2ggY2FuIGJlIGZvdW5kIGF0IGJlVFJVU1Rl
+ZCdzIHdlYiBzaXRlLCBodHRwczovL3d3dy5iZVRSVVNUZWQuY29tL3ZhdWx0L3Rl
+cm1zMDEGCCsGAQUFBwIBFiVodHRwczovL3d3dy5iZVRSVVNUZWQuY29tL3ZhdWx0
+L3Rlcm1zMDQGA1UdHwQtMCswKaAnoCWkIzAhMRIwEAYDVQQKEwliZVRSVVNUZWQx
+CzAJBgNVBAYTAldXMB0GA1UdDgQWBBQquZtpLjub2M3eKjEENGvKBxirZzAfBgNV
+HSMEGDAWgBQquZtpLjub2M3eKjEENGvKBxirZzAOBgNVHQ8BAf8EBAMCAf4wDQYJ
+KoZIhvcNAQEFBQADggEBAHlh26Nebhax6nZR+csVm8tpvuaBa58oH2U+3RGFktTo
+Qb9+M70j5/Egv6S0phkBxoyNNXxlpE8JpNbYIxUFE6dDea/bow6be3ga8wSGWsb2
+jCBHOElQBp1yZzrwmAOtlmdE/D8QDYZN5AA7KXvOOzuZhmElQITcE2K3+spZ1gMe
+1lMBzW1MaFVA4e5rxyoAAEiCswoBw2AqDPeCNe5IhpbkdNQ96gFxugR1QKepfzk5
+mlWXKWWuGVUlBXJH0+gY3Ljpr0NzARJ0o+FcXxVdJPP55PS2Z2cS52QiivalQaYc
+tmBjRYoQtLpGEK5BV2VsPyMQPyEQWbfkQN0mDCP2qq4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGUTCCBTmgAwIBAgIEPLVPQDANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQKEwli
+ZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEGA1UEAxMq
+YmVUUlVTVGVkIFJvb3QgQ0EgLSBFbnRydXN0IEltcGxlbWVudGF0aW9uMB4XDTAy
+MDQxMTA4MjQyN1oXDTIyMDQxMTA4NTQyN1owZjESMBAGA1UEChMJYmVUUlVTVGVk
+MRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAxBgNVBAMTKmJlVFJVU1Rl
+ZCBSb290IENBIC0gRW50cnVzdCBJbXBsZW1lbnRhdGlvbjCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALr0RAOqEmq1Q+xVkrYwfTVXDNvzDSduTPdQqJtO
+K2/b9a0cS12zqcH+e0TrW6MFDR/FNCswACnxeECypP869AGIF37m1CbTukzqMvtD
+d5eHI8XbQ6P1KqNRXuE70mVpflUVm3rnafdE4Fe1FehmYA8NA/uCjqPoEXtsvsdj
+DheT389Lrm5zdeDzqrmkwAkbhepxKYhBMvnwKg5sCfJ0a2ZsUhMfGLzUPvfYbiCe
+yv78IZTuEyhL11xeDGbu6bsPwTSxfwh28z0mcMmLJR1iJAzqHHVOwBLkuhMdMCkt
+VjMFu5dZfsZJT4nXLySotohAtWSSU1Yk5KKghbNekLQSM80CAwEAAaOCAwUwggMB
+MIIBtwYDVR0gBIIBrjCCAaowggGmBg8rBgEEAbE+AAACCSiDkTEwggGRMIIBSQYI
+KwYBBQUHAgIwggE7GoIBN1JlbGlhbmNlIG9uIG9yIHVzZSBvZiB0aGlzIENlcnRp
+ZmljYXRlIGNyZWF0ZXMgYW4gYWNrbm93bGVkZ21lbnQgYW5kIGFjY2VwdGFuY2Ug
+b2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0
+aW9ucyBvZiB1c2UsIHRoZSBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
+dCBhbmQgdGhlIFJlbHlpbmcgUGFydHkgQWdyZWVtZW50LCB3aGljaCBjYW4gYmUg
+Zm91bmQgYXQgdGhlIGJlVFJVU1RlZCB3ZWIgc2l0ZSwgaHR0cHM6Ly93d3cuYmV0
+cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRleC5odG1sMEIGCCsGAQUF
+BwIBFjZodHRwczovL3d3dy5iZXRydXN0ZWQuY29tL3Byb2R1Y3RzX3NlcnZpY2Vz
+L2luZGV4Lmh0bWwwEQYJYIZIAYb4QgEBBAQDAgAHMIGJBgNVHR8EgYEwfzB9oHug
+eaR3MHUxEjAQBgNVBAoTCWJlVFJVU1RlZDEbMBkGA1UECxMSYmVUUlVTVGVkIFJv
+b3QgQ0FzMTMwMQYDVQQDEypiZVRSVVNUZWQgUm9vdCBDQSAtIEVudHJ1c3QgSW1w
+bGVtZW50YXRpb24xDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMjA0MTEw
+ODI0MjdagQ8yMDIyMDQxMTA4NTQyN1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaA
+FH1w5a44iwY/qhwaj/nPJDCqhIQWMB0GA1UdDgQWBBR9cOWuOIsGP6ocGo/5zyQw
+qoSEFjAMBgNVHRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIE
+kDANBgkqhkiG9w0BAQUFAAOCAQEAKrgXzh8QlOu4mre5X+za95IkrNySO8cgjfKZ
+5V04ocI07cUTWVwFtStPYZuR+0H8/NU8TZh2BvWBfevdkObRVlTa4y0MnxEylCIB
+evZsLHRnBMylj44ss0O1lKLQfelifwa+JwGDnjr9iu6YQ0pr17WXOzq/T220Y/oz
+ADQuLW2WyXvKmWO6vvT2MKAtmJbpVkQFqUSjYRDrgqFnXbxdJ3Wqiig2KjiS2d2k
+XgClzMx8KSreKJCrt+G2/30lC0DYqjSjLd4H61/OCt3Kfjp9JsFiaDrmLzfzgYYh
+xKlkqu9FNtEaZnz46TfW1mG+oq1I59/mdP7TbX3SJdysYlep9w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFaDCCBFCgAwIBAgIQO1nHe81bV569N1KsdrSqGjANBgkqhkiG9w0BAQUFADBi
+MRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENB
+czEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1wbGVtZW50YXRp
+b24wHhcNMDIwNDExMTExODEzWhcNMjIwNDEyMTEwNzI1WjBiMRIwEAYDVQQKEwli
+ZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEvMC0GA1UEAxMm
+YmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1wbGVtZW50YXRpb24wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkujQwCY5X0LkGLG9uJIAiv11DpvpPrILn
+HGhwhRujbrWqeNluB0s/6d/16uhUoWGKDi9pdRi3DOUUjXFumLhV/AyV0Jtu4S2I
+1DpAa5LxmZZk3tv/ePTulh1HiXzUvrmIdyM6CeYEnm2qXtLIvZpOGd+J6lsOfsPk
+tPDgaTuID0GQ+NRxQyTBjyZLO1bp/4xsN+lFrYWMU8NghpBKlsmzVLC7F/AcRdnU
+GxlkVgoZ98zh/4avflherHqQH8koOUV7orbHnB/ahdQhhlkwk75TMzf270HPM8er
+cmsl9fNTGwxMLvF1S++gh/f+ihXQbNXL+WhTuXAVE8L1LvtDNXUtAgMBAAGjggIY
+MIICFDAMBgNVHRMEBTADAQH/MIIBtQYDVR0gBIIBrDCCAagwggGkBg8rBgEEAbE+
+AAADCSiDkTEwggGPMEEGCCsGAQUFBwIBFjVodHRwOi8vd3d3LmJldHJ1c3RlZC5j
+b20vcHJvZHVjdHNfc2VydmljZXMvaW5kZXguaHRtbDCCAUgGCCsGAQUFBwICMIIB
+OhqCATZSZWxpYW5jZSBvbiBvciB1c2Ugb2YgdGhpcyBDZXJ0aWZpY2F0ZSBjcmVh
+dGVzIGFuIGFja25vd2xlZGdtZW50IGFuZCBhY2NlcHRhbmNlIG9mIHRoZSB0aGVu
+IGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNl
+LCB0aGUgQ2VydGlmaWNhdGlvbiBQcmFjdGljZSBTdGF0ZW1lbnQgYW5kIHRoZSBS
+ZWx5aW5nIFBhcnR5IEFncmVlbWVudCwgd2hpY2ggY2FuIGJlIGZvdW5kIGF0IHRo
+ZSBiZVRSVVNUZWQgd2ViIHNpdGUsIGh0dHA6Ly93d3cuYmV0cnVzdGVkLmNvbS9w
+cm9kdWN0c19zZXJ2aWNlcy9pbmRleC5odG1sMAsGA1UdDwQEAwIBBjAfBgNVHSME
+GDAWgBSp7BR++dlDzFMrFK3P9/BZiUHNGTAdBgNVHQ4EFgQUqewUfvnZQ8xTKxSt
+z/fwWYlBzRkwDQYJKoZIhvcNAQEFBQADggEBANuXsHXqDMTBmMpWBcCorSZIry0g
+6IHHtt9DwSwddUvUQo3neqh03GZCWYez9Wlt2ames30cMcH1VOJZJEnl7r05pmuK
+mET7m9cqg5c0Lcd9NUwtNLg+DcTsiCevnpL9UGGCqGAHFFPMZRPB9kdEadIxyKbd
+LrML3kqNWz2rDcI1UqJWN8wyiyiFQpyRQHpwKzg21eFzGh/l+n5f3NacOzDq28Bb
+J1zTcwfBwvNMm2+fG8oeqqg4MwlYsq78B+g23FW6L09A/nq9BqaBwZMifIYRCgZ3
+SK41ty8ymmFei74pnykkiFY5LKjSq5YDWtRIn7lAhAuYaPsBQ9Yb4gmxlxw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
+MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
+QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
+MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
+jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
+ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
+ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
+Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
+AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
+HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
+uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
+TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
+xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
+CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
+O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
+6GAqm4VKQPNriiTsBhYscw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
+MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
+GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
+YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
+MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
+BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
+GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
+BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
+3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
+YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
+rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
+ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
+oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
+QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
+b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
+AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
+GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
+Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
+G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
+l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
+smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb
+MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
+GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp
+ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow
+fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV
+BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM
+cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S
+HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996
+CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk
+3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz
+6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV
+HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
+EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
+Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw
+Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww
+DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0
+5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
+Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI
+gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ
+aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl
+izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb
+MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
+GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0
+aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla
+MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD
+VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW
+fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt
+TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL
+fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW
+1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7
+kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G
+A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
+ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo
+dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu
+Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/
+HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
+pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS
+jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+
+xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn
+dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV
+UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL
+EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ
+BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x
+ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg
+bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ
+j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV
+Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG
+SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx
+JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI
+RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw
+MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5
+fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i
++DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG
+SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN
+QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+
+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGpMQsw
+CQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENp
+dHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UE
+CxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0B
+CQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDExODE4NTVaFw0wODExMjgx
+ODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMO
+U2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0
+IENvLjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdf
+WvnTLnUv2Chi0ZMv/E3Uq4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uK
+xBmd9LIO/BZsubEFkoPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBE
+zUNKcI5YhZXhTizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F
+5X5yP4WdlGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMv
+OnNn7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+Legz
+ZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvVWlHG4VME
+lo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX8ngvYzZAOONG
+Dx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn86Oawde3uPclwx12q
+gUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsTF7ANUkz+/m9c4pFuHf2k
+Ytdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV
+UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL
+EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ
+BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x
+ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/
+k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso
+LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o
+TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG
+SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx
+JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI
+RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3
+MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C
+TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5
+WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG
+SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR
+xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL
+B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGpMQsw
+CQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENp
+dHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UE
+CxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0B
+CQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAyMjQ2MTZaFw0wODExMjcy
+MjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMO
+U2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0
+IENvLjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbV
+p9oaBBg5kkp4o4HC9Xd6ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWw
+BZoPFflrWXJW8vo5/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl
+5WJp3OXuAFK9MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi
+3sOP17ihYqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+
+QVCvbK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWogWxyQ
+2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6HE3K1GjN
+I3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV6YyDfFk/xPEL
+553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8PzGn0EdzMzkbzE5q
+10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30sPDst2yC7S8xmUJMqbIN
+uBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEgzCCA+ygAwIBAgIEOJ725DANBgkqhkiG9w0BAQQFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9HQ0NBX0NQUyBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAyMDAw
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVu
+dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAyMDcxNjE2NDBaFw0yMDAy
+MDcxNjQ2NDBaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
+LmVudHJ1c3QubmV0L0dDQ0FfQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
+YWIuKTElMCMGA1UECxMcKGMpIDIwMDAgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
+A1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCTdLS25MVL1qFof2LV7PdRV7Ny
+Spj10InJrWPNTTVRaoTUrcloeW+46xHbh65cJFET8VQlhK8pK5/jgOLZy93GRUk0
+iJBeAZfv6lOm3fzB3ksqJeTpNfpVBQbliXrqpBFXO/x8PTbNZzVtpKklWb1m9fkn
+5JVn1j+SgF7yNH0rhQIDAQABo4IBnjCCAZowEQYJYIZIAYb4QgEBBAQDAgAHMIHd
+BgNVHR8EgdUwgdIwgc+ggcyggcmkgcYwgcMxFDASBgNVBAoTC0VudHJ1c3QubmV0
+MUAwPgYDVQQLFDd3d3cuZW50cnVzdC5uZXQvR0NDQV9DUFMgaW5jb3JwLiBieSBy
+ZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0Lm5l
+dCBMaW1pdGVkMTMwMQYDVQQDEypFbnRydXN0Lm5ldCBDbGllbnQgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMDAy
+MDcxNjE2NDBagQ8yMDIwMDIwNzE2NDY0MFowCwYDVR0PBAQDAgEGMB8GA1UdIwQY
+MBaAFISLdP3FjcD/J20gN0V8/i3OutN9MB0GA1UdDgQWBBSEi3T9xY3A/ydtIDdF
+fP4tzrrTfTAMBgNVHRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4w
+AwIEkDANBgkqhkiG9w0BAQQFAAOBgQBObzWAO9GK9Q6nIMstZVXQkvTnhLUGJoMS
+hAusO7JE7r3PQNsgDrpuFOow4DtifH+La3xKp9U1PL6oXOpLu5OOgGarDyn9TS2/
+GpsKkMWr2tGzhtQvJFJcem3G8v7lTRowjJDyutdKPkN+1MhQGof4T4HHdguEOnKd
+zmVml64mXg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIElTCCA/6gAwIBAgIEOJsRPDANBgkqhkiG9w0BAQQFADCBujEUMBIGA1UEChML
+RW50cnVzdC5uZXQxPzA9BgNVBAsUNnd3dy5lbnRydXN0Lm5ldC9TU0xfQ1BTIGlu
+Y29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDIwMDAg
+RW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJl
+IFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAyMDQxNzIwMDBa
+Fw0yMDAyMDQxNzUwMDBaMIG6MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDE/MD0GA1UE
+CxQ2d3d3LmVudHJ1c3QubmV0L1NTTF9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
+dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0Lm5ldCBMaW1pdGVk
+MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
+b24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwV9OcfHO
+8GCGD9JYf9Mzly0XonUwtZZkJi9ow0SrqHXmAGc0V55lxyKbc+bT3QgON1WqJUaB
+bL3+qPZ1V1eMkGxKwz6LS0MKyRFWmponIpnPVZ5h2QLifLZ8OAfc439PmrkDQYC2
+dWcTC5/oVzbIXQA23mYU2m52H083jIITiQIDAQABo4IBpDCCAaAwEQYJYIZIAYb4
+QgEBBAQDAgAHMIHjBgNVHR8EgdswgdgwgdWggdKggc+kgcwwgckxFDASBgNVBAoT
+C0VudHJ1c3QubmV0MT8wPQYDVQQLFDZ3d3cuZW50cnVzdC5uZXQvU1NMX0NQUyBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAyMDAw
+IEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3QubmV0IFNlY3Vy
+ZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEw
+KwYDVR0QBCQwIoAPMjAwMDAyMDQxNzIwMDBagQ8yMDIwMDIwNDE3NTAwMFowCwYD
+VR0PBAQDAgEGMB8GA1UdIwQYMBaAFMtswGvjuz7L/CKc/vuLkpyw8m4iMB0GA1Ud
+DgQWBBTLbMBr47s+y/winP77i5KcsPJuIjAMBgNVHRMEBTADAQH/MB0GCSqGSIb2
+fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQQFAAOBgQBi24GRzsia
+d0Iv7L0no1MPUBvqTpLwqa+poLpIYcvvyQbvH9X07t9WLebKahlzqlO+krNQAraF
+JnJj2HVQYnUUt7NQGj/KEQALhUVpbbalrlHhStyCP2yMNLJ3a9kC9n8O6mUE8c1U
+yrrJzOCE98g+EZfTYAkYvAX/bIkz8OwVDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy
+MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
+LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
+YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
+A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
+K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
+sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
+MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
+XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
+HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
+4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA
+vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G
+CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA
+WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
+oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ
+h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18
+f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN
+B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy
+vUxFnmG6v4SBkgPR0ml8xQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50cnVzdC5u
+ZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBsaW1pdHMgbGlh
+Yi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
+BAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBaMIHJMQswCQYDVQQGEwJVUzEU
+MBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9D
+bGllbnRfQ0FfSW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMq
+RW50cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0G
+CSqGSIb3DQEBAQUAA4GLADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo
+6oT9n3V5z8GKUZSvx1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux
+5zDeg7K6PvHViTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zm
+AqTmT173iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSC
+ARkwggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50
+cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5m
+by9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMp
+IDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQg
+Q2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCyg
+KqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9DbGllbnQxLmNybDArBgNV
+HRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkxMDEyMTkyNDMwWjALBgNVHQ8E
+BAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW/O5bs8qZdIuV6kwwHQYDVR0OBBYE
+FMT7nCl7l81MlvzuW7PKmXSLlepMMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA
+BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7
+pFuPeJoSSJn59DXeDDYHAmsQOokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzz
+wy5E97BnRqqS5TvaHBkUODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/a
+EkP/TOYGJqibGapEPHayXOw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
+ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
+ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
+MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
+b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
+bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
+U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
+A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
+I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
+wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
+AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
+oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
+BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
+dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
+MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
+b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
+dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
+MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
+E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
+MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
+hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
+95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
+2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
+UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
+dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
+MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
+dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
+BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
+cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
+AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
+MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
+aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
+ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
+IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
+MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
+A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
+1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
+ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
+MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
+LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
+RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
+WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
+Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
+AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
+eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
+zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
+/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2Vj
+dXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0
+NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYD
+VQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0G
+vxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/
+BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0C
+AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEX
+MBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJl
+IGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkw
+NjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBq
+y/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQF
+MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
+A4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
+0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1
+E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
+R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
+9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
+fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
+iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
+1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
+MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
+ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
+uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
+Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
+tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
+hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
+5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
+VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
+bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
+b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
+iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
+r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
+04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
+GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
+3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
+lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB+jCCAWMCAgGjMA0GCSqGSIb3DQEBBAUAMEUxCzAJBgNVBAYTAlVTMRgwFgYD
+VQQKEw9HVEUgQ29ycG9yYXRpb24xHDAaBgNVBAMTE0dURSBDeWJlclRydXN0IFJv
+b3QwHhcNOTYwMjIzMjMwMTAwWhcNMDYwMjIzMjM1OTAwWjBFMQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMRwwGgYDVQQDExNHVEUgQ3liZXJU
+cnVzdCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC45k+625h8cXyv
+RLfTD0bZZOWTwUKOx7pJjTUteueLveUFMVnGsS8KDPufpz+iCWaEVh43KRuH6X4M
+ypqfpX/1FZSj1aJGgthoTNE3FQZor734sLPwKfWVWgkWYXcKIiXUT0Wqx73llt/5
+1KiOQswkwB6RJ0q1bQaAYznEol44AwIDAQABMA0GCSqGSIb3DQEBBAUAA4GBABKz
+dcZfHeFhVYAA1IFLezEPI2PnPfMD+fQ2qLvZ46WXTeorKeDWanOB5sCJo9Px4KWl
+IjeaY8JIILTbcuPI9tl8vrGvU9oUtCG41tWW4/5ODFlitppK+ULdjG+BqXH/9Apy
+bW1EDp3zdHSo1TRJ6V6e6bR64eVaH4QwnNOfpSXY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARwxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UE
+ChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UE
+ChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEzMDEGA1UECxMq
+SVBTIENBIENoYWluZWQgQ0FzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYD
+VQQDEypJUFMgQ0EgQ2hhaW5lZCBDQXMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx
+HjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczAeFw0wMTEyMjkwMDUzNTha
+Fw0yNTEyMjcwMDUzNThaMIIBHDELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNl
+bG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQg
+cHVibGlzaGluZyBTZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMu
+ZXMgQy5JLkYuICBCLTYwOTI5NDUyMTMwMQYDVQQLEypJUFMgQ0EgQ2hhaW5lZCBD
+QXMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMzAxBgNVBAMTKklQUyBDQSBDaGFp
+bmVkIENBcyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYP
+aXBzQG1haWwuaXBzLmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcVpJJ
+spQgvJhPUOtopKdJC7/SMejHT8KGC/po/UNaivNgkjWZOLtNA1IhW/A3mTXhQSCB
+hYEFcYGdtJUZqV92NC5jNzVXjrQfQj8VXOF6wV8TGDIxya2+o8eDZh65nAQTy2nB
+Bt4wBrszo7Uf8I9vzv+W6FS+ZoCua9tBhDaiPQIDAQABo4IEQzCCBD8wHQYDVR0O
+BBYEFKGtMbH5PuEXpsirNPxShwkeYlJBMIIBTgYDVR0jBIIBRTCCAUGAFKGtMbH5
+PuEXpsirNPxShwkeYlJBoYIBJKSCASAwggEcMQswCQYDVQQGEwJFUzESMBAGA1UE
+CBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExLjAsBgNVBAoTJUlQUyBJ
+bnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMubC4xKzApBgNVBAoUImlwc0Bt
+YWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0NTIxMzAxBgNVBAsTKklQUyBDQSBD
+aGFpbmVkIENBcyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAxMqSVBT
+IENBIENoYWluZWQgQ0FzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZI
+hvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8E
+BQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMG
+CCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYB
+BAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMw
+EYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0BtYWlsLmlwcy5lczBC
+BglghkgBhvhCAQ0ENRYzQ2hhaW5lZCBDQSBDZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkg
+aHR0cDovL3d3dy5pcHMuZXMvMCkGCWCGSAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlw
+cy5lcy9pcHMyMDAyLzA3BglghkgBhvhCAQQEKhYoaHR0cDovL3d3dy5pcHMuZXMv
+aXBzMjAwMi9pcHMyMDAyQ0FDLmNybDA8BglghkgBhvhCAQMELxYtaHR0cDovL3d3
+dy5pcHMuZXMvaXBzMjAwMi9yZXZvY2F0aW9uQ0FDLmh0bWw/MDkGCWCGSAGG+EIB
+BwQsFipodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3JlbmV3YWxDQUMuaHRtbD8w
+NwYJYIZIAYb4QgEIBCoWKGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcG9saWN5
+Q0FDLmh0bWwwbQYDVR0fBGYwZDAuoCygKoYoaHR0cDovL3d3dy5pcHMuZXMvaXBz
+MjAwMi9pcHMyMDAyQ0FDLmNybDAyoDCgLoYsaHR0cDovL3d3d2JhY2suaXBzLmVz
+L2lwczIwMDIvaXBzMjAwMkNBQy5jcmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUF
+BzABhhNodHRwOi8vb2NzcC5pcHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAERyMJ1W
+WKJBGyi3leGmGpVfp3hAK+/blkr8THFj2XOVvQLiogbHvpcqk4A0hgP63Ng9HgfN
+HnNDJGD1HWHc3JagvPsd4+cSACczAsDAK1M92GsDgaPb1pOVIO/Tln4mkImcJpvN
+b2ar7QMiRDjMWb2f2/YHogF/JsRj9SVCXmK9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH6jCCB1OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARIxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UE
+ChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UE
+ChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEuMCwGA1UECxMl
+SVBTIENBIENMQVNFMSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMl
+SVBTIENBIENMQVNFMSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3
+DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAxMTIyOTAwNTkzOFoXDTI1MTIyNzAw
+NTkzOFowggESMQswCQYDVQQGEwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYD
+VQQHEwlCYXJjZWxvbmExLjAsBgNVBAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5n
+IFNlcnZpY2VzIHMubC4xKzApBgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4g
+IEItNjA5Mjk0NTIxLjAsBgNVBAsTJUlQUyBDQSBDTEFTRTEgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxLjAsBgNVBAMTJUlQUyBDQSBDTEFTRTEgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4FEnpwvdr9G5Q1uCN0VWcu+atsIS7ywS
+zHb5BlmvXSHU0lq4oNTzav3KaY1mSPd05u42veiWkXWmcSjK5yISMmmwPh5r9FBS
+YmL9Yzt9fuzuOOpi9GyocY3h6YvJP8a1zZRCb92CRTzo3wno7wpVqVZHYUxJZHMQ
+KD/Kvwn/xi8CAwEAAaOCBEowggRGMB0GA1UdDgQWBBTrsxl588GlHKzcuh9morKb
+adB4CDCCAUQGA1UdIwSCATswggE3gBTrsxl588GlHKzcuh9morKbadB4CKGCARqk
+ggEWMIIBEjELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UE
+BxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQgcHVibGlzaGluZyBT
+ZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMuZXMgQy5JLkYuICBC
+LTYwOTI5NDUyMS4wLAYDVQQLEyVJUFMgQ0EgQ0xBU0UxIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MS4wLAYDVQQDEyVJUFMgQ0EgQ0xBU0UxIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYD
+VR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggr
+BgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIB
+FQYKKwYBBAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhC
+AQEEBAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGB
+D2lwc0BtYWlsLmlwcy5lczBBBglghkgBhvhCAQ0ENBYyQ0xBU0UxIENBIENlcnRp
+ZmljYXRlIGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgEC
+BBwWGmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMDoGCWCGSAGG+EIBBAQtFito
+dHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTEuY3JsMD8GCWCG
+SAGG+EIBAwQyFjBodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jldm9jYXRpb25D
+TEFTRTEuaHRtbD8wPAYJYIZIAYb4QgEHBC8WLWh0dHA6Ly93d3cuaXBzLmVzL2lw
+czIwMDIvcmVuZXdhbENMQVNFMS5odG1sPzA6BglghkgBhvhCAQgELRYraHR0cDov
+L3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lDTEFTRTEuaHRtbDBzBgNVHR8EbDBq
+MDGgL6AthitodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTEu
+Y3JsMDWgM6Axhi9odHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAy
+Q0xBU0UxLmNybDAvBggrBgEFBQcBAQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9v
+Y3NwLmlwcy5lcy8wDQYJKoZIhvcNAQEFBQADgYEAK9Dr/drIyllq2tPMMi7JVBuK
+Yn4VLenZMdMu9Ccj/1urxUq2ckCuU3T0vAW0xtnIyXf7t/k0f3gA+Nak5FI/LEpj
+V4F1Wo7ojPsCwJTGKbqz3Bzosq/SLmJbGqmODszFV0VRFOlOHIilkfSj945RyKm+
+hjM+5i9Ibq9UkE6tsSU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH6jCCB1OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARIxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UE
+ChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UE
+ChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEuMCwGA1UECxMl
+SVBTIENBIENMQVNFMyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMl
+SVBTIENBIENMQVNFMyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3
+DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAxMTIyOTAxMDE0NFoXDTI1MTIyNzAx
+MDE0NFowggESMQswCQYDVQQGEwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYD
+VQQHEwlCYXJjZWxvbmExLjAsBgNVBAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5n
+IFNlcnZpY2VzIHMubC4xKzApBgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4g
+IEItNjA5Mjk0NTIxLjAsBgNVBAsTJUlQUyBDQSBDTEFTRTMgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxLjAsBgNVBAMTJUlQUyBDQSBDTEFTRTMgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqxf+DrDGaBtT8FK+n/ra+osTBLsBjzLZ
+H49NzjaY2uQARIwo2BNEKqRrThckQpzTiKRBgtYj+4vJhuW5qYIF3PHeH+AMmVWY
+8jjsbJ0gA8DvqqPGZARRLXgNo9KoOtYkTOmWehisEyMiG3zoMRGzXwmqMHBxRiVr
+SXGAK5UBsh8CAwEAAaOCBEowggRGMB0GA1UdDgQWBBS4k/8uy9wsjqLnev42USGj
+mFsMNDCCAUQGA1UdIwSCATswggE3gBS4k/8uy9wsjqLnev42USGjmFsMNKGCARqk
+ggEWMIIBEjELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UE
+BxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQgcHVibGlzaGluZyBT
+ZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMuZXMgQy5JLkYuICBC
+LTYwOTI5NDUyMS4wLAYDVQQLEyVJUFMgQ0EgQ0xBU0UzIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MS4wLAYDVQQDEyVJUFMgQ0EgQ0xBU0UzIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYD
+VR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggr
+BgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIB
+FQYKKwYBBAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhC
+AQEEBAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGB
+D2lwc0BtYWlsLmlwcy5lczBBBglghkgBhvhCAQ0ENBYyQ0xBU0UzIENBIENlcnRp
+ZmljYXRlIGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgEC
+BBwWGmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMDoGCWCGSAGG+EIBBAQtFito
+dHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTMuY3JsMD8GCWCG
+SAGG+EIBAwQyFjBodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jldm9jYXRpb25D
+TEFTRTMuaHRtbD8wPAYJYIZIAYb4QgEHBC8WLWh0dHA6Ly93d3cuaXBzLmVzL2lw
+czIwMDIvcmVuZXdhbENMQVNFMy5odG1sPzA6BglghkgBhvhCAQgELRYraHR0cDov
+L3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lDTEFTRTMuaHRtbDBzBgNVHR8EbDBq
+MDGgL6AthitodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTMu
+Y3JsMDWgM6Axhi9odHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAy
+Q0xBU0UzLmNybDAvBggrBgEFBQcBAQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9v
+Y3NwLmlwcy5lcy8wDQYJKoZIhvcNAQEFBQADgYEAF2VcmZVDAyevJuXr0LMXI/dD
+qsfwfewPxqmurpYPdikc4gYtfibFPPqhwYHOU7BC0ZdXGhd+pFFhxu7pXu8Fuuu9
+D6eSb9ijBmgpjnn1/7/5p6/ksc7C0YBCJwUENPjDfxZ4IwwHJPJGR607VNCv1TGy
+r33I6unUVtkOE7LFRVA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARQxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UE
+ChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UE
+ChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEvMC0GA1UECxMm
+SVBTIENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLzAtBgNVBAMT
+JklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZI
+hvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwHhcNMDExMjI5MDEwNTMyWhcNMjUxMjI3
+MDEwNTMyWjCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQ
+BgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hp
+bmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5G
+LiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTEgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUExIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALsw19zQVL01Tp/FTILq0VA8R5j8
+m2mdd81u4D/u6zJfX5/S0HnllXNEITLgCtud186Nq1KLK3jgm1t99P1tCeWu4Wwd
+ByOgF9H5fahGRpEiqLJpxq339fWUoTCUvQDMRH/uxJ7JweaPCjbB/SQ9AaD1e+J8
+eGZDi09Z8pvZ+kmzAgMBAAGjggRTMIIETzAdBgNVHQ4EFgQUZyaW56G/2LUDnf47
+3P7yiuYV3TAwggFGBgNVHSMEggE9MIIBOYAUZyaW56G/2LUDnf473P7yiuYV3TCh
+ggEcpIIBGDCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQ
+BgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hp
+bmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5G
+LiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTEgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUExIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOC
+AQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUF
+BwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYB
+BAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglg
+hkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1Ud
+EgQTMBGBD2lwc0BtYWlsLmlwcy5lczBCBglghkgBhvhCAQ0ENRYzQ0xBU0VBMSBD
+QSBDZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkgaHR0cDovL3d3dy5pcHMuZXMvMCkGCWCG
+SAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyLzA7BglghkgBhvhC
+AQQELhYsaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0VBMS5j
+cmwwQAYJYIZIAYb4QgEDBDMWMWh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmV2
+b2NhdGlvbkNMQVNFQTEuaHRtbD8wPQYJYIZIAYb4QgEHBDAWLmh0dHA6Ly93d3cu
+aXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFQTEuaHRtbD8wOwYJYIZIAYb4QgEI
+BC4WLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcG9saWN5Q0xBU0VBMS5odG1s
+MHUGA1UdHwRuMGwwMqAwoC6GLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvaXBz
+MjAwMkNMQVNFQTEuY3JsMDagNKAyhjBodHRwOi8vd3d3YmFjay5pcHMuZXMvaXBz
+MjAwMi9pcHMyMDAyQ0xBU0VBMS5jcmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUF
+BzABhhNodHRwOi8vb2NzcC5pcHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAH66iqyA
+AIQVCtWYUQxkxZwCWINmyq0eB81+atqAB98DNEock8RLWCA1NnHtogo1EqWmZaeF
+aQoO42Hu6r4okzPV7Oi+xNtff6j5YzHIa5biKcJboOeXNp13XjFr/tOn2yrb25aL
+H2betgPAK7N41lUH5Y85UN4HI3LmvSAUS7SG
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARQxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UE
+ChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UE
+ChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEvMC0GA1UECxMm
+SVBTIENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLzAtBgNVBAMT
+JklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZI
+hvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwHhcNMDExMjI5MDEwNzUwWhcNMjUxMjI3
+MDEwNzUwWjCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQ
+BgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hp
+bmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5G
+LiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTMgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUEzIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO6AAPYaZC6tasiDsYun7o/ZttvN
+G7uGBiJ2MwwSbUhWYdLcgiViL5/SaTBlA0IjWLxH3GvWdV0XPOH/8lhneaDBgbHU
+VqLyjRGZ/fZ98cfEXgIqmuJKtROKAP2Md4bm15T1IHUuDky/dMQ/gT6DtKM4Ninn
+6Cr1jIhBqoCm42zvAgMBAAGjggRTMIIETzAdBgNVHQ4EFgQUHp9XUEe2YZM50yz8
+2l09BXW3mQIwggFGBgNVHSMEggE9MIIBOYAUHp9XUEe2YZM50yz82l09BXW3mQKh
+ggEcpIIBGDCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQ
+BgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hp
+bmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5G
+LiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTMgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUEzIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOC
+AQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUF
+BwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYB
+BAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglg
+hkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1Ud
+EgQTMBGBD2lwc0BtYWlsLmlwcy5lczBCBglghkgBhvhCAQ0ENRYzQ0xBU0VBMyBD
+QSBDZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkgaHR0cDovL3d3dy5pcHMuZXMvMCkGCWCG
+SAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyLzA7BglghkgBhvhC
+AQQELhYsaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0VBMy5j
+cmwwQAYJYIZIAYb4QgEDBDMWMWh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmV2
+b2NhdGlvbkNMQVNFQTMuaHRtbD8wPQYJYIZIAYb4QgEHBDAWLmh0dHA6Ly93d3cu
+aXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFQTMuaHRtbD8wOwYJYIZIAYb4QgEI
+BC4WLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcG9saWN5Q0xBU0VBMy5odG1s
+MHUGA1UdHwRuMGwwMqAwoC6GLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvaXBz
+MjAwMkNMQVNFQTMuY3JsMDagNKAyhjBodHRwOi8vd3d3YmFjay5pcHMuZXMvaXBz
+MjAwMi9pcHMyMDAyQ0xBU0VBMy5jcmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUF
+BzABhhNodHRwOi8vb2NzcC5pcHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAEo9IEca
+2on0eisxeewBwMwB9dbB/MjD81ACUZBYKp/nNQlbMAqBACVHr9QPDp5gJqiVp4MI
+3y2s6Q73nMify5NF8bpqxmdRSmlPa/59Cy9SKcJQrSRE7SOzSMtEQMEDlQwKeAYS
+AfWRMS1Jjbs/RU4s4OjNtckUFQzjB4ObJnXv
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICtzCCAiACAQAwDQYJKoZIhvcNAQEEBQAwgaMxCzAJBgNVBAYTAkVTMRIwEAYD
+VQQIEwlCQVJDRUxPTkExEjAQBgNVBAcTCUJBUkNFTE9OQTEZMBcGA1UEChMQSVBT
+IFNlZ3VyaWRhZCBDQTEYMBYGA1UECxMPQ2VydGlmaWNhY2lvbmVzMRcwFQYDVQQD
+Ew5JUFMgU0VSVklET1JFUzEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVz
+MB4XDTk4MDEwMTIzMjEwN1oXDTA5MTIyOTIzMjEwN1owgaMxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcTCUJBUkNFTE9OQTEZMBcGA1UE
+ChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UECxMPQ2VydGlmaWNhY2lvbmVzMRcw
+FQYDVQQDEw5JUFMgU0VSVklET1JFUzEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwu
+aXBzLmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsT1J0nznqjtwlxLyY
+XZhkJAk8IbPMGbWOlI6H0fg3PqHILVikgDVboXVsHUUMH2Fjal5vmwpMwci4YSM1
+gf/+rHhwLWjhOgeYlQJU3c0jt4BT18g3RXIGJBK6E2Ehim51KODFDzT9NthFf+G4
+Nu+z4cYgjui0OLzhPvYR3oydAQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBACzzw3lY
+JN7GO9HgQmm47mSzPWIBubOE3yN93ZjPEKn+ANgilgUTB1RXxafey9m4iEL2mdsU
+dx+2/iU94aI+A6mB0i1sR/WWRowiq8jMDQ6XXotBtDvECgZAHd1G9AHduoIuPD14
+cJ58GNCr+Lh3B0Zx8coLY1xq+XKU1QFPoNtC
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIIODCCB6GgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCAR4xCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UE
+ChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UE
+ChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjE0MDIGA1UECxMr
+SVBTIENBIFRpbWVzdGFtcGluZyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTE0MDIG
+A1UEAxMrSVBTIENBIFRpbWVzdGFtcGluZyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAxMTIyOTAxMTAx
+OFoXDTI1MTIyNzAxMTAxOFowggEeMQswCQYDVQQGEwJFUzESMBAGA1UECBMJQmFy
+Y2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExLjAsBgNVBAoTJUlQUyBJbnRlcm5l
+dCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMubC4xKzApBgNVBAoUImlwc0BtYWlsLmlw
+cy5lcyBDLkkuRi4gIEItNjA5Mjk0NTIxNDAyBgNVBAsTK0lQUyBDQSBUaW1lc3Rh
+bXBpbmcgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxNDAyBgNVBAMTK0lQUyBDQSBU
+aW1lc3RhbXBpbmcgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHjAcBgkqhkiG9w0B
+CQEWD2lwc0BtYWlsLmlwcy5lczCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+vLjuVqWajOY2ycJioGaBjRrVetJznw6EZLqVtJCneK/K/lRhW86yIFcBrkSSQxA4
+Efdo/BdApWgnMjvEp+ZCccWZ73b/K5Uk9UmSGGjKALWkWi9uy9YbLA1UZ2t6KaFY
+q6JaANZbuxjC3/YeE1Z2m6Vo4pjOxgOKNNtMg0GmqaMCAwEAAaOCBIAwggR8MB0G
+A1UdDgQWBBSL0BBQCYHynQnVDmB4AyKiP8jKZjCCAVAGA1UdIwSCAUcwggFDgBSL
+0BBQCYHynQnVDmB4AyKiP8jKZqGCASakggEiMIIBHjELMAkGA1UEBhMCRVMxEjAQ
+BgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJ
+UFMgSW50ZXJuZXQgcHVibGlzaGluZyBTZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJp
+cHNAbWFpbC5pcHMuZXMgQy5JLkYuICBCLTYwOTI5NDUyMTQwMgYDVQQLEytJUFMg
+Q0EgVGltZXN0YW1waW5nIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTQwMgYDVQQD
+EytJUFMgQ0EgVGltZXN0YW1waW5nIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4w
+HAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAM
+BgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYB
+BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIB
+FgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcwGgYD
+VR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0BtYWlsLmlw
+cy5lczBHBglghkgBhvhCAQ0EOhY4VGltZXN0YW1waW5nIENBIENlcnRpZmljYXRl
+IGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgECBBwWGmh0
+dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMEAGCWCGSAGG+EIBBAQzFjFodHRwOi8v
+d3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJUaW1lc3RhbXBpbmcuY3JsMEUGCWCG
+SAGG+EIBAwQ4FjZodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jldm9jYXRpb25U
+aW1lc3RhbXBpbmcuaHRtbD8wQgYJYIZIAYb4QgEHBDUWM2h0dHA6Ly93d3cuaXBz
+LmVzL2lwczIwMDIvcmVuZXdhbFRpbWVzdGFtcGluZy5odG1sPzBABglghkgBhvhC
+AQgEMxYxaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lUaW1lc3RhbXBp
+bmcuaHRtbDB/BgNVHR8EeDB2MDegNaAzhjFodHRwOi8vd3d3Lmlwcy5lcy9pcHMy
+MDAyL2lwczIwMDJUaW1lc3RhbXBpbmcuY3JsMDugOaA3hjVodHRwOi8vd3d3YmFj
+ay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyVGltZXN0YW1waW5nLmNybDAvBggrBgEF
+BQcBAQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZI
+hvcNAQEFBQADgYEAZbrBzAAalZHK6Ww6vzoeFAh8+4Pua2JR0zORtWB5fgTYXXk3
+6MNbsMRnLWhasl8OCvrNPzpFoeo2zyYepxEoxZSPhExTCMWTs/zif/WN87GphV+I
+3pGW7hdbrqXqcGV4LCFkAZXOzkw+UPS2Wctjjba9GNSHSl/c7+lW8AoM6HU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
+BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
+aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
+9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy
+NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
+azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
+Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
+cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD
+cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs
+2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY
+JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE
+Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ
+n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
+PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcWgAwIBAgIQCgEBAQAAAnwAAAALAAAAAjANBgkqhkiG9w0BAQUFADA6
+MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp
+dHkgMTAyNCBWMzAeFw0wMTAyMjIyMTAxNDlaFw0yNjAyMjIyMDAxNDlaMDoxGTAX
+BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAx
+MDI0IFYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDV3f5mCc8kPD6ugU5O
+isRpgFtZO9+5TUzKtS3DJy08rwBCbbwoppbPf9dYrIMKo1W1exeQFYRMiu4mmdxY
+78c4pqqv0I5CyGLXq6yp+0p9v+r+Ek3d/yYtbzZUaMjShFbuklNhCbM/OZuoyZu9
+zp9+1BlqFikYvtc6adwlWzMaUQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBTEwBykB5T9zU0B1FTapQxf3q4FWjAd
+BgNVHQ4EFgQUxMAcpAeU/c1NAdRU2qUMX96uBVowDQYJKoZIhvcNAQEFBQADgYEA
+Py1q4yZDlX2Jl2X7deRyHUZXxGFraZ8SmyzVWujAovBDleMf6XbN3Ou8k6BlCsdN
+T1+nr6JGFLkM88y9am63nd4lQtBU/55oc2PcJOsiv6hy8l4A4Q1OOkNumU4/iXgD
+mMrzVcydro7BqkWY+o8aoI2II/EVQQ2lRj6RP4vr93E=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6
+MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp
+dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX
+BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy
+MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp
+eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg
+/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl
+wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh
+AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2
+PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu
+AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR
+MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc
+HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/
+Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
+f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO
+rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch
+6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3
+7CAFYd4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
+MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
+dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
+WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
+VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
+9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
+DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
+Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
+QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
+xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
+A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
+kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
+Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
+Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
+JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
+RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
+MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx
+MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV
+BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG
+29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk
+oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk
+3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL
+qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN
+nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw
+DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG
+MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX
+ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H
+DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO
+TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv
+kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w
+zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
+MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
+MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
+BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
+Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
+5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
+3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
+vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
+8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
+DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
+MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
+zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
+3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
+FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
+Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
+ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO
+TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh
+dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy
+MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk
+ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn
+ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71
+9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO
+hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U
+tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o
+BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh
+SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww
+OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv
+cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA
+7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k
+/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm
+eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6
+u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy
+7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
+iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF
+MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU
+QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI
+MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENBMSkwJwYJKoZIhvcN
+AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla
+Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy
+ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y
+IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1
+c3RDZW50ZXIgQ2xhc3MgMiBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA
+dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0y
+AClxgwENv4wB3NrGrTmkqYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDw
+TFXlay3HhQswHJJOgtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8
+/vhYnvgpjbB7zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3
+LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+WLDO/
+jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xRT3h2oNms
+Gb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/AcASZ4smZHcFFk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF
+MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU
+QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI
+MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENBMSkwJwYJKoZIhvcN
+AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla
+Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy
+ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y
+IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1
+c3RDZW50ZXIgQ2xhc3MgMyBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA
+dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUF
+Lg2N7KBAahwOJ6ZQkmtQGwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGw
+Dtf7pBc9r6tpepYnv68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDW
+w1Krj10nnGvAo+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3
+LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4iJIE
+Tb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yCGdHHsbHD
+2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQSCdS7kjXvD9s0
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE
+SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg
+Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV
+BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl
+cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA
+vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu
+Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a
+0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1
+4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN
+eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD
+R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG
+A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu
+dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME
+Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3
+WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw
+HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ
+KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO
+Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX
+wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+
+2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89
+9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0
+jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38
+aQNiuJkFBT1reBK9sG9l
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJE
+SzEMMAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEw
+ODM5MzBaFw0zNzAyMTEwOTA5MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNU
+REMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuHnEz9pPPEXyG9VhDr
+2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0zY0s
+2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItU
+GBxIYXvViGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKj
+dGqPqcNiKXEx5TukYBdedObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+r
+TpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB5DCB4TCB3gYIKoFQgSkB
+AQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5kay9yZXBv
+c2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRl
+ciBmcmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEu
+MS4xLiBDZXJ0aWZpY2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIg
+T0lEIDEuMi4yMDguMTY5LjEuMS4xLjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1Ud
+HwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEMMAoGA1UEChMDVERDMRQwEgYD
+VQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYmaHR0cDovL2Ny
+bC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy
+MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZ
+J2cdUBVLc647+RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqG
+SIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACrom
+JkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4A9G28kNBKWKnctj7fAXmMXAnVBhO
+inxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYscA+UYyAFMP8uXBV2Y
+caaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9AOoB
+mbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQ
+YqbsFbS1AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9
+BKNDLdr8C2LqL19iUw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
+VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
+ZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBlcnNvbmFsIEJhc2lj
+IENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNpY0B0aGF3dGUuY29tMB4X
+DTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgcsxCzAJBgNVBAYTAlpBMRUw
+EwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UE
+ChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2Vy
+dmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBD
+QTEoMCYGCSqGSIb3DQEJARYZcGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53
+dXLdjUmbllegeNTKP1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdK
+wPQIcOk8RHtQfmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7
+G1sY0b8jkyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQF
+AAOBgQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95B21P
+9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
+VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
+ZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVt
+YWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUu
+Y29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgdExCzAJBgNVBAYT
+AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEa
+MBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRp
+b24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBG
+cmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhh
+d3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfY
+DFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5E
+rHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVq
+uzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
+BgkqhkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgCneSa
+/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr5PjRznei
+gQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
+VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
+ZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBlcnNvbmFsIFByZW1p
+dW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXByZW1pdW1AdGhhd3RlLmNv
+bTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5NTlaMIHPMQswCQYDVQQGEwJa
+QTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xGjAY
+BgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5nMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9u
+IFNlcnZpY2VzIERpdmlzaW9uMSMwIQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJl
+bWl1bSBDQTEqMCgGCSqGSIb3DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUu
+Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0Vs
+Bd/eJxZRNkERbGw77f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWI
+Et12TfIa/G8jHnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYD
+ZicRFTuqW/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
+SIb3DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVxeTBh
+KXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1KzGJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
+VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
+dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
+MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
+MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
+A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
+b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
+cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
+bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
+VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
+ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
+uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
+pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
+VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
+MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
+MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
+DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
+dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
+cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
+DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
+gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
+yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
+L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
+EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
+7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
+qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
+BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAd
+BgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcwMTAxMDAwMDAwWhcN
+MjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4g
+Q2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsG
+A1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1l
+c3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYrWHhhRYZT
+6jR7UZztsOYuGA7+4F+oJ9O0yeB8WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQa
+Wt9MevPZQx08EHp5JduQ/vBR5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL
+8vg7ij5FrHGSALSQQZj7X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMB
+Af8wDQYJKoZIhvcNAQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC
+9RAIDb/LogWK0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQ
+pgCed/r8zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZ
+CayJSdM=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
+kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
+IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
+EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
+VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
+dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
+E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
+D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
+4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
+lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
+bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
+o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
+MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
+LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
+BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
+AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
+Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
+j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
+KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
+2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
+mfnGV/TJVTl4uix5yaaIK/QI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB
+rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt
+Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa
+Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV
+BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l
+dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE
+AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B
+YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9
+hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l
+L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm
+SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM
+1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws
+6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
+DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw
+Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50
+aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
+AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u
+7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0
+xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ
+rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim
+eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk
+USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
+A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
+MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
+d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
+cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
+0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
+M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
+MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
+oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
+DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
+oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
+dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
+bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
+BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
+//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
+CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
+CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
+3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
+KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCB
+ozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3Qt
+TmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5WhcNMTkwNzA5MTg1
+NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYD
+VQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VS
+Rmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCz+5Gh5DZVhawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2
+N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCH
+iZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hARe
+YFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1
+axwiP8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6g
+yN7igEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD
+AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPh
+ahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9V
+VE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0GCSqGSIb3DQEB
+BQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y
+IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6Lzs
+QCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4
+ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qM
+YEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUb
+QErNaLly7HF27FSOH4UMAWr6pjisH8SE
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
+BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
+aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
+9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy
+NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
+azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
+Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
+cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y
+LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+
+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y
+TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0
+LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW
+I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
+nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
+BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
+aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
+9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
+NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
+azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
+Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
+cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
+dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
+WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
+v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
+UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
+IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
+W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8xCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh
+c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05
+NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYD
+VQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMSBQdWJsaWMgUHJp
+bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3noaACpEO+jglr0aIguVzqKCbJF0N
+H8xlbgyw0FaEGIeaBpsQoXPftFg5a27B9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR
+4k5FVmkfeAKA2txHkSm7NsljXMXg1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATAN
+BgkqhkiG9w0BAQIFAAOBgQBMP7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZo
+EWx8QszznC7EBz8UsA9P/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5
+FvjqBUuUfx3CHMjjt/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89Fx
+lA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
+c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
+MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
+emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
+DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
+FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg
+UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
+YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
+MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK
+VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm
+Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID
+AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J
+h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul
+uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68
+DzFc6PLZ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
+LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
+aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
+VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
+IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4
+nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO
+8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV
+ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb
+PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2
+6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr
+n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a
+qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4
+wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3
+ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs
+pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4
+E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAyIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZMJaLtVRKXxaeAufqDwSCg+i8VDXyh
+YGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvEerf4Zh+AVPy3wo5ZShRXRtGak75BkQO7
+FYCTXOvnzAhsPz6zSvz/S2wj1VCCJkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0G
+CSqGSIb3DQEBAgUAA4GBAIobK/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxg
+J8pFUs4W7z8GZOeUaHxgMxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Nc
+r6Pc5iaAIzy4RHT3Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns
+YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
+MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y
+aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe
+Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj
+IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx
+KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM
+HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw
+DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC
+AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji
+nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
+rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn
+jBJ7xUS0rg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy
+aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s
+IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp
+Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV
+BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp
+Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g
+Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
+IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU
+J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO
+JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY
+wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o
+koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN
+qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E
+Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe
+xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u
+7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU
+sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI
+sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP
+cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
+lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
+AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
+c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
+MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
+emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
+DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
+FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
+UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
+YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
+MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
+pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
+13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
+AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
+U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
+F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
+oJ2daZH9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
+LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
+aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
+VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
+IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
+N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
+KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
+kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
+CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
+Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
+imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
+2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
+DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
+/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
+F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
+TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
+c3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
+MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
+emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
+DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
+FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMg
+UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
+YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
+MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDM
+HO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtK
+qsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwID
+AQABMA0GCSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwj
+cSGIL4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y
+cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckztImRP
+T8qAkbYp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
+LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
+aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
+VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
+IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1
+GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ
++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd
+U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm
+NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
+ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/
+ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1
+CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq
+g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
+fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c
+2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/
+bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
+VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0
+MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNV
+BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2Vy
+dmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJ
+ADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII
+0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphI
+uR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZI
+hvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3
+YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc
+1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDzTCCAzagAwIBAgIQU2GyYK7bcY6nlLMTM/QHCTANBgkqhkiG9w0BAQUFADCB
+wTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTwwOgYDVQQL
+EzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1
+dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmswHhcNMDAwOTI2MDAwMDAwWhcNMTAwOTI1MjM1OTU5WjCBpTEXMBUGA1UEChMO
+VmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsx
+OzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5j
+b20vcnBhIChjKTAwMSwwKgYDVQQDEyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIEF1
+dGhvcml0eSBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0hmdZ8IAIVli
+zrQJIkRpivglWtvtDbc2fk7gu5Q+kCWHwmFHKdm9VLhjzCx9abQzNvQ3B5rB3UBU
+/OB4naCTuQk9I1F/RMIUdNsKvsvJMDRAmD7Q1yUQgZS9B0+c1lQn3y6ov8uQjI11
+S7zi6ESHzeZBCiVu6PQkAsVSD27smHUCAwEAAaOB3zCB3DAPBgNVHRMECDAGAQH/
+AgEAMEUGA1UdIAQ+MDwwOgYMYIZIAYb4RQEHFwEDMCowKAYIKwYBBQUHAgEWHGh0
+dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9ycGEwMQYDVR0fBCowKDAmoCSgIoYgaHR0
+cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwCwYDVR0PBAQDAgEGMEIGCCsG
+AQUFBwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwDQYJKoZIhvcNAQEFBQADgYEAgnBold+2DcIBcBlK0lRW
+HqzyRUyHuPU163hLBanInTsZIS5wNEqi9YngFXVF5yg3ADQnKeg3S/LvRJdrF1Ea
+w1adPBqK9kpGRjeM+sv1ZFo4aC4cw+9wzrhGBha/937ntag+RaypJXUie28/sJyU
+58dzq6wf7iWbwBbtt8pb8BQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr
+MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl
+cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
+bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw
+CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h
+dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l
+cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h
+2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E
+lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV
+ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq
+299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t
+vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL
+dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF
+AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
+zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3
+LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd
+7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw
+++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
+398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgICAx4wDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMCVVMx
+DTALBgNVBAoTBFZJU0ExLzAtBgNVBAsTJlZpc2EgSW50ZXJuYXRpb25hbCBTZXJ2
+aWNlIEFzc29jaWF0aW9uMRIwEAYDVQQDEwlHUCBSb290IDIwHhcNMDAwODE2MjI1
+MTAwWhcNMjAwODE1MjM1OTAwWjBhMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklT
+QTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRp
+b24xEjAQBgNVBAMTCUdQIFJvb3QgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAKkBcLWqxEDwq2omYXkZAPy/mzdZDK9vZBv42pWUJGkzEXDK41Z0ohdX
+ZFwgBuHW73G3O/erwWnQSaSxBNf0V2KJXLB1LRckaeNCYOTudNargFbYiCjh+20i
+/SN8RnNPflRzHqgsVVh1t0zzWkWlAhr62p3DRcMiXvOL8WAp0sdftAw6UYPvMPjU
+58fy+pmjIlC++QU3o63tmsPm7IgbthknGziLgE3sucfFicv8GjLtI/C1AVj59o/g
+halMCXI5Etuz9c9OYmTaxhkVOmMd6RdVoUwiPDQyRvhlV7or7zaMavrZ2UT0qt2E
+1w0cslSsMoW0ZA3eQbuxNMYBhjJk1Z8CAwEAAaNCMEAwHQYDVR0OBBYEFJ59SzS/
+ca3CBfYDdYDOqU8axCRMMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
+MA0GCSqGSIb3DQEBBQUAA4IBAQAhpXYUVfmtJ3CPPPTVbMjMCqujmAuKBiPFyWHb
+mQdpNSYx/scuhMKZYdQN6X0uEyt8joW2hcdLzzW2LEc9zikv2G+fiRxkk78IvXbQ
+kIqUs38oW26sTTMs7WXcFsziza6kPWKSBpUmv9+55CCmc2rBvveURNZNbyoLaxhN
+dBA2aGpawWqn3TYpjLgwi08hPwAuVDAHOrqK5MOeyti12HvOdUVmB/RtLdh6yumJ
+ivIj2C/LbgA2T/vwLwHMD8AiZfSr4k5hLQOCfZEWtTDVFN5ex5D8ofyrEK9ca3Cn
+B+8phuiyJccg/ybdd+95RBTEvd07xQObdyPsoOy7Wjm1zK0G
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIETzCCAzegAwIBAgIEO63vKTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIzMTQxODE3WhcNMTEw
+OTIzMTMxODE3WjB1MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWdu
+ZXQgLSBDQSBLbGFzYSAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4SRW9Q58g5DY1Hw7h
+gCRKBEdPdGn0MFHsfw7rlu/oQm7IChI/uWd9q5wwo77YojtTDjRnpgZsjqBeynX8T90vFILqsY2K
+5CF1OESalwvVr3sZiQX79lisuFKat92u6hBFikFIVxfHHB67Af+g7u0dEHdDW7lwy81MwFYxBTRy
+9wIDAQABo4IBbTCCAWkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwggEEBgNVHSAE
+gfwwgfkwgfYGDSsGAQQBvj8CAQoBAQAwgeQwgZoGCCsGAQUFBwICMIGNGoGKQ2VydHlmaWthdCB3
+eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtOiAiUG9saXR5a2EgQ2VydHlmaWthY2ppIGRs
+YSBSb290Q0EiLiBDZXJ0eWZpa2F0IHd5c3Rhd2lvbnkgcHJ6ZXogUm9vdENBIHcgaGllcmFyY2hp
+aSBDQyBTaWduZXQuMEUGCCsGAQUFBwIBFjlodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0b3Jp
+dW0vZG9rdW1lbnR5L3BjX3Jvb3RjYS50eHQwHwYDVR0jBBgwFoAUwJvFIw0C4aZOSGsfAOnjmhQb
+sa8wHQYDVR0OBBYEFMODHtVZd1T7TftXR/nEI1zR54njMA0GCSqGSIb3DQEBBQUAA4IBAQBRIHQB
+FIGh8Jpxt87AgSLwIEEk4+oGy769u3NtoaR0R3WNMdmt7fXTi0tyTQ9V4AIszxVjhnUPaKnF1KYy
+f8Tl+YTzk9ZfFkZ3kCdSaILZAOIrmqWNLPmjUQ5/JiMGho0e1YmWUcMci84+pIisTsytFzVP32/W
++sz2H4FQAvOIMmxB7EJX9AdbnXn9EXZ+4nCqi0ft5z96ZqOJJiCB3vSaoYg+wdkcvb6souMJzuc2
+uptXtR1Xf3ihlHaGW+hmnpcwFA6AoNrom6Vgzk6U1ienx0Cw28BhRSKqzKkyXkuK8gRflZUx84uf
+tXncwKJrMiE3lvgOOBITRzcahirLer4c
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE9zCCA9+gAwIBAgIEPL/xoTANBgkqhkiG9w0BAQUFADB2MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MSAwHgYDVQQDExdDQyBTaWduZXQgLSBQQ0EgS2xhc2EgMjAeFw0wMjA0MTkxMDI5NTNa
+Fw0xNzA0MTgxMjUzMDdaMHUxCzAJBgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4g
+eiBvLm8uMSQwIgYDVQQLExtDZW50cnVtIENlcnR5ZmlrYWNqaSBTaWduZXQxHzAdBgNVBAMTFkND
+IFNpZ25ldCAtIENBIEtsYXNhIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqgLJu
+QqY4yavbSgHg8CyfKTx4BokNSDOVz4eD9vptUr11Kqd06ED1hlH7Sg0goBFAfntNU/QTKwSBaNui
+me7C4sSEdgsKrPoAhGb4Mq8y7Ty7RqZz7mkzNMqzL2L2U4yQ2QjvpH8MH0IBqOWEcpSkpwnrCDIm
+RoTfd+YlZWKi2JceQixUUYIQ45Ox8+x8hHbvvZdgqtcvo8PW27qoHkp/7hMuJ44kDAGrmxffBXl/
+OBRZp0uO1CSLcMcVJzyr2phKhy406MYdWrtNPEluGs0GFDzd0nrIctiWAO4cmct4S72S9Q6e//0G
+O9f3/Ca5Kb2I1xYLj/xE+HgjHX9aD2MhAgMBAAGjggGMMIIBiDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjCB4wYDVR0gBIHbMIHYMIHVBg0rBgEEAb4/AhQKAQEAMIHDMHUGCCsGAQUF
+BwICMGkaZ0NlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBQQ0EyIC0gQ2VydHlmaWthdHkgVXJ6ZWRvdyBLbGFzeSAyIi4wSgYI
+KwYBBQUHAgEWPmh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9kb2t1bWVudHkva2xh
+c2EyL3BjX3BjYTIudHh0MD8GA1UdHwQ4MDYwNKAyoDCGLmh0dHA6Ly93d3cuc2lnbmV0LnBsL3Jl
+cG96eXRvcml1bS9jcmwvcGNhMi5jcmwwHwYDVR0jBBgwFoAUwGxGyl2CfpYHRonE82AVXO08kMIw
+HQYDVR0OBBYEFLtFBlILy4HNKVSzvHxBTM0HDowlMA0GCSqGSIb3DQEBBQUAA4IBAQBWTsCbqXrX
+hBBev5v5cIuc6gJM8ww7oR0uMQRZoFSqvQUPWBYM2/TLI/f8UM9hSShUVj3zEsSj/vFHagUVmzuV
+Xo5u0WK8iaqATSyEVBhADHrPG6wYcLKJlagge/ILA0m+SieyP2sjYD9MUB9KZIEyBKv0429UuDTw
+6P7pslxMWJBSNyQxaLIs0SRKsqZZWkc7ZYAj2apSkBMX2Is1oHA+PwkF6jQMwCao/+CndXPUzfCF
+6caa9WwW31W26MlXCvSmJgfiTPwGvm4PkPmOnmWZ3CczzhHl4q7ztHFzshJH3sZWDnrWwBFjzz5e
+Pr3WHV1wA7EY6oT4zBx+2gT9XBTB
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUzCCAzugAwIBAgIEPq+qjzANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJQTDE3MDUGA1UE
+ChMuQ1ppQyBDZW50cmFzdCBTQSB3IGltaWVuaXUgTWluaXN0cmEgR29zcG9kYXJraTEZMBcGA1UE
+AxMQQ1ppQyBDZW50cmFzdCBTQTAeFw0wMzA0MzAxMDUwNTVaFw0wODA0MjgxMDUwNTVaMGgxCzAJ
+BgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4geiBvLm8uMR8wHQYDVQQDExZDQyBT
+aWduZXQgLSBDQSBLbGFzYSAzMRcwFQYDVQQFEw5OdW1lciB3cGlzdTogNDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALVdeOM62cPH2NERFxbS5FIp/HSv3fgesdVsTUFxZbGtE+/E0RMl
+KZQJHH9emx7vRYubsi4EOLCjYsCOTFvgGRIpZzx7R7T5c0Di5XFkRU4gjBl7aHJoKb5SLzGlWdoX
+GsekVtl6keEACrizV2EafqjI8cnBWY7OxQ1ooLQp5AeFjXg+5PT0lO6TUZAubqjFbhVbxSWjqvdj
+93RGfyYE76MnNn4c2xWySD07n7uno06TC0IJe6+3WSX1h+76VsIFouWBXOoM7cxxiLjoqdBVu24+
+P8e81SukE7qEvOwDPmk9ZJFtt1nBNg8a1kaixcljrA/43XwOPz6qnJ+cIj/xywECAwEAAaOCAQow
+ggEGMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMDMGA1UdIAEB/wQpMCcwJQYEVR0g
+ADAdMBsGCCsGAQUFBwIBFg93d3cuY2VudHJhc3QucGwwgY4GA1UdIwSBhjCBg4AU2a7r85Cp1iJN
+W0Ca1LR6VG3996ShZaRjMGExCzAJBgNVBAYTAlBMMTcwNQYDVQQKEy5DWmlDIENlbnRyYXN0IFNB
+IHcgaW1pZW5pdSBNaW5pc3RyYSBHb3Nwb2RhcmtpMRkwFwYDVQQDExBDWmlDIENlbnRyYXN0IFNB
+ggQ9/0sQMB0GA1UdDgQWBBR7Y8wZkHq0zrY7nn1tFSdQ0PlJuTANBgkqhkiG9w0BAQUFAAOCAQEA
+ldt/svO5c1MU08FKgrOXCGEbEPbQxhpM0xcd6Iv3dCo6qugEgjEs9Qm5CwUNKMnFsvR27cJWUvZb
+MVcvwlwCwclOdwF6u/QRS8bC2HYErhYo9bp9yuxxzuow2A94c5fPqfVrjXy+vDouchAm6+A5Wjzv
+J8wxVFDCs+9iGACmyUWr/JGXCYiQIbQkwlkRKHHlan9ymKf1NvIej/3EpeT8fKr6ywxGuhAfqofW
+pg3WJY/RCB4lTzD8vZGNwfMFGkWhJkypad3i9w3lGmDVpsHaWtCgGfd0H7tUtWPkP+t7EjIRCD9J
+HYnTR+wbbewc5vOI+UobR15ynGfFIaSIiMTVtQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEejCCA2KgAwIBAgIEP4vk6TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2Vu
+dHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBD
+QSBLbGFzYSAyMB4XDTAzMTAxNDExNTgyMloXDTE3MDQxODEyNTMwN1owdzELMAkG
+A1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6IG8uby4xJDAiBgNV
+BAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEhMB8GA1UEAxMYQ0MgU2ln
+bmV0IC0gT0NTUCBLbGFzYSAyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo
+VCsaBStblXQYVNthe3dvaCrfvKpPXngh4almm988iIlEv9CVTaAdCfaJNihvA+Vs
+Qw8++ix1VqteMQE474/MV/YaXigP0Zr0QB+g+/7PWVlv+5U9Gzp9+Xx4DJay8AoI
+iB7Iy5Qf9iZiHm5BiPRIuUXT4ZRbZRYPh0/76vgRsQIDAQABo4IBkjCCAY4wDgYD
+VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEEGA1UdHwQ6MDgwNqA0
+oDKGMGh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9jcmwva2xhc2Ey
+LmNybDCB2AYDVR0gBIHQMIHNMIHKBg4rBgEEAb4/AoFICgwBADCBtzBsBggrBgEF
+BQcCAjBgGl5DZXJ0eWZpa2F0IHd5ZGFueSB6Z29kbmllIHogZG9rdW1lbnRlbSAi
+UG9saXR5a2EgQ2VydHlmaWthY2ppIC0gQ2VydHlmaWthdHkgcmVzcG9uZGVyb3cg
+T0NTUCIuMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0
+b3JpdW0vZG9rdW1lbnR5L3BjX29jc3BfMV8wLnBkZjAfBgNVHSMEGDAWgBS7RQZS
+C8uBzSlUs7x8QUzNBw6MJTAdBgNVHQ4EFgQUKEVrOY7cEHvsVgvoyZdytlbtgwEw
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAQrRg5MV6dxr0HU2IsLInxhvt
+iUVmSFkIUsBCjzLoewOXA16d2oDyHhI/eE+VgAsp+2ANjZu4xRteHIHoYMsN218M
+eD2MLRsYS0U9xxAFK9gDj/KscPbrrdoqLvtPSMhUb4adJS9HLhvUe6BicvBf3A71
+iCNe431axGNDWKnpuj2KUpj4CFHYsWCXky847YtTXDjri9NIwJJauazsrSjK+oXp
+ngRS506mdQ7vWrtApkh8zhhWp7duCkjcCo1O8JxqYr2qEW1fXmgOISe010v2mmuv
+hHxPyVwoAU4KkOw0nbXZn53yak0is5+XmAjh0wWue44AssHrjC9nUh3mkLt6eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEezCCA2OgAwIBAgIEP4vnLzANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEfMB0GA1UEAxMWQ0Mg
+U2lnbmV0IC0gQ0EgS2xhc2EgMzEXMBUGA1UEBRMOTnVtZXIgd3Bpc3U6IDQwHhcN
+MDMxMDE0MTIwODAwWhcNMDgwNDI4MTA1MDU1WjB3MQswCQYDVQQGEwJQTDEfMB0G
+A1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBD
+ZXJ0eWZpa2FjamkgU2lnbmV0MSEwHwYDVQQDExhDQyBTaWduZXQgLSBPQ1NQIEts
+YXNhIDMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM/9GwvARNuCVN+PqZmO
+4FqH8vTqhenUyqRkmAVT4YhLu0a9AXeLAYVDu+NTkYzsAUMAfu55rIKHNLlm6WbF
+KvLiKKz4p4pbUr+ToPcwl/TDotidloUdBAxDg0SL+PmQqACZDe3seJho2IYf2vDL
+/G4TLMbKmNB0mlWFuN0f4fJNAgMBAAGjggGgMIIBnDAOBgNVHQ8BAf8EBAMCB4Aw
+EwYDVR0lBAwwCgYIKwYBBQUHAwkwTwYDVR0fBEgwRjBEoEKgQIY+aHR0cDovL3d3
+dy5zaWduZXQucGwva3dhbGlmaWtvd2FuZS9yZXBvenl0b3JpdW0vY3JsL2tsYXNh
+My5jcmwwgdgGA1UdIASB0DCBzTCBygYOKwYBBAG+PwKCLAoCAQAwgbcwbAYIKwYB
+BQUHAgIwYBpeQ2VydHlmaWthdCB3eWRhbnkgemdvZG5pZSB6IGRva3VtZW50ZW0g
+IlBvbGl0eWthIENlcnR5ZmlrYWNqaSAtIENlcnR5ZmlrYXR5IHJlc3BvbmRlcm93
+IE9DU1AiLjBHBggrBgEFBQcCARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5
+dG9yaXVtL2Rva3VtZW50eS9wY19vY3NwXzFfMC5wZGYwHwYDVR0jBBgwFoAUe2PM
+GZB6tM62O559bRUnUND5SbkwHQYDVR0OBBYEFG4jnCMvBALRQXtmDn9TyXQ/EKP+
+MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBACXrKG5Def5lpRwmZom3UEDq
+bl7y4U3qomG4B+ok2FVZGgPZti+ZgvrenPj7PtbYCUBPsCSTNrznKinoT3gD9lQQ
+xkEHwdc6VD1GlFp+qI64u0+wS9Epatrdf7aBnizrOIB4LJd4E2TWQ6trspetjMIU
+upyWls1BmYUxB91R7QkTiAUSNZ87s3auhZuG4f0V0JLVCcg2rn7AN1rfMkgxCbHk
+GxiQbYWFljl6aatxR3odnnzVUe1I8uoY2JXpmmUcOG4dNGuQYziyKG3mtXCQWvug
+5qi9Mf3KUh1oSTKx6HfLjjNl1+wMB5Mdb8LF0XyZLdJM9yIZh7SBRsYm9QiXevY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPL7eEDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwNDE4MTQ1NDA4WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7BrBlbN5ma
+M5eg0BOTqoZ+9NBDvU8Lm5rTdrMswFTCathzpVVLK/JD4K3+4oCZ9SRAspEXE4gvwb08ASY6w5s+
+HpRkeJw8YzMFR5kDZD5adgnCAy4vDfIXYZgppXPaTQ8wnfUZ7BZ7Zfa7QBemUIcJIzJBB0UqgtxW
+Ceol9IekpBRVmuuSA6QG0Jkm+pGDJ05yj2eQG8jTcBENM7sVA8rGRMyFA4skSZ+D0OG6FS2xC1i9
+JyN0ag1yII/LPx8HK5J4W9MaPRNjAEeaa2qI9EpchwrOxnyVbQfSedCG1VRJfAsE/9tT9CMUPZ3x
+W20QjQcSZJqVcmGW9gVsXKQOVLsCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQEBMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUwGxGyl2CfpYHRonE
+82AVXO08kMIwDQYJKoZIhvcNAQEFBQADggEBABp1TAUsa+BeVWg4cjowc8yTJ5XN3GvN96GObMkx
+UGY7U9kVrLI71xBgoNVyzXTiMNDBvjh7vdPWjpl5SDiRpnnKiOFXA43HvNWzUaOkTu1mxjJsZsan
+ot1Xt6j0ZDC+03FjLHdYMyM9kSWp6afb4980EPYZCcSzgM5TOGfJmNii5Tq468VFKrX+52Aou1G2
+2Ohu+EEOlOrG7ylKv1hHUJJCjwN0ZVEIn1nDbrU9FeGCz8J9ihVUvnENEBbBkU37PWqWuHitKQDV
+tcwTwJJdR8cmKq3NmkwAm9fPacidQLpaw0WkuGrS+fEDhu1Nhy9xELP6NA9GRTCNxm/dXlcwnmY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPV0tNDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwODE2MTY0OTU2WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALN3LanJtdue
+Ne6geWUTFENa+lEuzqELcoqhYB+a/tJcPEkc6TX/bYPzalRRjqs+quMP6KZTU0DixOrV+K7iWaqA
+iQ913HX5IBLmKDCrTVW/ZvSDpiBKbxlHfSNuJxAuVT6HdbzK7yAW38ssX+yS2tZYHZ5FhZcfqzPE
+OpO94mAKcBUhk6T/ki0evXX/ZvvktwmF3hKattzwtM4JMLurAEl8SInyEYULw5JdlfcBez2Tg6Db
+w34hA1A+ckTwhxzecrB8TUe2BnQKOs9vr2cCACpFFcOmPkM0Drtjctr1QHm1tYSqRFRf9VcV5tfC
+3P8QqoK4ONjtLPHc9x5NE1uK/FMCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQECMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUXvthcPHlH5BgGhlM
+ErJNXWlhlgAwDQYJKoZIhvcNAQEFBQADggEBACIce95Mvn710KCAISA0CuHD4aznTU6pLoCDShW4
+7OR+GTpJUm1coTcUqlBHV9mra4VFrBcBuOkHZoBLq/jmE0QJWnpSEULDcH9J3mF0nqO9SM+mWyJG
+dsJF/XU/7smummgjMNQXwzQTtWORF+6v5KUbWX85anO2wR+M6YTBWC55zWpWi4RG3vkHFs5Ze2oF
+JTlpuxw9ZgxTnWlwI9QR2MvEhYIUMKMOWxw1nt0kKj+5TCNQQGh/VJJ1dsiroGh/io1DOcePEhKz
+1Ag52y6Wf0nJJB9yk0sFakqZH18F7eQecQImgZyyeRtsG95leNugB3BXWCW+KxwiBrtQTXv4dTE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEzzCCA7egAwIBAgIEO6ocGTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIwMTY0MjE5WhcNMjYw
+OTIxMTU0MjE5WjBxMQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MRswGQYDVQQDExJDQyBTaWdu
+ZXQgLSBSb290Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrr2vydnNpELfGW3Ks
+ARiDhJvwDtUe4AbWev+OfMc3+vA29nX8ZmIwno3gmItjo5DbUCCRiCMq5c9epcGu+kg4a3BJChVX
+REl8gVh0ST15rr3RKrSc4VgsvQzl0ZUraeQLl8JoRT5PLsUj3qwF78jUCQVckiiLVcnGfZtFCm+D
+CJXliQBDMB9XFAUEiO/DtEBs0B7wJGx7lgJeJpQUcGiaOPjcJDYOk7rNAYmmD2gWeSlepufO8luU
+YG/YDxTC4mqhRqfa4MnVO5dqy+ICj2UvUpHbZDB0KfGRibgBYeQP1kuqgIzJN4UqknVAJb0aMBSP
+l+9k2fAUdchx1njlbdcbAgMBAAGjggFtMIIBaTAPBgNVHRMBAf8EBTADAQH/MIIBBAYDVR0gBIH8
+MIH5MIH2Bg0rBgEEAb4/AgEKAQEAMIHkMIGaBggrBgEFBQcCAjCBjRqBikNlcnR5ZmlrYXQgd3lz
+dGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0eWthIENlcnR5ZmlrYWNqaSBkbGEg
+Um9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6IFJvb3RDQSB3IGhpZXJhcmNoaWkg
+Q0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVt
+L2Rva3VtZW50eS9wY19yb290Y2EudHh0MB0GA1UdDgQWBBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAf
+BgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN
+AQEFBQADggEBAGnY5QmYqnnO9OqFOWZxxb25UHRnaRF6IV9aaGit5BZufZj2Tq3v8L3SgE34GOoI
+cdRMMG5JEpEU4mN/Ef3oY6Eo+7HfqaPHI4KFmbDSPiK5s+wmf+bQSm0Yq5/h4ZOdcAESlLQeLSt1
+CQk2JoKQJ6pyAf6xJBgWEIlm4RXE4J3324PUiOp83kW6MDvaa1xY976WyInr4rwoLgxVl11LZeKW
+ha0RJJxJgw/NyWpKG7LWCm1fglF8JH51vZNndGYq1iKtfnrIOvLZq6bzaCiZm1EurD8HE6P7pmAB
+KK6o3C2OXlNfNIgwkDN/cDqk5TYsTkrpfriJPdxXBH8hQOkW89g=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/TCCA2agAwIBAgIEP4/gkTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBDQSBLbGFzYSAxMB4XDTAzMTAxNzEyMjkwMloX
+DTExMDkyMzExMTgxN1owdjELMAkGA1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6
+IG8uby4xJDAiBgNVBAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEgMB4GA1UEAxMXQ0Mg
+U2lnbmV0IC0gVFNBIEtsYXNhIDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOJYrISEtSsd
+uHajROh5/n7NGrkpYTT9NEaPe9+ucuQ37KxIbfJwXJjgUc1dw4wCkcQ12FJarD1X6mSQ4cfN/60v
+LfKI5ZD4nhJTMKlAj1pX9ScQ/MuyvKStCbn5WTkjPhjRAM0tdwXSnzuTEunfw0Oup559y3Iqxg1c
+ExflB6cfAgMBAAGjggGXMIIBkzBBBgNVHR8EOjA4MDagNKAyhjBodHRwOi8vd3d3LnNpZ25ldC5w
+bC9yZXBvenl0b3JpdW0vY3JsL2tsYXNhMS5jcmwwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM
+MAoGCCsGAQUFBwMIMIHaBgNVHSAEgdIwgc8wgcwGDSsGAQQBvj8CZAoRAgEwgbowbwYIKwYBBQUH
+AgIwYxphQ2VydHlmaWthdCB3eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtICJQb2xpdHlr
+YSBDZXJ0eWZpa2FjamkgQ0MgU2lnbmV0IC0gWm5ha293YW5pZSBjemFzZW0iLjBHBggrBgEFBQcC
+ARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY190c2ExXzJf
+MS5wZGYwHwYDVR0jBBgwFoAUw4Me1Vl3VPtN+1dH+cQjXNHnieMwHQYDVR0OBBYEFJdDwEqtcavO
+Yd9u9tej53vWXwNBMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADgYEAnpiQkqLCJQYXUrqMHUEz
++z3rOqS0XzSFnVVLhkVssvXc8S3FkJIiQTUrkScjI4CToCzujj3EyfNxH6yiLlMbskF8I31JxIeB
+vueqV+s+o76CZm3ycu9hb0I4lswuxoT+q5ZzPR8Irrb51rZXlolR+7KtwMg4sFDJZ8RNgOf7tbA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
+MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
+ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
+b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
+MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
+ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
+IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
+AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
+unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
+BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
+7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
+0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
+roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
+A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
+aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
+26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
+BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
+EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
+BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
+aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
+AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
+p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
+1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
+XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
+eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
+tGWaIZDgqtCYvDi1czyL+Nw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn
+MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
+ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo
+YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9
+MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy
+NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G
+A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA
+A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0
+Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s
+QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV
+eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795
+B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh
+z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T
+AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i
+ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w
+TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH
+MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD
+VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE
+VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
+bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B
+AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM
+bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi
+ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG
+VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c
+ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/
+AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
+PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
+cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
+MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
+IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
+ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
+VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
+kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
+EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
+H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
+HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
+DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
+QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
+Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
+AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
+yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
+FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
+ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
+kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
+l7+ijrRU
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
+YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
+RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
+UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
+2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
+Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
+nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
+/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
+PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
+QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
+SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
+IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
+zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
+BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
+ZQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
+b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
+cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
+JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
+mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
+VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
+AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
+AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
+pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
+dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
+fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
+NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
+H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
+PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
+xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
+Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
+hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
+EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
+FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
+nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
+eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
+hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
+Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
++OkuE6N36B9K
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb
+MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx
+ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w
+MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD
+VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx
+FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu
+ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7
+gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH
+fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a
+ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT
+ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk
+c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto
+dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt
+aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI
+hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk
+QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/
+h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
+nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR
+rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2
+9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
+PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
+Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
+rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
+OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
+xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
+7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
+aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
+SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
+ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
+AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
+R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
+JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
+Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
+NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
+NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
+ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
+BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
+Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
+4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
+KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
+rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
+94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
+sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
+gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
+kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
+vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
+O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
+AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
+9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
+eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
+0vdXcDazv/wor3ElhVsT/h5/WrQ8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMx
+IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1
+dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w
+HhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTELMAkGA1UEBhMCRVMx
+IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1
+dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5u
+Cp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5Vj1H5WuretXDE7aTt/6MNbg9kUDGvASdY
+rv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJHlShbz++AbOCQl4oBPB3z
+hxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf3H5idPay
+BQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcL
+iam8NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcb
+AgMBAAGjgZ8wgZwwKgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lv
+bmFsLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0
+MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
+FgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQADggEBAEdz/o0n
+VPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
+u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36m
+hoEyIwOdyPdfwUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzfl
+ZKG+TQyTmAyX9odtsz/ny4Cm7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBp
+QWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YGVM+h4k0460tQtcsm9MracEpqoeJ5
+quGnM/b9Sh/22WA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
+MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
+IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
+PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
+Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
+TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
+5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
+S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
+2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
+EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
+EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
+/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
+A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
+abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
+I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
+4iIprn2DQKi6bA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
+R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
+MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
+AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
+ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
+7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
+kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
+mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
+KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
+6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
+4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
+oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
+UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
+AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
+MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
+c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
+VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
+c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
+WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
+FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
+XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
+se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
+KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
+IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
+y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
+hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
+QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
+Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
+HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
+KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
+dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
+L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
+Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
+ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
+T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
+GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
+1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
+OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
+6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
+QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
+MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
+c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
+BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
+IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
+VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
+cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
+QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
+F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
+c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
+mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
+VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
+teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
+f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
+Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
+nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
+/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
+MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
+aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
+IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
+ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
+uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
+Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
+QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
+koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
+ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
+DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
+bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
+MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
+v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
+eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
+tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
+C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
+zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
+mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
+V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
+bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
+3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
+J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
+291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
+ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
+AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
+MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
+YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
+MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
+ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
+MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
+ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
+PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
+wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
+EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
+avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
+sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
+/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
+IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
+OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
+TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
+dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
+ReYNnyicsbkqWletNw+vHX/bvZ8=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx
+ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
+b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD
+EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05
+OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G
+A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
+Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l
+dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK
+gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX
+iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc
+Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E
+BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G
+SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu
+b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh
+bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv
+Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln
+aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0
+IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
+c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph
+biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo
+ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP
+UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj
+YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo
+dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA
+bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06
+sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa
+n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS
+NitjrFgBazMpUIaD8QFI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx
+ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
+b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD
+EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X
+DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw
+DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u
+c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr
+TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA
+OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC
+2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW
+RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P
+AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW
+ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0
+YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz
+b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO
+ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB
+IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs
+b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
+ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s
+YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg
+a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g
+SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0
+aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg
+YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg
+Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY
+ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g
+pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4
+Fp1hBWeAyNDYpQcCNJgEjTME1A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV
+MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe
+TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0
+dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB
+KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0
+N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC
+dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu
+MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL
+b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD
+zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi
+3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8
+WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY
+Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi
+NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC
+ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4
+QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0
+YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz
+aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
+IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm
+ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg
+ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs
+amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv
+IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3
+Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6
+ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1
+YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg
+dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs
+b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G
+CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO
+xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP
+0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ
+QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk
+f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK
+8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx
+ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
+b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD
+EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz
+aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w
+MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G
+A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
+Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l
+dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh
+bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq
+eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe
+r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5
+3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd
+vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l
+mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC
+wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg
+hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0
+TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh
+biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg
+ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg
+dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6
+b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl
+c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0
+ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3
+dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu
+ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh
+bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo
+ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3
+Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u
+ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA
+A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ
+MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+
+NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR
+VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY
+83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3
+macqaJVmlaut74nLYKkGEsaUR+ko
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
+YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
+GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
+Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
+WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
+rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
+ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
+Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
+PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
+/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
+oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
+yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
+EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
+A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
+MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
+BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
+g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
+fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
+WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
+B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
+hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
+TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
+mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
+ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
+4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
+8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
+YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
+V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
+4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
+H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
+8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
+vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
+mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
+btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
+T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
+WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
+c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
+4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
+VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
+CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
+aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
+dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
+czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
+A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
+Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
+7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
+d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
+4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
+t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
+DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
+k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
+zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
+Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
+mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
+4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
+MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
+GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
+MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
+Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
+iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
+/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
+jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
+HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
+sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
+gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
+KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
+AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
+URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
+H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
+I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
+iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
+f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
+MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
+FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
+MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
+cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
+Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
+0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
+wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
+7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
+8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
+BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
+JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
+NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
+6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
+3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
+D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
+CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
+3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
+ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
+LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
+BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
+dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
+cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
+YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
+dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
+bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
+YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
+TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
+9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
+jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
+FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
+ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
+ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
+EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
+L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
+O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
+um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
+NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFFjCCBH+gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsDELMAkGA1UEBhMCSUwx
+DzANBgNVBAgTBklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0
+Q29tIEx0ZC4xGjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBG
+cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS
+YWRtaW5Ac3RhcnRjb20ub3JnMB4XDTA1MDMxNzE3Mzc0OFoXDTM1MDMxMDE3Mzc0
+OFowgbAxCzAJBgNVBAYTAklMMQ8wDQYDVQQIEwZJc3JhZWwxDjAMBgNVBAcTBUVp
+bGF0MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMRowGAYDVQQLExFDQSBBdXRob3Jp
+dHkgRGVwLjEpMCcGA1UEAxMgRnJlZSBTU0wgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkxITAfBgkqhkiG9w0BCQEWEmFkbWluQHN0YXJ0Y29tLm9yZzCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEA7YRgACOeyEpRKSfeOqE5tWmrCbIvNP1h3D3TsM+x
+18LEwrHkllbEvqoUDufMOlDIOmKdw6OsWXuO7lUaHEe+o5c5s7XvIywI6Nivcy+5
+yYPo7QAPyHWlLzRMGOh2iCNJitu27Wjaw7ViKUylS7eYtAkUEKD4/mJ2IhULpNYI
+LzUCAwEAAaOCAjwwggI4MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgHmMB0G
+A1UdDgQWBBQcicOWzL3+MtUNjIExtpidjShkjTCB3QYDVR0jBIHVMIHSgBQcicOW
+zL3+MtUNjIExtpidjShkjaGBtqSBszCBsDELMAkGA1UEBhMCSUwxDzANBgNVBAgT
+BklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4x
+GjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBGcmVlIFNTTCBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSYWRtaW5Ac3Rh
+cnRjb20ub3JnggEAMB0GA1UdEQQWMBSBEmFkbWluQHN0YXJ0Y29tLm9yZzAdBgNV
+HRIEFjAUgRJhZG1pbkBzdGFydGNvbS5vcmcwEQYJYIZIAYb4QgEBBAQDAgAHMC8G
+CWCGSAGG+EIBDQQiFiBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAy
+BglghkgBhvhCAQQEJRYjaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL2NhLWNybC5j
+cmwwKAYJYIZIAYb4QgECBBsWGWh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy8wOQYJ
+YIZIAYb4QgEIBCwWKmh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9pbmRleC5waHA/
+YXBwPTExMTANBgkqhkiG9w0BAQQFAAOBgQBscSXhnjSRIe/bbL0BCFaPiNhBOlP1
+ct8nV0t2hPdopP7rPwl+KLhX6h/BquL/lp9JmeaylXOWxkjHXo0Hclb4g4+fd68p
+00UOpO6wNnQt8M2YI3s3S9r+UZjEHjQ8iP2ZO1CnwYszx8JSFhKVU2Ui77qLzmLb
+cCOxgN8aIDjnfg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk
+MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
+YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
+Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT
+AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
+Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9
+m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih
+FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/
+TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F
+EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco
+kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu
+HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF
+vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo
+19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC
+L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW
+bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX
+JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
+FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
+BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc
+K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf
+ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik
+Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB
+sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e
+3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR
+ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip
+mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH
+b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf
+rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms
+hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y
+zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6
+MBr1mmz0DlP5OlvRHA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
+biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
+MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
+d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
+76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
+bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
+6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
+emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
+MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
+MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
+MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
+FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
+aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
+gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
+qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
+lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
+8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
+L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
+45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
+UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
+O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
+bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
+GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
+77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
+hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
+92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
+Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
+ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
+Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
+BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu
+IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw
+WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD
+ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y
+IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn
+IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+
+6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob
+jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw
+izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl
++zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY
+zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP
+pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF
+KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW
+ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB
+AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
+BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0
+ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW
+IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA
+A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0
+uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+
+FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7
+jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/
+u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D
+YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1
+puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa
+icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG
+DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x
+kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z
+Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
+BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
+IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
+RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
+U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
+Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
+YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
+nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
+6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
+eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
+c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
+MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
+HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
+jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
+5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
+rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
+F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
+wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
+cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
+AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
+WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
+xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
+2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
+IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
+aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
+em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
+dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
+OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
+tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
+MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
+PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
+IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
+gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
+yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
+F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
+jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
+ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
+VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
+YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
+EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
+Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
+DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
+MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
+UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
+TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
+qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
+ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
+JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
+hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
+EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
+nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
+udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
+ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
+LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
+pYYsfPQS
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
+NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
+LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
+A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
+W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
+3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
+6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
+Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
+NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
+r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
+DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
+YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
+xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
+/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
+LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
+jVaMaA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc
+UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
+c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg
+MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8
+dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz
+MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy
+dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD
+VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg
+xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu
+xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7
+XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k
+heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J
+YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C
+urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1
+JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51
+b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV
+9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7
+kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh
+fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
+B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA
+aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS
+RGQDJereW26fyfJOrN3H
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc
+UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
+c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS
+S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
+SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3
+WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv
+bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU
+UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw
+bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe
+LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef
+J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh
+R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ
+Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX
+JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p
+zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S
+Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq
+ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
+Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz
+gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH
+uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS
+y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
+biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
+U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
+nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
+t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
+SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
+BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
+rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
+NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
+BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
+aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
+MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
+p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
+5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
+WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
+4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
+hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9v
+dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDAxMDExMTY0MTI4WhcNMjEwMTE0
+MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSww
+KgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0G
+A1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n13
+5zHCLielTWi5MbqNQ1mXx3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHE
+SxP9cMIlrCL1dQu3U+SlK93OvRw6esP3E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4O
+JgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5OEL8pahbSCOz6+MlsoCu
+ltQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4jsNtlAHCE
+AQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMB
+AAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcB
+CzAyMDAGCCsGAQUFBwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRw
+b2xpY3kwDQYJKoZIhvcNAQEFBQADggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo
+7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrvm+0fazbuSCUlFLZWohDo7qd/
+0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0ROhPs7fpvcmR7
+nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx
+x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ
+33ZwmVxwQ023tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
+gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
+MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
+UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
+NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
+dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
+dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
+38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
+KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
+DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
+qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
+JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
+PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
+BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
+jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
+eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
+ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
+vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
+qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
+IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
+i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
+O+7ETPTsJ3xCwnR8gooJybQDJbw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
+MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
+IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
+IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
+RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
+U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
+IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
+ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
+QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
+rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
+NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
+QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
+txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
+BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
+AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
+tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
+IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
+6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
+Cm26OWMohpLzGITY+9HPBVZkVw==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBvjELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UE
+ChMfU29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9z
+dG1hc3RlcjEgMB4GA1UEAxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkq
+hkiG9w0BCQEWFmhvc3RtYXN0ZXJAc3BpLWluYy5vcmcwHhcNMDMwMTE1MTYyOTE3
+WhcNMDcwMTE0MTYyOTE3WjCBvjELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0luZGlh
+bmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdhcmUgaW4g
+dGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1hc3RlcjEgMB4GA1UE
+AxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkqhkiG9w0BCQEWFmhvc3Rt
+YXN0ZXJAc3BpLWluYy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPB6
+rdoiLR3RodtM22LMcfwfqb5OrJNl7fwmvskgF7yP6sdD2bOfDIXhg9852jhY8/kL
+VOFe1ELAL2OyN4RAxk0rliZQVgeTgqvgkOVIBbNwgnjN6mqtuWzFiPL+NXQExq40
+I3whM+4lEiwSHaV+MYxWanMdhc+kImT50LKfkxcdAgMBAAGjggEfMIIBGzAdBgNV
+HQ4EFgQUB63oQR1/vda/G4F6P4xLiN4E0vowgesGA1UdIwSB4zCB4IAUB63oQR1/
+vda/G4F6P4xLiN4E0vqhgcSkgcEwgb4xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdJ
+bmRpYW5hMRUwEwYDVQQHEwxJbmRpYW5hcG9saXMxKDAmBgNVBAoTH1NvZnR3YXJl
+IGluIHRoZSBQdWJsaWMgSW50ZXJlc3QxEzARBgNVBAsTCmhvc3RtYXN0ZXIxIDAe
+BgNVBAMTF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MSUwIwYJKoZIhvcNAQkBFhZo
+b3N0bWFzdGVyQHNwaS1pbmMub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAm/Abn8c2y1nO3fgpAIslxvi9iNBZDhQtJ0VQZY6wgSfANyDOR4DW
+iexO/AlorB49KnkFS7TjCAoLOZhcg5FaNiKnlstMI5krQmau1Qnb/vGSNsE/UGms
+1ts+QYPUs0KmGEAFUri2XzLy+aQo9Kw74VBvqnxvaaMeY5yMcKNOieY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIIDjCCBfagAwIBAgIJAOiOtsn4KhQoMA0GCSqGSIb3DQEBBQUAMIG8MQswCQYD
+VQQGEwJVUzEQMA4GA1UECBMHSW5kaWFuYTEVMBMGA1UEBxMMSW5kaWFuYXBvbGlz
+MSgwJgYDVQQKEx9Tb2Z0d2FyZSBpbiB0aGUgUHVibGljIEludGVyZXN0MRMwEQYD
+VQQLEwpob3N0bWFzdGVyMR4wHAYDVQQDExVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkx
+JTAjBgkqhkiG9w0BCQEWFmhvc3RtYXN0ZXJAc3BpLWluYy5vcmcwHhcNMDgwNTEz
+MDgwNzU2WhcNMTgwNTExMDgwNzU2WjCBvDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdh
+cmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1hc3RlcjEe
+MBwGA1UEAxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSUwIwYJKoZIhvcNAQkBFhZo
+b3N0bWFzdGVyQHNwaS1pbmMub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEA3DbmR0LCxFF1KYdAw9iOIQbSGE7r7yC9kDyFEBOMKVuUY/b0LfEGQpG5
+GcRCaQi/izZF6igFM0lIoCdDkzWKQdh4s/Dvs24t3dHLfer0dSbTPpA67tfnLAS1
+fOH1fMVO73e9XKKTM5LOfYFIz2u1IiwIg/3T1c87Lf21SZBb9q1NE8re06adU1Fx
+Y0b4ShZcmO4tbZoWoXaQ4mBDmdaJ1mwuepiyCwMs43pPx93jzONKao15Uvr0wa8u
+jyoIyxspgpJyQ7zOiKmqp4pRQ1WFmjcDeJPI8L20QcgHQprLNZd6ioFl3h1UCAHx
+ZFy3FxpRvB7DWYd2GBaY7r/2Z4GLBjXFS21ZGcfSxki+bhQog0oQnBv1b7ypjvVp
+/rLBVcznFMn5WxRTUQfqzj3kTygfPGEJ1zPSbqdu1McTCW9rXRTunYkbpWry9vjQ
+co7qch8vNGopCsUK7BxAhRL3pqXTT63AhYxMfHMgzFMY8bJYTAH1v+pk1Vw5xc5s
+zFNaVrpBDyXfa1C2x4qgvQLCxTtVpbJkIoRRKFauMe5e+wsWTUYFkYBE7axt8Feo
++uthSKDLG7Mfjs3FIXcDhB78rKNDCGOM7fkn77SwXWfWT+3Qiz5dW8mRvZYChD3F
+TbxCP3T9PF2sXEg2XocxLxhsxGjuoYvJWdAY4wCAs1QnLpnwFVMCAwEAAaOCAg8w
+ggILMB0GA1UdDgQWBBQ0cdE41xU2g0dr1zdkQjuOjVKdqzCB8QYDVR0jBIHpMIHm
+gBQ0cdE41xU2g0dr1zdkQjuOjVKdq6GBwqSBvzCBvDELMAkGA1UEBhMCVVMxEDAO
+BgNVBAgTB0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMf
+U29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1h
+c3RlcjEeMBwGA1UEAxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSUwIwYJKoZIhvcN
+AQkBFhZob3N0bWFzdGVyQHNwaS1pbmMub3JnggkA6I62yfgqFCgwDwYDVR0TAQH/
+BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAAcwCQYDVR0SBAIwADAuBglghkgBhvhC
+AQ0EIRYfU29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDAwBglghkgBhvhC
+AQQEIxYhaHR0cHM6Ly9jYS5zcGktaW5jLm9yZy9jYS1jcmwucGVtMDIGCWCGSAGG
++EIBAwQlFiNodHRwczovL2NhLnNwaS1pbmMub3JnL2NlcnQtY3JsLnBlbTAhBgNV
+HREEGjAYgRZob3N0bWFzdGVyQHNwaS1pbmMub3JnMA4GA1UdDwEB/wQEAwIBBjAN
+BgkqhkiG9w0BAQUFAAOCAgEAtM294LnqsgMrfjLp3nI/yUuCXp3ir1UJogxU6M8Y
+PCggHam7AwIvUjki+RfPrWeQswN/2BXja367m1YBrzXU2rnHZxeb1NUON7MgQS4M
+AcRb+WU+wmHo0vBqlXDDxm/VNaSsWXLhid+hoJ0kvSl56WEq2dMeyUakCHhBknIP
+qxR17QnwovBc78MKYiC3wihmrkwvLo9FYyaW8O4x5otVm6o6+YI5HYg84gd1GuEP
+sTC8cTLSOv76oYnzQyzWcsR5pxVIBcDYLXIC48s9Fmq6ybgREOJJhcyWR2AFJS7v
+dVkz9UcZFu/abF8HyKZQth3LZjQl/GaD68W2MEH4RkRiqMEMVObqTFoo5q7Gt/5/
+O5aoLu7HaD7dAD0prypjq1/uSSotxdz70cbT0ZdWUoa2lOvUYFG3/B6bzAKb1B+P
++UqPti4oOxfMxaYF49LTtcYDyeFIQpvLP+QX4P4NAZUJurgNceQJcHdC2E3hQqlg
+g9cXiUPS1N2nGLar1CQlh7XU4vwuImm9rWgs/3K1mKoGnOcqarihk3bOsPN/nOHg
+T7jYhkalMwIsJWE3KpLIrIF0aGOHM3a9BX9e1dUCbb2v/ypaqknsmHlHU5H2DjRa
+yaXG67Ljxay2oHA1u8hRadDytaIybrw/oDc5fHE2pgXfDBLkFqfF1stjo5VwP+YE
+o2A=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDvjCCA3ygAwIBAgIFJQaThoEwCwYHKoZIzjgEAwUAMIGFMQswCQYDVQQGEwJG
+UjEPMA0GA1UECBMGRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczEQMA4GA1UEChMHUE0v
+U0dETjEOMAwGA1UECxMFRENTU0kxDjAMBgNVBAMTBUlHQy9BMSMwIQYJKoZIhvcN
+AQkBFhRpZ2NhQHNnZG4ucG0uZ291di5mcjAeFw0wMjEyMTMxNDM5MTVaFw0yMDEw
+MTcxNDM5MTRaMIGFMQswCQYDVQQGEwJGUjEPMA0GA1UECBMGRnJhbmNlMQ4wDAYD
+VQQHEwVQYXJpczEQMA4GA1UEChMHUE0vU0dETjEOMAwGA1UECxMFRENTU0kxDjAM
+BgNVBAMTBUlHQy9BMSMwIQYJKoZIhvcNAQkBFhRpZ2NhQHNnZG4ucG0uZ291di5m
+cjCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCFkMImdk9zDzJfTO4XPdAAmLbAdWws
+ZiEMZh19RyTo3CyhFqO77OIXrwY6vc1pcc3MgWJ0dgQpAgrDMtmFFxpUu4gmjVsx
+8GpxQC+4VOgLY8Cvmcd/UDzYg07EIRto8BwCpPJ/JfUxwzV2V3N713aAX+cEoKZ/
+s+kgxC6nZCA7oQIVALME/JYjkdW2uKIGngsEPbXAjdhDAoGADh/uqWJx94UBm31c
+9d8ZTBfRGRnmSSRVFDgPWgA69JD4BR5da8tKz+1HjfMhDXljbMH86ixpD5Ka1Z0V
+pRYUPbyAoB37tsmXMJY7kjyD19d5VdaZboUjVvhH6UJy5lpNNNGSvFl4fqkxyvw+
+pq1QV0N5RcvK120hlXdfHUX+YKYDgYQAAoGAQGr7IuKJcYIvJRMjxwl43KxXY2xC
+aoCiM/bv117MfI94aNf1UusGhp7CbYAY9CXuL60P0oPMAajbaTE5Z34AuITeHq3Y
+CNMHwxalip8BHqSSGmGiQsXeK7T+r1rPXsccZ1c5ikGDZ4xn5gUaCyy2rCmb+fOJ
+6VAfCbAbAjmNKwejdzB1MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgFGMBUG
+A1UdIAQOMAwwCgYIKoF6AXkBAQEwHQYDVR0OBBYEFPkeNRcUf8idzpKblYbLNxs0
+MQhSMB8GA1UdIwQYMBaAFPkeNRcUf8idzpKblYbLNxs0MQhSMAsGByqGSM44BAMF
+AAMvADAsAhRVh+CJA5eVyEYU5AO9Tm7GxX0rmQIUBCqsU5u1WxoZ5lEXicDX5/Ob
+sRQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT
+AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ
+TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG
+9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw
+MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM
+BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO
+MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2
+LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI
+s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2
+xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4
+u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b
+F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx
+Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd
+PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV
+HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx
+NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF
+AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ
+L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY
+YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
+Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a
+NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R
+0982gaEbeC9xs/FZTEYYKKuF0mBWWg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGCDCCA/CgAwIBAgIBATANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wNTEwMTQwNzM2NTVaFw0zMzAzMjgwNzM2NTVaMFQxFDAS
+BgNVBAoTC0NBY2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5v
+cmcxHDAaBgNVBAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCrSTURSHzSJn5TlM9Dqd0o10Iqi/OHeBlYfA+e2ol9
+4fvrcpANdKGWZKufoCSZc9riVXbHF3v1BKxGuMO+f2SNEGwk82GcwPKQ+lHm9WkB
+Y8MPVuJKQs/iRIwlKKjFeQl9RrmK8+nzNCkIReQcn8uUBByBqBSzmGXEQ+xOgo0J
+0b2qW42S0OzekMV/CsLj6+YxWl50PpczWejDAz1gM7/30W9HxM3uYoNSbi4ImqTZ
+FRiRpoWSR7CuSOtttyHshRpocjWr//AQXcD0lKdq1TuSfkyQBX6TwSyLpI5idBVx
+bgtxA+qvFTia1NIFcm+M+SvrWnIl+TlG43IbPgTDZCciECqKT1inA62+tC4T7V2q
+SNfVfdQqe1z6RgRQ5MwOQluM7dvyz/yWk+DbETZUYjQ4jwxgmzuXVjit89Jbi6Bb
+6k6WuHzX1aCGcEDTkSm3ojyt9Yy7zxqSiuQ0e8DYbF/pCsLDpyCaWt8sXVJcukfV
+m+8kKHA4IC/VfynAskEDaJLM4JzMl0tF7zoQCqtwOpiVcK01seqFK6QcgCExqa5g
+eoAmSAC4AcCTY1UikTxW56/bOiXzjzFU6iaLgVn5odFTEcV7nQP2dBHgbbEsPyyG
+kZlxmqZ3izRg0RS0LKydr4wQ05/EavhvE/xzWfdmQnQeiuP43NJvmJzLR5iVQAX7
+6QIDAQABo4G/MIG8MA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUHAQEEUTBPMCMG
+CCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggrBgEFBQcwAoYc
+aHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBBMD8GCCsGAQQB
+gZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9yZy9pbmRleC5w
+aHA/aWQ9MTAwDQYJKoZIhvcNAQEEBQADggIBAH8IiKHaGlBJ2on7oQhy84r3HsQ6
+tHlbIDCxRd7CXdNlafHCXVRUPIVfuXtCkcKZ/RtRm6tGpaEQU55tiKxzbiwzpvD0
+nuB1wT6IRanhZkP+VlrRekF490DaSjrxC1uluxYG5sLnk7mFTZdPsR44Q4Dvmw2M
+77inYACHV30eRBzLI++bPJmdr7UpHEV5FpZNJ23xHGzDwlVks7wU4vOkHx4y/CcV
+Bc/dLq4+gmF78CEQGPZE6lM5+dzQmiDgxrvgu1pPxJnIB721vaLbLmINQjRBvP+L
+ivVRIqqIMADisNS8vmW61QNXeZvo3MhN+FDtkaVSKKKs+zZYPumUK5FQhxvWXtaM
+zPcPEAxSTtAWYeXlCmy/F8dyRlecmPVsYGN6b165Ti/Iubm7aoW8mA3t+T6XhDSU
+rgCvoeXnkm5OvfPi2RSLXNLrAWygF6UtEOucekq9ve7O/e0iQKtwOIj1CodqwqsF
+YMlIBdpTwd5Ed2qz8zw87YC8pjhKKSRf/lk7myV6VmMAZLldpGJ9VzZPrYPvH5JT
+oI53V93lYRE9IwCQTDz6o2CTBKOvNfYOao9PSmCnhQVsRqGP9Md246FZV/dxssRu
+FFxtbUFm3xuTsdQAw+7Lzzw9IYCpX2Nl/N3gX6T0K/CFcUHUZyX7GrGXrtaZghNB
+0m6lG5kngOcLqagA
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
+MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
+BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
+IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
+MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
+ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
+T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
+FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
+cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
+BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
+fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
+GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFijCCA3KgAwIBAgIQDHbanJEMTiye/hXQWJM8TDANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJOTDESMBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdp
+Tm90YXIgUm9vdCBDQTEgMB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmww
+HhcNMDcwNTE2MTcxOTM2WhcNMjUwMzMxMTgxOTIxWjBfMQswCQYDVQQGEwJOTDES
+MBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdpTm90YXIgUm9vdCBDQTEg
+MB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmwwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCssFjBAL3YIQgLK5r+blYwBZ8bd5AQQVzDDYcRd46B
+8cp86Yxq7Th0Nbva3/m7wAk3tJZzgX0zGpg595NvlX89ubF1h7pRSOiLcD6VBMXY
+tsMW2YiwsYcdcNqGtA8Ui3rPENF0NqISe3eGSnnme98CEWilToauNFibJBN4ViIl
+HgGLS1Fx+4LMWZZpiFpoU8W5DQI3y0u8ZkqQfioLBQftFl9VkHXYRskbg+IIvvEj
+zJkd1ioPgyAVWCeCLvriIsJJsbkBgWqdbZ1Ad2h2TiEqbYRAhU52mXyC8/O3AlnU
+JgEbjt+tUwbRrhjd4rI6y9eIOI6sWym5GdOY+RgDz0iChmYLG2kPyes4iHomGgVM
+ktck1JbyrFIto0fVUvY//s6EBnCmqj6i8rZWNBhXouSBbefK8GrTx5FrAoNBfBXv
+a5pkXuPQPOWx63tdhvvL5ndJzaNl3Pe5nLjkC1+Tz8wwGjIczhxjlaX56uF0i57p
+K6kwe6AYHw4YC+VbqdPRbB4HZ4+RS6mKvNJmqpMBiLKR+jFc1abBUggJzQpjotMi
+puih2TkGl/VujQKQjBR7P4DNG5y6xFhyI6+2Vp/GekIzKQc/gsnmHwUNzUwoNovT
+yD4cxojvXu6JZOkd69qJfjKmadHdzIif0dDJZiHcBmfFlHqabWJMfczgZICynkeO
+owIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUiGi/4I41xDs4a2L3KDuEgcgM100wDQYJKoZIhvcNAQEFBQADggIBADsC
+jcs8MOhuoK3yc7NfniUTBAXT9uOLuwt5zlPe5JbF0a9zvNXD0EBVfEB/zRtfCdXy
+fJ9oHbtdzno5wozWmHvFg1Wo1X1AyuAe94leY12hE8JdiraKfADzI8PthV9xdvBo
+Y6pFITlIYXg23PFDk9Qlx/KAZeFTAnVR/Ho67zerhChXDNjU1JlWbOOi/lmEtDHo
+M/hklJRRl6s5xUvt2t2AC298KQ3EjopyDedTFLJgQT2EkTFoPSdE2+Xe9PpjRchM
+Ppj1P0G6Tss3DbpmmPHdy59c91Q2gmssvBNhl0L4eLvMyKKfyvBovWsdst+Nbwed
+2o5nx0ceyrm/KkKRt2NTZvFCo+H0Wk1Ya7XkpDOtXHAd3ODy63MUkZoDweoAZbwH
+/M8SESIsrqC9OuCiKthZ6SnTGDWkrBFfGbW1G/8iSlzGeuQX7yCpp/Q/rYqnmgQl
+nQ7KN+ZQ/YxCKQSa7LnPS3K94gg2ryMvYuXKAdNw23yCIywWMQzGNgeQerEfZ1jE
+O1hZibCMjFCz2IbLaKPECudpSyDOwR5WS5WpI2jYMNjD67BVUc3l/Su49bsRn1NU
+9jQZjHkJNsphFyUXC4KYcwx3dMPVDceoEkzHp1RxRy4sGn3J4ys7SN4nhKdjNrN9
+j6BkOSQNPXuHr2ZcdBtLc7LljPCGmbjlxd+Ewbfr
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
+MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
+UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
+ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
+c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
+OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
+mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
+BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
+qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
+gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
+bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
+dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
+6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
+h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
+/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
+wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
+pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx
+IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs
+cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v
+dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0
+MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl
+bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD
+DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r
+WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU
+Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs
+HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj
+z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf
+SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl
+AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG
+KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P
+AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j
+BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC
+VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX
+ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB
+ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd
+/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB
+A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn
+k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9
+iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv
+2G0xffX8oRAHh84vWdw+WNs=
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/camster_duckdns_org.crt b/circuitpython/lib/axtls/ssl/test/camster_duckdns_org.crt
new file mode 100644
index 0000000..453bafa
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/camster_duckdns_org.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFXTCCBEWgAwIBAgIQRKGXkBbin0Hge3vNu4Z04TANBgkqhkiG9w0BAQsFADCB
+kDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxNjA0BgNV
+BAMTLUNPTU9ETyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD
+QTAeFw0xNDExMTIwMDAwMDBaFw0xNzExMTEyMzU5NTlaMFcxITAfBgNVBAsTGERv
+bWFpbiBDb250cm9sIFZhbGlkYXRlZDEUMBIGA1UECxMLUG9zaXRpdmVTU0wxHDAa
+BgNVBAMTE2NhbXN0ZXIuZHVja2Rucy5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCvKi9/3GOq1pqgnIQR2hTxr1kv17pUzpQeAVOZVCd/q6KbMrsw
+ayPj41hJd+EVtu6DV8Zd/Rxv4P6i2HTRWev9aE2+vFfTmhIZG0HUZqs3Fbq6yONn
+ox8d7Dsu/vwIkyaIE9mMAYYr81bX86v8cmvCHatCO/lluwUqjnXUjYpMOpTopHjC
+hNzUe63ZtUDVmXfTBHneO5GLZqhQSSX7rd33cJzkojGCoPSFP5TUhN5WGyRi+xa2
+bD+Q5xXlC4f/WVXiZxGiGPrWIpQBO5Y5o33S6Vo2ck9Bvg2g1atsR02m+yARtmH3
++IDlvg7DeyLL3AXgUwDNHnRb0t9LVDXcYOJnAgMBAAGjggHpMIIB5TAfBgNVHSME
+GDAWgBSQr2o6lFoL2JDqElZz30O0Oija5zAdBgNVHQ4EFgQUGNRX8FYKZUYa1F4+
+L7nyHOn3ArcwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwEGCCsGAQUFBwMCME8GA1UdIARIMEYwOgYLKwYBBAGyMQECAgcw
+KzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNvbS9DUFMwCAYG
+Z4EMAQIBMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly9jcmwuY29tb2RvY2EuY29t
+L0NPTU9ET1JTQURvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5jcmwwgYUG
+CCsGAQUFBwEBBHkwdzBPBggrBgEFBQcwAoZDaHR0cDovL2NydC5jb21vZG9jYS5j
+b20vQ09NT0RPUlNBRG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAk
+BggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMDcGA1UdEQQwMC6C
+E2NhbXN0ZXIuZHVja2Rucy5vcmeCF3d3dy5jYW1zdGVyLmR1Y2tkbnMub3JnMA0G
+CSqGSIb3DQEBCwUAA4IBAQBpfJIXHPyoxbXlS1Jy5V4oDpDR+vKRIXXUPDp6GlmK
+6w8W7M536W7JamLrT8wbA04hKgtjQkXD8pXZPFHBNJ92Lza5fKB/KiIlObz386lK
+Z9AVc10TwWlkZlFYhYVhQ+kpTtcUUdj5QI2org81s9XQoSViVOM8cxIuYk/er20g
+jY3Nvdbjg4dtakH1nsITGMYLN+wJglSAq1QGSQ76fLyYhMfF25nNjPYP96SFf1Dd
+XinknP2tED6ukzIgfkimlyn2/XIbnz4Xry8ouq4x/cPd8MOcffWt1QWlGIel5B8i
+I1vtVHceHSsHjnnNPSkXIn0/lpc5vzVZ+bw9yLt+Lvc6
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/comodo.sha384.cer b/circuitpython/lib/axtls/ssl/test/comodo.sha384.cer
new file mode 100644
index 0000000..b52ea61
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/comodo.sha384.cer
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
+gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
+VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
+AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
+2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
+ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
+4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
+m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
+vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
+8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
+IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
+KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
+GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
+s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
+JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
+AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
+MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
+bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
+zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
+Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
+Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
+B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
+PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
+pu/xO28QOG8=
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/deutsche_telecom.x509_ca b/circuitpython/lib/axtls/ssl/test/deutsche_telecom.x509_ca
new file mode 100644
index 0000000..0f4b96a
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/deutsche_telecom.x509_ca
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/equifax.x509_ca b/circuitpython/lib/axtls/ssl/test/equifax.x509_ca
new file mode 100644
index 0000000..79b0a3f
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/equifax.x509_ca
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/gnutls.cer b/circuitpython/lib/axtls/ssl/test/gnutls.cer
new file mode 100755
index 0000000..312e365
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/gnutls.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/header_issue.dat b/circuitpython/lib/axtls/ssl/test/header_issue.dat
new file mode 100755
index 0000000..a48d23d
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/header_issue.dat
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/killgnutls.sh b/circuitpython/lib/axtls/ssl/test/killgnutls.sh
new file mode 100755
index 0000000..f910f4b
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/killgnutls.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+ps -ef|grep gnutls-serv | /usr/bin/awk '{print $2}' |xargs kill -9
diff --git a/circuitpython/lib/axtls/ssl/test/killopenssl.sh b/circuitpython/lib/axtls/ssl/test/killopenssl.sh
new file mode 100755
index 0000000..17950fb
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/killopenssl.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+ps -ef|grep openssl | /usr/bin/awk '{print $2}' |xargs kill -9
diff --git a/circuitpython/lib/axtls/ssl/test/make_certs.sh b/circuitpython/lib/axtls/ssl/test/make_certs.sh
new file mode 100755
index 0000000..f234b3f
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/make_certs.sh
@@ -0,0 +1,253 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2007-2016, Cameron Rich
+#
+# 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 axTLS project 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 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.
+#
+
+#
+# Generate the certificates and keys for testing.
+#
+
+PROJECT_NAME="axTLS Project"
+
+# Generate the openssl configuration files.
+cat > ca_cert.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+req_extensions = v3_ca
+
+[ req_distinguished_name ]
+ O = $PROJECT_NAME Dodgy Certificate Authority
+
+[ v3_ca ]
+basicConstraints = critical, CA:true
+keyUsage = critical, cRLSign, keyCertSign, digitalSignature
+EOF
+
+cat > certs.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+req_extensions = v3_usr_cert
+
+[ req_distinguished_name ]
+ O = $PROJECT_NAME
+ CN = localhost
+
+[ v3_usr_cert ]
+basicConstraints = critical, CA:false
+keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = www.example.net
+DNS.2 = www.example.org
+EOF
+
+cat > device_cert.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+
+[ req_distinguished_name ]
+ O = $PROJECT_NAME Device Certificate
+EOF
+
+cat > intermediate_ca.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+req_extensions = v3_intermediate_ca
+
+[ req_distinguished_name ]
+ O = $PROJECT_NAME Intermediate CA
+
+[ v3_intermediate_ca ]
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = cRLSign, keyCertSign, digitalSignature
+EOF
+
+cat > intermediate_ca2.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+req_extensions = v3_intermediate_ca2
+
+[ req_distinguished_name ]
+ O = $PROJECT_NAME Intermediate 2 CA
+
+[ v3_intermediate_ca2 ]
+basicConstraints = critical, CA:true, pathlen:10
+keyUsage = encipherOnly, keyCertSign, decipherOnly
+EOF
+
+# private key generation
+openssl genrsa -out axTLS.ca_key.pem 2048
+openssl genrsa -out axTLS.key_1024.pem 1024
+openssl genrsa -out axTLS.key_2048.pem 2048
+openssl genrsa -out axTLS.key_4096.pem 4096
+openssl genrsa -out axTLS.key_device.pem 2048
+openssl genrsa -out axTLS.key_intermediate_ca.pem 2048
+openssl genrsa -out axTLS.key_intermediate_ca2.pem 2048
+openssl genrsa -out axTLS.key_end_chain.pem 2048
+openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 1024
+openssl genrsa -aes256 -passout pass:abcd -out axTLS.key_aes256.pem 1024
+
+# convert private keys into DER format
+openssl rsa -in axTLS.key_1024.pem -out axTLS.key_1024 -outform DER
+openssl rsa -in axTLS.key_2048.pem -out axTLS.key_2048 -outform DER
+openssl rsa -in axTLS.key_4096.pem -out axTLS.key_4096 -outform DER
+
+# cert requests
+openssl req -out axTLS.ca_x509.csr -key axTLS.ca_key.pem -new \
+ -config ./ca_cert.conf
+openssl req -out axTLS.x509_1024.csr -key axTLS.key_1024.pem -new \
+ -config ./certs.conf
+openssl req -out axTLS.x509_2048.csr -key axTLS.key_2048.pem -new \
+ -config ./certs.conf
+openssl req -out axTLS.x509_4096.csr -key axTLS.key_4096.pem -new \
+ -config ./certs.conf
+openssl req -out axTLS.x509_device.csr -key axTLS.key_device.pem -new \
+ -config ./device_cert.conf
+openssl req -out axTLS.x509_intermediate_ca.csr \
+ -key axTLS.key_intermediate_ca.pem -new \
+ -config ./intermediate_ca.conf
+openssl req -out axTLS.x509_intermediate_ca2.csr \
+ -key axTLS.key_intermediate_ca2.pem -new \
+ -config ./intermediate_ca2.conf
+openssl req -out axTLS.x509_end_chain.csr -key axTLS.key_end_chain.pem -new \
+ -config ./certs.conf
+openssl req -out axTLS.x509_aes128.csr -key axTLS.key_aes128.pem \
+ -new -config ./certs.conf -passin pass:abcd
+openssl req -out axTLS.x509_aes256.csr -key axTLS.key_aes256.pem \
+ -new -config ./certs.conf -passin pass:abcd
+
+# generate the actual certs.
+openssl x509 -req -in axTLS.ca_x509.csr -out axTLS.ca_x509.pem \
+ -sha1 -days 5000 -signkey axTLS.ca_key.pem \
+ -CAkey axTLS.ca_key.pem -extfile ./ca_cert.conf -extensions v3_ca
+openssl x509 -req -in axTLS.ca_x509.csr -out axTLS.ca_x509_sha256.pem \
+ -sha256 -days 5000 -signkey axTLS.ca_key.pem \
+ -CAkey axTLS.ca_key.pem -extfile ./ca_cert.conf -extensions v3_ca
+openssl x509 -req -in axTLS.x509_1024.csr -out axTLS.x509_1024.pem \
+ -sha1 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_1024.csr -out axTLS.x509_1024_sha256.pem \
+ -sha256 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509_sha256.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_1024.csr -out axTLS.x509_1024_sha384.pem \
+ -sha384 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509_sha256.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_1024.csr -out axTLS.x509_1024_sha512.pem \
+ -sha512 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509_sha256.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_2048.csr -out axTLS.x509_2048.pem \
+ -sha1 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_4096.csr -out axTLS.x509_4096.pem \
+ -sha1 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_device.csr -out axTLS.x509_device.pem \
+ -sha1 -CAcreateserial -days 5000 \
+ -CA axTLS.x509_1024.pem -CAkey axTLS.key_1024.pem
+openssl x509 -req -in axTLS.x509_intermediate_ca.csr -out axTLS.x509_intermediate_ca.pem \
+ -sha256 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem \
+ -extfile ./intermediate_ca.conf -extensions v3_intermediate_ca
+openssl x509 -req -in axTLS.x509_intermediate_ca2.csr -out axTLS.x509_intermediate_ca2.pem \
+ -sha256 -CAcreateserial -days 5000 \
+ -CA axTLS.x509_intermediate_ca.pem \
+ -CAkey axTLS.key_intermediate_ca.pem \
+ -extfile ./intermediate_ca2.conf -extensions v3_intermediate_ca2
+openssl x509 -req -in axTLS.x509_end_chain.csr -out axTLS.x509_end_chain.pem \
+ -sha256 -CAcreateserial -days 5000 \
+ -CA axTLS.x509_intermediate_ca.pem \
+ -CAkey axTLS.key_intermediate_ca.pem \
+ -extfile ./certs.conf -extensions v3_usr_cert
+# basic constraint path len failure
+openssl x509 -req -in axTLS.x509_end_chain.csr \
+ -out axTLS.x509_end_chain_bad.pem \
+ -sha256 -CAcreateserial -days 5000 \
+ -CA axTLS.x509_intermediate_ca2.pem \
+ -CAkey axTLS.key_intermediate_ca2.pem \
+ -extfile ./certs.conf -extensions v3_usr_cert
+cat axTLS.x509_intermediate_ca.pem >> axTLS.x509_intermediate_ca2.pem
+openssl x509 -req -in axTLS.x509_aes128.csr \
+ -out axTLS.x509_aes128.pem \
+ -sha1 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+openssl x509 -req -in axTLS.x509_aes256.csr \
+ -out axTLS.x509_aes256.pem \
+ -sha1 -CAcreateserial -days 5000 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+
+# note: must be root to do this
+DATE_NOW=`date`
+if date -s "Jan 1 2025"; then
+openssl x509 -req -in axTLS.x509_1024.csr -out axTLS.x509_bad_before.pem \
+ -sha1 -CAcreateserial -days 365 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+date -s "$DATE_NOW"
+touch axTLS.x509_bad_before.pem
+fi
+openssl x509 -req -in axTLS.x509_1024.csr -out axTLS.x509_bad_after.pem \
+ -sha1 -CAcreateserial -days -365 \
+ -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem
+
+# some cleanup
+rm axTLS*.csr
+rm *.srl
+rm *.conf
+
+# need this for the client tests
+openssl x509 -in axTLS.ca_x509.pem -outform DER -out axTLS.ca_x509.cer
+openssl x509 -in axTLS.x509_1024.pem -outform DER -out axTLS.x509_1024.cer
+openssl x509 -in axTLS.x509_2048.pem -outform DER -out axTLS.x509_2048.cer
+openssl x509 -in axTLS.x509_4096.pem -outform DER -out axTLS.x509_4096.cer
+
+# generate pkcs8 files (use RC4-128 for encryption)
+openssl pkcs8 -in axTLS.key_1024.pem -passout pass:abcd -topk8 -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8
+openssl pkcs8 -in axTLS.key_1024.pem -passout pass:abcd -topk8 -outform DER -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8
+openssl pkcs8 -in axTLS.key_1024.pem -nocrypt -topk8 -out axTLS.unencrypted_pem.p8
+openssl pkcs8 -in axTLS.key_1024.pem -nocrypt -topk8 -outform DER -out axTLS.unencrypted.p8
+
+# generate pkcs12 files (use RC4-128 for encryption)
+openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_with_CA" -out axTLS.withCA.p12 -password pass:abcd
+openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_without_CA" -out axTLS.withoutCA.p12 -password pass:abcd
+openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -out axTLS.noname.p12 -password pass:abcd
+
+# PEM certificate chain
+cat axTLS.ca_x509.pem >> axTLS.x509_device.pem
+
+# set default key/cert for use in the server
+xxd -i axTLS.x509_1024.cer | sed -e \
+ "s/axTLS_x509_1024_cer/default_certificate/" > ../../ssl/cert.h
+xxd -i axTLS.key_1024 | sed -e \
+ "s/axTLS_key_1024/default_private_key/" > ../../ssl/private_key.h
diff --git a/circuitpython/lib/axtls/ssl/test/microsoft.x509_ca b/circuitpython/lib/axtls/ssl/test/microsoft.x509_ca
new file mode 100644
index 0000000..b908034
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/microsoft.x509_ca
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/microsoft.x509_ca.pem b/circuitpython/lib/axtls/ssl/test/microsoft.x509_ca.pem
new file mode 100644
index 0000000..478e60b
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/microsoft.x509_ca.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEEjCCAvqgAwIBAgIPAMEAizw8iBHRPvZj7N9AMA0GCSqGSIb3DQEBBAUAMHAx
+KzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29ycC4xHjAc
+BgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0
+IFJvb3QgQXV0aG9yaXR5MB4XDTk3MDExMDA3MDAwMFoXDTIwMTIzMTA3MDAwMFow
+cDErMCkGA1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jvc29mdCBDb3JwLjEe
+MBwGA1UECxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3Nv
+ZnQgUm9vdCBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCpAr3BcOY78k4bKJ+XeF4w6qKpjSVf+P6VTKO3/p2iID58UaKboo9gMmvRQmR5
+7qx2yVTa8uuchhyPn4Rms8VremIj1h083g8BkuiWxL8tZpqaaCaZ0Dosvwy1WCbB
+RucKPjiWLKkoOajsSYNC44QPu5psVWGsgnyhYC13TOmZtGQ7mlAcMQgkFJ+p55Er
+GOY9mGMUYFgFZZ8dN1KH96fvlALGG9O/VUWziYC/OuxUlE6u/ad6bXROrxjMlgko
+IQBXkGBpN7tLEgc8Vv9b+6RmCgim0oFWV++2O14WgXcE2va+roCV/rDNf9anGnJc
+PMq88AijIjCzBoXJsyB3E4XfAgMBAAGjgagwgaUwgaIGA1UdAQSBmjCBl4AQW9Bw
+72lyniNRfhSyTY7/y6FyMHAxKzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBN
+aWNyb3NvZnQgQ29ycC4xHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEh
+MB8GA1UEAxMYTWljcm9zb2Z0IFJvb3QgQXV0aG9yaXR5gg8AwQCLPDyIEdE+9mPs
+30AwDQYJKoZIhvcNAQEEBQADggEBAJXoC8CN85cYNe24ASTYdxHzXGAyn54Lyz4F
+kYiPyTrmIfLwV5MstaBHyGLv/NfMOztaqTZUaf4kbT/JzKreBXzdMY09nxBwarv+
+Ek8YacD80EPjEVogT+pie6+qGcgrNyUtvmWhEoolD2Oj91Qc+SHJ1hXzUqxuQzIH
+/YIX+OVnbA1R9r3xUse958Qw/CAxCYgdlSkaTdUdAqXxgOADtFv0sd3IV+5lScdS
+VLa0AygS/5DW8AiPfriXxas3LOR65Kh343agANBqP8HSNorgQRKoNWobats14dQc
+BOSoRQTIWjM4bk0cDWK3CqKM09VUP0bNHFWmcNsSOoeTdZ+n0qA=
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/ms_iis.cer b/circuitpython/lib/axtls/ssl/test/ms_iis.cer
new file mode 100755
index 0000000..250b926
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/ms_iis.cer
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB5jCCAVOgAwIBAgIQWPe7KyA+U7lLUohulwW2HDAJBgUrDgMCHQUAMCExHzAd
+BgNVBAMTFmF4dGxzLmNlcm9jY2x1Yi5jb20uYXUwHhcNMDgwMzE3MTAyMTA2WhcN
+MDkwMzE3MTAyMTA2WjAhMR8wHQYDVQQDExZheHRscy5jZXJvY2NsdWIuY29tLmF1
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9JqHlQjrQMt3JW8yxcGhFagDa
+D4QiIY8+KItTt13fIBt5g1AG4VXniaylSqKKYNPwVzqSWl7WhxMmoFU73veF8o4M
+G0Zc5qbVB6ukrSV4WaTgHrIO6pWkyiaQ4L/eYfCo/2pByhl0IUKkf/TMN346/rFg
+JgrElx01l6QHNQrzVQIDAQABoycwJTATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNV
+HQ8EBwMFALAAAAAwCQYFKw4DAh0FAAOBgQAbH94H1fryngROJ//Oa0D3vvTO8CJ3
+8VW+3gQEwrPBOWmN6RV8OM0dE6pf8wD3s7PTCcM5+/HI1Qk53nUGrNiOmKM1s0JB
+bvsO9RT+UF8mtdbo/n30M0MHMWPCC76baW3R+ANBp/V/z4l1ytpUTt+MHvz0VlUs
+J4uJA3s3uh23Tg==
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/perf_bigint.c b/circuitpython/lib/axtls/ssl/test/perf_bigint.c
new file mode 100644
index 0000000..6336d1b
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/perf_bigint.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2007, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * Some performance testing of bigint.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "os_port.h"
+#include "ssl.h"
+
+/**************************************************************************
+ * BIGINT tests
+ *
+ **************************************************************************/
+
+int main(int argc, char *argv[])
+{
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ RSA_CTX *rsa_ctx = NULL;
+ BI_CTX *ctx;
+ bigint *bi_data, *bi_res;
+ float diff;
+ int res = 1;
+ struct timeval tv_old, tv_new;
+ const char *plaintext;
+ uint8_t compare[MAX_KEY_BYTE_SIZE];
+ int i, max_biggie = 10; /* really crank performance */
+ int len;
+ uint8_t *buf;
+
+ /**
+ * 512 bit key
+ */
+ plaintext = /* 64 byte number */
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^";
+
+ len = get_file("../ssl/test/axTLS.key_512", &buf);
+ asn1_get_private_key(buf, len, &rsa_ctx);
+ ctx = rsa_ctx->bi_ctx;
+ bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext));
+ bi_res = RSA_public(rsa_ctx, bi_data);
+ bi_data = bi_res; /* reuse again */
+
+ gettimeofday(&tv_old, NULL);
+ for (i = 0; i < max_biggie; i++)
+ {
+ bi_res = RSA_private(rsa_ctx, bi_copy(bi_data));
+ if (i < max_biggie-1)
+ {
+ bi_free(ctx, bi_res);
+ }
+ }
+
+ gettimeofday(&tv_new, NULL);
+ bi_free(ctx, bi_data);
+
+ diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 +
+ (tv_new.tv_usec-tv_old.tv_usec)/1000;
+ printf("512 bit decrypt time: %.2fms\n", diff/max_biggie);
+ TTY_FLUSH();
+ bi_export(ctx, bi_res, compare, 64);
+ RSA_free(rsa_ctx);
+ free(buf);
+ if (memcmp(plaintext, compare, 64) != 0)
+ goto end;
+
+ /**
+ * 1024 bit key
+ */
+ plaintext = /* 128 byte number */
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^";
+
+ len = get_file("../ssl/test/axTLS.key_1024", &buf);
+ rsa_ctx = NULL;
+ asn1_get_private_key(buf, len, &rsa_ctx);
+ ctx = rsa_ctx->bi_ctx;
+ bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext));
+ bi_res = RSA_public(rsa_ctx, bi_data);
+ bi_data = bi_res; /* reuse again */
+
+ gettimeofday(&tv_old, NULL);
+ for (i = 0; i < max_biggie; i++)
+ {
+ bi_res = RSA_private(rsa_ctx, bi_copy(bi_data));
+ if (i < max_biggie-1)
+ {
+ bi_free(ctx, bi_res);
+ }
+ }
+
+ gettimeofday(&tv_new, NULL);
+ bi_free(ctx, bi_data);
+
+ diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 +
+ (tv_new.tv_usec-tv_old.tv_usec)/1000;
+ printf("1024 bit decrypt time: %.2fms\n", diff/max_biggie);
+ TTY_FLUSH();
+ bi_export(ctx, bi_res, compare, 128);
+ RSA_free(rsa_ctx);
+ free(buf);
+ if (memcmp(plaintext, compare, 128) != 0)
+ goto end;
+
+ /**
+ * 2048 bit key
+ */
+ plaintext = /* 256 byte number */
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^";
+
+ len = get_file("../ssl/test/axTLS.key_2048", &buf);
+ rsa_ctx = NULL;
+ asn1_get_private_key(buf, len, &rsa_ctx);
+ ctx = rsa_ctx->bi_ctx;
+ bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext));
+ bi_res = RSA_public(rsa_ctx, bi_data);
+ bi_data = bi_res; /* reuse again */
+
+ gettimeofday(&tv_old, NULL);
+ for (i = 0; i < max_biggie; i++)
+ {
+ bi_res = RSA_private(rsa_ctx, bi_copy(bi_data));
+ if (i < max_biggie-1)
+ {
+ bi_free(ctx, bi_res);
+ }
+ }
+ gettimeofday(&tv_new, NULL);
+ bi_free(ctx, bi_data);
+
+ diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 +
+ (tv_new.tv_usec-tv_old.tv_usec)/1000;
+ printf("2048 bit decrypt time: %.2fms\n", diff/max_biggie);
+ TTY_FLUSH();
+ bi_export(ctx, bi_res, compare, 256);
+ RSA_free(rsa_ctx);
+ free(buf);
+ if (memcmp(plaintext, compare, 256) != 0)
+ goto end;
+
+ /**
+ * 4096 bit key
+ */
+ plaintext = /* 512 byte number */
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^";
+
+ len = get_file("../ssl/test/axTLS.key_4096", &buf);
+ rsa_ctx = NULL;
+ asn1_get_private_key(buf, len, &rsa_ctx);
+ ctx = rsa_ctx->bi_ctx;
+ bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext));
+ gettimeofday(&tv_old, NULL);
+ bi_res = RSA_public(rsa_ctx, bi_data);
+ gettimeofday(&tv_new, NULL);
+ diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 +
+ (tv_new.tv_usec-tv_old.tv_usec)/1000;
+ printf("4096 bit encrypt time: %.2fms\n", diff);
+ TTY_FLUSH();
+ bi_data = bi_res; /* reuse again */
+
+ gettimeofday(&tv_old, NULL);
+ for (i = 0; i < max_biggie; i++)
+ {
+ bi_res = RSA_private(rsa_ctx, bi_copy(bi_data));
+ if (i < max_biggie-1)
+ {
+ bi_free(ctx, bi_res);
+ }
+ }
+
+ gettimeofday(&tv_new, NULL);
+ bi_free(ctx, bi_data);
+
+ diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 +
+ (tv_new.tv_usec-tv_old.tv_usec)/1000;
+ printf("4096 bit decrypt time: %.2fms\n", diff/max_biggie);
+ TTY_FLUSH();
+ bi_export(ctx, bi_res, compare, 512);
+ RSA_free(rsa_ctx);
+ free(buf);
+ if (memcmp(plaintext, compare, 512) != 0)
+ goto end;
+
+ /* done */
+ printf("Bigint performance testing complete\n");
+ res = 0;
+
+end:
+ return res;
+#else
+ return 0;
+#endif
+}
diff --git a/circuitpython/lib/axtls/ssl/test/qualityssl.com.der b/circuitpython/lib/axtls/ssl/test/qualityssl.com.der
new file mode 100644
index 0000000..c73b246
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/qualityssl.com.der
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/socgen.cer b/circuitpython/lib/axtls/ssl/test/socgen.cer
new file mode 100755
index 0000000..a427870
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/socgen.cer
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/ssltest.c b/circuitpython/lib/axtls/ssl/test/ssltest.c
new file mode 100644
index 0000000..ceb241e
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/ssltest.c
@@ -0,0 +1,2589 @@
+/*
+ * Copyright (c) 2007-2016, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/*
+ * The testing of the crypto and ssl stuff goes here. Keeps the individual code
+ * modules from being uncluttered with test code.
+ *
+ * This is test code - I make no apologies for the quality!
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifndef WIN32
+#include <pthread.h>
+#endif
+
+#include "os_port.h"
+#include "ssl.h"
+
+#define DEFAULT_CERT "../ssl/test/axTLS.x509_1024.cer"
+#define DEFAULT_KEY "../ssl/test/axTLS.key_1024"
+//#define DEFAULT_SVR_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES
+#define DEFAULT_SVR_OPTION 0
+//#define DEFAULT_CLNT_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES
+#define DEFAULT_CLNT_OPTION 0
+
+/* hack to remove gcc warning */
+#define SYSTEM(A) if (system(A) < 0) printf("system call error\n");
+
+static int g_port = 19001;
+
+/**************************************************************************
+ * AES tests
+ *
+ * Run through a couple of the RFC3602 tests to verify that AES is correct.
+ **************************************************************************/
+#define TEST1_SIZE 16
+#define TEST2_SIZE 32
+
+static int AES_test(BI_CTX *bi_ctx)
+{
+ AES_CTX aes_key;
+ int res = 1;
+ uint8_t key[TEST1_SIZE];
+ uint8_t iv[TEST1_SIZE];
+
+ {
+ /*
+ Case #1: Encrypting 16 bytes (1 block) using AES-CBC
+ Key : 0x06a9214036b8a15b512e03d534120006
+ IV : 0x3dafba429d9eb430b422da802c9fac41
+ Plaintext : "Single block msg"
+ Ciphertext: 0xe353779c1079aeb82708942dbe77181a
+
+ */
+ char *in_str = "Single block msg";
+ uint8_t ct[TEST1_SIZE];
+ uint8_t enc_data[TEST1_SIZE];
+ uint8_t dec_data[TEST1_SIZE];
+
+ bigint *key_bi = bi_str_import(
+ bi_ctx, "06A9214036B8A15B512E03D534120006");
+ bigint *iv_bi = bi_str_import(
+ bi_ctx, "3DAFBA429D9EB430B422DA802C9FAC41");
+ bigint *ct_bi = bi_str_import(
+ bi_ctx, "E353779C1079AEB82708942DBE77181A");
+ bi_export(bi_ctx, key_bi, key, TEST1_SIZE);
+ bi_export(bi_ctx, iv_bi, iv, TEST1_SIZE);
+ bi_export(bi_ctx, ct_bi, ct, TEST1_SIZE);
+
+ AES_set_key(&aes_key, key, iv, AES_MODE_128);
+ AES_cbc_encrypt(&aes_key, (const uint8_t *)in_str,
+ enc_data, sizeof(enc_data));
+ if (memcmp(enc_data, ct, sizeof(ct)))
+ {
+ printf("Error: AES ENCRYPT #1 failed\n");
+ goto end;
+ }
+
+ AES_set_key(&aes_key, key, iv, AES_MODE_128);
+ AES_convert_key(&aes_key);
+ AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data));
+
+ if (memcmp(dec_data, in_str, sizeof(dec_data)))
+ {
+ printf("Error: AES DECRYPT #1 failed\n");
+ goto end;
+ }
+ }
+
+ {
+ /*
+ Case #2: Encrypting 32 bytes (2 blocks) using AES-CBC
+ Key : 0xc286696d887c9aa0611bbb3e2025a45a
+ IV : 0x562e17996d093d28ddb3ba695a2e6f58
+ Plaintext : 0x000102030405060708090a0b0c0d0e0f
+ 101112131415161718191a1b1c1d1e1f
+ Ciphertext: 0xd296cd94c2cccf8a3a863028b5e1dc0a
+ 7586602d253cfff91b8266bea6d61ab1
+ */
+ uint8_t in_data[TEST2_SIZE];
+ uint8_t ct[TEST2_SIZE];
+ uint8_t enc_data[TEST2_SIZE];
+ uint8_t dec_data[TEST2_SIZE];
+
+ bigint *in_bi = bi_str_import(bi_ctx,
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ bigint *key_bi = bi_str_import(
+ bi_ctx, "C286696D887C9AA0611BBB3E2025A45A");
+ bigint *iv_bi = bi_str_import(
+ bi_ctx, "562E17996D093D28DDB3BA695A2E6F58");
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "D296CD94C2CCCF8A3A863028B5E1DC0A7586602D253CFFF91B8266BEA6D61AB1");
+ bi_export(bi_ctx, in_bi, in_data, TEST2_SIZE);
+ bi_export(bi_ctx, key_bi, key, TEST1_SIZE);
+ bi_export(bi_ctx, iv_bi, iv, TEST1_SIZE);
+ bi_export(bi_ctx, ct_bi, ct, TEST2_SIZE);
+
+ AES_set_key(&aes_key, key, iv, AES_MODE_128);
+ AES_cbc_encrypt(&aes_key, (const uint8_t *)in_data,
+ enc_data, sizeof(enc_data));
+
+ if (memcmp(enc_data, ct, sizeof(ct)))
+ {
+ printf("Error: ENCRYPT #2 failed\n");
+ goto end;
+ }
+
+ AES_set_key(&aes_key, key, iv, AES_MODE_128);
+ AES_convert_key(&aes_key);
+ AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data));
+ if (memcmp(dec_data, in_data, sizeof(dec_data)))
+ {
+ printf("Error: DECRYPT #2 failed\n");
+ goto end;
+ }
+ }
+
+ res = 0;
+ printf("All AES tests passed\n");
+
+end:
+ return res;
+}
+
+/**************************************************************************
+ * SHA1 tests
+ *
+ * Run through a couple of the RFC3174 tests to verify that SHA1 is correct.
+ **************************************************************************/
+static int SHA1_test(BI_CTX *bi_ctx)
+{
+ SHA1_CTX ctx;
+ uint8_t ct[SHA1_SIZE];
+ uint8_t digest[SHA1_SIZE];
+ int res = 1;
+
+ {
+ const char *in_str = "abc";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "A9993E364706816ABA3E25717850C26C9CD0D89D");
+ bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE);
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA1_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA1 #1 failed\n");
+ goto end;
+ }
+ }
+
+ {
+ const char *in_str =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "84983E441C3BD26EBAAE4AA1F95129E5E54670F1");
+ bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE);
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA1_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA1 #2 failed\n");
+ goto end;
+ }
+ }
+
+ res = 0;
+ printf("All SHA1 tests passed\n");
+
+end:
+ return res;
+}
+
+/**************************************************************************
+ * SHA256 tests
+ *
+ * Run through a couple of the SHA-2 tests to verify that SHA256 is correct.
+ **************************************************************************/
+static int SHA256_test(BI_CTX *bi_ctx)
+{
+ SHA256_CTX ctx;
+ uint8_t ct[SHA256_SIZE];
+ uint8_t digest[SHA256_SIZE];
+ int res = 1;
+
+ {
+ const char *in_str = "abc";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
+ bi_export(bi_ctx, ct_bi, ct, SHA256_SIZE);
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA256_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA256 #1 failed\n");
+ goto end;
+ }
+ }
+
+ {
+ const char *in_str =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1");
+ bi_export(bi_ctx, ct_bi, ct, SHA256_SIZE);
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA256_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA256 #2 failed\n");
+ goto end;
+ }
+ }
+
+ res = 0;
+ printf("All SHA256 tests passed\n");
+
+end:
+ return res;
+}
+
+/**************************************************************************
+ * SHA384 tests
+ *
+ * Run through a couple of the SHA-2 tests to verify that SHA384 is correct.
+ **************************************************************************/
+static int SHA384_test(BI_CTX *bi_ctx)
+{
+ SHA384_CTX ctx;
+ uint8_t ct[SHA384_SIZE];
+ uint8_t digest[SHA384_SIZE];
+ int res = 1;
+
+ {
+ const char *in_str = "abc";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1631A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7");
+ bi_export(bi_ctx, ct_bi, ct, SHA384_SIZE);
+
+ SHA384_Init(&ctx);
+ SHA384_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA384_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA384 #1 failed\n");
+ goto end;
+ }
+ }
+
+ {
+ const char *in_str =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "3391FDDDFC8DC7393707A65B1B4709397CF8B1D162AF05ABFE8F450DE5F36BC6B0455A8520BC4E6F5FE95B1FE3C8452B");
+ bi_export(bi_ctx, ct_bi, ct, SHA384_SIZE);
+
+ SHA384_Init(&ctx);
+ SHA384_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA384_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA384 #2 failed\n");
+ goto end;
+ }
+ }
+
+ res = 0;
+ printf("All SHA384 tests passed\n");
+
+end:
+ return res;
+}
+/**************************************************************************
+ * SHA512 tests
+ *
+ * Run through a couple of the SHA-2 tests to verify that SHA512 is correct.
+ **************************************************************************/
+static int SHA512_test(BI_CTX *bi_ctx)
+{
+ SHA512_CTX ctx;
+ uint8_t ct[SHA512_SIZE];
+ uint8_t digest[SHA512_SIZE];
+ int res = 1;
+
+ {
+ const char *in_str = "abc";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA20A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD454D4423643CE80E2A9AC94FA54CA49F");
+ bi_export(bi_ctx, ct_bi, ct, SHA512_SIZE);
+
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA512_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA512 #1 failed\n");
+ goto end;
+ }
+ }
+
+ {
+ const char *in_str =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "204A8FC6DDA82F0A0CED7BEB8E08A41657C16EF468B228A8279BE331A703C33596FD15C13B1B07F9AA1D3BEA57789CA031AD85C7A71DD70354EC631238CA3445");
+ bi_export(bi_ctx, ct_bi, ct, SHA512_SIZE);
+
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ SHA512_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: SHA512 #2 failed\n");
+ goto end;
+ }
+ }
+
+ res = 0;
+ printf("All SHA512 tests passed\n");
+
+end:
+ return res;
+}
+/**************************************************************************
+ * MD5 tests
+ *
+ * Run through a couple of the RFC1321 tests to verify that MD5 is correct.
+ **************************************************************************/
+static int MD5_test(BI_CTX *bi_ctx)
+{
+ MD5_CTX ctx;
+ uint8_t ct[MD5_SIZE];
+ uint8_t digest[MD5_SIZE];
+ int res = 1;
+
+ {
+ const char *in_str = "abc";
+ bigint *ct_bi = bi_str_import(bi_ctx,
+ "900150983CD24FB0D6963F7D28E17F72");
+ bi_export(bi_ctx, ct_bi, ct, MD5_SIZE);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ MD5_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: MD5 #1 failed\n");
+ goto end;
+ }
+ }
+
+ {
+ const char *in_str =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ bigint *ct_bi = bi_str_import(
+ bi_ctx, "D174AB98D277D9F5A5611C2C9F419D9F");
+ bi_export(bi_ctx, ct_bi, ct, MD5_SIZE);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, (const uint8_t *)in_str, strlen(in_str));
+ MD5_Final(digest, &ctx);
+
+ if (memcmp(digest, ct, sizeof(ct)))
+ {
+ printf("Error: MD5 #2 failed\n");
+ goto end;
+ }
+ }
+ res = 0;
+ printf("All MD5 tests passed\n");
+
+end:
+ return res;
+}
+
+/**************************************************************************
+ * HMAC tests
+ *
+ * Run through a couple of the RFC2202 tests to verify that HMAC is correct.
+ **************************************************************************/
+static int HMAC_test(BI_CTX *bi_ctx)
+{
+ uint8_t key[SHA256_SIZE];
+ uint8_t ct[SHA256_SIZE];
+ uint8_t dgst[SHA256_SIZE];
+ int res = 1;
+ const char *key_str;
+
+ const char *data_str = "Hi There";
+ bigint *key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B");
+ bigint *ct_bi = bi_str_import(bi_ctx, "9294727A3638BB1C13F48EF8158BFC9D");
+ bi_export(bi_ctx, key_bi, key, MD5_SIZE);
+ bi_export(bi_ctx, ct_bi, ct, MD5_SIZE);
+ hmac_md5((const uint8_t *)data_str, 8, key, MD5_SIZE, dgst);
+ if (memcmp(dgst, ct, MD5_SIZE))
+ {
+ printf("HMAC MD5 #1 failed\n");
+ goto end;
+ }
+
+ data_str = "what do ya want for nothing?";
+ key_str = "Jefe";
+ ct_bi = bi_str_import(bi_ctx, "750C783E6AB0B503EAA86E310A5DB738");
+ bi_export(bi_ctx, ct_bi, ct, MD5_SIZE);
+ hmac_md5((const uint8_t *)data_str, 28, (const uint8_t *)key_str, 4, dgst);
+ if (memcmp(dgst, ct, MD5_SIZE))
+ {
+ printf("HMAC MD5 #2 failed\n");
+ goto end;
+ }
+
+ data_str = "Hi There";
+ key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B");
+ ct_bi = bi_str_import(bi_ctx, "B617318655057264E28BC0B6FB378C8EF146BE00");
+ bi_export(bi_ctx, key_bi, key, SHA1_SIZE);
+ bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE);
+
+ hmac_sha1((const uint8_t *)data_str, 8,
+ (const uint8_t *)key, SHA1_SIZE, dgst);
+ if (memcmp(dgst, ct, SHA1_SIZE))
+ {
+ printf("HMAC SHA1 #1 failed\n");
+ goto end;
+ }
+
+ data_str = "what do ya want for nothing?";
+ key_str = "Jefe";
+ ct_bi = bi_str_import(bi_ctx, "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79");
+ bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE);
+
+ hmac_sha1((const uint8_t *)data_str, 28, (const uint8_t *)key_str, 4, dgst);
+ if (memcmp(dgst, ct, SHA1_SIZE))
+ {
+ printf("HMAC SHA1 #2 failed\n");
+ goto end;
+ }
+
+ data_str = "Hi There";
+ key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B");
+ ct_bi = bi_str_import(bi_ctx,
+ "B0344C61D8DB38535CA8AFCEAF0BF12B881DC200C9833DA726E9376C2E32CFF7");
+ bi_export(bi_ctx, key_bi, key, 20);
+ bi_export(bi_ctx, ct_bi, ct, SHA256_SIZE);
+
+ hmac_sha256((const uint8_t *)data_str, 8,
+ (const uint8_t *)key, 20, dgst);
+
+ if (memcmp(dgst, ct, SHA256_SIZE))
+ {
+ printf("HMAC SHA256 #1 failed\n");
+ goto end;
+ }
+
+ data_str = "what do ya want for nothing?";
+ key_str = "Jefe";
+ ct_bi = bi_str_import(bi_ctx,
+ "5BDCC146BF60754E6A042426089575C75A003F089D2739839DEC58B964EC3843");
+ bi_export(bi_ctx, ct_bi, ct, SHA256_SIZE);
+
+ hmac_sha256((const uint8_t *)data_str, 28,
+ (const uint8_t *)key_str, 4, dgst);
+ if (memcmp(dgst, ct, SHA256_SIZE))
+ {
+ printf("HMAC SHA256 #2 failed\n");
+ goto end;
+ }
+
+ // other test
+ /*uint8_t secret[16];
+ key_str = "9BBE436BA940F017B17652849A71DB35";
+ ct_bi = bi_str_import(bi_ctx, key_str);
+ bi_export(bi_ctx, ct_bi, secret, 16);
+
+ uint8_t random[26];
+ data_str = "74657374206C6162656CA0BA9F936CDA311827A6F796FFD5198C";
+ ct_bi = bi_str_import(bi_ctx, data_str);
+ bi_export(bi_ctx, ct_bi, random, 26);
+
+ uint8_t output[256];
+ p_hash_sha256(secret, 16, random, 26, output, 100);
+ ct_bi = bi_import(bi_ctx, output, 100);
+ bi_print("RESULT", ct_bi);
+ */
+
+ /*uint8_t secret[48];
+ uint8_t random[256];
+ uint8_t output[256];
+
+ key_str =
+ "8C6D256467157DAEC7BAEBC1371E6DABFF1AB686EFA7DCF6B65242AA6EEBFC0A7472A1E583C4F2B23F784F25A6DE05A6";
+ ct_bi = bi_str_import(bi_ctx, key_str);
+ bi_export(bi_ctx, ct_bi, secret, 48);
+
+ data_str =
+ "636C69656E742066696E697368656475F80B2E4375CFA44105D16694A5E2D232302FF27241BDF52BA681C13E2CDF9F";
+ ct_bi = bi_str_import(bi_ctx, data_str);
+ bi_export(bi_ctx, ct_bi, random, 47);
+
+ p_hash_sha256(secret, 48, random, 47, output, 12);
+ ct_bi = bi_import(bi_ctx, output, 12);
+ bi_print("RESULT1", ct_bi);*/
+
+ res = 0;
+ printf("All HMAC tests passed\n");
+
+end:
+ return res;
+}
+
+/**************************************************************************
+ * BIGINT tests
+ *
+ **************************************************************************/
+static int BIGINT_test(BI_CTX *ctx)
+{
+ int res = 1;
+
+#ifndef CONFIG_INTEGER_8BIT
+#ifndef CONFIG_INTEGER_16BIT
+ bigint *bi_data, *bi_exp, *bi_res;
+ const char *expnt, *plaintext, *mod;
+ uint8_t compare[MAX_KEY_BYTE_SIZE];
+ /**
+ * 512 bit key
+ */
+ plaintext = /* 64 byte number */
+ "01aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee";
+
+ mod = "C30773C8ABE09FCC279EE0E5343370DE"
+ "8B2FFDB6059271E3005A7CEEF0D35E0A"
+ "1F9915D95E63560836CC2EB2C289270D"
+ "BCAE8CAF6F5E907FC2759EE220071E1B";
+
+ expnt = "A1E556CD1738E10DF539E35101334E97"
+ "BE8D391C57A5C89A7AD9A2EA2ACA1B3D"
+ "F3140F5091CC535CBAA47CEC4159EE1F"
+ "B6A3661AFF1AB758426EAB158452A9B9";
+
+ bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext));
+ bi_exp = int_to_bi(ctx, 0x10001);
+ bi_set_mod(ctx, bi_str_import(ctx, mod), 0);
+ bi_res = bi_mod_power(ctx, bi_data, bi_exp);
+
+ bi_data = bi_res; /* resuse again - see if we get the original */
+
+ bi_exp = bi_str_import(ctx, expnt);
+ bi_res = bi_mod_power(ctx, bi_data, bi_exp);
+ bi_free_mod(ctx, 0);
+
+ bi_export(ctx, bi_res, compare, 64);
+ if (memcmp(plaintext, compare, 64) != 0)
+ goto end;
+#endif
+#endif
+
+ /*
+ * Multiply with psssible carry issue (8 bit)
+ */
+ {
+ bigint *bi_x = bi_str_import(ctx,
+ "AFD5060E224B70DA99EFB385BA5C0D2BEA0AD1DAAA52686E1A02D677BC65C1DA7A496BBDCC02999E8814F10AFC4B8E0DD4E6687E0762CE717A5EA1E452B5C56065C8431F0FB9D23CFF3A4B4149798C0670AF7F9565A0EAE5CF1AB16A1F0C3DD5E485DC5ABB96EBE0B6778A15B7302CBCE358E4BF2E2E30932758AC6EFA9F5828");
+ bigint *arg2 = bi_clone(ctx, bi_x);
+ bigint *arg3 = bi_clone(ctx, bi_x);
+ bigint *sqr_result = bi_square(ctx, bi_x);
+ bigint *mlt_result = bi_multiply(ctx, arg2, arg3);
+
+ if (bi_compare(sqr_result, mlt_result) != 0)
+ {
+ bi_print("SQR_RESULT", sqr_result);
+ bi_print("MLT_RESULT", mlt_result);
+ bi_free(ctx, sqr_result);
+ bi_free(ctx, mlt_result);
+ goto end;
+ }
+
+ bi_free(ctx, sqr_result);
+ bi_free(ctx, mlt_result);
+ }
+
+ printf("All BIGINT tests passed\n");
+ res = 0;
+
+end:
+ return res;
+}
+
+/**************************************************************************
+ * RSA tests
+ *
+ * Use the results from openssl to verify PKCS1 etc
+ **************************************************************************/
+static int RSA_test(void)
+{
+ int res = 1;
+ const char *plaintext = /* 128 byte hex number */
+ "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee2"
+ "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeee2\012";
+ uint8_t enc_data[128], dec_data[128];
+ RSA_CTX *rsa_ctx = NULL;
+ BI_CTX *bi_ctx;
+ bigint *plaintext_bi;
+ bigint *enc_data_bi, *dec_data_bi;
+ uint8_t enc_data2[128], dec_data2[128];
+ int len;
+ uint8_t *buf;
+
+ RNG_initialize();
+
+ /* extract the private key elements */
+ len = get_file("../ssl/test/axTLS.key_1024", &buf);
+ if (asn1_get_private_key(buf, len, &rsa_ctx) < 0)
+ {
+ goto end;
+ }
+
+ free(buf);
+ bi_ctx = rsa_ctx->bi_ctx;
+ plaintext_bi = bi_import(bi_ctx,
+ (const uint8_t *)plaintext, strlen(plaintext));
+
+ /* basic rsa encrypt */
+ enc_data_bi = RSA_public(rsa_ctx, plaintext_bi);
+ bi_export(bi_ctx, bi_copy(enc_data_bi), enc_data, sizeof(enc_data));
+
+ /* basic rsa decrypt */
+ dec_data_bi = RSA_private(rsa_ctx, enc_data_bi);
+ bi_export(bi_ctx, dec_data_bi, dec_data, sizeof(dec_data));
+
+ if (memcmp(dec_data, plaintext, strlen(plaintext)))
+ {
+ printf("Error: DECRYPT #1 failed\n");
+ goto end;
+ }
+
+ if (RSA_encrypt(rsa_ctx, (const uint8_t *)"abc", 3, enc_data2, 0) < 0)
+ {
+ printf("Error: ENCRYPT #2 failed\n");
+ goto end;
+ }
+
+ RSA_decrypt(rsa_ctx, enc_data2, dec_data2, sizeof(dec_data2), 1);
+ if (memcmp("abc", dec_data2, 3))
+ {
+ printf("Error: DECRYPT #2 failed\n");
+ goto end;
+ }
+
+ RSA_free(rsa_ctx);
+ res = 0;
+ printf("All RSA tests passed\n");
+
+end:
+ RNG_terminate();
+ return res;
+}
+
+/**************************************************************************
+ * Cert Testing
+ *
+ **************************************************************************/
+static int cert_tests(void)
+{
+ int res = -1, len;
+ X509_CTX *x509_ctx;
+ SSL_CTX *ssl_ctx;
+ uint8_t *buf;
+
+ /* check a bunch of 3rd party certificates */
+ ssl_ctx = ssl_ctx_new(0, 0);
+ len = get_file("../ssl/test/microsoft.x509_ca", &buf);
+ if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0)
+ {
+ printf("Cert #1\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ free(buf);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ len = get_file("../ssl/test/thawte.x509_ca", &buf);
+ if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0)
+ {
+ printf("Cert #2\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ free(buf);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ len = get_file("../ssl/test/deutsche_telecom.x509_ca", &buf);
+ if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0)
+ {
+ printf("Cert #3\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ free(buf);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ len = get_file("../ssl/test/equifax.x509_ca", &buf);
+ if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0)
+ {
+ printf("Cert #4\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ free(buf);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ len = get_file("../ssl/test/gnutls.cer", &buf);
+ if ((res = add_cert(ssl_ctx, buf, len)) < 0)
+ {
+ printf("Cert #5\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ free(buf);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ len = get_file("../ssl/test/socgen.cer", &buf);
+ if ((res = add_cert(ssl_ctx, buf, len)) < 0)
+ {
+ printf("Cert #6\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ free(buf);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ if ((res = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT,
+ "../ssl/test/camster_duckdns_org.crt", NULL)) != SSL_OK)
+ {
+ printf("Cert #7\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ if ((res = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT,
+ "../ssl/test/comodo.sha384.cer", NULL)) != SSL_OK)
+ {
+ printf("Cert #8\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+
+ ssl_ctx = ssl_ctx_new(0, 0);
+ if ((res = ssl_obj_load(ssl_ctx,
+ SSL_OBJ_X509_CERT, "../ssl/test/ms_iis.cer", NULL)) != SSL_OK)
+ {
+ printf("Cert #9\n");
+ ssl_display_error(res);
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+
+ if (get_file("../ssl/test/qualityssl.com.der", &buf) < 0 ||
+ x509_new(buf, &len, &x509_ctx))
+ {
+ printf("Cert #10\n");
+ res = -1;
+ goto bad_cert;
+ }
+
+ if (strcmp(x509_ctx->subject_alt_dnsnames[1], "qualityssl.com"))
+ {
+ printf("Cert #11\n");
+ res = -1;
+ goto bad_cert;
+ }
+ x509_free(x509_ctx);
+ free(buf);
+
+ // this bundle has two DSA (1.2.840.10040.4.3 invalid) certificates
+ ssl_ctx = ssl_ctx_new(0, 0);
+ if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT,
+ "../ssl/test/ca-bundle.crt", NULL))
+ {
+ goto bad_cert;
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ res = 0; /* all ok */
+ printf("All Certificate tests passed\n");
+
+bad_cert:
+ if (res)
+ printf("Error: A certificate test failed\n");
+
+ return res;
+}
+
+/**
+ * init a server socket.
+ */
+static int server_socket_init(int *port)
+{
+ struct sockaddr_in serv_addr;
+ int server_fd;
+ char yes = 1;
+
+ /* Create socket for incoming connections */
+ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ return -1;
+ }
+
+ setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+
+go_again:
+ /* Construct local address structure */
+ memset(&serv_addr, 0, sizeof(serv_addr)); /* Zero out structure */
+ serv_addr.sin_family = AF_INET; /* Internet address family */
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
+ serv_addr.sin_port = htons(*port); /* Local port */
+
+ /* Bind to the local address */
+ if (bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
+ {
+ (*port)++;
+ goto go_again;
+ }
+ /* Mark the socket so it will listen for incoming connections */
+ if (listen(server_fd, 3000) < 0)
+ {
+ return -1;
+ }
+
+ return server_fd;
+}
+
+/**
+ * init a client socket.
+ */
+static int client_socket_init(uint16_t port)
+{
+ struct sockaddr_in address;
+ int client_fd;
+
+ address.sin_family = AF_INET;
+ address.sin_port = htons(port);
+ address.sin_addr.s_addr = inet_addr("127.0.0.1");
+ client_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
+ {
+ perror("socket");
+ SOCKET_CLOSE(client_fd);
+ client_fd = -1;
+ }
+
+ return client_fd;
+}
+
+/**************************************************************************
+ * SSL Server Testing
+ *
+ **************************************************************************/
+typedef struct
+{
+ /* not used as yet */
+ int dummy;
+} SVR_CTX;
+
+typedef struct
+{
+ const char *testname;
+ const char *openssl_option;
+} client_t;
+
+static void do_client(client_t *clnt)
+{
+ char openssl_buf[2048];
+ usleep(200000); /* allow server to start */
+
+ /* show the session ids in the reconnect test */
+ if (strcmp(clnt->testname, "Session Reuse") == 0)
+ {
+ sprintf(openssl_buf, "echo \"hello client\" | openssl s_client "
+ "-connect localhost:%d %s 2>&1 | grep \"Session-ID:\"",
+ g_port, clnt->openssl_option);
+ }
+ else if (strstr(clnt->testname, "GNUTLS") == NULL)
+ {
+ sprintf(openssl_buf, "echo \"hello client\" | openssl s_client "
+#ifdef WIN32
+ "-connect localhost:%d -quiet %s",
+#else
+ "-connect localhost:%d -quiet %s > /dev/null 2>&1",
+#endif
+ g_port, clnt->openssl_option);
+ }
+ else /* gnutls */
+ {
+ sprintf(openssl_buf, "echo \"hello client\" | gnutls-cli "
+#ifdef WIN32
+ "-p %d %s localhost",
+#else
+ "-p %d %s localhost > /dev/null 2>&1",
+#endif
+ g_port, clnt->openssl_option);
+ }
+
+//printf("CLIENT %s\n", openssl_buf);
+ SYSTEM(openssl_buf);
+}
+
+static int SSL_server_test(
+ const char *testname,
+ const char *openssl_option,
+ const char *device_cert,
+ const char *product_cert,
+ const char *private_key,
+ const char *ca_cert,
+ const char *password,
+ int axtls_option)
+{
+ int server_fd, ret = 0;
+ SSL_CTX *ssl_ctx = NULL;
+ struct sockaddr_in client_addr;
+ uint8_t *read_buf;
+ socklen_t clnt_len = sizeof(client_addr);
+ client_t client_data;
+#ifndef WIN32
+ pthread_t thread;
+#endif
+ g_port++;
+
+ client_data.testname = testname;
+ client_data.openssl_option = openssl_option;
+
+ if ((server_fd = server_socket_init(&g_port)) < 0)
+ goto error;
+
+ if (private_key)
+ {
+ axtls_option |= SSL_NO_DEFAULT_KEY;
+ }
+
+ if ((ssl_ctx = ssl_ctx_new(axtls_option, SSL_DEFAULT_SVR_SESS)) == NULL)
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+
+ if (private_key)
+ {
+ int obj_type = SSL_OBJ_RSA_KEY;
+
+ if (strstr(private_key, ".p8"))
+ obj_type = SSL_OBJ_PKCS8;
+ else if (strstr(private_key, ".p12"))
+ obj_type = SSL_OBJ_PKCS12;
+
+ if (ssl_obj_load(ssl_ctx, obj_type, private_key, password))
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+ }
+
+ if (device_cert) /* test chaining */
+ {
+ if ((ret = ssl_obj_load(ssl_ctx,
+ SSL_OBJ_X509_CERT, device_cert, NULL)) != SSL_OK)
+ goto error;
+ }
+
+ if (product_cert) /* test chaining */
+ {
+ if ((ret = ssl_obj_load(ssl_ctx,
+ SSL_OBJ_X509_CERT, product_cert, NULL)) != SSL_OK)
+ goto error;
+ }
+
+ if (ca_cert) /* test adding certificate authorities */
+ {
+ if ((ret = ssl_obj_load(ssl_ctx,
+ SSL_OBJ_X509_CACERT, ca_cert, NULL)) != SSL_OK)
+ goto error;
+ }
+
+#ifndef WIN32
+ pthread_create(&thread, NULL,
+ (void *(*)(void *))do_client, (void *)&client_data);
+ pthread_detach(thread);
+#else
+ CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_client,
+ (LPVOID)&client_data, 0, NULL);
+#endif
+
+ for (;;)
+ {
+ int client_fd, size = 0;
+ SSL *ssl;
+
+ /* Wait for a client to connect */
+ if ((client_fd = accept(server_fd,
+ (struct sockaddr *)&client_addr, &clnt_len)) < 0)
+ {
+ ret = SSL_ERROR_SOCK_SETUP_FAILURE;
+ goto error;
+ }
+
+ /* we are ready to go */
+ ssl = ssl_server_new(ssl_ctx, client_fd);
+ while ((size = ssl_read(ssl, &read_buf)) == SSL_OK);
+ SOCKET_CLOSE(client_fd);
+
+ if (size == SSL_CLOSE_NOTIFY)
+ {
+ /* do nothing */
+ }
+ else if (size < SSL_OK) /* got some alert or something nasty */
+ {
+ ret = size;
+
+ if (ret == SSL_ERROR_CONN_LOST)
+ continue;
+
+ break; /* we've got a problem */
+ }
+ else /* looks more promising */
+ {
+ if (strstr("hello client", (char *)read_buf) == NULL)
+ {
+ printf("SSL server test \"%s\" passed\n", testname);
+ TTY_FLUSH();
+ ret = 0;
+ break;
+ }
+ }
+
+ ssl_free(ssl);
+ }
+
+ SOCKET_CLOSE(server_fd);
+
+error:
+ ssl_ctx_free(ssl_ctx);
+ return ret;
+}
+
+int SSL_server_tests(void)
+{
+ int ret = -1;
+ struct stat stat_buf;
+ SVR_CTX svr_test_ctx;
+ memset(&svr_test_ctx, 0, sizeof(SVR_CTX));
+
+ printf("### starting server tests\n"); TTY_FLUSH();
+
+ /* Go through the algorithms */
+
+ /*
+ * TLS client hello
+ */
+
+ /*
+ * AES128-SHA TLS1.2
+ */
+ if ((ret = SSL_server_test("AES128-SHA TLS1.2",
+ "-cipher AES128-SHA -tls1_2",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES256-SHA TLS1.2
+ */
+ if ((ret = SSL_server_test("AES256-SHA TLS1.2",
+ "-cipher AES256-SHA -tls1_2",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES128-SHA256 TLS1.2
+ */
+ if ((ret = SSL_server_test("AES128-SHA256 TLS1.2",
+ "-cipher AES128-SHA256 -tls1_2",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+
+ /*
+ * AES256-SHA256 TLS1.2
+ */
+ if ((ret = SSL_server_test("AES256-SHA256 TLS1.2",
+ "-cipher AES256-SHA256 -tls1_2",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES128-SHA TLS1.1
+ */
+ if ((ret = SSL_server_test("AES128-SHA TLS1.1",
+ "-cipher AES128-SHA -tls1_1",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES128-SHA TLS1.0
+ */
+ if ((ret = SSL_server_test("AES128-SHA TLS1.0",
+ "-cipher AES128-SHA -tls1",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * Session Reuse
+ * all the session id's should match for session resumption.
+ */
+ if ((ret = SSL_server_test("Session Reuse",
+ "-cipher AES128-SHA -reconnect -tls1_2",
+ DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL,
+ DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * 1024 bit RSA key (check certificate chaining)
+ */
+ if ((ret = SSL_server_test("1024 bit key",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_1024.cer", NULL,
+ "../ssl/test/axTLS.key_1024",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * 2048 bit RSA key
+ */
+ if ((ret = SSL_server_test("2048 bit key",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_2048.cer", NULL,
+ "../ssl/test/axTLS.key_2048",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * 4096 bit RSA key
+ */
+ if ((ret = SSL_server_test("4096 bit key",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_4096.cer", NULL,
+ "../ssl/test/axTLS.key_4096",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * RSA1024/SHA256
+ */
+ if ((ret = SSL_server_test("RSA1024/SHA256",
+ "-tls1_2",
+ "../ssl/test/axTLS.x509_1024_sha256.pem" , NULL,
+ "../ssl/test/axTLS.key_1024",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * RSA1024/SHA384
+ */
+ if ((ret = SSL_server_test("RSA1024/SHA384",
+ "-tls1_2",
+ "../ssl/test/axTLS.x509_1024_sha384.pem" , NULL,
+ "../ssl/test/axTLS.key_1024",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * RSA1024/SHA512
+ */
+ if ((ret = SSL_server_test("RSA1024/SHA512",
+ "-tls1_2",
+ "../ssl/test/axTLS.x509_1024_sha512.pem" , NULL,
+ "../ssl/test/axTLS.key_1024",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * Client Verification
+ */
+ if ((ret = SSL_server_test("Client Verification TLS1.2",
+ "-cipher AES128-SHA -tls1_2 "
+ "-cert ../ssl/test/axTLS.x509_2048.pem "
+ "-key ../ssl/test/axTLS.key_2048.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ "../ssl/test/axTLS.ca_x509.cer", NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)))
+ goto cleanup;
+
+ if ((ret = SSL_server_test("Client Verification TLS1.1",
+ "-cipher AES128-SHA -tls1_1 "
+ "-cert ../ssl/test/axTLS.x509_2048.pem "
+ "-key ../ssl/test/axTLS.key_2048.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ "../ssl/test/axTLS.ca_x509.cer", NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)))
+ goto cleanup;
+
+ /* this test should fail */
+ if (stat("../ssl/test/axTLS.x509_bad_before.pem", &stat_buf) >= 0)
+ {
+ if ((ret = SSL_server_test("Error: Bad Before Cert",
+ "-cipher AES128-SHA -tls1_2 "
+ "-cert ../ssl/test/axTLS.x509_bad_before.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ "../ssl/test/axTLS.ca_x509.cer", NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) !=
+ SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID))
+ goto cleanup;
+
+ printf("SSL server test \"%s\" passed\n", "Bad Before Cert");
+ TTY_FLUSH();
+ }
+
+ /* this test should fail */
+ if ((ret = SSL_server_test("Error: Bad After Cert",
+ "-cipher AES128-SHA -tls1_2 "
+ "-cert ../ssl/test/axTLS.x509_bad_after.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ "../ssl/test/axTLS.ca_x509.cer", NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) !=
+ SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED))
+ goto cleanup;
+
+ printf("SSL server test \"%s\" passed\n", "Bad After Cert");
+ TTY_FLUSH();
+
+ /*
+ * No trusted cert
+ */
+ if ((ret = SSL_server_test("Error: No trusted certificate",
+ "-cipher AES128-SHA -tls1_2 "
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ NULL, NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) !=
+ SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT))
+ goto cleanup;
+
+ printf("SSL server test \"%s\" passed\n", "No trusted certificate");
+ TTY_FLUSH();
+
+ /*
+ * Self-signed (from the server)
+ */
+ if ((ret = SSL_server_test("Error: Self-signed certificate (from server)",
+ "-cipher AES128-SHA -tls1_2 "
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem "
+ "-CAfile ../ssl/test/axTLS.ca_x509.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ NULL, NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) !=
+ SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED))
+ goto cleanup;
+
+ printf("SSL server test \"%s\" passed\n",
+ "Self-signed certificate (from server)");
+ TTY_FLUSH();
+
+ /*
+ * Self-signed (from the client)
+ */
+ if ((ret = SSL_server_test("Self-signed certificate (from client)",
+ "-cipher AES128-SHA -tls1_2 "
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem ",
+ NULL,
+ "../ssl/test/axTLS.x509_1024.pem",
+ "../ssl/test/axTLS.key_1024.pem",
+ "../ssl/test/axTLS.ca_x509.cer",
+ NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)))
+ goto cleanup;
+
+ /*
+ * Key in PEM format
+ */
+ if ((ret = SSL_server_test("Key in PEM format",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_1024.cer", NULL,
+ "../ssl/test/axTLS.key_1024.pem", NULL,
+ NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * Cert in PEM format
+ */
+ if ((ret = SSL_server_test("Cert in PEM format",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_1024.pem", NULL,
+ "../ssl/test/axTLS.key_1024.pem", NULL,
+ NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * Cert chain in PEM format
+ */
+ if ((ret = SSL_server_test("Cert chain in PEM format",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_device.pem",
+ NULL, "../ssl/test/axTLS.key_device.pem",
+ "../ssl/test/axTLS.ca_x509.pem", NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES128 Encrypted key
+ */
+ if ((ret = SSL_server_test("AES128 encrypted key",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_aes128.pem", NULL,
+ "../ssl/test/axTLS.key_aes128.pem",
+ NULL, "abcd", DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES256 Encrypted key
+ */
+ if ((ret = SSL_server_test("AES256 encrypted key",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_aes256.pem", NULL,
+ "../ssl/test/axTLS.key_aes256.pem",
+ NULL, "abcd", DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * AES128 Encrypted invalid key
+ */
+ if ((ret = SSL_server_test("AES128 encrypted invalid key",
+ "-cipher AES128-SHA -tls1_2",
+ "../ssl/test/axTLS.x509_aes128.pem", NULL,
+ "../ssl/test/axTLS.key_aes128.pem",
+ NULL, "xyz", DEFAULT_SVR_OPTION)) != SSL_ERROR_INVALID_KEY)
+ goto cleanup;
+
+ printf("SSL server test \"%s\" passed\n", "AES128 encrypted invalid key");
+ TTY_FLUSH();
+
+ /*
+ * PKCS#8 key (encrypted)
+ */
+ if ((ret = SSL_server_test("pkcs#8 encrypted",
+ "-cipher AES128-SHA -tls1_2",
+ DEFAULT_CERT, NULL, "../ssl/test/axTLS.encrypted.p8",
+ NULL, "abcd", DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * PKCS#8 key (unencrypted DER format)
+ */
+ if ((ret = SSL_server_test("pkcs#8 DER unencrypted",
+ "-cipher AES128-SHA -tls1_2",
+ DEFAULT_CERT, NULL, "../ssl/test/axTLS.unencrypted.p8",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * PKCS#8 key (unencrypted PEM format)
+ */
+ if ((ret = SSL_server_test("pkcs#8 PEM unencrypted",
+ "-cipher AES128-SHA -tls1_2",
+ DEFAULT_CERT, NULL, "../ssl/test/axTLS.unencrypted_pem.p8",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * PKCS#12 key/certificate
+ */
+ if ((ret = SSL_server_test("pkcs#12 with CA", "-cipher AES128-SHA",
+ NULL, NULL, "../ssl/test/axTLS.withCA.p12",
+ NULL, "abcd", DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ if ((ret = SSL_server_test("pkcs#12 no CA", "-cipher AES128-SHA",
+ DEFAULT_CERT, NULL, "../ssl/test/axTLS.withoutCA.p12",
+ NULL, "abcd", DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ /*
+ * GNUTLS
+ */
+ if ((ret = SSL_server_test("GNUTLS client",
+ "",
+ "../ssl/test/axTLS.x509_1024.cer", NULL,
+ "../ssl/test/axTLS.key_1024",
+ NULL, NULL, DEFAULT_SVR_OPTION)))
+ goto cleanup;
+
+ if ((ret = SSL_server_test("GNUTLS client with verify",
+ "--x509certfile ../ssl/test/axTLS.x509_1024.pem "
+ "--x509keyfile ../ssl/test/axTLS.key_1024.pem",
+ "../ssl/test/axTLS.x509_1024.cer", NULL,
+ "../ssl/test/axTLS.key_1024",
+ "../ssl/test/axTLS.ca_x509.cer", NULL,
+ DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)))
+ goto cleanup;
+ ret = 0;
+
+cleanup:
+ if (ret)
+ {
+ printf("Error: A server test failed\n");
+ ssl_display_error(ret);
+ exit(1);
+ }
+ else
+ {
+ printf("All server tests passed\n"); TTY_FLUSH();
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ * SSL Client Testing
+ *
+ **************************************************************************/
+typedef struct
+{
+ uint8_t session_id[SSL_SESSION_ID_SIZE];
+#ifndef WIN32
+ pthread_t server_thread;
+#endif
+ int start_server;
+ int stop_server;
+ int do_reneg;
+} CLNT_SESSION_RESUME_CTX;
+
+typedef struct
+{
+ const char *testname;
+ const char *openssl_option;
+ int do_gnutls;
+} server_t;
+
+static void do_server(server_t *svr)
+{
+ char openssl_buf[2048];
+#ifndef WIN32
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+#endif
+ if (svr->do_gnutls)
+ {
+ sprintf(openssl_buf, "gnutls-serv "
+ "-p %d --quiet %s ", g_port, svr->openssl_option);
+ }
+ else
+ {
+ sprintf(openssl_buf, "openssl s_server "
+#ifdef WIN32
+ "-accept %d -quiet %s",
+#else
+ "-accept %d -quiet %s > /dev/null",
+#endif
+ g_port, svr->openssl_option);
+ }
+//printf("SERVER %s\n", openssl_buf);
+ SYSTEM(openssl_buf);
+}
+
+static int SSL_client_test(
+ const char *test,
+ SSL_CTX **ssl_ctx,
+ const char *openssl_option,
+ CLNT_SESSION_RESUME_CTX *sess_resume,
+ uint32_t client_options,
+ const char *private_key,
+ const char *password,
+ const char *cert)
+{
+ server_t server_data;
+ SSL *ssl = NULL;
+ int client_fd = -1;
+ uint8_t *session_id = NULL;
+ int ret = 1;
+#ifndef WIN32
+ pthread_t thread;
+#endif
+
+ server_data.do_gnutls = strstr(test, "GNUTLS") != NULL;
+
+ if (sess_resume == NULL || sess_resume->start_server)
+ {
+ g_port++;
+ server_data.openssl_option = openssl_option;
+
+#ifndef WIN32
+ pthread_create(&thread, NULL,
+ (void *(*)(void *))do_server, (void *)&server_data);
+ pthread_detach(thread);
+#else
+ CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_server,
+ (LPVOID)&server_data, 0, NULL);
+#endif
+ }
+
+ usleep(200000); /* allow server to start */
+
+ if (*ssl_ctx == NULL)
+ {
+ if (private_key)
+ {
+ client_options |= SSL_NO_DEFAULT_KEY;
+ }
+
+ if ((*ssl_ctx = ssl_ctx_new(
+ client_options, SSL_DEFAULT_CLNT_SESS)) == NULL)
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto client_test_exit;
+ }
+
+ if (private_key)
+ {
+ int obj_type = SSL_OBJ_RSA_KEY;
+
+ if (strstr(private_key, ".p8"))
+ obj_type = SSL_OBJ_PKCS8;
+ else if (strstr(private_key, ".p12"))
+ obj_type = SSL_OBJ_PKCS12;
+
+ if (ssl_obj_load(*ssl_ctx, obj_type, private_key, password))
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto client_test_exit;
+ }
+ }
+
+ if (cert)
+ {
+ if ((ret = ssl_obj_load(*ssl_ctx,
+ SSL_OBJ_X509_CERT, cert, NULL)) != SSL_OK)
+ {
+ printf("could not add cert %s (%d)\n", cert, ret);
+ TTY_FLUSH();
+ goto client_test_exit;
+ }
+ }
+
+ if (ssl_obj_load(*ssl_ctx, SSL_OBJ_X509_CACERT,
+ "../ssl/test/axTLS.ca_x509.cer", NULL))
+ {
+ printf("could not add cert auth\n"); TTY_FLUSH();
+ goto client_test_exit;
+ }
+ }
+
+ if (sess_resume && !sess_resume->start_server)
+ {
+ session_id = sess_resume->session_id;
+ }
+
+ if ((client_fd = client_socket_init(g_port)) < 0)
+ {
+ printf("could not start socket on %d\n", g_port); TTY_FLUSH();
+ goto client_test_exit;
+ }
+
+ ssl = ssl_client_new(*ssl_ctx, client_fd,
+ session_id, sizeof(session_id), NULL);
+
+ /* check the return status */
+ if ((ret = ssl_handshake_status(ssl)))
+ goto client_test_exit;
+
+ /* renegotiate client */
+ if (sess_resume && sess_resume->do_reneg)
+ {
+ if (ssl_renegotiate(ssl) == -SSL_ALERT_NO_RENEGOTIATION)
+ ret = 0;
+ else
+ ret = -SSL_ALERT_NO_RENEGOTIATION;
+
+ goto client_test_exit;
+ }
+
+ if (sess_resume)
+ {
+ memcpy(sess_resume->session_id,
+ ssl_get_session_id(ssl), SSL_SESSION_ID_SIZE);
+ }
+
+ if (IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER) &&
+ (ret = ssl_verify_cert(ssl)))
+ {
+ goto client_test_exit;
+ }
+
+ ssl_write(ssl, (uint8_t *)"hello world\n", 13);
+ if (sess_resume)
+ {
+ const uint8_t *sess_id = ssl_get_session_id(ssl);
+ int i;
+
+ printf(" Session-ID: ");
+ for (i = 0; i < SSL_SESSION_ID_SIZE; i++)
+ {
+ printf("%02X", sess_id[i]);
+ }
+ printf("\n");
+ TTY_FLUSH();
+ }
+
+ ret = 0;
+
+client_test_exit:
+ ssl_free(ssl);
+ SOCKET_CLOSE(client_fd);
+ usleep(200000); /* allow openssl to say something */
+
+ if (sess_resume)
+ {
+ if (sess_resume->stop_server)
+ {
+ ssl_ctx_free(*ssl_ctx);
+ *ssl_ctx = NULL;
+ }
+ else if (sess_resume->start_server)
+ {
+#ifndef WIN32
+ sess_resume->server_thread = thread;
+#endif
+ }
+ }
+ else
+ {
+ ssl_ctx_free(*ssl_ctx);
+ *ssl_ctx = NULL;
+ }
+
+ if (ret == 0)
+ {
+ printf("SSL client test \"%s\" passed\n", test);
+ TTY_FLUSH();
+ }
+
+ return ret;
+}
+
+int SSL_client_tests(void)
+{
+ int ret = -1;
+ SSL_CTX *ssl_ctx = NULL;
+ CLNT_SESSION_RESUME_CTX sess_resume;
+ memset(&sess_resume, 0, sizeof(CLNT_SESSION_RESUME_CTX));
+
+ sess_resume.start_server = 1;
+ printf("### starting client tests\n");
+
+ if ((ret = SSL_client_test("1024 bit key",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem",
+ &sess_resume,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ /* all the session id's should match for session resumption */
+ sess_resume.start_server = 0;
+ if ((ret = SSL_client_test("Client session resumption #1",
+ &ssl_ctx, NULL, &sess_resume,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ // no client renegotiation
+ sess_resume.do_reneg = 1;
+ // test relies on openssl killing the call
+ if ((ret = SSL_client_test("Client renegotiation",
+ &ssl_ctx, NULL, &sess_resume,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+ sess_resume.do_reneg = 0;
+
+ sess_resume.stop_server = 1;
+ if ((ret = SSL_client_test("Client session resumption #2",
+ &ssl_ctx, NULL, &sess_resume,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("1024 bit key",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("2048 bit key",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_2048.pem "
+ "-key ../ssl/test/axTLS.key_2048.pem", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("4096 bit key",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_4096.pem "
+ "-key ../ssl/test/axTLS.key_4096.pem", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("TLS 1.1",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem -tls1_1", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("TLS 1.0",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_1024.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem -tls1", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("Basic Constraint - len OK",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_end_chain.pem -key "
+ "../ssl/test/axTLS.key_end_chain.pem -CAfile "
+ "../ssl/test/axTLS.x509_intermediate_ca.pem",
+ NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ if ((ret = SSL_client_test("Basic Constraint - len NOT OK",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_end_chain_bad.pem -key "
+ "../ssl/test/axTLS.key_end_chain.pem -CAfile "
+ "../ssl/test/axTLS.x509_intermediate_ca2.pem",
+ NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL))
+ != SSL_X509_ERROR(X509_VFY_ERROR_BASIC_CONSTRAINT))
+ {
+ printf("*** Error: %d\n", ret);
+ if (ret == 0)
+ ret = SSL_NOT_OK;
+
+ goto cleanup;
+ }
+
+ printf("SSL server test \"%s\" passed\n", "Basic Constraint - len NOT OK");
+
+ if ((ret = SSL_client_test("Server cert chaining",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_device.pem "
+ "-key ../ssl/test/axTLS.key_device.pem "
+ "-CAfile ../ssl/test/axTLS.x509_1024.pem ", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)))
+ goto cleanup;
+
+ /* Check the server can verify the client */
+ if ((ret = SSL_client_test("Client peer authentication",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_2048.pem "
+ "-key ../ssl/test/axTLS.key_2048.pem "
+ "-CAfile ../ssl/test/axTLS.ca_x509.pem "
+ "-verify 1 ", NULL, DEFAULT_CLNT_OPTION,
+ "../ssl/test/axTLS.key_1024", NULL,
+ "../ssl/test/axTLS.x509_1024.cer")))
+ goto cleanup;
+
+ /* Check the server can verify the client */
+ if ((ret = SSL_client_test("Client peer authentication TLS1.1",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_2048.pem "
+ "-key ../ssl/test/axTLS.key_2048.pem "
+ "-CAfile ../ssl/test/axTLS.ca_x509.pem "
+ "-verify 1 -tls1_1", NULL, DEFAULT_CLNT_OPTION,
+ "../ssl/test/axTLS.key_1024", NULL,
+ "../ssl/test/axTLS.x509_1024.cer")))
+ goto cleanup;
+
+ /* Should get an "ERROR" from openssl (as the handshake fails as soon as
+ * the certificate verification fails) */
+ if ((ret = SSL_client_test("Error: Expired cert (verify now)",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_bad_after.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem", NULL,
+ DEFAULT_CLNT_OPTION, NULL, NULL, NULL)) !=
+ SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED))
+ {
+ printf("*** Error: %d\n", ret);
+ goto cleanup;
+ }
+
+ printf("SSL client test \"Expired cert (verify now)\" passed\n");
+
+ /* There is no "ERROR" from openssl */
+ if ((ret = SSL_client_test("Error: Expired cert (verify later)",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_bad_after.pem "
+ "-key ../ssl/test/axTLS.key_1024.pem", NULL,
+ DEFAULT_CLNT_OPTION|SSL_SERVER_VERIFY_LATER, NULL,
+ NULL, NULL)) != SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED))
+ {
+ printf("*** Error: %d\n", ret); TTY_FLUSH();
+ goto cleanup;
+ }
+
+ printf("SSL client test \"Expired cert (verify later)\" passed\n");
+
+ /* invalid cert type */
+ /*if ((ret = SSL_client_test("Error: Invalid certificate type",
+ &ssl_ctx,
+ "-cert ../ssl/test/axTLS.x509_2048.pem "
+ "-key ../ssl/test/axTLS.key_2048.pem "
+ "-CAfile ../ssl/test/axTLS.ca_x509.pem "
+ "-verify 1 ", NULL, DEFAULT_CLNT_OPTION,
+ "../ssl/test/axTLS.key_1024.pem", NULL,
+ "../ssl/test/axTLS.x509_1024.pem"))
+ != SSL_ERROR_INVALID_KEY)
+ {
+ if (ret == 0)
+ ret = SSL_NOT_OK;
+
+ printf("*** Error: %d\n", ret); TTY_FLUSH();
+ goto cleanup;
+ }
+
+ printf("SSL client test \"Invalid certificate type\" passed\n"); */
+
+ if ((ret = SSL_client_test("GNUTLS client",
+ &ssl_ctx,
+ "--x509certfile ../ssl/test/axTLS.x509_1024.pem "
+ "--x509keyfile ../ssl/test/axTLS.key_1024.pem -g", NULL,
+ DEFAULT_CLNT_OPTION,
+ "../ssl/test/axTLS.key_1024.pem", NULL,
+ "../ssl/test/axTLS.x509_1024.pem")))
+ goto cleanup;
+
+ ret = 0;
+
+ if ((ret = SSL_client_test("GNUTLS client with verify",
+ &ssl_ctx,
+ "--x509certfile ../ssl/test/axTLS.x509_1024.pem "
+ "--x509keyfile ../ssl/test/axTLS.key_1024.pem -r -g", NULL,
+ DEFAULT_CLNT_OPTION|SSL_SERVER_VERIFY_LATER,
+ "../ssl/test/axTLS.key_1024.pem", NULL,
+ "../ssl/test/axTLS.x509_1024.pem")))
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ if (ret)
+ {
+ ssl_display_error(ret);
+ printf("Error: A client test failed\n");
+ SYSTEM("sh ../ssl/test/killopenssl.sh");
+ SYSTEM("sh ../ssl/test/killgnutls.sh");
+ exit(1);
+ }
+ else
+ {
+ printf("All client tests passed\n"); TTY_FLUSH();
+ }
+
+ ssl_ctx_free(ssl_ctx);
+ return ret;
+}
+
+/**************************************************************************
+ * SSL Basic Testing (test a big packet handshake)
+ *
+ **************************************************************************/
+static uint8_t basic_buf[256*1024];
+
+static void do_basic(void)
+{
+ int client_fd;
+ SSL *ssl_clnt;
+ SSL_CTX *ssl_clnt_ctx = ssl_ctx_new(
+ DEFAULT_CLNT_OPTION, SSL_DEFAULT_CLNT_SESS);
+ usleep(200000); /* allow server to start */
+
+ if ((client_fd = client_socket_init(g_port)) < 0)
+ goto error;
+
+ if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT,
+ "../ssl/test/axTLS.ca_x509.cer", NULL))
+ goto error;
+
+ ssl_clnt = ssl_client_new(ssl_clnt_ctx, client_fd, NULL, 0, NULL);
+
+ /* check the return status */
+ if (ssl_handshake_status(ssl_clnt) < 0)
+ {
+ ssl_display_error(ssl_handshake_status(ssl_clnt));
+ goto error;
+ }
+
+ ssl_write(ssl_clnt, basic_buf, sizeof(basic_buf));
+ ssl_free(ssl_clnt);
+
+error:
+ ssl_ctx_free(ssl_clnt_ctx);
+ SOCKET_CLOSE(client_fd);
+
+ /* exit this thread */
+}
+
+static int SSL_basic_test(void)
+{
+ int server_fd, client_fd, ret = 0, size = 0, offset = 0;
+ SSL_CTX *ssl_svr_ctx = NULL;
+ struct sockaddr_in client_addr;
+ uint8_t *read_buf;
+ socklen_t clnt_len = sizeof(client_addr);
+ SSL *ssl_svr;
+#ifndef WIN32
+ pthread_t thread;
+#endif
+ memset(basic_buf, 0xA5, sizeof(basic_buf)/2);
+ memset(&basic_buf[sizeof(basic_buf)/2], 0x5A, sizeof(basic_buf)/2);
+
+ if ((server_fd = server_socket_init(&g_port)) < 0)
+ goto error;
+
+ ssl_svr_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS);
+ if ((ret = ssl_obj_load(ssl_svr_ctx, SSL_OBJ_X509_CERT,
+ "../ssl/test/axTLS.x509_1024.pem", NULL)) != SSL_OK)
+ goto error;
+
+ if ((ret = ssl_obj_load(ssl_svr_ctx, SSL_OBJ_RSA_KEY,
+ "../ssl/test/axTLS.key_1024.pem", NULL)) != SSL_OK)
+ goto error;
+#ifndef WIN32
+ pthread_create(&thread, NULL,
+ (void *(*)(void *))do_basic, NULL);
+ pthread_detach(thread);
+#else
+ CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_basic, NULL, 0, NULL);
+#endif
+
+ /* Wait for a client to connect */
+ if ((client_fd = accept(server_fd,
+ (struct sockaddr *) &client_addr, &clnt_len)) < 0)
+ {
+ ret = SSL_ERROR_SOCK_SETUP_FAILURE;
+ goto error;
+ }
+
+ /* we are ready to go */
+ ssl_svr = ssl_server_new(ssl_svr_ctx, client_fd);
+
+ do
+ {
+ while ((size = ssl_read(ssl_svr, &read_buf)) == SSL_OK);
+
+ if (size < SSL_OK) /* got some alert or something nasty */
+ {
+ ssl_display_error(size);
+ ret = size;
+ break;
+ }
+ else /* looks more promising */
+ {
+ if (memcmp(read_buf, &basic_buf[offset], size) != 0)
+ {
+ ret = SSL_NOT_OK;
+ break;
+ }
+ }
+
+ offset += size;
+ } while (offset < sizeof(basic_buf));
+
+ printf(ret == SSL_OK && offset == sizeof(basic_buf) ?
+ "SSL basic test passed\n" :
+ "SSL basic test failed\n");
+ TTY_FLUSH();
+
+ ssl_free(ssl_svr);
+ SOCKET_CLOSE(server_fd);
+ SOCKET_CLOSE(client_fd);
+
+error:
+ ssl_ctx_free(ssl_svr_ctx);
+ return ret;
+}
+
+/**************************************************************************
+ * SSL unblocked case
+ *
+ **************************************************************************/
+static void do_unblocked(void)
+{
+ int client_fd;
+ SSL *ssl_clnt;
+ SSL_CTX *ssl_clnt_ctx = ssl_ctx_new(
+ DEFAULT_CLNT_OPTION,
+ SSL_DEFAULT_CLNT_SESS |
+ SSL_CONNECT_IN_PARTS);
+ usleep(200000); /* allow server to start */
+
+ if ((client_fd = client_socket_init(g_port)) < 0)
+ goto error;
+
+ {
+#ifdef WIN32
+ u_long argp = 1;
+ ioctlsocket(client_fd, FIONBIO, &argp);
+#else
+ int flags = fcntl(client_fd, F_GETFL, NULL);
+ fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
+#endif
+ }
+
+ if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT,
+ "../ssl/test/axTLS.ca_x509.cer", NULL))
+ goto error;
+
+ ssl_clnt = ssl_client_new(ssl_clnt_ctx, client_fd, NULL, 0, NULL);
+
+ while (ssl_handshake_status(ssl_clnt) != SSL_OK)
+ {
+ if (ssl_read(ssl_clnt, NULL) < 0)
+ {
+ ssl_display_error(ssl_handshake_status(ssl_clnt));
+ goto error;
+ }
+ }
+
+ ssl_write(ssl_clnt, basic_buf, sizeof(basic_buf));
+ ssl_free(ssl_clnt);
+
+error:
+ ssl_ctx_free(ssl_clnt_ctx);
+ SOCKET_CLOSE(client_fd);
+
+ /* exit this thread */
+}
+
+static int SSL_unblocked_test(void)
+{
+ int server_fd, client_fd, ret = 0, size = 0, offset = 0;
+ SSL_CTX *ssl_svr_ctx = NULL;
+ struct sockaddr_in client_addr;
+ uint8_t *read_buf;
+ socklen_t clnt_len = sizeof(client_addr);
+ SSL *ssl_svr;
+#ifndef WIN32
+ pthread_t thread;
+#endif
+ memset(basic_buf, 0xA5, sizeof(basic_buf)/2);
+ memset(&basic_buf[sizeof(basic_buf)/2], 0x5A, sizeof(basic_buf)/2);
+
+ if ((server_fd = server_socket_init(&g_port)) < 0)
+ goto error;
+
+ ssl_svr_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS);
+ if ((ret = ssl_obj_load(ssl_svr_ctx, SSL_OBJ_X509_CERT,
+ "../ssl/test/axTLS.x509_1024.pem", NULL)) != SSL_OK)
+ goto error;
+
+ if ((ret = ssl_obj_load(ssl_svr_ctx, SSL_OBJ_RSA_KEY,
+ "../ssl/test/axTLS.key_1024.pem", NULL)) != SSL_OK)
+ goto error;
+
+#ifndef WIN32
+ pthread_create(&thread, NULL,
+ (void *(*)(void *))do_unblocked, NULL);
+ pthread_detach(thread);
+#else
+ CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_unblocked,
+ NULL, 0, NULL);
+#endif
+
+ /* Wait for a client to connect */
+ if ((client_fd = accept(server_fd,
+ (struct sockaddr *) &client_addr, &clnt_len)) < 0)
+ {
+ ret = SSL_ERROR_SOCK_SETUP_FAILURE;
+ goto error;
+ }
+
+ /* we are ready to go */
+ ssl_svr = ssl_server_new(ssl_svr_ctx, client_fd);
+
+ do
+ {
+ while ((size = ssl_read(ssl_svr, &read_buf)) == SSL_OK);
+
+ if (size < SSL_OK) /* got some alert or something nasty */
+ {
+ ssl_display_error(size);
+ ret = size;
+ break;
+ }
+ else /* looks more promising */
+ {
+ if (memcmp(read_buf, &basic_buf[offset], size) != 0)
+ {
+ ret = SSL_NOT_OK;
+ break;
+ }
+ }
+
+ offset += size;
+ } while (offset < sizeof(basic_buf));
+
+ printf(ret == SSL_OK && offset == sizeof(basic_buf) ?
+ "SSL unblocked test passed\n" :
+ "SSL unblocked test failed\n");
+ TTY_FLUSH();
+
+ ssl_free(ssl_svr);
+ SOCKET_CLOSE(server_fd);
+ SOCKET_CLOSE(client_fd);
+
+error:
+ ssl_ctx_free(ssl_svr_ctx);
+ return ret;
+}
+
+#if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING)
+/**************************************************************************
+ * Multi-Threading Tests
+ *
+ **************************************************************************/
+#define NUM_THREADS 100
+
+typedef struct
+{
+ SSL_CTX *ssl_clnt_ctx;
+ int port;
+ int thread_id;
+} multi_t;
+
+void do_multi_clnt(multi_t *multi_data)
+{
+ int res = 1, client_fd, i;
+ SSL *ssl = NULL;
+ char tmp[5];
+
+ if ((client_fd = client_socket_init(multi_data->port)) < 0)
+ goto client_test_exit;
+
+ usleep(200000);
+ ssl = ssl_client_new(multi_data->ssl_clnt_ctx, client_fd, NULL, 0, NULL);
+
+ if ((res = ssl_handshake_status(ssl)))
+ {
+ printf("Client ");
+ ssl_display_error(res);
+ goto client_test_exit;
+ }
+
+ sprintf(tmp, "%d\n", multi_data->thread_id);
+ for (i = 0; i < 10; i++)
+ ssl_write(ssl, (uint8_t *)tmp, strlen(tmp)+1);
+
+client_test_exit:
+ ssl_free(ssl);
+ SOCKET_CLOSE(client_fd);
+ free(multi_data);
+}
+
+void do_multi_svr(SSL *ssl)
+{
+ uint8_t *read_buf;
+ int *res_ptr = malloc(sizeof(int));
+ int res;
+
+ for (;;)
+ {
+ res = ssl_read(ssl, &read_buf);
+
+ /* kill the client */
+ if (res != SSL_OK)
+ {
+ if (res == SSL_ERROR_CONN_LOST)
+ {
+ SOCKET_CLOSE(ssl->client_fd);
+ ssl_free(ssl);
+ break;
+ }
+ else if (res > 0)
+ {
+ /* do nothing */
+ }
+ else /* some problem */
+ {
+ printf("Server ");
+ ssl_display_error(res);
+ goto error;
+ }
+ }
+ }
+
+ res = SSL_OK;
+error:
+ *res_ptr = res;
+ pthread_exit(res_ptr);
+}
+
+int multi_thread_test(void)
+{
+ int server_fd = -1;
+ SSL_CTX *ssl_server_ctx;
+ SSL_CTX *ssl_clnt_ctx;
+ pthread_t clnt_threads[NUM_THREADS];
+ pthread_t svr_threads[NUM_THREADS];
+ int i, res = 0;
+ struct sockaddr_in client_addr;
+ socklen_t clnt_len = sizeof(client_addr);
+
+ printf("Do multi-threading test (takes a minute)\n");
+
+ ssl_svr_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS);
+ if ((ret = ssl_obj_load(ssl_svr_ctx, SSL_OBJ_X509_CERT,
+ "../ssl/test/axTLS.x509_1024.pem", NULL)) != SSL_OK)
+ goto error;
+
+ if ((ret = ssl_obj_load(ssl_svr_ctx, SSL_OBJ_RSA_KEY,
+ "../ssl/test/axTLS.key_1024.pem", NULL)) != SSL_OK)
+ goto error;
+ ssl_clnt_ctx = ssl_ctx_new(DEFAULT_CLNT_OPTION, SSL_DEFAULT_CLNT_SESS);
+
+ if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT,
+ "../ssl/test/axTLS.ca_x509.cer", NULL))
+ goto error;
+
+ if ((server_fd = server_socket_init(&g_port)) < 0)
+ goto error;
+
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ multi_t *multi_data = (multi_t *)malloc(sizeof(multi_t));
+ multi_data->ssl_clnt_ctx = ssl_clnt_ctx;
+ multi_data->port = g_port;
+ multi_data->thread_id = i+1;
+ pthread_create(&clnt_threads[i], NULL,
+ (void *(*)(void *))do_multi_clnt, (void *)multi_data);
+ pthread_detach(clnt_threads[i]);
+ }
+
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ SSL *ssl_svr;
+ int client_fd = accept(server_fd,
+ (struct sockaddr *)&client_addr, &clnt_len);
+
+ if (client_fd < 0)
+ goto error;
+
+ ssl_svr = ssl_server_new(ssl_server_ctx, client_fd);
+
+ pthread_create(&svr_threads[i], NULL,
+ (void *(*)(void *))do_multi_svr, (void *)ssl_svr);
+ }
+
+ /* make sure we've run all of the threads */
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ void *thread_res;
+ pthread_join(svr_threads[i], &thread_res);
+
+ if (*((int *)thread_res) != 0)
+ res = 1;
+
+ free(thread_res);
+ }
+
+ if (res)
+ goto error;
+
+ printf("Multi-thread test passed (%d)\n", NUM_THREADS);
+error:
+ ssl_ctx_free(ssl_svr_ctx);
+ ssl_ctx_free(ssl_clnt_ctx);
+ SOCKET_CLOSE(server_fd);
+ return res;
+}
+#endif /* !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) */
+
+/**************************************************************************
+ * Header issue
+ *
+ **************************************************************************/
+//static void do_header_issue(void)
+//{
+// char axtls_buf[2048];
+//#ifndef WIN32
+// pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+//#endif
+// sprintf(axtls_buf, "./axssl s_client -connect localhost:%d", g_port);
+// SYSTEM(axtls_buf);
+//}
+//
+//static int header_issue(void)
+//{
+// FILE *f = fopen("../ssl/test/header_issue.dat", "r");
+// int server_fd = -1, client_fd = -1, ret = 1;
+// uint8_t buf[2048];
+// int size = 0;
+// struct sockaddr_in client_addr;
+// socklen_t clnt_len = sizeof(client_addr);
+//#ifndef WIN32
+// pthread_t thread;
+//#endif
+//
+// if (f == NULL || (server_fd = server_socket_init(&g_port)) < 0)
+// goto error;
+//
+//#ifndef WIN32
+// pthread_create(&thread, NULL,
+// (void *(*)(void *))do_header_issue, NULL);
+// pthread_detach(thread);
+//#else
+// CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_header_issue,
+// NULL, 0, NULL);
+//#endif
+// if ((client_fd = accept(server_fd,
+// (struct sockaddr *) &client_addr, &clnt_len)) < 0)
+// {
+// ret = SSL_ERROR_SOCK_SETUP_FAILURE;
+// goto error;
+// }
+//
+// size = fread(buf, 1, sizeof(buf), f);
+// if (SOCKET_WRITE(client_fd, buf, size) < 0)
+// {
+// ret = SSL_ERROR_SOCK_SETUP_FAILURE;
+// goto error;
+// }
+//
+// usleep(200000);
+//
+// ret = 0;
+//error:
+// fclose(f);
+// SOCKET_CLOSE(client_fd);
+// SOCKET_CLOSE(server_fd);
+// TTY_FLUSH();
+// SYSTEM("killall axssl");
+// return ret;
+//}
+
+/**************************************************************************
+ * main()
+ *
+ **************************************************************************/
+int main(int argc, char *argv[])
+{
+ int ret = 1;
+ BI_CTX *bi_ctx;
+ int fd;
+
+#ifdef WIN32
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSAStartup(wVersionRequested, &wsaData);
+ fd = _open("test_result.txt", O_WRONLY|O_TEMPORARY|O_CREAT, _S_IWRITE);
+ dup2(fd, 2); /* write stderr to this file */
+#else
+ fd = open("/dev/null", O_WRONLY); /* write stderr to /dev/null */
+ signal(SIGPIPE, SIG_IGN); /* ignore pipe errors */
+ dup2(fd, 2);
+#endif
+
+ /* can't do testing in this mode */
+#if defined CONFIG_SSL_GENERATE_X509_CERT
+ printf("Error: Must compile with default key/certificates\n");
+ exit(1);
+#endif
+
+ bi_ctx = bi_initialize();
+
+ if (AES_test(bi_ctx))
+ {
+ printf("AES tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (MD5_test(bi_ctx))
+ {
+ printf("MD5 tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (SHA1_test(bi_ctx))
+ {
+ printf("SHA1 tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (SHA256_test(bi_ctx))
+ {
+ printf("SHA256 tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (SHA384_test(bi_ctx))
+ {
+ printf("SHA384 tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (SHA512_test(bi_ctx))
+ {
+ printf("SHA512 tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (HMAC_test(bi_ctx))
+ {
+ printf("HMAC tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (BIGINT_test(bi_ctx))
+ {
+ printf("BigInt tests failed!\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ bi_terminate(bi_ctx);
+
+ if (RSA_test())
+ {
+ printf("RSA tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+ if (cert_tests())
+ {
+ printf("CERT tests failed\n");
+ goto cleanup;
+ }
+ TTY_FLUSH();
+
+#if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING)
+ if (multi_thread_test())
+ goto cleanup;
+#endif
+
+ if (SSL_basic_test())
+ goto cleanup;
+
+ SYSTEM("sh ../ssl/test/killopenssl.sh");
+
+ if (SSL_unblocked_test())
+ goto cleanup;
+
+ SYSTEM("sh ../ssl/test/killopenssl.sh");
+
+ if (SSL_client_tests())
+ goto cleanup;
+
+ SYSTEM("sh ../ssl/test/killopenssl.sh");
+ SYSTEM("sh ../ssl/test/killgnutls.sh");
+
+ if (SSL_server_tests())
+ goto cleanup;
+
+ SYSTEM("sh ../ssl/test/killopenssl.sh");
+
+// if (header_issue())
+// {
+// printf("Header tests failed\n"); TTY_FLUSH();
+// goto cleanup;
+// }
+
+ ret = 0; /* all ok */
+ printf("**** ALL TESTS PASSED ****\n"); TTY_FLUSH();
+cleanup:
+
+ if (ret)
+ printf("Error: Some tests failed!\n");
+
+ close(fd);
+ return ret;
+}
diff --git a/circuitpython/lib/axtls/ssl/test/test_axssl.sh b/circuitpython/lib/axtls/ssl/test/test_axssl.sh
new file mode 100755
index 0000000..acf11a6
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/test_axssl.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2007, Cameron Rich
+#
+# 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 axTLS project 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 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.
+#
+
+#
+# Test the various axssl bindings. To run it, got to the _install directory
+# and run this script from there.
+#
+
+if grep "CONFIG_PLATFORM_WIN32=y" "../config/.config" > /dev/null; then
+ JAVA_EXE="$JAVA_HOME/bin/java.exe"
+ PERL_BIN="/cygdrive/c/Perl/bin/perl"
+ KILL_AXSSL="kill %1"
+ KILL_CSHARP="kill %1"
+ KILL_PERL="kill %1"
+ KILL_JAVA="kill %1"
+ KILL_LUA="kill %1"
+else
+ if grep "CONFIG_PLATFORM_CYGWIN=y" "../config/.config" > /dev/null; then
+ # no .net or java on cygwin
+ PERL_BIN=/usr/bin/perl
+ KILL_AXSSL="killall axssl"
+ KILL_PERL="killall /usr/bin/perl"
+ KILL_LUA="killall /usr/local/bin/lua"
+ else # Linux
+ JAVA_EXE=/usr/java/default/bin/java
+ PERL_BIN=/usr/bin/perl
+ KILL_AXSSL="killall axssl"
+ KILL_CSHARP="killall mono"
+ KILL_PERL="killall /usr/bin/perl"
+ RUN_CSHARP="mono"
+ KILL_JAVA="killall $JAVA_EXE"
+ KILL_LUA="killall /usr/local/bin/lua"
+ fi
+fi
+
+BASE=..
+SERVER_ARGS="s_server -accept 15001 -verify -CAfile $BASE/ssl/test/axTLS.ca_x509.cer"
+CLIENT_ARGS="s_client -reconnect -connect localhost:15001 -verify -CAfile $BASE/ssl/test/axTLS.ca_x509.cer -key $BASE/ssl/test/axTLS.key_1024 -cert $BASE/ssl/test/axTLS.x509_1024.cer"
+
+# check pem arguments
+SERVER_PEM_ARGS="s_server -accept 15001 -pass abcd -key $BASE/ssl/test/axTLS.key_aes128.pem -cert $BASE/ssl/test/axTLS.x509_aes128.pem"
+CLIENT_PEM_ARGS="s_client -connect localhost:15001 -CAfile $BASE/ssl/test/axTLS.ca_x509.pem -key $BASE/ssl/test/axTLS.key_1024.pem -cert $BASE/ssl/test/axTLS.x509_1024.pem"
+
+export LD_LIBRARY_PATH=.:`perl -e 'use Config; print $Config{archlib};'`/CORE
+
+if [ -x ./axssl ]; then
+echo "############################# C SAMPLE ###########################"
+./axssl $SERVER_ARGS &
+echo "C Test passed" | ./axssl $CLIENT_ARGS
+$KILL_AXSSL
+sleep 1
+
+./axssl $SERVER_PEM_ARGS &
+echo "C Test passed" | ./axssl $CLIENT_PEM_ARGS
+$KILL_AXSSL
+sleep 1
+echo "### C tests complete"
+fi
+
+if [ -f ./axtls.jar ]; then
+echo "########################## JAVA SAMPLE ###########################"
+"$JAVA_EXE" -jar ./axtls.jar $SERVER_ARGS &
+echo "Java Test passed" | "$JAVA_EXE" -jar ./axtls.jar $CLIENT_ARGS
+$KILL_JAVA
+sleep 1
+
+"$JAVA_EXE" -jar ./axtls.jar $SERVER_PEM_ARGS &
+echo "Java Test passed" | "$JAVA_EXE" -jar ./axtls.jar $CLIENT_PEM_ARGS
+$KILL_JAVA
+sleep 1
+
+echo "### Java tests complete"
+fi
+
+if [ -x ./axssl.csharp.exe ]; then
+echo "############################ C# SAMPLE ###########################"
+$RUN_CSHARP ./axssl.csharp.exe $SERVER_ARGS &
+echo "C# Test passed" | $RUN_CSHARP ./axssl.csharp.exe $CLIENT_ARGS
+$KILL_CSHARP
+sleep 1
+
+$RUN_CSHARP ./axssl.csharp.exe $SERVER_PEM_ARGS &
+echo "C# Test passed" | $RUN_CSHARP ./axssl.csharp.exe $CLIENT_PEM_ARGS
+$KILL_CSHARP
+sleep 1
+
+echo "### C# tests complete"
+fi
+
+if [ -x ./axssl.vbnet.exe ]; then
+echo "######################## VB.NET SAMPLE ###########################"
+echo $SERVER_ARGS
+echo $CLIENT_ARGS
+./axssl.vbnet $SERVER_ARGS &
+echo "VB.NET Test passed" | ./axssl.vbnet.exe $CLIENT_ARGS
+kill %1
+sleep 1
+
+./axssl.vbnet $SERVER_PEM_ARGS &
+echo "VB.NET Test passed" | ./axssl.vbnet.exe $CLIENT_PEM_ARGS
+kill %1
+sleep 1
+echo "### VB.NET tests complete"
+fi
+
+if [ -f ./axssl.pl ]; then
+echo "########################## PERL SAMPLE ###########################"
+"$PERL_BIN" ./axssl.pl $SERVER_ARGS &
+echo "Perl Test passed" | "$PERL_BIN" ./axssl.pl $CLIENT_ARGS
+$KILL_PERL
+sleep 1
+
+"$PERL_BIN" ./axssl.pl $SERVER_PEM_ARGS &
+echo "Perl Test passed" | "$PERL_BIN" ./axssl.pl $CLIENT_PEM_ARGS
+$KILL_PERL
+sleep 1
+echo "### Perl tests complete"
+fi
+
+if [ -f ./axssl.lua ]; then
+echo "########################## LUA SAMPLE ###########################"
+./axssl.lua $SERVER_ARGS &
+echo "Lua Test passed" | ./axssl.lua $CLIENT_ARGS
+$KILL_LUA
+sleep 1
+
+./axssl.lua $SERVER_PEM_ARGS &
+echo "Lua Test passed" | ./axssl.lua $CLIENT_PEM_ARGS
+$KILL_LUA
+sleep 1
+echo "### Lua tests complete"
+fi
+
+echo "########################## ALL TESTS COMPLETE ###########################"
diff --git a/circuitpython/lib/axtls/ssl/test/thawte.x509_ca b/circuitpython/lib/axtls/ssl/test/thawte.x509_ca
new file mode 100644
index 0000000..59b1059
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/thawte.x509_ca
Binary files differ
diff --git a/circuitpython/lib/axtls/ssl/test/verisign.x509_ca.pem b/circuitpython/lib/axtls/ssl/test/verisign.x509_ca.pem
new file mode 100644
index 0000000..d5ef5d2
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/verisign.x509_ca.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmDCCAgECECCol67bggLewTagTia9h3MwDQYJKoZIhvcNAQECBQAwgYwxCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEwMC4GA1UECxMnRm9y
+IFRlc3QgUHVycG9zZXMgT25seS4gIE5vIGFzc3VyYW5jZXMuMTIwMAYDVQQDEylW
+ZXJpU2lnbiBUcmlhbCBTZWN1cmUgU2VydmVyIFRlc3QgUm9vdCBDQTAeFw0wNTAy
+MDkwMDAwMDBaFw0yNTAyMDgyMzU5NTlaMIGMMQswCQYDVQQGEwJVUzEXMBUGA1UE
+ChMOVmVyaVNpZ24sIEluYy4xMDAuBgNVBAsTJ0ZvciBUZXN0IFB1cnBvc2VzIE9u
+bHkuICBObyBhc3N1cmFuY2VzLjEyMDAGA1UEAxMpVmVyaVNpZ24gVHJpYWwgU2Vj
+dXJlIFNlcnZlciBUZXN0IFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBAJ8h98U7klaZH5cEn6CSEKmGWVBsTwHIaMAAVqGqCUn7Q9C10sEOIHBznyLy
+eSDjMs5M1nC/iAA7KCASf/yHz0AdlU+1IRSijwHTF/2dYSoTTxP2GCmtL1Ga4i7+
+zDDo086V7+NiFAGJj+CYey47ue4Xa33o/4YOA9PGL87oqFe7AgMBAAEwDQYJKoZI
+hvcNAQECBQADgYEAOq447rP5EDqFEl3vhLhgTbnyaskNYwPvxk+0grnQyDA4sF/q
+gK8nFlnvLmAOF3DmfuqW6WSr4zqTYzpwmJlsn48Om/yWirL8GuWRftit2POxTfHS
+B8VmR+PZx2k24UgWUZyojDGxJtiHd3tjCdqFgTit4NK429cWOcZrh47xeOI=
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/test/verisign.x509_my_cert.pem b/circuitpython/lib/axtls/ssl/test/verisign.x509_my_cert.pem
new file mode 100644
index 0000000..5b6c1ff
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/test/verisign.x509_my_cert.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEQzCCA6ygAwIBAgIQR/dXCzC/x5Ta5RvL6hKEojANBgkqhkiG9w0BAQUFADCB
+jDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTAwLgYDVQQL
+EydGb3IgVGVzdCBQdXJwb3NlcyBPbmx5LiAgTm8gYXNzdXJhbmNlcy4xMjAwBgNV
+BAMTKVZlcmlTaWduIFRyaWFsIFNlY3VyZSBTZXJ2ZXIgVGVzdCBSb290IENBMB4X
+DTA2MDExNjAwMDAwMFoXDTA2MDEzMDIzNTk1OVowgbkxCzAJBgNVBAYTAkFVMQww
+CgYDVQQIEwNRbGQxETAPBgNVBAcUCEJyaXNiYW5lMRkwFwYDVQQKFBBheG9sb1RM
+UyBQcm9qZWN0MRUwEwYDVQQLFAwxMDI0IGJpdCBrZXkxOjA4BgNVBAsUMVRlcm1z
+IG9mIHVzZSBhdCB3d3cudmVyaXNpZ24uY29tL2Nwcy90ZXN0Y2EgKGMpMDUxGzAZ
+BgNVBAMUEnd3dy5heG9sb3Rscy5jby5ucjCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAttzj5S7qfOZIrh9xg8bgjTOKbSIbLBuMnxAwfGRcUrQO2EQOHd6kMjXR
+hqY/cG2IG4G8AeqdV3nHlKbrbHbRa1lFgP6b0BQCE8TyxmP+tIAqn5L6/HTm+EEi
+Ad1Pxjeok6e7F6UXHxJltSGHmOhAf3C5kPq/FQ6QZeG4yD/uzPkCAwEAAaOCAXUw
+ggFxMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMEcGA1UdHwRAMD4wPKA6oDiGNmh0
+dHA6Ly9TVlJTZWN1cmUtY3JsLnZlcmlzaWduLmNvbS9TVlJUcmlhbFJvb3QyMDA1
+LmNybDBKBgNVHSAEQzBBMD8GCmCGSAGG+EUBBxUwMTAvBggrBgEFBQcCARYjaHR0
+cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nwcy90ZXN0Y2EwHQYDVR0lBBYwFAYIKwYB
+BQUHAwEGCCsGAQUFBwMCMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0
+cDovL29jc3AudmVyaXNpZ24uY29tMG0GCCsGAQUFBwEMBGEwX6FdoFswWTBXMFUW
+CWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgsexkuMCUW
+I2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMA0GCSqGSIb3DQEB
+BQUAA4GBACtlCTJFENCcHCQLHJfiotqr2XR+oWu0MstNm8dG6WB+zYprrT+kOPDn
+1rMO7YLx76f67fC+lIXz720kQHk6LsZ8hPBQvIXnfIsKjng73DeFzBmTMFz6Qxjd
++E0FUCKplqrdwUkmR4kH6O4pdGE4AlXJNiUI2903yYdSRVMOuLuR
+-----END CERTIFICATE-----
diff --git a/circuitpython/lib/axtls/ssl/tls1.c b/circuitpython/lib/axtls/ssl/tls1.c
new file mode 100755
index 0000000..8c21825
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/tls1.c
@@ -0,0 +1,2463 @@
+/*
+ * Copyright (c) 2007-2019, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * Common ssl/tlsv1 code to both the client and server implementations.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "os_port.h"
+#include "ssl.h"
+
+/* The session expiry time */
+#define SSL_EXPIRY_TIME (CONFIG_SSL_EXPIRY_TIME*3600)
+
+static const uint8_t g_hello_request[] = { HS_HELLO_REQUEST, 0, 0, 0 };
+static const uint8_t g_chg_cipher_spec_pkt[] = { 1 };
+static const char * server_finished = "server finished";
+static const char * client_finished = "client finished";
+
+static int do_handshake(SSL *ssl, uint8_t *buf, int read_len);
+static int set_key_block(SSL *ssl, int is_write);
+static int verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len);
+static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt);
+static int send_raw_packet(SSL *ssl, uint8_t protocol);
+static int check_certificate_chain(SSL *ssl);
+
+/**
+ * The server will pick the cipher based on the order that the order that the
+ * ciphers are listed. This order is defined at compile time.
+ */
+#ifndef CONFIG_SSL_SKELETON_MODE
+static void session_free(SSL_SESSION *ssl_sessions[], int sess_index);
+#endif
+
+const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] =
+#ifdef CONFIG_SSL_PROT_LOW /* low security, fast speed */
+{ SSL_AES128_SHA, SSL_AES128_SHA256, SSL_AES256_SHA, SSL_AES256_SHA256 };
+#elif CONFIG_SSL_PROT_MEDIUM /* medium security, medium speed */
+{ SSL_AES128_SHA256, SSL_AES256_SHA256, SSL_AES256_SHA, SSL_AES128_SHA };
+#else /* CONFIG_SSL_PROT_HIGH */ /* high security, low speed */
+{ SSL_AES256_SHA256, SSL_AES128_SHA256, SSL_AES256_SHA, SSL_AES128_SHA };
+#endif
+
+/**
+ * The cipher map containing all the essentials for each cipher.
+ */
+static const cipher_info_t cipher_info[NUM_PROTOCOLS] =
+{
+ { /* AES128-SHA */
+ SSL_AES128_SHA, /* AES128-SHA */
+ 16, /* key size */
+ 16, /* iv size */
+ 16, /* block padding size */
+ SHA1_SIZE, /* digest size */
+ 2*(SHA1_SIZE+16+16), /* key block size */
+ ssl_hmac_sha1, /* hmac algorithm */
+ (crypt_func)AES_cbc_encrypt, /* encrypt */
+ (crypt_func)AES_cbc_decrypt /* decrypt */
+ },
+ { /* AES256-SHA */
+ SSL_AES256_SHA, /* AES256-SHA */
+ 32, /* key size */
+ 16, /* iv size */
+ 16, /* block padding size */
+ SHA1_SIZE, /* digest size */
+ 2*(SHA1_SIZE+32+16), /* key block size */
+ ssl_hmac_sha1, /* hmac algorithm */
+ (crypt_func)AES_cbc_encrypt, /* encrypt */
+ (crypt_func)AES_cbc_decrypt /* decrypt */
+ },
+ { /* AES128-SHA256 */
+ SSL_AES128_SHA256, /* AES128-SHA256 */
+ 16, /* key size */
+ 16, /* iv size */
+ 16, /* block padding size */
+ SHA256_SIZE, /* digest size */
+ 2*(SHA256_SIZE+32+16), /* key block size */
+ hmac_sha256, /* hmac algorithm */
+ (crypt_func)AES_cbc_encrypt, /* encrypt */
+ (crypt_func)AES_cbc_decrypt /* decrypt */
+ },
+ { /* AES256-SHA256 */
+ SSL_AES256_SHA256, /* AES256-SHA256 */
+ 32, /* key size */
+ 16, /* iv size */
+ 16, /* block padding size */
+ SHA256_SIZE, /* digest size */
+ 2*(SHA256_SIZE+32+16), /* key block size */
+ hmac_sha256, /* hmac algorithm */
+ (crypt_func)AES_cbc_encrypt, /* encrypt */
+ (crypt_func)AES_cbc_decrypt /* decrypt */
+ }
+};
+
+static void prf(SSL *ssl, const uint8_t *sec, int sec_len,
+ uint8_t *seed, int seed_len,
+ uint8_t *out, int olen);
+static const cipher_info_t *get_cipher_info(uint8_t cipher);
+static void increment_read_sequence(SSL *ssl);
+static void increment_write_sequence(SSL *ssl);
+static void add_hmac_digest(SSL *ssl, int snd, uint8_t *hmac_header,
+ const uint8_t *buf, int buf_len, uint8_t *hmac_buf);
+
+/* win32 VC6.0 doesn't have variadic macros */
+#if defined(WIN32) && !defined(CONFIG_SSL_FULL_MODE)
+void DISPLAY_BYTES(SSL *ssl, const char *format,
+ const uint8_t *data, int size, ...) {}
+#endif
+
+/**
+ * Allocates new SSL extensions structure and returns pointer to it
+ *
+ */
+EXP_FUNC SSL_EXTENSIONS * STDCALL ssl_ext_new()
+{
+ return (SSL_EXTENSIONS *)calloc(1, sizeof(SSL_EXTENSIONS));
+}
+
+/**
+ * Allocates new SSL extensions structure and returns pointer to it
+ *
+ */
+EXP_FUNC void STDCALL ssl_ext_free(SSL_EXTENSIONS *ssl_ext)
+{
+ if (ssl_ext == NULL )
+ {
+ return;
+ }
+
+ free(ssl_ext);
+}
+
+/**
+ * Establish a new client/server context.
+ */
+EXP_FUNC SSL_CTX *STDCALL ssl_ctx_new(uint32_t options, int num_sessions)
+{
+ SSL_CTX *ssl_ctx = (SSL_CTX *)calloc(1, sizeof (SSL_CTX));
+ ssl_ctx->options = options;
+ RNG_initialize();
+
+#ifdef CONFIG_SSL_ENABLE_SERVER
+ if (load_key_certs(ssl_ctx) < 0)
+ {
+ free(ssl_ctx); /* can't load our key/certificate pair, so die */
+ return NULL;
+ }
+#endif
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ ssl_ctx->num_sessions = num_sessions;
+#endif
+
+ SSL_CTX_MUTEX_INIT(ssl_ctx->mutex);
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ if (num_sessions)
+ {
+ ssl_ctx->ssl_sessions = (SSL_SESSION **)
+ calloc(1, num_sessions*sizeof(SSL_SESSION *));
+ }
+#endif
+
+ return ssl_ctx;
+}
+
+/*
+ * Remove a client/server context.
+ */
+EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx)
+{
+ SSL *ssl;
+ int i;
+
+ if (ssl_ctx == NULL)
+ return;
+
+ ssl = ssl_ctx->head;
+
+ /* clear out all the ssl entries */
+ while (ssl)
+ {
+ SSL *next = ssl->next;
+ ssl_free(ssl);
+ ssl = next;
+ }
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ /* clear out all the sessions */
+ for (i = 0; i < ssl_ctx->num_sessions; i++)
+ session_free(ssl_ctx->ssl_sessions, i);
+
+ free(ssl_ctx->ssl_sessions);
+#endif
+
+ i = 0;
+ while (i < CONFIG_SSL_MAX_CERTS && ssl_ctx->certs[i].buf)
+ {
+ free(ssl_ctx->certs[i].buf);
+ ssl_ctx->certs[i++].buf = NULL;
+ }
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ remove_ca_certs(ssl_ctx->ca_cert_ctx);
+#endif
+ ssl_ctx->chain_length = 0;
+ SSL_CTX_MUTEX_DESTROY(ssl_ctx->mutex);
+ RSA_free(ssl_ctx->rsa_ctx);
+ RNG_terminate();
+ free(ssl_ctx);
+}
+
+/*
+ * Free any used resources used by this connection.
+ */
+EXP_FUNC void STDCALL ssl_free(SSL *ssl)
+{
+ SSL_CTX *ssl_ctx;
+
+ if (ssl == NULL) /* just ignore null pointers */
+ return;
+
+ /* only notify if we weren't notified first */
+ /* spec says we must notify when we are dying */
+ if (!IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY))
+ send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY);
+
+ ssl_ctx = ssl->ssl_ctx;
+
+ SSL_CTX_LOCK(ssl_ctx->mutex);
+
+ /* adjust the server SSL list */
+ if (ssl->prev)
+ ssl->prev->next = ssl->next;
+ else
+ ssl_ctx->head = ssl->next;
+
+ if (ssl->next)
+ ssl->next->prev = ssl->prev;
+ else
+ ssl_ctx->tail = ssl->prev;
+
+ SSL_CTX_UNLOCK(ssl_ctx->mutex);
+
+ /* may already be free - but be sure */
+ free(ssl->encrypt_ctx);
+ ssl->encrypt_ctx = NULL;
+ free(ssl->decrypt_ctx);
+ ssl->decrypt_ctx = NULL;
+ disposable_free(ssl);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ x509_free(ssl->x509_ctx);
+#endif
+
+ ssl_ext_free(ssl->extensions);
+ ssl->extensions = NULL;
+ free(ssl);
+}
+
+/*
+ * Read the SSL connection and send any alerts for various errors.
+ */
+EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data)
+{
+ int ret = basic_read(ssl, in_data);
+
+ /* check for return code so we can send an alert */
+ if (ret < SSL_OK && ret != SSL_CLOSE_NOTIFY && ret != SSL_EAGAIN)
+ {
+ if (ret != SSL_ERROR_CONN_LOST)
+ {
+ send_alert(ssl, ret);
+#ifndef CONFIG_SSL_SKELETON_MODE
+ /* something nasty happened, so get rid of this session */
+ kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl);
+#endif
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Write application data to the client
+ */
+EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len)
+{
+ int n = out_len, nw, i, tot = 0;
+
+ /* maximum size of a TLS packet is around 16kB, so fragment */
+ do
+ {
+ nw = n;
+
+ if (nw > RT_MAX_PLAIN_LENGTH) /* fragment if necessary */
+ nw = RT_MAX_PLAIN_LENGTH;
+
+ if ((i = send_packet(ssl, PT_APP_PROTOCOL_DATA,
+ &out_data[tot], nw)) <= 0)
+ {
+ out_len = i; /* an error */
+ break;
+ }
+
+ tot += i;
+ n -= i;
+ } while (n > 0);
+
+ return out_len;
+}
+
+/**
+ * Add a certificate to the certificate chain.
+ */
+int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len)
+{
+ int ret = SSL_ERROR_NO_CERT_DEFINED, i = 0;
+ SSL_CERT *ssl_cert;
+ X509_CTX *cert = NULL;
+ int offset;
+
+ while (i < CONFIG_SSL_MAX_CERTS && ssl_ctx->certs[i].buf)
+ i++;
+
+ if (i == CONFIG_SSL_MAX_CERTS) /* too many certs */
+ {
+#ifdef CONFIG_SSL_DIAGNOSTICS
+ printf("Error: maximum number of certs added (%d) - change of "
+ "compile-time configuration required\n",
+ CONFIG_SSL_MAX_CERTS);
+#endif
+ goto error;
+ }
+
+ if ((ret = x509_new(buf, &offset, &cert)))
+ goto error;
+
+#if defined (CONFIG_SSL_FULL_MODE)
+ if (ssl_ctx->options & SSL_DISPLAY_CERTS)
+ x509_print(cert, NULL);
+#endif
+
+ ssl_cert = &ssl_ctx->certs[i];
+ ssl_cert->size = len;
+ ssl_cert->buf = (uint8_t *)malloc(len);
+
+ switch (cert->sig_type)
+ {
+ case SIG_TYPE_SHA1:
+ ssl_cert->hash_alg = SIG_ALG_SHA1;
+ break;
+
+ case SIG_TYPE_SHA256:
+ ssl_cert->hash_alg = SIG_ALG_SHA256;
+ break;
+
+ case SIG_TYPE_SHA384:
+ ssl_cert->hash_alg = SIG_ALG_SHA384;
+ break;
+
+ case SIG_TYPE_SHA512:
+ ssl_cert->hash_alg = SIG_ALG_SHA512;
+ break;
+ }
+
+ memcpy(ssl_cert->buf, buf, len);
+ ssl_ctx->chain_length++;
+ len -= offset;
+ ret = SSL_OK; /* ok so far */
+
+ /* recurse? */
+ if (len > 0)
+ {
+ ret = add_cert(ssl_ctx, &buf[offset], len);
+ }
+
+error:
+ x509_free(cert); /* don't need anymore */
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Add a certificate authority.
+ */
+int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len)
+{
+ int ret = X509_OK; /* ignore errors for now */
+ int i = 0;
+ CA_CERT_CTX *ca_cert_ctx;
+
+ if (ssl_ctx->ca_cert_ctx == NULL)
+ ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)calloc(1, sizeof(CA_CERT_CTX));
+
+ ca_cert_ctx = ssl_ctx->ca_cert_ctx;
+
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ i++;
+
+ while (len > 0)
+ {
+ int offset;
+ if (i >= CONFIG_X509_MAX_CA_CERTS)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: maximum number of CA certs added (%d) - change of "
+ "compile-time configuration required\n",
+ CONFIG_X509_MAX_CA_CERTS);
+#endif
+ ret = X509_MAX_CERTS;
+ break;
+ }
+
+ /* ignore the return code */
+ if (x509_new(buf, &offset, &ca_cert_ctx->cert[i]) == X509_OK)
+ {
+#if defined (CONFIG_SSL_FULL_MODE)
+ if (ssl_ctx->options & SSL_DISPLAY_CERTS)
+ x509_print(ca_cert_ctx->cert[i], NULL);
+#endif
+ }
+
+ i++;
+ len -= offset;
+ }
+
+ return ret;
+}
+
+/*
+ * Retrieve an X.509 distinguished name component
+ */
+EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component)
+{
+ if (ssl->x509_ctx == NULL)
+ return NULL;
+
+ switch (component)
+ {
+ case SSL_X509_CERT_COMMON_NAME:
+ return ssl->x509_ctx->cert_dn[X509_COMMON_NAME];
+
+ case SSL_X509_CERT_ORGANIZATION:
+ return ssl->x509_ctx->cert_dn[X509_ORGANIZATION];
+
+ case SSL_X509_CERT_ORGANIZATIONAL_NAME:
+ return ssl->x509_ctx->cert_dn[X509_ORGANIZATIONAL_UNIT];
+
+ case SSL_X509_CERT_LOCATION:
+ return ssl->x509_ctx->cert_dn[X509_LOCATION];
+
+ case SSL_X509_CERT_COUNTRY:
+ return ssl->x509_ctx->cert_dn[X509_COUNTRY];
+
+ case SSL_X509_CERT_STATE:
+ return ssl->x509_ctx->cert_dn[X509_STATE];
+
+ case SSL_X509_CA_CERT_COMMON_NAME:
+ return ssl->x509_ctx->ca_cert_dn[X509_COMMON_NAME];
+
+ case SSL_X509_CA_CERT_ORGANIZATION:
+ return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATION];
+
+ case SSL_X509_CA_CERT_ORGANIZATIONAL_NAME:
+ return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATIONAL_UNIT];
+
+ case SSL_X509_CA_CERT_LOCATION:
+ return ssl->x509_ctx->ca_cert_dn[X509_LOCATION];
+
+ case SSL_X509_CA_CERT_COUNTRY:
+ return ssl->x509_ctx->ca_cert_dn[X509_COUNTRY];
+
+ case SSL_X509_CA_CERT_STATE:
+ return ssl->x509_ctx->ca_cert_dn[X509_STATE];
+
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * Retrieve a "Subject Alternative Name" from a v3 certificate
+ */
+EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl,
+ int dnsindex)
+{
+ int i;
+
+ if (ssl->x509_ctx == NULL || ssl->x509_ctx->subject_alt_dnsnames == NULL)
+ return NULL;
+
+ for (i = 0; i < dnsindex; ++i)
+ {
+ if (ssl->x509_ctx->subject_alt_dnsnames[i] == NULL)
+ return NULL;
+ }
+
+ return ssl->x509_ctx->subject_alt_dnsnames[dnsindex];
+}
+
+#endif /* CONFIG_SSL_CERT_VERIFICATION */
+
+/*
+ * Find an ssl object based on the client's file descriptor.
+ */
+EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, long client_fd)
+{
+ SSL *ssl;
+
+ SSL_CTX_LOCK(ssl_ctx->mutex);
+ ssl = ssl_ctx->head;
+
+ /* search through all the ssl entries */
+ while (ssl)
+ {
+ if (ssl->client_fd == client_fd)
+ {
+ SSL_CTX_UNLOCK(ssl_ctx->mutex);
+ return ssl;
+ }
+
+ ssl = ssl->next;
+ }
+
+ SSL_CTX_UNLOCK(ssl_ctx->mutex);
+ return NULL;
+}
+
+/*
+ * Force the client to perform its handshake again.
+ */
+EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl)
+{
+ int ret = SSL_OK;
+
+ disposable_new(ssl);
+#ifdef CONFIG_SSL_ENABLE_CLIENT
+ if (IS_SET_SSL_FLAG(SSL_IS_CLIENT))
+ {
+ ret = do_client_connect(ssl);
+ }
+ else
+#endif
+ {
+ send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
+ g_hello_request, sizeof(g_hello_request));
+ SET_SSL_FLAG(SSL_NEED_RECORD);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Get what we need for key info.
+ * @param cipher [in] The cipher information we are after
+ * @param key_size [out] The key size for the cipher
+ * @param iv_size [out] The iv size for the cipher
+ * @return The amount of key information we need.
+ */
+static const cipher_info_t *get_cipher_info(uint8_t cipher)
+{
+ int i;
+
+ for (i = 0; i < NUM_PROTOCOLS; i++)
+ {
+ if (cipher_info[i].cipher == cipher)
+ {
+ return &cipher_info[i];
+ }
+ }
+
+ return NULL; /* error */
+}
+
+/*
+ * Get a new ssl context for a new connection.
+ */
+SSL *ssl_new(SSL_CTX *ssl_ctx, long client_fd)
+{
+ SSL *ssl = (SSL *)calloc(1, sizeof(SSL));
+ ssl->ssl_ctx = ssl_ctx;
+ ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */
+ ssl->client_fd = client_fd;
+ ssl->flag = SSL_NEED_RECORD;
+ ssl->bm_data = ssl->bm_all_data+BM_RECORD_OFFSET; /* space at the start */
+ ssl->hs_status = SSL_NOT_OK; /* not connected */
+#ifdef CONFIG_ENABLE_VERIFICATION
+ ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx;
+#endif
+ disposable_new(ssl);
+
+ /* a bit hacky but saves a few bytes of memory */
+ ssl->flag |= ssl_ctx->options;
+ SSL_CTX_LOCK(ssl_ctx->mutex);
+
+ if (ssl_ctx->head == NULL)
+ {
+ ssl_ctx->head = ssl;
+ ssl_ctx->tail = ssl;
+ }
+ else
+ {
+ ssl->prev = ssl_ctx->tail;
+ ssl_ctx->tail->next = ssl;
+ ssl_ctx->tail = ssl;
+ }
+
+ SSL_CTX_UNLOCK(ssl_ctx->mutex);
+ return ssl;
+}
+
+/*
+ * Add a private key to a context.
+ */
+int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj)
+{
+ int ret = SSL_OK;
+
+ /* get the private key details */
+ if (asn1_get_private_key(ssl_obj->buf, ssl_obj->len, &ssl_ctx->rsa_ctx))
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/**
+ * Increment the read sequence number (as a 64 bit endian indepenent #)
+ */
+static void increment_read_sequence(SSL *ssl)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ {
+ if (++ssl->read_sequence[i])
+ break;
+ }
+}
+
+/**
+ * Increment the read sequence number (as a 64 bit endian indepenent #)
+ */
+static void increment_write_sequence(SSL *ssl)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ {
+ if (++ssl->write_sequence[i])
+ break;
+ }
+}
+
+/**
+ * Work out the HMAC digest in a packet.
+ */
+static void add_hmac_digest(SSL *ssl, int mode, uint8_t *hmac_header,
+ const uint8_t *buf, int buf_len, uint8_t *hmac_buf)
+{
+ int hmac_len = buf_len + 8 + SSL_RECORD_SIZE;
+ uint8_t *t_buf = (uint8_t *)alloca(hmac_len);
+
+ memcpy(t_buf, (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) ?
+ ssl->write_sequence : ssl->read_sequence, 8);
+ memcpy(&t_buf[8], hmac_header, SSL_RECORD_SIZE);
+ memcpy(&t_buf[8+SSL_RECORD_SIZE], buf, buf_len);
+
+ ssl->cipher_info->hmac(t_buf, hmac_len,
+ (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) ?
+ ssl->server_mac : ssl->client_mac,
+ ssl->cipher_info->digest_size, hmac_buf);
+
+#if 0
+ print_blob("record", hmac_header, SSL_RECORD_SIZE);
+ print_blob("buf", buf, buf_len);
+ if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE)
+ {
+ print_blob("write seq", ssl->write_sequence, 8);
+ }
+ else
+ {
+ print_blob("read seq", ssl->read_sequence, 8);
+ }
+
+ if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ)
+ {
+ print_blob("server mac",
+ ssl->server_mac, ssl->cipher_info->digest_size);
+ }
+ else
+ {
+ print_blob("client mac",
+ ssl->client_mac, ssl->cipher_info->digest_size);
+ }
+ print_blob("hmac", hmac_buf, ssl->cipher_info->digest_size);
+#endif
+}
+
+/**
+ * Verify that the digest of a packet is correct.
+ */
+static int verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len)
+{
+ uint8_t hmac_buf[SHA256_SIZE]; // size of largest digest
+ int hmac_offset;
+
+ int last_blk_size = buf[read_len-1], i;
+ hmac_offset = read_len-last_blk_size-ssl->cipher_info->digest_size-1;
+
+ /* guard against a timing attack - make sure we do the digest */
+ if (hmac_offset < 0)
+ {
+ hmac_offset = 0;
+ }
+ else
+ {
+ /* already looked at last byte */
+ for (i = 1; i < last_blk_size; i++)
+ {
+ if (buf[read_len-i] != last_blk_size)
+ {
+ hmac_offset = 0;
+ break;
+ }
+ }
+ }
+
+ /* sanity check the offset */
+ ssl->hmac_header[3] = hmac_offset >> 8; /* insert size */
+ ssl->hmac_header[4] = hmac_offset & 0xff;
+ add_hmac_digest(ssl, mode, ssl->hmac_header, buf, hmac_offset, hmac_buf);
+
+ if (memcmp(hmac_buf, &buf[hmac_offset], ssl->cipher_info->digest_size))
+ {
+ return SSL_ERROR_INVALID_HMAC;
+ }
+
+ return hmac_offset;
+}
+
+/**
+ * Add a packet to the end of our sent and received packets, so that we may use
+ * it to calculate the hash at the end.
+ */
+void add_packet(SSL *ssl, const uint8_t *pkt, int len)
+{
+ // TLS1.2+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2 || ssl->version == 0)
+ {
+ SHA256_Update(&ssl->dc->sha256_ctx, pkt, len);
+ }
+
+ if (ssl->version < SSL_PROTOCOL_VERSION_TLS1_2 ||
+ ssl->next_state == HS_SERVER_HELLO ||
+ ssl->next_state == 0)
+ {
+ MD5_Update(&ssl->dc->md5_ctx, pkt, len);
+ SHA1_Update(&ssl->dc->sha1_ctx, pkt, len);
+ }
+}
+
+/**
+ * Work out the MD5 PRF.
+ */
+static void p_hash_md5(const uint8_t *sec, int sec_len,
+ uint8_t *seed, int seed_len, uint8_t *out, int olen)
+{
+ uint8_t a1[MD5_SIZE+77];
+
+ /* A(1) */
+ ssl_hmac_md5(seed, seed_len, sec, sec_len, a1);
+ memcpy(&a1[MD5_SIZE], seed, seed_len);
+ ssl_hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out);
+
+ while (olen > MD5_SIZE)
+ {
+ uint8_t a2[MD5_SIZE];
+ out += MD5_SIZE;
+ olen -= MD5_SIZE;
+
+ /* A(N) */
+ ssl_hmac_md5(a1, MD5_SIZE, sec, sec_len, a2);
+ memcpy(a1, a2, MD5_SIZE);
+
+ /* work out the actual hash */
+ ssl_hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out);
+ }
+}
+
+/**
+ * Work out the SHA1 PRF.
+ */
+static void p_hash_sha1(const uint8_t *sec, int sec_len,
+ uint8_t *seed, int seed_len, uint8_t *out, int olen)
+{
+ uint8_t a1[SHA1_SIZE+77];
+
+ /* A(1) */
+ ssl_hmac_sha1(seed, seed_len, sec, sec_len, a1);
+ memcpy(&a1[SHA1_SIZE], seed, seed_len);
+ ssl_hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out);
+
+ while (olen > SHA1_SIZE)
+ {
+ uint8_t a2[SHA1_SIZE];
+ out += SHA1_SIZE;
+ olen -= SHA1_SIZE;
+
+ /* A(N) */
+ ssl_hmac_sha1(a1, SHA1_SIZE, sec, sec_len, a2);
+ memcpy(a1, a2, SHA1_SIZE);
+
+ /* work out the actual hash */
+ ssl_hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out);
+ }
+}
+
+/**
+ * Work out the SHA256 PRF.
+ */
+static void p_hash_sha256(const uint8_t *sec, int sec_len,
+ uint8_t *seed, int seed_len, uint8_t *out, int olen)
+{
+ uint8_t a1[SHA256_SIZE+77];
+
+ /* A(1) */
+ hmac_sha256(seed, seed_len, sec, sec_len, a1);
+ memcpy(&a1[SHA256_SIZE], seed, seed_len);
+ hmac_sha256(a1, SHA256_SIZE+seed_len, sec, sec_len, out);
+
+ while (olen > SHA256_SIZE)
+ {
+ uint8_t a2[SHA256_SIZE];
+ out += SHA256_SIZE;
+ olen -= SHA256_SIZE;
+
+ // A(N)
+ hmac_sha256(a1, SHA256_SIZE, sec, sec_len, a2);
+ memcpy(a1, a2, SHA256_SIZE);
+
+ // work out the actual hash
+ hmac_sha256(a1, SHA256_SIZE+seed_len, sec, sec_len, out);
+ }
+}
+
+/**
+ * Work out the PRF.
+ */
+static void prf(SSL *ssl, const uint8_t *sec, int sec_len,
+ uint8_t *seed, int seed_len,
+ uint8_t *out, int olen)
+{
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ p_hash_sha256(sec, sec_len, seed, seed_len, out, olen);
+ }
+ else // TLS1.0/1.1
+ {
+ int len, i;
+ const uint8_t *S1, *S2;
+ uint8_t xbuf[2*(SHA256_SIZE+32+16) + MD5_SIZE]; /* max keyblock */
+ uint8_t ybuf[2*(SHA256_SIZE+32+16) + SHA1_SIZE]; /* max keyblock */
+
+ len = sec_len/2;
+ S1 = sec;
+ S2 = &sec[len];
+ len += (sec_len & 1); /* add for odd, make longer */
+
+ p_hash_md5(S1, len, seed, seed_len, xbuf, olen);
+ p_hash_sha1(S2, len, seed, seed_len, ybuf, olen);
+
+ for (i = 0; i < olen; i++)
+ out[i] = xbuf[i] ^ ybuf[i];
+ }
+}
+
+/**
+ * Generate a master secret based on the client/server random data and the
+ * premaster secret.
+ */
+void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret)
+{
+ uint8_t buf[77];
+//print_blob("premaster secret", premaster_secret, 48);
+ strcpy((char *)buf, "master secret");
+ memcpy(&buf[13], ssl->dc->client_random, SSL_RANDOM_SIZE);
+ memcpy(&buf[45], ssl->dc->server_random, SSL_RANDOM_SIZE);
+ prf(ssl, premaster_secret, SSL_SECRET_SIZE, buf, 77, ssl->dc->master_secret,
+ SSL_SECRET_SIZE);
+#if 0
+ print_blob("client random", ssl->dc->client_random, 32);
+ print_blob("server random", ssl->dc->server_random, 32);
+ print_blob("master secret", ssl->dc->master_secret, 48);
+#endif
+}
+
+/**
+ * Generate a 'random' blob of data used for the generation of keys.
+ */
+static void generate_key_block(SSL *ssl,
+ uint8_t *client_random, uint8_t *server_random,
+ uint8_t *master_secret, uint8_t *key_block, int key_block_size)
+{
+ uint8_t buf[77];
+ strcpy((char *)buf, "key expansion");
+ memcpy(&buf[13], server_random, SSL_RANDOM_SIZE);
+ memcpy(&buf[45], client_random, SSL_RANDOM_SIZE);
+ prf(ssl, master_secret, SSL_SECRET_SIZE, buf, 77,
+ key_block, key_block_size);
+}
+
+/**
+ * Calculate the digest used in the finished message. This function also
+ * doubles up as a certificate verify function.
+ */
+int finished_digest(SSL *ssl, const char *label, uint8_t *digest)
+{
+ uint8_t mac_buf[SHA1_SIZE+MD5_SIZE+15];
+ uint8_t *q = mac_buf;
+ int dgst_len;
+
+ if (label)
+ {
+ strcpy((char *)q, label);
+ q += strlen(label);
+ }
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ SHA256_CTX sha256_ctx = ssl->dc->sha256_ctx; // interim copy
+ SHA256_Final(q, &sha256_ctx);
+ q += SHA256_SIZE;
+ dgst_len = (int)(q-mac_buf);
+ }
+ else // TLS1.0/1.1
+ {
+ MD5_CTX md5_ctx = ssl->dc->md5_ctx; // interim copy
+ SHA1_CTX sha1_ctx = ssl->dc->sha1_ctx;
+
+ MD5_Final(q, &md5_ctx);
+ q += MD5_SIZE;
+
+ SHA1_Final(q, &sha1_ctx);
+ q += SHA1_SIZE;
+ dgst_len = (int)(q-mac_buf);
+ }
+
+ if (label)
+ {
+ prf(ssl, ssl->dc->master_secret, SSL_SECRET_SIZE,
+ mac_buf, dgst_len, digest, SSL_FINISHED_HASH_SIZE);
+ }
+ else /* for use in a certificate verify */
+ {
+ memcpy(digest, mac_buf, dgst_len);
+ }
+
+#if 0
+ printf("label: %s\n", label);
+ print_blob("mac_buf", mac_buf, dgst_len);
+ print_blob("finished digest", digest, SSL_FINISHED_HASH_SIZE);
+#endif
+
+ return dgst_len;
+}
+
+/**
+ * Retrieve (and initialise) the context of a cipher.
+ */
+static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt)
+{
+ switch (ssl->cipher)
+ {
+ case SSL_AES128_SHA:
+ case SSL_AES128_SHA256:
+ {
+ AES_CTX *aes_ctx = (AES_CTX *)malloc(sizeof(AES_CTX));
+ AES_set_key(aes_ctx, key, iv, AES_MODE_128);
+
+ if (is_decrypt)
+ {
+ AES_convert_key(aes_ctx);
+ }
+
+ return (void *)aes_ctx;
+ }
+
+ case SSL_AES256_SHA:
+ case SSL_AES256_SHA256:
+ {
+ AES_CTX *aes_ctx = (AES_CTX *)malloc(sizeof(AES_CTX));
+ AES_set_key(aes_ctx, key, iv, AES_MODE_256);
+
+ if (is_decrypt)
+ {
+ AES_convert_key(aes_ctx);
+ }
+
+ return (void *)aes_ctx;
+ }
+
+ }
+
+ return NULL; /* its all gone wrong */
+}
+
+/**
+ * Send a packet over the socket.
+ */
+static int send_raw_packet(SSL *ssl, uint8_t protocol)
+{
+ uint8_t *rec_buf = ssl->bm_all_data;
+ int pkt_size = SSL_RECORD_SIZE+ssl->bm_index;
+ int sent = 0;
+ int ret = SSL_OK;
+
+ rec_buf[0] = protocol;
+ rec_buf[1] = 0x03; /* version = 3.1 or higher */
+ rec_buf[2] = ssl->version & 0x0f;
+ rec_buf[3] = ssl->bm_index >> 8;
+ rec_buf[4] = ssl->bm_index & 0xff;
+
+ DISPLAY_BYTES(ssl, "sending %d bytes", ssl->bm_all_data,
+ pkt_size, pkt_size);
+
+ while (sent < pkt_size)
+ {
+ ret = SOCKET_WRITE(ssl->client_fd,
+ &ssl->bm_all_data[sent], pkt_size-sent);
+
+ if (ret >= 0)
+ sent += ret;
+ else
+ {
+
+#ifdef WIN32
+ if (GetLastError() != WSAEWOULDBLOCK)
+#else
+ if (SOCKET_ERRNO() != EAGAIN && SOCKET_ERRNO() != EWOULDBLOCK)
+#endif
+ return SSL_ERROR_CONN_LOST;
+ }
+
+#ifdef PORT_USE_SELECT
+ // TODO: This should be factored into SOCKET_WAIT_WRITABLE(),
+ // with semantic being waiting until socket can be written
+ // regardless whether it is in blocking or non-blocking mode.
+ /* keep going until the write buffer has some space */
+ if (sent != pkt_size)
+ {
+ fd_set wfds;
+ FD_ZERO(&wfds);
+ FD_SET(ssl->client_fd, &wfds);
+
+ /* block and wait for it */
+ if (select(ssl->client_fd + 1, NULL, &wfds, NULL, NULL) < 0)
+ return SSL_ERROR_CONN_LOST;
+ }
+#endif
+ }
+
+ ssl->bm_index = 0;
+
+ if (protocol != PT_APP_PROTOCOL_DATA)
+ {
+ /* always return SSL_OK during handshake */
+ ret = SSL_OK;
+ }
+
+ return ret;
+}
+
+/**
+ * Send an encrypted packet with padding bytes if necessary.
+ */
+int send_packet(SSL *ssl, uint8_t protocol, const uint8_t *in, int length)
+{
+ int ret, msg_length = 0;
+
+ /* if our state is bad, don't bother */
+ if (ssl->hs_status == SSL_ERROR_DEAD)
+ return SSL_ERROR_CONN_LOST;
+
+ if (IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY))
+ return SSL_CLOSE_NOTIFY;
+
+ if (in) /* has the buffer already been initialised? */
+ {
+ memcpy(ssl->bm_data, in, length);
+ }
+
+ msg_length += length;
+
+ if (IS_SET_SSL_FLAG(SSL_TX_ENCRYPTED))
+ {
+ int mode = IS_SET_SSL_FLAG(SSL_IS_CLIENT) ?
+ SSL_CLIENT_WRITE : SSL_SERVER_WRITE;
+ uint8_t hmac_header[SSL_RECORD_SIZE] =
+ {
+ protocol,
+ 0x03, /* version = 3.1 or higher */
+ ssl->version & 0x0f,
+ msg_length >> 8,
+ msg_length & 0xff
+ };
+
+ if (protocol == PT_HANDSHAKE_PROTOCOL)
+ {
+ DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0);
+
+ if (ssl->bm_data[0] != HS_HELLO_REQUEST)
+ {
+ add_packet(ssl, ssl->bm_data, msg_length);
+ }
+ }
+
+ /* add the packet digest */
+ add_hmac_digest(ssl, mode, hmac_header, ssl->bm_data, msg_length,
+ &ssl->bm_data[msg_length]);
+ msg_length += ssl->cipher_info->digest_size;
+
+ /* add padding */
+ {
+ int last_blk_size = msg_length%ssl->cipher_info->padding_size;
+ int pad_bytes = ssl->cipher_info->padding_size - last_blk_size;
+
+ /* ensure we always have at least 1 padding byte */
+ if (pad_bytes == 0)
+ pad_bytes += ssl->cipher_info->padding_size;
+
+ memset(&ssl->bm_data[msg_length], pad_bytes-1, pad_bytes);
+ msg_length += pad_bytes;
+ }
+
+ DISPLAY_BYTES(ssl, "unencrypted write", ssl->bm_data, msg_length);
+ increment_write_sequence(ssl);
+
+ /* add the explicit IV for TLS1.1 */
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_1)
+ {
+ uint8_t iv_size = ssl->cipher_info->iv_size;
+ memmove(ssl->bm_data + iv_size, ssl->bm_data, msg_length);
+ if (get_random(iv_size, ssl->bm_data) < 0)
+ return SSL_NOT_OK;
+
+ msg_length += iv_size;
+ }
+
+ /* now encrypt the packet */
+ ssl->cipher_info->encrypt(ssl->encrypt_ctx, ssl->bm_data,
+ ssl->bm_data, msg_length);
+ }
+ else if (protocol == PT_HANDSHAKE_PROTOCOL)
+ {
+ DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0);
+
+ if (ssl->bm_data[0] != HS_HELLO_REQUEST)
+ {
+ add_packet(ssl, ssl->bm_data, length);
+ }
+ }
+
+ ssl->bm_index = msg_length;
+ if ((ret = send_raw_packet(ssl, protocol)) <= 0)
+ return ret;
+
+ return length; /* just return what we wanted to send */
+}
+
+/**
+ * Work out the cipher keys we are going to use for this session based on the
+ * master secret.
+ */
+static int set_key_block(SSL *ssl, int is_write)
+{
+ const cipher_info_t *ciph_info = get_cipher_info(ssl->cipher);
+ uint8_t *q;
+ uint8_t client_key[32], server_key[32]; /* big enough for AES256 */
+ uint8_t client_iv[16], server_iv[16]; /* big enough for AES128/256 */
+ int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
+
+ if (ciph_info == NULL)
+ return -1;
+
+ /* only do once in a handshake */
+ if (!ssl->dc->key_block_generated)
+ {
+ generate_key_block(ssl, ssl->dc->client_random, ssl->dc->server_random,
+ ssl->dc->master_secret, ssl->dc->key_block,
+ ciph_info->key_block_size);
+#if 0
+ print_blob("master", ssl->dc->master_secret, SSL_SECRET_SIZE);
+ print_blob("keyblock", ssl->dc->key_block, ciph_info->key_block_size);
+ print_blob("client random", ssl->dc->client_random, 32);
+ print_blob("server random", ssl->dc->server_random, 32);
+#endif
+ ssl->dc->key_block_generated = 1;
+ }
+
+ q = ssl->dc->key_block;
+
+ if ((is_client && is_write) || (!is_client && !is_write))
+ {
+ memcpy(ssl->client_mac, q, ciph_info->digest_size);
+ }
+
+ q += ciph_info->digest_size;
+
+ if ((!is_client && is_write) || (is_client && !is_write))
+ {
+ memcpy(ssl->server_mac, q, ciph_info->digest_size);
+ }
+
+ q += ciph_info->digest_size;
+ memcpy(client_key, q, ciph_info->key_size);
+ q += ciph_info->key_size;
+ memcpy(server_key, q, ciph_info->key_size);
+ q += ciph_info->key_size;
+
+ memcpy(client_iv, q, ciph_info->iv_size);
+ q += ciph_info->iv_size;
+ memcpy(server_iv, q, ciph_info->iv_size);
+ q += ciph_info->iv_size;
+#if 0
+ print_blob("client key", client_key, ciph_info->key_size);
+ print_blob("server key", server_key, ciph_info->key_size);
+ print_blob("client iv", client_iv, ciph_info->iv_size);
+ print_blob("server iv", server_iv, ciph_info->iv_size);
+#endif
+
+ free(is_write ? ssl->encrypt_ctx : ssl->decrypt_ctx);
+
+ /* now initialise the ciphers */
+ if (is_client)
+ {
+ finished_digest(ssl, server_finished, ssl->dc->final_finish_mac);
+
+ if (is_write)
+ ssl->encrypt_ctx = crypt_new(ssl, client_key, client_iv, 0);
+ else
+ ssl->decrypt_ctx = crypt_new(ssl, server_key, server_iv, 1);
+ }
+ else
+ {
+ finished_digest(ssl, client_finished, ssl->dc->final_finish_mac);
+
+ if (is_write)
+ ssl->encrypt_ctx = crypt_new(ssl, server_key, server_iv, 0);
+ else
+ ssl->decrypt_ctx = crypt_new(ssl, client_key, client_iv, 1);
+ }
+
+ ssl->cipher_info = ciph_info;
+ return 0;
+}
+
+/**
+ * Read the SSL connection.
+ */
+int basic_read(SSL *ssl, uint8_t **in_data)
+{
+ int ret = SSL_OK;
+ int read_len, is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
+ uint8_t *buf = ssl->bm_data;
+
+ if (IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY))
+ return SSL_CLOSE_NOTIFY;
+
+ read_len = SOCKET_READ(ssl->client_fd, &buf[ssl->bm_read_index],
+ ssl->need_bytes-ssl->got_bytes);
+
+ if (read_len < 0)
+ {
+#ifdef WIN32
+ if (GetLastError() == WSAEWOULDBLOCK)
+#else
+ if (SOCKET_ERRNO() == EAGAIN || SOCKET_ERRNO() == EWOULDBLOCK)
+#endif
+ return SSL_EAGAIN;
+ }
+
+ /* connection has gone, so die */
+ if (read_len <= 0)
+ {
+ ret = SSL_ERROR_CONN_LOST;
+ ssl->hs_status = SSL_ERROR_DEAD; /* make sure it stays dead */
+ goto error;
+ }
+
+ DISPLAY_BYTES(ssl, "received %d bytes",
+ &ssl->bm_data[ssl->bm_read_index], read_len, read_len);
+
+ ssl->got_bytes += read_len;
+ ssl->bm_read_index += read_len;
+
+ /* haven't quite got what we want, so try again later */
+ if (ssl->got_bytes < ssl->need_bytes)
+ return SSL_OK;
+
+ read_len = ssl->got_bytes;
+ ssl->got_bytes = 0;
+
+ if (IS_SET_SSL_FLAG(SSL_NEED_RECORD))
+ {
+ /* check for sslv2 "client hello" */
+ if ((buf[0] & 0x80) && buf[2] == 1)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: no SSLv23 handshaking allowed\n");
+#endif
+ ret = SSL_ERROR_NOT_SUPPORTED;
+ goto error; /* not an error - just get out of here */
+ }
+
+ ssl->need_bytes = (buf[3] << 8) + buf[4];
+
+ /* do we violate the spec with the message size? */
+ if (ssl->need_bytes > RT_MAX_PLAIN_LENGTH+RT_EXTRA-BM_RECORD_OFFSET)
+ {
+ printf("TLS buffer overflow, record size: %u (+5)\n", ssl->need_bytes);
+ ssl->need_bytes = SSL_RECORD_SIZE;
+ ret = SSL_ERROR_RECORD_OVERFLOW;
+ goto error;
+ }
+
+ CLR_SSL_FLAG(SSL_NEED_RECORD);
+ memcpy(ssl->hmac_header, buf, 3); /* store for hmac */
+ ssl->record_type = buf[0];
+ goto error; /* no error, we're done */
+ }
+
+ /* for next time - just do it now in case of an error */
+ SET_SSL_FLAG(SSL_NEED_RECORD);
+ ssl->need_bytes = SSL_RECORD_SIZE;
+
+ /* decrypt if we need to */
+ if (IS_SET_SSL_FLAG(SSL_RX_ENCRYPTED))
+ {
+ ssl->cipher_info->decrypt(ssl->decrypt_ctx, buf, buf, read_len);
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_1)
+ {
+ buf += ssl->cipher_info->iv_size;
+ read_len -= ssl->cipher_info->iv_size;
+ }
+
+ read_len = verify_digest(ssl,
+ is_client ? SSL_CLIENT_READ : SSL_SERVER_READ, buf, read_len);
+
+ /* does the hmac work? */
+ if (read_len < 0)
+ {
+ ret = read_len;
+ goto error;
+ }
+
+ DISPLAY_BYTES(ssl, "decrypted", buf, read_len);
+ increment_read_sequence(ssl);
+ }
+
+ /* The main part of the SSL packet */
+ switch (ssl->record_type)
+ {
+ case PT_HANDSHAKE_PROTOCOL:
+ if (ssl->dc != NULL)
+ {
+ ssl->dc->bm_proc_index = 0;
+ ret = do_handshake(ssl, buf, read_len);
+ }
+ else /* no client renegotiation allowed */
+ {
+ ret = SSL_ERROR_NO_CLIENT_RENOG;
+ goto error;
+ }
+ break;
+
+ case PT_CHANGE_CIPHER_SPEC:
+ if (ssl->next_state != HS_FINISHED)
+ {
+ ret = SSL_ERROR_INVALID_HANDSHAKE;
+ goto error;
+ }
+
+ if (set_key_block(ssl, 0) < 0)
+ {
+ ret = SSL_ERROR_INVALID_HANDSHAKE;
+ goto error;
+ }
+
+ /* all encrypted from now on */
+ SET_SSL_FLAG(SSL_RX_ENCRYPTED);
+ memset(ssl->read_sequence, 0, 8);
+ break;
+
+ case PT_APP_PROTOCOL_DATA:
+ if (in_data && ssl->hs_status == SSL_OK)
+ {
+ *in_data = buf; /* point to the work buffer */
+ (*in_data)[read_len] = 0; /* null terminate just in case */
+ ret = read_len;
+ }
+ else
+ ret = SSL_ERROR_INVALID_PROT_MSG;
+ break;
+
+ case PT_ALERT_PROTOCOL:
+ /* return the alert # with alert bit set */
+ if (buf[0] == SSL_ALERT_TYPE_WARNING &&
+ buf[1] == SSL_ALERT_CLOSE_NOTIFY)
+ {
+ ret = SSL_CLOSE_NOTIFY;
+ send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY);
+ SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY);
+ }
+ else
+ {
+ ret = -buf[1];
+ DISPLAY_ALERT(ssl, buf[1]);
+ }
+
+ break;
+
+ default:
+ ret = SSL_ERROR_INVALID_PROT_MSG;
+ break;
+ }
+
+error:
+ ssl->bm_read_index = 0; /* reset to go again */
+
+ if (ret < SSL_OK && in_data)/* if all wrong, then clear this buffer ptr */
+ *in_data = NULL;
+
+ return ret;
+}
+
+/**
+ * Do some basic checking of data and then perform the appropriate handshaking.
+ */
+static int do_handshake(SSL *ssl, uint8_t *buf, int read_len)
+{
+ int hs_len = (buf[2]<<8) + buf[3];
+ uint8_t handshake_type = buf[0];
+ int ret = SSL_OK;
+ int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
+
+ /* some integrity checking on the handshake */
+ PARANOIA_CHECK(read_len-SSL_HS_HDR_SIZE, hs_len);
+
+ if (handshake_type != ssl->next_state)
+ {
+ /* handle a special case on the client */
+ if (!is_client || handshake_type != HS_CERT_REQ ||
+ ssl->next_state != HS_SERVER_HELLO_DONE)
+ {
+ ret = SSL_ERROR_INVALID_HANDSHAKE;
+ goto error;
+ }
+ }
+
+ hs_len += SSL_HS_HDR_SIZE; /* adjust for when adding packets */
+ ssl->bm_index = hs_len; /* store the size and check later */
+ DISPLAY_STATE(ssl, 0, handshake_type, 0);
+
+ if (handshake_type != HS_CERT_VERIFY && handshake_type != HS_HELLO_REQUEST)
+ add_packet(ssl, buf, hs_len);
+
+#if defined(CONFIG_SSL_ENABLE_CLIENT) && defined(CONFIG_SSL_ENABLE_SERVER)
+ ret = is_client ?
+ do_clnt_handshake(ssl, handshake_type, buf, hs_len) :
+ do_svr_handshake(ssl, handshake_type, buf, hs_len);
+#elif defined(CONFIG_SSL_ENABLE_CLIENT)
+ ret = do_clnt_handshake(ssl, handshake_type, buf, hs_len);
+#else
+ ret = do_svr_handshake(ssl, handshake_type, buf, hs_len);
+#endif
+
+ /* just use recursion to get the rest */
+ if (hs_len < read_len && ret == SSL_OK)
+ ret = do_handshake(ssl, &buf[hs_len], read_len-hs_len);
+
+error:
+ return ret;
+}
+
+/**
+ * Sends the change cipher spec message. We have just read a finished message
+ * from the client.
+ */
+int send_change_cipher_spec(SSL *ssl)
+{
+ int ret = send_packet(ssl, PT_CHANGE_CIPHER_SPEC,
+ g_chg_cipher_spec_pkt, sizeof(g_chg_cipher_spec_pkt));
+
+ if (ret >= 0 && set_key_block(ssl, 1) < 0)
+ ret = SSL_ERROR_INVALID_HANDSHAKE;
+
+ if (ssl->cipher_info)
+ SET_SSL_FLAG(SSL_TX_ENCRYPTED);
+
+ memset(ssl->write_sequence, 0, 8);
+ return ret;
+}
+
+/**
+ * Send a "finished" message
+ */
+int send_finished(SSL *ssl)
+{
+ uint8_t buf[SHA1_SIZE+MD5_SIZE+15+4] = {
+ HS_FINISHED, 0, 0, SSL_FINISHED_HASH_SIZE };
+
+ /* now add the finished digest mac (12 bytes) */
+ finished_digest(ssl,
+ IS_SET_SSL_FLAG(SSL_IS_CLIENT) ?
+ client_finished : server_finished, &buf[4]);
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ /* store in the session cache */
+ if (!IS_SET_SSL_FLAG(SSL_SESSION_RESUME) && ssl->ssl_ctx->num_sessions)
+ {
+ memcpy(ssl->session->master_secret,
+ ssl->dc->master_secret, SSL_SECRET_SIZE);
+ }
+#endif
+
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
+ buf, SSL_FINISHED_HASH_SIZE+4);
+}
+
+/**
+ * Send an alert message.
+ * Return 1 if the alert was an "error".
+ */
+int send_alert(SSL *ssl, int error_code)
+{
+ int alert_num = 0;
+ int is_warning = 0;
+ uint8_t buf[2];
+
+ /* Don't bother, we're already dead */
+ if (ssl->hs_status == SSL_ERROR_DEAD)
+ {
+ return SSL_ERROR_CONN_LOST;
+ }
+
+#ifdef CONFIG_SSL_DIAGNOSTICS
+ if (IS_SET_SSL_FLAG(SSL_DISPLAY_STATES))
+ ssl_display_error(error_code);
+#endif
+
+ switch (error_code)
+ {
+ case SSL_ALERT_CLOSE_NOTIFY:
+ is_warning = 1;
+ alert_num = SSL_ALERT_CLOSE_NOTIFY;
+ break;
+
+ case SSL_ERROR_CONN_LOST: /* don't send alert just yet */
+ is_warning = 1;
+ break;
+
+ case SSL_ERROR_NO_CIPHER:
+ alert_num = SSL_ALERT_HANDSHAKE_FAILURE;
+ break;
+
+ case SSL_ERROR_INVALID_HMAC:
+ alert_num = SSL_ALERT_BAD_RECORD_MAC;
+ break;
+
+ case SSL_ERROR_FINISHED_INVALID:
+ case SSL_ERROR_INVALID_KEY:
+ alert_num = SSL_ALERT_DECRYPT_ERROR;
+ break;
+
+ case SSL_ERROR_INVALID_VERSION:
+ alert_num = SSL_ALERT_INVALID_VERSION;
+ break;
+
+ case SSL_ERROR_INVALID_SESSION:
+ alert_num = SSL_ALERT_ILLEGAL_PARAMETER;
+ break;
+
+ case SSL_ERROR_NO_CLIENT_RENOG:
+ alert_num = SSL_ALERT_NO_RENEGOTIATION;
+ break;
+
+ case SSL_ERROR_RECORD_OVERFLOW:
+ alert_num = SSL_ALERT_RECORD_OVERFLOW;
+ break;
+
+ case SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED):
+ case SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID):
+ alert_num = SSL_ALERT_CERTIFICATE_EXPIRED;
+ break;
+
+ case SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT):
+ alert_num = SSL_ALERT_UNKNOWN_CA;
+ break;
+
+ case SSL_X509_ERROR(X509_VFY_ERROR_UNSUPPORTED_DIGEST):
+ case SSL_ERROR_INVALID_CERT_HASH_ALG:
+ alert_num = SSL_ALERT_UNSUPPORTED_CERTIFICATE;
+ break;
+
+ case SSL_ERROR_BAD_CERTIFICATE:
+ case SSL_X509_ERROR(X509_VFY_ERROR_BAD_SIGNATURE):
+ alert_num = SSL_ALERT_BAD_CERTIFICATE;
+ break;
+
+ case SSL_ERROR_INVALID_HANDSHAKE:
+ case SSL_ERROR_INVALID_PROT_MSG:
+ default:
+ /* a catch-all for anything bad */
+ alert_num = (error_code <= SSL_X509_OFFSET) ?
+ SSL_ALERT_CERTIFICATE_UNKNOWN: SSL_ALERT_UNEXPECTED_MESSAGE;
+ break;
+ }
+
+ buf[0] = is_warning ? 1 : 2;
+ buf[1] = alert_num;
+ send_packet(ssl, PT_ALERT_PROTOCOL, buf, sizeof(buf));
+ DISPLAY_ALERT(ssl, alert_num);
+ return is_warning ? 0 : 1;
+}
+
+/**
+ * Process a client finished message.
+ */
+int process_finished(SSL *ssl, uint8_t *buf, int hs_len)
+{
+ int ret = SSL_OK;
+ int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
+ int resume = IS_SET_SSL_FLAG(SSL_SESSION_RESUME);
+
+ PARANOIA_CHECK(ssl->bm_index, SSL_FINISHED_HASH_SIZE+4);
+
+ /* check that we all work before we continue */
+ if (memcmp(ssl->dc->final_finish_mac, &buf[4], SSL_FINISHED_HASH_SIZE))
+ return SSL_ERROR_FINISHED_INVALID;
+
+ if ((!is_client && !resume) || (is_client && resume))
+ {
+ if ((ret = send_change_cipher_spec(ssl)) == SSL_OK)
+ ret = send_finished(ssl);
+ }
+
+ /* if we ever renegotiate */
+ ssl->next_state = is_client ? HS_HELLO_REQUEST : HS_CLIENT_HELLO;
+ ssl->hs_status = ret; /* set the final handshake status */
+
+error:
+ return ret;
+}
+
+/**
+ * Send a certificate.
+ */
+int send_certificate(SSL *ssl)
+{
+ int ret = SSL_OK;
+ int i = 0;
+ uint8_t *buf = ssl->bm_data;
+ int offset = 7;
+ int chain_length;
+
+ buf[0] = HS_CERTIFICATE;
+ buf[1] = 0;
+ buf[4] = 0;
+
+ /* spec says we must check if the hash/sig algorithm is OK */
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2 &&
+ ((ret = check_certificate_chain(ssl)) != SSL_OK))
+ {
+ ret = SSL_ERROR_INVALID_CERT_HASH_ALG;
+ goto error;
+ }
+
+ while (i < ssl->ssl_ctx->chain_length)
+ {
+ SSL_CERT *cert = &ssl->ssl_ctx->certs[i];
+ buf[offset++] = 0;
+ buf[offset++] = cert->size >> 8; /* cert 1 length */
+ buf[offset++] = cert->size & 0xff;
+ memcpy(&buf[offset], cert->buf, cert->size);
+ offset += cert->size;
+ i++;
+ }
+
+ chain_length = offset - 7;
+ buf[5] = chain_length >> 8; /* cert chain length */
+ buf[6] = chain_length & 0xff;
+ chain_length += 3;
+ buf[2] = chain_length >> 8; /* handshake length */
+ buf[3] = chain_length & 0xff;
+ ssl->bm_index = offset;
+ ret = send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset);
+
+error:
+ return ret;
+}
+
+/**
+ * Create a blob of memory that we'll get rid of once the handshake is
+ * complete.
+ */
+void disposable_new(SSL *ssl)
+{
+ if (ssl->dc == NULL)
+ {
+ ssl->dc = (DISPOSABLE_CTX *)calloc(1, sizeof(DISPOSABLE_CTX));
+ SHA256_Init(&ssl->dc->sha256_ctx);
+ MD5_Init(&ssl->dc->md5_ctx);
+ SHA1_Init(&ssl->dc->sha1_ctx);
+ }
+}
+
+/**
+ * Remove the temporary blob of memory.
+ */
+void disposable_free(SSL *ssl)
+{
+ if (ssl->dc)
+ {
+ memset(ssl->dc, 0, sizeof(DISPOSABLE_CTX));
+ free(ssl->dc);
+ ssl->dc = NULL;
+ }
+
+}
+
+#ifndef CONFIG_SSL_SKELETON_MODE /* no session resumption in this mode */
+/**
+ * Find if an existing session has the same session id. If so, use the
+ * master secret from this session for session resumption.
+ */
+SSL_SESSION *ssl_session_update(int max_sessions, SSL_SESSION *ssl_sessions[],
+ SSL *ssl, const uint8_t *session_id)
+{
+ time_t tm = time(NULL);
+ time_t oldest_sess_time = tm;
+ SSL_SESSION *oldest_sess = NULL;
+ int i;
+
+ /* no sessions? Then bail */
+ if (max_sessions == 0)
+ return NULL;
+
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+ if (session_id)
+ {
+ for (i = 0; i < max_sessions; i++)
+ {
+ if (ssl_sessions[i])
+ {
+ /* kill off any expired sessions (including those in
+ the future) */
+ if ((tm > ssl_sessions[i]->conn_time + SSL_EXPIRY_TIME) ||
+ (tm < ssl_sessions[i]->conn_time))
+ {
+ session_free(ssl_sessions, i);
+ continue;
+ }
+
+ /* if the session id matches, it must still be less than
+ the expiry time */
+ if (memcmp(ssl_sessions[i]->session_id, session_id,
+ SSL_SESSION_ID_SIZE) == 0)
+ {
+ ssl->session_index = i;
+ memcpy(ssl->dc->master_secret,
+ ssl_sessions[i]->master_secret, SSL_SECRET_SIZE);
+ SET_SSL_FLAG(SSL_SESSION_RESUME);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+ return ssl_sessions[i]; /* a session was found */
+ }
+ }
+ }
+ }
+
+ /* If we've got here, no matching session was found - so create one */
+ for (i = 0; i < max_sessions; i++)
+ {
+ if (ssl_sessions[i] == NULL)
+ {
+ /* perfect, this will do */
+ ssl_sessions[i] = (SSL_SESSION *)calloc(1, sizeof(SSL_SESSION));
+ ssl_sessions[i]->conn_time = tm;
+ ssl->session_index = i;
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+ return ssl_sessions[i]; /* return the session object */
+ }
+ else if (ssl_sessions[i]->conn_time <= oldest_sess_time)
+ {
+ /* find the oldest session */
+ oldest_sess_time = ssl_sessions[i]->conn_time;
+ oldest_sess = ssl_sessions[i];
+ ssl->session_index = i;
+ }
+ }
+
+ /* ok, we've used up all of our sessions. So blow the oldest session away */
+ oldest_sess->conn_time = tm;
+ memset(oldest_sess->session_id, 0, SSL_SESSION_ID_SIZE);
+ memset(oldest_sess->master_secret, 0, SSL_SECRET_SIZE);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+ return oldest_sess;
+}
+
+/**
+ * Free an existing session.
+ */
+static void session_free(SSL_SESSION *ssl_sessions[], int sess_index)
+{
+ if (ssl_sessions[sess_index])
+ {
+ free(ssl_sessions[sess_index]);
+ ssl_sessions[sess_index] = NULL;
+ }
+}
+
+/**
+ * This ssl object doesn't want this session anymore.
+ */
+void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl)
+{
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+
+ if (ssl->ssl_ctx->num_sessions)
+ {
+ session_free(ssl_sessions, ssl->session_index);
+ ssl->session = NULL;
+ }
+
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+}
+#endif /* CONFIG_SSL_SKELETON_MODE */
+
+/*
+ * Get the session id for a handshake. This will be a 32 byte sequence.
+ */
+EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl)
+{
+ return ssl->session_id;
+}
+
+/*
+ * Get the session id size for a handshake.
+ */
+EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl)
+{
+ return ssl->sess_id_size;
+}
+
+/*
+ * Return the cipher id (in the SSL form).
+ */
+EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl)
+{
+ return ssl->cipher;
+}
+
+/*
+ * Return the status of the handshake.
+ */
+EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl)
+{
+ return ssl->hs_status;
+}
+
+/*
+ * Retrieve various parameters about the SSL engine.
+ */
+EXP_FUNC int STDCALL ssl_get_config(int offset)
+{
+ switch (offset)
+ {
+ /* return the appropriate build mode */
+ case SSL_BUILD_MODE:
+#if defined(CONFIG_SSL_FULL_MODE)
+ return SSL_BUILD_FULL_MODE;
+#elif defined(CONFIG_SSL_ENABLE_CLIENT)
+ return SSL_BUILD_ENABLE_CLIENT;
+#elif defined(CONFIG_ENABLE_VERIFICATION)
+ return SSL_BUILD_ENABLE_VERIFICATION;
+#elif defined(CONFIG_SSL_SERVER_ONLY )
+ return SSL_BUILD_SERVER_ONLY;
+#else
+ return SSL_BUILD_SKELETON_MODE;
+#endif
+
+ case SSL_MAX_CERT_CFG_OFFSET:
+ return CONFIG_SSL_MAX_CERTS;
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ case SSL_MAX_CA_CERT_CFG_OFFSET:
+ return CONFIG_X509_MAX_CA_CERTS;
+#endif
+#ifdef CONFIG_SSL_HAS_PEM
+ case SSL_HAS_PEM:
+ return 1;
+#endif
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Check the certificate chain to see if the certs are supported
+ */
+static int check_certificate_chain(SSL *ssl)
+{
+ int i = 0;
+ int ret = SSL_OK;
+
+ while (i < ssl->ssl_ctx->chain_length)
+ {
+ int j = 0;
+ uint8_t found = 0;
+ SSL_CERT *cert = &ssl->ssl_ctx->certs[i];
+
+ while (j < ssl->num_sig_algs)
+ {
+ if (ssl->sig_algs[j++] == cert->hash_alg)
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+
+ ret = SSL_ERROR_INVALID_CERT_HASH_ALG;
+ goto error;
+ }
+
+ i++;
+ }
+
+error:
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Authenticate a received certificate.
+ */
+EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl)
+{
+ int ret;
+ int pathLenConstraint = 0;
+
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+ ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx,
+ &pathLenConstraint);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+
+ if (ret) /* modify into an SSL error type */
+ {
+ ret = SSL_X509_ERROR(ret);
+ }
+
+ return ret;
+}
+#endif /* CONFIG_SSL_CERT_VERIFICATION */
+
+/**
+ * Process a certificate message.
+ */
+int process_certificate(SSL *ssl, X509_CTX **x509_ctx)
+{
+ int ret = SSL_OK;
+ uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index];
+ int pkt_size = ssl->bm_index;
+ int cert_size, offset = 5, offset_start;
+ int total_cert_len = (buf[offset]<<8) + buf[offset+1];
+ int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT);
+ X509_CTX *chain = 0;
+ X509_CTX **certs = 0;
+ int *cert_used = 0;
+ int num_certs = 0;
+ int i = 0;
+ offset += 2;
+
+ PARANOIA_CHECK(pkt_size, total_cert_len + offset);
+
+ // record the start point for the second pass
+ offset_start = offset;
+
+ // first pass - count the certificates
+ while (offset < total_cert_len)
+ {
+ offset++; /* skip empty char */
+ cert_size = (buf[offset]<<8) + buf[offset+1];
+ offset += 2;
+ offset += cert_size;
+ num_certs++;
+ }
+
+ PARANOIA_CHECK(pkt_size, offset);
+
+ certs = (X509_CTX**) calloc(num_certs, sizeof(void*));
+ cert_used = (int*) calloc(num_certs, sizeof(int));
+ num_certs = 0;
+
+ // restore the offset from the saved value
+ offset = offset_start;
+
+ // second pass - load the certificates
+ while (offset < total_cert_len)
+ {
+ offset++; /* skip empty char */
+ cert_size = (buf[offset]<<8) + buf[offset+1];
+ offset += 2;
+
+ if (x509_new(&buf[offset], NULL, certs+num_certs))
+ {
+ ret = SSL_ERROR_BAD_CERTIFICATE;
+ goto error;
+ }
+
+#if defined (CONFIG_SSL_FULL_MODE)
+ if (ssl->ssl_ctx->options & SSL_DISPLAY_CERTS)
+ x509_print(certs[num_certs], NULL);
+#endif
+ num_certs++;
+ offset += cert_size;
+ }
+
+ PARANOIA_CHECK(pkt_size, offset);
+
+ // third pass - link certs together, assume server cert is the first
+ *x509_ctx = certs[0];
+ chain = certs[0];
+ cert_used[0] = 1;
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ // repeat until the end of the chain is found
+ while (1)
+ {
+ // look for CA cert
+ for( i = 1; i < num_certs; i++ )
+ {
+ if (certs[i] == chain)
+ continue;
+
+ if (cert_used[i])
+ continue; // don't allow loops
+
+ if (asn1_compare_dn(chain->ca_cert_dn, certs[i]->cert_dn) == 0)
+ {
+ // CA cert found, add it to the chain
+ cert_used[i] = 1;
+ chain->next = certs[i];
+ chain = certs[i];
+ break;
+ }
+ }
+
+ // no CA cert found, reached the end of the chain
+ if (i >= num_certs)
+ break;
+ }
+
+ // clean up any certs that aren't part of the chain
+ for (i = 1; i < num_certs; i++)
+ {
+ if (cert_used[i] == 0)
+ x509_free(certs[i]);
+ }
+
+ /* if we are client we can do the verify now or later */
+ if (is_client && !IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER))
+ {
+ ret = ssl_verify_cert(ssl);
+ }
+#endif
+
+ ssl->next_state = is_client ? HS_SERVER_HELLO_DONE : HS_CLIENT_KEY_XCHG;
+ ssl->dc->bm_proc_index += offset;
+error:
+ // clean up arrays
+ if (certs)
+ free(certs);
+
+ if (cert_used)
+ free(cert_used);
+
+ return ret;
+}
+
+//#endif /* CONFIG_SSL_CERT_VERIFICATION */
+
+/**
+ * Debugging routine to display SSL handshaking stuff.
+ */
+#ifdef CONFIG_SSL_DIAGNOSTICS
+/**
+ * Debugging routine to display SSL states.
+ */
+void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok)
+{
+ const char *str;
+
+ if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES))
+ return;
+
+ printf(not_ok ? "Error - invalid State:\t" : "State:\t");
+ printf(is_send ? "sending " : "receiving ");
+
+ switch (state)
+ {
+ case HS_HELLO_REQUEST:
+ str = "Hello Request (0)";
+ break;
+
+ case HS_CLIENT_HELLO:
+ str = "Client Hello (1)";
+ break;
+
+ case HS_SERVER_HELLO:
+ str = "Server Hello (2)";
+ break;
+
+ case HS_CERTIFICATE:
+ str = "Certificate (11)";
+ break;
+
+ case HS_SERVER_KEY_XCHG:
+ str = "Certificate Request (12)";
+ break;
+
+ case HS_CERT_REQ:
+ str = "Certificate Request (13)";
+ break;
+
+ case HS_SERVER_HELLO_DONE:
+ str = "Server Hello Done (14)";
+ break;
+
+ case HS_CERT_VERIFY:
+ str = "Certificate Verify (15)";
+ break;
+
+ case HS_CLIENT_KEY_XCHG:
+ str = "Client Key Exchange (16)";
+ break;
+
+ case HS_FINISHED:
+ str = "Finished (16)";
+ break;
+
+ default:
+ str = "Error (Unknown)";
+
+ break;
+ }
+
+ printf("%s\n", str);
+}
+
+/**
+ * Debugging routine to display RSA objects
+ */
+void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx)
+{
+#ifdef CONFIG_SSL_FULL_MODE
+ if (!IS_SET_SSL_FLAG(SSL_DISPLAY_RSA))
+ return;
+
+ RSA_print(rsa_ctx);
+ TTY_FLUSH();
+#endif
+}
+
+/**
+ * Debugging routine to display SSL handshaking bytes.
+ */
+void DISPLAY_BYTES(SSL *ssl, const char *format,
+ const uint8_t *data, int size, ...)
+{
+ va_list(ap);
+
+ if (!IS_SET_SSL_FLAG(SSL_DISPLAY_BYTES))
+ return;
+
+ va_start(ap, size);
+ print_blob(format, data, size, va_arg(ap, char *));
+ va_end(ap);
+ TTY_FLUSH();
+}
+
+/**
+ * Debugging routine to display SSL handshaking errors.
+ */
+EXP_FUNC void STDCALL ssl_display_error(int error_code)
+{
+ if (error_code == SSL_OK)
+ return;
+
+ printf("Error: ");
+
+ /* X509 error? */
+ if (error_code < SSL_X509_OFFSET)
+ {
+ printf("%s\n", x509_display_error(error_code - SSL_X509_OFFSET));
+ return;
+ }
+
+ /* SSL alert error code */
+ if (error_code > SSL_ERROR_CONN_LOST)
+ {
+ printf("SSL error %d\n", -error_code);
+ return;
+ }
+
+ switch (error_code)
+ {
+ case SSL_ERROR_DEAD:
+ printf("connection dead");
+ break;
+
+ case SSL_ERROR_RECORD_OVERFLOW:
+ printf("record overflow");
+ break;
+
+ case SSL_ERROR_INVALID_HANDSHAKE:
+ printf("invalid handshake");
+ break;
+
+ case SSL_ERROR_INVALID_PROT_MSG:
+ printf("invalid protocol message");
+ break;
+
+ case SSL_ERROR_INVALID_HMAC:
+ printf("invalid mac");
+ break;
+
+ case SSL_ERROR_INVALID_VERSION:
+ printf("invalid version");
+ break;
+
+ case SSL_ERROR_INVALID_SESSION:
+ printf("invalid session");
+ break;
+
+ case SSL_ERROR_NO_CIPHER:
+ printf("no cipher");
+ break;
+
+ case SSL_ERROR_INVALID_CERT_HASH_ALG:
+ printf("invalid cert hash algorithm");
+ break;
+
+ case SSL_ERROR_CONN_LOST:
+ printf("connection lost");
+ break;
+
+ case SSL_ERROR_BAD_CERTIFICATE:
+ printf("bad certificate");
+ break;
+
+ case SSL_ERROR_INVALID_KEY:
+ printf("invalid key");
+ break;
+
+ case SSL_ERROR_FINISHED_INVALID:
+ printf("finished invalid");
+ break;
+
+ case SSL_ERROR_NO_CERT_DEFINED:
+ printf("no certificate defined");
+ break;
+
+ case SSL_ERROR_NO_CLIENT_RENOG:
+ printf("client renegotiation not supported");
+ break;
+
+ case SSL_ERROR_NOT_SUPPORTED:
+ printf("Option not supported");
+ break;
+
+ default:
+ printf("undefined as yet - %d", error_code);
+ break;
+ }
+
+ printf("\n");
+}
+
+/**
+ * Debugging routine to display alerts.
+ */
+
+/**
+ * Debugging routine to display alerts.
+ */
+void DISPLAY_ALERT(SSL *ssl, int alert)
+{
+ if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES))
+ return;
+
+ printf("Alert: ");
+
+ switch (alert)
+ {
+ case SSL_ALERT_CLOSE_NOTIFY:
+ printf("close notify");
+ break;
+
+ case SSL_ALERT_UNEXPECTED_MESSAGE:
+ printf("unexpected message");
+ break;
+
+ case SSL_ALERT_BAD_RECORD_MAC:
+ printf("bad record mac");
+ break;
+
+ case SSL_ALERT_RECORD_OVERFLOW:
+ printf("record overlow");
+ break;
+
+ case SSL_ALERT_HANDSHAKE_FAILURE:
+ printf("handshake failure");
+ break;
+
+ case SSL_ALERT_BAD_CERTIFICATE:
+ printf("bad certificate");
+ break;
+
+ case SSL_ALERT_UNSUPPORTED_CERTIFICATE:
+ printf("unsupported certificate");
+ break;
+
+ case SSL_ALERT_CERTIFICATE_EXPIRED:
+ printf("certificate expired");
+ break;
+
+ case SSL_ALERT_CERTIFICATE_UNKNOWN:
+ printf("certificate unknown");
+ break;
+
+ case SSL_ALERT_ILLEGAL_PARAMETER:
+ printf("illegal parameter");
+ break;
+
+ case SSL_ALERT_UNKNOWN_CA:
+ printf("unknown ca");
+ break;
+
+ case SSL_ALERT_DECODE_ERROR:
+ printf("decode error");
+ break;
+
+ case SSL_ALERT_DECRYPT_ERROR:
+ printf("decrypt error");
+ break;
+
+ case SSL_ALERT_INVALID_VERSION:
+ printf("invalid version");
+ break;
+
+ case SSL_ALERT_NO_RENEGOTIATION:
+ printf("no renegotiation");
+ break;
+
+ default:
+ printf("alert - (unknown %d)", alert);
+ break;
+ }
+
+ printf("\n");
+}
+
+#endif /* CONFIG_SSL_FULL_MODE */
+
+/**
+ * Return the version of this library.
+ */
+EXP_FUNC const char * STDCALL ssl_version()
+{
+ static const char * axtls_version = AXTLS_VERSION;
+ return axtls_version;
+}
+
+/**
+ * Enable the various language bindings to work regardless of the
+ * configuration - they just return an error statement and a bad return code.
+ */
+#if !defined(CONFIG_SSL_DIAGNOSTICS)
+EXP_FUNC void STDCALL ssl_display_error(int error_code) {}
+#endif
+
+#ifdef CONFIG_BINDINGS
+#if !defined(CONFIG_SSL_ENABLE_CLIENT)
+EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, long client_fd,
+ const uint8_t *session_id, uint8_t sess_id_size,
+ SSL_EXTENSIONS* ssl_ext)
+{
+ printf("%s", unsupported_str);
+ return NULL;
+}
+#endif
+
+#if !defined(CONFIG_SSL_CERT_VERIFICATION)
+EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl)
+{
+ printf("%s", unsupported_str);
+ return -1;
+}
+
+
+EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component)
+{
+ printf("%s", unsupported_str);
+ return NULL;
+}
+
+EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int index)
+{
+ printf("%s", unsupported_str);
+ return NULL;
+}
+
+#endif /* CONFIG_SSL_CERT_VERIFICATION */
+
+#endif /* CONFIG_BINDINGS */
+
diff --git a/circuitpython/lib/axtls/ssl/tls1.h b/circuitpython/lib/axtls/ssl/tls1.h
new file mode 100755
index 0000000..e8ed8a7
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/tls1.h
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2007-2016, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * @file tls1.h
+ *
+ * @brief The definitions for the TLS library.
+ */
+#ifndef HEADER_SSL_LIB_H
+#define HEADER_SSL_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "version.h"
+#include "config.h"
+#include "os_int.h"
+#include "os_port.h"
+#include "crypto.h"
+#include "crypto_misc.h"
+
+#define SSL_PROTOCOL_MIN_VERSION 0x31 /* TLS v1.0 */
+#define SSL_PROTOCOL_VERSION_MAX 0x33 /* TLS v1.3 */
+#define SSL_PROTOCOL_VERSION_TLS1_1 0x32 /* TLS v1.1 */
+#define SSL_PROTOCOL_VERSION_TLS1_2 0x33 /* TLS v1.2 */
+#define SSL_RANDOM_SIZE 32
+#define SSL_SECRET_SIZE 48
+#define SSL_FINISHED_HASH_SIZE 12
+#define SSL_RECORD_SIZE 5
+#define SSL_SERVER_READ 0
+#define SSL_SERVER_WRITE 1
+#define SSL_CLIENT_READ 2
+#define SSL_CLIENT_WRITE 3
+#define SSL_HS_HDR_SIZE 4
+
+/* the flags we use while establishing a connection */
+#define SSL_NEED_RECORD 0x0001
+#define SSL_TX_ENCRYPTED 0x0002
+#define SSL_RX_ENCRYPTED 0x0004
+#define SSL_SESSION_RESUME 0x0008
+#define SSL_IS_CLIENT 0x0010
+#define SSL_HAS_CERT_REQ 0x0020
+#define SSL_SENT_CLOSE_NOTIFY 0x0040
+
+/* some macros to muck around with flag bits */
+#define SET_SSL_FLAG(A) (ssl->flag |= A)
+#define CLR_SSL_FLAG(A) (ssl->flag &= ~A)
+#define IS_SET_SSL_FLAG(A) (ssl->flag & A)
+
+#define MAX_KEY_BYTE_SIZE 512 /* for a 4096 bit key */
+/* On send, will send another SSL fragment if plaintext exceeds RT_MAX_PLAIN_LENGTH */
+#ifndef RT_MAX_PLAIN_LENGTH
+#define RT_MAX_PLAIN_LENGTH 16384
+#endif
+/* Total receive buffer is RT_MAX_PLAIN_LENGTH + RT_EXTRA */
+#ifndef RT_EXTRA
+#define RT_EXTRA 1024
+#endif
+#define BM_RECORD_OFFSET 5
+
+#define NUM_PROTOCOLS 4
+
+#define MAX_SIG_ALGORITHMS 4
+#define SIG_ALG_SHA1 2
+#define SIG_ALG_SHA256 4
+#define SIG_ALG_SHA384 5
+#define SIG_ALG_SHA512 6
+#define SIG_ALG_RSA 1
+
+#define PARANOIA_CHECK(A, B) if (A < B) { \
+ ret = SSL_ERROR_INVALID_HANDSHAKE; goto error; }
+
+/* protocol types */
+enum
+{
+ PT_CHANGE_CIPHER_SPEC = 20,
+ PT_ALERT_PROTOCOL,
+ PT_HANDSHAKE_PROTOCOL,
+ PT_APP_PROTOCOL_DATA
+};
+
+/* handshaking types */
+enum
+{
+ HS_HELLO_REQUEST,
+ HS_CLIENT_HELLO,
+ HS_SERVER_HELLO,
+ HS_CERTIFICATE = 11,
+ HS_SERVER_KEY_XCHG,
+ HS_CERT_REQ,
+ HS_SERVER_HELLO_DONE,
+ HS_CERT_VERIFY,
+ HS_CLIENT_KEY_XCHG,
+ HS_FINISHED = 20
+};
+
+/* SSL extension types */
+enum
+{
+ SSL_EXT_SERVER_NAME = 0,
+ SSL_EXT_MAX_FRAGMENT_SIZE,
+ SSL_EXT_SIG_ALG = 0x0d,
+};
+
+typedef struct
+{
+ uint8_t cipher;
+ uint8_t key_size;
+ uint8_t iv_size;
+ uint8_t padding_size;
+ uint8_t digest_size;
+ uint8_t key_block_size;
+ hmac_func hmac;
+ crypt_func encrypt;
+ crypt_func decrypt;
+} cipher_info_t;
+
+struct _SSLObjLoader
+{
+ uint8_t *buf;
+ int len;
+};
+
+typedef struct _SSLObjLoader SSLObjLoader;
+
+typedef struct
+{
+ time_t conn_time;
+ uint8_t session_id[SSL_SESSION_ID_SIZE];
+ uint8_t master_secret[SSL_SECRET_SIZE];
+} SSL_SESSION;
+
+typedef struct
+{
+ uint8_t *buf;
+ int size;
+ uint8_t hash_alg;
+} SSL_CERT;
+
+typedef struct
+{
+ MD5_CTX md5_ctx;
+ SHA1_CTX sha1_ctx;
+ SHA256_CTX sha256_ctx;
+ uint8_t client_random[SSL_RANDOM_SIZE]; /* client's random sequence */
+ uint8_t server_random[SSL_RANDOM_SIZE]; /* server's random sequence */
+ uint8_t final_finish_mac[128];
+ uint8_t master_secret[SSL_SECRET_SIZE];
+ uint8_t key_block[256];
+ uint16_t bm_proc_index;
+ uint8_t key_block_generated;
+} DISPOSABLE_CTX;
+
+typedef struct
+{
+ const char *host_name; /* Needed for the SNI support */
+ /* Needed for the Max Fragment Size Extension.
+ Allowed values: 2^9, 2^10 .. 2^14 */
+ uint16_t max_fragment_size;
+} SSL_EXTENSIONS;
+
+struct _SSL
+{
+ uint32_t flag;
+ uint16_t need_bytes;
+ uint16_t got_bytes;
+ uint8_t record_type;
+ uint8_t cipher;
+ uint8_t sess_id_size;
+ uint8_t version;
+ uint8_t client_version;
+ int16_t next_state;
+ int16_t hs_status;
+ DISPOSABLE_CTX *dc; /* temporary data which we'll get rid of soon */
+ long client_fd;
+ const cipher_info_t *cipher_info;
+ void *encrypt_ctx;
+ void *decrypt_ctx;
+ uint8_t bm_all_data[RT_MAX_PLAIN_LENGTH+RT_EXTRA];
+ uint8_t *bm_data;
+ uint16_t bm_index;
+ uint16_t bm_read_index;
+ uint8_t sig_algs[MAX_SIG_ALGORITHMS];
+ uint8_t num_sig_algs;
+ struct _SSL *next; /* doubly linked list */
+ struct _SSL *prev;
+ struct _SSL_CTX *ssl_ctx; /* back reference to a clnt/svr ctx */
+#ifndef CONFIG_SSL_SKELETON_MODE
+ uint16_t session_index;
+ SSL_SESSION *session;
+#endif
+#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_ENABLE_CLIENT)
+ X509_CTX *x509_ctx;
+#endif
+
+ uint8_t session_id[SSL_SESSION_ID_SIZE];
+ uint8_t client_mac[SHA256_SIZE]; /* for HMAC verification */
+ uint8_t server_mac[SHA256_SIZE]; /* for HMAC verification */
+ uint8_t read_sequence[8]; /* 64 bit sequence number */
+ uint8_t write_sequence[8]; /* 64 bit sequence number */
+ uint8_t hmac_header[SSL_RECORD_SIZE]; /* rx hmac */
+ SSL_EXTENSIONS *extensions; /* Contains the SSL (client) extensions */
+};
+
+typedef struct _SSL SSL;
+
+struct _SSL_CTX
+{
+ uint32_t options;
+ uint8_t chain_length;
+ RSA_CTX *rsa_ctx;
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ CA_CERT_CTX *ca_cert_ctx;
+#endif
+ SSL *head;
+ SSL *tail;
+ SSL_CERT certs[CONFIG_SSL_MAX_CERTS];
+#ifndef CONFIG_SSL_SKELETON_MODE
+ uint16_t num_sessions;
+ SSL_SESSION **ssl_sessions;
+#endif
+#ifdef CONFIG_SSL_CTX_MUTEXING
+ SSL_CTX_MUTEX_TYPE mutex;
+#endif
+#ifdef CONFIG_OPENSSL_COMPATIBLE
+ void *bonus_attr;
+#endif
+};
+
+typedef struct _SSL_CTX SSL_CTX;
+
+/* backwards compatibility */
+typedef struct _SSL_CTX SSLCTX;
+
+extern const uint8_t ssl_prot_prefs[NUM_PROTOCOLS];
+
+SSL *ssl_new(SSL_CTX *ssl_ctx, long client_fd);
+void disposable_new(SSL *ssl);
+void disposable_free(SSL *ssl);
+int send_packet(SSL *ssl, uint8_t protocol,
+ const uint8_t *in, int length);
+int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len);
+int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len);
+int process_finished(SSL *ssl, uint8_t *buf, int hs_len);
+int process_sslv23_client_hello(SSL *ssl);
+int send_alert(SSL *ssl, int error_code);
+int send_finished(SSL *ssl);
+int send_certificate(SSL *ssl);
+int basic_read(SSL *ssl, uint8_t **in_data);
+int send_change_cipher_spec(SSL *ssl);
+int finished_digest(SSL *ssl, const char *label, uint8_t *digest);
+void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret);
+void add_packet(SSL *ssl, const uint8_t *pkt, int len);
+int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len);
+int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj);
+void ssl_obj_free(SSLObjLoader *ssl_obj);
+int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password);
+int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password);
+int load_key_certs(SSL_CTX *ssl_ctx);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len);
+void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx);
+#endif
+#ifdef CONFIG_SSL_ENABLE_CLIENT
+int do_client_connect(SSL *ssl);
+#endif
+
+#ifdef CONFIG_SSL_DIAGNOSTICS
+void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok);
+void DISPLAY_BYTES(SSL *ssl, const char *format,
+ const uint8_t *data, int size, ...);
+void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx);
+void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx);
+void DISPLAY_ALERT(SSL *ssl, int alert);
+#else
+#define DISPLAY_STATE(A,B,C,D)
+#define DISPLAY_CERT(A,B)
+#define DISPLAY_RSA(A,B)
+#define DISPLAY_ALERT(A, B)
+#ifdef WIN32
+void DISPLAY_BYTES(SSL *ssl, const char *format,/* win32 has no variadic macros */
+ const uint8_t *data, int size, ...);
+#else
+#define DISPLAY_BYTES(A,B,C,D,...)
+#endif
+#endif
+
+//#ifdef CONFIG_SSL_CERT_VERIFICATION
+int process_certificate(SSL *ssl, X509_CTX **x509_ctx);
+//#endif
+
+SSL_SESSION *ssl_session_update(int max_sessions,
+ SSL_SESSION *ssl_sessions[], SSL *ssl,
+ const uint8_t *session_id);
+void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/tls1_clnt.c b/circuitpython/lib/axtls/ssl/tls1_clnt.c
new file mode 100644
index 0000000..667825f
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/tls1_clnt.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2007-2016, Cameron Rich
+ *
+ * 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 axTLS project 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 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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#include "os_port.h"
+#include "ssl.h"
+
+#ifdef CONFIG_SSL_ENABLE_CLIENT /* all commented out if no client */
+
+/* support sha512/384/256/1 RSA */
+static const uint8_t g_sig_alg[] = {
+ 0x00, SSL_EXT_SIG_ALG,
+ 0x00, 0x0a, 0x00, 0x08,
+ SIG_ALG_SHA512, SIG_ALG_RSA,
+ SIG_ALG_SHA384, SIG_ALG_RSA,
+ SIG_ALG_SHA256, SIG_ALG_RSA,
+ SIG_ALG_SHA1, SIG_ALG_RSA
+};
+
+static const uint8_t g_asn1_sha256[] =
+{
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
+};
+
+static int send_client_hello(SSL *ssl);
+static int process_server_hello(SSL *ssl);
+static int process_server_hello_done(SSL *ssl);
+static int send_client_key_xchg(SSL *ssl);
+static int process_cert_req(SSL *ssl);
+static int send_cert_verify(SSL *ssl);
+
+/*
+ * Establish a new SSL connection to an SSL server.
+ */
+EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, long client_fd, const
+ uint8_t *session_id, uint8_t sess_id_size, SSL_EXTENSIONS* ssl_ext)
+{
+ SSL *ssl = ssl_new(ssl_ctx, client_fd);
+ ssl->version = SSL_PROTOCOL_VERSION_MAX; /* try top version first */
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ if (session_id && ssl_ctx->num_sessions)
+ {
+ if (sess_id_size > SSL_SESSION_ID_SIZE) /* validity check */
+ {
+ ssl_free(ssl);
+ return NULL;
+ }
+
+ memcpy(ssl->session_id, session_id, sess_id_size);
+ ssl->sess_id_size = sess_id_size;
+ SET_SSL_FLAG(SSL_SESSION_RESUME); /* just flag for later */
+ }
+#endif
+
+ ssl->extensions = ssl_ext;
+
+ SET_SSL_FLAG(SSL_IS_CLIENT);
+ do_client_connect(ssl);
+ return ssl;
+}
+
+/*
+ * Process the handshake record.
+ */
+int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len)
+{
+ int ret;
+
+ /* To get here the state must be valid */
+ switch (handshake_type)
+ {
+ case HS_SERVER_HELLO:
+ ret = process_server_hello(ssl);
+ break;
+
+ case HS_CERTIFICATE:
+ ret = process_certificate(ssl, &ssl->x509_ctx);
+ break;
+
+ case HS_SERVER_HELLO_DONE:
+ if ((ret = process_server_hello_done(ssl)) == SSL_OK)
+ {
+ if (IS_SET_SSL_FLAG(SSL_HAS_CERT_REQ))
+ {
+ if ((ret = send_certificate(ssl)) == SSL_OK &&
+ (ret = send_client_key_xchg(ssl)) == SSL_OK)
+ {
+ send_cert_verify(ssl);
+ }
+ }
+ else
+ {
+ ret = send_client_key_xchg(ssl);
+ }
+
+ if (ret == SSL_OK &&
+ (ret = send_change_cipher_spec(ssl)) == SSL_OK)
+ {
+ ret = send_finished(ssl);
+ }
+ }
+ break;
+
+ case HS_CERT_REQ:
+ ret = process_cert_req(ssl);
+ break;
+
+ case HS_FINISHED:
+ ret = process_finished(ssl, buf, hs_len);
+ disposable_free(ssl); /* free up some memory */
+ /* note: client renegotiation is not allowed after this */
+ break;
+
+ case HS_HELLO_REQUEST:
+ disposable_new(ssl);
+ ret = do_client_connect(ssl);
+ break;
+
+ default:
+ ret = SSL_ERROR_INVALID_HANDSHAKE;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Do the handshaking from the beginning.
+ */
+int do_client_connect(SSL *ssl)
+{
+ int ret = SSL_OK;
+
+ send_client_hello(ssl); /* send the client hello */
+ ssl->bm_read_index = 0;
+ ssl->next_state = HS_SERVER_HELLO;
+ ssl->hs_status = SSL_NOT_OK; /* not connected */
+
+ /* sit in a loop until it all looks good */
+ if (!IS_SET_SSL_FLAG(SSL_CONNECT_IN_PARTS))
+ {
+ while (ssl->hs_status != SSL_OK)
+ {
+ ret = ssl_read(ssl, NULL);
+
+ if (ret < SSL_OK)
+ break;
+ }
+
+ ssl->hs_status = ret; /* connected? */
+ }
+
+ return ret;
+}
+
+/*
+ * Send the initial client hello.
+ */
+static int send_client_hello(SSL *ssl)
+{
+ uint8_t *buf = ssl->bm_data;
+ time_t tm = time(NULL);
+ uint8_t *tm_ptr = &buf[6]; /* time will go here */
+ int i, offset, ext_offset;
+ int ext_len = 0;
+
+
+ buf[0] = HS_CLIENT_HELLO;
+ buf[1] = 0;
+ buf[2] = 0;
+ /* byte 3 is calculated later */
+ buf[4] = 0x03;
+ buf[5] = ssl->version & 0x0f;
+
+ /* client random value - spec says that 1st 4 bytes are big endian time */
+ *tm_ptr++ = (uint8_t)(((long)tm & 0xff000000) >> 24);
+ *tm_ptr++ = (uint8_t)(((long)tm & 0x00ff0000) >> 16);
+ *tm_ptr++ = (uint8_t)(((long)tm & 0x0000ff00) >> 8);
+ *tm_ptr++ = (uint8_t)(((long)tm & 0x000000ff));
+ if (get_random(SSL_RANDOM_SIZE-4, &buf[10]) < 0)
+ return SSL_NOT_OK;
+
+ memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE);
+ offset = 6 + SSL_RANDOM_SIZE;
+
+ /* give session resumption a go */
+ if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) /* set initially by user */
+ {
+ buf[offset++] = ssl->sess_id_size;
+ memcpy(&buf[offset], ssl->session_id, ssl->sess_id_size);
+ offset += ssl->sess_id_size;
+ CLR_SSL_FLAG(SSL_SESSION_RESUME); /* clear so we can set later */
+ }
+ else
+ {
+ /* no session id - because no session resumption just yet */
+ buf[offset++] = 0;
+ }
+
+ buf[offset++] = 0; /* number of ciphers */
+ buf[offset++] = NUM_PROTOCOLS*2;/* number of ciphers */
+
+ /* put all our supported protocols in our request */
+ for (i = 0; i < NUM_PROTOCOLS; i++)
+ {
+ buf[offset++] = 0; /* cipher we are using */
+ buf[offset++] = ssl_prot_prefs[i];
+ }
+
+ buf[offset++] = 1; /* no compression */
+ buf[offset++] = 0;
+
+ ext_offset = offset;
+
+ buf[offset++] = 0; /* total length of extensions */
+ buf[offset++] = 0;
+
+ /* send the signature algorithm extension for TLS 1.2+ */
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2)
+ {
+ memcpy(&buf[offset], g_sig_alg, sizeof(g_sig_alg));
+ offset += sizeof(g_sig_alg);
+ ext_len += sizeof(g_sig_alg);
+ }
+
+ if (ssl->extensions != NULL)
+ {
+ /* send the host name if specified */
+ if (ssl->extensions->host_name != NULL)
+ {
+ size_t host_len = strlen(ssl->extensions->host_name);
+ buf[offset++] = 0;
+ buf[offset++] = SSL_EXT_SERVER_NAME; /* server_name(0) (65535) */
+ buf[offset++] = 0;
+ buf[offset++] = host_len + 5; /* server_name length */
+ buf[offset++] = 0;
+ buf[offset++] = host_len + 3; /* server_list length */
+ buf[offset++] = 0; /* host_name(0) (255) */
+ buf[offset++] = 0;
+ buf[offset++] = host_len; /* host_name length */
+ strncpy((char*) &buf[offset], ssl->extensions->host_name, host_len);
+ offset += host_len;
+ ext_len += host_len + 9;
+ }
+
+ if (ssl->extensions->max_fragment_size)
+ {
+ buf[offset++] = 0;
+ buf[offset++] = SSL_EXT_MAX_FRAGMENT_SIZE;
+
+ buf[offset++] = 0; // size of data
+ buf[offset++] = 2;
+
+ buf[offset++] = (uint8_t)
+ ((ssl->extensions->max_fragment_size >> 8) & 0xff);
+ buf[offset++] = (uint8_t)
+ (ssl->extensions->max_fragment_size & 0xff);
+ ext_len += 6;
+ }
+ }
+
+ if (ext_len > 0)
+ {
+ // update the extensions length value
+ buf[ext_offset] = (uint8_t) ((ext_len >> 8) & 0xff);
+ buf[ext_offset + 1] = (uint8_t) (ext_len & 0xff);
+ }
+
+ buf[3] = offset - 4; /* handshake size */
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset);
+}
+
+/*
+ * Process the server hello.
+ */
+static int process_server_hello(SSL *ssl)
+{
+ uint8_t *buf = ssl->bm_data;
+ int pkt_size = ssl->bm_index;
+#ifndef CONFIG_SSL_SKELETON_MODE
+ int num_sessions = ssl->ssl_ctx->num_sessions;
+#endif
+ uint8_t sess_id_size;
+ int offset, ret = SSL_OK;
+
+ /* check that we are talking to a TLSv1 server */
+ uint8_t version = (buf[4] << 4) + buf[5];
+ if (version > SSL_PROTOCOL_VERSION_MAX)
+ {
+ version = SSL_PROTOCOL_VERSION_MAX;
+ }
+ else if (ssl->version < SSL_PROTOCOL_MIN_VERSION)
+ {
+ ret = SSL_ERROR_INVALID_VERSION;
+#ifdef CONFIG_SSL_DIAGNOSTICS
+ ssl_display_error(ret);
+#endif
+ goto error;
+ }
+
+ ssl->version = version;
+
+ /* get the server random value */
+ memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE);
+ offset = 6 + SSL_RANDOM_SIZE; /* skip of session id size */
+ sess_id_size = buf[offset++];
+
+ if (sess_id_size > SSL_SESSION_ID_SIZE)
+ {
+ ret = SSL_ERROR_INVALID_SESSION;
+ goto error;
+ }
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ if (num_sessions)
+ {
+ ssl->session = ssl_session_update(num_sessions,
+ ssl->ssl_ctx->ssl_sessions, ssl, &buf[offset]);
+ memcpy(ssl->session->session_id, &buf[offset], sess_id_size);
+
+ /* pad the rest with 0's */
+ if (sess_id_size < SSL_SESSION_ID_SIZE)
+ {
+ memset(&ssl->session->session_id[sess_id_size], 0,
+ SSL_SESSION_ID_SIZE-sess_id_size);
+ }
+ }
+#endif
+
+ memcpy(ssl->session_id, &buf[offset], sess_id_size);
+ ssl->sess_id_size = sess_id_size;
+ offset += sess_id_size;
+
+ /* get the real cipher we are using - ignore MSB */
+ ssl->cipher = buf[++offset];
+ ssl->next_state = IS_SET_SSL_FLAG(SSL_SESSION_RESUME) ?
+ HS_FINISHED : HS_CERTIFICATE;
+
+ offset += 2; // ignore compression
+ PARANOIA_CHECK(pkt_size, offset);
+
+ ssl->dc->bm_proc_index = offset;
+ PARANOIA_CHECK(pkt_size, offset);
+
+ // no extensions
+error:
+ return ret;
+}
+
+/**
+ * Process the server hello done message.
+ */
+static int process_server_hello_done(SSL *ssl)
+{
+ ssl->next_state = HS_FINISHED;
+ return SSL_OK;
+}
+
+/*
+ * Send a client key exchange message.
+ */
+static int send_client_key_xchg(SSL *ssl)
+{
+ uint8_t *buf = ssl->bm_data;
+ uint8_t premaster_secret[SSL_SECRET_SIZE];
+ int enc_secret_size = -1;
+
+ buf[0] = HS_CLIENT_KEY_XCHG;
+ buf[1] = 0;
+
+ // spec says client must use the what is initially negotiated -
+ // and this is our current version
+ premaster_secret[0] = 0x03;
+ premaster_secret[1] = SSL_PROTOCOL_VERSION_MAX & 0x0f;
+ if (get_random(SSL_SECRET_SIZE-2, &premaster_secret[2]) < 0)
+ return SSL_NOT_OK;
+
+ DISPLAY_RSA(ssl, ssl->x509_ctx->rsa_ctx);
+
+ /* rsa_ctx->bi_ctx is not thread-safe */
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+ enc_secret_size = RSA_encrypt(ssl->x509_ctx->rsa_ctx, premaster_secret,
+ SSL_SECRET_SIZE, &buf[6], 0);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+
+ buf[2] = (enc_secret_size + 2) >> 8;
+ buf[3] = (enc_secret_size + 2) & 0xff;
+ buf[4] = enc_secret_size >> 8;
+ buf[5] = enc_secret_size & 0xff;
+
+ generate_master_secret(ssl, premaster_secret);
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, enc_secret_size+6);
+}
+
+/*
+ * Process the certificate request.
+ */
+static int process_cert_req(SSL *ssl)
+{
+ uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index];
+ int ret = SSL_OK;
+ int cert_req_size = (buf[2]<<8) + buf[3];
+ int offset = 4;
+ int pkt_size = ssl->bm_index;
+ uint8_t cert_type_len, sig_alg_len;
+
+ PARANOIA_CHECK(pkt_size, offset + cert_req_size);
+ ssl->dc->bm_proc_index = cert_req_size;
+
+ /* don't do any processing - we will send back an RSA certificate anyway */
+ ssl->next_state = HS_SERVER_HELLO_DONE;
+ SET_SSL_FLAG(SSL_HAS_CERT_REQ);
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ // supported certificate types
+ cert_type_len = buf[offset++];
+ PARANOIA_CHECK(pkt_size, offset + cert_type_len);
+ offset += cert_type_len;
+
+ // supported signature algorithms
+ sig_alg_len = buf[offset++] << 8;
+ sig_alg_len += buf[offset++];
+ PARANOIA_CHECK(pkt_size, offset + sig_alg_len);
+
+ while (sig_alg_len > 0)
+ {
+ uint8_t hash_alg = buf[offset++];
+ uint8_t sig_alg = buf[offset++];
+ sig_alg_len -= 2;
+
+ if (sig_alg == SIG_ALG_RSA &&
+ (hash_alg == SIG_ALG_SHA1 ||
+ hash_alg == SIG_ALG_SHA256 ||
+ hash_alg == SIG_ALG_SHA384 ||
+ hash_alg == SIG_ALG_SHA512))
+ {
+ ssl->sig_algs[ssl->num_sig_algs++] = hash_alg;
+ }
+ }
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Send a certificate verify message.
+ */
+static int send_cert_verify(SSL *ssl)
+{
+ uint8_t *buf = ssl->bm_data;
+ uint8_t dgst[SHA1_SIZE+MD5_SIZE+15];
+ RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx;
+ int n = 0, ret;
+ int offset = 0;
+ int dgst_len;
+
+ if (rsa_ctx == NULL)
+ return SSL_OK;
+
+ DISPLAY_RSA(ssl, rsa_ctx);
+
+ buf[0] = HS_CERT_VERIFY;
+ buf[1] = 0;
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ buf[4] = SIG_ALG_SHA256;
+ buf[5] = SIG_ALG_RSA;
+ offset = 6;
+ memcpy(dgst, g_asn1_sha256, sizeof(g_asn1_sha256));
+ dgst_len = finished_digest(ssl, NULL, &dgst[sizeof(g_asn1_sha256)]) +
+ sizeof(g_asn1_sha256);
+ }
+ else
+ {
+ offset = 4;
+ dgst_len = finished_digest(ssl, NULL, dgst);
+ }
+
+ /* rsa_ctx->bi_ctx is not thread-safe */
+ if (rsa_ctx)
+ {
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+ n = RSA_encrypt(rsa_ctx, dgst, dgst_len, &buf[offset + 2], 1);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+
+ if (n == 0)
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+ }
+
+ buf[offset] = n >> 8; /* add the RSA size */
+ buf[offset+1] = n & 0xff;
+ n += 2;
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ n += 2; // sig/alg
+ offset -= 2;
+ }
+
+ buf[2] = n >> 8;
+ buf[3] = n & 0xff;
+ ret = send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, n + offset);
+
+error:
+ return ret;
+}
+
+#endif /* CONFIG_SSL_ENABLE_CLIENT */
diff --git a/circuitpython/lib/axtls/ssl/tls1_svr.c b/circuitpython/lib/axtls/ssl/tls1_svr.c
new file mode 100644
index 0000000..8f947ed
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/tls1_svr.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2007-2017, Cameron Rich
+ *
+ * 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 axTLS project 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 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "os_port.h"
+#include "ssl.h"
+
+#ifdef CONFIG_SSL_ENABLE_SERVER
+
+static const uint8_t g_hello_done[] = { HS_SERVER_HELLO_DONE, 0, 0, 0 };
+static const uint8_t g_asn1_sha256[] =
+{
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
+};
+
+static int process_client_hello(SSL *ssl);
+static int send_server_hello_sequence(SSL *ssl);
+static int send_server_hello(SSL *ssl);
+static int send_server_hello_done(SSL *ssl);
+static int process_client_key_xchg(SSL *ssl);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+static int send_certificate_request(SSL *ssl);
+static int process_cert_verify(SSL *ssl);
+#endif
+
+/*
+ * Establish a new SSL connection to an SSL client.
+ */
+EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, long client_fd)
+{
+ SSL *ssl;
+
+ ssl = ssl_new(ssl_ctx, client_fd);
+ ssl->next_state = HS_CLIENT_HELLO;
+
+#ifdef CONFIG_SSL_DIAGNOSTICS
+ if (ssl_ctx->chain_length == 0)
+ printf("Warning - no server certificate defined\n"); TTY_FLUSH();
+#endif
+
+ return ssl;
+}
+
+/*
+ * Process the handshake record.
+ */
+int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len)
+{
+ int ret = SSL_OK;
+ ssl->hs_status = SSL_NOT_OK; /* not connected */
+
+ /* To get here the state must be valid */
+ switch (handshake_type)
+ {
+ case HS_CLIENT_HELLO:
+ if ((ret = process_client_hello(ssl)) == SSL_OK)
+ ret = send_server_hello_sequence(ssl);
+ break;
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ case HS_CERTIFICATE:/* the client sends its cert */
+ ret = process_certificate(ssl, &ssl->x509_ctx);
+
+ if (ret == SSL_OK) /* verify the cert */
+ {
+ int cert_res;
+ int pathLenConstraint = 0;
+
+ cert_res = x509_verify(ssl->ssl_ctx->ca_cert_ctx,
+ ssl->x509_ctx, &pathLenConstraint);
+ ret = (cert_res == 0) ? SSL_OK : SSL_X509_ERROR(cert_res);
+ }
+ break;
+
+ case HS_CERT_VERIFY:
+ ret = process_cert_verify(ssl);
+ add_packet(ssl, buf, hs_len); /* needs to be done after */
+ break;
+#endif
+ case HS_CLIENT_KEY_XCHG:
+ ret = process_client_key_xchg(ssl);
+ break;
+
+ case HS_FINISHED:
+ ret = process_finished(ssl, buf, hs_len);
+ disposable_free(ssl); /* free up some memory */
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Process a client hello message.
+ */
+static int process_client_hello(SSL *ssl)
+{
+ uint8_t *buf = ssl->bm_data;
+ int pkt_size = ssl->bm_index;
+ int i, j, cs_len, id_len, offset = 6 + SSL_RANDOM_SIZE;
+ int ret = SSL_OK;
+
+ uint8_t version = (buf[4] << 4) + buf[5];
+ ssl->version = ssl->client_version = version;
+
+ if (version > SSL_PROTOCOL_VERSION_MAX)
+ {
+ /* use client's version instead */
+ ssl->version = SSL_PROTOCOL_VERSION_MAX;
+ }
+ else if (version < SSL_PROTOCOL_MIN_VERSION) /* old version supported? */
+ {
+ ret = SSL_ERROR_INVALID_VERSION;
+#ifdef CONFIG_SSL_DIAGNOSTICS
+ ssl_display_error(ret);
+#endif
+ goto error;
+ }
+
+ memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE);
+
+ /* process the session id */
+ id_len = buf[offset++];
+ if (id_len > SSL_SESSION_ID_SIZE)
+ {
+ return SSL_ERROR_INVALID_SESSION;
+ }
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions,
+ ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL);
+#endif
+
+ offset += id_len;
+ cs_len = (buf[offset]<<8) + buf[offset+1];
+ offset += 2;
+
+ PARANOIA_CHECK(pkt_size, offset + cs_len);
+
+ /* work out what cipher suite we are going to use - client defines
+ the preference */
+ for (i = 0; i < cs_len; i += 2)
+ {
+ /* only support ciphersuites with the form (0x00, xxxx) */
+ if (buf[offset+i])
+ continue;
+
+ for (j = 0; j < NUM_PROTOCOLS; j++)
+ {
+ if (ssl_prot_prefs[j] == buf[offset+i+1]) /* got a match? */
+ {
+ ssl->cipher = ssl_prot_prefs[j];
+ goto do_compression;
+ }
+ }
+ }
+
+ /* ouch! protocol is not supported */
+ return SSL_ERROR_NO_CIPHER;
+
+ /* completely ignore compression */
+do_compression:
+ offset += cs_len;
+ id_len = buf[offset++];
+ offset += id_len;
+ PARANOIA_CHECK(pkt_size, offset + id_len);
+
+ if (offset == pkt_size)
+ {
+ /* no extensions */
+ goto error;
+ }
+
+ /* extension size */
+ id_len = buf[offset++] << 8;
+ id_len += buf[offset++];
+ PARANOIA_CHECK(pkt_size, offset + id_len);
+
+ // Check for extensions from the client - only the signature algorithm
+ // is supported
+ while (offset < pkt_size)
+ {
+ int ext = buf[offset++] << 8;
+ ext += buf[offset++];
+ int ext_len = buf[offset++] << 8;
+ ext_len += buf[offset++];
+ PARANOIA_CHECK(pkt_size, offset + ext_len);
+
+ if (ext == SSL_EXT_SIG_ALG)
+ {
+ while (ext_len > 0)
+ {
+ uint8_t hash_alg = buf[offset++];
+ uint8_t sig_alg = buf[offset++];
+ ext_len -= 2;
+
+ if (sig_alg == SIG_ALG_RSA &&
+ (hash_alg == SIG_ALG_SHA1 ||
+ hash_alg == SIG_ALG_SHA256 ||
+ hash_alg == SIG_ALG_SHA384 ||
+ hash_alg == SIG_ALG_SHA512))
+ {
+ ssl->sig_algs[ssl->num_sig_algs++] = hash_alg;
+ }
+ }
+ }
+ else
+ {
+ offset += ext_len;
+ }
+ }
+
+ /* default is RSA/SHA1 */
+ if (ssl->num_sig_algs == 0)
+ {
+ ssl->sig_algs[ssl->num_sig_algs++] = SIG_ALG_SHA1;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Send the entire server hello sequence
+ */
+#if CONFIG_SSL_ENABLE_SERVER
+static int send_server_hello_sequence(SSL *ssl)
+{
+ int ret;
+
+ if ((ret = send_server_hello(ssl)) == SSL_OK)
+ {
+#ifndef CONFIG_SSL_SKELETON_MODE
+ /* resume handshake? */
+ if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME))
+ {
+ if ((ret = send_change_cipher_spec(ssl)) == SSL_OK)
+ {
+ ret = send_finished(ssl);
+ ssl->next_state = HS_FINISHED;
+ }
+ }
+ else
+#endif
+ if ((ret = send_certificate(ssl)) == SSL_OK)
+ {
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ /* ask the client for its certificate */
+ if (IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION))
+ {
+ if ((ret = send_certificate_request(ssl)) == SSL_OK)
+ {
+ ret = send_server_hello_done(ssl);
+ ssl->next_state = HS_CERTIFICATE;
+ }
+ }
+ else
+#endif
+ {
+ ret = send_server_hello_done(ssl);
+ ssl->next_state = HS_CLIENT_KEY_XCHG;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Send a server hello message.
+ */
+static int send_server_hello(SSL *ssl)
+{
+ uint8_t *buf = ssl->bm_data;
+ int offset = 0;
+
+ buf[0] = HS_SERVER_HELLO;
+ buf[1] = 0;
+ buf[2] = 0;
+ /* byte 3 is calculated later */
+ buf[4] = 0x03;
+ buf[5] = ssl->version & 0x0f;
+
+ /* server random value */
+ if (get_random(SSL_RANDOM_SIZE, &buf[6]) < 0)
+ return SSL_NOT_OK;
+
+ memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE);
+ offset = 6 + SSL_RANDOM_SIZE;
+
+#ifndef CONFIG_SSL_SKELETON_MODE
+ if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME))
+ {
+ /* retrieve id from session cache */
+ buf[offset++] = SSL_SESSION_ID_SIZE;
+ memcpy(&buf[offset], ssl->session->session_id, SSL_SESSION_ID_SIZE);
+ memcpy(ssl->session_id, ssl->session->session_id, SSL_SESSION_ID_SIZE);
+ ssl->sess_id_size = SSL_SESSION_ID_SIZE;
+ offset += SSL_SESSION_ID_SIZE;
+ }
+ else /* generate our own session id */
+#endif
+ {
+#ifndef CONFIG_SSL_SKELETON_MODE
+ buf[offset++] = SSL_SESSION_ID_SIZE;
+ get_random(SSL_SESSION_ID_SIZE, &buf[offset]);
+ memcpy(ssl->session_id, &buf[offset], SSL_SESSION_ID_SIZE);
+ ssl->sess_id_size = SSL_SESSION_ID_SIZE;
+
+ /* store id in session cache */
+ if (ssl->ssl_ctx->num_sessions)
+ {
+ memcpy(ssl->session->session_id,
+ ssl->session_id, SSL_SESSION_ID_SIZE);
+ }
+
+ offset += SSL_SESSION_ID_SIZE;
+#else
+ buf[offset++] = 0; /* don't bother with session id in skelton mode */
+#endif
+ }
+
+ buf[offset++] = 0; /* cipher we are using */
+ buf[offset++] = ssl->cipher;
+ buf[offset++] = 0; /* no compression and no extensions supported */
+ buf[3] = offset - 4; /* handshake size */
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset);
+}
+
+/*
+ * Send the server hello done message.
+ */
+static int send_server_hello_done(SSL *ssl)
+{
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
+ g_hello_done, sizeof(g_hello_done));
+}
+#endif
+
+/*
+ * Pull apart a client key exchange message. Decrypt the pre-master key (using
+ * our RSA private key) and then work out the master key. Initialise the
+ * ciphers.
+ */
+static int process_client_key_xchg(SSL *ssl)
+{
+ uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index];
+ int pkt_size = ssl->bm_index;
+ int premaster_size, secret_length = (buf[2] << 8) + buf[3];
+ uint8_t premaster_secret[MAX_KEY_BYTE_SIZE];
+ RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx;
+ int offset = 4;
+ int ret = SSL_OK;
+
+ if (rsa_ctx == NULL)
+ {
+ ret = SSL_ERROR_NO_CERT_DEFINED;
+ goto error;
+ }
+
+ /* is there an extra size field? */
+ if ((secret_length - 2) == rsa_ctx->num_octets)
+ offset += 2;
+
+ PARANOIA_CHECK(pkt_size, rsa_ctx->num_octets+offset);
+
+ /* rsa_ctx->bi_ctx is not thread-safe */
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+ premaster_size = RSA_decrypt(rsa_ctx, &buf[offset], premaster_secret,
+ sizeof(premaster_secret), 1);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+
+ if (premaster_size != SSL_SECRET_SIZE ||
+ premaster_secret[0] != 0x03 || /* must be the same as client
+ offered version */
+ premaster_secret[1] != (ssl->client_version & 0x0f))
+ {
+ /* guard against a Bleichenbacher attack */
+ if (get_random(SSL_SECRET_SIZE, premaster_secret) < 0)
+ return SSL_NOT_OK;
+
+ /* and continue - will die eventually when checking the mac */
+ }
+
+ generate_master_secret(ssl, premaster_secret);
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ ssl->next_state = IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION) ?
+ HS_CERT_VERIFY : HS_FINISHED;
+#else
+ ssl->next_state = HS_FINISHED;
+#endif
+
+ ssl->dc->bm_proc_index += rsa_ctx->num_octets+offset;
+error:
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+static const uint8_t g_cert_request[] = { HS_CERT_REQ, 0,
+ 0, 0x0e,
+ 1, 1, // rsa sign
+ 0x00, 0x08,
+ SIG_ALG_SHA256, SIG_ALG_RSA,
+ SIG_ALG_SHA512, SIG_ALG_RSA,
+ SIG_ALG_SHA384, SIG_ALG_RSA,
+ SIG_ALG_SHA1, SIG_ALG_RSA,
+ 0, 0
+};
+
+static const uint8_t g_cert_request_v1[] = { HS_CERT_REQ, 0, 0, 4, 1, 0, 0, 0 };
+
+/*
+ * Send the certificate request message.
+ */
+static int send_certificate_request(SSL *ssl)
+{
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
+ g_cert_request, sizeof(g_cert_request));
+ }
+ else
+ {
+ return send_packet(ssl, PT_HANDSHAKE_PROTOCOL,
+ g_cert_request_v1, sizeof(g_cert_request_v1));
+ }
+}
+
+/*
+ * Ensure the client has the private key by first decrypting the packet and
+ * then checking the packet digests.
+ */
+static int process_cert_verify(SSL *ssl)
+{
+ uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index];
+ int pkt_size = ssl->bm_index;
+ uint8_t dgst_buf[MAX_KEY_BYTE_SIZE];
+ uint8_t dgst[MD5_SIZE + SHA1_SIZE];
+ X509_CTX *x509_ctx = ssl->x509_ctx;
+ int ret = SSL_OK;
+ int offset = 6;
+ int rsa_len;
+ int n;
+
+ DISPLAY_RSA(ssl, x509_ctx->rsa_ctx);
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ // TODO: should really need to be able to handle other algorihms. An
+ // assumption is made on RSA/SHA256 and appears to be OK.
+ //uint8_t hash_alg = buf[4];
+ //uint8_t sig_alg = buf[5];
+ offset = 8;
+ rsa_len = (buf[6] << 8) + buf[7];
+ }
+ else
+ {
+ rsa_len = (buf[4] << 8) + buf[5];
+ }
+
+ PARANOIA_CHECK(pkt_size, offset + rsa_len);
+
+ /* rsa_ctx->bi_ctx is not thread-safe */
+ SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
+ n = RSA_decrypt(x509_ctx->rsa_ctx, &buf[offset], dgst_buf,
+ sizeof(dgst_buf), 0);
+ SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
+
+ if (ssl->version >= SSL_PROTOCOL_VERSION_TLS1_2) // TLS1.2+
+ {
+ if (memcmp(dgst_buf, g_asn1_sha256, sizeof(g_asn1_sha256)))
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+
+ finished_digest(ssl, NULL, dgst); /* calculate the digest */
+ if (memcmp(&dgst_buf[sizeof(g_asn1_sha256)], dgst, SHA256_SIZE))
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto error;
+ }
+ }
+ else // TLS1.0/1.1
+ {
+ if (n != SHA1_SIZE + MD5_SIZE)
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ goto end_cert_vfy;
+ }
+
+ finished_digest(ssl, NULL, dgst); /* calculate the digest */
+ if (memcmp(dgst_buf, dgst, MD5_SIZE + SHA1_SIZE))
+ {
+ ret = SSL_ERROR_INVALID_KEY;
+ }
+ }
+
+end_cert_vfy:
+ ssl->next_state = HS_FINISHED;
+error:
+ return ret;
+}
+
+#endif
+
+#endif
diff --git a/circuitpython/lib/axtls/ssl/x509.c b/circuitpython/lib/axtls/ssl/x509.c
new file mode 100644
index 0000000..b36f205
--- /dev/null
+++ b/circuitpython/lib/axtls/ssl/x509.c
@@ -0,0 +1,921 @@
+/*
+ * Copyright (c) 2007-2017, Cameron Rich
+ *
+ * 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 axTLS project 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 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.
+ */
+
+/**
+ * @file x509.c
+ *
+ * Certificate processing.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "os_port.h"
+#include "crypto_misc.h"
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+static int x509_v3_subject_alt_name(const uint8_t *cert, int offset,
+ X509_CTX *x509_ctx);
+static int x509_v3_basic_constraints(const uint8_t *cert, int offset,
+ X509_CTX *x509_ctx);
+static int x509_v3_key_usage(const uint8_t *cert, int offset,
+ X509_CTX *x509_ctx);
+#endif
+
+/**
+ * Construct a new x509 object.
+ * @return 0 if ok. < 0 if there was a problem.
+ */
+int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx)
+{
+ int begin_tbs, end_tbs;
+ int ret = X509_NOT_OK, offset = 0, cert_size = 0;
+ int version = 0;
+ X509_CTX *x509_ctx;
+#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */
+ BI_CTX *bi_ctx;
+#endif
+
+ *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX));
+ x509_ctx = *ctx;
+
+ /* get the certificate size */
+ asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE);
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ begin_tbs = offset; /* start of the tbs */
+ end_tbs = begin_tbs; /* work out the end of the tbs */
+ asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE);
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ /* optional version */
+ if (cert[offset] == ASN1_EXPLICIT_TAG &&
+ asn1_version(cert, &offset, &version) == X509_NOT_OK)
+ goto end_cert;
+
+ if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ /* make sure the signature is ok */
+ if (asn1_signature_type(cert, &offset, x509_ctx))
+ {
+ ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST;
+ goto end_cert;
+ }
+
+ if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) ||
+ asn1_validity(cert, &offset, x509_ctx) ||
+ asn1_name(cert, &offset, x509_ctx->cert_dn) ||
+ asn1_public_key(cert, &offset, x509_ctx))
+ {
+ goto end_cert;
+ }
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */
+ bi_ctx = x509_ctx->rsa_ctx->bi_ctx;
+
+ /* use the appropriate signature algorithm */
+ switch (x509_ctx->sig_type)
+ {
+ case SIG_TYPE_MD5:
+ {
+ MD5_CTX md5_ctx;
+ uint8_t md5_dgst[MD5_SIZE];
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ MD5_Final(md5_dgst, &md5_ctx);
+ x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE);
+ }
+ break;
+
+ case SIG_TYPE_SHA1:
+ {
+ SHA1_CTX sha_ctx;
+ uint8_t sha_dgst[SHA1_SIZE];
+ SHA1_Init(&sha_ctx);
+ SHA1_Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ SHA1_Final(sha_dgst, &sha_ctx);
+ x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE);
+ }
+ break;
+
+ case SIG_TYPE_SHA256:
+ {
+ SHA256_CTX sha256_ctx;
+ uint8_t sha256_dgst[SHA256_SIZE];
+ SHA256_Init(&sha256_ctx);
+ SHA256_Update(&sha256_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ SHA256_Final(sha256_dgst, &sha256_ctx);
+ x509_ctx->digest = bi_import(bi_ctx, sha256_dgst, SHA256_SIZE);
+ }
+ break;
+
+ case SIG_TYPE_SHA384:
+ {
+ SHA384_CTX sha384_ctx;
+ uint8_t sha384_dgst[SHA384_SIZE];
+ SHA384_Init(&sha384_ctx);
+ SHA384_Update(&sha384_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ SHA384_Final(sha384_dgst, &sha384_ctx);
+ x509_ctx->digest = bi_import(bi_ctx, sha384_dgst, SHA384_SIZE);
+ }
+ break;
+
+ case SIG_TYPE_SHA512:
+ {
+ SHA512_CTX sha512_ctx;
+ uint8_t sha512_dgst[SHA512_SIZE];
+ SHA512_Init(&sha512_ctx);
+ SHA512_Update(&sha512_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ SHA512_Final(sha512_dgst, &sha512_ctx);
+ x509_ctx->digest = bi_import(bi_ctx, sha512_dgst, SHA512_SIZE);
+ }
+ break;
+ }
+
+ if (version == 2 && asn1_next_obj(cert, &offset, ASN1_V3_DATA) > 0)
+ {
+ x509_v3_subject_alt_name(cert, offset, x509_ctx);
+ x509_v3_basic_constraints(cert, offset, x509_ctx);
+ x509_v3_key_usage(cert, offset, x509_ctx);
+ }
+
+ offset = end_tbs; /* skip the rest of v3 data */
+ if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_signature(cert, &offset, x509_ctx))
+ goto end_cert;
+#endif
+ ret = X509_OK;
+end_cert:
+ if (len)
+ {
+ *len = cert_size;
+ }
+
+ if (ret)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: Invalid X509 ASN.1 file (%s)\n",
+ x509_display_error(ret));
+#endif
+ x509_free(x509_ctx);
+ *ctx = NULL;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */
+static int x509_v3_subject_alt_name(const uint8_t *cert, int offset,
+ X509_CTX *x509_ctx)
+{
+ if ((offset = asn1_is_subject_alt_name(cert, offset)) > 0)
+ {
+ x509_ctx->subject_alt_name_present = true;
+ x509_ctx->subject_alt_name_is_critical =
+ asn1_is_critical_ext(cert, &offset);
+
+ if (asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) > 0)
+ {
+ int altlen;
+
+ if ((altlen = asn1_next_obj(cert, &offset, ASN1_SEQUENCE)) > 0)
+ {
+ int endalt = offset + altlen;
+ int totalnames = 0;
+
+ while (offset < endalt)
+ {
+ int type = cert[offset++];
+ int dnslen = get_asn1_length(cert, &offset);
+
+ if (type == ASN1_CONTEXT_DNSNAME)
+ {
+ /* sanity check the hostname due to
+https://tools.cisco.com/security/center/viewAlert.x?alertId=18852
+ */
+ if (strnlen((const char *)&cert[offset], dnslen) !=
+ dnslen)
+ {
+ return X509_VFY_ERROR_NO_TRUSTED_CERT;
+ }
+
+ x509_ctx->subject_alt_dnsnames = (char**)
+ realloc(x509_ctx->subject_alt_dnsnames,
+ (totalnames + 2) * sizeof(char*));
+ x509_ctx->subject_alt_dnsnames[totalnames] =
+ (char*)malloc(dnslen + 1);
+ x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL;
+ memcpy(x509_ctx->subject_alt_dnsnames[totalnames],
+ cert + offset, dnslen);
+ x509_ctx->subject_alt_dnsnames[totalnames][dnslen] = 0;
+ totalnames++;
+ }
+
+ offset += dnslen;
+ }
+ }
+ }
+ }
+
+ return X509_OK;
+}
+
+/**
+ * Basic constraints - see https://tools.ietf.org/html/rfc5280#page-39
+ */
+static int x509_v3_basic_constraints(const uint8_t *cert, int offset,
+ X509_CTX *x509_ctx)
+{
+ int ret = X509_OK;
+ int lenSeq = 0;
+
+ if ((offset = asn1_is_basic_constraints(cert, offset)) == 0)
+ goto end_contraints;
+
+ x509_ctx->basic_constraint_present = true;
+ x509_ctx->basic_constraint_is_critical =
+ asn1_is_critical_ext(cert, &offset);
+
+ /* Assign Defaults in case not specified
+ basic_constraint_cA will already by zero by virtue of the calloc */
+ x509_ctx->basic_constraint_cA = 0;
+ /* basic_constraint_pathLenConstraint is unlimited by default.
+ 10000 is just a large number (limits.h is not already included) */
+ x509_ctx->basic_constraint_pathLenConstraint = 10000;
+
+ if ((asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) < 0) ||
+ ((lenSeq = asn1_next_obj(cert, &offset, ASN1_SEQUENCE)) < 0))
+ {
+ ret = X509_NOT_OK;
+ }
+
+ /* If the Sequence Length is greater than zero,
+ continue with the basic_constraint_cA */
+ if ((lenSeq>0)&&(asn1_get_bool(cert, &offset,
+ &x509_ctx->basic_constraint_cA) < 0))
+ {
+ ret = X509_NOT_OK;
+ }
+
+ /* If the Sequence Length is greater than 3, it has more content than
+ the basic_constraint_cA bool, so grab the pathLenConstraint */
+ if ((lenSeq>3) && (asn1_get_int(cert, &offset,
+ &x509_ctx->basic_constraint_pathLenConstraint) < 0))
+ {
+ ret = X509_NOT_OK;
+ }
+
+end_contraints:
+ return ret;
+}
+
+/*
+ * Key usage - see https://tools.ietf.org/html/rfc5280#section-4.2.1.3
+ */
+static int x509_v3_key_usage(const uint8_t *cert, int offset,
+ X509_CTX *x509_ctx)
+{
+ int ret = X509_OK;
+
+ if ((offset = asn1_is_key_usage(cert, offset)) == 0)
+ goto end_key_usage;
+
+ x509_ctx->key_usage_present = true;
+ x509_ctx->key_usage_is_critical = asn1_is_critical_ext(cert, &offset);
+
+ if (asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) < 0 ||
+ asn1_get_bit_string_as_int(cert, &offset, &x509_ctx->key_usage))
+ {
+ ret = X509_NOT_OK;
+ }
+
+end_key_usage:
+ return ret;
+}
+#endif
+
+/**
+ * Free an X.509 object's resources.
+ */
+void x509_free(X509_CTX *x509_ctx)
+{
+ X509_CTX *next;
+ int i;
+
+ if (x509_ctx == NULL) /* if already null, then don't bother */
+ return;
+
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ free(x509_ctx->ca_cert_dn[i]);
+ free(x509_ctx->cert_dn[i]);
+ }
+
+ free(x509_ctx->signature);
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ if (x509_ctx->digest)
+ {
+ bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest);
+ }
+
+ if (x509_ctx->subject_alt_dnsnames)
+ {
+ for (i = 0; x509_ctx->subject_alt_dnsnames[i]; ++i)
+ free(x509_ctx->subject_alt_dnsnames[i]);
+
+ free(x509_ctx->subject_alt_dnsnames);
+ }
+#endif
+
+ RSA_free(x509_ctx->rsa_ctx);
+ next = x509_ctx->next;
+ free(x509_ctx);
+ x509_free(next); /* clear the chain */
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+
+static const uint8_t sig_prefix_md5[] = {0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10};
+static const uint8_t sig_prefix_sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14};
+static const uint8_t sig_prefix_sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+static const uint8_t sig_prefix_sha384[] = {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
+static const uint8_t sig_prefix_sha512[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
+
+/**
+ * Take a signature and decrypt it.
+ */
+static bigint *sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
+ uint8_t sig_type, bigint *modulus, bigint *pub_exp)
+{
+ int i;
+ bigint *decrypted_bi, *dat_bi;
+ bigint *bir = NULL;
+ uint8_t *block = (uint8_t *)alloca(sig_len);
+
+ const uint8_t *sig_prefix = NULL;
+ uint8_t sig_prefix_size = 0, hash_len = 0;
+ /* adjust our expections */
+ switch (sig_type)
+ {
+ case SIG_TYPE_MD5:
+ sig_prefix = sig_prefix_md5;
+ sig_prefix_size = sizeof(sig_prefix_md5);
+ break;
+ case SIG_TYPE_SHA1:
+ sig_prefix = sig_prefix_sha1;
+ sig_prefix_size = sizeof(sig_prefix_sha1);
+ break;
+ case SIG_TYPE_SHA256:
+ sig_prefix = sig_prefix_sha256;
+ sig_prefix_size = sizeof(sig_prefix_sha256);
+ break;
+ case SIG_TYPE_SHA384:
+ sig_prefix = sig_prefix_sha384;
+ sig_prefix_size = sizeof(sig_prefix_sha384);
+ break;
+ case SIG_TYPE_SHA512:
+ sig_prefix = sig_prefix_sha512;
+ sig_prefix_size = sizeof(sig_prefix_sha512);
+ break;
+ }
+
+ if (sig_prefix)
+ hash_len = sig_prefix[sig_prefix_size - 1];
+
+ /* check length (#A) */
+ if (sig_len < 2 + 8 + 1 + sig_prefix_size + hash_len)
+ goto err;
+
+ /* decrypt */
+ dat_bi = bi_import(ctx, sig, sig_len);
+ ctx->mod_offset = BIGINT_M_OFFSET;
+
+ /* convert to a normal block */
+ decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp);
+
+ bi_export(ctx, decrypted_bi, block, sig_len);
+ ctx->mod_offset = BIGINT_M_OFFSET;
+
+ /* check the first 2 bytes */
+ if (block[0] != 0 || block[1] != 1)
+ goto err;
+
+ /* check the padding */
+ i = 2; /* start at the first padding byte */
+ while (i < sig_len - 1 - sig_prefix_size - hash_len)
+ { /* together with (#A), we require at least 8 bytes of padding */
+ if (block[i++] != 0xFF)
+ goto err;
+ }
+
+ /* check end of padding */
+ if (block[i++] != 0)
+ goto err;
+
+ /* check the ASN.1 metadata */
+ if (memcmp(block+i, sig_prefix, sig_prefix_size))
+ goto err;
+
+ /* now we can get the hash we need */
+ bir = bi_import(ctx, block + i + sig_prefix_size, hash_len);
+
+err:
+ /* save a few bytes of memory */
+ bi_clear_cache(ctx);
+ return bir;
+}
+
+/**
+ * Do some basic checks on the certificate chain.
+ *
+ * Certificate verification consists of a number of checks:
+ * - The date of the certificate is after the start date.
+ * - The date of the certificate is before the finish date.
+ * - A root certificate exists in the certificate store.
+ * - That the certificate(s) are not self-signed.
+ * - The certificate chain is valid.
+ * - The signature of the certificate is valid.
+ * - Basic constraints
+ */
+int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert,
+ int *pathLenConstraint)
+{
+ int ret = X509_OK, i = 0;
+ bigint *cert_sig;
+ X509_CTX *next_cert = NULL;
+ BI_CTX *ctx = NULL;
+ bigint *mod = NULL, *expn = NULL;
+ int match_ca_cert = 0;
+ struct timeval tv;
+ uint8_t is_self_signed = 0;
+
+ if (cert == NULL)
+ {
+ ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
+ goto end_verify;
+ }
+
+ /* a self-signed certificate that is not in the CA store - use this
+ to check the signature */
+ if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0)
+ {
+ is_self_signed = 1;
+ ctx = cert->rsa_ctx->bi_ctx;
+ mod = cert->rsa_ctx->m;
+ expn = cert->rsa_ctx->e;
+ }
+
+ gettimeofday(&tv, NULL);
+
+ /* check the not before date */
+ if (tv.tv_sec < cert->not_before)
+ {
+ ret = X509_VFY_ERROR_NOT_YET_VALID;
+ goto end_verify;
+ }
+
+ /* check the not after date */
+ if (tv.tv_sec > cert->not_after)
+ {
+ ret = X509_VFY_ERROR_EXPIRED;
+ goto end_verify;
+ }
+
+ if (cert->basic_constraint_present)
+ {
+ /* If the cA boolean is not asserted,
+ then the keyCertSign bit in the key usage extension MUST NOT be
+ asserted. */
+ if (!cert->basic_constraint_cA &&
+ IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN))
+ {
+ ret = X509_VFY_ERROR_BASIC_CONSTRAINT;
+ goto end_verify;
+ }
+
+ /* The pathLenConstraint field is meaningful only if the cA boolean is
+ asserted and the key usage extension, if present, asserts the
+ keyCertSign bit. In this case, it gives the maximum number of
+ non-self-issued intermediate certificates that may follow this
+ certificate in a valid certification path. */
+ if (cert->basic_constraint_cA &&
+ (!cert->key_usage_present ||
+ IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN)) &&
+ (cert->basic_constraint_pathLenConstraint+1) < *pathLenConstraint)
+ {
+ ret = X509_VFY_ERROR_BASIC_CONSTRAINT;
+ goto end_verify;
+ }
+ }
+
+ next_cert = cert->next;
+
+ /* last cert in the chain - look for a trusted cert */
+ if (next_cert == NULL)
+ {
+ if (ca_cert_ctx != NULL)
+ {
+ /* go thru the CA store */
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ /* the extension is present but the cA boolean is not
+ asserted, then the certified public key MUST NOT be used
+ to verify certificate signatures. */
+ if (cert->basic_constraint_present &&
+ !ca_cert_ctx->cert[i]->basic_constraint_cA)
+ {
+ i++;
+ continue;
+ }
+
+ if (asn1_compare_dn(cert->ca_cert_dn,
+ ca_cert_ctx->cert[i]->cert_dn) == 0)
+ {
+ /* use this CA certificate for signature verification */
+ match_ca_cert = true;
+ ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx;
+ mod = ca_cert_ctx->cert[i]->rsa_ctx->m;
+ expn = ca_cert_ctx->cert[i]->rsa_ctx->e;
+
+
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ /* couldn't find a trusted cert (& let self-signed errors
+ be returned) */
+ if (!match_ca_cert && !is_self_signed)
+ {
+ ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
+ goto end_verify;
+ }
+ }
+ else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0)
+ {
+ /* check the chain */
+ ret = X509_VFY_ERROR_INVALID_CHAIN;
+ goto end_verify;
+ }
+ else /* use the next certificate in the chain for signature verify */
+ {
+ ctx = next_cert->rsa_ctx->bi_ctx;
+ mod = next_cert->rsa_ctx->m;
+ expn = next_cert->rsa_ctx->e;
+ }
+
+ /* cert is self signed */
+ if (!match_ca_cert && is_self_signed)
+ {
+ ret = X509_VFY_ERROR_SELF_SIGNED;
+ goto end_verify;
+ }
+
+ /* check the signature */
+ cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, cert->sig_type,
+ bi_clone(ctx, mod), bi_clone(ctx, expn));
+
+ if (cert_sig && cert->digest)
+ {
+ if (bi_compare(cert_sig, cert->digest) != 0)
+ ret = X509_VFY_ERROR_BAD_SIGNATURE;
+
+
+ bi_free(ctx, cert_sig);
+ }
+ else
+ {
+ ret = X509_VFY_ERROR_BAD_SIGNATURE;
+ }
+
+ if (ret)
+ goto end_verify;
+
+ /* go down the certificate chain using recursion. */
+ if (next_cert != NULL)
+ {
+ (*pathLenConstraint)++; /* don't include last certificate */
+ ret = x509_verify(ca_cert_ctx, next_cert, pathLenConstraint);
+ }
+
+end_verify:
+ return ret;
+}
+#endif
+
+#if defined (CONFIG_SSL_FULL_MODE)
+/**
+ * Used for diagnostics.
+ */
+static const char *not_part_of_cert = "<Not Part Of Certificate>";
+void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx)
+{
+ if (cert == NULL)
+ return;
+
+ printf("=== CERTIFICATE ISSUED TO ===\n");
+ printf("Common Name (CN):\t\t");
+ printf("%s\n", cert->cert_dn[X509_COMMON_NAME] ?
+ cert->cert_dn[X509_COMMON_NAME] : not_part_of_cert);
+
+ printf("Organization (O):\t\t");
+ printf("%s\n", cert->cert_dn[X509_ORGANIZATION] ?
+ cert->cert_dn[X509_ORGANIZATION] : not_part_of_cert);
+
+ if (cert->cert_dn[X509_ORGANIZATIONAL_UNIT])
+ {
+ printf("Organizational Unit (OU):\t");
+ printf("%s\n", cert->cert_dn[X509_ORGANIZATIONAL_UNIT]);
+ }
+
+ if (cert->cert_dn[X509_LOCATION])
+ {
+ printf("Location (L):\t\t\t");
+ printf("%s\n", cert->cert_dn[X509_LOCATION]);
+ }
+
+ if (cert->cert_dn[X509_COUNTRY])
+ {
+ printf("Country (C):\t\t\t");
+ printf("%s\n", cert->cert_dn[X509_COUNTRY]);
+ }
+
+ if (cert->cert_dn[X509_STATE])
+ {
+ printf("State (ST):\t\t\t");
+ printf("%s\n", cert->cert_dn[X509_STATE]);
+ }
+
+ if (cert->basic_constraint_present)
+ {
+ printf("Basic Constraints:\t\t%sCA:%s, pathlen:%d\n",
+ cert->basic_constraint_is_critical ?
+ "critical, " : "",
+ cert->basic_constraint_cA? "TRUE" : "FALSE",
+ cert->basic_constraint_pathLenConstraint);
+ }
+
+ if (cert->key_usage_present)
+ {
+ printf("Key Usage:\t\t\t%s", cert->key_usage_is_critical ?
+ "critical, " : "");
+ bool has_started = false;
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_DIGITAL_SIGNATURE))
+ {
+ printf("Digital Signature");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_NON_REPUDIATION))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Non Repudiation");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_ENCIPHERMENT))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Key Encipherment");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_DATA_ENCIPHERMENT))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Data Encipherment");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_AGREEMENT))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Key Agreement");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Key Cert Sign");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_CRL_SIGN))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("CRL Sign");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_ENCIPHER_ONLY))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Encipher Only");
+ has_started = true;
+ }
+
+ if (IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_DECIPHER_ONLY))
+ {
+ if (has_started)
+ printf(", ");
+
+ printf("Decipher Only");
+ has_started = true;
+ }
+
+ printf("\n");
+ }
+
+ if (cert->subject_alt_name_present)
+ {
+ printf("Subject Alt Name:\t\t%s", cert->subject_alt_name_is_critical
+ ? "critical, " : "");
+ if (cert->subject_alt_dnsnames)
+ {
+ int i = 0;
+
+ while (cert->subject_alt_dnsnames[i])
+ printf("%s ", cert->subject_alt_dnsnames[i++]);
+ }
+ printf("\n");
+
+ }
+
+ printf("=== CERTIFICATE ISSUED BY ===\n");
+ printf("Common Name (CN):\t\t");
+ printf("%s\n", cert->ca_cert_dn[X509_COMMON_NAME] ?
+ cert->ca_cert_dn[X509_COMMON_NAME] : not_part_of_cert);
+
+ printf("Organization (O):\t\t");
+ printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATION] ?
+ cert->ca_cert_dn[X509_ORGANIZATION] : not_part_of_cert);
+
+ if (cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT])
+ {
+ printf("Organizational Unit (OU):\t");
+ printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT]);
+ }
+
+ if (cert->ca_cert_dn[X509_LOCATION])
+ {
+ printf("Location (L):\t\t\t");
+ printf("%s\n", cert->ca_cert_dn[X509_LOCATION]);
+ }
+
+ if (cert->ca_cert_dn[X509_COUNTRY])
+ {
+ printf("Country (C):\t\t\t");
+ printf("%s\n", cert->ca_cert_dn[X509_COUNTRY]);
+ }
+
+ if (cert->ca_cert_dn[X509_STATE])
+ {
+ printf("State (ST):\t\t\t");
+ printf("%s\n", cert->ca_cert_dn[X509_STATE]);
+ }
+
+ printf("Not Before:\t\t\t%s", ctime(&cert->not_before));
+ printf("Not After:\t\t\t%s", ctime(&cert->not_after));
+ printf("RSA bitsize:\t\t\t%d\n", cert->rsa_ctx->num_octets*8);
+ printf("Sig Type:\t\t\t");
+ switch (cert->sig_type)
+ {
+ case SIG_TYPE_MD5:
+ printf("MD5\n");
+ break;
+ case SIG_TYPE_SHA1:
+ printf("SHA1\n");
+ break;
+ case SIG_TYPE_SHA256:
+ printf("SHA256\n");
+ break;
+ case SIG_TYPE_SHA384:
+ printf("SHA384\n");
+ break;
+ case SIG_TYPE_SHA512:
+ printf("SHA512\n");
+ break;
+ default:
+ printf("Unrecognized: %d\n", cert->sig_type);
+ break;
+ }
+
+ if (ca_cert_ctx)
+ {
+ int pathLenConstraint = 0;
+ printf("Verify:\t\t\t\t%s\n",
+ x509_display_error(x509_verify(ca_cert_ctx, cert,
+ &pathLenConstraint)));
+ }
+
+#if 0
+ print_blob("Signature", cert->signature, cert->sig_len);
+ bi_print("Modulus", cert->rsa_ctx->m);
+ bi_print("Pub Exp", cert->rsa_ctx->e);
+#endif
+
+ if (ca_cert_ctx)
+ {
+ x509_print(cert->next, ca_cert_ctx);
+ }
+
+ TTY_FLUSH();
+}
+#endif
+
+const char * x509_display_error(int error)
+{
+ switch (error)
+ {
+ case X509_OK:
+ return "Certificate verify successful";
+
+ case X509_NOT_OK:
+ return "X509 not ok";
+
+ case X509_VFY_ERROR_NO_TRUSTED_CERT:
+ return "No trusted cert is available";
+
+ case X509_VFY_ERROR_BAD_SIGNATURE:
+ return "Bad signature";
+
+ case X509_VFY_ERROR_NOT_YET_VALID:
+ return "Cert is not yet valid";
+
+ case X509_VFY_ERROR_EXPIRED:
+ return "Cert has expired";
+
+ case X509_VFY_ERROR_SELF_SIGNED:
+ return "Cert is self-signed";
+
+ case X509_VFY_ERROR_INVALID_CHAIN:
+ return "Chain is invalid (check order of certs)";
+
+ case X509_VFY_ERROR_UNSUPPORTED_DIGEST:
+ return "Unsupported digest";
+
+ case X509_INVALID_PRIV_KEY:
+ return "Invalid private key";
+
+ case X509_VFY_ERROR_BASIC_CONSTRAINT:
+ return "Basic constraint invalid";
+
+ default:
+ return "Unknown";
+ }
+}
+//#endif /* CONFIG_SSL_FULL_MODE */
+