diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/prov/openssl')
8 files changed, 1530 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/prov/openssl/info.txt b/comm/third_party/botan/src/lib/prov/openssl/info.txt new file mode 100644 index 0000000000..32e71a8481 --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/info.txt @@ -0,0 +1,21 @@ +<defines> +OPENSSL -> 20151219 +</defines> + +load_on vendor + +<header:internal> +openssl.h +</header:internal> + +<libs> +all!windows -> crypto +windows -> libeay32 +</libs> + +<requires> +block +stream +modes +pubkey +</requires> diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl.h b/comm/third_party/botan/src/lib/prov/openssl/openssl.h new file mode 100644 index 0000000000..a68dda5af0 --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl.h @@ -0,0 +1,118 @@ +/* +* Utils for calling OpenSSL +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_INTERNAL_OPENSSL_H_ +#define BOTAN_INTERNAL_OPENSSL_H_ + +#include <botan/pk_ops_fwd.h> +#include <botan/secmem.h> +#include <botan/exceptn.h> +#include <memory> +#include <string> + +#include <openssl/err.h> +#include <openssl/evp.h> + +#if defined(BOTAN_HAS_RC4) +#include <openssl/rc4.h> +#endif + +namespace Botan { + +class BlockCipher; +class Cipher_Mode; +class StreamCipher; +class HashFunction; +class RandomNumberGenerator; +enum Cipher_Dir : int; + +class BOTAN_PUBLIC_API(2,0) OpenSSL_Error final : public Exception + { + public: + OpenSSL_Error(const std::string& what, int err) : + Exception(what + " failed: " + ERR_error_string(err, nullptr)), + m_err(err) {} + + ErrorType error_type() const noexcept override { return ErrorType::OpenSSLError; } + + int error_code() const noexcept override { return m_err; } + + private: + int m_err; + }; + +/* Block Ciphers */ + +std::unique_ptr<BlockCipher> +make_openssl_block_cipher(const std::string& name); + +/* Cipher Modes */ + +Cipher_Mode* +make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction); + +/* Hash */ + +std::unique_ptr<HashFunction> +make_openssl_hash(const std::string& name); + +/* RSA */ + +#if defined(BOTAN_HAS_RSA) + +class RSA_PublicKey; +class RSA_PrivateKey; + +std::unique_ptr<PK_Ops::Encryption> +make_openssl_rsa_enc_op(const RSA_PublicKey& key, const std::string& params); +std::unique_ptr<PK_Ops::Decryption> +make_openssl_rsa_dec_op(const RSA_PrivateKey& key, const std::string& params); + +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 + +/* ECDSA */ + +#if defined(BOTAN_HAS_ECDSA) + +class ECDSA_PublicKey; +class ECDSA_PrivateKey; + +std::unique_ptr<PK_Ops::Verification> +make_openssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params); +std::unique_ptr<PK_Ops::Signature> +make_openssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params); + +#endif + +/* ECDH */ + +#if defined(BOTAN_HAS_ECDH) + +class ECDH_PrivateKey; + +std::unique_ptr<PK_Ops::Key_Agreement> +make_openssl_ecdh_ka_op(const ECDH_PrivateKey& key, const std::string& params); + +#endif + +#if defined(BOTAN_HAS_RC4) + +std::unique_ptr<StreamCipher> +make_openssl_rc4(size_t skip); + +#endif + +} + +#endif diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp new file mode 100644 index 0000000000..7f9bdcf6c5 --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp @@ -0,0 +1,234 @@ +/* +* Block Ciphers via OpenSSL +* (C) 1999-2010,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/block_cipher.h> +#include <botan/internal/openssl.h> +#include <openssl/evp.h> + +namespace Botan { + +namespace { + +class OpenSSL_BlockCipher final : public BlockCipher + { + public: + OpenSSL_BlockCipher(const std::string& name, + const EVP_CIPHER* cipher); + + OpenSSL_BlockCipher(const std::string& name, + const EVP_CIPHER* cipher, + size_t kl_min, size_t kl_max, size_t kl_mod); + + ~OpenSSL_BlockCipher(); + + void clear() override; + std::string provider() const override { return "openssl"; } + std::string name() const override { return m_cipher_name; } + BlockCipher* clone() const override; + + size_t block_size() const override { return m_block_sz; } + + Key_Length_Specification key_spec() const override { return m_cipher_key_spec; } + + void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override + { + verify_key_set(m_key_set); + int out_len = 0; + if(!EVP_EncryptUpdate(m_encrypt, out, &out_len, in, blocks * m_block_sz)) + throw OpenSSL_Error("EVP_EncryptUpdate", ERR_get_error()); + } + + void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override + { + verify_key_set(m_key_set); + int out_len = 0; + if(!EVP_DecryptUpdate(m_decrypt, out, &out_len, in, blocks * m_block_sz)) + throw OpenSSL_Error("EVP_DecryptUpdate", ERR_get_error()); + } + + void key_schedule(const uint8_t key[], size_t key_len) override; + + size_t m_block_sz; + Key_Length_Specification m_cipher_key_spec; + std::string m_cipher_name; + EVP_CIPHER_CTX *m_encrypt; + EVP_CIPHER_CTX *m_decrypt; + bool m_key_set; + }; + +OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name, + const EVP_CIPHER* algo) : + m_block_sz(EVP_CIPHER_block_size(algo)), + m_cipher_key_spec(EVP_CIPHER_key_length(algo)), + m_cipher_name(algo_name), + m_key_set(false) + { + if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE) + throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in"); + + m_encrypt = EVP_CIPHER_CTX_new(); + m_decrypt = EVP_CIPHER_CTX_new(); + if (m_encrypt == nullptr || m_decrypt == nullptr) + throw OpenSSL_Error("Can't allocate new context", ERR_get_error()); + + EVP_CIPHER_CTX_init(m_encrypt); + EVP_CIPHER_CTX_init(m_decrypt); + + if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + + if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error()); + } + +OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name, + const EVP_CIPHER* algo, + size_t key_min, + size_t key_max, + size_t key_mod) : + m_block_sz(EVP_CIPHER_block_size(algo)), + m_cipher_key_spec(key_min, key_max, key_mod), + m_cipher_name(algo_name), + m_key_set(false) + { + if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE) + throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in"); + + m_encrypt = EVP_CIPHER_CTX_new(); + m_decrypt = EVP_CIPHER_CTX_new(); + if (m_encrypt == nullptr || m_decrypt == nullptr) + throw OpenSSL_Error("Can't allocate new context", ERR_get_error()); + + EVP_CIPHER_CTX_init(m_encrypt); + EVP_CIPHER_CTX_init(m_decrypt); + + if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + + if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error()); + } + +OpenSSL_BlockCipher::~OpenSSL_BlockCipher() + { + EVP_CIPHER_CTX_cleanup(m_encrypt); + EVP_CIPHER_CTX_cleanup(m_decrypt); + + EVP_CIPHER_CTX_free(m_encrypt); + EVP_CIPHER_CTX_free(m_decrypt); + } + +/* +* Set the key +*/ +void OpenSSL_BlockCipher::key_schedule(const uint8_t key[], size_t length) + { + secure_vector<uint8_t> full_key(key, key + length); + + if(m_cipher_name == "TripleDES" && length == 16) + { + full_key += std::make_pair(key, 8); + } + else + { + if(EVP_CIPHER_CTX_set_key_length(m_encrypt, length) == 0 || + EVP_CIPHER_CTX_set_key_length(m_decrypt, length) == 0) + throw Invalid_Argument("OpenSSL_BlockCipher: Bad key length for " + + m_cipher_name); + } + + if(!EVP_EncryptInit_ex(m_encrypt, nullptr, nullptr, full_key.data(), nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, nullptr, nullptr, full_key.data(), nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + + m_key_set = true; + } + +/* +* Return a clone of this object +*/ +BlockCipher* OpenSSL_BlockCipher::clone() const + { + return new OpenSSL_BlockCipher(m_cipher_name, + EVP_CIPHER_CTX_cipher(m_encrypt), + m_cipher_key_spec.minimum_keylength(), + m_cipher_key_spec.maximum_keylength(), + m_cipher_key_spec.keylength_multiple()); + } + +/* +* Clear memory of sensitive data +*/ +void OpenSSL_BlockCipher::clear() + { + const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_encrypt); + + m_key_set = false; + + if(!EVP_CIPHER_CTX_cleanup(m_encrypt)) + throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_cleanup(m_decrypt)) + throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup decrypt", ERR_get_error()); + EVP_CIPHER_CTX_init(m_encrypt); + EVP_CIPHER_CTX_init(m_decrypt); + if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error()); + } + +} + +std::unique_ptr<BlockCipher> +make_openssl_block_cipher(const std::string& name) + { +#define MAKE_OPENSSL_BLOCK(evp_fn) \ + std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn())) +#define MAKE_OPENSSL_BLOCK_KEYLEN(evp_fn, kl_min, kl_max, kl_mod) \ + std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn(), kl_min, kl_max, kl_mod)) + +#if defined(BOTAN_HAS_AES) && !defined(OPENSSL_NO_AES) + if(name == "AES-128") + return MAKE_OPENSSL_BLOCK(EVP_aes_128_ecb); + if(name == "AES-192") + return MAKE_OPENSSL_BLOCK(EVP_aes_192_ecb); + if(name == "AES-256") + return MAKE_OPENSSL_BLOCK(EVP_aes_256_ecb); +#endif + +#if defined(BOTAN_HAS_CAMELLIA) && !defined(OPENSSL_NO_CAMELLIA) + if(name == "Camellia-128") + return MAKE_OPENSSL_BLOCK(EVP_camellia_128_ecb); + if(name == "Camellia-192") + return MAKE_OPENSSL_BLOCK(EVP_camellia_192_ecb); + if(name == "Camellia-256") + return MAKE_OPENSSL_BLOCK(EVP_camellia_256_ecb); +#endif + +#if defined(BOTAN_HAS_DES) && !defined(OPENSSL_NO_DES) + if(name == "TripleDES") + return MAKE_OPENSSL_BLOCK_KEYLEN(EVP_des_ede3_ecb, 16, 24, 8); +#endif + + return nullptr; + } + +} + diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp new file mode 100644 index 0000000000..3f691f68ac --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp @@ -0,0 +1,383 @@ +/* +* ECDSA and ECDH via OpenSSL +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/openssl.h> + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + #include <botan/der_enc.h> + #include <botan/pkcs8.h> + #include <botan/internal/pk_ops_impl.h> +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include <botan/ecdsa.h> +#endif + +#if defined(BOTAN_HAS_ECDH) + #include <botan/ecdh.h> +#endif + +#include <openssl/x509.h> +#include <openssl/objects.h> + +#if !defined(OPENSSL_NO_EC) + #include <openssl/ec.h> +#endif + +#if !defined(OPENSSL_NO_ECDSA) + #include <openssl/ecdsa.h> +#endif + +#if !defined(OPENSSL_NO_ECDH) + #include <openssl/ecdh.h> +#endif + +namespace Botan { + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +namespace { + +secure_vector<uint8_t> 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(pub_key.encode(PointGFp::UNCOMPRESSED), BIT_STRING) + .end_cons() + .end_cons() + .get_contents(); + } + +int OpenSSL_EC_curve_builtin(int nid) + { + // the NID macro is still defined even though the curve may not be + // supported, so we need to check the list of builtin curves at runtime + EC_builtin_curve builtin_curves[100]; + size_t num = 0; + + if (!(num = EC_get_builtin_curves(builtin_curves, sizeof(builtin_curves)))) + { + return -1; + } + + for(size_t i = 0; i < num; ++i) + { + if(builtin_curves[i].nid == nid) + { + return nid; + } + } + + return -1; + } + +int OpenSSL_EC_nid_for(const OID& oid) + { + if(oid.empty()) + return -1; + + const std::string name = oid.to_formatted_string(); + + if(name == "secp192r1") + return OpenSSL_EC_curve_builtin(NID_X9_62_prime192v1); + if(name == "secp224r1") + return OpenSSL_EC_curve_builtin(NID_secp224r1); + if(name == "secp256r1") + return OpenSSL_EC_curve_builtin(NID_X9_62_prime256v1); + if(name == "secp384r1") + return OpenSSL_EC_curve_builtin(NID_secp384r1); + if(name == "secp521r1") + return OpenSSL_EC_curve_builtin(NID_secp521r1); + + // OpenSSL 1.0.2 added brainpool curves +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + if(name == "brainpool160r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP160r1); + if(name == "brainpool192r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP192r1); + if(name == "brainpool224r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP224r1); + if(name == "brainpool256r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP256r1); + if(name == "brainpool320r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP320r1); + if(name == "brainpool384r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP384r1); + if(name == "brainpool512r1") + return OpenSSL_EC_curve_builtin(NID_brainpoolP512r1); +#endif + + return -1; + } + +} + +#endif + +#if defined(BOTAN_HAS_ECDSA) && !defined(OPENSSL_NO_ECDSA) + +namespace { + +class OpenSSL_ECDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA + { + public: + 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", ERR_get_error()); + + if(!::EC_KEY_set_group(m_ossl_ec.get(), grp.get())) + throw OpenSSL_Error("EC_KEY_set_group", ERR_get_error()); + + const std::vector<uint8_t> enc = ecdsa.public_point().encode(PointGFp::UNCOMPRESSED); + const uint8_t* 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", ERR_get_error()); + + const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get()); + m_order_bits = ::EC_GROUP_get_degree(group); + } + + size_t max_input_bits() const override { return m_order_bits; } + + bool with_recovery() const override { return false; } + + bool verify(const uint8_t msg[], size_t msg_len, + const uint8_t sig_bytes[], size_t sig_len) override + { + const size_t order_bytes = (m_order_bits + 7) / 8; + if(sig_len != 2 * order_bytes) + return false; + + std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free); + sig.reset(::ECDSA_SIG_new()); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + sig->r = BN_bin2bn(sig_bytes , sig_len / 2, sig->r); + sig->s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, sig->s); +#else + BIGNUM* r = BN_bin2bn(sig_bytes , sig_len / 2, nullptr); + BIGNUM* s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, nullptr); + if(r == nullptr || s == nullptr) + throw OpenSSL_Error("BN_bin2bn sig s", ERR_get_error()); + + ECDSA_SIG_set0(sig.get(), r, s); +#endif + + const int res = ECDSA_do_verify(msg, msg_len, sig.get(), m_ossl_ec.get()); + if(res < 0) + { + int err = ERR_get_error(); + + bool hard_error = true; + +#if defined(EC_R_BAD_SIGNATURE) + if(ERR_GET_REASON(err) == EC_R_BAD_SIGNATURE) + hard_error = false; +#endif +#if defined(EC_R_POINT_AT_INFINITY) + if(ERR_GET_REASON(err) == EC_R_POINT_AT_INFINITY) + hard_error = false; +#endif +#if defined(ECDSA_R_BAD_SIGNATURE) + if(ERR_GET_REASON(err) == ECDSA_R_BAD_SIGNATURE) + hard_error = false; +#endif + + if(hard_error) + throw OpenSSL_Error("ECDSA_do_verify", err); + } + 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 final : public PK_Ops::Signature_with_EMSA + { + public: + 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<uint8_t> der = PKCS8_for_openssl(ecdsa); + const uint8_t* der_ptr = der.data(); + m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size())); + if(!m_ossl_ec) + throw OpenSSL_Error("d2i_ECPrivateKey", ERR_get_error()); + + const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get()); + m_order_bits = ::EC_GROUP_get_degree(group); + m_order_bytes = (m_order_bits + 7) / 8; + } + + size_t signature_length() const override { return 2*m_order_bytes; } + + secure_vector<uint8_t> raw_sign(const uint8_t 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", ERR_get_error()); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + const BIGNUM* r = sig->r; + const BIGNUM* s = sig->s; +#else + const BIGNUM* r; + const BIGNUM* s; + ECDSA_SIG_get0(sig.get(), &r, &s); +#endif + + const size_t r_bytes = BN_num_bytes(r); + const size_t s_bytes = BN_num_bytes(s); + secure_vector<uint8_t> sigval(2*m_order_bytes); + BN_bn2bin(r, &sigval[m_order_bytes - r_bytes]); + BN_bn2bin(s, &sigval[2*m_order_bytes - s_bytes]); + return sigval; + } + + 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; + size_t m_order_bytes; + }; + +} + +std::unique_ptr<PK_Ops::Verification> +make_openssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params) + { + const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid()); + if(nid < 0) + { + throw Lookup_Error("OpenSSL ECDSA does not support this curve"); + } + + try + { + return std::unique_ptr<PK_Ops::Verification>(new OpenSSL_ECDSA_Verification_Operation(key, params, nid)); + } + catch(OpenSSL_Error&) + { + throw Lookup_Error("OpenSSL ECDSA does not support this key"); + } + } + +std::unique_ptr<PK_Ops::Signature> +make_openssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params) + { + const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid()); + if(nid < 0) + { + throw Lookup_Error("OpenSSL ECDSA does not support this curve"); + } + return std::unique_ptr<PK_Ops::Signature>(new OpenSSL_ECDSA_Signing_Operation(key, params)); + } + +#endif + +#if defined(BOTAN_HAS_ECDH) && !defined(OPENSSL_NO_ECDH) + +namespace { + +class OpenSSL_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF + { + public: + + OpenSSL_ECDH_KA_Operation(const ECDH_PrivateKey& ecdh, const std::string& kdf) : + PK_Ops::Key_Agreement_with_KDF(kdf), m_ossl_ec(::EC_KEY_new(), ::EC_KEY_free) + { + m_value_size = ecdh.domain().get_p_bytes(); + const secure_vector<uint8_t> der = PKCS8_for_openssl(ecdh); + const uint8_t* der_ptr = der.data(); + m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size())); + if(!m_ossl_ec) + throw OpenSSL_Error("d2i_ECPrivateKey", ERR_get_error()); + } + + size_t agreed_value_size() const override { return m_value_size; } + + secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override + { + const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get()); + const size_t out_len = (::EC_GROUP_get_degree(group) + 7) / 8; + secure_vector<uint8_t> out(out_len); + + std::unique_ptr<EC_POINT, std::function<void (EC_POINT*)>> pub_key( + ::EC_POINT_new(group), ::EC_POINT_free); + + if(!pub_key) + throw OpenSSL_Error("EC_POINT_new", ERR_get_error()); + + const int os2ecp_rc = + ::EC_POINT_oct2point(group, pub_key.get(), w, w_len, nullptr); + + if(os2ecp_rc != 1) + throw OpenSSL_Error("EC_POINT_oct2point", ERR_get_error()); + + const int ecdh_rc = ::ECDH_compute_key(out.data(), + out.size(), + pub_key.get(), + m_ossl_ec.get(), + /*KDF*/nullptr); + + if(ecdh_rc <= 0) + throw OpenSSL_Error("ECDH_compute_key", ERR_get_error()); + + const size_t ecdh_sz = static_cast<size_t>(ecdh_rc); + + if(ecdh_sz > out.size()) + throw Internal_Error("OpenSSL ECDH returned more than requested"); + + out.resize(ecdh_sz); + return out; + } + + private: + std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec; + size_t m_value_size; + }; + +} + +std::unique_ptr<PK_Ops::Key_Agreement> +make_openssl_ecdh_ka_op(const ECDH_PrivateKey& key, const std::string& params) + { + const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid()); + if(nid < 0) + { + throw Lookup_Error("OpenSSL ECDH does not support this curve"); + } + + return std::unique_ptr<PK_Ops::Key_Agreement>(new OpenSSL_ECDH_KA_Operation(key, params)); + } + +#endif + +} + diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp new file mode 100644 index 0000000000..9a56228c7b --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp @@ -0,0 +1,136 @@ +/* +* OpenSSL Hash Functions +* (C) 1999-2007,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/hash.h> +#include <botan/internal/openssl.h> +#include <openssl/evp.h> +#include <unordered_map> + +namespace Botan { + +namespace { + +class OpenSSL_HashFunction final : public HashFunction + { + public: + void clear() override + { + const EVP_MD* algo = EVP_MD_CTX_md(m_md); + if(!EVP_DigestInit_ex(m_md, algo, nullptr)) + throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error()); + } + + std::string provider() const override { return "openssl"; } + std::string name() const override { return m_name; } + + HashFunction* clone() const override + { + const EVP_MD* algo = EVP_MD_CTX_md(m_md); + return new OpenSSL_HashFunction(name(), algo); + } + + std::unique_ptr<HashFunction> copy_state() const override + { + std::unique_ptr<OpenSSL_HashFunction> copy(new OpenSSL_HashFunction(m_name, nullptr)); + EVP_MD_CTX_copy(copy->m_md, m_md); + return std::unique_ptr<HashFunction>(copy.release()); + } + + size_t output_length() const override + { + return EVP_MD_size(EVP_MD_CTX_md(m_md)); + } + + size_t hash_block_size() const override + { + return EVP_MD_block_size(EVP_MD_CTX_md(m_md)); + } + + OpenSSL_HashFunction(const std::string& name, const EVP_MD* md) : m_name(name) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + m_md = EVP_MD_CTX_create(); +#else + m_md = EVP_MD_CTX_new(); +#endif + + if(m_md == nullptr) + throw OpenSSL_Error("Can't allocate new context", ERR_get_error()); + EVP_MD_CTX_init(m_md); + if(md && !EVP_DigestInit_ex(m_md, md, nullptr)) + throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error()); + } + + OpenSSL_HashFunction(EVP_MD_CTX* ctx) : m_md(ctx) + { + } + + ~OpenSSL_HashFunction() + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_MD_CTX_destroy(m_md); +#else + EVP_MD_CTX_free(m_md); +#endif + } + + private: + void add_data(const uint8_t input[], size_t length) override + { + if(!EVP_DigestUpdate(m_md, input, length)) + throw OpenSSL_Error("EVP_DigestUpdate", ERR_get_error()); + } + + void final_result(uint8_t output[]) override + { + if(!EVP_DigestFinal_ex(m_md, output, nullptr)) + throw OpenSSL_Error("EVP_DigestFinal_ex", ERR_get_error()); + const EVP_MD* algo = EVP_MD_CTX_md(m_md); + if(!EVP_DigestInit_ex(m_md, algo, nullptr)) + throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error()); + } + + std::string m_name; + EVP_MD_CTX* m_md; + }; + +} + +std::unique_ptr<HashFunction> +make_openssl_hash(const std::string& name) + { +#define MAKE_OPENSSL_HASH(fn) \ + std::unique_ptr<HashFunction>(new OpenSSL_HashFunction(name, fn ())) + +#if defined(BOTAN_HAS_SHA2_32) && !defined(OPENSSL_NO_SHA256) + if(name == "SHA-224") + return MAKE_OPENSSL_HASH(EVP_sha224); + if(name == "SHA-256") + return MAKE_OPENSSL_HASH(EVP_sha256); +#endif + +#if defined(BOTAN_HAS_SHA2_64) && !defined(OPENSSL_NO_SHA512) + if(name == "SHA-384") + return MAKE_OPENSSL_HASH(EVP_sha384); + if(name == "SHA-512") + return MAKE_OPENSSL_HASH(EVP_sha512); +#endif + +#if defined(BOTAN_HAS_SHA1) && !defined(OPENSSL_NO_SHA) + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return MAKE_OPENSSL_HASH(EVP_sha1); +#endif + +#if defined(BOTAN_HAS_MD5) && !defined(OPENSSL_NO_MD5) + if(name == "MD5") + return MAKE_OPENSSL_HASH(EVP_md5); + #endif + + return nullptr; + } + +} diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp new file mode 100644 index 0000000000..81f8413a2a --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp @@ -0,0 +1,233 @@ +/* +* Cipher Modes via OpenSSL +* (C) 1999-2010,2015 Jack Lloyd +* (C) 2017 Alexander Bluhm (genua GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cipher_mode.h> +#include <botan/internal/rounding.h> +#include <botan/internal/openssl.h> +#include <openssl/evp.h> +#include <limits.h> + +namespace Botan { + +namespace { + +class OpenSSL_Cipher_Mode final : public Cipher_Mode + { + public: + OpenSSL_Cipher_Mode(const std::string& name, + const EVP_CIPHER* cipher, + Cipher_Dir direction); + ~OpenSSL_Cipher_Mode(); + + std::string provider() const override { return "openssl"; } + std::string name() const override { return m_mode_name; } + + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + size_t process(uint8_t msg[], size_t msg_len) override; + void finish(secure_vector<uint8_t>& final_block, size_t offset0) override; + size_t output_length(size_t input_length) const override; + size_t update_granularity() const override; + size_t minimum_final_size() const override; + size_t default_nonce_length() const override; + bool valid_nonce_length(size_t nonce_len) const override; + void clear() override; + void reset() override; + Key_Length_Specification key_spec() const override; + + private: + void key_schedule(const uint8_t key[], size_t length) override; + + const std::string m_mode_name; + const Cipher_Dir m_direction; + size_t m_block_size; + EVP_CIPHER_CTX* m_cipher; + bool m_key_set; + bool m_nonce_set; + }; + +OpenSSL_Cipher_Mode::OpenSSL_Cipher_Mode(const std::string& name, + const EVP_CIPHER* algo, + Cipher_Dir direction) : + m_mode_name(name), + m_direction(direction), + m_key_set(false), + m_nonce_set(false) + { + m_block_size = EVP_CIPHER_block_size(algo); + + if(EVP_CIPHER_mode(algo) != EVP_CIPH_CBC_MODE) + throw Invalid_Argument("OpenSSL_BlockCipher: Non-CBC EVP was passed in"); + + m_cipher = EVP_CIPHER_CTX_new(); + if (m_cipher == nullptr) + throw OpenSSL_Error("Can't allocate new context", ERR_get_error()); + + EVP_CIPHER_CTX_init(m_cipher); + if(!EVP_CipherInit_ex(m_cipher, algo, nullptr, nullptr, nullptr, + m_direction == ENCRYPTION ? 1 : 0)) + throw OpenSSL_Error("EVP_CipherInit_ex", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_cipher, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding", ERR_get_error()); + } + +OpenSSL_Cipher_Mode::~OpenSSL_Cipher_Mode() + { + EVP_CIPHER_CTX_free(m_cipher); + } + +void OpenSSL_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + verify_key_set(m_key_set); + + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + if(nonce_len) + { + if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nonce, -1)) + throw OpenSSL_Error("EVP_CipherInit_ex nonce", ERR_get_error()); + } + else if(m_nonce_set == false) + { + const std::vector<uint8_t> zeros(m_block_size); + if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, zeros.data(), -1)) + throw OpenSSL_Error("EVP_CipherInit_ex nonce", ERR_get_error()); + } + // otherwise existing CBC state left unchanged + + m_nonce_set = true; + } + +size_t OpenSSL_Cipher_Mode::process(uint8_t msg[], size_t msg_len) + { + verify_key_set(m_key_set); + BOTAN_STATE_CHECK(m_nonce_set); + + if(msg_len == 0) + return 0; + if(msg_len > INT_MAX) + throw Internal_Error("msg_len overflow"); + int outl = msg_len; + secure_vector<uint8_t> out(outl); + + if(!EVP_CipherUpdate(m_cipher, out.data(), &outl, msg, msg_len)) + throw OpenSSL_Error("EVP_CipherUpdate", ERR_get_error()); + copy_mem(msg, out.data(), outl); + return outl; + } + +void OpenSSL_Cipher_Mode::finish(secure_vector<uint8_t>& buffer, + size_t offset) + { + verify_key_set(m_key_set); + BOTAN_STATE_CHECK(m_nonce_set); + + BOTAN_ASSERT(buffer.size() >= offset, "Offset ok"); + uint8_t* buf = buffer.data() + offset; + const size_t buf_size = buffer.size() - offset; + + size_t written = process(buf, buf_size); + int outl = buf_size - written; + secure_vector<uint8_t> out(outl); + + if(!EVP_CipherFinal_ex(m_cipher, out.data(), &outl)) + throw OpenSSL_Error("EVP_CipherFinal_ex", ERR_get_error()); + copy_mem(buf + written, out.data(), outl); + written += outl; + buffer.resize(offset + written); + } + +size_t OpenSSL_Cipher_Mode::update_granularity() const + { + return m_block_size * BOTAN_BLOCK_CIPHER_PAR_MULT; + } + +size_t OpenSSL_Cipher_Mode::minimum_final_size() const + { + return 0; // no padding + } + +size_t OpenSSL_Cipher_Mode::default_nonce_length() const + { + return m_block_size; + } + +bool OpenSSL_Cipher_Mode::valid_nonce_length(size_t nonce_len) const + { + return (nonce_len == 0 || nonce_len == m_block_size); + } + +size_t OpenSSL_Cipher_Mode::output_length(size_t input_length) const + { + if(input_length == 0) + return m_block_size; + else + return round_up(input_length, m_block_size); + } + +void OpenSSL_Cipher_Mode::clear() + { + m_key_set = false; + m_nonce_set = false; + + const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_cipher); + + if(!EVP_CIPHER_CTX_cleanup(m_cipher)) + throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup", ERR_get_error()); + EVP_CIPHER_CTX_init(m_cipher); + if(!EVP_CipherInit_ex(m_cipher, algo, nullptr, nullptr, nullptr, + m_direction == ENCRYPTION ? 1 : 0)) + throw OpenSSL_Error("EVP_CipherInit_ex clear", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_cipher, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding clear", ERR_get_error()); + } + +void OpenSSL_Cipher_Mode::reset() + { + if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nullptr, -1)) + throw OpenSSL_Error("EVP_CipherInit_ex clear", ERR_get_error()); + m_nonce_set = false; + } + +Key_Length_Specification OpenSSL_Cipher_Mode::key_spec() const + { + return Key_Length_Specification(EVP_CIPHER_CTX_key_length(m_cipher)); + } + +void OpenSSL_Cipher_Mode::key_schedule(const uint8_t key[], size_t length) + { + if(!EVP_CIPHER_CTX_set_key_length(m_cipher, length)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_key_length", ERR_get_error()); + if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, key, nullptr, -1)) + throw OpenSSL_Error("EVP_CipherInit_ex key", ERR_get_error()); + m_key_set = true; + m_nonce_set = false; + } + +} + +Cipher_Mode* +make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction) + { +#define MAKE_OPENSSL_MODE(evp_fn) \ + new OpenSSL_Cipher_Mode(name, (evp_fn)(), direction) + +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && !defined(OPENSSL_NO_AES) + if(name == "AES-128/CBC/NoPadding") + return MAKE_OPENSSL_MODE(EVP_aes_128_cbc); + if(name == "AES-192/CBC/NoPadding") + return MAKE_OPENSSL_MODE(EVP_aes_192_cbc); + if(name == "AES-256/CBC/NoPadding") + return MAKE_OPENSSL_MODE(EVP_aes_256_cbc); +#endif + +#undef MAKE_OPENSSL_MODE + return nullptr; + } + +} diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp new file mode 100644 index 0000000000..dbda890f82 --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp @@ -0,0 +1,93 @@ +/* +* OpenSSL RC4 +* (C) 1999-2007,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/stream_cipher.h> + +#if defined(BOTAN_HAS_OPENSSL) && defined(BOTAN_HAS_RC4) + +#include <botan/internal/openssl.h> +#include <botan/parsing.h> +#include <botan/exceptn.h> +#include <openssl/rc4.h> + +namespace Botan { + +namespace { + +class OpenSSL_RC4 final : public StreamCipher + { + public: + void clear() override { clear_mem(&m_rc4, 1); m_key_set = false; } + + std::string provider() const override { return "openssl"; } + + std::string name() const override + { + switch(m_skip) + { + case 0: + return "RC4"; + case 256: + return "MARK-4"; + default: + return "RC4(" + std::to_string(m_skip) + ")"; + } + } + + StreamCipher* clone() const override { return new OpenSSL_RC4(m_skip); } + + Key_Length_Specification key_spec() const override + { + return Key_Length_Specification(1, 32); + } + + explicit OpenSSL_RC4(size_t skip = 0) : m_skip(skip) { clear(); } + ~OpenSSL_RC4() { clear(); } + + void set_iv(const uint8_t*, size_t len) override + { + if(len > 0) + throw Invalid_IV_Length("RC4", len); + } + + void seek(uint64_t) override + { + throw Not_Implemented("RC4 does not support seeking"); + } + private: + void cipher(const uint8_t in[], uint8_t out[], size_t length) override + { + verify_key_set(m_key_set); + ::RC4(&m_rc4, length, in, out); + } + + void key_schedule(const uint8_t key[], size_t length) override + { + ::RC4_set_key(&m_rc4, length, key); + uint8_t d = 0; + for(size_t i = 0; i != m_skip; ++i) + ::RC4(&m_rc4, 1, &d, &d); + m_key_set = true; + } + + size_t m_skip; + RC4_KEY m_rc4; + bool m_key_set; + }; + +} + +std::unique_ptr<StreamCipher> +make_openssl_rc4(size_t skip) + { + return std::unique_ptr<StreamCipher>(new OpenSSL_RC4(skip)); + } + + +} + +#endif diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp new file mode 100644 index 0000000000..6744b35b2c --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp @@ -0,0 +1,312 @@ +/* +* RSA operations provided by OpenSSL +* (C) 2015 Jack Lloyd +* (C) 2017 Alexander Bluhm +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/openssl.h> + +#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> + +#include <functional> +#include <memory> +#include <cstdlib> + +#include <openssl/rsa.h> +#include <openssl/x509.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <limits.h> + +namespace Botan { + +namespace { + +std::pair<int, size_t> get_openssl_enc_pad(const std::string& eme) + { + 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)" || eme == "EME1(SHA-1)") + return std::make_pair(RSA_PKCS1_OAEP_PADDING, 41); + else + throw Lookup_Error("OpenSSL RSA does not support EME " + eme); + } + +class OpenSSL_RSA_Encryption_Operation final : public PK_Ops::Encryption + { + public: + + 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<uint8_t> der = rsa.public_key_bits(); + const uint8_t* der_ptr = der.data(); + m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size())); + if(!m_openssl_rsa) + throw OpenSSL_Error("d2i_RSAPublicKey", ERR_get_error()); + + m_bits = 8 * (n_size() - pad_overhead) - 1; + } + + size_t ciphertext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); } + + size_t max_input_bits() const override { return m_bits; }; + + secure_vector<uint8_t> encrypt(const uint8_t 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<uint8_t> outbuf(mod_sz); + + secure_vector<uint8_t> 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", ERR_get_error()); + + 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 final : public PK_Ops::Decryption + { + public: + + OpenSSL_RSA_Decryption_Operation(const RSA_PrivateKey& rsa, int pad) : + m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad) + { + const secure_vector<uint8_t> der = rsa.private_key_bits(); + const uint8_t* der_ptr = der.data(); + m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size())); + if(!m_openssl_rsa) + throw OpenSSL_Error("d2i_RSAPrivateKey", ERR_get_error()); + } + + size_t plaintext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); } + + secure_vector<uint8_t> decrypt(uint8_t& valid_mask, + const uint8_t msg[], size_t msg_len) override + { + secure_vector<uint8_t> 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()) + { + valid_mask = 0; + buf.resize(0); + } + else + { + valid_mask = 0xFF; + buf.resize(rc); + } + + if(m_padding == RSA_NO_PADDING) + { + return CT::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 final : public PK_Ops::Verification_with_EMSA + { + public: + + 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<uint8_t> der = rsa.public_key_bits(); + const uint8_t* der_ptr = der.data(); + m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size())); + if(!m_openssl_rsa) + throw OpenSSL_Error("d2i_RSAPublicKey", ERR_get_error()); + } + + size_t max_input_bits() const override + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + return ::BN_num_bits(m_openssl_rsa->n) - 1; +#else + return ::RSA_bits(m_openssl_rsa.get()) - 1; +#endif + } + + bool with_recovery() const override { return true; } + + secure_vector<uint8_t> verify_mr(const uint8_t 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<uint8_t> inbuf(mod_sz); + + if(msg_len > 0) + copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len); + + secure_vector<uint8_t> 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 CT::strip_leading_zeros(outbuf); + } + private: + std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa; + }; + +class OpenSSL_RSA_Signing_Operation final : public PK_Ops::Signature_with_EMSA + { + public: + + 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<uint8_t> der = rsa.private_key_bits(); + const uint8_t* der_ptr = der.data(); + m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size())); + if(!m_openssl_rsa) + throw OpenSSL_Error("d2i_RSAPrivateKey", ERR_get_error()); + } + + size_t signature_length() const override { return ::RSA_size(m_openssl_rsa.get()); } + + secure_vector<uint8_t> raw_sign(const uint8_t 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<uint8_t> inbuf(mod_sz); + copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len); + + secure_vector<uint8_t> 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", ERR_get_error()); + + return outbuf; + } + + size_t max_input_bits() const override + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + return ::BN_num_bits(m_openssl_rsa->n) - 1; +#else + return ::RSA_bits(m_openssl_rsa.get()) - 1; +#endif + } + + private: + std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa; + }; + +} + +std::unique_ptr<PK_Ops::Encryption> +make_openssl_rsa_enc_op(const RSA_PublicKey& key, const std::string& params) + { + auto pad_info = get_openssl_enc_pad(params); + return std::unique_ptr<PK_Ops::Encryption>( + new OpenSSL_RSA_Encryption_Operation(key, pad_info.first, pad_info.second)); + } + +std::unique_ptr<PK_Ops::Decryption> +make_openssl_rsa_dec_op(const RSA_PrivateKey& key, const std::string& params) + { + auto pad_info = get_openssl_enc_pad(params); + return std::unique_ptr<PK_Ops::Decryption>(new OpenSSL_RSA_Decryption_Operation(key, pad_info.first)); + } + +std::unique_ptr<PK_Ops::Verification> +make_openssl_rsa_ver_op(const RSA_PublicKey& key, const std::string& params) + { + return std::unique_ptr<PK_Ops::Verification>(new OpenSSL_RSA_Verification_Operation(key, params)); + } + +std::unique_ptr<PK_Ops::Signature> +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", ERR_get_error()); + if(!BN_set_word(bn.get(), RSA_F4)) + throw OpenSSL_Error("BN_set_word", ERR_get_error()); + + std::unique_ptr<RSA, std::function<void (RSA*)>> rsa(RSA_new(), RSA_free); + if(!rsa) + throw OpenSSL_Error("RSA_new", ERR_get_error()); + if(!RSA_generate_key_ex(rsa.get(), rsa_bits, bn.get(), nullptr)) + throw OpenSSL_Error("RSA_generate_key_ex", ERR_get_error()); + + uint8_t* der = nullptr; + int bytes = i2d_RSAPrivateKey(rsa.get(), &der); + if(bytes < 0) + throw OpenSSL_Error("i2d_RSAPrivateKey", ERR_get_error()); + + const secure_vector<uint8_t> keydata(der, der + bytes); + secure_scrub_memory(der, bytes); + std::free(der); + return std::unique_ptr<Botan::RSA_PrivateKey> + (new RSA_PrivateKey(AlgorithmIdentifier(), keydata)); + } +} + +#endif // BOTAN_HAS_RSA |