1 // Copyright 2022, 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 //! BoringSSL-based implementation of AES-CMAC. 16 use crate::types::CmacCtx; 17 use crate::{malloc_err, openssl_last_err}; 18 use alloc::boxed::Box; 19 use alloc::vec::Vec; 20 use bssl_sys as ffi; 21 use kmr_common::{crypto, crypto::OpaqueOr, explicit, km_err, vec_try, Error}; 22 use log::error; 23 24 /// [`crypto::AesCmac`] implementation based on BoringSSL. 25 pub struct BoringAesCmac; 26 27 impl crypto::AesCmac for BoringAesCmac { begin( &self, key: OpaqueOr<crypto::aes::Key>, ) -> Result<Box<dyn crypto::AccumulatingOperation>, Error>28 fn begin( 29 &self, 30 key: OpaqueOr<crypto::aes::Key>, 31 ) -> Result<Box<dyn crypto::AccumulatingOperation>, Error> { 32 let key = explicit!(key)?; 33 // Safety: all of the `ffi::EVP_aes_<N>_cbc` functions return a non-null valid pointer. 34 let (cipher, k) = unsafe { 35 match &key { 36 crypto::aes::Key::Aes128(k) => (ffi::EVP_aes_128_cbc(), &k[..]), 37 crypto::aes::Key::Aes192(k) => (ffi::EVP_aes_192_cbc(), &k[..]), 38 crypto::aes::Key::Aes256(k) => (ffi::EVP_aes_256_cbc(), &k[..]), 39 } 40 }; 41 42 let op = BoringAesCmacOperation { 43 // Safety: raw pointer is immediately checked for null below, and BoringSSL only emits 44 // valid pointers or null. 45 ctx: unsafe { CmacCtx(ffi::CMAC_CTX_new()) }, 46 }; 47 if op.ctx.0.is_null() { 48 return Err(malloc_err!()); 49 } 50 51 // Safety: `op.ctx` is known non-null and valid, as is `cipher`. `key_len` is length of 52 // `key.0`, which is a valid `Vec<u8>`. 53 let result = unsafe { 54 ffi::CMAC_Init( 55 op.ctx.0, 56 k.as_ptr() as *const libc::c_void, 57 k.len(), 58 cipher, 59 core::ptr::null_mut(), 60 ) 61 }; 62 if result != 1 { 63 error!("Failed to CMAC_Init()"); 64 return Err(openssl_last_err()); 65 } 66 Ok(Box::new(op)) 67 } 68 } 69 70 /// AES-CMAC implementation based on BoringSSL. 71 /// 72 /// This implementation uses the `unsafe` wrappers around `CMAC_*` functions directly, because 73 /// BoringSSL does not support the `EVP_PKEY_CMAC` implementations that are used in the rust-openssl 74 /// crate. 75 pub struct BoringAesCmacOperation { 76 // Safety: `ctx` is always non-null and valid except for initial error path in `begin()` 77 ctx: CmacCtx, 78 } 79 80 impl core::ops::Drop for BoringAesCmacOperation { drop(&mut self)81 fn drop(&mut self) { 82 // Safety: `self.ctx` might be null (in the error path when `ffi::CMAC_CTX_new` fails) 83 // but `ffi::CMAC_CTX_free` copes with null. 84 unsafe { 85 ffi::CMAC_CTX_free(self.ctx.0); 86 } 87 } 88 } 89 90 impl crypto::AccumulatingOperation for BoringAesCmacOperation { update(&mut self, data: &[u8]) -> Result<(), Error>91 fn update(&mut self, data: &[u8]) -> Result<(), Error> { 92 // Safety: `self.ctx` is non-null and valid, and `data` is a valid slice. 93 let result = unsafe { ffi::CMAC_Update(self.ctx.0, data.as_ptr(), data.len()) }; 94 if result != 1 { 95 return Err(openssl_last_err()); 96 } 97 Ok(()) 98 } 99 finish(self: Box<Self>) -> Result<Vec<u8>, Error>100 fn finish(self: Box<Self>) -> Result<Vec<u8>, Error> { 101 let mut output_len: usize = crypto::aes::BLOCK_SIZE; 102 let mut output = vec_try![0; crypto::aes::BLOCK_SIZE]?; 103 // Safety: `self.ctx` is non-null and valid; `output_len` is correct size of `output` 104 // buffer. 105 let result = unsafe { 106 ffi::CMAC_Final(self.ctx.0, output.as_mut_ptr(), &mut output_len as *mut usize) 107 }; 108 if result != 1 { 109 return Err(openssl_last_err()); 110 } 111 if output_len != crypto::aes::BLOCK_SIZE { 112 return Err(km_err!(BoringSslError, "Unexpected CMAC output size of {}", output_len)); 113 } 114 Ok(output) 115 } 116 } 117