diff options
author | Jack Lloyd <[email protected]> | 2017-08-02 16:53:38 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-08-02 16:53:38 -0400 |
commit | 230b7ad5a259631dba617aba596ce7619a24ca17 (patch) | |
tree | 36a2141cc63d3715d3c7c654eebe3fcf291ad182 /src | |
parent | 5a9b5c2c2f32909ab7963307291827ed7bd2d102 (diff) | |
parent | 825c23811f480d3c3646ded125c9e7b7dc9feb8f (diff) |
Merge GH #1094 Add initial BearSSL provider
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/hash/hash.cpp | 17 | ||||
-rw-r--r-- | src/lib/prov/bearssl/bearssl.h | 50 | ||||
-rw-r--r-- | src/lib/prov/bearssl/bearssl_ec.cpp | 209 | ||||
-rw-r--r-- | src/lib/prov/bearssl/bearssl_hash.cpp | 120 | ||||
-rw-r--r-- | src/lib/prov/bearssl/info.txt | 13 | ||||
-rw-r--r-- | src/lib/pubkey/ecdsa/ecdsa.cpp | 34 | ||||
-rw-r--r-- | src/tests/test_pubkey.cpp | 4 |
7 files changed, 444 insertions, 3 deletions
diff --git a/src/lib/hash/hash.cpp b/src/lib/hash/hash.cpp index c8dd58a66..bd162ff2c 100644 --- a/src/lib/hash/hash.cpp +++ b/src/lib/hash/hash.cpp @@ -88,6 +88,10 @@ #include <botan/blake2b.h> #endif +#if defined(BOTAN_HAS_BEARSSL) + #include <botan/internal/bearssl.h> +#endif + #if defined(BOTAN_HAS_OPENSSL) #include <botan/internal/openssl.h> #endif @@ -108,6 +112,17 @@ std::unique_ptr<HashFunction> HashFunction::create(const std::string& algo_spec, } #endif +#if defined(BOTAN_HAS_BEARSSL) + if(provider.empty() || provider == "bearssl") + { + if(auto hash = make_bearssl_hash(algo_spec)) + return hash; + + if(!provider.empty()) + return nullptr; + } +#endif + // TODO: CommonCrypto hashes if(provider.empty() == false && provider != "base") @@ -323,7 +338,7 @@ HashFunction::create_or_throw(const std::string& algo, std::vector<std::string> HashFunction::providers(const std::string& algo_spec) { - return probe_providers_of<HashFunction>(algo_spec, {"base", "openssl"}); + return probe_providers_of<HashFunction>(algo_spec, {"base", "bearssl", "openssl"}); } } diff --git a/src/lib/prov/bearssl/bearssl.h b/src/lib/prov/bearssl/bearssl.h new file mode 100644 index 000000000..1ba7d2dc6 --- /dev/null +++ b/src/lib/prov/bearssl/bearssl.h @@ -0,0 +1,50 @@ +/* +* Utils for calling BearSSL +* (C) 2015,2016 Jack Lloyd +* (C) 2017 Patrick Wildt +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_INTERNAL_BEARSSL_H__ +#define BOTAN_INTERNAL_BEARSSL_H__ + +#include <botan/pk_ops_fwd.h> +#include <botan/secmem.h> +#include <botan/exceptn.h> +#include <memory> +#include <string> + +namespace Botan { + +class HashFunction; + +class BearSSL_Error : public Exception + { + public: + BearSSL_Error(const std::string& what) : + Exception(what + " failed") {} + }; + +/* Hash */ + +std::unique_ptr<HashFunction> +make_bearssl_hash(const std::string& name); + +/* ECDSA */ + +#if defined(BOTAN_HAS_ECDSA) + +class ECDSA_PublicKey; +class ECDSA_PrivateKey; + +std::unique_ptr<PK_Ops::Verification> +make_bearssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params); +std::unique_ptr<PK_Ops::Signature> +make_bearssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params); + +#endif + +} + +#endif diff --git a/src/lib/prov/bearssl/bearssl_ec.cpp b/src/lib/prov/bearssl/bearssl_ec.cpp new file mode 100644 index 000000000..fe661f357 --- /dev/null +++ b/src/lib/prov/bearssl/bearssl_ec.cpp @@ -0,0 +1,209 @@ +/* +* ECDSA via BearSSL +* (C) 2015,2016 Jack Lloyd +* (C) 2017 Patrick Wildt +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/exceptn.h> +#include <botan/hash.h> +#include <botan/scan_name.h> +#include <botan/internal/bearssl.h> + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + #include <botan/der_enc.h> + #include <botan/pkcs8.h> + #include <botan/oids.h> + #include <botan/internal/pk_ops_impl.h> +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include <botan/ecdsa.h> +#endif + +extern "C" { + #include <bearssl_hash.h> + #include <bearssl_ec.h> +} + +namespace Botan { + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +namespace { + +int BearSSL_EC_curve_for(const OID& oid) + { + if(oid.empty()) + return -1; + + const std::string name = OIDS::lookup(oid); + + if(name == "secp256r1") + return BR_EC_secp256r1; + if(name == "secp384r1") + return BR_EC_secp384r1; + if(name == "secp521r1") + return BR_EC_secp521r1; + + return -1; + } + +const br_hash_class *BearSSL_hash_class_for(const std::string& emsa) + { + if (emsa == "EMSA1(SHA-1)") + return &br_sha1_vtable; + if (emsa == "EMSA1(SHA-224)") + return &br_sha224_vtable; + if (emsa == "EMSA1(SHA-256)") + return &br_sha256_vtable; + if (emsa == "EMSA1(SHA-384)") + return &br_sha384_vtable; + if (emsa == "EMSA1(SHA-512)") + return &br_sha512_vtable; + + return nullptr; + } +} + +#endif + +#if defined(BOTAN_HAS_ECDSA) + +namespace { + +class BearSSL_ECDSA_Verification_Operation : public PK_Ops::Verification + { + public: + BearSSL_ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const std::string& emsa) : + m_order_bits(ecdsa.domain().get_order().bits()) + { + const int curve = BearSSL_EC_curve_for(ecdsa.domain().get_oid()); + if (curve < 0) + throw Lookup_Error("BearSSL ECDSA does not support this curve"); + + m_hash = BearSSL_hash_class_for(emsa); + if (m_hash == nullptr) + throw Lookup_Error("BearSSL ECDSA does not support EMSA " + emsa); + + const SCAN_Name req(emsa); + m_hf = make_bearssl_hash(req.arg(0)); + if (m_hf == nullptr) + throw Lookup_Error("BearSSL ECDSA does not support hash " + req.arg(0)); + + const secure_vector<uint8_t> enc = EC2OSP(ecdsa.public_point(), PointGFp::UNCOMPRESSED); + m_key.qlen = enc.size(); + m_key.q = new uint8_t[m_key.qlen]; + memcpy(m_key.q, enc.data(), m_key.qlen); + m_key.curve = curve; + } + + void update(const uint8_t msg[], size_t msg_len) override + { + m_hf->update(msg, msg_len); + } + + bool is_valid_signature(const uint8_t sig[], size_t sig_len) override + { + const size_t order_bytes = (m_order_bits + 7) / 8; + if (sig_len != 2 * order_bytes) + return false; + secure_vector<uint8_t> msg = m_hf->final(); + + br_ecdsa_vrfy engine = br_ecdsa_vrfy_raw_get_default(); + if (!engine(&br_ec_prime_i31, msg.data(), msg.size(), &m_key, sig, sig_len)) + return false; + + return true; + } + + size_t max_input_bits() const { return m_order_bits; } + + ~BearSSL_ECDSA_Verification_Operation() + { + delete m_key.q; + } + + private: + br_ec_public_key m_key; + std::unique_ptr<HashFunction> m_hf; + const br_hash_class *m_hash; + size_t m_order_bits; + }; + +class BearSSL_ECDSA_Signing_Operation : public PK_Ops::Signature + { + public: + BearSSL_ECDSA_Signing_Operation(const ECDSA_PrivateKey& ecdsa, const std::string& emsa) : + m_order_bits(ecdsa.domain().get_order().bits()) + { + const int curve = BearSSL_EC_curve_for(ecdsa.domain().get_oid()); + if(curve < 0) + throw Lookup_Error("BearSSL ECDSA does not support this curve"); + + m_hash = BearSSL_hash_class_for(emsa); + if (m_hash == nullptr) + throw Lookup_Error("BearSSL ECDSA does not support EMSA " + emsa); + + const SCAN_Name req(emsa); + m_hf = make_bearssl_hash(req.arg(0)); + if (m_hf == nullptr) + throw Lookup_Error("BearSSL ECDSA does not support hash " + req.arg(0)); + + m_key.xlen = ecdsa.private_value().bytes(); + m_key.x = new uint8_t[m_key.xlen]; + ecdsa.private_value().binary_encode(m_key.x); + m_key.curve = curve; + } + + void update(const uint8_t msg[], size_t msg_len) override + { + m_hf->update(msg, msg_len); + } + + secure_vector<uint8_t> sign(RandomNumberGenerator&) override + { + const size_t order_bytes = (m_order_bits + 7) / 8; + secure_vector<uint8_t> sigval(2*order_bytes); + + br_ecdsa_sign engine = br_ecdsa_sign_raw_get_default(); + size_t sign_len = engine(&br_ec_prime_i31, m_hash, m_hf->final().data(), &m_key, sigval.data()); + if (sign_len == 0) + throw BearSSL_Error("br_ecdsa_sign"); + + sigval.resize(sign_len); + return sigval; + } + + size_t max_input_bits() const { return m_order_bits; } + + ~BearSSL_ECDSA_Signing_Operation() + { + delete m_key.x; + } + + private: + br_ec_private_key m_key; + std::unique_ptr<HashFunction> m_hf; + const br_hash_class *m_hash; + size_t m_order_bits; + }; + +} + +std::unique_ptr<PK_Ops::Verification> +make_bearssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params) + { + return std::unique_ptr<PK_Ops::Verification>(new BearSSL_ECDSA_Verification_Operation(key, params)); + } + +std::unique_ptr<PK_Ops::Signature> +make_bearssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params) + { + return std::unique_ptr<PK_Ops::Signature>(new BearSSL_ECDSA_Signing_Operation(key, params)); + } + +#endif + +} diff --git a/src/lib/prov/bearssl/bearssl_hash.cpp b/src/lib/prov/bearssl/bearssl_hash.cpp new file mode 100644 index 000000000..9620d6d70 --- /dev/null +++ b/src/lib/prov/bearssl/bearssl_hash.cpp @@ -0,0 +1,120 @@ +/* +* BearSSL Hash Functions +* (C) 1999-2007,2015 Jack Lloyd +* (C) 2017 Patrick Wildt +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/hash.h> +#include <botan/internal/bearssl.h> +#include <unordered_map> + +extern "C" { + #include <bearssl_hash.h> +} + +namespace Botan { + +namespace { + +class BearSSL_HashFunction : public HashFunction + { + public: + void clear() override + { + m_ctx.vtable->init(&m_ctx.vtable); + } + + std::string provider() const override { return "bearssl"; } + std::string name() const override { return m_name; } + + HashFunction* clone() const override + { + return new BearSSL_HashFunction(m_ctx.vtable, m_name); + } + + std::unique_ptr<HashFunction> copy_state() const override + { + std::unique_ptr<BearSSL_HashFunction> copy(new BearSSL_HashFunction(m_ctx.vtable, m_name)); + memcpy(©->m_ctx, &m_ctx, sizeof(m_ctx)); + return std::move(copy); + } + + size_t output_length() const override + { + return (m_ctx.vtable->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + } + + size_t hash_block_size() const override + { + return 1 << ((m_ctx.vtable->desc >> BR_HASHDESC_LBLEN_OFF) & BR_HASHDESC_LBLEN_MASK); + } + + BearSSL_HashFunction(const br_hash_class *hash, const std::string name) + { + m_name = name; + hash->init(&m_ctx.vtable); + } + + ~BearSSL_HashFunction() + { + } + + private: + void add_data(const uint8_t input[], size_t length) override + { + m_ctx.vtable->update(&m_ctx.vtable, input, length); + } + + void final_result(uint8_t output[]) override + { + m_ctx.vtable->out(&m_ctx.vtable, output); + m_ctx.vtable->init(&m_ctx.vtable); + } + + std::string m_name; + br_hash_compat_context m_ctx; + }; + +} + +std::unique_ptr<HashFunction> +make_bearssl_hash(const std::string& name) + { +#define MAKE_BEARSSL_HASH(vtable) \ + std::unique_ptr<HashFunction>(new BearSSL_HashFunction(vtable, name)) + +#if defined(BOTAN_HAS_SHA2_32) + if(name == "SHA-224") + return MAKE_BEARSSL_HASH(&br_sha224_vtable); + if(name == "SHA-256") + return MAKE_BEARSSL_HASH(&br_sha256_vtable); +#endif + +#if defined(BOTAN_HAS_SHA2_64) + if(name == "SHA-384") + return MAKE_BEARSSL_HASH(&br_sha384_vtable); + if(name == "SHA-512") + return MAKE_BEARSSL_HASH(&br_sha512_vtable); +#endif + +#if defined(BOTAN_HAS_SHA1) + if(name == "SHA-160" || name == "SHA-1") + return MAKE_BEARSSL_HASH(&br_sha1_vtable); +#endif + +#if defined(BOTAN_HAS_MD5) + if(name == "MD5") + return MAKE_BEARSSL_HASH(&br_md5_vtable); +#endif + +#if defined(BOTAN_HAS_PARALLEL_HASH) + if(name == "Parallel(MD5,SHA-160)") + return MAKE_BEARSSL_HASH(&br_md5sha1_vtable); +#endif + + return nullptr; + } + +} diff --git a/src/lib/prov/bearssl/info.txt b/src/lib/prov/bearssl/info.txt new file mode 100644 index 000000000..cf38a1fe7 --- /dev/null +++ b/src/lib/prov/bearssl/info.txt @@ -0,0 +1,13 @@ +<defines> +BEARSSL -> 20170628 +</defines> + +load_on vendor + +<header:internal> +bearssl.h +</header:internal> + +<libs> +all!windows -> bearssl +</libs> diff --git a/src/lib/pubkey/ecdsa/ecdsa.cpp b/src/lib/pubkey/ecdsa/ecdsa.cpp index bb65fb138..72551c8c7 100644 --- a/src/lib/pubkey/ecdsa/ecdsa.cpp +++ b/src/lib/pubkey/ecdsa/ecdsa.cpp @@ -18,6 +18,10 @@ #include <botan/rfc6979.h> #endif +#if defined(BOTAN_HAS_BEARSSL) + #include <botan/internal/bearssl.h> +#endif + #if defined(BOTAN_HAS_OPENSSL) #include <botan/internal/openssl.h> #endif @@ -156,6 +160,21 @@ std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_verification_op(const std::string& params, const std::string& provider) const { +#if defined(BOTAN_HAS_BEARSSL) + if(provider == "bearssl" || provider.empty()) + { + try + { + return make_bearssl_ecdsa_ver_op(*this, params); + } + catch(Lookup_Error& e) + { + if(provider == "bearssl") + throw; + } + } +#endif + #if defined(BOTAN_HAS_OPENSSL) if(provider == "openssl" || provider.empty()) { @@ -182,6 +201,21 @@ ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, const std::string& params, const std::string& provider) const { +#if defined(BOTAN_HAS_BEARSSL) + if(provider == "bearssl" || provider.empty()) + { + try + { + return make_bearssl_ecdsa_sig_op(*this, params); + } + catch(Lookup_Error& e) + { + if(provider == "bearssl") + throw; + } + } +#endif + #if defined(BOTAN_HAS_OPENSSL) if(provider == "openssl" || provider.empty()) { diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index a38b7d3f3..d25bf8d68 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -82,9 +82,9 @@ void check_invalid_ciphertexts(Test::Result& result, " invalid ciphertexts, rejected " + std::to_string(ciphertext_rejected)); } -std::vector<std::string> PK_Test::possible_providers(const std::string&) +std::vector<std::string> PK_Test::possible_providers(const std::string& params) { - return Test::provider_filter({ "base", "openssl", "tpm" }); + return Test::provider_filter({ "base", "bearssl", "openssl", "tpm" }); } Test::Result |