max / everycycle
- Co-Authored-By
- Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 file changed,
+91 insertions,
-3 deletions
| @@ -6,15 +6,17 @@ | |||
| 6 | 6 | //! 3. The card payload's `box_report` hash matches what | |
| 7 | 7 | //! `box_payload_hash` derives from the box payload. | |
| 8 | 8 | //! 4. Tampering with any signed field breaks verification. | |
| 9 | + | //! 5. Key derivation, canonical encoding, and signature bytes match a | |
| 10 | + | //! pinned fixture, so a format break cannot pass silently. | |
| 9 | 11 | ||
| 10 | 12 | use everycycle_appraise::{ | |
| 11 | 13 | TenantKey, VerifyError, box_payload_hash, canonical_json, sign_box_report, sign_card_report, | |
| 12 | 14 | verify_box_report, verify_card_report, | |
| 13 | 15 | }; | |
| 14 | 16 | use everycycle_hal::{ | |
| 15 | - | BoxReportPayload, CapabilityVector, CardReportPayload, DeviceIdentity, DriverBinding, | |
| 16 | - | HostIdentity, Interconnect, LaunchOverhead, Memory, PeerLink, Precision, PrecisionThroughput, | |
| 17 | - | ProbeEnvironment, ThermalEnvelope, | |
| 17 | + | BoxReport, BoxReportPayload, CapabilityVector, CardReportPayload, DeviceIdentity, | |
| 18 | + | DriverBinding, HostIdentity, Interconnect, LaunchOverhead, Memory, PeerLink, Precision, | |
| 19 | + | PrecisionThroughput, ProbeEnvironment, Signature, ThermalEnvelope, | |
| 18 | 20 | }; | |
| 19 | 21 | ||
| 20 | 22 | const TENANT: &str = "tailoredmachines"; | |
| @@ -176,6 +178,92 @@ | |||
| 176 | 178 | assert!(verify_box_report(&box_report).is_err()); | |
| 177 | 179 | } | |
| 178 | 180 | ||
| 181 | + | /// Byte-level constants pinned from the fixture above. Every other test | |
| 182 | + | /// in this file signs and verifies inside one process, so all of them | |
| 183 | + | /// stay green if seed-to-key derivation, canonical encoding, or | |
| 184 | + | /// signature encoding changes. These literals are the only thing that | |
| 185 | + | /// notices. | |
| 186 | + | /// | |
| 187 | + | /// Regenerating them is never the fix for a failure here. A mismatch | |
| 188 | + | /// means previously issued appraisals no longer verify, which is a | |
| 189 | + | /// format break that needs a version bump, not a new constant. | |
| 190 | + | mod pinned { | |
| 191 | + | /// `TenantKey::from_seed(TENANT, SEED).pubkey_b64()`. Guards | |
| 192 | + | /// seed-to-key derivation and the base64 alphabet. | |
| 193 | + | pub(crate) const PUBKEY_B64: &str = "6kpsY+KcUgq+9VB7Ey7F+ZVHdq6+vnuSQh7qaRRG0iw="; | |
| 194 | + | ||
| 195 | + | /// `canonical_json(&box_payload())`. Guards key ordering at every | |
| 196 | + | /// level, compact separators, and float formatting. | |
| 197 | + | pub(crate) const BOX_JSON: &str = r#"{"ambient_c":24.5,"driver_versions":[{"bus_address":"0000:01:00.0","driver_version":"580.126.18"}],"host":{"cpu_model":"synthetic-cpu","dmi_string":"synthetic-dmi","hostname":"astra"},"present_devices":["0000:01:00.0"],"probe_suite_version":"0.1.0","probed_at":"2026-06-20T18:23:11Z","report_version":"1"}"#; | |
| 198 | + | ||
| 199 | + | /// `box_payload_hash(&box_payload())`. Guards the SHA-256 digest and | |
| 200 | + | /// the lowercase hex encoding. | |
| 201 | + | pub(crate) const BOX_HASH: &str = | |
| 202 | + | "b875c714b30278ded37f8522bcec679565a97a723e30820e4f378e027e22799d"; | |
| 203 | + | ||
| 204 | + | /// Signature over `BOX_JSON`. Ed25519 nonces are derived from the | |
| 205 | + | /// key and message (RFC 8032), so this is reproducible. | |
| 206 | + | pub(crate) const BOX_SIG: &str = | |
| 207 | + | "gBVK+hWaX5SkmjzgwvhhsyYb9CTSo0I7xf1394G3TEQddri8CexNzs198N10cAocMu98DSrvowIHlI9oveCFCQ=="; | |
| 208 | + | ||
| 209 | + | /// Signature over the card payload, which nests `CapabilityVector` | |
| 210 | + | /// and so also covers enum-variant and nested-object encoding that | |
| 211 | + | /// the box payload never exercises. | |
| 212 | + | pub(crate) const CARD_SIG: &str = | |
| 213 | + | "8Hwfz2Lf+CAsn8iU2AsNhwH0gyO192aAjAT4bCcaSUJiUyqQsdsiiXXMckMek5OF/7Ip+sv/X069dKImdHFVDg=="; | |
| 214 | + | } | |
| 215 | + | ||
| 216 | + | #[test] | |
| 217 | + | fn seed_derives_the_pinned_pubkey() { | |
| 218 | + | let key = TenantKey::from_seed(TENANT, SEED); | |
| 219 | + | assert_eq!(key.pubkey_b64(), pinned::PUBKEY_B64); | |
| 220 | + | } | |
| 221 | + | ||
| 222 | + | #[test] | |
| 223 | + | fn box_payload_encodes_to_the_pinned_bytes() { | |
| 224 | + | assert_eq!(canonical_json(&box_payload()).unwrap(), pinned::BOX_JSON); | |
| 225 | + | assert_eq!(box_payload_hash(&box_payload()).unwrap(), pinned::BOX_HASH); | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | #[test] | |
| 229 | + | fn signatures_match_the_pinned_fixture() { | |
| 230 | + | let key = TenantKey::from_seed(TENANT, SEED); | |
| 231 | + | ||
| 232 | + | let box_report = sign_box_report(&key, box_payload()).unwrap(); | |
| 233 | + | assert_eq!(box_report.signature.alg, "ed25519"); | |
| 234 | + | assert_eq!(box_report.signature.tenant_identity, TENANT); | |
| 235 | + | assert_eq!(box_report.signature.tenant_pubkey, pinned::PUBKEY_B64); | |
| 236 | + | assert_eq!(box_report.signature.sig, pinned::BOX_SIG); | |
| 237 | + | ||
| 238 | + | let card_report = sign_card_report( | |
| 239 | + | &key, | |
| 240 | + | CardReportPayload { | |
| 241 | + | report_version: REPORT_VERSION.into(), | |
| 242 | + | probed_at: PROBED_AT.into(), | |
| 243 | + | box_report: pinned::BOX_HASH.into(), | |
| 244 | + | capability: capability(), | |
| 245 | + | }, | |
| 246 | + | ) | |
| 247 | + | .unwrap(); | |
| 248 | + | assert_eq!(card_report.signature.sig, pinned::CARD_SIG); | |
| 249 | + | } | |
| 250 | + | ||
| 251 | + | /// A report carrying the pinned signature verifies without ever calling | |
| 252 | + | /// the signing path, which is what an archived appraisal does. | |
| 253 | + | #[test] | |
| 254 | + | fn a_report_rebuilt_from_pinned_bytes_verifies() { | |
| 255 | + | let report = BoxReport { | |
| 256 | + | payload: box_payload(), | |
| 257 | + | signature: Signature { | |
| 258 | + | alg: "ed25519".into(), | |
| 259 | + | tenant_identity: TENANT.into(), | |
| 260 | + | tenant_pubkey: pinned::PUBKEY_B64.into(), | |
| 261 | + | sig: pinned::BOX_SIG.into(), | |
| 262 | + | }, | |
| 263 | + | }; | |
| 264 | + | verify_box_report(&report).expect("pinned signature still verifies"); | |
| 265 | + | } | |
| 266 | + | ||
| 179 | 267 | #[test] | |
| 180 | 268 | fn box_hash_changes_with_any_field() { | |
| 181 | 269 | let baseline = box_payload_hash(&box_payload()).unwrap(); |