Skip to content
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

Expose a safe interface for EVP_AEAD #68

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

initsecret
Copy link

Fix #58

@jyn514 jyn514 changed the title Expose the EVP_AEAD Interface Expose a safe interface for EVP_AEAD Feb 21, 2022
Copy link
Contributor

@jyn514 jyn514 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a good start :) I left a lot of comments - none of them are critical, but it would be nice to address at least some before merging.

Could you add EVP_aead_aes_256_gcm_randnonce and EVP_aead_aes_128_gcm_randnonce under a cfg(feature = "fips") flag? EVP_aead_aes_128_ccm_bluetooth, EVP_aead_aes_128_ccm_bluetooth_8, and EVP_has_aes_hardware also seem to be missing, I think you can add those unconditionally.

boring/src/aead.rs Show resolved Hide resolved

/// Returns the length of nonces used with this AEAD.
pub fn nonce_len(&self) -> usize {
unsafe { EVP_AEAD_nonce_length(self.0) as usize }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting here doesn't seem correct - can you change this to return libc::size_t instead? Same for all the other functions returning usize.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to maintain parity with symm.rs which uses this pattern when returning lengths:

boring/boring/src/symm.rs

Lines 187 to 191 in e6ddc40

/// Returns the length of keys used with this cipher.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn key_len(&self) -> usize {
unsafe { EVP_CIPHER_key_length(self.0) as usize }
}

unsafe { EVP_AEAD_nonce_length(self.0) as usize }
}

/// Returns the maximum ciphertext overhead with this AEAD.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the upstream documentation here, I'd prefer not to reuse the word "overhead" when defining what it means.

Suggested change
/// Returns the maximum ciphertext overhead with this AEAD.
/// Returns the maximum number of additional bytes added by the act of sealing data with `self`.
///
/// Corresponds to [`EVP_AEAD_max_overhead`](https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_max_overhead).

}

pub fn as_ptr(&self) -> *const ffi::EVP_AEAD {
self.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

boring does this elsewhere in the codebase with ForeignType - is there a reason you didn't use that here? https://docs.rs/boring/latest/boring/dh/struct.Dh.html#impl-ForeignType

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to follow the one in symm.rs.

boring/boring/src/symm.rs

Lines 182 to 185 in e6ddc40

#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_ptr(&self) -> *const ffi::EVP_CIPHER {
self.0
}

}

unsafe impl Sync for Aead {}
unsafe impl Send for Aead {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you know the cipher is thread-safe?

Copy link
Author

@initsecret initsecret Feb 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I don't, I accidentally assumed that it was, and the the documentation doesn't seem explicit on this, so I'll remove the lines.

unsafe impl Send for Aead {}

/// Represents an AEAD context.
pub struct AeadCrypter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mentioned wanting to come up with a better name - I think AeadContext would be a good fit. But this also seems fine as-is.

/// Encrypts the provided data and authenticates the ciphertext and the provided aad.
///
/// Returns the sealed data as a vector.
pub fn encrypt_aead(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be an inherent method on Aead maybe?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I defined them separately for parity with symm.rs.

boring/boring/src/symm.rs

Lines 635 to 685 in e6ddc40

/// Like `encrypt`, but for AEAD ciphers such as AES GCM.
///
/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag
/// will be copied into the `tag` field.
///
/// The size of the `tag` buffer indicates the required size of the tag. While some ciphers support
/// a range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 bytes,
/// for example.
pub fn encrypt_aead(
t: Cipher,
key: &[u8],
iv: Option<&[u8]>,
aad: &[u8],
data: &[u8],
tag: &mut [u8],
) -> Result<Vec<u8>, ErrorStack> {
let mut c = Crypter::new(t, Mode::Encrypt, key, iv)?;
let mut out = vec![0; data.len() + t.block_size()];
c.aad_update(aad)?;
let count = c.update(data, &mut out)?;
let rest = c.finalize(&mut out[count..])?;
c.get_tag(tag)?;
out.truncate(count + rest);
Ok(out)
}
/// Like `decrypt`, but for AEAD ciphers such as AES GCM.
///
/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag
/// should be provided in the `tag` field.
pub fn decrypt_aead(
t: Cipher,
key: &[u8],
iv: Option<&[u8]>,
aad: &[u8],
data: &[u8],
tag: &[u8],
) -> Result<Vec<u8>, ErrorStack> {
let mut c = Crypter::new(t, Mode::Decrypt, key, iv)?;
let mut out = vec![0; data.len() + t.block_size()];
c.aad_update(aad)?;
let count = c.update(data, &mut out)?;
c.set_tag(tag)?;
let rest = c.finalize(&mut out[count..])?;
out.truncate(count + rest);
Ok(out)
}

let mut aeadcrypter = AeadCrypter::new(aead, key)?;
let mut output = vec![0u8; data.len() + aead.max_ciphertext_overhead()];
let bytes_written_to_output = aeadcrypter.seal(nonce, aad, data, &mut output)?;
Ok(output[..bytes_written_to_output].to_vec())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Ok(output[..bytes_written_to_output].to_vec())
output.truncate(bytes_written_to_output);
Ok(output)

/// Authenticates the provided data and aad and decrypts the provided data.
///
/// Returns the unsealed data as a vector.
pub fn decrypt_aead(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, this seems better as an inherent method.

let key = [0u8; 16];
let nonce = [0u8; 16];
let aad = [0u8; 16];
let data = [0u8; 16];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use something other than zeros here? I'm not sure what boringssl does on zeros, and it seems better to be closer to a real scenario anyway.

@initsecret
Copy link
Author

Thank you for detailed comments! I made a few of the suggested changes; the others seem to break parity with symm.rs which afaict seems okay, but I wanted to confirm with you before making the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose the EVP_AEAD Interface
2 participants