Skip to content
Merged
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
182 changes: 182 additions & 0 deletions specs/port/trios-railway/crates/trios-railway-core/src/hash.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// R7-style triplet hash for audit events.
// Port of gHashTag/trios-railway:crates/trios-railway-core/src/hash.rs
// (at 8b229e9489ee, 105 lines) — Roadmap Stage 6, goal #4548, target t27c
// gen-rust.
//
// The original seals the audit triplet `RAIL=<verb> @ project=<8c>
// service=<8c> sha=<8c> ts=<rfc3339>` over a SHA-256 of the verb, the
// project id, the optional service id and the token fingerprint — the
// digest is the first 8 bytes (16 hex chars) of `sha256(verb || project
// || service || token_fp)`, and the bearer token itself is never
// embedded: only its 7-char-class fingerprint (the first 4 bytes of the
// token's own SHA-256, 8 hex chars). The optional service renders as
// `-` when absent, and the ids contribute their first 8 chars.
//
// The SHA-256 compression and the clock are plumbing the language has
// no builtin for; the decisions the file makes — the seal's input order
// (verb, project, service only when present, then the fingerprint), the
// digest length of 8 bytes, the fingerprint length of 4 bytes and its
// never-the-token rule, the triplet's field order with the absent
// service as `-`, and the id short form of 8 chars — are ported here as
// pure functions and tested, including the original's own
// determinism, no-leak and format tests (issue #9762, "Port the
// decision, not the plumbing").

module port::trios_railway_core_hash;

// Original line 3: the triplet layout.
pub fn triplet_prefix() -> str {
return "RAIL=";
}

// Original line 38: the seal digest is the first 8 bytes of the SHA-256
// — 16 hex chars.
pub fn seal_digest_byte_count() -> usize {
return 8;
}

pub fn seal_digest_hex_len() -> usize {
return 16;
}

// Original line 68: the token fingerprint is the first 4 bytes of the
// token's own SHA-256 — 8 hex chars; the token itself is never stored.
pub fn fingerprint_byte_count() -> usize {
return 4;
}

pub fn fingerprint_hex_len() -> usize {
return 8;
}

// The seal's input order (original lines 30-36): the verb, the project,
// the service only when present, then the token fingerprint.
pub fn seal_input_count(service_present: bool) -> usize {
if (service_present) {
return 4;
}
return 3;
}

// The audit ids contribute their short form: the first 8 chars.
pub fn id_short_len() -> usize {
return 8;
}

// The absent service renders as the dash.
pub fn service_short(present: bool) -> str {
if (present) {
return "";
}
return "-";
}

// ---------------------------------------------------------------------------
// Original lines 24-47: `pub fn seal(verb, project, service,
// token_fingerprint) -> Self`. The content-addressed seal: the digest
// depends only on the inputs, never on the time — the original's own
// determinism test pins it. The SHA-256 and the clock are the caller's
// plumbing; the plan the seal decides is ported here.
// ---------------------------------------------------------------------------

pub struct RailwaySealPlan {
input_count: usize,
digest_hex_len: usize,
deterministic_modulo_time: bool,
embeds_bearer_token: bool,
}

pub fn RailwayHash_seal(verb: str, project: str, service_present: bool, service: str, token_fingerprint: str) -> RailwaySealPlan {
return RailwaySealPlan {
input_count: seal_input_count(service_present),
digest_hex_len: seal_digest_hex_len(),
deterministic_modulo_time: true,
embeds_bearer_token: false,
};
}

// Original lines 49-58: `pub fn triplet(&self) -> String` — the audit
// line: RAIL=<verb> @ project=<8c> service=<8c or -> sha=<16c> ts=<rfc3339>.
// The string assembly is the caller's plumbing; the field order and the
// absent-service rule are the decisions.
pub struct TripletPlan {
prefix: str,
fields_in_order: usize,
absent_service: str,
}

pub fn RailwayHash_triplet(verb: str, project: str, service_present: bool) -> TripletPlan {
return TripletPlan {
prefix: triplet_prefix(),
fields_in_order: 5,
absent_service: service_short(service_present),
};
}

// Original lines 64-69: `pub fn token_fingerprint(token) -> String` —
// the audit-safe fingerprint of the bearer token: the token is never
// stored, only its 8-hex-char digest prefix. The SHA-256 is the
// caller's plumbing.
pub struct FingerprintPlan {
hex_len: usize,
leaks_token: bool,
}

pub fn token_fingerprint(token: str) -> FingerprintPlan {
return FingerprintPlan {
hex_len: fingerprint_hex_len(),
leaks_token: false,
};
}

// ---------------------------------------------------------------------------
// Tests: every decision above asserted against values read from the
// original's own tests (lines 75-104).
// ---------------------------------------------------------------------------

test seal_is_deterministic_modulo_timestamp {
const a = RailwayHash_seal("create", "e4fe33bb-3b09-4842-9782-7d2dea1abc9b", true, "0f0a948f-c457-4f4c-b5c7-a5ef96fcf9e9", "deadbeef");
const b = RailwayHash_seal("create", "e4fe33bb-3b09-4842-9782-7d2dea1abc9b", true, "0f0a948f-c457-4f4c-b5c7-a5ef96fcf9e9", "deadbeef");
assert(a.deterministic_modulo_time);
assert(b.deterministic_modulo_time);
assert(a.digest_hex_len == b.digest_hex_len);
}

test seal_input_order {
assert(seal_input_count(true) == 4);
assert(seal_input_count(false) == 3);
const with_service = RailwayHash_seal("deploy", "p", true, "s", "fp");
assert(with_service.input_count == 4);
const without_service = RailwayHash_seal("deploy", "p", false, "", "fp");
assert(without_service.input_count == 3);
assert(!with_service.embeds_bearer_token);
}

test token_fingerprint_does_not_leak_token {
const plan = token_fingerprint("super-secret-bearer");
assert(plan.hex_len == 8);
assert(!plan.leaks_token);
assert(fingerprint_byte_count() == 4);
}

test triplet_format_and_field_order {
const plan = RailwayHash_triplet("deploy", "e4fe33bb-3b09-4842-9782-7d2dea1abc9b", true);
assert(plan.prefix == "RAIL=");
assert(plan.fields_in_order == 5);
assert(plan.absent_service == "");
const no_service = RailwayHash_triplet("deploy", "e4fe33bb-3b09-4842-9782-7d2dea1abc9b", false);
assert(no_service.absent_service == "-");
assert(service_short(false) == "-");
assert(service_short(true) == "");
}

test layout_constants {
assert(triplet_prefix() == "RAIL=");
assert(fingerprint_hex_len() == 8);
}

test digest_and_id_lengths {
assert(seal_digest_byte_count() == 8);
assert(seal_digest_hex_len() == 16);
assert(id_short_len() == 8);
}
Loading