dcap-qvl 0.4.0

This crate implements the quote verification logic for DCAP (Data Center Attestation Primitives) in pure Rust.
Documentation
//! Pluggable configuration trait surface for quote verification.
//!
//! Quote verification depends on a small set of operations on certificates,
//! signatures, and crypto primitives. Each is captured by a small trait so a
//! downstream consumer (e.g. a smart contract building for WASM) can supply a
//! smaller-footprint implementation without forking this crate. The audited
//! in-tree implementations live in [`crate::x509`], [`crate::signature`], and
//! [`crate::crypto`]; the bundles in [`crate::configs`] (incl.
//! [`DefaultConfig`](crate::configs::DefaultConfig)) pair them into ready-to-use
//! [`Config`]s.
//!
//! ## Config trait
//!
//! [`Config`] bundles the pluggable components as associated types so they can
//! be swapped independently — e.g. keep the audited cert parser but bring your
//! own micro-encoder. A custom config is just a marker type implementing
//! [`Config`]:
//!
//! ```ignore
//! struct MyConfig;
//! impl dcap_qvl::config::Config for MyConfig {
//!     type X509       = dcap_qvl::x509::X509CertBackend; // keep audited
//!     type SigEncoder = MyMicroEncoder;                  // custom
//!     type Crypto     = dcap_qvl::crypto::RingCrypto;
//! }
//!
//! dcap_qvl::verify::verify_with::<MyConfig>(quote, &collateral, now)?;
//! ```
//!
//! ## Auditing
//!
//! Only the in-tree backends ([`crate::x509::X509CertBackend`],
//! [`crate::signature::DerSigEncoder`], [`crate::crypto::RingCrypto`] /
//! [`crate::crypto::RustCryptoCrypto`]) and the bundles in [`crate::configs`]
//! are audited as part of `dcap-qvl`. Custom implementations of [`X509Codec`]
//! / [`ParsedCert`] / [`EcdsaSigEncoder`] / [`CryptoProvider`] are the
//! implementer's responsibility; they SHOULD be checked for byte-for-byte
//! equivalence against [`DefaultConfig`](crate::configs::DefaultConfig) on a
//! representative corpus (see `tests/config_conformance.rs`).
//!
//! ## Trait shape
//!
//! Sub-trait methods (other than [`X509Codec::from_der`]) are associated
//! functions, not `&self` methods, so backends are expected to be zero-sized
//! marker types. [`X509Codec`] is the exception: it produces a [`ParsedCert`]
//! that owns or borrows the parsed data, so multiple field accesses share a
//! single parse.

use alloc::string::String;
use alloc::vec::Vec;
use anyhow::Result;

/// X.509 certificate parser factory.
///
/// Implementations MUST conform to RFC 5280 and X.690 (DER). A backend
/// parses a certificate once via [`X509Codec::from_der`], returning a
/// [`ParsedCert`] that exposes the fields needed by quote verification as
/// `&self` accessors — so a caller that touches multiple fields of the same
/// certificate pays the parsing cost only once.
///
/// The associated type [`X509Codec::Parsed`] is generic over the input
/// lifetime, allowing implementations to choose whether to own their parsed
/// representation or borrow zero-copy from the input DER bytes.
pub trait X509Codec {
    /// Parsed representation. May own its data, or may borrow from the input
    /// `cert_der` slice (zero-copy backends).
    type Parsed<'a>: ParsedCert
    where
        Self: 'a;

    /// Parse a DER-encoded X.509 certificate.
    fn from_der<'a>(cert_der: &'a [u8]) -> Result<Self::Parsed<'a>>;
}

/// Read-only accessors over a parsed X.509 certificate.
pub trait ParsedCert {
    /// Returns the issuer Distinguished Name as a human-readable string in
    /// RFC 4514 form (e.g. `"CN=Foo,O=Bar"`).
    ///
    /// The string is consumed by `intel::quote_ca` for substring matching, so
    /// a stable, conventional representation is required.
    fn issuer_dn(&self) -> Result<String>;

    /// Returns the OCTET STRING contents of the unique extension whose
    /// `extnID` equals `oid`, where `oid` is the DER-encoded OID body
    /// (no tag/length).
    ///
    /// * `Ok(Some(value))` — exactly one matching extension was found.
    /// * `Ok(None)` — no matching extension.
    /// * `Err(_)` — the extension was found more than once, or the certificate
    ///   is malformed.
    fn extension(&self, oid: &[u8]) -> Result<Option<Vec<u8>>>;
}

/// DER encoding of ECDSA signatures.
///
/// DCAP quotes carry signatures as raw `r ‖ s` byte strings, but webpki's
/// signature verifier expects them encoded as `Ecdsa-Sig-Value` (RFC 5480):
/// `SEQUENCE { r INTEGER, s INTEGER }`. This trait performs that conversion.
pub trait EcdsaSigEncoder {
    /// Encodes `(r, s)` as DER `SEQUENCE { INTEGER r, INTEGER s }`.
    ///
    /// Each component is treated as an unsigned big-endian magnitude:
    /// implementations MUST strip leading zero bytes and prepend a single
    /// `0x00` when the high bit of the leading byte would otherwise make the
    /// integer negative under DER's signed-integer rules.
    fn encode_ecdsa_sig(r: &[u8], s: &[u8]) -> Result<Vec<u8>>;
}

/// Cryptographic primitives required by quote verification: the ECDSA
/// signature verification algorithm passed to webpki, plus a SHA-256 hash.
///
/// Implementations are typically zero-sized marker types whose methods are
/// associated functions delegating to a chosen crypto crate (ring, RustCrypto,
/// hardware accelerator, etc.).
pub trait CryptoProvider {
    /// ECDSA P-256 SHA-256 algorithm passed to webpki's signature verifier.
    fn sig_algo() -> &'static dyn rustls_pki_types::SignatureVerificationAlgorithm;
    /// SHA-256 of `data`.
    fn sha256(data: &[u8]) -> [u8; 32];
}

/// Configuration bundle selecting an implementation for each pluggable
/// component.
///
/// Implementations are typically zero-sized marker types. Adding a new
/// pluggable component in the future means adding a new associated type here,
/// which is backwards-compatible (existing custom configs continue to work as
/// long as they implement the new associated type — defaulted with a `where`
/// clause if needed).
pub trait Config {
    /// X.509 certificate parser.
    type X509: X509Codec;
    /// ECDSA `r ‖ s` → DER `Ecdsa-Sig-Value` encoder.
    type SigEncoder: EcdsaSigEncoder;
    /// Cryptographic primitives (ECDSA signature verification + SHA-256).
    type Crypto: CryptoProvider;
}