Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

server/lib/tests/oci_evidence/keyless.rs

//! Keyless signing as `cosign sign` writes it, against a Sigstore minted in the test
//! (`provenance/tests/common`), and the signed trust bundle that pins it (HOL-OCI-EVIDENCE).
#![allow(dead_code)]

#[path = "../../../../provenance/tests/common/mod.rs"]
pub mod sigstore;

use rcgen::SigningKey as _;
use sigstore::{MiniSigstore, SIGNED_AT};

use super::*;

/// The signed trust bundle that pins `ms`, and the bundle trust policy that verifies it.
pub struct Trust {
    pub _dir: tempfile::TempDir,
    pub bundle: String,
    pub policy: String,
}

pub fn trust(ms: &MiniSigstore) -> Trust {
    use holger_provenance::trust::{build_trust_bundle, BundleSigner, TrustBundleOptions};
    use update_bundle::policy::{
        ApprovedSigner, CertificateValidity, ExpiredPosture, TrustMode, TrustPolicy,
    };
    use update_bundle::schema::POLICY_SCHEMA;
    let mint = |cn: &str| {
        let key = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap();
        let mut p = rcgen::CertificateParams::new(vec![]).unwrap();
        p.distinguished_name.push(rcgen::DnType::CommonName, cn);
        let der = p.self_signed(&key).unwrap().der().to_vec();
        (
            holger_provenance::crypto::sha256_hex(&der),
            BundleSigner {
                cert_der: der,
                key_pkcs8: key.serialize_der(),
            },
        )
    };
    let (fr, reviewer) = mint("trust-reviewer");
    let (fo, owner) = mint("trust-owner");
    let (fl, release) = mint("trust-release");
    let policy = TrustPolicy {
        schema_version: POLICY_SCHEMA.to_string(),
        trust_mode: Some(TrustMode::PinnedSigners),
        trusted_roots: vec![],
        certificate_validity: CertificateValidity::Enforce,
        approved_signers: vec![
            ApprovedSigner {
                fingerprint: fr,
                subject: None,
                roles: vec!["security-reviewer".into()],
            },
            ApprovedSigner {
                fingerprint: fo,
                subject: None,
                roles: vec!["system-owner".into()],
            },
        ],
        required_approval_roles: vec!["security-reviewer".into(), "system-owner".into()],
        minimum_distinct_approvers: 2,
        trusted_release_signers: vec![fl],
        release_signer_must_be_distinct: true,
        maximum_bundle_age_secs: None,
        require_expiry: true,
        on_expired_metadata: Some(ExpiredPosture::Refuse),
        require_sbom: false,
        require_vex: false,
        require_scan: false,
        allowed_products: vec![holger_provenance::TRUST_PRODUCT.into()],
        allowed_models: vec![],
    };
    let dir = tempfile::tempdir().unwrap();
    let now = chrono::Utc::now();
    let bundle = build_trust_bundle(
        &ms.trusted_root_json(),
        TrustBundleOptions {
            sequence: 1,
            created_at: now - chrono::Duration::minutes(5),
            expires_at: Some(now + chrono::Duration::days(90)),
            supplier: "holger-sigstore-trust".into(),
            producer: "test".into(),
            source_uri: Some("https://tuf.example.test".into()),
            policy: policy.clone(),
            approvals: vec![
                ("security-reviewer".into(), reviewer),
                ("system-owner".into(), owner),
            ],
            release,
            out: dir.path().join("sigstore-trust.holger.znippy"),
            apk_keys: vec![],
            pgp_keys: vec![],
        },
    )
    .expect("the trust bundle is signed");
    let policy_path = dir.path().join("bundle-trust.ron");
    std::fs::write(&policy_path, policy.to_ron().unwrap()).unwrap();
    Trust {
        bundle: bundle.display().to_string(),
        policy: policy_path.display().to_string(),
        _dir: dir,
    }
}

/// **A keyless cosign `.sig` manifest** for `digest`, signed as (`issuer`, `subject`): the
/// simple-signing payload `cosign sign` writes, its signature, the Fulcio-like leaf and the
/// chain, and the Rekor bundle (a `hashedrekord` 0.0.1 body, its SET) as layer annotations.
pub fn keyless_sig(
    ms: &MiniSigstore,
    reference: &str,
    digest: &str,
    issuer: &str,
    subject: &str,
) -> (Vec<u8>, Vec<Vec<u8>>) {
    use holger_provenance::{crypto, tlog};
    let payload = serde_json::to_vec(&serde_json::json!({
        "critical": {
            "identity": {"docker-reference": reference},
            "image": {"docker-manifest-digest": digest},
            "type": "cosign container image signature",
        },
        "optional": null,
    }))
    .unwrap();
    let (key, cert) = ms.leaf(issuer, subject, (2026, 10, 23));
    let sig = key.sign(&payload).unwrap();
    let cert_pem = sigstore::pem_of_der("CERTIFICATE", &cert);
    let body = serde_json::to_vec(&serde_json::json!({
        "apiVersion": "0.0.1", "kind": "hashedrekord",
        "spec": {
            "data": {"hash": {"algorithm": "sha256", "value": crypto::sha256_hex(&payload)}},
            "signature": {"content": b64(&sig), "publicKey": {"content": b64(cert_pem.as_bytes())}},
        },
    }))
    .unwrap();
    let entry = holger_provenance::material::TlogEntry {
        log_index: 4242,
        log_id: ms.rekor_id(),
        integrated_time: SIGNED_AT,
        kind: "hashedrekord".into(),
        version: "0.0.1".into(),
        body: body.clone(),
        set: None,
        proof: None,
    };
    let set = ms.rekor.sign(&tlog::set_payload(&entry)).unwrap();
    let bundle = serde_json::json!({
        "SignedEntryTimestamp": b64(&set),
        "Payload": {"body": b64(&body), "integratedTime": SIGNED_AT, "logIndex": 4242, "logID": hex::encode(ms.rekor_id())},
    });
    let config = br#"{"architecture":"","created":"0001-01-01T00:00:00Z","history":[{"created":"0001-01-01T00:00:00Z"}],"os":"","rootfs":{"type":"layers","diff_ids":[]},"config":{}}"#.to_vec();
    let manifest = serde_json::to_vec(&serde_json::json!({
        "schemaVersion": 2, "mediaType": MANIFEST,
        "config": {"mediaType": CONFIG, "digest": super::digest(&config), "size": config.len()},
        "layers": [{
            "mediaType": "application/vnd.dev.cosign.simplesigning.v1+json",
            "digest": super::digest(&payload), "size": payload.len(),
            "annotations": {
                "dev.cosignproject.cosign/signature": b64(&sig),
                "dev.sigstore.cosign/certificate": cert_pem,
                "dev.sigstore.cosign/chain": sigstore::pem_of_der("CERTIFICATE", &ms.root_der),
                "dev.sigstore.cosign/bundle": bundle.to_string(),
            },
        }],
    }))
    .unwrap();
    (manifest, vec![config, payload])
}

/// Push `img` by digest, its `.sig` (when given), then the tag that takes it in.
pub fn push_signed(
    r: &Running,
    name: &str,
    img: &Img,
    sig: Option<(Vec<u8>, Vec<Vec<u8>>)>,
) -> (u16, String, Vec<u8>) {
    push_blobs(r, "ida", name, &img.blobs());
    assert_eq!(
        push_manifest(r, "ida", name, &img.digest, &img.manifest).0,
        201
    );
    if let Some((m, blobs)) = sig {
        push_blobs(r, "ida", name, &blobs);
        let tag = format!("sha256-{}.sig", img.digest.trim_start_matches("sha256:"));
        assert_eq!(push_manifest(r, "ida", name, &tag, &m).0, 201);
    }
    push_manifest(r, "ida", name, "v1", &img.manifest)
}