Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkey: refactor PEM/DER serialization code #328

Merged
merged 6 commits into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ext/openssl/ossl.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,13 +679,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
*
* A key can also be loaded from a file.
*
* key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
* key2 = OpenSSL::PKey.read File.read 'private_key.pem'
* key2.public? # => true
* key2.private? # => true
*
* or
*
* key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
* key3 = OpenSSL::PKey.read File.read 'public_key.pem'
* key3.public? # => true
* key3.private? # => false
*
Expand All @@ -697,7 +697,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
*
* key4_pem = File.read 'private.secure.pem'
* pass_phrase = 'my secure pass phrase goes here'
* key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase
* key4 = OpenSSL::PKey.read key4_pem, pass_phrase
*
* == RSA Encryption
*
Expand Down
130 changes: 91 additions & 39 deletions ext/openssl/ossl_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,34 +95,30 @@ const rb_data_type_t ossl_evp_pkey_type = {
static VALUE
pkey_new0(EVP_PKEY *pkey)
{
VALUE obj;
VALUE klass, obj;
int type;

if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE)
ossl_raise(rb_eRuntimeError, "pkey is empty");

switch (type) {
#if !defined(OPENSSL_NO_RSA)
case EVP_PKEY_RSA:
return ossl_rsa_new(pkey);
case EVP_PKEY_RSA: klass = cRSA; break;
#endif
#if !defined(OPENSSL_NO_DSA)
case EVP_PKEY_DSA:
return ossl_dsa_new(pkey);
case EVP_PKEY_DSA: klass = cDSA; break;
#endif
#if !defined(OPENSSL_NO_DH)
case EVP_PKEY_DH:
return ossl_dh_new(pkey);
case EVP_PKEY_DH: klass = cDH; break;
#endif
#if !defined(OPENSSL_NO_EC)
case EVP_PKEY_EC:
return ossl_ec_new(pkey);
case EVP_PKEY_EC: klass = cEC; break;
#endif
default:
obj = NewPKey(cPKey);
SetPKey(obj, pkey);
return obj;
default: klass = cPKey; break;
}
obj = NewPKey(klass);
SetPKey(obj, pkey);
return obj;
}

VALUE
Expand All @@ -140,6 +136,35 @@ ossl_pkey_new(EVP_PKEY *pkey)
return obj;
}

EVP_PKEY *
ossl_pkey_read_generic(BIO *bio, VALUE pass)
{
void *ppass = (void *)pass;
EVP_PKEY *pkey;

if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
goto out;
OSSL_BIO_reset(bio);
if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, ppass)))
goto out;
OSSL_BIO_reset(bio);
if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
goto out;
OSSL_BIO_reset(bio);
/* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, ppass)))
goto out;
OSSL_BIO_reset(bio);
if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
goto out;
OSSL_BIO_reset(bio);
if ((pkey = PEM_read_bio_Parameters(bio, NULL)))
goto out;

out:
return pkey;
}

/*
* call-seq:
* OpenSSL::PKey.read(string [, pwd ]) -> PKey
Expand All @@ -164,30 +189,11 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
VALUE data, pass;

rb_scan_args(argc, argv, "11", &data, &pass);
pass = ossl_pem_passwd_value(pass);

bio = ossl_obj2bio(&data);
if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
goto ok;
OSSL_BIO_reset(bio);
if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
goto ok;
OSSL_BIO_reset(bio);
if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
goto ok;
OSSL_BIO_reset(bio);
/* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
goto ok;
OSSL_BIO_reset(bio);
if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
goto ok;

BIO_free(bio);
ossl_raise(ePKeyError, "Could not parse PKey");

ok:
pkey = ossl_pkey_read_generic(bio, ossl_pem_passwd_value(pass));
BIO_free(bio);
if (!pkey)
ossl_raise(ePKeyError, "Could not parse PKey");
return ossl_pkey_new(pkey);
}

Expand Down Expand Up @@ -335,6 +341,52 @@ ossl_pkey_inspect(VALUE self)
OBJ_nid2sn(nid));
}

VALUE
ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der)
{
EVP_PKEY *pkey;
VALUE cipher, pass;
const EVP_CIPHER *enc = NULL;
BIO *bio;

GetPKey(self, pkey);
rb_scan_args(argc, argv, "02", &cipher, &pass);
if (!NIL_P(cipher)) {
enc = ossl_evp_get_cipherbyname(cipher);
pass = ossl_pem_passwd_value(pass);
}

bio = BIO_new(BIO_s_mem());
if (!bio)
ossl_raise(ePKeyError, "BIO_new");
if (to_der) {
if (!i2d_PrivateKey_bio(bio, pkey)) {
BIO_free(bio);
ossl_raise(ePKeyError, "i2d_PrivateKey_bio");
}
}
else {
#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0,
ossl_pem_passwd_cb,
(void *)pass)) {
#else
char pem_str[80];
const char *aname;

EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &aname, pkey->ameth);
snprintf(pem_str, sizeof(pem_str), "%s PRIVATE KEY", aname);
if (!PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey, pem_str, bio,
pkey, enc, NULL, 0, ossl_pem_passwd_cb,
(void *)pass)) {
#endif
BIO_free(bio);
ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional");
}
}
return ossl_membio2str(bio);
}

static VALUE
do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der)
{
Expand Down Expand Up @@ -404,8 +456,8 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
return do_pkcs8_export(argc, argv, self, 0);
}

static VALUE
do_spki_export(VALUE self, int to_der)
VALUE
ossl_pkey_export_spki(VALUE self, int to_der)
{
EVP_PKEY *pkey;
BIO *bio;
Expand Down Expand Up @@ -438,7 +490,7 @@ do_spki_export(VALUE self, int to_der)
static VALUE
ossl_pkey_public_to_der(VALUE self)
{
return do_spki_export(self, 1);
return ossl_pkey_export_spki(self, 1);
}

/*
Expand All @@ -450,7 +502,7 @@ ossl_pkey_public_to_der(VALUE self)
static VALUE
ossl_pkey_public_to_pem(VALUE self)
{
return do_spki_export(self, 0);
return ossl_pkey_export_spki(self, 0);
}

/*
Expand Down
18 changes: 15 additions & 3 deletions ext/openssl/ossl_pkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,24 @@ void ossl_generate_cb_stop(void *ptr);

VALUE ossl_pkey_new(EVP_PKEY *);
void ossl_pkey_check_public_key(const EVP_PKEY *);
EVP_PKEY *ossl_pkey_read_generic(BIO *, VALUE);
EVP_PKEY *GetPKeyPtr(VALUE);
EVP_PKEY *DupPKeyPtr(VALUE);
EVP_PKEY *GetPrivPKeyPtr(VALUE);

/*
* Serializes _self_ in X.509 SubjectPublicKeyInfo format and returns the
* resulting String. Sub-classes use this when overriding #to_der.
*/
VALUE ossl_pkey_export_spki(VALUE self, int to_der);
/*
* Serializes the private key _self_ in the traditional private key format
* and returns the resulting String. Sub-classes use this when overriding
* #to_der.
*/
VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self,
int to_der);

void Init_ossl_pkey(void);

/*
Expand All @@ -56,7 +71,6 @@ void Init_ossl_pkey(void);
extern VALUE cRSA;
extern VALUE eRSAError;

VALUE ossl_rsa_new(EVP_PKEY *);
void Init_ossl_rsa(void);

/*
Expand All @@ -65,7 +79,6 @@ void Init_ossl_rsa(void);
extern VALUE cDSA;
extern VALUE eDSAError;

VALUE ossl_dsa_new(EVP_PKEY *);
void Init_ossl_dsa(void);

/*
Expand All @@ -74,7 +87,6 @@ void Init_ossl_dsa(void);
extern VALUE cDH;
extern VALUE eDHError;

VALUE ossl_dh_new(EVP_PKEY *);
void Init_ossl_dh(void);

/*
Expand Down
Loading