Feat: Added support for JWK decoding Ed25519 keys - #506
Conversation
|
Thanks for the review @arckoor! I have implemented the requested changes if I could please get a re-review. |
arckoor
left a comment
There was a problem hiding this comment.
Sorry this took so long to get back to.
Classifying Ed448 curves properly and later on rejecting them with UnsupportedAlgorithm is a nice idea, however doing it like this is misleading for PEM users:
let privkey_pem = include_bytes!("private_ed448_key.pem");
let encoding_key = EncodingKey::from_ed_pem(privkey_pem).unwrap(); // this succeeds
let claims = Claims {... };
let token = encode(&Header::new(Algorithm::EdDSA), &claims, &encoding_key).unwrap(); // obviously fails because it's not EdDSA, but there is also no Ed448 algorithm here :/If I can construct a (PEM) key, I'd expect to be able to sign with it.
So I'd prefer if that bit were removed. Should also mean that you don't need classify_ed_curve at all anymore, and can just match based on key length
| pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, | ||
| traits::PublicKeyParts, | ||
| }; | ||
| use ed25519_dalek::SigningKey; |
There was a problem hiding this comment.
Import as Ed25519SigningKey
| // --- EllipticCurve Constants --- | ||
| // Key Lengths | ||
| // ED25519: https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 | ||
| pub(crate) const ED25519_PUBLIC_KEY_LENGTH: usize = 32; | ||
| // ED448: https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.5 | ||
| pub(crate) const ED448_PUBLIC_KEY_LENGTH: usize = 57; |
| } | ||
|
|
||
| pub struct EdDSAVerifier(VerifyingKey); | ||
| pub struct EdDSAVerifier(pub VerifyingKey); |
|
@arckoor I'm made the changes and added a test case for extracting the PEM key which now fails as expected 👍 |
|
Sorry, I should have been clearer, I meant to remove the 448 stuff entirely
The 448 test is good to keep though |
|
@arckoor I have removed all Ed448 references now (keeping the test). Let me know if there is anything else you would like me to change. |
| let (curve_type, x) = match &key.kind() { | ||
| DecodingKeyKind::SecretOrDer(pub_bytes) => { | ||
| match pub_bytes.len() { | ||
| // ED25519: https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 | ||
| 32 => (EllipticCurve::Ed25519, pub_bytes), | ||
| _ => return Err(ErrorKind::InvalidEddsaKey.into()), | ||
| } | ||
| } | ||
| _ => return Err(ErrorKind::InvalidKeyFormat.into()), | ||
| }; |
There was a problem hiding this comment.
In #515 should be able to use try_get_as_bytes (just note to self so I don't forget :p)
| // Note: here we will receive a DER key which contains a 16 byte ANS.1 header | ||
| let curve_type: EllipticCurve = match key.inner().len() { | ||
| // 16 byte header + 32 byte Ed25519 key | ||
| 48 => Ok(EllipticCurve::Ed25519), |
There was a problem hiding this comment.
do we need that check @arckoor? That's going to error for v2 eg aws-lc-rs::Ed25519KeyPair::generate_pkcs8 (unless it's handled somewhere else/am misusing it)
There was a problem hiding this comment.
I mean yeah, but isn't this actually the job of PemEncodedKey? For the from_der methods, users are currently just required to pass the "correct" key format, but for PEM we do a fair amount of parsing. For public keys we at least try to extract the public key bytes from whatever wrapper there is, for private keys we just use the inner content. E.g. this breaks completely
use aws_lc_rs::signature::Ed25519KeyPair;
use jsonwebtoken::{Algorithm, EncodingKey, jwk::Jwk};
fn main() {
let rng = aws_lc_rs::rand::SystemRandom::new();
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
let pem = pem::encode(&pem::Pem::new("PRIVATE KEY", pkcs8.as_ref().to_vec()));
let encoding_key = EncodingKey::from_ed_pem(pem.as_bytes()).unwrap();
Jwk::from_encoding_key(&encoding_key, Algorithm::EdDSA).unwrap(); // panics, len 83
}IMO EncodingKey and DecodingKey should convert any incoming key into a format that the rest of the library can use. If that is done properly, checks like this can just be omitted everywhere.
I have toyed with the idea of replacing simple_asn1 with der (for zero alloc parsing, which would remove the zeroizing troubles from PemEncodedKey entirely)
With that done I'd also like to refactor the from_der methods to actually accept proper DER encodings, so accepting V2 could be done there as well
There was a problem hiding this comment.
Honestly it would be nice if people didn't have to know what they are using (is it der? is it pcks v1? maybe v2? pem?) and we just look at the format to infer the type of keys.
There was a problem hiding this comment.
Yes, EncodingKey::new(bytes: &[u8], alg: Algorithm) would be very neat to have I think
Would perhaps still need an extra distinction like we have now for HMAC, but for the asymmetric stuff it should definitely be possible
There was a problem hiding this comment.
Yep, something like that was exactly was I was thinking
Summary: Added decoding JWK support for Ed25519 keys
Changes:
unimplementedJwk::from_encoding_keywhenAlgorithm::EdDSAxparameter (https://datatracker.ietf.org/doc/html/rfc8037#section-2)EllipticCurvetypeEd448for Ed448 curvesSolves: #244