blob: 0d36aee904e9f6e4b46eb552713ed9fdbe0c700e [file]
use crate::dice::Payload;
use crate::publickey::PublicKey;
use anyhow::Result;
use std::collections::HashSet;
use std::fmt::{self, Display, Formatter};
use thiserror::Error;
/// Enumeration of the different forms that a DICE chain can take.
pub enum ChainForm {
/// A proper DICE chain with multiple layers of trust.
Proper(Chain),
/// A degenerate DICE chain consisting of a single self-signed certificate.
Degenerate(DegenerateChain),
}
/// Represents a DICE chain. This consists of the root public key (which signs the first
/// certificate), followed by a chain of certificates.
#[derive(Debug)]
pub struct Chain {
root_public_key: PublicKey,
payloads: Vec<Payload>,
}
#[derive(Error, Debug, PartialEq, Eq)]
pub(crate) enum ValidationError {
#[error("no payloads")]
NoPayloads,
#[error("issuer `{1}` is not previous subject `{2}` in payload {0}")]
IssuerMismatch(usize, String, String),
#[error("repeated subject in payload {0}")]
RepeatedSubject(usize, String),
#[error("repeated key in payload {0}")]
RepeatedKey(usize),
}
impl Chain {
/// Builds a [`Chain`] after checking that it is well-formed. The issuer of each entry must be
/// equal to the subject of the previous entry. The chain is not allowed to contain any
/// repeated subjects or subject public keys as that would suggest something untoward has
/// happened.
pub(crate) fn validate(
root_public_key: PublicKey,
payloads: Vec<Payload>,
) -> Result<Self, ValidationError> {
if payloads.is_empty() {
return Err(ValidationError::NoPayloads);
}
let mut subjects = HashSet::with_capacity(payloads.len());
let mut keys = HashSet::with_capacity(1 + payloads.len());
keys.insert(root_public_key.to_pem());
let mut previous_subject: Option<&str> = None;
for (n, payload) in payloads.iter().enumerate() {
if let Some(previous_subject) = previous_subject {
if payload.issuer() != previous_subject {
return Err(ValidationError::IssuerMismatch(
n,
payload.issuer().to_string(),
previous_subject.to_string(),
));
}
}
if subjects.replace(payload.subject()).is_some() {
return Err(ValidationError::RepeatedSubject(n, payload.subject().to_string()));
}
if keys.replace(payload.subject_public_key().to_pem()).is_some() {
return Err(ValidationError::RepeatedKey(n));
}
previous_subject = Some(payload.subject());
}
Ok(Self { root_public_key, payloads })
}
/// Get the root public key which verifies the first certificate in the chain.
pub fn root_public_key(&self) -> &PublicKey {
&self.root_public_key
}
/// Get the payloads of the certificates in the chain, from root to leaf.
pub fn payloads(&self) -> &[Payload] {
&self.payloads
}
/// Get the payload from the final certificate in the chain.
pub fn leaf(&self) -> &Payload {
// There is always at least one payload.
self.payloads.last().unwrap()
}
}
impl Display for Chain {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
writeln!(f, "Root public key:")?;
writeln!(f, "{}", self.root_public_key.to_pem())?;
for (i, payload) in self.payloads.iter().enumerate() {
writeln!(f, "Cert {}:", i)?;
writeln!(f, "{}", payload)?;
}
Ok(())
}
}
#[derive(Error, Debug, PartialEq, Eq)]
pub(crate) enum DegenerateChainError {
#[error("issuer empty")]
IssuerEmpty,
#[error("subject empty")]
SubjectEmpty,
}
/// A degenerate DICE chain. These chains consist of a single, self-signed certificate and the
/// entries contain less information than usual. They are expected from devices that haven't
/// implemented everything necessary to produce a proper DICE Chain.
#[derive(Debug)]
pub struct DegenerateChain {
issuer: String,
subject: String,
subject_public_key: PublicKey,
}
impl DegenerateChain {
pub(crate) fn new<I: Into<String>, S: Into<String>>(
issuer: I,
subject: S,
subject_public_key: PublicKey,
) -> Result<Self, DegenerateChainError> {
let issuer = issuer.into();
let subject = subject.into();
if issuer.is_empty() {
return Err(DegenerateChainError::IssuerEmpty);
}
if subject.is_empty() {
return Err(DegenerateChainError::SubjectEmpty);
}
Ok(Self { issuer, subject, subject_public_key })
}
/// Gets the issuer of the degenerate chain.
pub fn issuer(&self) -> &str {
&self.issuer
}
/// Gets the subject of the degenerate chain.
pub fn subject(&self) -> &str {
&self.subject
}
/// Gets the public key of the degenerate chain.
pub fn public_key(&self) -> &PublicKey {
&self.subject_public_key
}
}
impl Display for DegenerateChain {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
writeln!(f, "Public key:")?;
writeln!(f, "{}", self.public_key().to_pem())?;
writeln!(f, "Issuer: {}", self.issuer)?;
writeln!(f, "Subject: {}", self.subject)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dice::{DiceMode, PayloadBuilder};
use crate::publickey::testkeys::{PrivateKey, ED25519_KEY_PEM, P256_KEY_PEM, P384_KEY_PEM};
#[test]
fn chain_validate_valid() {
let root_public_key = PrivateKey::from_pem(P256_KEY_PEM[0]).public_key();
let keys = P256_KEY_PEM[1..4].iter().copied().enumerate();
let payloads = keys.map(|(n, key)| valid_payload(n, key).build().unwrap()).collect();
Chain::validate(root_public_key, payloads).unwrap();
}
#[test]
fn chain_validate_valid_with_mixed_kinds_of_key() {
let root_public_key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
let keys = [P256_KEY_PEM[0], P384_KEY_PEM[0]].into_iter().enumerate();
let payloads = keys.map(|(n, key)| valid_payload(n, key).build().unwrap()).collect();
Chain::validate(root_public_key, payloads).unwrap();
}
#[test]
fn chain_validate_fails_without_payloads() {
let root_public_key = PrivateKey::from_pem(P256_KEY_PEM[0]).public_key();
let payloads = Vec::new();
let err = Chain::validate(root_public_key, payloads).unwrap_err();
assert_eq!(err, ValidationError::NoPayloads);
}
#[test]
fn chain_validate_fails_when_root_key_repeated() {
let key = P256_KEY_PEM[0];
let root_public_key = PrivateKey::from_pem(key).public_key();
let payloads = vec![valid_payload(0, key).build().unwrap()];
let err = Chain::validate(root_public_key, payloads).unwrap_err();
assert_eq!(err, ValidationError::RepeatedKey(0));
}
#[test]
fn chain_validate_fails_with_repeated_subject_public_keys() {
let repeated_key = P256_KEY_PEM[0];
let root_public_key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
let payloads = vec![
valid_payload(0, repeated_key).build().unwrap(),
valid_payload(1, repeated_key).build().unwrap(),
];
let err = Chain::validate(root_public_key, payloads).unwrap_err();
assert_eq!(err, ValidationError::RepeatedKey(1));
}
#[test]
fn chain_validate_fails_with_repeated_subjects() {
let keys = &P256_KEY_PEM[..3];
let repeated = "match";
let root_public_key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
let payloads = vec![
valid_payload(0, keys[0]).subject(repeated).build().unwrap(),
valid_payload(1, keys[1]).issuer(repeated).build().unwrap(),
valid_payload(2, keys[2]).subject(repeated).build().unwrap(),
];
let err = Chain::validate(root_public_key, payloads).unwrap_err();
assert_eq!(err, ValidationError::RepeatedSubject(2, repeated.into()));
}
#[test]
fn chain_validate_fails_with_mismatching_issuer_and_subject() {
let expected = "expected";
let wrong = "wrong";
let root_public_key = PrivateKey::from_pem(P256_KEY_PEM[0]).public_key();
let payloads = vec![
valid_payload(0, P256_KEY_PEM[1]).subject(expected).build().unwrap(),
valid_payload(1, P256_KEY_PEM[2]).issuer(wrong).build().unwrap(),
];
let err = Chain::validate(root_public_key, payloads).unwrap_err();
assert_eq!(err, ValidationError::IssuerMismatch(1, wrong.into(), expected.into()));
}
fn valid_payload(index: usize, pem: &str) -> PayloadBuilder {
PayloadBuilder::with_subject_public_key(PrivateKey::from_pem(pem).public_key())
.issuer(format!("component {}", index))
.subject(format!("component {}", index + 1))
.mode(DiceMode::Normal)
.code_hash(vec![0; 64])
.authority_hash(vec![0; 64])
}
#[test]
fn degenerate_chain_valid() {
let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
DegenerateChain::new("issuer", "subject", key).unwrap();
}
#[test]
fn degenerate_chain_empty_issuer() {
let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
let err = DegenerateChain::new("", "subject", key).unwrap_err();
assert_eq!(err, DegenerateChainError::IssuerEmpty);
}
#[test]
fn degenerate_chain_empty_subject() {
let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
let err = DegenerateChain::new("issuer", "", key).unwrap_err();
assert_eq!(err, DegenerateChainError::SubjectEmpty);
}
}