Uh oh!
There was an error while loading. Please reload this page.
Integrate OAEP and PSS into existing interface - #43
Conversation
Removes the PublicKey trait, using Deref to provide the RSAPublicKey methods to RSAPrivateKey.
Test assertion is manual and done with openssl
str4d
commented
Mar 6, 2020
Hmm, pulling in 2462f1d from #18 goes in the opposite direction to #42. It looks like the main thing it does (remove How painful would it be to just drop that commit entirely? It would likely make the history clearer. |
str4d
commented
Mar 6, 2020
Hmm, the change in that commit to store an |
dignifiedquire
commented
Mar 6, 2020
Yeah, I changed a lot of things during the merge, which is..unfortunate. So at this point I am thinking of squashing this into two commits at the end likely (1) for oaep and (2) for pss, after doing/undoing all things we want to do. |
dignifiedquire
commented
Mar 6, 2020
@str4d I made the OAEP and PSS impls generic over the Key traits now |
roblabla
left a comment
There was a problem hiding this comment.
Hey that looks great! I have a couple suggestions
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| write!(f, "PaddingScheme::PKCS1v15({:?})", hash) | ||
| } | ||
| PaddingScheme::OAEP { ref label, .. } => { | ||
| // TODO: How to print the digest name? |
There was a problem hiding this comment.
Could make it Box<dyn DynDigest + Debug> if really necessary. AFAIK all the digest crates have debug unconditionally implemented on them.
There was a problem hiding this comment.
that doesn't work, the compiler is upset about more than one non auto trait :(
There was a problem hiding this comment.
Ah right, it's possible to make your own trait (e.g. trait MyDynDigest: DynDigest + Debug {}) and then use that as a Box<dyn MyDynDigest>, but at this point it's too much boilerplate just for the sake of getting the digest name to show IMO.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
tarcieri
commented
Mar 8, 2020
Relevant to the PSS use case: final two week comment period for shipping |
| } | ||
| } | ||
| impl Deref for RSAPrivateKey { |
There was a problem hiding this comment.
Now that we have PrivateKey: PublicKeyParts, this impl Deref should be removed.
There was a problem hiding this comment.
Not quite, removing this, removes the ability to call .verify on a private key
Uh oh!
There was an error while loading. Please reload this page.
| if pad_size < ciphertext.len() { | ||
| return Err(Error::Verification); | ||
| } |
There was a problem hiding this comment.
Should this check also be in raw_decryption_primitive?
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
dignifiedquire
commented
Apr 10, 2020
@str4d thanks for the review, I think I addressed all important things |
| /// Encryption and Decryption using PKCS1v15 padding. | ||
| PKCS1v15Encrypt, | ||
| /// Sign and Verify using PKCS1v15 padding. | ||
| PKCS1v15Sign { hash: Option<Hash> }, |
There was a problem hiding this comment.
The hash: None case appears to still be necessary for unpadded signatures (at least, that's what the test case in src/pkcs1v15.rs leads me to believe).
There was a problem hiding this comment.
Yes, which is why it is still an Option, not sure what you mean.
There was a problem hiding this comment.
I was just noting this because it wasn't immediately obvious during review that the Option<Hash> was necessary besides enabling the prior encryption case to specify None.
Uh oh!
There was an error while loading. Please reload this page.
str4d
left a comment
There was a problem hiding this comment.
I made a close review pass over the RSAES-OAEP implementation, comparing it against RFC 8017 section 7.1.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-Authored-By: str4d <thestr4d@gmail.com>
dignifiedquire
commented
Apr 11, 2020
@str4d applied the new round of CR |
str4d
left a comment
There was a problem hiding this comment.
These are causing the current CI failure.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
str4d
left a comment
There was a problem hiding this comment.
I made a close review pass over the RSASSA-PSS implementation. The relevant sections of RFC 8017 were already inlined as comments.
| None => (0..=em_len - (h_len + 2)) | ||
| .rev() | ||
| .try_fold(None, |state, i| match (state, db[em_len - h_len - i - 2]) { | ||
| (Some(i), _) => Ok(Some(i)), | ||
| (_, 1) => Ok(Some(i)), | ||
| (_, 0) => Ok(None), | ||
| _ => Err(Error::Verification), | ||
| })? | ||
| .ok_or(Error::Verification)?, |
There was a problem hiding this comment.
Is there precedent for detecting the salt length? I know it is possible due to the structure of DB, but there is no mention of doing so in RFC 8017. I can't read the IEEE documents that reference different possible salt lengths, which maybe discusses this. As is, we never exercise the explicit-salt-length case below.
There was a problem hiding this comment.
I don't know @roblabla I believe you wrote this code initially, do you have any answer to this?
| salt_len: Option<usize>, | ||
| digest: &mut dyn DynDigest, | ||
| ) -> Result<Vec<u8>> { | ||
| let salt_len = salt_len.unwrap_or_else(|| priv_key.size() - 2 - digest.output_size()); |
There was a problem hiding this comment.
Is there precedent for this default salt length? The RFC mentions that common choices are digest.output_size() and 0, while this is selecting the salt to be as large as could fit. I can't read the IEEE documents that reference different possible salt lengths. In any case, I don't think there's a problem with this, given that we are detecting the salt length in verify.
| // 1. If the length of M is greater than the input limitation for the | ||
| // hash function (2^61 - 1 octets for SHA-1), output "message too | ||
| // long" and stop. |
There was a problem hiding this comment.
Length is not checked, but with the current API we never actually see the message.
| // 1. If the length of M is greater than the input limitation for the | ||
| // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" | ||
| // and stop. |
There was a problem hiding this comment.
Length is not checked, but with the current API we never actually see the message.
| // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and | ||
| // let H be the next hLen octets. | ||
| let (db, h) = em.split_at_mut(em_len - h_len - 1); | ||
| let h = &mut h[..(em_len - 1) - (em_len - h_len - 1)]; |
There was a problem hiding this comment.
This would be clearer (and closer to the spec):
| let h = &mut h[..(em_len - 1) - (em_len - h_len - 1)]; | |
| let h = &mut h[..h_len]; |
| None => (0..=em_len - (h_len + 2)) | ||
| .rev() | ||
| .try_fold(None, |state, i| match (state, db[em_len - h_len - i - 2]) { |
There was a problem hiding this comment.
| None => (0..=em_len - (h_len + 2)) | |
| .rev() | |
| .try_fold(None, |state, i| match(state, db[em_len - h_len - i - 2]){ | |
| None => (0..em_len - h_len - 1) | |
| .try_fold(None, |state, i| match(state, db[i]){ |
There was a problem hiding this comment.
this change results in failures, haven't debugged why, gonna leave the logic as is for now
| for e in &db[..em_len - h_len - s_len - 2] { | ||
| if *e != 0x00 { | ||
| return Err(Error::Verification); | ||
| } | ||
| } | ||
| if db[em_len - h_len - s_len - 2] != 0x01 { | ||
| return Err(Error::Verification); | ||
| } |
There was a problem hiding this comment.
| for e in&db[..em_len - h_len - s_len - 2]{ | |
| if*e != 0x00{ | |
| returnErr(Error::Verification); | |
| } | |
| } | |
| if db[em_len - h_len - s_len - 2] != 0x01{ | |
| returnErr(Error::Verification); | |
| } | |
| let(zeroes, rest) = db.split_at(em_len - h_len - s_len - 2); | |
| if zeroes.iter().any(|e| *e != 0x00) || rest[0] != 0x01{ | |
| returnErr(Error::Verification); | |
| } |
| let h0 = hash.result_reset(); | ||
| // 14. If H = H', output "consistent." Otherwise, output "inconsistent." | ||
| if Into::<bool>::into(h0.ct_eq(h)) { |
There was a problem hiding this comment.
| ifInto::<bool>::into(h0.ct_eq(h)){ | |
| if h0.ct_eq(h).into(){ |
| return Err(Error::Internal); | ||
| } | ||
| let (db, h) = em.split_at_mut(em_len - s_len - h_len - 2 + 1 + s_len); |
There was a problem hiding this comment.
| let(db, h) = em.split_at_mut(em_len - s_len - h_len - 2 + 1 + s_len); | |
| let(db, h) = em.split_at_mut(em_len - h_len - 1); |
| // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length | ||
| // emLen - hLen - 1. | ||
| db[em_len - s_len - h_len - 2] = 0x01; | ||
| db[em_len - s_len - h_len - 1..].copy_from_slice(salt); |
There was a problem hiding this comment.
We're assuming here that the em buffer passed into emsa_pss_encode is already zeroed. This happens to be the case, but given that emsa_pss_encode is only called from one place, and this PR isn't attempting to be no-std compatible, it would be clearer if em was constructed and returned from this function. Non-blocking.
This integrates #18 and #26 by expanding the use of
PaddingSchemeto capture all optional values for the different schemes.It is not ideal, but it works well and keeps in line with the existing API for now. I think we should still continue to improve on the api and think through #34 more, but in the meantime this a version that folks can start to use, and isn't too different from the existing api