-
Notifications
You must be signed in to change notification settings - Fork 727
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
Allow exporting public components of RSAKeyPair
.
#441
Conversation
That is one huge CI setup. :) Thinking some more about just what's in this PR, I have 2 questions myself:
For the opposite direction (loading JWK), I want to propose an API change. This can be a separate PR just fine. My Rust is still not as fluent as I'd like it to be, but I think this should be possible:
Does that sound good? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for contributing this.
Please make the requested changes. Also, please add the contributor's statement to the end of your commit messages; see https://github.com/briansmith/ring#contributing.
src/rsa/signing.rs
Outdated
/// Exports the public modulus as big endian to the output array. | ||
/// | ||
/// The array should be of at least the size returned by | ||
/// `public_modulus_len`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest this (with fixed line wrapping to 80 characters):
/// Extracts the public modulus.
///
/// `out` must be exactly `public_modulus_len()` bytes long. It
/// will be filled with the big-endian-encoded bytes of the
/// public key, without any padding.
src/rsa/signing.rs
Outdated
/// `public_modulus_len`. | ||
pub fn export_public_modulus(&self, out: &mut [u8]) | ||
-> Result<(), error::Unspecified> { | ||
self.n.fill_be_bytes(out) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fill_be_bytes()
zero-pads the result. I think that JWT and similar things don't want zero-padded output, so there needs to be a check that out
is exactly the right length. This check would make this symmetric with the ring::signature::verify_rsa()
and other APIs exposed by ring.
Also, let's add unit tests that verify that we reject 1-byte-too-long and 1-byte-too-short out
, and accept correct-length out
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we leave this to the user? The user must allocate enough space somehow based on public_modulus_len
. If there's not enough space, fill_be_bytes
errors, if there's more space, it's padded, but that may be the user's intent. Doesn't verify_rsa
also accept padded input?
I'll look into the unit tests!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's an excerpt from the documentation for verify_rsa
:
/// `n` is the public key modulus and `e` is the public key exponent. Both are
/// interpreted as unsigned big-endian encoded values. Both must be positive
/// and neither may have any leading zeros.
These requirements come directly from JWA https://tools.ietf.org/html/rfc7518#section-6.3.1.1 (for n):
Note that implementers have found that some cryptographic libraries prefix an extra zero-valued octet to the modulus representations they return, for instance, returning 257 octets for a 2048-bit key, rather than 256. Implementations using such libraries will need to take care to omit the extra octet from the base64url-encoded representation.
and (for e):
For instance, when representing the value 65537, the octet sequence to be base64url-encoded MUST consist of the three octets [1, 0, 1];
Thus supporting extra zero padding doesn't do anybody any good, AFAICT.
src/rsa/signing.rs
Outdated
|
||
/// Return the public exponent. | ||
pub fn public_exponent(&self) -> u64 { | ||
self.e.value() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's better to output the exponent as a big-endian byte slice, like the modulus? That would be symmetric with ring::signature::verify_rsa
and export_public_modulus()
, and it is probably what all the callers need anyway.
src/rsa/bigint.rs
Outdated
GFp_BN_bn2bin_padded(out.as_mut_ptr(), out.len(), | ||
self.as_ref()) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this unsafe
implementation of fill_be_bytes()
to Nonnegative
, and then make the Modulus
and Elem
implementations be simple wrappers around it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how to do this. Modulus
contains a BN_MONT_CTX
, which owns a BIGNUM
, but Nonnegative
also wants ownership of its BIGNUM
.
bit_length
has the same issue. I can try to add both to BIGNUM
, then make the others wrappers.
@@ -218,6 +218,21 @@ pub struct Modulus<M> { | |||
ring: PhantomData<M>, | |||
} | |||
|
|||
impl<M> Modulus<M> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change this to (wrapping at 80 characters):
/// Exporting information about the value is only allowed for the public
/// modulus `N`, not for private moduli.
impl Modulus<super::N> {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we not allow this? JWK also has a private key mapping: https://tools.ietf.org/html/rfc7517#appendix-A.2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we not allow this? JWK also has a private key mapping: https://tools.ietf.org/html/rfc7517#appendix-A.2
In ring, when you construct a private or symmetric key object, there's no way to get the key material out. This way, you can safely pass an &RSAKeyPair
or &OpeningKey
to a function and know that that function won't ever leak the key.
If you look at Ed25519KeyPair::generate_serializable
, you can see that when we generate a key pair, we also output the bytes needed to serialize the key, along with the generated key pair. When we add RSA key generation to ring, we'll do it the same way.
It isn't perfect but I don't have a better suggestion either.
I agree.
Your suggestion is now tracked in #445. Let's discuss it there. I'll need some time to process it. |
This would fix #299. |
6a27497
to
13a4458
Compare
I've addressed feedback as far as I could. I don't see a reason why we wouldn't allow exporting the private components too, though I'm also not sure what that API would look like. Doing the same for all RSA private parts would add a slew of methods. But I don't have anything else. |
13a4458
to
b7b00d6
Compare
Coverage increased (+0.01%) to 93.538% when pulling b7b00d6db6545e9ac0eab59cce86a4d6c7f1a2ad on stephank:feat/rsa-export into cb57b1e on briansmith:master. |
b7b00d6
to
abec71d
Compare
Only reason this wasn't possible because verification needed the number of bits. Added an implementation of `bit_length` to `Modulus` to make this possible. I agree to license my contributions to each file under the terms given at the top of each file I changed.
I agree to license my contributions to each file under the terms given at the top of each file I changed.
Used internally in place of passing around an `(n, e)` pair, and as part of `RSAKeyPair`, but also part of the public API. Some of the `src/rsa/rsa.rs` functions and `RSAKeyPair` methods pertaining to the public key components have been moved to `RSAPublicKey` methods. Parts of `check_public_modulus_and_exponent` are now found in the `RSAPublicKey` constructors, while the `n_min_bits` and `n_max_bits` checks are still in a separate `check_modulus` method. (The parameters differ between verification and signing.) I agree to license my contributions to each file under the terms given at the top of each file I changed.
abec71d
to
15f5e6b
Compare
@stephank Sorry for the long delay on this. I independently did some foundational work for this. For example, I added the equivalent of the Lines 24 to 25 in 0c0f7c4
Now there are a lot of conflicts. I'm going to close this PR since there's an issue open for getting RSA public keys from RSA private keys and I think we should explore a new approach to resolving that. |
@briansmith No worries! Thanks for all the effort you're putting into ring & co, and sorry I couldn't keep up. :) |
There are two ways to configure an X509_STORE_CTX after X509_STORE_CTX_init. One can either modify the already initialized X509_VERIFY_PARAM or replace it. Modifying the existing one is more common. Replacing it actually misses some defaults. (See issue #441 for details.) In preparation for actually being able to test changes to the default, switch tests to that model. In doing so, no longer need to explicitly configure the depth and can test that default. (Though we should write tests for the depth at some point.) Bug: 439, 441 Change-Id: I254a82585d70d44eb94920f604891ebfbff4af4c Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/49745 Commit-Queue: David Benjamin <[email protected]> Commit-Queue: Adam Langley <[email protected]> Reviewed-by: Adam Langley <[email protected]>
In Portier, we load RSA private keys once, leveraging the DER parsing in Ring. However, we need to then serve the public keys as JWK to clients. These additions allow us to grab the public components of an
RSAKeyPair
to build the JWK.I agree to license my contributions to each file under the terms given at the top of each file I changed.