aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/pubkey
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2015-10-19 19:25:28 -0400
committerJack Lloyd <[email protected]>2015-10-19 19:25:28 -0400
commitdbe3754faf68687ccf58743d6f500d36e6419e77 (patch)
tree7e9e0a0849fc7c3f1b7e90c8723130cbe08d4f82 /src/lib/pubkey
parentb1afb8dbc759653fb53631ff92f7afce6cc9de98 (diff)
Break up openssl provider
For RSA, RC4, and ECDSA put the openssl versions in the same directory as the base version. They just rely on a macro check for the openssl module to test for the desire to use OpenSSL.
Diffstat (limited to 'src/lib/pubkey')
-rw-r--r--src/lib/pubkey/ecdsa/openssl_ecdsa.cpp213
-rw-r--r--src/lib/pubkey/rsa/openssl_rsa.cpp297
2 files changed, 510 insertions, 0 deletions
diff --git a/src/lib/pubkey/ecdsa/openssl_ecdsa.cpp b/src/lib/pubkey/ecdsa/openssl_ecdsa.cpp
new file mode 100644
index 000000000..502702804
--- /dev/null
+++ b/src/lib/pubkey/ecdsa/openssl_ecdsa.cpp
@@ -0,0 +1,213 @@
+/*
+* ECDSA via OpenSSL
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/build.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+
+#include <botan/internal/openssl.h>
+#include <openssl/x509.h>
+
+#if !defined(OPENSSL_NO_ECDSA)
+
+#include <botan/der_enc.h>
+#include <botan/ecdsa.h>
+#include <botan/pkcs8.h>
+#include <botan/oids.h>
+#include <botan/internal/pk_utils.h>
+
+#include <openssl/ecdsa.h>
+#include <openssl/ec.h>
+#include <openssl/objects.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<byte> PKCS8_for_openssl(const EC_PrivateKey& ec)
+ {
+ const PointGFp& pub_key = ec.public_point();
+ const BigInt& priv_key = ec.private_value();
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(1))
+ .encode(BigInt::encode_1363(priv_key, priv_key.bytes()), OCTET_STRING)
+ .start_cons(ASN1_Tag(0), PRIVATE)
+ .raw_bytes(ec.domain().DER_encode(EC_DOMPAR_ENC_OID))
+ .end_cons()
+ .start_cons(ASN1_Tag(1), PRIVATE)
+ .encode(EC2OSP(pub_key, PointGFp::UNCOMPRESSED), BIT_STRING)
+ .end_cons()
+ .end_cons()
+ .get_contents();
+ }
+
+int OpenSSL_EC_nid_for(const OID& oid)
+ {
+ if(oid.empty())
+ return -1;
+
+ static const std::map<std::string, int> nid_map = {
+ //{ "secp160r1", NID_secp160r1 },
+ //{ "secp160r2", NID_secp160r2 },
+ { "secp192r1", NID_X9_62_prime192v1 },
+ { "secp224r1", NID_secp224r1 },
+ { "secp256r1", NID_X9_62_prime256v1 },
+ { "secp384r1", NID_secp384r1 },
+ { "secp521r1", NID_secp521r1 }
+ // TODO: OpenSSL 1.0.2 added brainpool curves
+ };
+
+ const std::string name = OIDS::lookup(oid);
+ auto i = nid_map.find(name);
+ if(i != nid_map.end())
+ return i->second;
+
+ return -1;
+ }
+
+class OpenSSL_ECDSA_Verification_Operation : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+ typedef ECDSA_PublicKey Key_Type;
+
+ static OpenSSL_ECDSA_Verification_Operation* make(const Spec& spec)
+ {
+ if(const ECDSA_PublicKey* ecdsa = dynamic_cast<const ECDSA_PublicKey*>(&spec.key()))
+ {
+ const int nid = OpenSSL_EC_nid_for(ecdsa->domain().get_oid());
+ if(nid > 0)
+ return new OpenSSL_ECDSA_Verification_Operation(*ecdsa, spec.padding(), nid);
+ }
+
+ return nullptr;
+ }
+
+ OpenSSL_ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const std::string& emsa, int nid) :
+ PK_Ops::Verification_with_EMSA(emsa), m_ossl_ec(::EC_KEY_new(), ::EC_KEY_free)
+ {
+ std::unique_ptr<::EC_GROUP, std::function<void (::EC_GROUP*)>> grp(::EC_GROUP_new_by_curve_name(nid),
+ ::EC_GROUP_free);
+
+ if(!grp)
+ throw OpenSSL_Error("EC_GROUP_new_by_curve_name");
+
+ ::EC_KEY_set_group(m_ossl_ec.get(), grp.get());
+
+ const secure_vector<byte> enc = EC2OSP(ecdsa.public_point(), PointGFp::UNCOMPRESSED);
+ const byte* enc_ptr = enc.data();
+ EC_KEY* key_ptr = m_ossl_ec.get();
+ if(!::o2i_ECPublicKey(&key_ptr, &enc_ptr, enc.size()))
+ throw OpenSSL_Error("o2i_ECPublicKey");
+
+ const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+ m_order_bits = ::EC_GROUP_get_degree(group);
+ }
+
+ size_t message_parts() const override { return 2; }
+ size_t message_part_size() const override { return (m_order_bits + 7) / 8; }
+ size_t max_input_bits() const override { return m_order_bits; }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const byte msg[], size_t msg_len,
+ const byte sig_bytes[], size_t sig_len) override
+ {
+ if(sig_len != message_part_size() * message_parts())
+ return false;
+
+ std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free);
+ sig.reset(::ECDSA_SIG_new());
+
+ sig->r = BN_bin2bn(sig_bytes , sig_len / 2, nullptr);
+ sig->s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, nullptr);
+
+ const int res = ECDSA_do_verify(msg, msg_len, sig.get(), m_ossl_ec.get());
+ if(res < 0)
+ throw OpenSSL_Error("ECDSA_do_verify");
+ return (res == 1);
+ }
+
+ private:
+ std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+ size_t m_order_bits = 0;
+ };
+
+class OpenSSL_ECDSA_Signing_Operation : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+ typedef ECDSA_PrivateKey Key_Type;
+
+ static OpenSSL_ECDSA_Signing_Operation* make(const Spec& spec)
+ {
+ if(const ECDSA_PrivateKey* ecdsa = dynamic_cast<const ECDSA_PrivateKey*>(&spec.key()))
+ {
+ const int nid = OpenSSL_EC_nid_for(ecdsa->domain().get_oid());
+ if(nid > 0)
+ return new OpenSSL_ECDSA_Signing_Operation(*ecdsa, spec.padding());
+ }
+
+ return nullptr;
+ }
+
+ OpenSSL_ECDSA_Signing_Operation(const ECDSA_PrivateKey& ecdsa, const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_ossl_ec(nullptr, ::EC_KEY_free)
+ {
+ const secure_vector<byte> der = PKCS8_for_openssl(ecdsa);
+ const byte* der_ptr = der.data();
+ m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_ossl_ec)
+ throw OpenSSL_Error("d2i_ECPrivateKey");
+
+ const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+ m_order_bits = ::EC_GROUP_get_degree(group);
+ }
+
+ secure_vector<byte> raw_sign(const byte msg[], size_t msg_len,
+ RandomNumberGenerator&) override
+ {
+ std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free);
+ sig.reset(::ECDSA_do_sign(msg, msg_len, m_ossl_ec.get()));
+
+ if(!sig)
+ throw OpenSSL_Error("ECDSA_do_sign");
+
+ const size_t order_bytes = message_part_size();
+ const size_t r_bytes = BN_num_bytes(sig->r);
+ const size_t s_bytes = BN_num_bytes(sig->s);
+ secure_vector<byte> sigval(2*order_bytes);
+ BN_bn2bin(sig->r, &sigval[order_bytes - r_bytes]);
+ BN_bn2bin(sig->s, &sigval[2*order_bytes - s_bytes]);
+ return sigval;
+ }
+
+ size_t message_parts() const override { return 2; }
+ size_t message_part_size() const override { return (m_order_bits + 7) / 8; }
+ size_t max_input_bits() const override { return m_order_bits; }
+
+ private:
+ std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+ size_t m_order_bits = 0;
+ };
+
+BOTAN_REGISTER_TYPE(PK_Ops::Verification, OpenSSL_ECDSA_Verification_Operation, "ECDSA",
+ OpenSSL_ECDSA_Verification_Operation::make,
+ "openssl", BOTAN_OPENSSL_ECDSA_PRIO);
+
+BOTAN_REGISTER_TYPE(PK_Ops::Signature, OpenSSL_ECDSA_Signing_Operation, "ECDSA",
+ OpenSSL_ECDSA_Signing_Operation::make,
+ "openssl", BOTAN_OPENSSL_ECDSA_PRIO);
+
+}
+
+}
+
+#endif
+
+#endif
diff --git a/src/lib/pubkey/rsa/openssl_rsa.cpp b/src/lib/pubkey/rsa/openssl_rsa.cpp
new file mode 100644
index 000000000..f2825634a
--- /dev/null
+++ b/src/lib/pubkey/rsa/openssl_rsa.cpp
@@ -0,0 +1,297 @@
+/*
+* RSA operations provided by OpenSSL
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rsa.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+
+#include <botan/internal/openssl.h>
+#include <botan/internal/pk_utils.h>
+#include <functional>
+#include <memory>
+
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+
+namespace Botan {
+
+namespace {
+
+std::pair<int, size_t> get_openssl_enc_pad(const std::string& eme)
+ {
+ ERR_load_crypto_strings();
+ if(eme == "Raw")
+ return std::make_pair(RSA_NO_PADDING, 0);
+ else if(eme == "EME-PKCS1-v1_5")
+ return std::make_pair(RSA_PKCS1_PADDING, 11);
+ else if(eme == "OAEP(SHA-1)")
+ return std::make_pair(RSA_PKCS1_OAEP_PADDING, 41);
+ else
+ throw Lookup_Error("OpenSSL RSA does not support EME " + eme);
+ }
+
+secure_vector<byte> strip_leading_zeros(const secure_vector<byte>& input)
+ {
+ size_t leading_zeros = 0;
+
+ for(size_t i = 0; i != input.size(); ++i)
+ {
+ if(input[i] != 0)
+ break;
+ ++leading_zeros;
+ }
+
+ secure_vector<byte> output(&input[leading_zeros],
+ &input[input.size()]);
+ return output;
+ }
+
+class OpenSSL_RSA_Encryption_Operation : public PK_Ops::Encryption
+ {
+ public:
+ typedef RSA_PublicKey Key_Type;
+
+ static OpenSSL_RSA_Encryption_Operation* make(const Spec& spec)
+ {
+ try
+ {
+ if(auto* key = dynamic_cast<const RSA_PublicKey*>(&spec.key()))
+ {
+ auto pad_info = get_openssl_enc_pad(spec.padding());
+ return new OpenSSL_RSA_Encryption_Operation(*key, pad_info.first, pad_info.second);
+ }
+ }
+ catch(...) {}
+
+ return nullptr;
+ }
+
+ OpenSSL_RSA_Encryption_Operation(const RSA_PublicKey& rsa, int pad, size_t pad_overhead) :
+ m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad)
+ {
+ const std::vector<byte> der = rsa.x509_subject_public_key();
+ const byte* der_ptr = der.data();
+ m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPublicKey");
+
+ m_bits = 8 * (n_size() - pad_overhead) - 1;
+ }
+
+ size_t max_input_bits() const override { return m_bits; };
+
+ secure_vector<byte> encrypt(const byte msg[], size_t msg_len,
+ RandomNumberGenerator&) override
+ {
+ const size_t mod_sz = n_size();
+
+ if(msg_len > mod_sz)
+ throw Invalid_Argument("Input too large for RSA key");
+
+ secure_vector<byte> outbuf(mod_sz);
+
+ secure_vector<byte> inbuf;
+
+ if(m_padding == RSA_NO_PADDING)
+ {
+ inbuf.resize(mod_sz);
+ copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+ }
+ else
+ {
+ inbuf.assign(msg, msg + msg_len);
+ }
+
+ int rc = ::RSA_public_encrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+ m_openssl_rsa.get(), m_padding);
+ if(rc < 0)
+ throw OpenSSL_Error("RSA_public_encrypt");
+
+ return outbuf;
+ }
+
+ private:
+ size_t n_size() const { return ::RSA_size(m_openssl_rsa.get()); }
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ size_t m_bits = 0;
+ int m_padding = 0;
+ };
+
+class OpenSSL_RSA_Decryption_Operation : public PK_Ops::Decryption
+ {
+ public:
+ typedef RSA_PrivateKey Key_Type;
+
+ static OpenSSL_RSA_Decryption_Operation* make(const Spec& spec)
+ {
+ try
+ {
+ if(auto* key = dynamic_cast<const RSA_PrivateKey*>(&spec.key()))
+ {
+ auto pad_info = get_openssl_enc_pad(spec.padding());
+ return new OpenSSL_RSA_Decryption_Operation(*key, pad_info.first);
+ }
+ }
+ catch(...) {}
+
+ return nullptr;
+ }
+
+ OpenSSL_RSA_Decryption_Operation(const RSA_PrivateKey& rsa, int pad) :
+ m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad)
+ {
+ const secure_vector<byte> der = rsa.pkcs8_private_key();
+ const byte* der_ptr = der.data();
+ m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPrivateKey");
+ }
+
+ size_t max_input_bits() const override { return ::BN_num_bits(m_openssl_rsa->n) - 1; }
+
+ secure_vector<byte> decrypt(const byte msg[], size_t msg_len) override
+ {
+ secure_vector<byte> buf(::RSA_size(m_openssl_rsa.get()));
+ int rc = ::RSA_private_decrypt(msg_len, msg, buf.data(), m_openssl_rsa.get(), m_padding);
+ if(rc < 0 || static_cast<size_t>(rc) > buf.size())
+ throw OpenSSL_Error("RSA_private_decrypt");
+ buf.resize(rc);
+
+ if(m_padding == RSA_NO_PADDING)
+ {
+ return strip_leading_zeros(buf);
+ }
+ return buf;
+ }
+
+ private:
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ int m_padding = 0;
+ };
+
+class OpenSSL_RSA_Verification_Operation : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+ typedef RSA_PublicKey Key_Type;
+
+ static OpenSSL_RSA_Verification_Operation* make(const Spec& spec)
+ {
+ if(const RSA_PublicKey* rsa = dynamic_cast<const RSA_PublicKey*>(&spec.key()))
+ {
+ return new OpenSSL_RSA_Verification_Operation(*rsa, spec.padding());
+ }
+
+ return nullptr;
+ }
+
+ OpenSSL_RSA_Verification_Operation(const RSA_PublicKey& rsa, const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_openssl_rsa(nullptr, ::RSA_free)
+ {
+ const std::vector<byte> der = rsa.x509_subject_public_key();
+ const byte* der_ptr = der.data();
+ m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size()));
+ }
+
+ size_t max_input_bits() const override { return ::BN_num_bits(m_openssl_rsa->n) - 1; }
+
+ bool with_recovery() const override { return true; }
+
+ secure_vector<byte> verify_mr(const byte msg[], size_t msg_len) override
+ {
+ const size_t mod_sz = ::RSA_size(m_openssl_rsa.get());
+
+ if(msg_len > mod_sz)
+ throw Invalid_Argument("OpenSSL RSA verify input too large");
+
+ secure_vector<byte> inbuf(mod_sz);
+ copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+
+ secure_vector<byte> outbuf(mod_sz);
+
+ int rc = ::RSA_public_decrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+ m_openssl_rsa.get(), RSA_NO_PADDING);
+ if(rc < 0)
+ throw Invalid_Argument("RSA_public_decrypt");
+
+ return strip_leading_zeros(outbuf);
+ }
+ private:
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ };
+
+class OpenSSL_RSA_Signing_Operation : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+ typedef RSA_PrivateKey Key_Type;
+
+ static OpenSSL_RSA_Signing_Operation* make(const Spec& spec)
+ {
+ if(const RSA_PrivateKey* rsa = dynamic_cast<const RSA_PrivateKey*>(&spec.key()))
+ {
+ return new OpenSSL_RSA_Signing_Operation(*rsa, spec.padding());
+ }
+
+ return nullptr;
+ }
+
+ OpenSSL_RSA_Signing_Operation(const RSA_PrivateKey& rsa, const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_openssl_rsa(nullptr, ::RSA_free)
+ {
+ const secure_vector<byte> der = rsa.pkcs8_private_key();
+ const byte* der_ptr = der.data();
+ m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPrivateKey");
+ }
+
+ secure_vector<byte> raw_sign(const byte msg[], size_t msg_len,
+ RandomNumberGenerator&) override
+ {
+ const size_t mod_sz = ::RSA_size(m_openssl_rsa.get());
+
+ if(msg_len > mod_sz)
+ throw Invalid_Argument("OpenSSL RSA sign input too large");
+
+ secure_vector<byte> inbuf(mod_sz);
+ copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+
+ secure_vector<byte> outbuf(mod_sz);
+
+ int rc = ::RSA_private_encrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+ m_openssl_rsa.get(), RSA_NO_PADDING);
+ if(rc < 0)
+ throw OpenSSL_Error("RSA_private_encrypt");
+
+ return outbuf;
+ }
+
+ size_t max_input_bits() const override { return ::BN_num_bits(m_openssl_rsa->n) - 1; }
+
+ private:
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ };
+
+BOTAN_REGISTER_TYPE(PK_Ops::Verification, OpenSSL_RSA_Verification_Operation, "RSA",
+ OpenSSL_RSA_Verification_Operation::make, "openssl", BOTAN_OPENSSL_RSA_PRIO);
+
+BOTAN_REGISTER_TYPE(PK_Ops::Signature, OpenSSL_RSA_Signing_Operation, "RSA",
+ OpenSSL_RSA_Signing_Operation::make, "openssl", BOTAN_OPENSSL_RSA_PRIO);
+
+BOTAN_REGISTER_TYPE(PK_Ops::Encryption, OpenSSL_RSA_Encryption_Operation, "RSA",
+ OpenSSL_RSA_Encryption_Operation::make, "openssl", BOTAN_OPENSSL_RSA_PRIO);
+
+BOTAN_REGISTER_TYPE(PK_Ops::Decryption, OpenSSL_RSA_Decryption_Operation, "RSA",
+ OpenSSL_RSA_Decryption_Operation::make, "openssl", BOTAN_OPENSSL_RSA_PRIO);
+
+}
+
+}
+
+#endif // BOTAN_HAS_OPENSSL