blob: f14787fcfc57e604f0274163835a9d3351ab3c1f [file] [log] [blame]
// Copyright 2023, 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.
//! This module contains the structs related to the CSR (Certificate Signing Request)
//! sent from the client VM to the service VM for attestation.
use alloc::vec;
use alloc::vec::Vec;
use ciborium::Value;
use coset::{self, CborSerializable, CoseError};
use log::error;
/// Represents a CSR sent from the client VM to the service VM for attestation.
///
/// See client_vm_csr.cddl for the definition of the CSR.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Csr {
/// The DICE certificate chain of the client VM.
pub dice_cert_chain: Vec<u8>,
/// The signed CSR payload in COSE_Sign structure, which includes two signatures:
/// - one by CDI_Leaf_Priv of the client VM's DICE chain,
/// - another by the private key corresponding to the public key.
pub signed_csr_payload: Vec<u8>,
}
impl Csr {
/// Serializes this object to a CBOR-encoded vector.
pub fn into_cbor_vec(self) -> coset::Result<Vec<u8>> {
let value = Value::Array(vec![
Value::Bytes(self.dice_cert_chain),
Value::Bytes(self.signed_csr_payload),
]);
value.to_vec()
}
/// Creates an object instance from the provided CBOR-encoded slice.
pub fn from_cbor_slice(data: &[u8]) -> coset::Result<Self> {
let value = Value::from_slice(data)?;
let Value::Array(mut arr) = value else {
return Err(CoseError::UnexpectedItem(cbor_value_type(&value), "array"));
};
if arr.len() != 2 {
return Err(CoseError::UnexpectedItem("array", "array with 2 items"));
}
Ok(Self {
signed_csr_payload: value_to_bytes(arr.remove(1), "signed_csr_payload")?,
dice_cert_chain: value_to_bytes(arr.remove(0), "dice_cert_chain")?,
})
}
}
/// Represents the data to be signed and sent from the client VM to the service VM
/// for attestation.
///
/// It will be signed by both CDI_Leaf_Priv of the client VM's DICE chain and
/// the private key corresponding to the public key to be attested.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CsrPayload {
/// COSE_Key encoded EC P-256 public key to be attested.
pub public_key: Vec<u8>,
/// A random array with a length between 0 and 64.
/// It will be included in the certificate chain in the attestation result,
/// serving as proof of the freshness of the result.
pub challenge: Vec<u8>,
}
impl CsrPayload {
/// Serializes this object to a CBOR-encoded vector.
pub fn into_cbor_vec(self) -> coset::Result<Vec<u8>> {
let value = Value::Array(vec![Value::Bytes(self.public_key), Value::Bytes(self.challenge)]);
value.to_vec()
}
/// Creates an object instance from the provided CBOR-encoded slice.
pub fn from_cbor_slice(data: &[u8]) -> coset::Result<Self> {
let value = Value::from_slice(data)?;
let Value::Array(mut arr) = value else {
return Err(CoseError::UnexpectedItem(cbor_value_type(&value), "array"));
};
if arr.len() != 2 {
return Err(CoseError::UnexpectedItem("array", "array with 2 items"));
}
Ok(Self {
challenge: value_to_bytes(arr.remove(1), "challenge")?,
public_key: value_to_bytes(arr.remove(0), "public_key")?,
})
}
}
/// Converts the provided value `v` to bytes array.
pub fn value_to_bytes(v: Value, context: &'static str) -> coset::Result<Vec<u8>> {
v.into_bytes().map_err(|e| to_unexpected_item_error(&e, "bstr", context))
}
/// Builds a `CoseError::UnexpectedItem` error when the provided value `v` is not of the expected
/// type `expected_type` and logs the error message with the provided `context`.
pub fn to_unexpected_item_error(
v: &Value,
expected_type: &'static str,
context: &'static str,
) -> CoseError {
let v_type = cbor_value_type(v);
assert!(v_type != expected_type);
error!("The provided value type '{v_type}' is not of type '{expected_type}': {context}");
CoseError::UnexpectedItem(v_type, expected_type)
}
/// Reads the type of the provided value `v`.
pub fn cbor_value_type(v: &Value) -> &'static str {
match v {
Value::Integer(_) => "int",
Value::Bytes(_) => "bstr",
Value::Float(_) => "float",
Value::Text(_) => "tstr",
Value::Bool(_) => "bool",
Value::Null => "nul",
Value::Tag(_, _) => "tag",
Value::Array(_) => "array",
Value::Map(_) => "map",
_ => "other",
}
}