-
Notifications
You must be signed in to change notification settings - Fork 31
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 TryFrom from &[u8] and Vec<u8> to PrivateKeyDer #39
Conversation
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.
src/lib.rs
Outdated
|
||
fn try_from(key: &'a [u8]) -> Result<Self, Self::Error> { | ||
if key.len() <= 5 || key[0] != 0x30 { | ||
return Err("Invalid key format"); |
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.
Nit: I think we prefer lowercase error strings (also for the other instance).
src/lib.rs
Outdated
fn try_from(key: Vec<u8>) -> Result<Self, Self::Error> { | ||
let private_key_der = PrivateKeyDer::try_from(&key[..])?; | ||
|
||
match private_key_der { |
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.
Nit: would be nice to hoist the Ok()
out of the match
.
src/lib.rs
Outdated
type Error = &'static str; | ||
|
||
fn try_from(key: Vec<u8>) -> Result<Self, Self::Error> { | ||
let private_key_der = PrivateKeyDer::try_from(&key[..])?; |
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.
Nit: I think it would be okay to just inline the binding here? (As in, match PrivateKeyDer::try_from(&key[..])? { .. }
.)
src/lib.rs
Outdated
skip_len += key[1] - 0x80; | ||
} | ||
|
||
match key[skip_len as usize..] { |
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.
Not a real review, but I think this panics with this path:
key[0] := 0x30
andkey.len() > 5
to get us hereskip_len := 2
key[1] := 0xff
, saykey[1] >= 0x81
passesskip_len += 0xff - 0x80
, ie. 129key[129..]
would panic if key is too short
I would suggest using get(skip_len as usize..)
and then adding Some()
around the match arms.
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.
In the limit, this could benefit from a little fuzzing, I suppose? Shouldn't be too hard to bring up a cargo fuzz
harness...
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 did as you said. Maybe we should additionally limit skip length to small number. For example 4 bytes, as that would indicate 4GB payload. (If I understood it correctly)
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.
In the limit, this could benefit from a little fuzzing, I suppose? Shouldn't be too hard to bring up a
cargo fuzz
harness...
If it is okay to add a more complex test, I would like to use openssl to generate large number of private keys programmatically and ensure the function chooses a correct key type.
Fuzzing to detect panics is lesser concern for me (as the function is small).
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.
If it is okay to add a more complex test, I would like to use openssl to generate large number of private keys programmatically and ensure the function chooses a correct key type.
Depends on the complexity? I think several hundred lines of code would be worth it if it doesn't blow up test cycle times beyond a few minutes.
Fuzzing to detect panics is lesser concern for me (as the function is small).
Given that the code already tried to be pretty careful and a panic could still happen makes me a little less optimistic, I guess!
6a8c6ea
to
58c67dc
Compare
I tried with more keys generated programmatically and the conversion failed. Please wait for me to fix the issues before review. (I guess the function will become more complex to cover different types) |
It seems openssl bindings somehow flipped some of the formats for RSA (use pkcs1 for pkcs8 and vice versa) or at least I hope so. I will investigate this more later. I removed the key files as I added programmatic tests which passes without changing the conversion function. Edit: I have opened this issue at openssl crate. It seems to me that the formats are indeed flipped somehow. |
e8868b9
to
037cfe1
Compare
src/lib.rs
Outdated
Some([0x02, 0x01, 0x00, // Version = 0 | ||
0x30, 0x0D, // Sequence of two elements, which have 13 bytes length | ||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, // Object Identifier for RSA Encryption (PKCS #1) | ||
..]) => { | ||
Ok(Self::Pkcs1(key.into())) | ||
} | ||
|
||
Some([0x02, 0x01, 0x01, // This is decoded as PKI header with value 1 | ||
..]) => { | ||
Ok(Self::Sec1(key.into())) | ||
} | ||
|
||
Some([0x02, 0x01, 0x00, // Version = 0 | ||
..]) => { | ||
Ok(Self::Pkcs8(key.into())) | ||
} |
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.
At the moment PKCS#1 and PKCS#8 cases are switched. Additionally, PKCS#8 shouldn't be specific to the rsaEncryption
OID. I think we should look for:
- PKCS#8: sequence, integer 0, sequence (ie,
Some([0x02, 0x01, 0x00, 0x30])
) - PKCS#1: sequence, integer 0
- SEC1: sequence, integer 1
I think a static set of sample/test files is nicer than an openssl dependency (even if it it's just a dev-dependency). It also has the advantage of being 100% reproducible.
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.
There are some suitable files in https://github.com/rustls/rustls/tree/main/rustls/src/testdata (rsa*.der, eddsakey.der, nist*.der)
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.
At the moment PKCS#1 and PKCS#8 cases are switched.
Oh sorry.........
I think a static set of sample/test files is nicer than an openssl dependency (even if it it's just a dev-dependency). It also has the advantage of being 100% reproducible.
Yes I could add it but I prefer to also keep the openssl tests, as I can simply increase the iterations number and try many keys of the same algorithm to check that they are valid.
5dd8225
to
f53fbcc
Compare
I removed openssl test and went back to static files. |
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 think the general approach here is workable, but I'd like to see it written with less magic values and more information about the heuristics in use and why we think they're stable signals.
In reviewing the diff I adapted your code to be closer to what I'm thinking of. Rather than deliver a bunch of review feedback I opened a second PR based on your own with what I'm thinking: #40
WDYT?
// We skip the tag and the length of the sequence | ||
let mut skip_len = 2; | ||
|
||
// We skip additonal length bytes |
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.
typo here:
// We skip additonal length bytes | |
// We skip additional length bytes |
I am closing this PR because I think the PR there is much better. We can continue discussion there. |
I am not very confident about this yet.
I am mainly using asn1 decoders to learn the differences manually.
In addition, the approach is heuristic in nature because it would be hard to implement a more complex parser.
I am planning to add more test private key files.