1 // Copyright 2023, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Wrappers of the AEAD functions in BoringSSL aead.h. 16 17 use crate::util::{check_int_result, to_call_failed_error}; 18 use bssl_avf_error::{ApiName, Result}; 19 use bssl_sys::{ 20 EVP_AEAD_CTX_free, EVP_AEAD_CTX_new, EVP_AEAD_CTX_open, EVP_AEAD_CTX_seal, 21 EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm, 22 EVP_aead_aes_256_gcm_randnonce, EVP_AEAD, EVP_AEAD_CTX, EVP_AEAD_DEFAULT_TAG_LENGTH, 23 }; 24 use core::ptr::NonNull; 25 26 /// BoringSSL spec recommends to use 12-byte nonces. 27 /// 28 /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_aead_aes_256_gcm 29 pub const AES_GCM_NONCE_LENGTH: usize = 12; 30 31 /// Magic value indicating that the default tag length for an AEAD should be used to 32 /// initialize `AeadContext`. 33 const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize; 34 35 /// Represents an AEAD algorithm. 36 #[derive(Clone, Copy, Debug)] 37 pub struct Aead(&'static EVP_AEAD); 38 39 impl Aead { 40 /// This is AES-256 in Galois Counter Mode. 41 /// AES-GCM should only be used with 12-byte (96-bit) nonces as suggested in the 42 /// BoringSSL spec: 43 /// 44 /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html aes_256_gcm() -> Self45 pub fn aes_256_gcm() -> Self { 46 // SAFETY: This function does not access any Rust variables and simply returns 47 // a pointer to the static variable in BoringSSL. 48 let p = unsafe { EVP_aead_aes_256_gcm() }; 49 // SAFETY: The returned pointer should always be valid and points to a static 50 // `EVP_AEAD`. 51 Self(unsafe { &*p }) 52 } 53 54 /// AES-256 in Galois Counter Mode with internal nonce generation. 55 /// The 12-byte nonce is appended to the tag and is generated internally. aes_256_gcm_randnonce() -> Self56 pub fn aes_256_gcm_randnonce() -> Self { 57 // SAFETY: This function does not access any Rust variables and simply returns 58 // a pointer to the static variable in BoringSSL. 59 let p = unsafe { EVP_aead_aes_256_gcm_randnonce() }; 60 // SAFETY: The returned pointer should always be valid and points to a static 61 // `EVP_AEAD`. 62 Self(unsafe { &*p }) 63 } 64 65 /// Returns the maximum number of additional bytes added by the act of sealing data. max_overhead(&self) -> usize66 pub fn max_overhead(&self) -> usize { 67 // SAFETY: This function only reads from self. 68 unsafe { EVP_AEAD_max_overhead(self.0) } 69 } 70 71 /// Returns the length, in bytes, of the per-message nonce. nonce_length(&self) -> usize72 pub fn nonce_length(&self) -> usize { 73 // SAFETY: This function only reads from self. 74 unsafe { EVP_AEAD_nonce_length(self.0) } 75 } 76 } 77 78 /// Represents an AEAD algorithm configuration. 79 pub struct AeadContext { 80 ctx: NonNull<EVP_AEAD_CTX>, 81 aead: Aead, 82 } 83 84 impl Drop for AeadContext { drop(&mut self)85 fn drop(&mut self) { 86 // SAFETY: It is safe because the pointer has been created with `EVP_AEAD_CTX_new` 87 // and isn't used after this. 88 unsafe { EVP_AEAD_CTX_free(self.ctx.as_ptr()) } 89 } 90 } 91 92 impl AeadContext { 93 /// Creates a new `AeadContext` with the given `Aead` algorithm, `key` and `tag_len`. 94 /// 95 /// The default tag length will be used if `tag_len` is None. new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self>96 pub fn new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self> { 97 let tag_len = tag_len.unwrap_or(AEAD_DEFAULT_TAG_LENGTH); 98 // SAFETY: This function only reads the given data and the returned pointer is 99 // checked below. 100 let ctx = unsafe { EVP_AEAD_CTX_new(aead.0, key.as_ptr(), key.len(), tag_len) }; 101 let ctx = 102 NonNull::new(ctx).ok_or_else(|| to_call_failed_error(ApiName::EVP_AEAD_CTX_new))?; 103 Ok(Self { ctx, aead }) 104 } 105 106 /// Encrypts and authenticates `data` and writes the result to `out`. 107 /// The `out` length should be at least the `data` length plus the `max_overhead` of the 108 /// `aead` and the length of `nonce` should match the `nonce_length` of the `aead`. 109 /// Otherwise, an error will be returned. 110 /// 111 /// The output is returned as a subslice of `out`. seal<'b>( &self, data: &[u8], nonce: &[u8], ad: &[u8], out: &'b mut [u8], ) -> Result<&'b [u8]>112 pub fn seal<'b>( 113 &self, 114 data: &[u8], 115 nonce: &[u8], 116 ad: &[u8], 117 out: &'b mut [u8], 118 ) -> Result<&'b [u8]> { 119 let mut out_len = 0; 120 // SAFETY: Only reads from/writes to the provided slices. 121 let ret = unsafe { 122 EVP_AEAD_CTX_seal( 123 self.ctx.as_ptr(), 124 out.as_mut_ptr(), 125 &mut out_len, 126 out.len(), 127 nonce.as_ptr(), 128 nonce.len(), 129 data.as_ptr(), 130 data.len(), 131 ad.as_ptr(), 132 ad.len(), 133 ) 134 }; 135 check_int_result(ret, ApiName::EVP_AEAD_CTX_seal)?; 136 out.get(0..out_len).ok_or_else(|| to_call_failed_error(ApiName::EVP_AEAD_CTX_seal)) 137 } 138 139 /// Authenticates `data` and decrypts it to `out`. 140 /// The `out` length should be at least the `data` length, and the length of `nonce` should 141 /// match the `nonce_length` of the `aead`. 142 /// Otherwise, an error will be returned. 143 /// 144 /// The output is returned as a subslice of `out`. open<'b>( &self, data: &[u8], nonce: &[u8], ad: &[u8], out: &'b mut [u8], ) -> Result<&'b [u8]>145 pub fn open<'b>( 146 &self, 147 data: &[u8], 148 nonce: &[u8], 149 ad: &[u8], 150 out: &'b mut [u8], 151 ) -> Result<&'b [u8]> { 152 let mut out_len = 0; 153 // SAFETY: Only reads from/writes to the provided slices. 154 // `data` and `out` are checked to be non-alias internally. 155 let ret = unsafe { 156 EVP_AEAD_CTX_open( 157 self.ctx.as_ptr(), 158 out.as_mut_ptr(), 159 &mut out_len, 160 out.len(), 161 nonce.as_ptr(), 162 nonce.len(), 163 data.as_ptr(), 164 data.len(), 165 ad.as_ptr(), 166 ad.len(), 167 ) 168 }; 169 check_int_result(ret, ApiName::EVP_AEAD_CTX_open)?; 170 out.get(0..out_len).ok_or_else(|| to_call_failed_error(ApiName::EVP_AEAD_CTX_open)) 171 } 172 173 /// Returns the `Aead` represented by this `AeadContext`. aead(&self) -> Aead174 pub fn aead(&self) -> Aead { 175 self.aead 176 } 177 } 178