extern crate alloc;
use alloc::string::{String, ToString};
use chrono::{DateTime, TimeZone, Utc};
use crate::card::{weights_hash_of, ModelCard, StringSet, CARD_VERSION_V1};
use crate::error::WeightsError;
fn fixture_card(issued_at: DateTime<Utc>, expires_at: DateTime<Utc>) -> ModelCard {
match ModelCard::new(
"0000000000000000000000000000000000000000000000000000000000000001",
StringSet::default(),
StringSet::default(),
"public-internet",
"https://example.com/issuer",
issued_at,
expires_at,
) {
Ok(card) => card,
Err(_) => {
unreachable!("fixture_card constants satisfy ModelCard::new's validate() invariants")
}
}
}
#[kani::proof]
#[kani::unwind(8)]
pub fn public_weights_hash_of_determinism_and_tampering() {
let bytes: [u8; 4] = kani::any();
let flip_index: u8 = kani::any();
kani::assume((flip_index as usize) < bytes.len());
let first = weights_hash_of(&bytes);
let second = weights_hash_of(&bytes);
assert_eq!(first, second);
assert_eq!(first.len(), 64);
for byte in first.as_bytes() {
assert!(matches!(*byte, b'0'..=b'9' | b'a'..=b'f'));
}
let mut tampered = bytes;
tampered[flip_index as usize] ^= 0x01;
let tampered_digest = weights_hash_of(&tampered);
assert_ne!(first, tampered_digest);
}
#[kani::proof]
#[kani::unwind(8)]
pub fn public_model_card_require_live_fail_closed() {
let issued_at = match Utc.with_ymd_and_hms(2026, 4, 30, 12, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!("fixed issued_at fixture must construct"),
};
let expires_at = match Utc.with_ymd_and_hms(2026, 5, 30, 12, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!("fixed expires_at fixture must construct"),
};
let card = fixture_card(issued_at, expires_at);
let bucket: u8 = kani::any();
kani::assume(bucket < 3);
let now = match bucket {
0 => match Utc.with_ymd_and_hms(2026, 5, 1, 12, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!("fixed live `now` fixture must construct"),
},
1 => expires_at,
_ => match Utc.with_ymd_and_hms(2026, 6, 30, 12, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!("fixed expired `now` fixture must construct"),
},
};
let result = card.require_live(now);
if now < expires_at {
assert!(result.is_ok());
} else {
assert!(matches!(result, Err(WeightsError::Expired { .. })));
}
}
#[kani::proof]
#[kani::unwind(8)]
pub fn public_weights_error_urn_is_stable() {
let pick: u8 = kani::any();
kani::assume(pick < 8);
let err: WeightsError = match pick {
0 => WeightsError::Encoding(String::new()),
1 => WeightsError::MissingField("training_data_class"),
2 => WeightsError::SchemaRejected(String::new()),
3 => {
let expires_at = match Utc.with_ymd_and_hms(2026, 5, 1, 0, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!(),
};
let now = match Utc.with_ymd_and_hms(2026, 6, 1, 0, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!(),
};
WeightsError::Expired { expires_at, now }
}
4 => WeightsError::BundleRejected(String::new()),
5 => WeightsError::CardMismatch {
expected: String::new(),
found: String::new(),
},
6 => WeightsError::ScopeNotSubset {
scope: String::new(),
},
7 => WeightsError::ToolBanned {
tool: String::new(),
},
_ => unreachable!("pick is bounded by kani::assume above"),
};
let urn = err.urn();
assert!(urn.starts_with("urn:chio:error:weights:"));
let expected = match pick {
0 => "urn:chio:error:weights:internal-encoding",
1 => "urn:chio:error:weights:schema-rejected",
2 => "urn:chio:error:weights:schema-rejected",
3 => "urn:chio:error:weights:card-expired",
4 => "urn:chio:error:weights:bundle-rejected",
5 => "urn:chio:error:weights:card-mismatch",
6 => "urn:chio:error:weights:scope-not-subset",
7 => "urn:chio:error:weights:tool-banned",
_ => unreachable!(),
};
assert_eq!(urn, expected);
}
#[kani::proof]
#[kani::unwind(8)]
pub fn public_model_card_new_pins_schema_version() {
let issued_at = match Utc.with_ymd_and_hms(2026, 4, 30, 12, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!("fixed issued_at fixture must construct"),
};
let expires_at = match Utc.with_ymd_and_hms(2026, 5, 30, 12, 0, 0) {
chrono::LocalResult::Single(t) => t,
_ => unreachable!("fixed expires_at fixture must construct"),
};
let card = fixture_card(issued_at, expires_at);
assert_eq!(card.card_version, CARD_VERSION_V1.to_string());
assert!(card.validate().is_ok());
assert!(card.expires_at >= card.issued_at);
}