[bssl] Implement AEAD BoringSSL wrapper
AEAD will be used to encrypt the private key in the RKP HAL.
Bug: 302286887
Test: atest libbssl_avf_nostd.test
Change-Id: I734cf5ecd8b938f2c47f439d6f51594ccafb4ff2
diff --git a/libs/bssl/src/aead.rs b/libs/bssl/src/aead.rs
new file mode 100644
index 0000000..a7d03b9
--- /dev/null
+++ b/libs/bssl/src/aead.rs
@@ -0,0 +1,160 @@
+// 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;
+
+/// 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
+ }
+}