Initial import.
authorThomas Pornin <pornin@bolet.org>
Wed, 2 Nov 2016 23:01:13 +0000 (19:01 -0400)
committerThomas Pornin <pornin@bolet.org>
Wed, 2 Nov 2016 23:01:13 +0000 (19:01 -0400)
292 files changed:
LICENSE.txt [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.txt [new file with mode: 0644]
T0/BlobWriter.cs [new file with mode: 0644]
T0/CPU.cs [new file with mode: 0644]
T0/CodeElement.cs [new file with mode: 0644]
T0/CodeElementJump.cs [new file with mode: 0644]
T0/CodeElementUInt.cs [new file with mode: 0644]
T0/CodeElementUIntExpr.cs [new file with mode: 0644]
T0/CodeElementUIntInt.cs [new file with mode: 0644]
T0/CodeElementUIntUInt.cs [new file with mode: 0644]
T0/ConstData.cs [new file with mode: 0644]
T0/Opcode.cs [new file with mode: 0644]
T0/OpcodeCall.cs [new file with mode: 0644]
T0/OpcodeConst.cs [new file with mode: 0644]
T0/OpcodeGetLocal.cs [new file with mode: 0644]
T0/OpcodeJump.cs [new file with mode: 0644]
T0/OpcodeJumpIf.cs [new file with mode: 0644]
T0/OpcodeJumpIfNot.cs [new file with mode: 0644]
T0/OpcodeJumpUncond.cs [new file with mode: 0644]
T0/OpcodePutLocal.cs [new file with mode: 0644]
T0/OpcodeRet.cs [new file with mode: 0644]
T0/SType.cs [new file with mode: 0644]
T0/T0Comp.cs [new file with mode: 0644]
T0/TPointerBase.cs [new file with mode: 0644]
T0/TPointerBlob.cs [new file with mode: 0644]
T0/TPointerExpr.cs [new file with mode: 0644]
T0/TPointerNull.cs [new file with mode: 0644]
T0/TPointerXT.cs [new file with mode: 0644]
T0/TValue.cs [new file with mode: 0644]
T0/Word.cs [new file with mode: 0644]
T0/WordBuilder.cs [new file with mode: 0644]
T0/WordData.cs [new file with mode: 0644]
T0/WordInterpreted.cs [new file with mode: 0644]
T0/WordNative.cs [new file with mode: 0644]
T0/kern.t0 [new file with mode: 0644]
T0Comp.exe [new file with mode: 0755]
build/.do_not_remove [new file with mode: 0644]
inc/bearssl.h [new file with mode: 0644]
inc/bearssl_block.h [new file with mode: 0644]
inc/bearssl_ec.h [new file with mode: 0644]
inc/bearssl_hash.h [new file with mode: 0644]
inc/bearssl_hmac.h [new file with mode: 0644]
inc/bearssl_pem.h [new file with mode: 0644]
inc/bearssl_prf.h [new file with mode: 0644]
inc/bearssl_rand.h [new file with mode: 0644]
inc/bearssl_rsa.h [new file with mode: 0644]
inc/bearssl_ssl.h [new file with mode: 0644]
inc/bearssl_x509.h [new file with mode: 0644]
mkT0.sh [new file with mode: 0755]
samples/README.txt [new file with mode: 0644]
samples/cert-ee-ec+rsa.pem [new file with mode: 0644]
samples/cert-ee-ec.pem [new file with mode: 0644]
samples/cert-ee-rsa.pem [new file with mode: 0644]
samples/cert-ica-ec.pem [new file with mode: 0644]
samples/cert-ica-rsa.pem [new file with mode: 0644]
samples/cert-root-ec.pem [new file with mode: 0644]
samples/cert-root-rsa.pem [new file with mode: 0644]
samples/chain-ec+rsa.h [new file with mode: 0644]
samples/chain-ec.h [new file with mode: 0644]
samples/chain-rsa.h [new file with mode: 0644]
samples/client_basic.c [new file with mode: 0644]
samples/custom_profile.c [new file with mode: 0644]
samples/key-ec.h [new file with mode: 0644]
samples/key-ee-ec.pem [new file with mode: 0644]
samples/key-ee-rsa.pem [new file with mode: 0644]
samples/key-ica-ec.pem [new file with mode: 0644]
samples/key-ica-rsa.pem [new file with mode: 0644]
samples/key-root-ec.pem [new file with mode: 0644]
samples/key-root-rsa.pem [new file with mode: 0644]
samples/key-rsa.h [new file with mode: 0644]
samples/server_basic.c [new file with mode: 0644]
src/codec/ccopy.c [new file with mode: 0644]
src/codec/dec16be.c [new file with mode: 0644]
src/codec/dec16le.c [new file with mode: 0644]
src/codec/dec32be.c [new file with mode: 0644]
src/codec/dec32le.c [new file with mode: 0644]
src/codec/dec64be.c [new file with mode: 0644]
src/codec/dec64le.c [new file with mode: 0644]
src/codec/enc16be.c [new file with mode: 0644]
src/codec/enc16le.c [new file with mode: 0644]
src/codec/enc32be.c [new file with mode: 0644]
src/codec/enc32le.c [new file with mode: 0644]
src/codec/enc64be.c [new file with mode: 0644]
src/codec/enc64le.c [new file with mode: 0644]
src/codec/pemdec.c [new file with mode: 0644]
src/codec/pemdec.t0 [new file with mode: 0644]
src/config.h [new file with mode: 0644]
src/ec/ec_prime_i31.c [new file with mode: 0644]
src/ec/ec_prime_i31_secp256r1.c [new file with mode: 0644]
src/ec/ec_prime_i31_secp384r1.c [new file with mode: 0644]
src/ec/ec_prime_i31_secp521r1.c [new file with mode: 0644]
src/ec/ec_secp256r1.c [new file with mode: 0644]
src/ec/ec_secp384r1.c [new file with mode: 0644]
src/ec/ec_secp521r1.c [new file with mode: 0644]
src/ec/ecdsa_atr.c [new file with mode: 0644]
src/ec/ecdsa_i31_bits.c [new file with mode: 0644]
src/ec/ecdsa_i31_sign_asn1.c [new file with mode: 0644]
src/ec/ecdsa_i31_sign_raw.c [new file with mode: 0644]
src/ec/ecdsa_i31_vrfy_asn1.c [new file with mode: 0644]
src/ec/ecdsa_i31_vrfy_raw.c [new file with mode: 0644]
src/ec/ecdsa_rta.c [new file with mode: 0644]
src/hash/dig_oid.c [new file with mode: 0644]
src/hash/dig_size.c [new file with mode: 0644]
src/hash/ghash_ctmul.c [new file with mode: 0644]
src/hash/ghash_ctmul32.c [new file with mode: 0644]
src/hash/ghash_ctmul64.c [new file with mode: 0644]
src/hash/md5.c [new file with mode: 0644]
src/hash/md5sha1.c [new file with mode: 0644]
src/hash/multihash.c [new file with mode: 0644]
src/hash/sha1.c [new file with mode: 0644]
src/hash/sha2big.c [new file with mode: 0644]
src/hash/sha2small.c [new file with mode: 0644]
src/inner.h [new file with mode: 0644]
src/int/i31_add.c [new file with mode: 0644]
src/int/i31_bitlen.c [new file with mode: 0644]
src/int/i31_decmod.c [new file with mode: 0644]
src/int/i31_decode.c [new file with mode: 0644]
src/int/i31_decred.c [new file with mode: 0644]
src/int/i31_encode.c [new file with mode: 0644]
src/int/i31_fmont.c [new file with mode: 0644]
src/int/i31_iszero.c [new file with mode: 0644]
src/int/i31_modpow.c [new file with mode: 0644]
src/int/i31_montmul.c [new file with mode: 0644]
src/int/i31_mulacc.c [new file with mode: 0644]
src/int/i31_muladd.c [new file with mode: 0644]
src/int/i31_ninv31.c [new file with mode: 0644]
src/int/i31_reduce.c [new file with mode: 0644]
src/int/i31_rshift.c [new file with mode: 0644]
src/int/i31_sub.c [new file with mode: 0644]
src/int/i31_tmont.c [new file with mode: 0644]
src/int/i32_add.c [new file with mode: 0644]
src/int/i32_bitlen.c [new file with mode: 0644]
src/int/i32_decmod.c [new file with mode: 0644]
src/int/i32_decode.c [new file with mode: 0644]
src/int/i32_decred.c [new file with mode: 0644]
src/int/i32_div32.c [new file with mode: 0644]
src/int/i32_encode.c [new file with mode: 0644]
src/int/i32_fmont.c [new file with mode: 0644]
src/int/i32_iszero.c [new file with mode: 0644]
src/int/i32_modpow.c [new file with mode: 0644]
src/int/i32_montmul.c [new file with mode: 0644]
src/int/i32_mulacc.c [new file with mode: 0644]
src/int/i32_muladd.c [new file with mode: 0644]
src/int/i32_ninv32.c [new file with mode: 0644]
src/int/i32_reduce.c [new file with mode: 0644]
src/int/i32_sub.c [new file with mode: 0644]
src/int/i32_tmont.c [new file with mode: 0644]
src/mac/hmac.c [new file with mode: 0644]
src/mac/hmac_ct.c [new file with mode: 0644]
src/rand/hmac_drbg.c [new file with mode: 0644]
src/rsa/rsa_i31_pkcs1_sign.c [new file with mode: 0644]
src/rsa/rsa_i31_pkcs1_vrfy.c [new file with mode: 0644]
src/rsa/rsa_i31_priv.c [new file with mode: 0644]
src/rsa/rsa_i31_pub.c [new file with mode: 0644]
src/rsa/rsa_i32_pkcs1_sign.c [new file with mode: 0644]
src/rsa/rsa_i32_pkcs1_vrfy.c [new file with mode: 0644]
src/rsa/rsa_i32_priv.c [new file with mode: 0644]
src/rsa/rsa_i32_pub.c [new file with mode: 0644]
src/rsa/rsa_ssl_decrypt.c [new file with mode: 0644]
src/ssl/prf.c [new file with mode: 0644]
src/ssl/prf_md5sha1.c [new file with mode: 0644]
src/ssl/prf_sha256.c [new file with mode: 0644]
src/ssl/prf_sha384.c [new file with mode: 0644]
src/ssl/ssl_client.c [new file with mode: 0644]
src/ssl/ssl_client_full.c [new file with mode: 0644]
src/ssl/ssl_engine.c [new file with mode: 0644]
src/ssl/ssl_hashes.c [new file with mode: 0644]
src/ssl/ssl_hs_client.c [new file with mode: 0644]
src/ssl/ssl_hs_client.t0 [new file with mode: 0644]
src/ssl/ssl_hs_common.t0 [new file with mode: 0644]
src/ssl/ssl_hs_server.c [new file with mode: 0644]
src/ssl/ssl_hs_server.t0 [new file with mode: 0644]
src/ssl/ssl_io.c [new file with mode: 0644]
src/ssl/ssl_lru.c [new file with mode: 0644]
src/ssl/ssl_rec_cbc.c [new file with mode: 0644]
src/ssl/ssl_rec_gcm.c [new file with mode: 0644]
src/ssl/ssl_server.c [new file with mode: 0644]
src/ssl/ssl_server_full_ec.c [new file with mode: 0644]
src/ssl/ssl_server_full_rsa.c [new file with mode: 0644]
src/ssl/ssl_server_mine2g.c [new file with mode: 0644]
src/ssl/ssl_server_minf2g.c [new file with mode: 0644]
src/ssl/ssl_server_minr2g.c [new file with mode: 0644]
src/ssl/ssl_server_minu2g.c [new file with mode: 0644]
src/ssl/ssl_server_minv2g.c [new file with mode: 0644]
src/ssl/ssl_single_ec.c [new file with mode: 0644]
src/ssl/ssl_single_rsa.c [new file with mode: 0644]
src/symcipher/aes_big_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_big_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_big_ctr.c [new file with mode: 0644]
src/symcipher/aes_big_dec.c [new file with mode: 0644]
src/symcipher/aes_big_enc.c [new file with mode: 0644]
src/symcipher/aes_common.c [new file with mode: 0644]
src/symcipher/aes_ct.c [new file with mode: 0644]
src/symcipher/aes_ct64.c [new file with mode: 0644]
src/symcipher/aes_ct64_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_ct64_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_ct64_ctr.c [new file with mode: 0644]
src/symcipher/aes_ct64_dec.c [new file with mode: 0644]
src/symcipher/aes_ct64_enc.c [new file with mode: 0644]
src/symcipher/aes_ct_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_ct_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_ct_ctr.c [new file with mode: 0644]
src/symcipher/aes_ct_dec.c [new file with mode: 0644]
src/symcipher/aes_ct_enc.c [new file with mode: 0644]
src/symcipher/aes_small_cbcdec.c [new file with mode: 0644]
src/symcipher/aes_small_cbcenc.c [new file with mode: 0644]
src/symcipher/aes_small_ctr.c [new file with mode: 0644]
src/symcipher/aes_small_dec.c [new file with mode: 0644]
src/symcipher/aes_small_enc.c [new file with mode: 0644]
src/symcipher/des_ct.c [new file with mode: 0644]
src/symcipher/des_ct_cbcdec.c [new file with mode: 0644]
src/symcipher/des_ct_cbcenc.c [new file with mode: 0644]
src/symcipher/des_support.c [new file with mode: 0644]
src/symcipher/des_tab.c [new file with mode: 0644]
src/symcipher/des_tab_cbcdec.c [new file with mode: 0644]
src/symcipher/des_tab_cbcenc.c [new file with mode: 0644]
src/x509/asn1.t0 [new file with mode: 0644]
src/x509/skey_decoder.c [new file with mode: 0644]
src/x509/skey_decoder.t0 [new file with mode: 0644]
src/x509/x509_decoder.c [new file with mode: 0644]
src/x509/x509_decoder.t0 [new file with mode: 0644]
src/x509/x509_knownkey.c [new file with mode: 0644]
src/x509/x509_minimal.c [new file with mode: 0644]
src/x509/x509_minimal.t0 [new file with mode: 0644]
test/test_crypto.c [new file with mode: 0644]
test/test_math.c [new file with mode: 0644]
test/test_speed.c [new file with mode: 0644]
test/test_x509.c [new file with mode: 0644]
test/x509/alltests.txt [new file with mode: 0644]
test/x509/dn-ee.der [new file with mode: 0644]
test/x509/dn-ica1.der [new file with mode: 0644]
test/x509/dn-ica2.der [new file with mode: 0644]
test/x509/dn-root.der [new file with mode: 0644]
test/x509/ee-badsig1.crt [new file with mode: 0644]
test/x509/ee-badsig2.crt [new file with mode: 0644]
test/x509/ee-dates.crt [new file with mode: 0644]
test/x509/ee-md5.crt [new file with mode: 0644]
test/x509/ee-names.crt [new file with mode: 0644]
test/x509/ee-names2.crt [new file with mode: 0644]
test/x509/ee-names3.crt [new file with mode: 0644]
test/x509/ee-names4.crt [new file with mode: 0644]
test/x509/ee-p256-sha1.crt [new file with mode: 0644]
test/x509/ee-p256-sha224.crt [new file with mode: 0644]
test/x509/ee-p256-sha256.crt [new file with mode: 0644]
test/x509/ee-p256-sha384.crt [new file with mode: 0644]
test/x509/ee-p256-sha512.crt [new file with mode: 0644]
test/x509/ee-p256.crt [new file with mode: 0644]
test/x509/ee-p384.crt [new file with mode: 0644]
test/x509/ee-p521.crt [new file with mode: 0644]
test/x509/ee-sha1.crt [new file with mode: 0644]
test/x509/ee-sha224.crt [new file with mode: 0644]
test/x509/ee-sha384.crt [new file with mode: 0644]
test/x509/ee-sha512.crt [new file with mode: 0644]
test/x509/ee-trailing.crt [new file with mode: 0644]
test/x509/ee.crt [new file with mode: 0644]
test/x509/ica1-1016.crt [new file with mode: 0644]
test/x509/ica1-1017.crt [new file with mode: 0644]
test/x509/ica1-4096.crt [new file with mode: 0644]
test/x509/ica1-p256.crt [new file with mode: 0644]
test/x509/ica1-p384.crt [new file with mode: 0644]
test/x509/ica1-p521.crt [new file with mode: 0644]
test/x509/ica1.crt [new file with mode: 0644]
test/x509/ica2-1016.crt [new file with mode: 0644]
test/x509/ica2-1017.crt [new file with mode: 0644]
test/x509/ica2-4096.crt [new file with mode: 0644]
test/x509/ica2-notCA.crt [new file with mode: 0644]
test/x509/ica2-p256.crt [new file with mode: 0644]
test/x509/ica2-p384.crt [new file with mode: 0644]
test/x509/ica2-p521.crt [new file with mode: 0644]
test/x509/ica2.crt [new file with mode: 0644]
test/x509/junk.crt [new file with mode: 0644]
test/x509/root-p256.crt [new file with mode: 0644]
test/x509/root-p384.crt [new file with mode: 0644]
test/x509/root-p521.crt [new file with mode: 0644]
test/x509/root.crt [new file with mode: 0644]
tools/brssl.c [new file with mode: 0644]
tools/brssl.h [new file with mode: 0644]
tools/certs.c [new file with mode: 0644]
tools/chain.c [new file with mode: 0644]
tools/client.c [new file with mode: 0644]
tools/errors.c [new file with mode: 0644]
tools/files.c [new file with mode: 0644]
tools/keys.c [new file with mode: 0644]
tools/names.c [new file with mode: 0644]
tools/server.c [new file with mode: 0644]
tools/skey.c [new file with mode: 0644]
tools/sslio.c [new file with mode: 0644]
tools/ta.c [new file with mode: 0644]
tools/vector.c [new file with mode: 0644]
tools/verify.c [new file with mode: 0644]
tools/xmem.c [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..0885020
--- /dev/null
@@ -0,0 +1,21 @@
+Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+
+Permission is hereby granted, free of charge, to any person obtaining 
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be 
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..5eabd31
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,606 @@
+# Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining 
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be 
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+.POSIX:
+
+# ========================================================================
+# Configurable elements: C compiler and flags, linker flags, static
+# library archival command.
+
+CC = gcc
+CFLAGS = -W -Wall -Os -fPIC -I src -I inc
+#CFLAGS = -W -Wall -g -fPIC -I src -I inc
+LDFLAGS = 
+AR = ar -rcs
+
+# Nothing is meant to be changed below this line.
+
+# ========================================================================
+
+HEADERS = inc/bearssl.h inc/bearssl_block.h inc/bearssl_ec.h inc/bearssl_hash.h inc/bearssl_hmac.h inc/bearssl_pem.h inc/bearssl_prf.h inc/bearssl_rand.h inc/bearssl_rsa.h inc/bearssl_ssl.h inc/bearssl_x509.h src/inner.h src/config.h
+BUILD = build
+
+BEARSSLLIB = libbearssl.a
+BRSSL = brssl
+TESTCRYPTO = testcrypto
+TESTSPEED = testspeed
+TESTX509 = testx509
+TESTMATH = testmath
+
+OBJCODEC = $(BUILD)/ccopy.o $(BUILD)/dec16be.o $(BUILD)/dec16le.o $(BUILD)/dec32be.o $(BUILD)/dec32le.o $(BUILD)/dec64be.o $(BUILD)/dec64le.o $(BUILD)/enc16be.o $(BUILD)/enc16le.o $(BUILD)/enc32be.o $(BUILD)/enc32le.o $(BUILD)/enc64be.o $(BUILD)/enc64le.o $(BUILD)/pemdec.o
+OBJEC = $(BUILD)/ec_prime_i31.o $(BUILD)/ec_prime_i31_secp256r1.o $(BUILD)/ec_prime_i31_secp384r1.o $(BUILD)/ec_prime_i31_secp521r1.o $(BUILD)/ec_secp256r1.o $(BUILD)/ec_secp384r1.o $(BUILD)/ec_secp521r1.o $(BUILD)/ecdsa_atr.o $(BUILD)/ecdsa_i31_bits.o $(BUILD)/ecdsa_i31_sign_asn1.o $(BUILD)/ecdsa_i31_sign_raw.o $(BUILD)/ecdsa_i31_vrfy_asn1.o $(BUILD)/ecdsa_i31_vrfy_raw.o $(BUILD)/ecdsa_rta.o
+OBJHASH = $(BUILD)/dig_oid.o $(BUILD)/dig_size.o $(BUILD)/ghash_ctmul.o $(BUILD)/ghash_ctmul32.o $(BUILD)/ghash_ctmul64.o $(BUILD)/md5.o $(BUILD)/md5sha1.o $(BUILD)/multihash.o $(BUILD)/sha1.o $(BUILD)/sha2big.o $(BUILD)/sha2small.o
+OBJINT31 = $(BUILD)/i31_add.o $(BUILD)/i31_bitlen.o $(BUILD)/i31_decmod.o $(BUILD)/i31_decode.o $(BUILD)/i31_decred.o $(BUILD)/i31_encode.o $(BUILD)/i31_fmont.o $(BUILD)/i31_iszero.o $(BUILD)/i31_modpow.o $(BUILD)/i31_montmul.o $(BUILD)/i31_mulacc.o $(BUILD)/i31_muladd.o $(BUILD)/i31_ninv31.o $(BUILD)/i31_reduce.o $(BUILD)/i31_rshift.o $(BUILD)/i31_sub.o $(BUILD)/i31_tmont.o
+OBJINT32 = $(BUILD)/i32_add.o $(BUILD)/i32_bitlen.o $(BUILD)/i32_decmod.o $(BUILD)/i32_decode.o $(BUILD)/i32_decred.o $(BUILD)/i32_div32.o $(BUILD)/i32_encode.o $(BUILD)/i32_fmont.o $(BUILD)/i32_iszero.o $(BUILD)/i32_modpow.o $(BUILD)/i32_montmul.o $(BUILD)/i32_mulacc.o $(BUILD)/i32_muladd.o $(BUILD)/i32_ninv32.o $(BUILD)/i32_reduce.o $(BUILD)/i32_sub.o $(BUILD)/i32_tmont.o
+OBJMAC = $(BUILD)/hmac.o $(BUILD)/hmac_ct.o
+OBJRAND = $(BUILD)/hmac_drbg.o
+OBJRSA = $(BUILD)/rsa_i31_pkcs1_sign.o $(BUILD)/rsa_i31_pkcs1_vrfy.o $(BUILD)/rsa_i31_priv.o $(BUILD)/rsa_i31_pub.o $(BUILD)/rsa_i32_pkcs1_sign.o $(BUILD)/rsa_i32_pkcs1_vrfy.o $(BUILD)/rsa_i32_priv.o $(BUILD)/rsa_i32_pub.o $(BUILD)/rsa_ssl_decrypt.o
+OBJSSL = $(BUILD)/prf.o $(BUILD)/prf_md5sha1.o $(BUILD)/prf_sha256.o $(BUILD)/prf_sha384.o $(BUILD)/ssl_client.o $(BUILD)/ssl_client_full.o $(BUILD)/ssl_engine.o $(BUILD)/ssl_hashes.o $(BUILD)/ssl_hs_client.o $(BUILD)/ssl_hs_server.o $(BUILD)/ssl_io.o $(BUILD)/ssl_lru.o $(BUILD)/ssl_rec_cbc.o $(BUILD)/ssl_rec_gcm.o $(BUILD)/ssl_server.o $(BUILD)/ssl_server_mine2g.o $(BUILD)/ssl_server_minf2g.o $(BUILD)/ssl_server_minr2g.o $(BUILD)/ssl_server_minu2g.o $(BUILD)/ssl_server_minv2g.o $(BUILD)/ssl_server_full_ec.o $(BUILD)/ssl_server_full_rsa.o $(BUILD)/ssl_single_ec.o $(BUILD)/ssl_single_rsa.o
+OBJSYMCIPHER = $(BUILD)/aes_big_cbcdec.o $(BUILD)/aes_big_cbcenc.o $(BUILD)/aes_big_ctr.o $(BUILD)/aes_big_dec.o $(BUILD)/aes_big_enc.o $(BUILD)/aes_common.o $(BUILD)/aes_ct.o $(BUILD)/aes_ct64.o $(BUILD)/aes_ct64_cbcdec.o $(BUILD)/aes_ct64_cbcenc.o $(BUILD)/aes_ct64_ctr.o $(BUILD)/aes_ct64_dec.o $(BUILD)/aes_ct64_enc.o $(BUILD)/aes_ct_cbcdec.o $(BUILD)/aes_ct_cbcenc.o $(BUILD)/aes_ct_ctr.o $(BUILD)/aes_ct_dec.o $(BUILD)/aes_ct_enc.o $(BUILD)/aes_small_cbcdec.o $(BUILD)/aes_small_cbcenc.o $(BUILD)/aes_small_ctr.o $(BUILD)/aes_small_dec.o $(BUILD)/aes_small_enc.o $(BUILD)/des_ct.o $(BUILD)/des_ct_cbcdec.o $(BUILD)/des_ct_cbcenc.o $(BUILD)/des_support.o $(BUILD)/des_tab.o $(BUILD)/des_tab_cbcdec.o $(BUILD)/des_tab_cbcenc.o
+OBJX509 = $(BUILD)/skey_decoder.o $(BUILD)/x509_decoder.o $(BUILD)/x509_knownkey.o $(BUILD)/x509_minimal.o
+OBJ = $(OBJCODEC) $(OBJEC) $(OBJHASH) $(OBJINT31) $(OBJINT32) $(OBJMAC) $(OBJRAND) $(OBJRSA) $(OBJSSL) $(OBJSYMCIPHER) $(OBJX509)
+OBJBRSSL = $(BUILD)/brssl.o $(BUILD)/certs.o $(BUILD)/chain.o $(BUILD)/client.o $(BUILD)/errors.o $(BUILD)/files.o $(BUILD)/keys.o $(BUILD)/names.o $(BUILD)/server.o $(BUILD)/skey.o $(BUILD)/sslio.o $(BUILD)/ta.o $(BUILD)/vector.o $(BUILD)/verify.o $(BUILD)/xmem.o
+OBJTESTCRYPTO = $(BUILD)/test_crypto.o
+OBJTESTSPEED = $(BUILD)/test_speed.o
+OBJTESTX509 = $(BUILD)/test_x509.o
+OBJTESTMATH = $(BUILD)/test_math.o
+
+T0COMP = T0Comp.exe
+T0SRC = T0/BlobWriter.cs T0/CPU.cs T0/CodeElement.cs T0/CodeElementJump.cs T0/CodeElementUInt.cs T0/CodeElementUIntExpr.cs T0/CodeElementUIntInt.cs T0/CodeElementUIntUInt.cs T0/ConstData.cs T0/Opcode.cs T0/OpcodeCall.cs T0/OpcodeConst.cs T0/OpcodeGetLocal.cs T0/OpcodeJump.cs T0/OpcodeJumpIf.cs T0/OpcodeJumpIfNot.cs T0/OpcodeJumpUncond.cs T0/OpcodePutLocal.cs T0/OpcodeRet.cs T0/SType.cs T0/T0Comp.cs T0/TPointerBase.cs T0/TPointerBlob.cs T0/TPointerExpr.cs T0/TPointerNull.cs T0/TPointerXT.cs T0/TValue.cs T0/Word.cs T0/WordBuilder.cs T0/WordData.cs T0/WordInterpreted.cs T0/WordNative.cs
+T0KERN = T0/kern.t0
+
+all: compile
+
+compile: $(BEARSSLLIB) $(BRSSL) $(TESTCRYPTO) $(TESTSPEED) $(TESTX509)
+
+$(BEARSSLLIB): $(BUILD) $(OBJ)
+       $(AR) $(BEARSSLLIB) $(OBJ)
+
+$(BRSSL): $(BEARSSLLIB) $(OBJBRSSL)
+       $(CC) $(LDFLAGS) -o $(BRSSL) $(OBJBRSSL) $(BEARSSLLIB)
+
+$(TESTCRYPTO): $(BEARSSLLIB) $(OBJTESTCRYPTO)
+       $(CC) $(LDFLAGS) -o $(TESTCRYPTO) $(OBJTESTCRYPTO) $(BEARSSLLIB)
+
+$(TESTSPEED): $(BEARSSLLIB) $(OBJTESTSPEED)
+       $(CC) $(LDFLAGS) -o $(TESTSPEED) $(OBJTESTSPEED) $(BEARSSLLIB)
+
+$(TESTX509): $(BEARSSLLIB) $(OBJTESTX509)
+       $(CC) $(LDFLAGS) -o $(TESTX509) $(OBJTESTX509) $(BEARSSLLIB)
+
+$(TESTMATH): $(BEARSSLLIB) $(OBJTESTMATH)
+       $(CC) $(LDFLAGS) -o $(TESTMATH) $(OBJTESTMATH) $(BEARSSLLIB) -lgmp
+
+$(BUILD):
+       -mkdir -p $(BUILD)
+
+T0: $(T0COMP) T0Gen
+
+T0Gen:
+       mono T0Comp.exe -o src/codec/pemdec -r br_pem_decoder src/codec/pemdec.t0
+       mono T0Comp.exe -o src/ssl/ssl_hs_client -r br_ssl_hs_client src/ssl/ssl_hs_common.t0 src/ssl/ssl_hs_client.t0
+       mono T0Comp.exe -o src/ssl/ssl_hs_server -r br_ssl_hs_server src/ssl/ssl_hs_common.t0 src/ssl/ssl_hs_server.t0
+       mono T0Comp.exe -o src/x509/skey_decoder -r br_skey_decoder src/x509/asn1.t0 src/x509/skey_decoder.t0
+       mono T0Comp.exe -o src/x509/x509_decoder -r br_x509_decoder src/x509/asn1.t0 src/x509/x509_decoder.t0
+       mono T0Comp.exe -o src/x509/x509_minimal -r br_x509_minimal src/x509/asn1.t0 src/x509/x509_minimal.t0
+
+$(T0COMP): $(T0SRC) $(T0KERN)
+       ./mkT0.sh
+
+clean:
+       -rm -f $(OBJ) $(BEARSSLLIB) $(OBJSSL) $(BRSSL) $(OBJBRSSL) $(TESTCRYPTO) $(OBJTESTCRYPTO) $(TESTSPEED) $(OBJTESTSPEED) $(TESTX509) $(OBJTESTX509) $(TESTMATH) $(OBJTESTMATH)
+
+$(BUILD)/ccopy.o: src/codec/ccopy.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ccopy.o src/codec/ccopy.c
+
+$(BUILD)/dec16be.o: src/codec/dec16be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec16be.o src/codec/dec16be.c
+
+$(BUILD)/dec16le.o: src/codec/dec16le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec16le.o src/codec/dec16le.c
+
+$(BUILD)/dec32be.o: src/codec/dec32be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec32be.o src/codec/dec32be.c
+
+$(BUILD)/dec32le.o: src/codec/dec32le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec32le.o src/codec/dec32le.c
+
+$(BUILD)/dec64be.o: src/codec/dec64be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec64be.o src/codec/dec64be.c
+
+$(BUILD)/dec64le.o: src/codec/dec64le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dec64le.o src/codec/dec64le.c
+
+$(BUILD)/enc16be.o: src/codec/enc16be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc16be.o src/codec/enc16be.c
+
+$(BUILD)/enc16le.o: src/codec/enc16le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc16le.o src/codec/enc16le.c
+
+$(BUILD)/enc32be.o: src/codec/enc32be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc32be.o src/codec/enc32be.c
+
+$(BUILD)/enc32le.o: src/codec/enc32le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc32le.o src/codec/enc32le.c
+
+$(BUILD)/enc64be.o: src/codec/enc64be.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc64be.o src/codec/enc64be.c
+
+$(BUILD)/enc64le.o: src/codec/enc64le.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/enc64le.o src/codec/enc64le.c
+
+$(BUILD)/pemdec.o: src/codec/pemdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/pemdec.o src/codec/pemdec.c
+
+$(BUILD)/ec_g_secp256r1.o: src/ec/ec_g_secp256r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_g_secp256r1.o src/ec/ec_g_secp256r1.c
+
+$(BUILD)/ec_g_secp384r1.o: src/ec/ec_g_secp384r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_g_secp384r1.o src/ec/ec_g_secp384r1.c
+
+$(BUILD)/ec_g_secp521r1.o: src/ec/ec_g_secp521r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_g_secp521r1.o src/ec/ec_g_secp521r1.c
+
+
+$(BUILD)/ec_prime_i31.o: src/ec/ec_prime_i31.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31.o src/ec/ec_prime_i31.c
+
+$(BUILD)/ec_prime_i31_secp256r1.o: src/ec/ec_prime_i31_secp256r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31_secp256r1.o src/ec/ec_prime_i31_secp256r1.c
+
+$(BUILD)/ec_prime_i31_secp384r1.o: src/ec/ec_prime_i31_secp384r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31_secp384r1.o src/ec/ec_prime_i31_secp384r1.c
+
+$(BUILD)/ec_prime_i31_secp521r1.o: src/ec/ec_prime_i31_secp521r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_prime_i31_secp521r1.o src/ec/ec_prime_i31_secp521r1.c
+
+$(BUILD)/ec_secp256r1.o: src/ec/ec_secp256r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_secp256r1.o src/ec/ec_secp256r1.c
+
+$(BUILD)/ec_secp384r1.o: src/ec/ec_secp384r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_secp384r1.o src/ec/ec_secp384r1.c
+
+$(BUILD)/ec_secp521r1.o: src/ec/ec_secp521r1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ec_secp521r1.o src/ec/ec_secp521r1.c
+
+$(BUILD)/ecdsa_atr.o: src/ec/ecdsa_atr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_atr.o src/ec/ecdsa_atr.c
+
+$(BUILD)/ecdsa_i31_bits.o: src/ec/ecdsa_i31_bits.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_bits.o src/ec/ecdsa_i31_bits.c
+
+$(BUILD)/ecdsa_i31_sign_asn1.o: src/ec/ecdsa_i31_sign_asn1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_sign_asn1.o src/ec/ecdsa_i31_sign_asn1.c
+
+$(BUILD)/ecdsa_i31_sign_raw.o: src/ec/ecdsa_i31_sign_raw.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_sign_raw.o src/ec/ecdsa_i31_sign_raw.c
+
+$(BUILD)/ecdsa_i31_vrfy_asn1.o: src/ec/ecdsa_i31_vrfy_asn1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_vrfy_asn1.o src/ec/ecdsa_i31_vrfy_asn1.c
+
+$(BUILD)/ecdsa_i31_vrfy_raw.o: src/ec/ecdsa_i31_vrfy_raw.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_i31_vrfy_raw.o src/ec/ecdsa_i31_vrfy_raw.c
+
+$(BUILD)/ecdsa_rta.o: src/ec/ecdsa_rta.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ecdsa_rta.o src/ec/ecdsa_rta.c
+
+$(BUILD)/dig_oid.o: src/hash/dig_oid.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dig_oid.o src/hash/dig_oid.c
+
+$(BUILD)/dig_size.o: src/hash/dig_size.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/dig_size.o src/hash/dig_size.c
+
+$(BUILD)/ghash_ctmul.o: src/hash/ghash_ctmul.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ghash_ctmul.o src/hash/ghash_ctmul.c
+
+$(BUILD)/ghash_ctmul32.o: src/hash/ghash_ctmul32.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ghash_ctmul32.o src/hash/ghash_ctmul32.c
+
+$(BUILD)/ghash_ctmul64.o: src/hash/ghash_ctmul64.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ghash_ctmul64.o src/hash/ghash_ctmul64.c
+
+$(BUILD)/md5.o: src/hash/md5.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/md5.o src/hash/md5.c
+
+$(BUILD)/md5sha1.o: src/hash/md5sha1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/md5sha1.o src/hash/md5sha1.c
+
+$(BUILD)/multihash.o: src/hash/multihash.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/multihash.o src/hash/multihash.c
+
+$(BUILD)/sha1.o: src/hash/sha1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sha1.o src/hash/sha1.c
+
+$(BUILD)/sha2big.o: src/hash/sha2big.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sha2big.o src/hash/sha2big.c
+
+$(BUILD)/sha2small.o: src/hash/sha2small.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sha2small.o src/hash/sha2small.c
+
+$(BUILD)/i31_add.o: src/int/i31_add.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_add.o src/int/i31_add.c
+
+$(BUILD)/i31_bitlen.o: src/int/i31_bitlen.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_bitlen.o src/int/i31_bitlen.c
+
+$(BUILD)/i31_decmod.o: src/int/i31_decmod.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_decmod.o src/int/i31_decmod.c
+
+$(BUILD)/i31_decode.o: src/int/i31_decode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_decode.o src/int/i31_decode.c
+
+$(BUILD)/i31_decred.o: src/int/i31_decred.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_decred.o src/int/i31_decred.c
+
+$(BUILD)/i31_encode.o: src/int/i31_encode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_encode.o src/int/i31_encode.c
+
+$(BUILD)/i31_fmont.o: src/int/i31_fmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_fmont.o src/int/i31_fmont.c
+
+$(BUILD)/i31_iszero.o: src/int/i31_iszero.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_iszero.o src/int/i31_iszero.c
+
+$(BUILD)/i31_modpow.o: src/int/i31_modpow.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_modpow.o src/int/i31_modpow.c
+
+$(BUILD)/i31_montmul.o: src/int/i31_montmul.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_montmul.o src/int/i31_montmul.c
+
+$(BUILD)/i31_mulacc.o: src/int/i31_mulacc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_mulacc.o src/int/i31_mulacc.c
+
+$(BUILD)/i31_muladd.o: src/int/i31_muladd.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_muladd.o src/int/i31_muladd.c
+
+$(BUILD)/i31_ninv31.o: src/int/i31_ninv31.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_ninv31.o src/int/i31_ninv31.c
+
+$(BUILD)/i31_reduce.o: src/int/i31_reduce.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_reduce.o src/int/i31_reduce.c
+
+$(BUILD)/i31_rshift.o: src/int/i31_rshift.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_rshift.o src/int/i31_rshift.c
+
+$(BUILD)/i31_sub.o: src/int/i31_sub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_sub.o src/int/i31_sub.c
+
+$(BUILD)/i31_tmont.o: src/int/i31_tmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i31_tmont.o src/int/i31_tmont.c
+
+$(BUILD)/i32_add.o: src/int/i32_add.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_add.o src/int/i32_add.c
+
+$(BUILD)/i32_bitlen.o: src/int/i32_bitlen.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_bitlen.o src/int/i32_bitlen.c
+
+$(BUILD)/i32_decmod.o: src/int/i32_decmod.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_decmod.o src/int/i32_decmod.c
+
+$(BUILD)/i32_decode.o: src/int/i32_decode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_decode.o src/int/i32_decode.c
+
+$(BUILD)/i32_decred.o: src/int/i32_decred.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_decred.o src/int/i32_decred.c
+
+$(BUILD)/i32_div32.o: src/int/i32_div32.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_div32.o src/int/i32_div32.c
+
+$(BUILD)/i32_encode.o: src/int/i32_encode.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_encode.o src/int/i32_encode.c
+
+$(BUILD)/i32_fmont.o: src/int/i32_fmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_fmont.o src/int/i32_fmont.c
+
+$(BUILD)/i32_iszero.o: src/int/i32_iszero.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_iszero.o src/int/i32_iszero.c
+
+$(BUILD)/i32_modpow.o: src/int/i32_modpow.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_modpow.o src/int/i32_modpow.c
+
+$(BUILD)/i32_montmul.o: src/int/i32_montmul.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_montmul.o src/int/i32_montmul.c
+
+$(BUILD)/i32_mulacc.o: src/int/i32_mulacc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_mulacc.o src/int/i32_mulacc.c
+
+$(BUILD)/i32_muladd.o: src/int/i32_muladd.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_muladd.o src/int/i32_muladd.c
+
+$(BUILD)/i32_ninv32.o: src/int/i32_ninv32.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_ninv32.o src/int/i32_ninv32.c
+
+$(BUILD)/i32_reduce.o: src/int/i32_reduce.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_reduce.o src/int/i32_reduce.c
+
+$(BUILD)/i32_sub.o: src/int/i32_sub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_sub.o src/int/i32_sub.c
+
+$(BUILD)/i32_tmont.o: src/int/i32_tmont.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/i32_tmont.o src/int/i32_tmont.c
+
+$(BUILD)/hmac.o: src/mac/hmac.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/hmac.o src/mac/hmac.c
+
+$(BUILD)/hmac_ct.o: src/mac/hmac_ct.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/hmac_ct.o src/mac/hmac_ct.c
+
+$(BUILD)/hmac_drbg.o: src/rand/hmac_drbg.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/hmac_drbg.o src/rand/hmac_drbg.c
+
+$(BUILD)/rsa_i31_pkcs1_sign.o: src/rsa/rsa_i31_pkcs1_sign.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_pkcs1_sign.o src/rsa/rsa_i31_pkcs1_sign.c
+
+$(BUILD)/rsa_i31_pkcs1_vrfy.o: src/rsa/rsa_i31_pkcs1_vrfy.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_pkcs1_vrfy.o src/rsa/rsa_i31_pkcs1_vrfy.c
+
+$(BUILD)/rsa_i31_priv.o: src/rsa/rsa_i31_priv.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_priv.o src/rsa/rsa_i31_priv.c
+
+$(BUILD)/rsa_i31_pub.o: src/rsa/rsa_i31_pub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i31_pub.o src/rsa/rsa_i31_pub.c
+
+$(BUILD)/rsa_i32_pkcs1_sign.o: src/rsa/rsa_i32_pkcs1_sign.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_pkcs1_sign.o src/rsa/rsa_i32_pkcs1_sign.c
+
+$(BUILD)/rsa_i32_pkcs1_vrfy.o: src/rsa/rsa_i32_pkcs1_vrfy.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_pkcs1_vrfy.o src/rsa/rsa_i32_pkcs1_vrfy.c
+
+$(BUILD)/rsa_i32_priv.o: src/rsa/rsa_i32_priv.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_priv.o src/rsa/rsa_i32_priv.c
+
+$(BUILD)/rsa_i32_pub.o: src/rsa/rsa_i32_pub.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_i32_pub.o src/rsa/rsa_i32_pub.c
+
+$(BUILD)/rsa_ssl_decrypt.o: src/rsa/rsa_ssl_decrypt.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/rsa_ssl_decrypt.o src/rsa/rsa_ssl_decrypt.c
+
+$(BUILD)/prf.o: src/ssl/prf.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf.o src/ssl/prf.c
+
+$(BUILD)/prf_md5sha1.o: src/ssl/prf_md5sha1.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf_md5sha1.o src/ssl/prf_md5sha1.c
+
+$(BUILD)/prf_sha256.o: src/ssl/prf_sha256.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf_sha256.o src/ssl/prf_sha256.c
+
+$(BUILD)/prf_sha384.o: src/ssl/prf_sha384.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/prf_sha384.o src/ssl/prf_sha384.c
+
+$(BUILD)/ssl_client.o: src/ssl/ssl_client.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_client.o src/ssl/ssl_client.c
+
+$(BUILD)/ssl_client_full.o: src/ssl/ssl_client_full.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_client_full.o src/ssl/ssl_client_full.c
+
+$(BUILD)/ssl_engine.o: src/ssl/ssl_engine.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_engine.o src/ssl/ssl_engine.c
+
+$(BUILD)/ssl_hashes.o: src/ssl/ssl_hashes.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_hashes.o src/ssl/ssl_hashes.c
+
+$(BUILD)/ssl_hs_client.o: src/ssl/ssl_hs_client.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_hs_client.o src/ssl/ssl_hs_client.c
+
+$(BUILD)/ssl_hs_server.o: src/ssl/ssl_hs_server.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_hs_server.o src/ssl/ssl_hs_server.c
+
+$(BUILD)/ssl_io.o: src/ssl/ssl_io.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_io.o src/ssl/ssl_io.c
+
+$(BUILD)/ssl_lru.o: src/ssl/ssl_lru.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_lru.o src/ssl/ssl_lru.c
+
+$(BUILD)/ssl_rec_cbc.o: src/ssl/ssl_rec_cbc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_rec_cbc.o src/ssl/ssl_rec_cbc.c
+
+$(BUILD)/ssl_rec_gcm.o: src/ssl/ssl_rec_gcm.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_rec_gcm.o src/ssl/ssl_rec_gcm.c
+
+$(BUILD)/ssl_server.o: src/ssl/ssl_server.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server.o src/ssl/ssl_server.c
+
+$(BUILD)/ssl_server_mine2g.o: src/ssl/ssl_server_mine2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_mine2g.o src/ssl/ssl_server_mine2g.c
+
+$(BUILD)/ssl_server_minf2g.o: src/ssl/ssl_server_minf2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minf2g.o src/ssl/ssl_server_minf2g.c
+
+$(BUILD)/ssl_server_minr2g.o: src/ssl/ssl_server_minr2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minr2g.o src/ssl/ssl_server_minr2g.c
+
+$(BUILD)/ssl_server_minu2g.o: src/ssl/ssl_server_minu2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minu2g.o src/ssl/ssl_server_minu2g.c
+
+$(BUILD)/ssl_server_minv2g.o: src/ssl/ssl_server_minv2g.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_minv2g.o src/ssl/ssl_server_minv2g.c
+
+$(BUILD)/ssl_server_full_ec.o: src/ssl/ssl_server_full_ec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_full_ec.o src/ssl/ssl_server_full_ec.c
+
+$(BUILD)/ssl_server_full_rsa.o: src/ssl/ssl_server_full_rsa.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_server_full_rsa.o src/ssl/ssl_server_full_rsa.c
+
+$(BUILD)/ssl_single_ec.o: src/ssl/ssl_single_ec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_single_ec.o src/ssl/ssl_single_ec.c
+
+$(BUILD)/ssl_single_rsa.o: src/ssl/ssl_single_rsa.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ssl_single_rsa.o src/ssl/ssl_single_rsa.c
+
+$(BUILD)/aes_big_cbcdec.o: src/symcipher/aes_big_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_cbcdec.o src/symcipher/aes_big_cbcdec.c
+
+$(BUILD)/aes_big_cbcenc.o: src/symcipher/aes_big_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_cbcenc.o src/symcipher/aes_big_cbcenc.c
+
+$(BUILD)/aes_big_ctr.o: src/symcipher/aes_big_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_ctr.o src/symcipher/aes_big_ctr.c
+
+$(BUILD)/aes_big_dec.o: src/symcipher/aes_big_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_dec.o src/symcipher/aes_big_dec.c
+
+$(BUILD)/aes_big_enc.o: src/symcipher/aes_big_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_big_enc.o src/symcipher/aes_big_enc.c
+
+$(BUILD)/aes_common.o: src/symcipher/aes_common.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_common.o src/symcipher/aes_common.c
+
+$(BUILD)/aes_ct.o: src/symcipher/aes_ct.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct.o src/symcipher/aes_ct.c
+
+$(BUILD)/aes_ct64.o: src/symcipher/aes_ct64.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64.o src/symcipher/aes_ct64.c
+
+$(BUILD)/aes_ct64_cbcdec.o: src/symcipher/aes_ct64_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_cbcdec.o src/symcipher/aes_ct64_cbcdec.c
+
+$(BUILD)/aes_ct64_cbcenc.o: src/symcipher/aes_ct64_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_cbcenc.o src/symcipher/aes_ct64_cbcenc.c
+
+$(BUILD)/aes_ct64_ctr.o: src/symcipher/aes_ct64_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_ctr.o src/symcipher/aes_ct64_ctr.c
+
+$(BUILD)/aes_ct64_dec.o: src/symcipher/aes_ct64_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_dec.o src/symcipher/aes_ct64_dec.c
+
+$(BUILD)/aes_ct64_enc.o: src/symcipher/aes_ct64_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct64_enc.o src/symcipher/aes_ct64_enc.c
+
+$(BUILD)/aes_ct_cbcdec.o: src/symcipher/aes_ct_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_cbcdec.o src/symcipher/aes_ct_cbcdec.c
+
+$(BUILD)/aes_ct_cbcenc.o: src/symcipher/aes_ct_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_cbcenc.o src/symcipher/aes_ct_cbcenc.c
+
+$(BUILD)/aes_ct_ctr.o: src/symcipher/aes_ct_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_ctr.o src/symcipher/aes_ct_ctr.c
+
+$(BUILD)/aes_ct_dec.o: src/symcipher/aes_ct_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_dec.o src/symcipher/aes_ct_dec.c
+
+$(BUILD)/aes_ct_enc.o: src/symcipher/aes_ct_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_ct_enc.o src/symcipher/aes_ct_enc.c
+
+$(BUILD)/aes_small_cbcdec.o: src/symcipher/aes_small_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_cbcdec.o src/symcipher/aes_small_cbcdec.c
+
+$(BUILD)/aes_small_cbcenc.o: src/symcipher/aes_small_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_cbcenc.o src/symcipher/aes_small_cbcenc.c
+
+$(BUILD)/aes_small_ctr.o: src/symcipher/aes_small_ctr.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_ctr.o src/symcipher/aes_small_ctr.c
+
+$(BUILD)/aes_small_dec.o: src/symcipher/aes_small_dec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_dec.o src/symcipher/aes_small_dec.c
+
+$(BUILD)/aes_small_enc.o: src/symcipher/aes_small_enc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/aes_small_enc.o src/symcipher/aes_small_enc.c
+
+$(BUILD)/des_ct.o: src/symcipher/des_ct.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_ct.o src/symcipher/des_ct.c
+
+$(BUILD)/des_ct_cbcdec.o: src/symcipher/des_ct_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_ct_cbcdec.o src/symcipher/des_ct_cbcdec.c
+
+$(BUILD)/des_ct_cbcenc.o: src/symcipher/des_ct_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_ct_cbcenc.o src/symcipher/des_ct_cbcenc.c
+
+$(BUILD)/des_support.o: src/symcipher/des_support.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_support.o src/symcipher/des_support.c
+
+$(BUILD)/des_tab.o: src/symcipher/des_tab.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_tab.o src/symcipher/des_tab.c
+
+$(BUILD)/des_tab_cbcdec.o: src/symcipher/des_tab_cbcdec.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_tab_cbcdec.o src/symcipher/des_tab_cbcdec.c
+
+$(BUILD)/des_tab_cbcenc.o: src/symcipher/des_tab_cbcenc.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/des_tab_cbcenc.o src/symcipher/des_tab_cbcenc.c
+
+$(BUILD)/skey_decoder.o: src/x509/skey_decoder.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/skey_decoder.o src/x509/skey_decoder.c
+
+$(BUILD)/x509_decoder.o: src/x509/x509_decoder.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/x509_decoder.o src/x509/x509_decoder.c
+
+$(BUILD)/x509_knownkey.o: src/x509/x509_knownkey.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/x509_knownkey.o src/x509/x509_knownkey.c
+
+$(BUILD)/x509_minimal.o: src/x509/x509_minimal.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/x509_minimal.o src/x509/x509_minimal.c
+
+$(BUILD)/test_crypto.o: test/test_crypto.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_crypto.o test/test_crypto.c
+
+$(BUILD)/test_math.o: test/test_math.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_math.o test/test_math.c
+
+$(BUILD)/test_speed.o: test/test_speed.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_speed.o test/test_speed.c
+
+$(BUILD)/test_x509.o: test/test_x509.c $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/test_x509.o test/test_x509.c
+
+$(BUILD)/brssl.o: tools/brssl.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/brssl.o tools/brssl.c
+
+$(BUILD)/certs.o: tools/certs.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/certs.o tools/certs.c
+
+$(BUILD)/chain.o: tools/chain.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/chain.o tools/chain.c
+
+$(BUILD)/client.o: tools/client.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/client.o tools/client.c
+
+$(BUILD)/errors.o: tools/errors.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/errors.o tools/errors.c
+
+$(BUILD)/files.o: tools/files.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/files.o tools/files.c
+
+$(BUILD)/keys.o: tools/keys.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/keys.o tools/keys.c
+
+$(BUILD)/names.o: tools/names.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/names.o tools/names.c
+
+$(BUILD)/server.o: tools/server.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/server.o tools/server.c
+
+$(BUILD)/skey.o: tools/skey.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/skey.o tools/skey.c
+
+$(BUILD)/sslio.o: tools/sslio.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/sslio.o tools/sslio.c
+
+$(BUILD)/ta.o: tools/ta.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/ta.o tools/ta.c
+
+$(BUILD)/vector.o: tools/vector.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/vector.o tools/vector.c
+
+$(BUILD)/verify.o: tools/verify.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/verify.o tools/verify.c
+
+$(BUILD)/xmem.o: tools/xmem.c tools/brssl.h $(HEADERS)
+       $(CC) $(CFLAGS) -c -o $(BUILD)/xmem.o tools/xmem.c
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..e72537d
--- /dev/null
@@ -0,0 +1,169 @@
+# Disclaimer
+
+BearSSL is for now considered alpha-level software. This means that it
+probably still has some bugs, possibly very serious ones (e.g. buffer
+overflows -- one of the perks of using C as programming language). It
+still lacks some functionalities. The API will probably change and may
+break both source and binary compatibility.
+
+In other words, you would be quite mad to use it for any production
+purpose. Right now, this is for learning, testing and possibly
+contributing.
+
+The usage license is explicited in the `LICENSE.txt` file. This is the
+"MIT license". It can be summarised in the following way:
+
+ - You can use and reuse the library as you wish, and modify it, and
+   integrate it in your own code, and distribute it as is or in any
+   modified form, and so on.
+
+ - The only obligation that the license terms put upon you is that you
+   acknowledge and make it clear that if anything breaks, it is not my
+   fault, and I am not liable for anything, regardless of the type and
+   amount of collateral damage. The license terms say that the copyright
+   notice "shall be included in all copies or substantial portions of
+   the Software": this is how the disclaimer is "made explicit".
+   Basically, I have put it in every source file, so just keep it there.
+
+# Installation
+
+As of version 0.1, BearSSL is a simple static library. Most of the
+process is rather manual and old-style, and there is no installer (this
+will be added in a later version, in particular when all the man pages
+for BearSSL functions are written).
+
+ 1. Have a look at the top of the `Makefile`. There you can configure the
+    command names and flags for invoking the C compiler, linker, and
+    static library archiver.
+
+ 2. There are a few configurable switches in `src/config.h`. These switches
+    relate to compile-time options, e.g. support of a system-provided
+    random source. On usual platforms (e.g. Linux or OS X), auto-detection
+    should work, but you can always override things with `config.h`.
+
+ 3. Type `make`. This should produce the static library (`libbearssl.a`),
+    the test executables (`testcrypto`, `testspeed` and `testx509`), and
+    the command-line debug tool (`brssl`). You might want to run the tests:
+
+     - `testcrypto all` runs the cryptographic tests (test vectors on all
+       implemented cryptogaphic functions). It can be slow.
+
+     - `testspeed all` runs a number of performance benchmarks, there again
+       on cryptographic functions. It gives a taste of how things go on the
+       current platform.
+
+     - `testx509` runs X.509 validation tests. The test certificates are
+       all in `test/x509/`.
+
+ 4. The `brssl` command-line tool is a stand-alone binary. It can exercise
+    some of the functionalities of BearSSL, in particular running a test
+    SSL client or server. It is not meant for production purposes (e.g.
+    the SSL client has a mode where it disregards the inability to validate
+    the server's certificate, which is inherently unsafe, but convenient
+    for debug).
+
+ 5. Using the library means writing some application code that invokes it,
+    and linking with the static library. The header files are all in the
+    `inc` directory; copy them wherever makes sense (e.g. in the
+    `/usr/local/include` directory). The library itself (`libbearssl.a`)
+    is what you link against.
+
+    Alternatively, you may want to copy the source files directly into
+    your own application code. This will make integrating ulterior versions
+    of BearSSL more difficult. If you still want to go down that road,
+    then simply copy all the `*.h` and `*.c` files from the `src` and `inc`
+    directories into your application source code. In the BearSSL source
+    archive, the source files are segregated into various sub-directories,
+    but this is for my convenience only. There is no technical requirement
+    for that, and all files can be dumped together in a simple directory.
+
+    Dependencies are simple and systematic:
+
+     - Each `*.c` file includes `inner.h`
+     - `inner.h` includes `config.h` and `bearssl.h`
+     - `bearssl.h` includes the other `bearssl_*.h`
+
+# Versioning
+
+I follow this simple version numbering scheme:
+
+ - Version numbers are `x.y` or `x.y.z` where `x`, `y` ans `z` are
+   decimal integers (possibly greater than 10). When the `.z` part is
+   missing, it is equivalent to `.0`.
+
+ - Backward compatibility is maintained, at both source and binary levels,
+   for each major version: this means that if some application code was
+   designed for version `x.y`, then it should compile, link and run
+   properly with any version `x.y'` for any `y'` greater than `y`.
+
+   The major version `0` is an exception. You shall not expect that any
+   version that starts with `0.` offers any kind of compatibility,
+   either source or binary, with any other `0.` version. (Of course I
+   will try to maintain some decent level of source compatibility, but I
+   make no promise in that respect. Since the API uses caller-allocated
+   context structures, I already know that binary compatibility _will_
+   be broken.)
+
+ - Sub-versions (the `y` part) are about added functionality. That is,
+   it can be expected that `1.3` will contain some extra functions when
+   compared to `1.2`. The next version level (the `z` part) is for
+   bugfixes that do not add any functionality.
+
+# API Usage
+
+Right now there is little documentation. The following principles are
+maintained:
+
+ - All public symbols (global functions and data elements, macros) have
+   a name that starts with `br_` or `BR_`.
+
+ - The header files (the `bearssl_*.h` in the `inc` directory) contain
+   for now the most complete documentation (as comments).
+
+ - Context structures are allocated by the caller. BearSSL does not
+   contain any single `malloc()` call; this means that there is no
+   "freeing up" call to be done. When you don't need some BearSSL
+   functionality, just cease to call it, and that's it.
+
+ - BearSSL contains no modifiable static data. It is thus thread-safe
+   and reentrant, _for distinct contexts_. Accessing the same context
+   structure from distinct threads, though, is a recipe for disaster.
+
+ - The main SSL I/O API is organised as a state machine. A running
+   SSL engine (client or server) has four I/O ports:
+
+    - It can receive bytes from the transport medium ("record data").
+    - It can send bytes to the transport medium.
+    - It can receive application data, to be sent to the peer through
+      the SSL tunnel.
+    - It can produce application data, built from the records sent by
+      the peer.
+
+   BearSSL never performs I/O by itself; it expects the caller to
+   provide or retrieve the data. Each port consists in a pair of
+   functions: one yields the pointer to the buffer from which data
+   can be read or to which data can be written, and the maximum
+   size for such an operation; the other function is used to
+   inform the engine about how many bytes were actually read or
+   written.
+
+   For instance, if the `br_ssl_engine_sendrec_buf()` function returns a
+   non-NULL pointer, then this means that there are bytes to be sent to
+   the transport medium. When the caller has indeed sent some or all of
+   these bytes, it informs the engine with
+   `br_ssl_engine_sendrec_ack()`.
+
+   This state-machine API means that the engine never blocks. Each
+   invocation may trigger computations, but will always return as
+   promptly as the CPU power allows. All the I/O waiting is supposed to
+   be done on the outside. This structure allows managing several
+   concurrent SSL engines, along with other I/O tasks, with a single
+   mono-threaded loop using `select()` or `poll()`. It also makes it
+   easier to integrate BearSSL with various transport mechanisms (e.g.
+   messages in the EAP-TLS authentication framework).
+
+ - Nevertheless, there are situations where simple blocking calls _can_
+   be used, and are convenient. For these situations, use the
+   `br_sslio_context` wrapper. Then do blocking reads and writes with
+   `br_sslio_read()` and similar functions. The sample client code
+   in `samples/client_basic.c` shows how such things are done.
diff --git a/T0/BlobWriter.cs b/T0/BlobWriter.cs
new file mode 100644 (file)
index 0000000..3bbc8e6
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.IO;
+using System.Text;
+
+/*
+ * A simple class for writing out bytes as hexadecimal constants or
+ * explicit expressions for the initializer of a C array of 'unsigned
+ * char'. It starts every line with a given number of tabs, and aims at
+ * keeping lines below a given threshold (each indentation tab counts as
+ * 8 characters). An explicit newline is inserted before the first
+ * element, and commas are used as separators.
+ */
+
+class BlobWriter {
+
+       TextWriter w;
+       int maxLineLen;
+       int indent;
+       int lineLen;
+
+       /*
+        * Create a new instance. 'maxLineLen' is in characters, and
+        * 'indent' is the number of tab characters at the start of
+        * each line.
+        */
+       internal BlobWriter(TextWriter w, int maxLineLen, int indent)
+       {
+               this.w = w;
+               this.maxLineLen = maxLineLen;
+               this.indent = indent;
+               lineLen = -1;
+       }
+
+       void DoNL()
+       {
+               w.WriteLine();
+               for (int i = 0; i < indent; i ++) {
+                       w.Write('\t');
+               }
+               lineLen = (indent << 3);
+       }
+
+       /*
+        * Append a new byte value; it will be converted to an hexadecimal
+        * constant in the output.
+        */
+       internal void Append(byte b)
+       {
+               if (lineLen < 0) {
+                       DoNL();
+               } else {
+                       w.Write(',');
+                       lineLen ++;
+                       if ((lineLen + 5) > maxLineLen) {
+                               DoNL();
+                       } else {
+                               w.Write(' ');
+                               lineLen ++;
+                       }
+               }
+               w.Write("0x{0:X2}", b);
+               lineLen += 4;
+       }
+
+       /*
+        * Append a C expression, which will be used as is. The expression
+        * may resolve to several bytes if it uses internal commas. The
+        * writer will try to honour the expected line length, but it
+        * won't insert a newline character inside the expression.
+        */
+       internal void Append(string expr)
+       {
+               if (lineLen < 0) {
+                       DoNL();
+               } else {
+                       w.Write(',');
+                       lineLen ++;
+                       if ((lineLen + 1 + expr.Length) > maxLineLen) {
+                               DoNL();
+                       } else {
+                               w.Write(' ');
+                               lineLen ++;
+                       }
+               }
+               w.Write("{0}", expr);
+               lineLen += expr.Length;
+       }
+}
diff --git a/T0/CPU.cs b/T0/CPU.cs
new file mode 100644 (file)
index 0000000..22f1a17
--- /dev/null
+++ b/T0/CPU.cs
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * Execution of code during compilation is done in a virtual CPU
+ * incarnated by this class, that contains the relevant registers.
+ *
+ * Accesses to the data on the stack are mapped to accesses to an
+ * internal array, with no explicit control on boundaries. Since the
+ * internal array may be larger than the actual stack contents,
+ * nonsensical accesses may still "work" to some extent. The whole
+ * thing won't derail beyond the CLR VM, though.
+ */
+
+class CPU {
+
+       /*
+        * Next instruction to execute is in ipBuf[ipOff].
+        */
+       internal Opcode[] ipBuf;
+       internal int ipOff;
+
+       /*
+        * stackBuf and stackPtr implement the data stack. The system
+        * stack uses frames; 'rsp' points to the current top frame.
+        */
+       TValue[] stackBuf;
+       int stackPtr;
+       Frame rsp;
+
+       internal CPU()
+       {
+               stackBuf = new TValue[16];
+               stackPtr = -1;
+               rsp = null;
+       }
+
+       /*
+        * Enter a function, reserving space for 'numLocals' local variables.
+        */
+       internal void Enter(Opcode[] code, int numLocals)
+       {
+               Frame f = new Frame(rsp, numLocals);
+               rsp = f;
+               f.savedIpBuf = ipBuf;
+               f.savedIpOff = ipOff;
+               ipBuf = code;
+               ipOff = 0;
+       }
+
+       /*
+        * Exit the current function.
+        */
+       internal void Exit()
+       {
+               ipBuf = rsp.savedIpBuf;
+               ipOff = rsp.savedIpOff;
+               rsp = rsp.upper;
+       }
+
+       /*
+        * Get the current stack depth (number of elements).
+        */
+       internal int Depth {
+               get {
+                       return stackPtr + 1;
+               }
+       }
+
+       /*
+        * Pop a value from the stack.
+        */
+       internal TValue Pop()
+       {
+               return stackBuf[stackPtr --];
+       }
+
+       /*
+        * Push a value on the stack.
+        */
+       internal void Push(TValue v)
+       {
+               int len = stackBuf.Length;
+               if (++ stackPtr == len) {
+                       TValue[] nbuf = new TValue[len << 1];
+                       Array.Copy(stackBuf, 0, nbuf, 0, len);
+                       stackBuf = nbuf;
+               }
+               stackBuf[stackPtr] = v;
+       }
+
+       /*
+        * Look at the value at depth 'depth' (0 is top of stack). The
+        * stack is unchanged.
+        */
+       internal TValue Peek(int depth)
+       {
+               return stackBuf[stackPtr - depth];
+       }
+
+       /*
+        * Rotate the stack at depth 'depth': the value at that depth
+        * is moved to the top of stack.
+        */
+       internal void Rot(int depth)
+       {
+               TValue v = stackBuf[stackPtr - depth];
+               Array.Copy(stackBuf, stackPtr - (depth - 1),
+                       stackBuf, stackPtr - depth, depth);
+               stackBuf[stackPtr] = v;
+       }
+
+       /*
+        * Inverse-rotate the stack at depth 'depth': the value at the
+        * top of stack is moved to that depth.
+        */
+       internal void NRot(int depth)
+       {
+               TValue v = stackBuf[stackPtr];
+               Array.Copy(stackBuf, stackPtr - depth,
+                       stackBuf, stackPtr - (depth - 1), depth);
+               stackBuf[stackPtr - depth] = v;
+       }
+
+       /*
+        * Get the current contents of the local variable 'num'.
+        */
+       internal TValue GetLocal(int num)
+       {
+               return rsp.locals[num];
+       }
+
+       /*
+        * Set the contents of the local variable 'num'.
+        */
+       internal void PutLocal(int num, TValue v)
+       {
+               rsp.locals[num] = v;
+       }
+
+       /*
+        * The system stack really is a linked list of Frame instances.
+        */
+       class Frame {
+
+               internal Frame upper;
+               internal Opcode[] savedIpBuf;
+               internal int savedIpOff;
+               internal TValue[] locals;
+
+               internal Frame(Frame upper, int numLocals)
+               {
+                       this.upper = upper;
+                       locals = new TValue[numLocals];
+               }
+       }
+}
diff --git a/T0/CodeElement.cs b/T0/CodeElement.cs
new file mode 100644 (file)
index 0000000..5731c5a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+abstract class CodeElement {
+
+       internal int Address { get; set; }
+
+       internal int LastLength { get; set; }
+
+       internal abstract int Length { get; }
+
+       internal CodeElement()
+       {
+               Address = -1;
+       }
+
+       internal virtual void SetJumpTarget(CodeElement target)
+       {
+               throw new Exception("Code element accepts no target");
+       }
+
+       internal abstract int Encode(BlobWriter bw);
+
+       internal static int Encode7EUnsigned(uint val, BlobWriter bw)
+       {
+               int len = 1;
+               for (uint w = val; w >= 0x80; w >>= 7) {
+                       len ++;
+               }
+               if (bw != null) {
+                       for (int k = (len - 1) * 7; k >= 0; k -= 7) {
+                               int x = (int)(val >> k) & 0x7F;
+                               if (k > 0) {
+                                       x |= 0x80;
+                               }
+                               bw.Append((byte)x);
+                       }
+               }
+               return len;
+       }
+
+       internal static int Encode7ESigned(int val, BlobWriter bw)
+       {
+               int len = 1;
+               if (val < 0) {
+                       for (int w = val; w < -0x40; w >>= 7) {
+                               len ++;
+                       }
+               } else {
+                       for (int w = val; w >= 0x40; w >>= 7) {
+                               len ++;
+                       }
+               }
+               if (bw != null) {
+                       for (int k = (len - 1) * 7; k >= 0; k -= 7) {
+                               int x = (int)(val >> k) & 0x7F;
+                               if (k > 0) {
+                                       x |= 0x80;
+                               }
+                               bw.Append((byte)x);
+                       }
+               }
+               return len;
+       }
+}
diff --git a/T0/CodeElementJump.cs b/T0/CodeElementJump.cs
new file mode 100644 (file)
index 0000000..5abd7eb
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementJump : CodeElement {
+
+       uint jumpType;
+       CodeElement target;
+
+       internal CodeElementJump(uint jumpType)
+       {
+               this.jumpType = jumpType;
+       }
+
+       internal override int Length {
+               get {
+                       int len = Encode7EUnsigned(jumpType, null);
+                       int joff = JumpOff;
+                       if (joff == Int32.MinValue) {
+                               len ++;
+                       } else {
+                               len += Encode7ESigned(joff, null);
+                       }
+                       return len;
+               }
+       }
+
+       internal override void SetJumpTarget(CodeElement target)
+       {
+               this.target = target;
+       }
+
+       int JumpOff {
+               get {
+                       if (target == null || Address < 0 || target.Address < 0)
+                       {
+                               return Int32.MinValue;
+                       } else {
+                               return target.Address - (Address + LastLength);
+                       }
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               if (bw == null) {
+                       return Length;
+               }
+               int len = Encode7EUnsigned(jumpType, bw);
+               int joff = JumpOff;
+               if (joff == Int32.MinValue) {
+                       throw new Exception("Unresolved addresses");
+               }
+               return len + Encode7ESigned(joff, bw);
+       }
+}
diff --git a/T0/CodeElementUInt.cs b/T0/CodeElementUInt.cs
new file mode 100644 (file)
index 0000000..e5f3607
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUInt : CodeElement {
+
+       uint val;
+
+       internal CodeElementUInt(uint val) : base()
+       {
+               this.val = val;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val, null);
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               return Encode7EUnsigned(val, bw);
+       }
+}
diff --git a/T0/CodeElementUIntExpr.cs b/T0/CodeElementUIntExpr.cs
new file mode 100644 (file)
index 0000000..d24ba58
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUIntExpr : CodeElement {
+
+       uint val;
+       TPointerExpr cx;
+       int off;
+
+       internal CodeElementUIntExpr(uint val,
+               TPointerExpr cx, int off) : base()
+       {
+               this.val = val;
+               this.cx = cx;
+               this.off = off;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val, null)
+                               + (cx.GetMaxBitLength(off) + 6) / 7;
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               int len1 = Encode7EUnsigned(val, bw);
+               int len2 = (cx.GetMaxBitLength(off) + 6) / 7;
+               bw.Append(String.Format("T0_INT{0}({1})",
+                       len2, cx.ToCExpr(off)));
+               return len1 + len2;
+       }
+}
diff --git a/T0/CodeElementUIntInt.cs b/T0/CodeElementUIntInt.cs
new file mode 100644 (file)
index 0000000..022ffb8
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUIntInt : CodeElement {
+
+       uint val1;
+       int val2;
+
+       internal CodeElementUIntInt(uint val1, int val2) : base()
+       {
+               this.val1 = val1;
+               this.val2 = val2;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val1, null)
+                               + Encode7ESigned(val2, null);
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               int len = Encode7EUnsigned(val1, bw);
+               len += Encode7ESigned(val2, bw);
+               return len;
+       }
+}
diff --git a/T0/CodeElementUIntUInt.cs b/T0/CodeElementUIntUInt.cs
new file mode 100644 (file)
index 0000000..3d4ee33
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class CodeElementUIntUInt : CodeElement {
+
+       uint val1, val2;
+
+       internal CodeElementUIntUInt(uint val1, uint val2) : base()
+       {
+               this.val1 = val1;
+               this.val2 = val2;
+       }
+
+       internal override int Length {
+               get {
+                       return Encode7EUnsigned(val1, null)
+                               + Encode7EUnsigned(val2, null);
+               }
+       }
+
+       internal override int Encode(BlobWriter bw)
+       {
+               int len = Encode7EUnsigned(val1, bw);
+               len += Encode7EUnsigned(val2, bw);
+               return len;
+       }
+}
diff --git a/T0/ConstData.cs b/T0/ConstData.cs
new file mode 100644 (file)
index 0000000..6a06b64
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+class ConstData {
+
+       internal long ID { get; private set; }
+       internal int Address { get; set; }
+       internal int Length {
+               get {
+                       return len;
+               }
+       }
+
+       byte[] buf;
+       int len;
+
+       internal ConstData(T0Comp ctx)
+       {
+               ID = ctx.NextBlobID();
+               buf = new byte[4];
+               len = 0;
+       }
+
+       void Expand(int elen)
+       {
+               int tlen = len + elen;
+               if (tlen > buf.Length) {
+                       int nlen = Math.Max(buf.Length << 1, tlen);
+                       byte[] nbuf = new byte[nlen];
+                       Array.Copy(buf, 0, nbuf, 0, len);
+                       buf = nbuf;
+               }
+       }
+
+       internal void Add8(byte b)
+       {
+               Expand(1);
+               buf[len ++] = b;
+       }
+
+       internal void Add16(int x)
+       {
+               Expand(2);
+               buf[len ++] = (byte)(x >> 8);
+               buf[len ++] = (byte)x;
+       }
+
+       internal void Add24(int x)
+       {
+               Expand(3);
+               buf[len ++] = (byte)(x >> 16);
+               buf[len ++] = (byte)(x >> 8);
+               buf[len ++] = (byte)x;
+       }
+
+       internal void Add32(int x)
+       {
+               Expand(4);
+               buf[len ++] = (byte)(x >> 24);
+               buf[len ++] = (byte)(x >> 16);
+               buf[len ++] = (byte)(x >> 8);
+               buf[len ++] = (byte)x;
+       }
+
+       internal void AddString(string s)
+       {
+               byte[] sd = Encoding.UTF8.GetBytes(s);
+               Expand(sd.Length + 1);
+               Array.Copy(sd, 0, buf, len, sd.Length);
+               buf[len + sd.Length] = 0;
+               len += sd.Length + 1;
+       }
+
+       void CheckIndex(int off, int dlen)
+       {
+               if (off < 0 || off > (len - dlen)) {
+                       throw new IndexOutOfRangeException();
+               }
+       }
+
+       internal void Set8(int off, byte v)
+       {
+               CheckIndex(off, 1);
+               buf[off] = v;
+       }
+
+       internal byte Read8(int off)
+       {
+               CheckIndex(off, 1);
+               return buf[off];
+       }
+
+       internal int Read16(int off)
+       {
+               CheckIndex(off, 2);
+               return (buf[off] << 8) | buf[off + 1];
+       }
+
+       internal int Read24(int off)
+       {
+               CheckIndex(off, 3);
+               return (buf[off] << 16) | (buf[off + 1] << 8) | buf[off + 2];
+       }
+
+       internal int Read32(int off)
+       {
+               CheckIndex(off, 4);
+               return (buf[off] << 24) | (buf[off + 1] << 16)
+                       | (buf[off + 2] << 8) | buf[off + 3];
+       }
+
+       internal string ToString(int off)
+       {
+               StringBuilder sb = new StringBuilder();
+               for (;;) {
+                       int x = DecodeUTF8(ref off);
+                       if (x == 0) {
+                               return sb.ToString();
+                       }
+                       if (x < 0x10000) {
+                               sb.Append((char)x);
+                       } else {
+                               x -= 0x10000;
+                               sb.Append((char)(0xD800 + (x >> 10)));
+                               sb.Append((char)(0xDC00 + (x & 0x3FF)));
+                       }
+               }
+       }
+
+       int DecodeUTF8(ref int off)
+       {
+               if (off >= len) {
+                       throw new IndexOutOfRangeException();
+               }
+               int x = buf[off ++];
+               if (x < 0xC0 || x > 0xF7) {
+                       return x;
+               }
+               int elen, acc;
+               if (x >= 0xF0) {
+                       elen = 3;
+                       acc = x & 0x07;
+               } else if (x >= 0xE0) {
+                       elen = 2;
+                       acc = x & 0x0F;
+               } else {
+                       elen = 1;
+                       acc = x & 0x1F;
+               }
+               if (off + elen > len) {
+                       return x;
+               }
+               for (int i = 0; i < elen; i ++) {
+                       int y = buf[off + i];
+                       if (y < 0x80 || y >= 0xC0) {
+                               return x;
+                       }
+                       acc = (acc << 6) + (y & 0x3F);
+               }
+               if (acc > 0x10FFFF) {
+                       return x;
+               }
+               off += elen;
+               return acc;
+       }
+
+       internal void Encode(BlobWriter bw)
+       {
+               for (int i = 0; i < len; i ++) {
+                       bw.Append(buf[i]);
+               }
+       }
+}
diff --git a/T0/Opcode.cs b/T0/Opcode.cs
new file mode 100644 (file)
index 0000000..81d1e9d
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+abstract class Opcode {
+
+       internal Opcode()
+       {
+       }
+
+       /*
+        * Execute this opcode.
+        */
+       internal abstract void Run(CPU cpu);
+
+       /*
+        * Resolve the target (word reference) for this opcode.
+        */
+       internal virtual void ResolveTarget(Word target)
+       {
+               throw new Exception("Not a call opcode");
+       }
+
+       /*
+        * Resolve the jump offset for this opcode. Displacement is
+        * relative to the address of the opcode that immediately follows
+        * the jump code; thus, 0 implies no jump at all.
+        */
+       internal virtual void ResolveJump(int disp)
+       {
+               throw new Exception("Not a jump opcode");
+       }
+
+       /*
+        * Get the Word that this opcode references; this can happen
+        * only with "call" and "const" opcodes. For all other opcodes,
+        * this method returns null.
+        */
+       internal virtual Word GetReference(T0Comp ctx)
+       {
+               return null;
+       }
+
+       /*
+        * Get the data block that this opcode references; this can happen
+        * only with "const" opcodes. For all other opcodes, this method
+        * returns null.
+        */
+       internal virtual ConstData GetDataBlock(T0Comp ctx)
+       {
+               return null;
+       }
+
+       /*
+        * Test whether this opcode may "fall through", i.e. execution
+        * may at least potentially proceed to the next opcode.
+        */
+       internal virtual bool MayFallThrough {
+               get {
+                       return true;
+               }
+       }
+
+       /*
+        * Get jump displacement. For non-jump opcodes, this returns 0.
+        */
+       internal virtual int JumpDisp {
+               get {
+                       return 0;
+               }
+       }
+
+       /*
+        * Get stack effect for this opcode (number of elements added to
+        * the stack, could be negative). For OpcodeCall, this returns
+        * 0.
+        */
+       internal virtual int StackAction {
+               get {
+                       return 0;
+               }
+       }
+
+       internal abstract CodeElement ToCodeElement();
+
+       /*
+        * This method is called for the CodeElement corresponding to
+        * this opcode, at gcode[off]; it is used to compute actual
+        * byte jump offsets when converting code to C.
+        */
+       internal virtual void FixUp(CodeElement[] gcode, int off)
+       {
+       }
+}
diff --git a/T0/OpcodeCall.cs b/T0/OpcodeCall.cs
new file mode 100644 (file)
index 0000000..0980042
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeCall : Opcode {
+
+       Word target;
+
+       internal OpcodeCall() : this(null)
+       {
+       }
+
+       internal OpcodeCall(Word target)
+       {
+               this.target = target;
+       }
+
+       internal override void ResolveTarget(Word target)
+       {
+               if (this.target != null) {
+                       throw new Exception("Opcode already resolved");
+               }
+               this.target = target;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               target.Run(cpu);
+       }
+
+       internal override Word GetReference(T0Comp ctx)
+       {
+               if (target == null) {
+                       throw new Exception("Unresolved call target");
+               }
+               return target;
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUInt((uint)target.Slot);
+       }
+
+       public override string ToString()
+       {
+               return "call " + (target == null ? "UNRESOLVED" : target.Name);
+       }
+}
diff --git a/T0/OpcodeConst.cs b/T0/OpcodeConst.cs
new file mode 100644 (file)
index 0000000..ae75ae5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeConst : Opcode {
+
+       TValue val;
+
+       internal OpcodeConst(TValue val)
+       {
+               this.val = val;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.Push(val);
+       }
+
+       internal override Word GetReference(T0Comp ctx)
+       {
+               TPointerXT xt = val.ptr as TPointerXT;
+               if (xt == null) {
+                       return null;
+               }
+               xt.Resolve(ctx);
+               return xt.Target;
+       }
+
+       internal override ConstData GetDataBlock(T0Comp ctx)
+       {
+               TPointerBlob bp = val.ptr as TPointerBlob;
+               return bp == null ? null : bp.Blob;
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               if (val.ptr == null) {
+                       return new CodeElementUIntInt(1, val.Int);
+               }
+               TPointerXT xt = val.ptr as TPointerXT;
+               if (xt != null) {
+                       if (val.x != 0) {
+                               throw new Exception(
+                                       "Cannot compile XT: non-zero offset");
+                       }
+                       return new CodeElementUIntInt(1, xt.Target.Slot);
+               }
+               TPointerBlob bp = val.ptr as TPointerBlob;
+               if (bp != null) {
+                       return new CodeElementUIntInt(1,
+                               val.x + bp.Blob.Address);
+               }
+               TPointerExpr cx = val.ptr as TPointerExpr;
+               if (cx != null) {
+                       return new CodeElementUIntExpr(1, cx, val.x);
+               }
+               throw new Exception(String.Format(
+                       "Cannot embed constant (type = {0})",
+                       val.ptr.GetType().FullName));
+       }
+
+       internal override int StackAction {
+               get {
+                       return 1;
+               }
+       }
+
+       public override string ToString()
+       {
+               return "const " + val.ToString();
+       }
+}
diff --git a/T0/OpcodeGetLocal.cs b/T0/OpcodeGetLocal.cs
new file mode 100644 (file)
index 0000000..59d24fc
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeGetLocal : Opcode {
+
+       int num;
+
+       internal OpcodeGetLocal(int num)
+       {
+               this.num = num;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.Push(cpu.GetLocal(num));
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUIntUInt(2, (uint)num);
+       }
+
+       internal override int StackAction {
+               get {
+                       return 1;
+               }
+       }
+
+       public override string ToString()
+       {
+               return "getlocal " + num;
+       }
+}
diff --git a/T0/OpcodeJump.cs b/T0/OpcodeJump.cs
new file mode 100644 (file)
index 0000000..4f3ec68
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+abstract class OpcodeJump : Opcode {
+
+       int disp;
+
+       internal OpcodeJump() : this(Int32.MinValue)
+       {
+       }
+
+       internal OpcodeJump(int disp)
+       {
+               this.disp = disp;
+       }
+
+       internal override int JumpDisp {
+               get {
+                       return disp;
+               }
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.ipOff += disp;
+       }
+
+       internal override void ResolveJump(int disp)
+       {
+               if (this.disp != Int32.MinValue) {
+                       throw new Exception("Jump already resolved");
+               }
+               this.disp = disp;
+       }
+
+       internal override void FixUp(CodeElement[] gcode, int off)
+       {
+               gcode[off].SetJumpTarget(gcode[off + 1 + disp]);
+       }
+}
diff --git a/T0/OpcodeJumpIf.cs b/T0/OpcodeJumpIf.cs
new file mode 100644 (file)
index 0000000..d702434
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeJumpIf : OpcodeJump {
+
+       internal OpcodeJumpIf() : base()
+       {
+       }
+
+       internal OpcodeJumpIf(int disp) : base(disp)
+       {
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               TValue v = cpu.Pop();
+               if (v.Bool) {
+                       base.Run(cpu);
+               }
+       }
+
+       internal override int StackAction {
+               get {
+                       return -1;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementJump(5);
+       }
+
+       public override string ToString()
+       {
+               if (JumpDisp == Int32.MinValue) {
+                       return "jumpif UNRESOLVED";
+               } else {
+                       return "jumpif disp=" + JumpDisp;
+               }
+       }
+}
diff --git a/T0/OpcodeJumpIfNot.cs b/T0/OpcodeJumpIfNot.cs
new file mode 100644 (file)
index 0000000..afbf19d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeJumpIfNot : OpcodeJump {
+
+       internal OpcodeJumpIfNot() : base()
+       {
+       }
+
+       internal OpcodeJumpIfNot(int disp) : base(disp)
+       {
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               TValue v = cpu.Pop();
+               if (!v.Bool) {
+                       base.Run(cpu);
+               }
+       }
+
+       internal override int StackAction {
+               get {
+                       return -1;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementJump(6);
+       }
+
+       public override string ToString()
+       {
+               if (JumpDisp == Int32.MinValue) {
+                       return "jumpifnot UNRESOLVED";
+               } else {
+                       return "jumpifnot disp=" + JumpDisp;
+               }
+       }
+}
diff --git a/T0/OpcodeJumpUncond.cs b/T0/OpcodeJumpUncond.cs
new file mode 100644 (file)
index 0000000..e7d8a82
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeJumpUncond : OpcodeJump {
+
+       internal OpcodeJumpUncond() : base()
+       {
+       }
+
+       internal OpcodeJumpUncond(int disp) : base(disp)
+       {
+       }
+
+       /*
+        * Unconditional jumps do not "fall through" unless they
+        * happen to be a jump to the next instruction...
+        */
+       internal override bool MayFallThrough {
+               get {
+                       return JumpDisp == 0;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementJump(4);
+       }
+
+       public override string ToString()
+       {
+               if (JumpDisp == Int32.MinValue) {
+                       return "jump UNRESOLVED";
+               } else {
+                       return "jump disp=" + JumpDisp;
+               }
+       }
+}
diff --git a/T0/OpcodePutLocal.cs b/T0/OpcodePutLocal.cs
new file mode 100644 (file)
index 0000000..b148a65
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodePutLocal : Opcode {
+
+       int num;
+
+       internal OpcodePutLocal(int num)
+       {
+               this.num = num;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.PutLocal(num, cpu.Pop());
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUIntUInt(3, (uint)num);
+       }
+
+       internal override int StackAction {
+               get {
+                       return -1;
+               }
+       }
+
+       public override string ToString()
+       {
+               return "putlocal " + num;
+       }
+}
diff --git a/T0/OpcodeRet.cs b/T0/OpcodeRet.cs
new file mode 100644 (file)
index 0000000..187473b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class OpcodeRet : Opcode {
+
+       internal override void Run(CPU cpu)
+       {
+               cpu.Exit();
+       }
+
+       internal override bool MayFallThrough {
+               get {
+                       return false;
+               }
+       }
+
+       internal override CodeElement ToCodeElement()
+       {
+               return new CodeElementUInt(0);
+       }
+
+       public override string ToString()
+       {
+               return "ret";
+       }
+}
diff --git a/T0/SType.cs b/T0/SType.cs
new file mode 100644 (file)
index 0000000..80e9f01
--- /dev/null
@@ -0,0 +1,129 @@
+using System;
+
+/*
+ * This structure contains the stack effect of a word: number of stack
+ * element consumed on input, and number of stack element produced on
+ * output.
+ */
+
+struct SType {
+
+       /*
+        * Get number of stack elements consumed on input; this is -1 if
+        * the stack effect is not known.
+        */
+       internal int DataIn {
+               get {
+                       return din;
+               }
+       }
+
+       /*
+        * Get number of stack elements produced on output; this is -1 if
+        * either the stack effect is not known, or if the word never
+        * exits.
+        */
+       internal int DataOut {
+               get {
+                       return dout;
+               }
+       }
+
+       /*
+        * Tell whether the stack effect is known.
+        */
+       internal bool IsKnown {
+               get {
+                       return din >= 0;
+               }
+       }
+
+       /*
+        * Tell whether the stack effect is known and the word never exits.
+        */
+       internal bool NoExit {
+               get {
+                       return din >= 0 && dout < 0;
+               }
+       }
+
+       int din, dout;
+
+       internal SType(int din, int dout)
+       {
+               if (din < 0) {
+                       din = -1;
+               }
+               if (dout < 0) {
+                       dout = -1;
+               }
+               this.din = din;
+               this.dout = dout;
+       }
+
+       /*
+        * Special value for the unknown stack effect.
+        */
+       internal static SType UNKNOWN = new SType(-1, -1);
+
+       /*
+        * Constant for the "blank stack effect".
+        */
+       internal static SType BLANK = new SType(0, 0);
+
+       public static bool operator ==(SType s1, SType s2)
+       {
+               return s1.din == s2.din && s1.dout == s2.dout;
+       }
+
+       public static bool operator !=(SType s1, SType s2)
+       {
+               return s1.din != s2.din || s1.dout != s2.dout;
+       }
+
+       public override bool Equals(Object obj)
+       {
+               return (obj is SType) && ((SType)obj == this);
+       }
+
+       public override int GetHashCode()
+       {
+               return din * 31 + dout * 17;
+       }
+
+       public override string ToString()
+       {
+               if (!IsKnown) {
+                       return "UNKNOWN";
+               } else if (NoExit) {
+                       return string.Format("in:{0},noexit", din);
+               } else {
+                       return string.Format("in:{0},out:{1}", din, dout);
+               }
+       }
+
+       /*
+        * Test whether this stack effect is a sub-effect of the provided
+        * stack effect s. Stack effect s1 is a sub-effect of stack-effect
+        * s2 if any of the following holds:
+        * -- s1 and s2 are known, s1.din <= s2.din and s1 does not exit.
+        * -- s1 and s2 are known, s1.din <= s2.din, s1 and s2 exit,
+        *    and s1.din - s1.dout == s2.din - s2.dout.
+        */
+       internal bool IsSubOf(SType s)
+       {
+               if (!IsKnown || !s.IsKnown) {
+                       return false;
+               }
+               if (din > s.din) {
+                       return false;
+               }
+               if (NoExit) {
+                       return true;
+               }
+               if (s.NoExit) {
+                       return false;
+               }
+               return (din - dout) == (s.din - s.dout);
+       }
+}
diff --git a/T0/T0Comp.cs b/T0/T0Comp.cs
new file mode 100644 (file)
index 0000000..143badb
--- /dev/null
@@ -0,0 +1,2070 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+/*
+ * This is the main compiler class.
+ */
+
+public class T0Comp {
+
+       /*
+        * Command-line entry point.
+        */
+       public static void Main(string[] args)
+       {
+               try {
+                       List<string> r = new List<string>();
+                       string outBase = null;
+                       List<string> entryPoints = new List<string>();
+                       string coreRun = null;
+                       bool flow = true;
+                       int dsLim = 32;
+                       int rsLim = 32;
+                       for (int i = 0; i < args.Length; i ++) {
+                               string a = args[i];
+                               if (!a.StartsWith("-")) {
+                                       r.Add(a);
+                                       continue;
+                               }
+                               if (a == "--") {
+                                       for (;;) {
+                                               if (++ i >= args.Length) {
+                                                       break;
+                                               }
+                                               r.Add(args[i]);
+                                       }
+                                       break;
+                               }
+                               while (a.StartsWith("-")) {
+                                       a = a.Substring(1);
+                               }
+                               int j = a.IndexOf('=');
+                               string pname;
+                               string pval, pval2;
+                               if (j < 0) {
+                                       pname = a.ToLowerInvariant();
+                                       pval = null;
+                                       pval2 = (i + 1) < args.Length
+                                               ? args[i + 1] : null;
+                               } else {
+                                       pname = a.Substring(0, j).Trim()
+                                               .ToLowerInvariant();
+                                       pval = a.Substring(j + 1);
+                                       pval2 = null;
+                               }
+                               switch (pname) {
+                               case "o":
+                               case "out":
+                                       if (pval == null) {
+                                               if (pval2 == null) {
+                                                       Usage();
+                                               }
+                                               i ++;
+                                               pval = pval2;
+                                       }
+                                       if (outBase != null) {
+                                               Usage();
+                                       }
+                                       outBase = pval;
+                                       break;
+                               case "r":
+                               case "run":
+                                       if (pval == null) {
+                                               if (pval2 == null) {
+                                                       Usage();
+                                               }
+                                               i ++;
+                                               pval = pval2;
+                                       }
+                                       coreRun = pval;
+                                       break;
+                               case "m":
+                               case "main":
+                                       if (pval == null) {
+                                               if (pval2 == null) {
+                                                       Usage();
+                                               }
+                                               i ++;
+                                               pval = pval2;
+                                       }
+                                       foreach (string ep in pval.Split(',')) {
+                                               string epz = ep.Trim();
+                                               if (epz.Length > 0) {
+                                                       entryPoints.Add(epz);
+                                               }
+                                       }
+                                       break;
+                               case "nf":
+                               case "noflow":
+                                       flow = false;
+                                       break;
+                               default:
+                                       Usage();
+                                       break;
+                               }
+                       }
+                       if (r.Count == 0) {
+                               Usage();
+                       }
+                       if (outBase == null) {
+                               outBase = "t0out";
+                       }
+                       if (entryPoints.Count == 0) {
+                               entryPoints.Add("main");
+                       }
+                       if (coreRun == null) {
+                               coreRun = outBase;
+                       }
+                       T0Comp tc = new T0Comp();
+                       tc.enableFlowAnalysis = flow;
+                       tc.dsLimit = dsLim;
+                       tc.rsLimit = rsLim;
+                       using (TextReader tr = new StreamReader(
+                               Assembly.GetExecutingAssembly()
+                               .GetManifestResourceStream("t0-kernel")))
+                       {
+                               tc.ProcessInput(tr);
+                       }
+                       foreach (string a in r) {
+                               Console.WriteLine("[{0}]", a);
+                               using (TextReader tr = File.OpenText(a)) {
+                                       tc.ProcessInput(tr);
+                               }
+                       }
+                       tc.Generate(outBase, coreRun, entryPoints.ToArray());
+               } catch (Exception e) {
+                       Console.WriteLine(e.ToString());
+                       Environment.Exit(1);
+               }
+       }
+
+       static void Usage()
+       {
+               Console.WriteLine(
+"usage: T0Comp.exe [ options... ] file...");
+               Console.WriteLine(
+"options:");
+               Console.WriteLine(
+"   -o file    use 'file' as base for output file name (default: 't0out')");
+               Console.WriteLine(
+"   -r name    use 'name' as base for run function (default: same as output)");
+               Console.WriteLine(
+"   -m name[,name...]");
+               Console.WriteLine(
+"              define entry point(s)");
+               Console.WriteLine(
+"   -nf        disable flow analysis");
+               Environment.Exit(1);
+       }
+
+       /*
+        * If 'delayedChar' is Int32.MinValue then there is no delayed
+        * character.
+        * If 'delayedChar' equals x >= 0 then there is one delayed
+        * character of value x.
+        * If 'delayedChar' equals y < 0 then there are two delayed
+        * characters, a newline (U+000A) followed by character of
+        * value -(y+1).
+        */
+       TextReader currentInput;
+       int delayedChar;
+
+       /*
+        * Common StringBuilder used to parse tokens; it is reused for
+        * each new token.
+        */
+       StringBuilder tokenBuilder;
+
+       /*
+        * There may be a delayed token in some cases.
+        */
+       String delayedToken;
+
+       /*
+        * Defined words are referenced by name in this map. Names are
+        * string-sensitive; for better reproducibility, the map is sorted
+        * (ordinal order).
+        */
+       IDictionary<string, Word> words;
+
+       /*
+        * Last defined word is also referenced in 'lastWord'. This is
+        * used by 'immediate'.
+        */
+       Word lastWord;
+
+       /*
+        * When compiling, this builder is used. A stack saves other
+        * builders in case of nested definition.
+        */
+       WordBuilder wordBuilder;
+       Stack<WordBuilder> savedWordBuilders;
+
+       /*
+        * C code defined for words is kept in this map, by word name.
+        */
+       IDictionary<string, string> allCCode;
+
+       /*
+        * 'compiling' is true when compiling tokens to a word, false
+        * when interpreting them.
+        */
+       bool compiling;
+
+       /*
+        * 'quitRunLoop' is set to true to trigger exit of the
+        * interpretation loop when the end of the current input file
+        * is reached.
+        */
+       bool quitRunLoop;
+
+       /*
+        * 'extraCode' is for C code that is to be added as preamble to
+        * the C output.
+        */
+       List<string> extraCode;
+
+       /*
+        * 'dataBlock' is the data block in which constant data bytes
+        * are accumulated.
+        */
+       ConstData dataBlock;
+
+       /*
+        * Counter for blocks of constant data.
+        */
+       long currentBlobID;
+
+       /*
+        * Flow analysis enable flag.
+        */
+       bool enableFlowAnalysis;
+
+       /*
+        * Data stack size limit.
+        */
+       int dsLimit;
+
+       /*
+        * Return stack size limit.
+        */
+       int rsLimit;
+
+       T0Comp()
+       {
+               tokenBuilder = new StringBuilder();
+               words = new SortedDictionary<string, Word>(
+                       StringComparer.Ordinal);
+               savedWordBuilders = new Stack<WordBuilder>();
+               allCCode = new SortedDictionary<string, string>(
+                       StringComparer.Ordinal);
+               compiling = false;
+               extraCode = new List<string>();
+               enableFlowAnalysis = true;
+
+               /*
+                * Native words are predefined and implemented only with
+                * native code. Some may be part of the generated output,
+                * if C code is set for them.
+                */
+
+               /*
+                * add-cc:
+                * Parses next token as a word name, then a C code snippet.
+                * Sets the C code for that word.
+                */
+               AddNative("add-cc:", false, SType.BLANK, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       if (allCCode.ContainsKey(tt)) {
+                               throw new Exception(
+                                       "C code already set for: " + tt);
+                       }
+                       allCCode[tt] = ParseCCode();
+               });
+
+               /*
+                * cc:
+                * Parses next token as a word name, then a C code snippet.
+                * A new word is defined, that throws an exception when
+                * invoked during compilation. The C code is set for that
+                * new word.
+                */
+               AddNative("cc:", false, SType.BLANK, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       Word w = AddNative(tt, false, cpu2 => {
+                               throw new Exception(
+                                       "C-only word: " + tt);
+                       });
+                       if (allCCode.ContainsKey(tt)) {
+                               throw new Exception(
+                                       "C code already set for: " + tt);
+                       }
+                       SType stackEffect;
+                       allCCode[tt] = ParseCCode(out stackEffect);
+                       w.StackEffect = stackEffect;
+               });
+
+               /*
+                * preamble
+                * Parses a C code snippet, then adds it to the generated
+                * output preamble.
+                */
+               AddNative("preamble", false, SType.BLANK, cpu => {
+                       extraCode.Add(ParseCCode());
+               });
+
+               /*
+                * make-CX
+                * Expects two integers and a string, and makes a
+                * constant that stands for the string as a C constant
+                * expression. The two integers are the expected range
+                * (min-max, inclusive).
+                */
+               AddNative("make-CX", false, new SType(3, 1), cpu => {
+                       TValue c = cpu.Pop();
+                       if (!(c.ptr is TPointerBlob)) {
+                               throw new Exception(string.Format(
+                                       "'{0}' is not a string", c));
+                       }
+                       int max = cpu.Pop();
+                       int min = cpu.Pop();
+                       TValue tv = new TValue(0, new TPointerExpr(
+                               c.ToString(), min, max));
+                       cpu.Push(tv);
+               });
+
+               /*
+                * CX  (immediate)
+                * Parses two integer constants, then a C code snippet.
+                * It then pushes on the stack, or compiles to the
+                * current word, a value consisting of the given C
+                * expression; the two integers indicate the expected
+                * range (min-max, inclusive) of the C expression when
+                * evaluated.
+                */
+               AddNative("CX", true, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing min value)");
+                       }
+                       int min = ParseInteger(tt);
+                       tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing max value)");
+                       }
+                       int max = ParseInteger(tt);
+                       if (max < min) {
+                               throw new Exception("min/max in wrong order");
+                       }
+                       TValue tv = new TValue(0, new TPointerExpr(
+                               ParseCCode().Trim(), min, max));
+                       if (compiling) {
+                               wordBuilder.Literal(tv);
+                       } else {
+                               cpu.Push(tv);
+                       }
+               });
+
+               /*
+                * co
+                * Interrupt the current execution. This implements
+                * coroutines. It cannot be invoked during compilation.
+                */
+               AddNative("co", false, SType.BLANK, cpu => {
+                       throw new Exception("No coroutine in compile mode");
+               });
+
+               /*
+                * :
+                * Parses next token as word name. It begins definition
+                * of that word, setting it as current target for
+                * word building. Any previously opened word is saved
+                * and will become available again as a target when that
+                * new word is finished building.
+                */
+               AddNative(":", false, cpu => {
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       if (compiling) {
+                               savedWordBuilders.Push(wordBuilder);
+                       } else {
+                               compiling = true;
+                       }
+                       wordBuilder = new WordBuilder(this, tt);
+                       tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (while compiling)");
+                       }
+                       if (tt == "(") {
+                               SType stackEffect = ParseStackEffectNF();
+                               if (!stackEffect.IsKnown) {
+                                       throw new Exception(
+                                               "Invalid stack effect syntax");
+                               }
+                               wordBuilder.StackEffect = stackEffect;
+                       } else {
+                               delayedToken = tt;
+                       }
+               });
+
+               /*
+                * Pops a string as word name, and two integers as stack
+                * effect. It begins definition of that word, setting it
+                * as current target for word building. Any previously
+                * opened word is saved and will become available again as
+                * a target when that new word is finished building.
+                *
+                * Stack effect is the pair 'din dout'. If din is negative,
+                * then the stack effect is "unknown". If din is nonnegative
+                * but dout is negative, then the word is reputed never to
+                * return.
+                */
+               AddNative("define-word", false, cpu => {
+                       int dout = cpu.Pop();
+                       int din = cpu.Pop();
+                       TValue s = cpu.Pop();
+                       if (!(s.ptr is TPointerBlob)) {
+                               throw new Exception(string.Format(
+                                       "Not a string: '{0}'", s));
+                       }
+                       string tt = s.ToString();
+                       if (compiling) {
+                               savedWordBuilders.Push(wordBuilder);
+                       } else {
+                               compiling = true;
+                       }
+                       wordBuilder = new WordBuilder(this, tt);
+                       wordBuilder.StackEffect = new SType(din, dout);
+               });
+
+               /*
+                * ;  (immediate)
+                * Ends current word. The current word is registered under
+                * its name, and the previously opened word (if any) becomes
+                * again the building target.
+                */
+               AddNative(";", true, cpu => {
+                       if (!compiling) {
+                               throw new Exception("Not compiling");
+                       }
+                       Word w = wordBuilder.Build();
+                       string name = w.Name;
+                       if (words.ContainsKey(name)) {
+                               throw new Exception(
+                                       "Word already defined: " + name);
+                       }
+                       words[name] = w;
+                       lastWord = w;
+                       if (savedWordBuilders.Count > 0) {
+                               wordBuilder = savedWordBuilders.Pop();
+                       } else {
+                               wordBuilder = null;
+                               compiling = false;
+                       }
+               });
+
+               /*
+                * immediate
+                * Sets the last defined word as immediate.
+                */
+               AddNative("immediate", false, cpu => {
+                       if (lastWord == null) {
+                               throw new Exception("No word defined yet");
+                       }
+                       lastWord.Immediate = true;
+               });
+
+               /*
+                * literal  (immediate)
+                * Pops the current TOS value, and add in the current word
+                * the action of pushing that value. This cannot be used
+                * when no word is being built.
+                */
+               WordNative wliteral = AddNative("literal", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Literal(cpu.Pop());
+               });
+
+               /*
+                * compile
+                * Pops the current TOS value, which must be an XT (pointer
+                * to a word); the action of calling that word is compiled
+                * in the current word.
+                */
+               WordNative wcompile = AddNative("compile", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Call(cpu.Pop().ToXT());
+               });
+
+               /*
+                * postpone  (immediate)
+                * Parses the next token as a word name, and add to the
+                * current word the action of calling that word. This
+                * basically removes immediatety from the next word.
+                */
+               AddNative("postpone", true, cpu => {
+                       CheckCompiling();
+                       string tt = Next();
+                       if (tt == null) {
+                               throw new Exception(
+                                       "EOF reached (missing name)");
+                       }
+                       TValue v;
+                       bool isVal = TryParseLiteral(tt, out v);
+                       Word w = LookupNF(tt);
+                       if (isVal && w != null) {
+                               throw new Exception(String.Format(
+                                       "Ambiguous: both defined word and"
+                                       + " literal: {0}", tt));
+                       }
+                       if (isVal) {
+                               wordBuilder.Literal(v);
+                               wordBuilder.CallExt(wliteral);
+                       } else if (w != null) {
+                               if (w.Immediate) {
+                                       wordBuilder.CallExt(w);
+                               } else {
+                                       wordBuilder.Literal(new TValue(0,
+                                               new TPointerXT(w)));
+                                       wordBuilder.CallExt(wcompile);
+                               }
+                       } else {
+                               wordBuilder.Literal(new TValue(0,
+                                       new TPointerXT(tt)));
+                               wordBuilder.CallExt(wcompile);
+                       }
+               });
+
+               /*
+                * Interrupt compilation with an error.
+                */
+               AddNative("exitvm", false, cpu => {
+                       throw new Exception();
+               });
+
+               /*
+                * Open a new data block. Its symbolic address is pushed
+                * on the stack.
+                */
+               AddNative("new-data-block", false, cpu => {
+                       dataBlock = new ConstData(this);
+                       cpu.Push(new TValue(0, new TPointerBlob(dataBlock)));
+               });
+
+               /*
+                * Define a new data word. The data address and name are
+                * popped from the stack.
+                */
+               AddNative("define-data-word", false, cpu => {
+                       string name = cpu.Pop().ToString();
+                       TValue va = cpu.Pop();
+                       TPointerBlob tb = va.ptr as TPointerBlob;
+                       if (tb == null) {
+                               throw new Exception(
+                                       "Address is not a data area");
+                       }
+                       Word w = new WordData(this, name, tb.Blob, va.x);
+                       if (words.ContainsKey(name)) {
+                               throw new Exception(
+                                       "Word already defined: " + name);
+                       }
+                       words[name] = w;
+                       lastWord = w;
+               });
+
+               /*
+                * Get an address pointing at the end of the current
+                * data block. This is the address of the next byte that
+                * will be added.
+                */
+               AddNative("current-data", false, cpu => {
+                       if (dataBlock == null) {
+                               throw new Exception(
+                                       "No current data block");
+                       }
+                       cpu.Push(new TValue(dataBlock.Length,
+                               new TPointerBlob(dataBlock)));
+               });
+
+               /*
+                * Add a byte value to the data block.
+                */
+               AddNative("data-add8", false, cpu => {
+                       if (dataBlock == null) {
+                               throw new Exception(
+                                       "No current data block");
+                       }
+                       int v = cpu.Pop();
+                       if (v < 0 || v > 0xFF) {
+                               throw new Exception(
+                                       "Byte value out of range: " + v);
+                       }
+                       dataBlock.Add8((byte)v);
+               });
+
+               /*
+                * Set a byte value in the data block.
+                */
+               AddNative("data-set8", false, cpu => {
+                       TValue va = cpu.Pop();
+                       TPointerBlob tb = va.ptr as TPointerBlob;
+                       if (tb == null) {
+                               throw new Exception(
+                                       "Address is not a data area");
+                       }
+                       int v = cpu.Pop();
+                       if (v < 0 || v > 0xFF) {
+                               throw new Exception(
+                                       "Byte value out of range: " + v);
+                       }
+                       tb.Blob.Set8(va.x, (byte)v);
+               });
+
+               /*
+                * Get a byte value from a data block.
+                */
+               AddNative("data-get8", false, new SType(1, 1), cpu => {
+                       TValue va = cpu.Pop();
+                       TPointerBlob tb = va.ptr as TPointerBlob;
+                       if (tb == null) {
+                               throw new Exception(
+                                       "Address is not a data area");
+                       }
+                       int v = tb.Blob.Read8(va.x);
+                       cpu.Push(v);
+               });
+
+               /*
+                *
+                */
+               AddNative("compile-local-read", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.GetLocal(cpu.Pop().ToString());
+               });
+               AddNative("compile-local-write", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.PutLocal(cpu.Pop().ToString());
+               });
+
+               AddNative("ahead", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Ahead();
+               });
+               AddNative("begin", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Begin();
+               });
+               AddNative("again", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Again();
+               });
+               AddNative("until", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AgainIfNot();
+               });
+               AddNative("untilnot", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AgainIf();
+               });
+               AddNative("if", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AheadIfNot();
+               });
+               AddNative("ifnot", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.AheadIf();
+               });
+               AddNative("then", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Then();
+               });
+               AddNative("cs-pick", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.CSPick(cpu.Pop());
+               });
+               AddNative("cs-roll", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.CSRoll(cpu.Pop());
+               });
+               AddNative("next-word", false, cpu => {
+                       string s = Next();
+                       if (s == null) {
+                               throw new Exception("No next word (EOF)");
+                       }
+                       cpu.Push(StringToBlob(s));
+               });
+               AddNative("parse", false, cpu => {
+                       int d = cpu.Pop();
+                       string s = ReadTerm(d);
+                       cpu.Push(StringToBlob(s));
+               });
+               AddNative("char", false, cpu => {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception("No next character (EOF)");
+                       }
+                       cpu.Push(c);
+               });
+               AddNative("'", false, cpu => {
+                       string name = Next();
+                       cpu.Push(new TValue(0, new TPointerXT(name)));
+               });
+
+               /*
+                * The "execute" word is valid in generated C code, but
+                * since it jumps to a runtime pointer, its actual stack
+                * effect cannot be computed in advance.
+                */
+               AddNative("execute", false, cpu => {
+                       cpu.Pop().Execute(this, cpu);
+               });
+
+               AddNative("[", true, cpu => {
+                       CheckCompiling();
+                       compiling = false;
+               });
+               AddNative("]", false, cpu => {
+                       compiling = true;
+               });
+               AddNative("(local)", false, cpu => {
+                       CheckCompiling();
+                       wordBuilder.DefLocal(cpu.Pop().ToString());
+               });
+               AddNative("ret", true, cpu => {
+                       CheckCompiling();
+                       wordBuilder.Ret();
+               });
+
+               AddNative("drop", false, new SType(1, 0), cpu => {
+                       cpu.Pop();
+               });
+               AddNative("dup", false, new SType(1, 2), cpu => {
+                       cpu.Push(cpu.Peek(0));
+               });
+               AddNative("swap", false, new SType(2, 2), cpu => {
+                       cpu.Rot(1);
+               });
+               AddNative("over", false, new SType(2, 3), cpu => {
+                       cpu.Push(cpu.Peek(1));
+               });
+               AddNative("rot", false, new SType(3, 3), cpu => {
+                       cpu.Rot(2);
+               });
+               AddNative("-rot", false, new SType(3, 3), cpu => {
+                       cpu.NRot(2);
+               });
+
+               /*
+                * "roll" and "pick" are special in that the stack slot
+                * they inspect might be known only at runtime, so an
+                * absolute stack effect cannot be attributed. Instead,
+                * we simply hope that the caller knows what it is doing,
+                * and we use a simple stack effect for just the count
+                * value and picked value.
+                */
+               AddNative("roll", false, new SType(1, 0), cpu => {
+                       cpu.Rot(cpu.Pop());
+               });
+               AddNative("pick", false, new SType(1, 1), cpu => {
+                       cpu.Push(cpu.Peek(cpu.Pop()));
+               });
+
+               AddNative("+", false, new SType(2, 1), cpu => {
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       if (b.ptr == null) {
+                               a.x += (int)b;
+                               cpu.Push(a);
+                       } else if (a.ptr is TPointerBlob
+                               && b.ptr is TPointerBlob)
+                       {
+                               cpu.Push(StringToBlob(
+                                       a.ToString() + b.ToString()));
+                       } else {
+                               throw new Exception(string.Format(
+                                       "Cannot add '{0}' to '{1}'", b, a));
+                       }
+               });
+               AddNative("-", false, new SType(2, 1), cpu => {
+                       /*
+                        * We can subtract two pointers, provided that
+                        * they point to the same blob. Otherwise,
+                        * the subtraction second operand must be an
+                        * integer.
+                        */
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       TPointerBlob ap = a.ptr as TPointerBlob;
+                       TPointerBlob bp = b.ptr as TPointerBlob;
+                       if (ap != null && bp != null && ap.Blob == bp.Blob) {
+                               cpu.Push(new TValue(a.x - b.x));
+                               return;
+                       }
+                       int bx = b;
+                       a.x -= bx;
+                       cpu.Push(a);
+               });
+               AddNative("neg", false, new SType(1, 1), cpu => {
+                       int ax = cpu.Pop();
+                       cpu.Push(-ax);
+               });
+               AddNative("*", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax * bx);
+               });
+               AddNative("/", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax / bx);
+               });
+               AddNative("u/", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax / bx);
+               });
+               AddNative("%", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax % bx);
+               });
+               AddNative("u%", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax % bx);
+               });
+               AddNative("<", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax < bx);
+               });
+               AddNative("<=", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax <= bx);
+               });
+               AddNative(">", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax > bx);
+               });
+               AddNative(">=", false, new SType(2, 1), cpu => {
+                       int bx = cpu.Pop();
+                       int ax = cpu.Pop();
+                       cpu.Push(ax >= bx);
+               });
+               AddNative("=", false, new SType(2, 1), cpu => {
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       cpu.Push(a.Equals(b));
+               });
+               AddNative("<>", false, new SType(2, 1), cpu => {
+                       TValue b = cpu.Pop();
+                       TValue a = cpu.Pop();
+                       cpu.Push(!a.Equals(b));
+               });
+               AddNative("u<", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop().UInt;
+                       uint ax = cpu.Pop().UInt;
+                       cpu.Push(new TValue(ax < bx));
+               });
+               AddNative("u<=", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop().UInt;
+                       uint ax = cpu.Pop().UInt;
+                       cpu.Push(new TValue(ax <= bx));
+               });
+               AddNative("u>", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop().UInt;
+                       uint ax = cpu.Pop().UInt;
+                       cpu.Push(new TValue(ax > bx));
+               });
+               AddNative("u>=", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax >= bx);
+               });
+               AddNative("and", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax & bx);
+               });
+               AddNative("or", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax | bx);
+               });
+               AddNative("xor", false, new SType(2, 1), cpu => {
+                       uint bx = cpu.Pop();
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax ^ bx);
+               });
+               AddNative("not", false, new SType(1, 1), cpu => {
+                       uint ax = cpu.Pop();
+                       cpu.Push(~ax);
+               });
+               AddNative("<<", false, new SType(2, 1), cpu => {
+                       int count = cpu.Pop();
+                       if (count < 0 || count > 31) {
+                               throw new Exception("Invalid shift count");
+                       }
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax << count);
+               });
+               AddNative(">>", false, new SType(2, 1), cpu => {
+                       int count = cpu.Pop();
+                       if (count < 0 || count > 31) {
+                               throw new Exception("Invalid shift count");
+                       }
+                       int ax = cpu.Pop();
+                       cpu.Push(ax >> count);
+               });
+               AddNative("u>>", false, new SType(2, 1), cpu => {
+                       int count = cpu.Pop();
+                       if (count < 0 || count > 31) {
+                               throw new Exception("Invalid shift count");
+                       }
+                       uint ax = cpu.Pop();
+                       cpu.Push(ax >> count);
+               });
+
+               AddNative(".", false, new SType(1, 0), cpu => {
+                       Console.Write(" {0}", cpu.Pop().ToString());
+               });
+               AddNative(".s", false, SType.BLANK, cpu => {
+                       int n = cpu.Depth;
+                       for (int i = n - 1; i >= 0; i --) {
+                               Console.Write(" {0}", cpu.Peek(i).ToString());
+                       }
+               });
+               AddNative("putc", false, new SType(1, 0), cpu => {
+                       Console.Write((char)cpu.Pop());
+               });
+               AddNative("puts", false, new SType(1, 0), cpu => {
+                       Console.Write("{0}", cpu.Pop().ToString());
+               });
+               AddNative("cr", false, SType.BLANK, cpu => {
+                       Console.WriteLine();
+               });
+               AddNative("eqstr", false, new SType(2, 1), cpu => {
+                       string s2 = cpu.Pop().ToString();
+                       string s1 = cpu.Pop().ToString();
+                       cpu.Push(s1 == s2);
+               });
+       }
+
+       WordNative AddNative(string name, bool immediate,
+               WordNative.NativeRun code)
+       {
+               return AddNative(name, immediate, SType.UNKNOWN, code);
+       }
+
+       WordNative AddNative(string name, bool immediate, SType stackEffect,
+               WordNative.NativeRun code)
+       {
+               if (words.ContainsKey(name)) {
+                       throw new Exception(
+                               "Word already defined: " + name);
+               }
+               WordNative w = new WordNative(this, name, code);
+               w.Immediate = immediate;
+               w.StackEffect = stackEffect;
+               words[name] = w;
+               return w;
+       }
+
+       internal long NextBlobID()
+       {
+               return currentBlobID ++;
+       }
+
+       int NextChar()
+       {
+               int c = delayedChar;
+               if (c >= 0) {
+                       delayedChar = Int32.MinValue;
+               } else if (c > Int32.MinValue) {
+                       delayedChar = -(c + 1);
+                       c = '\n';
+               } else {
+                       c = currentInput.Read();
+               }
+               if (c == '\r') {
+                       if (delayedChar >= 0) {
+                               c = delayedChar;
+                               delayedChar = Int32.MinValue;
+                       } else {
+                               c = currentInput.Read();
+                       }
+                       if (c != '\n') {
+                               delayedChar = c;
+                               c = '\n';
+                       }
+               }
+               return c;
+       }
+
+       /*
+        * Un-read the character value 'c'. That value MUST be the one
+        * that was obtained from NextChar().
+        */
+       void Unread(int c)
+       {
+               if (c < 0) {
+                       return;
+               }
+               if (delayedChar < 0) {
+                       if (delayedChar != Int32.MinValue) {
+                               throw new Exception(
+                                       "Already two delayed characters");
+                       }
+                       delayedChar = c;
+               } else if (c != '\n') {
+                       throw new Exception("Cannot delay two characters");
+               } else {
+                       delayedChar = -(delayedChar + 1);
+               }
+       }
+
+       string Next()
+       {
+               string r = delayedToken;
+               if (r != null) {
+                       delayedToken = null;
+                       return r;
+               }
+               tokenBuilder.Length = 0;
+               int c;
+               for (;;) {
+                       c = NextChar();
+                       if (c < 0) {
+                               return null;
+                       }
+                       if (!IsWS(c)) {
+                               break;
+                       }
+               }
+               if (c == '"') {
+                       return ParseString();
+               }
+               for (;;) {
+                       tokenBuilder.Append((char)c);
+                       c = NextChar();
+                       if (c < 0 || IsWS(c)) {
+                               Unread(c);
+                               return tokenBuilder.ToString();
+                       }
+               }
+       }
+
+       string ParseCCode()
+       {
+               SType stackEffect;
+               string r = ParseCCode(out stackEffect);
+               if (stackEffect.IsKnown) {
+                       throw new Exception(
+                               "Stack effect forbidden in this declaration");
+               }
+               return r;
+       }
+
+       string ParseCCode(out SType stackEffect)
+       {
+               string s = ParseCCodeNF(out stackEffect);
+               if (s == null) {
+                       throw new Exception("Error while parsing C code");
+               }
+               return s;
+       }
+
+       string ParseCCodeNF(out SType stackEffect)
+       {
+               stackEffect = SType.UNKNOWN;
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               return null;
+                       }
+                       if (!IsWS(c)) {
+                               if (c == '(') {
+                                       if (stackEffect.IsKnown) {
+                                               Unread(c);
+                                               return null;
+                                       }
+                                       stackEffect = ParseStackEffectNF();
+                                       if (!stackEffect.IsKnown) {
+                                               return null;
+                                       }
+                                       continue;
+                               } else if (c != '{') {
+                                       Unread(c);
+                                       return null;
+                               }
+                               break;
+                       }
+               }
+               StringBuilder sb = new StringBuilder();
+               int count = 1;
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               return null;
+                       }
+                       switch (c) {
+                       case '{':
+                               count ++;
+                               break;
+                       case '}':
+                               if (-- count == 0) {
+                                       return sb.ToString();
+                               }
+                               break;
+                       }
+                       sb.Append((char)c);
+               }
+       }
+
+       /*
+        * Parse a stack effect declaration. This method assumes that the
+        * opening parenthesis has just been read. If the parsing fails,
+        * then this method returns SType.UNKNOWN.
+        */
+       SType ParseStackEffectNF()
+       {
+               bool seenSep = false;
+               bool seenBang = false;
+               int din = 0, dout = 0;
+               for (;;) {
+                       string t = Next();
+                       if (t == null) {
+                               return SType.UNKNOWN;
+                       }
+                       if (t == "--") {
+                               if (seenSep) {
+                                       return SType.UNKNOWN;
+                               }
+                               seenSep = true;
+                       } else if (t == ")") {
+                               if (seenSep) {
+                                       if (seenBang && dout == 1) {
+                                               dout = -1;
+                                       }
+                                       return new SType(din, dout);
+                               } else {
+                                       return SType.UNKNOWN;
+                               }
+                       } else {
+                               if (seenSep) {
+                                       if (dout == 0 && t == "!") {
+                                               seenBang = true;
+                                       }
+                                       dout ++;
+                               } else {
+                                       din ++;
+                               }
+                       }
+               }
+       }
+
+       string ParseString()
+       {
+               StringBuilder sb = new StringBuilder();
+               sb.Append('"');
+               bool lcwb = false;
+               int hexNum = 0;
+               int acc = 0;
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception(
+                                       "Unfinished literal string");
+                       }
+                       if (hexNum > 0) {
+                               int d = HexVal(c);
+                               if (d < 0) {
+                                       throw new Exception(String.Format(
+                                               "not an hex digit: U+{0:X4}",
+                                               c));
+                               }
+                               acc = (acc << 4) + d;
+                               if (-- hexNum == 0) {
+                                       sb.Append((char)acc);
+                                       acc = 0;
+                               }
+                       } else if (lcwb) {
+                               lcwb = false;
+                               switch (c) {
+                               case '\n': SkipNL(); break;
+                               case 'x':
+                                       hexNum = 2;
+                                       break;
+                               case 'u':
+                                       hexNum = 4;
+                                       break;
+                               default:
+                                       sb.Append(SingleCharEscape(c));
+                                       break;
+                               }
+                       } else {
+                               switch (c) {
+                               case '"':
+                                       return sb.ToString();
+                               case '\\':
+                                       lcwb = true;
+                                       break;
+                               default:
+                                       sb.Append((char)c);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       static char SingleCharEscape(int c)
+       {
+               switch (c) {
+               case 'n': return '\n';
+               case 'r': return '\r';
+               case 't': return '\t';
+               case 's': return ' ';
+               default:
+                       return (char)c;
+               }
+       }
+
+       /*
+        * A backslash+newline sequence occurred in a literal string; we
+        * check and consume the newline escape sequence (whitespace at
+        * start of next line, then a double-quote character).
+        */
+       void SkipNL()
+       {
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception("EOF in literal string");
+                       }
+                       if (c == '\n') {
+                               throw new Exception(
+                                       "Unescaped newline in literal string");
+                       }
+                       if (IsWS(c)) {
+                               continue;
+                       }
+                       if (c == '"') {
+                               return;
+                       }
+                       throw new Exception(
+                               "Invalid newline escape in literal string");
+               }
+       }
+
+       static char DecodeCharConst(string t)
+       {
+               if (t.Length == 1 && t[0] != '\\') {
+                       return t[0];
+               }
+               if (t.Length >= 2 && t[0] == '\\') {
+                       switch (t[1]) {
+                       case 'x':
+                               if (t.Length == 4) {
+                                       int x = DecHex(t.Substring(2));
+                                       if (x >= 0) {
+                                               return (char)x;
+                                       }
+                               }
+                               break;
+                       case 'u':
+                               if (t.Length == 6) {
+                                       int x = DecHex(t.Substring(2));
+                                       if (x >= 0) {
+                                               return (char)x;
+                                       }
+                               }
+                               break;
+                       default:
+                               if (t.Length == 2) {
+                                       return SingleCharEscape(t[1]);
+                               }
+                               break;
+                       }
+               }
+               throw new Exception("Invalid literal char: `" + t);
+       }
+
+       static int DecHex(string s)
+       {
+               int acc = 0;
+               foreach (char c in s) {
+                       int d = HexVal(c);
+                       if (d < 0) {
+                               return -1;
+                       }
+                       acc = (acc << 4) + d;
+               }
+               return acc;
+       }
+
+       static int HexVal(int c)
+       {
+               if (c >= '0' && c <= '9') {
+                       return c - '0';
+               } else if (c >= 'A' && c <= 'F') {
+                       return c - ('A' - 10);
+               } else if (c >= 'a' && c <= 'f') {
+                       return c - ('a' - 10);
+               } else {
+                       return -1;
+               }
+       }
+
+       string ReadTerm(int ct)
+       {
+               StringBuilder sb = new StringBuilder();
+               for (;;) {
+                       int c = NextChar();
+                       if (c < 0) {
+                               throw new Exception(String.Format(
+                                       "EOF reached before U+{0:X4}", ct));
+                       }
+                       if (c == ct) {
+                               return sb.ToString();
+                       }
+                       sb.Append((char)c);
+               }
+       }
+
+       static bool IsWS(int c)
+       {
+               return c <= 32;
+       }
+
+       void ProcessInput(TextReader tr)
+       {
+               this.currentInput = tr;
+               delayedChar = -1;
+               Word w = new WordNative(this, "toplevel",
+                       xcpu => { CompileStep(xcpu); });
+               CPU cpu = new CPU();
+               Opcode[] code = new Opcode[] {
+                       new OpcodeCall(w),
+                       new OpcodeJumpUncond(-2)
+               };
+               quitRunLoop = false;
+               cpu.Enter(code, 0);
+               for (;;) {
+                       if (quitRunLoop) {
+                               break;
+                       }
+                       Opcode op = cpu.ipBuf[cpu.ipOff ++];
+                       op.Run(cpu);
+               }
+       }
+
+       void CompileStep(CPU cpu)
+       {
+               string tt = Next();
+               if (tt == null) {
+                       if (compiling) {
+                               throw new Exception("EOF while compiling");
+                       }
+                       quitRunLoop = true;
+                       return;
+               }
+               TValue v;
+               bool isVal = TryParseLiteral(tt, out v);
+               Word w = LookupNF(tt);
+               if (isVal && w != null) {
+                       throw new Exception(String.Format(
+                               "Ambiguous: both defined word and literal: {0}",
+                               tt));
+               }
+               if (compiling) {
+                       if (isVal) {
+                               wordBuilder.Literal(v);
+                       } else if (w != null) {
+                               if (w.Immediate) {
+                                       w.Run(cpu);
+                               } else {
+                                       wordBuilder.CallExt(w);
+                               }
+                       } else {
+                               wordBuilder.Call(tt);
+                       }
+               } else {
+                       if (isVal) {
+                               cpu.Push(v);
+                       } else if (w != null) {
+                               w.Run(cpu);
+                       } else {
+                               throw new Exception(String.Format(
+                                       "Unknown word: '{0}'", tt));
+                       }
+               }
+       }
+
+       string GetCCode(string name)
+       {
+               string ccode;
+               allCCode.TryGetValue(name, out ccode);
+               return ccode;
+       }
+
+       void Generate(string outBase, string coreRun,
+               params string[] entryPoints)
+       {
+               /*
+                * Gather all words that are part of the generated
+                * code. This is done by exploring references
+                * transitively. All such words are thus implicitly
+                * resolved.
+                */
+               IDictionary<string, Word> wordSet =
+                       new SortedDictionary<string, Word>(
+                               StringComparer.Ordinal);
+               Queue<Word> tx = new Queue<Word>();
+               foreach (string ep in entryPoints) {
+                       if (wordSet.ContainsKey(ep)) {
+                               continue;
+                       }
+                       Word w = Lookup(ep);
+                       wordSet[w.Name] = w;
+                       tx.Enqueue(w);
+               }
+               while (tx.Count > 0) {
+                       Word w = tx.Dequeue();
+                       foreach (Word w2 in w.GetReferences()) {
+                               if (wordSet.ContainsKey(w2.Name)) {
+                                       continue;
+                               }
+                               wordSet[w2.Name] = w2;
+                               tx.Enqueue(w2);
+                       }
+               }
+
+               /*
+                * Do flow analysis.
+                */
+               if (enableFlowAnalysis) {
+                       foreach (string ep in entryPoints) {
+                               Word w = wordSet[ep];
+                               w.AnalyseFlow();
+                               Console.WriteLine("{0}: ds={1} rs={2}",
+                                       ep, w.MaxDataStack, w.MaxReturnStack);
+                               if (w.MaxDataStack > dsLimit) {
+                                       throw new Exception("'" + ep
+                                               + "' exceeds data stack limit");
+                               }
+                               if (w.MaxReturnStack > rsLimit) {
+                                       throw new Exception("'" + ep
+                                               + "' exceeds return stack"
+                                               + " limit");
+                               }
+                       }
+               }
+
+               /*
+                * Gather referenced data areas and compute their
+                * addresses in the generated data block. The address
+                * 0 in the data block is unaffected so that no
+                * valid runtime pointer is equal to null.
+                */
+               IDictionary<long, ConstData> blocks =
+                       new SortedDictionary<long, ConstData>();
+               foreach (Word w in wordSet.Values) {
+                       foreach (ConstData cd in w.GetDataBlocks()) {
+                               blocks[cd.ID] = cd;
+                       }
+               }
+               int dataLen = 1;
+               foreach (ConstData cd in blocks.Values) {
+                       cd.Address = dataLen;
+                       dataLen += cd.Length;
+               }
+
+               /*
+                * Generated code is a sequence of "slot numbers", each
+                * referencing either a piece of explicit C code, or an
+                * entry in the table of interpreted words.
+                *
+                * Opcodes other than "call" get the slots 0 to 6:
+                *
+                *   0   ret           no argument
+                *   1   const         signed value
+                *   2   get local     local number
+                *   3   put local     local number
+                *   4   jump          signed offset
+                *   5   jump if       signed offset
+                *   6   jump if not   signed offset
+                *
+                * The argument, if any, is in "7E" format: the value is
+                * encoded in 7-bit chunk, with big-endian signed
+                * convention. Each 7-bit chunk is encoded over one byte;
+                * the upper bit is 1 for all chunks except the last one.
+                *
+                * Words with explicit C code get the slot numbers
+                * immediately after 6. Interpreted words come afterwards.
+                */
+               IDictionary<string, int> slots = new Dictionary<string, int>();
+               int curSlot = 7;
+
+               /*
+                * Get explicit C code for words which have such code.
+                * We use string equality on C code so that words with
+                * identical implementations get merged.
+                *
+                * We also check that words with no explicit C code are
+                * interpreted.
+                */
+               IDictionary<string, int> ccodeUni =
+                       new Dictionary<string, int>();
+               IDictionary<int, string> ccodeNames =
+                       new Dictionary<int, string>();
+               foreach (Word w in wordSet.Values) {
+                       string ccode = GetCCode(w.Name);
+                       if (ccode == null) {
+                               if (w is WordNative) {
+                                       throw new Exception(String.Format(
+                                               "No C code for native '{0}'",
+                                               w.Name));
+                               }
+                               continue;
+                       }
+                       int sn;
+                       if (ccodeUni.ContainsKey(ccode)) {
+                               sn = ccodeUni[ccode];
+                               ccodeNames[sn] += " " + EscapeCComment(w.Name);
+                       } else {
+                               sn = curSlot ++;
+                               ccodeUni[ccode] = sn;
+                               ccodeNames[sn] = EscapeCComment(w.Name);
+                       }
+                       slots[w.Name] = sn;
+                       w.Slot = sn;
+               }
+
+               /*
+                * Assign slot values to all remaining words; we know they
+                * are all interpreted.
+                */
+               int slotInterpreted = curSlot;
+               foreach (Word w in wordSet.Values) {
+                       if (GetCCode(w.Name) != null) {
+                               continue;
+                       }
+                       int sn = curSlot ++;
+                       slots[w.Name] = sn;
+                       w.Slot = sn;
+               }
+               int numInterpreted = curSlot - slotInterpreted;
+
+               /*
+                * Verify that all entry points are interpreted words.
+                */
+               foreach (string ep in entryPoints) {
+                       if (GetCCode(ep) != null) {
+                               throw new Exception(
+                                       "Non-interpreted entry point");
+                       }
+               }
+
+               /*
+                * Compute the code block. Each word (without any C code)
+                * yields some CodeElement instances.
+                */
+               List<CodeElement> gcodeList = new List<CodeElement>();
+               CodeElement[] interpretedEntry =
+                       new CodeElement[numInterpreted];
+               foreach (Word w in wordSet.Values) {
+                       if (GetCCode(w.Name) != null) {
+                               continue;
+                       }
+                       int n = gcodeList.Count;
+                       w.GenerateCodeElements(gcodeList);
+                       interpretedEntry[w.Slot - slotInterpreted] =
+                               gcodeList[n];
+               }
+               CodeElement[] gcode = gcodeList.ToArray();
+
+               /*
+                * Compute all addresses and offsets. This loops until
+                * the addresses stabilize.
+                */
+               int totalLen = -1;
+               int[] gcodeLen = new int[gcode.Length];
+               for (;;) {
+                       for (int i = 0; i < gcode.Length; i ++) {
+                               gcodeLen[i] = gcode[i].Length;
+                       }
+                       int off = 0;
+                       for (int i = 0; i < gcode.Length; i ++) {
+                               gcode[i].Address = off;
+                               gcode[i].LastLength = gcodeLen[i];
+                               off += gcodeLen[i];
+                       }
+                       if (off == totalLen) {
+                               break;
+                       }
+                       totalLen = off;
+               }
+
+               /*
+                * Produce output file.
+                */
+               using (TextWriter tw = File.CreateText(outBase + ".c")) {
+                       tw.NewLine = "\n";
+
+                       tw.WriteLine("{0}",
+@"/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+");
+
+                       /*
+                        * Add declarations (not definitions) for the
+                        * entry point initialisation functions, and the
+                        * runner.
+                        */
+                       tw.WriteLine();
+                       foreach (string ep in entryPoints) {
+                               tw.WriteLine("void {0}_init_{1}(void *t0ctx);",
+                                       coreRun, ep);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("void {0}_run(void *t0ctx);", coreRun);
+
+                       /*
+                        * Add preamble elements here. They may be needed
+                        * for evaluating constant expressions in the
+                        * code block.
+                        */
+                       foreach (string pp in extraCode) {
+                               tw.WriteLine();
+                               tw.WriteLine("{0}", pp);
+                       }
+
+                       BlobWriter bw;
+                       tw.WriteLine();
+                       tw.Write("static const uint8_t t0_datablock[] = {");
+                       bw = new BlobWriter(tw, 78, 1);
+                       bw.Append((byte)0);
+                       foreach (ConstData cd in blocks.Values) {
+                               cd.Encode(bw);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("};");
+
+                       tw.WriteLine();
+                       tw.Write("static const uint8_t t0_codeblock[] = {");
+                       bw = new BlobWriter(tw, 78, 1);
+                       foreach (CodeElement ce in gcode) {
+                               ce.Encode(bw);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("};");
+
+                       tw.WriteLine();
+                       tw.Write("static const uint16_t t0_caddr[] = {");
+                       for (int i = 0; i < interpretedEntry.Length; i ++) {
+                               if (i != 0) {
+                                       tw.Write(',');
+                               }
+                               tw.WriteLine();
+                               tw.Write("\t{0}", interpretedEntry[i].Address);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("};");
+
+                       tw.WriteLine();
+                       tw.WriteLine("#define T0_INTERPRETED   {0}",
+                               slotInterpreted);
+                       tw.WriteLine();
+                       tw.WriteLine("{0}",
+@"#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)");
+                       tw.WriteLine();
+                       tw.WriteLine("{0}",
+@"#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}");
+
+                       tw.WriteLine();
+                       foreach (string ep in entryPoints) {
+                               tw.WriteLine("T0_DEFENTRY({0}, {1})",
+                                       coreRun + "_init_" + ep,
+                                       wordSet[ep].Slot);
+                       }
+                       tw.WriteLine();
+                       tw.WriteLine("void");
+                       tw.WriteLine("{0}_run(void *t0ctx)", coreRun);
+                       tw.WriteLine("{0}",
+@"{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;");
+
+                       SortedDictionary<int, string> nccode =
+                               new SortedDictionary<int, string>();
+                       foreach (string k in ccodeUni.Keys) {
+                               nccode[ccodeUni[k]] = k;
+                       }
+                       foreach (int sn in nccode.Keys) {
+                               tw.WriteLine(
+@"                     case {0}: {{
+                               /* {1} */
+{2}
+                               }}
+                               break;", sn, ccodeNames[sn], nccode[sn]);
+                       }
+
+                       tw.WriteLine(
+@"                     }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}");
+               }
+
+               int codeLen = 0;
+               foreach (CodeElement ce in gcode) {
+                       codeLen += ce.Length;
+               }
+               int dataBlockLen = 0;
+               foreach (ConstData cd in blocks.Values) {
+                       dataBlockLen += cd.Length;
+               }
+
+               /*
+                * Write some statistics on produced code.
+                */
+               Console.WriteLine("code length: {0,6} byte(s)", codeLen);
+               Console.WriteLine("data length: {0,6} byte(s)", dataLen);
+               Console.WriteLine("interpreted words: {0}",
+                       interpretedEntry.Length);
+       }
+
+       internal Word Lookup(string name)
+       {
+               Word w = LookupNF(name);
+               if (w != null) {
+                       return w;
+               }
+               throw new Exception(String.Format("No such word: '{0}'", name));
+       }
+
+       internal Word LookupNF(string name)
+       {
+               Word w;
+               words.TryGetValue(name, out w);
+               return w;
+       }
+
+       internal TValue StringToBlob(string s)
+       {
+               return new TValue(0, new TPointerBlob(this, s));
+       }
+
+       internal bool TryParseLiteral(string tt, out TValue tv)
+       {
+               tv = new TValue(0);
+               if (tt.StartsWith("\"")) {
+                       tv = StringToBlob(tt.Substring(1));
+                       return true;
+               }
+               if (tt.StartsWith("`")) {
+                       tv = DecodeCharConst(tt.Substring(1));
+                       return true;
+               }
+               bool neg = false;
+               if (tt.StartsWith("-")) {
+                       neg = true;
+                       tt = tt.Substring(1);
+               } else if (tt.StartsWith("+")) {
+                       tt = tt.Substring(1);
+               }
+               uint radix = 10;
+               if (tt.StartsWith("0x") || tt.StartsWith("0X")) {
+                       radix = 16;
+                       tt = tt.Substring(2);
+               } else if (tt.StartsWith("0b") || tt.StartsWith("0B")) {
+                       radix = 2;
+                       tt = tt.Substring(2);
+               }
+               if (tt.Length == 0) {
+                       return false;
+               }
+               uint acc = 0;
+               bool overflow = false;
+               uint maxV = uint.MaxValue / radix;
+               foreach (char c in tt) {
+                       int d = HexVal(c);
+                       if (d < 0 || d >= radix) {
+                               return false;
+                       }
+                       if (acc > maxV) {
+                               overflow = true;
+                       }
+                       acc *= radix;
+                       if ((uint)d > uint.MaxValue - acc) {
+                               overflow = true;
+                       }
+                       acc += (uint)d;
+               }
+               int x = (int)acc;
+               if (neg) {
+                       if (acc > (uint)0x80000000) {
+                               overflow = true;
+                       }
+                       x = -x;
+               }
+               if (overflow) {
+                       throw new Exception(
+                               "invalid literal integer (overflow)");
+               }
+               tv = x;
+               return true;
+       }
+
+       int ParseInteger(string tt)
+       {
+               TValue tv;
+               if (!TryParseLiteral(tt, out tv)) {
+                       throw new Exception("not an integer: " + ToString());
+               }
+               return (int)tv;
+       }
+
+       void CheckCompiling()
+       {
+               if (!compiling) {
+                       throw new Exception("Not in compilation mode");
+               }
+       }
+
+       static string EscapeCComment(string s)
+       {
+               StringBuilder sb = new StringBuilder();
+               foreach (char c in s) {
+                       if (c >= 33 && c <= 126 && c != '%') {
+                               sb.Append(c);
+                       } else if (c < 0x100) {
+                               sb.AppendFormat("%{0:X2}", (int)c);
+                       } else if (c < 0x800) {
+                               sb.AppendFormat("%{0:X2}%{0:X2}",
+                                       ((int)c >> 6) | 0xC0,
+                                       ((int)c & 0x3F) | 0x80);
+                       } else {
+                               sb.AppendFormat("%{0:X2}%{0:X2}%{0:X2}",
+                                       ((int)c >> 12) | 0xE0,
+                                       (((int)c >> 6) & 0x3F) | 0x80,
+                                       ((int)c & 0x3F) | 0x80);
+                       }
+               }
+               return sb.ToString().Replace("*/", "%2A/");
+       }
+}
diff --git a/T0/TPointerBase.cs b/T0/TPointerBase.cs
new file mode 100644 (file)
index 0000000..f996d8d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerBase {
+
+       /* obsolete
+       internal virtual TValue Get(TValue vp)
+       {
+               throw new Exception(
+                       "cannot get values from this pointer");
+       }
+
+       internal virtual void Set(TValue vp, TValue nval)
+       {
+               throw new Exception(
+                       "cannot set values to this pointer");
+       }
+       */
+
+       internal virtual bool ToBool(TValue vp)
+       {
+               return true;
+       }
+
+       internal virtual void Execute(T0Comp ctx, CPU cpu)
+       {
+               throw new Exception("value is not an xt: " + ToString());
+       }
+
+       internal virtual string ToString(TValue vp)
+       {
+               return String.Format("{0}+{1}",
+                       GetType().Name, vp.x);
+       }
+
+       internal virtual bool Equals(TPointerBase tp)
+       {
+               return this == tp;
+       }
+}
diff --git a/T0/TPointerBlob.cs b/T0/TPointerBlob.cs
new file mode 100644 (file)
index 0000000..d4aeff6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerBlob : TPointerBase {
+
+       internal ConstData Blob { get; private set; }
+
+       internal TPointerBlob(ConstData cd)
+       {
+               this.Blob = cd;
+       }
+
+       internal TPointerBlob(T0Comp owner, string s)
+       {
+               Blob = new ConstData(owner);
+               Blob.AddString(s);
+       }
+
+       /* obsolete
+       internal override TValue Get8(TValue vp)
+       {
+               return new TValue((int)Blob.Read8(vp.x));
+       }
+
+       internal override TValue Get16(TValue vp)
+       {
+               return new TValue((int)Blob.Read16(vp.x));
+       }
+
+       internal override TValue Get24(TValue vp)
+       {
+               return new TValue((int)Blob.Read24(vp.x));
+       }
+
+       internal override TValue Get32(TValue vp)
+       {
+               return new TValue((int)Blob.Read32(vp.x));
+       }
+       */
+
+       internal override string ToString(TValue vp)
+       {
+               return Blob.ToString(vp.x);
+       }
+
+       internal override bool Equals(TPointerBase tp)
+       {
+               TPointerBlob tb = tp as TPointerBlob;
+               return tb != null && Blob == tb.Blob;
+       }
+}
diff --git a/T0/TPointerExpr.cs b/T0/TPointerExpr.cs
new file mode 100644 (file)
index 0000000..314b272
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+
+class TPointerExpr : TPointerBase {
+
+       string expr;
+       int min, max;
+
+       internal TPointerExpr(string expr, int min, int max)
+       {
+               this.expr = expr;
+               this.min = min;
+               this.max = max;
+       }
+
+       internal override bool ToBool(TValue vp)
+       {
+               throw new Exception("Cannot evaluate C-expr at compile time");
+       }
+
+       internal override string ToString(TValue vp)
+       {
+               return ToCExpr(vp.x);
+       }
+
+       internal string ToCExpr(int off)
+       {
+               if (off == 0) {
+                       return expr;
+               } else if (off > 0) {
+                       return String.Format(
+                               "(uint32_t)({0}) + {1}", expr, off);
+               } else {
+                       return String.Format(
+                               "(uint32_t)({0}) - {1}", expr, -(long)off);
+               }
+       }
+
+       internal int GetMaxBitLength(int off)
+       {
+               long rmin = (long)min + off;
+               long rmax = (long)max + off;
+               int numBits = 1;
+               if (rmin < 0) {
+                       numBits = Math.Max(numBits, BitLength(rmin));
+               }
+               if (rmax > 0) {
+                       numBits = Math.Max(numBits, BitLength(rmax));
+               }
+               return Math.Min(numBits, 32);
+       }
+
+       /*
+        * Get the minimal bit length of a value. This is for a signed
+        * representation: the length includes a sign bit. Thus, the
+        * returned value will be at least 1.
+        */
+       static int BitLength(long v)
+       {
+               int num = 1;
+               if (v < 0) {
+                       while (v != -1) {
+                               num ++;
+                               v >>= 1;
+                       }
+               } else {
+                       while (v != 0) {
+                               num ++;
+                               v >>= 1;
+                       }
+               }
+               return num;
+       }
+}
diff --git a/T0/TPointerNull.cs b/T0/TPointerNull.cs
new file mode 100644 (file)
index 0000000..f8eb11e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerNull : TPointerBase {
+
+       internal override bool ToBool(TValue vp)
+       {
+               return false;
+       }
+
+       internal override string ToString(TValue vp)
+       {
+               return "null";
+       }
+
+       internal override bool Equals(TPointerBase tp)
+       {
+               return tp is TPointerNull;
+       }
+}
diff --git a/T0/TPointerXT.cs b/T0/TPointerXT.cs
new file mode 100644 (file)
index 0000000..883f312
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class TPointerXT : TPointerBase {
+
+       internal string Name {
+               get; private set;
+       }
+
+       internal Word Target {
+               get; private set;
+       }
+
+       internal TPointerXT(string name)
+       {
+               this.Name = name;
+               this.Target = null;
+       }
+
+       internal TPointerXT(Word target)
+       {
+               this.Name = target.Name;
+               this.Target = target;
+       }
+
+       internal void Resolve(T0Comp ctx)
+       {
+               if (Target == null) {
+                       Target = ctx.Lookup(Name);
+               }
+       }
+
+       internal override void Execute(T0Comp ctx, CPU cpu)
+       {
+               Resolve(ctx);
+               Target.Run(cpu);
+       }
+
+       internal override string ToString(TValue vp)
+       {
+               return String.Format("<'{0}>", Name);
+       }
+
+       internal override bool Equals(TPointerBase tp)
+       {
+               TPointerXT tx = tp as TPointerXT;
+               return tx != null && Name == tx.Name;
+       }
+}
diff --git a/T0/TValue.cs b/T0/TValue.cs
new file mode 100644 (file)
index 0000000..e4f2255
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * Each value is represented with a TValue structure. Integers use the 'x'
+ * field, and 'ptr' is null; for pointers, the 'ptr' field is used, and the
+ * 'x' is then an offset in the object represented by 'ptr'.
+ */
+
+struct TValue {
+
+       internal int x;
+       internal TPointerBase ptr;
+
+       internal TValue(int x)
+       {
+               this.x = x;
+               this.ptr = null;
+       }
+
+       internal TValue(uint x)
+       {
+               this.x = (int)x;
+               this.ptr = null;
+       }
+
+       internal TValue(bool b)
+       {
+               this.x = b ? -1 : 0;
+               this.ptr = null;
+       }
+
+       internal TValue(int x, TPointerBase ptr)
+       {
+               this.x = x;
+               this.ptr = ptr;
+       }
+
+       /*
+        * Convert this value to a boolean; integer 0 and null pointer are
+        * 'false', other values are 'true'.
+        */
+       internal bool Bool {
+               get {
+                       if (ptr == null) {
+                               return x != 0;
+                       } else {
+                               return ptr.ToBool(this);
+                       }
+               }
+       }
+
+       /*
+        * Get this value as an integer. Pointers cannot be converted to
+        * integers.
+        */
+       internal int Int {
+               get {
+                       if (ptr == null) {
+                               return x;
+                       }
+                       throw new Exception("not an integer: " + ToString());
+               }
+       }
+
+       /*
+        * Get this value as an unsigned integer. This is the integer
+        * value, reduced modulo 2^32 in the 0..2^32-1 range.
+        */
+       internal uint UInt {
+               get {
+                       return (uint)Int;
+               }
+       }
+
+       /*
+        * String format of integers uses decimal representation. For
+        * pointers, this depends on the pointed-to value.
+        */
+       public override string ToString()
+       {
+               if (ptr == null) {
+                       return String.Format("{0}", x);
+               } else {
+                       return ptr.ToString(this);
+               }
+       }
+
+       /*
+        * If this value is an XT, then execute it. Otherwise, an exception
+        * is thrown.
+        */
+       internal void Execute(T0Comp ctx, CPU cpu)
+       {
+               ToXT().Execute(ctx, cpu);
+       }
+
+       /*
+        * Convert this value to an XT. On failure, an exception is thrown.
+        */
+       internal TPointerXT ToXT()
+       {
+               TPointerXT xt = ptr as TPointerXT;
+               if (xt == null) {
+                       throw new Exception(
+                               "value is not an xt: " + ToString());
+               }
+               return xt;
+       }
+
+       /*
+        * Compare this value to another.
+        */
+       internal bool Equals(TValue v)
+       {
+               if (x != v.x) {
+                       return false;
+               }
+               if (ptr == v.ptr) {
+                       return true;
+               }
+               if (ptr == null || v.ptr == null) {
+                       return false;
+               }
+               return ptr.Equals(v.ptr);
+       }
+
+       public static implicit operator TValue(bool val)
+       {
+               return new TValue(val);
+       }
+
+       public static implicit operator TValue(sbyte val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(byte val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(short val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(ushort val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(char val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(int val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator TValue(uint val)
+       {
+               return new TValue((int)val);
+       }
+
+       public static implicit operator bool(TValue v)
+       {
+               return v.Bool;
+       }
+
+       public static implicit operator sbyte(TValue v)
+       {
+               return (sbyte)v.Int;
+       }
+
+       public static implicit operator byte(TValue v)
+       {
+               return (byte)v.Int;
+       }
+
+       public static implicit operator short(TValue v)
+       {
+               return (short)v.Int;
+       }
+
+       public static implicit operator ushort(TValue v)
+       {
+               return (ushort)v.Int;
+       }
+
+       public static implicit operator char(TValue v)
+       {
+               return (char)v.Int;
+       }
+
+       public static implicit operator int(TValue v)
+       {
+               return (int)v.Int;
+       }
+
+       public static implicit operator uint(TValue v)
+       {
+               return (uint)v.Int;
+       }
+}
diff --git a/T0/Word.cs b/T0/Word.cs
new file mode 100644 (file)
index 0000000..2dfb66e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * A "word" is a function with a name. Words can be either native or
+ * interpreted; native words are implemented as some in-compiler special
+ * code.
+ *
+ * Some native words (not all of them) have a C implementation and can
+ * thus be part of the generated C code. Native words with no C
+ * implementation can be used only during compilation; this is typically
+ * the case for words that support the syntax (e.g. 'if').
+ */
+
+abstract class Word {
+
+       /*
+        * The compiler context for this word.
+        */
+       internal T0Comp TC {
+               get; private set;
+       }
+
+       /*
+        * Immediate words are executed immediately when encountered in the
+        * source code, even while compiling another word.
+        */
+       internal bool Immediate {
+               get; set;
+       }
+
+       /*
+        * Each word has a unique name. Names are case-sensitive.
+        */
+       internal string Name {
+               get; private set;
+       }
+
+       /*
+        * Words are allocated slot numbers when output code is generated.
+        */
+       internal int Slot {
+               get; set;
+       }
+
+       /*
+        * Each word may have a known stack effect.
+        */
+       internal SType StackEffect {
+               get; set;
+       }
+
+       internal Word(T0Comp owner, string name)
+       {
+               TC = owner;
+               Name = name;
+               StackEffect = SType.UNKNOWN;
+       }
+
+       /*
+        * Resolving a word means looking up all references to external
+        * words.
+        */
+       internal virtual void Resolve()
+       {
+       }
+
+       /*
+        * Execute this word. If the word is native, then its code is
+        * run right away; if the word is interpreted, then the entry
+        * sequence is executed.
+        */
+       internal virtual void Run(CPU cpu)
+       {
+               throw new Exception(String.Format(
+                       "cannot run '{0}' at compile-time", Name));
+       }
+
+       /*
+        * All words may have an explicit C implementations. To be part
+        * of the generated C code, a word must either be interpreted,
+        * or have an explicit C implementation, or both.
+        */
+       internal string CCode {
+               get; set;
+       }
+
+       /*
+        * Get all words referenced from this one. This implies
+        * resolving the word.
+        */
+       internal virtual List<Word> GetReferences()
+       {
+               return new List<Word>();
+       }
+
+       /*
+        * Get all data blocks directly referenced from this one. This
+        * implies resolving the word.
+        */
+       internal virtual List<ConstData> GetDataBlocks()
+       {
+               return new List<ConstData>();
+       }
+
+       /*
+        * Produce the code elements for this word.
+        */
+       internal virtual void GenerateCodeElements(List<CodeElement> dst)
+       {
+               throw new Exception("Word does not yield code elements");
+       }
+
+       /*
+        * Compute/verify stack effect for this word.
+        */
+       internal virtual void AnalyseFlow()
+       {
+       }
+
+       /*
+        * Get maximum data stack usage for this word. This is the number
+        * of extra slots that this word may need on the data stack. If
+        * the stack effect is not known, this returns -1.
+        */
+       internal virtual int MaxDataStack {
+               get {
+                       SType se = StackEffect;
+                       if (!se.IsKnown) {
+                               return -1;
+                       }
+                       if (se.NoExit) {
+                               return 0;
+                       } else {
+                               return Math.Min(0, se.DataOut - se.DataIn);
+                       }
+               }
+       }
+
+       /*
+        * Get maximum return stack usage for this word.
+        */
+       internal virtual int MaxReturnStack {
+               get {
+                       return 0;
+               }
+       }
+}
diff --git a/T0/WordBuilder.cs b/T0/WordBuilder.cs
new file mode 100644 (file)
index 0000000..c410930
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * A WordBuilder instance organizes construction of a new interpreted word.
+ *
+ * Opcodes are accumulated with specific methods. A control-flow stack
+ * is maintained to resolve jumps.
+ *
+ * Each instance shall be used for only one word.
+ */
+
+class WordBuilder {
+
+       T0Comp TC;
+       string name;
+       int[] cfStack;
+       int cfPtr;
+       List<Opcode> code;
+       List<string> toResolve;
+       Dictionary<string, int> locals;
+       bool jumpToLast;
+
+       internal SType StackEffect {
+               get; set;
+       }
+
+       /*
+        * Create a new instance, with the specified word name.
+        */
+       internal WordBuilder(T0Comp TC, string name)
+       {
+               this.TC = TC;
+               this.name = name;
+               cfStack = new int[16];
+               cfPtr = -1;
+               code = new List<Opcode>();
+               toResolve = new List<string>();
+               locals = new Dictionary<string, int>();
+               jumpToLast = true;
+               StackEffect = SType.UNKNOWN;
+       }
+
+       /*
+        * Build the word. The control-flow stack must be empty. A 'ret'
+        * opcode is automatically appended if required.
+        */
+       internal Word Build()
+       {
+               if (cfPtr != -1) {
+                       throw new Exception("control-flow stack is not empty");
+               }
+               if (jumpToLast || code[code.Count - 1].MayFallThrough) {
+                       Ret();
+               }
+               Word w = new WordInterpreted(TC, name, locals.Count,
+                       code.ToArray(), toResolve.ToArray());
+               w.StackEffect = StackEffect;
+               return w;
+       }
+
+       void Add(Opcode op)
+       {
+               Add(op, null);
+       }
+
+       void Add(Opcode op, string refName)
+       {
+               code.Add(op);
+               toResolve.Add(refName);
+               jumpToLast = false;
+       }
+
+       /*
+        * Rotate the control-flow stack at depth 'depth'.
+        */
+       internal void CSRoll(int depth)
+       {
+               int x = cfStack[cfPtr - depth];
+               Array.Copy(cfStack, cfPtr - (depth - 1),
+                       cfStack, cfPtr - depth, depth);
+               cfStack[cfPtr] = x;
+       }
+
+       /*
+        * Make a copy of the control-flow element at depth 'depth', and
+        * push it on top of the control-flow stack.
+        */
+       internal void CSPick(int depth)
+       {
+               int x = cfStack[cfPtr - depth];
+               CSPush(x);
+       }
+
+       void CSPush(int x)
+       {
+               int len = cfStack.Length;
+               if (++ cfPtr == len) {
+                       int[] ncf = new int[len << 1];
+                       Array.Copy(cfStack, 0, ncf, 0, len);
+                       cfStack = ncf;
+               }
+               cfStack[cfPtr] = x;
+       }
+
+       int CSPop()
+       {
+               return cfStack[cfPtr --];
+       }
+
+       /*
+        * Push an origin on the control-flow stack, corresponding to the
+        * next opcode to add.
+        */
+       internal void CSPushOrig()
+       {
+               CSPush(code.Count);
+       }
+
+       /*
+        * Push a destination on the control-flow stack, corresponding to
+        * the next opcode to add.
+        */
+       internal void CSPushDest()
+       {
+               CSPush(-code.Count - 1);
+       }
+
+       /*
+        * Pop an origin from the control-flow stack. An exception is
+        * thrown if the value is not an origin.
+        */
+       internal int CSPopOrig()
+       {
+               int x = CSPop();
+               if (x < 0) {
+                       throw new Exception("not an origin");
+               }
+               return x;
+       }
+
+       /*
+        * Pop a destination from the control-flow stack. An exception is
+        * thrown if the value is not a destination.
+        */
+       internal int CSPopDest()
+       {
+               int x = CSPop();
+               if (x >= 0) {
+                       throw new Exception("not a destination");
+               }
+               return -x - 1;
+       }
+
+       /*
+        * Add a "push literal" opcode.
+        */
+       internal void Literal(TValue v)
+       {
+               Add(new OpcodeConst(v));
+       }
+
+       /*
+        * Compile a "call" by name. This method implements the support
+        * for local variables:
+        *
+        *  - If the target is '>' followed by a local variable name, then
+        *    a "put local" opcode is added.
+        *
+        *  - Otherwise, if the target is a local variable name, then a
+        *    "get local" opcode is added.
+        *
+        *  - Otherwise, a call to the named word is added. The target name
+        *    will be resolved later on (typically, when the word containing
+        *    the call opcode is first invoked, or when C code is generated).
+        */
+       internal void Call(string target)
+       {
+               string lname;
+               bool write;
+               if (target.StartsWith(">")) {
+                       lname = target.Substring(1);
+                       write = true;
+               } else {
+                       lname = target;
+                       write = false;
+               }
+               int lnum;
+               if (locals.TryGetValue(lname, out lnum)) {
+                       if (write) {
+                               Add(new OpcodePutLocal(lnum));
+                       } else {
+                               Add(new OpcodeGetLocal(lnum));
+                       }
+               } else {
+                       Add(new OpcodeCall(), target);
+               }
+       }
+
+       /*
+        * Add a "call" opcode to the designated word.
+        */
+       internal void CallExt(Word wtarget)
+       {
+               Add(new OpcodeCall(wtarget), null);
+       }
+
+       /*
+        * Add a "call" opcode to a word which is not currently resolved.
+        * This method ignores local variables.
+        */
+       internal void CallExt(string target)
+       {
+               Add(new OpcodeCall(), target);
+       }
+
+       /*
+        * Add a "get local" opcode; the provided local name must already
+        * be defined.
+        */
+       internal void GetLocal(string name)
+       {
+               int lnum;
+               if (locals.TryGetValue(name, out lnum)) {
+                       Add(new OpcodeGetLocal(lnum));
+               } else {
+                       throw new Exception("no such local: " + name);
+               }
+       }
+
+       /*
+        * Add a "put local" opcode; the provided local name must already
+        * be defined.
+        */
+       internal void PutLocal(string name)
+       {
+               int lnum;
+               if (locals.TryGetValue(name, out lnum)) {
+                       Add(new OpcodePutLocal(lnum));
+               } else {
+                       throw new Exception("no such local: " + name);
+               }
+       }
+
+       /*
+        * Define a new local name.
+        */
+       internal void DefLocal(string lname)
+       {
+               if (locals.ContainsKey(lname)) {
+                       throw new Exception(String.Format(
+                               "local already defined: {0}", lname));
+               }
+               locals[lname] = locals.Count;
+       }
+
+       /*
+        * Add a "call" opcode whose target is an XT value (which may be
+        * resolved or as yet unresolved).
+        */
+       internal void Call(TPointerXT xt)
+       {
+               if (xt.Target == null) {
+                       Add(new OpcodeCall(), xt.Name);
+               } else {
+                       Add(new OpcodeCall(xt.Target));
+               }
+       }
+
+       /*
+        * Add a "ret" opcode.
+        */
+       internal void Ret()
+       {
+               Add(new OpcodeRet());
+       }
+
+       /*
+        * Add a forward unconditional jump. The new opcode address is
+        * pushed on the control-flow stack as an origin.
+        */
+       internal void Ahead()
+       {
+               CSPushOrig();
+               Add(new OpcodeJumpUncond());
+       }
+
+       /*
+        * Add a forward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'true'. The new opcode address is
+        * pushed on the control-flow stack as an origin.
+        */
+       internal void AheadIf()
+       {
+               CSPushOrig();
+               Add(new OpcodeJumpIf());
+       }
+
+       /*
+        * Add a forward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'false'. The new opcode address is
+        * pushed on the control-flow stack as an origin.
+        */
+       internal void AheadIfNot()
+       {
+               CSPushOrig();
+               Add(new OpcodeJumpIfNot());
+       }
+
+       /*
+        * Resolve a previous forward jump to the current code address.
+        * The top of control-flow stack is popped and must be an origin.
+        */
+       internal void Then()
+       {
+               int x = CSPopOrig();
+               code[x].ResolveJump(code.Count - x - 1);
+               jumpToLast = true;
+       }
+
+       /*
+        * Push the current code address on the control-flow stack as a
+        * destination, to be used by an ulterior backward jump.
+        */
+       internal void Begin()
+       {
+               CSPushDest();
+       }
+
+       /*
+        * Add a backward unconditional jump. The jump target is popped
+        * from the control-flow stack as a destination.
+        */
+       internal void Again()
+       {
+               int x = CSPopDest();
+               Add(new OpcodeJumpUncond(x - code.Count - 1));
+       }
+
+       /*
+        * Add a backward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'true'. The jump target is popped
+        * from the control-flow stack as a destination.
+        */
+       internal void AgainIf()
+       {
+               int x = CSPopDest();
+               Add(new OpcodeJumpIf(x - code.Count - 1));
+       }
+
+       /*
+        * Add a backward conditional jump, which will be taken at runtime
+        * if the top-of-stack value is 'false'. The jump target is popped
+        * from the control-flow stack as a destination.
+        */
+       internal void AgainIfNot()
+       {
+               int x = CSPopDest();
+               Add(new OpcodeJumpIfNot(x - code.Count - 1));
+       }
+}
diff --git a/T0/WordData.cs b/T0/WordData.cs
new file mode 100644 (file)
index 0000000..2d74bd3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+class WordData : Word {
+
+       ConstData blob;
+       string baseBlobName;
+       int offset;
+       bool ongoingResolution;
+
+       internal WordData(T0Comp owner, string name,
+               ConstData blob, int offset)
+               : base(owner, name)
+       {
+               this.blob = blob;
+               this.offset = offset;
+               StackEffect = new SType(0, 1);
+       }
+
+       internal WordData(T0Comp owner, string name,
+               string baseBlobName, int offset)
+               : base(owner, name)
+       {
+               this.baseBlobName = baseBlobName;
+               this.offset = offset;
+               StackEffect = new SType(0, 1);
+       }
+
+       internal override void Resolve()
+       {
+               if (blob != null) {
+                       return;
+               }
+               if (ongoingResolution) {
+                       throw new Exception(String.Format(
+                               "circular reference in blobs ({0})", Name));
+               }
+               ongoingResolution = true;
+               WordData wd = TC.Lookup(baseBlobName) as WordData;
+               if (wd == null) {
+                       throw new Exception(String.Format(
+                               "data word '{0}' based on non-data word '{1}'",
+                               Name, baseBlobName));
+               }
+               wd.Resolve();
+               blob = wd.blob;
+               offset += wd.offset;
+               ongoingResolution = false;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               Resolve();
+               cpu.Push(new TValue(offset, new TPointerBlob(blob)));
+       }
+
+       internal override List<ConstData> GetDataBlocks()
+       {
+               Resolve();
+               List<ConstData> r = new List<ConstData>();
+               r.Add(blob);
+               return r;
+       }
+
+       internal override void GenerateCodeElements(List<CodeElement> dst)
+       {
+               Resolve();
+               dst.Add(new CodeElementUInt(0));
+               dst.Add(new CodeElementUIntInt(1, blob.Address + offset));
+               dst.Add(new CodeElementUInt(0));
+       }
+}
diff --git a/T0/WordInterpreted.cs b/T0/WordInterpreted.cs
new file mode 100644 (file)
index 0000000..882170b
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * The implementation for interpreted words.
+ */
+
+class WordInterpreted : Word {
+
+       /*
+        * Get the number of local variables for this word.
+        */
+       internal int NumLocals {
+               get; private set;
+       }
+
+       /*
+        * Get the sequence of opcodes for this word.
+        */
+       internal Opcode[] Code {
+               get; private set;
+       }
+
+       string[] toResolve;
+
+       internal WordInterpreted(T0Comp owner, string name,
+               int numLocals, Opcode[] code, string[] toResolve)
+               : base(owner, name)
+       {
+               this.Code = code;
+               this.toResolve = toResolve;
+               NumLocals = numLocals;
+       }
+
+       internal override void Resolve()
+       {
+               if (toResolve == null) {
+                       return;
+               }
+               for (int i = 0; i < toResolve.Length; i ++) {
+                       string tt = toResolve[i];
+                       if (tt == null) {
+                               continue;
+                       }
+                       Code[i].ResolveTarget(TC.Lookup(tt));
+               }
+               toResolve = null;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               Resolve();
+               cpu.Enter(Code, NumLocals);
+       }
+
+       internal override List<Word> GetReferences()
+       {
+               Resolve();
+               List<Word> r = new List<Word>();
+               foreach (Opcode op in Code) {
+                       Word w = op.GetReference(TC);
+                       if (w != null) {
+                               r.Add(w);
+                       }
+               }
+               return r;
+       }
+
+       internal override List<ConstData> GetDataBlocks()
+       {
+               Resolve();
+               List<ConstData> r = new List<ConstData>();
+               foreach (Opcode op in Code) {
+                       ConstData cd = op.GetDataBlock(TC);
+                       if (cd != null) {
+                               r.Add(cd);
+                       }
+               }
+               return r;
+       }
+
+       internal override void GenerateCodeElements(List<CodeElement> dst)
+       {
+               Resolve();
+               int n = Code.Length;
+               CodeElement[] gcode = new CodeElement[n];
+               for (int i = 0; i < n; i ++) {
+                       gcode[i] = Code[i].ToCodeElement();
+               }
+               for (int i = 0; i < n; i ++) {
+                       Code[i].FixUp(gcode, i);
+               }
+               dst.Add(new CodeElementUInt((uint)NumLocals));
+               for (int i = 0; i < n; i ++) {
+                       dst.Add(gcode[i]);
+               }
+       }
+
+       int flowAnalysis;
+       int maxDataStack;
+       int maxReturnStack;
+
+       bool MergeSA(int[] sa, int j, int c)
+       {
+               if (sa[j] == Int32.MinValue) {
+                       sa[j] = c;
+                       return true;
+               } else if (sa[j] != c) {
+                       throw new Exception(string.Format(
+                               "In word '{0}', offset {1}:"
+                               + " stack action mismatch ({2} / {3})",
+                               Name, j, sa[j], c));
+               } else {
+                       return false;
+               }
+       }
+
+       internal override void AnalyseFlow()
+       {
+               switch (flowAnalysis) {
+               case 0:
+                       break;
+               case 1:
+                       return;
+               default:
+                       throw new Exception("recursive call detected in '"
+                               + Name + "'");
+               }
+               flowAnalysis = 2;
+               int n = Code.Length;
+               int[] sa = new int[n];
+               for (int i = 0; i < n; i ++) {
+                       sa[i] = Int32.MinValue;
+               }
+               sa[0] = 0;
+               int[] toExplore = new int[n];
+               int tX = 0, tY = 0;
+               int off = 0;
+
+               int exitSA = Int32.MinValue;
+               int mds = 0;
+               int mrs = 0;
+
+               int maxDepth = 0;
+
+               for (;;) {
+                       Opcode op = Code[off];
+                       bool mft = op.MayFallThrough;
+                       int c = sa[off];
+                       int a;
+                       if (op is OpcodeCall) {
+                               Word w = op.GetReference(TC);
+                               w.AnalyseFlow();
+                               SType se = w.StackEffect;
+                               if (!se.IsKnown) {
+                                       throw new Exception(string.Format(
+                                               "call from '{0}' to '{1}'"
+                                               + " with unknown stack effect",
+                                               Name, w.Name));
+                               }
+                               if (se.NoExit) {
+                                       mft = false;
+                                       a = 0;
+                               } else {
+                                       a = se.DataOut - se.DataIn;
+                               }
+                               mds = Math.Max(mds, c + w.MaxDataStack);
+                               mrs = Math.Max(mrs, w.MaxReturnStack);
+                               maxDepth = Math.Min(maxDepth, c - se.DataIn);
+                       } else if (op is OpcodeRet) {
+                               if (exitSA == Int32.MinValue) {
+                                       exitSA = c;
+                               } else if (exitSA != c) {
+                                       throw new Exception(string.Format(
+                                               "'{0}': exit stack action"
+                                               + " mismatch: {1} / {2}"
+                                               + " (offset {3})",
+                                               Name, exitSA, c, off));
+                               }
+                               a = 0;
+                       } else {
+                               a = op.StackAction;
+                               mds = Math.Max(mds, c + a);
+                       }
+                       c += a;
+                       maxDepth = Math.Min(maxDepth, c);
+
+                       int j = op.JumpDisp;
+                       if (j != 0) {
+                               j += off + 1;
+                               toExplore[tY ++] = j;
+                               MergeSA(sa, j, c);
+                       }
+                       off ++;
+                       if (!mft || !MergeSA(sa, off, c)) {
+                               if (tX < tY) {
+                                       off = toExplore[tX ++];
+                               } else {
+                                       break;
+                               }
+                       }
+               }
+
+               maxDataStack = mds;
+               maxReturnStack = 1 + NumLocals + mrs;
+
+               /*
+                * TODO: see about this warning. Usage of a 'fail'
+                * word (that does not exit) within a 'case..endcase'
+                * structure will make an unreachable opcode. In a future
+                * version we might want to automatically remove dead
+                * opcodes.
+               for (int i = 0; i < n; i ++) {
+                       if (sa[i] == Int32.MinValue) {
+                               Console.WriteLine("warning: word '{0}',"
+                                       + " offset {1}: unreachable opcode",
+                                       Name, i);
+                               continue;
+                       }
+               }
+                */
+
+               SType computed;
+               if (exitSA == Int32.MinValue) {
+                       computed = new SType(-maxDepth, -1);
+               } else {
+                       computed = new SType(-maxDepth, -maxDepth + exitSA);
+               }
+
+               if (StackEffect.IsKnown) {
+                       if (!computed.IsSubOf(StackEffect)) {
+                               throw new Exception(string.Format(
+                                       "word '{0}':"
+                                       + " computed stack effect {1}"
+                                       + " does not match declared {2}",
+                                       Name, computed.ToString(),
+                                       StackEffect.ToString()));
+                       }
+               } else {
+                       StackEffect = computed;
+               }
+
+               flowAnalysis = 1;
+       }
+
+       internal override int MaxDataStack {
+               get {
+                       AnalyseFlow();
+                       return maxDataStack;
+               }
+       }
+
+       internal override int MaxReturnStack {
+               get {
+                       AnalyseFlow();
+                       return maxReturnStack;
+               }
+       }
+}
diff --git a/T0/WordNative.cs b/T0/WordNative.cs
new file mode 100644 (file)
index 0000000..7868872
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+/*
+ * Class for native words.
+ */
+
+class WordNative : Word {
+
+       /*
+        * A type for the native implementation: a method that takes a
+        * CPU as parameter, and returns nothing.
+        */
+       internal delegate void NativeRun(CPU cpu);
+
+       NativeRun code;
+
+       internal WordNative(T0Comp owner, string name, NativeRun code)
+               : base(owner, name)
+       {
+               this.code = code;
+       }
+
+       internal WordNative(T0Comp owner, string name,
+               SType stackEffect, NativeRun code)
+               : this(owner, name, code)
+       {
+               StackEffect = stackEffect;
+       }
+
+       internal override void Run(CPU cpu)
+       {
+               code(cpu);
+       }
+}
diff --git a/T0/kern.t0 b/T0/kern.t0
new file mode 100644 (file)
index 0000000..9fce4f8
--- /dev/null
@@ -0,0 +1,309 @@
+: \ `\n parse drop ; immediate
+
+\ This file defines the core non-native functions (mainly used for
+\ parsing words, i.e. not part of the generated output). The line above
+\ defines the syntax for comments.
+
+\ Define parenthesis comments.
+\ : ( `) parse drop ; immediate
+
+: else postpone ahead 1 cs-roll postpone then ; immediate
+: while postpone if 1 cs-roll ; immediate
+: repeat postpone again postpone then ; immediate
+
+: ['] ' ; immediate
+: [compile] compile ; immediate
+
+: 2drop drop drop ;
+: dup2 over over ;
+
+\ Local variables are defined with the native word '(local)'. We define
+\ a helper construction that mimics what is found in Apple's Open Firmware
+\ implementation. The syntax is: { a b ... ; c d ... }
+\ I.e. there is an opening brace, then some names. Names appearing before
+\ the semicolon are locals that are both defined and then filled with the
+\ values on stack (in stack order: { a b } fills 'b' with the top-of-stack,
+\ and 'a' with the value immediately below). Names appearing after the
+\ semicolon are not initialized.
+: __deflocal ( from_stack name -- )
+       dup (local) swap if
+               compile-local-write
+       else
+               drop
+       then ;
+: __deflocals ( from_stack -- )
+       next-word
+       dup "}" eqstr if
+               2drop ret
+       then
+       dup ";" eqstr if
+               2drop 0 __deflocals ret
+       then
+       over __deflocals
+       __deflocal ;
+: {
+       -1 __deflocals ; immediate
+
+\ Data building words.
+: data:
+       new-data-block next-word define-data-word ;
+: hexb|
+       0 0 { acc z }
+       begin
+               char
+               dup `| = if
+                       z if "Truncated hexadecimal byte" puts cr exitvm then
+                       ret
+               then
+               dup 0x20 > if
+                       hexval
+                       z if acc 4 << + data-add8 else >acc then
+                       z not >z
+               then
+       again ;
+
+\ Convert hexadecimal character to number. Complain loudly if conversion
+\ is not possible.
+: hexval ( char -- x )
+       hexval-nf dup 0 < if "Not an hex digit: " puts . cr exitvm then ;
+
+\ Convert hexadecimal character to number. If not an hexadecimal digit,
+\ return -1.
+: hexval-nf ( char -- x )
+       dup dup `0 >= swap `9 <= and if `0 - ret then
+       dup dup `A >= swap `F <= and if `A - 10 + ret then
+       dup dup `a >= swap `f <= and if `a - 10 + ret then
+       drop -1 ;
+
+\ Convert decimal character to number. Complain loudly if conversion
+\ is not possible.
+: decval ( char -- x )
+       decval-nf dup 0 < if "Not a decimal digit: " puts . cr exitvm then ;
+
+\ Convert decimal character to number. If not a decimal digit,
+\ return -1.
+: decval-nf ( char -- x )
+       dup dup `0 >= swap `9 <= and if `0 - ret then
+       drop -1 ;
+
+\ Commonly used shorthands.
+: 1+ 1 + ;
+: 2+ 2 + ;
+: 1- 1 - ;
+: 2- 2 - ;
+: 0= 0 = ;
+: 0<> 0 <> ;
+: 0< 0 < ;
+: 0> 0 > ;
+
+\ Get a 16-bit value from the constant data block. This uses big-endian
+\ encoding.
+: data-get16 ( addr -- x )
+       dup data-get8 8 << swap 1+ data-get8 + ;
+
+\ The case..endcase construction is the equivalent of 'switch' is C.
+\ Usage:
+\     case
+\         E1 of C1 endof
+\         E2 of C2 endof
+\         ...
+\         CN
+\     endcase
+\
+\ Upon entry, it considers the TOS (let's call it X). It will then evaluate
+\ E1, which should yield a single value Y1; at that point, the X value is
+\ still on the stack, just below Y1, and must remain untouched. The 'of'
+\ word compares X with Y1; if they are equal, C1 is executed, and then
+\ control jumps to after the 'endcase'. The X value is popped from the
+\ stack immediately before evaluating C1.
+\
+\ If X and Y1 are not equal, flow proceeds to E2, to obtain a value Y2 to
+\ compare with X. And so on.
+\
+\ If none of the 'of' clauses found a match, then CN is evaluated. When CN
+\ is evaluated, the X value is on the TOS, and CN must either leave it on
+\ the stack, or replace it with exactly one value; the 'endcase' word
+\ expects (and drops) one value.
+\
+\ Implementation: this is mostly copied from ANS Forth specification,
+\ although simplified a bit because we know that our control-flow stack
+\ is independent of the data stack. During compilation, the number of
+\ clauses is maintained on the stack; each of..endof clause really is
+\ an 'if..else' that must be terminated with a matching 'then' in 'endcase'.
+
+: case 0 ; immediate
+: of 1+ postpone over postpone = postpone if postpone drop ; immediate
+: endof postpone else ; immediate
+: endcase
+       postpone drop
+       begin dup while 1- postpone then repeat drop ; immediate
+
+\ A simpler and more generic "case": there is no management for a value
+\ on the stack, and each test is supposed to come up with its own boolean
+\ value.
+: choice 0 ; immediate
+: uf 1+ postpone if ; immediate
+: ufnot 1+ postpone ifnot ; immediate
+: enduf postpone else ; immediate
+: endchoice begin dup while 1- postpone then repeat drop ; immediate
+
+\ C implementations for native words that can be used in generated code.
+add-cc: co { T0_CO(); }
+add-cc: execute { T0_ENTER(ip, rp, T0_POP()); }
+add-cc: drop { (void)T0_POP(); }
+add-cc: dup { T0_PUSH(T0_PEEK(0)); }
+add-cc: swap { T0_SWAP(); }
+add-cc: over { T0_PUSH(T0_PEEK(1)); }
+add-cc: rot { T0_ROT(); }
+add-cc: -rot { T0_NROT(); }
+add-cc: roll { T0_ROLL(T0_POP()); }
+add-cc: pick { T0_PICK(T0_POP()); }
+add-cc: + {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+}
+add-cc: - {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+}
+add-cc: neg {
+       uint32_t a = T0_POP();
+       T0_PUSH(-a);
+}
+add-cc: * {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a * b);
+}
+add-cc: / {
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSHi(a / b);
+}
+add-cc: u/ {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a / b);
+}
+add-cc: % {
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSHi(a % b);
+}
+add-cc: u% {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a % b);
+}
+add-cc: < {
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+}
+add-cc: <= {
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a <= b));
+}
+add-cc: > {
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a > b));
+}
+add-cc: >= {
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+}
+add-cc: = {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+}
+add-cc: <> {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+}
+add-cc: u< {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a < b));
+}
+add-cc: u<= {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a <= b));
+}
+add-cc: u> {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a > b));
+}
+add-cc: u>= {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a >= b));
+}
+add-cc: and {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+}
+add-cc: or {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a | b);
+}
+add-cc: xor {
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a ^ b);
+}
+add-cc: not {
+       uint32_t a = T0_POP();
+       T0_PUSH(~a);
+}
+add-cc: << {
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+}
+add-cc: >> {
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+}
+add-cc: u>> {
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x >> c);
+}
+add-cc: data-get8 {
+       size_t addr = T0_POP();
+       T0_PUSH(t0_datablock[addr]);
+}
+
+add-cc: . {
+       extern int printf(const char *fmt, ...);
+       printf(" %ld", (long)T0_POPi());
+}
+add-cc: putc {
+       extern int printf(const char *fmt, ...);
+       printf("%c", (char)T0_POPi());
+}
+add-cc: puts {
+       extern int printf(const char *fmt, ...);
+       printf("%s", &t0_datablock[T0_POPi()]);
+}
+add-cc: cr {
+       extern int printf(const char *fmt, ...);
+       printf("\n");
+}
+add-cc: eqstr {
+       const void *b = &t0_datablock[T0_POPi()];
+       const void *a = &t0_datablock[T0_POPi()];
+       T0_PUSH(-(int32_t)(strcmp(a, b) == 0));
+}
diff --git a/T0Comp.exe b/T0Comp.exe
new file mode 100755 (executable)
index 0000000..411645c
Binary files /dev/null and b/T0Comp.exe differ
diff --git a/build/.do_not_remove b/build/.do_not_remove
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/inc/bearssl.h b/inc/bearssl.h
new file mode 100644 (file)
index 0000000..c11080e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_H__
+#define BR_BEARSSL_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "bearssl_hash.h"
+#include "bearssl_hmac.h"
+#include "bearssl_rand.h"
+#include "bearssl_prf.h"
+#include "bearssl_block.h"
+#include "bearssl_rsa.h"
+#include "bearssl_ec.h"
+#include "bearssl_ssl.h"
+#include "bearssl_x509.h"
+#include "bearssl_pem.h"
+
+#endif
diff --git a/inc/bearssl_block.h b/inc/bearssl_block.h
new file mode 100644 (file)
index 0000000..6dc0e5b
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_BLOCK_H__
+#define BR_BEARSSL_BLOCK_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Block Ciphers
+ * -------------
+ *
+ * For a block cipher implementation, up to three separate sets of
+ * functions are provided, for CBC encryption, CBC decryption, and CTR
+ * encryption/decryption. Each set has its own context structure,
+ * initialized with the encryption key. Each set of functions is
+ * provided both as named functions, and through an OOP interface.
+ *
+ * For CBC encryption and decryption, the data to encrypt or decrypt is
+ * referenced as a sequence of blocks. The implementations assume that
+ * there is no partial block; no padding is applied or removed. The
+ * caller is responsible for handling any kind of padding.
+ *
+ * Function for CTR encryption are defined only for block ciphers with
+ * blocks of 16 bytes or more (i.e. AES, but not DES/3DES).
+ *
+ * Each implemented block cipher is identified by an "internal name"
+ * from which are derived the names of structures and functions that
+ * implement the cipher. For the block cipher of internal name "xxx",
+ * the following are defined:
+ *
+ * br_xxx_BLOCK_SIZE
+ *    A macro that evaluates to the block size (in bytes) of the
+ *    cipher. For all implemented block ciphers, this value is a
+ *    power of two.
+ *
+ * br_xxx_cbcenc_keys
+ *    Context structure that contains the subkeys resulting from the key
+ *    expansion. These subkeys are appropriate for CBC encryption. The
+ *    structure first field is called 'vtable' and points to the
+ *    appropriate OOP structure.
+ *
+ * br_xxx_cbcenc_init(br_xxx_cbcenc_keys *ctx, const void *key, size_t len)
+ *    Perform key expansion: subkeys for CBC encryption are computed and
+ *    written in the provided context structure. The key length MUST be
+ *    adequate for the implemented block cipher. This function also sets
+ *    the 'vtable' field.
+ *
+ * br_xxx_cbcenc_run(const br_xxx_cbcenc_keys *ctx,
+ *                   void *iv, void *data, size_t len)
+ *    Perform CBC encryption of 'len' bytes, in place. The encrypted data
+ *    replaces the cleartext. 'len' MUST be a multiple of the block length
+ *    (if it is not, the function may loop forever or overflow a buffer).
+ *    The IV is provided with the 'iv' pointer; it is also updated with
+ *    a copy of the last encrypted block.
+ *
+ * br_xxx_cbcdec_keys
+ *    Context structure that contains the subkeys resulting from the key
+ *    expansion. These subkeys are appropriate for CBC decryption. The
+ *    structure first field is called 'vtable' and points to the
+ *    appropriate OOP structure.
+ *
+ * br_xxx_cbcdec_init(br_xxx_cbcenc_keys *ctx, const void *key, size_t len)
+ *    Perform key expansion: subkeys for CBC decryption are computed and
+ *    written in the provided context structure. The key length MUST be
+ *    adequate for the implemented block cipher. This function also sets
+ *    the 'vtable' field.
+ *
+ * br_xxx_cbcdec_run(const br_xxx_cbcdec_keys *ctx,
+ *                   void *iv, void *data, size_t num_blocks)
+ *    Perform CBC decryption of 'len' bytes, in place. The decrypted data
+ *    replaces the ciphertext. 'len' MUST be a multiple of the block length
+ *    (if it is not, the function may loop forever or overflow a buffer).
+ *    The IV is provided with the 'iv' pointer; it is also updated with
+ *    a copy of the last encrypted block.
+ *
+ * br_xxx_ctr_keys
+ *    Context structure that contains the subkeys resulting from the key
+ *    expansion. These subkeys are appropriate for CTR encryption and
+ *    decryption. The structure first field is called 'vtable' and
+ *    points to the appropriate OOP structure.
+ *
+ * br_xxx_ctr_init(br_xxx_ctr_keys *ctx, const void *key, size_t len)
+ *    Perform key expansion: subkeys for CTR encryption and decryption
+ *    are computed and written in the provided context structure. The
+ *    key length MUST be adequate for the implemented block cipher. This
+ *    function also sets the 'vtable' field.
+ *
+ * br_xxx_ctr_run(const br_xxx_ctr_keys *ctx, const void *iv,
+ *                uint32_t cc, void *data, size_t len) [returns uint32_t]
+ *    Perform CTR encryption/decryption of some data. Processing is done
+ *    "in place" (the output data replaces the input data). This function
+ *    implements the "standard incrementing function" from NIST SP800-38A,
+ *    annex B: the IV length shall be 4 bytes less than the block size
+ *    (i.e. 12 bytes for AES) and the counter is the 32-bit value starting
+ *    with 'cc'. The data length ('len') is not necessarily a multiple of
+ *    the block size. The new counter value is returned, which supports
+ *    chunked processing, provided that each chunk length (except possibly
+ *    the last one) is a multiple of the block size.
+ *
+ *
+ * It shall be noted that the key expansion functions return 'void'. If
+ * the provided key length is not allowed, then there will be no error
+ * reporting; implementations need not validate the key length, thus an
+ * invalid key length may result in undefined behaviour (e.g. buffer
+ * overflow).
+ *
+ * Subkey structures contain no interior pointer, and no external
+ * resources are allocated upon key expansion. They can thus be
+ * discarded without any explicit deallocation.
+ *
+ *
+ * Object-oriented API: each context structure begins with a field
+ * (called 'vtable') that points to an instance of a structure that
+ * references the relevant functions through pointers. Each such
+ * structure contains the following:
+ *
+ *   context_size     size (in bytes) of the context structure for subkeys
+ *   block_size       cipher block size (in bytes)
+ *   log_block_size   base-2 logarithm of cipher block size
+ *   init             pointer to the key expansion function
+ *   run              pointer to the encryption/decryption function
+ *
+ * Static, constant instances of these structures are defined, under
+ * the names:
+ *
+ *   br_xxx_cbcenc_vtable
+ *   br_xxx_cbcdec_vtable
+ *   br_xxx_ctr_vtable
+ *
+ *
+ * Implemented Block Ciphers
+ * -------------------------
+ * 
+ *   Function   Name         Allowed key lengths (bytes)
+ *
+ *   AES        aes_ct       16, 24 and 32
+ *   AES        aes_ct64     16, 24 and 32
+ *   AES        aes_big      16, 24 and 32
+ *   AES        aes_small    16, 24 and 32
+ *   DES        des_ct       8, 16 and 24
+ *   DES        des_tab      8, 16 and 24
+ *
+ * 'aes_big' is a "classical" AES implementation, using tables. It
+ * is fast but not constant-time, since it makes data-dependent array
+ * accesses.
+ *
+ * 'aes_small' is an AES implementation optimized for code size. It
+ * is substantially slower than 'aes_big'; it is not constant-time
+ * either.
+ *
+ * 'aes_ct' is a constant-time implementation of AES; its code is about
+ * as big as that of 'aes_big', while its performance is comparable to
+ * that of 'aes_small'. However, it is constant-time. This
+ * implementation should thus be considered to be the "default" AES in
+ * BearSSL, to be used unless the operational context guarantees that a
+ * non-constant-time implementation is safe, or an architecture-specific
+ * constant-time implementation can be used (e.g. using dedicated
+ * hardware opcodes).
+ *
+ * 'aes_ct64' is another constant-time implementation of AES. It is
+ * similar to 'aes_ct' but uses 64-bit values, for faster processing
+ * on 64-bit machines.
+ *
+ * 'des_tab' is a classic, table-based implementation of DES/3DES. It
+ * is not constant-time.
+ *
+ * 'des_ct' is an constant-time implementation of DES/3DES. It is
+ * substantially slower than 'des_tab'.
+ */
+
+typedef struct br_block_cbcenc_class_ br_block_cbcenc_class;
+struct br_block_cbcenc_class_ {
+       size_t context_size;
+       unsigned block_size;
+       unsigned log_block_size;
+       void (*init)(const br_block_cbcenc_class **ctx,
+               const void *key, size_t key_len);
+       void (*run)(const br_block_cbcenc_class *const *ctx,
+               void *iv, void *data, size_t len);
+};
+
+typedef struct br_block_cbcdec_class_ br_block_cbcdec_class;
+struct br_block_cbcdec_class_ {
+       size_t context_size;
+       unsigned block_size;
+       unsigned log_block_size;
+       void (*init)(const br_block_cbcdec_class **ctx,
+               const void *key, size_t key_len);
+       void (*run)(const br_block_cbcdec_class *const *ctx,
+               void *iv, void *data, size_t len);
+};
+
+typedef struct br_block_ctr_class_ br_block_ctr_class;
+struct br_block_ctr_class_ {
+       size_t context_size;
+       unsigned block_size;
+       unsigned log_block_size;
+       void (*init)(const br_block_ctr_class **ctx,
+               const void *key, size_t key_len);
+       uint32_t (*run)(const br_block_ctr_class *const *ctx,
+               const void *iv, uint32_t cc, void *data, size_t len);
+};
+
+/*
+ * Traditional, table-based AES implementation. It is fast, but uses
+ * internal tables (in particular a 1 kB table for encryption, another
+ * 1 kB table for decryption, and a 256-byte table for key schedule),
+ * and it is not constant-time. In contexts where cache-timing attacks
+ * apply, this implementation may leak the secret key.
+ */
+#define br_aes_big_BLOCK_SIZE   16
+typedef struct {
+       const br_block_cbcenc_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_big_cbcenc_keys;
+typedef struct {
+       const br_block_cbcdec_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_big_cbcdec_keys;
+typedef struct {
+       const br_block_ctr_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_big_ctr_keys;
+extern const br_block_cbcenc_class br_aes_big_cbcenc_vtable;
+extern const br_block_cbcdec_class br_aes_big_cbcdec_vtable;
+extern const br_block_ctr_class br_aes_big_ctr_vtable;
+void br_aes_big_cbcenc_init(br_aes_big_cbcenc_keys *ctx,
+       const void *key, size_t len);
+void br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx,
+       const void *key, size_t len);
+void br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx,
+       const void *key, size_t len);
+void br_aes_big_cbcenc_run(const br_aes_big_cbcenc_keys *ctx, void *iv,
+       void *data, size_t len);
+void br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx, void *iv,
+       void *data, size_t len);
+uint32_t br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len);
+
+/*
+ * AES implementation optimized for size. It is slower than the
+ * traditional table-based AES implementation, but requires much less
+ * code. It still uses data-dependent table accesses (albeit within a
+ * much smaller 256-byte table), which makes it conceptually vulnerable
+ * to cache-timing attacks.
+ */
+#define br_aes_small_BLOCK_SIZE   16
+typedef struct {
+       const br_block_cbcenc_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_small_cbcenc_keys;
+typedef struct {
+       const br_block_cbcdec_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_small_cbcdec_keys;
+typedef struct {
+       const br_block_ctr_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_small_ctr_keys;
+extern const br_block_cbcenc_class br_aes_small_cbcenc_vtable;
+extern const br_block_cbcdec_class br_aes_small_cbcdec_vtable;
+extern const br_block_ctr_class br_aes_small_ctr_vtable;
+void br_aes_small_cbcenc_init(br_aes_small_cbcenc_keys *ctx,
+       const void *key, size_t len);
+void br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx,
+       const void *key, size_t len);
+void br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx,
+       const void *key, size_t len);
+void br_aes_small_cbcenc_run(const br_aes_small_cbcenc_keys *ctx, void *iv,
+       void *data, size_t len);
+void br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx, void *iv,
+       void *data, size_t len);
+uint32_t br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len);
+
+/*
+ * Constant-time AES implementation. Its size is similar to that of
+ * 'aes_big', and its performance is similar to that of 'aes_small' (faster
+ * decryption, slower encryption). However, it is constant-time, i.e.
+ * immune to cache-timing and similar attacks.
+ */
+#define br_aes_ct_BLOCK_SIZE   16
+typedef struct {
+       const br_block_cbcenc_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_ct_cbcenc_keys;
+typedef struct {
+       const br_block_cbcdec_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_ct_cbcdec_keys;
+typedef struct {
+       const br_block_ctr_class *vtable;
+       uint32_t skey[60];
+       unsigned num_rounds;
+} br_aes_ct_ctr_keys;
+extern const br_block_cbcenc_class br_aes_ct_cbcenc_vtable;
+extern const br_block_cbcdec_class br_aes_ct_cbcdec_vtable;
+extern const br_block_ctr_class br_aes_ct_ctr_vtable;
+void br_aes_ct_cbcenc_init(br_aes_ct_cbcenc_keys *ctx,
+       const void *key, size_t len);
+void br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx,
+       const void *key, size_t len);
+void br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx,
+       const void *key, size_t len);
+void br_aes_ct_cbcenc_run(const br_aes_ct_cbcenc_keys *ctx, void *iv,
+       void *data, size_t len);
+void br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx, void *iv,
+       void *data, size_t len);
+uint32_t br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len);
+
+/*
+ * 64-bit constant-time AES implementation. It is similar to 'aes_ct'
+ * but uses 64-bit registers, making it about twice faster than 'aes_ct'
+ * on 64-bit platforms, while remaining constant-time and with a similar
+ * code size. (The doubling in performance is only for CBC decryption
+ * and CTR mode; CBC encryption is non-parallel and cannot benefit from
+ * the larger registers.)
+ */
+#define br_aes_ct64_BLOCK_SIZE   16
+typedef struct {
+       const br_block_cbcenc_class *vtable;
+       uint64_t skey[30];
+       unsigned num_rounds;
+} br_aes_ct64_cbcenc_keys;
+typedef struct {
+       const br_block_cbcdec_class *vtable;
+       uint64_t skey[30];
+       unsigned num_rounds;
+} br_aes_ct64_cbcdec_keys;
+typedef struct {
+       const br_block_ctr_class *vtable;
+       uint64_t skey[30];
+       unsigned num_rounds;
+} br_aes_ct64_ctr_keys;
+extern const br_block_cbcenc_class br_aes_ct64_cbcenc_vtable;
+extern const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable;
+extern const br_block_ctr_class br_aes_ct64_ctr_vtable;
+void br_aes_ct64_cbcenc_init(br_aes_ct64_cbcenc_keys *ctx,
+       const void *key, size_t len);
+void br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx,
+       const void *key, size_t len);
+void br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx,
+       const void *key, size_t len);
+void br_aes_ct64_cbcenc_run(const br_aes_ct64_cbcenc_keys *ctx, void *iv,
+       void *data, size_t len);
+void br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx, void *iv,
+       void *data, size_t len);
+uint32_t br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len);
+
+/*
+ * These structures are large enough to accommodate subkeys for all
+ * AES implementations.
+ */
+typedef union {
+       const br_block_cbcenc_class *vtable;
+       br_aes_big_cbcenc_keys big;
+       br_aes_small_cbcenc_keys small;
+       br_aes_ct_cbcenc_keys ct;
+       br_aes_ct64_cbcenc_keys ct64;
+} br_aes_gen_cbcenc_keys;
+typedef union {
+       const br_block_cbcdec_class *vtable;
+       br_aes_big_cbcdec_keys big;
+       br_aes_small_cbcdec_keys small;
+       br_aes_ct_cbcdec_keys ct;
+       br_aes_ct64_cbcdec_keys ct64;
+} br_aes_gen_cbcdec_keys;
+typedef union {
+       const br_block_ctr_class *vtable;
+       br_aes_big_ctr_keys big;
+       br_aes_small_ctr_keys small;
+       br_aes_ct_ctr_keys ct;
+       br_aes_ct64_ctr_keys ct64;
+} br_aes_gen_ctr_keys;
+
+/*
+ * Traditional, table-based implementation for DES/3DES. Since tables are
+ * used, cache-timing attacks are conceptually possible.
+ */
+#define br_des_tab_BLOCK_SIZE   8
+typedef struct {
+       const br_block_cbcenc_class *vtable;
+       uint32_t skey[96];
+       unsigned num_rounds;
+} br_des_tab_cbcenc_keys;
+typedef struct {
+       const br_block_cbcdec_class *vtable;
+       uint32_t skey[96];
+       unsigned num_rounds;
+} br_des_tab_cbcdec_keys;
+extern const br_block_cbcenc_class br_des_tab_cbcenc_vtable;
+extern const br_block_cbcdec_class br_des_tab_cbcdec_vtable;
+void br_des_tab_cbcenc_init(br_des_tab_cbcenc_keys *ctx,
+       const void *key, size_t len);
+void br_des_tab_cbcdec_init(br_des_tab_cbcdec_keys *ctx,
+       const void *key, size_t len);
+void br_des_tab_cbcenc_run(const br_des_tab_cbcenc_keys *ctx, void *iv,
+       void *data, size_t len);
+void br_des_tab_cbcdec_run(const br_des_tab_cbcdec_keys *ctx, void *iv,
+       void *data, size_t len);
+
+/*
+ * Constant-time implementation for DES/3DES. It is substantially slower
+ * (by a factor of about 4x), but also immune to cache-timing attacks.
+ */
+#define br_des_ct_BLOCK_SIZE   8
+typedef struct {
+       const br_block_cbcenc_class *vtable;
+       uint32_t skey[96];
+       unsigned num_rounds;
+} br_des_ct_cbcenc_keys;
+typedef struct {
+       const br_block_cbcdec_class *vtable;
+       uint32_t skey[96];
+       unsigned num_rounds;
+} br_des_ct_cbcdec_keys;
+extern const br_block_cbcenc_class br_des_ct_cbcenc_vtable;
+extern const br_block_cbcdec_class br_des_ct_cbcdec_vtable;
+void br_des_ct_cbcenc_init(br_des_ct_cbcenc_keys *ctx,
+       const void *key, size_t len);
+void br_des_ct_cbcdec_init(br_des_ct_cbcdec_keys *ctx,
+       const void *key, size_t len);
+void br_des_ct_cbcenc_run(const br_des_ct_cbcenc_keys *ctx, void *iv,
+       void *data, size_t len);
+void br_des_ct_cbcdec_run(const br_des_ct_cbcdec_keys *ctx, void *iv,
+       void *data, size_t len);
+
+/*
+ * These structures are large enough to accommodate subkeys for all
+ * DES/3DES implementations.
+ */
+typedef union {
+       const br_block_cbcenc_class *vtable;
+       br_des_tab_cbcenc_keys tab;
+       br_des_ct_cbcenc_keys ct;
+} br_des_gen_cbcenc_keys;
+typedef union {
+       const br_block_cbcdec_class *vtable;
+       br_des_tab_cbcdec_keys tab;
+       br_des_ct_cbcdec_keys ct;
+} br_des_gen_cbcdec_keys;
+
+#endif
diff --git a/inc/bearssl_ec.h b/inc/bearssl_ec.h
new file mode 100644 (file)
index 0000000..a655418
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_EC_H__
+#define BR_BEARSSL_EC_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Elliptic Curves
+ * ---------------
+ *
+ * ECDSA signatures have two standard formats, called "raw" and "asn1".
+ * Internally, such a signature is a pair of modular integers (r,s).
+ * The "raw" format is the concatenation of the unsigned big-endian
+ * encodings of these two integers, possibly left-padded with zeros so
+ * that they have the same encoded length. The "asn1" format is the
+ * DER encoding of an ASN.1 structure that contains the two integer
+ * values:
+ *
+ *   ECDSASignature ::= SEQUENCE {
+ *       r   INTEGER,
+ *       s   INTEGER
+ *   }
+ *
+ * Low-level implementations defined here work on the "raw" format.
+ * Conversion functions are provided.
+ *
+ * Note that for a given signature, the "raw" format is not fully
+ * deterministic, in that it does not enforce a minimal common length.
+ * The functions below MUST ensure, when producing signatures, that
+ * the signature length never exceeds 2*qlen, where qlen is the length,
+ * in bytes, of the minimal unsigned big-endian encoding of the curve
+ * subgroup order.
+ *
+ * Conversion of a "raw" format signature into "asn1" may enlarge a
+ * signature by no more than 9 bytes for all supported curves.
+ */
+
+/*
+ * Standard curve ID. These ID are equal to the assigned numerical
+ * identifiers assigned to these curves for TLS:
+ *    http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
+ */
+#define BR_EC_sect163k1           1
+#define BR_EC_sect163r1           2
+#define BR_EC_sect163r2           3
+#define BR_EC_sect193r1           4
+#define BR_EC_sect193r2           5
+#define BR_EC_sect233k1           6
+#define BR_EC_sect233r1           7
+#define BR_EC_sect239k1           8
+#define BR_EC_sect283k1           9
+#define BR_EC_sect283r1          10
+#define BR_EC_sect409k1          11
+#define BR_EC_sect409r1          12
+#define BR_EC_sect571k1          13
+#define BR_EC_sect571r1          14
+#define BR_EC_secp160k1          15
+#define BR_EC_secp160r1          16
+#define BR_EC_secp160r2          17
+#define BR_EC_secp192k1          18
+#define BR_EC_secp192r1          19
+#define BR_EC_secp224k1          20
+#define BR_EC_secp224r1          21
+#define BR_EC_secp256k1          22
+#define BR_EC_secp256r1          23
+#define BR_EC_secp384r1          24
+#define BR_EC_secp521r1          25
+#define BR_EC_brainpoolP256r1    26
+#define BR_EC_brainpoolP384r1    27
+#define BR_EC_brainpoolP512r1    28
+
+/*
+ * Structure for an EC public key.
+ */
+typedef struct {
+       int curve;
+       unsigned char *q;
+       size_t qlen;
+} br_ec_public_key;
+
+/*
+ * Structure for an EC private key.
+ */
+typedef struct {
+       int curve;
+       unsigned char *x;
+       size_t xlen;
+} br_ec_private_key;
+
+/*
+ * Type for an EC implementation.
+ *
+ *  supported_curves
+ *     Bit mask for supported curves: if curve 'id' is supported, then
+ *     bit '1 << id' is set.
+ *
+ *  generator
+ *     Get a pointer to the conventional generator for a given curve.
+ *
+ *  order
+ *     Get a pointer to the curve order (minimal unsigned big-endian
+ *     encoding).
+ *
+ *  mul
+ *     Compute x*G. Provided point G (encoded size Glen) must be valid and
+ *     distinct from the point at infinity. 'x' must be non-zero and less
+ *     than the curve order. On error, 0 is returned; an invalid G (or
+ *     point at infinity) is always detected, as well as a case of x = 0.
+ *     However, if x is a non-zero multiple of the curve order, then it is
+ *     not guaranteed that an error is reported.
+ *
+ *  muladd
+ *     compute x*A+y*B, result being written over A. Points and multipliers
+ *     must fulfill the same conditions as for mul().
+ */
+typedef struct {
+       uint32_t supported_curves;
+       const unsigned char *(*generator)(int curve, size_t *len);
+       const unsigned char *(*order)(int curve, size_t *len);
+       uint32_t (*mul)(unsigned char *G, size_t Glen,
+               const unsigned char *x, size_t xlen, int curve);
+       uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
+               const unsigned char *x, size_t xlen,
+               const unsigned char *y, size_t ylen, int curve);
+} br_ec_impl;
+
+/*
+ * The 'i31' implementation for elliptic curves. It supports secp256r1,
+ * secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521).
+ */
+extern const br_ec_impl br_ec_prime_i31;
+
+/*
+ * Convert a signature from "raw" to "asn1". Conversion is done "in
+ * place" and the new length is returned. Conversion may enlarge the
+ * signature, but by no more than 9 bytes at most. On error, 0 is
+ * returned (error conditions include an odd raw signature length, or an
+ * oversized integer).
+ */
+size_t br_ecdsa_raw_to_asn1(void *sig, size_t sig_len);
+
+/*
+ * Convert a signature from "asn1" to "raw". Conversion is done "in
+ * place" and the new length is returned. Conversion in that direction
+ * always reduced signature length. On error, 0 is returned (error
+ * conditions include an invalid signature format or an oversized
+ * integer).
+ */
+size_t br_ecdsa_asn1_to_raw(void *sig, size_t sig_len);
+
+/*
+ * Type for an ECDSA signer function. A pointer to the EC implementation
+ * is provided. The hash value is assumed to have the length inferred
+ * from the designated hash function class.
+ *
+ * Signature is written in the buffer pointed to by 'sig', and the length
+ * (in bytes) is returned. On error, nothing is written in the buffer,
+ * and 0 is returned.
+ *
+ * The signature format is either "raw" or "asn1", depending on the
+ * implementation; maximum length is predictable from the implemented
+ * curve:
+ *
+ *   curve        raw   asn1
+ *   NIST P-256    64     72
+ *   NIST P-384    96    104
+ *   NIST P-521   132    139
+ */
+typedef size_t (*br_ecdsa_sign)(const br_ec_impl *impl,
+       const br_hash_class *hf, const void *hash_value,
+       const br_ec_private_key *sk, void *sig);
+
+/*
+ * Verify ECDSA signature. Returned value is 1 on success, 0 on error.
+ */
+typedef uint32_t (*br_ecdsa_vrfy)(const br_ec_impl *impl,
+       const void *hash, size_t hash_len,
+       const br_ec_public_key *pk, const void *sig, size_t sig_len);
+
+/*
+ * ECDSA implementation using the "i31" integers.
+ */
+size_t br_ecdsa_i31_sign_asn1(const br_ec_impl *impl,
+       const br_hash_class *hf, const void *hash_value,
+       const br_ec_private_key *sk, void *sig);
+size_t br_ecdsa_i31_sign_raw(const br_ec_impl *impl,
+       const br_hash_class *hf, const void *hash_value,
+       const br_ec_private_key *sk, void *sig);
+uint32_t br_ecdsa_i31_vrfy_asn1(const br_ec_impl *impl,
+       const void *hash, size_t hash_len,
+       const br_ec_public_key *pk, const void *sig, size_t sig_len);
+uint32_t br_ecdsa_i31_vrfy_raw(const br_ec_impl *impl,
+       const void *hash, size_t hash_len,
+       const br_ec_public_key *pk, const void *sig, size_t sig_len);
+
+#endif
diff --git a/inc/bearssl_hash.h b/inc/bearssl_hash.h
new file mode 100644 (file)
index 0000000..1684a91
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_HASH_H__
+#define BR_BEARSSL_HASH_H__
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+/*
+ * Hash Functions
+ * --------------
+ *
+ * For hash function 'xxx', the following elements are defined:
+ *
+ * br_xxx_vtable
+ *    An externally defined instance of br_hash_class.
+ *
+ * br_xxx_SIZE
+ *    A macro that evaluates to the output size (in bytes) of the
+ *    hash function.
+ *
+ * br_xxx_ID
+ *    A macro that evaluates to a symbolic identifier for the hash
+ *    function. Such identifiers are used with HMAC and signature
+ *    algorithm implementations.
+ *    NOTE: the numerical value of these identifiers MUST match the
+ *    constants for hash function identification in TLS 1.2 (see RFC
+ *    5246, section 7.4.1.4.1). These are values 1 to 6, for MD5,
+ *    SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512, respectively.
+ *
+ * br_xxx_context
+ *    Context for an ongoing computation. It is allocated by the
+ *    caller, and a pointer to it is passed to all functions. A
+ *    context contains no interior pointer, so it can be moved around
+ *    and cloned (with a simple memcpy() or equivalent) in order to
+ *    capture the function state at some point. Computations that use
+ *    distinct context structures are independent of each other. The
+ *    first field of br_xxx_context is always a pointer to the
+ *    br_xxx_vtable structure; br_xxx_init() sets that pointer.
+ *
+ * br_xxx_init(br_xxx_context *ctx)
+ *    Initialize the provided context. Previous contents of the structure
+ *    are ignored. This calls resets the context to the start of a new
+ *    hash computation.
+ *
+ * br_xxx_update(br_xxx_context *ctx, const void *data, size_t len)
+ *    Add some more bytes to the hash computation represented by the
+ *    provided context.
+ *
+ * br_xxx_out(const br_xxx_context *ctx, void *out)
+ *    Complete the hash computation and write the result in the provided
+ *    buffer. The output buffer MUST be large enough to accomodate the
+ *    result. The context is NOT modified by this operation, so this
+ *    function can be used to get a "partial hash" while still keeping
+ *    the possibility of adding more bytes to the input.
+ *
+ * br_xxx_state(const br_xxx_context *ctx, void *out)
+ *    Get a copy of the "current state" for the computation so far. For
+ *    MD functions (MD5, SHA-1, SHA-2 family), this is the running state
+ *    resulting from the processing of the last complete input block.
+ *    Returned value is the current input length (in bytes).
+ *
+ * br_xxx_set_state(br_xxx_context *ctx, const void *stb, uint64_t count)
+ *    Set the internal state to the provided values. The 'stb' and 'count'
+ *    values shall match that which was obtained from br_xxx_state(). This
+ *    restores the hash state only if the state values were at an
+ *    appropriate block boundary. This does NOT set the 'vtable' pointer
+ *    in the context.
+ *
+ * Context structures can be discarded without any explicit deallocation.
+ * Hash function implementations are purely software and don't reserve
+ * any resources outside of the context structure itself.
+ *
+ * Implemented hash functions are:
+ *
+ *   Function    Name      Output length   State length
+ *
+ *   MD5         md5       16              16
+ *   SHA-1       sha1      20              20
+ *   SHA-224     sha224    28              32
+ *   SHA-256     sha256    32              32
+ *   SHA-384     sha384    48              64
+ *   SHA-512     sha512    64              64
+ *   MD5+SHA-1   md5sha1   36              36
+ *
+ * (MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the
+ * same input; in the implementation, the internal data buffer is
+ * shared, thus making it more memory-efficient than separate MD5 and
+ * SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS
+ * 1.1.)
+ *
+ *
+ * An object-oriented API is also available: the first field of the
+ * context is a pointer to a br_hash_class structure, that has the
+ * following contents:
+ *
+ *   context_size   total size of the required context structure
+ *   desc           descriptor (see below)
+ *   init           context initialization or reset (function pointer)
+ *   update         process some more bytes (function pointer)
+ *   out            get hash output so far (function pointer)
+ *   state          get copy of internal state (function pointer)
+ *   set_state      reset the internal state (function pointer)
+ *
+ * The descriptor is a combination of the following elements:
+ *   bits 0 to 7     hash algorithm identifier
+ *   bits 8 to 14    hash output size (in bytes)
+ *   bits 15 to 22   hash internal state size (in bytes)
+ *   bits 23 to 26   log (base 2) of hash internal block size (in bytes)
+ *   bit 28          1 if using MD padding, 0 otherwise
+ *   bit 29          1 if MD padding uses a 128-bit bit length, 0 otherwise
+ *   bit 30          1 if MD padding is big-endian, 0 otherwise
+ *
+ * For function 'xxx', the br_xxx_init() function sets the first field
+ * to a pointer to the relevant br_hash_class instance (i.e.
+ * br_xxx_vtable).
+ *
+ * Users of this object-oriented API may make the following assumptions:
+ *   Hash output size is no more than 64 bytes.
+ *   Hash internal state size is no more than 64 bytes.
+ *   Internal block size is a power of two, no less than 2^4 and no more
+ *   than 2^8.
+ * For functions that do not have an internal block size that is a
+ * power of 2, the relevant element is 0.
+ */
+
+typedef struct br_hash_class_ br_hash_class;
+struct br_hash_class_ {
+       size_t context_size;
+       uint32_t desc;
+       void (*init)(const br_hash_class **ctx);
+       void (*update)(const br_hash_class **ctx, const void *data, size_t len);
+       void (*out)(const br_hash_class *const *ctx, void *dst);
+       uint64_t (*state)(const br_hash_class *const *ctx, void *dst);
+       void (*set_state)(const br_hash_class **ctx,
+               const void *stb, uint64_t count);
+};
+
+#define BR_HASHDESC_ID(id)           ((uint32_t)(id) << BR_HASHDESC_ID_OFF)
+#define BR_HASHDESC_ID_OFF           0
+#define BR_HASHDESC_ID_MASK          0xFF
+
+#define BR_HASHDESC_OUT(size)        ((uint32_t)(size) << BR_HASHDESC_OUT_OFF)
+#define BR_HASHDESC_OUT_OFF          8
+#define BR_HASHDESC_OUT_MASK         0x7F
+
+#define BR_HASHDESC_STATE(size)      ((uint32_t)(size) << BR_HASHDESC_STATE_OFF)
+#define BR_HASHDESC_STATE_OFF        15
+#define BR_HASHDESC_STATE_MASK       0xFF
+
+#define BR_HASHDESC_LBLEN(ls)        ((uint32_t)(ls) << BR_HASHDESC_LBLEN_OFF)
+#define BR_HASHDESC_LBLEN_OFF        23
+#define BR_HASHDESC_LBLEN_MASK       0x0F
+
+#define BR_HASHDESC_MD_PADDING       ((uint32_t)1 << 28)
+#define BR_HASHDESC_MD_PADDING_128   ((uint32_t)1 << 29)
+#define BR_HASHDESC_MD_PADDING_BE    ((uint32_t)1 << 30)
+
+/*
+ * Specific hash functions.
+ *
+ * Rules for contexts:
+ * -- No interior pointer.
+ * -- No pointer to external dynamically allocated resources.
+ * -- First field is called 'vtable' and is a pointer to a
+ *    const-qualified br_hash_class instance (pointer is set by init()).
+ * -- SHA-224 and SHA-256 contexts are identical.
+ * -- SHA-384 and SHA-512 contexts are identical.
+ *
+ * Thus, contexts can be moved and cloned to capture the hash function
+ * current state; and there is no need for any explicit "release" function.
+ */
+
+#define br_md5_ID     1
+#define br_md5_SIZE   16
+extern const br_hash_class br_md5_vtable;
+typedef struct {
+       const br_hash_class *vtable;
+       unsigned char buf[64];
+       uint64_t count;
+       uint32_t val[4];
+} br_md5_context;
+void br_md5_init(br_md5_context *ctx);
+void br_md5_update(br_md5_context *ctx, const void *data, size_t len);
+void br_md5_out(const br_md5_context *ctx, void *out);
+uint64_t br_md5_state(const br_md5_context *ctx, void *out);
+void br_md5_set_state(br_md5_context *ctx, const void *stb, uint64_t count);
+
+#define br_sha1_ID     2
+#define br_sha1_SIZE   20
+extern const br_hash_class br_sha1_vtable;
+typedef struct {
+       const br_hash_class *vtable;
+       unsigned char buf[64];
+       uint64_t count;
+       uint32_t val[5];
+} br_sha1_context;
+void br_sha1_init(br_sha1_context *ctx);
+void br_sha1_update(br_sha1_context *ctx, const void *data, size_t len);
+void br_sha1_out(const br_sha1_context *ctx, void *out);
+uint64_t br_sha1_state(const br_sha1_context *ctx, void *out);
+void br_sha1_set_state(br_sha1_context *ctx, const void *stb, uint64_t count);
+
+#define br_sha224_ID     3
+#define br_sha224_SIZE   28
+extern const br_hash_class br_sha224_vtable;
+typedef struct {
+       const br_hash_class *vtable;
+       unsigned char buf[64];
+       uint64_t count;
+       uint32_t val[8];
+} br_sha224_context;
+void br_sha224_init(br_sha224_context *ctx);
+void br_sha224_update(br_sha224_context *ctx, const void *data, size_t len);
+void br_sha224_out(const br_sha224_context *ctx, void *out);
+uint64_t br_sha224_state(const br_sha224_context *ctx, void *out);
+void br_sha224_set_state(br_sha224_context *ctx,
+       const void *stb, uint64_t count);
+
+#define br_sha256_ID     4
+#define br_sha256_SIZE   32
+extern const br_hash_class br_sha256_vtable;
+typedef br_sha224_context br_sha256_context;
+void br_sha256_init(br_sha256_context *ctx);
+#define br_sha256_update      br_sha224_update
+void br_sha256_out(const br_sha256_context *ctx, void *out);
+#define br_sha256_state       br_sha224_state
+#define br_sha256_set_state   br_sha224_set_state
+
+#define br_sha384_ID     5
+#define br_sha384_SIZE   48
+extern const br_hash_class br_sha384_vtable;
+typedef struct {
+       const br_hash_class *vtable;
+       unsigned char buf[128];
+       uint64_t count;
+       uint64_t val[8];
+} br_sha384_context;
+void br_sha384_init(br_sha384_context *ctx);
+void br_sha384_update(br_sha384_context *ctx, const void *data, size_t len);
+void br_sha384_out(const br_sha384_context *ctx, void *out);
+uint64_t br_sha384_state(const br_sha384_context *ctx, void *out);
+void br_sha384_set_state(br_sha384_context *ctx,
+       const void *stb, uint64_t count);
+
+#define br_sha512_ID     6
+#define br_sha512_SIZE   64
+extern const br_hash_class br_sha512_vtable;
+typedef br_sha384_context br_sha512_context;
+void br_sha512_init(br_sha512_context *ctx);
+#define br_sha512_update      br_sha384_update
+void br_sha512_out(const br_sha512_context *ctx, void *out);
+#define br_sha512_state       br_sha384_state
+#define br_sha512_set_state   br_sha384_set_state
+
+/*
+ * "md5sha1" is a special hash function that computes both MD5 and SHA-1
+ * on the same input, and produces a 36-byte output (MD5 and SHA-1
+ * concatenation, in that order). State size is also 36 bytes.
+ */
+#define br_md5sha1_ID     0
+#define br_md5sha1_SIZE   36
+extern const br_hash_class br_md5sha1_vtable;
+typedef struct {
+       const br_hash_class *vtable;
+       unsigned char buf[64];
+       uint64_t count;
+       uint32_t val_md5[4];
+       uint32_t val_sha1[5];
+} br_md5sha1_context;
+void br_md5sha1_init(br_md5sha1_context *ctx);
+void br_md5sha1_update(br_md5sha1_context *ctx, const void *data, size_t len);
+void br_md5sha1_out(const br_md5sha1_context *ctx, void *out);
+uint64_t br_md5sha1_state(const br_md5sha1_context *ctx, void *out);
+void br_md5sha1_set_state(br_md5sha1_context *ctx,
+       const void *stb, uint64_t count);
+
+/*
+ * The br_hash_compat_context type is a type which is large enough to
+ * serve as context for all standard hash functions defined above.
+ */
+typedef union {
+       const br_hash_class *vtable;
+       br_md5_context md5;
+       br_sha1_context sha1;
+       br_sha224_context sha224;
+       br_sha256_context sha256;
+       br_sha384_context sha384;
+       br_sha512_context sha512;
+} br_hash_compat_context;
+
+/*
+ * The multi-hasher is a construct that handles hashing of the same input
+ * data with several hash functions, with a single shared input buffer.
+ * It can handle MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512
+ * simultaneously, though which functions are activated depends on
+ * the set implementation pointers.
+ */
+
+typedef struct {
+       unsigned char buf[128];
+       uint64_t count;
+       uint32_t val_32[25];
+       uint64_t val_64[16];
+       const br_hash_class *impl[6];
+} br_multihash_context;
+
+/*
+ * Clear a complete multihash context. This should always be called once
+ * on a given context, before setting implementation pointers.
+ */
+void br_multihash_zero(br_multihash_context *ctx);
+
+/*
+ * Set a hash function implementation, identified by ID.
+ */
+static inline void
+br_multihash_setimpl(br_multihash_context *ctx,
+       int id, const br_hash_class *impl)
+{
+       /*
+        * This code relies on hash functions ID being values 1 to 6,
+        * in the MD5 to SHA-512 order.
+        */
+       ctx->impl[id - 1] = impl;
+}
+
+/*
+ * Get the configured hash implementation, identified by ID. This returns
+ * NULL for unsupported hash implementations. The hash identifier MUST
+ * be a valid one (from br_md5_ID to br_sha512_ID, inclusive).
+ */
+static inline const br_hash_class *
+br_multihash_getimpl(const br_multihash_context *ctx, int id)
+{
+       return ctx->impl[id - 1];
+}
+
+/*
+ * Reset a multihash context. The hash functions for which implementation
+ * pointers have been set are reset and initialized.
+ */
+void br_multihash_init(br_multihash_context *ctx);
+
+/*
+ * Input some bytes into the context.
+ */
+void br_multihash_update(br_multihash_context *ctx,
+       const void *data, size_t len);
+
+/*
+ * Get the hash of the bytes injected so far, with the specified hash
+ * function. The hash function is given by ID (e.g. br_md5_ID for MD5).
+ * The hash output is written on 'dst'. The hash length is returned (in
+ * bytes); if the specified hash function is not implemented by this
+ * context, then this function returns 0.
+ *
+ * Obtaining the hash output does not invalidate the current hashing
+ * operation, thus "partial hashes" can be obtained.
+ */
+size_t br_multihash_out(const br_multihash_context *ctx, int id, void *dst);
+
+/*
+ * Type for a GHASH implementation. GHASH is a sort of keyed hash meant
+ * to be used to implement GCM in combination with a block cipher (with
+ * 16-byte blocks).
+ *
+ * The y[] array has length 16 bytes and is used for input and output; in
+ * a complete GHASH run, it starts with an all-zero value. h[] is a 16-byte
+ * value that serves as key (it is derived from the encryption key in GCM,
+ * using the block cipher). The data length (len) is expressed in bytes.
+ *
+ * If the data length is not a multiple of 16, then the data is implicitly
+ * padded with zeros up to the next multiple of 16. Thus, when using GHASH
+ * in GCM, this method may be called twice, for the associated data and
+ * for the ciphertext, respectively; the zero-padding implements exactly
+ * the GCM rules.
+ */
+typedef void (*br_ghash)(void *y, const void *h, const void *data, size_t len);
+
+/*
+ * Implementation of GHASH using normal 32x32->64 multiplications. It is
+ * constant-time (if multiplications are constant-time).
+ */
+void br_ghash_ctmul(void *y, const void *h, const void *data, size_t len);
+
+/*
+ * Implementation of GHASH using normal 32x32->32 multiplications; this
+ * may be faster than br_ghash_ctmul() on platforms for which the inner
+ * multiplication opcode does not yield the upper 32 bits of the product.
+ * It is constant-time (if multiplications are constant-time).
+ */
+void br_ghash_ctmul32(void *y, const void *h, const void *data, size_t len);
+
+/*
+ * Implementation of GHASH using 64x64->64 multiplications. It is
+ * constant-time (if multiplications are constant-time).
+ */
+void br_ghash_ctmul64(void *y, const void *h, const void *data, size_t len);
+
+#endif
diff --git a/inc/bearssl_hmac.h b/inc/bearssl_hmac.h
new file mode 100644 (file)
index 0000000..d65653a
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_HMAC_H__
+#define BR_BEARSSL_HMAC_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "bearssl_hash.h"
+
+/*
+ * HMAC
+ * ----
+ *
+ * HMAC is initialized with a key and an underlying hash function; it
+ * then fills a "key context". That context contains the processed
+ * key.
+ *
+ * With the key context, a HMAC context can be initialized to process
+ * the input bytes and obtain the MAC output. The key context is not
+ * modified during that process, and can be reused.
+ *
+ * IMPORTANT: HMAC shall be used only with functions that have the
+ * following properties:
+ *   hash output size does not exceed 64 bytes
+ *   hash internal state size does not exceed 64 bytes
+ *   internal block length is a power of 2 between 16 and 256 bytes
+ */
+
+/*
+ * Key context.
+ */
+typedef struct {
+       const br_hash_class *dig_vtable;
+       unsigned char ksi[64], kso[64];
+} br_hmac_key_context;
+
+/*
+ * Initialize the key context with the provided key, using the hash function
+ * identified by digest_class.
+ */
+void br_hmac_key_init(br_hmac_key_context *kc,
+       const br_hash_class *digest_class, const void *key, size_t key_len);
+
+/*
+ * A helper structure that is big enough to accommodate all context
+ * structures for all hash functions for which HMAC is supported.
+ */
+typedef union {
+       const br_hash_class *vtable;
+       br_md5_context md5;
+       br_sha1_context sha1;
+       br_sha224_context sha224;
+       br_sha256_context sha256;
+       br_sha384_context sha384;
+       br_sha512_context sha512;
+} br_hmac_allhash_context;
+
+/*
+ * Context for an HMAC computation.
+ */
+typedef struct {
+       br_hmac_allhash_context dig;
+       unsigned char kso[64];
+       size_t out_len;
+} br_hmac_context;
+
+/*
+ * Initialize a HMAC context with a key context. The key context is
+ * unmodified. Relevant data from the key context is immediately copied;
+ * the key context can thus be independently reused, modified or released
+ * without impacting this HMAC computation.
+ *
+ * An explicit output length can be specified; the actual output length
+ * will be the minimum of that value and the natural HMAC output length.
+ * If out_len is 0, then the natural HMAC output length is selected.
+ */
+void br_hmac_init(br_hmac_context *ctx,
+       const br_hmac_key_context *kc, size_t out_len);
+
+/*
+ * Get the MAC output size. The context must have been initialized.
+ */
+#define br_hmac_size(ctx)   ((ctx)->out_len)
+
+/*
+ * Process some more bytes.
+ */
+void br_hmac_update(br_hmac_context *ctx, const void *data, size_t len);
+
+/*
+ * Compute the HMAC output. The destination buffer MUST be large enough
+ * to accomodate the result. The context is NOT modified; further bytes
+ * may be processed. Thus, "partial HMAC" values can be efficiently
+ * obtained.
+ *
+ * Returned value is the output length (in bytes).
+ */
+size_t br_hmac_out(const br_hmac_context *ctx, void *out);
+
+/*
+ * Compute the HMAC output in constant time. Some extra input bytes are
+ * processed, then the output is computed. The extra input consists in
+ * the 'len' bytes pointed to by 'data'. The 'len' parameter must lie
+ * between 'min_len' and 'max_len' (inclusive); max_len bytes are
+ * actually read from 'data'. Computing time (and memory access pattern)
+ * will not depend upon the data bytes or the value of 'len'.
+ *
+ * The output is written in the 'out' buffer, that MUST be large enough
+ * to receive it.
+ *
+ * The difference max_len-min_len MUST be less than 2^30.
+ *
+ * This function computes the output properly only if the underlying
+ * hash function uses MD padding (i.e. MD5, SHA-1, SHA-224, SHA-256,
+ * SHA-384 or SHA-512).
+ *
+ * The provided context is NOT modified.
+ *
+ * Returned value is the MAC length (in bytes).
+ */
+size_t br_hmac_outCT(const br_hmac_context *ctx,
+       const void *data, size_t len, size_t min_len, size_t max_len,
+       void *out);
+
+#endif
diff --git a/inc/bearssl_pem.h b/inc/bearssl_pem.h
new file mode 100644 (file)
index 0000000..a3872de
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_PEM_H__
+#define BR_BEARSSL_PEM_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Context for a PEM decoder.
+ */
+typedef struct {
+       /* CPU for the T0 virtual machine. */
+       struct {
+               uint32_t *dp;
+               uint32_t *rp;
+               const unsigned char *ip;
+       } cpu;
+       uint32_t dp_stack[32];
+       uint32_t rp_stack[32];
+       int err;
+
+       const unsigned char *hbuf;
+       size_t hlen;
+
+       void (*dest)(void *dest_ctx, const void *src, size_t len);
+       void *dest_ctx;
+
+       unsigned char event;
+       char name[128];
+       unsigned char buf[255];
+       size_t ptr;
+
+} br_pem_decoder_context;
+
+/*
+ * Initialise a PEM decoder structure.
+ */
+void br_pem_decoder_init(br_pem_decoder_context *ctx);
+
+/*
+ * Push some bytes into the decoder. Returned value is the number of
+ * bytes actually consumed; this may be less than the number of provided
+ * bytes if an event is produced. When an event is produced, it must
+ * be read (with br_pem_decoder_event()); until the event is read, this
+ * function will return 0.
+ */
+size_t br_pem_decoder_push(br_pem_decoder_context *ctx,
+       const void *data, size_t len);
+
+/*
+ * Set the receiver for decoded data. The provided function (with opaque
+ * context pointer) will be called with successive data chunks.
+ */
+static inline void
+br_pem_decoder_setdest(br_pem_decoder_context *ctx,
+       void (*dest)(void *dest_ctx, const void *src, size_t len),
+       void *dest_ctx)
+{
+       ctx->dest = dest;
+       ctx->dest_ctx = dest_ctx;
+}
+
+/*
+ * Get the last event. This is 0 if no event has been produced. Calling
+ * ths function clears the event and allows new source bytes to be
+ * processed.
+ */
+int br_pem_decoder_event(br_pem_decoder_context *ctx);
+
+/*
+ * This event is called when the start of a new object has been detected.
+ * The object name (normalised to uppercase) can be accessed with
+ * br_pem_decoder_name(). The caller MUST provide an appropriate receiver
+ * (with br_pem_decoder_setdest()) before sending new data bytes.
+ */
+#define BR_PEM_BEGIN_OBJ   1
+
+/*
+ * This event is called when the end of the current object is reached
+ * (normally).
+ */
+#define BR_PEM_END_OBJ     2
+
+/*
+ * This event is called when decoding fails while decoding an object.
+ * This formally closes the current object and brings the decoder back
+ * to the "out of any object" state. The offending line in the source
+ * is consumed.
+ */
+#define BR_PEM_ERROR       3
+
+/*
+ * Get the name of the encountered object. That name is normalised to
+ * uppercase (for ASCII characters).
+ */
+static inline const char *
+br_pem_decoder_name(br_pem_decoder_context *ctx)
+{
+       return ctx->name;
+}
+
+#endif
diff --git a/inc/bearssl_prf.h b/inc/bearssl_prf.h
new file mode 100644 (file)
index 0000000..779a75b
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_PRF_H__
+#define BR_BEARSSL_PRF_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * The TLS PRF
+ * -----------
+ *
+ * TLS 1.0 and 1.1 define a PRF that is based on both MD5 and SHA-1. This
+ * is implemented by the br_tls10_prf() function.
+ *
+ * TLS 1.2 redefines the PRF, using an explicit hash function. The
+ * br_tls12_sha256_prf() and br_tls12_sha384_prf() functions apply that
+ * PRF with, respectively, SHA-256 and SHA-384.
+ *
+ * The PRF always uses as input three parameters: a "secret" (some
+ * bytes), a "label" (ASCII string), and a "seed" (again some bytes).
+ * An arbitrary output length can be produced.
+ */
+
+void br_tls10_prf(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len);
+
+void br_tls12_sha256_prf(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len);
+
+void br_tls12_sha384_prf(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len);
+
+/*
+ * A convenient type name for a PRF implementation.
+ */
+typedef void (*br_tls_prf_impl)(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len);
+
+#endif
diff --git a/inc/bearssl_rand.h b/inc/bearssl_rand.h
new file mode 100644 (file)
index 0000000..0c3bc4d
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_RAND_H__
+#define BR_BEARSSL_RAND_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Pseudo-Random Generators
+ * ------------------------
+ *
+ * A PRNG is a state-based engine that outputs pseudo-random bytes on
+ * demand. It is initialized with an initial seed, and additional seed
+ * bytes can be added afterwards. Bytes produced depend on the seeds
+ * and also on the exact sequence of calls (including sizes requested
+ * for each call).
+ *
+ * An object-oriented API is defined, with rules similar to that of
+ * hash functions. The context structure for a PRNG must start with
+ * a pointer to the vtable. The vtable contains the following fields:
+ *
+ *  context_size   size of the context structure for this PRNG
+ *  init           initialize context with an initial seed
+ *  generate       produce some pseudo-random bytes
+ *  update         insert some additional seed
+ *
+ * Note that the init() method may accept additional parameters, provided
+ * as a 'const void *' pointer at API level. These additional parameters
+ * depend on the implemented PRNG.
+ */
+
+typedef struct br_prng_class_ br_prng_class;
+struct br_prng_class_ {
+       size_t context_size;
+       void (*init)(const br_prng_class **ctx, const void *params,
+               const void *seed, size_t seed_len);
+       void (*generate)(const br_prng_class **ctx, void *out, size_t len);
+       void (*update)(const br_prng_class **ctx,
+               const void *seed, size_t seed_len);
+};
+
+/*
+ * HMAC_DRBG is a pseudo-random number generator based on HMAC (with
+ * an underlying hash function). HMAC_DRBG is specified in NIST Special
+ * Publication 800-90A. It works as a stateful machine:
+ * -- It has an internal state.
+ * -- The state can be updated with additional "entropy" (some bytes
+ *    provided from the outside).
+ * -- Each request is for some bits (up to some limit). For each request,
+ *    an internal "reseed counter" is incremented.
+ * -- When the reseed counter reaches a given threshold, a reseed is
+ *    necessary.
+ *
+ * Standard limits are quite high: each request can produce up to 2^19
+ * bits (i.e. 64 kB of data), and the threshold for the reseed counter
+ * is 2^48. In practice, we cannot really reach that reseed counter, so
+ * the implementation simply omits the counter. Similarly, we consider
+ * that it is up to callers NOT to ask for more than 64 kB of randomness
+ * in one go. Under these conditions, this implementation cannot fail,
+ * and thus functions need not return any status code.
+ *
+ * (Asking for more than 64 kB of data in one generate() call won't make
+ * the implementation fail, and, as far as we know, it will not induce
+ * any actual weakness; this is "merely" out of the formal usage range
+ * defined for HMAC_DRBG.)
+ *
+ * A dedicated context structure (caller allocated, as usual) contains
+ * the current PRNG state.
+ *
+ * For the OOP interface, the "additional parameters" are a pointer to
+ * the class of the hash function to use.
+ */
+
+typedef struct {
+       const br_prng_class *vtable;
+       unsigned char K[64];
+       unsigned char V[64];
+       const br_hash_class *digest_class;
+} br_hmac_drbg_context;
+
+extern const br_prng_class br_hmac_drbg_vtable;
+
+/*
+ * Initialize a HMAC_DRBG instance, with the provided initial seed (of
+ * 'len' bytes). The 'seed' used here is what is called, in SP 800-90A
+ * terminology, the concatenation of the "seed", "nonce" and
+ * "personalization string", in that order.
+ *
+ * Formally, the underlying digest can only be SHA-1 or one of the SHA-2
+ * functions. This implementation also works with any other implemented
+ * hash function (e.g. MD5), but such usage is non-standard and not
+ * recommended.
+ */
+void br_hmac_drbg_init(br_hmac_drbg_context *ctx,
+       const br_hash_class *digest_class, const void *seed, size_t len);
+
+/*
+ * Obtain some pseudorandom bits from HMAC_DRBG. The provided context
+ * is updated. The output bits are written in 'out' ('len' bytes). The
+ * size of the requested chunk of pseudorandom bits MUST NOT exceed
+ * 64 kB (the function won't fail if more bytes are requested, but
+ * the usage will be outside of the HMAC_DRBG specification limits).
+ */
+void br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len);
+
+/*
+ * Update an HMAC_DRBG instance with some new entropy. The extra 'seed'
+ * complements the current state but does not completely replace any
+ * previous seed. The process is such that pushing new entropy, even of
+ * questionable quality, will not make the output "less random" in any
+ * practical way.
+ */
+void br_hmac_drbg_update(br_hmac_drbg_context *ctx,
+       const void *seed, size_t len);
+
+/*
+ * Get the hash function implementation used by a given instance of
+ * HMAC_DRBG.
+ */
+static inline const br_hash_class *
+br_hmac_drbg_get_hash(const br_hmac_drbg_context *ctx)
+{
+       return ctx->digest_class;
+}
+
+#endif
diff --git a/inc/bearssl_rsa.h b/inc/bearssl_rsa.h
new file mode 100644 (file)
index 0000000..cbda5d3
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_RSA_H__
+#define BR_BEARSSL_RSA_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * RSA
+ * ---
+ *
+ * A RSA engine consists in two functions, for public-key and private-key
+ * operations (modular exponentiations). In both cases, the same buffer is
+ * used as source and destination.
+ *
+ * Key elements are provided as arrays of bytes, in big-endian unsigned
+ * encoding (leading zeros are correctly skipped, hence signed encodings
+ * can also be used). The source/destination array (x[]) is an array of
+ * bytes that, per PKCS#1 rules, MUST have the same length as the modulus,
+ * exactly: missing or extra leading bytes, even of value 0x00, are not
+ * tolerated for x[].
+ *
+ * Parameter validation: the engine MUST gracefully handle incorrect key
+ * parameters (e.g. an even modulus); it needs not detect all cases of
+ * incorrect key parameters. For public key operations, the engine MUST
+ * validate the length of x[] (it must match the numerical length, in
+ * bytes, of the modulus); it MUST also check that the provided x[]
+ * decodes to an integer that is numerically less than the modulus. For
+ * private key operation, the engine may assume that the length and
+ * contents of x[] are appropriate (it MUST NOT allow an invalid value
+ * to result in a buffer overflow, but an invalid input x[] may result
+ * in an undetected invalid output).
+ *
+ * Constant-time requirements: the following information may leak through
+ * execution time and memory access pattern:
+ * -- the actual bit length of the modulus;
+ * -- the actual bit length of each prime factor;
+ * -- the byte lengths as provided to the function calls.
+ */
+
+/*
+ * A structure type for a RSA public key, consisting in a modulus and
+ * a public exponent, encoded in unsigned big-endian format. The two
+ * arrays may be larger than needed; functions that accept a RSA public
+ * key are supposed to check the actual modulus length when needed.
+ */
+typedef struct {
+       unsigned char *n;
+       size_t nlen;
+       unsigned char *e;
+       size_t elen;
+} br_rsa_public_key;
+
+/*
+ * A structure type for a RSA private key. The key elements are:
+ *   n_bitlen   modulus bit length
+ *   p          prime modulus factor
+ *   q          other prime modulus factor (may be greater or lower than p)
+ *   dp         private exponent, reduced modulo p-1
+ *   dq         private exponent, reduced modulo q-1
+ *   iq         CRT coefficient: q*iq = 1 mod p.
+ */
+typedef struct {
+       uint32_t n_bitlen;
+       unsigned char *p;
+       size_t plen;
+       unsigned char *q;
+       size_t qlen;
+       unsigned char *dp;
+       size_t dplen;
+       unsigned char *dq;
+       size_t dqlen;
+       unsigned char *iq;
+       size_t iqlen;
+} br_rsa_private_key;
+
+/*
+ * Type for a public-key engine. The source buffer x[], of size xlen,
+ * is modified in place.
+ *
+ * Returned value is 1 on success, 0 on error.
+ *
+ * If the source buffer length (xlen) does not exactly match the modulus
+ * length, then an error is reported and x[] is unmodified.
+ */
+typedef uint32_t (*br_rsa_public)(unsigned char *x, size_t xlen,
+       const br_rsa_public_key *pk);
+
+/*
+ * Type for a RSA signature verification engine (PKCS#1 v1.5 signatures).
+ * Parameters are:
+ * -- The signature itself. The provided array is NOT modified.
+ * -- The encoded OID for the hash function. The provided array must begin
+ *    with a single byte that contains the length of the OID value (in
+ *    bytes), followed by exactly that many bytes.
+ *    This parameter may be NULL, in which case the raw hash value should
+ *    be used with the PKCS#1 v1.5 "type 1" padding (used in SSL/TLS up
+ *    to TLS-1.1, with a 36-byte hash value).
+ * -- The hash output length, in bytes.
+ * -- The public key.
+ * -- An output buffer for the hash value. The caller must still compare
+ *    it with the hash of the data over which the signature is computed.
+ *
+ * CONSTRAINTS:
+ * -- Hash length MUST be no more than 64 bytes.
+ * -- OID value length MUST be no more than 32 bytes (i.e. hash_oid[0]
+ *    must have a value in the 0..32 range, inclusive).
+ *
+ * This function verifies that the signature length (xlen) matches the
+ * modulus length (this function returns 0 on mismatch). If the modulus
+ * size exceeds the maximum supported RSA size, then the function also
+ * returns 0.
+ *
+ * Returned value is 1 on success, 0 on error.
+ *
+ * Implementations of this type need not be constant-time.
+ */
+typedef uint32_t (*br_rsa_pkcs1_vrfy)(const unsigned char *x, size_t xlen,
+       const unsigned char *hash_oid, size_t hash_len,
+       const br_rsa_public_key *pk, unsigned char *hash_out);
+
+/*
+ * Type for a private-key engine. The x[] buffer is modified in place, and
+ * its length is inferred from the modulus length (x[] is assumed to have
+ * a length of (sk->n_bitlen+7)/8 bytes).
+ *
+ * Returned value is 1 on success, 0 on error.
+ */
+typedef uint32_t (*br_rsa_private)(unsigned char *x,
+       const br_rsa_private_key *sk);
+
+/*
+ * Type for a RSA signature generation engine (PKCS#1 v1.5 signatures).
+ * Parameters are:
+ * -- The encoded OID for the hash function. The provided array must begin
+ *    with a single byte that contains the length of the OID value (in
+ *    bytes), followed by exactly that many bytes.
+ *    This parameter may be NULL, in which case the raw hash value should
+ *    be used with the PKCS#1 v1.5 "type 1" padding (used in SSL/TLS up
+ *    to TLS-1.1, with a 36-byte hash value).
+ * -- The hashed data, and length (in bytes).
+ * -- The private key.
+ * -- The output buffer.
+ *
+ * Returned value is 1 on success, 0 on error. Error conditions include
+ * a too small modulus for the provided hash OID and value, or some
+ * invalid key parameters. The signature length is exactly
+ * (sk->n_bitlen+7)/8 bytes.
+ *
+ * This function is expected to be constant-time with regards to the
+ * private key bytes (lengths of the modulus and the individual factors
+ * may leak, though) and to the hashed data.
+ */
+typedef uint32_t (*br_rsa_pkcs1_sign)(const unsigned char *hash_oid,
+       const unsigned char *hash, size_t hash_len,
+       const br_rsa_private_key *sk, unsigned char *x);
+
+/*
+ * RSA "i32" engine. Integers are internally represented as arrays of
+ * 32-bit integers, and the core multiplication primitive is the
+ * 32x32->64 multiplication.
+ */
+
+uint32_t br_rsa_i32_public(unsigned char *x, size_t xlen,
+       const br_rsa_public_key *pk);
+uint32_t br_rsa_i32_pkcs1_vrfy(const unsigned char *x, size_t xlen,
+       const unsigned char *hash_oid, size_t hash_len,
+       const br_rsa_public_key *pk, unsigned char *hash_out);
+uint32_t br_rsa_i32_private(unsigned char *x,
+       const br_rsa_private_key *sk);
+uint32_t br_rsa_i32_pkcs1_sign(const unsigned char *hash_oid,
+       const unsigned char *hash, size_t hash_len,
+       const br_rsa_private_key *sk, unsigned char *x);
+
+/*
+ * RSA "i31" engine. Similar to i32, but only 31 bits are used per 32-bit
+ * word. This uses slightly more stack space (about 4% more) and code
+ * space, but it quite faster.
+ */
+
+uint32_t br_rsa_i31_public(unsigned char *x, size_t xlen,
+       const br_rsa_public_key *pk);
+uint32_t br_rsa_i31_pkcs1_vrfy(const unsigned char *x, size_t xlen,
+       const unsigned char *hash_oid, size_t hash_len,
+       const br_rsa_public_key *pk, unsigned char *hash_out);
+uint32_t br_rsa_i31_private(unsigned char *x,
+       const br_rsa_private_key *sk);
+uint32_t br_rsa_i31_pkcs1_sign(const unsigned char *hash_oid,
+       const unsigned char *hash, size_t hash_len,
+       const br_rsa_private_key *sk, unsigned char *x);
+
+/*
+ * Perform RSA decryption for SSL/TLS. This function uses the provided core
+ * and private key to decrypt the message in data[] of size 'len'. The
+ * buffer is modified; the decryption result MUST have length 48, and
+ * is written into the first 48 bytes of data[].
+ *
+ * In success, this rturns 1. On error, 0 is returned, and the buffer
+ * contents are indeterminate.
+ */
+uint32_t br_rsa_ssl_decrypt(br_rsa_private core, const br_rsa_private_key *sk,
+       unsigned char *data, size_t len);
+
+#endif
diff --git a/inc/bearssl_ssl.h b/inc/bearssl_ssl.h
new file mode 100644 (file)
index 0000000..24d773c
--- /dev/null
@@ -0,0 +1,1856 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_SSL_H__
+#define BR_BEARSSL_SSL_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "bearssl_block.h"
+#include "bearssl_hash.h"
+#include "bearssl_hmac.h"
+#include "bearssl_prf.h"
+#include "bearssl_rand.h"
+#include "bearssl_x509.h"
+
+/*
+ * SSL
+ * ---
+ *
+ */
+
+/* Optimal input buffer size. */
+#define BR_SSL_BUFSIZE_INPUT    (16384 + 325)
+
+/* Optimal output buffer size. */
+#define BR_SSL_BUFSIZE_OUTPUT   (16384 + 85)
+
+/* Optimal buffer size for monodirectional engine
+   (shared input/output buffer). */
+#define BR_SSL_BUFSIZE_MONO     BR_SSL_BUFSIZE_INPUT
+
+/* Optimal buffer size for bidirectional engine
+   (single buffer split into two separate input/output buffers). */
+#define BR_SSL_BUFSIZE_BIDI     (BR_SSL_BUFSIZE_INPUT + BR_SSL_BUFSIZE_OUTPUT)
+
+/*
+ * Constants for known SSL/TLS protocol versions (SSL 3.0, TLS 1.0, TLS 1.1
+ * and TLS 1.2). Note that though there is a constant for SSL 3.0, that
+ * protocol version is not actually supported.
+ */
+#define BR_SSL30   0x0300
+#define BR_TLS10   0x0301
+#define BR_TLS11   0x0302
+#define BR_TLS12   0x0303
+
+/*
+ * Error constants. They are used to report the reason why a context has
+ * been marked as failed.
+ *
+ * Implementation note: SSL-level error codes should be in the 1..31
+ * range. The 32..63 range is for certificate decoding and validation
+ * errors. Received fatal alerts imply an error code in the 256..511 range.
+ */
+
+/* No error so far (0). */
+#define BR_ERR_OK                      0
+
+/* Caller-provided parameter is incorrect. */
+#define BR_ERR_BAD_PARAM               1
+
+/* Operation requested by the caller cannot be applied with the current
+   context state (e.g. reading data while outgoing data is waiting to
+   be sent). */
+#define BR_ERR_BAD_STATE               2
+
+/* Incoming protocol or record version is unsupported. */
+#define BR_ERR_UNSUPPORTED_VERSION     3
+
+/* Incoming record version does not match the expected version. */
+#define BR_ERR_BAD_VERSION             4
+
+/* Incoming record length is invalid. */
+#define BR_ERR_BAD_LENGTH              5
+
+/* Incoming record is too large to be processed, or buffer is too small
+   for the handshake message to send. */
+#define BR_ERR_TOO_LARGE               6
+
+/* Decryption found an invalid padding, or the record MAC is not correct. */
+#define BR_ERR_BAD_MAC                 7
+
+/* No initial entropy was provided, and none can be obtained from the OS. */
+#define BR_ERR_NO_RANDOM               8
+
+/* Incoming record type is unknown. */
+#define BR_ERR_UNKNOWN_TYPE            9
+
+/* Incoming record or message has wrong type with regards to the
+   current engine state. */
+#define BR_ERR_UNEXPECTED             10
+
+/* ChangeCipherSpec message from the peer has invalid contents. */
+#define BR_ERR_BAD_CCS                12
+
+/* Alert message from the peer has invalid contents (odd length). */
+#define BR_ERR_BAD_ALERT              13
+
+/* Incoming handshake message decoding failed. */
+#define BR_ERR_BAD_HANDSHAKE          14
+
+/* ServerHello contains a session ID which is larger than 32 bytes. */
+#define BR_ERR_OVERSIZED_ID           15
+
+/* Server wants to use a cipher suite that we did not claim to support.
+   This is also reported if we tried to advertise a cipher suite that
+   we do not support. */
+#define BR_ERR_BAD_CIPHER_SUITE       16
+
+/* Server wants to use a compression that we did not claim to support. */
+#define BR_ERR_BAD_COMPRESSION        17
+
+/* Server's max fragment length does not match client's. */
+#define BR_ERR_BAD_FRAGLEN            18
+
+/* Secure renegotiation failed. */
+#define BR_ERR_BAD_SECRENEG           19
+
+/* Server sent an extension type that we did not announce, or used the
+   same extension type several times in a single ServerHello. */
+#define BR_ERR_EXTRA_EXTENSION        20
+
+/* Invalid Server Name Indication contents (when used by the server,
+   this extension shall be empty). */
+#define BR_ERR_BAD_SNI                21
+
+/* Invalid ServerHelloDone from the server (length is not 0). */
+#define BR_ERR_BAD_HELLO_DONE         22
+
+/* Internal limit exceeded (e.g. server's public key is too large). */
+#define BR_ERR_LIMIT_EXCEEDED         23
+
+/* Finished message from peer does not match the expected value. */
+#define BR_ERR_BAD_FINISHED           24
+
+/* Session resumption attempt with distinct version or cipher suite. */
+#define BR_ERR_RESUME_MISMATCH        25
+
+/* Unsupported or invalid algorithm (ECDHE curve, signature algorithm,
+   hash function). */
+#define BR_ERR_INVALID_ALGORITHM      26
+
+/* Invalid signature on ServerKeyExchange message. */
+#define BR_ERR_BAD_SIGNATURE          27
+
+/* I/O error or premature close on underlying transport stream. This
+   error code is set only by the simplified I/O API ("br_sslio_*"). */
+#define BR_ERR_IO                     31
+
+/* When a fatal alert is received from the peer, the alert value is added
+   to this constant. */
+#define BR_ERR_RECV_FATAL_ALERT      256
+
+/* When a fatal alert is sent to the peer, the alert value is added
+   to this constant. */
+#define BR_ERR_SEND_FATAL_ALERT      512
+
+/* ===================================================================== */
+
+/*
+ * The decryption engine for incoming records is an object that implements
+ * the following functions:
+ *
+ *   check_length   test whether the provided record length is valid
+ *   decrypt        decrypt and verify the provided record
+ *
+ * The decrypt() function receives as parameters a pointer to its context
+ * structure, the record type, the record version, a pointer to the
+ * start of the record payload, and a pointer to a word containing the
+ * payload length. The decrypt() function may assume that the length is
+ * proper (check_length() was called and returned 1). On success, a
+ * pointer to the first plaintext byte is returned, and *len is adjusted
+ * to contain the plaintext length; on error, NULL is returned.
+ *
+ * The decryption engine is responsible for keeping track of the record
+ * sequence number.
+ */
+typedef struct br_sslrec_in_class_ br_sslrec_in_class;
+struct br_sslrec_in_class_ {
+       size_t context_size;
+       int (*check_length)(const br_sslrec_in_class *const *ctx,
+               size_t record_len);
+       unsigned char *(*decrypt)(const br_sslrec_in_class **ctx,
+               int record_type, unsigned version,
+               void *payload, size_t *len);
+};
+
+/*
+ * The encryption engine for outgoing records is an object that implements
+ * the following functions:
+ *
+ *   max_plaintext   get start and end offsets for payload
+ *   encrypt         encrypt and apply MAC on current record
+ *
+ * The max_plaintext() function receives as inputs the start and end
+ * of the buffer where the payload will be stored; this function assumes
+ * that there will be room for a record header (5 bytes) BEFORE the
+ * offset specified by *start. The max_plaintext() function then adjusts
+ * the two offsets to designate the area for the plaintext.
+ *
+ * The encrypt() function assumes that the provided plaintext data is
+ * in a buffer with enough room before and after the data chunk to
+ * receive the needed headers (i.e. the plaintext is at offsets which
+ * were computed by an earlier call to max_plaintext()). It returns
+ * a pointer to the start of the encrypted record, and writes the
+ * encrypted record length in '*len' (that length includes the record
+ * header).
+ *
+ * The encryption engine MUST fill the record header. If the engine
+ * performs a "split" into several records, then the successive records
+ * MUST be consecutive in RAM; the returned length is thus the sum of
+ * the individual record lengths.
+ *
+ * The encryption engine is responsible for keeping track of the record
+ * sequence number.
+ */
+typedef struct br_sslrec_out_class_ br_sslrec_out_class;
+struct br_sslrec_out_class_ {
+       size_t context_size;
+       void (*max_plaintext)(const br_sslrec_out_class *const *ctx,
+               size_t *start, size_t *end);
+       unsigned char *(*encrypt)(const br_sslrec_out_class **ctx,
+               int record_type, unsigned version,
+               void *plaintext, size_t *len);
+};
+
+/*
+ * An outgoing no-encryption engine is defined, to process outgoing
+ * records before completion of the initial handshake.
+ */
+typedef struct {
+       const br_sslrec_out_class *vtable;
+} br_sslrec_out_clear_context;
+extern const br_sslrec_out_class br_sslrec_out_clear_vtable;
+
+/* ===================================================================== */
+
+/*
+ * An engine for processing incoming records with a block cipher in
+ * CBC mode has an extra initialization function, that takes as inputs:
+ * -- a block cipher (CBC decryption) and its key;
+ * -- a hash function for HMAC, with the MAC key and output length;
+ * -- an optional initial IV.
+ * If the IV is not provided (the 'iv' parameter is NULL), then the
+ * engine will use an explicit per-record IV (as is mandated in TLS 1.1+).
+ *
+ * The initialization function is responsible for setting the 'vtable'
+ * field of the context.
+ */
+typedef struct br_sslrec_in_cbc_class_ br_sslrec_in_cbc_class;
+struct br_sslrec_in_cbc_class_ {
+       br_sslrec_in_class inner;
+       void (*init)(const br_sslrec_in_cbc_class **ctx,
+               const br_block_cbcdec_class *bc_impl,
+               const void *bc_key, size_t bc_key_len,
+               const br_hash_class *dig_impl,
+               const void *mac_key, size_t mac_key_len, size_t mac_out_len,
+               const void *iv);
+};
+
+/*
+ * An engine for processing outgoing records with a block cipher in
+ * CBC mode has an extra initialization function, that takes as inputs:
+ * -- a block cipher (CBC encryption) and its key;
+ * -- a hash function for HMAC, with the MAC key and output length;
+ * -- an optional initial IV.
+ * If the IV is not provided (the 'iv' parameter is NULL), then the
+ * engine will use an explicit per-record IV (as is mandated in TLS 1.1+).
+ *
+ * The initialization function is responsible for setting the 'vtable'
+ * field of the context.
+ */
+typedef struct br_sslrec_out_cbc_class_ br_sslrec_out_cbc_class;
+struct br_sslrec_out_cbc_class_ {
+       br_sslrec_out_class inner;
+       void (*init)(const br_sslrec_out_cbc_class **ctx,
+               const br_block_cbcenc_class *bc_impl,
+               const void *bc_key, size_t bc_key_len,
+               const br_hash_class *dig_impl,
+               const void *mac_key, size_t mac_key_len, size_t mac_out_len,
+               const void *iv);
+};
+
+/*
+ * Context structure for decrypting incoming records with CBC + HMAC.
+ */
+typedef struct {
+       const br_sslrec_in_cbc_class *vtable;
+       uint64_t seq;
+       union {
+               const br_block_cbcdec_class *vtable;
+               br_aes_gen_cbcdec_keys aes;
+               br_des_gen_cbcdec_keys des;
+       } bc;
+       br_hmac_key_context mac;
+       size_t mac_len;
+       unsigned char iv[16];
+       int explicit_IV;
+} br_sslrec_in_cbc_context;
+extern const br_sslrec_in_cbc_class br_sslrec_in_cbc_vtable;
+
+/*
+ * Context structure for encrypting outgoing records with CBC + HMAC.
+ */
+typedef struct {
+       const br_sslrec_out_cbc_class *vtable;
+       uint64_t seq;
+       union {
+               const br_block_cbcenc_class *vtable;
+               br_aes_gen_cbcenc_keys aes;
+               br_des_gen_cbcenc_keys des;
+       } bc;
+       br_hmac_key_context mac;
+       size_t mac_len;
+       unsigned char iv[16];
+       int explicit_IV;
+} br_sslrec_out_cbc_context;
+extern const br_sslrec_out_cbc_class br_sslrec_out_cbc_vtable;
+
+/* ===================================================================== */
+
+/*
+ * An engine for processing incoming records with a block cipher in
+ * GCM mode has an extra initialization function, that takes as inputs:
+ * -- a block cipher (CTR) and its key;
+ * -- a GHASH implementation;
+ * -- an initial IV (4 bytes).
+ *
+ * The initialization function is responsible for setting the 'vtable'
+ * field of the context.
+ */
+typedef struct br_sslrec_in_gcm_class_ br_sslrec_in_gcm_class;
+struct br_sslrec_in_gcm_class_ {
+       br_sslrec_in_class inner;
+       void (*init)(const br_sslrec_in_gcm_class **ctx,
+               const br_block_ctr_class *bc_impl,
+               const void *key, size_t key_len,
+               br_ghash gh_impl,
+               const void *iv);
+};
+
+/*
+ * An engine for processing outgoing records with a block cipher in
+ * GCM mode has an extra initialization function, that takes as inputs:
+ * -- a block cipher (CTR) and its key;
+ * -- a GHASH implementation;
+ * -- an initial IV (4 bytes).
+ *
+ * The initialization function is responsible for setting the 'vtable'
+ * field of the context.
+ */
+typedef struct br_sslrec_out_gcm_class_ br_sslrec_out_gcm_class;
+struct br_sslrec_out_gcm_class_ {
+       br_sslrec_out_class inner;
+       void (*init)(const br_sslrec_out_gcm_class **ctx,
+               const br_block_ctr_class *bc_impl,
+               const void *key, size_t key_len,
+               br_ghash gh_impl,
+               const void *iv);
+};
+
+/*
+ * We use the same context structure for incoming and outgoing records
+ * with GCM, because it allows internal code sharing.
+ */
+typedef struct {
+       union {
+               const void *gen;
+               const br_sslrec_in_gcm_class *in;
+               const br_sslrec_out_gcm_class *out;
+       } vtable;
+       uint64_t seq;
+       union {
+               const br_block_ctr_class *vtable;
+               br_aes_gen_ctr_keys aes;
+       } bc;
+       br_ghash gh;
+       unsigned char iv[4];
+       unsigned char h[16];
+} br_sslrec_gcm_context;
+
+extern const br_sslrec_in_gcm_class br_sslrec_in_gcm_vtable;
+extern const br_sslrec_out_gcm_class br_sslrec_out_gcm_vtable;
+
+/* ===================================================================== */
+
+/*
+ * Type for session parameters, to be saved for session resumption.
+ */
+typedef struct {
+       unsigned char session_id[32];
+       unsigned char session_id_len;
+       uint16_t version;
+       uint16_t cipher_suite;
+       unsigned char master_secret[48];
+} br_ssl_session_parameters;
+
+/*
+ * Maximum numnber of cipher suites supported by a client or server.
+ */
+#define BR_MAX_CIPHER_SUITES   40
+
+/*
+ * Context structure for SSL engine. This is common to the client and
+ * server; the engine manages records, including alerts, closures, and
+ * transitions to new encryption/MAC algorithms. Processing of handshake
+ * records is delegated to externally provided code. This structure
+ * should not be used directly, but is meant to be included as first
+ * field of the context structures for SSL clients and servers.
+ */
+typedef struct {
+
+       /*
+        * The error code. When non-zero, then the state is "failed" and
+        * no I/O may occur until reset.
+        */
+       int err;
+
+       /*
+        * Configured I/O buffers. They are either disjoint, or identical.
+        */
+       unsigned char *ibuf, *obuf;
+       size_t ibuf_len, obuf_len;
+
+       /*
+        * Maximum fragment length applies to outgoing records; incoming
+        * records can be processed as long as they fit in the input
+        * buffer. It is guaranteed that incoming records at least as big
+        * as max_frag_len can be processed.
+        */
+       uint16_t max_frag_len;
+       unsigned char log_max_frag_len;
+       unsigned char peer_log_max_frag_len;
+
+       /*
+        * Buffering management registers.
+        */
+       size_t ixa, ixb, ixc;
+       size_t oxa, oxb, oxc;
+       unsigned char iomode;
+       unsigned char incrypt;
+
+       /*
+        * Shutdown flag: when set to non-zero, incoming record bytes
+        * will not be accepted anymore. This is used after a close_notify
+        * has been received: afterwards, the engine no longer claims that
+        * it could receive bytes from the transport medium.
+        */
+       unsigned char shutdown_recv;
+
+       /*
+        * 'record_type_in' is set to the incoming record type when the
+        * record header has been received.
+        * 'record_type_out' is used to make the next outgoing record
+        * header when it is ready to go.
+        */
+       unsigned char record_type_in, record_type_out;
+
+       /*
+        * When a record is received, its version is extracted:
+        * -- if 'version_in' is 0, then it is set to the received version;
+        * -- otherwise, if the received version is not identical to
+        *    the 'version_in' contents, then a failure is reported.
+        *
+        * This implements the SSL requirement that all records shall
+        * use the negotiated protocol version, once decided (in the
+        * ServerHello). It is up to the handshake handler to adjust this
+        * field when necessary.
+        */
+       uint16_t version_in;
+
+       /*
+        * 'version_out' is used when the next outgoing record is ready
+        * to go.
+        */
+       uint16_t version_out;
+
+       /*
+        * Record handler contexts.
+        */
+       union {
+               const br_sslrec_in_class *vtable;
+               br_sslrec_in_cbc_context cbc;
+               br_sslrec_gcm_context gcm;
+       } in;
+       union {
+               const br_sslrec_out_class *vtable;
+               br_sslrec_out_clear_context clear;
+               br_sslrec_out_cbc_context cbc;
+               br_sslrec_gcm_context gcm;
+       } out;
+
+       /*
+        * The "application data" flag. It is set when application data
+        * can be exchanged, cleared otherwise.
+        */
+       unsigned char application_data;
+
+       /*
+        * Context RNG.
+        */
+       br_hmac_drbg_context rng;
+       int rng_init_done;
+       int rng_os_rand_done;
+
+       /*
+        * Supported minimum and maximum versions, and cipher suites.
+        */
+       uint16_t version_min;
+       uint16_t version_max;
+       uint16_t suites_buf[BR_MAX_CIPHER_SUITES];
+       unsigned char suites_num;
+
+       /*
+        * For clients, the server name to send as a SNI extension. For
+        * servers, the name received in the SNI extension (if any).
+        */
+       char server_name[256];
+
+       /*
+        * "Security parameters". These are filled by the handshake
+        * handler, and used when switching encryption state.
+        */
+       unsigned char client_random[32];
+       unsigned char server_random[32];
+       /* obsolete
+       unsigned char session_id[32];
+       unsigned char session_id_len;
+       uint16_t version;
+       uint16_t cipher_suite;
+       unsigned char master_secret[48];
+       */
+       br_ssl_session_parameters session;
+
+       /*
+        * ECDHE elements: curve and point from the peer. The server also
+        * uses that buffer for the point to send to the client.
+        */
+       unsigned char ecdhe_curve;
+       unsigned char ecdhe_point[133];
+       unsigned char ecdhe_point_len;
+
+       /*
+        * Secure renegotiation (RFC 5746): 'reneg' can be:
+        *   0   first handshake (server support is not known)
+        *   1   server does not support secure renegotiation
+        *   2   server supports secure renegotiation
+        *
+        * The saved_finished buffer contains the client and the
+        * server "Finished" values from the last handshake, in
+        * that order (12 bytes each).
+        */
+       unsigned char reneg;
+       unsigned char saved_finished[24];
+
+       /*
+        * Context variables for the handshake processor.
+        * The 'pad' must be large enough to accommodate an
+        * RSA-encrypted pre-master secret, or a RSA signature on
+        * key exchange parameters; since we want to support up to
+        * RSA-4096, this means at least 512 bytes.
+        * (Other pad usages require its length to be at least 256.)
+        */
+       struct {
+               uint32_t *dp;
+               uint32_t *rp;
+               const unsigned char *ip;
+       } cpu;
+       uint32_t dp_stack[32];
+       uint32_t rp_stack[32];
+       unsigned char pad[512];
+       unsigned char *hbuf_in, *hbuf_out, *saved_hbuf_out;
+       size_t hlen_in, hlen_out;
+       void (*hsrun)(void *ctx);
+
+       /*
+        * The 'action' value communicates OOB information between the
+        * engine and the handshake processor.
+        *
+        * From the engine:
+        *   0  invocation triggered by I/O
+        *   1  invocation triggered by explicit close
+        *   2  invocation triggered by explicit renegotiation
+        */
+       unsigned char action;
+
+       /*
+        * State for alert messages. Value is either 0, or the value of
+        * the alert level byte (level is either 1 for warning, or 2 for
+        * fatal; we convert all other values to 'fatal').
+        */
+       unsigned char alert;
+
+       /*
+        * Closure flags. This flag is set when a close_notify has been
+        * received from the peer.
+        */
+       unsigned char close_received;
+
+       /*
+        * Multi-hasher for the handshake messages. The handshake handler
+        * is responsible for resetting it when appropriate.
+        */
+       br_multihash_context mhash;
+
+       /*
+        * Pointer to the X.509 engine. The engine is supposed to be
+        * already initialized. It is used to validate the peer's
+        * certificate.
+        */
+       const br_x509_class **x509ctx;
+
+       /*
+        * Pointers to implementations; left to NULL for unsupported
+        * functions. For the raw hash functions, implementations are
+        * referenced from the multihasher (mhash field).
+        */
+       br_tls_prf_impl prf10;
+       br_tls_prf_impl prf_sha256;
+       br_tls_prf_impl prf_sha384;
+       const br_block_cbcenc_class *iaes_cbcenc;
+       const br_block_cbcdec_class *iaes_cbcdec;
+       const br_block_ctr_class *iaes_ctr;
+       const br_block_cbcenc_class *ides_cbcenc;
+       const br_block_cbcdec_class *ides_cbcdec;
+       br_ghash ighash;
+       const br_sslrec_in_cbc_class *icbc_in;
+       const br_sslrec_out_cbc_class *icbc_out;
+       const br_sslrec_in_gcm_class *igcm_in;
+       const br_sslrec_out_gcm_class *igcm_out;
+       const br_ec_impl *iec;
+
+} br_ssl_engine_context;
+
+/*
+ * Set the minimum and maximum supported protocol versions.
+ */
+static inline void
+br_ssl_engine_set_versions(br_ssl_engine_context *cc,
+       unsigned version_min, unsigned version_max)
+{
+       cc->version_min = version_min;
+       cc->version_max = version_max;
+}
+
+/*
+ * Set the list of cipher suites advertised by this context. The provided
+ * array is copied into the context. It is the caller responsibility
+ * to ensure that all provided suites will be supported by the context.
+ */
+void br_ssl_engine_set_suites(br_ssl_engine_context *cc,
+       const uint16_t *suites, size_t suites_num);
+
+/*
+ * Set the X.509 engine. The context should be already initialized and
+ * ready to process a new chain.
+ */
+static inline void
+br_ssl_engine_set_x509(br_ssl_engine_context *cc, const br_x509_class **x509ctx)
+{
+       cc->x509ctx = x509ctx;
+}
+
+/*
+ * Set a hash function implementation (by ID).
+ */
+static inline void
+br_ssl_engine_set_hash(br_ssl_engine_context *ctx,
+       int id, const br_hash_class *impl)
+{
+       br_multihash_setimpl(&ctx->mhash, id, impl);
+}
+
+/*
+ * Get a hash function implementation (by ID).
+ */
+static inline const br_hash_class *
+br_ssl_engine_get_hash(br_ssl_engine_context *ctx, int id)
+{
+       return br_multihash_getimpl(&ctx->mhash, id);
+}
+
+/*
+ * Set the PRF implementation (for TLS 1.0 and 1.1).
+ */
+static inline void
+br_ssl_engine_set_prf10(br_ssl_engine_context *cc, br_tls_prf_impl impl)
+{
+       cc->prf10 = impl;
+}
+
+/*
+ * Set the PRF implementation (for TLS 1.2, with SHA-256).
+ */
+static inline void
+br_ssl_engine_set_prf_sha256(br_ssl_engine_context *cc, br_tls_prf_impl impl)
+{
+       cc->prf_sha256 = impl;
+}
+
+/*
+ * Set the PRF implementation (for TLS 1.2, with SHA-384).
+ */
+static inline void
+br_ssl_engine_set_prf_sha384(br_ssl_engine_context *cc, br_tls_prf_impl impl)
+{
+       cc->prf_sha384 = impl;
+}
+
+/*
+ * Set the AES/CBC implementations.
+ */
+static inline void
+br_ssl_engine_set_aes_cbc(br_ssl_engine_context *cc,
+       const br_block_cbcenc_class *impl_enc,
+       const br_block_cbcdec_class *impl_dec)
+{
+       cc->iaes_cbcenc = impl_enc;
+       cc->iaes_cbcdec = impl_dec;
+}
+
+/*
+ * Set the AES/CTR implementation.
+ */
+static inline void
+br_ssl_engine_set_aes_ctr(br_ssl_engine_context *cc,
+       const br_block_ctr_class *impl)
+{
+       cc->iaes_ctr = impl;
+}
+
+/*
+ * Set the 3DES/CBC implementations.
+ */
+static inline void
+br_ssl_engine_set_des_cbc(br_ssl_engine_context *cc,
+       const br_block_cbcenc_class *impl_enc,
+       const br_block_cbcdec_class *impl_dec)
+{
+       cc->ides_cbcenc = impl_enc;
+       cc->ides_cbcdec = impl_dec;
+}
+
+/*
+ * Set the GHASH implementation (for GCM).
+ */
+static inline void
+br_ssl_engine_set_ghash(br_ssl_engine_context *cc, br_ghash impl)
+{
+       cc->ighash = impl;
+}
+
+/*
+ * Set the CBC+HMAC record processor implementations.
+ */
+static inline void
+br_ssl_engine_set_cbc(br_ssl_engine_context *cc,
+       const br_sslrec_in_cbc_class *impl_in,
+       const br_sslrec_out_cbc_class *impl_out)
+{
+       cc->icbc_in = impl_in;
+       cc->icbc_out = impl_out;
+}
+
+/*
+ * Set the GCM record processor implementations.
+ */
+static inline void
+br_ssl_engine_set_gcm(br_ssl_engine_context *cc,
+       const br_sslrec_in_gcm_class *impl_in,
+       const br_sslrec_out_gcm_class *impl_out)
+{
+       cc->igcm_in = impl_in;
+       cc->igcm_out = impl_out;
+}
+
+/*
+ * Set the ECC core operations implementation. The 'iec' parameter
+ * points to the core EC code used for both ECDHE and ECDSA.
+ */
+static inline void
+br_ssl_engine_set_ec(br_ssl_engine_context *cc, const br_ec_impl *iec)
+{
+       cc->iec = iec;
+}
+
+/*
+ * Set the I/O buffer for a SSL engine. Once this call has been made,
+ * br_ssl_client_reset() or br_ssl_server_reset() must be called before
+ * using the context.
+ *
+ * If 'bidi' is 1, then the buffer will be internally split to support
+ * concurrent input and output; otherwise, the caller will be responsible
+ * for reading all buffered incoming data before writing. The latter
+ * case makes support of HTTPS pipelining difficult, thus bidirectional
+ * buffering is recommended if the RAM can be spared.
+ *
+ * The BR_SSL_BUFSIZE_MONO and BR_SSL_BUFSIZE_BIDI macros yield optimal
+ * buffer sizes for the monodirectional and bidirectional cases,
+ * respectively. If using optimal sizes (or larger), then records with
+ * the maximum length supported by the TLS standard will be accepted
+ * and emitted.
+ */
+void br_ssl_engine_set_buffer(br_ssl_engine_context *cc,
+       void *iobuf, size_t iobuf_len, int bidi);
+
+/*
+ * Set the I/O buffers for a SSL engine. This call sets two buffers, for
+ * concurrent input and output. The two buffers MUST be disjoint. Once
+ * this call has been made, br_ssl_client_reset() or
+ * br_ssl_server_reset() must be called before using the context.
+ *
+ * The BR_SSL_BUFSIZE_INPUT and BR_SSL_BUFSIZE_OUTPUT macros evaluate to
+ * optimal sizes for the input and output buffers, respectively. If
+ * using optimal sizes (or larger), then records with the maximum length
+ * supported by the TLS standard will be accepted and emitted.
+ */
+void br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *cc,
+       void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len);
+
+/*
+ * Inject some "initial entropy" in the context. This entropy will be added
+ * to what can be obtained from the underlying operating system, if that
+ * OS is supported.
+ *
+ * This function may be called several times; all injected entropy chunks
+ * are cumulatively mixed.
+ *
+ * If entropy gathering from the OS is supported and compiled in, then this
+ * step is optional. Otherwise, it is mandatory to inject randomness, and
+ * the caller MUST take care to push (as one or several successive calls)
+ * enough entropy to achieve cryptographic resistance (at least 80 bits,
+ * preferably 128 or more). The engine will report an error if no entropy
+ * was provided and none can be obtained from the OS.
+ *
+ * Take care that this function cannot assess the cryptographic quality of
+ * the provided bytes.
+ *
+ * In all generality, "entropy" must here be considered to mean "that
+ * which the attacker cannot predict". If your OS/architecture does not
+ * have a suitable source of randomness, then you can make do with the
+ * combination of a large enough secret value (possibly a copy of an
+ * asymmetric private key that you also store on the system) AND a
+ * non-repeating value (e.g. current time, provided that the local clock
+ * cannot be reset or altered by the attacker).
+ */
+void br_ssl_engine_inject_entropy(br_ssl_engine_context *cc,
+       const void *data, size_t len);
+
+/*
+ * Get the "server name" in this engine. For clients, this is the name
+ * provided with br_ssl_client_reset(); for servers, this is the name
+ * received from the client as part of the ClientHello message. If there
+ * is no such name (e.g. the client did not send an SNI extension) then
+ * the returned string is empty (returned pointer points to a byte of
+ * value 0).
+ */
+static inline const char *
+br_ssl_engine_get_server_name(br_ssl_engine_context *cc)
+{
+       return cc->server_name;
+}
+
+/*
+ * An SSL engine (client or server) has, at any time, a state which is
+ * the combination of zero, one or more of these flags:
+ *
+ *   BR_SSL_CLOSED    engine is finished, no more I/O (until next reset)
+ *   BR_SSL_SENDREC   engine has some bytes to send to the peer
+ *   BR_SSL_RECVREC   engine expects some bytes from the peer
+ *   BR_SSL_SENDAPP   engine may receive application data to send (or flush)
+ *   BR_SSL_RECVAPP   engine has obtained some application data from the peer,
+ *                    that should be read by the caller
+ *
+ * If no flag at all is set (state value is 0), then the engine is not
+ * fully initialized yet.
+ *
+ * The BR_SSL_CLOSED flag is exclusive; when it is set, no other flag is set.
+ * To distinguish between a normal closure and an error, use
+ * br_ssl_engine_last_error().
+ *
+ * Generally speaking, BR_SSL_SENDREC and BR_SSL_SENDAPP are mutually
+ * exclusive: the input buffer, at any point, either accumulates
+ * plaintext data, or contains an assembled record that is being sent.
+ * Similarly, BR_SSL_RECVREC and BR_SSL_RECVAPP are mutually exclusive.
+ * This may change in a future library version.
+ */
+
+#define BR_SSL_CLOSED    0x0001
+#define BR_SSL_SENDREC   0x0002
+#define BR_SSL_RECVREC   0x0004
+#define BR_SSL_SENDAPP   0x0008
+#define BR_SSL_RECVAPP   0x0010
+
+/*
+ * Get the current engine state.
+ */
+unsigned br_ssl_engine_current_state(const br_ssl_engine_context *cc);
+
+/*
+ * Get the engine error indicator. This is BR_ERR_OK (0) if no error was
+ * encountered since the last call to br_ssl_client_reset() or
+ * br_ssl_server_reset(). Only these calls clear the error indicator.
+ */
+static inline int
+br_ssl_engine_last_error(const br_ssl_engine_context *cc)
+{
+       return cc->err;
+}
+
+/*
+ * There are four I/O operations, each identified by a symbolic name:
+ *
+ *   sendapp   inject application data in the engine
+ *   recvapp   retrieving application data from the engine
+ *   sendrec   sending records on the transport medium
+ *   recvrec   receiving records from the transport medium
+ *
+ * Terminology works thus: in a layered model where the SSL engine sits
+ * between the application and the network, "send" designates operations
+ * where bytes flow from application to network, and "recv" for the
+ * reverse operation. Application data (the plaintext that is to be
+ * conveyed through SSL) is "app", while encrypted records are "rec".
+ * Note that from the SSL engine point of view, "sendapp" and "recvrec"
+ * designate bytes that enter the engine ("inject" operation), while
+ * "recvapp" and "sendrec" designate bytes that exit the engine
+ * ("extract" operation).
+ *
+ * For the operation 'xxx', two functions are defined:
+ *
+ *   br_ssl_engine_xxx_buf
+ *      Returns a pointer and length to the buffer to use for that
+ *      operation. '*len' is set to the number of bytes that may be read
+ *      from the buffer (extract operation) or written to the buffer
+ *      (inject operation). If no byte may be exchanged for that operation
+ *      at that point, then '*len' is set to zero, and NULL is returned.
+ *      The engine state is unmodified by this call.
+ *
+ *   br_ssl_engine_xxx_ack
+ *      Informs the engine that 'len' bytes have been read from the buffer
+ *      (extract operation) or written to the buffer (inject operation).
+ *      The 'len' value MUST NOT be zero. The 'len' value MUST NOT exceed
+ *      that which was obtained from a preceeding br_ssl_engine_xxx_buf()
+ *      call.
+ */
+
+unsigned char *br_ssl_engine_sendapp_buf(
+       const br_ssl_engine_context *cc, size_t *len);
+void br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len);
+
+unsigned char *br_ssl_engine_recvapp_buf(
+       const br_ssl_engine_context *cc, size_t *len);
+void br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len);
+
+unsigned char *br_ssl_engine_sendrec_buf(
+       const br_ssl_engine_context *cc, size_t *len);
+void br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len);
+
+unsigned char *br_ssl_engine_recvrec_buf(
+       const br_ssl_engine_context *cc, size_t *len);
+void br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len);
+
+/*
+ * If some application data has been buffered in the engine, then wrap
+ * it into a record and mark it for sending. If no application data has
+ * been buffered but the engine would be ready to accept some, AND the
+ * 'force' parameter is non-zero, then an empty record is assembled and
+ * marked for sending. In all other cases, this function does nothing.
+ *
+ * Empty records are technically legal, but not all existing SSL/TLS
+ * implementations support them. Empty records can be useful as a
+ * transparent "keep-alive" mechanism to maintain some low-level
+ * network activity.
+ */
+void br_ssl_engine_flush(br_ssl_engine_context *cc, int force);
+
+/*
+ * Close the context. If, at that point, the context is open and in
+ * ready state, then a close_notify alert is assembled and marked for
+ * sending. Otherwise, no such alert is assembled.
+ */
+void br_ssl_engine_close(br_ssl_engine_context *cc);
+
+/*
+ * Initiate a renegotiation. If the engine is failed or closed, or if
+ * the peer is known not to support secure renegotiation (RFC 5746),
+ * then this function returns 0. Otherwise, this function returns 1 and
+ * a renegotiation attempt is triggered, unless a handshake is already
+ * taking place, in which case the call is ignored.
+ */
+int br_ssl_engine_renegotiate(br_ssl_engine_context *cc);
+
+/*
+ * Context structure for a SSL client.
+ */
+typedef struct {
+       /*
+        * The encapsulated engine context.
+        */
+       br_ssl_engine_context eng;
+
+       /*
+        * Implementations.
+        */
+       br_rsa_public irsapub;
+       br_rsa_pkcs1_vrfy irsavrfy;
+       br_ecdsa_vrfy iecdsa;
+
+} br_ssl_client_context;
+
+/*
+ * Each br_ssl_client_init_xxx() function sets the list of supported
+ * cipher suites and used implementations, as specified by the profile
+ * name 'xxx'. Defined profile names are:
+ *
+ *    full    all supported versions and suites; constant-time implementations
+ *    FIXME: add other profiles
+ */
+
+void br_ssl_client_init_full(br_ssl_client_context *cc,
+       br_x509_minimal_context *xc,
+       const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
+
+/*
+ * Clear the complete contents of a SSL client context, including the
+ * reference to the configured buffer, implementations, cipher suites
+ * and state.
+ */
+void br_ssl_client_zero(br_ssl_client_context *cc);
+
+/*
+ * Set the RSA public-key operations implementation. This will be used
+ * to encrypt the pre-master secret with the server's RSA public key
+ * (RSA-encryption cipher suites only).
+ */
+static inline void
+br_ssl_client_set_rsapub(br_ssl_client_context *cc, br_rsa_public irsapub)
+{
+       cc->irsapub = irsapub;
+}
+
+/*
+ * Set the RSA signature verification implementation. This will be used
+ * to verify the server's signature on its ServerKeyExchange message
+ * (ECDHE_RSA cipher suites only).
+ */
+static inline void
+br_ssl_client_set_rsavrfy(br_ssl_client_context *cc, br_rsa_pkcs1_vrfy irsavrfy)
+{
+       cc->irsavrfy = irsavrfy;
+}
+
+/*
+ * Set the ECDSA implementation (signature verification). The ECC core
+ * implementation must also have been set.
+ */
+static inline void
+br_ssl_client_set_ecdsa(br_ssl_client_context *cc, br_ecdsa_vrfy iecdsa)
+{
+       cc->iecdsa = iecdsa;
+}
+
+/*
+ * Prepare or reset a client context for connecting with a server of
+ * name 'server_name'. The 'server_name' parameter is used to fill the
+ * SNI extension; if the parameter is NULL then no SNI extension will
+ * be sent.
+ *
+ * If 'resume_session' is non-zero and the context was previously used
+ * then the session parameters may be reused (depending on whether the
+ * server previously sent a non-empty session ID, and accepts the session
+ * resumption).
+ *
+ * On failure, the context is marked as failed, and this function
+ * returns 0. A possible failure condition is when no initial entropy
+ * was injected, and none could be obtained from the OS (either OS
+ * randomness gathering is not supported, or it failed).
+ */
+int br_ssl_client_reset(br_ssl_client_context *cc,
+       const char *server_name, int resume_session);
+
+/*
+ * Forget any session in the context. This means that the next handshake
+ * that uses this context will necessarily be a full handshake (this
+ * applies both to new connections and to renegotiations).
+ */
+static inline void
+br_ssl_client_forget_session(br_ssl_client_context *cc)
+{
+       cc->eng.session.session_id_len = 0;
+}
+
+/*
+ * Type for a "translated cipher suite", as an array of 16-bit integers:
+ * first element is the cipher suite identifier (as used on the wire),
+ * and the second element is the concatenation of four 4-bit elements which
+ * characterise the cipher suite contents. In most to least significant
+ * order, these 4-bit elements are:
+ *
+ *   Bits 12 to 15: key exchange + server key type
+ *      0   RSA            RSA key exchange, key is RSA (encryption)
+ *      1   ECDHE-RSA      ECDHE key exchange, key is RSA (signature)
+ *      2   ECDHE-ECDSA    ECDHE key exchange, key is EC (signature)
+ *      3   ECDH-RSA       Key is EC (key exchange), cert is signed with RSA
+ *      4   ECDH-ECDSA     Key is EC (key exchange), cert is signed with ECDSA
+ *
+ *   Bits 8 to 11: symmetric encryption algorithm
+ *      0   3DES/CBC
+ *      1   AES-128/CBC
+ *      2   AES-256/CBC
+ *      3   AES-128/GCM
+ *      4   AES-256/GCM
+ *      5   ChaCha20/Poly1305
+ *
+ *   Bits 4 to 7: MAC algorithm
+ *      0   AEAD           No dedicated MAC because encryption is AEAD
+ *      2   HMAC/SHA-1     Value matches br_sha1_ID
+ *      4   HMAC/SHA-256   Value matches br_sha256_ID
+ *      5   HMAC/SHA-384   Value matches br_sha384_ID
+ *
+ *   Bits 0 to 3: hash function for PRF when used with TLS-1.2
+ *      4   SHA-256        Value matches br_sha256_ID
+ *      5   SHA-384        Value matches br_sha384_ID
+ */
+typedef uint16_t br_suite_translated[2];
+
+#define BR_SSLKEYX_RSA           0
+#define BR_SSLKEYX_ECDHE_RSA     1
+#define BR_SSLKEYX_ECDHE_ECDSA   2
+#define BR_SSLKEYX_ECDH_RSA      3
+#define BR_SSLKEYX_ECDH_ECDSA    4
+
+#define BR_SSLENC_3DES_CBC       0
+#define BR_SSLENC_AES128_CBC     1
+#define BR_SSLENC_AES256_CBC     2
+#define BR_SSLENC_AES128_GCM     3
+#define BR_SSLENC_AES256_GCM     4
+#define BR_SSLENC_CHACHA20       5
+
+#define BR_SSLMAC_AEAD           0
+#define BR_SSLMAC_SHA1           br_sha1_ID
+#define BR_SSLMAC_SHA256         br_sha256_ID
+#define BR_SSLMAC_SHA384         br_sha384_ID
+
+#define BR_SSLPRF_SHA256         br_sha256_ID
+#define BR_SSLPRF_SHA384         br_sha384_ID
+
+/*
+ * Pre-declaration for the SSL server context.
+ */
+typedef struct br_ssl_server_context_ br_ssl_server_context;
+
+/*
+ * Type for the server policy choices, taken after analysis of the client
+ * message:
+ *
+ *     cipher_suite   Cipher suite to use.
+ *
+ *     hash_id        Signature hash function identifier (hash function
+ *                    to use for signing the ServerKeyExchange, when the
+ *                    suite uses ECDHE).
+ *
+ *     chain          The certificate chain to send (number of certificates
+ *     chain_len      is in chain_length). The certificates are send "as is"
+ *                    and shall be in standard SSL/TLS order (i.e. end-entity
+ *                    first, each subsequent certificate signs the previous).
+ */
+typedef struct {
+       uint16_t cipher_suite;
+       int hash_id;
+       const br_x509_certificate *chain;
+       size_t chain_len;
+} br_ssl_server_choices;
+
+/*
+ * Type for the certificate and private key handler on the server: an
+ * object with the following methods:
+ *
+ *   choose    Select the parameters for this connection (cipher suite,
+ *             certificate chain...). The selection is written into the
+ *             '*choices' structure. Returned value is 1 on success, or
+ *             0 on error (an error here means that the handshake will
+ *             fail, and a handshake_failure alert will be sent to the
+ *             client).
+ *
+ *   do_keyx   Perform the server-side key exchange operation. Returned
+ *             value is 1 on success, 0 on error (see below). This is
+ *             called only when the selected cipher suite calls for a
+ *             RSA or ECDH key exchange involving the server key.
+ *
+ *   do_sign   Perform the server-side signature operation. Returned
+ *             value is the signature length, or 0 on error (see below).
+ *             This is called only when the selected cipher suite calls
+ *             for an ECDHE key exchange, signed by the server with its key.
+ *
+ *
+ * The do_keyx() method shall apply the following semantics:
+ *
+ * -- For RSA key exchange, it shall decrypt the incoming data along
+ * the rules of PKCS#1 v1.5. The method must verify the proper padding
+ * and also that the decrypted message length is exactly 48 bytes.
+ * IMPORTANT: these operations MUST be constant-time (or adequatly blinded).
+ * The decrypted message is written in the first 48 bytes of data[]. The
+ * caller makes sure that the data[] buffer is large enough, and that 'len'
+ * is at least 59 bytes.
+ *
+ * -- For ECDH key exchange, the provided data is an EC point (uncompressed
+ * format); the method shall multiply that point with the server private
+ * key, and write the X coordinate of the resulting point in the data[]
+ * buffer, starting at offset 1 (so if the method produces a compressed or
+ * uncompressed point, form offset 0, then everything is fine).
+ *
+ * In both cases, returned value is 1 on success, 0 on error.
+ *
+ *
+ * The do_sign() method shall compute the signature on the hash value
+ * provided in the data[] buffer. The 'hv_len' value contains the hash
+ * value length, while the 'len' parameter is the total size of the
+ * buffer. The method must verify that the signature length is no more
+ * than 'len' bytes, and report an error otherwise.
+ *
+ * The hash identifier is either 0 for the MD5+SHA-1 method in TLS-1.0 and
+ * 1.1, or a non-zero hash function identifier in TLS-1.2 and later. In
+ * the MD5+SHA-1 method, the hash value has length 36 bytes and there is
+ * no hash function identifying header to add in the padding.
+ *
+ * Returned value is the signature length (in bytes). On error, this method
+ * shall return 0.
+ */
+typedef struct br_ssl_server_policy_class_ br_ssl_server_policy_class;
+struct br_ssl_server_policy_class_ {
+       size_t context_size;
+       int (*choose)(const br_ssl_server_policy_class **pctx,
+               const br_ssl_server_context *cc,
+               br_ssl_server_choices *choices);
+       uint32_t (*do_keyx)(const br_ssl_server_policy_class **pctx,
+               unsigned char *data, size_t len);
+       size_t (*do_sign)(const br_ssl_server_policy_class **pctx,
+               int hash_id, size_t hv_len, unsigned char *data, size_t len);
+};
+
+/*
+ * A single-chain RSA policy handler, that always uses a single chain and
+ * a RSA key. It may be restricted to do only signatures or only key
+ * exchange.
+ */
+typedef struct {
+       const br_ssl_server_policy_class *vtable;
+       const br_x509_certificate *chain;
+       size_t chain_len;
+       const br_rsa_private_key *sk;
+       unsigned allowed_usages;
+       br_rsa_private irsacore;
+       br_rsa_pkcs1_sign irsasign;
+} br_ssl_server_policy_rsa_context;
+
+/*
+ * A single-chain EC policy handler, that always uses a single chain and
+ * an EC key. It may be restricted to do only signatures or only key
+ * exchange.
+ */
+typedef struct {
+       const br_ssl_server_policy_class *vtable;
+       const br_x509_certificate *chain;
+       size_t chain_len;
+       const br_ec_private_key *sk;
+       unsigned allowed_usages;
+       unsigned cert_issuer_key_type;
+       const br_multihash_context *mhash;
+       const br_ec_impl *iec;
+       br_ecdsa_sign iecdsa;
+} br_ssl_server_policy_ec_context;
+
+/*
+ * Class type for a session parameter cache.
+ *
+ *  save   Record session parameters. The session ID has been randomly
+ *         generated, and the session ID length is always 32 bytes.
+ *         The method shall copy the provided information (the structure
+ *         is transient).
+ *
+ *  load   Find session parameters by ID. The session ID is in the relevant
+ *         field in the '*params' structure, and has always length exactly
+ *         32 bytes. The method shall fill in the other field with the
+ *         session data, if found. Returned value is 1 when the session was
+ *         found, 0 otherwise.
+ *
+ * Note that the requesting server context is provided. Implementations
+ * may used some of the resources of that context, e.g. random number
+ * generator or implementations of some cryptographic algorithms.
+ */
+typedef struct br_ssl_session_cache_class_ br_ssl_session_cache_class;
+struct br_ssl_session_cache_class_ {
+       size_t context_size;
+       void (*save)(const br_ssl_session_cache_class **ctx,
+               br_ssl_server_context *server_ctx,
+               const br_ssl_session_parameters *params);
+       int (*load)(const br_ssl_session_cache_class **ctx,
+               br_ssl_server_context *server_ctx,
+               br_ssl_session_parameters *params);
+};
+
+/*
+ * Context for a very basic cache system that uses a linked list, managed
+ * with an LRU algorithm (when the cache is full and a new set of parameters
+ * must be saved, the least recently used entry is evicted). The storage
+ * buffer is externally provided. Internally, an index tree is used to
+ * speed up operations.
+ */
+typedef struct {
+       const br_ssl_session_cache_class *vtable;
+       unsigned char *store;
+       size_t store_len, store_ptr;
+       unsigned char index_key[32];
+       const br_hash_class *hash;
+       int init_done;
+       uint32_t head, tail, root;
+} br_ssl_session_cache_lru;
+
+/*
+ * Initialise a LRU session cache with the provided storage space.
+ */
+void br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc,
+       unsigned char *store, size_t store_len);
+
+/*
+ * Context structure for a SSL server.
+ */
+struct br_ssl_server_context_ {
+       /*
+        * The encapsulated engine context.
+        */
+       br_ssl_engine_context eng;
+
+       /*
+        * Flags.
+        */
+       uint32_t flags;
+
+       /*
+        * Maximum version from the client.
+        */
+       uint16_t client_max_version;
+
+       /*
+        * Session cache.
+        */
+       const br_ssl_session_cache_class **cache_vtable;
+
+       /*
+        * Translated cipher suites supported by the client. The list
+        * is trimmed to include only the cipher suites that the
+        * server also supports; they are in the same order as in the
+        * client message.
+        */
+       br_suite_translated client_suites[BR_MAX_CIPHER_SUITES];
+       unsigned char client_suites_num;
+
+       /*
+        * Hash functions supported by the client, with ECDSA and RSA
+        * (bit mask). For hash function with id 'x', set bit index is
+        * x for RSA, x+8 for ECDSA.
+        */
+       uint16_t hashes;
+
+       /*
+        * Curves supported by the client (bit mask, for named curves).
+        */
+       uint32_t curves;
+
+       /*
+        * Context for chain handler.
+        */
+       const br_ssl_server_policy_class **policy_vtable;
+       const br_x509_certificate *chain;
+       size_t chain_len;
+       const unsigned char *cert_cur;
+       size_t cert_len;
+       unsigned char sign_hash_id;
+
+       /*
+        * For the core handlers, thus avoiding (in most cases) the
+        * need for an externally provided policy context.
+        */
+       union {
+               const br_ssl_server_policy_class *vtable;
+               br_ssl_server_policy_rsa_context single_rsa;
+               br_ssl_server_policy_ec_context single_ec;
+       } chain_handler;
+
+       /*
+        * Buffer for the ECDHE private key.
+        */
+       unsigned char ecdhe_key[70];
+       size_t ecdhe_key_len;
+
+       /*
+        * Server-specific implementations.
+        */
+};
+
+/*
+ * Get currently defined server behavioural flags.
+ */
+static inline uint32_t
+br_ssl_server_get_flags(br_ssl_server_context *cc)
+{
+       return cc->flags;
+}
+
+/*
+ * Set all server flags. Flags which are not in the 'flags' argument
+ * are cleared.
+ */
+static inline void
+br_ssl_server_set_all_flags(br_ssl_server_context *cc, uint32_t flags)
+{
+       cc->flags = flags;
+}
+
+/*
+ * Add some server flags. The provided flags are set in the server context,
+ * but other flags are untouched.
+ */
+static inline void
+br_ssl_server_add_flags(br_ssl_server_context *cc, uint32_t flags)
+{
+       cc->flags |= flags;
+}
+
+/*
+ * Remove some server flags. The provided flags are cleared from the
+ * server context, but other flags are untouched.
+ */
+static inline void
+br_ssl_server_remove_flags(br_ssl_server_context *cc, uint32_t flags)
+{
+       cc->flags &= ~flags;
+}
+
+/*
+ * If this flag is set, then the server will enforce its own cipher suite
+ * preference order; otherwise, it follows the client preferences.
+ */
+#define BR_OPT_ENFORCE_SERVER_PREFERENCES      ((uint32_t)1 << 0)
+
+/*
+ * Each br_ssl_server_init_xxx() function sets the list of supported
+ * cipher suites and used implementations, as specified by the profile
+ * name 'xxx'. Defined profile names are:
+ *
+ *    full_rsa    all supported algorithm, server key type is RSA
+ *    full_ec     all supported algorithm, server key type is EC
+ *    FIXME: add other profiles
+ *
+ * Naming scheme for "minimal" profiles: min123
+ *
+ * -- character 1: key exchange
+ *      r = RSA
+ *      e = ECDHE_RSA
+ *      f = ECDHE_ECDSA
+ *      u = ECDH_RSA
+ *      v = ECDH_ECDSA
+ * -- character 2: version / PRF
+ *      0 = TLS 1.0 / 1.1 with MD5+SHA-1
+ *      2 = TLS 1.2 with SHA-256
+ *      3 = TLS 1.2 with SHA-384
+ * -- character 3: encryption
+ *      a = AES/CBC
+ *      g = AES/GCM
+ *      d = 3DES/CBC
+ */
+
+void br_ssl_server_init_full_rsa(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk);
+
+void br_ssl_server_init_full_ec(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       unsigned cert_issuer_key_type, const br_ec_private_key *sk);
+
+void br_ssl_server_init_minr2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk);
+void br_ssl_server_init_mine2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk);
+void br_ssl_server_init_minf2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk);
+void br_ssl_server_init_minu2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk);
+void br_ssl_server_init_minv2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk);
+
+/*
+ * Get the supported client suites. The returned array is ordered by
+ * client or server preferences, depending on the relevant flag.
+ */
+static inline const br_suite_translated *
+br_ssl_server_get_client_suites(const br_ssl_server_context *cc, size_t *num)
+{
+       *num = cc->client_suites_num;
+       return cc->client_suites;
+}
+
+/*
+ * Get the hash functions supported by the client. This is a field of
+ * bits: for hash function of ID x, bit x is set if the hash function
+ * is supported in RSA signatures, 8+x if it is supported with ECDSA.
+ */
+static inline uint16_t
+br_ssl_server_get_client_hashes(const br_ssl_server_context *cc)
+{
+       return cc->hashes;
+}
+
+/*
+ * Get the elliptic curves supported by the client. This is a bit field
+ * (bit x is set if curve of ID x is supported).
+ */
+static inline uint32_t
+br_ssl_server_get_client_curves(const br_ssl_server_context *cc)
+{
+       return cc->curves;
+}
+
+/*
+ * Clear the complete contents of a SSL server context, including the
+ * reference to the configured buffer, implementations, cipher suites
+ * and state.
+ */
+void br_ssl_server_zero(br_ssl_server_context *cc);
+
+/*
+ * Set an externally provided policy context.
+ */
+static inline void
+br_ssl_server_set_policy(br_ssl_server_context *cc,
+       const br_ssl_server_policy_class **pctx)
+{
+       cc->policy_vtable = pctx;
+}
+
+/*
+ * Set the server certificate chain and key (single RSA case).
+ * The 'allowed_usages' is a combination of usages, namely
+ * BR_KEYTYPE_KEYX and/or BR_KEYTYPE_SIGN.
+ */
+void br_ssl_server_set_single_rsa(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_length,
+       const br_rsa_private_key *sk, unsigned allowed_usages,
+       br_rsa_private irsacore, br_rsa_pkcs1_sign irsasign);
+
+/*
+ * Set the server certificate chain and key (single EC case).
+ * The 'allowed_usages' is a combination of usages, namely
+ * BR_KEYTYPE_KEYX and/or BR_KEYTYPE_SIGN.
+ */
+void br_ssl_server_set_single_ec(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_length,
+       const br_ec_private_key *sk, unsigned allowed_usages,
+       unsigned cert_issuer_key_type,
+       const br_ec_impl *iec, br_ecdsa_sign iecdsa);
+
+/*
+ * Configure the server context to use the provided cache for session
+ * parameters.
+ */
+static inline void
+br_ssl_server_set_cache(br_ssl_server_context *cc,
+       const br_ssl_session_cache_class **vtable)
+{
+       cc->cache_vtable = vtable;
+}
+
+/*
+ * Prepare or reset a server context for handling an incoming client.
+ */
+int br_ssl_server_reset(br_ssl_server_context *cc);
+
+/* ===================================================================== */
+
+/*
+ * Context for the simplified I/O context. The transport medium is accessed
+ * through the low_read() and low_write() callback functions, each with
+ * its own opaque context pointer.
+ *
+ *  low_read()    read some bytes, at most 'len' bytes, into data[]. The
+ *                returned value is the number of read bytes, or -1 on error.
+ *                The 'len' parameter is guaranteed never to exceed 20000,
+ *                so the length always fits in an 'int' on all platforms.
+ *
+ *  low_write()   write up to 'len' bytes, to be read from data[]. The
+ *                returned value is the number of written bytes, or -1 on
+ *                error. The 'len' parameter is guaranteed never to exceed
+ *                20000, so the length always fits in an 'int' on all
+ *                parameters.
+ *
+ * A socket closure (if the transport medium is a socket) should be reported
+ * as an error (-1). The callbacks shall endeavour to block until at least
+ * one byte can be read or written; a callback returning 0 at times is
+ * acceptable, but this normally leads to the callback being immediately
+ * called again, so the callback should at least always try to block for
+ * some time if no I/O can take place.
+ *
+ * The SSL engine naturally applies some buffering, so the callbacks need
+ * not apply buffers of their own.
+ */
+typedef struct {
+       br_ssl_engine_context *engine;
+       int (*low_read)(void *read_context,
+               unsigned char *data, size_t len);
+       void *read_context;
+       int (*low_write)(void *write_context,
+               const unsigned char *data, size_t len);
+       void *write_context;
+} br_sslio_context;
+
+/*
+ * Initialise a simplified I/O context over the provided engine and
+ * I/O callbacks.
+ */
+void br_sslio_init(br_sslio_context *ctx,
+       br_ssl_engine_context *engine,
+       int (*low_read)(void *read_context,
+               unsigned char *data, size_t len),
+       void *read_context,
+       int (*low_write)(void *write_context,
+               const unsigned char *data, size_t len),
+       void *write_context);
+
+/*
+ * Read some application data from a SSL connection. This call returns
+ * only when at least one byte has been obtained. Returned value is
+ * the number of bytes read, or -1 on error. The number of bytes
+ * always fits on an 'int' (data from a single SSL/TLS record is
+ * returned).
+ *
+ * On error or SSL closure, this function returns -1. The caller should
+ * inspect the error status on the SSL engine to distinguish between
+ * normal closure and error.
+ */
+int br_sslio_read(br_sslio_context *cc, void *dst, size_t len);
+
+/*
+ * Read some application data from a SSL connection. This call returns
+ * only when ALL requested bytes have been read. Returned value is 0
+ * on success, -1 on error. A normal SSL closure before that many bytes
+ * are obtained is reported as an error by this function.
+ */
+int br_sslio_read_all(br_sslio_context *cc, void *dst, size_t len);
+
+/*
+ * Write some application data onto a SSL connection. This call returns
+ * only when at least one byte had been written onto the connection (but
+ * not necessarily flushed). Returned value is the number of written
+ * bytes, or -1 on error (error conditions include a closed connection).
+ * It is guaranteed that the number of bytes written by such a call will
+ * fit in an 'int' on all architectures.
+ *
+ * Note that some written bytes may be buffered; use br_sslio_flush()
+ * to make sure that the data is sent to the transport stream.
+ */
+int br_sslio_write(br_sslio_context *cc, const void *src, size_t len);
+
+/*
+ * Write some application data onto a SSL connection. This call returns
+ * only when ALL the bytes have been written onto the connection (but
+ * not necessarily flushed). Returned value is 0 on success, -1 on error.
+ * 
+ * Note that some written bytes may be buffered; use br_sslio_flush()
+ * to make sure that the data is sent to the transport stream.
+ */
+int br_sslio_write_all(br_sslio_context *cc, const void *src, size_t len);
+
+/*
+ * Make sure that any buffered application data in the provided context
+ * get packed up and sent unto the low_write() callback method. If that
+ * callback method represents a buffered system, it is up to the caller
+ * to then "flush" that system too.
+ *
+ * Returned value is 0 on success, -1 on error.
+ */
+int br_sslio_flush(br_sslio_context *cc);
+
+/*
+ * Perform a SSL close. This implies sending a close_notify, and reading
+ * the response from the server. Returned value is 0 on success, -1 on
+ * error.
+ */
+int br_sslio_close(br_sslio_context *cc);
+
+/* ===================================================================== */
+
+/*
+ * Symbolic constants for cipher suites.
+ */
+
+/* From RFC 5246 */
+#define BR_TLS_NULL_WITH_NULL_NULL                   0x0000
+#define BR_TLS_RSA_WITH_NULL_MD5                     0x0001
+#define BR_TLS_RSA_WITH_NULL_SHA                     0x0002
+#define BR_TLS_RSA_WITH_NULL_SHA256                  0x003B
+#define BR_TLS_RSA_WITH_RC4_128_MD5                  0x0004
+#define BR_TLS_RSA_WITH_RC4_128_SHA                  0x0005
+#define BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA             0x000A
+#define BR_TLS_RSA_WITH_AES_128_CBC_SHA              0x002F
+#define BR_TLS_RSA_WITH_AES_256_CBC_SHA              0x0035
+#define BR_TLS_RSA_WITH_AES_128_CBC_SHA256           0x003C
+#define BR_TLS_RSA_WITH_AES_256_CBC_SHA256           0x003D
+#define BR_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA          0x000D
+#define BR_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA          0x0010
+#define BR_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA         0x0013
+#define BR_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA         0x0016
+#define BR_TLS_DH_DSS_WITH_AES_128_CBC_SHA           0x0030
+#define BR_TLS_DH_RSA_WITH_AES_128_CBC_SHA           0x0031
+#define BR_TLS_DHE_DSS_WITH_AES_128_CBC_SHA          0x0032
+#define BR_TLS_DHE_RSA_WITH_AES_128_CBC_SHA          0x0033
+#define BR_TLS_DH_DSS_WITH_AES_256_CBC_SHA           0x0036
+#define BR_TLS_DH_RSA_WITH_AES_256_CBC_SHA           0x0037
+#define BR_TLS_DHE_DSS_WITH_AES_256_CBC_SHA          0x0038
+#define BR_TLS_DHE_RSA_WITH_AES_256_CBC_SHA          0x0039
+#define BR_TLS_DH_DSS_WITH_AES_128_CBC_SHA256        0x003E
+#define BR_TLS_DH_RSA_WITH_AES_128_CBC_SHA256        0x003F
+#define BR_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256       0x0040
+#define BR_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256       0x0067
+#define BR_TLS_DH_DSS_WITH_AES_256_CBC_SHA256        0x0068
+#define BR_TLS_DH_RSA_WITH_AES_256_CBC_SHA256        0x0069
+#define BR_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256       0x006A
+#define BR_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256       0x006B
+#define BR_TLS_DH_anon_WITH_RC4_128_MD5              0x0018
+#define BR_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA         0x001B
+#define BR_TLS_DH_anon_WITH_AES_128_CBC_SHA          0x0034
+#define BR_TLS_DH_anon_WITH_AES_256_CBC_SHA          0x003A
+#define BR_TLS_DH_anon_WITH_AES_128_CBC_SHA256       0x006C
+#define BR_TLS_DH_anon_WITH_AES_256_CBC_SHA256       0x006D
+
+/* From RFC 4492 */
+#define BR_TLS_ECDH_ECDSA_WITH_NULL_SHA              0xC001
+#define BR_TLS_ECDH_ECDSA_WITH_RC4_128_SHA           0xC002
+#define BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA      0xC003
+#define BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA       0xC004
+#define BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA       0xC005
+#define BR_TLS_ECDHE_ECDSA_WITH_NULL_SHA             0xC006
+#define BR_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA          0xC007
+#define BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA     0xC008
+#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA      0xC009
+#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA      0xC00A
+#define BR_TLS_ECDH_RSA_WITH_NULL_SHA                0xC00B
+#define BR_TLS_ECDH_RSA_WITH_RC4_128_SHA             0xC00C
+#define BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA        0xC00D
+#define BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA         0xC00E
+#define BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA         0xC00F
+#define BR_TLS_ECDHE_RSA_WITH_NULL_SHA               0xC010
+#define BR_TLS_ECDHE_RSA_WITH_RC4_128_SHA            0xC011
+#define BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA       0xC012
+#define BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA        0xC013
+#define BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA        0xC014
+#define BR_TLS_ECDH_anon_WITH_NULL_SHA               0xC015
+#define BR_TLS_ECDH_anon_WITH_RC4_128_SHA            0xC016
+#define BR_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA       0xC017
+#define BR_TLS_ECDH_anon_WITH_AES_128_CBC_SHA        0xC018
+#define BR_TLS_ECDH_anon_WITH_AES_256_CBC_SHA        0xC019
+
+/* From RFC 5288 */
+#define BR_TLS_RSA_WITH_AES_128_GCM_SHA256           0x009C
+#define BR_TLS_RSA_WITH_AES_256_GCM_SHA384           0x009D
+#define BR_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256       0x009E
+#define BR_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384       0x009F
+#define BR_TLS_DH_RSA_WITH_AES_128_GCM_SHA256        0x00A0
+#define BR_TLS_DH_RSA_WITH_AES_256_GCM_SHA384        0x00A1
+#define BR_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256       0x00A2
+#define BR_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384       0x00A3
+#define BR_TLS_DH_DSS_WITH_AES_128_GCM_SHA256        0x00A4
+#define BR_TLS_DH_DSS_WITH_AES_256_GCM_SHA384        0x00A5
+#define BR_TLS_DH_anon_WITH_AES_128_GCM_SHA256       0x00A6
+#define BR_TLS_DH_anon_WITH_AES_256_GCM_SHA384       0x00A7
+
+/* From RFC 5289 */
+#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256   0xC023
+#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384   0xC024
+#define BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256    0xC025
+#define BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384    0xC026
+#define BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256     0xC027
+#define BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384     0xC028
+#define BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256      0xC029
+#define BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384      0xC02A
+#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256   0xC02B
+#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384   0xC02C
+#define BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256    0xC02D
+#define BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384    0xC02E
+#define BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256     0xC02F
+#define BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384     0xC030
+#define BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256      0xC031
+#define BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384      0xC032
+
+/* From RFC 7905 */
+#define BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256     0xCCA8
+#define BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256   0xCCA9
+#define BR_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256       0xCCAA
+#define BR_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256           0xCCAB
+#define BR_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256     0xCCAC
+#define BR_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256       0xCCAD
+#define BR_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256       0xCCAE
+
+/*
+ * Symbolic constants for alerts.
+ */
+#define BR_ALERT_CLOSE_NOTIFY                0
+#define BR_ALERT_UNEXPECTED_MESSAGE         10
+#define BR_ALERT_BAD_RECORD_MAC             20
+#define BR_ALERT_RECORD_OVERFLOW            22
+#define BR_ALERT_DECOMPRESSION_FAILURE      30
+#define BR_ALERT_HANDSHAKE_FAILURE          40
+#define BR_ALERT_BAD_CERTIFICATE            42
+#define BR_ALERT_UNSUPPORTED_CERTIFICATE    43
+#define BR_ALERT_CERTIFICATE_REVOKED        44
+#define BR_ALERT_CERTIFICATE_EXPIRED        45
+#define BR_ALERT_CERTIFICATE_UNKNOWN        46
+#define BR_ALERT_ILLEGAL_PARAMETER          47
+#define BR_ALERT_UNKNOWN_CA                 48
+#define BR_ALERT_ACCESS_DENIED              49
+#define BR_ALERT_DECODE_ERROR               50
+#define BR_ALERT_DECRYPT_ERROR              51
+#define BR_ALERT_PROTOCOL_VERSION           70
+#define BR_ALERT_INSUFFICIENT_SECURITY      71
+#define BR_ALERT_INTERNAL_ERROR             80
+#define BR_ALERT_USER_CANCELED              90
+#define BR_ALERT_NO_RENEGOTIATION          100
+#define BR_ALERT_UNSUPPORTED_EXTENSION     110
+
+#endif
diff --git a/inc/bearssl_x509.h b/inc/bearssl_x509.h
new file mode 100644 (file)
index 0000000..b35eddd
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BR_BEARSSL_X509_H__
+#define BR_BEARSSL_X509_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "bearssl_ec.h"
+#include "bearssl_hash.h"
+#include "bearssl_rsa.h"
+
+/*
+ * X.509 Certificate Chain Processing
+ * ----------------------------------
+ *
+ * An X.509 processing engine receives an X.509 chain, chunk by chunk,
+ * as received from a SSL/TLS client or server (the client receives the
+ * server's certificate chain, and the server receives the client's
+ * certificate chain if it requested a client certificate). The chain
+ * is thus injected in the engine in SSL order (end-entity first).
+ *
+ * The engine's job is to return the public key to use for SSL/TLS.
+ * How exactly that key is obtained and verified is entirely up to the
+ * engine.
+ */
+
+/*
+ * X.509 error codes are in the 32..63 range.
+ */
+
+/* Validation was successful; this is not actually an error. */
+#define BR_ERR_X509_OK                    32
+
+/* Invalid value in an ASN.1 structure. */
+#define BR_ERR_X509_INVALID_VALUE         33
+
+/* Truncated certificate. */
+#define BR_ERR_X509_TRUNCATED             34
+
+/* Empty certificate chain (no certificate at all). */
+#define BR_ERR_X509_EMPTY_CHAIN           35
+
+/* Decoding error: inner element extends beyond outer element size. */
+#define BR_ERR_X509_INNER_TRUNC           36
+
+/* Decoding error: unsupported tag class (application or private). */
+#define BR_ERR_X509_BAD_TAG_CLASS         37
+
+/* Decoding error: unsupported tag value. */
+#define BR_ERR_X509_BAD_TAG_VALUE         38
+
+/* Decoding error: indefinite length. */
+#define BR_ERR_X509_INDEFINITE_LENGTH     39
+
+/* Decoding error: extraneous element. */
+#define BR_ERR_X509_EXTRA_ELEMENT         40
+
+/* Decoding error: unexpected element. */
+#define BR_ERR_X509_UNEXPECTED            41
+
+/* Decoding error: expected constructed element, but is primitive. */
+#define BR_ERR_X509_NOT_CONSTRUCTED       42
+
+/* Decoding error: expected primitive element, but is constructed. */
+#define BR_ERR_X509_NOT_PRIMITIVE         43
+
+/* Decoding error: BIT STRING length is not multiple of 8. */
+#define BR_ERR_X509_PARTIAL_BYTE          44
+
+/* Decoding error: BOOLEAN value has invalid length. */
+#define BR_ERR_X509_BAD_BOOLEAN           45
+
+/* Decoding error: value is off-limits. */
+#define BR_ERR_X509_OVERFLOW              46
+
+/* Invalid distinguished name. */
+#define BR_ERR_X509_BAD_DN                47
+
+/* Invalid date/time representation. */
+#define BR_ERR_X509_BAD_TIME              48
+
+/* Certificate contains unsupported features that cannot be ignored. */
+#define BR_ERR_X509_UNSUPPORTED           49
+
+/* Key or signature size exceeds internal limits. */
+#define BR_ERR_X509_LIMIT_EXCEEDED        50
+
+/* Key type does not match that which was expected. */
+#define BR_ERR_X509_WRONG_KEY_TYPE        51
+
+/* Signature is invalid. */
+#define BR_ERR_X509_BAD_SIGNATURE         52
+
+/* Validation time is unknown. */
+#define BR_ERR_X509_TIME_UNKNOWN          53
+
+/* Certificate is expired or not yet valid. */
+#define BR_ERR_X509_EXPIRED               54
+
+/* Issuer/Subject DN mismatch in the chain. */
+#define BR_ERR_X509_DN_MISMATCH           55
+
+/* Expected server name was not found in the chain. */
+#define BR_ERR_X509_BAD_SERVER_NAME       56
+
+/* Unknown critical extension in certificate. */
+#define BR_ERR_X509_CRITICAL_EXTENSION    57
+
+/* Not a CA, or path length constraint violation */
+#define BR_ERR_X509_NOT_CA                58
+
+/* Key Usage extension prohibits intended usage. */
+#define BR_ERR_X509_FORBIDDEN_KEY_USAGE   59
+
+/* Public key found in certificate is too small. */
+#define BR_ERR_X509_WEAK_PUBLIC_KEY       60
+
+/* Chain could not be linked to a trust anchor. */
+#define BR_ERR_X509_NOT_TRUSTED           62
+
+/*
+ * A structure to encode public keys.
+ */
+typedef struct {
+       unsigned char key_type;
+       union {
+               br_rsa_public_key rsa;
+               br_ec_public_key ec;
+       } key;
+} br_x509_pkey;
+
+/*
+ * A trust anchor consists in:
+ * -- an encoded DN
+ * -- a public key
+ * -- flags
+ */
+typedef struct {
+       unsigned char *dn;
+       size_t dn_len;
+       /* unsigned char hashed_DN[64]; */
+       unsigned flags;
+       br_x509_pkey pkey;
+} br_x509_trust_anchor;
+
+/* Trust anchor flag: trust anchor is a CA and thus acceptable for
+   signing other certificates. Without this flag, the trust anchor
+   is only for direct trust (name and key match EE certificate). */
+#define BR_X509_TA_CA        0x0001
+
+/*
+ * Key type: combination of a basic key type (low 4 bits) and some
+ * optional flags.
+ *
+ * For a public key, the basic key type only is set.
+ *
+ * For an expected key type, the flags indicate the intended purpose(s)
+ * for the key; the basic key type may be set to 0 to indicate that any
+ * key type compatible with the indicated purpose is acceptable.
+ */
+#define BR_KEYTYPE_RSA    1
+#define BR_KEYTYPE_EC     2
+
+#define BR_KEYTYPE_KEYX   0x10   /* key is for key exchange or encryption */
+#define BR_KEYTYPE_SIGN   0x20   /* key is for verifying signatures */
+
+/*
+ * start_chain   Called when a new chain is started. If 'server_name'
+ *               is not NULL and non-empty, then it is a name that
+ *               should be looked for in the EE certificate (in the
+ *               SAN extension as dNSName, or in the subjectDN's CN
+ *               if there is no SAN extension).
+ *               The caller ensures that the provided 'server_name'
+ *               pointer remains valid throughout validation.
+ *
+ * start_cert    Begins a new certificate in the chain. The provided
+ *               length is in bytes; this is the total certificate length.
+ *
+ * append        Get some additional bytes for the current certificate.
+ *
+ * end_cert      Ends the current certificate.
+ *
+ * end_chain     Called at the end of the chain. Returned value is
+ *               0 on success, or a non-zero error code.
+ *
+ * get_pkey      Returns the EE certificate public key.
+ *
+ * For a complete chain, start_chain() and end_chain() are always
+ * called. For each certificate, start_cert(), some append() calls, then
+ * end_cert() are called, in that order. There may be no append() call
+ * at all if the certificate is empty (which is not valid but may happen
+ * if the peer sends exactly that).
+ *
+ * get_pkey() shall return a pointer to a structure that is valid as
+ * long as a new chain is not started. This may be a sub-structure
+ * within the context for the engine. This function MAY return a valid
+ * pointer to a public key even in some cases of validation failure,
+ * depending on the validation engine.
+ */
+typedef struct br_x509_class_ br_x509_class;
+struct br_x509_class_ {
+       size_t context_size;
+       void (*start_chain)(const br_x509_class **ctx,
+               unsigned expected_key_type,
+               const char *server_name);
+       void (*start_cert)(const br_x509_class **ctx, uint32_t length);
+       void (*append)(const br_x509_class **ctx,
+               const unsigned char *buf, size_t len);
+       void (*end_cert)(const br_x509_class **ctx);
+       unsigned (*end_chain)(const br_x509_class **ctx);
+       const br_x509_pkey *(*get_pkey)(const br_x509_class *const *ctx);
+};
+
+/*
+ * The "known key" X.509 engine is a trivial engine that completely
+ * ignores the certificates, and instead reports an externally
+ * configured public key.
+ */
+typedef struct {
+       const br_x509_class *vtable;
+       br_x509_pkey pkey;
+} br_x509_knownkey_context;
+extern const br_x509_class br_x509_knownkey_vtable;
+
+/*
+ * Initialize a "known key" X.509 engine with a known RSA public key.
+ * The provided pointers are linked in, not copied, so they must
+ * remain valid while the public key may be in usage (i.e. at least up
+ * to the end of the handshake -- and since there may be renegotiations,
+ * these buffers should stay until the connection is finished).
+ */
+void br_x509_knownkey_init_rsa(br_x509_knownkey_context *ctx,
+       const br_rsa_public_key *pk);
+
+/*
+ * Initialize a "known key" X.509 engine with a known EC public key.
+ * The provided pointers are linked in, not copied, so they must
+ * remain valid while the public key may be in usage (i.e. at least up
+ * to the end of the handshake -- and since there may be renegotiations,
+ * these buffers should stay until the connection is finished).
+ */
+void br_x509_knownkey_init_ec(br_x509_knownkey_context *ctx,
+       const br_ec_public_key *pk);
+
+/*
+ * The minimal X.509 engine has some state buffers which must be large
+ * enough to simultaneously accommodate:
+ * -- the public key extracted from the current certificate;
+ * -- the signature on the current certificate or on the previous
+ *    certificate;
+ * -- the public key extracted from the EE certificate.
+ *
+ * We store public key elements in their raw unsigned big-endian
+ * encoding. We want to support up to RSA-4096 with a short (up to 64
+ * bits) public exponent, thus a buffer for a public key must have
+ * length at least 520 bytes. Similarly, a RSA-4096 signature has length
+ * 512 bytes.
+ *
+ * Though RSA public exponents can formally be as large as the modulus
+ * (mathematically, even larger exponents would work, but PKCS#1 forbids
+ * them), exponents that do not fit on 32 bits are extremely rare,
+ * notably because some widespread implementation (e.g. Microsoft's
+ * CryptoAPI) don't support them. Moreover, large public exponent do not
+ * seem to imply any tangible security benefit, and they increase the
+ * cost of public key operations.
+ *
+ * EC public keys are shorter than RSA public keys; even with curve
+ * NIST P-521 (the largest curve we care to support), a public key is
+ * encoded over 133 bytes only.
+ */
+#define BR_X509_BUFSIZE_KEY   520
+#define BR_X509_BUFSIZE_SIG   512
+
+/*
+ * The "minimal" X.509 engine performs basic decoding of certificates and
+ * some validations:
+ *  -- DN matching
+ *  -- signatures
+ *  -- validity dates
+ *  -- Basic Constraints extension
+ *  -- Server name check against SAN extension
+ */
+typedef struct {
+       const br_x509_class *vtable;
+
+       /* Structure for returning the EE public key. */
+       br_x509_pkey pkey;
+
+       /* CPU for the T0 virtual machine. */
+       struct {
+               uint32_t *dp;
+               uint32_t *rp;
+               const unsigned char *ip;
+       } cpu;
+       uint32_t dp_stack[32];
+       uint32_t rp_stack[32];
+       int err;
+
+       /* Server name to match with the SAN / CN of the EE certificate. */
+       const char *server_name;
+
+       /* Expected EE key type and usage. */
+       unsigned char expected_key_type;
+
+       /* Explicitly set date and time. */
+       uint32_t days, seconds;
+
+       /* Current certificate length (in bytes). Set to 0 when the
+          certificate has been fully processed. */
+       uint32_t cert_length;
+
+       /* Number of certificates processed so far in the current chain.
+          It is incremented at the end of the processing of a certificate,
+          so it is 0 for the EE. */
+       uint32_t num_certs;
+
+       /* Certificate data chunk. */
+       const unsigned char *hbuf;
+       size_t hlen;
+
+       /* The pad serves as destination for various operations. */
+       unsigned char pad[256];
+
+       /* Buffer for EE public key data. */
+       unsigned char ee_pkey_data[BR_X509_BUFSIZE_KEY];
+
+       /* Buffer for currently decoded public key. */
+       unsigned char pkey_data[BR_X509_BUFSIZE_KEY];
+
+       /* Signature type: signer key type, offset to the hash
+          function OID (in the T0 data block) and hash function
+          output length (TBS hash length). */
+       unsigned char cert_signer_key_type;
+       uint16_t cert_sig_hash_oid;
+       unsigned char cert_sig_hash_len;
+
+       /* Current/last certificate signature. */
+       unsigned char cert_sig[BR_X509_BUFSIZE_SIG];
+       uint16_t cert_sig_len;
+
+       /* Minimum RSA key length (difference in bytes from 128). */
+       int16_t min_rsa_size;
+
+       /* Configured trust anchors. */
+       const br_x509_trust_anchor *trust_anchors;
+       size_t trust_anchors_num;
+
+       /*
+        * Multi-hasher for the TBS.
+        */
+       unsigned char do_mhash;
+       br_multihash_context mhash;
+       unsigned char tbs_hash[64];
+
+       /*
+        * Simple hasher for the subject/issuer DN.
+        */
+       unsigned char do_dn_hash;
+       const br_hash_class *dn_hash_impl;
+       br_hash_compat_context dn_hash;
+       unsigned char current_dn_hash[64];
+       unsigned char next_dn_hash[64];
+       unsigned char saved_dn_hash[64];
+
+       /*
+        * Public key cryptography implementations (signature verification).
+        */
+       br_rsa_pkcs1_vrfy irsa;
+       br_ecdsa_vrfy iecdsa;
+       const br_ec_impl *iec;
+
+} br_x509_minimal_context;
+extern const br_x509_class br_x509_minimal_vtable;
+
+/*
+ * Initialize a "minimal" X.509 engine. Parameters are:
+ *  -- context to initialize
+ *  -- hash function to use for hashing normalized DN
+ *  -- list of trust anchors
+ *
+ * After initialization, some hash function implementations for signature
+ * verification MUST be added.
+ */
+void br_x509_minimal_init(br_x509_minimal_context *ctx,
+       const br_hash_class *dn_hash_impl,
+       const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
+
+/*
+ * Set a hash function implementation, identified by ID, for purposes of
+ * verifying signatures on certificates. This must be called after
+ * br_x509_minimal_init().
+ */
+static inline void
+br_x509_minimal_set_hash(br_x509_minimal_context *ctx,
+       int id, const br_hash_class *impl)
+{
+       br_multihash_setimpl(&ctx->mhash, id, impl);
+}
+
+/*
+ * Set a RSA implementation, for purposes of verifying signatures on
+ * certificates. This must be called after br_x509_minimal_init().
+ */
+static inline void
+br_x509_minimal_set_rsa(br_x509_minimal_context *ctx,
+       br_rsa_pkcs1_vrfy irsa)
+{
+       ctx->irsa = irsa;
+}
+
+/*
+ * Set an ECDSA implementation, for purposes of verifying signatures on
+ * certificates. This must be called after br_x509_minimal_init().
+ */
+static inline void
+br_x509_minimal_set_ecdsa(br_x509_minimal_context *ctx,
+       const br_ec_impl *iec, br_ecdsa_vrfy iecdsa)
+{
+       ctx->iecdsa = iecdsa;
+       ctx->iec = iec;
+}
+
+/*
+ * Set the validation time, normally to the current date and time.
+ * This consists in two 32-bit counts:
+ *
+ * -- Days are counted in a proleptic Gregorian calendar since
+ * January 1st, 0 AD. Year "0 AD" is the one that preceded "1 AD";
+ * it is also traditionally known as "1 BC".
+ *
+ * -- Seconds are counted since midnight, from 0 to 86400 (a count of
+ * 86400 is possible only if a leap second happened).
+ *
+ * If the validation date and time are not explicitly set, but BearSSL
+ * was compiled with support for the system clock on the underlying
+ * platform, then the current time will automatically be used. Otherwise,
+ * validation will fail (except in case of direct trust of the EE key).
+ */
+static inline void
+br_x509_minimal_set_time(br_x509_minimal_context *ctx,
+       uint32_t days, uint32_t seconds)
+{
+       ctx->days = days;
+       ctx->seconds = seconds;
+}
+
+/*
+ * Set the minimal acceptable length for RSA keys, in bytes. Default
+ * is 128 bytes, which means RSA keys of 1017 bits or more. This setting
+ * applies to keys extracted from certificates (EE and intermediate CA).
+ * It does _not_ apply to "CA" trust anchors.
+ */
+static inline void
+br_x509_minimal_set_minrsa(br_x509_minimal_context *ctx, int byte_length)
+{
+       ctx->min_rsa_size = (int16_t)(byte_length - 128);
+}
+
+/*
+ * An X.509 decoder context. This is not for X.509 validation, but for
+ * using certificates as trust anchors (e.g. self-signed certificates
+ * read from files).
+ */
+typedef struct {
+
+       /* Structure for returning the public key. */
+       br_x509_pkey pkey;
+
+       /* CPU for the T0 virtual machine. */
+       struct {
+               uint32_t *dp;
+               uint32_t *rp;
+               const unsigned char *ip;
+       } cpu;
+       uint32_t dp_stack[32];
+       uint32_t rp_stack[32];
+       int err;
+
+       /* The pad serves as destination for various operations. */
+       unsigned char pad[256];
+
+       /* Flag set when decoding succeeds. */
+       unsigned char decoded;
+
+       /* Validity dates. */
+       uint32_t notbefore_days, notbefore_seconds;
+       uint32_t notafter_days, notafter_seconds;
+
+       /* The "CA" flag. This is set to true if the certificate contains
+          a Basic Constraints extension that asserts CA status. */
+       unsigned char isCA;
+
+       /* DN processing: the subject DN is extracted and pushed to the
+          provided callback. */
+       unsigned char copy_dn;
+       void *append_dn_ctx;
+       void (*append_dn)(void *ctx, const void *buf, size_t len);
+
+       /* Certificate data chunk. */
+       const unsigned char *hbuf;
+       size_t hlen;
+
+       /* Buffer for decoded public key. */
+       unsigned char pkey_data[BR_X509_BUFSIZE_KEY];
+
+       /* Type of key and hash function used in the certificate signature. */
+       unsigned char signer_key_type;
+       unsigned char signer_hash_id;
+
+} br_x509_decoder_context;
+
+/*
+ * Initialise an X.509 decoder context for processing a new certificate.
+ */
+void br_x509_decoder_init(br_x509_decoder_context *ctx,
+       void (*append_dn)(void *ctx, const void *buf, size_t len),
+       void *append_dn_ctx);
+
+/*
+ * Push some certificate bytes into a decoder context.
+ */
+void br_x509_decoder_push(br_x509_decoder_context *ctx,
+       const void *data, size_t len);
+
+/*
+ * Obtain the decoded public key. Returned value is a pointer to a
+ * structure internal to the decoder context; releasing or reusing the
+ * decoder context invalidates that structure.
+ *
+ * If decoding was not finished, or failed, then NULL is returned.
+ */
+static inline br_x509_pkey *
+br_x509_decoder_get_pkey(br_x509_decoder_context *ctx)
+{
+       if (ctx->decoded && ctx->err == 0) {
+               return &ctx->pkey;
+       } else {
+               return NULL;
+       }
+}
+
+/*
+ * Get decoder error. If no error was reported yet but the certificate
+ * decoding is not finished, then the error code is BR_ERR_X509_TRUNCATED.
+ * If decoding was successful, then 0 is returned.
+ */
+static inline int
+br_x509_decoder_last_error(br_x509_decoder_context *ctx)
+{
+       if (ctx->err != 0) {
+               return ctx->err;
+       }
+       if (!ctx->decoded) {
+               return BR_ERR_X509_TRUNCATED;
+       }
+       return 0;
+}
+
+/*
+ * Get the "isCA" flag from an X.509 decoder context. This flag is set
+ * if the decoded certificate claims to be a CA through a Basic
+ * Constraints extension.
+ */
+static inline int
+br_x509_decoder_isCA(br_x509_decoder_context *ctx)
+{
+       return ctx->isCA;
+}
+
+/*
+ * Get the issuing CA key type (type of key used to sign the decoded
+ * certificate). This is BR_KEYTYPE_RSA or BR_KEYTYPE_EC. The value 0
+ * is returned if the signature type was not recognised.
+ */
+static inline int
+br_x509_decoder_get_signer_key_type(br_x509_decoder_context *ctx)
+{
+       return ctx->signer_key_type;
+}
+
+/*
+ * Get the identifier for the hash function used to sign the decoded
+ * certificate. This is 0 if the hash function was not recognised.
+ */
+static inline int
+br_x509_decoder_get_signer_hash_id(br_x509_decoder_context *ctx)
+{
+       return ctx->signer_hash_id;
+}
+
+/*
+ * Type for an X.509 certificate (DER-encoded).
+ */
+typedef struct {
+       unsigned char *data;
+       size_t data_len;
+} br_x509_certificate;
+
+/*
+ * Private key decoder context.
+ */
+typedef struct {
+
+       /* Structure for returning the private key. */
+       union {
+               br_rsa_private_key rsa;
+               br_ec_private_key ec;
+       } key;
+
+       /* CPU for the T0 virtual machine. */
+       struct {
+               uint32_t *dp;
+               uint32_t *rp;
+               const unsigned char *ip;
+       } cpu;
+       uint32_t dp_stack[32];
+       uint32_t rp_stack[32];
+       int err;
+
+       /* Private key data chunk. */
+       const unsigned char *hbuf;
+       size_t hlen;
+
+       /* The pad serves as destination for various operations. */
+       unsigned char pad[256];
+
+       /* Decoded key type; 0 until decoding is complete. */
+       unsigned char key_type;
+
+       /* Buffer for the private key elements. It shall be large enough
+          to accommodate all elements for a RSA-4096 private key (roughly
+          five 2048-bit integers, possibly a bit more). */
+       unsigned char key_data[3 * BR_X509_BUFSIZE_SIG];
+
+} br_skey_decoder_context;
+
+/*
+ * Initialise a private key decoder context.
+ */
+void br_skey_decoder_init(br_skey_decoder_context *ctx);
+
+/*
+ * Push some data bytes into a private key decoder context.
+ */
+void br_skey_decoder_push(br_skey_decoder_context *ctx,
+       const void *data, size_t len);
+
+/*
+ * Get the decoding status for a private key. This is either 0 on success,
+ * or a non-zero error code.
+ */
+static inline int
+br_skey_decoder_last_error(const br_skey_decoder_context *ctx)
+{
+       if (ctx->err != 0) {
+               return ctx->err;
+       }
+       if (ctx->key_type == 0) {
+               return BR_ERR_X509_TRUNCATED;
+       }
+       return 0;
+}
+
+/*
+ * Get the decoded private key type. This is 0 if decoding is not finished
+ * or failed.
+ */
+static inline int
+br_skey_decoder_key_type(const br_skey_decoder_context *ctx)
+{
+       if (ctx->err == 0) {
+               return ctx->key_type;
+       } else {
+               return 0;
+       }
+}
+
+/*
+ * Get the decoded RSA private key. This function returns NULL if the
+ * decoding failed, or is not finished, or the key is not RSA. The returned
+ * pointer references structures within the context that can become
+ * invalid if the context is reused or released.
+ */
+static inline const br_rsa_private_key *
+br_skey_decoder_get_rsa(const br_skey_decoder_context *ctx)
+{
+       if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_RSA) {
+               return &ctx->key.rsa;
+       } else {
+               return NULL;
+       }
+}
+
+/*
+ * Get the decoded EC private key. This function returns NULL if the
+ * decoding failed, or is not finished, or the key is not EC. The returned
+ * pointer references structures within the context that can become
+ * invalid if the context is reused or released.
+ */
+static inline const br_ec_private_key *
+br_skey_decoder_get_ec(const br_skey_decoder_context *ctx)
+{
+       if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_EC) {
+               return &ctx->key.ec;
+       } else {
+               return NULL;
+       }
+}
+
+#endif
diff --git a/mkT0.sh b/mkT0.sh
new file mode 100755 (executable)
index 0000000..61de5c6
--- /dev/null
+++ b/mkT0.sh
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+CSC=$(which mono-csc || which dmcs || echo "none")
+
+if [ $CSC = "none" ]; then
+       echo "Error: Please install mono-devel."
+       exit 1
+fi
+
+set -e
+$CSC /out:T0Comp.exe /main:T0Comp /res:T0/kern.t0,t0-kernel T0/*.cs
diff --git a/samples/README.txt b/samples/README.txt
new file mode 100644 (file)
index 0000000..77c93c7
--- /dev/null
@@ -0,0 +1,36 @@
+This directory contains sample code for using BearSSL.
+
+client_basic.c
+
+   A sample client code, that connects to a server, performs a SSL
+   handshake, sends a basic HTTP GET request, and dumps the complete
+   answer on stdout.
+
+   Compile it against BearSSL headers (in the ../inc directory) and
+   library (libbearssl.a). This code will validate the server
+   certificate against two hardcoded trust anchors.
+
+server_basic.c
+
+   A sample SSL server, that serves one client at a time. It reads a
+   single HTTP request (that it does not really parse; it just waits for
+   the two successive line endings that mark the end of the request),
+   and pushes a basic response.
+
+   Compile it against BearSSL headers (in the ../inc directory) and
+   library (libbearssl.a). Depending on compilation options (see the
+   code), it will use one of several certificate chains, that exercise
+   various combinations of RSA and EC keys and signatures. These
+   certificate chains link to the trust anchors that are hardcoded
+   in client_basic.c, so the sample client and the sample server can
+   be tested against each other.
+
+custom_profile.c
+
+   A sample C source file that shows how to write your own client or
+   server profiles (selections of cipher suites and algorithms).
+
+
+The .pem files are certificate and keys corresponding to the chains
+and anchors used by the sample client and server. They are provided
+for reference only; these files are not used by the examples.
diff --git a/samples/cert-ee-ec+rsa.pem b/samples/cert-ee-ec+rsa.pem
new file mode 100644 (file)
index 0000000..07f5457
--- /dev/null
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICcTCCAVmgAwIBAgIUbmO7Sc5BfgOM9Ubyiq5hCDWwlLMwDQYJKoZIhvcNAQELBQAwJzELMAkG
+A1UEBhMCQ0ExGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBDQTAeFw0xMDAxMDEwMDAwMDBaFw0zNzEy
+MzEyMzU5NTlaMCExCzAJBgNVBAYTAkNBMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIB
+BggqhkjOPQMBBwNCAARfOJ2n/02Kr/Y0OUYa/Drf9COqqer7xQjeAI6+eaU3WExt3QHKq0ffibbH
+Fx84/B0gFN1FwOCPk044C/zpmaFJo2YwZDAfBgNVHSMEGDAWgBR8z6PGKffzxaoZ0MAW6+BAD85E
+pzAdBgNVHQ4EFgQUww6GqnW0FcDllQkyvl6SdankRJswDAYDVR0TAQH/BAIwADAUBgNVHREEDTAL
+gglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAI6JY6ebqBCOnhjQpHrdLIIWjwsO1cpXu19v
+/FcowCG6rZSoF0JF1zH3hFcZRiQ8x0k4P0h6CRrrUbrFdY5MsiWwxyI+5+VG27E2/BuFUbDtgxbw
+OnnaXShq7mogTLBwXrDtem0tGsm0ccLEox0lhjBUsZgmwVHg+DGtZ0id5qFSOyBHyXDagLWk9D9y
+azcwVzksRptE8dlOu6Zf45rFf2y2Zcu/QHKS0Gj2rnl+JMFbaDAoU3FhevQ2ezvCtuwf3DBABA3q
+swrPddO9nqtxcWh/pUFSAOmzruQe8btpxjvV3tLCJWkIPDfM94Jq5ah7agLavaQBMzPz3kYA7HXP
+4H0=
+-----END CERTIFICATE-----
diff --git a/samples/cert-ee-ec.pem b/samples/cert-ee-ec.pem
new file mode 100644 (file)
index 0000000..ff8ddbc
--- /dev/null
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVagAwIBAgIUHE0AkWniRqyQfGRcU/H/t8HLbnowCgYIKoZIzj0EAwIwJzELMAkGA1UE
+BhMCQ0ExGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBDQTAeFw0xMDAxMDEwMDAwMDBaFw0zNzEyMzEy
+MzU5NTlaMCExCzAJBgNVBAYTAkNBMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggq
+hkjOPQMBBwNCAARfOJ2n/02Kr/Y0OUYa/Drf9COqqer7xQjeAI6+eaU3WExt3QHKq0ffibbHFx84
+/B0gFN1FwOCPk044C/zpmaFJo2YwZDAfBgNVHSMEGDAWgBTw0PEi+XpIFwZ7Pb249c1VnFw+cDAd
+BgNVHQ4EFgQUww6GqnW0FcDllQkyvl6SdankRJswDAYDVR0TAQH/BAIwADAUBgNVHREEDTALggls
+b2NhbGhvc3QwCgYIKoZIzj0EAwIDSAAwRQIhAJH79ATQ5S4B1IzwF2IP3MyAyhjEQHwnA8s0Aw2b
+yFlNAiAFVWni2KFAMzQOfkkyZB0/ax/QLbcvUgRWr9M3j4eZog==
+-----END CERTIFICATE-----
diff --git a/samples/cert-ee-rsa.pem b/samples/cert-ee-rsa.pem
new file mode 100644 (file)
index 0000000..ef33e40
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIDPDCCAiSgAwIBAgIUWNq6Ns3toNpcEDNzjgxkknmSrwMwDQYJKoZIhvcNAQELBQAwJzELMAkG
+A1UEBhMCQ0ExGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBDQTAeFw0xMDAxMDEwMDAwMDBaFw0zNzEy
+MzEyMzU5NTlaMCExCzAJBgNVBAYTAkNBMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDUeh0nuis6Z7KRavvng0TK7Rx1rd1Ng2LWqmiVsiQhexWuKplo
+Fe1m8LhY59P1LsbZKl7nDi7n/GdZwMhhfUukb92f2ciFh2THuhoPKdSWqHiaa2IgqTLQ7qmMKGFH
+olAqY/Yh3trY1fB/xQCCcOajv1yJJ09RkncDw7DMLjvsI/IvU0GviZP/0oCxQ5fe1hmgkhJ6PWZ5
+4cG84Xdwoos9RoRTP+ROQkE3kh4f/Tiz9++HOYDTVs/04BPeZLBypAOExEHtb/o+4soEINLX3CyC
+K3ribaEcSNvPiU80lz0oqFPa58HhcxWjMHZ/jyNCFD1RNNJarTyby8j+f26OQPO9AgMBAAGjZjBk
+MB8GA1UdIwQYMBaAFMUBrXzmY8mcF1/FoqfhUF/o9ajGMB0GA1UdDgQWBBTFAa185mPJnBdfxaKn
+4VBf6PWoxjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsF
+AAOCAQEAcbNdIcIO19DG+Epzh00iAifQx/j9Gm1iWIIIdiAHwEiS8+mYWusNTlaVY2hNq9QAduA3
+zwsRYVlc3valFFnZJZ9Z2dNehqwdpiwyQhkyE0ALVM1nJra9tJakyh9/N9aodes6gVEwuflKAW/R
+1u1P3z8wYAZnko5hhV8atYyzD2Gp+t9dxGQA6oexM199y6OFJG4sZTvqcz+G0/3o5ALGYWomF1IB
+JVx/qM5pH6xhLLcEr/2kepnLJhVM/3TUcwxXDCbr1yrcXMNBu8Lzzha9jnv76d+rIQ2Rs43Yz8j0
+SbnQ4xZwP7Pe1Acl+kZEUolNicjiyrUzf8chvSjv/mZ0Aw==
+-----END CERTIFICATE-----
diff --git a/samples/cert-ica-ec.pem b/samples/cert-ica-ec.pem
new file mode 100644 (file)
index 0000000..204ab76
--- /dev/null
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBqTCCAU6gAwIBAgIUINPr4oz+2uajLF478mY6KzZ7sMowCgYIKoZIzj0EAwIwHDELMAkGA1UE
+BhMCQ0ExDTALBgNVBAMTBFJvb3QwHhcNMTAwMTAxMDAwMDAwWhcNMzcxMjMxMjM1OTU5WjAnMQsw
+CQYDVQQGEwJDQTEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0D
+AQcDQgAEcC6SggEXbG2r4dFjCUhJ0qY1UtM8c7uyiDeYh/GN4Oxlmg4T9e2RYci2bTOEbq6OVYDN
+SZ4Hv9CunebQsycWoaNjMGEwHwYDVR0jBBgwFoAUlUG04meq8X+8j3nzaBRaa5IWokAwHQYDVR0O
+BBYEFPDQ8SL5ekgXBns9vbj1zVWcXD5wMA4GA1UdDwEB/wQEAwIAhjAPBgNVHRMBAf8EBTADAQH/
+MAoGCCqGSM49BAMCA0kAMEYCIQCF40ZomdYCellmHLdPNS0INjhhfgVI2GlDH+tW6a0GDgIhAIJw
+tGIDSUbIVFkF2XjbUxzgbmb1DxQ7yS04EnCRVvmp
+-----END CERTIFICATE-----
diff --git a/samples/cert-ica-rsa.pem b/samples/cert-ica-rsa.pem
new file mode 100644 (file)
index 0000000..058ffab
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIDNDCCAhygAwIBAgIUcA9g7vAHmpxprJdiJk9dBbb5j0gwDQYJKoZIhvcNAQELBQAwHDELMAkG
+A1UEBhMCQ0ExDTALBgNVBAMTBFJvb3QwHhcNMTAwMTAxMDAwMDAwWhcNMzcxMjMxMjM1OTU5WjAn
+MQswCQYDVQQGEwJDQTEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlIENBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAs+hrr5wWUuOBDFCrJc7MDcfyH39Q3yxcNdZiLmMnQafkU6hLJ/oTkaP6
+CUovO17Pd7OKwc1JlZx1DWR07+TXS7mhm2jSMHFI6vdLFN8/R6nYu+yPKMz637QflHyW/AgFKPno
+9C8v7mKcijrghVhgtg8tMLTAQVSRTB9frfEZ8MAipn3YP3k0WUJ7W7VBxGR/Us88NyKhL3kllCRB
+wj/6x3X7SLUNGKf0VPMubthDWMSrUOgFrZG2HgF1s1Sc3qCZFfus8VyXSVHM71gSb3NrszQUAQ9a
+nfqq1pPT4urDq7xO7cxRobj4lLa0LKiGKx/2UUMpUl4TibNqeGBOTsAbpQIDAQABo2MwYTAfBgNV
+HSMEGDAWgBTDCry0kGOWkkW8J6DwWIkq1XgAEjAdBgNVHQ4EFgQUfM+jxin388WqGdDAFuvgQA/O
+RKcwDgYDVR0PAQH/BAQDAgCGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFQ7
+9OrG5OjAWxKyrfq9qfRiA61XTG8Hp0c1dT5IoltxEAGPk5mdp0fjjj6vLboG/tTkl7wQjaalOjzm
+Ics72hPjSiPrvLqlkJGtVW7V3YVLayfSOXYGLtQjW7tVtUk/fS8hy5Z1GZmpmfELuz7HEKeLelK5
+SeQUCHjnPdmYV9r/2rmNZnWAtV2532ll2xbnHsRA5EaKHnYyFueDZ9p4VqsPTFzxcNpmIPT4D/bc
+L3KXa3hAeZ1bbb4DznBCqCpxEd8ugQHqhhKRT9AY7YSkSDC5uXtWPu+N4R/9kLJEhVhvpzB0fPGu
+jJk/8U1XxZVowjay7MJoesCBqVUF58+vUKw=
+-----END CERTIFICATE-----
diff --git a/samples/cert-root-ec.pem b/samples/cert-root-ec.pem
new file mode 100644 (file)
index 0000000..1bfd578
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBijCCATCgAwIBAgIBCTAKBggqhkjOPQQDAjAcMQswCQYDVQQGEwJDQTENMAsGA1UEAxMEUm9v
+dDAeFw0xMDAxMDEwMDAwMDBaFw0zNzEyMzEyMzU5NTlaMBwxCzAJBgNVBAYTAkNBMQ0wCwYDVQQD
+EwRSb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcXS6q7kwLoHV5Vf58yBoDJz5ZNu0IA1t
+6kDQSm5C/baaaCVE9t97xPze3Xu7xdt8dj9BZkBu26eHwuXYxfN/jaNjMGEwHwYDVR0jBBgwFoAU
+lUG04meq8X+8j3nzaBRaa5IWokAwHQYDVR0OBBYEFJVBtOJnqvF/vI9582gUWmuSFqJAMA4GA1Ud
+DwEB/wQEAwIAhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQCz1GCIAoRtqU2h
+x62hec7E+/XxnUGDORWnkliiGrcmxQIgNPq9NHD7ts0xYjTwZsd0bN62+iY93lM4LHbkNV9q/gA=
+-----END CERTIFICATE-----
diff --git a/samples/cert-root-rsa.pem b/samples/cert-root-rsa.pem
new file mode 100644 (file)
index 0000000..6b5ebd0
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIIDFjCCAf6gAwIBAgIBCDANBgkqhkiG9w0BAQsFADAcMQswCQYDVQQGEwJDQTENMAsGA1UEAxME
+Um9vdDAeFw0xMDAxMDEwMDAwMDBaFw0zNzEyMzEyMzU5NTlaMBwxCzAJBgNVBAYTAkNBMQ0wCwYD
+VQQDEwRSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAttk01FD9s696c/HOOL9d
+b0Xh/U6xmMZggybSF9HFt5qjwd5jOZec8F5cyBwXuYgZbfC2LjBQoVRuk8DbzzDLnx4nefHDmVI1
+qj2237CtfMtJzcDt52YQKunOKB8hUPp3TC3a7zxY606/zun7Gtqjg6PNo8qTgNza8xfMeqszgJyy
+1H9GP8U83GGUtycpbiq8Wwk21MY7Deu+ztsdHLwQanFxs/LKKJp38orsQu+xSo7i8hoyKs3ApkYs
+msKFN5F/RqGTgaF0Zt+6szkgkZP6HaGohefk+Qf2EPaoJwG2fxLDQMPJ4rCrSRg6ZLZZt5W1ljbf
+ImmqcmpUTicpow6XFQIDAQABo2MwYTAfBgNVHSMEGDAWgBTDCry0kGOWkkW8J6DwWIkq1XgAEjAd
+BgNVHQ4EFgQUwwq8tJBjlpJFvCeg8FiJKtV4ABIwDgYDVR0PAQH/BAQDAgCGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAA7SaR2V1Gx71RiL5Cdfr15jkU88snnm+v+0PvIfQrNP
+4RzKJkaD3j//oZf6Hr2yH6c7Bi3OhCxBFNbXjDXiAXVRoTjOSxNSwVq43utl9Z4IKAZzQlEYW5S5
+U0oskEq2q6jDEWoj8lUtQW4GAVuZhq7lNs0jJJu84IBsxHJFgZJ/7+LHFLXs+vQkz3hjzMZ7gIt+
+lwXI/gdY9ld1QB6WLLIzxs7zid+Z0LZTYi851vbmMwUqgL8V5Np0Q0EVHHy1c6LQ0hj1kVLay2ZN
+d2dsUMCQJI5EF2kWon+xFDpAtv0vUT2xuMkYFhjS/aBxzevWCsXuUQphBYaIGli8P68NNPo=
+-----END CERTIFICATE-----
diff --git a/samples/chain-ec+rsa.h b/samples/chain-ec+rsa.h
new file mode 100644 (file)
index 0000000..2a27ff5
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "bearssl.h"
+
+/*
+ * A sample server certificate chain with a single intermediate CA.
+ * Certificate key type: EC
+ * Signing algorithm for both certificates: RSA
+ */
+
+static const unsigned char CERT0[] = {
+       0x30, 0x82, 0x02, 0x71, 0x30, 0x82, 0x01, 0x59, 0xA0, 0x03, 0x02, 0x01,
+       0x02, 0x02, 0x14, 0x6E, 0x63, 0xBB, 0x49, 0xCE, 0x41, 0x7E, 0x03, 0x8C,
+       0xF5, 0x46, 0xF2, 0x8A, 0xAE, 0x61, 0x08, 0x35, 0xB0, 0x94, 0xB3, 0x30,
+       0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+       0x05, 0x00, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+       0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+       0x04, 0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64,
+       0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 0x31,
+       0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
+       0x17, 0x0D, 0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39,
+       0x35, 0x39, 0x5A, 0x30, 0x21, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+       0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
+       0x55, 0x04, 0x03, 0x13, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x68, 0x6F,
+       0x73, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE,
+       0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01,
+       0x07, 0x03, 0x42, 0x00, 0x04, 0x5F, 0x38, 0x9D, 0xA7, 0xFF, 0x4D, 0x8A,
+       0xAF, 0xF6, 0x34, 0x39, 0x46, 0x1A, 0xFC, 0x3A, 0xDF, 0xF4, 0x23, 0xAA,
+       0xA9, 0xEA, 0xFB, 0xC5, 0x08, 0xDE, 0x00, 0x8E, 0xBE, 0x79, 0xA5, 0x37,
+       0x58, 0x4C, 0x6D, 0xDD, 0x01, 0xCA, 0xAB, 0x47, 0xDF, 0x89, 0xB6, 0xC7,
+       0x17, 0x1F, 0x38, 0xFC, 0x1D, 0x20, 0x14, 0xDD, 0x45, 0xC0, 0xE0, 0x8F,
+       0x93, 0x4E, 0x38, 0x0B, 0xFC, 0xE9, 0x99, 0xA1, 0x49, 0xA3, 0x66, 0x30,
+       0x64, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16,
+       0x80, 0x14, 0x7C, 0xCF, 0xA3, 0xC6, 0x29, 0xF7, 0xF3, 0xC5, 0xAA, 0x19,
+       0xD0, 0xC0, 0x16, 0xEB, 0xE0, 0x40, 0x0F, 0xCE, 0x44, 0xA7, 0x30, 0x1D,
+       0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xC3, 0x0E, 0x86,
+       0xAA, 0x75, 0xB4, 0x15, 0xC0, 0xE5, 0x95, 0x09, 0x32, 0xBE, 0x5E, 0x92,
+       0x75, 0xA9, 0xE4, 0x44, 0x9B, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13,
+       0x01, 0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, 0x30, 0x14, 0x06, 0x03, 0x55,
+       0x1D, 0x11, 0x04, 0x0D, 0x30, 0x0B, 0x82, 0x09, 0x6C, 0x6F, 0x63, 0x61,
+       0x6C, 0x68, 0x6F, 0x73, 0x74, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48,
+       0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+       0x00, 0x8E, 0x89, 0x63, 0xA7, 0x9B, 0xA8, 0x10, 0x8E, 0x9E, 0x18, 0xD0,
+       0xA4, 0x7A, 0xDD, 0x2C, 0x82, 0x16, 0x8F, 0x0B, 0x0E, 0xD5, 0xCA, 0x57,
+       0xBB, 0x5F, 0x6F, 0xFC, 0x57, 0x28, 0xC0, 0x21, 0xBA, 0xAD, 0x94, 0xA8,
+       0x17, 0x42, 0x45, 0xD7, 0x31, 0xF7, 0x84, 0x57, 0x19, 0x46, 0x24, 0x3C,
+       0xC7, 0x49, 0x38, 0x3F, 0x48, 0x7A, 0x09, 0x1A, 0xEB, 0x51, 0xBA, 0xC5,
+       0x75, 0x8E, 0x4C, 0xB2, 0x25, 0xB0, 0xC7, 0x22, 0x3E, 0xE7, 0xE5, 0x46,
+       0xDB, 0xB1, 0x36, 0xFC, 0x1B, 0x85, 0x51, 0xB0, 0xED, 0x83, 0x16, 0xF0,
+       0x3A, 0x79, 0xDA, 0x5D, 0x28, 0x6A, 0xEE, 0x6A, 0x20, 0x4C, 0xB0, 0x70,
+       0x5E, 0xB0, 0xED, 0x7A, 0x6D, 0x2D, 0x1A, 0xC9, 0xB4, 0x71, 0xC2, 0xC4,
+       0xA3, 0x1D, 0x25, 0x86, 0x30, 0x54, 0xB1, 0x98, 0x26, 0xC1, 0x51, 0xE0,
+       0xF8, 0x31, 0xAD, 0x67, 0x48, 0x9D, 0xE6, 0xA1, 0x52, 0x3B, 0x20, 0x47,
+       0xC9, 0x70, 0xDA, 0x80, 0xB5, 0xA4, 0xF4, 0x3F, 0x72, 0x6B, 0x37, 0x30,
+       0x57, 0x39, 0x2C, 0x46, 0x9B, 0x44, 0xF1, 0xD9, 0x4E, 0xBB, 0xA6, 0x5F,
+       0xE3, 0x9A, 0xC5, 0x7F, 0x6C, 0xB6, 0x65, 0xCB, 0xBF, 0x40, 0x72, 0x92,
+       0xD0, 0x68, 0xF6, 0xAE, 0x79, 0x7E, 0x24, 0xC1, 0x5B, 0x68, 0x30, 0x28,
+       0x53, 0x71, 0x61, 0x7A, 0xF4, 0x36, 0x7B, 0x3B, 0xC2, 0xB6, 0xEC, 0x1F,
+       0xDC, 0x30, 0x40, 0x04, 0x0D, 0xEA, 0xB3, 0x0A, 0xCF, 0x75, 0xD3, 0xBD,
+       0x9E, 0xAB, 0x71, 0x71, 0x68, 0x7F, 0xA5, 0x41, 0x52, 0x00, 0xE9, 0xB3,
+       0xAE, 0xE4, 0x1E, 0xF1, 0xBB, 0x69, 0xC6, 0x3B, 0xD5, 0xDE, 0xD2, 0xC2,
+       0x25, 0x69, 0x08, 0x3C, 0x37, 0xCC, 0xF7, 0x82, 0x6A, 0xE5, 0xA8, 0x7B,
+       0x6A, 0x02, 0xDA, 0xBD, 0xA4, 0x01, 0x33, 0x33, 0xF3, 0xDE, 0x46, 0x00,
+       0xEC, 0x75, 0xCF, 0xE0, 0x7D
+};
+
+static const unsigned char CERT1[] = {
+       0x30, 0x82, 0x03, 0x34, 0x30, 0x82, 0x02, 0x1C, 0xA0, 0x03, 0x02, 0x01,
+       0x02, 0x02, 0x14, 0x70, 0x0F, 0x60, 0xEE, 0xF0, 0x07, 0x9A, 0x9C, 0x69,
+       0xAC, 0x97, 0x62, 0x26, 0x4F, 0x5D, 0x05, 0xB6, 0xF9, 0x8F, 0x48, 0x30,
+       0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+       0x05, 0x00, 0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+       0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55,
+       0x04, 0x03, 0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74, 0x30, 0x1E, 0x17, 0x0D,
+       0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+       0x5A, 0x17, 0x0D, 0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35,
+       0x39, 0x35, 0x39, 0x5A, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03,
+       0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06,
+       0x03, 0x55, 0x04, 0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D,
+       0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+       0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
+       0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01,
+       0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xB3, 0xE8, 0x6B, 0xAF, 0x9C, 0x16,
+       0x52, 0xE3, 0x81, 0x0C, 0x50, 0xAB, 0x25, 0xCE, 0xCC, 0x0D, 0xC7, 0xF2,
+       0x1F, 0x7F, 0x50, 0xDF, 0x2C, 0x5C, 0x35, 0xD6, 0x62, 0x2E, 0x63, 0x27,
+       0x41, 0xA7, 0xE4, 0x53, 0xA8, 0x4B, 0x27, 0xFA, 0x13, 0x91, 0xA3, 0xFA,
+       0x09, 0x4A, 0x2F, 0x3B, 0x5E, 0xCF, 0x77, 0xB3, 0x8A, 0xC1, 0xCD, 0x49,
+       0x95, 0x9C, 0x75, 0x0D, 0x64, 0x74, 0xEF, 0xE4, 0xD7, 0x4B, 0xB9, 0xA1,
+       0x9B, 0x68, 0xD2, 0x30, 0x71, 0x48, 0xEA, 0xF7, 0x4B, 0x14, 0xDF, 0x3F,
+       0x47, 0xA9, 0xD8, 0xBB, 0xEC, 0x8F, 0x28, 0xCC, 0xFA, 0xDF, 0xB4, 0x1F,
+       0x94, 0x7C, 0x96, 0xFC, 0x08, 0x05, 0x28, 0xF9, 0xE8, 0xF4, 0x2F, 0x2F,
+       0xEE, 0x62, 0x9C, 0x8A, 0x3A, 0xE0, 0x85, 0x58, 0x60, 0xB6, 0x0F, 0x2D,
+       0x30, 0xB4, 0xC0, 0x41, 0x54, 0x91, 0x4C, 0x1F, 0x5F, 0xAD, 0xF1, 0x19,
+       0xF0, 0xC0, 0x22, 0xA6, 0x7D, 0xD8, 0x3F, 0x79, 0x34, 0x59, 0x42, 0x7B,
+       0x5B, 0xB5, 0x41, 0xC4, 0x64, 0x7F, 0x52, 0xCF, 0x3C, 0x37, 0x22, 0xA1,
+       0x2F, 0x79, 0x25, 0x94, 0x24, 0x41, 0xC2, 0x3F, 0xFA, 0xC7, 0x75, 0xFB,
+       0x48, 0xB5, 0x0D, 0x18, 0xA7, 0xF4, 0x54, 0xF3, 0x2E, 0x6E, 0xD8, 0x43,
+       0x58, 0xC4, 0xAB, 0x50, 0xE8, 0x05, 0xAD, 0x91, 0xB6, 0x1E, 0x01, 0x75,
+       0xB3, 0x54, 0x9C, 0xDE, 0xA0, 0x99, 0x15, 0xFB, 0xAC, 0xF1, 0x5C, 0x97,
+       0x49, 0x51, 0xCC, 0xEF, 0x58, 0x12, 0x6F, 0x73, 0x6B, 0xB3, 0x34, 0x14,
+       0x01, 0x0F, 0x5A, 0x9D, 0xFA, 0xAA, 0xD6, 0x93, 0xD3, 0xE2, 0xEA, 0xC3,
+       0xAB, 0xBC, 0x4E, 0xED, 0xCC, 0x51, 0xA1, 0xB8, 0xF8, 0x94, 0xB6, 0xB4,
+       0x2C, 0xA8, 0x86, 0x2B, 0x1F, 0xF6, 0x51, 0x43, 0x29, 0x52, 0x5E, 0x13,
+       0x89, 0xB3, 0x6A, 0x78, 0x60, 0x4E, 0x4E, 0xC0, 0x1B, 0xA5, 0x02, 0x03,
+       0x01, 0x00, 0x01, 0xA3, 0x63, 0x30, 0x61, 0x30, 0x1F, 0x06, 0x03, 0x55,
+       0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xC3, 0x0A, 0xBC, 0xB4,
+       0x90, 0x63, 0x96, 0x92, 0x45, 0xBC, 0x27, 0xA0, 0xF0, 0x58, 0x89, 0x2A,
+       0xD5, 0x78, 0x00, 0x12, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04,
+       0x16, 0x04, 0x14, 0x7C, 0xCF, 0xA3, 0xC6, 0x29, 0xF7, 0xF3, 0xC5, 0xAA,
+       0x19, 0xD0, 0xC0, 0x16, 0xEB, 0xE0, 0x40, 0x0F, 0xCE, 0x44, 0xA7, 0x30,
+       0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03,
+       0x02, 0x00, 0x86, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01,
+       0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0D, 0x06, 0x09,
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03,
+       0x82, 0x01, 0x01, 0x00, 0x54, 0x3B, 0xF4, 0xEA, 0xC6, 0xE4, 0xE8, 0xC0,
+       0x5B, 0x12, 0xB2, 0xAD, 0xFA, 0xBD, 0xA9, 0xF4, 0x62, 0x03, 0xAD, 0x57,
+       0x4C, 0x6F, 0x07, 0xA7, 0x47, 0x35, 0x75, 0x3E, 0x48, 0xA2, 0x5B, 0x71,
+       0x10, 0x01, 0x8F, 0x93, 0x99, 0x9D, 0xA7, 0x47, 0xE3, 0x8E, 0x3E, 0xAF,
+       0x2D, 0xBA, 0x06, 0xFE, 0xD4, 0xE4, 0x97, 0xBC, 0x10, 0x8D, 0xA6, 0xA5,
+       0x3A, 0x3C, 0xE6, 0x21, 0xCB, 0x3B, 0xDA, 0x13, 0xE3, 0x4A, 0x23, 0xEB,
+       0xBC, 0xBA, 0xA5, 0x90, 0x91, 0xAD, 0x55, 0x6E, 0xD5, 0xDD, 0x85, 0x4B,
+       0x6B, 0x27, 0xD2, 0x39, 0x76, 0x06, 0x2E, 0xD4, 0x23, 0x5B, 0xBB, 0x55,
+       0xB5, 0x49, 0x3F, 0x7D, 0x2F, 0x21, 0xCB, 0x96, 0x75, 0x19, 0x99, 0xA9,
+       0x99, 0xF1, 0x0B, 0xBB, 0x3E, 0xC7, 0x10, 0xA7, 0x8B, 0x7A, 0x52, 0xB9,
+       0x49, 0xE4, 0x14, 0x08, 0x78, 0xE7, 0x3D, 0xD9, 0x98, 0x57, 0xDA, 0xFF,
+       0xDA, 0xB9, 0x8D, 0x66, 0x75, 0x80, 0xB5, 0x5D, 0xB9, 0xDF, 0x69, 0x65,
+       0xDB, 0x16, 0xE7, 0x1E, 0xC4, 0x40, 0xE4, 0x46, 0x8A, 0x1E, 0x76, 0x32,
+       0x16, 0xE7, 0x83, 0x67, 0xDA, 0x78, 0x56, 0xAB, 0x0F, 0x4C, 0x5C, 0xF1,
+       0x70, 0xDA, 0x66, 0x20, 0xF4, 0xF8, 0x0F, 0xF6, 0xDC, 0x2F, 0x72, 0x97,
+       0x6B, 0x78, 0x40, 0x79, 0x9D, 0x5B, 0x6D, 0xBE, 0x03, 0xCE, 0x70, 0x42,
+       0xA8, 0x2A, 0x71, 0x11, 0xDF, 0x2E, 0x81, 0x01, 0xEA, 0x86, 0x12, 0x91,
+       0x4F, 0xD0, 0x18, 0xED, 0x84, 0xA4, 0x48, 0x30, 0xB9, 0xB9, 0x7B, 0x56,
+       0x3E, 0xEF, 0x8D, 0xE1, 0x1F, 0xFD, 0x90, 0xB2, 0x44, 0x85, 0x58, 0x6F,
+       0xA7, 0x30, 0x74, 0x7C, 0xF1, 0xAE, 0x8C, 0x99, 0x3F, 0xF1, 0x4D, 0x57,
+       0xC5, 0x95, 0x68, 0xC2, 0x36, 0xB2, 0xEC, 0xC2, 0x68, 0x7A, 0xC0, 0x81,
+       0xA9, 0x55, 0x05, 0xE7, 0xCF, 0xAF, 0x50, 0xAC
+};
+
+static const br_x509_certificate CHAIN[] = {
+       { (unsigned char *)CERT0, sizeof CERT0 },
+       { (unsigned char *)CERT1, sizeof CERT1 }
+};
+
+#define CHAIN_LEN   2
diff --git a/samples/chain-ec.h b/samples/chain-ec.h
new file mode 100644 (file)
index 0000000..b4927a3
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "bearssl.h"
+
+/*
+ * A sample server certificate chain with a single intermediate CA.
+ * Certificate key type: EC
+ * Signing algorithm for both certificates: ECDSA
+ */
+
+static const unsigned char CERT0[] = {
+       0x30, 0x82, 0x01, 0xB0, 0x30, 0x82, 0x01, 0x56, 0xA0, 0x03, 0x02, 0x01,
+       0x02, 0x02, 0x14, 0x1C, 0x4D, 0x00, 0x91, 0x69, 0xE2, 0x46, 0xAC, 0x90,
+       0x7C, 0x64, 0x5C, 0x53, 0xF1, 0xFF, 0xB7, 0xC1, 0xCB, 0x6E, 0x7A, 0x30,
+       0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30,
+       0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+       0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+       0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74,
+       0x65, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x30, 0x30, 0x31,
+       0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x33,
+       0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5A,
+       0x30, 0x21, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+       0x02, 0x43, 0x41, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03,
+       0x13, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x68, 0x6F, 0x73, 0x74, 0x30,
+       0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
+       0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42,
+       0x00, 0x04, 0x5F, 0x38, 0x9D, 0xA7, 0xFF, 0x4D, 0x8A, 0xAF, 0xF6, 0x34,
+       0x39, 0x46, 0x1A, 0xFC, 0x3A, 0xDF, 0xF4, 0x23, 0xAA, 0xA9, 0xEA, 0xFB,
+       0xC5, 0x08, 0xDE, 0x00, 0x8E, 0xBE, 0x79, 0xA5, 0x37, 0x58, 0x4C, 0x6D,
+       0xDD, 0x01, 0xCA, 0xAB, 0x47, 0xDF, 0x89, 0xB6, 0xC7, 0x17, 0x1F, 0x38,
+       0xFC, 0x1D, 0x20, 0x14, 0xDD, 0x45, 0xC0, 0xE0, 0x8F, 0x93, 0x4E, 0x38,
+       0x0B, 0xFC, 0xE9, 0x99, 0xA1, 0x49, 0xA3, 0x66, 0x30, 0x64, 0x30, 0x1F,
+       0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xF0,
+       0xD0, 0xF1, 0x22, 0xF9, 0x7A, 0x48, 0x17, 0x06, 0x7B, 0x3D, 0xBD, 0xB8,
+       0xF5, 0xCD, 0x55, 0x9C, 0x5C, 0x3E, 0x70, 0x30, 0x1D, 0x06, 0x03, 0x55,
+       0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xC3, 0x0E, 0x86, 0xAA, 0x75, 0xB4,
+       0x15, 0xC0, 0xE5, 0x95, 0x09, 0x32, 0xBE, 0x5E, 0x92, 0x75, 0xA9, 0xE4,
+       0x44, 0x9B, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF,
+       0x04, 0x02, 0x30, 0x00, 0x30, 0x14, 0x06, 0x03, 0x55, 0x1D, 0x11, 0x04,
+       0x0D, 0x30, 0x0B, 0x82, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x68, 0x6F,
+       0x73, 0x74, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04,
+       0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00, 0x91, 0xFB,
+       0xF4, 0x04, 0xD0, 0xE5, 0x2E, 0x01, 0xD4, 0x8C, 0xF0, 0x17, 0x62, 0x0F,
+       0xDC, 0xCC, 0x80, 0xCA, 0x18, 0xC4, 0x40, 0x7C, 0x27, 0x03, 0xCB, 0x34,
+       0x03, 0x0D, 0x9B, 0xC8, 0x59, 0x4D, 0x02, 0x20, 0x05, 0x55, 0x69, 0xE2,
+       0xD8, 0xA1, 0x40, 0x33, 0x34, 0x0E, 0x7E, 0x49, 0x32, 0x64, 0x1D, 0x3F,
+       0x6B, 0x1F, 0xD0, 0x2D, 0xB7, 0x2F, 0x52, 0x04, 0x56, 0xAF, 0xD3, 0x37,
+       0x8F, 0x87, 0x99, 0xA2
+};
+
+static const unsigned char CERT1[] = {
+       0x30, 0x82, 0x01, 0xA9, 0x30, 0x82, 0x01, 0x4E, 0xA0, 0x03, 0x02, 0x01,
+       0x02, 0x02, 0x14, 0x20, 0xD3, 0xEB, 0xE2, 0x8C, 0xFE, 0xDA, 0xE6, 0xA3,
+       0x2C, 0x5E, 0x3B, 0xF2, 0x66, 0x3A, 0x2B, 0x36, 0x7B, 0xB0, 0xCA, 0x30,
+       0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30,
+       0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+       0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+       0x04, 0x52, 0x6F, 0x6F, 0x74, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x30, 0x30,
+       0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D,
+       0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39,
+       0x5A, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+       0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04,
+       0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64, 0x69,
+       0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+       0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
+       0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x70, 0x2E, 0x92,
+       0x82, 0x01, 0x17, 0x6C, 0x6D, 0xAB, 0xE1, 0xD1, 0x63, 0x09, 0x48, 0x49,
+       0xD2, 0xA6, 0x35, 0x52, 0xD3, 0x3C, 0x73, 0xBB, 0xB2, 0x88, 0x37, 0x98,
+       0x87, 0xF1, 0x8D, 0xE0, 0xEC, 0x65, 0x9A, 0x0E, 0x13, 0xF5, 0xED, 0x91,
+       0x61, 0xC8, 0xB6, 0x6D, 0x33, 0x84, 0x6E, 0xAE, 0x8E, 0x55, 0x80, 0xCD,
+       0x49, 0x9E, 0x07, 0xBF, 0xD0, 0xAE, 0x9D, 0xE6, 0xD0, 0xB3, 0x27, 0x16,
+       0xA1, 0xA3, 0x63, 0x30, 0x61, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23,
+       0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x95, 0x41, 0xB4, 0xE2, 0x67, 0xAA,
+       0xF1, 0x7F, 0xBC, 0x8F, 0x79, 0xF3, 0x68, 0x14, 0x5A, 0x6B, 0x92, 0x16,
+       0xA2, 0x40, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04,
+       0x14, 0xF0, 0xD0, 0xF1, 0x22, 0xF9, 0x7A, 0x48, 0x17, 0x06, 0x7B, 0x3D,
+       0xBD, 0xB8, 0xF5, 0xCD, 0x55, 0x9C, 0x5C, 0x3E, 0x70, 0x30, 0x0E, 0x06,
+       0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x00,
+       0x86, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04,
+       0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86,
+       0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02,
+       0x21, 0x00, 0x85, 0xE3, 0x46, 0x68, 0x99, 0xD6, 0x02, 0x7A, 0x59, 0x66,
+       0x1C, 0xB7, 0x4F, 0x35, 0x2D, 0x08, 0x36, 0x38, 0x61, 0x7E, 0x05, 0x48,
+       0xD8, 0x69, 0x43, 0x1F, 0xEB, 0x56, 0xE9, 0xAD, 0x06, 0x0E, 0x02, 0x21,
+       0x00, 0x82, 0x70, 0xB4, 0x62, 0x03, 0x49, 0x46, 0xC8, 0x54, 0x59, 0x05,
+       0xD9, 0x78, 0xDB, 0x53, 0x1C, 0xE0, 0x6E, 0x66, 0xF5, 0x0F, 0x14, 0x3B,
+       0xC9, 0x2D, 0x38, 0x12, 0x70, 0x91, 0x56, 0xF9, 0xA9
+};
+
+static const br_x509_certificate CHAIN[] = {
+       { (unsigned char *)CERT0, sizeof CERT0 },
+       { (unsigned char *)CERT1, sizeof CERT1 }
+};
+
+#define CHAIN_LEN   2
diff --git a/samples/chain-rsa.h b/samples/chain-rsa.h
new file mode 100644 (file)
index 0000000..f838789
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "bearssl.h"
+
+/*
+ * A sample server certificate chain with a single intermediate CA.
+ * Certificate key type: RSA
+ * Signing algorithm for both certificates: RSA
+ */
+
+static const unsigned char CERT0[] = {
+       0x30, 0x82, 0x03, 0x3C, 0x30, 0x82, 0x02, 0x24, 0xA0, 0x03, 0x02, 0x01,
+       0x02, 0x02, 0x14, 0x58, 0xDA, 0xBA, 0x36, 0xCD, 0xED, 0xA0, 0xDA, 0x5C,
+       0x10, 0x33, 0x73, 0x8E, 0x0C, 0x64, 0x92, 0x79, 0x92, 0xAF, 0x03, 0x30,
+       0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+       0x05, 0x00, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+       0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+       0x04, 0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64,
+       0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 0x31,
+       0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
+       0x17, 0x0D, 0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39,
+       0x35, 0x39, 0x5A, 0x30, 0x21, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+       0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
+       0x55, 0x04, 0x03, 0x13, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x68, 0x6F,
+       0x73, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86,
+       0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+       0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD4,
+       0x7A, 0x1D, 0x27, 0xBA, 0x2B, 0x3A, 0x67, 0xB2, 0x91, 0x6A, 0xFB, 0xE7,
+       0x83, 0x44, 0xCA, 0xED, 0x1C, 0x75, 0xAD, 0xDD, 0x4D, 0x83, 0x62, 0xD6,
+       0xAA, 0x68, 0x95, 0xB2, 0x24, 0x21, 0x7B, 0x15, 0xAE, 0x2A, 0x99, 0x68,
+       0x15, 0xED, 0x66, 0xF0, 0xB8, 0x58, 0xE7, 0xD3, 0xF5, 0x2E, 0xC6, 0xD9,
+       0x2A, 0x5E, 0xE7, 0x0E, 0x2E, 0xE7, 0xFC, 0x67, 0x59, 0xC0, 0xC8, 0x61,
+       0x7D, 0x4B, 0xA4, 0x6F, 0xDD, 0x9F, 0xD9, 0xC8, 0x85, 0x87, 0x64, 0xC7,
+       0xBA, 0x1A, 0x0F, 0x29, 0xD4, 0x96, 0xA8, 0x78, 0x9A, 0x6B, 0x62, 0x20,
+       0xA9, 0x32, 0xD0, 0xEE, 0xA9, 0x8C, 0x28, 0x61, 0x47, 0xA2, 0x50, 0x2A,
+       0x63, 0xF6, 0x21, 0xDE, 0xDA, 0xD8, 0xD5, 0xF0, 0x7F, 0xC5, 0x00, 0x82,
+       0x70, 0xE6, 0xA3, 0xBF, 0x5C, 0x89, 0x27, 0x4F, 0x51, 0x92, 0x77, 0x03,
+       0xC3, 0xB0, 0xCC, 0x2E, 0x3B, 0xEC, 0x23, 0xF2, 0x2F, 0x53, 0x41, 0xAF,
+       0x89, 0x93, 0xFF, 0xD2, 0x80, 0xB1, 0x43, 0x97, 0xDE, 0xD6, 0x19, 0xA0,
+       0x92, 0x12, 0x7A, 0x3D, 0x66, 0x79, 0xE1, 0xC1, 0xBC, 0xE1, 0x77, 0x70,
+       0xA2, 0x8B, 0x3D, 0x46, 0x84, 0x53, 0x3F, 0xE4, 0x4E, 0x42, 0x41, 0x37,
+       0x92, 0x1E, 0x1F, 0xFD, 0x38, 0xB3, 0xF7, 0xEF, 0x87, 0x39, 0x80, 0xD3,
+       0x56, 0xCF, 0xF4, 0xE0, 0x13, 0xDE, 0x64, 0xB0, 0x72, 0xA4, 0x03, 0x84,
+       0xC4, 0x41, 0xED, 0x6F, 0xFA, 0x3E, 0xE2, 0xCA, 0x04, 0x20, 0xD2, 0xD7,
+       0xDC, 0x2C, 0x82, 0x2B, 0x7A, 0xE2, 0x6D, 0xA1, 0x1C, 0x48, 0xDB, 0xCF,
+       0x89, 0x4F, 0x34, 0x97, 0x3D, 0x28, 0xA8, 0x53, 0xDA, 0xE7, 0xC1, 0xE1,
+       0x73, 0x15, 0xA3, 0x30, 0x76, 0x7F, 0x8F, 0x23, 0x42, 0x14, 0x3D, 0x51,
+       0x34, 0xD2, 0x5A, 0xAD, 0x3C, 0x9B, 0xCB, 0xC8, 0xFE, 0x7F, 0x6E, 0x8E,
+       0x40, 0xF3, 0xBD, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x66, 0x30, 0x64,
+       0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+       0x14, 0xC5, 0x01, 0xAD, 0x7C, 0xE6, 0x63, 0xC9, 0x9C, 0x17, 0x5F, 0xC5,
+       0xA2, 0xA7, 0xE1, 0x50, 0x5F, 0xE8, 0xF5, 0xA8, 0xC6, 0x30, 0x1D, 0x06,
+       0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xC5, 0x01, 0xAD, 0x7C,
+       0xE6, 0x63, 0xC9, 0x9C, 0x17, 0x5F, 0xC5, 0xA2, 0xA7, 0xE1, 0x50, 0x5F,
+       0xE8, 0xF5, 0xA8, 0xC6, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01,
+       0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, 0x30, 0x14, 0x06, 0x03, 0x55, 0x1D,
+       0x11, 0x04, 0x0D, 0x30, 0x0B, 0x82, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C,
+       0x68, 0x6F, 0x73, 0x74, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+       0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+       0x71, 0xB3, 0x5D, 0x21, 0xC2, 0x0E, 0xD7, 0xD0, 0xC6, 0xF8, 0x4A, 0x73,
+       0x87, 0x4D, 0x22, 0x02, 0x27, 0xD0, 0xC7, 0xF8, 0xFD, 0x1A, 0x6D, 0x62,
+       0x58, 0x82, 0x08, 0x76, 0x20, 0x07, 0xC0, 0x48, 0x92, 0xF3, 0xE9, 0x98,
+       0x5A, 0xEB, 0x0D, 0x4E, 0x56, 0x95, 0x63, 0x68, 0x4D, 0xAB, 0xD4, 0x00,
+       0x76, 0xE0, 0x37, 0xCF, 0x0B, 0x11, 0x61, 0x59, 0x5C, 0xDE, 0xF6, 0xA5,
+       0x14, 0x59, 0xD9, 0x25, 0x9F, 0x59, 0xD9, 0xD3, 0x5E, 0x86, 0xAC, 0x1D,
+       0xA6, 0x2C, 0x32, 0x42, 0x19, 0x32, 0x13, 0x40, 0x0B, 0x54, 0xCD, 0x67,
+       0x26, 0xB6, 0xBD, 0xB4, 0x96, 0xA4, 0xCA, 0x1F, 0x7F, 0x37, 0xD6, 0xA8,
+       0x75, 0xEB, 0x3A, 0x81, 0x51, 0x30, 0xB9, 0xF9, 0x4A, 0x01, 0x6F, 0xD1,
+       0xD6, 0xED, 0x4F, 0xDF, 0x3F, 0x30, 0x60, 0x06, 0x67, 0x92, 0x8E, 0x61,
+       0x85, 0x5F, 0x1A, 0xB5, 0x8C, 0xB3, 0x0F, 0x61, 0xA9, 0xFA, 0xDF, 0x5D,
+       0xC4, 0x64, 0x00, 0xEA, 0x87, 0xB1, 0x33, 0x5F, 0x7D, 0xCB, 0xA3, 0x85,
+       0x24, 0x6E, 0x2C, 0x65, 0x3B, 0xEA, 0x73, 0x3F, 0x86, 0xD3, 0xFD, 0xE8,
+       0xE4, 0x02, 0xC6, 0x61, 0x6A, 0x26, 0x17, 0x52, 0x01, 0x25, 0x5C, 0x7F,
+       0xA8, 0xCE, 0x69, 0x1F, 0xAC, 0x61, 0x2C, 0xB7, 0x04, 0xAF, 0xFD, 0xA4,
+       0x7A, 0x99, 0xCB, 0x26, 0x15, 0x4C, 0xFF, 0x74, 0xD4, 0x73, 0x0C, 0x57,
+       0x0C, 0x26, 0xEB, 0xD7, 0x2A, 0xDC, 0x5C, 0xC3, 0x41, 0xBB, 0xC2, 0xF3,
+       0xCE, 0x16, 0xBD, 0x8E, 0x7B, 0xFB, 0xE9, 0xDF, 0xAB, 0x21, 0x0D, 0x91,
+       0xB3, 0x8D, 0xD8, 0xCF, 0xC8, 0xF4, 0x49, 0xB9, 0xD0, 0xE3, 0x16, 0x70,
+       0x3F, 0xB3, 0xDE, 0xD4, 0x07, 0x25, 0xFA, 0x46, 0x44, 0x52, 0x89, 0x4D,
+       0x89, 0xC8, 0xE2, 0xCA, 0xB5, 0x33, 0x7F, 0xC7, 0x21, 0xBD, 0x28, 0xEF,
+       0xFE, 0x66, 0x74, 0x03
+};
+
+static const unsigned char CERT1[] = {
+       0x30, 0x82, 0x03, 0x34, 0x30, 0x82, 0x02, 0x1C, 0xA0, 0x03, 0x02, 0x01,
+       0x02, 0x02, 0x14, 0x70, 0x0F, 0x60, 0xEE, 0xF0, 0x07, 0x9A, 0x9C, 0x69,
+       0xAC, 0x97, 0x62, 0x26, 0x4F, 0x5D, 0x05, 0xB6, 0xF9, 0x8F, 0x48, 0x30,
+       0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+       0x05, 0x00, 0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+       0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55,
+       0x04, 0x03, 0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74, 0x30, 0x1E, 0x17, 0x0D,
+       0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+       0x5A, 0x17, 0x0D, 0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35,
+       0x39, 0x35, 0x39, 0x5A, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03,
+       0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06,
+       0x03, 0x55, 0x04, 0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D,
+       0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+       0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
+       0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01,
+       0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xB3, 0xE8, 0x6B, 0xAF, 0x9C, 0x16,
+       0x52, 0xE3, 0x81, 0x0C, 0x50, 0xAB, 0x25, 0xCE, 0xCC, 0x0D, 0xC7, 0xF2,
+       0x1F, 0x7F, 0x50, 0xDF, 0x2C, 0x5C, 0x35, 0xD6, 0x62, 0x2E, 0x63, 0x27,
+       0x41, 0xA7, 0xE4, 0x53, 0xA8, 0x4B, 0x27, 0xFA, 0x13, 0x91, 0xA3, 0xFA,
+       0x09, 0x4A, 0x2F, 0x3B, 0x5E, 0xCF, 0x77, 0xB3, 0x8A, 0xC1, 0xCD, 0x49,
+       0x95, 0x9C, 0x75, 0x0D, 0x64, 0x74, 0xEF, 0xE4, 0xD7, 0x4B, 0xB9, 0xA1,
+       0x9B, 0x68, 0xD2, 0x30, 0x71, 0x48, 0xEA, 0xF7, 0x4B, 0x14, 0xDF, 0x3F,
+       0x47, 0xA9, 0xD8, 0xBB, 0xEC, 0x8F, 0x28, 0xCC, 0xFA, 0xDF, 0xB4, 0x1F,
+       0x94, 0x7C, 0x96, 0xFC, 0x08, 0x05, 0x28, 0xF9, 0xE8, 0xF4, 0x2F, 0x2F,
+       0xEE, 0x62, 0x9C, 0x8A, 0x3A, 0xE0, 0x85, 0x58, 0x60, 0xB6, 0x0F, 0x2D,
+       0x30, 0xB4, 0xC0, 0x41, 0x54, 0x91, 0x4C, 0x1F, 0x5F, 0xAD, 0xF1, 0x19,
+       0xF0, 0xC0, 0x22, 0xA6, 0x7D, 0xD8, 0x3F, 0x79, 0x34, 0x59, 0x42, 0x7B,
+       0x5B, 0xB5, 0x41, 0xC4, 0x64, 0x7F, 0x52, 0xCF, 0x3C, 0x37, 0x22, 0xA1,
+       0x2F, 0x79, 0x25, 0x94, 0x24, 0x41, 0xC2, 0x3F, 0xFA, 0xC7, 0x75, 0xFB,
+       0x48, 0xB5, 0x0D, 0x18, 0xA7, 0xF4, 0x54, 0xF3, 0x2E, 0x6E, 0xD8, 0x43,
+       0x58, 0xC4, 0xAB, 0x50, 0xE8, 0x05, 0xAD, 0x91, 0xB6, 0x1E, 0x01, 0x75,
+       0xB3, 0x54, 0x9C, 0xDE, 0xA0, 0x99, 0x15, 0xFB, 0xAC, 0xF1, 0x5C, 0x97,
+       0x49, 0x51, 0xCC, 0xEF, 0x58, 0x12, 0x6F, 0x73, 0x6B, 0xB3, 0x34, 0x14,
+       0x01, 0x0F, 0x5A, 0x9D, 0xFA, 0xAA, 0xD6, 0x93, 0xD3, 0xE2, 0xEA, 0xC3,
+       0xAB, 0xBC, 0x4E, 0xED, 0xCC, 0x51, 0xA1, 0xB8, 0xF8, 0x94, 0xB6, 0xB4,
+       0x2C, 0xA8, 0x86, 0x2B, 0x1F, 0xF6, 0x51, 0x43, 0x29, 0x52, 0x5E, 0x13,
+       0x89, 0xB3, 0x6A, 0x78, 0x60, 0x4E, 0x4E, 0xC0, 0x1B, 0xA5, 0x02, 0x03,
+       0x01, 0x00, 0x01, 0xA3, 0x63, 0x30, 0x61, 0x30, 0x1F, 0x06, 0x03, 0x55,
+       0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xC3, 0x0A, 0xBC, 0xB4,
+       0x90, 0x63, 0x96, 0x92, 0x45, 0xBC, 0x27, 0xA0, 0xF0, 0x58, 0x89, 0x2A,
+       0xD5, 0x78, 0x00, 0x12, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04,
+       0x16, 0x04, 0x14, 0x7C, 0xCF, 0xA3, 0xC6, 0x29, 0xF7, 0xF3, 0xC5, 0xAA,
+       0x19, 0xD0, 0xC0, 0x16, 0xEB, 0xE0, 0x40, 0x0F, 0xCE, 0x44, 0xA7, 0x30,
+       0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03,
+       0x02, 0x00, 0x86, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01,
+       0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0D, 0x06, 0x09,
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03,
+       0x82, 0x01, 0x01, 0x00, 0x54, 0x3B, 0xF4, 0xEA, 0xC6, 0xE4, 0xE8, 0xC0,
+       0x5B, 0x12, 0xB2, 0xAD, 0xFA, 0xBD, 0xA9, 0xF4, 0x62, 0x03, 0xAD, 0x57,
+       0x4C, 0x6F, 0x07, 0xA7, 0x47, 0x35, 0x75, 0x3E, 0x48, 0xA2, 0x5B, 0x71,
+       0x10, 0x01, 0x8F, 0x93, 0x99, 0x9D, 0xA7, 0x47, 0xE3, 0x8E, 0x3E, 0xAF,
+       0x2D, 0xBA, 0x06, 0xFE, 0xD4, 0xE4, 0x97, 0xBC, 0x10, 0x8D, 0xA6, 0xA5,
+       0x3A, 0x3C, 0xE6, 0x21, 0xCB, 0x3B, 0xDA, 0x13, 0xE3, 0x4A, 0x23, 0xEB,
+       0xBC, 0xBA, 0xA5, 0x90, 0x91, 0xAD, 0x55, 0x6E, 0xD5, 0xDD, 0x85, 0x4B,
+       0x6B, 0x27, 0xD2, 0x39, 0x76, 0x06, 0x2E, 0xD4, 0x23, 0x5B, 0xBB, 0x55,
+       0xB5, 0x49, 0x3F, 0x7D, 0x2F, 0x21, 0xCB, 0x96, 0x75, 0x19, 0x99, 0xA9,
+       0x99, 0xF1, 0x0B, 0xBB, 0x3E, 0xC7, 0x10, 0xA7, 0x8B, 0x7A, 0x52, 0xB9,
+       0x49, 0xE4, 0x14, 0x08, 0x78, 0xE7, 0x3D, 0xD9, 0x98, 0x57, 0xDA, 0xFF,
+       0xDA, 0xB9, 0x8D, 0x66, 0x75, 0x80, 0xB5, 0x5D, 0xB9, 0xDF, 0x69, 0x65,
+       0xDB, 0x16, 0xE7, 0x1E, 0xC4, 0x40, 0xE4, 0x46, 0x8A, 0x1E, 0x76, 0x32,
+       0x16, 0xE7, 0x83, 0x67, 0xDA, 0x78, 0x56, 0xAB, 0x0F, 0x4C, 0x5C, 0xF1,
+       0x70, 0xDA, 0x66, 0x20, 0xF4, 0xF8, 0x0F, 0xF6, 0xDC, 0x2F, 0x72, 0x97,
+       0x6B, 0x78, 0x40, 0x79, 0x9D, 0x5B, 0x6D, 0xBE, 0x03, 0xCE, 0x70, 0x42,
+       0xA8, 0x2A, 0x71, 0x11, 0xDF, 0x2E, 0x81, 0x01, 0xEA, 0x86, 0x12, 0x91,
+       0x4F, 0xD0, 0x18, 0xED, 0x84, 0xA4, 0x48, 0x30, 0xB9, 0xB9, 0x7B, 0x56,
+       0x3E, 0xEF, 0x8D, 0xE1, 0x1F, 0xFD, 0x90, 0xB2, 0x44, 0x85, 0x58, 0x6F,
+       0xA7, 0x30, 0x74, 0x7C, 0xF1, 0xAE, 0x8C, 0x99, 0x3F, 0xF1, 0x4D, 0x57,
+       0xC5, 0x95, 0x68, 0xC2, 0x36, 0xB2, 0xEC, 0xC2, 0x68, 0x7A, 0xC0, 0x81,
+       0xA9, 0x55, 0x05, 0xE7, 0xCF, 0xAF, 0x50, 0xAC
+};
+
+static const br_x509_certificate CHAIN[] = {
+       { (unsigned char *)CERT0, sizeof CERT0 },
+       { (unsigned char *)CERT1, sizeof CERT1 }
+};
+
+#define CHAIN_LEN   2
diff --git a/samples/client_basic.c b/samples/client_basic.c
new file mode 100644 (file)
index 0000000..d4f79fb
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include "bearssl.h"
+
+/*
+ * Connect to the specified host and port. The connected socket is
+ * returned, or -1 on error.
+ */
+static int
+host_connect(const char *host, const char *port)
+{
+       struct addrinfo hints, *si, *p;
+       int fd;
+       int err;
+
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       err = getaddrinfo(host, port, &hints, &si);
+       if (err != 0) {
+               fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+                       gai_strerror(err));
+               return -1;
+       }
+       fd = -1;
+       for (p = si; p != NULL; p = p->ai_next) {
+               struct sockaddr *sa;
+               void *addr;
+               char tmp[INET6_ADDRSTRLEN + 50];
+
+               sa = (struct sockaddr *)p->ai_addr;
+               if (sa->sa_family == AF_INET) {
+                       addr = &((struct sockaddr_in *)sa)->sin_addr;
+               } else if (sa->sa_family == AF_INET6) {
+                       addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
+               } else {
+                       addr = NULL;
+               }
+               if (addr != NULL) {
+                       inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
+               } else {
+                       sprintf(tmp, "<unknown family: %d>",
+                               (int)sa->sa_family);
+               }
+               fprintf(stderr, "connecting to: %s\n", tmp);
+               fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+               if (fd < 0) {
+                       perror("socket()");
+                       continue;
+               }
+               if (connect(fd, p->ai_addr, p->ai_addrlen) < 0) {
+                       perror("connect()");
+                       close(fd);
+                       continue;
+               }
+               break;
+       }
+       if (p == NULL) {
+               freeaddrinfo(si);
+               fprintf(stderr, "ERROR: failed to connect\n");
+               return -1;
+       }
+       freeaddrinfo(si);
+       fprintf(stderr, "connected.\n");
+       return fd;
+}
+
+/*
+ * Low-level data read callback for the simplified SSL I/O API.
+ */
+static int
+sock_read(void *ctx, unsigned char *buf, size_t len)
+{
+       for (;;) {
+               ssize_t rlen;
+
+               rlen = read(*(int *)ctx, buf, len);
+               if (rlen <= 0) {
+                       if (rlen < 0 && errno == EINTR) {
+                               continue;
+                       }
+                       return -1;
+               }
+               return (int)rlen;
+       }
+}
+
+/*
+ * Low-level data write callback for the simplified SSL I/O API.
+ */
+static int
+sock_write(void *ctx, const unsigned char *buf, size_t len)
+{
+       for (;;) {
+               ssize_t wlen;
+
+               wlen = write(*(int *)ctx, buf, len);
+               if (wlen <= 0) {
+                       if (wlen < 0 && errno == EINTR) {
+                               continue;
+                       }
+                       return -1;
+               }
+               return (int)wlen;
+       }
+}
+
+/*
+ * The hardcoded trust anchors. These are the two DN + public key that
+ * correspond to the self-signed certificates cert-root-rsa.pem and
+ * cert-root-ec.pem.
+ *
+ * C code for hardcoded trust anchors can be generated with the "brssl"
+ * command-line tool (with the "ta" command).
+ */
+
+static const unsigned char TA0_DN[] = {
+       0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+       0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03,
+       0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74
+};
+
+static const unsigned char TA0_RSA_N[] = {
+       0xB6, 0xD9, 0x34, 0xD4, 0x50, 0xFD, 0xB3, 0xAF, 0x7A, 0x73, 0xF1, 0xCE,
+       0x38, 0xBF, 0x5D, 0x6F, 0x45, 0xE1, 0xFD, 0x4E, 0xB1, 0x98, 0xC6, 0x60,
+       0x83, 0x26, 0xD2, 0x17, 0xD1, 0xC5, 0xB7, 0x9A, 0xA3, 0xC1, 0xDE, 0x63,
+       0x39, 0x97, 0x9C, 0xF0, 0x5E, 0x5C, 0xC8, 0x1C, 0x17, 0xB9, 0x88, 0x19,
+       0x6D, 0xF0, 0xB6, 0x2E, 0x30, 0x50, 0xA1, 0x54, 0x6E, 0x93, 0xC0, 0xDB,
+       0xCF, 0x30, 0xCB, 0x9F, 0x1E, 0x27, 0x79, 0xF1, 0xC3, 0x99, 0x52, 0x35,
+       0xAA, 0x3D, 0xB6, 0xDF, 0xB0, 0xAD, 0x7C, 0xCB, 0x49, 0xCD, 0xC0, 0xED,
+       0xE7, 0x66, 0x10, 0x2A, 0xE9, 0xCE, 0x28, 0x1F, 0x21, 0x50, 0xFA, 0x77,
+       0x4C, 0x2D, 0xDA, 0xEF, 0x3C, 0x58, 0xEB, 0x4E, 0xBF, 0xCE, 0xE9, 0xFB,
+       0x1A, 0xDA, 0xA3, 0x83, 0xA3, 0xCD, 0xA3, 0xCA, 0x93, 0x80, 0xDC, 0xDA,
+       0xF3, 0x17, 0xCC, 0x7A, 0xAB, 0x33, 0x80, 0x9C, 0xB2, 0xD4, 0x7F, 0x46,
+       0x3F, 0xC5, 0x3C, 0xDC, 0x61, 0x94, 0xB7, 0x27, 0x29, 0x6E, 0x2A, 0xBC,
+       0x5B, 0x09, 0x36, 0xD4, 0xC6, 0x3B, 0x0D, 0xEB, 0xBE, 0xCE, 0xDB, 0x1D,
+       0x1C, 0xBC, 0x10, 0x6A, 0x71, 0x71, 0xB3, 0xF2, 0xCA, 0x28, 0x9A, 0x77,
+       0xF2, 0x8A, 0xEC, 0x42, 0xEF, 0xB1, 0x4A, 0x8E, 0xE2, 0xF2, 0x1A, 0x32,
+       0x2A, 0xCD, 0xC0, 0xA6, 0x46, 0x2C, 0x9A, 0xC2, 0x85, 0x37, 0x91, 0x7F,
+       0x46, 0xA1, 0x93, 0x81, 0xA1, 0x74, 0x66, 0xDF, 0xBA, 0xB3, 0x39, 0x20,
+       0x91, 0x93, 0xFA, 0x1D, 0xA1, 0xA8, 0x85, 0xE7, 0xE4, 0xF9, 0x07, 0xF6,
+       0x10, 0xF6, 0xA8, 0x27, 0x01, 0xB6, 0x7F, 0x12, 0xC3, 0x40, 0xC3, 0xC9,
+       0xE2, 0xB0, 0xAB, 0x49, 0x18, 0x3A, 0x64, 0xB6, 0x59, 0xB7, 0x95, 0xB5,
+       0x96, 0x36, 0xDF, 0x22, 0x69, 0xAA, 0x72, 0x6A, 0x54, 0x4E, 0x27, 0x29,
+       0xA3, 0x0E, 0x97, 0x15
+};
+
+static const unsigned char TA0_RSA_E[] = {
+       0x01, 0x00, 0x01
+};
+
+static const unsigned char TA1_DN[] = {
+       0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+       0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03,
+       0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74
+};
+
+static const unsigned char TA1_EC_Q[] = {
+       0x04, 0x71, 0x74, 0xBA, 0xAB, 0xB9, 0x30, 0x2E, 0x81, 0xD5, 0xE5, 0x57,
+       0xF9, 0xF3, 0x20, 0x68, 0x0C, 0x9C, 0xF9, 0x64, 0xDB, 0xB4, 0x20, 0x0D,
+       0x6D, 0xEA, 0x40, 0xD0, 0x4A, 0x6E, 0x42, 0xFD, 0xB6, 0x9A, 0x68, 0x25,
+       0x44, 0xF6, 0xDF, 0x7B, 0xC4, 0xFC, 0xDE, 0xDD, 0x7B, 0xBB, 0xC5, 0xDB,
+       0x7C, 0x76, 0x3F, 0x41, 0x66, 0x40, 0x6E, 0xDB, 0xA7, 0x87, 0xC2, 0xE5,
+       0xD8, 0xC5, 0xF3, 0x7F, 0x8D
+};
+
+static const br_x509_trust_anchor TAs[2] = {
+       {
+               (unsigned char *)TA0_DN, sizeof TA0_DN,
+               BR_X509_TA_CA,
+               {
+                       BR_KEYTYPE_RSA,
+                       { .rsa = {
+                               (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N,
+                               (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E,
+                       } }
+               }
+       },
+       {
+               (unsigned char *)TA1_DN, sizeof TA1_DN,
+               BR_X509_TA_CA,
+               {
+                       BR_KEYTYPE_EC,
+                       { .ec = {
+                               BR_EC_secp256r1,
+                               (unsigned char *)TA1_EC_Q, sizeof TA1_EC_Q,
+                       } }
+               }
+       }
+};
+
+#define TAs_NUM   2
+
+/*
+ * Main program: this is a simple program that expects 2 or 3 arguments.
+ * The first two arguments are a hostname and a port; the program will
+ * open a SSL connection with that server and port. It will then send
+ * a simple HTTP GET request, using the third argument as target path
+ * ("/" is used as path if no third argument was provided). The HTTP
+ * response, complete with header and contents, is received and written
+ * on stdout.
+ */
+int
+main(int argc, char *argv[])
+{
+       const char *host, *port, *path;
+       int fd;
+       br_ssl_client_context sc;
+       br_x509_minimal_context xc;
+       unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];
+       br_sslio_context ioc;
+
+       /*
+        * Parse command-line argument: host, port, and path. The path
+        * is optional; if absent, "/" is used.
+        */
+       if (argc < 3 || argc > 4) {
+               return EXIT_FAILURE;
+       }
+       host = argv[1];
+       port = argv[2];
+       if (argc == 4) {
+               path = argv[3];
+       } else {
+               path = "/";
+       }
+
+       /*
+        * Open the socket to the target server.
+        */
+       fd = host_connect(host, port);
+       if (fd < 0) {
+               return EXIT_FAILURE;
+       }
+
+       /*
+        * Initialise the client context:
+        * -- Use the "full" profile (all supported algorithms).
+        * -- The provided X.509 validation engine is initialised, with
+        *    the hardcoded trust anchor.
+        */
+       br_ssl_client_init_full(&sc, &xc, TAs, TAs_NUM);
+
+       /*
+        * Set the I/O buffer to the provided array. We allocated a
+        * buffer large enough for full-duplex behaviour with all
+        * allowed sizes of SSL records, hence we set the last argument
+        * to 1 (which means "split the buffer into separate input and
+        * output areas").
+        */
+       br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);
+
+       /*
+        * Reset the client context, for a new handshake. We provide the
+        * target host name: it will be used for the SNI extension. The
+        * last parameter is 0: we are not trying to resume a session.
+        */
+       br_ssl_client_reset(&sc, host, 0);
+
+       /*
+        * Initialise the simplified I/O wrapper context, to use our
+        * SSL client context, and the two callbacks for socket I/O.
+        */
+       br_sslio_init(&ioc, &sc.eng, sock_read, &fd, sock_write, &fd);
+
+       /*
+        * Note that while the context has, at that point, already
+        * assembled the ClientHello to send, nothing happened on the
+        * network yet. Real I/O will occur only with the next call.
+        *
+        * We write our simple HTTP request. We could test each call
+        * for an error (-1), but this is not strictly necessary, since
+        * the error state "sticks": if the context fails for any reason
+        * (e.g. bad server certificate), then it will remain in failed
+        * state and all subsequent calls will return -1 as well.
+        */
+       br_sslio_write_all(&ioc, "GET ", 4);
+       br_sslio_write_all(&ioc, path, strlen(path));
+       br_sslio_write_all(&ioc, " HTTP/1.0\r\nHost: ", 17);
+       br_sslio_write_all(&ioc, host, strlen(host));
+       br_sslio_write_all(&ioc, "\r\n\r\n", 4);
+
+       /*
+        * SSL is a buffered protocol: we make sure that all our request
+        * bytes are sent onto the wire.
+        */
+       br_sslio_flush(&ioc);
+
+       /*
+        * Read the server's response. We use here a small 512-byte buffer,
+        * but most of the buffering occurs in the client context: the
+        * server will send full records (up to 16384 bytes worth of data
+        * each), and the client context buffers one full record at a time.
+        */
+       for (;;) {
+               int rlen;
+               unsigned char tmp[512];
+
+               rlen = br_sslio_read(&ioc, tmp, sizeof tmp);
+               if (rlen < 0) {
+                       break;
+               }
+               fwrite(tmp, 1, rlen, stdout);
+       }
+
+       /*
+        * Close the socket.
+        */
+       close(fd);
+
+       /*
+        * Check whether we closed properly or not. If the engine is
+        * closed, then its error status allows to distinguish between
+        * a normal closure and a SSL error.
+        *
+        * If the engine is NOT closed, then this means that the
+        * underlying network socket was closed or failed in some way.
+        * Note that many Web servers out there do not properly close
+        * their SSL connections (they don't send a close_notify alert),
+        * which will be reported here as "socket closed without proper
+        * SSL termination".
+        */
+       if (br_ssl_engine_current_state(&sc.eng) == BR_SSL_CLOSED) {
+               int err;
+
+               err = br_ssl_engine_last_error(&sc.eng);
+               if (err == 0) {
+                       fprintf(stderr, "closed.\n");
+                       return EXIT_SUCCESS;
+               } else {
+                       fprintf(stderr, "SSL error %d\n", err);
+                       return EXIT_FAILURE;
+               }
+       } else {
+               fprintf(stderr,
+                       "socket closed without proper SSL termination\n");
+               return EXIT_FAILURE;
+       }
+}
diff --git a/samples/custom_profile.c b/samples/custom_profile.c
new file mode 100644 (file)
index 0000000..e88d948
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "bearssl.h"
+
+/*
+ * A "profile" is an initialisation function for a SSL context, that
+ * configures a list of cipher suites and algorithm implementations.
+ * While BearSSL comes with a few predefined profiles, you might one
+ * to define you own, using the example below as guidance.
+ *
+ * Each individual initialisation call sets a parameter or an algorithm
+ * support. Setting a specific algorithm pulls in the implementation of
+ * that algorithm in the compiled binary, as per static linking
+ * behaviour. Removing some of this calls will then reduce total code
+ * footprint, but also mechanically prevents some features to be
+ * supported (protocol versions and cipher suites).
+ *
+ * The two below define profiles for the client and the server contexts,
+ * respectively. Of course, in a typical size-constrained application,
+ * you would use one or the other, not both, to avoid pulling in code
+ * for both.
+ */
+
+void
+example_client_profile(br_ssl_client_context *cc
+       /* and possibly some other arguments */)
+{
+       /*
+        * A list of cipher suites, by preference (first is most
+        * preferred). The list below contains all cipher suites supported
+        * by BearSSL; trim it done to your needs.
+        */
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA
+       };
+
+       /*
+        * Client context must be cleared at some point. This sets
+        * every value and pointer to 0 or NULL.
+        */
+       br_ssl_client_zero(cc);
+
+       /*
+        * Define minimum and maximum protocol versions. Supported
+        * versions are:
+        *    BR_TLS10    TLS 1.0
+        *    BR_TLS11    TLS 1.1
+        *    BR_TLS12    TLS 1.2
+        */
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12);
+
+       /*
+        * Set the PRF implementation(s).
+        * For TLS 1.0 and 1.1, the "prf10" is needed.
+        * For TLS 1.2, this depends on the cipher suite:
+        *  -- cipher suites with a name ending in "SHA384" need "prf_sha384";
+        *  -- all others need "prf_sha256".
+        *
+        * Note that a cipher suite like TLS_RSA_WITH_AES_128_CBC_SHA will
+        * use SHA-1 for the per-record MAC (that's what the final "SHA"
+        * means), but still SHA-256 for the PRF when selected along with
+        * the TLS-1.2 protocol version.
+        */
+       br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf);
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+       br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf);
+
+       /*
+        * Set hash functions for the engine. Required hash functions
+        * depend on the protocol and cipher suite:
+        *
+        * -- TLS 1.0 and 1.1 require both MD5 and SHA-1.
+        * -- With TLS 1.2, cipher suites with a name ending in "SHA384"
+        *    require SHA-384.
+        * -- With TLS 1.2, cipher suites with a name ending in "SHA256"
+        *    require SHA-256.
+        * -- With TLS 1.2, cipher suites with a name ending in "SHA"
+        *    require both SHA-256 and SHA-1.
+        *
+        * Note that with current implementations, SHA-224 and SHA-256
+        * share the same file, so if you use one, you may have the other
+        * one with no additional overhead. Similarly, SHA-384 and SHA-512
+        * share the same implementation code.
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_md5_ID, &br_md5_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha1_ID, &br_sha1_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha224_ID, &br_sha224_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha384_ID, &br_sha384_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha512_ID, &br_sha512_vtable);
+
+       /*
+        * Set the cipher suites. All specified cipher suite MUST be
+        * supported, and the relevant algorithms MUST have been
+        * configured (failure to provide needed implementations may
+        * trigger unwanted behaviours like segfaults or overflows).
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+
+       /*
+        * Public-key algorithm imeplementations.
+        *
+        * -- RSA public core ("rsapub") is needed for "RSA" key exchange
+        *    (cipher suites whose name starts with TLS_RSA).
+        *
+        * -- RSA signature verification ("rsavrfy") is needed for
+        *    "ECDHE_RSA" cipher suites (not ECDH_RSA).
+        *
+        * -- Elliptic curve implementation ("ec") is needed for cipher
+        *    suites that use elliptic curves (both "ECDH" and "ECDHE"
+        *    cipher suites).
+        *
+        * -- ECDSA signature verification is needed for "ECDHE_ECDSA"
+        *    cipher suites (but not for ECDH_ECDSA or ECDH_RSA).
+        *
+        * The RSA code comes in two variants, called "i31" and "i32".
+        * Right now, the "i31" is somewhat faster.
+        */
+       br_ssl_client_set_rsapub(cc, &br_rsa_i31_public);
+       br_ssl_client_set_rsavrfy(cc, &br_rsa_i31_pkcs1_vrfy);
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+       br_ssl_client_set_ecdsa(cc, &br_ecdsa_i31_vrfy_asn1);
+
+       /*
+        * Record handler:
+        * -- Cipher suites in AES_128_CBC, AES_256_CBC and 3DES_EDE_CBC
+        *    need the CBC record handler ("set_cbc").
+        * -- Cipher suites in AES_128_GCM and AES_256_GCM need the GCM
+        *    record handler ("set_gcm").
+        */
+       br_ssl_engine_set_cbc(&cc->eng,
+               &br_sslrec_in_cbc_vtable,
+               &br_sslrec_out_cbc_vtable);
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+
+       /*
+        * Symmetric encryption:
+        * -- AES_128_CBC and AES_256_CBC require an "aes_cbc" implementation
+        *    (actually two implementations, for encryption and decryption).
+        * -- 3DES_EDE_CBC requires a "des_cbc" implementation
+        *    (actually two implementations, for encryption and decryption).
+        * -- AES_128_GCM and AES_256_GCM require an "aes_ctr" imeplementation
+        *    and also a GHASH implementation.
+        *
+        * Two 3DES implementations are provided:
+        *
+        *    des_tab     Classical table-based implementation; it is
+        *                not constant-time.
+        *
+        *    dest_ct     Constant-time DES/3DES implementation. It is
+        *                slower than des_tab.
+        *
+        * Four AES implementations are provided:
+        *
+        *    aes_ct      Constant-time AES implementation, for 32-bit
+        *                systems.
+        *
+        *    aes_ct64    Constant-time AES implementation, for 64-bit
+        *                systems. It actually also runs on 32-bit systems,
+        *                but, on such systems, it yields larger code and
+        *                slightly worse performance. On 64-bit systems,
+        *                aes_ct64 is about twice faster than aes_ct for
+        *                CTR processing (GCM encryption and decryption),
+        *                and for CBC (decryption only).
+        *
+        *    aes_small   Smallest implementation provided, but also the
+        *                slowest, and it is not constant-time. Use it
+        *                only if desperate for code size.
+        *
+        *    aes_big     Classical table-based AES implementation. This
+        *                is decently fast and still resonably compact,
+        *                but it is not constant-time.
+        *
+        * Whether having constant-time implementations is absolutely
+        * required for security depends on the context (in particular
+        * whether the target architecture actually has cache memory),
+        * and while side-channel analysis for non-constant-time AES
+        * code has been demonstrated in lab conditions, it certainly
+        * does not apply to all actual usages, and it has never been
+        * spotted in the wild. It is still considered cautious to use
+        * constant-time code by default, and to consider the other
+        * implementations only if duly measured performance issues make
+        * it mandatory.
+        */
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct_cbcenc_vtable,
+               &br_aes_ct_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       /* Alternate: aes_ct64
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct64_cbcenc_vtable,
+               &br_aes_ct64_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       */
+       /* Alternate: aes_small
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_small_cbcenc_vtable,
+               &br_aes_small_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_small_ctr_vtable);
+       */
+       /* Alternate: aes_big
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_big_cbcenc_vtable,
+               &br_aes_big_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_big_ctr_vtable);
+       */
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_ct_cbcenc_vtable,
+               &br_des_ct_cbcdec_vtable);
+       /* Alternate: des_tab
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_tab_cbcenc_vtable,
+               &br_des_tab_cbcdec_vtable);
+       */
+
+       /*
+        * GHASH is needed for AES_128_GCM and AES_256_GCM. Three
+        * implementations are provided:
+        *
+        *    ctmul     Uses 32-bit multiplications with a 64-bit result.
+        *
+        *    ctmul32   Uses 32-bit multiplications with a 32-bit result.
+        *
+        *    ctmul64   Uses 64-bit multiplications with a 64-bit result.
+        *
+        * On 64-bit platforms, ctmul64 is the smallest and fastest of
+        * the three. On 32-bit systems, ctmul should be prefered. The
+        * ctmul32 implementation is meant to be used for the specific
+        * 32-bit systems that do not have a 32x32->64 multiplier (i.e.
+        * the ARM Cortex-M0 and Cortex-M0+).
+        *
+        * These implementations are all constant-time as long as the
+        * underlying multiplication opcode is constant-time (which is
+        * true for all modern systems, but not for older architectures
+        * such that ARM9 or 80486).
+        */
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+       /* Alternate: ghash_ctmul32
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul32);
+       */
+       /* Alternate: ghash_ctmul64
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+       */
+
+#if 0
+       /*
+        * For a client, the normal case is to validate the server
+        * certificate with regards to a set of trust anchors. This
+        * entails using a br_x509_minimal_context structure, configured
+        * with the relevant algorithms, as shown below.
+        *
+        * Alternatively, the client could "know" the intended server
+        * public key through an out-of-band mechanism, in which case
+        * a br_x509_knownkey_context is appropriate, for a much reduced
+        * code footprint.
+        *
+        * We assume here that the following extra parameters have been
+        * provided:
+        *
+        *   xc                  engine context (br_x509_minimal_context *)
+        *   trust_anchors       trust anchors (br_x509_trust_anchor *)
+        *   trust_anchors_num   number of trust anchors (size_t)
+        */
+
+       /*
+        * The X.509 engine needs a hash function for processing the
+        * subject and issuer DN of certificates and trust anchors. Any
+        * supported hash function is appropriate; here we use SHA-256.
+        * The trust an
+        */
+       br_x509_minimal_init(xc, &br_sha256_vtable,
+               trust_anchors, trust_anchors_num);
+
+       /*
+        * Set suites and asymmetric crypto implementations. We use the
+        * "i31" code for RSA (it is somewhat faster than the "i32"
+        * implementation). These implementations are used for
+        * signature verification on certificates, but not for the
+        * SSL-specific usage of the server's public key. For instance,
+        * if the server has an EC public key but the rest of the chain
+        * (intermediate CA, root...) use RSA, then you would need only
+        * the RSA verification function below.
+        */
+       br_x509_minimal_set_rsa(xc, &br_rsa_i31_pkcs1_vrfy);
+       br_x509_minimal_set_ecdsa(xc,
+               &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
+
+       /*
+        * Set supported hash functions. These are for signatures on
+        * certificates. There again, you only need the hash functions
+        * that are actually used in certificates, but if a given
+        * function was included for the SSL engine, you may as well
+        * add it here.
+        *
+        * Note: the engine explicitly rejects signatures that use MD5.
+        * Thus, there is no need for MD5 here.
+        */
+       br_ssl_engine_set_hash(xc, br_sha1_ID, &br_sha1_vtable);
+       br_ssl_engine_set_hash(xc, br_sha224_ID, &br_sha224_vtable);
+       br_ssl_engine_set_hash(xc, br_sha256_ID, &br_sha256_vtable);
+       br_ssl_engine_set_hash(xc, br_sha384_ID, &br_sha384_vtable);
+       br_ssl_engine_set_hash(xc, br_sha512_ID, &br_sha512_vtable);
+
+       /*
+        * Link the X.509 engine in the SSL engine.
+        */
+       br_ssl_engine_set_x509(&cc->eng, &xc->vtable);
+#endif
+}
+
+/*
+ * Example server profile. Most of it is shared with the client
+ * profile, so see the comments in the client function for details.
+ *
+ * This example function assumes a server with a (unique) RSA private
+ * key, so the list of cipher suites is trimmed down for RSA.
+ */
+void
+example_server_profile(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk)
+{
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA
+       };
+
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12);
+
+       br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf);
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+       br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf);
+
+       /*
+        * Apart from the requirements listed in the client side, these
+        * hash functions are also used by the server to compute its
+        * signature on ECDHE parameters. Which functions are needed
+        * depends on what the client may support; furthermore, the
+        * client may fail to send the relevant extension, in which
+        * case the server will default to whatever it can (as per the
+        * standard, it should be SHA-1 in that case).
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_md5_ID, &br_md5_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha1_ID, &br_sha1_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha224_ID, &br_sha224_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha384_ID, &br_sha384_vtable);
+       br_ssl_engine_set_hash(&cc->eng, br_sha512_ID, &br_sha512_vtable);
+
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+
+       /*
+        * Elliptic curve implementation is used for ECDHE suites (but
+        * not for ECDH).
+        */
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations. Here, we indicate that the RSA
+        * private key is fit for both signing and decrypting, and we
+        * provide the two relevant implementations.
+
+        * BR_KEYTYPE_KEYX allows TLS_RSA_*, BR_KEYTYPE_SIGN allows
+        * TLS_ECDHE_RSA_*.
+        */
+       br_ssl_server_set_single_rsa(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
+               br_rsa_i31_private, br_rsa_i31_pkcs1_sign);
+       /*
+        * If the server used an EC private key, this call would look
+        * like this:
+
+       br_ssl_server_set_single_ec(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
+               cert_issuer_key_type,
+               &br_ec_prime_i31, br_ecdsa_i31_sign_asn1);
+
+        * Note the tricky points:
+        *
+        * -- "ECDH" cipher suites use only the EC code (&br_ec_prime_i31);
+        *    the ECDHE_ECDSA cipher suites need both the EC code and
+        *    the ECDSA signature implementation.
+        *
+        * -- For "ECDH" (not "ECDHE") cipher suites, the engine must
+        *    know the key type (RSA or EC) for the intermediate CA that
+        *    issued the server's certificate; this is an artefact of
+        *    how the protocol is defined. BearSSL won't try to decode
+        *    the server's certificate to obtain that information (it
+        *    could do that, the code is there, but it would increase the
+        *    footprint). So this must be provided by the caller.
+        *
+        * -- BR_KEYTYPE_KEYX allows ECDH, BR_KEYTYPE_SIGN allows
+        *    ECDHE_ECDSA.
+        */
+
+       br_ssl_engine_set_cbc(&cc->eng,
+               &br_sslrec_in_cbc_vtable,
+               &br_sslrec_out_cbc_vtable);
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct_cbcenc_vtable,
+               &br_aes_ct_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       /* Alternate: aes_ct64
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct64_cbcenc_vtable,
+               &br_aes_ct64_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       */
+       /* Alternate: aes_small
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_small_cbcenc_vtable,
+               &br_aes_small_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_small_ctr_vtable);
+       */
+       /* Alternate: aes_big
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_big_cbcenc_vtable,
+               &br_aes_big_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_big_ctr_vtable);
+       */
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_ct_cbcenc_vtable,
+               &br_des_ct_cbcdec_vtable);
+       /* Alternate: des_tab
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_tab_cbcenc_vtable,
+               &br_des_tab_cbcdec_vtable);
+       */
+
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+       /* Alternate: ghash_ctmul32
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul32);
+       */
+       /* Alternate: ghash_ctmul64
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+       */
+}
diff --git a/samples/key-ec.h b/samples/key-ec.h
new file mode 100644 (file)
index 0000000..c9d0fa4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "bearssl.h"
+
+/*
+ * The private key for the server certificate (EC).
+ */
+
+static const unsigned char EC_X[] = {
+       0x03, 0x91, 0x5B, 0x42, 0x06, 0x90, 0x73, 0x91, 0x1B, 0x48, 0xEF, 0x08,
+       0xFB, 0xB5, 0xAD, 0x75, 0x65, 0xF9, 0xE6, 0xF7, 0x21, 0x47, 0x62, 0x48,
+       0xFA, 0x3F, 0x97, 0x7B, 0x70, 0x9D, 0x86, 0xA5
+};
+
+static const br_ec_private_key EC = {
+       23,
+       (unsigned char *)EC_X, sizeof EC_X
+};
diff --git a/samples/key-ee-ec.pem b/samples/key-ee-ec.pem
new file mode 100644 (file)
index 0000000..2e6a136
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAORW0IGkHORG0jvCPu1rXVl+eb3IUdiSPo/l3twnYaloAoGCCqGSM49AwEHoUQDQgAE
+Xzidp/9Niq/2NDlGGvw63/Qjqqnq+8UI3gCOvnmlN1hMbd0ByqtH34m2xxcfOPwdIBTdRcDgj5NO
+OAv86ZmhSQ==
+-----END EC PRIVATE KEY-----
diff --git a/samples/key-ee-rsa.pem b/samples/key-ee-rsa.pem
new file mode 100644 (file)
index 0000000..e8b22b4
--- /dev/null
@@ -0,0 +1,23 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA1HodJ7orOmeykWr754NEyu0cda3dTYNi1qpolbIkIXsVriqZaBXtZvC4WOfT
+9S7G2Spe5w4u5/xnWcDIYX1LpG/dn9nIhYdkx7oaDynUlqh4mmtiIKky0O6pjChhR6JQKmP2Id7a
+2NXwf8UAgnDmo79ciSdPUZJ3A8OwzC477CPyL1NBr4mT/9KAsUOX3tYZoJISej1meeHBvOF3cKKL
+PUaEUz/kTkJBN5IeH/04s/fvhzmA01bP9OAT3mSwcqQDhMRB7W/6PuLKBCDS19wsgit64m2hHEjb
+z4lPNJc9KKhT2ufB4XMVozB2f48jQhQ9UTTSWq08m8vI/n9ujkDzvQIDAQABAoIBADzb40jzQpl+
+hT+wsIl96HDlXI76Z1Zh6SgKdF1YQpASdMHHstwE19Rx46OXd3cVWGBwifFNdzL8cU/cb6i43jcx
+0X2NQCm6/6tTi05HkXw7shus4VTwkb0VdxvNnxuJCsQxkJjf/7g3AyVdtIkoNG+3ipZAW7BGLu+1
+mAjLv18h4LniI8JGWQK75z8xNQzEdOFVyqC1PujjC5emesxlRW8Ks+MzQ+20WTpHGkLAoLJZJ1XE
+bDQqFbkRXu4imKLtA4iyBrEpXEKZR85tVO+NQOGdPyH/ugK23nSDcJlI2PTHSlGABTpZZUmigO4O
+b8lppU7cFa2JemKG3kEj0BWDOWMCgYEA+fYynFHXcbJi7YEk2vapLMMtVZSVUeU2Ep4uH47YIiJk
+XqP8YPAU3BBIb08afcw3Iyd2tjGq3nDJ7KsKUPHqeXl0vjurLmOXom8KRXvXbNJtG3AxA68miyjF
++ElnRUHx0zUFJyp5IdoGtj2i6DxA+m/E/PXEBeuaMapAfl7uIlsCgYEA2Zwa3JRR9sGW2g4RPzco
+ejOwxL7faCvTHGVnejyvWVCrKTYXORVxl2LdzSXujf8mj3Ehvo+chU464STH4Urf0GCzxEQurHMW
+XwfJOnNe2pvu4rSpPTMUe+6n1Kz3U+Y+8IVXTIuWG93XNvyJN1l1lnWLLvcELSmJ2befcTvi7ccC
+gYEA5PwCLyvWRwTZFaRaI/EU17nRHPYpuEVXPMUFkclk/BgvhHeLay5knZiZEscPiLB8zkqHuK5V
+TsNaZ+HkaHTFjRSTuvWkgrGfpqE8cpzZo4o9g4ZKkIpyr8bhXOu5nDumEgsfNlr1bupxfZ+HTmJs
+UD/14JowQhAsSFUkEeBbHMMCgYEAjazgoDPAmVK4kAcQm4Ohys3UjINomD3QGHC8ygywbQnkJdSd
+kgCwD8vCdEn54mD4DfOt8I83bGLeWq7Do55H0TbkUyfA622SZxR+optyagmToe3VMY8MCxP6GLDz
+5Z/F4notuBw5ArOP5rDL9Uk9EVQ95bnU8kJVCXZPTD2dJQkCgYByDKfPBpVp9HUgNAPgz5pRk/VC
+LvKFvs5POLWMoplC871lOOI0PyGd9b2zv3M8GN728H+hwlXyOOkOHjHn21HFcY1ncTqfVVJg7kX2
+CJiBt3sv8pZ9c9Cmq6qDSUE1qZBnztO5c1SqhACIiJAdhpvluM6JChtHYjHCP8OMhgk8hg==
+-----END RSA PRIVATE KEY-----
diff --git a/samples/key-ica-ec.pem b/samples/key-ica-ec.pem
new file mode 100644 (file)
index 0000000..7443098
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEJOkvo4MZ9N+wlTodEzmWor1RauybudPe92K15viDbzoAoGCCqGSM49AwEHoUQDQgAE
+cC6SggEXbG2r4dFjCUhJ0qY1UtM8c7uyiDeYh/GN4Oxlmg4T9e2RYci2bTOEbq6OVYDNSZ4Hv9Cu
+nebQsycWoQ==
+-----END EC PRIVATE KEY-----
diff --git a/samples/key-ica-rsa.pem b/samples/key-ica-rsa.pem
new file mode 100644 (file)
index 0000000..2e38e34
--- /dev/null
@@ -0,0 +1,23 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAs+hrr5wWUuOBDFCrJc7MDcfyH39Q3yxcNdZiLmMnQafkU6hLJ/oTkaP6CUov
+O17Pd7OKwc1JlZx1DWR07+TXS7mhm2jSMHFI6vdLFN8/R6nYu+yPKMz637QflHyW/AgFKPno9C8v
+7mKcijrghVhgtg8tMLTAQVSRTB9frfEZ8MAipn3YP3k0WUJ7W7VBxGR/Us88NyKhL3kllCRBwj/6
+x3X7SLUNGKf0VPMubthDWMSrUOgFrZG2HgF1s1Sc3qCZFfus8VyXSVHM71gSb3NrszQUAQ9anfqq
+1pPT4urDq7xO7cxRobj4lLa0LKiGKx/2UUMpUl4TibNqeGBOTsAbpQIDAQABAoIBAAtBpQ85SGpK
+QsZG+9ZjQIAyPPN8j05PY7uYnM8DNC8W9qHHW2B2dKf9pwTSx+7CiV+Xc7yZgBukzOwYF3r1CgV6
+aWKkZdZTGDlfXKrDJx3wQhfL/s8SODYr+nfbbcT6KXx9WnaAx1J2iA3cDjU5qN9rRqwP+yF7TZYC
+NoXXGoTmHb7gj1H1IJNdiUgG0netXpM2HydRLzwHDweKav6sm4V4TTeHPYokVp8W7lL7xRhwQXVD
+lcE1noY5NczXV88k9n0EbqGDwX3pJoDC/xWzBFh/+oupxotM4KY2dKJGmtKTmiIef3u43CPv5SYh
+V2OcEXn+joV5qHGhg6yN+ri0/ucCgYEA4x2NBFNtbpx3lmc2r2hwfbawcTVKKlxqJWdoG2bd7HQz
+bO+uqtxhLOxEou4S9uPUVylvpNlDs3xQ+DF9p8oTo5XlHvA6DT/cAjWSQ+MRHqCV30T0lDSd0McZ
+4PaV1nq94bGuPVrGe/Dex5dL+VVhn2tpoqwudsyx5hHA8hFE1zcCgYEAysnhrQa1HZQ0hOaA2c/e
+A8I47dEIyGi4N6uJVBUMM1ecKifs4FKybOtI+XM3mwuAXUyVQGi2SZBBpE4bN3oUDWGwYF7SAJV0
+JJHpBxlZBvYRC7Vfh8HFPYIQCynP0Q4BeQVDaCPW3puJsTWnn2lNhT/PsSm3v76JwTRrBoqaGgMC
+gYAwyyiAxWu9V+BZb9NP3CBO4fEGYWyNrU0gvBahzHfhVRW3Ucc07iPygtA8MOniIRB9qWlTAVqK
+NSswJ3HXmpKdkpanDvVp405hKyFBdIc5DUclsKrbLHK7aAsnSdLnQXeKBaJpjBcYiadTOi4YYz+W
+AH2xdUyGOXP++dF6MDuaAQKBgQCtWyHygW5pR94R0t9J1FpuCiYSn4ULlgINjTXLzGZuqbGVlCX6
+qpdfZ1At92IMyCtHFwXsVtemUYzcAe1gYpsryVw3Njf+ScVM0fNMn02tFsQBp15wNqT/7OT8NhUz
+GO8HXwl9yE2SZZKzDDQsoZ+kjqVlRU2QvDkVElN/9xK/swKBgDz6m4XHvhvITo2bd8/i4FxTN1gr
+napgAc2DHOzCmeyYCjKvZAJDLeNleni1kj1UfrzVrH2BFM38asRJu8xPI4WjfH1fFktNjXVu6Ctk
+YFu0/0TlWkzowZHmE/K0M0tuJLt42m/DLW8deMyl38x+PUPUZb4OdVAKAua/voOMZtQw
+-----END RSA PRIVATE KEY-----
diff --git a/samples/key-root-ec.pem b/samples/key-root-ec.pem
new file mode 100644 (file)
index 0000000..e76db90
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEINx41f6h/utlj52XNFbIGJdkF3qZy1/g9hH2/yV4t/tnoAoGCCqGSM49AwEHoUQDQgAE
+cXS6q7kwLoHV5Vf58yBoDJz5ZNu0IA1t6kDQSm5C/baaaCVE9t97xPze3Xu7xdt8dj9BZkBu26eH
+wuXYxfN/jQ==
+-----END EC PRIVATE KEY-----
diff --git a/samples/key-root-rsa.pem b/samples/key-root-rsa.pem
new file mode 100644 (file)
index 0000000..82a2eb0
--- /dev/null
@@ -0,0 +1,23 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAttk01FD9s696c/HOOL9db0Xh/U6xmMZggybSF9HFt5qjwd5jOZec8F5cyBwX
+uYgZbfC2LjBQoVRuk8DbzzDLnx4nefHDmVI1qj2237CtfMtJzcDt52YQKunOKB8hUPp3TC3a7zxY
+606/zun7Gtqjg6PNo8qTgNza8xfMeqszgJyy1H9GP8U83GGUtycpbiq8Wwk21MY7Deu+ztsdHLwQ
+anFxs/LKKJp38orsQu+xSo7i8hoyKs3ApkYsmsKFN5F/RqGTgaF0Zt+6szkgkZP6HaGohefk+Qf2
+EPaoJwG2fxLDQMPJ4rCrSRg6ZLZZt5W1ljbfImmqcmpUTicpow6XFQIDAQABAoIBAENjavKDBPWn
+yygXKqtEb/GWHlNmoNKO7jv33z9TGxzvW8IULZqos3jtNiG0JNRGgiTALcx5FwZWYUiIMBq8v5bd
+nKv3O+DyaP/crdzkNxRCsekoXSXGuleugsHLs1IudTA4yDMamSTkCZH/LwH3KYNXJ+9hNhqsiu9D
+yqM9HIad2k/hmVFv4NDdH3VWqsQJFfrGEXgKRJKNpuzKeST3oo4iLcwH9NGiKXfjW23ekOiLYFwZ
+q8Yd6tOgNLpGuDWk9rlXPJALyXFZ/3UXZaKU5f0ZRfPfMQgM8toeWeWJS0FW7g6aLCJATsfOu/II
+j3nTVCPryvGF4mwe/y+Rgbd67xMCgYEA3Wpiac27hvbLzwbSTWYsKql5uKV0HTim3haNDuzMHJVY
+ZRJSuTY+rZBoaiuDRKMCMI3dQM8AZUJfHQclyTxjtn17Ig8cTKMN6Wr2MRTBplCq3wNLVjbOPAE2
+cv74gUfgpRLdlyueYO7j6PcTS+l41+wB1A/zAEm346Eoxoztt5cCgYEA02ipYQFX4T+8BSkcEyzP
+qeMT9d3BLPm4tTeq2sXRgcfEbF/ENcAX9OfPSEx4kVtzsfJXma5/dg0fuVgl1hO5WLBT+L6MJ/A2
+sGX3Y7x+doXI0xcNd/Epr7oWqZuCO7477vzJcNtud/KQJj2EmRNaHKKtaohqO5dnNhsM540lnDMC
+gYBU40KT2eJ5ngkJeE4Mio2IVa1rE1PvGBcxsmemPzcKBl/7cAjzJU7mcCT3/3K2T+C5CMq43CQE
+rmuUz3a3LkX0YytgJXbuEt10jiORMaoEv4yjL7okdaKf8r8TW5mexxXjc9Ys7PYtp6kNWhy1z+8a
+qUsSKIM7qwerZ9AgP0usRQKBgQDEHy4y/coG/tdweii/aSzlT/Huf2B8VtaR1yi7eBTaLvb8CwO9
+UY1n970GN1sKjiqQhF/cBFPesmIh0bKYHQgvTLU555uiWWiC0LVmYzF2xrn9ij9GbAXeLeZkRg3V
+Wq/DD+PYvNiIkhBESYG/eIJ6WjhCwna6/cQUH5gjH4AqnQKBgQCsKyjLtx+FeFu7PZ8AgCGWRVvs
+suxB74pxlMqMgBbBHyAGQOTTuaaTr/gwNXAAErAXS1X9VsAcAkM/yFv19mdZmJQG9vUMOe48AZDB
+c7xt3t3Ul71eAlbAYkrQGtq64yO3w7ny4C7GUVqMX03XwKToCrXqeKfGwrZGNP+lgZYrbw==
+-----END RSA PRIVATE KEY-----
diff --git a/samples/key-rsa.h b/samples/key-rsa.h
new file mode 100644 (file)
index 0000000..8de538d
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "bearssl.h"
+
+/*
+ * The private key for the server certificate (RSA).
+ */
+
+static const unsigned char RSA_P[] = {
+       0xF9, 0xF6, 0x32, 0x9C, 0x51, 0xD7, 0x71, 0xB2, 0x62, 0xED, 0x81, 0x24,
+       0xDA, 0xF6, 0xA9, 0x2C, 0xC3, 0x2D, 0x55, 0x94, 0x95, 0x51, 0xE5, 0x36,
+       0x12, 0x9E, 0x2E, 0x1F, 0x8E, 0xD8, 0x22, 0x22, 0x64, 0x5E, 0xA3, 0xFC,
+       0x60, 0xF0, 0x14, 0xDC, 0x10, 0x48, 0x6F, 0x4F, 0x1A, 0x7D, 0xCC, 0x37,
+       0x23, 0x27, 0x76, 0xB6, 0x31, 0xAA, 0xDE, 0x70, 0xC9, 0xEC, 0xAB, 0x0A,
+       0x50, 0xF1, 0xEA, 0x79, 0x79, 0x74, 0xBE, 0x3B, 0xAB, 0x2E, 0x63, 0x97,
+       0xA2, 0x6F, 0x0A, 0x45, 0x7B, 0xD7, 0x6C, 0xD2, 0x6D, 0x1B, 0x70, 0x31,
+       0x03, 0xAF, 0x26, 0x8B, 0x28, 0xC5, 0xF8, 0x49, 0x67, 0x45, 0x41, 0xF1,
+       0xD3, 0x35, 0x05, 0x27, 0x2A, 0x79, 0x21, 0xDA, 0x06, 0xB6, 0x3D, 0xA2,
+       0xE8, 0x3C, 0x40, 0xFA, 0x6F, 0xC4, 0xFC, 0xF5, 0xC4, 0x05, 0xEB, 0x9A,
+       0x31, 0xAA, 0x40, 0x7E, 0x5E, 0xEE, 0x22, 0x5B
+};
+
+static const unsigned char RSA_Q[] = {
+       0xD9, 0x9C, 0x1A, 0xDC, 0x94, 0x51, 0xF6, 0xC1, 0x96, 0xDA, 0x0E, 0x11,
+       0x3F, 0x37, 0x28, 0x7A, 0x33, 0xB0, 0xC4, 0xBE, 0xDF, 0x68, 0x2B, 0xD3,
+       0x1C, 0x65, 0x67, 0x7A, 0x3C, 0xAF, 0x59, 0x50, 0xAB, 0x29, 0x36, 0x17,
+       0x39, 0x15, 0x71, 0x97, 0x62, 0xDD, 0xCD, 0x25, 0xEE, 0x8D, 0xFF, 0x26,
+       0x8F, 0x71, 0x21, 0xBE, 0x8F, 0x9C, 0x85, 0x4E, 0x3A, 0xE1, 0x24, 0xC7,
+       0xE1, 0x4A, 0xDF, 0xD0, 0x60, 0xB3, 0xC4, 0x44, 0x2E, 0xAC, 0x73, 0x16,
+       0x5F, 0x07, 0xC9, 0x3A, 0x73, 0x5E, 0xDA, 0x9B, 0xEE, 0xE2, 0xB4, 0xA9,
+       0x3D, 0x33, 0x14, 0x7B, 0xEE, 0xA7, 0xD4, 0xAC, 0xF7, 0x53, 0xE6, 0x3E,
+       0xF0, 0x85, 0x57, 0x4C, 0x8B, 0x96, 0x1B, 0xDD, 0xD7, 0x36, 0xFC, 0x89,
+       0x37, 0x59, 0x75, 0x96, 0x75, 0x8B, 0x2E, 0xF7, 0x04, 0x2D, 0x29, 0x89,
+       0xD9, 0xB7, 0x9F, 0x71, 0x3B, 0xE2, 0xED, 0xC7
+};
+
+static const unsigned char RSA_DP[] = {
+       0xE4, 0xFC, 0x02, 0x2F, 0x2B, 0xD6, 0x47, 0x04, 0xD9, 0x15, 0xA4, 0x5A,
+       0x23, 0xF1, 0x14, 0xD7, 0xB9, 0xD1, 0x1C, 0xF6, 0x29, 0xB8, 0x45, 0x57,
+       0x3C, 0xC5, 0x05, 0x91, 0xC9, 0x64, 0xFC, 0x18, 0x2F, 0x84, 0x77, 0x8B,
+       0x6B, 0x2E, 0x64, 0x9D, 0x98, 0x99, 0x12, 0xC7, 0x0F, 0x88, 0xB0, 0x7C,
+       0xCE, 0x4A, 0x87, 0xB8, 0xAE, 0x55, 0x4E, 0xC3, 0x5A, 0x67, 0xE1, 0xE4,
+       0x68, 0x74, 0xC5, 0x8D, 0x14, 0x93, 0xBA, 0xF5, 0xA4, 0x82, 0xB1, 0x9F,
+       0xA6, 0xA1, 0x3C, 0x72, 0x9C, 0xD9, 0xA3, 0x8A, 0x3D, 0x83, 0x86, 0x4A,
+       0x90, 0x8A, 0x72, 0xAF, 0xC6, 0xE1, 0x5C, 0xEB, 0xB9, 0x9C, 0x3B, 0xA6,
+       0x12, 0x0B, 0x1F, 0x36, 0x5A, 0xF5, 0x6E, 0xEA, 0x71, 0x7D, 0x9F, 0x87,
+       0x4E, 0x62, 0x6C, 0x50, 0x3F, 0xF5, 0xE0, 0x9A, 0x30, 0x42, 0x10, 0x2C,
+       0x48, 0x55, 0x24, 0x11, 0xE0, 0x5B, 0x1C, 0xC3
+};
+
+static const unsigned char RSA_DQ[] = {
+       0x8D, 0xAC, 0xE0, 0xA0, 0x33, 0xC0, 0x99, 0x52, 0xB8, 0x90, 0x07, 0x10,
+       0x9B, 0x83, 0xA1, 0xCA, 0xCD, 0xD4, 0x8C, 0x83, 0x68, 0x98, 0x3D, 0xD0,
+       0x18, 0x70, 0xBC, 0xCA, 0x0C, 0xB0, 0x6D, 0x09, 0xE4, 0x25, 0xD4, 0x9D,
+       0x92, 0x00, 0xB0, 0x0F, 0xCB, 0xC2, 0x74, 0x49, 0xF9, 0xE2, 0x60, 0xF8,
+       0x0D, 0xF3, 0xAD, 0xF0, 0x8F, 0x37, 0x6C, 0x62, 0xDE, 0x5A, 0xAE, 0xC3,
+       0xA3, 0x9E, 0x47, 0xD1, 0x36, 0xE4, 0x53, 0x27, 0xC0, 0xEB, 0x6D, 0x92,
+       0x67, 0x14, 0x7E, 0xA2, 0x9B, 0x72, 0x6A, 0x09, 0x93, 0xA1, 0xED, 0xD5,
+       0x31, 0x8F, 0x0C, 0x0B, 0x13, 0xFA, 0x18, 0xB0, 0xF3, 0xE5, 0x9F, 0xC5,
+       0xE2, 0x7A, 0x2D, 0xB8, 0x1C, 0x39, 0x02, 0xB3, 0x8F, 0xE6, 0xB0, 0xCB,
+       0xF5, 0x49, 0x3D, 0x11, 0x54, 0x3D, 0xE5, 0xB9, 0xD4, 0xF2, 0x42, 0x55,
+       0x09, 0x76, 0x4F, 0x4C, 0x3D, 0x9D, 0x25, 0x09
+};
+
+static const unsigned char RSA_IQ[] = {
+       0x72, 0x0C, 0xA7, 0xCF, 0x06, 0x95, 0x69, 0xF4, 0x75, 0x20, 0x34, 0x03,
+       0xE0, 0xCF, 0x9A, 0x51, 0x93, 0xF5, 0x42, 0x2E, 0xF2, 0x85, 0xBE, 0xCE,
+       0x4F, 0x38, 0xB5, 0x8C, 0xA2, 0x99, 0x42, 0xF3, 0xBD, 0x65, 0x38, 0xE2,
+       0x34, 0x3F, 0x21, 0x9D, 0xF5, 0xBD, 0xB3, 0xBF, 0x73, 0x3C, 0x18, 0xDE,
+       0xF6, 0xF0, 0x7F, 0xA1, 0xC2, 0x55, 0xF2, 0x38, 0xE9, 0x0E, 0x1E, 0x31,
+       0xE7, 0xDB, 0x51, 0xC5, 0x71, 0x8D, 0x67, 0x71, 0x3A, 0x9F, 0x55, 0x52,
+       0x60, 0xEE, 0x45, 0xF6, 0x08, 0x98, 0x81, 0xB7, 0x7B, 0x2F, 0xF2, 0x96,
+       0x7D, 0x73, 0xD0, 0xA6, 0xAB, 0xAA, 0x83, 0x49, 0x41, 0x35, 0xA9, 0x90,
+       0x67, 0xCE, 0xD3, 0xB9, 0x73, 0x54, 0xAA, 0x84, 0x00, 0x88, 0x88, 0x90,
+       0x1D, 0x86, 0x9B, 0xE5, 0xB8, 0xCE, 0x89, 0x0A, 0x1B, 0x47, 0x62, 0x31,
+       0xC2, 0x3F, 0xC3, 0x8C, 0x86, 0x09, 0x3C, 0x86
+};
+
+static const br_rsa_private_key RSA = {
+       2048,
+       (unsigned char *)RSA_P, sizeof RSA_P,
+       (unsigned char *)RSA_Q, sizeof RSA_Q,
+       (unsigned char *)RSA_DP, sizeof RSA_DP,
+       (unsigned char *)RSA_DQ, sizeof RSA_DQ,
+       (unsigned char *)RSA_IQ, sizeof RSA_IQ
+};
diff --git a/samples/server_basic.c b/samples/server_basic.c
new file mode 100644 (file)
index 0000000..fdd801e
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include "bearssl.h"
+
+/*
+ * This sample code can use three possible certificate chains:
+ * -- A full-RSA chain (server key is RSA, certificates are signed with RSA)
+ * -- A full-EC chain (server key is EC, certificates are signed with ECDSA)
+ * -- A mixed chain (server key is EC, certificates are signed with RSA)
+ *
+ * The macros below define which chain is selected. This impacts the list
+ * of supported cipher suites.
+ */
+
+#if !(SERVER_RSA || SERVER_EC || SERVER_MIXED)
+#define SERVER_RSA     1
+#define SERVER_EC      0
+#define SERVER_MIXED   0
+#endif
+
+#if SERVER_RSA
+#include "chain-rsa.h"
+#include "key-rsa.h"
+#define SKEY   RSA
+#elif SERVER_EC
+#include "chain-ec.h"
+#include "key-ec.h"
+#define SKEY   EC
+#elif SERVER_MIXED
+#include "chain-ec+rsa.h"
+#include "key-ec.h"
+#define SKEY   EC
+#else
+#error Must use one of RSA, EC or MIXED chains.
+#endif
+
+/*
+ * Create a server socket bound to the specified host and port. If 'host'
+ * is NULL, this will bind "generically" (all addresses).
+ *
+ * Returned value is the server socket descriptor, or -1 on error.
+ */
+static int
+host_bind(const char *host, const char *port)
+{
+       struct addrinfo hints, *si, *p;
+       int fd;
+       int err;
+
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       err = getaddrinfo(host, port, &hints, &si);
+       if (err != 0) {
+               fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+                       gai_strerror(err));
+               return -1;
+       }
+       fd = -1;
+       for (p = si; p != NULL; p = p->ai_next) {
+               struct sockaddr *sa;
+               struct sockaddr_in sa4;
+               struct sockaddr_in6 sa6;
+               size_t sa_len;
+               void *addr;
+               char tmp[INET6_ADDRSTRLEN + 50];
+               int opt;
+
+               sa = (struct sockaddr *)p->ai_addr;
+               if (sa->sa_family == AF_INET) {
+                       sa4 = *(struct sockaddr_in *)sa;
+                       sa = (struct sockaddr *)&sa4;
+                       sa_len = sizeof sa4;
+                       addr = &sa4.sin_addr;
+                       if (host == NULL) {
+                               sa4.sin_addr.s_addr = INADDR_ANY;
+                       }
+               } else if (sa->sa_family == AF_INET6) {
+                       sa6 = *(struct sockaddr_in6 *)sa;
+                       sa = (struct sockaddr *)&sa6;
+                       sa_len = sizeof sa6;
+                       addr = &sa6.sin6_addr;
+                       if (host == NULL) {
+                               sa6.sin6_addr = in6addr_any;
+                       }
+               } else {
+                       addr = NULL;
+                       sa_len = p->ai_addrlen;
+               }
+               if (addr != NULL) {
+                       inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
+               } else {
+                       sprintf(tmp, "<unknown family: %d>",
+                               (int)sa->sa_family);
+               }
+               fprintf(stderr, "binding to: %s\n", tmp);
+               fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+               if (fd < 0) {
+                       perror("socket()");
+                       continue;
+               }
+               opt = 1;
+               setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
+               opt = 0;
+               setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt);
+               if (bind(fd, sa, sa_len) < 0) {
+                       perror("bind()");
+                       close(fd);
+                       continue;
+               }
+               break;
+       }
+       if (p == NULL) {
+               freeaddrinfo(si);
+               fprintf(stderr, "ERROR: failed to bind\n");
+               return -1;
+       }
+       freeaddrinfo(si);
+       if (listen(fd, 5) < 0) {
+               perror("listen()");
+               close(fd);
+               return -1;
+       }
+       fprintf(stderr, "bound.\n");
+       return fd;
+}
+
+/*
+ * Accept a single client on the provided server socket. This is blocking.
+ * On error, this returns -1.
+ */
+static int
+accept_client(int server_fd)
+{
+       int fd;
+       struct sockaddr sa;
+       socklen_t sa_len;
+       char tmp[INET6_ADDRSTRLEN + 50];
+       const char *name;
+
+       sa_len = sizeof sa;
+       fd = accept(server_fd, &sa, &sa_len);
+       if (fd < 0) {
+               perror("accept()");
+               return -1;
+       }
+       name = NULL;
+       switch (sa.sa_family) {
+       case AF_INET:
+               name = inet_ntop(AF_INET,
+                       &((struct sockaddr_in *)&sa)->sin_addr,
+                       tmp, sizeof tmp);
+               break;
+       case AF_INET6:
+               name = inet_ntop(AF_INET,
+                       &((struct sockaddr_in *)&sa)->sin_addr,
+                       tmp, sizeof tmp);
+               break;
+       }
+       if (name == NULL) {
+               sprintf(tmp, "<unknown: %lu>", (unsigned long)sa.sa_family);
+               name = tmp;
+       }
+       fprintf(stderr, "accepting connection from: %s\n", name);
+       return fd;
+}
+
+/*
+ * Low-level data read callback for the simplified SSL I/O API.
+ */
+static int
+sock_read(void *ctx, unsigned char *buf, size_t len)
+{
+       for (;;) {
+               ssize_t rlen;
+
+               rlen = read(*(int *)ctx, buf, len);
+               if (rlen <= 0) {
+                       if (rlen < 0 && errno == EINTR) {
+                               continue;
+                       }
+                       return -1;
+               }
+               return (int)rlen;
+       }
+}
+
+/*
+ * Low-level data write callback for the simplified SSL I/O API.
+ */
+static int
+sock_write(void *ctx, const unsigned char *buf, size_t len)
+{
+       for (;;) {
+               ssize_t wlen;
+
+               wlen = write(*(int *)ctx, buf, len);
+               if (wlen <= 0) {
+                       if (wlen < 0 && errno == EINTR) {
+                               continue;
+                       }
+                       return -1;
+               }
+               return (int)wlen;
+       }
+}
+
+/*
+ * Sample HTTP response to send.
+ */
+static const char *HTTP_RES =
+       "HTTP/1.0 200 OK\r\n"
+       "Content-Length: 46\r\n"
+       "Connection: close\r\n"
+       "Content-Type: text/html; charset=iso-8859-1\r\n"
+       "\r\n"
+       "<html>\r\n"
+       "<body>\r\n"
+       "<p>Test!</p>\r\n"
+       "</body>\r\n"
+       "</html>\r\n";
+
+/*
+ * Main program: this is a simple program that expects 1 argument: a
+ * port number. This will start a simple network server on that port,
+ * that expects incoming SSL clients. It handles only one client at a
+ * time (handling several would require threads, sub-processes, or
+ * multiplexing with select()/poll(), all of which being possible).
+ *
+ * For each client, the server will wait for two successive newline
+ * characters (ignoring CR characters, so CR+LF is accepted), then
+ * produce a sample static HTTP response. This is very crude, but
+ * sufficient for explanatory purposes.
+ */
+int
+main(int argc, char *argv[])
+{
+       const char *port;
+       int fd;
+
+       if (argc != 2) {
+               return EXIT_FAILURE;
+       }
+       port = argv[1];
+
+       /*
+        * Open the server socket.
+        */
+       fd = host_bind(NULL, port);
+       if (fd < 0) {
+               return EXIT_FAILURE;
+       }
+
+       /*
+        * Process each client, one at a time.
+        */
+       for (;;) {
+               int cfd;
+               br_ssl_server_context sc;
+               unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];
+               br_sslio_context ioc;
+               int lcwn, err;
+
+               cfd = accept_client(fd);
+               if (cfd < 0) {
+                       return EXIT_FAILURE;
+               }
+
+               /*
+                * Initialise the context with the cipher suites and
+                * algorithms. This depends on the server key type
+                * (and, for EC keys, the signature algorithm used by
+                * the CA to sign the server's certificate).
+                *
+                * Depending on the defined macros, we may select one of
+                * the "minimal" profiles. Key exchange algorithm depends
+                * on the key type:
+                *   RSA key: RSA or ECDHE_RSA
+                *   EC key, cert signed with ECDSA: ECDH_ECDSA or ECDHE_ECDSA
+                *   EC key, cert signed with RSA: ECDH_RSA or ECDHE_ECDSA
+                */
+#if SERVER_RSA
+#if SERVER_PROFILE_MIN_FS
+               br_ssl_server_init_mine2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#elif SERVER_PROFILE_MIN_NOFS
+               br_ssl_server_init_minr2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+               br_ssl_server_init_full_rsa(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#endif
+#elif SERVER_EC
+#if SERVER_PROFILE_MIN_FS
+               br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#elif SERVER_PROFILE_MIN_NOFS
+               br_ssl_server_init_minv2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+               br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,
+                       BR_KEYTYPE_EC, &SKEY);
+#endif
+#else /* SERVER_MIXED */
+#if SERVER_PROFILE_MIN_FS
+               br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#elif SERVER_PROFILE_MIN_NOFS
+               br_ssl_server_init_minu2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+               br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,
+                       BR_KEYTYPE_RSA, &SKEY);
+#endif
+#endif
+               /*
+                * Set the I/O buffer to the provided array. We
+                * allocated a buffer large enough for full-duplex
+                * behaviour with all allowed sizes of SSL records,
+                * hence we set the last argument to 1 (which means
+                * "split the buffer into separate input and output
+                * areas").
+                */
+               br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);
+
+               /*
+                * Reset the server context, for a new handshake.
+                */
+               br_ssl_server_reset(&sc);
+
+               /*
+                * Initialise the simplified I/O wrapper context.
+                */
+               br_sslio_init(&ioc, &sc.eng, sock_read, &cfd, sock_write, &cfd);
+
+               /*
+                * Read bytes until two successive LF (or CR+LF) are received.
+                */
+               lcwn = 0;
+               for (;;) {
+                       unsigned char x;
+
+                       if (br_sslio_read(&ioc, &x, 1) < 0) {
+                               goto client_drop;
+                       }
+                       if (x == 0x0D) {
+                               continue;
+                       }
+                       if (x == 0x0A) {
+                               if (lcwn) {
+                                       break;
+                               }
+                               lcwn = 1;
+                       } else {
+                               lcwn = 0;
+                       }
+               }
+
+               /*
+                * Write a response and close the connection.
+                */
+               br_sslio_write_all(&ioc, HTTP_RES, strlen(HTTP_RES));
+               br_sslio_close(&ioc);
+
+       client_drop:
+               err = br_ssl_engine_last_error(&sc.eng);
+               if (err == 0) {
+                       fprintf(stderr, "SSL closed (correctly).\n");
+               } else {
+                       fprintf(stderr, "SSL error: %d\n", err);
+               }
+               close(cfd);
+       }
+}
diff --git a/src/codec/ccopy.c b/src/codec/ccopy.c
new file mode 100644 (file)
index 0000000..2beace7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               uint32_t x, y;
+
+               x = *s ++;
+               y = *d;
+               *d = MUX(ctl, x, y);
+               d ++;
+       }
+}
diff --git a/src/codec/dec16be.c b/src/codec/dec16be.c
new file mode 100644 (file)
index 0000000..4f3f7f4
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_dec16be(uint16_t *v, size_t num, const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (num -- > 0) {
+               *v ++ = br_dec16be(buf);
+               buf += 2;
+       }
+}
diff --git a/src/codec/dec16le.c b/src/codec/dec16le.c
new file mode 100644 (file)
index 0000000..84d8536
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_dec16le(uint16_t *v, size_t num, const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (num -- > 0) {
+               *v ++ = br_dec16le(buf);
+               buf += 2;
+       }
+}
diff --git a/src/codec/dec32be.c b/src/codec/dec32be.c
new file mode 100644 (file)
index 0000000..5a8fc59
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_dec32be(uint32_t *v, size_t num, const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (num -- > 0) {
+               *v ++ = br_dec32be(buf);
+               buf += 4;
+       }
+}
diff --git a/src/codec/dec32le.c b/src/codec/dec32le.c
new file mode 100644 (file)
index 0000000..ed36e71
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_dec32le(uint32_t *v, size_t num, const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (num -- > 0) {
+               *v ++ = br_dec32le(buf);
+               buf += 4;
+       }
+}
diff --git a/src/codec/dec64be.c b/src/codec/dec64be.c
new file mode 100644 (file)
index 0000000..0c40a76
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_dec64be(uint64_t *v, size_t num, const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (num -- > 0) {
+               *v ++ = br_dec64be(buf);
+               buf += 8;
+       }
+}
diff --git a/src/codec/dec64le.c b/src/codec/dec64le.c
new file mode 100644 (file)
index 0000000..cbd02c2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_dec64le(uint64_t *v, size_t num, const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (num -- > 0) {
+               *v ++ = br_dec64le(buf);
+               buf += 8;
+       }
+}
diff --git a/src/codec/enc16be.c b/src/codec/enc16be.c
new file mode 100644 (file)
index 0000000..6e06652
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_enc16be(void *dst, const uint16_t *v, size_t num)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (num -- > 0) {
+               br_enc16be(buf, *v ++);
+               buf += 2;
+       }
+}
diff --git a/src/codec/enc16le.c b/src/codec/enc16le.c
new file mode 100644 (file)
index 0000000..3e5049a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_enc16le(void *dst, const uint16_t *v, size_t num)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (num -- > 0) {
+               br_enc16le(buf, *v ++);
+               buf += 2;
+       }
+}
diff --git a/src/codec/enc32be.c b/src/codec/enc32be.c
new file mode 100644 (file)
index 0000000..97298b5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_enc32be(void *dst, const uint32_t *v, size_t num)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (num -- > 0) {
+               br_enc32be(buf, *v ++);
+               buf += 4;
+       }
+}
diff --git a/src/codec/enc32le.c b/src/codec/enc32le.c
new file mode 100644 (file)
index 0000000..9e9c856
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_enc32le(void *dst, const uint32_t *v, size_t num)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (num -- > 0) {
+               br_enc32le(buf, *v ++);
+               buf += 4;
+       }
+}
diff --git a/src/codec/enc64be.c b/src/codec/enc64be.c
new file mode 100644 (file)
index 0000000..d548944
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_enc64be(void *dst, const uint64_t *v, size_t num)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (num -- > 0) {
+               br_enc64be(buf, *v ++);
+               buf += 8;
+       }
+}
diff --git a/src/codec/enc64le.c b/src/codec/enc64le.c
new file mode 100644 (file)
index 0000000..1f1d68e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_range_enc64le(void *dst, const uint64_t *v, size_t num)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (num -- > 0) {
+               br_enc64le(buf, *v ++);
+               buf += 8;
+       }
+}
diff --git a/src/codec/pemdec.c b/src/codec/pemdec.c
new file mode 100644 (file)
index 0000000..1dbdf95
--- /dev/null
@@ -0,0 +1,508 @@
+/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+
+
+void br_pem_decoder_init_main(void *t0ctx);
+
+void br_pem_decoder_run(void *t0ctx);
+
+
+
+#include "inner.h"
+
+#define CTX   ((br_pem_decoder_context *)((unsigned char *)t0ctx - offsetof(br_pem_decoder_context, cpu)))
+
+/* see bearssl_pem.h */
+void
+br_pem_decoder_init(br_pem_decoder_context *ctx)
+{
+       memset(ctx, 0, sizeof *ctx);
+       ctx->cpu.dp = &ctx->dp_stack[0];
+       ctx->cpu.rp = &ctx->rp_stack[0];
+       br_pem_decoder_init_main(&ctx->cpu);
+       br_pem_decoder_run(&ctx->cpu);
+}
+
+/* see bearssl_pem.h */
+size_t
+br_pem_decoder_push(br_pem_decoder_context *ctx,
+       const void *data, size_t len)
+{
+       if (ctx->event) {
+               return 0;
+       }
+       ctx->hbuf = data;
+       ctx->hlen = len;
+       br_pem_decoder_run(&ctx->cpu);
+       return len - ctx->hlen;
+}
+
+/* see bearssl_pem.h */
+int
+br_pem_decoder_event(br_pem_decoder_context *ctx)
+{
+       int event;
+
+       event = ctx->event;
+       ctx->event = 0;
+       return event;
+}
+
+
+
+static const uint8_t t0_datablock[] = {
+       0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20,
+       0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x00
+};
+
+static const uint8_t t0_codeblock[] = {
+       0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01,
+       0x01, 0x08, 0x00, 0x00, 0x13, 0x13, 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_pem_decoder_context, event)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_pem_decoder_context, name)), 0x00, 0x00, 0x05,
+       0x14, 0x2C, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x03, 0x13, 0x04, 0x76, 0x01,
+       0x2D, 0x0C, 0x06, 0x05, 0x2E, 0x01, 0x03, 0x2D, 0x00, 0x01, 0x0D, 0x27,
+       0x05, 0x04, 0x01, 0x03, 0x2D, 0x00, 0x15, 0x2E, 0x01, 0x02, 0x2D, 0x00,
+       0x01, 0x01, 0x7F, 0x03, 0x00, 0x24, 0x01, 0x00, 0x17, 0x0D, 0x06, 0x03,
+       0x13, 0x04, 0x3C, 0x01, 0x7F, 0x17, 0x0D, 0x06, 0x13, 0x13, 0x02, 0x00,
+       0x05, 0x06, 0x2E, 0x01, 0x03, 0x2D, 0x04, 0x03, 0x01, 0x7F, 0x22, 0x01,
+       0x00, 0x00, 0x04, 0x23, 0x01, 0x01, 0x17, 0x0D, 0x06, 0x09, 0x13, 0x01,
+       0x00, 0x22, 0x01, 0x00, 0x00, 0x04, 0x14, 0x01, 0x02, 0x17, 0x0D, 0x06,
+       0x06, 0x13, 0x01, 0x7F, 0x00, 0x04, 0x08, 0x13, 0x01, 0x03, 0x2D, 0x01,
+       0x00, 0x00, 0x13, 0x01, 0x00, 0x03, 0x00, 0x04, 0xFF, 0x33, 0x01, 0x2C,
+       0x14, 0x01, 0x2D, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x7F, 0x00, 0x14, 0x31,
+       0x06, 0x02, 0x13, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01,
+       0x02, 0x00, 0x25, 0x14, 0x1C, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00,
+       0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x03,
+       0x00, 0x25, 0x14, 0x1C, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, 0x02,
+       0x00, 0x01, 0x06, 0x0A, 0x07, 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D,
+       0x06, 0x04, 0x13, 0x01, 0x03, 0x00, 0x14, 0x01, 0x3D, 0x0D, 0x06, 0x2E,
+       0x13, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x03, 0x00,
+       0x2F, 0x05, 0x04, 0x13, 0x01, 0x03, 0x00, 0x01, 0x3D, 0x0C, 0x06, 0x03,
+       0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x0F, 0x10, 0x06, 0x03, 0x01, 0x03,
+       0x00, 0x02, 0x00, 0x01, 0x04, 0x0F, 0x1B, 0x01, 0x01, 0x00, 0x25, 0x14,
+       0x1C, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x06,
+       0x0A, 0x07, 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13,
+       0x01, 0x03, 0x00, 0x14, 0x01, 0x3D, 0x0D, 0x06, 0x20, 0x13, 0x2F, 0x05,
+       0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x03, 0x10, 0x06, 0x03, 0x01,
+       0x03, 0x00, 0x02, 0x00, 0x01, 0x0A, 0x0F, 0x1B, 0x02, 0x00, 0x01, 0x02,
+       0x0F, 0x1B, 0x01, 0x01, 0x00, 0x25, 0x14, 0x1C, 0x06, 0x05, 0x13, 0x2E,
+       0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x06, 0x0A, 0x07, 0x03, 0x00, 0x02,
+       0x00, 0x01, 0x10, 0x0F, 0x1B, 0x02, 0x00, 0x01, 0x08, 0x0F, 0x1B, 0x02,
+       0x00, 0x1B, 0x01, 0x00, 0x00, 0x00, 0x14, 0x14, 0x01, 0x80, 0x41, 0x0E,
+       0x1A, 0x01, 0x80, 0x5A, 0x0B, 0x10, 0x06, 0x05, 0x01, 0x80, 0x41, 0x08,
+       0x00, 0x14, 0x14, 0x01, 0x80, 0x61, 0x0E, 0x1A, 0x01, 0x80, 0x7A, 0x0B,
+       0x10, 0x06, 0x05, 0x01, 0x80, 0x47, 0x08, 0x00, 0x14, 0x14, 0x01, 0x30,
+       0x0E, 0x1A, 0x01, 0x39, 0x0B, 0x10, 0x06, 0x04, 0x01, 0x04, 0x07, 0x00,
+       0x14, 0x01, 0x2B, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x3E, 0x00, 0x14, 0x01,
+       0x2F, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x3F, 0x00, 0x01, 0x3D, 0x0C, 0x1E,
+       0x00, 0x00, 0x28, 0x01, 0x01, 0x2D, 0x23, 0x06, 0x02, 0x04, 0x7B, 0x04,
+       0x75, 0x00, 0x14, 0x12, 0x2A, 0x14, 0x05, 0x04, 0x1F, 0x01, 0x7F, 0x00,
+       0x2C, 0x2A, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x05, 0x13, 0x1F, 0x01, 0x00,
+       0x00, 0x0D, 0x05, 0x05, 0x13, 0x2E, 0x01, 0x00, 0x00, 0x1D, 0x04, 0x5E,
+       0x00, 0x01, 0x01, 0x27, 0x06, 0x0B, 0x21, 0x01, 0x80, 0x7F, 0x2B, 0x14,
+       0x06, 0x02, 0x30, 0x00, 0x13, 0x04, 0x6E, 0x00, 0x2C, 0x14, 0x31, 0x05,
+       0x01, 0x00, 0x13, 0x04, 0x77, 0x00, 0x14, 0x14, 0x01, 0x80, 0x61, 0x0E,
+       0x1A, 0x01, 0x80, 0x7A, 0x0B, 0x10, 0x06, 0x03, 0x01, 0x20, 0x08, 0x00,
+       0x01, 0x14, 0x03, 0x00, 0x1A, 0x17, 0x05, 0x05, 0x1F, 0x2E, 0x01, 0x00,
+       0x00, 0x2C, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x06, 0x1F, 0x02, 0x00, 0x1A,
+       0x08, 0x00, 0x2A, 0x17, 0x19, 0x1D, 0x1A, 0x1E, 0x1A, 0x04, 0x62, 0x00,
+       0x18, 0x14, 0x1C, 0x05, 0x01, 0x00, 0x13, 0x11, 0x04, 0x76, 0x00, 0x20,
+       0x19, 0x11, 0x00, 0x00, 0x2C, 0x01, 0x0A, 0x0C, 0x06, 0x02, 0x04, 0x78,
+       0x00, 0x01, 0x01, 0x7F, 0x03, 0x00, 0x2C, 0x14, 0x01, 0x0A, 0x0C, 0x06,
+       0x09, 0x31, 0x05, 0x04, 0x01, 0x00, 0x03, 0x00, 0x04, 0x70, 0x13, 0x02,
+       0x00, 0x00, 0x00, 0x14, 0x06, 0x14, 0x1E, 0x14, 0x21, 0x07, 0x16, 0x01,
+       0x2D, 0x0C, 0x06, 0x08, 0x21, 0x07, 0x1D, 0x01, 0x00, 0x1A, 0x19, 0x00,
+       0x04, 0x69, 0x21, 0x19, 0x00, 0x00, 0x14, 0x01, 0x0A, 0x0C, 0x1A, 0x01,
+       0x20, 0x0B, 0x10, 0x00
+};
+
+static const uint16_t t0_caddr[] = {
+       0,
+       5,
+       10,
+       15,
+       19,
+       24,
+       29,
+       67,
+       149,
+       384,
+       464,
+       476,
+       511,
+       530,
+       540,
+       559,
+       594,
+       605,
+       610,
+       620,
+       645,
+       672
+};
+
+#define T0_INTERPRETED   28
+
+#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)
+
+#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}
+
+T0_DEFENTRY(br_pem_decoder_init_main, 38)
+
+void
+br_pem_decoder_run(void *t0ctx)
+{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 7: {
+                               /* + */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+
+                               }
+                               break;
+                       case 8: {
+                               /* - */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+
+                               }
+                               break;
+                       case 9: {
+                               /* < */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+
+                               }
+                               break;
+                       case 10: {
+                               /* << */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+
+                               }
+                               break;
+                       case 11: {
+                               /* <= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a <= b));
+
+                               }
+                               break;
+                       case 12: {
+                               /* <> */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+
+                               }
+                               break;
+                       case 13: {
+                               /* = */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+
+                               }
+                               break;
+                       case 14: {
+                               /* >= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+
+                               }
+                               break;
+                       case 15: {
+                               /* >> */
+
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+
+                               }
+                               break;
+                       case 16: {
+                               /* and */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+
+                               }
+                               break;
+                       case 17: {
+                               /* co */
+ T0_CO(); 
+                               }
+                               break;
+                       case 18: {
+                               /* data-get8 */
+
+       size_t addr = T0_POP();
+       T0_PUSH(t0_datablock[addr]);
+
+                               }
+                               break;
+                       case 19: {
+                               /* drop */
+ (void)T0_POP(); 
+                               }
+                               break;
+                       case 20: {
+                               /* dup */
+ T0_PUSH(T0_PEEK(0)); 
+                               }
+                               break;
+                       case 21: {
+                               /* flush-buf */
+
+       if (CTX->ptr > 0) {
+               CTX->dest(CTX->dest_ctx, CTX->buf, CTX->ptr);
+               CTX->ptr = 0;
+       }
+
+                               }
+                               break;
+                       case 22: {
+                               /* get8 */
+
+       size_t addr = T0_POP();
+       T0_PUSH(*((unsigned char *)CTX + addr));
+
+                               }
+                               break;
+                       case 23: {
+                               /* over */
+ T0_PUSH(T0_PEEK(1)); 
+                               }
+                               break;
+                       case 24: {
+                               /* read8-native */
+
+       if (CTX->hlen > 0) {
+               T0_PUSH(*CTX->hbuf ++);
+               CTX->hlen --;
+       } else {
+               T0_PUSHi(-1);
+       }
+
+                               }
+                               break;
+                       case 25: {
+                               /* set8 */
+
+       size_t addr = T0_POP();
+       unsigned x = T0_POP();
+       *((unsigned char *)CTX + addr) = x;
+
+                               }
+                               break;
+                       case 26: {
+                               /* swap */
+ T0_SWAP(); 
+                               }
+                               break;
+                       case 27: {
+                               /* write8 */
+
+       unsigned char x = (unsigned char)T0_POP();
+       CTX->buf[CTX->ptr ++] = x;
+       if (CTX->ptr == sizeof CTX->buf) {
+               if (CTX->dest) {
+                       CTX->dest(CTX->dest_ctx, CTX->buf, sizeof CTX->buf);
+               }
+               CTX->ptr = 0;
+       }
+
+                               }
+                               break;
+                       }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}
diff --git a/src/codec/pemdec.t0 b/src/codec/pemdec.t0
new file mode 100644 (file)
index 0000000..7a0798c
--- /dev/null
@@ -0,0 +1,303 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+preamble {
+
+#include "inner.h"
+
+#define CTX   ((br_pem_decoder_context *)((unsigned char *)t0ctx - offsetof(br_pem_decoder_context, cpu)))
+
+/* see bearssl_pem.h */
+void
+br_pem_decoder_init(br_pem_decoder_context *ctx)
+{
+       memset(ctx, 0, sizeof *ctx);
+       ctx->cpu.dp = &ctx->dp_stack[0];
+       ctx->cpu.rp = &ctx->rp_stack[0];
+       br_pem_decoder_init_main(&ctx->cpu);
+       br_pem_decoder_run(&ctx->cpu);
+}
+
+/* see bearssl_pem.h */
+size_t
+br_pem_decoder_push(br_pem_decoder_context *ctx,
+       const void *data, size_t len)
+{
+       if (ctx->event) {
+               return 0;
+       }
+       ctx->hbuf = data;
+       ctx->hlen = len;
+       br_pem_decoder_run(&ctx->cpu);
+       return len - ctx->hlen;
+}
+
+/* see bearssl_pem.h */
+int
+br_pem_decoder_event(br_pem_decoder_context *ctx)
+{
+       int event;
+
+       event = ctx->event;
+       ctx->event = 0;
+       return event;
+}
+
+}
+
+\ Define a word that evaluates to the address of a field within the
+\ decoder context.
+: addr:
+       next-word { field }
+       "addr-" field + 0 1 define-word
+       0 8191 "offsetof(br_pem_decoder_context, " field + ")" + make-CX
+       postpone literal postpone ; ;
+
+addr: event
+addr: name
+addr: buf
+addr: ptr
+
+\ Set a byte at a specific address (offset within the context).
+cc: set8 ( value addr -- ) {
+       size_t addr = T0_POP();
+       unsigned x = T0_POP();
+       *((unsigned char *)CTX + addr) = x;
+}
+
+\ Get a byte at a specific address (offset within the context).
+cc: get8 ( addr -- value ) {
+       size_t addr = T0_POP();
+       T0_PUSH(*((unsigned char *)CTX + addr));
+}
+
+\ Send an event.
+: send-event ( event -- )
+       addr-event set8 co ;
+
+\ Low-level function to read a single byte. Returned value is the byte
+\ (0 to 255), or -1 if there is no available data.
+cc: read8-native ( -- x ) {
+       if (CTX->hlen > 0) {
+               T0_PUSH(*CTX->hbuf ++);
+               CTX->hlen --;
+       } else {
+               T0_PUSHi(-1);
+       }
+}
+
+\ Read next byte. Block until the next byte is available.
+: read8 ( -- x )
+       begin read8-native dup 0< ifnot ret then drop co again ;
+
+\ Read bytes until next end-of-line.
+: skip-newline ( -- )
+       begin read8 `\n <> while repeat ;
+
+\ Read bytes until next end-of-line; verify that they are all whitespace.
+\ This returns -1 if they were all whitespace, 0 otherwise.
+: skip-newline-ws ( -- bool )
+       -1 { r }
+       begin read8 dup `\n <> while ws? ifnot 0 >r then repeat
+       drop r ;
+
+\ Normalise a byte to uppercase (ASCII only).
+: norm-upper ( x -- x )
+       dup dup `a >= swap `z <= and if 32 - then ;
+
+\ Read bytes and compare with the provided string. On mismatch, the
+\ rest of the line is consumed. Matching is not case sensitive.
+: match-string ( str -- bool )
+       begin
+               dup data-get8 norm-upper dup ifnot 2drop -1 ret then
+               read8 norm-upper dup `\n = if drop 2drop 0 ret then
+               = ifnot drop skip-newline 0 ret then
+               1+
+       again ;
+
+\ Read bytes into the provided buffer, but no more than the provided
+\ count. Reading stops when end-of-line is reached. Returned value
+\ is the count of bytes written to the buffer, or 0 if the buffer size
+\ was exceeded. All bytes are normalised to uppercase (ASCII only).
+: read-bytes ( addr len -- len )
+       dup { orig-len }
+       swap
+       begin
+               over ifnot 2drop skip-newline 0 ret then
+               read8 dup `\n = if 2drop orig-len swap - ret then
+               norm-upper over set8 1+ swap 1- swap
+       again ;
+
+\ Remove trailing dashes from the name buffer.
+: trim-dashes ( len -- )
+       begin dup while
+               1-
+               dup addr-name + get8 `- <> if
+                       addr-name + 1+ 0 swap set8 ret
+               then
+       repeat
+       addr-name set8 ;
+
+\ Scan input for next "begin" banner.
+: next-banner-begin ( -- )
+       begin
+               "-----BEGIN " match-string if
+                       addr-name 127 read-bytes
+                       dup if trim-dashes ret then
+                       drop
+               then
+       again ;
+
+\ Convert a Base64 character to its numerical value. Returned value is
+\ 0 to 63 for Base64 characters, -1 for '=', and -2 for all other characters.
+: from-base64 ( char -- x )
+       dup dup `A >= swap `Z <= and if 65 - ret then
+       dup dup `a >= swap `z <= and if 71 - ret then
+       dup dup `0 >= swap `9 <= and if 4 + ret then
+       dup `+ = if drop 62 ret then
+       dup `/ = if drop 63 ret then
+       `= <> 1- ;
+
+\ Test whether a character is whitespace (but not a newline).
+: ws? ( x -- bool )
+       dup `\n <> swap 32 <= and ;
+
+\ Read next character, skipping whitespace (except newline).
+: next-nonws ( -- x )
+       begin
+               read8 dup ws? ifnot ret then
+               drop
+       again ;
+
+\ Write one byte in the output buffer.
+cc: write8 ( x -- ) {
+       unsigned char x = (unsigned char)T0_POP();
+       CTX->buf[CTX->ptr ++] = x;
+       if (CTX->ptr == sizeof CTX->buf) {
+               if (CTX->dest) {
+                       CTX->dest(CTX->dest_ctx, CTX->buf, sizeof CTX->buf);
+               }
+               CTX->ptr = 0;
+       }
+}
+
+\ Flush the output buffer.
+cc: flush-buf ( -- ) {
+       if (CTX->ptr > 0) {
+               CTX->dest(CTX->dest_ctx, CTX->buf, CTX->ptr);
+               CTX->ptr = 0;
+       }
+}
+
+\ Decode the four next Base64 characters. Returned value is:
+\    0   quartet processed, three bytes produced.
+\   -1   dash encountered as first character (no leading whitespace).
+\    1   quartet processed, one or two bytes produced, terminator reached.
+\    2   end-of-line reached.
+\    3   error.
+\ For all positive return values, the remaining of the current line has been
+\ consumed.
+: decode-next-quartet ( -- r )
+       \ Process first character. It may be a dash.
+       read8 dup `- = if drop -1 ret then
+       dup ws? if drop next-nonws then
+       dup `\n = if drop 2 ret then
+       from-base64 dup 0< if drop skip-newline 3 ret then
+       { acc }
+
+       \ Second character.
+       next-nonws dup `\n = if drop 3 ret then
+       from-base64 dup 0< if drop skip-newline 3 ret then
+       acc 6 << + >acc
+
+       \ Third character: may be an equal sign.
+       next-nonws dup `\n = if drop 3 ret then
+       dup `= = if
+               \ Fourth character must be an equal sign.
+               drop
+               next-nonws dup `\n = if drop 3 ret then
+               skip-newline-ws ifnot drop 3 ret then
+               `= <> if 3 ret then
+               acc 0x0F and if 3 ret then
+               acc 4 >> write8
+               1 ret
+       then
+       from-base64 dup 0< if drop skip-newline 3 ret then
+       acc 6 << + >acc
+
+       \ Fourth character: may be an equal sign.
+       next-nonws dup `\n = if drop 3 ret then
+       dup `= = if
+               drop skip-newline-ws ifnot 3 ret then
+               acc 0x03 and if 3 ret then
+               acc 10 >> write8
+               acc 2 >> write8
+               1 ret
+       then
+       from-base64 dup 0< if drop skip-newline 3 ret then
+       acc 6 << + >acc
+       acc 16 >> write8
+       acc 8 >> write8
+       acc write8
+       0 ;
+
+\ Check trailer line (possibly, the leading dash has been read). This
+\ sends the appropriate event.
+: check-trailer ( bool -- )
+       ifnot
+               begin read8 dup `\n = while drop repeat
+               `- <> if skip-newline 3 send-event ret then
+       then
+       "----END " match-string ifnot 3 send-event ret then
+       flush-buf
+       skip-newline 2 send-event ;
+
+\ Decode one line worth of characters. Returned value is 0 if the end of the
+\ object is reached, -1 otherwise. The end of object or error event is sent.
+: decode-line ( -- bool )
+       -1 { first }
+       begin
+               decode-next-quartet
+               case
+                       0 of endof
+                       -1 of
+                               first ifnot
+                                       skip-newline 3 send-event
+                               else
+                                       -1 check-trailer
+                               then
+                               0 ret
+                       endof
+                       1 of 0 check-trailer 0 ret endof
+                       2 of -1 ret endof
+
+                       \ On decoding error
+                       drop 3 send-event 0 ret
+               endcase
+               0 >first
+       again ;
+
+: main ( -- ! )
+       begin
+               next-banner-begin 1 send-event
+               begin decode-line while repeat
+       again ;
diff --git a/src/config.h b/src/config.h
new file mode 100644 (file)
index 0000000..259f5bb
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef CONFIG_H__
+#define CONFIG_H__
+
+/*
+ * This file contains compile-time flags that can override the
+ * autodetection performed in relevant files. Each flag is a macro; it
+ * deactivates the feature if defined to 0, activates it if defined to a
+ * non-zero integer (normally 1). If the macro is not defined, then
+ * autodetection applies.
+ */
+
+/*
+ * When BR_64 is enabled, 64-bit integer types are assumed to be
+ * efficient (i.e. the architecture has 64-bit registers and can
+ * do 64-bit operations as fast as 32-bit operations).
+ *
+#define BR_64   1
+ */
+
+/*
+ * When BR_SLOW_MUL is enabled, multiplications are assumed to be
+ * substantially slow with regards to other integer operations, thus
+ * making it worth to make more operations for a given task if it allows
+ * using less multiplications.
+ *
+#define BR_SLOW_MUL   1
+ */
+
+/*
+ * When BR_CT_MUL31 is enabled, multiplications of 31-bit values (used
+ * in the "i31" big integer implementation) use an alternate implementation
+ * which is slower and larger than the normal multiplication, but should
+ * ensure constant-time multiplications even on architectures where the
+ * multiplication opcode takes a variable number of cycles to complete.
+ *
+#define BR_CT_MUL31   1
+ */
+
+/*
+ * When BR_USE_URANDOM is enabled, the SSL engine will use /dev/urandom
+ * to automatically obtain quality randomness for seedings its internal
+ * PRNG.
+ *
+#define BR_USE_URANDOM   1
+ */
+
+/*
+ * When BR_USE_WIN32_RAND is enabled, the SSL engine will use the Win32
+ * (CryptoAPI) functions (CryptAcquireContext(), CryptGenRandom()...) to
+ * automatically obtain quality randomness for seedings its internal PRNG.
+ *
+ * Note: if both BR_USE_URANDOM and BR_USE_WIN32_RAND are defined, the
+ * former takes precedence.
+ *
+#define BR_USE_WIN32_RAND   1
+ */
+
+/*
+ * When BR_USE_UNIX_TIME is enabled, the X.509 validation engine obtains
+ * the current time from the OS by calling time(), and assuming that the
+ * returned value (a 'time_t') is an integer that counts time in seconds
+ * since the Unix Epoch (Jan 1st, 1970, 00:00 UTC).
+ *
+#define BR_USE_UNIX_TIME   1
+ */
+
+/*
+ * When BR_USE_WIN32_TIME is enabled, the X.509 validation engine obtains
+ * the current time from the OS by calling the Win32 function
+ * GetSystemTimeAsFileTime().
+ *
+ * Note: if both BR_USE_UNIX_TIME and BR_USE_WIN32_TIME are defined, the
+ * former takes precedence.
+ *
+#define BR_USE_WIN32_TIME   1
+ */
+
+#endif
diff --git a/src/ec/ec_prime_i31.c b/src/ec/ec_prime_i31.c
new file mode 100644 (file)
index 0000000..440641e
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Parameters for supported curves (field modulus, and 'b' equation
+ * parameter; both values use the 'i31' format, and 'b' is in Montgomery
+ * representation).
+ */
+
+static const uint32_t P256_P[] = {
+       0x00000108,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x00000007,
+       0x00000000, 0x00000000, 0x00000040, 0x7FFFFF80,
+       0x000000FF
+};
+
+static const uint32_t P256_R2[] = {
+       0x00000108,
+       0x00014000, 0x00018000, 0x00000000, 0x7FF40000,
+       0x7FEFFFFF, 0x7FF7FFFF, 0x7FAFFFFF, 0x005FFFFF,
+       0x00000000
+};
+
+static const uint32_t P256_B[] = {
+       0x00000108,
+       0x6FEE1803, 0x6229C4BD, 0x21B139BE, 0x327150AA,
+       0x3567802E, 0x3F7212ED, 0x012E4355, 0x782DD38D,
+       0x0000000E
+};
+
+static const uint32_t P384_P[] = {
+       0x0000018C,
+       0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8,
+       0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x00000FFF
+};
+
+static const uint32_t P384_R2[] = {
+       0x0000018C,
+       0x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF,
+       0x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF,
+       0x00008000, 0x00008000, 0x00000000, 0x00000000,
+       0x00000000
+};
+
+static const uint32_t P384_B[] = {
+       0x0000018C,
+       0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C,
+       0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B,
+       0x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE,
+       0x000008A5
+};
+
+static const uint32_t P521_P[] = {
+       0x00000219,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x01FFFFFF
+};
+
+static const uint32_t P521_R2[] = {
+       0x00000219,
+       0x00001000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000
+};
+
+static const uint32_t P521_B[] = {
+       0x00000219,
+       0x540FC00A, 0x228FEA35, 0x2C34F1EF, 0x67BF107A,
+       0x46FC1CD5, 0x1605E9DD, 0x6937B165, 0x272A3D8F,
+       0x42785586, 0x44C8C778, 0x15F3B8B4, 0x64B73366,
+       0x03BA8B69, 0x0D05B42A, 0x21F929A2, 0x2C31C393,
+       0x00654FAE
+};
+
+typedef struct {
+       const uint32_t *p;
+       const uint32_t *b;
+       const uint32_t *R2;
+       uint32_t p0i;
+} curve_params;
+
+static inline const curve_params *
+id_to_curve(int curve)
+{
+       static const curve_params pp[] = {
+               { P256_P, P256_B, P256_R2, 0x00000001 },
+               { P384_P, P384_B, P384_R2, 0x00000001 },
+               { P521_P, P521_B, P521_R2, 0x00000001 }
+       };
+
+       return &pp[curve - BR_EC_secp256r1];
+}
+
+#define I31_LEN   ((BR_MAX_EC_SIZE + 61) / 31)
+
+/*
+ * Type for a point in Jacobian coordinates:
+ * -- three values, x, y and z, in Montgomery representation
+ * -- affine coordinates are X = x / z^2 and Y = y / z^3
+ * -- for the point at infinity, z = 0
+ */
+typedef struct {
+       uint32_t c[3][I31_LEN];
+} jacobian;
+
+/*
+ * We use a custom interpreter that uses a dozen registers, and
+ * only four operations:
+ *    MSET(d, a)       copy a into d
+ *    MADD(d, a)       d = d+a (modular)
+ *    MSUB(d, a)       d = d-a (modular)
+ *    MMUL(d, a, b)    d = a*b (Montgomery multiplication)
+ *    MINV(d, a, b)    invert d modulo p; a and b are used as scratch registers
+ *    MTZ(d)           clear return value if d = 0
+ * Destination of MMUL (d) must be distinct from operands (a and b).
+ * There is no such constraint for MSUB and MADD.
+ *
+ * Registers include the operand coordinates, and temporaries.
+ */
+#define MSET(d, a)      (0x0000 + ((d) << 8) + ((a) << 4))
+#define MADD(d, a)      (0x1000 + ((d) << 8) + ((a) << 4))
+#define MSUB(d, a)      (0x2000 + ((d) << 8) + ((a) << 4))
+#define MMUL(d, a, b)   (0x3000 + ((d) << 8) + ((a) << 4) + (b))
+#define MINV(d, a, b)   (0x4000 + ((d) << 8) + ((a) << 4) + (b))
+#define MTZ(d)          (0x5000 + ((d) << 8))
+#define ENDCODE         0
+
+/*
+ * Registers for the input operands.
+ */
+#define P1x    0
+#define P1y    1
+#define P1z    2
+#define P2x    3
+#define P2y    4
+#define P2z    5
+
+/*
+ * Alternate names for the first input operand.
+ */
+#define Px     0
+#define Py     1
+#define Pz     2
+
+/*
+ * Temporaries.
+ */
+#define t1     6
+#define t2     7
+#define t3     8
+#define t4     9
+#define t5    10
+#define t6    11
+#define t7    12
+
+/*
+ * Extra scratch registers available when there is no second operand (e.g.
+ * for "double" and "affine").
+ */
+#define t8     3
+#define t9     4
+#define t10    5
+
+/*
+ * Doubling formulas are:
+ *
+ *   s = 4*x*y^2
+ *   m = 3*(x + z^2)*(x - z^2)
+ *   x' = m^2 - 2*s
+ *   y' = m*(s - x') - 8*y^4
+ *   z' = 2*y*z
+ *
+ * If y = 0 (P has order 2) then this yields infinity (z' = 0), as it
+ * should. This case should not happen anyway, because our curves have
+ * prime order, and thus do not contain any point of order 2.
+ *
+ * If P is infinity (z = 0), then again the formulas yield infinity,
+ * which is correct. Thus, this code works for all points.
+ *
+ * Cost: 8 multiplications
+ */
+static const uint16_t code_double[] = {
+       /*
+        * Compute z^2 (in t1).
+        */
+       MMUL(t1, Pz, Pz),
+
+       /*
+        * Compute x-z^2 (in t2) and then x+z^2 (in t1).
+        */
+       MSET(t2, Px),
+       MSUB(t2, t1),
+       MADD(t1, Px),
+
+       /*
+        * Compute m = 3*(x+z^2)*(x-z^2) (in t1).
+        */
+       MMUL(t3, t1, t2),
+       MSET(t1, t3),
+       MADD(t1, t3),
+       MADD(t1, t3),
+
+       /*
+        * Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3).
+        */
+       MMUL(t3, Py, Py),
+       MADD(t3, t3),
+       MMUL(t2, Px, t3),
+       MADD(t2, t2),
+
+       /*
+        * Compute x' = m^2 - 2*s.
+        */
+       MMUL(Px, t1, t1),
+       MSUB(Px, t2),
+       MSUB(Px, t2),
+
+       /*
+        * Compute z' = 2*y*z.
+        */
+       MMUL(t4, Py, Pz),
+       MSET(Pz, t4),
+       MADD(Pz, t4),
+
+       /*
+        * Compute y' = m*(s - x') - 8*y^4. Note that we already have
+        * 2*y^2 in t3.
+        */
+       MSUB(t2, Px),
+       MMUL(Py, t1, t2),
+       MMUL(t4, t3, t3),
+       MSUB(Py, t4),
+       MSUB(Py, t4),
+
+       ENDCODE
+};
+
+/*
+ * Addtions formulas are:
+ *
+ *   u1 = x1 * z2^2
+ *   u2 = x2 * z1^2
+ *   s1 = y1 * z2^3
+ *   s2 = y2 * z1^3
+ *   h = u2 - u1
+ *   r = s2 - s1
+ *   x3 = r^2 - h^3 - 2 * u1 * h^2
+ *   y3 = r * (u1 * h^2 - x3) - s1 * h^3
+ *   z3 = h * z1 * z2
+ *
+ * If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that
+ * z3 == 0, so the result is correct.
+ * If either of P1 or P2 is infinity, but not both, then z3 == 0, which is
+ * not correct.
+ * h == 0 only if u1 == u2; this happens in two cases:
+ * -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2
+ * -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity)
+ *
+ * Thus, the following situations are not handled correctly:
+ * -- P1 = 0 and P2 != 0
+ * -- P1 != 0 and P2 = 0
+ * -- P1 = P2
+ * All other cases are properly computed. However, even in "incorrect"
+ * situations, the three coordinates still are properly formed field
+ * elements.
+ *
+ * The returned flag is cleared if r == 0. This happens in the following
+ * cases:
+ * -- Both points are on the same horizontal line (same Y coordinate).
+ * -- Both points are infinity.
+ * -- One point is infinity and the other is on line Y = 0.
+ * The third case cannot happen with our curves (there is no valid point
+ * on line Y = 0 since that would be a point of order 2). If the two
+ * source points are non-infinity, then remains only the case where the
+ * two points are on the same horizontal line.
+ *
+ * This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and
+ * P2 != 0:
+ * -- If the returned value is not the point at infinity, then it was properly
+ * computed.
+ * -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result
+ * is indeed the point at infinity.
+ * -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should
+ * use the 'double' code.
+ *
+ * Cost: 16 multiplications
+ */
+static const uint16_t code_add[] = {
+       /*
+        * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3).
+        */
+       MMUL(t3, P2z, P2z),
+       MMUL(t1, P1x, t3),
+       MMUL(t4, P2z, t3),
+       MMUL(t3, P1y, t4),
+
+       /*
+        * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).
+        */
+       MMUL(t4, P1z, P1z),
+       MMUL(t2, P2x, t4),
+       MMUL(t5, P1z, t4),
+       MMUL(t4, P2y, t5),
+
+       /*
+        * Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4).
+        */
+       MSUB(t2, t1),
+       MSUB(t4, t3),
+
+       /*
+        * Report cases where r = 0 through the returned flag.
+        */
+       MTZ(t4),
+
+       /*
+        * Compute u1*h^2 (in t6) and h^3 (in t5).
+        */
+       MMUL(t7, t2, t2),
+       MMUL(t6, t1, t7),
+       MMUL(t5, t7, t2),
+
+       /*
+        * Compute x3 = r^2 - h^3 - 2*u1*h^2.
+        * t1 and t7 can be used as scratch registers.
+        */
+       MMUL(P1x, t4, t4),
+       MSUB(P1x, t5),
+       MSUB(P1x, t6),
+       MSUB(P1x, t6),
+
+       /*
+        * Compute y3 = r*(u1*h^2 - x3) - s1*h^3.
+        */
+       MSUB(t6, P1x),
+       MMUL(P1y, t4, t6),
+       MMUL(t1, t5, t3),
+       MSUB(P1y, t1),
+
+       /*
+        * Compute z3 = h*z1*z2.
+        */
+       MMUL(t1, P1z, P2z),
+       MMUL(P1z, t1, t2),
+
+       ENDCODE
+};
+
+/*
+ * Check that the point is on the curve. This code snippet assumes the
+ * following conventions:
+ * -- Coordinates x and y have been freshly decoded in P1 (but not
+ * converted to Montgomery coordinates yet).
+ * -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1.
+ */
+static const uint16_t code_check[] = {
+
+       /* Convert x and y to Montgomery representation. */
+       MMUL(t1, P1x, P2x),
+       MMUL(t2, P1y, P2x),
+       MSET(P1x, t1),
+       MSET(P1y, t2),
+
+       /* Compute x^3 in t1. */
+       MMUL(t2, P1x, P1x),
+       MMUL(t1, P1x, t2),
+
+       /* Subtract 3*x from t1. */
+       MSUB(t1, P1x),
+       MSUB(t1, P1x),
+       MSUB(t1, P1x),
+
+       /* Add b. */
+       MADD(t1, P2y),
+
+       /* Compute y^2 in t2. */
+       MMUL(t2, P1y, P1y),
+
+       /* Compare y^2 with x^3 - 3*x + b; they must match. */
+       MSUB(t1, t2),
+       MTZ(t1),
+
+       /* Set z to 1 (in Montgomery representation). */
+       MMUL(P1z, P2x, P2z),
+
+       ENDCODE
+};
+
+/*
+ * Conversion back to affine coordinates. This code snippet assumes that
+ * the z coordinate of P2 is set to 1 (not in Montgomery representation).
+ */
+static const uint16_t code_affine[] = {
+
+       /* Save z*R in t1. */
+       MSET(t1, P1z),
+
+       /* Compute z^3 in t2. */
+       MMUL(t2, P1z, P1z),
+       MMUL(t3, P1z, t2),
+       MMUL(t2, t3, P2z),
+
+       /* Invert to (1/z^3) in t2. */
+       MINV(t2, t3, t4),
+
+       /* Compute y. */
+       MSET(t3, P1y),
+       MMUL(P1y, t2, t3),
+
+       /* Compute (1/z^2) in t3. */
+       MMUL(t3, t2, t1),
+
+       /* Compute x. */
+       MSET(t2, P1x),
+       MMUL(P1x, t2, t3),
+
+       ENDCODE
+};
+
+static uint32_t
+run_code(jacobian *P1, const jacobian *P2,
+       const curve_params *cc, const uint16_t *code)
+{
+       uint32_t r;
+       uint32_t t[13][I31_LEN];
+       size_t u;
+
+       r = 1;
+
+       /*
+        * Copy the two operands in the dedicated registers.
+        */
+       memcpy(t[P1x], P1->c, 3 * I31_LEN * sizeof(uint32_t));
+       memcpy(t[P2x], P2->c, 3 * I31_LEN * sizeof(uint32_t));
+
+       /*
+        * Run formulas.
+        */
+       for (u = 0;; u ++) {
+               unsigned op, d, a, b;
+
+               op = code[u];
+               if (op == 0) {
+                       break;
+               }
+               d = (op >> 8) & 0x0F;
+               a = (op >> 4) & 0x0F;
+               b = op & 0x0F;
+               op >>= 12;
+               switch (op) {
+                       uint32_t ctl;
+                       size_t plen;
+                       unsigned char tp[(BR_MAX_EC_SIZE + 7) >> 3];
+
+               case 0:
+                       memcpy(t[d], t[a], I31_LEN * sizeof(uint32_t));
+                       break;
+               case 1:
+                       ctl = br_i31_add(t[d], t[a], 1);
+                       ctl |= NOT(br_i31_sub(t[d], cc->p, 0));
+                       br_i31_sub(t[d], cc->p, ctl);
+                       break;
+               case 2:
+                       br_i31_add(t[d], cc->p, br_i31_sub(t[d], t[a], 1));
+                       break;
+               case 3:
+                       br_i31_montymul(t[d], t[a], t[b], cc->p, cc->p0i);
+                       break;
+               case 4:
+                       plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;
+                       br_i31_encode(tp, plen, cc->p);
+                       tp[plen - 1] -= 2;
+                       br_i31_modpow(t[d], tp, plen,
+                               cc->p, cc->p0i, t[a], t[b]);
+                       break;
+               default:
+                       r &= ~br_i31_iszero(t[d]);
+                       break;
+               }
+       }
+
+       /*
+        * Copy back result.
+        */
+       memcpy(P1->c, t[P1x], 3 * I31_LEN * sizeof(uint32_t));
+       return r;
+}
+
+static void
+set_one(uint32_t *x, const uint32_t *p)
+{
+       size_t plen;
+
+       plen = (p[0] + 63) >> 5;
+       memset(x, 0, plen * sizeof *x);
+       x[0] = p[0];
+       x[1] = 0x00000001;
+}
+
+static void
+point_zero(jacobian *P, const curve_params *cc)
+{
+       memset(P, 0, sizeof *P);
+       P->c[0][0] = P->c[1][0] = P->c[2][0] = cc->p[0];
+}
+
+static inline void
+point_double(jacobian *P, const curve_params *cc)
+{
+       run_code(P, P, cc, code_double);
+}
+
+static inline uint32_t
+point_add(jacobian *P1, const jacobian *P2, const curve_params *cc)
+{
+       return run_code(P1, P2, cc, code_add);
+}
+
+static void
+point_mul(jacobian *P, const unsigned char *x, size_t xlen,
+       const curve_params *cc)
+{
+       /*
+        * We do a simple double-and-add ladder with a 2-bit window
+        * to make only one add every two doublings. We thus first
+        * precompute 2P and 3P in some local buffers.
+        *
+        * We always perform two doublings and one addition; the
+        * addition is with P, 2P and 3P and is done in a temporary
+        * array.
+        *
+        * The addition code cannot handle cases where one of the
+        * operands is infinity, which is the case at the start of the
+        * ladder. We therefore need to maintain a flag that controls
+        * this situation.
+        */
+       uint32_t qz;
+       jacobian P2, P3, Q, T, U;
+
+       memcpy(&P2, P, sizeof P2);
+       point_double(&P2, cc);
+       memcpy(&P3, P, sizeof P3);
+       point_add(&P3, &P2, cc);
+
+       point_zero(&Q, cc);
+       qz = 1;
+       while (xlen -- > 0) {
+               int k;
+
+               for (k = 6; k >= 0; k -= 2) {
+                       uint32_t bits;
+                       uint32_t bnz;
+
+                       point_double(&Q, cc);
+                       point_double(&Q, cc);
+                       memcpy(&T, P, sizeof T);
+                       memcpy(&U, &Q, sizeof U);
+                       bits = (*x >> k) & (uint32_t)3;
+                       bnz = NEQ(bits, 0);
+                       CCOPY(EQ(bits, 2), &T, &P2, sizeof T);
+                       CCOPY(EQ(bits, 3), &T, &P3, sizeof T);
+                       point_add(&U, &T, cc);
+                       CCOPY(bnz & qz, &Q, &T, sizeof Q);
+                       CCOPY(bnz & ~qz, &Q, &U, sizeof Q);
+                       qz &= ~bnz;
+               }
+               x ++;
+       }
+       memcpy(P, &Q, sizeof Q);
+}
+
+/*
+ * Decode point into Jacobian coordinates. This function does not support
+ * the point at infinity. If the point is invalid then this returns 0, but
+ * the coordinates are still set to properly formed field elements.
+ */
+static uint32_t
+point_decode(jacobian *P, const void *src, size_t len, const curve_params *cc)
+{
+       /*
+        * Points must use uncompressed format:
+        * -- first byte is 0x04;
+        * -- coordinates X and Y use unsigned big-endian, with the same
+        *    length as the field modulus.
+        *
+        * We don't support hybrid format (uncompressed, but first byte
+        * has value 0x06 or 0x07, depending on the least significant bit
+        * of Y) because it is rather useless, and explicitly forbidden
+        * by PKIX (RFC 5480, section 2.2).
+        *
+        * We don't support compressed format either, because it is not
+        * much used in practice (there are or were patent-related
+        * concerns about point compression, which explains the lack of
+        * generalised support). Also, point compression support would
+        * need a bit more code.
+        */
+       const unsigned char *buf;
+       size_t plen, zlen;
+       uint32_t r;
+       jacobian Q;
+
+       buf = src;
+       point_zero(P, cc);
+       plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;
+       if (len != 1 + (plen << 1)) {
+               return 0;
+       }
+       r = br_i31_decode_mod(P->c[0], buf + 1, plen, cc->p);
+       r &= br_i31_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p);
+
+       /*
+        * Check first byte.
+        */
+       r &= EQ(buf[0], 0x04);
+       /* obsolete
+       r &= EQ(buf[0], 0x04) | (EQ(buf[0] & 0xFE, 0x06)
+               & ~(uint32_t)(buf[0] ^ buf[plen << 1]));
+       */
+
+       /*
+        * Convert coordinates and check that the point is valid.
+        */
+       zlen = ((cc->p[0] + 63) >> 5) * sizeof(uint32_t);
+       memcpy(Q.c[0], cc->R2, zlen);
+       memcpy(Q.c[1], cc->b, zlen);
+       set_one(Q.c[2], cc->p);
+       r &= ~run_code(P, &Q, cc, code_check);
+       return r;
+}
+
+/*
+ * Encode a point. This method assumes that the point is correct and is
+ * not the point at infinity. Encoded size is always 1+2*plen, where
+ * plen is the field modulus length, in bytes.
+ */
+static void
+point_encode(void *dst, const jacobian *P, const curve_params *cc)
+{
+       unsigned char *buf;
+       uint32_t xbl;
+       size_t plen;
+       jacobian Q, T;
+
+       buf = dst;
+       xbl = cc->p[0];
+       xbl -= (xbl >> 5);
+       plen = (xbl + 7) >> 3;
+       buf[0] = 0x04;
+       memcpy(&Q, P, sizeof *P);
+       set_one(T.c[2], cc->p);
+       run_code(&Q, &T, cc, code_affine);
+       br_i31_encode(buf + 1, plen, Q.c[0]);
+       br_i31_encode(buf + 1 + plen, plen, Q.c[1]);
+}
+
+static const br_ec_curve_def *
+id_to_curve_def(int curve)
+{
+       switch (curve) {
+       case BR_EC_secp256r1:
+               return &br_secp256r1;
+       case BR_EC_secp384r1:
+               return &br_secp384r1;
+       case BR_EC_secp521r1:
+               return &br_secp521r1;
+       }
+       return NULL;
+}
+
+static const unsigned char *
+api_generator(int curve, size_t *len)
+{
+       const br_ec_curve_def *cd;
+
+       cd = id_to_curve_def(curve);
+       *len = cd->generator_len;
+       return cd->generator;
+}
+
+static const unsigned char *
+api_order(int curve, size_t *len)
+{
+       const br_ec_curve_def *cd;
+
+       cd = id_to_curve_def(curve);
+       *len = cd->order_len;
+       return cd->order;
+}
+
+static uint32_t
+api_mul(unsigned char *G, size_t Glen,
+       const unsigned char *x, size_t xlen, int curve)
+{
+       uint32_t r;
+       const curve_params *cc;
+       jacobian P;
+
+       cc = id_to_curve(curve);
+       r = point_decode(&P, G, Glen, cc);
+       point_mul(&P, x, xlen, cc);
+       point_encode(G, &P, cc);
+       return r;
+}
+
+static uint32_t
+api_muladd(unsigned char *A, const unsigned char *B, size_t len,
+       const unsigned char *x, size_t xlen,
+       const unsigned char *y, size_t ylen, int curve)
+{
+       uint32_t r, t, z;
+       const curve_params *cc;
+       jacobian P, Q;
+
+       /*
+        * TODO: see about merging the two ladders. Right now, we do
+        * two independant point multiplications, which is a bit
+        * wasteful of CPU resources (but yields short code).
+        */
+
+       cc = id_to_curve(curve);
+       r = point_decode(&P, A, len, cc);
+       r &= point_decode(&Q, B, len, cc);
+       point_mul(&P, x, xlen, cc);
+       point_mul(&Q, y, ylen, cc);
+
+       /*
+        * We want to compute P+Q. Since the base points A and B are distinct
+        * from infinity, and the multipliers are non-zero and lower than the
+        * curve order, then we know that P and Q are non-infinity. This
+        * leaves two special situations to test for:
+        * -- If P = Q then we must use point_double().
+        * -- If P+Q = 0 then we must report an error.
+        */
+       t = point_add(&P, &Q, cc);
+       point_double(&Q, cc);
+       z = br_i31_iszero(P.c[2]);
+
+       /*
+        * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we
+        * have the following:
+        *
+        *   z = 0, t = 0   return P (normal addition)
+        *   z = 0, t = 1   return P (normal addition)
+        *   z = 1, t = 0   return Q (a 'double' case)
+        *   z = 1, t = 1   report an error (P+Q = 0)
+        */
+       CCOPY(z & ~t, &P, &Q, sizeof Q);
+       point_encode(A, &P, cc);
+       r &= ~(z & t);
+
+       return r;
+}
+
+/* see bearssl_ec.h */
+const br_ec_impl br_ec_prime_i31 = {
+       (uint32_t)0x03800000,
+       &api_generator,
+       &api_order,
+       &api_mul,
+       &api_muladd
+};
diff --git a/src/ec/ec_prime_i31_secp256r1.c b/src/ec/ec_prime_i31_secp256r1.c
new file mode 100644 (file)
index 0000000..007b6b2
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const uint32_t P256_P[] = {
+       0x00000108,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x00000007,
+       0x00000000, 0x00000000, 0x00000040, 0x7FFFFF80,
+       0x000000FF
+};
+
+static const uint32_t P256_B[] = {
+       0x00000108,
+       0x6FEE1803, 0x6229C4BD, 0x21B139BE, 0x327150AA,
+       0x3567802E, 0x3F7212ED, 0x012E4355, 0x782DD38D,
+       0x0000000E
+};
+
+/* see inner.h */
+const br_ec_prime_i31_curve br_ec_prime_i31_secp256r1 = {
+       P256_P,
+       P256_B,
+       0x00000001
+};
diff --git a/src/ec/ec_prime_i31_secp384r1.c b/src/ec/ec_prime_i31_secp384r1.c
new file mode 100644 (file)
index 0000000..9f92b4f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const uint32_t P384_P[] = {
+       0x0000018C,
+       0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8,
+       0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x00000FFF
+};
+
+static const uint32_t P384_B[] = {
+       0x0000018C,
+       0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C,
+       0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B,
+       0x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE,
+       0x000008A5
+};
+
+/* see inner.h */
+const br_ec_prime_i31_curve br_ec_prime_i31_secp384r1 = {
+       P384_P,
+       P384_B,
+       0x00000001
+};
diff --git a/src/ec/ec_prime_i31_secp521r1.c b/src/ec/ec_prime_i31_secp521r1.c
new file mode 100644 (file)
index 0000000..84d7d54
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const uint32_t P521_P[] = {
+       0x00000219,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
+       0x01FFFFFF
+};
+
+static const uint32_t P521_B[] = {
+       0x00000219,
+       0x540FC00A, 0x228FEA35, 0x2C34F1EF, 0x67BF107A,
+       0x46FC1CD5, 0x1605E9DD, 0x6937B165, 0x272A3D8F,
+       0x42785586, 0x44C8C778, 0x15F3B8B4, 0x64B73366,
+       0x03BA8B69, 0x0D05B42A, 0x21F929A2, 0x2C31C393,
+       0x00654FAE
+};
+
+/* see inner.h */
+const br_ec_prime_i31_curve br_ec_prime_i31_secp521r1 = {
+       P521_P,
+       P521_B,
+       0x00000001
+};
diff --git a/src/ec/ec_secp256r1.c b/src/ec/ec_secp256r1.c
new file mode 100644 (file)
index 0000000..a9d6c45
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const unsigned char P256_N[] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+       0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
+};
+
+static const unsigned char P256_G[] = {
+       0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42,
+       0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40,
+       0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33,
+       0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+       0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F,
+       0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E,
+       0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E,
+       0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51,
+       0xF5
+};
+
+/* see inner.h */
+const br_ec_curve_def br_secp256r1 = {
+       BR_EC_secp256r1,
+       P256_N, sizeof P256_N,
+       P256_G, sizeof P256_G
+};
diff --git a/src/ec/ec_secp384r1.c b/src/ec/ec_secp384r1.c
new file mode 100644 (file)
index 0000000..693d93e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const unsigned char P384_N[] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, 
+       0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 
+       0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73
+};
+
+static const unsigned char P384_G[] = {
+       0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05,
+       0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD,
+       0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B,
+       0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A,
+       0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29,
+       0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A,
+       0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C,
+       0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC,
+       0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14,
+       0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8,
+       0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81,
+       0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E,
+       0x5F
+};
+
+/* see inner.h */
+const br_ec_curve_def br_secp384r1 = {
+       BR_EC_secp384r1,
+       P384_N, sizeof P384_N,
+       P384_G, sizeof P384_G
+};
diff --git a/src/ec/ec_secp521r1.c b/src/ec/ec_secp521r1.c
new file mode 100644 (file)
index 0000000..161acd0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const unsigned char P521_N[] = {
+       0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F,
+       0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09,
+       0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C,
+       0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38,
+       0x64, 0x09
+};
+
+static const unsigned char P521_G[] = {
+       0x04, 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04,
+       0x04, 0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23,
+       0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05,
+       0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B,
+       0x4D, 0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF,
+       0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2,
+       0xFF, 0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85,
+       0x6A, 0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2,
+       0xE5, 0xBD, 0x66, 0x01, 0x18, 0x39, 0x29, 0x6A,
+       0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F,
+       0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44,
+       0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD,
+       0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE, 0x72,
+       0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9,
+       0x01, 0x3F, 0xAD, 0x07, 0x61, 0x35, 0x3C, 0x70,
+       0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94,
+       0x76, 0x9F, 0xD1, 0x66, 0x50
+};
+
+/* see inner.h */
+const br_ec_curve_def br_secp521r1 = {
+       BR_EC_secp521r1,
+       P521_N, sizeof P521_N,
+       P521_G, sizeof P521_G
+};
diff --git a/src/ec/ecdsa_atr.c b/src/ec/ecdsa_atr.c
new file mode 100644 (file)
index 0000000..91a1d0c
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ec.h */
+size_t
+br_ecdsa_asn1_to_raw(void *sig, size_t sig_len)
+{
+       /*
+        * Note: this code is a bit lenient in that it accepts a few
+        * deviations to DER with regards to minimality of encoding of
+        * lengths and integer values. These deviations are still
+        * unambiguous.
+        */
+
+       unsigned char *buf, *r, *s;
+       size_t zlen, rlen, slen, off;
+       unsigned char tmp[254];
+
+       buf = sig;
+       if (sig_len < 8) {
+               return 0;
+       }
+       if (buf[0] != 0x30) {
+               return 0;
+       }
+       zlen = buf[1];
+       if (zlen > 0x80) {
+               if (zlen != 0x81) {
+                       return 0;
+               }
+               zlen = buf[2];
+               if (zlen != sig_len - 3) {
+                       return 0;
+               }
+               off = 3;
+       } else {
+               if (zlen != sig_len - 2) {
+                       return 0;
+               }
+               off = 2;
+       }
+       if (buf[off ++] != 0x02) {
+               return 0;
+       }
+       rlen = buf[off ++];
+       if (rlen >= 0x80) {
+               return 0;
+       }
+       r = buf + off;
+       off += rlen;
+       if (off + 2 > sig_len) {
+               return 0;
+       }
+       if (buf[off ++] != 0x02) {
+               return 0;
+       }
+       slen = buf[off ++];
+       if (slen >= 0x80 || slen != sig_len - off) {
+               return 0;
+       }
+       s = buf + off;
+
+       while (rlen > 0 && *r == 0) {
+               rlen --;
+               r ++;
+       }
+       while (slen > 0 && *s == 0) {
+               slen --;
+               s ++;
+       }
+
+       zlen = rlen > slen ? rlen : slen;
+       sig_len = zlen << 1;
+       memset(tmp, 0, sig_len);
+       memcpy(tmp + zlen - rlen, r, rlen);
+       memcpy(tmp + sig_len - slen, s, slen);
+       memcpy(sig, tmp, sig_len);
+       return sig_len;
+}
diff --git a/src/ec/ecdsa_i31_bits.c b/src/ec/ecdsa_i31_bits.c
new file mode 100644 (file)
index 0000000..9a8d673
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_ecdsa_i31_bits2int(uint32_t *x,
+       const void *src, size_t len, uint32_t ebitlen)
+{
+       uint32_t bitlen, hbitlen;
+       int sc;
+
+       bitlen = ebitlen - (ebitlen >> 5);
+       hbitlen = (uint32_t)len << 3;
+       if (hbitlen > bitlen) {
+               len = (bitlen + 7) >> 3;
+               sc = (int)((hbitlen - bitlen) & 7);
+       } else {
+               sc = 0;
+       }
+       br_i31_zero(x, ebitlen);
+       br_i31_decode(x, src, len);
+       br_i31_rshift(x, sc);
+       x[0] = ebitlen;
+}
diff --git a/src/ec/ecdsa_i31_sign_asn1.c b/src/ec/ecdsa_i31_sign_asn1.c
new file mode 100644 (file)
index 0000000..cf0d351
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define ORDER_LEN   ((BR_MAX_EC_SIZE + 7) >> 3)
+
+/* see bearssl_ec.h */
+size_t
+br_ecdsa_i31_sign_asn1(const br_ec_impl *impl,
+       const br_hash_class *hf, const void *hash_value,
+       const br_ec_private_key *sk, void *sig)
+{
+       unsigned char rsig[(ORDER_LEN << 1) + 12];
+       size_t sig_len;
+
+       sig_len = br_ecdsa_i31_sign_raw(impl, hf, hash_value, sk, rsig);
+       if (sig_len == 0) {
+               return 0;
+       }
+       sig_len = br_ecdsa_raw_to_asn1(rsig, sig_len);
+       memcpy(sig, rsig, sig_len);
+       return sig_len;
+}
diff --git a/src/ec/ecdsa_i31_sign_raw.c b/src/ec/ecdsa_i31_sign_raw.c
new file mode 100644 (file)
index 0000000..3849545
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define I31_LEN     ((BR_MAX_EC_SIZE + 61) / 31)
+#define POINT_LEN   (1 + (((BR_MAX_EC_SIZE + 7) >> 3) << 1))
+#define ORDER_LEN   ((BR_MAX_EC_SIZE + 7) >> 3)
+
+/* see bearssl_ec.h */
+size_t
+br_ecdsa_i31_sign_raw(const br_ec_impl *impl,
+       const br_hash_class *hf, const void *hash_value,
+       const br_ec_private_key *sk, void *sig)
+{
+       /*
+        * IMPORTANT: this code is fit only for curves with a prime
+        * order. This is needed so that modular reduction of the X
+        * coordinate of a point can be done with a simple subtraction.
+        * We also rely on the last byte of the curve order to be distinct
+        * from 0 and 1.
+        */
+       const br_ec_curve_def *cd;
+       uint32_t n[I31_LEN], r[I31_LEN], s[I31_LEN], x[I31_LEN];
+       uint32_t m[I31_LEN], k[I31_LEN], t1[I31_LEN], t2[I31_LEN];
+       unsigned char tt[ORDER_LEN << 1];
+       unsigned char eU[POINT_LEN];
+       size_t hash_len, nlen, ulen;
+       uint32_t n0i, ctl;
+       br_hmac_drbg_context drbg;
+
+       /*
+        * Get the curve parameters (generator and order).
+        */
+       switch (sk->curve) {
+       case BR_EC_secp256r1:
+               cd = &br_secp256r1;
+               break;
+       case BR_EC_secp384r1:
+               cd = &br_secp384r1;
+               break;
+       case BR_EC_secp521r1:
+               cd = &br_secp521r1;
+               break;
+       default:
+               return 0;
+       }
+
+       /*
+        * Get modulus.
+        */
+       nlen = cd->order_len;
+       br_i31_decode(n, cd->order, nlen);
+       n0i = br_i31_ninv31(n[1]);
+
+       /*
+        * Get private key as an i31 integer. This also checks that the
+        * private key is well-defined (not zero, and less than the
+        * curve order).
+        */
+       if (!br_i31_decode_mod(x, sk->x, sk->xlen, n)) {
+               return 0;
+       }
+       if (br_i31_iszero(x)) {
+               return 0;
+       }
+
+       /*
+        * Get hash length.
+        */
+       hash_len = (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK;
+
+       /*
+        * Truncate and reduce the hash value modulo the curve order.
+        */
+       br_ecdsa_i31_bits2int(m, hash_value, hash_len, n[0]);
+       br_i31_sub(m, n, br_i31_sub(m, n, 0) ^ 1);
+
+       /*
+        * RFC 6979 generation of the "k" value.
+        *
+        * The process uses HMAC_DRBG (with the hash function used to
+        * process the message that is to be signed). The seed is the
+        * concatenation of the encodings of the private key and
+        * the hash value (after truncation and modular reduction).
+        */
+       br_i31_encode(tt, nlen, x);
+       br_i31_encode(tt + nlen, nlen, m);
+       br_hmac_drbg_init(&drbg, hf, tt, nlen << 1);
+       for (;;) {
+               br_hmac_drbg_generate(&drbg, tt, nlen);
+               br_ecdsa_i31_bits2int(k, tt, nlen, n[0]);
+               if (br_i31_iszero(k)) {
+                       continue;
+               }
+               if (br_i31_sub(k, n, 0)) {
+                       break;
+               }
+       }
+
+       /*
+        * Compute k*G and extract the X coordinate, then reduce it
+        * modulo the curve order. Since we support only curves with
+        * prime order, that reduction is only a matter of computing
+        * a subtraction.
+        */
+       ulen = cd->generator_len;
+       memcpy(eU, cd->generator, ulen);
+       br_i31_encode(tt, nlen, k);
+       if (!impl->mul(eU, ulen, tt, nlen, sk->curve)) {
+               /*
+                * Point multiplication may fail here only if the
+                * EC implementation does not support the curve, or the
+                * private key is incorrect (x is a multiple of the curve
+                * order).
+                */
+               return 0;
+       }
+       br_i31_zero(r, n[0]);
+       br_i31_decode(r, &eU[1], ulen >> 1);
+       r[0] = n[0];
+       br_i31_sub(r, n, br_i31_sub(r, n, 0) ^ 1);
+
+       /*
+        * Compute 1/k in double-Montgomery representation. We do so by
+        * first converting _from_ Montgomery representation (twice),
+        * then using a modular exponentiation.
+        */
+       br_i31_from_monty(k, n, n0i);
+       br_i31_from_monty(k, n, n0i);
+       memcpy(tt, cd->order, nlen);
+       tt[nlen - 1] -= 2;
+       br_i31_modpow(k, tt, nlen, n, n0i, t1, t2);
+
+       /*
+        * Compute s = (m+xr)/k (mod n).
+        * The k[] array contains R^2/k (double-Montgomery representation);
+        * we thus can use direct Montgomery multiplications and conversions
+        * from Montgomery, avoiding any call to br_i31_to_monty() (which
+        * is slower).
+        */
+       br_i31_from_monty(m, n, n0i);
+       br_i31_montymul(t1, x, r, n, n0i);
+       ctl = br_i31_add(t1, m, 1);
+       ctl |= br_i31_sub(t1, n, 0) ^ 1;
+       br_i31_sub(t1, n, ctl);
+       br_i31_montymul(s, t1, k, n, n0i);
+
+       /*
+        * Encode r and s in the signature.
+        */
+       br_i31_encode(sig, nlen, r);
+       br_i31_encode((unsigned char *)sig + nlen, nlen, s);
+       return nlen << 1;
+}
diff --git a/src/ec/ecdsa_i31_vrfy_asn1.c b/src/ec/ecdsa_i31_vrfy_asn1.c
new file mode 100644 (file)
index 0000000..e98b779
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define FIELD_LEN   ((BR_MAX_EC_SIZE + 7) >> 3)
+
+/* see bearssl_ec.h */
+uint32_t
+br_ecdsa_i31_vrfy_asn1(const br_ec_impl *impl,
+       const void *hash, size_t hash_len,
+       const br_ec_public_key *pk,
+       const void *sig, size_t sig_len)
+{
+       unsigned char rsig[(FIELD_LEN << 1) + 12];
+
+       if (sig_len > sizeof rsig) {
+               return 0;
+       }
+       memcpy(rsig, sig, sig_len);
+       sig_len = br_ecdsa_asn1_to_raw(rsig, sig_len);
+       return br_ecdsa_i31_vrfy_raw(impl, hash, hash_len, pk, rsig, sig_len);
+}
diff --git a/src/ec/ecdsa_i31_vrfy_raw.c b/src/ec/ecdsa_i31_vrfy_raw.c
new file mode 100644 (file)
index 0000000..54dcfc2
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define I31_LEN     ((BR_MAX_EC_SIZE + 61) / 31)
+#define POINT_LEN   (1 + (((BR_MAX_EC_SIZE + 7) >> 3) << 1))
+
+/* see bearssl_ec.h */
+uint32_t
+br_ecdsa_i31_vrfy_raw(const br_ec_impl *impl,
+       const void *hash, size_t hash_len,
+       const br_ec_public_key *pk,
+       const void *sig, size_t sig_len)
+{
+       /*
+        * IMPORTANT: this code is fit only for curves with a prime
+        * order. This is needed so that modular reduction of the X
+        * coordinate of a point can be done with a simple subtraction.
+        */
+       const br_ec_curve_def *cd;
+       uint32_t n[I31_LEN], r[I31_LEN], s[I31_LEN], t1[I31_LEN], t2[I31_LEN];
+       unsigned char tx[(BR_MAX_EC_SIZE + 7) >> 3];
+       unsigned char ty[(BR_MAX_EC_SIZE + 7) >> 3];
+       unsigned char eU[POINT_LEN];
+       size_t nlen, rlen, ulen;
+       uint32_t n0i, res;
+
+       /*
+        * Get the curve parameters (generator and order).
+        */
+       switch (pk->curve) {
+       case BR_EC_secp256r1:
+               cd = &br_secp256r1;
+               break;
+       case BR_EC_secp384r1:
+               cd = &br_secp384r1;
+               break;
+       case BR_EC_secp521r1:
+               cd = &br_secp521r1;
+               break;
+       default:
+               return 0;
+       }
+
+       /*
+        * Signature length must be even.
+        */
+       if (sig_len & 1) {
+               return 0;
+       }
+       rlen = sig_len >> 1;
+
+       /*
+        * Public key point must have the proper size for this curve.
+        */
+       if (pk->qlen != cd->generator_len) {
+               return 0;
+       }
+
+       /*
+        * Get modulus; then decode the r and s values. They must be
+        * lower than the modulus, and s must not be null.
+        */
+       nlen = cd->order_len;
+       br_i31_decode(n, cd->order, nlen);
+       n0i = br_i31_ninv31(n[1]);
+       if (!br_i31_decode_mod(r, sig, rlen, n)) {
+               return 0;
+       }
+       if (!br_i31_decode_mod(s, (const unsigned char *)sig + rlen, rlen, n)) {
+               return 0;
+       }
+       if (br_i31_iszero(s)) {
+               return 0;
+       }
+
+       /*
+        * Invert s. We do that with a modular exponentiation; we use
+        * the fact that for all the curves we support, the least
+        * significant byte is not 0 or 1, so we can subtract 2 without
+        * any carry to process.
+        * We also want 1/s in Montgomery representation, which can be
+        * done by converting _from_ Montgomery representation before
+        * the inversion (because (1/s)*R = 1/(s/R)).
+        */
+       br_i31_from_monty(s, n, n0i);
+       memcpy(tx, cd->order, nlen);
+       tx[nlen - 1] -= 2;
+       br_i31_modpow(s, tx, nlen, n, n0i, t1, t2);
+
+       /*
+        * Truncate the hash to the modulus length (in bits) and reduce
+        * it modulo the curve order. The modular reduction can be done
+        * with a subtraction since the truncation already reduced the
+        * value to the modulus bit length.
+        */
+       br_ecdsa_i31_bits2int(t1, hash, hash_len, n[0]);
+       br_i31_sub(t1, n, br_i31_sub(t1, n, 0) ^ 1);
+
+       /*
+        * Multiply the (truncated, reduced) hash value with 1/s, result in
+        * t2, encoded in ty.
+        */
+       br_i31_montymul(t2, t1, s, n, n0i);
+       br_i31_encode(ty, nlen, t2);
+
+       /*
+        * Multiply r with 1/s, result in t1, encoded in tx.
+        */
+       br_i31_montymul(t1, r, s, n, n0i);
+       br_i31_encode(tx, nlen, t1);
+
+       /*
+        * Compute the point x*Q + y*G.
+        */
+       ulen = cd->generator_len;
+       memcpy(eU, pk->q, ulen);
+       res = impl->muladd(eU, cd->generator, ulen,
+               tx, nlen, ty, nlen, cd->curve);
+
+       /*
+        * Get the X coordinate, reduce modulo the curve order, and
+        * compare with the 'r' value.
+        *
+        * The modular reduction can be done with subtractions because
+        * we work with curves of prime order, so the curve order is
+        * close to the field order (Hasse's theorem).
+        */
+       br_i31_zero(t1, n[0]);
+       br_i31_decode(t1, &eU[1], ulen >> 1);
+       t1[0] = n[0];
+       br_i31_sub(t1, n, br_i31_sub(t1, n, 0) ^ 1);
+       res &= ~br_i31_sub(t1, r, 1);
+       res &= br_i31_iszero(t1);
+       return res;
+}
diff --git a/src/ec/ecdsa_rta.c b/src/ec/ecdsa_rta.c
new file mode 100644 (file)
index 0000000..71f0d39
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static size_t
+asn1_int_length(const unsigned char *x, size_t xlen)
+{
+       while (xlen > 0 && *x == 0) {
+               x ++;
+               xlen --;
+       }
+       if (xlen == 0 || *x >= 0x80) {
+               xlen ++;
+       }
+       return xlen;
+}
+
+/* see bearssl_ec.h */
+size_t
+br_ecdsa_raw_to_asn1(void *sig, size_t sig_len)
+{
+       /*
+        * Internal buffer is large enough to accommodate a signature
+        * such that r and s fit on 125 bytes each (signed encoding),
+        * meaning a curve order of up to 1000 bits. This is the limit
+        * that ensures "simple" length encodings.
+        */
+       unsigned char *buf;
+       size_t hlen, rlen, slen, zlen, off;
+       unsigned char tmp[257];
+
+       buf = sig;
+       if ((sig_len & 1) != 0) {
+               return 0;
+       }
+       hlen = sig_len >> 1;
+       rlen = asn1_int_length(buf, hlen);
+       slen = asn1_int_length(buf + hlen, hlen);
+       if (rlen > 125 || slen > 125) {
+               return 0;
+       }
+       tmp[0] = 0x30;
+       zlen = rlen + slen + 4;
+       if (zlen >= 0x80) {
+               tmp[1] = 0x81;
+               tmp[2] = zlen;
+               off = 3;
+       } else {
+               tmp[1] = zlen;
+               off = 2;
+       }
+       tmp[off ++] = 0x02;
+       tmp[off ++] = rlen;
+       if (rlen > hlen) {
+               tmp[off] = 0x00;
+               memcpy(tmp + off + 1, buf, hlen);
+       } else {
+               memcpy(tmp + off, buf + hlen - rlen, rlen);
+       }
+       off += rlen;
+       tmp[off ++] = 0x02;
+       tmp[off ++] = slen;
+       if (slen > hlen) {
+               tmp[off] = 0x00;
+               memcpy(tmp + off + 1, buf + hlen, hlen);
+       } else {
+               memcpy(tmp + off, buf + sig_len - slen, slen);
+       }
+       off += slen;
+       memcpy(sig, tmp, off);
+       return off;
+}
diff --git a/src/hash/dig_oid.c b/src/hash/dig_oid.c
new file mode 100644 (file)
index 0000000..496f230
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const unsigned char md5_OID[] = {
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05
+};
+
+static const unsigned char sha1_OID[] = {
+       0x2B, 0x0E, 0x03, 0x02, 0x1A
+};
+
+static const unsigned char sha224_OID[] = {
+       0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
+};
+
+static const unsigned char sha256_OID[] = {
+       0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+static const unsigned char sha384_OID[] = {
+       0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+static const unsigned char sha512_OID[] = {
+       0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+/* see inner.h */
+const unsigned char *
+br_digest_OID(int digest_id, size_t *len)
+{
+       switch (digest_id) {
+       case br_md5_ID:
+               *len = sizeof md5_OID;
+               return md5_OID;
+       case br_sha1_ID:
+               *len = sizeof sha1_OID;
+               return sha1_OID;
+       case br_sha224_ID:
+               *len = sizeof sha224_OID;
+               return sha224_OID;
+       case br_sha256_ID:
+               *len = sizeof sha256_OID;
+               return sha256_OID;
+       case br_sha384_ID:
+               *len = sizeof sha384_OID;
+               return sha384_OID;
+       case br_sha512_ID:
+               *len = sizeof sha512_OID;
+               return sha512_OID;
+       default:
+               *len = 0;
+               return NULL;
+       }
+}
diff --git a/src/hash/dig_size.c b/src/hash/dig_size.c
new file mode 100644 (file)
index 0000000..4625d2c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+size_t
+br_digest_size_by_ID(int digest_id)
+{
+       switch (digest_id) {
+       case br_md5sha1_ID:
+               return br_md5_SIZE + br_sha1_SIZE;
+       case br_md5_ID:
+               return br_md5_SIZE;
+       case br_sha1_ID:
+               return br_sha1_SIZE;
+       case br_sha224_ID:
+               return br_sha224_SIZE;
+       case br_sha256_ID:
+               return br_sha256_SIZE;
+       case br_sha384_ID:
+               return br_sha384_SIZE;
+       case br_sha512_ID:
+               return br_sha512_SIZE;
+       default:
+               /* abort(); */
+               return 0;
+       }
+}
diff --git a/src/hash/ghash_ctmul.c b/src/hash/ghash_ctmul.c
new file mode 100644 (file)
index 0000000..f76f6e9
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * We compute "carryless multiplications" through normal integer
+ * multiplications, masking out enough bits to create "holes" in which
+ * carries may expand without altering our bits; we really use 8 data
+ * bits per 32-bit word, space every fourth bit. Accumulated carries
+ * may not exceed 8 in total, which fits in 4 bits.
+ *
+ * It would be possible to use a 3-bit spacing, allowing two operands,
+ * one with 7 non-zero data bits, the other one with 10 or 11 non-zero
+ * data bits; this asymmetric splitting makes the overall code more
+ * complex with thresholds and exceptions, and does not appear to be
+ * worth the effort.
+ */
+
+/*
+ * We cannot really autodetect whether multiplications are "slow" or
+ * not. A typical example is the ARM Cortex M0+, which exists in two
+ * versions: one with a 1-cycle multiplication opcode, the other with
+ * a 32-cycle multiplication opcodes. They both use exactly the same
+ * architecture and ABI, and cannot be distinguished from each other
+ * at compile-time.
+ *
+ * Since most modern CPU (even embedded CPU) still have fast
+ * multiplications, we use the "fast mul" code by default.
+ */
+
+#if BR_SLOW_MUL
+
+/*
+ * This implementation uses Karatsuba-like reduction to make fewer
+ * integer multiplications (9 instead of 16), at the expense of extra
+ * logical operations (XOR, shifts...). On modern x86 CPU that offer
+ * fast, pipelined multiplications, this code is about twice slower than
+ * the simpler code with 16 multiplications. This tendency may be
+ * reversed on low-end platforms with expensive multiplications.
+ */
+
+#define MUL32(h, l, x, y)   do { \
+               uint64_t mul32tmp = MUL(x, y); \
+               (h) = (uint32_t)(mul32tmp >> 32); \
+               (l) = (uint32_t)mul32tmp; \
+       } while (0)
+
+static inline void
+bmul(uint32_t *hi, uint32_t *lo, uint32_t x, uint32_t y)
+{
+       uint32_t x0, x1, x2, x3;
+       uint32_t y0, y1, y2, y3;
+       uint32_t a0, a1, a2, a3, a4, a5, a6, a7, a8;
+       uint32_t b0, b1, b2, b3, b4, b5, b6, b7, b8;
+
+       x0 = x & (uint32_t)0x11111111;
+       x1 = x & (uint32_t)0x22222222;
+       x2 = x & (uint32_t)0x44444444;
+       x3 = x & (uint32_t)0x88888888;
+       y0 = y & (uint32_t)0x11111111;
+       y1 = y & (uint32_t)0x22222222;
+       y2 = y & (uint32_t)0x44444444;
+       y3 = y & (uint32_t)0x88888888;
+
+       /*
+        * (x0+W*x1)*(y0+W*y1) -> a0:b0
+        * (x2+W*x3)*(y2+W*y3) -> a3:b3
+        * ((x0+x2)+W*(x1+x3))*((y0+y2)+W*(y1+y3)) -> a6:b6
+        */
+       a0 = x0;
+       b0 = y0;
+       a1 = x1 >> 1;
+       b1 = y1 >> 1;
+       a2 = a0 ^ a1;
+       b2 = b0 ^ b1;
+       a3 = x2 >> 2;
+       b3 = y2 >> 2;
+       a4 = x3 >> 3;
+       b4 = y3 >> 3;
+       a5 = a3 ^ a4;
+       b5 = b3 ^ b4;
+       a6 = a0 ^ a3;
+       b6 = b0 ^ b3;
+       a7 = a1 ^ a4;
+       b7 = b1 ^ b4;
+       a8 = a6 ^ a7;
+       b8 = b6 ^ b7;
+
+       MUL32(b0, a0, b0, a0);
+       MUL32(b1, a1, b1, a1);
+       MUL32(b2, a2, b2, a2);
+       MUL32(b3, a3, b3, a3);
+       MUL32(b4, a4, b4, a4);
+       MUL32(b5, a5, b5, a5);
+       MUL32(b6, a6, b6, a6);
+       MUL32(b7, a7, b7, a7);
+       MUL32(b8, a8, b8, a8);
+
+       a0 &= (uint32_t)0x11111111;
+       a1 &= (uint32_t)0x11111111;
+       a2 &= (uint32_t)0x11111111;
+       a3 &= (uint32_t)0x11111111;
+       a4 &= (uint32_t)0x11111111;
+       a5 &= (uint32_t)0x11111111;
+       a6 &= (uint32_t)0x11111111;
+       a7 &= (uint32_t)0x11111111;
+       a8 &= (uint32_t)0x11111111;
+       b0 &= (uint32_t)0x11111111;
+       b1 &= (uint32_t)0x11111111;
+       b2 &= (uint32_t)0x11111111;
+       b3 &= (uint32_t)0x11111111;
+       b4 &= (uint32_t)0x11111111;
+       b5 &= (uint32_t)0x11111111;
+       b6 &= (uint32_t)0x11111111;
+       b7 &= (uint32_t)0x11111111;
+       b8 &= (uint32_t)0x11111111;
+
+       a2 ^= a0 ^ a1;
+       b2 ^= b0 ^ b1;
+       a0 ^= (a2 << 1) ^ (a1 << 2);
+       b0 ^= (b2 << 1) ^ (b1 << 2);
+       a5 ^= a3 ^ a4;
+       b5 ^= b3 ^ b4;
+       a3 ^= (a5 << 1) ^ (a4 << 2);
+       b3 ^= (b5 << 1) ^ (b4 << 2);
+       a8 ^= a6 ^ a7;
+       b8 ^= b6 ^ b7;
+       a6 ^= (a8 << 1) ^ (a7 << 2);
+       b6 ^= (b8 << 1) ^ (b7 << 2);
+       a6 ^= a0 ^ a3;
+       b6 ^= b0 ^ b3;
+       *lo = a0 ^ (a6 << 2) ^ (a3 << 4);
+       *hi = b0 ^ (b6 << 2) ^ (b3 << 4) ^ (a6 >> 30) ^ (a3 >> 28);
+}
+
+#else
+
+/*
+ * Simple multiplication in GF(2)[X], using 16 integer multiplications.
+ */
+
+static inline void
+bmul(uint32_t *hi, uint32_t *lo, uint32_t x, uint32_t y)
+{
+       uint32_t x0, x1, x2, x3;
+       uint32_t y0, y1, y2, y3;
+       uint64_t z0, z1, z2, z3;
+       uint64_t z;
+
+       x0 = x & (uint32_t)0x11111111;
+       x1 = x & (uint32_t)0x22222222;
+       x2 = x & (uint32_t)0x44444444;
+       x3 = x & (uint32_t)0x88888888;
+       y0 = y & (uint32_t)0x11111111;
+       y1 = y & (uint32_t)0x22222222;
+       y2 = y & (uint32_t)0x44444444;
+       y3 = y & (uint32_t)0x88888888;
+       z0 = MUL(x0, y0) ^ MUL(x1, y3) ^ MUL(x2, y2) ^ MUL(x3, y1);
+       z1 = MUL(x0, y1) ^ MUL(x1, y0) ^ MUL(x2, y3) ^ MUL(x3, y2);
+       z2 = MUL(x0, y2) ^ MUL(x1, y1) ^ MUL(x2, y0) ^ MUL(x3, y3);
+       z3 = MUL(x0, y3) ^ MUL(x1, y2) ^ MUL(x2, y1) ^ MUL(x3, y0);
+       z0 &= (uint64_t)0x1111111111111111;
+       z1 &= (uint64_t)0x2222222222222222;
+       z2 &= (uint64_t)0x4444444444444444;
+       z3 &= (uint64_t)0x8888888888888888;
+       z = z0 | z1 | z2 | z3;
+       *lo = (uint32_t)z;
+       *hi = (uint32_t)(z >> 32);
+}
+
+#endif
+
+/* see bearssl_hash.h */
+void
+br_ghash_ctmul(void *y, const void *h, const void *data, size_t len)
+{
+       const unsigned char *buf, *hb;
+       unsigned char *yb;
+       uint32_t yw[4];
+       uint32_t hw[4];
+
+       buf = data;
+       yb = y;
+       hb = h;
+       yw[3] = br_dec32be(yb);
+       yw[2] = br_dec32be(yb + 4);
+       yw[1] = br_dec32be(yb + 8);
+       yw[0] = br_dec32be(yb + 12);
+       hw[3] = br_dec32be(hb);
+       hw[2] = br_dec32be(hb + 4);
+       hw[1] = br_dec32be(hb + 8);
+       hw[0] = br_dec32be(hb + 12);
+       while (len > 0) {
+               const unsigned char *src;
+               unsigned char tmp[16];
+               int i;
+               uint32_t a[9], b[9], zw[8];
+               uint32_t c0, c1, c2, c3, d0, d1, d2, d3, e0, e1, e2, e3;
+
+               if (len >= 16) {
+                       src = buf;
+                       buf += 16;
+                       len -= 16;
+               } else {
+                       memcpy(tmp, buf, len);
+                       memset(tmp + len, 0, (sizeof tmp) - len);
+                       src = tmp;
+                       len = 0;
+               }
+               yw[3] ^= br_dec32be(src);
+               yw[2] ^= br_dec32be(src + 4);
+               yw[1] ^= br_dec32be(src + 8);
+               yw[0] ^= br_dec32be(src + 12);
+
+               /*
+                * y[0,1]*h[0,1] -> 0..2
+                * y[2,3]*h[2,3] -> 3..5
+                * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6..8
+                */
+               a[0] = yw[0];
+               b[0] = hw[0];
+               a[1] = yw[1];
+               b[1] = hw[1];
+               a[2] = a[0] ^ a[1];
+               b[2] = b[0] ^ b[1];
+
+               a[3] = yw[2];
+               b[3] = hw[2];
+               a[4] = yw[3];
+               b[4] = hw[3];
+               a[5] = a[3] ^ a[4];
+               b[5] = b[3] ^ b[4];
+
+               a[6] = a[0] ^ a[3];
+               b[6] = b[0] ^ b[3];
+               a[7] = a[1] ^ a[4];
+               b[7] = b[1] ^ b[4];
+               a[8] = a[6] ^ a[7];
+               b[8] = b[6] ^ b[7];
+
+               for (i = 0; i < 9; i ++) {
+                       bmul(&b[i], &a[i], b[i], a[i]);
+               }
+
+               c0 = a[0];
+               c1 = b[0] ^ a[2] ^ a[0] ^ a[1];
+               c2 = a[1] ^ b[2] ^ b[0] ^ b[1];
+               c3 = b[1];
+               d0 = a[3];
+               d1 = b[3] ^ a[5] ^ a[3] ^ a[4];
+               d2 = a[4] ^ b[5] ^ b[3] ^ b[4];
+               d3 = b[4];
+               e0 = a[6];
+               e1 = b[6] ^ a[8] ^ a[6] ^ a[7];
+               e2 = a[7] ^ b[8] ^ b[6] ^ b[7];
+               e3 = b[7];
+
+               e0 ^= c0 ^ d0;
+               e1 ^= c1 ^ d1;
+               e2 ^= c2 ^ d2;
+               e3 ^= c3 ^ d3;
+               c2 ^= e0;
+               c3 ^= e1;
+               d0 ^= e2;
+               d1 ^= e3;
+
+               zw[0] = c0 << 1;
+               zw[1] = (c1 << 1) | (c0 >> 31);
+               zw[2] = (c2 << 1) | (c1 >> 31);
+               zw[3] = (c3 << 1) | (c2 >> 31);
+               zw[4] = (d0 << 1) | (c3 >> 31);
+               zw[5] = (d1 << 1) | (d0 >> 31);
+               zw[6] = (d2 << 1) | (d1 >> 31);
+               zw[7] = (d3 << 1) | (d2 >> 31);
+
+               for (i = 0; i < 4; i ++) {
+                       uint32_t lw;
+
+                       lw = zw[i];
+                       zw[i + 4] ^= lw ^ (lw >> 1) ^ (lw >> 2) ^ (lw >> 7);
+                       zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25);
+               }
+               memcpy(yw, zw + 4, sizeof yw);
+       }
+       br_enc32be(yb, yw[3]);
+       br_enc32be(yb + 4, yw[2]);
+       br_enc32be(yb + 8, yw[1]);
+       br_enc32be(yb + 12, yw[0]);
+}
diff --git a/src/hash/ghash_ctmul32.c b/src/hash/ghash_ctmul32.c
new file mode 100644 (file)
index 0000000..d3380d4
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * This implementation uses 32-bit multiplications, and only the low
+ * 32 bits for each multiplication result. This is meant primarily for
+ * the ARM Cortex M0 and M0+, whose multiplication opcode does not yield
+ * the upper 32 bits; but it might also be useful on architectures where
+ * access to the upper 32 bits requires use of specific registers that
+ * create contention (e.g. on i386, "mul" necessarily outputs the result
+ * in edx:eax, while "imul" can use any registers but is limited to the
+ * low 32 bits).
+ *
+ * The implementation trick that is used here is bit-reversing (bit 0
+ * is swapped with bit 31, bit 1 with bit 30, and so on). In GF(2)[X],
+ * for all values x and y, we have:
+ *    rev32(x) * rev32(y) = rev64(x * y)
+ * In other words, if we bit-reverse (over 32 bits) the operands, then we
+ * bit-reverse (over 64 bits) the result.
+ */
+
+/*
+ * Multiplication in GF(2)[X], truncated to its low 32 bits.
+ */
+static inline uint32_t
+bmul32(uint32_t x, uint32_t y)
+{
+       uint32_t x0, x1, x2, x3;
+       uint32_t y0, y1, y2, y3;
+       uint32_t z0, z1, z2, z3;
+
+       x0 = x & (uint32_t)0x11111111;
+       x1 = x & (uint32_t)0x22222222;
+       x2 = x & (uint32_t)0x44444444;
+       x3 = x & (uint32_t)0x88888888;
+       y0 = y & (uint32_t)0x11111111;
+       y1 = y & (uint32_t)0x22222222;
+       y2 = y & (uint32_t)0x44444444;
+       y3 = y & (uint32_t)0x88888888;
+       z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1);
+       z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2);
+       z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3);
+       z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0);
+       z0 &= (uint32_t)0x11111111;
+       z1 &= (uint32_t)0x22222222;
+       z2 &= (uint32_t)0x44444444;
+       z3 &= (uint32_t)0x88888888;
+       return z0 | z1 | z2 | z3;
+}
+
+/*
+ * Bit-reverse a 32-bit word.
+ */
+static uint32_t
+rev32(uint32_t x)
+{
+#define RMS(m, s)   do { \
+               x = ((x & (uint32_t)(m)) << (s)) \
+                       | ((x >> (s)) & (uint32_t)(m)); \
+       } while (0)
+
+       RMS(0x55555555, 1);
+       RMS(0x33333333, 2);
+       RMS(0x0F0F0F0F, 4);
+       RMS(0x00FF00FF, 8);
+       return (x << 16) | (x >> 16);
+
+#undef RMS
+}
+
+/* see bearssl_hash.h */
+void
+br_ghash_ctmul32(void *y, const void *h, const void *data, size_t len)
+{
+       const unsigned char *buf, *hb;
+       unsigned char *yb;
+       uint32_t yw[4];
+       uint32_t hw[4], hwr[4];
+
+       buf = data;
+       yb = y;
+       hb = h;
+       yw[3] = br_dec32be(yb);
+       yw[2] = br_dec32be(yb + 4);
+       yw[1] = br_dec32be(yb + 8);
+       yw[0] = br_dec32be(yb + 12);
+       hw[3] = br_dec32be(hb);
+       hw[2] = br_dec32be(hb + 4);
+       hw[1] = br_dec32be(hb + 8);
+       hw[0] = br_dec32be(hb + 12);
+       hwr[3] = rev32(hw[3]);
+       hwr[2] = rev32(hw[2]);
+       hwr[1] = rev32(hw[1]);
+       hwr[0] = rev32(hw[0]);
+       while (len > 0) {
+               const unsigned char *src;
+               unsigned char tmp[16];
+               int i;
+               uint32_t a[18], b[18], c[18];
+               uint32_t d0, d1, d2, d3, d4, d5, d6, d7;
+               uint32_t zw[8];
+
+               if (len >= 16) {
+                       src = buf;
+                       buf += 16;
+                       len -= 16;
+               } else {
+                       memcpy(tmp, buf, len);
+                       memset(tmp + len, 0, (sizeof tmp) - len);
+                       src = tmp;
+                       len = 0;
+               }
+               yw[3] ^= br_dec32be(src);
+               yw[2] ^= br_dec32be(src + 4);
+               yw[1] ^= br_dec32be(src + 8);
+               yw[0] ^= br_dec32be(src + 12);
+
+               /*
+                * We are using Karatsuba: the 128x128 multiplication is
+                * reduced to three 64x64 multiplications, hence nine
+                * 32x32 multiplications. With the bit-reversal trick,
+                * we have to perform 18 32x32 multiplications.
+                */
+
+               /*
+                * y[0,1]*h[0,1] -> 0,1,4
+                * y[2,3]*h[2,3] -> 2,3,5
+                * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6,7,8
+                */
+
+               a[0] = yw[0];
+               a[1] = yw[1];
+               a[2] = yw[2];
+               a[3] = yw[3];
+               a[4] = a[0] ^ a[1];
+               a[5] = a[2] ^ a[3];
+               a[6] = a[0] ^ a[2];
+               a[7] = a[1] ^ a[3];
+               a[8] = a[6] ^ a[7];
+
+               a[ 9] = rev32(yw[0]);
+               a[10] = rev32(yw[1]);
+               a[11] = rev32(yw[2]);
+               a[12] = rev32(yw[3]);
+               a[13] = a[ 9] ^ a[10];
+               a[14] = a[11] ^ a[12];
+               a[15] = a[ 9] ^ a[11];
+               a[16] = a[10] ^ a[12];
+               a[17] = a[15] ^ a[16];
+
+               b[0] = hw[0];
+               b[1] = hw[1];
+               b[2] = hw[2];
+               b[3] = hw[3];
+               b[4] = b[0] ^ b[1];
+               b[5] = b[2] ^ b[3];
+               b[6] = b[0] ^ b[2];
+               b[7] = b[1] ^ b[3];
+               b[8] = b[6] ^ b[7];
+
+               b[ 9] = hwr[0];
+               b[10] = hwr[1];
+               b[11] = hwr[2];
+               b[12] = hwr[3];
+               b[13] = b[ 9] ^ b[10];
+               b[14] = b[11] ^ b[12];
+               b[15] = b[ 9] ^ b[11];
+               b[16] = b[10] ^ b[12];
+               b[17] = b[15] ^ b[16];
+
+               for (i = 0; i < 18; i ++) {
+                       c[i] = bmul32(a[i], b[i]);
+               }
+
+               c[4] ^= c[0] ^ c[1];
+               c[5] ^= c[2] ^ c[3];
+               c[8] ^= c[6] ^ c[7];
+
+               c[13] ^= c[ 9] ^ c[10];
+               c[14] ^= c[11] ^ c[12];
+               c[17] ^= c[15] ^ c[16];
+
+               /*
+                * y[0,1]*h[0,1] -> 0,9^4,1^13,10
+                * y[2,3]*h[2,3] -> 2,11^5,3^14,12
+                * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6,15^8,7^17,16
+                */
+               d0 = c[0];
+               d1 = c[4] ^ (rev32(c[9]) >> 1);
+               d2 = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >> 1);
+               d3 = c[4] ^ c[5] ^ c[8]
+                       ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >> 1);
+               d4 = c[2] ^ c[1] ^ c[3] ^ c[7]
+                       ^ (rev32(c[13] ^ c[14] ^ c[17]) >> 1);
+               d5 = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >> 1);
+               d6 = c[3] ^ (rev32(c[14]) >> 1);
+               d7 = rev32(c[12]) >> 1;
+
+               zw[0] = d0 << 1;
+               zw[1] = (d1 << 1) | (d0 >> 31);
+               zw[2] = (d2 << 1) | (d1 >> 31);
+               zw[3] = (d3 << 1) | (d2 >> 31);
+               zw[4] = (d4 << 1) | (d3 >> 31);
+               zw[5] = (d5 << 1) | (d4 >> 31);
+               zw[6] = (d6 << 1) | (d5 >> 31);
+               zw[7] = (d7 << 1) | (d6 >> 31);
+
+               for (i = 0; i < 4; i ++) {
+                       uint32_t lw;
+
+                       lw = zw[i];
+                       zw[i + 4] ^= lw ^ (lw >> 1) ^ (lw >> 2) ^ (lw >> 7);
+                       zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25);
+               }
+               memcpy(yw, zw + 4, sizeof yw);
+       }
+       br_enc32be(yb, yw[3]);
+       br_enc32be(yb + 4, yw[2]);
+       br_enc32be(yb + 8, yw[1]);
+       br_enc32be(yb + 12, yw[0]);
+}
diff --git a/src/hash/ghash_ctmul64.c b/src/hash/ghash_ctmul64.c
new file mode 100644 (file)
index 0000000..7819d7f
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * This is the 64-bit variant of ghash_ctmul32(), with 64-bit operands
+ * and bit reversal of 64-bit words.
+ */
+
+static inline uint64_t
+bmul64(uint64_t x, uint64_t y)
+{
+       uint64_t x0, x1, x2, x3;
+       uint64_t y0, y1, y2, y3;
+       uint64_t z0, z1, z2, z3;
+
+       x0 = x & (uint64_t)0x1111111111111111;
+       x1 = x & (uint64_t)0x2222222222222222;
+       x2 = x & (uint64_t)0x4444444444444444;
+       x3 = x & (uint64_t)0x8888888888888888;
+       y0 = y & (uint64_t)0x1111111111111111;
+       y1 = y & (uint64_t)0x2222222222222222;
+       y2 = y & (uint64_t)0x4444444444444444;
+       y3 = y & (uint64_t)0x8888888888888888;
+       z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1);
+       z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2);
+       z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3);
+       z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0);
+       z0 &= (uint64_t)0x1111111111111111;
+       z1 &= (uint64_t)0x2222222222222222;
+       z2 &= (uint64_t)0x4444444444444444;
+       z3 &= (uint64_t)0x8888888888888888;
+       return z0 | z1 | z2 | z3;
+}
+
+static uint64_t
+rev64(uint64_t x)
+{
+#define RMS(m, s)   do { \
+               x = ((x & (uint64_t)(m)) << (s)) \
+                       | ((x >> (s)) & (uint64_t)(m)); \
+       } while (0)
+
+       RMS(0x5555555555555555,  1);
+       RMS(0x3333333333333333,  2);
+       RMS(0x0F0F0F0F0F0F0F0F,  4);
+       RMS(0x00FF00FF00FF00FF,  8);
+       RMS(0x0000FFFF0000FFFF, 16);
+       return (x << 32) | (x >> 32);
+
+#undef RMS
+}
+
+/* see bearssl_ghash.h */
+void
+br_ghash_ctmul64(void *y, const void *h, const void *data, size_t len)
+{
+       const unsigned char *buf, *hb;
+       unsigned char *yb;
+       uint64_t y0, y1;
+       uint64_t h0, h1, h2, h0r, h1r, h2r;
+
+       buf = data;
+       yb = y;
+       hb = h;
+       y1 = br_dec64be(yb);
+       y0 = br_dec64be(yb + 8);
+       h1 = br_dec64be(hb);
+       h0 = br_dec64be(hb + 8);
+       h0r = rev64(h0);
+       h1r = rev64(h1);
+       h2 = h0 ^ h1;
+       h2r = h0r ^ h1r;
+       while (len > 0) {
+               const unsigned char *src;
+               unsigned char tmp[16];
+               uint64_t y0r, y1r, y2, y2r;
+               uint64_t z0, z1, z2, z0h, z1h, z2h;
+               uint64_t v0, v1, v2, v3;
+
+               if (len >= 16) {
+                       src = buf;
+                       buf += 16;
+                       len -= 16;
+               } else {
+                       memcpy(tmp, buf, len);
+                       memset(tmp + len, 0, (sizeof tmp) - len);
+                       src = tmp;
+                       len = 0;
+               }
+               y1 ^= br_dec64be(src);
+               y0 ^= br_dec64be(src + 8);
+
+               y0r = rev64(y0);
+               y1r = rev64(y1);
+               y2 = y0 ^ y1;
+               y2r = y0r ^ y1r;
+
+               z0 = bmul64(y0, h0);
+               z1 = bmul64(y1, h1);
+               z2 = bmul64(y2, h2);
+               z0h = bmul64(y0r, h0r);
+               z1h = bmul64(y1r, h1r);
+               z2h = bmul64(y2r, h2r);
+               z2 ^= z0 ^ z1;
+               z2h ^= z0h ^ z1h;
+               z0h = rev64(z0h) >> 1;
+               z1h = rev64(z1h) >> 1;
+               z2h = rev64(z2h) >> 1;
+
+               v0 = z0;
+               v1 = z0h ^ z2;
+               v2 = z1 ^ z2h;
+               v3 = z1h;
+
+               v3 = (v3 << 1) | (v2 >> 63);
+               v2 = (v2 << 1) | (v1 >> 63);
+               v1 = (v1 << 1) | (v0 >> 63);
+               v0 = (v0 << 1);
+
+               v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
+               v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
+               v3 ^= v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7);
+               v2 ^= (v1 << 63) ^ (v1 << 62) ^ (v1 << 57);
+
+               y0 = v2;
+               y1 = v3;
+       }
+
+       br_enc64be(yb, y1);
+       br_enc64be(yb + 8, y0);
+}
diff --git a/src/hash/md5.c b/src/hash/md5.c
new file mode 100644 (file)
index 0000000..0df7abe
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define F(B, C, D)     ((((C) ^ (D)) & (B)) ^ (D))
+#define G(B, C, D)     ((((C) ^ (B)) & (D)) ^ (C))
+#define H(B, C, D)     ((B) ^ (C) ^ (D))
+#define I(B, C, D)     ((C) ^ ((B) | ~(D)))
+
+#define ROTL(x, n)    (((x) << (n)) | ((x) >> (32 - (n))))
+
+/* see inner.h */
+const uint32_t br_md5_IV[4] = {
+       0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476
+};
+
+static const uint32_t K[64] = {
+       0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
+       0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
+       0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
+       0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
+
+       0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
+       0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
+       0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
+       0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
+
+       0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
+       0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
+       0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
+       0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
+
+       0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
+       0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
+       0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
+       0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391
+};
+
+static const unsigned char MP[48] = {
+       1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
+       5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
+       0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9
+};
+
+/* see inner.h */
+void
+br_md5_round(const unsigned char *buf, uint32_t *val)
+{
+       uint32_t m[16];
+       uint32_t a, b, c, d;
+       int i;
+
+       a = val[0];
+       b = val[1];
+       c = val[2];
+       d = val[3];
+       /* obsolete
+       for (i = 0; i < 16; i ++) {
+               m[i] = br_dec32le(buf + (i << 2));
+       }
+       */
+       br_range_dec32le(m, 16, buf);
+
+       for (i = 0; i < 16; i += 4) {
+               a = b + ROTL(a + F(b, c, d) + m[i + 0] + K[i + 0],  7);
+               d = a + ROTL(d + F(a, b, c) + m[i + 1] + K[i + 1], 12);
+               c = d + ROTL(c + F(d, a, b) + m[i + 2] + K[i + 2], 17);
+               b = c + ROTL(b + F(c, d, a) + m[i + 3] + K[i + 3], 22);
+       }
+       for (i = 16; i < 32; i += 4) {
+               a = b + ROTL(a + G(b, c, d) + m[MP[i - 16]] + K[i + 0],  5);
+               d = a + ROTL(d + G(a, b, c) + m[MP[i - 15]] + K[i + 1],  9);
+               c = d + ROTL(c + G(d, a, b) + m[MP[i - 14]] + K[i + 2], 14);
+               b = c + ROTL(b + G(c, d, a) + m[MP[i - 13]] + K[i + 3], 20);
+       }
+       for (i = 32; i < 48; i += 4) {
+               a = b + ROTL(a + H(b, c, d) + m[MP[i - 16]] + K[i + 0],  4);
+               d = a + ROTL(d + H(a, b, c) + m[MP[i - 15]] + K[i + 1], 11);
+               c = d + ROTL(c + H(d, a, b) + m[MP[i - 14]] + K[i + 2], 16);
+               b = c + ROTL(b + H(c, d, a) + m[MP[i - 13]] + K[i + 3], 23);
+       }
+       for (i = 48; i < 64; i += 4) {
+               a = b + ROTL(a + I(b, c, d) + m[MP[i - 16]] + K[i + 0],  6);
+               d = a + ROTL(d + I(a, b, c) + m[MP[i - 15]] + K[i + 1], 10);
+               c = d + ROTL(c + I(d, a, b) + m[MP[i - 14]] + K[i + 2], 15);
+               b = c + ROTL(b + I(c, d, a) + m[MP[i - 13]] + K[i + 3], 21);
+       }
+
+       val[0] += a;
+       val[1] += b;
+       val[2] += c;
+       val[3] += d;
+}
+
+/* see bearssl.h */
+void
+br_md5_init(br_md5_context *cc)
+{
+       cc->vtable = &br_md5_vtable;
+       memcpy(cc->val, br_md5_IV, sizeof cc->val);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_md5_update(br_md5_context *cc, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t ptr;
+
+       buf = data;
+       ptr = (size_t)cc->count & 63;
+       while (len > 0) {
+               size_t clen;
+
+               clen = 64 - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(cc->buf + ptr, buf, clen);
+               ptr += clen;
+               buf += clen;
+               len -= clen;
+               cc->count += (uint64_t)clen;
+               if (ptr == 64) {
+                       br_md5_round(cc->buf, cc->val);
+                       ptr = 0;
+               }
+       }
+}
+
+/* see bearssl.h */
+void
+br_md5_out(const br_md5_context *cc, void *dst)
+{
+       unsigned char buf[64];
+       uint32_t val[4];
+       size_t ptr;
+
+       ptr = (size_t)cc->count & 63;
+       memcpy(buf, cc->buf, ptr);
+       memcpy(val, cc->val, sizeof val);
+       buf[ptr ++] = 0x80;
+       if (ptr > 56) {
+               memset(buf + ptr, 0, 64 - ptr);
+               br_md5_round(buf, val);
+               memset(buf, 0, 56);
+       } else {
+               memset(buf + ptr, 0, 56 - ptr);
+       }
+       br_enc64le(buf + 56, cc->count << 3);
+       br_md5_round(buf, val);
+       br_range_enc32le(dst, val, 4);
+}
+
+/* see bearssl.h */
+uint64_t
+br_md5_state(const br_md5_context *cc, void *dst)
+{
+       br_range_enc32le(dst, cc->val, 4);
+       return cc->count;
+}
+
+/* see bearssl.h */
+void
+br_md5_set_state(br_md5_context *cc, const void *stb, uint64_t count)
+{
+       br_range_dec32le(cc->val, 4, stb);
+       cc->count = count;
+}
+
+/* see bearssl.h */
+const br_hash_class br_md5_vtable = {
+       sizeof(br_md5_context),
+       BR_HASHDESC_ID(br_md5_ID)
+               | BR_HASHDESC_OUT(16)
+               | BR_HASHDESC_STATE(16)
+               | BR_HASHDESC_LBLEN(6)
+               | BR_HASHDESC_MD_PADDING,
+       (void (*)(const br_hash_class **))&br_md5_init,
+       (void (*)(const br_hash_class **, const void *, size_t))&br_md5_update,
+       (void (*)(const br_hash_class *const *, void *))&br_md5_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))&br_md5_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_md5_set_state
+};
diff --git a/src/hash/md5sha1.c b/src/hash/md5sha1.c
new file mode 100644 (file)
index 0000000..f701aee
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl.h */
+void
+br_md5sha1_init(br_md5sha1_context *cc)
+{
+       cc->vtable = &br_md5sha1_vtable;
+       memcpy(cc->val_md5, br_md5_IV, sizeof cc->val_md5);
+       memcpy(cc->val_sha1, br_sha1_IV, sizeof cc->val_sha1);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_md5sha1_update(br_md5sha1_context *cc, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t ptr;
+
+       buf = data;
+       ptr = (size_t)cc->count & 63;
+       while (len > 0) {
+               size_t clen;
+
+               clen = 64 - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(cc->buf + ptr, buf, clen);
+               ptr += clen;
+               buf += clen;
+               len -= clen;
+               cc->count += (uint64_t)clen;
+               if (ptr == 64) {
+                       br_md5_round(cc->buf, cc->val_md5);
+                       br_sha1_round(cc->buf, cc->val_sha1);
+                       ptr = 0;
+               }
+       }
+}
+
+/* see bearssl.h */
+void
+br_md5sha1_out(const br_md5sha1_context *cc, void *dst)
+{
+       unsigned char buf[64];
+       uint32_t val_md5[4];
+       uint32_t val_sha1[5];
+       size_t ptr;
+       unsigned char *out;
+       uint64_t count;
+
+       count = cc->count;
+       ptr = (size_t)count & 63;
+       memcpy(buf, cc->buf, ptr);
+       memcpy(val_md5, cc->val_md5, sizeof val_md5);
+       memcpy(val_sha1, cc->val_sha1, sizeof val_sha1);
+       buf[ptr ++] = 0x80;
+       if (ptr > 56) {
+               memset(buf + ptr, 0, 64 - ptr);
+               br_md5_round(buf, val_md5);
+               br_sha1_round(buf, val_sha1);
+               memset(buf, 0, 56);
+       } else {
+               memset(buf + ptr, 0, 56 - ptr);
+       }
+       count <<= 3;
+       br_enc64le(buf + 56, count);
+       br_md5_round(buf, val_md5);
+       br_enc64be(buf + 56, count);
+       br_sha1_round(buf, val_sha1);
+       out = dst;
+       br_range_enc32le(out, val_md5, 4);
+       br_range_enc32be(out + 16, val_sha1, 5);
+}
+
+/* see bearssl.h */
+uint64_t
+br_md5sha1_state(const br_md5sha1_context *cc, void *dst)
+{
+       unsigned char *out;
+
+       out = dst;
+       br_range_enc32le(out, cc->val_md5, 4);
+       br_range_enc32be(out + 16, cc->val_sha1, 5);
+       return cc->count;
+}
+
+/* see bearssl.h */
+void
+br_md5sha1_set_state(br_md5sha1_context *cc, const void *stb, uint64_t count)
+{
+       const unsigned char *buf;
+
+       buf = stb;
+       br_range_dec32le(cc->val_md5, 4, buf);
+       br_range_dec32be(cc->val_sha1, 5, buf + 16);
+       cc->count = count;
+}
+
+/* see bearssl.h */
+const br_hash_class br_md5sha1_vtable = {
+       sizeof(br_md5sha1_context),
+       BR_HASHDESC_ID(br_md5sha1_ID)
+               | BR_HASHDESC_OUT(36)
+               | BR_HASHDESC_STATE(36)
+               | BR_HASHDESC_LBLEN(6),
+       (void (*)(const br_hash_class **))&br_md5sha1_init,
+       (void (*)(const br_hash_class **, const void *, size_t))
+               &br_md5sha1_update,
+       (void (*)(const br_hash_class *const *, void *))
+               &br_md5sha1_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))
+               &br_md5sha1_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_md5sha1_set_state
+};
diff --git a/src/hash/multihash.c b/src/hash/multihash.c
new file mode 100644 (file)
index 0000000..b6df2e0
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * An aggregate context that is large enough for all supported hash
+ * functions.
+ */
+typedef union {
+       const br_hash_class *vtable;
+       br_md5_context md5;
+       br_sha1_context sha1;
+       br_sha224_context sha224;
+       br_sha256_context sha256;
+       br_sha384_context sha384;
+       br_sha512_context sha512;
+} gen_hash_context;
+
+/*
+ * Get the offset to the state for a specific hash function within the
+ * context structure. This shall be called only for the supported hash
+ * functions,
+ */
+static size_t
+get_state_offset(int id)
+{
+       if (id >= 5) {
+               /*
+                * SHA-384 has id 5, and SHA-512 has id 6. Both use
+                * eight 64-bit words for their state.
+                */
+               return offsetof(br_multihash_context, val_64)
+                       + ((size_t)(id - 5) * (8 * sizeof(uint64_t)));
+       } else {
+               /*
+                * MD5 has id 1, SHA-1 has id 2, SHA-224 has id 3 and
+                * SHA-256 has id 4. They use 32-bit words for their
+                * states (4 words for MD5, 5 for SHA-1, 8 for SHA-224
+                * and 8 for SHA-256).
+                */
+               unsigned x;
+
+               x = id - 1;
+               x = ((x + (x & (x >> 1))) << 2) + (x >> 1);
+               return offsetof(br_multihash_context, val_32)
+                       + x * sizeof(uint32_t);
+       }
+}
+
+/* see bearssl_hash.h */
+void
+br_multihash_zero(br_multihash_context *ctx)
+{
+       /*
+        * This is not standard, but yields very short and efficient code,
+        * and it works "everywhere".
+        */
+       memset(ctx, 0, sizeof *ctx);
+}
+
+/* see bearssl_hash.h */
+void
+br_multihash_init(br_multihash_context *ctx)
+{
+       int i;
+
+       ctx->count = 0;
+       for (i = 1; i <= 6; i ++) {
+               const br_hash_class *hc;
+
+               hc = ctx->impl[i - 1];
+               if (hc != NULL) {
+                       gen_hash_context g;
+
+                       hc->init(&g.vtable);
+                       hc->state(&g.vtable,
+                               (unsigned char *)ctx + get_state_offset(i));
+               }
+       }
+}
+
+/* see bearssl_hash.h */
+void
+br_multihash_update(br_multihash_context *ctx, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t ptr;
+
+       buf = data;
+       ptr = (size_t)ctx->count & 127;
+       while (len > 0) {
+               size_t clen;
+
+               clen = 128 - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(ctx->buf + ptr, buf, clen);
+               ptr += clen;
+               buf += clen;
+               len -= clen;
+               ctx->count += (uint64_t)clen;
+               if (ptr == 128) {
+                       int i;
+
+                       for (i = 1; i <= 6; i ++) {
+                               const br_hash_class *hc;
+
+                               hc = ctx->impl[i - 1];
+                               if (hc != NULL) {
+                                       gen_hash_context g;
+                                       unsigned char *state;
+
+                                       state = (unsigned char *)ctx
+                                               + get_state_offset(i);
+                                       hc->set_state(&g.vtable,
+                                               state, ctx->count - 128);
+                                       hc->update(&g.vtable, ctx->buf, 128);
+                                       hc->state(&g.vtable, state);
+                               }
+                       }
+                       ptr = 0;
+               }
+       }
+}
+
+/* see bearssl_hash.h */
+size_t
+br_multihash_out(const br_multihash_context *ctx, int id, void *dst)
+{
+       const br_hash_class *hc;
+       gen_hash_context g;
+       const unsigned char *state;
+
+       hc = ctx->impl[id - 1];
+       if (hc == NULL) {
+               return 0;
+       }
+       state = (const unsigned char *)ctx + get_state_offset(id);
+       hc->set_state(&g.vtable, state, ctx->count & ~(uint64_t)127);
+       hc->update(&g.vtable, ctx->buf, ctx->count & (uint64_t)127);
+       hc->out(&g.vtable, dst);
+       return (hc->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK;
+}
diff --git a/src/hash/sha1.c b/src/hash/sha1.c
new file mode 100644 (file)
index 0000000..4f65d84
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define F(B, C, D)     ((((C) ^ (D)) & (B)) ^ (D))
+#define G(B, C, D)     ((B) ^ (C) ^ (D))
+#define H(B, C, D)     (((D) & (C)) | (((D) | (C)) & (B)))
+#define I(B, C, D)     G(B, C, D)
+
+#define ROTL(x, n)    (((x) << (n)) | ((x) >> (32 - (n))))
+
+#define K1     ((uint32_t)0x5A827999)
+#define K2     ((uint32_t)0x6ED9EBA1)
+#define K3     ((uint32_t)0x8F1BBCDC)
+#define K4     ((uint32_t)0xCA62C1D6)
+
+/* see inner.h */
+const uint32_t br_sha1_IV[5] = {
+       0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
+};
+
+/* see inner.h */
+void
+br_sha1_round(const unsigned char *buf, uint32_t *val)
+{
+       uint32_t m[80];
+       uint32_t a, b, c, d, e;
+       int i;
+
+       a = val[0];
+       b = val[1];
+       c = val[2];
+       d = val[3];
+       e = val[4];
+       br_range_dec32be(m, 16, buf);
+       for (i = 16; i < 80; i ++) {
+               uint32_t x = m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16];
+               m[i] = ROTL(x, 1);
+       }
+
+       for (i = 0; i < 20; i += 5) {
+               e += ROTL(a, 5) + F(b, c, d) + K1 + m[i + 0]; b = ROTL(b, 30);
+               d += ROTL(e, 5) + F(a, b, c) + K1 + m[i + 1]; a = ROTL(a, 30);
+               c += ROTL(d, 5) + F(e, a, b) + K1 + m[i + 2]; e = ROTL(e, 30);
+               b += ROTL(c, 5) + F(d, e, a) + K1 + m[i + 3]; d = ROTL(d, 30);
+               a += ROTL(b, 5) + F(c, d, e) + K1 + m[i + 4]; c = ROTL(c, 30);
+       }
+       for (i = 20; i < 40; i += 5) {
+               e += ROTL(a, 5) + G(b, c, d) + K2 + m[i + 0]; b = ROTL(b, 30);
+               d += ROTL(e, 5) + G(a, b, c) + K2 + m[i + 1]; a = ROTL(a, 30);
+               c += ROTL(d, 5) + G(e, a, b) + K2 + m[i + 2]; e = ROTL(e, 30);
+               b += ROTL(c, 5) + G(d, e, a) + K2 + m[i + 3]; d = ROTL(d, 30);
+               a += ROTL(b, 5) + G(c, d, e) + K2 + m[i + 4]; c = ROTL(c, 30);
+       }
+       for (i = 40; i < 60; i += 5) {
+               e += ROTL(a, 5) + H(b, c, d) + K3 + m[i + 0]; b = ROTL(b, 30);
+               d += ROTL(e, 5) + H(a, b, c) + K3 + m[i + 1]; a = ROTL(a, 30);
+               c += ROTL(d, 5) + H(e, a, b) + K3 + m[i + 2]; e = ROTL(e, 30);
+               b += ROTL(c, 5) + H(d, e, a) + K3 + m[i + 3]; d = ROTL(d, 30);
+               a += ROTL(b, 5) + H(c, d, e) + K3 + m[i + 4]; c = ROTL(c, 30);
+       }
+       for (i = 60; i < 80; i += 5) {
+               e += ROTL(a, 5) + I(b, c, d) + K4 + m[i + 0]; b = ROTL(b, 30);
+               d += ROTL(e, 5) + I(a, b, c) + K4 + m[i + 1]; a = ROTL(a, 30);
+               c += ROTL(d, 5) + I(e, a, b) + K4 + m[i + 2]; e = ROTL(e, 30);
+               b += ROTL(c, 5) + I(d, e, a) + K4 + m[i + 3]; d = ROTL(d, 30);
+               a += ROTL(b, 5) + I(c, d, e) + K4 + m[i + 4]; c = ROTL(c, 30);
+       }
+
+       val[0] += a;
+       val[1] += b;
+       val[2] += c;
+       val[3] += d;
+       val[4] += e;
+}
+
+/* see bearssl.h */
+void
+br_sha1_init(br_sha1_context *cc)
+{
+       cc->vtable = &br_sha1_vtable;
+       memcpy(cc->val, br_sha1_IV, sizeof cc->val);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_sha1_update(br_sha1_context *cc, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t ptr;
+
+       buf = data;
+       ptr = (size_t)cc->count & 63;
+       while (len > 0) {
+               size_t clen;
+
+               clen = 64 - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(cc->buf + ptr, buf, clen);
+               ptr += clen;
+               buf += clen;
+               len -= clen;
+               cc->count += (uint64_t)clen;
+               if (ptr == 64) {
+                       br_sha1_round(cc->buf, cc->val);
+                       ptr = 0;
+               }
+       }
+}
+
+/* see bearssl.h */
+void
+br_sha1_out(const br_sha1_context *cc, void *dst)
+{
+       unsigned char buf[64];
+       uint32_t val[5];
+       size_t ptr;
+
+       ptr = (size_t)cc->count & 63;
+       memcpy(buf, cc->buf, ptr);
+       memcpy(val, cc->val, sizeof val);
+       buf[ptr ++] = 0x80;
+       if (ptr > 56) {
+               memset(buf + ptr, 0, 64 - ptr);
+               br_sha1_round(buf, val);
+               memset(buf, 0, 56);
+       } else {
+               memset(buf + ptr, 0, 56 - ptr);
+       }
+       br_enc64be(buf + 56, cc->count << 3);
+       br_sha1_round(buf, val);
+       br_range_enc32be(dst, val, 5);
+}
+
+/* see bearssl.h */
+uint64_t
+br_sha1_state(const br_sha1_context *cc, void *dst)
+{
+       br_range_enc32be(dst, cc->val, 5);
+       return cc->count;
+}
+
+/* see bearssl.h */
+void
+br_sha1_set_state(br_sha1_context *cc, const void *stb, uint64_t count)
+{
+       br_range_dec32be(cc->val, 5, stb);
+       cc->count = count;
+}
+
+/* see bearssl.h */
+const br_hash_class br_sha1_vtable = {
+       sizeof(br_sha1_context),
+       BR_HASHDESC_ID(br_sha1_ID)
+               | BR_HASHDESC_OUT(20)
+               | BR_HASHDESC_STATE(20)
+               | BR_HASHDESC_LBLEN(6)
+               | BR_HASHDESC_MD_PADDING
+               | BR_HASHDESC_MD_PADDING_BE,
+       (void (*)(const br_hash_class **))&br_sha1_init,
+       (void (*)(const br_hash_class **, const void *, size_t))&br_sha1_update,
+       (void (*)(const br_hash_class *const *, void *))&br_sha1_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))&br_sha1_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_sha1_set_state
+};
diff --git a/src/hash/sha2big.c b/src/hash/sha2big.c
new file mode 100644 (file)
index 0000000..5be92ed
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define CH(X, Y, Z)    ((((Y) ^ (Z)) & (X)) ^ (Z))
+#define MAJ(X, Y, Z)   (((Y) & (Z)) | (((Y) | (Z)) & (X)))
+
+#define ROTR(x, n)    (((uint64_t)(x) << (64 - (n))) | ((uint64_t)(x) >> (n)))
+
+#define BSG5_0(x)      (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39))
+#define BSG5_1(x)      (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41))
+#define SSG5_0(x)      (ROTR(x, 1) ^ ROTR(x, 8) ^ (uint64_t)((x) >> 7))
+#define SSG5_1(x)      (ROTR(x, 19) ^ ROTR(x, 61) ^ (uint64_t)((x) >> 6))
+
+static const uint64_t IV384[8] = {
+       0xCBBB9D5DC1059ED8, 0x629A292A367CD507,
+       0x9159015A3070DD17, 0x152FECD8F70E5939,
+       0x67332667FFC00B31, 0x8EB44A8768581511,
+       0xDB0C2E0D64F98FA7, 0x47B5481DBEFA4FA4
+};
+
+static const uint64_t IV512[8] = {
+       0x6A09E667F3BCC908, 0xBB67AE8584CAA73B,
+       0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
+       0x510E527FADE682D1, 0x9B05688C2B3E6C1F,
+       0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179
+};
+
+static const uint64_t K[80] = {
+       0x428A2F98D728AE22, 0x7137449123EF65CD,
+       0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC,
+       0x3956C25BF348B538, 0x59F111F1B605D019,
+       0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118,
+       0xD807AA98A3030242, 0x12835B0145706FBE,
+       0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2,
+       0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1,
+       0x9BDC06A725C71235, 0xC19BF174CF692694,
+       0xE49B69C19EF14AD2, 0xEFBE4786384F25E3,
+       0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65,
+       0x2DE92C6F592B0275, 0x4A7484AA6EA6E483,
+       0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5,
+       0x983E5152EE66DFAB, 0xA831C66D2DB43210,
+       0xB00327C898FB213F, 0xBF597FC7BEEF0EE4,
+       0xC6E00BF33DA88FC2, 0xD5A79147930AA725,
+       0x06CA6351E003826F, 0x142929670A0E6E70,
+       0x27B70A8546D22FFC, 0x2E1B21385C26C926,
+       0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF,
+       0x650A73548BAF63DE, 0x766A0ABB3C77B2A8,
+       0x81C2C92E47EDAEE6, 0x92722C851482353B,
+       0xA2BFE8A14CF10364, 0xA81A664BBC423001,
+       0xC24B8B70D0F89791, 0xC76C51A30654BE30,
+       0xD192E819D6EF5218, 0xD69906245565A910,
+       0xF40E35855771202A, 0x106AA07032BBD1B8,
+       0x19A4C116B8D2D0C8, 0x1E376C085141AB53,
+       0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8,
+       0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB,
+       0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3,
+       0x748F82EE5DEFB2FC, 0x78A5636F43172F60,
+       0x84C87814A1F0AB72, 0x8CC702081A6439EC,
+       0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9,
+       0xBEF9A3F7B2C67915, 0xC67178F2E372532B,
+       0xCA273ECEEA26619C, 0xD186B8C721C0C207,
+       0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178,
+       0x06F067AA72176FBA, 0x0A637DC5A2C898A6,
+       0x113F9804BEF90DAE, 0x1B710B35131C471B,
+       0x28DB77F523047D84, 0x32CAAB7B40C72493,
+       0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C,
+       0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A,
+       0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817
+};
+
+static void
+sha2big_round(const unsigned char *buf, uint64_t *val)
+{
+
+#define SHA2BIG_STEP(A, B, C, D, E, F, G, H, j)   do { \
+               uint64_t T1, T2; \
+               T1 = H + BSG5_1(E) + CH(E, F, G) + K[j] + w[j]; \
+               T2 = BSG5_0(A) + MAJ(A, B, C); \
+               D += T1; \
+               H = T1 + T2; \
+       } while (0)
+
+       int i;
+       uint64_t a, b, c, d, e, f, g, h;
+       uint64_t w[80];
+
+       br_range_dec64be(w, 16, buf);
+       for (i = 16; i < 80; i ++) {
+               w[i] = SSG5_1(w[i - 2]) + w[i - 7]
+                       + SSG5_0(w[i - 15]) + w[i - 16];
+       }
+       a = val[0];
+       b = val[1];
+       c = val[2];
+       d = val[3];
+       e = val[4];
+       f = val[5];
+       g = val[6];
+       h = val[7];
+       for (i = 0; i < 80; i += 8) {
+               SHA2BIG_STEP(a, b, c, d, e, f, g, h, i + 0);
+               SHA2BIG_STEP(h, a, b, c, d, e, f, g, i + 1);
+               SHA2BIG_STEP(g, h, a, b, c, d, e, f, i + 2);
+               SHA2BIG_STEP(f, g, h, a, b, c, d, e, i + 3);
+               SHA2BIG_STEP(e, f, g, h, a, b, c, d, i + 4);
+               SHA2BIG_STEP(d, e, f, g, h, a, b, c, i + 5);
+               SHA2BIG_STEP(c, d, e, f, g, h, a, b, i + 6);
+               SHA2BIG_STEP(b, c, d, e, f, g, h, a, i + 7);
+       }
+       val[0] += a;
+       val[1] += b;
+       val[2] += c;
+       val[3] += d;
+       val[4] += e;
+       val[5] += f;
+       val[6] += g;
+       val[7] += h;
+}
+
+static void
+sha2big_update(br_sha384_context *cc, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t ptr;
+
+       buf = data;
+       ptr = (size_t)cc->count & 127;
+       cc->count += (uint64_t)len;
+       while (len > 0) {
+               size_t clen;
+
+               clen = 128 - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(cc->buf + ptr, buf, clen);
+               ptr += clen;
+               buf += clen;
+               len -= clen;
+               if (ptr == 128) {
+                       sha2big_round(cc->buf, cc->val);
+                       ptr = 0;
+               }
+       }
+}
+
+static void
+sha2big_out(const br_sha384_context *cc, void *dst, int num)
+{
+       unsigned char buf[128];
+       uint64_t val[8];
+       size_t ptr;
+
+       ptr = (size_t)cc->count & 127;
+       memcpy(buf, cc->buf, ptr);
+       memcpy(val, cc->val, sizeof val);
+       buf[ptr ++] = 0x80;
+       if (ptr > 112) {
+               memset(buf + ptr, 0, 128 - ptr);
+               sha2big_round(buf, val);
+               memset(buf, 0, 112);
+       } else {
+               memset(buf + ptr, 0, 112 - ptr);
+       }
+       br_enc64be(buf + 112, cc->count >> 61);
+       br_enc64be(buf + 120, cc->count << 3);
+       sha2big_round(buf, val);
+       br_range_enc64be(dst, val, num);
+}
+
+/* see bearssl.h */
+void
+br_sha384_init(br_sha384_context *cc)
+{
+       cc->vtable = &br_sha384_vtable;
+       memcpy(cc->val, IV384, sizeof IV384);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_sha384_update(br_sha384_context *cc, const void *data, size_t len)
+{
+       sha2big_update(cc, data, len);
+}
+
+/* see bearssl.h */
+void
+br_sha384_out(const br_sha384_context *cc, void *dst)
+{
+       sha2big_out(cc, dst, 6);
+}
+
+/* see bearssl.h */
+uint64_t
+br_sha384_state(const br_sha384_context *cc, void *dst)
+{
+       br_range_enc64be(dst, cc->val, 8);
+       return cc->count;
+}
+
+/* see bearssl.h */
+void
+br_sha384_set_state(br_sha384_context *cc, const void *stb, uint64_t count)
+{
+       br_range_dec64be(cc->val, 8, stb);
+       cc->count = count;
+}
+
+/* see bearssl.h */
+void
+br_sha512_init(br_sha512_context *cc)
+{
+       cc->vtable = &br_sha512_vtable;
+       memcpy(cc->val, IV512, sizeof IV512);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_sha512_out(const br_sha512_context *cc, void *dst)
+{
+       sha2big_out(cc, dst, 8);
+}
+
+/* see bearssl.h */
+const br_hash_class br_sha384_vtable = {
+       sizeof(br_sha384_context),
+       BR_HASHDESC_ID(br_sha384_ID)
+               | BR_HASHDESC_OUT(48)
+               | BR_HASHDESC_STATE(64)
+               | BR_HASHDESC_LBLEN(7)
+               | BR_HASHDESC_MD_PADDING
+               | BR_HASHDESC_MD_PADDING_BE
+               | BR_HASHDESC_MD_PADDING_128,
+       (void (*)(const br_hash_class **))&br_sha384_init,
+       (void (*)(const br_hash_class **, const void *, size_t))
+               &br_sha384_update,
+       (void (*)(const br_hash_class *const *, void *))&br_sha384_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))&br_sha384_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_sha384_set_state
+};
+
+/* see bearssl.h */
+const br_hash_class br_sha512_vtable = {
+       sizeof(br_sha512_context),
+       BR_HASHDESC_ID(br_sha512_ID)
+               | BR_HASHDESC_OUT(64)
+               | BR_HASHDESC_STATE(64)
+               | BR_HASHDESC_LBLEN(7)
+               | BR_HASHDESC_MD_PADDING
+               | BR_HASHDESC_MD_PADDING_BE
+               | BR_HASHDESC_MD_PADDING_128,
+       (void (*)(const br_hash_class **))&br_sha512_init,
+       (void (*)(const br_hash_class **, const void *, size_t))
+               &br_sha512_update,
+       (void (*)(const br_hash_class *const *, void *))&br_sha512_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))&br_sha512_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_sha512_set_state
+};
diff --git a/src/hash/sha2small.c b/src/hash/sha2small.c
new file mode 100644 (file)
index 0000000..ca19655
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define CH(X, Y, Z)    ((((Y) ^ (Z)) & (X)) ^ (Z))
+#define MAJ(X, Y, Z)   (((Y) & (Z)) | (((Y) | (Z)) & (X)))
+
+#define ROTR(x, n)    (((uint32_t)(x) << (32 - (n))) | ((uint32_t)(x) >> (n)))
+
+#define BSG2_0(x)      (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define BSG2_1(x)      (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SSG2_0(x)      (ROTR(x, 7) ^ ROTR(x, 18) ^ (uint32_t)((x) >> 3))
+#define SSG2_1(x)      (ROTR(x, 17) ^ ROTR(x, 19) ^ (uint32_t)((x) >> 10))
+
+/* see inner.h */
+const uint32_t br_sha224_IV[8] = {
+       0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
+       0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
+};
+
+/* see inner.h */
+const uint32_t br_sha256_IV[8] = {
+       0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+       0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
+};
+
+static const uint32_t K[64] = {
+       0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
+       0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+       0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
+       0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+       0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
+       0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+       0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
+       0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+       0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
+       0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+       0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
+       0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+       0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
+       0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+       0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
+       0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
+};
+
+/* see inner.h */
+void
+br_sha2small_round(const unsigned char *buf, uint32_t *val)
+{
+
+#define SHA2_STEP(A, B, C, D, E, F, G, H, j)   do { \
+               uint32_t T1, T2; \
+               T1 = H + BSG2_1(E) + CH(E, F, G) + K[j] + w[j]; \
+               T2 = BSG2_0(A) + MAJ(A, B, C); \
+               D += T1; \
+               H = T1 + T2; \
+       } while (0)
+
+       int i;
+       uint32_t a, b, c, d, e, f, g, h;
+       uint32_t w[64];
+
+       br_range_dec32be(w, 16, buf);
+       for (i = 16; i < 64; i ++) {
+               w[i] = SSG2_1(w[i - 2]) + w[i - 7]
+                       + SSG2_0(w[i - 15]) + w[i - 16];
+       }
+       a = val[0];
+       b = val[1];
+       c = val[2];
+       d = val[3];
+       e = val[4];
+       f = val[5];
+       g = val[6];
+       h = val[7];
+       for (i = 0; i < 64; i += 8) {
+               SHA2_STEP(a, b, c, d, e, f, g, h, i + 0);
+               SHA2_STEP(h, a, b, c, d, e, f, g, i + 1);
+               SHA2_STEP(g, h, a, b, c, d, e, f, i + 2);
+               SHA2_STEP(f, g, h, a, b, c, d, e, i + 3);
+               SHA2_STEP(e, f, g, h, a, b, c, d, i + 4);
+               SHA2_STEP(d, e, f, g, h, a, b, c, i + 5);
+               SHA2_STEP(c, d, e, f, g, h, a, b, i + 6);
+               SHA2_STEP(b, c, d, e, f, g, h, a, i + 7);
+       }
+       val[0] += a;
+       val[1] += b;
+       val[2] += c;
+       val[3] += d;
+       val[4] += e;
+       val[5] += f;
+       val[6] += g;
+       val[7] += h;
+
+#if 0
+/* obsolete */
+#define SHA2_MEXP1(pc)   do { \
+               W[pc] = br_dec32be(buf + ((pc) << 2)); \
+       } while (0)
+
+#define SHA2_MEXP2(pc)   do { \
+               W[(pc) & 0x0F] = SSG2_1(W[((pc) - 2) & 0x0F]) \
+                       + W[((pc) - 7) & 0x0F] \
+                       + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]; \
+       } while (0)
+
+#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, pc)   do { \
+               uint32_t t1, t2; \
+               SHA2_MEXP ## n(pc); \
+               t1 = h + BSG2_1(e) + CH(e, f, g) \
+                       + K[pcount + (pc)] + W[(pc) & 0x0F]; \
+               t2 = BSG2_0(a) + MAJ(a, b, c); \
+               d += t1; \
+               h = t1 + t2; \
+       } while (0)
+
+#define SHA2_STEP1(a, b, c, d, e, f, g, h, pc) \
+       SHA2_STEPn(1, a, b, c, d, e, f, g, h, pc)
+#define SHA2_STEP2(a, b, c, d, e, f, g, h, pc) \
+       SHA2_STEPn(2, a, b, c, d, e, f, g, h, pc)
+
+       uint32_t A, B, C, D, E, F, G, H;
+       uint32_t W[16];
+       unsigned pcount;
+
+       A = val[0];
+       B = val[1];
+       C = val[2];
+       D = val[3];
+       E = val[4];
+       F = val[5];
+       G = val[6];
+       H = val[7];
+       pcount = 0;
+       SHA2_STEP1(A, B, C, D, E, F, G, H,  0);
+       SHA2_STEP1(H, A, B, C, D, E, F, G,  1);
+       SHA2_STEP1(G, H, A, B, C, D, E, F,  2);
+       SHA2_STEP1(F, G, H, A, B, C, D, E,  3);
+       SHA2_STEP1(E, F, G, H, A, B, C, D,  4);
+       SHA2_STEP1(D, E, F, G, H, A, B, C,  5);
+       SHA2_STEP1(C, D, E, F, G, H, A, B,  6);
+       SHA2_STEP1(B, C, D, E, F, G, H, A,  7);
+       SHA2_STEP1(A, B, C, D, E, F, G, H,  8);
+       SHA2_STEP1(H, A, B, C, D, E, F, G,  9);
+       SHA2_STEP1(G, H, A, B, C, D, E, F, 10);
+       SHA2_STEP1(F, G, H, A, B, C, D, E, 11);
+       SHA2_STEP1(E, F, G, H, A, B, C, D, 12);
+       SHA2_STEP1(D, E, F, G, H, A, B, C, 13);
+       SHA2_STEP1(C, D, E, F, G, H, A, B, 14);
+       SHA2_STEP1(B, C, D, E, F, G, H, A, 15);
+       for (pcount = 16; pcount < 64; pcount += 16) {
+               SHA2_STEP2(A, B, C, D, E, F, G, H,  0);
+               SHA2_STEP2(H, A, B, C, D, E, F, G,  1);
+               SHA2_STEP2(G, H, A, B, C, D, E, F,  2);
+               SHA2_STEP2(F, G, H, A, B, C, D, E,  3);
+               SHA2_STEP2(E, F, G, H, A, B, C, D,  4);
+               SHA2_STEP2(D, E, F, G, H, A, B, C,  5);
+               SHA2_STEP2(C, D, E, F, G, H, A, B,  6);
+               SHA2_STEP2(B, C, D, E, F, G, H, A,  7);
+               SHA2_STEP2(A, B, C, D, E, F, G, H,  8);
+               SHA2_STEP2(H, A, B, C, D, E, F, G,  9);
+               SHA2_STEP2(G, H, A, B, C, D, E, F, 10);
+               SHA2_STEP2(F, G, H, A, B, C, D, E, 11);
+               SHA2_STEP2(E, F, G, H, A, B, C, D, 12);
+               SHA2_STEP2(D, E, F, G, H, A, B, C, 13);
+               SHA2_STEP2(C, D, E, F, G, H, A, B, 14);
+               SHA2_STEP2(B, C, D, E, F, G, H, A, 15);
+       }
+       val[0] += A;
+       val[1] += B;
+       val[2] += C;
+       val[3] += D;
+       val[4] += E;
+       val[5] += F;
+       val[6] += G;
+       val[7] += H;
+#endif
+}
+
+static void
+sha2small_update(br_sha224_context *cc, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t ptr;
+
+       buf = data;
+       ptr = (size_t)cc->count & 63;
+       cc->count += (uint64_t)len;
+       while (len > 0) {
+               size_t clen;
+
+               clen = 64 - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(cc->buf + ptr, buf, clen);
+               ptr += clen;
+               buf += clen;
+               len -= clen;
+               if (ptr == 64) {
+                       br_sha2small_round(cc->buf, cc->val);
+                       ptr = 0;
+               }
+       }
+}
+
+static void
+sha2small_out(const br_sha224_context *cc, void *dst, int num)
+{
+       unsigned char buf[64];
+       uint32_t val[8];
+       size_t ptr;
+
+       ptr = (size_t)cc->count & 63;
+       memcpy(buf, cc->buf, ptr);
+       memcpy(val, cc->val, sizeof val);
+       buf[ptr ++] = 0x80;
+       if (ptr > 56) {
+               memset(buf + ptr, 0, 64 - ptr);
+               br_sha2small_round(buf, val);
+               memset(buf, 0, 56);
+       } else {
+               memset(buf + ptr, 0, 56 - ptr);
+       }
+       br_enc64be(buf + 56, cc->count << 3);
+       br_sha2small_round(buf, val);
+       br_range_enc32be(dst, val, num);
+}
+
+/* see bearssl.h */
+void
+br_sha224_init(br_sha224_context *cc)
+{
+       cc->vtable = &br_sha224_vtable;
+       memcpy(cc->val, br_sha224_IV, sizeof cc->val);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_sha224_update(br_sha224_context *cc, const void *data, size_t len)
+{
+       sha2small_update(cc, data, len);
+}
+
+/* see bearssl.h */
+void
+br_sha224_out(const br_sha224_context *cc, void *dst)
+{
+       sha2small_out(cc, dst, 7);
+}
+
+/* see bearssl.h */
+uint64_t
+br_sha224_state(const br_sha224_context *cc, void *dst)
+{
+       br_range_enc32be(dst, cc->val, 8);
+       return cc->count;
+}
+
+/* see bearssl.h */
+void
+br_sha224_set_state(br_sha224_context *cc, const void *stb, uint64_t count)
+{
+       br_range_dec32be(cc->val, 8, stb);
+       cc->count = count;
+}
+
+/* see bearssl.h */
+void
+br_sha256_init(br_sha256_context *cc)
+{
+       cc->vtable = &br_sha256_vtable;
+       memcpy(cc->val, br_sha256_IV, sizeof cc->val);
+       cc->count = 0;
+}
+
+/* see bearssl.h */
+void
+br_sha256_out(const br_sha256_context *cc, void *dst)
+{
+       sha2small_out(cc, dst, 8);
+}
+
+/* see bearssl.h */
+const br_hash_class br_sha224_vtable = {
+       sizeof(br_sha224_context),
+       BR_HASHDESC_ID(br_sha224_ID)
+               | BR_HASHDESC_OUT(28)
+               | BR_HASHDESC_STATE(32)
+               | BR_HASHDESC_LBLEN(6)
+               | BR_HASHDESC_MD_PADDING
+               | BR_HASHDESC_MD_PADDING_BE,
+       (void (*)(const br_hash_class **))&br_sha224_init,
+       (void (*)(const br_hash_class **,
+               const void *, size_t))&br_sha224_update,
+       (void (*)(const br_hash_class *const *, void *))&br_sha224_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))&br_sha224_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_sha224_set_state
+};
+
+/* see bearssl.h */
+const br_hash_class br_sha256_vtable = {
+       sizeof(br_sha256_context),
+       BR_HASHDESC_ID(br_sha256_ID)
+               | BR_HASHDESC_OUT(32)
+               | BR_HASHDESC_STATE(32)
+               | BR_HASHDESC_LBLEN(6)
+               | BR_HASHDESC_MD_PADDING
+               | BR_HASHDESC_MD_PADDING_BE,
+       (void (*)(const br_hash_class **))&br_sha256_init,
+       (void (*)(const br_hash_class **,
+               const void *, size_t))&br_sha256_update,
+       (void (*)(const br_hash_class *const *, void *))&br_sha256_out,
+       (uint64_t (*)(const br_hash_class *const *, void *))&br_sha256_state,
+       (void (*)(const br_hash_class **, const void *, uint64_t))
+               &br_sha256_set_state
+};
diff --git a/src/inner.h b/src/inner.h
new file mode 100644 (file)
index 0000000..9edaadd
--- /dev/null
@@ -0,0 +1,1546 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INNER_H__
+#define INNER_H__
+
+#include <string.h>
+#include <limits.h>
+
+#include "config.h"
+#include "bearssl.h"
+
+/*
+ * Maximum size for a RSA modulus (in bits). Allocated stack buffers
+ * depend on that size, so this value should be kept small. Currently,
+ * 2048-bit RSA keys offer adequate security, and should still do so for
+ * the next few decades; however, a number of widespread PKI have
+ * already set their root keys to RSA-4096, so we should be able to
+ * process such keys.
+ *
+ * This value MUST be a multiple of 64.
+ */
+#define BR_MAX_RSA_SIZE   4096
+
+/*
+ * Maximum size for a RSA factor (in bits). This is for RSA private-key
+ * operations. Default is to support factors up to a bit more than half
+ * the maximum modulus size.
+ *
+ * This value MUST be a multiple of 32.
+ */
+#define BR_MAX_RSA_FACTOR   ((BR_MAX_RSA_SIZE + 64) >> 1)
+
+/*
+ * Maximum size for an EC curve (modulus or order), in bits. Size of
+ * stack buffers depends on that parameter. This size MUST be a multiple
+ * of 8 (so that decoding an integer with that many bytes does not
+ * overflow).
+ */
+#define BR_MAX_EC_SIZE   528
+
+/*
+ * Some macros to recognize the current architecture. Right now, we are
+ * interested into automatically recognizing architecture with efficient
+ * 64-bit types so that we may automatically use implementations that
+ * use 64-bit registers in that case. Future versions may detect, e.g.,
+ * availability of SSE2 intrinsics.
+ *
+ * If 'unsigned long' is a 64-bit type, then we assume that 64-bit types
+ * are efficient. Otherwise, we rely on macros that depend on compiler,
+ * OS and architecture. In any case, failure to detect the architecture
+ * as 64-bit means that the 32-bit code will be used, and that code
+ * works also on 64-bit architectures (the 64-bit code may simply be
+ * more efficient).
+ *
+ * The test on 'unsigned long' should already catch most cases, the one
+ * notable exception being Windows code where 'unsigned long' is kept to
+ * 32-bit for compatbility with all the legacy code that liberally uses
+ * the 'DWORD' type for 32-bit values.
+ *
+ * Macro names are taken from: http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros
+ */
+#ifndef BR_64
+#if ((ULONG_MAX >> 31) >> 31) == 3
+#define BR_64   1
+#elif defined(__ia64) || defined(__itanium__) || defined(_M_IA64)
+#define BR_64   1
+#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \
+       || defined(__64BIT__) || defined(_LP64) || defined(__LP64__)
+#define BR_64   1
+#elif defined(__sparc64__)
+#define BR_64   1
+#elif defined(__x86_64__) || defined(_M_X64)
+#define BR_64   1
+#endif
+#endif
+
+/* ==================================================================== */
+/*
+ * Encoding/decoding functions.
+ *
+ * 32-bit and 64-bit decoding, both little-endian and big-endian, is
+ * implemented with the inline functions below. These functions are
+ * generic: they don't depend on the architecture natural endianness,
+ * and they can handle unaligned accesses. Optimized versions for some
+ * specific architectures may be implemented at a later time.
+ */
+
+static inline void
+br_enc16le(void *dst, unsigned x)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       buf[0] = (unsigned char)x;
+       buf[1] = (unsigned char)(x >> 8);
+}
+
+static inline void
+br_enc16be(void *dst, unsigned x)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       buf[0] = (unsigned char)(x >> 8);
+       buf[1] = (unsigned char)x;
+}
+
+static inline unsigned
+br_dec16le(const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       return (unsigned)buf[0] | ((unsigned)buf[1] << 8);
+}
+
+static inline unsigned
+br_dec16be(const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       return ((unsigned)buf[0] << 8) | (unsigned)buf[1];
+}
+
+static inline void
+br_enc32le(void *dst, uint32_t x)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       buf[0] = (unsigned char)x;
+       buf[1] = (unsigned char)(x >> 8);
+       buf[2] = (unsigned char)(x >> 16);
+       buf[3] = (unsigned char)(x >> 24);
+}
+
+static inline void
+br_enc32be(void *dst, uint32_t x)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       buf[0] = (unsigned char)(x >> 24);
+       buf[1] = (unsigned char)(x >> 16);
+       buf[2] = (unsigned char)(x >> 8);
+       buf[3] = (unsigned char)x;
+}
+
+static inline uint32_t
+br_dec32le(const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       return (uint32_t)buf[0]
+               | ((uint32_t)buf[1] << 8)
+               | ((uint32_t)buf[2] << 16)
+               | ((uint32_t)buf[3] << 24);
+}
+
+static inline uint32_t
+br_dec32be(const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       return ((uint32_t)buf[0] << 24)
+               | ((uint32_t)buf[1] << 16)
+               | ((uint32_t)buf[2] << 8)
+               | (uint32_t)buf[3];
+}
+
+static inline void
+br_enc64le(void *dst, uint64_t x)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       br_enc32le(buf, (uint32_t)x);
+       br_enc32le(buf + 4, (uint32_t)(x >> 32));
+}
+
+static inline void
+br_enc64be(void *dst, uint64_t x)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       br_enc32be(buf, (uint32_t)(x >> 32));
+       br_enc32be(buf + 4, (uint32_t)x);
+}
+
+static inline uint64_t
+br_dec64le(const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       return (uint64_t)br_dec32le(buf)
+               | ((uint64_t)br_dec32le(buf + 4) << 32);
+}
+
+static inline uint64_t
+br_dec64be(const void *src)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       return ((uint64_t)br_dec32be(buf) << 32)
+               | (uint64_t)br_dec32be(buf + 4);
+}
+
+/*
+ * Range decoding and encoding (for several successive values).
+ */
+void br_range_dec16le(uint16_t *v, size_t num, const void *src);
+void br_range_dec16be(uint16_t *v, size_t num, const void *src);
+void br_range_enc16le(void *dst, const uint16_t *v, size_t num);
+void br_range_enc16be(void *dst, const uint16_t *v, size_t num);
+
+void br_range_dec32le(uint32_t *v, size_t num, const void *src);
+void br_range_dec32be(uint32_t *v, size_t num, const void *src);
+void br_range_enc32le(void *dst, const uint32_t *v, size_t num);
+void br_range_enc32be(void *dst, const uint32_t *v, size_t num);
+
+void br_range_dec64le(uint64_t *v, size_t num, const void *src);
+void br_range_dec64be(uint64_t *v, size_t num, const void *src);
+void br_range_enc64le(void *dst, const uint64_t *v, size_t num);
+void br_range_enc64be(void *dst, const uint64_t *v, size_t num);
+
+/*
+ * Byte-swap a 32-bit integer.
+ */
+static inline uint32_t
+br_swap32(uint32_t x)
+{
+       x = ((x & (uint32_t)0x00FF00FF) << 8)
+               | ((x >> 8) & (uint32_t)0x00FF00FF);
+       return (x << 16) | (x >> 16);
+}
+
+/* ==================================================================== */
+/*
+ * Support code for hash functions.
+ */
+
+/*
+ * IV for MD5, SHA-1, SHA-224 and SHA-256.
+ */
+extern const uint32_t br_md5_IV[];
+extern const uint32_t br_sha1_IV[];
+extern const uint32_t br_sha224_IV[];
+extern const uint32_t br_sha256_IV[];
+
+/*
+ * Round functions for MD5, SHA-1, SHA-224 and SHA-256 (SHA-224 and
+ * SHA-256 use the same round function).
+ */
+void br_md5_round(const unsigned char *buf, uint32_t *val);
+void br_sha1_round(const unsigned char *buf, uint32_t *val);
+void br_sha2small_round(const unsigned char *buf, uint32_t *val);
+
+/*
+ * The core function for the TLS PRF. It computes
+ * P_hash(secret, label + seed), and XORs the result into the dst buffer.
+ */
+void br_tls_phash(void *dst, size_t len,
+       const br_hash_class *dig,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len);
+
+/*
+ * Copy all configured hash implementations from a multihash context
+ * to another.
+ */
+static inline void
+br_multihash_copyimpl(br_multihash_context *dst,
+       const br_multihash_context *src)
+{
+       memcpy(dst->impl, src->impl, sizeof src->impl);
+}
+
+/* ==================================================================== */
+/*
+ * Constant-time primitives. These functions manipulate 32-bit values in
+ * order to provide constant-time comparisons and multiplexers.
+ *
+ * Boolean values (the "ctl" bits) MUST have value 0 or 1.
+ *
+ * Implementation notes:
+ * =====================
+ *
+ * The uintN_t types are unsigned and with width exactly N bits; the C
+ * standard guarantees that computations are performed modulo 2^N, and
+ * there can be no overflow. Negation (unary '-') works on unsigned types
+ * as well.
+ *
+ * The intN_t types are guaranteed to have width exactly N bits, with no
+ * padding bit, and using two's complement representation. Casting
+ * intN_t to uintN_t really is conversion modulo 2^N. Beware that intN_t
+ * types, being signed, trigger implementation-defined behaviour on
+ * overflow (including raising some signal): with GCC, while modular
+ * arithmetics are usually applied, the optimizer may assume that
+ * overflows don't occur (unless the -fwrapv command-line option is
+ * added); Clang has the additional -ftrapv option to explicitly trap on
+ * integer overflow or underflow.
+ */
+
+/*
+ * Negate a boolean.
+ */
+static inline uint32_t
+NOT(uint32_t ctl)
+{
+       return ctl ^ 1;
+}
+
+/*
+ * Multiplexer: returns x if ctl == 1, y if ctl == 0.
+ */
+static inline uint32_t
+MUX(uint32_t ctl, uint32_t x, uint32_t y)
+{
+       return y ^ (-ctl & (x ^ y));
+}
+
+/*
+ * Equality check: returns 1 if x == y, 0 otherwise.
+ */
+static inline uint32_t
+EQ(uint32_t x, uint32_t y)
+{
+       uint32_t q;
+
+       q = x ^ y;
+       return NOT((q | -q) >> 31);
+}
+
+/*
+ * Inequality check: returns 1 if x != y, 0 otherwise.
+ */
+static inline uint32_t
+NEQ(uint32_t x, uint32_t y)
+{
+       uint32_t q;
+
+       q = x ^ y;
+       return (q | -q) >> 31;
+}
+
+/*
+ * Comparison: returns 1 if x > y, 0 otherwise.
+ */
+static inline uint32_t
+GT(uint32_t x, uint32_t y)
+{
+       /*
+        * If both x < 2^31 and x < 2^31, then y-x will have its high
+        * bit set if x > y, cleared otherwise.
+        *
+        * If either x >= 2^31 or y >= 2^31 (but not both), then the
+        * result is the high bit of x.
+        *
+        * If both x >= 2^31 and y >= 2^31, then we can virtually
+        * subtract 2^31 from both, and we are back to the first case.
+        * Since (y-2^31)-(x-2^31) = y-x, the subtraction is already
+        * fine.
+        */
+       uint32_t z;
+
+       z = y - x;
+       return (z ^ ((x ^ y) & (x ^ z))) >> 31;
+}
+
+/*
+ * Other comparisons (greater-or-equal, lower-than, lower-or-equal).
+ */
+#define GE(x, y)   NOT(GT(y, x))
+#define LT(x, y)   GT(y, x)
+#define LE(x, y)   NOT(GT(x, y))
+
+/*
+ * General comparison: returned value is -1, 0 or 1, depending on
+ * whether x is lower than, equal to, or greater than y.
+ */
+static inline int32_t
+CMP(uint32_t x, uint32_t y)
+{
+       return (int32_t)GT(x, y) | -(int32_t)GT(y, x);
+}
+
+/*
+ * Returns 1 if x == 0, 0 otherwise. Take care that the operand is signed.
+ */
+static inline uint32_t
+EQ0(int32_t x)
+{
+       uint32_t q;
+
+       q = (uint32_t)x;
+       return ~(q | -q) >> 31;
+}
+
+/*
+ * Returns 1 if x > 0, 0 otherwise. Take care that the operand is signed.
+ */
+static inline uint32_t
+GT0(int32_t x)
+{
+       /*
+        * High bit of -x is 0 if x == 0, but 1 if x > 0.
+        */
+       uint32_t q;
+
+       q = (uint32_t)x;
+       return (~q & -q) >> 31;
+}
+
+/*
+ * Returns 1 if x >= 0, 0 otherwise. Take care that the operand is signed.
+ */
+static inline uint32_t
+GE0(int32_t x)
+{
+       return ~(uint32_t)x >> 31;
+}
+
+/*
+ * Returns 1 if x < 0, 0 otherwise. Take care that the operand is signed.
+ */
+static inline uint32_t
+LT0(int32_t x)
+{
+       return (uint32_t)x >> 31;
+}
+
+/*
+ * Returns 1 if x <= 0, 0 otherwise. Take care that the operand is signed.
+ */
+static inline uint32_t
+LE0(int32_t x)
+{
+       uint32_t q;
+
+       /*
+        * ~-x has its high bit set if and only if -x is nonnegative (as
+        * a signed int), i.e. x is in the -(2^31-1) to 0 range. We must
+        * do an OR with x itself to account for x = -2^31.
+        */
+       q = (uint32_t)x;
+       return (q | ~-q) >> 31;
+}
+
+/*
+ * Conditional copy: src[] is copied into dst[] if and only if ctl is 1.
+ * dst[] and src[] may overlap completely (but not partially).
+ */
+void br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len);
+
+#define CCOPY   br_ccopy
+
+/*
+ * Compute the bit length of a 32-bit integer. Returned value is between 0
+ * and 32 (inclusive).
+ */
+static inline uint32_t
+BIT_LENGTH(uint32_t x)
+{
+       uint32_t k, c;
+
+       k = NEQ(x, 0);
+       c = GT(x, 0xFFFF); x = MUX(c, x >> 16, x); k += c << 4;
+       c = GT(x, 0x00FF); x = MUX(c, x >>  8, x); k += c << 3;
+       c = GT(x, 0x000F); x = MUX(c, x >>  4, x); k += c << 2;
+       c = GT(x, 0x0003); x = MUX(c, x >>  2, x); k += c << 1;
+       k += GT(x, 0x0001);
+       return k;
+}
+
+/*
+ * Compute the minimum of x and y.
+ */
+static inline uint32_t
+MIN(uint32_t x, uint32_t y)
+{
+       return MUX(GT(x, y), y, x);
+}
+
+/*
+ * Compute the maximum of x and y.
+ */
+static inline uint32_t
+MAX(uint32_t x, uint32_t y)
+{
+       return MUX(GT(x, y), x, y);
+}
+
+/*
+ * Multiply two 32-bit integers, with a 64-bit result. This default
+ * implementation assumes that the basic multiplication operator
+ * yields constant-time code.
+ */
+#define MUL(x, y)   ((uint64_t)(x) * (uint64_t)(y))
+
+#if BR_CT_MUL31
+
+/*
+ * Alternate implementation of MUL31, that will be constant-time on some
+ * (old) platforms where the default MUL31 is not. Unfortunately, it is
+ * also substantially slower, and yields larger code, on more modern
+ * platforms, which is why it is deactivated by default.
+ */
+#define MUL31(x, y)   ((uint64_t)((x) | (uint32_t)0x80000000) \
+                       * (uint64_t)((y) | (uint32_t)0x80000000) \
+                       - ((uint64_t)(x) << 31) - ((uint64_t)(y) << 31) \
+                       - ((uint64_t)1 << 62))
+
+#else
+
+/*
+ * Multiply two 31-bit integers, with a 62-bit result. This default
+ * implementation assumes that the basic multiplication operator
+ * yields constant-time code.
+ */
+#define MUL31(x, y)   ((uint64_t)(x) * (uint64_t)(y))
+
+#endif
+
+/*
+ * Constant-time division. The dividend hi:lo is divided by the
+ * divisor d; the quotient is returned and the remainder is written
+ * in *r. If hi == d, then the quotient does not fit on 32 bits;
+ * returned value is thus truncated. If hi > d, returned values are
+ * indeterminate.
+ */
+uint32_t br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r);
+
+/*
+ * Wrapper for br_divrem(); the remainder is returned, and the quotient
+ * is discarded.
+ */
+static inline uint32_t
+br_rem(uint32_t hi, uint32_t lo, uint32_t d)
+{
+       uint32_t r;
+
+       br_divrem(hi, lo, d, &r);
+       return r;
+}
+
+/*
+ * Wrapper for br_divrem(); the quotient is returned, and the remainder
+ * is discarded.
+ */
+static inline uint32_t
+br_div(uint32_t hi, uint32_t lo, uint32_t d)
+{
+       uint32_t r;
+
+       return br_divrem(hi, lo, d, &r);
+}
+
+/* ==================================================================== */
+
+/*
+ * Integers 'i32'
+ * --------------
+ *
+ * The 'i32' functions implement computations on big integers using
+ * an internal representation as an array of 32-bit integers. For
+ * an array x[]:
+ *  -- x[0] contains the "announced bit length" of the integer
+ *  -- x[1], x[2]... contain the value in little-endian order (x[1]
+ *     contains the least significant 32 bits)
+ *
+ * Multiplications rely on the elementary 32x32->64 multiplication.
+ *
+ * The announced bit length specifies the number of bits that are
+ * significant in the subsequent 32-bit words. Unused bits in the
+ * last (most significant) word are set to 0; subsequent words are
+ * uninitialized and need not exist at all.
+ *
+ * The execution time and memory access patterns of all computations
+ * depend on the announced bit length, but not on the actual word
+ * values. For modular integers, the announced bit length of any integer
+ * modulo n is equal to the actual bit length of n; thus, computations
+ * on modular integers are "constant-time" (only the modulus length may
+ * leak).
+ */
+
+/*
+ * Compute the actual bit length of an integer. The argument x should
+ * point to the first (least significant) value word of the integer.
+ * The len 'xlen' contains the number of 32-bit words to access.
+ *
+ * CT: value or length of x does not leak.
+ */
+uint32_t br_i32_bit_length(uint32_t *x, size_t xlen);
+
+/*
+ * Decode an integer from its big-endian unsigned representation. The
+ * "true" bit length of the integer is computed, but all words of x[]
+ * corresponding to the full 'len' bytes of the source are set.
+ *
+ * CT: value or length of x does not leak.
+ */
+void br_i32_decode(uint32_t *x, const void *src, size_t len);
+
+/*
+ * Decode an integer from its big-endian unsigned representation. The
+ * integer MUST be lower than m[]; the announced bit length written in
+ * x[] will be equal to that of m[]. All 'len' bytes from the source are
+ * read.
+ *
+ * Returned value is 1 if the decode value fits within the modulus, 0
+ * otherwise. In the latter case, the x[] buffer will be set to 0 (but
+ * still with the announced bit length of m[]).
+ *
+ * CT: value or length of x does not leak. Memory access pattern depends
+ * only of 'len' and the announced bit length of m. Whether x fits or
+ * not does not leak either.
+ */
+uint32_t br_i32_decode_mod(uint32_t *x,
+       const void *src, size_t len, const uint32_t *m);
+
+/*
+ * Reduce an integer (a[]) modulo another (m[]). The result is written
+ * in x[] and its announced bit length is set to be equal to that of m[].
+ *
+ * x[] MUST be distinct from a[] and m[].
+ *
+ * CT: only announced bit lengths leak, not values of x, a or m.
+ */
+void br_i32_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m);
+
+/*
+ * Decode an integer from its big-endian unsigned representation, and
+ * reduce it modulo the provided modulus m[]. The announced bit length
+ * of the result is set to be equal to that of the modulus.
+ *
+ * x[] MUST be distinct from m[].
+ */
+void br_i32_decode_reduce(uint32_t *x,
+       const void *src, size_t len, const uint32_t *m);
+
+/*
+ * Encode an integer into its big-endian unsigned representation. The
+ * output length in bytes is provided (parameter 'len'); if the length
+ * is too short then the integer is appropriately truncated; if it is
+ * too long then the extra bytes are set to 0.
+ */
+void br_i32_encode(void *dst, size_t len, const uint32_t *x);
+
+/*
+ * Multiply x[] by 2^32 and then add integer z, modulo m[]. This
+ * function assumes that x[] and m[] have the same announced bit
+ * length, and the announced bit length of m[] matches its true
+ * bit length.
+ *
+ * x[] and m[] MUST be distinct arrays.
+ *
+ * CT: only the common announced bit length of x and m leaks, not
+ * the values of x, z or m.
+ */
+void br_i32_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m);
+
+/*
+ * Extract one word from an integer. The offset is counted in bits.
+ * The word MUST entirely fit within the word elements corresponding
+ * to the announced bit length of a[].
+ */
+static inline uint32_t
+br_i32_word(const uint32_t *a, uint32_t off)
+{
+       size_t u;
+       unsigned j;
+
+       u = (size_t)(off >> 5) + 1;
+       j = (unsigned)off & 31;
+       if (j == 0) {
+               return a[u];
+       } else {
+               return (a[u] >> j) | (a[u + 1] << (32 - j));
+       }
+}
+
+/*
+ * Test whether an integer is zero.
+ */
+uint32_t br_i32_iszero(const uint32_t *x);
+
+/*
+ * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[]
+ * is unmodified, but the carry is still computed and returned. The
+ * arrays a[] and b[] MUST have the same announced bit length.
+ *
+ * a[] and b[] MAY be the same array, but partial overlap is not allowed.
+ */
+uint32_t br_i32_add(uint32_t *a, const uint32_t *b, uint32_t ctl);
+
+/*
+ * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0,
+ * then a[] is unmodified, but the carry is still computed and returned.
+ * The arrays a[] and b[] MUST have the same announced bit length.
+ *
+ * a[] and b[] MAY be the same array, but partial overlap is not allowed.
+ */
+uint32_t br_i32_sub(uint32_t *a, const uint32_t *b, uint32_t ctl);
+
+/*
+ * Compute d+a*b, result in d. The initial announced bit length of d[]
+ * MUST match that of a[]. The d[] array MUST be large enough to
+ * accommodate the full result, plus (possibly) an extra word. The
+ * resulting announced bit length of d[] will be the sum of the announced
+ * bit lengths of a[] and b[] (therefore, it may be larger than the actual
+ * bit length of the numerical result).
+ *
+ * a[] and b[] may be the same array. d[] must be disjoint from both a[]
+ * and b[].
+ */
+void br_i32_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b);
+
+/*
+ * Zeroize an integer. The announced bit length is set to the provided
+ * value, and the corresponding words are set to 0.
+ */
+static inline void
+br_i32_zero(uint32_t *x, uint32_t bit_len)
+{
+       *x ++ = bit_len;
+       memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
+}
+
+/*
+ * Compute -(1/x) mod 2^32. If x is even, then this function returns 0.
+ */
+uint32_t br_i32_ninv32(uint32_t x);
+
+/*
+ * Convert a modular integer to Montgomery representation. The integer x[]
+ * MUST be lower than m[], but with the same announced bit length.
+ */
+void br_i32_to_monty(uint32_t *x, const uint32_t *m);
+
+/*
+ * Convert a modular integer back from Montgomery representation. The
+ * integer x[] MUST be lower than m[], but with the same announced bit
+ * length. The "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is
+ * the least significant value word of m[] (this works only if m[] is
+ * an odd integer).
+ */
+void br_i32_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i);
+
+/*
+ * Compute a modular Montgomery multiplication. d[] is filled with the
+ * value of x*y/R modulo m[] (where R is the Montgomery factor). The
+ * array d[] MUST be distinct from x[], y[] and m[]. x[] and y[] MUST be
+ * numerically lower than m[]. x[] and y[] MAY be the same array. The
+ * "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is the least
+ * significant value word of m[] (this works only if m[] is an odd
+ * integer).
+ */
+void br_i32_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
+       const uint32_t *m, uint32_t m0i);
+
+/*
+ * Compute a modular exponentiation. x[] MUST be an integer modulo m[]
+ * (same announced bit length, lower value). m[] MUST be odd. The
+ * exponent is in big-endian unsigned notation, over 'elen' bytes. The
+ * "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is the least
+ * significant value word of m[] (this works only if m[] is an odd
+ * integer). The t1[] and t2[] parameters must be temporary arrays,
+ * each large enough to accommodate an integer with the same size as m[].
+ */
+void br_i32_modpow(uint32_t *x, const unsigned char *e, size_t elen,
+       const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2);
+
+/* ==================================================================== */
+
+/*
+ * Integers 'i31'
+ * --------------
+ *
+ * The 'i31' functions implement computations on big integers using
+ * an internal representation as an array of 32-bit integers. For
+ * an array x[]:
+ *  -- x[0] encodes the array length and the "announced bit length"
+ *     of the integer: namely, if the announced bit length is k,
+ *     then x[0] = ((k / 31) << 5) + (k % 31).
+ *  -- x[1], x[2]... contain the value in little-endian order, 31
+ *     bits per word (x[1] contains the least significant 31 bits).
+ *     The upper bit of each word is 0.
+ *
+ * Multiplications rely on the elementary 32x32->64 multiplication.
+ *
+ * The announced bit length specifies the number of bits that are
+ * significant in the subsequent 32-bit words. Unused bits in the
+ * last (most significant) word are set to 0; subsequent words are
+ * uninitialized and need not exist at all.
+ *
+ * The execution time and memory access patterns of all computations
+ * depend on the announced bit length, but not on the actual word
+ * values. For modular integers, the announced bit length of any integer
+ * modulo n is equal to the actual bit length of n; thus, computations
+ * on modular integers are "constant-time" (only the modulus length may
+ * leak).
+ */
+
+/*
+ * Test whether an integer is zero.
+ */
+uint32_t br_i31_iszero(const uint32_t *x);
+
+/*
+ * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[]
+ * is unmodified, but the carry is still computed and returned. The
+ * arrays a[] and b[] MUST have the same announced bit length.
+ *
+ * a[] and b[] MAY be the same array, but partial overlap is not allowed.
+ */
+uint32_t br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl);
+
+/*
+ * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0,
+ * then a[] is unmodified, but the carry is still computed and returned.
+ * The arrays a[] and b[] MUST have the same announced bit length.
+ *
+ * a[] and b[] MAY be the same array, but partial overlap is not allowed.
+ */
+uint32_t br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl);
+
+/*
+ * Compute the ENCODED actual bit length of an integer. The argument x
+ * should point to the first (least significant) value word of the
+ * integer. The len 'xlen' contains the number of 32-bit words to
+ * access. The upper bit of each value word MUST be 0.
+ * Returned value is ((k / 31) << 5) + (k % 31) if the bit length is k.
+ *
+ * CT: value or length of x does not leak.
+ */
+uint32_t br_i31_bit_length(uint32_t *x, size_t xlen);
+
+/*
+ * Decode an integer from its big-endian unsigned representation. The
+ * "true" bit length of the integer is computed and set in the encoded
+ * announced bit length (x[0]), but all words of x[] corresponding to
+ * the full 'len' bytes of the source are set.
+ *
+ * CT: value or length of x does not leak.
+ */
+void br_i31_decode(uint32_t *x, const void *src, size_t len);
+
+/*
+ * Decode an integer from its big-endian unsigned representation. The
+ * integer MUST be lower than m[]; the (encoded) announced bit length
+ * written in x[] will be equal to that of m[]. All 'len' bytes from the
+ * source are read.
+ *
+ * Returned value is 1 if the decode value fits within the modulus, 0
+ * otherwise. In the latter case, the x[] buffer will be set to 0 (but
+ * still with the announced bit length of m[]).
+ *
+ * CT: value or length of x does not leak. Memory access pattern depends
+ * only of 'len' and the announced bit length of m. Whether x fits or
+ * not does not leak either.
+ */
+uint32_t br_i31_decode_mod(uint32_t *x,
+       const void *src, size_t len, const uint32_t *m);
+
+/*
+ * Zeroize an integer. The announced bit length is set to the provided
+ * value, and the corresponding words are set to 0. The ENCODED bit length
+ * is expected here.
+ */
+static inline void
+br_i31_zero(uint32_t *x, uint32_t bit_len)
+{
+       *x ++ = bit_len;
+       memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
+}
+
+/*
+ * Right-shift an integer. The shift amount must be lower than 31
+ * bits.
+ */
+void br_i31_rshift(uint32_t *x, int count);
+
+/*
+ * Reduce an integer (a[]) modulo another (m[]). The result is written
+ * in x[] and its announced bit length is set to be equal to that of m[].
+ *
+ * x[] MUST be distinct from a[] and m[].
+ *
+ * CT: only announced bit lengths leak, not values of x, a or m.
+ */
+void br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m);
+
+/*
+ * Decode an integer from its big-endian unsigned representation, and
+ * reduce it modulo the provided modulus m[]. The announced bit length
+ * of the result is set to be equal to that of the modulus.
+ *
+ * x[] MUST be distinct from m[].
+ */
+void br_i31_decode_reduce(uint32_t *x,
+       const void *src, size_t len, const uint32_t *m);
+
+/*
+ * Multiply x[] by 2^31 and then add integer z, modulo m[]. This
+ * function assumes that x[] and m[] have the same announced bit
+ * length, the announced bit length of m[] matches its true
+ * bit length.
+ *
+ * x[] and m[] MUST be distinct arrays. z MUST fit in 31 bits (upper
+ * bit set to 0).
+ *
+ * CT: only the common announced bit length of x and m leaks, not
+ * the values of x, z or m.
+ */
+void br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m);
+
+/*
+ * Encode an integer into its big-endian unsigned representation. The
+ * output length in bytes is provided (parameter 'len'); if the length
+ * is too short then the integer is appropriately truncated; if it is
+ * too long then the extra bytes are set to 0.
+ */
+void br_i31_encode(void *dst, size_t len, const uint32_t *x);
+
+/*
+ * Compute -(1/x) mod 2^31. If x is even, then this function returns 0.
+ */
+uint32_t br_i31_ninv31(uint32_t x);
+
+/*
+ * Compute a modular Montgomery multiplication. d[] is filled with the
+ * value of x*y/R modulo m[] (where R is the Montgomery factor). The
+ * array d[] MUST be distinct from x[], y[] and m[]. x[] and y[] MUST be
+ * numerically lower than m[]. x[] and y[] MAY be the same array. The
+ * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least
+ * significant value word of m[] (this works only if m[] is an odd
+ * integer).
+ */
+void br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
+       const uint32_t *m, uint32_t m0i);
+
+/*
+ * Convert a modular integer to Montgomery representation. The integer x[]
+ * MUST be lower than m[], but with the same announced bit length.
+ */
+void br_i31_to_monty(uint32_t *x, const uint32_t *m);
+
+/*
+ * Convert a modular integer back from Montgomery representation. The
+ * integer x[] MUST be lower than m[], but with the same announced bit
+ * length. The "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is
+ * the least significant value word of m[] (this works only if m[] is
+ * an odd integer).
+ */
+void br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i);
+
+/*
+ * Compute a modular exponentiation. x[] MUST be an integer modulo m[]
+ * (same announced bit length, lower value). m[] MUST be odd. The
+ * exponent is in big-endian unsigned notation, over 'elen' bytes. The
+ * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least
+ * significant value word of m[] (this works only if m[] is an odd
+ * integer). The t1[] and t2[] parameters must be temporary arrays,
+ * each large enough to accommodate an integer with the same size as m[].
+ */
+void br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
+       const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2);
+
+/*
+ * Compute d+a*b, result in d. The initial announced bit length of d[]
+ * MUST match that of a[]. The d[] array MUST be large enough to
+ * accommodate the full result, plus (possibly) an extra word. The
+ * resulting announced bit length of d[] will be the sum of the announced
+ * bit lengths of a[] and b[] (therefore, it may be larger than the actual
+ * bit length of the numerical result).
+ *
+ * a[] and b[] may be the same array. d[] must be disjoint from both a[]
+ * and b[].
+ */
+void br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b);
+
+/* ==================================================================== */
+
+static inline size_t
+br_digest_size(const br_hash_class *digest_class)
+{
+       return (size_t)(digest_class->desc >> BR_HASHDESC_OUT_OFF)
+               & BR_HASHDESC_OUT_MASK;
+}
+
+/*
+ * Get the output size (in bytes) of a hash function.
+ */
+size_t br_digest_size_by_ID(int digest_id);
+
+/*
+ * Get the OID (encoded OBJECT IDENTIFIER value, without tag and length)
+ * for a hash function. If digest_id is not a supported digest identifier
+ * (in particular if it is equal to 0, i.e. br_md5sha1_ID), then NULL is
+ * returned and *len is set to 0.
+ */
+const unsigned char *br_digest_OID(int digest_id, size_t *len);
+
+/* ==================================================================== */
+/*
+ * DES support functions.
+ */
+
+/*
+ * Apply DES Initial Permutation.
+ */
+void br_des_do_IP(uint32_t *xl, uint32_t *xr);
+
+/*
+ * Apply DES Final Permutation (inverse of IP).
+ */
+void br_des_do_invIP(uint32_t *xl, uint32_t *xr);
+
+/*
+ * Key schedule unit: for a DES key (8 bytes), compute 16 subkeys. Each
+ * subkey is two 28-bit words represented as two 32-bit words; the PC-2
+ * bit extration is NOT applied.
+ */
+void br_des_keysched_unit(uint32_t *skey, const void *key);
+
+/*
+ * Reversal of 16 DES sub-keys (for decryption).
+ */
+void br_des_rev_skey(uint32_t *skey);
+
+/*
+ * DES/3DES key schedule for 'des_tab' (encryption direction). Returned
+ * value is the number of rounds.
+ */
+unsigned br_des_tab_keysched(uint32_t *skey, const void *key, size_t key_len);
+
+/*
+ * DES/3DES key schedule for 'des_ct' (encryption direction). Returned
+ * value is the number of rounds.
+ */
+unsigned br_des_ct_keysched(uint32_t *skey, const void *key, size_t key_len);
+
+/*
+ * DES/3DES subkey decompression (from the compressed bitsliced subkeys).
+ */
+void br_des_ct_skey_expand(uint32_t *sk_exp,
+       unsigned num_rounds, const uint32_t *skey);
+
+/*
+ * DES/3DES block encryption/decryption ('des_tab').
+ */
+void br_des_tab_process_block(unsigned num_rounds,
+       const uint32_t *skey, void *block);
+
+/*
+ * DES/3DES block encryption/decryption ('des_ct').
+ */
+void br_des_ct_process_block(unsigned num_rounds,
+       const uint32_t *skey, void *block);
+
+/* ==================================================================== */
+/*
+ * AES support functions.
+ */
+
+/*
+ * The AES S-box (256-byte table).
+ */
+extern const unsigned char br_aes_S[];
+
+/*
+ * AES key schedule. skey[] is filled with n+1 128-bit subkeys, where n
+ * is the number of rounds (10 to 14, depending on key size). The number
+ * of rounds is returned. If the key size is invalid (not 16, 24 or 32),
+ * then 0 is returned.
+ *
+ * This implementation uses a 256-byte table and is NOT constant-time.
+ */
+unsigned br_aes_keysched(uint32_t *skey, const void *key, size_t key_len);
+
+/*
+ * AES key schedule for decryption ('aes_big' implementation).
+ */
+unsigned br_aes_big_keysched_inv(uint32_t *skey,
+       const void *key, size_t key_len);
+
+/*
+ * AES block encryption with the 'aes_big' implementation (fast, but
+ * not constant-time). This function encrypts a single block "in place".
+ */
+void br_aes_big_encrypt(unsigned num_rounds, const uint32_t *skey, void *data);
+
+/*
+ * AES block decryption with the 'aes_big' implementation (fast, but
+ * not constant-time). This function decrypts a single block "in place".
+ */
+void br_aes_big_decrypt(unsigned num_rounds, const uint32_t *skey, void *data);
+
+/*
+ * AES block encryption with the 'aes_small' implementation (small, but
+ * slow and not constant-time). This function encrypts a single block
+ * "in place".
+ */
+void br_aes_small_encrypt(unsigned num_rounds,
+       const uint32_t *skey, void *data);
+
+/*
+ * AES block decryption with the 'aes_small' implementation (small, but
+ * slow and not constant-time). This function decrypts a single block
+ * "in place".
+ */
+void br_aes_small_decrypt(unsigned num_rounds,
+       const uint32_t *skey, void *data);
+
+/*
+ * The constant-time implementation is "bitsliced": the 128-bit state is
+ * split over eight 32-bit words q* in the following way:
+ *
+ * -- Input block consists in 16 bytes:
+ *    a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33
+ * In the terminology of FIPS 197, this is a 4x4 matrix which is read
+ * column by column.
+ *
+ * -- Each byte is split into eight bits which are distributed over the
+ * eight words, at the same rank. Thus, for a byte x at rank k, bit 0
+ * (least significant) of x will be at rank k in q0 (if that bit is b,
+ * then it contributes "b << k" to the value of q0), bit 1 of x will be
+ * at rank k in q1, and so on.
+ *
+ * -- Ranks given to bits are in "row order" and are either all even, or
+ * all odd. Two independent AES states are thus interleaved, one using
+ * the even ranks, the other the odd ranks. Row order means:
+ *    a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23 a30 a31 a32 a33
+ *
+ * Converting input bytes from two AES blocks to bitslice representation
+ * is done in the following way:
+ * -- Decode first block into the four words q0 q2 q4 q6, in that order,
+ * using little-endian convention.
+ * -- Decode second block into the four words q1 q3 q5 q7, in that order,
+ * using little-endian convention.
+ * -- Call br_aes_ct_ortho().
+ *
+ * Converting back to bytes is done by using the reverse operations. Note
+ * that br_aes_ct_ortho() is its own inverse.
+ */
+
+/*
+ * Perform bytewise orthogonalization of eight 32-bit words. Bytes
+ * of q0..q7 are spread over all words: for a byte x that occurs
+ * at rank i in q[j] (byte x uses bits 8*i to 8*i+7 in q[j]), the bit
+ * of rank k in x (0 <= k <= 7) goes to q[k] at rank 8*i+j.
+ *
+ * This operation is an involution.
+ */
+void br_aes_ct_ortho(uint32_t *q);
+
+/*
+ * The AES S-box, as a bitsliced constant-time version. The input array
+ * consists in eight 32-bit words; 32 S-box instances are computed in
+ * parallel. Bits 0 to 7 of each S-box input (bit 0 is least significant)
+ * are spread over the words 0 to 7, at the same rank.
+ */
+void br_aes_ct_bitslice_Sbox(uint32_t *q);
+
+/*
+ * Like br_aes_bitslice_Sbox(), but for the inverse S-box.
+ */
+void br_aes_ct_bitslice_invSbox(uint32_t *q);
+
+/*
+ * Compute AES encryption on bitsliced data. Since input is stored on
+ * eight 32-bit words, two block encryptions are actually performed
+ * in parallel.
+ */
+void br_aes_ct_bitslice_encrypt(unsigned num_rounds,
+       const uint32_t *skey, uint32_t *q);
+
+/*
+ * Compute AES decryption on bitsliced data. Since input is stored on
+ * eight 32-bit words, two block decryptions are actually performed
+ * in parallel.
+ */
+void br_aes_ct_bitslice_decrypt(unsigned num_rounds,
+       const uint32_t *skey, uint32_t *q);
+
+/*
+ * AES key schedule, constant-time version. skey[] is filled with n+1
+ * 128-bit subkeys, where n is the number of rounds (10 to 14, depending
+ * on key size). The number of rounds is returned. If the key size is
+ * invalid (not 16, 24 or 32), then 0 is returned.
+ */
+unsigned br_aes_ct_keysched(uint32_t *comp_skey,
+       const void *key, size_t key_len);
+
+/*
+ * Expand AES subkeys as produced by br_aes_ct_keysched(), into
+ * a larger array suitable for br_aes_ct_bitslice_encrypt() and
+ * br_aes_ct_bitslice_decrypt().
+ */
+void br_aes_ct_skey_expand(uint32_t *skey,
+       unsigned num_rounds, const uint32_t *comp_skey);
+
+/*
+ * For the ct64 implementation, the same bitslicing technique is used,
+ * but four instances are interleaved. First instance uses bits 0, 4,
+ * 8, 12,... of each word; second instance uses bits 1, 5, 9, 13,...
+ * and so on.
+ */
+
+/*
+ * Perform bytewise orthogonalization of eight 64-bit words. Bytes
+ * of q0..q7 are spread over all words: for a byte x that occurs
+ * at rank i in q[j] (byte x uses bits 8*i to 8*i+7 in q[j]), the bit
+ * of rank k in x (0 <= k <= 7) goes to q[k] at rank 8*i+j.
+ *
+ * This operation is an involution.
+ */
+void br_aes_ct64_ortho(uint64_t *q);
+
+/*
+ * Interleave bytes for an AES input block. If input bytes are
+ * denoted 0123456789ABCDEF, and have been decoded with little-endian
+ * convention (w[0] contains 0123, with '3' being most significant;
+ * w[1] contains 4567, and so on), then output word q0 will be
+ * set to 08192A3B (again little-endian convention) and q1 will
+ * be set to 4C5D6E7F.
+ */
+void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w);
+
+/*
+ * Perform the opposite of br_aes_ct64_interleave_in().
+ */
+void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1);
+
+/*
+ * The AES S-box, as a bitsliced constant-time version. The input array
+ * consists in eight 64-bit words; 64 S-box instances are computed in
+ * parallel. Bits 0 to 7 of each S-box input (bit 0 is least significant)
+ * are spread over the words 0 to 7, at the same rank.
+ */
+void br_aes_ct64_bitslice_Sbox(uint64_t *q);
+
+/*
+ * Like br_aes_bitslice_Sbox(), but for the inverse S-box.
+ */
+void br_aes_ct64_bitslice_invSbox(uint64_t *q);
+
+/*
+ * Compute AES encryption on bitsliced data. Since input is stored on
+ * eight 64-bit words, four block encryptions are actually performed
+ * in parallel.
+ */
+void br_aes_ct64_bitslice_encrypt(unsigned num_rounds,
+       const uint64_t *skey, uint64_t *q);
+
+/*
+ * Compute AES decryption on bitsliced data. Since input is stored on
+ * eight 64-bit words, four block decryptions are actually performed
+ * in parallel.
+ */
+void br_aes_ct64_bitslice_decrypt(unsigned num_rounds,
+       const uint64_t *skey, uint64_t *q);
+
+/*
+ * AES key schedule, constant-time version. skey[] is filled with n+1
+ * 128-bit subkeys, where n is the number of rounds (10 to 14, depending
+ * on key size). The number of rounds is returned. If the key size is
+ * invalid (not 16, 24 or 32), then 0 is returned.
+ */
+unsigned br_aes_ct64_keysched(uint64_t *comp_skey,
+       const void *key, size_t key_len);
+
+/*
+ * Expand AES subkeys as produced by br_aes_ct64_keysched(), into
+ * a larger array suitable for br_aes_ct64_bitslice_encrypt() and
+ * br_aes_ct64_bitslice_decrypt().
+ */
+void br_aes_ct64_skey_expand(uint64_t *skey,
+       unsigned num_rounds, const uint64_t *comp_skey);
+
+/* ==================================================================== */
+/*
+ * Elliptic curves.
+ */
+
+/*
+ * Type for generic EC parameters: curve order (unsigned big-endian
+ * encoding) and encoded conventional generator.
+ */
+typedef struct {
+       int curve;
+       const unsigned char *order;
+       size_t order_len;
+       const unsigned char *generator;
+       size_t generator_len;
+} br_ec_curve_def;
+
+extern const br_ec_curve_def br_secp256r1;
+extern const br_ec_curve_def br_secp384r1;
+extern const br_ec_curve_def br_secp521r1;
+
+/*
+ * Type for the parameters for a "prime curve":
+ *   coordinates are in GF(p), with p prime
+ *   curve equation is Y^2 = X^3 - 3*X + b
+ *   b is in Montgomery representation
+ *   curve order is n and is prime
+ *   base point is G (encoded) and has order n
+ */
+typedef struct {
+       const uint32_t *p;
+       const uint32_t *b;
+       const uint32_t p0i;
+} br_ec_prime_i31_curve;
+
+extern const br_ec_prime_i31_curve br_ec_prime_i31_secp256r1;
+extern const br_ec_prime_i31_curve br_ec_prime_i31_secp384r1;
+extern const br_ec_prime_i31_curve br_ec_prime_i31_secp521r1;
+
+#define BR_EC_I31_LEN   ((BR_MAX_EC_SIZE + 61) / 31)
+
+/*
+ * Decode some bytes as an i31 integer, with truncation (corresponding
+ * to the 'bits2int' operation in RFC 6979). The target ENCODED bit
+ * length is provided as last parameter. The resulting value will have
+ * this declared bit length, and consists the big-endian unsigned decoding
+ * of exactly that many bits in the source (capped at the source length).
+ */
+void br_ecdsa_i31_bits2int(uint32_t *x,
+       const void *src, size_t len, uint32_t ebitlen);
+
+/* ==================================================================== */
+/*
+ * SSL/TLS support functions.
+ */
+
+/*
+ * Record types.
+ */
+#define BR_SSL_CHANGE_CIPHER_SPEC    20
+#define BR_SSL_ALERT                 21
+#define BR_SSL_HANDSHAKE             22
+#define BR_SSL_APPLICATION_DATA      23
+
+/*
+ * Handshake message types.
+ */
+#define BR_SSL_HELLO_REQUEST          0
+#define BR_SSL_CLIENT_HELLO           1
+#define BR_SSL_SERVER_HELLO           2
+#define BR_SSL_CERTIFICATE           11
+#define BR_SSL_SERVER_KEY_EXCHANGE   12
+#define BR_SSL_CERTIFICATE_REQUEST   13
+#define BR_SSL_SERVER_HELLO_DONE     14
+#define BR_SSL_CERTIFICATE_VERIFY    15
+#define BR_SSL_CLIENT_KEY_EXCHANGE   16
+#define BR_SSL_FINISHED              20
+
+/*
+ * Alert levels.
+ */
+#define BR_LEVEL_WARNING   1
+#define BR_LEVEL_FATAL     2
+
+/*
+ * Low-level I/O state.
+ */
+#define BR_IO_FAILED   0
+#define BR_IO_IN       1
+#define BR_IO_OUT      2
+#define BR_IO_INOUT    3
+
+/*
+ * Mark a SSL engine as failed. The provided error code is recorded if
+ * the engine was not already marked as failed. If 'err' is 0, then the
+ * engine is marked as closed (without error).
+ */
+void br_ssl_engine_fail(br_ssl_engine_context *cc, int err);
+
+/*
+ * Test whether the engine is closed (normally or as a failure).
+ */
+static inline int
+br_ssl_engine_closed(const br_ssl_engine_context *cc)
+{
+       return cc->iomode == BR_IO_FAILED;
+}
+
+/*
+ * Configure a new maximum fragment length. If possible, the maximum
+ * length for outgoing records is immediately adjusted (if there are
+ * not already too many buffered bytes for that).
+ */
+void br_ssl_engine_new_max_frag_len(
+       br_ssl_engine_context *rc, unsigned max_frag_len);
+
+/*
+ * Test whether the current incoming record has been fully received
+ * or not. This functions returns 0 only if a complete record header
+ * has been received, but some of the (possibly encrypted) payload
+ * has not yet been obtained.
+ */
+int br_ssl_engine_recvrec_finished(const br_ssl_engine_context *rc);
+
+/*
+ * Flush the current record (if not empty). This is meant to be called
+ * from the handshake processor only.
+ */
+void br_ssl_engine_flush_record(br_ssl_engine_context *cc);
+
+/*
+ * Test whether there is some accumulated payload to send.
+ */
+static inline int
+br_ssl_engine_has_pld_to_send(const br_ssl_engine_context *rc)
+{
+       return rc->oxa != rc->oxb && rc->oxa != rc->oxc;
+}
+
+/*
+ * Initialize RNG in engine. Returned value is 1 on success, 0 on error.
+ * This function will try to use the OS-provided RNG, if available. If
+ * there is no OS-provided RNG, or if it failed, and no entropy was
+ * injected by the caller, then a failure will be reported. On error,
+ * the context error code is set.
+ */
+int br_ssl_engine_init_rand(br_ssl_engine_context *cc);
+
+/*
+ * Reset the handshake-related parts of the engine.
+ */
+void br_ssl_engine_hs_reset(br_ssl_engine_context *cc,
+       void (*hsinit)(void *), void (*hsrun)(void *));
+
+/*
+ * Get the PRF to use for this context, for the provided PRF hash
+ * function ID.
+ */
+br_tls_prf_impl br_ssl_engine_get_PRF(br_ssl_engine_context *cc, int prf_id);
+
+/*
+ * Consume the provided pre-master secret and compute the corresponding
+ * master secret. The 'prf_id' is the ID of the hash function to use
+ * with the TLS 1.2 PRF (ignored if the version is TLS 1.0 or 1.1).
+ */
+void br_ssl_engine_compute_master(br_ssl_engine_context *cc,
+       int prf_id, const void *pms, size_t len);
+
+/*
+ * Switch to CBC decryption for incoming records.
+ *    cc               the engine context
+ *    is_client        non-zero for a client, zero for a server
+ *    prf_id           id of hash function for PRF (ignored if not TLS 1.2+)
+ *    mac_id           id of hash function for HMAC
+ *    bc_impl          block cipher implementation (CBC decryption)
+ *    cipher_key_len   block cipher key length (in bytes)
+ */
+void br_ssl_engine_switch_cbc_in(br_ssl_engine_context *cc,
+       int is_client, int prf_id, int mac_id,
+       const br_block_cbcdec_class *bc_impl, size_t cipher_key_len);
+
+/*
+ * Switch to CBC encryption for outgoing records.
+ *    cc               the engine context
+ *    is_client        non-zero for a client, zero for a server
+ *    prf_id           id of hash function for PRF (ignored if not TLS 1.2+)
+ *    mac_id           id of hash function for HMAC
+ *    bc_impl          block cipher implementation (CBC encryption)
+ *    cipher_key_len   block cipher key length (in bytes)
+ */
+void br_ssl_engine_switch_cbc_out(br_ssl_engine_context *cc,
+       int is_client, int prf_id, int mac_id,
+       const br_block_cbcenc_class *bc_impl, size_t cipher_key_len);
+
+/*
+ * Switch to GCM decryption for incoming records.
+ *    cc               the engine context
+ *    is_client        non-zero for a client, zero for a server
+ *    prf_id           id of hash function for PRF
+ *    bc_impl          block cipher implementation (CTR)
+ *    cipher_key_len   block cipher key length (in bytes)
+ */
+void br_ssl_engine_switch_gcm_in(br_ssl_engine_context *cc,
+       int is_client, int prf_id,
+       const br_block_ctr_class *bc_impl, size_t cipher_key_len);
+
+/*
+ * Switch to GCM encryption for outgoing records.
+ *    cc               the engine context
+ *    is_client        non-zero for a client, zero for a server
+ *    prf_id           id of hash function for PRF
+ *    bc_impl          block cipher implementation (CTR)
+ *    cipher_key_len   block cipher key length (in bytes)
+ */
+void br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc,
+       int is_client, int prf_id,
+       const br_block_ctr_class *bc_impl, size_t cipher_key_len);
+
+/*
+ * Calls to T0-generated code.
+ */
+void br_ssl_hs_client_init_main(void *ctx);
+void br_ssl_hs_client_run(void *ctx);
+void br_ssl_hs_server_init_main(void *ctx);
+void br_ssl_hs_server_run(void *ctx);
+
+/*
+ * Get the hash function to use for signatures, given a bit mask of
+ * supported hash functions. This implements a strict choice order
+ * (namely SHA-256, SHA-384, SHA-512, SHA-224, SHA-1). If the mask
+ * does not document support of any of these hash functions, then this
+ * functions returns 0.
+ */
+int br_ssl_choose_hash(unsigned bf);
+
+/* ==================================================================== */
+
+#endif
diff --git a/src/int/i31_add.c b/src/int/i31_add.c
new file mode 100644 (file)
index 0000000..2ca47c6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl)
+{
+       uint32_t cc;
+       size_t u, m;
+
+       cc = 0;
+       m = (a[0] + 63) >> 5;
+       for (u = 1; u < m; u ++) {
+               uint32_t aw, bw, naw;
+
+               aw = a[u];
+               bw = b[u];
+               naw = aw + bw + cc;
+               cc = naw >> 31;
+               a[u] = MUX(ctl, naw & (uint32_t)0x7FFFFFFF, aw);
+       }
+       return cc;
+}
diff --git a/src/int/i31_bitlen.c b/src/int/i31_bitlen.c
new file mode 100644 (file)
index 0000000..3e127c2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i31_bit_length(uint32_t *x, size_t xlen)
+{
+       uint32_t tw, twk;
+
+       tw = 0;
+       twk = 0;
+       while (xlen -- > 0) {
+               uint32_t w, c;
+
+               c = EQ(tw, 0);
+               w = x[xlen];
+               tw = MUX(c, w, tw);
+               twk = MUX(c, (uint32_t)xlen, twk);
+       }
+       return (twk << 5) + BIT_LENGTH(tw);
+}
diff --git a/src/int/i31_decmod.c b/src/int/i31_decmod.c
new file mode 100644 (file)
index 0000000..745bc10
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i31_decode_mod(uint32_t *x, const void *src, size_t len, const uint32_t *m)
+{
+       /*
+        * Two-pass algorithm: in the first pass, we determine whether the
+        * value fits; in the second pass, we do the actual write.
+        *
+        * During the first pass, 'r' contains the comparison result so
+        * far:
+        *  0x00000000   value is equal to the modulus
+        *  0x00000001   value is greater than the modulus
+        *  0xFFFFFFFF   value is lower than the modulus
+        *
+        * Since we iterate starting with the least significant bytes (at
+        * the end of src[]), each new comparison overrides the previous
+        * except when the comparison yields 0 (equal).
+        *
+        * During the second pass, 'r' is either 0xFFFFFFFF (value fits)
+        * or 0x00000000 (value does not fit).
+        *
+        * We must iterate over all bytes of the source, _and_ possibly
+        * some extra virutal bytes (with value 0) so as to cover the
+        * complete modulus as well. We also add 4 such extra bytes beyond
+        * the modulus length because it then guarantees that no accumulated
+        * partial word remains to be processed.
+        */
+       const unsigned char *buf;
+       size_t mlen, tlen;
+       int pass;
+       uint32_t r;
+
+       buf = src;
+       mlen = (m[0] + 31) >> 5;
+       tlen = (mlen << 2);
+       if (tlen < len) {
+               tlen = len;
+       }
+       tlen += 4;
+       r = 0;
+       for (pass = 0; pass < 2; pass ++) {
+               size_t u, v;
+               uint32_t acc;
+               int acc_len;
+
+               v = 1;
+               acc = 0;
+               acc_len = 0;
+               for (u = 0; u < tlen; u ++) {
+                       uint32_t b;
+
+                       if (u < len) {
+                               b = buf[len - 1 - u];
+                       } else {
+                               b = 0;
+                       }
+                       acc |= (b << acc_len);
+                       acc_len += 8;
+                       if (acc_len >= 31) {
+                               uint32_t xw;
+
+                               xw = acc & (uint32_t)0x7FFFFFFF;
+                               acc_len -= 31;
+                               acc = b >> (8 - acc_len);
+                               if (v <= mlen) {
+                                       if (pass) {
+                                               x[v] = r & xw;
+                                       } else {
+                                               uint32_t cc;
+
+                                               cc = (uint32_t)CMP(xw, m[v]);
+                                               r = MUX(EQ(cc, 0), r, cc);
+                                       }
+                               } else {
+                                       if (!pass) {
+                                               r = MUX(EQ(xw, 0), r, 1);
+                                       }
+                               }
+                               v ++;
+                       }
+               }
+
+               /*
+                * When we reach this point at the end of the first pass:
+                * r is either 0, 1 or -1; we want to set r to 0 if it
+                * is equal to 0 or 1, and leave it to -1 otherwise.
+                *
+                * When we reach this point at the end of the second pass:
+                * r is either 0 or -1; we want to leave that value
+                * untouched. This is a subcase of the previous.
+                */
+               r >>= 1;
+               r |= (r << 1);
+       }
+
+       x[0] = m[0];
+       return r & (uint32_t)1;
+}
diff --git a/src/int/i31_decode.c b/src/int/i31_decode.c
new file mode 100644 (file)
index 0000000..8ec6d90
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_decode(uint32_t *x, const void *src, size_t len)
+{
+       const unsigned char *buf;
+       size_t u, v;
+       uint32_t acc;
+       int acc_len;
+
+       buf = src;
+       u = len;
+       v = 1;
+       acc = 0;
+       acc_len = 0;
+       while (u -- > 0) {
+               uint32_t b;
+
+               b = buf[u];
+               acc |= (b << acc_len);
+               acc_len += 8;
+               if (acc_len >= 31) {
+                       x[v ++] = acc & (uint32_t)0x7FFFFFFF;
+                       acc_len -= 31;
+                       acc = b >> (8 - acc_len);
+               }
+       }
+       if (acc_len != 0) {
+               x[v ++] = acc;
+       }
+       x[0] = br_i31_bit_length(x + 1, v - 1);
+}
diff --git a/src/int/i31_decred.c b/src/int/i31_decred.c
new file mode 100644 (file)
index 0000000..43db662
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_decode_reduce(uint32_t *x,
+       const void *src, size_t len, const uint32_t *m)
+{
+       uint32_t m_ebitlen, m_rbitlen;
+       size_t mblen, k;
+       const unsigned char *buf;
+       uint32_t acc;
+       int acc_len;
+
+       /*
+        * Get the encoded bit length.
+        */
+       m_ebitlen = m[0];
+
+       /*
+        * Special case for an invalid (null) modulus.
+        */
+       if (m_ebitlen == 0) {
+               x[0] = 0;
+               return;
+       }
+
+       /*
+        * Clear the destination.
+        */
+       br_i31_zero(x, m_ebitlen);
+
+       /*
+        * First decode directly as many bytes as possible. This requires
+        * computing the actual bit length.
+        */
+       m_rbitlen = m_ebitlen >> 5;
+       m_rbitlen = (m_ebitlen & 31) + (m_rbitlen << 5) - m_rbitlen;
+       mblen = (m_rbitlen + 7) >> 3;
+       k = mblen - 1;
+       if (k >= len) {
+               br_i31_decode(x, src, len);
+               x[0] = m_ebitlen;
+               return;
+       }
+       buf = src;
+       br_i31_decode(x, buf, k);
+       x[0] = m_ebitlen;
+
+       /*
+        * Input remaining bytes, using 31-bit words.
+        */
+       acc = 0;
+       acc_len = 0;
+       while (k < len) {
+               uint32_t v;
+
+               v = buf[k ++];
+               if (acc_len >= 23) {
+                       acc_len -= 23;
+                       acc <<= (8 - acc_len);
+                       acc |= v >> acc_len;
+                       br_i31_muladd_small(x, acc, m);
+                       acc = v & (0xFF >> (8 - acc_len));
+               } else {
+                       acc = (acc << 8) | v;
+                       acc_len += 8;
+               }
+       }
+
+       /*
+        * We may have some bits accumulated. We then perform a shift to
+        * be able to inject these bits as a full 31-bit word.
+        */
+       if (acc_len != 0) {
+               acc = (acc | (x[1] << acc_len)) & 0x7FFFFFFF;
+               br_i31_rshift(x, 31 - acc_len);
+               br_i31_muladd_small(x, acc, m);
+       }
+}
diff --git a/src/int/i31_encode.c b/src/int/i31_encode.c
new file mode 100644 (file)
index 0000000..b6b40c4
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_encode(void *dst, size_t len, const uint32_t *x)
+{
+       unsigned char *buf;
+       size_t k, xlen;
+       uint32_t acc;
+       int acc_len;
+
+       xlen = (x[0] + 31) >> 5;
+       if (xlen == 0) {
+               memset(dst, 0, len);
+               return;
+       }
+       buf = (unsigned char *)dst + len;
+       k = 1;
+       acc = 0;
+       acc_len = 0;
+       while (len != 0) {
+               uint32_t w;
+
+               w = (k <= xlen) ? x[k] : 0;
+               k ++;
+               if (acc_len == 0) {
+                       acc = w;
+                       acc_len = 31;
+               } else {
+                       uint32_t z;
+
+                       z = acc | (w << acc_len);
+                       acc_len --;
+                       acc = w >> (31 - acc_len);
+                       if (len >= 4) {
+                               buf -= 4;
+                               len -= 4;
+                               br_enc32be(buf, z);
+                       } else {
+                               switch (len) {
+                               case 3:
+                                       buf[-3] = (unsigned char)(z >> 16);
+                                       /* fall through */
+                               case 2:
+                                       buf[-2] = (unsigned char)(z >> 8);
+                                       /* fall through */
+                               case 1:
+                                       buf[-1] = (unsigned char)z;
+                                       break;
+                               }
+                               return;
+                       }
+               }
+       }
+}
diff --git a/src/int/i31_fmont.c b/src/int/i31_fmont.c
new file mode 100644 (file)
index 0000000..4e14361
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i)
+{
+       size_t len, u, v;
+
+       len = (m[0] + 31) >> 5;
+       for (u = 0; u < len; u ++) {
+               uint32_t f;
+               uint64_t cc;
+
+               f = (x[1] * m0i) & 0x7FFFFFFF;
+               cc = 0;
+               for (v = 0; v < len; v ++) {
+                       uint64_t z;
+
+                       z = (uint64_t)x[v + 1] + MUL31(f, m[v + 1]) + cc;
+                       cc = z >> 31;
+                       if (v != 0) {
+                               x[v] = (uint32_t)z & 0x7FFFFFFF;
+                       }
+               }
+               x[len] = (uint32_t)cc;
+       }
+
+       /*
+        * We may have to do an extra subtraction, but only if the
+        * value in x[] is indeed greater than or equal to that of m[],
+        * which is why we must do two calls (first call computes the
+        * carry, second call performs the subtraction only if the carry
+        * is 0).
+        */
+       br_i31_sub(x, m, NOT(br_i31_sub(x, m, 0)));
+}
diff --git a/src/int/i31_iszero.c b/src/int/i31_iszero.c
new file mode 100644 (file)
index 0000000..8a7ea44
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i31_iszero(const uint32_t *x)
+{
+       uint32_t z;
+       size_t u;
+
+       z = 0;
+       for (u = (x[0] + 31) >> 5; u > 0; u --) {
+               z |= x[u];
+       }
+       return ~(z | -z) >> 31;
+}
diff --git a/src/int/i31_modpow.c b/src/int/i31_modpow.c
new file mode 100644 (file)
index 0000000..4ef3f5d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_modpow(uint32_t *x,
+       const unsigned char *e, size_t elen,
+       const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2)
+{
+       size_t mlen;
+       uint32_t k;
+
+       /*
+        * 'mlen' is the length of m[] expressed in bytes (including
+        * the "bit length" first field).
+        */
+       mlen = ((m[0] + 63) >> 5) * sizeof m[0];
+
+       /*
+        * Throughout the algorithm:
+        * -- t1[] is in Montgomery representation; it contains x, x^2,
+        * x^4, x^8...
+        * -- The result is accumulated, in normal representation, in
+        * the x[] array.
+        * -- t2[] is used as destination buffer for each multiplication.
+        *
+        * Note that there is no need to call br_i32_from_monty().
+        */
+       memcpy(t1, x, mlen);
+       br_i31_to_monty(t1, m);
+       br_i31_zero(x, m[0]);
+       x[1] = 1;
+       for (k = 0; k < ((uint32_t)elen << 3); k ++) {
+               uint32_t ctl;
+
+               ctl = (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1;
+               br_i31_montymul(t2, x, t1, m, m0i);
+               CCOPY(ctl, x, t2, mlen);
+               br_i31_montymul(t2, t1, t1, m, m0i);
+               memcpy(t1, t2, mlen);
+       }
+}
diff --git a/src/int/i31_montmul.c b/src/int/i31_montmul.c
new file mode 100644 (file)
index 0000000..0857797
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
+       const uint32_t *m, uint32_t m0i)
+{
+       size_t len, len4, u, v;
+       uint64_t dh;
+
+       len = (m[0] + 31) >> 5;
+       len4 = len & ~(size_t)3;
+       br_i32_zero(d, m[0]);
+       dh = 0;
+       for (u = 0; u < len; u ++) {
+               uint32_t f, xu;
+               uint64_t r, zh;
+
+               xu = x[u + 1];
+               f = ((d[1] + x[u + 1] * y[1]) * m0i) & 0x7FFFFFFF;
+
+               r = 0;
+               for (v = 0; v < len4; v += 4) {
+                       uint64_t z;
+
+                       z = (uint64_t)d[v + 1] + MUL31(xu, y[v + 1])
+                               + MUL31(f, m[v + 1]) + r;
+                       r = z >> 31;
+                       d[v + 0] = (uint32_t)z & 0x7FFFFFFF;
+                       z = (uint64_t)d[v + 2] + MUL31(xu, y[v + 2])
+                               + MUL31(f, m[v + 2]) + r;
+                       r = z >> 31;
+                       d[v + 1] = (uint32_t)z & 0x7FFFFFFF;
+                       z = (uint64_t)d[v + 3] + MUL31(xu, y[v + 3])
+                               + MUL31(f, m[v + 3]) + r;
+                       r = z >> 31;
+                       d[v + 2] = (uint32_t)z & 0x7FFFFFFF;
+                       z = (uint64_t)d[v + 4] + MUL31(xu, y[v + 4])
+                               + MUL31(f, m[v + 4]) + r;
+                       r = z >> 31;
+                       d[v + 3] = (uint32_t)z & 0x7FFFFFFF;
+               }
+               for (; v < len; v ++) {
+                       uint64_t z;
+
+                       z = (uint64_t)d[v + 1] + MUL31(xu, y[v + 1])
+                               + MUL31(f, m[v + 1]) + r;
+                       r = z >> 31;
+                       d[v] = (uint32_t)z & 0x7FFFFFFF;
+               }
+
+               zh = dh + r;
+               d[len] = (uint32_t)zh & 0x7FFFFFFF;
+               dh = zh >> 31;
+       }
+
+       /*
+        * We must write back the bit length because it was overwritten in
+        * the loop (not overwriting it would require a test in the loop,
+        * which would yield bigger and slower code).
+        */
+       d[0] = m[0];
+
+       /*
+        * d[] may still be greater than m[] at that point; notably, the
+        * 'dh' word may be non-zero.
+        */
+       br_i31_sub(d, m, NEQ(dh, 0) | NOT(br_i31_sub(d, m, 0)));
+}
diff --git a/src/int/i31_mulacc.c b/src/int/i31_mulacc.c
new file mode 100644 (file)
index 0000000..04a42c7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b)
+{
+       size_t alen, blen, u;
+
+       alen = (a[0] + 31) >> 5;
+       blen = (b[0] + 31) >> 5;
+       d[0] = a[0] + b[0];
+       for (u = 0; u < blen; u ++) {
+               uint32_t f;
+               size_t v;
+               uint64_t cc;
+
+               f = b[1 + u];
+               cc = 0;
+               for (v = 0; v < alen; v ++) {
+                       uint64_t z;
+
+                       z = (uint64_t)d[1 + u + v] + MUL31(f, a[1 + v]) + cc;
+                       cc = z >> 31;
+                       d[1 + u + v] = (uint32_t)z & 0x7FFFFFFF;
+               }
+               d[1 + u + alen] = (uint32_t)cc;
+       }
+}
diff --git a/src/int/i31_muladd.c b/src/int/i31_muladd.c
new file mode 100644 (file)
index 0000000..3c52077
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
+{
+       uint32_t m_bitlen;
+       unsigned mblr;
+       size_t u, mlen;
+       uint32_t a0, a1, b0, hi, g, q, tb;
+       uint32_t under, over;
+       uint32_t cc;
+
+       /*
+        * We can test on the modulus bit length since we accept to
+        * leak that length.
+        */
+       m_bitlen = m[0];
+       if (m_bitlen == 0) {
+               return;
+       }
+       if (m_bitlen <= 31) {
+               uint32_t hi, lo;
+
+               hi = x[1] >> 1;
+               lo = (x[1] << 31) | z;
+               x[1] = br_rem(hi, lo, m[1]);
+               return;
+       }
+       mlen = (m_bitlen + 31) >> 5;
+       mblr = (unsigned)m_bitlen & 31;
+
+       /*
+        * Principle: we estimate the quotient (x*2^31+z)/m by
+        * doing a 64/32 division with the high words.
+        *
+        * Let:
+        *   w = 2^31
+        *   a = (w*a0 + a1) * w^N + a2
+        *   b = b0 * w^N + b2
+        * such that:
+        *   0 <= a0 < w
+        *   0 <= a1 < w
+        *   0 <= a2 < w^N
+        *   w/2 <= b0 < w
+        *   0 <= b2 < w^N
+        *   a < w*b
+        * I.e. the two top words of a are a0:a1, the top word of b is
+        * b0, we ensured that b0 is "full" (high bit set), and a is
+        * such that the quotient q = a/b fits on one word (0 <= q < w).
+        *
+        * If a = b*q + r (with 0 <= r < q), we can estimate q by
+        * doing an Euclidean division on the top words:
+        *   a0*w+a1 = b0*u + v  (with 0 <= v < w)
+        * Then the following holds:
+        *   0 <= u <= w
+        *   u-2 <= q <= u
+        */
+       hi = x[mlen];
+       if (mblr == 0) {
+               a0 = x[mlen];
+               memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
+               x[1] = z;
+               a1 = x[mlen];
+               b0 = m[mlen];
+       } else {
+               a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
+                       & 0x7FFFFFFF;
+               memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
+               x[1] = z;
+               a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
+                       & 0x7FFFFFFF;
+               b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr))
+                       & 0x7FFFFFFF;
+       }
+
+       /*
+        * We estimate a divisor q. If the quotient returned by br_div()
+        * is g:
+        * -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF.
+        * -- Otherwise:
+        *    -- if g == 0 then we set q = 0;
+        *    -- otherwise, we set q = g - 1.
+        * The properties described above then ensure that the true
+        * quotient is q-1, q or q+1.
+        *
+        * Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We
+        * must adjust the parameters to br_div() accordingly.
+        */
+       g = br_div(a0 >> 1, a1 | (a0 << 31), b0);
+       q = MUX(EQ(a0, b0), 0x7FFFFFFF, MUX(EQ(g, 0), 0, g - 1));
+
+       /*
+        * We subtract q*m from x (with the extra high word of value 'hi').
+        * Since q may be off by 1 (in either direction), we may have to
+        * add or subtract m afterwards.
+        *
+        * The 'tb' flag will be true (1) at the end of the loop if the
+        * result is greater than or equal to the modulus (not counting
+        * 'hi' or the carry).
+        */
+       cc = 0;
+       tb = 1;
+       for (u = 1; u <= mlen; u ++) {
+               uint32_t mw, zw, xw, nxw;
+               uint64_t zl;
+
+               mw = m[u];
+               zl = MUL31(mw, q) + cc;
+               cc = (uint32_t)(zl >> 31);
+               zw = (uint32_t)zl & (uint32_t)0x7FFFFFFF;
+               xw = x[u];
+               nxw = xw - zw;
+               cc += nxw >> 31;
+               nxw &= 0x7FFFFFFF;
+               x[u] = nxw;
+               tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw));
+       }
+
+       /*
+        * If we underestimated q, then either cc < hi (one extra bit
+        * beyond the top array word), or cc == hi and tb is true (no
+        * extra bit, but the result is not lower than the modulus). In
+        * these cases we must subtract m once.
+        *
+        * Otherwise, we may have overestimated, which will show as
+        * cc > hi (thus a negative result). Correction is adding m once.
+        */
+       over = GT(cc, hi);
+       under = ~over & (tb | LT(cc, hi));
+       br_i31_add(x, m, over);
+       br_i31_sub(x, m, under);
+}
diff --git a/src/int/i31_ninv31.c b/src/int/i31_ninv31.c
new file mode 100644 (file)
index 0000000..dd83c96
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i31_ninv31(uint32_t x)
+{
+       uint32_t y;
+
+       y = 2 - x;
+       y *= 2 - y * x;
+       y *= 2 - y * x;
+       y *= 2 - y * x;
+       y *= 2 - y * x;
+       return MUX(x & 1, -y, 0) & 0x7FFFFFFF;
+}
diff --git a/src/int/i31_reduce.c b/src/int/i31_reduce.c
new file mode 100644 (file)
index 0000000..5c9523e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m)
+{
+       uint32_t m_bitlen, a_bitlen;
+       size_t mlen, alen, u;
+
+       m_bitlen = m[0];
+       mlen = (m_bitlen + 31) >> 5;
+
+       x[0] = m_bitlen;
+       if (m_bitlen == 0) {
+               return;
+       }
+
+       /*
+        * If the source is shorter, then simply copy all words from a[]
+        * and zero out the upper words.
+        */
+       a_bitlen = a[0];
+       alen = (a_bitlen + 31) >> 5;
+       if (a_bitlen < m_bitlen) {
+               memcpy(x + 1, a + 1, alen * sizeof *a);
+               for (u = alen; u < mlen; u ++) {
+                       x[u + 1] = 0;
+               }
+               return;
+       }
+
+       /*
+        * The source length is at least equal to that of the modulus.
+        * We must thus copy N-1 words, and input the remaining words
+        * one by one.
+        */
+       memcpy(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a);
+       x[mlen] = 0;
+       for (u = 1 + alen - mlen; u > 0; u --) {
+               br_i31_muladd_small(x, a[u], m);
+       }
+}
diff --git a/src/int/i31_rshift.c b/src/int/i31_rshift.c
new file mode 100644 (file)
index 0000000..db6ba0b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_rshift(uint32_t *x, int count)
+{
+       size_t u, len;
+       uint32_t r;
+
+       len = (x[0] + 31) >> 5;
+       if (len == 0) {
+               return;
+       }
+       r = x[1] >> count;
+       for (u = 2; u <= len; u ++) {
+               uint32_t w;
+
+               w = x[u];
+               x[u - 1] = ((w << (31 - count)) | r) & 0x7FFFFFFF;
+               r = w >> count;
+       }
+       x[len] = r;
+}
diff --git a/src/int/i31_sub.c b/src/int/i31_sub.c
new file mode 100644 (file)
index 0000000..3910895
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl)
+{
+       uint32_t cc;
+       size_t u, m;
+
+       cc = 0;
+       m = (a[0] + 63) >> 5;
+       for (u = 1; u < m; u ++) {
+               uint32_t aw, bw, naw;
+
+               aw = a[u];
+               bw = b[u];
+               naw = aw - bw - cc;
+               cc = naw >> 31;
+               a[u] = MUX(ctl, naw & 0x7FFFFFFF, aw);
+       }
+       return cc;
+}
diff --git a/src/int/i31_tmont.c b/src/int/i31_tmont.c
new file mode 100644 (file)
index 0000000..4798ff6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i31_to_monty(uint32_t *x, const uint32_t *m)
+{
+       uint32_t k;
+
+       for (k = (m[0] + 31) >> 5; k > 0; k --) {
+               br_i31_muladd_small(x, 0, m);
+       }
+}
diff --git a/src/int/i32_add.c b/src/int/i32_add.c
new file mode 100644 (file)
index 0000000..620baff
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i32_add(uint32_t *a, const uint32_t *b, uint32_t ctl)
+{
+       uint32_t cc;
+       size_t u, m;
+
+       cc = 0;
+       m = (a[0] + 63) >> 5;
+       for (u = 1; u < m; u ++) {
+               uint32_t aw, bw, naw;
+
+               aw = a[u];
+               bw = b[u];
+               naw = aw + bw + cc;
+
+               /*
+                * Carry is 1 if naw < aw. Carry is also 1 if naw == aw
+                * AND the carry was already 1.
+                */
+               cc = (cc & EQ(naw, aw)) | LT(naw, aw);
+               a[u] = MUX(ctl, naw, aw);
+       }
+       return cc;
+}
diff --git a/src/int/i32_bitlen.c b/src/int/i32_bitlen.c
new file mode 100644 (file)
index 0000000..40ce9fa
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i32_bit_length(uint32_t *x, size_t xlen)
+{
+       uint32_t tw, twk;
+
+       tw = 0;
+       twk = 0;
+       while (xlen -- > 0) {
+               uint32_t w, c;
+
+               c = EQ(tw, 0);
+               w = x[xlen];
+               tw = MUX(c, w, tw);
+               twk = MUX(c, (uint32_t)xlen, twk);
+       }
+       return (twk << 5) + BIT_LENGTH(tw);
+}
diff --git a/src/int/i32_decmod.c b/src/int/i32_decmod.c
new file mode 100644 (file)
index 0000000..a859af1
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i32_decode_mod(uint32_t *x, const void *src, size_t len, const uint32_t *m)
+{
+       const unsigned char *buf;
+       uint32_t r;
+       size_t u, v, mlen;
+
+       buf = src;
+
+       /*
+        * First pass: determine whether the value fits. The 'r' value
+        * will contain the comparison result, as 0x00000000 (value is
+        * equal to the modulus), 0x00000001 (value is greater than the
+        * modulus), or 0xFFFFFFFF (value is lower than the modulus).
+        */
+       mlen = (m[0] + 7) >> 3;
+       r = 0;
+       for (u = (mlen > len) ? mlen : len; u > 0; u --) {
+               uint32_t mb, xb;
+
+               v = u - 1;
+               if (v >= mlen) {
+                       mb = 0;
+               } else {
+                       mb = (m[1 + (v >> 2)] >> ((v & 3) << 3)) & 0xFF;
+               }
+               if (v >= len) {
+                       xb = 0;
+               } else {
+                       xb = buf[len - u];
+               }
+               r = MUX(EQ(r, 0), (uint32_t)CMP(xb, mb), r);
+       }
+
+       /*
+        * Only r == 0xFFFFFFFF is acceptable. We want to set r to 0xFF if
+        * the value fits, 0x00 otherwise.
+        */
+       r >>= 24;
+       br_i32_zero(x, m[0]);
+       u = (mlen > len) ? len : mlen;
+       while (u > 0) {
+               uint32_t xb;
+
+               xb = buf[len - u] & r;
+               u --;
+               x[1 + (u >> 2)] |= xb << ((u & 3) << 3);
+       }
+       return r >> 7;
+}
diff --git a/src/int/i32_decode.c b/src/int/i32_decode.c
new file mode 100644 (file)
index 0000000..f289038
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_decode(uint32_t *x, const void *src, size_t len)
+{
+       const unsigned char *buf;
+       size_t u, v;
+
+       buf = src;
+       u = len;
+       v = 1;
+       for (;;) {
+               if (u < 4) {
+                       uint32_t w;
+
+                       if (u < 2) {
+                               if (u == 0) {
+                                       break;
+                               } else {
+                                       w = buf[0];
+                               }
+                       } else {
+                               if (u == 2) {
+                                       w = br_dec16be(buf);
+                               } else {
+                                       w = ((uint32_t)buf[0] << 16)
+                                               | br_dec16be(buf + 1);
+                               }
+                       }
+                       x[v ++] = w;
+                       break;
+               } else {
+                       u -= 4;
+                       x[v ++] = br_dec32be(buf + u);
+               }
+       }
+       x[0] = br_i32_bit_length(x + 1, v - 1);
+}
diff --git a/src/int/i32_decred.c b/src/int/i32_decred.c
new file mode 100644 (file)
index 0000000..dc476db
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_decode_reduce(uint32_t *x,
+       const void *src, size_t len, const uint32_t *m)
+{
+       uint32_t m_bitlen;
+       size_t mblen, k, q;
+       const unsigned char *buf;
+
+       m_bitlen = m[0];
+
+       /*
+        * Special case for an invalid modulus.
+        */
+       if (m_bitlen == 0) {
+               x[0] = 0;
+               return;
+       }
+
+       /*
+        * Clear the destination.
+        */
+       br_i32_zero(x, m_bitlen);
+
+       /*
+        * First decode directly as many bytes as possible without
+        * reduction, taking care to leave a number of bytes which
+        * is a multiple of 4.
+        */
+       mblen = (m_bitlen + 7) >> 3;
+       k = mblen - 1;
+
+       /*
+        * Up to k bytes can be safely decoded.
+        */
+       if (k >= len) {
+               br_i32_decode(x, src, len);
+               x[0] = m_bitlen;
+               return;
+       }
+
+       /*
+        * We want to first inject some bytes with direct decoding,
+        * then extra bytes by whole 32-bit words. First compute
+        * the size that should be injected that way.
+        */
+       buf = src;
+       q = (len - k + 3) & ~(size_t)3;
+
+       /*
+        * It may happen that this is more than what we already have
+        * (by at most 3 bytes). Such a case may happen only with
+        * a very short modulus. In that case, we must process the first
+        * bytes "manually".
+        */
+       if (q > len) {
+               int i;
+               uint32_t w;
+
+               w = 0;
+               for (i = 0; i < 4; i ++) {
+                       w <<= 8;
+                       if (q <= len) {
+                               w |= buf[len - q];
+                       }
+                       q --;
+               }
+               br_i32_muladd_small(x, w, m);
+       } else {
+               br_i32_decode(x, buf, len - q);
+               x[0] = m_bitlen;
+       }
+
+       /*
+        * At that point, we have exactly q bytes to inject, and q is
+        * a multiple of 4.
+        */
+       for (k = len - q; k < len; k += 4) {
+               br_i32_muladd_small(x, br_dec32be(buf + k), m);
+       }
+}
diff --git a/src/int/i32_div32.c b/src/int/i32_div32.c
new file mode 100644 (file)
index 0000000..276ddfe
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r)
+{
+       // TODO: optimize this
+       uint32_t q;
+       uint32_t ch, cf;
+       int k;
+
+       q = 0;
+       ch = EQ(hi, d);
+       hi = MUX(ch, 0, hi);
+       for (k = 31; k > 0; k --) {
+               int j;
+               uint32_t w, ctl, hi2, lo2;
+
+               j = 32 - k;
+               w = (hi << j) | (lo >> k);
+               ctl = GE(w, d) | (hi >> k);
+               hi2 = (w - d) >> j;
+               lo2 = lo - (d << k);
+               hi = MUX(ctl, hi2, hi);
+               lo = MUX(ctl, lo2, lo);
+               q |= ctl << k;
+       }
+       cf = GE(lo, d) | hi;
+       q |= cf;
+       *r = MUX(cf, lo - d, lo);
+       return q;
+}
diff --git a/src/int/i32_encode.c b/src/int/i32_encode.c
new file mode 100644 (file)
index 0000000..303652f
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_encode(void *dst, size_t len, const uint32_t *x)
+{
+       unsigned char *buf;
+       size_t k;
+
+       buf = dst;
+
+       /*
+        * Compute the announced size of x in bytes; extra bytes are
+        * filled with zeros.
+        */
+       k = (x[0] + 7) >> 3;
+       while (len > k) {
+               *buf ++ = 0;
+               len --;
+       }
+
+       /*
+        * Now we use k as index within x[]. That index starts at 1;
+        * we initialize it to the topmost complete word, and process
+        * any remaining incomplete word.
+        */
+       k = (len + 3) >> 2;
+       switch (len & 3) {
+       case 3:
+               *buf ++ = x[k] >> 16;
+               /* fall through */
+       case 2:
+               *buf ++ = x[k] >> 8;
+               /* fall through */
+       case 1:
+               *buf ++ = x[k];
+               k --;
+       }
+
+       /*
+        * Encode all complete words.
+        */
+       while (k > 0) {
+               br_enc32be(buf, x[k]);
+               k --;
+               buf += 4;
+       }
+}
diff --git a/src/int/i32_fmont.c b/src/int/i32_fmont.c
new file mode 100644 (file)
index 0000000..dc1c934
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i)
+{
+       size_t len, u, v;
+
+       len = (m[0] + 31) >> 5;
+       for (u = 0; u < len; u ++) {
+               uint32_t f;
+               uint64_t cc;
+
+               f = x[1] * m0i;
+               cc = 0;
+               for (v = 0; v < len; v ++) {
+                       uint64_t z;
+
+                       z = (uint64_t)x[v + 1] + MUL(f, m[v + 1]) + cc;
+                       cc = z >> 32;
+                       if (v != 0) {
+                               x[v] = (uint32_t)z;
+                       }
+               }
+               x[len] = (uint32_t)cc;
+       }
+
+       /*
+        * We may have to do an extra subtraction, but only if the
+        * value in x[] is indeed greater than or equal to that of m[],
+        * which is why we must do two calls (first call computes the
+        * carry, second call performs the subtraction only if the carry
+        * is 0).
+        */
+       br_i32_sub(x, m, NOT(br_i32_sub(x, m, 0)));
+}
diff --git a/src/int/i32_iszero.c b/src/int/i32_iszero.c
new file mode 100644 (file)
index 0000000..659df7f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i32_iszero(const uint32_t *x)
+{
+       uint32_t z;
+       size_t u;
+
+       z = 0;
+       for (u = (x[0] + 31) >> 5; u > 0; u --) {
+               z |= x[u];
+       }
+       return ~(z | -z) >> 31;
+}
diff --git a/src/int/i32_modpow.c b/src/int/i32_modpow.c
new file mode 100644 (file)
index 0000000..034aba0
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_modpow(uint32_t *x,
+       const unsigned char *e, size_t elen,
+       const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2)
+{
+       size_t mlen;
+       uint32_t k;
+
+       /*
+        * 'mlen' is the length of m[] expressed in bytes (including
+        * the "bit length" first field).
+        */
+       mlen = ((m[0] + 63) >> 5) * sizeof m[0];
+
+       /*
+        * Throughout the algorithm:
+        * -- t1[] is in Montgomery representation; it contains x, x^2,
+        * x^4, x^8...
+        * -- The result is accumulated, in normal representation, in
+        * the x[] array.
+        * -- t2[] is used as destination buffer for each multiplication.
+        *
+        * Note that there is no need to call br_i32_from_monty().
+        */
+       memcpy(t1, x, mlen);
+       br_i32_to_monty(t1, m);
+       br_i32_zero(x, m[0]);
+       x[1] = 1;
+       for (k = 0; k < ((uint32_t)elen << 3); k ++) {
+               uint32_t ctl;
+
+               ctl = (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1;
+               br_i32_montymul(t2, x, t1, m, m0i);
+               CCOPY(ctl, x, t2, mlen);
+               br_i32_montymul(t2, t1, t1, m, m0i);
+               memcpy(t1, t2, mlen);
+       }
+}
diff --git a/src/int/i32_montmul.c b/src/int/i32_montmul.c
new file mode 100644 (file)
index 0000000..7edb376
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
+       const uint32_t *m, uint32_t m0i)
+{
+       size_t len, u, v;
+       uint64_t dh;
+
+       len = (m[0] + 31) >> 5;
+       br_i32_zero(d, m[0]);
+       dh = 0;
+       for (u = 0; u < len; u ++) {
+               uint32_t f, xu;
+               uint64_t r1, r2, zh;
+
+               xu = x[u + 1];
+               f = (d[1] + x[u + 1] * y[1]) * m0i;
+               r1 = 0;
+               r2 = 0;
+               for (v = 0; v < len; v ++) {
+                       uint64_t z;
+                       uint32_t t;
+
+                       z = (uint64_t)d[v + 1] + MUL(xu, y[v + 1]) + r1;
+                       r1 = z >> 32;
+                       t = (uint32_t)z;
+                       z = (uint64_t)t + MUL(f, m[v + 1]) + r2;
+                       r2 = z >> 32;
+                       if (v != 0) {
+                               d[v] = (uint32_t)z;
+                       }
+               }
+               zh = dh + r1 + r2;
+               d[len] = (uint32_t)zh;
+               dh = zh >> 32;
+       }
+
+       /*
+        * d[] may still be greater than m[] at that point; notably, the
+        * 'dh' word may be non-zero.
+        */
+       br_i32_sub(d, m, NEQ(dh, 0) | NOT(br_i32_sub(d, m, 0)));
+}
diff --git a/src/int/i32_mulacc.c b/src/int/i32_mulacc.c
new file mode 100644 (file)
index 0000000..f62c782
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b)
+{
+       size_t alen, blen, u;
+
+       alen = (a[0] + 31) >> 5;
+       blen = (b[0] + 31) >> 5;
+       d[0] = a[0] + b[0];
+       for (u = 0; u < blen; u ++) {
+               uint32_t f;
+               size_t v;
+               uint64_t cc;
+
+               f = b[1 + u];
+               cc = 0;
+               for (v = 0; v < alen; v ++) {
+                       uint64_t z;
+
+                       z = (uint64_t)d[1 + u + v] + MUL(f, a[1 + v]) + cc;
+                       cc = z >> 32;
+                       d[1 + u + v] = (uint32_t)z;
+               }
+               d[1 + u + alen] = (uint32_t)cc;
+       }
+}
diff --git a/src/int/i32_muladd.c b/src/int/i32_muladd.c
new file mode 100644 (file)
index 0000000..dd526ad
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
+{
+       uint32_t m_bitlen;
+       size_t u, mlen;
+       uint32_t a0, a1, b0, hi, g, q, tb;
+       uint32_t chf, clow, under, over;
+       uint64_t cc;
+
+       /*
+        * We can test on the modulus bit length since we accept to
+        * leak that length.
+        */
+       m_bitlen = m[0];
+       if (m_bitlen == 0) {
+               return;
+       }
+       if (m_bitlen <= 32) {
+               x[1] = br_rem(x[1], z, m[1]);
+               return;
+       }
+       mlen = (m_bitlen + 31) >> 5;
+
+       /*
+        * Principle: we estimate the quotient (x*2^32+z)/m by
+        * doing a 64/32 division with the high words.
+        *
+        * Let:
+        *   w = 2^32
+        *   a = (w*a0 + a1) * w^N + a2
+        *   b = b0 * w^N + b2
+        * such that:
+        *   0 <= a0 < w
+        *   0 <= a1 < w
+        *   0 <= a2 < w^N
+        *   w/2 <= b0 < w
+        *   0 <= b2 < w^N
+        *   a < w*b
+        * I.e. the two top words of a are a0:a1, the top word of b is
+        * b0, we ensured that b0 is "full" (high bit set), and a is
+        * such that the quotient q = a/b fits on one word (0 <= q < w).
+        *
+        * If a = b*q + r (with 0 <= r < q), we can estimate q by
+        * doing an Euclidean division on the top words:
+        *   a0*w+a1 = b0*u + v  (with 0 <= v < w)
+        * Then the following holds:
+        *   0 <= u <= w
+        *   u-2 <= q <= u
+        */
+       a0 = br_i32_word(x, m_bitlen - 32);
+       hi = x[mlen];
+       memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
+       x[1] = z;
+       a1 = br_i32_word(x, m_bitlen - 32);
+       b0 = br_i32_word(m, m_bitlen - 32);
+
+       /*
+        * We estimate a divisor q. If the quotient returned by br_div()
+        * is g:
+        * -- If a0 == b0 then g == 0; we want q = 0xFFFFFFFF.
+        * -- Otherwise:
+        *    -- if g == 0 then we set q = 0;
+        *    -- otherwise, we set q = g - 1.
+        * The properties described above then ensure that the true
+        * quotient is q-1, q or q+1.
+        */
+       g = br_div(a0, a1, b0);
+       q = MUX(EQ(a0, b0), 0xFFFFFFFF, MUX(EQ(g, 0), 0, g - 1));
+
+       /*
+        * We subtract q*m from x (with the extra high word of value 'hi').
+        * Since q may be off by 1 (in either direction), we may have to
+        * add or subtract m afterwards.
+        *
+        * The 'tb' flag will be true (1) at the end of the loop if the
+        * result is greater than or equal to the modulus (not counting
+        * 'hi' or the carry).
+        */
+       cc = 0;
+       tb = 1;
+       for (u = 1; u <= mlen; u ++) {
+               uint32_t mw, zw, xw, nxw;
+               uint64_t zl;
+
+               mw = m[u];
+               zl = MUL(mw, q) + cc;
+               cc = (uint32_t)(zl >> 32);
+               zw = (uint32_t)zl;
+               xw = x[u];
+               nxw = xw - zw;
+               cc += (uint64_t)GT(nxw, xw);
+               x[u] = nxw;
+               tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw));
+       }
+
+       /*
+        * If we underestimated q, then either cc < hi (one extra bit
+        * beyond the top array word), or cc == hi and tb is true (no
+        * extra bit, but the result is not lower than the modulus). In
+        * these cases we must subtract m once.
+        *
+        * Otherwise, we may have overestimated, which will show as
+        * cc > hi (thus a negative result). Correction is adding m once.
+        */
+       chf = (uint32_t)(cc >> 32);
+       clow = (uint32_t)cc;
+       over = chf | GT(clow, hi);
+       under = ~over & (tb | (~chf & LT(clow, hi)));
+       br_i32_add(x, m, over);
+       br_i32_sub(x, m, under);
+}
diff --git a/src/int/i32_ninv32.c b/src/int/i32_ninv32.c
new file mode 100644 (file)
index 0000000..6564434
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i32_ninv32(uint32_t x)
+{
+       uint32_t y;
+
+       y = 2 - x;
+       y *= 2 - y * x;
+       y *= 2 - y * x;
+       y *= 2 - y * x;
+       y *= 2 - y * x;
+       return MUX(x & 1, -y, 0);
+}
diff --git a/src/int/i32_reduce.c b/src/int/i32_reduce.c
new file mode 100644 (file)
index 0000000..90fff09
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m)
+{
+       uint32_t m_bitlen, a_bitlen;
+       size_t mlen, alen, u;
+
+       m_bitlen = m[0];
+       mlen = (m_bitlen + 31) >> 5;
+
+       x[0] = m_bitlen;
+       if (m_bitlen == 0) {
+               return;
+       }
+
+       /*
+        * If the source is shorter, then simply copy all words from a[]
+        * and zero out the upper words.
+        */
+       a_bitlen = a[0];
+       alen = (a_bitlen + 31) >> 5;
+       if (a_bitlen < m_bitlen) {
+               memcpy(x + 1, a + 1, alen * sizeof *a);
+               for (u = alen; u < mlen; u ++) {
+                       x[u + 1] = 0;
+               }
+               return;
+       }
+
+       /*
+        * The source length is at least equal to that of the modulus.
+        * We must thus copy N-1 words, and input the remaining words
+        * one by one.
+        */
+       memcpy(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a);
+       x[mlen] = 0;
+       for (u = 1 + alen - mlen; u > 0; u --) {
+               br_i32_muladd_small(x, a[u], m);
+       }
+}
diff --git a/src/int/i32_sub.c b/src/int/i32_sub.c
new file mode 100644 (file)
index 0000000..9c50023
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+uint32_t
+br_i32_sub(uint32_t *a, const uint32_t *b, uint32_t ctl)
+{
+       uint32_t cc;
+       size_t u, m;
+
+       cc = 0;
+       m = (a[0] + 63) >> 5;
+       for (u = 1; u < m; u ++) {
+               uint32_t aw, bw, naw;
+
+               aw = a[u];
+               bw = b[u];
+               naw = aw - bw - cc;
+
+               /*
+                * Carry is 1 if naw > aw. Carry is 1 also if naw == aw
+                * AND the carry was already 1.
+                */
+               cc = (cc & EQ(naw, aw)) | GT(naw, aw);
+               a[u] = MUX(ctl, naw, aw);
+       }
+       return cc;
+}
diff --git a/src/int/i32_tmont.c b/src/int/i32_tmont.c
new file mode 100644 (file)
index 0000000..058cd88
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_i32_to_monty(uint32_t *x, const uint32_t *m)
+{
+       uint32_t k;
+
+       for (k = (m[0] + 31) >> 5; k > 0; k --) {
+               br_i32_muladd_small(x, 0, m);
+       }
+}
diff --git a/src/mac/hmac.c b/src/mac/hmac.c
new file mode 100644 (file)
index 0000000..765e454
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static inline size_t
+block_size(const br_hash_class *dig)
+{
+       unsigned ls;
+       
+       ls = (unsigned)(dig->desc >> BR_HASHDESC_LBLEN_OFF)
+               & BR_HASHDESC_LBLEN_MASK;
+       return (size_t)1 << ls;
+}
+
+static void
+process_key(const br_hash_class **hc, void *ks,
+       const void *key, size_t key_len, unsigned bb)
+{
+       unsigned char tmp[256];
+       size_t blen, u;
+
+       blen = block_size(*hc);
+       memcpy(tmp, key, key_len);
+       for (u = 0; u < key_len; u ++) {
+               tmp[u] ^= (unsigned char)bb;
+       }
+       memset(tmp + key_len, bb, blen - key_len);
+       (*hc)->init(hc);
+       (*hc)->update(hc, tmp, blen);
+       (*hc)->state(hc, ks);
+}
+
+/* see bearssl.h */
+void
+br_hmac_key_init(br_hmac_key_context *kc,
+       const br_hash_class *dig, const void *key, size_t key_len)
+{
+       br_hmac_allhash_context hc;
+       unsigned char kbuf[64];
+
+       kc->dig_vtable = dig;
+       hc.vtable = dig;
+       if (key_len > block_size(dig)) {
+               dig->init(&hc.vtable);
+               dig->update(&hc.vtable, key, key_len);
+               dig->out(&hc.vtable, kbuf);
+               key = kbuf;
+               key_len = br_digest_size(dig);
+       }
+       process_key(&hc.vtable, kc->ksi, key, key_len, 0x36);
+       process_key(&hc.vtable, kc->kso, key, key_len, 0x5C);
+}
+
+/* see bearssl.h */
+void
+br_hmac_init(br_hmac_context *ctx,
+       const br_hmac_key_context *kc, size_t out_len)
+{
+       const br_hash_class *dig;
+       size_t blen, hlen;
+
+       dig = kc->dig_vtable;
+       blen = block_size(dig);
+       dig->init(&ctx->dig.vtable);
+       dig->set_state(&ctx->dig.vtable, kc->ksi, (uint64_t)blen);
+       memcpy(ctx->kso, kc->kso, sizeof kc->kso);
+       hlen = br_digest_size(dig);
+       if (out_len > 0 && out_len < hlen) {
+               hlen = out_len;
+       }
+       ctx->out_len = hlen;
+}
+
+/* see bearssl.h */
+void
+br_hmac_update(br_hmac_context *ctx, const void *data, size_t len)
+{
+       ctx->dig.vtable->update(&ctx->dig.vtable, data, len);
+}
+
+/* see bearssl.h */
+size_t
+br_hmac_out(const br_hmac_context *ctx, void *out)
+{
+       const br_hash_class *dig;
+       br_hmac_allhash_context hc;
+       unsigned char tmp[64];
+       size_t blen, hlen;
+
+       dig = ctx->dig.vtable;
+       dig->out(&ctx->dig.vtable, tmp);
+       blen = block_size(dig);
+       dig->init(&hc.vtable);
+       dig->set_state(&hc.vtable, ctx->kso, (uint64_t)blen);
+       hlen = br_digest_size(dig);
+       dig->update(&hc.vtable, tmp, hlen);
+       dig->out(&hc.vtable, tmp);
+       memcpy(out, tmp, ctx->out_len);
+       return ctx->out_len;
+}
diff --git a/src/mac/hmac_ct.c b/src/mac/hmac_ct.c
new file mode 100644 (file)
index 0000000..d3ab425
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static inline size_t
+hash_size(const br_hash_class *dig)
+{
+       return (unsigned)(dig->desc >> BR_HASHDESC_OUT_OFF)
+               & BR_HASHDESC_OUT_MASK;
+}
+
+static inline size_t
+block_size(const br_hash_class *dig)
+{
+       unsigned ls;
+       
+       ls = (unsigned)(dig->desc >> BR_HASHDESC_LBLEN_OFF)
+               & BR_HASHDESC_LBLEN_MASK;
+       return (size_t)1 << ls;
+}
+
+/* see bearssl.h */
+size_t
+br_hmac_outCT(const br_hmac_context *ctx,
+       const void *data, size_t len, size_t min_len, size_t max_len,
+       void *out)
+{
+       /*
+        * Method implemented here is inspired from the descriptions on:
+        *    https://www.imperialviolet.org/2013/02/04/luckythirteen.html
+        *
+        * Principle: we input bytes one by one. We use a MUX to push
+        * padding bytes instead of data bytes when appropriate. At each
+        * block limit, we get the current hash function state: this is
+        * a potential output, since we handle MD padding ourselves.
+        *
+        * be     1 for big-endian, 0 for little-endian
+        * po     minimal MD padding length
+        * bs     block size (always a power of 2)
+        * hlen   hash output size
+        */
+
+       const br_hash_class *dig;
+       br_hmac_allhash_context hc;
+       int be;
+       uint32_t po, bs;
+       uint32_t kr, km, kl, kz, u;
+       uint64_t count, ncount, bit_len;
+       unsigned char tmp1[64], tmp2[64];
+       size_t hlen;
+
+       /*
+        * Copy the current hash context.
+        */
+       hc = ctx->dig;
+
+       /*
+        * Get function-specific information.
+        */
+       dig = hc.vtable;
+       be = (dig->desc & BR_HASHDESC_MD_PADDING_BE) != 0;
+       po = 9;
+       if (dig->desc & BR_HASHDESC_MD_PADDING_128) {
+               po += 8;
+       }
+       bs = block_size(dig);
+       hlen = hash_size(dig);
+
+       /*
+        * Get current input length and compute total bit length.
+        */
+       count = dig->state(&hc.vtable, tmp1);
+       bit_len = (count + (uint64_t)len) << 3;
+
+       /*
+        * We can input the blocks that we are sure we will use.
+        * This offers better performance (no MUX for these blocks)
+        * and also ensures that the remaining lengths fit on 32 bits.
+        */
+       ncount = (count + (uint64_t)min_len) & ~(uint64_t)(bs - 1);
+       if (ncount > count) {
+               size_t zlen;
+
+               zlen = (size_t)(ncount - count);
+               dig->update(&hc.vtable, data, zlen);
+               data = (const unsigned char *)data + zlen;
+               len -= zlen;
+               max_len -= zlen;
+               count = ncount;
+       }
+
+       /*
+        * At that point:
+        * -- 'count' contains the number of bytes already processed
+        * (in total).
+        * -- We must input 'len' bytes. 'min_len' is unimportant: we
+        * used it to know how many full blocks we could process
+        * directly. Now only len and max_len matter.
+        *
+        * We compute kr, kl, kz and km.
+        *  kr   number of input bytes already in the current block
+        *  km   index of the first byte after the end of the last padding
+        *       block, if length is max_len
+        *  kz   index of the last byte of the actual last padding block
+        *  kl   index of the start of the encoded length
+        *
+        * km, kz and kl are counted from the current offset in the
+        * input data.
+        */
+       kr = (uint32_t)count & (bs - 1);
+       kz = ((kr + (uint32_t)len + po + bs - 1) & ~(bs - 1)) - 1 - kr;
+       kl = kz - 7;
+       km = ((kr + (uint32_t)max_len + po + bs - 1) & ~(bs - 1)) - kr;
+
+       /*
+        * We must now process km bytes. For index u from 0 to km-1:
+        *   d is from data[] if u < max_len, 0x00 otherwise
+        *   e is an encoded length byte or 0x00, depending on u
+        * The tests for d and e need not be constant-time, since
+        * they relate only to u and max_len, not to the actual length.
+        *
+        * Actual input length is then:
+        *   d      if u < len
+        *   0x80   if u == len
+        *   0x00   if u > len and u < kl
+        *   e      if u >= kl
+        *
+        * Hash state is obtained whenever we reach a full block. This
+        * is the result we want if and only if u == kz.
+        */
+       for (u = 0; u < km; u ++) {
+               uint32_t v;
+               uint32_t d, e, x0, x1;
+               unsigned char x[1];
+
+               d = (u < max_len) ? ((const unsigned char *)data)[u] : 0x00;
+               v = (kr + u) & (bs - 1);
+               if (v >= (bs - 8)) {
+                       unsigned j;
+
+                       j = (v - (bs - 8)) << 3;
+                       if (be) {
+                               e = (uint32_t)(bit_len >> (56 - j));
+                       } else {
+                               e = (uint32_t)(bit_len >> j);
+                       }
+                       e &= 0xFF;
+               } else {
+                       e = 0x00;
+               }
+               x0 = MUX(EQ(u, (uint32_t)len), 0x80, d);
+               x1 = MUX(LT(u, kl), 0x00, e);
+               x[0] = MUX(LE(u, (uint32_t)len), x0, x1);
+               dig->update(&hc.vtable, x, 1);
+               if (v == (bs - 1)) {
+                       dig->state(&hc.vtable, tmp1);
+                       CCOPY(EQ(u, kz), tmp2, tmp1, hlen);
+               }
+       }
+
+       /*
+        * Inner hash output is in tmp2[]; we finish processing.
+        */
+       dig->init(&hc.vtable);
+       dig->set_state(&hc.vtable, ctx->kso, (uint64_t)bs);
+       dig->update(&hc.vtable, tmp2, hlen);
+       dig->out(&hc.vtable, tmp2);
+       memcpy(out, tmp2, ctx->out_len);
+       return ctx->out_len;
+}
diff --git a/src/rand/hmac_drbg.c b/src/rand/hmac_drbg.c
new file mode 100644 (file)
index 0000000..d746756
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl.h */
+void
+br_hmac_drbg_init(br_hmac_drbg_context *ctx,
+       const br_hash_class *digest_class, const void *seed, size_t len)
+{
+       size_t hlen;
+
+       ctx->vtable = &br_hmac_drbg_vtable;
+       hlen = br_digest_size(digest_class);
+       memset(ctx->K, 0x00, hlen);
+       memset(ctx->V, 0x01, hlen);
+       ctx->digest_class = digest_class;
+       br_hmac_drbg_update(ctx, seed, len);
+}
+
+/* see bearssl.h */
+void
+br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len)
+{
+       const br_hash_class *dig;
+       br_hmac_key_context kc;
+       br_hmac_context hc;
+       size_t hlen;
+       unsigned char *buf;
+       unsigned char x;
+
+       dig = ctx->digest_class;
+       hlen = br_digest_size(dig);
+       br_hmac_key_init(&kc, dig, ctx->K, hlen);
+       buf = out;
+       while (len > 0) {
+               size_t clen;
+
+               br_hmac_init(&hc, &kc, 0);
+               br_hmac_update(&hc, ctx->V, hlen);
+               br_hmac_out(&hc, ctx->V);
+               clen = hlen;
+               if (clen > len) {
+                       clen = len;
+               }
+               memcpy(buf, ctx->V, clen);
+               buf += clen;
+               len -= clen;
+       }
+
+       /*
+        * To prepare the state for the next request, we should call
+        * br_hmac_drbg_update() with an empty additional seed. However,
+        * we already have an initialized HMAC context with the right
+        * initial key, and we don't want to push another one on the
+        * stack, so we inline that update() call here.
+        */
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, ctx->V, hlen);
+       x = 0x00;
+       br_hmac_update(&hc, &x, 1);
+       br_hmac_out(&hc, ctx->K);
+       br_hmac_key_init(&kc, dig, ctx->K, hlen);
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, ctx->V, hlen);
+       br_hmac_out(&hc, ctx->V);
+}
+
+/* see bearssl.h */
+void
+br_hmac_drbg_update(br_hmac_drbg_context *ctx, const void *seed, size_t len)
+{
+       const br_hash_class *dig;
+       br_hmac_key_context kc;
+       br_hmac_context hc;
+       size_t hlen;
+       unsigned char x;
+
+       dig = ctx->digest_class;
+       hlen = br_digest_size(dig);
+
+       /*
+        * 1. K = HMAC(K, V || 0x00 || seed)
+        */
+       br_hmac_key_init(&kc, dig, ctx->K, hlen);
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, ctx->V, hlen);
+       x = 0x00;
+       br_hmac_update(&hc, &x, 1);
+       br_hmac_update(&hc, seed, len);
+       br_hmac_out(&hc, ctx->K);
+       br_hmac_key_init(&kc, dig, ctx->K, hlen);
+
+       /*
+        * 2. V = HMAC(K, V)
+        */
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, ctx->V, hlen);
+       br_hmac_out(&hc, ctx->V);
+
+       /*
+        * 3. If the additional seed is empty, then stop here.
+        */
+       if (len == 0) {
+               return;
+       }
+
+       /*
+        * 4. K = HMAC(K, V || 0x01 || seed)
+        */
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, ctx->V, hlen);
+       x = 0x01;
+       br_hmac_update(&hc, &x, 1);
+       br_hmac_update(&hc, seed, len);
+       br_hmac_out(&hc, ctx->K);
+       br_hmac_key_init(&kc, dig, ctx->K, hlen);
+
+       /*
+        * 5. V = HMAC(K, V)
+        */
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, ctx->V, hlen);
+       br_hmac_out(&hc, ctx->V);
+}
+
+/* see bearssl.h */
+const br_prng_class br_hmac_drbg_vtable = {
+       sizeof(br_hmac_drbg_context),
+       (void (*)(const br_prng_class **, const void *, const void *, size_t))
+               &br_hmac_drbg_init,
+       (void (*)(const br_prng_class **, void *, size_t))
+               &br_hmac_drbg_generate,
+       (void (*)(const br_prng_class **, const void *, size_t))
+               &br_hmac_drbg_update
+};
diff --git a/src/rsa/rsa_i31_pkcs1_sign.c b/src/rsa/rsa_i31_pkcs1_sign.c
new file mode 100644 (file)
index 0000000..431a119
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i31_pkcs1_sign(const unsigned char *hash_oid,
+       const unsigned char *hash, size_t hash_len,
+       const br_rsa_private_key *sk, unsigned char *x)
+{
+       size_t u, x3, xlen;
+
+       /*
+        * Padded hash value has format:
+        *  00 01 FF .. FF 00 30 x1 30 x2 06 x3 OID 05 00 04 x4 HASH
+        *
+        * with the following rules:
+        *
+        *  -- Total length is equal to the modulus length (unsigned
+        *     encoding).
+        *
+        *  -- There must be at least eight bytes of value 0xFF.
+        *
+        *  -- x4 is equal to the hash length (hash_len).
+        *
+        *  -- x3 is equal to the encoded OID value length (hash_oid[0]).
+        *
+        *  -- x2 = x3 + 4.
+        *
+        *  -- x1 = x2 + x4 + 4 = x3 + x4 + 8.
+        *
+        * Note: the "05 00" is optional (signatures with and without
+        * that sequence exist in practice), but notes in PKCS#1 seem to
+        * indicate that the presence of that sequence (specifically,
+        * an ASN.1 NULL value for the hash parameters) may be slightly
+        * more "standard" than the opposite.
+        */
+       xlen = (sk->n_bitlen + 7) >> 3;
+
+       if (hash_oid == NULL) {
+               if (xlen < hash_len + 11) {
+                       return 0;
+               }
+               x[0] = 0x00;
+               x[1] = 0x01;
+               u = xlen - hash_len;
+               memset(x + 2, 0xFF, u - 3);
+               x[u - 1] = 0x00;
+       } else {
+               x3 = hash_oid[0];
+
+               /*
+                * Check that there is enough room for all the elements,
+                * including at least eight bytes of value 0xFF.
+                */
+               if (xlen < (x3 + hash_len + 21)) {
+                       return 0;
+               }
+               x[0] = 0x00;
+               x[1] = 0x01;
+               u = xlen - x3 - hash_len - 11;
+               memset(x + 2, 0xFF, u - 2);
+               x[u] = 0x00;
+               x[u + 1] = 0x30;
+               x[u + 2] = x3 + hash_len + 8;
+               x[u + 3] = 0x30;
+               x[u + 4] = x3 + 4;
+               x[u + 5] = 0x06;
+               memcpy(x + u + 6, hash_oid, x3 + 1);
+               u += x3 + 7;
+               x[u ++] = 0x05;
+               x[u ++] = 0x00;
+               x[u ++] = 0x04;
+               x[u ++] = hash_len;
+       }
+       memcpy(x + u, hash, hash_len);
+
+       /*
+        * Do the actual computation.
+        */
+       return br_rsa_i31_private(x, sk);
+}
diff --git a/src/rsa/rsa_i31_pkcs1_vrfy.c b/src/rsa/rsa_i31_pkcs1_vrfy.c
new file mode 100644 (file)
index 0000000..c3ffb62
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i31_pkcs1_vrfy(const unsigned char *x, size_t xlen,
+       const unsigned char *hash_oid, size_t hash_len,
+       const br_rsa_public_key *pk, unsigned char *hash_out)
+{
+       static const unsigned char pad1[] = {
+               0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+       };
+
+       unsigned char sig[BR_MAX_RSA_SIZE >> 3];
+       unsigned char pad2[43];
+       size_t u, x2, x3, pad_len, zlen;
+
+       if (xlen > (sizeof sig) || xlen < 11) {
+               return 0;
+       }
+       memcpy(sig, x, xlen);
+       if (!br_rsa_i31_public(sig, xlen, pk)) {
+               return 0;
+       }
+
+       /*
+        * Expected format:
+        *  00 01 FF ... FF 00 30 x1 30 x2 06 x3 OID [ 05 00 ] 04 x4 HASH
+        *
+        * with the following rules:
+        *
+        *  -- Total length is that of the modulus and the signature
+        *     (this was already verified by br_rsa_i31_public()).
+        *
+        *  -- There are at least eight bytes of value 0xFF.
+        *
+        *  -- x4 is equal to the hash length (hash_len).
+        *
+        *  -- x3 is equal to the encoded OID value length (so x3 is the
+        *     first byte of hash_oid[]).
+        *
+        *  -- If the "05 00" is present, then x2 == x3 + 4; otherwise,
+        *     x2 == x3 + 2.
+        *
+        *  -- x1 == x2 + x4 + 4.
+        *
+        * So the total length after the last "FF" is either x3 + x4 + 11
+        * (with the "05 00") or x3 + x4 + 9 (without the "05 00").
+        */
+
+       /*
+        * Check the "00 01 FF .. FF 00" with at least eight 0xFF bytes.
+        * The comparaison is valid because we made sure that the signature
+        * is at least 11 bytes long.
+        */
+       if (memcmp(sig, pad1, sizeof pad1) != 0) {
+               return 0;
+       }
+       for (u = sizeof pad1; u < xlen; u ++) {
+               if (sig[u] != 0xFF) {
+                       break;
+               }
+       }
+
+       /*
+        * Remaining length is xlen - u bytes (including the 00 just
+        * after the last FF). This must be equal to one of the two
+        * possible values (depending on whether the "05 00" sequence is
+        * present or not).
+        */
+       if (hash_oid == NULL) {
+               if (xlen - u != hash_len + 1 || sig[u] != 0x00) {
+                       return 0;
+               }
+       } else {
+               x3 = hash_oid[0];
+               pad_len = x3 + 9;
+               memset(pad2, 0, pad_len);
+               zlen = xlen - u - hash_len;
+               if (zlen == pad_len) {
+                       x2 = x3 + 2;
+               } else if (zlen == pad_len + 2) {
+                       x2 = x3 + 4;
+                       pad_len = zlen;
+                       pad2[pad_len - 4] = 0x05;
+               } else {
+                       return 0;
+               }
+               pad2[1] = 0x30;
+               pad2[2] = x2 + hash_len + 4;
+               pad2[3] = 0x30;
+               pad2[4] = x2;
+               pad2[5] = 0x06;
+               memcpy(pad2 + 6, hash_oid, x3 + 1);
+               pad2[pad_len - 2] = 0x04;
+               pad2[pad_len - 1] = hash_len;
+               if (memcmp(pad2, sig + u, pad_len) != 0) {
+                       return 0;
+               }
+       }
+       memcpy(hash_out, sig + xlen - hash_len, hash_len);
+       return 1;
+}
diff --git a/src/rsa/rsa_i31_priv.c b/src/rsa/rsa_i31_priv.c
new file mode 100644 (file)
index 0000000..efff6ae
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define U   (1 + ((BR_MAX_RSA_FACTOR + 30) / 31))
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i31_private(unsigned char *x, const br_rsa_private_key *sk)
+{
+       const unsigned char *p, *q;
+       size_t plen, qlen;
+       uint32_t tmp[6 * U];
+       uint32_t *mp, *mq, *s1, *s2, *t1, *t2, *t3;
+       uint32_t p0i, q0i;
+       size_t xlen;
+
+       /*
+        * All our temporary buffers are from the tmp[] array.
+        *
+        * The mp, mq, s1, s2, t1 and t2 buffers are large enough to
+        * contain a RSA factor. The t3 buffer can contain a complete
+        * RSA modulus. t3 shares its storage space with s2, s1 and t1,
+        * in that order (this is important, see below).
+        */
+       mq = tmp;
+       mp = tmp + U;
+       t2 = tmp + 2 * U;
+       s2 = tmp + 3 * U;
+       s1 = tmp + 4 * U;
+       t1 = tmp + 5 * U;
+       t3 = s2;
+
+       /*
+        * Compute the actual lengths (in bytes) of p and q, and check
+        * that they fit within our stack buffers.
+        */
+       p = sk->p;
+       plen = sk->plen;
+       while (plen > 0 && *p == 0) {
+               p ++;
+               plen --;
+       }
+       q = sk->q;
+       qlen = sk->qlen;
+       while (qlen > 0 && *q == 0) {
+               q ++;
+               qlen --;
+       }
+       if (plen > (BR_MAX_RSA_FACTOR >> 3)
+               || qlen > (BR_MAX_RSA_FACTOR >> 3))
+       {
+               return 0;
+       }
+
+       /*
+        * Decode p and q.
+        */
+       br_i31_decode(mp, p, plen);
+       br_i31_decode(mq, q, qlen);
+
+       /*
+        * Compute signature length (in bytes).
+        */
+       xlen = (sk->n_bitlen + 7) >> 3;
+
+       /*
+        * Compute s1 = x^dp mod p.
+        */
+       p0i = br_i31_ninv31(mp[1]);
+       br_i31_decode_reduce(s1, x, xlen, mp);
+       br_i31_modpow(s1, sk->dp, sk->dplen, mp, p0i, t1, t2);
+
+       /*
+        * Compute s2 = x^dq mod q.
+        */
+       q0i = br_i31_ninv31(mq[1]);
+       br_i31_decode_reduce(s2, x, xlen, mq);
+       br_i31_modpow(s2, sk->dq, sk->dqlen, mq, q0i, t1, t2);
+
+       /*
+        * Compute:
+        *   h = (s1 - s2)*(1/q) mod p
+        * s1 is an integer modulo p, but s2 is modulo q. PKCS#1 is
+        * unclear about whether p may be lower than q (some existing,
+        * widely deployed implementations of RSA don't tolerate p < q),
+        * but we want to support that occurrence, so we need to use the
+        * reduction function.
+        *
+        * Since we use br_i31_decode_reduce() for iq (purportedly, the
+        * inverse of q modulo p), we also tolerate improperly large
+        * values for this parameter.
+        */
+       br_i31_reduce(t2, s2, mp);
+       br_i31_add(s1, mp, br_i31_sub(s1, t2, 1));
+       br_i31_to_monty(s1, mp);
+       br_i31_decode_reduce(t1, sk->iq, sk->iqlen, mp);
+       br_i31_montymul(t2, s1, t1, mp, p0i);
+
+       /*
+        * h is now in t2. We compute the final result:
+        *   s = s2 + q*h
+        * All these operations are non-modular.
+        *
+        * We need mq, s2 and t2. We use the t3 buffer as destination.
+        * The buffers mp, s1 and t1 are no longer needed. Moreover,
+        * the first step is to copy s2 into the destination buffer t3.
+        * We thus arranged for t3 to actually share space with s2, and
+        * to be followed by the space formerly used by s1 and t1.
+        */
+       br_i31_mulacc(t3, mq, t2);
+
+       /*
+        * Encode the result. Since we already checked the value of xlen,
+        * we can just use it right away.
+        */
+       br_i31_encode(x, xlen, t3);
+
+       /*
+        * The only error conditions remaining at that point are invalid
+        * values for p and q (even integers).
+        */
+       return p0i & q0i & 1;
+}
diff --git a/src/rsa/rsa_i31_pub.c b/src/rsa/rsa_i31_pub.c
new file mode 100644 (file)
index 0000000..18e069f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i31_public(unsigned char *x, size_t xlen,
+       const br_rsa_public_key *pk)
+{
+       const unsigned char *n;
+       size_t nlen;
+       uint32_t m[1 + ((BR_MAX_RSA_SIZE + 30) / 31)];
+       uint32_t a[1 + ((BR_MAX_RSA_SIZE + 30) / 31)];
+       uint32_t t1[1 + ((BR_MAX_RSA_SIZE + 30) / 31)];
+       uint32_t t2[1 + ((BR_MAX_RSA_SIZE + 30) / 31)];
+       uint32_t m0i, r;
+
+       /*
+        * Get the actual length of the modulus, and see if it fits within
+        * our stack buffer. We also check that the length of x[] is valid.
+        */
+       n = pk->n;
+       nlen = pk->nlen;
+       while (nlen > 0 && *n == 0) {
+               n ++;
+               nlen --;
+       }
+       if (nlen == 0 || nlen > (BR_MAX_RSA_SIZE >> 3) || xlen != nlen) {
+               return 0;
+       }
+       br_i31_decode(m, n, nlen);
+       m0i = br_i31_ninv31(m[1]);
+
+       /*
+        * Note: if m[] is even, then m0i == 0. Otherwise, m0i must be
+        * an odd integer.
+        */
+       r = m0i & 1;
+
+       /*
+        * Decode x[] into a[]; we also check that its value is proper.
+        */
+       r &= br_i31_decode_mod(a, x, xlen, m);
+
+       /*
+        * Compute the modular exponentiation.
+        */
+       br_i31_modpow(a, pk->e, pk->elen, m, m0i, t1, t2);
+
+       /*
+        * Encode the result.
+        */
+       br_i31_encode(x, xlen, a);
+       return r;
+}
diff --git a/src/rsa/rsa_i32_pkcs1_sign.c b/src/rsa/rsa_i32_pkcs1_sign.c
new file mode 100644 (file)
index 0000000..d6d64d0
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i32_pkcs1_sign(const unsigned char *hash_oid,
+       const unsigned char *hash, size_t hash_len,
+       const br_rsa_private_key *sk, unsigned char *x)
+{
+       size_t u, x3, xlen;
+
+       /*
+        * Padded hash value has format:
+        *  00 01 FF .. FF 00 30 x1 30 x2 06 x3 OID 05 00 04 x4 HASH
+        *
+        * with the following rules:
+        *
+        *  -- Total length is equal to the modulus length (unsigned
+        *     encoding).
+        *
+        *  -- There must be at least eight bytes of value 0xFF.
+        *
+        *  -- x4 is equal to the hash length (hash_len).
+        *
+        *  -- x3 is equal to the encoded OID value length (hash_oid[0]).
+        *
+        *  -- x2 = x3 + 4.
+        *
+        *  -- x1 = x2 + x4 + 4 = x3 + x4 + 8.
+        *
+        * Note: the "05 00" is optional (signatures with and without
+        * that sequence exist in practice), but notes in PKCS#1 seem to
+        * indicate that the presence of that sequence (specifically,
+        * an ASN.1 NULL value for the hash parameters) may be slightly
+        * more "standard" than the opposite.
+        */
+       xlen = (sk->n_bitlen + 7) >> 3;
+
+       if (hash_oid == NULL) {
+               if (xlen < hash_len + 11) {
+                       return 0;
+               }
+               u = xlen - hash_len;
+               memset(x + 2, 0xFF, u - 3);
+               x[u - 1] = 0x00;
+       } else {
+               x3 = hash_oid[0];
+
+               /*
+                * Check that there is enough room for all the elements,
+                * including at least eight bytes of value 0xFF.
+                */
+               if (xlen < (x3 + hash_len + 21)) {
+                       return 0;
+               }
+               x[0] = 0x00;
+               x[1] = 0x01;
+               u = xlen - x3 - hash_len - 11;
+               memset(x + 2, 0xFF, u - 2);
+               x[u] = 0x00;
+               x[u + 1] = 0x30;
+               x[u + 2] = x3 + hash_len + 8;
+               x[u + 3] = 0x30;
+               x[u + 4] = x3 + 4;
+               x[u + 5] = 0x06;
+               memcpy(x + u + 6, hash_oid, x3 + 1);
+               u += x3 + 7;
+               x[u ++] = 0x05;
+               x[u ++] = 0x00;
+               x[u ++] = 0x04;
+               x[u ++] = hash_len;
+       }
+       memcpy(x + u + 4, hash, hash_len);
+
+       /*
+        * Do the actual computation.
+        */
+       return br_rsa_i32_private(x, sk);
+}
diff --git a/src/rsa/rsa_i32_pkcs1_vrfy.c b/src/rsa/rsa_i32_pkcs1_vrfy.c
new file mode 100644 (file)
index 0000000..cc20ba8
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i32_pkcs1_vrfy(const unsigned char *x, size_t xlen,
+       const unsigned char *hash_oid, size_t hash_len,
+       const br_rsa_public_key *pk, unsigned char *hash_out)
+{
+       static const unsigned char pad1[] = {
+               0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+       };
+
+       unsigned char sig[BR_MAX_RSA_SIZE >> 3];
+       unsigned char pad2[43];
+       size_t u, x2, x3, pad_len, zlen;
+
+       if (xlen > (sizeof sig) || xlen < 11) {
+               return 0;
+       }
+       memcpy(sig, x, xlen);
+       if (!br_rsa_i32_public(sig, xlen, pk)) {
+               return 0;
+       }
+
+       /*
+        * Expected format:
+        *  00 01 FF ... FF 00 30 x1 30 x2 06 x3 OID [ 05 00 ] 04 x4 HASH
+        *
+        * with the following rules:
+        *
+        *  -- Total length is that of the modulus and the signature
+        *     (this was already verified by br_rsa_i32_public()).
+        *
+        *  -- There are at least eight bytes of value 0xFF.
+        *
+        *  -- x4 is equal to the hash length (hash_len).
+        *
+        *  -- x3 is equal to the encoded OID value length (so x3 is the
+        *     first byte of hash_oid[]).
+        *
+        *  -- If the "05 00" is present, then x2 == x3 + 4; otherwise,
+        *     x2 == x3 + 2.
+        *
+        *  -- x1 == x2 + x4 + 4.
+        *
+        * So the total length after the last "FF" is either x3 + x4 + 11
+        * (with the "05 00") or x3 + x4 + 9 (without the "05 00").
+        */
+
+       /*
+        * Check the "00 01 FF .. FF 00" with at least eight 0xFF bytes.
+        * The comparaison is valid because we made sure that the signature
+        * is at least 11 bytes long.
+        */
+       if (memcmp(sig, pad1, sizeof pad1) != 0) {
+               return 0;
+       }
+       for (u = sizeof pad1; u < xlen; u ++) {
+               if (sig[u] != 0xFF) {
+                       break;
+               }
+       }
+
+       /*
+        * Remaining length is xlen - u bytes (including the 00 just
+        * after the last FF). This must be equal to one of the two
+        * possible values (depending on whether the "05 00" sequence is
+        * present or not).
+        */
+       if (hash_oid == NULL) {
+               if (xlen - u != hash_len + 1 || sig[u] != 0x00) {
+                       return 0;
+               }
+       } else {
+               x3 = hash_oid[0];
+               pad_len = x3 + 9;
+               memset(pad2, 0, pad_len);
+               zlen = xlen - u - hash_len;
+               if (zlen == pad_len) {
+                       x2 = x3 + 2;
+               } else if (zlen == pad_len + 2) {
+                       x2 = x3 + 4;
+                       pad_len = zlen;
+                       pad2[pad_len - 4] = 0x05;
+               } else {
+                       return 0;
+               }
+               pad2[1] = 0x30;
+               pad2[2] = x2 + hash_len + 4;
+               pad2[3] = 0x30;
+               pad2[4] = x2;
+               pad2[5] = 0x06;
+               memcpy(pad2 + 6, hash_oid, x3 + 1);
+               pad2[pad_len - 2] = 0x04;
+               pad2[pad_len - 1] = hash_len;
+               if (memcmp(pad2, sig + u, pad_len) != 0) {
+                       return 0;
+               }
+       }
+       memcpy(hash_out, sig + xlen - hash_len, hash_len);
+       return 1;
+}
diff --git a/src/rsa/rsa_i32_priv.c b/src/rsa/rsa_i32_priv.c
new file mode 100644 (file)
index 0000000..3c08d00
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define U   (1 + (BR_MAX_RSA_FACTOR >> 5))
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i32_private(unsigned char *x, const br_rsa_private_key *sk)
+{
+       const unsigned char *p, *q;
+       size_t plen, qlen;
+       uint32_t tmp[6 * U];
+       uint32_t *mp, *mq, *s1, *s2, *t1, *t2, *t3;
+       uint32_t p0i, q0i;
+       size_t xlen;
+
+       /*
+        * All our temporary buffers are from the tmp[] array.
+        *
+        * The mp, mq, s1, s2, t1 and t2 buffers are large enough to
+        * contain a RSA factor. The t3 buffer can contain a complete
+        * RSA modulus. t3 shares its storage space with s2, s1 and t1,
+        * in that order (this is important, see below).
+        */
+       mq = tmp;
+       mp = tmp + U;
+       t2 = tmp + 2 * U;
+       s2 = tmp + 3 * U;
+       s1 = tmp + 4 * U;
+       t1 = tmp + 5 * U;
+       t3 = s2;
+
+       /*
+        * Compute the actual lengths (in bytes) of p and q, and check
+        * that they fit within our stack buffers.
+        */
+       p = sk->p;
+       plen = sk->plen;
+       while (plen > 0 && *p == 0) {
+               p ++;
+               plen --;
+       }
+       q = sk->q;
+       qlen = sk->qlen;
+       while (qlen > 0 && *q == 0) {
+               q ++;
+               qlen --;
+       }
+       if (plen > (BR_MAX_RSA_FACTOR >> 3)
+               || qlen > (BR_MAX_RSA_FACTOR >> 3))
+       {
+               return 0;
+       }
+
+       /*
+        * Decode p and q.
+        */
+       br_i32_decode(mp, p, plen);
+       br_i32_decode(mq, q, qlen);
+
+       /*
+        * obsolete -- we do not compute the length of n, it is now
+        * an input parameter.
+       br_i32_zero(t3, mp[0]);
+       br_i32_mulacc(t3, mp, mq);
+       n_bitlen = br_i32_bit_length(t3 + 1, (t3[0] + 31) >> 5);
+       if (xlen != ((n_bitlen + 7) >> 3)) {
+               return 0;
+       }
+        */
+       xlen = (sk->n_bitlen + 7) >> 3;
+
+       /*
+        * Compute s1 = x^dp mod p.
+        */
+       p0i = br_i32_ninv32(mp[1]);
+       br_i32_decode_reduce(s1, x, xlen, mp);
+       br_i32_modpow(s1, sk->dp, sk->dplen, mp, p0i, t1, t2);
+
+       /*
+        * Compute s2 = x^dq mod q.
+        */
+       q0i = br_i32_ninv32(mq[1]);
+       br_i32_decode_reduce(s2, x, xlen, mq);
+       br_i32_modpow(s2, sk->dq, sk->dqlen, mq, q0i, t1, t2);
+
+       /*
+        * Compute:
+        *   h = (s1 - s2)*(1/q) mod p
+        * s1 is an integer modulo p, but s2 is modulo q. PKCS#1 is
+        * unclear about whether p may be lower than q (some existing,
+        * widely deployed implementations of RSA don't tolerate p < q),
+        * but we want to support that occurrence, so we need to use the
+        * reduction function.
+        *
+        * Since we use br_i32_decode_reduce() for iq (purportedly, the
+        * inverse of q modulo p), we also tolerate improperly large
+        * values for this parameter.
+        */
+       br_i32_reduce(t2, s2, mp);
+       br_i32_add(s1, mp, br_i32_sub(s1, t2, 1));
+       br_i32_to_monty(s1, mp);
+       br_i32_decode_reduce(t1, sk->iq, sk->iqlen, mp);
+       br_i32_montymul(t2, s1, t1, mp, p0i);
+
+       /*
+        * h is now in t2. We compute the final result:
+        *   s = s2 + q*h
+        * All these operations are non-modular.
+        *
+        * We need mq, s2 and t2. We use the t3 buffer as destination.
+        * The buffers mp, s1 and t1 are no longer needed. Moreover,
+        * the first step is to copy s2 into the destination buffer t3.
+        * We thus arranged for t3 to actually share space with s2, and
+        * to be followed by the space formerly used by s1 and t1.
+        */
+       br_i32_mulacc(t3, mq, t2);
+
+       /*
+        * Encode the result. Since we already checked the value of xlen,
+        * we can just use it right away.
+        */
+       br_i32_encode(x, xlen, t3);
+
+       /*
+        * The only error conditions remaining at that point are invalid
+        * values for p and q (even integers).
+        */
+       return p0i & q0i & 1;
+}
diff --git a/src/rsa/rsa_i32_pub.c b/src/rsa/rsa_i32_pub.c
new file mode 100644 (file)
index 0000000..6e8d8e3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_i32_public(unsigned char *x, size_t xlen,
+       const br_rsa_public_key *pk)
+{
+       const unsigned char *n;
+       size_t nlen;
+       uint32_t m[1 + (BR_MAX_RSA_SIZE >> 5)];
+       uint32_t a[1 + (BR_MAX_RSA_SIZE >> 5)];
+       uint32_t t1[1 + (BR_MAX_RSA_SIZE >> 5)];
+       uint32_t t2[1 + (BR_MAX_RSA_SIZE >> 5)];
+       uint32_t m0i, r;
+
+       /*
+        * Get the actual length of the modulus, and see if it fits within
+        * our stack buffer. We also check that the length of x[] is valid.
+        */
+       n = pk->n;
+       nlen = pk->nlen;
+       while (nlen > 0 && *n == 0) {
+               n ++;
+               nlen --;
+       }
+       if (nlen == 0 || nlen > (BR_MAX_RSA_SIZE >> 3) || xlen != nlen) {
+               return 0;
+       }
+       br_i32_decode(m, n, nlen);
+       m0i = br_i32_ninv32(m[1]);
+
+       /*
+        * Note: if m[] is even, then m0i == 0. Otherwise, m0i must be
+        * an odd integer.
+        */
+       r = m0i & 1;
+
+       /*
+        * Decode x[] into a[]; we also check that its value is proper.
+        */
+       r &= br_i32_decode_mod(a, x, xlen, m);
+
+       /*
+        * Compute the modular exponentiation.
+        */
+       br_i32_modpow(a, pk->e, pk->elen, m, m0i, t1, t2);
+
+       /*
+        * Encode the result.
+        */
+       br_i32_encode(x, xlen, a);
+       return r;
+}
diff --git a/src/rsa/rsa_ssl_decrypt.c b/src/rsa/rsa_ssl_decrypt.c
new file mode 100644 (file)
index 0000000..047eb18
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_rsa.h */
+uint32_t
+br_rsa_ssl_decrypt(br_rsa_private core, const br_rsa_private_key *sk,
+       unsigned char *data, size_t len)
+{
+       uint32_t x;
+       size_t u;
+
+       /*
+        * A first check on length. Since this test works only on the
+        * buffer length, it needs not (and cannot) be constant-time.
+        */
+       if (len < 59 || len != (sk->n_bitlen + 7) >> 3) {
+               return 0;
+       }
+       x = core(data, sk);
+
+       x &= EQ(data[0], 0x00);
+       x &= EQ(data[1], 0x02);
+       for (u = 2; u < (len - 49); u ++) {
+               x &= NEQ(data[u], 0);
+       }
+       x &= EQ(data[len - 49], 0x00);
+       memmove(data, data + len - 48, 48);
+       return x;
+}
diff --git a/src/ssl/prf.c b/src/ssl/prf.c
new file mode 100644 (file)
index 0000000..43a74c3
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_tls_phash(void *dst, size_t len,
+       const br_hash_class *dig,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len)
+{
+       unsigned char *buf;
+       unsigned char tmp[64], a[64];
+       br_hmac_key_context kc;
+       br_hmac_context hc;
+       size_t label_len, hlen;
+
+       if (len == 0) {
+               return;
+       }
+       buf = dst;
+       for (label_len = 0; label[label_len]; label_len ++);
+       hlen = br_digest_size(dig);
+       br_hmac_key_init(&kc, dig, secret, secret_len);
+       br_hmac_init(&hc, &kc, 0);
+       br_hmac_update(&hc, label, label_len);
+       br_hmac_update(&hc, seed, seed_len);
+       br_hmac_out(&hc, a);
+       for (;;) {
+               size_t u;
+
+               br_hmac_init(&hc, &kc, 0);
+               br_hmac_update(&hc, a, hlen);
+               br_hmac_update(&hc, label, label_len);
+               br_hmac_update(&hc, seed, seed_len);
+               br_hmac_out(&hc, tmp);
+               for (u = 0; u < hlen && u < len; u ++) {
+                       buf[u] ^= tmp[u];
+               }
+               buf += u;
+               len -= u;
+               if (len == 0) {
+                       return;
+               }
+               br_hmac_init(&hc, &kc, 0);
+               br_hmac_update(&hc, a, hlen);
+               br_hmac_out(&hc, a);
+       }
+}
diff --git a/src/ssl/prf_md5sha1.c b/src/ssl/prf_md5sha1.c
new file mode 100644 (file)
index 0000000..414dfed
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl.h */
+void
+br_tls10_prf(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len)
+{
+       const unsigned char *s1;
+       size_t slen;
+
+       s1 = secret;
+       slen = (secret_len + 1) >> 1;
+       memset(dst, 0, len);
+       br_tls_phash(dst, len, &br_md5_vtable,
+               s1, slen, label, seed, seed_len);
+       br_tls_phash(dst, len, &br_sha1_vtable,
+               s1 + secret_len - slen, slen, label, seed, seed_len);
+}
diff --git a/src/ssl/prf_sha256.c b/src/ssl/prf_sha256.c
new file mode 100644 (file)
index 0000000..cec916a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl.h */
+void
+br_tls12_sha256_prf(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len)
+{
+       memset(dst, 0, len);
+       br_tls_phash(dst, len, &br_sha256_vtable,
+               secret, secret_len, label, seed, seed_len);
+}
diff --git a/src/ssl/prf_sha384.c b/src/ssl/prf_sha384.c
new file mode 100644 (file)
index 0000000..5069560
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl.h */
+void
+br_tls12_sha384_prf(void *dst, size_t len,
+       const void *secret, size_t secret_len,
+       const char *label, const void *seed, size_t seed_len)
+{
+       memset(dst, 0, len);
+       br_tls_phash(dst, len, &br_sha384_vtable,
+               secret, secret_len, label, seed, seed_len);
+}
diff --git a/src/ssl/ssl_client.c b/src/ssl/ssl_client.c
new file mode 100644 (file)
index 0000000..28c404b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_client_zero(br_ssl_client_context *cc)
+{
+       /*
+        * For really standard C, we should explicitly set to NULL all
+        * pointers, and 0 all other fields. However, on all our target
+        * architectures, a direct memset() will work, be faster, and
+        * use a lot less code.
+        */
+       memset(cc, 0, sizeof *cc);
+}
+
+/* see bearssl_ssl.h */
+int
+br_ssl_client_reset(br_ssl_client_context *cc,
+       const char *server_name, int resume_session)
+{
+       size_t n;
+
+       br_ssl_engine_set_buffer(&cc->eng, NULL, 0, 0);
+       cc->eng.version_out = cc->eng.version_min;
+       if (!resume_session) {
+               br_ssl_client_forget_session(cc);
+       }
+       if (!br_ssl_engine_init_rand(&cc->eng)) {
+               return 0;
+       }
+
+       /*
+        * We always set back the "reneg" flag to 0 because we use it
+        * to distinguish between first handshake and renegotiation.
+        * Note that "renegotiation" and "session resumption" are two
+        * different things.
+        */
+       cc->eng.reneg = 0;
+
+       if (server_name == NULL) {
+               cc->eng.server_name[0] = 0;
+       } else {
+               n = strlen(server_name) + 1;
+               if (n > sizeof cc->eng.server_name) {
+                       br_ssl_engine_fail(&cc->eng, BR_ERR_BAD_PARAM);
+                       return 0;
+               }
+               memcpy(cc->eng.server_name, server_name, n);
+       }
+
+       br_ssl_engine_hs_reset(&cc->eng,
+               br_ssl_hs_client_init_main, br_ssl_hs_client_run);
+       return br_ssl_engine_last_error(&cc->eng) == BR_ERR_OK;
+}
diff --git a/src/ssl/ssl_client_full.c b/src/ssl/ssl_client_full.c
new file mode 100644 (file)
index 0000000..ad1f0cc
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_client_init_full(br_ssl_client_context *cc,
+       br_x509_minimal_context *xc,
+       const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num)
+{
+       /*
+        * The "full" profile supports all implemented cipher suites.
+        *
+        * Rationale for suite order, from most important to least
+        * important rule:
+        *
+        * -- Don't use 3DES if AES is available.
+        * -- Try to have Forward Secrecy (ECDHE suite) if possible.
+        * -- When not using Forward Secrecy, ECDH key exchange is
+        *    better than RSA key exchange (slightly more expensive on the
+        *    client, but much cheaper on the server, and it implies smaller
+        *    messages).
+        * -- GCM is better than CBC.
+        * -- AES-128 is preferred over AES-256 (AES-128 is already
+        *    strong enough, and AES-256 is 40% more expensive).
+        */
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA
+       };
+
+       /*
+        * All hash functions are activated.
+        * Note: the X.509 validation engine will nonetheless refuse to
+        * validate signatures that use MD5 as hash function.
+        */
+       static const br_hash_class *hashes[] = {
+               &br_md5_vtable,
+               &br_sha1_vtable,
+               &br_sha224_vtable,
+               &br_sha256_vtable,
+               &br_sha384_vtable,
+               &br_sha512_vtable
+       };
+
+       int id;
+
+       /*
+        * Reset client context and set supported versions from TLS-1.0
+        * to TLS-1.2 (inclusive).
+        */
+       br_ssl_client_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12);
+
+       /*
+        * X.509 engine uses SHA-256 to hash certificate DN (for
+        * comparisons).
+        */
+       br_x509_minimal_init(xc, &br_sha256_vtable,
+               trust_anchors, trust_anchors_num);
+
+       /*
+        * Set suites and asymmetric crypto implementations. We use the
+        * "i31" code for RSA (it is somewhat faster than the "i32"
+        * implementation).
+        * TODO: change that when better implementations are made available.
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+       br_ssl_client_set_rsapub(cc, &br_rsa_i31_public);
+       br_ssl_client_set_rsavrfy(cc, &br_rsa_i31_pkcs1_vrfy);
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+       br_ssl_client_set_ecdsa(cc, &br_ecdsa_i31_vrfy_asn1);
+       br_x509_minimal_set_rsa(xc, &br_rsa_i31_pkcs1_vrfy);
+       br_x509_minimal_set_ecdsa(xc,
+               &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
+
+       /*
+        * Set supported hash functions, for the SSL engine and for the
+        * X.509 engine.
+        */
+       for (id = br_md5_ID; id <= br_sha512_ID; id ++) {
+               const br_hash_class *hc;
+
+               hc = hashes[id - 1];
+               br_ssl_engine_set_hash(&cc->eng, id, hc);
+               br_x509_minimal_set_hash(xc, id, hc);
+       }
+
+       /*
+        * Link the X.509 engine in the SSL engine.
+        */
+       br_ssl_engine_set_x509(&cc->eng, &xc->vtable);
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf);
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+       br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct64_cbcenc_vtable,
+               &br_aes_ct64_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct_cbcenc_vtable,
+               &br_aes_ct_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_ct_cbcenc_vtable,
+               &br_des_ct_cbcdec_vtable);
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_cbc(&cc->eng,
+               &br_sslrec_in_cbc_vtable,
+               &br_sslrec_out_cbc_vtable);
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_engine.c b/src/ssl/ssl_engine.c
new file mode 100644 (file)
index 0000000..8af773d
--- /dev/null
@@ -0,0 +1,1468 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * If BR_USE_URANDOM is not defined, then try to autodetect its presence
+ * through compiler macros.
+ */
+#ifndef BR_USE_URANDOM
+
+/*
+ * Macro values documented on:
+ *    https://sourceforge.net/p/predef/wiki/OperatingSystems/
+ *
+ * Only the most common systems have been included here for now. This
+ * should be enriched later on.
+ */
+#if defined _AIX \
+       || defined __ANDROID__ \
+       || defined __FreeBSD__ \
+       || defined __NetBSD__ \
+       || defined __OpenBSD__ \
+       || defined __DragonFly__ \
+       || defined __linux__ \
+       || (defined __sun && (defined __SVR4 || defined __svr4__)) \
+       || (defined __APPLE__ && defined __MACH__)
+#define BR_USE_URANDOM   1
+#endif
+
+#endif
+
+/*
+ * If BR_USE_WIN32_RAND is not defined, perform autodetection here.
+ */
+#ifndef BR_USE_WIN32_RAND
+
+#if defined _WIN32 || defined _WIN64
+#define BR_USE_WIN32_RAND   1
+#endif
+
+#endif
+
+#if BR_USE_URANDOM
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+#if BR_USE_WIN32_RAND
+#include <windows.h>
+#include <wincrypt.h>
+#pragma comment(lib, "advapi32")
+#endif
+
+/* ==================================================================== */
+/*
+ * This part of the file does the low-level record management.
+ */
+
+/*
+ * IMPLEMENTATION NOTES
+ * ====================
+ *
+ * In this file, we designate by "input" (and the "i" letter) the "recv"
+ * operations: incoming records from the peer, from which payload data
+ * is obtained, and must be extracted by the application (or the SSL
+ * handshake engine). Similarly, "output" (and the "o" letter) is for
+ * "send": payload data injected by the application (and SSL handshake
+ * engine), to be wrapped into records, that are then conveyed to the
+ * peer over the transport medium.
+ *
+ * The input and output buffers may be distinct or shared. When
+ * shared, input and output cannot occur concurrently; the caller
+ * must make sure that it never needs to output data while input
+ * data has been received. In practice, a shared buffer prevents
+ * pipelining of HTTP requests, or similar protocols; however, a
+ * shared buffer saves RAM.
+ *
+ * The input buffer is pointed to by 'ibuf' and has size 'ibuf_len';
+ * the output buffer is pointed to by 'obuf' and has size 'obuf_len'.
+ * From the size of these buffers is derived the maximum fragment
+ * length, which will be honoured upon sending records; regardless of
+ * that length, incoming records will be processed as long as they
+ * fit in the input buffer, and their length still complies with the
+ * protocol specification (maximum plaintext payload length is 16384
+ * bytes).
+ *
+ * Three registers are used to manage buffering in ibuf, called ixa,
+ * ixb and ixc. Similarly, three registers are used to manage buffering
+ * in obuf, called oxa, oxb and oxc.
+ *
+ *
+ * At any time, the engine is in one of the following modes:
+ * -- Failed mode: an error occurs, no I/O can happen.
+ * -- Input mode: the engine can either receive record bytes from the
+ * transport layer, or it has some buffered payload bytes to yield.
+ * -- Output mode: the engine can either receive payload bytes, or it
+ * has some record bytes to send to the transport layer.
+ * -- Input/Output mode: both input and output modes are active. When
+ * the buffer is shared, this can happen only when the buffer is empty
+ * (no buffered payload bytes or record bytes in either direction).
+ *
+ *
+ * Failed mode:
+ * ------------
+ *
+ * I/O failed for some reason (invalid received data, not enough room
+ * for the next record...). No I/O may ever occur again for this context,
+ * until an explicit reset is performed. This mode, and the error code,
+ * are also used for protocol errors, especially handshake errors.
+ *
+ *
+ * Input mode:
+ * -----------
+ *
+ *  ixa   index within ibuf[] for the currently read data
+ *  ixb   maximum index within ibuf[] for the currently read data
+ *  ixc   number of bytes not yet received for the current record
+ * 
+ * -- When ixa == ixb, there is no available data for readers. When
+ * ixa != ixb, there is available data and it starts at offset ixa.
+ *
+ * -- When waiting for the next record header, ixa and ixb are equal
+ * and contain a value ranging from 0 to 4; ixc is equal to 5-ixa.
+ *
+ * -- When the header has been received, record data is obtained. The
+ * ixc field records how many bytes are still needed to reach the
+ * end of the current record.
+ *
+ *    ** If encryption is active, then ixa and ixb are kept equal, and
+ *    point to the end of the currently received record bytes. When
+ *    ixc reaches 0, decryption/MAC is applied, and ixa and ixb are
+ *    adjusted.
+ *
+ *    ** If encryption is not active, then ixa and ixb are distinct
+ *    and data can be read right away. Additional record data is
+ *    obtained only when ixa == ixb.
+ *
+ * Note: in input mode and no encryption, records larger than the buffer
+ * size are allowed. When encryption is active, the complete record must
+ * fit within the buffer, since it cannot be decrypted/MACed until it
+ * has been completely received.
+ *
+ * -- When receiving the next record header, 'version_in' contains the
+ * expected input version (0 if not expecting a specific version); on
+ * mismatch, the mode switches to 'failed'.
+ *
+ * -- When the header has been received, 'version_in' contains the received
+ * version. It is up to the caller to check and adjust the 'version_in' field
+ * to implement the required semantics.
+ *
+ * -- The 'record_type_in' field is updated with the incoming record type
+ * when the next record header has been received.
+ *
+ *
+ * Output mode:
+ * ------------
+ *
+ *  oxa   index within obuf[] for the currently accumulated data
+ *  oxb   maximum index within obuf[] for record data
+ *  oxc   pointer for start of record data, and for record sending
+ *
+ * -- When oxa != oxb, more data can be accumulated into the current
+ * record; when oxa == oxb, a closed record is being sent.
+ *
+ * -- When accumulating data, oxc points to the start of the data.
+ *
+ * -- During record sending, oxa (and oxb) point to the next record byte
+ * to send, and oxc indicates the end of the current record.
+ *
+ * Note: sent records must fit within the buffer, since the header is
+ * adjusted only when the complete record has been assembled.
+ *
+ * -- The 'version_out' and 'record_type_out' fields are used to build the
+ * record header when the mode is switched to 'sending'.
+ *
+ *
+ * Modes:
+ * ------
+ *
+ * The state register iomode contains one of the following values:
+ *
+ *  BR_IO_FAILED   I/O failed
+ *  BR_IO_IN       input mode
+ *  BR_IO_OUT      output mode
+ *  BR_IO_INOUT    input/output mode
+ *
+ * Whether encryption is active on incoming records is indicated by the
+ * incrypt flag. For outgoing records, there is no such flag; "encryption"
+ * is always considered active, but initially uses functions that do not
+ * encrypt anything. The 'incrypt' flag is needed because when there is
+ * no active encryption, records larger than the I/O buffer are accepted.
+ *
+ * Note: we do not support no-encryption modes (MAC only).
+ *
+ * TODO: implement GCM support
+ *
+ *
+ * Misc:
+ * -----
+ *
+ * 'max_frag_len' is the maximum plaintext size for an outgoing record.
+ * By default, it is set to the maximum value that fits in the provided
+ * buffers, in the following list: 512, 1024, 2048, 4096, 16384. The
+ * caller may change it if needed, but the new value MUST still fit in
+ * the buffers, and it MUST be one of the list above for compatibility
+ * with the Maximum Fragment Length extension.
+ *
+ * For incoming records, only the total buffer length and current
+ * encryption mode impact the maximum length for incoming records. The
+ * 'max_frag_len' value is still adjusted so that records up to that
+ * length can be both received and sent.
+ *
+ *
+ * Offsets and lengths:
+ * --------------------
+ *
+ * When sending fragments with TLS-1.1+, the maximum overhead is:
+ *   5 bytes for the record header
+ *   16 bytes for the explicit IV
+ *   48 bytes for the MAC (HMAC/SHA-384)
+ *   16 bytes for the padding (AES)
+ * so a total of 85 extra bytes. Note that we support block cipher sizes
+ * up to 16 bytes (AES) and HMAC output sizes up to 48 bytes (SHA-384).
+ *
+ * With TLS-1.0 and CBC mode, we apply a 1/n-1 split, for a maximum
+ * overhead of:
+ *   5 bytes for the first record header
+ *   32 bytes for the first record payload (AES-CBC + HMAC/SHA-1)
+ *   5 bytes for the second record header
+ *   20 bytes for the MAC (HMAC/SHA-1)
+ *   16 bytes for the padding (AES)
+ *   -1 byte to account for the payload byte in the first record
+ * so a total of 77 extra bytes at most, less than the 85 bytes above.
+ * Note that with TLS-1.0, the MAC is HMAC with either MD5 or SHA-1, but
+ * no other hash function.
+ *
+ * The implementation does not try to send larger records when the current
+ * encryption mode has less overhead.
+ *
+ * Maximum input record overhead is:
+ *   5 bytes for the record header
+ *   16 bytes for the explicit IV (TLS-1.1+)
+ *   48 bytes for the MAC (HMAC/SHA-384)
+ *   256 bytes for the padding
+ * so a total of 325 extra bytes.
+ *
+ * When receiving the next record header, it is written into the buffer
+ * bytes 0 to 4 (inclusive). Record data is always written into buf[]
+ * starting at offset 5. When encryption is active, the plaintext data
+ * may start at a larger offset (e.g. because of an explicit IV).
+ */
+
+#define MAX_OUT_OVERHEAD    85
+#define MAX_IN_OVERHEAD    325
+
+/* see inner.h */
+void
+br_ssl_engine_fail(br_ssl_engine_context *rc, int err)
+{
+       if (rc->iomode != BR_IO_FAILED) {
+               rc->iomode = BR_IO_FAILED;
+               rc->err = err;
+       }
+}
+
+/*
+ * Adjust registers for a new incoming record.
+ */
+static void
+make_ready_in(br_ssl_engine_context *rc)
+{
+       rc->ixa = rc->ixb = 0;
+       rc->ixc = 5;
+       if (rc->iomode == BR_IO_IN) {
+               rc->iomode = BR_IO_INOUT;
+       }
+}
+
+/*
+ * Adjust registers for a new outgoing record.
+ */
+static void
+make_ready_out(br_ssl_engine_context *rc)
+{
+       size_t a, b;
+
+       a = 5;
+       b = rc->obuf_len - a;
+       rc->out.vtable->max_plaintext(&rc->out.vtable, &a, &b);
+       if ((b - a) > rc->max_frag_len) {
+               b = a + rc->max_frag_len;
+       }
+       rc->oxa = a;
+       rc->oxb = b;
+       rc->oxc = a;
+       if (rc->iomode == BR_IO_OUT) {
+               rc->iomode = BR_IO_INOUT;
+       }
+}
+
+/* see inner.h */
+void
+br_ssl_engine_new_max_frag_len(br_ssl_engine_context *rc, unsigned max_frag_len)
+{
+       size_t nxb;
+
+       rc->max_frag_len = max_frag_len;
+       nxb = rc->oxc + max_frag_len;
+       if (rc->oxa < rc->oxb && rc->oxb > nxb && rc->oxa < nxb) {
+               rc->oxb = nxb;
+       }
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_set_buffer(br_ssl_engine_context *rc,
+       void *buf, size_t buf_len, int bidi)
+{
+       if (buf == NULL) {
+               br_ssl_engine_set_buffers_bidi(rc, NULL, 0, NULL, 0);
+       } else {
+               /*
+                * In bidirectional mode, we want to maximise input
+                * buffer size, since we support arbitrary fragmentation
+                * when sending, but the peer will not necessarily
+                * comply to any low fragment length (in particular if
+                * we are the server, because the maximum fragment
+                * length extension is under client control).
+                *
+                * We keep a minimum size of 512 bytes for the plaintext
+                * of our outgoing records.
+                *
+                * br_ssl_engine_set_buffers_bidi() will compute the maximum
+                * fragment length for outgoing records by using the minimum
+                * of allocated spaces for both input and output records,
+                * rounded down to a standard length.
+                */
+               if (bidi) {
+                       size_t w;
+
+                       if (buf_len < (512 + MAX_IN_OVERHEAD
+                               + 512 + MAX_OUT_OVERHEAD))
+                       {
+                               rc->iomode = BR_IO_FAILED;
+                               rc->err = BR_ERR_BAD_PARAM;
+                               return;
+                       } else if (buf_len < (16384 + MAX_IN_OVERHEAD
+                               + 512 + MAX_OUT_OVERHEAD))
+                       {
+                               w = 512 + MAX_OUT_OVERHEAD;
+                       } else {
+                               w = buf_len - (16384 + MAX_IN_OVERHEAD);
+                       }
+                       br_ssl_engine_set_buffers_bidi(rc,
+                               buf, buf_len - w,
+                               (unsigned char *)buf + w, w);
+               } else {
+                       br_ssl_engine_set_buffers_bidi(rc,
+                               buf, buf_len, NULL, 0);
+               }
+       }
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *rc,
+       void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len)
+{
+       rc->iomode = BR_IO_INOUT;
+       rc->incrypt = 0;
+       rc->err = BR_ERR_OK;
+       rc->version_in = 0;
+       rc->record_type_in = 0;
+       rc->version_out = 0;
+       rc->record_type_out = 0;
+       if (ibuf == NULL) {
+               if (rc->ibuf == NULL) {
+                       br_ssl_engine_fail(rc, BR_ERR_BAD_PARAM);
+               }
+       } else {
+               unsigned u;
+
+               rc->ibuf = ibuf;
+               rc->ibuf_len = ibuf_len;
+               if (obuf == NULL) {
+                       obuf = ibuf;
+                       obuf_len = ibuf_len;
+               }
+               rc->obuf = obuf;
+               rc->obuf_len = obuf_len;
+
+               /*
+                * Compute the maximum fragment length, that fits for
+                * both incoming and outgoing records. This length will
+                * be used in fragment length negotiation, so we must
+                * honour it both ways. Regardless, larger incoming
+                * records will be accepted, as long as they fit in the
+                * actual buffer size.
+                */
+               for (u = 14; u >= 9; u --) {
+                       size_t flen;
+
+                       flen = (size_t)1 << u;
+                       if (obuf_len >= flen + MAX_OUT_OVERHEAD
+                               && ibuf_len >= flen + MAX_IN_OVERHEAD)
+                       {
+                               break;
+                       }
+               }
+               if (u == 8) {
+                       br_ssl_engine_fail(rc, BR_ERR_BAD_PARAM);
+                       return;
+               } else if (u == 13) {
+                       u = 12;
+               }
+               rc->max_frag_len = (size_t)1 << u;
+               rc->log_max_frag_len = u;
+               rc->peer_log_max_frag_len = 0;
+       }
+       rc->out.vtable = &br_sslrec_out_clear_vtable;
+       make_ready_in(rc);
+       make_ready_out(rc);
+}
+
+/*
+ * Clear buffers in both directions.
+ */
+static void
+engine_clearbuf(br_ssl_engine_context *rc)
+{
+       make_ready_in(rc);
+       make_ready_out(rc);
+}
+
+/* see inner.h */
+int
+br_ssl_engine_init_rand(br_ssl_engine_context *cc)
+{
+       /*
+        * TODO: use getrandom() on Linux systems, with a fallback to
+        * opening /dev/urandom if that system call fails.
+        *
+        * Use similar OS facilities on other OS (getentropy() on OpenBSD,
+        * specialized sysctl on NetBSD and FreeBSD...).
+        */
+#if BR_USE_URANDOM
+       if (!cc->rng_os_rand_done) {
+               int f;
+
+               f = open("/dev/urandom", O_RDONLY);
+               if (f >= 0) {
+                       unsigned char tmp[32];
+                       size_t u;
+
+                       for (u = 0; u < sizeof tmp;) {
+                               ssize_t len;
+
+                               len = read(f, tmp + u, (sizeof tmp) - u);
+                               if (len < 0) {
+                                       if (errno == EINTR) {
+                                               continue;
+                                       }
+                                       break;
+                               }
+                               u += (size_t)len;
+                       }
+                       close(f);
+                       if (u == sizeof tmp) {
+                               br_ssl_engine_inject_entropy(cc, tmp, u);
+                               cc->rng_os_rand_done = 1;
+                       }
+               }
+       }
+#elif BR_USE_WIN32_RAND
+       if (!cc->rng_os_rand_done) {
+               HCRYPTPROV hp;
+
+               if (CryptAcquireContextW(&hp, 0, 0, PROV_RSA_FULL,
+                       CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+               {
+                       BYTE buf[32];
+
+                       if (CryptGenRandom(hp, sizeof buf, buf)) {
+                               br_ssl_engine_inject_entropy(cc,
+                                       buf, sizeof buf);
+                               cc->rng_os_rand_done = 1;
+                       }
+                       CryptReleaseContext(hp, 0);
+               }
+       }
+#endif
+
+       if (!cc->rng_init_done) {
+               br_ssl_engine_fail(cc, BR_ERR_NO_RANDOM);
+               return 0;
+       }
+       return 1;
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_inject_entropy(br_ssl_engine_context *cc,
+       const void *data, size_t len)
+{
+       if (cc->rng_init_done) {
+               br_hmac_drbg_update(&cc->rng, data, len);
+       } else {
+               /*
+                * If using TLS-1.2, then SHA-256 or SHA-384 must be
+                * present (or both); we prefer SHA-256 which is faster
+                * for 32-bit systems.
+                *
+                * If using TLS-1.0 or 1.1 then SHA-1 must be present.
+                *
+                * Though HMAC_DRBG/SHA-1 is, as far as we know, as safe
+                * as these things can be, we still prefer the SHA-2
+                * functions over SHA-1, if only for public relations
+                * (known theoretical weaknesses of SHA-1 with regards to
+                * collisions are mostly irrelevant here, but they still
+                * make people nervous).
+                */
+               const br_hash_class *h;
+
+               h = br_multihash_getimpl(&cc->mhash, br_sha256_ID);
+               if (!h) {
+                       h = br_multihash_getimpl(&cc->mhash, br_sha384_ID);
+                       if (!h) {
+                               h = br_multihash_getimpl(&cc->mhash,
+                                       br_sha1_ID);
+                               if (!h) {
+                                       br_ssl_engine_fail(cc,
+                                               BR_ERR_BAD_STATE);
+                                       return;
+                               }
+                       }
+               }
+               br_hmac_drbg_init(&cc->rng, h, data, len);
+               cc->rng_init_done = 1;
+       }
+}
+
+/*
+ * We define a few internal functions that implement the low-level engine
+ * API for I/O; the external API (br_ssl_engine_sendapp_buf() and similar
+ * functions) is built upon these function, with special processing for
+ * records which are not of type "application data".
+ *
+ *   recvrec_buf, recvrec_ack     receives bytes from transport medium
+ *   sendrec_buf, sendrec_ack     send bytes to transport medium
+ *   recvpld_buf, recvpld_ack     receives payload data from engine
+ *   sendpld_buf, sendpld_ack     send payload data to engine
+ */
+
+static unsigned char *
+recvrec_buf(const br_ssl_engine_context *rc, size_t *len)
+{
+       if (rc->shutdown_recv) {
+               *len = 0;
+               return NULL;
+       }
+
+       /*
+        * Bytes from the transport can be injected only if the mode is
+        * compatible (in or in/out), and ixa == ixb; ixc then contains
+        * the number of bytes that are still expected (but it may
+        * exceed our buffer size).
+        *
+        * We cannot get "stuck" here (buffer is full, but still more
+        * data is expected) because oversized records are detected when
+        * their header is processed.
+        */
+       switch (rc->iomode) {
+       case BR_IO_IN:
+       case BR_IO_INOUT:
+               if (rc->ixa == rc->ixb) {
+                       size_t z;
+
+                       z = rc->ixc;
+                       if (z > rc->ibuf_len - rc->ixa) {
+                               z = rc->ibuf_len - rc->ixa;
+                       }
+                       *len = z;
+                       return rc->ibuf + rc->ixa;
+               }
+               break;
+       }
+       *len = 0;
+       return NULL;
+}
+
+static void
+recvrec_ack(br_ssl_engine_context *rc, size_t len)
+{
+       unsigned char *pbuf;
+       size_t pbuf_len;
+
+       /*
+        * Adjust state if necessary (for a shared input/output buffer):
+        * we got some incoming bytes, so we cannot (temporarily) handle
+        * outgoing data.
+        */
+       if (rc->iomode == BR_IO_INOUT && rc->ibuf == rc->obuf) {
+               rc->iomode = BR_IO_IN;
+       }
+
+       /*
+        * Adjust data pointers.
+        */
+       rc->ixb = (rc->ixa += len);
+       rc->ixc -= len;
+
+       /*
+        * If we are receiving a header and did not fully obtained it
+        * yet, then just wait for the next bytes.
+        */
+       if (rc->ixa < 5) {
+               return;
+       }
+
+       /*
+        * If we just obtained a full header, process it.
+        */
+       if (rc->ixa == 5) {
+               unsigned version;
+               unsigned rlen;
+
+               /*
+                * Get record type and version. We support only versions
+                * 3.x (if the version major number does not match, then
+                * we suppose that the record format is too alien for us
+                * to process it).
+                *
+                * Note: right now, we reject clients that try to send
+                * a ClientHello in a format compatible with SSL-2.0. It
+                * is unclear whether this will ever be supported; and
+                * if we want to support it, then this might be done in
+                * in the server-specific code, not here.
+                */
+               rc->record_type_in = rc->ibuf[0];
+               version = br_dec16be(rc->ibuf + 1);
+               if ((version >> 8) != 3) {
+                       br_ssl_engine_fail(rc, BR_ERR_UNSUPPORTED_VERSION);
+                       return;
+               }
+
+               /*
+                * We ensure that successive records have the same
+                * version. The handshake code must check and adjust the
+                * variables when necessary to accommodate the protocol
+                * negotiation details.
+                */
+               if (rc->version_in != 0 && rc->version_in != version) {
+                       br_ssl_engine_fail(rc, BR_ERR_BAD_VERSION);
+                       return;
+               }
+               rc->version_in = version;
+
+               /*
+                * Decode record length. We must check that the length
+                * is valid (relatively to the current encryption mode)
+                * and also (if encryption is active) that the record
+                * will fit in our buffer.
+                *
+                * When no encryption is active, we can process records
+                * by chunks, and thus accept any record up to the
+                * maximum allowed plaintext length (16384 bytes).
+                */
+               rlen = br_dec16be(rc->ibuf + 3);
+               if (rc->incrypt) {
+                       if (!rc->in.vtable->check_length(
+                               &rc->in.vtable, rlen))
+                       {
+                               br_ssl_engine_fail(rc, BR_ERR_BAD_LENGTH);
+                               return;
+                       }
+                       if (rlen > (rc->ibuf_len - 5)) {
+                               br_ssl_engine_fail(rc, BR_ERR_TOO_LARGE);
+                               return;
+                       }
+               } else {
+                       if (rlen > 16384) {
+                               br_ssl_engine_fail(rc, BR_ERR_BAD_LENGTH);
+                               return;
+                       }
+               }
+
+               /*
+                * If the record is completely empty then we must switch
+                * to a new record. Note that, in that case, we
+                * completely ignore the record type, which is fitting
+                * since we received no actual data of that type.
+                *
+                * A completely empty record is technically allowed as
+                * long as encryption/MAC is not active, i.e. before
+                * completion of the first handshake. It it still weird;
+                * it might conceptually be useful as a heartbeat or
+                * keep-alive mechanism while some lengthy operation is
+                * going on, e.g. interaction with a human user.
+                */
+               if (rlen == 0) {
+                       make_ready_in(rc);
+               } else {
+                       rc->ixa = rc->ixb = 5;
+                       rc->ixc = rlen;
+               }
+               return;
+       }
+
+       /*
+        * If there is no active encryption, then the data can be read
+        * right away. Note that we do not receive bytes from the
+        * transport medium when we still have payload bytes to be
+        * acknowledged.
+        */
+       if (!rc->incrypt) {
+               rc->ixa = 5;
+               return;
+       }
+
+       /*
+        * Since encryption is active, we must wait for a full record
+        * before processing it.
+        */
+       if (rc->ixc != 0) {
+               return;
+       }
+
+       /*
+        * We got the full record. Decrypt it.
+        */
+       pbuf_len = rc->ixa - 5;
+       pbuf = rc->in.vtable->decrypt(&rc->in.vtable,
+               rc->record_type_in, rc->version_in, rc->ibuf + 5, &pbuf_len);
+       if (pbuf == 0) {
+               br_ssl_engine_fail(rc, BR_ERR_BAD_MAC);
+               return;
+       }
+       rc->ixa = (size_t)(pbuf - rc->ibuf);
+       rc->ixb = rc->ixa + pbuf_len;
+
+       /*
+        * Decryption may have yielded an empty record, in which case
+        * we get back to "ready" state immediately.
+        */
+       if (rc->ixa == rc->ixb) {
+               make_ready_in(rc);
+       }
+}
+
+/* see inner.h */
+int
+br_ssl_engine_recvrec_finished(const br_ssl_engine_context *rc)
+{
+       switch (rc->iomode) {
+       case BR_IO_IN:
+       case BR_IO_INOUT:
+               return rc->ixc == 0 || rc->ixa < 5;
+       default:
+               return 1;
+       }
+}
+
+static unsigned char *
+recvpld_buf(const br_ssl_engine_context *rc, size_t *len)
+{
+       /*
+        * There is payload data to be read only if the mode is
+        * compatible, and ixa != ixb.
+        */
+       switch (rc->iomode) {
+       case BR_IO_IN:
+       case BR_IO_INOUT:
+               *len = rc->ixb - rc->ixa;
+               return (*len == 0) ? NULL : (rc->ibuf + rc->ixa);
+       default:
+               *len = 0;
+               return NULL;
+       }
+}
+
+static void
+recvpld_ack(br_ssl_engine_context *rc, size_t len)
+{
+       rc->ixa += len;
+
+       /*
+        * If we read all the available data, then we either expect
+        * the remainder of the current record (if the current record
+        * was not finished; this may happen when encryption is not
+        * active), or go to "ready" state.
+        */
+       if (rc->ixa == rc->ixb) {
+               if (rc->ixc == 0) {
+                       make_ready_in(rc);
+               } else {
+                       rc->ixa = rc->ixb = 5;
+               }
+       }
+}
+
+static unsigned char *
+sendpld_buf(const br_ssl_engine_context *rc, size_t *len)
+{
+       /*
+        * Payload data can be injected only if the current mode is
+        * compatible, and oxa != oxb.
+        */
+       switch (rc->iomode) {
+       case BR_IO_OUT:
+       case BR_IO_INOUT:
+               *len = rc->oxb - rc->oxa;
+               return (*len == 0) ? NULL : (rc->obuf + rc->oxa);
+       default:
+               *len = 0;
+               return NULL;
+       }
+}
+
+/*
+ * If some payload bytes have been accumulated, then wrap them into
+ * an outgoing record. Otherwise, this function does nothing, unless
+ * 'force' is non-zero, in which case an empty record is assembled.
+ *
+ * The caller must take care not to invoke this function if the engine
+ * is not currently ready to receive payload bytes to send.
+ */
+static void
+sendpld_flush(br_ssl_engine_context *rc, int force)
+{
+       size_t xlen;
+       unsigned char *buf;
+
+       if (rc->oxa == rc->oxb) {
+               return;
+       }
+       xlen = rc->oxa - rc->oxc;
+       if (xlen == 0 && !force) {
+               return;
+       }
+       buf = rc->out.vtable->encrypt(&rc->out.vtable,
+               rc->record_type_out, rc->version_out,
+               rc->obuf + rc->oxc, &xlen);
+       rc->oxb = rc->oxa = (size_t)(buf - rc->obuf);
+       rc->oxc = rc->oxa + xlen;
+}
+
+static void
+sendpld_ack(br_ssl_engine_context *rc, size_t len)
+{
+       /*
+        * If using a shared buffer, then we may have to modify the
+        * current mode.
+        */
+       if (rc->iomode == BR_IO_INOUT && rc->ibuf == rc->obuf) {
+               rc->iomode = BR_IO_OUT;
+       }
+       rc->oxa += len;
+       if (rc->oxa >= rc->oxb) {
+               sendpld_flush(rc, 0);
+       }
+}
+
+static unsigned char *
+sendrec_buf(const br_ssl_engine_context *rc, size_t *len)
+{
+       /*
+        * When still gathering payload bytes, oxc points to the start
+        * of the record data, so oxc <= oxa. However, when a full
+        * record has been completed, oxc points to the end of the record,
+        * so oxc > oxa.
+        */
+       switch (rc->iomode) {
+       case BR_IO_OUT:
+       case BR_IO_INOUT:
+               if (rc->oxc > rc->oxa) {
+                       *len = rc->oxc - rc->oxa;
+                       return rc->obuf + rc->oxa;
+               }
+               break;
+       }
+       *len = 0;
+       return NULL;
+}
+
+static void
+sendrec_ack(br_ssl_engine_context *rc, size_t len)
+{
+       rc->oxb = (rc->oxa += len);
+       if (rc->oxa == rc->oxc) {
+               make_ready_out(rc);
+       }
+}
+
+/*
+ * Test whether there is some buffered outgoing record that still must
+ * sent.
+ */
+static inline int
+has_rec_tosend(const br_ssl_engine_context *rc)
+{
+       return rc->oxa == rc->oxb && rc->oxa != rc->oxc;
+}
+
+/*
+ * The "no encryption" mode has no overhead. It limits the payload size
+ * to the maximum size allowed by the standard (16384 bytes); the caller
+ * is responsible for possibly enforcing a smaller fragment length.
+ */
+static void
+clear_max_plaintext(const br_sslrec_out_clear_context *cc,
+       size_t *start, size_t *end)
+{
+       size_t len;
+
+       (void)cc;
+       len = *end - *start;
+       if (len > 16384) {
+               *end = *start + 16384;
+       }
+}
+
+/*
+ * In "no encryption" mode, encryption is trivial (a no-operation) so
+ * we just have to encode the header.
+ */
+static unsigned char *
+clear_encrypt(br_sslrec_out_clear_context *cc,
+       int record_type, unsigned version, void *data, size_t *data_len)
+{
+       unsigned char *buf;
+
+       (void)cc;
+       buf = (unsigned char *)data - 5;
+       buf[0] = record_type;
+       br_enc16be(buf + 1, version);
+       br_enc16be(buf + 3, *data_len);
+       *data_len += 5;
+       return buf;
+}
+
+/* see bearssl_ssl.h */
+const br_sslrec_out_class br_sslrec_out_clear_vtable = {
+       sizeof(br_sslrec_out_clear_context),
+       (void (*)(const br_sslrec_out_class *const *, size_t *, size_t *))
+               &clear_max_plaintext,
+       (unsigned char *(*)(const br_sslrec_out_class **,
+               int, unsigned, void *, size_t *))
+               &clear_encrypt
+};
+
+/* ==================================================================== */
+/*
+ * In this part of the file, we handle the various record types, and
+ * communications with the handshake processor.
+ */
+
+/*
+ * IMPLEMENTATION NOTES
+ * ====================
+ *
+ * The handshake processor is written in T0 and runs as a coroutine.
+ * It receives the contents of all records except application data, and
+ * is responsible for producing the contents of all records except
+ * application data.
+ *
+ * A state flag is maintained, which specifies whether application data
+ * is acceptable or not. When it is set:
+ *
+ * -- Application data can be injected as payload data (provided that
+ *    the output buffer is ready for that).
+ *
+ * -- Incoming application data records are accepted, and yield data
+ *    that the caller may retrieve.
+ *
+ * When the flag is cleared, application data is not accepted from the
+ * application, and incoming application data records trigger an error.
+ *
+ *
+ * Records of type handshake, alert or change-cipher-spec are handled
+ * by the handshake processor. The handshake processor is written in T0
+ * and runs as a coroutine; it gets invoked whenever one of the following
+ * situations is reached:
+ *
+ * -- An incoming record has type handshake, alert or change-cipher-spec,
+ *    and yields data that can be read (zero-length records are thus
+ *    ignored).
+ *
+ * -- An outgoing record has just finished being sent, and the "application
+ *    data" flag is cleared.
+ *
+ * -- The caller wishes to perform a close (call to br_ssl_engine_close()).
+ *
+ * -- The caller wishes to perform a renegotiation (call to
+ *    br_ssl_engine_renegotiate()).
+ *
+ * Whenever the handshake processor is entered, access to the payload
+ * buffers is provided, along with some information about explicit
+ * closures or renegotiations.
+ */
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_set_suites(br_ssl_engine_context *cc,
+       const uint16_t *suites, size_t suites_num)
+{
+       if ((suites_num * sizeof *suites) > sizeof cc->suites_buf) {
+               br_ssl_engine_fail(cc, BR_ERR_BAD_PARAM);
+               return;
+       }
+       memcpy(cc->suites_buf, suites, suites_num * sizeof *suites);
+       cc->suites_num = suites_num;
+}
+
+/*
+ * Give control to handshake processor. 'action' is 1 for a close,
+ * 2 for a renegotiation, or 0 for a jump due to I/O completion.
+ */
+static void
+jump_handshake(br_ssl_engine_context *cc, int action)
+{
+       /*
+        * We use a loop because the handshake processor actions may
+        * allow for more actions; namely, if the processor reads all
+        * input data, then it may allow for output data to be produced,
+        * in case of a shared in/out buffer.
+        */
+       for (;;) {
+               size_t hlen_in, hlen_out;
+
+               /*
+                * Get input buffer. We do not want to provide
+                * application data to the handshake processor (we could
+                * get called with an explicit close or renegotiation
+                * while there is application data ready to be read).
+                */
+               cc->hbuf_in = recvpld_buf(cc, &hlen_in);
+               if (cc->hbuf_in != NULL
+                       && cc->record_type_in == BR_SSL_APPLICATION_DATA)
+               {
+                       hlen_in = 0;
+               }
+
+               /*
+                * Get output buffer. The handshake processor never
+                * leaves an unfinished outgoing record, so if there is
+                * buffered output, then it MUST be some application
+                * data, so the processor cannot write to it.
+                */
+               cc->saved_hbuf_out = cc->hbuf_out = sendpld_buf(cc, &hlen_out);
+               if (cc->hbuf_out != NULL && br_ssl_engine_has_pld_to_send(cc)) {
+                       hlen_out = 0;
+               }
+
+               /*
+                * Note: hlen_in and hlen_out can be both non-zero only if
+                * the input and output buffers are disjoint. Thus, we can
+                * offer both buffers to the handshake code.
+                */
+
+               cc->hlen_in = hlen_in;
+               cc->hlen_out = hlen_out;
+               cc->action = action;
+               cc->hsrun(&cc->cpu);
+               if (cc->hbuf_out != cc->saved_hbuf_out) {
+                       sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out);
+               }
+               if (hlen_in != cc->hlen_in) {
+                       recvpld_ack(cc, hlen_in - cc->hlen_in);
+                       if (cc->hlen_in == 0) {
+                               /*
+                                * We read all data bytes, which may have
+                                * released the output buffer in case it
+                                * is shared with the input buffer, and
+                                * the handshake code might be waiting for
+                                * that.
+                                */
+                               action = 0;
+                               continue;
+                       }
+               }
+               break;
+       }
+}
+
+/* see inner.h */
+void
+br_ssl_engine_flush_record(br_ssl_engine_context *cc)
+{
+       if (cc->hbuf_out != cc->saved_hbuf_out) {
+               sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out);
+       }
+       if (br_ssl_engine_has_pld_to_send(cc)) {
+               sendpld_flush(cc, 0);
+       }
+       cc->saved_hbuf_out = cc->hbuf_out = sendpld_buf(cc, &cc->hlen_out);
+}
+
+/* see bearssl_ssl.h */
+unsigned char *
+br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len)
+{
+       if (!cc->application_data) {
+               *len = 0;
+               return NULL;
+       }
+       return sendpld_buf(cc, len);
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len)
+{
+       sendpld_ack(cc, len);
+}
+
+/* see bearssl_ssl.h */
+unsigned char *
+br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len)
+{
+       if (!cc->application_data
+               || cc->record_type_in != BR_SSL_APPLICATION_DATA)
+       {
+               *len = 0;
+               return NULL;
+       }
+       return recvpld_buf(cc, len);
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len)
+{
+       recvpld_ack(cc, len);
+}
+
+/* see bearssl_ssl.h */
+unsigned char *
+br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len)
+{
+       return sendrec_buf(cc, len);
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len)
+{
+       sendrec_ack(cc, len);
+       if (len != 0 && !has_rec_tosend(cc)
+               && (cc->record_type_out != BR_SSL_APPLICATION_DATA
+               || cc->application_data == 0))
+       {
+               jump_handshake(cc, 0);
+       }
+}
+
+/* see bearssl_ssl.h */
+unsigned char *
+br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len)
+{
+       return recvrec_buf(cc, len);
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len)
+{
+       unsigned char *buf;
+
+       recvrec_ack(cc, len);
+       if (br_ssl_engine_closed(cc)) {
+               return;
+       }
+
+       /*
+        * We just received some bytes from the peer. This may have
+        * yielded some payload bytes, in which case we must process
+        * them according to the record type.
+        */
+       buf = recvpld_buf(cc, &len);
+       if (buf != NULL) {
+               switch (cc->record_type_in) {
+               case BR_SSL_CHANGE_CIPHER_SPEC:
+               case BR_SSL_ALERT:
+               case BR_SSL_HANDSHAKE:
+                       jump_handshake(cc, 0);
+                       break;
+               case BR_SSL_APPLICATION_DATA:
+                       if (cc->application_data) {
+                               break;
+                       }
+                       /* Fall through */
+               default:
+                       br_ssl_engine_fail(cc, BR_ERR_UNEXPECTED);
+                       break;
+               }
+       }
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_close(br_ssl_engine_context *cc)
+{
+       if (!br_ssl_engine_closed(cc)) {
+               jump_handshake(cc, 1);
+       }
+}
+
+/* see bearssl_ssl.h */
+int
+br_ssl_engine_renegotiate(br_ssl_engine_context *cc)
+{
+       if (br_ssl_engine_closed(cc) || cc->reneg == 1) {
+               return 0;
+       }
+       jump_handshake(cc, 2);
+       return 1;
+}
+
+/* see bearssl.h */
+unsigned
+br_ssl_engine_current_state(const br_ssl_engine_context *cc)
+{
+       unsigned s;
+       size_t len;
+
+       if (br_ssl_engine_closed(cc)) {
+               return BR_SSL_CLOSED;
+       }
+
+       s = 0;
+       if (br_ssl_engine_sendrec_buf(cc, &len) != NULL) {
+               s |= BR_SSL_SENDREC;
+       }
+       if (br_ssl_engine_recvrec_buf(cc, &len) != NULL) {
+               s |= BR_SSL_RECVREC;
+       }
+       if (br_ssl_engine_sendapp_buf(cc, &len) != NULL) {
+               s |= BR_SSL_SENDAPP;
+       }
+       if (br_ssl_engine_recvapp_buf(cc, &len) != NULL) {
+               s |= BR_SSL_RECVAPP;
+       }
+       return s;
+}
+
+/* see bearssl_ssl.h */
+void
+br_ssl_engine_flush(br_ssl_engine_context *cc, int force)
+{
+       if (!br_ssl_engine_closed(cc) && cc->application_data) {
+               sendpld_flush(cc, force);
+       }
+}
+
+/* see inner.h */
+void
+br_ssl_engine_hs_reset(br_ssl_engine_context *cc,
+       void (*hsinit)(void *), void (*hsrun)(void *))
+{
+       engine_clearbuf(cc);
+       cc->cpu.dp = cc->dp_stack;
+       cc->cpu.rp = cc->rp_stack;
+       hsinit(&cc->cpu);
+       cc->hsrun = hsrun;
+       cc->shutdown_recv = 0;
+       cc->application_data = 0;
+       jump_handshake(cc, 0);
+}
+
+/* see inner.h */
+br_tls_prf_impl
+br_ssl_engine_get_PRF(br_ssl_engine_context *cc, int prf_id)
+{
+       if (cc->session.version >= BR_TLS12) {
+               if (prf_id == br_sha384_ID) {
+                       return cc->prf_sha384;
+               } else {
+                       return cc->prf_sha256;
+               }
+       } else {
+               return cc->prf10;
+       }
+}
+
+/* see inner.h */
+void
+br_ssl_engine_compute_master(br_ssl_engine_context *cc,
+       int prf_id, const void *pms, size_t pms_len)
+{
+       br_tls_prf_impl iprf;
+       unsigned char seed[64];
+
+       iprf = br_ssl_engine_get_PRF(cc, prf_id);
+       memcpy(seed, cc->client_random, 32);
+       memcpy(seed + 32, cc->server_random, 32);
+       iprf(cc->session.master_secret, sizeof cc->session.master_secret,
+               pms, pms_len, "master secret", seed, sizeof seed);
+}
+
+/*
+ * Compute key block.
+ */
+static void
+compute_key_block(br_ssl_engine_context *cc, int prf_id,
+       size_t half_len, unsigned char *kb)
+{
+       br_tls_prf_impl iprf;
+       unsigned char seed[64];
+
+       iprf = br_ssl_engine_get_PRF(cc, prf_id);
+       memcpy(seed, cc->server_random, 32);
+       memcpy(seed + 32, cc->client_random, 32);
+       iprf(kb, half_len << 1,
+               cc->session.master_secret, sizeof cc->session.master_secret,
+               "key expansion", seed, sizeof seed);
+}
+
+/* see inner.h */
+void
+br_ssl_engine_switch_cbc_in(br_ssl_engine_context *cc,
+       int is_client, int prf_id, int mac_id,
+       const br_block_cbcdec_class *bc_impl, size_t cipher_key_len)
+{
+       unsigned char kb[192];
+       unsigned char *cipher_key, *mac_key, *iv;
+       const br_hash_class *imh;
+       size_t mac_key_len, mac_out_len, iv_len;
+
+       imh = br_ssl_engine_get_hash(cc, mac_id);
+       mac_out_len = (imh->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK;
+       mac_key_len = mac_out_len;
+
+       /*
+        * TLS 1.1+ uses per-record explicit IV, so no IV to generate here.
+        */
+       if (cc->session.version >= BR_TLS11) {
+               iv_len = 0;
+       } else {
+               iv_len = bc_impl->block_size;
+       }
+       compute_key_block(cc, prf_id,
+               mac_key_len + cipher_key_len + iv_len, kb);
+       if (is_client) {
+               mac_key = &kb[mac_key_len];
+               cipher_key = &kb[(mac_key_len << 1) + cipher_key_len];
+               iv = &kb[((mac_key_len + cipher_key_len) << 1) + iv_len];
+       } else {
+               mac_key = &kb[0];
+               cipher_key = &kb[mac_key_len << 1];
+               iv = &kb[(mac_key_len + cipher_key_len) << 1];
+       }
+       if (iv_len == 0) {
+               iv = NULL;
+       }
+       cc->icbc_in->init(&cc->in.cbc.vtable,
+               bc_impl, cipher_key, cipher_key_len,
+               imh, mac_key, mac_key_len, mac_out_len, iv);
+       cc->incrypt = 1;
+}
+
+/* see inner.h */
+void
+br_ssl_engine_switch_cbc_out(br_ssl_engine_context *cc,
+       int is_client, int prf_id, int mac_id,
+       const br_block_cbcenc_class *bc_impl, size_t cipher_key_len)
+{
+       unsigned char kb[192];
+       unsigned char *cipher_key, *mac_key, *iv;
+       const br_hash_class *imh;
+       size_t mac_key_len, mac_out_len, iv_len;
+
+       imh = br_ssl_engine_get_hash(cc, mac_id);
+       mac_out_len = (imh->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK;
+       mac_key_len = mac_out_len;
+
+       /*
+        * TLS 1.1+ uses per-record explicit IV, so no IV to generate here.
+        */
+       if (cc->session.version >= BR_TLS11) {
+               iv_len = 0;
+       } else {
+               iv_len = bc_impl->block_size;
+       }
+       compute_key_block(cc, prf_id,
+               mac_key_len + cipher_key_len + iv_len, kb);
+       if (is_client) {
+               mac_key = &kb[0];
+               cipher_key = &kb[mac_key_len << 1];
+               iv = &kb[(mac_key_len + cipher_key_len) << 1];
+       } else {
+               mac_key = &kb[mac_key_len];
+               cipher_key = &kb[(mac_key_len << 1) + cipher_key_len];
+               iv = &kb[((mac_key_len + cipher_key_len) << 1) + iv_len];
+       }
+       if (iv_len == 0) {
+               iv = NULL;
+       }
+       cc->icbc_out->init(&cc->out.cbc.vtable,
+               bc_impl, cipher_key, cipher_key_len,
+               imh, mac_key, mac_key_len, mac_out_len, iv);
+}
+
+/* see inner.h */
+void
+br_ssl_engine_switch_gcm_in(br_ssl_engine_context *cc,
+       int is_client, int prf_id,
+       const br_block_ctr_class *bc_impl, size_t cipher_key_len)
+{
+       unsigned char kb[72];
+       unsigned char *cipher_key, *iv;
+
+       compute_key_block(cc, prf_id, cipher_key_len + 4, kb);
+       if (is_client) {
+               cipher_key = &kb[cipher_key_len];
+               iv = &kb[(cipher_key_len << 1) + 4];
+       } else {
+               cipher_key = &kb[0];
+               iv = &kb[cipher_key_len << 1];
+       }
+       cc->igcm_in->init(&cc->in.gcm.vtable.in,
+               bc_impl, cipher_key, cipher_key_len, cc->ighash, iv);
+       cc->incrypt = 1;
+}
+
+/* see inner.h */
+void
+br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc,
+       int is_client, int prf_id,
+       const br_block_ctr_class *bc_impl, size_t cipher_key_len)
+{
+       unsigned char kb[72];
+       unsigned char *cipher_key, *iv;
+
+       compute_key_block(cc, prf_id, cipher_key_len + 4, kb);
+       if (is_client) {
+               cipher_key = &kb[0];
+               iv = &kb[cipher_key_len << 1];
+       } else {
+               cipher_key = &kb[cipher_key_len];
+               iv = &kb[(cipher_key_len << 1) + 4];
+       }
+       cc->igcm_out->init(&cc->out.gcm.vtable.out,
+               bc_impl, cipher_key, cipher_key_len, cc->ighash, iv);
+}
diff --git a/src/ssl/ssl_hashes.c b/src/ssl/ssl_hashes.c
new file mode 100644 (file)
index 0000000..e10a980
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+int
+br_ssl_choose_hash(unsigned bf)
+{
+       static const unsigned char pref[] = {
+               br_sha256_ID, br_sha384_ID, br_sha512_ID,
+               br_sha224_ID, br_sha1_ID
+       };
+       size_t u;
+
+       for (u = 0; u < sizeof pref; u ++) {
+               int x;
+
+               x = pref[u];
+               if ((bf >> x) & 1) {
+                       return x;
+               }
+       }
+       return 0;
+}
diff --git a/src/ssl/ssl_hs_client.c b/src/ssl/ssl_hs_client.c
new file mode 100644 (file)
index 0000000..16c708a
--- /dev/null
@@ -0,0 +1,1473 @@
+/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+
+
+void br_ssl_hs_client_init_main(void *t0ctx);
+
+void br_ssl_hs_client_run(void *t0ctx);
+
+
+
+#include <stddef.h>
+#include <string.h>
+
+#include "inner.h"
+
+/*
+ * This macro evaluates to a pointer to the current engine context.
+ */
+#define ENG  ((br_ssl_engine_context *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu)))
+
+
+
+
+
+/*
+ * This macro evaluates to a pointer to the client context, under that
+ * specific name. It must be noted that since the engine context is the
+ * first field of the br_ssl_client_context structure ('eng'), then
+ * pointers values of both types are interchangeable, modulo an
+ * appropriate cast. This also means that "adresses" computed as offsets
+ * within the structure work for both kinds of context.
+ */
+#define CTX  ((br_ssl_client_context *)ENG)
+
+/*
+ * Generate the pre-master secret for RSA key exchange, and encrypt it
+ * with the server's public key. Returned value is either the encrypted
+ * data length (in bytes), or -x on error, with 'x' being an error code.
+ *
+ * This code assumes that the public key has been already verified (it
+ * was properly obtained by the X.509 engine, and it has the right type,
+ * i.e. it is of type RSA and suitable for encryption).
+ */
+static int
+make_pms_rsa(br_ssl_client_context *ctx, int prf_id)
+{
+       const br_x509_class **xc;
+       const br_x509_pkey *pk;
+       const unsigned char *n;
+       unsigned char *pms;
+       size_t nlen, u;
+
+       xc = ctx->eng.x509ctx;
+       pk = (*xc)->get_pkey(xc);
+
+       /*
+        * Compute actual RSA key length, in case there are leading zeros.
+        */
+       n = pk->key.rsa.n;
+       nlen = pk->key.rsa.nlen;
+       while (nlen > 0 && *n == 0) {
+               n ++;
+               nlen --;
+       }
+
+       /*
+        * We need at least 59 bytes (48 bytes for pre-master secret, and
+        * 11 bytes for the PKCS#1 type 2 padding). Note that the X.509
+        * minimal engine normally blocks RSA keys shorter than 128 bytes,
+        * so this is mostly for public keys provided explicitly by the
+        * caller.
+        */
+       if (nlen < 59) {
+               return -BR_ERR_X509_WEAK_PUBLIC_KEY;
+       }
+       if (nlen > sizeof ctx->eng.pad) {
+               return -BR_ERR_LIMIT_EXCEEDED;
+       }
+
+       /*
+        * Make PMS.
+        */
+       pms = ctx->eng.pad + nlen - 48;
+       br_enc16be(pms, ctx->eng.version_max);
+       br_hmac_drbg_generate(&ctx->eng.rng, pms + 2, 46);
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, pms, 48);
+
+       /*
+        * Apply PKCS#1 type 2 padding.
+        */
+       ctx->eng.pad[0] = 0x00;
+       ctx->eng.pad[1] = 0x02;
+       ctx->eng.pad[nlen - 49] = 0x00;
+       br_hmac_drbg_generate(&ctx->eng.rng, ctx->eng.pad + 2, nlen - 51);
+       for (u = 2; u < nlen - 49; u ++) {
+               while (ctx->eng.pad[u] == 0) {
+                       br_hmac_drbg_generate(&ctx->eng.rng,
+                               &ctx->eng.pad[u], 1);
+               }
+       }
+
+       /*
+        * Compute RSA encryption.
+        */
+       if (!ctx->irsapub(ctx->eng.pad, nlen, &pk->key.rsa)) {
+               return -BR_ERR_LIMIT_EXCEEDED;
+       }
+       return (int)nlen;
+}
+
+/*
+ * OID for hash functions in RSA signatures.
+ */
+static const unsigned char HASH_OID_SHA1[] = {
+       0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
+};
+
+static const unsigned char HASH_OID_SHA224[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
+};
+
+static const unsigned char HASH_OID_SHA256[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+static const unsigned char HASH_OID_SHA384[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+static const unsigned char HASH_OID_SHA512[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+static const unsigned char *HASH_OID[] = {
+       HASH_OID_SHA1,
+       HASH_OID_SHA224,
+       HASH_OID_SHA256,
+       HASH_OID_SHA384,
+       HASH_OID_SHA512
+};
+
+/*
+ * Check the RSA signature on the ServerKeyExchange message.
+ *   hash      hash function ID (2 to 6), or 0 for MD5+SHA-1 (with RSA only)
+ *   use_rsa   non-zero for RSA signature, zero for ECDSA
+ *   sig_len   signature length (in bytes); signature value is in the pad
+ * Returned value is 0 on success, or an error code.
+ */
+static int
+verify_SKE_sig(br_ssl_client_context *ctx,
+       int hash, int use_rsa, size_t sig_len)
+{
+       const br_x509_class **xc;
+       const br_x509_pkey *pk;
+       br_multihash_context mhc;
+       unsigned char hv[64], head[4];
+       size_t hv_len;
+
+       xc = ctx->eng.x509ctx;
+       pk = (*xc)->get_pkey(xc);
+       br_multihash_zero(&mhc);
+       br_multihash_copyimpl(&mhc, &ctx->eng.mhash);
+       br_multihash_init(&mhc);
+       br_multihash_update(&mhc,
+               ctx->eng.client_random, sizeof ctx->eng.client_random);
+       br_multihash_update(&mhc,
+               ctx->eng.server_random, sizeof ctx->eng.server_random);
+       head[0] = 3;
+       head[1] = 0;
+       head[2] = ctx->eng.ecdhe_curve;
+       head[3] = ctx->eng.ecdhe_point_len;
+       br_multihash_update(&mhc, head, sizeof head);
+       br_multihash_update(&mhc,
+               ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len);
+       if (hash) {
+               hv_len = br_multihash_out(&mhc, hash, hv);
+               if (hv_len == 0) {
+                       return BR_ERR_INVALID_ALGORITHM;
+               }
+       } else {
+               if (!br_multihash_out(&mhc, br_md5_ID, hv)
+                       || !br_multihash_out(&mhc, br_sha1_ID, hv + 16))
+               {
+                       return BR_ERR_INVALID_ALGORITHM;
+               }
+               hv_len = 36;
+       }
+       if (use_rsa) {
+               unsigned char tmp[64];
+               const unsigned char *hash_oid;
+
+               if (hash) {
+                       hash_oid = HASH_OID[hash - 2];
+               } else {
+                       hash_oid = NULL;
+               }
+               if (!ctx->irsavrfy(ctx->eng.pad, sig_len,
+                       hash_oid, hv_len, &pk->key.rsa, tmp)
+                       || memcmp(tmp, hv, hv_len) != 0)
+               {
+                       return BR_ERR_BAD_SIGNATURE;
+               }
+       } else {
+               if (!ctx->iecdsa(ctx->eng.iec, hv, hv_len, &pk->key.ec,
+                       ctx->eng.pad, sig_len))
+               {
+                       return BR_ERR_BAD_SIGNATURE;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Perform client-size ECDH (or ECDHE). The point that should be sent to
+ * the server is written in the pad; returned value is either the point
+ * length (in bytes), or -x on error, with 'x' being an error code.
+ *
+ * The point _from_ the server is taken from ecdhe_point[] if 'ecdhe'
+ * is non-zero, or from the X.509 engine context if 'ecdhe' is zero
+ * (for static ECDH).
+ */
+static int
+make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id)
+{
+       int curve;
+       unsigned char key[66], point[133];
+       const unsigned char *generator, *order, *point_src;
+       size_t glen, olen, point_len;
+       unsigned char mask;
+
+       if (ecdhe) {
+               curve = ctx->eng.ecdhe_curve;
+               point_src = ctx->eng.ecdhe_point;
+               point_len = ctx->eng.ecdhe_point_len;
+       } else {
+               const br_x509_class **xc;
+               const br_x509_pkey *pk;
+
+               xc = ctx->eng.x509ctx;
+               pk = (*xc)->get_pkey(xc);
+               curve = pk->key.ec.curve;
+               point_src = pk->key.ec.q;
+               point_len = pk->key.ec.qlen;
+       }
+       if ((ctx->eng.iec->supported_curves & ((uint32_t)1 << curve)) == 0) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       /*
+        * We need to generate our key, as a non-zero random value which
+        * is lower than the curve order, in a "large enough" range. We
+        * force top bit to 0 and bottom bit to 1, which guarantees that
+        * the value is in the proper range.
+        */
+       order = ctx->eng.iec->order(curve, &olen);
+       mask = 0xFF;
+       while (mask >= order[0]) {
+               mask >>= 1;
+       }
+       br_hmac_drbg_generate(&ctx->eng.rng, key, olen);
+       key[0] &= mask;
+       key[olen - 1] |= 0x01;
+
+       /*
+        * Compute the common ECDH point, whose X coordinate is the
+        * pre-master secret.
+        */
+       generator = ctx->eng.iec->generator(curve, &glen);
+       if (glen != point_len) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       memcpy(point, point_src, glen);
+       if (!ctx->eng.iec->mul(point, glen, key, olen, curve)) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       /*
+        * The pre-master secret is the X coordinate.
+        */
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, point + 1, glen >> 1);
+
+       memcpy(point, generator, glen);
+       if (!ctx->eng.iec->mul(point, glen, key, olen, curve)) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+       memcpy(ctx->eng.pad, point, glen);
+       return (int)glen;
+}
+
+
+
+static const uint8_t t0_datablock[] = {
+       0x00, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x2F, 0x01, 0x24, 0x00, 0x35, 0x02,
+       0x24, 0x00, 0x3C, 0x01, 0x44, 0x00, 0x3D, 0x02, 0x44, 0x00, 0x9C, 0x03,
+       0x04, 0x00, 0x9D, 0x04, 0x05, 0xC0, 0x03, 0x40, 0x24, 0xC0, 0x04, 0x41,
+       0x24, 0xC0, 0x05, 0x42, 0x24, 0xC0, 0x08, 0x20, 0x24, 0xC0, 0x09, 0x21,
+       0x24, 0xC0, 0x0A, 0x22, 0x24, 0xC0, 0x0D, 0x30, 0x24, 0xC0, 0x0E, 0x31,
+       0x24, 0xC0, 0x0F, 0x32, 0x24, 0xC0, 0x12, 0x10, 0x24, 0xC0, 0x13, 0x11,
+       0x24, 0xC0, 0x14, 0x12, 0x24, 0xC0, 0x23, 0x21, 0x44, 0xC0, 0x24, 0x22,
+       0x55, 0xC0, 0x25, 0x41, 0x44, 0xC0, 0x26, 0x42, 0x55, 0xC0, 0x27, 0x11,
+       0x44, 0xC0, 0x28, 0x12, 0x55, 0xC0, 0x29, 0x31, 0x44, 0xC0, 0x2A, 0x32,
+       0x55, 0xC0, 0x2B, 0x23, 0x04, 0xC0, 0x2C, 0x24, 0x05, 0xC0, 0x2D, 0x43,
+       0x04, 0xC0, 0x2E, 0x44, 0x05, 0xC0, 0x2F, 0x13, 0x04, 0xC0, 0x30, 0x14,
+       0x05, 0xC0, 0x31, 0x33, 0x04, 0xC0, 0x32, 0x34, 0x05, 0xCC, 0xA8, 0x15,
+       0x04, 0xCC, 0xA9, 0x25, 0x04, 0x00, 0x00
+};
+
+static const uint8_t t0_codeblock[] = {
+       0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x01,
+       0x00, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x01, 0x01, 0x08,
+       0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x02, 0x08, 0x00, 0x00,
+       0x01, 0x02, 0x09, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_CCS), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_CIPHER_SUITE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_COMPRESSION), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_FINISHED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_FRAGLEN), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_HANDSHAKE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_HELLO_DONE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_PARAM), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_SECRENEG), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_SNI), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_BAD_VERSION),
+       0x00, 0x00, 0x01, T0_INT1(BR_ERR_EXTRA_EXTENSION), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_INVALID_ALGORITHM), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OK),
+       0x00, 0x00, 0x01, T0_INT1(BR_ERR_OVERSIZED_ID), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_RESUME_MISMATCH), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_UNEXPECTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_UNSUPPORTED_VERSION), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, action)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, alert)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, application_data)), 0x00, 0x00,
+       0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, cipher_suite)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, client_random)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, close_received)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_curve)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point_len)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, log_max_frag_len)),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, pad)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, record_type_in)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, record_type_out)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, reneg)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, saved_finished)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, server_name)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, server_random)), 0x00, 0x00,
+       0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id_len)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, shutdown_recv)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_buf)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_num)), 0x00, 0x00,
+       0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, version)),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_in)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, version_max)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, version_min)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_out)),
+       0x00, 0x00, 0x09, 0x1B, 0x40, 0x06, 0x02, 0x50, 0x1C, 0x00, 0x00, 0x06,
+       0x08, 0x1E, 0x0D, 0x05, 0x02, 0x59, 0x1C, 0x04, 0x01, 0x2B, 0x00, 0x00,
+       0x01, 0x01, 0x00, 0x01, 0x03, 0x00, 0x79, 0x1B, 0x46, 0x32, 0x7D, 0x1B,
+       0x05, 0x04, 0x48, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0D, 0x06, 0x02, 0x7D,
+       0x00, 0x46, 0x04, 0x6B, 0x00, 0x06, 0x02, 0x50, 0x1C, 0x00, 0x00, 0x1B,
+       0x6A, 0x32, 0x05, 0x03, 0x01, 0x0C, 0x08, 0x32, 0x5F, 0x1E, 0x81, 0x0A,
+       0x15, 0x66, 0x01, 0x0C, 0x22, 0x00, 0x00, 0x1B, 0x16, 0x01, 0x08, 0x0B,
+       0x32, 0x44, 0x16, 0x08, 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x5E, 0x2C,
+       0x1D, 0x13, 0x26, 0x06, 0x08, 0x02, 0x00, 0x81, 0x29, 0x03, 0x00, 0x04,
+       0x74, 0x01, 0x00, 0x81, 0x21, 0x02, 0x00, 0x1B, 0x13, 0x11, 0x06, 0x02,
+       0x57, 0x1C, 0x81, 0x29, 0x04, 0x75, 0x01, 0x01, 0x00, 0x5E, 0x2C, 0x01,
+       0x16, 0x68, 0x2C, 0x25, 0x81, 0x2D, 0x1D, 0x81, 0x11, 0x06, 0x0B, 0x01,
+       0x7F, 0x81, 0x0D, 0x01, 0x7F, 0x81, 0x2C, 0x04, 0x80, 0x42, 0x81, 0x0E,
+       0x5F, 0x1E, 0x81, 0x01, 0x01, T0_INT1(BR_KEYTYPE_SIGN), 0x11, 0x06,
+       0x02, 0x81, 0x12, 0x81, 0x15, 0x1B, 0x01, 0x0D, 0x0D, 0x06, 0x09, 0x1A,
+       0x81, 0x14, 0x81, 0x15, 0x01, 0x7F, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00,
+       0x01, 0x0E, 0x0D, 0x05, 0x02, 0x5A, 0x1C, 0x06, 0x02, 0x4F, 0x1C, 0x24,
+       0x06, 0x02, 0x5A, 0x1C, 0x02, 0x00, 0x06, 0x02, 0x81, 0x33, 0x81, 0x2E,
+       0x01, 0x7F, 0x81, 0x2C, 0x01, 0x7F, 0x81, 0x0D, 0x01, 0x01, 0x5E, 0x2C,
+       0x01, 0x17, 0x68, 0x2C, 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x7A, 0x01,
+       0x0C, 0x10, 0x01, 0x00, 0x28, 0x0D, 0x06, 0x05, 0x1A, 0x01,
+       T0_INT1(BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX), 0x04, 0x30, 0x01, 0x01,
+       0x28, 0x0D, 0x06, 0x05, 0x1A, 0x01,
+       T0_INT1(BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN), 0x04, 0x25, 0x01, 0x02,
+       0x28, 0x0D, 0x06, 0x05, 0x1A, 0x01,
+       T0_INT1(BR_KEYTYPE_EC  | BR_KEYTYPE_SIGN), 0x04, 0x1A, 0x01, 0x03,
+       0x28, 0x0D, 0x06, 0x05, 0x1A, 0x01,
+       T0_INT1(BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX), 0x04, 0x0F, 0x01, 0x04,
+       0x28, 0x0D, 0x06, 0x05, 0x1A, 0x01,
+       T0_INT1(BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX), 0x04, 0x04, 0x01, 0x00,
+       0x32, 0x1A, 0x00, 0x00, 0x65, 0x1F, 0x01, 0x0E, 0x0D, 0x06, 0x04, 0x01,
+       0x00, 0x04, 0x02, 0x01, 0x05, 0x00, 0x00, 0x2E, 0x06, 0x04, 0x01, 0x06,
+       0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x69, 0x1F, 0x1B, 0x06, 0x08, 0x01,
+       0x01, 0x09, 0x01, 0x11, 0x07, 0x04, 0x03, 0x1A, 0x01, 0x05, 0x00, 0x01,
+       0x2F, 0x03, 0x00, 0x1A, 0x01, 0x00, 0x31, 0x06, 0x03, 0x02, 0x00, 0x08,
+       0x30, 0x06, 0x03, 0x02, 0x00, 0x08, 0x1B, 0x06, 0x06, 0x01, 0x01, 0x0B,
+       0x01, 0x06, 0x08, 0x00, 0x00, 0x6B, 0x2D, 0x1B, 0x06, 0x03, 0x01, 0x09,
+       0x08, 0x00, 0x01, 0x2E, 0x1B, 0x06, 0x1E, 0x01, 0x00, 0x03, 0x00, 0x1B,
+       0x06, 0x0E, 0x1B, 0x01, 0x01, 0x11, 0x02, 0x00, 0x08, 0x03, 0x00, 0x01,
+       0x01, 0x10, 0x04, 0x6F, 0x1A, 0x02, 0x00, 0x01, 0x01, 0x0B, 0x01, 0x06,
+       0x08, 0x00, 0x00, 0x7F, 0x81, 0x28, 0x1B, 0x01, 0x07, 0x11, 0x01, 0x00,
+       0x28, 0x0D, 0x06, 0x09, 0x1A, 0x01, 0x10, 0x11, 0x06, 0x01, 0x7F, 0x04,
+       0x2C, 0x01, 0x01, 0x28, 0x0D, 0x06, 0x23, 0x1A, 0x1A, 0x01, 0x00, 0x5E,
+       0x2C, 0x81, 0x10, 0x69, 0x1F, 0x01, 0x01, 0x0D, 0x06, 0x11, 0x1D, 0x13,
+       0x26, 0x06, 0x05, 0x81, 0x28, 0x1A, 0x04, 0x77, 0x01, 0x80, 0x64, 0x81,
+       0x21, 0x04, 0x01, 0x7F, 0x04, 0x03, 0x5A, 0x1C, 0x1A, 0x04, 0xFF, 0x3C,
+       0x01, 0x1B, 0x03, 0x00, 0x09, 0x1B, 0x40, 0x06, 0x02, 0x50, 0x1C, 0x02,
+       0x00, 0x00, 0x00, 0x7A, 0x01, 0x0F, 0x11, 0x00, 0x00, 0x5D, 0x1F, 0x01,
+       0x00, 0x28, 0x0D, 0x06, 0x10, 0x1A, 0x1B, 0x01, 0x01, 0x0C, 0x06, 0x03,
+       0x1A, 0x01, 0x02, 0x5D, 0x2C, 0x01, 0x00, 0x04, 0x15, 0x01, 0x01, 0x28,
+       0x0D, 0x06, 0x09, 0x1A, 0x01, 0x00, 0x5D, 0x2C, 0x42, 0x00, 0x04, 0x06,
+       0x01, 0x82, 0x00, 0x08, 0x1C, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x20, 0x06,
+       0x06, 0x2A, 0x81, 0x0B, 0x27, 0x04, 0x77, 0x1B, 0x06, 0x04, 0x01, 0x01,
+       0x6F, 0x2C, 0x00, 0x00, 0x20, 0x06, 0x0B, 0x67, 0x1F, 0x01, 0x14, 0x0C,
+       0x06, 0x02, 0x5A, 0x1C, 0x04, 0x12, 0x81, 0x28, 0x01, 0x07, 0x11, 0x1B,
+       0x01, 0x02, 0x0C, 0x06, 0x06, 0x06, 0x02, 0x5A, 0x1C, 0x04, 0x6F, 0x1A,
+       0x81, 0x1E, 0x01, 0x01, 0x0C, 0x24, 0x27, 0x06, 0x02, 0x49, 0x1C, 0x1B,
+       0x01, 0x01, 0x81, 0x24, 0x26, 0x81, 0x0F, 0x00, 0x01, 0x81, 0x15, 0x01,
+       0x0B, 0x0D, 0x05, 0x02, 0x5A, 0x1C, 0x5F, 0x1E, 0x81, 0x01, 0x3F, 0x81,
+       0x1C, 0x81, 0x09, 0x1B, 0x06, 0x26, 0x81, 0x1C, 0x81, 0x09, 0x1B, 0x3E,
+       0x1B, 0x06, 0x19, 0x1B, 0x01, 0x82, 0x00, 0x0E, 0x06, 0x05, 0x01, 0x82,
+       0x00, 0x04, 0x01, 0x1B, 0x03, 0x00, 0x66, 0x02, 0x00, 0x81, 0x13, 0x02,
+       0x00, 0x3B, 0x04, 0x64, 0x7B, 0x3C, 0x04, 0x57, 0x7B, 0x7B, 0x3D, 0x1B,
+       0x06, 0x01, 0x1C, 0x1A, 0x00, 0x00, 0x7C, 0x81, 0x15, 0x01, 0x14, 0x0C,
+       0x06, 0x02, 0x5A, 0x1C, 0x66, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0x81, 0x13,
+       0x7B, 0x66, 0x1B, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0x21, 0x05, 0x02, 0x4C,
+       0x1C, 0x00, 0x00, 0x81, 0x16, 0x06, 0x02, 0x5A, 0x1C, 0x06, 0x02, 0x4E,
+       0x1C, 0x00, 0x09, 0x81, 0x15, 0x01, 0x02, 0x0D, 0x05, 0x02, 0x5A, 0x1C,
+       0x81, 0x1B, 0x03, 0x00, 0x02, 0x00, 0x75, 0x1E, 0x0A, 0x02, 0x00, 0x74,
+       0x1E, 0x0E, 0x27, 0x06, 0x02, 0x5B, 0x1C, 0x02, 0x00, 0x73, 0x1E, 0x0C,
+       0x06, 0x02, 0x53, 0x1C, 0x02, 0x00, 0x76, 0x2B, 0x6C, 0x01, 0x20, 0x81,
+       0x13, 0x01, 0x00, 0x03, 0x01, 0x81, 0x1D, 0x03, 0x02, 0x02, 0x02, 0x01,
+       0x20, 0x0E, 0x06, 0x02, 0x58, 0x1C, 0x66, 0x02, 0x02, 0x81, 0x13, 0x02,
+       0x02, 0x6E, 0x1F, 0x0D, 0x02, 0x02, 0x01, 0x00, 0x0E, 0x11, 0x06, 0x0B,
+       0x6D, 0x66, 0x02, 0x02, 0x21, 0x06, 0x04, 0x01, 0x7F, 0x03, 0x01, 0x6D,
+       0x66, 0x02, 0x02, 0x22, 0x02, 0x02, 0x6E, 0x2C, 0x02, 0x00, 0x72, 0x02,
+       0x01, 0x78, 0x81, 0x1B, 0x1B, 0x81, 0x1F, 0x40, 0x06, 0x02, 0x4A, 0x1C,
+       0x5F, 0x02, 0x01, 0x78, 0x81, 0x1D, 0x06, 0x02, 0x4B, 0x1C, 0x1B, 0x06,
+       0x81, 0x3D, 0x81, 0x1B, 0x81, 0x09, 0x81, 0x06, 0x03, 0x03, 0x81, 0x04,
+       0x03, 0x04, 0x81, 0x02, 0x03, 0x05, 0x81, 0x05, 0x03, 0x06, 0x81, 0x07,
+       0x03, 0x07, 0x81, 0x03, 0x03, 0x08, 0x1B, 0x06, 0x81, 0x0B, 0x81, 0x1B,
+       0x01, 0x00, 0x28, 0x0D, 0x06, 0x10, 0x1A, 0x02, 0x03, 0x05, 0x02, 0x54,
+       0x1C, 0x01, 0x00, 0x03, 0x03, 0x81, 0x1A, 0x04, 0x80, 0x70, 0x01, 0x01,
+       0x28, 0x0D, 0x06, 0x10, 0x1A, 0x02, 0x05, 0x05, 0x02, 0x54, 0x1C, 0x01,
+       0x00, 0x03, 0x05, 0x81, 0x18, 0x04, 0x80, 0x5A, 0x01, 0x83, 0xFE, 0x01,
+       0x28, 0x0D, 0x06, 0x10, 0x1A, 0x02, 0x04, 0x05, 0x02, 0x54, 0x1C, 0x01,
+       0x00, 0x03, 0x04, 0x81, 0x19, 0x04, 0x80, 0x42, 0x01, 0x0D, 0x28, 0x0D,
+       0x06, 0x0F, 0x1A, 0x02, 0x06, 0x05, 0x02, 0x54, 0x1C, 0x01, 0x00, 0x03,
+       0x06, 0x81, 0x17, 0x04, 0x2D, 0x01, 0x0A, 0x28, 0x0D, 0x06, 0x0F, 0x1A,
+       0x02, 0x07, 0x05, 0x02, 0x54, 0x1C, 0x01, 0x00, 0x03, 0x07, 0x81, 0x17,
+       0x04, 0x18, 0x01, 0x0B, 0x28, 0x0D, 0x06, 0x0F, 0x1A, 0x02, 0x08, 0x05,
+       0x02, 0x54, 0x1C, 0x01, 0x00, 0x03, 0x08, 0x81, 0x17, 0x04, 0x03, 0x54,
+       0x1C, 0x1A, 0x04, 0xFE, 0x71, 0x02, 0x04, 0x06, 0x0D, 0x02, 0x04, 0x01,
+       0x05, 0x0E, 0x06, 0x02, 0x51, 0x1C, 0x01, 0x01, 0x69, 0x2C, 0x7B, 0x7B,
+       0x02, 0x01, 0x00, 0x04, 0x81, 0x15, 0x01, 0x0C, 0x0D, 0x05, 0x02, 0x5A,
+       0x1C, 0x81, 0x1D, 0x01, 0x03, 0x0D, 0x05, 0x02, 0x55, 0x1C, 0x81, 0x1B,
+       0x1B, 0x62, 0x2C, 0x1B, 0x01, 0x20, 0x0F, 0x06, 0x02, 0x55, 0x1C, 0x2E,
+       0x32, 0x10, 0x01, 0x01, 0x11, 0x05, 0x02, 0x55, 0x1C, 0x81, 0x1D, 0x1B,
+       0x01, 0x81, 0x05, 0x0E, 0x06, 0x02, 0x55, 0x1C, 0x1B, 0x64, 0x2C, 0x63,
+       0x32, 0x81, 0x13, 0x72, 0x1E, 0x01, 0x86, 0x03, 0x0F, 0x03, 0x00, 0x5F,
+       0x1E, 0x81, 0x26, 0x03, 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x00, 0x06,
+       0x23, 0x81, 0x1D, 0x1B, 0x1B, 0x01, 0x02, 0x0A, 0x32, 0x01, 0x06, 0x0E,
+       0x27, 0x06, 0x02, 0x55, 0x1C, 0x03, 0x02, 0x81, 0x1D, 0x02, 0x01, 0x01,
+       0x01, 0x0B, 0x01, 0x03, 0x08, 0x0D, 0x05, 0x02, 0x55, 0x1C, 0x04, 0x08,
+       0x02, 0x01, 0x06, 0x04, 0x01, 0x00, 0x03, 0x02, 0x81, 0x1B, 0x1B, 0x03,
+       0x03, 0x1B, 0x01, 0x84, 0x00, 0x0E, 0x06, 0x02, 0x56, 0x1C, 0x66, 0x32,
+       0x81, 0x13, 0x02, 0x02, 0x02, 0x01, 0x02, 0x03, 0x38, 0x1B, 0x06, 0x01,
+       0x1C, 0x1A, 0x7B, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x02, 0x00, 0x77,
+       0x02, 0x01, 0x02, 0x00, 0x29, 0x1B, 0x01, 0x00, 0x0D, 0x06, 0x02, 0x48,
+       0x00, 0x81, 0x2A, 0x04, 0x73, 0x00, 0x1B, 0x06, 0x05, 0x81, 0x1D, 0x1A,
+       0x04, 0x78, 0x1A, 0x00, 0x00, 0x81, 0x16, 0x1B, 0x42, 0x06, 0x07, 0x1A,
+       0x06, 0x02, 0x4E, 0x1C, 0x04, 0x73, 0x00, 0x00, 0x81, 0x1E, 0x01, 0x03,
+       0x81, 0x1C, 0x32, 0x1A, 0x32, 0x00, 0x00, 0x81, 0x1B, 0x81, 0x22, 0x00,
+       0x00, 0x81, 0x1B, 0x01, 0x01, 0x0D, 0x05, 0x02, 0x4D, 0x1C, 0x81, 0x1D,
+       0x01, 0x08, 0x08, 0x65, 0x1F, 0x0D, 0x05, 0x02, 0x4D, 0x1C, 0x00, 0x00,
+       0x81, 0x1B, 0x69, 0x1F, 0x05, 0x16, 0x01, 0x01, 0x0D, 0x05, 0x02, 0x51,
+       0x1C, 0x81, 0x1D, 0x01, 0x00, 0x0D, 0x05, 0x02, 0x51, 0x1C, 0x01, 0x02,
+       0x69, 0x2C, 0x04, 0x1E, 0x01, 0x19, 0x0D, 0x05, 0x02, 0x51, 0x1C, 0x81,
+       0x1D, 0x01, 0x18, 0x0D, 0x05, 0x02, 0x51, 0x1C, 0x66, 0x01, 0x18, 0x81,
+       0x13, 0x6A, 0x66, 0x01, 0x18, 0x21, 0x05, 0x02, 0x51, 0x1C, 0x00, 0x00,
+       0x81, 0x1B, 0x06, 0x02, 0x52, 0x1C, 0x00, 0x00, 0x01, 0x02, 0x77, 0x81,
+       0x1E, 0x01, 0x08, 0x0B, 0x81, 0x1E, 0x08, 0x00, 0x00, 0x01, 0x03, 0x77,
+       0x81, 0x1E, 0x01, 0x08, 0x0B, 0x81, 0x1E, 0x08, 0x01, 0x08, 0x0B, 0x81,
+       0x1E, 0x08, 0x00, 0x00, 0x01, 0x01, 0x77, 0x81, 0x1E, 0x00, 0x00, 0x2A,
+       0x1B, 0x40, 0x05, 0x01, 0x00, 0x1A, 0x81, 0x2A, 0x04, 0x75, 0x02, 0x03,
+       0x00, 0x71, 0x1F, 0x03, 0x01, 0x01, 0x00, 0x1B, 0x02, 0x01, 0x0A, 0x06,
+       0x10, 0x1B, 0x01, 0x01, 0x0B, 0x70, 0x08, 0x1E, 0x02, 0x00, 0x0D, 0x06,
+       0x01, 0x00, 0x44, 0x04, 0x6A, 0x1A, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x15,
+       0x68, 0x2C, 0x32, 0x3A, 0x1A, 0x3A, 0x1A, 0x1D, 0x00, 0x00, 0x01, 0x01,
+       0x32, 0x81, 0x20, 0x00, 0x00, 0x32, 0x28, 0x77, 0x32, 0x1B, 0x06, 0x06,
+       0x81, 0x1E, 0x1A, 0x45, 0x04, 0x77, 0x1A, 0x00, 0x00, 0x7A, 0x41, 0x00,
+       0x02, 0x03, 0x00, 0x5F, 0x1E, 0x7A, 0x03, 0x01, 0x02, 0x01, 0x01, 0x0F,
+       0x11, 0x02, 0x01, 0x01, 0x04, 0x10, 0x01, 0x0F, 0x11, 0x02, 0x01, 0x01,
+       0x08, 0x10, 0x01, 0x0F, 0x11, 0x01, 0x00, 0x28, 0x0D, 0x06, 0x10, 0x1A,
+       0x01, 0x00, 0x01, 0x18, 0x02, 0x00, 0x06, 0x03, 0x35, 0x04, 0x01, 0x36,
+       0x04, 0x80, 0x56, 0x01, 0x01, 0x28, 0x0D, 0x06, 0x10, 0x1A, 0x01, 0x01,
+       0x01, 0x10, 0x02, 0x00, 0x06, 0x03, 0x35, 0x04, 0x01, 0x36, 0x04, 0x80,
+       0x40, 0x01, 0x02, 0x28, 0x0D, 0x06, 0x0F, 0x1A, 0x01, 0x01, 0x01, 0x20,
+       0x02, 0x00, 0x06, 0x03, 0x35, 0x04, 0x01, 0x36, 0x04, 0x2B, 0x01, 0x03,
+       0x28, 0x0D, 0x06, 0x0E, 0x1A, 0x1A, 0x01, 0x10, 0x02, 0x00, 0x06, 0x03,
+       0x33, 0x04, 0x01, 0x34, 0x04, 0x17, 0x01, 0x04, 0x28, 0x0D, 0x06, 0x0E,
+       0x1A, 0x1A, 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, 0x33, 0x04, 0x01, 0x34,
+       0x04, 0x03, 0x50, 0x1C, 0x1A, 0x00, 0x00, 0x7A, 0x01, 0x0C, 0x10, 0x1B,
+       0x43, 0x32, 0x01, 0x03, 0x0A, 0x11, 0x00, 0x00, 0x7A, 0x01, 0x0C, 0x10,
+       0x01, 0x01, 0x0D, 0x00, 0x00, 0x7A, 0x01, 0x0C, 0x10, 0x42, 0x00, 0x00,
+       0x14, 0x01, 0x00, 0x5C, 0x1F, 0x1B, 0x06, 0x1F, 0x01, 0x01, 0x28, 0x0D,
+       0x06, 0x06, 0x1A, 0x01, 0x00, 0x7E, 0x04, 0x11, 0x01, 0x02, 0x28, 0x0D,
+       0x06, 0x0A, 0x1A, 0x5E, 0x1F, 0x06, 0x03, 0x01, 0x10, 0x27, 0x04, 0x01,
+       0x1A, 0x04, 0x01, 0x1A, 0x61, 0x1F, 0x05, 0x34, 0x20, 0x06, 0x31, 0x67,
+       0x1F, 0x01, 0x14, 0x28, 0x0D, 0x06, 0x06, 0x1A, 0x01, 0x02, 0x27, 0x04,
+       0x23, 0x01, 0x15, 0x28, 0x0D, 0x06, 0x0A, 0x1A, 0x81, 0x0C, 0x06, 0x03,
+       0x01, 0x7F, 0x7E, 0x04, 0x13, 0x01, 0x16, 0x28, 0x0D, 0x06, 0x06, 0x1A,
+       0x01, 0x01, 0x27, 0x04, 0x07, 0x1A, 0x01, 0x04, 0x27, 0x01, 0x00, 0x1A,
+       0x13, 0x06, 0x03, 0x01, 0x08, 0x27, 0x00, 0x00, 0x14, 0x1B, 0x05, 0x10,
+       0x20, 0x06, 0x0D, 0x67, 0x1F, 0x01, 0x15, 0x0D, 0x06, 0x05, 0x1A, 0x81,
+       0x0C, 0x04, 0x01, 0x17, 0x00, 0x00, 0x81, 0x28, 0x01, 0x07, 0x11, 0x01,
+       0x01, 0x0E, 0x06, 0x02, 0x5A, 0x1C, 0x00, 0x01, 0x03, 0x00, 0x1D, 0x13,
+       0x06, 0x05, 0x02, 0x00, 0x68, 0x2C, 0x00, 0x81, 0x28, 0x1A, 0x04, 0x73,
+       0x00, 0x01, 0x14, 0x81, 0x2B, 0x01, 0x01, 0x81, 0x37, 0x1D, 0x1B, 0x01,
+       0x00, 0x81, 0x24, 0x01, 0x16, 0x81, 0x2B, 0x81, 0x2F, 0x1D, 0x00, 0x01,
+       0x81, 0x04, 0x81, 0x06, 0x08, 0x81, 0x02, 0x08, 0x81, 0x05, 0x08, 0x81,
+       0x07, 0x08, 0x81, 0x03, 0x08, 0x03, 0x00, 0x01, 0x01, 0x81, 0x37, 0x01,
+       0x27, 0x6E, 0x1F, 0x08, 0x71, 0x1F, 0x01, 0x01, 0x0B, 0x08, 0x02, 0x00,
+       0x06, 0x04, 0x46, 0x02, 0x00, 0x08, 0x81, 0x36, 0x74, 0x1E, 0x81, 0x35,
+       0x60, 0x01, 0x04, 0x12, 0x60, 0x01, 0x04, 0x08, 0x01, 0x1C, 0x23, 0x60,
+       0x01, 0x20, 0x81, 0x30, 0x6D, 0x6E, 0x1F, 0x81, 0x32, 0x71, 0x1F, 0x1B,
+       0x01, 0x01, 0x0B, 0x81, 0x35, 0x70, 0x32, 0x1B, 0x06, 0x11, 0x45, 0x28,
+       0x1E, 0x1B, 0x81, 0x23, 0x05, 0x02, 0x4A, 0x1C, 0x81, 0x35, 0x32, 0x46,
+       0x32, 0x04, 0x6C, 0x48, 0x01, 0x01, 0x81, 0x37, 0x01, 0x00, 0x81, 0x37,
+       0x02, 0x00, 0x06, 0x81, 0x2E, 0x02, 0x00, 0x81, 0x35, 0x81, 0x04, 0x06,
+       0x12, 0x01, 0x83, 0xFE, 0x01, 0x81, 0x35, 0x6A, 0x81, 0x04, 0x01, 0x04,
+       0x09, 0x1B, 0x81, 0x35, 0x45, 0x81, 0x32, 0x81, 0x06, 0x06, 0x1C, 0x01,
+       0x00, 0x81, 0x35, 0x6B, 0x81, 0x06, 0x01, 0x04, 0x09, 0x1B, 0x81, 0x35,
+       0x01, 0x02, 0x09, 0x1B, 0x81, 0x35, 0x01, 0x00, 0x81, 0x37, 0x01, 0x03,
+       0x09, 0x81, 0x31, 0x81, 0x02, 0x06, 0x0F, 0x01, 0x01, 0x81, 0x35, 0x01,
+       0x01, 0x81, 0x35, 0x65, 0x1F, 0x01, 0x08, 0x09, 0x81, 0x37, 0x81, 0x05,
+       0x06, 0x1F, 0x01, 0x0D, 0x81, 0x35, 0x81, 0x05, 0x01, 0x04, 0x09, 0x1B,
+       0x81, 0x35, 0x01, 0x02, 0x09, 0x81, 0x35, 0x30, 0x06, 0x04, 0x01, 0x03,
+       0x81, 0x34, 0x31, 0x06, 0x04, 0x01, 0x01, 0x81, 0x34, 0x81, 0x07, 0x1B,
+       0x06, 0x27, 0x01, 0x0A, 0x81, 0x35, 0x01, 0x04, 0x09, 0x1B, 0x81, 0x35,
+       0x47, 0x81, 0x35, 0x2E, 0x01, 0x00, 0x1B, 0x01, 0x20, 0x0A, 0x06, 0x0E,
+       0x81, 0x00, 0x10, 0x01, 0x01, 0x11, 0x06, 0x03, 0x1B, 0x81, 0x35, 0x44,
+       0x04, 0x6C, 0x48, 0x04, 0x01, 0x1A, 0x81, 0x03, 0x06, 0x0D, 0x01, 0x0B,
+       0x81, 0x35, 0x01, 0x02, 0x81, 0x35, 0x01, 0x82, 0x00, 0x81, 0x35, 0x00,
+       0x00, 0x01, 0x10, 0x81, 0x37, 0x5F, 0x1E, 0x1B, 0x81, 0x27, 0x06, 0x10,
+       0x81, 0x0A, 0x19, 0x1B, 0x46, 0x81, 0x36, 0x1B, 0x81, 0x35, 0x66, 0x32,
+       0x81, 0x30, 0x04, 0x12, 0x1B, 0x81, 0x25, 0x32, 0x81, 0x0A, 0x18, 0x1B,
+       0x44, 0x81, 0x36, 0x1B, 0x81, 0x37, 0x66, 0x32, 0x81, 0x30, 0x00, 0x00,
+       0x7C, 0x01, 0x14, 0x81, 0x37, 0x01, 0x0C, 0x81, 0x36, 0x66, 0x01, 0x0C,
+       0x81, 0x30, 0x00, 0x00, 0x39, 0x1B, 0x01, 0x00, 0x0D, 0x06, 0x02, 0x48,
+       0x00, 0x81, 0x28, 0x1A, 0x04, 0x72, 0x00, 0x1B, 0x81, 0x35, 0x81, 0x30,
+       0x00, 0x00, 0x1B, 0x81, 0x37, 0x81, 0x30, 0x00, 0x00, 0x01, 0x0B, 0x81,
+       0x37, 0x01, 0x03, 0x81, 0x36, 0x01, 0x00, 0x81, 0x36, 0x00, 0x01, 0x03,
+       0x00, 0x2F, 0x1A, 0x1B, 0x01, 0x10, 0x11, 0x06, 0x08, 0x01, 0x04, 0x81,
+       0x37, 0x02, 0x00, 0x81, 0x37, 0x1B, 0x01, 0x08, 0x11, 0x06, 0x08, 0x01,
+       0x03, 0x81, 0x37, 0x02, 0x00, 0x81, 0x37, 0x1B, 0x01, 0x20, 0x11, 0x06,
+       0x08, 0x01, 0x05, 0x81, 0x37, 0x02, 0x00, 0x81, 0x37, 0x1B, 0x01, 0x80,
+       0x40, 0x11, 0x06, 0x08, 0x01, 0x06, 0x81, 0x37, 0x02, 0x00, 0x81, 0x37,
+       0x01, 0x04, 0x11, 0x06, 0x08, 0x01, 0x02, 0x81, 0x37, 0x02, 0x00, 0x81,
+       0x37, 0x00, 0x00, 0x1B, 0x01, 0x08, 0x37, 0x81, 0x37, 0x81, 0x37, 0x00,
+       0x00, 0x1B, 0x01, 0x10, 0x37, 0x81, 0x37, 0x81, 0x35, 0x00, 0x00, 0x1B,
+       0x3A, 0x06, 0x02, 0x1A, 0x00, 0x81, 0x28, 0x1A, 0x04, 0x75
+};
+
+static const uint16_t t0_caddr[] = {
+       0,
+       5,
+       10,
+       15,
+       20,
+       25,
+       30,
+       35,
+       40,
+       44,
+       48,
+       52,
+       56,
+       60,
+       64,
+       68,
+       72,
+       76,
+       80,
+       84,
+       88,
+       92,
+       96,
+       100,
+       104,
+       108,
+       112,
+       116,
+       120,
+       125,
+       130,
+       135,
+       140,
+       145,
+       150,
+       155,
+       160,
+       165,
+       170,
+       175,
+       180,
+       185,
+       190,
+       195,
+       200,
+       205,
+       210,
+       215,
+       220,
+       225,
+       230,
+       235,
+       240,
+       245,
+       250,
+       255,
+       264,
+       277,
+       281,
+       306,
+       312,
+       332,
+       343,
+       380,
+       483,
+       487,
+       552,
+       567,
+       578,
+       596,
+       625,
+       635,
+       671,
+       741,
+       755,
+       761,
+       808,
+       828,
+       881,
+       950,
+       983,
+       995,
+       1320,
+       1477,
+       1502,
+       1513,
+       1528,
+       1539,
+       1545,
+       1568,
+       1628,
+       1636,
+       1649,
+       1668,
+       1675,
+       1687,
+       1722,
+       1734,
+       1741,
+       1757,
+       1761,
+       1899,
+       1912,
+       1921,
+       1928,
+       2032,
+       2054,
+       2068,
+       2085,
+       2108,
+       2397,
+       2444,
+       2460,
+       2475,
+       2482,
+       2489,
+       2503,
+       2579,
+       2589,
+       2599
+};
+
+#define T0_INTERPRETED   64
+
+#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)
+
+#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}
+
+T0_DEFENTRY(br_ssl_hs_client_init_main, 136)
+
+void
+br_ssl_hs_client_run(void *t0ctx)
+{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 7: {
+                               /* * */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a * b);
+
+                               }
+                               break;
+                       case 8: {
+                               /* + */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+
+                               }
+                               break;
+                       case 9: {
+                               /* - */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+
+                               }
+                               break;
+                       case 10: {
+                               /* < */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+
+                               }
+                               break;
+                       case 11: {
+                               /* << */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+
+                               }
+                               break;
+                       case 12: {
+                               /* <> */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+
+                               }
+                               break;
+                       case 13: {
+                               /* = */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+
+                               }
+                               break;
+                       case 14: {
+                               /* > */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a > b));
+
+                               }
+                               break;
+                       case 15: {
+                               /* >= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+
+                               }
+                               break;
+                       case 16: {
+                               /* >> */
+
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+
+                               }
+                               break;
+                       case 17: {
+                               /* and */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+
+                               }
+                               break;
+                       case 18: {
+                               /* bzero */
+
+       size_t len = (size_t)T0_POP();
+       void *addr = (unsigned char *)ENG + (size_t)T0_POP();
+       memset(addr, 0, len);
+
+                               }
+                               break;
+                       case 19: {
+                               /* can-output? */
+
+       T0_PUSHi(-(ENG->hlen_out > 0));
+
+                               }
+                               break;
+                       case 20: {
+                               /* co */
+ T0_CO(); 
+                               }
+                               break;
+                       case 21: {
+                               /* compute-Finished-inner */
+
+       int prf_id = T0_POP();
+       int from_client = T0_POPi();
+       unsigned char seed[48];
+       size_t seed_len;
+
+       br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+       if (ENG->session.version >= BR_TLS12) {
+               seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+       } else {
+               br_multihash_out(&ENG->mhash, br_md5_ID, seed);
+               br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
+               seed_len = 36;
+       }
+       prf(ENG->pad, 12, ENG->session.master_secret,
+               sizeof ENG->session.master_secret,
+               from_client ? "client finished" : "server finished",
+               seed, seed_len);
+
+                               }
+                               break;
+                       case 22: {
+                               /* data-get8 */
+
+       size_t addr = T0_POP();
+       T0_PUSH(t0_datablock[addr]);
+
+                               }
+                               break;
+                       case 23: {
+                               /* discard-input */
+
+       ENG->hlen_in = 0;
+
+                               }
+                               break;
+                       case 24: {
+                               /* do-ecdh */
+
+       unsigned prf_id = T0_POP();
+       unsigned ecdhe = T0_POP();
+       int x;
+
+       x = make_pms_ecdh(CTX, ecdhe, prf_id);
+       if (x < 0) {
+               br_ssl_engine_fail(ENG, -x);
+               T0_CO();
+       } else {
+               T0_PUSH(x);
+       }
+
+                               }
+                               break;
+                       case 25: {
+                               /* do-rsa-encrypt */
+
+       int x;
+
+       x = make_pms_rsa(CTX, T0_POP());
+       if (x < 0) {
+               br_ssl_engine_fail(ENG, -x);
+               T0_CO();
+       } else {
+               T0_PUSH(x);
+       }
+
+                               }
+                               break;
+                       case 26: {
+                               /* drop */
+ (void)T0_POP(); 
+                               }
+                               break;
+                       case 27: {
+                               /* dup */
+ T0_PUSH(T0_PEEK(0)); 
+                               }
+                               break;
+                       case 28: {
+                               /* fail */
+
+       br_ssl_engine_fail(ENG, (int)T0_POPi());
+       T0_CO();
+
+                               }
+                               break;
+                       case 29: {
+                               /* flush-record */
+
+       br_ssl_engine_flush_record(ENG);
+
+                               }
+                               break;
+                       case 30: {
+                               /* get16 */
+
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*(uint16_t *)((unsigned char *)ENG + addr));
+
+                               }
+                               break;
+                       case 31: {
+                               /* get8 */
+
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*((unsigned char *)ENG + addr));
+
+                               }
+                               break;
+                       case 32: {
+                               /* has-input? */
+
+       T0_PUSHi(-(ENG->hlen_in != 0));
+
+                               }
+                               break;
+                       case 33: {
+                               /* memcmp */
+
+       size_t len = (size_t)T0_POP();
+       void *addr2 = (unsigned char *)ENG + (size_t)T0_POP();
+       void *addr1 = (unsigned char *)ENG + (size_t)T0_POP();
+       int x = memcmp(addr1, addr2, len);
+       T0_PUSH((uint32_t)-(x == 0));
+
+                               }
+                               break;
+                       case 34: {
+                               /* memcpy */
+
+       size_t len = (size_t)T0_POP();
+       void *src = (unsigned char *)ENG + (size_t)T0_POP();
+       void *dst = (unsigned char *)ENG + (size_t)T0_POP();
+       memcpy(dst, src, len);
+
+                               }
+                               break;
+                       case 35: {
+                               /* mkrand */
+
+       size_t len = (size_t)T0_POP();
+       void *addr = (unsigned char *)ENG + (size_t)T0_POP();
+       br_hmac_drbg_generate(&ENG->rng, addr, len);
+
+                               }
+                               break;
+                       case 36: {
+                               /* more-incoming-bytes? */
+
+       T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG));
+
+                               }
+                               break;
+                       case 37: {
+                               /* multihash-init */
+
+       br_multihash_init(&ENG->mhash);
+
+                               }
+                               break;
+                       case 38: {
+                               /* not */
+
+       uint32_t a = T0_POP();
+       T0_PUSH(~a);
+
+                               }
+                               break;
+                       case 39: {
+                               /* or */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a | b);
+
+                               }
+                               break;
+                       case 40: {
+                               /* over */
+ T0_PUSH(T0_PEEK(1)); 
+                               }
+                               break;
+                       case 41: {
+                               /* read-chunk-native */
+
+       size_t clen = ENG->hlen_in;
+       if (clen > 0) {
+               uint32_t addr, len;
+
+               len = T0_POP();
+               addr = T0_POP();
+               if ((size_t)len < clen) {
+                       clen = (size_t)len;
+               }
+               memcpy((unsigned char *)ENG + addr, ENG->hbuf_in, clen);
+               if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen);
+               }
+               T0_PUSH(addr + (uint32_t)clen);
+               T0_PUSH(len - (uint32_t)clen);
+               ENG->hbuf_in += clen;
+               ENG->hlen_in -= clen;
+       }
+
+                               }
+                               break;
+                       case 42: {
+                               /* read8-native */
+
+       if (ENG->hlen_in > 0) {
+               unsigned char x;
+
+               x = *ENG->hbuf_in ++;
+               if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, &x, 1);
+               }
+               T0_PUSH(x);
+               ENG->hlen_in --;
+       } else {
+               T0_PUSHi(-1);
+       }
+
+                               }
+                               break;
+                       case 43: {
+                               /* set16 */
+
+       size_t addr = (size_t)T0_POP();
+       *(uint16_t *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
+
+                               }
+                               break;
+                       case 44: {
+                               /* set8 */
+
+       size_t addr = (size_t)T0_POP();
+       *((unsigned char *)ENG + addr) = (unsigned char)T0_POP();
+
+                               }
+                               break;
+                       case 45: {
+                               /* strlen */
+
+       void *str = (unsigned char *)ENG + (size_t)T0_POP();
+       T0_PUSH((uint32_t)strlen(str));
+
+                               }
+                               break;
+                       case 46: {
+                               /* supported-curves */
+
+       uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves;
+       T0_PUSH(x);
+
+                               }
+                               break;
+                       case 47: {
+                               /* supported-hash-functions */
+
+       int i;
+       unsigned x, num;
+
+       x = 0;
+       num = 0;
+       for (i = br_sha1_ID; i <= br_sha512_ID; i ++) {
+               if (br_multihash_getimpl(&ENG->mhash, i)) {
+                       x |= 1U << i;
+                       num ++;
+               }
+       }
+       T0_PUSH(x);
+       T0_PUSH(num);
+
+                               }
+                               break;
+                       case 48: {
+                               /* supports-ecdsa? */
+
+       T0_PUSHi(-(CTX->iecdsa != 0));
+
+                               }
+                               break;
+                       case 49: {
+                               /* supports-rsa-sign? */
+
+       T0_PUSHi(-(CTX->irsavrfy != 0));
+
+                               }
+                               break;
+                       case 50: {
+                               /* swap */
+ T0_SWAP(); 
+                               }
+                               break;
+                       case 51: {
+                               /* switch-aesgcm-in */
+
+       int is_client, prf_id;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id,
+               ENG->iaes_ctr, cipher_key_len);
+
+                               }
+                               break;
+                       case 52: {
+                               /* switch-aesgcm-out */
+
+       int is_client, prf_id;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id,
+               ENG->iaes_ctr, cipher_key_len);
+
+                               }
+                               break;
+                       case 53: {
+                               /* switch-cbc-in */
+
+       int is_client, prf_id, mac_id, aes;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       aes = T0_POP();
+       mac_id = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id,
+               aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len);
+
+                               }
+                               break;
+                       case 54: {
+                               /* switch-cbc-out */
+
+       int is_client, prf_id, mac_id, aes;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       aes = T0_POP();
+       mac_id = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id,
+               aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len);
+
+                               }
+                               break;
+                       case 55: {
+                               /* u>> */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x >> c);
+
+                               }
+                               break;
+                       case 56: {
+                               /* verify-SKE-sig */
+
+       size_t sig_len = T0_POP();
+       int use_rsa = T0_POPi();
+       int hash = T0_POPi();
+
+       T0_PUSH(verify_SKE_sig(CTX, hash, use_rsa, sig_len));
+
+                               }
+                               break;
+                       case 57: {
+                               /* write-blob-chunk */
+
+       size_t clen = ENG->hlen_out;
+       if (clen > 0) {
+               uint32_t addr, len;
+
+               len = T0_POP();
+               addr = T0_POP();
+               if ((size_t)len < clen) {
+                       clen = (size_t)len;
+               }
+               memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen);
+               if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen);
+               }
+               T0_PUSH(addr + (uint32_t)clen);
+               T0_PUSH(len - (uint32_t)clen);
+               ENG->hbuf_out += clen;
+               ENG->hlen_out -= clen;
+       }
+
+                               }
+                               break;
+                       case 58: {
+                               /* write8-native */
+
+       unsigned char x;
+
+       x = (unsigned char)T0_POP();
+       if (ENG->hlen_out > 0) {
+               if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, &x, 1);
+               }
+               *ENG->hbuf_out ++ = x;
+               ENG->hlen_out --;
+               T0_PUSHi(-1);
+       } else {
+               T0_PUSHi(0);
+       }
+
+                               }
+                               break;
+                       case 59: {
+                               /* x509-append */
+
+       const br_x509_class *xc;
+       size_t len;
+
+       xc = *(ENG->x509ctx);
+       len = T0_POP();
+       xc->append(ENG->x509ctx, ENG->pad, len);
+
+                               }
+                               break;
+                       case 60: {
+                               /* x509-end-cert */
+
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->end_cert(ENG->x509ctx);
+
+                               }
+                               break;
+                       case 61: {
+                               /* x509-end-chain */
+
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       T0_PUSH(xc->end_chain(ENG->x509ctx));
+
+                               }
+                               break;
+                       case 62: {
+                               /* x509-start-cert */
+
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->start_cert(ENG->x509ctx, T0_POP());
+
+                               }
+                               break;
+                       case 63: {
+                               /* x509-start-chain */
+
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->start_chain(ENG->x509ctx, T0_POP(), ENG->server_name);
+
+                               }
+                               break;
+                       }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}
diff --git a/src/ssl/ssl_hs_client.t0 b/src/ssl/ssl_hs_client.t0
new file mode 100644 (file)
index 0000000..e2a76f7
--- /dev/null
@@ -0,0 +1,992 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+\ ----------------------------------------------------------------------
+\ Handshake processing code, for the client.
+\ The common T0 code (ssl_hs_common.t0) shall be read first.
+
+preamble {
+
+/*
+ * This macro evaluates to a pointer to the client context, under that
+ * specific name. It must be noted that since the engine context is the
+ * first field of the br_ssl_client_context structure ('eng'), then
+ * pointers values of both types are interchangeable, modulo an
+ * appropriate cast. This also means that "adresses" computed as offsets
+ * within the structure work for both kinds of context.
+ */
+#define CTX  ((br_ssl_client_context *)ENG)
+
+/*
+ * Generate the pre-master secret for RSA key exchange, and encrypt it
+ * with the server's public key. Returned value is either the encrypted
+ * data length (in bytes), or -x on error, with 'x' being an error code.
+ *
+ * This code assumes that the public key has been already verified (it
+ * was properly obtained by the X.509 engine, and it has the right type,
+ * i.e. it is of type RSA and suitable for encryption).
+ */
+static int
+make_pms_rsa(br_ssl_client_context *ctx, int prf_id)
+{
+       const br_x509_class **xc;
+       const br_x509_pkey *pk;
+       const unsigned char *n;
+       unsigned char *pms;
+       size_t nlen, u;
+
+       xc = ctx->eng.x509ctx;
+       pk = (*xc)->get_pkey(xc);
+
+       /*
+        * Compute actual RSA key length, in case there are leading zeros.
+        */
+       n = pk->key.rsa.n;
+       nlen = pk->key.rsa.nlen;
+       while (nlen > 0 && *n == 0) {
+               n ++;
+               nlen --;
+       }
+
+       /*
+        * We need at least 59 bytes (48 bytes for pre-master secret, and
+        * 11 bytes for the PKCS#1 type 2 padding). Note that the X.509
+        * minimal engine normally blocks RSA keys shorter than 128 bytes,
+        * so this is mostly for public keys provided explicitly by the
+        * caller.
+        */
+       if (nlen < 59) {
+               return -BR_ERR_X509_WEAK_PUBLIC_KEY;
+       }
+       if (nlen > sizeof ctx->eng.pad) {
+               return -BR_ERR_LIMIT_EXCEEDED;
+       }
+
+       /*
+        * Make PMS.
+        */
+       pms = ctx->eng.pad + nlen - 48;
+       br_enc16be(pms, ctx->eng.version_max);
+       br_hmac_drbg_generate(&ctx->eng.rng, pms + 2, 46);
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, pms, 48);
+
+       /*
+        * Apply PKCS#1 type 2 padding.
+        */
+       ctx->eng.pad[0] = 0x00;
+       ctx->eng.pad[1] = 0x02;
+       ctx->eng.pad[nlen - 49] = 0x00;
+       br_hmac_drbg_generate(&ctx->eng.rng, ctx->eng.pad + 2, nlen - 51);
+       for (u = 2; u < nlen - 49; u ++) {
+               while (ctx->eng.pad[u] == 0) {
+                       br_hmac_drbg_generate(&ctx->eng.rng,
+                               &ctx->eng.pad[u], 1);
+               }
+       }
+
+       /*
+        * Compute RSA encryption.
+        */
+       if (!ctx->irsapub(ctx->eng.pad, nlen, &pk->key.rsa)) {
+               return -BR_ERR_LIMIT_EXCEEDED;
+       }
+       return (int)nlen;
+}
+
+/*
+ * OID for hash functions in RSA signatures.
+ */
+static const unsigned char HASH_OID_SHA1[] = {
+       0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
+};
+
+static const unsigned char HASH_OID_SHA224[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
+};
+
+static const unsigned char HASH_OID_SHA256[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+static const unsigned char HASH_OID_SHA384[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+static const unsigned char HASH_OID_SHA512[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+static const unsigned char *HASH_OID[] = {
+       HASH_OID_SHA1,
+       HASH_OID_SHA224,
+       HASH_OID_SHA256,
+       HASH_OID_SHA384,
+       HASH_OID_SHA512
+};
+
+/*
+ * Check the RSA signature on the ServerKeyExchange message.
+ *   hash      hash function ID (2 to 6), or 0 for MD5+SHA-1 (with RSA only)
+ *   use_rsa   non-zero for RSA signature, zero for ECDSA
+ *   sig_len   signature length (in bytes); signature value is in the pad
+ * Returned value is 0 on success, or an error code.
+ */
+static int
+verify_SKE_sig(br_ssl_client_context *ctx,
+       int hash, int use_rsa, size_t sig_len)
+{
+       const br_x509_class **xc;
+       const br_x509_pkey *pk;
+       br_multihash_context mhc;
+       unsigned char hv[64], head[4];
+       size_t hv_len;
+
+       xc = ctx->eng.x509ctx;
+       pk = (*xc)->get_pkey(xc);
+       br_multihash_zero(&mhc);
+       br_multihash_copyimpl(&mhc, &ctx->eng.mhash);
+       br_multihash_init(&mhc);
+       br_multihash_update(&mhc,
+               ctx->eng.client_random, sizeof ctx->eng.client_random);
+       br_multihash_update(&mhc,
+               ctx->eng.server_random, sizeof ctx->eng.server_random);
+       head[0] = 3;
+       head[1] = 0;
+       head[2] = ctx->eng.ecdhe_curve;
+       head[3] = ctx->eng.ecdhe_point_len;
+       br_multihash_update(&mhc, head, sizeof head);
+       br_multihash_update(&mhc,
+               ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len);
+       if (hash) {
+               hv_len = br_multihash_out(&mhc, hash, hv);
+               if (hv_len == 0) {
+                       return BR_ERR_INVALID_ALGORITHM;
+               }
+       } else {
+               if (!br_multihash_out(&mhc, br_md5_ID, hv)
+                       || !br_multihash_out(&mhc, br_sha1_ID, hv + 16))
+               {
+                       return BR_ERR_INVALID_ALGORITHM;
+               }
+               hv_len = 36;
+       }
+       if (use_rsa) {
+               unsigned char tmp[64];
+               const unsigned char *hash_oid;
+
+               if (hash) {
+                       hash_oid = HASH_OID[hash - 2];
+               } else {
+                       hash_oid = NULL;
+               }
+               if (!ctx->irsavrfy(ctx->eng.pad, sig_len,
+                       hash_oid, hv_len, &pk->key.rsa, tmp)
+                       || memcmp(tmp, hv, hv_len) != 0)
+               {
+                       return BR_ERR_BAD_SIGNATURE;
+               }
+       } else {
+               if (!ctx->iecdsa(ctx->eng.iec, hv, hv_len, &pk->key.ec,
+                       ctx->eng.pad, sig_len))
+               {
+                       return BR_ERR_BAD_SIGNATURE;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Perform client-size ECDH (or ECDHE). The point that should be sent to
+ * the server is written in the pad; returned value is either the point
+ * length (in bytes), or -x on error, with 'x' being an error code.
+ *
+ * The point _from_ the server is taken from ecdhe_point[] if 'ecdhe'
+ * is non-zero, or from the X.509 engine context if 'ecdhe' is zero
+ * (for static ECDH).
+ */
+static int
+make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id)
+{
+       int curve;
+       unsigned char key[66], point[133];
+       const unsigned char *generator, *order, *point_src;
+       size_t glen, olen, point_len;
+       unsigned char mask;
+
+       if (ecdhe) {
+               curve = ctx->eng.ecdhe_curve;
+               point_src = ctx->eng.ecdhe_point;
+               point_len = ctx->eng.ecdhe_point_len;
+       } else {
+               const br_x509_class **xc;
+               const br_x509_pkey *pk;
+
+               xc = ctx->eng.x509ctx;
+               pk = (*xc)->get_pkey(xc);
+               curve = pk->key.ec.curve;
+               point_src = pk->key.ec.q;
+               point_len = pk->key.ec.qlen;
+       }
+       if ((ctx->eng.iec->supported_curves & ((uint32_t)1 << curve)) == 0) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       /*
+        * We need to generate our key, as a non-zero random value which
+        * is lower than the curve order, in a "large enough" range. We
+        * force top bit to 0 and bottom bit to 1, which guarantees that
+        * the value is in the proper range.
+        */
+       order = ctx->eng.iec->order(curve, &olen);
+       mask = 0xFF;
+       while (mask >= order[0]) {
+               mask >>= 1;
+       }
+       br_hmac_drbg_generate(&ctx->eng.rng, key, olen);
+       key[0] &= mask;
+       key[olen - 1] |= 0x01;
+
+       /*
+        * Compute the common ECDH point, whose X coordinate is the
+        * pre-master secret.
+        */
+       generator = ctx->eng.iec->generator(curve, &glen);
+       if (glen != point_len) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       memcpy(point, point_src, glen);
+       if (!ctx->eng.iec->mul(point, glen, key, olen, curve)) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       /*
+        * The pre-master secret is the X coordinate.
+        */
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, point + 1, glen >> 1);
+
+       memcpy(point, generator, glen);
+       if (!ctx->eng.iec->mul(point, glen, key, olen, curve)) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+       memcpy(ctx->eng.pad, point, glen);
+       return (int)glen;
+}
+
+}
+
+\ =======================================================================
+
+: addr-ctx:
+       next-word { field }
+       "addr-" field + 0 1 define-word
+       0 8191 "offsetof(br_ssl_client_context, " field + ")" + make-CX
+       postpone literal postpone ; ;
+
+\ Length of the Secure Renegotiation extension. This is 5 for the
+\ first handshake, 17 for a renegotiation (if the server supports the
+\ extension), or 0 if we know that the server does not support the
+\ extension.
+: ext-reneg-length ( -- n )
+       addr-reneg get8 dup if 1 - 17 * else drop 5 then ;
+
+\ Length of SNI extension.
+: ext-sni-length ( -- len )
+       addr-server_name strlen dup if 9 + then ;
+
+\ Length of Maximum Fragment Length extension.
+: ext-frag-length ( -- len )
+       addr-log_max_frag_len get8 14 = if 0 else 5 then ;
+
+\ Test support for RSA signatures.
+cc: supports-rsa-sign? ( -- bool ) {
+       T0_PUSHi(-(CTX->irsavrfy != 0));
+}
+
+\ Test support for ECDSA signatures.
+cc: supports-ecdsa? ( -- bool ) {
+       T0_PUSHi(-(CTX->iecdsa != 0));
+}
+
+\ Length of Signatures extension.
+: ext-signatures-length ( -- len )
+       supported-hash-functions { x } drop
+       0
+       supports-rsa-sign? if x + then
+       supports-ecdsa? if x + then
+       dup if 1 << 6 + then ;
+
+\ Write supported hash functions ( sign -- )
+: write-hashes
+       { sign }
+       supported-hash-functions drop
+       \ We advertise hash functions in the following preference order:
+       \   SHA-256 SHA-224 SHA-384 SHA-512 SHA-1
+       \ Rationale:
+       \ -- SHA-256 and SHA-224 are more efficient on 32-bit architectures
+       \ -- SHA-1 is less than ideally collision-resistant
+       dup 0x10 and if 4 write8 sign write8 then
+       dup 0x08 and if 3 write8 sign write8 then
+       dup 0x20 and if 5 write8 sign write8 then
+       dup 0x40 and if 6 write8 sign write8 then
+       0x04 and if 2 write8 sign write8 then ;
+
+\ Length of Supported Curves extension.
+: ext-supported-curves-length ( -- len )
+       supported-curves dup if
+               0 { x }
+               begin dup while
+                       dup 1 and x + >x
+                       1 >>
+               repeat
+               drop x 1 << 6 +
+       then ;
+
+\ Length of Supported Point Formats extension.
+: ext-point-format-length ( -- len )
+       supported-curves if 6 else 0 then ;
+
+\ Write handshake message: ClientHello
+: write-ClientHello ( -- )
+       { ; total-ext-length }
+
+       \ Compute length for extensions (without the general two-byte header)
+       ext-reneg-length ext-sni-length + ext-frag-length +
+       ext-signatures-length +
+       ext-supported-curves-length + ext-point-format-length +
+       >total-ext-length
+
+       \ ClientHello type
+       1 write8
+
+       \ Compute and write length
+       39 addr-session_id_len get8 + addr-suites_num get8 1 << +
+       total-ext-length if 2+ total-ext-length + then
+       write24
+
+       \ Protocol version
+       addr-version_max get16 write16
+
+       \ Client random
+       addr-client_random 4 bzero
+       addr-client_random 4 + 28 mkrand
+       addr-client_random 32 write-blob
+
+       \ Session ID
+       addr-session_id addr-session_id_len get8 write-blob-head8
+
+       \ Supported cipher suites. We also check here that we indeed
+       \ support all these suites.
+       addr-suites_num get8 dup 1 << write16
+       addr-suites_buf swap
+       begin
+               dup while 1-
+               over get16
+               dup suite-supported? ifnot ERR_BAD_CIPHER_SUITE fail then
+               write16
+               swap 2+ swap
+       repeat
+       2drop
+
+       \ Compression methods (only "null" compression)
+       1 write8 0 write8
+
+       \ Extensions
+       total-ext-length if
+               total-ext-length write16
+               ext-reneg-length if
+                       0xFF01 write16          \ extension type (0xFF01)
+                       addr-saved_finished
+                       ext-reneg-length 4 - dup write16 \ extension length
+                       1- write-blob-head8              \ verify data
+               then
+               ext-sni-length if
+                       0x0000 write16          \ extension type (0)
+                       addr-server_name
+                       ext-sni-length 4 - dup write16 \ extension length
+                       2 - dup write16                \ ServerNameList length
+                       0 write8                       \ name type: host_name
+                       3 - write-blob-head16          \ the name itself
+               then
+               ext-frag-length if
+                       0x0001 write16          \ extension type (1)
+                       0x0001 write16          \ extension length
+                       addr-log_max_frag_len get8 8 - write8
+               then
+               ext-signatures-length if
+                       0x000D write16          \ extension type (13)
+                       ext-signatures-length 4 - dup write16 \ extension length
+                       2 - write16             \ list length
+                       supports-ecdsa? if 3 write-hashes then
+                       supports-rsa-sign? if 1 write-hashes then
+               then
+               \ TODO: add an API to specify preference order for curves.
+               \ Right now we use increasing id order, which makes P-256
+               \ the preferred curve.
+               ext-supported-curves-length dup if
+                       0x000A write16          \ extension type (10)
+                       4 - dup write16         \ extension length
+                       2- write16              \ list length
+                       supported-curves 0
+                       begin dup 32 < while
+                               dup2 >> 1 and if dup write16 then
+                               1+
+                       repeat
+                       2drop
+               else
+                       drop
+               then
+               ext-point-format-length if
+                       0x000B write16          \ extension type (11)
+                       0x0002 write16          \ extension length
+                       0x0100 write16          \ value: 1 format: uncompressed
+               then
+       then
+       ;
+
+\ =======================================================================
+
+\ Parse server SNI extension. If present, then it should be empty.
+: read-server-sni ( lim -- lim )
+       read16 if ERR_BAD_SNI fail then ;
+
+\ Parse server Max Fragment Length extension. If present, then it should
+\ advertise the same length as the client. Note that whether the server
+\ sends it or not changes nothing for us: we won't send any record larger
+\ than the advertised value anyway, and we will accept incoming records
+\ up to our input buffer length.
+: read-server-frag ( lim -- lim )
+       read16 1 = ifnot ERR_BAD_FRAGLEN fail then
+       read8 8 + addr-log_max_frag_len get8 = ifnot ERR_BAD_FRAGLEN fail then ;
+
+\ Parse server Secure Renegotiation extension. This is called only if
+\ the client sent that extension, so we only have two cases to
+\ distinguish: first handshake, and renegotiation; in the latter case,
+\ we know that the server supports the extension, otherwise the client
+\ would not have sent it.
+: read-server-reneg ( lim -- lim )
+       read16
+       addr-reneg get8 ifnot
+               \ "reneg" is 0, so this is a first handshake. The server's
+               \ extension MUST be empty. We also learn that the server
+               \ supports the extension.
+               1 = ifnot ERR_BAD_SECRENEG fail then
+               read8 0 = ifnot ERR_BAD_SECRENEG fail then
+               2 addr-reneg set8
+       else
+               \ "reneg" is non-zero, and we sent an extension, so it must
+               \ be 2 and this is a renegotiation. We must verify that
+               \ the extension contents have length exactly 24 bytes and
+               \ match the saved client and server "Finished".
+               25 = ifnot ERR_BAD_SECRENEG fail then
+               read8 24 = ifnot ERR_BAD_SECRENEG fail then
+               addr-pad 24 read-blob
+               addr-saved_finished addr-pad 24 memcmp ifnot
+                       ERR_BAD_SECRENEG fail
+               then
+       then ;
+
+\ Save a value in a 16-bit field, or check it in case of session resumption.
+: check-resume ( val addr resume -- )
+       if get16 = ifnot ERR_RESUME_MISMATCH fail then else set16 then ;
+
+cc: DEBUG-BLOB ( addr len -- ) {
+       extern int printf(const char *fmt, ...);
+
+       size_t len = T0_POP();
+       unsigned char *buf = (unsigned char *)CTX + T0_POP();
+       size_t u;
+
+       printf("BLOB:");
+       for (u = 0; u < len; u ++) {
+               if (u % 16 == 0) {
+                       printf("\n    ");
+               }
+               printf(" %02x", buf[u]);
+       }
+       printf("\n");
+}
+
+\ Parse incoming ServerHello. Returned value is true (-1) on session
+\ resumption.
+: read-ServerHello ( -- bool )
+       \ Get header, and check message type.
+       read-handshake-header 2 = ifnot ERR_UNEXPECTED fail then
+
+       \ Get protocol version.
+       read16 { version }
+       version addr-version_min get16 < version addr-version_max get16 > or if
+               ERR_UNSUPPORTED_VERSION fail
+       then
+
+       \ Enforce chosen version for subsequent records in both directions.
+       version addr-version_in get16 <> if ERR_BAD_VERSION fail then
+       version addr-version_out set16
+
+       \ Server random.
+       addr-server_random 32 read-blob
+
+       \ The "session resumption" flag.
+       0 { resume }
+
+       \ Session ID.
+       read8 { idlen }
+       idlen 32 > if ERR_OVERSIZED_ID fail then
+       addr-pad idlen read-blob
+       idlen addr-session_id_len get8 = idlen 0 > and if
+               addr-session_id addr-pad idlen memcmp if
+                       \ Server session ID is non-empty and matches what
+                       \ we sent, so this is a session resumption.
+                       -1 >resume
+               then
+       then
+       addr-session_id addr-pad idlen memcpy
+       idlen addr-session_id_len set8
+
+       \ Record version.
+       version addr-version resume check-resume
+
+       \ Cipher suite. We check that it is part of the list of cipher
+       \ suites that we advertised.
+       \ read16 { suite ; found }
+       \ 0 >found
+       \ addr-suites_buf dup addr-suites_num get8 1 << +
+       \ begin dup2 < while
+       \       2 - dup get16
+       \       suite = found or >found
+       \ repeat
+       \ 2drop found ifnot ERR_BAD_CIPHER_SUITE fail then
+       read16
+       dup scan-suite 0< if ERR_BAD_CIPHER_SUITE fail then
+       addr-cipher_suite resume check-resume
+
+       \ Compression method. Should be 0 (no compression).
+       read8 if ERR_BAD_COMPRESSION fail then
+
+       \ Parse extensions (if any). If there is no extension, then the
+       \ read limit (on the TOS) should be 0 at that point.
+       dup if
+               \ Length of extension list.
+               \ message size.
+               read16 open-elt
+
+               \ Enumerate extensions. For each of them, check that we
+               \ sent an extension of that type, and did not see it
+               \ yet; and then process it.
+               ext-sni-length { ok-sni }
+               ext-reneg-length { ok-reneg }
+               ext-frag-length { ok-frag }
+               ext-signatures-length { ok-signatures }
+               ext-supported-curves-length { ok-curves }
+               ext-point-format-length { ok-points }
+               begin dup while
+                       read16
+                       case
+                               \ Server Name Indication. The server may
+                               \ send such an extension if it uses the SNI
+                               \ from the client, but that "response
+                               \ extension" is supposed to be empty.
+                               0x0000 of
+                                       ok-sni ifnot
+                                               ERR_EXTRA_EXTENSION fail
+                                       then
+                                       0 >ok-sni
+                                       read-server-sni
+                               endof
+
+                               \ Max Frag Length. The contents shall be
+                               \ a single byte whose value matches the one
+                               \ sent by the client.
+                               0x0001 of
+                                       ok-frag ifnot
+                                               ERR_EXTRA_EXTENSION fail
+                                       then
+                                       0 >ok-frag
+                                       read-server-frag
+                               endof
+
+                               \ Secure Renegotiation.
+                               0xFF01 of
+                                       ok-reneg ifnot
+                                               ERR_EXTRA_EXTENSION fail
+                                       then
+                                       0 >ok-reneg
+                                       read-server-reneg
+                               endof
+
+                               \ Signature Algorithms.
+                               \ Normally, the server should never send this
+                               \ extension (so says RFC 5246 #7.4.1.4.1),
+                               \ but some existing servers do.
+                               0x000D of
+                                       ok-signatures ifnot
+                                               ERR_EXTRA_EXTENSION fail
+                                       then
+                                       0 >ok-signatures
+                                       read-ignore-16
+                               endof
+
+                               \ Supported Curves.
+                               0x000A of
+                                       ok-curves ifnot
+                                               ERR_EXTRA_EXTENSION fail
+                                       then
+                                       0 >ok-curves
+                                       read-ignore-16
+                               endof
+
+                               \ Supported Point Formats.
+                               0x000B of
+                                       ok-points ifnot
+                                               ERR_EXTRA_EXTENSION fail
+                                       then
+                                       0 >ok-points
+                                       read-ignore-16
+                               endof
+
+                               ERR_EXTRA_EXTENSION fail
+                       endcase
+               repeat
+
+               \ If we sent a secure renegotiation extension but did not
+               \ receive a response, then the server does not support
+               \ secure renegotiation. This is a hard failure if this
+               \ is a renegotiation.
+               ok-reneg if
+                       ok-reneg 5 > if ERR_BAD_SECRENEG fail then
+                       1 addr-reneg set8
+               then
+               close-elt
+       then
+       close-elt
+       resume
+       ;
+
+cc: x509-start-chain ( expected-key-type -- ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->start_chain(ENG->x509ctx, T0_POP(), ENG->server_name);
+}
+
+cc: x509-start-cert ( length -- ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->start_cert(ENG->x509ctx, T0_POP());
+}
+
+cc: x509-append ( length -- ) {
+       const br_x509_class *xc;
+       size_t len;
+
+       xc = *(ENG->x509ctx);
+       len = T0_POP();
+       xc->append(ENG->x509ctx, ENG->pad, len);
+}
+
+cc: x509-end-cert ( -- ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->end_cert(ENG->x509ctx);
+}
+
+cc: x509-end-chain ( -- err ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       T0_PUSH(xc->end_chain(ENG->x509ctx));
+}
+
+\ Parse Certificate
+: read-Certificate ( -- )
+       \ Get header, and check message type.
+       read-handshake-header 11 = ifnot ERR_UNEXPECTED fail then
+
+       \ Start processing the chain through the X.509 engine.
+       addr-cipher_suite get16 expected-key-type
+       x509-start-chain
+
+       \ Total chain length is a 24-bit integer.
+       read24 open-elt
+       begin
+               dup while
+               read24 open-elt
+               dup x509-start-cert
+
+               \ We read the certificate by chunks through the pad, so
+               \ as to use the existing reading function (read-blob)
+               \ that also ensures proper hashing.
+               begin
+                       dup while
+                       dup 256 > if 256 else dup then { len }
+                       addr-pad len read-blob
+                       len x509-append
+               repeat
+               close-elt
+               x509-end-cert
+       repeat
+
+       \ We must close the chain AND the handshake message.
+       close-elt
+       close-elt
+
+       \ Chain processing is finished; get the error code.
+       x509-end-chain
+       dup if fail then drop
+       ;
+
+\ Verify signature on ECDHE point sent by the server.
+\   'hash' is the hash function to use (1 to 6, or 0 for RSA with MD5+SHA-1)
+\   'use-rsa' is 0 for ECDSA, -1 for for RSA
+\   'sig-len' is the signature length (in bytes)
+\ The signature itself is in the pad.
+cc: verify-SKE-sig ( hash use-rsa sig-len -- err ) {
+       size_t sig_len = T0_POP();
+       int use_rsa = T0_POPi();
+       int hash = T0_POPi();
+
+       T0_PUSH(verify_SKE_sig(CTX, hash, use_rsa, sig_len));
+}
+
+\ Parse ServerKeyExchange
+: read-ServerKeyExchange ( -- )
+       \ Get header, and check message type.
+       read-handshake-header 12 = ifnot ERR_UNEXPECTED fail then
+
+       \ We expect a named curve, and we must support it.
+       read8 3 = ifnot ERR_INVALID_ALGORITHM fail then
+       read16 dup addr-ecdhe_curve set8
+       dup 32 >= if ERR_INVALID_ALGORITHM fail then
+       supported-curves swap >> 1 and ifnot ERR_INVALID_ALGORITHM fail then
+
+       \ Read the server point.
+       read8
+       dup 133 > if ERR_INVALID_ALGORITHM fail then
+       dup addr-ecdhe_point_len set8
+       addr-ecdhe_point swap read-blob
+
+       \ If using TLS-1.2+, then the hash function and signature algorithm
+       \ are explicitly provided; the signature algorithm must match what
+       \ the cipher suite specifies. With TLS-1.0 and 1.1, the signature
+       \ algorithm is inferred from the cipher suite, and the hash is
+       \ either MD5+SHA-1 (for RSA signatures) or SHA-1 (for ECDSA).
+       addr-version get16 0x0303 >= { tls1.2+ }
+       addr-cipher_suite get16 use-rsa-ecdhe? { use-rsa }
+       2 { hash }
+       tls1.2+ if
+               \ Read hash function; accept only the SHA-* identifiers
+               \ (from SHA-1 to SHA-512, no MD5 here).
+               read8
+               dup dup 2 < swap 6 > or if ERR_INVALID_ALGORITHM fail then
+               >hash
+               read8
+               \ Get expected signature algorithm and compare with what
+               \ the server just sent. Expected value is 1 for RSA, 3
+               \ for ECDSA. Note that 'use-rsa' evaluates to -1 for RSA,
+               \ 0 for ECDSA.
+               use-rsa 1 << 3 + = ifnot ERR_INVALID_ALGORITHM fail then
+       else
+               \ For MD5+SHA-1, we set 'hash' to 0.
+               use-rsa if 0 >hash then
+       then
+
+       \ Read signature into the pad.
+       read16 dup { sig-len }
+
+       dup 512 > if ERR_LIMIT_EXCEEDED fail then
+       addr-pad swap read-blob
+
+       \ Verify signature.
+       hash use-rsa sig-len verify-SKE-sig
+       dup if fail then drop
+
+       close-elt ;
+
+\ Parse CertificateRequest. Header has already been read.
+: read-contents-CertificateRequest ( lim -- )
+       \ TODO: implement client certificates. Right now, we simply
+       \ drop the complete message.
+       begin dup while read8 drop repeat drop ;
+
+\ Write an empty Certificate message.
+: write-empty-Certificate ( -- )
+       11 write8 3 write24 0 write24 ;
+
+cc: do-rsa-encrypt ( prf_id -- nlen ) {
+       int x;
+
+       x = make_pms_rsa(CTX, T0_POP());
+       if (x < 0) {
+               br_ssl_engine_fail(ENG, -x);
+               T0_CO();
+       } else {
+               T0_PUSH(x);
+       }
+}
+
+cc: do-ecdh ( echde prf_id -- ulen ) {
+       unsigned prf_id = T0_POP();
+       unsigned ecdhe = T0_POP();
+       int x;
+
+       x = make_pms_ecdh(CTX, ecdhe, prf_id);
+       if (x < 0) {
+               br_ssl_engine_fail(ENG, -x);
+               T0_CO();
+       } else {
+               T0_PUSH(x);
+       }
+}
+
+\ Write ClientKeyExchange
+: write-ClientKeyExchange ( -- )
+       16 write8
+       addr-cipher_suite get16
+       dup use-rsa-keyx? if
+               prf-id do-rsa-encrypt
+               dup 2+ write24
+               dup write16
+               addr-pad swap write-blob
+       else
+               dup use-ecdhe? swap prf-id do-ecdh
+               dup 1+ write24
+               dup write8
+               addr-pad swap write-blob
+       then ;
+
+\ =======================================================================
+
+\ Perform a handshake.
+: do-handshake ( -- )
+       0 addr-application_data set8
+       22 addr-record_type_out set8
+       multihash-init
+
+       write-ClientHello
+       flush-record
+       read-ServerHello
+
+       if
+               \ Session resumption.
+               -1 read-CCS-Finished
+               -1 write-CCS-Finished
+
+       else
+
+               \ Not a session resumption.
+
+               read-Certificate
+
+               \ Depending on cipher suite, we may now expect a
+               \ ServerKeyExchange.
+               addr-cipher_suite get16 expected-key-type
+               CX 0 63 { BR_KEYTYPE_SIGN } and if
+                       read-ServerKeyExchange
+               then
+
+               \ Get next header.
+               read-handshake-header
+
+               \ If this is a CertificateRequest, parse it, then read
+               \ next header.
+               dup 13 = if
+                       drop read-contents-CertificateRequest
+                       read-handshake-header
+                       -1
+               else
+                       0
+               then
+               { seen-CR }
+
+               \ At that point, we should have a ServerHelloDone,
+               \ whose length must be 0.
+               14 = ifnot ERR_UNEXPECTED fail then
+               if ERR_BAD_HELLO_DONE fail then
+
+               \ There should not be more bytes in the record at that point.
+               more-incoming-bytes? if ERR_UNEXPECTED fail then
+
+               seen-CR if
+                       \ TODO: client certificate support.
+                       write-empty-Certificate
+               then
+               write-ClientKeyExchange
+
+               \ TODO: CertificateVerify
+
+               -1 write-CCS-Finished
+               -1 read-CCS-Finished
+       then
+
+       \ Now we should be invoked only in case of renegotiation.
+       1 addr-application_data set8
+       23 addr-record_type_out set8 ;
+
+\ Read a HelloRequest message.
+: read-HelloRequest ( -- )
+       \ A HelloRequest has length 0 and type 0.
+       read-handshake-header-core
+       if ERR_UNEXPECTED fail then
+       if ERR_BAD_HANDSHAKE fail then ;
+
+\ Entry point.
+: main ( -- ! )
+       \ Perform initial handshake.
+       do-handshake
+
+       begin
+               \ Wait for further invocation. At that point, we should
+               \ get either an explicit call for renegotiation, or
+               \ an incoming HelloRequest handshake message.
+               wait-co
+               dup 0x07 and case
+                       0x00 of
+                               0x10 and if
+                                       do-handshake
+                               then
+                       endof
+                       0x01 of
+                               drop
+                               0 addr-application_data set8
+                               read-HelloRequest
+                               \ Reject renegotiations if the peer does not
+                               \ support secure renegotiation. Theoretically
+                               \ we could just ignore that, however if the
+                               \ server sent an HelloRequest then it is
+                               \ expecting a handshake and will wait for our
+                               \ ClientHello.
+                               addr-reneg get8 1 = if
+                                       flush-record
+                                       begin can-output? not while
+                                               wait-co drop
+                                       repeat
+                                       100 send-warning
+                               else
+                                       do-handshake
+                               then
+                       endof
+                       ERR_UNEXPECTED fail
+               endcase
+       again
+       ;
diff --git a/src/ssl/ssl_hs_common.t0 b/src/ssl/ssl_hs_common.t0
new file mode 100644 (file)
index 0000000..a842b29
--- /dev/null
@@ -0,0 +1,1012 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+\ ----------------------------------------------------------------------
+\ This is the common T0 code for processing handshake messages (code that
+\ is used by both client and server).
+
+preamble {
+
+#include <stddef.h>
+#include <string.h>
+
+#include "inner.h"
+
+/*
+ * This macro evaluates to a pointer to the current engine context.
+ */
+#define ENG  ((br_ssl_engine_context *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu)))
+
+}
+
+\ IMPLEMENTATION NOTES
+\ ====================
+\
+\ This code handles all records except application data records.
+\ Application data is accepted (incoming records, outgoing payload data)
+\ only when the application_data flag is set, which is done at the end
+\ of the handshake; and it is cleared whenever a renegotiation or a
+\ closure takes place.
+\
+\ Incoming alerts are processed on the fly; fatal alerts terminate the
+\ context, while warnings are ignored, except for close_notify, which
+\ triggers the closure procedure. That procedure never returns (it ends
+\ with an 'ERR_OK fail' call). We can thus make this processing right
+\ into the read functions.
+\
+\ Specific actions from the caller (closure or renegotiation) may happen
+\ only when jumping back into the T0 code, i.e. just after a 'co' call.
+\ Similarly, incoming record type may change only while the caller has
+\ control, so we need to check that type only when returning from a 'co'.
+\
+\ The handshake processor needs to defer back to the caller ('co') only
+\ in one of the following situations:
+\
+\ -- Some handshake data is expected.
+\
+\ -- The handshake is finished, and application data may flow. There may
+\    be some incoming handshake data (HelloRequest from the server). This
+\    is the only situation where a renegotiation call won't be ignored.
+\
+\ -- Some change-cipher-spec data is expected.
+\
+\ -- An alert record is expected. Other types of incoming records will be
+\    skipped.
+\
+\ -- Waiting for the currently accumulated record to be sent and the
+\    output buffer to become free again for another record.
+
+\ Placeholder for handling not yet implemented functionalities.
+: NYI ( -- ! )
+       "NOT YET IMPLEMENTED!" puts cr -1 fail ;
+
+\ Mark the context as failed with a specific error code. This also
+\ returns control to the caller.
+cc: fail ( err -- ! ) {
+       br_ssl_engine_fail(ENG, (int)T0_POPi());
+       T0_CO();
+}
+
+\ Read a byte from the context (address is offset in context).
+cc: get8 ( addr -- val ) {
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*((unsigned char *)ENG + addr));
+}
+
+\ Read a 16-bit word from the context (address is offset in context).
+cc: get16 ( addr -- val ) {
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*(uint16_t *)((unsigned char *)ENG + addr));
+}
+
+\ Read a 32-bit word from the context (address is offset in context).
+cc: get32 ( addr -- val ) {
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*(uint32_t *)((unsigned char *)ENG + addr));
+}
+
+\ Set a byte in the context (address is offset in context).
+cc: set8 ( val addr -- ) {
+       size_t addr = (size_t)T0_POP();
+       *((unsigned char *)ENG + addr) = (unsigned char)T0_POP();
+}
+
+\ Set a 16-bit word in the context (address is offset in context).
+cc: set16 ( val addr -- ) {
+       size_t addr = (size_t)T0_POP();
+       *(uint16_t *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
+}
+
+\ Set a 32-bit word in the context (address is offset in context).
+cc: set32 ( val addr -- ) {
+       size_t addr = (size_t)T0_POP();
+       *(uint32_t *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP();
+}
+
+\ Define a word that evaluates as an address of a field within the
+\ engine context. The field name (C identifier) must follow in the
+\ source. For field 'foo', the defined word is 'addr-foo'.
+: addr-eng:
+       next-word { field }
+       "addr-" field + 0 1 define-word
+       0 8191 "offsetof(br_ssl_engine_context, " field + ")" + make-CX
+       postpone literal postpone ; ;
+
+addr-eng: max_frag_len
+addr-eng: log_max_frag_len
+addr-eng: peer_log_max_frag_len
+addr-eng: shutdown_recv
+addr-eng: record_type_in
+addr-eng: record_type_out
+addr-eng: version_in
+addr-eng: version_out
+addr-eng: application_data
+addr-eng: version_min
+addr-eng: version_max
+addr-eng: suites_buf
+addr-eng: suites_num
+addr-eng: server_name
+\ addr-eng: version
+\ addr-eng: cipher_suite
+addr-eng: client_random
+addr-eng: server_random
+\ addr-eng: session_id_len
+\ addr-eng: session_id
+addr-eng: ecdhe_curve
+addr-eng: ecdhe_point
+addr-eng: ecdhe_point_len
+addr-eng: reneg
+addr-eng: saved_finished
+addr-eng: pad
+addr-eng: action
+addr-eng: alert
+addr-eng: close_received
+
+\ Similar to 'addr-eng:', for fields in the 'session' substructure.
+: addr-session-field:
+       next-word { field }
+       "addr-" field + 0 1 define-word
+       0 8191 "offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, " field + ")" + make-CX
+       postpone literal postpone ; ;
+
+addr-session-field: session_id
+addr-session-field: session_id_len
+addr-session-field: version
+addr-session-field: cipher_suite
+addr-session-field: master_secret
+
+\ Define a word that evaluates to an error constant. This assumes that
+\ all relevant error codes are in the 0..63 range.
+: err:
+       next-word { name }
+       name 0 1 define-word
+       0 63 "BR_" name + make-CX postpone literal postpone ; ;
+
+err: ERR_OK
+err: ERR_BAD_PARAM
+err: ERR_BAD_STATE
+err: ERR_UNSUPPORTED_VERSION
+err: ERR_BAD_VERSION
+err: ERR_BAD_LENGTH
+err: ERR_TOO_LARGE
+err: ERR_BAD_MAC
+err: ERR_NO_RANDOM
+err: ERR_UNKNOWN_TYPE
+err: ERR_UNEXPECTED
+err: ERR_BAD_CCS
+err: ERR_BAD_ALERT
+err: ERR_BAD_HANDSHAKE
+err: ERR_OVERSIZED_ID
+err: ERR_BAD_CIPHER_SUITE
+err: ERR_BAD_COMPRESSION
+err: ERR_BAD_FRAGLEN
+err: ERR_BAD_SECRENEG
+err: ERR_EXTRA_EXTENSION
+err: ERR_BAD_SNI
+err: ERR_BAD_HELLO_DONE
+err: ERR_LIMIT_EXCEEDED
+err: ERR_BAD_FINISHED
+err: ERR_RESUME_MISMATCH
+err: ERR_INVALID_ALGORITHM
+
+\ Get supported curves (bit mask).
+cc: supported-curves ( -- x ) {
+       uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves;
+       T0_PUSH(x);
+}
+
+\ Get supported hash functions (bit mask and number).
+cc: supported-hash-functions ( -- x num ) {
+       int i;
+       unsigned x, num;
+
+       x = 0;
+       num = 0;
+       for (i = br_sha1_ID; i <= br_sha512_ID; i ++) {
+               if (br_multihash_getimpl(&ENG->mhash, i)) {
+                       x |= 1U << i;
+                       num ++;
+               }
+       }
+       T0_PUSH(x);
+       T0_PUSH(num);
+}
+
+\ (Re)initialise the multihasher.
+cc: multihash-init ( -- ) {
+       br_multihash_init(&ENG->mhash);
+}
+
+\ Flush the current record: if some payload data has been accumulated,
+\ close the record and schedule it for sending. If there is no such data,
+\ this function does nothing.
+cc: flush-record ( -- ) {
+       br_ssl_engine_flush_record(ENG);
+}
+
+\ Yield control to the caller.
+\ When the control is returned to us, react to the new context. Returned
+\ value is a bitwise combination of the following:
+\   0x01   handshake data is available
+\   0x02   change-cipher-spec data is available
+\   0x04   some data other than handshake or change-cipher-spec is available
+\   0x08   output buffer is ready for a new outgoing record
+\   0x10   renegotiation is requested and not to be ignored
+\ Flags 0x01, 0x02 and 0x04 are mutually exclusive.
+: wait-co ( -- state )
+       co
+       0
+       addr-action get8 dup if
+               case
+                       1 of 0 do-close endof
+                       2 of addr-application_data get8 if 0x10 or then endof
+               endcase
+       else
+               drop
+       then
+       addr-close_received get8 ifnot
+               has-input? if
+                       addr-record_type_in get8 case
+
+                               \ ChangeCipherSpec
+                               20 of 0x02 or endof
+
+                               \ Alert -- if close_notify received, trigger
+                               \ the closure sequence.
+                               21 of process-alerts if -1 do-close then endof
+
+                               \ Handshake
+                               22 of 0x01 or endof
+
+                               \ Not CCS, Alert or Handshake.
+                               drop 0x04 or 0
+                       endcase
+               then
+       then
+       can-output? if 0x08 or then ;
+
+\ Send an alert message. This shall be called only when there is room for
+\ an outgoing record.
+: send-alert ( level alert -- )
+       21 addr-record_type_out set8
+       swap write8-native drop write8-native drop
+       flush-record ;
+
+\ Send an alert message of level "warning". This shall be called only when
+\ there is room for an outgoing record.
+: send-warning ( alert -- )
+       1 swap send-alert ;
+
+\ Fail by sending a fatal alert.
+: fail-alert ( alert -- ! )
+       { alert }
+       flush-record
+       begin can-output? not while wait-co drop repeat
+       2 alert send-alert
+       begin can-output? not while wait-co drop repeat
+       alert 512 + fail ;
+
+\ Perform the close operation:
+\ -- Prevent new application data from the caller.
+\ -- Incoming data is discarded (except alerts).
+\ -- Outgoing data is flushed.
+\ -- A close_notify alert is sent.
+\ -- If 'cnr' is zero, then incoming data is discarded until a close_notify
+\    is received.
+\ -- At the end, the context is terminated.
+: do-close ( cnr -- ! )
+       \ 'cnr' is set to non-zero when a close_notify is received from
+       \ the peer.
+       { cnr }
+
+       \ Get out of application data state.
+       0 addr-application_data set8
+
+       \ Flush existing payload if any.
+       flush-record
+
+       \ Wait for room to send the close_notify. Since individual records
+       \ can always hold at least 512 bytes, we know that when there is
+       \ room, then there is room for a complete close_notify (two bytes).
+       begin can-output? not while cnr wait-for-close >cnr repeat
+
+       \ Write the close_notify and flush it.
+       \ 21 addr-record_type_out set8
+       \ 1 write8-native 0 write8-native 2drop
+       \ flush-record
+       0 send-warning
+
+       \ Loop until our record has been sent (we know it's gone when
+       \ writing is again possible) and a close_notify has been received.
+       cnr
+       begin
+               dup can-output? and if ERR_OK fail then
+               wait-for-close
+       again ;
+
+\ Yield control to the engine, with a possible flush. If 'cnr' is 0,
+\ then input is analysed: all input is discarded, until a close_notify
+\ is received.
+: wait-for-close ( cnr -- cnr )
+       co
+       dup ifnot
+               has-input? if
+                       addr-record_type_in get8 21 = if
+                               drop process-alerts
+                       else
+                               discard-input
+                       then
+               then
+       then ;
+
+\ Test whether there is some accumulated payload that still needs to be
+\ sent.
+cc: payload-to-send? ( -- bool ) {
+       T0_PUSHi(-br_ssl_engine_has_pld_to_send(ENG));
+}
+
+\ Test whether there is some available input data.
+cc: has-input? ( -- bool ) {
+       T0_PUSHi(-(ENG->hlen_in != 0));
+}
+
+\ Test whether some payload bytes may be written.
+cc: can-output? ( -- bool ) {
+       T0_PUSHi(-(ENG->hlen_out > 0));
+}
+
+\ Discard current input entirely.
+cc: discard-input ( -- ) {
+       ENG->hlen_in = 0;
+}
+
+\ Low-level read for one byte. If there is no available byte right
+\ away, then -1 is returned. Otherwise, the byte value is returned.
+\ If the current record type is "handshake" then the read byte is also
+\ injected in the multi-hasher.
+cc: read8-native ( -- x ) {
+       if (ENG->hlen_in > 0) {
+               unsigned char x;
+
+               x = *ENG->hbuf_in ++;
+               if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, &x, 1);
+               }
+               T0_PUSH(x);
+               ENG->hlen_in --;
+       } else {
+               T0_PUSHi(-1);
+       }
+}
+
+\ Low-level read for several bytes. On entry, this expects an address
+\ (offset in the engine context) and a length; these values designate
+\ where the chunk should go. Upon exit, the new address and length
+\ are pushed; that output length contains how many bytes could not be
+\ read. If there is no available byte for reading, the address and
+\ length are unchanged.
+\ If the current record type is "handshake" then the read bytes are
+\ injected in the multi-hasher.
+cc: read-chunk-native ( addr len -- addr len ) {
+       size_t clen = ENG->hlen_in;
+       if (clen > 0) {
+               uint32_t addr, len;
+
+               len = T0_POP();
+               addr = T0_POP();
+               if ((size_t)len < clen) {
+                       clen = (size_t)len;
+               }
+               memcpy((unsigned char *)ENG + addr, ENG->hbuf_in, clen);
+               if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen);
+               }
+               T0_PUSH(addr + (uint32_t)clen);
+               T0_PUSH(len - (uint32_t)clen);
+               ENG->hbuf_in += clen;
+               ENG->hlen_in -= clen;
+       }
+}
+
+\ Process available alert bytes. If a fatal alert is received, then the
+\ context is terminated; otherwise, this returns either true (-1) if a
+\ close_notify was received, false (0) otherwise.
+: process-alerts ( -- bool )
+       0
+       begin has-input? while read8-native process-alert-byte or repeat
+       dup if 1 addr-shutdown_recv set8 then ;
+
+\ Process an alert byte. Returned value is non-zero if this is a close_notify,
+\ zero otherwise.
+: process-alert-byte ( x -- bool )
+       addr-alert get8 case
+               0 of
+                       \ 'alert' field is 0, so this byte shall be a level.
+                       \ Levels shall be 1 (alert) or 2 (fatal); we convert
+                       \ all other values to "fatal".
+                       dup 1 <> if drop 2 then
+                       addr-alert set8 0
+               endof
+               1 of
+                       0 addr-alert set8
+                       \ close_notify has value 0.
+                       0= ret
+               endof
+               \ Fatal alert implies context termination.
+               256 + fail
+       endcase ;
+
+\ In general we only deal with handshake data here. Alerts are processed
+\ in specific code right when they are received, and ChangeCipherSpec has
+\ its own handling code. So we need to check that the data is "handshake"
+\ only when returning from a coroutine call.
+
+\ Yield control to the engine. Alerts are processed; if incoming data is
+\ neither handshake or alert, then an error is triggered.
+: wait-for-handshake ( -- )
+       wait-co 0x07 and 0x01 > if ERR_UNEXPECTED fail then ;
+
+\ Flush outgoing data (if any), then wait for the output buffer to be
+\ clear; when this is done, set the output record type to the specified
+\ value.
+: wait-rectype-out ( rectype -- )
+       { rectype }
+       flush-record
+       begin
+               can-output? if rectype addr-record_type_out set8 ret then
+               wait-co drop
+       again ;
+
+\ Read one byte of handshake data. Block until that byte is available.
+\ This does not check any length.
+: read8-nc ( -- x )
+       begin
+               read8-native dup 0< ifnot ret then
+               drop wait-for-handshake
+       again ;
+
+\ Test whether there are some more bytes in the current record. These
+\ bytes have not necessarily been received yet (processing of unencrypted
+\ records may begin before all bytes are received).
+cc: more-incoming-bytes? ( -- bool ) {
+       T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG));
+}
+
+\ For reading functions, the TOS is supposed to contain the number of bytes
+\ that can still be read (from encapsulating structure header), and it is
+\ updated.
+
+: check-len ( lim len -- lim )
+       - dup 0< if ERR_BAD_PARAM fail then ;
+
+\ Read one byte of handshake data. This pushes an integer in the 0..255 range.
+: read8 ( lim -- lim x )
+       1 check-len read8-nc ;
+
+\ Read a 16-bit value (in the 0..65535 range)
+: read16 ( lim -- lim n )
+       2 check-len read8-nc 8 << read8-nc + ;
+
+\ Read a 24-bit value (in the 0..16777215 range)
+: read24 ( lim -- lim n )
+       3 check-len read8-nc 8 << read8-nc + 8 << read8-nc + ;
+
+\ Read some bytes. The "address" is an offset within the context
+\ structure.
+: read-blob ( lim addr len -- lim )
+       { addr len }
+       len check-len
+       addr len
+       begin
+               read-chunk-native
+               dup 0 = if 2drop ret then
+               wait-for-handshake
+       again ;
+
+\ Read some bytes and drop them.
+: skip-blob ( lim len -- lim )
+       swap over check-len swap
+       begin dup while read8-nc drop 1- repeat
+       drop ;
+
+\ Read a 16-bit length, then skip exactly that many bytes.
+: read-ignore-16 ( lim -- lim )
+       read16 skip-blob ;
+
+\ Open a substructure: the inner structure length is checked against,
+\ and substracted, from the output structure current limit.
+: open-elt ( lim len -- lim-outer lim-inner )
+       dup { len }
+       - dup 0< if ERR_BAD_PARAM fail then
+       len ;
+
+\ Close the current structure. This checks that the limit is 0.
+: close-elt ( lim -- )
+       if ERR_BAD_PARAM fail then ;
+
+\ Write one byte of handshake data.
+: write8 ( n -- )
+       begin
+               dup write8-native if drop ret then
+               wait-co drop
+       again ;
+
+\ Low-level write for one byte. On exit, it pushes either -1 (byte was
+\ written) or 0 (no room in output buffer).
+cc: write8-native ( x -- bool ) {
+       unsigned char x;
+
+       x = (unsigned char)T0_POP();
+       if (ENG->hlen_out > 0) {
+               if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, &x, 1);
+               }
+               *ENG->hbuf_out ++ = x;
+               ENG->hlen_out --;
+               T0_PUSHi(-1);
+       } else {
+               T0_PUSHi(0);
+       }
+}
+
+\ Write a 16-bit value.
+: write16 ( n -- )
+       dup 8 u>> write8 write8 ;
+
+\ Write a 24-bit value.
+: write24 ( n -- )
+       dup 16 u>> write8 write16 ;
+
+\ Write some bytes. The "address" is an offset within the context
+\ structure.
+: write-blob ( addr len -- )
+       begin
+               write-blob-chunk
+               dup 0 = if 2drop ret then
+               wait-co drop
+       again ;
+
+cc: write-blob-chunk ( addr len -- addr len ) {
+       size_t clen = ENG->hlen_out;
+       if (clen > 0) {
+               uint32_t addr, len;
+
+               len = T0_POP();
+               addr = T0_POP();
+               if ((size_t)len < clen) {
+                       clen = (size_t)len;
+               }
+               memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen);
+               if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen);
+               }
+               T0_PUSH(addr + (uint32_t)clen);
+               T0_PUSH(len - (uint32_t)clen);
+               ENG->hbuf_out += clen;
+               ENG->hlen_out -= clen;
+       }
+}
+
+\ Write a blob with the length as header (over one byte)
+: write-blob-head8 ( addr len -- )
+       dup write8 write-blob ;
+
+\ Write a blob with the length as header (over two bytes)
+: write-blob-head16 ( addr len -- )
+       dup write16 write-blob ;
+
+\ Perform a byte-to-byte comparison between two blobs. Each blob is
+\ provided as an "address" (offset in the context structure); the
+\ length is common. Returned value is true (-1) if the two blobs are
+\ equal, false (0) otherwise.
+cc: memcmp ( addr1 addr2 len -- bool ) {
+       size_t len = (size_t)T0_POP();
+       void *addr2 = (unsigned char *)ENG + (size_t)T0_POP();
+       void *addr1 = (unsigned char *)ENG + (size_t)T0_POP();
+       int x = memcmp(addr1, addr2, len);
+       T0_PUSH((uint32_t)-(x == 0));
+}
+
+\ Copy bytes between two areas, whose addresses are provided as
+\ offsets in the context structure.
+cc: memcpy ( dst src len -- ) {
+       size_t len = (size_t)T0_POP();
+       void *src = (unsigned char *)ENG + (size_t)T0_POP();
+       void *dst = (unsigned char *)ENG + (size_t)T0_POP();
+       memcpy(dst, src, len);
+}
+
+\ Get string length (zero-terminated). The string address is provided as
+\ an offset relative to the context start. Returned length does not include
+\ the terminated 0.
+cc: strlen ( str -- len ) {
+       void *str = (unsigned char *)ENG + (size_t)T0_POP();
+       T0_PUSH((uint32_t)strlen(str));
+}
+
+\ Fill a buffer with zeros. The buffer address is an offset in the context.
+cc: bzero ( addr len -- ) {
+       size_t len = (size_t)T0_POP();
+       void *addr = (unsigned char *)ENG + (size_t)T0_POP();
+       memset(addr, 0, len);
+}
+
+\ Scan the list of supported cipher suites for a given value. If found,
+\ then the list index at which it was found is returned; otherwise, -1
+\ is returned.
+: scan-suite ( suite -- index )
+       { suite }
+       addr-suites_num get8 { num }
+       0
+       begin dup num < while
+               dup 1 << addr-suites_buf + get16 suite = if ret then
+               1+
+       repeat
+       drop -1 ;
+
+\ =======================================================================
+
+\ Generate random bytes into buffer (address is offset in context).
+cc: mkrand ( addr len -- ) {
+       size_t len = (size_t)T0_POP();
+       void *addr = (unsigned char *)ENG + (size_t)T0_POP();
+       br_hmac_drbg_generate(&ENG->rng, addr, len);
+}
+
+\ Read a handshake message header: type and length. These are returned
+\ in reverse order (type is TOS, length is below it).
+: read-handshake-header-core ( -- lim type )
+       read8-nc 3 read24 swap drop swap ;
+
+\ Read a handshake message header: type and length. If the header is for
+\ a HelloRequest message, then it is discarded and a new header is read
+\ (repeatedly if necessary).
+: read-handshake-header ( -- lim type )
+       begin
+               read-handshake-header-core dup 0= while
+               drop if ERR_BAD_HANDSHAKE fail then
+       repeat ;
+
+\ =======================================================================
+
+\ Cipher suite processing.
+\
+\ Unfortunately, cipher suite identifiers are attributed mostly arbitrary,
+\ so we have to map the cipher suite numbers we support into aggregate
+\ words that encode the information we need. Table below is organized
+\ as a sequence of pairs of 16-bit words, the first being the cipher suite
+\ identifier, the second encoding the algorithm elements. The suites are
+\ ordered by increasing cipher suite ID, so that fast lookups may be
+\ performed with a binary search (not implemented for the moment, since it
+\ does not appear to matter much in practice).
+\
+\ Algorithm elements are encoded over 4 bits each, in the following order
+\ (most significant to least significant):
+\ 
+\ -- Server key type:
+\       0  RSA           (RSA key exchange)
+\       1  ECDHE-RSA     (ECDHE key exchange, RSA signature)
+\       2  ECDHE-ECDSA   (ECDHE key exchange, ECDSA signature)
+\       3  ECDH-RSA      (ECDH key exchange, certificate is RSA-signed)
+\       4  ECDH-ECDSA    (ECDH key exchange, certificate is ECDSA-signed)
+\ -- Encryption algorithm:
+\       0  3DES/CBC
+\       1  AES-128/CBC
+\       2  AES-256/CBC
+\       3  AES-128/GCM
+\       4  AES-256/GCM
+\       5  ChaCha20/Poly1305
+\ -- MAC algorithm:
+\       0  none         (for suites with AEAD encryption)
+\       2  HMAC/SHA-1
+\       4  HMAC/SHA-256
+\       5  HMAC/SHA-384
+\ -- PRF for TLS-1.2:
+\       4  with SHA-256
+\       5  with SHA-384
+
+data: cipher-suite-def
+
+hexb| 000A 0024 | \ TLS_RSA_WITH_3DES_EDE_CBC_SHA
+hexb| 002F 0124 | \ TLS_RSA_WITH_AES_128_CBC_SHA
+hexb| 0035 0224 | \ TLS_RSA_WITH_AES_256_CBC_SHA
+hexb| 003C 0144 | \ TLS_RSA_WITH_AES_128_CBC_SHA256
+hexb| 003D 0244 | \ TLS_RSA_WITH_AES_256_CBC_SHA256
+
+hexb| 009C 0304 | \ TLS_RSA_WITH_AES_128_GCM_SHA256
+hexb| 009D 0405 | \ TLS_RSA_WITH_AES_256_GCM_SHA384
+
+hexb| C003 4024 | \ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
+hexb| C004 4124 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
+hexb| C005 4224 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
+hexb| C008 2024 | \ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
+hexb| C009 2124 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+hexb| C00A 2224 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+hexb| C00D 3024 | \ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
+hexb| C00E 3124 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
+hexb| C00F 3224 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
+hexb| C012 1024 | \ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+hexb| C013 1124 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+hexb| C014 1224 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+
+hexb| C023 2144 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
+hexb| C024 2255 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
+hexb| C025 4144 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
+hexb| C026 4255 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
+hexb| C027 1144 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+hexb| C028 1255 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
+hexb| C029 3144 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
+hexb| C02A 3255 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
+hexb| C02B 2304 | \ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+hexb| C02C 2405 | \ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+hexb| C02D 4304 | \ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+hexb| C02E 4405 | \ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
+hexb| C02F 1304 | \ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+hexb| C030 1405 | \ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+hexb| C031 3304 | \ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+hexb| C032 3405 | \ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
+
+hexb| CCA8 1504 | \ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+hexb| CCA9 2504 | \ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
+
+hexb| 0000 | \ List terminator.
+
+\ Convert cipher suite identifier to element words. This returns 0 if
+\ the cipher suite is not known.
+: cipher-suite-to-elements ( suite -- elts )
+       { id }
+       cipher-suite-def
+       begin
+               dup 2+ swap data-get16
+               dup ifnot 2drop 0 ret then
+               id = if data-get16 ret then
+               2+
+       again ;
+
+\ Check that a given cipher suite is supported.
+: suite-supported? ( suite -- bool )
+       cipher-suite-to-elements 0<> ;
+
+\ Get expected key type for cipher suite. The key type is one of
+\ BR_KEYTYPE_RSA or BR_KEYTYPE_EC, combined with either BR_KEYTYPE_KEYX
+\ (RSA encryption or static ECDH) or BR_KEYTYPE_SIGN (RSA or ECDSA
+\ signature, for ECDHE cipher suites).
+: expected-key-type ( suite -- key-type )
+       cipher-suite-to-elements 12 >>
+       case
+               0 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX } endof
+               1 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN } endof
+               2 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_SIGN } endof
+               3 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX } endof
+               4 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX } endof
+               0 swap
+       endcase ;
+
+\ Test whether the cipher suite uses RSA key exchange.
+: use-rsa-keyx? ( suite -- bool )
+       cipher-suite-to-elements 12 >> 0= ;
+
+\ Test whether the cipher suite uses ECDHE key exchange, signed with RSA.
+: use-rsa-ecdhe? ( suite -- bool )
+       cipher-suite-to-elements 12 >> 1 = ;
+
+\ Test whether the cipher suite uses ECDHE key exchange, signed with ECDSA.
+: use-ecdsa-ecdhe? ( suite -- bool )
+       cipher-suite-to-elements 12 >> 2 = ;
+
+\ Test whether the cipher suite uses ECDHE key exchange (with RSA or ECDSA).
+: use-ecdhe? ( suite -- bool )
+       cipher-suite-to-elements 12 >> dup 0> swap 3 < and ;
+
+\ Test whether the cipher suite uses ECDH (static) key exchange.
+: use-ecdh? ( suite -- bool )
+       cipher-suite-to-elements 12 >> 2 > ;
+
+\ Get identifier for the PRF (TLS 1.2).
+: prf-id ( suite -- id )
+       cipher-suite-to-elements 15 and ;
+
+\ Switch to negotiated security parameters for input or output.
+: switch-encryption ( is-client for-input -- )
+       { for-input }
+       addr-cipher_suite get16 cipher-suite-to-elements { elts }
+
+       \ prf_id
+       elts 15 and
+
+       \ mac_id
+       elts 4 >> 15 and
+
+       \ cipher type and key length
+       elts 8 >> 15 and case
+               \ 3DES/CBC
+               0 of 0 24
+                       for-input if
+                               switch-cbc-in
+                       else
+                               switch-cbc-out
+                       then
+               endof
+
+               \ AES-128/CBC
+               1 of 1 16
+                       for-input if
+                               switch-cbc-in
+                       else
+                               switch-cbc-out
+                       then
+               endof
+
+               \ AES-256/CBC
+               2 of 1 32
+                       for-input if
+                               switch-cbc-in
+                       else
+                               switch-cbc-out
+                       then
+               endof
+
+               \ AES-128/GCM
+               3 of drop 16
+                       for-input if
+                               switch-aesgcm-in
+                       else
+                               switch-aesgcm-out
+                       then
+               endof
+
+               \ AES-256/GCM
+               4 of drop 32
+                       for-input if
+                               switch-aesgcm-in
+                       else
+                               switch-aesgcm-out
+                       then
+               endof
+
+               \ ChaCha20/Poly1305
+               \ 5 of endof
+
+               ERR_BAD_PARAM fail
+       endcase
+       ;
+
+cc: switch-cbc-out ( is_client prf_id mac_id aes cipher_key_len -- ) {
+       int is_client, prf_id, mac_id, aes;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       aes = T0_POP();
+       mac_id = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id,
+               aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len);
+}
+
+cc: switch-cbc-in ( is_client prf_id mac_id aes cipher_key_len -- ) {
+       int is_client, prf_id, mac_id, aes;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       aes = T0_POP();
+       mac_id = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id,
+               aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len);
+}
+
+cc: switch-aesgcm-out ( is_client prf_id cipher_key_len -- ) {
+       int is_client, prf_id;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id,
+               ENG->iaes_ctr, cipher_key_len);
+}
+
+cc: switch-aesgcm-in ( is_client prf_id cipher_key_len -- ) {
+       int is_client, prf_id;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id,
+               ENG->iaes_ctr, cipher_key_len);
+}
+
+\ Write Finished message.
+: write-Finished ( from_client -- )
+       compute-Finished
+       20 write8 12 write24 addr-pad 12 write-blob ;
+
+\ Read Finished message.
+: read-Finished ( from_client -- )
+       compute-Finished
+       read-handshake-header 20 <> if ERR_UNEXPECTED fail then
+       addr-pad 12 + 12 read-blob
+       close-elt
+       addr-pad dup 12 + 12 memcmp ifnot ERR_BAD_FINISHED fail then ;
+
+\ Compute the "Finished" contents (either the value to send, or the
+\ expected value). The 12-byte string is written in the pad. The
+\ "from_client" value is non-zero for the Finished sent by the client.
+\ The computed value is also saved in the relevant buffer for handling
+\ secure renegotiation.
+: compute-Finished ( from_client -- )
+       dup addr-saved_finished swap ifnot 12 + then swap
+       addr-cipher_suite get16 prf-id compute-Finished-inner
+       addr-pad 12 memcpy ;
+
+cc: compute-Finished-inner ( from_client prf_id -- ) {
+       int prf_id = T0_POP();
+       int from_client = T0_POPi();
+       unsigned char seed[48];
+       size_t seed_len;
+
+       br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+       if (ENG->session.version >= BR_TLS12) {
+               seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+       } else {
+               br_multihash_out(&ENG->mhash, br_md5_ID, seed);
+               br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
+               seed_len = 36;
+       }
+       prf(ENG->pad, 12, ENG->session.master_secret,
+               sizeof ENG->session.master_secret,
+               from_client ? "client finished" : "server finished",
+               seed, seed_len);
+}
+
+\ Receive ChangeCipherSpec and Finished from the peer.
+: read-CCS-Finished ( is-client -- )
+       has-input? if
+               addr-record_type_in get8 20 <> if ERR_UNEXPECTED fail then
+       else
+               begin
+                       wait-co 0x07 and dup 0x02 <> while
+                       if ERR_UNEXPECTED fail then
+               repeat
+               drop
+       then
+       read8-nc 1 <> more-incoming-bytes? or if ERR_BAD_CCS fail then
+       dup 1 switch-encryption
+
+       \ Read and verify Finished from peer.
+       not read-Finished ;
+
+\ Send ChangeCipherSpec and Finished to the peer.
+: write-CCS-Finished ( is-client -- )
+       \ Flush and wait for output buffer to be clear, so that we may
+       \ write our ChangeCipherSpec. We must switch immediately after
+       \ triggering the flush.
+       20 wait-rectype-out
+       1 write8
+       flush-record
+       dup 0 switch-encryption
+       22 wait-rectype-out
+       write-Finished
+       flush-record ;
diff --git a/src/ssl/ssl_hs_server.c b/src/ssl/ssl_hs_server.c
new file mode 100644 (file)
index 0000000..060e2fb
--- /dev/null
@@ -0,0 +1,1479 @@
+/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+
+
+void br_ssl_hs_server_init_main(void *t0ctx);
+
+void br_ssl_hs_server_run(void *t0ctx);
+
+
+
+#include <stddef.h>
+#include <string.h>
+
+#include "inner.h"
+
+/*
+ * This macro evaluates to a pointer to the current engine context.
+ */
+#define ENG  ((br_ssl_engine_context *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu)))
+
+
+
+
+
+/*
+ * This macro evaluates to a pointer to the server context, under that
+ * specific name. It must be noted that since the engine context is the
+ * first field of the br_ssl_server_context structure ('eng'), then
+ * pointers values of both types are interchangeable, modulo an
+ * appropriate cast. This also means that "adresses" computed as offsets
+ * within the structure work for both kinds of context.
+ */
+#define CTX  ((br_ssl_server_context *)ENG)
+
+/*
+ * Decrypt the pre-master secret (RSA key exchange).
+ */
+static void
+do_rsa_decrypt(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *epms, size_t len)
+{
+       uint32_t x;
+       unsigned char rpms[48];
+
+       /*
+        * Decrypt the PMS.
+        */
+       x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, len);
+
+       /*
+        * Set the first two bytes to the maximum supported client
+        * protocol version. These bytes are used for version rollback
+        * detection; forceing the two bytes will make the master secret
+        * wrong if the bytes are not correct. This process is
+        * recommended by RFC 5246 (section 7.4.7.1).
+        */
+       br_enc16be(epms, ctx->client_max_version);
+
+       /*
+        * Make a random PMS and copy it above the decrypted value if the
+        * decryption failed. Note that we use a constant-time conditional
+        * copy.
+        */
+       br_hmac_drbg_generate(&ctx->eng.rng, rpms, sizeof rpms);
+       br_ccopy(x ^ 1, epms, rpms, sizeof rpms);
+
+       /*
+        * Compute master secret.
+        */
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, epms, 48);
+
+       /*
+        * Clear the pre-master secret from RAM: it is normally a buffer
+        * in the context, hence potentially long-lived.
+        */
+       memset(epms, 0, len);
+}
+
+/*
+ * Common part for ECDH and ECDHE.
+ */
+static void
+ecdh_common(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *cpoint, size_t cpoint_len, uint32_t ctl)
+{
+       unsigned char rpms[80];
+       size_t pms_len;
+
+       /*
+        * The point length is supposed to be 1+2*Xlen, where Xlen is
+        * the length (in bytes) of the X coordinate, i.e. the pre-master
+        * secret. If the provided point is too large, then it is
+        * obviously incorrect (i.e. everybody can see that it is
+        * incorrect), so leaking that fact is not a problem.
+        */
+       pms_len = cpoint_len >> 1;
+       if (pms_len > sizeof rpms) {
+               pms_len = sizeof rpms;
+               ctl = 0;
+       }
+
+       /*
+        * Make a random PMS and copy it above the decrypted value if the
+        * decryption failed. Note that we use a constant-time conditional
+        * copy.
+        */
+       br_hmac_drbg_generate(&ctx->eng.rng, rpms, pms_len);
+       br_ccopy(ctl ^ 1, cpoint + 1, rpms, pms_len);
+
+       /*
+        * Compute master secret.
+        */
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, cpoint + 1, pms_len);
+
+       /*
+        * Clear the pre-master secret from RAM: it is normally a buffer
+        * in the context, hence potentially long-lived.
+        */
+       memset(cpoint, 0, cpoint_len);
+}
+
+/*
+ * Do the ECDH key exchange (not ECDHE).
+ */
+static void
+do_ecdh(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *cpoint, size_t cpoint_len)
+{
+       uint32_t x;
+
+       /*
+        * Finalise the key exchange.
+        */
+       x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable,
+               cpoint, cpoint_len);
+       ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
+}
+
+/*
+ * Do the ECDHE key exchange (part 1: generation of transient key, and
+ * computing of the point to send to the client). Returned value is the
+ * signature length (in bytes), or -x on error (with x being an error
+ * code). The encoded point is written in the ecdhe_point[] context buffer
+ * (length in ecdhe_point_len).
+ */
+static int
+do_ecdhe_part1(br_ssl_server_context *ctx, int curve)
+{
+       int hash;
+       unsigned mask;
+       const unsigned char *order, *generator;
+       size_t olen, glen;
+       br_multihash_context mhc;
+       unsigned char head[4];
+       size_t hv_len, sig_len;
+
+       if (!((ctx->eng.iec->supported_curves >> curve) & 1)) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+       ctx->eng.ecdhe_curve = curve;
+
+       /*
+        * Generate our private key. We need a non-zero random value
+        * which is lower than the curve order, in a "large enough"
+        * range. We force the top bit to 0 and bottom bit to 1, which
+        * does the trick. Note that contrary to what happens in ECDSA,
+        * this is not a problem if we do not cover the full range of
+        * possible values.
+        */
+       order = ctx->eng.iec->order(curve, &olen);
+       mask = 0xFF;
+       while (mask >= order[0]) {
+               mask >>= 1;
+       }
+       br_hmac_drbg_generate(&ctx->eng.rng, ctx->ecdhe_key, olen);
+       ctx->ecdhe_key[0] &= mask;
+       ctx->ecdhe_key[olen - 1] |= 0x01;
+       ctx->ecdhe_key_len = olen;
+
+       /*
+        * Compute our ECDH point.
+        */
+       generator = ctx->eng.iec->generator(curve, &glen);
+       memcpy(ctx->eng.ecdhe_point, generator, glen);
+       ctx->eng.ecdhe_point_len = glen;
+       if (!ctx->eng.iec->mul(ctx->eng.ecdhe_point, glen,
+               ctx->ecdhe_key, olen, curve))
+       {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       /*
+        * Compute the signature.
+        */
+       br_multihash_zero(&mhc);
+       br_multihash_copyimpl(&mhc, &ctx->eng.mhash);
+       br_multihash_init(&mhc);
+       br_multihash_update(&mhc,
+               ctx->eng.client_random, sizeof ctx->eng.client_random);
+       br_multihash_update(&mhc,
+               ctx->eng.server_random, sizeof ctx->eng.server_random);
+       head[0] = 3;
+       head[1] = 0;
+       head[2] = curve;
+       head[3] = ctx->eng.ecdhe_point_len;
+       br_multihash_update(&mhc, head, sizeof head);
+       br_multihash_update(&mhc,
+               ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len);
+       hash = ctx->sign_hash_id;
+       if (hash) {
+               hv_len = br_multihash_out(&mhc, hash, ctx->eng.pad);
+               if (hv_len == 0) {
+                       return -BR_ERR_INVALID_ALGORITHM;
+               }
+       } else {
+               if (!br_multihash_out(&mhc, br_md5_ID, ctx->eng.pad)
+                       || !br_multihash_out(&mhc,
+                       br_sha1_ID, ctx->eng.pad + 16))
+               {
+                       return -BR_ERR_INVALID_ALGORITHM;
+               }
+               hv_len = 36;
+       }
+       sig_len = (*ctx->policy_vtable)->do_sign(ctx->policy_vtable,
+               hash, hv_len, ctx->eng.pad, sizeof ctx->eng.pad);
+       return sig_len ? (int)sig_len : -BR_ERR_INVALID_ALGORITHM;
+}
+
+/*
+ * Do the ECDHE key exchange (part 2: computation of the shared secret
+ * from the point sent by the client).
+ */
+static void
+do_ecdhe_part2(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *cpoint, size_t cpoint_len)
+{
+       int curve;
+       uint32_t x;
+
+       curve = ctx->eng.ecdhe_curve;
+
+       /*
+        * Finalise the key exchange.
+        */
+       x = ctx->eng.iec->mul(cpoint, cpoint_len,
+               ctx->ecdhe_key, ctx->ecdhe_key_len, curve);
+       ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
+
+       /*
+        * Clear the ECDHE private key. Forward Secrecy is achieved insofar
+        * as that key does not get stolen, so we'd better destroy it
+        * as soon as it ceases to be useful.
+        */
+       memset(ctx->ecdhe_key, 0, ctx->ecdhe_key_len);
+}
+
+
+
+static const uint8_t t0_datablock[] = {
+       0x00, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x2F, 0x01, 0x24, 0x00, 0x35, 0x02,
+       0x24, 0x00, 0x3C, 0x01, 0x44, 0x00, 0x3D, 0x02, 0x44, 0x00, 0x9C, 0x03,
+       0x04, 0x00, 0x9D, 0x04, 0x05, 0xC0, 0x03, 0x40, 0x24, 0xC0, 0x04, 0x41,
+       0x24, 0xC0, 0x05, 0x42, 0x24, 0xC0, 0x08, 0x20, 0x24, 0xC0, 0x09, 0x21,
+       0x24, 0xC0, 0x0A, 0x22, 0x24, 0xC0, 0x0D, 0x30, 0x24, 0xC0, 0x0E, 0x31,
+       0x24, 0xC0, 0x0F, 0x32, 0x24, 0xC0, 0x12, 0x10, 0x24, 0xC0, 0x13, 0x11,
+       0x24, 0xC0, 0x14, 0x12, 0x24, 0xC0, 0x23, 0x21, 0x44, 0xC0, 0x24, 0x22,
+       0x55, 0xC0, 0x25, 0x41, 0x44, 0xC0, 0x26, 0x42, 0x55, 0xC0, 0x27, 0x11,
+       0x44, 0xC0, 0x28, 0x12, 0x55, 0xC0, 0x29, 0x31, 0x44, 0xC0, 0x2A, 0x32,
+       0x55, 0xC0, 0x2B, 0x23, 0x04, 0xC0, 0x2C, 0x24, 0x05, 0xC0, 0x2D, 0x43,
+       0x04, 0xC0, 0x2E, 0x44, 0x05, 0xC0, 0x2F, 0x13, 0x04, 0xC0, 0x30, 0x14,
+       0x05, 0xC0, 0x31, 0x33, 0x04, 0xC0, 0x32, 0x34, 0x05, 0xCC, 0xA8, 0x15,
+       0x04, 0xCC, 0xA9, 0x25, 0x04, 0x00, 0x00
+};
+
+static const uint8_t t0_codeblock[] = {
+       0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x01,
+       0x00, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x01, 0x01, 0x08,
+       0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x02, 0x08, 0x00, 0x00,
+       0x21, 0x21, 0x00, 0x00, 0x01, T0_INT1(BR_ERR_BAD_CCS), 0x00, 0x00,
+       0x01, T0_INT1(BR_ERR_BAD_FINISHED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_FRAGLEN), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_HANDSHAKE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_PARAM), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_SECRENEG), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_BAD_VERSION), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OK),
+       0x00, 0x00, 0x01, T0_INT1(BR_ERR_OVERSIZED_ID), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_UNEXPECTED), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, action)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, alert)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, application_data)), 0x00, 0x00,
+       0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, cipher_suite)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_server_context, client_max_version)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, client_random)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_server_context, client_suites)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_server_context, client_suites_num)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, close_received)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_server_context, curves)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point_len)),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_server_context, flags)),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_server_context, hashes)),
+       0x00, 0x00, 0x5D, 0x01,
+       T0_INT2(BR_MAX_CIPHER_SUITES * sizeof(br_suite_translated)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, log_max_frag_len)),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, pad)), 0x00,
+       0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, peer_log_max_frag_len)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, record_type_in)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, record_type_out)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, reneg)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, saved_finished)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, server_name)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, server_random)), 0x00, 0x00,
+       0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id_len)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, shutdown_recv)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_server_context, sign_hash_id)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_buf)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_num)), 0x00,
+       0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, version)),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_in)),
+       0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_ssl_engine_context, version_max)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_ssl_engine_context, version_min)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_out)),
+       0x00, 0x00, 0x09, 0x22, 0x44, 0x06, 0x02, 0x50, 0x23, 0x00, 0x00, 0x01,
+       0x01, 0x00, 0x01, 0x03, 0x00, 0x7B, 0x22, 0x4A, 0x3B, 0x7F, 0x22, 0x05,
+       0x04, 0x4B, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0E, 0x06, 0x02, 0x7F, 0x00,
+       0x4A, 0x04, 0x6B, 0x00, 0x06, 0x02, 0x50, 0x23, 0x00, 0x00, 0x22, 0x6C,
+       0x3B, 0x05, 0x03, 0x01, 0x0C, 0x08, 0x3B, 0x5A, 0x25, 0x81, 0x07, 0x19,
+       0x67, 0x01, 0x0C, 0x2A, 0x00, 0x00, 0x22, 0x1B, 0x01, 0x08, 0x0B, 0x3B,
+       0x48, 0x1B, 0x08, 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x59, 0x38, 0x24,
+       0x16, 0x2F, 0x06, 0x08, 0x02, 0x00, 0x81, 0x26, 0x03, 0x00, 0x04, 0x74,
+       0x01, 0x00, 0x81, 0x1E, 0x02, 0x00, 0x22, 0x16, 0x12, 0x06, 0x02, 0x54,
+       0x23, 0x81, 0x26, 0x04, 0x75, 0x00, 0x01, 0x00, 0x59, 0x38, 0x01, 0x16,
+       0x6A, 0x38, 0x2D, 0x81, 0x0B, 0x2C, 0x06, 0x02, 0x56, 0x23, 0x06, 0x0C,
+       0x81, 0x2C, 0x01, 0x00, 0x81, 0x29, 0x01, 0x00, 0x81, 0x0A, 0x04, 0x14,
+       0x81, 0x2C, 0x81, 0x2A, 0x81, 0x2E, 0x81, 0x2D, 0x24, 0x81, 0x0C, 0x01,
+       0x00, 0x81, 0x0A, 0x01, 0x00, 0x81, 0x29, 0x34, 0x01, 0x01, 0x59, 0x38,
+       0x01, 0x17, 0x6A, 0x38, 0x00, 0x00, 0x31, 0x31, 0x00, 0x01, 0x03, 0x00,
+       0x24, 0x16, 0x2F, 0x06, 0x05, 0x81, 0x25, 0x21, 0x04, 0x77, 0x01, 0x02,
+       0x02, 0x00, 0x81, 0x1D, 0x16, 0x2F, 0x06, 0x05, 0x81, 0x25, 0x21, 0x04,
+       0x77, 0x02, 0x00, 0x01, 0x84, 0x00, 0x08, 0x23, 0x00, 0x00, 0x63, 0x26,
+       0x3B, 0x11, 0x01, 0x01, 0x12, 0x2E, 0x00, 0x00, 0x01, 0x7F, 0x81, 0x01,
+       0x81, 0x25, 0x22, 0x01, 0x07, 0x12, 0x01, 0x00, 0x31, 0x0E, 0x06, 0x0A,
+       0x21, 0x01, 0x10, 0x12, 0x06, 0x02, 0x81, 0x1C, 0x04, 0x24, 0x01, 0x01,
+       0x31, 0x0E, 0x06, 0x1B, 0x21, 0x21, 0x6B, 0x27, 0x01, 0x01, 0x0D, 0x06,
+       0x06, 0x01, 0x00, 0x81, 0x01, 0x04, 0x0A, 0x24, 0x16, 0x2F, 0x06, 0x05,
+       0x81, 0x25, 0x21, 0x04, 0x77, 0x04, 0x03, 0x56, 0x23, 0x21, 0x04, 0x44,
+       0x01, 0x22, 0x03, 0x00, 0x09, 0x22, 0x44, 0x06, 0x02, 0x50, 0x23, 0x02,
+       0x00, 0x00, 0x00, 0x7C, 0x01, 0x0F, 0x12, 0x00, 0x00, 0x58, 0x27, 0x01,
+       0x00, 0x31, 0x0E, 0x06, 0x10, 0x21, 0x22, 0x01, 0x01, 0x0D, 0x06, 0x03,
+       0x21, 0x01, 0x02, 0x58, 0x38, 0x01, 0x00, 0x04, 0x15, 0x01, 0x01, 0x31,
+       0x0E, 0x06, 0x09, 0x21, 0x01, 0x00, 0x58, 0x38, 0x46, 0x00, 0x04, 0x06,
+       0x01, 0x82, 0x00, 0x08, 0x23, 0x21, 0x00, 0x00, 0x01, 0x00, 0x28, 0x06,
+       0x06, 0x33, 0x81, 0x08, 0x30, 0x04, 0x77, 0x22, 0x06, 0x04, 0x01, 0x01,
+       0x71, 0x38, 0x00, 0x00, 0x28, 0x06, 0x0B, 0x69, 0x27, 0x01, 0x14, 0x0D,
+       0x06, 0x02, 0x56, 0x23, 0x04, 0x12, 0x81, 0x25, 0x01, 0x07, 0x12, 0x22,
+       0x01, 0x02, 0x0D, 0x06, 0x06, 0x06, 0x02, 0x56, 0x23, 0x04, 0x6F, 0x21,
+       0x81, 0x1A, 0x01, 0x01, 0x0D, 0x2C, 0x30, 0x06, 0x02, 0x4C, 0x23, 0x22,
+       0x01, 0x01, 0x81, 0x20, 0x2F, 0x81, 0x0D, 0x00, 0x0A, 0x81, 0x12, 0x01,
+       0x01, 0x0E, 0x05, 0x02, 0x56, 0x23, 0x81, 0x17, 0x22, 0x03, 0x00, 0x5B,
+       0x36, 0x5C, 0x01, 0x20, 0x81, 0x0E, 0x81, 0x19, 0x22, 0x01, 0x20, 0x0F,
+       0x06, 0x02, 0x55, 0x23, 0x22, 0x70, 0x38, 0x6F, 0x3B, 0x81, 0x0E, 0x17,
+       0x03, 0x01, 0x81, 0x17, 0x81, 0x06, 0x01, 0x00, 0x03, 0x02, 0x01, 0x00,
+       0x03, 0x03, 0x65, 0x81, 0x02, 0x14, 0x31, 0x08, 0x03, 0x04, 0x03, 0x05,
+       0x22, 0x06, 0x80, 0x57, 0x81, 0x17, 0x22, 0x03, 0x06, 0x02, 0x01, 0x06,
+       0x0A, 0x22, 0x5A, 0x25, 0x0E, 0x06, 0x04, 0x01, 0x7F, 0x03, 0x03, 0x22,
+       0x01, 0x81, 0x7F, 0x0E, 0x06, 0x0A, 0x6B, 0x27, 0x06, 0x02, 0x51, 0x23,
+       0x01, 0x7F, 0x03, 0x02, 0x81, 0x1B, 0x22, 0x44, 0x06, 0x03, 0x21, 0x04,
+       0x27, 0x01, 0x00, 0x81, 0x04, 0x06, 0x0B, 0x01, 0x02, 0x0B, 0x5D, 0x08,
+       0x02, 0x06, 0x3B, 0x36, 0x04, 0x16, 0x21, 0x02, 0x05, 0x02, 0x04, 0x10,
+       0x06, 0x02, 0x4F, 0x23, 0x02, 0x06, 0x02, 0x05, 0x36, 0x02, 0x05, 0x01,
+       0x04, 0x08, 0x03, 0x05, 0x04, 0xFF, 0x25, 0x21, 0x01, 0x00, 0x03, 0x07,
+       0x81, 0x19, 0x81, 0x06, 0x22, 0x06, 0x0A, 0x81, 0x19, 0x05, 0x04, 0x01,
+       0x7F, 0x03, 0x07, 0x04, 0x73, 0x7D, 0x01, 0x00, 0x6D, 0x38, 0x01, 0x88,
+       0x04, 0x64, 0x36, 0x01, 0x84, 0x80, 0x80, 0x00, 0x60, 0x37, 0x22, 0x06,
+       0x80, 0x58, 0x81, 0x17, 0x81, 0x06, 0x22, 0x06, 0x80, 0x4F, 0x81, 0x17,
+       0x01, 0x00, 0x31, 0x0E, 0x06, 0x05, 0x21, 0x81, 0x11, 0x04, 0x3F, 0x01,
+       0x01, 0x31, 0x0E, 0x06, 0x05, 0x21, 0x81, 0x0F, 0x04, 0x34, 0x01, 0x83,
+       0xFE, 0x01, 0x31, 0x0E, 0x06, 0x05, 0x21, 0x81, 0x10, 0x04, 0x27, 0x01,
+       0x0D, 0x31, 0x0E, 0x06, 0x05, 0x21, 0x81, 0x15, 0x04, 0x1C, 0x01, 0x0A,
+       0x31, 0x0E, 0x06, 0x05, 0x21, 0x81, 0x16, 0x04, 0x11, 0x01, 0x0B, 0x31,
+       0x0E, 0x06, 0x05, 0x21, 0x81, 0x14, 0x04, 0x06, 0x21, 0x81, 0x14, 0x01,
+       0x00, 0x21, 0x04, 0xFF, 0x2D, 0x7D, 0x7D, 0x02, 0x01, 0x02, 0x03, 0x12,
+       0x03, 0x01, 0x77, 0x25, 0x22, 0x02, 0x00, 0x0F, 0x06, 0x03, 0x21, 0x02,
+       0x00, 0x22, 0x01, 0x86, 0x00, 0x0A, 0x06, 0x02, 0x52, 0x23, 0x02, 0x00,
+       0x78, 0x25, 0x0A, 0x06, 0x05, 0x01, 0x80, 0x46, 0x81, 0x03, 0x02, 0x01,
+       0x06, 0x10, 0x75, 0x25, 0x02, 0x00, 0x0C, 0x06, 0x05, 0x21, 0x75, 0x25,
+       0x04, 0x04, 0x01, 0x00, 0x03, 0x01, 0x22, 0x75, 0x36, 0x22, 0x76, 0x36,
+       0x22, 0x79, 0x36, 0x01, 0x86, 0x03, 0x10, 0x03, 0x08, 0x02, 0x02, 0x06,
+       0x04, 0x01, 0x02, 0x6B, 0x38, 0x02, 0x07, 0x05, 0x04, 0x01, 0x28, 0x81,
+       0x03, 0x3A, 0x21, 0x01, 0x82, 0x01, 0x07, 0x64, 0x25, 0x12, 0x22, 0x64,
+       0x36, 0x45, 0x03, 0x09, 0x60, 0x26, 0x39, 0x12, 0x22, 0x60, 0x37, 0x05,
+       0x04, 0x01, 0x00, 0x03, 0x09, 0x02, 0x01, 0x06, 0x03, 0x01, 0x7F, 0x00,
+       0x6F, 0x01, 0x20, 0x2B, 0x5D, 0x22, 0x03, 0x05, 0x22, 0x02, 0x04, 0x0A,
+       0x06, 0x80, 0x47, 0x22, 0x25, 0x22, 0x7C, 0x02, 0x09, 0x05, 0x13, 0x22,
+       0x01, 0x0C, 0x11, 0x22, 0x01, 0x01, 0x0E, 0x3B, 0x01, 0x02, 0x0E, 0x30,
+       0x06, 0x04, 0x4B, 0x01, 0x00, 0x22, 0x02, 0x08, 0x05, 0x0E, 0x22, 0x01,
+       0x81, 0x70, 0x12, 0x01, 0x20, 0x0D, 0x06, 0x04, 0x4B, 0x01, 0x00, 0x22,
+       0x22, 0x06, 0x10, 0x02, 0x05, 0x4A, 0x36, 0x02, 0x05, 0x36, 0x02, 0x05,
+       0x01, 0x04, 0x08, 0x03, 0x05, 0x04, 0x01, 0x4B, 0x01, 0x04, 0x08, 0x04,
+       0xFF, 0x32, 0x21, 0x02, 0x05, 0x5D, 0x09, 0x01, 0x02, 0x11, 0x22, 0x05,
+       0x04, 0x01, 0x28, 0x81, 0x03, 0x5E, 0x38, 0x15, 0x05, 0x04, 0x01, 0x28,
+       0x81, 0x03, 0x01, 0x00, 0x00, 0x04, 0x81, 0x12, 0x01, 0x10, 0x0E, 0x05,
+       0x02, 0x56, 0x23, 0x5A, 0x25, 0x81, 0x24, 0x06, 0x19, 0x81, 0x17, 0x22,
+       0x01, 0x84, 0x00, 0x0F, 0x06, 0x02, 0x53, 0x23, 0x22, 0x03, 0x00, 0x67,
+       0x3B, 0x81, 0x0E, 0x02, 0x00, 0x5A, 0x25, 0x81, 0x07, 0x20, 0x5A, 0x25,
+       0x22, 0x81, 0x22, 0x3B, 0x81, 0x21, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01,
+       0x02, 0x02, 0x30, 0x06, 0x17, 0x81, 0x19, 0x22, 0x03, 0x03, 0x67, 0x3B,
+       0x81, 0x0E, 0x02, 0x03, 0x5A, 0x25, 0x81, 0x07, 0x02, 0x02, 0x06, 0x03,
+       0x1F, 0x04, 0x01, 0x1D, 0x7D, 0x00, 0x00, 0x7E, 0x81, 0x12, 0x01, 0x14,
+       0x0D, 0x06, 0x02, 0x56, 0x23, 0x67, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0x81,
+       0x0E, 0x7D, 0x67, 0x22, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0x29, 0x05, 0x02,
+       0x4D, 0x23, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x02, 0x00, 0x7A, 0x02,
+       0x01, 0x02, 0x00, 0x32, 0x22, 0x01, 0x00, 0x0E, 0x06, 0x02, 0x4B, 0x00,
+       0x81, 0x27, 0x04, 0x73, 0x00, 0x81, 0x17, 0x01, 0x01, 0x0D, 0x06, 0x02,
+       0x4E, 0x23, 0x81, 0x19, 0x22, 0x22, 0x46, 0x3B, 0x01, 0x05, 0x10, 0x30,
+       0x06, 0x02, 0x4E, 0x23, 0x01, 0x08, 0x08, 0x22, 0x66, 0x27, 0x0A, 0x06,
+       0x0D, 0x22, 0x01, 0x01, 0x3B, 0x0B, 0x35, 0x22, 0x66, 0x38, 0x68, 0x38,
+       0x04, 0x01, 0x21, 0x00, 0x00, 0x81, 0x17, 0x6B, 0x27, 0x01, 0x00, 0x31,
+       0x0E, 0x06, 0x14, 0x21, 0x01, 0x01, 0x0E, 0x05, 0x02, 0x51, 0x23, 0x81,
+       0x19, 0x06, 0x02, 0x51, 0x23, 0x01, 0x02, 0x6B, 0x38, 0x04, 0x2A, 0x01,
+       0x02, 0x31, 0x0E, 0x06, 0x21, 0x21, 0x01, 0x0D, 0x0E, 0x05, 0x02, 0x51,
+       0x23, 0x81, 0x19, 0x01, 0x0C, 0x0E, 0x05, 0x02, 0x51, 0x23, 0x67, 0x01,
+       0x0C, 0x81, 0x0E, 0x6C, 0x67, 0x01, 0x0C, 0x29, 0x05, 0x02, 0x51, 0x23,
+       0x04, 0x03, 0x51, 0x23, 0x21, 0x00, 0x00, 0x81, 0x17, 0x81, 0x06, 0x81,
+       0x17, 0x81, 0x06, 0x22, 0x06, 0x22, 0x81, 0x19, 0x06, 0x04, 0x81, 0x14,
+       0x04, 0x18, 0x81, 0x17, 0x22, 0x01, 0x81, 0x7F, 0x0C, 0x06, 0x0D, 0x22,
+       0x6D, 0x08, 0x01, 0x00, 0x3B, 0x38, 0x6D, 0x3B, 0x81, 0x0E, 0x04, 0x02,
+       0x81, 0x1F, 0x04, 0x5B, 0x7D, 0x7D, 0x00, 0x00, 0x81, 0x13, 0x22, 0x46,
+       0x06, 0x07, 0x21, 0x06, 0x02, 0x4F, 0x23, 0x04, 0x73, 0x00, 0x00, 0x81,
+       0x1A, 0x01, 0x03, 0x81, 0x18, 0x3B, 0x21, 0x3B, 0x00, 0x00, 0x81, 0x17,
+       0x81, 0x1F, 0x00, 0x02, 0x81, 0x17, 0x81, 0x06, 0x01, 0x00, 0x64, 0x36,
+       0x81, 0x17, 0x81, 0x06, 0x22, 0x06, 0x34, 0x81, 0x19, 0x03, 0x00, 0x81,
+       0x19, 0x03, 0x01, 0x02, 0x00, 0x01, 0x02, 0x10, 0x02, 0x00, 0x01, 0x06,
+       0x0C, 0x12, 0x02, 0x01, 0x01, 0x01, 0x0E, 0x02, 0x01, 0x01, 0x03, 0x0E,
+       0x30, 0x12, 0x06, 0x11, 0x64, 0x25, 0x01, 0x01, 0x02, 0x01, 0x49, 0x01,
+       0x02, 0x0B, 0x02, 0x00, 0x08, 0x0B, 0x30, 0x64, 0x36, 0x04, 0x49, 0x7D,
+       0x7D, 0x00, 0x00, 0x81, 0x17, 0x81, 0x06, 0x81, 0x17, 0x81, 0x06, 0x01,
+       0x00, 0x60, 0x37, 0x22, 0x06, 0x16, 0x81, 0x17, 0x22, 0x01, 0x20, 0x0A,
+       0x06, 0x0B, 0x01, 0x01, 0x3B, 0x0B, 0x60, 0x26, 0x30, 0x60, 0x37, 0x04,
+       0x01, 0x21, 0x04, 0x67, 0x7D, 0x7D, 0x00, 0x00, 0x01, 0x02, 0x7A, 0x81,
+       0x1A, 0x01, 0x08, 0x0B, 0x81, 0x1A, 0x08, 0x00, 0x00, 0x01, 0x03, 0x7A,
+       0x81, 0x1A, 0x01, 0x08, 0x0B, 0x81, 0x1A, 0x08, 0x01, 0x08, 0x0B, 0x81,
+       0x1A, 0x08, 0x00, 0x00, 0x01, 0x01, 0x7A, 0x81, 0x1A, 0x00, 0x00, 0x33,
+       0x22, 0x44, 0x05, 0x01, 0x00, 0x21, 0x81, 0x27, 0x04, 0x75, 0x02, 0x03,
+       0x00, 0x74, 0x27, 0x03, 0x01, 0x01, 0x00, 0x22, 0x02, 0x01, 0x0A, 0x06,
+       0x10, 0x22, 0x01, 0x01, 0x0B, 0x73, 0x08, 0x25, 0x02, 0x00, 0x0E, 0x06,
+       0x01, 0x00, 0x48, 0x04, 0x6A, 0x21, 0x01, 0x7F, 0x00, 0x00, 0x24, 0x16,
+       0x2F, 0x06, 0x05, 0x81, 0x25, 0x21, 0x04, 0x77, 0x01, 0x16, 0x6A, 0x38,
+       0x01, 0x00, 0x81, 0x33, 0x01, 0x00, 0x81, 0x32, 0x24, 0x01, 0x17, 0x6A,
+       0x38, 0x00, 0x00, 0x01, 0x15, 0x6A, 0x38, 0x3B, 0x43, 0x21, 0x43, 0x21,
+       0x24, 0x00, 0x00, 0x01, 0x01, 0x3B, 0x81, 0x1D, 0x00, 0x00, 0x3B, 0x31,
+       0x7A, 0x3B, 0x22, 0x06, 0x06, 0x81, 0x1A, 0x21, 0x49, 0x04, 0x77, 0x21,
+       0x00, 0x02, 0x03, 0x00, 0x5A, 0x25, 0x7C, 0x03, 0x01, 0x02, 0x01, 0x01,
+       0x0F, 0x12, 0x02, 0x01, 0x01, 0x04, 0x11, 0x01, 0x0F, 0x12, 0x02, 0x01,
+       0x01, 0x08, 0x11, 0x01, 0x0F, 0x12, 0x01, 0x00, 0x31, 0x0E, 0x06, 0x10,
+       0x21, 0x01, 0x00, 0x01, 0x18, 0x02, 0x00, 0x06, 0x03, 0x3E, 0x04, 0x01,
+       0x3F, 0x04, 0x80, 0x56, 0x01, 0x01, 0x31, 0x0E, 0x06, 0x10, 0x21, 0x01,
+       0x01, 0x01, 0x10, 0x02, 0x00, 0x06, 0x03, 0x3E, 0x04, 0x01, 0x3F, 0x04,
+       0x80, 0x40, 0x01, 0x02, 0x31, 0x0E, 0x06, 0x0F, 0x21, 0x01, 0x01, 0x01,
+       0x20, 0x02, 0x00, 0x06, 0x03, 0x3E, 0x04, 0x01, 0x3F, 0x04, 0x2B, 0x01,
+       0x03, 0x31, 0x0E, 0x06, 0x0E, 0x21, 0x21, 0x01, 0x10, 0x02, 0x00, 0x06,
+       0x03, 0x3C, 0x04, 0x01, 0x3D, 0x04, 0x17, 0x01, 0x04, 0x31, 0x0E, 0x06,
+       0x0E, 0x21, 0x21, 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, 0x3C, 0x04, 0x01,
+       0x3D, 0x04, 0x03, 0x50, 0x23, 0x21, 0x00, 0x00, 0x7C, 0x01, 0x0C, 0x11,
+       0x01, 0x02, 0x0F, 0x00, 0x00, 0x7C, 0x01, 0x0C, 0x11, 0x22, 0x47, 0x3B,
+       0x01, 0x03, 0x0A, 0x12, 0x00, 0x00, 0x7C, 0x01, 0x0C, 0x11, 0x01, 0x01,
+       0x0E, 0x00, 0x00, 0x7C, 0x01, 0x0C, 0x11, 0x46, 0x00, 0x00, 0x18, 0x01,
+       0x00, 0x57, 0x27, 0x22, 0x06, 0x20, 0x01, 0x01, 0x31, 0x0E, 0x06, 0x07,
+       0x21, 0x01, 0x00, 0x81, 0x00, 0x04, 0x11, 0x01, 0x02, 0x31, 0x0E, 0x06,
+       0x0A, 0x21, 0x59, 0x27, 0x06, 0x03, 0x01, 0x10, 0x30, 0x04, 0x01, 0x21,
+       0x04, 0x01, 0x21, 0x5F, 0x27, 0x05, 0x35, 0x28, 0x06, 0x32, 0x69, 0x27,
+       0x01, 0x14, 0x31, 0x0E, 0x06, 0x06, 0x21, 0x01, 0x02, 0x30, 0x04, 0x24,
+       0x01, 0x15, 0x31, 0x0E, 0x06, 0x0B, 0x21, 0x81, 0x09, 0x06, 0x04, 0x01,
+       0x7F, 0x81, 0x00, 0x04, 0x13, 0x01, 0x16, 0x31, 0x0E, 0x06, 0x06, 0x21,
+       0x01, 0x01, 0x30, 0x04, 0x07, 0x21, 0x01, 0x04, 0x30, 0x01, 0x00, 0x21,
+       0x16, 0x06, 0x03, 0x01, 0x08, 0x30, 0x00, 0x00, 0x18, 0x22, 0x05, 0x10,
+       0x28, 0x06, 0x0D, 0x69, 0x27, 0x01, 0x15, 0x0E, 0x06, 0x05, 0x21, 0x81,
+       0x09, 0x04, 0x01, 0x1C, 0x00, 0x00, 0x81, 0x25, 0x01, 0x07, 0x12, 0x01,
+       0x01, 0x0F, 0x06, 0x02, 0x56, 0x23, 0x00, 0x01, 0x03, 0x00, 0x24, 0x16,
+       0x06, 0x05, 0x02, 0x00, 0x6A, 0x38, 0x00, 0x81, 0x25, 0x21, 0x04, 0x73,
+       0x00, 0x01, 0x14, 0x81, 0x28, 0x01, 0x01, 0x81, 0x33, 0x24, 0x22, 0x01,
+       0x00, 0x81, 0x20, 0x01, 0x16, 0x81, 0x28, 0x81, 0x2B, 0x24, 0x00, 0x00,
+       0x01, 0x0B, 0x81, 0x33, 0x40, 0x22, 0x01, 0x03, 0x08, 0x81, 0x32, 0x81,
+       0x32, 0x13, 0x22, 0x44, 0x06, 0x02, 0x21, 0x00, 0x81, 0x32, 0x1A, 0x22,
+       0x06, 0x06, 0x67, 0x3B, 0x81, 0x2F, 0x04, 0x76, 0x21, 0x04, 0x6A, 0x00,
+       0x7E, 0x01, 0x14, 0x81, 0x33, 0x01, 0x0C, 0x81, 0x32, 0x67, 0x01, 0x0C,
+       0x81, 0x2F, 0x00, 0x03, 0x03, 0x00, 0x01, 0x02, 0x81, 0x33, 0x01, 0x80,
+       0x46, 0x6B, 0x27, 0x01, 0x02, 0x0E, 0x06, 0x0C, 0x02, 0x00, 0x06, 0x04,
+       0x01, 0x05, 0x04, 0x02, 0x01, 0x1D, 0x04, 0x02, 0x01, 0x00, 0x03, 0x01,
+       0x68, 0x27, 0x06, 0x04, 0x01, 0x05, 0x04, 0x02, 0x01, 0x00, 0x03, 0x02,
+       0x02, 0x01, 0x02, 0x02, 0x08, 0x22, 0x06, 0x03, 0x01, 0x02, 0x08, 0x08,
+       0x81, 0x32, 0x75, 0x25, 0x81, 0x31, 0x6E, 0x01, 0x04, 0x14, 0x6E, 0x01,
+       0x04, 0x08, 0x01, 0x1C, 0x2B, 0x6E, 0x01, 0x20, 0x81, 0x2F, 0x01, 0x20,
+       0x81, 0x33, 0x6F, 0x01, 0x20, 0x81, 0x2F, 0x5A, 0x25, 0x81, 0x31, 0x01,
+       0x00, 0x81, 0x33, 0x02, 0x01, 0x02, 0x02, 0x08, 0x22, 0x06, 0x30, 0x81,
+       0x31, 0x02, 0x01, 0x22, 0x06, 0x13, 0x01, 0x83, 0xFE, 0x01, 0x81, 0x31,
+       0x01, 0x04, 0x09, 0x22, 0x81, 0x31, 0x49, 0x6C, 0x3B, 0x81, 0x30, 0x04,
+       0x01, 0x21, 0x02, 0x02, 0x06, 0x0F, 0x01, 0x01, 0x81, 0x31, 0x01, 0x01,
+       0x81, 0x31, 0x68, 0x27, 0x01, 0x08, 0x09, 0x81, 0x33, 0x04, 0x01, 0x21,
+       0x00, 0x00, 0x01, 0x0E, 0x81, 0x33, 0x01, 0x00, 0x81, 0x32, 0x00, 0x03,
+       0x5A, 0x25, 0x81, 0x22, 0x05, 0x01, 0x00, 0x60, 0x26, 0x01, 0x00, 0x81,
+       0x02, 0x11, 0x01, 0x01, 0x12, 0x46, 0x06, 0x03, 0x48, 0x04, 0x74, 0x03,
+       0x00, 0x21, 0x02, 0x00, 0x1E, 0x22, 0x44, 0x06, 0x02, 0x2E, 0x23, 0x03,
+       0x01, 0x75, 0x25, 0x01, 0x86, 0x03, 0x10, 0x03, 0x02, 0x01, 0x0C, 0x81,
+       0x33, 0x02, 0x01, 0x62, 0x27, 0x08, 0x02, 0x02, 0x01, 0x02, 0x12, 0x08,
+       0x01, 0x06, 0x08, 0x81, 0x32, 0x01, 0x03, 0x81, 0x33, 0x02, 0x00, 0x81,
+       0x31, 0x61, 0x62, 0x27, 0x81, 0x30, 0x02, 0x02, 0x06, 0x10, 0x72, 0x27,
+       0x81, 0x33, 0x5A, 0x25, 0x81, 0x23, 0x01, 0x01, 0x0B, 0x01, 0x03, 0x08,
+       0x81, 0x33, 0x02, 0x01, 0x81, 0x31, 0x67, 0x02, 0x01, 0x81, 0x2F, 0x00,
+       0x00, 0x42, 0x22, 0x01, 0x00, 0x0E, 0x06, 0x02, 0x4B, 0x00, 0x81, 0x25,
+       0x21, 0x04, 0x72, 0x00, 0x22, 0x81, 0x33, 0x81, 0x2F, 0x00, 0x00, 0x22,
+       0x01, 0x08, 0x41, 0x81, 0x33, 0x81, 0x33, 0x00, 0x00, 0x22, 0x01, 0x10,
+       0x41, 0x81, 0x33, 0x81, 0x31, 0x00, 0x00, 0x22, 0x43, 0x06, 0x02, 0x21,
+       0x00, 0x81, 0x25, 0x21, 0x04, 0x75
+};
+
+static const uint16_t t0_caddr[] = {
+       0,
+       5,
+       10,
+       15,
+       20,
+       25,
+       30,
+       35,
+       39,
+       43,
+       47,
+       51,
+       55,
+       59,
+       63,
+       67,
+       71,
+       75,
+       79,
+       83,
+       88,
+       93,
+       98,
+       103,
+       108,
+       113,
+       118,
+       123,
+       128,
+       133,
+       138,
+       143,
+       148,
+       153,
+       159,
+       164,
+       169,
+       174,
+       179,
+       184,
+       189,
+       194,
+       199,
+       204,
+       209,
+       214,
+       219,
+       224,
+       229,
+       234,
+       239,
+       244,
+       249,
+       254,
+       259,
+       268,
+       272,
+       297,
+       303,
+       323,
+       334,
+       371,
+       431,
+       435,
+       471,
+       481,
+       546,
+       560,
+       566,
+       613,
+       633,
+       686,
+       1211,
+       1296,
+       1329,
+       1354,
+       1402,
+       1476,
+       1525,
+       1540,
+       1551,
+       1557,
+       1628,
+       1669,
+       1682,
+       1701,
+       1708,
+       1720,
+       1755,
+       1784,
+       1796,
+       1803,
+       1819,
+       1957,
+       1966,
+       1979,
+       1988,
+       1995,
+       2101,
+       2123,
+       2137,
+       2154,
+       2177,
+       2213,
+       2229,
+       2383,
+       2393,
+       2502,
+       2517,
+       2524,
+       2534,
+       2544
+};
+
+#define T0_INTERPRETED   68
+
+#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)
+
+#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}
+
+T0_DEFENTRY(br_ssl_hs_server_init_main, 133)
+
+void
+br_ssl_hs_server_run(void *t0ctx)
+{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 7: {
+                               /* * */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a * b);
+
+                               }
+                               break;
+                       case 8: {
+                               /* + */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+
+                               }
+                               break;
+                       case 9: {
+                               /* - */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+
+                               }
+                               break;
+                       case 10: {
+                               /* < */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+
+                               }
+                               break;
+                       case 11: {
+                               /* << */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+
+                               }
+                               break;
+                       case 12: {
+                               /* <= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a <= b));
+
+                               }
+                               break;
+                       case 13: {
+                               /* <> */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+
+                               }
+                               break;
+                       case 14: {
+                               /* = */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+
+                               }
+                               break;
+                       case 15: {
+                               /* > */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a > b));
+
+                               }
+                               break;
+                       case 16: {
+                               /* >= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+
+                               }
+                               break;
+                       case 17: {
+                               /* >> */
+
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+
+                               }
+                               break;
+                       case 18: {
+                               /* and */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+
+                               }
+                               break;
+                       case 19: {
+                               /* begin-cert */
+
+       if (CTX->chain_len == 0) {
+               T0_PUSHi(-1);
+       } else {
+               CTX->cert_cur = CTX->chain->data;
+               CTX->cert_len = CTX->chain->data_len;
+               CTX->chain ++;
+               CTX->chain_len --;
+               T0_PUSH(CTX->cert_len);
+       }
+
+                               }
+                               break;
+                       case 20: {
+                               /* bzero */
+
+       size_t len = (size_t)T0_POP();
+       void *addr = (unsigned char *)ENG + (size_t)T0_POP();
+       memset(addr, 0, len);
+
+                               }
+                               break;
+                       case 21: {
+                               /* call-policy-handler */
+
+       int x;
+       br_ssl_server_choices choices;
+
+       x = (*CTX->policy_vtable)->choose(
+               CTX->policy_vtable, CTX, &choices);
+       ENG->session.cipher_suite = choices.cipher_suite;
+       CTX->sign_hash_id = choices.hash_id;
+       CTX->chain = choices.chain;
+       CTX->chain_len = choices.chain_len;
+       T0_PUSHi(-(x != 0));
+
+                               }
+                               break;
+                       case 22: {
+                               /* can-output? */
+
+       T0_PUSHi(-(ENG->hlen_out > 0));
+
+                               }
+                               break;
+                       case 23: {
+                               /* check-resume */
+
+       if (ENG->session.session_id_len == 32
+               && CTX->cache_vtable != NULL && (*CTX->cache_vtable)->load(
+                       CTX->cache_vtable, CTX, &ENG->session))
+       {
+               T0_PUSHi(-1);
+       } else {
+               T0_PUSH(0);
+       }
+
+                               }
+                               break;
+                       case 24: {
+                               /* co */
+ T0_CO(); 
+                               }
+                               break;
+                       case 25: {
+                               /* compute-Finished-inner */
+
+       int prf_id = T0_POP();
+       int from_client = T0_POPi();
+       unsigned char seed[48];
+       size_t seed_len;
+
+       br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+       if (ENG->session.version >= BR_TLS12) {
+               seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+       } else {
+               br_multihash_out(&ENG->mhash, br_md5_ID, seed);
+               br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
+               seed_len = 36;
+       }
+       prf(ENG->pad, 12, ENG->session.master_secret,
+               sizeof ENG->session.master_secret,
+               from_client ? "client finished" : "server finished",
+               seed, seed_len);
+
+                               }
+                               break;
+                       case 26: {
+                               /* copy-cert-chunk */
+
+       size_t clen;
+
+       clen = CTX->cert_len;
+       if (clen > sizeof ENG->pad) {
+               clen = sizeof ENG->pad;
+       }
+       memcpy(ENG->pad, CTX->cert_cur, clen);
+       CTX->cert_cur += clen;
+       CTX->cert_len -= clen;
+       T0_PUSH(clen);
+
+                               }
+                               break;
+                       case 27: {
+                               /* data-get8 */
+
+       size_t addr = T0_POP();
+       T0_PUSH(t0_datablock[addr]);
+
+                               }
+                               break;
+                       case 28: {
+                               /* discard-input */
+
+       ENG->hlen_in = 0;
+
+                               }
+                               break;
+                       case 29: {
+                               /* do-ecdh */
+
+       int prf_id = T0_POPi();
+       size_t len = T0_POP();
+       do_ecdh(CTX, prf_id, ENG->pad, len);
+
+                               }
+                               break;
+                       case 30: {
+                               /* do-ecdhe-part1 */
+
+       int curve = T0_POPi();
+       T0_PUSHi(do_ecdhe_part1(CTX, curve));
+
+                               }
+                               break;
+                       case 31: {
+                               /* do-ecdhe-part2 */
+
+       int prf_id = T0_POPi();
+       size_t len = T0_POP();
+       do_ecdhe_part2(CTX, prf_id, ENG->pad, len);
+
+                               }
+                               break;
+                       case 32: {
+                               /* do-rsa-decrypt */
+
+       int prf_id = T0_POPi();
+       size_t len = T0_POP();
+       do_rsa_decrypt(CTX, prf_id, ENG->pad, len);
+
+                               }
+                               break;
+                       case 33: {
+                               /* drop */
+ (void)T0_POP(); 
+                               }
+                               break;
+                       case 34: {
+                               /* dup */
+ T0_PUSH(T0_PEEK(0)); 
+                               }
+                               break;
+                       case 35: {
+                               /* fail */
+
+       br_ssl_engine_fail(ENG, (int)T0_POPi());
+       T0_CO();
+
+                               }
+                               break;
+                       case 36: {
+                               /* flush-record */
+
+       br_ssl_engine_flush_record(ENG);
+
+                               }
+                               break;
+                       case 37: {
+                               /* get16 */
+
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*(uint16_t *)((unsigned char *)ENG + addr));
+
+                               }
+                               break;
+                       case 38: {
+                               /* get32 */
+
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*(uint32_t *)((unsigned char *)ENG + addr));
+
+                               }
+                               break;
+                       case 39: {
+                               /* get8 */
+
+       size_t addr = (size_t)T0_POP();
+       T0_PUSH(*((unsigned char *)ENG + addr));
+
+                               }
+                               break;
+                       case 40: {
+                               /* has-input? */
+
+       T0_PUSHi(-(ENG->hlen_in != 0));
+
+                               }
+                               break;
+                       case 41: {
+                               /* memcmp */
+
+       size_t len = (size_t)T0_POP();
+       void *addr2 = (unsigned char *)ENG + (size_t)T0_POP();
+       void *addr1 = (unsigned char *)ENG + (size_t)T0_POP();
+       int x = memcmp(addr1, addr2, len);
+       T0_PUSH((uint32_t)-(x == 0));
+
+                               }
+                               break;
+                       case 42: {
+                               /* memcpy */
+
+       size_t len = (size_t)T0_POP();
+       void *src = (unsigned char *)ENG + (size_t)T0_POP();
+       void *dst = (unsigned char *)ENG + (size_t)T0_POP();
+       memcpy(dst, src, len);
+
+                               }
+                               break;
+                       case 43: {
+                               /* mkrand */
+
+       size_t len = (size_t)T0_POP();
+       void *addr = (unsigned char *)ENG + (size_t)T0_POP();
+       br_hmac_drbg_generate(&ENG->rng, addr, len);
+
+                               }
+                               break;
+                       case 44: {
+                               /* more-incoming-bytes? */
+
+       T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG));
+
+                               }
+                               break;
+                       case 45: {
+                               /* multihash-init */
+
+       br_multihash_init(&ENG->mhash);
+
+                               }
+                               break;
+                       case 46: {
+                               /* neg */
+
+       uint32_t a = T0_POP();
+       T0_PUSH(-a);
+
+                               }
+                               break;
+                       case 47: {
+                               /* not */
+
+       uint32_t a = T0_POP();
+       T0_PUSH(~a);
+
+                               }
+                               break;
+                       case 48: {
+                               /* or */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a | b);
+
+                               }
+                               break;
+                       case 49: {
+                               /* over */
+ T0_PUSH(T0_PEEK(1)); 
+                               }
+                               break;
+                       case 50: {
+                               /* read-chunk-native */
+
+       size_t clen = ENG->hlen_in;
+       if (clen > 0) {
+               uint32_t addr, len;
+
+               len = T0_POP();
+               addr = T0_POP();
+               if ((size_t)len < clen) {
+                       clen = (size_t)len;
+               }
+               memcpy((unsigned char *)ENG + addr, ENG->hbuf_in, clen);
+               if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen);
+               }
+               T0_PUSH(addr + (uint32_t)clen);
+               T0_PUSH(len - (uint32_t)clen);
+               ENG->hbuf_in += clen;
+               ENG->hlen_in -= clen;
+       }
+
+                               }
+                               break;
+                       case 51: {
+                               /* read8-native */
+
+       if (ENG->hlen_in > 0) {
+               unsigned char x;
+
+               x = *ENG->hbuf_in ++;
+               if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, &x, 1);
+               }
+               T0_PUSH(x);
+               ENG->hlen_in --;
+       } else {
+               T0_PUSHi(-1);
+       }
+
+                               }
+                               break;
+                       case 52: {
+                               /* save-session */
+
+       if (CTX->cache_vtable != NULL) {
+               (*CTX->cache_vtable)->save(
+                       CTX->cache_vtable, CTX, &ENG->session);
+       }
+
+                               }
+                               break;
+                       case 53: {
+                               /* set-max-frag-len */
+
+       size_t max_frag_len = T0_POP();
+
+       br_ssl_engine_new_max_frag_len(ENG, max_frag_len);
+
+       /*
+        * We must adjust our own output limit. Since we call this only
+        * after receiving a ClientHello and before beginning to send
+        * the ServerHello, the next output record should be empty at
+        * that point, so we can use max_frag_len as a limit.
+        */
+       if (ENG->hlen_out > max_frag_len) {
+               ENG->hlen_out = max_frag_len;
+       }
+
+                               }
+                               break;
+                       case 54: {
+                               /* set16 */
+
+       size_t addr = (size_t)T0_POP();
+       *(uint16_t *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
+
+                               }
+                               break;
+                       case 55: {
+                               /* set32 */
+
+       size_t addr = (size_t)T0_POP();
+       *(uint32_t *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP();
+
+                               }
+                               break;
+                       case 56: {
+                               /* set8 */
+
+       size_t addr = (size_t)T0_POP();
+       *((unsigned char *)ENG + addr) = (unsigned char)T0_POP();
+
+                               }
+                               break;
+                       case 57: {
+                               /* supported-curves */
+
+       uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves;
+       T0_PUSH(x);
+
+                               }
+                               break;
+                       case 58: {
+                               /* supported-hash-functions */
+
+       int i;
+       unsigned x, num;
+
+       x = 0;
+       num = 0;
+       for (i = br_sha1_ID; i <= br_sha512_ID; i ++) {
+               if (br_multihash_getimpl(&ENG->mhash, i)) {
+                       x |= 1U << i;
+                       num ++;
+               }
+       }
+       T0_PUSH(x);
+       T0_PUSH(num);
+
+                               }
+                               break;
+                       case 59: {
+                               /* swap */
+ T0_SWAP(); 
+                               }
+                               break;
+                       case 60: {
+                               /* switch-aesgcm-in */
+
+       int is_client, prf_id;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id,
+               ENG->iaes_ctr, cipher_key_len);
+
+                               }
+                               break;
+                       case 61: {
+                               /* switch-aesgcm-out */
+
+       int is_client, prf_id;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id,
+               ENG->iaes_ctr, cipher_key_len);
+
+                               }
+                               break;
+                       case 62: {
+                               /* switch-cbc-in */
+
+       int is_client, prf_id, mac_id, aes;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       aes = T0_POP();
+       mac_id = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id,
+               aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len);
+
+                               }
+                               break;
+                       case 63: {
+                               /* switch-cbc-out */
+
+       int is_client, prf_id, mac_id, aes;
+       unsigned cipher_key_len;
+
+       cipher_key_len = T0_POP();
+       aes = T0_POP();
+       mac_id = T0_POP();
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id,
+               aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len);
+
+                               }
+                               break;
+                       case 64: {
+                               /* total-chain-length */
+
+       size_t u;
+       uint32_t total;
+
+       total = 0;
+       for (u = 0; u < CTX->chain_len; u ++) {
+               total += 3 + (uint32_t)CTX->chain[u].data_len;
+       }
+       T0_PUSH(total);
+
+                               }
+                               break;
+                       case 65: {
+                               /* u>> */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x >> c);
+
+                               }
+                               break;
+                       case 66: {
+                               /* write-blob-chunk */
+
+       size_t clen = ENG->hlen_out;
+       if (clen > 0) {
+               uint32_t addr, len;
+
+               len = T0_POP();
+               addr = T0_POP();
+               if ((size_t)len < clen) {
+                       clen = (size_t)len;
+               }
+               memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen);
+               if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen);
+               }
+               T0_PUSH(addr + (uint32_t)clen);
+               T0_PUSH(len - (uint32_t)clen);
+               ENG->hbuf_out += clen;
+               ENG->hlen_out -= clen;
+       }
+
+                               }
+                               break;
+                       case 67: {
+                               /* write8-native */
+
+       unsigned char x;
+
+       x = (unsigned char)T0_POP();
+       if (ENG->hlen_out > 0) {
+               if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
+                       br_multihash_update(&ENG->mhash, &x, 1);
+               }
+               *ENG->hbuf_out ++ = x;
+               ENG->hlen_out --;
+               T0_PUSHi(-1);
+       } else {
+               T0_PUSHi(0);
+       }
+
+                               }
+                               break;
+                       }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}
diff --git a/src/ssl/ssl_hs_server.t0 b/src/ssl/ssl_hs_server.t0
new file mode 100644 (file)
index 0000000..5069a63
--- /dev/null
@@ -0,0 +1,1013 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+\ ----------------------------------------------------------------------
+\ Handshake processing code, for the server.
+\ The common T0 code (ssl_hs_common.t0) shall be read first.
+
+preamble {
+
+/*
+ * This macro evaluates to a pointer to the server context, under that
+ * specific name. It must be noted that since the engine context is the
+ * first field of the br_ssl_server_context structure ('eng'), then
+ * pointers values of both types are interchangeable, modulo an
+ * appropriate cast. This also means that "adresses" computed as offsets
+ * within the structure work for both kinds of context.
+ */
+#define CTX  ((br_ssl_server_context *)ENG)
+
+/*
+ * Decrypt the pre-master secret (RSA key exchange).
+ */
+static void
+do_rsa_decrypt(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *epms, size_t len)
+{
+       uint32_t x;
+       unsigned char rpms[48];
+
+       /*
+        * Decrypt the PMS.
+        */
+       x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, len);
+
+       /*
+        * Set the first two bytes to the maximum supported client
+        * protocol version. These bytes are used for version rollback
+        * detection; forceing the two bytes will make the master secret
+        * wrong if the bytes are not correct. This process is
+        * recommended by RFC 5246 (section 7.4.7.1).
+        */
+       br_enc16be(epms, ctx->client_max_version);
+
+       /*
+        * Make a random PMS and copy it above the decrypted value if the
+        * decryption failed. Note that we use a constant-time conditional
+        * copy.
+        */
+       br_hmac_drbg_generate(&ctx->eng.rng, rpms, sizeof rpms);
+       br_ccopy(x ^ 1, epms, rpms, sizeof rpms);
+
+       /*
+        * Compute master secret.
+        */
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, epms, 48);
+
+       /*
+        * Clear the pre-master secret from RAM: it is normally a buffer
+        * in the context, hence potentially long-lived.
+        */
+       memset(epms, 0, len);
+}
+
+/*
+ * Common part for ECDH and ECDHE.
+ */
+static void
+ecdh_common(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *cpoint, size_t cpoint_len, uint32_t ctl)
+{
+       unsigned char rpms[80];
+       size_t pms_len;
+
+       /*
+        * The point length is supposed to be 1+2*Xlen, where Xlen is
+        * the length (in bytes) of the X coordinate, i.e. the pre-master
+        * secret. If the provided point is too large, then it is
+        * obviously incorrect (i.e. everybody can see that it is
+        * incorrect), so leaking that fact is not a problem.
+        */
+       pms_len = cpoint_len >> 1;
+       if (pms_len > sizeof rpms) {
+               pms_len = sizeof rpms;
+               ctl = 0;
+       }
+
+       /*
+        * Make a random PMS and copy it above the decrypted value if the
+        * decryption failed. Note that we use a constant-time conditional
+        * copy.
+        */
+       br_hmac_drbg_generate(&ctx->eng.rng, rpms, pms_len);
+       br_ccopy(ctl ^ 1, cpoint + 1, rpms, pms_len);
+
+       /*
+        * Compute master secret.
+        */
+       br_ssl_engine_compute_master(&ctx->eng, prf_id, cpoint + 1, pms_len);
+
+       /*
+        * Clear the pre-master secret from RAM: it is normally a buffer
+        * in the context, hence potentially long-lived.
+        */
+       memset(cpoint, 0, cpoint_len);
+}
+
+/*
+ * Do the ECDH key exchange (not ECDHE).
+ */
+static void
+do_ecdh(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *cpoint, size_t cpoint_len)
+{
+       uint32_t x;
+
+       /*
+        * Finalise the key exchange.
+        */
+       x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable,
+               cpoint, cpoint_len);
+       ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
+}
+
+/*
+ * Do the ECDHE key exchange (part 1: generation of transient key, and
+ * computing of the point to send to the client). Returned value is the
+ * signature length (in bytes), or -x on error (with x being an error
+ * code). The encoded point is written in the ecdhe_point[] context buffer
+ * (length in ecdhe_point_len).
+ */
+static int
+do_ecdhe_part1(br_ssl_server_context *ctx, int curve)
+{
+       int hash;
+       unsigned mask;
+       const unsigned char *order, *generator;
+       size_t olen, glen;
+       br_multihash_context mhc;
+       unsigned char head[4];
+       size_t hv_len, sig_len;
+
+       if (!((ctx->eng.iec->supported_curves >> curve) & 1)) {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+       ctx->eng.ecdhe_curve = curve;
+
+       /*
+        * Generate our private key. We need a non-zero random value
+        * which is lower than the curve order, in a "large enough"
+        * range. We force the top bit to 0 and bottom bit to 1, which
+        * does the trick. Note that contrary to what happens in ECDSA,
+        * this is not a problem if we do not cover the full range of
+        * possible values.
+        */
+       order = ctx->eng.iec->order(curve, &olen);
+       mask = 0xFF;
+       while (mask >= order[0]) {
+               mask >>= 1;
+       }
+       br_hmac_drbg_generate(&ctx->eng.rng, ctx->ecdhe_key, olen);
+       ctx->ecdhe_key[0] &= mask;
+       ctx->ecdhe_key[olen - 1] |= 0x01;
+       ctx->ecdhe_key_len = olen;
+
+       /*
+        * Compute our ECDH point.
+        */
+       generator = ctx->eng.iec->generator(curve, &glen);
+       memcpy(ctx->eng.ecdhe_point, generator, glen);
+       ctx->eng.ecdhe_point_len = glen;
+       if (!ctx->eng.iec->mul(ctx->eng.ecdhe_point, glen,
+               ctx->ecdhe_key, olen, curve))
+       {
+               return -BR_ERR_INVALID_ALGORITHM;
+       }
+
+       /*
+        * Compute the signature.
+        */
+       br_multihash_zero(&mhc);
+       br_multihash_copyimpl(&mhc, &ctx->eng.mhash);
+       br_multihash_init(&mhc);
+       br_multihash_update(&mhc,
+               ctx->eng.client_random, sizeof ctx->eng.client_random);
+       br_multihash_update(&mhc,
+               ctx->eng.server_random, sizeof ctx->eng.server_random);
+       head[0] = 3;
+       head[1] = 0;
+       head[2] = curve;
+       head[3] = ctx->eng.ecdhe_point_len;
+       br_multihash_update(&mhc, head, sizeof head);
+       br_multihash_update(&mhc,
+               ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len);
+       hash = ctx->sign_hash_id;
+       if (hash) {
+               hv_len = br_multihash_out(&mhc, hash, ctx->eng.pad);
+               if (hv_len == 0) {
+                       return -BR_ERR_INVALID_ALGORITHM;
+               }
+       } else {
+               if (!br_multihash_out(&mhc, br_md5_ID, ctx->eng.pad)
+                       || !br_multihash_out(&mhc,
+                       br_sha1_ID, ctx->eng.pad + 16))
+               {
+                       return -BR_ERR_INVALID_ALGORITHM;
+               }
+               hv_len = 36;
+       }
+       sig_len = (*ctx->policy_vtable)->do_sign(ctx->policy_vtable,
+               hash, hv_len, ctx->eng.pad, sizeof ctx->eng.pad);
+       return sig_len ? (int)sig_len : -BR_ERR_INVALID_ALGORITHM;
+}
+
+/*
+ * Do the ECDHE key exchange (part 2: computation of the shared secret
+ * from the point sent by the client).
+ */
+static void
+do_ecdhe_part2(br_ssl_server_context *ctx, int prf_id,
+       unsigned char *cpoint, size_t cpoint_len)
+{
+       int curve;
+       uint32_t x;
+
+       curve = ctx->eng.ecdhe_curve;
+
+       /*
+        * Finalise the key exchange.
+        */
+       x = ctx->eng.iec->mul(cpoint, cpoint_len,
+               ctx->ecdhe_key, ctx->ecdhe_key_len, curve);
+       ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
+
+       /*
+        * Clear the ECDHE private key. Forward Secrecy is achieved insofar
+        * as that key does not get stolen, so we'd better destroy it
+        * as soon as it ceases to be useful.
+        */
+       memset(ctx->ecdhe_key, 0, ctx->ecdhe_key_len);
+}
+
+}
+
+\ =======================================================================
+
+: addr-ctx:
+       next-word { field }
+       "addr-" field + 0 1 define-word
+       0 8191 "offsetof(br_ssl_server_context, " field + ")" + make-CX
+       postpone literal postpone ; ;
+
+addr-ctx: flags
+addr-ctx: client_max_version
+addr-ctx: client_suites
+addr-ctx: client_suites_num
+addr-ctx: hashes
+addr-ctx: curves
+addr-ctx: sign_hash_id
+
+\ Get address and length of the client_suites[] buffer. Length is expressed
+\ in bytes.
+: addr-len-client_suites ( -- addr len )
+       addr-client_suites
+       CX 0 1023 { BR_MAX_CIPHER_SUITES * sizeof(br_suite_translated) } ;
+
+\ Check a server flag by index.
+: flag? ( index -- bool )
+       addr-flags get32 swap >> 1 and neg ;
+
+\ Read the client SNI extension.
+: read-client-sni ( lim -- lim )
+       \ Open extension value.
+       read16 open-elt
+
+       \ Open ServerNameList.
+       read16 open-elt
+
+       \ Find if there is a name of type 0 (host_name) with a length
+       \ that fits in our dedicated buffer.
+       begin dup while
+               read8 if
+                       read-ignore-16
+               else
+                       read16
+                       dup 255 <= if
+                               dup addr-server_name + 0 swap set8
+                               addr-server_name swap read-blob
+                       else
+                               skip-blob
+                       then
+               then
+       repeat
+
+       \ Close ServerNameList.
+       close-elt
+
+       \ Close extension value.
+       close-elt ;
+
+\ Set the new maximum fragment length. BEWARE: this shall be called only
+\ after reading the ClientHello and before writing the ServerHello.
+cc: set-max-frag-len ( len -- ) {
+       size_t max_frag_len = T0_POP();
+
+       br_ssl_engine_new_max_frag_len(ENG, max_frag_len);
+
+       /*
+        * We must adjust our own output limit. Since we call this only
+        * after receiving a ClientHello and before beginning to send
+        * the ServerHello, the next output record should be empty at
+        * that point, so we can use max_frag_len as a limit.
+        */
+       if (ENG->hlen_out > max_frag_len) {
+               ENG->hlen_out = max_frag_len;
+       }
+}
+
+\ Read the client Max Frag Length extension.
+: read-client-frag ( lim -- lim )
+       \ Extension value must have length exactly 1 byte.
+       read16 1 <> if ERR_BAD_FRAGLEN fail then
+       read8
+
+       \ The byte value must be 1, 2, 3 or 4.
+       dup dup 0= swap 5 >= or if ERR_BAD_FRAGLEN fail then
+
+       \ If our own maximum fragment length is greater, then we reduce
+       \ our length.
+       8 + dup addr-log_max_frag_len get8 < if
+               dup 1 swap << set-max-frag-len
+               dup addr-log_max_frag_len set8
+               addr-peer_log_max_frag_len set8
+       else
+               drop
+       then ;
+
+\ Read the Secure Renegotiation extension from the client.
+: read-client-reneg ( lim -- lim )
+       \ Get value length.
+       read16
+
+       \ The "reneg" value is one of:
+       \   0   on first handshake, client support is unknown
+       \   1   client does not support secure renegotiation
+       \   2   client supports secure renegotiation
+       addr-reneg get8 case
+               0 of
+                       \ First handshake, value length shall be 1.
+                       1 = ifnot ERR_BAD_SECRENEG fail then
+                       read8 if ERR_BAD_SECRENEG fail then
+                       2 addr-reneg set8
+               endof
+               2 of
+                       \ Renegotiation, value shall consist of 13 bytes
+                       \ (header + copy of the saved client "Finished").
+                       13 = ifnot ERR_BAD_SECRENEG fail then
+                       read8 12 = ifnot ERR_BAD_SECRENEG fail then
+                       addr-pad 12 read-blob
+                       addr-saved_finished addr-pad 12 memcmp ifnot
+                               ERR_BAD_SECRENEG fail
+                       then
+               endof
+
+               \ If "reneg" is 1 then the client is not supposed to support
+               \ the extension, and it sends it nonetheless, which means
+               \ foul play.
+               ERR_BAD_SECRENEG fail
+       endcase ;
+
+\ Read the Signature Algorithms extension.
+: read-signatures ( lim -- lim )
+       \ Open extension value.
+       read16 open-elt
+
+       \ Clear list of supported signature algorithms.
+       0 addr-hashes set16
+
+       \ Get list of algorithms length.
+       read16 open-elt
+       begin dup while
+               read8 { hash } read8 { sign }
+               \ We keep the value if the signature is either 1 (RSA) or
+               \ 3 (ECDSA), and the hash is one of the SHA-* functions
+               \ (2 to 6, from SHA-1 to SHA-512). Note that we reject
+               \ any use of MD5. Also, we do not keep track of the client
+               \ preferences.
+               hash 2 >= hash 6 <= and
+               sign 1 = sign 3 = or
+               and if
+                       addr-hashes get16
+                       1 sign 1- 2 << hash + << or addr-hashes set16
+               then
+       repeat
+       close-elt
+
+       \ Close extension value.
+       close-elt ;
+
+\ Read the Supported Curves extension.
+: read-supported-curves ( lim -- lim )
+       \ Open extension value.
+       read16 open-elt
+
+       \ Open list of curve identifiers.
+       read16 open-elt
+
+       \ Get all supported curves.
+       0 addr-curves set32
+       begin dup while
+               read16 dup 32 < if
+                       1 swap << addr-curves get32 or addr-curves set32
+               else
+                       drop
+               then
+       repeat
+       close-elt
+       close-elt ;
+
+\ Call policy handler to get cipher suite, hash function identifier and
+\ certificate chain. Returned value is 0 (false) on failure.
+cc: call-policy-handler ( -- bool ) {
+       int x;
+       br_ssl_server_choices choices;
+
+       x = (*CTX->policy_vtable)->choose(
+               CTX->policy_vtable, CTX, &choices);
+       ENG->session.cipher_suite = choices.cipher_suite;
+       CTX->sign_hash_id = choices.hash_id;
+       CTX->chain = choices.chain;
+       CTX->chain_len = choices.chain_len;
+       T0_PUSHi(-(x != 0));
+}
+
+\ Check for a remembered session.
+cc: check-resume ( -- bool ) {
+       if (ENG->session.session_id_len == 32
+               && CTX->cache_vtable != NULL && (*CTX->cache_vtable)->load(
+                       CTX->cache_vtable, CTX, &ENG->session))
+       {
+               T0_PUSHi(-1);
+       } else {
+               T0_PUSH(0);
+       }
+}
+
+\ Save the current session.
+cc: save-session ( -- ) {
+       if (CTX->cache_vtable != NULL) {
+               (*CTX->cache_vtable)->save(
+                       CTX->cache_vtable, CTX, &ENG->session);
+       }
+}
+
+\ Read ClientHello. If the session is resumed, then -1 is returned.
+: read-ClientHello ( -- resume )
+       \ Get header, and check message type.
+       read-handshake-header 1 = ifnot ERR_UNEXPECTED fail then
+
+       \ Get maximum protocol version from client.
+       read16 dup { client-version-max } addr-client_max_version set16
+
+       \ Client random.
+       addr-client_random 32 read-blob
+
+       \ Client session ID.
+       read8 dup 32 > if ERR_OVERSIZED_ID fail then
+       dup addr-session_id_len set8
+       addr-session_id swap read-blob
+
+       \ Lookup session for resumption. We should do that here because
+       \ we need to verify that the remembered cipher suite is still
+       \ matched by this ClientHello.
+       check-resume { resume }
+
+       \ Cipher suites. We read all cipher suites from client, each time
+       \ matching against our own list. We accumulare suites in the
+       \ client_suites[] context buffer: we keep suites that are
+       \ supported by both the client and the server (so the list size
+       \ cannot exceed that of the server list), and we keep them in
+       \ either client or server preference order (depending on the
+       \ relevant flag).
+       \
+       \ We also need to identify the pseudo cipher suite for secure
+       \ renegotiation here.
+       read16 open-elt
+       0 { reneg-scsv }
+       0 { resume-suite }
+       addr-len-client_suites dup2 bzero
+       over + { css-off css-max }
+       begin
+               dup while
+               read16 dup { suite }
+
+               \ Check that when resuming a session, the requested
+               \ suite is still valid.
+               resume if
+                       dup addr-cipher_suite get16 = if
+                               -1 >resume-suite
+                       then
+               then
+
+               \ Special handling for TLS_EMPTY_RENEGOTIATION_INFO_SCSV.
+               \ This fake cipher suite may occur only in the first
+               \ handshake.
+               dup 0x00FF = if
+                       addr-reneg get8 if ERR_BAD_SECRENEG fail then
+                       -1 >reneg-scsv
+               then
+
+               \ Test whether the suite is supported by the server.
+               scan-suite dup 0< if
+                       \ We do not support this cipher suite. Note
+                       \ that this also covers the case of pseudo
+                       \ cipher suites.
+                       drop
+               else
+                       \ If we use server order, then we place the
+                       \ suite at the computed offset; otherwise, we
+                       \ append it to the list at the current place.
+                       0 flag? if
+                               2 << addr-client_suites + suite swap set16
+                       else
+                               drop
+                               \ We need to test for list length because
+                               \ the client list may have duplicates,
+                               \ that we do not filter. Duplicates are
+                               \ invalid so this is not a problem if we
+                               \ reject such clients.
+                               css-off css-max >= if
+                                       ERR_BAD_HANDSHAKE fail
+                               then
+                               suite css-off set16
+                               css-off 4 + >css-off
+                       then
+               then
+       repeat
+       drop
+
+       \ Compression methods. We need method 0 (no compression).
+       0 { ok-compression }
+       read8 open-elt
+       begin dup while
+               read8 ifnot -1 >ok-compression then
+       repeat
+       close-elt
+
+       \ Set default values for parameters that may be affected by
+       \ extensions:
+       \ -- server name is empty
+       \ -- client is reputed to know RSA and ECDSA, both with SHA-1
+       \ -- the default elliptic curve is P-256 (secp256r1, id = 23)
+       0 addr-server_name set8
+       0x404 addr-hashes set16
+       0x800000 addr-curves set32
+
+       \ Process extensions, if any.
+       dup if
+               read16 open-elt
+               begin dup while
+                       read16 case
+                               \ Server Name Indication.
+                               0x0000 of
+                                       read-client-sni
+                               endof
+                               \ Max Frag Length.
+                               0x0001 of
+                                       read-client-frag
+                               endof
+                               \ Secure Renegotiation.
+                               0xFF01 of
+                                       read-client-reneg
+                               endof
+                               \ Signature Algorithms.
+                               0x000D of
+                                       read-signatures
+                               endof
+                               \ Supported Curves.
+                               0x000A of
+                                       read-supported-curves
+                               endof
+                               \ Supported Point Formats.
+                               0x000B of
+                                       \ We only support "uncompressed", and
+                                       \ all implementations are supposed to
+                                       \ support it anyway.
+                                       read-ignore-16
+                               endof
+
+                               \ Other extensions are ignored.
+                               drop read-ignore-16 0
+                       endcase
+               repeat
+               close-elt
+       then
+
+       \ Close message.
+       close-elt
+
+       \ Cancel session resumption if the cipher suite was not found.
+       resume resume-suite and >resume
+
+       \ Now check the received data. Since the client is expecting an
+       \ answer, we can send an appropriate fatal alert on any error.
+
+       \ Compute protocol version as the minimum of our maximum version,
+       \ and the maximum version sent by the client. If that is less than
+       \ 0x0300 (SSL-3.0), then fail. Otherwise, we may at least send an
+       \ alert with that version. We still reject versions lower than our
+       \ configured minimum.
+       addr-version_max get16
+       dup client-version-max > if drop client-version-max then
+       dup 0x0300 < if ERR_BAD_VERSION fail then
+       client-version-max addr-version_min get16 < if
+               70 fail-alert
+       then
+       \ If resuming the session, then enforce the previously negotiated
+       \ version (if still possible).
+       resume if
+               addr-version get16 client-version-max <= if
+                       drop addr-version get16
+               else
+                       0 >resume
+               then
+       then
+       dup addr-version set16
+       dup addr-version_in set16
+       dup addr-version_out set16
+       0x0303 >= { can-tls12 }
+
+       \ If the client sent TLS_EMPTY_RENEGOTIATION_INFO_SCSV, then
+       \ we should mark the client as "supporting secure renegotiation".
+       reneg-scsv if 2 addr-reneg set8 then
+
+       \ Check compression.
+       ok-compression ifnot 40 fail-alert then
+
+       \ Filter hash function support by what the server also supports.
+       \ If no common hash function remains, then ECDHE suites are not
+       \ possible.
+       supported-hash-functions drop 257 *
+       addr-hashes get16 and dup addr-hashes set16
+       0<> { can-ecdhe }
+
+       \ Filter supported curves. If there is no common curve between
+       \ client and us, then ECDHE suites cannot be used. Note that we
+       \ may still allow ECDH, depending on the EC key handler.
+       addr-curves get32 supported-curves and dup addr-curves set32
+       ifnot 0 >can-ecdhe then
+
+       \ If resuming a session, then the next steps are not necessary;
+       \ we won't invoke the policy handler.
+       resume if -1 ret then
+
+       \ We are not resuming, so a new session ID should be generated.
+       addr-session_id 32 mkrand
+
+       \ Translate common cipher suites, then squeeze out holes: there
+       \ may be holes because of the way we fill the list when the
+       \ server preference order is enforced, and also in case some
+       \ suites are filtered out. In particular:
+       \ -- ECDHE suites are removed if there is no common hash function
+       \    (for signatures) or no common curve.
+       \ -- TLS-1.2-only suites are removed if the negociated version is
+       \    TLS-1.1 or lower.
+       addr-client_suites dup >css-off
+       begin dup css-max < while
+               dup get16 dup cipher-suite-to-elements
+               can-ecdhe ifnot
+                       dup 12 >> dup 1 = swap 2 = or if
+                               2drop 0 dup
+                       then
+               then
+               can-tls12 ifnot
+                       \ Suites compatible with TLS-1.0 and TLS-1.1 are
+                       \ exactly the ones that use HMAC/SHA-1.
+                       dup 0xF0 and 0x20 <> if
+                               2drop 0 dup
+                       then
+               then
+               dup if
+                       css-off 2+ set16 css-off set16
+                       css-off 4 + >css-off
+               else
+                       2drop
+               then
+               4 +
+       repeat
+       drop
+       css-off addr-client_suites - 2 >>
+       dup ifnot
+               \ No common cipher suite: handshake failure.
+               40 fail-alert
+       then
+       addr-client_suites_num set8
+
+       \ Call policy handler to obtain the cipher suite and other
+       \ parameters.
+       call-policy-handler ifnot 40 fail-alert then
+
+       \ We are not resuming a session.
+       0 ;
+
+\ Write ServerHello.
+: write-ServerHello ( initial -- )
+       { initial }
+       \ Compute ServerHello length. Right now we only send the
+       \ "secure renegotiation" extension.
+       2 write8 70
+
+       addr-reneg get8 2 = if
+               initial if 5 else 29 then
+       else
+               0
+       then
+       { ext-reneg-len }
+       addr-peer_log_max_frag_len get8 if 5 else 0 then
+       { ext-max-frag-len }
+
+       ext-reneg-len ext-max-frag-len + dup if 2 + then +
+       write24
+
+       \ Protocol version
+       addr-version get16 write16
+
+       \ Server random
+       addr-server_random 4 bzero
+       addr-server_random 4 + 28 mkrand
+       addr-server_random 32 write-blob
+
+       \ Session ID
+       \ TODO: if we have no session cache at all, we might send here
+       \ an empty session ID. This would save a bit of network
+       \ bandwidth.
+       32 write8
+       addr-session_id 32 write-blob
+
+       \ Cipher suite
+       addr-cipher_suite get16 write16
+
+       \ Compression method
+       0 write8
+
+       \ Extensions
+       ext-reneg-len ext-max-frag-len + dup if
+               write16
+               ext-reneg-len dup if
+                       0xFF01 write16
+                       4 - dup write16
+                       1- addr-saved_finished swap write-blob-head8
+               else
+                       drop
+               then
+               ext-max-frag-len if
+                       0x0001 write16
+                       1 write16 addr-peer_log_max_frag_len get8 8 - write8
+               then
+       else
+               drop
+       then ;
+
+\ Compute total chain length. This includes the individual certificate
+\ headers, but not the total chain header. This also sets the cert_cur,
+\ cert_len and chain_len context fields.
+cc: total-chain-length ( -- len ) {
+       size_t u;
+       uint32_t total;
+
+       total = 0;
+       for (u = 0; u < CTX->chain_len; u ++) {
+               total += 3 + (uint32_t)CTX->chain[u].data_len;
+       }
+       T0_PUSH(total);
+}
+
+\ Get length for current certificate in the chain; if the chain end was
+\ reached, then this returns -1.
+cc: begin-cert ( -- len ) {
+       if (CTX->chain_len == 0) {
+               T0_PUSHi(-1);
+       } else {
+               CTX->cert_cur = CTX->chain->data;
+               CTX->cert_len = CTX->chain->data_len;
+               CTX->chain ++;
+               CTX->chain_len --;
+               T0_PUSH(CTX->cert_len);
+       }
+}
+
+\ Copy a chunk of certificate data into the pad. Returned value is the
+\ chunk length, or 0 if the certificate end is reached.
+cc: copy-cert-chunk ( -- len ) {
+       size_t clen;
+
+       clen = CTX->cert_len;
+       if (clen > sizeof ENG->pad) {
+               clen = sizeof ENG->pad;
+       }
+       memcpy(ENG->pad, CTX->cert_cur, clen);
+       CTX->cert_cur += clen;
+       CTX->cert_len -= clen;
+       T0_PUSH(clen);
+}
+
+\ Write the server Certificate.
+: write-Certificate ( -- )
+       11 write8
+       total-chain-length
+       dup 3 + write24 write24
+       begin
+               begin-cert
+               dup 0< if drop ret then write24
+               begin copy-cert-chunk dup while
+                       addr-pad swap write-blob
+               repeat
+               drop
+       again ;
+
+\ Do the first part of ECDHE. Returned value is the computed signature
+\ length, or a negative error code on error.
+cc: do-ecdhe-part1 ( curve -- len ) {
+       int curve = T0_POPi();
+       T0_PUSHi(do_ecdhe_part1(CTX, curve));
+}
+
+\ Write the Server Key Exchange message (if applicable).
+: write-ServerKeyExchange ( -- )
+       addr-cipher_suite get16 use-ecdhe? ifnot ret then
+
+       \ We must select an appropriate curve among the curves that
+       \ are supported both by us and the peer. Right now we use
+       \ the one with the smallest ID, which in practice means P-256.
+       \ (TODO: add some option to make that behaviour configurable.)
+       \
+       \ This loop always terminates because previous processing made
+       \ sure that ECDHE suites are not selectable if there is no common
+       \ curve.
+       addr-curves get32 0
+       begin dup2 >> 1 and 0= while 1+ repeat
+       { curve-id } drop
+
+       \ Compute the signed curve point to send.
+       curve-id do-ecdhe-part1 dup 0< if neg fail then { sig-len }
+
+       \ If using TLS-1.2+, then the hash function and signature
+       \ algorithm are explicitly encoded in the message.
+       addr-version get16 0x0303 >= { tls1.2+ }
+
+       12 write8
+       sig-len addr-ecdhe_point_len get8 + tls1.2+ 2 and + 6 + write24
+
+       \ Curve parameters: named curve with 16-bit ID.
+       3 write8 curve-id write16
+
+       \ Public point.
+       addr-ecdhe_point addr-ecdhe_point_len get8 write-blob-head8
+
+       \ If TLS-1.2+, write hash and signature identifiers.
+       tls1.2+ if
+               \ Hash identifier is in the sign_hash_id field.
+               addr-sign_hash_id get8 write8
+               \ 'use-rsa-ecdhe?' returns -1 for RSA, 0 for ECDSA.
+               \ The byte on the wire shall be 1 for RSA, 3 for ECDSA.
+               addr-cipher_suite get16 use-rsa-ecdhe? 1 << 3 + write8
+       then
+
+       \ Signature.
+       sig-len write16
+       addr-pad sig-len write-blob ;
+
+\ Write the Server Hello Done message.
+: write-ServerHelloDone ( -- )
+       14 write8 0 write24 ;
+
+\ Perform RSA decryption of the client-sent pre-master secret. The value
+\ is in the pad, and its length is provided as parameter.
+cc: do-rsa-decrypt ( len prf_id -- ) {
+       int prf_id = T0_POPi();
+       size_t len = T0_POP();
+       do_rsa_decrypt(CTX, prf_id, ENG->pad, len);
+}
+
+\ Perform ECDH (not ECDHE). The point from the client is in the pad, and
+\ its length is provided as parameter.
+cc: do-ecdh ( len prf_id -- ) {
+       int prf_id = T0_POPi();
+       size_t len = T0_POP();
+       do_ecdh(CTX, prf_id, ENG->pad, len);
+}
+
+\ Do the second part of ECDHE.
+cc: do-ecdhe-part2 ( len prf_id -- ) {
+       int prf_id = T0_POPi();
+       size_t len = T0_POP();
+       do_ecdhe_part2(CTX, prf_id, ENG->pad, len);
+}
+
+\ Read the Client Key Exchange.
+: read-ClientKeyExchange ( -- )
+       \ Get header, and check message type.
+       read-handshake-header 16 = ifnot ERR_UNEXPECTED fail then
+
+       \ What we should get depends on the cipher suite.
+       addr-cipher_suite get16 use-rsa-keyx? if
+               \ RSA key exchange: we expect a RSA-encrypted value.
+               read16
+               dup 512 > if ERR_LIMIT_EXCEEDED fail then
+               dup { enc-rsa-len }
+               addr-pad swap read-blob
+               enc-rsa-len addr-cipher_suite get16 prf-id do-rsa-decrypt
+       then
+       addr-cipher_suite get16 dup use-ecdhe? swap use-ecdh? { ecdhe ecdh }
+       ecdh ecdhe or if
+               \ ECDH or ECDHE key exchange: we expect an EC point.
+               read8 dup { ec-point-len }
+               addr-pad swap read-blob
+               ec-point-len addr-cipher_suite get16 prf-id
+               ecdhe if do-ecdhe-part2 else do-ecdh then
+       then
+       close-elt ;
+
+\ Send a HelloRequest.
+: send-HelloRequest ( -- )
+       flush-record
+       begin can-output? not while wait-co drop repeat
+       22 addr-record_type_out set8
+       0 write8 0 write24 flush-record
+       23 addr-record_type_out set8 ;
+
+\ Make a handshake.
+: do-handshake ( initial -- )
+       0 addr-application_data set8
+       22 addr-record_type_out set8
+       multihash-init
+       read-ClientHello
+       more-incoming-bytes? if ERR_UNEXPECTED fail then
+       if
+               \ Session resumption
+               write-ServerHello
+               0 write-CCS-Finished
+               0 read-CCS-Finished
+       else
+               \ Not a session resumption
+               write-ServerHello
+               write-Certificate
+               write-ServerKeyExchange
+               write-ServerHelloDone
+               flush-record
+               read-ClientKeyExchange
+               0 read-CCS-Finished
+               0 write-CCS-Finished
+               save-session
+       then
+       1 addr-application_data set8
+       23 addr-record_type_out set8 ;
+
+\ Entry point.
+: main ( -- ! )
+       \ Perform initial handshake.
+       -1 do-handshake
+
+       begin
+               \ Wait for further invocation. At that point, we should
+               \ get either an explicit call for renegotiation, or
+               \ an incoming ClientHello handshake message.
+               wait-co
+               dup 0x07 and case
+                       0x00 of
+                               0x10 and if
+                                       \ The best we can do is ask for a
+                                       \ renegotiation, then wait for it
+                                       \ to happen.
+                                       send-HelloRequest
+                               then
+                       endof
+                       0x01 of
+                               \ Reject renegotiations if the peer does not
+                               \ support secure renegotiation. As allowed
+                               \ by RFC 5246, we do not send a
+                               \ no_renegotiation alert and just ignore the
+                               \ HelloRequest.
+                               drop
+                               addr-reneg get8 1 <> if
+                                       0 do-handshake
+                               else
+                                       flush-record
+                                       begin can-output? not while
+                                               wait-co drop
+                                       repeat
+                               then
+                       endof
+                       ERR_UNEXPECTED fail
+               endcase
+       again
+       ;
diff --git a/src/ssl/ssl_io.c b/src/ssl/ssl_io.c
new file mode 100644 (file)
index 0000000..b409636
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_sslio_init(br_sslio_context *ctx,
+       br_ssl_engine_context *engine,
+       int (*low_read)(void *read_context,
+               unsigned char *data, size_t len),
+       void *read_context,
+       int (*low_write)(void *write_context,
+               const unsigned char *data, size_t len),
+       void *write_context)
+{
+       ctx->engine = engine;
+       ctx->low_read = low_read;
+       ctx->read_context = read_context;
+       ctx->low_write = low_write;
+       ctx->write_context = write_context;
+}
+
+/*
+ * Run the engine, until the specified target state is achieved, or
+ * an error occurs. The target state is SENDAPP, RECVAPP, or the
+ * combination of both (the combination matches either). When a match is
+ * achieved, this function returns 0. On error, it returns -1.
+ */
+static int
+run_until(br_sslio_context *ctx, unsigned target)
+{
+       for (;;) {
+               unsigned state;
+
+               state = br_ssl_engine_current_state(ctx->engine);
+               if (state & BR_SSL_CLOSED) {
+                       return -1;
+               }
+
+               /*
+                * If there is some record data to send, do it. This takes
+                * precedence over everything else.
+                */
+               if (state & BR_SSL_SENDREC) {
+                       unsigned char *buf;
+                       size_t len;
+                       int wlen;
+
+                       buf = br_ssl_engine_sendrec_buf(ctx->engine, &len);
+                       wlen = ctx->low_write(ctx->write_context, buf, len);
+                       if (wlen < 0) {
+                               /*
+                                * If we received a close_notify and we
+                                * still send something, then we have our
+                                * own response close_notify to send, and
+                                * the peer is allowed by RFC 5246 not to
+                                * wait for it.
+                                */
+                               if (!ctx->engine->shutdown_recv) {
+                                       br_ssl_engine_fail(
+                                               ctx->engine, BR_ERR_IO);
+                               }
+                               return -1;
+                       }
+                       if (wlen > 0) {
+                               br_ssl_engine_sendrec_ack(ctx->engine, wlen);
+                       }
+                       continue;
+               }
+
+               /*
+                * If we reached our target, then we are finished.
+                */
+               if (state & target) {
+                       return 0;
+               }
+
+               /*
+                * If some application data must be read, and we did not
+                * exit, then this means that we are trying to write data,
+                * and that's not possible until the application data is
+                * read. This may happen if using a shared in/out buffer,
+                * and the underlying protocol is not strictly half-duplex.
+                * This is unrecoverable here, so we report an error.
+                */
+               if (state & BR_SSL_RECVAPP) {
+                       return -1;
+               }
+
+               /*
+                * If we reached that point, then either we are trying
+                * to read data and there is some, or the engine is stuck
+                * until a new record is obtained.
+                */
+               if (state & BR_SSL_RECVREC) {
+                       unsigned char *buf;
+                       size_t len;
+                       int rlen;
+
+                       buf = br_ssl_engine_recvrec_buf(ctx->engine, &len);
+                       rlen = ctx->low_read(ctx->read_context, buf, len);
+                       if (rlen < 0) {
+                               br_ssl_engine_fail(ctx->engine, BR_ERR_IO);
+                               return -1;
+                       }
+                       if (rlen > 0) {
+                               br_ssl_engine_recvrec_ack(ctx->engine, rlen);
+                       }
+                       continue;
+               }
+
+               /*
+                * We can reach that point if the target RECVAPP, and
+                * the state contains SENDAPP only. This may happen with
+                * a shared in/out buffer. In that case, we must flush
+                * the buffered data to "make room" for a new incoming
+                * record.
+                */
+               br_ssl_engine_flush(ctx->engine, 0);
+       }
+}
+
+/* see bearssl_ssl.h */
+int
+br_sslio_read(br_sslio_context *ctx, void *dst, size_t len)
+{
+       unsigned char *buf;
+       size_t alen;
+
+       if (run_until(ctx, BR_SSL_RECVAPP) < 0) {
+               return -1;
+       }
+       buf = br_ssl_engine_recvapp_buf(ctx->engine, &alen);
+       if (alen > len) {
+               alen = len;
+       }
+       memcpy(dst, buf, alen);
+       br_ssl_engine_recvapp_ack(ctx->engine, alen);
+       return (int)alen;
+}
+
+/* see bearssl_ssl.h */
+int
+br_sslio_read_all(br_sslio_context *ctx, void *dst, size_t len)
+{
+       unsigned char *buf;
+
+       buf = dst;
+       while (len > 0) {
+               int rlen;
+
+               rlen = br_sslio_read(ctx, buf, len);
+               if (rlen < 0) {
+                       return -1;
+               }
+               buf += rlen;
+               len -= (size_t)rlen;
+       }
+       return 0;
+}
+
+/* see bearssl_ssl.h */
+int
+br_sslio_write(br_sslio_context *ctx, const void *src, size_t len)
+{
+       unsigned char *buf;
+       size_t alen;
+
+       if (run_until(ctx, BR_SSL_SENDAPP) < 0) {
+               return -1;
+       }
+       buf = br_ssl_engine_sendapp_buf(ctx->engine, &alen);
+       if (alen > len) {
+               alen = len;
+       }
+       memcpy(buf, src, alen);
+       br_ssl_engine_sendapp_ack(ctx->engine, alen);
+       return (int)alen;
+}
+
+/* see bearssl_ssl.h */
+int
+br_sslio_write_all(br_sslio_context *ctx, const void *src, size_t len)
+{
+       const unsigned char *buf;
+
+       buf = src;
+       while (len > 0) {
+               int wlen;
+
+               wlen = br_sslio_write(ctx, buf, len);
+               if (wlen < 0) {
+                       return -1;
+               }
+               buf += wlen;
+               len -= (size_t)wlen;
+       }
+       return 0;
+}
+
+/* see bearssl_ssl.h */
+int
+br_sslio_flush(br_sslio_context *ctx)
+{
+       /*
+        * We trigger a flush. We know the data is gone when there is
+        * no longer any record data to send, and we can either read
+        * or write application data. The call to run_until() does the
+        * job because it ensures that any assembled record data is
+        * first sent down the wire before considering anything else.
+        */
+       br_ssl_engine_flush(ctx->engine, 0);
+       return run_until(ctx, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
+}
+
+/* see bearssl_ssl.h */
+int
+br_sslio_close(br_sslio_context *ctx)
+{
+       br_ssl_engine_close(ctx->engine);
+       while (br_ssl_engine_current_state(ctx->engine) != BR_SSL_CLOSED) {
+               /*
+                * Discard any incoming application data.
+                */
+               size_t len;
+
+               run_until(ctx, BR_SSL_RECVAPP);
+               if (br_ssl_engine_recvapp_buf(ctx->engine, &len) != NULL) {
+                       br_ssl_engine_recvapp_ack(ctx->engine, len);
+               }
+       }
+       return br_ssl_engine_last_error(ctx->engine) == BR_ERR_OK;
+}
diff --git a/src/ssl/ssl_lru.c b/src/ssl/ssl_lru.c
new file mode 100644 (file)
index 0000000..b29b3ed
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Each entry consists in a fixed number of bytes. Entries are concatenated
+ * in the store block. "Addresses" are really offsets in the block,
+ * expressed over 32 bits (so the cache may have size at most 4 GB, which
+ * "ought to be enough for everyone"). The "null address" is 0xFFFFFFFF.
+ * Note that since the storage block alignment is in no way guaranted, we
+ * perform only accesses that can handle unaligned data.
+ *
+ * Two concurrent data structures are maintained:
+ *
+ * -- Entries are organised in a doubly-linked list; saved entries are added
+ * at the head, and loaded entries are moved to the head. Eviction uses
+ * the list tail (this is the LRU algorithm).
+ *
+ * -- Entries are indexed with a binary tree: all left descendants of a
+ * node have a lower session ID (in lexicographic order), while all
+ * right descendants have a higher session ID. The tree is balanced.
+ *
+ * Entry format:
+ *
+ *   session ID          32 bytes
+ *   master secret       48 bytes
+ *   protocol version    2 bytes (big endian)
+ *   cipher suite        2 bytes (big endian)
+ *   list prev           4 bytes (big endian)
+ *   list next           4 bytes (big endian)
+ *   tree left child     4 bytes (big endian)
+ *   tree right child    4 bytes (big endian)
+ *   tree node colour    1 byte (0 = red, 1 = black)
+ *
+ * We need to keep the tree balanced because an attacker could make
+ * handshakes, selecting some specific sessions (by reusing them) to
+ * try to make us make an imbalanced tree that makes lookups expensive
+ * (a denial-of-service attack that would persist as long as the cache
+ * remains, i.e. even after the attacker made all his connections).
+ * To do that, we replace the session ID (or the start of the session ID)
+ * with a HMAC value computed over the replaced part; the hash function
+ * implementation and the key are obtained from the server context upon
+ * first save() call.
+ */
+#define SESSION_ID_LEN       32
+#define MASTER_SECRET_LEN    48
+
+#define SESSION_ID_OFF        0
+#define MASTER_SECRET_OFF    32
+#define VERSION_OFF          80
+#define CIPHER_SUITE_OFF     82
+#define LIST_PREV_OFF        84
+#define LIST_NEXT_OFF        88
+#define TREE_LEFT_OFF        92
+#define TREE_RIGHT_OFF       96
+
+#define LRU_ENTRY_LEN       100
+
+#define ADDR_NULL   ((uint32_t)-1)
+
+#define GETSET(name, off) \
+static inline uint32_t get_ ## name(br_ssl_session_cache_lru *cc, uint32_t x) \
+{ \
+       return br_dec32be(cc->store + x + (off)); \
+} \
+static inline void set_ ## name(br_ssl_session_cache_lru *cc, \
+       uint32_t x, uint32_t val) \
+{ \
+       br_enc32be(cc->store + x + (off), val); \
+}
+
+GETSET(prev, LIST_PREV_OFF)
+GETSET(next, LIST_NEXT_OFF)
+GETSET(left, TREE_LEFT_OFF)
+GETSET(right, TREE_RIGHT_OFF)
+
+/*
+ * Transform the session ID by replacing the first N bytes with a HMAC
+ * value computed over these bytes, using the random key K (the HMAC
+ * value is truncated if needed). HMAC will use the same hash function
+ * as the DRBG in the SSL server context, so with SHA-256, SHA-384,
+ * or SHA-1, depending on what is available.
+ *
+ * The risk of collision is considered too small to be a concern; and
+ * the impact of a collision is low (the handshake won't succeed). This
+ * risk is much lower than any transmission error, which would lead to
+ * the same consequences.
+ */
+static void
+mask_id(br_ssl_session_cache_lru *cc,
+       const unsigned char *src, unsigned char *dst)
+{
+       br_hmac_key_context hkc;
+       br_hmac_context hc;
+
+       memcpy(dst, src, SESSION_ID_LEN);
+       br_hmac_key_init(&hkc, cc->hash, cc->index_key, sizeof cc->index_key);
+       br_hmac_init(&hc, &hkc, SESSION_ID_LEN);
+       br_hmac_update(&hc, src, SESSION_ID_LEN);
+       br_hmac_out(&hc, dst);
+}
+
+/*
+ * Find a node by ID. Returned value is the node address, or ADDR_NULL if
+ * the node is not found.
+ *
+ * If addr_link is not NULL, then '*addr_link' is set to the address of the
+ * last followed link. If the found node is the root, then '*addr_link' is
+ * set to ADDR_NULL.
+ */
+static uint32_t
+find_node(br_ssl_session_cache_lru *cc, const unsigned char *id,
+       uint32_t *addr_link)
+{
+       uint32_t x, y;
+
+       x = cc->root;
+       y = ADDR_NULL;
+       while (x != ADDR_NULL) {
+               int r;
+
+               r = memcmp(id, cc->store + x + SESSION_ID_OFF, SESSION_ID_LEN);
+               if (r < 0) {
+                       y = x + TREE_LEFT_OFF;
+                       x = get_left(cc, x);
+               } else if (r == 0) {
+                       if (addr_link != NULL) {
+                               *addr_link = y;
+                       }
+                       return x;
+               } else {
+                       y = x + TREE_RIGHT_OFF;
+                       x = get_right(cc, x);
+               }
+       }
+       if (addr_link != NULL) {
+               *addr_link = y;
+       }
+       return ADDR_NULL;
+}
+
+/*
+ * For node x, find its replacement upon removal.
+ *
+ *  -- If node x has no child, then this returns ADDR_NULL.
+ *  -- Otherwise, if node x has a left child, then the replacement is the
+ *     rightmost left-descendent.
+ *  -- Otherwise, the replacement is the leftmost right-descendent.
+ *
+ * If a node is returned, then '*al' is set to the address of the field
+ * that points to that node.
+ */
+static uint32_t
+find_replacement_node(br_ssl_session_cache_lru *cc, uint32_t x, uint32_t *al)
+{
+       uint32_t y1, y2;
+
+       y1 = get_left(cc, x);
+       if (y1 != ADDR_NULL) {
+               y2 = x + TREE_LEFT_OFF;
+               for (;;) {
+                       uint32_t z;
+
+                       z = get_right(cc, y1);
+                       if (z == ADDR_NULL) {
+                               *al = y2;
+                               return y1;
+                       }
+                       y2 = y1 + TREE_RIGHT_OFF;
+                       y1 = z;
+               }
+       }
+       y1 = get_right(cc, x);
+       if (y1 != ADDR_NULL) {
+               y2 = x + TREE_RIGHT_OFF;
+               for (;;) {
+                       uint32_t z;
+
+                       z = get_left(cc, y1);
+                       if (z == ADDR_NULL) {
+                               *al = y2;
+                               return y1;
+                       }
+                       y2 = y1 + TREE_LEFT_OFF;
+                       y1 = z;
+               }
+       }
+       *al = ADDR_NULL;
+       return ADDR_NULL;
+}
+
+static inline void
+set_link(br_ssl_session_cache_lru *cc, uint32_t alx, uint32_t x)
+{
+       if (alx == ADDR_NULL) {
+               cc->root = x;
+       } else {
+               br_enc32be(cc->store + alx, x);
+       }
+}
+
+static void
+remove_node(br_ssl_session_cache_lru *cc, uint32_t x)
+{
+       uint32_t alx, y, aly;
+
+       /*
+        * Find node back and its ancestor link.
+        */
+       find_node(cc, cc->store + x + SESSION_ID_OFF, &alx);
+
+       /*
+        * Find replacement node.
+        */
+       y = find_replacement_node(cc, x, &aly);
+
+       /*
+        * Unlink replacement node.
+        */
+       set_link(cc, aly, ADDR_NULL);
+
+       /*
+        * Link the replacement node in its new place.
+        */
+       set_link(cc, alx, y);
+}
+
+static void
+lru_save(const br_ssl_session_cache_class **ctx,
+       br_ssl_server_context *server_ctx,
+       const br_ssl_session_parameters *params)
+{
+       br_ssl_session_cache_lru *cc;
+       unsigned char id[SESSION_ID_LEN];
+       uint32_t x, alx;
+
+       cc = (br_ssl_session_cache_lru *)ctx;
+
+       /*
+        * If the buffer is too small, we don't record anything. This
+        * test avoids problems in subsequent code.
+        */
+       if (cc->store_len < LRU_ENTRY_LEN) {
+               return;
+       }
+
+       /*
+        * Upon the first save in a session cache instance, we obtain
+        * a random key for our indexing.
+        */
+       if (!cc->init_done) {
+               br_hmac_drbg_generate(&server_ctx->eng.rng,
+                       cc->index_key, sizeof cc->index_key);
+               cc->hash = br_hmac_drbg_get_hash(&server_ctx->eng.rng);
+               cc->init_done = 1;
+       }
+       mask_id(cc, params->session_id, id);
+
+       /*
+        * Look for the node in the tree. If the same ID is already used,
+        * then reject it. This is a collision event, which should be
+        * exceedingly rare.
+        * Note: we do NOT record the emplacement here, because the
+        * removal of an entry may change the tree topology.
+        */
+       if (find_node(cc, id, NULL) != ADDR_NULL) {
+               return;
+       }
+
+       /*
+        * Find some room for the new parameters. If the cache is not
+        * full yet, add it to the end of the area and bump the pointer up.
+        * Otherwise, evict the list tail entry. Note that we already
+        * filtered out the case of a ridiculously small buffer that
+        * cannot hold any entry at all; thus, if there is no room for an
+        * extra entry, then the cache cannot be empty.
+        */
+       if (cc->store_ptr > (cc->store_len - LRU_ENTRY_LEN)) {
+               /*
+                * Evict tail. If the buffer has room for a single entry,
+                * then this may also be the head.
+                */
+               x = cc->tail;
+               cc->tail = get_prev(cc, x);
+               if (cc->tail == ADDR_NULL) {
+                       cc->head = ADDR_NULL;
+               } else {
+                       set_next(cc, cc->tail, ADDR_NULL);
+               }
+
+               /*
+                * Remove the node from the tree.
+                */
+               remove_node(cc, x);
+       } else {
+               /*
+                * Allocate room for new node.
+                */
+               x = cc->store_ptr;
+               cc->store_ptr += LRU_ENTRY_LEN;
+       }
+
+       /*
+        * Find the emplacement for the new node, and link it.
+        */
+       find_node(cc, id, &alx);
+       set_link(cc, alx, x);
+       set_left(cc, x, ADDR_NULL);
+       set_right(cc, x, ADDR_NULL);
+
+       /*
+        * New entry becomes new list head. It may also become the list
+        * tail if the cache was empty at that point.
+        */
+       if (cc->head == ADDR_NULL) {
+               cc->tail = x;
+       } else {
+               set_prev(cc, cc->head, x);
+       }
+       set_prev(cc, x, ADDR_NULL);
+       set_next(cc, x, cc->head);
+       cc->head = x;
+
+       /*
+        * Fill data in the entry.
+        */
+       memcpy(cc->store + x + SESSION_ID_OFF, id, SESSION_ID_LEN);
+       memcpy(cc->store + x + MASTER_SECRET_OFF,
+               params->master_secret, MASTER_SECRET_LEN);
+       br_enc16be(cc->store + x + VERSION_OFF, params->version);
+       br_enc16be(cc->store + x + CIPHER_SUITE_OFF, params->cipher_suite);
+}
+
+static int
+lru_load(const br_ssl_session_cache_class **ctx,
+       br_ssl_server_context *server_ctx,
+       br_ssl_session_parameters *params)
+{
+       br_ssl_session_cache_lru *cc;
+       unsigned char id[SESSION_ID_LEN];
+       uint32_t x;
+
+       (void)server_ctx;
+       cc = (br_ssl_session_cache_lru *)ctx;
+       if (!cc->init_done) {
+               return 0;
+       }
+       mask_id(cc, params->session_id, id);
+       x = find_node(cc, id, NULL);
+       if (x != ADDR_NULL) {
+               params->version = br_dec16be(
+                       cc->store + x + VERSION_OFF);
+               params->cipher_suite = br_dec16be(
+                       cc->store + x + CIPHER_SUITE_OFF);
+               memcpy(params->master_secret,
+                       cc->store + x + MASTER_SECRET_OFF,
+                       MASTER_SECRET_LEN);
+               if (x != cc->head) {
+                       /*
+                        * Found node is not at list head, so move
+                        * it to the head.
+                        */
+                       uint32_t p, n;
+
+                       p = get_prev(cc, x);
+                       n = get_next(cc, x);
+                       set_next(cc, p, n);
+                       if (n == ADDR_NULL) {
+                               cc->tail = p;
+                       } else {
+                               set_prev(cc, n, p);
+                       }
+                       set_prev(cc, cc->head, x);
+                       set_next(cc, x, cc->head);
+                       set_prev(cc, x, ADDR_NULL);
+                       cc->head = x;
+               }
+               return 1;
+       }
+       return 0;
+}
+
+static const br_ssl_session_cache_class lru_class = {
+       sizeof(br_ssl_session_cache_lru),
+       &lru_save,
+       &lru_load
+};
+
+/* see inner.h */
+void
+br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc,
+       unsigned char *store, size_t store_len)
+{
+       cc->vtable = &lru_class;
+       cc->store = store;
+       cc->store_len = store_len;
+       cc->store_ptr = 0;
+       cc->init_done = 0;
+       cc->head = ADDR_NULL;
+       cc->tail = ADDR_NULL;
+       cc->root = ADDR_NULL;
+}
diff --git a/src/ssl/ssl_rec_cbc.c b/src/ssl/ssl_rec_cbc.c
new file mode 100644 (file)
index 0000000..c080604
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static void
+in_cbc_init(br_sslrec_in_cbc_context *cc,
+       const br_block_cbcdec_class *bc_impl,
+       const void *bc_key, size_t bc_key_len,
+       const br_hash_class *dig_impl,
+       const void *mac_key, size_t mac_key_len, size_t mac_out_len,
+       const void *iv)
+{
+       cc->vtable = &br_sslrec_in_cbc_vtable;
+       cc->seq = 0;
+       bc_impl->init(&cc->bc.vtable, bc_key, bc_key_len);
+       br_hmac_key_init(&cc->mac, dig_impl, mac_key, mac_key_len);
+       cc->mac_len = mac_out_len;
+       if (iv == NULL) {
+               memset(cc->iv, 0, sizeof cc->iv);
+               cc->explicit_IV = 1;
+       } else {
+               memcpy(cc->iv, iv, bc_impl->block_size);
+               cc->explicit_IV = 0;
+       }
+}
+
+static int
+cbc_check_length(const br_sslrec_in_cbc_context *cc, size_t rlen)
+{
+       /*
+        * Plaintext size: at most 16384 bytes
+        * Padding: at most 256 bytes
+        * MAC: mac_len extra bytes
+        * TLS 1.1+: each record has an explicit IV
+        *
+        * Minimum length includes at least one byte of padding, and the
+        * MAC.
+        *
+        * Total length must be a multiple of the block size.
+        */
+       size_t blen;
+       size_t min_len, max_len;
+
+       blen = cc->bc.vtable->block_size;
+       min_len = (blen + cc->mac_len) & ~(blen - 1);
+       max_len = (16384 + 256 + cc->mac_len) & ~(blen - 1);
+       if (cc->explicit_IV) {
+               min_len += blen;
+               max_len += blen;
+       }
+       return min_len <= rlen && rlen <= max_len;
+}
+
+/*
+ * Rotate array buf[] of length 'len' to the left (towards low indices)
+ * by 'num' bytes if ctl is 1; otherwise, leave it unchanged. This is
+ * constant-time. 'num' MUST be lower than 'len'. 'len' MUST be lower
+ * than or equal to 64.
+ */
+static void
+cond_rotate(uint32_t ctl, unsigned char *buf, size_t len, size_t num)
+{
+       unsigned char tmp[64];
+       size_t u, v;
+
+       for (u = 0, v = num; u < len; u ++) {
+               tmp[u] = MUX(ctl, buf[v], buf[u]);
+               if (++ v == len) {
+                       v = 0;
+               }
+       }
+       memcpy(buf, tmp, len);
+}
+
+static unsigned char *
+cbc_decrypt(br_sslrec_in_cbc_context *cc,
+       int record_type, unsigned version, void *data, size_t *data_len)
+{
+       /*
+        * We represent all lengths on 32-bit integers, because:
+        * -- SSL record lengths always fit in 32 bits;
+        * -- our constant-time primitives operate on 32-bit integers.
+        */
+       unsigned char *buf;
+       uint32_t u, v, len, blen, min_len, max_len;
+       uint32_t good, pad_len, rot_count, len_withmac, len_nomac;
+       unsigned char tmp1[64], tmp2[64];
+       int i;
+       br_hmac_context hc;
+
+       buf = data;
+       len = *data_len;
+       blen = cc->bc.vtable->block_size;
+
+       /*
+        * Decrypt data, and skip the explicit IV (if applicable). Note
+        * that the total length is supposed to have been verified by
+        * the caller. If there is an explicit IV, then we actually
+        * "decrypt" it using the implicit IV (from previous record),
+        * which is useless but harmless.
+        */
+       cc->bc.vtable->run(&cc->bc.vtable, cc->iv, data, len);
+       if (cc->explicit_IV) {
+               buf += blen;
+               len -= blen;
+       }
+
+       /*
+        * Compute minimum and maximum length of plaintext + MAC. These
+        * lengths can be inferred from the outside: they are not secret.
+        */
+       min_len = (cc->mac_len + 256 < len) ? len - 256 : cc->mac_len;
+       max_len = len - 1;
+
+       /*
+        * Use the last decrypted byte to compute the actual payload
+        * length. Take care not to underflow (we use unsigned types).
+        */
+       pad_len = buf[max_len];
+       good = LE(pad_len, (uint32_t)(max_len - min_len));
+       len = MUX(good, (uint32_t)(max_len - pad_len), min_len);
+
+       /*
+        * Check padding contents: all padding bytes must be equal to
+        * the value of pad_len.
+        */
+       for (u = min_len; u < max_len; u ++) {
+               good &= LT(u, len) | EQ(buf[u], pad_len);
+       }
+
+       /*
+        * Extract the MAC value. This is done in one pass, but results
+        * in a "rotated" MAC value depending on where it actually
+        * occurs. The 'rot_count' value is set to the offset of the
+        * first MAC byte within tmp1[].
+        *
+        * min_len and max_len are also adjusted to the minimum and
+        * maximum lengths of the plaintext alone (without the MAC).
+        */
+       len_withmac = (uint32_t)len;
+       len_nomac = len_withmac - cc->mac_len;
+       min_len -= cc->mac_len;
+       rot_count = 0;
+       memset(tmp1, 0, cc->mac_len);
+       v = 0;
+       for (u = min_len; u < max_len; u ++) {
+               tmp1[v] |= MUX(GE(u, len_nomac) & LT(u, len_withmac),
+                       buf[u], 0x00);
+               rot_count = MUX(EQ(u, len_nomac), v, rot_count);
+               if (++ v == cc->mac_len) {
+                       v = 0;
+               }
+       }
+       max_len -= cc->mac_len;
+
+       /*
+        * Rotate back the MAC value. The loop below does the constant-time
+        * rotation in time n*log n for a MAC output of length n. We assume
+        * that the MAC output length is no more than 64 bytes, so the
+        * rotation count fits on 6 bits.
+        */
+       for (i = 5; i >= 0; i --) {
+               uint32_t rc;
+
+               rc = (uint32_t)1 << i;
+               cond_rotate(rot_count >> i, tmp1, cc->mac_len, rc);
+               rot_count &= ~rc;
+       }
+
+       /*
+        * Recompute the HMAC value. The input is the concatenation of
+        * the sequence number (8 bytes), the record header (5 bytes),
+        * and the payload.
+        *
+        * At that point, min_len is the minimum plaintext length, but
+        * max_len still includes the MAC length.
+        */
+       br_enc64be(tmp2, cc->seq ++);
+       tmp2[8] = (unsigned char)record_type;
+       br_enc16be(tmp2 + 9, version);
+       br_enc16be(tmp2 + 11, len_nomac);
+       br_hmac_init(&hc, &cc->mac, cc->mac_len);
+       br_hmac_update(&hc, tmp2, 13);
+       br_hmac_outCT(&hc, buf, len_nomac, min_len, max_len, tmp2);
+
+       /*
+        * Compare the extracted and recomputed MAC values.
+        */
+       for (u = 0; u < cc->mac_len; u ++) {
+               good &= EQ0(tmp1[u] ^ tmp2[u]);
+       }
+
+       /*
+        * Check that the plaintext length is valid. The previous
+        * check was on the encrypted length, but the padding may have
+        * turned shorter than expected.
+        *
+        * Once this final test is done, the critical "constant-time"
+        * section ends and we can make conditional jumps again.
+        */
+       good &= LE(len_nomac, 16384);
+
+       if (!good) {
+               return 0;
+       }
+       *data_len = len_nomac;
+       return buf;
+}
+
+/* see bearssl_ssl.h */
+const br_sslrec_in_cbc_class br_sslrec_in_cbc_vtable = {
+       {
+               sizeof(br_sslrec_in_cbc_context),
+               (int (*)(const br_sslrec_in_class *const *, size_t))
+                       &cbc_check_length,
+               (unsigned char *(*)(const br_sslrec_in_class **,
+                       int, unsigned, void *, size_t *))
+                       &cbc_decrypt
+       },
+       (void (*)(const br_sslrec_in_cbc_class **,
+               const br_block_cbcdec_class *, const void *, size_t,
+               const br_hash_class *, const void *, size_t, size_t,
+               const void *))
+               &in_cbc_init
+};
+
+/*
+ * For CBC output:
+ *
+ * -- With TLS 1.1+, there is an explicit IV. Generation method uses
+ * HMAC, computed over the current sequence number, and the current MAC
+ * key. The resulting value is truncated to the size of a block, and
+ * added at the head of the plaintext; it will get encrypted along with
+ * the data. This custom generation mechanism is "safe" under the
+ * assumption that HMAC behaves like a random oracle; since the MAC for
+ * a record is computed over the concatenation of the sequence number,
+ * the record header and the plaintext, the HMAC-for-IV will not collide
+ * with the normal HMAC.
+ *
+ * -- With TLS 1.0, for application data, we want to enforce a 1/n-1
+ * split, as a countermeasure against chosen-plaintext attacks. We thus
+ * need to leave some room in the buffer for that extra record.
+ */
+
+static void
+out_cbc_init(br_sslrec_out_cbc_context *cc,
+       const br_block_cbcenc_class *bc_impl,
+       const void *bc_key, size_t bc_key_len,
+       const br_hash_class *dig_impl,
+       const void *mac_key, size_t mac_key_len, size_t mac_out_len,
+       const void *iv)
+{
+       cc->vtable = &br_sslrec_out_cbc_vtable;
+       cc->seq = 0;
+       bc_impl->init(&cc->bc.vtable, bc_key, bc_key_len);
+       br_hmac_key_init(&cc->mac, dig_impl, mac_key, mac_key_len);
+       cc->mac_len = mac_out_len;
+       if (iv == NULL) {
+               memset(cc->iv, 0, sizeof cc->iv);
+               cc->explicit_IV = 1;
+       } else {
+               memcpy(cc->iv, iv, bc_impl->block_size);
+               cc->explicit_IV = 0;
+       }
+}
+
+static void
+cbc_max_plaintext(const br_sslrec_out_cbc_context *cc,
+       size_t *start, size_t *end)
+{
+       size_t blen, len;
+
+       blen = cc->bc.vtable->block_size;
+       if (cc->explicit_IV) {
+               *start += blen;
+       } else {
+               *start += 4 + ((cc->mac_len + blen + 1) & ~(blen - 1));
+       }
+       len = (*end - *start) & ~(blen - 1);
+       len -= 1 + cc->mac_len;
+       if (len > 16384) {
+               len = 16384;
+       }
+       *end = *start + len;
+}
+
+static unsigned char *
+cbc_encrypt(br_sslrec_out_cbc_context *cc,
+       int record_type, unsigned version, void *data, size_t *data_len)
+{
+       unsigned char *buf, *rbuf;
+       size_t len, blen, plen;
+       unsigned char tmp[13];
+       br_hmac_context hc;
+
+       buf = data;
+       len = *data_len;
+       blen = cc->bc.vtable->block_size;
+
+       /*
+        * If using TLS 1.0, with more than one byte of plaintext, and
+        * the record is application data, then we need to compute
+        * a "split". We do not perform the split on other record types
+        * because it turned out that some existing, deployed
+        * implementations of SSL/TLS do not tolerate the splitting of
+        * some message types (in particular the Finished message).
+        *
+        * If using TLS 1.1+, then there is an explicit IV. We produce
+        * that IV by adding an extra initial plaintext block, whose
+        * value is computed with HMAC over the record sequence number.
+        */
+       if (cc->explicit_IV) {
+               /*
+                * We use here the fact that all the HMAC variants we
+                * support can produce at least 16 bytes, while all the
+                * block ciphers we support have blocks of no more than
+                * 16 bytes. Thus, we can always truncate the HMAC output
+                * down to the block size.
+                */
+               br_enc64be(tmp, cc->seq);
+               br_hmac_init(&hc, &cc->mac, blen);
+               br_hmac_update(&hc, tmp, 8);
+               br_hmac_out(&hc, buf - blen);
+               rbuf = buf - blen - 5;
+       } else {
+               if (len > 1 && record_type == BR_SSL_APPLICATION_DATA) {
+                       /*
+                        * To do the split, we use a recursive invocation;
+                        * since we only give one byte to the inner call,
+                        * the recursion stops there.
+                        *
+                        * We need to compute the exact size of the extra
+                        * record, so that the two resulting records end up
+                        * being sequential in RAM.
+                        *
+                        * We use here the fact that cbc_max_plaintext()
+                        * adjusted the start offset to leave room for the
+                        * initial fragment.
+                        */
+                       size_t xlen;
+
+                       rbuf = buf - 4
+                               - ((cc->mac_len + blen + 1) & ~(blen - 1));
+                       rbuf[0] = buf[0];
+                       xlen = 1;
+                       rbuf = cbc_encrypt(cc, record_type,
+                               version, rbuf, &xlen);
+                       buf ++;
+                       len --;
+               } else {
+                       rbuf = buf - 5;
+               }
+       }
+
+       /*
+        * Compute MAC.
+        */
+       br_enc64be(tmp, cc->seq ++);
+       tmp[8] = record_type;
+       br_enc16be(tmp + 9, version);
+       br_enc16be(tmp + 11, len);
+       br_hmac_init(&hc, &cc->mac, cc->mac_len);
+       br_hmac_update(&hc, tmp, 13);
+       br_hmac_update(&hc, buf, len);
+       br_hmac_out(&hc, buf + len);
+       len += cc->mac_len;
+
+       /*
+        * Add padding.
+        */
+       plen = blen - (len & (blen - 1));
+       memset(buf + len, (unsigned)plen - 1, plen);
+       len += plen;
+
+       /*
+        * If an explicit IV is used, the corresponding extra block was
+        * already put in place earlier; we just have to account for it
+        * here.
+        */
+       if (cc->explicit_IV) {
+               buf -= blen;
+               len += blen;
+       }
+
+       /*
+        * Encrypt the whole thing. If there is an explicit IV, we also
+        * encrypt it, which is fine (encryption of a uniformly random
+        * block is still a uniformly random block).
+        */
+       cc->bc.vtable->run(&cc->bc.vtable, cc->iv, buf, len);
+
+       /*
+        * Add the header and return.
+        */
+       buf[-5] = record_type;
+       br_enc16be(buf - 4, version);
+       br_enc16be(buf - 2, len);
+       *data_len = (size_t)((buf + len) - rbuf);
+       return rbuf;
+}
+
+/* see bearssl_ssl.h */
+const br_sslrec_out_cbc_class br_sslrec_out_cbc_vtable = {
+       {
+               sizeof(br_sslrec_out_cbc_context),
+               (void (*)(const br_sslrec_out_class *const *,
+                       size_t *, size_t *))
+                       &cbc_max_plaintext,
+               (unsigned char *(*)(const br_sslrec_out_class **,
+                       int, unsigned, void *, size_t *))
+                       &cbc_encrypt
+       },
+       (void (*)(const br_sslrec_out_cbc_class **,
+               const br_block_cbcenc_class *, const void *, size_t,
+               const br_hash_class *, const void *, size_t, size_t,
+               const void *))
+               &out_cbc_init
+};
diff --git a/src/ssl/ssl_rec_gcm.c b/src/ssl/ssl_rec_gcm.c
new file mode 100644 (file)
index 0000000..cfbccec
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * GCM initialisation. This does everything except setting the vtable,
+ * which depends on whether this is a context for encrypting or for
+ * decrypting.
+ */
+static void
+gen_gcm_init(br_sslrec_gcm_context *cc,
+       const br_block_ctr_class *bc_impl,
+       const void *key, size_t key_len,
+       br_ghash gh_impl,
+       const void *iv)
+{
+       unsigned char tmp[12];
+
+       cc->seq = 0;
+       bc_impl->init(&cc->bc.vtable, key, key_len);
+       cc->gh = gh_impl;
+       memcpy(cc->iv, iv, sizeof cc->iv);
+       memset(cc->h, 0, sizeof cc->h);
+       memset(tmp, 0, sizeof tmp);
+       bc_impl->run(&cc->bc.vtable, tmp, 0, cc->h, sizeof cc->h);
+}
+
+static void
+in_gcm_init(br_sslrec_gcm_context *cc,
+       const br_block_ctr_class *bc_impl,
+       const void *key, size_t key_len,
+       br_ghash gh_impl,
+       const void *iv)
+{
+       cc->vtable.in = &br_sslrec_in_gcm_vtable;
+       gen_gcm_init(cc, bc_impl, key, key_len, gh_impl, iv);
+}
+
+static int
+gcm_check_length(const br_sslrec_gcm_context *cc, size_t rlen)
+{
+       /*
+        * GCM adds a fixed overhead:
+        *   8 bytes for the nonce_explicit (before the ciphertext)
+        *  16 bytes for the authentication tag (after the ciphertext)
+        */
+       (void)cc;
+       return rlen >= 24 && rlen <= (16384 + rlen);
+}
+
+/*
+ * Compute the authentication tag. The value written in 'tag' must still
+ * be CTR-encrypted.
+ */
+static void
+do_tag(br_sslrec_gcm_context *cc,
+       int record_type, unsigned version,
+       void *data, size_t len, void *tag)
+{
+       unsigned char header[13];
+       unsigned char footer[16];
+
+       /*
+        * Compute authentication tag. Three elements must be injected in
+        * sequence, each possibly 0-padded to reach a length multiple
+        * of the block size: the 13-byte header (sequence number, record
+        * type, protocol version, record length), the cipher text, and
+        * the word containing the encodings of the bit lengths of the two
+        * other elements.
+        */
+       br_enc64be(header, cc->seq ++);
+       header[8] = (unsigned char)record_type;
+       br_enc16be(header + 9, version);
+       br_enc16be(header + 11, len);
+       br_enc64be(footer, (uint64_t)(sizeof header) << 3);
+       br_enc64be(footer + 8, (uint64_t)len << 3);
+       memset(tag, 0, 16);
+       cc->gh(tag, cc->h, header, sizeof header);
+       cc->gh(tag, cc->h, data, len);
+       cc->gh(tag, cc->h, footer, sizeof footer);
+}
+
+/*
+ * Do CTR encryption. This also does CTR encryption of a single block at
+ * address 'xortag' with the counter value appropriate for the final
+ * processing of the authentication tag.
+ */
+static void
+do_ctr(br_sslrec_gcm_context *cc, const void *nonce, void *data, size_t len,
+       void *xortag)
+{
+       unsigned char iv[12];
+
+       memcpy(iv, cc->iv, 4);
+       memcpy(iv + 4, nonce, 8);
+       cc->bc.vtable->run(&cc->bc.vtable, iv, 2, data, len);
+       cc->bc.vtable->run(&cc->bc.vtable, iv, 1, xortag, 16);
+}
+
+static unsigned char *
+gcm_decrypt(br_sslrec_gcm_context *cc,
+       int record_type, unsigned version, void *data, size_t *data_len)
+{
+       unsigned char *buf;
+       size_t len, u;
+       uint32_t bad;
+       unsigned char tag[16];
+
+       buf = (unsigned char *)data + 8;
+       len = *data_len - 24;
+       do_tag(cc, record_type, version, buf, len, tag);
+       do_ctr(cc, data, buf, len, tag);
+
+       /*
+        * Compare the computed tag with the value from the record. It
+        * is possibly useless to do a constant-time comparison here,
+        * but it does not hurt.
+        */
+       bad = 0;
+       for (u = 0; u < 16; u ++) {
+               bad |= tag[u] ^ buf[len + u];
+       }
+       if (bad) {
+               return NULL;
+       }
+       *data_len = len;
+       return buf;
+}
+
+/* see bearssl_ssl.h */
+const br_sslrec_in_gcm_class br_sslrec_in_gcm_vtable = {
+       {
+               sizeof(br_sslrec_gcm_context),
+               (int (*)(const br_sslrec_in_class *const *, size_t))
+                       &gcm_check_length,
+               (unsigned char *(*)(const br_sslrec_in_class **,
+                       int, unsigned, void *, size_t *))
+                       &gcm_decrypt
+       },
+       (void (*)(const br_sslrec_in_gcm_class **,
+               const br_block_ctr_class *, const void *, size_t,
+               br_ghash, const void *))
+               &in_gcm_init
+};
+
+static void
+out_gcm_init(br_sslrec_gcm_context *cc,
+       const br_block_ctr_class *bc_impl,
+       const void *key, size_t key_len,
+       br_ghash gh_impl,
+       const void *iv)
+{
+       cc->vtable.out = &br_sslrec_out_gcm_vtable;
+       gen_gcm_init(cc, bc_impl, key, key_len, gh_impl, iv);
+}
+
+static void
+gcm_max_plaintext(const br_sslrec_gcm_context *cc,
+       size_t *start, size_t *end)
+{
+       size_t len;
+
+       (void)cc;
+       *start += 8;
+       len = *end - *start - 16;
+       if (len > 16384) {
+               len = 16384;
+       }
+       *end = *start + len;
+}
+
+static unsigned char *
+gcm_encrypt(br_sslrec_gcm_context *cc,
+       int record_type, unsigned version, void *data, size_t *data_len)
+{
+       unsigned char *buf;
+       size_t u, len;
+       unsigned char tmp[16];
+
+       buf = (unsigned char *)data;
+       len = *data_len;
+       memset(tmp, 0, sizeof tmp);
+       br_enc64be(buf - 8, cc->seq);
+       do_ctr(cc, buf - 8, buf, len, tmp);
+       do_tag(cc, record_type, version, buf, len, buf + len);
+       for (u = 0; u < 16; u ++) {
+               buf[len + u] ^= tmp[u];
+       }
+       len += 24;
+       buf -= 13;
+       buf[0] = (unsigned char)record_type;
+       br_enc16be(buf + 1, version);
+       br_enc16be(buf + 3, len);
+       *data_len = len + 5;
+       return buf;
+}
+
+/* see bearssl_ssl.h */
+const br_sslrec_out_gcm_class br_sslrec_out_gcm_vtable = {
+       {
+               sizeof(br_sslrec_gcm_context),
+               (void (*)(const br_sslrec_out_class *const *,
+                       size_t *, size_t *))
+                       &gcm_max_plaintext,
+               (unsigned char *(*)(const br_sslrec_out_class **,
+                       int, unsigned, void *, size_t *))
+                       &gcm_encrypt
+       },
+       (void (*)(const br_sslrec_out_gcm_class **,
+               const br_block_ctr_class *, const void *, size_t,
+               br_ghash, const void *))
+               &out_gcm_init
+};
diff --git a/src/ssl/ssl_server.c b/src/ssl/ssl_server.c
new file mode 100644 (file)
index 0000000..5578b63
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_zero(br_ssl_server_context *cc)
+{
+       /*
+        * For really standard C, we should explicitly set to NULL all
+        * pointers, and 0 all other fields. However, on all our target
+        * architectures, a direct memset() will work, be faster, and
+        * use a lot less code.
+        */
+       memset(cc, 0, sizeof *cc);
+}
+
+/* see bearssl_ssl.h */
+int
+br_ssl_server_reset(br_ssl_server_context *cc)
+{
+       br_ssl_engine_set_buffer(&cc->eng, NULL, 0, 0);
+       if (!br_ssl_engine_init_rand(&cc->eng)) {
+               return 0;
+       }
+       cc->eng.reneg = 0;
+       br_ssl_engine_hs_reset(&cc->eng,
+               br_ssl_hs_server_init_main, br_ssl_hs_server_run);
+       return br_ssl_engine_last_error(&cc->eng) == BR_ERR_OK;
+}
diff --git a/src/ssl/ssl_server_full_ec.c b/src/ssl/ssl_server_full_ec.c
new file mode 100644 (file)
index 0000000..eed4002
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_full_ec(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       unsigned cert_issuer_key_type, const br_ec_private_key *sk)
+{
+       /*
+        * The "full" profile supports all implemented cipher suites.
+        *
+        * Rationale for suite order, from most important to least
+        * important rule:
+        *
+        * -- Don't use 3DES if AES is available.
+        * -- Try to have Forward Secrecy (ECDHE suite) if possible.
+        * -- GCM is better than CBC.
+        * -- AES-128 is preferred over AES-256 (AES-128 is already
+        *    strong enough, and AES-256 is 40% more expensive).
+        *
+        * Note that for ECDH suites, the list will be automatically
+        * filtered based on the issuing CA key type.
+        */
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
+       };
+
+       /*
+        * All hash functions are activated.
+        * Note: the X.509 validation engine will nonetheless refuse to
+        * validate signatures that use MD5 as hash function.
+        */
+       static const br_hash_class *hashes[] = {
+               &br_md5_vtable,
+               &br_sha1_vtable,
+               &br_sha224_vtable,
+               &br_sha256_vtable,
+               &br_sha384_vtable,
+               &br_sha512_vtable
+       };
+
+       int id;
+
+       /*
+        * Reset server context and set supported versions from TLS-1.0
+        * to TLS-1.2 (inclusive).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12);
+
+       /*
+        * Set suites and elliptic curve implementation (for ECDHE).
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_ec(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
+               cert_issuer_key_type,
+               &br_ec_prime_i31, br_ecdsa_i31_sign_asn1);
+
+       /*
+        * Set supported hash functions.
+        */
+       for (id = br_md5_ID; id <= br_sha512_ID; id ++) {
+               const br_hash_class *hc;
+
+               hc = hashes[id - 1];
+               br_ssl_engine_set_hash(&cc->eng, id, hc);
+       }
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf);
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+       br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct64_cbcenc_vtable,
+               &br_aes_ct64_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct_cbcenc_vtable,
+               &br_aes_ct_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_ct_cbcenc_vtable,
+               &br_des_ct_cbcdec_vtable);
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_cbc(&cc->eng,
+               &br_sslrec_in_cbc_vtable,
+               &br_sslrec_out_cbc_vtable);
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_server_full_rsa.c b/src/ssl/ssl_server_full_rsa.c
new file mode 100644 (file)
index 0000000..21be99d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_full_rsa(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk)
+{
+       /*
+        * The "full" profile supports all implemented cipher suites.
+        *
+        * Rationale for suite order, from most important to least
+        * important rule:
+        *
+        * -- Don't use 3DES if AES is available.
+        * -- Try to have Forward Secrecy (ECDHE suite) if possible.
+        * -- GCM is better than CBC.
+        * -- AES-128 is preferred over AES-256 (AES-128 is already
+        *    strong enough, and AES-256 is 40% more expensive).
+        */
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
+               BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA,
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA,
+               BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+               BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA
+       };
+
+       /*
+        * All hash functions are activated.
+        * Note: the X.509 validation engine will nonetheless refuse to
+        * validate signatures that use MD5 as hash function.
+        */
+       static const br_hash_class *hashes[] = {
+               &br_md5_vtable,
+               &br_sha1_vtable,
+               &br_sha224_vtable,
+               &br_sha256_vtable,
+               &br_sha384_vtable,
+               &br_sha512_vtable
+       };
+
+       int id;
+
+       /*
+        * Reset server context and set supported versions from TLS-1.0
+        * to TLS-1.2 (inclusive).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12);
+
+       /*
+        * Set suites and elliptic curve implementation (for ECDHE).
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_rsa(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
+               br_rsa_i31_private, br_rsa_i31_pkcs1_sign);
+
+       /*
+        * Set supported hash functions.
+        */
+       for (id = br_md5_ID; id <= br_sha512_ID; id ++) {
+               const br_hash_class *hc;
+
+               hc = hashes[id - 1];
+               br_ssl_engine_set_hash(&cc->eng, id, hc);
+       }
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf);
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+       br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct64_cbcenc_vtable,
+               &br_aes_ct64_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_cbc(&cc->eng,
+               &br_aes_ct_cbcenc_vtable,
+               &br_aes_ct_cbcdec_vtable);
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+       br_ssl_engine_set_des_cbc(&cc->eng,
+               &br_des_ct_cbcenc_vtable,
+               &br_des_ct_cbcdec_vtable);
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_cbc(&cc->eng,
+               &br_sslrec_in_cbc_vtable,
+               &br_sslrec_out_cbc_vtable);
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_server_mine2g.c b/src/ssl/ssl_server_mine2g.c
new file mode 100644 (file)
index 0000000..5de566e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_mine2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk)
+{
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+       };
+
+       /*
+        * Reset server context and set supported versions to TLS-1.2 (only).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
+
+       /*
+        * Set suites and elliptic curve implementation (for ECDHE).
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_rsa(cc, chain, chain_len, sk,
+               BR_KEYTYPE_SIGN, 0, br_rsa_i31_pkcs1_sign);
+
+       /*
+        * Set supported hash functions.
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_server_minf2g.c b/src/ssl/ssl_server_minf2g.c
new file mode 100644 (file)
index 0000000..257aff2
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_minf2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk)
+{
+       static const uint16_t suites[] = {
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+       };
+
+       /*
+        * Reset server context and set supported versions to TLS-1.2 (only).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
+
+       /*
+        * Set suites and elliptic curve implementation (for ECDHE).
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+       br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_ec(cc, chain, chain_len, sk,
+               BR_KEYTYPE_SIGN, 0, &br_ec_prime_i31, br_ecdsa_i31_sign_asn1);
+
+       /*
+        * Set supported hash functions.
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_server_minr2g.c b/src/ssl/ssl_server_minr2g.c
new file mode 100644 (file)
index 0000000..05ad891
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_minr2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk)
+{
+       static const uint16_t suites[] = {
+               BR_TLS_RSA_WITH_AES_128_GCM_SHA256
+       };
+
+       /*
+        * Reset server context and set supported versions to TLS-1.2 (only).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
+
+       /*
+        * Set suites.
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_rsa(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX, br_rsa_i31_private, 0);
+
+       /*
+        * Set supported hash functions.
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_server_minu2g.c b/src/ssl/ssl_server_minu2g.c
new file mode 100644 (file)
index 0000000..e4b31ed
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_minu2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk)
+{
+       static const uint16_t suites[] = {
+               BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+       };
+
+       /*
+        * Reset server context and set supported versions to TLS-1.2 (only).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
+
+       /*
+        * Set suites.
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_ec(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX, BR_KEYTYPE_RSA, &br_ec_prime_i31, 0);
+
+       /*
+        * Set supported hash functions.
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_server_minv2g.c b/src/ssl/ssl_server_minv2g.c
new file mode 100644 (file)
index 0000000..cd29b8e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_init_minv2g(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk)
+{
+       static const uint16_t suites[] = {
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+       };
+
+       /*
+        * Reset server context and set supported versions to TLS-1.2 (only).
+        */
+       br_ssl_server_zero(cc);
+       br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
+
+       /*
+        * Set suites.
+        */
+       br_ssl_engine_set_suites(&cc->eng, suites,
+               (sizeof suites) / (sizeof suites[0]));
+
+       /*
+        * Set the "server policy": handler for the certificate chain
+        * and private key operations.
+        */
+       br_ssl_server_set_single_ec(cc, chain, chain_len, sk,
+               BR_KEYTYPE_KEYX, BR_KEYTYPE_EC, &br_ec_prime_i31, 0);
+
+       /*
+        * Set supported hash functions.
+        */
+       br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+
+       /*
+        * Set the PRF implementations.
+        */
+       br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+
+       /*
+        * Symmetric encryption. We use the "constant-time"
+        * implementations, which are the safest.
+        *
+        * On architectures detected as "64-bit", use the 64-bit
+        * versions (aes_ct64, ghash_ctmul64).
+        */
+#if BR_64
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct64_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul64);
+#else
+       br_ssl_engine_set_aes_ctr(&cc->eng,
+               &br_aes_ct_ctr_vtable);
+       br_ssl_engine_set_ghash(&cc->eng,
+               &br_ghash_ctmul);
+#endif
+
+       /*
+        * Set the SSL record engines (CBC, GCM).
+        */
+       br_ssl_engine_set_gcm(&cc->eng,
+               &br_sslrec_in_gcm_vtable,
+               &br_sslrec_out_gcm_vtable);
+}
diff --git a/src/ssl/ssl_single_ec.c b/src/ssl/ssl_single_ec.c
new file mode 100644 (file)
index 0000000..9dd0238
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static int
+se_choose(const br_ssl_server_policy_class **pctx,
+       const br_ssl_server_context *cc,
+       br_ssl_server_choices *choices)
+{
+       br_ssl_server_policy_ec_context *pc;
+       const br_suite_translated *st;
+       size_t u, st_num;
+       int hash_id;
+
+       pc = (br_ssl_server_policy_ec_context *)pctx;
+       st = br_ssl_server_get_client_suites(cc, &st_num);
+       hash_id = br_ssl_choose_hash(br_ssl_server_get_client_hashes(cc));
+       choices->chain = pc->chain;
+       choices->chain_len = pc->chain_len;
+       for (u = 0; u < st_num; u ++) {
+               unsigned tt;
+
+               tt = st[u][1];
+               switch (tt >> 12) {
+               case BR_SSLKEYX_ECDH_RSA:
+                       if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0
+                               && pc->cert_issuer_key_type == BR_KEYTYPE_RSA)
+                       {
+                               choices->cipher_suite = st[u][0];
+                               return 1;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDH_ECDSA:
+                       if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0
+                               && pc->cert_issuer_key_type == BR_KEYTYPE_EC)
+                       {
+                               choices->cipher_suite = st[u][0];
+                               return 1;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDHE_ECDSA:
+                       if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0
+                               && hash_id != 0)
+                       {
+                               choices->cipher_suite = st[u][0];
+                               choices->hash_id = hash_id;
+                               return 1;
+                       }
+                       break;
+               }
+       }
+       return 0;
+}
+
+static uint32_t
+se_do_keyx(const br_ssl_server_policy_class **pctx,
+       unsigned char *data, size_t len)
+{
+       br_ssl_server_policy_ec_context *pc;
+
+       pc = (br_ssl_server_policy_ec_context *)pctx;
+       return pc->iec->mul(data, len, pc->sk->x, pc->sk->xlen, pc->sk->curve);
+}
+
+static size_t
+se_do_sign(const br_ssl_server_policy_class **pctx,
+       int hash_id, size_t hv_len, unsigned char *data, size_t len)
+{
+       br_ssl_server_policy_ec_context *pc;
+       unsigned char hv[64];
+       const br_hash_class *hc;
+
+       pc = (br_ssl_server_policy_ec_context *)pctx;
+       hc = br_multihash_getimpl(pc->mhash, hash_id);
+       if (hc == NULL) {
+               return 0;
+       }
+       memcpy(hv, data, hv_len);
+       if (len < 139) {
+               return 0;
+       }
+       return pc->iecdsa(pc->iec, hc, hv, pc->sk, data);
+}
+
+static const br_ssl_server_policy_class se_policy_vtable = {
+       sizeof(br_ssl_server_policy_ec_context),
+       se_choose,
+       se_do_keyx,
+       se_do_sign
+};
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_set_single_ec(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_ec_private_key *sk, unsigned allowed_usages,
+       unsigned cert_issuer_key_type,
+       const br_ec_impl *iec, br_ecdsa_sign iecdsa)
+{
+       cc->chain_handler.single_ec.vtable = &se_policy_vtable;
+       cc->chain_handler.single_ec.chain = chain;
+       cc->chain_handler.single_ec.chain_len = chain_len;
+       cc->chain_handler.single_ec.sk = sk;
+       cc->chain_handler.single_ec.allowed_usages = allowed_usages;
+       cc->chain_handler.single_ec.cert_issuer_key_type = cert_issuer_key_type;
+       cc->chain_handler.single_ec.mhash = &cc->eng.mhash;
+       cc->chain_handler.single_ec.iec = iec;
+       cc->chain_handler.single_ec.iecdsa = iecdsa;
+       cc->policy_vtable = &cc->chain_handler.single_ec.vtable;
+}
diff --git a/src/ssl/ssl_single_rsa.c b/src/ssl/ssl_single_rsa.c
new file mode 100644 (file)
index 0000000..e174d91
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static int
+sr_choose(const br_ssl_server_policy_class **pctx,
+       const br_ssl_server_context *cc,
+       br_ssl_server_choices *choices)
+{
+       br_ssl_server_policy_rsa_context *pc;
+       const br_suite_translated *st;
+       size_t u, st_num;
+       int hash_id;
+
+       pc = (br_ssl_server_policy_rsa_context *)pctx;
+       st = br_ssl_server_get_client_suites(cc, &st_num);
+       hash_id = br_ssl_choose_hash(br_ssl_server_get_client_hashes(cc));
+       choices->chain = pc->chain;
+       choices->chain_len = pc->chain_len;
+       for (u = 0; u < st_num; u ++) {
+               unsigned tt;
+
+               tt = st[u][1];
+               switch (tt >> 12) {
+               case BR_SSLKEYX_RSA:
+                       if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0) {
+                               choices->cipher_suite = st[u][0];
+                               return 1;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDHE_RSA:
+                       if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0
+                               && hash_id != 0)
+                       {
+                               choices->cipher_suite = st[u][0];
+                               choices->hash_id = hash_id;
+                               return 1;
+                       }
+                       break;
+               }
+       }
+       return 0;
+}
+
+static uint32_t
+sr_do_keyx(const br_ssl_server_policy_class **pctx,
+       unsigned char *data, size_t len)
+{
+       br_ssl_server_policy_rsa_context *pc;
+
+       pc = (br_ssl_server_policy_rsa_context *)pctx;
+       return br_rsa_ssl_decrypt(pc->irsacore, pc->sk, data, len);
+}
+
+/*
+ * OID for hash functions in RSA signatures.
+ */
+static const unsigned char HASH_OID_SHA1[] = {
+       0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
+};
+
+static const unsigned char HASH_OID_SHA224[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
+};
+
+static const unsigned char HASH_OID_SHA256[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+static const unsigned char HASH_OID_SHA384[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+static const unsigned char HASH_OID_SHA512[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+static const unsigned char *HASH_OID[] = {
+       HASH_OID_SHA1,
+       HASH_OID_SHA224,
+       HASH_OID_SHA256,
+       HASH_OID_SHA384,
+       HASH_OID_SHA512
+};
+
+static size_t
+sr_do_sign(const br_ssl_server_policy_class **pctx,
+       int hash_id, size_t hv_len, unsigned char *data, size_t len)
+{
+       br_ssl_server_policy_rsa_context *pc;
+       unsigned char hv[64];
+       size_t sig_len;
+       const unsigned char *hash_oid;
+
+       pc = (br_ssl_server_policy_rsa_context *)pctx;
+       memcpy(hv, data, hv_len);
+       if (hash_id == 0) {
+               hash_oid = NULL;
+       } else if (hash_id >= 2 && hash_id <= 6) {
+               hash_oid = HASH_OID[hash_id - 2];
+       } else {
+               return 0;
+       }
+       sig_len = (pc->sk->n_bitlen + 7) >> 3;
+       if (len < sig_len) {
+               return 0;
+       }
+       return pc->irsasign(hash_oid, hv, hv_len, pc->sk, data) ? sig_len : 0;
+}
+
+static const br_ssl_server_policy_class sr_policy_vtable = {
+       sizeof(br_ssl_server_policy_rsa_context),
+       sr_choose,
+       sr_do_keyx,
+       sr_do_sign
+};
+
+/* see bearssl_ssl.h */
+void
+br_ssl_server_set_single_rsa(br_ssl_server_context *cc,
+       const br_x509_certificate *chain, size_t chain_len,
+       const br_rsa_private_key *sk, unsigned allowed_usages,
+       br_rsa_private irsacore, br_rsa_pkcs1_sign irsasign)
+{
+       cc->chain_handler.single_rsa.vtable = &sr_policy_vtable;
+       cc->chain_handler.single_rsa.chain = chain;
+       cc->chain_handler.single_rsa.chain_len = chain_len;
+       cc->chain_handler.single_rsa.sk = sk;
+       cc->chain_handler.single_rsa.allowed_usages = allowed_usages;
+       cc->chain_handler.single_rsa.irsacore = irsacore;
+       cc->chain_handler.single_rsa.irsasign = irsasign;
+       cc->policy_vtable = &cc->chain_handler.single_rsa.vtable;
+}
diff --git a/src/symcipher/aes_big_cbcdec.c b/src/symcipher/aes_big_cbcdec.c
new file mode 100644 (file)
index 0000000..d969a3b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_big_cbcdec_vtable;
+       ctx->num_rounds = br_aes_big_keysched_inv(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               unsigned char tmp[16];
+               int i;
+
+               memcpy(tmp, buf, 16);
+               br_aes_big_decrypt(ctx->num_rounds, ctx->skey, buf);
+               for (i = 0; i < 16; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               memcpy(ivbuf, tmp, 16);
+               buf += 16;
+               len -= 16;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcdec_class br_aes_big_cbcdec_vtable = {
+       sizeof(br_aes_big_cbcdec_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcdec_class **, const void *, size_t))
+               &br_aes_big_cbcdec_init,
+       (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t))
+               &br_aes_big_cbcdec_run
+};
diff --git a/src/symcipher/aes_big_cbcenc.c b/src/symcipher/aes_big_cbcenc.c
new file mode 100644 (file)
index 0000000..265e53b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_big_cbcenc_init(br_aes_big_cbcenc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_big_cbcenc_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_big_cbcenc_run(const br_aes_big_cbcenc_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               int i;
+
+               for (i = 0; i < 16; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               br_aes_big_encrypt(ctx->num_rounds, ctx->skey, buf);
+               memcpy(ivbuf, buf, 16);
+               buf += 16;
+               len -= 16;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcenc_class br_aes_big_cbcenc_vtable = {
+       sizeof(br_aes_big_cbcenc_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcenc_class **, const void *, size_t))
+               &br_aes_big_cbcenc_init,
+       (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t))
+               &br_aes_big_cbcenc_run
+};
diff --git a/src/symcipher/aes_big_ctr.c b/src/symcipher/aes_big_ctr.c
new file mode 100644 (file)
index 0000000..18fbb84
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_big_ctr_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+uint32_t
+br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len)
+{
+       unsigned char *buf;
+
+       buf = data;
+       while (len > 0) {
+               unsigned char tmp[16];
+
+               memcpy(tmp, iv, 12);
+               br_enc32be(tmp + 12, cc ++);
+               br_aes_big_encrypt(ctx->num_rounds, ctx->skey, tmp);
+               if (len <= 16) {
+                       xorbuf(buf, tmp, len);
+                       break;
+               }
+               xorbuf(buf, tmp, 16);
+               buf += 16;
+               len -= 16;
+       }
+       return cc;
+}
+
+/* see bearssl_block.h */
+const br_block_ctr_class br_aes_big_ctr_vtable = {
+       sizeof(br_aes_big_ctr_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctr_class **, const void *, size_t))
+               &br_aes_big_ctr_init,
+       (uint32_t (*)(const br_block_ctr_class *const *,
+               const void *, uint32_t, void *, size_t))
+               &br_aes_big_ctr_run
+};
diff --git a/src/symcipher/aes_big_dec.c b/src/symcipher/aes_big_dec.c
new file mode 100644 (file)
index 0000000..a5d0e3c
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Inverse S-box (used in key schedule for decryption).
+ */
+static const unsigned char iS[] = {
+       0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E,
+       0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
+       0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32,
+       0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+       0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49,
+       0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
+       0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50,
+       0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+       0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05,
+       0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
+       0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41,
+       0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+       0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8,
+       0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
+       0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B,
+       0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+       0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59,
+       0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
+       0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D,
+       0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+       0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63,
+       0x55, 0x21, 0x0C, 0x7D
+};
+
+static const uint32_t iSsm0[] = {
+       0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1,
+       0xACFA58AB, 0x4BE30393, 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25,
+       0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, 0xDEB15A49, 0x25BA1B67,
+       0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6,
+       0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3,
+       0x49E06929, 0x8EC9C844, 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD,
+       0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, 0x63DF4A18, 0xE51A3182,
+       0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94,
+       0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2,
+       0xE31F8F57, 0x6655AB2A, 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5,
+       0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, 0x8ACF1C2B, 0xA779B492,
+       0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A,
+       0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA,
+       0x5E719F06, 0xBD6E1051, 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46,
+       0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, 0x1998FB24, 0xD6BDE997,
+       0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB,
+       0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48,
+       0x1E1170AC, 0x6C5A724E, 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927,
+       0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, 0x0C0A67B1, 0x9357E70F,
+       0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16,
+       0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD,
+       0x2DB6A8B9, 0x141EA9C8, 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD,
+       0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, 0x8B432976, 0xCB23C6DC,
+       0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120,
+       0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3,
+       0x0D8652EC, 0x77C1E3D0, 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422,
+       0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, 0x87494EC7, 0xD938D1C1,
+       0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4,
+       0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8,
+       0x2E39F75E, 0x82C3AFF5, 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3,
+       0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, 0xCD267809, 0x6E5918F4,
+       0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6,
+       0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331,
+       0xC6A59430, 0x35A266C0, 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815,
+       0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, 0x764DD68D, 0x43EFB04D,
+       0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F,
+       0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252,
+       0xE9105633, 0x6DD64713, 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89,
+       0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, 0x9CD2DF59, 0x55F2733F,
+       0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86,
+       0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C,
+       0x283C498B, 0xFF0D9541, 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190,
+       0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742
+};
+
+static unsigned
+mul2(unsigned x)
+{
+       x <<= 1;
+       return x ^ ((unsigned)(-(int)(x >> 8)) & 0x11B);
+}
+
+static unsigned
+mul9(unsigned x)
+{
+       return x ^ mul2(mul2(mul2(x)));
+}
+
+static unsigned
+mulb(unsigned x)
+{
+       unsigned x2;
+       
+       x2 = mul2(x);
+       return x ^ x2 ^ mul2(mul2(x2));
+}
+
+static unsigned
+muld(unsigned x)
+{
+       unsigned x4;
+
+       x4 = mul2(mul2(x));
+       return x ^ x4 ^ mul2(x4);
+}
+
+static unsigned
+mule(unsigned x)
+{
+       unsigned x2, x4;
+
+       x2 = mul2(x);
+       x4 = mul2(x2);
+       return x2 ^ x4 ^ mul2(x4);
+}
+
+/* see inner.h */
+unsigned
+br_aes_big_keysched_inv(uint32_t *skey, const void *key, size_t key_len)
+{
+       unsigned num_rounds;
+       int i, m;
+
+       /*
+        * Sub-keys for decryption are distinct from encryption sub-keys
+        * in that InvMixColumns() is already applied for the inner
+        * rounds.
+        */
+       num_rounds = br_aes_keysched(skey, key, key_len);
+       m = (int)(num_rounds << 2);
+       for (i = 4; i < m; i ++) {
+               uint32_t p;
+               unsigned p0, p1, p2, p3;
+               uint32_t q0, q1, q2, q3;
+
+               p = skey[i];
+               p0 = p >> 24;
+               p1 = (p >> 16) & 0xFF;
+               p2 = (p >> 8) & 0xFF;
+               p3 = p & 0xFF;
+               q0 = mule(p0) ^ mulb(p1) ^ muld(p2) ^ mul9(p3);
+               q1 = mul9(p0) ^ mule(p1) ^ mulb(p2) ^ muld(p3);
+               q2 = muld(p0) ^ mul9(p1) ^ mule(p2) ^ mulb(p3);
+               q3 = mulb(p0) ^ muld(p1) ^ mul9(p2) ^ mule(p3);
+               skey[i] = (q0 << 24) | (q1 << 16) | (q2 << 8) | q3;
+       }
+       return num_rounds;
+}
+
+static inline uint32_t
+rotr(uint32_t x, int n)
+{
+       return (x << (32 - n)) | (x >> n);
+}
+
+#define iSboxExt0(x)   (iSsm0[x])
+#define iSboxExt1(x)   (rotr(iSsm0[x], 8))
+#define iSboxExt2(x)   (rotr(iSsm0[x], 16))
+#define iSboxExt3(x)   (rotr(iSsm0[x], 24))
+
+/* see bearssl.h */
+void
+br_aes_big_decrypt(unsigned num_rounds, const uint32_t *skey, void *data)
+{
+       unsigned char *buf;
+       uint32_t s0, s1, s2, s3;
+       uint32_t t0, t1, t2, t3;
+       unsigned u;
+
+       buf = data;
+       s0 = br_dec32be(buf);
+       s1 = br_dec32be(buf + 4);
+       s2 = br_dec32be(buf + 8);
+       s3 = br_dec32be(buf + 12);
+       s0 ^= skey[(num_rounds << 2) + 0];
+       s1 ^= skey[(num_rounds << 2) + 1];
+       s2 ^= skey[(num_rounds << 2) + 2];
+       s3 ^= skey[(num_rounds << 2) + 3];
+       for (u = num_rounds - 1; u > 0; u --) {
+               uint32_t v0 = iSboxExt0(s0 >> 24)
+                       ^ iSboxExt1((s3 >> 16) & 0xFF)
+                       ^ iSboxExt2((s2 >> 8) & 0xFF)
+                       ^ iSboxExt3(s1 & 0xFF);
+               uint32_t v1 = iSboxExt0(s1 >> 24)
+                       ^ iSboxExt1((s0 >> 16) & 0xFF)
+                       ^ iSboxExt2((s3 >> 8) & 0xFF)
+                       ^ iSboxExt3(s2 & 0xFF);
+               uint32_t v2 = iSboxExt0(s2 >> 24)
+                       ^ iSboxExt1((s1 >> 16) & 0xFF)
+                       ^ iSboxExt2((s0 >> 8) & 0xFF)
+                       ^ iSboxExt3(s3 & 0xFF);
+               uint32_t v3 = iSboxExt0(s3 >> 24)
+                       ^ iSboxExt1((s2 >> 16) & 0xFF)
+                       ^ iSboxExt2((s1 >> 8) & 0xFF)
+                       ^ iSboxExt3(s0 & 0xFF);
+               s0 = v0;
+               s1 = v1;
+               s2 = v2;
+               s3 = v3;
+               s0 ^= skey[u << 2];
+               s1 ^= skey[(u << 2) + 1];
+               s2 ^= skey[(u << 2) + 2];
+               s3 ^= skey[(u << 2) + 3];
+       }
+       t0 = ((uint32_t)iS[s0 >> 24] << 24)
+               | ((uint32_t)iS[(s3 >> 16) & 0xFF] << 16)
+               | ((uint32_t)iS[(s2 >> 8) & 0xFF] << 8)
+               | (uint32_t)iS[s1 & 0xFF];
+       t1 = ((uint32_t)iS[s1 >> 24] << 24)
+               | ((uint32_t)iS[(s0 >> 16) & 0xFF] << 16)
+               | ((uint32_t)iS[(s3 >> 8) & 0xFF] << 8)
+               | (uint32_t)iS[s2 & 0xFF];
+       t2 = ((uint32_t)iS[s2 >> 24] << 24)
+               | ((uint32_t)iS[(s1 >> 16) & 0xFF] << 16)
+               | ((uint32_t)iS[(s0 >> 8) & 0xFF] << 8)
+               | (uint32_t)iS[s3 & 0xFF];
+       t3 = ((uint32_t)iS[s3 >> 24] << 24)
+               | ((uint32_t)iS[(s2 >> 16) & 0xFF] << 16)
+               | ((uint32_t)iS[(s1 >> 8) & 0xFF] << 8)
+               | (uint32_t)iS[s0 & 0xFF];
+       s0 = t0 ^ skey[0];
+       s1 = t1 ^ skey[1];
+       s2 = t2 ^ skey[2];
+       s3 = t3 ^ skey[3];
+       br_enc32be(buf, s0);
+       br_enc32be(buf + 4, s1);
+       br_enc32be(buf + 8, s2);
+       br_enc32be(buf + 12, s3);
+}
diff --git a/src/symcipher/aes_big_enc.c b/src/symcipher/aes_big_enc.c
new file mode 100644 (file)
index 0000000..bbabb9a
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define S   br_aes_S
+
+static const uint32_t Ssm0[] = {
+       0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD,
+       0xDE6F6FB1, 0x91C5C554, 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D,
+       0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, 0x8FCACA45, 0x1F82829D,
+       0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B,
+       0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7,
+       0xE4727296, 0x9BC0C05B, 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A,
+       0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, 0x6834345C, 0x51A5A5F4,
+       0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F,
+       0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1,
+       0x0A05050F, 0x2F9A9AB5, 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D,
+       0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, 0x1209091B, 0x1D83839E,
+       0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB,
+       0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E,
+       0x5E2F2F71, 0x13848497, 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C,
+       0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, 0xD46A6ABE, 0x8DCBCB46,
+       0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A,
+       0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7,
+       0x66333355, 0x11858594, 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81,
+       0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, 0xA25151F3, 0x5DA3A3FE,
+       0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504,
+       0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A,
+       0xFDF3F30E, 0xBFD2D26D, 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F,
+       0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, 0x93C4C457, 0x55A7A7F2,
+       0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395,
+       0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E,
+       0x3B9090AB, 0x0B888883, 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C,
+       0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, 0xDBE0E03B, 0x64323256,
+       0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4,
+       0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4,
+       0xD3E4E437, 0xF279798B, 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7,
+       0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, 0xD86C6CB4, 0xAC5656FA,
+       0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818,
+       0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1,
+       0x73B4B4C7, 0x97C6C651, 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21,
+       0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, 0xE0707090, 0x7C3E3E42,
+       0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12,
+       0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158,
+       0x3A1D1D27, 0x279E9EB9, 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133,
+       0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, 0x2D9B9BB6, 0x3C1E1E22,
+       0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A,
+       0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631,
+       0x844242C6, 0xD06868B8, 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11,
+       0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A
+};
+
+static inline uint32_t
+rotr(uint32_t x, int n)
+{
+       return (x << (32 - n)) | (x >> n);
+}
+
+#define SboxExt0(x)   (Ssm0[x])
+#define SboxExt1(x)   (rotr(Ssm0[x], 8))
+#define SboxExt2(x)   (rotr(Ssm0[x], 16))
+#define SboxExt3(x)   (rotr(Ssm0[x], 24))
+
+
+/* see bearssl.h */
+void
+br_aes_big_encrypt(unsigned num_rounds, const uint32_t *skey, void *data)
+{
+       unsigned char *buf;
+       uint32_t s0, s1, s2, s3;
+       uint32_t t0, t1, t2, t3;
+       unsigned u;
+
+       buf = data;
+       s0 = br_dec32be(buf);
+       s1 = br_dec32be(buf + 4);
+       s2 = br_dec32be(buf + 8);
+       s3 = br_dec32be(buf + 12);
+       s0 ^= skey[0];
+       s1 ^= skey[1];
+       s2 ^= skey[2];
+       s3 ^= skey[3];
+       for (u = 1; u < num_rounds; u ++) {
+               uint32_t v0, v1, v2, v3;
+
+               v0 = SboxExt0(s0 >> 24)
+                       ^ SboxExt1((s1 >> 16) & 0xFF)
+                       ^ SboxExt2((s2 >> 8) & 0xFF)
+                       ^ SboxExt3(s3 & 0xFF);
+               v1 = SboxExt0(s1 >> 24)
+                       ^ SboxExt1((s2 >> 16) & 0xFF)
+                       ^ SboxExt2((s3 >> 8) & 0xFF)
+                       ^ SboxExt3(s0 & 0xFF);
+               v2 = SboxExt0(s2 >> 24)
+                       ^ SboxExt1((s3 >> 16) & 0xFF)
+                       ^ SboxExt2((s0 >> 8) & 0xFF)
+                       ^ SboxExt3(s1 & 0xFF);
+               v3 = SboxExt0(s3 >> 24)
+                       ^ SboxExt1((s0 >> 16) & 0xFF)
+                       ^ SboxExt2((s1 >> 8) & 0xFF)
+                       ^ SboxExt3(s2 & 0xFF);
+               s0 = v0;
+               s1 = v1;
+               s2 = v2;
+               s3 = v3;
+               s0 ^= skey[u << 2];
+               s1 ^= skey[(u << 2) + 1];
+               s2 ^= skey[(u << 2) + 2];
+               s3 ^= skey[(u << 2) + 3];
+       }
+       t0 = ((uint32_t)S[s0 >> 24] << 24)
+               | ((uint32_t)S[(s1 >> 16) & 0xFF] << 16)
+               | ((uint32_t)S[(s2 >> 8) & 0xFF] << 8)
+               | (uint32_t)S[s3 & 0xFF];
+       t1 = ((uint32_t)S[s1 >> 24] << 24)
+               | ((uint32_t)S[(s2 >> 16) & 0xFF] << 16)
+               | ((uint32_t)S[(s3 >> 8) & 0xFF] << 8)
+               | (uint32_t)S[s0 & 0xFF];
+       t2 = ((uint32_t)S[s2 >> 24] << 24)
+               | ((uint32_t)S[(s3 >> 16) & 0xFF] << 16)
+               | ((uint32_t)S[(s0 >> 8) & 0xFF] << 8)
+               | (uint32_t)S[s1 & 0xFF];
+       t3 = ((uint32_t)S[s3 >> 24] << 24)
+               | ((uint32_t)S[(s0 >> 16) & 0xFF] << 16)
+               | ((uint32_t)S[(s1 >> 8) & 0xFF] << 8)
+               | (uint32_t)S[s2 & 0xFF];
+       s0 = t0 ^ skey[num_rounds << 2];
+       s1 = t1 ^ skey[(num_rounds << 2) + 1];
+       s2 = t2 ^ skey[(num_rounds << 2) + 2];
+       s3 = t3 ^ skey[(num_rounds << 2) + 3];
+       br_enc32be(buf, s0);
+       br_enc32be(buf + 4, s1);
+       br_enc32be(buf + 8, s2);
+       br_enc32be(buf + 12, s3);
+}
diff --git a/src/symcipher/aes_common.c b/src/symcipher/aes_common.c
new file mode 100644 (file)
index 0000000..72c64fb
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static const uint32_t Rcon[] = {
+       0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
+       0x40000000, 0x80000000, 0x1B000000, 0x36000000
+};
+
+#define S   br_aes_S
+
+/* see inner.h */
+const unsigned char br_aes_S[] = {
+       0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
+       0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
+       0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
+       0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+       0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
+       0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
+       0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
+       0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+       0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
+       0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
+       0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
+       0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+       0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
+       0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
+       0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
+       0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+       0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
+       0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
+       0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
+       0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+       0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
+       0xB0, 0x54, 0xBB, 0x16
+};
+
+static uint32_t
+SubWord(uint32_t x)
+{
+       return ((uint32_t)S[x >> 24] << 24)
+               | ((uint32_t)S[(x >> 16) & 0xFF] << 16)
+               | ((uint32_t)S[(x >> 8) & 0xFF] << 8)
+               | (uint32_t)S[x & 0xFF];
+}
+
+/* see inner.h */
+unsigned
+br_aes_keysched(uint32_t *skey, const void *key, size_t key_len)
+{
+       unsigned num_rounds;
+       int i, j, k, nk, nkf;
+
+       switch (key_len) {
+       case 16:
+               num_rounds = 10;
+               break;
+       case 24:
+               num_rounds = 12;
+               break;
+       case 32:
+               num_rounds = 14;
+               break;
+       default:
+               /* abort(); */
+               return 0;
+       }
+       nk = (int)(key_len >> 2);
+       nkf = (int)((num_rounds + 1) << 2);
+       for (i = 0; i < nk; i ++) {
+               skey[i] = br_dec32be((const unsigned char *)key + (i << 2));
+       }
+       for (i = nk, j = 0, k = 0; i < nkf; i ++) {
+               uint32_t tmp;
+
+               tmp = skey[i - 1];
+               if (j == 0) {
+                       tmp = (tmp << 8) | (tmp >> 24);
+                       tmp = SubWord(tmp) ^ Rcon[k];
+               } else if (nk > 6 && j == 4) {
+                       tmp = SubWord(tmp);
+               }
+               skey[i] = skey[i - nk] ^ tmp;
+               if (++ j == nk) {
+                       j = 0;
+                       k ++;
+               }
+       }
+       return num_rounds;
+}
diff --git a/src/symcipher/aes_ct.c b/src/symcipher/aes_ct.c
new file mode 100644 (file)
index 0000000..66776d9
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_aes_ct_bitslice_Sbox(uint32_t *q)
+{
+       /*
+        * This S-box implementation is a straightforward translation of
+        * the circuit described by Boyar and Peralta in "A new
+        * combinational logic minimization technique with applications
+        * to cryptology" (https://eprint.iacr.org/2009/191.pdf).
+        *
+        * Note that variables x* (input) and s* (output) are numbered
+        * in "reverse" order (x0 is the high bit, x7 is the low bit).
+        */
+
+       uint32_t x0, x1, x2, x3, x4, x5, x6, x7;
+       uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9;
+       uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19;
+       uint32_t y20, y21;
+       uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9;
+       uint32_t z10, z11, z12, z13, z14, z15, z16, z17;
+       uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
+       uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19;
+       uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29;
+       uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39;
+       uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49;
+       uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59;
+       uint32_t t60, t61, t62, t63, t64, t65, t66, t67;
+       uint32_t s0, s1, s2, s3, s4, s5, s6, s7;
+
+       x0 = q[7];
+       x1 = q[6];
+       x2 = q[5];
+       x3 = q[4];
+       x4 = q[3];
+       x5 = q[2];
+       x6 = q[1];
+       x7 = q[0];
+
+       /*
+        * Top linear transformation.
+        */
+       y14 = x3 ^ x5;
+       y13 = x0 ^ x6;
+       y9 = x0 ^ x3;
+       y8 = x0 ^ x5;
+       t0 = x1 ^ x2;
+       y1 = t0 ^ x7;
+       y4 = y1 ^ x3;
+       y12 = y13 ^ y14;
+       y2 = y1 ^ x0;
+       y5 = y1 ^ x6;
+       y3 = y5 ^ y8;
+       t1 = x4 ^ y12;
+       y15 = t1 ^ x5;
+       y20 = t1 ^ x1;
+       y6 = y15 ^ x7;
+       y10 = y15 ^ t0;
+       y11 = y20 ^ y9;
+       y7 = x7 ^ y11;
+       y17 = y10 ^ y11;
+       y19 = y10 ^ y8;
+       y16 = t0 ^ y11;
+       y21 = y13 ^ y16;
+       y18 = x0 ^ y16;
+
+       /*
+        * Non-linear section.
+        */
+       t2 = y12 & y15;
+       t3 = y3 & y6;
+       t4 = t3 ^ t2;
+       t5 = y4 & x7;
+       t6 = t5 ^ t2;
+       t7 = y13 & y16;
+       t8 = y5 & y1;
+       t9 = t8 ^ t7;
+       t10 = y2 & y7;
+       t11 = t10 ^ t7;
+       t12 = y9 & y11;
+       t13 = y14 & y17;
+       t14 = t13 ^ t12;
+       t15 = y8 & y10;
+       t16 = t15 ^ t12;
+       t17 = t4 ^ t14;
+       t18 = t6 ^ t16;
+       t19 = t9 ^ t14;
+       t20 = t11 ^ t16;
+       t21 = t17 ^ y20;
+       t22 = t18 ^ y19;
+       t23 = t19 ^ y21;
+       t24 = t20 ^ y18;
+
+       t25 = t21 ^ t22;
+       t26 = t21 & t23;
+       t27 = t24 ^ t26;
+       t28 = t25 & t27;
+       t29 = t28 ^ t22;
+       t30 = t23 ^ t24;
+       t31 = t22 ^ t26;
+       t32 = t31 & t30;
+       t33 = t32 ^ t24;
+       t34 = t23 ^ t33;
+       t35 = t27 ^ t33;
+       t36 = t24 & t35;
+       t37 = t36 ^ t34;
+       t38 = t27 ^ t36;
+       t39 = t29 & t38;
+       t40 = t25 ^ t39;
+
+       t41 = t40 ^ t37;
+       t42 = t29 ^ t33;
+       t43 = t29 ^ t40;
+       t44 = t33 ^ t37;
+       t45 = t42 ^ t41;
+       z0 = t44 & y15;
+       z1 = t37 & y6;
+       z2 = t33 & x7;
+       z3 = t43 & y16;
+       z4 = t40 & y1;
+       z5 = t29 & y7;
+       z6 = t42 & y11;
+       z7 = t45 & y17;
+       z8 = t41 & y10;
+       z9 = t44 & y12;
+       z10 = t37 & y3;
+       z11 = t33 & y4;
+       z12 = t43 & y13;
+       z13 = t40 & y5;
+       z14 = t29 & y2;
+       z15 = t42 & y9;
+       z16 = t45 & y14;
+       z17 = t41 & y8;
+
+       /*
+        * Bottom linear transformation.
+        */
+       t46 = z15 ^ z16;
+       t47 = z10 ^ z11;
+       t48 = z5 ^ z13;
+       t49 = z9 ^ z10;
+       t50 = z2 ^ z12;
+       t51 = z2 ^ z5;
+       t52 = z7 ^ z8;
+       t53 = z0 ^ z3;
+       t54 = z6 ^ z7;
+       t55 = z16 ^ z17;
+       t56 = z12 ^ t48;
+       t57 = t50 ^ t53;
+       t58 = z4 ^ t46;
+       t59 = z3 ^ t54;
+       t60 = t46 ^ t57;
+       t61 = z14 ^ t57;
+       t62 = t52 ^ t58;
+       t63 = t49 ^ t58;
+       t64 = z4 ^ t59;
+       t65 = t61 ^ t62;
+       t66 = z1 ^ t63;
+       s0 = t59 ^ t63;
+       s6 = t56 ^ ~t62;
+       s7 = t48 ^ ~t60;
+       t67 = t64 ^ t65;
+       s3 = t53 ^ t66;
+       s4 = t51 ^ t66;
+       s5 = t47 ^ t65;
+       s1 = t64 ^ ~s3;
+       s2 = t55 ^ ~t67;
+
+       q[7] = s0;
+       q[6] = s1;
+       q[5] = s2;
+       q[4] = s3;
+       q[3] = s4;
+       q[2] = s5;
+       q[1] = s6;
+       q[0] = s7;
+}
+
+/* see inner.h */
+void
+br_aes_ct_ortho(uint32_t *q)
+{
+#define SWAPN(cl, ch, s, x, y)   do { \
+               uint32_t a, b; \
+               a = (x); \
+               b = (y); \
+               (x) = (a & (uint32_t)cl) | ((b & (uint32_t)cl) << (s)); \
+               (y) = ((a & (uint32_t)ch) >> (s)) | (b & (uint32_t)ch); \
+       } while (0)
+
+#define SWAP2(x, y)   SWAPN(0x55555555, 0xAAAAAAAA, 1, x, y)
+#define SWAP4(x, y)   SWAPN(0x33333333, 0xCCCCCCCC, 2, x, y)
+#define SWAP8(x, y)   SWAPN(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y)
+
+       SWAP2(q[0], q[1]);
+       SWAP2(q[2], q[3]);
+       SWAP2(q[4], q[5]);
+       SWAP2(q[6], q[7]);
+
+       SWAP4(q[0], q[2]);
+       SWAP4(q[1], q[3]);
+       SWAP4(q[4], q[6]);
+       SWAP4(q[5], q[7]);
+
+       SWAP8(q[0], q[4]);
+       SWAP8(q[1], q[5]);
+       SWAP8(q[2], q[6]);
+       SWAP8(q[3], q[7]);
+}
+
+static const unsigned char Rcon[] = {
+       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+};
+
+static uint32_t
+sub_word(uint32_t x)
+{
+       uint32_t q[8];
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               q[i] = x;
+       }
+       br_aes_ct_ortho(q);
+       br_aes_ct_bitslice_Sbox(q);
+       br_aes_ct_ortho(q);
+       return q[0];
+}
+
+/* see inner.h */
+unsigned
+br_aes_ct_keysched(uint32_t *comp_skey, const void *key, size_t key_len)
+{
+       unsigned num_rounds;
+       int i, j, k, nk, nkf;
+       uint32_t tmp;
+       uint32_t skey[120];
+
+       switch (key_len) {
+       case 16:
+               num_rounds = 10;
+               break;
+       case 24:
+               num_rounds = 12;
+               break;
+       case 32:
+               num_rounds = 14;
+               break;
+       default:
+               /* abort(); */
+               return 0;
+       }
+       nk = (int)(key_len >> 2);
+       nkf = (int)((num_rounds + 1) << 2);
+       tmp = 0;
+       for (i = 0; i < nk; i ++) {
+               tmp = br_dec32le((const unsigned char *)key + (i << 2));
+               skey[(i << 1) + 0] = tmp;
+               skey[(i << 1) + 1] = tmp;
+       }
+       for (i = nk, j = 0, k = 0; i < nkf; i ++) {
+               if (j == 0) {
+                       tmp = (tmp << 24) | (tmp >> 8);
+                       tmp = sub_word(tmp) ^ Rcon[k];
+               } else if (nk > 6 && j == 4) {
+                       tmp = sub_word(tmp);
+               }
+               tmp ^= skey[(i - nk) << 1];
+               skey[(i << 1) + 0] = tmp;
+               skey[(i << 1) + 1] = tmp;
+               if (++ j == nk) {
+                       j = 0;
+                       k ++;
+               }
+       }
+       for (i = 0; i < nkf; i += 4) {
+               br_aes_ct_ortho(skey + (i << 1));
+       }
+       for (i = 0, j = 0; i < nkf; i ++, j += 2) {
+               comp_skey[i] = (skey[j + 0] & 0x55555555)
+                       | (skey[j + 1] & 0xAAAAAAAA);
+       }
+       return num_rounds;
+}
+
+/* see inner.h */
+void
+br_aes_ct_skey_expand(uint32_t *skey,
+       unsigned num_rounds, const uint32_t *comp_skey)
+{
+       unsigned u, v, n;
+
+       n = (num_rounds + 1) << 2;
+       for (u = 0, v = 0; u < n; u ++, v += 2) {
+               uint32_t x, y;
+
+               x = y = comp_skey[u];
+               x &= 0x55555555;
+               skey[v + 0] = x | (x << 1);
+               y &= 0xAAAAAAAA;
+               skey[v + 1] = y | (y >> 1);
+       }
+}
diff --git a/src/symcipher/aes_ct64.c b/src/symcipher/aes_ct64.c
new file mode 100644 (file)
index 0000000..981e63d
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_aes_ct64_bitslice_Sbox(uint64_t *q)
+{
+       /*
+        * This S-box implementation is a straightforward translation of
+        * the circuit described by Boyar and Peralta in "A new
+        * combinational logic minimization technique with applications
+        * to cryptology" (https://eprint.iacr.org/2009/191.pdf).
+        *
+        * Note that variables x* (input) and s* (output) are numbered
+        * in "reverse" order (x0 is the high bit, x7 is the low bit).
+        */
+
+       uint64_t x0, x1, x2, x3, x4, x5, x6, x7;
+       uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9;
+       uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19;
+       uint64_t y20, y21;
+       uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9;
+       uint64_t z10, z11, z12, z13, z14, z15, z16, z17;
+       uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
+       uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19;
+       uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29;
+       uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39;
+       uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49;
+       uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59;
+       uint64_t t60, t61, t62, t63, t64, t65, t66, t67;
+       uint64_t s0, s1, s2, s3, s4, s5, s6, s7;
+
+       x0 = q[7];
+       x1 = q[6];
+       x2 = q[5];
+       x3 = q[4];
+       x4 = q[3];
+       x5 = q[2];
+       x6 = q[1];
+       x7 = q[0];
+
+       /*
+        * Top linear transformation.
+        */
+       y14 = x3 ^ x5;
+       y13 = x0 ^ x6;
+       y9 = x0 ^ x3;
+       y8 = x0 ^ x5;
+       t0 = x1 ^ x2;
+       y1 = t0 ^ x7;
+       y4 = y1 ^ x3;
+       y12 = y13 ^ y14;
+       y2 = y1 ^ x0;
+       y5 = y1 ^ x6;
+       y3 = y5 ^ y8;
+       t1 = x4 ^ y12;
+       y15 = t1 ^ x5;
+       y20 = t1 ^ x1;
+       y6 = y15 ^ x7;
+       y10 = y15 ^ t0;
+       y11 = y20 ^ y9;
+       y7 = x7 ^ y11;
+       y17 = y10 ^ y11;
+       y19 = y10 ^ y8;
+       y16 = t0 ^ y11;
+       y21 = y13 ^ y16;
+       y18 = x0 ^ y16;
+
+       /*
+        * Non-linear section.
+        */
+       t2 = y12 & y15;
+       t3 = y3 & y6;
+       t4 = t3 ^ t2;
+       t5 = y4 & x7;
+       t6 = t5 ^ t2;
+       t7 = y13 & y16;
+       t8 = y5 & y1;
+       t9 = t8 ^ t7;
+       t10 = y2 & y7;
+       t11 = t10 ^ t7;
+       t12 = y9 & y11;
+       t13 = y14 & y17;
+       t14 = t13 ^ t12;
+       t15 = y8 & y10;
+       t16 = t15 ^ t12;
+       t17 = t4 ^ t14;
+       t18 = t6 ^ t16;
+       t19 = t9 ^ t14;
+       t20 = t11 ^ t16;
+       t21 = t17 ^ y20;
+       t22 = t18 ^ y19;
+       t23 = t19 ^ y21;
+       t24 = t20 ^ y18;
+
+       t25 = t21 ^ t22;
+       t26 = t21 & t23;
+       t27 = t24 ^ t26;
+       t28 = t25 & t27;
+       t29 = t28 ^ t22;
+       t30 = t23 ^ t24;
+       t31 = t22 ^ t26;
+       t32 = t31 & t30;
+       t33 = t32 ^ t24;
+       t34 = t23 ^ t33;
+       t35 = t27 ^ t33;
+       t36 = t24 & t35;
+       t37 = t36 ^ t34;
+       t38 = t27 ^ t36;
+       t39 = t29 & t38;
+       t40 = t25 ^ t39;
+
+       t41 = t40 ^ t37;
+       t42 = t29 ^ t33;
+       t43 = t29 ^ t40;
+       t44 = t33 ^ t37;
+       t45 = t42 ^ t41;
+       z0 = t44 & y15;
+       z1 = t37 & y6;
+       z2 = t33 & x7;
+       z3 = t43 & y16;
+       z4 = t40 & y1;
+       z5 = t29 & y7;
+       z6 = t42 & y11;
+       z7 = t45 & y17;
+       z8 = t41 & y10;
+       z9 = t44 & y12;
+       z10 = t37 & y3;
+       z11 = t33 & y4;
+       z12 = t43 & y13;
+       z13 = t40 & y5;
+       z14 = t29 & y2;
+       z15 = t42 & y9;
+       z16 = t45 & y14;
+       z17 = t41 & y8;
+
+       /*
+        * Bottom linear transformation.
+        */
+       t46 = z15 ^ z16;
+       t47 = z10 ^ z11;
+       t48 = z5 ^ z13;
+       t49 = z9 ^ z10;
+       t50 = z2 ^ z12;
+       t51 = z2 ^ z5;
+       t52 = z7 ^ z8;
+       t53 = z0 ^ z3;
+       t54 = z6 ^ z7;
+       t55 = z16 ^ z17;
+       t56 = z12 ^ t48;
+       t57 = t50 ^ t53;
+       t58 = z4 ^ t46;
+       t59 = z3 ^ t54;
+       t60 = t46 ^ t57;
+       t61 = z14 ^ t57;
+       t62 = t52 ^ t58;
+       t63 = t49 ^ t58;
+       t64 = z4 ^ t59;
+       t65 = t61 ^ t62;
+       t66 = z1 ^ t63;
+       s0 = t59 ^ t63;
+       s6 = t56 ^ ~t62;
+       s7 = t48 ^ ~t60;
+       t67 = t64 ^ t65;
+       s3 = t53 ^ t66;
+       s4 = t51 ^ t66;
+       s5 = t47 ^ t65;
+       s1 = t64 ^ ~s3;
+       s2 = t55 ^ ~t67;
+
+       q[7] = s0;
+       q[6] = s1;
+       q[5] = s2;
+       q[4] = s3;
+       q[3] = s4;
+       q[2] = s5;
+       q[1] = s6;
+       q[0] = s7;
+}
+
+/* see inner.h */
+void
+br_aes_ct64_ortho(uint64_t *q)
+{
+#define SWAPN(cl, ch, s, x, y)   do { \
+               uint64_t a, b; \
+               a = (x); \
+               b = (y); \
+               (x) = (a & (uint64_t)cl) | ((b & (uint64_t)cl) << (s)); \
+               (y) = ((a & (uint64_t)ch) >> (s)) | (b & (uint64_t)ch); \
+       } while (0)
+
+#define SWAP2(x, y)    SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA,  1, x, y)
+#define SWAP4(x, y)    SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC,  2, x, y)
+#define SWAP8(x, y)    SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0,  4, x, y)
+
+       SWAP2(q[0], q[1]);
+       SWAP2(q[2], q[3]);
+       SWAP2(q[4], q[5]);
+       SWAP2(q[6], q[7]);
+
+       SWAP4(q[0], q[2]);
+       SWAP4(q[1], q[3]);
+       SWAP4(q[4], q[6]);
+       SWAP4(q[5], q[7]);
+
+       SWAP8(q[0], q[4]);
+       SWAP8(q[1], q[5]);
+       SWAP8(q[2], q[6]);
+       SWAP8(q[3], q[7]);
+}
+
+/* see inner.h */
+void
+br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w)
+{
+       uint64_t x0, x1, x2, x3;
+
+       x0 = w[0];
+       x1 = w[1];
+       x2 = w[2];
+       x3 = w[3];
+       x0 |= (x0 << 16);
+       x1 |= (x1 << 16);
+       x2 |= (x2 << 16);
+       x3 |= (x3 << 16);
+       x0 &= (uint64_t)0x0000FFFF0000FFFF;
+       x1 &= (uint64_t)0x0000FFFF0000FFFF;
+       x2 &= (uint64_t)0x0000FFFF0000FFFF;
+       x3 &= (uint64_t)0x0000FFFF0000FFFF;
+       x0 |= (x0 << 8);
+       x1 |= (x1 << 8);
+       x2 |= (x2 << 8);
+       x3 |= (x3 << 8);
+       x0 &= (uint64_t)0x00FF00FF00FF00FF;
+       x1 &= (uint64_t)0x00FF00FF00FF00FF;
+       x2 &= (uint64_t)0x00FF00FF00FF00FF;
+       x3 &= (uint64_t)0x00FF00FF00FF00FF;
+       *q0 = x0 | (x2 << 8);
+       *q1 = x1 | (x3 << 8);
+}
+
+/* see inner.h */
+void
+br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1)
+{
+       uint64_t x0, x1, x2, x3;
+
+       x0 = q0 & (uint64_t)0x00FF00FF00FF00FF;
+       x1 = q1 & (uint64_t)0x00FF00FF00FF00FF;
+       x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF;
+       x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF;
+       x0 |= (x0 >> 8);
+       x1 |= (x1 >> 8);
+       x2 |= (x2 >> 8);
+       x3 |= (x3 >> 8);
+       x0 &= (uint64_t)0x0000FFFF0000FFFF;
+       x1 &= (uint64_t)0x0000FFFF0000FFFF;
+       x2 &= (uint64_t)0x0000FFFF0000FFFF;
+       x3 &= (uint64_t)0x0000FFFF0000FFFF;
+       w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16);
+       w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16);
+       w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16);
+       w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16);
+}
+
+static const unsigned char Rcon[] = {
+       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+};
+
+static uint32_t
+sub_word(uint32_t x)
+{
+       uint64_t q[8];
+
+       memset(q, 0, sizeof q);
+       q[0] = x;
+       br_aes_ct64_ortho(q);
+       br_aes_ct64_bitslice_Sbox(q);
+       br_aes_ct64_ortho(q);
+       return (uint32_t)q[0];
+}
+
+/* see inner.h */
+unsigned
+br_aes_ct64_keysched(uint64_t *comp_skey, const void *key, size_t key_len)
+{
+       unsigned num_rounds;
+       int i, j, k, nk, nkf;
+       uint32_t tmp;
+       uint32_t skey[60];
+
+       switch (key_len) {
+       case 16:
+               num_rounds = 10;
+               break;
+       case 24:
+               num_rounds = 12;
+               break;
+       case 32:
+               num_rounds = 14;
+               break;
+       default:
+               /* abort(); */
+               return 0;
+       }
+       nk = (int)(key_len >> 2);
+       nkf = (int)((num_rounds + 1) << 2);
+       br_range_dec32le(skey, (key_len >> 2), key);
+       tmp = skey[(key_len >> 2) - 1];
+       for (i = nk, j = 0, k = 0; i < nkf; i ++) {
+               if (j == 0) {
+                       tmp = (tmp << 24) | (tmp >> 8);
+                       tmp = sub_word(tmp) ^ Rcon[k];
+               } else if (nk > 6 && j == 4) {
+                       tmp = sub_word(tmp);
+               }
+               tmp ^= skey[i - nk];
+               skey[i] = tmp;
+               if (++ j == nk) {
+                       j = 0;
+                       k ++;
+               }
+       }
+
+       for (i = 0, j = 0; i < nkf; i += 4, j += 2) {
+               uint64_t q[8];
+
+               br_aes_ct64_interleave_in(&q[0], &q[4], skey + i);
+               q[1] = q[0];
+               q[2] = q[0];
+               q[3] = q[0];
+               q[5] = q[4];
+               q[6] = q[4];
+               q[7] = q[4];
+               br_aes_ct64_ortho(q);
+               comp_skey[j + 0] =
+                         (q[0] & (uint64_t)0x1111111111111111)
+                       | (q[1] & (uint64_t)0x2222222222222222)
+                       | (q[2] & (uint64_t)0x4444444444444444)
+                       | (q[3] & (uint64_t)0x8888888888888888);
+               comp_skey[j + 1] =
+                         (q[4] & (uint64_t)0x1111111111111111)
+                       | (q[5] & (uint64_t)0x2222222222222222)
+                       | (q[6] & (uint64_t)0x4444444444444444)
+                       | (q[7] & (uint64_t)0x8888888888888888);
+       }
+       return num_rounds;
+}
+
+/* see inner.h */
+void
+br_aes_ct64_skey_expand(uint64_t *skey,
+       unsigned num_rounds, const uint64_t *comp_skey)
+{
+       unsigned u, v, n;
+
+       n = (num_rounds + 1) << 2;
+       for (u = 0, v = 0; u < n; u ++, v += 4) {
+               uint64_t x0, x1, x2, x3;
+
+               x0 = x1 = x2 = x3 = comp_skey[u];
+               x0 &= (uint64_t)0x1111111111111111;
+               x1 &= (uint64_t)0x2222222222222222;
+               x2 &= (uint64_t)0x4444444444444444;
+               x3 &= (uint64_t)0x8888888888888888;
+               x1 >>= 1;
+               x2 >>= 2;
+               x3 >>= 3;
+               skey[v + 0] = (x0 << 4) - x0;
+               skey[v + 1] = (x1 << 4) - x1;
+               skey[v + 2] = (x2 << 4) - x2;
+               skey[v + 3] = (x3 << 4) - x3;
+       }
+}
diff --git a/src/symcipher/aes_ct64_cbcdec.c b/src/symcipher/aes_ct64_cbcdec.c
new file mode 100644 (file)
index 0000000..814dce7
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct64_cbcdec_vtable;
+       ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf;
+       uint64_t sk_exp[240];
+       uint32_t ivw[4];
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       br_range_dec32le(ivw, 4, iv);
+       buf = data;
+       while (len > 0) {
+               uint64_t q[8];
+               uint32_t w1[16], w2[16];
+               int i;
+
+               if (len >= 64) {
+                       br_range_dec32le(w1, 16, buf);
+               } else {
+                       br_range_dec32le(w1, len >> 2, buf);
+               }
+               for (i = 0; i < 4; i ++) {
+                       br_aes_ct64_interleave_in(
+                               &q[i], &q[i + 4], w1 + (i << 2));
+               }
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_decrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               for (i = 0; i < 4; i ++) {
+                       br_aes_ct64_interleave_out(
+                               w2 + (i << 2), q[i], q[i + 4]);
+               }
+               for (i = 0; i < 4; i ++) {
+                       w2[i] ^= ivw[i];
+               }
+               if (len >= 64) {
+                       for (i = 4; i < 16; i ++) {
+                               w2[i] ^= w1[i - 4];
+                       }
+                       memcpy(ivw, w1 + 12, sizeof ivw);
+                       br_range_enc32le(buf, w2, 16);
+               } else {
+                       int j;
+
+                       j = (int)(len >> 2);
+                       for (i = 4; i < j; i ++) {
+                               w2[i] ^= w1[i - 4];
+                       }
+                       memcpy(ivw, w1 + j - 4, sizeof ivw);
+                       br_range_enc32le(buf, w2, j);
+                       break;
+               }
+               buf += 64;
+               len -= 64;
+       }
+       br_range_enc32le(iv, ivw, 4);
+}
+
+/* see bearssl_block.h */
+const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable = {
+       sizeof(br_aes_ct64_cbcdec_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcdec_class **, const void *, size_t))
+               &br_aes_ct64_cbcdec_init,
+       (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t))
+               &br_aes_ct64_cbcdec_run
+};
diff --git a/src/symcipher/aes_ct64_cbcenc.c b/src/symcipher/aes_ct64_cbcenc.c
new file mode 100644 (file)
index 0000000..e320614
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_cbcenc_init(br_aes_ct64_cbcenc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct64_cbcenc_vtable;
+       ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_cbcenc_run(const br_aes_ct64_cbcenc_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf;
+       uint64_t sk_exp[240];
+       uint32_t ivw[4];
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       br_range_dec32le(ivw, 4, iv);
+       buf = data;
+       while (len > 0) {
+               uint32_t w[4];
+               uint64_t q[8];
+
+               w[0] = ivw[0] ^ br_dec32le(buf);
+               w[1] = ivw[1] ^ br_dec32le(buf + 4);
+               w[2] = ivw[2] ^ br_dec32le(buf + 8);
+               w[3] = ivw[3] ^ br_dec32le(buf + 12);
+               br_aes_ct64_interleave_in(&q[0], &q[4], w);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_interleave_out(w, q[0], q[4]);
+               memcpy(ivw, w, sizeof w);
+               br_enc32le(buf, w[0]);
+               br_enc32le(buf + 4, w[1]);
+               br_enc32le(buf + 8, w[2]);
+               br_enc32le(buf + 12, w[3]);
+               buf += 16;
+               len -= 16;
+       }
+       br_range_enc32le(iv, ivw, 4);
+}
+
+/* see bearssl_block.h */
+const br_block_cbcenc_class br_aes_ct64_cbcenc_vtable = {
+       sizeof(br_aes_ct64_cbcenc_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcenc_class **, const void *, size_t))
+               &br_aes_ct64_cbcenc_init,
+       (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t))
+               &br_aes_ct64_cbcenc_run
+};
diff --git a/src/symcipher/aes_ct64_ctr.c b/src/symcipher/aes_ct64_ctr.c
new file mode 100644 (file)
index 0000000..6d5a566
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct64_ctr_vtable;
+       ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+uint32_t
+br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len)
+{
+       unsigned char *buf;
+       uint32_t ivw[16];
+       uint64_t sk_exp[240];
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       br_range_dec32le(ivw, 3, iv);
+       memcpy(ivw + 4, ivw, 3 * sizeof(uint32_t));
+       memcpy(ivw + 8, ivw, 3 * sizeof(uint32_t));
+       memcpy(ivw + 12, ivw, 3 * sizeof(uint32_t));
+       buf = data;
+       while (len > 0) {
+               uint64_t q[8];
+               uint32_t w[16];
+               unsigned char tmp[64];
+               int i;
+
+               /*
+                * TODO: see if we can save on the first br_aes_ct64_ortho()
+                * call, since iv0/iv1/iv2 are constant for the whole run.
+                */
+               memcpy(w, ivw, sizeof ivw);
+               w[3] = br_swap32(cc);
+               w[7] = br_swap32(cc + 1);
+               w[11] = br_swap32(cc + 2);
+               w[15] = br_swap32(cc + 3);
+               for (i = 0; i < 4; i ++) {
+                       br_aes_ct64_interleave_in(
+                               &q[i], &q[i + 4], w + (i << 2));
+               }
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               for (i = 0; i < 4; i ++) {
+                       br_aes_ct64_interleave_out(
+                               w + (i << 2), q[i], q[i + 4]);
+               }
+               br_range_enc32le(tmp, w, 16);
+               if (len <= 64) {
+                       xorbuf(buf, tmp, len);
+                       cc += (uint32_t)len >> 4;
+                       break;
+               }
+               xorbuf(buf, tmp, 64);
+               buf += 64;
+               len -= 64;
+               cc += 4;
+       }
+       return cc;
+}
+
+/* see bearssl_block.h */
+const br_block_ctr_class br_aes_ct64_ctr_vtable = {
+       sizeof(br_aes_ct64_ctr_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctr_class **, const void *, size_t))
+               &br_aes_ct64_ctr_init,
+       (uint32_t (*)(const br_block_ctr_class *const *,
+               const void *, uint32_t, void *, size_t))
+               &br_aes_ct64_ctr_run
+};
diff --git a/src/symcipher/aes_ct64_dec.c b/src/symcipher/aes_ct64_dec.c
new file mode 100644 (file)
index 0000000..ab00e09
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_aes_ct64_bitslice_invSbox(uint64_t *q)
+{
+       /*
+        * See br_aes_ct_bitslice_invSbox(). This is the natural extension
+        * to 64-bit registers.
+        */
+       uint64_t q0, q1, q2, q3, q4, q5, q6, q7;
+
+       q0 = ~q[0];
+       q1 = ~q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = ~q[5];
+       q6 = ~q[6];
+       q7 = q[7];
+       q[7] = q1 ^ q4 ^ q6;
+       q[6] = q0 ^ q3 ^ q5;
+       q[5] = q7 ^ q2 ^ q4;
+       q[4] = q6 ^ q1 ^ q3;
+       q[3] = q5 ^ q0 ^ q2;
+       q[2] = q4 ^ q7 ^ q1;
+       q[1] = q3 ^ q6 ^ q0;
+       q[0] = q2 ^ q5 ^ q7;
+
+       br_aes_ct64_bitslice_Sbox(q);
+
+       q0 = ~q[0];
+       q1 = ~q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = ~q[5];
+       q6 = ~q[6];
+       q7 = q[7];
+       q[7] = q1 ^ q4 ^ q6;
+       q[6] = q0 ^ q3 ^ q5;
+       q[5] = q7 ^ q2 ^ q4;
+       q[4] = q6 ^ q1 ^ q3;
+       q[3] = q5 ^ q0 ^ q2;
+       q[2] = q4 ^ q7 ^ q1;
+       q[1] = q3 ^ q6 ^ q0;
+       q[0] = q2 ^ q5 ^ q7;
+}
+
+static void
+add_round_key(uint64_t *q, const uint64_t *sk)
+{
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               q[i] ^= sk[i];
+       }
+}
+
+static void
+inv_shift_rows(uint64_t *q)
+{
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               uint64_t x;
+
+               x = q[i];
+               q[i] = (x & (uint64_t)0x000000000000FFFF)
+                       | ((x & (uint64_t)0x000000000FFF0000) << 4)
+                       | ((x & (uint64_t)0x00000000F0000000) >> 12)
+                       | ((x & (uint64_t)0x000000FF00000000) << 8)
+                       | ((x & (uint64_t)0x0000FF0000000000) >> 8)
+                       | ((x & (uint64_t)0x000F000000000000) << 12)
+                       | ((x & (uint64_t)0xFFF0000000000000) >> 4);
+       }
+}
+
+static inline uint64_t
+rotr32(uint64_t x)
+{
+       return (x << 32) | (x >> 32);
+}
+
+static void
+inv_mix_columns(uint64_t *q)
+{
+       uint64_t q0, q1, q2, q3, q4, q5, q6, q7;
+       uint64_t r0, r1, r2, r3, r4, r5, r6, r7;
+
+       q0 = q[0];
+       q1 = q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = q[5];
+       q6 = q[6];
+       q7 = q[7];
+       r0 = (q0 >> 16) | (q0 << 48);
+       r1 = (q1 >> 16) | (q1 << 48);
+       r2 = (q2 >> 16) | (q2 << 48);
+       r3 = (q3 >> 16) | (q3 << 48);
+       r4 = (q4 >> 16) | (q4 << 48);
+       r5 = (q5 >> 16) | (q5 << 48);
+       r6 = (q6 >> 16) | (q6 << 48);
+       r7 = (q7 >> 16) | (q7 << 48);
+
+       q[0] = q5 ^ q6 ^ q7 ^ r0 ^ r5 ^ r7 ^ rotr32(q0 ^ q5 ^ q6 ^ r0 ^ r5);
+       q[1] = q0 ^ q5 ^ r0 ^ r1 ^ r5 ^ r6 ^ r7 ^ rotr32(q1 ^ q5 ^ q7 ^ r1 ^ r5 ^ r6);
+       q[2] = q0 ^ q1 ^ q6 ^ r1 ^ r2 ^ r6 ^ r7 ^ rotr32(q0 ^ q2 ^ q6 ^ r2 ^ r6 ^ r7);
+       q[3] = q0 ^ q1 ^ q2 ^ q5 ^ q6 ^ r0 ^ r2 ^ r3 ^ r5 ^ rotr32(q0 ^ q1 ^ q3 ^ q5 ^ q6 ^ q7 ^ r0 ^ r3 ^ r5 ^ r7);
+       q[4] = q1 ^ q2 ^ q3 ^ q5 ^ r1 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr32(q1 ^ q2 ^ q4 ^ q5 ^ q7 ^ r1 ^ r4 ^ r5 ^ r6);
+       q[5] = q2 ^ q3 ^ q4 ^ q6 ^ r2 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr32(q2 ^ q3 ^ q5 ^ q6 ^ r2 ^ r5 ^ r6 ^ r7);
+       q[6] = q3 ^ q4 ^ q5 ^ q7 ^ r3 ^ r5 ^ r6 ^ r7 ^ rotr32(q3 ^ q4 ^ q6 ^ q7 ^ r3 ^ r6 ^ r7);
+       q[7] = q4 ^ q5 ^ q6 ^ r4 ^ r6 ^ r7 ^ rotr32(q4 ^ q5 ^ q7 ^ r4 ^ r7);
+}
+
+/* see inner.h */
+void
+br_aes_ct64_bitslice_decrypt(unsigned num_rounds,
+       const uint64_t *skey, uint64_t *q)
+{
+       unsigned u;
+
+       add_round_key(q, skey + (num_rounds << 3));
+       for (u = num_rounds - 1; u > 0; u --) {
+               inv_shift_rows(q);
+               br_aes_ct64_bitslice_invSbox(q);
+               add_round_key(q, skey + (u << 3));
+               inv_mix_columns(q);
+       }
+       inv_shift_rows(q);
+       br_aes_ct64_bitslice_invSbox(q);
+       add_round_key(q, skey);
+}
diff --git a/src/symcipher/aes_ct64_enc.c b/src/symcipher/aes_ct64_enc.c
new file mode 100644 (file)
index 0000000..78631ce
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static inline void
+add_round_key(uint64_t *q, const uint64_t *sk)
+{
+       q[0] ^= sk[0];
+       q[1] ^= sk[1];
+       q[2] ^= sk[2];
+       q[3] ^= sk[3];
+       q[4] ^= sk[4];
+       q[5] ^= sk[5];
+       q[6] ^= sk[6];
+       q[7] ^= sk[7];
+}
+
+static inline void
+shift_rows(uint64_t *q)
+{
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               uint64_t x;
+
+               x = q[i];
+               q[i] = (x & (uint64_t)0x000000000000FFFF)
+                       | ((x & (uint64_t)0x00000000FFF00000) >> 4)
+                       | ((x & (uint64_t)0x00000000000F0000) << 12)
+                       | ((x & (uint64_t)0x0000FF0000000000) >> 8)
+                       | ((x & (uint64_t)0x000000FF00000000) << 8)
+                       | ((x & (uint64_t)0xF000000000000000) >> 12)
+                       | ((x & (uint64_t)0x0FFF000000000000) << 4);
+       }
+}
+
+static inline uint64_t
+rotr32(uint64_t x)
+{
+       return (x << 32) | (x >> 32);
+}
+
+static inline void
+mix_columns(uint64_t *q)
+{
+       uint64_t q0, q1, q2, q3, q4, q5, q6, q7;
+       uint64_t r0, r1, r2, r3, r4, r5, r6, r7;
+
+       q0 = q[0];
+       q1 = q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = q[5];
+       q6 = q[6];
+       q7 = q[7];
+       r0 = (q0 >> 16) | (q0 << 48);
+       r1 = (q1 >> 16) | (q1 << 48);
+       r2 = (q2 >> 16) | (q2 << 48);
+       r3 = (q3 >> 16) | (q3 << 48);
+       r4 = (q4 >> 16) | (q4 << 48);
+       r5 = (q5 >> 16) | (q5 << 48);
+       r6 = (q6 >> 16) | (q6 << 48);
+       r7 = (q7 >> 16) | (q7 << 48);
+
+       q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0);
+       q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1);
+       q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2);
+       q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3);
+       q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4);
+       q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5);
+       q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6);
+       q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7);
+}
+
+/* see inner.h */
+void
+br_aes_ct64_bitslice_encrypt(unsigned num_rounds,
+       const uint64_t *skey, uint64_t *q)
+{
+       unsigned u;
+
+       add_round_key(q, skey);
+       for (u = 1; u < num_rounds; u ++) {
+               br_aes_ct64_bitslice_Sbox(q);
+               shift_rows(q);
+               mix_columns(q);
+               add_round_key(q, skey + (u << 3));
+       }
+       br_aes_ct64_bitslice_Sbox(q);
+       shift_rows(q);
+       add_round_key(q, skey + (num_rounds << 3));
+}
diff --git a/src/symcipher/aes_ct_cbcdec.c b/src/symcipher/aes_ct_cbcdec.c
new file mode 100644 (file)
index 0000000..522645a
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct_cbcdec_vtable;
+       ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t sk_exp[120];
+
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       ivbuf = iv;
+       iv0 = br_dec32le(ivbuf);
+       iv1 = br_dec32le(ivbuf + 4);
+       iv2 = br_dec32le(ivbuf + 8);
+       iv3 = br_dec32le(ivbuf + 12);
+       buf = data;
+       while (len > 0) {
+               uint32_t q[8], sq[8];
+
+               q[0] = br_dec32le(buf);
+               q[2] = br_dec32le(buf + 4);
+               q[4] = br_dec32le(buf + 8);
+               q[6] = br_dec32le(buf + 12);
+               if (len >= 32) {
+                       q[1] = br_dec32le(buf + 16);
+                       q[3] = br_dec32le(buf + 20);
+                       q[5] = br_dec32le(buf + 24);
+                       q[7] = br_dec32le(buf + 28);
+               } else {
+                       q[1] = 0;
+                       q[3] = 0;
+                       q[5] = 0;
+                       q[7] = 0;
+               }
+               memcpy(sq, q, sizeof q);
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_decrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+               br_enc32le(buf, q[0] ^ iv0);
+               br_enc32le(buf + 4, q[2] ^ iv1);
+               br_enc32le(buf + 8, q[4] ^ iv2);
+               br_enc32le(buf + 12, q[6] ^ iv3);
+               if (len < 32) {
+                       iv0 = sq[0];
+                       iv1 = sq[2];
+                       iv2 = sq[4];
+                       iv3 = sq[6];
+                       break;
+               }
+               br_enc32le(buf + 16, q[1] ^ sq[0]);
+               br_enc32le(buf + 20, q[3] ^ sq[2]);
+               br_enc32le(buf + 24, q[5] ^ sq[4]);
+               br_enc32le(buf + 28, q[7] ^ sq[6]);
+               iv0 = sq[1];
+               iv1 = sq[3];
+               iv2 = sq[5];
+               iv3 = sq[7];
+               buf += 32;
+               len -= 32;
+       }
+       br_enc32le(ivbuf, iv0);
+       br_enc32le(ivbuf + 4, iv1);
+       br_enc32le(ivbuf + 8, iv2);
+       br_enc32le(ivbuf + 12, iv3);
+}
+
+/* see bearssl_block.h */
+const br_block_cbcdec_class br_aes_ct_cbcdec_vtable = {
+       sizeof(br_aes_ct_cbcdec_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcdec_class **, const void *, size_t))
+               &br_aes_ct_cbcdec_init,
+       (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t))
+               &br_aes_ct_cbcdec_run
+};
diff --git a/src/symcipher/aes_ct_cbcenc.c b/src/symcipher/aes_ct_cbcenc.c
new file mode 100644 (file)
index 0000000..cb85977
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct_cbcenc_init(br_aes_ct_cbcenc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct_cbcenc_vtable;
+       ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct_cbcenc_run(const br_aes_ct_cbcenc_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+       uint32_t q[8];
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t sk_exp[120];
+
+       q[1] = 0;
+       q[3] = 0;
+       q[5] = 0;
+       q[7] = 0;
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       ivbuf = iv;
+       iv0 = br_dec32le(ivbuf);
+       iv1 = br_dec32le(ivbuf + 4);
+       iv2 = br_dec32le(ivbuf + 8);
+       iv3 = br_dec32le(ivbuf + 12);
+       buf = data;
+       while (len > 0) {
+               q[0] = iv0 ^ br_dec32le(buf);
+               q[2] = iv1 ^ br_dec32le(buf + 4);
+               q[4] = iv2 ^ br_dec32le(buf + 8);
+               q[6] = iv3 ^ br_dec32le(buf + 12);
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+               iv0 = q[0];
+               iv1 = q[2];
+               iv2 = q[4];
+               iv3 = q[6];
+               br_enc32le(buf, iv0);
+               br_enc32le(buf + 4, iv1);
+               br_enc32le(buf + 8, iv2);
+               br_enc32le(buf + 12, iv3);
+               buf += 16;
+               len -= 16;
+       }
+       br_enc32le(ivbuf, iv0);
+       br_enc32le(ivbuf + 4, iv1);
+       br_enc32le(ivbuf + 8, iv2);
+       br_enc32le(ivbuf + 12, iv3);
+}
+
+/* see bearssl_block.h */
+const br_block_cbcenc_class br_aes_ct_cbcenc_vtable = {
+       sizeof(br_aes_ct_cbcenc_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcenc_class **, const void *, size_t))
+               &br_aes_ct_cbcenc_init,
+       (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t))
+               &br_aes_ct_cbcenc_run
+};
diff --git a/src/symcipher/aes_ct_ctr.c b/src/symcipher/aes_ct_ctr.c
new file mode 100644 (file)
index 0000000..f407689
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct_ctr_vtable;
+       ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+uint32_t
+br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len)
+{
+       unsigned char *buf;
+       const unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2;
+       uint32_t sk_exp[120];
+
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       ivbuf = iv;
+       iv0 = br_dec32le(ivbuf);
+       iv1 = br_dec32le(ivbuf + 4);
+       iv2 = br_dec32le(ivbuf + 8);
+       buf = data;
+       while (len > 0) {
+               uint32_t q[8];
+               unsigned char tmp[32];
+
+               /*
+                * TODO: see if we can save on the first br_aes_ct_ortho()
+                * call, since iv0/iv1/iv2 are constant for the whole run.
+                */
+               q[0] = q[1] = iv0;
+               q[2] = q[3] = iv1;
+               q[4] = q[5] = iv2;
+               q[6] = br_swap32(cc);
+               q[7] = br_swap32(cc + 1);
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+               br_enc32le(tmp, q[0]);
+               br_enc32le(tmp + 4, q[2]);
+               br_enc32le(tmp + 8, q[4]);
+               br_enc32le(tmp + 12, q[6]);
+               br_enc32le(tmp + 16, q[1]);
+               br_enc32le(tmp + 20, q[3]);
+               br_enc32le(tmp + 24, q[5]);
+               br_enc32le(tmp + 28, q[7]);
+
+               if (len <= 32) {
+                       xorbuf(buf, tmp, len);
+                       cc ++;
+                       if (len > 16) {
+                               cc ++;
+                       }
+                       break;
+               }
+               xorbuf(buf, tmp, 32);
+               buf += 32;
+               len -= 32;
+               cc += 2;
+       }
+       return cc;
+}
+
+/* see bearssl_block.h */
+const br_block_ctr_class br_aes_ct_ctr_vtable = {
+       sizeof(br_aes_ct_ctr_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctr_class **, const void *, size_t))
+               &br_aes_ct_ctr_init,
+       (uint32_t (*)(const br_block_ctr_class *const *,
+               const void *, uint32_t, void *, size_t))
+               &br_aes_ct_ctr_run
+};
diff --git a/src/symcipher/aes_ct_dec.c b/src/symcipher/aes_ct_dec.c
new file mode 100644 (file)
index 0000000..7f32d2b
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_aes_ct_bitslice_invSbox(uint32_t *q)
+{
+       /*
+        * AES S-box is:
+        *   S(x) = A(I(x)) ^ 0x63
+        * where I() is inversion in GF(256), and A() is a linear
+        * transform (0 is formally defined to be its own inverse).
+        * Since inversion is an involution, the inverse S-box can be
+        * computed from the S-box as:
+        *   iS(x) = B(S(B(x ^ 0x63)) ^ 0x63)
+        * where B() is the inverse of A(). Indeed, for any y in GF(256):
+        *   iS(S(y)) = B(A(I(B(A(I(y)) ^ 0x63 ^ 0x63))) ^ 0x63 ^ 0x63) = y
+        *
+        * Note: we reuse the implementation of the forward S-box,
+        * instead of duplicating it here, so that total code size is
+        * lower. By merging the B() transforms into the S-box circuit
+        * we could make faster CBC decryption, but CBC decryption is
+        * already quite faster than CBC encryption because we can
+        * process two blocks in parallel.
+        */
+       uint32_t q0, q1, q2, q3, q4, q5, q6, q7;
+
+       q0 = ~q[0];
+       q1 = ~q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = ~q[5];
+       q6 = ~q[6];
+       q7 = q[7];
+       q[7] = q1 ^ q4 ^ q6;
+       q[6] = q0 ^ q3 ^ q5;
+       q[5] = q7 ^ q2 ^ q4;
+       q[4] = q6 ^ q1 ^ q3;
+       q[3] = q5 ^ q0 ^ q2;
+       q[2] = q4 ^ q7 ^ q1;
+       q[1] = q3 ^ q6 ^ q0;
+       q[0] = q2 ^ q5 ^ q7;
+
+       br_aes_ct_bitslice_Sbox(q);
+
+       q0 = ~q[0];
+       q1 = ~q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = ~q[5];
+       q6 = ~q[6];
+       q7 = q[7];
+       q[7] = q1 ^ q4 ^ q6;
+       q[6] = q0 ^ q3 ^ q5;
+       q[5] = q7 ^ q2 ^ q4;
+       q[4] = q6 ^ q1 ^ q3;
+       q[3] = q5 ^ q0 ^ q2;
+       q[2] = q4 ^ q7 ^ q1;
+       q[1] = q3 ^ q6 ^ q0;
+       q[0] = q2 ^ q5 ^ q7;
+}
+
+static void
+add_round_key(uint32_t *q, const uint32_t *sk)
+{
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               q[i] ^= sk[i];
+       }
+}
+
+static void
+inv_shift_rows(uint32_t *q)
+{
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               uint32_t x;
+
+               x = q[i];
+               q[i] = (x & 0x000000FF)
+                       | ((x & 0x00003F00) << 2) | ((x & 0x0000C000) >> 6)
+                       | ((x & 0x000F0000) << 4) | ((x & 0x00F00000) >> 4)
+                       | ((x & 0x03000000) << 6) | ((x & 0xFC000000) >> 2);
+       }
+}
+
+static inline uint32_t
+rotr16(uint32_t x)
+{
+       return (x << 16) | (x >> 16);
+}
+
+static void
+inv_mix_columns(uint32_t *q)
+{
+       uint32_t q0, q1, q2, q3, q4, q5, q6, q7;
+       uint32_t r0, r1, r2, r3, r4, r5, r6, r7;
+
+       q0 = q[0];
+       q1 = q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = q[5];
+       q6 = q[6];
+       q7 = q[7];
+       r0 = (q0 >> 8) | (q0 << 24);
+       r1 = (q1 >> 8) | (q1 << 24);
+       r2 = (q2 >> 8) | (q2 << 24);
+       r3 = (q3 >> 8) | (q3 << 24);
+       r4 = (q4 >> 8) | (q4 << 24);
+       r5 = (q5 >> 8) | (q5 << 24);
+       r6 = (q6 >> 8) | (q6 << 24);
+       r7 = (q7 >> 8) | (q7 << 24);
+
+       q[0] = q5 ^ q6 ^ q7 ^ r0 ^ r5 ^ r7 ^ rotr16(q0 ^ q5 ^ q6 ^ r0 ^ r5);
+       q[1] = q0 ^ q5 ^ r0 ^ r1 ^ r5 ^ r6 ^ r7 ^ rotr16(q1 ^ q5 ^ q7 ^ r1 ^ r5 ^ r6);
+       q[2] = q0 ^ q1 ^ q6 ^ r1 ^ r2 ^ r6 ^ r7 ^ rotr16(q0 ^ q2 ^ q6 ^ r2 ^ r6 ^ r7);
+       q[3] = q0 ^ q1 ^ q2 ^ q5 ^ q6 ^ r0 ^ r2 ^ r3 ^ r5 ^ rotr16(q0 ^ q1 ^ q3 ^ q5 ^ q6 ^ q7 ^ r0 ^ r3 ^ r5 ^ r7);
+       q[4] = q1 ^ q2 ^ q3 ^ q5 ^ r1 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr16(q1 ^ q2 ^ q4 ^ q5 ^ q7 ^ r1 ^ r4 ^ r5 ^ r6);
+       q[5] = q2 ^ q3 ^ q4 ^ q6 ^ r2 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr16(q2 ^ q3 ^ q5 ^ q6 ^ r2 ^ r5 ^ r6 ^ r7);
+       q[6] = q3 ^ q4 ^ q5 ^ q7 ^ r3 ^ r5 ^ r6 ^ r7 ^ rotr16(q3 ^ q4 ^ q6 ^ q7 ^ r3 ^ r6 ^ r7);
+       q[7] = q4 ^ q5 ^ q6 ^ r4 ^ r6 ^ r7 ^ rotr16(q4 ^ q5 ^ q7 ^ r4 ^ r7);
+}
+
+/* see inner.h */
+void
+br_aes_ct_bitslice_decrypt(unsigned num_rounds,
+       const uint32_t *skey, uint32_t *q)
+{
+       unsigned u;
+
+       add_round_key(q, skey + (num_rounds << 3));
+       for (u = num_rounds - 1; u > 0; u --) {
+               inv_shift_rows(q);
+               br_aes_ct_bitslice_invSbox(q);
+               add_round_key(q, skey + (u << 3));
+               inv_mix_columns(q);
+       }
+       inv_shift_rows(q);
+       br_aes_ct_bitslice_invSbox(q);
+       add_round_key(q, skey);
+}
diff --git a/src/symcipher/aes_ct_enc.c b/src/symcipher/aes_ct_enc.c
new file mode 100644 (file)
index 0000000..089bf35
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+static inline void
+add_round_key(uint32_t *q, const uint32_t *sk)
+{
+       q[0] ^= sk[0];
+       q[1] ^= sk[1];
+       q[2] ^= sk[2];
+       q[3] ^= sk[3];
+       q[4] ^= sk[4];
+       q[5] ^= sk[5];
+       q[6] ^= sk[6];
+       q[7] ^= sk[7];
+}
+
+static inline void
+shift_rows(uint32_t *q)
+{
+       int i;
+
+       for (i = 0; i < 8; i ++) {
+               uint32_t x;
+
+               x = q[i];
+               q[i] = (x & 0x000000FF)
+                       | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6)
+                       | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4)
+                       | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2);
+       }
+}
+
+static inline uint32_t
+rotr16(uint32_t x)
+{
+       return (x << 16) | (x >> 16);
+}
+
+static inline void
+mix_columns(uint32_t *q)
+{
+       uint32_t q0, q1, q2, q3, q4, q5, q6, q7;
+       uint32_t r0, r1, r2, r3, r4, r5, r6, r7;
+
+       q0 = q[0];
+       q1 = q[1];
+       q2 = q[2];
+       q3 = q[3];
+       q4 = q[4];
+       q5 = q[5];
+       q6 = q[6];
+       q7 = q[7];
+       r0 = (q0 >> 8) | (q0 << 24);
+       r1 = (q1 >> 8) | (q1 << 24);
+       r2 = (q2 >> 8) | (q2 << 24);
+       r3 = (q3 >> 8) | (q3 << 24);
+       r4 = (q4 >> 8) | (q4 << 24);
+       r5 = (q5 >> 8) | (q5 << 24);
+       r6 = (q6 >> 8) | (q6 << 24);
+       r7 = (q7 >> 8) | (q7 << 24);
+
+       q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0);
+       q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1);
+       q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2);
+       q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3);
+       q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4);
+       q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5);
+       q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6);
+       q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7);
+}
+
+/* see inner.h */
+void
+br_aes_ct_bitslice_encrypt(unsigned num_rounds,
+       const uint32_t *skey, uint32_t *q)
+{
+       unsigned u;
+
+       add_round_key(q, skey);
+       for (u = 1; u < num_rounds; u ++) {
+               br_aes_ct_bitslice_Sbox(q);
+               shift_rows(q);
+               mix_columns(q);
+               add_round_key(q, skey + (u << 3));
+       }
+       br_aes_ct_bitslice_Sbox(q);
+       shift_rows(q);
+       add_round_key(q, skey + (num_rounds << 3));
+}
diff --git a/src/symcipher/aes_small_cbcdec.c b/src/symcipher/aes_small_cbcdec.c
new file mode 100644 (file)
index 0000000..8567244
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_small_cbcdec_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               unsigned char tmp[16];
+               int i;
+
+               memcpy(tmp, buf, 16);
+               br_aes_small_decrypt(ctx->num_rounds, ctx->skey, buf);
+               for (i = 0; i < 16; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               memcpy(ivbuf, tmp, 16);
+               buf += 16;
+               len -= 16;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcdec_class br_aes_small_cbcdec_vtable = {
+       sizeof(br_aes_small_cbcdec_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcdec_class **, const void *, size_t))
+               &br_aes_small_cbcdec_init,
+       (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t))
+               &br_aes_small_cbcdec_run
+};
diff --git a/src/symcipher/aes_small_cbcenc.c b/src/symcipher/aes_small_cbcenc.c
new file mode 100644 (file)
index 0000000..0dc2910
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_small_cbcenc_init(br_aes_small_cbcenc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_small_cbcenc_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_small_cbcenc_run(const br_aes_small_cbcenc_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               int i;
+
+               for (i = 0; i < 16; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               br_aes_small_encrypt(ctx->num_rounds, ctx->skey, buf);
+               memcpy(ivbuf, buf, 16);
+               buf += 16;
+               len -= 16;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcenc_class br_aes_small_cbcenc_vtable = {
+       sizeof(br_aes_small_cbcenc_keys),
+       16,
+       4,
+       (void (*)(const br_block_cbcenc_class **, const void *, size_t))
+               &br_aes_small_cbcenc_init,
+       (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t))
+               &br_aes_small_cbcenc_run
+};
diff --git a/src/symcipher/aes_small_ctr.c b/src/symcipher/aes_small_ctr.c
new file mode 100644 (file)
index 0000000..d5d371c
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_small_ctr_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+uint32_t
+br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx,
+       const void *iv, uint32_t cc, void *data, size_t len)
+{
+       unsigned char *buf;
+
+       buf = data;
+       while (len > 0) {
+               unsigned char tmp[16];
+
+               memcpy(tmp, iv, 12);
+               br_enc32be(tmp + 12, cc ++);
+               br_aes_small_encrypt(ctx->num_rounds, ctx->skey, tmp);
+               if (len <= 16) {
+                       xorbuf(buf, tmp, len);
+                       break;
+               }
+               xorbuf(buf, tmp, 16);
+               buf += 16;
+               len -= 16;
+       }
+       return cc;
+}
+
+/* see bearssl_block.h */
+const br_block_ctr_class br_aes_small_ctr_vtable = {
+       sizeof(br_aes_small_ctr_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctr_class **, const void *, size_t))
+               &br_aes_small_ctr_init,
+       (uint32_t (*)(const br_block_ctr_class *const *,
+               const void *, uint32_t, void *, size_t))
+               &br_aes_small_ctr_run
+};
diff --git a/src/symcipher/aes_small_dec.c b/src/symcipher/aes_small_dec.c
new file mode 100644 (file)
index 0000000..59dca8e
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Inverse S-box.
+ */
+static const unsigned char iS[] = {
+       0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E,
+       0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
+       0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32,
+       0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+       0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49,
+       0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
+       0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50,
+       0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+       0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05,
+       0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
+       0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41,
+       0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+       0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8,
+       0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
+       0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B,
+       0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+       0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59,
+       0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
+       0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D,
+       0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+       0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63,
+       0x55, 0x21, 0x0C, 0x7D
+};
+
+static void
+add_round_key(unsigned *state, const uint32_t *skeys)
+{
+       int i;
+
+       for (i = 0; i < 16; i += 4) {
+               uint32_t k;
+
+               k = *skeys ++;
+               state[i + 0] ^= (unsigned)(k >> 24);
+               state[i + 1] ^= (unsigned)(k >> 16) & 0xFF;
+               state[i + 2] ^= (unsigned)(k >> 8) & 0xFF;
+               state[i + 3] ^= (unsigned)k & 0xFF;
+       }
+}
+
+static void
+inv_sub_bytes(unsigned *state)
+{
+       int i;
+
+       for (i = 0; i < 16; i ++) {
+               state[i] = iS[state[i]];
+       }
+}
+
+static void
+inv_shift_rows(unsigned *state)
+{
+       unsigned tmp;
+
+       tmp = state[13];
+       state[13] = state[9];
+       state[9] = state[5];
+       state[5] = state[1];
+       state[1] = tmp;
+
+       tmp = state[2];
+       state[2] = state[10];
+       state[10] = tmp;
+       tmp = state[6];
+       state[6] = state[14];
+       state[14] = tmp;
+
+       tmp = state[3];
+       state[3] = state[7];
+       state[7] = state[11];
+       state[11] = state[15];
+       state[15] = tmp;
+}
+
+static inline unsigned
+gf256red(unsigned x)
+{
+       unsigned y;
+
+       y = x >> 8;
+       return (x ^ y ^ (y << 1) ^ (y << 3) ^ (y << 4)) & 0xFF;
+}
+
+static void
+inv_mix_columns(unsigned *state)
+{
+       int i;
+
+       for (i = 0; i < 16; i += 4) {
+               unsigned s0, s1, s2, s3;
+               unsigned t0, t1, t2, t3;
+
+               s0 = state[i + 0];
+               s1 = state[i + 1];
+               s2 = state[i + 2];
+               s3 = state[i + 3];
+               t0 = (s0 << 1) ^ (s0 << 2) ^ (s0 << 3)
+                       ^ s1 ^ (s1 << 1) ^ (s1 << 3)
+                       ^ s2 ^ (s2 << 2) ^ (s2 << 3)
+                       ^ s3 ^ (s3 << 3);
+               t1 = s0 ^ (s0 << 3)
+                       ^ (s1 << 1) ^ (s1 << 2) ^ (s1 << 3)
+                       ^ s2 ^ (s2 << 1) ^ (s2 << 3)
+                       ^ s3 ^ (s3 << 2) ^ (s3 << 3);
+               t2 = s0 ^ (s0 << 2) ^ (s0 << 3)
+                       ^ s1 ^ (s1 << 3)
+                       ^ (s2 << 1) ^ (s2 << 2) ^ (s2 << 3)
+                       ^ s3 ^ (s3 << 1) ^ (s3 << 3);
+               t3 = s0 ^ (s0 << 1) ^ (s0 << 3)
+                       ^ s1 ^ (s1 << 2) ^ (s1 << 3)
+                       ^ s2 ^ (s2 << 3)
+                       ^ (s3 << 1) ^ (s3 << 2) ^ (s3 << 3);
+               state[i + 0] = gf256red(t0);
+               state[i + 1] = gf256red(t1);
+               state[i + 2] = gf256red(t2);
+               state[i + 3] = gf256red(t3);
+       }
+}
+
+/* see inner.h */
+void
+br_aes_small_decrypt(unsigned num_rounds, const uint32_t *skey, void *data)
+{
+       unsigned char *buf;
+       unsigned state[16];
+       unsigned u;
+
+       buf = data;
+       for (u = 0; u < 16; u ++) {
+               state[u] = buf[u];
+       }
+       add_round_key(state, skey + (num_rounds << 2));
+       for (u = num_rounds - 1; u > 0; u --) {
+               inv_shift_rows(state);
+               inv_sub_bytes(state);
+               add_round_key(state, skey + (u << 2));
+               inv_mix_columns(state);
+       }
+       inv_shift_rows(state);
+       inv_sub_bytes(state);
+       add_round_key(state, skey);
+       for (u = 0; u < 16; u ++) {
+               buf[u] = state[u];
+       }
+}
diff --git a/src/symcipher/aes_small_enc.c b/src/symcipher/aes_small_enc.c
new file mode 100644 (file)
index 0000000..29f48a8
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+#define S   br_aes_S
+
+static void
+add_round_key(unsigned *state, const uint32_t *skeys)
+{
+       int i;
+
+       for (i = 0; i < 16; i += 4) {
+               uint32_t k;
+
+               k = *skeys ++;
+               state[i + 0] ^= (unsigned)(k >> 24);
+               state[i + 1] ^= (unsigned)(k >> 16) & 0xFF;
+               state[i + 2] ^= (unsigned)(k >> 8) & 0xFF;
+               state[i + 3] ^= (unsigned)k & 0xFF;
+       }
+}
+
+static void
+sub_bytes(unsigned *state)
+{
+       int i;
+
+       for (i = 0; i < 16; i ++) {
+               state[i] = S[state[i]];
+       }
+}
+
+static void
+shift_rows(unsigned *state)
+{
+       unsigned tmp;
+
+       tmp = state[1];
+       state[1] = state[5];
+       state[5] = state[9];
+       state[9] = state[13];
+       state[13] = tmp;
+
+       tmp = state[2];
+       state[2] = state[10];
+       state[10] = tmp;
+       tmp = state[6];
+       state[6] = state[14];
+       state[14] = tmp;
+
+       tmp = state[15];
+       state[15] = state[11];
+       state[11] = state[7];
+       state[7] = state[3];
+       state[3] = tmp;
+}
+
+static void
+mix_columns(unsigned *state)
+{
+       int i;
+
+       for (i = 0; i < 16; i += 4) {
+               unsigned s0, s1, s2, s3;
+               unsigned t0, t1, t2, t3;
+
+               s0 = state[i + 0];
+               s1 = state[i + 1];
+               s2 = state[i + 2];
+               s3 = state[i + 3];
+               t0 = (s0 << 1) ^ s1 ^ (s1 << 1) ^ s2 ^ s3;
+               t1 = s0 ^ (s1 << 1) ^ s2 ^ (s2 << 1) ^ s3;
+               t2 = s0 ^ s1 ^ (s2 << 1) ^ s3 ^ (s3 << 1);
+               t3 = s0 ^ (s0 << 1) ^ s1 ^ s2 ^ (s3 << 1);
+               state[i + 0] = t0 ^ ((unsigned)(-(int)(t0 >> 8)) & 0x11B);
+               state[i + 1] = t1 ^ ((unsigned)(-(int)(t1 >> 8)) & 0x11B);
+               state[i + 2] = t2 ^ ((unsigned)(-(int)(t2 >> 8)) & 0x11B);
+               state[i + 3] = t3 ^ ((unsigned)(-(int)(t3 >> 8)) & 0x11B);
+       }
+}
+
+/* see inner.h */
+void
+br_aes_small_encrypt(unsigned num_rounds, const uint32_t *skey, void *data)
+{
+       unsigned char *buf;
+       unsigned state[16];
+       unsigned u;
+
+       buf = data;
+       for (u = 0; u < 16; u ++) {
+               state[u] = buf[u];
+       }
+       add_round_key(state, skey);
+       for (u = 1; u < num_rounds; u ++) {
+               sub_bytes(state);
+               shift_rows(state);
+               mix_columns(state);
+               add_round_key(state, skey + (u << 2));
+       }
+       sub_bytes(state);
+       shift_rows(state);
+       add_round_key(state, skey + (num_rounds << 2));
+       for (u = 0; u < 16; u ++) {
+               buf[u] = state[u];
+       }
+}
diff --git a/src/symcipher/des_ct.c b/src/symcipher/des_ct.c
new file mode 100644 (file)
index 0000000..581c0ab
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * During key schedule, we need to apply bit extraction PC-2 then permute
+ * things into our bitslice representation. PC-2 extracts 48 bits out
+ * of two 28-bit words (kl and kr), and we store these bits into two
+ * 32-bit words sk0 and sk1.
+ *
+ *  -- bit 16+x of sk0 comes from bit QL0[x] of kl
+ *  -- bit x of sk0 comes from bit QR0[x] of kr
+ *  -- bit 16+x of sk1 comes from bit QL1[x] of kl
+ *  -- bit x of sk1 comes from bit QR1[x] of kr
+ */
+
+static const unsigned char QL0[] = {
+       17,  4, 27, 23, 13, 22,  7, 18,
+       16, 24,  2, 20,  1,  8, 15, 26
+};
+
+static const unsigned char QR0[] = {
+       25, 19,  9,  1,  5, 11, 23,  8,
+       17,  0, 22,  3,  6, 20, 27, 24
+};
+
+static const unsigned char QL1[] = {
+       28, 28, 14, 11, 28, 28, 25,  0,
+       28, 28,  5,  9, 28, 28, 12, 21
+};
+
+static const unsigned char QR1[] = {
+       28, 28, 15,  4, 28, 28, 26, 16,
+       28, 28, 12,  7, 28, 28, 10, 14
+};
+
+/*
+ * 32-bit rotation. The C compiler is supposed to recognize it as a
+ * rotation and use the local architecture rotation opcode (if available).
+ */
+static inline uint32_t
+rotl(uint32_t x, int n)
+{
+       return (x << n) | (x >> (32 - n));
+}
+
+/*
+ * Compute key schedule for 8 key bytes (produces 32 subkey words).
+ */
+static void
+keysched_unit(uint32_t *skey, const void *key)
+{
+       int i;
+
+       br_des_keysched_unit(skey, key);
+
+       /*
+        * Apply PC-2 + bitslicing.
+        */
+       for (i = 0; i < 16; i ++) {
+               uint32_t kl, kr, sk0, sk1;
+               int j;
+
+               kl = skey[(i << 1) + 0];
+               kr = skey[(i << 1) + 1];
+               sk0 = 0;
+               sk1 = 0;
+               for (j = 0; j < 16; j ++) {
+                       sk0 <<= 1;
+                       sk1 <<= 1;
+                       sk0 |= ((kl >> QL0[j]) & (uint32_t)1) << 16;
+                       sk0 |= (kr >> QR0[j]) & (uint32_t)1;
+                       sk1 |= ((kl >> QL1[j]) & (uint32_t)1) << 16;
+                       sk1 |= (kr >> QR1[j]) & (uint32_t)1;
+               }
+
+               skey[(i << 1) + 0] = sk0;
+               skey[(i << 1) + 1] = sk1;
+       }
+
+#if 0
+               /*
+                * Speed-optimized version for PC-2 + bitslicing.
+                * (Unused. Kept for reference only.)
+                */
+               sk0 = kl & (uint32_t)0x00100000;
+               sk0 |= (kl & (uint32_t)0x08008000) << 2;
+               sk0 |= (kl & (uint32_t)0x00400000) << 4;
+               sk0 |= (kl & (uint32_t)0x00800000) << 5;
+               sk0 |= (kl & (uint32_t)0x00040000) << 6;
+               sk0 |= (kl & (uint32_t)0x00010000) << 7;
+               sk0 |= (kl & (uint32_t)0x00000100) << 10;
+               sk0 |= (kl & (uint32_t)0x00022000) << 14;
+               sk0 |= (kl & (uint32_t)0x00000082) << 18;
+               sk0 |= (kl & (uint32_t)0x00000004) << 19;
+               sk0 |= (kl & (uint32_t)0x04000000) >> 10;
+               sk0 |= (kl & (uint32_t)0x00000010) << 26;
+               sk0 |= (kl & (uint32_t)0x01000000) >> 2;
+
+               sk0 |= kr & (uint32_t)0x00000100;
+               sk0 |= (kr & (uint32_t)0x00000008) << 1;
+               sk0 |= (kr & (uint32_t)0x00000200) << 4;
+               sk0 |= rotl(kr & (uint32_t)0x08000021, 6);
+               sk0 |= (kr & (uint32_t)0x01000000) >> 24;
+               sk0 |= (kr & (uint32_t)0x00000002) << 11;
+               sk0 |= (kr & (uint32_t)0x00100000) >> 18;
+               sk0 |= (kr & (uint32_t)0x00400000) >> 17;
+               sk0 |= (kr & (uint32_t)0x00800000) >> 14;
+               sk0 |= (kr & (uint32_t)0x02020000) >> 10;
+               sk0 |= (kr & (uint32_t)0x00080000) >> 5;
+               sk0 |= (kr & (uint32_t)0x00000040) >> 3;
+               sk0 |= (kr & (uint32_t)0x00000800) >> 1;
+
+               sk1 = kl & (uint32_t)0x02000000;
+               sk1 |= (kl & (uint32_t)0x00001000) << 5;
+               sk1 |= (kl & (uint32_t)0x00000200) << 11;
+               sk1 |= (kl & (uint32_t)0x00004000) << 15;
+               sk1 |= (kl & (uint32_t)0x00000020) << 16;
+               sk1 |= (kl & (uint32_t)0x00000800) << 17;
+               sk1 |= (kl & (uint32_t)0x00000001) << 24;
+               sk1 |= (kl & (uint32_t)0x00200000) >> 5;
+
+               sk1 |= (kr & (uint32_t)0x00000010) << 8;
+               sk1 |= (kr & (uint32_t)0x04000000) >> 17;
+               sk1 |= (kr & (uint32_t)0x00004000) >> 14;
+               sk1 |= (kr & (uint32_t)0x00000400) >> 9;
+               sk1 |= (kr & (uint32_t)0x00010000) >> 8;
+               sk1 |= (kr & (uint32_t)0x00001000) >> 7;
+               sk1 |= (kr & (uint32_t)0x00000080) >> 3;
+               sk1 |= (kr & (uint32_t)0x00008000) >> 2;
+#endif
+}
+
+/* see inner.h */
+unsigned
+br_des_ct_keysched(uint32_t *skey, const void *key, size_t key_len)
+{
+       switch (key_len) {
+       case 8:
+               keysched_unit(skey, key);
+               return 1;
+       case 16:
+               keysched_unit(skey, key);
+               keysched_unit(skey + 32, (const unsigned char *)key + 8);
+               br_des_rev_skey(skey + 32);
+               memcpy(skey + 64, skey, 32 * sizeof *skey);
+               return 3;
+       default:
+               keysched_unit(skey, key);
+               keysched_unit(skey + 32, (const unsigned char *)key + 8);
+               br_des_rev_skey(skey + 32);
+               keysched_unit(skey + 64, (const unsigned char *)key + 16);
+               return 3;
+       }
+}
+
+/*
+ * DES confusion function. This function performs expansion E (32 to
+ * 48 bits), XOR with subkey, S-boxes, and permutation P.
+ */
+static inline uint32_t
+Fconf(uint32_t r0, const uint32_t *sk)
+{
+       /*
+        * Each 6->4 S-box is virtually turned into four 6->1 boxes; we
+        * thus end up with 32 boxes that we call "T-boxes" here. We will
+        * evaluate them with bitslice code.
+        *
+        * Each T-box is a circuit of multiplexers (sort of) and thus
+        * takes 70 inputs: the 6 actual T-box inputs, and 64 constants
+        * that describe the T-box output for all combinations of the
+        * 6 inputs. With this model, all T-boxes are identical (with
+        * distinct inputs) and thus can be executed in parallel with
+        * bitslice code.
+        *
+        * T-boxes are numbered from 0 to 31, in least-to-most
+        * significant order. Thus, S-box S1 corresponds to T-boxes 31,
+        * 30, 29 and 28, in that order. T-box 'n' is computed with the
+        * bits at rank 'n' in the 32-bit words.
+        *
+        * Words x0 to x5 contain the T-box inputs 0 to 5.
+        */
+       uint32_t x0, x1, x2, x3, x4, x5, z0;
+       uint32_t y0, y1, y2, y3, y4, y5, y6, y7, y8, y9;
+       uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19;
+       uint32_t y20, y21, y22, y23, y24, y25, y26, y27, y28, y29;
+       uint32_t y30;
+
+       /*
+        * Spread input bits over the 6 input words x*.
+        */
+       x1 = r0 & (uint32_t)0x11111111;
+       x2 = (r0 >> 1) & (uint32_t)0x11111111;
+       x3 = (r0 >> 2) & (uint32_t)0x11111111;
+       x4 = (r0 >> 3) & (uint32_t)0x11111111;
+       x1 = (x1 << 4) - x1;
+       x2 = (x2 << 4) - x2;
+       x3 = (x3 << 4) - x3;
+       x4 = (x4 << 4) - x4;
+       x0 = (x4 << 4) | (x4 >> 28);
+       x5 = (x1 >> 4) | (x1 << 28);
+
+       /*
+        * XOR with the subkey for this round.
+        */
+       x0 ^= sk[0];
+       x1 ^= sk[1];
+       x2 ^= sk[2];
+       x3 ^= sk[3];
+       x4 ^= sk[4];
+       x5 ^= sk[5];
+
+       /*
+        * The T-boxes are done in parallel, since they all use a
+        * "tree of multiplexer". We use "fake multiplexers":
+        *
+        *   y = a ^ (x & b)
+        *
+        * computes y as either 'a' (if x == 0) or 'a ^ b' (if x == 1).
+        */
+       y0 = (uint32_t)0xEFA72C4D ^ (x0 & (uint32_t)0xEC7AC69C);
+       y1 = (uint32_t)0xAEAAEDFF ^ (x0 & (uint32_t)0x500FB821);
+       y2 = (uint32_t)0x37396665 ^ (x0 & (uint32_t)0x40EFA809);
+       y3 = (uint32_t)0x68D7B833 ^ (x0 & (uint32_t)0xA5EC0B28);
+       y4 = (uint32_t)0xC9C755BB ^ (x0 & (uint32_t)0x252CF820);
+       y5 = (uint32_t)0x73FC3606 ^ (x0 & (uint32_t)0x40205801);
+       y6 = (uint32_t)0xA2A0A918 ^ (x0 & (uint32_t)0xE220F929);
+       y7 = (uint32_t)0x8222BD90 ^ (x0 & (uint32_t)0x44A3F9E1);
+       y8 = (uint32_t)0xD6B6AC77 ^ (x0 & (uint32_t)0x794F104A);
+       y9 = (uint32_t)0x3069300C ^ (x0 & (uint32_t)0x026F320B);
+       y10 = (uint32_t)0x6CE0D5CC ^ (x0 & (uint32_t)0x7640B01A);
+       y11 = (uint32_t)0x59A9A22D ^ (x0 & (uint32_t)0x238F1572);
+       y12 = (uint32_t)0xAC6D0BD4 ^ (x0 & (uint32_t)0x7A63C083);
+       y13 = (uint32_t)0x21C83200 ^ (x0 & (uint32_t)0x11CCA000);
+       y14 = (uint32_t)0xA0E62188 ^ (x0 & (uint32_t)0x202F69AA);
+       /* y15 = (uint32_t)0x00000000 ^ (x0 & (uint32_t)0x00000000); */
+       y16 = (uint32_t)0xAF7D655A ^ (x0 & (uint32_t)0x51B33BE9);
+       y17 = (uint32_t)0xF0168AA3 ^ (x0 & (uint32_t)0x3B0FE8AE);
+       y18 = (uint32_t)0x90AA30C6 ^ (x0 & (uint32_t)0x90BF8816);
+       y19 = (uint32_t)0x5AB2750A ^ (x0 & (uint32_t)0x09E34F9B);
+       y20 = (uint32_t)0x5391BE65 ^ (x0 & (uint32_t)0x0103BE88);
+       y21 = (uint32_t)0x93372BAF ^ (x0 & (uint32_t)0x49AC8E25);
+       y22 = (uint32_t)0xF288210C ^ (x0 & (uint32_t)0x922C313D);
+       y23 = (uint32_t)0x920AF5C0 ^ (x0 & (uint32_t)0x70EF31B0);
+       y24 = (uint32_t)0x63D312C0 ^ (x0 & (uint32_t)0x6A707100);
+       y25 = (uint32_t)0x537B3006 ^ (x0 & (uint32_t)0xB97C9011);
+       y26 = (uint32_t)0xA2EFB0A5 ^ (x0 & (uint32_t)0xA320C959);
+       y27 = (uint32_t)0xBC8F96A5 ^ (x0 & (uint32_t)0x6EA0AB4A);
+       y28 = (uint32_t)0xFAD176A5 ^ (x0 & (uint32_t)0x6953DDF8);
+       y29 = (uint32_t)0x665A14A3 ^ (x0 & (uint32_t)0xF74F3E2B);
+       y30 = (uint32_t)0xF2EFF0CC ^ (x0 & (uint32_t)0xF0306CAD);
+       /* y31 = (uint32_t)0x00000000 ^ (x0 & (uint32_t)0x00000000); */
+
+       y0 = y0 ^ (x1 & y1);
+       y1 = y2 ^ (x1 & y3);
+       y2 = y4 ^ (x1 & y5);
+       y3 = y6 ^ (x1 & y7);
+       y4 = y8 ^ (x1 & y9);
+       y5 = y10 ^ (x1 & y11);
+       y6 = y12 ^ (x1 & y13);
+       y7 = y14; /* was: y14 ^ (x1 & y15) */
+       y8 = y16 ^ (x1 & y17);
+       y9 = y18 ^ (x1 & y19);
+       y10 = y20 ^ (x1 & y21);
+       y11 = y22 ^ (x1 & y23);
+       y12 = y24 ^ (x1 & y25);
+       y13 = y26 ^ (x1 & y27);
+       y14 = y28 ^ (x1 & y29);
+       y15 = y30; /* was: y30 ^ (x1 & y31) */
+
+       y0 = y0 ^ (x2 & y1);
+       y1 = y2 ^ (x2 & y3);
+       y2 = y4 ^ (x2 & y5);
+       y3 = y6 ^ (x2 & y7);
+       y4 = y8 ^ (x2 & y9);
+       y5 = y10 ^ (x2 & y11);
+       y6 = y12 ^ (x2 & y13);
+       y7 = y14 ^ (x2 & y15);
+
+       y0 = y0 ^ (x3 & y1);
+       y1 = y2 ^ (x3 & y3);
+       y2 = y4 ^ (x3 & y5);
+       y3 = y6 ^ (x3 & y7);
+
+       y0 = y0 ^ (x4 & y1);
+       y1 = y2 ^ (x4 & y3);
+
+       y0 = y0 ^ (x5 & y1);
+
+       /*
+        * The P permutation:
+        * -- Each bit move is converted into a mask + left rotation.
+        * -- Rotations that use the same movement are coalesced together.
+        * -- Left and right shifts are used as alternatives to a rotation
+        * where appropriate (this will help architectures that do not have
+        * a rotation opcode).
+        */
+       z0 = (y0 & (uint32_t)0x00000004) << 3;
+       z0 |= (y0 & (uint32_t)0x00004000) << 4;
+       z0 |= rotl(y0 & 0x12020120, 5);
+       z0 |= (y0 & (uint32_t)0x00100000) << 6;
+       z0 |= (y0 & (uint32_t)0x00008000) << 9;
+       z0 |= (y0 & (uint32_t)0x04000000) >> 22;
+       z0 |= (y0 & (uint32_t)0x00000001) << 11;
+       z0 |= rotl(y0 & 0x20000200, 12);
+       z0 |= (y0 & (uint32_t)0x00200000) >> 19;
+       z0 |= (y0 & (uint32_t)0x00000040) << 14;
+       z0 |= (y0 & (uint32_t)0x00010000) << 15;
+       z0 |= (y0 & (uint32_t)0x00000002) << 16;
+       z0 |= rotl(y0 & 0x40801800, 17);
+       z0 |= (y0 & (uint32_t)0x00080000) >> 13;
+       z0 |= (y0 & (uint32_t)0x00000010) << 21;
+       z0 |= (y0 & (uint32_t)0x01000000) >> 10;
+       z0 |= rotl(y0 & 0x88000008, 24);
+       z0 |= (y0 & (uint32_t)0x00000480) >> 7;
+       z0 |= (y0 & (uint32_t)0x00442000) >> 6;
+       return z0;
+}
+
+/*
+ * Process one block through 16 successive rounds, omitting the swap
+ * in the final round.
+ */
+static void
+process_block_unit(uint32_t *pl, uint32_t *pr, const uint32_t *sk_exp)
+{
+       int i;
+       uint32_t l, r;
+
+       l = *pl;
+       r = *pr;
+       for (i = 0; i < 16; i ++) {
+               uint32_t t;
+
+               t = l ^ Fconf(r, sk_exp);
+               l = r;
+               r = t;
+               sk_exp += 6;
+       }
+       *pl = r;
+       *pr = l;
+}
+
+/* see inner.h */
+void
+br_des_ct_process_block(unsigned num_rounds,
+       const uint32_t *sk_exp, void *block)
+{
+       unsigned char *buf;
+       uint32_t l, r;
+
+       buf = block;
+       l = br_dec32be(buf);
+       r = br_dec32be(buf + 4);
+       br_des_do_IP(&l, &r);
+       while (num_rounds -- > 0) {
+               process_block_unit(&l, &r, sk_exp);
+               sk_exp += 96;
+       }
+       br_des_do_invIP(&l, &r);
+       br_enc32be(buf, l);
+       br_enc32be(buf + 4, r);
+}
+
+/* see inner.h */
+void
+br_des_ct_skey_expand(uint32_t *sk_exp,
+       unsigned num_rounds, const uint32_t *skey)
+{
+       num_rounds <<= 4;
+       while (num_rounds -- > 0) {
+               uint32_t v, w0, w1, w2, w3;
+
+               v = *skey ++;
+               w0 = v & 0x11111111;
+               w1 = (v >> 1) & 0x11111111;
+               w2 = (v >> 2) & 0x11111111;
+               w3 = (v >> 3) & 0x11111111;
+               *sk_exp ++ = (w0 << 4) - w0;
+               *sk_exp ++ = (w1 << 4) - w1;
+               *sk_exp ++ = (w2 << 4) - w2;
+               *sk_exp ++ = (w3 << 4) - w3;
+               v = *skey ++;
+               w0 = v & 0x11111111;
+               w1 = (v >> 1) & 0x11111111;
+               *sk_exp ++ = (w0 << 4) - w0;
+               *sk_exp ++ = (w1 << 4) - w1;
+       }
+}
diff --git a/src/symcipher/des_ct_cbcdec.c b/src/symcipher/des_ct_cbcdec.c
new file mode 100644 (file)
index 0000000..d208a3d
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_des_ct_cbcdec_init(br_des_ct_cbcdec_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_des_ct_cbcdec_vtable;
+       ctx->num_rounds = br_des_ct_keysched(ctx->skey, key, len);
+       if (len == 8) {
+               br_des_rev_skey(ctx->skey);
+       } else {
+               int i;
+
+               for (i = 0; i < 48; i += 2) {
+                       uint32_t t;
+
+                       t = ctx->skey[i];
+                       ctx->skey[i] = ctx->skey[94 - i];
+                       ctx->skey[94 - i] = t;
+                       t = ctx->skey[i + 1];
+                       ctx->skey[i + 1] = ctx->skey[95 - i];
+                       ctx->skey[95 - i] = t;
+               }
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_des_ct_cbcdec_run(const br_des_ct_cbcdec_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+       uint32_t sk_exp[288];
+
+       br_des_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               unsigned char tmp[8];
+               int i;
+
+               memcpy(tmp, buf, 8);
+               br_des_ct_process_block(ctx->num_rounds, sk_exp, buf);
+               for (i = 0; i < 8; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               memcpy(ivbuf, tmp, 8);
+               buf += 8;
+               len -= 8;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcdec_class br_des_ct_cbcdec_vtable = {
+       sizeof(br_des_ct_cbcdec_keys),
+       8,
+       3,
+       (void (*)(const br_block_cbcdec_class **, const void *, size_t))
+               &br_des_ct_cbcdec_init,
+       (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t))
+               &br_des_ct_cbcdec_run
+};
diff --git a/src/symcipher/des_ct_cbcenc.c b/src/symcipher/des_ct_cbcenc.c
new file mode 100644 (file)
index 0000000..4b3610e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_des_ct_cbcenc_init(br_des_ct_cbcenc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_des_ct_cbcenc_vtable;
+       ctx->num_rounds = br_des_ct_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_des_ct_cbcenc_run(const br_des_ct_cbcenc_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+       uint32_t sk_exp[288];
+
+       br_des_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               int i;
+
+               for (i = 0; i < 8; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               br_des_ct_process_block(ctx->num_rounds, sk_exp, buf);
+               memcpy(ivbuf, buf, 8);
+               buf += 8;
+               len -= 8;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcenc_class br_des_ct_cbcenc_vtable = {
+       sizeof(br_des_ct_cbcenc_keys),
+       8,
+       3,
+       (void (*)(const br_block_cbcenc_class **, const void *, size_t))
+               &br_des_ct_cbcenc_init,
+       (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t))
+               &br_des_ct_cbcenc_run
+};
diff --git a/src/symcipher/des_support.c b/src/symcipher/des_support.c
new file mode 100644 (file)
index 0000000..37f6db3
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see inner.h */
+void
+br_des_do_IP(uint32_t *xl, uint32_t *xr)
+{
+       /*
+        * Permutation algorithm is initially from Richard Outerbridge;
+        * implementation here is adapted from Crypto++ "des.cpp" file
+        * (which is in public domain).
+        */
+       uint32_t l, r, t;
+
+       l = *xl;
+       r = *xr;
+       t = ((l >>  4) ^ r) & (uint32_t)0x0F0F0F0F;
+       r ^= t;
+       l ^= t <<  4;
+       t = ((l >> 16) ^ r) & (uint32_t)0x0000FFFF;
+       r ^= t;
+       l ^= t << 16;
+       t = ((r >>  2) ^ l) & (uint32_t)0x33333333;
+       l ^= t;
+       r ^= t <<  2;
+       t = ((r >>  8) ^ l) & (uint32_t)0x00FF00FF;
+       l ^= t;
+       r ^= t <<  8;
+       t = ((l >>  1) ^ r) & (uint32_t)0x55555555;
+       r ^= t;
+       l ^= t <<  1;
+       *xl = l;
+       *xr = r;
+}
+
+/* see inner.h */
+void
+br_des_do_invIP(uint32_t *xl, uint32_t *xr)
+{
+       /*
+        * See br_des_do_IP().
+        */
+       uint32_t l, r, t;
+
+       l = *xl;
+       r = *xr;
+       t = ((l >>  1) ^ r) & 0x55555555;
+       r ^= t;
+       l ^= t <<  1;
+       t = ((r >>  8) ^ l) & 0x00FF00FF;
+       l ^= t;
+       r ^= t <<  8;
+       t = ((r >>  2) ^ l) & 0x33333333;
+       l ^= t;
+       r ^= t <<  2;
+       t = ((l >> 16) ^ r) & 0x0000FFFF;
+       r ^= t;
+       l ^= t << 16;
+       t = ((l >>  4) ^ r) & 0x0F0F0F0F;
+       r ^= t;
+       l ^= t <<  4;
+       *xl = l;
+       *xr = r;
+}
+
+/* see inner.h */
+void
+br_des_keysched_unit(uint32_t *skey, const void *key)
+{
+       uint32_t xl, xr, kl, kr;
+       int i;
+
+       xl = br_dec32be(key);
+       xr = br_dec32be((const unsigned char *)key + 4);
+
+       /*
+        * Permutation PC-1 is quite similar to the IP permutation.
+        * Definition of IP (in FIPS 46-3 notations) is:
+        *   58 50 42 34 26 18 10 2
+        *   60 52 44 36 28 20 12 4
+        *   62 54 46 38 30 22 14 6
+        *   64 56 48 40 32 24 16 8
+        *   57 49 41 33 25 17  9 1
+        *   59 51 43 35 27 19 11 3
+        *   61 53 45 37 29 21 13 5
+        *   63 55 47 39 31 23 15 7
+        *
+        * Definition of PC-1 is:
+        *   57 49 41 33 25 17  9 1
+        *   58 50 42 34 26 18 10 2
+        *   59 51 43 35 27 19 11 3
+        *   60 52 44 36
+        *   63 55 47 39 31 23 15 7
+        *   62 54 46 38 30 22 14 6
+        *   61 53 45 37 29 21 13 5
+        *   28 20 12  4
+        */
+       br_des_do_IP(&xl, &xr);
+       kl = ((xr & (uint32_t)0xFF000000) >> 4)
+               | ((xl & (uint32_t)0xFF000000) >> 12)
+               | ((xr & (uint32_t)0x00FF0000) >> 12)
+               | ((xl & (uint32_t)0x00FF0000) >> 20);
+       kr = ((xr & (uint32_t)0x000000FF) << 20)
+               | ((xl & (uint32_t)0x0000FF00) << 4)
+               | ((xr & (uint32_t)0x0000FF00) >> 4)
+               | ((xl & (uint32_t)0x000F0000) >> 16);
+
+       /*
+        * For each round, rotate the two 28-bit words kl and kr.
+        * The extraction of the 48-bit subkey (PC-2) is not done yet.
+        */
+       for (i = 0; i < 16; i ++) {
+               if ((1 << i) & 0x8103) {
+                       kl = (kl << 1) | (kl >> 27);
+                       kr = (kr << 1) | (kr >> 27);
+               } else {
+                       kl = (kl << 2) | (kl >> 26);
+                       kr = (kr << 2) | (kr >> 26);
+               }
+               kl &= (uint32_t)0x0FFFFFFF;
+               kr &= (uint32_t)0x0FFFFFFF;
+               skey[(i << 1) + 0] = kl;
+               skey[(i << 1) + 1] = kr;
+       }
+}
+
+/* see inner.h */
+void
+br_des_rev_skey(uint32_t *skey)
+{
+       int i;
+
+       for (i = 0; i < 16; i += 2) {
+               uint32_t t;
+
+               t = skey[i + 0];
+               skey[i + 0] = skey[30 - i];
+               skey[30 - i] = t;
+               t = skey[i + 1];
+               skey[i + 1] = skey[31 - i];
+               skey[31 - i] = t;
+       }
+}
diff --git a/src/symcipher/des_tab.c b/src/symcipher/des_tab.c
new file mode 100644 (file)
index 0000000..3f8e4f9
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * PC2left[x] tells where bit x goes when applying PC-2. 'x' is a bit
+ * position in the left rotated key word. Both position are in normal
+ * order (rightmost bit is 0).
+ */
+static const unsigned char PC2left[] = {
+       16,  3,  7, 24, 20, 11, 24,
+       13,  2, 10, 24, 22,  5, 15,
+       23,  1,  9, 21, 12, 24,  6,
+        4, 14, 18,  8, 17,  0, 19
+};
+
+/*
+ * Similar to PC2left[x], for the right rotated key word.
+ */
+static const unsigned char PC2right[] = {
+        8, 18, 24,  6, 22, 15,  3,
+       10, 12, 19,  5, 14, 11, 24,
+        4, 23, 16,  9, 24, 20,  2,
+       24,  7, 13,  0, 21, 17,  1
+};
+
+/*
+ * S-boxes and PC-1 merged.
+ */
+static const uint32_t S1[] = {
+       0x00808200, 0x00000000, 0x00008000, 0x00808202,
+       0x00808002, 0x00008202, 0x00000002, 0x00008000,
+       0x00000200, 0x00808200, 0x00808202, 0x00000200,
+       0x00800202, 0x00808002, 0x00800000, 0x00000002,
+       0x00000202, 0x00800200, 0x00800200, 0x00008200,
+       0x00008200, 0x00808000, 0x00808000, 0x00800202,
+       0x00008002, 0x00800002, 0x00800002, 0x00008002,
+       0x00000000, 0x00000202, 0x00008202, 0x00800000,
+       0x00008000, 0x00808202, 0x00000002, 0x00808000,
+       0x00808200, 0x00800000, 0x00800000, 0x00000200,
+       0x00808002, 0x00008000, 0x00008200, 0x00800002,
+       0x00000200, 0x00000002, 0x00800202, 0x00008202,
+       0x00808202, 0x00008002, 0x00808000, 0x00800202,
+       0x00800002, 0x00000202, 0x00008202, 0x00808200,
+       0x00000202, 0x00800200, 0x00800200, 0x00000000,
+       0x00008002, 0x00008200, 0x00000000, 0x00808002
+};
+
+static const uint32_t S2[] = {
+       0x40084010, 0x40004000, 0x00004000, 0x00084010,
+       0x00080000, 0x00000010, 0x40080010, 0x40004010,
+       0x40000010, 0x40084010, 0x40084000, 0x40000000,
+       0x40004000, 0x00080000, 0x00000010, 0x40080010,
+       0x00084000, 0x00080010, 0x40004010, 0x00000000,
+       0x40000000, 0x00004000, 0x00084010, 0x40080000,
+       0x00080010, 0x40000010, 0x00000000, 0x00084000,
+       0x00004010, 0x40084000, 0x40080000, 0x00004010,
+       0x00000000, 0x00084010, 0x40080010, 0x00080000,
+       0x40004010, 0x40080000, 0x40084000, 0x00004000,
+       0x40080000, 0x40004000, 0x00000010, 0x40084010,
+       0x00084010, 0x00000010, 0x00004000, 0x40000000,
+       0x00004010, 0x40084000, 0x00080000, 0x40000010,
+       0x00080010, 0x40004010, 0x40000010, 0x00080010,
+       0x00084000, 0x00000000, 0x40004000, 0x00004010,
+       0x40000000, 0x40080010, 0x40084010, 0x00084000
+};
+
+static const uint32_t S3[] = {
+       0x00000104, 0x04010100, 0x00000000, 0x04010004,
+       0x04000100, 0x00000000, 0x00010104, 0x04000100,
+       0x00010004, 0x04000004, 0x04000004, 0x00010000,
+       0x04010104, 0x00010004, 0x04010000, 0x00000104,
+       0x04000000, 0x00000004, 0x04010100, 0x00000100,
+       0x00010100, 0x04010000, 0x04010004, 0x00010104,
+       0x04000104, 0x00010100, 0x00010000, 0x04000104,
+       0x00000004, 0x04010104, 0x00000100, 0x04000000,
+       0x04010100, 0x04000000, 0x00010004, 0x00000104,
+       0x00010000, 0x04010100, 0x04000100, 0x00000000,
+       0x00000100, 0x00010004, 0x04010104, 0x04000100,
+       0x04000004, 0x00000100, 0x00000000, 0x04010004,
+       0x04000104, 0x00010000, 0x04000000, 0x04010104,
+       0x00000004, 0x00010104, 0x00010100, 0x04000004,
+       0x04010000, 0x04000104, 0x00000104, 0x04010000,
+       0x00010104, 0x00000004, 0x04010004, 0x00010100
+};
+
+static const uint32_t S4[] = {
+       0x80401000, 0x80001040, 0x80001040, 0x00000040,
+       0x00401040, 0x80400040, 0x80400000, 0x80001000,
+       0x00000000, 0x00401000, 0x00401000, 0x80401040,
+       0x80000040, 0x00000000, 0x00400040, 0x80400000,
+       0x80000000, 0x00001000, 0x00400000, 0x80401000,
+       0x00000040, 0x00400000, 0x80001000, 0x00001040,
+       0x80400040, 0x80000000, 0x00001040, 0x00400040,
+       0x00001000, 0x00401040, 0x80401040, 0x80000040,
+       0x00400040, 0x80400000, 0x00401000, 0x80401040,
+       0x80000040, 0x00000000, 0x00000000, 0x00401000,
+       0x00001040, 0x00400040, 0x80400040, 0x80000000,
+       0x80401000, 0x80001040, 0x80001040, 0x00000040,
+       0x80401040, 0x80000040, 0x80000000, 0x00001000,
+       0x80400000, 0x80001000, 0x00401040, 0x80400040,
+       0x80001000, 0x00001040, 0x00400000, 0x80401000,
+       0x00000040, 0x00400000, 0x00001000, 0x00401040
+};
+
+static const uint32_t S5[] = {
+       0x00000080, 0x01040080, 0x01040000, 0x21000080,
+       0x00040000, 0x00000080, 0x20000000, 0x01040000,
+       0x20040080, 0x00040000, 0x01000080, 0x20040080,
+       0x21000080, 0x21040000, 0x00040080, 0x20000000,
+       0x01000000, 0x20040000, 0x20040000, 0x00000000,
+       0x20000080, 0x21040080, 0x21040080, 0x01000080,
+       0x21040000, 0x20000080, 0x00000000, 0x21000000,
+       0x01040080, 0x01000000, 0x21000000, 0x00040080,
+       0x00040000, 0x21000080, 0x00000080, 0x01000000,
+       0x20000000, 0x01040000, 0x21000080, 0x20040080,
+       0x01000080, 0x20000000, 0x21040000, 0x01040080,
+       0x20040080, 0x00000080, 0x01000000, 0x21040000,
+       0x21040080, 0x00040080, 0x21000000, 0x21040080,
+       0x01040000, 0x00000000, 0x20040000, 0x21000000,
+       0x00040080, 0x01000080, 0x20000080, 0x00040000,
+       0x00000000, 0x20040000, 0x01040080, 0x20000080
+};
+
+static const uint32_t S6[] = {
+       0x10000008, 0x10200000, 0x00002000, 0x10202008,
+       0x10200000, 0x00000008, 0x10202008, 0x00200000,
+       0x10002000, 0x00202008, 0x00200000, 0x10000008,
+       0x00200008, 0x10002000, 0x10000000, 0x00002008,
+       0x00000000, 0x00200008, 0x10002008, 0x00002000,
+       0x00202000, 0x10002008, 0x00000008, 0x10200008,
+       0x10200008, 0x00000000, 0x00202008, 0x10202000,
+       0x00002008, 0x00202000, 0x10202000, 0x10000000,
+       0x10002000, 0x00000008, 0x10200008, 0x00202000,
+       0x10202008, 0x00200000, 0x00002008, 0x10000008,
+       0x00200000, 0x10002000, 0x10000000, 0x00002008,
+       0x10000008, 0x10202008, 0x00202000, 0x10200000,
+       0x00202008, 0x10202000, 0x00000000, 0x10200008,
+       0x00000008, 0x00002000, 0x10200000, 0x00202008,
+       0x00002000, 0x00200008, 0x10002008, 0x00000000,
+       0x10202000, 0x10000000, 0x00200008, 0x10002008
+};
+
+static const uint32_t S7[] = {
+       0x00100000, 0x02100001, 0x02000401, 0x00000000,
+       0x00000400, 0x02000401, 0x00100401, 0x02100400,
+       0x02100401, 0x00100000, 0x00000000, 0x02000001,
+       0x00000001, 0x02000000, 0x02100001, 0x00000401,
+       0x02000400, 0x00100401, 0x00100001, 0x02000400,
+       0x02000001, 0x02100000, 0x02100400, 0x00100001,
+       0x02100000, 0x00000400, 0x00000401, 0x02100401,
+       0x00100400, 0x00000001, 0x02000000, 0x00100400,
+       0x02000000, 0x00100400, 0x00100000, 0x02000401,
+       0x02000401, 0x02100001, 0x02100001, 0x00000001,
+       0x00100001, 0x02000000, 0x02000400, 0x00100000,
+       0x02100400, 0x00000401, 0x00100401, 0x02100400,
+       0x00000401, 0x02000001, 0x02100401, 0x02100000,
+       0x00100400, 0x00000000, 0x00000001, 0x02100401,
+       0x00000000, 0x00100401, 0x02100000, 0x00000400,
+       0x02000001, 0x02000400, 0x00000400, 0x00100001
+};
+
+static const uint32_t S8[] = {
+       0x08000820, 0x00000800, 0x00020000, 0x08020820,
+       0x08000000, 0x08000820, 0x00000020, 0x08000000,
+       0x00020020, 0x08020000, 0x08020820, 0x00020800,
+       0x08020800, 0x00020820, 0x00000800, 0x00000020,
+       0x08020000, 0x08000020, 0x08000800, 0x00000820,
+       0x00020800, 0x00020020, 0x08020020, 0x08020800,
+       0x00000820, 0x00000000, 0x00000000, 0x08020020,
+       0x08000020, 0x08000800, 0x00020820, 0x00020000,
+       0x00020820, 0x00020000, 0x08020800, 0x00000800,
+       0x00000020, 0x08020020, 0x00000800, 0x00020820,
+       0x08000800, 0x00000020, 0x08000020, 0x08020000,
+       0x08020020, 0x08000000, 0x00020000, 0x08000820,
+       0x00000000, 0x08020820, 0x00020020, 0x08000020,
+       0x08020000, 0x08000800, 0x08000820, 0x00000000,
+       0x08020820, 0x00020800, 0x00020800, 0x00000820,
+       0x00000820, 0x00020020, 0x08000000, 0x08020800
+};
+
+static inline uint32_t
+Fconf(uint32_t r0, uint32_t skl, uint32_t skr)
+{
+       uint32_t r1;
+
+       r1 = (r0 << 16) | (r0 >> 16);
+       return
+                 S1[((r1 >> 11) ^ (skl >> 18)) & 0x3F]
+               | S2[((r0 >> 23) ^ (skl >> 12)) & 0x3F]
+               | S3[((r0 >> 19) ^ (skl >>  6)) & 0x3F]
+               | S4[((r0 >> 15) ^ (skl      )) & 0x3F]
+               | S5[((r0 >> 11) ^ (skr >> 18)) & 0x3F]
+               | S6[((r0 >>  7) ^ (skr >> 12)) & 0x3F]
+               | S7[((r0 >>  3) ^ (skr >>  6)) & 0x3F]
+               | S8[((r1 >> 15) ^ (skr      )) & 0x3F];
+}
+
+static void
+process_block_unit(uint32_t *pl, uint32_t *pr, const uint32_t *skey)
+{
+       int i;
+       uint32_t l, r;
+
+       l = *pl;
+       r = *pr;
+       for (i = 0; i < 16; i ++) {
+               uint32_t t;
+
+               t = l ^ Fconf(r, skey[(i << 1) + 0], skey[(i << 1) + 1]);
+               l = r;
+               r = t;
+       }
+       *pl = r;
+       *pr = l;
+}
+
+/* see inner.h */
+void
+br_des_tab_process_block(unsigned num_rounds, const uint32_t *skey, void *block)
+{
+       unsigned char *buf;
+       uint32_t l, r;
+
+       buf = block;
+       l = br_dec32be(buf);
+       r = br_dec32be(buf + 4);
+       br_des_do_IP(&l, &r);
+       while (num_rounds -- > 0) {
+               process_block_unit(&l, &r, skey);
+               skey += 32;
+       }
+       br_des_do_invIP(&l, &r);
+       br_enc32be(buf, l);
+       br_enc32be(buf + 4, r);
+}
+
+static void
+keysched_unit(uint32_t *skey, const void *key)
+{
+       int i;
+
+       br_des_keysched_unit(skey, key);
+
+       /*
+        * Apply PC-2 to get the 48-bit subkeys.
+        */
+       for (i = 0; i < 16; i ++) {
+               uint32_t xl, xr, ul, ur;
+               int j;
+
+               xl = skey[(i << 1) + 0];
+               xr = skey[(i << 1) + 1];
+               ul = 0;
+               ur = 0;
+               for (j = 0; j < 28; j ++) {
+                       ul |= (xl & 1) << PC2left[j];
+                       ur |= (xr & 1) << PC2right[j];
+                       xl >>= 1;
+                       xr >>= 1;
+               }
+               skey[(i << 1) + 0] = ul;
+               skey[(i << 1) + 1] = ur;
+       }
+}
+
+/* see inner.h */
+unsigned
+br_des_tab_keysched(uint32_t *skey, const void *key, size_t key_len)
+{
+       switch (key_len) {
+       case 8:
+               keysched_unit(skey, key);
+               return 1;
+       case 16:
+               keysched_unit(skey, key);
+               keysched_unit(skey + 32, (const unsigned char *)key + 8);
+               br_des_rev_skey(skey + 32);
+               memcpy(skey + 64, skey, 32 * sizeof *skey);
+               return 3;
+       default:
+               keysched_unit(skey, key);
+               keysched_unit(skey + 32, (const unsigned char *)key + 8);
+               br_des_rev_skey(skey + 32);
+               keysched_unit(skey + 64, (const unsigned char *)key + 16);
+               return 3;
+       }
+}
diff --git a/src/symcipher/des_tab_cbcdec.c b/src/symcipher/des_tab_cbcdec.c
new file mode 100644 (file)
index 0000000..e7eabe9
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_des_tab_cbcdec_init(br_des_tab_cbcdec_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_des_tab_cbcdec_vtable;
+       ctx->num_rounds = br_des_tab_keysched(ctx->skey, key, len);
+       if (len == 8) {
+               br_des_rev_skey(ctx->skey);
+       } else {
+               int i;
+
+               for (i = 0; i < 48; i += 2) {
+                       uint32_t t;
+
+                       t = ctx->skey[i];
+                       ctx->skey[i] = ctx->skey[94 - i];
+                       ctx->skey[94 - i] = t;
+                       t = ctx->skey[i + 1];
+                       ctx->skey[i + 1] = ctx->skey[95 - i];
+                       ctx->skey[95 - i] = t;
+               }
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_des_tab_cbcdec_run(const br_des_tab_cbcdec_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               unsigned char tmp[8];
+               int i;
+
+               memcpy(tmp, buf, 8);
+               br_des_tab_process_block(ctx->num_rounds, ctx->skey, buf);
+               for (i = 0; i < 8; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               memcpy(ivbuf, tmp, 8);
+               buf += 8;
+               len -= 8;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcdec_class br_des_tab_cbcdec_vtable = {
+       sizeof(br_des_tab_cbcdec_keys),
+       8,
+       3,
+       (void (*)(const br_block_cbcdec_class **, const void *, size_t))
+               &br_des_tab_cbcdec_init,
+       (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t))
+               &br_des_tab_cbcdec_run
+};
diff --git a/src/symcipher/des_tab_cbcenc.c b/src/symcipher/des_tab_cbcenc.c
new file mode 100644 (file)
index 0000000..3a45ba3
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_des_tab_cbcenc_init(br_des_tab_cbcenc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_des_tab_cbcenc_vtable;
+       ctx->num_rounds = br_des_tab_keysched(ctx->skey, key, len);
+}
+
+/* see bearssl_block.h */
+void
+br_des_tab_cbcenc_run(const br_des_tab_cbcenc_keys *ctx,
+       void *iv, void *data, size_t len)
+{
+       unsigned char *buf, *ivbuf;
+
+       ivbuf = iv;
+       buf = data;
+       while (len > 0) {
+               int i;
+
+               for (i = 0; i < 8; i ++) {
+                       buf[i] ^= ivbuf[i];
+               }
+               br_des_tab_process_block(ctx->num_rounds, ctx->skey, buf);
+               memcpy(ivbuf, buf, 8);
+               buf += 8;
+               len -= 8;
+       }
+}
+
+/* see bearssl_block.h */
+const br_block_cbcenc_class br_des_tab_cbcenc_vtable = {
+       sizeof(br_des_tab_cbcenc_keys),
+       8,
+       3,
+       (void (*)(const br_block_cbcenc_class **, const void *, size_t))
+               &br_des_tab_cbcenc_init,
+       (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t))
+               &br_des_tab_cbcenc_run
+};
diff --git a/src/x509/asn1.t0 b/src/x509/asn1.t0
new file mode 100644 (file)
index 0000000..9d812df
--- /dev/null
@@ -0,0 +1,579 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+\ =======================================================================
+
+\ This file contains code which is common to all engines that do some
+\ ASN.1 decoding. It should not be compiled on its own, but only along
+\ with another file (e.g. x509_minimal.t0) which uses it.
+\
+\ Users must define several things:
+\
+\ -- In the preamble, a macro called "CTX" that evaluates to the current
+\ context structure.
+\
+\ -- In the preamble, a macro called "CONTEXT_NAME" that evaluates to the
+\ context structure type. This will be invoked during compilation.
+\
+\ -- A word called "read8-low" ( -- x ) that reads the next byte, or -1
+\ if the input buffer is empty. That word is usually written in C.
+\
+\ -- A word called "read-blob-inner" ( addr len -- addr len ) that is
+\ the multi-byte version of read8-low.
+\
+\ -- A word called "skip-remaining-inner" ( lim -- lim ) which reads but
+\ drops some input bytes.
+
+preamble {
+
+#include "inner.h"
+
+}
+
+\ Read next source character, skipping blanks.
+: skip-blanks begin char dup 32 > if ret then drop again ;
+
+: fail-oid
+       "Invalid OID" puts cr exitvm ;
+
+\ Read a decimal integer, followed by either a dot or whitespace.
+\ Note: this does not check for overflows.
+: parse-number ( -- val nextchar )
+       char decval
+       begin
+               char
+               dup dup `. = swap 32 <= or if ret then
+               decval swap 10 * +
+       again ;
+
+\ Encode a number in unsigned 7E format.
+: encode7E ( val -- )
+       0 encode7E-inner ;
+
+: encode7E-inner ( val eb -- )
+       swap dup 0x7F > if
+               dup 7 u>> 0x80 encode7E-inner 0x7F and
+       then
+       or data-add8 ;
+
+\ Decode an OID from source, and encode it. First byte is length,
+\ followed by encoded ASN.1 DER value. The OID is encoded in the
+\ current data block.
+: OID
+       \ Get current data address, and push a 0 for length.
+       current-data 0 data-add8
+       \ Skip blanks and get first digit, which must be 0, 1 or 2.
+       skip-blanks decval dup 2 > if fail-oid then
+       40 *
+       \ Next character must be a dot.
+       char `. <> if fail-oid then
+       \ Second group must be one or two digits.
+       parse-number { nextchar }
+       dup 40 >= if fail-oid then
+       + encode7E
+       \ While next character is a dot, keep encoding numbers.
+       begin nextchar `. = while
+               parse-number >nextchar
+               encode7E
+       repeat
+       \ Write back length in the first byte.
+       dup current-data swap - 1- swap data-set8
+       ; immediate
+
+\ Define a new data word for an encoded OID. The OID is read from the
+\ source.
+: OID:
+       new-data-block next-word define-data-word postpone OID ;
+
+\ Define a word that evaluates to the address of a field within the
+\ context.
+: addr:
+       next-word { field }
+       "addr-" field + 0 1 define-word
+       0 8191 "offsetof(CONTEXT_NAME, " field + ")" + make-CX
+       postpone literal postpone ; ;
+
+addr: pad
+
+\ Define a word that evaluates to an error code through a macro name.
+: err:
+       next-word { name }
+       name 0 1 define-word
+       0 63 "BR_" name + make-CX postpone literal postpone ; ;
+
+err: ERR_X509_INVALID_VALUE
+err: ERR_X509_TRUNCATED
+err: ERR_X509_EMPTY_CHAIN
+err: ERR_X509_INNER_TRUNC
+err: ERR_X509_BAD_TAG_CLASS
+err: ERR_X509_BAD_TAG_VALUE
+err: ERR_X509_INDEFINITE_LENGTH
+err: ERR_X509_EXTRA_ELEMENT
+err: ERR_X509_UNEXPECTED
+err: ERR_X509_NOT_CONSTRUCTED
+err: ERR_X509_NOT_PRIMITIVE
+err: ERR_X509_PARTIAL_BYTE
+err: ERR_X509_BAD_BOOLEAN
+err: ERR_X509_OVERFLOW
+err: ERR_X509_BAD_DN
+err: ERR_X509_BAD_TIME
+err: ERR_X509_UNSUPPORTED
+err: ERR_X509_LIMIT_EXCEEDED
+err: ERR_X509_WRONG_KEY_TYPE
+err: ERR_X509_BAD_SIGNATURE
+err: ERR_X509_EXPIRED
+err: ERR_X509_DN_MISMATCH
+err: ERR_X509_BAD_SERVER_NAME
+err: ERR_X509_CRITICAL_EXTENSION
+err: ERR_X509_NOT_CA
+err: ERR_X509_FORBIDDEN_KEY_USAGE
+err: ERR_X509_WEAK_PUBLIC_KEY
+
+: KEYTYPE_RSA     CX 0 15 { BR_KEYTYPE_RSA } ;
+: KEYTYPE_EC      CX 0 15 { BR_KEYTYPE_EC } ;
+
+cc: fail ( err -- ! ) {
+       CTX->err = T0_POPi();
+       T0_CO();
+}
+
+\ Read one byte from the stream.
+: read8-nc ( -- x )
+       begin
+               read8-low dup 0 >= if ret then
+               drop co
+       again ;
+
+\ Read one byte, enforcing current read limit.
+: read8 ( lim -- lim x )
+       dup ifnot ERR_X509_INNER_TRUNC fail then
+       1- read8-nc ;
+
+\ Read all bytes from the current element, then close it (i.e. drop the
+\ limit). Destination address is an offset within the context.
+: read-blob ( lim addr -- )
+       swap
+       begin dup while read-blob-inner dup if co then repeat
+       2drop ;
+
+\ Skip remaining bytes in the current structure, but do not close it
+\ (thus, this leaves the value 0 on the stack).
+: skip-remaining ( lim -- lim )
+       begin dup while skip-remaining-inner dup if co then repeat ;
+
+: skip-remaining-inner ( lim -- lim )
+       0 over read-blob-inner -rot 2drop ;
+
+cc: set8 ( val addr -- ) {
+       uint32_t addr = T0_POP();
+       *((unsigned char *)CTX + addr) = (unsigned char)T0_POP();
+}
+
+cc: set16 ( val addr -- ) {
+       uint32_t addr = T0_POP();
+       *(uint16_t *)((unsigned char *)CTX + addr) = T0_POP();
+}
+
+cc: set32 ( val addr -- ) {
+       uint32_t addr = T0_POP();
+       *(uint32_t *)((unsigned char *)CTX + addr) = T0_POP();
+}
+
+cc: get8 ( addr -- val ) {
+       uint32_t addr = T0_POP();
+       T0_PUSH(*((unsigned char *)CTX + addr));
+}
+
+cc: get16 ( addr -- val ) {
+       uint32_t addr = T0_POP();
+       T0_PUSH(*(uint16_t *)((unsigned char *)CTX + addr));
+}
+
+cc: get32 ( addr -- val ) {
+       uint32_t addr = T0_POP();
+       T0_PUSH(*(uint32_t *)((unsigned char *)CTX + addr));
+}
+
+\ Read an ASN.1 tag. This function returns the "constructed" status
+\ and the tag value. The constructed status is a boolean (-1 for
+\ constructed, 0 for primitive). The tag value is either 0 to 31 for
+\ a universal tag, or 32+x for a contextual tag of value x. Tag classes
+\ "application" and "private" are rejected. Universal tags beyond 30
+\ are rejected. Contextual tags beyond 30 are rejected. Thus, accepted
+\ tags will necessarily fit on exactly one byte. This does not support
+\ the whole of ASN.1/BER, but is sufficient for certificate parsing.
+: read-tag ( lim -- lim constructed value )
+       read8 { fb }
+
+       \ Constructed flag is bit 5.
+       fb 5 >> 0x01 and neg
+
+       \ Class is in bits 6 and 7. Accepted classes are 00 (universal)
+       \ and 10 (context). We check that bit 6 is 0, and shift back
+       \ bit 7 so that we get 0 (universal) or 32 (context).
+       fb 6 >> dup 0x01 and if ERR_X509_BAD_TAG_CLASS fail then
+       4 <<
+
+       \ Tag value is in bits 0..4. If the value is 31, then this is
+       \ an extended tag, encoded over subsequent bytes, and we do
+       \ not support that.
+       fb 0x1F and dup 0x1F = if ERR_X509_BAD_TAG_VALUE fail then
+       + ;
+
+\ Read a tag, but only if not at the end of the current object. If there
+\ is no room for another element (limit is zero), then this will push a
+\ synthetic "no tag" value (primitive, with value -1).
+: read-tag-or-end ( lim -- lim constructed value )
+       dup ifnot 0 -1 ret then
+       read-tag ;
+
+\ Compare the read tag with the provided value. If equal, then the
+\ element is skipped, and a new tag is read (or end of object).
+: iftag-skip ( lim constructed value ref -- lim constructed value )
+       over = if
+               2drop
+               read-length-open-elt skip-close-elt
+               read-tag-or-end
+       then ;
+
+\ Read an ASN.1 length. This supports only definite lengths (theoretically,
+\ certificates may use an indefinite length for the outer structure, using
+\ DER only in the TBS, but this never happens in practice, except in a
+\ single example certificate from 15 years ago that also fails to decode
+\ properly for other reasons).
+: read-length ( lim -- lim length )
+       read8
+       \ Lengths in 0x00..0x7F get encoded as a single byte.
+       dup 0x80 < if ret then
+
+       \ If the byte is 0x80 then this is an indefinite length, and we
+       \ do not support that.
+       0x80 - dup ifnot ERR_X509_INDEFINITE_LENGTH fail then
+
+       \ Masking out bit 7, this yields the number of bytes over which
+       \ the value is encoded. Since the total certicate length must
+       \ fit over 3 bytes (this is a consequence of SSL/TLS message
+       \ format), we can reject big lengths and keep the length in a
+       \ single integer.
+       { n } 0
+       begin n 0 > while n 1- >n
+               dup 0xFFFFFF > if ERR_X509_INNER_TRUNC fail then
+               8 << swap read8 rot +
+       repeat ;
+
+\ Open a sub-structure. This subtracts the length from the limit, and
+\ pushes the length back as new limit.
+: open-elt ( lim length -- lim_outer lim_inner )
+       dup2 < if ERR_X509_INNER_TRUNC fail then
+       dup { len } - len ;
+
+\ Read a length and open the value as a sub-structure.
+: read-length-open-elt ( lim -- lim_outer lim_inner )
+       read-length open-elt ;
+
+\ Close a sub-structure. This verifies that there is no remaining
+\ element to read.
+: close-elt ( lim -- )
+       if ERR_X509_EXTRA_ELEMENT fail then ;
+
+\ Skip remaining bytes in the current structure, then close it.
+: skip-close-elt ( lim -- )
+       skip-remaining drop ;
+
+\ Read a length and then skip the value.
+: read-length-skip ( lim -- lim )
+       read-length-open-elt skip-close-elt ;
+
+\ Check that a given tag is constructed and has the expected value.
+: check-tag-constructed ( constructed value refvalue -- )
+       = ifnot ERR_X509_UNEXPECTED fail then
+       check-constructed ;
+
+\ Check that the top value is true; report a "not constructed"
+\ error otherwise.
+: check-constructed ( constructed -- )
+       ifnot ERR_X509_NOT_CONSTRUCTED fail then ;
+
+\ Check that a given tag is primitive and has the expected value.
+: check-tag-primitive ( constructed value refvalue -- )
+       = ifnot ERR_X509_UNEXPECTED fail then
+       check-primitive ;
+
+\ Check that the top value is true; report a "not primitive"
+\ error otherwise.
+: check-primitive ( constructed -- )
+       if ERR_X509_NOT_PRIMITIVE fail then ;
+
+\ Check that the tag is for a constructed SEQUENCE.
+: check-sequence ( constructed value -- )
+       0x10 check-tag-constructed ;
+
+\ Read a tag, check that it is for a constructed SEQUENCE, and open
+\ it as a sub-element.
+: read-sequence-open ( lim -- lim_outer lim_inner )
+       read-tag check-sequence read-length-open-elt ;
+
+\ Read the next element as a BIT STRING with no ignore bits, and open
+\ it as a sub-element.
+: read-bits-open ( lim -- lim_outer lim_inner )
+       read-tag 0x03 check-tag-primitive
+       read-length-open-elt
+       read8 if ERR_X509_PARTIAL_BYTE fail then ;
+
+OID: rsaEncryption               1.2.840.113549.1.1.1
+
+OID: sha1WithRSAEncryption       1.2.840.113549.1.1.5
+OID: sha224WithRSAEncryption     1.2.840.113549.1.1.14
+OID: sha256WithRSAEncryption     1.2.840.113549.1.1.11
+OID: sha384WithRSAEncryption     1.2.840.113549.1.1.12
+OID: sha512WithRSAEncryption     1.2.840.113549.1.1.13
+
+OID: id-sha1                     1.3.14.3.2.26
+OID: id-sha224                   2.16.840.1.101.3.4.2.4
+OID: id-sha256                   2.16.840.1.101.3.4.2.1
+OID: id-sha384                   2.16.840.1.101.3.4.2.2
+OID: id-sha512                   2.16.840.1.101.3.4.2.3
+
+OID: id-ecPublicKey              1.2.840.10045.2.1
+
+OID: ansix9p256r1                1.2.840.10045.3.1.7
+OID: ansix9p384r1                1.3.132.0.34
+OID: ansix9p521r1                1.3.132.0.35
+
+OID: ecdsa-with-SHA1             1.2.840.10045.4.1
+OID: ecdsa-with-SHA224           1.2.840.10045.4.3.1
+OID: ecdsa-with-SHA256           1.2.840.10045.4.3.2
+OID: ecdsa-with-SHA384           1.2.840.10045.4.3.3
+OID: ecdsa-with-SHA512           1.2.840.10045.4.3.4
+
+\ Read a "small value". This assumes that the tag has just been read
+\ and processed, but not the length. The first pad byte is set to the
+\ value length; the encoded value iself follows. If the value length
+\ exceeds 255 bytes, then a single 0 is written in the pad, and this
+\ method returns false (0). Otherwise, it returns true (-1).
+\ Either way, the element is fully read.
+: read-small-value ( lim -- lim bool )
+       read-length-open-elt
+       dup 255 > if skip-close-elt 0 addr-pad set8 0 ret then
+       dup addr-pad set8
+       addr-pad 1+ read-blob
+       -1 ;
+
+\ Read an OID as a "small value" (tag, length and value). A boolean
+\ value is returned, which is true (-1) if the OID value fits on the pad,
+\ false (0) otherwise.
+: read-OID ( lim -- lim bool )
+       read-tag 0x06 check-tag-primitive read-small-value ;
+
+\ Read a value and interpret it as an INTEGER or ENUMERATED value. If
+\ the integer value does not fit on an unsigned 32-bit value, an error
+\ is reported. This function assumes that the tag has just been read
+\ and processed, but not the length.
+: read-small-int-value ( lim -- lim x )
+       read-length-open-elt
+       dup ifnot ERR_X509_OVERFLOW fail then
+       read8 dup 0x80 >= if ERR_X509_OVERFLOW fail then
+       { x }
+       begin dup while
+               read8 x dup 0xFFFFFF >= if ERR_X509_OVERFLOW fail then
+               8 << + >x
+       repeat
+       drop x ;
+
+\ Compare the OID in the pad with an OID in the constant data block.
+\ Returned value is -1 on equality, 0 otherwise.
+cc: eqOID ( addrConst -- bool ) {
+       const unsigned char *a2 = &t0_datablock[T0_POP()];
+       const unsigned char *a1 = &CTX->pad[0];
+       size_t len = a1[0];
+       int x;
+       if (len == a2[0]) {
+               x = -(memcmp(a1 + 1, a2 + 1, len) == 0);
+       } else {
+               x = 0;
+       }
+       T0_PUSH((uint32_t)x);
+}
+
+\ Compare two blobs in the context. Returned value is -1 on equality, 0
+\ otherwise.
+cc: eqblob ( addr1 addr2 len -- bool ) {
+       size_t len = T0_POP();
+       const unsigned char *a2 = (const unsigned char *)CTX + T0_POP();
+       const unsigned char *a1 = (const unsigned char *)CTX + T0_POP();
+       T0_PUSHi(-(memcmp(a1, a2, len) == 0));
+}
+
+\ Check that a value is in a given range (inclusive).
+: between? ( x min max -- bool )
+       { min max } dup min >= swap max <= and ;
+
+\ Convert the provided byte value into a number in the 0..9 range,
+\ assuming that it is an ASCII digit. A non-digit triggers an error
+\ (a "bad time" error since this is used in date/time decoding).
+: digit-dec ( char -- value )
+       `0 - dup 0 9 between? ifnot ERR_X509_BAD_TIME fail then ;
+
+\ Read two ASCII digits and return the value in the 0..99 range. An
+\ error is reported if the characters are not ASCII digits.
+: read-dec2 ( lim -- lim x )
+       read8 digit-dec 10 * { x } read8 digit-dec x + ;
+
+\ Read two ASCII digits and check that the value is in the provided
+\ range (inclusive).
+: read-dec2-range ( lim min max -- lim x )
+       { min max }
+       read-dec2 dup min max between? ifnot ERR_X509_BAD_TIME fail then ;
+
+\ Maximum days in a month and accumulated day count. Each
+\ 16-bit value contains the month day count in its lower 5 bits. The first
+\ 12 values are for a normal year, the other 12 for a leap year.
+data: month-to-days
+hexb| 001F 03FC 077F 0B5E 0F1F 12FE 16BF 1A9F 1E7E 223F 261E 29DF |
+hexb| 001F 03FD 079F 0B7E 0F3F 131E 16DF 1ABF 1E9E 225F 263E 29FF |
+
+\ Read a date (UTCTime or GeneralizedTime). The date value is converted
+\ to a day count and a second count. The day count starts at 0 for
+\ January 1st, 0 AD (that's they year before 1 AD, also known as 1 BC)
+\ in a proleptic Gregorian calendar (i.e. Gregorian rules are assumed to
+\ extend indefinitely in the past). The second count is between 0 and
+\ 86400 (inclusive, in case of a leap second).
+: read-date ( lim -- lim days seconds )
+       \ Read tag; must be UTCTime or GeneralizedTime. Year count is
+       \ 4 digits with GeneralizedTime, 2 digits with UTCTime.
+       read-tag
+       dup 0x17 0x18 between? ifnot ERR_X509_BAD_TIME fail then
+       0x18 = { y4d }
+       check-primitive
+       read-length-open-elt
+
+       \ We compute the days and seconds counts during decoding, in
+       \ order to minimize the number of needed temporary variables.
+       { ; days seconds x }
+
+       \ Year is 4-digit with GeneralizedTime. With UTCTime, the year
+       \ is in the 1950..2049 range, and only the last two digits are
+       \ present in the encoding.
+       read-dec2
+       y4d if
+               100 * >x read-dec2 x +
+       else
+               dup 50 < if 100 + then 1900 +
+       then
+       >x
+       x 365 * x 3 + 4 / + x 99 + 100 / - x 399 + 400 / + >days
+
+       \ Month is 1..12. Number of days in a months depend on the
+       \ month and on the year (year count is in x at that point).
+       1 12 read-dec2-range
+       1- 1 <<
+       x 4 % 0= x 100 % 0<> x 400 % 0= or and if 24 + then
+       month-to-days + data-get16
+       dup 5 >> days + >days
+       0x1F and
+
+       \ Day. At this point, the TOS contains the maximum day count for
+       \ the current month.
+       1 swap read-dec2-range
+       days + 1- >days
+
+       \ Hour, minute and seconds. Count of seconds is allowed to go to
+       \ 60 in case of leap seconds (in practice, leap seconds really
+       \ occur only at the very end of the day, so this computation is
+       \ exact for a real leap second, and a spurious leap second only
+       \ implies a one-second shift that we can ignore).
+       0 23 read-dec2-range 3600 * >seconds
+       0 59 read-dec2-range 60 * seconds + >seconds
+       0 60 read-dec2-range seconds + >seconds
+
+       \ At this point, we may have fractional seconds. This should
+       \ happen only with GeneralizedTime, but we accept it for UTCTime
+       \ too (and, anyway, we ignore these fractional seconds).
+       read8 dup `. = if
+               drop
+               begin read8 dup `0 `9 between? while drop repeat
+       then
+
+       \ The time zone should be 'Z', not followed by anything. Other
+       \ time zone indications are not DER and thus not supposed to
+       \ appear in certificates.
+       `Z <> if ERR_X509_BAD_TIME fail then
+       close-elt
+       days seconds ;
+
+\ Read an INTEGER (tag, length and value). The INTEGER is supposed to be
+\ positive; its unsigned big-endian encoding is stored in the provided
+\ in-context buffer. Returned value is the decoded length. If the integer
+\ did not fit, or the value is negative, then an error is reported.
+: read-integer ( lim addr len -- lim dlen )
+       rot read-tag 0x02 check-tag-primitive -rot
+       read-integer-next ;
+
+\ Identical to read-integer, but the tag has already been read and checked.
+: read-integer-next ( lim addr len -- lim dlen )
+       dup { addr len origlen }
+       read-length-open-elt
+       \ Read first byte; sign bit must be 0.
+       read8 dup 0x80 >= if ERR_X509_OVERFLOW fail then
+       \ Skip leading bytes of value 0. If there are only bytes of
+       \ value 0, then return.
+       begin dup 0 = while
+               drop dup ifnot drop 0 ret then
+               read8
+       repeat
+       \ At that point, we have the first non-zero byte on the stack.
+       begin
+               len dup ifnot ERR_X509_LIMIT_EXCEEDED fail then 1- >len
+               addr set8 addr 1+ >addr
+               dup while read8
+       repeat
+       drop origlen len - ;
+
+\ Read a BOOLEAN value. This should be called immediately after reading
+\ the tag.
+: read-boolean ( lim constructed value -- lim bool )
+       0x01 check-tag-primitive
+       read-length 1 <> if ERR_X509_BAD_BOOLEAN fail then
+       read8 0<> ;
+
+\ Identify an elliptic curve: read the OID, then check it against the
+\ known curve OID.
+: read-curve-ID ( lim -- lim curve )
+       read-OID ifnot ERR_X509_UNSUPPORTED fail then
+       choice
+               ansix9p256r1 eqOID uf 23 enduf
+               ansix9p384r1 eqOID uf 24 enduf
+               ansix9p521r1 eqOID uf 25 enduf
+               ERR_X509_UNSUPPORTED fail
+       endchoice ;
+
+\ A convenient debug word: print the current data stack contents.
+cc: DEBUG ( -- ) {
+       extern int printf(const char *fmt, ...);
+       uint32_t *p;
+
+       printf("<stack:");
+       for (p = &CTX->dp_stack[0]; p != dp; p ++) {
+               printf(" %lu", (unsigned long)*p);
+       }
+       printf(" >\n");
+}
diff --git a/src/x509/skey_decoder.c b/src/x509/skey_decoder.c
new file mode 100644 (file)
index 0000000..6788145
--- /dev/null
@@ -0,0 +1,646 @@
+/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+
+
+void br_skey_decoder_init_main(void *t0ctx);
+
+void br_skey_decoder_run(void *t0ctx);
+
+
+
+#include "inner.h"
+
+
+
+
+
+#include "inner.h"
+
+#define CTX   ((br_skey_decoder_context *)((unsigned char *)t0ctx - offsetof(br_skey_decoder_context, cpu)))
+#define CONTEXT_NAME   br_skey_decoder_context
+
+/* see bearssl_x509.h */
+void
+br_skey_decoder_init(br_skey_decoder_context *ctx)
+{
+       memset(ctx, 0, sizeof *ctx);
+       ctx->cpu.dp = &ctx->dp_stack[0];
+       ctx->cpu.rp = &ctx->rp_stack[0];
+       br_skey_decoder_init_main(&ctx->cpu);
+       br_skey_decoder_run(&ctx->cpu);
+}
+
+/* see bearssl_x509.h */
+void
+br_skey_decoder_push(br_skey_decoder_context *ctx,
+       const void *data, size_t len)
+{
+       ctx->hbuf = data;
+       ctx->hlen = len;
+       br_skey_decoder_run(&ctx->cpu);
+}
+
+
+
+static const uint8_t t0_datablock[] = {
+       0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x07,
+       0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE,
+       0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B,
+       0x81, 0x04, 0x00, 0x23
+};
+
+static const uint8_t t0_codeblock[] = {
+       0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x13,
+       0x13, 0x00, 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00,
+       0x01, T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INVALID_VALUE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_data)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_type)), 0x00, 0x00,
+       0x33, 0x48, 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, pad)),
+       0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, 0x1C, 0x00, 0x00, 0x01, 0x22,
+       0x00, 0x00, 0x05, 0x02, 0x2C, 0x16, 0x00, 0x00, 0x06, 0x02, 0x2D, 0x16,
+       0x00, 0x00, 0x01, 0x10, 0x3D, 0x00, 0x00, 0x0D, 0x05, 0x02, 0x2F, 0x16,
+       0x3A, 0x00, 0x00, 0x0D, 0x05, 0x02, 0x2F, 0x16, 0x3B, 0x00, 0x00, 0x06,
+       0x02, 0x27, 0x16, 0x00, 0x01, 0x03, 0x00, 0x54, 0x57, 0x01, 0x02, 0x3E,
+       0x55, 0x23, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x04, 0x3E, 0x02, 0x00,
+       0x41, 0x3F, 0x00, 0x02, 0x03, 0x00, 0x53, 0x14, 0x14, 0x03, 0x01, 0x48,
+       0x0E, 0x06, 0x02, 0x30, 0x16, 0x33, 0x4C, 0x58, 0x01, 0x7F, 0x19, 0x0D,
+       0x06, 0x04, 0x13, 0x13, 0x04, 0x29, 0x01, 0x20, 0x19, 0x0D, 0x06, 0x16,
+       0x13, 0x3A, 0x53, 0x4D, 0x02, 0x00, 0x06, 0x09, 0x02, 0x00, 0x0C, 0x06,
+       0x02, 0x2A, 0x16, 0x04, 0x02, 0x03, 0x00, 0x3F, 0x04, 0x0D, 0x01, 0x21,
+       0x19, 0x0D, 0x06, 0x04, 0x13, 0x3A, 0x04, 0x03, 0x30, 0x16, 0x13, 0x5D,
+       0x02, 0x00, 0x05, 0x02, 0x30, 0x16, 0x02, 0x00, 0x02, 0x01, 0x1D, 0x00,
+       0x02, 0x53, 0x4B, 0x05, 0x02, 0x30, 0x16, 0x5B, 0x15, 0x06, 0x07, 0x5D,
+       0x01, 0x7F, 0x03, 0x01, 0x04, 0x16, 0x46, 0x15, 0x06, 0x10, 0x01, 0x00,
+       0x03, 0x01, 0x14, 0x06, 0x03, 0x4D, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00,
+       0x04, 0x02, 0x30, 0x16, 0x3F, 0x57, 0x01, 0x04, 0x3E, 0x53, 0x02, 0x01,
+       0x06, 0x03, 0x43, 0x04, 0x03, 0x02, 0x00, 0x40, 0x3F, 0x5D, 0x02, 0x01,
+       0x06, 0x03, 0x32, 0x04, 0x01, 0x31, 0x00, 0x00, 0x54, 0x57, 0x01, 0x02,
+       0x3E, 0x55, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x02, 0x3E, 0x44, 0x3F,
+       0x00, 0x07, 0x35, 0x50, 0x14, 0x05, 0x02, 0x2F, 0x16, 0x23, 0x01, 0x03,
+       0x0B, 0x33, 0x17, 0x47, 0x07, 0x03, 0x00, 0x4F, 0x4F, 0x35, 0x4E, 0x14,
+       0x14, 0x03, 0x01, 0x03, 0x02, 0x51, 0x14, 0x03, 0x03, 0x02, 0x02, 0x07,
+       0x14, 0x03, 0x02, 0x51, 0x14, 0x03, 0x04, 0x02, 0x02, 0x07, 0x14, 0x03,
+       0x02, 0x51, 0x14, 0x03, 0x05, 0x02, 0x02, 0x07, 0x14, 0x03, 0x02, 0x51,
+       0x03, 0x06, 0x02, 0x00, 0x02, 0x01, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05,
+       0x02, 0x06, 0x1E, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x01, 0x0B, 0x00,
+       0x00, 0x01, 0x00, 0x20, 0x14, 0x06, 0x08, 0x01, 0x01, 0x21, 0x20, 0x22,
+       0x20, 0x04, 0x75, 0x13, 0x00, 0x00, 0x01,
+       T0_INT2(3 * BR_X509_BUFSIZE_KEY), 0x00, 0x01, 0x01, 0x87, 0xFF, 0xFF,
+       0x7F, 0x54, 0x57, 0x01, 0x02, 0x3E, 0x55, 0x01, 0x01, 0x0E, 0x06, 0x02,
+       0x30, 0x16, 0x57, 0x01, 0x02, 0x19, 0x0D, 0x06, 0x06, 0x13, 0x3B, 0x44,
+       0x32, 0x04, 0x1C, 0x01, 0x04, 0x19, 0x0D, 0x06, 0x08, 0x13, 0x3B, 0x01,
+       0x00, 0x41, 0x31, 0x04, 0x0E, 0x01, 0x10, 0x19, 0x0D, 0x06, 0x05, 0x13,
+       0x3A, 0x42, 0x04, 0x03, 0x30, 0x16, 0x13, 0x03, 0x00, 0x3F, 0x02, 0x00,
+       0x34, 0x1F, 0x5A, 0x27, 0x16, 0x00, 0x01, 0x45, 0x0A, 0x06, 0x02, 0x29,
+       0x16, 0x14, 0x03, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x57, 0x01, 0x06,
+       0x3E, 0x56, 0x00, 0x00, 0x20, 0x14, 0x06, 0x07, 0x1A, 0x14, 0x06, 0x01,
+       0x12, 0x04, 0x76, 0x24, 0x00, 0x00, 0x4B, 0x05, 0x02, 0x30, 0x16, 0x37,
+       0x15, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x38, 0x15, 0x06, 0x04, 0x01,
+       0x18, 0x04, 0x0A, 0x39, 0x15, 0x06, 0x04, 0x01, 0x19, 0x04, 0x02, 0x30,
+       0x16, 0x00, 0x00, 0x1C, 0x57, 0x01, 0x02, 0x3E, 0x09, 0x50, 0x00, 0x00,
+       0x35, 0x4E, 0x13, 0x00, 0x03, 0x14, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+       0x53, 0x59, 0x14, 0x01, 0x81, 0x00, 0x0F, 0x06, 0x02, 0x2E, 0x16, 0x14,
+       0x01, 0x00, 0x0D, 0x06, 0x0B, 0x13, 0x14, 0x05, 0x04, 0x13, 0x01, 0x00,
+       0x00, 0x59, 0x04, 0x6F, 0x02, 0x01, 0x14, 0x05, 0x02, 0x2B, 0x16, 0x23,
+       0x03, 0x01, 0x02, 0x02, 0x1F, 0x02, 0x02, 0x22, 0x03, 0x02, 0x14, 0x06,
+       0x03, 0x59, 0x04, 0x68, 0x13, 0x02, 0x00, 0x02, 0x01, 0x08, 0x00, 0x00,
+       0x14, 0x35, 0x1C, 0x08, 0x20, 0x1C, 0x07, 0x20, 0x4E, 0x00, 0x01, 0x59,
+       0x14, 0x01, 0x81, 0x00, 0x0A, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x08,
+       0x14, 0x05, 0x02, 0x28, 0x16, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01,
+       0x00, 0x0E, 0x06, 0x19, 0x02, 0x00, 0x23, 0x03, 0x00, 0x14, 0x01, 0x87,
+       0xFF, 0xFF, 0x7F, 0x0E, 0x06, 0x02, 0x29, 0x16, 0x01, 0x08, 0x0B, 0x20,
+       0x59, 0x1C, 0x07, 0x04, 0x60, 0x00, 0x00, 0x52, 0x4A, 0x00, 0x00, 0x57,
+       0x3C, 0x53, 0x00, 0x01, 0x53, 0x14, 0x05, 0x02, 0x2E, 0x16, 0x59, 0x14,
+       0x01, 0x81, 0x00, 0x0F, 0x06, 0x02, 0x2E, 0x16, 0x03, 0x00, 0x14, 0x06,
+       0x16, 0x59, 0x02, 0x00, 0x14, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x0F, 0x06,
+       0x02, 0x2E, 0x16, 0x01, 0x08, 0x0B, 0x07, 0x03, 0x00, 0x04, 0x67, 0x13,
+       0x02, 0x00, 0x00, 0x00, 0x53, 0x14, 0x01, 0x81, 0x7F, 0x0E, 0x06, 0x08,
+       0x5C, 0x01, 0x00, 0x36, 0x1F, 0x01, 0x00, 0x00, 0x14, 0x36, 0x1F, 0x36,
+       0x22, 0x4C, 0x01, 0x7F, 0x00, 0x01, 0x59, 0x03, 0x00, 0x02, 0x00, 0x01,
+       0x05, 0x10, 0x01, 0x01, 0x11, 0x18, 0x02, 0x00, 0x01, 0x06, 0x10, 0x14,
+       0x01, 0x01, 0x11, 0x06, 0x02, 0x25, 0x16, 0x01, 0x04, 0x0B, 0x02, 0x00,
+       0x01, 0x1F, 0x11, 0x14, 0x01, 0x1F, 0x0D, 0x06, 0x02, 0x26, 0x16, 0x07,
+       0x00, 0x00, 0x14, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x57, 0x00,
+       0x00, 0x14, 0x05, 0x02, 0x29, 0x16, 0x23, 0x5A, 0x00, 0x00, 0x1B, 0x14,
+       0x01, 0x00, 0x0F, 0x06, 0x01, 0x00, 0x13, 0x12, 0x04, 0x74, 0x00, 0x01,
+       0x01, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, 0x14, 0x06, 0x07, 0x5E, 0x14,
+       0x06, 0x01, 0x12, 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x19, 0x1A, 0x09,
+       0x24, 0x00
+};
+
+static const uint16_t t0_caddr[] = {
+       0,
+       5,
+       10,
+       14,
+       18,
+       22,
+       26,
+       30,
+       34,
+       38,
+       42,
+       46,
+       50,
+       54,
+       58,
+       62,
+       66,
+       70,
+       75,
+       80,
+       84,
+       89,
+       93,
+       97,
+       101,
+       107,
+       113,
+       118,
+       126,
+       134,
+       140,
+       163,
+       244,
+       311,
+       329,
+       404,
+       408,
+       412,
+       429,
+       434,
+       505,
+       519,
+       526,
+       540,
+       573,
+       582,
+       587,
+       654,
+       665,
+       721,
+       725,
+       730,
+       778,
+       804,
+       848,
+       859,
+       868,
+       881,
+       885,
+       889,
+       901
+};
+
+#define T0_INTERPRETED   34
+
+#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)
+
+#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}
+
+T0_DEFENTRY(br_skey_decoder_init_main, 73)
+
+void
+br_skey_decoder_run(void *t0ctx)
+{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 7: {
+                               /* + */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+
+                               }
+                               break;
+                       case 8: {
+                               /* - */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+
+                               }
+                               break;
+                       case 9: {
+                               /* -rot */
+ T0_NROT(); 
+                               }
+                               break;
+                       case 10: {
+                               /* < */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+
+                               }
+                               break;
+                       case 11: {
+                               /* << */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+
+                               }
+                               break;
+                       case 12: {
+                               /* <> */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+
+                               }
+                               break;
+                       case 13: {
+                               /* = */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+
+                               }
+                               break;
+                       case 14: {
+                               /* > */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a > b));
+
+                               }
+                               break;
+                       case 15: {
+                               /* >= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+
+                               }
+                               break;
+                       case 16: {
+                               /* >> */
+
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+
+                               }
+                               break;
+                       case 17: {
+                               /* and */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+
+                               }
+                               break;
+                       case 18: {
+                               /* co */
+ T0_CO(); 
+                               }
+                               break;
+                       case 19: {
+                               /* drop */
+ (void)T0_POP(); 
+                               }
+                               break;
+                       case 20: {
+                               /* dup */
+ T0_PUSH(T0_PEEK(0)); 
+                               }
+                               break;
+                       case 21: {
+                               /* eqOID */
+
+       const unsigned char *a2 = &t0_datablock[T0_POP()];
+       const unsigned char *a1 = &CTX->pad[0];
+       size_t len = a1[0];
+       int x;
+       if (len == a2[0]) {
+               x = -(memcmp(a1 + 1, a2 + 1, len) == 0);
+       } else {
+               x = 0;
+       }
+       T0_PUSH((uint32_t)x);
+
+                               }
+                               break;
+                       case 22: {
+                               /* fail */
+
+       CTX->err = T0_POPi();
+       T0_CO();
+
+                               }
+                               break;
+                       case 23: {
+                               /* get8 */
+
+       uint32_t addr = T0_POP();
+       T0_PUSH(*((unsigned char *)CTX + addr));
+
+                               }
+                               break;
+                       case 24: {
+                               /* neg */
+
+       uint32_t a = T0_POP();
+       T0_PUSH(-a);
+
+                               }
+                               break;
+                       case 25: {
+                               /* over */
+ T0_PUSH(T0_PEEK(1)); 
+                               }
+                               break;
+                       case 26: {
+                               /* read-blob-inner */
+
+       uint32_t len = T0_POP();
+       uint32_t addr = T0_POP();
+       size_t clen = CTX->hlen;
+       if (clen > len) {
+               clen = (size_t)len;
+       }
+       if (addr != 0) {
+               memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen);
+       }
+       CTX->hbuf += clen;
+       CTX->hlen -= clen;
+       T0_PUSH(addr + clen);
+       T0_PUSH(len - clen);
+
+                               }
+                               break;
+                       case 27: {
+                               /* read8-low */
+
+       if (CTX->hlen == 0) {
+               T0_PUSHi(-1);
+       } else {
+               CTX->hlen --;
+               T0_PUSH(*CTX->hbuf ++);
+       }
+
+                               }
+                               break;
+                       case 28: {
+                               /* rot */
+ T0_ROT(); 
+                               }
+                               break;
+                       case 29: {
+                               /* set-ec-key */
+
+       size_t xlen = T0_POP();
+       uint32_t curve = T0_POP();
+       CTX->key.ec.curve = curve;
+       CTX->key.ec.x = CTX->key_data;
+       CTX->key.ec.xlen = xlen;
+
+                               }
+                               break;
+                       case 30: {
+                               /* set-rsa-key */
+
+       size_t iqlen = T0_POP();
+       size_t dqlen = T0_POP();
+       size_t dplen = T0_POP();
+       size_t qlen = T0_POP();
+       size_t plen = T0_POP();
+       uint32_t n_bitlen = T0_POP();
+       size_t off;
+
+       CTX->key.rsa.n_bitlen = n_bitlen;
+       CTX->key.rsa.p = CTX->key_data;
+       CTX->key.rsa.plen = plen;
+       off = plen;
+       CTX->key.rsa.q = CTX->key_data + off;
+       CTX->key.rsa.qlen = qlen;
+       off += qlen;
+       CTX->key.rsa.dp = CTX->key_data + off;
+       CTX->key.rsa.dplen = dplen;
+       off += dplen;
+       CTX->key.rsa.dq = CTX->key_data + off;
+       CTX->key.rsa.dqlen = dqlen;
+       off += dqlen;
+       CTX->key.rsa.iq = CTX->key_data + off;
+       CTX->key.rsa.iqlen = iqlen;
+
+                               }
+                               break;
+                       case 31: {
+                               /* set8 */
+
+       uint32_t addr = T0_POP();
+       *((unsigned char *)CTX + addr) = (unsigned char)T0_POP();
+
+                               }
+                               break;
+                       case 32: {
+                               /* swap */
+ T0_SWAP(); 
+                               }
+                               break;
+                       case 33: {
+                               /* u>> */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x >> c);
+
+                               }
+                               break;
+                       }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}
diff --git a/src/x509/skey_decoder.t0 b/src/x509/skey_decoder.t0
new file mode 100644 (file)
index 0000000..336b932
--- /dev/null
@@ -0,0 +1,373 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+preamble {
+
+#include "inner.h"
+
+#define CTX   ((br_skey_decoder_context *)((unsigned char *)t0ctx - offsetof(br_skey_decoder_context, cpu)))
+#define CONTEXT_NAME   br_skey_decoder_context
+
+/* see bearssl_x509.h */
+void
+br_skey_decoder_init(br_skey_decoder_context *ctx)
+{
+       memset(ctx, 0, sizeof *ctx);
+       ctx->cpu.dp = &ctx->dp_stack[0];
+       ctx->cpu.rp = &ctx->rp_stack[0];
+       br_skey_decoder_init_main(&ctx->cpu);
+       br_skey_decoder_run(&ctx->cpu);
+}
+
+/* see bearssl_x509.h */
+void
+br_skey_decoder_push(br_skey_decoder_context *ctx,
+       const void *data, size_t len)
+{
+       ctx->hbuf = data;
+       ctx->hlen = len;
+       br_skey_decoder_run(&ctx->cpu);
+}
+
+}
+
+addr: key_type
+addr: key_data
+
+cc: read8-low ( -- x ) {
+       if (CTX->hlen == 0) {
+               T0_PUSHi(-1);
+       } else {
+               CTX->hlen --;
+               T0_PUSH(*CTX->hbuf ++);
+       }
+}
+
+cc: read-blob-inner ( addr len -- addr len ) {
+       uint32_t len = T0_POP();
+       uint32_t addr = T0_POP();
+       size_t clen = CTX->hlen;
+       if (clen > len) {
+               clen = (size_t)len;
+       }
+       if (addr != 0) {
+               memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen);
+       }
+       CTX->hbuf += clen;
+       CTX->hlen -= clen;
+       T0_PUSH(addr + clen);
+       T0_PUSH(len - clen);
+}
+
+\ Get the length of the key_data buffer.
+: len-key_data
+       CX 0 8191 { 3 * BR_X509_BUFSIZE_KEY } ;
+
+\ Get the address and length for the key_data buffer.
+: addr-len-key_data ( -- addr len )
+       addr-key_data len-key_data ;
+
+\ Set the private key (RSA).
+cc: set-rsa-key ( n_bitlen plen qlen dplen dqlen iqlen -- ) {
+       size_t iqlen = T0_POP();
+       size_t dqlen = T0_POP();
+       size_t dplen = T0_POP();
+       size_t qlen = T0_POP();
+       size_t plen = T0_POP();
+       uint32_t n_bitlen = T0_POP();
+       size_t off;
+
+       CTX->key.rsa.n_bitlen = n_bitlen;
+       CTX->key.rsa.p = CTX->key_data;
+       CTX->key.rsa.plen = plen;
+       off = plen;
+       CTX->key.rsa.q = CTX->key_data + off;
+       CTX->key.rsa.qlen = qlen;
+       off += qlen;
+       CTX->key.rsa.dp = CTX->key_data + off;
+       CTX->key.rsa.dplen = dplen;
+       off += dplen;
+       CTX->key.rsa.dq = CTX->key_data + off;
+       CTX->key.rsa.dqlen = dqlen;
+       off += dqlen;
+       CTX->key.rsa.iq = CTX->key_data + off;
+       CTX->key.rsa.iqlen = iqlen;
+}
+
+\ Set the private key (EC).
+cc: set-ec-key ( curve xlen -- ) {
+       size_t xlen = T0_POP();
+       uint32_t curve = T0_POP();
+       CTX->key.ec.curve = curve;
+       CTX->key.ec.x = CTX->key_data;
+       CTX->key.ec.xlen = xlen;
+}
+
+\ Get the bit length for an integer (unsigned).
+: int-bit-length ( x -- bitlen )
+       0 swap
+       begin dup while 1 u>> swap 1+ swap repeat
+       drop ;
+
+\ Read an INTEGER into the key_data buffer, but then ignore it.
+: read-integer-ignore ( lim -- lim )
+       addr-len-key_data read-integer drop ;
+
+\ Read an INTEGER into the key_data buffer, at the provided offset.
+\ Returned value is the integer length (in bytes).
+: read-integer-off ( lim off -- lim dlen )
+       dup addr-len-key_data rot - swap rot + swap read-integer ;
+
+\ Decode RSA key, starting with the SEQUENCE tag.
+: decode-RSA ( lim -- lim )
+       read-sequence-open
+
+       \ Version should be 0.
+       read-tag 0x02 check-tag-primitive read-small-int-value if
+               ERR_X509_UNSUPPORTED fail
+       then
+
+       \ Read tag for the modulus; should be INTEGER. Then use the
+       \ decode-RSA-next function for the remainder of the key.
+       read-tag 0x02 check-tag-primitive
+       decode-RSA-next
+
+       \ Close the SEQUENCE.
+       close-elt ;
+
+\ Decode RSA key; the version, and the tag for the modulus, have been
+\ read.
+: decode-RSA-next ( lim -- lim )
+       \ Modulus: we read it but we do not keep it; we merely gather
+       \ the modulus bit length.
+       addr-len-key_data read-integer-next
+       dup ifnot ERR_X509_UNEXPECTED fail then
+       1- 3 << addr-key_data get8 int-bit-length + { n_bitlen }
+
+       \ Public exponent: read but skip.
+       read-integer-ignore
+
+       \ Private exponent: read but skip.
+       read-integer-ignore
+
+       \ First prime factor.
+       addr-len-key_data read-integer dup dup { off plen }
+
+       \ Second prime factor.
+       read-integer-off dup { qlen } off + dup >off
+
+       \ First reduced private exponent.
+       read-integer-off dup { dplen } off + dup >off
+
+       \ Second reduced private exponent.
+       read-integer-off dup { dqlen } off + dup >off
+
+       \ CRT coefficient.
+       read-integer-off { iqlen }
+
+       \ Set RSA key.
+       n_bitlen plen qlen dplen dqlen iqlen set-rsa-key
+
+       \ The caller will close the sequence, thereby validating that there
+       \ is no extra field.
+       ;
+
+\ Decode an EC key, starting with the SEQUENCE tag.
+: decode-EC ( lim curve -- lim )
+       { curve }
+       read-sequence-open
+
+       \ Version should be 1.
+       read-tag 0x02 check-tag-primitive read-small-int-value 1- if
+               ERR_X509_UNSUPPORTED fail
+       then
+
+       \ Read tag for the private key; should be OCTET STRING. Then use the
+       \ decode-EC-next function for the remainder of the key.
+       read-tag 0x04 check-tag-primitive
+       curve decode-EC-next
+
+       \ Close the SEQUENCE.
+       close-elt ;
+
+\ Decode an EC key; the version, and the tag for the OCTET STRING, have
+\ already been read. The curve ID is provided (0 if unknown).
+: decode-EC-next ( lim curve -- lim )
+       { curve }
+
+       \ Read the private key proper.
+       read-length-open-elt
+       dup dup { xlen } len-key_data > if ERR_X509_UNSUPPORTED fail then
+       addr-key_data read-blob
+
+       \ Next element might be the curve identifier.
+       read-tag-or-end
+       case
+
+               \ End of structure.
+               -1 of drop endof
+
+               \ Curve parameters; we support only named curves.
+               0x20 of
+                       check-constructed read-length-open-elt
+                       read-curve-ID
+                       curve if
+                               curve <> if ERR_X509_INVALID_VALUE fail then
+                       else
+                               >curve
+                       then
+                       close-elt
+               endof
+
+               \ Public key. We ignore it.
+               0x21 of check-constructed endof
+
+               ERR_X509_UNSUPPORTED fail
+       endcase
+       skip-remaining
+
+       \ The curve must have been defined one way or another.
+       curve ifnot ERR_X509_UNSUPPORTED fail then
+
+       \ Set the EC key.
+       curve xlen set-ec-key
+
+       \ The caller will close the sequence.
+       ;
+
+\ Decode a PKCS#8 object. The version and the tag for the AlgorithmIdentifier
+\ structure have already been read. This function returns the key type.
+: decode-PKCS8-next ( lim -- lim keytype )
+       \ Decode the AlgorithmIdentifier.
+       read-length-open-elt
+       read-OID ifnot ERR_X509_UNSUPPORTED fail then
+       { ; is-rsa curve }
+       choice
+               rsaEncryption eqOID uf
+                       \ RSA private key. We ignore the parameters.
+                       skip-remaining -1 >is-rsa
+               enduf
+               id-ecPublicKey eqOID uf
+                       \ EC private key. Parameters, if present, shall
+                       \ identify the curve.
+                       0 >is-rsa
+                       dup if read-curve-ID else 0 then >curve
+               enduf
+
+               ERR_X509_UNSUPPORTED fail
+       endchoice
+       close-elt
+
+       \ Open private key value and decode it.
+       read-tag 0x04 check-tag-primitive
+       read-length-open-elt
+       is-rsa if
+               decode-RSA
+       else
+               curve decode-EC
+       then
+       close-elt
+
+       \ We ignore any extra field, i.e. attributes or public key.
+       skip-remaining
+
+       \ Return the key type.
+       is-rsa if KEYTYPE_RSA else KEYTYPE_EC then
+       ;
+
+\ Decode a private key.
+: main ( -- ! )
+       \ RSA private key format is defined in PKCS#1 (RFC 3447):
+       \   RSAPrivateKey ::= SEQUENCE {
+       \       version   INTEGER, -- 0 or 1
+       \       n         INTEGER,
+       \       e         INTEGER,
+       \       d         INTEGER,
+       \       p         INTEGER,
+       \       q         INTEGER,
+       \       dp        INTEGER,
+       \       dq        INTEGER,
+       \       iq        INTEGER,
+       \       other     OtherPrimeInfos OPTIONAL
+       \   }
+       \ We do not support keys with more than two primes (these have
+       \ version 1); thus, we expect the version field to be 0, and
+       \ the 'other' field to be absent.
+       \
+       \ EC private key format is defined in RFC 5915:
+       \   ECPrivateKey ::= SEQUENCE {
+       \       version      INTEGER,   -- always 1
+       \       privateKey   OCTET STRING,
+       \       parameters   [0] EXPLICIT OBJECT IDENTIFIER OPTIONAL,
+       \       publicKey    [1] EXPLICIT BIT STRING OPTIONAL
+       \   }
+       \ The "parameters" might conceptually be a complex curve description
+       \ structure but we support only named curves. The private key
+       \ contents are the unsigned big-endian encoding of the key value,
+       \ which is exactly what we want.
+       \
+       \ PKCS#8 (unencrypted) is:
+       \   OneAsymmetricKey ::= SEQUENCE {
+       \       version      INTEGER,   -- 0 or 1
+       \       algorithm    AlgorithmIdentifier,
+       \       privateKey   OCTET STRING,
+       \       attributes   [0] IMPLICIT Attributes OPTIONAL,
+       \       publicKey    [1] IMPLICIT BIT STRING OPTIONAL
+       \   }
+       \ The 'publicKey' field is an add-on from RFC 5958 and may be
+       \ present only if the 'version' is v2 (i.e. has value 1). We
+       \ ignore it anyway.
+
+       \ An arbitrary upper limit on the private key size.
+       0xFFFFFF
+
+       \ Open the outer SEQUENCE.
+       read-sequence-open
+
+       \ All our schemas begin with a small INTEGER which is either 0 or
+       \ 1. We don't care which it is.
+       read-tag 0x02 check-tag-primitive read-small-int-value 1 > if
+               ERR_X509_UNSUPPORTED fail
+       then
+
+       \ Get next tag: it should be either an INTEGER (RSA private key),
+       \ an OCTET STRING (EC private key), or a SEQUENCE (for an
+       \ AlgorithmIdentifier, in a PKCS#8 object).
+       read-tag
+       case
+               0x02 of check-primitive decode-RSA-next KEYTYPE_RSA endof
+               0x04 of check-primitive 0 decode-EC-next KEYTYPE_EC endof
+               0x10 of check-constructed decode-PKCS8-next endof
+               ERR_X509_UNSUPPORTED fail
+       endcase
+       { key-type }
+
+       \ Close the SEQUENCE.
+       close-elt
+
+       \ Set the key type, which marks the decoding as a success.
+       key-type addr-key_type set8
+
+       \ Read one byte, then fail: if the read succeeds, then there is
+       \ some trailing byte.
+       read8-nc ERR_X509_EXTRA_ELEMENT fail
+       ;
diff --git a/src/x509/x509_decoder.c b/src/x509/x509_decoder.c
new file mode 100644 (file)
index 0000000..d81bba2
--- /dev/null
@@ -0,0 +1,769 @@
+/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+
+
+void br_x509_decoder_init_main(void *t0ctx);
+
+void br_x509_decoder_run(void *t0ctx);
+
+
+
+#include "inner.h"
+
+
+
+
+
+#include "inner.h"
+
+#define CTX   ((br_x509_decoder_context *)((unsigned char *)t0ctx - offsetof(br_x509_decoder_context, cpu)))
+#define CONTEXT_NAME   br_x509_decoder_context
+
+/* see bearssl_x509.h */
+void
+br_x509_decoder_init(br_x509_decoder_context *ctx,
+       void (*append_dn)(void *ctx, const void *buf, size_t len),
+       void *append_dn_ctx)
+{
+       memset(ctx, 0, sizeof *ctx);
+       /* obsolete
+       ctx->err = 0;
+       ctx->hbuf = NULL;
+       ctx->hlen = 0;
+       */
+       ctx->append_dn = append_dn;
+       ctx->append_dn_ctx = append_dn_ctx;
+       ctx->cpu.dp = &ctx->dp_stack[0];
+       ctx->cpu.rp = &ctx->rp_stack[0];
+       br_x509_decoder_init_main(&ctx->cpu);
+       br_x509_decoder_run(&ctx->cpu);
+}
+
+/* see bearssl_x509.h */
+void
+br_x509_decoder_push(br_x509_decoder_context *ctx,
+       const void *data, size_t len)
+{
+       ctx->hbuf = data;
+       ctx->hlen = len;
+       br_x509_decoder_run(&ctx->cpu);
+}
+
+
+
+static const uint8_t t0_datablock[] = {
+       0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x09,
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x09, 0x2A, 0x86,
+       0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E, 0x09, 0x2A, 0x86, 0x48, 0x86,
+       0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+       0x01, 0x01, 0x0C, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
+       0x0D, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86,
+       0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22,
+       0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D,
+       0x04, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x08,
+       0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x08, 0x2A, 0x86, 0x48,
+       0xCE, 0x3D, 0x04, 0x03, 0x03, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04,
+       0x03, 0x04, 0x00, 0x1F, 0x03, 0xFC, 0x07, 0x7F, 0x0B, 0x5E, 0x0F, 0x1F,
+       0x12, 0xFE, 0x16, 0xBF, 0x1A, 0x9F, 0x1E, 0x7E, 0x22, 0x3F, 0x26, 0x1E,
+       0x29, 0xDF, 0x00, 0x1F, 0x03, 0xFD, 0x07, 0x9F, 0x0B, 0x7E, 0x0F, 0x3F,
+       0x13, 0x1E, 0x16, 0xDF, 0x1A, 0xBF, 0x1E, 0x9E, 0x22, 0x5F, 0x26, 0x3E,
+       0x29, 0xFF, 0x03, 0x55, 0x1D, 0x13
+};
+
+static const uint8_t t0_codeblock[] = {
+       0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x01,
+       0x01, 0x09, 0x00, 0x00, 0x01, 0x01, 0x0A, 0x00, 0x00, 0x1A, 0x1A, 0x00,
+       0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_BOOLEAN), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_TIME), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_PARTIAL_BYTE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, copy_dn)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(CONTEXT_NAME, decoded)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, isCA)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(br_x509_decoder_context, pkey_data)), 0x01,
+       T0_INT2(BR_X509_BUFSIZE_KEY), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, notafter_days)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, notafter_seconds)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, notbefore_days)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, notbefore_seconds)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, pad)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, signer_hash_id)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, signer_key_type)), 0x00, 0x00, 0x01,
+       0x80, 0x45, 0x00, 0x00, 0x01, 0x80, 0x4E, 0x00, 0x00, 0x01, 0x80, 0x54,
+       0x00, 0x00, 0x01, 0x81, 0x36, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x1B,
+       0x02, 0x01, 0x13, 0x26, 0x02, 0x00, 0x0F, 0x15, 0x00, 0x00, 0x05, 0x02,
+       0x34, 0x1D, 0x00, 0x00, 0x06, 0x02, 0x35, 0x1D, 0x00, 0x00, 0x01, 0x10,
+       0x4F, 0x00, 0x00, 0x11, 0x05, 0x02, 0x38, 0x1D, 0x4C, 0x00, 0x00, 0x11,
+       0x05, 0x02, 0x38, 0x1D, 0x4D, 0x00, 0x00, 0x06, 0x02, 0x30, 0x1D, 0x00,
+       0x00, 0x1B, 0x19, 0x01, 0x08, 0x0E, 0x26, 0x29, 0x19, 0x09, 0x00, 0x00,
+       0x01, 0x30, 0x0A, 0x1B, 0x01, 0x00, 0x01, 0x09, 0x4B, 0x05, 0x02, 0x2F,
+       0x1D, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x01, 0x80, 0x5A, 0x00, 0x00,
+       0x01, 0x80, 0x62, 0x00, 0x00, 0x01, 0x80, 0x6B, 0x00, 0x00, 0x01, 0x80,
+       0x74, 0x00, 0x00, 0x01, 0x80, 0x7D, 0x00, 0x00, 0x01, 0x3D, 0x00, 0x00,
+       0x20, 0x11, 0x06, 0x04, 0x2B, 0x6B, 0x7A, 0x71, 0x00, 0x04, 0x01, 0x00,
+       0x3D, 0x25, 0x01, 0x00, 0x3C, 0x25, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x6D,
+       0x6D, 0x70, 0x1B, 0x01, 0x20, 0x11, 0x06, 0x11, 0x1A, 0x4C, 0x6B, 0x70,
+       0x01, 0x02, 0x50, 0x6E, 0x01, 0x02, 0x12, 0x06, 0x02, 0x39, 0x1D, 0x51,
+       0x70, 0x01, 0x02, 0x50, 0x6C, 0x6D, 0x7A, 0x6D, 0x7A, 0x6D, 0x65, 0x43,
+       0x24, 0x42, 0x24, 0x65, 0x41, 0x24, 0x40, 0x24, 0x51, 0x01, 0x01, 0x3C,
+       0x25, 0x6D, 0x7A, 0x01, 0x00, 0x3C, 0x25, 0x6D, 0x6D, 0x60, 0x05, 0x02,
+       0x39, 0x1D, 0x74, 0x1C, 0x06, 0x1C, 0x7A, 0x61, 0x6D, 0x3F, 0x68, 0x03,
+       0x00, 0x3F, 0x26, 0x02, 0x00, 0x09, 0x26, 0x02, 0x00, 0x0A, 0x68, 0x03,
+       0x01, 0x51, 0x51, 0x02, 0x00, 0x02, 0x01, 0x18, 0x04, 0x1E, 0x5A, 0x1C,
+       0x06, 0x18, 0x64, 0x03, 0x02, 0x51, 0x61, 0x1B, 0x03, 0x03, 0x1B, 0x3F,
+       0x23, 0x0D, 0x06, 0x02, 0x33, 0x1D, 0x62, 0x02, 0x02, 0x02, 0x03, 0x17,
+       0x04, 0x02, 0x39, 0x1D, 0x51, 0x01, 0x00, 0x3E, 0x25, 0x71, 0x01, 0x21,
+       0x5B, 0x01, 0x22, 0x5B, 0x1B, 0x01, 0x23, 0x11, 0x06, 0x28, 0x1A, 0x4C,
+       0x6B, 0x6D, 0x1B, 0x06, 0x1D, 0x6D, 0x60, 0x1A, 0x70, 0x1B, 0x01, 0x01,
+       0x11, 0x06, 0x03, 0x63, 0x1A, 0x70, 0x01, 0x04, 0x50, 0x6B, 0x4A, 0x1C,
+       0x06, 0x03, 0x5F, 0x04, 0x01, 0x7B, 0x51, 0x51, 0x04, 0x60, 0x51, 0x51,
+       0x04, 0x08, 0x01, 0x7F, 0x11, 0x05, 0x02, 0x38, 0x1D, 0x1A, 0x51, 0x6D,
+       0x60, 0x06, 0x80, 0x63, 0x75, 0x1C, 0x06, 0x06, 0x01, 0x02, 0x3B, 0x04,
+       0x80, 0x57, 0x76, 0x1C, 0x06, 0x06, 0x01, 0x03, 0x3B, 0x04, 0x80, 0x4D,
+       0x77, 0x1C, 0x06, 0x06, 0x01, 0x04, 0x3B, 0x04, 0x80, 0x43, 0x78, 0x1C,
+       0x06, 0x05, 0x01, 0x05, 0x3B, 0x04, 0x3A, 0x79, 0x1C, 0x06, 0x05, 0x01,
+       0x06, 0x3B, 0x04, 0x31, 0x55, 0x1C, 0x06, 0x05, 0x01, 0x02, 0x3A, 0x04,
+       0x28, 0x56, 0x1C, 0x06, 0x05, 0x01, 0x03, 0x3A, 0x04, 0x1F, 0x57, 0x1C,
+       0x06, 0x05, 0x01, 0x04, 0x3A, 0x04, 0x16, 0x58, 0x1C, 0x06, 0x05, 0x01,
+       0x05, 0x3A, 0x04, 0x0D, 0x59, 0x1C, 0x06, 0x05, 0x01, 0x06, 0x3A, 0x04,
+       0x04, 0x01, 0x00, 0x01, 0x00, 0x04, 0x04, 0x01, 0x00, 0x01, 0x00, 0x46,
+       0x25, 0x45, 0x25, 0x7A, 0x61, 0x7A, 0x51, 0x1A, 0x01, 0x01, 0x3D, 0x25,
+       0x73, 0x30, 0x1D, 0x00, 0x00, 0x01, 0x81, 0x06, 0x00, 0x01, 0x54, 0x0D,
+       0x06, 0x02, 0x32, 0x1D, 0x1B, 0x03, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x00,
+       0x6D, 0x71, 0x1B, 0x01, 0x01, 0x11, 0x06, 0x08, 0x63, 0x01, 0x01, 0x15,
+       0x3E, 0x25, 0x04, 0x01, 0x2B, 0x7A, 0x00, 0x00, 0x70, 0x01, 0x06, 0x50,
+       0x6F, 0x00, 0x00, 0x70, 0x01, 0x03, 0x50, 0x6B, 0x72, 0x06, 0x02, 0x37,
+       0x1D, 0x00, 0x00, 0x26, 0x1B, 0x06, 0x07, 0x21, 0x1B, 0x06, 0x01, 0x16,
+       0x04, 0x76, 0x2B, 0x00, 0x00, 0x01, 0x01, 0x50, 0x6A, 0x01, 0x01, 0x10,
+       0x06, 0x02, 0x2C, 0x1D, 0x72, 0x27, 0x00, 0x00, 0x60, 0x05, 0x02, 0x39,
+       0x1D, 0x47, 0x1C, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x48, 0x1C, 0x06,
+       0x04, 0x01, 0x18, 0x04, 0x0A, 0x49, 0x1C, 0x06, 0x04, 0x01, 0x19, 0x04,
+       0x02, 0x39, 0x1D, 0x00, 0x04, 0x70, 0x1B, 0x01, 0x17, 0x01, 0x18, 0x4B,
+       0x05, 0x02, 0x2F, 0x1D, 0x01, 0x18, 0x11, 0x03, 0x00, 0x4D, 0x6B, 0x66,
+       0x02, 0x00, 0x06, 0x0C, 0x01, 0x80, 0x64, 0x08, 0x03, 0x01, 0x66, 0x02,
+       0x01, 0x09, 0x04, 0x0E, 0x1B, 0x01, 0x32, 0x0D, 0x06, 0x04, 0x01, 0x80,
+       0x64, 0x09, 0x01, 0x8E, 0x6C, 0x09, 0x03, 0x01, 0x02, 0x01, 0x01, 0x82,
+       0x6D, 0x08, 0x02, 0x01, 0x01, 0x03, 0x09, 0x01, 0x04, 0x0C, 0x09, 0x02,
+       0x01, 0x01, 0x80, 0x63, 0x09, 0x01, 0x80, 0x64, 0x0C, 0x0A, 0x02, 0x01,
+       0x01, 0x83, 0x0F, 0x09, 0x01, 0x83, 0x10, 0x0C, 0x09, 0x03, 0x03, 0x01,
+       0x01, 0x01, 0x0C, 0x67, 0x2A, 0x01, 0x01, 0x0E, 0x02, 0x01, 0x01, 0x04,
+       0x07, 0x28, 0x02, 0x01, 0x01, 0x80, 0x64, 0x07, 0x27, 0x02, 0x01, 0x01,
+       0x83, 0x10, 0x07, 0x28, 0x1F, 0x15, 0x06, 0x03, 0x01, 0x18, 0x09, 0x5D,
+       0x09, 0x52, 0x1B, 0x01, 0x05, 0x14, 0x02, 0x03, 0x09, 0x03, 0x03, 0x01,
+       0x1F, 0x15, 0x01, 0x01, 0x26, 0x67, 0x02, 0x03, 0x09, 0x2A, 0x03, 0x03,
+       0x01, 0x00, 0x01, 0x17, 0x67, 0x01, 0x9C, 0x10, 0x08, 0x03, 0x02, 0x01,
+       0x00, 0x01, 0x3B, 0x67, 0x01, 0x3C, 0x08, 0x02, 0x02, 0x09, 0x03, 0x02,
+       0x01, 0x00, 0x01, 0x3C, 0x67, 0x02, 0x02, 0x09, 0x03, 0x02, 0x72, 0x1B,
+       0x01, 0x2E, 0x11, 0x06, 0x0D, 0x1A, 0x72, 0x1B, 0x01, 0x30, 0x01, 0x39,
+       0x4B, 0x06, 0x03, 0x1A, 0x04, 0x74, 0x01, 0x80, 0x5A, 0x10, 0x06, 0x02,
+       0x2F, 0x1D, 0x51, 0x02, 0x03, 0x02, 0x02, 0x00, 0x01, 0x72, 0x53, 0x01,
+       0x0A, 0x08, 0x03, 0x00, 0x72, 0x53, 0x02, 0x00, 0x09, 0x00, 0x02, 0x03,
+       0x00, 0x03, 0x01, 0x66, 0x1B, 0x02, 0x01, 0x02, 0x00, 0x4B, 0x05, 0x02,
+       0x2F, 0x1D, 0x00, 0x00, 0x23, 0x70, 0x01, 0x02, 0x50, 0x0B, 0x69, 0x00,
+       0x03, 0x1B, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x6B, 0x72, 0x1B, 0x01,
+       0x81, 0x00, 0x13, 0x06, 0x02, 0x36, 0x1D, 0x1B, 0x01, 0x00, 0x11, 0x06,
+       0x0B, 0x1A, 0x1B, 0x05, 0x04, 0x1A, 0x01, 0x00, 0x00, 0x72, 0x04, 0x6F,
+       0x02, 0x01, 0x1B, 0x05, 0x02, 0x33, 0x1D, 0x2A, 0x03, 0x01, 0x02, 0x02,
+       0x25, 0x02, 0x02, 0x29, 0x03, 0x02, 0x1B, 0x06, 0x03, 0x72, 0x04, 0x68,
+       0x1A, 0x02, 0x00, 0x02, 0x01, 0x0A, 0x00, 0x01, 0x72, 0x1B, 0x01, 0x81,
+       0x00, 0x0D, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x0A, 0x1B, 0x05, 0x02,
+       0x31, 0x1D, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x12, 0x06,
+       0x19, 0x02, 0x00, 0x2A, 0x03, 0x00, 0x1B, 0x01, 0x87, 0xFF, 0xFF, 0x7F,
+       0x12, 0x06, 0x02, 0x32, 0x1D, 0x01, 0x08, 0x0E, 0x26, 0x72, 0x23, 0x09,
+       0x04, 0x60, 0x00, 0x00, 0x6A, 0x5E, 0x00, 0x00, 0x6B, 0x7A, 0x00, 0x00,
+       0x70, 0x4E, 0x6B, 0x00, 0x01, 0x6B, 0x1B, 0x05, 0x02, 0x36, 0x1D, 0x72,
+       0x1B, 0x01, 0x81, 0x00, 0x13, 0x06, 0x02, 0x36, 0x1D, 0x03, 0x00, 0x1B,
+       0x06, 0x16, 0x72, 0x02, 0x00, 0x1B, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x13,
+       0x06, 0x02, 0x36, 0x1D, 0x01, 0x08, 0x0E, 0x09, 0x03, 0x00, 0x04, 0x67,
+       0x1A, 0x02, 0x00, 0x00, 0x00, 0x6B, 0x1B, 0x01, 0x81, 0x7F, 0x12, 0x06,
+       0x08, 0x7A, 0x01, 0x00, 0x44, 0x25, 0x01, 0x00, 0x00, 0x1B, 0x44, 0x25,
+       0x44, 0x29, 0x62, 0x01, 0x7F, 0x00, 0x01, 0x72, 0x03, 0x00, 0x02, 0x00,
+       0x01, 0x05, 0x14, 0x01, 0x01, 0x15, 0x1E, 0x02, 0x00, 0x01, 0x06, 0x14,
+       0x1B, 0x01, 0x01, 0x15, 0x06, 0x02, 0x2D, 0x1D, 0x01, 0x04, 0x0E, 0x02,
+       0x00, 0x01, 0x1F, 0x15, 0x1B, 0x01, 0x1F, 0x11, 0x06, 0x02, 0x2E, 0x1D,
+       0x09, 0x00, 0x00, 0x1B, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x70,
+       0x00, 0x00, 0x1B, 0x05, 0x02, 0x32, 0x1D, 0x2A, 0x73, 0x00, 0x00, 0x22,
+       0x1B, 0x01, 0x00, 0x13, 0x06, 0x01, 0x00, 0x1A, 0x16, 0x04, 0x74, 0x00,
+       0x01, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00,
+       0x01, 0x1F, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00,
+       0x7B, 0x1A, 0x00, 0x00, 0x1B, 0x06, 0x07, 0x7C, 0x1B, 0x06, 0x01, 0x16,
+       0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x20, 0x21, 0x0B, 0x2B, 0x00
+};
+
+static const uint16_t t0_caddr[] = {
+       0,
+       5,
+       10,
+       15,
+       20,
+       24,
+       28,
+       32,
+       36,
+       40,
+       44,
+       48,
+       52,
+       56,
+       60,
+       64,
+       68,
+       72,
+       76,
+       80,
+       84,
+       88,
+       93,
+       98,
+       103,
+       111,
+       116,
+       121,
+       126,
+       131,
+       136,
+       141,
+       146,
+       151,
+       156,
+       161,
+       166,
+       181,
+       187,
+       193,
+       198,
+       206,
+       214,
+       220,
+       231,
+       246,
+       250,
+       255,
+       260,
+       265,
+       270,
+       275,
+       279,
+       289,
+       620,
+       625,
+       639,
+       659,
+       666,
+       678,
+       692,
+       707,
+       740,
+       960,
+       974,
+       991,
+       1000,
+       1067,
+       1123,
+       1127,
+       1131,
+       1136,
+       1184,
+       1210,
+       1254,
+       1265,
+       1274,
+       1287,
+       1291,
+       1295,
+       1299,
+       1303,
+       1307,
+       1311,
+       1315,
+       1327
+};
+
+#define T0_INTERPRETED   39
+
+#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)
+
+#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}
+
+T0_DEFENTRY(br_x509_decoder_init_main, 92)
+
+void
+br_x509_decoder_run(void *t0ctx)
+{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 7: {
+                               /* %25 */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSHi(a % b);
+
+                               }
+                               break;
+                       case 8: {
+                               /* * */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a * b);
+
+                               }
+                               break;
+                       case 9: {
+                               /* + */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+
+                               }
+                               break;
+                       case 10: {
+                               /* - */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+
+                               }
+                               break;
+                       case 11: {
+                               /* -rot */
+ T0_NROT(); 
+                               }
+                               break;
+                       case 12: {
+                               /* / */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSHi(a / b);
+
+                               }
+                               break;
+                       case 13: {
+                               /* < */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+
+                               }
+                               break;
+                       case 14: {
+                               /* << */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+
+                               }
+                               break;
+                       case 15: {
+                               /* <= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a <= b));
+
+                               }
+                               break;
+                       case 16: {
+                               /* <> */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+
+                               }
+                               break;
+                       case 17: {
+                               /* = */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+
+                               }
+                               break;
+                       case 18: {
+                               /* > */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a > b));
+
+                               }
+                               break;
+                       case 19: {
+                               /* >= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+
+                               }
+                               break;
+                       case 20: {
+                               /* >> */
+
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+
+                               }
+                               break;
+                       case 21: {
+                               /* and */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+
+                               }
+                               break;
+                       case 22: {
+                               /* co */
+ T0_CO(); 
+                               }
+                               break;
+                       case 23: {
+                               /* copy-ec-pkey */
+
+       size_t qlen = T0_POP();
+       uint32_t curve = T0_POP();
+       CTX->pkey.key_type = BR_KEYTYPE_EC;
+       CTX->pkey.key.ec.curve = curve;
+       CTX->pkey.key.ec.q = CTX->pkey_data;
+       CTX->pkey.key.ec.qlen = qlen;
+
+                               }
+                               break;
+                       case 24: {
+                               /* copy-rsa-pkey */
+
+       size_t elen = T0_POP();
+       size_t nlen = T0_POP();
+       CTX->pkey.key_type = BR_KEYTYPE_RSA;
+       CTX->pkey.key.rsa.n = CTX->pkey_data;
+       CTX->pkey.key.rsa.nlen = nlen;
+       CTX->pkey.key.rsa.e = CTX->pkey_data + nlen;
+       CTX->pkey.key.rsa.elen = elen;
+
+                               }
+                               break;
+                       case 25: {
+                               /* data-get8 */
+
+       size_t addr = T0_POP();
+       T0_PUSH(t0_datablock[addr]);
+
+                               }
+                               break;
+                       case 26: {
+                               /* drop */
+ (void)T0_POP(); 
+                               }
+                               break;
+                       case 27: {
+                               /* dup */
+ T0_PUSH(T0_PEEK(0)); 
+                               }
+                               break;
+                       case 28: {
+                               /* eqOID */
+
+       const unsigned char *a2 = &t0_datablock[T0_POP()];
+       const unsigned char *a1 = &CTX->pad[0];
+       size_t len = a1[0];
+       int x;
+       if (len == a2[0]) {
+               x = -(memcmp(a1 + 1, a2 + 1, len) == 0);
+       } else {
+               x = 0;
+       }
+       T0_PUSH((uint32_t)x);
+
+                               }
+                               break;
+                       case 29: {
+                               /* fail */
+
+       CTX->err = T0_POPi();
+       T0_CO();
+
+                               }
+                               break;
+                       case 30: {
+                               /* neg */
+
+       uint32_t a = T0_POP();
+       T0_PUSH(-a);
+
+                               }
+                               break;
+                       case 31: {
+                               /* or */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a | b);
+
+                               }
+                               break;
+                       case 32: {
+                               /* over */
+ T0_PUSH(T0_PEEK(1)); 
+                               }
+                               break;
+                       case 33: {
+                               /* read-blob-inner */
+
+       uint32_t len = T0_POP();
+       uint32_t addr = T0_POP();
+       size_t clen = CTX->hlen;
+       if (clen > len) {
+               clen = (size_t)len;
+       }
+       if (addr != 0) {
+               memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen);
+       }
+       if (CTX->copy_dn && CTX->append_dn) {
+               CTX->append_dn(CTX->append_dn_ctx, CTX->hbuf, clen);
+       }
+       CTX->hbuf += clen;
+       CTX->hlen -= clen;
+       T0_PUSH(addr + clen);
+       T0_PUSH(len - clen);
+
+                               }
+                               break;
+                       case 34: {
+                               /* read8-low */
+
+       if (CTX->hlen == 0) {
+               T0_PUSHi(-1);
+       } else {
+               unsigned char x = *CTX->hbuf ++;
+               if (CTX->copy_dn && CTX->append_dn) {
+                       CTX->append_dn(CTX->append_dn_ctx, &x, 1);
+               }
+               CTX->hlen --;
+               T0_PUSH(x);
+       }
+
+                               }
+                               break;
+                       case 35: {
+                               /* rot */
+ T0_ROT(); 
+                               }
+                               break;
+                       case 36: {
+                               /* set32 */
+
+       uint32_t addr = T0_POP();
+       *(uint32_t *)((unsigned char *)CTX + addr) = T0_POP();
+
+                               }
+                               break;
+                       case 37: {
+                               /* set8 */
+
+       uint32_t addr = T0_POP();
+       *((unsigned char *)CTX + addr) = (unsigned char)T0_POP();
+
+                               }
+                               break;
+                       case 38: {
+                               /* swap */
+ T0_SWAP(); 
+                               }
+                               break;
+                       }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}
diff --git a/src/x509/x509_decoder.t0 b/src/x509/x509_decoder.t0
new file mode 100644 (file)
index 0000000..1b6089b
--- /dev/null
@@ -0,0 +1,321 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+preamble {
+
+#include "inner.h"
+
+#define CTX   ((br_x509_decoder_context *)((unsigned char *)t0ctx - offsetof(br_x509_decoder_context, cpu)))
+#define CONTEXT_NAME   br_x509_decoder_context
+
+/* see bearssl_x509.h */
+void
+br_x509_decoder_init(br_x509_decoder_context *ctx,
+       void (*append_dn)(void *ctx, const void *buf, size_t len),
+       void *append_dn_ctx)
+{
+       memset(ctx, 0, sizeof *ctx);
+       /* obsolete
+       ctx->err = 0;
+       ctx->hbuf = NULL;
+       ctx->hlen = 0;
+       */
+       ctx->append_dn = append_dn;
+       ctx->append_dn_ctx = append_dn_ctx;
+       ctx->cpu.dp = &ctx->dp_stack[0];
+       ctx->cpu.rp = &ctx->rp_stack[0];
+       br_x509_decoder_init_main(&ctx->cpu);
+       br_x509_decoder_run(&ctx->cpu);
+}
+
+/* see bearssl_x509.h */
+void
+br_x509_decoder_push(br_x509_decoder_context *ctx,
+       const void *data, size_t len)
+{
+       ctx->hbuf = data;
+       ctx->hlen = len;
+       br_x509_decoder_run(&ctx->cpu);
+}
+
+}
+
+addr: decoded
+addr: notbefore_days
+addr: notbefore_seconds
+addr: notafter_days
+addr: notafter_seconds
+addr: isCA
+addr: copy_dn
+addr: signer_key_type
+addr: signer_hash_id
+
+cc: read8-low ( -- x ) {
+       if (CTX->hlen == 0) {
+               T0_PUSHi(-1);
+       } else {
+               unsigned char x = *CTX->hbuf ++;
+               if (CTX->copy_dn && CTX->append_dn) {
+                       CTX->append_dn(CTX->append_dn_ctx, &x, 1);
+               }
+               CTX->hlen --;
+               T0_PUSH(x);
+       }
+}
+
+cc: read-blob-inner ( addr len -- addr len ) {
+       uint32_t len = T0_POP();
+       uint32_t addr = T0_POP();
+       size_t clen = CTX->hlen;
+       if (clen > len) {
+               clen = (size_t)len;
+       }
+       if (addr != 0) {
+               memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen);
+       }
+       if (CTX->copy_dn && CTX->append_dn) {
+               CTX->append_dn(CTX->append_dn_ctx, CTX->hbuf, clen);
+       }
+       CTX->hbuf += clen;
+       CTX->hlen -= clen;
+       T0_PUSH(addr + clen);
+       T0_PUSH(len - clen);
+}
+
+\ Get the address and length for the pkey_data buffer.
+: addr-len-pkey_data ( -- addr len )
+       CX 0 8191 { offsetof(br_x509_decoder_context, pkey_data) }
+       CX 0 8191 { BR_X509_BUFSIZE_KEY } ;
+
+\ Copy the public key (RSA) to the permanent buffer.
+cc: copy-rsa-pkey ( nlen elen -- ) {
+       size_t elen = T0_POP();
+       size_t nlen = T0_POP();
+       CTX->pkey.key_type = BR_KEYTYPE_RSA;
+       CTX->pkey.key.rsa.n = CTX->pkey_data;
+       CTX->pkey.key.rsa.nlen = nlen;
+       CTX->pkey.key.rsa.e = CTX->pkey_data + nlen;
+       CTX->pkey.key.rsa.elen = elen;
+}
+
+\ Copy the public key (EC) to the permanent buffer.
+cc: copy-ec-pkey ( curve qlen -- ) {
+       size_t qlen = T0_POP();
+       uint32_t curve = T0_POP();
+       CTX->pkey.key_type = BR_KEYTYPE_EC;
+       CTX->pkey.key.ec.curve = curve;
+       CTX->pkey.key.ec.q = CTX->pkey_data;
+       CTX->pkey.key.ec.qlen = qlen;
+}
+
+\ Extensions with specific processing.
+OID: basicConstraints    2.5.29.19
+
+\ Process a Basic Constraints extension. We want the "CA" flag only.
+: process-basicConstraints ( lim -- lim )
+       read-sequence-open
+       read-tag-or-end dup 0x01 = if
+               read-boolean 1 and addr-isCA set8
+       else
+               2drop
+       then
+       skip-close-elt
+       ;
+
+\ Decode a certificate.
+: main ( -- ! )
+
+       \ Initialise state flags.
+       0 addr-decoded set8
+       0 addr-copy_dn set8
+
+       \ An arbitrary limit for the total certificate size.
+       0xFFFFFF
+
+       \ Open the outer SEQUENCE.
+       read-sequence-open
+
+       \ TBS
+       read-sequence-open
+
+       \ First element may be an explicit version. We accept only
+       \ versions 0 to 2 (certificates v1 to v3).
+       read-tag dup 0x20 = if
+               drop check-constructed read-length-open-elt
+               read-tag
+               0x02 check-tag-primitive
+               read-small-int-value
+               2 > if ERR_X509_UNSUPPORTED fail then
+               close-elt
+               read-tag
+       then
+
+       \ Serial number. We just check that the tag is correct.
+       0x02 check-tag-primitive read-length-skip
+
+       \ Signature algorithm.
+       read-sequence-open skip-close-elt
+
+       \ Issuer name.
+       read-sequence-open skip-close-elt
+
+       \ Validity dates.
+       read-sequence-open
+       read-date addr-notbefore_seconds set32 addr-notbefore_days set32
+       read-date addr-notafter_seconds set32 addr-notafter_days set32
+       close-elt
+
+       \ Subject name.
+       1 addr-copy_dn set8
+       read-sequence-open skip-close-elt
+       0 addr-copy_dn set8
+
+       \ Public Key.
+       read-sequence-open
+       \ Algorithm Identifier. Right now we are only interested in the
+       \ OID, since we only support RSA keys.
+       \ TODO: support EC keys
+       read-sequence-open
+       read-OID ifnot ERR_X509_UNSUPPORTED fail then
+       choice
+               \ RSA public key.
+               rsaEncryption eqOID uf
+                       skip-close-elt
+                       \ Public key itself: the BIT STRING contains bytes
+                       \ (no partial byte) and these bytes encode the
+                       \ actual value.
+                       read-bits-open
+                               \ RSA public key is a SEQUENCE of two
+                               \ INTEGER. We get both INTEGER values into
+                               \ the pkey_data[] buffer, if they fit.
+                               read-sequence-open
+                               addr-len-pkey_data
+                               read-integer { nlen }
+                               addr-len-pkey_data swap nlen + swap nlen -
+                               read-integer { elen }
+                               close-elt
+                       close-elt
+                       nlen elen copy-rsa-pkey
+               enduf
+
+               \ EC public key.
+               id-ecPublicKey eqOID uf
+                       \ We support only named curves, for which the
+                       \ "parameters" field in the AlgorithmIdentifier
+                       \ field should be an OID.
+                       read-curve-ID { curve }
+                       close-elt
+                       read-bits-open
+                       dup { qlen }
+                       dup addr-len-pkey_data rot < if
+                               ERR_X509_LIMIT_EXCEEDED fail
+                       then
+                       read-blob
+                       curve qlen copy-ec-pkey
+               enduf
+               ERR_X509_UNSUPPORTED fail
+       endchoice
+       close-elt
+
+       \ This flag will be set to true if the Basic Constraints extension
+       \ is encountered.
+       0 addr-isCA set8
+
+       \ Skip issuerUniqueID and subjectUniqueID, and process extensions
+       \ if present. Extensions are an explicit context tag of value 3
+       \ around a SEQUENCE OF extensions. Each extension is a SEQUENCE
+       \ with an OID, an optional boolean, and a value; the value is
+       \ an OCTET STRING.
+       read-tag-or-end
+       0x21 iftag-skip
+       0x22 iftag-skip
+       dup 0x23 = if
+               drop
+               check-constructed read-length-open-elt
+               read-sequence-open
+               begin dup while
+                       read-sequence-open
+                       read-OID drop
+                       read-tag dup 0x01 = if
+                               read-boolean drop
+                               read-tag
+                       then
+                       0x04 check-tag-primitive read-length-open-elt
+                       choice
+                               \ Extensions with specific processing.
+                               basicConstraints eqOID uf
+                                       process-basicConstraints
+                               enduf
+                               skip-remaining
+                       endchoice
+                       close-elt
+                       close-elt
+               repeat
+               close-elt
+               close-elt
+       else
+               -1 = ifnot ERR_X509_UNEXPECTED fail then
+               drop
+       then
+
+       close-elt
+
+       \ signature algorithm
+       read-sequence-open
+       read-OID if
+               choice
+                       sha1WithRSAEncryption    eqOID uf 2 KEYTYPE_RSA enduf
+                       sha224WithRSAEncryption  eqOID uf 3 KEYTYPE_RSA enduf
+                       sha256WithRSAEncryption  eqOID uf 4 KEYTYPE_RSA enduf
+                       sha384WithRSAEncryption  eqOID uf 5 KEYTYPE_RSA enduf
+                       sha512WithRSAEncryption  eqOID uf 6 KEYTYPE_RSA enduf
+
+                       ecdsa-with-SHA1          eqOID uf 2 KEYTYPE_EC enduf
+                       ecdsa-with-SHA224        eqOID uf 3 KEYTYPE_EC enduf
+                       ecdsa-with-SHA256        eqOID uf 4 KEYTYPE_EC enduf
+                       ecdsa-with-SHA384        eqOID uf 5 KEYTYPE_EC enduf
+                       ecdsa-with-SHA512        eqOID uf 6 KEYTYPE_EC enduf
+
+                       0 0
+               endchoice
+       else
+               0 0
+       then
+       addr-signer_key_type set8
+       addr-signer_hash_id set8
+       skip-close-elt
+       \ read-sequence-open skip-close-elt
+
+       \ signature value
+       read-bits-open skip-close-elt
+
+       \ Close the outer SEQUENCE.
+       close-elt
+       drop
+
+       \ Mark the decoding as successful.
+       1 addr-decoded set8
+
+       \ Read one byte, then fail: if the read succeeds, then there is
+       \ some trailing byte.
+       read8-nc ERR_X509_EXTRA_ELEMENT fail
+       ;
diff --git a/src/x509/x509_knownkey.c b/src/x509/x509_knownkey.c
new file mode 100644 (file)
index 0000000..f00c32b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_x509.h */
+void
+br_x509_knownkey_init_rsa(br_x509_knownkey_context *ctx,
+       const br_rsa_public_key *pk)
+{
+       ctx->vtable = &br_x509_knownkey_vtable;
+       ctx->pkey.key_type = BR_KEYTYPE_RSA;
+       ctx->pkey.key.rsa = *pk;
+}
+
+/* see bearssl_x509.h */
+void
+br_x509_knownkey_init_ec(br_x509_knownkey_context *ctx,
+       const br_ec_public_key *pk)
+{
+       ctx->vtable = &br_x509_knownkey_vtable;
+       ctx->pkey.key_type = BR_KEYTYPE_EC;
+       ctx->pkey.key.ec = *pk;
+}
+
+static void
+kk_start_chain(const br_x509_class **ctx,
+       unsigned expected_key_type, const char *server_name)
+{
+       (void)ctx;
+       (void)expected_key_type;
+       (void)server_name;
+}
+
+static void
+kk_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+       (void)ctx;
+       (void)length;
+}
+
+static void
+kk_append(const br_x509_class **ctx, const unsigned char *buf, size_t len)
+{
+       (void)ctx;
+       (void)buf;
+       (void)len;
+}
+
+static void
+kk_end_cert(const br_x509_class **ctx)
+{
+       (void)ctx;
+}
+
+static unsigned
+kk_end_chain(const br_x509_class **ctx)
+{
+       (void)ctx;
+       return 0;
+}
+
+static const br_x509_pkey *
+kk_get_pkey(const br_x509_class *const *ctx)
+{
+       return &((const br_x509_knownkey_context *)ctx)->pkey;
+}
+
+/* see bearssl_x509.h */
+const br_x509_class br_x509_knownkey_vtable = {
+       sizeof(br_x509_knownkey_context),
+       kk_start_chain,
+       kk_start_cert,
+       kk_append,
+       kk_end_cert,
+       kk_end_chain,
+       kk_get_pkey
+};
diff --git a/src/x509/x509_minimal.c b/src/x509/x509_minimal.c
new file mode 100644 (file)
index 0000000..55d9e23
--- /dev/null
@@ -0,0 +1,1551 @@
+/* Automatically generated code; do not modify directly. */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+       uint32_t *dp;
+       uint32_t *rp;
+       const unsigned char *ip;
+} t0_context;
+
+static uint32_t
+t0_parse7E_unsigned(const unsigned char **p)
+{
+       uint32_t x;
+
+       x = 0;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       return x;
+               }
+       }
+}
+
+static int32_t
+t0_parse7E_signed(const unsigned char **p)
+{
+       int neg;
+       uint32_t x;
+
+       neg = ((**p) >> 6) & 1;
+       x = (uint32_t)-neg;
+       for (;;) {
+               unsigned y;
+
+               y = *(*p) ++;
+               x = (x << 7) | (uint32_t)(y & 0x7F);
+               if (y < 0x80) {
+                       if (neg) {
+                               return -(int32_t)~x - 1;
+                       } else {
+                               return (int32_t)x;
+                       }
+               }
+       }
+}
+
+#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
+#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
+#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
+#define T0_INT1(x)       T0_FBYTE(x, 0)
+#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
+
+static const uint8_t t0_datablock[];
+
+
+void br_x509_minimal_init_main(void *t0ctx);
+
+void br_x509_minimal_run(void *t0ctx);
+
+
+
+#include "inner.h"
+
+
+
+
+
+#include "inner.h"
+
+/*
+ * Implementation Notes
+ * --------------------
+ *
+ * The C code pushes the data by chunks; all decoding is done in the
+ * T0 code. The cert_length value is set to the certificate length when
+ * a new certificate is started; the T0 code picks it up as outer limit,
+ * and decoding functions use it to ensure that no attempt is made at
+ * reading past it. The T0 code also checks that once the certificate is
+ * decoded, there are no trailing bytes.
+ *
+ * The T0 code sets cert_length to 0 when the certificate is fully
+ * decoded.
+ *
+ * The C code must still perform two checks:
+ *
+ *  -- If the certificate length is 0, then the T0 code will not be
+ *  invoked at all. This invalid condition must thus be reported by the
+ *  C code.
+ *
+ *  -- When reaching the end of certificate, the C code must verify that
+ *  the certificate length has been set to 0, thereby signaling that
+ *  the T0 code properly decoded a certificate.
+ *
+ * Processing of a chain works in the following way:
+ *
+ *  -- The error flag is set to a non-zero value when validation is
+ *  finished. The value is either BR_ERR_X509_OK (validation is
+ *  successful) or another non-zero error code. When a non-zero error
+ *  code is obtained, the remaining bytes in the current certificate and
+ *  the subsequent certificates (if any) are completely ignored.
+ *
+ *  -- Each certificate is decoded in due course, with the following
+ *  "interesting points":
+ *
+ *     -- Start of the TBS: the multihash engine is reset and activated.
+ *
+ *     -- Start of the issuer DN: the secondary hash engine is started,
+ *     to process the encoded issuer DN.
+ *
+ *     -- End of the issuer DN: the secondary hash engine is stopped. The
+ *     resulting hash value is computed and then copied into the
+ *     next_dn_hash[] buffer.
+ *
+ *     -- Start of the subject DN: the secondary hash engine is started,
+ *     to process the encoded subject DN.
+ *
+ *     -- For the EE certificate only: the Common Name, if any, is matched
+ *     against the expected server name.
+ *
+ *     -- End of the subject DN: the secondary hash engine is stopped. The
+ *     resulting hash value is computed into the pad. It is then processed:
+ *
+ *        -- If this is the EE certificate, then the hash is ignored
+ *        (except for direct trust processing, see later; the hash is
+ *        simply left in current_dn_hash[]).
+ *
+ *        -- Otherwise, the hashed subject DN is compared with the saved
+ *        hash value (in saved_dn_hash[]). They must match.
+ *
+ *     Either way, the next_dn_hash[] value is then copied into the
+ *     saved_dn_hash[] value. Thus, at that point, saved_dn_hash[]
+ *     contains the hash of the issuer DN for the current certificate,
+ *     and current_dn_hash[] contains the hash of the subject DN for the
+ *     current certificate.
+ *
+ *     -- Public key: it is decoded into the cert_pkey[] buffer. Unknown
+ *     key types are reported at that point.
+ *
+ *        -- If this is the EE certificate, then the key type is compared
+ *        with the expected key type (initialization parameter). The public
+ *        key data is copied to ee_pkey_data[]. The key and hashed subject
+ *        DN are also compared with the "direct trust" keys; if the key
+ *        and DN are matched, then validation ends with a success.
+ *
+ *        -- Otherwise, the saved signature (cert_sig[]) is verified
+ *        against the saved TBS hash (tbs_hash[]) and that freshly
+ *        decoded public key. Failure here ends validation with an error.
+ *
+ *     -- Extensions: extension values are processed in due order.
+ *
+ *        -- Basic Constraints: for all certificates except EE, must be
+ *        present, indicate a CA, and have a path legnth compatible with
+ *        the chain length so far.
+ *
+ *        -- Key Usage: for the EE, if present, must allow signatures
+ *        or encryption/key exchange, as required for the cipher suite.
+ *        For non-EE, if present, must have the "certificate sign" bit.
+ *
+ *        -- Subject Alt Name: for the EE, dNSName names are matched
+ *        against the server name. Ignored for non-EE.
+ *
+ *        -- Authority Key Identifier, Subject Key Identifier, Issuer
+ *        Alt Name, Subject Directory Attributes, CRL Distribution Points
+ *        Freshest CRL, Authority Info Access and Subject Info Access
+ *        extensions are always ignored: they either contain only
+ *        informative data, or they relate to revocation processing, which
+ *        we explicitly do not support.
+ *
+ *        -- All other extensions are ignored if non-critical. If a
+ *        critical extension other than the ones above is encountered,
+ *        then a failure is reported.
+ *
+ *     -- End of the TBS: the multihash engine is stopped.
+ *
+ *     -- Signature algorithm: the signature algorithm on the
+ *     certificate is decoded. A failure is reported if that algorithm
+ *     is unknown. The hashed TBS corresponding to the signature hash
+ *     function is computed and stored in tbs_hash[] (if not supported,
+ *     then a failure is reported). The hash OID and length are stored
+ *     in cert_sig_hash_oid and cert_sig_hash_len.
+ *
+ *     -- Signature value: the signature value is copied into the
+ *     cert_sig[] array.
+ *
+ *     -- Certificate end: the hashed issuer DN (saved_dn_hash[]) is
+ *     looked up in the trust store (CA trust anchors only); for all
+ *     that match, the signature (cert_sig[]) is verified against the
+ *     anchor public key (hashed TBS is in tbs_hash[]). If one of these
+ *     signatures is valid, then validation ends with a success.
+ *
+ *  -- If the chain end is reached without obtaining a validation success,
+ *  then validation is reported as failed.
+ */
+
+#ifndef BR_USE_UNIX_TIME
+#if defined __unix__ || defined __linux__ \
+       || defined _POSIX_SOURCE || defined _POSIX_C_SOURCE \
+       || (defined __APPLE__ && defined __MACH__)
+#define BR_USE_UNIX_TIME   1
+#endif
+#endif
+
+#ifndef BR_USE_WIN32_TIME
+#if defined _WIN32 || defined _WIN64
+#define BR_USE_WIN32_TIME   1
+#endif
+#endif
+
+#if BR_USE_UNIX_TIME
+#include <time.h>
+#endif
+
+#if BR_USE_WIN32_TIME
+#include <windows.h>
+#endif
+
+void br_x509_minimal_init_main(void *ctx);
+void br_x509_minimal_run(void *ctx);
+
+/* see bearssl_x509.h */
+void
+br_x509_minimal_init(br_x509_minimal_context *ctx,
+       const br_hash_class *dn_hash_impl,
+       const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num)
+{
+       memset(ctx, 0, sizeof *ctx);
+       ctx->vtable = &br_x509_minimal_vtable;
+       ctx->dn_hash_impl = dn_hash_impl;
+       ctx->trust_anchors = trust_anchors;
+       ctx->trust_anchors_num = trust_anchors_num;
+}
+
+static void
+xm_start_chain(const br_x509_class **ctx,
+       unsigned expected_key_type, const char *server_name)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       memset(&cc->pkey, 0, sizeof cc->pkey);
+       cc->num_certs = 0;
+       cc->err = 0;
+       cc->cpu.dp = cc->dp_stack;
+       cc->cpu.rp = cc->rp_stack;
+       br_x509_minimal_init_main(&cc->cpu);
+       cc->expected_key_type = expected_key_type;
+       if (server_name == NULL || *server_name == 0) {
+               cc->server_name = NULL;
+       } else {
+               cc->server_name = server_name;
+       }
+}
+
+static void
+xm_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err != 0) {
+               return;
+       }
+       if (length == 0) {
+               cc->err = BR_ERR_X509_TRUNCATED;
+               return;
+       }
+       cc->cert_length = length;
+}
+
+static void
+xm_append(const br_x509_class **ctx, const unsigned char *buf, size_t len)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err != 0) {
+               return;
+       }
+       cc->hbuf = buf;
+       cc->hlen = len;
+       br_x509_minimal_run(&cc->cpu);
+}
+
+static void
+xm_end_cert(const br_x509_class **ctx)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err == 0 && cc->cert_length != 0) {
+               cc->err = BR_ERR_X509_TRUNCATED;
+       }
+       cc->num_certs ++;
+}
+
+static unsigned
+xm_end_chain(const br_x509_class **ctx)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err == 0) {
+               if (cc->num_certs == 0) {
+                       cc->err = BR_ERR_X509_EMPTY_CHAIN;
+               } else {
+                       cc->err = BR_ERR_X509_NOT_TRUSTED;
+               }
+       } else if (cc->err == BR_ERR_X509_OK) {
+               return 0;
+       }
+       return (unsigned)cc->err;
+}
+
+static const br_x509_pkey *
+xm_get_pkey(const br_x509_class *const *ctx)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err == BR_ERR_X509_OK
+               || cc->err == BR_ERR_X509_NOT_TRUSTED)
+       {
+               return &((br_x509_minimal_context *)ctx)->pkey;
+       } else {
+               return NULL;
+       }
+}
+
+/* see bearssl_x509.h */
+const br_x509_class br_x509_minimal_vtable = {
+       sizeof(br_x509_minimal_context),
+       xm_start_chain,
+       xm_start_cert,
+       xm_append,
+       xm_end_cert,
+       xm_end_chain,
+       xm_get_pkey
+};
+
+#define CTX   ((br_x509_minimal_context *)((unsigned char *)t0ctx - offsetof(br_x509_minimal_context, cpu)))
+#define CONTEXT_NAME   br_x509_minimal_context
+
+#define DNHASH_LEN   ((CTX->dn_hash_impl->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK)
+
+/*
+ * Hash a DN (from a trust anchor) into the provided buffer. This uses the
+ * DN hash implementation and context structure from the X.509 engine
+ * context.
+ */
+static void
+hash_dn(br_x509_minimal_context *ctx, const void *dn, size_t len,
+       unsigned char *out)
+{
+       ctx->dn_hash_impl->init(&ctx->dn_hash.vtable);
+       ctx->dn_hash_impl->update(&ctx->dn_hash.vtable, dn, len);
+       ctx->dn_hash_impl->out(&ctx->dn_hash.vtable, out);
+}
+
+/*
+ * Compare two big integers for equality. The integers use unsigned big-endian
+ * encoding; extra leading bytes (of value 0) are allowed.
+ */
+static int
+eqbigint(const unsigned char *b1, size_t len1,
+       const unsigned char *b2, size_t len2)
+{
+       while (len1 > 0 && *b1 == 0) {
+               b1 ++;
+               len1 --;
+       }
+       while (len2 > 0 && *b2 == 0) {
+               b2 ++;
+               len2 --;
+       }
+       if (len1 != len2) {
+               return 0;
+       }
+       return memcmp(b1, b2, len1) == 0;
+}
+
+/*
+ * Verify the signature on the certificate with the provided public key.
+ * This function checks the public key type with regards to the expected
+ * type. Returned value is either 0 on success, or a non-zero error code.
+ */
+static int
+verify_signature(br_x509_minimal_context *ctx, const br_x509_pkey *pk)
+{
+       int kt;
+
+       kt = ctx->cert_signer_key_type;
+       if ((pk->key_type & 0x0F) != kt) {
+               return BR_ERR_X509_WRONG_KEY_TYPE;
+       }
+       switch (kt) {
+               unsigned char tmp[64];
+
+       case BR_KEYTYPE_RSA:
+               if (ctx->irsa == 0) {
+                       return BR_ERR_X509_UNSUPPORTED;
+               }
+               if (!ctx->irsa(ctx->cert_sig, ctx->cert_sig_len,
+                       &t0_datablock[ctx->cert_sig_hash_oid],
+                       ctx->cert_sig_hash_len, &pk->key.rsa, tmp))
+               {
+                       return BR_ERR_X509_BAD_SIGNATURE;
+               }
+               if (memcmp(ctx->tbs_hash, tmp, ctx->cert_sig_hash_len) != 0) {
+                       return BR_ERR_X509_BAD_SIGNATURE;
+               }
+               return 0;
+
+       case BR_KEYTYPE_EC:
+               if (ctx->iecdsa == 0) {
+                       return BR_ERR_X509_UNSUPPORTED;
+               }
+               if (!ctx->iecdsa(ctx->iec, ctx->tbs_hash,
+                       ctx->cert_sig_hash_len, &pk->key.ec,
+                       ctx->cert_sig, ctx->cert_sig_len))
+               {
+                       return BR_ERR_X509_BAD_SIGNATURE;
+               }
+               return 0;
+
+       default:
+               return BR_ERR_X509_UNSUPPORTED;
+       }
+}
+
+/*
+ * Compare two strings for equality, in a case-insensitive way. This
+ * function handles casing only for ASCII letters.
+ */
+static int
+eqnocase(const void *s1, const void *s2, size_t len)
+{
+       const unsigned char *buf1, *buf2;
+
+       buf1 = s1;
+       buf2 = s2;
+       while (len -- > 0) {
+               int x1, x2;
+
+               x1 = *buf1 ++;
+               x2 = *buf2 ++;
+               if (x1 >= 'A' && x1 <= 'Z') {
+                       x1 += 'a' - 'A';
+               }
+               if (x2 >= 'A' && x2 <= 'Z') {
+                       x2 += 'a' - 'A';
+               }
+               if (x1 != x2) {
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+
+
+static const uint8_t t0_datablock[] = {
+       0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x09,
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x09, 0x2A, 0x86,
+       0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E, 0x09, 0x2A, 0x86, 0x48, 0x86,
+       0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+       0x01, 0x01, 0x0C, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
+       0x0D, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x09, 0x60, 0x86, 0x48, 0x01,
+       0x65, 0x03, 0x04, 0x02, 0x04, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+       0x04, 0x02, 0x01, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
+       0x02, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x07,
+       0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE,
+       0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B,
+       0x81, 0x04, 0x00, 0x23, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01,
+       0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x08, 0x2A, 0x86,
+       0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D,
+       0x04, 0x03, 0x03, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04,
+       0x00, 0x1F, 0x03, 0xFC, 0x07, 0x7F, 0x0B, 0x5E, 0x0F, 0x1F, 0x12, 0xFE,
+       0x16, 0xBF, 0x1A, 0x9F, 0x1E, 0x7E, 0x22, 0x3F, 0x26, 0x1E, 0x29, 0xDF,
+       0x00, 0x1F, 0x03, 0xFD, 0x07, 0x9F, 0x0B, 0x7E, 0x0F, 0x3F, 0x13, 0x1E,
+       0x16, 0xDF, 0x1A, 0xBF, 0x1E, 0x9E, 0x22, 0x5F, 0x26, 0x3E, 0x29, 0xFF,
+       0x03, 0x55, 0x1D, 0x13, 0x03, 0x55, 0x1D, 0x0F, 0x03, 0x55, 0x1D, 0x11,
+       0x03, 0x55, 0x1D, 0x23, 0x03, 0x55, 0x1D, 0x0E, 0x03, 0x55, 0x1D, 0x12,
+       0x03, 0x55, 0x1D, 0x09, 0x03, 0x55, 0x1D, 0x1F, 0x03, 0x55, 0x1D, 0x2E,
+       0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x08, 0x2B, 0x06,
+       0x01, 0x05, 0x05, 0x07, 0x01, 0x0B
+};
+
+static const uint8_t t0_codeblock[] = {
+       0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x01,
+       0x01, 0x09, 0x00, 0x00, 0x01, 0x01, 0x0A, 0x00, 0x00, 0x22, 0x22, 0x00,
+       0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_BOOLEAN), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_DN), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_SERVER_NAME), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_BAD_TIME), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_CRITICAL_EXTENSION), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_DN_MISMATCH), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_EXPIRED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_FORBIDDEN_KEY_USAGE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_CA), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_PARTIAL_BYTE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_WEAK_PUBLIC_KEY), 0x00, 0x00, 0x01,
+       T0_INT1(BR_ERR_X509_WRONG_KEY_TYPE), 0x00, 0x00, 0x01,
+       T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA),
+       0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_length)), 0x00,
+       0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_hash_len)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_hash_oid)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_len)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, cert_signer_key_type)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(CONTEXT_NAME, current_dn_hash)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(CONTEXT_NAME, expected_key_type)), 0x00, 0x00,
+       0x01, T0_INT2(offsetof(br_x509_minimal_context, pkey_data)), 0x01,
+       T0_INT2(BR_X509_BUFSIZE_KEY), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, min_rsa_size)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, next_dn_hash)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, num_certs)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, pad)), 0x00, 0x00, 0x01,
+       T0_INT2(offsetof(CONTEXT_NAME, saved_dn_hash)), 0x00, 0x00, 0x81, 0x31,
+       0x6E, 0x00, 0x00, 0x01, 0x80, 0x73, 0x00, 0x00, 0x01, 0x80, 0x7C, 0x00,
+       0x00, 0x01, 0x81, 0x02, 0x00, 0x00, 0x01, 0x82, 0x08, 0x00, 0x00, 0x01,
+       0x81, 0x70, 0x00, 0x00, 0x01, 0x81, 0x64, 0x00, 0x04, 0x03, 0x00, 0x03,
+       0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x03, 0x02, 0x01, 0x11, 0x06, 0x07,
+       0x02, 0x02, 0x02, 0x00, 0x0D, 0x04, 0x05, 0x02, 0x03, 0x02, 0x01, 0x0D,
+       0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x23, 0x02, 0x01, 0x13, 0x39, 0x02,
+       0x00, 0x0F, 0x15, 0x00, 0x00, 0x05, 0x02, 0x4F, 0x26, 0x00, 0x00, 0x06,
+       0x02, 0x50, 0x26, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x00, 0x11, 0x05,
+       0x02, 0x53, 0x26, 0x70, 0x00, 0x00, 0x11, 0x05, 0x02, 0x53, 0x26, 0x71,
+       0x00, 0x00, 0x06, 0x02, 0x49, 0x26, 0x00, 0x00, 0x01, 0x82, 0x00, 0x00,
+       0x00, 0x23, 0x1E, 0x01, 0x08, 0x0E, 0x39, 0x3D, 0x1E, 0x09, 0x00, 0x09,
+       0x03, 0x00, 0x59, 0x29, 0x81, 0x1D, 0x37, 0x81, 0x1D, 0x81, 0x20, 0x23,
+       0x01, 0x20, 0x11, 0x06, 0x15, 0x22, 0x70, 0x81, 0x1B, 0x81, 0x20, 0x01,
+       0x02, 0x74, 0x81, 0x1E, 0x01, 0x02, 0x12, 0x06, 0x02, 0x54, 0x26, 0x75,
+       0x81, 0x20, 0x01, 0x02, 0x74, 0x81, 0x1C, 0x81, 0x1D, 0x81, 0x2A, 0x81,
+       0x10, 0x63, 0x5F, 0x1F, 0x16, 0x81, 0x1D, 0x81, 0x15, 0x27, 0x67, 0x06,
+       0x02, 0x48, 0x26, 0x81, 0x15, 0x27, 0x6E, 0x06, 0x02, 0x48, 0x26, 0x75,
+       0x81, 0x10, 0x02, 0x00, 0x06, 0x05, 0x2B, 0x03, 0x01, 0x04, 0x08, 0x5F,
+       0x66, 0x1F, 0x25, 0x05, 0x02, 0x47, 0x26, 0x66, 0x63, 0x1F, 0x16, 0x81,
+       0x1D, 0x81, 0x1D, 0x81, 0x11, 0x05, 0x02, 0x54, 0x26, 0x81, 0x24, 0x24,
+       0x06, 0x2C, 0x81, 0x2A, 0x81, 0x12, 0x81, 0x1D, 0x61, 0x81, 0x18, 0x03,
+       0x03, 0x61, 0x39, 0x02, 0x03, 0x09, 0x39, 0x02, 0x03, 0x0A, 0x81, 0x18,
+       0x03, 0x04, 0x75, 0x62, 0x28, 0x01, 0x81, 0x00, 0x09, 0x02, 0x03, 0x12,
+       0x06, 0x02, 0x55, 0x26, 0x75, 0x58, 0x03, 0x02, 0x04, 0x3E, 0x81, 0x01,
+       0x24, 0x06, 0x37, 0x81, 0x11, 0x05, 0x02, 0x54, 0x26, 0x68, 0x24, 0x06,
+       0x04, 0x01, 0x17, 0x04, 0x12, 0x69, 0x24, 0x06, 0x04, 0x01, 0x18, 0x04,
+       0x0A, 0x6A, 0x24, 0x06, 0x04, 0x01, 0x19, 0x04, 0x02, 0x54, 0x26, 0x03,
+       0x05, 0x75, 0x81, 0x12, 0x23, 0x03, 0x06, 0x23, 0x61, 0x32, 0x0D, 0x06,
+       0x02, 0x4D, 0x26, 0x81, 0x13, 0x57, 0x03, 0x02, 0x04, 0x02, 0x54, 0x26,
+       0x75, 0x02, 0x00, 0x06, 0x33, 0x60, 0x2A, 0x01, 0x0F, 0x15, 0x23, 0x06,
+       0x09, 0x02, 0x02, 0x11, 0x05, 0x02, 0x56, 0x26, 0x04, 0x01, 0x22, 0x02,
+       0x02, 0x58, 0x2E, 0x11, 0x06, 0x08, 0x22, 0x02, 0x03, 0x02, 0x04, 0x1D,
+       0x04, 0x10, 0x57, 0x2E, 0x11, 0x06, 0x08, 0x22, 0x02, 0x05, 0x02, 0x06,
+       0x1C, 0x04, 0x03, 0x54, 0x26, 0x22, 0x04, 0x24, 0x02, 0x02, 0x58, 0x2E,
+       0x11, 0x06, 0x08, 0x22, 0x02, 0x03, 0x02, 0x04, 0x21, 0x04, 0x10, 0x57,
+       0x2E, 0x11, 0x06, 0x08, 0x22, 0x02, 0x05, 0x02, 0x06, 0x20, 0x04, 0x03,
+       0x54, 0x26, 0x22, 0x23, 0x06, 0x01, 0x26, 0x22, 0x01, 0x00, 0x03, 0x07,
+       0x81, 0x21, 0x01, 0x21, 0x81, 0x07, 0x01, 0x22, 0x81, 0x07, 0x23, 0x01,
+       0x23, 0x11, 0x06, 0x81, 0x36, 0x22, 0x70, 0x81, 0x1B, 0x81, 0x1D, 0x23,
+       0x06, 0x81, 0x28, 0x01, 0x00, 0x03, 0x08, 0x81, 0x1D, 0x81, 0x11, 0x22,
+       0x81, 0x20, 0x23, 0x01, 0x01, 0x11, 0x06, 0x06, 0x81, 0x14, 0x03, 0x08,
+       0x81, 0x20, 0x01, 0x04, 0x74, 0x81, 0x1B, 0x6D, 0x24, 0x06, 0x11, 0x02,
+       0x00, 0x06, 0x04, 0x81, 0x2B, 0x04, 0x06, 0x81, 0x0E, 0x01, 0x7F, 0x03,
+       0x07, 0x04, 0x80, 0x72, 0x81, 0x09, 0x24, 0x06, 0x07, 0x02, 0x00, 0x81,
+       0x0F, 0x04, 0x80, 0x66, 0x81, 0x2D, 0x24, 0x06, 0x13, 0x02, 0x00, 0x06,
+       0x0A, 0x01, 0x00, 0x03, 0x01, 0x81, 0x0D, 0x03, 0x01, 0x04, 0x02, 0x81,
+       0x2B, 0x04, 0x80, 0x4E, 0x6C, 0x24, 0x06, 0x05, 0x81, 0x2B, 0x04, 0x80,
+       0x45, 0x81, 0x30, 0x24, 0x06, 0x04, 0x81, 0x2B, 0x04, 0x3C, 0x81, 0x08,
+       0x24, 0x06, 0x04, 0x81, 0x2B, 0x04, 0x33, 0x81, 0x2E, 0x24, 0x06, 0x04,
+       0x81, 0x2B, 0x04, 0x2A, 0x76, 0x24, 0x06, 0x04, 0x81, 0x2B, 0x04, 0x22,
+       0x81, 0x00, 0x24, 0x06, 0x04, 0x81, 0x2B, 0x04, 0x19, 0x6B, 0x24, 0x06,
+       0x04, 0x81, 0x2B, 0x04, 0x11, 0x81, 0x2F, 0x24, 0x06, 0x04, 0x81, 0x2B,
+       0x04, 0x08, 0x02, 0x08, 0x06, 0x02, 0x46, 0x26, 0x81, 0x2B, 0x75, 0x75,
+       0x04, 0xFE, 0x54, 0x75, 0x75, 0x04, 0x08, 0x01, 0x7F, 0x11, 0x05, 0x02,
+       0x53, 0x26, 0x22, 0x75, 0x38, 0x02, 0x00, 0x06, 0x08, 0x02, 0x01, 0x3A,
+       0x2D, 0x05, 0x02, 0x42, 0x26, 0x02, 0x00, 0x06, 0x01, 0x17, 0x02, 0x00,
+       0x02, 0x07, 0x2D, 0x05, 0x02, 0x4E, 0x26, 0x81, 0x20, 0x72, 0x81, 0x1B,
+       0x81, 0x11, 0x06, 0x81, 0x07, 0x81, 0x25, 0x24, 0x06, 0x08, 0x01, 0x02,
+       0x58, 0x81, 0x02, 0x04, 0x80, 0x6C, 0x81, 0x26, 0x24, 0x06, 0x08, 0x01,
+       0x03, 0x58, 0x81, 0x03, 0x04, 0x80, 0x5F, 0x81, 0x27, 0x24, 0x06, 0x08,
+       0x01, 0x04, 0x58, 0x81, 0x04, 0x04, 0x80, 0x52, 0x81, 0x28, 0x24, 0x06,
+       0x08, 0x01, 0x05, 0x58, 0x81, 0x05, 0x04, 0x80, 0x45, 0x81, 0x29, 0x24,
+       0x06, 0x07, 0x01, 0x06, 0x58, 0x81, 0x06, 0x04, 0x39, 0x7B, 0x24, 0x06,
+       0x07, 0x01, 0x02, 0x57, 0x81, 0x02, 0x04, 0x2E, 0x7C, 0x24, 0x06, 0x07,
+       0x01, 0x03, 0x57, 0x81, 0x03, 0x04, 0x23, 0x7D, 0x24, 0x06, 0x07, 0x01,
+       0x04, 0x57, 0x81, 0x04, 0x04, 0x18, 0x7E, 0x24, 0x06, 0x07, 0x01, 0x05,
+       0x57, 0x81, 0x05, 0x04, 0x0D, 0x7F, 0x24, 0x06, 0x07, 0x01, 0x06, 0x57,
+       0x81, 0x06, 0x04, 0x02, 0x54, 0x26, 0x5C, 0x33, 0x5E, 0x35, 0x1B, 0x23,
+       0x05, 0x02, 0x54, 0x26, 0x5B, 0x35, 0x04, 0x02, 0x54, 0x26, 0x81, 0x2A,
+       0x81, 0x12, 0x23, 0x01, T0_INT2(BR_X509_BUFSIZE_SIG), 0x12, 0x06, 0x02,
+       0x4D, 0x26, 0x23, 0x5D, 0x33, 0x5A, 0x81, 0x13, 0x75, 0x75, 0x01, 0x00,
+       0x59, 0x34, 0x18, 0x00, 0x00, 0x01, 0x30, 0x0A, 0x23, 0x01, 0x00, 0x01,
+       0x09, 0x6F, 0x05, 0x02, 0x45, 0x26, 0x00, 0x00, 0x2E, 0x2E, 0x00, 0x00,
+       0x01, 0x81, 0x08, 0x00, 0x00, 0x01, 0x81, 0x10, 0x00, 0x00, 0x01, 0x81,
+       0x19, 0x00, 0x00, 0x01, 0x81, 0x22, 0x00, 0x00, 0x01, 0x81, 0x2B, 0x00,
+       0x00, 0x01, 0x82, 0x04, 0x00, 0x00, 0x01, 0x80, 0x6B, 0x00, 0x00, 0x01,
+       0x3D, 0x00, 0x00, 0x01, 0x80, 0x43, 0x00, 0x00, 0x01, 0x80, 0x4D, 0x00,
+       0x00, 0x01, 0x80, 0x57, 0x00, 0x00, 0x01, 0x80, 0x61, 0x00, 0x00, 0x2E,
+       0x11, 0x06, 0x07, 0x3F, 0x81, 0x1B, 0x81, 0x2A, 0x81, 0x21, 0x00, 0x00,
+       0x01, 0x81, 0x78, 0x00, 0x00, 0x01, 0x81, 0x68, 0x00, 0x00, 0x01, 0x7F,
+       0x78, 0x19, 0x01, 0x00, 0x78, 0x19, 0x04, 0x7A, 0x00, 0x01, 0x81, 0x34,
+       0x00, 0x01, 0x7A, 0x0D, 0x06, 0x02, 0x4C, 0x26, 0x23, 0x03, 0x00, 0x0A,
+       0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x81, 0x1D, 0x23, 0x06,
+       0x18, 0x81, 0x20, 0x01, 0x22, 0x11, 0x06, 0x0C, 0x71, 0x81, 0x1F, 0x22,
+       0x2B, 0x02, 0x00, 0x2D, 0x03, 0x00, 0x04, 0x03, 0x22, 0x81, 0x1C, 0x04,
+       0x65, 0x75, 0x02, 0x00, 0x00, 0x00, 0x81, 0x1D, 0x81, 0x21, 0x23, 0x01,
+       0x01, 0x11, 0x06, 0x0A, 0x81, 0x14, 0x05, 0x02, 0x4E, 0x26, 0x81, 0x21,
+       0x04, 0x02, 0x4E, 0x26, 0x23, 0x01, 0x02, 0x11, 0x06, 0x0E, 0x22, 0x71,
+       0x81, 0x1E, 0x64, 0x29, 0x3E, 0x0D, 0x06, 0x02, 0x4E, 0x26, 0x81, 0x21,
+       0x01, 0x7F, 0x10, 0x06, 0x02, 0x53, 0x26, 0x22, 0x75, 0x00, 0x02, 0x06,
+       0x10, 0x60, 0x2A, 0x01, 0x10, 0x15, 0x06, 0x04, 0x01, 0x30, 0x04, 0x03,
+       0x01, 0x81, 0x40, 0x04, 0x02, 0x01, 0x04, 0x03, 0x00, 0x81, 0x20, 0x01,
+       0x03, 0x74, 0x81, 0x1B, 0x81, 0x22, 0x03, 0x01, 0x02, 0x01, 0x01, 0x07,
+       0x12, 0x06, 0x02, 0x53, 0x26, 0x23, 0x01, 0x00, 0x2E, 0x11, 0x06, 0x05,
+       0x22, 0x4A, 0x26, 0x04, 0x17, 0x01, 0x01, 0x2E, 0x11, 0x06, 0x0B, 0x22,
+       0x81, 0x22, 0x02, 0x01, 0x14, 0x02, 0x01, 0x0E, 0x04, 0x06, 0x22, 0x81,
+       0x22, 0x01, 0x00, 0x22, 0x02, 0x00, 0x15, 0x05, 0x02, 0x4A, 0x26, 0x81,
+       0x2A, 0x00, 0x02, 0x36, 0x01, 0x00, 0x65, 0x35, 0x81, 0x1D, 0x23, 0x06,
+       0x80, 0x6F, 0x81, 0x20, 0x01, 0x11, 0x73, 0x81, 0x1B, 0x23, 0x05, 0x02,
+       0x41, 0x26, 0x23, 0x06, 0x80, 0x5B, 0x81, 0x1D, 0x81, 0x20, 0x01, 0x06,
+       0x74, 0x81, 0x1B, 0x23, 0x01, 0x03, 0x11, 0x06, 0x1E, 0x81, 0x22, 0x01,
+       0x10, 0x0E, 0x03, 0x00, 0x81, 0x22, 0x01, 0x08, 0x0E, 0x02, 0x00, 0x09,
+       0x03, 0x00, 0x81, 0x22, 0x02, 0x00, 0x09, 0x01, 0x82, 0xD4, 0x88, 0x03,
+       0x11, 0x03, 0x01, 0x81, 0x2A, 0x02, 0x01, 0x06, 0x23, 0x81, 0x20, 0x23,
+       0x23, 0x01, 0x0C, 0x11, 0x39, 0x01, 0x13, 0x11, 0x2D, 0x39, 0x01, 0x14,
+       0x11, 0x2D, 0x06, 0x07, 0x71, 0x81, 0x1F, 0x22, 0x75, 0x04, 0x07, 0x22,
+       0x01, 0x00, 0x65, 0x35, 0x81, 0x2A, 0x04, 0x02, 0x81, 0x2A, 0x04, 0xFF,
+       0x21, 0x75, 0x04, 0xFF, 0x0D, 0x75, 0x1A, 0x00, 0x00, 0x81, 0x20, 0x01,
+       0x06, 0x74, 0x81, 0x1F, 0x00, 0x00, 0x81, 0x20, 0x01, 0x03, 0x74, 0x81,
+       0x1B, 0x81, 0x22, 0x06, 0x02, 0x52, 0x26, 0x00, 0x00, 0x39, 0x23, 0x06,
+       0x07, 0x2F, 0x23, 0x06, 0x01, 0x19, 0x04, 0x76, 0x3F, 0x00, 0x00, 0x01,
+       0x01, 0x74, 0x81, 0x1A, 0x01, 0x01, 0x10, 0x06, 0x02, 0x40, 0x26, 0x81,
+       0x22, 0x3B, 0x00, 0x04, 0x81, 0x20, 0x23, 0x01, 0x17, 0x01, 0x18, 0x6F,
+       0x05, 0x02, 0x45, 0x26, 0x01, 0x18, 0x11, 0x03, 0x00, 0x71, 0x81, 0x1B,
+       0x81, 0x16, 0x02, 0x00, 0x06, 0x0D, 0x01, 0x80, 0x64, 0x08, 0x03, 0x01,
+       0x81, 0x16, 0x02, 0x01, 0x09, 0x04, 0x0E, 0x23, 0x01, 0x32, 0x0D, 0x06,
+       0x04, 0x01, 0x80, 0x64, 0x09, 0x01, 0x8E, 0x6C, 0x09, 0x03, 0x01, 0x02,
+       0x01, 0x01, 0x82, 0x6D, 0x08, 0x02, 0x01, 0x01, 0x03, 0x09, 0x01, 0x04,
+       0x0C, 0x09, 0x02, 0x01, 0x01, 0x80, 0x63, 0x09, 0x01, 0x80, 0x64, 0x0C,
+       0x0A, 0x02, 0x01, 0x01, 0x83, 0x0F, 0x09, 0x01, 0x83, 0x10, 0x0C, 0x09,
+       0x03, 0x03, 0x01, 0x01, 0x01, 0x0C, 0x81, 0x17, 0x3E, 0x01, 0x01, 0x0E,
+       0x02, 0x01, 0x01, 0x04, 0x07, 0x3C, 0x02, 0x01, 0x01, 0x80, 0x64, 0x07,
+       0x3B, 0x02, 0x01, 0x01, 0x83, 0x10, 0x07, 0x3C, 0x2D, 0x15, 0x06, 0x03,
+       0x01, 0x18, 0x09, 0x81, 0x0B, 0x09, 0x77, 0x23, 0x01, 0x05, 0x14, 0x02,
+       0x03, 0x09, 0x03, 0x03, 0x01, 0x1F, 0x15, 0x01, 0x01, 0x39, 0x81, 0x17,
+       0x02, 0x03, 0x09, 0x3E, 0x03, 0x03, 0x01, 0x00, 0x01, 0x17, 0x81, 0x17,
+       0x01, 0x9C, 0x10, 0x08, 0x03, 0x02, 0x01, 0x00, 0x01, 0x3B, 0x81, 0x17,
+       0x01, 0x3C, 0x08, 0x02, 0x02, 0x09, 0x03, 0x02, 0x01, 0x00, 0x01, 0x3C,
+       0x81, 0x17, 0x02, 0x02, 0x09, 0x03, 0x02, 0x81, 0x22, 0x23, 0x01, 0x2E,
+       0x11, 0x06, 0x0E, 0x22, 0x81, 0x22, 0x23, 0x01, 0x30, 0x01, 0x39, 0x6F,
+       0x06, 0x03, 0x22, 0x04, 0x73, 0x01, 0x80, 0x5A, 0x10, 0x06, 0x02, 0x45,
+       0x26, 0x75, 0x02, 0x03, 0x02, 0x02, 0x00, 0x01, 0x81, 0x22, 0x79, 0x01,
+       0x0A, 0x08, 0x03, 0x00, 0x81, 0x22, 0x79, 0x02, 0x00, 0x09, 0x00, 0x02,
+       0x03, 0x00, 0x03, 0x01, 0x81, 0x16, 0x23, 0x02, 0x01, 0x02, 0x00, 0x6F,
+       0x05, 0x02, 0x45, 0x26, 0x00, 0x00, 0x32, 0x81, 0x20, 0x01, 0x02, 0x74,
+       0x0B, 0x81, 0x19, 0x00, 0x03, 0x23, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+       0x81, 0x1B, 0x81, 0x22, 0x23, 0x01, 0x81, 0x00, 0x13, 0x06, 0x02, 0x51,
+       0x26, 0x23, 0x01, 0x00, 0x11, 0x06, 0x0C, 0x22, 0x23, 0x05, 0x04, 0x22,
+       0x01, 0x00, 0x00, 0x81, 0x22, 0x04, 0x6E, 0x02, 0x01, 0x23, 0x05, 0x02,
+       0x4D, 0x26, 0x3E, 0x03, 0x01, 0x02, 0x02, 0x35, 0x02, 0x02, 0x3D, 0x03,
+       0x02, 0x23, 0x06, 0x04, 0x81, 0x22, 0x04, 0x67, 0x22, 0x02, 0x00, 0x02,
+       0x01, 0x0A, 0x00, 0x01, 0x81, 0x22, 0x23, 0x01, 0x81, 0x00, 0x0D, 0x06,
+       0x01, 0x00, 0x01, 0x81, 0x00, 0x0A, 0x23, 0x05, 0x02, 0x4B, 0x26, 0x03,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x12, 0x06, 0x1A, 0x02, 0x00,
+       0x3E, 0x03, 0x00, 0x23, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x12, 0x06, 0x02,
+       0x4C, 0x26, 0x01, 0x08, 0x0E, 0x39, 0x81, 0x22, 0x32, 0x09, 0x04, 0x5F,
+       0x00, 0x00, 0x81, 0x1A, 0x81, 0x0C, 0x00, 0x00, 0x81, 0x1B, 0x81, 0x2A,
+       0x00, 0x00, 0x81, 0x20, 0x72, 0x81, 0x1B, 0x00, 0x01, 0x81, 0x1B, 0x23,
+       0x05, 0x02, 0x51, 0x26, 0x81, 0x22, 0x23, 0x01, 0x81, 0x00, 0x13, 0x06,
+       0x02, 0x51, 0x26, 0x03, 0x00, 0x23, 0x06, 0x17, 0x81, 0x22, 0x02, 0x00,
+       0x23, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x13, 0x06, 0x02, 0x51, 0x26, 0x01,
+       0x08, 0x0E, 0x09, 0x03, 0x00, 0x04, 0x66, 0x22, 0x02, 0x00, 0x00, 0x00,
+       0x81, 0x1B, 0x23, 0x01, 0x81, 0x7F, 0x12, 0x06, 0x09, 0x81, 0x2A, 0x01,
+       0x00, 0x65, 0x35, 0x01, 0x00, 0x00, 0x23, 0x65, 0x35, 0x65, 0x3D, 0x81,
+       0x13, 0x01, 0x7F, 0x00, 0x01, 0x81, 0x22, 0x03, 0x00, 0x02, 0x00, 0x01,
+       0x05, 0x14, 0x01, 0x01, 0x15, 0x2C, 0x02, 0x00, 0x01, 0x06, 0x14, 0x23,
+       0x01, 0x01, 0x15, 0x06, 0x02, 0x43, 0x26, 0x01, 0x04, 0x0E, 0x02, 0x00,
+       0x01, 0x1F, 0x15, 0x23, 0x01, 0x1F, 0x11, 0x06, 0x02, 0x44, 0x26, 0x09,
+       0x00, 0x00, 0x23, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x81, 0x20,
+       0x00, 0x00, 0x23, 0x05, 0x02, 0x4C, 0x26, 0x3E, 0x81, 0x23, 0x00, 0x00,
+       0x30, 0x23, 0x01, 0x00, 0x13, 0x06, 0x01, 0x00, 0x22, 0x19, 0x04, 0x74,
+       0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, 0x15, 0x00,
+       0x00, 0x01, 0x1F, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, 0x33, 0x00,
+       0x00, 0x81, 0x2B, 0x22, 0x00, 0x00, 0x23, 0x06, 0x08, 0x81, 0x2C, 0x23,
+       0x06, 0x01, 0x19, 0x04, 0x75, 0x00, 0x00, 0x01, 0x00, 0x2E, 0x2F, 0x0B,
+       0x3F, 0x00, 0x00, 0x01, 0x81, 0x6C, 0x00, 0x00, 0x01, 0x81, 0x7C, 0x00,
+       0x00, 0x01, 0x82, 0x11, 0x00, 0x00, 0x01, 0x81, 0x74, 0x00, 0x00, 0x01,
+       0x03, 0x31, 0x01, 0x03, 0x31, 0x00
+};
+
+static const uint16_t t0_caddr[] = {
+       0,
+       5,
+       10,
+       15,
+       20,
+       24,
+       28,
+       32,
+       36,
+       40,
+       44,
+       48,
+       52,
+       56,
+       60,
+       64,
+       68,
+       72,
+       76,
+       80,
+       84,
+       88,
+       92,
+       96,
+       100,
+       104,
+       108,
+       112,
+       116,
+       120,
+       124,
+       129,
+       134,
+       139,
+       144,
+       149,
+       154,
+       159,
+       164,
+       172,
+       177,
+       182,
+       187,
+       192,
+       197,
+       202,
+       207,
+       212,
+       217,
+       222,
+       227,
+       232,
+       261,
+       276,
+       282,
+       288,
+       293,
+       301,
+       309,
+       315,
+       320,
+       331,
+       1053,
+       1068,
+       1072,
+       1077,
+       1082,
+       1087,
+       1092,
+       1097,
+       1102,
+       1107,
+       1111,
+       1116,
+       1121,
+       1126,
+       1131,
+       1144,
+       1149,
+       1154,
+       1165,
+       1170,
+       1184,
+       1222,
+       1275,
+       1363,
+       1489,
+       1498,
+       1513,
+       1527,
+       1544,
+       1776,
+       1792,
+       1810,
+       1821,
+       1892,
+       1950,
+       1956,
+       1962,
+       1969,
+       2020,
+       2049,
+       2094,
+       2106,
+       2116,
+       2129,
+       2133,
+       2137,
+       2141,
+       2145,
+       2149,
+       2153,
+       2158,
+       2171,
+       2179,
+       2184,
+       2189,
+       2194,
+       2199
+};
+
+#define T0_INTERPRETED   59
+
+#define T0_ENTER(ip, rp, slot)   do { \
+               const unsigned char *t0_newip; \
+               uint32_t t0_lnum; \
+               t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
+               t0_lnum = t0_parse7E_unsigned(&t0_newip); \
+               (rp) += t0_lnum; \
+               *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
+               (ip) = t0_newip; \
+       } while (0)
+
+#define T0_DEFENTRY(name, slot) \
+void \
+name(void *ctx) \
+{ \
+       t0_context *t0ctx = ctx; \
+       t0ctx->ip = &t0_codeblock[0]; \
+       T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
+}
+
+T0_DEFENTRY(br_x509_minimal_init_main, 138)
+
+void
+br_x509_minimal_run(void *t0ctx)
+{
+       uint32_t *dp, *rp;
+       const unsigned char *ip;
+
+#define T0_LOCAL(x)    (*(rp - 2 - (x)))
+#define T0_POP()       (*-- dp)
+#define T0_POPi()      (*(int32_t *)(-- dp))
+#define T0_PEEK(x)     (*(dp - 1 - (x)))
+#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
+#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
+#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
+#define T0_RPOP()      (*-- rp)
+#define T0_RPOPi()     (*(int32_t *)(-- rp))
+#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
+#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
+#define T0_ROLL(x)     do { \
+       size_t t0len = (size_t)(x); \
+       uint32_t t0tmp = *(dp - 1 - t0len); \
+       memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_SWAP()      do { \
+       uint32_t t0tmp = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_ROT()       do { \
+       uint32_t t0tmp = *(dp - 3); \
+       *(dp - 3) = *(dp - 2); \
+       *(dp - 2) = *(dp - 1); \
+       *(dp - 1) = t0tmp; \
+} while (0)
+#define T0_NROT()       do { \
+       uint32_t t0tmp = *(dp - 1); \
+       *(dp - 1) = *(dp - 2); \
+       *(dp - 2) = *(dp - 3); \
+       *(dp - 3) = t0tmp; \
+} while (0)
+#define T0_PICK(x)      do { \
+       uint32_t t0depth = (x); \
+       T0_PUSH(T0_PEEK(t0depth)); \
+} while (0)
+#define T0_CO()         do { \
+       goto t0_exit; \
+} while (0)
+#define T0_RET()        break
+
+       dp = ((t0_context *)t0ctx)->dp;
+       rp = ((t0_context *)t0ctx)->rp;
+       ip = ((t0_context *)t0ctx)->ip;
+       for (;;) {
+               uint32_t t0x;
+
+               t0x = t0_parse7E_unsigned(&ip);
+               if (t0x < T0_INTERPRETED) {
+                       switch (t0x) {
+                               int32_t t0off;
+
+                       case 0: /* ret */
+                               t0x = T0_RPOP();
+                               rp -= (t0x >> 16);
+                               t0x &= 0xFFFF;
+                               if (t0x == 0) {
+                                       ip = NULL;
+                                       goto t0_exit;
+                               }
+                               ip = &t0_codeblock[t0x];
+                               break;
+                       case 1: /* literal constant */
+                               T0_PUSHi(t0_parse7E_signed(&ip));
+                               break;
+                       case 2: /* read local */
+                               T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
+                               break;
+                       case 3: /* write local */
+                               T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
+                               break;
+                       case 4: /* jump */
+                               t0off = t0_parse7E_signed(&ip);
+                               ip += t0off;
+                               break;
+                       case 5: /* jump if */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 6: /* jump if not */
+                               t0off = t0_parse7E_signed(&ip);
+                               if (!T0_POP()) {
+                                       ip += t0off;
+                               }
+                               break;
+                       case 7: {
+                               /* %25 */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSHi(a % b);
+
+                               }
+                               break;
+                       case 8: {
+                               /* * */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a * b);
+
+                               }
+                               break;
+                       case 9: {
+                               /* + */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a + b);
+
+                               }
+                               break;
+                       case 10: {
+                               /* - */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a - b);
+
+                               }
+                               break;
+                       case 11: {
+                               /* -rot */
+ T0_NROT(); 
+                               }
+                               break;
+                       case 12: {
+                               /* / */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSHi(a / b);
+
+                               }
+                               break;
+                       case 13: {
+                               /* < */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a < b));
+
+                               }
+                               break;
+                       case 14: {
+                               /* << */
+
+       int c = (int)T0_POPi();
+       uint32_t x = T0_POP();
+       T0_PUSH(x << c);
+
+                               }
+                               break;
+                       case 15: {
+                               /* <= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a <= b));
+
+                               }
+                               break;
+                       case 16: {
+                               /* <> */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a != b));
+
+                               }
+                               break;
+                       case 17: {
+                               /* = */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(-(uint32_t)(a == b));
+
+                               }
+                               break;
+                       case 18: {
+                               /* > */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a > b));
+
+                               }
+                               break;
+                       case 19: {
+                               /* >= */
+
+       int32_t b = T0_POPi();
+       int32_t a = T0_POPi();
+       T0_PUSH(-(uint32_t)(a >= b));
+
+                               }
+                               break;
+                       case 20: {
+                               /* >> */
+
+       int c = (int)T0_POPi();
+       int32_t x = T0_POPi();
+       T0_PUSHi(x >> c);
+
+                               }
+                               break;
+                       case 21: {
+                               /* and */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a & b);
+
+                               }
+                               break;
+                       case 22: {
+                               /* blobcopy */
+
+       size_t len = T0_POP();
+       unsigned char *src = (unsigned char *)CTX + T0_POP();
+       unsigned char *dst = (unsigned char *)CTX + T0_POP();
+       memcpy(dst, src, len);
+
+                               }
+                               break;
+                       case 23: {
+                               /* check-direct-trust */
+
+       size_t u;
+
+       for (u = 0; u < CTX->trust_anchors_num; u ++) {
+               const br_x509_trust_anchor *ta;
+               unsigned char hashed_DN[64];
+               int kt;
+
+               ta = &CTX->trust_anchors[u];
+               if (ta->flags & BR_X509_TA_CA) {
+                       continue;
+               }
+               hash_dn(CTX, ta->dn, ta->dn_len, hashed_DN);
+               if (memcmp(hashed_DN, CTX->current_dn_hash, DNHASH_LEN)) {
+                       continue;
+               }
+               kt = CTX->pkey.key_type;
+               if ((ta->pkey.key_type & 0x0F) != kt) {
+                       continue;
+               }
+               switch (kt) {
+
+               case BR_KEYTYPE_RSA:
+                       if (!eqbigint(CTX->pkey.key.rsa.n,
+                               CTX->pkey.key.rsa.nlen,
+                               ta->pkey.key.rsa.n,
+                               ta->pkey.key.rsa.nlen)
+                               || !eqbigint(CTX->pkey.key.rsa.e,
+                               CTX->pkey.key.rsa.elen,
+                               ta->pkey.key.rsa.e,
+                               ta->pkey.key.rsa.elen))
+                       {
+                               continue;
+                       }
+                       break;
+
+               case BR_KEYTYPE_EC:
+                       if (CTX->pkey.key.ec.curve != ta->pkey.key.ec.curve
+                               || CTX->pkey.key.ec.qlen != ta->pkey.key.ec.qlen
+                               || memcmp(CTX->pkey.key.ec.q,
+                                       ta->pkey.key.ec.q,
+                                       ta->pkey.key.ec.qlen) != 0)
+                       {
+                               continue;
+                       }
+                       break;
+
+               default:
+                       continue;
+               }
+
+               /*
+                * Direct trust match!
+                */
+               CTX->err = BR_ERR_X509_OK;
+               T0_CO();
+       }
+
+                               }
+                               break;
+                       case 24: {
+                               /* check-trust-anchor-CA */
+
+       size_t u;
+
+       for (u = 0; u < CTX->trust_anchors_num; u ++) {
+               const br_x509_trust_anchor *ta;
+               unsigned char hashed_DN[64];
+
+               ta = &CTX->trust_anchors[u];
+               if (!(ta->flags & BR_X509_TA_CA)) {
+                       continue;
+               }
+               hash_dn(CTX, ta->dn, ta->dn_len, hashed_DN);
+               if (memcmp(hashed_DN, CTX->saved_dn_hash, DNHASH_LEN)) {
+                       continue;
+               }
+               if (verify_signature(CTX, &ta->pkey) == 0) {
+                       CTX->err = BR_ERR_X509_OK;
+                       T0_CO();
+               }
+       }
+
+                               }
+                               break;
+                       case 25: {
+                               /* co */
+ T0_CO(); 
+                               }
+                               break;
+                       case 26: {
+                               /* compute-dn-hash */
+
+       CTX->dn_hash_impl->out(&CTX->dn_hash.vtable, CTX->current_dn_hash);
+       CTX->do_dn_hash = 0;
+
+                               }
+                               break;
+                       case 27: {
+                               /* compute-tbs-hash */
+
+       int id = T0_POPi();
+       size_t len;
+       len = br_multihash_out(&CTX->mhash, id, CTX->tbs_hash);
+       T0_PUSH(len);
+
+                               }
+                               break;
+                       case 28: {
+                               /* copy-ee-ec-pkey */
+
+       size_t qlen = T0_POP();
+       uint32_t curve = T0_POP();
+       memcpy(CTX->ee_pkey_data, CTX->pkey_data, qlen);
+       CTX->pkey.key_type = BR_KEYTYPE_EC;
+       CTX->pkey.key.ec.curve = curve;
+       CTX->pkey.key.ec.q = CTX->ee_pkey_data;
+       CTX->pkey.key.ec.qlen = qlen;
+
+                               }
+                               break;
+                       case 29: {
+                               /* copy-ee-rsa-pkey */
+
+       size_t elen = T0_POP();
+       size_t nlen = T0_POP();
+       memcpy(CTX->ee_pkey_data, CTX->pkey_data, nlen + elen);
+       CTX->pkey.key_type = BR_KEYTYPE_RSA;
+       CTX->pkey.key.rsa.n = CTX->ee_pkey_data;
+       CTX->pkey.key.rsa.nlen = nlen;
+       CTX->pkey.key.rsa.e = CTX->ee_pkey_data + nlen;
+       CTX->pkey.key.rsa.elen = elen;
+
+                               }
+                               break;
+                       case 30: {
+                               /* data-get8 */
+
+       size_t addr = T0_POP();
+       T0_PUSH(t0_datablock[addr]);
+
+                               }
+                               break;
+                       case 31: {
+                               /* dn-hash-length */
+
+       T0_PUSH(DNHASH_LEN);
+
+                               }
+                               break;
+                       case 32: {
+                               /* do-ecdsa-vrfy */
+
+       size_t qlen = T0_POP();
+       int curve = T0_POP();
+       br_x509_pkey pk;
+
+       pk.key_type = BR_KEYTYPE_EC;
+       pk.key.ec.curve = curve;
+       pk.key.ec.q = CTX->pkey_data;
+       pk.key.ec.qlen = qlen;
+       T0_PUSH(verify_signature(CTX, &pk));
+
+                               }
+                               break;
+                       case 33: {
+                               /* do-rsa-vrfy */
+
+       size_t elen = T0_POP();
+       size_t nlen = T0_POP();
+       br_x509_pkey pk;
+
+       pk.key_type = BR_KEYTYPE_RSA;
+       pk.key.rsa.n = CTX->pkey_data;
+       pk.key.rsa.nlen = nlen;
+       pk.key.rsa.e = CTX->pkey_data + nlen;
+       pk.key.rsa.elen = elen;
+       T0_PUSH(verify_signature(CTX, &pk));
+
+                               }
+                               break;
+                       case 34: {
+                               /* drop */
+ (void)T0_POP(); 
+                               }
+                               break;
+                       case 35: {
+                               /* dup */
+ T0_PUSH(T0_PEEK(0)); 
+                               }
+                               break;
+                       case 36: {
+                               /* eqOID */
+
+       const unsigned char *a2 = &t0_datablock[T0_POP()];
+       const unsigned char *a1 = &CTX->pad[0];
+       size_t len = a1[0];
+       int x;
+       if (len == a2[0]) {
+               x = -(memcmp(a1 + 1, a2 + 1, len) == 0);
+       } else {
+               x = 0;
+       }
+       T0_PUSH((uint32_t)x);
+
+                               }
+                               break;
+                       case 37: {
+                               /* eqblob */
+
+       size_t len = T0_POP();
+       const unsigned char *a2 = (const unsigned char *)CTX + T0_POP();
+       const unsigned char *a1 = (const unsigned char *)CTX + T0_POP();
+       T0_PUSHi(-(memcmp(a1, a2, len) == 0));
+
+                               }
+                               break;
+                       case 38: {
+                               /* fail */
+
+       CTX->err = T0_POPi();
+       T0_CO();
+
+                               }
+                               break;
+                       case 39: {
+                               /* get-system-date */
+
+       if (CTX->days == 0 && CTX->seconds == 0) {
+#if BR_USE_UNIX_TIME
+               time_t x = time(NULL);
+
+               T0_PUSH((uint32_t)(x / 86400) + 719528);
+               T0_PUSH((uint32_t)(x % 86400));
+#elif BR_USE_WIN32_TIME
+               FILETIME ft;
+               uint64_t x;
+
+               GetSystemTimeAsFileTime(&ft);
+               x = ((uint64_t)ft.dwHighDateTime << 32)
+                       + (uint64_t)ft.dwLowDateTime;
+               x = (x / 10000000);
+               T0_PUSH((uint32_t)(x / 86400) + 584754);
+               T0_PUSH((uint32_t)(x % 86400));
+#else
+               CTX->err = BR_ERR_X509_TIME_UNKNOWN;
+               T0_CO();
+#endif
+       } else {
+               T0_PUSH(CTX->days);
+               T0_PUSH(CTX->seconds);
+       }
+
+                               }
+                               break;
+                       case 40: {
+                               /* get16 */
+
+       uint32_t addr = T0_POP();
+       T0_PUSH(*(uint16_t *)((unsigned char *)CTX + addr));
+
+                               }
+                               break;
+                       case 41: {
+                               /* get32 */
+
+       uint32_t addr = T0_POP();
+       T0_PUSH(*(uint32_t *)((unsigned char *)CTX + addr));
+
+                               }
+                               break;
+                       case 42: {
+                               /* get8 */
+
+       uint32_t addr = T0_POP();
+       T0_PUSH(*((unsigned char *)CTX + addr));
+
+                               }
+                               break;
+                       case 43: {
+                               /* match-server-name */
+
+       size_t n1, n2;
+
+       if (CTX->server_name == NULL) {
+               T0_PUSH(0);
+               T0_RET();
+       }
+       n1 = strlen(CTX->server_name);
+       n2 = CTX->pad[0];
+       if (n1 == n2 && eqnocase(&CTX->pad[1], CTX->server_name, n1)) {
+               T0_PUSHi(-1);
+               T0_RET();
+       }
+       if (n2 >= 2 && CTX->pad[1] == '*' && CTX->pad[2] == '.') {
+               size_t u;
+
+               u = 0;
+               while (u < n1 && CTX->server_name[u] != '.') {
+                       u ++;
+               }
+               u ++;
+               n1 -= u;
+               if ((n2 - 2) == n1
+                       && eqnocase(&CTX->pad[3], CTX->server_name + u, n1))
+               {
+                       T0_PUSHi(-1);
+                       T0_RET();
+               }
+       }
+       T0_PUSH(0);
+
+                               }
+                               break;
+                       case 44: {
+                               /* neg */
+
+       uint32_t a = T0_POP();
+       T0_PUSH(-a);
+
+                               }
+                               break;
+                       case 45: {
+                               /* or */
+
+       uint32_t b = T0_POP();
+       uint32_t a = T0_POP();
+       T0_PUSH(a | b);
+
+                               }
+                               break;
+                       case 46: {
+                               /* over */
+ T0_PUSH(T0_PEEK(1)); 
+                               }
+                               break;
+                       case 47: {
+                               /* read-blob-inner */
+
+       uint32_t len = T0_POP();
+       uint32_t addr = T0_POP();
+       size_t clen = CTX->hlen;
+       if (clen > len) {
+               clen = (size_t)len;
+       }
+       if (addr != 0) {
+               memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen);
+       }
+       if (CTX->do_mhash) {
+               br_multihash_update(&CTX->mhash, CTX->hbuf, clen);
+       }
+       if (CTX->do_dn_hash) {
+               CTX->dn_hash_impl->update(
+                       &CTX->dn_hash.vtable, CTX->hbuf, clen);
+       }
+       CTX->hbuf += clen;
+       CTX->hlen -= clen;
+       T0_PUSH(addr + clen);
+       T0_PUSH(len - clen);
+
+                               }
+                               break;
+                       case 48: {
+                               /* read8-low */
+
+       if (CTX->hlen == 0) {
+               T0_PUSHi(-1);
+       } else {
+               unsigned char x = *CTX->hbuf ++;
+               if (CTX->do_mhash) {
+                       br_multihash_update(&CTX->mhash, &x, 1);
+               }
+               if (CTX->do_dn_hash) {
+                       CTX->dn_hash_impl->update(&CTX->dn_hash.vtable, &x, 1);
+               }
+               CTX->hlen --;
+               T0_PUSH(x);
+       }
+
+                               }
+                               break;
+                       case 49: {
+                               /* roll */
+ T0_ROLL(T0_POP()); 
+                               }
+                               break;
+                       case 50: {
+                               /* rot */
+ T0_ROT(); 
+                               }
+                               break;
+                       case 51: {
+                               /* set16 */
+
+       uint32_t addr = T0_POP();
+       *(uint16_t *)((unsigned char *)CTX + addr) = T0_POP();
+
+                               }
+                               break;
+                       case 52: {
+                               /* set32 */
+
+       uint32_t addr = T0_POP();
+       *(uint32_t *)((unsigned char *)CTX + addr) = T0_POP();
+
+                               }
+                               break;
+                       case 53: {
+                               /* set8 */
+
+       uint32_t addr = T0_POP();
+       *((unsigned char *)CTX + addr) = (unsigned char)T0_POP();
+
+                               }
+                               break;
+                       case 54: {
+                               /* start-dn-hash */
+
+       CTX->dn_hash_impl->init(&CTX->dn_hash.vtable);
+       CTX->do_dn_hash = 1;
+
+                               }
+                               break;
+                       case 55: {
+                               /* start-tbs-hash */
+
+       br_multihash_init(&CTX->mhash);
+       CTX->do_mhash = 1;
+
+                               }
+                               break;
+                       case 56: {
+                               /* stop-tbs-hash */
+
+       CTX->do_mhash = 0;
+
+                               }
+                               break;
+                       case 57: {
+                               /* swap */
+ T0_SWAP(); 
+                               }
+                               break;
+                       case 58: {
+                               /* zero-server-name */
+
+       T0_PUSHi(-(CTX->server_name == NULL));
+
+                               }
+                               break;
+                       }
+
+               } else {
+                       T0_ENTER(ip, rp, t0x);
+               }
+       }
+t0_exit:
+       ((t0_context *)t0ctx)->dp = dp;
+       ((t0_context *)t0ctx)->rp = rp;
+       ((t0_context *)t0ctx)->ip = ip;
+}
diff --git a/src/x509/x509_minimal.t0 b/src/x509/x509_minimal.t0
new file mode 100644 (file)
index 0000000..bdb3e18
--- /dev/null
@@ -0,0 +1,1294 @@
+\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+\
+\ Permission is hereby granted, free of charge, to any person obtaining 
+\ a copy of this software and associated documentation files (the
+\ "Software"), to deal in the Software without restriction, including
+\ without limitation the rights to use, copy, modify, merge, publish,
+\ distribute, sublicense, and/or sell copies of the Software, and to
+\ permit persons to whom the Software is furnished to do so, subject to
+\ the following conditions:
+\
+\ The above copyright notice and this permission notice shall be 
+\ included in all copies or substantial portions of the Software.
+\
+\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+\ SOFTWARE.
+
+preamble {
+
+#include "inner.h"
+
+/*
+ * Implementation Notes
+ * --------------------
+ *
+ * The C code pushes the data by chunks; all decoding is done in the
+ * T0 code. The cert_length value is set to the certificate length when
+ * a new certificate is started; the T0 code picks it up as outer limit,
+ * and decoding functions use it to ensure that no attempt is made at
+ * reading past it. The T0 code also checks that once the certificate is
+ * decoded, there are no trailing bytes.
+ *
+ * The T0 code sets cert_length to 0 when the certificate is fully
+ * decoded.
+ *
+ * The C code must still perform two checks:
+ *
+ *  -- If the certificate length is 0, then the T0 code will not be
+ *  invoked at all. This invalid condition must thus be reported by the
+ *  C code.
+ *
+ *  -- When reaching the end of certificate, the C code must verify that
+ *  the certificate length has been set to 0, thereby signaling that
+ *  the T0 code properly decoded a certificate.
+ *
+ * Processing of a chain works in the following way:
+ *
+ *  -- The error flag is set to a non-zero value when validation is
+ *  finished. The value is either BR_ERR_X509_OK (validation is
+ *  successful) or another non-zero error code. When a non-zero error
+ *  code is obtained, the remaining bytes in the current certificate and
+ *  the subsequent certificates (if any) are completely ignored.
+ *
+ *  -- Each certificate is decoded in due course, with the following
+ *  "interesting points":
+ *
+ *     -- Start of the TBS: the multihash engine is reset and activated.
+ *
+ *     -- Start of the issuer DN: the secondary hash engine is started,
+ *     to process the encoded issuer DN.
+ *
+ *     -- End of the issuer DN: the secondary hash engine is stopped. The
+ *     resulting hash value is computed and then copied into the
+ *     next_dn_hash[] buffer.
+ *
+ *     -- Start of the subject DN: the secondary hash engine is started,
+ *     to process the encoded subject DN.
+ *
+ *     -- For the EE certificate only: the Common Name, if any, is matched
+ *     against the expected server name.
+ *
+ *     -- End of the subject DN: the secondary hash engine is stopped. The
+ *     resulting hash value is computed into the pad. It is then processed:
+ *
+ *        -- If this is the EE certificate, then the hash is ignored
+ *        (except for direct trust processing, see later; the hash is
+ *        simply left in current_dn_hash[]).
+ *
+ *        -- Otherwise, the hashed subject DN is compared with the saved
+ *        hash value (in saved_dn_hash[]). They must match.
+ *
+ *     Either way, the next_dn_hash[] value is then copied into the
+ *     saved_dn_hash[] value. Thus, at that point, saved_dn_hash[]
+ *     contains the hash of the issuer DN for the current certificate,
+ *     and current_dn_hash[] contains the hash of the subject DN for the
+ *     current certificate.
+ *
+ *     -- Public key: it is decoded into the cert_pkey[] buffer. Unknown
+ *     key types are reported at that point.
+ *
+ *        -- If this is the EE certificate, then the key type is compared
+ *        with the expected key type (initialization parameter). The public
+ *        key data is copied to ee_pkey_data[]. The key and hashed subject
+ *        DN are also compared with the "direct trust" keys; if the key
+ *        and DN are matched, then validation ends with a success.
+ *
+ *        -- Otherwise, the saved signature (cert_sig[]) is verified
+ *        against the saved TBS hash (tbs_hash[]) and that freshly
+ *        decoded public key. Failure here ends validation with an error.
+ *
+ *     -- Extensions: extension values are processed in due order.
+ *
+ *        -- Basic Constraints: for all certificates except EE, must be
+ *        present, indicate a CA, and have a path legnth compatible with
+ *        the chain length so far.
+ *
+ *        -- Key Usage: for the EE, if present, must allow signatures
+ *        or encryption/key exchange, as required for the cipher suite.
+ *        For non-EE, if present, must have the "certificate sign" bit.
+ *
+ *        -- Subject Alt Name: for the EE, dNSName names are matched
+ *        against the server name. Ignored for non-EE.
+ *
+ *        -- Authority Key Identifier, Subject Key Identifier, Issuer
+ *        Alt Name, Subject Directory Attributes, CRL Distribution Points
+ *        Freshest CRL, Authority Info Access and Subject Info Access
+ *        extensions are always ignored: they either contain only
+ *        informative data, or they relate to revocation processing, which
+ *        we explicitly do not support.
+ *
+ *        -- All other extensions are ignored if non-critical. If a
+ *        critical extension other than the ones above is encountered,
+ *        then a failure is reported.
+ *
+ *     -- End of the TBS: the multihash engine is stopped.
+ *
+ *     -- Signature algorithm: the signature algorithm on the
+ *     certificate is decoded. A failure is reported if that algorithm
+ *     is unknown. The hashed TBS corresponding to the signature hash
+ *     function is computed and stored in tbs_hash[] (if not supported,
+ *     then a failure is reported). The hash OID and length are stored
+ *     in cert_sig_hash_oid and cert_sig_hash_len.
+ *
+ *     -- Signature value: the signature value is copied into the
+ *     cert_sig[] array.
+ *
+ *     -- Certificate end: the hashed issuer DN (saved_dn_hash[]) is
+ *     looked up in the trust store (CA trust anchors only); for all
+ *     that match, the signature (cert_sig[]) is verified against the
+ *     anchor public key (hashed TBS is in tbs_hash[]). If one of these
+ *     signatures is valid, then validation ends with a success.
+ *
+ *  -- If the chain end is reached without obtaining a validation success,
+ *  then validation is reported as failed.
+ */
+
+#ifndef BR_USE_UNIX_TIME
+#if defined __unix__ || defined __linux__ \
+       || defined _POSIX_SOURCE || defined _POSIX_C_SOURCE \
+       || (defined __APPLE__ && defined __MACH__)
+#define BR_USE_UNIX_TIME   1
+#endif
+#endif
+
+#ifndef BR_USE_WIN32_TIME
+#if defined _WIN32 || defined _WIN64
+#define BR_USE_WIN32_TIME   1
+#endif
+#endif
+
+#if BR_USE_UNIX_TIME
+#include <time.h>
+#endif
+
+#if BR_USE_WIN32_TIME
+#include <windows.h>
+#endif
+
+void br_x509_minimal_init_main(void *ctx);
+void br_x509_minimal_run(void *ctx);
+
+/* see bearssl_x509.h */
+void
+br_x509_minimal_init(br_x509_minimal_context *ctx,
+       const br_hash_class *dn_hash_impl,
+       const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num)
+{
+       memset(ctx, 0, sizeof *ctx);
+       ctx->vtable = &br_x509_minimal_vtable;
+       ctx->dn_hash_impl = dn_hash_impl;
+       ctx->trust_anchors = trust_anchors;
+       ctx->trust_anchors_num = trust_anchors_num;
+}
+
+static void
+xm_start_chain(const br_x509_class **ctx,
+       unsigned expected_key_type, const char *server_name)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       memset(&cc->pkey, 0, sizeof cc->pkey);
+       cc->num_certs = 0;
+       cc->err = 0;
+       cc->cpu.dp = cc->dp_stack;
+       cc->cpu.rp = cc->rp_stack;
+       br_x509_minimal_init_main(&cc->cpu);
+       cc->expected_key_type = expected_key_type;
+       if (server_name == NULL || *server_name == 0) {
+               cc->server_name = NULL;
+       } else {
+               cc->server_name = server_name;
+       }
+}
+
+static void
+xm_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err != 0) {
+               return;
+       }
+       if (length == 0) {
+               cc->err = BR_ERR_X509_TRUNCATED;
+               return;
+       }
+       cc->cert_length = length;
+}
+
+static void
+xm_append(const br_x509_class **ctx, const unsigned char *buf, size_t len)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err != 0) {
+               return;
+       }
+       cc->hbuf = buf;
+       cc->hlen = len;
+       br_x509_minimal_run(&cc->cpu);
+}
+
+static void
+xm_end_cert(const br_x509_class **ctx)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err == 0 && cc->cert_length != 0) {
+               cc->err = BR_ERR_X509_TRUNCATED;
+       }
+       cc->num_certs ++;
+}
+
+static unsigned
+xm_end_chain(const br_x509_class **ctx)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err == 0) {
+               if (cc->num_certs == 0) {
+                       cc->err = BR_ERR_X509_EMPTY_CHAIN;
+               } else {
+                       cc->err = BR_ERR_X509_NOT_TRUSTED;
+               }
+       } else if (cc->err == BR_ERR_X509_OK) {
+               return 0;
+       }
+       return (unsigned)cc->err;
+}
+
+static const br_x509_pkey *
+xm_get_pkey(const br_x509_class *const *ctx)
+{
+       br_x509_minimal_context *cc;
+
+       cc = (br_x509_minimal_context *)ctx;
+       if (cc->err == BR_ERR_X509_OK
+               || cc->err == BR_ERR_X509_NOT_TRUSTED)
+       {
+               return &((br_x509_minimal_context *)ctx)->pkey;
+       } else {
+               return NULL;
+       }
+}
+
+/* see bearssl_x509.h */
+const br_x509_class br_x509_minimal_vtable = {
+       sizeof(br_x509_minimal_context),
+       xm_start_chain,
+       xm_start_cert,
+       xm_append,
+       xm_end_cert,
+       xm_end_chain,
+       xm_get_pkey
+};
+
+#define CTX   ((br_x509_minimal_context *)((unsigned char *)t0ctx - offsetof(br_x509_minimal_context, cpu)))
+#define CONTEXT_NAME   br_x509_minimal_context
+
+#define DNHASH_LEN   ((CTX->dn_hash_impl->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK)
+
+/*
+ * Hash a DN (from a trust anchor) into the provided buffer. This uses the
+ * DN hash implementation and context structure from the X.509 engine
+ * context.
+ */
+static void
+hash_dn(br_x509_minimal_context *ctx, const void *dn, size_t len,
+       unsigned char *out)
+{
+       ctx->dn_hash_impl->init(&ctx->dn_hash.vtable);
+       ctx->dn_hash_impl->update(&ctx->dn_hash.vtable, dn, len);
+       ctx->dn_hash_impl->out(&ctx->dn_hash.vtable, out);
+}
+
+/*
+ * Compare two big integers for equality. The integers use unsigned big-endian
+ * encoding; extra leading bytes (of value 0) are allowed.
+ */
+static int
+eqbigint(const unsigned char *b1, size_t len1,
+       const unsigned char *b2, size_t len2)
+{
+       while (len1 > 0 && *b1 == 0) {
+               b1 ++;
+               len1 --;
+       }
+       while (len2 > 0 && *b2 == 0) {
+               b2 ++;
+               len2 --;
+       }
+       if (len1 != len2) {
+               return 0;
+       }
+       return memcmp(b1, b2, len1) == 0;
+}
+
+/*
+ * Verify the signature on the certificate with the provided public key.
+ * This function checks the public key type with regards to the expected
+ * type. Returned value is either 0 on success, or a non-zero error code.
+ */
+static int
+verify_signature(br_x509_minimal_context *ctx, const br_x509_pkey *pk)
+{
+       int kt;
+
+       kt = ctx->cert_signer_key_type;
+       if ((pk->key_type & 0x0F) != kt) {
+               return BR_ERR_X509_WRONG_KEY_TYPE;
+       }
+       switch (kt) {
+               unsigned char tmp[64];
+
+       case BR_KEYTYPE_RSA:
+               if (ctx->irsa == 0) {
+                       return BR_ERR_X509_UNSUPPORTED;
+               }
+               if (!ctx->irsa(ctx->cert_sig, ctx->cert_sig_len,
+                       &t0_datablock[ctx->cert_sig_hash_oid],
+                       ctx->cert_sig_hash_len, &pk->key.rsa, tmp))
+               {
+                       return BR_ERR_X509_BAD_SIGNATURE;
+               }
+               if (memcmp(ctx->tbs_hash, tmp, ctx->cert_sig_hash_len) != 0) {
+                       return BR_ERR_X509_BAD_SIGNATURE;
+               }
+               return 0;
+
+       case BR_KEYTYPE_EC:
+               if (ctx->iecdsa == 0) {
+                       return BR_ERR_X509_UNSUPPORTED;
+               }
+               if (!ctx->iecdsa(ctx->iec, ctx->tbs_hash,
+                       ctx->cert_sig_hash_len, &pk->key.ec,
+                       ctx->cert_sig, ctx->cert_sig_len))
+               {
+                       return BR_ERR_X509_BAD_SIGNATURE;
+               }
+               return 0;
+
+       default:
+               return BR_ERR_X509_UNSUPPORTED;
+       }
+}
+
+/*
+ * Compare two strings for equality, in a case-insensitive way. This
+ * function handles casing only for ASCII letters.
+ */
+static int
+eqnocase(const void *s1, const void *s2, size_t len)
+{
+       const unsigned char *buf1, *buf2;
+
+       buf1 = s1;
+       buf2 = s2;
+       while (len -- > 0) {
+               int x1, x2;
+
+               x1 = *buf1 ++;
+               x2 = *buf2 ++;
+               if (x1 >= 'A' && x1 <= 'Z') {
+                       x1 += 'a' - 'A';
+               }
+               if (x2 >= 'A' && x2 <= 'Z') {
+                       x2 += 'a' - 'A';
+               }
+               if (x1 != x2) {
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+}
+
+cc: read8-low ( -- x ) {
+       if (CTX->hlen == 0) {
+               T0_PUSHi(-1);
+       } else {
+               unsigned char x = *CTX->hbuf ++;
+               if (CTX->do_mhash) {
+                       br_multihash_update(&CTX->mhash, &x, 1);
+               }
+               if (CTX->do_dn_hash) {
+                       CTX->dn_hash_impl->update(&CTX->dn_hash.vtable, &x, 1);
+               }
+               CTX->hlen --;
+               T0_PUSH(x);
+       }
+}
+
+addr: cert_length
+addr: num_certs
+
+cc: read-blob-inner ( addr len -- addr len ) {
+       uint32_t len = T0_POP();
+       uint32_t addr = T0_POP();
+       size_t clen = CTX->hlen;
+       if (clen > len) {
+               clen = (size_t)len;
+       }
+       if (addr != 0) {
+               memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen);
+       }
+       if (CTX->do_mhash) {
+               br_multihash_update(&CTX->mhash, CTX->hbuf, clen);
+       }
+       if (CTX->do_dn_hash) {
+               CTX->dn_hash_impl->update(
+                       &CTX->dn_hash.vtable, CTX->hbuf, clen);
+       }
+       CTX->hbuf += clen;
+       CTX->hlen -= clen;
+       T0_PUSH(addr + clen);
+       T0_PUSH(len - clen);
+}
+
+\ Compute the TBS hash, using the provided hash ID. The hash value is
+\ written in the tbs_hash[] array, and the hash length is returned. If
+\ the requested hash function is not supported, then 0 is returned.
+cc: compute-tbs-hash ( id -- hashlen ) {
+       int id = T0_POPi();
+       size_t len;
+       len = br_multihash_out(&CTX->mhash, id, CTX->tbs_hash);
+       T0_PUSH(len);
+}
+
+\ Push true (-1) if no server name is expected in the EE certificate.
+cc: zero-server-name ( -- bool ) {
+       T0_PUSHi(-(CTX->server_name == NULL));
+}
+
+addr: expected_key_type
+addr: cert_sig
+addr: cert_sig_len
+addr: cert_signer_key_type
+addr: cert_sig_hash_oid
+addr: cert_sig_hash_len
+addr: tbs_hash
+addr: min_rsa_size
+
+\ Start TBS hash computation. The hash functions are reinitialised.
+cc: start-tbs-hash ( -- ) {
+       br_multihash_init(&CTX->mhash);
+       CTX->do_mhash = 1;
+}
+
+\ Stop TBS hash computation.
+cc: stop-tbs-hash ( -- ) {
+       CTX->do_mhash = 0;
+}
+
+\ Start DN hash computation.
+cc: start-dn-hash ( -- ) {
+       CTX->dn_hash_impl->init(&CTX->dn_hash.vtable);
+       CTX->do_dn_hash = 1;
+}
+
+\ Terminate DN hash computation and write the DN hash into the
+\ current_dn_hash buffer.
+cc: compute-dn-hash ( -- ) {
+       CTX->dn_hash_impl->out(&CTX->dn_hash.vtable, CTX->current_dn_hash);
+       CTX->do_dn_hash = 0;
+}
+
+\ Get the length of hash values obtained with the DN hasher.
+cc: dn-hash-length ( -- len ) {
+       T0_PUSH(DNHASH_LEN);
+}
+
+\ Copy data between two areas in the context.
+cc: blobcopy ( addr-dst addr-src len -- ) {
+       size_t len = T0_POP();
+       unsigned char *src = (unsigned char *)CTX + T0_POP();
+       unsigned char *dst = (unsigned char *)CTX + T0_POP();
+       memcpy(dst, src, len);
+}
+
+addr: current_dn_hash
+addr: next_dn_hash
+addr: saved_dn_hash
+
+\ Read a DN. The normalized DN hash is computed and stored in the
+\ current_dn_hash. The Common Name is also extracted to the pad, if
+\ it is present and small enough (255 bytes at most); the CN length is
+\ then written in pad[0]. If these conditions are not met, then pad[0]
+\ is set to 0.
+: read-DN ( lim -- lim )
+       \ Activate DN hashing.
+       start-dn-hash
+
+       \ Prepare pad.
+       0 addr-pad set8
+
+       \ Parse the DN structure: it is a SEQUENCE of SET of
+       \ AttributeTypeAndValue. Each AttributeTypeAndValue is a
+       \ SEQUENCE { OBJECT IDENTIFIER, ANY }.
+       read-sequence-open
+       begin
+               dup while
+
+               read-tag 0x11 check-tag-constructed read-length-open-elt
+               dup ifnot ERR_X509_BAD_DN fail then
+               begin
+                       dup while
+
+                       read-sequence-open
+                       \ We want to recognize the OID for Common Name,
+                       \ but we don't want to use read-OID because we
+                       \ need to preserve the pad contents. Instead, we
+                       \ use the fact that the encoding for the value of
+                       \ id-at-commonName is 55 04 03 (three bytes).
+                       read-tag 0x06 check-tag-primitive read-length-open-elt
+                       dup 3 = if
+                               read8 16 << { tmp }
+                               read8 8 << tmp + >tmp
+                               read8 tmp +
+                               0x550403 = { isCN }
+                       then
+                       skip-close-elt
+                       
+                       \ If this is a Common Name, then we want to copy
+                       \ it to the pad, but only if it uses a mono-byte
+                       \ encoding (Printable, Teletex or UTF-8).
+                       isCN if
+                               read-tag
+                               dup dup 0x0C = swap 0x13 = or swap 0x14 = or if
+                                       check-primitive
+                                       read-small-value drop
+                                       close-elt
+                               else
+                                       drop
+                                       0 addr-pad set8
+                                       skip-close-elt
+                               then
+                       else
+                               skip-close-elt
+                       then
+               repeat
+               close-elt
+       repeat
+       close-elt
+
+       \ Compute DN hash and deactivate DN hashing.
+       compute-dn-hash ;
+
+\ Get the validation date and time from the context or system.
+cc: get-system-date ( -- days seconds ) {
+       if (CTX->days == 0 && CTX->seconds == 0) {
+#if BR_USE_UNIX_TIME
+               time_t x = time(NULL);
+
+               T0_PUSH((uint32_t)(x / 86400) + 719528);
+               T0_PUSH((uint32_t)(x % 86400));
+#elif BR_USE_WIN32_TIME
+               FILETIME ft;
+               uint64_t x;
+
+               GetSystemTimeAsFileTime(&ft);
+               x = ((uint64_t)ft.dwHighDateTime << 32)
+                       + (uint64_t)ft.dwLowDateTime;
+               x = (x / 10000000);
+               T0_PUSH((uint32_t)(x / 86400) + 584754);
+               T0_PUSH((uint32_t)(x % 86400));
+#else
+               CTX->err = BR_ERR_X509_TIME_UNKNOWN;
+               T0_CO();
+#endif
+       } else {
+               T0_PUSH(CTX->days);
+               T0_PUSH(CTX->seconds);
+       }
+}
+
+\ Compare two dates (days+seconds) together.
+: before ( days1 seconds1 days2 seconds2 -- bool )
+       { d1 s1 d2 s2 }
+       d1 d2 = if s1 s2 < else d1 d2 < then ;
+
+: after ( days1 seconds1 days2 seconds2 -- bool )
+       swap2 before ;
+
+\ Swap the top two elements with the two elements immediately below.
+: swap2 ( a b c d -- c d a b )
+       3 roll 3 roll ;
+
+\ Match the name in the pad with the expected server name. Returned value
+\ is true (-1) on match, false (0) otherwise. If there is no expected
+\ server name, then 0 is returned.
+\ Match conditions: either an exact match (case insensitive), or a
+\ wildcard match, if the found name starts with "*.". We only match a
+\ starting wildcard, and only against a complete DN name component.
+cc: match-server-name ( -- bool ) {
+       size_t n1, n2;
+
+       if (CTX->server_name == NULL) {
+               T0_PUSH(0);
+               T0_RET();
+       }
+       n1 = strlen(CTX->server_name);
+       n2 = CTX->pad[0];
+       if (n1 == n2 && eqnocase(&CTX->pad[1], CTX->server_name, n1)) {
+               T0_PUSHi(-1);
+               T0_RET();
+       }
+       if (n2 >= 2 && CTX->pad[1] == '*' && CTX->pad[2] == '.') {
+               size_t u;
+
+               u = 0;
+               while (u < n1 && CTX->server_name[u] != '.') {
+                       u ++;
+               }
+               u ++;
+               n1 -= u;
+               if ((n2 - 2) == n1
+                       && eqnocase(&CTX->pad[3], CTX->server_name + u, n1))
+               {
+                       T0_PUSHi(-1);
+                       T0_RET();
+               }
+       }
+       T0_PUSH(0);
+}
+
+\ Get the address and length for the pkey_data buffer.
+: addr-len-pkey_data ( -- addr len )
+       CX 0 8191 { offsetof(br_x509_minimal_context, pkey_data) }
+       CX 0 8191 { BR_X509_BUFSIZE_KEY } ;
+
+\ Copy the EE public key to the permanent buffer (RSA).
+cc: copy-ee-rsa-pkey ( nlen elen -- ) {
+       size_t elen = T0_POP();
+       size_t nlen = T0_POP();
+       memcpy(CTX->ee_pkey_data, CTX->pkey_data, nlen + elen);
+       CTX->pkey.key_type = BR_KEYTYPE_RSA;
+       CTX->pkey.key.rsa.n = CTX->ee_pkey_data;
+       CTX->pkey.key.rsa.nlen = nlen;
+       CTX->pkey.key.rsa.e = CTX->ee_pkey_data + nlen;
+       CTX->pkey.key.rsa.elen = elen;
+}
+
+\ Copy the EE public key to the permanent buffer (EC).
+cc: copy-ee-ec-pkey ( curve qlen -- ) {
+       size_t qlen = T0_POP();
+       uint32_t curve = T0_POP();
+       memcpy(CTX->ee_pkey_data, CTX->pkey_data, qlen);
+       CTX->pkey.key_type = BR_KEYTYPE_EC;
+       CTX->pkey.key.ec.curve = curve;
+       CTX->pkey.key.ec.q = CTX->ee_pkey_data;
+       CTX->pkey.key.ec.qlen = qlen;
+}
+
+\ Check whether the current certificate (EE) is directly trusted.
+cc: check-direct-trust ( -- ) {
+       size_t u;
+
+       for (u = 0; u < CTX->trust_anchors_num; u ++) {
+               const br_x509_trust_anchor *ta;
+               unsigned char hashed_DN[64];
+               int kt;
+
+               ta = &CTX->trust_anchors[u];
+               if (ta->flags & BR_X509_TA_CA) {
+                       continue;
+               }
+               hash_dn(CTX, ta->dn, ta->dn_len, hashed_DN);
+               if (memcmp(hashed_DN, CTX->current_dn_hash, DNHASH_LEN)) {
+                       continue;
+               }
+               kt = CTX->pkey.key_type;
+               if ((ta->pkey.key_type & 0x0F) != kt) {
+                       continue;
+               }
+               switch (kt) {
+
+               case BR_KEYTYPE_RSA:
+                       if (!eqbigint(CTX->pkey.key.rsa.n,
+                               CTX->pkey.key.rsa.nlen,
+                               ta->pkey.key.rsa.n,
+                               ta->pkey.key.rsa.nlen)
+                               || !eqbigint(CTX->pkey.key.rsa.e,
+                               CTX->pkey.key.rsa.elen,
+                               ta->pkey.key.rsa.e,
+                               ta->pkey.key.rsa.elen))
+                       {
+                               continue;
+                       }
+                       break;
+
+               case BR_KEYTYPE_EC:
+                       if (CTX->pkey.key.ec.curve != ta->pkey.key.ec.curve
+                               || CTX->pkey.key.ec.qlen != ta->pkey.key.ec.qlen
+                               || memcmp(CTX->pkey.key.ec.q,
+                                       ta->pkey.key.ec.q,
+                                       ta->pkey.key.ec.qlen) != 0)
+                       {
+                               continue;
+                       }
+                       break;
+
+               default:
+                       continue;
+               }
+
+               /*
+                * Direct trust match!
+                */
+               CTX->err = BR_ERR_X509_OK;
+               T0_CO();
+       }
+}
+
+\ Check the signature on the certificate with regards to all trusted CA.
+\ We use the issuer hash (in saved_dn_hash[]) as CA identifier.
+cc: check-trust-anchor-CA ( -- ) {
+       size_t u;
+
+       for (u = 0; u < CTX->trust_anchors_num; u ++) {
+               const br_x509_trust_anchor *ta;
+               unsigned char hashed_DN[64];
+
+               ta = &CTX->trust_anchors[u];
+               if (!(ta->flags & BR_X509_TA_CA)) {
+                       continue;
+               }
+               hash_dn(CTX, ta->dn, ta->dn_len, hashed_DN);
+               if (memcmp(hashed_DN, CTX->saved_dn_hash, DNHASH_LEN)) {
+                       continue;
+               }
+               if (verify_signature(CTX, &ta->pkey) == 0) {
+                       CTX->err = BR_ERR_X509_OK;
+                       T0_CO();
+               }
+       }
+}
+
+\ Verify RSA signature. This uses the public key that was just decoded
+\ into CTX->pkey_data; the modulus and exponent length are provided as
+\ parameters. The resulting hash value is compared with the one in
+\ tbs_hash. Returned value is 0 on success, or a non-zero error code.
+cc: do-rsa-vrfy ( nlen elen -- err ) {
+       size_t elen = T0_POP();
+       size_t nlen = T0_POP();
+       br_x509_pkey pk;
+
+       pk.key_type = BR_KEYTYPE_RSA;
+       pk.key.rsa.n = CTX->pkey_data;
+       pk.key.rsa.nlen = nlen;
+       pk.key.rsa.e = CTX->pkey_data + nlen;
+       pk.key.rsa.elen = elen;
+       T0_PUSH(verify_signature(CTX, &pk));
+}
+
+\ Verify ECDSA signature. This uses the public key that was just decoded
+\ into CTX->pkey_dayta; the curve ID and public point length are provided
+\ as parameters. The hash value in tbs_hash is used. Returned value is 0
+\ on success, or non-zero error code.
+cc: do-ecdsa-vrfy ( curve qlen -- err ) {
+       size_t qlen = T0_POP();
+       int curve = T0_POP();
+       br_x509_pkey pk;
+
+       pk.key_type = BR_KEYTYPE_EC;
+       pk.key.ec.curve = curve;
+       pk.key.ec.q = CTX->pkey_data;
+       pk.key.ec.qlen = qlen;
+       T0_PUSH(verify_signature(CTX, &pk));
+}
+
+cc: print-bytes ( addr len -- ) {
+       extern int printf(const char *fmt, ...);
+       size_t len = T0_POP();
+       unsigned char *buf = (unsigned char *)CTX + T0_POP();
+       size_t u;
+
+       for (u = 0; u < len; u ++) {
+               printf("%02X", buf[u]);
+       }
+}
+
+cc: printOID ( -- ) {
+       extern int printf(const char *fmt, ...);
+       size_t u, len;
+
+       len = CTX->pad[0];
+       if (len == 0) {
+               printf("*");
+               T0_RET();
+       }
+       printf("%u.%u", CTX->pad[1] / 40, CTX->pad[1] % 40);
+       u = 2;
+       while (u <= len) {
+               unsigned long ul;
+
+               ul = 0;
+               for (;;) {
+                       int x;
+
+                       if (u > len) {
+                               printf("BAD");
+                               T0_RET();
+                       }
+                       x = CTX->pad[u ++];
+                       ul = (ul << 7) + (x & 0x7F);
+                       if (!(x & 0x80)) {
+                               break;
+                       }
+               }
+               printf(".%lu", ul);
+       }
+}
+
+\ Extensions with specific processing.
+OID: basicConstraints    2.5.29.19
+OID: keyUsage            2.5.29.15
+OID: subjectAltName      2.5.29.17
+
+\ Extensions which are ignored when encountered, even if critical.
+OID: authorityKeyIdentifier        2.5.29.35
+OID: subjectKeyIdentifier          2.5.29.14
+OID: issuerAltName                 2.5.29.18
+OID: subjectDirectoryAttributes    2.5.29.9
+OID: crlDistributionPoints         2.5.29.31
+OID: freshestCRL                   2.5.29.46
+OID: authorityInfoAccess           1.3.6.1.5.5.7.1.1
+OID: subjectInfoAccess             1.3.6.1.5.5.7.1.11
+
+\ Process a Basic Constraints extension. This should be called only if
+\ the certificate is not the EE. We check that the extension contains
+\ the "CA" flag, and that the path length, if specified, is compatible
+\ with the current chain length.
+: process-basicConstraints ( lim -- lim )
+       read-sequence-open
+       read-tag-or-end
+       dup 0x01 = if
+               read-boolean ifnot ERR_X509_NOT_CA fail then
+               read-tag-or-end
+       else
+               ERR_X509_NOT_CA fail
+       then
+       dup 0x02 = if
+               drop check-primitive read-small-int-value
+               addr-num_certs get32 1- < if ERR_X509_NOT_CA fail then
+               read-tag-or-end
+       then
+       -1 <> if ERR_X509_UNEXPECTED fail then
+       drop
+       close-elt
+       ;
+
+\ Process a Key Usage extension.
+\ For the EE certificate:
+\   -- if the expected key usage is "key exchange", then the extension
+\      must contain either keyEncipherment (2) or dataEncipherment (3);
+\   -- if the expected key usage is "signature", then the extension
+\      must contain either digitalSignature (0) or nonRepudiation (1).
+\ For CA certificates, the extension must contain keyCertSign (5).
+: process-keyUsage ( lim ee -- lim )
+       \ Compute flags, depending on EE status and expected key usage.
+       \ This is a mask of bits in the first byte.
+       if
+               addr-expected_key_type get8 0x10 and if 0x30 else 0xC0 then
+       else
+               0x04
+       then
+       { mask }
+       \ Read tag for the BIT STRING and open it.
+       read-tag 0x03 check-tag-primitive
+       read-length-open-elt
+       \ First byte indicates number of ignored bits in the last byte. It
+       \ must be between 0 and 7.
+       read8 { ign }
+       ign 7 > if ERR_X509_UNEXPECTED fail then
+       \ Depending on length, we have either 0, 1 or more bytes to read.
+       dup case
+               0 of ERR_X509_FORBIDDEN_KEY_USAGE fail endof
+               1 of read8 ign >> ign << endof
+               drop read8 0
+       endcase
+       mask and ifnot ERR_X509_FORBIDDEN_KEY_USAGE fail then
+       skip-close-elt ;
+
+\ Process a Subject Alt Name extension. Returned value is a boolean set
+\ to true if the expected server name was matched against a dNSName in
+\ the extension.
+: process-SAN ( lim -- lim bool )
+       0 { m }
+       read-sequence-open
+       begin dup while
+               \ We check only names of type dNSName; they use IA5String,
+               \ which is basically ASCII.
+               read-tag 0x22 = if
+                       check-primitive
+                       read-small-value drop
+                       match-server-name m or >m
+               else
+                       drop read-length-skip
+               then
+       repeat
+       close-elt
+       m ;
+
+\ Decode a certificate. The "ee" boolean must be true for the EE.
+: decode-certificate ( ee -- )
+       { ee }
+
+       \ Obtain the total certificate length.
+       addr-cert_length get32
+
+       \ Open the outer SEQUENCE.
+       read-sequence-open
+
+       \ TBS
+       \ Activate hashing.
+       start-tbs-hash
+       read-sequence-open
+
+       \ First element may be an explicit version. We accept only
+       \ versions 0 to 2 (certificates v1 to v3).
+       read-tag dup 0x20 = if
+               drop check-constructed read-length-open-elt
+               read-tag
+               0x02 check-tag-primitive
+               read-small-int-value
+               2 > if ERR_X509_UNSUPPORTED fail then
+               close-elt
+               read-tag
+       then
+
+       \ Serial number. We just check that the tag is correct.
+       0x02 check-tag-primitive
+       read-length-skip
+
+       \ Signature algorithm. This structure is redundant with the one
+       \ on the outside; we just skip it.
+       read-sequence-open skip-close-elt
+
+       \ Issuer name: hashed, then copied into next_dn_hash[].
+       read-DN
+       addr-next_dn_hash addr-current_dn_hash dn-hash-length blobcopy
+
+       \ Validity dates.
+       read-sequence-open
+       read-date get-system-date after if ERR_X509_EXPIRED fail then
+       read-date get-system-date before if ERR_X509_EXPIRED fail then
+       close-elt
+
+       \ Subject name.
+       read-DN
+       ee if
+               \ For the EE, we must check whether the Common Name, if
+               \ any, matches the expected server name.
+               match-server-name { eename }
+       else
+               \ For a non-EE certificate, the hashed subject DN must match
+               \ the saved hashed issuer DN from the previous certificate.
+               addr-current_dn_hash addr-saved_dn_hash dn-hash-length eqblob
+               ifnot ERR_X509_DN_MISMATCH fail then
+       then
+       \ Move the hashed issuer DN for this certificate into the
+       \ saved_dn_hash[] array.
+       addr-saved_dn_hash addr-next_dn_hash dn-hash-length blobcopy
+
+       \ Public Key.
+       read-sequence-open
+       \ Algorithm Identifier. Right now we are only interested in the
+       \ OID, since we only support RSA keys.
+       read-sequence-open
+       read-OID ifnot ERR_X509_UNSUPPORTED fail then
+       { ; pkey-type }
+       choice
+               \ RSA public key.
+               rsaEncryption eqOID uf
+                       skip-close-elt
+                       \ Public key itself: the BIT STRING contains bytes
+                       \ (no partial byte) and these bytes encode the
+                       \ actual value.
+                       read-bits-open
+                               \ RSA public key is a SEQUENCE of two
+                               \ INTEGER. We get both INTEGER values into
+                               \ the pkey_data[] buffer, if they fit.
+                               read-sequence-open
+                               addr-len-pkey_data
+                               read-integer { nlen }
+                               addr-len-pkey_data swap nlen + swap nlen -
+                               read-integer { elen }
+                               close-elt
+
+                               \ Check that the public key fits our minimal
+                               \ size requirements. Note that the integer
+                               \ decoder already skipped the leading bytes
+                               \ of value 0, so we are working on the true
+                               \ modulus length here.
+                               addr-min_rsa_size get16 128 + nlen > if
+                                       ERR_X509_WEAK_PUBLIC_KEY fail
+                               then
+                       close-elt
+                       KEYTYPE_RSA >pkey-type
+               enduf
+
+               \ EC public key.
+               id-ecPublicKey eqOID uf
+                       \ We support only named curves, for which the
+                       \ "parameters" field in the AlgorithmIdentifier
+                       \ field should be an OID.
+                       read-OID ifnot ERR_X509_UNSUPPORTED fail then
+                       choice
+                               ansix9p256r1 eqOID uf 23 enduf
+                               ansix9p384r1 eqOID uf 24 enduf
+                               ansix9p521r1 eqOID uf 25 enduf
+                               ERR_X509_UNSUPPORTED fail
+                       endchoice
+                       { curve }
+                       close-elt
+                       read-bits-open
+                       dup { qlen }
+                       dup addr-len-pkey_data rot < if
+                               ERR_X509_LIMIT_EXCEEDED fail
+                       then
+                       read-blob
+                       KEYTYPE_EC >pkey-type
+               enduf
+
+               \ Not a recognised public key type.
+               ERR_X509_UNSUPPORTED fail
+       endchoice
+       close-elt
+
+       \ Process public key.
+       ee if
+               \ For the EE certificate, check that the key type
+               \ matches that which was expected, then copy the
+               \ data to the relevant buffer.
+               addr-expected_key_type get8 0x0F and
+               dup if
+                       pkey-type = ifnot ERR_X509_WRONG_KEY_TYPE fail then
+               else
+                       drop
+               then
+               pkey-type case
+                       KEYTYPE_RSA of nlen elen copy-ee-rsa-pkey endof
+                       KEYTYPE_EC of curve qlen copy-ee-ec-pkey endof
+                       ERR_X509_UNSUPPORTED fail
+               endcase
+       else
+               \ Verify signature on previous certificate. We invoke
+               \ the RSA implementation.
+               pkey-type case
+                       KEYTYPE_RSA of nlen elen do-rsa-vrfy endof
+                       KEYTYPE_EC of curve qlen do-ecdsa-vrfy endof
+                       ERR_X509_UNSUPPORTED fail
+               endcase
+               dup if fail then
+               drop
+       then
+
+       \ This flag will be set to true if the Basic Constraints extension
+       \ is encountered.
+       0 { seenBC }
+
+       \ Skip issuerUniqueID and subjectUniqueID, and process extensions
+       \ if present. Extensions are an explicit context tag of value 3
+       \ around a SEQUENCE OF extensions. Each extension is a SEQUENCE
+       \ with an OID, an optional boolean, and a value; the value is
+       \ an OCTET STRING.
+       read-tag-or-end
+       0x21 iftag-skip
+       0x22 iftag-skip
+       dup 0x23 = if
+               drop
+               check-constructed read-length-open-elt
+               read-sequence-open
+               begin dup while
+                       0 { critical }
+                       read-sequence-open
+                       read-OID drop
+                       read-tag dup 0x01 = if
+                               read-boolean >critical
+                               read-tag
+                       then
+                       0x04 check-tag-primitive read-length-open-elt
+                       choice
+                               \ Extensions with specific processing.
+                               basicConstraints eqOID uf
+                                       ee if
+                                               skip-remaining
+                                       else
+                                               process-basicConstraints
+                                               -1 >seenBC
+                                       then
+                               enduf
+                               keyUsage         eqOID uf
+                                       ee process-keyUsage
+                               enduf
+                               subjectAltName   eqOID uf
+                                       ee if
+                                               0 >eename
+                                               process-SAN >eename
+                                       else
+                                               skip-remaining
+                                       then
+                               enduf
+
+                               \ Extensions which are always ignored,
+                               \ even if critical.
+                               authorityKeyIdentifier     eqOID uf
+                                       skip-remaining
+                               enduf
+                               subjectKeyIdentifier       eqOID uf
+                                       skip-remaining
+                               enduf
+                               issuerAltName              eqOID uf
+                                       skip-remaining
+                               enduf
+                               subjectDirectoryAttributes eqOID uf
+                                       skip-remaining
+                               enduf
+                               crlDistributionPoints      eqOID uf
+                                       skip-remaining
+                               enduf
+                               freshestCRL                eqOID uf
+                                       skip-remaining
+                               enduf
+                               authorityInfoAccess        eqOID uf
+                                       skip-remaining
+                               enduf
+                               subjectInfoAccess          eqOID uf
+                                       skip-remaining
+                               enduf
+
+                               \ Unrecognized extensions trigger a failure
+                               \ if critical; otherwise, they are just
+                               \ ignored.
+                               critical if
+                                       ERR_X509_CRITICAL_EXTENSION fail
+                               then
+                               skip-remaining
+                       endchoice
+                       close-elt
+                       close-elt
+               repeat
+               close-elt
+               close-elt
+       else
+               -1 = ifnot ERR_X509_UNEXPECTED fail then
+               drop
+       then
+
+       close-elt
+       \ Terminate hashing.
+       stop-tbs-hash
+
+       \ For the EE certificate, verify that the intended server name
+       \ was matched.
+       ee if
+               eename zero-server-name or ifnot
+                       ERR_X509_BAD_SERVER_NAME fail
+               then
+       then
+
+       \ If this is the EE certificate, then direct trust may apply.
+       \ Note: we do this at this point, not immediately after decoding
+       \ the public key, because even in case of direct trust we still
+       \ want to check the server name with regards to the SAN extension.
+       \ However, we want to check direct trust before trying to decode
+       \ the signature algorithm, because it should work even if that
+       \ algorithm is not supported.
+       ee if check-direct-trust then
+
+       \ Non-EE certificates MUST have a Basic Constraints extension
+       \ (that marks them as being CA).
+       ee seenBC or ifnot ERR_X509_NOT_CA fail then
+
+       \ signature algorithm
+       read-tag check-sequence read-length-open-elt
+       \ Read and understand the OID. Right now, we support only
+       \ RSA with PKCS#1 v1.5 padding, and hash functions SHA-1,
+       \ SHA-224, SHA-256, SHA-384 and SHA-512. We purposely do NOT
+       \ support MD5 here.
+       \ TODO: add support for RSA/PSS
+       read-OID if
+               \ Based on the signature OID, we get:
+               \ -- the signing key type
+               \ -- the hash function numeric identifier
+               \ -- the hash function OID
+               choice
+                       sha1WithRSAEncryption   eqOID
+                               uf 2 KEYTYPE_RSA id-sha1   enduf
+                       sha224WithRSAEncryption eqOID
+                               uf 3 KEYTYPE_RSA id-sha224 enduf
+                       sha256WithRSAEncryption eqOID
+                               uf 4 KEYTYPE_RSA id-sha256 enduf
+                       sha384WithRSAEncryption eqOID
+                               uf 5 KEYTYPE_RSA id-sha384 enduf
+                       sha512WithRSAEncryption eqOID
+                               uf 6 KEYTYPE_RSA id-sha512 enduf
+
+                       ecdsa-with-SHA1   eqOID
+                               uf 2 KEYTYPE_EC id-sha1 enduf
+                       ecdsa-with-SHA224 eqOID
+                               uf 3 KEYTYPE_EC id-sha224 enduf
+                       ecdsa-with-SHA256 eqOID
+                               uf 4 KEYTYPE_EC id-sha256 enduf
+                       ecdsa-with-SHA384 eqOID
+                               uf 5 KEYTYPE_EC id-sha384 enduf
+                       ecdsa-with-SHA512 eqOID
+                               uf 6 KEYTYPE_EC id-sha512 enduf
+                       ERR_X509_UNSUPPORTED fail
+               endchoice
+               addr-cert_sig_hash_oid set16
+               addr-cert_signer_key_type set8
+
+               \ Compute the TBS hash into tbs_hash.
+               compute-tbs-hash
+               dup ifnot ERR_X509_UNSUPPORTED fail then
+               addr-cert_sig_hash_len set8
+       else
+               ERR_X509_UNSUPPORTED fail
+       then
+       \ We ignore the parameters, whether they are present or not,
+       \ because we got all the information from the OID.
+       skip-close-elt
+
+       \ signature value
+       read-bits-open
+       dup CX 0 8191 { BR_X509_BUFSIZE_SIG } > if
+               ERR_X509_LIMIT_EXCEEDED fail
+       then
+       dup addr-cert_sig_len set16
+       addr-cert_sig read-blob
+
+       \ Close the outer SEQUENCE.
+       close-elt
+
+       \ Close the advertised total certificate length. This checks that
+       \ there is no trailing garbage after the certificate.
+       close-elt
+
+       \ Flag the certificate as fully processed.
+       0 addr-cert_length set32
+
+       \ Check whether the issuer for the current certificate is known
+       \ as a trusted CA; in which case, verify the signature.
+       check-trust-anchor-CA ;
+
+: main
+       -1 decode-certificate
+       co
+       begin
+               0 decode-certificate co
+       again
+       ;
diff --git a/test/test_crypto.c b/test/test_crypto.c
new file mode 100644 (file)
index 0000000..ce0f64f
--- /dev/null
@@ -0,0 +1,5155 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bearssl.h"
+#include "inner.h"
+
+/*
+ * Decode an hexadecimal string. Returned value is the number of decoded
+ * bytes.
+ */
+static size_t
+hextobin(unsigned char *dst, const char *src)
+{
+       size_t num;
+       unsigned acc;
+       int z;
+
+       num = 0;
+       z = 0;
+       acc = 0;
+       while (*src != 0) {
+               int c = *src ++;
+               if (c >= '0' && c <= '9') {
+                       c -= '0';
+               } else if (c >= 'A' && c <= 'F') {
+                       c -= ('A' - 10); \
+               } else if (c >= 'a' && c <= 'f') {
+                       c -= ('a' - 10); \
+               } else {
+                       continue;
+               }
+               if (z) {
+                       *dst ++ = (acc << 4) + c;
+                       num ++;
+               } else {
+                       acc = c;
+               }
+               z = !z;
+       }
+       return num;
+}
+
+static void
+check_equals(char *banner, const void *v1, const void *v2, size_t len)
+{
+       size_t u;
+       const unsigned char *b;
+
+       if (memcmp(v1, v2, len) == 0) {
+               return;
+       }
+       fprintf(stderr, "\n%s failed\n", banner);
+       fprintf(stderr, "v1: ");
+       for (u = 0, b = v1; u < len; u ++) {
+               fprintf(stderr, "%02X", b[u]);
+       }
+       fprintf(stderr, "\nv2: ");
+       for (u = 0, b = v2; u < len; u ++) {
+               fprintf(stderr, "%02X", b[u]);
+       }
+       fprintf(stderr, "\n");
+       exit(EXIT_FAILURE);
+}
+
+#define HASH_SIZE(cname)   br_ ## cname ## _SIZE
+
+#define TEST_HASH(Name, cname) \
+static void \
+test_ ## cname ## _internal(char *data, char *refres) \
+{ \
+       br_ ## cname ## _context mc; \
+       unsigned char res[HASH_SIZE(cname)], ref[HASH_SIZE(cname)]; \
+       size_t u, n; \
+ \
+       hextobin(ref, refres); \
+       n = strlen(data); \
+       br_ ## cname ## _init(&mc); \
+       br_ ## cname ## _update(&mc, data, n); \
+       br_ ## cname ## _out(&mc, res); \
+       check_equals("KAT " #Name " 1", res, ref, HASH_SIZE(cname)); \
+       br_ ## cname ## _init(&mc); \
+       for (u = 0; u < n; u ++) { \
+               br_ ## cname ## _update(&mc, data + u, 1); \
+       } \
+       br_ ## cname ## _out(&mc, res); \
+       check_equals("KAT " #Name " 2", res, ref, HASH_SIZE(cname)); \
+       for (u = 0; u < n; u ++) { \
+               br_ ## cname ## _context mc2; \
+               br_ ## cname ## _init(&mc); \
+               br_ ## cname ## _update(&mc, data, u); \
+               mc2 = mc; \
+               br_ ## cname ## _update(&mc, data + u, n - u); \
+               br_ ## cname ## _out(&mc, res); \
+               check_equals("KAT " #Name " 3", res, ref, HASH_SIZE(cname)); \
+               br_ ## cname ## _update(&mc2, data + u, n - u); \
+               br_ ## cname ## _out(&mc2, res); \
+               check_equals("KAT " #Name " 4", res, ref, HASH_SIZE(cname)); \
+       } \
+       memset(&mc, 0, sizeof mc); \
+       memset(res, 0, sizeof res); \
+       br_ ## cname ## _vtable.init(&mc.vtable); \
+       mc.vtable->update(&mc.vtable, data, n); \
+       mc.vtable->out(&mc.vtable, res); \
+       check_equals("KAT " #Name " 5", res, ref, HASH_SIZE(cname)); \
+       memset(res, 0, sizeof res); \
+       mc.vtable->init(&mc.vtable); \
+       mc.vtable->update(&mc.vtable, data, n); \
+       mc.vtable->out(&mc.vtable, res); \
+       check_equals("KAT " #Name " 6", res, ref, HASH_SIZE(cname)); \
+}
+
+#define KAT_MILLION_A(Name, cname, refres)   do { \
+               br_ ## cname ## _context mc; \
+               unsigned char buf[1000]; \
+               unsigned char res[HASH_SIZE(cname)], ref[HASH_SIZE(cname)]; \
+               int i; \
+ \
+               hextobin(ref, refres); \
+               memset(buf, 'a', sizeof buf); \
+               br_ ## cname ## _init(&mc); \
+               for (i = 0; i < 1000; i ++) { \
+                       br_ ## cname ## _update(&mc, buf, sizeof buf); \
+               } \
+               br_ ## cname ## _out(&mc, res); \
+               check_equals("KAT " #Name " 5", res, ref, HASH_SIZE(cname)); \
+       } while (0)
+
+TEST_HASH(MD5, md5)
+TEST_HASH(SHA-1, sha1)
+TEST_HASH(SHA-224, sha224)
+TEST_HASH(SHA-256, sha256)
+TEST_HASH(SHA-384, sha384)
+TEST_HASH(SHA-512, sha512)
+
+static void
+test_MD5(void)
+{
+       printf("Test MD5: ");
+       fflush(stdout);
+       test_md5_internal("", "d41d8cd98f00b204e9800998ecf8427e");
+       test_md5_internal("a", "0cc175b9c0f1b6a831c399e269772661");
+       test_md5_internal("abc", "900150983cd24fb0d6963f7d28e17f72");
+       test_md5_internal("message digest", "f96b697d7cb7938d525a2f31aaf161d0");
+       test_md5_internal("abcdefghijklmnopqrstuvwxyz",
+               "c3fcd3d76192e4007dfb496cca67e13b");
+       test_md5_internal("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu"
+               "vwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f");
+       test_md5_internal("1234567890123456789012345678901234567890123456789"
+               "0123456789012345678901234567890",
+               "57edf4a22be3c955ac49da2e2107b67a");
+       KAT_MILLION_A(MD5, md5,
+               "7707d6ae4e027c70eea2a935c2296f21");
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_SHA1(void)
+{
+       printf("Test SHA-1: ");
+       fflush(stdout);
+       test_sha1_internal("abc", "a9993e364706816aba3e25717850c26c9cd0d89d");
+       test_sha1_internal("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlm"
+               "nomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+
+       KAT_MILLION_A(SHA-1, sha1,
+               "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_SHA224(void)
+{
+       printf("Test SHA-224: ");
+       fflush(stdout);
+       test_sha224_internal("abc",
+   "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7");
+       test_sha224_internal("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlm"
+               "nomnopnopq",
+   "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525");
+
+       KAT_MILLION_A(SHA-224, sha224,
+               "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67");
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_SHA256(void)
+{
+       printf("Test SHA-256: ");
+       fflush(stdout);
+       test_sha256_internal("abc",
+   "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+       test_sha256_internal("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlm"
+               "nomnopnopq",
+   "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
+
+       KAT_MILLION_A(SHA-256, sha256,
+   "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_SHA384(void)
+{
+       printf("Test SHA-384: ");
+       fflush(stdout);
+       test_sha384_internal("abc",
+               "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163"
+               "1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7");
+       test_sha384_internal(
+               "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+               "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+               "09330c33f71147e83d192fc782cd1b4753111b173b3b05d2"
+               "2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039");
+
+       KAT_MILLION_A(SHA-384, sha384,
+               "9d0e1809716474cb086e834e310a4a1ced149e9c00f24852"
+               "7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985");
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_SHA512(void)
+{
+       printf("Test SHA-512: ");
+       fflush(stdout);
+       test_sha512_internal("abc",
+   "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"
+   "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
+       test_sha512_internal(
+               "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+               "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+   "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018"
+   "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909");
+
+       KAT_MILLION_A(SHA-512, sha512,
+   "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"
+   "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b");
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_MD5_SHA1(void)
+{
+       unsigned char buf[500], out[36], outM[16], outS[20];
+       unsigned char seed[1];
+       br_hmac_drbg_context rc;
+       br_md5_context mc;
+       br_sha1_context sc;
+       br_md5sha1_context cc;
+       size_t u;
+
+       printf("Test MD5+SHA-1: ");
+       fflush(stdout);
+
+       seed[0] = 0;
+       br_hmac_drbg_init(&rc, &br_sha256_vtable, seed, sizeof seed);
+       for (u = 0; u < sizeof buf; u ++) {
+               size_t v;
+
+               br_hmac_drbg_generate(&rc, buf, u);
+               br_md5_init(&mc);
+               br_md5_update(&mc, buf, u);
+               br_md5_out(&mc, outM);
+               br_sha1_init(&sc);
+               br_sha1_update(&sc, buf, u);
+               br_sha1_out(&sc, outS);
+               br_md5sha1_init(&cc);
+               br_md5sha1_update(&cc, buf, u);
+               br_md5sha1_out(&cc, out);
+               check_equals("MD5+SHA-1 [1]", out, outM, 16);
+               check_equals("MD5+SHA-1 [2]", out + 16, outS, 20);
+               br_md5sha1_init(&cc);
+               for (v = 0; v < u; v ++) {
+                       br_md5sha1_update(&cc, buf + v, 1);
+               }
+               br_md5sha1_out(&cc, out);
+               check_equals("MD5+SHA-1 [3]", out, outM, 16);
+               check_equals("MD5+SHA-1 [4]", out + 16, outS, 20);
+       }
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+/*
+ * Compute a hash function, on some data, by ID. Returned value is
+ * hash output length.
+ */
+static size_t
+do_hash(int id, const void *data, size_t len, void *out)
+{
+       br_md5_context cmd5;
+       br_sha1_context csha1;
+       br_sha224_context csha224;
+       br_sha256_context csha256;
+       br_sha384_context csha384;
+       br_sha512_context csha512;
+
+       switch (id) {
+       case br_md5_ID:
+               br_md5_init(&cmd5);
+               br_md5_update(&cmd5, data, len);
+               br_md5_out(&cmd5, out);
+               return 16;
+       case br_sha1_ID:
+               br_sha1_init(&csha1);
+               br_sha1_update(&csha1, data, len);
+               br_sha1_out(&csha1, out);
+               return 20;
+       case br_sha224_ID:
+               br_sha224_init(&csha224);
+               br_sha224_update(&csha224, data, len);
+               br_sha224_out(&csha224, out);
+               return 28;
+       case br_sha256_ID:
+               br_sha256_init(&csha256);
+               br_sha256_update(&csha256, data, len);
+               br_sha256_out(&csha256, out);
+               return 32;
+       case br_sha384_ID:
+               br_sha384_init(&csha384);
+               br_sha384_update(&csha384, data, len);
+               br_sha384_out(&csha384, out);
+               return 48;
+       case br_sha512_ID:
+               br_sha512_init(&csha512);
+               br_sha512_update(&csha512, data, len);
+               br_sha512_out(&csha512, out);
+               return 64;
+       default:
+               fprintf(stderr, "Uknown hash function: %d\n", id);
+               exit(EXIT_FAILURE);
+               return 0;
+       }
+}
+
+/*
+ * Tests for a multihash. Returned value should be 258 multiplied by the
+ * number of hash functions implemented by the context.
+ */
+static int
+test_multihash_inner(br_multihash_context *mc)
+{
+       /*
+        * Try hashing messages for all lengths from 0 to 257 bytes
+        * (inclusive). Each attempt is done twice, with data input
+        * either in one go, or byte by byte. In the byte by byte
+        * test, intermediate result are obtained and checked.
+        */
+       size_t len;
+       unsigned char buf[258];
+       int i;
+       int tcount;
+
+       tcount = 0;
+       for (len = 0; len < sizeof buf; len ++) {
+               br_sha1_context sc;
+               unsigned char tmp[20];
+
+               br_sha1_init(&sc);
+               br_sha1_update(&sc, buf, len);
+               br_sha1_out(&sc, tmp);
+               buf[len] = tmp[0];
+       }
+       for (len = 0; len <= 257; len ++) {
+               size_t u;
+
+               br_multihash_init(mc);
+               br_multihash_update(mc, buf, len);
+               for (i = 1; i <= 6; i ++) {
+                       unsigned char tmp[64], tmp2[64];
+                       size_t olen, olen2;
+
+                       olen = br_multihash_out(mc, i, tmp);
+                       if (olen == 0) {
+                               continue;
+                       }
+                       olen2 = do_hash(i, buf, len, tmp2);
+                       if (olen != olen2) {
+                               fprintf(stderr,
+                                       "Bad hash output length: %u / %u\n",
+                                       (unsigned)olen, (unsigned)olen2);
+                               exit(EXIT_FAILURE);
+                       }
+                       check_equals("Hash output", tmp, tmp2, olen);
+                       tcount ++;
+               }
+
+               br_multihash_init(mc);
+               for (u = 0; u < len; u ++) {
+                       br_multihash_update(mc, buf + u, 1);
+                       for (i = 1; i <= 6; i ++) {
+                               unsigned char tmp[64], tmp2[64];
+                               size_t olen, olen2;
+
+                               olen = br_multihash_out(mc, i, tmp);
+                               if (olen == 0) {
+                                       continue;
+                               }
+                               olen2 = do_hash(i, buf, u + 1, tmp2);
+                               if (olen != olen2) {
+                                       fprintf(stderr, "Bad hash output"
+                                               " length: %u / %u\n",
+                                               (unsigned)olen,
+                                               (unsigned)olen2);
+                                       exit(EXIT_FAILURE);
+                               }
+                               check_equals("Hash output", tmp, tmp2, olen);
+                       }
+               }
+       }
+       return tcount;
+}
+
+static void
+test_multihash(void)
+{
+       br_multihash_context mc;
+
+       printf("Test MultiHash: ");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_md5_ID, &br_md5_vtable);
+       if (test_multihash_inner(&mc) != 258) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_sha1_ID, &br_sha1_vtable);
+       if (test_multihash_inner(&mc) != 258) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_sha224_ID, &br_sha224_vtable);
+       if (test_multihash_inner(&mc) != 258) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_sha256_ID, &br_sha256_vtable);
+       if (test_multihash_inner(&mc) != 258) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_sha384_ID, &br_sha384_vtable);
+       if (test_multihash_inner(&mc) != 258) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_sha512_ID, &br_sha512_vtable);
+       if (test_multihash_inner(&mc) != 258) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       br_multihash_zero(&mc);
+       br_multihash_setimpl(&mc, br_md5_ID, &br_md5_vtable);
+       br_multihash_setimpl(&mc, br_sha1_ID, &br_sha1_vtable);
+       br_multihash_setimpl(&mc, br_sha224_ID, &br_sha224_vtable);
+       br_multihash_setimpl(&mc, br_sha256_ID, &br_sha256_vtable);
+       br_multihash_setimpl(&mc, br_sha384_ID, &br_sha384_vtable);
+       br_multihash_setimpl(&mc, br_sha512_ID, &br_sha512_vtable);
+       if (test_multihash_inner(&mc) != 258 * 6) {
+               fprintf(stderr, "Failed test count\n");
+       }
+       printf(".");
+       fflush(stdout);
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+do_KAT_HMAC_bin_bin(const br_hash_class *digest_class,
+       const void *key, size_t key_len,
+       const void *data, size_t data_len, const char *href)
+{
+       br_hmac_key_context kc;
+       br_hmac_context ctx;
+       unsigned char tmp[64], ref[64];
+       size_t u, len;
+
+       len = hextobin(ref, href);
+       br_hmac_key_init(&kc, digest_class, key, key_len);
+       br_hmac_init(&ctx, &kc, 0);
+       br_hmac_update(&ctx, data, data_len);
+       br_hmac_out(&ctx, tmp);
+       check_equals("KAT HMAC 1", tmp, ref, len);
+
+       br_hmac_init(&ctx, &kc, 0);
+       for (u = 0; u < data_len; u ++) {
+               br_hmac_update(&ctx, (const unsigned char *)data + u, 1);
+       }
+       br_hmac_out(&ctx, tmp);
+       check_equals("KAT HMAC 2", tmp, ref, len);
+
+       for (u = 0; u < data_len; u ++) {
+               br_hmac_init(&ctx, &kc, 0);
+               br_hmac_update(&ctx, data, u);
+               br_hmac_out(&ctx, tmp);
+               br_hmac_update(&ctx,
+                       (const unsigned char *)data + u, data_len - u);
+               br_hmac_out(&ctx, tmp);
+               check_equals("KAT HMAC 3", tmp, ref, len);
+       }
+}
+
+static void
+do_KAT_HMAC_str_str(const br_hash_class *digest_class, const char *key,
+       const char *data, const char *href)
+{
+       do_KAT_HMAC_bin_bin(digest_class, key, strlen(key),
+               data, strlen(data), href);
+}
+
+static void
+do_KAT_HMAC_hex_hex(const br_hash_class *digest_class, const char *skey,
+       const char *sdata, const char *href)
+{
+       unsigned char key[1024];
+       unsigned char data[1024];
+
+       do_KAT_HMAC_bin_bin(digest_class, key, hextobin(key, skey),
+               data, hextobin(data, sdata), href);
+}
+
+static void
+do_KAT_HMAC_hex_str(const br_hash_class *digest_class,
+       const char *skey, const char *data, const char *href)
+{
+       unsigned char key[1024];
+
+       do_KAT_HMAC_bin_bin(digest_class, key, hextobin(key, skey),
+               data, strlen(data), href);
+}
+
+static void
+test_HMAC_CT(const br_hash_class *digest_class,
+       const void *key, size_t key_len, const void *data)
+{
+       br_hmac_key_context kc;
+       br_hmac_context hc1, hc2;
+       unsigned char buf1[64], buf2[64];
+       size_t u, v;
+
+       br_hmac_key_init(&kc, digest_class, key, key_len);
+
+       for (u = 0; u < 130; u ++) {
+               for (v = 0; v < 130; v ++) {
+                       size_t min_len, max_len;
+                       size_t w;
+
+                       min_len = v;
+                       max_len = v + 256;
+                       for (w = min_len; w <= max_len; w ++) {
+                               char tmp[30];
+                               size_t hlen1, hlen2;
+
+                               br_hmac_init(&hc1, &kc, 0);
+                               br_hmac_update(&hc1, data, u + w);
+                               hlen1 = br_hmac_out(&hc1, buf1);
+                               br_hmac_init(&hc2, &kc, 0);
+                               br_hmac_update(&hc2, data, u);
+                               hlen2 = br_hmac_outCT(&hc2,
+                                       (const unsigned char *)data + u, w,
+                                       min_len, max_len, buf2);
+                               if (hlen1 != hlen2) {
+                                       fprintf(stderr, "HMAC length mismatch:"
+                                               " %u / %u\n", (unsigned)hlen1,
+                                               (unsigned)hlen2);
+                                       exit(EXIT_FAILURE);
+                               }
+                               sprintf(tmp, "HMAC CT %u,%u,%u",
+                                       (unsigned)u, (unsigned)v, (unsigned)w);
+                               check_equals(tmp, buf1, buf2, hlen1);
+                       }
+               }
+               printf(".");
+               fflush(stdout);
+       }
+       printf(" ");
+       fflush(stdout);
+}
+
+static void
+test_HMAC(void)
+{
+       unsigned char data[1000];
+       unsigned x;
+       size_t u;
+       const char key[] = "test HMAC key";
+
+       printf("Test HMAC: ");
+       fflush(stdout);
+       do_KAT_HMAC_hex_str(&br_md5_vtable,
+               "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+               "Hi There",
+               "9294727a3638bb1c13f48ef8158bfc9d");
+       do_KAT_HMAC_str_str(&br_md5_vtable,
+               "Jefe",
+               "what do ya want for nothing?",
+               "750c783e6ab0b503eaa86e310a5db738");
+       do_KAT_HMAC_hex_hex(&br_md5_vtable,
+               "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+               "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+               "56be34521d144c88dbb8c733f0e8b3f6");
+       do_KAT_HMAC_hex_hex(&br_md5_vtable,
+               "0102030405060708090a0b0c0d0e0f10111213141516171819",
+               "CDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCD",
+               "697eaf0aca3a3aea3a75164746ffaa79");
+       do_KAT_HMAC_hex_str(&br_md5_vtable,
+               "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+               "Test With Truncation",
+               "56461ef2342edc00f9bab995690efd4c");
+       do_KAT_HMAC_hex_str(&br_md5_vtable,
+               "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+               "Test Using Larger Than Block-Size Key - Hash Key First",
+               "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd");
+       do_KAT_HMAC_hex_str(&br_md5_vtable,
+               "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+               "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+               "6f630fad67cda0ee1fb1f562db3aa53e");
+
+       do_KAT_HMAC_hex_str(&br_sha1_vtable,
+               "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+               "Hi There",
+               "b617318655057264e28bc0b6fb378c8ef146be00");
+       do_KAT_HMAC_str_str(&br_sha1_vtable,
+               "Jefe",
+               "what do ya want for nothing?",
+               "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");
+       do_KAT_HMAC_hex_hex(&br_sha1_vtable,
+               "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+               "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+               "125d7342b9ac11cd91a39af48aa17b4f63f175d3");
+       do_KAT_HMAC_hex_hex(&br_sha1_vtable,
+               "0102030405060708090a0b0c0d0e0f10111213141516171819",
+               "CDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCD",
+               "4c9007f4026250c6bc8414f9bf50c86c2d7235da");
+       do_KAT_HMAC_hex_str(&br_sha1_vtable,
+               "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+               "Test With Truncation",
+               "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04");
+       do_KAT_HMAC_hex_str(&br_sha1_vtable,
+               "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+               "Test Using Larger Than Block-Size Key - Hash Key First",
+               "aa4ae5e15272d00e95705637ce8a3b55ed402112");
+       do_KAT_HMAC_hex_str(&br_sha1_vtable,
+               "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+               "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+               "e8e99d0f45237d786d6bbaa7965c7808bbff1a91");
+
+       /* From RFC 4231 */
+
+       do_KAT_HMAC_hex_hex(&br_sha224_vtable,
+               "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+               "4869205468657265",
+               "896fb1128abbdf196832107cd49df33f"
+               "47b4b1169912ba4f53684b22");
+
+       do_KAT_HMAC_hex_hex(&br_sha256_vtable,
+               "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+               "4869205468657265",
+               "b0344c61d8db38535ca8afceaf0bf12b"
+               "881dc200c9833da726e9376c2e32cff7");
+
+       do_KAT_HMAC_hex_hex(&br_sha384_vtable,
+               "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+               "4869205468657265",
+               "afd03944d84895626b0825f4ab46907f"
+               "15f9dadbe4101ec682aa034c7cebc59c"
+               "faea9ea9076ede7f4af152e8b2fa9cb6");
+
+       do_KAT_HMAC_hex_hex(&br_sha512_vtable,
+               "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+               "4869205468657265",
+               "87aa7cdea5ef619d4ff0b4241a1d6cb0"
+               "2379f4e2ce4ec2787ad0b30545e17cde"
+               "daa833b7d6b8a702038b274eaea3f4e4"
+               "be9d914eeb61f1702e696c203a126854");
+
+       do_KAT_HMAC_hex_hex(&br_sha224_vtable,
+               "4a656665",
+               "7768617420646f2079612077616e7420"
+               "666f72206e6f7468696e673f",
+               "a30e01098bc6dbbf45690f3a7e9e6d0f"
+               "8bbea2a39e6148008fd05e44");
+
+       do_KAT_HMAC_hex_hex(&br_sha256_vtable,
+               "4a656665",
+               "7768617420646f2079612077616e7420"
+               "666f72206e6f7468696e673f",
+               "5bdcc146bf60754e6a042426089575c7"
+               "5a003f089d2739839dec58b964ec3843");
+
+       do_KAT_HMAC_hex_hex(&br_sha384_vtable,
+               "4a656665",
+               "7768617420646f2079612077616e7420"
+               "666f72206e6f7468696e673f",
+               "af45d2e376484031617f78d2b58a6b1b"
+               "9c7ef464f5a01b47e42ec3736322445e"
+               "8e2240ca5e69e2c78b3239ecfab21649");
+
+       do_KAT_HMAC_hex_hex(&br_sha512_vtable,
+               "4a656665",
+               "7768617420646f2079612077616e7420"
+               "666f72206e6f7468696e673f",
+               "164b7a7bfcf819e2e395fbe73b56e0a3"
+               "87bd64222e831fd610270cd7ea250554"
+               "9758bf75c05a994a6d034f65f8f0e6fd"
+               "caeab1a34d4a6b4b636e070a38bce737");
+
+       do_KAT_HMAC_hex_hex(&br_sha224_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaa",
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddd",
+               "7fb3cb3588c6c1f6ffa9694d7d6ad264"
+               "9365b0c1f65d69d1ec8333ea");
+
+       do_KAT_HMAC_hex_hex(&br_sha256_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaa",
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddd",
+               "773ea91e36800e46854db8ebd09181a7"
+               "2959098b3ef8c122d9635514ced565fe");
+
+       do_KAT_HMAC_hex_hex(&br_sha384_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaa",
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddd",
+               "88062608d3e6ad8a0aa2ace014c8a86f"
+               "0aa635d947ac9febe83ef4e55966144b"
+               "2a5ab39dc13814b94e3ab6e101a34f27");
+
+       do_KAT_HMAC_hex_hex(&br_sha512_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaa",
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddddddddddddddddddddddddddddddd"
+               "dddd",
+               "fa73b0089d56a284efb0f0756c890be9"
+               "b1b5dbdd8ee81a3655f83e33b2279d39"
+               "bf3e848279a722c806b485a47e67c807"
+               "b946a337bee8942674278859e13292fb");
+
+       do_KAT_HMAC_hex_hex(&br_sha224_vtable,
+               "0102030405060708090a0b0c0d0e0f10"
+               "111213141516171819",
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcd",
+               "6c11506874013cac6a2abc1bb382627c"
+               "ec6a90d86efc012de7afec5a");
+
+       do_KAT_HMAC_hex_hex(&br_sha256_vtable,
+               "0102030405060708090a0b0c0d0e0f10"
+               "111213141516171819",
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcd",
+               "82558a389a443c0ea4cc819899f2083a"
+               "85f0faa3e578f8077a2e3ff46729665b");
+
+       do_KAT_HMAC_hex_hex(&br_sha384_vtable,
+               "0102030405060708090a0b0c0d0e0f10"
+               "111213141516171819",
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcd",
+               "3e8a69b7783c25851933ab6290af6ca7"
+               "7a9981480850009cc5577c6e1f573b4e"
+               "6801dd23c4a7d679ccf8a386c674cffb");
+
+       do_KAT_HMAC_hex_hex(&br_sha512_vtable,
+               "0102030405060708090a0b0c0d0e0f10"
+               "111213141516171819",
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+               "cdcd",
+               "b0ba465637458c6990e5a8c5f61d4af7"
+               "e576d97ff94b872de76f8050361ee3db"
+               "a91ca5c11aa25eb4d679275cc5788063"
+               "a5f19741120c4f2de2adebeb10a298dd");
+
+       do_KAT_HMAC_hex_hex(&br_sha224_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54657374205573696e67204c61726765"
+               "72205468616e20426c6f636b2d53697a"
+               "65204b6579202d2048617368204b6579"
+               "204669727374",
+               "95e9a0db962095adaebe9b2d6f0dbce2"
+               "d499f112f2d2b7273fa6870e");
+
+       do_KAT_HMAC_hex_hex(&br_sha256_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54657374205573696e67204c61726765"
+               "72205468616e20426c6f636b2d53697a"
+               "65204b6579202d2048617368204b6579"
+               "204669727374",
+               "60e431591ee0b67f0d8a26aacbf5b77f"
+               "8e0bc6213728c5140546040f0ee37f54");
+
+       do_KAT_HMAC_hex_hex(&br_sha384_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54657374205573696e67204c61726765"
+               "72205468616e20426c6f636b2d53697a"
+               "65204b6579202d2048617368204b6579"
+               "204669727374",
+               "4ece084485813e9088d2c63a041bc5b4"
+               "4f9ef1012a2b588f3cd11f05033ac4c6"
+               "0c2ef6ab4030fe8296248df163f44952");
+
+       do_KAT_HMAC_hex_hex(&br_sha512_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54657374205573696e67204c61726765"
+               "72205468616e20426c6f636b2d53697a"
+               "65204b6579202d2048617368204b6579"
+               "204669727374",
+               "80b24263c7c1a3ebb71493c1dd7be8b4"
+               "9b46d1f41b4aeec1121b013783f8f352"
+               "6b56d037e05f2598bd0fd2215d6a1e52"
+               "95e64f73f63f0aec8b915a985d786598");
+
+       do_KAT_HMAC_hex_hex(&br_sha224_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54686973206973206120746573742075"
+               "73696e672061206c6172676572207468"
+               "616e20626c6f636b2d73697a65206b65"
+               "7920616e642061206c61726765722074"
+               "68616e20626c6f636b2d73697a652064"
+               "6174612e20546865206b6579206e6565"
+               "647320746f2062652068617368656420"
+               "6265666f7265206265696e6720757365"
+               "642062792074686520484d414320616c"
+               "676f726974686d2e",
+               "3a854166ac5d9f023f54d517d0b39dbd"
+               "946770db9c2b95c9f6f565d1");
+
+       do_KAT_HMAC_hex_hex(&br_sha256_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54686973206973206120746573742075"
+               "73696e672061206c6172676572207468"
+               "616e20626c6f636b2d73697a65206b65"
+               "7920616e642061206c61726765722074"
+               "68616e20626c6f636b2d73697a652064"
+               "6174612e20546865206b6579206e6565"
+               "647320746f2062652068617368656420"
+               "6265666f7265206265696e6720757365"
+               "642062792074686520484d414320616c"
+               "676f726974686d2e",
+               "9b09ffa71b942fcb27635fbcd5b0e944"
+               "bfdc63644f0713938a7f51535c3a35e2");
+
+       do_KAT_HMAC_hex_hex(&br_sha384_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54686973206973206120746573742075"
+               "73696e672061206c6172676572207468"
+               "616e20626c6f636b2d73697a65206b65"
+               "7920616e642061206c61726765722074"
+               "68616e20626c6f636b2d73697a652064"
+               "6174612e20546865206b6579206e6565"
+               "647320746f2062652068617368656420"
+               "6265666f7265206265696e6720757365"
+               "642062792074686520484d414320616c"
+               "676f726974686d2e",
+               "6617178e941f020d351e2f254e8fd32c"
+               "602420feb0b8fb9adccebb82461e99c5"
+               "a678cc31e799176d3860e6110c46523e");
+
+       do_KAT_HMAC_hex_hex(&br_sha512_vtable,
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+               "aaaaaa",
+               "54686973206973206120746573742075"
+               "73696e672061206c6172676572207468"
+               "616e20626c6f636b2d73697a65206b65"
+               "7920616e642061206c61726765722074"
+               "68616e20626c6f636b2d73697a652064"
+               "6174612e20546865206b6579206e6565"
+               "647320746f2062652068617368656420"
+               "6265666f7265206265696e6720757365"
+               "642062792074686520484d414320616c"
+               "676f726974686d2e",
+               "e37b6a775dc87dbaa4dfa9f96e5e3ffd"
+               "debd71f8867289865df5a32d20cdc944"
+               "b6022cac3c4982b10d5eeb55c3e4de15"
+               "134676fb6de0446065c97440fa8c6a58");
+
+       for (x = 1, u = 0; u < sizeof data; u ++) {
+               data[u] = x;
+               x = (x * 45) % 257;
+       }
+       printf("(MD5) ");
+       test_HMAC_CT(&br_md5_vtable, key, sizeof key, data);
+       printf("(SHA-1) ");
+       test_HMAC_CT(&br_sha1_vtable, key, sizeof key, data);
+       printf("(SHA-224) ");
+       test_HMAC_CT(&br_sha224_vtable, key, sizeof key, data);
+       printf("(SHA-256) ");
+       test_HMAC_CT(&br_sha256_vtable, key, sizeof key, data);
+       printf("(SHA-384) ");
+       test_HMAC_CT(&br_sha384_vtable, key, sizeof key, data);
+       printf("(SHA-512) ");
+       test_HMAC_CT(&br_sha512_vtable, key, sizeof key, data);
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_HMAC_DRBG(void)
+{
+       br_hmac_drbg_context ctx;
+       unsigned char seed[42], tmp[30];
+       unsigned char ref1[30], ref2[30], ref3[30];
+       size_t seed_len;
+
+       printf("Test HMAC_DRBG: ");
+       fflush(stdout);
+
+       seed_len = hextobin(seed,
+               "009A4D6792295A7F730FC3F2B49CBC0F62E862272F"
+               "01795EDF0D54DB760F156D0DAC04C0322B3A204224");
+       hextobin(ref1,
+               "9305A46DE7FF8EB107194DEBD3FD48AA"
+               "20D5E7656CBE0EA69D2A8D4E7C67");
+       hextobin(ref2,
+               "C70C78608A3B5BE9289BE90EF6E81A9E"
+               "2C1516D5751D2F75F50033E45F73");
+       hextobin(ref3,
+               "475E80E992140567FCC3A50DAB90FE84"
+               "BCD7BB03638E9C4656A06F37F650");
+       br_hmac_drbg_init(&ctx, &br_sha256_vtable, seed, seed_len);
+       br_hmac_drbg_generate(&ctx, tmp, sizeof tmp);
+       check_equals("KAT HMAC_DRBG 1", tmp, ref1, sizeof tmp);
+       br_hmac_drbg_generate(&ctx, tmp, sizeof tmp);
+       check_equals("KAT HMAC_DRBG 2", tmp, ref2, sizeof tmp);
+       br_hmac_drbg_generate(&ctx, tmp, sizeof tmp);
+       check_equals("KAT HMAC_DRBG 3", tmp, ref3, sizeof tmp);
+
+       memset(&ctx, 0, sizeof ctx);
+       br_hmac_drbg_vtable.init(&ctx.vtable,
+               &br_sha256_vtable, seed, seed_len);
+       ctx.vtable->generate(&ctx.vtable, tmp, sizeof tmp);
+       check_equals("KAT HMAC_DRBG 4", tmp, ref1, sizeof tmp);
+       ctx.vtable->generate(&ctx.vtable, tmp, sizeof tmp);
+       check_equals("KAT HMAC_DRBG 5", tmp, ref2, sizeof tmp);
+       ctx.vtable->generate(&ctx.vtable, tmp, sizeof tmp);
+       check_equals("KAT HMAC_DRBG 6", tmp, ref3, sizeof tmp);
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+do_KAT_PRF(
+       void (*prf)(void *dst, size_t len,
+               const void *secret, size_t secret_len,
+               const char *label, const void *seed, size_t seed_len),
+       const char *ssecret, const char *label, const char *sseed,
+       const char *sref)
+{
+       unsigned char secret[100], seed[100], ref[500], out[500];
+       size_t secret_len, seed_len, ref_len;
+
+       secret_len = hextobin(secret, ssecret);
+       seed_len = hextobin(seed, sseed);
+       ref_len = hextobin(ref, sref);
+       prf(out, ref_len, secret, secret_len, label, seed, seed_len);
+       check_equals("TLS PRF KAT", out, ref, ref_len);
+}
+
+static void
+test_PRF(void)
+{
+       printf("Test TLS PRF: ");
+       fflush(stdout);
+
+       /*
+        * Test vector taken from an email that was on:
+        * http://www.imc.org/ietf-tls/mail-archive/msg01589.html
+        * but no longer exists there; a version archived in 2008
+        * can be found on http://www.archive.org/
+        */
+       do_KAT_PRF(&br_tls10_prf,
+               "abababababababababababababababababababababababababababababababababababababababababababababababab",
+               "PRF Testvector",
+               "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+               "d3d4d1e349b5d515044666d51de32bab258cb521b6b053463e354832fd976754443bcf9a296519bc289abcbc1187e4ebd31e602353776c408aafb74cbc85eff69255f9788faa184cbb957a9819d84a5d7eb006eb459d3ae8de9810454b8b2d8f1afbc655a8c9a013");
+
+       /*
+        * Test vectors are taken from:
+        * https://www.ietf.org/mail-archive/web/tls/current/msg03416.html
+        */
+       do_KAT_PRF(&br_tls12_sha256_prf,
+               "9bbe436ba940f017b17652849a71db35",
+               "test label",
+               "a0ba9f936cda311827a6f796ffd5198c",
+               "e3f229ba727be17b8d122620557cd453c2aab21d07c3d495329b52d4e61edb5a6b301791e90d35c9c9a46b4e14baf9af0fa022f7077def17abfd3797c0564bab4fbc91666e9def9b97fce34f796789baa48082d122ee42c5a72e5a5110fff70187347b66");
+       do_KAT_PRF(&br_tls12_sha384_prf,
+               "b80b733d6ceefcdc71566ea48e5567df",
+               "test label",
+               "cd665cf6a8447dd6ff8b27555edb7465",
+               "7b0c18e9ced410ed1804f2cfa34a336a1c14dffb4900bb5fd7942107e81c83cde9ca0faa60be9fe34f82b1233c9146a0e534cb400fed2700884f9dc236f80edd8bfa961144c9e8d792eca722a7b32fc3d416d473ebc2c5fd4abfdad05d9184259b5bf8cd4d90fa0d31e2dec479e4f1a26066f2eea9a69236a3e52655c9e9aee691c8f3a26854308d5eaa3be85e0990703d73e56f");
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+/*
+ * AES known-answer tests. Order: key, plaintext, ciphertext.
+ */
+static const char *const KAT_AES[] = {
+       /*
+        * From FIPS-197.
+        */
+       "000102030405060708090a0b0c0d0e0f",
+       "00112233445566778899aabbccddeeff",
+       "69c4e0d86a7b0430d8cdb78070b4c55a",
+
+       "000102030405060708090a0b0c0d0e0f1011121314151617",
+       "00112233445566778899aabbccddeeff",
+       "dda97ca4864cdfe06eaf70a0ec0d7191",
+
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+       "00112233445566778899aabbccddeeff",
+       "8ea2b7ca516745bfeafc49904b496089",
+
+       /*
+        * From NIST validation suite (ECBVarTxt128.rsp).
+        */
+       "00000000000000000000000000000000",
+       "80000000000000000000000000000000",
+       "3ad78e726c1ec02b7ebfe92b23d9ec34",
+
+       "00000000000000000000000000000000",
+       "c0000000000000000000000000000000",
+       "aae5939c8efdf2f04e60b9fe7117b2c2",
+
+       "00000000000000000000000000000000",
+       "e0000000000000000000000000000000",
+       "f031d4d74f5dcbf39daaf8ca3af6e527",
+
+       "00000000000000000000000000000000",
+       "f0000000000000000000000000000000",
+       "96d9fd5cc4f07441727df0f33e401a36",
+
+       "00000000000000000000000000000000",
+       "f8000000000000000000000000000000",
+       "30ccdb044646d7e1f3ccea3dca08b8c0",
+
+       "00000000000000000000000000000000",
+       "fc000000000000000000000000000000",
+       "16ae4ce5042a67ee8e177b7c587ecc82",
+
+       "00000000000000000000000000000000",
+       "fe000000000000000000000000000000",
+       "b6da0bb11a23855d9c5cb1b4c6412e0a",
+
+       "00000000000000000000000000000000",
+       "ff000000000000000000000000000000",
+       "db4f1aa530967d6732ce4715eb0ee24b",
+
+       "00000000000000000000000000000000",
+       "ff800000000000000000000000000000",
+       "a81738252621dd180a34f3455b4baa2f",
+
+       "00000000000000000000000000000000",
+       "ffc00000000000000000000000000000",
+       "77e2b508db7fd89234caf7939ee5621a",
+
+       "00000000000000000000000000000000",
+       "ffe00000000000000000000000000000",
+       "b8499c251f8442ee13f0933b688fcd19",
+
+       "00000000000000000000000000000000",
+       "fff00000000000000000000000000000",
+       "965135f8a81f25c9d630b17502f68e53",
+
+       "00000000000000000000000000000000",
+       "fff80000000000000000000000000000",
+       "8b87145a01ad1c6cede995ea3670454f",
+
+       "00000000000000000000000000000000",
+       "fffc0000000000000000000000000000",
+       "8eae3b10a0c8ca6d1d3b0fa61e56b0b2",
+
+       "00000000000000000000000000000000",
+       "fffe0000000000000000000000000000",
+       "64b4d629810fda6bafdf08f3b0d8d2c5",
+
+       "00000000000000000000000000000000",
+       "ffff0000000000000000000000000000",
+       "d7e5dbd3324595f8fdc7d7c571da6c2a",
+
+       "00000000000000000000000000000000",
+       "ffff8000000000000000000000000000",
+       "f3f72375264e167fca9de2c1527d9606",
+
+       "00000000000000000000000000000000",
+       "ffffc000000000000000000000000000",
+       "8ee79dd4f401ff9b7ea945d86666c13b",
+
+       "00000000000000000000000000000000",
+       "ffffe000000000000000000000000000",
+       "dd35cea2799940b40db3f819cb94c08b",
+
+       "00000000000000000000000000000000",
+       "fffff000000000000000000000000000",
+       "6941cb6b3e08c2b7afa581ebdd607b87",
+
+       "00000000000000000000000000000000",
+       "fffff800000000000000000000000000",
+       "2c20f439f6bb097b29b8bd6d99aad799",
+
+       "00000000000000000000000000000000",
+       "fffffc00000000000000000000000000",
+       "625d01f058e565f77ae86378bd2c49b3",
+
+       "00000000000000000000000000000000",
+       "fffffe00000000000000000000000000",
+       "c0b5fd98190ef45fbb4301438d095950",
+
+       "00000000000000000000000000000000",
+       "ffffff00000000000000000000000000",
+       "13001ff5d99806efd25da34f56be854b",
+
+       "00000000000000000000000000000000",
+       "ffffff80000000000000000000000000",
+       "3b594c60f5c8277a5113677f94208d82",
+
+       "00000000000000000000000000000000",
+       "ffffffc0000000000000000000000000",
+       "e9c0fc1818e4aa46bd2e39d638f89e05",
+
+       "00000000000000000000000000000000",
+       "ffffffe0000000000000000000000000",
+       "f8023ee9c3fdc45a019b4e985c7e1a54",
+
+       "00000000000000000000000000000000",
+       "fffffff0000000000000000000000000",
+       "35f40182ab4662f3023baec1ee796b57",
+
+       "00000000000000000000000000000000",
+       "fffffff8000000000000000000000000",
+       "3aebbad7303649b4194a6945c6cc3694",
+
+       "00000000000000000000000000000000",
+       "fffffffc000000000000000000000000",
+       "a2124bea53ec2834279bed7f7eb0f938",
+
+       "00000000000000000000000000000000",
+       "fffffffe000000000000000000000000",
+       "b9fb4399fa4facc7309e14ec98360b0a",
+
+       "00000000000000000000000000000000",
+       "ffffffff000000000000000000000000",
+       "c26277437420c5d634f715aea81a9132",
+
+       "00000000000000000000000000000000",
+       "ffffffff800000000000000000000000",
+       "171a0e1b2dd424f0e089af2c4c10f32f",
+
+       "00000000000000000000000000000000",
+       "ffffffffc00000000000000000000000",
+       "7cadbe402d1b208fe735edce00aee7ce",
+
+       "00000000000000000000000000000000",
+       "ffffffffe00000000000000000000000",
+       "43b02ff929a1485af6f5c6d6558baa0f",
+
+       "00000000000000000000000000000000",
+       "fffffffff00000000000000000000000",
+       "092faacc9bf43508bf8fa8613ca75dea",
+
+       "00000000000000000000000000000000",
+       "fffffffff80000000000000000000000",
+       "cb2bf8280f3f9742c7ed513fe802629c",
+
+       "00000000000000000000000000000000",
+       "fffffffffc0000000000000000000000",
+       "215a41ee442fa992a6e323986ded3f68",
+
+       "00000000000000000000000000000000",
+       "fffffffffe0000000000000000000000",
+       "f21e99cf4f0f77cea836e11a2fe75fb1",
+
+       "00000000000000000000000000000000",
+       "ffffffffff0000000000000000000000",
+       "95e3a0ca9079e646331df8b4e70d2cd6",
+
+       "00000000000000000000000000000000",
+       "ffffffffff8000000000000000000000",
+       "4afe7f120ce7613f74fc12a01a828073",
+
+       "00000000000000000000000000000000",
+       "ffffffffffc000000000000000000000",
+       "827f000e75e2c8b9d479beed913fe678",
+
+       "00000000000000000000000000000000",
+       "ffffffffffe000000000000000000000",
+       "35830c8e7aaefe2d30310ef381cbf691",
+
+       "00000000000000000000000000000000",
+       "fffffffffff000000000000000000000",
+       "191aa0f2c8570144f38657ea4085ebe5",
+
+       "00000000000000000000000000000000",
+       "fffffffffff800000000000000000000",
+       "85062c2c909f15d9269b6c18ce99c4f0",
+
+       "00000000000000000000000000000000",
+       "fffffffffffc00000000000000000000",
+       "678034dc9e41b5a560ed239eeab1bc78",
+
+       "00000000000000000000000000000000",
+       "fffffffffffe00000000000000000000",
+       "c2f93a4ce5ab6d5d56f1b93cf19911c1",
+
+       "00000000000000000000000000000000",
+       "ffffffffffff00000000000000000000",
+       "1c3112bcb0c1dcc749d799743691bf82",
+
+       "00000000000000000000000000000000",
+       "ffffffffffff80000000000000000000",
+       "00c55bd75c7f9c881989d3ec1911c0d4",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffc0000000000000000000",
+       "ea2e6b5ef182b7dff3629abd6a12045f",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffe0000000000000000000",
+       "22322327e01780b17397f24087f8cc6f",
+
+       "00000000000000000000000000000000",
+       "fffffffffffff0000000000000000000",
+       "c9cacb5cd11692c373b2411768149ee7",
+
+       "00000000000000000000000000000000",
+       "fffffffffffff8000000000000000000",
+       "a18e3dbbca577860dab6b80da3139256",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffc000000000000000000",
+       "79b61c37bf328ecca8d743265a3d425c",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffe000000000000000000",
+       "d2d99c6bcc1f06fda8e27e8ae3f1ccc7",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffff000000000000000000",
+       "1bfd4b91c701fd6b61b7f997829d663b",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffff800000000000000000",
+       "11005d52f25f16bdc9545a876a63490a",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffc00000000000000000",
+       "3a4d354f02bb5a5e47d39666867f246a",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffe00000000000000000",
+       "d451b8d6e1e1a0ebb155fbbf6e7b7dc3",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffff00000000000000000",
+       "6898d4f42fa7ba6a10ac05e87b9f2080",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffff80000000000000000",
+       "b611295e739ca7d9b50f8e4c0e754a3f",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffc0000000000000000",
+       "7d33fc7d8abe3ca1936759f8f5deaf20",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffe0000000000000000",
+       "3b5e0f566dc96c298f0c12637539b25c",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffff0000000000000000",
+       "f807c3e7985fe0f5a50e2cdb25c5109e",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffff8000000000000000",
+       "41f992a856fb278b389a62f5d274d7e9",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffc000000000000000",
+       "10d3ed7a6fe15ab4d91acbc7d0767ab1",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffe000000000000000",
+       "21feecd45b2e675973ac33bf0c5424fc",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffff000000000000000",
+       "1480cb3955ba62d09eea668f7c708817",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffff800000000000000",
+       "66404033d6b72b609354d5496e7eb511",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffc00000000000000",
+       "1c317a220a7d700da2b1e075b00266e1",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffe00000000000000",
+       "ab3b89542233f1271bf8fd0c0f403545",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffff00000000000000",
+       "d93eae966fac46dca927d6b114fa3f9e",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffff80000000000000",
+       "1bdec521316503d9d5ee65df3ea94ddf",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffc0000000000000",
+       "eef456431dea8b4acf83bdae3717f75f",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffe0000000000000",
+       "06f2519a2fafaa596bfef5cfa15c21b9",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffff0000000000000",
+       "251a7eac7e2fe809e4aa8d0d7012531a",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffff8000000000000",
+       "3bffc16e4c49b268a20f8d96a60b4058",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffc000000000000",
+       "e886f9281999c5bb3b3e8862e2f7c988",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffe000000000000",
+       "563bf90d61beef39f48dd625fcef1361",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffff000000000000",
+       "4d37c850644563c69fd0acd9a049325b",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffff800000000000",
+       "b87c921b91829ef3b13ca541ee1130a6",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffc00000000000",
+       "2e65eb6b6ea383e109accce8326b0393",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffe00000000000",
+       "9ca547f7439edc3e255c0f4d49aa8990",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffff00000000000",
+       "a5e652614c9300f37816b1f9fd0c87f9",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffff80000000000",
+       "14954f0b4697776f44494fe458d814ed",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffc0000000000",
+       "7c8d9ab6c2761723fe42f8bb506cbcf7",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffe0000000000",
+       "db7e1932679fdd99742aab04aa0d5a80",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffff0000000000",
+       "4c6a1c83e568cd10f27c2d73ded19c28",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffff8000000000",
+       "90ecbe6177e674c98de412413f7ac915",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffc000000000",
+       "90684a2ac55fe1ec2b8ebd5622520b73",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffe000000000",
+       "7472f9a7988607ca79707795991035e6",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffff000000000",
+       "56aff089878bf3352f8df172a3ae47d8",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffff800000000",
+       "65c0526cbe40161b8019a2a3171abd23",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffc00000000",
+       "377be0be33b4e3e310b4aabda173f84f",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffe00000000",
+       "9402e9aa6f69de6504da8d20c4fcaa2f",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffff00000000",
+       "123c1f4af313ad8c2ce648b2e71fb6e1",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffff80000000",
+       "1ffc626d30203dcdb0019fb80f726cf4",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffc0000000",
+       "76da1fbe3a50728c50fd2e621b5ad885",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffe0000000",
+       "082eb8be35f442fb52668e16a591d1d6",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffff0000000",
+       "e656f9ecf5fe27ec3e4a73d00c282fb3",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffff8000000",
+       "2ca8209d63274cd9a29bb74bcd77683a",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffc000000",
+       "79bf5dce14bb7dd73a8e3611de7ce026",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffe000000",
+       "3c849939a5d29399f344c4a0eca8a576",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffff000000",
+       "ed3c0a94d59bece98835da7aa4f07ca2",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffff800000",
+       "63919ed4ce10196438b6ad09d99cd795",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffc00000",
+       "7678f3a833f19fea95f3c6029e2bc610",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffe00000",
+       "3aa426831067d36b92be7c5f81c13c56",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffff00000",
+       "9272e2d2cdd11050998c845077a30ea0",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffff80000",
+       "088c4b53f5ec0ff814c19adae7f6246c",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffc0000",
+       "4010a5e401fdf0a0354ddbcc0d012b17",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffe0000",
+       "a87a385736c0a6189bd6589bd8445a93",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffff0000",
+       "545f2b83d9616dccf60fa9830e9cd287",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffff8000",
+       "4b706f7f92406352394037a6d4f4688d",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffc000",
+       "b7972b3941c44b90afa7b264bfba7387",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffe000",
+       "6f45732cf10881546f0fd23896d2bb60",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffff000",
+       "2e3579ca15af27f64b3c955a5bfc30ba",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffff800",
+       "34a2c5a91ae2aec99b7d1b5fa6780447",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffc00",
+       "a4d6616bd04f87335b0e53351227a9ee",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffe00",
+       "7f692b03945867d16179a8cefc83ea3f",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffff00",
+       "3bd141ee84a0e6414a26e7a4f281f8a2",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffff80",
+       "d1788f572d98b2b16ec5d5f3922b99bc",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffc0",
+       "0833ff6f61d98a57b288e8c3586b85a6",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffe0",
+       "8568261797de176bf0b43becc6285afb",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffff0",
+       "f9b0fda0c4a898f5b9e6f661c4ce4d07",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffff8",
+       "8ade895913685c67c5269f8aae42983e",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffffc",
+       "39bde67d5c8ed8a8b1c37eb8fa9f5ac0",
+
+       "00000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffffe",
+       "5c005e72c1418c44f569f2ea33ba54f3",
+
+       "00000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffff",
+       "3f5b8cc9ea855a0afa7347d23e8d664e",
+
+       /*
+        * From NIST validation suite (ECBVarTxt192.rsp).
+        */
+       "000000000000000000000000000000000000000000000000",
+       "80000000000000000000000000000000",
+       "6cd02513e8d4dc986b4afe087a60bd0c",
+
+       "000000000000000000000000000000000000000000000000",
+       "c0000000000000000000000000000000",
+       "2ce1f8b7e30627c1c4519eada44bc436",
+
+       "000000000000000000000000000000000000000000000000",
+       "e0000000000000000000000000000000",
+       "9946b5f87af446f5796c1fee63a2da24",
+
+       "000000000000000000000000000000000000000000000000",
+       "f0000000000000000000000000000000",
+       "2a560364ce529efc21788779568d5555",
+
+       "000000000000000000000000000000000000000000000000",
+       "f8000000000000000000000000000000",
+       "35c1471837af446153bce55d5ba72a0a",
+
+       "000000000000000000000000000000000000000000000000",
+       "fc000000000000000000000000000000",
+       "ce60bc52386234f158f84341e534cd9e",
+
+       "000000000000000000000000000000000000000000000000",
+       "fe000000000000000000000000000000",
+       "8c7c27ff32bcf8dc2dc57c90c2903961",
+
+       "000000000000000000000000000000000000000000000000",
+       "ff000000000000000000000000000000",
+       "32bb6a7ec84499e166f936003d55a5bb",
+
+       "000000000000000000000000000000000000000000000000",
+       "ff800000000000000000000000000000",
+       "a5c772e5c62631ef660ee1d5877f6d1b",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffc00000000000000000000000000000",
+       "030d7e5b64f380a7e4ea5387b5cd7f49",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffe00000000000000000000000000000",
+       "0dc9a2610037009b698f11bb7e86c83e",
+
+       "000000000000000000000000000000000000000000000000",
+       "fff00000000000000000000000000000",
+       "0046612c766d1840c226364f1fa7ed72",
+
+       "000000000000000000000000000000000000000000000000",
+       "fff80000000000000000000000000000",
+       "4880c7e08f27befe78590743c05e698b",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffc0000000000000000000000000000",
+       "2520ce829a26577f0f4822c4ecc87401",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffe0000000000000000000000000000",
+       "8765e8acc169758319cb46dc7bcf3dca",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffff0000000000000000000000000000",
+       "e98f4ba4f073df4baa116d011dc24a28",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffff8000000000000000000000000000",
+       "f378f68c5dbf59e211b3a659a7317d94",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffc000000000000000000000000000",
+       "283d3b069d8eb9fb432d74b96ca762b4",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffe000000000000000000000000000",
+       "a7e1842e8a87861c221a500883245c51",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffff000000000000000000000000000",
+       "77aa270471881be070fb52c7067ce732",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffff800000000000000000000000000",
+       "01b0f476d484f43f1aeb6efa9361a8ac",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffc00000000000000000000000000",
+       "1c3a94f1c052c55c2d8359aff2163b4f",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffe00000000000000000000000000",
+       "e8a067b604d5373d8b0f2e05a03b341b",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffff00000000000000000000000000",
+       "a7876ec87f5a09bfea42c77da30fd50e",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffff80000000000000000000000000",
+       "0cf3e9d3a42be5b854ca65b13f35f48d",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffc0000000000000000000000000",
+       "6c62f6bbcab7c3e821c9290f08892dda",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffe0000000000000000000000000",
+       "7f5e05bd2068738196fee79ace7e3aec",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffff0000000000000000000000000",
+       "440e0d733255cda92fb46e842fe58054",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffff8000000000000000000000000",
+       "aa5d5b1c4ea1b7a22e5583ac2e9ed8a7",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffc000000000000000000000000",
+       "77e537e89e8491e8662aae3bc809421d",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffe000000000000000000000000",
+       "997dd3e9f1598bfa73f75973f7e93b76",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffff000000000000000000000000",
+       "1b38d4f7452afefcb7fc721244e4b72e",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffff800000000000000000000000",
+       "0be2b18252e774dda30cdda02c6906e3",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffc00000000000000000000000",
+       "d2695e59c20361d82652d7d58b6f11b2",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffe00000000000000000000000",
+       "902d88d13eae52089abd6143cfe394e9",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffff00000000000000000000000",
+       "d49bceb3b823fedd602c305345734bd2",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffff80000000000000000000000",
+       "707b1dbb0ffa40ef7d95def421233fae",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffc0000000000000000000000",
+       "7ca0c1d93356d9eb8aa952084d75f913",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffe0000000000000000000000",
+       "f2cbf9cb186e270dd7bdb0c28febc57d",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffff0000000000000000000000",
+       "c94337c37c4e790ab45780bd9c3674a0",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffff8000000000000000000000",
+       "8e3558c135252fb9c9f367ed609467a1",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffc000000000000000000000",
+       "1b72eeaee4899b443914e5b3a57fba92",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffe000000000000000000000",
+       "011865f91bc56868d051e52c9efd59b7",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffff000000000000000000000",
+       "e4771318ad7a63dd680f6e583b7747ea",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffff800000000000000000000",
+       "61e3d194088dc8d97e9e6db37457eac5",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffc00000000000000000000",
+       "36ff1ec9ccfbc349e5d356d063693ad6",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffe00000000000000000000",
+       "3cc9e9a9be8cc3f6fb2ea24088e9bb19",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffff00000000000000000000",
+       "1ee5ab003dc8722e74905d9a8fe3d350",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffff80000000000000000000",
+       "245339319584b0a412412869d6c2eada",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffc0000000000000000000",
+       "7bd496918115d14ed5380852716c8814",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffe0000000000000000000",
+       "273ab2f2b4a366a57d582a339313c8b1",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffff0000000000000000000",
+       "113365a9ffbe3b0ca61e98507554168b",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffff8000000000000000000",
+       "afa99c997ac478a0dea4119c9e45f8b1",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffc000000000000000000",
+       "9216309a7842430b83ffb98638011512",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffe000000000000000000",
+       "62abc792288258492a7cb45145f4b759",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffff000000000000000000",
+       "534923c169d504d7519c15d30e756c50",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffff800000000000000000",
+       "fa75e05bcdc7e00c273fa33f6ee441d2",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffc00000000000000000",
+       "7d350fa6057080f1086a56b17ec240db",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffe00000000000000000",
+       "f34e4a6324ea4a5c39a661c8fe5ada8f",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffff00000000000000000",
+       "0882a16f44088d42447a29ac090ec17e",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffff80000000000000000",
+       "3a3c15bfc11a9537c130687004e136ee",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffc0000000000000000",
+       "22c0a7678dc6d8cf5c8a6d5a9960767c",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffe0000000000000000",
+       "b46b09809d68b9a456432a79bdc2e38c",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffff0000000000000000",
+       "93baaffb35fbe739c17c6ac22eecf18f",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffff8000000000000000",
+       "c8aa80a7850675bc007c46df06b49868",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffc000000000000000",
+       "12c6f3877af421a918a84b775858021d",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffe000000000000000",
+       "33f123282c5d633924f7d5ba3f3cab11",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffff000000000000000",
+       "a8f161002733e93ca4527d22c1a0c5bb",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffff800000000000000",
+       "b72f70ebf3e3fda23f508eec76b42c02",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffc00000000000000",
+       "6a9d965e6274143f25afdcfc88ffd77c",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffe00000000000000",
+       "a0c74fd0b9361764ce91c5200b095357",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffff00000000000000",
+       "091d1fdc2bd2c346cd5046a8c6209146",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffff80000000000000",
+       "e2a37580116cfb71856254496ab0aca8",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffc0000000000000",
+       "e0b3a00785917c7efc9adba322813571",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffe0000000000000",
+       "733d41f4727b5ef0df4af4cf3cffa0cb",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffff0000000000000",
+       "a99ebb030260826f981ad3e64490aa4f",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffff8000000000000",
+       "73f34c7d3eae5e80082c1647524308ee",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffc000000000000",
+       "40ebd5ad082345b7a2097ccd3464da02",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffe000000000000",
+       "7cc4ae9a424b2cec90c97153c2457ec5",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffff000000000000",
+       "54d632d03aba0bd0f91877ebdd4d09cb",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffff800000000000",
+       "d3427be7e4d27cd54f5fe37b03cf0897",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffc00000000000",
+       "b2099795e88cc158fd75ea133d7e7fbe",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffe00000000000",
+       "a6cae46fb6fadfe7a2c302a34242817b",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffff00000000000",
+       "026a7024d6a902e0b3ffccbaa910cc3f",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffff80000000000",
+       "156f07767a85a4312321f63968338a01",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffc0000000000",
+       "15eec9ebf42b9ca76897d2cd6c5a12e2",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffe0000000000",
+       "db0d3a6fdcc13f915e2b302ceeb70fd8",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffff0000000000",
+       "71dbf37e87a2e34d15b20e8f10e48924",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffff8000000000",
+       "c745c451e96ff3c045e4367c833e3b54",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffc000000000",
+       "340da09c2dd11c3b679d08ccd27dd595",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffe000000000",
+       "8279f7c0c2a03ee660c6d392db025d18",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffff000000000",
+       "a4b2c7d8eba531ff47c5041a55fbd1ec",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffff800000000",
+       "74569a2ca5a7bd5131ce8dc7cbfbf72f",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffc00000000",
+       "3713da0c0219b63454035613b5a403dd",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffe00000000",
+       "8827551ddcc9df23fa72a3de4e9f0b07",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffff00000000",
+       "2e3febfd625bfcd0a2c06eb460da1732",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffff80000000",
+       "ee82e6ba488156f76496311da6941deb",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffc0000000",
+       "4770446f01d1f391256e85a1b30d89d3",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffe0000000",
+       "af04b68f104f21ef2afb4767cf74143c",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffff0000000",
+       "cf3579a9ba38c8e43653173e14f3a4c6",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffff8000000",
+       "b3bba904f4953e09b54800af2f62e7d4",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffc000000",
+       "fc4249656e14b29eb9c44829b4c59a46",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffe000000",
+       "9b31568febe81cfc2e65af1c86d1a308",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffff000000",
+       "9ca09c25f273a766db98a480ce8dfedc",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffff800000",
+       "b909925786f34c3c92d971883c9fbedf",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffc00000",
+       "82647f1332fe570a9d4d92b2ee771d3b",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffe00000",
+       "3604a7e80832b3a99954bca6f5b9f501",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffff00000",
+       "884607b128c5de3ab39a529a1ef51bef",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffff80000",
+       "670cfa093d1dbdb2317041404102435e",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffc0000",
+       "7a867195f3ce8769cbd336502fbb5130",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffe0000",
+       "52efcf64c72b2f7ca5b3c836b1078c15",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffff0000",
+       "4019250f6eefb2ac5ccbcae044e75c7e",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffff8000",
+       "022c4f6f5a017d292785627667ddef24",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffc000",
+       "e9c21078a2eb7e03250f71000fa9e3ed",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffe000",
+       "a13eaeeb9cd391da4e2b09490b3e7fad",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffff000",
+       "c958a171dca1d4ed53e1af1d380803a9",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffff800",
+       "21442e07a110667f2583eaeeee44dc8c",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffc00",
+       "59bbb353cf1dd867a6e33737af655e99",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffe00",
+       "43cd3b25375d0ce41087ff9fe2829639",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffff00",
+       "6b98b17e80d1118e3516bd768b285a84",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffff80",
+       "ae47ed3676ca0c08deea02d95b81db58",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffc0",
+       "34ec40dc20413795ed53628ea748720b",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffe0",
+       "4dc68163f8e9835473253542c8a65d46",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffff0",
+       "2aabb999f43693175af65c6c612c46fb",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffff8",
+       "e01f94499dac3547515c5b1d756f0f58",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffffc",
+       "9d12435a46480ce00ea349f71799df9a",
+
+       "000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffffe",
+       "cef41d16d266bdfe46938ad7884cc0cf",
+
+       "000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffff",
+       "b13db4da1f718bc6904797c82bcf2d32",
+
+       /*
+        * From NIST validation suite (ECBVarTxt256.rsp).
+        */
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "80000000000000000000000000000000",
+       "ddc6bf790c15760d8d9aeb6f9a75fd4e",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "c0000000000000000000000000000000",
+       "0a6bdc6d4c1e6280301fd8e97ddbe601",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "e0000000000000000000000000000000",
+       "9b80eefb7ebe2d2b16247aa0efc72f5d",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "f0000000000000000000000000000000",
+       "7f2c5ece07a98d8bee13c51177395ff7",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "f8000000000000000000000000000000",
+       "7818d800dcf6f4be1e0e94f403d1e4c2",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fc000000000000000000000000000000",
+       "e74cd1c92f0919c35a0324123d6177d3",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fe000000000000000000000000000000",
+       "8092a4dcf2da7e77e93bdd371dfed82e",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ff000000000000000000000000000000",
+       "49af6b372135acef10132e548f217b17",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ff800000000000000000000000000000",
+       "8bcd40f94ebb63b9f7909676e667f1e7",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffc00000000000000000000000000000",
+       "fe1cffb83f45dcfb38b29be438dbd3ab",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffe00000000000000000000000000000",
+       "0dc58a8d886623705aec15cb1e70dc0e",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fff00000000000000000000000000000",
+       "c218faa16056bd0774c3e8d79c35a5e4",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fff80000000000000000000000000000",
+       "047bba83f7aa841731504e012208fc9e",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffc0000000000000000000000000000",
+       "dc8f0e4915fd81ba70a331310882f6da",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffe0000000000000000000000000000",
+       "1569859ea6b7206c30bf4fd0cbfac33c",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffff0000000000000000000000000000",
+       "300ade92f88f48fa2df730ec16ef44cd",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffff8000000000000000000000000000",
+       "1fe6cc3c05965dc08eb0590c95ac71d0",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffc000000000000000000000000000",
+       "59e858eaaa97fec38111275b6cf5abc0",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffe000000000000000000000000000",
+       "2239455e7afe3b0616100288cc5a723b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffff000000000000000000000000000",
+       "3ee500c5c8d63479717163e55c5c4522",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffff800000000000000000000000000",
+       "d5e38bf15f16d90e3e214041d774daa8",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffc00000000000000000000000000",
+       "b1f4066e6f4f187dfe5f2ad1b17819d0",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffe00000000000000000000000000",
+       "6ef4cc4de49b11065d7af2909854794a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffff00000000000000000000000000",
+       "ac86bc606b6640c309e782f232bf367f",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffff80000000000000000000000000",
+       "36aff0ef7bf3280772cf4cac80a0d2b2",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffc0000000000000000000000000",
+       "1f8eedea0f62a1406d58cfc3ecea72cf",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffe0000000000000000000000000",
+       "abf4154a3375a1d3e6b1d454438f95a6",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffff0000000000000000000000000",
+       "96f96e9d607f6615fc192061ee648b07",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffff8000000000000000000000000",
+       "cf37cdaaa0d2d536c71857634c792064",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffc000000000000000000000000",
+       "fbd6640c80245c2b805373f130703127",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffe000000000000000000000000",
+       "8d6a8afe55a6e481badae0d146f436db",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffff000000000000000000000000",
+       "6a4981f2915e3e68af6c22385dd06756",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffff800000000000000000000000",
+       "42a1136e5f8d8d21d3101998642d573b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffc00000000000000000000000",
+       "9b471596dc69ae1586cee6158b0b0181",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffe00000000000000000000000",
+       "753665c4af1eff33aa8b628bf8741cfd",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffff00000000000000000000000",
+       "9a682acf40be01f5b2a4193c9a82404d",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffff80000000000000000000000",
+       "54fafe26e4287f17d1935f87eb9ade01",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffc0000000000000000000000",
+       "49d541b2e74cfe73e6a8e8225f7bd449",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffe0000000000000000000000",
+       "11a45530f624ff6f76a1b3826626ff7b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffff0000000000000000000000",
+       "f96b0c4a8bc6c86130289f60b43b8fba",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffff8000000000000000000000",
+       "48c7d0e80834ebdc35b6735f76b46c8b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffc000000000000000000000",
+       "2463531ab54d66955e73edc4cb8eaa45",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffe000000000000000000000",
+       "ac9bd8e2530469134b9d5b065d4f565b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffff000000000000000000000",
+       "3f5f9106d0e52f973d4890e6f37e8a00",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffff800000000000000000000",
+       "20ebc86f1304d272e2e207e59db639f0",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffc00000000000000000000",
+       "e67ae6426bf9526c972cff072b52252c",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffe00000000000000000000",
+       "1a518dddaf9efa0d002cc58d107edfc8",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffff00000000000000000000",
+       "ead731af4d3a2fe3b34bed047942a49f",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffff80000000000000000000",
+       "b1d4efe40242f83e93b6c8d7efb5eae9",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffc0000000000000000000",
+       "cd2b1fec11fd906c5c7630099443610a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffe0000000000000000000",
+       "a1853fe47fe29289d153161d06387d21",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffff0000000000000000000",
+       "4632154179a555c17ea604d0889fab14",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffff8000000000000000000",
+       "dd27cac6401a022e8f38f9f93e774417",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffc000000000000000000",
+       "c090313eb98674f35f3123385fb95d4d",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffe000000000000000000",
+       "cc3526262b92f02edce548f716b9f45c",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffff000000000000000000",
+       "c0838d1a2b16a7c7f0dfcc433c399c33",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffff800000000000000000",
+       "0d9ac756eb297695eed4d382eb126d26",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffc00000000000000000",
+       "56ede9dda3f6f141bff1757fa689c3e1",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffe00000000000000000",
+       "768f520efe0f23e61d3ec8ad9ce91774",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffff00000000000000000",
+       "b1144ddfa75755213390e7c596660490",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffff80000000000000000",
+       "1d7c0c4040b355b9d107a99325e3b050",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffc0000000000000000",
+       "d8e2bb1ae8ee3dcf5bf7d6c38da82a1a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffe0000000000000000",
+       "faf82d178af25a9886a47e7f789b98d7",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffff0000000000000000",
+       "9b58dbfd77fe5aca9cfc190cd1b82d19",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffff8000000000000000",
+       "77f392089042e478ac16c0c86a0b5db5",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffc000000000000000",
+       "19f08e3420ee69b477ca1420281c4782",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffe000000000000000",
+       "a1b19beee4e117139f74b3c53fdcb875",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffff000000000000000",
+       "a37a5869b218a9f3a0868d19aea0ad6a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffff800000000000000",
+       "bc3594e865bcd0261b13202731f33580",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffc00000000000000",
+       "811441ce1d309eee7185e8c752c07557",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffe00000000000000",
+       "959971ce4134190563518e700b9874d1",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffff00000000000000",
+       "76b5614a042707c98e2132e2e805fe63",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffff80000000000000",
+       "7d9fa6a57530d0f036fec31c230b0cc6",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffc0000000000000",
+       "964153a83bf6989a4ba80daa91c3e081",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffe0000000000000",
+       "a013014d4ce8054cf2591d06f6f2f176",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffff0000000000000",
+       "d1c5f6399bf382502e385eee1474a869",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffff8000000000000",
+       "0007e20b8298ec354f0f5fe7470f36bd",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffc000000000000",
+       "b95ba05b332da61ef63a2b31fcad9879",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffe000000000000",
+       "4620a49bd967491561669ab25dce45f4",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffff000000000000",
+       "12e71214ae8e04f0bb63d7425c6f14d5",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffff800000000000",
+       "4cc42fc1407b008fe350907c092e80ac",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffc00000000000",
+       "08b244ce7cbc8ee97fbba808cb146fda",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffe00000000000",
+       "39b333e8694f21546ad1edd9d87ed95b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffff00000000000",
+       "3b271f8ab2e6e4a20ba8090f43ba78f3",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffff80000000000",
+       "9ad983f3bf651cd0393f0a73cccdea50",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffc0000000000",
+       "8f476cbff75c1f725ce18e4bbcd19b32",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffe0000000000",
+       "905b6267f1d6ab5320835a133f096f2a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffff0000000000",
+       "145b60d6d0193c23f4221848a892d61a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffff8000000000",
+       "55cfb3fb6d75cad0445bbc8dafa25b0f",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffc000000000",
+       "7b8e7098e357ef71237d46d8b075b0f5",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffe000000000",
+       "2bf27229901eb40f2df9d8398d1505ae",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffff000000000",
+       "83a63402a77f9ad5c1e931a931ecd706",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffff800000000",
+       "6f8ba6521152d31f2bada1843e26b973",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffc00000000",
+       "e5c3b8e30fd2d8e6239b17b44bd23bbd",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffe00000000",
+       "1ac1f7102c59933e8b2ddc3f14e94baa",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffff00000000",
+       "21d9ba49f276b45f11af8fc71a088e3d",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffff80000000",
+       "649f1cddc3792b4638635a392bc9bade",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffc0000000",
+       "e2775e4b59c1bc2e31a2078c11b5a08c",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffe0000000",
+       "2be1fae5048a25582a679ca10905eb80",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffff0000000",
+       "da86f292c6f41ea34fb2068df75ecc29",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffff8000000",
+       "220df19f85d69b1b562fa69a3c5beca5",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffc000000",
+       "1f11d5d0355e0b556ccdb6c7f5083b4d",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffe000000",
+       "62526b78be79cb384633c91f83b4151b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffff000000",
+       "90ddbcb950843592dd47bbef00fdc876",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffff800000",
+       "2fd0e41c5b8402277354a7391d2618e2",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffc00000",
+       "3cdf13e72dee4c581bafec70b85f9660",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffe00000",
+       "afa2ffc137577092e2b654fa199d2c43",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffff00000",
+       "8d683ee63e60d208e343ce48dbc44cac",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffff80000",
+       "705a4ef8ba2133729c20185c3d3a4763",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffc0000",
+       "0861a861c3db4e94194211b77ed761b9",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffe0000",
+       "4b00c27e8b26da7eab9d3a88dec8b031",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffff0000",
+       "5f397bf03084820cc8810d52e5b666e9",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffff8000",
+       "63fafabb72c07bfbd3ddc9b1203104b8",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffc000",
+       "683e2140585b18452dd4ffbb93c95df9",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffe000",
+       "286894e48e537f8763b56707d7d155c8",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffff000",
+       "a423deabc173dcf7e2c4c53e77d37cd1",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffff800",
+       "eb8168313e1cfdfdb5e986d5429cf172",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffc00",
+       "27127daafc9accd2fb334ec3eba52323",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffe00",
+       "ee0715b96f72e3f7a22a5064fc592f4c",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffff00",
+       "29ee526770f2a11dcfa989d1ce88830f",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffff80",
+       "0493370e054b09871130fe49af730a5a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffc0",
+       "9b7b940f6c509f9e44a4ee140448ee46",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffe0",
+       "2915be4a1ecfdcbe3e023811a12bb6c7",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffff0",
+       "7240e524bc51d8c4d440b1be55d1062c",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffff8",
+       "da63039d38cb4612b2dc36ba26684b93",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffffc",
+       "0f59cb5a4b522e2ac56c1a64f558ad9a",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "fffffffffffffffffffffffffffffffe",
+       "7bfe9d876c6d63c1d035da8fe21c409d",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "ffffffffffffffffffffffffffffffff",
+       "acdace8078a32b1a182bfa4987ca1347",
+
+       /*
+        * Table end marker.
+        */
+       NULL
+};
+
+/*
+ * AES known-answer tests for CBC. Order: key, IV, plaintext, ciphertext.
+ */
+static const char *const KAT_AES_CBC[] = {
+       /*
+        * From NIST validation suite "Multiblock Message Test"
+        * (cbcmmt128.rsp).
+        */
+       "1f8e4973953f3fb0bd6b16662e9a3c17",
+       "2fe2b333ceda8f98f4a99b40d2cd34a8",
+       "45cf12964fc824ab76616ae2f4bf0822",
+       "0f61c4d44c5147c03c195ad7e2cc12b2",
+
+       "0700d603a1c514e46b6191ba430a3a0c",
+       "aad1583cd91365e3bb2f0c3430d065bb",
+       "068b25c7bfb1f8bdd4cfc908f69dffc5ddc726a197f0e5f720f730393279be91",
+       "c4dc61d9725967a3020104a9738f23868527ce839aab1752fd8bdb95a82c4d00",
+
+       "3348aa51e9a45c2dbe33ccc47f96e8de",
+       "19153c673160df2b1d38c28060e59b96",
+       "9b7cee827a26575afdbb7c7a329f887238052e3601a7917456ba61251c214763d5e1847a6ad5d54127a399ab07ee3599",
+       "d5aed6c9622ec451a15db12819952b6752501cf05cdbf8cda34a457726ded97818e1f127a28d72db5652749f0c6afee5",
+
+       "b7f3c9576e12dd0db63e8f8fac2b9a39",
+       "c80f095d8bb1a060699f7c19974a1aa0",
+       "9ac19954ce1319b354d3220460f71c1e373f1cd336240881160cfde46ebfed2e791e8d5a1a136ebd1dc469dec00c4187722b841cdabcb22c1be8a14657da200e",
+       "19b9609772c63f338608bf6eb52ca10be65097f89c1e0905c42401fd47791ae2c5440b2d473116ca78bd9ff2fb6015cfd316524eae7dcb95ae738ebeae84a467",
+
+       "b6f9afbfe5a1562bba1368fc72ac9d9c",
+       "3f9d5ebe250ee7ce384b0d00ee849322",
+       "db397ec22718dbffb9c9d13de0efcd4611bf792be4fce0dc5f25d4f577ed8cdbd4eb9208d593dda3d4653954ab64f05676caa3ce9bfa795b08b67ceebc923fdc89a8c431188e9e482d8553982cf304d1",
+       "10ea27b19e16b93af169c4a88e06e35c99d8b420980b058e34b4b8f132b13766f72728202b089f428fecdb41c79f8aa0d0ef68f5786481cca29e2126f69bc14160f1ae2187878ba5c49cf3961e1b7ee9",
+
+       "bbe7b7ba07124ff1ae7c3416fe8b465e",
+       "7f65b5ee3630bed6b84202d97fb97a1e",
+       "2aad0c2c4306568bad7447460fd3dac054346d26feddbc9abd9110914011b4794be2a9a00a519a51a5b5124014f4ed2735480db21b434e99a911bb0b60fe0253763725b628d5739a5117b7ee3aefafc5b4c1bf446467e7bf5f78f31ff7caf187",
+       "3b8611bfc4973c5cd8e982b073b33184cd26110159172e44988eb5ff5661a1e16fad67258fcbfee55469267a12dc374893b4e3533d36f5634c3095583596f135aa8cd1138dc898bc5651ee35a92ebf89ab6aeb5366653bc60a70e0074fc11efe",
+
+       "89a553730433f7e6d67d16d373bd5360",
+       "f724558db3433a523f4e51a5bea70497",
+       "807bc4ea684eedcfdcca30180680b0f1ae2814f35f36d053c5aea6595a386c1442770f4d7297d8b91825ee7237241da8925dd594ccf676aecd46ca2068e8d37a3a0ec8a7d5185a201e663b5ff36ae197110188a23503763b8218826d23ced74b31e9f6e2d7fbfa6cb43420c7807a8625",
+       "406af1429a478c3d07e555c5287a60500d37fc39b68e5bbb9bafd6ddb223828561d6171a308d5b1a4551e8a5e7d572918d25c968d3871848d2f16635caa9847f38590b1df58ab5efb985f2c66cfaf86f61b3f9c0afad6c963c49cee9b8bc81a2ddb06c967f325515a4849eec37ce721a",
+
+       "c491ca31f91708458e29a925ec558d78",
+       "9ef934946e5cd0ae97bd58532cb49381",
+       "cb6a787e0dec56f9a165957f81af336ca6b40785d9e94093c6190e5152649f882e874d79ac5e167bd2a74ce5ae088d2ee854f6539e0a94796b1e1bd4c9fcdbc79acbef4d01eeb89776d18af71ae2a4fc47dd66df6c4dbe1d1850e466549a47b636bcc7c2b3a62495b56bb67b6d455f1eebd9bfefecbca6c7f335cfce9b45cb9d",
+       "7b2931f5855f717145e00f152a9f4794359b1ffcb3e55f594e33098b51c23a6c74a06c1d94fded7fd2ae42c7db7acaef5844cb33aeddc6852585ed0020a6699d2cb53809cefd169148ce42292afab063443978306c582c18b9ce0da3d084ce4d3c482cfd8fcf1a85084e89fb88b40a084d5e972466d07666126fb761f84078f2",
+
+       "f6e87d71b0104d6eb06a68dc6a71f498",
+       "1c245f26195b76ebebc2edcac412a2f8",
+       "f82bef3c73a6f7f80db285726d691db6bf55eec25a859d3ba0e0445f26b9bb3b16a3161ed1866e4dd8f2e5f8ecb4e46d74a7a78c20cdfc7bcc9e479ba7a0caba9438238ad0c01651d5d98de37f03ddce6e6b4bd4ab03cf9e8ed818aedfa1cf963b932067b97d776dce1087196e7e913f7448e38244509f0caf36bd8217e15336d35c149fd4e41707893fdb84014f8729",
+       "b09512f3eff9ed0d85890983a73dadbb7c3678d52581be64a8a8fc586f490f2521297a478a0598040ebd0f5509fafb0969f9d9e600eaef33b1b93eed99687b167f89a5065aac439ce46f3b8d22d30865e64e45ef8cd30b6984353a844a11c8cd60dba0e8866b3ee30d24b3fa8a643b328353e06010fa8273c8fd54ef0a2b6930e5520aae5cd5902f9b86a33592ca4365",
+
+       "2c14413751c31e2730570ba3361c786b",
+       "1dbbeb2f19abb448af849796244a19d7",
+       "40d930f9a05334d9816fe204999c3f82a03f6a0457a8c475c94553d1d116693adc618049f0a769a2eed6a6cb14c0143ec5cccdbc8dec4ce560cfd206225709326d4de7948e54d603d01b12d7fed752fb23f1aa4494fbb00130e9ded4e77e37c079042d828040c325b1a5efd15fc842e44014ca4374bf38f3c3fc3ee327733b0c8aee1abcd055772f18dc04603f7b2c1ea69ff662361f2be0a171bbdcea1e5d3f",
+       "6be8a12800455a320538853e0cba31bd2d80ea0c85164a4c5c261ae485417d93effe2ebc0d0a0b51d6ea18633d210cf63c0c4ddbc27607f2e81ed9113191ef86d56f3b99be6c415a4150299fb846ce7160b40b63baf1179d19275a2e83698376d28b92548c68e06e6d994e2c1501ed297014e702cdefee2f656447706009614d801de1caaf73f8b7fa56cf1ba94b631933bbe577624380850f117435a0355b2b",
+
+       /*
+        * From NIST validation suite "Multiblock Message Test"
+        * (cbcmmt192.rsp).
+        */
+       "ba75f4d1d9d7cf7f551445d56cc1a8ab2a078e15e049dc2c",
+       "531ce78176401666aa30db94ec4a30eb",
+       "c51fc276774dad94bcdc1d2891ec8668",
+       "70dd95a14ee975e239df36ff4aee1d5d",
+
+       "eab3b19c581aa873e1981c83ab8d83bbf8025111fb2e6b21",
+       "f3d6667e8d4d791e60f7505ba383eb05",
+       "9d4e4cccd1682321856df069e3f1c6fa391a083a9fb02d59db74c14081b3acc4",
+       "51d44779f90d40a80048276c035cb49ca2a47bcb9b9cf7270b9144793787d53f",
+
+       "16c93bb398f1fc0cf6d68fc7a5673cdf431fa147852b4a2d",
+       "eaaeca2e07ddedf562f94df63f0a650f",
+       "c5ce958613bf741718c17444484ebaf1050ddcacb59b9590178cbe69d7ad7919608cb03af13bbe04f3506b718a301ea0",
+       "ed6a50e0c6921d52d6647f75d67b4fd56ace1fedb8b5a6a997b4d131640547d22c5d884a75e6752b5846b5b33a5181f4",
+
+       "067bb17b4df785697eaccf961f98e212cb75e6797ce935cb",
+       "8b59c9209c529ca8391c9fc0ce033c38",
+       "db3785a889b4bd387754da222f0e4c2d2bfe0d79e05bc910fba941beea30f1239eacf0068f4619ec01c368e986fca6b7c58e490579d29611bd10087986eff54f",
+       "d5f5589760bf9c762228fde236de1fa2dd2dad448db3fa9be0c4196efd46a35c84dd1ac77d9db58c95918cb317a6430a08d2fb6a8e8b0f1c9b72c7a344dc349f",
+
+       "0fd39de83e0be77a79c8a4a612e3dd9c8aae2ce35e7a2bf8",
+       "7e1d629b84f93b079be51f9a5f5cb23c",
+       "38fbda37e28fa86d9d83a4345e419dea95d28c7818ff25925db6ac3aedaf0a86154e20a4dfcc5b1b4192895393e5eb5846c88bdbd41ecf7af3104f410eaee470f5d9017ed460475f626953035a13db1f",
+       "edadae2f9a45ff3473e02d904c94d94a30a4d92da4deb6bcb4b0774472694571842039f21c496ef93fd658842c735f8a81fcd0aa578442ab893b18f606aed1bab11f81452dd45e9b56adf2eccf4ea095",
+
+       "e3fecc75f0075a09b383dfd389a3d33cc9b854b3b254c0f4",
+       "36eab883afef936cc38f63284619cd19",
+       "931b2f5f3a5820d53a6beaaa6431083a3488f4eb03b0f5b57ef838e1579623103bd6e6800377538b2e51ef708f3c4956432e8a8ee6a34e190642b26ad8bdae6c2af9a6c7996f3b6004d2671e41f1c9f40ee03d1c4a52b0a0654a331f15f34dce",
+       "75395974bd32b3665654a6c8e396b88ae34b123575872a7ab687d8e76b46df911a8a590cd01d2f5c330be3a6626e9dd3aa5e10ed14e8ff829811b6fed50f3f533ca4385a1cbca78f5c4744e50f2f8359165c2485d1324e76c3eae76a0ccac629",
+
+       "f9c27565eb07947c8cb51b79248430f7b1066c3d2fdc3d13",
+       "2bd67cc89ab7948d644a49672843cbd9",
+       "6abcc270173cf114d44847e911a050db57ba7a2e2c161c6f37ccb6aaa4677bddcaf50cad0b5f8758fcf7c0ebc650ceb5cd52cafb8f8dd3edcece55d9f1f08b9fa8f54365cf56e28b9596a7e1dd1d3418e4444a7724add4cf79d527b183ec88de4be4eeff29c80a97e54f85351cb189ee",
+       "ca282924a61187feb40520979106e5cc861957f23828dcb7285e0eaac8a0ca2a6b60503d63d6039f4693dba32fa1f73ae2e709ca94911f28a5edd1f30eaddd54680c43acc9c74cd90d8bb648b4e544275f47e514daa20697f66c738eb30337f017fca1a26da4d1a0cc0a0e98e2463070",
+
+       "fb09cf9e00dbf883689d079c920077c0073c31890b55bab5",
+       "e3c89bd097c3abddf64f4881db6dbfe2",
+       "c1a37683fb289467dd1b2c89efba16bbd2ee24cf18d19d44596ded2682c79a2f711c7a32bf6a24badd32a4ee637c73b7a41da6258635650f91fb9ffa45bdfc3cb122136241b3deced8996aa51ea8d3e81c9d70e006a44bc0571ed48623a0d622a93fa9da290baaedf5d9e876c94620945ff8ecc83f27379ed55cf490c5790f27",
+       "8158e21420f25b59d6ae943fa1cbf21f02e979f419dab0126a721b7eef55bee9ad97f5ccff7d239057bbc19a8c378142f7672f1d5e7e17d7bebcb0070e8355cace6660171a53b61816ae824a6ef69ce470b6ffd3b5bb4b438874d91d27854d3b6f25860d3868958de3307d62b1339bdddb8a318c0ce0f33c17caf0e9f6040820",
+
+       "bca6fa3c67fd294e958f66fe8bd64f45f428f5bc8e9733a7",
+       "92a47f2833f1450d1da41717bdc6e83c",
+       "5becbc31d8bead6d36ae014a5863d14a431e6b55d29ea6baaa417271716db3a33b2e506b452086dfe690834ac2de30bc41254ec5401ec47d064237c7792fdcd7914d8af20eb114756642d519021a8c75a92f6bc53d326ae9a5b7e1b10a9756574692934d9939fc399e0c203f7edf8e7e6482eadd31a0400770e897b48c6bca2b404593045080e93377358c42a0f4dede",
+       "926db248cc1ba20f0c57631a7c8aef094f791937b905949e3460240e8bfa6fa483115a1b310b6e4369caebc5262888377b1ddaa5800ea496a2bdff0f9a1031e7129c9a20e35621e7f0b8baca0d87030f2ae7ca8593c8599677a06fd4b26009ead08fecac24caa9cf2cad3b470c8227415a7b1e0f2eab3fad96d70a209c8bb26c627677e2531b9435ca6e3c444d195b5f",
+
+       "162ad50ee64a0702aa551f571dedc16b2c1b6a1e4d4b5eee",
+       "24408038161a2ccae07b029bb66355c1",
+       "be8abf00901363987a82cc77d0ec91697ba3857f9e4f84bd79406c138d02698f003276d0449120bef4578d78fecabe8e070e11710b3f0a2744bd52434ec70015884c181ebdfd51c604a71c52e4c0e110bc408cd462b248a80b8a8ac06bb952ac1d7faed144807f1a731b7febcaf7835762defe92eccfc7a9944e1c702cffe6bc86733ed321423121085ac02df8962bcbc1937092eebf0e90a8b20e3dd8c244ae",
+       "c82cf2c476dea8cb6a6e607a40d2f0391be82ea9ec84a537a6820f9afb997b76397d005424faa6a74dc4e8c7aa4a8900690f894b6d1dca80675393d2243adac762f159301e357e98b724762310cd5a7bafe1c2a030dba46fd93a9fdb89cc132ca9c17dc72031ec6822ee5a9d99dbca66c784c01b0885cbb62e29d97801927ec415a5d215158d325f9ee689437ad1b7684ad33c0d92739451ac87f39ff8c31b84",
+
+       /*
+        * From NIST validation suite "Multiblock Message Test"
+        * (cbcmmt256.rsp).
+        */
+       "6ed76d2d97c69fd1339589523931f2a6cff554b15f738f21ec72dd97a7330907",
+       "851e8764776e6796aab722dbb644ace8",
+       "6282b8c05c5c1530b97d4816ca434762",
+       "6acc04142e100a65f51b97adf5172c41",
+
+       "dce26c6b4cfb286510da4eecd2cffe6cdf430f33db9b5f77b460679bd49d13ae",
+       "fdeaa134c8d7379d457175fd1a57d3fc",
+       "50e9eee1ac528009e8cbcd356975881f957254b13f91d7c6662d10312052eb00",
+       "2fa0df722a9fd3b64cb18fb2b3db55ff2267422757289413f8f657507412a64c",
+
+       "fe8901fecd3ccd2ec5fdc7c7a0b50519c245b42d611a5ef9e90268d59f3edf33",
+       "bd416cb3b9892228d8f1df575692e4d0",
+       "8d3aa196ec3d7c9b5bb122e7fe77fb1295a6da75abe5d3a510194d3a8a4157d5c89d40619716619859da3ec9b247ced9",
+       "608e82c7ab04007adb22e389a44797fed7de090c8c03ca8a2c5acd9e84df37fbc58ce8edb293e98f02b640d6d1d72464",
+
+       "0493ff637108af6a5b8e90ac1fdf035a3d4bafd1afb573be7ade9e8682e663e5",
+       "c0cd2bebccbb6c49920bd5482ac756e8",
+       "8b37f9148df4bb25956be6310c73c8dc58ea9714ff49b643107b34c9bff096a94fedd6823526abc27a8e0b16616eee254ab4567dd68e8ccd4c38ac563b13639c",
+       "05d5c77729421b08b737e41119fa4438d1f570cc772a4d6c3df7ffeda0384ef84288ce37fc4c4c7d1125a499b051364c389fd639bdda647daa3bdadab2eb5594",
+
+       "9adc8fbd506e032af7fa20cf5343719de6d1288c158c63d6878aaf64ce26ca85",
+       "11958dc6ab81e1c7f01631e9944e620f",
+       "c7917f84f747cd8c4b4fedc2219bdbc5f4d07588389d8248854cf2c2f89667a2d7bcf53e73d32684535f42318e24cd45793950b3825e5d5c5c8fcd3e5dda4ce9246d18337ef3052d8b21c5561c8b660e",
+       "9c99e68236bb2e929db1089c7750f1b356d39ab9d0c40c3e2f05108ae9d0c30b04832ccdbdc08ebfa426b7f5efde986ed05784ce368193bb3699bc691065ac62e258b9aa4cc557e2b45b49ce05511e65",
+
+       "73b8faf00b3302ac99855cf6f9e9e48518690a5906a4869d4dcf48d282faae2a",
+       "b3cb97a80a539912b8c21f450d3b9395",
+       "3adea6e06e42c4f041021491f2775ef6378cb08824165edc4f6448e232175b60d0345b9f9c78df6596ec9d22b7b9e76e8f3c76b32d5d67273f1d83fe7a6fc3dd3c49139170fa5701b3beac61b490f0a9e13f844640c4500f9ad3087adfb0ae10",
+       "ac3d6dbafe2e0f740632fd9e820bf6044cd5b1551cbb9cc03c0b25c39ccb7f33b83aacfca40a3265f2bbff879153448acacb88fcfb3bb7b10fe463a68c0109f028382e3e557b1adf02ed648ab6bb895df0205d26ebbfa9a5fd8cebd8e4bee3dc",
+
+       "9ddf3745896504ff360a51a3eb49c01b79fccebc71c3abcb94a949408b05b2c9",
+       "e79026639d4aa230b5ccffb0b29d79bc",
+       "cf52e5c3954c51b94c9e38acb8c9a7c76aebdaa9943eae0a1ce155a2efdb4d46985d935511471452d9ee64d2461cb2991d59fc0060697f9a671672163230f367fed1422316e52d29eceacb8768f56d9b80f6d278093c9a8acd3cfd7edd8ebd5c293859f64d2f8486ae1bd593c65bc014",
+       "34df561bd2cfebbcb7af3b4b8d21ca5258312e7e2e4e538e35ad2490b6112f0d7f148f6aa8d522a7f3c61d785bd667db0e1dc4606c318ea4f26af4fe7d11d4dcff0456511b4aed1a0d91ba4a1fd6cd9029187bc5881a5a07fe02049d39368e83139b12825bae2c7be81e6f12c61bb5c5",
+
+       "458b67bf212d20f3a57fce392065582dcefbf381aa22949f8338ab9052260e1d",
+       "4c12effc5963d40459602675153e9649",
+       "256fd73ce35ae3ea9c25dd2a9454493e96d8633fe633b56176dce8785ce5dbbb84dbf2c8a2eeb1e96b51899605e4f13bbc11b93bf6f39b3469be14858b5b720d4a522d36feed7a329c9b1e852c9280c47db8039c17c4921571a07d1864128330e09c308ddea1694e95c84500f1a61e614197e86a30ecc28df64ccb3ccf5437aa",
+       "90b7b9630a2378f53f501ab7beff039155008071bc8438e789932cfd3eb1299195465e6633849463fdb44375278e2fdb1310821e6492cf80ff15cb772509fb426f3aeee27bd4938882fd2ae6b5bd9d91fa4a43b17bb439ebbe59c042310163a82a5fe5388796eee35a181a1271f00be29b852d8fa759bad01ff4678f010594cd",
+
+       "d2412db0845d84e5732b8bbd642957473b81fb99ca8bff70e7920d16c1dbec89",
+       "51c619fcf0b23f0c7925f400a6cacb6d",
+       "026006c4a71a180c9929824d9d095b8faaa86fc4fa25ecac61d85ff6de92dfa8702688c02a282c1b8af4449707f22d75e91991015db22374c95f8f195d5bb0afeb03040ff8965e0e1339dba5653e174f8aa5a1b39fe3ac839ce307a4e44b4f8f1b0063f738ec18acdbff2ebfe07383e734558723e741f0a1836dafdf9de82210a9248bc113b3c1bc8b4e252ca01bd803",
+       "0254b23463bcabec5a395eb74c8fb0eb137a07bc6f5e9f61ec0b057de305714f8fa294221c91a159c315939b81e300ee902192ec5f15254428d8772f79324ec43298ca21c00b370273ee5e5ed90e43efa1e05a5d171209fe34f9f29237dba2a6726650fd3b1321747d1208863c6c3c6b3e2d879ab5f25782f08ba8f2abbe63e0bedb4a227e81afb36bb6645508356d34",
+
+       "48be597e632c16772324c8d3fa1d9c5a9ecd010f14ec5d110d3bfec376c5532b",
+       "d6d581b8cf04ebd3b6eaa1b53f047ee1",
+       "0c63d413d3864570e70bb6618bf8a4b9585586688c32bba0a5ecc1362fada74ada32c52acfd1aa7444ba567b4e7daaecf7cc1cb29182af164ae5232b002868695635599807a9a7f07a1f137e97b1e1c9dabc89b6a5e4afa9db5855edaa575056a8f4f8242216242bb0c256310d9d329826ac353d715fa39f80cec144d6424558f9f70b98c920096e0f2c855d594885a00625880e9dfb734163cecef72cf030b8",
+       "fc5873e50de8faf4c6b84ba707b0854e9db9ab2e9f7d707fbba338c6843a18fc6facebaf663d26296fb329b4d26f18494c79e09e779647f9bafa87489630d79f4301610c2300c19dbf3148b7cac8c4f4944102754f332e92b6f7c5e75bc6179eb877a078d4719009021744c14f13fd2a55a2b9c44d18000685a845a4f632c7c56a77306efa66a24d05d088dcd7c13fe24fc447275965db9e4d37fbc9304448cd",
+
+       /*
+        * End-of-table marker.
+        */
+       NULL
+};
+
+/*
+ * AES known-answer tests for CTR. Order: key, IV, plaintext, ciphertext.
+ */
+static const char *const KAT_AES_CTR[] = {
+       /*
+        * From RFC 3686.
+        */
+       "ae6852f8121067cc4bf7a5765577f39e",
+       "000000300000000000000000",
+       "53696e676c6520626c6f636b206d7367",
+       "e4095d4fb7a7b3792d6175a3261311b8",
+
+       "7e24067817fae0d743d6ce1f32539163",
+       "006cb6dbc0543b59da48d90b",
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+       "5104a106168a72d9790d41ee8edad388eb2e1efc46da57c8fce630df9141be28",
+
+       "7691be035e5020a8ac6e618529f9a0dc",
+       "00e0017b27777f3f4a1786f0",
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223",
+       "c1cf48a89f2ffdd9cf4652e9efdb72d74540a42bde6d7836d59a5ceaaef3105325b2072f",
+
+       "16af5b145fc9f579c175f93e3bfb0eed863d06ccfdb78515",
+       "0000004836733c147d6d93cb",
+       "53696e676c6520626c6f636b206d7367",
+       "4b55384fe259c9c84e7935a003cbe928",
+
+       "7c5cb2401b3dc33c19e7340819e0f69c678c3db8e6f6a91a",
+       "0096b03b020c6eadc2cb500d",
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+       "453243fc609b23327edfaafa7131cd9f8490701c5ad4a79cfc1fe0ff42f4fb00",
+
+       "02bf391ee8ecb159b959617b0965279bf59b60a786d3e0fe",
+       "0007bdfd5cbd60278dcc0912",
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223",
+       "96893fc55e5c722f540b7dd1ddf7e758d288bc95c69165884536c811662f2188abee0935",
+
+       "776beff2851db06f4c8a0542c8696f6c6a81af1eec96b4d37fc1d689e6c1c104",
+       "00000060db5672c97aa8f0b2",
+       "53696e676c6520626c6f636b206d7367",
+       "145ad01dbf824ec7560863dc71e3e0c0",
+
+       "f6d66d6bd52d59bb0796365879eff886c66dd51a5b6a99744b50590c87a23884",
+       "00faac24c1585ef15a43d875",
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+       "f05e231b3894612c49ee000b804eb2a9b8306b508f839d6a5530831d9344af1c",
+
+       "ff7a617ce69148e4f1726e2f43581de2aa62d9f805532edff1eed687fb54153d",
+       "001cc5b751a51d70a1c11148",
+       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223",
+       "eb6c52821d0bbbf7ce7594462aca4faab407df866569fd07f48cc0b583d6071f1ec0e6b8",
+
+       /*
+        * End-of-table marker.
+        */
+       NULL
+};
+
+static void
+monte_carlo_AES_encrypt(const br_block_cbcenc_class *ve,
+       char *skey, char *splain, char *scipher)
+{
+       unsigned char key[32];
+       unsigned char buf[16];
+       unsigned char pbuf[16];
+       unsigned char cipher[16];
+       size_t key_len;
+       int i, j, k;
+       br_aes_gen_cbcenc_keys v_ec;
+       const br_block_cbcenc_class **ec;
+
+       ec = &v_ec.vtable;
+       key_len = hextobin(key, skey);
+       hextobin(buf, splain);
+       hextobin(cipher, scipher);
+       for (i = 0; i < 100; i ++) {
+               ve->init(ec, key, key_len);
+               for (j = 0; j < 1000; j ++) {
+                       unsigned char iv[16];
+
+                       memcpy(pbuf, buf, sizeof buf);
+                       memset(iv, 0, sizeof iv);
+                       ve->run(ec, iv, buf, sizeof buf);
+               }
+               switch (key_len) {
+               case 16:
+                       for (k = 0; k < 16; k ++) {
+                               key[k] ^= buf[k];
+                       }
+                       break;
+               case 24:
+                       for (k = 0; k < 8; k ++) {
+                               key[k] ^= pbuf[8 + k];
+                       }
+                       for (k = 0; k < 16; k ++) {
+                               key[8 + k] ^= buf[k];
+                       }
+                       break;
+               default:
+                       for (k = 0; k < 16; k ++) {
+                               key[k] ^= pbuf[k];
+                               key[16 + k] ^= buf[k];
+                       }
+                       break;
+               }
+               printf(".");
+               fflush(stdout);
+       }
+       printf(" ");
+       fflush(stdout);
+       check_equals("MC AES encrypt", buf, cipher, sizeof buf);
+}
+
+static void
+monte_carlo_AES_decrypt(const br_block_cbcdec_class *vd,
+       char *skey, char *scipher, char *splain)
+{
+       unsigned char key[32];
+       unsigned char buf[16];
+       unsigned char pbuf[16];
+       unsigned char plain[16];
+       size_t key_len;
+       int i, j, k;
+       br_aes_gen_cbcdec_keys v_dc;
+       const br_block_cbcdec_class **dc;
+
+       dc = &v_dc.vtable;
+       key_len = hextobin(key, skey);
+       hextobin(buf, scipher);
+       hextobin(plain, splain);
+       for (i = 0; i < 100; i ++) {
+               vd->init(dc, key, key_len);
+               for (j = 0; j < 1000; j ++) {
+                       unsigned char iv[16];
+
+                       memcpy(pbuf, buf, sizeof buf);
+                       memset(iv, 0, sizeof iv);
+                       vd->run(dc, iv, buf, sizeof buf);
+               }
+               switch (key_len) {
+               case 16:
+                       for (k = 0; k < 16; k ++) {
+                               key[k] ^= buf[k];
+                       }
+                       break;
+               case 24:
+                       for (k = 0; k < 8; k ++) {
+                               key[k] ^= pbuf[8 + k];
+                       }
+                       for (k = 0; k < 16; k ++) {
+                               key[8 + k] ^= buf[k];
+                       }
+                       break;
+               default:
+                       for (k = 0; k < 16; k ++) {
+                               key[k] ^= pbuf[k];
+                               key[16 + k] ^= buf[k];
+                       }
+                       break;
+               }
+               printf(".");
+               fflush(stdout);
+       }
+       printf(" ");
+       fflush(stdout);
+       check_equals("MC AES decrypt", buf, plain, sizeof buf);
+}
+
+static void
+test_AES_generic(char *name,
+       const br_block_cbcenc_class *ve,
+       const br_block_cbcdec_class *vd,
+       const br_block_ctr_class *vc,
+       int with_MC, int with_CBC)
+{
+       size_t u;
+
+       printf("Test %s: ", name);
+       fflush(stdout);
+
+       if (ve->block_size != 16 || vd->block_size != 16
+               || ve->log_block_size != 4 || vd->log_block_size != 4)
+       {
+               fprintf(stderr, "%s failed: wrong block size\n", name);
+               exit(EXIT_FAILURE);
+       }
+
+       for (u = 0; KAT_AES[u]; u += 3) {
+               unsigned char key[32];
+               unsigned char plain[16];
+               unsigned char cipher[16];
+               unsigned char buf[16];
+               unsigned char iv[16];
+               size_t key_len;
+               br_aes_gen_cbcenc_keys v_ec;
+               br_aes_gen_cbcdec_keys v_dc;
+               const br_block_cbcenc_class **ec;
+               const br_block_cbcdec_class **dc;
+
+               ec = &v_ec.vtable;
+               dc = &v_dc.vtable;
+               key_len = hextobin(key, KAT_AES[u]);
+               hextobin(plain, KAT_AES[u + 1]);
+               hextobin(cipher, KAT_AES[u + 2]);
+               ve->init(ec, key, key_len);
+               memcpy(buf, plain, sizeof plain);
+               memset(iv, 0, sizeof iv);
+               ve->run(ec, iv, buf, sizeof buf);
+               check_equals("KAT AES encrypt", buf, cipher, sizeof cipher);
+               vd->init(dc, key, key_len);
+               memset(iv, 0, sizeof iv);
+               vd->run(dc, iv, buf, sizeof buf);
+               check_equals("KAT AES decrypt", buf, plain, sizeof plain);
+       }
+
+       if (with_CBC) {
+               for (u = 0; KAT_AES_CBC[u]; u += 4) {
+                       unsigned char key[32];
+                       unsigned char ivref[16];
+                       unsigned char plain[200];
+                       unsigned char cipher[200];
+                       unsigned char buf[200];
+                       unsigned char iv[16];
+                       size_t key_len, data_len, v;
+                       br_aes_gen_cbcenc_keys v_ec;
+                       br_aes_gen_cbcdec_keys v_dc;
+                       const br_block_cbcenc_class **ec;
+                       const br_block_cbcdec_class **dc;
+
+                       ec = &v_ec.vtable;
+                       dc = &v_dc.vtable;
+                       key_len = hextobin(key, KAT_AES_CBC[u]);
+                       hextobin(ivref, KAT_AES_CBC[u + 1]);
+                       data_len = hextobin(plain, KAT_AES_CBC[u + 2]);
+                       hextobin(cipher, KAT_AES_CBC[u + 3]);
+                       ve->init(ec, key, key_len);
+
+                       memcpy(buf, plain, data_len);
+                       memcpy(iv, ivref, 16);
+                       ve->run(ec, iv, buf, data_len);
+                       check_equals("KAT CBC AES encrypt",
+                               buf, cipher, data_len);
+                       vd->init(dc, key, key_len);
+                       memcpy(iv, ivref, 16);
+                       vd->run(dc, iv, buf, data_len);
+                       check_equals("KAT CBC AES decrypt",
+                               buf, plain, data_len);
+
+                       memcpy(buf, plain, data_len);
+                       memcpy(iv, ivref, 16);
+                       for (v = 0; v < data_len; v += 16) {
+                               ve->run(ec, iv, buf + v, 16);
+                       }
+                       check_equals("KAT CBC AES encrypt (2)",
+                               buf, cipher, data_len);
+                       memcpy(iv, ivref, 16);
+                       for (v = 0; v < data_len; v += 16) {
+                               vd->run(dc, iv, buf + v, 16);
+                       }
+                       check_equals("KAT CBC AES decrypt (2)",
+                               buf, plain, data_len);
+               }
+       }
+
+       if (vc != NULL) {
+               if (vc->block_size != 16 || vc->log_block_size != 4) {
+                       fprintf(stderr, "%s failed: wrong block size\n", name);
+                       exit(EXIT_FAILURE);
+               }
+               for (u = 0; KAT_AES_CTR[u]; u += 4) {
+                       unsigned char key[32];
+                       unsigned char iv[12];
+                       unsigned char plain[200];
+                       unsigned char cipher[200];
+                       unsigned char buf[200];
+                       size_t key_len, data_len, v;
+                       uint32_t c;
+                       br_aes_gen_ctr_keys v_xc;
+                       const br_block_ctr_class **xc;
+
+                       xc = &v_xc.vtable;
+                       key_len = hextobin(key, KAT_AES_CTR[u]);
+                       hextobin(iv, KAT_AES_CTR[u + 1]);
+                       data_len = hextobin(plain, KAT_AES_CTR[u + 2]);
+                       hextobin(cipher, KAT_AES_CTR[u + 3]);
+                       vc->init(xc, key, key_len);
+
+                       memcpy(buf, plain, data_len);
+                       vc->run(xc, iv, 1, buf, data_len);
+                       check_equals("KAT CTR AES (1)", buf, cipher, data_len);
+                       vc->run(xc, iv, 1, buf, data_len);
+                       check_equals("KAT CTR AES (2)", buf, plain, data_len);
+
+                       memcpy(buf, plain, data_len);
+                       c = 1;
+                       for (v = 0; v < data_len; v += 32) {
+                               size_t clen;
+
+                               clen = data_len - v;
+                               if (clen > 32) {
+                                       clen = 32;
+                               }
+                               c = vc->run(xc, iv, c, buf + v, clen);
+                       }
+                       check_equals("KAT CTR AES (3)", buf, cipher, data_len);
+
+                       memcpy(buf, plain, data_len);
+                       c = 1;
+                       for (v = 0; v < data_len; v += 16) {
+                               size_t clen;
+
+                               clen = data_len - v;
+                               if (clen > 16) {
+                                       clen = 16;
+                               }
+                               c = vc->run(xc, iv, c, buf + v, clen);
+                       }
+                       check_equals("KAT CTR AES (4)", buf, cipher, data_len);
+               }
+       }
+
+       if (with_MC) {
+               monte_carlo_AES_encrypt(
+                       ve,
+                       "139a35422f1d61de3c91787fe0507afd",
+                       "b9145a768b7dc489a096b546f43b231f",
+                       "fb2649694783b551eacd9d5db6126d47");
+               monte_carlo_AES_decrypt(
+                       vd,
+                       "0c60e7bf20ada9baa9e1ddf0d1540726",
+                       "b08a29b11a500ea3aca42c36675b9785",
+                       "d1d2bfdc58ffcad2341b095bce55221e");
+
+               monte_carlo_AES_encrypt(
+                       ve,
+                       "b9a63e09e1dfc42e93a90d9bad739e5967aef672eedd5da9",
+                       "85a1f7a58167b389cddc8a9ff175ee26",
+                       "5d1196da8f184975e240949a25104554");
+               monte_carlo_AES_decrypt(
+                       vd,
+                       "4b97585701c03fbebdfa8555024f589f1482c58a00fdd9fd",
+                       "d0bd0e02ded155e4516be83f42d347a4",
+                       "b63ef1b79507a62eba3dafcec54a6328");
+
+               monte_carlo_AES_encrypt(
+                       ve,
+                       "f9e8389f5b80712e3886cc1fa2d28a3b8c9cd88a2d4a54c6aa86ce0fef944be0",
+                       "b379777f9050e2a818f2940cbbd9aba4",
+                       "c5d2cb3d5b7ff0e23e308967ee074825");
+               monte_carlo_AES_decrypt(
+                       vd,
+                       "2b09ba39b834062b9e93f48373b8dd018dedf1e5ba1b8af831ebbacbc92a2643",
+                       "89649bd0115f30bd878567610223a59d",
+                       "e3d3868f578caf34e36445bf14cefc68");
+       }
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_AES_big(void)
+{
+       test_AES_generic("AES_big",
+               &br_aes_big_cbcenc_vtable,
+               &br_aes_big_cbcdec_vtable,
+               &br_aes_big_ctr_vtable,
+               1, 1);
+}
+
+static void
+test_AES_small(void)
+{
+       test_AES_generic("AES_small",
+               &br_aes_small_cbcenc_vtable,
+               &br_aes_small_cbcdec_vtable,
+               &br_aes_small_ctr_vtable,
+               1, 1);
+}
+
+static void
+test_AES_ct(void)
+{
+       test_AES_generic("AES_ct",
+               &br_aes_ct_cbcenc_vtable,
+               &br_aes_ct_cbcdec_vtable,
+               &br_aes_ct_ctr_vtable,
+               1, 1);
+}
+
+static void
+test_AES_ct64(void)
+{
+       test_AES_generic("AES_ct64",
+               &br_aes_ct64_cbcenc_vtable,
+               &br_aes_ct64_cbcdec_vtable,
+               &br_aes_ct64_ctr_vtable,
+               1, 1);
+}
+
+/*
+ * DES known-answer tests. Order: plaintext, key, ciphertext.
+ * (mostly from NIST SP 800-20).
+ */
+static const char *const KAT_DES[] = {
+       "10316E028C8F3B4A", "0000000000000000", "82DCBAFBDEAB6602",
+       "8000000000000000", "0000000000000000", "95A8D72813DAA94D",
+       "4000000000000000", "0000000000000000", "0EEC1487DD8C26D5",
+       "2000000000000000", "0000000000000000", "7AD16FFB79C45926",
+       "1000000000000000", "0000000000000000", "D3746294CA6A6CF3",
+       "0800000000000000", "0000000000000000", "809F5F873C1FD761",
+       "0400000000000000", "0000000000000000", "C02FAFFEC989D1FC",
+       "0200000000000000", "0000000000000000", "4615AA1D33E72F10",
+       "0100000000000000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0080000000000000", "0000000000000000", "2055123350C00858",
+       "0040000000000000", "0000000000000000", "DF3B99D6577397C8",
+       "0020000000000000", "0000000000000000", "31FE17369B5288C9",
+       "0010000000000000", "0000000000000000", "DFDD3CC64DAE1642",
+       "0008000000000000", "0000000000000000", "178C83CE2B399D94",
+       "0004000000000000", "0000000000000000", "50F636324A9B7F80",
+       "0002000000000000", "0000000000000000", "A8468EE3BC18F06D",
+       "0001000000000000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000800000000000", "0000000000000000", "A2DC9E92FD3CDE92",
+       "0000400000000000", "0000000000000000", "CAC09F797D031287",
+       "0000200000000000", "0000000000000000", "90BA680B22AEB525",
+       "0000100000000000", "0000000000000000", "CE7A24F350E280B6",
+       "0000080000000000", "0000000000000000", "882BFF0AA01A0B87",
+       "0000040000000000", "0000000000000000", "25610288924511C2",
+       "0000020000000000", "0000000000000000", "C71516C29C75D170",
+       "0000010000000000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000008000000000", "0000000000000000", "5199C29A52C9F059",
+       "0000004000000000", "0000000000000000", "C22F0A294A71F29F",
+       "0000002000000000", "0000000000000000", "EE371483714C02EA",
+       "0000001000000000", "0000000000000000", "A81FBD448F9E522F",
+       "0000000800000000", "0000000000000000", "4F644C92E192DFED",
+       "0000000400000000", "0000000000000000", "1AFA9A66A6DF92AE",
+       "0000000200000000", "0000000000000000", "B3C1CC715CB879D8",
+       "0000000100000000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000000080000000", "0000000000000000", "19D032E64AB0BD8B",
+       "0000000040000000", "0000000000000000", "3CFAA7A7DC8720DC",
+       "0000000020000000", "0000000000000000", "B7265F7F447AC6F3",
+       "0000000010000000", "0000000000000000", "9DB73B3C0D163F54",
+       "0000000008000000", "0000000000000000", "8181B65BABF4A975",
+       "0000000004000000", "0000000000000000", "93C9B64042EAA240",
+       "0000000002000000", "0000000000000000", "5570530829705592",
+       "0000000001000000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000000000800000", "0000000000000000", "8638809E878787A0",
+       "0000000000400000", "0000000000000000", "41B9A79AF79AC208",
+       "0000000000200000", "0000000000000000", "7A9BE42F2009A892",
+       "0000000000100000", "0000000000000000", "29038D56BA6D2745",
+       "0000000000080000", "0000000000000000", "5495C6ABF1E5DF51",
+       "0000000000040000", "0000000000000000", "AE13DBD561488933",
+       "0000000000020000", "0000000000000000", "024D1FFA8904E389",
+       "0000000000010000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000000000008000", "0000000000000000", "D1399712F99BF02E",
+       "0000000000004000", "0000000000000000", "14C1D7C1CFFEC79E",
+       "0000000000002000", "0000000000000000", "1DE5279DAE3BED6F",
+       "0000000000001000", "0000000000000000", "E941A33F85501303",
+       "0000000000000800", "0000000000000000", "DA99DBBC9A03F379",
+       "0000000000000400", "0000000000000000", "B7FC92F91D8E92E9",
+       "0000000000000200", "0000000000000000", "AE8E5CAA3CA04E85",
+       "0000000000000100", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000000000000080", "0000000000000000", "9CC62DF43B6EED74",
+       "0000000000000040", "0000000000000000", "D863DBB5C59A91A0",
+       "0000000000000020", "0000000000000000", "A1AB2190545B91D7",
+       "0000000000000010", "0000000000000000", "0875041E64C570F7",
+       "0000000000000008", "0000000000000000", "5A594528BEBEF1CC",
+       "0000000000000004", "0000000000000000", "FCDB3291DE21F0C0",
+       "0000000000000002", "0000000000000000", "869EFD7F9F265A09",
+       "0000000000000001", "0000000000000000", "8CA64DE9C1B123A7",
+       "0000000000000000", "8000000000000000", "95F8A5E5DD31D900",
+       "0000000000000000", "4000000000000000", "DD7F121CA5015619",
+       "0000000000000000", "2000000000000000", "2E8653104F3834EA",
+       "0000000000000000", "1000000000000000", "4BD388FF6CD81D4F",
+       "0000000000000000", "0800000000000000", "20B9E767B2FB1456",
+       "0000000000000000", "0400000000000000", "55579380D77138EF",
+       "0000000000000000", "0200000000000000", "6CC5DEFAAF04512F",
+       "0000000000000000", "0100000000000000", "0D9F279BA5D87260",
+       "0000000000000000", "0080000000000000", "D9031B0271BD5A0A",
+       "0000000000000000", "0040000000000000", "424250B37C3DD951",
+       "0000000000000000", "0020000000000000", "B8061B7ECD9A21E5",
+       "0000000000000000", "0010000000000000", "F15D0F286B65BD28",
+       "0000000000000000", "0008000000000000", "ADD0CC8D6E5DEBA1",
+       "0000000000000000", "0004000000000000", "E6D5F82752AD63D1",
+       "0000000000000000", "0002000000000000", "ECBFE3BD3F591A5E",
+       "0000000000000000", "0001000000000000", "F356834379D165CD",
+       "0000000000000000", "0000800000000000", "2B9F982F20037FA9",
+       "0000000000000000", "0000400000000000", "889DE068A16F0BE6",
+       "0000000000000000", "0000200000000000", "E19E275D846A1298",
+       "0000000000000000", "0000100000000000", "329A8ED523D71AEC",
+       "0000000000000000", "0000080000000000", "E7FCE22557D23C97",
+       "0000000000000000", "0000040000000000", "12A9F5817FF2D65D",
+       "0000000000000000", "0000020000000000", "A484C3AD38DC9C19",
+       "0000000000000000", "0000010000000000", "FBE00A8A1EF8AD72",
+       "0000000000000000", "0000008000000000", "750D079407521363",
+       "0000000000000000", "0000004000000000", "64FEED9C724C2FAF",
+       "0000000000000000", "0000002000000000", "F02B263B328E2B60",
+       "0000000000000000", "0000001000000000", "9D64555A9A10B852",
+       "0000000000000000", "0000000800000000", "D106FF0BED5255D7",
+       "0000000000000000", "0000000400000000", "E1652C6B138C64A5",
+       "0000000000000000", "0000000200000000", "E428581186EC8F46",
+       "0000000000000000", "0000000100000000", "AEB5F5EDE22D1A36",
+       "0000000000000000", "0000000080000000", "E943D7568AEC0C5C",
+       "0000000000000000", "0000000040000000", "DF98C8276F54B04B",
+       "0000000000000000", "0000000020000000", "B160E4680F6C696F",
+       "0000000000000000", "0000000010000000", "FA0752B07D9C4AB8",
+       "0000000000000000", "0000000008000000", "CA3A2B036DBC8502",
+       "0000000000000000", "0000000004000000", "5E0905517BB59BCF",
+       "0000000000000000", "0000000002000000", "814EEB3B91D90726",
+       "0000000000000000", "0000000001000000", "4D49DB1532919C9F",
+       "0000000000000000", "0000000000800000", "25EB5FC3F8CF0621",
+       "0000000000000000", "0000000000400000", "AB6A20C0620D1C6F",
+       "0000000000000000", "0000000000200000", "79E90DBC98F92CCA",
+       "0000000000000000", "0000000000100000", "866ECEDD8072BB0E",
+       "0000000000000000", "0000000000080000", "8B54536F2F3E64A8",
+       "0000000000000000", "0000000000040000", "EA51D3975595B86B",
+       "0000000000000000", "0000000000020000", "CAFFC6AC4542DE31",
+       "0000000000000000", "0000000000010000", "8DD45A2DDF90796C",
+       "0000000000000000", "0000000000008000", "1029D55E880EC2D0",
+       "0000000000000000", "0000000000004000", "5D86CB23639DBEA9",
+       "0000000000000000", "0000000000002000", "1D1CA853AE7C0C5F",
+       "0000000000000000", "0000000000001000", "CE332329248F3228",
+       "0000000000000000", "0000000000000800", "8405D1ABE24FB942",
+       "0000000000000000", "0000000000000400", "E643D78090CA4207",
+       "0000000000000000", "0000000000000200", "48221B9937748A23",
+       "0000000000000000", "0000000000000100", "DD7C0BBD61FAFD54",
+       "0000000000000000", "0000000000000080", "2FBC291A570DB5C4",
+       "0000000000000000", "0000000000000040", "E07C30D7E4E26E12",
+       "0000000000000000", "0000000000000020", "0953E2258E8E90A1",
+       "0000000000000000", "0000000000000010", "5B711BC4CEEBF2EE",
+       "0000000000000000", "0000000000000008", "CC083F1E6D9E85F6",
+       "0000000000000000", "0000000000000004", "D2FD8867D50D2DFE",
+       "0000000000000000", "0000000000000002", "06E7EA22CE92708F",
+       "0000000000000000", "0000000000000001", "166B40B44ABA4BD6",
+       "0000000000000000", "0000000000000000", "8CA64DE9C1B123A7",
+       "0101010101010101", "0101010101010101", "994D4DC157B96C52",
+       "0202020202020202", "0202020202020202", "E127C2B61D98E6E2",
+       "0303030303030303", "0303030303030303", "984C91D78A269CE3",
+       "0404040404040404", "0404040404040404", "1F4570BB77550683",
+       "0505050505050505", "0505050505050505", "3990ABF98D672B16",
+       "0606060606060606", "0606060606060606", "3F5150BBA081D585",
+       "0707070707070707", "0707070707070707", "C65242248C9CF6F2",
+       "0808080808080808", "0808080808080808", "10772D40FAD24257",
+       "0909090909090909", "0909090909090909", "F0139440647A6E7B",
+       "0A0A0A0A0A0A0A0A", "0A0A0A0A0A0A0A0A", "0A288603044D740C",
+       "0B0B0B0B0B0B0B0B", "0B0B0B0B0B0B0B0B", "6359916942F7438F",
+       "0C0C0C0C0C0C0C0C", "0C0C0C0C0C0C0C0C", "934316AE443CF08B",
+       "0D0D0D0D0D0D0D0D", "0D0D0D0D0D0D0D0D", "E3F56D7F1130A2B7",
+       "0E0E0E0E0E0E0E0E", "0E0E0E0E0E0E0E0E", "A2E4705087C6B6B4",
+       "0F0F0F0F0F0F0F0F", "0F0F0F0F0F0F0F0F", "D5D76E09A447E8C3",
+       "1010101010101010", "1010101010101010", "DD7515F2BFC17F85",
+       "1111111111111111", "1111111111111111", "F40379AB9E0EC533",
+       "1212121212121212", "1212121212121212", "96CD27784D1563E5",
+       "1313131313131313", "1313131313131313", "2911CF5E94D33FE1",
+       "1414141414141414", "1414141414141414", "377B7F7CA3E5BBB3",
+       "1515151515151515", "1515151515151515", "701AA63832905A92",
+       "1616161616161616", "1616161616161616", "2006E716C4252D6D",
+       "1717171717171717", "1717171717171717", "452C1197422469F8",
+       "1818181818181818", "1818181818181818", "C33FD1EB49CB64DA",
+       "1919191919191919", "1919191919191919", "7572278F364EB50D",
+       "1A1A1A1A1A1A1A1A", "1A1A1A1A1A1A1A1A", "69E51488403EF4C3",
+       "1B1B1B1B1B1B1B1B", "1B1B1B1B1B1B1B1B", "FF847E0ADF192825",
+       "1C1C1C1C1C1C1C1C", "1C1C1C1C1C1C1C1C", "521B7FB3B41BB791",
+       "1D1D1D1D1D1D1D1D", "1D1D1D1D1D1D1D1D", "26059A6A0F3F6B35",
+       "1E1E1E1E1E1E1E1E", "1E1E1E1E1E1E1E1E", "F24A8D2231C77538",
+       "1F1F1F1F1F1F1F1F", "1F1F1F1F1F1F1F1F", "4FD96EC0D3304EF6",
+       "2020202020202020", "2020202020202020", "18A9D580A900B699",
+       "2121212121212121", "2121212121212121", "88586E1D755B9B5A",
+       "2222222222222222", "2222222222222222", "0F8ADFFB11DC2784",
+       "2323232323232323", "2323232323232323", "2F30446C8312404A",
+       "2424242424242424", "2424242424242424", "0BA03D9E6C196511",
+       "2525252525252525", "2525252525252525", "3E55E997611E4B7D",
+       "2626262626262626", "2626262626262626", "B2522FB5F158F0DF",
+       "2727272727272727", "2727272727272727", "2109425935406AB8",
+       "2828282828282828", "2828282828282828", "11A16028F310FF16",
+       "2929292929292929", "2929292929292929", "73F0C45F379FE67F",
+       "2A2A2A2A2A2A2A2A", "2A2A2A2A2A2A2A2A", "DCAD4338F7523816",
+       "2B2B2B2B2B2B2B2B", "2B2B2B2B2B2B2B2B", "B81634C1CEAB298C",
+       "2C2C2C2C2C2C2C2C", "2C2C2C2C2C2C2C2C", "DD2CCB29B6C4C349",
+       "2D2D2D2D2D2D2D2D", "2D2D2D2D2D2D2D2D", "7D07A77A2ABD50A7",
+       "2E2E2E2E2E2E2E2E", "2E2E2E2E2E2E2E2E", "30C1B0C1FD91D371",
+       "2F2F2F2F2F2F2F2F", "2F2F2F2F2F2F2F2F", "C4427B31AC61973B",
+       "3030303030303030", "3030303030303030", "F47BB46273B15EB5",
+       "3131313131313131", "3131313131313131", "655EA628CF62585F",
+       "3232323232323232", "3232323232323232", "AC978C247863388F",
+       "3333333333333333", "3333333333333333", "0432ED386F2DE328",
+       "3434343434343434", "3434343434343434", "D254014CB986B3C2",
+       "3535353535353535", "3535353535353535", "B256E34BEDB49801",
+       "3636363636363636", "3636363636363636", "37F8759EB77E7BFC",
+       "3737373737373737", "3737373737373737", "5013CA4F62C9CEA0",
+       "3838383838383838", "3838383838383838", "8940F7B3EACA5939",
+       "3939393939393939", "3939393939393939", "E22B19A55086774B",
+       "3A3A3A3A3A3A3A3A", "3A3A3A3A3A3A3A3A", "B04A2AAC925ABB0B",
+       "3B3B3B3B3B3B3B3B", "3B3B3B3B3B3B3B3B", "8D250D58361597FC",
+       "3C3C3C3C3C3C3C3C", "3C3C3C3C3C3C3C3C", "51F0114FB6A6CD37",
+       "3D3D3D3D3D3D3D3D", "3D3D3D3D3D3D3D3D", "9D0BB4DB830ECB73",
+       "3E3E3E3E3E3E3E3E", "3E3E3E3E3E3E3E3E", "E96089D6368F3E1A",
+       "3F3F3F3F3F3F3F3F", "3F3F3F3F3F3F3F3F", "5C4CA877A4E1E92D",
+       "4040404040404040", "4040404040404040", "6D55DDBC8DEA95FF",
+       "4141414141414141", "4141414141414141", "19DF84AC95551003",
+       "4242424242424242", "4242424242424242", "724E7332696D08A7",
+       "4343434343434343", "4343434343434343", "B91810B8CDC58FE2",
+       "4444444444444444", "4444444444444444", "06E23526EDCCD0C4",
+       "4545454545454545", "4545454545454545", "EF52491D5468D441",
+       "4646464646464646", "4646464646464646", "48019C59E39B90C5",
+       "4747474747474747", "4747474747474747", "0544083FB902D8C0",
+       "4848484848484848", "4848484848484848", "63B15CADA668CE12",
+       "4949494949494949", "4949494949494949", "EACC0C1264171071",
+       "4A4A4A4A4A4A4A4A", "4A4A4A4A4A4A4A4A", "9D2B8C0AC605F274",
+       "4B4B4B4B4B4B4B4B", "4B4B4B4B4B4B4B4B", "C90F2F4C98A8FB2A",
+       "4C4C4C4C4C4C4C4C", "4C4C4C4C4C4C4C4C", "03481B4828FD1D04",
+       "4D4D4D4D4D4D4D4D", "4D4D4D4D4D4D4D4D", "C78FC45A1DCEA2E2",
+       "4E4E4E4E4E4E4E4E", "4E4E4E4E4E4E4E4E", "DB96D88C3460D801",
+       "4F4F4F4F4F4F4F4F", "4F4F4F4F4F4F4F4F", "6C69E720F5105518",
+       "5050505050505050", "5050505050505050", "0D262E418BC893F3",
+       "5151515151515151", "5151515151515151", "6AD84FD7848A0A5C",
+       "5252525252525252", "5252525252525252", "C365CB35B34B6114",
+       "5353535353535353", "5353535353535353", "1155392E877F42A9",
+       "5454545454545454", "5454545454545454", "531BE5F9405DA715",
+       "5555555555555555", "5555555555555555", "3BCDD41E6165A5E8",
+       "5656565656565656", "5656565656565656", "2B1FF5610A19270C",
+       "5757575757575757", "5757575757575757", "D90772CF3F047CFD",
+       "5858585858585858", "5858585858585858", "1BEA27FFB72457B7",
+       "5959595959595959", "5959595959595959", "85C3E0C429F34C27",
+       "5A5A5A5A5A5A5A5A", "5A5A5A5A5A5A5A5A", "F9038021E37C7618",
+       "5B5B5B5B5B5B5B5B", "5B5B5B5B5B5B5B5B", "35BC6FF838DBA32F",
+       "5C5C5C5C5C5C5C5C", "5C5C5C5C5C5C5C5C", "4927ACC8CE45ECE7",
+       "5D5D5D5D5D5D5D5D", "5D5D5D5D5D5D5D5D", "E812EE6E3572985C",
+       "5E5E5E5E5E5E5E5E", "5E5E5E5E5E5E5E5E", "9BB93A89627BF65F",
+       "5F5F5F5F5F5F5F5F", "5F5F5F5F5F5F5F5F", "EF12476884CB74CA",
+       "6060606060606060", "6060606060606060", "1BF17E00C09E7CBF",
+       "6161616161616161", "6161616161616161", "29932350C098DB5D",
+       "6262626262626262", "6262626262626262", "B476E6499842AC54",
+       "6363636363636363", "6363636363636363", "5C662C29C1E96056",
+       "6464646464646464", "6464646464646464", "3AF1703D76442789",
+       "6565656565656565", "6565656565656565", "86405D9B425A8C8C",
+       "6666666666666666", "6666666666666666", "EBBF4810619C2C55",
+       "6767676767676767", "6767676767676767", "F8D1CD7367B21B5D",
+       "6868686868686868", "6868686868686868", "9EE703142BF8D7E2",
+       "6969696969696969", "6969696969696969", "5FDFFFC3AAAB0CB3",
+       "6A6A6A6A6A6A6A6A", "6A6A6A6A6A6A6A6A", "26C940AB13574231",
+       "6B6B6B6B6B6B6B6B", "6B6B6B6B6B6B6B6B", "1E2DC77E36A84693",
+       "6C6C6C6C6C6C6C6C", "6C6C6C6C6C6C6C6C", "0F4FF4D9BC7E2244",
+       "6D6D6D6D6D6D6D6D", "6D6D6D6D6D6D6D6D", "A4C9A0D04D3280CD",
+       "6E6E6E6E6E6E6E6E", "6E6E6E6E6E6E6E6E", "9FAF2C96FE84919D",
+       "6F6F6F6F6F6F6F6F", "6F6F6F6F6F6F6F6F", "115DBC965E6096C8",
+       "7070707070707070", "7070707070707070", "AF531E9520994017",
+       "7171717171717171", "7171717171717171", "B971ADE70E5C89EE",
+       "7272727272727272", "7272727272727272", "415D81C86AF9C376",
+       "7373737373737373", "7373737373737373", "8DFB864FDB3C6811",
+       "7474747474747474", "7474747474747474", "10B1C170E3398F91",
+       "7575757575757575", "7575757575757575", "CFEF7A1C0218DB1E",
+       "7676767676767676", "7676767676767676", "DBAC30A2A40B1B9C",
+       "7777777777777777", "7777777777777777", "89D3BF37052162E9",
+       "7878787878787878", "7878787878787878", "80D9230BDAEB67DC",
+       "7979797979797979", "7979797979797979", "3440911019AD68D7",
+       "7A7A7A7A7A7A7A7A", "7A7A7A7A7A7A7A7A", "9626FE57596E199E",
+       "7B7B7B7B7B7B7B7B", "7B7B7B7B7B7B7B7B", "DEA0B796624BB5BA",
+       "7C7C7C7C7C7C7C7C", "7C7C7C7C7C7C7C7C", "E9E40542BDDB3E9D",
+       "7D7D7D7D7D7D7D7D", "7D7D7D7D7D7D7D7D", "8AD99914B354B911",
+       "7E7E7E7E7E7E7E7E", "7E7E7E7E7E7E7E7E", "6F85B98DD12CB13B",
+       "7F7F7F7F7F7F7F7F", "7F7F7F7F7F7F7F7F", "10130DA3C3A23924",
+       "8080808080808080", "8080808080808080", "EFECF25C3C5DC6DB",
+       "8181818181818181", "8181818181818181", "907A46722ED34EC4",
+       "8282828282828282", "8282828282828282", "752666EB4CAB46EE",
+       "8383838383838383", "8383838383838383", "161BFABD4224C162",
+       "8484848484848484", "8484848484848484", "215F48699DB44A45",
+       "8585858585858585", "8585858585858585", "69D901A8A691E661",
+       "8686868686868686", "8686868686868686", "CBBF6EEFE6529728",
+       "8787878787878787", "8787878787878787", "7F26DCF425149823",
+       "8888888888888888", "8888888888888888", "762C40C8FADE9D16",
+       "8989898989898989", "8989898989898989", "2453CF5D5BF4E463",
+       "8A8A8A8A8A8A8A8A", "8A8A8A8A8A8A8A8A", "301085E3FDE724E1",
+       "8B8B8B8B8B8B8B8B", "8B8B8B8B8B8B8B8B", "EF4E3E8F1CC6706E",
+       "8C8C8C8C8C8C8C8C", "8C8C8C8C8C8C8C8C", "720479B024C397EE",
+       "8D8D8D8D8D8D8D8D", "8D8D8D8D8D8D8D8D", "BEA27E3795063C89",
+       "8E8E8E8E8E8E8E8E", "8E8E8E8E8E8E8E8E", "468E5218F1A37611",
+       "8F8F8F8F8F8F8F8F", "8F8F8F8F8F8F8F8F", "50ACE16ADF66BFE8",
+       "9090909090909090", "9090909090909090", "EEA24369A19F6937",
+       "9191919191919191", "9191919191919191", "6050D369017B6E62",
+       "9292929292929292", "9292929292929292", "5B365F2FB2CD7F32",
+       "9393939393939393", "9393939393939393", "F0B00B264381DDBB",
+       "9494949494949494", "9494949494949494", "E1D23881C957B96C",
+       "9595959595959595", "9595959595959595", "D936BF54ECA8BDCE",
+       "9696969696969696", "9696969696969696", "A020003C5554F34C",
+       "9797979797979797", "9797979797979797", "6118FCEBD407281D",
+       "9898989898989898", "9898989898989898", "072E328C984DE4A2",
+       "9999999999999999", "9999999999999999", "1440B7EF9E63D3AA",
+       "9A9A9A9A9A9A9A9A", "9A9A9A9A9A9A9A9A", "79BFA264BDA57373",
+       "9B9B9B9B9B9B9B9B", "9B9B9B9B9B9B9B9B", "C50E8FC289BBD876",
+       "9C9C9C9C9C9C9C9C", "9C9C9C9C9C9C9C9C", "A399D3D63E169FA9",
+       "9D9D9D9D9D9D9D9D", "9D9D9D9D9D9D9D9D", "4B8919B667BD53AB",
+       "9E9E9E9E9E9E9E9E", "9E9E9E9E9E9E9E9E", "D66CDCAF3F6724A2",
+       "9F9F9F9F9F9F9F9F", "9F9F9F9F9F9F9F9F", "E40E81FF3F618340",
+       "A0A0A0A0A0A0A0A0", "A0A0A0A0A0A0A0A0", "10EDB8977B348B35",
+       "A1A1A1A1A1A1A1A1", "A1A1A1A1A1A1A1A1", "6446C5769D8409A0",
+       "A2A2A2A2A2A2A2A2", "A2A2A2A2A2A2A2A2", "17ED1191CA8D67A3",
+       "A3A3A3A3A3A3A3A3", "A3A3A3A3A3A3A3A3", "B6D8533731BA1318",
+       "A4A4A4A4A4A4A4A4", "A4A4A4A4A4A4A4A4", "CA439007C7245CD0",
+       "A5A5A5A5A5A5A5A5", "A5A5A5A5A5A5A5A5", "06FC7FDE1C8389E7",
+       "A6A6A6A6A6A6A6A6", "A6A6A6A6A6A6A6A6", "7A3C1F3BD60CB3D8",
+       "A7A7A7A7A7A7A7A7", "A7A7A7A7A7A7A7A7", "E415D80048DBA848",
+       "A8A8A8A8A8A8A8A8", "A8A8A8A8A8A8A8A8", "26F88D30C0FB8302",
+       "A9A9A9A9A9A9A9A9", "A9A9A9A9A9A9A9A9", "D4E00A9EF5E6D8F3",
+       "AAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAA", "C4322BE19E9A5A17",
+       "ABABABABABABABAB", "ABABABABABABABAB", "ACE41A06BFA258EA",
+       "ACACACACACACACAC", "ACACACACACACACAC", "EEAAC6D17880BD56",
+       "ADADADADADADADAD", "ADADADADADADADAD", "3C9A34CA4CB49EEB",
+       "AEAEAEAEAEAEAEAE", "AEAEAEAEAEAEAEAE", "9527B0287B75F5A3",
+       "AFAFAFAFAFAFAFAF", "AFAFAFAFAFAFAFAF", "F2D9D1BE74376C0C",
+       "B0B0B0B0B0B0B0B0", "B0B0B0B0B0B0B0B0", "939618DF0AEFAAE7",
+       "B1B1B1B1B1B1B1B1", "B1B1B1B1B1B1B1B1", "24692773CB9F27FE",
+       "B2B2B2B2B2B2B2B2", "B2B2B2B2B2B2B2B2", "38703BA5E2315D1D",
+       "B3B3B3B3B3B3B3B3", "B3B3B3B3B3B3B3B3", "FCB7E4B7D702E2FB",
+       "B4B4B4B4B4B4B4B4", "B4B4B4B4B4B4B4B4", "36F0D0B3675704D5",
+       "B5B5B5B5B5B5B5B5", "B5B5B5B5B5B5B5B5", "62D473F539FA0D8B",
+       "B6B6B6B6B6B6B6B6", "B6B6B6B6B6B6B6B6", "1533F3ED9BE8EF8E",
+       "B7B7B7B7B7B7B7B7", "B7B7B7B7B7B7B7B7", "9C4EA352599731ED",
+       "B8B8B8B8B8B8B8B8", "B8B8B8B8B8B8B8B8", "FABBF7C046FD273F",
+       "B9B9B9B9B9B9B9B9", "B9B9B9B9B9B9B9B9", "B7FE63A61C646F3A",
+       "BABABABABABABABA", "BABABABABABABABA", "10ADB6E2AB972BBE",
+       "BBBBBBBBBBBBBBBB", "BBBBBBBBBBBBBBBB", "F91DCAD912332F3B",
+       "BCBCBCBCBCBCBCBC", "BCBCBCBCBCBCBCBC", "46E7EF47323A701D",
+       "BDBDBDBDBDBDBDBD", "BDBDBDBDBDBDBDBD", "8DB18CCD9692F758",
+       "BEBEBEBEBEBEBEBE", "BEBEBEBEBEBEBEBE", "E6207B536AAAEFFC",
+       "BFBFBFBFBFBFBFBF", "BFBFBFBFBFBFBFBF", "92AA224372156A00",
+       "C0C0C0C0C0C0C0C0", "C0C0C0C0C0C0C0C0", "A3B357885B1E16D2",
+       "C1C1C1C1C1C1C1C1", "C1C1C1C1C1C1C1C1", "169F7629C970C1E5",
+       "C2C2C2C2C2C2C2C2", "C2C2C2C2C2C2C2C2", "62F44B247CF1348C",
+       "C3C3C3C3C3C3C3C3", "C3C3C3C3C3C3C3C3", "AE0FEEB0495932C8",
+       "C4C4C4C4C4C4C4C4", "C4C4C4C4C4C4C4C4", "72DAF2A7C9EA6803",
+       "C5C5C5C5C5C5C5C5", "C5C5C5C5C5C5C5C5", "4FB5D5536DA544F4",
+       "C6C6C6C6C6C6C6C6", "C6C6C6C6C6C6C6C6", "1DD4E65AAF7988B4",
+       "C7C7C7C7C7C7C7C7", "C7C7C7C7C7C7C7C7", "76BF084C1535A6C6",
+       "C8C8C8C8C8C8C8C8", "C8C8C8C8C8C8C8C8", "AFEC35B09D36315F",
+       "C9C9C9C9C9C9C9C9", "C9C9C9C9C9C9C9C9", "C8078A6148818403",
+       "CACACACACACACACA", "CACACACACACACACA", "4DA91CB4124B67FE",
+       "CBCBCBCBCBCBCBCB", "CBCBCBCBCBCBCBCB", "2DABFEB346794C3D",
+       "CCCCCCCCCCCCCCCC", "CCCCCCCCCCCCCCCC", "FBCD12C790D21CD7",
+       "CDCDCDCDCDCDCDCD", "CDCDCDCDCDCDCDCD", "536873DB879CC770",
+       "CECECECECECECECE", "CECECECECECECECE", "9AA159D7309DA7A0",
+       "CFCFCFCFCFCFCFCF", "CFCFCFCFCFCFCFCF", "0B844B9D8C4EA14A",
+       "D0D0D0D0D0D0D0D0", "D0D0D0D0D0D0D0D0", "3BBD84CE539E68C4",
+       "D1D1D1D1D1D1D1D1", "D1D1D1D1D1D1D1D1", "CF3E4F3E026E2C8E",
+       "D2D2D2D2D2D2D2D2", "D2D2D2D2D2D2D2D2", "82F85885D542AF58",
+       "D3D3D3D3D3D3D3D3", "D3D3D3D3D3D3D3D3", "22D334D6493B3CB6",
+       "D4D4D4D4D4D4D4D4", "D4D4D4D4D4D4D4D4", "47E9CB3E3154D673",
+       "D5D5D5D5D5D5D5D5", "D5D5D5D5D5D5D5D5", "2352BCC708ADC7E9",
+       "D6D6D6D6D6D6D6D6", "D6D6D6D6D6D6D6D6", "8C0F3BA0C8601980",
+       "D7D7D7D7D7D7D7D7", "D7D7D7D7D7D7D7D7", "EE5E9FD70CEF00E9",
+       "D8D8D8D8D8D8D8D8", "D8D8D8D8D8D8D8D8", "DEF6BDA6CABF9547",
+       "D9D9D9D9D9D9D9D9", "D9D9D9D9D9D9D9D9", "4DADD04A0EA70F20",
+       "DADADADADADADADA", "DADADADADADADADA", "C1AA16689EE1B482",
+       "DBDBDBDBDBDBDBDB", "DBDBDBDBDBDBDBDB", "F45FC26193E69AEE",
+       "DCDCDCDCDCDCDCDC", "DCDCDCDCDCDCDCDC", "D0CFBB937CEDBFB5",
+       "DDDDDDDDDDDDDDDD", "DDDDDDDDDDDDDDDD", "F0752004EE23D87B",
+       "DEDEDEDEDEDEDEDE", "DEDEDEDEDEDEDEDE", "77A791E28AA464A5",
+       "DFDFDFDFDFDFDFDF", "DFDFDFDFDFDFDFDF", "E7562A7F56FF4966",
+       "E0E0E0E0E0E0E0E0", "E0E0E0E0E0E0E0E0", "B026913F2CCFB109",
+       "E1E1E1E1E1E1E1E1", "E1E1E1E1E1E1E1E1", "0DB572DDCE388AC7",
+       "E2E2E2E2E2E2E2E2", "E2E2E2E2E2E2E2E2", "D9FA6595F0C094CA",
+       "E3E3E3E3E3E3E3E3", "E3E3E3E3E3E3E3E3", "ADE4804C4BE4486E",
+       "E4E4E4E4E4E4E4E4", "E4E4E4E4E4E4E4E4", "007B81F520E6D7DA",
+       "E5E5E5E5E5E5E5E5", "E5E5E5E5E5E5E5E5", "961AEB77BFC10B3C",
+       "E6E6E6E6E6E6E6E6", "E6E6E6E6E6E6E6E6", "8A8DD870C9B14AF2",
+       "E7E7E7E7E7E7E7E7", "E7E7E7E7E7E7E7E7", "3CC02E14B6349B25",
+       "E8E8E8E8E8E8E8E8", "E8E8E8E8E8E8E8E8", "BAD3EE68BDDB9607",
+       "E9E9E9E9E9E9E9E9", "E9E9E9E9E9E9E9E9", "DFF918E93BDAD292",
+       "EAEAEAEAEAEAEAEA", "EAEAEAEAEAEAEAEA", "8FE559C7CD6FA56D",
+       "EBEBEBEBEBEBEBEB", "EBEBEBEBEBEBEBEB", "C88480835C1A444C",
+       "ECECECECECECECEC", "ECECECECECECECEC", "D6EE30A16B2CC01E",
+       "EDEDEDEDEDEDEDED", "EDEDEDEDEDEDEDED", "6932D887B2EA9C1A",
+       "EEEEEEEEEEEEEEEE", "EEEEEEEEEEEEEEEE", "0BFC865461F13ACC",
+       "EFEFEFEFEFEFEFEF", "EFEFEFEFEFEFEFEF", "228AEA0D403E807A",
+       "F0F0F0F0F0F0F0F0", "F0F0F0F0F0F0F0F0", "2A2891F65BB8173C",
+       "F1F1F1F1F1F1F1F1", "F1F1F1F1F1F1F1F1", "5D1B8FAF7839494B",
+       "F2F2F2F2F2F2F2F2", "F2F2F2F2F2F2F2F2", "1C0A9280EECF5D48",
+       "F3F3F3F3F3F3F3F3", "F3F3F3F3F3F3F3F3", "6CBCE951BBC30F74",
+       "F4F4F4F4F4F4F4F4", "F4F4F4F4F4F4F4F4", "9CA66E96BD08BC70",
+       "F5F5F5F5F5F5F5F5", "F5F5F5F5F5F5F5F5", "F5D779FCFBB28BF3",
+       "F6F6F6F6F6F6F6F6", "F6F6F6F6F6F6F6F6", "0FEC6BBF9B859184",
+       "F7F7F7F7F7F7F7F7", "F7F7F7F7F7F7F7F7", "EF88D2BF052DBDA8",
+       "F8F8F8F8F8F8F8F8", "F8F8F8F8F8F8F8F8", "39ADBDDB7363090D",
+       "F9F9F9F9F9F9F9F9", "F9F9F9F9F9F9F9F9", "C0AEAF445F7E2A7A",
+       "FAFAFAFAFAFAFAFA", "FAFAFAFAFAFAFAFA", "C66F54067298D4E9",
+       "FBFBFBFBFBFBFBFB", "FBFBFBFBFBFBFBFB", "E0BA8F4488AAF97C",
+       "FCFCFCFCFCFCFCFC", "FCFCFCFCFCFCFCFC", "67B36E2875D9631C",
+       "FDFDFDFDFDFDFDFD", "FDFDFDFDFDFDFDFD", "1ED83D49E267191D",
+       "FEFEFEFEFEFEFEFE", "FEFEFEFEFEFEFEFE", "66B2B23EA84693AD",
+       "FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF", "7359B2163E4EDC58",
+       "0001020304050607", "0011223344556677", "3EF0A891CF8ED990",
+       "2BD6459F82C5B300", "EA024714AD5C4D84", "126EFE8ED312190A",
+
+       NULL
+};
+
+/*
+ * Known-answer tests for DES/3DES in CBC mode. Order: key, IV,
+ * plaintext, ciphertext.
+ */
+static const char *const KAT_DES_CBC[] = {
+       /*
+        * From NIST validation suite (tdesmmt.zip).
+        */
+       "34a41a8c293176c1b30732ecfe38ae8a34a41a8c293176c1",
+       "f55b4855228bd0b4",
+       "7dd880d2a9ab411c",
+       "c91892948b6cadb4",
+
+       "70a88fa1dfb9942fa77f40157ffef2ad70a88fa1dfb9942f",
+       "ece08ce2fdc6ce80",
+       "bc225304d5a3a5c9918fc5006cbc40cc",
+       "27f67dc87af7ddb4b68f63fa7c2d454a",
+
+       "e091790be55be0bc0780153861a84adce091790be55be0bc",
+       "fd7d430f86fbbffe",
+       "03c7fffd7f36499c703dedc9df4de4a92dd4382e576d6ae9",
+       "053aeba85dd3a23bfbe8440a432f9578f312be60fb9f0035",
+
+       "857feacd16157c58e5347a70e56e578a857feacd16157c58",
+       "002dcb6d46ef0969",
+       "1f13701c7f0d7385307507a18e89843ebd295bd5e239ef109347a6898c6d3fd5",
+       "a0e4edde34f05bd8397ce279e49853e9387ba04be562f5fa19c3289c3f5a3391",
+
+       "a173545b265875ba852331fbb95b49a8a173545b265875ba",
+       "ab385756391d364c",
+       "d08894c565608d9ae51dda63b85b3b33b1703bb5e4f1abcbb8794e743da5d6f3bf630f2e9b6d5b54",
+       "370b47acf89ac6bdbb13c9a7336787dc41e1ad8beead32281d0609fb54968404bdf2894892590658",
+
+       "26376bcb2f23df1083cd684fe00ed3c726376bcb2f23df10",
+       "33acfb0f3d240ea6",
+       "903a1911da1e6877f23c1985a9b61786ef438e0ce1240885035ad60fc916b18e5d71a1fb9c5d1eff61db75c0076f6efb",
+       "7a4f7510f6ec0b93e2495d21a8355684d303a770ebda2e0e51ff33d72b20cb73e58e2e3de2ef6b2e12c504c0f181ba63",
+
+       "3e1f98135d027cec752f67765408a7913e1f98135d027cec",
+       "11f5f2304b28f68b",
+       "7c022f5af24f7925d323d4d0e20a2ce49272c5e764b22c806f4b6ddc406d864fe5bd1c3f45556d3eb30c8676c2f8b54a5a32423a0bd95a07",
+       "2bb4b131fa4ae0b4f0378a2cdb68556af6eee837613016d7ea936f3931f25f8b3ae351d5e9d00be665676e2400408b5db9892d95421e7f1a",
+
+       "13b9d549cd136ec7bf9e9810ef2cdcbf13b9d549cd136ec7",
+       "a82c1b1057badcc8",
+       "1fff1563bc1645b55cb23ea34a0049dfc06607150614b621dedcb07f20433402a2d869c95ac4a070c7a3da838c928a385f899c5d21ecb58f4e5cbdad98d39b8c",
+       "75f804d4a2c542a31703e23df26cc38861a0729090e6eae5672c1db8c0b09fba9b125bbca7d6c7d330b3859e6725c6d26de21c4e3af7f5ea94df3cde2349ce37",
+
+       "20320dfdad579bb57c6e4acd769dbadf20320dfdad579bb5",
+       "879201b5857ccdea",
+       "0431283cc8bb4dc7750a9d5c68578486932091632a12d0a79f2c54e3d122130881fff727050f317a40fcd1a8d13793458b99fc98254ba6a233e3d95b55cf5a3faff78809999ea4bf",
+       "85d17840eb2af5fc727027336bfd71a2b31bd14a1d9eb64f8a08bfc4f56eaa9ca7654a5ae698287869cc27324813730de4f1384e0b8cfbc472ff5470e3c5e4bd8ceb23dc2d91988c",
+
+       "23abb073a2df34cb3d1fdce6b092582c23abb073a2df34cb",
+       "7d7fbf19e8562d32",
+       "31e718fd95e6d7ca4f94763191add2674ab07c909d88c486916c16d60a048a0cf8cdb631cebec791362cd0c202eb61e166b65c1f65d0047c8aec57d3d84b9e17032442dce148e1191b06a12c284cc41e",
+       "c9a3f75ab6a7cd08a7fd53ca540aafe731d257ee1c379fadcc4cc1a06e7c12bddbeb7562c436d1da849ed072629e82a97b56d9becc25ff4f16f21c5f2a01911604f0b5c49df96cb641faee662ca8aa68",
+
+       "b5cb1504802326c73df186e3e352a20de643b0d63ee30e37",
+       "43f791134c5647ba",
+       "dcc153cef81d6f24",
+       "92538bd8af18d3ba",
+
+       "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",
+       "c2e999cb6249023c",
+       "c689aee38a301bb316da75db36f110b5",
+       "e9afaba5ec75ea1bbe65506655bb4ecb",
+
+       "1a5d4c0825072a15a8ad9dfdaeda8c048adffb85bc4fced0",
+       "7fcfa736f7548b6f",
+       "983c3edacd939406010e1bc6ff9e12320ac5008117fa8f84",
+       "d84fa24f38cf451ca2c9adc960120bd8ff9871584fe31cee",
+
+       "d98aadc76d4a3716158c32866efbb9ce834af2297379a49d",
+       "3c5220327c502b44",
+       "6174079dda53ca723ebf00a66837f8d5ce648c08acaa5ee45ffe62210ef79d3e",
+       "f5bd4d600bed77bec78409e3530ebda1d815506ed53103015b87e371ae000958",
+
+       "ef6d3e54266d978ffb0b8ce6689d803e2cd34cc802fd0252",
+       "38bae5bce06d0ad9",
+       "c4f228b537223cd01c0debb5d9d4e12ba71656618d119b2f8f0af29d23efa3a9e43c4c458a1b79a0",
+       "9e3289fb18379f55aa4e45a7e0e6df160b33b75f8627ad0954f8fdcb78cee55a4664caeda1000fe5",
+
+       "625bc19b19df83abfb2f5bec9d4f2062017525a75bc26e70",
+       "bd0cff364ff69a91",
+       "8152d2ab876c3c8201403a5a406d3feaf27319dbea6ad01e24f4d18203704b86de70da6bbb6d638e5aba3ff576b79b28",
+       "706fe7a973fac40e25b2b4499ce527078944c70e976d017b6af86a3a7a6b52943a72ba18a58000d2b61fdc3bfef2bc4a",
+
+       "b6383176046e6880a1023bf45768b5bf5119022fe054bfe5",
+       "ec13ca541c43401e",
+       "cd5a886e9af011346c4dba36a424f96a78a1ddf28aaa4188bf65451f4efaffc7179a6dd237c0ae35d9b672314e5cb032612597f7e462c6f3",
+       "b030f976f46277ee211c4a324d5c87555d1084513a1223d3b84416b52bbc28f4b77f3a9d8d0d91dc37d3dbe8af8be98f74674b02f9a38527",
+
+       "3d8cf273d343b9aedccddacb91ad86206737adc86b4a49a7",
+       "bb3a9a0c71c62ef0",
+       "1fde3991c32ce220b5b6666a9234f2fd7bd24b921829fd9cdc6eb4218be9eac9faa9c2351777349128086b6d58776bc86ff2f76ee1b3b2850a318462b8983fa1",
+       "422ce705a46bb52ad928dab6c863166d617c6fc24003633120d91918314bbf464cea7345c3c35f2042f2d6929735d74d7728f22fea618a0b9cf5b1281acb13fb",
+
+       "fbceb5cb646b925be0b92f7f6b493d5e5b16e9159732732a",
+       "2e17b3c7025ae86b",
+       "4c309bc8e1e464fdd2a2b8978645d668d455f7526bd8d7b6716a722f6a900b815c4a73cc30e788065c1dfca7bf5958a6cc5440a5ebe7f8691c20278cde95db764ff8ce8994ece89c",
+       "c02129bdf4bbbd75e71605a00b12c80db6b4e05308e916615011f09147ed915dd1bc67f27f9e027e4e13df36b55464a31c11b4d1fe3d855d89df492e1a7201b995c1ba16a8dbabee",
+
+       "9b162a0df8ad9b61c88676e3d586434570b902f12a2046e0",
+       "ebd6fefe029ad54b",
+       "f4c1c918e77355c8156f0fd778da52bff121ae5f2f44eaf4d2754946d0e10d1f18ce3a0176e69c18b7d20b6e0d0bee5eb5edfe4bd60e4d92adcd86bce72e76f94ee5cbcaa8b01cfddcea2ade575e66ac",
+       "1ff3c8709f403a8eff291aedf50c010df5c5ff64a8b205f1fce68564798897a390db16ee0d053856b75898009731da290fcc119dad987277aacef694872e880c4bb41471063fae05c89f25e4bd0cad6a",
+
+       NULL
+};
+
+static void
+xor_buf(unsigned char *dst, const unsigned char *src, size_t len)
+{
+       while (len -- > 0) {
+               *dst ++ ^= *src ++;
+       }
+}
+
+static void
+monte_carlo_DES_encrypt(const br_block_cbcenc_class *ve)
+{
+       unsigned char k1[8], k2[8], k3[8];
+       unsigned char buf[8];
+       unsigned char cipher[8];
+       int i, j;
+       br_des_gen_cbcenc_keys v_ec;
+       void *ec;
+
+       ec = &v_ec;
+       hextobin(k1, "9ec2372c86379df4");
+       hextobin(k2, "ad7ac4464f73805d");
+       hextobin(k3, "20c4f87564527c91");
+       hextobin(buf, "b624d6bd41783ab1");
+       hextobin(cipher, "eafd97b190b167fe");
+       for (i = 0; i < 400; i ++) {
+               unsigned char key[24];
+
+               memcpy(key, k1, 8);
+               memcpy(key + 8, k2, 8);
+               memcpy(key + 16, k3, 8);
+               ve->init(ec, key, sizeof key);
+               for (j = 0; j < 10000; j ++) {
+                       unsigned char iv[8];
+
+                       memset(iv, 0, sizeof iv);
+                       ve->run(ec, iv, buf, sizeof buf);
+                       switch (j) {
+                       case 9997: xor_buf(k3, buf, 8); break;
+                       case 9998: xor_buf(k2, buf, 8); break;
+                       case 9999: xor_buf(k1, buf, 8); break;
+                       }
+               }
+               printf(".");
+               fflush(stdout);
+       }
+       printf(" ");
+       fflush(stdout);
+       check_equals("MC DES encrypt", buf, cipher, sizeof buf);
+}
+
+static void
+monte_carlo_DES_decrypt(const br_block_cbcdec_class *vd)
+{
+       unsigned char k1[8], k2[8], k3[8];
+       unsigned char buf[8];
+       unsigned char plain[8];
+       int i, j;
+       br_des_gen_cbcdec_keys v_dc;
+       void *dc;
+
+       dc = &v_dc;
+       hextobin(k1, "79b63486e0ce37e0");
+       hextobin(k2, "08e65231abae3710");
+       hextobin(k3, "1f5eb69e925ef185");
+       hextobin(buf, "2783aa729432fe96");
+       hextobin(plain, "44937ca532cdbf98");
+       for (i = 0; i < 400; i ++) {
+               unsigned char key[24];
+
+               memcpy(key, k1, 8);
+               memcpy(key + 8, k2, 8);
+               memcpy(key + 16, k3, 8);
+               vd->init(dc, key, sizeof key);
+               for (j = 0; j < 10000; j ++) {
+                       unsigned char iv[8];
+
+                       memset(iv, 0, sizeof iv);
+                       vd->run(dc, iv, buf, sizeof buf);
+                       switch (j) {
+                       case 9997: xor_buf(k3, buf, 8); break;
+                       case 9998: xor_buf(k2, buf, 8); break;
+                       case 9999: xor_buf(k1, buf, 8); break;
+                       }
+               }
+               printf(".");
+               fflush(stdout);
+       }
+       printf(" ");
+       fflush(stdout);
+       check_equals("MC DES decrypt", buf, plain, sizeof buf);
+}
+
+static void
+test_DES_generic(char *name,
+       const br_block_cbcenc_class *ve,
+       const br_block_cbcdec_class *vd,
+       int with_MC, int with_CBC)
+{
+       size_t u;
+
+       printf("Test %s: ", name);
+       fflush(stdout);
+
+       if (ve->block_size != 8 || vd->block_size != 8) {
+               fprintf(stderr, "%s failed: wrong block size\n", name);
+               exit(EXIT_FAILURE);
+       }
+
+       for (u = 0; KAT_DES[u]; u += 3) {
+               unsigned char key[24];
+               unsigned char plain[8];
+               unsigned char cipher[8];
+               unsigned char buf[8];
+               unsigned char iv[8];
+               size_t key_len;
+               br_des_gen_cbcenc_keys v_ec;
+               br_des_gen_cbcdec_keys v_dc;
+               const br_block_cbcenc_class **ec;
+               const br_block_cbcdec_class **dc;
+
+               ec = &v_ec.vtable;
+               dc = &v_dc.vtable;
+               key_len = hextobin(key, KAT_DES[u]);
+               hextobin(plain, KAT_DES[u + 1]);
+               hextobin(cipher, KAT_DES[u + 2]);
+               ve->init(ec, key, key_len);
+               memcpy(buf, plain, sizeof plain);
+               memset(iv, 0, sizeof iv);
+               ve->run(ec, iv, buf, sizeof buf);
+               check_equals("KAT DES encrypt", buf, cipher, sizeof cipher);
+               vd->init(dc, key, key_len);
+               memset(iv, 0, sizeof iv);
+               vd->run(dc, iv, buf, sizeof buf);
+               check_equals("KAT DES decrypt", buf, plain, sizeof plain);
+
+               if (key_len == 8) {
+                       memcpy(key + 8, key, 8);
+                       memcpy(key + 16, key, 8);
+                       ve->init(ec, key, 24);
+                       memcpy(buf, plain, sizeof plain);
+                       memset(iv, 0, sizeof iv);
+                       ve->run(ec, iv, buf, sizeof buf);
+                       check_equals("KAT DES->3 encrypt",
+                               buf, cipher, sizeof cipher);
+                       vd->init(dc, key, 24);
+                       memset(iv, 0, sizeof iv);
+                       vd->run(dc, iv, buf, sizeof buf);
+                       check_equals("KAT DES->3 decrypt",
+                               buf, plain, sizeof plain);
+               }
+       }
+
+       if (with_CBC) {
+               for (u = 0; KAT_DES_CBC[u]; u += 4) {
+                       unsigned char key[24];
+                       unsigned char ivref[8];
+                       unsigned char plain[200];
+                       unsigned char cipher[200];
+                       unsigned char buf[200];
+                       unsigned char iv[8];
+                       size_t key_len, data_len, v;
+                       br_des_gen_cbcenc_keys v_ec;
+                       br_des_gen_cbcdec_keys v_dc;
+                       const br_block_cbcenc_class **ec;
+                       const br_block_cbcdec_class **dc;
+
+                       ec = &v_ec.vtable;
+                       dc = &v_dc.vtable;
+                       key_len = hextobin(key, KAT_DES_CBC[u]);
+                       hextobin(ivref, KAT_DES_CBC[u + 1]);
+                       data_len = hextobin(plain, KAT_DES_CBC[u + 2]);
+                       hextobin(cipher, KAT_DES_CBC[u + 3]);
+                       ve->init(ec, key, key_len);
+
+                       memcpy(buf, plain, data_len);
+                       memcpy(iv, ivref, 8);
+                       ve->run(ec, iv, buf, data_len);
+                       check_equals("KAT CBC DES encrypt",
+                               buf, cipher, data_len);
+                       vd->init(dc, key, key_len);
+                       memcpy(iv, ivref, 8);
+                       vd->run(dc, iv, buf, data_len);
+                       check_equals("KAT CBC DES decrypt",
+                               buf, plain, data_len);
+
+                       memcpy(buf, plain, data_len);
+                       memcpy(iv, ivref, 8);
+                       for (v = 0; v < data_len; v += 8) {
+                               ve->run(ec, iv, buf + v, 8);
+                       }
+                       check_equals("KAT CBC DES encrypt (2)",
+                               buf, cipher, data_len);
+                       memcpy(iv, ivref, 8);
+                       for (v = 0; v < data_len; v += 8) {
+                               vd->run(dc, iv, buf + v, 8);
+                       }
+                       check_equals("KAT CBC DES decrypt (2)",
+                               buf, plain, data_len);
+               }
+       }
+
+       if (with_MC) {
+               monte_carlo_DES_encrypt(ve);
+               monte_carlo_DES_decrypt(vd);
+       }
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_DES_tab(void)
+{
+       test_DES_generic("DES_tab",
+               &br_des_tab_cbcenc_vtable,
+               &br_des_tab_cbcdec_vtable,
+               1, 1);
+}
+
+static void
+test_DES_ct(void)
+{
+       test_DES_generic("DES_ct",
+               &br_des_ct_cbcenc_vtable,
+               &br_des_ct_cbcdec_vtable,
+               1, 1);
+}
+
+/*
+ * A 1024-bit RSA key, generated with OpenSSL.
+ */
+static const unsigned char RSA_N[] = {
+       0xBF, 0xB4, 0xA6, 0x2E, 0x87, 0x3F, 0x9C, 0x8D,
+       0xA0, 0xC4, 0x2E, 0x7B, 0x59, 0x36, 0x0F, 0xB0,
+       0xFF, 0xE1, 0x25, 0x49, 0xE5, 0xE6, 0x36, 0xB0,
+       0x48, 0xC2, 0x08, 0x6B, 0x77, 0xA7, 0xC0, 0x51,
+       0x66, 0x35, 0x06, 0xA9, 0x59, 0xDF, 0x17, 0x7F,
+       0x15, 0xF6, 0xB4, 0xE5, 0x44, 0xEE, 0x72, 0x3C,
+       0x53, 0x11, 0x52, 0xC9, 0xC9, 0x61, 0x4F, 0x92,
+       0x33, 0x64, 0x70, 0x43, 0x07, 0xF1, 0x3F, 0x7F,
+       0x15, 0xAC, 0xF0, 0xC1, 0x54, 0x7D, 0x55, 0xC0,
+       0x29, 0xDC, 0x9E, 0xCC, 0xE4, 0x1D, 0x11, 0x72,
+       0x45, 0xF4, 0xD2, 0x70, 0xFC, 0x34, 0xB2, 0x1F,
+       0xF3, 0xAD, 0x6A, 0xF0, 0xE5, 0x56, 0x11, 0xF8,
+       0x0C, 0x3A, 0x8B, 0x04, 0x46, 0x7C, 0x77, 0xD9,
+       0x41, 0x1F, 0x40, 0xBE, 0x93, 0x80, 0x9D, 0x23,
+       0x75, 0x80, 0x12, 0x26, 0x5A, 0x72, 0x1C, 0xDD,
+       0x47, 0xB3, 0x2A, 0x33, 0xD8, 0x19, 0x61, 0xE3
+};
+static const unsigned char RSA_E[] = {
+       0x01, 0x00, 0x01
+};
+/* unused
+static const unsigned char RSA_D[] = {
+       0xAE, 0x56, 0x0B, 0x56, 0x7E, 0xDA, 0x83, 0x75,
+       0x6C, 0xC1, 0x5C, 0x00, 0x02, 0x96, 0x1E, 0x58,
+       0xF9, 0xA9, 0xF7, 0x2E, 0x27, 0xEB, 0x5E, 0xCA,
+       0x9B, 0xB0, 0x10, 0xD6, 0x22, 0x7F, 0xA4, 0x6E,
+       0xA2, 0x03, 0x10, 0xE6, 0xCB, 0x7B, 0x0D, 0x34,
+       0x1E, 0x76, 0x37, 0xF5, 0xD3, 0xE5, 0x00, 0x70,
+       0x09, 0x9E, 0xD4, 0x69, 0xFB, 0x40, 0x0A, 0x8B,
+       0xCB, 0x3E, 0xC8, 0xB4, 0xBC, 0xB1, 0x50, 0xEA,
+       0x9D, 0xD9, 0x89, 0x8A, 0x98, 0x40, 0x79, 0xD1,
+       0x07, 0x66, 0xA7, 0x90, 0x63, 0x82, 0xB1, 0xE0,
+       0x24, 0xD0, 0x89, 0x6A, 0xEC, 0xC5, 0xF3, 0x21,
+       0x7D, 0xB8, 0xA5, 0x45, 0x3A, 0x3B, 0x34, 0x42,
+       0xC2, 0x82, 0x3C, 0x8D, 0xFA, 0x5D, 0xA0, 0xA8,
+       0x24, 0xC8, 0x40, 0x22, 0x19, 0xCB, 0xB5, 0x85,
+       0x67, 0x69, 0x60, 0xE4, 0xD0, 0x7E, 0xA3, 0x3B,
+       0xF7, 0x70, 0x50, 0xC9, 0x5C, 0x97, 0x29, 0x49
+};
+*/
+static const unsigned char RSA_P[] = {
+       0xF2, 0xE7, 0x6F, 0x66, 0x2E, 0xC4, 0x03, 0xD4,
+       0x89, 0x24, 0xCC, 0xE1, 0xCD, 0x3F, 0x01, 0x82,
+       0xC1, 0xFB, 0xAF, 0x44, 0xFA, 0xCC, 0x0E, 0xAA,
+       0x9D, 0x74, 0xA9, 0x65, 0xEF, 0xED, 0x4C, 0x87,
+       0xF0, 0xB3, 0xC6, 0xEA, 0x61, 0x85, 0xDE, 0x4E,
+       0x66, 0xB2, 0x5A, 0x9F, 0x7A, 0x41, 0xC5, 0x66,
+       0x57, 0xDF, 0x88, 0xF0, 0xB5, 0xF2, 0xC7, 0x7E,
+       0xE6, 0x55, 0x21, 0x96, 0x83, 0xD8, 0xAB, 0x57
+};
+static const unsigned char RSA_Q[] = {
+       0xCA, 0x0A, 0x92, 0xBF, 0x58, 0xB0, 0x2E, 0xF6,
+       0x66, 0x50, 0xB1, 0x48, 0x29, 0x42, 0x86, 0x6C,
+       0x98, 0x06, 0x7E, 0xB8, 0xB5, 0x4F, 0xFB, 0xC4,
+       0xF3, 0xC3, 0x36, 0x91, 0x07, 0xB6, 0xDB, 0xE9,
+       0x56, 0x3C, 0x51, 0x7D, 0xB5, 0xEC, 0x0A, 0xA9,
+       0x7C, 0x66, 0xF9, 0xD8, 0x25, 0xDE, 0xD2, 0x94,
+       0x5A, 0x58, 0xF1, 0x93, 0xE4, 0xF0, 0x5F, 0x27,
+       0xBD, 0x83, 0xC7, 0xCA, 0x48, 0x6A, 0xB2, 0x55
+};
+static const unsigned char RSA_DP[] = {
+       0xAF, 0x97, 0xBE, 0x60, 0x0F, 0xCE, 0x83, 0x36,
+       0x51, 0x2D, 0xD9, 0x2E, 0x22, 0x41, 0x39, 0xC6,
+       0x5C, 0x94, 0xA4, 0xCF, 0x28, 0xBD, 0xFA, 0x9C,
+       0x3B, 0xD6, 0xE9, 0xDE, 0x56, 0xE3, 0x24, 0x3F,
+       0xE1, 0x31, 0x14, 0xCA, 0xBA, 0x55, 0x1B, 0xAF,
+       0x71, 0x6D, 0xDD, 0x35, 0x0C, 0x1C, 0x1F, 0xA7,
+       0x2C, 0x3E, 0xDB, 0xAF, 0xA6, 0xD8, 0x2A, 0x7F,
+       0x01, 0xE2, 0xE8, 0xB4, 0xF5, 0xFA, 0xDB, 0x61
+};
+static const unsigned char RSA_DQ[] = {
+       0x29, 0xC0, 0x4B, 0x98, 0xFD, 0x13, 0xD3, 0x70,
+       0x99, 0xAE, 0x1D, 0x24, 0x83, 0x5A, 0x3A, 0xFB,
+       0x1F, 0xE3, 0x5F, 0xB6, 0x7D, 0xC9, 0x5C, 0x86,
+       0xD3, 0xB4, 0xC8, 0x86, 0xE9, 0xE8, 0x30, 0xC3,
+       0xA4, 0x4D, 0x6C, 0xAD, 0xA4, 0xB5, 0x75, 0x72,
+       0x96, 0xC1, 0x94, 0xE9, 0xC4, 0xD1, 0xAA, 0x04,
+       0x7C, 0x33, 0x1B, 0x20, 0xEB, 0xD3, 0x7C, 0x66,
+       0x72, 0xF4, 0x53, 0x8A, 0x0A, 0xB2, 0xF9, 0xCD
+};
+static const unsigned char RSA_IQ[] = {
+       0xE8, 0xEB, 0x04, 0x79, 0xA5, 0xC1, 0x79, 0xDE,
+       0xD5, 0x49, 0xA1, 0x0B, 0x48, 0xB9, 0x0E, 0x55,
+       0x74, 0x2C, 0x54, 0xEE, 0xA8, 0xB0, 0x01, 0xC2,
+       0xD2, 0x3C, 0x3E, 0x47, 0x3A, 0x7C, 0xC8, 0x3D,
+       0x2E, 0x33, 0x54, 0x4D, 0x40, 0x29, 0x41, 0x74,
+       0xBA, 0xE1, 0x93, 0x09, 0xEC, 0xE0, 0x1B, 0x4D,
+       0x1F, 0x2A, 0xCA, 0x4A, 0x0B, 0x5F, 0xE6, 0xBE,
+       0x59, 0x0A, 0xC4, 0xC9, 0xD9, 0x82, 0xAC, 0xE1
+};
+
+static const br_rsa_public_key RSA_PK = {
+       (void *)RSA_N, sizeof RSA_N,
+       (void *)RSA_E, sizeof RSA_E
+};
+
+static const br_rsa_private_key RSA_SK = {
+       1024,
+       (void *)RSA_P, sizeof RSA_P,
+       (void *)RSA_Q, sizeof RSA_Q,
+       (void *)RSA_DP, sizeof RSA_DP,
+       (void *)RSA_DQ, sizeof RSA_DQ,
+       (void *)RSA_IQ, sizeof RSA_IQ
+};
+
+static void
+test_RSA_core(char *name, br_rsa_public fpub, br_rsa_private fpriv)
+{
+       unsigned char t1[128], t2[128], t3[128];
+
+       printf("Test %s: ", name);
+       fflush(stdout);
+
+       /*
+        * A KAT test (computed with OpenSSL).
+        */
+       hextobin(t1, "45A3DC6A106BCD3BD0E48FB579643AA3FF801E5903E80AA9B43A695A8E7F454E93FA208B69995FF7A6D5617C2FEB8E546375A664977A48931842AAE796B5A0D64393DCA35F3490FC157F5BD83B9D58C2F7926E6AE648A2BD96CAB8FCCD3D35BB11424AD47D973FF6D69CA774841AEC45DFAE99CCF79893E7047FDE6CB00AA76D");
+       hextobin(t2, "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A05000414A94A8FE5CCB19BA61C4C0873D391E987982FBBD3");
+       memcpy(t3, t1, sizeof t1);
+       if (!fpub(t3, sizeof t3, &RSA_PK)) {
+               fprintf(stderr, "RSA public operation failed\n");
+               exit(EXIT_FAILURE);
+       }
+       check_equals("KAT RSA pub", t2, t3, sizeof t2);
+       if (!fpriv(t3, &RSA_SK)) {
+               fprintf(stderr, "RSA private operation failed\n");
+               exit(EXIT_FAILURE);
+       }
+       check_equals("KAT RSA priv", t1, t3, sizeof t1);
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_RSA_i31(void)
+{
+       test_RSA_core("RSA i31 core", &br_rsa_i31_public, &br_rsa_i31_private);
+       /* FIXME
+       test_RSA_sign("RSA i31 sign",
+               &br_rsa_i31_pkcs1_vrfy, &br_rsa_i31_pkcs1_sign);
+       */
+}
+
+static void
+test_RSA_i32(void)
+{
+       test_RSA_core("RSA i32 core", &br_rsa_i32_public, &br_rsa_i32_private);
+       /* FIXME
+       test_RSA_sign("RSA i32 sign",
+               &br_rsa_i32_pkcs1_vrfy, &br_rsa_i32_pkcs1_sign);
+       */
+}
+
+#if 0
+static void
+test_RSA_signatures(void)
+{
+       uint32_t n[40], e[2], p[20], q[20], dp[20], dq[20], iq[20], x[40];
+       unsigned char hv[20], sig[128];
+       unsigned char ref[128], tmp[128];
+       br_sha1_context hc;
+
+       printf("Test RSA signatures: ");
+       fflush(stdout);
+
+       /*
+        * Decode RSA key elements.
+        */
+       br_int_decode(n, sizeof n / sizeof n[0], RSA_N, sizeof RSA_N);
+       br_int_decode(e, sizeof e / sizeof e[0], RSA_E, sizeof RSA_E);
+       br_int_decode(p, sizeof p / sizeof p[0], RSA_P, sizeof RSA_P);
+       br_int_decode(q, sizeof q / sizeof q[0], RSA_Q, sizeof RSA_Q);
+       br_int_decode(dp, sizeof dp / sizeof dp[0], RSA_DP, sizeof RSA_DP);
+       br_int_decode(dq, sizeof dq / sizeof dq[0], RSA_DQ, sizeof RSA_DQ);
+       br_int_decode(iq, sizeof iq / sizeof iq[0], RSA_IQ, sizeof RSA_IQ);
+
+       /*
+        * Decode reference signature (computed with OpenSSL).
+        */
+       hextobin(ref, "45A3DC6A106BCD3BD0E48FB579643AA3FF801E5903E80AA9B43A695A8E7F454E93FA208B69995FF7A6D5617C2FEB8E546375A664977A48931842AAE796B5A0D64393DCA35F3490FC157F5BD83B9D58C2F7926E6AE648A2BD96CAB8FCCD3D35BB11424AD47D973FF6D69CA774841AEC45DFAE99CCF79893E7047FDE6CB00AA76D");
+
+       /*
+        * Recompute signature. Since PKCS#1 v1.5 signatures are
+        * deterministic, we should get the same as the reference signature.
+        */
+       br_sha1_init(&hc);
+       br_sha1_update(&hc, "test", 4);
+       br_sha1_out(&hc, hv);
+       if (!br_rsa_sign(sig, sizeof sig, p, q, dp, dq, iq, br_sha1_ID, hv)) {
+               fprintf(stderr, "RSA-1024/SHA-1 sig generate failed\n");
+               exit(EXIT_FAILURE);
+       }
+       check_equals("KAT RSA-sign 1", sig, ref, sizeof sig);
+
+       /*
+        * Verify signature.
+        */
+       if (!br_rsa_verify(sig, sizeof sig, n, e, br_sha1_ID, hv)) {
+               fprintf(stderr, "RSA-1024/SHA-1 sig verify failed\n");
+               exit(EXIT_FAILURE);
+       }
+       hv[5] ^= 0x01;
+       if (br_rsa_verify(sig, sizeof sig, n, e, br_sha1_ID, hv)) {
+               fprintf(stderr, "RSA-1024/SHA-1 sig verify should have failed\n");
+               exit(EXIT_FAILURE);
+       }
+       hv[5] ^= 0x01;
+
+       /*
+        * Generate a signature with the alternate encoding (no NULL) and
+        * verify it.
+        */
+       hextobin(tmp, "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00301F300706052B0E03021A0414A94A8FE5CCB19BA61C4C0873D391E987982FBBD3");
+       br_int_decode(x, sizeof x / sizeof x[0], tmp, sizeof tmp);
+       x[0] = n[0];
+       br_rsa_private_core(x, p, q, dp, dq, iq);
+       br_int_encode(sig, sizeof sig, x);
+       if (!br_rsa_verify(sig, sizeof sig, n, e, br_sha1_ID, hv)) {
+               fprintf(stderr, "RSA-1024/SHA-1 sig verify (alt) failed\n");
+               exit(EXIT_FAILURE);
+       }
+       hv[5] ^= 0x01;
+       if (br_rsa_verify(sig, sizeof sig, n, e, br_sha1_ID, hv)) {
+               fprintf(stderr, "RSA-1024/SHA-1 sig verify (alt) should have failed\n");
+               exit(EXIT_FAILURE);
+       }
+       hv[5] ^= 0x01;
+
+       printf("done.\n");
+       fflush(stdout);
+}
+#endif
+
+/*
+ * From: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
+ */
+static const char *const KAT_GHASH[] = {
+
+       "66e94bd4ef8a2c3b884cfa59ca342b2e",
+       "",
+       "",
+       "00000000000000000000000000000000",
+
+       "66e94bd4ef8a2c3b884cfa59ca342b2e",
+       "",
+       "0388dace60b6a392f328c2b971b2fe78",
+       "f38cbb1ad69223dcc3457ae5b6b0f885",
+
+       "b83b533708bf535d0aa6e52980d53b78",
+       "",
+       "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+       "7f1b32b81b820d02614f8895ac1d4eac",
+
+       "b83b533708bf535d0aa6e52980d53b78",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+       "698e57f70e6ecc7fd9463b7260a9ae5f",
+
+       "b83b533708bf535d0aa6e52980d53b78",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+       "df586bb4c249b92cb6922877e444d37b",
+
+       "b83b533708bf535d0aa6e52980d53b78",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+       "1c5afe9760d3932f3c9a878aac3dc3de",
+
+       "aae06992acbf52a3e8f4a96ec9300bd7",
+       "",
+       "98e7247c07f0fe411c267e4384b0f600",
+       "e2c63f0ac44ad0e02efa05ab6743d4ce",
+
+       "466923ec9ae682214f2c082badb39249",
+       "",
+       "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+       "51110d40f6c8fff0eb1ae33445a889f0",
+
+       "466923ec9ae682214f2c082badb39249",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+       "ed2ce3062e4a8ec06db8b4c490e8a268",
+
+       "466923ec9ae682214f2c082badb39249",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+       "1e6a133806607858ee80eaf237064089",
+
+       "466923ec9ae682214f2c082badb39249",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+       "82567fb0b4cc371801eadec005968e94",
+
+       "dc95c078a2408989ad48a21492842087",
+       "",
+       "cea7403d4d606b6e074ec5d3baf39d18",
+       "83de425c5edc5d498f382c441041ca92",
+
+       "acbef20579b4b8ebce889bac8732dad7",
+       "",
+       "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+       "4db870d37cb75fcb46097c36230d1612",
+
+       "acbef20579b4b8ebce889bac8732dad7",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+       "8bd0c4d8aacd391e67cca447e8c38f65",
+
+       "acbef20579b4b8ebce889bac8732dad7",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+       "75a34288b8c68f811c52b2e9a2f97f63",
+
+       "acbef20579b4b8ebce889bac8732dad7",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+       "d5ffcf6fc5ac4d69722187421a7f170b",
+
+       NULL,
+};
+
+static void
+test_GHASH(char *name, br_ghash gh)
+{
+       size_t u;
+
+       printf("Test %s: ", name);
+       fflush(stdout);
+
+       for (u = 0; KAT_GHASH[u]; u += 4) {
+               unsigned char h[16];
+               unsigned char a[100];
+               size_t a_len;
+               unsigned char c[100];
+               size_t c_len;
+               unsigned char p[16];
+               unsigned char y[16];
+               unsigned char ref[16];
+
+               hextobin(h, KAT_GHASH[u]);
+               a_len = hextobin(a, KAT_GHASH[u + 1]);
+               c_len = hextobin(c, KAT_GHASH[u + 2]);
+               hextobin(ref, KAT_GHASH[u + 3]);
+               memset(y, 0, sizeof y);
+               gh(y, h, a, a_len);
+               gh(y, h, c, c_len);
+               memset(p, 0, sizeof p);
+               br_enc32be(p + 4, (uint32_t)a_len << 3);
+               br_enc32be(p + 12, (uint32_t)c_len << 3);
+               gh(y, h, p, sizeof p);
+               check_equals("KAT GHASH", y, ref, sizeof ref);
+       }
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_GHASH_ctmul(void)
+{
+       test_GHASH("GHASH_ctmul", br_ghash_ctmul);
+}
+
+static void
+test_GHASH_ctmul32(void)
+{
+       test_GHASH("GHASH_ctmul32", br_ghash_ctmul32);
+}
+
+static void
+test_GHASH_ctmul64(void)
+{
+       test_GHASH("GHASH_ctmul64", br_ghash_ctmul64);
+}
+
+static void
+test_EC_inner(const char *sk, const char *sU,
+       const br_ec_impl *impl, int curve)
+{
+       unsigned char bk[70];
+       unsigned char eG[150], eU[150];
+       uint32_t n[22], n0i;
+       size_t klen, ulen, nlen;
+       const br_ec_curve_def *cd;
+       br_hmac_drbg_context rng;
+       int i;
+
+       klen = hextobin(bk, sk);
+       ulen = hextobin(eU, sU);
+       switch (curve) {
+       case BR_EC_secp256r1:
+               cd = &br_secp256r1;
+               break;
+       case BR_EC_secp384r1:
+               cd = &br_secp384r1;
+               break;
+       case BR_EC_secp521r1:
+               cd = &br_secp521r1;
+               break;
+       default:
+               fprintf(stderr, "Unknown curve: %d\n", curve);
+               exit(EXIT_FAILURE);
+               break;
+       }
+       if (ulen != cd->generator_len) {
+               fprintf(stderr, "KAT vector wrong (%lu / %lu)\n",
+                       (unsigned long)ulen,
+                       (unsigned long)cd->generator_len);
+       }
+       memcpy(eG, cd->generator, ulen);
+       if (impl->mul(eG, ulen, bk, klen, curve) != 1) {
+               fprintf(stderr, "KAT multiplication failed\n");
+               exit(EXIT_FAILURE);
+       }
+       if (memcmp(eG, eU, ulen) != 0) {
+               fprintf(stderr, "KAT mul: mismatch\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Test the two-point-mul function. We want to test the basic
+        * functionality, and the following special cases:
+        *   x = y
+        *   x + y = curve order
+        */
+       nlen = cd->order_len;
+       br_i31_decode(n, cd->order, nlen);
+       n0i = br_i31_ninv31(n[1]);
+       br_hmac_drbg_init(&rng, &br_sha256_vtable, "seed for EC", 11);
+       for (i = 0; i < 10; i ++) {
+               unsigned char ba[80], bb[80], bx[80], by[80], bz[80];
+               uint32_t a[22], b[22], x[22], y[22], z[22], t1[22], t2[22];
+               uint32_t r;
+               unsigned char eA[160], eB[160], eC[160], eD[160];
+
+               /*
+                * Generate random a and b, and compute A = a*G and B = b*G.
+                */
+               br_hmac_drbg_generate(&rng, ba, sizeof ba);
+               br_i31_decode_reduce(a, ba, sizeof ba, n);
+               br_i31_encode(ba, nlen, a);
+               br_hmac_drbg_generate(&rng, bb, sizeof bb);
+               br_i31_decode_reduce(b, bb, sizeof bb, n);
+               br_i31_encode(bb, nlen, b);
+               memcpy(eA, cd->generator, ulen);
+               impl->mul(eA, ulen, ba, nlen, cd->curve);
+               memcpy(eB, cd->generator, ulen);
+               impl->mul(eB, ulen, bb, nlen, cd->curve);
+
+               /*
+                * Generate random x and y (modulo n).
+                */
+               br_hmac_drbg_generate(&rng, bx, sizeof bx);
+               br_i31_decode_reduce(x, bx, sizeof bx, n);
+               br_i31_encode(bx, nlen, x);
+               br_hmac_drbg_generate(&rng, by, sizeof by);
+               br_i31_decode_reduce(y, by, sizeof by, n);
+               br_i31_encode(by, nlen, y);
+
+               /*
+                * Compute z = a*x + b*y (mod n).
+                */
+               memcpy(t1, x, sizeof x);
+               br_i31_to_monty(t1, n);
+               br_i31_montymul(z, a, t1, n, n0i);
+               memcpy(t1, y, sizeof y);
+               br_i31_to_monty(t1, n);
+               br_i31_montymul(t2, b, t1, n, n0i);
+               r = br_i31_add(z, t2, 1);
+               r |= br_i31_sub(z, n, 0) ^ 1;
+               br_i31_sub(z, n, r);
+               br_i31_encode(bz, nlen, z);
+
+               /*
+                * Compute C = x*A + y*B with muladd(), and also
+                * D = z*G with mul(). The two points must match.
+                */
+               memcpy(eC, eA, ulen);
+               if (impl->muladd(eC, eB, ulen,
+                       bx, nlen, by, nlen, cd->curve) != 1)
+               {
+                       fprintf(stderr, "muladd() failed (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+               memcpy(eD, cd->generator, ulen);
+               if (impl->mul(eD, ulen, bz, nlen, cd->curve) != 1) {
+                       fprintf(stderr, "mul() failed (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+               if (memcmp(eC, eD, nlen) != 0) {
+                       fprintf(stderr, "mul() / muladd() mismatch\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               /*
+                * Check with x*A = y*B. We do so by setting b = x and y = a.
+                */
+               memcpy(b, x, sizeof x);
+               br_i31_encode(bb, nlen, b);
+               memcpy(eB, cd->generator, ulen);
+               impl->mul(eB, ulen, bb, nlen, cd->curve);
+               memcpy(y, a, sizeof a);
+               br_i31_encode(by, nlen, y);
+
+               memcpy(t1, x, sizeof x);
+               br_i31_to_monty(t1, n);
+               br_i31_montymul(z, a, t1, n, n0i);
+               memcpy(t1, y, sizeof y);
+               br_i31_to_monty(t1, n);
+               br_i31_montymul(t2, b, t1, n, n0i);
+               r = br_i31_add(z, t2, 1);
+               r |= br_i31_sub(z, n, 0) ^ 1;
+               br_i31_sub(z, n, r);
+               br_i31_encode(bz, nlen, z);
+
+               memcpy(eC, eA, ulen);
+               if (impl->muladd(eC, eB, ulen,
+                       bx, nlen, by, nlen, cd->curve) != 1)
+               {
+                       fprintf(stderr, "muladd() failed (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+               memcpy(eD, cd->generator, ulen);
+               if (impl->mul(eD, ulen, bz, nlen, cd->curve) != 1) {
+                       fprintf(stderr, "mul() failed (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+               if (memcmp(eC, eD, nlen) != 0) {
+                       fprintf(stderr,
+                               "mul() / muladd() mismatch (x*A=y*B)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               /*
+                * Check with x*A + y*B = 0. At that point, b = x, so we
+                * just need to set y = -a (mod n).
+                */
+               memcpy(y, n, sizeof n);
+               br_i31_sub(y, a, 1);
+               br_i31_encode(by, nlen, y);
+               memcpy(eC, eA, ulen);
+               if (impl->muladd(eC, eB, ulen,
+                       bx, nlen, by, nlen, cd->curve) != 0)
+               {
+                       fprintf(stderr, "muladd() should have failed\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       printf(".");
+       fflush(stdout);
+}
+
+static void
+test_EC_KAT(const char *name, const br_ec_impl *impl, uint32_t curve_mask)
+{
+
+       printf("Test %s: ", name);
+       fflush(stdout);
+
+       if (curve_mask & ((uint32_t)1 << BR_EC_secp256r1)) {
+               test_EC_inner(
+                       "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
+                       "0460FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB67903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
+                       impl, BR_EC_secp256r1);
+       }
+       if (curve_mask & ((uint32_t)1 << BR_EC_secp384r1)) {
+               test_EC_inner(
+                       "6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5",
+                       "04EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC138015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720",
+                       impl, BR_EC_secp384r1);
+       }
+       if (curve_mask & ((uint32_t)1 << BR_EC_secp521r1)) {
+               test_EC_inner(
+                       "00FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
+                       "0401894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A400493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5",
+                       impl, BR_EC_secp521r1);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+static void
+test_EC_prime_i31(void)
+{
+       test_EC_KAT("EC_prime_i31", &br_ec_prime_i31,
+               (uint32_t)1 << BR_EC_secp256r1
+               | (uint32_t)1 << BR_EC_secp384r1
+               | (uint32_t)1 << BR_EC_secp521r1);
+}
+
+static const unsigned char EC_P256_PUB_POINT[] = {
+       0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D,
+       0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D,
+       0x68, 0xC0, 0x49, 0xB8, 0x92, 0x3B, 0x61, 0xFA,
+       0x6C, 0xE6, 0x69, 0x62, 0x2E, 0x60, 0xF2, 0x9F,
+       0xB6, 0x79, 0x03, 0xFE, 0x10, 0x08, 0xB8, 0xBC,
+       0x99, 0xA4, 0x1A, 0xE9, 0xE9, 0x56, 0x28, 0xBC,
+       0x64, 0xF2, 0xF1, 0xB2, 0x0C, 0x2D, 0x7E, 0x9F,
+       0x51, 0x77, 0xA3, 0xC2, 0x94, 0xD4, 0x46, 0x22,
+       0x99
+};
+
+static const unsigned char EC_P256_PRIV_X[] = {
+       0xC9, 0xAF, 0xA9, 0xD8, 0x45, 0xBA, 0x75, 0x16,
+       0x6B, 0x5C, 0x21, 0x57, 0x67, 0xB1, 0xD6, 0x93,
+       0x4E, 0x50, 0xC3, 0xDB, 0x36, 0xE8, 0x9B, 0x12,
+       0x7B, 0x8A, 0x62, 0x2B, 0x12, 0x0F, 0x67, 0x21
+};
+
+static const br_ec_public_key EC_P256_PUB = {
+       BR_EC_secp256r1,
+       (unsigned char *)EC_P256_PUB_POINT, sizeof EC_P256_PUB_POINT
+};
+
+static const br_ec_private_key EC_P256_PRIV = {
+       BR_EC_secp256r1,
+       (unsigned char *)EC_P256_PRIV_X, sizeof EC_P256_PRIV_X
+};
+
+static const unsigned char EC_P384_PUB_POINT[] = {
+       0x04, 0xEC, 0x3A, 0x4E, 0x41, 0x5B, 0x4E, 0x19,
+       0xA4, 0x56, 0x86, 0x18, 0x02, 0x9F, 0x42, 0x7F,
+       0xA5, 0xDA, 0x9A, 0x8B, 0xC4, 0xAE, 0x92, 0xE0,
+       0x2E, 0x06, 0xAA, 0xE5, 0x28, 0x6B, 0x30, 0x0C,
+       0x64, 0xDE, 0xF8, 0xF0, 0xEA, 0x90, 0x55, 0x86,
+       0x60, 0x64, 0xA2, 0x54, 0x51, 0x54, 0x80, 0xBC,
+       0x13, 0x80, 0x15, 0xD9, 0xB7, 0x2D, 0x7D, 0x57,
+       0x24, 0x4E, 0xA8, 0xEF, 0x9A, 0xC0, 0xC6, 0x21,
+       0x89, 0x67, 0x08, 0xA5, 0x93, 0x67, 0xF9, 0xDF,
+       0xB9, 0xF5, 0x4C, 0xA8, 0x4B, 0x3F, 0x1C, 0x9D,
+       0xB1, 0x28, 0x8B, 0x23, 0x1C, 0x3A, 0xE0, 0xD4,
+       0xFE, 0x73, 0x44, 0xFD, 0x25, 0x33, 0x26, 0x47,
+       0x20
+};
+
+static const unsigned char EC_P384_PRIV_X[] = {
+       0x6B, 0x9D, 0x3D, 0xAD, 0x2E, 0x1B, 0x8C, 0x1C,
+       0x05, 0xB1, 0x98, 0x75, 0xB6, 0x65, 0x9F, 0x4D,
+       0xE2, 0x3C, 0x3B, 0x66, 0x7B, 0xF2, 0x97, 0xBA,
+       0x9A, 0xA4, 0x77, 0x40, 0x78, 0x71, 0x37, 0xD8,
+       0x96, 0xD5, 0x72, 0x4E, 0x4C, 0x70, 0xA8, 0x25,
+       0xF8, 0x72, 0xC9, 0xEA, 0x60, 0xD2, 0xED, 0xF5
+};
+
+static const br_ec_public_key EC_P384_PUB = {
+       BR_EC_secp384r1,
+       (unsigned char *)EC_P384_PUB_POINT, sizeof EC_P384_PUB_POINT
+};
+
+static const br_ec_private_key EC_P384_PRIV = {
+       BR_EC_secp384r1,
+       (unsigned char *)EC_P384_PRIV_X, sizeof EC_P384_PRIV_X
+};
+
+static const unsigned char EC_P521_PUB_POINT[] = {
+       0x04, 0x01, 0x89, 0x45, 0x50, 0xD0, 0x78, 0x59,
+       0x32, 0xE0, 0x0E, 0xAA, 0x23, 0xB6, 0x94, 0xF2,
+       0x13, 0xF8, 0xC3, 0x12, 0x1F, 0x86, 0xDC, 0x97,
+       0xA0, 0x4E, 0x5A, 0x71, 0x67, 0xDB, 0x4E, 0x5B,
+       0xCD, 0x37, 0x11, 0x23, 0xD4, 0x6E, 0x45, 0xDB,
+       0x6B, 0x5D, 0x53, 0x70, 0xA7, 0xF2, 0x0F, 0xB6,
+       0x33, 0x15, 0x5D, 0x38, 0xFF, 0xA1, 0x6D, 0x2B,
+       0xD7, 0x61, 0xDC, 0xAC, 0x47, 0x4B, 0x9A, 0x2F,
+       0x50, 0x23, 0xA4, 0x00, 0x49, 0x31, 0x01, 0xC9,
+       0x62, 0xCD, 0x4D, 0x2F, 0xDD, 0xF7, 0x82, 0x28,
+       0x5E, 0x64, 0x58, 0x41, 0x39, 0xC2, 0xF9, 0x1B,
+       0x47, 0xF8, 0x7F, 0xF8, 0x23, 0x54, 0xD6, 0x63,
+       0x0F, 0x74, 0x6A, 0x28, 0xA0, 0xDB, 0x25, 0x74,
+       0x1B, 0x5B, 0x34, 0xA8, 0x28, 0x00, 0x8B, 0x22,
+       0xAC, 0xC2, 0x3F, 0x92, 0x4F, 0xAA, 0xFB, 0xD4,
+       0xD3, 0x3F, 0x81, 0xEA, 0x66, 0x95, 0x6D, 0xFE,
+       0xAA, 0x2B, 0xFD, 0xFC, 0xF5
+};
+
+static const unsigned char EC_P521_PRIV_X[] = {
+       0x00, 0xFA, 0xD0, 0x6D, 0xAA, 0x62, 0xBA, 0x3B,
+       0x25, 0xD2, 0xFB, 0x40, 0x13, 0x3D, 0xA7, 0x57,
+       0x20, 0x5D, 0xE6, 0x7F, 0x5B, 0xB0, 0x01, 0x8F,
+       0xEE, 0x8C, 0x86, 0xE1, 0xB6, 0x8C, 0x7E, 0x75,
+       0xCA, 0xA8, 0x96, 0xEB, 0x32, 0xF1, 0xF4, 0x7C,
+       0x70, 0x85, 0x58, 0x36, 0xA6, 0xD1, 0x6F, 0xCC,
+       0x14, 0x66, 0xF6, 0xD8, 0xFB, 0xEC, 0x67, 0xDB,
+       0x89, 0xEC, 0x0C, 0x08, 0xB0, 0xE9, 0x96, 0xB8,
+       0x35, 0x38
+};
+
+static const br_ec_public_key EC_P521_PUB = {
+       BR_EC_secp521r1,
+       (unsigned char *)EC_P521_PUB_POINT, sizeof EC_P521_PUB_POINT
+};
+
+static const br_ec_private_key EC_P521_PRIV = {
+       BR_EC_secp521r1,
+       (unsigned char *)EC_P521_PRIV_X, sizeof EC_P521_PRIV_X
+};
+
+typedef struct {
+       const br_ec_public_key *pub;
+       const br_ec_private_key *priv;
+       const br_hash_class *hf;
+       const char *msg;
+       const char *sk;
+       const char *sraw;
+       const char *sasn1;
+} ecdsa_kat_vector;
+
+const ecdsa_kat_vector ECDSA_KAT[] = {
+
+       /* Test vectors for P-256, from RFC 6979. */
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha1_vtable, "sample",
+               "882905F1227FD620FBF2ABF21244F0BA83D0DC3A9103DBBEE43A1FB858109DB4",
+               "61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D326D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB",
+               "3044022061340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D3202206D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha224_vtable, "sample",
+               "103F90EE9DC52E5E7FB5132B7033C63066D194321491862059967C715985D473",
+               "53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3FB9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C",
+               "3045022053B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F022100B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha256_vtable, "sample",
+               "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60",
+               "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8",
+               "3046022100EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716022100F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha384_vtable, "sample",
+               "09F634B188CEFD98E7EC88B1AA9852D734D0BC272F7D2A47DECC6EBEB375AAD4",
+               "0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF77194861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954",
+               "304402200EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF771902204861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha512_vtable, "sample",
+               "5FA81C63109BADB88C1F367B47DA606DA28CAD69AA22C4FE6AD7DF73A7173AA5",
+               "8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F002362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE",
+               "30450221008496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F0002202362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha1_vtable, "test",
+               "8C9520267C55D6B980DF741E56B4ADEE114D84FBFA2E62137954164028632A2E",
+               "0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A8901B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1",
+               "304402200CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89022001B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha224_vtable, "test",
+               "669F4426F2688B8BE0DB3A6BD1989BDAEFFF84B649EEB84F3DD26080F667FAA7",
+               "C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D",
+               "3046022100C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692022100C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha256_vtable, "test",
+               "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0",
+               "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083",
+               "3045022100F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D383670220019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha384_vtable, "test",
+               "16AEFFA357260B04B1DD199693960740066C1A8F3E8EDD79070AA914D361B3B8",
+               "83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB68DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C",
+               "304602210083910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB60221008DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C"
+       },
+       {
+               &EC_P256_PUB,
+               &EC_P256_PRIV,
+               &br_sha512_vtable, "test",
+               "6915D11632ACA3C40D5D51C08DAF9C555933819548784480E93499000D9F0B7F",
+               "461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A0439AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55",
+               "30440220461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04022039AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55"
+       },
+
+       /* Test vectors for P-384, from RFC 6979. */
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha1_vtable, "sample",
+               "4471EF7518BB2C7C20F62EAE1C387AD0C5E8E470995DB4ACF694466E6AB096630F29E5938D25106C3C340045A2DB01A7",
+               "EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA37B9BA002899F6FDA3A4A9386790D4EB2A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF26F49CA031D4857570CCB5CA4424A443",
+               "3066023100EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA37B9BA002899F6FDA3A4A9386790D4EB2023100A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF26F49CA031D4857570CCB5CA4424A443"
+       },
+
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha224_vtable, "sample",
+               "A4E4D2F0E729EB786B31FC20AD5D849E304450E0AE8E3E341134A5C1AFA03CAB8083EE4E3C45B06A5899EA56C51B5879",
+               "42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366450F76EE3DE43F5A125333A6BE0601229DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E4834C082C03D83028EFBF93A3C23940CA8D",
+               "3065023042356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366450F76EE3DE43F5A125333A6BE0601220231009DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E4834C082C03D83028EFBF93A3C23940CA8D"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha256_vtable, "sample",
+               "180AE9F9AEC5438A44BC159A1FCB277C7BE54FA20E7CF404B490650A8ACC414E375572342863C899F9F2EDF9747A9B60",
+               "21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33BDE1E888E63355D92FA2B3C36D8FB2CDF3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEBEFDC63ECCD1AC42EC0CB8668A4FA0AB0",
+               "3065023021B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33BDE1E888E63355D92FA2B3C36D8FB2CD023100F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEBEFDC63ECCD1AC42EC0CB8668A4FA0AB0"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha384_vtable, "sample",
+               "94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9",
+               "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE4699EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8",
+               "306602310094EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE4602310099EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha512_vtable, "sample",
+               "92FC3C7183A883E24216D1141F1A8976C5B0DD797DFA597E3D7B32198BD35331A4E966532593A52980D0E3AAA5E10EC3",
+               "ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799CFE30F35CC900056D7C99CD7882433709512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112DC7CC3EF3446DEFCEB01A45C2667FDD5",
+               "3065023100ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799CFE30F35CC900056D7C99CD78824337090230512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112DC7CC3EF3446DEFCEB01A45C2667FDD5"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha1_vtable, "test",
+               "66CC2C8F4D303FC962E5FF6A27BD79F84EC812DDAE58CF5243B64A4AD8094D47EC3727F3A3C186C15054492E30698497",
+               "4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678ACD9D29876DAF46638645F7F404B11C7D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A2991695BA1C84541327E966FA7B50F7382282",
+               "306502304BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678ACD9D29876DAF46638645F7F404B11C7023100D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A2991695BA1C84541327E966FA7B50F7382282"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha224_vtable, "test",
+               "18FA39DB95AA5F561F30FA3591DC59C0FA3653A80DAFFA0B48D1A4C6DFCBFF6E3D33BE4DC5EB8886A8ECD093F2935726",
+               "E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E62464A9A817C47FF78B8C11066B24080E7207041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C6141C53EA5ABEF0D8231077A04540A96B66",
+               "3065023100E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E62464A9A817C47FF78B8C11066B24080E72023007041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C6141C53EA5ABEF0D8231077A04540A96B66"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha256_vtable, "test",
+               "0CFAC37587532347DC3389FDC98286BBA8C73807285B184C83E62E26C401C0FAA48DD070BA79921A3457ABFF2D630AD7",
+               "6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559F918EEDAF2293BE5B475CC8F0188636B2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D51AB373F9845C0514EEFB14024787265",
+               "306402306D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559F918EEDAF2293BE5B475CC8F0188636B02302D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D51AB373F9845C0514EEFB14024787265"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha384_vtable, "test",
+               "015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA",
+               "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DBDDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5",
+               "30660231008203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB023100DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5"
+       },
+       {
+               &EC_P384_PUB,
+               &EC_P384_PRIV,
+               &br_sha512_vtable, "test",
+               "3780C4F67CB15518B6ACAE34C9F83568D2E12E47DEAB6C50A4E4EE5319D1E8CE0E2CC8A136036DC4B9C00E6888F66B6C",
+               "A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D06FB6495CD21B4B6E340FC236584FB277976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B224634A2092CD3792E0159AD9CEE37659C736",
+               "3066023100A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D06FB6495CD21B4B6E340FC236584FB277023100976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B224634A2092CD3792E0159AD9CEE37659C736"
+       },
+
+       /* Test vectors for P-521, from RFC 6979. */
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha1_vtable, "sample",
+               "0089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9",
+               "00343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D75D00E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5D16",
+               "3081870241343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D75D024200E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5D16"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha224_vtable, "sample",
+               "0121415EC2CD7726330A61F7F3FA5DE14BE9436019C4DB8CB4041F3B54CF31BE0493EE3F427FB906393D895A19C9523F3A1D54BB8702BD4AA9C99DAB2597B92113F3",
+               "01776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A30715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2ED2E0050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17BA41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B41F",
+               "308187024201776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A30715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2ED2E024150CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17BA41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B41F"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha256_vtable, "sample",
+               "00EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0",
+               "01511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E1A7004A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7ECFC",
+               "308187024201511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E1A702414A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7ECFC"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha384_vtable, "sample",
+               "01546A108BC23A15D6F21872F7DED661FA8431DDBD922D0DCDB77CC878C8553FFAD064C95A920A750AC9137E527390D2D92F153E66196966EA554D9ADFCB109C4211",
+               "01EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C6745101F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65D61",
+               "308188024201EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67451024201F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65D61"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha512_vtable, "sample",
+               "01DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3",
+               "00C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA00617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A",
+               "308187024200C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA0241617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha1_vtable, "test",
+               "00BB9F2BF4FE1038CCF4DABD7139A56F6FD8BB1386561BD3C6A4FC818B20DF5DDBA80795A947107A1AB9D12DAA615B1ADE4F7A9DC05E8E6311150F47F5C57CE8B222",
+               "013BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D036701E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC916797FF",
+               "3081880242013BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0367024201E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC916797FF"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha224_vtable, "test",
+               "0040D09FCF3C8A5F62CF4FB223CBBB2B9937F6B0577C27020A99602C25A01136987E452988781484EDBBCF1C47E554E7FC901BC3085E5206D9F619CFF07E73D6F706",
+               "01C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE17FB0177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD519A4",
+               "308188024201C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE17FB02420177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD519A4"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha256_vtable, "test",
+               "001DE74955EFAABC4C4F17F8E84D881D1310B5392D7700275F82F145C61E843841AF09035BF7A6210F5A431A6A9E81C9323354A9E69135D44EBD2FCAA7731B909258",
+               "000E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D8071042EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656AA800CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694E86",
+               "30818702410E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D8071042EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656AA8024200CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694E86"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha384_vtable, "test",
+               "01F1FC4A349A7DA9A9E116BFDD055DC08E78252FF8E23AC276AC88B1770AE0B5DCEB1ED14A4916B769A523CE1E90BA22846AF11DF8B300C38818F713DADD85DE0C88",
+               "014BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF6075578C0133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0ED94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B979",
+               "3081880242014BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF6075578C02420133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0ED94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B979"
+       },
+       {
+               &EC_P521_PUB,
+               &EC_P521_PRIV,
+               &br_sha512_vtable, "test",
+               "016200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D",
+               "013E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D01FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3",
+               "3081880242013E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D024201FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3"
+       },
+
+       /* Terminator for list of test vectors. */
+       {
+               0, 0, 0, 0, 0, 0, 0
+       }
+};
+
+static void
+test_ECDSA_KAT(br_ecdsa_sign sign, br_ecdsa_vrfy vrfy, int asn1)
+{
+       size_t u;
+
+       for (u = 0;; u ++) {
+               const ecdsa_kat_vector *kv;
+               unsigned char hash[64];
+               size_t hash_len;
+               unsigned char sig[150], sig2[150];
+               size_t sig_len, sig2_len;
+               br_hash_compat_context hc;
+
+               kv = &ECDSA_KAT[u];
+               if (kv->pub == 0) {
+                       break;
+               }
+               kv->hf->init(&hc.vtable);
+               kv->hf->update(&hc.vtable, kv->msg, strlen(kv->msg));
+               kv->hf->out(&hc.vtable, hash);
+               hash_len = (kv->hf->desc >> BR_HASHDESC_OUT_OFF)
+                       & BR_HASHDESC_OUT_MASK;
+               if (asn1) {
+                       sig_len = hextobin(sig, kv->sasn1);
+               } else {
+                       sig_len = hextobin(sig, kv->sraw);
+               }
+
+               if (vrfy(&br_ec_prime_i31, hash, hash_len,
+                       kv->pub, sig, sig_len) != 1)
+               {
+                       fprintf(stderr, "ECDSA KAT verify failed (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+               hash[0] ^= 0x80;
+               if (vrfy(&br_ec_prime_i31, hash, hash_len,
+                       kv->pub, sig, sig_len) != 0)
+               {
+                       fprintf(stderr, "ECDSA KAT verify shoud have failed\n");
+                       exit(EXIT_FAILURE);
+               }
+               hash[0] ^= 0x80;
+               if (vrfy(&br_ec_prime_i31, hash, hash_len,
+                       kv->pub, sig, sig_len) != 1)
+               {
+                       fprintf(stderr, "ECDSA KAT verify failed (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               sig2_len = sign(&br_ec_prime_i31, kv->hf, hash, kv->priv, sig2);
+               if (sig2_len == 0) {
+                       fprintf(stderr, "ECDSA KAT sign failed\n");
+                       exit(EXIT_FAILURE);
+               }
+               if (sig2_len != sig_len || memcmp(sig, sig2, sig_len) != 0) {
+                       fprintf(stderr, "ECDSA KAT wrong signature value\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               printf(".");
+               fflush(stdout);
+       }
+}
+
+static void
+test_ECDSA_i31(void)
+{
+       printf("Test ECDSA/i31: ");
+       fflush(stdout);
+       printf("[raw]");
+       fflush(stdout);
+       test_ECDSA_KAT(&br_ecdsa_i31_sign_raw, &br_ecdsa_i31_vrfy_raw, 0);
+       printf(" [asn1]");
+       fflush(stdout);
+       test_ECDSA_KAT(&br_ecdsa_i31_sign_asn1, &br_ecdsa_i31_vrfy_asn1, 1);
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+static int
+eq_name(const char *s1, const char *s2)
+{
+       for (;;) {
+               int c1, c2;
+
+               for (;;) {
+                       c1 = *s1 ++;
+                       if (c1 >= 'A' && c1 <= 'Z') {
+                               c1 += 'a' - 'A';
+                       } else {
+                               switch (c1) {
+                               case '-': case '_': case '.': case ' ':
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               for (;;) {
+                       c2 = *s2 ++;
+                       if (c2 >= 'A' && c2 <= 'Z') {
+                               c2 += 'a' - 'A';
+                       } else {
+                               switch (c2) {
+                               case '-': case '_': case '.': case ' ':
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               if (c1 != c2) {
+                       return 0;
+               }
+               if (c1 == 0) {
+                       return 1;
+               }
+       }
+}
+
+#define STU(x)   { &test_ ## x, #x }
+
+static const struct {
+       void (*fn)(void);
+       char *name;
+} tfns[] = {
+       STU(MD5),
+       STU(SHA1),
+       STU(SHA224),
+       STU(SHA256),
+       STU(SHA384),
+       STU(SHA512),
+       STU(MD5_SHA1),
+       STU(multihash),
+       STU(HMAC),
+       STU(HMAC_DRBG),
+       STU(PRF),
+       STU(AES_big),
+       STU(AES_small),
+       STU(AES_ct),
+       STU(AES_ct64),
+       STU(DES_tab),
+       STU(DES_ct),
+       STU(RSA_i31),
+       STU(RSA_i32),
+       STU(GHASH_ctmul),
+       STU(GHASH_ctmul32),
+       STU(GHASH_ctmul64),
+       STU(EC_prime_i31),
+       /* STU(EC_prime_i32), */
+       STU(ECDSA_i31),
+       { 0, 0 }
+};
+
+int
+main(int argc, char *argv[])
+{
+       size_t u;
+
+       if (argc <= 1) {
+               printf("usage: testcrypto all | name...\n");
+               printf("individual test names:\n");
+               for (u = 0; tfns[u].name; u ++) {
+                       printf("   %s\n", tfns[u].name);
+               }
+       } else {
+               for (u = 0; tfns[u].name; u ++) {
+                       int i;
+
+                       for (i = 1; i < argc; i ++) {
+                               if (eq_name(argv[i], tfns[u].name)
+                                       || eq_name(argv[i], "all"))
+                               {
+                                       tfns[u].fn();
+                                       break;
+                               }
+                       }
+               }
+       }
+       return 0;
+}
diff --git a/test/test_math.c b/test/test_math.c
new file mode 100644 (file)
index 0000000..b36f9f7
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <gmp.h>
+
+#include "bearssl.h"
+#include "inner.h"
+
+/*
+ * Pointers to implementations.
+ */
+typedef struct {
+       uint32_t word_size;
+       void (*zero)(uint32_t *x, uint32_t bit_len);
+       void (*decode)(uint32_t *x, const void *src, size_t len);
+       uint32_t (*decode_mod)(uint32_t *x,
+               const void *src, size_t len, const uint32_t *m);
+       void (*reduce)(uint32_t *x, const uint32_t *a, const uint32_t *m);
+       void (*decode_reduce)(uint32_t *x,
+               const void *src, size_t len, const uint32_t *m);
+       void (*encode)(void *dst, size_t len, const uint32_t *x);
+       uint32_t (*add)(uint32_t *a, const uint32_t *b, uint32_t ctl);
+       uint32_t (*sub)(uint32_t *a, const uint32_t *b, uint32_t ctl);
+       uint32_t (*ninv)(uint32_t x);
+       void (*montymul)(uint32_t *d, const uint32_t *x, const uint32_t *y,
+               const uint32_t *m, uint32_t m0i);
+       void (*to_monty)(uint32_t *x, const uint32_t *m);
+       void (*from_monty)(uint32_t *x, const uint32_t *m, uint32_t m0i);
+       void (*modpow)(uint32_t *x, const unsigned char *e, size_t elen,
+               const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2);
+} int_impl;
+
+static const int_impl i31_impl = {
+       31,
+       &br_i31_zero,
+       &br_i31_decode,
+       &br_i31_decode_mod,
+       &br_i31_reduce,
+       &br_i31_decode_reduce,
+       &br_i31_encode,
+       &br_i31_add,
+       &br_i31_sub,
+       &br_i31_ninv31,
+       &br_i31_montymul,
+       &br_i31_to_monty,
+       &br_i31_from_monty,
+       &br_i31_modpow
+};
+static const int_impl i32_impl = {
+       32,
+       &br_i32_zero,
+       &br_i32_decode,
+       &br_i32_decode_mod,
+       &br_i32_reduce,
+       &br_i32_decode_reduce,
+       &br_i32_encode,
+       &br_i32_add,
+       &br_i32_sub,
+       &br_i32_ninv32,
+       &br_i32_montymul,
+       &br_i32_to_monty,
+       &br_i32_from_monty,
+       &br_i32_modpow
+};
+
+static const int_impl *impl;
+
+static gmp_randstate_t RNG;
+
+/*
+ * Get a random prime of length 'size' bits. This function also guarantees
+ * that x-1 is not a multiple of 65537.
+ */
+static void
+rand_prime(mpz_t x, int size)
+{
+       for (;;) {
+               mpz_urandomb(x, RNG, size - 1);
+               mpz_setbit(x, 0);
+               mpz_setbit(x, size - 1);
+               if (mpz_probab_prime_p(x, 50)) {
+                       mpz_sub_ui(x, x, 1);
+                       if (mpz_divisible_ui_p(x, 65537)) {
+                               continue;
+                       }
+                       mpz_add_ui(x, x, 1);
+                       return;
+               }
+       }
+}
+
+/*
+ * Print out a GMP integer (for debug).
+ */
+static void
+print_z(mpz_t z)
+{
+       unsigned char zb[1000];
+       size_t zlen, k;
+
+       mpz_export(zb, &zlen, 1, 1, 0, 0, z);
+       if (zlen == 0) {
+               printf(" 00");
+               return;
+       }
+       if ((zlen & 3) != 0) {
+               k = 4 - (zlen & 3);
+               memmove(zb + k, zb, zlen);
+               memset(zb, 0, k);
+               zlen += k;
+       }
+       for (k = 0; k < zlen; k += 4) {
+               printf(" %02X%02X%02X%02X",
+                       zb[k], zb[k + 1], zb[k + 2], zb[k + 3]);
+       }
+}
+
+/*
+ * Print out an i31 or i32 integer (for debug).
+ */
+static void
+print_u(uint32_t *x)
+{
+       size_t k;
+
+       if (x[0] == 0) {
+               printf(" 00000000 (0, 0)");
+               return;
+       }
+       for (k = (x[0] + 31) >> 5; k > 0; k --) {
+               printf(" %08lX", (unsigned long)x[k]);
+       }
+       printf(" (%u, %u)", (unsigned)(x[0] >> 5), (unsigned)(x[0] & 31));
+}
+
+/*
+ * Check that an i31/i32 number and a GMP number are equal.
+ */
+static void
+check_eqz(uint32_t *x, mpz_t z)
+{
+       unsigned char xb[1000];
+       unsigned char zb[1000];
+       size_t xlen, zlen;
+       int good;
+
+       xlen = ((x[0] + 31) & ~(uint32_t)31) >> 3;
+       impl->encode(xb, xlen, x);
+       mpz_export(zb, &zlen, 1, 1, 0, 0, z);
+       good = 1;
+       if (xlen < zlen) {
+               good = 0;
+       } else if (xlen > zlen) {
+               size_t u;
+
+               for (u = xlen; u > zlen; u --) {
+                       if (xb[xlen - u] != 0) {
+                               good = 0;
+                               break;
+                       }
+               }
+       }
+       good = good && memcmp(xb + xlen - zlen, zb, zlen) == 0;
+       if (!good) {
+               size_t u;
+
+               printf("Mismatch:\n");
+               printf("  x = ");
+               print_u(x);
+               printf("\n");
+               printf("  ex = ");
+               for (u = 0; u < xlen; u ++) {
+                       printf("%02X", xb[u]);
+               }
+               printf("\n");
+               printf("  z = ");
+               print_z(z);
+               printf("\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+/* obsolete
+static void
+mp_to_br(uint32_t *mx, uint32_t x_bitlen, mpz_t x)
+{
+       uint32_t x_ebitlen;
+       size_t xlen;
+
+       if (mpz_sizeinbase(x, 2) > x_bitlen) {
+               abort();
+       }
+       x_ebitlen = ((x_bitlen / 31) << 5) + (x_bitlen % 31);
+       br_i31_zero(mx, x_ebitlen);
+       mpz_export(mx + 1, &xlen, -1, sizeof *mx, 0, 1, x);
+}
+*/
+
+static void
+test_modint(void)
+{
+       int i, j, k;
+       mpz_t p, a, b, v, t1;
+
+       printf("Test modular integers: ");
+       fflush(stdout);
+
+       gmp_randinit_mt(RNG);
+       mpz_init(p);
+       mpz_init(a);
+       mpz_init(b);
+       mpz_init(v);
+       mpz_init(t1);
+       mpz_set_ui(t1, (unsigned long)time(NULL));
+       gmp_randseed(RNG, t1);
+       for (k = 2; k <= 128; k ++) {
+               for (i = 0; i < 10; i ++) {
+                       unsigned char ep[100], ea[100], eb[100], ev[100];
+                       size_t plen, alen, blen, vlen;
+                       uint32_t mp[40], ma[40], mb[40], mv[60], mx[100];
+                       uint32_t mt1[40], mt2[40], mt3[40];
+                       uint32_t ctl;
+                       uint32_t mp0i;
+
+                       rand_prime(p, k);
+                       mpz_urandomm(a, RNG, p);
+                       mpz_urandomm(b, RNG, p);
+                       mpz_urandomb(v, RNG, k + 60);
+                       if (mpz_sgn(b) == 0) {
+                               mpz_set_ui(b, 1);
+                       }
+                       mpz_export(ep, &plen, 1, 1, 0, 0, p);
+                       mpz_export(ea, &alen, 1, 1, 0, 0, a);
+                       mpz_export(eb, &blen, 1, 1, 0, 0, b);
+                       mpz_export(ev, &vlen, 1, 1, 0, 0, v);
+
+                       impl->decode(mp, ep, plen);
+                       if (impl->decode_mod(ma, ea, alen, mp) != 1) {
+                               printf("Decode error\n");
+                               printf("  ea = ");
+                               print_z(a);
+                               printf("\n");
+                               printf("  p = ");
+                               print_u(mp);
+                               printf("\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       mp0i = impl->ninv(mp[1]);
+                       if (impl->decode_mod(mb, eb, blen, mp) != 1) {
+                               printf("Decode error\n");
+                               printf("  eb = ");
+                               print_z(b);
+                               printf("\n");
+                               printf("  p = ");
+                               print_u(mp);
+                               printf("\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       impl->decode(mv, ev, vlen);
+                       check_eqz(mp, p);
+                       check_eqz(ma, a);
+                       check_eqz(mb, b);
+                       check_eqz(mv, v);
+
+                       impl->decode_mod(ma, ea, alen, mp);
+                       impl->decode_mod(mb, eb, blen, mp);
+                       ctl = impl->add(ma, mb, 1);
+                       ctl |= impl->sub(ma, mp, 0) ^ (uint32_t)1;
+                       impl->sub(ma, mp, ctl);
+                       mpz_add(t1, a, b);
+                       mpz_mod(t1, t1, p);
+                       check_eqz(ma, t1);
+
+                       impl->decode_mod(ma, ea, alen, mp);
+                       impl->decode_mod(mb, eb, blen, mp);
+                       impl->add(ma, mp, impl->sub(ma, mb, 1));
+                       mpz_sub(t1, a, b);
+                       mpz_mod(t1, t1, p);
+                       check_eqz(ma, t1);
+
+                       impl->decode_reduce(ma, ev, vlen, mp);
+                       mpz_mod(t1, v, p);
+                       check_eqz(ma, t1);
+
+                       impl->decode(mv, ev, vlen);
+                       impl->reduce(ma, mv, mp);
+                       mpz_mod(t1, v, p);
+                       check_eqz(ma, t1);
+
+                       impl->decode_mod(ma, ea, alen, mp);
+                       impl->to_monty(ma, mp);
+                       mpz_mul_2exp(t1, a, ((k + impl->word_size - 1)
+                               / impl->word_size) * impl->word_size);
+                       mpz_mod(t1, t1, p);
+                       check_eqz(ma, t1);
+                       impl->from_monty(ma, mp, mp0i);
+                       check_eqz(ma, a);
+
+                       impl->decode_mod(ma, ea, alen, mp);
+                       impl->decode_mod(mb, eb, blen, mp);
+                       impl->to_monty(ma, mp);
+                       impl->montymul(mt1, ma, mb, mp, mp0i);
+                       mpz_mul(t1, a, b);
+                       mpz_mod(t1, t1, p);
+                       check_eqz(mt1, t1);
+
+                       impl->decode_mod(ma, ea, alen, mp);
+                       impl->modpow(ma, ev, vlen, mp, mp0i, mt1, mt2);
+                       mpz_powm(t1, a, v, p);
+                       check_eqz(ma, t1);
+
+                       /*
+                       br_modint_decode(ma, mp, ea, alen);
+                       br_modint_decode(mb, mp, eb, blen);
+                       if (!br_modint_div(ma, mb, mp, mt1, mt2, mt3)) {
+                               fprintf(stderr, "division failed\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       mpz_sub_ui(t1, p, 2);
+                       mpz_powm(t1, b, t1, p);
+                       mpz_mul(t1, a, t1);
+                       mpz_mod(t1, t1, p);
+                       check_eqz(ma, t1);
+
+                       br_modint_decode(ma, mp, ea, alen);
+                       br_modint_decode(mb, mp, eb, blen);
+                       for (j = 0; j <= (2 * k + 5); j ++) {
+                               br_int_add(mx, j, ma, mb);
+                               mpz_add(t1, a, b);
+                               mpz_tdiv_r_2exp(t1, t1, j);
+                               check_eqz(mx, t1);
+
+                               br_int_mul(mx, j, ma, mb);
+                               mpz_mul(t1, a, b);
+                               mpz_tdiv_r_2exp(t1, t1, j);
+                               check_eqz(mx, t1);
+                       }
+                       */
+               }
+               printf(".");
+               fflush(stdout);
+       }
+       mpz_clear(p);
+       mpz_clear(a);
+       mpz_clear(b);
+       mpz_clear(v);
+       mpz_clear(t1);
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+#if 0
+static void
+test_RSA_core(void)
+{
+       int i, j, k;
+       mpz_t n, e, d, p, q, dp, dq, iq, t1, t2, phi;
+
+       printf("Test RSA core: ");
+       fflush(stdout);
+
+       gmp_randinit_mt(RNG);
+       mpz_init(n);
+       mpz_init(e);
+       mpz_init(d);
+       mpz_init(p);
+       mpz_init(q);
+       mpz_init(dp);
+       mpz_init(dq);
+       mpz_init(iq);
+       mpz_init(t1);
+       mpz_init(t2);
+       mpz_init(phi);
+       mpz_set_ui(t1, (unsigned long)time(NULL));
+       gmp_randseed(RNG, t1);
+
+       /*
+        * To test corner cases, we want to try RSA keys such that the
+        * lengths of both factors can be arbitrary modulo 2^32. Factors
+        * p and q need not be of the same length; p can be greater than
+        * q and q can be greater than p.
+        *
+        * To keep computation time reasonable, we use p and q factors of
+        * less than 128 bits; this is way too small for secure RSA,
+        * but enough to exercise all code paths (since we work only with
+        * 32-bit words).
+        */
+       for (i = 64; i <= 96; i ++) {
+               rand_prime(p, i);
+               for (j = i - 33; j <= i + 33; j ++) {
+                       uint32_t mp[40], mq[40], mdp[40], mdq[40], miq[40];
+
+                       /*
+                        * Generate a RSA key pair, with p of length i bits,
+                        * and q of length j bits.
+                        */
+                       do {
+                               rand_prime(q, j);
+                       } while (mpz_cmp(p, q) == 0);
+                       mpz_mul(n, p, q);
+                       mpz_set_ui(e, 65537);
+                       mpz_sub_ui(t1, p, 1);
+                       mpz_sub_ui(t2, q, 1);
+                       mpz_mul(phi, t1, t2);
+                       mpz_invert(d, e, phi);
+                       mpz_mod(dp, d, t1);
+                       mpz_mod(dq, d, t2);
+                       mpz_invert(iq, q, p);
+
+                       /*
+                        * Convert the key pair elements to BearSSL arrays.
+                        */
+                       mp_to_br(mp, mpz_sizeinbase(p, 2), p);
+                       mp_to_br(mq, mpz_sizeinbase(q, 2), q);
+                       mp_to_br(mdp, mpz_sizeinbase(dp, 2), dp);
+                       mp_to_br(mdq, mpz_sizeinbase(dq, 2), dq);
+                       mp_to_br(miq, mp[0], iq);
+
+                       /*
+                        * Compute and check ten public/private operations.
+                        */
+                       for (k = 0; k < 10; k ++) {
+                               uint32_t mx[40];
+
+                               mpz_urandomm(t1, RNG, n);
+                               mpz_powm(t2, t1, e, n);
+                               mp_to_br(mx, mpz_sizeinbase(n, 2), t2);
+                               br_rsa_private_core(mx, mp, mq, mdp, mdq, miq);
+                               check_eqz(mx, t1);
+                       }
+               }
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+#endif
+
+int
+main(void)
+{
+       printf("===== i32 ======\n");
+       impl = &i32_impl;
+       test_modint();
+       printf("===== i31 ======\n");
+       impl = &i31_impl;
+       test_modint();
+       /*
+       test_RSA_core();
+       */
+       return 0;
+}
diff --git a/test/test_speed.c b/test/test_speed.c
new file mode 100644 (file)
index 0000000..b4049fe
--- /dev/null
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "inner.h"
+
+#define HASH_SIZE(cname)   br_ ## cname ## _SIZE
+
+#define SPEED_HASH(Name, cname) \
+static void \
+test_speed_ ## cname(void) \
+{ \
+       unsigned char buf[8192]; \
+       unsigned char tmp[HASH_SIZE(cname)]; \
+       br_ ## cname ## _context mc; \
+       int i; \
+       long num; \
+ \
+       memset(buf, 'T', sizeof buf); \
+       for (i = 0; i < 10; i ++) { \
+               br_ ## cname ## _init(&mc); \
+               br_ ## cname ## _update(&mc, buf, sizeof buf); \
+               br_ ## cname ## _out(&mc, tmp); \
+       } \
+       num = 10; \
+       for (;;) { \
+               clock_t begin, end; \
+               double tt; \
+               long k; \
+ \
+               br_ ## cname ## _init(&mc); \
+               begin = clock(); \
+               for (k = num; k > 0; k --) { \
+                       br_ ## cname ## _update(&mc, buf, sizeof buf); \
+               } \
+               end = clock(); \
+               br_ ## cname ## _out(&mc, tmp); \
+               tt = (double)(end - begin) / CLOCKS_PER_SEC; \
+               if (tt >= 2.0) { \
+                       printf("%-30s %8.2f MB/s\n", #Name, \
+                               ((double)sizeof buf) * (double)num \
+                               / (tt * 1000000.0)); \
+                       fflush(stdout); \
+                       return; \
+               } \
+               num <<= 1; \
+       } \
+}
+
+#define BLOCK_SIZE(cname)   br_ ## cname ## _BLOCK_SIZE
+
+#define SPEED_BLOCKCIPHER_CBC(Name, fname, cname, klen, dir) \
+static void \
+test_speed_ ## fname(void) \
+{ \
+       unsigned char key[klen]; \
+       unsigned char buf[8192 - (8192 % BLOCK_SIZE(cname))]; \
+       unsigned char iv[BLOCK_SIZE(cname)]; \
+       const br_block_cbc ## dir ## _class *vt; \
+       br_ ## cname ## _cbc ## dir ## _keys ec; \
+       int i; \
+       long num; \
+ \
+       memset(key, 'T', sizeof key); \
+       memset(buf, 'P', sizeof buf); \
+       memset(iv, 'X', sizeof iv); \
+       vt = &br_ ## cname ## _cbc ## dir ## _vtable; \
+       for (i = 0; i < 10; i ++) { \
+               vt->init(&ec.vtable, key, sizeof key); \
+               vt->run(&ec.vtable, iv, buf, sizeof buf); \
+       } \
+       num = 10; \
+       for (;;) { \
+               clock_t begin, end; \
+               double tt; \
+               long k; \
+ \
+               vt->init(&ec.vtable, key, sizeof key); \
+               begin = clock(); \
+               for (k = num; k > 0; k --) { \
+                       vt->run(&ec.vtable, iv, buf, sizeof buf); \
+               } \
+               end = clock(); \
+               tt = (double)(end - begin) / CLOCKS_PER_SEC; \
+               if (tt >= 2.0) { \
+                       printf("%-30s %8.2f MB/s\n", #Name, \
+                               ((double)sizeof buf) * (double)num \
+                               / (tt * 1000000.0)); \
+                       fflush(stdout); \
+                       return; \
+               } \
+               num <<= 1; \
+       } \
+}
+
+#define SPEED_BLOCKCIPHER_CTR(Name, fname, cname, klen) \
+static void \
+test_speed_ ## fname(void) \
+{ \
+       unsigned char key[klen]; \
+       unsigned char buf[8192 - (8192 % BLOCK_SIZE(cname))]; \
+       unsigned char iv[BLOCK_SIZE(cname) - 4]; \
+       const br_block_ctr_class *vt; \
+       br_ ## cname ## _ctr_keys ec; \
+       int i; \
+       long num; \
+ \
+       memset(key, 'T', sizeof key); \
+       memset(buf, 'P', sizeof buf); \
+       memset(iv, 'X', sizeof iv); \
+       vt = &br_ ## cname ## _ctr_vtable; \
+       for (i = 0; i < 10; i ++) { \
+               vt->init(&ec.vtable, key, sizeof key); \
+               vt->run(&ec.vtable, iv, 1, buf, sizeof buf); \
+       } \
+       num = 10; \
+       for (;;) { \
+               clock_t begin, end; \
+               double tt; \
+               long k; \
+ \
+               vt->init(&ec.vtable, key, sizeof key); \
+               begin = clock(); \
+               for (k = num; k > 0; k --) { \
+                       vt->run(&ec.vtable, iv, 1, buf, sizeof buf); \
+               } \
+               end = clock(); \
+               tt = (double)(end - begin) / CLOCKS_PER_SEC; \
+               if (tt >= 2.0) { \
+                       printf("%-30s %8.2f MB/s\n", #Name, \
+                               ((double)sizeof buf) * (double)num \
+                               / (tt * 1000000.0)); \
+                       fflush(stdout); \
+                       return; \
+               } \
+               num <<= 1; \
+       } \
+}
+
+SPEED_HASH(MD5, md5)
+SPEED_HASH(SHA-1, sha1)
+SPEED_HASH(SHA-256, sha256)
+SPEED_HASH(SHA-512, sha512)
+
+#define SPEED_AES(iname) \
+SPEED_BLOCKCIPHER_CBC(AES-128 CBC encrypt (iname), aes128_ ## iname ## _cbcenc, aes_ ## iname, 16, enc) \
+SPEED_BLOCKCIPHER_CBC(AES-128 CBC decrypt (iname), aes128_ ## iname ## _cbcdec, aes_ ## iname, 16, dec) \
+SPEED_BLOCKCIPHER_CBC(AES-192 CBC encrypt (iname), aes192_ ## iname ## _cbcenc, aes_ ## iname, 24, enc) \
+SPEED_BLOCKCIPHER_CBC(AES-192 CBC decrypt (iname), aes192_ ## iname ## _cbcdec, aes_ ## iname, 24, dec) \
+SPEED_BLOCKCIPHER_CBC(AES-256 CBC encrypt (iname), aes256_ ## iname ## _cbcenc, aes_ ## iname, 32, enc) \
+SPEED_BLOCKCIPHER_CBC(AES-256 CBC decrypt (iname), aes256_ ## iname ## _cbcdec, aes_ ## iname, 32, dec) \
+SPEED_BLOCKCIPHER_CTR(AES-128 CTR (iname), aes128_ ## iname ## _ctr, aes_ ## iname, 16) \
+SPEED_BLOCKCIPHER_CTR(AES-192 CTR (iname), aes192_ ## iname ## _ctr, aes_ ## iname, 24) \
+SPEED_BLOCKCIPHER_CTR(AES-256 CTR (iname), aes256_ ## iname ## _ctr, aes_ ## iname, 32)
+
+SPEED_AES(big)
+SPEED_AES(small)
+SPEED_AES(ct)
+SPEED_AES(ct64)
+
+#define SPEED_DES(iname) \
+SPEED_BLOCKCIPHER_CBC(DES CBC encrypt (iname), des_ ## iname ## _cbcenc, des_ ## iname, 8, enc) \
+SPEED_BLOCKCIPHER_CBC(DES CBC decrypt (iname), des_ ## iname ## _cbcdec, des_ ## iname, 8, dec) \
+SPEED_BLOCKCIPHER_CBC(3DES CBC encrypt (iname), 3des_ ## iname ## _cbcenc, des_ ## iname, 24, enc) \
+SPEED_BLOCKCIPHER_CBC(3DES CBC decrypt (iname), 3des_ ## iname ## _cbcdec, des_ ## iname, 24, dec)
+
+SPEED_DES(tab)
+SPEED_DES(ct)
+
+static void
+test_speed_ghash_inner(char *name, br_ghash gh)
+{
+       unsigned char buf[8192], h[16], y[16];
+       int i;
+       long num;
+
+       memset(buf, 'T', sizeof buf);
+       memset(h, 'P', sizeof h);
+       memset(y, 0, sizeof y);
+       for (i = 0; i < 10; i ++) {
+               gh(y, h, buf, sizeof buf);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       gh(y, h, buf, sizeof buf);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f MB/s\n", name,
+                               ((double)sizeof buf) * (double)num
+                               / (tt * 1000000.0));
+                       fflush(stdout);
+                       return;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_ghash_ctmul(void)
+{
+       test_speed_ghash_inner("GHASH (ctmul)", &br_ghash_ctmul);
+}
+
+static void
+test_speed_ghash_ctmul32(void)
+{
+       test_speed_ghash_inner("GHASH (ctmul32)", &br_ghash_ctmul32);
+}
+
+static void
+test_speed_ghash_ctmul64(void)
+{
+       test_speed_ghash_inner("GHASH (ctmul64)", &br_ghash_ctmul64);
+}
+
+static const unsigned char RSA_N[] = {
+       0xE9, 0xF2, 0x4A, 0x2F, 0x96, 0xDF, 0x0A, 0x23,
+       0x01, 0x85, 0xF1, 0x2C, 0xB2, 0xA8, 0xEF, 0x23,
+       0xCE, 0x2E, 0xB0, 0x4E, 0x18, 0x31, 0x95, 0x5B,
+       0x98, 0x2D, 0x9B, 0x8C, 0xE3, 0x1A, 0x2B, 0x96,
+       0xB5, 0xC7, 0xEE, 0xED, 0x72, 0x43, 0x2D, 0xFE,
+       0x7F, 0x61, 0x33, 0xEA, 0x14, 0xFC, 0xDE, 0x80,
+       0x17, 0x42, 0xF0, 0xF3, 0xC3, 0xC7, 0x89, 0x47,
+       0x76, 0x5B, 0xFA, 0x33, 0xC4, 0x8C, 0x94, 0xDE,
+       0x6A, 0x75, 0xD8, 0x1A, 0xF4, 0x49, 0xBC, 0xF3,
+       0xB7, 0x9E, 0x2C, 0x8D, 0xEC, 0x5A, 0xEE, 0xBF,
+       0x4B, 0x5A, 0x7F, 0xEF, 0x21, 0x39, 0xDB, 0x1D,
+       0x83, 0x5E, 0x7E, 0x2F, 0xAA, 0x5E, 0xBA, 0x28,
+       0xC3, 0xA2, 0x53, 0x19, 0xFB, 0x2F, 0x78, 0x6B,
+       0x14, 0x60, 0x49, 0x3C, 0xCC, 0x1B, 0xE9, 0x1E,
+       0x3D, 0x10, 0xA4, 0xEB, 0x7F, 0x66, 0x98, 0xF6,
+       0xC3, 0xAC, 0x35, 0xF5, 0x01, 0x84, 0xFF, 0x7D,
+       0x1F, 0x72, 0xBE, 0xB4, 0xD1, 0x89, 0xC8, 0xDD,
+       0x44, 0xE7, 0xB5, 0x2E, 0x2C, 0xE1, 0x85, 0xF5,
+       0x15, 0x50, 0xA9, 0x08, 0xC7, 0x67, 0xD9, 0x2B,
+       0x6C, 0x11, 0xB3, 0xEB, 0x28, 0x8D, 0xF4, 0xCC,
+       0xE3, 0xC3, 0xC5, 0x04, 0x0E, 0x7C, 0x8D, 0xDB,
+       0x39, 0x06, 0x6A, 0x74, 0x75, 0xDF, 0xA8, 0x0F,
+       0xDA, 0x67, 0x5A, 0x73, 0x1E, 0xFD, 0x8E, 0x4C,
+       0xEE, 0x17, 0xEE, 0x1E, 0x67, 0xDB, 0x98, 0x70,
+       0x60, 0xF7, 0xB9, 0xB5, 0x1F, 0x19, 0x93, 0xD6,
+       0x3F, 0x2F, 0x1F, 0xB6, 0x5B, 0x59, 0xAA, 0x85,
+       0xBB, 0x25, 0xE4, 0x13, 0xEF, 0xE7, 0xB9, 0x87,
+       0x9C, 0x3F, 0x5E, 0xE4, 0x08, 0xA3, 0x51, 0xCF,
+       0x8B, 0xAD, 0xF4, 0xE6, 0x1A, 0x5F, 0x51, 0xDD,
+       0xA8, 0xBE, 0xE8, 0xD1, 0x20, 0x19, 0x61, 0x6C,
+       0x18, 0xAB, 0xCA, 0x0A, 0xD9, 0x82, 0xA6, 0x94,
+       0xD5, 0x69, 0x2A, 0xF6, 0x43, 0x66, 0x31, 0x09
+};
+
+static const unsigned char RSA_E[] = {
+       0x01, 0x00, 0x01
+};
+
+static const unsigned char RSA_P[] = {
+       0xFD, 0x39, 0x40, 0x56, 0x20, 0x80, 0xC5, 0x81,
+       0x4C, 0x5F, 0x0C, 0x1A, 0x52, 0x84, 0x03, 0x2F,
+       0xCE, 0x82, 0xB0, 0xD8, 0x30, 0x23, 0x7F, 0x77,
+       0x45, 0xC2, 0x01, 0xC4, 0x68, 0x96, 0x0D, 0xA7,
+       0x22, 0xA9, 0x6C, 0xA9, 0x1A, 0x33, 0xE5, 0x2F,
+       0xB5, 0x07, 0x9A, 0xF9, 0xEA, 0x33, 0xA5, 0xC8,
+       0x96, 0x60, 0x6A, 0xCA, 0xEB, 0xE5, 0x6E, 0x09,
+       0x46, 0x7E, 0x2D, 0xEF, 0x93, 0x7D, 0x56, 0xED,
+       0x75, 0x70, 0x3B, 0x96, 0xC4, 0xD5, 0xDB, 0x0B,
+       0x3F, 0x69, 0xDF, 0x06, 0x18, 0x76, 0xF4, 0xCF,
+       0xF8, 0x84, 0x22, 0xDF, 0xBD, 0x71, 0x62, 0x7B,
+       0x67, 0x99, 0xBC, 0x09, 0x95, 0x54, 0xA4, 0x98,
+       0x83, 0xF5, 0xA9, 0xCF, 0x09, 0xA5, 0x1F, 0x61,
+       0x25, 0xB4, 0x70, 0x6C, 0x91, 0xB8, 0xB3, 0xD0,
+       0xCE, 0x9C, 0x45, 0x65, 0x9B, 0xEF, 0xD4, 0x70,
+       0xBE, 0x86, 0xD2, 0x98, 0x5D, 0xEB, 0xE3, 0xFF
+};
+
+static const unsigned char RSA_Q[] = {
+       0xEC, 0x82, 0xEE, 0x63, 0x5F, 0x40, 0x52, 0xDB,
+       0x38, 0x7A, 0x37, 0x6A, 0x54, 0x5B, 0xD9, 0xA0,
+       0x73, 0xB4, 0xBB, 0x52, 0xB2, 0x84, 0x07, 0xD0,
+       0xCC, 0x82, 0x0D, 0x20, 0xB3, 0xFA, 0xD5, 0xB6,
+       0x25, 0x92, 0x35, 0x4D, 0xB4, 0xC7, 0x36, 0x48,
+       0xCE, 0x5E, 0x21, 0x4A, 0xA6, 0x74, 0x65, 0xF4,
+       0x7D, 0x1D, 0xBC, 0x3B, 0xE2, 0xF4, 0x3E, 0x11,
+       0x58, 0x10, 0x6C, 0x04, 0x46, 0x9E, 0x8D, 0x57,
+       0xE0, 0x04, 0xE2, 0xEC, 0x47, 0xCF, 0xB3, 0x2A,
+       0xFD, 0x4C, 0x55, 0x18, 0xDB, 0xDE, 0x3B, 0xDC,
+       0xF4, 0x5B, 0xDA, 0xF3, 0x1A, 0xC8, 0x41, 0x6F,
+       0x73, 0x3B, 0xFE, 0x3C, 0xA0, 0xDB, 0xBA, 0x6E,
+       0x65, 0xA5, 0xE8, 0x02, 0xA5, 0x6C, 0xEA, 0x03,
+       0xF6, 0x99, 0xF7, 0xCB, 0x4B, 0xB7, 0x11, 0x51,
+       0x93, 0x88, 0x3F, 0xF9, 0x06, 0x85, 0xA9, 0x1E,
+       0xCA, 0x64, 0xF8, 0x11, 0xA5, 0x1A, 0xCA, 0xF7
+};
+
+static const unsigned char RSA_DP[] = {
+       0x77, 0x95, 0xE0, 0x02, 0x4C, 0x9B, 0x43, 0xAA,
+       0xCA, 0x4C, 0x60, 0xC4, 0xD5, 0x8F, 0x2E, 0x8A,
+       0x17, 0x36, 0xB5, 0x19, 0x83, 0xB2, 0x5F, 0xF2,
+       0x0D, 0xE9, 0x8F, 0x38, 0x18, 0x44, 0x34, 0xF2,
+       0x67, 0x76, 0x27, 0xB0, 0xBC, 0x85, 0x21, 0x89,
+       0x24, 0x2F, 0x11, 0x4B, 0x51, 0x05, 0x4F, 0x17,
+       0xA9, 0x9C, 0xA3, 0x12, 0x6D, 0xD1, 0x0D, 0xE4,
+       0x27, 0x7C, 0x53, 0x69, 0x3E, 0xF8, 0x04, 0x63,
+       0x64, 0x00, 0xBA, 0xC3, 0x7A, 0xF5, 0x9B, 0xDA,
+       0x75, 0xFA, 0x23, 0xAF, 0x17, 0x42, 0xA6, 0x5E,
+       0xC8, 0xF8, 0x6E, 0x17, 0xC7, 0xB9, 0x92, 0x4E,
+       0xC1, 0x20, 0x63, 0x23, 0x0B, 0x78, 0xCB, 0xBA,
+       0x93, 0x27, 0x23, 0x28, 0x79, 0x5F, 0x97, 0xB0,
+       0x23, 0x44, 0x51, 0x8B, 0x94, 0x4D, 0xEB, 0xED,
+       0x82, 0x85, 0x5E, 0x68, 0x9B, 0xF9, 0xE9, 0x13,
+       0xCD, 0x86, 0x92, 0x52, 0x0E, 0x98, 0xE6, 0x35
+};
+
+static const unsigned char RSA_DQ[] = {
+       0xD8, 0xDD, 0x71, 0xB3, 0x62, 0xBA, 0xBB, 0x7E,
+       0xD1, 0xF9, 0x96, 0xE8, 0x83, 0xB3, 0xB9, 0x08,
+       0x9C, 0x30, 0x03, 0x77, 0xDF, 0xC2, 0x9A, 0xDC,
+       0x05, 0x39, 0xD6, 0xC9, 0xBE, 0xDE, 0x68, 0xA9,
+       0xDD, 0x27, 0x84, 0x82, 0xDD, 0x19, 0xB1, 0x97,
+       0xEE, 0xCA, 0x77, 0x22, 0x59, 0x20, 0xEF, 0xFF,
+       0xCF, 0xDD, 0xBD, 0x24, 0xF8, 0x84, 0xD6, 0x88,
+       0xD6, 0xC4, 0x30, 0x17, 0x77, 0x9D, 0x98, 0xA3,
+       0x14, 0x01, 0xC7, 0x05, 0xBB, 0x0F, 0x23, 0x0D,
+       0x6F, 0x37, 0x57, 0xEC, 0x34, 0x67, 0x41, 0x62,
+       0xE8, 0x19, 0x75, 0xD9, 0x66, 0x1C, 0x6B, 0x8B,
+       0xC3, 0x11, 0x26, 0x9C, 0xF7, 0x2E, 0xA3, 0x72,
+       0xE8, 0xF7, 0xC8, 0x96, 0xEC, 0x92, 0xC2, 0xBD,
+       0xA1, 0x98, 0x2A, 0x93, 0x99, 0xB8, 0xA2, 0x43,
+       0xB7, 0xD0, 0xBE, 0x40, 0x1C, 0x8F, 0xE0, 0xB4,
+       0x20, 0x07, 0x97, 0x43, 0xAE, 0xAD, 0xB3, 0x9F
+};
+
+static const unsigned char RSA_IQ[] = {
+       0xB7, 0xE2, 0x60, 0xA9, 0x62, 0xEC, 0xEC, 0x0B,
+       0x57, 0x02, 0x96, 0xF9, 0x36, 0x35, 0x2C, 0x37,
+       0xAF, 0xC2, 0xEE, 0x71, 0x49, 0x26, 0x8E, 0x0F,
+       0x27, 0xB1, 0xFA, 0x0F, 0xEA, 0xDC, 0xF0, 0x8B,
+       0x53, 0x6C, 0xB2, 0x46, 0x27, 0xCD, 0x29, 0xA2,
+       0x35, 0x0F, 0x5D, 0x8A, 0x3F, 0x20, 0x8C, 0x13,
+       0x3D, 0xA1, 0xFF, 0x85, 0x91, 0x99, 0xE8, 0x50,
+       0xED, 0xF1, 0x29, 0x00, 0xEE, 0x24, 0x90, 0xB5,
+       0x5F, 0x3A, 0x74, 0x26, 0xD7, 0xA2, 0x24, 0x8D,
+       0x89, 0x88, 0xD8, 0x35, 0x22, 0x22, 0x8A, 0x66,
+       0x5D, 0x5C, 0xDE, 0x83, 0x8C, 0xFA, 0x27, 0xE6,
+       0xB9, 0xEB, 0x72, 0x08, 0xCD, 0x53, 0x4B, 0x93,
+       0x0F, 0xAD, 0xC3, 0xF8, 0x7C, 0xFE, 0x84, 0xD7,
+       0x08, 0xF3, 0xBE, 0x3D, 0x60, 0x1E, 0x95, 0x8D,
+       0x44, 0x5B, 0x65, 0x7E, 0xC1, 0x30, 0xC3, 0x84,
+       0xC0, 0xB0, 0xFE, 0xBF, 0x28, 0x54, 0x1E, 0xC4
+};
+
+static const br_rsa_public_key RSA_PK = {
+       (void *)RSA_N, sizeof RSA_N,
+       (void *)RSA_E, sizeof RSA_E
+};
+
+static const br_rsa_private_key RSA_SK = {
+       2048,
+       (void *)RSA_P, sizeof RSA_P,
+       (void *)RSA_Q, sizeof RSA_Q,
+       (void *)RSA_DP, sizeof RSA_DP,
+       (void *)RSA_DQ, sizeof RSA_DQ,
+       (void *)RSA_IQ, sizeof RSA_IQ
+};
+
+static void
+test_speed_rsa_inner(char *name,
+       br_rsa_public fpub, br_rsa_private fpriv)
+{
+       unsigned char tmp[sizeof RSA_N];
+       int i;
+       long num;
+
+       memset(tmp, 'R', sizeof tmp);
+       tmp[0] = 0;
+       for (i = 0; i < 10; i ++) {
+               if (!fpriv(tmp, &RSA_SK)) {
+                       abort();
+               }
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       fpriv(tmp, &RSA_SK);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f priv/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+       for (i = 0; i < 10; i ++) {
+               if (!fpub(tmp, sizeof tmp, &RSA_PK)) {
+                       abort();
+               }
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       fpub(tmp, sizeof tmp, &RSA_PK);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f pub/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_rsa_i31(void)
+{
+       test_speed_rsa_inner("RSA i31",
+               &br_rsa_i31_public, &br_rsa_i31_private);
+}
+
+static void
+test_speed_rsa_i32(void)
+{
+       test_speed_rsa_inner("RSA i32",
+               &br_rsa_i32_public, &br_rsa_i32_private);
+}
+
+static void
+test_speed_ec_inner(const char *name,
+       const br_ec_impl *impl, const br_ec_curve_def *cd)
+{
+       unsigned char bx[80], U[160];
+       uint32_t x[22], n[22];
+       size_t nlen, ulen;
+       int i;
+       long num;
+
+       nlen = cd->order_len;
+       br_i31_decode(n, cd->order, nlen);
+       memset(bx, 'T', sizeof bx);
+       br_i31_decode_reduce(x, bx, sizeof bx, n);
+       br_i31_encode(bx, nlen, x);
+       ulen = cd->generator_len;
+       memcpy(U, cd->generator, ulen);
+       for (i = 0; i < 10; i ++) {
+               impl->mul(U, ulen, bx, nlen, cd->curve);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       impl->mul(U, ulen, bx, nlen, cd->curve);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f mul/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_ec_prime_i31(void)
+{
+       test_speed_ec_inner("EC i31 P-256", &br_ec_prime_i31, &br_secp256r1);
+       test_speed_ec_inner("EC i31 P-384", &br_ec_prime_i31, &br_secp384r1);
+       test_speed_ec_inner("EC i31 P-521", &br_ec_prime_i31, &br_secp521r1);
+}
+
+static void
+test_speed_ecdsa_inner(const char *name,
+       const br_ec_impl *impl, const br_ec_curve_def *cd,
+       br_ecdsa_sign sign, br_ecdsa_vrfy vrfy)
+{
+       unsigned char bx[80], U[160], hv[32], sig[160];
+       uint32_t x[22], n[22];
+       size_t nlen, ulen, sig_len;
+       int i;
+       long num;
+       br_ec_private_key sk;
+       br_ec_public_key pk;
+
+       nlen = cd->order_len;
+       br_i31_decode(n, cd->order, nlen);
+       memset(bx, 'T', sizeof bx);
+       br_i31_decode_reduce(x, bx, sizeof bx, n);
+       br_i31_encode(bx, nlen, x);
+       ulen = cd->generator_len;
+       memcpy(U, cd->generator, ulen);
+       impl->mul(U, ulen, bx, nlen, cd->curve);
+       sk.curve = cd->curve;
+       sk.x = bx;
+       sk.xlen = nlen;
+       pk.curve = cd->curve;
+       pk.q = U;
+       pk.qlen = ulen;
+
+       memset(hv, 'H', sizeof hv);
+       sig_len = sign(impl, &br_sha256_vtable, hv, &sk, sig);
+       if (vrfy(impl, hv, sizeof hv, &pk, sig, sig_len) != 1) {
+               fprintf(stderr, "self-test sign/verify failed\n");
+               exit(EXIT_FAILURE);
+       }
+
+       for (i = 0; i < 10; i ++) {
+               hv[1] ++;
+               sign(impl, &br_sha256_vtable, hv, &sk, sig);
+               vrfy(impl, hv, sizeof hv, &pk, sig, sig_len);
+       }
+
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       hv[1] ++;
+                       sig_len = sign(impl, &br_sha256_vtable, hv, &sk, sig);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f sign/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       vrfy(impl, hv, sizeof hv, &pk, sig, sig_len);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f verify/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_ecdsa_i31(void)
+{
+       test_speed_ecdsa_inner("ECDSA i31 P-256",
+               &br_ec_prime_i31, &br_secp256r1,
+               &br_ecdsa_i31_sign_asn1,
+               &br_ecdsa_i31_vrfy_asn1);
+       test_speed_ecdsa_inner("ECDSA i31 P-384",
+               &br_ec_prime_i31, &br_secp384r1,
+               &br_ecdsa_i31_sign_asn1,
+               &br_ecdsa_i31_vrfy_asn1);
+       test_speed_ecdsa_inner("ECDSA i31 P-521",
+               &br_ec_prime_i31, &br_secp521r1,
+               &br_ecdsa_i31_sign_asn1,
+               &br_ecdsa_i31_vrfy_asn1);
+}
+
+#if 0
+/* obsolete */
+static void
+test_speed_ec_prime_i31_inner(const char *name,
+       const unsigned char *bg, const br_ec_prime_i31_curve *cc)
+{
+       unsigned char bx[80], point[160];
+       uint32_t x[BR_EC_I31_LEN];
+       br_ec_prime_i31_jacobian P;
+       uint32_t xbl;
+       size_t plen;
+       int i;
+       long num;
+
+       xbl = cc->p[0];
+       xbl -= (xbl >> 5);
+       plen = (xbl + 7) >> 3;
+       memset(bx, 'T', sizeof bx);
+       br_i31_decode_reduce(x, bx, sizeof bx, cc->p);
+       br_i31_encode(bx, plen, x);
+       br_ec_prime_i31_decode(&P, bg, 1 + (plen << 1), cc);
+       for (i = 0; i < 10; i ++) {
+               br_ec_prime_i31_mul(&P, bx, plen, cc);
+               br_ec_prime_i31_encode(point, &P, cc);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_ec_prime_i31_mul(&P, bx, plen, cc);
+                       br_ec_prime_i31_encode(point, &P, cc);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f mul/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_ec_prime_i31(void)
+{
+       test_speed_ec_prime_i31_inner("EC i31 P-256",
+               br_g_secp256r1, &br_ec_prime_i31_secp256r1);
+       test_speed_ec_prime_i31_inner("EC i31 P-384",
+               br_g_secp384r1, &br_ec_prime_i31_secp384r1);
+       test_speed_ec_prime_i31_inner("EC i31 P-521",
+               br_g_secp521r1, &br_ec_prime_i31_secp521r1);
+}
+
+static void
+test_speed_ec_prime_i32_inner(const char *name,
+       const unsigned char *bg, const br_ec_prime_i32_curve *cc)
+{
+       unsigned char bx[80], point[160];
+       uint32_t x[BR_EC_I32_LEN];
+       br_ec_prime_i32_jacobian P;
+       size_t plen;
+       int i;
+       long num;
+
+       plen = (cc->p[0] + 7) >> 3;
+       memset(bx, 'T', sizeof bx);
+       br_i32_decode_reduce(x, bx, sizeof bx, cc->p);
+       br_i32_encode(bx, plen, x);
+       br_ec_prime_i32_decode(&P, bg, 1 + (plen << 1), cc);
+       for (i = 0; i < 10; i ++) {
+               br_ec_prime_i32_mul(&P, bx, plen, cc);
+               br_ec_prime_i32_encode(point, &P, cc);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_ec_prime_i32_mul(&P, bx, plen, cc);
+                       br_ec_prime_i32_encode(point, &P, cc);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f mul/s\n", name,
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_ec_prime_i32(void)
+{
+       test_speed_ec_prime_i32_inner("EC i32 P-256",
+               br_g_secp256r1, &br_ec_prime_i32_secp256r1);
+       test_speed_ec_prime_i32_inner("EC i32 P-384",
+               br_g_secp384r1, &br_ec_prime_i32_secp384r1);
+       test_speed_ec_prime_i32_inner("EC i32 P-521",
+               br_g_secp521r1, &br_ec_prime_i32_secp521r1);
+}
+#endif
+
+static void
+test_speed_i31(void)
+{
+       static const unsigned char bp[] = {
+               0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+               0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+               0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
+       };
+
+       unsigned char tmp[60 + sizeof bp];
+       uint32_t p[10], x[10], y[10], z[10], p0i;
+       int i;
+       long num;
+
+       br_i31_decode(p, bp, sizeof bp);
+       p0i = br_i31_ninv31(p[1]);
+       memset(tmp, 'T', sizeof tmp);
+       br_i31_decode_reduce(x, tmp, sizeof tmp, p);
+       memset(tmp, 'U', sizeof tmp);
+       br_i31_decode_reduce(y, tmp, sizeof tmp, p);
+
+       for (i = 0; i < 10; i ++) {
+               br_i31_to_monty(x, p);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_i31_to_monty(x, p);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f ops/s\n", "i31 to_monty",
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+
+       for (i = 0; i < 10; i ++) {
+               br_i31_from_monty(x, p, p0i);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_i31_from_monty(x, p, p0i);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f ops/s\n", "i31 from_monty",
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+
+       for (i = 0; i < 10; i ++) {
+               br_i31_montymul(z, x, y, p, p0i);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_i31_montymul(z, x, y, p, p0i);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f ops/s\n", "i31 montymul",
+                               (double)num / tt);
+                       fflush(stdout);
+                       break;
+               }
+               num <<= 1;
+       }
+}
+
+#if 0
+
+static unsigned char P2048[] = {
+       0xFD, 0xB6, 0xE0, 0x3E, 0x00, 0x49, 0x4C, 0xF0, 0x69, 0x3A, 0xDD, 0x7D,
+       0xF8, 0xA2, 0x41, 0xB0, 0x6C, 0x67, 0xC5, 0xBA, 0xB8, 0x46, 0x80, 0xF5,
+       0xBF, 0xAB, 0x98, 0xFC, 0x84, 0x73, 0xA5, 0x63, 0xC9, 0x52, 0x12, 0xDA,
+       0x4C, 0xC1, 0x5B, 0x9D, 0x8D, 0xDF, 0xCD, 0xFE, 0xC5, 0xAD, 0x5A, 0x6F,
+       0xDD, 0x02, 0xD9, 0xEC, 0x71, 0xEF, 0xEB, 0xB6, 0x95, 0xED, 0x94, 0x25,
+       0x0E, 0x63, 0xDD, 0x6A, 0x52, 0xC7, 0x93, 0xAF, 0x85, 0x9D, 0x2C, 0xBE,
+       0x5C, 0xBE, 0x35, 0xD8, 0xDD, 0x39, 0xEF, 0x1B, 0xB1, 0x49, 0x67, 0xB2,
+       0x33, 0xC9, 0x7C, 0xE1, 0x51, 0x79, 0x51, 0x59, 0xCA, 0x6E, 0x2A, 0xDF,
+       0x0D, 0x76, 0x1C, 0xE7, 0xA5, 0xC0, 0x1E, 0x6C, 0x56, 0x3A, 0x32, 0xE5,
+       0xB5, 0xC5, 0xD4, 0xDB, 0xFE, 0xFF, 0xF8, 0xF2, 0x96, 0xA9, 0xC9, 0x65,
+       0x59, 0x9E, 0x01, 0x79, 0x9D, 0x38, 0x68, 0x0F, 0xAD, 0x43, 0x3A, 0xD6,
+       0x84, 0x0A, 0xE2, 0xEF, 0x96, 0xC1, 0x6D, 0x89, 0x74, 0x19, 0x63, 0x82,
+       0x3B, 0xA0, 0x9C, 0xBA, 0x78, 0xDE, 0xDC, 0xC2, 0xE7, 0xD4, 0xFA, 0xD6,
+       0x19, 0x21, 0x29, 0xAE, 0x5E, 0xF4, 0x38, 0x81, 0xC6, 0x9E, 0x0E, 0x3C,
+       0xCD, 0xC0, 0xDC, 0x93, 0x5D, 0xFD, 0x9A, 0x5C, 0xAB, 0x54, 0x1F, 0xFF,
+       0x9C, 0x12, 0x1B, 0x4C, 0xDF, 0x2D, 0x9C, 0x85, 0xF9, 0x68, 0x15, 0x89,
+       0x42, 0x9B, 0x6C, 0x45, 0x89, 0x3A, 0xBC, 0xE9, 0x19, 0x91, 0xBE, 0x0C,
+       0xEF, 0x90, 0xCC, 0xF6, 0xD6, 0xF0, 0x3D, 0x5C, 0xF5, 0xE5, 0x0F, 0x2F,
+       0x02, 0x8A, 0x83, 0x4B, 0x93, 0x2F, 0x14, 0x12, 0x1F, 0x56, 0x9A, 0x12,
+       0x58, 0x88, 0xAE, 0x60, 0xB8, 0x5A, 0xE4, 0xA1, 0xBF, 0x4A, 0x81, 0x84,
+       0xAB, 0xBB, 0xE4, 0xD0, 0x1D, 0x41, 0xD9, 0x0A, 0xAB, 0x1E, 0x47, 0x5B,
+       0x31, 0xAC, 0x2B, 0x73
+};
+
+static unsigned char G2048[] = {
+       0x02
+};
+
+static void
+test_speed_modpow(void)
+{
+       uint32_t mx[65], mp[65], me[65], t1[65], t2[65], len;
+       unsigned char e[64];
+       int i;
+       long num;
+
+       len = br_int_decode(mp, sizeof mp / sizeof mp[0],
+               P2048, sizeof P2048);
+       if (len != 65) {
+               abort();
+       }
+       memset(e, 'P', sizeof e);
+       if (!br_int_decode(me, sizeof me / sizeof me[0], e, sizeof e)) {
+               abort();
+       }
+       if (!br_modint_decode(mx, mp, G2048, sizeof G2048)) {
+               abort();
+       }
+       for (i = 0; i < 10; i ++) {
+               br_modint_to_monty(mx, mp);
+               br_modint_montypow(mx, me, mp, t1, t2);
+               br_modint_from_monty(mx, mp);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_modint_to_monty(mx, mp);
+                       br_modint_montypow(mx, me, mp, t1, t2);
+                       br_modint_from_monty(mx, mp);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f exp/s\n", "pow[2048:256]",
+                               (double)num / tt);
+                       fflush(stdout);
+                       return;
+               }
+               num <<= 1;
+       }
+}
+
+static void
+test_speed_moddiv(void)
+{
+       uint32_t mx[65], my[65], mp[65], t1[65], t2[65], t3[65], len;
+       unsigned char x[255], y[255];
+       int i;
+       long num;
+
+       len = br_int_decode(mp, sizeof mp / sizeof mp[0],
+               P2048, sizeof P2048);
+       if (len != 65) {
+               abort();
+       }
+       memset(x, 'T', sizeof x);
+       memset(y, 'P', sizeof y);
+       if (!br_modint_decode(mx, mp, x, sizeof x)) {
+               abort();
+       }
+       if (!br_modint_decode(my, mp, y, sizeof y)) {
+               abort();
+       }
+       for (i = 0; i < 10; i ++) {
+               br_modint_div(mx, my, mp, t1, t2, t3);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       br_modint_div(mx, my, mp, t1, t2, t3);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f div/s\n", "div[2048]",
+                               (double)num / tt);
+                       fflush(stdout);
+                       return;
+               }
+               num <<= 1;
+       }
+}
+#endif
+
+#define STU(x)   { test_speed_ ## x, #x }
+
+static const struct {
+       void (*fn)(void);
+       char *name;
+} tfns[] = {
+       STU(md5),
+       STU(sha1),
+       STU(sha256),
+       STU(sha512),
+
+       STU(aes128_big_cbcenc),
+       STU(aes128_big_cbcdec),
+       STU(aes192_big_cbcenc),
+       STU(aes192_big_cbcdec),
+       STU(aes256_big_cbcenc),
+       STU(aes256_big_cbcdec),
+       STU(aes128_big_ctr),
+       STU(aes192_big_ctr),
+       STU(aes256_big_ctr),
+
+       STU(aes128_small_cbcenc),
+       STU(aes128_small_cbcdec),
+       STU(aes192_small_cbcenc),
+       STU(aes192_small_cbcdec),
+       STU(aes256_small_cbcenc),
+       STU(aes256_small_cbcdec),
+       STU(aes128_small_ctr),
+       STU(aes192_small_ctr),
+       STU(aes256_small_ctr),
+
+       STU(aes128_ct_cbcenc),
+       STU(aes128_ct_cbcdec),
+       STU(aes192_ct_cbcenc),
+       STU(aes192_ct_cbcdec),
+       STU(aes256_ct_cbcenc),
+       STU(aes256_ct_cbcdec),
+       STU(aes128_ct_ctr),
+       STU(aes192_ct_ctr),
+       STU(aes256_ct_ctr),
+
+       STU(aes128_ct64_cbcenc),
+       STU(aes128_ct64_cbcdec),
+       STU(aes192_ct64_cbcenc),
+       STU(aes192_ct64_cbcdec),
+       STU(aes256_ct64_cbcenc),
+       STU(aes256_ct64_cbcdec),
+       STU(aes128_ct64_ctr),
+       STU(aes192_ct64_ctr),
+       STU(aes256_ct64_ctr),
+
+       STU(des_tab_cbcenc),
+       STU(des_tab_cbcdec),
+       STU(3des_tab_cbcenc),
+       STU(3des_tab_cbcdec),
+
+       STU(des_ct_cbcenc),
+       STU(des_ct_cbcdec),
+       STU(3des_ct_cbcenc),
+       STU(3des_ct_cbcdec),
+
+       STU(ghash_ctmul),
+       STU(ghash_ctmul32),
+       STU(ghash_ctmul64),
+
+       STU(rsa_i31),
+       STU(rsa_i32),
+       STU(ec_prime_i31),
+       STU(ecdsa_i31),
+
+       STU(i31)
+};
+
+static int
+eq_name(const char *s1, const char *s2)
+{
+       for (;;) {
+               int c1, c2;
+
+               for (;;) {
+                       c1 = *s1 ++;
+                       if (c1 >= 'A' && c1 <= 'Z') {
+                               c1 += 'a' - 'A';
+                       } else {
+                               switch (c1) {
+                               case '-': case '_': case '.': case ' ':
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               for (;;) {
+                       c2 = *s2 ++;
+                       if (c2 >= 'A' && c2 <= 'Z') {
+                               c2 += 'a' - 'A';
+                       } else {
+                               switch (c2) {
+                               case '-': case '_': case '.': case ' ':
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               if (c1 != c2) {
+                       return 0;
+               }
+               if (c1 == 0) {
+                       return 1;
+               }
+       }
+}
+
+int
+main(int argc, char *argv[])
+{
+       size_t u;
+
+       if (argc <= 1) {
+               printf("usage: testspeed all | name...\n");
+               printf("individual test names:\n");
+               for (u = 0; u < (sizeof tfns) / (sizeof tfns[0]); u ++) {
+                       printf("   %s\n", tfns[u].name);
+               }
+       } else {
+               for (u = 0; u < (sizeof tfns) / (sizeof tfns[0]); u ++) {
+                       int i;
+
+                       for (i = 1; i < argc; i ++) {
+                               if (eq_name(argv[i], tfns[u].name)
+                                       || eq_name(argv[i], "all"))
+                               {
+                                       tfns[u].fn();
+                                       break;
+                               }
+                       }
+               }
+       }
+       return 0;
+}
diff --git a/test/test_x509.c b/test/test_x509.c
new file mode 100644 (file)
index 0000000..a591b46
--- /dev/null
@@ -0,0 +1,1634 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "bearssl.h"
+
+#define DIRNAME        "test/x509"
+#define CONFFILE       (DIRNAME "/alltests.txt")
+#define DEFAULT_TIME   "2016-08-30T18:00:00Z"
+
+static void *
+xmalloc(size_t len)
+{
+       void *buf;
+
+       if (len == 0) {
+               return NULL;
+       }
+       buf = malloc(len);
+       if (buf == NULL) {
+               fprintf(stderr, "error: cannot allocate %lu byte(s)\n",
+                       (unsigned long)len);
+               exit(EXIT_FAILURE);
+       }
+       return buf;
+}
+
+static void
+xfree(void *buf)
+{
+       if (buf != NULL) {
+               free(buf);
+       }
+}
+
+static char *
+xstrdup(const char *name)
+{
+       size_t n;
+       char *s;
+
+       if (name == NULL) {
+               return NULL;
+       }
+       n = strlen(name) + 1;
+       s = xmalloc(n);
+       memcpy(s, name, n);
+       return s;
+}
+
+typedef struct {
+       char *buf;
+       size_t ptr, len;
+} string_builder;
+
+static string_builder *
+SB_new(void)
+{
+       string_builder *sb;
+
+       sb = xmalloc(sizeof *sb);
+       sb->len = 8;
+       sb->buf = xmalloc(sb->len);
+       sb->ptr = 0;
+       return sb;
+}
+
+static void
+SB_expand(string_builder *sb, size_t extra_len)
+{
+       size_t nlen;
+       char *nbuf;
+
+       if (extra_len < (sb->len - sb->ptr)) {
+               return;
+       }
+       nlen = sb->len << 1;
+       if (extra_len > (nlen - sb->ptr)) {
+               nlen = sb->ptr + extra_len;
+       }
+       nbuf = xmalloc(nlen);
+       memcpy(nbuf, sb->buf, sb->ptr);
+       xfree(sb->buf);
+       sb->buf = nbuf;
+       sb->len = nlen;
+}
+
+static void
+SB_append_char(string_builder *sb, int c)
+{
+       SB_expand(sb, 1);
+       sb->buf[sb->ptr ++] = c;
+}
+
+/* unused
+static void
+SB_append_string(string_builder *sb, const char *s)
+{
+       size_t n;
+
+       n = strlen(s);
+       SB_expand(sb, n);
+       memcpy(sb->buf + sb->ptr, s, n);
+       sb->ptr += n;
+}
+*/
+
+/* unused
+static char *
+SB_to_string(string_builder *sb)
+{
+       char *s;
+
+       s = xmalloc(sb->ptr + 1);
+       memcpy(s, sb->buf, sb->ptr);
+       s[sb->ptr] = 0;
+       return s;
+}
+*/
+
+static char *
+SB_contents(string_builder *sb)
+{
+       return sb->buf;
+}
+
+static size_t
+SB_length(string_builder *sb)
+{
+       return sb->ptr;
+}
+
+static void
+SB_set_length(string_builder *sb, size_t len)
+{
+       if (sb->ptr < len) {
+               SB_expand(sb, len - sb->ptr);
+               memset(sb->buf + sb->ptr, ' ', len - sb->ptr);
+       }
+       sb->ptr = len;
+}
+
+static void
+SB_reset(string_builder *sb)
+{
+       SB_set_length(sb, 0);
+}
+
+static void
+SB_free(string_builder *sb)
+{
+       xfree(sb->buf);
+       xfree(sb);
+}
+
+typedef struct ht_elt_ {
+       char *name;
+       void *value;
+       struct ht_elt_ *next;
+} ht_elt;
+
+typedef struct {
+       size_t size;
+       ht_elt **buckets;
+       size_t num_buckets;
+} HT;
+
+static HT *
+HT_new(void)
+{
+       HT *ht;
+       size_t u;
+
+       ht = xmalloc(sizeof *ht);
+       ht->size = 0;
+       ht->num_buckets = 8;
+       ht->buckets = xmalloc(ht->num_buckets * sizeof(ht_elt *));
+       for (u = 0; u < ht->num_buckets; u ++) {
+               ht->buckets[u] = NULL;
+       }
+       return ht;
+}
+
+static uint32_t
+hash_string(const char *name)
+{
+       uint32_t hc;
+
+       hc = 0;
+       while (*name) {
+               int x;
+
+               hc = (hc << 5) - hc;
+               x = *(const unsigned char *)name;
+               if (x >= 'A' && x <= 'Z') {
+                       x += 'a' - 'A';
+               }
+               hc += (uint32_t)x;
+               name ++;
+       }
+       return hc;
+}
+
+static int
+eqstring(const char *s1, const char *s2)
+{
+       while (*s1 && *s2) {
+               int x1, x2;
+
+               x1 = *(const unsigned char *)s1;
+               x2 = *(const unsigned char *)s2;
+               if (x1 >= 'A' && x1 <= 'Z') {
+                       x1 += 'a' - 'A';
+               }
+               if (x2 >= 'A' && x2 <= 'Z') {
+                       x2 += 'a' - 'A';
+               }
+               if (x1 != x2) {
+                       return 0;
+               }
+               s1 ++;
+               s2 ++;
+       }
+       return !(*s1 || *s2);
+}
+
+static void
+HT_expand(HT *ht)
+{
+       size_t n, n2, u;
+       ht_elt **new_buckets;
+
+       n = ht->num_buckets;
+       n2 = n << 1;
+       new_buckets = xmalloc(n2 * sizeof *new_buckets);
+       for (u = 0; u < n; u ++) {
+               ht_elt *e, *f;
+
+               f = NULL;
+               for (e = ht->buckets[u]; e != NULL; e = f) {
+                       uint32_t hc;
+                       size_t v;
+
+                       hc = hash_string(e->name);
+                       v = (size_t)(hc & ((uint32_t)n2 - 1));
+                       f = e->next;
+                       e->next = new_buckets[v];
+                       new_buckets[v] = e;
+               }
+       }
+       xfree(ht->buckets);
+       ht->buckets = new_buckets;
+       ht->num_buckets = n2;
+}
+
+static void *
+HT_put(HT *ht, const char *name, void *value)
+{
+       uint32_t hc;
+       size_t k;
+       ht_elt *e, **prev;
+
+       hc = hash_string(name);
+       k = (size_t)(hc & ((uint32_t)ht->num_buckets - 1));
+       prev = &ht->buckets[k];
+       e = *prev;
+       while (e != NULL) {
+               if (eqstring(name, e->name)) {
+                       void *old_value;
+
+                       old_value = e->value;
+                       if (value == NULL) {
+                               *prev = e->next;
+                               xfree(e->name);
+                               xfree(e);
+                               ht->size --;
+                       } else {
+                               e->value = value;
+                       }
+                       return old_value;
+               }
+               prev = &e->next;
+               e = *prev;
+       }
+       if (value != NULL) {
+               e = xmalloc(sizeof *e);
+               e->name = xstrdup(name);
+               e->value = value;
+               e->next = ht->buckets[k];
+               ht->buckets[k] = e;
+               ht->size ++;
+               if (ht->size > ht->num_buckets) {
+                       HT_expand(ht);
+               }
+       }
+       return NULL;
+}
+
+/* unused
+static void *
+HT_remove(HT *ht, const char *name)
+{
+       return HT_put(ht, name, NULL);
+}
+*/
+
+static void *
+HT_get(const HT *ht, const char *name)
+{
+       uint32_t hc;
+       size_t k;
+       ht_elt *e;
+
+       hc = hash_string(name);
+       k = (size_t)(hc & ((uint32_t)ht->num_buckets - 1));
+       for (e = ht->buckets[k]; e != NULL; e = e->next) {
+               if (eqstring(name, e->name)) {
+                       return e->value;
+               }
+       }
+       return NULL;
+}
+
+static void
+HT_clear(HT *ht, void (*free_value)(void *value))
+{
+       size_t u;
+
+       for (u = 0; u < ht->num_buckets; u ++) {
+               ht_elt *e, *f;
+
+               f = NULL;
+               for (e = ht->buckets[u]; e != NULL; e = f) {
+                       f = e->next;
+                       xfree(e->name);
+                       if (free_value != 0) {
+                               free_value(e->value);
+                       }
+                       xfree(e);
+               }
+               ht->buckets[u] = NULL;
+       }
+       ht->size = 0;
+}
+
+static void
+HT_free(HT *ht, void (*free_value)(void *value))
+{
+       HT_clear(ht, free_value);
+       xfree(ht->buckets);
+       xfree(ht);
+}
+
+/* unused
+static size_t
+HT_size(HT *ht)
+{
+       return ht->size;
+}
+*/
+
+static unsigned char *
+read_all(FILE *f, size_t *len)
+{
+       unsigned char *buf;
+       size_t ptr, blen;
+
+       blen = 1024;
+       buf = xmalloc(blen);
+       ptr = 0;
+       for (;;) {
+               size_t rlen;
+
+               if (ptr == blen) {
+                       unsigned char *buf2;
+
+                       blen <<= 1;
+                       buf2 = xmalloc(blen);
+                       memcpy(buf2, buf, ptr);
+                       xfree(buf);
+                       buf = buf2;
+               }
+               rlen = fread(buf + ptr, 1, blen - ptr, f);
+               if (rlen == 0) {
+                       unsigned char *buf3;
+
+                       buf3 = xmalloc(ptr);
+                       memcpy(buf3, buf, ptr);
+                       xfree(buf);
+                       *len = ptr;
+                       return buf3;
+               }
+               ptr += rlen;
+       }
+}
+
+static unsigned char *
+read_file(const char *name, size_t *len)
+{
+       FILE *f;
+       unsigned char *buf;
+
+#ifdef DIRNAME
+       char *dname;
+
+       dname = xmalloc(strlen(DIRNAME) + strlen(name) + 2);
+       sprintf(dname, "%s/%s", DIRNAME, name);
+       name = dname;
+#endif
+       f = fopen(name, "rb");
+       if (f == NULL) {
+               fprintf(stderr, "could not open file '%s'\n", name);
+               exit(EXIT_FAILURE);
+       }
+       buf = read_all(f, len);
+       if (ferror(f)) {
+               fprintf(stderr, "read error on file '%s'\n", name);
+               exit(EXIT_FAILURE);
+       }
+       fclose(f);
+#ifdef DIRNAME
+       xfree(dname);
+#endif
+       return buf;
+}
+
+static int
+parse_dec(const char *s, unsigned len, int *val)
+{
+       int acc;
+
+       acc = 0;
+       while (len -- > 0) {
+               int c;
+
+               c = *s ++;
+               if (c >= '0' && c <= '9') {
+                       acc = (acc * 10) + (c - '0');
+               } else {
+                       return -1;
+               }
+       }
+       *val = acc;
+       return 0;
+}
+
+static int
+parse_choice(const char *s, const char *acceptable)
+{
+       int c;
+
+       c = *s;
+       while (*acceptable) {
+               if (c == *acceptable ++) {
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int
+month_length(int year, int month)
+{
+       static const int base_month_length[] = {
+               31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+       };
+
+       int x;
+
+       x = base_month_length[month - 1];
+       if (month == 2 && year % 4 == 0
+               && (year % 100 != 0 || year % 400 == 0))
+       {
+               x ++;
+       }
+       return x;
+}
+
+/*
+ * Convert a time string to a days+seconds count. Returned value is 0
+ * on success, -1 on error.
+ */
+static int
+string_to_time(const char *s, uint32_t *days, uint32_t *seconds)
+{
+       int year, month, day, hour, minute, second;
+       int day_of_year, leaps;
+
+       if (parse_dec(s, 4, &year) < 0) {
+               return -1;
+       }
+       s += 4;
+       if (parse_choice(s ++, "-:/ ") < 0) {
+               return -1;
+       }
+       if (parse_dec(s, 2, &month) < 0) {
+               return -1;
+       }
+       s += 2;
+       if (parse_choice(s ++, "-:/ ") < 0) {
+               return -1;
+       }
+       if (parse_dec(s, 2, &day) < 0) {
+               return -1;
+       }
+       s += 2;
+       if (parse_choice(s ++, " T") < 0) {
+               return -1;
+       }
+       if (parse_dec(s, 2, &hour) < 0) {
+               return -1;
+       }
+       s += 2;
+       if (parse_choice(s ++, "-:/ ") < 0) {
+               return -1;
+       }
+       if (parse_dec(s, 2, &minute) < 0) {
+               return -1;
+       }
+       s += 2;
+       if (parse_choice(s ++, "-:/ ") < 0) {
+               return -1;
+       }
+       if (parse_dec(s, 2, &second) < 0) {
+               return -1;
+       }
+       s += 2;
+       if (*s == '.') {
+               while (*s && *s >= '0' && *s <= '9') {
+                       s ++;
+               }
+       }
+       if (*s) {
+               if (*s ++ != 'Z') {
+                       return -1;
+               }
+               if (*s) {
+                       return -1;
+               }
+       }
+
+       if (month < 1 || month > 12) {
+               return -1;
+       }
+       day_of_year = 0;
+       for (int i = 1; i < month; i ++) {
+               day_of_year += month_length(year, i);
+       }
+       if (day < 1 || day > month_length(year, month)) {
+               return -1;
+       }
+       day_of_year += (day - 1);
+       leaps = (year + 3) / 4 - (year + 99) / 100 + (year + 399) / 400;
+
+       if (hour > 23 || minute > 59 || second > 60) {
+               return -1;
+       }
+       *days = (uint32_t)year * 365 + (uint32_t)leaps + day_of_year;
+       *seconds = (uint32_t)hour * 3600 + minute * 60 + second;
+       return 0;
+}
+
+static FILE *conf;
+static int conf_delayed_char;
+static long conf_linenum;
+static string_builder *line_builder;
+static long current_linenum;
+
+static void
+conf_init(const char *fname)
+{
+       conf = fopen(fname, "r");
+       if (conf == NULL) {
+               fprintf(stderr, "could not open file '%s'\n", fname);
+               exit(EXIT_FAILURE);
+       }
+       conf_delayed_char = -1;
+       conf_linenum = 1;
+       line_builder = SB_new();
+}
+
+static void
+conf_close(void)
+{
+       if (conf != NULL) {
+               if (ferror(conf)) {
+                       fprintf(stderr, "read error on configuration file\n");
+                       exit(EXIT_FAILURE);
+               }
+               fclose(conf);
+               conf = NULL;
+       }
+       if (line_builder != NULL) {
+               SB_free(line_builder);
+               line_builder = NULL;
+       }
+}
+
+/*
+ * Get next character from the config file.
+ */
+static int
+conf_next_low(void)
+{
+       int x;
+
+       x = conf_delayed_char;
+       if (x >= 0) {
+               conf_delayed_char = -1;
+       } else {
+               x = fgetc(conf);
+               if (x == EOF) {
+                       x = -1;
+               }
+       }
+       if (x == '\r') {
+               x = fgetc(conf);
+               if (x == EOF) {
+                       x = -1;
+               }
+               if (x != '\n') {
+                       conf_delayed_char = x;
+                       x = '\n';
+               }
+       }
+       if (x == '\n') {
+               conf_linenum ++;
+       }
+       return x;
+}
+
+static int
+is_ws(int x)
+{
+       return x <= 32;
+}
+
+static int
+is_name_char(int c)
+{
+       return (c >= 'A' && c <= 'Z')
+               || (c >= 'a' && c <= 'z')
+               || (c >= '0' && c <= '9')
+               || (c == '_' || c == '-' || c == '.');
+}
+
+/*
+ * Read a complete line. This handles line continuation; empty lines and
+ * comment lines are skipped; leading and trailing whitespace is removed.
+ * Returned value is 0 (line read) or -1 (no line, EOF reached). The line
+ * contents are accumulated in the line_builder.
+ */
+static int
+conf_next_line(void)
+{
+       for (;;) {
+               int c;
+               int lcwb;
+
+               SB_reset(line_builder);
+
+               /*
+                * Get first non-whitespace character. This skips empty
+                * lines. Comment lines (first non-whitespace character
+                * is a semicolon) are also skipped.
+                */
+               for (;;) {
+                       c = conf_next_low();
+                       if (c < 0) {
+                               return -1;
+                       }
+                       if (is_ws(c)) {
+                               continue;
+                       }
+                       if (c == ';') {
+                               for (;;) {
+                                       c = conf_next_low();
+                                       if (c < 0) {
+                                               return -1;
+                                       }
+                                       if (c == '\n') {
+                                               break;
+                                       }
+                               }
+                               continue;
+                       }
+                       break;
+               }
+
+               /*
+                * Read up the remaining of the line. The line continuation
+                * sequence (final backslash) is detected and processed.
+                */
+               current_linenum = conf_linenum;
+               lcwb = (c == '\\');
+               SB_append_char(line_builder, c);
+               for (;;) {
+                       c = conf_next_low();
+                       if (c < 0) {
+                               break;
+                       }
+                       if (lcwb) {
+                               if (c == '\n') {
+                                       SB_set_length(line_builder,
+                                               SB_length(line_builder) - 1);
+                               }
+                               lcwb = 0;
+                               continue;
+                       }
+                       if (c == '\n') {
+                               break;
+                       } else if (c == '\\') {
+                               lcwb = 1;
+                       }
+                       SB_append_char(line_builder, c);
+               }
+
+               /*
+                * Remove trailing whitespace (if any).
+                */
+               for (;;) {
+                       size_t u;
+
+                       u = SB_length(line_builder);
+                       if (u == 0 || !is_ws(
+                               SB_contents(line_builder)[u - 1]))
+                       {
+                               break;
+                       }
+                       SB_set_length(line_builder, u - 1);
+               }
+
+               /*
+                * We might end up with a totally empty line (in case there
+                * was a line continuation but nothing else), in which case
+                * we must loop.
+                */
+               if (SB_length(line_builder) > 0) {
+                       return 0;
+               }
+       }
+}
+
+/*
+ * Test whether the current line is a section header. If yes, then the
+ * header name is extracted, and returned as a newly allocated string.
+ * Otherwise, NULL is returned.
+ */
+static char *
+parse_header_name(void)
+{
+       char *buf, *name;
+       size_t u, v, w, len;
+
+       buf = SB_contents(line_builder);
+       len = SB_length(line_builder);
+       if (len < 2 || buf[0] != '[' || buf[len - 1] != ']') {
+               return NULL;
+       }
+       u = 1;
+       v = len - 1;
+       while (u < v && is_ws(buf[u])) {
+               u ++;
+       }
+       while (u < v && is_ws(buf[v - 1])) {
+               v --;
+       }
+       if (u == v) {
+               return NULL;
+       }
+       for (w = u; w < v; w ++) {
+               if (!is_name_char(buf[w])) {
+                       return NULL;
+               }
+       }
+       len = v - u;
+       name = xmalloc(len + 1);
+       memcpy(name, buf + u, len);
+       name[len] = 0;
+       return name;
+}
+
+/*
+ * Parse the current line as a 'name = value' pair. The pair is pushed into
+ * the provided hash table. On error (including a duplicate key name),
+ * this function returns -1; otherwise, it returns 0.
+ */
+static int
+parse_keyvalue(HT *d)
+{
+       char *buf, *name, *value;
+       size_t u, len;
+
+       buf = SB_contents(line_builder);
+       len = SB_length(line_builder);
+       for (u = 0; u < len; u ++) {
+               if (!is_name_char(buf[u])) {
+                       break;
+               }
+       }
+       if (u == 0) {
+               return -1;
+       }
+       name = xmalloc(u + 1);
+       memcpy(name, buf, u);
+       name[u] = 0;
+       if (HT_get(d, name) != NULL) {
+               xfree(name);
+               return -1;
+       }
+       while (u < len && is_ws(buf[u])) {
+               u ++;
+       }
+       if (u >= len || buf[u] != '=') {
+               xfree(name);
+               return -1;
+       }
+       u ++;
+       while (u < len && is_ws(buf[u])) {
+               u ++;
+       }
+       value = xmalloc(len - u + 1);
+       memcpy(value, buf + u, len - u);
+       value[len - u] = 0;
+       HT_put(d, name, value);
+       xfree(name);
+       return 0;
+}
+
+/*
+ * Public keys, indexed by name. Elements are pointers to br_x509_pkey
+ * structures.
+ */
+static HT *keys;
+
+/*
+ * Trust anchors, indexed by name. Elements are pointers to
+ * test_trust_anchor structures.
+ */
+static HT *trust_anchors;
+
+typedef struct {
+       unsigned char *dn;
+       size_t dn_len;
+       unsigned flags;
+       char *key_name;
+} test_trust_anchor;
+
+/*
+ * Test case: trust anchors, certificates (file names), key type and
+ * usage, expected status and EE public key.
+ */
+typedef struct {
+       char *name;
+       char **ta_names;
+       char **cert_names;
+       char *servername;
+       unsigned key_type_usage;
+       unsigned status;
+       char *ee_key_name;
+       unsigned hashes;
+       uint32_t days, seconds;
+} test_case;
+
+static test_case *all_chains;
+static size_t all_chains_ptr, all_chains_len;
+
+static void
+free_key(void *value)
+{
+       br_x509_pkey *pk;
+
+       pk = value;
+       switch (pk->key_type) {
+       case BR_KEYTYPE_RSA:
+               xfree((void *)pk->key.rsa.n);
+               xfree((void *)pk->key.rsa.e);
+               break;
+       case BR_KEYTYPE_EC:
+               xfree((void *)pk->key.ec.q);
+               break;
+       default:
+               fprintf(stderr, "unknown key type: %d\n", pk->key_type);
+               exit(EXIT_FAILURE);
+               break;
+       }
+       xfree(pk);
+}
+
+static void
+free_trust_anchor(void *value)
+{
+       test_trust_anchor *ttc;
+
+       ttc = value;
+       xfree(ttc->dn);
+       xfree(ttc->key_name);
+       xfree(ttc);
+}
+
+static void
+free_test_case_contents(test_case *tc)
+{
+       size_t u;
+
+       xfree(tc->name);
+       for (u = 0; tc->ta_names[u]; u ++) {
+               xfree(tc->ta_names[u]);
+       }
+       xfree(tc->ta_names);
+       for (u = 0; tc->cert_names[u]; u ++) {
+               xfree(tc->cert_names[u]);
+       }
+       xfree(tc->cert_names);
+       xfree(tc->servername);
+       xfree(tc->ee_key_name);
+}
+
+static char *
+get_value(char *objtype, HT *objdata, long linenum, char *name)
+{
+       char *value;
+
+       value = HT_get(objdata, name);
+       if (value == NULL) {
+               fprintf(stderr,
+                       "missing property '%s' in section '%s' (line %ld)\n",
+                       name, objtype, linenum);
+               exit(EXIT_FAILURE);
+       }
+       return value;
+}
+
+static unsigned char *
+parse_hex(const char *name, long linenum, const char *value, size_t *len)
+{
+       unsigned char *buf;
+
+       buf = NULL;
+       for (;;) {
+               size_t u, ptr;
+               int acc, z;
+
+               ptr = 0;
+               acc = 0;
+               z = 0;
+               for (u = 0; value[u]; u ++) {
+                       int c;
+
+                       c = value[u];
+                       if (c >= '0' && c <= '9') {
+                               c -= '0';
+                       } else if (c >= 'A' && c <= 'F') {
+                               c -= 'A' - 10;
+                       } else if (c >= 'a' && c <= 'f') {
+                               c -= 'a' - 10;
+                       } else if (c == ' ' || c == ':') {
+                               continue;
+                       } else {
+                               fprintf(stderr, "invalid hexadecimal character"
+                                       " in '%s' (line %ld)\n",
+                                       name, linenum);
+                               exit(EXIT_FAILURE);
+                       }
+                       if (z) {
+                               if (buf != NULL) {
+                                       buf[ptr] = (acc << 4) + c;
+                               }
+                               ptr ++;
+                       } else {
+                               acc = c;
+                       }
+                       z = !z;
+               }
+               if (z) {
+                       fprintf(stderr, "invalid hexadecimal value (partial"
+                               " byte) in '%s' (line %ld)\n",
+                               name, linenum);
+                       exit(EXIT_FAILURE);
+               }
+               if (buf == NULL) {
+                       buf = xmalloc(ptr);
+               } else {
+                       *len = ptr;
+                       return buf;
+               }
+       }
+}
+
+static char **
+split_names(const char *value)
+{
+       char **names;
+       size_t len;
+
+       names = NULL;
+       len = strlen(value);
+       for (;;) {
+               size_t u, ptr;
+
+               ptr = 0;
+               u = 0;
+               while (u < len) {
+                       size_t v;
+
+                       while (u < len && is_ws(value[u])) {
+                               u ++;
+                       }
+                       v = u;
+                       while (v < len && !is_ws(value[v])) {
+                               v ++;
+                       }
+                       if (v > u) {
+                               if (names != NULL) {
+                                       char *name;
+
+                                       name = xmalloc(v - u + 1);
+                                       memcpy(name, value + u, v - u);
+                                       name[v - u] = 0;
+                                       names[ptr] = name;
+                               }
+                               ptr ++;
+                       }
+                       u = v;
+               }
+               if (names == NULL) {
+                       names = xmalloc((ptr + 1) * sizeof *names);
+               } else {
+                       names[ptr] = NULL;
+                       return names;
+               }
+       }
+}
+
+static int
+string_to_hash(const char *name)
+{
+       char tmp[20];
+       size_t u, v;
+
+       for (u = 0, v = 0; name[u]; u ++) {
+               int c;
+
+               c = name[u];
+               if ((c >= '0' && c <= '9')
+                       || (c >= 'A' && c <= 'Z')
+                       || (c >= 'a' && c <= 'z'))
+               {
+                       tmp[v ++] = c;
+                       if (v == sizeof tmp) {
+                               return -1;
+                       }
+               }
+       }
+       tmp[v] = 0;
+       if (eqstring(tmp, "md5")) {
+               return br_md5_ID;
+       } else if (eqstring(tmp, "sha1")) {
+               return br_sha1_ID;
+       } else if (eqstring(tmp, "sha224")) {
+               return br_sha224_ID;
+       } else if (eqstring(tmp, "sha256")) {
+               return br_sha256_ID;
+       } else if (eqstring(tmp, "sha384")) {
+               return br_sha384_ID;
+       } else if (eqstring(tmp, "sha512")) {
+               return br_sha512_ID;
+       } else {
+               return -1;
+       }
+}
+
+static int
+string_to_curve(const char *name)
+{
+       char tmp[20];
+       size_t u, v;
+
+       for (u = 0, v = 0; name[u]; u ++) {
+               int c;
+
+               c = name[u];
+               if ((c >= '0' && c <= '9')
+                       || (c >= 'A' && c <= 'Z')
+                       || (c >= 'a' && c <= 'z'))
+               {
+                       tmp[v ++] = c;
+                       if (v == sizeof tmp) {
+                               return -1;
+                       }
+               }
+       }
+       tmp[v] = 0;
+       if (eqstring(tmp, "p256") || eqstring(tmp, "secp256r1")) {
+               return BR_EC_secp256r1;
+       } else if (eqstring(tmp, "p384") || eqstring(tmp, "secp384r1")) {
+               return BR_EC_secp384r1;
+       } else if (eqstring(tmp, "p521") || eqstring(tmp, "secp521r1")) {
+               return BR_EC_secp521r1;
+       } else {
+               return -1;
+       }
+}
+
+static void
+parse_object(char *objtype, HT *objdata, long linenum)
+{
+       char *name;
+
+       name = get_value(objtype, objdata, linenum, "name");
+       if (eqstring(objtype, "key")) {
+               char *stype;
+               br_x509_pkey *pk;
+
+               stype = get_value(objtype, objdata, linenum, "type");
+               pk = xmalloc(sizeof *pk);
+               if (eqstring(stype, "RSA")) {
+                       char *sn, *se;
+
+                       sn = get_value(objtype, objdata, linenum, "n");
+                       se = get_value(objtype, objdata, linenum, "e");
+                       pk->key_type = BR_KEYTYPE_RSA;
+                       pk->key.rsa.n = parse_hex("modulus", linenum,
+                               sn, &pk->key.rsa.nlen);
+                       pk->key.rsa.e = parse_hex("exponent", linenum,
+                               se, &pk->key.rsa.elen);
+               } else if (eqstring(stype, "EC")) {
+                       char *sc, *sq;
+                       int curve;
+
+                       sc = get_value(objtype, objdata, linenum, "curve");
+                       sq = get_value(objtype, objdata, linenum, "q");
+                       curve = string_to_curve(sc);
+                       if (curve < 0) {
+                               fprintf(stderr, "unknown curve name: '%s'"
+                                       " (line %ld)\n", sc, linenum);
+                               exit(EXIT_FAILURE);
+                       }
+                       pk->key_type = BR_KEYTYPE_EC;
+                       pk->key.ec.curve = curve;
+                       pk->key.ec.q = parse_hex("public point", linenum,
+                               sq, &pk->key.ec.qlen);
+               } else {
+                       fprintf(stderr, "unknown key type '%s' (line %ld)\n",
+                               stype, linenum);
+                       exit(EXIT_FAILURE);
+               }
+               if (HT_put(keys, name, pk) != NULL) {
+                       fprintf(stderr, "duplicate key: '%s' (line %ld)\n",
+                               name, linenum);
+                       exit(EXIT_FAILURE);
+               }
+       } else if (eqstring(objtype, "anchor")) {
+               char *dnfile, *kname, *tatype;
+               test_trust_anchor *tta;
+
+               dnfile = get_value(objtype, objdata, linenum, "DN_file");
+               kname = get_value(objtype, objdata, linenum, "key");
+               tatype = get_value(objtype, objdata, linenum, "type");
+               tta = xmalloc(sizeof *tta);
+               tta->dn = read_file(dnfile, &tta->dn_len);
+               tta->key_name = xstrdup(kname);
+               if (eqstring(tatype, "CA")) {
+                       tta->flags = BR_X509_TA_CA;
+               } else if (eqstring(tatype, "EE")) {
+                       tta->flags = 0;
+               } else {
+                       fprintf(stderr,
+                               "unknown trust anchor type: '%s' (line %ld)\n",
+                               tatype, linenum);
+               }
+               if (HT_put(trust_anchors, name, tta) != NULL) {
+                       fprintf(stderr,
+                               "duplicate trust anchor: '%s' (line %ld)\n",
+                               name, linenum);
+                       exit(EXIT_FAILURE);
+               }
+       } else if (eqstring(objtype, "chain")) {
+               test_case tc;
+               char *ktype, *kusage, *sstatus, *shashes, *stime;
+
+               ktype = get_value(objtype, objdata, linenum, "keytype");
+               kusage = get_value(objtype, objdata, linenum, "keyusage");
+               sstatus = get_value(objtype, objdata, linenum, "status");
+               tc.name = xstrdup(name);
+               tc.ta_names = split_names(
+                       get_value(objtype, objdata, linenum, "anchors"));
+               tc.cert_names = split_names(
+                       get_value(objtype, objdata, linenum, "chain"));
+               tc.servername = xstrdup(HT_get(objdata, "servername"));
+               if (eqstring(ktype, "RSA")) {
+                       tc.key_type_usage = BR_KEYTYPE_RSA;
+               } else if (eqstring(ktype, "EC")) {
+                       tc.key_type_usage = BR_KEYTYPE_EC;
+               } else {
+                       fprintf(stderr,
+                               "unknown key type: '%s' (line %ld)\n",
+                               ktype, linenum);
+                       exit(EXIT_FAILURE);
+               }
+               if (eqstring(kusage, "KEYX")) {
+                       tc.key_type_usage |= BR_KEYTYPE_KEYX;
+               } else if (eqstring(kusage, "SIGN")) {
+                       tc.key_type_usage |= BR_KEYTYPE_SIGN;
+               } else {
+                       fprintf(stderr,
+                               "unknown key usage: '%s' (line %ld)\n",
+                               kusage, linenum);
+                       exit(EXIT_FAILURE);
+               }
+               tc.status = (unsigned)atoi(sstatus);
+               if (tc.status == 0) {
+                       tc.ee_key_name = xstrdup(
+                               get_value(objtype, objdata, linenum, "eekey"));
+               } else {
+                       tc.ee_key_name = NULL;
+               }
+               shashes = HT_get(objdata, "hashes");
+               if (shashes == NULL) {
+                       tc.hashes = (unsigned)-1;
+               } else {
+                       char **hns;
+                       size_t u;
+
+                       tc.hashes = 0;
+                       hns = split_names(shashes);
+                       for (u = 0;; u ++) {
+                               char *hn;
+                               int id;
+
+                               hn = hns[u];
+                               if (hn == NULL) {
+                                       break;
+                               }
+                               id = string_to_hash(hn);
+                               if (id < 0) {
+                                       fprintf(stderr,
+                                               "unknown hash function '%s'"
+                                               " (line %ld)\n", hn, linenum);
+                                       exit(EXIT_FAILURE);
+                               }
+                               tc.hashes |= (unsigned)1 << id;
+                               xfree(hn);
+                       }
+                       xfree(hns);
+               }
+               stime = HT_get(objdata, "time");
+               if (stime == NULL) {
+                       stime = DEFAULT_TIME;
+               }
+               if (string_to_time(stime, &tc.days, &tc.seconds) < 0) {
+                       fprintf(stderr, "invalid time string '%s' (line %ld)\n",
+                               stime, linenum);
+                       exit(EXIT_FAILURE);
+               }
+               if (all_chains_ptr == all_chains_len) {
+                       if (all_chains_len == 0) {
+                               all_chains_len = 8;
+                               all_chains = xmalloc(
+                                       all_chains_len * sizeof *all_chains);
+                       } else {
+                               test_case *ntc;
+                               size_t nlen;
+
+                               nlen = all_chains_len << 1;
+                               ntc = xmalloc(nlen * sizeof *ntc);
+                               memcpy(ntc, all_chains,
+                                       all_chains_len * sizeof *all_chains);
+                               xfree(all_chains);
+                               all_chains = ntc;
+                               all_chains_len = nlen;
+                       }
+               }
+               all_chains[all_chains_ptr ++] = tc;
+       } else {
+               fprintf(stderr, "unknown section type '%s' (line %ld)\n",
+                       objtype, linenum);
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void
+process_conf_file(const char *fname)
+{
+       char *objtype;
+       HT *objdata;
+       long objlinenum;
+
+       keys = HT_new();
+       trust_anchors = HT_new();
+       all_chains = NULL;
+       all_chains_ptr = 0;
+       all_chains_len = 0;
+       conf_init(fname);
+       objtype = NULL;
+       objdata = HT_new();
+       objlinenum = 0;
+       for (;;) {
+               char *hname;
+
+               if (conf_next_line() < 0) {
+                       break;
+               }
+               hname = parse_header_name();
+               if (hname != NULL) {
+                       if (objtype != NULL) {
+                               parse_object(objtype, objdata, objlinenum);
+                               HT_clear(objdata, xfree);
+                               xfree(objtype);
+                       }
+                       objtype = hname;
+                       objlinenum = current_linenum;
+                       continue;
+               }
+               if (objtype == NULL) {
+                       fprintf(stderr, "no current section (line %ld)\n",
+                               current_linenum);
+                       exit(EXIT_FAILURE);
+               }
+               if (parse_keyvalue(objdata) < 0) {
+                       fprintf(stderr, "wrong configuration, line %ld\n",
+                               current_linenum);
+                       exit(EXIT_FAILURE);
+               }
+       }
+       if (objtype != NULL) {
+               parse_object(objtype, objdata, objlinenum);
+               xfree(objtype);
+       }
+       HT_free(objdata, xfree);
+       conf_close();
+}
+
+static const struct {
+       int id;
+       const br_hash_class *impl;
+} hash_impls[] = {
+       { br_md5_ID, &br_md5_vtable },
+       { br_sha1_ID, &br_sha1_vtable },
+       { br_sha224_ID, &br_sha224_vtable },
+       { br_sha256_ID, &br_sha256_vtable },
+       { br_sha384_ID, &br_sha384_vtable },
+       { br_sha512_ID, &br_sha512_vtable },
+       { 0, NULL }
+};
+
+typedef struct {
+       unsigned char *data;
+       size_t len;
+} blob;
+
+static int
+eqbigint(const unsigned char *b1, size_t b1_len,
+       const unsigned char *b2, size_t b2_len)
+{
+       while (b1_len > 0 && *b1 == 0) {
+               b1 ++;
+               b1_len --;
+       }
+       while (b2_len > 0 && *b2 == 0) {
+               b2 ++;
+               b2_len --;
+       }
+       return b1_len == b2_len && memcmp(b1, b2, b1_len) == 0;
+}
+
+static int
+eqpkey(const br_x509_pkey *pk1, const br_x509_pkey *pk2)
+{
+       if (pk1 == pk2) {
+               return 1;
+       }
+       if (pk1 == NULL || pk2 == NULL) {
+               return 0;
+       }
+       if (pk1->key_type != pk2->key_type) {
+               return 0;
+       }
+       switch (pk1->key_type) {
+       case BR_KEYTYPE_RSA:
+               return eqbigint(pk1->key.rsa.n, pk1->key.rsa.nlen,
+                       pk2->key.rsa.n, pk2->key.rsa.nlen)
+                       && eqbigint(pk1->key.rsa.e, pk1->key.rsa.elen,
+                       pk2->key.rsa.e, pk2->key.rsa.elen);
+       case BR_KEYTYPE_EC:
+               return pk1->key.ec.curve == pk2->key.ec.curve
+                       && pk1->key.ec.qlen == pk2->key.ec.qlen
+                       && memcmp(pk1->key.ec.q,
+                               pk2->key.ec.q, pk1->key.ec.qlen) == 0;
+       default:
+               fprintf(stderr, "unknown key type: %d\n", pk1->key_type);
+               exit(EXIT_FAILURE);
+               break;
+       }
+       return 0;
+}
+
+static size_t max_dp_usage;
+static size_t max_rp_usage;
+
+static void
+run_test_case(test_case *tc)
+{
+       br_x509_minimal_context ctx;
+       br_x509_trust_anchor *anchors;
+       size_t num_anchors;
+       size_t u;
+       const br_hash_class *dnhash;
+       size_t num_certs;
+       blob *certs;
+       br_x509_pkey *ee_pkey_ref;
+       const br_x509_pkey *ee_pkey;
+       unsigned status;
+
+       printf("%s: ", tc->name);
+       fflush(stdout);
+
+       /*
+        * Get the hash function to use for hashing DN. We can use just
+        * any supported hash function, but for the elegance of things,
+        * we will use one of the hash function implementations
+        * supported for this test case (with SHA-1 as fallback).
+        */
+       dnhash = &br_sha1_vtable;
+       for (u = 0; hash_impls[u].id; u ++) {
+               if ((tc->hashes & ((unsigned)1 << (hash_impls[u].id))) != 0) {
+                       dnhash = hash_impls[u].impl;
+               }
+       }
+
+       /*
+        * Get trust anchors.
+        */
+       for (num_anchors = 0; tc->ta_names[num_anchors]; num_anchors ++);
+       anchors = xmalloc(num_anchors * sizeof *anchors);
+       for (u = 0; tc->ta_names[u]; u ++) {
+               test_trust_anchor *tta;
+               br_x509_pkey *tak;
+
+               tta = HT_get(trust_anchors, tc->ta_names[u]);
+               if (tta == NULL) {
+                       fprintf(stderr, "no such trust anchor: '%s'\n",
+                               tc->ta_names[u]);
+                       exit(EXIT_FAILURE);
+               }
+               tak = HT_get(keys, tta->key_name);
+               if (tak == NULL) {
+                       fprintf(stderr, "no such public key: '%s'\n",
+                               tta->key_name);
+                       exit(EXIT_FAILURE);
+               }
+               anchors[u].dn = tta->dn;
+               anchors[u].dn_len = tta->dn_len;
+               anchors[u].flags = tta->flags;
+               anchors[u].pkey = *tak;
+       }
+
+       /*
+        * Read all relevant certificates.
+        */
+       for (num_certs = 0; tc->cert_names[num_certs]; num_certs ++);
+       certs = xmalloc(num_certs * sizeof *certs);
+       for (u = 0; u < num_certs; u ++) {
+               certs[u].data = read_file(tc->cert_names[u], &certs[u].len);
+       }
+
+       /*
+        * Get expected EE public key (if any).
+        */
+       if (tc->ee_key_name == NULL) {
+               ee_pkey_ref = NULL;
+       } else {
+               ee_pkey_ref = HT_get(keys, tc->ee_key_name);
+               if (ee_pkey_ref == NULL) {
+                       fprintf(stderr, "no such public key: '%s'\n",
+                               tc->ee_key_name);
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       /*
+        * Initialise the engine.
+        */
+       br_x509_minimal_init(&ctx, dnhash, anchors, num_anchors);
+       for (u = 0; hash_impls[u].id; u ++) {
+               int id;
+
+               id = hash_impls[u].id;
+               if ((tc->hashes & ((unsigned)1 << id)) != 0) {
+                       br_x509_minimal_set_hash(&ctx, id, hash_impls[u].impl);
+               }
+       }
+       br_x509_minimal_set_rsa(&ctx, br_rsa_i32_pkcs1_vrfy);
+       br_x509_minimal_set_ecdsa(&ctx,
+               &br_ec_prime_i31, br_ecdsa_i31_vrfy_asn1);
+
+       /*
+        * Set the validation date.
+        */
+       br_x509_minimal_set_time(&ctx, tc->days, tc->seconds);
+
+       /*
+        * Put "canaries" to detect actual stack usage.
+        */
+       for (u = 0; u < (sizeof ctx.dp_stack) / sizeof(uint32_t); u ++) {
+               ctx.dp_stack[u] = 0xA7C083FE;
+       }
+       for (u = 0; u < (sizeof ctx.rp_stack) / sizeof(uint32_t); u ++) {
+               ctx.rp_stack[u] = 0xA7C083FE;
+       }
+
+       /*
+        * Run the engine. We inject certificates by chunks of 100 bytes
+        * in order to exercise the coroutine API.
+        */
+       ctx.vtable->start_chain(&ctx.vtable,
+               tc->key_type_usage, tc->servername);
+       for (u = 0; u < num_certs; u ++) {
+               size_t v;
+
+               ctx.vtable->start_cert(&ctx.vtable, certs[u].len);
+               v = 0;
+               while (v < certs[u].len) {
+                       size_t w;
+
+                       w = certs[u].len - v;
+                       if (w > 100) {
+                               w = 100;
+                       }
+                       ctx.vtable->append(&ctx.vtable, certs[u].data + v, w);
+                       v += w;
+               }
+               ctx.vtable->end_cert(&ctx.vtable);
+       }
+       status = ctx.vtable->end_chain(&ctx.vtable);
+       ee_pkey = ctx.vtable->get_pkey(&ctx.vtable);
+
+       /*
+        * Check results. Note that we may still get a public key if
+        * the path is "not trusted" (but otherwise fine).
+        */
+       if (status != tc->status) {
+               fprintf(stderr, "wrong status (got %d, expected %d)\n",
+                       status, tc->status);
+               exit(EXIT_FAILURE);
+       }
+       if (status == BR_ERR_X509_NOT_TRUSTED) {
+               ee_pkey = NULL;
+       }
+       if (!eqpkey(ee_pkey, ee_pkey_ref)) {
+               fprintf(stderr, "wrong EE public key\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Check stack usage.
+        */
+       for (u = (sizeof ctx.dp_stack) / sizeof(uint32_t); u > 0; u --) {
+               if (ctx.dp_stack[u - 1] != 0xA7C083FE) {
+                       if (max_dp_usage < u) {
+                               max_dp_usage = u;
+                       }
+                       break;
+               }
+       }
+       for (u = (sizeof ctx.rp_stack) / sizeof(uint32_t); u > 0; u --) {
+               if (ctx.rp_stack[u - 1] != 0xA7C083FE) {
+                       if (max_rp_usage < u) {
+                               max_rp_usage = u;
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * Release everything.
+        */
+       for (u = 0; u < num_certs; u ++) {
+               xfree(certs[u].data);
+       }
+       xfree(certs);
+       xfree(anchors);
+       printf("OK\n");
+}
+
+int
+main(void)
+{
+       size_t u;
+
+       process_conf_file(CONFFILE);
+
+       max_dp_usage = 0;
+       max_rp_usage = 0;
+       for (u = 0; u < all_chains_ptr; u ++) {
+               run_test_case(&all_chains[u]);
+       }
+
+       printf("Maximum data stack usage:    %u\n", (unsigned)max_dp_usage);
+       printf("Maximum return stack usage:  %u\n", (unsigned)max_rp_usage);
+
+       HT_free(keys, free_key);
+       HT_free(trust_anchors, free_trust_anchor);
+       for (u = 0; u < all_chains_ptr; u ++) {
+               free_test_case_contents(&all_chains[u]);
+       }
+       xfree(all_chains);
+       return 0;
+}
diff --git a/test/x509/alltests.txt b/test/x509/alltests.txt
new file mode 100644 (file)
index 0000000..e92de8b
--- /dev/null
@@ -0,0 +1,660 @@
+; Most/all of these test chains use the same structure:
+;    root -> ica1 -> ica2 -> ee
+; "ica1" is "Intermediate CA 1"
+; "ee" is "end-entity", i.e. the client or server certificate itself
+;
+; In SSL/TLS order, the EE comes first. The root may or may not be included
+; as a self-signed certificate.
+
+[key]
+name = root-rsa2048
+type = RSA
+n = B6D934D450FDB3AF7A73F1CE38BF5D6F45E1FD4EB198C6608326D217D1C5B79AA3C1DE6339979CF05E5CC81C17B988196DF0B62E3050A1546E93C0DBCF30CB9F1E2779F1C3995235AA3DB6DFB0AD7CCB49CDC0EDE766102AE9CE281F2150FA774C2DDAEF3C58EB4EBFCEE9FB1ADAA383A3CDA3CA9380DCDAF317CC7AAB33809CB2D47F463FC53CDC6194B727296E2ABC5B0936D4C63B0DEBBECEDB1D1CBC106A7171B3F2CA289A77F28AEC42EFB14A8EE2F21A322ACDC0A6462C9AC28537917F46A19381A17466DFBAB339209193FA1DA1A885E7E4F907F610F6A82701B67F12C340C3C9E2B0AB49183A64B659B795B59636DF2269AA726A544E2729A30E9715
+e = 010001
+
+[key]
+name = root-p256
+type = EC
+curve = P-256
+q = 047174BAABB9302E81D5E557F9F320680C9CF964DBB4200D6DEA40D04A6E42FDB69A682544F6DF7BC4FCDEDD7BBBC5DB7C763F4166406EDBA787C2E5D8C5F37F8D
+
+[key]
+name = root-p384
+type = EC
+curve = P-384
+q = 040ED28B3F7F0A38A6DB72CB4DAC8198C3D595BFABEE2E4A3CC6797F1A272C57AD715F96B5FDA29C4DD87B75B1438B6A92C4FD0282A3080A857F28AB31FF8B49F805470A01EE551F7F27C914E7E780AE474558D6F5539BAE806626514FE560478B
+
+[key]
+name = root-p521
+type = EC
+curve = P-521
+q = 040168E669615D1B20F2E753D2C86312F51094D3E5C6CF49E8D73418278CD769FE40A84AD4F34865D59D94D5685B389E0CFD0450754CAE81ED1D4A91D0773F7A002ED701DEF2DBDEFC7554E74CD600693DBDE1A7E09CD9044774C744C7CE575BF8B645FF79FCCE06116F61D44FDAE62D3046F4EB41DECB8219B279A5B8CE2A47F3DF0D463B
+
+[key]
+name = ica1-rsa2048
+type = RSA
+n = B3E86BAF9C1652E3810C50AB25CECC0DC7F21F7F50DF2C5C35D6622E632741A7E453A84B27FA1391A3FA094A2F3B5ECF77B38AC1CD49959C750D6474EFE4D74BB9A19B68D2307148EAF74B14DF3F47A9D8BBEC8F28CCFADFB41F947C96FC080528F9E8F42F2FEE629C8A3AE0855860B60F2D30B4C04154914C1F5FADF119F0C022A67DD83F793459427B5BB541C4647F52CF3C3722A12F7925942441C23FFAC775FB48B50D18A7F454F32E6ED84358C4AB50E805AD91B61E0175B3549CDEA09915FBACF15C974951CCEF58126F736BB33414010F5A9DFAAAD693D3E2EAC3ABBC4EEDCC51A1B8F894B6B42CA8862B1FF6514329525E1389B36A78604E4EC01BA5
+e = 010001
+
+[key]
+name = ica2-rsa2048
+type = RSA
+n = AE15F7CBEEE3961BCA63D22681B2D8163423735684FCFDB2E98E9DD0D9D0D706A191EF8D4F604E16BDE6529EE557867B7A7FFCBC34AD86EC9150ADD5C7D18D83E95ABA2FDB0DB92131FAA2FD91EC37836261809C6A82253309DF8F7893EACFDB93B0A2687CEA873E369C4B379A71E52084C3789A2BA42C7E76D561A9131272F14B411BC6A555BA9D8965C06699C0F17C9B61B24E6601B0A9C4FDC8C1D0C789BE2746DE6271BA27F52A850F436026BC2A9D07AAD608DC26D86956A1D308DED858936B0EC2AF783E2574D49F001820BFD7158DB9D13D8900A01264E186C0D580F124B2D2FB4C677CAE3DC9BF47026DE47C0D4490518CCD026237F86FB96701C695
+e = 010001
+
+[key]
+name = ee-rsa2048
+type = RSA
+n = D47A1D27BA2B3A67B2916AFBE78344CAED1C75ADDD4D8362D6AA6895B224217B15AE2A996815ED66F0B858E7D3F52EC6D92A5EE70E2EE7FC6759C0C8617D4BA46FDD9FD9C8858764C7BA1A0F29D496A8789A6B6220A932D0EEA98C286147A2502A63F621DEDAD8D5F07FC5008270E6A3BF5C89274F51927703C3B0CC2E3BEC23F22F5341AF8993FFD280B14397DED619A092127A3D6679E1C1BCE17770A28B3D4684533FE44E424137921E1FFD38B3F7EF873980D356CFF4E013DE64B072A40384C441ED6FFA3EE2CA0420D2D7DC2C822B7AE26DA11C48DBCF894F34973D28A853DAE7C1E17315A330767F8F2342143D5134D25AAD3C9BCBC8FE7F6E8E40F3BD
+e = 010001
+
+[key]
+name = ee-p256
+type = EC
+curve = P-256
+q = 045F389DA7FF4D8AAFF63439461AFC3ADFF423AAA9EAFBC508DE008EBE79A537584C6DDD01CAAB47DF89B6C7171F38FC1D2014DD45C0E08F934E380BFCE999A149
+
+[key]
+name = ee-p384
+type = EC
+curve = P-384
+q = 0415A488877F3D14830E29A1C2F2C0745CE8CF5E684304D1668972389BA615B34E9648D5A7861E49DFFFBFFFEAD7FC6AF11BC4516C3557332DD86DDFDE2A236CCEA844EBD594CCD3ED5B7AE0061BD6595737B59FE754BCDAB6FE38D34D93DBBF30
+
+[key]
+name = ee-p521
+type = EC
+curve = P-521
+q = 040060547ACA9D520FB3272833236CBF8E71AC286A3001FBB1E2C3FD8BAB0817DDE4E4FA53550F120D678F4D55AE4FF36C7C8EAE9E32A08A44FC66F45331E08946077A0139B87FE54B986012A94838C8006034941CD0512E596436D2E8E61CA93585D5C06EAD5094585B5B2A3E013803B3E6AAA1D4156EF09E8352029BB70AC6BF338F918B
+
+; Trust anchor: the root.
+[anchor]
+name = root
+DN_file = dn-root.der
+key = root-rsa2048
+type = CA
+
+; Trust anchor: root with an ECDSA key (in P-256 curve)
+[anchor]
+name = root-p256
+DN_file = dn-root.der
+key = root-p256
+type = CA
+
+; Trust anchor: root with an ECDSA key (in P-384 curve)
+[anchor]
+name = root-p384
+DN_file = dn-root.der
+key = root-p384
+type = CA
+
+; Trust anchor: root with an ECDSA key (in P-521 curve)
+[anchor]
+name = root-p521
+DN_file = dn-root.der
+key = root-p521
+type = CA
+
+; Intermediate CA 1 as trust anchor.
+[anchor]
+name = ica1
+DN_file = dn-ica1.der
+key = ica1-rsa2048
+type = CA
+
+; Intermediate CA 2 as trust anchor.
+[anchor]
+name = ica2
+DN_file = dn-ica2.der
+key = ica2-rsa2048
+type = CA
+
+; EE certificate as trust anchor (direct trust only).
+[anchor]
+name = ee
+DN_file = dn-ee.der
+key = ee-rsa2048
+type = EE
+
+; Base valid chain.
+[chain]
+name = base
+anchors = root
+chain = ee.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Valid chain except that no trust anchor is provided; this should fail
+; with BR_ERR_X509_NOT_TRUSTED.
+[chain]
+name = noTA
+anchors = 
+chain = ee.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 62
+
+; Use of intermediate CA 1 as anchor (extra certificates are ignored).
+[chain]
+name = anchorICA1
+anchors = ica1
+chain = ee.crt ica2.crt junk.crt junk.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Use of intermediate CA 2 as anchor (extra certificates are ignored).
+[chain]
+name = anchorICA2
+anchors = ica2
+chain = ee.crt junk.crt junk.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Direct trust of EE.
+[chain]
+name = directTrust
+anchors = ee
+chain = ee.crt junk.crt junk.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Server name check: name does not match the SAN nor the CN.
+[chain]
+name = wrongName1
+anchors = root
+chain = ee.crt ica2.crt ica1.crt
+servername = foo.example.com
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Server name check: name matches the CN but not the SAN, and there is
+; a SAN so the CN is ignored.
+[chain]
+name = wrongName2
+anchors = root
+chain = ee-names.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Server name check: name does not match CN, but matches the first SAN
+; name.
+[chain]
+name = goodName1
+anchors = root
+chain = ee-names.crt ica2.crt ica1.crt
+servername = foo.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Server name check: name does not match CN, but matches the second SAN
+; name.
+[chain]
+name = goodName2
+anchors = root
+chain = ee-names.crt ica2.crt ica1.crt
+servername = barqux.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Server name check: no SAN, but the CN matches the server name.
+[chain]
+name = goodName3
+anchors = root
+chain = ee-names2.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Server name check: no SAN, and the CN does not match the server name.
+[chain]
+name = wrongName3
+anchors = root
+chain = ee-names2.crt ica2.crt ica1.crt
+servername = foo.example.com
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Server name check: no SAN, and the CN does not match the server name,
+; although its byte contents seem to match (but with BMPString encoding).
+[chain]
+name = wrongName4
+anchors = root
+chain = ee-names3.crt ica2.crt ica1.crt
+servername = www1.example.com
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Server name check: no SAN, and the CN uses BMPString encoding, but we
+; do not actually request a server name check, so this should pass.
+[chain]
+name = ignoreName1
+anchors = root
+chain = ee-names3.crt ica2.crt ica1.crt
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Wildcard processing: the name 'localhost' should not match because
+; the engine recognises the wildcard only in a '*.' starting sequence,
+; so the lone '*' in a SAN will not be accepted.
+[chain]
+name = wildcard1
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = localhost
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Wildcard processing: the name 'example.com' will be matched by '*.com'.
+[chain]
+name = wildcard2
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Wildcard processing: the name 'www.example.com' will be matched by
+; '*.example.com'.
+[chain]
+name = wildcard3
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Wildcard processing: the name 'foo.foo.example.com' will not be matched by
+; 'foo.*.example.com' because we accept the wildcard only in the first name
+; component.
+[chain]
+name = wildcard4
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = foo.foo.example.com
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Wildcard processing: the name 'foo.bar.example.com' will not be matched by
+; 'foo.*.example.com', but '*.bar.example.com' will fit.
+[chain]
+name = wildcard5
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = foo.bar.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Wildcard processing: the name 'foo.bar.example.foobar' will not be matched by
+; '*.*.example.foobar' because we support only a single level of wildcard.
+[chain]
+name = wildcard6
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = foo.bar.example.foobar
+keytype = RSA
+keyusage = KEYX
+status = 56
+
+; Wildcard processing: the name 'foo.*.example.foobar' will be matched
+; by '*.*.example.foobar' because the '*' in the provided server name matches
+; the second '*' in '*.*.example.foobar'. This is a corner case with no
+; practical impact because expected server names are usually extracted from
+; URL and cannot have embedded '*' in them.
+[chain]
+name = wildcard7
+anchors = root
+chain = ee-names4.crt ica2.crt ica1.crt
+servername = foo.*.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Hash function support: the chain uses only SHA-256.
+[chain]
+name = hashSHA256Only
+anchors = root
+chain = ee.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+hashes = sha256
+eekey = ee-rsa2048
+status = 0
+
+; Hash function support: the chain uses only SHA-256.
+[chain]
+name = hashSHA256Unsupported
+anchors = root
+chain = ee.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+hashes = md5 sha1 sha224 sha384 sha512
+status = 49
+
+; Hash function support: signature on EE uses SHA-1.
+[chain]
+name = hashSHA1
+anchors = root
+chain = ee-sha1.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Hash function support: signature on EE uses SHA-224.
+[chain]
+name = hashSHA224
+anchors = root
+chain = ee-sha224.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Hash function support: signature on EE uses SHA-384.
+[chain]
+name = hashSHA384
+anchors = root
+chain = ee-sha384.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Hash function support: signature on EE uses SHA-512.
+[chain]
+name = hashSHA512
+anchors = root
+chain = ee-sha512.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Hash function support: signature on EE uses MD5. This is rejected by
+; the engine (even though MD5 is supported as a hash function).
+[chain]
+name = hashMD5
+anchors = root
+chain = ee-md5.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 49
+
+; EE certificate has trailing garbage (an extra byte), which should be
+; rejected.
+[chain]
+name = trailingGarbage
+anchors = root
+chain = ee-trailing.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 40
+
+; Signature on EE certificate is incorrect (one byte modified in signature).
+[chain]
+name = badSignature1
+anchors = root
+chain = ee-badsig1.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 52
+
+; Signature on EE certificate is incorrect (one byte modified in serial
+; number).
+[chain]
+name = badSignature2
+anchors = root
+chain = ee-badsig2.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 52
+
+; Signature on EE certificate is incorrect but this is ignored because we
+; use a direct trust model here.
+[chain]
+name = ignoredSignature1
+anchors = ee
+chain = ee-badsig1.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Signature on EE certificate is incorrect but this is ignored because we
+; use a direct trust model here.
+[chain]
+name = ignoredSignature2
+anchors = ee
+chain = ee-badsig2.crt ica2.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Intermediate CA 1 has a 1016-bit RSA key, which should be rejected
+; with BR_ERR_X509_WEAK_PUBLIC_KEY.
+[chain]
+name = rsa1016
+anchors = root
+chain = ee.crt ica2-1016.crt ica1-1016.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 60
+
+; Intermediate CA 1 has a 1017-bit RSA key, which should be accepted
+; (because that's 128 bytes, which is the lower limit).
+[chain]
+name = rsa1017
+anchors = root
+chain = ee.crt ica2-1017.crt ica1-1017.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; Intermediate CA 1 has a 4096-bit RSA key, which should be supported.
+[chain]
+name = rsa4096
+anchors = root
+chain = ee.crt ica2-4096.crt ica1-4096.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; EE is valid from 2010/02/17 11:40:35 to 2098/07/20 15:11:08. The
+; start date is in UTCTime, the end date is in GeneralizedTime.
+[chain]
+name = date1
+anchors = ica2
+chain = ee-dates.crt ica2.crt ica1.crt
+time = 2010-02-17 11:40:34Z
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 54
+
+; EE is valid from 2010/02/17 11:40:35 to 2098/07/20 15:11:08. The
+; start date is in UTCTime, the end date is in GeneralizedTime.
+[chain]
+name = date2
+anchors = ica2
+chain = ee-dates.crt ica2.crt ica1.crt
+time = 2010-02-17 11:40:36Z
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; EE is valid from 2010/02/17 11:40:35 to 2098/07/20 15:11:08. The
+; start date is in UTCTime, the end date is in GeneralizedTime.
+[chain]
+name = date3
+anchors = ica2
+chain = ee-dates.crt ica2.crt ica1.crt
+time = 2098-07-20 15:11:07Z
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+eekey = ee-rsa2048
+status = 0
+
+; EE is valid from 2010/02/17 11:40:35 to 2098/07/20 15:11:08. The
+; start date is in UTCTime, the end date is in GeneralizedTime.
+[chain]
+name = date4
+anchors = ica2
+chain = ee-dates.crt ica2.crt ica1.crt
+time = 2098-07-20 15:11:09Z
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 54
+
+; Intermediate CA 2 certificate is not a CA.
+[chain]
+name = notCA
+anchors = root
+chain = ee-dates.crt ica2-notCA.crt ica1.crt
+servername = www.example.com
+keytype = RSA
+keyusage = KEYX
+status = 58
+
+; A chain using ECDSA with P-256.
+[chain]
+name = secp256r1
+anchors = root-p256
+chain = ee-p256.crt ica2-p256.crt ica1-p256.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p256
+status = 0
+
+; A chain using ECDSA with P-384.
+[chain]
+name = secp384r1
+anchors = root-p384
+chain = ee-p384.crt ica2-p384.crt ica1-p384.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p384
+status = 0
+
+; A chain using ECDSA with P-521.
+[chain]
+name = secp521r1
+anchors = root-p521
+chain = ee-p521.crt ica2-p521.crt ica1-p521.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p521
+status = 0
+
+; A chain using ECDSA with P-256, signature on EE uses SHA-1.
+[chain]
+name = secp256r1-sha1
+anchors = root-p256
+chain = ee-p256-sha1.crt ica2-p256.crt ica1-p256.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p256
+status = 0
+
+; A chain using ECDSA with P-256, signature on EE uses SHA-224.
+[chain]
+name = secp256r1-sha224
+anchors = root-p256
+chain = ee-p256-sha224.crt ica2-p256.crt ica1-p256.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p256
+status = 0
+
+; A chain using ECDSA with P-256, signature on EE uses SHA-256.
+[chain]
+name = secp256r1-sha256
+anchors = root-p256
+chain = ee-p256-sha256.crt ica2-p256.crt ica1-p256.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p256
+status = 0
+
+; A chain using ECDSA with P-256, signature on EE uses SHA-384.
+[chain]
+name = secp256r1-sha384
+anchors = root-p256
+chain = ee-p256-sha384.crt ica2-p256.crt ica1-p256.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p256
+status = 0
+
+; A chain using ECDSA with P-256, signature on EE uses SHA-512.
+[chain]
+name = secp256r1-sha512
+anchors = root-p256
+chain = ee-p256-sha512.crt ica2-p256.crt ica1-p256.crt
+servername = www.example.com
+keytype = EC
+keyusage = SIGN
+eekey = ee-p256
+status = 0
diff --git a/test/x509/dn-ee.der b/test/x509/dn-ee.der
new file mode 100644 (file)
index 0000000..ca1c9ed
--- /dev/null
@@ -0,0 +1 @@
+0'1\v0  \ 6\ 3U\ 4\ 6\13\ 2CA1\180\16\ 6\ 3U\ 4\ 3\13\ fwww.example.com
\ No newline at end of file
diff --git a/test/x509/dn-ica1.der b/test/x509/dn-ica1.der
new file mode 100644 (file)
index 0000000..3b0103f
--- /dev/null
@@ -0,0 +1 @@
+011\v0  \ 6\ 3U\ 4\ 6\13\ 2CA1"0 \ 6\ 3U\ 4\ 3\13\19Example Intermediate CA 1
\ No newline at end of file
diff --git a/test/x509/dn-ica2.der b/test/x509/dn-ica2.der
new file mode 100644 (file)
index 0000000..d8cddad
--- /dev/null
@@ -0,0 +1 @@
+011\v0  \ 6\ 3U\ 4\ 6\13\ 2CA1"0 \ 6\ 3U\ 4\ 3\13\19Example Intermediate CA 2
\ No newline at end of file
diff --git a/test/x509/dn-root.der b/test/x509/dn-root.der
new file mode 100644 (file)
index 0000000..ea891f9
--- /dev/null
@@ -0,0 +1 @@
+0$1\v0  \ 6\ 3U\ 4\ 6\13\ 2CA1\150\13\ 6\ 3U\ 4\ 3\13\fExample Root
\ No newline at end of file
diff --git a/test/x509/ee-badsig1.crt b/test/x509/ee-badsig1.crt
new file mode 100644 (file)
index 0000000..03bcc77
Binary files /dev/null and b/test/x509/ee-badsig1.crt differ
diff --git a/test/x509/ee-badsig2.crt b/test/x509/ee-badsig2.crt
new file mode 100644 (file)
index 0000000..127309d
Binary files /dev/null and b/test/x509/ee-badsig2.crt differ
diff --git a/test/x509/ee-dates.crt b/test/x509/ee-dates.crt
new file mode 100644 (file)
index 0000000..9dfa40a
Binary files /dev/null and b/test/x509/ee-dates.crt differ
diff --git a/test/x509/ee-md5.crt b/test/x509/ee-md5.crt
new file mode 100644 (file)
index 0000000..1d5724d
Binary files /dev/null and b/test/x509/ee-md5.crt differ
diff --git a/test/x509/ee-names.crt b/test/x509/ee-names.crt
new file mode 100644 (file)
index 0000000..5631de8
Binary files /dev/null and b/test/x509/ee-names.crt differ
diff --git a/test/x509/ee-names2.crt b/test/x509/ee-names2.crt
new file mode 100644 (file)
index 0000000..f1f8133
Binary files /dev/null and b/test/x509/ee-names2.crt differ
diff --git a/test/x509/ee-names3.crt b/test/x509/ee-names3.crt
new file mode 100644 (file)
index 0000000..2b6bb2a
Binary files /dev/null and b/test/x509/ee-names3.crt differ
diff --git a/test/x509/ee-names4.crt b/test/x509/ee-names4.crt
new file mode 100644 (file)
index 0000000..6c24d1f
Binary files /dev/null and b/test/x509/ee-names4.crt differ
diff --git a/test/x509/ee-p256-sha1.crt b/test/x509/ee-p256-sha1.crt
new file mode 100644 (file)
index 0000000..6da9ca7
Binary files /dev/null and b/test/x509/ee-p256-sha1.crt differ
diff --git a/test/x509/ee-p256-sha224.crt b/test/x509/ee-p256-sha224.crt
new file mode 100644 (file)
index 0000000..165868b
Binary files /dev/null and b/test/x509/ee-p256-sha224.crt differ
diff --git a/test/x509/ee-p256-sha256.crt b/test/x509/ee-p256-sha256.crt
new file mode 100644 (file)
index 0000000..de3c29e
Binary files /dev/null and b/test/x509/ee-p256-sha256.crt differ
diff --git a/test/x509/ee-p256-sha384.crt b/test/x509/ee-p256-sha384.crt
new file mode 100644 (file)
index 0000000..c117aca
Binary files /dev/null and b/test/x509/ee-p256-sha384.crt differ
diff --git a/test/x509/ee-p256-sha512.crt b/test/x509/ee-p256-sha512.crt
new file mode 100644 (file)
index 0000000..f8a6016
Binary files /dev/null and b/test/x509/ee-p256-sha512.crt differ
diff --git a/test/x509/ee-p256.crt b/test/x509/ee-p256.crt
new file mode 100644 (file)
index 0000000..de3c29e
Binary files /dev/null and b/test/x509/ee-p256.crt differ
diff --git a/test/x509/ee-p384.crt b/test/x509/ee-p384.crt
new file mode 100644 (file)
index 0000000..bda5ea9
Binary files /dev/null and b/test/x509/ee-p384.crt differ
diff --git a/test/x509/ee-p521.crt b/test/x509/ee-p521.crt
new file mode 100644 (file)
index 0000000..281160c
Binary files /dev/null and b/test/x509/ee-p521.crt differ
diff --git a/test/x509/ee-sha1.crt b/test/x509/ee-sha1.crt
new file mode 100644 (file)
index 0000000..6fbadac
Binary files /dev/null and b/test/x509/ee-sha1.crt differ
diff --git a/test/x509/ee-sha224.crt b/test/x509/ee-sha224.crt
new file mode 100644 (file)
index 0000000..7f1fa9d
Binary files /dev/null and b/test/x509/ee-sha224.crt differ
diff --git a/test/x509/ee-sha384.crt b/test/x509/ee-sha384.crt
new file mode 100644 (file)
index 0000000..15e3ffc
Binary files /dev/null and b/test/x509/ee-sha384.crt differ
diff --git a/test/x509/ee-sha512.crt b/test/x509/ee-sha512.crt
new file mode 100644 (file)
index 0000000..f5721a8
Binary files /dev/null and b/test/x509/ee-sha512.crt differ
diff --git a/test/x509/ee-trailing.crt b/test/x509/ee-trailing.crt
new file mode 100644 (file)
index 0000000..810c94c
Binary files /dev/null and b/test/x509/ee-trailing.crt differ
diff --git a/test/x509/ee.crt b/test/x509/ee.crt
new file mode 100644 (file)
index 0000000..8914d31
Binary files /dev/null and b/test/x509/ee.crt differ
diff --git a/test/x509/ica1-1016.crt b/test/x509/ica1-1016.crt
new file mode 100644 (file)
index 0000000..4735602
Binary files /dev/null and b/test/x509/ica1-1016.crt differ
diff --git a/test/x509/ica1-1017.crt b/test/x509/ica1-1017.crt
new file mode 100644 (file)
index 0000000..3f79466
Binary files /dev/null and b/test/x509/ica1-1017.crt differ
diff --git a/test/x509/ica1-4096.crt b/test/x509/ica1-4096.crt
new file mode 100644 (file)
index 0000000..9cc49bc
Binary files /dev/null and b/test/x509/ica1-4096.crt differ
diff --git a/test/x509/ica1-p256.crt b/test/x509/ica1-p256.crt
new file mode 100644 (file)
index 0000000..e96d9af
Binary files /dev/null and b/test/x509/ica1-p256.crt differ
diff --git a/test/x509/ica1-p384.crt b/test/x509/ica1-p384.crt
new file mode 100644 (file)
index 0000000..695f8a6
Binary files /dev/null and b/test/x509/ica1-p384.crt differ
diff --git a/test/x509/ica1-p521.crt b/test/x509/ica1-p521.crt
new file mode 100644 (file)
index 0000000..017da0b
Binary files /dev/null and b/test/x509/ica1-p521.crt differ
diff --git a/test/x509/ica1.crt b/test/x509/ica1.crt
new file mode 100644 (file)
index 0000000..ea3bfe3
Binary files /dev/null and b/test/x509/ica1.crt differ
diff --git a/test/x509/ica2-1016.crt b/test/x509/ica2-1016.crt
new file mode 100644 (file)
index 0000000..55ab956
Binary files /dev/null and b/test/x509/ica2-1016.crt differ
diff --git a/test/x509/ica2-1017.crt b/test/x509/ica2-1017.crt
new file mode 100644 (file)
index 0000000..2b88a5c
Binary files /dev/null and b/test/x509/ica2-1017.crt differ
diff --git a/test/x509/ica2-4096.crt b/test/x509/ica2-4096.crt
new file mode 100644 (file)
index 0000000..efc702d
Binary files /dev/null and b/test/x509/ica2-4096.crt differ
diff --git a/test/x509/ica2-notCA.crt b/test/x509/ica2-notCA.crt
new file mode 100644 (file)
index 0000000..21fb835
Binary files /dev/null and b/test/x509/ica2-notCA.crt differ
diff --git a/test/x509/ica2-p256.crt b/test/x509/ica2-p256.crt
new file mode 100644 (file)
index 0000000..94f8cca
Binary files /dev/null and b/test/x509/ica2-p256.crt differ
diff --git a/test/x509/ica2-p384.crt b/test/x509/ica2-p384.crt
new file mode 100644 (file)
index 0000000..5dcbb0c
Binary files /dev/null and b/test/x509/ica2-p384.crt differ
diff --git a/test/x509/ica2-p521.crt b/test/x509/ica2-p521.crt
new file mode 100644 (file)
index 0000000..d7d4757
Binary files /dev/null and b/test/x509/ica2-p521.crt differ
diff --git a/test/x509/ica2.crt b/test/x509/ica2.crt
new file mode 100644 (file)
index 0000000..09bdaa6
Binary files /dev/null and b/test/x509/ica2.crt differ
diff --git a/test/x509/junk.crt b/test/x509/junk.crt
new file mode 100644 (file)
index 0000000..54e2084
Binary files /dev/null and b/test/x509/junk.crt differ
diff --git a/test/x509/root-p256.crt b/test/x509/root-p256.crt
new file mode 100644 (file)
index 0000000..819654a
Binary files /dev/null and b/test/x509/root-p256.crt differ
diff --git a/test/x509/root-p384.crt b/test/x509/root-p384.crt
new file mode 100644 (file)
index 0000000..bd397d5
Binary files /dev/null and b/test/x509/root-p384.crt differ
diff --git a/test/x509/root-p521.crt b/test/x509/root-p521.crt
new file mode 100644 (file)
index 0000000..a95e82b
Binary files /dev/null and b/test/x509/root-p521.crt differ
diff --git a/test/x509/root.crt b/test/x509/root.crt
new file mode 100644 (file)
index 0000000..4352b51
Binary files /dev/null and b/test/x509/root.crt differ
diff --git a/tools/brssl.c b/tools/brssl.c
new file mode 100644 (file)
index 0000000..982dc18
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static void
+usage(void)
+{
+       fprintf(stderr, "usage: brssl command [ options ]\n");
+       fprintf(stderr, "available commands:\n");
+       fprintf(stderr, "   client       run SSL client\n");
+       fprintf(stderr, "   server       run SSL server\n");
+       fprintf(stderr, "   verify       verify certificate chain\n");
+       fprintf(stderr, "   skey         decode private key\n");
+       fprintf(stderr, "   ta           decode trust anchors\n");
+       fprintf(stderr, "   chain        make C code for certificate chains\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *cmd;
+
+       if (argc < 2) {
+               usage();
+               return EXIT_FAILURE;
+       }
+       cmd = argv[1];
+       if (eqstr(cmd, "client")) {
+               if (do_client(argc - 2, argv + 2) < 0) {
+                       return EXIT_FAILURE;
+               }
+       } else if (eqstr(cmd, "server")) {
+               if (do_server(argc - 2, argv + 2) < 0) {
+                       return EXIT_FAILURE;
+               }
+       } else if (eqstr(cmd, "verify")) {
+               if (do_verify(argc - 2, argv + 2) < 0) {
+                       return EXIT_FAILURE;
+               }
+       } else if (eqstr(cmd, "skey")) {
+               if (do_skey(argc - 2, argv + 2) < 0) {
+                       return EXIT_FAILURE;
+               }
+       } else if (eqstr(cmd, "ta")) {
+               if (do_ta(argc - 2, argv + 2) < 0) {
+                       return EXIT_FAILURE;
+               }
+       } else if (eqstr(cmd, "chain")) {
+               if (do_chain(argc - 2, argv + 2) < 0) {
+                       return EXIT_FAILURE;
+               }
+       } else {
+               fprintf(stderr, "unknown command: '%s'\n", cmd);
+               usage();
+               return EXIT_FAILURE;
+       }
+       return 0;
+}
diff --git a/tools/brssl.h b/tools/brssl.h
new file mode 100644 (file)
index 0000000..99ce38d
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BRSSL_H__
+#define BRSSL_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "bearssl.h"
+
+/*
+ * malloc() wrapper:
+ * -- If len is 0, then NULL is returned.
+ * -- If len is non-zero, and allocation fails, then an error message is
+ *    printed and the process exits with an error code.
+ */
+void *xmalloc(size_t len);
+
+/*
+ * free() wrapper, meant to release blocks allocated with xmalloc().
+ */
+void xfree(void *buf);
+
+/*
+ * Duplicate a character string into a newly allocated block.
+ */
+char *xstrdup(const void *src);
+
+/*
+ * Allocate a new block with the provided length, filled with a copy
+ * of exactly that many bytes starting at address 'src'.
+ */
+void *xblobdup(const void *src, size_t len);
+
+/*
+ * Duplicate a public key, into newly allocated blocks. The returned
+ * key must be later on released with xfreepkey().
+ */
+br_x509_pkey *xpkeydup(const br_x509_pkey *pk);
+
+/*
+ * Release a public key that was allocated with xpkeydup(). If pk is NULL,
+ * this function does nothing.
+ */
+void xfreepkey(br_x509_pkey *pk);
+
+/*
+ * Macros for growable arrays.
+ */
+
+/*
+ * Make a structure type for a vector of 'type'.
+ */
+#define VECTOR(type)   struct { \
+               type *buf; \
+               size_t ptr, len; \
+       }
+
+/*
+ * Constant initialiser for a vector.
+ */
+#define VEC_INIT   { 0, 0, 0 }
+
+/*
+ * Clear a vector.
+ */
+#define VEC_CLEAR(vec)   do { \
+               xfree((vec).buf); \
+               (vec).buf = NULL; \
+               (vec).ptr = 0; \
+               (vec).len = 0; \
+       } while (0)
+
+/*
+ * Clear a vector, first calling the provided function on each vector
+ * element.
+ */
+#define VEC_CLEAREXT(vec, fun)   do { \
+               size_t vec_tmp; \
+               for (vec_tmp = 0; vec_tmp < (vec).ptr; vec_tmp ++) { \
+                       (fun)(&(vec).buf[vec_tmp]); \
+               } \
+               VEC_CLEAR(vec); \
+       } while (0)
+
+/*
+ * Add a value at the end of a vector.
+ */
+#define VEC_ADD(vec, x)   do { \
+               (vec).buf = vector_expand((vec).buf, sizeof *((vec).buf), \
+                       &(vec).ptr, &(vec).len, 1); \
+               (vec).buf[(vec).ptr ++] = (x); \
+       } while (0)
+
+/*
+ * Add several values at the end of a vector.
+ */
+#define VEC_ADDMANY(vec, xp, num)   do { \
+               size_t vec_num = (num); \
+               (vec).buf = vector_expand((vec).buf, sizeof *((vec).buf), \
+                       &(vec).ptr, &(vec).len, vec_num); \
+               memcpy((vec).buf + (vec).ptr, \
+                       (xp), vec_num * sizeof *((vec).buf)); \
+               (vec).ptr += vec_num; \
+       } while (0)
+
+/*
+ * Access a vector element by index. This is a lvalue, and can be modified.
+ */
+#define VEC_ELT(vec, idx)   ((vec).buf[idx])
+
+/*
+ * Get current vector length.
+ */
+#define VEC_LEN(vec)   ((vec).ptr)
+
+/*
+ * Copy all vector elements into a newly allocated block.
+ */
+#define VEC_TOARRAY(vec)    xblobdup((vec).buf, sizeof *((vec).buf) * (vec).ptr)
+
+/*
+ * Internal function used to handle memory allocations for vectors.
+ */
+void *vector_expand(void *buf,
+       size_t esize, size_t *ptr, size_t *len, size_t extra);
+
+/*
+ * Type for a vector of bytes.
+ */
+typedef VECTOR(unsigned char) bvector;
+
+/*
+ * Compare two strings for equality; returned value is 1 if the strings
+ * are to be considered equal, 0 otherwise. Comparison is case-insensitive
+ * (ASCII letters only) and skips some characters (all whitespace, defined
+ * as ASCII codes 0 to 32 inclusive, and also '-', '_', '.', '/', '+' and
+ * ':').
+ */
+int eqstr(const char *s1, const char *s2);
+
+/*
+ * Structure for a known protocol version.
+ */
+typedef struct {
+       const char *name;
+       unsigned version;
+       const char *comment;
+} protocol_version;
+
+/*
+ * Known protocol versions. Last element has a NULL name.
+ */
+extern const protocol_version protocol_versions[];
+
+/*
+ * Parse a version name. If the name is not recognized, then an error
+ * message is printed, and 0 is returned.
+ */
+unsigned parse_version(const char *name, size_t len);
+
+/*
+ * Type for a known hash function.
+ */
+typedef struct {
+       const char *name;
+       const br_hash_class *hclass;
+       const char *comment;
+} hash_function;
+
+/*
+ * Known hash functions. Last element has a NULL name.
+ */
+extern const hash_function hash_functions[];
+
+/*
+ * Parse hash function names. This function expects a comma-separated
+ * list of names, and returns a bit mask corresponding to the matched
+ * names. If one of the name does not match, or the list is empty, then
+ * an error message is printed, and 0 is returned.
+ */
+unsigned parse_hash_functions(const char *arg);
+
+/*
+ * Type for a known cipher suite.
+ */
+typedef struct {
+       const char *name;
+       uint16_t suite;
+       unsigned req;
+       const char *comment;
+} cipher_suite;
+
+/*
+ * Known cipher suites. Last element has a NULL name.
+ */
+extern const cipher_suite cipher_suites[];
+
+/*
+ * Flags for cipher suite requirements.
+ */
+#define REQ_TLS12          0x0001   /* suite needs TLS 1.2 */
+#define REQ_SHA1           0x0002   /* suite needs SHA-1 */
+#define REQ_SHA256         0x0004   /* suite needs SHA-256 */
+#define REQ_SHA384         0x0008   /* suite needs SHA-384 */
+#define REQ_AESCBC         0x0010   /* suite needs AES/CBC encryption */
+#define REQ_AESGCM         0x0020   /* suite needs AES/GCM encryption */
+#define REQ_3DESCBC        0x0040   /* suite needs 3DES/CBC encryption */
+#define REQ_RSAKEYX        0x0080   /* suite uses RSA key exchange */
+#define REQ_ECDHE_RSA      0x0100   /* suite uses ECDHE_RSA key exchange */
+#define REQ_ECDHE_ECDSA    0x0200   /* suite uses ECDHE_ECDSA key exchange */
+#define REQ_ECDH           0x0400   /* suite uses static ECDH key exchange */
+
+/*
+ * Parse a list of cipher suite names. The names are comma-separated. If
+ * one of the name is not recognised, or the list is empty, then an
+ * appropriate error message is printed, and NULL is returned.
+ * The returned array is allocated with xmalloc() and must be released
+ * by the caller. That array is terminated with a dummy entry whose 'name'
+ * field is NULL. The number of entries (not counting the dummy entry)
+ * is also written into '*num'.
+ */
+cipher_suite *parse_suites(const char *arg, size_t *num);
+
+/*
+ * Get the name of a cipher suite. Returned value is NULL if the suite is
+ * not recognized.
+ */
+const char *get_suite_name(unsigned suite);
+
+/*
+ * Get the name of a cipher suite. The name is written in the provided
+ * buffer; if the suite is not recognised, then the name is
+ * "unknown (0x****)" where "****" is the hexadecimal value of the suite.
+ * If the name does not fit in the provided buffer, then dst[0] is set
+ * to 0 (unless len is 0, in which case nothing is written), and -1 is
+ * returned. Otherwise, the name is written in dst[] (with a terminating
+ * 0), and this function returns 0.
+ */
+int get_suite_name_ext(unsigned suite, char *dst, size_t len);
+
+/*
+ * Print out all known names (for protocol versions, cipher suites...).
+ */
+void list_names(void);
+
+/*
+ * Get the symbolic name for an elliptic curve (by ID).
+ */
+const char *ec_curve_name(int curve);
+
+/*
+ * Read a file completely. The returned block is allocated with xmalloc()
+ * and must be released by the caller.
+ * If the file cannot be found or read completely, or is empty, then an
+ * appropriate error message is written, and NULL is returned.
+ */
+unsigned char *read_file(const char *fname, size_t *len);
+
+/*
+ * Write a file completely. This returns 0 on success, -1 on error. On
+ * error, an appropriate error message is printed.
+ */
+int write_file(const char *fname, const void *data, size_t len);
+
+/*
+ * This function returns non-zero if the provided buffer "looks like"
+ * a DER-encoded ASN.1 object (criteria: it has the tag for a SEQUENCE
+ * with a definite length that matches the total object length).
+ */
+int looks_like_DER(const unsigned char *buf, size_t len);
+
+/*
+ * Type for a named blob (the 'name' is a normalised PEM header name).
+ */
+typedef struct {
+       char *name;
+       unsigned char *data;
+       size_t data_len;
+} pem_object;
+
+/*
+ * Release the contents of a named blob (buffer and name).
+ */
+void free_pem_object_contents(pem_object *po);
+
+/*
+ * Decode a buffer as a PEM file, and return all objects. On error, NULL
+ * is returned and an error message is printed. Absence of any object
+ * is an error.
+ *
+ * The returned array is terminated by a dummy object whose 'name' is
+ * NULL. The number of objects (not counting the dummy terminator) is
+ * written in '*num'.
+ */
+pem_object *decode_pem(const void *src, size_t len, size_t *num);
+
+/*
+ * Get the certificate(s) from a file. This accepts both a single
+ * DER-encoded certificate, and a text file that contains
+ * PEM-encoded certificates (and possibly other objects, which are
+ * then ignored).
+ *
+ * On decoding error, or if the file turns out to contain no certificate
+ * at all, then an error message is printed and NULL is returned.
+ *
+ * The returned array, and all referenced buffers, are allocated with
+ * xmalloc() and must be released by the caller. The returned array
+ * ends with a dummy entry whose 'data' field is NULL.
+ * The number of decoded certificates (not counting the dummy entry)
+ * is written into '*num'.
+ */
+br_x509_certificate *read_certificates(const char *fname, size_t *num);
+
+/*
+ * Interpret a certificate as a trust anchor. The trust anchor is
+ * newly allocated with xmalloc() and the caller must release it.
+ * On decoding error, an error message is printed, and this function
+ * returns NULL.
+ */
+br_x509_trust_anchor *certificate_to_trust_anchor(br_x509_certificate *xc);
+
+/*
+ * Type for a vector of trust anchors.
+ */
+typedef VECTOR(br_x509_trust_anchor) anchor_list;
+
+/*
+ * Release contents for a trust anchor (assuming they were dynamically
+ * allocated with xmalloc()). The structure itself is NOT released.
+ */
+void free_ta_contents(br_x509_trust_anchor *ta);
+
+/*
+ * Decode certificates from a file and interpret them as trust anchors.
+ * The trust anchors are added to the provided list. The number of found
+ * anchors is returned; on error, 0 is returned (finding no anchor at
+ * all is considered an error). An appropriate error message is displayed.
+ */
+size_t read_trust_anchors(anchor_list *dst, const char *fname);
+
+/*
+ * Special "no anchor" X.509 validator that wraps around another X.509
+ * validator and turns "not trusted" error codes into success. This is
+ * by definition insecure, but convenient for debug purposes.
+ */
+typedef struct {
+       const br_x509_class *vtable;
+       const br_x509_class **inner;
+} x509_noanchor_context;
+extern const br_x509_class x509_noanchor_vtable;
+
+/*
+ * Initialise a "no anchor" X.509 validator.
+ */
+void x509_noanchor_init(x509_noanchor_context *xwc,
+       const br_x509_class **inner);
+
+/*
+ * Aggregate type for a private key.
+ */
+typedef struct {
+       int key_type;  /* BR_KEYTYPE_RSA or BR_KEYTYPE_EC */
+       union {
+               br_rsa_private_key rsa;
+               br_ec_private_key ec;
+       } key;
+} private_key;
+
+/*
+ * Decode a private key from a file. On error, this prints an error
+ * message and returns NULL.
+ */
+private_key *read_private_key(const char *fname);
+
+/*
+ * Free a private key.
+ */
+void free_private_key(private_key *sk);
+
+/*
+ * Find the symbolic name and the description for an error. If 'err' is
+ * recognised then the error symbolic name is returned; if 'comment' is
+ * not NULL then '*comment' is then set to a descriptive human-readable
+ * message. If the error code 'err' is not recognised, then '*comment' is
+ * untouched and this function returns NULL.
+ */
+const char *find_error_name(int err, const char **comment);
+
+/*
+ * Run a SSL engine, with a socket connected to the peer, and using
+ * stdin/stdout to exchange application data.
+ *
+ * Returned value:
+ *    0        SSL connection closed successfully
+ *    x > 0    SSL error "x"
+ *   -1        early socket close
+ *   -2        stdout was closed, or something failed badly
+ */
+int run_ssl_engine(br_ssl_engine_context *eng, int fd, unsigned flags);
+
+#define RUN_ENGINE_VERBOSE     0x0001  /* enable verbose messages */
+#define RUN_ENGINE_TRACE       0x0002  /* hex dump of records */
+
+/*
+ * Do the "client" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_client(int argc, char *argv[]);
+
+/*
+ * Do the "server" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_server(int argc, char *argv[]);
+
+/*
+ * Do the "verify" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_verify(int argc, char *argv[]);
+
+/*
+ * Do the "skey" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_skey(int argc, char *argv[]);
+
+/*
+ * Do the "ta" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_ta(int argc, char *argv[]);
+
+/*
+ * Do the "chain" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_chain(int argc, char *argv[]);
+
+#endif
diff --git a/tools/certs.c b/tools/certs.c
new file mode 100644 (file)
index 0000000..fdee5c6
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+
+static void
+dn_append(void *ctx, const void *buf, size_t len)
+{
+       VEC_ADDMANY(*(bvector *)ctx, buf, len);
+}
+
+static int
+certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta,
+       br_x509_certificate *xc)
+{
+       br_x509_decoder_context dc;
+       bvector vdn = VEC_INIT;
+       br_x509_pkey *pk;
+
+       br_x509_decoder_init(&dc, dn_append, &vdn);
+       br_x509_decoder_push(&dc, xc->data, xc->data_len);
+       pk = br_x509_decoder_get_pkey(&dc);
+       if (pk == NULL) {
+               fprintf(stderr, "ERROR: CA decoding failed with error %d\n",
+                       br_x509_decoder_last_error(&dc));
+               VEC_CLEAR(vdn);
+               return -1;
+       }
+       ta->dn = VEC_TOARRAY(vdn);
+       ta->dn_len = VEC_LEN(vdn);
+       VEC_CLEAR(vdn);
+       ta->flags = 0;
+       if (br_x509_decoder_isCA(&dc)) {
+               ta->flags |= BR_X509_TA_CA;
+       }
+       switch (pk->key_type) {
+       case BR_KEYTYPE_RSA:
+               ta->pkey.key_type = BR_KEYTYPE_RSA;
+               ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
+               ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
+               ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
+               ta->pkey.key.rsa.elen = pk->key.rsa.elen;
+               break;
+       case BR_KEYTYPE_EC:
+               ta->pkey.key_type = BR_KEYTYPE_EC;
+               ta->pkey.key.ec.curve = pk->key.ec.curve;
+               ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
+               ta->pkey.key.ec.qlen = pk->key.ec.qlen;
+               break;
+       default:
+               fprintf(stderr, "ERROR: unsupported public key type in CA\n");
+               xfree(ta->dn);
+               return -1;
+       }
+       return 0;
+}
+
+/* see brssl.h */
+br_x509_trust_anchor *
+certificate_to_trust_anchor(br_x509_certificate *xc)
+{
+       br_x509_trust_anchor ta;
+
+       if (certificate_to_trust_anchor_inner(&ta, xc) < 0) {
+               return NULL;
+       } else {
+               return xblobdup(&ta, sizeof ta);
+       }
+}
+
+/* see brssl.h */
+void
+free_ta_contents(br_x509_trust_anchor *ta)
+{
+       xfree(ta->dn);
+       switch (ta->pkey.key_type) {
+       case BR_KEYTYPE_RSA:
+               xfree(ta->pkey.key.rsa.n);
+               xfree(ta->pkey.key.rsa.e);
+               break;
+       case BR_KEYTYPE_EC:
+               xfree(ta->pkey.key.ec.q);
+               break;
+       }
+}
+
+/* see brssl.h */
+size_t
+read_trust_anchors(anchor_list *dst, const char *fname)
+{
+       br_x509_certificate *xcs;
+       anchor_list tas = VEC_INIT;
+       size_t u, num;
+
+       xcs = read_certificates(fname, &num);
+       if (xcs == NULL) {
+               return 0;
+       }
+       for (u = 0; u < num; u ++) {
+               br_x509_trust_anchor ta;
+
+               if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) {
+                       VEC_CLEAREXT(tas, free_ta_contents);
+                       return 0;
+               }
+               VEC_ADD(tas, ta);
+       }
+       VEC_ADDMANY(*dst, &VEC_ELT(tas, 0), num);
+       VEC_CLEAR(tas);
+       return num;
+}
+
+static void
+xwc_start_chain(const br_x509_class **ctx,
+       unsigned expected_key_type, const char *server_name)
+{
+       x509_noanchor_context *xwc;
+
+       xwc = (x509_noanchor_context *)ctx;
+       (*xwc->inner)->start_chain(xwc->inner,
+               expected_key_type, server_name);
+}
+
+static void
+xwc_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+       x509_noanchor_context *xwc;
+
+       xwc = (x509_noanchor_context *)ctx;
+       (*xwc->inner)->start_cert(xwc->inner, length);
+}
+
+static void
+xwc_append(const br_x509_class **ctx, const unsigned char *buf, size_t len)
+{
+       x509_noanchor_context *xwc;
+
+       xwc = (x509_noanchor_context *)ctx;
+       (*xwc->inner)->append(xwc->inner, buf, len);
+}
+
+static void
+xwc_end_cert(const br_x509_class **ctx)
+{
+       x509_noanchor_context *xwc;
+
+       xwc = (x509_noanchor_context *)ctx;
+       (*xwc->inner)->end_cert(xwc->inner);
+}
+
+static unsigned
+xwc_end_chain(const br_x509_class **ctx)
+{
+       x509_noanchor_context *xwc;
+       unsigned r;
+
+       xwc = (x509_noanchor_context *)ctx;
+       r = (*xwc->inner)->end_chain(xwc->inner);
+       if (r == BR_ERR_X509_NOT_TRUSTED) {
+               r = 0;
+       }
+       return r;
+}
+
+static const br_x509_pkey *
+xwc_get_pkey(const br_x509_class *const *ctx)
+{
+       x509_noanchor_context *xwc;
+
+       xwc = (x509_noanchor_context *)ctx;
+       return (*xwc->inner)->get_pkey(xwc->inner);
+}
+
+/* see brssl.h */
+const br_x509_class x509_noanchor_vtable = {
+       sizeof(x509_noanchor_context),
+       xwc_start_chain,
+       xwc_start_cert,
+       xwc_append,
+       xwc_end_cert,
+       xwc_end_chain,
+       xwc_get_pkey
+};
+
+/* see brssl.h */
+void
+x509_noanchor_init(x509_noanchor_context *xwc, const br_x509_class **inner)
+{
+       xwc->vtable = &x509_noanchor_vtable;
+       xwc->inner = inner;
+}
diff --git a/tools/chain.c b/tools/chain.c
new file mode 100644 (file)
index 0000000..671f5e8
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static void
+print_blob(const char *name, const unsigned char *buf, size_t len)
+{
+       size_t u;
+
+       printf("\nstatic const unsigned char %s[] = {", name);
+       for (u = 0; u < len; u ++) {
+               if (u != 0) {
+                       printf(",");
+               }
+               if (u % 12 == 0) {
+                       printf("\n\t");
+               } else {
+                       printf(" ");
+               }
+               printf("0x%02X", buf[u]);
+       }
+       printf("\n};\n");
+}
+
+static void
+usage_chain(void)
+{
+       fprintf(stderr,
+"usage: brssl chain [ options ] file...\n");
+       fprintf(stderr,
+"options:\n");
+       fprintf(stderr,
+"   -q            suppress verbose messages\n");
+}
+
+/* see brssl.h */
+int
+do_chain(int argc, char *argv[])
+{
+       int retcode;
+       int verbose;
+       int i, num_files;
+       long k, ctr;
+
+       retcode = 0;
+       verbose = 1;
+       num_files = 0;
+       for (i = 0; i < argc; i ++) {
+               const char *arg;
+
+               arg = argv[i];
+               if (arg[0] != '-') {
+                       num_files ++;
+                       continue;
+               }
+               argv[i] = NULL;
+               if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+                       verbose = 1;
+               } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+                       verbose = 0;
+               } else {
+                       fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+                       usage_chain();
+                       goto chain_exit_error;
+               }
+       }
+       if (num_files == 0) {
+               fprintf(stderr, "ERROR: no certificate file provided\n");
+               usage_chain();
+               goto chain_exit_error;
+       }
+
+       ctr = 0;
+       for (i = 0; i < argc; i ++) {
+               const char *fname;
+               br_x509_certificate *xcs;
+               size_t u, num;
+
+               fname = argv[i];
+               if (fname == NULL) {
+                       continue;
+               }
+               if (verbose) {
+                       fprintf(stderr, "Reading file '%s': ", fname);
+                       fflush(stderr);
+               }
+               xcs = read_certificates(fname, &num);
+               if (xcs == NULL) {
+                       goto chain_exit_error;
+               }
+               if (verbose) {
+                       fprintf(stderr, "%lu certificate%s\n",
+                               (unsigned long)num, num > 1 ? "s" : "");
+               }
+               for (u = 0; u < num; u ++) {
+                       char tmp[50];
+
+                       sprintf(tmp, "CERT%ld", ctr ++);
+                       print_blob(tmp, xcs[u].data, xcs[u].data_len);
+                       xfree(xcs[u].data);
+               }
+               xfree(xcs);
+       }
+
+       printf("\nstatic const br_x509_certificate CHAIN[] = {");
+       for (k = 0; k < ctr; k ++) {
+               if (k != 0) {
+                       printf(",");
+               }
+               printf("\n\t{ (unsigned char *)CERT%ld, sizeof CERT%ld }",
+                       k, k);
+       }
+       printf("\n};\n");
+       printf("\n#define CHAIN_LEN   %ld\n", ctr);
+
+       /*
+        * Release allocated structures.
+        */
+chain_exit:
+       return retcode;
+
+chain_exit_error:
+       retcode = -1;
+       goto chain_exit;
+}
diff --git a/tools/client.c b/tools/client.c
new file mode 100644 (file)
index 0000000..3b462a8
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static int
+host_connect(const char *host, const char *port, int verbose)
+{
+       struct addrinfo hints, *si, *p;
+       int fd;
+       int err;
+
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       err = getaddrinfo(host, port, &hints, &si);
+       if (err != 0) {
+               fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+                       gai_strerror(err));
+               return -1;
+       }
+       fd = -1;
+       for (p = si; p != NULL; p = p->ai_next) {
+               struct sockaddr *sa;
+               void *addr;
+               char tmp[INET6_ADDRSTRLEN + 50];
+
+               sa = (struct sockaddr *)p->ai_addr;
+               if (sa->sa_family == AF_INET) {
+                       addr = &((struct sockaddr_in *)sa)->sin_addr;
+               } else if (sa->sa_family == AF_INET6) {
+                       addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
+               } else {
+                       addr = NULL;
+               }
+               if (addr != NULL) {
+                       inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
+               } else {
+                       sprintf(tmp, "<unknown family: %d>",
+                               (int)sa->sa_family);
+               }
+               if (verbose) {
+                       fprintf(stderr, "connecting to: %s\n", tmp);
+               }
+               fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+               if (fd < 0) {
+                       if (verbose) {
+                               perror("socket()");
+                       }
+                       continue;
+               }
+               if (connect(fd, p->ai_addr, p->ai_addrlen) < 0) {
+                       if (verbose) {
+                               perror("connect()");
+                       }
+                       close(fd);
+                       continue;
+               }
+               break;
+       }
+       if (p == NULL) {
+               freeaddrinfo(si);
+               fprintf(stderr, "ERROR: failed to connect\n");
+               return -1;
+       }
+       freeaddrinfo(si);
+       if (verbose) {
+               fprintf(stderr, "connected.\n");
+       }
+
+       /*
+        * We make the socket non-blocking, since we are going to use
+        * poll() to organise I/O.
+        */
+       fcntl(fd, F_SETFL, O_NONBLOCK);
+       return fd;
+}
+
+static void
+usage_client(void)
+{
+       fprintf(stderr,
+"usage: brssl client server[:port] [ options ]\n");
+       fprintf(stderr,
+"options:\n");
+       fprintf(stderr,
+"   -q            suppress verbose messages\n");
+       fprintf(stderr,
+"   -trace        activate extra debug messages (dump of all packets)\n");
+       fprintf(stderr,
+"   -sni name     use this specific name for SNI\n");
+       fprintf(stderr,
+"   -nosni        do not send any SNI\n");
+       fprintf(stderr,
+"   -mono         use monodirectional buffering\n");
+       fprintf(stderr,
+"   -buf length   set the I/O buffer length (in bytes)\n");
+       fprintf(stderr,
+"   -CA file      add certificates in 'file' to trust anchors\n");
+       fprintf(stderr,
+"   -list         list supported names (protocols, algorithms...)\n");
+       fprintf(stderr,
+"   -vmin name    set minimum supported version (default: TLS-1.0)\n");
+       fprintf(stderr,
+"   -vmax name    set maximum supported version (default: TLS-1.2)\n");
+       fprintf(stderr,
+"   -cs names     set list of supported cipher suites (comma-separated)\n");
+       fprintf(stderr,
+"   -hf names     add support for some hash functions (comma-separated)\n");
+}
+
+/* see brssl.h */
+int
+do_client(int argc, char *argv[])
+{
+       int retcode;
+       int verbose;
+       int trace;
+       int i, bidi;
+       const char *server_name;
+       char *host;
+       char *port;
+       const char *sni;
+       anchor_list anchors = VEC_INIT;
+       unsigned vmin, vmax;
+       cipher_suite *suites;
+       size_t num_suites;
+       uint16_t *suite_ids;
+       unsigned hfuns;
+       size_t u;
+       br_ssl_client_context cc;
+       br_x509_minimal_context xc;
+       x509_noanchor_context xwc;
+       const br_hash_class *dnhash;
+       unsigned char *iobuf;
+       size_t iobuf_len;
+       int fd;
+
+       retcode = 0;
+       verbose = 1;
+       trace = 0;
+       server_name = NULL;
+       host = NULL;
+       port = NULL;
+       sni = NULL;
+       bidi = 1;
+       vmin = 0;
+       vmax = 0;
+       suites = NULL;
+       num_suites = 0;
+       hfuns = 0;
+       suite_ids = NULL;
+       iobuf = NULL;
+       iobuf_len = 0;
+       fd = -1;
+       for (i = 0; i < argc; i ++) {
+               const char *arg;
+
+               arg = argv[i];
+               if (arg[0] != '-') {
+                       if (server_name != NULL) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate server name\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       server_name = arg;
+                       continue;
+               }
+               if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+                       verbose = 1;
+               } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+                       verbose = 0;
+               } else if (eqstr(arg, "-trace")) {
+                       trace = 1;
+               } else if (eqstr(arg, "-sni")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-sni'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       if (sni != NULL) {
+                               fprintf(stderr, "ERROR: duplicate SNI\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       sni = argv[i];
+               } else if (eqstr(arg, "-nosni")) {
+                       if (sni != NULL) {
+                               fprintf(stderr, "ERROR: duplicate SNI\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       sni = "";
+               } else if (eqstr(arg, "-mono")) {
+                       bidi = 0;
+               } else if (eqstr(arg, "-buf")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-buf'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       arg = argv[i];
+                       if (iobuf_len != 0) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate I/O buffer length\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       iobuf_len = strtoul(arg, 0, 10);
+               } else if (eqstr(arg, "-CA")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-CA'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       arg = argv[i];
+                       if (read_trust_anchors(&anchors, arg) == 0) {
+                               usage_client();
+                               goto client_exit_error;
+                       }
+               } else if (eqstr(arg, "-list")) {
+                       list_names();
+                       goto client_exit;
+               } else if (eqstr(arg, "-vmin")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-vmin'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       arg = argv[i];
+                       if (vmin != 0) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate minimum version\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       vmin = parse_version(arg, strlen(arg));
+                       if (vmin == 0) {
+                               fprintf(stderr,
+                                       "ERROR: unrecognised version '%s'\n",
+                                       arg);
+                               usage_client();
+                               goto client_exit_error;
+                       }
+               } else if (eqstr(arg, "-vmax")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-vmax'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       arg = argv[i];
+                       if (vmax != 0) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate maximum version\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       vmax = parse_version(arg, strlen(arg));
+                       if (vmax == 0) {
+                               fprintf(stderr,
+                                       "ERROR: unrecognised version '%s'\n",
+                                       arg);
+                               usage_client();
+                               goto client_exit_error;
+                       }
+               } else if (eqstr(arg, "-cs")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-cs'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       arg = argv[i];
+                       if (suites != NULL) {
+                               fprintf(stderr, "ERROR: duplicate list"
+                                       " of cipher suites\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       suites = parse_suites(arg, &num_suites);
+                       if (suites == NULL) {
+                               usage_client();
+                               goto client_exit_error;
+                       }
+               } else if (eqstr(arg, "-hf")) {
+                       unsigned x;
+
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-hf'\n");
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       arg = argv[i];
+                       x = parse_hash_functions(arg);
+                       if (x == 0) {
+                               usage_client();
+                               goto client_exit_error;
+                       }
+                       hfuns |= x;
+               } else {
+                       fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+                       usage_client();
+                       goto client_exit_error;
+               }
+       }
+       if (server_name == NULL) {
+               fprintf(stderr, "ERROR: no server name/address provided\n");
+               usage_client();
+               goto client_exit_error;
+       }
+       for (u = strlen(server_name); u > 0; u --) {
+               int c = server_name[u - 1];
+               if (c == ':') {
+                       break;
+               }
+               if (c < '0' || c > '9') {
+                       u = 0;
+                       break;
+               }
+       }
+       if (u == 0) {
+               host = xstrdup(server_name);
+               port = "443";
+       } else {
+               port = xstrdup(server_name + u);
+               host = xmalloc(u);
+               memcpy(host, server_name, u - 1);
+               host[u - 1] = 0;
+       }
+       if (sni == NULL) {
+               sni = host;
+       }
+
+       if (vmin == 0) {
+               vmin = BR_TLS10;
+       }
+       if (vmax == 0) {
+               vmax = BR_TLS12;
+       }
+       if (vmax < vmin) {
+               fprintf(stderr, "ERROR: impossible minimum/maximum protocol"
+                       " version combination\n");
+               usage_client();
+               goto client_exit_error;
+       }
+       if (suites == NULL) {
+               num_suites = 0;
+
+               for (u = 0; cipher_suites[u].name; u ++) {
+                       if ((cipher_suites[u].req & REQ_TLS12) == 0
+                               || vmax >= BR_TLS12)
+                       {
+                               num_suites ++;
+                       }
+               }
+               suites = xmalloc(num_suites * sizeof *suites);
+               num_suites = 0;
+               for (u = 0; cipher_suites[u].name; u ++) {
+                       if ((cipher_suites[u].req & REQ_TLS12) == 0
+                               || vmax >= BR_TLS12)
+                       {
+                               suites[num_suites ++] = cipher_suites[u];
+                       }
+               }
+       }
+       if (hfuns == 0) {
+               hfuns = (unsigned)-1;
+       }
+       if (iobuf_len == 0) {
+               if (bidi) {
+                       iobuf_len = BR_SSL_BUFSIZE_BIDI;
+               } else {
+                       iobuf_len = BR_SSL_BUFSIZE_MONO;
+               }
+       }
+       iobuf = xmalloc(iobuf_len);
+
+       /*
+        * Compute implementation requirements and inject implementations.
+        */
+       suite_ids = xmalloc(num_suites * sizeof *suite_ids);
+       br_ssl_client_zero(&cc);
+       br_ssl_engine_set_versions(&cc.eng, vmin, vmax);
+       dnhash = NULL;
+       for (u = 0; hash_functions[u].name; u ++) {
+               const br_hash_class *hc;
+               int id;
+
+               hc = hash_functions[u].hclass;
+               id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+               if ((hfuns & ((unsigned)1 << id)) != 0) {
+                       dnhash = hc;
+               }
+       }
+       if (dnhash == NULL) {
+               fprintf(stderr, "ERROR: no supported hash function\n");
+               goto client_exit_error;
+       }
+       br_x509_minimal_init(&xc, dnhash,
+               &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+       if (vmin <= BR_TLS11) {
+               if (!(hfuns & (1 << br_md5_ID))) {
+                       fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need MD5\n");
+                       goto client_exit_error;
+               }
+               if (!(hfuns & (1 << br_sha1_ID))) {
+                       fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need SHA-1\n");
+                       goto client_exit_error;
+               }
+       }
+       for (u = 0; u < num_suites; u ++) {
+               unsigned req;
+
+               req = suites[u].req;
+               suite_ids[u] = suites[u].suite;
+               if ((req & REQ_TLS12) != 0 && vmax < BR_TLS12) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires TLS 1.2\n",
+                               suites[u].name);
+                       goto client_exit_error;
+               }
+               if ((req & REQ_SHA1) != 0 && !(hfuns & (1 << br_sha1_ID))) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires SHA-1\n",
+                               suites[u].name);
+                       goto client_exit_error;
+               }
+               if ((req & REQ_SHA256) != 0 && !(hfuns & (1 << br_sha256_ID))) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires SHA-256\n",
+                               suites[u].name);
+                       goto client_exit_error;
+               }
+               if ((req & REQ_SHA384) != 0 && !(hfuns & (1 << br_sha384_ID))) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires SHA-384\n",
+                               suites[u].name);
+                       goto client_exit_error;
+               }
+               /* TODO: algorithm implementation selection */
+               if ((req & REQ_AESCBC) != 0) {
+                       br_ssl_engine_set_aes_cbc(&cc.eng,
+                               &br_aes_ct_cbcenc_vtable,
+                               &br_aes_ct_cbcdec_vtable);
+                       br_ssl_engine_set_cbc(&cc.eng,
+                               &br_sslrec_in_cbc_vtable,
+                               &br_sslrec_out_cbc_vtable);
+               }
+               if ((req & REQ_AESGCM) != 0) {
+                       br_ssl_engine_set_aes_ctr(&cc.eng,
+                               &br_aes_ct_ctr_vtable);
+                       br_ssl_engine_set_ghash(&cc.eng,
+                               &br_ghash_ctmul);
+                       br_ssl_engine_set_gcm(&cc.eng,
+                               &br_sslrec_in_gcm_vtable,
+                               &br_sslrec_out_gcm_vtable);
+               }
+               if ((req & REQ_3DESCBC) != 0) {
+                       br_ssl_engine_set_des_cbc(&cc.eng,
+                               &br_des_ct_cbcenc_vtable,
+                               &br_des_ct_cbcdec_vtable);
+                       br_ssl_engine_set_cbc(&cc.eng,
+                               &br_sslrec_in_cbc_vtable,
+                               &br_sslrec_out_cbc_vtable);
+               }
+               if ((req & REQ_RSAKEYX) != 0) {
+                       br_ssl_client_set_rsapub(&cc, &br_rsa_i31_public);
+               }
+               if ((req & REQ_ECDHE_RSA) != 0) {
+                       br_ssl_engine_set_ec(&cc.eng, &br_ec_prime_i31);
+                       br_ssl_client_set_rsavrfy(&cc, &br_rsa_i31_pkcs1_vrfy);
+               }
+               if ((req & REQ_ECDHE_ECDSA) != 0) {
+                       br_ssl_engine_set_ec(&cc.eng, &br_ec_prime_i31);
+                       br_ssl_client_set_ecdsa(&cc, &br_ecdsa_i31_vrfy_asn1);
+               }
+               if ((req & REQ_ECDH) != 0) {
+                       br_ssl_engine_set_ec(&cc.eng, &br_ec_prime_i31);
+               }
+       }
+       br_ssl_engine_set_suites(&cc.eng, suite_ids, num_suites);
+
+       for (u = 0; hash_functions[u].name; u ++) {
+               const br_hash_class *hc;
+               int id;
+
+               hc = hash_functions[u].hclass;
+               id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+               if ((hfuns & ((unsigned)1 << id)) != 0) {
+                       br_ssl_engine_set_hash(&cc.eng, id, hc);
+                       br_x509_minimal_set_hash(&xc, id, hc);
+               }
+       }
+       if (vmin <= BR_TLS11) {
+               br_ssl_engine_set_prf10(&cc.eng, &br_tls10_prf);
+       }
+       if (vmax >= BR_TLS12) {
+               if ((hfuns & ((unsigned)1 << br_sha256_ID)) != 0) {
+                       br_ssl_engine_set_prf_sha256(&cc.eng,
+                               &br_tls12_sha256_prf);
+               }
+               if ((hfuns & ((unsigned)1 << br_sha384_ID)) != 0) {
+                       br_ssl_engine_set_prf_sha384(&cc.eng,
+                               &br_tls12_sha384_prf);
+               }
+       }
+       br_x509_minimal_set_rsa(&xc, &br_rsa_i31_pkcs1_vrfy);
+       br_x509_minimal_set_ecdsa(&xc,
+               &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
+
+       /*
+        * If there is no provided trust anchor, then certificate validation
+        * will always fail. In that situation, we use our custom wrapper
+        * that tolerates unknown anchors.
+        */
+       if (VEC_LEN(anchors) == 0) {
+               if (verbose) {
+                       fprintf(stderr,
+                               "WARNING: no configured trust anchor\n");
+               }
+               x509_noanchor_init(&xwc, &xc.vtable);
+               br_ssl_engine_set_x509(&cc.eng, &xwc.vtable);
+       } else {
+               br_ssl_engine_set_x509(&cc.eng, &xc.vtable);
+       }
+
+       br_ssl_engine_set_buffer(&cc.eng, iobuf, iobuf_len, bidi);
+       br_ssl_client_reset(&cc, sni, 0);
+
+       /*
+        * Connect to the peer.
+        */
+       fd = host_connect(host, port, verbose);
+       if (fd < 0) {
+               goto client_exit_error;
+       }
+
+       /*
+        * Run the engine until completion.
+        */
+       if (run_ssl_engine(&cc.eng, fd,
+               (verbose ? RUN_ENGINE_VERBOSE : 0)
+               | (trace ? RUN_ENGINE_TRACE : 0)) != 0)
+       {
+               goto client_exit_error;
+       } else {
+               goto client_exit;
+       }
+
+       /*
+        * Release allocated structures.
+        */
+client_exit:
+       xfree(host);
+       xfree(suites);
+       xfree(suite_ids);
+       VEC_CLEAREXT(anchors, &free_ta_contents);
+       xfree(iobuf);
+       if (fd >= 0) {
+               close(fd);
+       }
+       return retcode;
+
+client_exit_error:
+       retcode = -1;
+       goto client_exit;
+}
diff --git a/tools/errors.c b/tools/errors.c
new file mode 100644 (file)
index 0000000..e9c6dfa
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static struct {
+       int err;
+       const char *name;
+       const char *comment;
+} errors[] = {
+       {
+               BR_ERR_BAD_PARAM,
+               "BR_ERR_BAD_PARAM",
+               "Caller-provided parameter is incorrect."
+       }, {
+               BR_ERR_BAD_STATE,
+               "BR_ERR_BAD_STATE",
+               "Operation requested by the caller cannot be applied with"
+               " the current context state (e.g. reading data while"
+               " outgoing data is waiting to be sent)."
+       }, {
+               BR_ERR_UNSUPPORTED_VERSION,
+               "BR_ERR_UNSUPPORTED_VERSION",
+               "Incoming protocol or record version is unsupported."
+       }, {
+               BR_ERR_BAD_VERSION,
+               "BR_ERR_BAD_VERSION",
+               "Incoming record version does not match the expected version."
+       }, {
+               BR_ERR_BAD_LENGTH,
+               "BR_ERR_BAD_LENGTH",
+               "Incoming record length is invalid."
+       }, {
+               BR_ERR_TOO_LARGE,
+               "BR_ERR_TOO_LARGE",
+               "Incoming record is too large to be processed, or buffer"
+               " is too small for the handshake message to send."
+       }, {
+               BR_ERR_BAD_MAC,
+               "BR_ERR_BAD_MAC",
+               "Decryption found an invalid padding, or the record MAC is"
+               " not correct."
+       }, {
+               BR_ERR_NO_RANDOM,
+               "BR_ERR_NO_RANDOM",
+               "No initial entropy was provided, and none can be obtained"
+               " from the OS."
+       }, {
+               BR_ERR_UNKNOWN_TYPE,
+               "BR_ERR_UNKNOWN_TYPE",
+               "Incoming record type is unknown."
+       }, {
+               BR_ERR_UNEXPECTED,
+               "BR_ERR_UNEXPECTED",
+               "Incoming record or message has wrong type with regards to"
+               " the current engine state."
+       }, {
+               BR_ERR_BAD_CCS,
+               "BR_ERR_BAD_CCS",
+               "ChangeCipherSpec message from the peer has invalid contents."
+       }, {
+               BR_ERR_BAD_ALERT,
+               "BR_ERR_BAD_ALERT",
+               "Alert message from the peer has invalid contents"
+               " (odd length)."
+       }, {
+               BR_ERR_BAD_HANDSHAKE,
+               "BR_ERR_BAD_HANDSHAKE",
+               "Incoming handshake message decoding failed."
+       }, {
+               BR_ERR_OVERSIZED_ID,
+               "BR_ERR_OVERSIZED_ID",
+               "ServerHello contains a session ID which is larger than"
+               " 32 bytes."
+       }, {
+               BR_ERR_BAD_CIPHER_SUITE,
+               "BR_ERR_BAD_CIPHER_SUITE",
+               "Server wants to use a cipher suite that we did not claim"
+               " to support. This is also reported if we tried to advertise"
+               " a cipher suite that we do not support."
+       }, {
+               BR_ERR_BAD_COMPRESSION,
+               "BR_ERR_BAD_COMPRESSION",
+               "Server wants to use a compression that we did not claim"
+               " to support."
+       }, {
+               BR_ERR_BAD_FRAGLEN,
+               "BR_ERR_BAD_FRAGLEN",
+               "Server's max fragment length does not match client's."
+       }, {
+               BR_ERR_BAD_SECRENEG,
+               "BR_ERR_BAD_SECRENEG",
+               "Secure renegotiation failed."
+       }, {
+               BR_ERR_EXTRA_EXTENSION,
+               "BR_ERR_EXTRA_EXTENSION",
+               "Server sent an extension type that we did not announce,"
+               " or used the same extension type several times in a"
+               " single ServerHello."
+       }, {
+               BR_ERR_BAD_SNI,
+               "BR_ERR_BAD_SNI",
+               "Invalid Server Name Indication contents (when used by"
+               " the server, this extension shall be empty)."
+       }, {
+               BR_ERR_BAD_HELLO_DONE,
+               "BR_ERR_BAD_HELLO_DONE",
+               "Invalid ServerHelloDone from the server (length is not 0)."
+       }, {
+               BR_ERR_LIMIT_EXCEEDED,
+               "BR_ERR_LIMIT_EXCEEDED",
+               "Internal limit exceeded (e.g. server's public key is too"
+               " large)."
+       }, {
+               BR_ERR_BAD_FINISHED,
+               "BR_ERR_BAD_FINISHED",
+               "Finished message from peer does not match the expected"
+               " value."
+       }, {
+               BR_ERR_RESUME_MISMATCH,
+               "BR_ERR_RESUME_MISMATCH",
+               "Session resumption attempt with distinct version or cipher"
+               " suite."
+       }, {
+               BR_ERR_INVALID_ALGORITHM,
+               "BR_ERR_INVALID_ALGORITHM",
+               "Unsupported or invalid algorithm (ECDHE curve, signature"
+               " algorithm, hash function)."
+       }, {
+               BR_ERR_BAD_SIGNATURE,
+               "BR_ERR_BAD_SIGNATURE",
+               "Invalid signature on ServerKeyExchange message."
+       }, {
+               BR_ERR_IO,
+               "BR_ERR_IO",
+               "I/O error or premature close on transport stream."
+       }, {
+               BR_ERR_X509_INVALID_VALUE,
+               "BR_ERR_X509_INVALID_VALUE",
+               "Invalid value in an ASN.1 structure."
+       },
+       {
+               BR_ERR_X509_TRUNCATED,
+               "BR_ERR_X509_TRUNCATED",
+               "Truncated certificate or other ASN.1 object."
+       },
+       {
+               BR_ERR_X509_EMPTY_CHAIN,
+               "BR_ERR_X509_EMPTY_CHAIN",
+               "Empty certificate chain (no certificate at all)."
+       },
+       {
+               BR_ERR_X509_INNER_TRUNC,
+               "BR_ERR_X509_INNER_TRUNC",
+               "Decoding error: inner element extends beyond outer element"
+               " size."
+       },
+       {
+               BR_ERR_X509_BAD_TAG_CLASS,
+               "BR_ERR_X509_BAD_TAG_CLASS",
+               "Decoding error: unsupported tag class (application or"
+               " private)."
+       },
+       {
+               BR_ERR_X509_BAD_TAG_VALUE,
+               "BR_ERR_X509_BAD_TAG_VALUE",
+               "Decoding error: unsupported tag value."
+       },
+       {
+               BR_ERR_X509_INDEFINITE_LENGTH,
+               "BR_ERR_X509_INDEFINITE_LENGTH",
+               "Decoding error: indefinite length."
+       },
+       {
+               BR_ERR_X509_EXTRA_ELEMENT,
+               "BR_ERR_X509_EXTRA_ELEMENT",
+               "Decoding error: extraneous element."
+       },
+       {
+               BR_ERR_X509_UNEXPECTED,
+               "BR_ERR_X509_UNEXPECTED",
+               "Decoding error: unexpected element."
+       },
+       {
+               BR_ERR_X509_NOT_CONSTRUCTED,
+               "BR_ERR_X509_NOT_CONSTRUCTED",
+               "Decoding error: expected constructed element, but is"
+               " primitive."
+       },
+       {
+               BR_ERR_X509_NOT_PRIMITIVE,
+               "BR_ERR_X509_NOT_PRIMITIVE",
+               "Decoding error: expected primitive element, but is"
+               " constructed."
+       },
+       {
+               BR_ERR_X509_PARTIAL_BYTE,
+               "BR_ERR_X509_PARTIAL_BYTE",
+               "Decoding error: BIT STRING length is not multiple of 8."
+       },
+       {
+               BR_ERR_X509_BAD_BOOLEAN,
+               "BR_ERR_X509_BAD_BOOLEAN",
+               "Decoding error: BOOLEAN value has invalid length."
+       },
+       {
+               BR_ERR_X509_OVERFLOW,
+               "BR_ERR_X509_OVERFLOW",
+               "Decoding error: value is off-limits."
+       },
+       {
+               BR_ERR_X509_BAD_DN,
+               "BR_ERR_X509_BAD_DN",
+               "Invalid distinguished name."
+       },
+       {
+               BR_ERR_X509_BAD_TIME,
+               "BR_ERR_X509_BAD_TIME",
+               "Invalid date/time representation."
+       },
+       {
+               BR_ERR_X509_UNSUPPORTED,
+               "BR_ERR_X509_UNSUPPORTED",
+               "Certificate contains unsupported features that cannot be"
+               " ignored."
+       },
+       {
+               BR_ERR_X509_LIMIT_EXCEEDED,
+               "BR_ERR_X509_LIMIT_EXCEEDED",
+               "Key or signature size exceeds internal limits."
+       },
+       {
+               BR_ERR_X509_WRONG_KEY_TYPE,
+               "BR_ERR_X509_WRONG_KEY_TYPE",
+               "Key type does not match that which was expected."
+       },
+       {
+               BR_ERR_X509_BAD_SIGNATURE,
+               "BR_ERR_X509_BAD_SIGNATURE",
+               "Signature is invalid."
+       },
+       {
+               BR_ERR_X509_TIME_UNKNOWN,
+               "BR_ERR_X509_TIME_UNKNOWN",
+               "Validation time is unknown."
+       },
+       {
+               BR_ERR_X509_EXPIRED,
+               "BR_ERR_X509_EXPIRED",
+               "Certificate is expired or not yet valid."
+       },
+       {
+               BR_ERR_X509_DN_MISMATCH,
+               "BR_ERR_X509_DN_MISMATCH",
+               "Issuer/Subject DN mismatch in the chain."
+       },
+       {
+               BR_ERR_X509_BAD_SERVER_NAME,
+               "BR_ERR_X509_BAD_SERVER_NAME",
+               "Expected server name was not found in the chain."
+       },
+       {
+               BR_ERR_X509_CRITICAL_EXTENSION,
+               "BR_ERR_X509_CRITICAL_EXTENSION",
+               "Unknown critical extension in certificate."
+       },
+       {
+               BR_ERR_X509_NOT_CA,
+               "BR_ERR_X509_NOT_CA",
+               "Not a CA, or path length constraint violation."
+       },
+       {
+               BR_ERR_X509_FORBIDDEN_KEY_USAGE,
+               "BR_ERR_X509_FORBIDDEN_KEY_USAGE",
+               "Key Usage extension prohibits intended usage."
+       },
+       {
+               BR_ERR_X509_WEAK_PUBLIC_KEY,
+               "BR_ERR_X509_WEAK_PUBLIC_KEY",
+               "Public key found in certificate is too small."
+       },
+       {
+               BR_ERR_X509_NOT_TRUSTED,
+               "BR_ERR_X509_NOT_TRUSTED",
+               "Chain could not be linked to a trust anchor."
+       },
+       { 0, 0, 0 }
+};
+
+/* see brssl.h */
+const char *
+find_error_name(int err, const char **comment)
+{
+       size_t u;
+
+       for (u = 0; errors[u].name; u ++) {
+               if (errors[u].err == err) {
+                       if (comment != NULL) {
+                               *comment = errors[u].comment;
+                       }
+                       return errors[u].name;
+               }
+       }
+       return NULL;
+}
diff --git a/tools/files.c b/tools/files.c
new file mode 100644 (file)
index 0000000..07af6c4
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+
+/* see brssl.h */
+unsigned char *
+read_file(const char *fname, size_t *len)
+{
+       bvector vbuf = VEC_INIT;
+       FILE *f;
+
+       *len = 0;
+       f = fopen(fname, "rb");
+       if (f == NULL) {
+               fprintf(stderr,
+                       "ERROR: could not open file '%s' for reading\n", fname);
+               return NULL;
+       }
+       for (;;) {
+               unsigned char tmp[1024];
+               size_t rlen;
+
+               rlen = fread(tmp, 1, sizeof tmp, f);
+               if (rlen == 0) {
+                       unsigned char *buf;
+
+                       if (ferror(f)) {
+                               fprintf(stderr,
+                                       "ERROR: read error on file '%s'\n",
+                                       fname);
+                               fclose(f);
+                               return NULL;
+                       }
+                       buf = VEC_TOARRAY(vbuf);
+                       *len = VEC_LEN(vbuf);
+                       VEC_CLEAR(vbuf);
+                       fclose(f);
+                       return buf;
+               }
+               VEC_ADDMANY(vbuf, tmp, rlen);
+       }
+}
+
+/* see brssl.h */
+int
+write_file(const char *fname, const void *data, size_t len)
+{
+       FILE *f;
+       const unsigned char *buf;
+
+       f = fopen(fname, "wb");
+       if (f == NULL) {
+               fprintf(stderr,
+                       "ERROR: could not open file '%s' for reading\n", fname);
+               return -1;
+       }
+       buf = data;
+       while (len > 0) {
+               size_t wlen;
+
+               wlen = fwrite(buf, 1, len, f);
+               if (wlen == 0) {
+                       fprintf(stderr,
+                               "ERROR: could not write all bytes to '%s'\n",
+                               fname);
+                       fclose(f);
+                       return -1;
+               }
+               buf += wlen;
+               len -= wlen;
+       }
+       if (ferror(f)) {
+               fprintf(stderr, "ERROR: write error on file '%s'\n", fname);
+               fclose(f);
+               return -1;
+       }
+       fclose(f);
+       return 0;
+}
+
+/* see brssl.h */
+int
+looks_like_DER(const unsigned char *buf, size_t len)
+{
+       int fb;
+       size_t dlen;
+
+       if (len < 2) {
+               return 0;
+       }
+       if (*buf ++ != 0x30) {
+               return 0;
+       }
+       fb = *buf ++;
+       len -= 2;
+       if (fb < 0x80) {
+               return (size_t)fb == len;
+       } else if (fb == 0x80) {
+               return 0;
+       } else {
+               fb -= 0x80;
+               if (len < (size_t)fb + 2) {
+                       return 0;
+               }
+               len -= (size_t)fb;
+               dlen = 0;
+               while (fb -- > 0) {
+                       if (dlen > (len >> 8)) {
+                               return 0;
+                       }
+                       dlen = (dlen << 8) + (size_t)*buf ++;
+               }
+               return dlen == len;
+       }
+}
+
+static void
+vblob_append(void *cc, const void *data, size_t len)
+{
+       bvector *bv;
+
+       bv = cc;
+       VEC_ADDMANY(*bv, data, len);
+}
+
+/* see brssl.h */
+void
+free_pem_object_contents(pem_object *po)
+{
+       if (po != NULL) {
+               xfree(po->name);
+               xfree(po->data);
+       }
+}
+
+/* see brssl.h */
+pem_object *
+decode_pem(const void *src, size_t len, size_t *num)
+{
+       VECTOR(pem_object) pem_list = VEC_INIT;
+       br_pem_decoder_context pc;
+       pem_object po, *pos;
+       const unsigned char *buf;
+       bvector bv = VEC_INIT;
+       int inobj;
+
+       br_pem_decoder_init(&pc);
+       buf = src;
+       inobj = 0;
+       po.name = NULL;
+       po.data = NULL;
+       po.data_len = 0;
+       while (len > 0) {
+               size_t tlen;
+
+               tlen = br_pem_decoder_push(&pc, buf, len);
+               buf += tlen;
+               len -= tlen;
+               switch (br_pem_decoder_event(&pc)) {
+
+               case BR_PEM_BEGIN_OBJ:
+                       po.name = xstrdup(br_pem_decoder_name(&pc));
+                       br_pem_decoder_setdest(&pc, vblob_append, &bv);
+                       inobj = 1;
+                       break;
+
+               case BR_PEM_END_OBJ:
+                       if (inobj) {
+                               po.data = VEC_TOARRAY(bv);
+                               po.data_len = VEC_LEN(bv);
+                               VEC_ADD(pem_list, po);
+                               VEC_CLEAR(bv);
+                               po.name = NULL;
+                               po.data = NULL;
+                               po.data_len = 0;
+                               inobj = 0;
+                       }
+                       break;
+
+               case BR_PEM_ERROR:
+                       xfree(po.name);
+                       VEC_CLEAR(bv);
+                       fprintf(stderr,
+                               "ERROR: invalid PEM encoding\n");
+                       VEC_CLEAREXT(pem_list, &free_pem_object_contents);
+                       return NULL;
+               }
+       }
+       if (inobj) {
+               fprintf(stderr, "ERROR: unfinished PEM object\n");
+               xfree(po.name);
+               VEC_CLEAR(bv);
+               VEC_CLEAREXT(pem_list, &free_pem_object_contents);
+               return NULL;
+       }
+
+       *num = VEC_LEN(pem_list);
+       VEC_ADD(pem_list, po);
+       pos = VEC_TOARRAY(pem_list);
+       VEC_CLEAR(pem_list);
+       return pos;
+}
+
+/* see brssl.h */
+br_x509_certificate *
+read_certificates(const char *fname, size_t *num)
+{
+       VECTOR(br_x509_certificate) cert_list = VEC_INIT;
+       unsigned char *buf;
+       size_t len;
+       pem_object *pos;
+       size_t u, num_pos;
+       br_x509_certificate *xcs;
+       br_x509_certificate dummy;
+
+       *num = 0;
+
+       /*
+        * TODO: reading the whole file is crude; we could parse them
+        * in a streamed fashion. But it does not matter much in practice.
+        */
+       buf = read_file(fname, &len);
+       if (buf == NULL) {
+               return NULL;
+       }
+
+       /*
+        * Check for a DER-encoded certificate.
+        */
+       if (looks_like_DER(buf, len)) {
+               xcs = xmalloc(2 * sizeof *xcs);
+               xcs[0].data = buf;
+               xcs[0].data_len = len;
+               xcs[1].data = NULL;
+               xcs[1].data_len = 0;
+               *num = 1;
+               return xcs;
+       }
+
+       pos = decode_pem(buf, len, &num_pos);
+       xfree(buf);
+       for (u = 0; u < num_pos; u ++) {
+               if (eqstr(pos[u].name, "CERTIFICATE")
+                       || eqstr(pos[u].name, "X509 CERTIFICATE"))
+               {
+                       br_x509_certificate xc;
+
+                       xc.data = pos[u].data;
+                       xc.data_len = pos[u].data_len;
+                       pos[u].data = NULL;
+                       VEC_ADD(cert_list, xc);
+               }
+       }
+       for (u = 0; u < num_pos; u ++) {
+               free_pem_object_contents(&pos[u]);
+       }
+       xfree(pos);
+
+       if (VEC_LEN(cert_list) == 0) {
+               fprintf(stderr, "ERROR: no certificate in file '%s'\n", fname);
+               return NULL;
+       }
+       *num = VEC_LEN(cert_list);
+       dummy.data = NULL;
+       dummy.data_len = 0;
+       VEC_ADD(cert_list, dummy);
+       xcs = VEC_TOARRAY(cert_list);
+       VEC_CLEAR(cert_list);
+       return xcs;
+}
diff --git a/tools/keys.c b/tools/keys.c
new file mode 100644 (file)
index 0000000..278a3b2
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static private_key *
+decode_key(const unsigned char *buf, size_t len)
+{
+       br_skey_decoder_context dc;
+       int err;
+       private_key *sk;
+
+       br_skey_decoder_init(&dc);
+       br_skey_decoder_push(&dc, buf, len);
+       err = br_skey_decoder_last_error(&dc);
+       if (err != 0) {
+               const char *errname, *errmsg;
+
+               fprintf(stderr, "ERROR (decoding): err=%d\n", err);
+               errname = find_error_name(err, &errmsg);
+               if (errname != NULL) {
+                       fprintf(stderr, "  %s: %s\n", errname, errmsg);
+               } else {
+                       fprintf(stderr, "  (unknown)\n");
+               }
+               return NULL;
+       }
+       switch (br_skey_decoder_key_type(&dc)) {
+               const br_rsa_private_key *rk;
+               const br_ec_private_key *ek;
+
+       case BR_KEYTYPE_RSA:
+               rk = br_skey_decoder_get_rsa(&dc);
+               sk = xmalloc(sizeof *sk);
+               sk->key_type = BR_KEYTYPE_RSA;
+               sk->key.rsa.n_bitlen = rk->n_bitlen;
+               sk->key.rsa.p = xblobdup(rk->p, rk->plen);
+               sk->key.rsa.plen = rk->plen;
+               sk->key.rsa.q = xblobdup(rk->q, rk->qlen);
+               sk->key.rsa.qlen = rk->qlen;
+               sk->key.rsa.dp = xblobdup(rk->dp, rk->dplen);
+               sk->key.rsa.dplen = rk->dplen;
+               sk->key.rsa.dq = xblobdup(rk->dq, rk->dqlen);
+               sk->key.rsa.dqlen = rk->dqlen;
+               sk->key.rsa.iq = xblobdup(rk->iq, rk->iqlen);
+               sk->key.rsa.iqlen = rk->iqlen;
+               break;
+
+       case BR_KEYTYPE_EC:
+               ek = br_skey_decoder_get_ec(&dc);
+               sk = xmalloc(sizeof *sk);
+               sk->key_type = BR_KEYTYPE_EC;
+               sk->key.ec.curve = ek->curve;
+               sk->key.ec.x = xblobdup(ek->x, ek->xlen);
+               sk->key.ec.xlen = ek->xlen;
+               break;
+
+       default:
+               fprintf(stderr, "Unknown key type: %d\n",
+                       br_skey_decoder_key_type(&dc));
+               sk = NULL;
+               break;
+       }
+
+       return sk;
+}
+
+/* see brssl.h */
+private_key *
+read_private_key(const char *fname)
+{
+       unsigned char *buf;
+       size_t len;
+       private_key *sk;
+       pem_object *pos;
+       size_t num, u;
+
+       buf = NULL;
+       pos = NULL;
+       sk = NULL;
+       buf = read_file(fname, &len);
+       if (buf == NULL) {
+               goto deckey_exit;
+       }
+       if (looks_like_DER(buf, len)) {
+               sk = decode_key(buf, len);
+               goto deckey_exit;
+       } else {
+               pos = decode_pem(buf, len, &num);
+               if (pos == NULL) {
+                       goto deckey_exit;
+               }
+               for (u = 0; pos[u].name; u ++) {
+                       const char *name;
+
+                       name = pos[u].name;
+                       if (eqstr(name, "RSA PRIVATE KEY")
+                               || eqstr(name, "EC PRIVATE KEY")
+                               || eqstr(name, "PRIVATE KEY"))
+                       {
+                               sk = decode_key(pos[u].data, pos[u].data_len);
+                               goto deckey_exit;
+                       }
+               }
+               fprintf(stderr, "ERROR: no private key in file '%s'\n", fname);
+               goto deckey_exit;
+       }
+
+deckey_exit:
+       if (buf != NULL) {
+               xfree(buf);
+       }
+       if (pos != NULL) {
+               for (u = 0; pos[u].name; u ++) {
+                       free_pem_object_contents(&pos[u]);
+               }
+               xfree(pos);
+       }
+       return sk;
+}
+
+/* see brssl.h */
+void
+free_private_key(private_key *sk)
+{
+       if (sk == NULL) {
+               return;
+       }
+       switch (sk->key_type) {
+       case BR_KEYTYPE_RSA:
+               xfree(sk->key.rsa.p);
+               xfree(sk->key.rsa.q);
+               xfree(sk->key.rsa.dp);
+               xfree(sk->key.rsa.dq);
+               xfree(sk->key.rsa.iq);
+               break;
+       case BR_KEYTYPE_EC:
+               xfree(sk->key.ec.x);
+               break;
+       }
+       xfree(sk);
+}
diff --git a/tools/names.c b/tools/names.c
new file mode 100644 (file)
index 0000000..dcc5b17
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "brssl.h"
+#include "bearssl.h"
+
+/* see brssl.h */
+const protocol_version protocol_versions[] = {
+       { "tls10", BR_TLS10, "TLS 1.0" },
+       { "tls11", BR_TLS11, "TLS 1.1" },
+       { "tls12", BR_TLS12, "TLS 1.2" },
+       { NULL, 0, NULL }
+};
+
+/* see brssl.h */
+const hash_function hash_functions[] = {
+       { "md5",     &br_md5_vtable,     "MD5" },
+       { "sha1",    &br_sha1_vtable,    "SHA-1" },
+       { "sha224",  &br_sha224_vtable,  "SHA-224" },
+       { "sha256",  &br_sha256_vtable,  "SHA-256" },
+       { "sha384",  &br_sha384_vtable,  "SHA-384" },
+       { "sha512",  &br_sha512_vtable,  "SHA-512" },
+       { NULL, 0, NULL }
+};
+
+/* see brssl.h */
+const cipher_suite cipher_suites[] = {
+       {
+               "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+               REQ_ECDHE_ECDSA | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+               "ECDHE with ECDSA, AES-128/GCM encryption (TLS 1.2+)"
+       },
+       {
+               "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+               BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+               REQ_ECDHE_RSA | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+               "ECDHE with RSA, AES-128/GCM encryption (TLS 1.2+)"
+       },
+       {
+               "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+               REQ_ECDHE_ECDSA | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+               "ECDHE with ECDSA, AES-256/GCM encryption (TLS 1.2+)"
+       },
+       {
+               "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+               BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               REQ_ECDHE_RSA | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+               "ECDHE with RSA, AES-256/GCM encryption (TLS 1.2+)"
+       },
+       {
+               "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+               REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+               "ECDHE with ECDSA, AES-128/CBC + SHA-256 (TLS 1.2+)"
+       },
+       {
+               "ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+               REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+               "ECDHE with RSA, AES-128/CBC + SHA-256 (TLS 1.2+)"
+       },
+       {
+               "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+               REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+               "ECDHE with ECDSA, AES-256/CBC + SHA-384 (TLS 1.2+)"
+       },
+       {
+               "ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+               REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+               "ECDHE with RSA, AES-256/CBC + SHA-384 (TLS 1.2+)"
+       },
+       {
+               "ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+               BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+               REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA1,
+               "ECDHE with ECDSA, AES-128/CBC + SHA-1"
+       },
+       {
+               "ECDHE_RSA_WITH_AES_128_CBC_SHA",
+               BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+               REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA1,
+               "ECDHE with RSA, AES-128/CBC + SHA-1"
+       },
+       {
+               "ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+               BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+               REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA1,
+               "ECDHE with ECDSA, AES-256/CBC + SHA-1"
+       },
+       {
+               "ECDHE_RSA_WITH_AES_256_CBC_SHA",
+               BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+               REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA1,
+               "ECDHE with RSA, AES-256/CBC + SHA-1"
+       },
+       {
+               "ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+               REQ_ECDH | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+               "ECDH key exchange (EC cert), AES-128/GCM (TLS 1.2+)"
+       },
+       {
+               "ECDH_RSA_WITH_AES_128_GCM_SHA256",
+               BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+               REQ_ECDH | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+               "ECDH key exchange (RSA cert), AES-128/GCM (TLS 1.2+)"
+       },
+       {
+               "ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+               REQ_ECDH | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+               "ECDH key exchange (EC cert), AES-256/GCM (TLS 1.2+)"
+       },
+       {
+               "ECDH_RSA_WITH_AES_256_GCM_SHA384",
+               BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+               REQ_ECDH | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+               "ECDH key exchange (RSA cert), AES-256/GCM (TLS 1.2+)"
+       },
+       {
+               "ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+               "ECDH key exchange (EC cert), AES-128/CBC + HMAC/SHA-256 (TLS 1.2+)"
+       },
+       {
+               "ECDH_RSA_WITH_AES_128_CBC_SHA256",
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+               "ECDH key exchange (RSA cert), AES-128/CBC + HMAC/SHA-256 (TLS 1.2+)"
+       },
+       {
+               "ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+               "ECDH key exchange (EC cert), AES-256/CBC + HMAC/SHA-384 (TLS 1.2+)"
+       },
+       {
+               "ECDH_RSA_WITH_AES_256_CBC_SHA384",
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+               "ECDH key exchange (RSA cert), AES-256/CBC + HMAC/SHA-384 (TLS 1.2+)"
+       },
+       {
+               "ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+               BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+               "ECDH key exchange (EC cert), AES-128/CBC + HMAC/SHA-1"
+       },
+       {
+               "ECDH_RSA_WITH_AES_128_CBC_SHA",
+               BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+               "ECDH key exchange (RSA cert), AES-128/CBC + HMAC/SHA-1"
+       },
+       {
+               "ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+               BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+               "ECDH key exchange (EC cert), AES-256/CBC + HMAC/SHA-1"
+       },
+       {
+               "ECDH_RSA_WITH_AES_256_CBC_SHA",
+               BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+               REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+               "ECDH key exchange (RSA cert), AES-256/CBC + HMAC/SHA-1"
+       },
+       {
+               "RSA_WITH_AES_128_GCM_SHA256",
+               BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
+               REQ_RSAKEYX | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+               "RSA key exchange, AES-128/GCM encryption (TLS 1.2+)"
+       },
+       {
+               "RSA_WITH_AES_256_GCM_SHA384",
+               BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+               REQ_RSAKEYX | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+               "RSA key exchange, AES-256/GCM encryption (TLS 1.2+)"
+       },
+       {
+               "RSA_WITH_AES_128_CBC_SHA256",
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
+               REQ_RSAKEYX | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+               "RSA key exchange, AES-128/CBC + HMAC/SHA-256 (TLS 1.2+)"
+       },
+       {
+               "RSA_WITH_AES_256_CBC_SHA256",
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
+               REQ_RSAKEYX | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+               "RSA key exchange, AES-256/CBC + HMAC/SHA-256 (TLS 1.2+)"
+       },
+       {
+               "RSA_WITH_AES_128_CBC_SHA",
+               BR_TLS_RSA_WITH_AES_128_CBC_SHA,
+               REQ_RSAKEYX | REQ_AESCBC | REQ_SHA1,
+               "RSA key exchange, AES-128/CBC + HMAC/SHA-1"
+       },
+       {
+               "RSA_WITH_AES_256_CBC_SHA",
+               BR_TLS_RSA_WITH_AES_256_CBC_SHA,
+               REQ_RSAKEYX | REQ_AESCBC | REQ_SHA1,
+               "RSA key exchange, AES-256/CBC + HMAC/SHA-1"
+       },
+       {
+               "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+               BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               REQ_ECDHE_ECDSA | REQ_3DESCBC | REQ_SHA1,
+               "ECDHE with ECDSA, 3DES/CBC + SHA-1"
+       },
+       {
+               "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+               BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+               REQ_ECDHE_RSA | REQ_3DESCBC | REQ_SHA1,
+               "ECDHE with RSA, 3DES/CBC + SHA-1"
+       },
+       {
+               "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+               BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+               REQ_ECDH | REQ_3DESCBC | REQ_SHA1,
+               "ECDH key exchange (EC cert), 3DES/CBC + HMAC/SHA-1"
+       },
+       {
+               "ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+               BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+               REQ_ECDH | REQ_3DESCBC | REQ_SHA1,
+               "ECDH key exchange (RSA cert), 3DES/CBC + HMAC/SHA-1"
+       },
+       {
+               "RSA_WITH_3DES_EDE_CBC_SHA",
+               BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+               REQ_RSAKEYX | REQ_3DESCBC | REQ_SHA1,
+               "RSA key exchange, 3DES/CBC + HMAC/SHA-1"
+       },
+       { NULL, 0, 0, NULL }
+};
+
+/* see brssl.h */
+const char *
+get_suite_name(unsigned suite)
+{
+       size_t u;
+
+       for (u = 0; cipher_suites[u].name; u ++) {
+               if (cipher_suites[u].suite == suite) {
+                       return cipher_suites[u].name;
+               }
+       }
+       return NULL;
+}
+
+/* see brssl.h */
+int
+get_suite_name_ext(unsigned suite, char *dst, size_t len)
+{
+       const char *name;
+       char tmp[30];
+       size_t n;
+
+       name = get_suite_name(suite);
+       if (name == NULL) {
+               sprintf(tmp, "unknown (0x%04X)", suite);
+               name = tmp;
+       }
+       n = 1 + strlen(name);
+       if (n > len) {
+               if (len > 0) {
+                       dst[0] = 0;
+               }
+               return -1;
+       }
+       memcpy(dst, name, n);
+       return 0;
+}
+
+/* see brssl.h */
+void
+list_names(void)
+{
+       size_t u;
+
+       printf("Protocol versions:\n");
+       for (u = 0; protocol_versions[u].name; u ++) {
+               printf("   %-8s %s\n",
+                       protocol_versions[u].name,
+                       protocol_versions[u].comment);
+       }
+       printf("Hash functions:\n");
+       for (u = 0; hash_functions[u].name; u ++) {
+               printf("   %-8s %s\n",
+                       hash_functions[u].name,
+                       hash_functions[u].comment);
+       }
+       printf("Cipher suites:\n");
+       for (u = 0; cipher_suites[u].name; u ++) {
+               printf("   %s\n        %s\n",
+                       cipher_suites[u].name,
+                       cipher_suites[u].comment);
+       }
+}
+
+static int
+is_ign(int c)
+{
+       if (c == 0) {
+               return 0;
+       }
+       if (c <= 32 || c == '-' || c == '_' || c == '.'
+               || c == '/' || c == '+' || c == ':')
+       {
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Get next non-ignored character, normalised:
+ *    ASCII letters are converted to lowercase
+ *    control characters, space, '-', '_', '.', '/', '+' and ':' are ignored
+ * A terminating zero is returned as 0.
+ */
+static int
+next_char(const char **ps, const char *limit)
+{
+       for (;;) {
+               int c;
+
+               if (*ps == limit) {
+                       return 0;
+               }
+               c = *(*ps) ++;
+               if (c == 0) {
+                       return 0;
+               }
+               if (c >= 'A' && c <= 'Z') {
+                       c += 'a' - 'A';
+               }
+               if (!is_ign(c)) {
+                       return c;
+               }
+       }
+}
+
+/*
+ * Partial string equality comparison, with normalisation.
+ */
+static int
+eqstr_chunk(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
+{
+       const char *lim1, *lim2;
+
+       lim1 = s1 + s1_len;
+       lim2 = s2 + s2_len;
+       for (;;) {
+               int c1, c2;
+
+               c1 = next_char(&s1, lim1);
+               c2 = next_char(&s2, lim2);
+               if (c1 != c2) {
+                       return 0;
+               }
+               if (c1 == 0) {
+                       return 1;
+               }
+       }
+       return 1;
+}
+
+/* see brssl.h */
+int
+eqstr(const char *s1, const char *s2)
+{
+       return eqstr_chunk(s1, strlen(s1), s2, strlen(s2));
+}
+
+/*
+ * Comma-separated list enumeration. This returns a pointer to the first
+ * word in the string, skipping leading ignored characters. '*len' is
+ * set to the word length (not counting trailing ignored characters).
+ * '*str' is updated to point to immediately after the next comma, or to
+ * the terminating zero, whichever comes first.
+ *
+ * Empty words are skipped. If there is no next non-empty word, then this
+ * function returns NULL and sets *len to 0.
+ */
+static const char *
+next_word(const char **str, size_t *len)
+{
+       int c;
+       const char *begin;
+       size_t u;
+
+       /*
+        * Find next non-ignored character which is not a comma.
+        */
+       for (;;) {
+               c = **str;
+               if (c == 0) {
+                       *len = 0;
+                       return NULL;
+               }
+               if (!is_ign(c) && c != ',') {
+                       break;
+               }
+               (*str) ++;
+       }
+
+       /*
+        * Find next comma or terminator.
+        */
+       begin = *str;
+       for (;;) {
+               c = *(*str);
+               if (c == 0 || c == ',') {
+                       break;
+               }
+               (*str) ++;
+       }
+
+       /*
+        * Remove trailing ignored characters.
+        */
+       u = (size_t)(*str - begin);
+       while (u > 0 && is_ign(begin[u - 1])) {
+               u --;
+       }
+       if (c == ',') {
+               (*str) ++;
+       }
+       *len = u;
+       return begin;
+}
+
+/* see brssl.h */
+unsigned
+parse_version(const char *name, size_t len)
+{
+       size_t u;
+
+       for (u = 0;; u ++) {
+               const char *ref;
+
+               ref = protocol_versions[u].name;
+               if (ref == NULL) {
+                       fprintf(stderr, "ERROR: unrecognised protocol"
+                               " version name: '%s'\n", name);
+                       return 0;
+               }
+               if (eqstr_chunk(ref, strlen(ref), name, len)) {
+                       return protocol_versions[u].version;
+               }
+       }
+}
+
+/* see brssl.h */
+unsigned
+parse_hash_functions(const char *arg)
+{
+       unsigned r;
+
+       r = 0;
+       for (;;) {
+               const char *name;
+               size_t len;
+               size_t u;
+
+               name = next_word(&arg, &len);
+               if (name == NULL) {
+                       break;
+               }
+               for (u = 0;; u ++) {
+                       const char *ref;
+
+                       ref = hash_functions[u].name;
+                       if (ref == 0) {
+                               fprintf(stderr, "ERROR: unrecognised"
+                                       " hash function name: '");
+                               fwrite(name, 1, len, stderr);
+                               fprintf(stderr, "'\n");
+                               return 0;
+                       }
+                       if (eqstr_chunk(ref, strlen(ref), name, len)) {
+                               int id;
+
+                               id = (hash_functions[u].hclass->desc
+                                       >> BR_HASHDESC_ID_OFF)
+                                       & BR_HASHDESC_ID_MASK;
+                               r |= (unsigned)1 << id;
+                               break;
+                       }
+               }
+       }
+       if (r == 0) {
+               fprintf(stderr, "ERROR: no hash function name provided\n");
+       }
+       return r;
+}
+
+/* see brssl.h */
+cipher_suite *
+parse_suites(const char *arg, size_t *num)
+{
+       VECTOR(cipher_suite) suites = VEC_INIT;
+       cipher_suite *r;
+
+       for (;;) {
+               const char *name;
+               size_t u, len;
+
+               name = next_word(&arg, &len);
+               if (name == NULL) {
+                       break;
+               }
+               for (u = 0;; u ++) {
+                       const char *ref;
+
+                       ref = cipher_suites[u].name;
+                       if (ref == NULL) {
+                               fprintf(stderr, "ERROR: unrecognised"
+                                       " cipher suite '");
+                               fwrite(name, 1, len, stderr);
+                               fprintf(stderr, "'\n");
+                               return 0;
+                       }
+                       if (eqstr_chunk(ref, strlen(ref), name, len)) {
+                               VEC_ADD(suites, cipher_suites[u]);
+                               break;
+                       }
+               }
+       }
+       if (VEC_LEN(suites) == 0) {
+               fprintf(stderr, "ERROR: no cipher suite provided\n");
+       }
+       r = VEC_TOARRAY(suites);
+       *num = VEC_LEN(suites);
+       VEC_CLEAR(suites);
+       return r;
+}
+
+/* see brssl.h */
+const char *
+ec_curve_name(int curve)
+{
+       switch (curve) {
+       case BR_EC_sect163k1:        return "sect163k1";
+       case BR_EC_sect163r1:        return "sect163r1";
+       case BR_EC_sect163r2:        return "sect163r2";
+       case BR_EC_sect193r1:        return "sect193r1";
+       case BR_EC_sect193r2:        return "sect193r2";
+       case BR_EC_sect233k1:        return "sect233k1";
+       case BR_EC_sect233r1:        return "sect233r1";
+       case BR_EC_sect239k1:        return "sect239k1";
+       case BR_EC_sect283k1:        return "sect283k1";
+       case BR_EC_sect283r1:        return "sect283r1";
+       case BR_EC_sect409k1:        return "sect409k1";
+       case BR_EC_sect409r1:        return "sect409r1";
+       case BR_EC_sect571k1:        return "sect571k1";
+       case BR_EC_sect571r1:        return "sect571r1";
+       case BR_EC_secp160k1:        return "secp160k1";
+       case BR_EC_secp160r1:        return "secp160r1";
+       case BR_EC_secp160r2:        return "secp160r2";
+       case BR_EC_secp192k1:        return "secp192k1";
+       case BR_EC_secp192r1:        return "secp192r1";
+       case BR_EC_secp224k1:        return "secp224k1";
+       case BR_EC_secp224r1:        return "secp224r1";
+       case BR_EC_secp256k1:        return "secp256k1";
+       case BR_EC_secp256r1:        return "secp256r1";
+       case BR_EC_secp384r1:        return "secp384r1";
+       case BR_EC_secp521r1:        return "secp521r1";
+       case BR_EC_brainpoolP256r1:  return "brainpoolP256r1";
+       case BR_EC_brainpoolP384r1:  return "brainpoolP384r1";
+       case BR_EC_brainpoolP512r1:  return "brainpoolP512r1";
+       default:
+               return "unknown";
+       }
+}
diff --git a/tools/server.c b/tools/server.c
new file mode 100644 (file)
index 0000000..fc16692
--- /dev/null
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static int
+host_bind(const char *host, const char *port, int verbose)
+{
+       struct addrinfo hints, *si, *p;
+       int fd;
+       int err;
+
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       err = getaddrinfo(host, port, &hints, &si);
+       if (err != 0) {
+               fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+                       gai_strerror(err));
+               return -1;
+       }
+       fd = -1;
+       for (p = si; p != NULL; p = p->ai_next) {
+               struct sockaddr *sa;
+               struct sockaddr_in sa4;
+               struct sockaddr_in6 sa6;
+               size_t sa_len;
+               void *addr;
+               char tmp[INET6_ADDRSTRLEN + 50];
+               int opt;
+
+               sa = (struct sockaddr *)p->ai_addr;
+               if (sa->sa_family == AF_INET) {
+                       sa4 = *(struct sockaddr_in *)sa;
+                       sa = (struct sockaddr *)&sa4;
+                       sa_len = sizeof sa4;
+                       addr = &sa4.sin_addr;
+                       if (host == NULL) {
+                               sa4.sin_addr.s_addr = INADDR_ANY;
+                       }
+               } else if (sa->sa_family == AF_INET6) {
+                       sa6 = *(struct sockaddr_in6 *)sa;
+                       sa = (struct sockaddr *)&sa6;
+                       sa_len = sizeof sa6;
+                       addr = &sa6.sin6_addr;
+                       if (host == NULL) {
+                               sa6.sin6_addr = in6addr_any;
+                       }
+               } else {
+                       addr = NULL;
+                       sa_len = p->ai_addrlen;
+               }
+               if (addr != NULL) {
+                       inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
+               } else {
+                       sprintf(tmp, "<unknown family: %d>",
+                               (int)sa->sa_family);
+               }
+               if (verbose) {
+                       fprintf(stderr, "binding to: %s\n", tmp);
+               }
+               fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+               if (fd < 0) {
+                       if (verbose) {
+                               perror("socket()");
+                       }
+                       continue;
+               }
+               opt = 1;
+               setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
+               opt = 0;
+               setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt);
+               if (bind(fd, sa, sa_len) < 0) {
+                       if (verbose) {
+                               perror("bind()");
+                       }
+                       close(fd);
+                       continue;
+               }
+               break;
+       }
+       if (p == NULL) {
+               freeaddrinfo(si);
+               fprintf(stderr, "ERROR: failed to bind\n");
+               return -1;
+       }
+       freeaddrinfo(si);
+       if (listen(fd, 5) < 0) {
+               if (verbose) {
+                       perror("listen()");
+               }
+               close(fd);
+               return -1;
+       }
+       if (verbose) {
+               fprintf(stderr, "bound.\n");
+       }
+       return fd;
+}
+
+static int
+accept_client(int server_fd, int verbose)
+{
+       int fd;
+       struct sockaddr sa;
+       socklen_t sa_len;
+
+       sa_len = sizeof sa;
+       fd = accept(server_fd, &sa, &sa_len);
+       if (fd < 0) {
+               if (verbose) {
+                       perror("accept()");
+               }
+               return -1;
+       }
+       if (verbose) {
+               char tmp[INET6_ADDRSTRLEN + 50];
+               const char *name;
+
+               name = NULL;
+               switch (sa.sa_family) {
+               case AF_INET:
+                       name = inet_ntop(AF_INET,
+                               &((struct sockaddr_in *)&sa)->sin_addr,
+                               tmp, sizeof tmp);
+                       break;
+               case AF_INET6:
+                       name = inet_ntop(AF_INET,
+                               &((struct sockaddr_in *)&sa)->sin_addr,
+                               tmp, sizeof tmp);
+                       break;
+               }
+               if (name == NULL) {
+                       sprintf(tmp, "<unknown: %lu>",
+                               (unsigned long)sa.sa_family);
+                       name = tmp;
+               }
+               fprintf(stderr, "accepting connection from: %s\n", name);
+       }
+
+       /*
+        * We make the socket non-blocking, since we are going to use
+        * poll() to organise I/O.
+        */
+       fcntl(fd, F_SETFL, O_NONBLOCK);
+       return fd;
+}
+
+static void
+usage_server(void)
+{
+       fprintf(stderr,
+"usage: brssl server [ options ]\n");
+       fprintf(stderr,
+"options:\n");
+       fprintf(stderr,
+"   -q              suppress verbose messages\n");
+       fprintf(stderr,
+"   -trace          activate extra debug messages (dump of all packets)\n");
+       fprintf(stderr,
+"   -b name         bind to a specific address or host name\n");
+       fprintf(stderr,
+"   -p port         bind to a specific port (default: 4433)\n");
+       fprintf(stderr,
+"   -mono           use monodirectional buffering\n");
+       fprintf(stderr,
+"   -buf length     set the I/O buffer length (in bytes)\n");
+       fprintf(stderr,
+"   -cache length   set the session cache storage length (in bytes)\n");
+       fprintf(stderr,
+"   -cert fname     read certificate chain from file 'fname'\n");
+       fprintf(stderr,
+"   -key fname      read private key from file 'fname'\n");
+       fprintf(stderr,
+"   -list           list supported names (protocols, algorithms...)\n");
+       fprintf(stderr,
+"   -vmin name      set minimum supported version (default: TLS-1.0)\n");
+       fprintf(stderr,
+"   -vmax name      set maximum supported version (default: TLS-1.2)\n");
+       fprintf(stderr,
+"   -cs names       set list of supported cipher suites (comma-separated)\n");
+       fprintf(stderr,
+"   -hf names       add support for some hash functions (comma-separated)\n");
+       fprintf(stderr,
+"   -serverpref     enforce server's preferences for cipher suites\n");
+       exit(EXIT_FAILURE);
+}
+
+typedef struct {
+       const br_ssl_server_policy_class *vtable;
+       int verbose;
+       br_x509_certificate *chain;
+       size_t chain_len;
+       int cert_signer_algo;
+       private_key *sk;
+} policy_context;
+
+static int
+get_cert_signer_algo(br_x509_certificate *xc)
+{
+       br_x509_decoder_context dc;
+       int err;
+
+       br_x509_decoder_init(&dc, 0, 0);
+       br_x509_decoder_push(&dc, xc->data, xc->data_len);
+       err = br_x509_decoder_last_error(&dc);
+       if (err != 0) {
+               return -err;
+       } else {
+               return br_x509_decoder_get_signer_key_type(&dc);
+       }
+}
+
+static int
+sp_choose(const br_ssl_server_policy_class **pctx,
+       const br_ssl_server_context *cc,
+       br_ssl_server_choices *choices)
+{
+       policy_context *pc;
+       const br_suite_translated *st;
+       size_t u, st_num;
+       unsigned chashes;
+       int hash_id;
+
+       pc = (policy_context *)pctx;
+       st = br_ssl_server_get_client_suites(cc, &st_num);
+       chashes = br_ssl_server_get_client_hashes(cc);
+       for (hash_id = 6; hash_id >= 2; hash_id --) {
+               if ((chashes >> hash_id) & 1) {
+                       break;
+               }
+       }
+       if (pc->verbose) {
+               fprintf(stderr, "Client parameters:\n");
+               fprintf(stderr, "   Maximum version:      ");
+               switch (cc->client_max_version) {
+               case BR_SSL30:
+                       fprintf(stderr, "SSL 3.0");
+                       break;
+               case BR_TLS10:
+                       fprintf(stderr, "TLS 1.0");
+                       break;
+               case BR_TLS11:
+                       fprintf(stderr, "TLS 1.1");
+                       break;
+               case BR_TLS12:
+                       fprintf(stderr, "TLS 1.2");
+                       break;
+               default:
+                       fprintf(stderr, "unknown (0x%04X)",
+                               (unsigned)cc->client_max_version);
+                       break;
+               }
+               fprintf(stderr, "\n");
+               fprintf(stderr, "   Compatible cipher suites:\n");
+               for (u = 0; u < st_num; u ++) {
+                       char csn[80];
+
+                       get_suite_name_ext(st[u][0], csn, sizeof csn);
+                       fprintf(stderr, "      %s\n", csn);
+               }
+               fprintf(stderr, "   Common hash functions:");
+               for (u = 2; u <= 6; u ++) {
+                       if ((chashes >> u) & 1) {
+                               int z;
+
+                               switch (u) {
+                               case 3: z = 224; break;
+                               case 4: z = 256; break;
+                               case 5: z = 384; break;
+                               case 6: z = 512; break;
+                               default:
+                                       z = 1;
+                                       break;
+                               }
+                               fprintf(stderr, " sha%d", z);
+                       }
+               }
+               fprintf(stderr, "\n");
+       }
+       for (u = 0; u < st_num; u ++) {
+               unsigned tt;
+
+               tt = st[u][1];
+               switch (tt >> 12) {
+               case BR_SSLKEYX_RSA:
+                       if (pc->sk->key_type == BR_KEYTYPE_RSA) {
+                               choices->cipher_suite = st[u][0];
+                               goto choose_ok;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDHE_RSA:
+                       if (pc->sk->key_type == BR_KEYTYPE_RSA) {
+                               choices->cipher_suite = st[u][0];
+                               choices->hash_id = hash_id;
+                               goto choose_ok;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDHE_ECDSA:
+                       if (pc->sk->key_type == BR_KEYTYPE_EC) {
+                               choices->cipher_suite = st[u][0];
+                               choices->hash_id = hash_id;
+                               goto choose_ok;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDH_RSA:
+                       if (pc->sk->key_type == BR_KEYTYPE_EC
+                               && pc->cert_signer_algo == BR_KEYTYPE_RSA)
+                       {
+                               choices->cipher_suite = st[u][0];
+                               goto choose_ok;
+                       }
+                       break;
+               case BR_SSLKEYX_ECDH_ECDSA:
+                       if (pc->sk->key_type == BR_KEYTYPE_EC
+                               && pc->cert_signer_algo == BR_KEYTYPE_EC)
+                       {
+                               choices->cipher_suite = st[u][0];
+                               goto choose_ok;
+                       }
+                       break;
+               }
+       }
+       return 0;
+
+choose_ok:
+       choices->chain = pc->chain;
+       choices->chain_len = pc->chain_len;
+       if (pc->verbose) {
+               char csn[80];
+
+               get_suite_name_ext(choices->cipher_suite, csn, sizeof csn);
+               fprintf(stderr, "Using: %s\n", csn);
+       }
+       return 1;
+}
+
+static uint32_t
+sp_do_keyx(const br_ssl_server_policy_class **pctx,
+       unsigned char *data, size_t len)
+{
+       policy_context *pc;
+
+       pc = (policy_context *)pctx;
+       switch (pc->sk->key_type) {
+       case BR_KEYTYPE_RSA:
+               return br_rsa_ssl_decrypt(
+                       &br_rsa_i31_private, &pc->sk->key.rsa,
+                       data, len);
+       case BR_KEYTYPE_EC:
+               return br_ec_prime_i31.mul(data, len, pc->sk->key.ec.x,
+                       pc->sk->key.ec.xlen, pc->sk->key.ec.curve);
+       default:
+               fprintf(stderr, "ERROR: unknown private key type (%d)\n",
+                       (int)pc->sk->key_type);
+               return 0;
+       }
+}
+
+/*
+ * OID for hash functions in RSA signatures.
+ */
+static const unsigned char HASH_OID_SHA1[] = {
+       0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
+};
+
+static const unsigned char HASH_OID_SHA224[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
+};
+
+static const unsigned char HASH_OID_SHA256[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+static const unsigned char HASH_OID_SHA384[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+static const unsigned char HASH_OID_SHA512[] = {
+       0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+static const unsigned char *HASH_OID[] = {
+       HASH_OID_SHA1,
+       HASH_OID_SHA224,
+       HASH_OID_SHA256,
+       HASH_OID_SHA384,
+       HASH_OID_SHA512
+};
+
+static const br_hash_class *
+get_hash_impl(int hash_id)
+{
+       size_t u;
+
+       for (u = 0; hash_functions[u].name; u ++) {
+               const br_hash_class *hc;
+               int id;
+
+               hc = hash_functions[u].hclass;
+               id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+               if (id == hash_id) {
+                       return hc;
+               }
+       }
+       return NULL;
+}
+
+static size_t
+sp_do_sign(const br_ssl_server_policy_class **pctx,
+       int hash_id, size_t hv_len, unsigned char *data, size_t len)
+{
+       policy_context *pc;
+       unsigned char hv[64];
+
+       pc = (policy_context *)pctx;
+       memcpy(hv, data, hv_len);
+       switch (pc->sk->key_type) {
+               size_t sig_len;
+               uint32_t x;
+               const unsigned char *hash_oid;
+               const br_hash_class *hc;
+
+       case BR_KEYTYPE_RSA:
+               if (hash_id == 0) {
+                       hash_oid = NULL;
+               } else if (hash_id >= 2 && hash_id <= 6) {
+                       hash_oid = HASH_OID[hash_id - 2];
+               } else {
+                       if (pc->verbose) {
+                               fprintf(stderr, "ERROR: cannot RSA-sign with"
+                                       " unknown hash function: %d\n",
+                                       hash_id);
+                       }
+                       return 0;
+               }
+               sig_len = (pc->sk->key.rsa.n_bitlen + 7) >> 3;
+               if (len < sig_len) {
+                       if (pc->verbose) {
+                               fprintf(stderr, "ERROR: cannot RSA-sign,"
+                                       " buffer is too small"
+                                       " (sig=%lu, buf=%lu)\n",
+                                       (unsigned long)sig_len,
+                                       (unsigned long)len);
+                       }
+                       return 0;
+               }
+               x = br_rsa_i31_pkcs1_sign(hash_oid, hv, hv_len,
+                       &pc->sk->key.rsa, data);
+               if (!x) {
+                       if (pc->verbose) {
+                               fprintf(stderr, "ERROR: RSA-sign failure\n");
+                       }
+                       return 0;
+               }
+               return sig_len;
+
+       case BR_KEYTYPE_EC:
+               hc = get_hash_impl(hash_id);
+               if (hc == NULL) {
+                       if (pc->verbose) {
+                               fprintf(stderr, "ERROR: cannot RSA-sign with"
+                                       " unknown hash function: %d\n",
+                                       hash_id);
+                       }
+                       return 0;
+               }
+               if (len < 139) {
+                       if (pc->verbose) {
+                               fprintf(stderr, "ERROR: cannot ECDSA-sign"
+                                       " (output buffer = %lu)\n",
+                                       (unsigned long)len);
+                       }
+                       return 0;
+               }
+               sig_len = br_ecdsa_i31_sign_asn1(&br_ec_prime_i31, 
+                       hc, hv, &pc->sk->key.ec, data);
+               if (sig_len == 0) {
+                       if (pc->verbose) {
+                               fprintf(stderr, "ERROR: ECDSA-sign failure\n");
+                       }
+                       return 0;
+               }
+               return sig_len;
+
+       default:
+               return 0;
+       }
+}
+
+static const br_ssl_server_policy_class policy_vtable = {
+       sizeof(policy_context),
+       sp_choose,
+       sp_do_keyx,
+       sp_do_sign
+};
+
+/* see brssl.h */
+int
+do_server(int argc, char *argv[])
+{
+       int retcode;
+       int verbose;
+       int trace;
+       int i, bidi;
+       const char *bind_name;
+       const char *port;
+       unsigned vmin, vmax;
+       cipher_suite *suites;
+       size_t num_suites;
+       uint16_t *suite_ids;
+       unsigned hfuns;
+       br_x509_certificate *chain;
+       size_t chain_len;
+       int cert_signer_algo;
+       private_key *sk;
+       size_t u;
+       br_ssl_server_context cc;
+       policy_context pc;
+       br_ssl_session_cache_lru lru;
+       unsigned char *iobuf, *cache;
+       size_t iobuf_len, cache_len;
+       uint32_t flags;
+       int server_fd, fd;
+
+       retcode = 0;
+       verbose = 1;
+       trace = 0;
+       bind_name = NULL;
+       port = NULL;
+       bidi = 1;
+       vmin = 0;
+       vmax = 0;
+       suites = NULL;
+       num_suites = 0;
+       hfuns = 0;
+       suite_ids = NULL;
+       chain = NULL;
+       chain_len = 0;
+       sk = NULL;
+       iobuf = NULL;
+       iobuf_len = 0;
+       cache = NULL;
+       cache_len = (size_t)-1;
+       flags = 0;
+       server_fd = -1;
+       fd = -1;
+       for (i = 0; i < argc; i ++) {
+               const char *arg;
+
+               arg = argv[i];
+               if (arg[0] != '-') {
+                       usage_server();
+                       goto server_exit_error;
+               }
+               if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+                       verbose = 1;
+               } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+                       verbose = 0;
+               } else if (eqstr(arg, "-trace")) {
+                       trace = 1;
+               } else if (eqstr(arg, "-b")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-b'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       if (bind_name != NULL) {
+                               fprintf(stderr, "ERROR: duplicate bind host\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       bind_name = argv[i];
+               } else if (eqstr(arg, "-p")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-p'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       if (port != NULL) {
+                               fprintf(stderr, "ERROR: duplicate bind port\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       port = argv[i];
+               } else if (eqstr(arg, "-mono")) {
+                       bidi = 0;
+               } else if (eqstr(arg, "-buf")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-buf'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       if (iobuf_len != 0) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate I/O buffer length\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       iobuf_len = strtoul(arg, 0, 10);
+               } else if (eqstr(arg, "-cache")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-cache'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       if (cache_len != (size_t)-1) {
+                               fprintf(stderr, "ERROR: duplicate session"
+                                       " cache length\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       cache_len = strtoul(arg, 0, 10);
+               } else if (eqstr(arg, "-cert")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-cert'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       if (chain != NULL) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate certificate chain\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       chain = read_certificates(arg, &chain_len);
+                       if (chain == NULL || chain_len == 0) {
+                               goto server_exit_error;
+                       }
+               } else if (eqstr(arg, "-key")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-key'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       if (sk != NULL) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate private key\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       sk = read_private_key(arg);
+                       if (sk == NULL) {
+                               goto server_exit_error;
+                       }
+               } else if (eqstr(arg, "-list")) {
+                       list_names();
+                       goto server_exit;
+               } else if (eqstr(arg, "-vmin")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-vmin'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       if (vmin != 0) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate minimum version\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       vmin = parse_version(arg, strlen(arg));
+                       if (vmin == 0) {
+                               fprintf(stderr,
+                                       "ERROR: unrecognised version '%s'\n",
+                                       arg);
+                               usage_server();
+                               goto server_exit_error;
+                       }
+               } else if (eqstr(arg, "-vmax")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-vmax'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       if (vmax != 0) {
+                               fprintf(stderr,
+                                       "ERROR: duplicate maximum version\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       vmax = parse_version(arg, strlen(arg));
+                       if (vmax == 0) {
+                               fprintf(stderr,
+                                       "ERROR: unrecognised version '%s'\n",
+                                       arg);
+                               usage_server();
+                               goto server_exit_error;
+                       }
+               } else if (eqstr(arg, "-cs")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-cs'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       if (suites != NULL) {
+                               fprintf(stderr, "ERROR: duplicate list"
+                                       " of cipher suites\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       suites = parse_suites(arg, &num_suites);
+                       if (suites == NULL) {
+                               usage_server();
+                               goto server_exit_error;
+                       }
+               } else if (eqstr(arg, "-hf")) {
+                       unsigned x;
+
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-hf'\n");
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       arg = argv[i];
+                       x = parse_hash_functions(arg);
+                       if (x == 0) {
+                               usage_server();
+                               goto server_exit_error;
+                       }
+                       hfuns |= x;
+               } else if (eqstr(arg, "-serverpref")) {
+                       flags |= BR_OPT_ENFORCE_SERVER_PREFERENCES;
+               } else {
+                       fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+                       usage_server();
+                       goto server_exit_error;
+               }
+       }
+       if (port == NULL) {
+               port = "4433";
+       }
+       if (vmin == 0) {
+               vmin = BR_TLS10;
+       }
+       if (vmax == 0) {
+               vmax = BR_TLS12;
+       }
+       if (vmax < vmin) {
+               fprintf(stderr, "ERROR: impossible minimum/maximum protocol"
+                       " version combination\n");
+               usage_server();
+               goto server_exit_error;
+       }
+       if (suites == NULL) {
+               num_suites = 0;
+
+               for (u = 0; cipher_suites[u].name; u ++) {
+                       if ((cipher_suites[u].req & REQ_TLS12) == 0
+                               || vmax >= BR_TLS12)
+                       {
+                               num_suites ++;
+                       }
+               }
+               suites = xmalloc(num_suites * sizeof *suites);
+               num_suites = 0;
+               for (u = 0; cipher_suites[u].name; u ++) {
+                       if ((cipher_suites[u].req & REQ_TLS12) == 0
+                               || vmax >= BR_TLS12)
+                       {
+                               suites[num_suites ++] = cipher_suites[u];
+                       }
+               }
+       }
+       if (hfuns == 0) {
+               hfuns = (unsigned)-1;
+       }
+       if (chain == NULL || chain_len == 0) {
+               fprintf(stderr, "ERROR: no certificate chain provided\n");
+               goto server_exit_error;
+       }
+       if (sk == NULL) {
+               fprintf(stderr, "ERROR: no private key provided\n");
+               goto server_exit_error;
+       }
+       switch (sk->key_type) {
+               int curve;
+               uint32_t supp;
+
+       case BR_KEYTYPE_RSA:
+               break;
+       case BR_KEYTYPE_EC:
+               curve = sk->key.ec.curve;
+               supp = br_ec_prime_i31.supported_curves;
+               if (curve > 31 || !((supp >> curve) & 1)) {
+                       fprintf(stderr, "ERROR: private key curve (%d)"
+                               " is not supported\n", curve);
+                       goto server_exit_error;
+               }
+               break;
+       default:
+               fprintf(stderr, "ERROR: unsupported private key type (%d)\n",
+                       sk->key_type);
+               break;
+       }
+       cert_signer_algo = get_cert_signer_algo(chain);
+       if (cert_signer_algo < 0) {
+               fprintf(stderr, "ERROR: server certificate cannot be"
+                       " decoded (err=%d)\n", -cert_signer_algo);
+               goto server_exit_error;
+       } else if (verbose) {
+               const char *csas;
+
+               switch (cert_signer_algo) {
+               case BR_KEYTYPE_RSA: csas = "RSA"; break;
+               case BR_KEYTYPE_EC:  csas = "EC"; break;
+               default:
+                       csas = "unknown";
+                       break;
+               }
+               fprintf(stderr, "Issuing CA key type: %d (%s)\n",
+                       cert_signer_algo, csas);
+       }
+       if (iobuf_len == 0) {
+               if (bidi) {
+                       iobuf_len = BR_SSL_BUFSIZE_BIDI;
+               } else {
+                       iobuf_len = BR_SSL_BUFSIZE_MONO;
+               }
+       }
+       iobuf = xmalloc(iobuf_len);
+       if (cache_len == (size_t)-1) {
+               cache_len = 5000;
+       }
+       cache = xmalloc(cache_len);
+
+       /*
+        * Compute implementation requirements and inject implementations.
+        */
+       suite_ids = xmalloc(num_suites * sizeof *suite_ids);
+       br_ssl_server_zero(&cc);
+       br_ssl_engine_set_versions(&cc.eng, vmin, vmax);
+       br_ssl_server_set_all_flags(&cc, flags);
+       if (vmin <= BR_TLS11) {
+               if (!(hfuns & (1 << br_md5_ID))) {
+                       fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need MD5\n");
+                       goto server_exit_error;
+               }
+               if (!(hfuns & (1 << br_sha1_ID))) {
+                       fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need SHA-1\n");
+                       goto server_exit_error;
+               }
+       }
+       for (u = 0; u < num_suites; u ++) {
+               unsigned req;
+
+               req = suites[u].req;
+               suite_ids[u] = suites[u].suite;
+               if ((req & REQ_TLS12) != 0 && vmax < BR_TLS12) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires TLS 1.2\n",
+                               suites[u].name);
+                       goto server_exit_error;
+               }
+               if ((req & REQ_SHA1) != 0 && !(hfuns & (1 << br_sha1_ID))) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires SHA-1\n",
+                               suites[u].name);
+                       goto server_exit_error;
+               }
+               if ((req & REQ_SHA256) != 0 && !(hfuns & (1 << br_sha256_ID))) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires SHA-256\n",
+                               suites[u].name);
+                       goto server_exit_error;
+               }
+               if ((req & REQ_SHA384) != 0 && !(hfuns & (1 << br_sha384_ID))) {
+                       fprintf(stderr,
+                               "ERROR: cipher suite %s requires SHA-384\n",
+                               suites[u].name);
+                       goto server_exit_error;
+               }
+               /* TODO: algorithm implementation selection */
+               if ((req & REQ_AESCBC) != 0) {
+                       br_ssl_engine_set_aes_cbc(&cc.eng,
+                               &br_aes_ct_cbcenc_vtable,
+                               &br_aes_ct_cbcdec_vtable);
+                       br_ssl_engine_set_cbc(&cc.eng,
+                               &br_sslrec_in_cbc_vtable,
+                               &br_sslrec_out_cbc_vtable);
+               }
+               if ((req & REQ_AESGCM) != 0) {
+                       br_ssl_engine_set_aes_ctr(&cc.eng,
+                               &br_aes_ct_ctr_vtable);
+                       br_ssl_engine_set_ghash(&cc.eng,
+                               &br_ghash_ctmul);
+                       br_ssl_engine_set_gcm(&cc.eng,
+                               &br_sslrec_in_gcm_vtable,
+                               &br_sslrec_out_gcm_vtable);
+               }
+               if ((req & REQ_3DESCBC) != 0) {
+                       br_ssl_engine_set_des_cbc(&cc.eng,
+                               &br_des_ct_cbcenc_vtable,
+                               &br_des_ct_cbcdec_vtable);
+                       br_ssl_engine_set_cbc(&cc.eng,
+                               &br_sslrec_in_cbc_vtable,
+                               &br_sslrec_out_cbc_vtable);
+               }
+               if ((req & (REQ_ECDHE_RSA | REQ_ECDHE_ECDSA)) != 0) {
+                       br_ssl_engine_set_ec(&cc.eng, &br_ec_prime_i31);
+               }
+       }
+       br_ssl_engine_set_suites(&cc.eng, suite_ids, num_suites);
+
+       for (u = 0; hash_functions[u].name; u ++) {
+               const br_hash_class *hc;
+               int id;
+
+               hc = hash_functions[u].hclass;
+               id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+               if ((hfuns & ((unsigned)1 << id)) != 0) {
+                       br_ssl_engine_set_hash(&cc.eng, id, hc);
+               }
+       }
+       if (vmin <= BR_TLS11) {
+               br_ssl_engine_set_prf10(&cc.eng, &br_tls10_prf);
+       }
+       if (vmax >= BR_TLS12) {
+               if ((hfuns & ((unsigned)1 << br_sha256_ID)) != 0) {
+                       br_ssl_engine_set_prf_sha256(&cc.eng,
+                               &br_tls12_sha256_prf);
+               }
+               if ((hfuns & ((unsigned)1 << br_sha384_ID)) != 0) {
+                       br_ssl_engine_set_prf_sha384(&cc.eng,
+                               &br_tls12_sha384_prf);
+               }
+       }
+
+       br_ssl_session_cache_lru_init(&lru, cache, cache_len);
+       br_ssl_server_set_cache(&cc, &lru.vtable);
+
+       pc.vtable = &policy_vtable;
+       pc.verbose = verbose;
+       pc.chain = chain;
+       pc.chain_len = chain_len;
+       pc.cert_signer_algo = cert_signer_algo;
+       pc.sk = sk;
+       br_ssl_server_set_policy(&cc, &pc.vtable);
+
+       br_ssl_engine_set_buffer(&cc.eng, iobuf, iobuf_len, bidi);
+
+       /*
+        * Open the server socket.
+        */
+       server_fd = host_bind(bind_name, port, verbose);
+       if (server_fd < 0) {
+               goto server_exit_error;
+       }
+
+       /*
+        * Process incoming clients, one at a time. Note that we do not
+        * accept any client until the previous connection has finished:
+        * this is voluntary, since the tool uses stdin/stdout for
+        * application data, and thus cannot really run two connections
+        * simultaneously.
+        */
+       for (;;) {
+               int x;
+
+               fd = accept_client(server_fd, verbose);
+               if (fd < 0) {
+                       goto server_exit_error;
+               }
+               br_ssl_server_reset(&cc);
+               x = run_ssl_engine(&cc.eng, fd,
+                       (verbose ? RUN_ENGINE_VERBOSE : 0)
+                       | (trace ? RUN_ENGINE_TRACE : 0));
+               close(fd);
+               fd = -1;
+               if (x < -1) {
+                       goto server_exit_error;
+               }
+       }
+
+       /*
+        * Release allocated structures.
+        */
+server_exit:
+       xfree(suites);
+       xfree(suite_ids);
+       if (chain != NULL) {
+               for (u = 0; u < chain_len; u ++) {
+                       xfree(chain[u].data);
+               }
+               xfree(chain);
+       }
+       if (sk != NULL) {
+               free_private_key(sk);
+       }
+       xfree(iobuf);
+       xfree(cache);
+       if (fd >= 0) {
+               close(fd);
+       }
+       return retcode;
+
+server_exit_error:
+       retcode = -1;
+       goto server_exit;
+}
diff --git a/tools/skey.c b/tools/skey.c
new file mode 100644 (file)
index 0000000..a9ecb32
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static void
+print_int_text(const char *name, const unsigned char *buf, size_t len)
+{
+       size_t u;
+
+       printf("%s = ", name);
+       for (u = 0; u < len; u ++) {
+               printf("%02X", buf[u]);
+       }
+       printf("\n");
+}
+
+static void
+print_int_C(const char *name, const unsigned char *buf, size_t len)
+{
+       size_t u;
+
+       printf("\nstatic const unsigned char %s[] = {", name);
+       for (u = 0; u < len; u ++) {
+               if (u != 0) {
+                       printf(",");
+               }
+               if (u % 12 == 0) {
+                       printf("\n\t");
+               } else {
+                       printf(" ");
+               }
+               printf("0x%02X", buf[u]);
+       }
+       printf("\n};\n");
+}
+
+static void
+print_rsa(const br_rsa_private_key *sk, int print_text, int print_C)
+{
+       if (print_text) {
+               print_int_text("p ", sk->p, sk->plen);
+               print_int_text("q ", sk->q, sk->qlen);
+               print_int_text("dp", sk->dp, sk->dplen);
+               print_int_text("dq", sk->dq, sk->dqlen);
+               print_int_text("iq", sk->iq, sk->iqlen);
+       }
+       if (print_C) {
+               print_int_C("RSA_P", sk->p, sk->plen);
+               print_int_C("RSA_Q", sk->q, sk->qlen);
+               print_int_C("RSA_DP", sk->dp, sk->dplen);
+               print_int_C("RSA_DQ", sk->dq, sk->dqlen);
+               print_int_C("RSA_IQ", sk->iq, sk->iqlen);
+               printf("\nstatic const br_rsa_private_key RSA = {\n");
+               printf("\t%lu,\n", (unsigned long)sk->n_bitlen);
+               printf("\t(unsigned char *)RSA_P, sizeof RSA_P,\n");
+               printf("\t(unsigned char *)RSA_Q, sizeof RSA_Q,\n");
+               printf("\t(unsigned char *)RSA_DP, sizeof RSA_DP,\n");
+               printf("\t(unsigned char *)RSA_DQ, sizeof RSA_DQ,\n");
+               printf("\t(unsigned char *)RSA_IQ, sizeof RSA_IQ\n");
+               printf("};\n");
+       }
+}
+
+static void
+print_ec(const br_ec_private_key *sk, int print_text, int print_C)
+{
+       if (print_text) {
+               print_int_text("x", sk->x, sk->xlen);
+       }
+       if (print_C) {
+               print_int_C("EC_X", sk->x, sk->xlen);
+               printf("\nstatic const br_ec_private_key EC = {\n");
+               printf("\t%d,\n", sk->curve);
+               printf("\t(unsigned char *)EC_X, sizeof EC_X\n");
+               printf("};\n");
+       }
+}
+
+static int
+decode_key(const unsigned char *buf, size_t len, int print_text, int print_C)
+{
+       br_skey_decoder_context dc;
+       int err;
+
+       br_skey_decoder_init(&dc);
+       br_skey_decoder_push(&dc, buf, len);
+       err = br_skey_decoder_last_error(&dc);
+       if (err != 0) {
+               const char *errname, *errmsg;
+
+               fprintf(stderr, "ERROR (decoding): err=%d\n", err);
+               errname = find_error_name(err, &errmsg);
+               if (errname != NULL) {
+                       fprintf(stderr, "  %s: %s\n", errname, errmsg);
+               } else {
+                       fprintf(stderr, "  (unknown)\n");
+               }
+               return -1;
+       }
+       switch (br_skey_decoder_key_type(&dc)) {
+               const br_rsa_private_key *rk;
+               const br_ec_private_key *ek;
+
+       case BR_KEYTYPE_RSA:
+               rk = br_skey_decoder_get_rsa(&dc);
+               printf("RSA key (%lu bits)\n", (unsigned long)rk->n_bitlen);
+               print_rsa(rk, print_text, print_C);
+               break;
+
+       case BR_KEYTYPE_EC:
+               ek = br_skey_decoder_get_ec(&dc);
+               printf("EC key (curve = %d: %s)\n",
+                       ek->curve, ec_curve_name(ek->curve));
+               print_ec(ek, print_text, print_C);
+               break;
+
+       default:
+               fprintf(stderr, "Unknown key type: %d\n",
+                       br_skey_decoder_key_type(&dc));
+               return -1;
+       }
+
+       return 0;
+}
+
+static void
+usage_skey(void)
+{
+       fprintf(stderr,
+"usage: brssl skey [ options ] file...\n");
+       fprintf(stderr,
+"options:\n");
+       fprintf(stderr,
+"   -q            suppress verbose messages\n");
+       fprintf(stderr,
+"   -text         print public key details (human-readable)\n");
+       fprintf(stderr,
+"   -C            print public key details (C code)\n");
+}
+
+/* see brssl.h */
+int
+do_skey(int argc, char *argv[])
+{
+       int retcode;
+       int verbose;
+       int i, num_files;
+       int print_text, print_C;
+       unsigned char *buf;
+       size_t len;
+       pem_object *pos;
+
+       retcode = 0;
+       verbose = 1;
+       print_text = 0;
+       print_C = 0;
+       num_files = 0;
+       buf = NULL;
+       pos = NULL;
+       for (i = 0; i < argc; i ++) {
+               const char *arg;
+
+               arg = argv[i];
+               if (arg[0] != '-') {
+                       num_files ++;
+                       continue;
+               }
+               argv[i] = NULL;
+               if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+                       verbose = 1;
+               } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+                       verbose = 0;
+               } else if (eqstr(arg, "-text")) {
+                       print_text = 1;
+               } else if (eqstr(arg, "-C")) {
+                       print_C = 1;
+               } else {
+                       fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+                       usage_skey();
+                       goto skey_exit_error;
+               }
+       }
+       if (num_files == 0) {
+               fprintf(stderr, "ERROR: no private key provided\n");
+               usage_skey();
+               goto skey_exit_error;
+       }
+
+       for (i = 0; i < argc; i ++) {
+               const char *fname;
+
+               fname = argv[i];
+               if (fname == NULL) {
+                       continue;
+               }
+               buf = read_file(fname, &len);
+               if (buf == NULL) {
+                       goto skey_exit_error;
+               }
+               if (looks_like_DER(buf, len)) {
+                       if (verbose) {
+                               fprintf(stderr, "File '%s': ASN.1/DER object\n",
+                                       fname);
+                       }
+                       if (decode_key(buf, len, print_text, print_C) < 0) {
+                               goto skey_exit_error;
+                       }
+               } else {
+                       size_t u, num;
+
+                       if (verbose) {
+                               fprintf(stderr, "File '%s': decoding as PEM\n",
+                                       fname);
+                       }
+                       pos = decode_pem(buf, len, &num);
+                       if (pos == NULL) {
+                               goto skey_exit_error;
+                       }
+                       for (u = 0; pos[u].name; u ++) {
+                               const char *name;
+
+                               name = pos[u].name;
+                               if (eqstr(name, "RSA PRIVATE KEY")
+                                       || eqstr(name, "EC PRIVATE KEY")
+                                       || eqstr(name, "PRIVATE KEY"))
+                               {
+                                       if (decode_key(pos[u].data,
+                                               pos[u].data_len,
+                                               print_text, print_C) < 0)
+                                       {
+                                               goto skey_exit_error;
+                                       }
+                               } else {
+                                       if (verbose) {
+                                               fprintf(stderr,
+                                                       "(skipping '%s')\n",
+                                                       name);
+                                       }
+                               }
+                       }
+                       for (u = 0; pos[u].name; u ++) {
+                               free_pem_object_contents(&pos[u]);
+                       }
+                       xfree(pos);
+                       pos = NULL;
+               }
+               xfree(buf);
+               buf = NULL;
+       }
+
+       /*
+        * Release allocated structures.
+        */
+skey_exit:
+       xfree(buf);
+       if (pos != NULL) {
+               size_t u;
+
+               for (u = 0; pos[u].name; u ++) {
+                       free_pem_object_contents(&pos[u]);
+               }
+               xfree(pos);
+       }
+       return retcode;
+
+skey_exit_error:
+       retcode = -1;
+       goto skey_exit;
+}
diff --git a/tools/sslio.c b/tools/sslio.c
new file mode 100644 (file)
index 0000000..64d6c96
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static void
+dump_blob(const char *name, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       size_t u;
+
+       buf = data;
+       fprintf(stderr, "%s (len = %lu)", name, (unsigned long)len);
+       for (u = 0; u < len; u ++) {
+               if ((u & 15) == 0) {
+                       fprintf(stderr, "\n%08lX  ", (unsigned long)u);
+               } else if ((u & 7) == 0) {
+                       fprintf(stderr, " ");
+               }
+               fprintf(stderr, " %02x", buf[u]);
+       }
+       fprintf(stderr, "\n");
+}
+
+/*
+ * Inspect the provided data in case it is a "command" to trigger a
+ * special behaviour. If the command is recognised, then it is executed
+ * and this function returns 1. Otherwise, this function returns 0.
+ */
+static int
+run_command(br_ssl_engine_context *cc, unsigned char *buf, size_t len)
+{
+       if (len < 2 || len > 3) {
+               return 0;
+       }
+       if (len == 3 && (buf[1] != '\r' || buf[2] != '\n')) {
+               return 0;
+       }
+       if (len == 2 && buf[1] != '\n') {
+               return 0;
+       }
+       switch (buf[0]) {
+       case 'Q':
+               fprintf(stderr, "closing...\n");
+               br_ssl_engine_close(cc);
+               return 1;
+       case 'R':
+               fprintf(stderr, "renegotiating...\n");
+               br_ssl_engine_renegotiate(cc);
+               return 1;
+       case 'F':
+               /*
+                * Session forget is nominally client-only. But the
+                * session parameters are in the engine structure, which
+                * is the first field of the client context, so the cast
+                * still works properly. On the server, this forgetting
+                * has no effect.
+                */
+               fprintf(stderr, "forgetting session...\n");
+               br_ssl_client_forget_session((br_ssl_client_context *)cc);
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+/* see brssl.h */
+int
+run_ssl_engine(br_ssl_engine_context *cc, int fd, unsigned flags)
+{
+       int hsdetails;
+       int retcode;
+       int verbose;
+       int trace;
+
+       hsdetails = 0;
+       retcode = 0;
+       verbose = (flags & RUN_ENGINE_VERBOSE) != 0;
+       trace = (flags & RUN_ENGINE_TRACE) != 0;
+
+       /*
+        * Make sure that stdin and stdout are non-blocking.
+        */
+       fcntl(0, F_SETFL, O_NONBLOCK);
+       fcntl(1, F_SETFL, O_NONBLOCK);
+
+       /*
+        * Perform the loop.
+        */
+       for (;;) {
+               unsigned st;
+               int sendrec, recvrec, sendapp, recvapp;
+               struct pollfd pfd[3];
+               int n;
+               size_t u, k_fd, k_in, k_out;
+
+               /*
+                * Get current engine state.
+                */
+               st = br_ssl_engine_current_state(cc);
+               if (st == BR_SSL_CLOSED) {
+                       int err;
+
+                       err = br_ssl_engine_last_error(cc);
+                       if (err == BR_ERR_OK) {
+                               if (verbose) {
+                                       fprintf(stderr,
+                                               "SSL closed normally\n");
+                               }
+                               retcode = 0;
+                               goto engine_exit;
+                       } else {
+                               fprintf(stderr, "ERROR: SSL error %d", err);
+                               retcode = err;
+                               if (err >= BR_ERR_SEND_FATAL_ALERT) {
+                                       err -= BR_ERR_SEND_FATAL_ALERT;
+                                       fprintf(stderr,
+                                               " (sent alert %d)\n", err);
+                               } else if (err >= BR_ERR_RECV_FATAL_ALERT) {
+                                       err -= BR_ERR_RECV_FATAL_ALERT;
+                                       fprintf(stderr,
+                                               " (received alert %d)\n", err);
+                               } else {
+                                       const char *ename;
+
+                                       ename = find_error_name(err, NULL);
+                                       if (ename == NULL) {
+                                               ename = "unknown";
+                                       }
+                                       fprintf(stderr, " (%s)\n", ename);
+                               }
+                               goto engine_exit;
+                       }
+               }
+
+               /*
+                * Compute descriptors that must be polled, depending
+                * on engine state.
+                */
+               sendrec = ((st & BR_SSL_SENDREC) != 0);
+               recvrec = ((st & BR_SSL_RECVREC) != 0);
+               sendapp = ((st & BR_SSL_SENDAPP) != 0);
+               recvapp = ((st & BR_SSL_RECVAPP) != 0);
+               if (verbose && sendapp && !hsdetails) {
+                       char csn[80];
+
+                       fprintf(stderr, "Handshake completed\n");
+                       fprintf(stderr, "   version:               ");
+                       switch (cc->session.version) {
+                       case BR_SSL30:
+                               fprintf(stderr, "SSL 3.0");
+                               break;
+                       case BR_TLS10:
+                               fprintf(stderr, "TLS 1.0");
+                               break;
+                       case BR_TLS11:
+                               fprintf(stderr, "TLS 1.1");
+                               break;
+                       case BR_TLS12:
+                               fprintf(stderr, "TLS 1.2");
+                               break;
+                       default:
+                               fprintf(stderr, "unknown (0x%04X)",
+                                       (unsigned)cc->session.version);
+                               break;
+                       }
+                       fprintf(stderr, "\n");
+                       get_suite_name_ext(
+                               cc->session.cipher_suite, csn, sizeof csn);
+                       fprintf(stderr, "   cipher suite:          %s\n", csn);
+                       fprintf(stderr, "   secure renegotiation:  %s\n",
+                               cc->reneg == 1 ? "no" : "yes");
+                       hsdetails = 1;
+               }
+
+               k_fd = 0;
+               k_in = 0;
+               k_out = 0;
+
+               u = 0;
+               if (sendrec || recvrec) {
+                       pfd[u].fd = fd;
+                       pfd[u].revents = 0;
+                       pfd[u].events = 0;
+                       if (sendrec) {
+                               pfd[u].events |= POLLOUT;
+                       }
+                       if (recvrec) {
+                               pfd[u].events |= POLLIN;
+                       }
+                       k_fd = u;
+                       u ++;
+               }
+               if (sendapp) {
+                       pfd[u].fd = 0;
+                       pfd[u].revents = 0;
+                       pfd[u].events = POLLIN;
+                       k_in = u;
+                       u ++;
+               }
+               if (recvapp) {
+                       pfd[u].fd = 1;
+                       pfd[u].revents = 0;
+                       pfd[u].events = POLLOUT;
+                       k_out = u;
+                       u ++;
+               }
+               n = poll(pfd, u, -1);
+               if (n < 0) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       perror("ERROR: poll()");
+                       retcode = -2;
+                       goto engine_exit;
+               }
+               if (n == 0) {
+                       continue;
+               }
+
+               /*
+                * We transform closures/errors into read+write accesses
+                * so as to force the read() or write() call that will
+                * detect the situation.
+                */
+               while (u -- > 0) {
+                       if (pfd[u].revents & (POLLERR | POLLHUP)) {
+                               pfd[u].revents |= POLLIN | POLLOUT;
+                       }
+               }
+
+               /*
+                * We give preference to outgoing data, on stdout and on
+                * the socket.
+                */
+               if (recvapp) {
+                       if (pfd[k_out].revents & POLLOUT) {
+                               unsigned char *buf;
+                               size_t len;
+                               ssize_t wlen;
+
+                               buf = br_ssl_engine_recvapp_buf(cc, &len);
+                               wlen = write(1, buf, len);
+                               if (wlen <= 0) {
+                                       if (verbose) {
+                                               fprintf(stderr,
+                                                       "stdout closed...\n");
+                                       }
+                                       retcode = -2;
+                                       goto engine_exit;
+                               }
+                               br_ssl_engine_recvapp_ack(cc, wlen);
+                               continue;
+                       }
+               }
+               if (sendrec) {
+                       if (pfd[k_fd].revents & POLLOUT) {
+                               unsigned char *buf;
+                               size_t len;
+                               ssize_t wlen;
+
+                               buf = br_ssl_engine_sendrec_buf(cc, &len);
+                               wlen = write(fd, buf, len);
+                               if (wlen <= 0) {
+                                       if (verbose) {
+                                               fprintf(stderr,
+                                                       "socket closed...\n");
+                                       }
+                                       retcode = -1;
+                                       goto engine_exit;
+                               }
+                               if (trace) {
+                                       dump_blob("Outgoing bytes", buf, wlen);
+                               }
+                               br_ssl_engine_sendrec_ack(cc, wlen);
+                               continue;
+                       }
+               }
+               if (recvrec) {
+                       if (pfd[k_fd].revents & POLLIN) {
+                               unsigned char *buf;
+                               size_t len;
+                               ssize_t rlen;
+
+                               buf = br_ssl_engine_recvrec_buf(cc, &len);
+                               rlen = read(fd, buf, len);
+                               if (rlen <= 0) {
+                                       if (verbose) {
+                                               fprintf(stderr,
+                                                       "socket closed...\n");
+                                       }
+                                       retcode = -1;
+                                       goto engine_exit;
+                               }
+                               if (trace) {
+                                       dump_blob("Incoming bytes", buf, rlen);
+                               }
+                               br_ssl_engine_recvrec_ack(cc, rlen);
+                               continue;
+                       }
+               }
+               if (sendapp) {
+                       if (pfd[k_in].revents & POLLIN) {
+                               unsigned char *buf;
+                               size_t len;
+                               ssize_t rlen;
+
+                               buf = br_ssl_engine_sendapp_buf(cc, &len);
+                               rlen = read(0, buf, len);
+                               if (rlen <= 0) {
+                                       if (verbose) {
+                                               fprintf(stderr,
+                                                       "stdin closed...\n");
+                                       }
+                                       br_ssl_engine_close(cc);
+                               } else if (!run_command(cc, buf, rlen)) {
+                                       br_ssl_engine_sendapp_ack(cc, rlen);
+                               }
+                               br_ssl_engine_flush(cc, 0);
+                               continue;
+                       }
+               }
+
+               /* We should never reach that point. */
+               fprintf(stderr, "ERROR: poll() misbehaves\n");
+               retcode = -2;
+               goto engine_exit;
+       }
+
+       /*
+        * Release allocated structures.
+        */
+engine_exit:
+       return retcode;
+}
diff --git a/tools/ta.c b/tools/ta.c
new file mode 100644 (file)
index 0000000..a29aae0
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static const char *
+curve_to_sym(int curve)
+{
+       switch (curve) {
+       case BR_EC_sect163k1:         return "BR_EC_sect163k1";
+       case BR_EC_sect163r1:         return "BR_EC_sect163r1";
+       case BR_EC_sect163r2:         return "BR_EC_sect163r2";
+       case BR_EC_sect193r1:         return "BR_EC_sect193r1";
+       case BR_EC_sect193r2:         return "BR_EC_sect193r2";
+       case BR_EC_sect233k1:         return "BR_EC_sect233k1";
+       case BR_EC_sect233r1:         return "BR_EC_sect233r1";
+       case BR_EC_sect239k1:         return "BR_EC_sect239k1";
+       case BR_EC_sect283k1:         return "BR_EC_sect283k1";
+       case BR_EC_sect283r1:         return "BR_EC_sect283r1";
+       case BR_EC_sect409k1:         return "BR_EC_sect409k1";
+       case BR_EC_sect409r1:         return "BR_EC_sect409r1";
+       case BR_EC_sect571k1:         return "BR_EC_sect571k1";
+       case BR_EC_sect571r1:         return "BR_EC_sect571r1";
+       case BR_EC_secp160k1:         return "BR_EC_secp160k1";
+       case BR_EC_secp160r1:         return "BR_EC_secp160r1";
+       case BR_EC_secp160r2:         return "BR_EC_secp160r2";
+       case BR_EC_secp192k1:         return "BR_EC_secp192k1";
+       case BR_EC_secp192r1:         return "BR_EC_secp192r1";
+       case BR_EC_secp224k1:         return "BR_EC_secp224k1";
+       case BR_EC_secp224r1:         return "BR_EC_secp224r1";
+       case BR_EC_secp256k1:         return "BR_EC_secp256k1";
+       case BR_EC_secp256r1:         return "BR_EC_secp256r1";
+       case BR_EC_secp384r1:         return "BR_EC_secp384r1";
+       case BR_EC_secp521r1:         return "BR_EC_secp521r1";
+       case BR_EC_brainpoolP256r1:   return "BR_EC_brainpoolP256r1";
+       case BR_EC_brainpoolP384r1:   return "BR_EC_brainpoolP384r1";
+       case BR_EC_brainpoolP512r1:   return "BR_EC_brainpoolP512r1";
+       }
+       return NULL;
+}
+
+static void
+print_blob(const char *name, const unsigned char *buf, size_t len)
+{
+       size_t u;
+
+       printf("\nstatic const unsigned char %s[] = {", name);
+       for (u = 0; u < len; u ++) {
+               if (u != 0) {
+                       printf(",");
+               }
+               if (u % 12 == 0) {
+                       printf("\n\t");
+               } else {
+                       printf(" ");
+               }
+               printf("0x%02X", buf[u]);
+       }
+       printf("\n};\n");
+}
+
+static int
+print_ta_internals(br_x509_trust_anchor *ta, long ctr)
+{
+       char tmp[25];
+
+       sprintf(tmp, "TA%ld_DN", ctr);
+       print_blob(tmp, ta->dn, ta->dn_len);
+       switch (ta->pkey.key_type) {
+       case BR_KEYTYPE_RSA:
+               sprintf(tmp, "TA%ld_RSA_N", ctr);
+               print_blob(tmp, ta->pkey.key.rsa.n, ta->pkey.key.rsa.nlen);
+               sprintf(tmp, "TA%ld_RSA_E", ctr);
+               print_blob(tmp, ta->pkey.key.rsa.e, ta->pkey.key.rsa.elen);
+               break;
+       case BR_KEYTYPE_EC:
+               sprintf(tmp, "TA%ld_EC_Q", ctr);
+               print_blob(tmp, ta->pkey.key.ec.q, ta->pkey.key.ec.qlen);
+               break;
+       default:
+               fprintf(stderr, "ERROR: unknown anchor key type '%d'\n",
+                       ta->pkey.key_type);
+               return -1;
+       }
+       return 0;
+}
+
+static void
+print_ta(br_x509_trust_anchor *ta, long ctr)
+{
+       char tmp[25];
+
+       printf("\t{\n");
+       printf("\t\t(unsigned char *)TA%ld_DN, sizeof TA%ld_DN,\n", ctr, ctr);
+       printf("\t\t%s,\n", (ta->flags & BR_X509_TA_CA)
+               ? "BR_X509_TA_CA" : "0");
+       printf("\t\t{\n");
+       switch (ta->pkey.key_type) {
+               const char *cname;
+
+       case BR_KEYTYPE_RSA:
+               printf("\t\t\tBR_KEYTYPE_RSA,\n");
+               printf("\t\t\t{ .rsa = {\n");
+               printf("\t\t\t\t(unsigned char *)TA%ld_RSA_N,"
+                       " sizeof TA%ld_RSA_N,\n", ctr, ctr);
+               printf("\t\t\t\t(unsigned char *)TA%ld_RSA_E,"
+                       " sizeof TA%ld_RSA_E,\n", ctr, ctr);
+               printf("\t\t\t} }\n");
+               break;
+       case BR_KEYTYPE_EC:
+               printf("\t\t\tBR_KEYTYPE_EC,\n");
+               printf("\t\t\t{ .ec = {\n");
+               cname = curve_to_sym(ta->pkey.key.ec.curve);
+               if (cname == NULL) {
+                       sprintf(tmp, "%d", ta->pkey.key.ec.curve);
+                       cname = tmp;
+               }
+               printf("\t\t\t\t%s,\n", cname);
+               printf("\t\t\t\t(unsigned char *)TA%ld_EC_Q,"
+                       " sizeof TA%ld_EC_Q,\n", ctr, ctr);
+               printf("\t\t\t} }\n");
+       }
+       printf("\t\t}\n");
+       printf("\t}");
+}
+
+static void
+usage_ta(void)
+{
+       fprintf(stderr,
+"usage: brssl ta [ options ] file...\n");
+       fprintf(stderr,
+"options:\n");
+       fprintf(stderr,
+"   -q            suppress verbose messages\n");
+}
+
+/* see brssl.h */
+int
+do_ta(int argc, char *argv[])
+{
+       int retcode;
+       int verbose;
+       int i, num_files;
+       anchor_list tas = VEC_INIT;
+       size_t u, num;
+
+       retcode = 0;
+       verbose = 1;
+       num_files = 0;
+       for (i = 0; i < argc; i ++) {
+               const char *arg;
+
+               arg = argv[i];
+               if (arg[0] != '-') {
+                       num_files ++;
+                       continue;
+               }
+               argv[i] = NULL;
+               if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+                       verbose = 1;
+               } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+                       verbose = 0;
+               } else {
+                       fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+                       usage_ta();
+                       goto ta_exit_error;
+               }
+       }
+       if (num_files == 0) {
+               fprintf(stderr, "ERROR: no certificate file provided\n");
+               usage_ta();
+               goto ta_exit_error;
+       }
+
+       for (i = 0; i < argc; i ++) {
+               const char *fname;
+               size_t len1, len2;
+
+               fname = argv[i];
+               if (fname == NULL) {
+                       continue;
+               }
+               if (verbose) {
+                       fprintf(stderr, "Reading file '%s': ", fname);
+                       fflush(stderr);
+               }
+               len1 = VEC_LEN(tas);
+               if (read_trust_anchors(&tas, fname) == 0) {
+                       goto ta_exit_error;
+               }
+               len2 = VEC_LEN(tas) - len1;
+               if (verbose) {
+                       fprintf(stderr, "%lu trust anchor%s\n",
+                               (unsigned long)len2, len2 > 1 ? "s" : "");
+               }
+       }
+       num = VEC_LEN(tas);
+       for (u = 0; u < num; u ++) {
+               if (print_ta_internals(&VEC_ELT(tas, u), u) < 0) {
+                       goto ta_exit_error;
+               }
+       }
+       printf("\nstatic const br_x509_trust_anchor TAs[%ld] = {", (long)num);
+       for (u = 0; u < num; u ++) {
+               if (u != 0) {
+                       printf(",");
+               }
+               printf("\n");
+               print_ta(&VEC_ELT(tas, u), u);
+       }
+       printf("\n};\n");
+       printf("\n#define TAs_NUM   %ld\n", (long)num);
+
+       /*
+        * Release allocated structures.
+        */
+ta_exit:
+       VEC_CLEAREXT(tas, free_ta_contents);
+       return retcode;
+
+ta_exit_error:
+       retcode = -1;
+       goto ta_exit;
+}
diff --git a/tools/vector.c b/tools/vector.c
new file mode 100644 (file)
index 0000000..96df307
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "brssl.h"
+
+/*
+ * Prepare a vector buffer for adding 'extra' elements.
+ *   buf      current buffer
+ *   esize    size of a vector element
+ *   ptr      pointer to the 'ptr' vector field
+ *   len      pointer to the 'len' vector field
+ *   extra    number of elements to add
+ *
+ * If the buffer must be enlarged, then this function allocates the new
+ * buffer and releases the old one. The new buffer address is then returned.
+ * If the buffer needs not be enlarged, then the buffer address is returned.
+ *
+ * In case of enlargement, the 'len' field is adjusted accordingly. The
+ * 'ptr' field is not modified.
+ */
+void *
+vector_expand(void *buf,
+       size_t esize, size_t *ptr, size_t *len, size_t extra)
+{
+       size_t nlen;
+       void *nbuf;
+
+       if (*len - *ptr >= extra) {
+               return buf;
+       }
+       nlen = (*len << 1);
+       if (nlen - *ptr < extra) {
+               nlen = extra + *ptr;
+               if (nlen < 8) {
+                       nlen = 8;
+               }
+       }
+       nbuf = xmalloc(nlen * esize);
+       if (buf != NULL) {
+               memcpy(nbuf, buf, *len * esize);
+               xfree(buf);
+       }
+       *len = nlen;
+       return nbuf;
+}
diff --git a/tools/verify.c b/tools/verify.c
new file mode 100644 (file)
index 0000000..064a61f
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static unsigned
+rsa_bit_length(const br_rsa_public_key *pk)
+{
+       size_t u;
+       unsigned x, bl;
+
+       for (u = 0; u < pk->nlen; u ++) {
+               if (pk->n[u] != 0) {
+                       break;
+               }
+       }
+       if (u == pk->nlen) {
+               return 0;
+       }
+       bl = (unsigned)(pk->nlen - u - 1) << 3;
+       x = pk->n[u];
+       while (x != 0) {
+               bl ++;
+               x >>= 1;
+       }
+       return bl;
+}
+
+static void
+print_rsa(const br_rsa_public_key *pk, int print_text, int print_C)
+{
+       if (print_text) {
+               size_t u;
+
+               printf("n = ");
+               for (u = 0; u < pk->nlen; u ++) {
+                       printf("%02X", pk->n[u]);
+               }
+               printf("\n");
+               printf("e = ");
+               for (u = 0; u < pk->elen; u ++) {
+                       printf("%02X", pk->e[u]);
+               }
+               printf("\n");
+       }
+       if (print_C) {
+               size_t u;
+
+               printf("\nstatic const unsigned char RSA_N[] = {");
+               for (u = 0; u < pk->nlen; u ++) {
+                       if (u != 0) {
+                               printf(",");
+                       }
+                       if (u % 12 == 0) {
+                               printf("\n\t");
+                       } else {
+                               printf(" ");
+                       }
+                       printf("0x%02X", pk->n[u]);
+               }
+               printf("\n};\n");
+               printf("\nstatic const unsigned char RSA_E[] = {");
+               for (u = 0; u < pk->elen; u ++) {
+                       if (u != 0) {
+                               printf(",");
+                       }
+                       if (u % 12 == 0) {
+                               printf("\n\t");
+                       } else {
+                               printf(" ");
+                       }
+                       printf("0x%02X", pk->e[u]);
+               }
+               printf("\n};\n");
+               printf("\nstatic const br_rsa_public_key RSA = {\n");
+               printf("\t(unsigned char *)RSA_N, sizeof RSA_N,\n");
+               printf("\t(unsigned char *)RSA_E, sizeof RSA_E\n");
+               printf("};\n");
+       }
+}
+
+static void
+print_ec(const br_ec_public_key *pk, int print_text, int print_C)
+{
+       if (print_text) {
+               size_t u;
+
+               printf("Q = ");
+               for (u = 0; u < pk->qlen; u ++) {
+                       printf("%02X", pk->q[u]);
+               }
+               printf("\n");
+       }
+       if (print_C) {
+               size_t u;
+
+               printf("\nstatic const unsigned char EC_Q[] = {");
+               for (u = 0; u < pk->qlen; u ++) {
+                       if (u != 0) {
+                               printf(",");
+                       }
+                       if (u % 12 == 0) {
+                               printf("\n\t");
+                       } else {
+                               printf(" ");
+                       }
+                       printf("0x%02X", pk->q[u]);
+               }
+               printf("\n};\n");
+               printf("\nstatic const br_ec_public_key EC = {\n");
+               printf("\t%d,\n", pk->curve);
+               printf("\t(unsigned char *)EC_Q, sizeof EC_Q\n");
+               printf("};\n");
+       }
+}
+
+static void
+usage_verify(void)
+{
+       fprintf(stderr,
+"usage: brssl verify [ options ] file...\n");
+       fprintf(stderr,
+"options:\n");
+       fprintf(stderr,
+"   -q            suppress verbose messages\n");
+       fprintf(stderr,
+"   -sni name     check presence of a specific server name\n");
+       fprintf(stderr,
+"   -CA file      add certificates in 'file' to trust anchors\n");
+       fprintf(stderr,
+"   -text         print public key details (human-readable)\n");
+       fprintf(stderr,
+"   -C            print public key details (C code)\n");
+}
+
+typedef VECTOR(br_x509_certificate) cert_list;
+
+static void
+free_cert_contents(br_x509_certificate *xc)
+{
+       xfree(xc->data);
+}
+
+/* see brssl.h */
+int
+do_verify(int argc, char *argv[])
+{
+       int retcode;
+       int verbose;
+       int i;
+       const char *sni;
+       anchor_list anchors = VEC_INIT;
+       cert_list chain = VEC_INIT;
+       size_t u;
+       br_x509_minimal_context mc;
+       int err_keyx, err_sign;
+       int print_text, print_C;
+       br_x509_pkey *pk;
+
+       retcode = 0;
+       verbose = 1;
+       sni = NULL;
+       print_text = 0;
+       print_C = 0;
+       pk = NULL;
+       err_keyx = 0;
+       err_sign = 0;
+       for (i = 0; i < argc; i ++) {
+               const char *arg;
+
+               arg = argv[i];
+               if (arg[0] != '-') {
+                       br_x509_certificate *xcs;
+                       size_t num;
+
+                       xcs = read_certificates(arg, &num);
+                       if (xcs == NULL) {
+                               usage_verify();
+                               goto verify_exit_error;
+                       }
+                       VEC_ADDMANY(chain, xcs, num);
+                       xfree(xcs);
+                       continue;
+               }
+               if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+                       verbose = 1;
+               } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+                       verbose = 0;
+               } else if (eqstr(arg, "-sni")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-sni'\n");
+                               usage_verify();
+                               goto verify_exit_error;
+                       }
+                       if (sni != NULL) {
+                               fprintf(stderr, "ERROR: duplicate SNI\n");
+                               usage_verify();
+                               goto verify_exit_error;
+                       }
+                       sni = argv[i];
+                       continue;
+               } else if (eqstr(arg, "-CA")) {
+                       if (++ i >= argc) {
+                               fprintf(stderr,
+                                       "ERROR: no argument for '-CA'\n");
+                               usage_verify();
+                               goto verify_exit_error;
+                       }
+                       arg = argv[i];
+                       if (read_trust_anchors(&anchors, arg) == 0) {
+                               usage_verify();
+                               goto verify_exit_error;
+                       }
+                       continue;
+               } else if (eqstr(arg, "-text")) {
+                       print_text = 1;
+               } else if (eqstr(arg, "-C")) {
+                       print_C = 1;
+               } else {
+                       fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+                       usage_verify();
+                       goto verify_exit_error;
+               }
+       }
+       if (VEC_LEN(chain) == 0) {
+               fprintf(stderr, "ERROR: no certificate chain provided\n");
+               usage_verify();
+               goto verify_exit_error;
+       }
+       br_x509_minimal_init(&mc, &br_sha256_vtable,
+               &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+       br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable);
+       br_x509_minimal_set_hash(&mc, br_sha224_ID, &br_sha224_vtable);
+       br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable);
+       br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable);
+       br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable);
+       br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy);
+       br_x509_minimal_set_ecdsa(&mc,
+               &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
+       for (i = 0; i < 2; i ++) {
+               const br_x509_pkey *tpk;
+               int err;
+
+               mc.vtable->start_chain(&mc.vtable,
+                       i == 0 ? BR_KEYTYPE_KEYX : BR_KEYTYPE_SIGN, sni);
+               for (u = 0; u < VEC_LEN(chain); u ++) {
+                       br_x509_certificate *xc;
+
+                       xc = &VEC_ELT(chain, u);
+                       mc.vtable->start_cert(&mc.vtable, xc->data_len);
+                       mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
+                       mc.vtable->end_cert(&mc.vtable);
+               }
+               err = mc.vtable->end_chain(&mc.vtable);
+               if (i == 0) {
+                       err_keyx = err;
+               } else {
+                       err_sign = err;
+               }
+               tpk = mc.vtable->get_pkey(&mc.vtable);
+               if (pk == NULL && tpk != NULL) {
+                       pk = xpkeydup(tpk);
+               }
+       }
+       if (err_keyx == 0 || err_sign == 0) {
+               if (verbose) {
+                       fprintf(stderr, "Validation success");
+                       if (err_keyx == 0 && err_sign == 0) {
+                               fprintf(stderr, " (key exchange, sign)\n");
+                       } else if (err_keyx == 0) {
+                               fprintf(stderr, " (key exchange)\n");
+                       } else if (err_sign == 0) {
+                               fprintf(stderr, " (signature)\n");
+                       }
+               }
+       } else {
+               if (verbose) {
+                       int err;
+                       const char *errname, *errmsg;
+
+                       /*
+                        * If the two error codes differ, we want the one
+                        * which is not a "forbidden key usage".
+                        */
+                       err = err_keyx;
+                       if (err == BR_ERR_X509_FORBIDDEN_KEY_USAGE) {
+                               err = err_sign;
+                       }
+                       fprintf(stderr, "Validation failed, err = %d", err);
+                       errname = find_error_name(err, &errmsg);
+                       if (errname != NULL) {
+                               fprintf(stderr, " (%s): %s\n", errname, errmsg);
+                       } else {
+                               fprintf(stderr, " (unknown)\n");
+                       }
+               }
+               retcode = -1;
+       }
+       if (pk != NULL) {
+               switch (pk->key_type) {
+               case BR_KEYTYPE_RSA:
+                       if (verbose) {
+                               fprintf(stderr, "Key type: RSA (%u bits)\n",
+                                       rsa_bit_length(&pk->key.rsa));
+                       }
+                       print_rsa(&pk->key.rsa, print_text, print_C);
+                       break;
+               case BR_KEYTYPE_EC:
+                       if (verbose) {
+                               fprintf(stderr, "Key type: EC (%s)\n",
+                                       ec_curve_name(pk->key.ec.curve));
+                       }
+                       print_ec(&pk->key.ec, print_text, print_C);
+                       break;
+               default:
+                       if (verbose) {
+                               fprintf(stderr, "Unknown key type\n");
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Release allocated structures.
+        */
+verify_exit:
+       VEC_CLEAREXT(anchors, &free_ta_contents);
+       VEC_CLEAREXT(chain, &free_cert_contents);
+       xfreepkey(pk);
+       return retcode;
+
+verify_exit_error:
+       retcode = -1;
+       goto verify_exit;
+}
diff --git a/tools/xmem.c b/tools/xmem.c
new file mode 100644 (file)
index 0000000..66fca89
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "brssl.h"
+
+/* see brssl.h */
+void *
+xmalloc(size_t len)
+{
+       void *buf;
+
+       if (len == 0) {
+               return NULL;
+       }
+       buf = malloc(len);
+       if (buf == NULL) {
+               fprintf(stderr, "ERROR: could not allocate %lu byte(s)\n",
+                       (unsigned long)len);
+               exit(EXIT_FAILURE);
+       }
+       return buf;
+}
+
+/* see brssl.h */
+void
+xfree(void *buf)
+{
+       if (buf != NULL) {
+               free(buf);
+       }
+}
+
+/* see brssl.h */
+void *
+xblobdup(const void *src, size_t len)
+{
+       void *buf;
+
+       buf = xmalloc(len);
+       memcpy(buf, src, len);
+       return buf;
+}
+
+/* see brssl.h */
+char *
+xstrdup(const void *src)
+{
+       return xblobdup(src, strlen(src) + 1);
+}
+
+/* see brssl.h */
+br_x509_pkey *
+xpkeydup(const br_x509_pkey *pk)
+{
+       br_x509_pkey *pk2;
+
+       pk2 = xmalloc(sizeof *pk2);
+       pk2->key_type = pk->key_type;
+       switch (pk->key_type) {
+       case BR_KEYTYPE_RSA:
+               pk2->key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
+               pk2->key.rsa.nlen = pk->key.rsa.nlen;
+               pk2->key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
+               pk2->key.rsa.elen = pk->key.rsa.elen;
+               break;
+       case BR_KEYTYPE_EC:
+               pk2->key.ec.curve = pk->key.ec.curve;
+               pk2->key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
+               pk2->key.ec.qlen = pk->key.ec.qlen;
+               break;
+       default:
+               fprintf(stderr, "Unknown public key type: %u\n",
+                       (unsigned)pk->key_type);
+               exit(EXIT_FAILURE);
+       }
+       return pk2;
+}
+
+/* see brssl.h */
+void
+xfreepkey(br_x509_pkey *pk)
+{
+       if (pk != NULL) {
+               switch (pk->key_type) {
+               case BR_KEYTYPE_RSA:
+                       xfree(pk->key.rsa.n);
+                       xfree(pk->key.rsa.e);
+                       break;
+               case BR_KEYTYPE_EC:
+                       xfree(pk->key.ec.q);
+                       break;
+               default:
+                       fprintf(stderr, "Unknown public key type: %u\n",
+                               (unsigned)pk->key_type);
+                       exit(EXIT_FAILURE);
+               }
+               xfree(pk);
+       }
+}