burnledger 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/cjs/audit-pack.d.ts +201 -0
  2. package/dist/cjs/audit-pack.d.ts.map +1 -0
  3. package/dist/cjs/audit-pack.js +867 -0
  4. package/dist/cjs/audit-pack.js.map +1 -0
  5. package/dist/cjs/client.d.ts +41 -1
  6. package/dist/cjs/client.d.ts.map +1 -1
  7. package/dist/cjs/client.js +58 -0
  8. package/dist/cjs/client.js.map +1 -1
  9. package/dist/cjs/index.d.ts +46 -2
  10. package/dist/cjs/index.d.ts.map +1 -1
  11. package/dist/cjs/index.js +69 -2
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/models.d.ts +28 -2
  14. package/dist/cjs/models.d.ts.map +1 -1
  15. package/dist/cjs/models.js +19 -1
  16. package/dist/cjs/models.js.map +1 -1
  17. package/dist/cjs/run-record.d.ts +130 -0
  18. package/dist/cjs/run-record.d.ts.map +1 -0
  19. package/dist/cjs/run-record.js +272 -0
  20. package/dist/cjs/run-record.js.map +1 -0
  21. package/dist/cjs/verify.d.ts +58 -0
  22. package/dist/cjs/verify.d.ts.map +1 -1
  23. package/dist/cjs/verify.js +207 -5
  24. package/dist/cjs/verify.js.map +1 -1
  25. package/dist/esm/audit-pack.d.ts +201 -0
  26. package/dist/esm/audit-pack.d.ts.map +1 -0
  27. package/dist/esm/audit-pack.js +858 -0
  28. package/dist/esm/audit-pack.js.map +1 -0
  29. package/dist/esm/cli.d.ts +33 -0
  30. package/dist/esm/cli.d.ts.map +1 -1
  31. package/dist/esm/cli.js +225 -5
  32. package/dist/esm/cli.js.map +1 -1
  33. package/dist/esm/client.d.ts +41 -1
  34. package/dist/esm/client.d.ts.map +1 -1
  35. package/dist/esm/client.js +59 -1
  36. package/dist/esm/client.js.map +1 -1
  37. package/dist/esm/index.d.ts +46 -2
  38. package/dist/esm/index.d.ts.map +1 -1
  39. package/dist/esm/index.js +52 -1
  40. package/dist/esm/index.js.map +1 -1
  41. package/dist/esm/models.d.ts +28 -2
  42. package/dist/esm/models.d.ts.map +1 -1
  43. package/dist/esm/models.js +18 -1
  44. package/dist/esm/models.js.map +1 -1
  45. package/dist/esm/run-record.d.ts +130 -0
  46. package/dist/esm/run-record.d.ts.map +1 -0
  47. package/dist/esm/run-record.js +262 -0
  48. package/dist/esm/run-record.js.map +1 -0
  49. package/dist/esm/verify.d.ts +58 -0
  50. package/dist/esm/verify.d.ts.map +1 -1
  51. package/dist/esm/verify.js +200 -8
  52. package/dist/esm/verify.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/audit-pack.ts +1067 -0
  55. package/src/cli.ts +234 -5
  56. package/src/client.ts +82 -1
  57. package/src/index.ts +117 -1
  58. package/src/models.ts +47 -3
  59. package/src/run-record.ts +371 -0
  60. package/src/verify.ts +228 -8
@@ -16,6 +16,7 @@
16
16
  * output at issuance time (transparency_status=PENDING, no transparency).
17
17
  */
18
18
  import type { CryptoOps } from "./crypto.js";
19
+ import { VerificationError } from "./errors.js";
19
20
  import type { TransparencyResult, VerificationResult } from "./models.js";
20
21
  /**
21
22
  * Every format this build can read, oldest first. Mirrors core.FormatVersions()
@@ -28,6 +29,17 @@ import type { TransparencyResult, VerificationResult } from "./models.js";
28
29
  * script on the page could otherwise push a version onto it.
29
30
  */
30
31
  export declare const KNOWN_FORMAT_VERSIONS: readonly string[];
32
+ /**
33
+ * Does this format sign the top-level run_id (ADR-031)?
34
+ *
35
+ * v10 onward, and optional even there: a record issued outside a run has none.
36
+ * A record OLDER than v10 that carries one is refused outright rather than
37
+ * signed without it — the field would ride in the JSON beside a signature that
38
+ * says nothing about it. Exported for the audit pack verifier, which asks the
39
+ * same question of a record that carries none: below v10 that says nothing
40
+ * about its run, from v10 it says the record was issued under no run.
41
+ */
42
+ export declare function signatureCoversRunId(version: string): boolean;
31
43
  /** Decode hex: whole bytes, digits in either case, nothing else. Anything else
32
44
  * throws TypeError. It is exported, so its input is a boundary: reading a stray
33
45
  * character as 0, as it once did, turned a typo into different bytes. */
@@ -199,6 +211,18 @@ export declare function registrationQualifier(image: IssuingImage): string;
199
211
  export declare function verifyCertificate(crypto: CryptoOps, certificate: Cert, publicKeys: Map<string, PublicKeyInfo>, options?: VerifyOptions): Promise<VerificationResult>;
200
212
  /** Verify the transparency proof embedded in a certificate. */
201
213
  export declare function verifyTransparency(crypto: CryptoOps, certificate: Cert, publicKeys: Map<string, PublicKeyInfo>): Promise<TransparencyResult>;
214
+ /** The first half of a transparency check, shared with the run record's
215
+ * (run-record.ts): the key's authority at the head's own timestamp, then the
216
+ * head's signature. The tree head carries its own timestamp, so this path
217
+ * always has an anchor; a timestamp that does not read leaves none, which is
218
+ * the same conservative answer as no proof at all. Throws the VerificationError
219
+ * the caller reports. Exported as an internal seam, not from index.ts. */
220
+ export declare function verifyTransparencyHead(crypto: CryptoOps, transparency: Doc, pki: PublicKeyInfo, keyId: string): Promise<void>;
221
+ /** The second half: an already-hashed leaf against the signed head, after the
222
+ * unsigned duplicates beside the proof are held to the signed copies. Throws
223
+ * the VerificationError the caller reports. Exported as an internal seam. */
224
+ export declare function verifyTransparencyInclusion(crypto: CryptoOps, transparency: Doc, leaf: Uint8Array): Promise<void>;
225
+ export declare function transparencyVerdictName(e: VerificationError): string;
202
226
  /** Reconstruct the certificate JSON as it existed at issuance time.
203
227
  *
204
228
  * At issuance: transparency_status = "PENDING", transparency = nil (omitted).
@@ -214,6 +238,30 @@ export declare function issuanceBytes(certificate: Cert): Uint8Array;
214
238
  * this and must be able to prove the two agree. Not re-exported from index.ts,
215
239
  * so it stays off the published API. */
216
240
  export declare function canonicalJson(obj: unknown): Uint8Array;
241
+ type Doc = Record<string, unknown>;
242
+ /** The 16 bytes of a UUID, in any form Go's uuid.Parse reads. Throws TypeError
243
+ * for anything else: a UUID reaches a signed payload only as its canonical
244
+ * string, and an unreadable one must not be signed as whatever text arrived. */
245
+ export declare function parseUuid(text: string): Uint8Array;
246
+ /** The canonical lowercase 8-4-4-4-12 form, as Go's uuid.UUID.String prints:
247
+ * the only form a UUID takes inside a signed payload. */
248
+ export declare function formatUuid(bytes: Uint8Array): string;
249
+ /**
250
+ * Decode a fixed-size byte field — a hash, a key, a signature.
251
+ *
252
+ * Go marshals `[N]byte` as an array of numbers; hand-written documents carry
253
+ * hex or standard base64. A well-formed value of the right size is never
254
+ * ambiguous between the two text forms, because base64 of 32 or 64 bytes ends
255
+ * in padding hex cannot contain.
256
+ *
257
+ * Everything else is refused here, by name: a number, an array element that is
258
+ * not a byte, text that is neither encoding, the wrong length. Decoding it
259
+ * leniently instead wrapped 300 to 44 inside a Uint8Array, produced bytes no
260
+ * signature covers and blamed the signature — or threw from inside atob.
261
+ */
262
+ /** Exported for run-record.ts, whose digests and roots arrive in the same
263
+ * shapes; not published from index.ts. */
264
+ export declare function decodeFixed(value: unknown, path: string, length: number): Uint8Array;
217
265
  /** Normalize a certificate timestamp to the UTC form Go signs.
218
266
  *
219
267
  * Go builds every payload timestamp as `t.UTC().Format("2006-01-02T15:04:05Z")`
@@ -239,6 +287,10 @@ export declare function canonicalJson(obj: unknown): Uint8Array;
239
287
  * always used, for callers normalizing a time that is not a field of a document.
240
288
  */
241
289
  export declare function formatTimestamp(ts: unknown, field?: string): string;
290
+ /** Port of Go's BuildLogLeafPayload (ADR-016 §4). A RUN_RECORD leaf names its
291
+ * run id where the other two name a certificate id, under the same key
292
+ * (ADR-031). Exported for run-record.ts; not published from index.ts. */
293
+ export declare function buildLogLeafPayload(entryType: unknown, certificateId: string, certificateHash: Uint8Array, appendedAt: unknown): Uint8Array;
242
294
  /**
243
295
  * Port of Go's BuildTreeHeadPayload.
244
296
  *
@@ -253,6 +305,12 @@ export declare function formatTimestamp(ts: unknown, field?: string): string;
253
305
  * between two modules of this package, not published surface.
254
306
  */
255
307
  export declare function buildTreeHeadPayload(head: Record<string, unknown>): Uint8Array;
308
+ /** Exported for run-record.ts, whose roots are trees over these leaves. */
309
+ export declare function hashLeaf(crypto: CryptoOps, data: Uint8Array): Promise<Uint8Array>;
310
+ /** The RFC 6962 root over already-hashed leaves, in the order given. Port of
311
+ * Go's treeHash, which like it takes at least one leaf: the empty tree has no
312
+ * root here, and a caller with nothing to commit to says so itself. */
313
+ export declare function merkleRoot(crypto: CryptoOps, leaves: Uint8Array[]): Promise<Uint8Array>;
256
314
  /** Verify a Merkle consistency proof (RFC 6962 Section 2.1.4).
257
315
  *
258
316
  * Port of Go's VerifyConsistency / Python's _verify_consistency.
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@
18
18
  */
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.VALID_REVOCATION_UNKNOWN = exports.PRE_PROTOCOL_12_IMAGES = exports.VALID_KEY_WINDOW_UNKNOWN = exports.VALID_KEY_COMPROMISED_LATER = exports.KEY_COMPROMISED = exports.KEY_OUTSIDE_VALIDITY = exports.KNOWN_FORMAT_VERSIONS = void 0;
21
+ exports.signatureCoversRunId = signatureCoversRunId;
21
22
  exports.hexToBytes = hexToBytes;
22
23
  exports.bytesToHex = bytesToHex;
23
24
  exports.publicKeyFromHex = publicKeyFromHex;
@@ -28,10 +29,19 @@ exports.issuingImage = issuingImage;
28
29
  exports.registrationQualifier = registrationQualifier;
29
30
  exports.verifyCertificate = verifyCertificate;
30
31
  exports.verifyTransparency = verifyTransparency;
32
+ exports.verifyTransparencyHead = verifyTransparencyHead;
33
+ exports.verifyTransparencyInclusion = verifyTransparencyInclusion;
34
+ exports.transparencyVerdictName = transparencyVerdictName;
31
35
  exports.issuanceBytes = issuanceBytes;
32
36
  exports.canonicalJson = canonicalJson;
37
+ exports.parseUuid = parseUuid;
38
+ exports.formatUuid = formatUuid;
39
+ exports.decodeFixed = decodeFixed;
33
40
  exports.formatTimestamp = formatTimestamp;
41
+ exports.buildLogLeafPayload = buildLogLeafPayload;
34
42
  exports.buildTreeHeadPayload = buildTreeHeadPayload;
43
+ exports.hashLeaf = hashLeaf;
44
+ exports.merkleRoot = merkleRoot;
35
45
  exports.verifyConsistency = verifyConsistency;
36
46
  exports.buildCertificateStatusPayload = buildCertificateStatusPayload;
37
47
  exports.buildKeyListPayload = buildKeyListPayload;
@@ -98,6 +108,17 @@ const FORMAT_VERSION_V8 = "8.0";
98
108
  const PAYLOAD_TYPE_ATTESTATION_V9 = "burnledger.attestation.v9";
99
109
  const PAYLOAD_TYPE_VERIFICATION_RECORD_V9 = "burnledger.verification_record.v9";
100
110
  const FORMAT_VERSION_V9 = "9.0";
111
+ // v10 signs one top-level field, run_id: the run-level record (ADR-031) the
112
+ // record was issued under, when it was issued under one. The attestation bytes
113
+ // are v9's; its tag still moves, for the reason every version's does.
114
+ //
115
+ // READ-ONLY IN THIS BUILD, mirroring core, for the runbook-invariant-5 reason
116
+ // spelled out for v8 above. Until issuance moves, a run binds its records by
117
+ // the run record's records_root alone (run-record.ts), which is the binding
118
+ // the verifier checks in every case.
119
+ const PAYLOAD_TYPE_ATTESTATION_V10 = "burnledger.attestation.v10";
120
+ const PAYLOAD_TYPE_VERIFICATION_RECORD_V10 = "burnledger.verification_record.v10";
121
+ const FORMAT_VERSION_V10 = "10.0";
101
122
  const FORMAT_VERSION_V3 = "3.0";
102
123
  /**
103
124
  * The only signature scheme this SDK can check. A v7 record names its own
@@ -123,6 +144,7 @@ exports.KNOWN_FORMAT_VERSIONS = Object.freeze([
123
144
  FORMAT_VERSION_V7,
124
145
  FORMAT_VERSION_V8,
125
146
  FORMAT_VERSION_V9,
147
+ FORMAT_VERSION_V10,
126
148
  ]);
127
149
  /**
128
150
  * Returns the version, or throws if this SDK cannot read it.
@@ -190,6 +212,19 @@ function signatureCoversRecoverableState(version) {
190
212
  function signatureCoversAuthorization(version) {
191
213
  return formatAtLeast(version, FORMAT_VERSION_V9);
192
214
  }
215
+ /**
216
+ * Does this format sign the top-level run_id (ADR-031)?
217
+ *
218
+ * v10 onward, and optional even there: a record issued outside a run has none.
219
+ * A record OLDER than v10 that carries one is refused outright rather than
220
+ * signed without it — the field would ride in the JSON beside a signature that
221
+ * says nothing about it. Exported for the audit pack verifier, which asks the
222
+ * same question of a record that carries none: below v10 that says nothing
223
+ * about its run, from v10 it says the record was issued under no run.
224
+ */
225
+ function signatureCoversRunId(version) {
226
+ return formatAtLeast(version, FORMAT_VERSION_V10);
227
+ }
193
228
  /** The two values a v9 record's per-system authorization may take. */
194
229
  const AUTHORIZATION_CERTIFIED = "certified";
195
230
  const AUTHORIZATION_NONE = "none";
@@ -752,14 +787,25 @@ async function verifyTransparency(crypto, certificate, publicKeys) {
752
787
  if (certificate.transparency == null) {
753
788
  return "NOT_AVAILABLE";
754
789
  }
790
+ const transparency = objectAt(certificate, "transparency", "transparency");
791
+ // Read before the key lookup, as it always was: a proof with no head is
792
+ // reported as that, not as a key the reader does not hold.
793
+ objectAt(transparency, "signed_tree_head", "transparency.signed_tree_head");
755
794
  const issuer = objectAt(certificate, "issuer", "issuer");
756
795
  const keyId = stringAt(issuer, "key_id", "issuer.key_id");
757
796
  const pki = publicKeys.get(keyId);
758
797
  if (pki === undefined)
759
798
  throw new errors_js_1.VerificationError(`unknown issuer key: ${keyId}`);
760
- // The tree head carries its own timestamp, so this path always has an anchor.
761
- requireUsableKey(pki, certificateAnchor(certificate), keyId);
762
- await checkCertificateInclusion(crypto, certificate, pki);
799
+ await verifyTransparencyHead(crypto, transparency, pki, keyId);
800
+ // The leaf is NOT the certificate. It is the canonical log_leaf.v3 payload
801
+ // over entry_type, certificate_id, certificate_hash and appended_at, where
802
+ // certificate_hash is SHA-256 of the issuance-time certificate JSON
803
+ // (ADR-016 §4). v2 hashed the certificate directly, so an issuance and a
804
+ // revocation of the same certificate produced identical leaves and the tree
805
+ // committed to neither the entry type nor when it happened.
806
+ const issuanceData = issuanceBytes(certificate);
807
+ const leafPayload = buildLogLeafPayload(transparency.entry_type, stringAt(certificate, "certificate_id", "certificate_id"), await crypto.sha256(issuanceData), transparency.appended_at);
808
+ await verifyTransparencyInclusion(crypto, transparency, await hashLeaf(crypto, leafPayload));
763
809
  return "INCLUDED";
764
810
  }
765
811
  /**
@@ -824,6 +870,91 @@ async function checkCertificateInclusion(crypto, certificate, pki) {
824
870
  throw new errors_js_1.VerificationError("merkle inclusion proof is invalid");
825
871
  }
826
872
  }
873
+ /** The first half of a transparency check, shared with the run record's
874
+ * (run-record.ts): the key's authority at the head's own timestamp, then the
875
+ * head's signature. The tree head carries its own timestamp, so this path
876
+ * always has an anchor; a timestamp that does not read leaves none, which is
877
+ * the same conservative answer as no proof at all. Throws the VerificationError
878
+ * the caller reports. Exported as an internal seam, not from index.ts. */
879
+ async function verifyTransparencyHead(crypto, transparency, pki, keyId) {
880
+ const sth = objectAt(transparency, "signed_tree_head", "transparency.signed_tree_head");
881
+ let anchor = null;
882
+ if (typeof sth.timestamp === "string") {
883
+ try {
884
+ anchor = parseRfc3339(sth.timestamp);
885
+ }
886
+ catch {
887
+ anchor = null;
888
+ }
889
+ }
890
+ requireUsableKey(pki, anchor, keyId);
891
+ const headPayload = buildTreeHeadPayload(sth);
892
+ const headSig = decodeFixed(sth.signature, "signed_tree_head.signature", 64);
893
+ if (!(await crypto.ed25519Verify(pki.keyBytes, headPayload, headSig))) {
894
+ throw new errors_js_1.VerificationError("tree head signature is invalid");
895
+ }
896
+ }
897
+ /** The second half: an already-hashed leaf against the signed head, after the
898
+ * unsigned duplicates beside the proof are held to the signed copies. Throws
899
+ * the VerificationError the caller reports. Exported as an internal seam. */
900
+ async function verifyTransparencyInclusion(crypto, transparency, leaf) {
901
+ const sth = objectAt(transparency, "signed_tree_head", "transparency.signed_tree_head");
902
+ const proofHashes = arrayAt(transparency, "inclusion_proof", "transparency.inclusion_proof").map((h, i) => decodeFixed(h, `transparency.inclusion_proof[${i}]`, 32));
903
+ const root = decodeFixed(sth.root_hash, "signed_tree_head.root_hash", 32);
904
+ // `?? 0` is not a convenience: encoding/json leaves 0 in Go's uint64 fields
905
+ // for an explicit null and for an absent key rather than failing, so refusing
906
+ // either would reject documents the reference accepts — the same class of
907
+ // divergence as #452. An offline verifier is only useful while it agrees.
908
+ const index = requireUint(transparency.entry_index ?? 0, "entry_index");
909
+ const treeSize = requireUint(sth.tree_size ?? 0, "signed_tree_head.tree_size");
910
+ // transparency.tree_size duplicates the signed one but carries no signature:
911
+ // BuildTreeHeadPayload covers only the copy inside signed_tree_head. Go used
912
+ // to verify against the unsigned copy while this SDK used the signed one, so
913
+ // the same document got two verdicts (#456). Both now require the two to agree
914
+ // and then verify against the signed copy — the server always writes them
915
+ // equal, so this rejects only edited documents.
916
+ const unsignedSize = requireUint(transparency.tree_size ?? 0, "transparency.tree_size");
917
+ if (unsignedSize !== treeSize) {
918
+ throw new errors_js_1.VerificationError(`transparency.tree_size (${unsignedSize}) does not match the signed tree head ` +
919
+ `(${treeSize}); it is not covered by any signature`);
920
+ }
921
+ // Same rule for the log id (Q31). transparency.log_id sits beside log_url and
922
+ // is the field a reader looks at to answer "which log is this?", and nothing
923
+ // signs it — so a holder could relabel a genuine proof as belonging to a
924
+ // different log while every signature still checked out. A head predating log
925
+ // ids has neither side set and passes.
926
+ const signedLogId = sth.log_id ?? "";
927
+ const unsignedLogId = transparency.log_id ?? "";
928
+ if (unsignedLogId !== signedLogId) {
929
+ throw new errors_js_1.VerificationError(`transparency.log_id (${JSON.stringify(unsignedLogId)}) does not match the signed ` +
930
+ `tree head (${JSON.stringify(signedLogId)}); it is not covered by any signature`);
931
+ }
932
+ if (!(await verifyInclusion(crypto, leaf, index, treeSize, proofHashes, root))) {
933
+ throw new errors_js_1.VerificationError("merkle inclusion proof is invalid");
934
+ }
935
+ }
936
+ /** The refusals of verifyTransparencyHead and verifyTransparencyInclusion under
937
+ * core.TransparencyResult's names, for the verifiers that report a pack or a
938
+ * run record beside Go's verdicts rather than by throwing. The key verdicts
939
+ * collapse to KEY_UNUSABLE as they do in Go, whose transparency result does not
940
+ * say which key problem. Anything unmatched is a field this SDK could not read:
941
+ * UNREADABLE_DOCUMENT, never a tampering claim. */
942
+ const TRANSPARENCY_VERDICTS = [
943
+ ["issuer key is compromised:", "KEY_UNUSABLE"],
944
+ ["issuer key was not valid when it signed:", "KEY_UNUSABLE"],
945
+ ["issuer key has an unusable status", "KEY_UNUSABLE"],
946
+ ["tree head signature is invalid", "INVALID_TREE_HEAD_SIGNATURE"],
947
+ ["transparency.tree_size (", "INVALID_INCLUSION_PROOF"],
948
+ ["transparency.log_id (", "INVALID_INCLUSION_PROOF"],
949
+ ["merkle inclusion proof is invalid", "INVALID_INCLUSION_PROOF"],
950
+ ];
951
+ function transparencyVerdictName(e) {
952
+ for (const [prefix, verdict] of TRANSPARENCY_VERDICTS) {
953
+ if (e.message.startsWith(prefix))
954
+ return verdict;
955
+ }
956
+ return "UNREADABLE_DOCUMENT";
957
+ }
827
958
  // ---------------------------------------------------------------------------
828
959
  // Issuance bytes — matches Go's core.IssuanceBytes(cert)
829
960
  // ---------------------------------------------------------------------------
@@ -915,6 +1046,45 @@ function arrayAt(parent, key, path) {
915
1046
  throw new errors_js_1.VerificationError(`${path} is not an array`);
916
1047
  return Array.from(value);
917
1048
  }
1049
+ /** A UUID in the forms Go's uuid.Parse accepts: 8-4-4-4-12 hex, the same in
1050
+ * braces or behind "urn:uuid:", or 32 bare hex digits. */
1051
+ const UUID_RE = /^(?:urn:uuid:)?\{?([0-9a-fA-F]{8})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{12})\}?$/;
1052
+ const UUID_BARE_RE = /^[0-9a-fA-F]{32}$/;
1053
+ /** The 16 bytes of a UUID, in any form Go's uuid.Parse reads. Throws TypeError
1054
+ * for anything else: a UUID reaches a signed payload only as its canonical
1055
+ * string, and an unreadable one must not be signed as whatever text arrived. */
1056
+ function parseUuid(text) {
1057
+ if (typeof text !== "string")
1058
+ throw new TypeError(`uuid is not a string: ${typeof text}`);
1059
+ const m = UUID_RE.exec(text);
1060
+ const hex = m !== null ? m.slice(1).join("") : UUID_BARE_RE.test(text) ? text : null;
1061
+ if (hex === null)
1062
+ throw new TypeError(`not a UUID: ${JSON.stringify(text)}`);
1063
+ return hexToBytes(hex);
1064
+ }
1065
+ /** The canonical lowercase 8-4-4-4-12 form, as Go's uuid.UUID.String prints:
1066
+ * the only form a UUID takes inside a signed payload. */
1067
+ function formatUuid(bytes) {
1068
+ if (bytes.length !== 16)
1069
+ throw new TypeError(`uuid is ${bytes.length} bytes, expected 16`);
1070
+ const h = bytesToHex(bytes);
1071
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
1072
+ }
1073
+ /** Read a UUID field as Go's uuid.UUID unmarshals it, refusing with a
1074
+ * VerificationError what encoding/json would refuse to parse. */
1075
+ function uuidAt(parent, key, path) {
1076
+ const v = parent[key];
1077
+ if (typeof v !== "string")
1078
+ throw new errors_js_1.VerificationError(`${path} is not a string`);
1079
+ try {
1080
+ return formatUuid(parseUuid(v));
1081
+ }
1082
+ catch (e) {
1083
+ if (e instanceof TypeError)
1084
+ throw new errors_js_1.VerificationError(`${path} is not a UUID: ${JSON.stringify(v)}`);
1085
+ throw e;
1086
+ }
1087
+ }
918
1088
  const HEX_TEXT_RE = /^(?:[0-9a-fA-F]{2})*$/;
919
1089
  const BASE64_TEXT_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
920
1090
  /**
@@ -930,6 +1100,8 @@ const BASE64_TEXT_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/
930
1100
  * leniently instead wrapped 300 to 44 inside a Uint8Array, produced bytes no
931
1101
  * signature covers and blamed the signature — or threw from inside atob.
932
1102
  */
1103
+ /** Exported for run-record.ts, whose digests and roots arrive in the same
1104
+ * shapes; not published from index.ts. */
933
1105
  function decodeFixed(value, path, length) {
934
1106
  if (value === undefined || value === null)
935
1107
  throw new errors_js_1.VerificationError(`${path} is missing`);
@@ -1167,6 +1339,8 @@ function requireMeasured(value, field, systemName, version) {
1167
1339
  /** Domain separator for the record itself, by certificate format version. */
1168
1340
  function certificatePayloadType(version) {
1169
1341
  checkFormatVersion(version);
1342
+ if (version === FORMAT_VERSION_V10)
1343
+ return PAYLOAD_TYPE_VERIFICATION_RECORD_V10;
1170
1344
  if (version === FORMAT_VERSION_V9)
1171
1345
  return PAYLOAD_TYPE_VERIFICATION_RECORD_V9;
1172
1346
  if (version === FORMAT_VERSION_V8)
@@ -1192,6 +1366,8 @@ function attestationPayloadType(version) {
1192
1366
  // Equality here, deliberately: each format has its OWN separator, so this is
1193
1367
  // a lookup rather than a "this version onward" question. v3 and v4 share one
1194
1368
  // because their attestation bytes are identical.
1369
+ if (version === FORMAT_VERSION_V10)
1370
+ return PAYLOAD_TYPE_ATTESTATION_V10;
1195
1371
  if (version === FORMAT_VERSION_V9)
1196
1372
  return PAYLOAD_TYPE_ATTESTATION_V9;
1197
1373
  if (version === FORMAT_VERSION_V8)
@@ -1321,11 +1497,25 @@ function buildCertificatePayload(cert) {
1321
1497
  version: stringAt(scope, "version", "scope.version"),
1322
1498
  };
1323
1499
  }
1500
+ // Signed from v10 when present. On an older format the field is refused
1501
+ // rather than dropped from the bytes: core.BuildCertificatePayload returns an
1502
+ // error there and the verdict is INVALID_CERTIFICATE_SIGNATURE, so the same
1503
+ // words open this message for the audit pack verifier to read it back as
1504
+ // that verdict. A JSON null is an absent run, as Go's *uuid.UUID reads it.
1505
+ if (cert.run_id != null) {
1506
+ if (!signatureCoversRunId(version)) {
1507
+ throw new errors_js_1.VerificationError(`certificate signature is invalid: the record carries run_id, which format ${version} does not ` +
1508
+ `sign (run_id is signed from ${FORMAT_VERSION_V10}), so no signature covers the document as presented`);
1509
+ }
1510
+ payload.run_id = uuidAt(cert, "run_id", "run_id");
1511
+ }
1324
1512
  return canonicalJson(payload);
1325
1513
  }
1326
- /** Port of Go's BuildLogLeafPayload (ADR-016 §4). */
1514
+ /** Port of Go's BuildLogLeafPayload (ADR-016 §4). A RUN_RECORD leaf names its
1515
+ * run id where the other two name a certificate id, under the same key
1516
+ * (ADR-031). Exported for run-record.ts; not published from index.ts. */
1327
1517
  function buildLogLeafPayload(entryType, certificateId, certificateHash, appendedAt) {
1328
- if (entryType !== "CERTIFICATE" && entryType !== "REVOCATION") {
1518
+ if (entryType !== "CERTIFICATE" && entryType !== "REVOCATION" && entryType !== "RUN_RECORD") {
1329
1519
  throw new errors_js_1.VerificationError(`invalid log entry_type: ${String(entryType)}`);
1330
1520
  }
1331
1521
  const payload = {
@@ -1393,9 +1583,21 @@ function buildTreeHeadPayload(head) {
1393
1583
  // ---------------------------------------------------------------------------
1394
1584
  // Merkle tree (RFC 6962) — ports of core/merkle.go
1395
1585
  // ---------------------------------------------------------------------------
1586
+ /** Exported for run-record.ts, whose roots are trees over these leaves. */
1396
1587
  async function hashLeaf(crypto, data) {
1397
1588
  return crypto.sha256(concatBytes(new Uint8Array([0x00]), data));
1398
1589
  }
1590
+ /** The RFC 6962 root over already-hashed leaves, in the order given. Port of
1591
+ * Go's treeHash, which like it takes at least one leaf: the empty tree has no
1592
+ * root here, and a caller with nothing to commit to says so itself. */
1593
+ async function merkleRoot(crypto, leaves) {
1594
+ if (leaves.length === 0)
1595
+ throw new RangeError("merkleRoot: no leaves");
1596
+ if (leaves.length === 1)
1597
+ return leaves[0];
1598
+ const k = splitPoint(leaves.length);
1599
+ return hashNode(crypto, await merkleRoot(crypto, leaves.slice(0, k)), await merkleRoot(crypto, leaves.slice(k)));
1600
+ }
1399
1601
  async function hashNode(crypto, left, right) {
1400
1602
  const prefix = new Uint8Array([0x01]);
1401
1603
  return crypto.sha256(concatBytes(prefix, concatBytes(left, right)));