Skip to content
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

Integrate OAEP and PSS into existing interface #43

Merged
merged 22 commits into from
Jun 11, 2020
Merged

Conversation

dignifiedquire
Copy link
Member

This integrates #18 and #26 by expanding the use of PaddingScheme to 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

This was referenced Mar 6, 2020
@str4d
Copy link
Contributor

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 PublicKey trait) is not present in the overall PR, and I can't find where that is undone, but I think it's in the refactor commit? GitHub isn't helping much here. There are other effects of that commit that are not undone (in particular, it makes public keys non-generic in pkcs1v15, the opposite of what I've done in #44).

How painful would it be to just drop that commit entirely? It would likely make the history clearer.

@str4d
Copy link
Contributor

str4d commented Mar 6, 2020

Hmm, the change in that commit to store an RSAPublicKey inside RSAPrivateKey does make sense to me. I'd personally alter that commit to only contain the pieces we want, but that's probably more hassle than it's worth, so just reverting changes we don't want is probably fine.

@dignifiedquire
Copy link
Member Author

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
Copy link
Member Author

@str4d I made the OAEP and PSS impls generic over the Key traits now

Copy link
Contributor

@roblabla roblabla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey that looks great! I have a couple suggestions

src/key.rs Outdated Show resolved Hide resolved
src/key.rs Outdated Show resolved Hide resolved
write!(f, "PaddingScheme::PKCS1v15({:?})", hash)
}
PaddingScheme::OAEP { ref label, .. } => {
// TODO: How to print the digest name?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could make it Box<dyn DynDigest + Debug> if really necessary. AFAIK all the digest crates have debug unconditionally implemented on them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that doesn't work, the compiler is upset about more than one non auto trait :(

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

src/padding.rs Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
@tarcieri
Copy link
Member

tarcieri commented Mar 8, 2020

Relevant to the PSS use case: final two week comment period for shipping signature 1.0: RustCrypto/traits#78

@@ -94,6 +97,13 @@ impl Drop for RSAPrivateKey {
}
}

impl Deref for RSAPrivateKey {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have PrivateKey: PublicKeyParts, this impl Deref should be removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite, removing this, removes the ability to call .verify on a private key

src/key.rs Outdated Show resolved Hide resolved
Comment on lines +31 to +33
if pad_size < ciphertext.len() {
return Err(Error::Verification);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check also be in raw_decryption_primitive?

src/key.rs Outdated Show resolved Hide resolved
src/key.rs Outdated Show resolved Hide resolved
src/pss.rs Outdated Show resolved Hide resolved
src/pss.rs Outdated Show resolved Hide resolved
src/pss.rs Outdated Show resolved Hide resolved
src/pss.rs Outdated Show resolved Hide resolved
src/pss.rs Outdated Show resolved Hide resolved
@dignifiedquire
Copy link
Member Author

@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> },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, which is why it is still an Option, not sure what you mean.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

src/algorithms.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@str4d str4d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a close review pass over the RSAES-OAEP implementation, comparing it against RFC 8017 section 7.1.

src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
src/oaep.rs Outdated Show resolved Hide resolved
@dignifiedquire
Copy link
Member Author

@str4d applied the new round of CR

Copy link
Contributor

@str4d str4d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are causing the current CI failure.

benches/key.rs Outdated Show resolved Hide resolved
benches/key.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@str4d str4d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a close review pass over the RSASSA-PSS implementation. The relevant sections of RFC 8017 were already inlined as comments.

Comment on lines +193 to +201
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)?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +78 to +80
// 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Length is not checked, but with the current API we never actually see the message.

Comment on lines +149 to +151
// 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Length is not checked, but with the current API we never actually see the message.

src/pss.rs Outdated
// 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)];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be clearer (and closer to the spec):

Suggested change
let h = &mut h[..(em_len - 1) - (em_len - h_len - 1)];
let h = &mut h[..h_len];

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +193 to +195
None => (0..=em_len - (h_len + 2))
.rev()
.try_fold(None, |state, i| match (state, db[em_len - h_len - i - 2]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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]) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change results in failures, haven't debugged why, gonna leave the logic as is for now

src/pss.rs Outdated
Comment on lines 207 to 214
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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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);
}
let (zeroes, rest) = db.split_at(em_len - h_len - s_len - 2);
if zeroes.iter().any(|e| *e != 0x00) || rest[0] != 0x01 {
return Err(Error::Verification);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

src/pss.rs Outdated
let h0 = hash.result_reset();

// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
if Into::<bool>::into(h0.ct_eq(h)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if Into::<bool>::into(h0.ct_eq(h)) {
if h0.ct_eq(h).into() {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

src/pss.rs Outdated
return Err(Error::Internal);
}

let (db, h) = em.split_at_mut(em_len - s_len - h_len - 2 + 1 + s_len);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +122 to +125
// 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@str4d str4d mentioned this pull request Jun 10, 2020
@dignifiedquire dignifiedquire merged commit 3bd9795 into master Jun 11, 2020
@dignifiedquire dignifiedquire deleted the oaep-dig branch June 11, 2020 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants