From: Thomas Pornin Date: Wed, 2 Nov 2016 23:01:13 +0000 (-0400) Subject: Initial import. X-Git-Tag: v0.4~67 X-Git-Url: https://bearssl.org/gitweb//home/git/?a=commitdiff_plain;h=3210f38e0491b39aec1ef419cb4114e9483089fb;p=BearSSL Initial import. --- 3210f38e0491b39aec1ef419cb4114e9483089fb diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0885020 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2016 Thomas Pornin + +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 index 0000000..5eabd31 --- /dev/null +++ b/Makefile @@ -0,0 +1,606 @@ +# Copyright (c) 2016 Thomas Pornin +# +# 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 index 0000000..e72537d --- /dev/null +++ b/README.txt @@ -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 index 0000000..3bbc8e6 --- /dev/null +++ b/T0/BlobWriter.cs @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..22f1a17 --- /dev/null +++ b/T0/CPU.cs @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5731c5a --- /dev/null +++ b/T0/CodeElement.cs @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5abd7eb --- /dev/null +++ b/T0/CodeElementJump.cs @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e5f3607 --- /dev/null +++ b/T0/CodeElementUInt.cs @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d24ba58 --- /dev/null +++ b/T0/CodeElementUIntExpr.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..022ffb8 --- /dev/null +++ b/T0/CodeElementUIntInt.cs @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3d4ee33 --- /dev/null +++ b/T0/CodeElementUIntUInt.cs @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..6a06b64 --- /dev/null +++ b/T0/ConstData.cs @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..81d1e9d --- /dev/null +++ b/T0/Opcode.cs @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..0980042 --- /dev/null +++ b/T0/OpcodeCall.cs @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..ae75ae5 --- /dev/null +++ b/T0/OpcodeConst.cs @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..59d24fc --- /dev/null +++ b/T0/OpcodeGetLocal.cs @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4f3ec68 --- /dev/null +++ b/T0/OpcodeJump.cs @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d702434 --- /dev/null +++ b/T0/OpcodeJumpIf.cs @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..afbf19d --- /dev/null +++ b/T0/OpcodeJumpIfNot.cs @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e7d8a82 --- /dev/null +++ b/T0/OpcodeJumpUncond.cs @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..b148a65 --- /dev/null +++ b/T0/OpcodePutLocal.cs @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..187473b --- /dev/null +++ b/T0/OpcodeRet.cs @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..80e9f01 --- /dev/null +++ b/T0/SType.cs @@ -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 index 0000000..143badb --- /dev/null +++ b/T0/T0Comp.cs @@ -0,0 +1,2070 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 r = new List(); + string outBase = null; + List entryPoints = new List(); + 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 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 savedWordBuilders; + + /* + * C code defined for words is kept in this map, by word name. + */ + IDictionary 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 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( + StringComparer.Ordinal); + savedWordBuilders = new Stack(); + allCCode = new SortedDictionary( + StringComparer.Ordinal); + compiling = false; + extraCode = new List(); + 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 wordSet = + new SortedDictionary( + StringComparer.Ordinal); + Queue tx = new Queue(); + 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 blocks = + new SortedDictionary(); + 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 slots = new Dictionary(); + 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 ccodeUni = + new Dictionary(); + IDictionary ccodeNames = + new Dictionary(); + 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 gcodeList = new List(); + 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 +#include + +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 nccode = + new SortedDictionary(); + 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 index 0000000..f996d8d --- /dev/null +++ b/T0/TPointerBase.cs @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d4aeff6 --- /dev/null +++ b/T0/TPointerBlob.cs @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..314b272 --- /dev/null +++ b/T0/TPointerExpr.cs @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f8eb11e --- /dev/null +++ b/T0/TPointerNull.cs @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..883f312 --- /dev/null +++ b/T0/TPointerXT.cs @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e4f2255 --- /dev/null +++ b/T0/TValue.cs @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..2dfb66e --- /dev/null +++ b/T0/Word.cs @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 GetReferences() + { + return new List(); + } + + /* + * Get all data blocks directly referenced from this one. This + * implies resolving the word. + */ + internal virtual List GetDataBlocks() + { + return new List(); + } + + /* + * Produce the code elements for this word. + */ + internal virtual void GenerateCodeElements(List 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 index 0000000..c410930 --- /dev/null +++ b/T0/WordBuilder.cs @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 code; + List toResolve; + Dictionary 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(); + toResolve = new List(); + locals = new Dictionary(); + 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 index 0000000..2d74bd3 --- /dev/null +++ b/T0/WordData.cs @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 GetDataBlocks() + { + Resolve(); + List r = new List(); + r.Add(blob); + return r; + } + + internal override void GenerateCodeElements(List 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 index 0000000..882170b --- /dev/null +++ b/T0/WordInterpreted.cs @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 GetReferences() + { + Resolve(); + List r = new List(); + foreach (Opcode op in Code) { + Word w = op.GetReference(TC); + if (w != null) { + r.Add(w); + } + } + return r; + } + + internal override List GetDataBlocks() + { + Resolve(); + List r = new List(); + foreach (Opcode op in Code) { + ConstData cd = op.GetDataBlock(TC); + if (cd != null) { + r.Add(cd); + } + } + return r; + } + + internal override void GenerateCodeElements(List 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 index 0000000..7868872 --- /dev/null +++ b/T0/WordNative.cs @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9fce4f8 --- /dev/null +++ b/T0/kern.t0 @@ -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 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 index 0000000..e69de29 diff --git a/inc/bearssl.h b/inc/bearssl.h new file mode 100644 index 0000000..c11080e --- /dev/null +++ b/inc/bearssl.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +#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 index 0000000..6dc0e5b --- /dev/null +++ b/inc/bearssl_block.h @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +/* + * 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 index 0000000..a655418 --- /dev/null +++ b/inc/bearssl_ec.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +/* + * 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 index 0000000..1684a91 --- /dev/null +++ b/inc/bearssl_hash.h @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include + +/* + * 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 index 0000000..d65653a --- /dev/null +++ b/inc/bearssl_hmac.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +#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 index 0000000..a3872de --- /dev/null +++ b/inc/bearssl_pem.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +/* + * 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 index 0000000..779a75b --- /dev/null +++ b/inc/bearssl_prf.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +/* + * 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 index 0000000..0c3bc4d --- /dev/null +++ b/inc/bearssl_rand.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +/* + * 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 index 0000000..cbda5d3 --- /dev/null +++ b/inc/bearssl_rsa.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +/* + * 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 index 0000000..24d773c --- /dev/null +++ b/inc/bearssl_ssl.h @@ -0,0 +1,1856 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +#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 index 0000000..b35eddd --- /dev/null +++ b/inc/bearssl_x509.h @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +#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 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 index 0000000..77c93c7 --- /dev/null +++ b/samples/README.txt @@ -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 index 0000000..07f5457 --- /dev/null +++ b/samples/cert-ee-ec+rsa.pem @@ -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 index 0000000..ff8ddbc --- /dev/null +++ b/samples/cert-ee-ec.pem @@ -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 index 0000000..ef33e40 --- /dev/null +++ b/samples/cert-ee-rsa.pem @@ -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 index 0000000..204ab76 --- /dev/null +++ b/samples/cert-ica-ec.pem @@ -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 index 0000000..058ffab --- /dev/null +++ b/samples/cert-ica-rsa.pem @@ -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 index 0000000..1bfd578 --- /dev/null +++ b/samples/cert-root-ec.pem @@ -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 index 0000000..6b5ebd0 --- /dev/null +++ b/samples/cert-root-rsa.pem @@ -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 index 0000000..2a27ff5 --- /dev/null +++ b/samples/chain-ec+rsa.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..b4927a3 --- /dev/null +++ b/samples/chain-ec.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f838789 --- /dev/null +++ b/samples/chain-rsa.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d4f79fb --- /dev/null +++ b/samples/client_basic.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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, "", + (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 index 0000000..e88d948 --- /dev/null +++ b/samples/custom_profile.c @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..c9d0fa4 --- /dev/null +++ b/samples/key-ec.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..2e6a136 --- /dev/null +++ b/samples/key-ee-ec.pem @@ -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 index 0000000..e8b22b4 --- /dev/null +++ b/samples/key-ee-rsa.pem @@ -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 index 0000000..7443098 --- /dev/null +++ b/samples/key-ica-ec.pem @@ -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 index 0000000..2e38e34 --- /dev/null +++ b/samples/key-ica-rsa.pem @@ -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 index 0000000..e76db90 --- /dev/null +++ b/samples/key-root-ec.pem @@ -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 index 0000000..82a2eb0 --- /dev/null +++ b/samples/key-root-rsa.pem @@ -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 index 0000000..8de538d --- /dev/null +++ b/samples/key-rsa.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..fdd801e --- /dev/null +++ b/samples/server_basic.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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, "", + (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, "", (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" + "\r\n" + "\r\n" + "

Test!

\r\n" + "\r\n" + "\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 index 0000000..2beace7 --- /dev/null +++ b/src/codec/ccopy.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4f3f7f4 --- /dev/null +++ b/src/codec/dec16be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..84d8536 --- /dev/null +++ b/src/codec/dec16le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5a8fc59 --- /dev/null +++ b/src/codec/dec32be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..ed36e71 --- /dev/null +++ b/src/codec/dec32le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..0c40a76 --- /dev/null +++ b/src/codec/dec64be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cbd02c2 --- /dev/null +++ b/src/codec/dec64le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..6e06652 --- /dev/null +++ b/src/codec/enc16be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3e5049a --- /dev/null +++ b/src/codec/enc16le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..97298b5 --- /dev/null +++ b/src/codec/enc32be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9e9c856 --- /dev/null +++ b/src/codec/enc32le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d548944 --- /dev/null +++ b/src/codec/enc64be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..1f1d68e --- /dev/null +++ b/src/codec/enc64le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..1dbdf95 --- /dev/null +++ b/src/codec/pemdec.c @@ -0,0 +1,508 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include + +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 index 0000000..7a0798c --- /dev/null +++ b/src/codec/pemdec.t0 @@ -0,0 +1,303 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 index 0000000..259f5bb --- /dev/null +++ b/src/config.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..440641e --- /dev/null +++ b/src/ec/ec_prime_i31.c @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..007b6b2 --- /dev/null +++ b/src/ec/ec_prime_i31_secp256r1.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9f92b4f --- /dev/null +++ b/src/ec/ec_prime_i31_secp384r1.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..84d7d54 --- /dev/null +++ b/src/ec/ec_prime_i31_secp521r1.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..a9d6c45 --- /dev/null +++ b/src/ec/ec_secp256r1.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..693d93e --- /dev/null +++ b/src/ec/ec_secp384r1.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..161acd0 --- /dev/null +++ b/src/ec/ec_secp521r1.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..91a1d0c --- /dev/null +++ b/src/ec/ecdsa_atr.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9a8d673 --- /dev/null +++ b/src/ec/ecdsa_i31_bits.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cf0d351 --- /dev/null +++ b/src/ec/ecdsa_i31_sign_asn1.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3849545 --- /dev/null +++ b/src/ec/ecdsa_i31_sign_raw.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e98b779 --- /dev/null +++ b/src/ec/ecdsa_i31_vrfy_asn1.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..54dcfc2 --- /dev/null +++ b/src/ec/ecdsa_i31_vrfy_raw.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..71f0d39 --- /dev/null +++ b/src/ec/ecdsa_rta.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..496f230 --- /dev/null +++ b/src/hash/dig_oid.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4625d2c --- /dev/null +++ b/src/hash/dig_size.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f76f6e9 --- /dev/null +++ b/src/hash/ghash_ctmul.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d3380d4 --- /dev/null +++ b/src/hash/ghash_ctmul32.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..7819d7f --- /dev/null +++ b/src/hash/ghash_ctmul64.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..0df7abe --- /dev/null +++ b/src/hash/md5.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f701aee --- /dev/null +++ b/src/hash/md5sha1.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..b6df2e0 --- /dev/null +++ b/src/hash/multihash.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4f65d84 --- /dev/null +++ b/src/hash/sha1.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5be92ed --- /dev/null +++ b/src/hash/sha2big.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..ca19655 --- /dev/null +++ b/src/hash/sha2small.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9edaadd --- /dev/null +++ b/src/inner.h @@ -0,0 +1,1546 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include + +#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 index 0000000..2ca47c6 --- /dev/null +++ b/src/int/i31_add.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3e127c2 --- /dev/null +++ b/src/int/i31_bitlen.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..745bc10 --- /dev/null +++ b/src/int/i31_decmod.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..8ec6d90 --- /dev/null +++ b/src/int/i31_decode.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..43db662 --- /dev/null +++ b/src/int/i31_decred.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..b6b40c4 --- /dev/null +++ b/src/int/i31_encode.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4e14361 --- /dev/null +++ b/src/int/i31_fmont.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..8a7ea44 --- /dev/null +++ b/src/int/i31_iszero.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4ef3f5d --- /dev/null +++ b/src/int/i31_modpow.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..0857797 --- /dev/null +++ b/src/int/i31_montmul.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..04a42c7 --- /dev/null +++ b/src/int/i31_mulacc.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3c52077 --- /dev/null +++ b/src/int/i31_muladd.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..dd83c96 --- /dev/null +++ b/src/int/i31_ninv31.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5c9523e --- /dev/null +++ b/src/int/i31_reduce.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..db6ba0b --- /dev/null +++ b/src/int/i31_rshift.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3910895 --- /dev/null +++ b/src/int/i31_sub.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4798ff6 --- /dev/null +++ b/src/int/i31_tmont.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..620baff --- /dev/null +++ b/src/int/i32_add.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..40ce9fa --- /dev/null +++ b/src/int/i32_bitlen.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..a859af1 --- /dev/null +++ b/src/int/i32_decmod.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f289038 --- /dev/null +++ b/src/int/i32_decode.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..dc476db --- /dev/null +++ b/src/int/i32_decred.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..276ddfe --- /dev/null +++ b/src/int/i32_div32.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..303652f --- /dev/null +++ b/src/int/i32_encode.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..dc1c934 --- /dev/null +++ b/src/int/i32_fmont.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..659df7f --- /dev/null +++ b/src/int/i32_iszero.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..034aba0 --- /dev/null +++ b/src/int/i32_modpow.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..7edb376 --- /dev/null +++ b/src/int/i32_montmul.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f62c782 --- /dev/null +++ b/src/int/i32_mulacc.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..dd526ad --- /dev/null +++ b/src/int/i32_muladd.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..6564434 --- /dev/null +++ b/src/int/i32_ninv32.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..90fff09 --- /dev/null +++ b/src/int/i32_reduce.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9c50023 --- /dev/null +++ b/src/int/i32_sub.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..058cd88 --- /dev/null +++ b/src/int/i32_tmont.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..765e454 --- /dev/null +++ b/src/mac/hmac.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d3ab425 --- /dev/null +++ b/src/mac/hmac_ct.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d746756 --- /dev/null +++ b/src/rand/hmac_drbg.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..431a119 --- /dev/null +++ b/src/rsa/rsa_i31_pkcs1_sign.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..c3ffb62 --- /dev/null +++ b/src/rsa/rsa_i31_pkcs1_vrfy.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..efff6ae --- /dev/null +++ b/src/rsa/rsa_i31_priv.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..18e069f --- /dev/null +++ b/src/rsa/rsa_i31_pub.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d6d64d0 --- /dev/null +++ b/src/rsa/rsa_i32_pkcs1_sign.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cc20ba8 --- /dev/null +++ b/src/rsa/rsa_i32_pkcs1_vrfy.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3c08d00 --- /dev/null +++ b/src/rsa/rsa_i32_priv.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..6e8d8e3 --- /dev/null +++ b/src/rsa/rsa_i32_pub.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..047eb18 --- /dev/null +++ b/src/rsa/rsa_ssl_decrypt.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..43a74c3 --- /dev/null +++ b/src/ssl/prf.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..414dfed --- /dev/null +++ b/src/ssl/prf_md5sha1.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cec916a --- /dev/null +++ b/src/ssl/prf_sha256.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5069560 --- /dev/null +++ b/src/ssl/prf_sha384.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..28c404b --- /dev/null +++ b/src/ssl/ssl_client.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..ad1f0cc --- /dev/null +++ b/src/ssl/ssl_client_full.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..8af773d --- /dev/null +++ b/src/ssl/ssl_engine.c @@ -0,0 +1,1468 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#endif + +#if BR_USE_WIN32_RAND +#include +#include +#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 index 0000000..e10a980 --- /dev/null +++ b/src/ssl/ssl_hashes.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..16c708a --- /dev/null +++ b/src/ssl/ssl_hs_client.c @@ -0,0 +1,1473 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include + +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 +#include + +#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 index 0000000..e2a76f7 --- /dev/null +++ b/src/ssl/ssl_hs_client.t0 @@ -0,0 +1,992 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 index 0000000..a842b29 --- /dev/null +++ b/src/ssl/ssl_hs_common.t0 @@ -0,0 +1,1012 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 +#include + +#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 index 0000000..060e2fb --- /dev/null +++ b/src/ssl/ssl_hs_server.c @@ -0,0 +1,1479 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include + +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 +#include + +#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 index 0000000..5069a63 --- /dev/null +++ b/src/ssl/ssl_hs_server.t0 @@ -0,0 +1,1013 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 index 0000000..b409636 --- /dev/null +++ b/src/ssl/ssl_io.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..b29b3ed --- /dev/null +++ b/src/ssl/ssl_lru.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..c080604 --- /dev/null +++ b/src/ssl/ssl_rec_cbc.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cfbccec --- /dev/null +++ b/src/ssl/ssl_rec_gcm.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5578b63 --- /dev/null +++ b/src/ssl/ssl_server.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..eed4002 --- /dev/null +++ b/src/ssl/ssl_server_full_ec.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..21be99d --- /dev/null +++ b/src/ssl/ssl_server_full_rsa.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..5de566e --- /dev/null +++ b/src/ssl/ssl_server_mine2g.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..257aff2 --- /dev/null +++ b/src/ssl/ssl_server_minf2g.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..05ad891 --- /dev/null +++ b/src/ssl/ssl_server_minr2g.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e4b31ed --- /dev/null +++ b/src/ssl/ssl_server_minu2g.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cd29b8e --- /dev/null +++ b/src/ssl/ssl_server_minv2g.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9dd0238 --- /dev/null +++ b/src/ssl/ssl_single_ec.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e174d91 --- /dev/null +++ b/src/ssl/ssl_single_rsa.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d969a3b --- /dev/null +++ b/src/symcipher/aes_big_cbcdec.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..265e53b --- /dev/null +++ b/src/symcipher/aes_big_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..18fbb84 --- /dev/null +++ b/src/symcipher/aes_big_ctr.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..a5d0e3c --- /dev/null +++ b/src/symcipher/aes_big_dec.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..bbabb9a --- /dev/null +++ b/src/symcipher/aes_big_enc.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..72c64fb --- /dev/null +++ b/src/symcipher/aes_common.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..66776d9 --- /dev/null +++ b/src/symcipher/aes_ct.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..981e63d --- /dev/null +++ b/src/symcipher/aes_ct64.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..814dce7 --- /dev/null +++ b/src/symcipher/aes_ct64_cbcdec.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e320614 --- /dev/null +++ b/src/symcipher/aes_ct64_cbcenc.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..6d5a566 --- /dev/null +++ b/src/symcipher/aes_ct64_ctr.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..ab00e09 --- /dev/null +++ b/src/symcipher/aes_ct64_dec.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..78631ce --- /dev/null +++ b/src/symcipher/aes_ct64_enc.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..522645a --- /dev/null +++ b/src/symcipher/aes_ct_cbcdec.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..cb85977 --- /dev/null +++ b/src/symcipher/aes_ct_cbcenc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..f407689 --- /dev/null +++ b/src/symcipher/aes_ct_ctr.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..7f32d2b --- /dev/null +++ b/src/symcipher/aes_ct_dec.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..089bf35 --- /dev/null +++ b/src/symcipher/aes_ct_enc.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..8567244 --- /dev/null +++ b/src/symcipher/aes_small_cbcdec.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..0dc2910 --- /dev/null +++ b/src/symcipher/aes_small_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d5d371c --- /dev/null +++ b/src/symcipher/aes_small_ctr.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..59dca8e --- /dev/null +++ b/src/symcipher/aes_small_dec.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..29f48a8 --- /dev/null +++ b/src/symcipher/aes_small_enc.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..581c0ab --- /dev/null +++ b/src/symcipher/des_ct.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..d208a3d --- /dev/null +++ b/src/symcipher/des_ct_cbcdec.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..4b3610e --- /dev/null +++ b/src/symcipher/des_ct_cbcenc.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..37f6db3 --- /dev/null +++ b/src/symcipher/des_support.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3f8e4f9 --- /dev/null +++ b/src/symcipher/des_tab.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..e7eabe9 --- /dev/null +++ b/src/symcipher/des_tab_cbcdec.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..3a45ba3 --- /dev/null +++ b/src/symcipher/des_tab_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..9d812df --- /dev/null +++ b/src/x509/asn1.t0 @@ -0,0 +1,579 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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("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 index 0000000..6788145 --- /dev/null +++ b/src/x509/skey_decoder.c @@ -0,0 +1,646 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include + +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 index 0000000..336b932 --- /dev/null +++ b/src/x509/skey_decoder.t0 @@ -0,0 +1,373 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 index 0000000..d81bba2 --- /dev/null +++ b/src/x509/x509_decoder.c @@ -0,0 +1,769 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include + +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 index 0000000..1b6089b --- /dev/null +++ b/src/x509/x509_decoder.t0 @@ -0,0 +1,321 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 index 0000000..f00c32b --- /dev/null +++ b/src/x509/x509_knownkey.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..55d9e23 --- /dev/null +++ b/src/x509/x509_minimal.c @@ -0,0 +1,1551 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include + +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 +#endif + +#if BR_USE_WIN32_TIME +#include +#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 index 0000000..bdb3e18 --- /dev/null +++ b/src/x509/x509_minimal.t0 @@ -0,0 +1,1294 @@ +\ Copyright (c) 2016 Thomas Pornin +\ +\ 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 +#endif + +#if BR_USE_WIN32_TIME +#include +#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 index 0000000..ce0f64f --- /dev/null +++ b/test/test_crypto.c @@ -0,0 +1,5155 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#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 index 0000000..b36f9f7 --- /dev/null +++ b/test/test_math.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#include + +#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 index 0000000..b4049fe --- /dev/null +++ b/test/test_speed.c @@ -0,0 +1,1098 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#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 index 0000000..a591b46 --- /dev/null +++ b/test/test_x509.c @@ -0,0 +1,1634 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include + +#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 index 0000000..e92de8b --- /dev/null +++ b/test/x509/alltests.txt @@ -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 index 0000000..ca1c9ed --- /dev/null +++ b/test/x509/dn-ee.der @@ -0,0 +1 @@ +0'1 0 UCA10Uwww.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 index 0000000..3b0103f --- /dev/null +++ b/test/x509/dn-ica1.der @@ -0,0 +1 @@ +011 0 UCA1"0 UExample 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 index 0000000..d8cddad --- /dev/null +++ b/test/x509/dn-ica2.der @@ -0,0 +1 @@ +011 0 UCA1"0 UExample 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 index 0000000..ea891f9 --- /dev/null +++ b/test/x509/dn-root.der @@ -0,0 +1 @@ +0$1 0 UCA10U Example 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 index 0000000..982dc18 --- /dev/null +++ b/tools/brssl.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..99ce38d --- /dev/null +++ b/tools/brssl.h @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include + +#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 index 0000000..fdee5c6 --- /dev/null +++ b/tools/certs.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..671f5e8 --- /dev/null +++ b/tools/chain.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..3b462a8 --- /dev/null +++ b/tools/client.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "", + (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 index 0000000..e9c6dfa --- /dev/null +++ b/tools/errors.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..07af6c4 --- /dev/null +++ b/tools/files.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..278a3b2 --- /dev/null +++ b/tools/keys.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..dcc5b17 --- /dev/null +++ b/tools/names.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..fc16692 --- /dev/null +++ b/tools/server.c @@ -0,0 +1,1053 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "", + (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, "", + (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 index 0000000..a9ecb32 --- /dev/null +++ b/tools/skey.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..64d6c96 --- /dev/null +++ b/tools/sslio.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..a29aae0 --- /dev/null +++ b/tools/ta.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..96df307 --- /dev/null +++ b/tools/vector.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 index 0000000..064a61f --- /dev/null +++ b/tools/verify.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +#include +#include +#include +#include + +#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 index 0000000..66fca89 --- /dev/null +++ b/tools/xmem.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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); + } +}