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