// // Copyright (C) 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Emulated implementation of device traits for `IRemotelyProvisionedComponent`. use core::cell::RefCell; use kmr_common::crypto::{ec, ec::CoseKeyPurpose, Ec, KeyMaterial}; use kmr_common::{crypto, explicit, rpc_err, vec_try, Error}; use kmr_crypto_boring::{ec::BoringEc, hmac::BoringHmac, rng::BoringRng}; use kmr_ta::device::{ CsrSigningAlgorithm, DiceInfo, PubDiceArtifacts, RetrieveRpcArtifacts, RpcV2Req, }; use kmr_wire::coset::{iana, CoseSign1Builder, HeaderBuilder}; use kmr_wire::keymint::{Digest, EcCurve}; use kmr_wire::{cbor::value::Value, coset::AsCborValue, rpc, CborError}; /// Trait to encapsulate deterministic derivation of secret data. pub trait DeriveBytes { /// Derive `output_len` bytes of data from `context`, deterministically. fn derive_bytes(&self, context: &[u8], output_len: usize) -> Result, Error>; } /// Common emulated implementation of RPC artifact retrieval. pub struct Artifacts { derive: T, sign_algo: CsrSigningAlgorithm, // Invariant once populated: `self.dice_info.signing_algorithm` == `self.sign_algo` dice_info: RefCell>, // Invariant once populated: `self.bcc_signing_key` is a variant that matches `self.sign_algo` bcc_signing_key: RefCell>, } impl RetrieveRpcArtifacts for Artifacts { fn derive_bytes_from_hbk( &self, _hkdf: &dyn crypto::Hkdf, context: &[u8], output_len: usize, ) -> Result, Error> { self.derive.derive_bytes(context, output_len) } fn get_dice_info(&self, _test_mode: rpc::TestMode) -> Result { if self.dice_info.borrow().is_none() { let (dice_info, priv_key) = self.generate_dice_artifacts(rpc::TestMode(false))?; *self.dice_info.borrow_mut() = Some(dice_info); *self.bcc_signing_key.borrow_mut() = Some(priv_key); } Ok(self .dice_info .borrow() .as_ref() .ok_or_else(|| rpc_err!(Failed, "DICE artifacts are not initialized."))? .clone()) } fn sign_data( &self, ec: &dyn crypto::Ec, data: &[u8], _rpc_v2: Option, ) -> Result, Error> { // DICE artifacts should have been initialized via `get_dice_info()` by the time this // method is called. let private_key = self .bcc_signing_key .borrow() .as_ref() .ok_or_else(|| rpc_err!(Failed, "DICE artifacts are not initialized."))? .clone(); let mut op = ec.begin_sign(private_key.into(), self.signing_digest())?; op.update(data)?; let sig = op.finish()?; crypto::ec::to_cose_signature(self.signing_curve(), sig) } } impl Artifacts { /// Constructor. pub fn new(derive: T, sign_algo: CsrSigningAlgorithm) -> Self { Self { derive, sign_algo, dice_info: RefCell::new(None), bcc_signing_key: RefCell::new(None), } } /// Indicate the curve used in signing. fn signing_curve(&self) -> EcCurve { match self.sign_algo { CsrSigningAlgorithm::ES256 => EcCurve::P256, CsrSigningAlgorithm::ES384 => EcCurve::P384, CsrSigningAlgorithm::EdDSA => EcCurve::Curve25519, } } /// Indicate the digest used in signing. fn signing_digest(&self) -> Digest { match self.sign_algo { CsrSigningAlgorithm::ES256 => Digest::Sha256, CsrSigningAlgorithm::ES384 => Digest::Sha384, CsrSigningAlgorithm::EdDSA => Digest::None, } } /// Indicate the COSE algorithm value associated with signing. fn signing_cose_algo(&self) -> iana::Algorithm { match self.sign_algo { CsrSigningAlgorithm::ES256 => iana::Algorithm::ES256, CsrSigningAlgorithm::ES384 => iana::Algorithm::ES384, CsrSigningAlgorithm::EdDSA => iana::Algorithm::EdDSA, } } fn generate_dice_artifacts( &self, _test_mode: rpc::TestMode, ) -> Result<(DiceInfo, ec::Key), Error> { let ec = BoringEc::default(); let key_material = match self.sign_algo { CsrSigningAlgorithm::EdDSA => { let secret = self.derive_bytes_from_hbk(&BoringHmac, b"Device Key Seed", 32)?; ec::import_raw_ed25519_key(&secret) } // TODO: generate the *same* key after reboot, by use of the TPM. CsrSigningAlgorithm::ES256 => { ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P256, &[]) } CsrSigningAlgorithm::ES384 => { ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P384, &[]) } }?; let (pub_cose_key, private_key) = match key_material { KeyMaterial::Ec(curve, curve_type, key) => ( key.public_cose_key( &ec, curve, curve_type, CoseKeyPurpose::Sign, None, /* no key ID */ rpc::TestMode(false), )?, key, ), _ => { return Err(rpc_err!( Failed, "expected the Ec variant of KeyMaterial for the cdi leaf key." )) } }; let cose_key_cbor = pub_cose_key.to_cbor_value().map_err(CborError::from)?; let cose_key_cbor_data = kmr_ta::rkp::serialize_cbor(&cose_key_cbor)?; // Construct `DiceChainEntryPayload` let dice_chain_entry_payload = Value::Map(vec_try![ // Issuer ( Value::Integer(1.into()), Value::Text(String::from("Issuer")) ), // Subject ( Value::Integer(2.into()), Value::Text(String::from("Subject")) ), // Subject public key ( Value::Integer((-4670552).into()), Value::Bytes(cose_key_cbor_data) ), // Key Usage field contains a CBOR byte string of the bits which correspond // to `keyCertSign` as per RFC 5280 Section 4.2.1.3 (in little-endian byte order) ( Value::Integer((-4670553).into()), Value::Bytes(vec_try![0x20]?) ), ]?); let dice_chain_entry_payload_data = kmr_ta::rkp::serialize_cbor(&dice_chain_entry_payload)?; // Construct `DiceChainEntry` let protected = HeaderBuilder::new() .algorithm(self.signing_cose_algo()) .build(); let dice_chain_entry = CoseSign1Builder::new() .protected(protected) .payload(dice_chain_entry_payload_data) .try_create_signature(&[], |input| { let mut op = ec.begin_sign(private_key.clone(), self.signing_digest())?; op.update(input)?; let sig = op.finish()?; crypto::ec::to_cose_signature(self.signing_curve(), sig) })? .build(); let dice_chain_entry_cbor = dice_chain_entry.to_cbor_value().map_err(CborError::from)?; // Construct `DiceCertChain` let dice_cert_chain = Value::Array(vec_try![cose_key_cbor, dice_chain_entry_cbor]?); let dice_cert_chain_data = kmr_ta::rkp::serialize_cbor(&dice_cert_chain)?; // Construct `UdsCerts` as an empty CBOR map let uds_certs_data = kmr_ta::rkp::serialize_cbor(&Value::Map(Vec::new()))?; let pub_dice_artifacts = PubDiceArtifacts { dice_cert_chain: dice_cert_chain_data, uds_certs: uds_certs_data, }; let dice_info = DiceInfo { pub_dice_artifacts, signing_algorithm: self.sign_algo, rpc_v2_test_cdi_priv: None, }; Ok((dice_info, explicit!(private_key)?)) } }