diff options
author | Jack Lloyd <[email protected]> | 2017-05-03 10:13:25 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-05-03 10:13:25 -0400 |
commit | 7f4f579db4408253c60c52b8f5bbe2b64aa88f1d (patch) | |
tree | 5c7f9973252360cbea24c50c0f6848efd86df3f2 | |
parent | 7cfdb78e5267ba542e9a8248cbec5f34033b6e42 (diff) | |
parent | 17afb2681aa704d8241f4dcaeb949d806ba8df09 (diff) |
Merge GH #1035 Support generating RSA keys with OpenSSL
-rw-r--r-- | src/lib/prov/openssl/openssl.h | 3 | ||||
-rw-r--r-- | src/lib/prov/openssl/openssl_rsa.cpp | 36 | ||||
-rw-r--r-- | src/lib/pubkey/pk_algs.cpp | 36 | ||||
-rw-r--r-- | src/lib/pubkey/pk_algs.h | 7 | ||||
-rw-r--r-- | src/tests/test_pubkey.cpp | 196 | ||||
-rw-r--r-- | src/tests/test_pubkey.h | 2 | ||||
-rw-r--r-- | src/tests/tests.cpp | 10 | ||||
-rw-r--r-- | src/tests/tests.h | 2 |
8 files changed, 195 insertions, 97 deletions
diff --git a/src/lib/prov/openssl/openssl.h b/src/lib/prov/openssl/openssl.h index 3cd39113b..37e8f9d4b 100644 --- a/src/lib/prov/openssl/openssl.h +++ b/src/lib/prov/openssl/openssl.h @@ -27,6 +27,7 @@ class BlockCipher; class Cipher_Mode; class StreamCipher; class HashFunction; +class RandomNumberGenerator; enum Cipher_Dir : int; class OpenSSL_Error : public Exception @@ -67,6 +68,8 @@ std::unique_ptr<PK_Ops::Verification> make_openssl_rsa_ver_op(const RSA_PublicKey& key, const std::string& params); std::unique_ptr<PK_Ops::Signature> make_openssl_rsa_sig_op(const RSA_PrivateKey& key, const std::string& params); +std::unique_ptr<RSA_PrivateKey> +make_openssl_rsa_private_key(RandomNumberGenerator& rng, size_t rsa_bits); #endif diff --git a/src/lib/prov/openssl/openssl_rsa.cpp b/src/lib/prov/openssl/openssl_rsa.cpp index 22cd6eb96..8c25d00ef 100644 --- a/src/lib/prov/openssl/openssl_rsa.cpp +++ b/src/lib/prov/openssl/openssl_rsa.cpp @@ -1,6 +1,7 @@ /* * RSA operations provided by OpenSSL * (C) 2015 Jack Lloyd +* (C) 2017 Alexander Bluhm * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,6 +11,7 @@ #if defined(BOTAN_HAS_RSA) #include <botan/rsa.h> +#include <botan/rng.h> #include <botan/internal/pk_ops_impl.h> #include <botan/internal/ct_utils.h> @@ -19,6 +21,7 @@ #include <openssl/rsa.h> #include <openssl/x509.h> #include <openssl/err.h> +#include <openssl/rand.h> namespace Botan { @@ -247,6 +250,39 @@ make_openssl_rsa_sig_op(const RSA_PrivateKey& key, const std::string& params) return std::unique_ptr<PK_Ops::Signature>(new OpenSSL_RSA_Signing_Operation(key, params)); } +std::unique_ptr<RSA_PrivateKey> +make_openssl_rsa_private_key(RandomNumberGenerator& rng, size_t rsa_bits) + { + if (rsa_bits > INT_MAX) + throw Internal_Error("rsa_bits overflow"); + + secure_vector<uint8_t> seed(BOTAN_SYSTEM_RNG_POLL_REQUEST); + rng.randomize(seed.data(), seed.size()); + RAND_seed(seed.data(), seed.size()); + + std::unique_ptr<BIGNUM, std::function<void (BIGNUM*)>> bn(BN_new(), BN_free); + if(!bn) + throw OpenSSL_Error("BN_new"); + if(!BN_set_word(bn.get(), RSA_F4)) + throw OpenSSL_Error("BN_set_word"); + + std::unique_ptr<RSA, std::function<void (RSA*)>> rsa(RSA_new(), RSA_free); + if(!rsa) + throw OpenSSL_Error("RSA_new"); + if(!RSA_generate_key_ex(rsa.get(), rsa_bits, bn.get(), NULL)) + throw OpenSSL_Error("RSA_generate_key_ex"); + + uint8_t* der = NULL; + int bytes = i2d_RSAPrivateKey(rsa.get(), &der); + if(bytes < 0) + throw OpenSSL_Error("i2d_RSAPrivateKey"); + + const secure_vector<uint8_t> keydata(der, der + bytes); + memset(der, 0, bytes); + free(der); + return std::unique_ptr<Botan::RSA_PrivateKey> + (new RSA_PrivateKey(AlgorithmIdentifier(), keydata)); + } } #endif // BOTAN_HAS_RSA diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp index 1e1fd739a..19d7361b4 100644 --- a/src/lib/pubkey/pk_algs.cpp +++ b/src/lib/pubkey/pk_algs.cpp @@ -56,6 +56,10 @@ #include <botan/xmss.h> #endif +#if defined(BOTAN_HAS_OPENSSL) + #include <botan/internal/openssl.h> +#endif + namespace Botan { std::unique_ptr<Public_Key> @@ -203,7 +207,8 @@ load_private_key(const AlgorithmIdentifier& alg_id, std::unique_ptr<Private_Key> create_private_key(const std::string& alg_name, RandomNumberGenerator& rng, - const std::string& params) + const std::string& params, + const std::string& provider) { /* * Default paramaters are chosen for work factor > 2**128 where possible @@ -218,6 +223,17 @@ create_private_key(const std::string& alg_name, if(alg_name == "RSA") { const size_t rsa_bits = (params.empty() ? 3072 : to_u32bit(params)); +#if defined(BOTAN_HAS_OPENSSL) + if(provider.empty() || provider == "openssl") + { + std::unique_ptr<Botan::Private_Key> pk; + if(pk = make_openssl_rsa_private_key(rng, rsa_bits)) + return pk; + + if(!provider.empty()) + return nullptr; + } +#endif return std::unique_ptr<Private_Key>(new RSA_PrivateKey(rng, rsa_bits)); } #endif @@ -311,4 +327,22 @@ create_private_key(const std::string& alg_name, return std::unique_ptr<Private_Key>(); } +std::vector<std::string> +probe_provider_private_key(const std::string& alg_name, + const std::vector<std::string> possible) + { + std::vector<std::string> providers; + for(auto&& prov : possible) + { + if(prov == "base" || +#if defined(BOTAN_HAS_OPENSSL) + (prov == "openssl" && alg_name == "RSA") || +#endif + 0) + { + providers.push_back(prov); // available + } + } + return providers; + } } diff --git a/src/lib/pubkey/pk_algs.h b/src/lib/pubkey/pk_algs.h index 04248459b..5deded423 100644 --- a/src/lib/pubkey/pk_algs.h +++ b/src/lib/pubkey/pk_algs.h @@ -33,7 +33,12 @@ load_private_key(const AlgorithmIdentifier& alg_id, BOTAN_DLL std::unique_ptr<Private_Key> create_private_key(const std::string& algo_name, RandomNumberGenerator& rng, - const std::string& algo_params = ""); + const std::string& algo_params = "", + const std::string& provider = ""); + +BOTAN_DLL std::vector<std::string> +probe_provider_private_key(const std::string& algo_name, + const std::vector<std::string> possible); } diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index 95cff74a3..a38b7d3f3 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -402,6 +402,14 @@ Test::Result PK_Key_Agreement_Test::run_one_test(const std::string& header, cons return result; } +std::vector<std::string> PK_Key_Generation_Test::possible_providers( + const std::string& algo) + { + std::vector<std::string> pk_provider = + Botan::probe_provider_private_key(algo, { "base", "openssl", "tpm" }); + return Test::provider_filter(pk_provider); + } + std::vector<Test::Result> PK_Key_Generation_Test::run() { std::vector<Test::Result> results; @@ -412,115 +420,125 @@ std::vector<Test::Result> PK_Key_Generation_Test::run() Test::Result result(report_name + " keygen"); + const std::vector<std::string> providers = possible_providers(algo_name()); + + if(providers.empty()) + { + result.note_missing("provider key generation " + algo_name()); + } + result.start_timer(); - std::unique_ptr<Botan::Private_Key> key_p = - Botan::create_private_key(algo_name(), Test::rng(), param); + for(auto&& prov : providers) + { + std::unique_ptr<Botan::Private_Key> key_p = + Botan::create_private_key(algo_name(), Test::rng(), param, prov); - const Botan::Private_Key& key = *key_p; + const Botan::Private_Key& key = *key_p; - result.confirm("Key passes self tests", key.check_key(Test::rng(), true)); + result.confirm("Key passes self tests", key.check_key(Test::rng(), true)); - result.test_gte("Key has reasonable estimated strength (lower)", key.estimated_strength(), 64); - result.test_lt("Key has reasonable estimated strength (upper)", key.estimated_strength(), 512); + result.test_gte("Key has reasonable estimated strength (lower)", key.estimated_strength(), 64); + result.test_lt("Key has reasonable estimated strength (upper)", key.estimated_strength(), 512); - // Test PEM public key round trips OK - try - { - Botan::DataSource_Memory data_src(Botan::X509::PEM_encode(key)); - std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src)); + // Test PEM public key round trips OK + try + { + Botan::DataSource_Memory data_src(Botan::X509::PEM_encode(key)); + std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src)); - result.confirm("recovered public key from private", loaded.get() != nullptr); - result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); - } - catch(std::exception& e) - { - result.test_failure("roundtrip PEM public key", e.what()); - } + result.confirm("recovered public key from private", loaded.get() != nullptr); + result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip PEM public key", e.what()); + } - // Test DER public key round trips OK - try - { - Botan::DataSource_Memory data_src(Botan::X509::BER_encode(key)); - std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src)); + // Test DER public key round trips OK + try + { + Botan::DataSource_Memory data_src(Botan::X509::BER_encode(key)); + std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src)); - result.confirm("recovered public key from private", loaded.get() != nullptr); - result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); - } - catch(std::exception& e) - { - result.test_failure("roundtrip BER public key", e.what()); - } + result.confirm("recovered public key from private", loaded.get() != nullptr); + result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip BER public key", e.what()); + } - // Test PEM private key round trips OK - try - { - Botan::DataSource_Memory data_src(Botan::PKCS8::PEM_encode(key)); - std::unique_ptr<Botan::Private_Key> loaded( - Botan::PKCS8::load_key(data_src, Test::rng())); + // Test PEM private key round trips OK + try + { + Botan::DataSource_Memory data_src(Botan::PKCS8::PEM_encode(key)); + std::unique_ptr<Botan::Private_Key> loaded( + Botan::PKCS8::load_key(data_src, Test::rng())); - result.confirm("recovered private key from PEM blob", loaded.get() != nullptr); - result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); - } - catch(std::exception& e) - { - result.test_failure("roundtrip PEM private key", e.what()); - } + result.confirm("recovered private key from PEM blob", loaded.get() != nullptr); + result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip PEM private key", e.what()); + } - try - { - Botan::DataSource_Memory data_src(Botan::PKCS8::BER_encode(key)); - std::unique_ptr<Botan::Public_Key> loaded(Botan::PKCS8::load_key(data_src, Test::rng())); + try + { + Botan::DataSource_Memory data_src(Botan::PKCS8::BER_encode(key)); + std::unique_ptr<Botan::Public_Key> loaded(Botan::PKCS8::load_key(data_src, Test::rng())); - result.confirm("recovered public key from private", loaded.get() != nullptr); - result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); - } - catch(std::exception& e) - { - result.test_failure("roundtrip BER private key", e.what()); - } + result.confirm("recovered public key from private", loaded.get() != nullptr); + result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip BER private key", e.what()); + } - const std::string passphrase = Test::random_password(); + const std::string passphrase = Test::random_password(); - try - { - Botan::DataSource_Memory data_src( - Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase, - std::chrono::milliseconds(10))); + try + { + Botan::DataSource_Memory data_src( + Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase, + std::chrono::milliseconds(10))); - std::unique_ptr<Botan::Private_Key> loaded( - Botan::PKCS8::load_key(data_src, Test::rng(), passphrase)); + std::unique_ptr<Botan::Private_Key> loaded( + Botan::PKCS8::load_key(data_src, Test::rng(), passphrase)); - result.confirm("recovered private key from encrypted blob", loaded.get() != nullptr); - result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); - } - catch(std::exception& e) - { - result.test_failure("roundtrip encrypted PEM private key", e.what()); - } + result.confirm("recovered private key from encrypted blob", loaded.get() != nullptr); + result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip encrypted PEM private key", e.what()); + } - try - { - Botan::DataSource_Memory data_src( - Botan::PKCS8::BER_encode(key, Test::rng(), passphrase, - std::chrono::milliseconds(10))); + try + { + Botan::DataSource_Memory data_src( + Botan::PKCS8::BER_encode(key, Test::rng(), passphrase, + std::chrono::milliseconds(10))); - std::unique_ptr<Botan::Private_Key> loaded( - Botan::PKCS8::load_key(data_src, Test::rng(), passphrase)); + std::unique_ptr<Botan::Private_Key> loaded( + Botan::PKCS8::load_key(data_src, Test::rng(), passphrase)); - result.confirm("recovered private key from BER blob", loaded.get() != nullptr); - result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); - } - catch(std::exception& e) - { - result.test_failure("roundtrip encrypted BER private key", e.what()); - } + result.confirm("recovered private key from BER blob", loaded.get() != nullptr); + result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip encrypted BER private key", e.what()); + } + } result.end_timer(); results.push_back(result); diff --git a/src/tests/test_pubkey.h b/src/tests/test_pubkey.h index 88a3d1c45..4a2ca6866 100644 --- a/src/tests/test_pubkey.h +++ b/src/tests/test_pubkey.h @@ -171,6 +171,8 @@ class PK_Key_Generation_Test : public Test virtual std::vector<std::string> keygen_params() const = 0; virtual std::string algo_name() const = 0; + + std::vector<std::string> possible_providers(const std::string&) override; }; void check_invalid_signatures(Test::Result& result, diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 78c579140..2252cb221 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -366,6 +366,11 @@ bool Test::Result::test_rc(const std::string& func, int expected, int rc) return test_success(); } +std::vector<std::string> Test::possible_providers(const std::string&) + { + return Test::provider_filter({ "base" }); + } + //static std::string Test::format_time(uint64_t ns) { @@ -912,11 +917,6 @@ parse_cpuid_bits(const std::vector<std::string>& tok) } -std::vector<std::string> Text_Based_Test::possible_providers(const std::string&) - { - return Test::provider_filter({ "base" }); - } - bool Text_Based_Test::skip_this_test(const std::string& /*header*/, const VarMap& /*vars*/) { diff --git a/src/tests/tests.h b/src/tests/tests.h index 4e992e0d6..0673705d9 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -352,6 +352,7 @@ class Test virtual std::vector<Test::Result> run() = 0; virtual ~Test() = default; + virtual std::vector<std::string> possible_providers(const std::string&); static std::vector<Test::Result> run_test(const std::string& what, bool fail_if_missing); @@ -463,7 +464,6 @@ class Text_Based_Test : public Test virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0; // Called before run_one_test - virtual std::vector<std::string> possible_providers(const std::string&); virtual bool skip_this_test(const std::string& header, const VarMap& vars); |