use vsf::schema::{SectionSchema, TypeConstraint};
use vsf::verification::{is_original, read_verified, verify_file_hash};
use vsf::vsf_builder::VsfBuilder;
use vsf::VsfType;
fn sample_digest() -> [u8; 32] {
*blake3::hash(b"octopus-payload-digest").as_bytes()
}
fn payload_schema() -> SectionSchema {
SectionSchema::new("payload")
.field("count", TypeConstraint::AnyUnsigned)
.field("digest", TypeConstraint::AnyHash)
.field("label", TypeConstraint::AnyString)
}
fn build_payload_doc() -> Vec<u8> {
VsfBuilder::new()
.add_section(
"payload",
vec![
("count".to_string(), VsfType::u4(0xBEEF)),
("digest".to_string(), VsfType::hp(sample_digest().to_vec())),
("label".to_string(), VsfType::a("octopus".to_string())),
],
)
.build()
.expect("well-formed payload document must build")
}
#[test]
fn verified_round_trip_reads_fields_back() {
let doc = build_payload_doc();
is_original(&doc).expect("provenance (hp) must self-verify on a fresh build");
verify_file_hash(&doc).expect("rolling hash (hb) must verify on a fresh build");
read_verified(&doc, None).expect("well-formed doc must pass read_verified");
let parsed = vsf::schema::SectionBuilder::parse_document(payload_schema(), &doc, None)
.expect("parse_document must read a verified section");
let count: u16 = parsed
.get_value("count")
.expect("count field must be present");
assert_eq!(count, 0xBEEF, "count must round-trip exactly");
let digest_values = parsed.get("digest").expect("digest field must be present");
let digest_bytes = match digest_values.first() {
Some(VsfType::hp(bytes)) => bytes.clone(),
other => panic!("digest should decode as hp, got {:?}", other),
};
assert_eq!(
digest_bytes,
sample_digest().to_vec(),
"digest bytes must round-trip unchanged"
);
let label: String = parsed.get_value("label").expect("label field must be present");
assert_eq!(label, "octopus", "label must round-trip exactly");
}
#[test]
fn hp_only_documents_verify_on_provenance_alone() {
let hp_only = VsfBuilder::new()
.provenance_only()
.add_section(
"error",
vec![
("reason".to_string(), VsfType::a("worker_failed".to_string())),
(
"detail".to_string(),
VsfType::a("upstream timeout".to_string()),
),
],
)
.build()
.expect("hp-only doc must build");
is_original(&hp_only).expect("hp self-consistency");
read_verified(&hp_only, None).expect("hp-only doc verifies on provenance alone");
let mut tampered = hp_only.clone();
let last = tampered.len() - 2;
tampered[last] ^= 0xFF;
read_verified(&tampered, None).expect_err("tampered hp-only doc must be rejected");
read_verified(&hp_only, Some([0x42; 32]))
.expect_err("pinned-signer read must refuse an unsigned hp-only doc");
let schema = SectionSchema::new("error").field("reason", TypeConstraint::AnyString);
let sec = vsf::schema::SectionBuilder::parse_document(schema, &hp_only, None)
.expect("parse_document accepts an hp-only doc");
let reason: String = sec.get_value("reason").expect("reason field");
assert_eq!(reason, "worker_failed");
}
#[test]
fn tampering_breaks_both_hashes() {
let mut doc = build_payload_doc();
is_original(&doc).expect("clean doc provenance ok");
verify_file_hash(&doc).expect("clean doc rolling hash ok");
let close = doc
.iter()
.rposition(|&b| b == b']')
.expect("document must have a closing bracket");
let target = close - 1;
doc[target] ^= 0x01;
assert!(
is_original(&doc).is_err(),
"provenance must fail after a payload byte is flipped"
);
assert!(
verify_file_hash(&doc).is_err(),
"rolling hash must fail after a payload byte is flipped"
);
assert!(
read_verified(&doc, None).is_err(),
"read_verified must refuse a tampered doc"
);
}
#[test]
fn parse_document_tolerates_unknown_field() {
let doc = VsfBuilder::new()
.add_section(
"payload",
vec![
("count".to_string(), VsfType::u4(0xBEEF)),
("digest".to_string(), VsfType::hp(sample_digest().to_vec())),
("label".to_string(), VsfType::a("octopus".to_string())),
("surprise".to_string(), VsfType::u3(1)),
],
)
.build()
.expect("doc with an extra field must build");
let parsed = vsf::schema::SectionBuilder::parse_document(payload_schema(), &doc, None)
.expect("parse_document must tolerate an unknown extra field");
let count: u16 = parsed.get_value("count").expect("count still present");
assert_eq!(count, 0xBEEF);
let label: String = parsed.get_value("label").expect("label still present");
assert_eq!(label, "octopus");
assert!(
parsed.get("surprise").is_err(),
"unknown field must be dropped from the parsed builder"
);
}
#[test]
fn emit_accept_fixture() {
let doc = build_payload_doc();
is_original(&doc).expect("fixture provenance ok");
verify_file_hash(&doc).expect("fixture rolling hash ok");
std::fs::write("/tmp/accept.vsf", &doc).expect("write /tmp/accept.vsf");
}
#[cfg(feature = "crypto")]
#[test]
fn emit_accept_signed_fixture() {
use ed25519_dalek::SigningKey;
let signing_key = SigningKey::from_bytes(&[7u8; 32]);
let pubkey: [u8; 32] = signing_key.verifying_key().to_bytes();
let secret: [u8; 32] = signing_key.to_bytes();
let unsigned = VsfBuilder::new()
.signed_only(VsfType::ke(pubkey.to_vec()))
.add_section(
"payload",
vec![
("count".to_string(), VsfType::u4(0xBEEF)),
("label".to_string(), VsfType::a("octopus".to_string())),
],
)
.build()
.expect("signed_only doc must build");
let signed = vsf::verification::sign_file(unsigned, &secret).expect("sign_file");
read_verified(&signed, Some(pubkey)).expect("signed fixture must verify");
std::fs::write("/tmp/accept_signed.vsf", &signed).expect("write /tmp/accept_signed.vsf");
}
#[cfg(feature = "crypto")]
#[test]
fn signed_round_trip_checks_signer() {
use ed25519_dalek::SigningKey;
let signing_key = SigningKey::from_bytes(&[7u8; 32]);
let pubkey: [u8; 32] = signing_key.verifying_key().to_bytes();
let secret: [u8; 32] = signing_key.to_bytes();
let unsigned = VsfBuilder::new()
.signed_only(VsfType::ke(pubkey.to_vec()))
.add_section(
"payload",
vec![
("count".to_string(), VsfType::u4(0xBEEF)),
("label".to_string(), VsfType::a("octopus".to_string())),
],
)
.build()
.expect("signed_only doc must build");
let signed =
vsf::verification::sign_file(unsigned, &secret).expect("sign_file must fill the signature");
read_verified(&signed, Some(pubkey)).expect("correct signer must pass read_verified");
read_verified(&signed, None).expect("valid signature with no pin must pass");
let wrong = [9u8; 32];
let err = read_verified(&signed, Some(wrong))
.expect_err("wrong signer key must be rejected");
assert!(
err.contains("untrusted"),
"wrong-signer rejection must name the reason, got: {err}"
);
let schema = SectionSchema::new("payload")
.field("count", TypeConstraint::AnyUnsigned)
.field("label", TypeConstraint::AnyString);
let parsed = vsf::schema::SectionBuilder::parse_document(schema, &signed, Some(pubkey))
.expect("parse_document must read a signed, verified section");
let count: u16 = parsed.get_value("count").expect("count present in signed doc");
assert_eq!(count, 0xBEEF);
}
#[test]
fn pinned_signer_rejects_unsigned_document() {
let doc = vsf::VsfBuilder::new()
.creation_time_oscillations(vsf::eagle_time_oscillations())
.add_section("data", vec![("x".to_string(), vsf::VsfType::u(7, false))])
.build()
.unwrap();
assert!(vsf::verification::read_verified(&doc, None).is_ok());
let err = vsf::verification::read_verified(&doc, Some([0x42; 32])).unwrap_err();
assert!(err.contains("unsigned"), "unexpected error: {err}");
}