-
-
Notifications
You must be signed in to change notification settings - Fork 424
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
remove unnecessary Result-wrapping in crypto.rs
#474
remove unnecessary Result-wrapping in crypto.rs
#474
Conversation
9e79b45
to
78efa92
Compare
78efa92
to
8f29bf9
Compare
These aren't I think you're right about EncryptionError, though. Someone has to check that these keys are the right length, but it can happen a level up. And then I think DecryptionError collapses down to a struct instead of a one-case enum (not that it matters so much for an implementation detail). |
8f29bf9
to
fbcaa0e
Compare
fbcaa0e
to
d71fe76
Compare
crypto.rs
crypto.rs
d71fe76
to
e144dca
Compare
Ok, so in light of this I've changed it to However, on top of that, I've also kept in:
I can revert any of that as desired. |
/// bad ciphertext: {0} | ||
/// | ||
/// Either the input is malformed, or the MAC doesn't match on decryption. | ||
/// | ||
/// These cases should not be distinguished; message corruption can cause either problem. | ||
#[derive(Debug, Display, Error)] | ||
#[ignore_extra_doc_attributes] | ||
pub struct DecryptionError(pub &'static str); |
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.
Nitpick: For some reason displaydoc on structs weirds me out; maybe it's because enum cases often get abbreviated docs anyway in a way that types don't. Mind switching to thiserror-style for the Display representation instead?
BadCiphertext(&'static str), | ||
} | ||
// TODO: Could be nice to have a type-safe library for manipulating units of bytes safely. | ||
const BITS_PER_BYTE: usize = std::mem::size_of::<u8>() * 8; |
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.
Rust guarantees that size_of::<u8>()
is 1, so I think it's fine to just write 8 here. (The named constant still makes sense though.)
Ok(ctext) | ||
} | ||
/// Use AES-256 in CTR mode. | ||
pub mod ctr { |
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 don't know if I really see the point of this. Yes, there's a little more organization, but "CTR" and "CBR" don't imply "AES-256" by themselves, and they're all just convenience wrappers for the session implementation in any case.
} | ||
/// Length in bytes of the [`Hmac`] key used for [`aes_256_ctr_hmac_encrypt`] and | ||
/// [`aes_256_ctr_hmac_decrypt`]. | ||
pub const MAC_KEY_LENGTH: usize = 80 / BITS_PER_BYTE; |
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.
The MAC key isn't 80 bits; only the MAC itself is. The key can in theory be any length and in practice we use a 32-byte key.
let key: [u8; AES_256_KEY_SIZE] = | ||
hex::decode("603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4") | ||
.expect("valid hex") | ||
.try_into() | ||
.expect("correct array size"); |
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.
The hex_literal
crate used elsewhere in the workspace can check these conditions at compile time; if you're adding more tests it might be a good time to switch over (incrementally).
@@ -24,7 +24,7 @@ | |||
|
|||
mod address; | |||
mod consts; | |||
mod crypto; | |||
pub(crate) mod crypto; |
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.
This doesn't need to be changed, right?
&sender_eph_keys.mac_key, | ||
) | ||
.expect("just generated these keys, they should be correct"); | ||
array_ref![&sender_eph_keys.mac_key, 0, crypto::ctr::MAC_KEY_LENGTH], |
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.
…and this is where the MAC key is incorrectly truncated, and our lack of "decrypting this existing message with this session state" tests has come back to bite us.
Err(crypto::DecryptionError::BadKeyOrIv) => { | ||
log::warn!( | ||
"{} session state corrupt for {}", | ||
current_or_previous, | ||
remote_address, | ||
); | ||
return Err(SignalProtocolError::InvalidSessionStructure( | ||
"invalid receiver chain message keys", | ||
)); | ||
} |
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.
This is the change that convinced me this is a good idea: at first I thought "what happens if those fields are corrupt"? But they can't be, because we already check that when getting the message keys out of the session state, and that wasn't in the type system anywhere.
log::error!( | ||
"incoming sender key state corrupt for {}, distribution ID {}, chain ID {}", | ||
sender, | ||
"outgoing sender key state corrupt for distribution ID {}", |
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.
Copy/paste error: "incoming" got switched for "outgoing" here and below.
let cipher_key: [u8; crypto::AES_256_KEY_SIZE] = | ||
message_keys.cipher_key().try_into().map_err(|_| { | ||
log::error!( | ||
"outgoing sender key state corrupt for distribution ID {}", | ||
distribution_id, | ||
); | ||
SignalProtocolError::InvalidSenderKeySession { distribution_id } | ||
})?; | ||
let iv: [u8; crypto::AES_NONCE_SIZE] = message_keys.iv().try_into().map_err(|_| { | ||
log::error!( | ||
"outgoing sender key state corrupt for distribution ID {}", | ||
distribution_id, | ||
); | ||
SignalProtocolError::InvalidSenderKeySession { distribution_id } | ||
})?; | ||
let ciphertext = crypto::cbc::aes_256_cbc_encrypt(plaintext, &cipher_key, &iv); |
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.
This duplication is a little annoying, but I can't see a great way to clean it up. Do you think it's worth putting that error generation into a local helper?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
This issue has been closed due to inactivity. |
Broken out from #287.
ctr
andcbc
submodules.pub const
sizes for array lengths.EncryptionError
and one case ofDecryptionError
.aes_256_*
andaes_256_ctr_hmac_*
for consistency.TODO