← Week 1: The SPIFFE Model

Day 3: X.509-SVIDs — How They Differ from Ordinary Certificates

Phase 4 · August 7, 2026

← Week 1: The SPIFFE Model

Agenda (2–3 hours)

  • Read (30 min): X.509-SVID spec (re-read with Phase 1 knowledge active)
  • Build (90 min): Generate an X.509-SVID manually with rcgen
  • Analyze (30 min): Compare an SVID DER to a classical TLS cert DER
← Week 1: The SPIFFE Model

An X.509-SVID Is an Ordinary X.509 Certificate

There is no new certificate format. An SVID is a standard RFC 5280 X.509 cert with:

  1. A URI SAN containing the SPIFFE ID
  2. Short validity
  3. Both id-kp-serverAuth and id-kp-clientAuth in EKU (it's used for mTLS)
  4. Leaf key usage: digitalSignature only (no keyCertSign, no keyEncipherment)

Everything else — DER encoding, chain validation, TLS handshake use — is identical
to what you built in Phases 1 and 2.

← Week 1: The SPIFFE Model

Generating an X.509-SVID with rcgen

use rcgen::{CertificateParams, DistinguishedName, ExtendedKeyUsagePurpose,
            KeyUsagePurpose, SanType};
use std::str::FromStr;

let mut params = CertificateParams::default();

// Identity is in the SAN, not the Subject
params.distinguished_name = DistinguishedName::new(); // empty subject is valid

// The SPIFFE ID goes here
params.subject_alt_names = vec![
    SanType::URI(rcgen::Ia5String::from_str(
        "spiffe://leo.amazon.com/ns/prod/svc/provisioning"
    )?),
];

// Short validity — 1 hour
let now = rcgen::date_time_ymd(2026, 8, 7);
params.not_before = now;
params.not_after = now; // rcgen: you'd add 1h via time offset

// Leaf: no CA flag
params.is_ca = rcgen::IsCa::NoCa;

// EKU: both server and client auth (used in mTLS)
params.extended_key_usages = vec![
    ExtendedKeyUsagePurpose::ServerAuth,
    ExtendedKeyUsagePurpose::ClientAuth,
];
← Week 1: The SPIFFE Model

Key Usage for SVIDs

Leaf SVID key usage:

KeyUsage:
    digitalSignature   ← MUST be set
    keyAgreement       ← set if key is ECDH-capable (P-256 etc.)
    # keyCertSign      ← MUST NOT be set (leaf, not CA)
    # keyEncipherment  ← optional; not required for ECDSA

Note: keyAgreement is not the same as TLS key exchange — that's handled
by the TLS layer's ephemeral key. The cert's key is used only for digitalSignature
in the TLS CertificateVerify message.

This is exactly what you configured in toy-pki for client certs.
SVIDs are structurally client certs with a URI SAN.

← Week 1: The SPIFFE Model

Parsing an SVID with x509-parser

use x509_parser::prelude::*;
use x509_parser::extensions::GeneralName;

fn extract_spiffe_id(cert_der: &[u8]) -> Option<String> {
    let (_, cert) = X509Certificate::from_der(cert_der).ok()?;
    
    for san in cert.subject_alternative_names()? {
        for name in &san.value.general_names {
            if let GeneralName::URI(uri) = name {
                if uri.starts_with("spiffe://") {
                    return Some(uri.to_string());
                }
            }
        }
    }
    None
}

This is the core of any SVID validator. You did the hard work in Phase 1 (x509-parser)
and Phase 2 (chain validation). SPIFFE adds one semantic check: URI SAN = spiffe://...

← Week 1: The SPIFFE Model

SVID Validity — The Short-Lived Philosophy

Classical TLS cert: 13 months (the ACM maximum)

X.509-SVID: typically 1 hour; maximum set by trust domain policy

Why so short?

  • Short validity IS revocation — no OCSP/CRL needed
  • If an attacker steals an SVID, it expires before they can do much
  • Automatic rotation means short validity is costless (SPIRE rotates before expiry)
  • PQC migration: upgrade the CA once → all workloads get PQC SVIDs on next rotation

The SPIRE agent polls the server and refreshes SVIDs before expiry.
The workload sees a fresh cert appear on the Workload API socket — zero downtime.

← Week 1: The SPIFFE Model

Challenge Assignment

Build a make-svid binary in a new spiffe-demo project:

  1. Create spiffe-demo/ as a new Rust project (will grow all week)
  2. Implement make_svid(spiffe_id: &str, signing_ca: &Ca) -> CertificateDer:
    • URI SAN containing the provided SPIFFE ID
    • 1-hour validity
    • EKU: serverAuth + clientAuth
    • Empty subject (DN)
    • Signed by the provided CA key
  3. Parse the output with x509-parser and print:
    • Serial number
    • Validity window
    • URI SAN (the SPIFFE ID)
    • Key usage + EKU flags
  4. Compute and print the cert size in bytes
  5. Compare: how does this differ from a classical TLS server cert (subject, validity, EKU)?

You already have rcgen and x509-parser from toy-pki — reuse that knowledge.

← Week 1: The SPIFFE Model

Resources

  • X.509-SVID spec: github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md
  • rcgen SanType::URI: docs.rs/rcgen (look for URI variant in SanType enum)
  • x509-parser GeneralName: docs.rs/x509-parser