use std::sync::atomic::{AtomicU64, Ordering};
use ring::signature::{ED25519, UnparsedPublicKey};
use serde::Deserialize;
use sha2::{Digest, Sha256};
static LAST_DEPLOYMENT_GENERATION: AtomicU64 = AtomicU64::new(0);
static FINAL_PAIR_NONCE: AtomicU64 = AtomicU64::new(1);
const DOMAIN: &str = "saddle/f08/log-directory/v1";
const USAGE: &str = "observability.log-directory.identity";
const SCHEDULER_DOMAIN: &str = "saddle/f08/scheduler-observation/v1";
const SCHEDULER_USAGE: &str = "runtime.scheduler.observation";
const SUPERVISOR_DOMAIN: &str = "saddle/f08/supervisor-startup-applicability/v1";
const SUPERVISOR_USAGE: &str = "runtime.supervisor.startup-applicability";
const FILESYSTEM_BUDGET_DOMAIN: &str = "saddle/f08/filesystem-service-budget/v1";
const FILESYSTEM_BUDGET_USAGE: &str = "observability.filesystem-service-budget";
const FIVE_LEAF_SIGNER_APPROVAL_DOMAIN: &str = "saddle/f08/five-leaf-v2-signer-approval/v1";
const FIVE_LEAF_SIGNER_APPROVAL_USAGE: &str = "capacity.five-leaf.generation.approval";
const FIVE_LEAF_SIGNING_REQUEST_NONCE_DOMAIN: &[u8] =
b"saddle/f08/five-leaf-v2-signing-request-nonce/v1";
pub struct VerifiedReceiptIssuerTrustAnchorOwner {
policy_public_key: [u8; 32],
time_public_key: [u8; 32],
revocation_public_key: [u8; 32],
scheduler_public_key: [u8; 32],
supervisor_public_key: [u8; 32],
filesystem_budget_public_key: [u8; 32],
generation_approval_public_key: [u8; 32],
external_calibration_public_key: [u8; 32],
policy_authority: [u8; 32],
time_authority: [u8; 32],
revocation_authority: [u8; 32],
scheduler_authority: [u8; 32],
supervisor_authority: [u8; 32],
filesystem_budget_authority: [u8; 32],
generation_approval_authority: [u8; 32],
external_calibration_authority: [u8; 32],
filesystem_target: [u8; 32],
filesystem_service: [u8; 32],
filesystem_identity: [u8; 32],
filesystem_mount: [u8; 32],
approved_gate: [u8; 32],
provider: [u8; 32],
application: [u8; 32],
build: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
generation_observation: [u8; 32],
generation_approved_lock: [u8; 32],
generation_source: [u8; 32],
generation_machine: [u8; 32],
generation_root: [u8; 32],
generation_custody_policy: [u8; 32],
generation: u64,
minimum_revocation_epoch: u64,
}
pub struct TrustedDeploymentPolicyReceipt {
scheduler_half: Option<SchedulerObservationPairingHalf>,
supervisor_half: Option<SupervisorStartupPairingHalf>,
#[allow(dead_code)]
filesystem_budget_half: Option<FilesystemServiceBudgetPairingHalf>,
issuance_nonce: [u8; 32],
policy_identity: [u8; 32],
gate: [u8; 32],
public_key: [u8; 32],
key_identity: [u8; 32],
algorithm: [u8; 32],
signature_domain: [u8; 32],
usage: [u8; 32],
provider: [u8; 32],
application: [u8; 32],
binary: [u8; 32],
build: [u8; 32],
source: [u8; 32],
artifact: [u8; 32],
routes: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
nonce: [u8; 32],
calibration: [u8; 32],
not_before: u64,
not_after: u64,
}
pub struct TrustedTimeReceipt {
issuance_nonce: [u8; 32],
clock: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
generation: u64,
verified_unix_seconds: u64,
}
pub struct RevocationSnapshotReceipt {
issuance_nonce: [u8; 32],
authority: [u8; 32],
key_identity: [u8; 32],
epoch: u64,
list_identity: [u8; 32],
deployment: [u8; 32],
generation: u64,
revoked: bool,
}
pub struct VerifiedDeploymentBundleApplicabilityOwner {
#[allow(dead_code)]
gate_build_half: Option<GateBuildPairingHalf>,
#[allow(dead_code)]
scheduler_observation_half: Option<SchedulerObservationPairingHalf>,
supervisor_startup_half: Option<SupervisorStartupPairingHalf>,
#[allow(dead_code)]
filesystem_budget_half: Option<FilesystemServiceBudgetPairingHalf>,
startup_time_policy_half: Option<StartupTimePolicyPairingHalf>,
split_seed: Option<DeploymentBindingSeed>,
generation_half: Option<crate::VerifiedSignedDeploymentGenerationOwner>,
bundle: [u8; 32],
public_key: [u8; 32],
key: [u8; 32],
provider: [u8; 32],
application: [u8; 32],
binary: [u8; 32],
build: [u8; 32],
source: [u8; 32],
artifact: [u8; 32],
routes: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
nonce: [u8; 32],
calibration: [u8; 32],
generation: u64,
revocation_epoch: u64,
}
struct SchedulerObservationPairingHalf {
issuance_nonce: [u8; 32],
observation: [u8; 32],
receipt: [u8; 32],
policy: [u8; 32],
calibration: [u8; 32],
binary: [u8; 32],
source: [u8; 32],
artifact: [u8; 32],
routes: [u8; 32],
gate: [u8; 32],
build: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
generation: u64,
key: [u8; 32],
revocation_epoch: u64,
revocation_list: [u8; 32],
}
struct SupervisorStartupPairingHalf {
issuance_nonce: [u8; 32],
applicability: [u8; 32],
policy: [u8; 32],
calibration: [u8; 32],
gate: [u8; 32],
build: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
generation: u64,
clock: [u8; 32],
signal_delivery_bound: [u8; 32],
hard_kill_enforcement: [u8; 32],
post_driver_mechanism: [u8; 32],
post_driver_install_applicability: [u8; 32],
key: [u8; 32],
revocation_epoch: u64,
revocation_list: [u8; 32],
}
#[allow(dead_code)]
struct FilesystemServiceBudgetPairingHalf {
issuance_nonce: [u8; 32],
budget: [u8; 32],
policy: [u8; 32],
target: [u8; 32],
calibration: [u8; 32],
service: [u8; 32],
build: [u8; 32],
environment: [u8; 32],
filesystem: [u8; 32],
mount: [u8; 32],
deployment: [u8; 32],
generation: u64,
key: [u8; 32],
revocation_epoch: u64,
revocation_list: [u8; 32],
}
pub struct VerifiedFilesystemServiceBudgetAuthoritySeed {
#[allow(dead_code)]
half: FilesystemServiceBudgetPairingHalf,
}
pub struct VerifiedDeploymentSupervisorStartupSeed {
#[allow(dead_code)]
half: SupervisorStartupPairingHalf,
}
pub struct VerifiedDeploymentSupervisorStartupOwner {
half: SupervisorStartupPairingHalf,
}
fn pair_supervisor_startup_seed(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
seed: VerifiedDeploymentSupervisorStartupSeed,
) -> Result<
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentSupervisorStartupOwner,
),
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentSupervisorStartupSeed,
),
> {
let Some(half) = owner.supervisor_startup_half.take() else {
return Err((owner, seed));
};
if half.issuance_nonce != seed.half.issuance_nonce
|| half.applicability != seed.half.applicability
|| half.policy != seed.half.policy
|| half.calibration != seed.half.calibration
|| half.gate != seed.half.gate
|| half.build != seed.half.build
|| half.deployment != seed.half.deployment
|| half.environment != seed.half.environment
|| half.generation != seed.half.generation
|| half.clock != seed.half.clock
|| half.signal_delivery_bound != seed.half.signal_delivery_bound
|| half.hard_kill_enforcement != seed.half.hard_kill_enforcement
|| half.post_driver_mechanism != seed.half.post_driver_mechanism
|| half.post_driver_install_applicability != seed.half.post_driver_install_applicability
|| half.key != seed.half.key
|| half.revocation_epoch != seed.half.revocation_epoch
|| half.revocation_list != seed.half.revocation_list
{
owner.supervisor_startup_half = Some(half);
return Err((owner, seed));
}
Ok((owner, VerifiedDeploymentSupervisorStartupOwner { half }))
}
fn restore_supervisor_startup_seed(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
supervisor: VerifiedDeploymentSupervisorStartupOwner,
) -> (
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentSupervisorStartupSeed,
) {
debug_assert!(owner.supervisor_startup_half.is_none());
let half = supervisor.half;
let seed = VerifiedDeploymentSupervisorStartupSeed {
half: SupervisorStartupPairingHalf { ..half },
};
owner.supervisor_startup_half = Some(half);
(owner, seed)
}
pub struct VerifiedDeploymentSchedulerObservationSeed {
#[allow(dead_code)]
half: SchedulerObservationPairingHalf,
}
pub struct VerifiedDeploymentSchedulerObservationOwner {
half: SchedulerObservationPairingHalf,
}
fn pair_scheduler_observation_seed(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
seed: VerifiedDeploymentSchedulerObservationSeed,
) -> Result<
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentSchedulerObservationOwner,
),
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentSchedulerObservationSeed,
),
> {
let Some(half) = owner.scheduler_observation_half.take() else {
return Err((owner, seed));
};
if half.issuance_nonce != seed.half.issuance_nonce
|| half.observation != seed.half.observation
|| half.receipt != seed.half.receipt
|| half.policy != seed.half.policy
|| half.calibration != seed.half.calibration
|| half.binary != seed.half.binary
|| half.source != seed.half.source
|| half.artifact != seed.half.artifact
|| half.routes != seed.half.routes
|| half.gate != seed.half.gate
|| half.build != seed.half.build
|| half.deployment != seed.half.deployment
|| half.environment != seed.half.environment
|| half.generation != seed.half.generation
|| half.key != seed.half.key
|| half.revocation_epoch != seed.half.revocation_epoch
|| half.revocation_list != seed.half.revocation_list
{
owner.scheduler_observation_half = Some(half);
return Err((owner, seed));
}
Ok((owner, VerifiedDeploymentSchedulerObservationOwner { half }))
}
fn restore_scheduler_observation_seed(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
scheduler: VerifiedDeploymentSchedulerObservationOwner,
) -> (
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentSchedulerObservationSeed,
) {
let half = scheduler.half;
owner.scheduler_observation_half = Some(SchedulerObservationPairingHalf { ..half });
(owner, VerifiedDeploymentSchedulerObservationSeed { half })
}
struct GateBuildPairingHalf {
issuance_nonce: [u8; 32],
gate: [u8; 32],
build: [u8; 32],
environment: [u8; 32],
deployment: [u8; 32],
generation: u64,
policy: [u8; 32],
key: [u8; 32],
revocation_epoch: u64,
revocation_list: [u8; 32],
}
pub struct VerifiedDeploymentGateBuildAuthoritySeed {
#[allow(dead_code)]
half: GateBuildPairingHalf,
}
pub struct VerifiedDeploymentGateBuildOwner {
half: GateBuildPairingHalf,
}
fn pair_gate_build_seed(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
seed: VerifiedDeploymentGateBuildAuthoritySeed,
) -> Result<
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentGateBuildOwner,
),
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentGateBuildAuthoritySeed,
),
> {
let Some(half) = owner.gate_build_half.take() else {
return Err((owner, seed));
};
if half.issuance_nonce != seed.half.issuance_nonce
|| half.gate != seed.half.gate
|| half.build != seed.half.build
|| half.environment != seed.half.environment
|| half.deployment != seed.half.deployment
|| half.generation != seed.half.generation
|| half.policy != seed.half.policy
|| half.key != seed.half.key
|| half.revocation_epoch != seed.half.revocation_epoch
|| half.revocation_list != seed.half.revocation_list
{
owner.gate_build_half = Some(half);
return Err((owner, seed));
}
Ok((owner, VerifiedDeploymentGateBuildOwner { half }))
}
fn restore_gate_build_seed(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
receipt: VerifiedDeploymentGateBuildOwner,
) -> (
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentGateBuildAuthoritySeed,
) {
let half = receipt.half;
owner.gate_build_half = Some(GateBuildPairingHalf { ..half });
(owner, VerifiedDeploymentGateBuildAuthoritySeed { half })
}
struct StartupTimePolicyPairingHalf {
issuance_nonce: [u8; 32],
policy: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
build: [u8; 32],
generation: u64,
not_before: u64,
not_after: u64,
revocation_epoch: u64,
revocation_list: [u8; 32],
}
pub struct VerifiedDeploymentStartupAuthoritySeed {
half: StartupTimePolicyPairingHalf,
request_timeout_ms: u64,
shutdown_grace_ms: u64,
}
pub struct VerifiedStartupTimePolicyOwner {
issuance_nonce: [u8; 32],
request_timeout_ms: u64,
shutdown_grace_ms: u64,
policy: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
build: [u8; 32],
generation: u64,
not_before: u64,
not_after: u64,
revocation_epoch: u64,
revocation_list: [u8; 32],
}
type StartupTimePolicyPairFailure = (
DeploymentApplicabilityError,
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentStartupAuthoritySeed,
);
#[allow(clippy::result_large_err)]
fn pair_startup_time_policy(
mut applicability: VerifiedDeploymentBundleApplicabilityOwner,
seed: VerifiedDeploymentStartupAuthoritySeed,
) -> Result<
(
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedStartupTimePolicyOwner,
),
StartupTimePolicyPairFailure,
> {
let Some(half) = applicability.startup_time_policy_half.take() else {
return Err((
DeploymentApplicabilityError::ReplayOrStaleGeneration,
applicability,
seed,
));
};
if half.issuance_nonce != seed.half.issuance_nonce
|| half.policy != seed.half.policy
|| half.deployment != seed.half.deployment
|| half.environment != seed.half.environment
|| half.build != seed.half.build
|| half.generation != seed.half.generation
|| half.not_before != seed.half.not_before
|| half.not_after != seed.half.not_after
|| half.revocation_epoch != seed.half.revocation_epoch
|| half.revocation_list != seed.half.revocation_list
{
applicability.startup_time_policy_half = Some(half);
return Err((
DeploymentApplicabilityError::ForeignPolicy,
applicability,
seed,
));
}
Ok((
applicability,
VerifiedStartupTimePolicyOwner {
issuance_nonce: half.issuance_nonce,
request_timeout_ms: seed.request_timeout_ms,
shutdown_grace_ms: seed.shutdown_grace_ms,
policy: half.policy,
deployment: half.deployment,
environment: half.environment,
build: half.build,
generation: half.generation,
not_before: half.not_before,
not_after: half.not_after,
revocation_epoch: half.revocation_epoch,
revocation_list: half.revocation_list,
},
))
}
fn restore_startup_time_policy(
mut applicability: VerifiedDeploymentBundleApplicabilityOwner,
owner: VerifiedStartupTimePolicyOwner,
) -> (
VerifiedDeploymentBundleApplicabilityOwner,
VerifiedDeploymentStartupAuthoritySeed,
) {
let half = StartupTimePolicyPairingHalf {
issuance_nonce: owner.issuance_nonce,
policy: owner.policy,
deployment: owner.deployment,
environment: owner.environment,
build: owner.build,
generation: owner.generation,
not_before: owner.not_before,
not_after: owner.not_after,
revocation_epoch: owner.revocation_epoch,
revocation_list: owner.revocation_list,
};
applicability.startup_time_policy_half = Some(StartupTimePolicyPairingHalf {
issuance_nonce: half.issuance_nonce,
policy: half.policy,
deployment: half.deployment,
environment: half.environment,
build: half.build,
generation: half.generation,
not_before: half.not_before,
not_after: half.not_after,
revocation_epoch: half.revocation_epoch,
revocation_list: half.revocation_list,
});
(
applicability,
VerifiedDeploymentStartupAuthoritySeed {
half,
request_timeout_ms: owner.request_timeout_ms,
shutdown_grace_ms: owner.shutdown_grace_ms,
},
)
}
struct DeploymentBindingSeed {
binding: [u8; 32],
nonce: [u8; 32],
generation: u64,
public_key: [u8; 32],
environment: [u8; 32],
build: [u8; 32],
revocation_epoch: u64,
verified_unix_seconds: u64,
not_after: u64,
}
pub struct VerifiedDeploymentSubjectContinuation {
owner: VerifiedDeploymentBundleApplicabilityOwner,
binding: [u8; 32],
nonce: [u8; 32],
generation: u64,
}
pub struct VerifiedDeploymentResourcePermit {
binding: [u8; 32],
nonce: [u8; 32],
generation: u64,
public_key: [u8; 32],
environment: [u8; 32],
build: [u8; 32],
revocation_epoch: u64,
verified_unix_seconds: u64,
not_after: u64,
}
pub struct VerifiedDeploymentDirectoryPermit {
binding: [u8; 32],
nonce: [u8; 32],
generation: u64,
}
pub struct VerifiedDeploymentDirectoryAuthority {
subject: VerifiedDeploymentSubjectContinuation,
}
#[allow(clippy::result_large_err)]
pub fn pair_deployment_directory_permit(
subject: VerifiedDeploymentSubjectContinuation,
permit: VerifiedDeploymentDirectoryPermit,
) -> Result<
VerifiedDeploymentDirectoryAuthority,
(
DeploymentSplitError,
VerifiedDeploymentSubjectContinuation,
VerifiedDeploymentDirectoryPermit,
),
> {
if subject.binding != [0; 32]
&& subject.binding == permit.binding
&& subject.nonce == permit.nonce
&& subject.generation != 0
&& subject.generation == permit.generation
{
Ok(VerifiedDeploymentDirectoryAuthority { subject })
} else {
Err((DeploymentSplitError::Foreign, subject, permit))
}
}
pub struct VerifiedDeploymentResourceObservationOwner {
binding: [u8; 32],
nonce: [u8; 32],
generation: u64,
environment: [u8; 32],
build: [u8; 32],
target: [u8; 32],
cgroup: [u8; 32],
capacity: [u64; 7],
observed_unix_seconds: u64,
revocation_epoch: u64,
}
pub struct VerifiedRuntimeResourceParts {
observation: VerifiedDeploymentResourceObservationOwner,
}
pub struct VerifiedFinalResourceReceipt {
parts: VerifiedRuntimeResourceParts,
pair_nonce: u64,
}
pub struct VerifiedFinalDirectoryReceipt {
pub(crate) whole_nonce: u64,
pub(crate) binding: crate::observability_identity::FinalDirectoryBinding,
pub(crate) pair_nonce: u64,
}
pub struct VerifiedFinalSubjectReceipt {
pub(crate) subject: crate::VerifiedSignedDirectorySubjectContinuation,
pub(crate) whole_nonce: u64,
pub(crate) binding: crate::observability_identity::FinalDirectoryBinding,
pub(crate) pair_nonce: u64,
}
pub struct VerifiedFinalDirectoryReceiptPair {
pub(crate) directory: VerifiedFinalDirectoryReceipt,
pub(crate) subject: VerifiedFinalSubjectReceipt,
pub(crate) body: VerifiedFinalDirectoryBodyBinding,
}
pub struct VerifiedFinalGeneratedReceipt {
pub(crate) owner: crate::VerifiedGeneratedComponentGenerationV2FinalTransaction,
pub(crate) generation: crate::VerifiedComponentGenerationPairingReceipt,
pub(crate) pair_nonce: u64,
}
pub struct VerifiedFinalStartupTimeReceipt {
owner: VerifiedStartupTimePolicyOwner,
pair_nonce: u64,
}
pub struct VerifiedFinalGateBuildReceipt {
owner: VerifiedDeploymentGateBuildOwner,
pair_nonce: u64,
}
pub struct VerifiedFinalSchedulerObservationReceipt {
owner: VerifiedDeploymentSchedulerObservationOwner,
pair_nonce: u64,
}
pub struct VerifiedFinalSupervisorStartupReceipt {
owner: VerifiedDeploymentSupervisorStartupOwner,
pair_nonce: u64,
}
pub struct VerifiedFinalPairingReceipts {
pub(crate) resource: VerifiedFinalResourceReceipt,
pub(crate) directory_pair: VerifiedFinalDirectoryReceiptPair,
pub(crate) generated: VerifiedFinalGeneratedReceipt,
pub(crate) time: VerifiedFinalStartupTimeReceipt,
pub(crate) gate: VerifiedFinalGateBuildReceipt,
pub(crate) scheduler: VerifiedFinalSchedulerObservationReceipt,
pub(crate) supervisor: VerifiedFinalSupervisorStartupReceipt,
}
pub struct VerifiedFinalPairingParts {
resource: VerifiedRuntimeResourceParts,
subject: crate::VerifiedSignedDirectorySubjectContinuation,
directory: VerifiedFinishedFinalDirectorySeal,
generated: crate::VerifiedGeneratedComponentGenerationV2FinalTransaction,
time: VerifiedStartupTimePolicyOwner,
gate: VerifiedDeploymentGateBuildOwner,
scheduler: VerifiedDeploymentSchedulerObservationOwner,
supervisor: VerifiedDeploymentSupervisorStartupOwner,
}
#[cfg(test)]
impl VerifiedFinalPairingParts {
pub(crate) fn filesystem_creator_binding_is_retained(&self) -> bool {
let binding = &self.directory.filesystem_creator;
binding.pair_nonce.get() != 0
&& binding.whole_nonce.get() != 0
&& binding.directory.is_fully_bound()
}
}
pub struct VerifiedFinalDirectoryBodyBinding {
pub(crate) whole_nonce: u64,
pub(crate) binding: crate::observability_identity::FinalDirectoryBinding,
}
#[allow(dead_code)]
pub struct VerifiedFinalFilesystemCreatorBinding {
pair_nonce: std::num::NonZeroU64,
whole_nonce: std::num::NonZeroU64,
directory: crate::observability_identity::FinalDirectoryBinding,
}
#[allow(dead_code)]
pub struct VerifiedFinishedFinalDirectorySeal {
filesystem_creator: VerifiedFinalFilesystemCreatorBinding,
}
#[doc(hidden)]
#[allow(clippy::result_large_err)]
pub fn restore_final_directory_receipt_pair(
pair: VerifiedFinalDirectoryReceiptPair,
) -> crate::VerifiedSignedDirectorySubjectContinuation {
pair.subject.subject
}
#[allow(clippy::result_large_err)]
pub fn consume_final_pairing_receipts(
receipts: VerifiedFinalPairingReceipts,
) -> Result<VerifiedFinalPairingParts, VerifiedFinalPairingReceipts> {
let nonce = receipts.resource.pair_nonce;
if nonce == 0
|| receipts.directory_pair.directory.pair_nonce != nonce
|| receipts.directory_pair.subject.pair_nonce != nonce
|| receipts.generated.pair_nonce != nonce
|| receipts.time.pair_nonce != nonce
|| receipts.gate.pair_nonce != nonce
|| receipts.scheduler.pair_nonce != nonce
|| receipts.supervisor.pair_nonce != nonce
|| receipts.directory_pair.directory.whole_nonce == 0
|| receipts.directory_pair.directory.whole_nonce
!= receipts.directory_pair.subject.whole_nonce
|| receipts.directory_pair.directory.binding != receipts.directory_pair.subject.binding
|| receipts.directory_pair.directory.whole_nonce != receipts.directory_pair.body.whole_nonce
|| receipts.directory_pair.directory.binding != receipts.directory_pair.body.binding
{
return Err(receipts);
}
let pair_nonce = match std::num::NonZeroU64::new(nonce) {
Some(nonce) => nonce,
None => return Err(receipts),
};
let whole_nonce = match std::num::NonZeroU64::new(receipts.directory_pair.directory.whole_nonce)
{
Some(nonce) => nonce,
None => return Err(receipts),
};
let VerifiedFinalPairingReceipts {
resource,
directory_pair,
generated,
time,
gate,
scheduler,
supervisor,
} = receipts;
let generation = match crate::consume_component_generation_pairing(generated.generation) {
Ok(()) => (),
Err(generation) => {
return Err(VerifiedFinalPairingReceipts {
resource,
directory_pair,
generated: VerifiedFinalGeneratedReceipt {
generation,
..generated
},
time,
gate,
scheduler,
supervisor,
});
}
};
let _generation = generation;
let VerifiedFinalDirectoryReceiptPair {
directory: _,
subject,
body,
} = directory_pair;
Ok(VerifiedFinalPairingParts {
resource: resource.parts,
subject: subject.subject,
directory: VerifiedFinishedFinalDirectorySeal {
filesystem_creator: VerifiedFinalFilesystemCreatorBinding {
pair_nonce,
whole_nonce,
directory: body.binding,
},
},
generated: generated.owner,
time: time.owner,
gate: gate.owner,
scheduler: scheduler.owner,
supervisor: supervisor.owner,
})
}
#[doc(hidden)]
pub fn finish_final_pairing_parts(
parts: VerifiedFinalPairingParts,
) -> (
VerifiedRuntimeResourceParts,
crate::VerifiedSignedDirectorySubjectContinuation,
VerifiedFinishedFinalDirectorySeal,
crate::VerifiedGeneratedComponentGenerationV2FinalTransaction,
VerifiedStartupTimePolicyOwner,
VerifiedDeploymentGateBuildOwner,
VerifiedDeploymentSchedulerObservationOwner,
VerifiedDeploymentSupervisorStartupOwner,
) {
(
parts.resource,
parts.subject,
parts.directory,
parts.generated,
parts.time,
parts.gate,
parts.scheduler,
parts.supervisor,
)
}
#[doc(hidden)]
pub fn recover_rejected_final_pairing_receipts(
receipts: VerifiedFinalPairingReceipts,
) -> (
VerifiedRuntimeResourceParts,
VerifiedFinalDirectoryReceiptPair,
crate::VerifiedGeneratedComponentGenerationV2FinalTransaction,
VerifiedDeploymentStartupAuthoritySeed,
VerifiedDeploymentGateBuildAuthoritySeed,
VerifiedDeploymentSchedulerObservationSeed,
VerifiedDeploymentSupervisorStartupSeed,
) {
let VerifiedFinalPairingReceipts {
resource,
directory_pair,
generated,
time,
gate,
scheduler,
supervisor,
} = receipts;
let _generation_receipt = generated.generation;
let (subject, supervisor_seed) = directory_pair
.subject
.subject
.restore_supervisor_startup(supervisor.owner);
let (subject, scheduler_seed) = subject.restore_scheduler_observation(scheduler.owner);
let (subject, gate_seed) = subject.restore_gate_build(gate.owner);
let (subject, seed) = subject.restore_startup_time_policy(time.owner);
let directory_pair = VerifiedFinalDirectoryReceiptPair {
subject: VerifiedFinalSubjectReceipt {
subject,
..directory_pair.subject
},
..directory_pair
};
(
resource.parts,
directory_pair,
generated.owner,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
)
}
#[allow(clippy::result_large_err)]
pub fn pair_final_startup_receipts(
parts: VerifiedRuntimeResourceParts,
directory_pair: VerifiedFinalDirectoryReceiptPair,
generated: crate::VerifiedGeneratedComponentGenerationV2FinalTransaction,
seed: VerifiedDeploymentStartupAuthoritySeed,
gate_seed: VerifiedDeploymentGateBuildAuthoritySeed,
scheduler_seed: VerifiedDeploymentSchedulerObservationSeed,
supervisor_seed: VerifiedDeploymentSupervisorStartupSeed,
) -> Result<
VerifiedFinalPairingReceipts,
(
VerifiedRuntimeResourceParts,
VerifiedFinalDirectoryReceiptPair,
crate::VerifiedGeneratedComponentGenerationV2FinalTransaction,
VerifiedDeploymentStartupAuthoritySeed,
VerifiedDeploymentGateBuildAuthoritySeed,
VerifiedDeploymentSchedulerObservationSeed,
VerifiedDeploymentSupervisorStartupSeed,
),
> {
let resource = &parts.observation;
let deployment = directory_pair.subject.subject.deployment_binding_view();
if !(resource.binding != [0; 32]
&& resource.binding == deployment.0
&& resource.nonce == deployment.1
&& resource.generation != 0
&& resource.generation == deployment.2
&& resource.build == deployment.3
&& directory_pair.directory.whole_nonce != 0
&& directory_pair.directory.whole_nonce == directory_pair.subject.whole_nonce
&& directory_pair.directory.binding == directory_pair.subject.binding)
{
return Err((
parts,
directory_pair,
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
let pair_nonce = match FINAL_PAIR_NONCE
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |n| n.checked_add(1))
{
Ok(nonce) if nonce != 0 => nonce,
_ => {
return Err((
parts,
directory_pair,
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
};
let VerifiedFinalDirectoryReceiptPair {
directory,
subject,
body,
} = directory_pair;
let (subject_owner, time) = match subject.subject.pair_startup_time_policy_recovering(seed) {
Ok(value) => value,
Err((subject_owner, seed)) => {
return Err((
parts,
VerifiedFinalDirectoryReceiptPair {
directory,
subject: VerifiedFinalSubjectReceipt {
subject: subject_owner,
..subject
},
body,
},
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
};
let (subject_owner, gate) = match subject_owner.pair_gate_build_recovering(gate_seed) {
Ok(value) => value,
Err((subject_owner, gate_seed)) => {
let (subject_owner, seed) = subject_owner.restore_startup_time_policy(time);
return Err((
parts,
VerifiedFinalDirectoryReceiptPair {
directory,
subject: VerifiedFinalSubjectReceipt {
subject: subject_owner,
..subject
},
body,
},
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
};
let (subject_owner, scheduler) =
match subject_owner.pair_scheduler_observation_recovering(scheduler_seed) {
Ok(value) => value,
Err((subject_owner, scheduler_seed)) => {
let (subject_owner, gate_seed) = subject_owner.restore_gate_build(gate);
let (subject_owner, seed) = subject_owner.restore_startup_time_policy(time);
return Err((
parts,
VerifiedFinalDirectoryReceiptPair {
directory,
subject: VerifiedFinalSubjectReceipt {
subject: subject_owner,
..subject
},
body,
},
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
};
let (subject_owner, generated, generation, supervisor) = match subject_owner
.pair_supervisor_startup_recovering(supervisor_seed)
{
Ok((subject_owner, supervisor)) => match generated.pair_subject_recovering(subject_owner) {
Ok((generated, subject_owner, generation)) => {
(subject_owner, generated, generation, supervisor)
}
Err((generated, subject_owner)) => {
let (subject_owner, supervisor_seed) =
subject_owner.restore_supervisor_startup(supervisor);
let (subject_owner, scheduler_seed) =
subject_owner.restore_scheduler_observation(scheduler);
let (subject_owner, gate_seed) = subject_owner.restore_gate_build(gate);
let (subject_owner, seed) = subject_owner.restore_startup_time_policy(time);
return Err((
parts,
VerifiedFinalDirectoryReceiptPair {
directory,
subject: VerifiedFinalSubjectReceipt {
subject: subject_owner,
..subject
},
body,
},
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
},
Err((subject_owner, supervisor_seed)) => {
let (subject_owner, scheduler_seed) =
subject_owner.restore_scheduler_observation(scheduler);
let (subject_owner, gate_seed) = subject_owner.restore_gate_build(gate);
let (subject_owner, seed) = subject_owner.restore_startup_time_policy(time);
return Err((
parts,
VerifiedFinalDirectoryReceiptPair {
directory,
subject: VerifiedFinalSubjectReceipt {
subject: subject_owner,
..subject
},
body,
},
generated,
seed,
gate_seed,
scheduler_seed,
supervisor_seed,
));
}
};
Ok(VerifiedFinalPairingReceipts {
resource: VerifiedFinalResourceReceipt { parts, pair_nonce },
directory_pair: VerifiedFinalDirectoryReceiptPair {
directory: VerifiedFinalDirectoryReceipt {
pair_nonce,
..directory
},
subject: VerifiedFinalSubjectReceipt {
subject: subject_owner,
pair_nonce,
..subject
},
body,
},
generated: VerifiedFinalGeneratedReceipt {
owner: generated,
generation,
pair_nonce,
},
time: VerifiedFinalStartupTimeReceipt {
owner: time,
pair_nonce,
},
gate: VerifiedFinalGateBuildReceipt {
owner: gate,
pair_nonce,
},
scheduler: VerifiedFinalSchedulerObservationReceipt {
owner: scheduler,
pair_nonce,
},
supervisor: VerifiedFinalSupervisorStartupReceipt {
owner: supervisor,
pair_nonce,
},
})
}
#[doc(hidden)]
pub struct RuntimeResourceShape {
capacity: [usize; 7],
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RuntimeResourceShapeError {
SizeOverflow,
}
impl VerifiedRuntimeResourceParts {
#[doc(hidden)]
pub fn resource_shape(&self) -> Result<RuntimeResourceShape, RuntimeResourceShapeError> {
let mut capacity = [0; 7];
for (target, source) in capacity.iter_mut().zip(self.observation.capacity) {
*target =
usize::try_from(source).map_err(|_| RuntimeResourceShapeError::SizeOverflow)?;
}
Ok(RuntimeResourceShape { capacity })
}
}
impl RuntimeResourceShape {
#[doc(hidden)]
pub fn into_capacity(self) -> [usize; 7] {
self.capacity
}
}
pub fn seal_verified_runtime_resource_parts(
observation: VerifiedDeploymentResourceObservationOwner,
) -> VerifiedRuntimeResourceParts {
VerifiedRuntimeResourceParts { observation }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeploymentSplitError {
AlreadySplit,
Signature,
Encoding,
Foreign,
Stale,
}
#[allow(clippy::result_large_err)]
pub fn consume_deployment_binding_split(
mut owner: VerifiedDeploymentBundleApplicabilityOwner,
) -> Result<
(
VerifiedDeploymentSubjectContinuation,
VerifiedDeploymentResourcePermit,
VerifiedDeploymentDirectoryPermit,
),
(
DeploymentSplitError,
VerifiedDeploymentBundleApplicabilityOwner,
),
> {
let Some(seed) = owner.split_seed.take() else {
return Err((DeploymentSplitError::AlreadySplit, owner));
};
Ok((
VerifiedDeploymentSubjectContinuation {
owner,
binding: seed.binding,
nonce: seed.nonce,
generation: seed.generation,
},
VerifiedDeploymentResourcePermit {
binding: seed.binding,
nonce: seed.nonce,
generation: seed.generation,
public_key: seed.public_key,
environment: seed.environment,
build: seed.build,
revocation_epoch: seed.revocation_epoch,
verified_unix_seconds: seed.verified_unix_seconds,
not_after: seed.not_after,
},
VerifiedDeploymentDirectoryPermit {
binding: seed.binding,
nonce: seed.nonce,
generation: seed.generation,
},
))
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct ResourceObservationDocument {
schema: String,
domain: String,
usage: String,
binding: String,
nonce: String,
generation: u64,
environment: String,
build: String,
target: String,
cgroup: String,
capacity: [u64; 7],
observed_unix_seconds: u64,
max_age_seconds: u64,
revocation_epoch: u64,
}
const RESOURCE_CPU_UNITS: usize = 0;
const RESOURCE_LOGICAL_MEMORY_BYTES: usize = 1;
const RESOURCE_REQUESTED_MEMORY_BYTES: usize = 2;
const RESOURCE_REGISTRATION_CREDITS: usize = 3;
const RESOURCE_EVENT_CREDITS: usize = 4;
const RESOURCE_DB_CONNECTIONS: usize = 5;
const RESOURCE_DB_OPERATIONS: usize = 6;
fn valid_resource_capacity_shape(capacity: [u64; 7]) -> bool {
let required = [
capacity[RESOURCE_CPU_UNITS],
capacity[RESOURCE_LOGICAL_MEMORY_BYTES],
capacity[RESOURCE_REQUESTED_MEMORY_BYTES],
capacity[RESOURCE_REGISTRATION_CREDITS],
capacity[RESOURCE_EVENT_CREDITS],
];
let db_connections = capacity[RESOURCE_DB_CONNECTIONS];
let db_operations = capacity[RESOURCE_DB_OPERATIONS];
!required.contains(&0)
&& ((db_connections == 0 && db_operations == 0)
|| (db_connections != 0 && db_operations != 0))
}
#[allow(clippy::result_large_err)]
pub fn verify_canonical_deployment_resource_observation(
permit: VerifiedDeploymentResourcePermit,
canonical: &[u8],
signature: &[u8],
) -> Result<
VerifiedDeploymentResourceObservationOwner,
(DeploymentSplitError, VerifiedDeploymentResourcePermit),
> {
macro_rules! fail {
($error:expr) => {
return Err(($error, permit))
};
}
if !is_canonical_json(canonical)
|| UnparsedPublicKey::new(&ED25519, permit.public_key)
.verify(canonical, signature)
.is_err()
{
fail!(DeploymentSplitError::Signature);
}
let doc: ResourceObservationDocument = match serde_json::from_slice(canonical) {
Ok(doc) => doc,
Err(_) => fail!(DeploymentSplitError::Encoding),
};
let identity = |value: &str| decode_identity(value);
let (Some(target), Some(cgroup)) = (identity(&doc.target), identity(&doc.cgroup)) else {
fail!(DeploymentSplitError::Encoding);
};
if doc.schema != "saddle-f08-deployment-resource-observation/1"
|| doc.domain != hex(&sha256(b"saddle/f08/deployment-resource-observation/v1"))
|| doc.usage != hex(&sha256(b"capacity.resource-envelope.observation"))
|| identity(&doc.binding) != Some(permit.binding)
|| identity(&doc.nonce) != Some(permit.nonce)
|| doc.generation != permit.generation
|| identity(&doc.environment) != Some(permit.environment)
|| identity(&doc.build) != Some(permit.build)
|| !valid_resource_capacity_shape(doc.capacity)
|| doc.revocation_epoch != permit.revocation_epoch
{
fail!(DeploymentSplitError::Foreign);
}
if doc.max_age_seconds == 0
|| doc.observed_unix_seconds > permit.verified_unix_seconds
|| permit.verified_unix_seconds > permit.not_after
|| permit
.verified_unix_seconds
.saturating_sub(doc.observed_unix_seconds)
> doc.max_age_seconds
{
fail!(DeploymentSplitError::Stale);
}
Ok(VerifiedDeploymentResourceObservationOwner {
binding: permit.binding,
nonce: permit.nonce,
generation: permit.generation,
environment: permit.environment,
build: permit.build,
target,
cgroup,
capacity: doc.capacity,
observed_unix_seconds: doc.observed_unix_seconds,
revocation_epoch: permit.revocation_epoch,
})
}
pub(super) struct DirectoryApplicabilityView {
pub public_key: [u8; 32],
pub key: [u8; 32],
pub bundle: [u8; 32],
pub provider: [u8; 32],
pub application: [u8; 32],
pub binary: [u8; 32],
pub build: [u8; 32],
pub source: [u8; 32],
pub artifact: [u8; 32],
pub routes: [u8; 32],
pub deployment: [u8; 32],
pub environment: [u8; 32],
pub nonce: [u8; 32],
pub calibration: [u8; 32],
pub generation: u64,
}
impl VerifiedDeploymentBundleApplicabilityOwner {
#[allow(clippy::result_large_err)]
pub(crate) fn take_generation_half(
mut self,
) -> Result<(Self, crate::VerifiedSignedDeploymentGenerationOwner), Self> {
match self.generation_half.take() {
Some(half) => Ok((self, half)),
None => Err(self),
}
}
#[doc(hidden)]
pub(crate) fn restore_generation_half(
mut self,
half: crate::VerifiedSignedDeploymentGenerationOwner,
) -> Self {
debug_assert!(self.generation_half.is_none());
self.generation_half = Some(half);
self
}
pub(super) fn directory_view(&self) -> DirectoryApplicabilityView {
DirectoryApplicabilityView {
public_key: self.public_key,
key: self.key,
bundle: self.bundle,
provider: self.provider,
application: self.application,
binary: self.binary,
build: self.build,
source: self.source,
artifact: self.artifact,
routes: self.routes,
deployment: self.deployment,
environment: self.environment,
nonce: self.nonce,
calibration: self.calibration,
generation: self.generation,
}
}
}
impl VerifiedDeploymentDirectoryAuthority {
pub(crate) fn pair_supervisor_startup_recovering(
self,
seed: VerifiedDeploymentSupervisorStartupSeed,
) -> Result<
(Self, VerifiedDeploymentSupervisorStartupOwner),
(Self, VerifiedDeploymentSupervisorStartupSeed),
> {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
match pair_supervisor_startup_seed(owner, seed) {
Ok((owner, supervisor)) => Ok((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
supervisor,
)),
Err((owner, seed)) => Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)),
}
}
pub(crate) fn restore_supervisor_startup(
self,
supervisor: VerifiedDeploymentSupervisorStartupOwner,
) -> (Self, VerifiedDeploymentSupervisorStartupSeed) {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
let (owner, seed) = restore_supervisor_startup_seed(owner, supervisor);
(
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)
}
pub(crate) fn pair_scheduler_observation_recovering(
self,
seed: VerifiedDeploymentSchedulerObservationSeed,
) -> Result<
(Self, VerifiedDeploymentSchedulerObservationOwner),
(Self, VerifiedDeploymentSchedulerObservationSeed),
> {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
match pair_scheduler_observation_seed(owner, seed) {
Ok((owner, scheduler)) => Ok((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
scheduler,
)),
Err((owner, seed)) => Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)),
}
}
pub(crate) fn restore_scheduler_observation(
self,
scheduler: VerifiedDeploymentSchedulerObservationOwner,
) -> (Self, VerifiedDeploymentSchedulerObservationSeed) {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
let (owner, seed) = restore_scheduler_observation_seed(owner, scheduler);
(
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)
}
pub(crate) fn pair_gate_build_recovering(
self,
seed: VerifiedDeploymentGateBuildAuthoritySeed,
) -> Result<
(Self, VerifiedDeploymentGateBuildOwner),
(Self, VerifiedDeploymentGateBuildAuthoritySeed),
> {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
match pair_gate_build_seed(owner, seed) {
Ok((owner, gate)) => Ok((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
gate,
)),
Err((owner, seed)) => Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)),
}
}
pub(crate) fn restore_gate_build(
self,
gate: VerifiedDeploymentGateBuildOwner,
) -> (Self, VerifiedDeploymentGateBuildAuthoritySeed) {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
let (owner, seed) = restore_gate_build_seed(owner, gate);
(
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)
}
pub(crate) fn pair_startup_time_policy_recovering(
self,
seed: VerifiedDeploymentStartupAuthoritySeed,
) -> Result<
(Self, VerifiedStartupTimePolicyOwner),
(Self, VerifiedDeploymentStartupAuthoritySeed),
> {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
match pair_startup_time_policy(owner, seed) {
Ok((owner, time)) => Ok((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
time,
)),
Err((_, owner, seed)) => Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)),
}
}
pub(crate) fn restore_startup_time_policy(
self,
time: VerifiedStartupTimePolicyOwner,
) -> (Self, VerifiedDeploymentStartupAuthoritySeed) {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
let (owner, seed) = restore_startup_time_policy(owner, time);
(
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
seed,
)
}
pub(crate) fn deployment_binding_view(&self) -> ([u8; 32], [u8; 32], u64, [u8; 32]) {
(
self.subject.binding,
self.subject.nonce,
self.subject.generation,
self.subject.owner.build,
)
}
pub(super) fn directory_view(&self) -> DirectoryApplicabilityView {
self.subject.owner.directory_view()
}
#[cfg(test)]
pub(super) fn into_applicability(self) -> VerifiedDeploymentBundleApplicabilityOwner {
self.subject.owner
}
pub(super) fn verify_deployment_pair(self, expected: [[u8; 32]; 12]) -> Result<Self, Self> {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
match verify_deployment_bundle_applicability_pair(
owner,
expected[0],
expected[1],
expected[2],
expected[3],
expected[4],
expected[5],
expected[6],
expected[7],
expected[8],
expected[9],
expected[10],
expected[11],
) {
Ok(owner) => Ok(Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
}),
Err((_, owner)) => Err(Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
}),
}
}
pub(super) fn pair_generation(
self,
generated: crate::VerifiedGeneratedComponentGenerationOwner,
) -> Result<
(Self, crate::VerifiedComponentGenerationPairingReceipt),
(Self, crate::VerifiedGeneratedComponentGenerationOwner),
> {
let VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
} = self.subject;
match pair_deployment_applicability_generation(owner, generated) {
Ok((owner, receipt)) => Ok((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
receipt,
)),
Err((owner, generated)) => Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
generated,
)),
}
}
pub(crate) fn pair_generation_recovering(
self,
generated: crate::VerifiedGeneratedComponentGenerationOwner,
) -> Result<
(
Self,
crate::VerifiedGeneratedComponentGenerationOwner,
crate::VerifiedComponentGenerationPairingReceipt,
),
(Self, crate::VerifiedGeneratedComponentGenerationOwner),
> {
let VerifiedDeploymentSubjectContinuation {
mut owner,
binding,
nonce,
generation,
} = self.subject;
let deployment = match owner.generation_half.take() {
Some(half) => half,
None => {
return Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
generated,
));
}
};
match crate::generation_pair::pair_component_generation_recovering(deployment, generated) {
Ok((deployment, generated, receipt)) => {
owner.generation_half = Some(deployment);
Ok((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
generated,
receipt,
))
}
Err((_, deployment, generated)) => {
owner.generation_half = Some(deployment);
Err((
Self {
subject: VerifiedDeploymentSubjectContinuation {
owner,
binding,
nonce,
generation,
},
},
generated,
))
}
}
}
}
#[allow(clippy::result_large_err)]
pub fn pair_deployment_applicability_generation(
applicability: VerifiedDeploymentBundleApplicabilityOwner,
generated: crate::VerifiedGeneratedComponentGenerationOwner,
) -> Result<
(
VerifiedDeploymentBundleApplicabilityOwner,
crate::VerifiedComponentGenerationPairingReceipt,
),
(
VerifiedDeploymentBundleApplicabilityOwner,
crate::VerifiedGeneratedComponentGenerationOwner,
),
> {
let (applicability, deployment) = match applicability.take_generation_half() {
Ok(parts) => parts,
Err(applicability) => return Err((applicability, generated)),
};
match crate::pair_component_generation(deployment, generated) {
Ok(receipt) => Ok((applicability, receipt)),
Err((_, deployment, generated)) => {
Err((applicability.restore_generation_half(deployment), generated))
}
}
}
#[cfg(test)]
pub(super) fn fixture_directory_applicability(
public_key: [u8; 32],
generation: u64,
) -> VerifiedDeploymentBundleApplicabilityOwner {
VerifiedDeploymentBundleApplicabilityOwner {
gate_build_half: Some(fixture_gate_build_half(generation)),
scheduler_observation_half: Some(fixture_scheduler_observation_half(generation)),
supervisor_startup_half: Some(fixture_supervisor_startup_half(generation)),
filesystem_budget_half: None,
startup_time_policy_half: Some(fixture_startup_time_half(generation)),
split_seed: Some(DeploymentBindingSeed {
binding: sha256(
&[
public_key.as_slice(),
[8; 32].as_slice(),
&generation.to_le_bytes(),
]
.concat(),
),
nonce: [14; 32],
generation,
public_key,
environment: [9; 32],
build: [4; 32],
revocation_epoch: 1,
verified_unix_seconds: 1,
not_after: 2,
}),
generation_half: Some(crate::generation_pair::deployment_generation_half(
generation, [5; 32], [4; 32],
)),
bundle: [13; 32],
public_key,
key: sha256(&public_key),
provider: [1; 32],
application: [2; 32],
binary: [3; 32],
build: [4; 32],
source: [5; 32],
artifact: [6; 32],
routes: [7; 32],
deployment: [8; 32],
environment: [9; 32],
nonce: [14; 32],
calibration: [15; 32],
generation,
revocation_epoch: generation,
}
}
#[cfg(test)]
fn fixture_scheduler_observation_half(generation: u64) -> SchedulerObservationPairingHalf {
SchedulerObservationPairingHalf {
issuance_nonce: [0x51; 32],
observation: [0x52; 32],
receipt: [0x53; 32],
policy: [0x54; 32],
calibration: [15; 32],
binary: [3; 32],
source: [5; 32],
artifact: [6; 32],
routes: [7; 32],
gate: [0x42; 32],
build: [4; 32],
deployment: [8; 32],
environment: [9; 32],
generation,
key: [0x55; 32],
revocation_epoch: 1,
revocation_list: [0x56; 32],
}
}
#[cfg(test)]
fn fixture_supervisor_startup_half(generation: u64) -> SupervisorStartupPairingHalf {
SupervisorStartupPairingHalf {
issuance_nonce: [0x61; 32],
applicability: [0x62; 32],
policy: [0x63; 32],
calibration: [15; 32],
gate: [0x42; 32],
build: [4; 32],
deployment: [8; 32],
environment: [9; 32],
generation,
clock: [0x64; 32],
signal_delivery_bound: [0x65; 32],
hard_kill_enforcement: [0x66; 32],
post_driver_mechanism: [0x67; 32],
post_driver_install_applicability: [0x68; 32],
key: [0x69; 32],
revocation_epoch: 1,
revocation_list: [0x6a; 32],
}
}
#[cfg(test)]
pub(super) fn fixture_supervisor_startup_seed(
generation: u64,
) -> VerifiedDeploymentSupervisorStartupSeed {
VerifiedDeploymentSupervisorStartupSeed {
half: fixture_supervisor_startup_half(generation),
}
}
#[cfg(test)]
pub(super) fn fixture_scheduler_observation_seed(
generation: u64,
) -> VerifiedDeploymentSchedulerObservationSeed {
VerifiedDeploymentSchedulerObservationSeed {
half: fixture_scheduler_observation_half(generation),
}
}
#[cfg(test)]
fn fixture_gate_build_half(generation: u64) -> GateBuildPairingHalf {
GateBuildPairingHalf {
issuance_nonce: [0x41; 32],
gate: [0x42; 32],
build: [4; 32],
environment: [9; 32],
deployment: [8; 32],
generation,
policy: [0x43; 32],
key: [0x44; 32],
revocation_epoch: 1,
revocation_list: [0x45; 32],
}
}
#[cfg(test)]
pub(super) fn fixture_gate_build_seed(generation: u64) -> VerifiedDeploymentGateBuildAuthoritySeed {
VerifiedDeploymentGateBuildAuthoritySeed {
half: fixture_gate_build_half(generation),
}
}
#[cfg(test)]
fn fixture_startup_time_half(generation: u64) -> StartupTimePolicyPairingHalf {
StartupTimePolicyPairingHalf {
issuance_nonce: [0x31; 32],
policy: [0x32; 32],
deployment: [8; 32],
environment: [9; 32],
build: [4; 32],
generation,
not_before: 1,
not_after: 2,
revocation_epoch: 1,
revocation_list: [0x33; 32],
}
}
#[cfg(test)]
pub(super) fn fixture_startup_time_seed(generation: u64) -> VerifiedDeploymentStartupAuthoritySeed {
VerifiedDeploymentStartupAuthoritySeed {
half: fixture_startup_time_half(generation),
request_timeout_ms: 2_000,
shutdown_grace_ms: 8_000,
}
}
#[cfg(test)]
pub(super) fn fixture_runtime_resource_parts(
permit: VerifiedDeploymentResourcePermit,
) -> VerifiedRuntimeResourceParts {
VerifiedRuntimeResourceParts {
observation: VerifiedDeploymentResourceObservationOwner {
binding: permit.binding,
nonce: permit.nonce,
generation: permit.generation,
environment: permit.environment,
build: permit.build,
target: [0x31; 32],
cgroup: [0x32; 32],
capacity: [100, 2, 3, 4, 5, 0, 0],
observed_unix_seconds: permit.verified_unix_seconds,
revocation_epoch: permit.revocation_epoch,
},
}
}
macro_rules! redacted_debug {
($type:ty) => {
impl std::fmt::Debug for $type {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(concat!(stringify!($type), "(<redacted>)"))
}
}
};
}
redacted_debug!(TrustedDeploymentPolicyReceipt);
redacted_debug!(VerifiedReceiptIssuerTrustAnchorOwner);
redacted_debug!(TrustedTimeReceipt);
redacted_debug!(RevocationSnapshotReceipt);
redacted_debug!(VerifiedDeploymentBundleApplicabilityOwner);
redacted_debug!(VerifiedDeploymentStartupAuthoritySeed);
redacted_debug!(VerifiedDeploymentGateBuildAuthoritySeed);
redacted_debug!(VerifiedDeploymentSchedulerObservationSeed);
redacted_debug!(VerifiedDeploymentSupervisorStartupSeed);
redacted_debug!(VerifiedFilesystemServiceBudgetAuthoritySeed);
redacted_debug!(VerifiedStartupTimePolicyOwner);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeploymentApplicabilityError {
Encoding,
Signature,
ForeignPolicy,
WrongUsage,
Expired,
Revoked,
ReplayOrStaleGeneration,
PolicyAsset,
TimeAsset,
RevocationAsset,
StartupTimePolicyAsset,
SchedulerObservationAsset,
SupervisorStartupAsset,
FilesystemServiceBudgetAsset,
FiveLeafV2SignerApprovalAsset,
}
type ReceiptIssuerFailure = (
DeploymentApplicabilityError,
VerifiedReceiptIssuerTrustAnchorOwner,
);
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct PolicyAsset {
schema: String,
gate_identity: String,
authority_identity: String,
algorithm: String,
signature_domain: String,
usage: String,
deployment_public_key_hex: String,
key_identity: String,
provider_identity: String,
application_identity: String,
binary_identity: String,
build_identity: String,
source_identity: String,
artifact_identity: String,
routes_identity: String,
deployment_identity: String,
environment_identity: String,
nonce_identity: String,
calibration_identity: String,
deployment_generation: u64,
not_before_unix_seconds: u64,
not_after_unix_seconds: u64,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TimeAsset {
schema: String,
authority_identity: String,
clock_identity: String,
deployment_identity: String,
environment_identity: String,
deployment_generation: u64,
verified_unix_seconds: u64,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RevocationAsset {
schema: String,
authority_identity: String,
key_identity: String,
deployment_identity: String,
deployment_generation: u64,
epoch: u64,
list_identity: String,
revoked: bool,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct StartupTimePolicyAsset {
schema: String,
signature_domain: String,
usage: String,
key_identity: String,
policy_identity: String,
deployment_identity: String,
environment_identity: String,
build_identity: String,
deployment_generation: u64,
request_timeout_ms: u64,
shutdown_grace_ms: u64,
not_before_unix_seconds: u64,
not_after_unix_seconds: u64,
revocation_epoch: u64,
revocation_list_identity: String,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct SchedulerObservationAsset {
schema: String,
signature_domain: String,
usage: String,
algorithm: String,
key_identity: String,
authority_identity: String,
policy_identity: String,
calibration_identity: String,
provider_identity: String,
application_identity: String,
binary_identity: String,
build_identity: String,
source_identity: String,
artifact_identity: String,
routes_identity: String,
gate_identity: String,
deployment_identity: String,
environment_identity: String,
deployment_generation: u64,
target_identity: String,
runtime_work_identity: String,
service_identity: String,
observation_identity: String,
receipt_identity: String,
tokio_version: String,
max_worker_threads: u16,
max_active_tasks: u16,
max_registrations: u16,
max_runnable_polls: u64,
max_wake_deliveries: u64,
max_join_observations: u64,
max_timer_deliveries: u64,
poll_delivery_nanos: u64,
wake_delivery_nanos: u64,
join_delivery_nanos: u64,
timer_delivery_nanos: u64,
signal_delivery_observed: bool,
timer_delivery_observed: bool,
finalizer_delivery_observed: bool,
observed_at_unix_seconds: u64,
not_before_unix_seconds: u64,
not_after_unix_seconds: u64,
revocation_epoch: u64,
revocation_list_identity: String,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct SupervisorStartupAsset {
schema: String,
signature_domain: String,
usage: String,
algorithm: String,
key_identity: String,
authority_identity: String,
policy_identity: String,
calibration_identity: String,
gate_identity: String,
build_identity: String,
deployment_identity: String,
environment_identity: String,
deployment_generation: u64,
trusted_clock_identity: String,
signal_delivery_bound_identity: String,
external_hard_kill_enforcement_identity: String,
post_driver_mechanism_identity: String,
post_driver_install_applicability_identity: String,
not_before_unix_seconds: u64,
not_after_unix_seconds: u64,
revocation_epoch: u64,
revocation_list_identity: String,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct FilesystemServiceBudgetAsset {
schema: String,
signature_domain: String,
usage: String,
algorithm: String,
key_identity: String,
authority_identity: String,
policy_identity: String,
target_identity: String,
domain: [u64; 11],
rates: [u64; 11],
advisory_lock: bool,
hard_link_no_replace: bool,
directory_fsync: bool,
data_fsync: bool,
max_parallel_filesystem_ops: u64,
calibration_identity: String,
service_identity: String,
build_identity: String,
environment_identity: String,
filesystem_identity: String,
mount_identity: String,
deployment_identity: String,
deployment_generation: u64,
observed_at_unix_seconds: u64,
not_before_unix_seconds: u64,
not_after_unix_seconds: u64,
revocation_epoch: u64,
revocation_list_identity: String,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct FiveLeafV2SignerApprovalAsset {
schema: String,
signature_domain: String,
usage: String,
algorithm: String,
authority_identity: String,
signer_public_key_hex: String,
signer_key_identity: String,
signer_domain: String,
signer_usage: String,
policy_identity: String,
provider_identity: String,
application_identity: String,
deployment_identity: String,
environment_identity: String,
observation_sha256: String,
approved_lock_identity: String,
source_identity: String,
build_identity: String,
machine_identity: String,
root_identity: String,
deployment_generation: u64,
custody_policy_identity: String,
not_before_unix_seconds: u64,
not_after_unix_seconds: u64,
revocation_epoch: u64,
revocation_list_identity: String,
}
#[allow(clippy::result_large_err)]
pub fn verify_production_deployment_receipts(
anchor: VerifiedReceiptIssuerTrustAnchorOwner,
policy_bytes: &[u8],
policy_signature: &[u8],
time_bytes: &[u8],
time_signature: &[u8],
revocation_bytes: &[u8],
revocation_signature: &[u8],
startup_time_policy_bytes: &[u8],
startup_time_policy_signature: &[u8],
scheduler_observation_bytes: &[u8],
scheduler_observation_signature: &[u8],
supervisor_startup_bytes: &[u8],
supervisor_startup_signature: &[u8],
filesystem_budget_bytes: &[u8],
filesystem_budget_signature: &[u8],
five_leaf_signer_approval_bytes: &[u8],
five_leaf_signer_approval_signature: &[u8],
) -> Result<
(
TrustedDeploymentPolicyReceipt,
TrustedTimeReceipt,
RevocationSnapshotReceipt,
VerifiedDeploymentStartupAuthoritySeed,
VerifiedDeploymentGateBuildAuthoritySeed,
VerifiedDeploymentSchedulerObservationSeed,
VerifiedDeploymentSupervisorStartupSeed,
VerifiedFilesystemServiceBudgetAuthoritySeed,
crate::VerifiedFiveLeafV2SignerPolicyOwner,
crate::VerifiedFiveLeafV2SigningRequestApprovalOwner,
),
ReceiptIssuerFailure,
> {
macro_rules! fail {
($error:expr) => {
return Err(($error, anchor))
};
}
if !is_canonical_json(policy_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.policy_public_key)
.verify(policy_bytes, policy_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::PolicyAsset);
}
if !is_canonical_json(time_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.time_public_key)
.verify(time_bytes, time_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::TimeAsset);
}
if !is_canonical_json(revocation_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.revocation_public_key)
.verify(revocation_bytes, revocation_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::RevocationAsset);
}
if !is_canonical_json(startup_time_policy_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.policy_public_key)
.verify(startup_time_policy_bytes, startup_time_policy_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::StartupTimePolicyAsset);
}
if !is_canonical_json(scheduler_observation_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.scheduler_public_key)
.verify(scheduler_observation_bytes, scheduler_observation_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::SchedulerObservationAsset);
}
if !is_canonical_json(supervisor_startup_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.supervisor_public_key)
.verify(supervisor_startup_bytes, supervisor_startup_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::SupervisorStartupAsset);
}
if !is_canonical_json(filesystem_budget_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.filesystem_budget_public_key)
.verify(filesystem_budget_bytes, filesystem_budget_signature)
.is_err()
{
fail!(DeploymentApplicabilityError::FilesystemServiceBudgetAsset);
}
if !is_canonical_json(five_leaf_signer_approval_bytes)
|| UnparsedPublicKey::new(&ED25519, anchor.generation_approval_public_key)
.verify(
five_leaf_signer_approval_bytes,
five_leaf_signer_approval_signature,
)
.is_err()
{
fail!(DeploymentApplicabilityError::FiveLeafV2SignerApprovalAsset);
}
let policy: PolicyAsset = match serde_json::from_slice(policy_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::PolicyAsset),
};
let time: TimeAsset = match serde_json::from_slice(time_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::TimeAsset),
};
let revocation: RevocationAsset = match serde_json::from_slice(revocation_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::RevocationAsset),
};
let startup: StartupTimePolicyAsset = match serde_json::from_slice(startup_time_policy_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::StartupTimePolicyAsset),
};
let scheduler: SchedulerObservationAsset =
match serde_json::from_slice(scheduler_observation_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::SchedulerObservationAsset),
};
let supervisor: SupervisorStartupAsset = match serde_json::from_slice(supervisor_startup_bytes)
{
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::SupervisorStartupAsset),
};
let filesystem_budget: FilesystemServiceBudgetAsset =
match serde_json::from_slice(filesystem_budget_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::FilesystemServiceBudgetAsset),
};
let five_leaf_signer: FiveLeafV2SignerApprovalAsset =
match serde_json::from_slice(five_leaf_signer_approval_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::FiveLeafV2SignerApprovalAsset),
};
let identity = |value: &str| decode_identity(value);
let deployment_key = match decode_identity(&policy.deployment_public_key_hex) {
Some(value) => value,
None => fail!(DeploymentApplicabilityError::PolicyAsset),
};
if policy.schema != "saddle-f08-approved-deployment-policy/2"
|| identity(&policy.gate_identity) != Some(anchor.approved_gate)
|| identity(&policy.authority_identity) != Some(anchor.policy_authority)
|| policy.algorithm != hex(&sha256(b"Ed25519"))
|| policy.signature_domain != hex(&sha256(DOMAIN.as_bytes()))
|| policy.usage != hex(&sha256(USAGE.as_bytes()))
|| identity(&policy.key_identity) != Some(sha256(&deployment_key))
|| identity(&policy.provider_identity) != Some(anchor.provider)
|| identity(&policy.application_identity) != Some(anchor.application)
|| identity(&policy.build_identity) != Some(anchor.build)
|| identity(&policy.deployment_identity) != Some(anchor.deployment)
|| identity(&policy.environment_identity) != Some(anchor.environment)
|| identity(&policy.binary_identity).is_none()
|| identity(&policy.source_identity).is_none()
|| identity(&policy.artifact_identity).is_none()
|| identity(&policy.routes_identity).is_none()
|| identity(&policy.nonce_identity).is_none()
|| identity(&policy.calibration_identity).is_none()
|| policy.deployment_generation != anchor.generation
|| policy.not_before_unix_seconds > policy.not_after_unix_seconds
{
fail!(DeploymentApplicabilityError::PolicyAsset);
}
if time.schema != "saddle-f08-trusted-time/1"
|| identity(&time.authority_identity) != Some(anchor.time_authority)
|| identity(&time.deployment_identity) != Some(anchor.deployment)
|| identity(&time.environment_identity) != Some(anchor.environment)
|| time.deployment_generation != anchor.generation
|| identity(&time.clock_identity).is_none()
|| time.verified_unix_seconds < policy.not_before_unix_seconds
|| time.verified_unix_seconds > policy.not_after_unix_seconds
{
fail!(DeploymentApplicabilityError::TimeAsset);
}
if revocation.schema != "saddle-f08-revocation-snapshot/1"
|| identity(&revocation.authority_identity) != Some(anchor.revocation_authority)
|| identity(&revocation.key_identity) != Some(sha256(&deployment_key))
|| identity(&revocation.deployment_identity) != Some(anchor.deployment)
|| revocation.deployment_generation != anchor.generation
|| revocation.epoch < anchor.minimum_revocation_epoch
|| identity(&revocation.list_identity).is_none()
|| revocation.revoked
{
fail!(DeploymentApplicabilityError::RevocationAsset);
}
const STARTUP_DOMAIN: &str = "saddle/f08/startup-time-policy/v1";
const STARTUP_USAGE: &str = "startup.public-time-policy";
if startup.schema != "saddle-f08-startup-time-policy/1"
|| startup.signature_domain != hex(&sha256(STARTUP_DOMAIN.as_bytes()))
|| startup.usage != hex(&sha256(STARTUP_USAGE.as_bytes()))
|| identity(&startup.key_identity) != Some(sha256(&deployment_key))
|| identity(&startup.policy_identity) != Some(sha256(policy_bytes))
|| identity(&startup.deployment_identity) != Some(anchor.deployment)
|| identity(&startup.environment_identity) != Some(anchor.environment)
|| identity(&startup.build_identity) != Some(anchor.build)
|| startup.deployment_generation != anchor.generation
|| startup.request_timeout_ms == 0
|| startup.shutdown_grace_ms == 0
|| startup.not_before_unix_seconds != policy.not_before_unix_seconds
|| startup.not_after_unix_seconds != policy.not_after_unix_seconds
|| startup.revocation_epoch != revocation.epoch
|| identity(&startup.revocation_list_identity) != identity(&revocation.list_identity)
{
fail!(DeploymentApplicabilityError::StartupTimePolicyAsset);
}
let scheduler_key_identity = sha256(&anchor.scheduler_public_key);
let scheduler_identities_valid = [
&scheduler.binary_identity,
&scheduler.source_identity,
&scheduler.artifact_identity,
&scheduler.routes_identity,
&scheduler.target_identity,
&scheduler.runtime_work_identity,
&scheduler.service_identity,
&scheduler.observation_identity,
&scheduler.receipt_identity,
&scheduler.tokio_version,
]
.into_iter()
.all(|value| identity(value).is_some_and(|value| value != [0; 32]));
let scheduler_bounds = [
scheduler.max_runnable_polls,
scheduler.max_wake_deliveries,
scheduler.max_join_observations,
scheduler.max_timer_deliveries,
scheduler.poll_delivery_nanos,
scheduler.wake_delivery_nanos,
scheduler.join_delivery_nanos,
scheduler.timer_delivery_nanos,
];
if scheduler.schema != "saddle-f08-scheduler-observation/1"
|| scheduler.signature_domain != hex(&sha256(SCHEDULER_DOMAIN.as_bytes()))
|| scheduler.usage != hex(&sha256(SCHEDULER_USAGE.as_bytes()))
|| scheduler.algorithm != hex(&sha256(b"Ed25519"))
|| identity(&scheduler.key_identity) != Some(scheduler_key_identity)
|| identity(&scheduler.authority_identity) != Some(anchor.scheduler_authority)
|| identity(&scheduler.policy_identity) != Some(sha256(policy_bytes))
|| identity(&scheduler.calibration_identity) != identity(&policy.calibration_identity)
|| identity(&scheduler.binary_identity) != identity(&policy.binary_identity)
|| identity(&scheduler.source_identity) != identity(&policy.source_identity)
|| identity(&scheduler.artifact_identity) != identity(&policy.artifact_identity)
|| identity(&scheduler.routes_identity) != identity(&policy.routes_identity)
|| identity(&scheduler.provider_identity) != Some(anchor.provider)
|| identity(&scheduler.application_identity) != Some(anchor.application)
|| identity(&scheduler.build_identity) != Some(anchor.build)
|| identity(&scheduler.gate_identity) != Some(anchor.approved_gate)
|| identity(&scheduler.deployment_identity) != Some(anchor.deployment)
|| identity(&scheduler.environment_identity) != Some(anchor.environment)
|| scheduler.deployment_generation != anchor.generation
|| !scheduler_identities_valid
|| scheduler.max_worker_threads == 0
|| scheduler.max_active_tasks == 0
|| scheduler.max_registrations == 0
|| scheduler_bounds.contains(&0)
|| !scheduler.signal_delivery_observed
|| !scheduler.timer_delivery_observed
|| !scheduler.finalizer_delivery_observed
|| scheduler.not_before_unix_seconds != policy.not_before_unix_seconds
|| scheduler.not_after_unix_seconds != policy.not_after_unix_seconds
|| scheduler.observed_at_unix_seconds != time.verified_unix_seconds
|| scheduler.revocation_epoch != revocation.epoch
|| identity(&scheduler.revocation_list_identity) != identity(&revocation.list_identity)
{
fail!(DeploymentApplicabilityError::SchedulerObservationAsset);
}
let supervisor_key_identity = sha256(&anchor.supervisor_public_key);
let supervisor_identities_valid = [
&supervisor.trusted_clock_identity,
&supervisor.signal_delivery_bound_identity,
&supervisor.external_hard_kill_enforcement_identity,
&supervisor.post_driver_mechanism_identity,
&supervisor.post_driver_install_applicability_identity,
]
.into_iter()
.all(|value| identity(value).is_some_and(|value| value != [0; 32]));
if supervisor.schema != "saddle-f08-supervisor-startup-applicability/1"
|| supervisor.signature_domain != hex(&sha256(SUPERVISOR_DOMAIN.as_bytes()))
|| supervisor.usage != hex(&sha256(SUPERVISOR_USAGE.as_bytes()))
|| supervisor.algorithm != hex(&sha256(b"Ed25519"))
|| identity(&supervisor.key_identity) != Some(supervisor_key_identity)
|| identity(&supervisor.authority_identity) != Some(anchor.supervisor_authority)
|| identity(&supervisor.policy_identity) != Some(sha256(policy_bytes))
|| identity(&supervisor.calibration_identity) != identity(&policy.calibration_identity)
|| identity(&supervisor.gate_identity) != Some(anchor.approved_gate)
|| identity(&supervisor.build_identity) != Some(anchor.build)
|| identity(&supervisor.deployment_identity) != Some(anchor.deployment)
|| identity(&supervisor.environment_identity) != Some(anchor.environment)
|| supervisor.deployment_generation != anchor.generation
|| identity(&supervisor.trusted_clock_identity) != identity(&time.clock_identity)
|| !supervisor_identities_valid
|| supervisor.not_before_unix_seconds != policy.not_before_unix_seconds
|| supervisor.not_after_unix_seconds != policy.not_after_unix_seconds
|| supervisor.revocation_epoch != revocation.epoch
|| identity(&supervisor.revocation_list_identity) != identity(&revocation.list_identity)
{
fail!(DeploymentApplicabilityError::SupervisorStartupAsset);
}
let filesystem_budget_key_identity = sha256(&anchor.filesystem_budget_public_key);
let budget_domain_valid = filesystem_budget
.domain
.iter()
.enumerate()
.all(|(index, value)| index == 6 || *value != 0);
let budget_rates_valid = filesystem_budget
.rates
.iter()
.enumerate()
.all(|(index, value)| index == 6 || *value != 0)
&& ((filesystem_budget.domain[6] == 0 && filesystem_budget.rates[6] == 0)
|| (filesystem_budget.domain[6] != 0 && filesystem_budget.rates[6] != 0));
if filesystem_budget.schema != "saddle-f08-filesystem-service-budget/1"
|| filesystem_budget.signature_domain != hex(&sha256(FILESYSTEM_BUDGET_DOMAIN.as_bytes()))
|| filesystem_budget.usage != hex(&sha256(FILESYSTEM_BUDGET_USAGE.as_bytes()))
|| filesystem_budget.algorithm != hex(&sha256(b"Ed25519"))
|| identity(&filesystem_budget.key_identity) != Some(filesystem_budget_key_identity)
|| identity(&filesystem_budget.authority_identity)
!= Some(anchor.filesystem_budget_authority)
|| anchor.filesystem_budget_public_key == anchor.policy_public_key
|| anchor.filesystem_budget_public_key == anchor.time_public_key
|| anchor.filesystem_budget_public_key == anchor.revocation_public_key
|| anchor.filesystem_budget_public_key == anchor.scheduler_public_key
|| anchor.filesystem_budget_public_key == anchor.supervisor_public_key
|| anchor.filesystem_budget_authority == anchor.policy_authority
|| anchor.filesystem_budget_authority == anchor.time_authority
|| anchor.filesystem_budget_authority == anchor.revocation_authority
|| anchor.filesystem_budget_authority == anchor.scheduler_authority
|| anchor.filesystem_budget_authority == anchor.supervisor_authority
|| identity(&filesystem_budget.policy_identity) != Some(sha256(policy_bytes))
|| identity(&filesystem_budget.target_identity) != Some(anchor.filesystem_target)
|| !budget_domain_valid
|| !budget_rates_valid
|| !filesystem_budget.advisory_lock
|| !filesystem_budget.hard_link_no_replace
|| !filesystem_budget.directory_fsync
|| !filesystem_budget.data_fsync
|| filesystem_budget.max_parallel_filesystem_ops != 1
|| identity(&filesystem_budget.calibration_identity)
!= identity(&policy.calibration_identity)
|| identity(&filesystem_budget.service_identity) != Some(anchor.filesystem_service)
|| identity(&filesystem_budget.build_identity) != Some(anchor.build)
|| identity(&filesystem_budget.environment_identity) != Some(anchor.environment)
|| identity(&filesystem_budget.filesystem_identity) != Some(anchor.filesystem_identity)
|| identity(&filesystem_budget.mount_identity) != Some(anchor.filesystem_mount)
|| identity(&filesystem_budget.deployment_identity) != Some(anchor.deployment)
|| filesystem_budget.deployment_generation != anchor.generation
|| filesystem_budget.observed_at_unix_seconds != time.verified_unix_seconds
|| filesystem_budget.not_before_unix_seconds != policy.not_before_unix_seconds
|| filesystem_budget.not_after_unix_seconds != policy.not_after_unix_seconds
|| filesystem_budget.revocation_epoch != revocation.epoch
|| identity(&filesystem_budget.revocation_list_identity)
!= identity(&revocation.list_identity)
{
fail!(DeploymentApplicabilityError::FilesystemServiceBudgetAsset);
}
let Some(generation_signer_public_key) = identity(&five_leaf_signer.signer_public_key_hex)
else {
fail!(DeploymentApplicabilityError::FiveLeafV2SignerApprovalAsset);
};
let generation_signer_key_identity = sha256(&generation_signer_public_key);
let approval_key_identity = sha256(&anchor.generation_approval_public_key);
let approval_identity = sha256(five_leaf_signer_approval_bytes);
let Some(custody_policy_identity) = identity(&five_leaf_signer.custody_policy_identity) else {
fail!(DeploymentApplicabilityError::FiveLeafV2SignerApprovalAsset);
};
let authority_keys = [
anchor.policy_public_key,
anchor.time_public_key,
anchor.revocation_public_key,
anchor.scheduler_public_key,
anchor.supervisor_public_key,
anchor.filesystem_budget_public_key,
anchor.external_calibration_public_key,
];
let authority_identities = [
anchor.policy_authority,
anchor.time_authority,
anchor.revocation_authority,
anchor.scheduler_authority,
anchor.supervisor_authority,
anchor.filesystem_budget_authority,
anchor.external_calibration_authority,
];
if five_leaf_signer.schema != "saddle-f08-five-leaf-v2-signer-approval/1"
|| five_leaf_signer.signature_domain
!= hex(&sha256(FIVE_LEAF_SIGNER_APPROVAL_DOMAIN.as_bytes()))
|| five_leaf_signer.usage != hex(&sha256(FIVE_LEAF_SIGNER_APPROVAL_USAGE.as_bytes()))
|| five_leaf_signer.algorithm != hex(&sha256(b"Ed25519"))
|| identity(&five_leaf_signer.authority_identity)
!= Some(anchor.generation_approval_authority)
|| identity(&five_leaf_signer.signer_key_identity) != Some(generation_signer_key_identity)
|| identity(&five_leaf_signer.signer_domain)
!= Some(sha256(crate::generation_pair::FIVE_LEAF_V2_DOMAIN))
|| identity(&five_leaf_signer.signer_usage)
!= Some(sha256(crate::generation_pair::FIVE_LEAF_V2_USAGE))
|| identity(&five_leaf_signer.policy_identity) != Some(sha256(policy_bytes))
|| identity(&five_leaf_signer.provider_identity) != Some(anchor.provider)
|| identity(&five_leaf_signer.application_identity) != Some(anchor.application)
|| identity(&five_leaf_signer.deployment_identity) != Some(anchor.deployment)
|| identity(&five_leaf_signer.environment_identity) != Some(anchor.environment)
|| identity(&five_leaf_signer.observation_sha256) != Some(anchor.generation_observation)
|| anchor.generation_observation != crate::generation_pair::APPROVED_OBSERVATION_SHA256
|| identity(&five_leaf_signer.approved_lock_identity)
!= Some(anchor.generation_approved_lock)
|| identity(&five_leaf_signer.source_identity) != Some(anchor.generation_source)
|| identity(&five_leaf_signer.build_identity) != Some(anchor.build)
|| identity(&five_leaf_signer.machine_identity) != Some(anchor.generation_machine)
|| identity(&five_leaf_signer.root_identity) != Some(anchor.generation_root)
|| five_leaf_signer.deployment_generation != anchor.generation
|| custody_policy_identity != anchor.generation_custody_policy
|| five_leaf_signer.not_before_unix_seconds != policy.not_before_unix_seconds
|| five_leaf_signer.not_after_unix_seconds != policy.not_after_unix_seconds
|| time.verified_unix_seconds < five_leaf_signer.not_before_unix_seconds
|| time.verified_unix_seconds > five_leaf_signer.not_after_unix_seconds
|| five_leaf_signer.revocation_epoch != revocation.epoch
|| identity(&five_leaf_signer.revocation_list_identity)
!= identity(&revocation.list_identity)
|| authority_keys.contains(&anchor.generation_approval_public_key)
|| authority_keys.contains(&generation_signer_public_key)
|| generation_signer_public_key == anchor.generation_approval_public_key
|| authority_identities.contains(&anchor.generation_approval_authority)
|| approval_key_identity == generation_signer_key_identity
|| approval_identity == [0; 32]
|| custody_policy_identity == [0; 32]
{
fail!(DeploymentApplicabilityError::FiveLeafV2SignerApprovalAsset);
}
let issuance_nonce = sha256(
&[
policy_bytes,
time_bytes,
revocation_bytes,
startup_time_policy_bytes,
scheduler_observation_bytes,
supervisor_startup_bytes,
filesystem_budget_bytes,
five_leaf_signer_approval_bytes,
]
.concat(),
);
let request_nonce = sha256(
&[
FIVE_LEAF_SIGNING_REQUEST_NONCE_DOMAIN,
issuance_nonce.as_slice(),
approval_identity.as_slice(),
custody_policy_identity.as_slice(),
]
.concat(),
);
let (five_leaf_signer_policy, five_leaf_request_approval) =
crate::generation_pair::issue_five_leaf_v2_signer_policy(
crate::generation_pair::FiveLeafV2ApprovedSignerFacts {
public_key: generation_signer_public_key,
key_identity: generation_signer_key_identity,
approved_lock: anchor.generation_approved_lock,
source: anchor.generation_source,
build: anchor.build,
machine: anchor.generation_machine,
root: anchor.generation_root,
generation: anchor.generation,
observation_sha256: anchor.generation_observation,
domain: sha256(crate::generation_pair::FIVE_LEAF_V2_DOMAIN),
usage: sha256(crate::generation_pair::FIVE_LEAF_V2_USAGE),
approval_identity,
custody_policy_identity,
request_nonce,
},
);
let pairing_half = || StartupTimePolicyPairingHalf {
issuance_nonce,
policy: sha256(policy_bytes),
deployment: anchor.deployment,
environment: anchor.environment,
build: anchor.build,
generation: anchor.generation,
not_before: policy.not_before_unix_seconds,
not_after: policy.not_after_unix_seconds,
revocation_epoch: revocation.epoch,
revocation_list: identity(&revocation.list_identity).unwrap(),
};
let gate_half = || GateBuildPairingHalf {
issuance_nonce,
gate: anchor.approved_gate,
build: anchor.build,
environment: anchor.environment,
deployment: anchor.deployment,
generation: anchor.generation,
policy: sha256(policy_bytes),
key: sha256(&deployment_key),
revocation_epoch: revocation.epoch,
revocation_list: identity(&revocation.list_identity).unwrap(),
};
let scheduler_half = || SchedulerObservationPairingHalf {
issuance_nonce,
observation: identity(&scheduler.observation_identity).unwrap(),
receipt: identity(&scheduler.receipt_identity).unwrap(),
policy: sha256(policy_bytes),
calibration: identity(&policy.calibration_identity).unwrap(),
binary: identity(&policy.binary_identity).unwrap(),
source: identity(&policy.source_identity).unwrap(),
artifact: identity(&policy.artifact_identity).unwrap(),
routes: identity(&policy.routes_identity).unwrap(),
gate: anchor.approved_gate,
build: anchor.build,
deployment: anchor.deployment,
environment: anchor.environment,
generation: anchor.generation,
key: scheduler_key_identity,
revocation_epoch: revocation.epoch,
revocation_list: identity(&revocation.list_identity).unwrap(),
};
let supervisor_half = || SupervisorStartupPairingHalf {
issuance_nonce,
applicability: sha256(supervisor_startup_bytes),
policy: sha256(policy_bytes),
calibration: identity(&policy.calibration_identity).unwrap(),
gate: anchor.approved_gate,
build: anchor.build,
deployment: anchor.deployment,
environment: anchor.environment,
generation: anchor.generation,
clock: identity(&time.clock_identity).unwrap(),
signal_delivery_bound: identity(&supervisor.signal_delivery_bound_identity).unwrap(),
hard_kill_enforcement: identity(&supervisor.external_hard_kill_enforcement_identity)
.unwrap(),
post_driver_mechanism: identity(&supervisor.post_driver_mechanism_identity).unwrap(),
post_driver_install_applicability: identity(
&supervisor.post_driver_install_applicability_identity,
)
.unwrap(),
key: supervisor_key_identity,
revocation_epoch: revocation.epoch,
revocation_list: identity(&revocation.list_identity).unwrap(),
};
let filesystem_budget_half = || FilesystemServiceBudgetPairingHalf {
issuance_nonce,
budget: sha256(filesystem_budget_bytes),
policy: sha256(policy_bytes),
target: identity(&filesystem_budget.target_identity).unwrap(),
calibration: identity(&filesystem_budget.calibration_identity).unwrap(),
service: identity(&filesystem_budget.service_identity).unwrap(),
build: anchor.build,
environment: anchor.environment,
filesystem: identity(&filesystem_budget.filesystem_identity).unwrap(),
mount: identity(&filesystem_budget.mount_identity).unwrap(),
deployment: anchor.deployment,
generation: anchor.generation,
key: filesystem_budget_key_identity,
revocation_epoch: revocation.epoch,
revocation_list: identity(&revocation.list_identity).unwrap(),
};
Ok((
TrustedDeploymentPolicyReceipt {
scheduler_half: Some(scheduler_half()),
supervisor_half: Some(supervisor_half()),
filesystem_budget_half: Some(filesystem_budget_half()),
issuance_nonce,
policy_identity: sha256(policy_bytes),
gate: anchor.approved_gate,
public_key: deployment_key,
key_identity: sha256(&deployment_key),
algorithm: sha256(b"Ed25519"),
signature_domain: sha256(DOMAIN.as_bytes()),
usage: sha256(USAGE.as_bytes()),
provider: anchor.provider,
application: anchor.application,
binary: identity(&policy.binary_identity).unwrap(),
build: anchor.build,
source: identity(&policy.source_identity).unwrap(),
artifact: identity(&policy.artifact_identity).unwrap(),
routes: identity(&policy.routes_identity).unwrap(),
deployment: anchor.deployment,
environment: anchor.environment,
nonce: identity(&policy.nonce_identity).unwrap(),
calibration: identity(&policy.calibration_identity).unwrap(),
not_before: policy.not_before_unix_seconds,
not_after: policy.not_after_unix_seconds,
},
TrustedTimeReceipt {
issuance_nonce,
clock: identity(&time.clock_identity).unwrap(),
deployment: anchor.deployment,
environment: anchor.environment,
generation: anchor.generation,
verified_unix_seconds: time.verified_unix_seconds,
},
RevocationSnapshotReceipt {
issuance_nonce,
authority: anchor.revocation_authority,
key_identity: sha256(&deployment_key),
epoch: revocation.epoch,
list_identity: identity(&revocation.list_identity).unwrap(),
deployment: anchor.deployment,
generation: anchor.generation,
revoked: false,
},
VerifiedDeploymentStartupAuthoritySeed {
half: pairing_half(),
request_timeout_ms: startup.request_timeout_ms,
shutdown_grace_ms: startup.shutdown_grace_ms,
},
VerifiedDeploymentGateBuildAuthoritySeed { half: gate_half() },
VerifiedDeploymentSchedulerObservationSeed {
half: scheduler_half(),
},
VerifiedDeploymentSupervisorStartupSeed {
half: supervisor_half(),
},
VerifiedFilesystemServiceBudgetAuthoritySeed {
half: filesystem_budget_half(),
},
five_leaf_signer_policy,
five_leaf_request_approval,
))
}
type Inputs = (
TrustedDeploymentPolicyReceipt,
TrustedTimeReceipt,
RevocationSnapshotReceipt,
);
type VerifyFailure = (
DeploymentApplicabilityError,
TrustedDeploymentPolicyReceipt,
TrustedTimeReceipt,
RevocationSnapshotReceipt,
);
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Document {
schema: String,
signature_domain: String,
algorithm: String,
usage: String,
key_identity: String,
provider_identity: String,
application_identity: String,
binary_identity: String,
build_identity: String,
source_identity: String,
artifact_identity: String,
routes_identity: String,
deployment_identity: String,
environment_identity: String,
nonce_identity: String,
calibration_identity: String,
deployment_generation: u64,
revocation_epoch: u64,
revocation_list_identity: String,
bundle_identity: String,
}
#[allow(clippy::result_large_err)]
pub fn verify_deployment_bundle_applicability(
inputs: Inputs,
canonical_signed_bytes: &[u8],
signature: &[u8],
) -> Result<VerifiedDeploymentBundleApplicabilityOwner, VerifyFailure> {
let (mut policy, time, revocation) = inputs;
macro_rules! fail {
($error:expr) => {
return Err(($error, policy, time, revocation))
};
}
if UnparsedPublicKey::new(&ED25519, policy.public_key)
.verify(canonical_signed_bytes, signature)
.is_err()
{
fail!(DeploymentApplicabilityError::Signature);
}
let doc: Document = match serde_json::from_slice(canonical_signed_bytes) {
Ok(value) => value,
Err(_) => fail!(DeploymentApplicabilityError::Encoding),
};
let identity = |value: &str| decode_identity(value);
if policy.issuance_nonce != time.issuance_nonce
|| policy.issuance_nonce != revocation.issuance_nonce
|| doc.schema != "saddle-f08-deployment-bundle/1"
|| identity(&doc.signature_domain) != Some(policy.signature_domain)
|| identity(&doc.algorithm) != Some(policy.algorithm)
|| identity(&doc.key_identity) != Some(policy.key_identity)
|| sha256(&policy.public_key) != policy.key_identity
|| identity(&doc.provider_identity) != Some(policy.provider)
|| identity(&doc.application_identity) != Some(policy.application)
|| identity(&doc.binary_identity) != Some(policy.binary)
|| identity(&doc.build_identity) != Some(policy.build)
|| identity(&doc.source_identity) != Some(policy.source)
|| identity(&doc.artifact_identity) != Some(policy.artifact)
|| identity(&doc.routes_identity) != Some(policy.routes)
|| identity(&doc.deployment_identity) != Some(policy.deployment)
|| identity(&doc.environment_identity) != Some(policy.environment)
|| identity(&doc.nonce_identity) != Some(policy.nonce)
|| identity(&doc.calibration_identity) != Some(policy.calibration)
|| time.deployment != policy.deployment
|| time.environment != policy.environment
|| time.generation != doc.deployment_generation
|| revocation.key_identity != policy.key_identity
|| revocation.deployment != policy.deployment
|| revocation.generation != doc.deployment_generation
|| doc.revocation_epoch != revocation.epoch
|| identity(&doc.revocation_list_identity) != Some(revocation.list_identity)
|| policy.clock_or_revocation_invalid(&time, &revocation)
{
fail!(DeploymentApplicabilityError::ForeignPolicy);
}
if doc.signature_domain != hex(&sha256(DOMAIN.as_bytes()))
|| doc.usage != hex(&sha256(USAGE.as_bytes()))
|| identity(&doc.usage) != Some(policy.usage)
{
fail!(DeploymentApplicabilityError::WrongUsage);
}
if time.verified_unix_seconds < policy.not_before
|| time.verified_unix_seconds > policy.not_after
{
fail!(DeploymentApplicabilityError::Expired);
}
if revocation.revoked {
fail!(DeploymentApplicabilityError::Revoked);
}
let bundle = match identity(&doc.bundle_identity) {
Some(value) if value != [0; 32] => value,
_ => fail!(DeploymentApplicabilityError::Encoding),
};
if LAST_DEPLOYMENT_GENERATION
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
(doc.deployment_generation > current).then_some(doc.deployment_generation)
})
.is_err()
{
fail!(DeploymentApplicabilityError::ReplayOrStaleGeneration);
}
Ok(VerifiedDeploymentBundleApplicabilityOwner {
gate_build_half: Some(GateBuildPairingHalf {
issuance_nonce: policy.issuance_nonce,
gate: policy.gate,
build: policy.build,
environment: policy.environment,
deployment: policy.deployment,
generation: doc.deployment_generation,
policy: policy.policy_identity,
key: policy.key_identity,
revocation_epoch: revocation.epoch,
revocation_list: revocation.list_identity,
}),
scheduler_observation_half: policy.scheduler_half.take(),
supervisor_startup_half: policy.supervisor_half.take(),
filesystem_budget_half: policy.filesystem_budget_half.take(),
startup_time_policy_half: Some(StartupTimePolicyPairingHalf {
issuance_nonce: policy.issuance_nonce,
policy: policy.policy_identity,
deployment: policy.deployment,
environment: policy.environment,
build: policy.build,
generation: doc.deployment_generation,
not_before: policy.not_before,
not_after: policy.not_after,
revocation_epoch: revocation.epoch,
revocation_list: revocation.list_identity,
}),
split_seed: Some(DeploymentBindingSeed {
binding: sha256(
&[
bundle.as_slice(),
policy.key_identity.as_slice(),
policy.provider.as_slice(),
policy.application.as_slice(),
policy.build.as_slice(),
policy.source.as_slice(),
policy.artifact.as_slice(),
policy.routes.as_slice(),
policy.deployment.as_slice(),
policy.environment.as_slice(),
policy.nonce.as_slice(),
&doc.deployment_generation.to_le_bytes(),
]
.concat(),
),
nonce: policy.nonce,
generation: doc.deployment_generation,
public_key: policy.public_key,
environment: policy.environment,
build: policy.build,
revocation_epoch: revocation.epoch,
verified_unix_seconds: time.verified_unix_seconds,
not_after: policy.not_after,
}),
generation_half: Some(crate::generation_pair::deployment_generation_half(
doc.deployment_generation,
policy.source,
policy.build,
)),
bundle,
public_key: policy.public_key,
key: policy.key_identity,
provider: policy.provider,
application: policy.application,
binary: policy.binary,
build: policy.build,
source: policy.source,
artifact: policy.artifact,
routes: policy.routes,
deployment: policy.deployment,
environment: policy.environment,
nonce: policy.nonce,
calibration: policy.calibration,
generation: doc.deployment_generation,
revocation_epoch: revocation.epoch,
})
}
impl TrustedDeploymentPolicyReceipt {
fn clock_or_revocation_invalid(
&self,
time: &TrustedTimeReceipt,
revocation: &RevocationSnapshotReceipt,
) -> bool {
time.clock == [0; 32] || revocation.authority == [0; 32] || revocation.epoch == 0
}
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::result_large_err)]
pub fn verify_deployment_bundle_applicability_pair(
owner: VerifiedDeploymentBundleApplicabilityOwner,
bundle: [u8; 32],
provider: [u8; 32],
application: [u8; 32],
binary: [u8; 32],
build: [u8; 32],
source: [u8; 32],
artifact: [u8; 32],
routes: [u8; 32],
deployment: [u8; 32],
environment: [u8; 32],
nonce: [u8; 32],
calibration: [u8; 32],
) -> Result<
VerifiedDeploymentBundleApplicabilityOwner,
(
DeploymentApplicabilityError,
VerifiedDeploymentBundleApplicabilityOwner,
),
> {
if owner.bundle == bundle
&& bundle != [0; 32]
&& owner.provider == provider
&& owner.application == application
&& owner.binary == binary
&& owner.build == build
&& owner.source == source
&& owner.artifact == artifact
&& owner.routes == routes
&& owner.deployment == deployment
&& owner.environment == environment
&& owner.nonce == nonce
&& owner.calibration == calibration
&& owner.key != [0; 32]
&& owner.generation != 0
&& owner.revocation_epoch != 0
{
Ok(owner)
} else {
Err((DeploymentApplicabilityError::ForeignPolicy, owner))
}
}
fn sha256(value: &[u8]) -> [u8; 32] {
Sha256::digest(value).into()
}
fn is_canonical_json(value: &[u8]) -> bool {
serde_json::from_slice::<serde_json::Value>(value)
.ok()
.and_then(|parsed| serde_json::to_vec(&parsed).ok())
.is_some_and(|canonical| canonical == value)
}
fn decode_identity(value: &str) -> Option<[u8; 32]> {
if value.len() != 64 {
return None;
}
let mut result = [0; 32];
for (index, pair) in value.as_bytes().chunks_exact(2).enumerate() {
result[index] = u8::from_str_radix(std::str::from_utf8(pair).ok()?, 16).ok()?;
}
Some(result)
}
fn hex(value: &[u8; 32]) -> String {
value.iter().map(|byte| format!("{byte:02x}")).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn directory_permits_reject_cross_split_and_return_owners_for_retry() {
let (subject_a, _resource_a, permit_a) =
consume_deployment_binding_split(fixture_directory_applicability([0xa1; 32], 41))
.unwrap_or_else(|_| panic!("first fixture must split"));
let (subject_b, _resource_b, permit_b) =
consume_deployment_binding_split(fixture_directory_applicability([0xb2; 32], 42))
.unwrap_or_else(|_| panic!("second fixture must split"));
assert_ne!(subject_a.binding, [0; 32]);
assert_ne!(permit_a.binding, [0; 32]);
assert_ne!(subject_a.generation, 0);
assert_ne!(permit_a.generation, 0);
assert_ne!(subject_b.binding, [0; 32]);
assert_ne!(permit_b.binding, [0; 32]);
let (_, subject_a, permit_b) = pair_deployment_directory_permit(subject_a, permit_b)
.err()
.unwrap_or_else(|| panic!("crossed directory owners must reject"));
let (_, subject_b, permit_a) = pair_deployment_directory_permit(subject_b, permit_a)
.err()
.unwrap_or_else(|| panic!("reverse crossed directory owners must reject"));
assert!(pair_deployment_directory_permit(subject_a, permit_a).is_ok());
assert!(pair_deployment_directory_permit(subject_b, permit_b).is_ok());
}
#[test]
fn resource_capacity_shape_accepts_db_zero_and_rejects_partial_or_core_zero() {
assert!(valid_resource_capacity_shape([1, 2, 3, 4, 5, 0, 0]));
assert!(valid_resource_capacity_shape([1, 2, 3, 4, 5, 6, 7]));
assert!(!valid_resource_capacity_shape([1, 2, 3, 4, 5, 0, 7]));
assert!(!valid_resource_capacity_shape([1, 2, 3, 4, 5, 6, 0]));
assert!(!valid_resource_capacity_shape([1, 2, 3, 0, 5, 0, 0]));
assert!(!valid_resource_capacity_shape([1, 2, 3, 4, 0, 0, 0]));
}
#[test]
fn runtime_resource_shape_borrows_without_splitting_signed_parts() {
let parts = VerifiedRuntimeResourceParts {
observation: VerifiedDeploymentResourceObservationOwner {
binding: id(1),
nonce: id(2),
generation: 3,
environment: id(4),
build: id(5),
target: id(6),
cgroup: id(7),
capacity: [100, 2, 3, 4, 5, 0, 0],
observed_unix_seconds: 8,
revocation_epoch: 9,
},
};
assert_eq!(
parts.resource_shape().unwrap().into_capacity(),
[100, 2, 3, 4, 5, 0, 0]
);
let _still_owned = parts;
}
use ring::{
rand::SystemRandom,
signature::{Ed25519KeyPair, KeyPair},
};
fn id(n: u8) -> [u8; 32] {
[n; 32]
}
fn key() -> Ed25519KeyPair {
let rng = SystemRandom::new();
let p = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
Ed25519KeyPair::from_pkcs8(p.as_ref()).unwrap()
}
fn inputs(key: &Ed25519KeyPair, generation: u64) -> Inputs {
let kid = sha256(key.public_key().as_ref());
(
TrustedDeploymentPolicyReceipt {
scheduler_half: None,
supervisor_half: None,
filesystem_budget_half: None,
issuance_nonce: id(30),
policy_identity: id(31),
gate: id(26),
public_key: key.public_key().as_ref().try_into().unwrap(),
key_identity: kid,
algorithm: sha256(b"Ed25519"),
signature_domain: sha256(DOMAIN.as_bytes()),
usage: sha256(USAGE.as_bytes()),
provider: id(1),
application: id(2),
binary: id(3),
build: id(4),
source: id(5),
artifact: id(6),
routes: id(7),
deployment: id(8),
environment: id(9),
nonce: id(14),
calibration: id(15),
not_before: 10,
not_after: 30,
},
TrustedTimeReceipt {
issuance_nonce: id(30),
clock: id(10),
deployment: id(8),
environment: id(9),
generation,
verified_unix_seconds: 20,
},
RevocationSnapshotReceipt {
issuance_nonce: id(30),
authority: id(11),
key_identity: kid,
epoch: generation,
list_identity: id(12),
deployment: id(8),
generation,
revoked: false,
},
)
}
fn document(key: &Ed25519KeyPair, generation: u64, usage: &str) -> Vec<u8> {
serde_json::to_vec(&serde_json::json!({
"schema":"saddle-f08-deployment-bundle/1","signature_domain":hex(&sha256(DOMAIN.as_bytes())),
"algorithm":hex(&sha256(b"Ed25519")),"usage":usage,"key_identity":hex(&sha256(key.public_key().as_ref())),
"provider_identity":hex(&id(1)),"application_identity":hex(&id(2)),"binary_identity":hex(&id(3)),
"build_identity":hex(&id(4)),"source_identity":hex(&id(5)),"artifact_identity":hex(&id(6)),
"routes_identity":hex(&id(7)),"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"nonce_identity":hex(&id(14)),"calibration_identity":hex(&id(15)),
"deployment_generation":generation,"revocation_epoch":generation,"revocation_list_identity":hex(&id(12)),
"bundle_identity":hex(&id(13))})).unwrap()
}
#[allow(clippy::result_large_err)]
fn pair(
owner: VerifiedDeploymentBundleApplicabilityOwner,
values: [[u8; 32]; 12],
) -> Result<
(),
(
DeploymentApplicabilityError,
VerifiedDeploymentBundleApplicabilityOwner,
),
> {
verify_deployment_bundle_applicability_pair(
owner, values[0], values[1], values[2], values[3], values[4], values[5], values[6],
values[7], values[8], values[9], values[10], values[11],
)
.map(|_| ())
}
fn public(key: &Ed25519KeyPair) -> [u8; 32] {
key.public_key().as_ref().try_into().unwrap()
}
fn anchor(
policy: &Ed25519KeyPair,
time: &Ed25519KeyPair,
revocation: &Ed25519KeyPair,
scheduler: &Ed25519KeyPair,
supervisor: &Ed25519KeyPair,
filesystem_budget: &Ed25519KeyPair,
generation_approval: &Ed25519KeyPair,
generation: u64,
) -> VerifiedReceiptIssuerTrustAnchorOwner {
VerifiedReceiptIssuerTrustAnchorOwner {
policy_public_key: public(policy),
time_public_key: public(time),
revocation_public_key: public(revocation),
scheduler_public_key: public(scheduler),
supervisor_public_key: public(supervisor),
filesystem_budget_public_key: public(filesystem_budget),
generation_approval_public_key: public(generation_approval),
external_calibration_public_key: [0xec; 32],
policy_authority: id(21),
time_authority: id(22),
revocation_authority: id(23),
scheduler_authority: id(25),
supervisor_authority: id(27),
filesystem_budget_authority: id(28),
generation_approval_authority: id(29),
external_calibration_authority: id(30),
generation_observation: crate::generation_pair::APPROVED_OBSERVATION_SHA256,
generation_approved_lock: id(48),
generation_source: id(5),
generation_machine: id(49),
generation_root: id(50),
generation_custody_policy: id(51),
filesystem_target: id(44),
filesystem_service: id(45),
filesystem_identity: id(46),
filesystem_mount: id(47),
approved_gate: id(26),
provider: id(1),
application: id(2),
build: id(4),
deployment: id(8),
environment: id(9),
generation,
minimum_revocation_epoch: generation,
}
}
fn signed(value: serde_json::Value, key: &Ed25519KeyPair) -> (Vec<u8>, Vec<u8>) {
let bytes = serde_json::to_vec(&value).unwrap();
let signature = key.sign(&bytes).as_ref().to_vec();
(bytes, signature)
}
#[test]
fn receipt_issuer_rejects_tamper_foreign_expired_revoked_and_then_upgrades() {
let policy_root = key();
let time_root = key();
let revocation_root = key();
let scheduler_root = key();
let supervisor_root = key();
let filesystem_budget_root = key();
let generation_approval_root = key();
let generation_signer = key();
let deployment_key = key();
let generation = 20_001;
let policy = serde_json::json!({"schema":"saddle-f08-approved-deployment-policy/2",
"gate_identity":hex(&id(26)),
"authority_identity":hex(&id(21)),"algorithm":hex(&sha256(b"Ed25519")),
"signature_domain":hex(&sha256(DOMAIN.as_bytes())),"usage":hex(&sha256(USAGE.as_bytes())),
"deployment_public_key_hex":hex(&public(&deployment_key)),"key_identity":hex(&sha256(&public(&deployment_key))),
"provider_identity":hex(&id(1)),"application_identity":hex(&id(2)),"binary_identity":hex(&id(3)),
"build_identity":hex(&id(4)),"source_identity":hex(&id(5)),"artifact_identity":hex(&id(6)),
"routes_identity":hex(&id(7)),"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"nonce_identity":hex(&id(14)),"calibration_identity":hex(&id(15)),"deployment_generation":generation,
"not_before_unix_seconds":10,"not_after_unix_seconds":30});
let time = serde_json::json!({"schema":"saddle-f08-trusted-time/1","authority_identity":hex(&id(22)),
"clock_identity":hex(&id(24)),"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"deployment_generation":generation,"verified_unix_seconds":20});
let revocation = serde_json::json!({"schema":"saddle-f08-revocation-snapshot/1","authority_identity":hex(&id(23)),
"key_identity":hex(&sha256(&public(&deployment_key))),"deployment_identity":hex(&id(8)),
"deployment_generation":generation,"epoch":generation,"list_identity":hex(&id(12)),"revoked":false});
let (policy_bytes, policy_sig) = signed(policy.clone(), &policy_root);
let (time_bytes, time_sig) = signed(time.clone(), &time_root);
let (rev_bytes, rev_sig) = signed(revocation.clone(), &revocation_root);
let startup = serde_json::json!({
"schema":"saddle-f08-startup-time-policy/1",
"signature_domain":hex(&sha256(b"saddle/f08/startup-time-policy/v1")),
"usage":hex(&sha256(b"startup.public-time-policy")),
"key_identity":hex(&sha256(&public(&deployment_key))),
"policy_identity":hex(&sha256(&policy_bytes)),
"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"build_identity":hex(&id(4)),"deployment_generation":generation,
"request_timeout_ms":2000,"shutdown_grace_ms":5000,
"not_before_unix_seconds":10,"not_after_unix_seconds":30,
"revocation_epoch":generation,"revocation_list_identity":hex(&id(12))
});
let (startup_bytes, startup_sig) = signed(startup, &policy_root);
let mut scheduler = serde_json::json!({
"schema":"saddle-f08-scheduler-observation/1",
"signature_domain":hex(&sha256(SCHEDULER_DOMAIN.as_bytes())),
"usage":hex(&sha256(SCHEDULER_USAGE.as_bytes())),
"algorithm":hex(&sha256(b"Ed25519")),
"key_identity":hex(&sha256(&public(&scheduler_root))),
"authority_identity":hex(&id(25)),
"policy_identity":hex(&sha256(&policy_bytes)),
"calibration_identity":hex(&id(15)),
"provider_identity":hex(&id(1)),"application_identity":hex(&id(2)),
"binary_identity":hex(&id(3)),"build_identity":hex(&id(4)),
"source_identity":hex(&id(5)),"artifact_identity":hex(&id(6)),
"routes_identity":hex(&id(7)),"gate_identity":hex(&id(26)),
"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"deployment_generation":generation
});
let scheduler_runtime = serde_json::json!({
"target_identity":hex(&id(31)),
"runtime_work_identity":hex(&id(32)),"service_identity":hex(&id(33)),
"observation_identity":hex(&id(34)),"receipt_identity":hex(&id(35)),
"tokio_version":hex(&id(36)),"max_worker_threads":1,"max_active_tasks":1,
"max_registrations":1,"max_runnable_polls":1,"max_wake_deliveries":1,
"max_join_observations":1,"max_timer_deliveries":1,
"poll_delivery_nanos":1,"wake_delivery_nanos":1,"join_delivery_nanos":1,
"timer_delivery_nanos":1,"signal_delivery_observed":true,
"timer_delivery_observed":true,"finalizer_delivery_observed":true,
"observed_at_unix_seconds":20,"not_before_unix_seconds":10,
"not_after_unix_seconds":30,"revocation_epoch":generation,
"revocation_list_identity":hex(&id(12))
});
scheduler
.as_object_mut()
.unwrap()
.extend(scheduler_runtime.as_object().unwrap().clone());
let (scheduler_bytes, scheduler_sig) = signed(scheduler.clone(), &scheduler_root);
let supervisor = serde_json::json!({
"schema":"saddle-f08-supervisor-startup-applicability/1",
"signature_domain":hex(&sha256(SUPERVISOR_DOMAIN.as_bytes())),
"usage":hex(&sha256(SUPERVISOR_USAGE.as_bytes())),
"algorithm":hex(&sha256(b"Ed25519")),
"key_identity":hex(&sha256(&public(&supervisor_root))),
"authority_identity":hex(&id(27)),
"policy_identity":hex(&sha256(&policy_bytes)),
"calibration_identity":hex(&id(15)),
"gate_identity":hex(&id(26)),"build_identity":hex(&id(4)),
"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"deployment_generation":generation,"trusted_clock_identity":hex(&id(24)),
"signal_delivery_bound_identity":hex(&id(40)),
"external_hard_kill_enforcement_identity":hex(&id(41)),
"post_driver_mechanism_identity":hex(&id(42)),
"post_driver_install_applicability_identity":hex(&id(43)),
"not_before_unix_seconds":10,"not_after_unix_seconds":30,
"revocation_epoch":generation,"revocation_list_identity":hex(&id(12))
});
let (supervisor_bytes, supervisor_sig) = signed(supervisor.clone(), &supervisor_root);
let filesystem_budget = serde_json::json!({
"schema":"saddle-f08-filesystem-service-budget/1",
"signature_domain":hex(&sha256(FILESYSTEM_BUDGET_DOMAIN.as_bytes())),
"usage":hex(&sha256(FILESYSTEM_BUDGET_USAGE.as_bytes())),
"algorithm":hex(&sha256(b"Ed25519")),
"key_identity":hex(&sha256(&public(&filesystem_budget_root))),
"authority_identity":hex(&id(28)),
"policy_identity":hex(&sha256(&policy_bytes)),
"target_identity":hex(&id(44)),
"domain":[1,2,3,4,5,6,0,8,9,10,11],
"rates":[11,12,13,14,15,16,0,18,19,20,21],
"advisory_lock":true,"hard_link_no_replace":true,
"directory_fsync":true,"data_fsync":true,
"max_parallel_filesystem_ops":1,
"calibration_identity":hex(&id(15)),"service_identity":hex(&id(45)),
"build_identity":hex(&id(4)),"environment_identity":hex(&id(9)),
"filesystem_identity":hex(&id(46)),"mount_identity":hex(&id(47)),
"deployment_identity":hex(&id(8)),"deployment_generation":generation,
"observed_at_unix_seconds":20,"not_before_unix_seconds":10,
"not_after_unix_seconds":30,"revocation_epoch":generation,
"revocation_list_identity":hex(&id(12))
});
let (filesystem_budget_bytes, filesystem_budget_sig) =
signed(filesystem_budget.clone(), &filesystem_budget_root);
let five_leaf_signer_approval = serde_json::json!({
"schema":"saddle-f08-five-leaf-v2-signer-approval/1",
"signature_domain":hex(&sha256(FIVE_LEAF_SIGNER_APPROVAL_DOMAIN.as_bytes())),
"usage":hex(&sha256(FIVE_LEAF_SIGNER_APPROVAL_USAGE.as_bytes())),
"algorithm":hex(&sha256(b"Ed25519")),
"authority_identity":hex(&id(29)),
"signer_public_key_hex":hex(&public(&generation_signer)),
"signer_key_identity":hex(&sha256(&public(&generation_signer))),
"signer_domain":hex(&sha256(crate::generation_pair::FIVE_LEAF_V2_DOMAIN)),
"signer_usage":hex(&sha256(crate::generation_pair::FIVE_LEAF_V2_USAGE)),
"policy_identity":hex(&sha256(&policy_bytes)),
"provider_identity":hex(&id(1)),"application_identity":hex(&id(2)),
"deployment_identity":hex(&id(8)),"environment_identity":hex(&id(9)),
"observation_sha256":hex(&crate::generation_pair::APPROVED_OBSERVATION_SHA256),
"approved_lock_identity":hex(&id(48)),"source_identity":hex(&id(5)),
"build_identity":hex(&id(4)),"machine_identity":hex(&id(49)),
"root_identity":hex(&id(50)),"deployment_generation":generation,
"custody_policy_identity":hex(&id(51)),
"not_before_unix_seconds":10,"not_after_unix_seconds":30,
"revocation_epoch":generation,"revocation_list_identity":hex(&id(12))
});
let (five_leaf_signer_approval_bytes, five_leaf_signer_approval_sig) =
signed(five_leaf_signer_approval.clone(), &generation_approval_root);
let failure = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
deployment_key.sign(&policy_bytes).as_ref(),
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::PolicyAsset);
let failure = verify_production_deployment_receipts(
failure.1,
&policy_bytes,
&[0; 64],
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::PolicyAsset);
let mut legacy = policy.clone();
legacy["schema"] = serde_json::json!("saddle-f08-approved-deployment-policy/1");
let (legacy_bytes, legacy_sig) = signed(legacy, &policy_root);
let failure = verify_production_deployment_receipts(
failure.1,
&legacy_bytes,
&legacy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::PolicyAsset);
let mut wrong_gate = policy.clone();
wrong_gate["gate_identity"] = serde_json::Value::String(hex(&id(99)));
let (wrong_gate_bytes, wrong_gate_sig) = signed(wrong_gate, &policy_root);
let failure = verify_production_deployment_receipts(
failure.1,
&wrong_gate_bytes,
&wrong_gate_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::PolicyAsset);
let mut foreign = policy.clone();
foreign["provider_identity"] = serde_json::Value::String(hex(&id(99)));
let (foreign_bytes, foreign_sig) = signed(foreign, &policy_root);
let failure = verify_production_deployment_receipts(
failure.1,
&foreign_bytes,
&foreign_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::PolicyAsset);
let mut expired = time.clone();
expired["verified_unix_seconds"] = serde_json::json!(31);
let (expired_bytes, expired_sig) = signed(expired, &time_root);
let failure = verify_production_deployment_receipts(
failure.1,
&policy_bytes,
&policy_sig,
&expired_bytes,
&expired_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::TimeAsset);
let mut revoked = revocation.clone();
revoked["revoked"] = serde_json::json!(true);
let (revoked_bytes, revoked_sig) = signed(revoked, &revocation_root);
let failure = verify_production_deployment_receipts(
failure.1,
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&revoked_bytes,
&revoked_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::RevocationAsset);
let mut zero_time: serde_json::Value = serde_json::from_slice(&startup_bytes).unwrap();
zero_time["request_timeout_ms"] = serde_json::json!(0);
let (zero_bytes, zero_sig) = signed(zero_time, &policy_root);
let failure = verify_production_deployment_receipts(
failure.1,
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&zero_bytes,
&zero_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
failure.0,
DeploymentApplicabilityError::StartupTimePolicyAsset
);
let self_signed_scheduler = key();
let failure = verify_production_deployment_receipts(
failure.1,
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
self_signed_scheduler.sign(&scheduler_bytes).as_ref(),
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
failure.0,
DeploymentApplicabilityError::SchedulerObservationAsset
);
let scheduler_drifts = [
("usage", serde_json::Value::String(hex(&id(90)))),
("key_identity", serde_json::Value::String(hex(&id(91)))),
("policy_identity", serde_json::Value::String(hex(&id(92)))),
(
"calibration_identity",
serde_json::Value::String(hex(&id(93))),
),
("binary_identity", serde_json::Value::String(hex(&id(89)))),
("source_identity", serde_json::Value::String(hex(&id(88)))),
("artifact_identity", serde_json::Value::String(hex(&id(87)))),
("routes_identity", serde_json::Value::String(hex(&id(86)))),
("gate_identity", serde_json::Value::String(hex(&id(94)))),
("build_identity", serde_json::Value::String(hex(&id(95)))),
(
"deployment_identity",
serde_json::Value::String(hex(&id(96))),
),
(
"environment_identity",
serde_json::Value::String(hex(&id(97))),
),
("deployment_generation", serde_json::json!(generation + 1)),
("observed_at_unix_seconds", serde_json::json!(31)),
("revocation_epoch", serde_json::json!(generation + 1)),
];
for (field, value) in scheduler_drifts {
let mut drift = scheduler.clone();
drift[field] = value;
let (drift_bytes, drift_sig) = signed(drift, &scheduler_root);
let rejected = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&drift_bytes,
&drift_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::SchedulerObservationAsset,
"scheduler drift unexpectedly accepted: {field}"
);
}
let self_signed_supervisor = key();
let rejected = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
self_signed_supervisor.sign(&supervisor_bytes).as_ref(),
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::SupervisorStartupAsset
);
let supervisor_drifts = [
("usage", serde_json::Value::String(hex(&id(70)))),
("key_identity", serde_json::Value::String(hex(&id(71)))),
("policy_identity", serde_json::Value::String(hex(&id(72)))),
(
"calibration_identity",
serde_json::Value::String(hex(&id(73))),
),
("gate_identity", serde_json::Value::String(hex(&id(74)))),
("build_identity", serde_json::Value::String(hex(&id(75)))),
(
"deployment_identity",
serde_json::Value::String(hex(&id(76))),
),
(
"environment_identity",
serde_json::Value::String(hex(&id(77))),
),
("deployment_generation", serde_json::json!(generation + 1)),
(
"trusted_clock_identity",
serde_json::Value::String(hex(&id(78))),
),
("not_after_unix_seconds", serde_json::json!(31)),
("revocation_epoch", serde_json::json!(generation + 1)),
];
for (field, value) in supervisor_drifts {
let mut drift = supervisor.clone();
drift[field] = value;
let (drift_bytes, drift_sig) = signed(drift, &supervisor_root);
let rejected = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&drift_bytes,
&drift_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::SupervisorStartupAsset,
"supervisor startup drift unexpectedly accepted: {field}"
);
}
let filesystem_budget_drifts = [
("usage", serde_json::Value::String(hex(&id(90)))),
("target_identity", serde_json::Value::String(hex(&id(91)))),
(
"calibration_identity",
serde_json::Value::String(hex(&id(92))),
),
("service_identity", serde_json::Value::String(hex(&id(93)))),
("build_identity", serde_json::Value::String(hex(&id(94)))),
(
"environment_identity",
serde_json::Value::String(hex(&id(95))),
),
(
"filesystem_identity",
serde_json::Value::String(hex(&id(96))),
),
("mount_identity", serde_json::Value::String(hex(&id(97)))),
(
"deployment_identity",
serde_json::Value::String(hex(&id(98))),
),
("deployment_generation", serde_json::json!(generation + 1)),
("max_parallel_filesystem_ops", serde_json::json!(2)),
("advisory_lock", serde_json::json!(false)),
(
"domain",
serde_json::json!([0, 2, 3, 4, 5, 6, 0, 8, 9, 10, 11]),
),
(
"rates",
serde_json::json!([11, 0, 13, 14, 15, 16, 0, 18, 19, 20, 21]),
),
(
"rates",
serde_json::json!([11, 12, 13, 14, 15, 16, 1, 18, 19, 20, 21]),
),
("revocation_epoch", serde_json::json!(generation + 1)),
];
let reused_budget_keys = [
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
];
for reused_key in reused_budget_keys {
let mut reused = filesystem_budget.clone();
reused["key_identity"] = serde_json::Value::String(hex(&sha256(&public(reused_key))));
let (reused_bytes, reused_sig) = signed(reused, reused_key);
let rejected = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
reused_key,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&reused_bytes,
&reused_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::FilesystemServiceBudgetAsset
);
}
for reused_authority in [id(21), id(22), id(23), id(25), id(27)] {
let mut reused = filesystem_budget.clone();
reused["authority_identity"] = serde_json::Value::String(hex(&reused_authority));
let (reused_bytes, reused_sig) = signed(reused, &filesystem_budget_root);
let mut reused_anchor = anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
);
reused_anchor.filesystem_budget_authority = reused_authority;
let rejected = verify_production_deployment_receipts(
reused_anchor,
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&reused_bytes,
&reused_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::FilesystemServiceBudgetAsset
);
}
let self_signed_budget = key();
let rejected = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
self_signed_budget.sign(&filesystem_budget_bytes).as_ref(),
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::FilesystemServiceBudgetAsset
);
for (field, value) in filesystem_budget_drifts {
let mut drift = filesystem_budget.clone();
drift[field] = value;
let (drift_bytes, drift_sig) = signed(drift, &filesystem_budget_root);
let rejected = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&drift_bytes,
&drift_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::FilesystemServiceBudgetAsset,
"filesystem budget drift unexpectedly accepted: {field}"
);
}
let mut recovered_anchor = failure.1;
for (field, value) in [
("usage", serde_json::json!(hex(&id(98)))),
("provider_identity", serde_json::json!(hex(&id(98)))),
("build_identity", serde_json::json!(hex(&id(98)))),
("custody_policy_identity", serde_json::json!(hex(&id(98)))),
("revocation_epoch", serde_json::json!(generation + 1)),
] {
let mut drift = five_leaf_signer_approval.clone();
drift[field] = value;
let (drift_bytes, drift_sig) = signed(drift, &generation_approval_root);
let rejected = verify_production_deployment_receipts(
recovered_anchor,
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&drift_bytes,
&drift_sig,
)
.unwrap_err();
assert_eq!(
rejected.0,
DeploymentApplicabilityError::FiveLeafV2SignerApprovalAsset,
"signer approval drift unexpectedly accepted: {field}"
);
recovered_anchor = rejected.1;
}
let (
policy_receipt,
time_receipt,
revocation_receipt,
original_seed,
original_gate_seed,
original_scheduler_seed,
original_supervisor_seed,
_original_budget_seed,
_original_signer_policy,
_original_request_approval,
) = verify_production_deployment_receipts(
recovered_anchor,
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&startup_bytes,
&startup_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap();
let mut alternate: serde_json::Value = serde_json::from_slice(&startup_bytes).unwrap();
alternate["request_timeout_ms"] = serde_json::json!(2001);
let (alternate_bytes, alternate_sig) = signed(alternate, &policy_root);
let (
_,
_,
_,
foreign_seed,
foreign_gate_seed,
foreign_scheduler_seed,
foreign_supervisor_seed,
_foreign_budget_seed,
_foreign_signer_policy,
_foreign_request_approval,
) = verify_production_deployment_receipts(
anchor(
&policy_root,
&time_root,
&revocation_root,
&scheduler_root,
&supervisor_root,
&filesystem_budget_root,
&generation_approval_root,
generation,
),
&policy_bytes,
&policy_sig,
&time_bytes,
&time_sig,
&rev_bytes,
&rev_sig,
&alternate_bytes,
&alternate_sig,
&scheduler_bytes,
&scheduler_sig,
&supervisor_bytes,
&supervisor_sig,
&filesystem_budget_bytes,
&filesystem_budget_sig,
&five_leaf_signer_approval_bytes,
&five_leaf_signer_approval_sig,
)
.unwrap();
let bundle = document(&deployment_key, generation, &hex(&sha256(USAGE.as_bytes())));
let applicability = verify_deployment_bundle_applicability(
(policy_receipt, time_receipt, revocation_receipt),
&bundle,
deployment_key.sign(&bundle).as_ref(),
)
.unwrap();
assert_eq!(
applicability
.supervisor_startup_half
.as_ref()
.unwrap()
.issuance_nonce,
original_supervisor_seed.half.issuance_nonce
);
assert_ne!(
applicability
.supervisor_startup_half
.as_ref()
.unwrap()
.issuance_nonce,
foreign_supervisor_seed.half.issuance_nonce
);
let (applicability, foreign_scheduler_seed) =
match pair_scheduler_observation_seed(applicability, foreign_scheduler_seed) {
Ok(_) => panic!("cross-issued scheduler seed unexpectedly paired"),
Err(restored) => restored,
};
let (applicability, scheduler_owner) =
pair_scheduler_observation_seed(applicability, original_scheduler_seed).unwrap();
let (applicability, _restored_scheduler_seed) =
restore_scheduler_observation_seed(applicability, scheduler_owner);
let _foreign_scheduler_seed_remains_linear = foreign_scheduler_seed;
let _supervisor_seeds_remain_linear = (original_supervisor_seed, foreign_supervisor_seed);
let (applicability, foreign_gate_seed) =
match pair_gate_build_seed(applicability, foreign_gate_seed) {
Ok(_) => panic!("cross-issued GateBuild seed unexpectedly paired"),
Err(restored) => restored,
};
let (applicability, gate_receipt) =
pair_gate_build_seed(applicability, original_gate_seed).unwrap();
let (applicability, _restored_gate_seed) =
restore_gate_build_seed(applicability, gate_receipt);
let _foreign_gate_seed_remains_linear = foreign_gate_seed;
let (_, restored, foreign_seed) =
pair_startup_time_policy(applicability, foreign_seed).unwrap_err();
let (_applicability, _time_policy) =
pair_startup_time_policy(restored, original_seed).unwrap();
let _foreign_seed_remains_linear = foreign_seed;
}
#[test]
fn rejects_self_signed_usage_time_revocation_foreign_and_replay() {
let trusted = key();
let foreign = key();
let generation = 10_001;
let valid = document(&trusted, generation, &hex(&sha256(USAGE.as_bytes())));
let mut failure = verify_deployment_bundle_applicability(
inputs(&trusted, generation),
&valid,
foreign.sign(&valid).as_ref(),
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::Signature);
let wrong = document(&trusted, generation, &hex(&sha256(b"listener")));
failure = verify_deployment_bundle_applicability(
(failure.1, failure.2, failure.3),
&wrong,
trusted.sign(&wrong).as_ref(),
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::WrongUsage);
failure.2.verified_unix_seconds = 31;
let signature = trusted.sign(&valid);
failure = verify_deployment_bundle_applicability(
(failure.1, failure.2, failure.3),
&valid,
signature.as_ref(),
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::Expired);
failure.2.verified_unix_seconds = 20;
failure.3.revoked = true;
failure = verify_deployment_bundle_applicability(
(failure.1, failure.2, failure.3),
&valid,
signature.as_ref(),
)
.unwrap_err();
assert_eq!(failure.0, DeploymentApplicabilityError::Revoked);
failure.3.revoked = false;
let owner = verify_deployment_bundle_applicability(
(failure.1, failure.2, failure.3),
&valid,
signature.as_ref(),
)
.unwrap();
let exact = [
id(13),
id(1),
id(2),
id(3),
id(4),
id(5),
id(6),
id(7),
id(8),
id(9),
id(14),
id(15),
];
let mut owner = owner;
for field in 0..exact.len() {
let mut drifted = exact;
drifted[field] = id(99);
owner = pair(owner, drifted).unwrap_err().1;
}
assert!(pair(owner, exact).is_ok());
let replay = verify_deployment_bundle_applicability(
inputs(&trusted, generation),
&valid,
signature.as_ref(),
)
.unwrap_err();
assert_eq!(
replay.0,
DeploymentApplicabilityError::ReplayOrStaleGeneration
);
}
}