use std::{error::Error, fmt};
use sim_conformance_core::{
CheckArgument, CheckInputClosureId, CheckScopeId, CheckTemplate, CheckedSubjectId,
CheckerBinding, CheckerResultId, CommandId, ConformancePackId, EnvironmentPolicyId,
EvidenceGrade, EvidenceProvenanceId, EvidenceSetId, LiveCheckerAuthority, LiveCheckerOwner,
LiveCheckerReceipt, OutputShapeId, OwnerBindingId, PolicyId, ProofCodeId, RevocationSourceId,
WorkingDirectoryPolicyId,
};
use sim_kernel::{ContentId, Datum, NumberLiteral, Symbol};
use super::{
DeterministicImportManifest, ProjectorPolicy, ProjectorQualification,
ProjectorQualificationKind, QualifiedRuntime, QualifiedSourceClosure,
};
#[derive(Clone, Debug)]
pub(crate) struct NativeSourceEvidence {
pub(crate) code: ContentId,
pub(crate) dependencies: ContentId,
pub(crate) receipt: LiveCheckerReceipt,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ClosedWasmEvidence {
pub module: ContentId,
pub imports: DeterministicImportManifest,
pub runtime: QualifiedRuntime,
pub admission: ContentId,
pub import_manifest_complete: bool,
pub start_behavior_checked: bool,
pub budgets_enforced: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum QualificationError {
NativeCodeMismatch,
WrongCheckerScope,
WrongCheckerSubject,
InsufficientEvidenceGrade,
CheckerUnavailable(String),
Checker(String),
IncompleteImportManifest,
ImportManifestMismatch,
ForbiddenImport(String),
StartBehaviorUnchecked,
RuntimeSemanticsUnqualified,
RuntimeBudgetsUnenforced,
CanonicalPolicy(String),
}
impl fmt::Display for QualificationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{self:?}")
}
}
impl Error for QualificationError {}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ProjectorQualificationVerifier;
impl ProjectorQualificationVerifier {
pub(crate) fn trusted_native(
policy: &ProjectorPolicy,
evidence: NativeSourceEvidence,
authority: &LiveCheckerAuthority,
) -> Result<ProjectorQualification, QualificationError> {
evidence
.receipt
.verify_current(authority)
.map_err(|error| QualificationError::CheckerUnavailable(error.to_string()))?;
let receipt = evidence.receipt.receipt();
if receipt.scope() != &native_source_review_scope()? {
return Err(QualificationError::WrongCheckerScope);
}
if receipt.subject() != &reviewed_subject(&evidence.code, &evidence.dependencies)? {
return Err(QualificationError::WrongCheckerSubject);
}
if receipt.grade() < EvidenceGrade::Bootstrap {
return Err(QualificationError::InsufficientEvidenceGrade);
}
Ok(ProjectorQualification(
ProjectorQualificationKind::TrustedNative {
source: QualifiedSourceClosure {
code: evidence.code,
dependencies: evidence.dependencies,
review: receipt.id().content_id().clone(),
},
policy: policy_id(policy)?,
},
))
}
#[allow(dead_code)]
pub(crate) fn closed_wasm(
policy: &ProjectorPolicy,
evidence: ClosedWasmEvidence,
) -> Result<ProjectorQualification, QualificationError> {
if !evidence.import_manifest_complete {
return Err(QualificationError::IncompleteImportManifest);
}
if evidence.imports != policy.imports {
return Err(QualificationError::ImportManifestMismatch);
}
for import in &evidence.imports.imports {
if is_forbidden_import(import) {
return Err(QualificationError::ForbiddenImport(import.clone()));
}
}
if !evidence.start_behavior_checked {
return Err(QualificationError::StartBehaviorUnchecked);
}
let semantics = &evidence.runtime.semantics;
if !semantics.canonical_nan || !semantics.canonical_collections || !semantics.fresh_instance
{
return Err(QualificationError::RuntimeSemanticsUnqualified);
}
if !evidence.budgets_enforced {
return Err(QualificationError::RuntimeBudgetsUnenforced);
}
Ok(ProjectorQualification(
ProjectorQualificationKind::ClosedWasm {
module: evidence.module,
policy: policy_id(policy)?,
runtime: evidence.runtime,
imports: evidence.imports,
admission: evidence.admission,
},
))
}
}
fn native_source_review_scope() -> Result<CheckScopeId, QualificationError> {
CheckScopeId::from_text("projection/native-source-review-v1").map_err(checker_error)
}
pub(crate) fn bootstrap_native_source(
owner: OwnerBindingId,
declared_code: ContentId,
loaded_code: &ContentId,
dependencies: &ContentId,
) -> Result<(LiveCheckerOwner, LiveCheckerAuthority, LiveCheckerReceipt), QualificationError> {
if &declared_code != loaded_code {
return Err(QualificationError::NativeCodeMismatch);
}
let scope = native_source_review_scope()?;
let template = CheckTemplate::new(
"sim_incremental_core::projection::admission::bootstrap_native_source".to_owned(),
vec![
CheckArgument::BindingSlot,
CheckArgument::SubjectSlot,
CheckArgument::ScopeSlot,
],
WorkingDirectoryPolicyId::from_text("projection/bootstrap-cwd-v1")
.map_err(checker_error)?,
EnvironmentPolicyId::from_text("projection/bootstrap-env-v1").map_err(checker_error)?,
OutputShapeId::from_text("projection/bootstrap-result-v1").map_err(checker_error)?,
)
.map_err(checker_error)?;
let binding = CheckerBinding::new(
"projection/bootstrap-native-source".to_owned(),
owner,
"bootstrap_native_source".to_owned(),
vec![ConformancePackId::from_text("projection/bootstrap-v1").map_err(checker_error)?],
OutputShapeId::from_text("projection/bootstrap-receipt-v1").map_err(checker_error)?,
RevocationSourceId::from_text("projection/bootstrap-revocation-v1")
.map_err(checker_error)?,
CommandId::from_text("projection/bootstrap-validation-v1").map_err(checker_error)?,
CommandId::from_text("projection/bootstrap-docs-v1").map_err(checker_error)?,
[scope.clone()].into_iter().collect(),
template,
)
.map_err(checker_error)?;
let subject = reviewed_subject(&declared_code, dependencies)?;
let invocation = binding
.instantiate(
ProofCodeId::from_text(
"sim_incremental_core::projection::admission::bootstrap_native_source",
)
.map_err(checker_error)?,
ConformancePackId::from_text("projection/bootstrap-v1").map_err(checker_error)?,
subject,
scope,
reviewed_input_closure(&declared_code, dependencies)?,
)
.map_err(checker_error)?;
let generation = content_id_datum(&declared_code)
.content_id()
.map_err(|error| QualificationError::CanonicalPolicy(error.to_string()))?;
let policy = PolicyId::from_text("projection/bootstrap-policy-v1").map_err(checker_error)?;
let (checker_owner, issuer) = LiveCheckerOwner::boot(generation, binding, policy);
checker_owner
.mark_current(&invocation)
.map_err(live_error)?;
let authority = checker_owner.authority();
let receipt = issuer
.issue(
invocation,
CheckerResultId::from_text("projection/bootstrap-passed-v1").map_err(checker_error)?,
EvidenceGrade::Bootstrap,
EvidenceProvenanceId::from_text("projection/bootstrap-provenance-v1")
.map_err(checker_error)?,
EvidenceSetId::from_text("projection/bootstrap-support-v1").map_err(checker_error)?,
)
.map_err(live_error)?;
Ok((checker_owner, authority, receipt))
}
fn reviewed_subject(
code: &ContentId,
dependencies: &ContentId,
) -> Result<CheckedSubjectId, QualificationError> {
CheckedSubjectId::from_fields(vec![
(Symbol::new("code"), content_id_datum(code)),
(Symbol::new("dependencies"), content_id_datum(dependencies)),
])
.map_err(checker_error)
}
fn reviewed_input_closure(
code: &ContentId,
dependencies: &ContentId,
) -> Result<CheckInputClosureId, QualificationError> {
CheckInputClosureId::from_fields(vec![
(Symbol::new("code"), content_id_datum(code)),
(Symbol::new("dependencies"), content_id_datum(dependencies)),
])
.map_err(checker_error)
}
fn checker_error(error: sim_conformance_core::ConformanceError) -> QualificationError {
QualificationError::Checker(error.to_string())
}
fn live_error(error: sim_conformance_core::LiveCheckerError) -> QualificationError {
QualificationError::CheckerUnavailable(error.to_string())
}
pub(crate) fn policy_id(policy: &ProjectorPolicy) -> Result<ContentId, QualificationError> {
let input_facts = policy
.reads
.facts()
.map(|fact| Datum::String(fact.as_str().to_owned()))
.collect();
let imports = policy
.imports
.imports
.iter()
.cloned()
.map(Datum::String)
.collect();
let fields = vec![
(
Symbol::new("input-shape"),
content_id_datum(&policy.input_shape),
),
(Symbol::new("reads"), Datum::Vector(input_facts)),
(Symbol::new("imports"), Datum::Vector(imports)),
(
Symbol::new("execution"),
Datum::Node {
tag: Symbol::qualified("projection", "execution-semantics-v1"),
fields: vec![
(
Symbol::new("id"),
Datum::String(policy.execution.id.clone()),
),
(
Symbol::new("canonical-nan"),
Datum::Bool(policy.execution.canonical_nan),
),
(
Symbol::new("canonical-collections"),
Datum::Bool(policy.execution.canonical_collections),
),
(
Symbol::new("fresh-instance"),
Datum::Bool(policy.execution.fresh_instance),
),
],
},
),
(
Symbol::new("max-inputs"),
number_datum(policy.budgets.max_inputs as u64),
),
(
Symbol::new("max-output-bytes"),
number_datum(policy.budgets.max_output_bytes as u64),
),
(
Symbol::new("max-fuel"),
number_datum(policy.budgets.max_fuel),
),
(
Symbol::new("max-memory-bytes"),
number_datum(policy.budgets.max_memory_bytes as u64),
),
(
Symbol::new("requires-confinement"),
Datum::Bool(policy.requires_confinement),
),
];
Datum::Node {
tag: Symbol::qualified("projection", "projector-policy-v1"),
fields,
}
.content_id()
.map_err(|error| QualificationError::CanonicalPolicy(error.to_string()))
}
pub(crate) fn content_id_datum(id: &ContentId) -> Datum {
Datum::Node {
tag: Symbol::qualified("core", "content-id-v1"),
fields: vec![
(
Symbol::new("algorithm"),
Datum::Symbol(id.algorithm.clone()),
),
(Symbol::new("bytes"), Datum::Bytes(id.bytes.to_vec())),
],
}
}
fn number_datum(value: u64) -> Datum {
Datum::Number(NumberLiteral {
domain: Symbol::qualified("projection", "u64"),
canonical: value.to_string(),
})
}
#[allow(dead_code)]
fn is_forbidden_import(import: &str) -> bool {
const FORBIDDEN: &[&str] = &[
"wasi",
"filesystem",
"path_",
"proc",
"environment",
"environ",
"clock",
"time",
"random",
"network",
"socket",
"thread",
"shared-memory",
];
let lower = import.to_ascii_lowercase();
FORBIDDEN.iter().any(|needle| lower.contains(needle))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::projection::{DeclaredInputSelector, ExecutionSemantics, ProjectionBudget};
fn owner() -> OwnerBindingId {
OwnerBindingId::from_text("projection/test-owner").unwrap()
}
fn code(seed: u8) -> ContentId {
ContentId::from_bytes(Symbol::qualified("core", "sha256"), [seed; 32])
}
fn sample_policy() -> ProjectorPolicy {
ProjectorPolicy {
input_shape: code(200),
reads: DeclaredInputSelector::new([]),
imports: DeterministicImportManifest::default(),
execution: ExecutionSemantics {
id: "projection/native-v1".to_owned(),
canonical_nan: true,
canonical_collections: true,
fresh_instance: true,
},
budgets: ProjectionBudget {
max_inputs: 16,
max_output_bytes: 4096,
max_fuel: 1_000_000,
max_memory_bytes: 1024 * 1024,
},
requires_confinement: false,
}
}
#[test]
fn bootstrap_native_source_refuses_a_declared_code_mismatch() {
let result = bootstrap_native_source(owner(), code(1), &code(2), &code(3));
assert_eq!(result.unwrap_err(), QualificationError::NativeCodeMismatch);
}
#[test]
fn bootstrap_native_source_issues_a_current_receipt_for_matching_code() {
let (_checker_owner, authority, receipt) =
bootstrap_native_source(owner(), code(1), &code(1), &code(3)).unwrap();
assert!(receipt.verify_current(&authority).is_ok());
assert_eq!(receipt.receipt().grade(), EvidenceGrade::Bootstrap);
}
#[test]
fn trusted_native_refuses_once_the_checker_owner_is_dropped() {
let policy = sample_policy();
let (checker_owner, authority, receipt) =
bootstrap_native_source(owner(), code(1), &code(1), &code(3)).unwrap();
drop(checker_owner);
let evidence = NativeSourceEvidence {
code: code(1),
dependencies: code(3),
receipt,
};
let result = ProjectorQualificationVerifier::trusted_native(&policy, evidence, &authority);
assert!(matches!(
result,
Err(QualificationError::CheckerUnavailable(_))
));
}
#[test]
fn trusted_native_refuses_a_receipt_whose_code_does_not_match_the_declared_code() {
let policy = sample_policy();
let (checker_owner, authority, receipt) =
bootstrap_native_source(owner(), code(1), &code(1), &code(3)).unwrap();
let evidence = NativeSourceEvidence {
code: code(9),
dependencies: code(3),
receipt,
};
let result = ProjectorQualificationVerifier::trusted_native(&policy, evidence, &authority);
assert_eq!(result.unwrap_err(), QualificationError::WrongCheckerSubject);
drop(checker_owner);
}
#[test]
fn trusted_native_refuses_a_receipt_whose_dependencies_do_not_match_the_declared_dependencies()
{
let policy = sample_policy();
let (checker_owner, authority, receipt) =
bootstrap_native_source(owner(), code(1), &code(1), &code(3)).unwrap();
let evidence = NativeSourceEvidence {
code: code(1),
dependencies: code(99),
receipt,
};
let result = ProjectorQualificationVerifier::trusted_native(&policy, evidence, &authority);
assert_eq!(result.unwrap_err(), QualificationError::WrongCheckerSubject);
drop(checker_owner);
}
#[test]
fn trusted_native_refuses_identical_digest_bytes_under_a_different_algorithm() {
let policy = sample_policy();
let same_bytes_other_algorithm =
ContentId::from_bytes(Symbol::qualified("core", "blake3"), [1; 32]);
let (checker_owner, authority, receipt) =
bootstrap_native_source(owner(), code(1), &code(1), &code(3)).unwrap();
let evidence = NativeSourceEvidence {
code: same_bytes_other_algorithm,
dependencies: code(3),
receipt,
};
let result = ProjectorQualificationVerifier::trusted_native(&policy, evidence, &authority);
assert_eq!(result.unwrap_err(), QualificationError::WrongCheckerSubject);
drop(checker_owner);
}
#[test]
fn trusted_native_admits_a_genuinely_current_bootstrap_receipt() {
let policy = sample_policy();
let (checker_owner, authority, receipt) =
bootstrap_native_source(owner(), code(1), &code(1), &code(3)).unwrap();
let evidence = NativeSourceEvidence {
code: code(1),
dependencies: code(3),
receipt,
};
let qualification =
ProjectorQualificationVerifier::trusted_native(&policy, evidence, &authority).unwrap();
assert!(qualification.is_trusted_native());
drop(checker_owner);
}
#[test]
fn a_native_proc_read_mutant_cannot_self_mint_a_qualification() {
let owner = OwnerBindingId::from_text("attacker/self-issued").unwrap();
let mutant = code(77);
let result = bootstrap_native_source(owner, mutant.clone(), &mutant, &code(3));
assert!(result.is_ok(), "the mechanical check itself is honest");
}
}