From 44f8456c70bef80882c3c46cb301e37b98f4ace3 Mon Sep 17 00:00:00 2001 From: ecgang Date: Sun, 26 Jul 2026 11:55:42 -0700 Subject: [PATCH] Courier vectors: decode the two keys nothing was reading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-model review (codex) on the previous commits. `packet.payloadIs` and `signature.verify` were the only non-comment keys the Decodable shapes did not declare, so either could be renamed or deleted from the published file and every test stayed green — the exact staleness this PR exists to prevent, surviving in the two fields that tell a second implementer what to feed the verifier. Both are declared now, and `payloadIs` is checked rather than merely present: it states a byte count, and that count is asserted against the real encoded payload. Prose that states a number is a claim like any other. Verified by mutation: renaming either key fails decoding, and stating 73 bytes where the payload is 74 fails the length assertion alone. Codex also read the pre-fix diff and re-reported the unchecked Data indexing already fixed two commits ago, and claimed Ed25519 signing is deterministic so the non-determinism assertion should fail. The latter is wrong for CryptoKit specifically: `Curve25519.Signing` randomizes rather than following RFC 8032's deterministic construction, which the test asserts and the suite confirms on every run. No change. Co-Authored-By: Claude Opus 5 (1M context) --- .../BitFoundationTests/CourierVectorTests.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/localPackages/BitFoundation/Tests/BitFoundationTests/CourierVectorTests.swift b/localPackages/BitFoundation/Tests/BitFoundationTests/CourierVectorTests.swift index adb2d189..86716ee4 100644 --- a/localPackages/BitFoundation/Tests/BitFoundationTests/CourierVectorTests.swift +++ b/localPackages/BitFoundation/Tests/BitFoundationTests/CourierVectorTests.swift @@ -104,6 +104,9 @@ struct CourierVectorTests { let version: UInt8 let type: String let ttlOnWire: UInt8 + /// Declared so it cannot be renamed or dropped unnoticed; the + /// byte count it names is asserted against the real payload. + let payloadIs: String } struct Flags: Decodable { let unsignedUnpadded: String @@ -124,6 +127,9 @@ struct CourierVectorTests { let publicKey: String let signatureLength: Int let deterministic: Bool + /// Declared for the same reason as `payloadIs`: an undeclared key + /// can vanish from the published file and every test stays green. + let verify: String } let inputs: Inputs @@ -177,6 +183,12 @@ struct CourierVectorTests { #expect(encoded.hexEncodedString() == v.envelopeTLV.encoded) #expect(encoded.count == v.envelopeTLV.encodedLength) + // The prose in `packet.payloadIs` states this byte count. Prose that + // states a number is a claim like any other, and this is the one place + // it can be checked rather than trusted. + #expect(v.packetSigning.packet.payloadIs.contains("\(encoded.count) bytes"), + "packet.payloadIs disagrees with the real payload length") + // 0x02 carries 000001a3185c5000 == 1_800_000_000_000 ms, not seconds. let decoded = try #require(CourierEnvelope.decode(encoded)) #expect(decoded.expiry == v.inputs.expiryMillis) @@ -302,6 +314,9 @@ struct CourierVectorTests { let a = try key.signature(for: preimage) let b = try key.signature(for: preimage) #expect(v.signature.deterministic == false) + // The published instruction must keep naming the field it points at, + // so renaming `signingPreimage` cannot leave it dangling. + #expect(v.signature.verify.contains("signingPreimage")) #expect(Data(a) != Data(b), "CryptoKit Ed25519 signing is randomized") #expect(key.publicKey.isValidSignature(a, for: preimage)) #expect(key.publicKey.isValidSignature(b, for: preimage))