use super::*;
use ring::rand::SystemRandom;
use ring::signature::{Ed25519KeyPair, KeyPair};
fn key() -> Ed25519KeyPair {
let bytes = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()).unwrap();
Ed25519KeyPair::from_pkcs8(bytes.as_ref()).unwrap()
}
fn hex(value: [u8; 32]) -> String {
value.iter().map(|byte| format!("{byte:02x}")).collect()
}
fn id(value: u8) -> String { hex([value; 32]) }
fn fixture(key_identity: [u8; 32], authority_identity: [u8; 32]) -> RendezvousManifestDocument {
let common = [id(1), id(2), id(3)];
RendezvousManifestDocument {
schema: SCHEMA.into(), signature_domain: DOMAIN.into(), usage: USAGE.into(),
algorithm: "Ed25519".into(), key_identity: hex(key_identity),
authority_identity: hex(authority_identity), five_leaf_v2_subject_identity: id(4),
five_leaf_v2_observation_identity: id(5), approved_lock_identity: id(6),
root_schema_identity: id(7), composer_schema_identity: id(8),
common_identities: common.clone(), owner_generation: 9,
leaves: LeavesDocument {
service: ServiceDocument { leaf_identity: id(10), common_identities: common.clone(),
owner_generation: 9, route_type_closure_identity: id(11),
routes: vec![ServiceRouteDocument { ordinal: 0, route_id: 12,
managed_commitment_bytes: 13, managed_objects_peak: 14,
db_connections: 1, db_operations: 1 }] },
runtime: RuntimeDocument { leaf_identity: id(15), common_identities: common.clone(),
owner_generation: 9, component_proof_identity: id(16),
expected_calibration_provenance: core::array::from_fn(|index| id(17 + index as u8)),
solver_schema_identity: id(26), admission_layout_identity: id(27),
build_costs: [[1; 7]; 4], support_limits: [1; 4], topology_constants: [1; 3],
routes: vec![RuntimeRouteDocument { ordinal: 0, route_id: 12,
framework_bytes: 28, task_storage_bytes: 29, response_carrier_bytes: 30 }] },
calibration: CalibrationDocument { leaf_identity: id(31), common_identities: common.clone(),
owner_generation: 9, provenance: core::array::from_fn(|index| id(32 + index as u8)),
fixed_costs: [1; 7], allocator_costs: [1; 2], calibrated_constants: [1; 3] },
admission: AdmissionDocument { leaf_identity: id(41), common_identities: common.clone(),
owner_generation: 9, solver_schema_identity: id(42), checked_relation_identity: id(43),
admission_layout_identity: id(44) },
observability: ObservabilityDocument { leaf_identity: id(45), common_identities: common.clone(),
owner_generation: 9, machine_identity: id(46),
component_identities: [id(47), id(48), id(49), id(50)], profile: [1; 6],
config_schema_identity: id(51), config_identity: id(52), config_values: [1; 5],
work_identity: id(53), sizes: [1; 4], alignments: [1; 4], offsets: [0, 1, 2, 3],
aggregate_layout: [4, 1], payload_machine_bytes: 1, source_identity: id(54),
build_identity: id(55), root_identity: id(56), constituent_identity: id(57),
queue_state_layout: QueueStateLayoutDocument { schema_identity: id(58),
type_identity: id(59), target_identity: id(60), size_bytes: 1,
alignment_bytes: 1, block_count: 1, reserve_bytes: 1,
authority_receipt_identity: id(61) } },
},
normalized: NormalizedDocument { costs: [[1; 7]; 5], allocator_costs: [1; 2],
support_limits: [1; 4], closure_constants: [1; 6],
routes: vec![NormalizedRouteDocument { ordinal: 0, route_id: 12,
managed_commitment_bytes: 13, managed_objects_peak: 14, framework_bytes: 28,
task_storage_bytes: 29, response_carrier_bytes: 30, db_connections: 1, db_operations: 1 }],
observability_layout_digest_values: [1; 20], root_identity: id(62),
costs_attestation_identity: id(63), support_attestation_identity: id(64),
route_set_attestation_identity: id(65) },
termination: TerminationDocument { leaf_identity: id(66), common_identities: common,
owner_generation: 9, topology: [1; 4], topology_identity: id(67),
db_normal_return_work_identity: id(68), writer_work_identity: id(69),
runtime_work_identity: id(70), composed_identity: id(71) },
constituent_commitments: CommitmentsDocument { service_capacity: id(72),
runtime_capacity: id(73), runtime_work: id(74), admission_relation: id(75),
db_normal_return_work: id(76), writer_work: id(77), observability: id(78) },
}
}
fn applicability(key: &Ed25519KeyPair) -> VerifiedRendezvousManifestKeyApplicabilityReceipt {
VerifiedRendezvousManifestKeyApplicabilityReceipt {
public_key: key.public_key().as_ref().try_into().unwrap(),
key_identity: sha256(key.public_key().as_ref()), authority_identity: [80; 32],
other_key_identities: core::array::from_fn(|index| [81 + index as u8; 32]),
other_authority_identities: core::array::from_fn(|index| [90 + index as u8; 32]),
domain: sha256(DOMAIN.as_bytes()), usage: sha256(USAGE.as_bytes()),
}
}
fn signed(document: &RendezvousManifestDocument, key: &Ed25519KeyPair) -> CanonicalSignedRendezvousManifestInput {
let canonical = serde_json::to_vec(document).unwrap();
let signature = key.sign(&canonical).as_ref().to_vec();
CanonicalSignedRendezvousManifestInput { canonical, signature }
}
#[test]
fn canonical_signed_structure_accepts_only_original_route_and_identity_shape() {
let signer = key();
let document = fixture(sha256(signer.public_key().as_ref()), [80; 32]);
assert!(verify_rendezvous_manifest_structural_preflight(applicability(&signer), signed(&document, &signer)).is_ok());
let mut drift = fixture(sha256(signer.public_key().as_ref()), [80; 32]);
drift.leaves.runtime.routes[0].route_id += 1;
assert_eq!(verify_rendezvous_manifest_structural_preflight(applicability(&signer), signed(&drift, &signer)).unwrap_err().0, RendezvousManifestPreflightError::Shape);
let mut uppercase = fixture(sha256(signer.public_key().as_ref()), [80; 32]);
uppercase.leaves.service.leaf_identity = "AA".repeat(32);
assert_eq!(verify_rendezvous_manifest_structural_preflight(applicability(&signer), signed(&uppercase, &signer)).unwrap_err().0, RendezvousManifestPreflightError::Shape);
}
#[test]
fn noncanonical_and_foreign_signature_fail_closed() {
let signer = key();
let document = fixture(sha256(signer.public_key().as_ref()), [80; 32]);
let mut input = signed(&document, &signer); input.canonical.push(b' ');
assert_eq!(verify_rendezvous_manifest_structural_preflight(applicability(&signer), input).unwrap_err().0, RendezvousManifestPreflightError::Encoding);
let foreign = key();
assert_eq!(verify_rendezvous_manifest_structural_preflight(applicability(&signer), signed(&document, &foreign)).unwrap_err().0, RendezvousManifestPreflightError::Signature);
}
#[test]
fn applicability_rejects_duplicate_key_and_authority_before_parsing() {
let applicability = VerifiedRendezvousManifestKeyApplicabilityReceipt {
public_key: [1; 32], key_identity: sha256(&[1; 32]), authority_identity: [2; 32],
other_key_identities: [sha256(&[1; 32]); 8], other_authority_identities: [[2; 32]; 8],
domain: sha256(DOMAIN.as_bytes()), usage: sha256(USAGE.as_bytes()),
};
let input = CanonicalSignedRendezvousManifestInput { canonical: b"{}".to_vec(), signature: vec![0; 64] };
assert_eq!(verify_rendezvous_manifest_structural_preflight(applicability, input).unwrap_err().0, RendezvousManifestPreflightError::Foreign);
}