diff --git a/include/ircd/openssl.h b/include/ircd/openssl.h index 7dc2311d8..194fd5e49 100644 --- a/include/ircd/openssl.h +++ b/include/ircd/openssl.h @@ -36,6 +36,9 @@ struct bignum_st; struct bignum_ctx; struct bio_st; struct evp_pkey_st; +struct ec_group_st; +struct ec_point_st; +struct ec_key_st; /// OpenSSL library interface. Provides things we need to expose from OpenSSL /// to the rest of the project. Anything that employs forward declared types @@ -58,6 +61,9 @@ namespace ircd::openssl using BN_CTX = ::bignum_ctx; using EVP_PKEY = ::evp_pkey_st; using BIO = ::bio_st; + using EC_GROUP = ::ec_group_st; + using EC_POINT = ::ec_point_st; + using EC_KEY = ::ec_key_st; // Library general string_view version(); @@ -75,6 +81,8 @@ namespace ircd::openssl EVP_PKEY &read_pem_priv(EVP_PKEY &out, const string_view &pem); string_view write_pem_pub(const mutable_buffer &out, const EVP_PKEY &); string_view write_pem_priv(const mutable_buffer &out, const EVP_PKEY &); + void set(EVP_PKEY &out, RSA &in); + void set(EVP_PKEY &out, EC_KEY &in); // RSA suite void check(const RSA &); @@ -83,7 +91,13 @@ namespace ircd::openssl string_view print(const mutable_buffer &buf, const RSA &, const off_t &offset = 0); RSA &genrsa(RSA &out, const uint &bits = 2048, const uint &e = 0x10001); void genrsa(const string_view &skfile, const string_view &pkfile, const json::object &opts = {}); - void set(EVP_PKEY &out, RSA &in); + + // EC suite + extern const EC_GROUP *secp256k1; + void check(const EC_KEY &); + bool check(const EC_KEY &, const std::nothrow_t); + string_view print(const mutable_buffer &buf, const EC_KEY &, const off_t &offset = 0); + void genec(const string_view &skfile, const string_view &pkfile, const EC_GROUP *const & = secp256k1); // X.509 suite const_raw_buffer i2d(const mutable_raw_buffer &out, const X509 &); @@ -92,7 +106,9 @@ namespace ircd::openssl string_view write_pem(const mutable_buffer &out, const X509 &); string_view print(const mutable_buffer &buf, const X509 &, ulong flags = -1); string_view printX509(const mutable_buffer &buf, const string_view &pem, ulong flags = -1); - string_view genX509(const mutable_buffer &out, const json::object &opts); + string_view genX509(const mutable_buffer &out, EVP_PKEY &, const json::object &opts); + string_view genX509_rsa(const mutable_buffer &out, const json::object &opts); + string_view genX509_ec(const mutable_buffer &out, const json::object &opts); string_view subject_common_name(const mutable_buffer &out, const X509 &); const X509 &peer_cert(const SSL &); X509 &peer_cert(SSL &); diff --git a/ircd/openssl.cc b/ircd/openssl.cc index c57b6e24d..ecff49dcd 100644 --- a/ircd/openssl.cc +++ b/ircd/openssl.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -142,14 +143,19 @@ ircd::openssl::peer_cert(const SSL &ssl) return *ret; } -ircd::string_view -ircd::openssl::genX509(const mutable_buffer &out, - const json::object &opts) +namespace ircd::openssl { - const custom_ptr rsa + static void genx509_readkeys(EVP_PKEY &, const json::object &); +} + +ircd::string_view +ircd::openssl::genX509_rsa(const mutable_buffer &out, + const json::object &opts) +{ + const custom_ptr priv { RSA_new(), - [](RSA *const rsa) { RSA_free(rsa); } + [](RSA *const key) { RSA_free(key); } }; const custom_ptr pk @@ -158,43 +164,74 @@ ircd::openssl::genX509(const mutable_buffer &out, [](EVP_PKEY *const pk) { EVP_PKEY_free(pk); } }; - set(*pk, *rsa); + set(*pk, *priv); + genx509_readkeys(*pk, opts); + check(*pk->pkey.rsa); + return genX509(out, *pk, opts); +} + +ircd::string_view +ircd::openssl::genX509_ec(const mutable_buffer &out, + const json::object &opts) +{ + const custom_ptr priv + { + EC_KEY_new(), + [](EC_KEY *const key) { EC_KEY_free(key); } + }; + + const custom_ptr pk + { + EVP_PKEY_new(), + [](EVP_PKEY *const pk) { EVP_PKEY_free(pk); } + }; + + set(*pk, *priv); + genx509_readkeys(*pk, opts); + check(*pk->pkey.ec); + return genX509(out, *pk, opts); +} + +void +ircd::openssl::genx509_readkeys(EVP_PKEY &pk, + const json::object &opts) +{ + const auto private_key_path + { + unquote(opts.at("tls_private_key_path")) + }; const auto public_key_path { unquote(opts.at("tls_public_key_path")) }; - bio::read_file(public_key_path, [&pk] - (const string_view &pem) + bio::read_file(private_key_path, [&pk](const string_view &pem) { - read_pem_pub(*pk, pem); + read_pem_priv(pk, pem); }); - const auto private_key_path + bio::read_file(public_key_path, [&pk](const string_view &pem) { - unquote(opts.at("tls_private_key_path")) - }; - - bio::read_file(private_key_path, [&pk] - (const string_view &pem) - { - read_pem_priv(*pk, pem); + read_pem_pub(pk, pem); }); +} - check(*pk->pkey.rsa); - +ircd::string_view +ircd::openssl::genX509(const mutable_buffer &out, + EVP_PKEY &pk, + const json::object &opts) +{ const custom_ptr x509 { X509_new(), [](X509 *const x509) { X509_free(x509); } }; - call(::X509_set_pubkey, x509.get(), pk.get()); - call(::X509_set_version, x509.get(), 2); + call(::X509_set_pubkey, x509.get(), &pk); append_entries(*x509, opts); - call(::X509_sign, x509.get(), pk.get(), EVP_sha256()); + call(::X509_sign, x509.get(), &pk, EVP_sha256()); return write_pem(out, *x509); } @@ -447,6 +484,102 @@ ircd::openssl::i2d(const mutable_raw_buffer &buf, return ret; } +// +// EC +// + +namespace ircd::openssl +{ + void ec_init(); + void ec_fini() noexcept; +} + +const EC_GROUP * +ircd::openssl::secp256k1 +{}; + +void +ircd::openssl::ec_init() +{ + EC_GROUP *_secp256k1; + if(!(_secp256k1 = EC_GROUP_new_by_curve_name(OBJ_sn2nid("secp256k1")))) + throw error{"Failed to initialize EC_GROUP secp256k1"}; + + EC_GROUP_set_asn1_flag(_secp256k1, OPENSSL_EC_NAMED_CURVE); + EC_GROUP_set_point_conversion_form(_secp256k1, POINT_CONVERSION_COMPRESSED); + secp256k1 = _secp256k1; +} + +void +ircd::openssl::ec_fini() +noexcept +{ + EC_GROUP_free(const_cast(secp256k1)); +} + +void +ircd::openssl::genec(const string_view &skfile, + const string_view &pkfile, + const EC_GROUP *const &group) +{ + const custom_ptr key + { + EC_KEY_new(), + [](EC_KEY *const key) { EC_KEY_free(key); } + }; + + const custom_ptr pk + { + EVP_PKEY_new(), + [](EVP_PKEY *const pk) { EVP_PKEY_free(pk); } + }; + + const auto write_priv{[&pk](const mutable_buffer &out) + { + return write_pem_priv(out, *pk); + }}; + + const auto write_pub{[&pk](const mutable_buffer &out) + { + return write_pem_pub(out, *pk); + }}; + + assert(group); + assert(EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE); + call(::EC_KEY_set_group, key.get(), group); + call(::EC_KEY_generate_key, key.get()); + assert(EC_KEY_get0_public_key(key.get())); + set(*pk, *key); + bio::write_file(skfile, write_priv); + bio::write_file(pkfile, write_pub); +} + +ircd::string_view +ircd::openssl::print(const mutable_buffer &buf, + const EC_KEY &key, + const off_t &offset) +{ + return bio::write(buf, [&key, &offset] + (BIO *const &bio) + { + call(::EC_KEY_print, bio, &key, offset); + }); +} + +void +ircd::openssl::check(const EC_KEY &key) +{ + if(!check(key, std::nothrow)) + throw error{"Invalid Elliptic Curve Key"}; +} + +bool +ircd::openssl::check(const EC_KEY &key, + const std::nothrow_t) +{ + return EC_KEY_check_key(&key) == 1; +} + // // RSA // @@ -566,13 +699,6 @@ ircd::openssl::print(const mutable_buffer &buf, }); } -void -ircd::openssl::set(EVP_PKEY &out, - RSA &in) -{ - call(::EVP_PKEY_set1_RSA, &out, &in); -} - size_t ircd::openssl::size(const RSA &key) { @@ -598,6 +724,20 @@ ircd::openssl::check(const RSA &key, // Envelope // +void +ircd::openssl::set(EVP_PKEY &out, + RSA &in) +{ + call(::EVP_PKEY_set1_RSA, &out, &in); +} + +void +ircd::openssl::set(EVP_PKEY &out, + EC_KEY &in) +{ + call(::EVP_PKEY_set1_EC_KEY, &out, &in); +} + ircd::string_view ircd::openssl::write_pem_priv(const mutable_buffer &out, const EVP_PKEY &evp) @@ -620,6 +760,10 @@ ircd::openssl::write_pem_priv(const mutable_buffer &out, call(::PEM_write_bio_RSAPrivateKey, bio, p->pkey.rsa, enc, kstr, klen, pwcb, u); break; + case EVP_PKEY_EC: + call(::PEM_write_bio_ECPrivateKey, bio, p->pkey.ec, enc, kstr, klen, pwcb, u); + break; + default: call(::PEM_write_bio_PrivateKey, bio, p, enc, kstr, klen, pwcb, u); break; @@ -641,6 +785,10 @@ ircd::openssl::write_pem_pub(const mutable_buffer &out, call(::PEM_write_bio_RSAPublicKey, bio, p->pkey.rsa); break; + case EVP_PKEY_EC: + call(::PEM_write_bio_EC_PUBKEY, bio, p->pkey.ec); + break; + default: call(::PEM_write_bio_PUBKEY, bio, p); break; @@ -667,6 +815,11 @@ ircd::openssl::read_pem_priv(EVP_PKEY &out_, ret = PEM_read_bio_RSAPrivateKey(bio, &out->pkey.rsa, pwcb, u); break; + case EVP_PKEY_EC: + ret = PEM_read_bio_ECPrivateKey(bio, &out->pkey.ec, pwcb, u); + EC_KEY_set_asn1_flag(out->pkey.ec, OPENSSL_EC_NAMED_CURVE); + break; + default: ret = PEM_read_bio_PrivateKey(bio, &out, pwcb, u); break; @@ -701,6 +854,11 @@ ircd::openssl::read_pem_pub(EVP_PKEY &out_, ret = PEM_read_bio_RSAPublicKey(bio, &out->pkey.rsa, pwcb, u); break; + case EVP_PKEY_EC: + ret = PEM_read_bio_EC_PUBKEY(bio, &out->pkey.ec, pwcb, u); + EC_KEY_set_asn1_flag(out->pkey.ec, OPENSSL_EC_NAMED_CURVE); + break; + default: ret = PEM_read_bio_PUBKEY(bio, &out, pwcb, u); break; @@ -1062,6 +1220,7 @@ ircd::openssl::init::init() OPENSSL_init(); ERR_load_crypto_strings(); ERR_load_ERR_strings(); + ec_init(); /* const auto their_id_callback @@ -1089,6 +1248,8 @@ ircd::openssl::init::init() ircd::openssl::init::~init() { + ec_fini(); + //assert(CRYPTO_get_locking_callback() == locking::callback); //assert(CRYPTO_THREADID_get_callback() == locking::id_callback);