Skip to content

Commit

Permalink
Merge pull request openssl#436 from rhenium/ky/pkey-generic-evp-more
Browse files Browse the repository at this point in the history
Use EVP API in more places
  • Loading branch information
rhenium committed Apr 17, 2021
2 parents 03cb3d5 + 797e9f8 commit c310840
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 292 deletions.
3 changes: 3 additions & 0 deletions ext/openssl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ def find_openssl_library
have_func("EVP_PBE_scrypt")
have_func("SSL_CTX_set_post_handshake_auth")

# added in 1.1.1
have_func("EVP_PKEY_check")

Logging::message "=== Checking done. ===\n"

create_header
Expand Down
38 changes: 38 additions & 0 deletions ext/openssl/ossl_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,43 @@ ossl_pkey_inspect(VALUE self)
OBJ_nid2sn(nid));
}

/*
* call-seq:
* pkey.to_text -> string
*
* Dumps key parameters, public key, and private key components contained in
* the key into a human-readable text.
*
* This is intended for debugging purpose.
*
* See also the man page EVP_PKEY_print_private(3).
*/
static VALUE
ossl_pkey_to_text(VALUE self)
{
EVP_PKEY *pkey;
BIO *bio;

GetPKey(self, pkey);
if (!(bio = BIO_new(BIO_s_mem())))
ossl_raise(ePKeyError, "BIO_new");

if (EVP_PKEY_print_private(bio, pkey, 0, NULL) == 1)
goto out;
OSSL_BIO_reset(bio);
if (EVP_PKEY_print_public(bio, pkey, 0, NULL) == 1)
goto out;
OSSL_BIO_reset(bio);
if (EVP_PKEY_print_params(bio, pkey, 0, NULL) == 1)
goto out;

BIO_free(bio);
ossl_raise(ePKeyError, "EVP_PKEY_print_params");

out:
return ossl_membio2str(bio);
}

VALUE
ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der)
{
Expand Down Expand Up @@ -1077,6 +1114,7 @@ Init_ossl_pkey(void)
rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
rb_define_method(cPKey, "oid", ossl_pkey_oid, 0);
rb_define_method(cPKey, "inspect", ossl_pkey_inspect, 0);
rb_define_method(cPKey, "to_text", ossl_pkey_to_text, 0);
rb_define_method(cPKey, "private_to_der", ossl_pkey_private_to_der, -1);
rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1);
rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
Expand Down
119 changes: 36 additions & 83 deletions ext/openssl/ossl_pkey_dh.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,96 +266,45 @@ ossl_dh_get_params(VALUE self)
return hash;
}

/*
* call-seq:
* dh.to_text -> aString
*
* Prints all parameters of key to buffer
* INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
* Don't use :-)) (I's up to you)
*/
static VALUE
ossl_dh_to_text(VALUE self)
{
DH *dh;
BIO *out;
VALUE str;

GetDH(self, dh);
if (!(out = BIO_new(BIO_s_mem()))) {
ossl_raise(eDHError, NULL);
}
if (!DHparams_print(out, dh)) {
BIO_free(out);
ossl_raise(eDHError, NULL);
}
str = ossl_membio2str(out);

return str;
}

/*
* call-seq:
* dh.public_key -> aDH
*
* Returns a new DH instance that carries just the public information, i.e.
* the prime _p_ and the generator _g_, but no public/private key yet. Such
* a pair may be generated using DH#generate_key!. The "public key" needed
* for a key exchange with DH#compute_key is considered as per-session
* information and may be retrieved with DH#pub_key once a key pair has
* been generated.
* If the current instance already contains private information (and thus a
* valid public/private key pair), this information will no longer be present
* in the new instance generated by DH#public_key. This feature is helpful for
* publishing the Diffie-Hellman parameters without leaking any of the private
* per-session information.
*
* === Example
* dh = OpenSSL::PKey::DH.new(2048) # has public and private key set
* public_key = dh.public_key # contains only prime and generator
* parameters = public_key.to_der # it's safe to publish this
*/
static VALUE
ossl_dh_to_public_key(VALUE self)
{
EVP_PKEY *pkey;
DH *orig_dh, *dh;
VALUE obj;

obj = rb_obj_alloc(rb_obj_class(self));
GetPKey(obj, pkey);

GetDH(self, orig_dh);
dh = DHparams_dup(orig_dh);
if (!dh)
ossl_raise(eDHError, "DHparams_dup");
if (!EVP_PKEY_assign_DH(pkey, dh)) {
DH_free(dh);
ossl_raise(eDHError, "EVP_PKEY_assign_DH");
}
return obj;
}

/*
* call-seq:
* dh.params_ok? -> true | false
*
* Validates the Diffie-Hellman parameters associated with this instance.
* It checks whether a safe prime and a suitable generator are used. If this
* is not the case, +false+ is returned.
*
* See also the man page EVP_PKEY_param_check(3).
*/
static VALUE
ossl_dh_check_params(VALUE self)
{
int ret;
#ifdef HAVE_EVP_PKEY_CHECK
EVP_PKEY *pkey;
EVP_PKEY_CTX *pctx;

GetPKey(self, pkey);
pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
if (!pctx)
ossl_raise(eDHError, "EVP_PKEY_CTX_new");
ret = EVP_PKEY_param_check(pctx);
EVP_PKEY_CTX_free(pctx);
#else
DH *dh;
int codes;

GetDH(self, dh);
if (!DH_check(dh, &codes)) {
return Qfalse;
}
ret = DH_check(dh, &codes) == 1 && codes == 0;
#endif

return codes == 0 ? Qtrue : Qfalse;
if (ret == 1)
return Qtrue;
else {
/* DH_check_ex() will put error entry on failure */
ossl_clear_error();
return Qfalse;
}
}

/*
Expand Down Expand Up @@ -412,26 +361,30 @@ Init_ossl_dh(void)
* The per-session private key, an OpenSSL::BN.
*
* === Example of a key exchange
* dh1 = OpenSSL::PKey::DH.new(2048)
* der = dh1.public_key.to_der #you may send this publicly to the participating party
* dh2 = OpenSSL::PKey::DH.new(der)
* dh2.generate_key! #generate the per-session key pair
* symm_key1 = dh1.compute_key(dh2.pub_key)
* symm_key2 = dh2.compute_key(dh1.pub_key)
* # you may send the parameters (der) and own public key (pub1) publicly
* # to the participating party
* dh1 = OpenSSL::PKey::DH.new(2048)
* der = dh1.to_der
* pub1 = dh1.pub_key
*
* # the other party generates its per-session key pair
* dhparams = OpenSSL::PKey::DH.new(der)
* dh2 = OpenSSL::PKey.generate_key(dhparams)
* pub2 = dh2.pub_key
*
* puts symm_key1 == symm_key2 # => true
* symm_key1 = dh1.compute_key(pub2)
* symm_key2 = dh2.compute_key(pub1)
* puts symm_key1 == symm_key2 # => true
*/
cDH = rb_define_class_under(mPKey, "DH", cPKey);
rb_define_method(cDH, "initialize", ossl_dh_initialize, -1);
rb_define_method(cDH, "initialize_copy", ossl_dh_initialize_copy, 1);
rb_define_method(cDH, "public?", ossl_dh_is_public, 0);
rb_define_method(cDH, "private?", ossl_dh_is_private, 0);
rb_define_method(cDH, "to_text", ossl_dh_to_text, 0);
rb_define_method(cDH, "export", ossl_dh_export, 0);
rb_define_alias(cDH, "to_pem", "export");
rb_define_alias(cDH, "to_s", "export");
rb_define_method(cDH, "to_der", ossl_dh_to_der, 0);
rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0);
rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0);

DEF_OSSL_PKEY_BN(cDH, dh, p);
Expand Down
71 changes: 0 additions & 71 deletions ext/openssl/ossl_pkey_dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,75 +264,6 @@ ossl_dsa_get_params(VALUE self)
return hash;
}

/*
* call-seq:
* dsa.to_text -> aString
*
* Prints all parameters of key to buffer
* INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
* Don't use :-)) (I's up to you)
*/
static VALUE
ossl_dsa_to_text(VALUE self)
{
DSA *dsa;
BIO *out;
VALUE str;

GetDSA(self, dsa);
if (!(out = BIO_new(BIO_s_mem()))) {
ossl_raise(eDSAError, NULL);
}
if (!DSA_print(out, dsa, 0)) { /* offset = 0 */
BIO_free(out);
ossl_raise(eDSAError, NULL);
}
str = ossl_membio2str(out);

return str;
}

/*
* call-seq:
* dsa.public_key -> aDSA
*
* Returns a new DSA instance that carries just the public key information.
* If the current instance has also private key information, this will no
* longer be present in the new instance. This feature is helpful for
* publishing the public key information without leaking any of the private
* information.
*
* === Example
* dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information
* pub_key = dsa.public_key # has only the public part available
* pub_key_der = pub_key.to_der # it's safe to publish this
*
*
*/
static VALUE
ossl_dsa_to_public_key(VALUE self)
{
EVP_PKEY *pkey, *pkey_new;
DSA *dsa;
VALUE obj;

GetPKeyDSA(self, pkey);
obj = rb_obj_alloc(rb_obj_class(self));
GetPKey(obj, pkey_new);

#define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup( \
(i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa))
dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey));
#undef DSAPublicKey_dup
if (!dsa)
ossl_raise(eDSAError, "DSAPublicKey_dup");
if (!EVP_PKEY_assign_DSA(pkey_new, dsa)) {
DSA_free(dsa);
ossl_raise(eDSAError, "EVP_PKEY_assign_DSA");
}
return obj;
}

/*
* call-seq:
* dsa.syssign(string) -> aString
Expand Down Expand Up @@ -469,12 +400,10 @@ Init_ossl_dsa(void)

rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0);
rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0);
rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0);
rb_define_method(cDSA, "export", ossl_dsa_export, -1);
rb_define_alias(cDSA, "to_pem", "export");
rb_define_alias(cDSA, "to_s", "export");
rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0);
rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0);
rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);

Expand Down
50 changes: 19 additions & 31 deletions ext/openssl/ossl_pkey_ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,32 +412,6 @@ ossl_ec_key_to_der(VALUE self)
else
return ossl_pkey_export_spki(self, 1);
}

/*
* call-seq:
* key.to_text => String
*
* See the OpenSSL documentation for EC_KEY_print()
*/
static VALUE ossl_ec_key_to_text(VALUE self)
{
EC_KEY *ec;
BIO *out;
VALUE str;

GetEC(self, ec);
if (!(out = BIO_new(BIO_s_mem()))) {
ossl_raise(eECError, "BIO_new(BIO_s_mem())");
}
if (!EC_KEY_print(out, ec, 0)) {
BIO_free(out);
ossl_raise(eECError, "EC_KEY_print");
}
str = ossl_membio2str(out);

return str;
}

/*
* call-seq:
* key.generate_key! => self
Expand All @@ -464,20 +438,35 @@ static VALUE ossl_ec_key_generate_key(VALUE self)
}

/*
* call-seq:
* key.check_key => true
* call-seq:
* key.check_key => true
*
* Raises an exception if the key is invalid.
* Raises an exception if the key is invalid.
*
* See the OpenSSL documentation for EC_KEY_check_key()
* See also the man page EVP_PKEY_public_check(3).
*/
static VALUE ossl_ec_key_check_key(VALUE self)
{
#ifdef HAVE_EVP_PKEY_CHECK
EVP_PKEY *pkey;
EVP_PKEY_CTX *pctx;
int ret;

GetPKey(self, pkey);
pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
if (!pctx)
ossl_raise(eDHError, "EVP_PKEY_CTX_new");
ret = EVP_PKEY_public_check(pctx);
EVP_PKEY_CTX_free(pctx);
if (ret != 1)
ossl_raise(eECError, "EVP_PKEY_public_check");
#else
EC_KEY *ec;

GetEC(self, ec);
if (EC_KEY_check_key(ec) != 1)
ossl_raise(eECError, "EC_KEY_check_key");
#endif

return Qtrue;
}
Expand Down Expand Up @@ -1601,7 +1590,6 @@ void Init_ossl_ec(void)
rb_define_method(cEC, "export", ossl_ec_key_export, -1);
rb_define_alias(cEC, "to_pem", "export");
rb_define_method(cEC, "to_der", ossl_ec_key_to_der, 0);
rb_define_method(cEC, "to_text", ossl_ec_key_to_text, 0);


rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc);
Expand Down

0 comments on commit c310840

Please sign in to comment.