Hi, I have a use-case that is roughly the following (simplified):
- A private key exists only in an SGX enclave
- A stream of ciphertexts are passed to the enclave, which it attempts to decrypt
- It must remain a secret which ones were decryptable by the enclave and which ones aren't, taking into account side-channel leakage via code and data access patterns.
Unfortunately I can't quite do that with the aes-gcm code as is:
fn decrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag,
) -> Result<(), Error> {
if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}
// TODO(tarcieri): interleave encryption with GHASH
// See: <https://github.com/RustCrypto/AEADs/issues/74>
let mut expected_tag = self.compute_tag(associated_data, buffer);
let mut ctr = self.init_ctr(nonce);
ctr.apply_keystream(&self.cipher, expected_tag.as_mut_slice());
use subtle::ConstantTimeEq;
if expected_tag.ct_eq(&tag).unwrap_u8() == 1 {
ctr.apply_keystream(&self.cipher, buffer);
Ok(())
} else {
Err(Error)
}
}
What I would really like is a version of this function that looks like this for example:
fn ct_decrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag,
) -> bool {
if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
return false;
}
// TODO(tarcieri): interleave encryption with GHASH
// See: <https://github.com/RustCrypto/AEADs/issues/74>
let mut expected_tag = self.compute_tag(associated_data, buffer);
let mut ctr = self.init_ctr(nonce);
ctr.apply_keystream(&self.cipher, expected_tag.as_mut_slice());
ctr.apply_keystream(&self.cipher, buffer);
use subtle::ConstantTimeEq;
bool::from(expected_tag.ct_eq(&tag))
}
The key differences being:
ctr.apply_keystream(&self.cipher, buffer);
happens unconditionally and we never branch on the outcome of mac check
- Instead of returning
Result
, we return bool
, or perhaps some subtle
type. Because afaik there is no branchless way to access Result
. (I don't know that I can assume that result.is_ok()
won't contain a branch? I assume it boils down to a rust match
against an enum, and would contain a branch unless the compiler optimized the branch away. I would hope that llvm can optimize this though.) But also, it's not clear that in rust, you can create Result
type without branching?
What do you think? Would you take a patch that
(1) Adds ct_detached_in_place_decrypt
to trait AeadInPlace
in RustCrypto/traits? (and possibly, makes detached_in_place_decrypt
chain to this by default?)
OR
(2) Adds a new trait AeadInPlaceCt
or similar in RustCrypto/traits , and adds the function there, and implements it on AesGcm
struct?
Slight preference for not putting subtle
types in an API because it will ease versioning?