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

nordisk/dwarves

crates/dwarves-slsa/tests/dsse.rs

//! DSSE v1: the PAE, the envelope, and the P-256 check.

mod common;

use base64::engine::general_purpose::STANDARD as B64;
use base64::Engine as _;
use dwarves_slsa::dsse::{self, Envelope, Signature};

fn envelope(k: &common::TestKey, payload: &[u8]) -> Envelope {
    let pae = dsse::pae("application/vnd.in-toto+json", payload);
    Envelope {
        payload_type: "application/vnd.in-toto+json".into(),
        payload: B64.encode(payload),
        signatures: vec![Signature {
            keyid: dsse::keyid(&k.spki),
            sig: B64.encode(common::sign(k, &pae)),
        }],
    }
}

#[test]
fn pae_is_the_dsse_spec_vector() {
    assert_eq!(
        dsse::pae("http://example.com/HelloWorld", b"hello world"),
        b"DSSEv1 29 http://example.com/HelloWorld 11 hello world".to_vec()
    );
    assert_eq!(dsse::pae("", b""), b"DSSEv1 0  0 ".to_vec());
}

#[test]
fn a_signed_envelope_verifies_with_its_key() {
    let k = common::key();
    let env = envelope(&k, b"{\"a\":1}\n");
    assert_eq!(dsse::verify_p256(&env, &k.spki).unwrap(), 0);
    assert_eq!(env.payload_bytes().unwrap(), b"{\"a\":1}\n");
}

#[test]
fn a_changed_payload_is_refused() {
    let k = common::key();
    let mut env = envelope(&k, b"{\"a\":1}\n");
    env.payload = B64.encode(b"{\"a\":2}\n");
    assert!(dsse::verify_p256(&env, &k.spki).is_err());
}

#[test]
fn a_changed_payload_type_is_refused() {
    let k = common::key();
    let mut env = envelope(&k, b"{}");
    env.payload_type = "application/json".into();
    assert!(dsse::verify_p256(&env, &k.spki).is_err());
}

#[test]
fn another_key_is_refused() {
    let k = common::key();
    let other = common::key();
    let env = envelope(&k, b"{}");
    let e = dsse::verify_p256(&env, &other.spki).unwrap_err();
    assert!(e.0.contains("verif"), "{e}");
}

#[test]
fn the_signature_that_verifies_is_found_among_several() {
    let k = common::key();
    let other = common::key();
    let mut env = envelope(&other, b"{}");
    env.signatures
        .push(envelope(&k, b"{}").signatures.remove(0));
    assert_eq!(dsse::verify_p256(&env, &k.spki).unwrap(), 1);
}

#[test]
fn an_envelope_signed_by_openssl_verifies() {
    // Signed outside this crate (openssl dgst -sha256 -sign over the PAE), as a YubiKey signs.
    let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/openssl");
    let spki = dsse::spki_from_pem(&std::fs::read_to_string(dir.join("release-test.pub")).unwrap())
        .unwrap();
    let bundle =
        dsse::parse_bundle(&std::fs::read_to_string(dir.join("release.intoto.jsonl")).unwrap())
            .unwrap();
    assert_eq!(bundle.len(), 1);
    assert_eq!(dsse::verify_p256(&bundle[0], &spki).unwrap(), 0);
}

#[test]
fn a_pem_public_key_reads_and_its_keyid_is_the_spki_sha256() {
    let k = common::key();
    let pem = format!(
        "-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----\n",
        B64.encode(&k.spki)
    );
    let spki = dsse::spki_from_pem(&pem).unwrap();
    assert_eq!(spki, k.spki);
    assert_eq!(
        dsse::keyid(&spki),
        dwarves_slsa::subjects::sha256_hex(&k.spki)
    );
    assert_eq!(dsse::p256_point(&spki).unwrap().len(), 65);
}

#[test]
fn a_key_that_is_not_p256_is_refused() {
    // An Ed25519 SubjectPublicKeyInfo.
    let ed = hex::decode("302a300506032b6570032100")
        .unwrap()
        .into_iter()
        .chain([7u8; 32])
        .collect::<Vec<u8>>();
    assert!(dsse::p256_point(&ed).is_err());
    assert!(dsse::p256_point(&[0u8; 10]).is_err());
    assert!(
        dsse::spki_from_pem("-----BEGIN PUBLIC KEY-----\n!!!\n-----END PUBLIC KEY-----\n").is_err()
    );
}

#[test]
fn a_bundle_is_json_lines_or_one_pretty_envelope() {
    let k = common::key();
    let a = envelope(&k, b"{}");
    let line = a.to_line();
    assert!(line.ends_with('\n'));
    assert_eq!(line.matches('\n').count(), 1);
    let two = format!("{line}{line}");
    assert_eq!(dsse::parse_bundle(&two).unwrap().len(), 2);
    let pretty = serde_json::to_string_pretty(&a).unwrap();
    assert_eq!(dsse::parse_bundle(&pretty).unwrap(), vec![a]);
    assert!(dsse::parse_bundle("not json").is_err());
    assert!(dsse::parse_bundle("").is_err());
}