• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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