Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ x509-cert = "0.2.5"
der = "0.7.10"
sha2 = "0.11.0"
sha1 = "0.11.0"
hkdf = "0.13.0"
rsa = "0.9.10"
p256 = { version = "0.13.2", features = ["ecdsa", "pkcs8"] }

Expand Down
104 changes: 104 additions & 0 deletions crates/rite-model/src/transcript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,57 @@ pub enum StepFact {
/// Wall-clock timestamp at failure.
failed_at: DateTime<Utc>,
},
/// The ceremony entropy source was seeded with machine randomness.
///
/// Emitted once by the runner at ceremony start (run-metadata). Records
/// the machine contribution `m` and the frozen derivation scheme so any
/// value the ceremony later draws is re-derivable from the transcript
/// alone. Part of the [entropy source](StepFact::EntropyDrawn) family.
///
/// Carries no timestamp of its own: it is emitted immediately after
/// [`CeremonyStarted`](StepFact::CeremonyStarted), whose `started_at`
/// stamps that instant.
EntropySeeded {
/// Lowercase hex of the gathered machine entropy `m`.
m: String,
/// Provenance of `m` (e.g. `os`). A single label today; comma-separated
/// if more than one source is ever mixed.
source: String,
/// Frozen derivation-scheme tag (e.g. `rite-kdf/v1`) that pins the
/// entire construction. A verifier rejects an unrecognised value.
derivation: String,
},
/// A human folded additional entropy into the seed, advancing the ratchet.
///
/// Emitted by the authored `gather_entropy` step. The verbatim operator
/// contribution is recorded so the epoch chain re-folds identically; it is
/// public, witnessed entropy, not a secret. Timed by its enclosing step
/// boundaries (and by the `PromptAnswered` that captured the input).
EntropyContributed {
/// Step under which the contribution was gathered.
step: StepId,
/// Epoch index produced by this fold (1 for the first contribution).
epoch: u32,
/// Verbatim operator contribution, fed as UTF-8 into the ratchet.
contribution: String,
},
/// A value was drawn from the entropy source (a nonce, certificate serial,
/// or challenge).
///
/// Emitted whenever an action draws bytes from the entropy source. The
/// derivation `path` plus the recorded seed let `rite verify` re-derive
/// the value and confirm the right value reached the right consumer. Like
/// other action-emitted evidence, it is timed by its enclosing step.
EntropyDrawn {
/// Step that drew the value.
step: StepId,
/// Derivation path `<epoch>/<step>/<purpose>`.
path: String,
/// Number of bytes drawn (cross-checks `value`).
len: usize,
/// Lowercase hex of the derived bytes.
value: String,
},
}

/// JSON-shape snapshot tests, the tripwire for accidental wire-format breaks.
Expand Down Expand Up @@ -639,4 +690,57 @@ mod schema_snapshot_tests {
}),
);
}

#[test]
fn entropy_seeded() {
assert_json(
&StepFact::EntropySeeded {
m: "00112233".to_string(),
source: "os".to_string(),
derivation: "rite-kdf/v1".to_string(),
},
&json!({
"type": "entropy_seeded",
"m": "00112233",
"source": "os",
"derivation": "rite-kdf/v1",
}),
);
}

#[test]
fn entropy_contributed() {
assert_json(
&StepFact::EntropyContributed {
step: StepId::new("roll_dice"),
epoch: 1,
contribution: "3 1 6 4 2 5".to_string(),
},
&json!({
"type": "entropy_contributed",
"step": "roll_dice",
"epoch": 1,
"contribution": "3 1 6 4 2 5",
}),
);
}

#[test]
fn entropy_drawn() {
assert_json(
&StepFact::EntropyDrawn {
step: StepId::new("issue"),
path: "0/issue/cert-serial".to_string(),
len: 9,
value: "aabbccddeeff00112233".to_string(),
},
&json!({
"type": "entropy_drawn",
"step": "issue",
"path": "0/issue/cert-serial",
"len": 9,
"value": "aabbccddeeff00112233",
}),
);
}
}
9 changes: 9 additions & 0 deletions crates/rite-model/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ pub enum ActionType {
ExportPublic,
/// Formal attestation statement.
Attest,
/// Fold human-supplied entropy into the ceremony seed.
///
/// A participant supplies a free-form random value (for example, the
/// result of rolling physical dice), which is mixed into the entropy
/// source's ratchet. Any later drawn value reflects the contribution and
/// stays re-derivable by `rite verify`.
GatherEntropy,
/// TPM attestation with PCR measurements and cryptographic quotes.
///
/// Requires the `rite-tpm` backend.
Expand Down Expand Up @@ -186,6 +193,7 @@ impl ActionType {
ActionType::OralReadback => "Read back a value aloud for verification.",
ActionType::MachineInfo => "Record system and environment information.",
ActionType::Attest => "Record a signed attestation from a participant.",
ActionType::GatherEntropy => "Fold human-supplied entropy into the ceremony seed.",
ActionType::TpmAttest => "Record TPM platform attestation (PCR values).",
ActionType::GenerateKeypair => "Generate an asymmetric keypair.",
ActionType::ExportPublic => "Export the public component of a keypair.",
Expand Down Expand Up @@ -213,6 +221,7 @@ impl std::fmt::Display for ActionType {
ActionType::UnwrapKey => write!(f, "unwrap_key"),
ActionType::ExportPublic => write!(f, "export_public"),
ActionType::Attest => write!(f, "attest"),
ActionType::GatherEntropy => write!(f, "gather_entropy"),
ActionType::TpmAttest => write!(f, "tpm_attest"),
ActionType::PivReadCertificate => write!(f, "piv_read_certificate"),
ActionType::PivSign => write!(f, "piv_sign"),
Expand Down
2 changes: 2 additions & 0 deletions crates/rite-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ serde_json = { workspace = true }
thiserror = { workspace = true }
chrono = { workspace = true }
sha2 = { workspace = true }
hkdf = { workspace = true }
rand = { workspace = true }
base16ct = { workspace = true }
base32ct = { workspace = true }
base64ct = { workspace = true }
Expand Down
10 changes: 10 additions & 0 deletions crates/rite-runtime/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ pub fn fact_summary(fact: &StepFact) -> Option<(Icon, String)> {
StepFact::CeremonyFailed { error, .. } => {
Some((Icon::Cross, format!("Ceremony failed: {}", error.message)))
}
StepFact::EntropySeeded { source, .. } => {
Some((Icon::Info, format!("Entropy source seeded ({source})")))
}
StepFact::EntropyContributed { epoch, .. } => Some((
Icon::Info,
format!("Entropy contribution folded (epoch {epoch})"),
)),
StepFact::EntropyDrawn { path, .. } => {
Some((Icon::Info, format!("Random value drawn: {path}")))
}
// `StepFact` is `#[non_exhaustive]`; future variants may carry
// payloads the live UI shouldn't blindly Debug-print. Surface a
// typed placeholder until each new variant is given a summary.
Expand Down
Loading