briansmith / ring

Safe, fast, small crypto using Rust
Other
3.68k stars 694 forks source link

Add AES-CTR-HMAC AEADs #656

Closed briansmith closed 7 months ago

briansmith commented 6 years ago

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.

josephlr commented 6 years 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.

josephlr commented 6 years ago

It looks like the necessary steps are as follows:

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:

josephlr commented 6 years ago

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();
}
briansmith commented 5 years ago

Please ignore the stuff about Paseto above. I think that simplifies this.

briansmith commented 7 months ago

Closing this. I think we should probably add AES-CTR as a primitive so that people can build their own AES-CTR-HMAC.