blob: 74bde84444d157123eff17fc02e8f2deaf1c37eb [file] [log] [blame]
// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Wrappers of the AEAD functions in BoringSSL aead.h.
use crate::util::{check_int_result, to_call_failed_error};
use bssl_avf_error::{ApiName, Result};
use bssl_ffi::{
EVP_AEAD_CTX_free, EVP_AEAD_CTX_new, EVP_AEAD_CTX_open, EVP_AEAD_CTX_seal,
EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm, EVP_AEAD, EVP_AEAD_CTX,
EVP_AEAD_DEFAULT_TAG_LENGTH,
};
use core::ptr::NonNull;
/// BoringSSL spec recommends to use 12-byte nonces.
///
/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_aead_aes_256_gcm
pub const AES_GCM_NONCE_LENGTH: usize = 12;
/// Magic value indicating that the default tag length for an AEAD should be used to
/// initialize `AeadCtx`.
const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize;
/// Represents an AEAD algorithm.
#[derive(Clone, Copy, Debug)]
pub struct Aead(&'static EVP_AEAD);
impl Aead {
/// This is AES-256 in Galois Counter Mode.
/// AES-GCM should only be used with 12-byte (96-bit) nonces as suggested in the
/// BoringSSL spec:
///
/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html
pub fn aes_256_gcm() -> Self {
// SAFETY: This function does not access any Rust variables and simply returns
// a pointer to the static variable in BoringSSL.
let p = unsafe { EVP_aead_aes_256_gcm() };
// SAFETY: The returned pointer should always be valid and points to a static
// `EVP_AEAD`.
Self(unsafe { &*p })
}
/// Returns the maximum number of additional bytes added by the act of sealing data.
pub fn max_overhead(&self) -> usize {
// SAFETY: This function only reads from self.
unsafe { EVP_AEAD_max_overhead(self.0) }
}
/// Returns the length, in bytes, of the per-message nonce.
pub fn nonce_length(&self) -> usize {
// SAFETY: This function only reads from self.
unsafe { EVP_AEAD_nonce_length(self.0) }
}
}
/// Represents an AEAD algorithm configuration.
pub struct AeadCtx {
ctx: NonNull<EVP_AEAD_CTX>,
aead: Aead,
}
impl Drop for AeadCtx {
fn drop(&mut self) {
// SAFETY: It is safe because the pointer has been created with `EVP_AEAD_CTX_new`
// and isn't used after this.
unsafe { EVP_AEAD_CTX_free(self.ctx.as_ptr()) }
}
}
impl AeadCtx {
/// Creates a new `AeadCtx` with the given `Aead` algorithm, `key` and `tag_len`.
///
/// The default tag length will be used if `tag_len` is None.
pub fn new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self> {
let tag_len = tag_len.unwrap_or(AEAD_DEFAULT_TAG_LENGTH);
// SAFETY: This function only reads the given data and the returned pointer is
// checked below.
let ctx = unsafe { EVP_AEAD_CTX_new(aead.0, key.as_ptr(), key.len(), tag_len) };
let ctx = NonNull::new(ctx).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_new))?;
Ok(Self { ctx, aead })
}
/// Encrypts and authenticates `data` and writes the result to `out`.
/// The `out` length should be at least the `data` length plus the `max_overhead` of the
/// `aead` and the length of `nonce` should match the `nonce_length` of the `aead`.
/// Otherwise, an error will be returned.
///
/// The output is returned as a subslice of `out`.
pub fn seal<'b>(
&self,
data: &[u8],
nonce: &[u8],
ad: &[u8],
out: &'b mut [u8],
) -> Result<&'b [u8]> {
let mut out_len = 0;
// SAFETY: Only reads from/writes to the provided slices.
let ret = unsafe {
EVP_AEAD_CTX_seal(
self.ctx.as_ptr(),
out.as_mut_ptr(),
&mut out_len,
out.len(),
nonce.as_ptr(),
nonce.len(),
data.as_ptr(),
data.len(),
ad.as_ptr(),
ad.len(),
)
};
check_int_result(ret, ApiName::EVP_AEAD_CTX_seal)?;
out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_seal))
}
/// Authenticates `data` and decrypts it to `out`.
/// The `out` length should be at least the `data` length, and the length of `nonce` should
/// match the `nonce_length` of the `aead`.
/// Otherwise, an error will be returned.
///
/// The output is returned as a subslice of `out`.
pub fn open<'b>(
&self,
data: &[u8],
nonce: &[u8],
ad: &[u8],
out: &'b mut [u8],
) -> Result<&'b [u8]> {
let mut out_len = 0;
// SAFETY: Only reads from/writes to the provided slices.
// `data` and `out` are checked to be non-alias internally.
let ret = unsafe {
EVP_AEAD_CTX_open(
self.ctx.as_ptr(),
out.as_mut_ptr(),
&mut out_len,
out.len(),
nonce.as_ptr(),
nonce.len(),
data.as_ptr(),
data.len(),
ad.as_ptr(),
ad.len(),
)
};
check_int_result(ret, ApiName::EVP_AEAD_CTX_open)?;
out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_open))
}
/// Returns the `Aead` represented by this `AeadCtx`.
pub fn aead(&self) -> Aead {
self.aead
}
}