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

Add support for raw private/public keys #646

Merged
merged 19 commits into from
Jul 12, 2023

Conversation

sylph01
Copy link
Contributor

@sylph01 sylph01 commented Jul 1, 2023

Based off of #373, applied review comments and fixed tests for older versions.

I removed the private? / public? methods and marshalling support that relies on these methods, as these methods are causing old versions of OpenSSL to not work. I tried fixing these methods, but wasn't able to come up with a consistent fix that works on all versions (out of scope for this PR, but the errors were mostly segfaults on the buffer used in EVP_PKEY_get_raw_private_key). I am thinking on working on this later, so any advice/support would be appreciated.

I would like to first add support for raw private/public keys independently, as these methods are needed to implement protocols that rely on X25519/X448 raw private/public key support (such as Hybrid Public Key Encryption and QUIC).

int pkey_id;
size_t keylen;

#ifndef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This doesn't cover the call of EVP_PKEY_new_raw_private_key below so this doesn't prevent a compile error when !HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY.
  2. [I believe] This kind of error should result into NoMethodError by excluding whole method implementation and declaration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment!
The approach in 2. looks good so I went with that in 80e9ef1.

@unasuke
Copy link
Contributor

unasuke commented Jul 1, 2023

as these methods are needed to implement protocols that rely on X25519/X448 raw private/public key support (such as Hybrid Public Key Encryption and QUIC).

Yes, QUIC (or rather TLS1.3) SHOULD support key exchange with X25519.

https://www.rfc-editor.org/rfc/rfc8446.html#section-9.1

This patch helps people who want to implement TLS 1.3. This is what I wanted.

@rhenium
Copy link
Member

rhenium commented Jul 1, 2023

Thank you for working on this!

I removed the private? / public? methods and marshalling support that relies on these methods, as these methods are causing old versions of OpenSSL to not work.

Sounds reasonable. It is a difficult task to support marshaling OpenSSL::PKey::PKey because it can be wrapping a wide variety of EVP_PKEYs. There is no convenient way to tell whether an EVP_PKEY represents a private key, a public key, or just key parameters, either. #527 is my abandoned effort to define #private? and #public? on OpenSSL::PKey::PKey.

@rhenium
Copy link
Member

rhenium commented Jul 1, 2023

I find the method naming confusing. In my understanding, "raw {private,public} key" is an OpenSSL term to mean the keys specifically used by X25519/Ed25519 family (at least currently), and these new methods are not supposed to work for other PKey types, such as RSA. I want to see the method names reflect it.

IMHO, using OpenSSL's function names (with "EVP_PKEY_" and "EVP_PKEY_get_" stripped) just fits here:

  • OpenSSL::PKey.new_raw_private_key
  • OpenSSL::PKey.new_raw_public_key
  • OpenSSL::PKey::PKey#raw_private_key
  • OpenSSL::PKey::PKey#raw_public_key

What do you think?

@rhenium
Copy link
Member

rhenium commented Jul 1, 2023

/cc @bdewater

* See the OpenSSL documentation for EVP_PKEY_new_raw_private_key()
*/

#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
Copy link
Member

@rhenium rhenium Jul 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think #ifdef needs to be moved above the doc comment so that rdoc can parse it correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed @ 202cac7 .
Maybe the first and second #ifdef blocks can be merged into one, but I thought having it separated would make it easier to understand that each method needs this condition to be met.


pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
if (!pkey)
ossl_raise(ePKeyError, "Could not parse PKey");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message should be adjusted because parsing isn't happening here (perhaps just "EVP_PKEY_new_raw_private_key" would work).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed @ e9ccf58

ext/openssl/ossl_pkey.c Show resolved Hide resolved
size_t len;

GetPKey(self, pkey);
EVP_PKEY_get_raw_private_key(pkey, NULL, &len);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably needs an error check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed @ 6628df1.
Maybe we could have a different error message for this call and the next call, but it could be difficult to tell what exactly caused the error on each call so I think leaving as it is now would be reasonable.

*/

#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
static VALUE ossl_pkey_private_to_raw(VALUE self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: please insert a newline after VALUE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed @ 26bd41d

OpenSSL::PKey.read(string)
end
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be removed too, as this would be unreachable now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed @ 4d67841

@sylph01
Copy link
Contributor Author

sylph01 commented Jul 7, 2023

OpenSSL::PKey.new_raw_private_key
OpenSSL::PKey.new_raw_public_key
OpenSSL::PKey::PKey#raw_private_key
OpenSSL::PKey::PKey#raw_public_key

Yes, I think these names would convey the intention that these aren't supposed to be used except for algorithms that support "raw public/private keys".

The fixes on the code are as below:

  • The renaming is done @ edadf2f
  • Documentation fix wasn't complete on the last commit so did that on f721fd0
  • Then renamed C functions to match its method name @ 18623dd

Copy link
Member

@rhenium rhenium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some minor comments, but otherwise it looks good to me!

static VALUE
ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
{

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this empty line.

pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
if (!pkey)
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line has trailing spaces.


/*
* call-seq:
* pkey.sign(digest, data) -> String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line looks like a copy-and-paste error.

rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
rb_define_method(cPKey, "raw_public_key", ossl_pkey_raw_public_key, 0);
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we group the #raw_*_key methods together?

* See the OpenSSL documentation for EVP_PKEY_get_raw_public_key()
*/

static VALUE ossl_pkey_raw_public_key(VALUE self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please insert a newline before the function name.

- Remove trailing whitespaces
- Remove extra line break
- Styling of function definition
- Remove wrong documentation
- Group raw_*_key methods together
@sylph01
Copy link
Contributor Author

sylph01 commented Jul 8, 2023

  • Applied styling fixes @ c40d4fe
  • Minor documentation fix @ 23262c2, where I fixed the documentation to say pkey instead of key as receiver object to match other occurences.

@rhenium rhenium merged commit 3f29525 into ruby:master Jul 12, 2023
@rhenium
Copy link
Member

rhenium commented Jul 12, 2023

I've merged this to master now. Thank you!

matzbot pushed a commit to ruby/ruby that referenced this pull request Jul 12, 2023
(ruby/openssl#646)

Add OpenSSL::PKey.new_raw_private_key, #raw_private_key and public
equivalents. These methods are useful for importing and exporting keys
that support "raw private/public key". Currently, OpenSSL implements
X25519/X448 and Ed25519/Ed448 keys.

[rhe: rewrote commit message]

ruby/openssl@3f29525618

Co-authored-by: Bart de Water <bartdewater@gmail.com>
@bdewater
Copy link
Contributor

Thank you both for getting this over the finish line!

anakinj pushed a commit to anakinj/openssl that referenced this pull request Feb 17, 2024
Add OpenSSL::PKey.new_raw_private_key, #raw_private_key and public
equivalents. These methods are useful for importing and exporting keys
that support "raw private/public key". Currently, OpenSSL implements
X25519/X448 and Ed25519/Ed448 keys.

[rhe: rewrote commit message]

Co-authored-by: Bart de Water <bartdewater@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants