| // |
| // 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<Vec<u8>, Error>; |
| } |
| |
| /// Common emulated implementation of RPC artifact retrieval. |
| pub struct Artifacts<T: DeriveBytes> { |
| derive: T, |
| sign_algo: CsrSigningAlgorithm, |
| // Invariant once populated: `self.dice_info.signing_algorithm` == `self.sign_algo` |
| dice_info: RefCell<Option<DiceInfo>>, |
| // Invariant once populated: `self.bcc_signing_key` is a variant that matches `self.sign_algo` |
| bcc_signing_key: RefCell<Option<ec::Key>>, |
| } |
| |
| impl<T: DeriveBytes> RetrieveRpcArtifacts for Artifacts<T> { |
| fn derive_bytes_from_hbk( |
| &self, |
| _hkdf: &dyn crypto::Hkdf, |
| context: &[u8], |
| output_len: usize, |
| ) -> Result<Vec<u8>, Error> { |
| self.derive.derive_bytes(context, output_len) |
| } |
| |
| fn get_dice_info(&self, _test_mode: rpc::TestMode) -> Result<DiceInfo, Error> { |
| 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<RpcV2Req>, |
| ) -> Result<Vec<u8>, 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<T: DeriveBytes> Artifacts<T> { |
| /// 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)?)) |
| } |
| } |