1 //
2 // Copyright (C) 2022 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 //! Device trait implementations using the TPM.
17 
18 use kmr_common::{
19     crypto, crypto::SHA256_DIGEST_LEN, km_err, vec_try, vec_try_with_capacity, Error,
20     FallibleAllocExt,
21 };
22 use kmr_ta::device::DeviceHmac;
23 
24 pub const ROOT_KEK_MARKER: &[u8] = b"CF Root KEK";
25 
26 /// Device HMAC implementation that uses the TPM.
27 #[derive(Clone)]
28 pub struct TpmHmac {
29     /// Opaque pointer to a `TpmResourceManager`.
30     trm: *mut libc::c_void,
31 }
32 
33 // Safety: Checked TpmResourceManager c++ definition to determine if it could be sent between
34 //         threads.
35 unsafe impl Send for TpmHmac {}
36 
37 impl TpmHmac {
new(trm: *mut libc::c_void) -> Self38     pub fn new(trm: *mut libc::c_void) -> Self {
39         Self { trm }
40     }
tpm_hmac(&self, data: &[u8]) -> Result<Vec<u8>, Error>41     fn tpm_hmac(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
42         let mut tag = vec_try![0; 32]?;
43 
44         // Safety: all slices are valid with correct lengths.
45         let rc = unsafe {
46             secure_env_tpm::tpm_hmac(
47                 self.trm,
48                 data.as_ptr(),
49                 data.len() as u32,
50                 tag.as_mut_ptr(),
51                 tag.len() as u32,
52             )
53         };
54         if rc == 0 {
55             Ok(tag)
56         } else {
57             Err(km_err!(UnknownError, "HMAC calculation failed"))
58         }
59     }
60 
hkdf_expand(&self, info: &[u8], out_len: usize) -> Result<Vec<u8>, Error>61     fn hkdf_expand(&self, info: &[u8], out_len: usize) -> Result<Vec<u8>, Error> {
62         // HKDF expand: feed the derivation info into HMAC (using the TPM key) repeatedly.
63         let n = (out_len + SHA256_DIGEST_LEN - 1) / SHA256_DIGEST_LEN;
64         if n > 256 {
65             return Err(km_err!(UnknownError, "overflow in hkdf"));
66         }
67         let mut t = vec_try_with_capacity!(SHA256_DIGEST_LEN)?;
68         let mut okm = vec_try_with_capacity!(n * SHA256_DIGEST_LEN)?;
69         let n = n as u8;
70         for idx in 0..n {
71             let mut input = vec_try_with_capacity!(t.len() + info.len() + 1)?;
72             input.extend_from_slice(&t);
73             input.extend_from_slice(info);
74             input.push(idx + 1);
75 
76             t = self.tpm_hmac(&input)?;
77             okm.try_extend_from_slice(&t)?;
78         }
79         okm.truncate(out_len);
80         Ok(okm)
81     }
82 }
83 
84 impl kmr_ta::device::DeviceHmac for TpmHmac {
hmac(&self, _imp: &dyn crypto::Hmac, data: &[u8]) -> Result<Vec<u8>, Error>85     fn hmac(&self, _imp: &dyn crypto::Hmac, data: &[u8]) -> Result<Vec<u8>, Error> {
86         self.tpm_hmac(data)
87     }
88 }
89 
90 impl crate::rpc::DeriveBytes for TpmHmac {
derive_bytes(&self, context: &[u8], output_len: usize) -> Result<Vec<u8>, Error>91     fn derive_bytes(&self, context: &[u8], output_len: usize) -> Result<Vec<u8>, Error> {
92         self.hkdf_expand(context, output_len)
93     }
94 }
95 
96 // TPM-backed implementation of key retrieval/management functionality.
97 pub struct Keys {
98     tpm_hmac: TpmHmac,
99 }
100 
101 impl Keys {
new(trm: *mut libc::c_void) -> Self102     pub fn new(trm: *mut libc::c_void) -> Self {
103         Self { tpm_hmac: TpmHmac::new(trm) }
104     }
105 }
106 
107 impl kmr_ta::device::RetrieveKeyMaterial for Keys {
root_kek(&self, _context: &[u8]) -> Result<crypto::OpaqueOr<crypto::hmac::Key>, Error>108     fn root_kek(&self, _context: &[u8]) -> Result<crypto::OpaqueOr<crypto::hmac::Key>, Error> {
109         Ok(crypto::OpaqueOr::Opaque(crypto::OpaqueKeyMaterial(ROOT_KEK_MARKER.to_vec())))
110     }
111 
kak(&self) -> Result<crypto::OpaqueOr<crypto::aes::Key>, Error>112     fn kak(&self) -> Result<crypto::OpaqueOr<crypto::aes::Key>, Error> {
113         // Generate a TPM-bound shared secret to use as the base of HMAC key negotiation.
114         let k = self.tpm_hmac.tpm_hmac(b"TPM ISharedSecret")?;
115         let k: [u8; 32] =
116             k.try_into().map_err(|_e| km_err!(UnknownError, "unexpected HMAC size"))?;
117         Ok(crypto::aes::Key::Aes256(k).into())
118     }
119 
hmac_key_agreed(&self, _key: &crypto::hmac::Key) -> Option<Box<dyn DeviceHmac>>120     fn hmac_key_agreed(&self, _key: &crypto::hmac::Key) -> Option<Box<dyn DeviceHmac>> {
121         // After `ISharedSecret` negotiation completes, the spec implies that the calculated HMAC
122         // key should be used by subsequent device HMAC calculations.  However, this implementation
123         // uses a TPM-HMAC key instead, so that HMAC calculations agree between KeyMint and
124         // Gatekeeper / ConfirmationUI.
125         // TODO(b/242838132): consider installing the calculated key into the TPM and using it
126         // thereafter.
127         Some(Box::new(self.tpm_hmac.clone()))
128     }
129 
unique_id_hbk(&self, _ckdf: &dyn crypto::Ckdf) -> Result<crypto::hmac::Key, Error>130     fn unique_id_hbk(&self, _ckdf: &dyn crypto::Ckdf) -> Result<crypto::hmac::Key, Error> {
131         // Generate a TPM-bound HBK to use for unique ID generation.
132         let mut k = self.tpm_hmac.tpm_hmac(b"TPM unique ID HBK")?;
133         k.truncate(16);
134         Ok(crypto::hmac::Key(k))
135     }
136 }
137 
138 pub struct KeyDerivation {
139     tpm_hmac: TpmHmac,
140 }
141 
142 impl KeyDerivation {
new(trm: *mut libc::c_void) -> Self143     pub fn new(trm: *mut libc::c_void) -> Self {
144         Self { tpm_hmac: TpmHmac::new(trm) }
145     }
146 }
147 
148 impl kmr_common::crypto::Hkdf for KeyDerivation {
hkdf( &self, _salt: &[u8], _ikm: &[u8], _info: &[u8], _out_len: usize, ) -> Result<Vec<u8>, Error>149     fn hkdf(
150         &self,
151         _salt: &[u8],
152         _ikm: &[u8],
153         _info: &[u8],
154         _out_len: usize,
155     ) -> Result<Vec<u8>, Error> {
156         // HKDF normally performs an initial extract step to create a pseudo-random key (PRK) for
157         // use in the HKDF expand processing.  This implementation uses a TPM HMAC key for HKDF
158         // expand processing instead, and so we cannot do a full HKDF call.
159         Err(km_err!(UnknownError, "unexpected call to full hkdf operation"))
160     }
161 
extract( &self, _salt: &[u8], _ikm: &[u8], ) -> Result<crypto::OpaqueOr<crypto::hmac::Key>, Error>162     fn extract(
163         &self,
164         _salt: &[u8],
165         _ikm: &[u8],
166     ) -> Result<crypto::OpaqueOr<crypto::hmac::Key>, Error> {
167         // Because we are using a TPM HMAC key for HKDF; there is nothing to extract
168         Err(km_err!(UnknownError, "unexpected call to hkdf extract"))
169     }
170 
expand( &self, prk: &crypto::OpaqueOr<crypto::hmac::Key>, info: &[u8], out_len: usize, ) -> Result<Vec<u8>, Error>171     fn expand(
172         &self,
173         prk: &crypto::OpaqueOr<crypto::hmac::Key>,
174         info: &[u8],
175         out_len: usize,
176     ) -> Result<Vec<u8>, Error> {
177         let key_material = match prk {
178             crypto::OpaqueOr::Opaque(key_material) => &key_material.0,
179             _ => {
180                 return Err(km_err!(
181                     UnknownError,
182                     "unexpected root kek type used in key derivation"
183                 ))
184             }
185         };
186         if key_material != ROOT_KEK_MARKER {
187             // This code expects that the value from `Keys::root_kek()` above will be passed
188             // unmodified to this function in its (only) use as key derivation.  If this is not the
189             // case, then the assumptions below around TPM use may no longer be correct.
190             return Err(km_err!(UnknownError, "unexpected root kek in key derivation"));
191         }
192         self.tpm_hmac.hkdf_expand(info, out_len)
193     }
194 }
195 
196 /// RPC artifact retrieval using key material derived from the TPM.
197 pub type RpcArtifacts = crate::rpc::Artifacts<TpmHmac>;
198