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