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 //! Functionality for remote key provisioning
16
17 use super::KeyMintTa;
18 use crate::coset::{
19 cbor::value::Value, iana, AsCborValue, CborSerializable, CoseKey, CoseMac0, CoseMac0Builder,
20 HeaderBuilder, Label,
21 };
22 use crate::RpcInfo;
23 use alloc::string::{String, ToString};
24 use alloc::{vec, vec::Vec};
25 use kmr_common::crypto::{
26 ec::{CoseKeyPurpose, RKP_TEST_KEY_CBOR_MARKER},
27 hmac_sha256, KeyMaterial,
28 };
29 use kmr_common::{keyblob, km_err, rpc_err, try_to_vec, Error, FallibleAllocExt};
30 use kmr_wire::{
31 cbor,
32 cbor::cbor,
33 keymint::{
34 Algorithm, Digest, EcCurve, KeyParam, KeyPurpose, SecurityLevel, VerifiedBootState,
35 UNDEFINED_NOT_AFTER, UNDEFINED_NOT_BEFORE,
36 },
37 read_to_value, rpc,
38 rpc::{
39 DeviceInfo, EekCurve, HardwareInfo, MacedPublicKey, ProtectedData,
40 MINIMUM_SUPPORTED_KEYS_IN_CSR,
41 },
42 rpc::{AUTH_REQ_SCHEMA_V1, CERT_TYPE_KEYMINT, IRPC_V2, IRPC_V3},
43 types::KeySizeInBits,
44 CborError,
45 };
46
47 const RPC_P256_KEYGEN_PARAMS: [KeyParam; 8] = [
48 KeyParam::Purpose(KeyPurpose::AttestKey),
49 KeyParam::Algorithm(Algorithm::Ec),
50 KeyParam::KeySize(KeySizeInBits(256)),
51 KeyParam::EcCurve(EcCurve::P256),
52 KeyParam::NoAuthRequired,
53 KeyParam::Digest(Digest::Sha256),
54 KeyParam::CertificateNotBefore(UNDEFINED_NOT_BEFORE),
55 KeyParam::CertificateNotAfter(UNDEFINED_NOT_AFTER),
56 ];
57
58 const MAX_CHALLENGE_SIZE_V2: usize = 64;
59
60 impl KeyMintTa {
61 /// Return the UDS certs for the device, encoded in CBOR as per `AdditionalDKSignatures`
62 /// structure in ProtectedData.aidl for IRPC HAL version 2 and as per `UdsCerts` structure in
63 /// IRPC HAL version 3.
uds_certs(&self) -> Result<Vec<u8>, Error>64 pub fn uds_certs(&self) -> Result<Vec<u8>, Error> {
65 let dice_info =
66 self.get_dice_info().ok_or_else(|| rpc_err!(Failed, "DICE info not available."))?;
67 try_to_vec(&dice_info.pub_dice_artifacts.uds_certs)
68 }
69
70 /// Return the CBOR-encoded `DeviceInfo`.
rpc_device_info(&self) -> Result<Vec<u8>, Error>71 pub fn rpc_device_info(&self) -> Result<Vec<u8>, Error> {
72 let info = self.rpc_device_info_cbor()?;
73 serialize_cbor(&info)
74 }
75
rpc_device_info_cbor(&self) -> Result<Value, Error>76 fn rpc_device_info_cbor(&self) -> Result<Value, Error> {
77 // First make sure all the relevant info is available.
78 let ids = self.get_attestation_ids().ok_or_else(|| {
79 km_err!(AttestationIdsNotProvisioned, "attestation ID info not available")
80 })?;
81 let boot_info = self
82 .boot_info
83 .as_ref()
84 .ok_or_else(|| km_err!(HardwareNotYetAvailable, "boot info not available"))?;
85 let hal_info = self
86 .hal_info
87 .as_ref()
88 .ok_or_else(|| km_err!(HardwareNotYetAvailable, "HAL info not available"))?;
89
90 let brand = String::from_utf8_lossy(&ids.brand);
91 let manufacturer = String::from_utf8_lossy(&ids.manufacturer);
92 let product = String::from_utf8_lossy(&ids.product);
93 let model = String::from_utf8_lossy(&ids.model);
94 let device = String::from_utf8_lossy(&ids.device);
95
96 let bootloader_state = if boot_info.device_boot_locked { "locked" } else { "unlocked" };
97 let vbmeta_digest = cbor::value::Value::Bytes(try_to_vec(&boot_info.verified_boot_hash)?);
98 let vb_state = match boot_info.verified_boot_state {
99 VerifiedBootState::Verified => "green",
100 VerifiedBootState::SelfSigned => "yellow",
101 VerifiedBootState::Unverified => "orange",
102 VerifiedBootState::Failed => "red",
103 };
104 let security_level = match self.hw_info.security_level {
105 SecurityLevel::TrustedEnvironment => "tee",
106 SecurityLevel::Strongbox => "strongbox",
107 l => {
108 return Err(km_err!(
109 HardwareTypeUnavailable,
110 "security level {:?} not supported",
111 l
112 ))
113 }
114 };
115
116 let fused = match &self.rpc_info {
117 RpcInfo::V2(rpc_info_v2) => rpc_info_v2.fused,
118 RpcInfo::V3(rpc_info_v3) => rpc_info_v3.fused,
119 };
120 // The DeviceInfo.aidl file specifies that map keys should be ordered according
121 // to RFC 7049 canonicalization rules, which are:
122 // - shorter-encoded key < longer-encoded key
123 // - lexicographic comparison for same-length keys
124 // Note that this is *different* than the ordering required in RFC 8949 s4.2.1.
125 let info = cbor!({
126 "brand" => brand,
127 "fused" => i32::from(fused),
128 "model" => model,
129 "device" => device,
130 "product" => product,
131 "vb_state" => vb_state,
132 "os_version" => hal_info.os_version.to_string(),
133 "manufacturer" => manufacturer,
134 "vbmeta_digest" => vbmeta_digest,
135 "security_level" => security_level,
136 "boot_patch_level" => boot_info.boot_patchlevel,
137 "bootloader_state" => bootloader_state,
138 "system_patch_level" => hal_info.os_patchlevel,
139 "vendor_patch_level" => hal_info.vendor_patchlevel,
140 })?;
141 Ok(info)
142 }
143
get_rpc_hardware_info(&self) -> Result<HardwareInfo, Error>144 pub(crate) fn get_rpc_hardware_info(&self) -> Result<HardwareInfo, Error> {
145 match &self.rpc_info {
146 RpcInfo::V2(rpc_info_v2) => Ok(HardwareInfo {
147 version_number: IRPC_V2,
148 rpc_author_name: rpc_info_v2.author_name.to_string(),
149 supported_eek_curve: rpc_info_v2.supported_eek_curve,
150 unique_id: Some(rpc_info_v2.unique_id.to_string()),
151 supported_num_keys_in_csr: MINIMUM_SUPPORTED_KEYS_IN_CSR,
152 }),
153 RpcInfo::V3(rpc_info_v3) => Ok(HardwareInfo {
154 version_number: IRPC_V3,
155 rpc_author_name: rpc_info_v3.author_name.to_string(),
156 supported_eek_curve: EekCurve::None,
157 unique_id: Some(rpc_info_v3.unique_id.to_string()),
158 supported_num_keys_in_csr: rpc_info_v3.supported_num_of_keys_in_csr,
159 }),
160 }
161 }
162
generate_ecdsa_p256_keypair( &mut self, test_mode: rpc::TestMode, ) -> Result<(MacedPublicKey, Vec<u8>), Error>163 pub(crate) fn generate_ecdsa_p256_keypair(
164 &mut self,
165 test_mode: rpc::TestMode,
166 ) -> Result<(MacedPublicKey, Vec<u8>), Error> {
167 if self.rpc_info.get_version() > IRPC_V2 && test_mode == rpc::TestMode(true) {
168 return Err(rpc_err!(
169 Removed,
170 "generate_ecdsa_p256_keypair does not support test mode in IRPC V3+ HAL."
171 ));
172 }
173
174 let (key_material, chars) = self.generate_key_material(&RPC_P256_KEYGEN_PARAMS)?;
175
176 let pub_cose_key = match key_material {
177 KeyMaterial::Ec(curve, curve_type, ref key) => key.public_cose_key(
178 &*self.imp.ec,
179 curve,
180 curve_type,
181 CoseKeyPurpose::Sign,
182 None,
183 test_mode,
184 )?,
185 _ => return Err(km_err!(InvalidKeyBlob, "expected key material of type variant EC.")),
186 };
187 let pub_cose_key_encoded = pub_cose_key.to_vec().map_err(CborError::from)?;
188 let maced_pub_key =
189 build_maced_pub_key(pub_cose_key_encoded, |data| -> Result<Vec<u8>, Error> {
190 // In test mode, use an all-zero HMAC key.
191 if test_mode == rpc::TestMode(true) {
192 return hmac_sha256(&*self.imp.hmac, &[0; 32], data);
193 }
194 self.dev.rpc.compute_hmac_sha256(&*self.imp.hmac, &*self.imp.hkdf, data)
195 })?;
196
197 let key_result = self.finish_keyblob_creation(
198 &RPC_P256_KEYGEN_PARAMS,
199 None,
200 chars,
201 key_material,
202 keyblob::SlotPurpose::KeyGeneration,
203 )?;
204
205 Ok((MacedPublicKey { maced_key: maced_pub_key }, key_result.key_blob))
206 }
207
generate_cert_req( &self, _test_mode: rpc::TestMode, _keys_to_sign: Vec<MacedPublicKey>, _eek_chain: &[u8], _challenge: &[u8], ) -> Result<(DeviceInfo, ProtectedData, Vec<u8>), Error>208 pub(crate) fn generate_cert_req(
209 &self,
210 _test_mode: rpc::TestMode,
211 _keys_to_sign: Vec<MacedPublicKey>,
212 _eek_chain: &[u8],
213 _challenge: &[u8],
214 ) -> Result<(DeviceInfo, ProtectedData, Vec<u8>), Error> {
215 if self.rpc_info.get_version() > IRPC_V2 {
216 return Err(rpc_err!(Removed, "generate_cert_req is not supported in IRPC V3+ HAL."));
217 }
218 let _device_info = self.rpc_device_info()?;
219 Err(km_err!(Unimplemented, "GenerateCertificateRequest is only required for RKP before v3"))
220 }
221
generate_cert_req_v2( &self, keys_to_sign: Vec<MacedPublicKey>, challenge: &[u8], ) -> Result<Vec<u8>, Error>222 pub(crate) fn generate_cert_req_v2(
223 &self,
224 keys_to_sign: Vec<MacedPublicKey>,
225 challenge: &[u8],
226 ) -> Result<Vec<u8>, Error> {
227 if self.rpc_info.get_version() < IRPC_V3 {
228 return Err(km_err!(
229 Unimplemented,
230 "generate_cert_req_v2 is not implemented for IRPC HAL V2 and below."
231 ));
232 }
233 if challenge.len() > MAX_CHALLENGE_SIZE_V2 {
234 return Err(km_err!(
235 InvalidArgument,
236 "Challenge is too big. Actual: {:?}. Maximum: {:?}.",
237 challenge.len(),
238 MAX_CHALLENGE_SIZE_V2
239 ));
240 }
241 // Validate mac and extract the public keys to sign from the MacedPublicKeys
242 let mut pub_cose_keys: Vec<Value> = Vec::new();
243 for key_to_sign in keys_to_sign {
244 let maced_pub_key = key_to_sign.maced_key;
245 let cose_mac0 = CoseMac0::from_slice(&maced_pub_key).map_err(CborError::from)?;
246 // Decode the public cose key from payload and check for test keys in production.
247 // TODO: if implementing IRPC V2, create a helper function to check for test keys that
248 // takes an indication of whether test mode is allowed
249 if let Some(pub_cose_key_data) = &cose_mac0.payload {
250 let pub_cose_key_cbor = read_to_value(pub_cose_key_data)?;
251 let pub_cose_key =
252 CoseKey::from_cbor_value(pub_cose_key_cbor.clone()).map_err(CborError::from)?;
253 let params = pub_cose_key.params;
254 for param in params {
255 if param.0 == Label::Int(RKP_TEST_KEY_CBOR_MARKER) {
256 return Err(rpc_err!(
257 TestKeyInProductionRequest,
258 "test key found in the request for generating CSR IRPC V3"
259 ));
260 }
261 }
262 pub_cose_keys.try_push(pub_cose_key_cbor)?;
263 } else {
264 return Err(rpc_err!(Failed, "no payload found in a MacedPublicKey"));
265 }
266
267 cose_mac0.verify_tag(&[], |expected_tag, data| -> Result<(), Error> {
268 let computed_tag =
269 self.dev.rpc.compute_hmac_sha256(&*self.imp.hmac, &*self.imp.hkdf, data)?;
270 if self.imp.compare.eq(expected_tag, &computed_tag) {
271 Ok(())
272 } else {
273 Err(rpc_err!(InvalidMac, "invalid tag found in a MacedPublicKey"))
274 }
275 })?;
276 }
277 // Construct the `CsrPayload`
278 let rpc_device_info = self.rpc_device_info_cbor()?;
279 let csr_payload = cbor!([
280 Value::Integer(self.rpc_info.get_version().into()),
281 Value::Text(String::from(CERT_TYPE_KEYMINT)),
282 rpc_device_info,
283 Value::Array(pub_cose_keys),
284 ])?;
285 let csr_payload_data = serialize_cbor(&csr_payload)?;
286 // Construct the payload for `SignedData`
287 let signed_data_payload =
288 cbor!([Value::Bytes(challenge.to_vec()), Value::Bytes(csr_payload_data)])?;
289 let signed_data_payload_data = serialize_cbor(&signed_data_payload)?;
290
291 // Process DICE info.
292 let dice_info =
293 self.get_dice_info().ok_or_else(|| rpc_err!(Failed, "DICE info not available."))?;
294 let uds_certs = read_to_value(&dice_info.pub_dice_artifacts.uds_certs)?;
295 let dice_cert_chain = read_to_value(&dice_info.pub_dice_artifacts.dice_cert_chain)?;
296
297 // Get `SignedData`
298 let signed_data_cbor = read_to_value(&self.dev.rpc.sign_data_in_cose_sign1(
299 &*self.imp.ec,
300 &dice_info.signing_algorithm,
301 &signed_data_payload_data,
302 &[],
303 None,
304 )?)?;
305
306 // Construct `AuthenticatedRequest<CsrPayload>`
307 let authn_req = cbor!([
308 Value::Integer(AUTH_REQ_SCHEMA_V1.into()),
309 uds_certs,
310 dice_cert_chain,
311 signed_data_cbor,
312 ])?;
313 serialize_cbor(&authn_req)
314 }
315 }
316
317 /// Helper function to construct `MacedPublicKey` in MacedPublicKey.aidl
build_maced_pub_key<F>(pub_cose_key: Vec<u8>, compute_mac: F) -> Result<Vec<u8>, Error> where F: FnOnce(&[u8]) -> Result<Vec<u8>, Error>,318 fn build_maced_pub_key<F>(pub_cose_key: Vec<u8>, compute_mac: F) -> Result<Vec<u8>, Error>
319 where
320 F: FnOnce(&[u8]) -> Result<Vec<u8>, Error>,
321 {
322 let protected = HeaderBuilder::new().algorithm(iana::Algorithm::HMAC_256_256).build();
323 let cose_mac_0 = CoseMac0Builder::new()
324 .protected(protected)
325 .payload(pub_cose_key)
326 .try_create_tag(&[], compute_mac)?
327 .build();
328 Ok(cose_mac_0.to_vec().map_err(CborError::from)?)
329 }
330
331 /// Helper function to serialize a `cbor::value::Value` into bytes.
serialize_cbor(cbor_value: &Value) -> Result<Vec<u8>, Error>332 pub fn serialize_cbor(cbor_value: &Value) -> Result<Vec<u8>, Error> {
333 let mut buf = Vec::new();
334 cbor::ser::into_writer(cbor_value, &mut buf)
335 .map_err(|_e| Error::Cbor(CborError::EncodeFailed))?;
336 Ok(buf)
337 }
338