Closed briansmith closed 7 months ago
@briansmith I'd be willing to take this on. Ring has almost everything we need to implement fscrypt in Rust. The remaining issue is getting a way to use AES-CTR-HMAC-SHA256 via ring.
It looks like the necessary steps are as follows:
int GFp_aes_ctr(const uint8_t *in, uint8_t *out, size_t len,
const AES_KEY *key, const uint8_t ivec[16]) {
(aes_ctr())(in, out, len, key, ivec);
}
aead/aes.rs
.aes_ctr()
wrapper and an Encrypt-then-MAC implementation to aead/aes_ctr.rs
.aead::Algorithm
for each type of hash we to use in the HMAC.Let me know if I'm missing something major here, or if these steps should be split into multiple PRs.
The main question I have is how we should specify the Hash used for the HMAC. We could either:
aead::AES_256_CTR_HMAC_SHA256
aead::AES_256_CTR_HMAC_SHA384
fn aes_256_ctr_hmac(digest_alg: &digest::Algorithm) -> aead::Algorithm
One potential problem might be the differences between Encrypt-then-MAC AES256-CTR-HMAC implementations. They can:
For example, assume we have an in-place AES256 CTR function
fn aes_ctr_in_place(in_out: &mut [u8], key: &[u8], iv: &[u8; 16]);
An in-place version of Paseto v1 encryption would look like:
struct Data<'a> {
header: &'a [u8],
footer: &'a [u8],
in_out_buf: &'a [u8],
mac: hmac::Signature,
nonce: [u8; 32],
}
fn paseto_v1(key: &aead::SealingKey, data: &mut Data) {
rand::SystemRandom::new().fill(&mut data.nonce);
let salt = hmac::SigningKey::new(&digest::SHA384, &data.nonce[:16]);
let prk = hkdf::extract(&salt, &key.key.ctx_buf);
let mut enc_key: [u8; 32];
hkdf::expand(&prk, "paseto-encryption-key", &mut enc_key);
let mut auth_key: [u8; 32];
hkdf::expand(&prk, "paseto-auth-key-for-aead", &mut auth_key);
aes_ctr_in_place(data.in_out_buf, &enc_key, &data.nonce[16:]);
let mut signer = hmac::with_key(&hmac::SigningKey::new(&digest::SHA384, &auth_key));
signer.update(data.header);
signer.update(&data.nonce);
signer.update(data.in_out_buf);
signer.update(data.footer);
data.mac = signer.sign();
}
While an in-place version of fscrypt's Wrap would look like:
struct Data<'a> {
in_out_buf: &'a [u8],
mac: hmac::Signature,
iv: [u8; 16],
}
fn fscrypt(key: &aead::SealingKey, in: &Data) {
rand::SystemRandom::new().fill(&mut data.iv);
let salt = hmac::SigningKey::new(&digest::SHA256, &[]);
let mut enc_auth: [u8; 64];
hkdf::extract_and_expand(&salt, &key.key.ctx_buf, &[], &mut enc_auth);
let (enc_key, auth_key) = split_at_mut(&mut enc_auth, 32);
aes_ctr_in_place(data.in_out_buf, enc_key, &data.iv);
let mut signer = hmac::with_key(&hmac::SigningKey::new(&digest::SHA256, auth_key));
signer.update(&data.iv);
signer.update(data.in_out_buf);
data.mac = signer.sign();
}
Please ignore the stuff about Paseto above. I think that simplifies this.
Closing this. I think we should probably add AES-CTR as a primitive so that people can build their own AES-CTR-HMAC.
This is needed for Paseto version #1 support. See https://github.com/paragonie/paseto/tree/master/docs/01-Protocol-Versions#version-1-compatibility-mode.
AES-CTR is already implemented internally; it's used by gcm.c. We "just" need to expose it to the Rust code in under aead/mod.rs and then use it in conjunction with the ring::hmac API.