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