blob: 39da50e14c5ddd5ee615453f22bddb50a125e7cf [file] [log] [blame]
//
// 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)?))
}
}