xref: /aosp_15_r20/system/keymint/boringssl/src/aes_cmac.rs (revision 9860b7637a5f185913c70aa0caabe3ecb78441e4)
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