NSS rc_crypto replacement part 1 #998
Conversation
|
It wasn't clear whether this was ready for review but I wanted to leave some preliminary comments. Sticking with the
|
|
|
||
| pub fn extract_and_expand( | ||
| salt: &hmac::SigningKey, | ||
| secret: &[u8], |
rfk
Apr 17, 2019
Member
Huh. It seems weird to me that salt is a SigningKey while secret is raw bytes, but this matches what ring does so shrug...I guess it just need to be this way in order for the underlying hmac calls to work correctly?
Huh. It seems weird to me that salt is a SigningKey while secret is raw bytes, but this matches what ring does so shrug...I guess it just need to be this way in order for the underlying hmac calls to work correctly?
eoger
Apr 17, 2019
•
Author
Contributor
Correct, from ring doc:
Salts have type hmac::SigningKey instead of &[u8] because they are frequently used for multiple HKDF operations, and it is more efficient to construct the SigningKey once and reuse it. Given a digest algorithm digest_alg and a salt salt: &[u8], the SigningKey should be constructed as hmac::SigningKey::new(digest_alg, salt).
Correct, from ring doc:
Salts have type hmac::SigningKey instead of &[u8] because they are frequently used for multiple HKDF operations, and it is more efficient to construct the SigningKey once and reuse it. Given a digest algorithm digest_alg and a salt salt: &[u8], the SigningKey should be constructed as hmac::SigningKey::new(digest_alg, salt).
| let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); | ||
| let expected_out = hex::decode( | ||
| "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", | ||
| ) |
rfk
Apr 17, 2019
Member
👍 I independently checked these test vectors against a known-good implementation
| Ok(out) | ||
| fn hmac(&self, ciphertext: &[u8]) -> Result<Signature> { | ||
| let key = SigningKey::new(&digest::SHA256, self.hmac_key()); | ||
| Ok(hmac::sign(&key, ciphertext)?) |
rfk
Apr 17, 2019
Member
Excellent cleanup here.
As discussed in meeting, a future iteration on this code should aim to move the whole "check hmac and decrypt" machinery into a single aead::open call of some sort. It will be complicated by sync needing to handle the ciphertext and hmac as two separate fields in the JSON, but I think it would be fine to require this code to concatenate them into a single buffer before calling rc_crypto to decrypt.
Excellent cleanup here.
As discussed in meeting, a future iteration on this code should aim to move the whole "check hmac and decrypt" machinery into a single aead::open call of some sort. It will be complicated by sync needing to handle the ciphertext and hmac as two separate fields in the JSON, but I think it would be fine to require this code to concatenate them into a single buffer before calling rc_crypto to decrypt.
| @@ -152,7 +136,7 @@ impl KeyBundle { | |||
| /// and the generated iv. | |||
| pub fn encrypt_bytes_rand_iv(&self, cleartext_bytes: &[u8]) -> Result<(Vec<u8>, [u8; 16])> { | |||
| let mut iv = [0u8; 16]; | |||
| openssl::rand::rand_bytes(&mut iv)?; | |||
| rand::fill(&mut iv)?; | |||
rfk
Apr 17, 2019
Member
Having this use a consistent RNG with the rest of the code is also a nice win.
Having this use a consistent RNG with the rest of the code is also a nice win.
1b769ab
to
57c7ba5
| } | ||
| ensure_nss_initialized(); | ||
| let result = unsafe { | ||
| nss_sys::NSS_SecureMemcmp( |
rfk
Apr 18, 2019
Member
👍 great!
| if base16::decode_slice(expected_hmac, &mut decoded_hmac).is_err() { | ||
| log::warn!("Garbage HMAC verification string: contained non base16 characters"); | ||
| return Ok(false); |
rfk
Apr 18, 2019
Member
I am very, very pleased to see this throwing an error rather than returning OK(false), excellent cleanup!
I am very, very pleased to see this throwing an error rather than returning OK(false), excellent cleanup!
|
I'd probably like to give this another pass after these fixes are made. None are large but there are several. |
| ulInfoLen: info.len() as u64 as CK_ULONG, | ||
| }); | ||
| let ptr_hkdf_params = Box::into_raw(hkdf_params); | ||
| let mut params = SECItem { |
thomcc
Apr 18, 2019
Contributor
This api is grody as hell.
This api is grody as hell.
|
|
||
| // Remap some constants. | ||
| pub const SECSuccess: SECStatus = _SECStatus_SECSuccess; | ||
| pub const SECFailure: SECStatus = _SECStatus_SECFailure; | ||
| pub const PR_FALSE: PRBool = 0; | ||
| pub const PR_TRUE: PRBool = 1; | ||
| pub const CK_FALSE: CK_BBOOL = 0; |
thomcc
Apr 18, 2019
Contributor
Gross.
Gross.
|
Well, hopefully this is all fine. I feel like there's a lot of potential cleanup we could do around SECItem in particular, but for now... It's fine. One last thing though, could you go through and make sure all our integer casts (e.g. |
| ) -> Result<SymKey> { | ||
| let mut item = SECItem { | ||
| type_: SECItemType::siBuffer, | ||
| data: buf.as_ptr() as *mut c_uchar, |
thomcc
Apr 18, 2019
Contributor
This kind of sucks that we have to cast *const u8 to *mut u8 here, but it shouldnt' write to it... hopefully.
This kind of sucks that we have to cast *const u8 to *mut u8 here, but it shouldnt' write to it... hopefully.
| unsafe fn PK11_FreeSymKey(symKey: *mut PK11SymKey); | ||
| unsafe fn PK11_DestroyContext(context: *mut PK11Context, freeit: PRBool); | ||
| unsafe fn PK11_GetInternalSlot() -> *mut PK11SlotInfo; | ||
| unsafe fn PK11_ImportSymKey(slot: *mut PK11SlotInfo, r#type: CK_MECHANISM_TYPE, origin: PK11Origin::Type, operation: CK_ATTRIBUTE_TYPE, key: *mut SECItem, wincx: *mut c_void) -> *mut PK11SymKey; |
thomcc
Apr 18, 2019
Contributor
We could say key: *const SECItem to make things closer to how they are in reality, but it's probably better to keep this mirroring the real declaration, even if it's not const-correct.
We could say key: *const SECItem to make things closer to how they are in reality, but it's probably better to keep this mirroring the real declaration, even if it's not const-correct.
Connects to #955.
Implemented crypto primitives