h33_substrate_verifier/substrate_layout.rs
1//! The 58-byte H33 signing substrate layout.
2//!
3//! This module is a pure, dependency-free reimplementation of the
4//! substrate wire format from the public specification (see
5//! `gitlab.com/drata5764111/h33/h33-substrate/SPEC.md`). It is NOT a
6//! reference to any internal H33 crate — a verifier that depended on
7//! the signer's private code would defeat the purpose of an open
8//! reference implementation.
9//!
10//! ## Byte layout (v1)
11//!
12//! ```text
13//! Offset Size Field Description
14//! ────── ──── ───── ───────────
15//! 0 1 version Schema version. Always 0x01 for v1.
16//! 1 1 computation_type Domain separator (see ComputationType).
17//! 2 32 fhe_commitment SHA3-256(canonical source bytes).
18//! 34 8 timestamp_ms Millisecond Unix timestamp, big-endian.
19//! 42 16 nonce 16 random bytes, unique per signing event.
20//! ────── ────
21//! Total: 58 bytes
22//! ```
23//!
24//! The signing message passed to the post-quantum signature algorithms
25//! is always `SHA3-256(substrate_bytes)` — exactly 32 bytes regardless
26//! of what the substrate committed to.
27
28/// Total size of a v1 substrate in bytes.
29pub const SUBSTRATE_SIZE: usize = 58;
30
31/// Schema version byte for v1 substrates.
32pub const SUBSTRATE_VERSION: u8 = 0x01;
33
34/// Size of the `fhe_commitment` field in bytes (SHA3-256 digest).
35pub const COMMITMENT_SIZE: usize = 32;
36
37/// Size of the `timestamp_ms` field in bytes (`u64` big-endian).
38pub const TIMESTAMP_SIZE: usize = 8;
39
40/// Size of the `nonce` field in bytes.
41pub const NONCE_SIZE: usize = 16;
42
43/// Offset at which the `fhe_commitment` starts.
44pub const COMMITMENT_OFFSET: usize = 2;
45
46/// Offset at which the `timestamp_ms` starts.
47pub const TIMESTAMP_OFFSET: usize = COMMITMENT_OFFSET + COMMITMENT_SIZE;
48
49/// Offset at which the `nonce` starts.
50pub const NONCE_OFFSET: usize = TIMESTAMP_OFFSET + TIMESTAMP_SIZE;
51
52/// Domain separator values defined by the substrate specification.
53///
54/// A substrate minted for one computation type can NEVER be confused
55/// with a substrate minted for another, because the type byte is
56/// covered by the SHA3-256 signing message.
57///
58/// Values are an append-only enum — once assigned, a value cannot be
59/// reused or removed without breaking every historical signature that
60/// used the old meaning.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
62#[non_exhaustive]
63#[repr(u8)]
64pub enum ComputationType {
65 /// FHE biometric authentication match.
66 BiometricAuth = 0x01,
67 /// FHE fraud scoring result.
68 FraudScore = 0x02,
69 /// `FedNow` payment attestation.
70 FedNowPayment = 0x03,
71 /// Solana transaction attestation.
72 SolanaAttestation = 0x04,
73 /// HATS governance proof.
74 HatsGovernance = 0x05,
75 /// Bitcoin UTXO quantum-insurance attestation.
76 BitcoinUtxo = 0x06,
77 /// ZK-KYC identity verification.
78 KycVerification = 0x07,
79 /// H33-Share cross-institution computation.
80 ShareComputation = 0x08,
81 /// `ArchiveSign` document attestation.
82 ArchiveSign = 0x09,
83 /// `MedVault` PHI operation.
84 MedVaultPhi = 0x0A,
85 /// `VaultKey` secret operation.
86 VaultKeyOp = 0x0B,
87 /// HTTP API response attestation (Tier 1 substrate response middleware).
88 ApiResponse = 0x0C,
89 /// Generic FHE computation — catch-all for unrelated uses.
90 GenericFhe = 0xFF,
91}
92
93impl ComputationType {
94 /// Convert from a raw byte. Returns `None` for values the verifier
95 /// does not recognize.
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use h33_substrate_verifier::ComputationType;
101 ///
102 /// assert_eq!(ComputationType::from_byte(0x01), Some(ComputationType::BiometricAuth));
103 /// assert_eq!(ComputationType::from_byte(0x0C), Some(ComputationType::ApiResponse));
104 /// assert_eq!(ComputationType::from_byte(0x42), None);
105 /// ```
106 #[must_use]
107 pub const fn from_byte(byte: u8) -> Option<Self> {
108 match byte {
109 0x01 => Some(Self::BiometricAuth),
110 0x02 => Some(Self::FraudScore),
111 0x03 => Some(Self::FedNowPayment),
112 0x04 => Some(Self::SolanaAttestation),
113 0x05 => Some(Self::HatsGovernance),
114 0x06 => Some(Self::BitcoinUtxo),
115 0x07 => Some(Self::KycVerification),
116 0x08 => Some(Self::ShareComputation),
117 0x09 => Some(Self::ArchiveSign),
118 0x0A => Some(Self::MedVaultPhi),
119 0x0B => Some(Self::VaultKeyOp),
120 0x0C => Some(Self::ApiResponse),
121 0xFF => Some(Self::GenericFhe),
122 _ => None,
123 }
124 }
125
126 /// Convert to the raw byte value.
127 #[must_use]
128 pub const fn to_byte(self) -> u8 {
129 self as u8
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn layout_constants_add_up() {
139 // Every offset + size combination must cleanly tile the 58-byte
140 // substrate with zero overlap and zero gaps.
141 assert_eq!(SUBSTRATE_SIZE, 58);
142 assert_eq!(COMMITMENT_OFFSET, 2);
143 assert_eq!(TIMESTAMP_OFFSET, 34);
144 assert_eq!(NONCE_OFFSET, 42);
145 assert_eq!(NONCE_OFFSET + NONCE_SIZE, SUBSTRATE_SIZE);
146 }
147
148 #[test]
149 fn computation_type_round_trip() {
150 let all = [
151 ComputationType::BiometricAuth,
152 ComputationType::FraudScore,
153 ComputationType::FedNowPayment,
154 ComputationType::SolanaAttestation,
155 ComputationType::HatsGovernance,
156 ComputationType::BitcoinUtxo,
157 ComputationType::KycVerification,
158 ComputationType::ShareComputation,
159 ComputationType::ArchiveSign,
160 ComputationType::MedVaultPhi,
161 ComputationType::VaultKeyOp,
162 ComputationType::ApiResponse,
163 ComputationType::GenericFhe,
164 ];
165 for ct in all {
166 assert_eq!(ComputationType::from_byte(ct.to_byte()), Some(ct));
167 }
168 }
169
170 #[test]
171 fn unrecognized_computation_type_bytes_are_none() {
172 assert_eq!(ComputationType::from_byte(0x00), None);
173 assert_eq!(ComputationType::from_byte(0x0D), None);
174 assert_eq!(ComputationType::from_byte(0x42), None);
175 assert_eq!(ComputationType::from_byte(0xFE), None);
176 }
177}