use super::*;
#[test]
fn test_verify_pack_uses_canonical_bytes() {
let seed: [u8; 32] = [0x88; 32];
let signing_key = keypair_from_seed(seed);
let content = "z: 3\na: 1\nm: 2";
let (envelope, key_id) = create_signed_envelope(&signing_key, content);
use pkcs8::EncodePublicKey;
let verifying_key = signing_key.verifying_key();
let spki_der = verifying_key.to_public_key_der().unwrap();
let trusted_key = crate::types::TrustedKey {
key_id,
algorithm: "Ed25519".to_string(),
public_key: BASE64.encode(spki_der.as_bytes()),
description: None,
added_at: None,
expires_at: None,
revoked: false,
};
let trust_store = TrustStore::new();
tokio::runtime::Runtime::new()
.unwrap()
.block_on(trust_store.add_pinned_key(&trusted_key))
.unwrap();
let fetch_result = FetchResult {
content: content.to_string(), headers: crate::types::PackHeaders {
digest: Some(compute_digest(content)),
signature: Some(BASE64.encode(serde_json::to_vec(&envelope).unwrap())),
key_id: envelope.signatures.first().map(|s| s.key_id.clone()),
etag: None,
cache_control: None,
content_length: None,
},
computed_digest: compute_digest(content),
};
let result = verify_pack(&fetch_result, &trust_store, &VerifyOptions::default());
assert!(
result.is_ok(),
"verify_pack should canonicalize content before DSSE verification: {:?}",
result
);
}
#[test]
fn test_verify_pack_canonicalization_equivalent_yaml_variants_contract() {
let seed: [u8; 32] = [0x90; 32];
let signing_key = keypair_from_seed(seed);
let source_yaml = "z: 3\na: 1\nm: 2";
let variant_yaml = "a: 1\nm: 2\nz: 3\n";
assert_eq!(compute_digest(source_yaml), compute_digest(variant_yaml));
let source_canonical = canonicalize_for_dsse(source_yaml).unwrap();
let variant_canonical = canonicalize_for_dsse(variant_yaml).unwrap();
assert_eq!(source_canonical, variant_canonical);
let changed_yaml = "a: 1\nm: 2\nz: 4\n";
assert_ne!(compute_digest(source_yaml), compute_digest(changed_yaml));
assert_ne!(
source_canonical,
canonicalize_for_dsse(changed_yaml).unwrap()
);
let (envelope, key_id) = create_signed_envelope(&signing_key, source_yaml);
use pkcs8::EncodePublicKey;
let verifying_key = signing_key.verifying_key();
let spki_der = verifying_key.to_public_key_der().unwrap();
let trusted_key = crate::types::TrustedKey {
key_id,
algorithm: "Ed25519".to_string(),
public_key: BASE64.encode(spki_der.as_bytes()),
description: None,
added_at: None,
expires_at: None,
revoked: false,
};
let trust_store = TrustStore::new();
tokio::runtime::Runtime::new()
.unwrap()
.block_on(trust_store.add_pinned_key(&trusted_key))
.unwrap();
let fetch_result = make_fetch_result(
variant_yaml,
Some(compute_digest(variant_yaml)),
Some(BASE64.encode(serde_json::to_vec(&envelope).unwrap())),
envelope.signatures.first().map(|s| s.key_id.clone()),
);
let result = verify_pack(&fetch_result, &trust_store, &VerifyOptions::default());
assert!(
result.is_ok(),
"canonical-equivalent YAML variant should verify: {:?}",
result
);
}
#[test]
fn test_canonical_bytes_differ_from_raw() {
let yaml = "z: 1\na: 2\nm: 3";
let raw_bytes = yaml.as_bytes();
let canonical_bytes = crate::canonicalize::to_canonical_jcs_bytes(
&crate::canonicalize::parse_yaml_strict(yaml).unwrap(),
)
.unwrap();
assert_ne!(
raw_bytes,
&canonical_bytes[..],
"Raw YAML and canonical JCS MUST differ for non-sorted keys"
);
let canonical_str = String::from_utf8(canonical_bytes).unwrap();
assert!(
canonical_str.starts_with(r#"{"a":"#),
"JCS must sort keys alphabetically, got: {}",
canonical_str
);
}
#[test]
fn test_verify_dsse_signature_bytes_directly() {
let seed: [u8; 32] = [0x99; 32];
let signing_key = keypair_from_seed(seed);
let content = "name: test\nversion: \"1.0.0\"";
let (envelope, key_id) = create_signed_envelope(&signing_key, content);
use pkcs8::EncodePublicKey;
let verifying_key = signing_key.verifying_key();
let spki_der = verifying_key.to_public_key_der().unwrap();
let trusted_key = crate::types::TrustedKey {
key_id,
algorithm: "Ed25519".to_string(),
public_key: BASE64.encode(spki_der.as_bytes()),
description: None,
added_at: None,
expires_at: None,
revoked: false,
};
let trust_store = TrustStore::new();
tokio::runtime::Runtime::new()
.unwrap()
.block_on(trust_store.add_pinned_key(&trusted_key))
.unwrap();
let canonical_bytes = crate::canonicalize::to_canonical_jcs_bytes(
&crate::canonicalize::parse_yaml_strict(content).unwrap(),
)
.unwrap();
let result = verify_dsse_signature_bytes(&canonical_bytes, &envelope, &trust_store);
assert!(
result.is_ok(),
"Canonical bytes verification should succeed: {:?}",
result
);
let raw_bytes = content.as_bytes();
let result = verify_dsse_signature_bytes(raw_bytes, &envelope, &trust_store);
assert!(
matches!(result, Err(RegistryError::DigestMismatch { .. })),
"Raw bytes should not match canonical payload: {:?}",
result
);
}
#[test]
fn test_verify_dsse_signature_legacy_helper_matches_bytes_path() {
let seed: [u8; 32] = [0x31; 32];
let signing_key = keypair_from_seed(seed);
let content = "z: 3\na: 1\nm: 2";
let (envelope, key_id) = create_signed_envelope(&signing_key, content);
use pkcs8::EncodePublicKey;
let verifying_key = signing_key.verifying_key();
let spki_der = verifying_key.to_public_key_der().unwrap();
let trusted_key = crate::types::TrustedKey {
key_id,
algorithm: "Ed25519".to_string(),
public_key: BASE64.encode(spki_der.as_bytes()),
description: None,
added_at: None,
expires_at: None,
revoked: false,
};
let trust_store = TrustStore::new();
tokio::runtime::Runtime::new()
.unwrap()
.block_on(trust_store.add_pinned_key(&trusted_key))
.unwrap();
let legacy_ok = verify_dsse_signature_legacy_for_tests(content, &envelope, &trust_store);
assert!(
legacy_ok.is_ok(),
"legacy helper should canonicalize input before verification: {:?}",
legacy_ok
);
let canonical_bytes = canonicalize_for_dsse(content).unwrap();
let bytes_ok = verify_dsse_signature_bytes(&canonical_bytes, &envelope, &trust_store);
assert!(
bytes_ok.is_ok(),
"bytes API should verify the same canonical payload: {:?}",
bytes_ok
);
let tampered_content = "z: 4\na: 1\nm: 2";
let legacy_err =
verify_dsse_signature_legacy_for_tests(tampered_content, &envelope, &trust_store);
assert!(
matches!(legacy_err, Err(RegistryError::DigestMismatch { .. })),
"legacy helper should keep mismatch classification: {:?}",
legacy_err
);
let tampered_canonical = canonicalize_for_dsse(tampered_content).unwrap();
let bytes_err = verify_dsse_signature_bytes(&tampered_canonical, &envelope, &trust_store);
assert!(
matches!(bytes_err, Err(RegistryError::DigestMismatch { .. })),
"bytes API should keep mismatch classification: {:?}",
bytes_err
);
}