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