-
Notifications
You must be signed in to change notification settings - Fork 721
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 ECDSA P-256 signing support #207
Comments
|
See https://go-review.googlesource.com/#/c/3340/ and the papers it cites. |
Status update: All the checked-off stuff above is now in the main ring repo. |
Status update: More checked-off stuff is now in the ring repo. Unassigning myself in case somebody else wants to take this on before I get to it. |
Is it still planned? It was asked for jsonwebtoken I'm entirely unqualified to pick this up though :( |
I doubt that is true!
Yes, I plan to do land it shortly, after I take care of the PR backlog. |
I've started to take a look at this and have some skeleton code (caveat, it gives incorrect results and is not cleaned up, but it does compile and is laid out following the NSA Suite B Implementer's Guide to ECDSA Section 3.4.1). However, before I dig too much deeper I'd like to ask a couple of questions and maybe get some feedback:
Overall caveat: I have never worked on crypto before and Rust is still fairly new to me, but it has been enjoyable to work in this codebase, it is nicely organized! |
As far as your specific questions go, basically the answer is "I've written the code for it, but it isn't public yet." That is, I have written all this code, but the implementation isn't in a PR yet. The problem is that it is using a 100% random nonce, and I want to change it to use a construction like https://blogs.cisco.com/security/fips-and-deterministic-ecdsa-achieving-robust-security-and-conformance, which is what BoringSSL also now does. However, I haven't had time to write the DRBG implementation yet, and that's what this is blocked on. |
That's great to hear! I can see the benefit of being deterministic and keeping to what BoringSSL does. I'd be happy to help as time allows, but again crypto code is beyond my current expertise. |
I've added the initial implementation of P-256 ECDSA signing in e5a4fe9 and I updated the checklist above to indicate what work is needed to call this "complete." The nonce reuse hardening will be added soon. |
Use |
Apologies for the dumb question, is |
Yes, currently. However, I would accept a PR that adds the ability to generate a key pair object directly, without going through PKCS#8. |
PR #662 implements a hardening countermeasure against bad PRNGs. Please take a look and provide feedback. |
@briansmith I'm trying to figure it out how to generate key pair, sign and verify P-256 ECDSA signature in order to implement ES256 for jsonwebtoken. How can I get a public_key from P-256 ECDSA key_pair to use it with let rng = rand::SystemRandom::new();
let alg = &signature::ECDSA_P256_SHA256_ASN1_SIGNING;
let pkcs8 = signature::ECDSAKeyPair::generate_pkcs8(alg, &rng).unwrap();
let key_pair = signature::key_pair_from_pkcs8(alg, untrusted::Input::from(pkcs8.as_ref())).unwrap();
const MESSAGE: &'static [u8] = b"hello, world";
let sig = signature::sign(&key_pair, &rng, untrusted::Input::from(MESSAGE)).unwrap();
// signature::verify(
// alg,
// untrusted::Input::from(keys.public_key_bytes()),
// untrusted::Input::from(MESSAGE),
// untrusted::Input::from(sig.as_ref())
// ).unwrap(); |
To implement ES256 for jsonwebtoken we need an API that is dealing with keys in form of fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<String> { ... }
fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<bool> { ... } In ring we already have pub fn verify(alg: &VerificationAlgorithm, public_key: untrusted::Input, msg: untrusted::Input, signature: untrusted::Input) -> Result<(), error::Unspecified> { ... }
pub fn sign(alg: &SigningAlgorithm, private_key: untrusted::Input, rng: &rand::SecureRandom, msg: untrusted::Input) -> Result<Signature, error::Unspecified> { ... }
// pub fn sign(key_pair: &KeyPair, rng: &rand::SecureRandom, msg: untrusted::Input) -> Result<Signature, error::Unspecified> { ... } I'm willing to work on that issue, just need some guidance and help in understanding the ring API design. @briansmith what do you think? |
I might be wrong, but I don't think that is true. My understanding is that you can implement all parts of ES256 with opaque keys. The only thing you can't do is import/export keys in the JWT format. if you need key import/export for non-PKCS#8 keys, I am open to exposing such an API. Rather than adding a new variant of |
FWIW, the fact that the verify function takes the inputs as |
jsonwebtoken takes fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<String> https://github.com/Keats/jsonwebtoken/blob/master/src/crypto.rs#L91
If you meant JWK, jsonwebtoken don't use them and that is a good thing. JWS specification doesn't require using JWK. It is just one format of many and definitely not an efficient one.
It would be great. It solves one part of the problem. I believe, it also will be useful in general. Brian:
Vincent:
@briansmith @Keats Would be great if you both agreed on some solution. At the moment, it looks as a deadlock. JWT is how everybody does authorization in the Web today. It is kind of shame there is no proper solution in Rust. The impact is huge, and we all may only benefit from a solution. Thanks guys. Looking forward for your decision. I'm ready to spend my time on the issue if needed. |
That's not a good API. Even if the authors want to limit the use of ring types in that crate's API, they could do so like this: use ring::signature;
struct KeyPair {
inner: signature::KeyPair,
}
fn sign(signing_input: &str, key: &KeyPair) -> Result<String> (FWIW, I generally don't spend a lot of time helping people build abstractions over different crypto libraries because I think such abstractions do more harm than good. IMO it would be better to just use |
@briansmith Thanks for your quick response. I understand your position. In our case, we can't use even We have one web application issuing access tokens (IAM) and many web applications that verifying a signature of these access tokens. Thus, the first one needs to know only about the private key. As for others, they only need a public key, and we can't trust them the private key. It makes no sense using an asymmetric algorithm otherwise. Is it possible to split Personally, I would love to see something similar to Erlang's crypto library that allow you to generate both keys in just one function call: {Pub, Priv} = crypto:generate_key(ecdh, secp256r1) |
But then you have to have several functions depending on which algorithm to use, as HMAC won't have a key pair for example. Very often in the case of asymmetric signing, you only have the public key so you wouldn't be able to build a KeyPair as @manifest mentioned. |
HMAC and digital signatures shouldn't have the same API anyway. The fact that the syntax is the same for both in JOSE is widely seen as one of its biggest design flaws. |
I still don't understand how the problem that I described might be solved. @briansmith could you please elaborate on how you see using ring to verify a signature in the untrusted environment? |
@briansmith Sorry to bother you, I'm just trying to find a solution for our problem.
It's what we need to solve the problem described above and start using ring library, or maybe you could suggest a different approach. |
I'm getting the impression we might be talking past each other since we seem to have some misunderstandings.
The ECDSA verification API already takes Only the ECDSA signing API requires a KeyPair object as input. So my suggestion is to change the JWT crate to require a In the future we'll have a type |
How can |
Incidentally, it turns out I already implemented the thing that was originally being asked for: |
@briansmith I'm following the steps you described above. I've generated There is Example let rng = rand::SystemRandom::new();
let alg = &signature::ECDSA_P256_SHA256_ASN1_SIGNING;
let pkcs8 = signature::ECDSAKeyPair::generate_pkcs8(alg, &rng).unwrap();
let key_pair = signature::key_pair_from_pkcs8(alg, untrusted::Input::from(pkcs8.as_ref())).unwrap();
const MESSAGE: &'static [u8] = b"hello, world";
let sig = signature::sign(&key_pair, &rng, untrusted::Input::from(MESSAGE)).unwrap();
// signature::verify(
// alg,
// untrusted::Input::from(keys.public_key_bytes()),
// untrusted::Input::from(MESSAGE),
// untrusted::Input::from(sig.as_ref())
// ).unwrap(); |
@Keats @manifest Please try ring 0.14.0-alpha2. In particular, look at |
I'm closing this because for the most part it was done. There are some details that need to be taken care of but it doesn't make sense to leave this issue open any more. If you run into troubles, please file a new issue. |
Thanks. Everything looks good. The only problem that it's still impossible to retrieve a public key because public_key function is private. I've opened an issue. |
Thanks for the feedback. I replied in the issue you filed. Let's continue the discussion there. |
How do you use verify? I can't figure out how to in version 0.16.14? |
@moeziniaf Doesn't the example for Ed25519 work for P-256 too?: https://docs.rs/ring/0.16.14/ring/signature/index.html#signing-and-verifying-with-ed25519 |
Is implementation of RFC6979 still something that is planned for ring? I might have a go at implementing it then. |
This is harder than EdDSA because there's no code to do the actual ECDSA signing anymore; the
ECDSA_sign
function incrypto/ecdsa/ecdsa.c
was removed to help with the migration to the new Rust ECC code. So, a new implementation of ECDSA signing in Rust is necessary.For all platforms it is OK (preferable, even) to use
crypto::ec::PrivateKey::generate
for the key generation (i.e. base point multiplication). So, that part is already done.I have some code sitting around to do the constant-time modular inversions (mod
q
) and (modn
). I will try to integrate it...soonish.For 64-bit platforms, we can use
crypto/ec/p256-64.c
andcrypto/ec/p256-x86_64.c
for the variable point multiplication (the multiplication done during the signing operation). However, we don't have any usable code for 32-bit platforms yet (at least, not checked in).For 32-bit ARM, x86, and AAarch64, I will eventually import in the ecp_nistz256 field operations from OpenSSL. Actually, I'm planning to do this real soon. However, I don't think the ecp_nistz256 group operations are good to use for any platform except x86-64 (even then...). We might try, instead, adapting the p256-64.c variable point multiplication to work using the ecp_nistz256 field operations (which would require adapting it to work on Montgomery-encoded field elements).
Similarly, we need to do make new nonce generation, as the old C code that Adam Langley wrote was removed. It may be worth copying how golang is doing it. Regardless, it would be nice to do as much of this as possible in Rust.
NIST has some test vectors for ECDSA signing, which are the absolute minimum level of testing necessary before the API can be exposed.
There is also no code for serializing private keys or deserializing them, which is pretty essential to most users of ECDSA signing. This should probably be split off into its own task as it is a fair amount of work on its own.
See also any general notes about signing APIs in #205.
The text was updated successfully, but these errors were encountered: