From 3210f38e0491b39aec1ef419cb4114e9483089fb Mon Sep 17 00:00:00 2001 From: Thomas Pornin Date: Wed, 2 Nov 2016 19:01:13 -0400 Subject: [PATCH] Initial import. --- LICENSE.txt | 21 + Makefile | 606 ++++ README.txt | 169 + T0/BlobWriter.cs | 112 + T0/CPU.cs | 181 ++ T0/CodeElement.cs | 88 + T0/CodeElementJump.cs | 78 + T0/CodeElementUInt.cs | 46 + T0/CodeElementUIntExpr.cs | 56 + T0/CodeElementUIntInt.cs | 51 + T0/CodeElementUIntUInt.cs | 50 + T0/ConstData.cs | 198 ++ T0/Opcode.cs | 117 + T0/OpcodeCall.cs | 71 + T0/OpcodeConst.cs | 95 + T0/OpcodeGetLocal.cs | 57 + T0/OpcodeJump.cs | 64 + T0/OpcodeJumpIf.cs | 65 + T0/OpcodeJumpIfNot.cs | 65 + T0/OpcodeJumpUncond.cs | 61 + T0/OpcodePutLocal.cs | 57 + T0/OpcodeRet.cs | 50 + T0/SType.cs | 129 + T0/T0Comp.cs | 2070 ++++++++++++ T0/TPointerBase.cs | 64 + T0/TPointerBlob.cs | 75 + T0/TPointerExpr.cs | 97 + T0/TPointerNull.cs | 44 + T0/TPointerXT.cs | 73 + T0/TValue.cs | 231 ++ T0/Word.cs | 172 + T0/WordBuilder.cs | 385 +++ T0/WordData.cs | 96 + T0/WordInterpreted.cs | 283 ++ T0/WordNative.cs | 59 + T0/kern.t0 | 309 ++ T0Comp.exe | Bin 0 -> 71680 bytes build/.do_not_remove | 0 inc/bearssl.h | 42 + inc/bearssl_block.h | 476 +++ inc/bearssl_ec.h | 220 ++ inc/bearssl_hash.h | 424 +++ inc/bearssl_hmac.h | 148 + inc/bearssl_pem.h | 125 + inc/bearssl_prf.h | 66 + inc/bearssl_rand.h | 149 + inc/bearssl_rsa.h | 227 ++ inc/bearssl_ssl.h | 1856 +++++++++++ inc/bearssl_x509.h | 733 +++++ mkT0.sh | 11 + samples/README.txt | 36 + samples/cert-ee-ec+rsa.pem | 14 + samples/cert-ee-ec.pem | 10 + samples/cert-ee-rsa.pem | 17 + samples/cert-ica-ec.pem | 10 + samples/cert-ica-rsa.pem | 17 + samples/cert-root-ec.pem | 9 + samples/cert-root-rsa.pem | 16 + samples/chain-ec+rsa.h | 166 + samples/chain-ec.h | 117 + samples/chain-rsa.h | 183 ++ samples/client_basic.c | 374 +++ samples/custom_profile.c | 529 +++ samples/key-ec.h | 40 + samples/key-ee-ec.pem | 5 + samples/key-ee-rsa.pem | 23 + samples/key-ica-ec.pem | 5 + samples/key-ica-rsa.pem | 23 + samples/key-root-ec.pem | 5 + samples/key-root-rsa.pem | 23 + samples/key-rsa.h | 108 + samples/server_basic.c | 402 +++ src/codec/ccopy.c | 44 + src/codec/dec16be.c | 38 + src/codec/dec16le.c | 38 + src/codec/dec32be.c | 38 + src/codec/dec32le.c | 38 + src/codec/dec64be.c | 38 + src/codec/dec64le.c | 38 + src/codec/enc16be.c | 38 + src/codec/enc16le.c | 38 + src/codec/enc32be.c | 38 + src/codec/enc32le.c | 38 + src/codec/enc64be.c | 38 + src/codec/enc64le.c | 38 + src/codec/pemdec.c | 508 +++ src/codec/pemdec.t0 | 303 ++ src/config.h | 102 + src/ec/ec_prime_i31.c | 791 +++++ src/ec/ec_prime_i31_secp256r1.c | 46 + src/ec/ec_prime_i31_secp384r1.c | 48 + src/ec/ec_prime_i31_secp521r1.c | 50 + src/ec/ec_secp256r1.c | 51 + src/ec/ec_secp384r1.c | 57 + src/ec/ec_secp521r1.c | 64 + src/ec/ecdsa_atr.c | 102 + src/ec/ecdsa_i31_bits.c | 47 + src/ec/ecdsa_i31_sign_asn1.c | 45 + src/ec/ecdsa_i31_sign_raw.c | 176 + src/ec/ecdsa_i31_vrfy_asn1.c | 44 + src/ec/ecdsa_i31_vrfy_raw.c | 158 + src/ec/ecdsa_rta.c | 94 + src/hash/dig_oid.c | 78 + src/hash/dig_size.c | 50 + src/hash/ghash_ctmul.c | 311 ++ src/hash/ghash_ctmul32.c | 244 ++ src/hash/ghash_ctmul64.c | 154 + src/hash/md5.c | 208 ++ src/hash/md5sha1.c | 141 + src/hash/multihash.c | 166 + src/hash/sha1.c | 191 ++ src/hash/sha2big.c | 285 ++ src/hash/sha2small.c | 341 ++ src/inner.h | 1546 +++++++++ src/int/i31_add.c | 46 + src/int/i31_bitlen.c | 44 + src/int/i31_decmod.c | 124 + src/int/i31_decode.c | 57 + src/int/i31_decred.c | 103 + src/int/i31_encode.c | 79 + src/int/i31_fmont.c | 60 + src/int/i31_iszero.c | 39 + src/int/i31_modpow.c | 65 + src/int/i31_montmul.c | 93 + src/int/i31_mulacc.c | 52 + src/int/i31_muladd.c | 157 + src/int/i31_ninv31.c | 39 + src/int/i31_reduce.c | 66 + src/int/i31_rshift.c | 47 + src/int/i31_sub.c | 46 + src/int/i31_tmont.c | 36 + src/int/i32_add.c | 51 + src/int/i32_bitlen.c | 44 + src/int/i32_decmod.c | 77 + src/int/i32_decode.c | 63 + src/int/i32_decred.c | 107 + src/int/i32_div32.c | 56 + src/int/i32_encode.c | 72 + src/int/i32_fmont.c | 60 + src/int/i32_iszero.c | 39 + src/int/i32_modpow.c | 65 + src/int/i32_montmul.c | 69 + src/int/i32_mulacc.c | 52 + src/int/i32_muladd.c | 138 + src/int/i32_ninv32.c | 39 + src/int/i32_reduce.c | 66 + src/int/i32_sub.c | 51 + src/int/i32_tmont.c | 36 + src/mac/hmac.c | 122 + src/mac/hmac_ct.c | 192 ++ src/rand/hmac_drbg.c | 157 + src/rsa/rsa_i31_pkcs1_sign.c | 104 + src/rsa/rsa_i31_pkcs1_vrfy.c | 126 + src/rsa/rsa_i31_priv.c | 146 + src/rsa/rsa_i31_pub.c | 77 + src/rsa/rsa_i32_pkcs1_sign.c | 102 + src/rsa/rsa_i32_pkcs1_vrfy.c | 126 + src/rsa/rsa_i32_priv.c | 153 + src/rsa/rsa_i32_pub.c | 77 + src/rsa/rsa_ssl_decrypt.c | 52 + src/ssl/prf.c | 71 + src/ssl/prf_md5sha1.c | 43 + src/ssl/prf_sha256.c | 36 + src/ssl/prf_sha384.c | 36 + src/ssl/ssl_client.c | 78 + src/ssl/ssl_client_full.c | 194 ++ src/ssl/ssl_engine.c | 1468 +++++++++ src/ssl/ssl_hashes.c | 46 + src/ssl/ssl_hs_client.c | 1473 +++++++++ src/ssl/ssl_hs_client.t0 | 992 ++++++ src/ssl/ssl_hs_common.t0 | 1012 ++++++ src/ssl/ssl_hs_server.c | 1479 +++++++++ src/ssl/ssl_hs_server.t0 | 1013 ++++++ src/ssl/ssl_io.c | 255 ++ src/ssl/ssl_lru.c | 424 +++ src/ssl/ssl_rec_cbc.c | 440 +++ src/ssl/ssl_rec_gcm.c | 235 ++ src/ssl/ssl_server.c | 52 + src/ssl/ssl_server_full_ec.c | 165 + src/ssl/ssl_server_full_rsa.c | 154 + src/ssl/ssl_server_mine2g.c | 92 + src/ssl/ssl_server_minf2g.c | 92 + src/ssl/ssl_server_minr2g.c | 91 + src/ssl/ssl_server_minu2g.c | 91 + src/ssl/ssl_server_minv2g.c | 91 + src/ssl/ssl_single_ec.c | 132 + src/ssl/ssl_single_rsa.c | 155 + src/symcipher/aes_big_cbcdec.c | 69 + src/symcipher/aes_big_cbcenc.c | 67 + src/symcipher/aes_big_ctr.c | 84 + src/symcipher/aes_big_dec.c | 254 ++ src/symcipher/aes_big_enc.c | 157 + src/symcipher/aes_common.c | 112 + src/symcipher/aes_ct.c | 328 ++ src/symcipher/aes_ct64.c | 398 +++ src/symcipher/aes_ct64_cbcdec.c | 104 + src/symcipher/aes_ct64_cbcenc.c | 81 + src/symcipher/aes_ct64_ctr.c | 114 + src/symcipher/aes_ct64_dec.c | 159 + src/symcipher/aes_ct64_enc.c | 115 + src/symcipher/aes_ct_cbcdec.c | 111 + src/symcipher/aes_ct_cbcenc.c | 91 + src/symcipher/aes_ct_ctr.c | 116 + src/symcipher/aes_ct_dec.c | 170 + src/symcipher/aes_ct_enc.c | 112 + src/symcipher/aes_small_cbcdec.c | 69 + src/symcipher/aes_small_cbcenc.c | 67 + src/symcipher/aes_small_ctr.c | 84 + src/symcipher/aes_small_dec.c | 176 + src/symcipher/aes_small_enc.c | 129 + src/symcipher/des_ct.c | 411 +++ src/symcipher/des_ct_cbcdec.c | 87 + src/symcipher/des_ct_cbcenc.c | 69 + src/symcipher/des_support.c | 166 + src/symcipher/des_tab.c | 310 ++ src/symcipher/des_tab_cbcdec.c | 85 + src/symcipher/des_tab_cbcenc.c | 67 + src/x509/asn1.t0 | 579 ++++ src/x509/skey_decoder.c | 646 ++++ src/x509/skey_decoder.t0 | 373 +++ src/x509/x509_decoder.c | 769 +++++ src/x509/x509_decoder.t0 | 321 ++ src/x509/x509_knownkey.c | 99 + src/x509/x509_minimal.c | 1551 +++++++++ src/x509/x509_minimal.t0 | 1294 ++++++++ test/test_crypto.c | 5155 ++++++++++++++++++++++++++++++ test/test_math.c | 482 +++ test/test_speed.c | 1098 +++++++ test/test_x509.c | 1634 ++++++++++ test/x509/alltests.txt | 660 ++++ test/x509/dn-ee.der | 1 + test/x509/dn-ica1.der | 1 + test/x509/dn-ica2.der | 1 + test/x509/dn-root.der | 1 + test/x509/ee-badsig1.crt | Bin 0 -> 870 bytes test/x509/ee-badsig2.crt | Bin 0 -> 870 bytes test/x509/ee-dates.crt | Bin 0 -> 872 bytes test/x509/ee-md5.crt | Bin 0 -> 870 bytes test/x509/ee-names.crt | Bin 0 -> 892 bytes test/x509/ee-names2.crt | Bin 0 -> 842 bytes test/x509/ee-names3.crt | Bin 0 -> 843 bytes test/x509/ee-names4.crt | Bin 0 -> 938 bytes test/x509/ee-p256-sha1.crt | Bin 0 -> 472 bytes test/x509/ee-p256-sha224.crt | Bin 0 -> 473 bytes test/x509/ee-p256-sha256.crt | Bin 0 -> 474 bytes test/x509/ee-p256-sha384.crt | Bin 0 -> 475 bytes test/x509/ee-p256-sha512.crt | Bin 0 -> 474 bytes test/x509/ee-p256.crt | Bin 0 -> 474 bytes test/x509/ee-p384.crt | Bin 0 -> 534 bytes test/x509/ee-p521.crt | Bin 0 -> 610 bytes test/x509/ee-sha1.crt | Bin 0 -> 870 bytes test/x509/ee-sha224.crt | Bin 0 -> 870 bytes test/x509/ee-sha384.crt | Bin 0 -> 870 bytes test/x509/ee-sha512.crt | Bin 0 -> 870 bytes test/x509/ee-trailing.crt | Bin 0 -> 871 bytes test/x509/ee.crt | Bin 0 -> 870 bytes test/x509/ica1-1016.crt | Bin 0 -> 709 bytes test/x509/ica1-1017.crt | Bin 0 -> 709 bytes test/x509/ica1-4096.crt | Bin 0 -> 1098 bytes test/x509/ica1-p256.crt | Bin 0 -> 446 bytes test/x509/ica1-p384.crt | Bin 0 -> 507 bytes test/x509/ica1-p521.crt | Bin 0 -> 581 bytes test/x509/ica1.crt | Bin 0 -> 842 bytes test/x509/ica2-1016.crt | Bin 0 -> 725 bytes test/x509/ica2-1017.crt | Bin 0 -> 726 bytes test/x509/ica2-4096.crt | Bin 0 -> 1111 bytes test/x509/ica2-notCA.crt | Bin 0 -> 852 bytes test/x509/ica2-p256.crt | Bin 0 -> 460 bytes test/x509/ica2-p384.crt | Bin 0 -> 520 bytes test/x509/ica2-p521.crt | Bin 0 -> 595 bytes test/x509/ica2.crt | Bin 0 -> 855 bytes test/x509/junk.crt | Bin 0 -> 800 bytes test/x509/root-p256.crt | Bin 0 -> 413 bytes test/x509/root-p384.crt | Bin 0 -> 475 bytes test/x509/root-p521.crt | Bin 0 -> 549 bytes test/x509/root.crt | Bin 0 -> 810 bytes tools/brssl.c | 87 + tools/brssl.h | 465 +++ tools/certs.c | 218 ++ tools/chain.c | 154 + tools/client.c | 611 ++++ tools/errors.c | 333 ++ tools/files.c | 298 ++ tools/keys.c | 170 + tools/names.c | 600 ++++ tools/server.c | 1053 ++++++ tools/skey.c | 298 ++ tools/sslio.c | 372 +++ tools/ta.c | 253 ++ tools/vector.c | 66 + tools/verify.c | 364 +++ tools/xmem.c | 120 + 292 files changed, 58526 insertions(+) create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.txt create mode 100644 T0/BlobWriter.cs create mode 100644 T0/CPU.cs create mode 100644 T0/CodeElement.cs create mode 100644 T0/CodeElementJump.cs create mode 100644 T0/CodeElementUInt.cs create mode 100644 T0/CodeElementUIntExpr.cs create mode 100644 T0/CodeElementUIntInt.cs create mode 100644 T0/CodeElementUIntUInt.cs create mode 100644 T0/ConstData.cs create mode 100644 T0/Opcode.cs create mode 100644 T0/OpcodeCall.cs create mode 100644 T0/OpcodeConst.cs create mode 100644 T0/OpcodeGetLocal.cs create mode 100644 T0/OpcodeJump.cs create mode 100644 T0/OpcodeJumpIf.cs create mode 100644 T0/OpcodeJumpIfNot.cs create mode 100644 T0/OpcodeJumpUncond.cs create mode 100644 T0/OpcodePutLocal.cs create mode 100644 T0/OpcodeRet.cs create mode 100644 T0/SType.cs create mode 100644 T0/T0Comp.cs create mode 100644 T0/TPointerBase.cs create mode 100644 T0/TPointerBlob.cs create mode 100644 T0/TPointerExpr.cs create mode 100644 T0/TPointerNull.cs create mode 100644 T0/TPointerXT.cs create mode 100644 T0/TValue.cs create mode 100644 T0/Word.cs create mode 100644 T0/WordBuilder.cs create mode 100644 T0/WordData.cs create mode 100644 T0/WordInterpreted.cs create mode 100644 T0/WordNative.cs create mode 100644 T0/kern.t0 create mode 100755 T0Comp.exe create mode 100644 build/.do_not_remove create mode 100644 inc/bearssl.h create mode 100644 inc/bearssl_block.h create mode 100644 inc/bearssl_ec.h create mode 100644 inc/bearssl_hash.h create mode 100644 inc/bearssl_hmac.h create mode 100644 inc/bearssl_pem.h create mode 100644 inc/bearssl_prf.h create mode 100644 inc/bearssl_rand.h create mode 100644 inc/bearssl_rsa.h create mode 100644 inc/bearssl_ssl.h create mode 100644 inc/bearssl_x509.h create mode 100755 mkT0.sh create mode 100644 samples/README.txt create mode 100644 samples/cert-ee-ec+rsa.pem create mode 100644 samples/cert-ee-ec.pem create mode 100644 samples/cert-ee-rsa.pem create mode 100644 samples/cert-ica-ec.pem create mode 100644 samples/cert-ica-rsa.pem create mode 100644 samples/cert-root-ec.pem create mode 100644 samples/cert-root-rsa.pem create mode 100644 samples/chain-ec+rsa.h create mode 100644 samples/chain-ec.h create mode 100644 samples/chain-rsa.h create mode 100644 samples/client_basic.c create mode 100644 samples/custom_profile.c create mode 100644 samples/key-ec.h create mode 100644 samples/key-ee-ec.pem create mode 100644 samples/key-ee-rsa.pem create mode 100644 samples/key-ica-ec.pem create mode 100644 samples/key-ica-rsa.pem create mode 100644 samples/key-root-ec.pem create mode 100644 samples/key-root-rsa.pem create mode 100644 samples/key-rsa.h create mode 100644 samples/server_basic.c create mode 100644 src/codec/ccopy.c create mode 100644 src/codec/dec16be.c create mode 100644 src/codec/dec16le.c create mode 100644 src/codec/dec32be.c create mode 100644 src/codec/dec32le.c create mode 100644 src/codec/dec64be.c create mode 100644 src/codec/dec64le.c create mode 100644 src/codec/enc16be.c create mode 100644 src/codec/enc16le.c create mode 100644 src/codec/enc32be.c create mode 100644 src/codec/enc32le.c create mode 100644 src/codec/enc64be.c create mode 100644 src/codec/enc64le.c create mode 100644 src/codec/pemdec.c create mode 100644 src/codec/pemdec.t0 create mode 100644 src/config.h create mode 100644 src/ec/ec_prime_i31.c create mode 100644 src/ec/ec_prime_i31_secp256r1.c create mode 100644 src/ec/ec_prime_i31_secp384r1.c create mode 100644 src/ec/ec_prime_i31_secp521r1.c create mode 100644 src/ec/ec_secp256r1.c create mode 100644 src/ec/ec_secp384r1.c create mode 100644 src/ec/ec_secp521r1.c create mode 100644 src/ec/ecdsa_atr.c create mode 100644 src/ec/ecdsa_i31_bits.c create mode 100644 src/ec/ecdsa_i31_sign_asn1.c create mode 100644 src/ec/ecdsa_i31_sign_raw.c create mode 100644 src/ec/ecdsa_i31_vrfy_asn1.c create mode 100644 src/ec/ecdsa_i31_vrfy_raw.c create mode 100644 src/ec/ecdsa_rta.c create mode 100644 src/hash/dig_oid.c create mode 100644 src/hash/dig_size.c create mode 100644 src/hash/ghash_ctmul.c create mode 100644 src/hash/ghash_ctmul32.c create mode 100644 src/hash/ghash_ctmul64.c create mode 100644 src/hash/md5.c create mode 100644 src/hash/md5sha1.c create mode 100644 src/hash/multihash.c create mode 100644 src/hash/sha1.c create mode 100644 src/hash/sha2big.c create mode 100644 src/hash/sha2small.c create mode 100644 src/inner.h create mode 100644 src/int/i31_add.c create mode 100644 src/int/i31_bitlen.c create mode 100644 src/int/i31_decmod.c create mode 100644 src/int/i31_decode.c create mode 100644 src/int/i31_decred.c create mode 100644 src/int/i31_encode.c create mode 100644 src/int/i31_fmont.c create mode 100644 src/int/i31_iszero.c create mode 100644 src/int/i31_modpow.c create mode 100644 src/int/i31_montmul.c create mode 100644 src/int/i31_mulacc.c create mode 100644 src/int/i31_muladd.c create mode 100644 src/int/i31_ninv31.c create mode 100644 src/int/i31_reduce.c create mode 100644 src/int/i31_rshift.c create mode 100644 src/int/i31_sub.c create mode 100644 src/int/i31_tmont.c create mode 100644 src/int/i32_add.c create mode 100644 src/int/i32_bitlen.c create mode 100644 src/int/i32_decmod.c create mode 100644 src/int/i32_decode.c create mode 100644 src/int/i32_decred.c create mode 100644 src/int/i32_div32.c create mode 100644 src/int/i32_encode.c create mode 100644 src/int/i32_fmont.c create mode 100644 src/int/i32_iszero.c create mode 100644 src/int/i32_modpow.c create mode 100644 src/int/i32_montmul.c create mode 100644 src/int/i32_mulacc.c create mode 100644 src/int/i32_muladd.c create mode 100644 src/int/i32_ninv32.c create mode 100644 src/int/i32_reduce.c create mode 100644 src/int/i32_sub.c create mode 100644 src/int/i32_tmont.c create mode 100644 src/mac/hmac.c create mode 100644 src/mac/hmac_ct.c create mode 100644 src/rand/hmac_drbg.c create mode 100644 src/rsa/rsa_i31_pkcs1_sign.c create mode 100644 src/rsa/rsa_i31_pkcs1_vrfy.c create mode 100644 src/rsa/rsa_i31_priv.c create mode 100644 src/rsa/rsa_i31_pub.c create mode 100644 src/rsa/rsa_i32_pkcs1_sign.c create mode 100644 src/rsa/rsa_i32_pkcs1_vrfy.c create mode 100644 src/rsa/rsa_i32_priv.c create mode 100644 src/rsa/rsa_i32_pub.c create mode 100644 src/rsa/rsa_ssl_decrypt.c create mode 100644 src/ssl/prf.c create mode 100644 src/ssl/prf_md5sha1.c create mode 100644 src/ssl/prf_sha256.c create mode 100644 src/ssl/prf_sha384.c create mode 100644 src/ssl/ssl_client.c create mode 100644 src/ssl/ssl_client_full.c create mode 100644 src/ssl/ssl_engine.c create mode 100644 src/ssl/ssl_hashes.c create mode 100644 src/ssl/ssl_hs_client.c create mode 100644 src/ssl/ssl_hs_client.t0 create mode 100644 src/ssl/ssl_hs_common.t0 create mode 100644 src/ssl/ssl_hs_server.c create mode 100644 src/ssl/ssl_hs_server.t0 create mode 100644 src/ssl/ssl_io.c create mode 100644 src/ssl/ssl_lru.c create mode 100644 src/ssl/ssl_rec_cbc.c create mode 100644 src/ssl/ssl_rec_gcm.c create mode 100644 src/ssl/ssl_server.c create mode 100644 src/ssl/ssl_server_full_ec.c create mode 100644 src/ssl/ssl_server_full_rsa.c create mode 100644 src/ssl/ssl_server_mine2g.c create mode 100644 src/ssl/ssl_server_minf2g.c create mode 100644 src/ssl/ssl_server_minr2g.c create mode 100644 src/ssl/ssl_server_minu2g.c create mode 100644 src/ssl/ssl_server_minv2g.c create mode 100644 src/ssl/ssl_single_ec.c create mode 100644 src/ssl/ssl_single_rsa.c create mode 100644 src/symcipher/aes_big_cbcdec.c create mode 100644 src/symcipher/aes_big_cbcenc.c create mode 100644 src/symcipher/aes_big_ctr.c create mode 100644 src/symcipher/aes_big_dec.c create mode 100644 src/symcipher/aes_big_enc.c create mode 100644 src/symcipher/aes_common.c create mode 100644 src/symcipher/aes_ct.c create mode 100644 src/symcipher/aes_ct64.c create mode 100644 src/symcipher/aes_ct64_cbcdec.c create mode 100644 src/symcipher/aes_ct64_cbcenc.c create mode 100644 src/symcipher/aes_ct64_ctr.c create mode 100644 src/symcipher/aes_ct64_dec.c create mode 100644 src/symcipher/aes_ct64_enc.c create mode 100644 src/symcipher/aes_ct_cbcdec.c create mode 100644 src/symcipher/aes_ct_cbcenc.c create mode 100644 src/symcipher/aes_ct_ctr.c create mode 100644 src/symcipher/aes_ct_dec.c create mode 100644 src/symcipher/aes_ct_enc.c create mode 100644 src/symcipher/aes_small_cbcdec.c create mode 100644 src/symcipher/aes_small_cbcenc.c create mode 100644 src/symcipher/aes_small_ctr.c create mode 100644 src/symcipher/aes_small_dec.c create mode 100644 src/symcipher/aes_small_enc.c create mode 100644 src/symcipher/des_ct.c create mode 100644 src/symcipher/des_ct_cbcdec.c create mode 100644 src/symcipher/des_ct_cbcenc.c create mode 100644 src/symcipher/des_support.c create mode 100644 src/symcipher/des_tab.c create mode 100644 src/symcipher/des_tab_cbcdec.c create mode 100644 src/symcipher/des_tab_cbcenc.c create mode 100644 src/x509/asn1.t0 create mode 100644 src/x509/skey_decoder.c create mode 100644 src/x509/skey_decoder.t0 create mode 100644 src/x509/x509_decoder.c create mode 100644 src/x509/x509_decoder.t0 create mode 100644 src/x509/x509_knownkey.c create mode 100644 src/x509/x509_minimal.c create mode 100644 src/x509/x509_minimal.t0 create mode 100644 test/test_crypto.c create mode 100644 test/test_math.c create mode 100644 test/test_speed.c create mode 100644 test/test_x509.c create mode 100644 test/x509/alltests.txt create mode 100644 test/x509/dn-ee.der create mode 100644 test/x509/dn-ica1.der create mode 100644 test/x509/dn-ica2.der create mode 100644 test/x509/dn-root.der create mode 100644 test/x509/ee-badsig1.crt create mode 100644 test/x509/ee-badsig2.crt create mode 100644 test/x509/ee-dates.crt create mode 100644 test/x509/ee-md5.crt create mode 100644 test/x509/ee-names.crt create mode 100644 test/x509/ee-names2.crt create mode 100644 test/x509/ee-names3.crt create mode 100644 test/x509/ee-names4.crt create mode 100644 test/x509/ee-p256-sha1.crt create mode 100644 test/x509/ee-p256-sha224.crt create mode 100644 test/x509/ee-p256-sha256.crt create mode 100644 test/x509/ee-p256-sha384.crt create mode 100644 test/x509/ee-p256-sha512.crt create mode 100644 test/x509/ee-p256.crt create mode 100644 test/x509/ee-p384.crt create mode 100644 test/x509/ee-p521.crt create mode 100644 test/x509/ee-sha1.crt create mode 100644 test/x509/ee-sha224.crt create mode 100644 test/x509/ee-sha384.crt create mode 100644 test/x509/ee-sha512.crt create mode 100644 test/x509/ee-trailing.crt create mode 100644 test/x509/ee.crt create mode 100644 test/x509/ica1-1016.crt create mode 100644 test/x509/ica1-1017.crt create mode 100644 test/x509/ica1-4096.crt create mode 100644 test/x509/ica1-p256.crt create mode 100644 test/x509/ica1-p384.crt create mode 100644 test/x509/ica1-p521.crt create mode 100644 test/x509/ica1.crt create mode 100644 test/x509/ica2-1016.crt create mode 100644 test/x509/ica2-1017.crt create mode 100644 test/x509/ica2-4096.crt create mode 100644 test/x509/ica2-notCA.crt create mode 100644 test/x509/ica2-p256.crt create mode 100644 test/x509/ica2-p384.crt create mode 100644 test/x509/ica2-p521.crt create mode 100644 test/x509/ica2.crt create mode 100644 test/x509/junk.crt create mode 100644 test/x509/root-p256.crt create mode 100644 test/x509/root-p384.crt create mode 100644 test/x509/root-p521.crt create mode 100644 test/x509/root.crt create mode 100644 tools/brssl.c create mode 100644 tools/brssl.h create mode 100644 tools/certs.c create mode 100644 tools/chain.c create mode 100644 tools/client.c create mode 100644 tools/errors.c create mode 100644 tools/files.c create mode 100644 tools/keys.c create mode 100644 tools/names.c create mode 100644 tools/server.c create mode 100644 tools/skey.c create mode 100644 tools/sslio.c create mode 100644 tools/ta.c create mode 100644 tools/vector.c create mode 100644 tools/verify.c create mode 100644 tools/xmem.c 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 0000000000000000000000000000000000000000..411645c0cf09792162e334169d500bc3ec3ca9bc GIT binary patch literal 71680 zcmd?Sdwd*K`9FSUW;46lTqm1s($Y5D`*z!eB$Se*=_T|6y_a4fDTQ{EY}&NRPS{O) zAtbaEP>@>{6)RLgMdhXwoaa2}IhUC^bFymFb;1xr`0(%Tw}tpRuJl{T@ZG^aWEWT8S1j($e|+NC zwUv)g+_1eT9qCWm+fwnqNM}5mv9e~MwM9k2DN6O)B|@y! z$b!u8n9#KXEg(dxCWI;Je>@rK2+|92CF>}yOS_R``{^q7As=-58DimOP{RLbt_qbb z;%4YwL22kcrsPzXylmA(VHV*%A`u}_1NRpIex{2FQ9F>>GXVTAC9oHHT-TiPLEc)M zN~b!J&9=ctu4oyqp5H=PUz%fzG2wm)ANSW!aluX7-8m%h3X73AL_ti=|jY4 zgDV&gHd>%cN_D;sk)T=G7BuxCl3wtux8HtSm+3JbQj-v*>pEqcK1AxnEd`_{3YBK# zhK#~ltWclIOqFe{KuVm{rVo*s^F4wmWdtK96eP1#0R6^Q`KXGGqJv8LSgD-r`SEwQ z?c7We?`&ITn_=fc_4_t4L?(KzQ~IO>uT;%dU3xVdSs(VJR%j+fu1_x?M#X$F^06-F zlCj)gMD(T6tIVKoG7B0ACi;fqJ|EKPvA$u%p$|bobUi#wq}Y>aJQ5xvKAEEk#LPSn zN010y`ZRrrjHzs6JA7gi?2vK8rcUMDslm#2w%NRvYH-0 z!l?x;;x`vogU77wU=I{Z#&95PMqzbO4@jS|xU_}KF!pmH-I85q80}fb!*6+FBXlD) zRQaR%f>{bG+uO77c0(GGTe>Mrhwf0FdBQR>mSmTZtsmu$E{D*ID4NKC<97us2pxUZHDB zV0>p`pn72oUB_W?-`J^rR!g0T;Ldnfn%^@33bYsSwj&9vzDdhEn!5 zG?FT==b5s@P~-t-@cgSX0qhgqvcdUJCAv-;34~r zW(!i+hS+C*1;EK9iqk;+o2B;>X=0|)lf19(V%LqBQ|9V23TTLE<%fDbXS26 z-3SNPO{k-rXflnO(wv#mO>7z6#3(U}mP1ZowhvOBY60}IS0lKzVe|d1&M z*^xN`0y?fFZ~_G8WFN0Uz0eC0zeF>5h9`B`^vpj6S^AJfDm%<5LL*q<=Mph86CES7 zlyin>B2t}vVxr@(%4h>w%KRAbsT52RZEUP*+$h`Hmsx{Uy(|BO&L?ktCtXjHGAf(K zC7zeYysNJ2(sxnz&kCY)_BiE?efM&z-ld#>#|WC!*1-nEfEC5O9W-nV?LjOBuIZ_4 zW1CDg5H*TyCeYO!mPKm|->6!K1!ddBQbu?IW&&YISzr5n8gtSh1uu*(*Nw_(qua2C z@5b2D9MZwqg}PDYWjZ(k6BU(gRJI-58oNg~#$;tzmwNIibZk}3RmA+VZj8-luJSNL z(!-q*9YdlV6G_;PVZcgbE(-=5&j+fPXwe0zLV=&hM$^XN5scBGt%(+@x#|C*jWI7c zt?^`FGNZk~jEJX!RR=rUR5mEjQvTbRzP32T#+xu{$as7n(&#*vv0S=xLB^4)ipKJ( z1(j_Z{0D<(=Mh!D^jHq;@QB`PGmw@a? zx)SN?Ek(xi0G6uMRUEM*DMGJlj zJs7O`4kcEby2YJxYEuup>0o(`{;!M$!}LE*ndQ+{66k zbLE|E%vO|b*b_9!?vhwDGmp`nMUA8)j+xg+hMA5~S$C(-0Fx3z5Q)@!;Eg1%cq_q6 zGj-5o7gn;ECgA`|*bu{NhT=gN({V+US^7nkHMA385MI)QW;6M{JZ2j9pTTe1ud`IB zP7_m*fzF*JgZY7ED4NO}37YmVNPy;)mw_UNz5LD8AeFMv=s&=iMq^bG)f0qRp4);@Kl8jkrF@KW}_#WZKDguOr;kFny@kul>txD>B(eb z>Wk$6jGZ?4f;N^8!6;S^LAfSHl%!Js>oOh>Bg}N})444ZfjP0&2AYOaHr!;&_L);9 zN$D9>Dim%?89KE*@$uRsirGIHYtTUu`=VQkEfa|oNiSwszOFN-^4*t2+vIH*7C)59 z(sMcdDxqK6k62~Fvkl)N1Kmvt9~lhR-`G344Tvr06+EToPM66pYmF2^~{dEak{^-xPcJ$eAlJpQD{ zlMnRhr64pxk4V+98>vFljnq+Nsb$$zsai4wi2*Tfh;6Lh(ceU~8>VV5OGi44`xnxy zyuouDXtHc#sEmP~E%CC;vf7oD@>@Iky`mz$4|eav?Wr#umT3%Y>b}erXTSnPip@R901VIMtWzjio5`9@I0isktJg6Hx^{3F59u_cB+hRxfusk z$=h=?KAGd`2E|4?H{&stF_4?_Q8Ge^8!O+5L|u3(MIxF|Pcp31;8^=yf= zRK`ed#(65^;@pgFDq~-6#!i*7KQ{x9OS$Hk? zWPvfV1kA&7=(^VxAkqi{N8lSIFh&WS?g;#sD?p?X0*=6gUV%l9z*k)XB8?Dm1Rf&+ z$4iSHfv>p&L>eLB2t36Cs#7j;1itPH5NU*fBk&6nsFDI`r_0@TQggvJG@z3|veZ%d z4ObzNnC>Y2mDhqZ9D(~>0V0hMa0Gtuv0w!2M(Or%x&lOEx+CzaS74bVaK9@+q!9v+ zz@NPW%N>CSTmd4D5O4(E^a`wS1is}85NU*fBk-12V5KAQZC8LuBLo})Bj4jz+-k~7 zA8`eUG(x};@OuSTI|2{70z?`i;0P3X1=ctM-*E+qG(x};u)G3m9f6~+0Fg!rI0BVk zfwLTehg<<7jSz4IPVx$@a|9lC1&B04z!8|}6~KB>dg&2YfJh?*9D(Uxfentpf4c%i z8X@2aoa`0Y=mkwyqO0`t5A=Qsl2a|MVrLckGN;uUCf z1Riq*h%`dL5m@0BXm;>j-?`6(G_G z0Y@P271-at4Nr0EU zBU>EyCtY?TG2LO`>(#&25%`fSK%@}@j=%x0K->}du`58N5dw|?9&tMcbvW!#x$Hz5 zA>googxJ+UJ<{n2{KOR?64RwXOq_)0wm&j>?}L@D#4SLuynwHTBb$wtfJHfG}YM?fdU!yBG}8&U+LuR_aavSsd93z_97n#GRDcd$%E{pve^JD zSXOLLLU~QEQw55bko)N0srZNfC(y$LT39q9u7y?59HvWQGqFUkp|*h+LVl|-gePGc zVnM-kclg>4v<|I z6Rln7v|yPC4W#U3T&e5PkLW#$zB`A$6gXyOfdFMW;&*=E0FC}p(dhjeJriD~GE+ZA zeWPSXYKG5A&2>}XcT?N)oZN$M>KP{`s}P&78@>^=gfEYWD|nQj$zatcEc~wz!K-+L zhK>+y0zsrch2hWON370s%wk`96IQE1(`RGXlWFGJ<8V=urj16&>rACLgeyYN!V9R` zP>ffDJKBaRrQZF282K0$P%awW7sRXZQc&~=z`kK3@ymRZ@%a?PI$km)IHwx#c?yX# zgh=w)2(e($tS(yE$~2M=34?-nBm!^u9ldX6n{V_5F*8{l9cL|H!n)+4U zF!KnEXcit?e;jOhtwG~R>Ju&_$Ad0@0V)Pt%itK@kEGtX6EtZh;zL!|l$FJ*SeVt( zg$=kG(g1dv$)=ab3_W@SI9QL&SFd0ip&61xbFyR?`MF;~Wnm+ALRmQN6p^0J zhg=cp`PIX7X#i-%ye9&cZH25gP}x4Uke`_QmDi9f{Q1ty#=9LK-T^WK6z|f)1>wSQ zQ6riNWnB-$=v^ESz3hgVy$|9sDQY%RJLWf&O344nl_%!P0!-jVjXor~ZXJ%@)#wRT5RmU$&FV?=Evl*n zb!X`?s)XDjnEI*P&e#>Sj7y6irh4M-WVnUwOcfMhQ1?7cmK`a7EM8Hz6-l2`$e4~F z;jfPZ_cxsj7%s4U)r0Lz$k+(}IX@askrSND9fB}D`bj8hoQPyqVI$U4Vr*d}yVUYU z4R_z8C{~>xtB%CAZlbTu3JBVY>+f3~Cz4V0cjj z{{^cSd?G`u-n{TbgjF&>2Hri~CCuuVir0eCn_w2ktOev3{|{r0 zODT`}#*N~`J~8GCfNz4!SGAD(o+_A|EQT==5#%q#n;p+gMenobBLj_$KBiL-!^9^7 z`D}85Uq&DYDR+*TiD2`!1u5*Y9-t~O_yN2n=K@`{QYrZ9)pZNlgzBbw6j#98V|sO~ zA}&?L(});X#BN1Ar#)8TwDStc24S1pvoM!bxnnvtM(Q-w$hZpOqj_G0`VAh@RRCr- zG0>a)X?k_!xK3}IAWnN&XnsA3+SnMTSrYqAY5vkvHu>tf`BSt8zaEe?dK%R+3#zV0 zZ0b|PW_lISTc=i5A5W(!RfrcG^$_<5!ZyYqvyp~feTYah=xA2-^9EJFd^3;LgL6!ycvuZs(yoQpwT1ABL(qwUP^u>1LT0{XFy( z>SpvdT#Eut6k+*tz48SJm>zfOQrX$y2r!2}gaF0+>Qk|3;rTGX2|f||b_EFhHY#6k zBv7jh;?B2EBW}5FAOe;-M8Gl$T8V7<`5kj3jlB6;T6h(eSCnm)Y#)QxTykC9!cm?wlZm6ux%co$v1snx=MYP)kNs$lN7McOMWTSX&i~#6?=x5=Z zP@ZY!*|dKFmpKsh<2nylXU9iAZV>K6do*4EA-|3O5)7G^X-|ZRy@0Sl<7vn$3q%(Z z=EuDc>&uk)9C_a%BUcvV=VBm+ZWC;N07b}^ci?y!4Xak(91y|qyaUMAhu}?L)9p}} z7Yc?$1&x>qg%z|44u$ef++r@G@(Zm(dkSnz)xe`xKCr34xQ`$`%}GZWgIQ(jL$Js< zpR7#LGkGgNwI0IJC18=Er6lzs)HIv%iX294dmT8q) zA-f)lHVBkUoj-APX5WIP*O6?+_)u7;D%0zUQ01-h*v6;Gn6a|PV{~f_M@aP3C?a%{ zby73+FKf&o78^Qu)zndHJjGQ3eX<#zyN38Gt&{8xpf%Ch&DcgNahz2(H8kEDH#IcD z8gEUo(V>H>H$8gpKxy2j_BqJn`vxmK73~#^5XrRn|DCXcA#=t3r_+rZGs2 zMYEw&?DA^YJ!7TI$EsV$aH!hriiwRF;e<6&x+0=mk&G*NjC>AGz;q<-nuKyCgEdJS z7eRu{ohZw_$1QiFEO(+)u2tP0nw%G!8V*h4Y0a8yO*<5tqFYng?{f5f3;t0dJ`J9j zW=*rEB64ZNbGn_pI^CLlHO34*6!q33)`&RU}&Z_!zbac5%F56ws8nH zghMAcB0z*yYn^P(vQ9q4JDd+&C!;ne6A86B2EE}>T{u)PACSzl>JM3Uw28=s2-3Ay z{q;7qm{uL9+d_3#JrvYi^{jv%kRYpOA&;JPM_|>3Y8v-}Y@|*t#!yiP`=aNOkquTu z3d_NM`+Q(Xqm=#>Hd6N6TbRI~*W`s}heM|}Vqz24Z0poRp;N&5lPb~gvre(M65pvP z{#5H!Yc}kEhKg@sL9m6#fQ^;uLzrKE@)5{s+?C5tlaD~=WZ0>EYYy`x7h_4VnY(f^ z$}T%C>nVp@#7Y!l$JyNK)s=Y1i*DLzHHLW;U)BiH%)9)oW;ud1Sxr`>y^tK)ESr>e z`N@%}SvG02YErkX8>y_0jB8NCTTfX0tWZlhG>`ZATP@bSL!r6WT$F|z4Re|`5AK?0 z&9hqIuIIDdbpri=a`cBo^Lam{HJ@*StcGg&@lVEkEo0%ms5So(@2}@sgumah=A*#* z)_g090%`8bh@7CnrKF`=N_(QfiH?29_C|+8uO6RG&kHRKhgzk@3$4~ep#|VfPCz)M z4gp#Vcyu$Z1w0dSKa*v0M76@=R;$%oILhMBLs&II>K;<3jFGYXEaj5b=}v&q>3EEW z=UYKLLD=F(EFOim*zP85k%e4qkxk<a|49-c{BT9@J6D5_=~J^#Xvq50~agr~nS?rN_hQSk9E#kosTf z;Ofcp8)W$nvixx93~N?sSva(uXI^WWwfs=%OzRBmOdd$uki8s^TW&44mcen)qLgrG zMOGhJB?q8YvJb4ZR#+?58oVvsX3>herIs~_;F>bmk4DZ!mUI= zlx0YBd}M`l^B*k1)FAo?&ZG-_)PGB&O@hrzKNhsK-A({(`^_%@F0!6 zfNiEvz}JT7)&Nwx2eBM}`Ro&G@Fa}D8oW8I8|mjEn*KkMr~9ef;NxoO_JyN=1IeO_GG?Uy;WE+k5BuZgz3`3TiTW{JD#mco$h-tpS#WY7xg1ro^`DbvCEzNfmhz>qiLP)2?;QiTpfvlJ+E za9%xBW%%WXjQs2@9HVDd75>fmj!M47q8PPk{oe$)(GwkcV~tl@;RQj;3+(VGdCPe? z7O;HSl8TBI1^k-|tw2Sf-NCIuTcPtl0yiLN^x%TeqGJWH`ftJnECLaLeTv=6PlWRG z zX6j2QKz`d~+Z1_?1Cj&%p`{i{Gj)VG#&t%H0Nct#g&+I&Bl}#`?q|WdAegD=iMzV6 zGjhz$hj6w)uKWcg?|t%71J}!E45{F$SmTre*eP(KALQ{kgMNl*M+igOmn^rrBfNF# z-L;Ll;k^oe&^;Yaayy8@|9^*nZ@$84-=~{ADbYibb3z5&pka=z_(5bkAq};PY0jh zwV9(S=#eSV{pKdG5XH!QkU956GE*|;SQr?bawtvZY>wt5C9BB(d=KTE_dd!wKewFd zKlj0LUsQLEBOlCs4>I)Z&?ClmdM{<+41j;REZP$J+e$$O$DNE~?_a@0D%_q~^G zWC=U%_fO*ScwSD8o!I{;a$9oai{kj7$ZUNdGVyE~+2>J~8oQ&ikD1+GHu%lQ>q5k# z!$*@ey)4EIiMJfpe40e+tM2@uMgymp;$yiEk48tI|6kQdrBbh_FUvih*wOdz*w>Y1 zpH6E`UbCd0N2O$SFv)tR$mm|0iaDle(x1U~fji)q%Ju?&3@v5-JYf`;>AAvw@Zse# zJuSdTaXgPlaYBk2#k>m56z?88=sCkX+c7peB!wv6=0O(ns;$t!JSAs(nTJSyjo({( zx;K3Ybt|glenTkxUmUuzsKPt)55htGuGD##JQfQSxjc}W{6yWf(=bfF8CVMj`5E76 zHl^>Q)ZMZAQuWUhUqpErH?8ysIO6o-D^V=oiMyo_UqOT+f^w;mYlh6DP3Rwl3i-Lm zwJ4^*YbDD|Q+XM}rZUm@K z`SLXlx~eeLLXcw~%R_^!TC}X7G74g{Nw`ZUfk0B=S{E&V8 z+fWC)E%|YqT=RI+1u~6Zo>{obb@=B|R3LX9o;4U0DI3`R|BjpTbKN9gw3+FB)T^j+ zvM&FPx}5B`9XF43%M&MZiyebt(BucuPM<-Kq!)N-H`%jkkCK0MPhmE>AxSmlFk;?m zdkO=47j%T@XKJl{!Fjup$Bo{Ihmfi<%RNybzw(NGRmqTQS&vt8>p@wSyjd!CrR0v@ z4xcDNiYxWeBE6I3wJ?ppKG#Pl=BBSfv0yfBJTH>>BZk1MG5J1~!`CC16|%WqB=bg< z#oo(n)d1fDcxoVDsCo$c(B-Iud~tdms*~SD50%twRvP6zyz2cb-CKC5;r#R;;U<3i z!sCD^Papb#t36lDO#k6Mh{e7y)k&B}8=1qWP*bR}YKV~ZbFQW!Qv3|)CRmBiqWWUa z{C0wlSd=fr!gCWU-7IgN&}Vm0NlVd+AtyK~bj_RyKf&C^Y?hB-xypKA`z1vi#ShKJ zXW&Xd~hh<~u*3&b!*tPGGuZH+2=E&z2ZhT}7Jh$gYoyiYM3naSiAml?Lz@LD@KR4?3N<~;cJ3coij1<=z|NP< z0K(m#d1=g|FrID`daa*+=hTeD}um+;?lt zd6WtJ=w9S*TQ&dVN zP%(1+xH47N1VK9QENJ81Nf6(F?|_Z+X&#jn!}EM>jl#JBPg5fq(PPwbBM!<_0Mqp7 zFOb7~iuvfYE~w$-yg0F1W-IlyWk$}~6iY?q_dzVPadHPg!I!-9NUnuJQo3N_;d8?Hh0H&g`2;9`_PKXu;M5UGe6VY#`bpX$#fwP}ni zow_kY-jTQpQ-;PTX7M^>1$1Hh#8^&N>@aq;1@$4agp%0*YKC{TpiTleXfMGCJ|wEp zAKo#aiGB|;P#uX5lapbm&i8=SA8~dZG|v8hK=cMH74JPc!nHL=R)`U2Z>?LM4IHi1?q%&%k(h} z`oZj?eoivzZ?sG`eD==><@lgEg8U=ZImhF`S0cro`@~krezpNz^pi!aJ~CGag^rFO z^FX5u?|H6NG*^t`?SCtefD(l{|TJwi=BHhnQ&Fm^RNW0zdJ8G>$uPoZ{> zo&;TV$HI5PMLVnEA?N(JEB1O%U$hsjekfDuA;H*>|RSMczhRZGcdX#2reTdB;V(W*< z92;&4N^u)LL6Yp{Z<5k0$#WQWqPX(o9WypfH_D^4fX6P8LWX^rE9K)4v#?i}?25@c zEP{1%eZ)@Uni%cZqZ|*;N1}Rg_rTH&%{(mUp{jS%XGU?#G4<0tu^K$HxsQ{)JP(#U zh>sQvxRE^P!BYF-)InnOo(C%nmXJgZ6puFX@pn?5`b^~t@!7P*W?oNk!ZOVuZ93aa zN>sK>D(REwEU~_59eZ0X9dC16S&dtW1L;SHkKZ7szLVjDVw{(0&^AdkI#c?>5Ezc= z<3h_w72#Qhe1TsAntFjB#CHTfLh&hx&H-VT>GBK3D9C=faXFAicuA%5F;O7BDVva9 z3EYs8(F`M20abxANB3sZT6+#OOCM4M(%xgluzaGTn;y*;_)@1jL>shY`om3oCd+*)$54`f3$=;4=#|q9d@jdZl!chDl!$*NpoOEtt z843^i^kUyI)^a$=NS;q2CmS!NKhg3-DY=2c=WR><=Oq&DdX+E_q~_eFca{rQ$f5nqQXB z=iv5GN^APq!d^K(CMI^=4PE)QDNi5zcnJbD zE|~lQI+%pvyB?A_VcKLQ?=ZHK`L55K+X6K8GjxNoSHpMsNL+R#e5fPub#I+o1wOv* zy>;rCJei1M6{BvH!|c~Q5j6yzHIF`uH41%iS_s8vC4ULa=S}iWdn~n~#p$d3`9u^g zr!?tR!zAjs4yOn6JPyZ@sz81OaNl9}h<^bc7aj z#fOo)l|Oud!lqL8k2WjVHVpUs9Jf#xDGONziHhhxn8sa(ageY}kcsk!B2->hEYz{`#dLh%R=v8h!@EP;E72=5x(-K8FR&^X* z`VbkP!N~I1>FyYwH+&fpaDuWA!}%t748u6ztl>c*Wf}JQAfgZc24UG8*&NxrIT`7X zr_zZ?SIX{>%#HN)^(DG`;sc3baC3zJ&P#VsFLD#zJ;_8mGO#@n>9kXcNYYN$B;x}; zyAqM^!DQz^kDW|MqJ8n6WbfX{U^>wi>9$i)LCSlQ+akN|R9AXdq^CAf3)ump4@B&4 zR=6#ZOr%g`SHvD1=pP)IQHzojkzSM-iFeq$642u{AiXy^5Z^-uz!un>97xxaxr^CI zQVr7fM7k&K$=@7ljzl9{XS`!|n&NBWwEM&!s)7zTGGss(SiBr&dLH*-04-s>yrO&5KL#D-o9y^d78Gck*@l99CChuDqxCDOH#)pUu(`_UjN=1U-=$N+Fg z(g_%C_u5JJ2v?b+A5|a^b=dHX49PAO)g_gqr}QF-9k)T#u6XYt%7B*iK)iEjB#LUw z6kM4|Ih7fXkZ3wGy<@s79T~9uYwYeCW}gLJRLu0ahk=!6IxPBK2mUzFZuFkqn#H>Z z&@!kN)MQtTx?)eVXP_tE+jC)}s}@nRWeY0C6-IyQPT751q{Za5NKH*-Mlc_N6j5Oi zN$-yLM|!%0`S~gmYgo2scd7@2NIvx`J|3*_cteloGUhLSZMaK_GvTOSoOGLO3x6}COGr{khveqE%Fg1fUba$#gR znBS4u){`W+ZjYzPgYfy*iz4%+5ArX>H8N=f{^V9CkDSmE?@Dy`^r1I*>>Wrf;E7ntb*Gw}et!x2K^lGGD2M8iW<`aRFq^>_|&XWG2_FCf?Q6D2K`U zltt|+c`1(OUx>6M#d%cY0cMe%#E3IMm2?fHD#bfFChbUau&*PLs*NndK-NoxVXr;d zgEVd2b&(oUr9AGkw`SOvdf8jSUSEe+JAq6*Bh&4biJw4*)&%GWSsrns z>_63r(1(y4CB;JB3*$ z*@YENbEJMI))6y#Kx&v7X;8^}v@P0-MvjIWP-U`i9zuN{Q|nshQ`pZ}2zLSEt|Ky0 z5tnBmnBtN8IW-+U19F<;VVD9;PT}$70QdO_4`DP5qXS}LmyUGwY^zBmu}Gv|nMiip zGz2<>U(L3}K>ZxFCWeYkm*jw@@esLpo?V#C9>XthF=eZkGZH)72Qxq;MPEOH0f+a1ka2Bm7JGVy=4uid2k-a?$z<7jK(pc&dXCl&GKR1Hq z2(L-{?VjWSFL5GmD!V5QP3ZyB56j49awQSjF_@;m)`*n?RLtTLrjJOe1g+j9gUJDV zuyZ@s0^AbQ?e6JN#B&u*@L2t%Q3kJZ$N~&b(7l&eMQD?F?=12xnjx_#(K(1Uy)1#M z2sIec2e6oi4SoG^jU91UCXwmNn^;$FfU%isU~!Bk8tx~s6rr-XTC|vQZ&i5ZLu)t3 zi8LiI!iY-^gTCFym29tPBWVs%hGLbooBoVtXCl$X)+}j&5ABHEF+iS(JC1Guk&Wl- z%euGKMq05TOM^S<7KEFkgmY6vj*E2m#<|PLHE%r97e|z;rRbv7>?YOhaQ8VB1{_@E zK8K#$s!nSnrlf13hubfK)jC#ry@@#P$q->YX@camJMNZn^U;g7Fy(RsVoL5DfcMC3 z4yw5vC~i}5v!mvTJ^hJJtR|wg2BoE3dPYV@)jn&{-V8mc1pe>CjSfoawEKIUHf&wJ zKC+aCxkwsnd%AnDg25EeYuw%eMEo}7&`kicAcJKV?CnT&k}D&-6Oo-sG#iJ#J;?VP z6jC+Zm>l9Tn95Fe;Z_Skju7Ms8fv6BY9osWdCf1^tg?KryIhT+5vjpd>rpij5EK-! z$J?9cMiOy!Q@fi7D-0e|1dc{G#}bD6AXWy`dx!xm;px(DZZG`7RIGsddT?_Et=t|e zKq!2t!{8*wGYGpKBZx7a2gJIpyS-)#IgF(4JtePhU2I+^1D(|+^R1zGQH%Ru8V@rZ zd^c;#)prZH))Z5P=yxNQa!7~9Z->A`-O1W%)bqtz1;EVHu?^YQrs12LhIaa_n^Dvx_dC&-SBF*mj=6Okny%i4^y(a;B6>!-SiILmKN~6 z!29&?qMRFQx&u$)9+;oq(6~->6xs%V$Cv(yFF(6M*l`R=&D!XUEPf7!p-6O>-P1M0;qWS? zH3OHlcH{apqr|af$%<%QwiaGRFxUEXT2X{o0e6GZ8tSt(q|j8%v~JCYYps_OajAfFpT+{;} zot&d@kQKgvpXchE`fu7dHP=2?`2Ow7)z|WG*cYvdI)==ED_dY{R#W2+_5C+auJU=g z+UNi0l+Pcf`#)$oZrOAE!pa@b{j1$N+voE}DaVc7zcFZJYo9nu`yj{5f5!Gv@iHg_ z=0Bu;wC(>v&y0$gK{WG!%-CIt|s+vYbMmTC)~SDLvyXWAVWJeR*bca#EHXM@0v4#HhKri6awK$6dyJi++%g`RY!=Joi@k$mG*|=^QNn>0mjh;@13p1L*&0r_lMs zRC*o$O~pUFAs6@_S)7S~S0nvN{KH-lI@g)^wk^Pwc1@5Bbn(@Ps}MV}b%K7T__TI6 zj()mNC-}Vnif)PmV~i0L=K`AIDuzdWl-`=REzcB}GyD+4FEG3>?SRv`GE+IHG zOt6vRT87P)Ptk4%)!wq*O9jF}OazVmS4v+U0n5XG2Cy-V1(Di^f3%ZgBF?~~jTEK) zZBsd}Cox8?RE`gFB8;tIEJSCxfMW||VKI#{>@fysm6*X8?LnD{GgfAC3D+?;L9}w- z#{uiG$9W}Vw=!1Ee+ROZzb2|fX`5K?0nVGqC6$Utfi1+o#01MdrLdihy{Is3uY_fP z1*`+-F2?fUf;!kdBm!C~{`jL1Uk0`ho`GM0O~iRES1~6xP5|qoKgoToLh-$Y<>#y*I6nJBIhAJ8;$ z3;t>1>slV*QHI|GG{loy0Ptz1Jg*fY_qUvWO)CTaFAV>WdGd6Q5uJD{k=8^V!5cjrz|EYNaH~EZluqW^#`NB-G)Y~cQzS}lm+4cGzJhr+^0ykB^;w`?&6I--W%^^B zJ_HyNpVI39KL@CZyY$(B4|DDh^+u%M&|3iWj0J$R4f_6Lo^dANGGirRr?COB*VqiW zi{T~47NkF7bOYXGkZpG`{aXgr_Ypu#yl##F9u8ayco)O_89v7FXAFPG@C}CkeBul< zoXT)E!$l0&F^n_pXLvEgYZxA8csIjuG5kKm=NbNy;c6KF#lgT*UTgR6Y~Oq-|$@ycp6g{Gdzpoy}m19;mW|ZnjtnaJU@^G zrLV9B@Kc4a>T4tD%Hlv(GB0FkVt}#zqNeCt;S)z>p7@ekZTQ6IiOdP?9*n1NOAKaO~I(XQ(<+1YehiZEU`di(P3bBs=O~2)*Auwtio zE>&0(IxTUv!Y)K!nfR!}u0UQH6^7r@q7UaCMt?lac}KMyiq11=b1MCg=G|7*X_SjQ zIqxIluA&~JLX^`oKzu}et7x}TDL%y*`TBBWjQA2`M+2mDtawdgf^?1*|6pu?;BnY; zl2}VqjW{Z(gp zR&+P8vsE6oN=%%uFlv>U*rqV@(oCHDK|1$~-y(0OcvR+zKNeLRv&0(;`!n)t#A2F| z#R2gquv&4a!rlgUvZ%wpK+4N2z6UepHH_^SCB@%1>ct%@Z*1|y#wp@k3ac)D%s5p% zp|GjNPZ)E=s|uS1>@+cMrqp=~utw3Qu+N!ctx5ctvHhaC_~%BmI5JCe-d6OoF<1Oa zV(`+ng7zHK?`YnV;@2?F{2%8X)s_|i)tD#D8qU+sDX!7xi!z0E7XJg|e}%&Oiw)mG zQ6;hb;o>2!Rh*=->xvI!B{D%_H!;S=p}f0_gT7WVQEB;RaS5==j8XetD;9}aF7{Vr zk(etv1zEOOY*H9my+mwR7+JPdT&^%G;S6yDV~>c>na|_YgohZT66P3Zh#x6V(sHKw zwZcftGEs$9JM{pn-*R!L!bs-|F&`^=lbnJ|SSj`^ zjI^v0*Gi1FtP&q)OlnyrzQCCD#47Ps7pu`$iHDUOd3UvVTw+|pYVk{jQH!k+CfydG z4&>dnVjN>q=Q=UP#cH&5qE2y=o$JLp5@X9Yh)pVwtll6x7?XBx5Z5v$b#4$Jcd;66 zgSc7Akx&lcZPd8G4f@g!qX=h@t*~D+ zHl~3*@iFYoj$t-#RoJV=G2i*3m$660Tg45&t>S8h`AQCpxcH31s0YM_?-cf@C@ne7 z7Z_7<4d|n6?Q&jZ@Ad! zeLF?u49VG3@_ApcC_GbQyGp+4>k|(t>jvqCwdiQ z2gO|_KgGJJe}&|HpyZc6Tima(he}@c^^3xlQtq*muy%pyWbB~$amin>hU!;&&y>9B zON-J~lJhquuNecPls=9`ZU0nKkvAyn6!!O$v3a{hS3Bh$5WdnpbFa8wVFkc06kk?Y zd1wsM`Kw8u-^mwkl3ZLH-KFwu2Wc^HP5_SjH8=f)S(R6HDa;C zs)1cA_9$!?u0rqhbi3|J=iW`C5Al54Ec3__nmn!V*zz&Hc3VQ_DCxz&caz6%kqi9jsFM-`8 z1{C%hu*2f>3Of$$Q{q{L6_(92Zx*GUQp;Fiw}?iCMStv122bu0qjx6j$%gtk@;WZS%s0#uL^&PEF+z-n_m-C z7&|J+vin4x!bt9$Vxi;|e-4GU`^9w%`+MjK;{ov!V+Tb+`Em1GqCTCe?YBh}V^Zh0 zMT^2n=Mk|;VPwmLVl`uA%RS;dqRYkpY8(|8ODynM;q&4l@d3v62P(?%0d}3rn^11} z9~L*Lycy+xH69kX$UHH-{4>TQ;s|3$1^N2F#Zw9+TfQq^QW)9tJrTyY*3K;Yn3(2b zmmB{h=D1kE|F~G^VnzP%iw=neTFQsCC&bkXTT*^lJR$B^*sAh!{}bXN#^j9mgm^>d zv1Lz)z#x|{&MvR=KPeU~?5*O5%_qfY82eb@-171MABj6%Y_k8y;=f#MhW{z?fQy~% z|A~0m#ZLAAR6OotbNx??r(CSn|BQIf#m?~mOuXn~tNcF~e{`|+{%6G-F4pdUPU!Sd z3-j%{f$u8p zam45E#gmHjr{y2?zapMd*o)-{fgMxWpUV#cdqrV^ifhFm#A_1EFRwTZ?9|;Q}|*6-HLSD(;n-Xsj^&uZb0V*b}12{{>(hUF?hg*To*junwuH z(f%wRRoD_>e-W=J&XvIaSDd+5YFQ6k{wn$yJ0RL&^^A@gTid)Jfg6@jJ>R| z0~K?Ozlt{$_MwV<@O;v~kV-fpuB-T~@rD?2v3tau;wpu$ExJeipLjxH?ZEyn%7&!e z4bburu}WbdsyOO@OYB$JEwJpkxJF^0W$YG(-ND$s3i}FUk1Fh2j2Rcn5{@!fsIc!c z)}pWRaRU>Q*bpGBS(3&I`_$9nsq_r}(Kkyn_tVmm~@{U)$;V;(KsXTx9IIzu($ESkAtawS|lw5cgqbo~(UIVGjbEqV4P^$phk1U{kdZD(nZq zrfFwxlX*V@HeGAnF0to;MKxcK#C{EIhW1m1{T^6M%iAII{se5M7FF1rz-DP%6(&Nj z88zB0VTiMCi_-vPE%>r&YFfSsXD;8l=}i8HlnjLDdwH-YrFg5LLi9niqr z3Zh(^NATAM!Fzp_dlOSW$MD`!@M@hn3F<=f{DpN&ssCku2{$q)UY&t|GxLv5uVkKF zPP|$0NNoj=f%hGxN7A>kw%&K8Y+?;wd(LNytZy}Hs^KYm2KI6;m7Hgi=cKkgqi1rAWH;yrfIb-n3Wq=v*(SSC6EBjB9;} z%l#!=_#)TBn|_HY(o3&!`WB8BI-A|#$U$^@C})E1Q}kqkjvAe*Ak{93TulrylsYBl z15A*GKB+ZCAP;~K8KttTbxsooS)YDg}gQvfc`1=@2y43UkXZUsY#sdr~qI4Bw zlJaAw|CHg+0Cge#CVP;VhsFvWZ>Fdf{=nmg%$3mFM`Yje=E_oJd&$W6_D~t;?_`^| zw`O`V{vH&^hF40KEBn!Vhf?ajL+K@17jG>_r)AVh`pfL$!`xH6vVUYcL0!DgY46x8 z>n?4RetVO7WLoNx5U-jM`F@5O3k~sel;{(GU|)IZ$2@dtr?k+^Lu0$_%XxU;NBX^W z^hyO$t}coI@ppT;hkEtU{Gtgj#XCw!JteFm%=SwPL0ybzos_GIX*s#hXqn;v|32j_ zg{^|#f8>h+h9h|&L3$+b262|4H&X55i+Q(-IP>%X-Xksm{2FrCn~z{`!glj9yd z5O-HW?JQwoReY8h?+*h`_n!oKGVY6s5e;7M8 z_WPG>Ult$puhAX^=SJM~ze?9Zx$UNk$YsF_6-pQ8V$z|QEAl2ed)^k7e z+|TyhkKCkwKl|hb(NH*`=Zocq_i(h_z;FnZ5kv*W4?RtyQtlLI7ha+N510O9F8v|a z{1E83=+A=wU;3{Vo$5=n-vxy%Z(+ZU5$VDoVn<54@M(s}^s~gi!r$qO+49A#ZI-yb zFo+hRww@)vShz!@_`Fs;1AA=va3t?9I<@bc`dJ!1$GAfLrtopGfq5=s_=-!4FVL6vr&Lv*Nl=~TOU`R3SGYA$K`?;-a#ZKSX0uO3)#a-e-ypelAyuj%f zIK5CO`c{Vf7~agV74J#!60HXD$2lG6^Z_5yZ_XolgyAuU0_WS^B{V<5i40pA#u@Hs zcniY^8NR?!3ot*!R)%qg`x)NC@Ii(zFx2vypJ6M*IK%x6Z(;Z#!xtE8LFQ-J$}rAw zKf_xXKFII|hFSshGi+rTXSko?Ees!I_yR+%kog(5GK@3auON9(DeSr4Zta@tq&a}6z3Lb%2K67 z-&aBDn;9Nqc#NS46FtIkA;YZ<_c6Sg;Sq+%7>Y{fXSk5zR)+f+-pud_!($9Z74tJ( z$Z#veeGG4Ac!c3GhGGo!GhE1UE5m&ZZ)SLe;W36{Eb}v5$Z#veeGG4Ac!c3G2~Q$^ zF^=G|@tmH(8W=8QxOEay_A$Jf;Sq+%7>dcv&u}5btqk`uJjPH=Va-#Sli|%XD1C(C zF@_>Wl#`5EMvJlB=r=};4;f!E?lYb;UN9U-ArTi@Hh!n{d&1?B|vRCAHJ(%fKfH4|p9IcV-RN6gF2Ys?$Wo6XzIyUlyeBjy|C zXZ&CFKj44X|4V;fARL$;XbKouPvl{JVFLQ`UjYB*}NRSftPsFUb;+Q z8KyJfq^zV|9X<%kjV$%qQE)U}@=H#c{xVB_o#7GI_9(+2gg*++KMo%Pd?tJt@EFry zWX|7(KMl%Z-{%1T$aKm12IsyNz7zEPN|G(9r239yDCsjfeQM>GvvPHK+JNWF;CT)H zq*f?L3uy4EAL%e!L5EKZ04Indz=>!DT}(k6=-5FT0*rxI$6pGngj6k{E>1>EU`Y2 z{+$AN2~K#`#Q|{|;H5aRP#3?zo@(rm1JuQ%fKlv%(6E2@9Hdui?MSZ%)WsU@T%^|mqSo4ZfbH5A@iwHov`=VvYG2Xr z(;nA;tUas!3V)QiOrN69(ofNw_0#p``Z~Q`->PrZFVR1uAJXsAAJpH{ef#K1l=nyb zd9kc_e?JMdRT7d-+lD&``2)L%gTsfmpNt*DQ_yNt0r6{DWp@qsCguy;=zb2JZIlv= z)@~F_Q#ck$EW#(UOXQ(d-t=-DbKu1{E=vyN;!F1QLsLeccuv5kMDcmXVtm&j*7Va? zx$3eAADcT#`hY{Fam2?;I;u;dY>bqn z?Cnp84Rv&8kJzx5PM=7mPRGaE8HBzEaB{eg4zhX>);dVr2C?C6I?h6zgD;U8oKA-- z!MjA@x>5ojNu>I5noUA7t>({{g}kgAOp4uN13qU_4YPi4dLYqPyKIfzCKc!Wl|AVJ@YDBA=!~5^o%&MG#78NKB%Nfp6lZ(1CgZ(( zaS9;bYp=u^bOUHrd2J!qQSQYl7b$`B7$|5&3!R}hilt=(H`yrq9?_4WY1y!7R8Gq> z=Rn2LS?hc4ffG>4A4|Hs;m*;S?4eQQ4T}Vw&L%p$*_pUf4D$#vV6RK0?cQApk=1M* zgEj@J=%Ak3fxb&lp;}WUrV*F;#tR*L8_N$;)50l(eLSR`AmFKKMj1wja=5Jkw^DzW zCIZ1gf}^n{x~5|X>Z_tj)OHTo7yxLLrQR!3qC=dHgUt{x^ktGj&)Fh781)BfmDnX( zQ>pk~jHms35xe~Z+mYn+5mOwMbe=)$02FlKXc{MW5n%&8eTiC!bA2MU3&%dBsUu=w zTC%4Tim3V>yT#%}$KbYY80%8@?li^OC~b>-;@grq^aMv9j^;*XQ7#6vQyK)0K*4b^ zqH|!6NKmg{6^FH}5PE5`L>;QcmE5>tX`{ecPL)R}6YDYzWqZ~P4y@^37f)_WWGviZ zEAQZJMVv1oI{OF3HjaZub`r;|kRCa7)A&s;T#C_{8y))%(+$Gz*dY+&7}@4`Z`mSG zRhrTl@2{h7ni@!-gF|Kn20dZ-Z&^ae{qzj%73&8((y~#cVclYbz0%&DNG(h9!IBtt zH>7&{0QRE%v)QXW2(8B%F9U4GA{+`iAX?Knov@=98s!kQ4hIp+QH2a&!Y2x$?Oiqy zSH+V(-8kZdqI59TnLzy$@jeQY%tZ}<7`W0nZi8A4oq>{QT*MBYJ&JIn5;E8}_-2tD zM-f6vbfJV;igPwGCnl0SG_Jz=602#9++fR~Taw(>ld_X^Gzo$R6>E_b6sd{}*-RpBIU!PO^ z`kd<5=hVL;r~VB&^>4_je?w0F8*=L3kW>GLoccH9)W0F8{tY?xZ)g?nNnLV*B_}uP zR&=CNdPQQdz*q^4(ai(Udf2wpc~$Nh}E< z4Q0%Bl(?G{H3zK-ULE2LZ&44Tz?s#mA!U ziO!uo*^vc}9JLZcghCiHbb-*~1R2AVIK`@3jVXf_M#hj!FyG;zt7K<_5Lzo?Nd~u) zjjQ5&m%_~(wx{TPG$xT0&0~!4GOCqV;FyqTG3MNlF74T~QSy++Wl3hIBo+{yUY1^g z6V)VrwaqgZX7^=DJ|&Olcb@3eWYd}P^mfuMmZdT2uj$50jhAZNU3g)!3O!oz>5Js9 z|JUBRJjr$4b^LVq^z`i+sikgN0hcwTgClEX%}683!tq0PoKznPEimSEPZVA0jZfulW+t)Sa? zm-lS?=EaM$G+3pCMyiz1 zFqIM-qf$ZxR7z-cN(sj1LaIa&<8q;_L=$6jp{zs|<8h&^L>FUmp{zt1<8Yy@L>psp zp{zt5<8Ps?L?2^sDr4j=q)PNL>K4jM^fBTV%1ZPx+7`-6^fA&F%0ypLn_rO%9U<0m zz2Zh>K(6121C(O;*NstMHwJs%7~^$gXxEJqT{i+5-Ex?OElLrs=thvD8?lLQ7L#$$ zd$bsgbl#`MT%_|}Eyg09_iHf~>AYu)p-AU_Tg*f{@7-b~(s}v`vIYyAsf%a>g)mqGj?(R{z7mfcF^X=A zGd%J3oogCYE&p{*=DaLs-d?u6=vi&Glgmj(Bl^h)oBGSEYx>A9HuzPmo0js0&GY&S z%J3TBryfr5&E$Ti4k@n#dwRq#_B=x4 zzIIS_KRz37hsCg@J*}m1L!X=a+zP8o&ua#;pwzNbD@xx|&90v3gCbJp>&1`#?<)6> zo>x?MLkmYsfof)8+m;hu$u<0FN7n1^C=k-F{5N^lJ5jdr;q2t|nqe#pR%dbEU=AJ}AiK zl1kt#5O1j-c)64&^?Xl#0ROl?&Rg+xd#c;jPrA9pZm7Lep}YgX-Q(gzIrVhp_%-3# z4MVAavRr3Tbgu}rVLUGzBAru9Wo(~@hb>2P<$OG8bs zn5nP3f)r{uwS{_9vI9=2-x4pwc6!j3gsytwWqeh3lDFK&^K`hb{_m(h_8G=M$T9mc zgm2sxf9K5>(JCx#R&CL^gN8FY@qs?r(W?4%-8rbzgUbC-AMM?kZhBs5mutQl!upT8 zs@4@6sA;V3d0i2O%67HE8cX&mHLu*RRF*rXm(});?oO7Aq6q7jYT|afVbVt+G3 z4|GBxV&Ytu_B_b$Z6@?oOIZ)_!|ABc?iE& z#C}>Djp!Fu_L6+iHNk;@U^Xu*~AclRL)%sZVoHJp3UZ)q3g4&ET5?oP<{D<S?JB)M9bDFCLQ&7Zl&O_aG=9gVV8yXVn%g8kz}tkzqU!42T#+2-?di5f67DyN3qljF`BM+C zW)I`q0YL#vz35bXR-Z%C%ey+=5o&wwNmqU2NvOZfSX6sOPp{aMQpzKJm+wbua3$|9 zc}iKW2G%0Z*U{wA&=#}ZBWGWxrQpRDy4MaW=OA_^9O1)4?@4*}1=^1P>9-$iqW;rI!0(yh{?npLwh#yducd0BOg60 zvDJ2874-|2s|u%ly1&thZK&v+}sCtt<9R+ z;E}K@A9CK+DIvyxqUGlp*$8hnsT9a;vs)z;d#C`Xda2;@WdMMy;% z7rh^Rk@bFXLOvZ$x+1$_bX_r>zoch$$@M6XjqoITB$wx)^r6Hfxv&qpT-S#<3n|97 z)Y+HUk_X%C^~qgS|HoBAbjR$$Q#OA(MpS{XV}=A*T+7kzd6jsC?$!dAZ|WmI_QJ4M zaCcR-660;y^4J;SUABX0c^(5{Q*gUPOHEP!^R#5P0gv4e_^8e^92*{C;KAg#YGeAU z(VraBF}$vhx#7$hp526fB0(6@mB0Yw!*~Q74fZ9KYl}qJXnZ%7!sISnTb^J7?gVHD zRr6W7Zw_caZD`{qlH@n8r1i*<+ZCO_|BgPB16xFAK4rbxNIFHGcznrtUp}~8 z$*JqtkFIEQJ!+Z`GMK{YN#k_nHQ`HFKlcs?;Mw6ET9ofF4m0T>tMs{8v0n3?Ezqd@rOcar6zfDH(g#Gxxetn{3&0x6BOd7E9`(H^R>sP!IQ{ zkg(gBqj;T_oo-k=WQdC{efB>3?Dj&7;H!wSNSJ$@b-J+~!{j>to9j?pgz=VP1T z+^@$;UW45GIUo|r)U zVji?lDkP3|o;8R>dH63aM;%LLvAr(`ZrbW?5A9<-_P7+p#rjZx04`gGO27EiGGBU7 zPd^;X!y504oF+m9O-oNKPd;$>hOnkr$ z66{Iiy$?-VDH3`#;wGWt-Jgn^Jr-u?q7s${X@ffDUGom*Rj_hm^A|z@(@JDg` zF^}*Q-igYD`2+P{T!EMre61yY^44(m)JZmt&aR~ID2^i5@eP1}1x1;XHz5^kmVHU| zKF;*}LdVt633Xo*XMfCw$oDobYJW}yrSfS-@G#-wDhZ1dg!9JYq9G2#gxYfMppiz1 zES5RULGHOX09tS3cYD!ihi2qq*#D>=IsEZocqrYkcX7&2ch8z;`$VPeur~^TX#+cH zvr*{urq%>!wJ7qY6vIPecX~`=-8x*~j|czbZz}_&~t1o2k#TwioF*5?4RbjJiJ%;q_q63m@p_0 z)2XCUnDWMQkL~+)>fsvYa5slMOfp3t0i$0op7(fiftq2GDfT&BF28t8I5pDmIpQ+u zeNpWL0}Dl*5=ns?tLm&1yN(pgv1Si#W9vbmh#S|5^B!M^TQ9Y|pW-;n&Mo`5zB^a6 z`cyx90laRq)>Iycl1K&1%8>?6L0|>6EB4!uBi=EN^G-_|e1?th8(FEo{a)UC(BCF) za2afsi$^xWo-)UNkg7;UbHJy@Hc5K1?(m3aBZ}XVFE}qP=^tM{E5%1Qp_sB^6#wt# z66d-;A3m{Y5GA*2eL z=&>jBdD25oEc(7E{QX?m8KGGxwy)ZZ%uO_hSf_KtWtqb_w_xvWI3I^gyoibG^MPLEn!LiJ z>Fu$hxVn9QUKkUPM_o$u$c=1&Gun!ly}9Q9)+Thu<0GFhy<`4s+7Y+Tb1qf-$gC{qctwBh^O$t}G1|bbLJb{DOF^>`65MbiQ zm~>wewO36&5!sZMq2cHyoit(1Hb0VmEbdrssnxUx4xFDVAI^%0)chcvt4i4Chrw2~ zLE+&@tkrjvt@fKbz@>wHb%uAYb9TLC*Vpu-3B4AN81@f{5i3r2aYS=c9O01J9R)PF z_Ky{UgfB(2WYF}3i15SPfKfOEzU=7%&ku6OBi`vSXxEk&;CI-u*LIG2FDp%y)8$k+ zP)=`_3U$mVH22DkPVl4ysD7~12U)o3=d>n;4Tnl`?0y6_uIP(my*QbUqjL1Hd?444 zhR5T1#(`HKy&w98OD>nY!bd%Bsr|g9-PeBFp&k-dUg~`?R1@-WD##D&Bir!O#@H4Y zL9vf0N9U3-)z5K;=T8e}QLlVAa@gVF6l6f09FNvf%ws21l3r3nH{(lg5rbsO9B=0UemLNkSAJ=LKH|a8v>zLezLtB2 zqAcQDFx(kldF=rTmyE`+*Wt5y#R8N+9{?S?M<(B$% z+Mv96P?QNRp*hE3Iqa%quusiu3W;v``Oe5s)8+%|xa=^#A?+x+@IvGm`sFXoAXB_5 z_p&!2*UyH^3+x-zqN{3i*=RuW1h2dt(A#I$?=fME*vuea^aZz!f*tNjIp%GzZoO6J z5C@kX5^@C^+(0IHXY><37PXH$oC#o&G#nUqUXIrt*+(as;OtM{^F!;h?UUDSvL$UV zWo&=BcVt;F9GoDSNUt5nmzs)F8iw(7!g%(5@sk&Ie9?oW<>|EUJ<&znKq)l)m`F}~ zT~x0Yyh<4o@mDmadt*fTP;=SNfWRVK2hiO2YdoAjk&ggvs?cq<6e?? z_-67C(T>X27vGhG!|>2~&!O#&TIN=I4;v|Zi6nF4HlJj~@yN9Z+KT?zeTMT8>q9<% z!aFW?c+kl*1zb65IvierdiR5jk$&_XJ@i+1;U|{Ej^r?pg2aTTcO>xzz_dB?Hv90C@8(ITj0t19}W{QV4|z= zx1td9MuNup~UWr z-<8>_j#fvN^<<@`(kXqurOyRH%c%Ker8C2QqT0%o*BTk^tbbo`B~(Vbl}fGNtyWrl zl+4CLz0#S^^gkP^hysdRbzR!WGt&HxhFYt&XOF%_I8&_TPZ zV=OZ$52|CGDUFnB^&?fG^-omabACV*<@}%rZ_43KS^sL8*SEWBsaU+qTDl26N6At` z`007i$Yupg~nMichekR!Dwb%bF&*Ld;HM%)n z-D+ohYfqzFtCY4^M^!cRTW4oRH6uFfi0Eq8)U7d+HKkB! zYA0*xo5=a2o90MEd34_FTvgL*rRmqEfw>@c8Kd6VsC8Rce-m}8WmYY#tFUfLo7EfQ z0ljBC1##56AX(}7b(=k-5uLTp1#VNF3)sdJPU;gz>N4~$W{p~BxwF{0s&D14w&&V& z>ZU4gR+Ol#!uk*Ep&|n~+<4M#spVG;v6YGAZaG+zDc94ev>IZe$P=ldJtq!U)mN+8z#$nggDhc0 zmsY(|lYsOn?GU4DGGr25s#zU&G`BVCV!<{^SMtpoBdzvowX-G?Wf`ca&|d%7#+lsI zq>CDvre3Ez1_m1UUyFs??FG=o1s2 zCsMu=*#x9^>eR?Nn)>NbJ+^+JUL*ueDKn!{A9;lyV&a9ULfmV5Y_E#;YE|rB-PRbZ zHk;KZtTWZZONliN8I|&od}o{3CiR!r%O2XR(wOl^y}inlo+ZL&b5tT!hAKAOtNJe~ znzpMz0< zZ|RClvJ&(DdX`vaGL72Un5lz|DwxPCRkL1=Qr*xbb6NCBAM83}*ScLt?b@)b1{T$U z6T)6s;i0P-qN})|s|HP78+H|c^bA>{B0kW`1yF3yF<6+ljt}z4IDWh)o@LG`nK4Rc zTJ8cMqLpj%a#Q}VbD`Cl%H;BR!g6Gu>siCWOl|9GAidDar0f%7S7@8x=z9-ye5`+_)u* zJ-Hk${TW9@m_JD&DS{%sq8PlgaG8q&vOokQ@YkFw%a6uRGVG_jk;A;w^vkp_{Uzoi?g@y z=*z3@`}XWxbe5J5F6v8``1|j@q5D;P8h)wh*CK*`8zQWi%fUKmsyCQVy*Kok`)Vb8 zS|#OwpH`pK!+YVJ_LBG}nRoPbS_@NWl;&LUDLwP|cO(DtU;H{&|KF>5hGGhR|9|=K zbtNl;%GBkQ!e}OKOd5DqkH0S~FbP`m_Js(}CKskuD*1!(mm~kKE-S(*P3s3C{k_$h z5#7%V_KaTHxC9UM>kj-f;H-uDOpci#YO)x*`h2-i#(wTCO#{#9ZjT3_U9Qv5ovuOq)n!u-bSwI`m=OLm8r-Ip Le^vPZjRXG!A*dCT literal 0 HcmV?d00001 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_PO