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 HMAC.
16 use crate::types::HmacCtx;
17 use crate::{malloc_err, openssl_last_err};
18 use alloc::boxed::Box;
19 use alloc::vec::Vec;
20 #[cfg(soong)]
21 use bssl_sys as ffi;
22 use kmr_common::{crypto, crypto::OpaqueOr, explicit, km_err, vec_try, Error};
23 use kmr_wire::keymint::Digest;
24 use log::error;
25 
26 /// [`crypto::Hmac`] implementation based on BoringSSL.
27 pub struct BoringHmac;
28 
29 impl crypto::Hmac for BoringHmac {
begin( &self, key: OpaqueOr<crypto::hmac::Key>, digest: Digest, ) -> Result<Box<dyn crypto::AccumulatingOperation>, Error>30     fn begin(
31         &self,
32         key: OpaqueOr<crypto::hmac::Key>,
33         digest: Digest,
34     ) -> Result<Box<dyn crypto::AccumulatingOperation>, Error> {
35         let key = explicit!(key)?;
36         let op = BoringHmacOperation {
37             // Safety: BoringSSL emits either null or a valid raw pointer, and the value is
38             // immediately checked for null below.
39             ctx: unsafe { HmacCtx(ffi::HMAC_CTX_new()) },
40         };
41         if op.ctx.0.is_null() {
42             return Err(malloc_err!());
43         }
44 
45         let digest = digest_into_openssl_ffi(digest)?;
46         #[cfg(soong)]
47         let key_len = key.0.len();
48         #[cfg(not(soong))]
49         let key_len = key.0.len() as i32;
50 
51         // Safety: `op.ctx` is known non-null and valid, as is the result of
52         // `digest_into_openssl_ffi()`.  `key_len` is length of `key.0`, which is a valid `Vec<u8>`.
53         let result = unsafe {
54             ffi::HMAC_Init_ex(
55                 op.ctx.0,
56                 key.0.as_ptr() as *const libc::c_void,
57                 key_len,
58                 digest,
59                 core::ptr::null_mut(),
60             )
61         };
62         if result != 1 {
63             error!("Failed to HMAC_Init_ex()");
64             return Err(openssl_last_err());
65         }
66         Ok(Box::new(op))
67     }
68 }
69 
70 /// [`crypto::HmacOperation`] implementation based on BoringSSL.
71 ///
72 /// This implementation uses the `unsafe` wrappers around `HMAC_*` functions directly, because
73 /// BoringSSL does not support the `EVP_PKEY_HMAC` implementations that are used in the rust-openssl
74 /// crate.
75 pub struct BoringHmacOperation {
76     // Safety: `ctx` is always non-null and valid except for initial error path in `begin()`
77     ctx: HmacCtx,
78 }
79 
80 impl core::ops::Drop for BoringHmacOperation {
drop(&mut self)81     fn drop(&mut self) {
82         // Safety: `self.ctx` might be null (in the error path when `ffi::HMAC_CTX_new` fails)
83         // but `ffi::HMAC_CTX_free` copes with null.
84         unsafe {
85             ffi::HMAC_CTX_free(self.ctx.0);
86         }
87     }
88 }
89 
90 impl crypto::AccumulatingOperation for BoringHmacOperation {
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::HMAC_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 = ffi::EVP_MAX_MD_SIZE as u32;
102         let mut output = vec_try![0; ffi::EVP_MAX_MD_SIZE as usize]?;
103 
104         // Safety: `self.ctx` is non-null and valid; `output_len` is correct size of `output`
105         // buffer.
106         let result = unsafe {
107             // (force line break for safety lint limitation)
108             ffi::HMAC_Final(self.ctx.0, output.as_mut_ptr(), &mut output_len as *mut u32)
109         };
110         if result != 1 {
111             return Err(openssl_last_err());
112         }
113         output.truncate(output_len as usize);
114         Ok(output)
115     }
116 }
117 
118 /// Translate a [`keymint::Digest`] into a raw [`ffi::EVD_MD`].
digest_into_openssl_ffi(digest: Digest) -> Result<*const ffi::EVP_MD, Error>119 fn digest_into_openssl_ffi(digest: Digest) -> Result<*const ffi::EVP_MD, Error> {
120     // Safety: all of the `EVP_<digest>` functions return a non-null valid pointer.
121     unsafe {
122         match digest {
123             Digest::Md5 => Ok(ffi::EVP_md5()),
124             Digest::Sha1 => Ok(ffi::EVP_sha1()),
125             Digest::Sha224 => Ok(ffi::EVP_sha224()),
126             Digest::Sha256 => Ok(ffi::EVP_sha256()),
127             Digest::Sha384 => Ok(ffi::EVP_sha384()),
128             Digest::Sha512 => Ok(ffi::EVP_sha512()),
129             d => Err(km_err!(UnsupportedDigest, "unknown digest {:?}", d)),
130         }
131     }
132 }
133