From 92b0ff1faaba7c3fd886e79550bf5fe0fc567e25 Mon Sep 17 00:00:00 2001 From: Patrick Wildt Date: Wed, 28 Jun 2017 16:39:29 +0200 Subject: BearSSL: Support for ECDSA This commit adds support for ECDSA using BearSSL as a backend. This means we can test BearSSL's ECDSA algorithms using the extensive Botan testsuite. --- src/lib/prov/bearssl/bearssl.h | 14 +++ src/lib/prov/bearssl/bearssl_ec.cpp | 192 ++++++++++++++++++++++++++++++++++++ src/lib/pubkey/ecdsa/ecdsa.cpp | 34 +++++++ src/tests/test_pubkey.cpp | 4 +- 4 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/lib/prov/bearssl/bearssl_ec.cpp (limited to 'src') diff --git a/src/lib/prov/bearssl/bearssl.h b/src/lib/prov/bearssl/bearssl.h index d438c47c7..9188770c3 100644 --- a/src/lib/prov/bearssl/bearssl.h +++ b/src/lib/prov/bearssl/bearssl.h @@ -30,6 +30,20 @@ class BearSSL_Error : public Exception std::unique_ptr make_bearssl_hash(const std::string& name); +/* ECDSA */ + +#if defined(BOTAN_HAS_ECDSA) + +class ECDSA_PublicKey; +class ECDSA_PrivateKey; + +std::unique_ptr +make_bearssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params); +std::unique_ptr +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..507345f24 --- /dev/null +++ b/src/lib/prov/bearssl/bearssl_ec.cpp @@ -0,0 +1,192 @@ +/* +* ECDSA via BearSSL +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + #include + #include + #include + #include +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include +#endif + +#include +#include + +namespace Botan { + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +namespace { + +int BearSSL_EC_curve_for(const EC_Group& group) + { + if(group == EC_Group("secp256r1")) + return BR_EC_secp256r1; + if(group == EC_Group("secp384r1")) + return BR_EC_secp384r1; + if(group == EC_Group("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 NULL; + } +} + +#endif + +#if defined(BOTAN_HAS_ECDSA) && !defined(OPENSSL_NO_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()); + if (curve < 0) + throw Lookup_Error("BearSSL ECDSA does not support this curve"); + + m_hash = BearSSL_hash_class_for(emsa); + if (m_hash == NULL) + 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 == NULL) + throw Lookup_Error("BearSSL ECDSA does not support hash " + req.arg(0)); + + const secure_vector 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, (unsigned char *)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 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; } + + private: + br_ec_public_key m_key; + std::unique_ptr 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()); + if(curve < 0) + throw Lookup_Error("BearSSL ECDSA does not support this curve"); + + m_hash = BearSSL_hash_class_for(emsa); + if (m_hash == NULL) + 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 == NULL) + 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 sign(RandomNumberGenerator&) override + { + const size_t order_bytes = (m_order_bits + 7) / 8; + secure_vector sigval(2*order_bytes); + size_t sign_len; + + br_ecdsa_sign engine = br_ecdsa_sign_raw_get_default(); + 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; } + + private: + br_ec_private_key m_key; + std::unique_ptr m_hf; + const br_hash_class *m_hash; + size_t m_order_bits; + }; + +} + +std::unique_ptr +make_bearssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params) + { + return std::unique_ptr(new BearSSL_ECDSA_Verification_Operation(key, params)); + } + +std::unique_ptr +make_bearssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params) + { + return std::unique_ptr(new BearSSL_ECDSA_Signing_Operation(key, params)); + } + +#endif + +} 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 #endif +#if defined(BOTAN_HAS_BEARSSL) + #include +#endif + #if defined(BOTAN_HAS_OPENSSL) #include #endif @@ -156,6 +160,21 @@ std::unique_ptr 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 PK_Test::possible_providers(const std::string&) +std::vector 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 -- cgit v1.2.3