@peac/protocol 0.12.13 → 0.13.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.
- package/dist/discovery.d.ts +21 -6
- package/dist/discovery.d.ts.map +1 -1
- package/dist/document-binding.d.ts +94 -0
- package/dist/document-binding.d.ts.map +1 -0
- package/dist/index.cjs +99 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +64 -8
- package/dist/index.mjs.map +1 -1
- package/dist/jwks-resolver.d.ts +18 -0
- package/dist/jwks-resolver.d.ts.map +1 -1
- package/dist/policy-binding.d.ts +33 -24
- package/dist/policy-binding.d.ts.map +1 -1
- package/dist/verifier-types.d.ts +51 -0
- package/dist/verifier-types.d.ts.map +1 -1
- package/dist/verify-local.cjs +15 -2
- package/dist/verify-local.cjs.map +1 -1
- package/dist/verify-local.d.ts +34 -0
- package/dist/verify-local.d.ts.map +1 -1
- package/dist/verify-local.mjs +15 -2
- package/dist/verify-local.mjs.map +1 -1
- package/package.json +4 -4
package/dist/discovery.d.ts
CHANGED
|
@@ -43,17 +43,32 @@ export declare function parsePolicyManifest(text: string, contentType?: string):
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function fetchPolicyManifest(baseUrl: string): Promise<PEACPolicyManifest>;
|
|
45
45
|
/**
|
|
46
|
-
* @deprecated
|
|
46
|
+
* @deprecated Parses a legacy dual-purpose `peac.txt` manifest that carried
|
|
47
|
+
* both policy hints and key-discovery fields (`verify`, `jwks`). This shape
|
|
48
|
+
* is retired: `peac.txt` is a policy-document surface per
|
|
49
|
+
* `docs/specs/PEAC-TXT.md`, and the normative key-discovery chain is
|
|
50
|
+
* `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS
|
|
51
|
+
* (see `docs/specs/PEAC-ISSUER.md`). Use `parseIssuerConfig` for issuer
|
|
52
|
+
* metadata, and `@peac/policy-kit.parsePolicyDocument` for `peac-policy/0.1`
|
|
53
|
+
* documents referenced from `peac.txt`. Removal target: next cleanup release.
|
|
47
54
|
*
|
|
48
|
-
* Parse a PEAC discovery manifest from YAML-like text.
|
|
49
|
-
*
|
|
55
|
+
* Parse a PEAC discovery manifest from YAML-like text. Retained only for
|
|
56
|
+
* backward compatibility with callers that have not yet migrated.
|
|
50
57
|
*/
|
|
51
58
|
export declare function parseDiscovery(text: string): PEACDiscovery;
|
|
52
59
|
/**
|
|
53
|
-
* @deprecated
|
|
60
|
+
* @deprecated Fetches /.well-known/peac.txt and parses it as a legacy
|
|
61
|
+
* dual-purpose manifest. This shape is retired: `peac.txt` is a policy
|
|
62
|
+
* document (see `docs/specs/PEAC-TXT.md`) and key discovery flows through
|
|
63
|
+
* `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS
|
|
64
|
+
* (see `docs/specs/PEAC-ISSUER.md`). Use `fetchIssuerConfig` for issuer
|
|
65
|
+
* metadata. To parse the `peac-policy/0.1` document referenced from
|
|
66
|
+
* `peac.txt`, fetch its bytes and call
|
|
67
|
+
* `@peac/policy-kit.parsePolicyDocument`. Removal target: next cleanup
|
|
68
|
+
* release.
|
|
54
69
|
*
|
|
55
|
-
* Fetch and parse PEAC discovery from an issuer URL.
|
|
56
|
-
*
|
|
70
|
+
* Fetch and parse PEAC discovery from an issuer URL. Retained only for
|
|
71
|
+
* backward compatibility with callers that have not yet migrated.
|
|
57
72
|
*/
|
|
58
73
|
export declare function fetchDiscovery(issuerUrl: string): Promise<PEACDiscovery>;
|
|
59
74
|
//# sourceMappingURL=discovery.d.ts.map
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EAOd,MAAM,cAAc,CAAC;AAMtB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,gBAAgB,CAoGzE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsCpF;AAiBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAoD1F;AAkFD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA+CtF;AAMD
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EAOd,MAAM,cAAc,CAAC;AAMtB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,gBAAgB,CAoGzE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsCpF;AAiBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAoD1F;AAkFD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA+CtF;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAkD1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2B9E"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document binding utilities (Layer 3)
|
|
3
|
+
*
|
|
4
|
+
* Generalized binding helpers for `peac-policy/0.1` policy documents,
|
|
5
|
+
* x402 PR #1986 `terms` representations, and any other referenced
|
|
6
|
+
* document. Three-state semantics (`verified` / `failed` / `unavailable`)
|
|
7
|
+
* are reused from `policy-binding`.
|
|
8
|
+
*
|
|
9
|
+
* Helper-naming rule (normative, see `docs/specs/DOCUMENT-BINDING.md`):
|
|
10
|
+
* - `computeJsonDocumentDigestJcs` is JSON-only (RFC 8785 JCS + SHA-256).
|
|
11
|
+
* - `computeTextDocumentDigestUtf8` is text-only (UTF-8 with `\n` line
|
|
12
|
+
* endings + NFC, preserving all other bytes including trailing
|
|
13
|
+
* whitespace).
|
|
14
|
+
* - `computeDocumentDigest` is the umbrella dispatcher.
|
|
15
|
+
*
|
|
16
|
+
* The `Jcs` suffix is reserved for JSON-only helpers; text-only helpers
|
|
17
|
+
* name their scheme. This prevents the `aipref/resolver.ts` historical
|
|
18
|
+
* bug where a sort-keys `JSON.stringify` digest was labelled `JCS-SHA256`.
|
|
19
|
+
*
|
|
20
|
+
* The `policy_binding` field on the verifier report is the byte-stable
|
|
21
|
+
* legacy mirror of `bindings.policy`; both are populated identically.
|
|
22
|
+
*
|
|
23
|
+
* @see docs/specs/DOCUMENT-BINDING.md
|
|
24
|
+
* @see packages/protocol/src/policy-binding.ts
|
|
25
|
+
*/
|
|
26
|
+
import type { JsonValue } from '@peac/kernel';
|
|
27
|
+
import type { PolicyBindingStatus } from './verifier-types.js';
|
|
28
|
+
export type DocumentRepresentation = 'uri' | 'markdown' | 'plaintext' | 'json';
|
|
29
|
+
/**
|
|
30
|
+
* Compute the RFC 8785 JCS canonicalization + SHA-256 digest of a JSON
|
|
31
|
+
* document. Returns the canonical PEAC self-describing hash format
|
|
32
|
+
* `'sha256:<64 lowercase hex>'`. Suitable for `peac-policy/0.1` policy
|
|
33
|
+
* docs and any other JSON-shaped document carried in or referenced from
|
|
34
|
+
* a record.
|
|
35
|
+
*/
|
|
36
|
+
export declare function computeJsonDocumentDigestJcs(value: JsonValue): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Compute the SHA-256 digest of a UTF-8 text document after the minimal
|
|
39
|
+
* canonical normalization documented in `docs/specs/DOCUMENT-BINDING.md`:
|
|
40
|
+
*
|
|
41
|
+
* 1. Normalize line endings to `\n` (strip `\r` before `\n`; replace a
|
|
42
|
+
* lone `\r` with `\n`).
|
|
43
|
+
* 2. Normalize Unicode to NFC.
|
|
44
|
+
* 3. Preserve all other bytes exactly.
|
|
45
|
+
*
|
|
46
|
+
* Trailing whitespace is **not** stripped. Blank lines are **not**
|
|
47
|
+
* collapsed. Case is **not** normalized. The intent is a deterministic
|
|
48
|
+
* representation envelope identity, not a "pretty-print before hashing"
|
|
49
|
+
* scheme. Text representations like `markdown` and `plaintext` are
|
|
50
|
+
* frequently signed by publishers, and silent transformations would
|
|
51
|
+
* break those signatures.
|
|
52
|
+
*
|
|
53
|
+
* Returns the canonical PEAC self-describing hash format
|
|
54
|
+
* `'sha256:<64 lowercase hex>'`.
|
|
55
|
+
*
|
|
56
|
+
* @param bytes UTF-8 string to hash (single representation envelope).
|
|
57
|
+
* @param representation Tag recorded by the caller; not currently
|
|
58
|
+
* altering the hash but reserved so future representation-specific
|
|
59
|
+
* normalization rules can be added without renaming the helper.
|
|
60
|
+
*/
|
|
61
|
+
export declare function computeTextDocumentDigestUtf8(bytes: string, _representation: 'markdown' | 'plaintext'): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Umbrella dispatcher. Selects the correct scheme-specific helper from
|
|
64
|
+
* `input.representation`.
|
|
65
|
+
*
|
|
66
|
+
* For `representation: 'uri'`, the bytes the caller fetched from the URI
|
|
67
|
+
* MUST be supplied via `bytes`. Without bytes the dispatcher returns
|
|
68
|
+
* `'unavailable'`: the URI alone is not enough to bind, and PEAC does
|
|
69
|
+
* not perform network I/O from this layer (callers fetch under their own
|
|
70
|
+
* SSRF / redirect / timeout policy). When bytes are supplied for a URI,
|
|
71
|
+
* they are hashed via the text helper.
|
|
72
|
+
*/
|
|
73
|
+
export declare function computeDocumentDigest(input: {
|
|
74
|
+
representation: 'json';
|
|
75
|
+
value: JsonValue;
|
|
76
|
+
} | {
|
|
77
|
+
representation: 'markdown' | 'plaintext';
|
|
78
|
+
bytes: string;
|
|
79
|
+
} | {
|
|
80
|
+
representation: 'uri';
|
|
81
|
+
uri: string;
|
|
82
|
+
bytes?: string;
|
|
83
|
+
}): Promise<string | 'unavailable'>;
|
|
84
|
+
/**
|
|
85
|
+
* Three-state binding check. Same semantics as `checkPolicyBinding`:
|
|
86
|
+
*
|
|
87
|
+
* - `'unavailable'` when either digest is missing.
|
|
88
|
+
* - `'verified'` when both digests are present and match exactly.
|
|
89
|
+
* - `'failed'` when both digests are present but do not match.
|
|
90
|
+
*
|
|
91
|
+
* The legacy `checkPolicyBinding` export delegates to this function.
|
|
92
|
+
*/
|
|
93
|
+
export declare function checkDocumentBinding(receiptDigest: string | undefined, localDigest: string | undefined): PolicyBindingStatus;
|
|
94
|
+
//# sourceMappingURL=document-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-binding.d.ts","sourceRoot":"","sources":["../src/document-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG/D,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,UAAU,GAAG,WAAW,GACxC,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EACD;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAC5C;IAAE,cAAc,EAAE,UAAU,GAAG,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,cAAc,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACzD,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,CAWjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,mBAAmB,CAKrB"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var uuidv7 = require('uuidv7');
|
|
4
|
-
var crypto = require('@peac/crypto');
|
|
4
|
+
var crypto$1 = require('@peac/crypto');
|
|
5
5
|
var zod = require('zod');
|
|
6
6
|
var schema = require('@peac/schema');
|
|
7
|
-
var crypto$
|
|
7
|
+
var crypto$2 = require('crypto');
|
|
8
8
|
var kernel = require('@peac/kernel');
|
|
9
9
|
|
|
10
10
|
// src/issue.ts
|
|
@@ -20,7 +20,7 @@ function fireTelemetryHook(fn, input) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
function hashReceipt(jws) {
|
|
23
|
-
const hash = crypto$
|
|
23
|
+
const hash = crypto$2.createHash("sha256").update(jws).digest("hex");
|
|
24
24
|
return `sha256:${hash.slice(0, 16)}`;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -163,7 +163,7 @@ async function issueWire01(options) {
|
|
|
163
163
|
}
|
|
164
164
|
const validatedSnapshot = schema.validateSubjectSnapshot(options.subject_snapshot);
|
|
165
165
|
const startTime = performance.now();
|
|
166
|
-
const jws = await crypto.sign(claims, options.privateKey, options.kid);
|
|
166
|
+
const jws = await crypto$1.sign(claims, options.privateKey, options.kid);
|
|
167
167
|
fireTelemetryHook(options.telemetry?.onReceiptIssued, {
|
|
168
168
|
receiptHash: hashReceipt(jws),
|
|
169
169
|
issuer: options.iss,
|
|
@@ -225,7 +225,7 @@ async function issueWire02(options) {
|
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
|
-
const jws = await crypto.signWire02(claims, options.privateKey, options.kid);
|
|
228
|
+
const jws = await crypto$1.signWire02(claims, options.privateKey, options.kid);
|
|
229
229
|
return { jws };
|
|
230
230
|
}
|
|
231
231
|
function parseIssuerConfig(json) {
|
|
@@ -991,8 +991,23 @@ async function fetchPointerSafe(pointerUrl, options) {
|
|
|
991
991
|
}
|
|
992
992
|
|
|
993
993
|
// src/jwks-resolver.ts
|
|
994
|
-
var
|
|
995
|
-
var
|
|
994
|
+
var HARD_CACHE_TTL_MS_BUILTIN = 5 * 60 * 1e3;
|
|
995
|
+
var HARD_MAX_CACHE_ENTRIES_BUILTIN = 1e3;
|
|
996
|
+
function __parseEnvPositiveInt(name, fallback, env = typeof process !== "undefined" ? process.env : {}) {
|
|
997
|
+
const raw = env[name];
|
|
998
|
+
if (!raw) return fallback;
|
|
999
|
+
if (!/^[1-9][0-9]*$/.test(raw)) return fallback;
|
|
1000
|
+
const parsed = parseInt(raw, 10);
|
|
1001
|
+
return parsed > 0 ? parsed : fallback;
|
|
1002
|
+
}
|
|
1003
|
+
var DEFAULT_CACHE_TTL_MS = __parseEnvPositiveInt(
|
|
1004
|
+
"PEAC_JWKS_CACHE_TTL_MS",
|
|
1005
|
+
HARD_CACHE_TTL_MS_BUILTIN
|
|
1006
|
+
);
|
|
1007
|
+
var DEFAULT_MAX_CACHE_ENTRIES = __parseEnvPositiveInt(
|
|
1008
|
+
"PEAC_JWKS_CACHE_MAX_ENTRIES",
|
|
1009
|
+
HARD_MAX_CACHE_ENTRIES_BUILTIN
|
|
1010
|
+
);
|
|
996
1011
|
var jwksCache = /* @__PURE__ */ new Map();
|
|
997
1012
|
var KID_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
998
1013
|
var MAX_KID_THUMBPRINT_ENTRIES = 1e4;
|
|
@@ -1267,7 +1282,7 @@ async function verifyReceipt(optionsOrJws) {
|
|
|
1267
1282
|
const startTime = performance.now();
|
|
1268
1283
|
let jwksFetchTime;
|
|
1269
1284
|
try {
|
|
1270
|
-
const { header, payload } = crypto.decode(receiptJws);
|
|
1285
|
+
const { header, payload } = crypto$1.decode(receiptJws);
|
|
1271
1286
|
const constraintResult = schema.validateKernelConstraints(payload);
|
|
1272
1287
|
if (!constraintResult.valid) {
|
|
1273
1288
|
const v = constraintResult.violations[0];
|
|
@@ -1343,7 +1358,7 @@ async function verifyReceipt(optionsOrJws) {
|
|
|
1343
1358
|
};
|
|
1344
1359
|
}
|
|
1345
1360
|
const publicKey = jwkToPublicKey(jwk);
|
|
1346
|
-
const result = await crypto.verify(receiptJws, publicKey);
|
|
1361
|
+
const result = await crypto$1.verify(receiptJws, publicKey);
|
|
1347
1362
|
if (!result.valid) {
|
|
1348
1363
|
const durationMs = performance.now() - startTime;
|
|
1349
1364
|
fireTelemetryHook(telemetry?.onReceiptVerified, {
|
|
@@ -1447,10 +1462,17 @@ function sanitizeParseIssues(issues) {
|
|
|
1447
1462
|
}));
|
|
1448
1463
|
}
|
|
1449
1464
|
async function verifyLocal(jws, publicKey, options = {}) {
|
|
1450
|
-
const {
|
|
1465
|
+
const {
|
|
1466
|
+
issuer,
|
|
1467
|
+
subjectUri,
|
|
1468
|
+
maxClockSkew = 300,
|
|
1469
|
+
strictness = "strict",
|
|
1470
|
+
policyDigest,
|
|
1471
|
+
bindings: callerBindings
|
|
1472
|
+
} = options;
|
|
1451
1473
|
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
1452
1474
|
try {
|
|
1453
|
-
const result = await crypto.verify(jws, publicKey);
|
|
1475
|
+
const result = await crypto$1.verify(jws, publicKey);
|
|
1454
1476
|
if (!result.valid) {
|
|
1455
1477
|
return {
|
|
1456
1478
|
valid: false,
|
|
@@ -1597,6 +1619,11 @@ async function verifyLocal(jws, publicKey, options = {}) {
|
|
|
1597
1619
|
}
|
|
1598
1620
|
};
|
|
1599
1621
|
}
|
|
1622
|
+
const bindings = {
|
|
1623
|
+
policy: bindingStatus,
|
|
1624
|
+
...callerBindings?.terms !== void 0 && { terms: callerBindings.terms },
|
|
1625
|
+
...callerBindings?.documents !== void 0 && callerBindings.documents.length > 0 && { documents: callerBindings.documents }
|
|
1626
|
+
};
|
|
1600
1627
|
return {
|
|
1601
1628
|
valid: true,
|
|
1602
1629
|
variant: "wire-02",
|
|
@@ -1604,7 +1631,8 @@ async function verifyLocal(jws, publicKey, options = {}) {
|
|
|
1604
1631
|
kid: result.header.kid,
|
|
1605
1632
|
wireVersion: "0.2",
|
|
1606
1633
|
warnings: schema.sortWarnings(accumulatedWarnings),
|
|
1607
|
-
policy_binding: bindingStatus
|
|
1634
|
+
policy_binding: bindingStatus,
|
|
1635
|
+
bindings
|
|
1608
1636
|
};
|
|
1609
1637
|
}
|
|
1610
1638
|
return {
|
|
@@ -1709,16 +1737,44 @@ function setVaryPurposeHeader(headers) {
|
|
|
1709
1737
|
headers.set("Vary", schema.PEAC_PURPOSE_HEADER);
|
|
1710
1738
|
}
|
|
1711
1739
|
}
|
|
1712
|
-
async function
|
|
1713
|
-
const hex = await crypto.jcsHash(
|
|
1740
|
+
async function computeJsonDocumentDigestJcs(value) {
|
|
1741
|
+
const hex = await crypto$1.jcsHash(value);
|
|
1714
1742
|
return `${kernel.HASH.prefix}${hex}`;
|
|
1715
1743
|
}
|
|
1716
|
-
function
|
|
1744
|
+
async function computeTextDocumentDigestUtf8(bytes, _representation) {
|
|
1745
|
+
const normalized = bytes.replace(/\r\n/g, "\n").replace(/\r/g, "\n").normalize("NFC");
|
|
1746
|
+
const buf = new TextEncoder().encode(normalized);
|
|
1747
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", buf);
|
|
1748
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1749
|
+
const hex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1750
|
+
return `${kernel.HASH.prefix}${hex}`;
|
|
1751
|
+
}
|
|
1752
|
+
async function computeDocumentDigest(input) {
|
|
1753
|
+
switch (input.representation) {
|
|
1754
|
+
case "json":
|
|
1755
|
+
return computeJsonDocumentDigestJcs(input.value);
|
|
1756
|
+
case "markdown":
|
|
1757
|
+
case "plaintext":
|
|
1758
|
+
return computeTextDocumentDigestUtf8(input.bytes, input.representation);
|
|
1759
|
+
case "uri":
|
|
1760
|
+
if (typeof input.bytes !== "string") return "unavailable";
|
|
1761
|
+
return computeTextDocumentDigestUtf8(input.bytes);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
function checkDocumentBinding(receiptDigest, localDigest) {
|
|
1717
1765
|
if (receiptDigest === void 0 || localDigest === void 0) {
|
|
1718
1766
|
return "unavailable";
|
|
1719
1767
|
}
|
|
1720
1768
|
return schema.verifyPolicyBinding(receiptDigest, localDigest);
|
|
1721
1769
|
}
|
|
1770
|
+
|
|
1771
|
+
// src/policy-binding.ts
|
|
1772
|
+
async function computePolicyDigestJcs(policy) {
|
|
1773
|
+
return computeJsonDocumentDigestJcs(policy);
|
|
1774
|
+
}
|
|
1775
|
+
function checkPolicyBinding(receiptDigest, localDigest) {
|
|
1776
|
+
return checkDocumentBinding(receiptDigest, localDigest);
|
|
1777
|
+
}
|
|
1722
1778
|
var DEFAULT_VERIFIER_LIMITS = {
|
|
1723
1779
|
max_receipt_bytes: kernel.VERIFIER_LIMITS.maxReceiptBytes,
|
|
1724
1780
|
max_jwks_bytes: kernel.VERIFIER_LIMITS.maxJwksBytes,
|
|
@@ -1861,7 +1917,7 @@ var VerificationReportBuilder = class {
|
|
|
1861
1917
|
* @param type - Input type
|
|
1862
1918
|
*/
|
|
1863
1919
|
async setInputAsync(receiptBytes, type = "receipt_jws") {
|
|
1864
|
-
const digestHex = await crypto.sha256Hex(receiptBytes);
|
|
1920
|
+
const digestHex = await crypto$1.sha256Hex(receiptBytes);
|
|
1865
1921
|
return this.setInputWithDigest(digestHex, type);
|
|
1866
1922
|
}
|
|
1867
1923
|
/**
|
|
@@ -2044,11 +2100,11 @@ function createReportBuilder(policy) {
|
|
|
2044
2100
|
}
|
|
2045
2101
|
async function computeReceiptDigest(receiptBytes) {
|
|
2046
2102
|
const bytes = typeof receiptBytes === "string" ? new TextEncoder().encode(receiptBytes) : receiptBytes;
|
|
2047
|
-
return crypto.sha256Hex(bytes);
|
|
2103
|
+
return crypto$1.sha256Hex(bytes);
|
|
2048
2104
|
}
|
|
2049
2105
|
async function buildFailureReport(policy, receiptBytes, reason, failedCheckId, errorCode, detail, options) {
|
|
2050
2106
|
const bytes = typeof receiptBytes === "string" ? new TextEncoder().encode(receiptBytes) : receiptBytes;
|
|
2051
|
-
const digestHex = await crypto.sha256Hex(bytes);
|
|
2107
|
+
const digestHex = await crypto$1.sha256Hex(bytes);
|
|
2052
2108
|
const builder = createReportBuilder(policy).setInputWithDigest(digestHex).failure(reason, options?.issuer, options?.kid);
|
|
2053
2109
|
const failedIndex = CHECK_IDS.indexOf(failedCheckId);
|
|
2054
2110
|
for (let i = 0; i < CHECK_IDS.length; i++) {
|
|
@@ -2066,7 +2122,7 @@ async function buildFailureReport(policy, receiptBytes, reason, failedCheckId, e
|
|
|
2066
2122
|
}
|
|
2067
2123
|
async function buildSuccessReport(policy, receiptBytes, issuer, kid, checkDetails, options) {
|
|
2068
2124
|
const bytes = typeof receiptBytes === "string" ? new TextEncoder().encode(receiptBytes) : receiptBytes;
|
|
2069
|
-
const digestHex = await crypto.sha256Hex(bytes);
|
|
2125
|
+
const digestHex = await crypto$1.sha256Hex(bytes);
|
|
2070
2126
|
const builder = createReportBuilder(policy).setInputWithDigest(digestHex).success(issuer, kid);
|
|
2071
2127
|
for (const checkId of CHECK_IDS) {
|
|
2072
2128
|
if (checkId === "issuer.discovery" && policy.mode === "offline_only") {
|
|
@@ -2148,7 +2204,7 @@ async function verifyReceiptCore(options) {
|
|
|
2148
2204
|
} = options;
|
|
2149
2205
|
const receiptJws = typeof receipt === "string" ? receipt : new TextDecoder().decode(receipt);
|
|
2150
2206
|
const receiptBytes = typeof receipt === "string" ? new TextEncoder().encode(receipt) : receipt;
|
|
2151
|
-
const receiptDigestHex = await crypto.sha256Hex(receiptBytes);
|
|
2207
|
+
const receiptDigestHex = await crypto$1.sha256Hex(receiptBytes);
|
|
2152
2208
|
const builder = createReportBuilder(policy);
|
|
2153
2209
|
builder.setInputWithDigest(receiptDigestHex);
|
|
2154
2210
|
const nowSeconds = referenceTime ?? Math.floor(Date.now() / 1e3);
|
|
@@ -2158,7 +2214,7 @@ async function verifyReceiptCore(options) {
|
|
|
2158
2214
|
let header;
|
|
2159
2215
|
let payload;
|
|
2160
2216
|
try {
|
|
2161
|
-
const decoded = crypto.decode(receiptJws);
|
|
2217
|
+
const decoded = crypto$1.decode(receiptJws);
|
|
2162
2218
|
header = decoded.header;
|
|
2163
2219
|
payload = decoded.payload;
|
|
2164
2220
|
builder.pass("jws.parse");
|
|
@@ -2232,7 +2288,7 @@ async function verifyReceiptCore(options) {
|
|
|
2232
2288
|
if (pinnedKey) {
|
|
2233
2289
|
builder.skip("issuer.discovery", { reason: "pinned_key_available" });
|
|
2234
2290
|
if (pinnedKey.jwk) {
|
|
2235
|
-
const actualThumbprint = await crypto.computeJwkThumbprint(pinnedKey.jwk);
|
|
2291
|
+
const actualThumbprint = await crypto$1.computeJwkThumbprint(pinnedKey.jwk);
|
|
2236
2292
|
if (actualThumbprint !== pinnedKey.jwk_thumbprint_sha256) {
|
|
2237
2293
|
builder.fail("key.resolve", "E_VERIFY_POLICY_VIOLATION", {
|
|
2238
2294
|
error: "Pinned JWK thumbprint does not match declared thumbprint",
|
|
@@ -2245,7 +2301,7 @@ async function verifyReceiptCore(options) {
|
|
|
2245
2301
|
report: includeMeta ? builder.addTimestamp().build() : builder.build()
|
|
2246
2302
|
};
|
|
2247
2303
|
}
|
|
2248
|
-
publicKey = crypto.jwkToPublicKeyBytes(pinnedKey.jwk);
|
|
2304
|
+
publicKey = crypto$1.jwkToPublicKeyBytes(pinnedKey.jwk);
|
|
2249
2305
|
keySource = "pinned_keys";
|
|
2250
2306
|
keyThumbprint = actualThumbprint;
|
|
2251
2307
|
builder.pass("key.resolve", {
|
|
@@ -2256,7 +2312,7 @@ async function verifyReceiptCore(options) {
|
|
|
2256
2312
|
});
|
|
2257
2313
|
} else if (pinnedKey.public_key) {
|
|
2258
2314
|
try {
|
|
2259
|
-
publicKey = crypto.base64urlDecode(pinnedKey.public_key);
|
|
2315
|
+
publicKey = crypto$1.base64urlDecode(pinnedKey.public_key);
|
|
2260
2316
|
if (publicKey.length !== 32) {
|
|
2261
2317
|
throw new Error(`Expected 32 bytes, got ${publicKey.length}`);
|
|
2262
2318
|
}
|
|
@@ -2320,7 +2376,7 @@ async function verifyReceiptCore(options) {
|
|
|
2320
2376
|
report: includeMeta ? builder.addTimestamp().build() : builder.build()
|
|
2321
2377
|
};
|
|
2322
2378
|
}
|
|
2323
|
-
const actualThumbprint = await crypto.computeJwkThumbprint(jwk);
|
|
2379
|
+
const actualThumbprint = await crypto$1.computeJwkThumbprint(jwk);
|
|
2324
2380
|
if (actualThumbprint !== pinnedKey.jwk_thumbprint_sha256) {
|
|
2325
2381
|
builder.fail("key.resolve", "E_VERIFY_POLICY_VIOLATION", {
|
|
2326
2382
|
error: "JWK thumbprint does not match pinned key",
|
|
@@ -2333,7 +2389,7 @@ async function verifyReceiptCore(options) {
|
|
|
2333
2389
|
report: includeMeta ? builder.addTimestamp().build() : builder.build()
|
|
2334
2390
|
};
|
|
2335
2391
|
}
|
|
2336
|
-
publicKey = crypto.jwkToPublicKeyBytes(jwk);
|
|
2392
|
+
publicKey = crypto$1.jwkToPublicKeyBytes(jwk);
|
|
2337
2393
|
keySource = "pinned_keys";
|
|
2338
2394
|
keyThumbprint = actualThumbprint;
|
|
2339
2395
|
builder.pass("key.resolve", { source: keySource, kid, thumbprint_verified: true });
|
|
@@ -2381,13 +2437,13 @@ async function verifyReceiptCore(options) {
|
|
|
2381
2437
|
report: includeMeta ? builder.addTimestamp().build() : builder.build()
|
|
2382
2438
|
};
|
|
2383
2439
|
}
|
|
2384
|
-
publicKey = crypto.jwkToPublicKeyBytes(jwk);
|
|
2440
|
+
publicKey = crypto$1.jwkToPublicKeyBytes(jwk);
|
|
2385
2441
|
keySource = "jwks_discovery";
|
|
2386
|
-
keyThumbprint = await crypto.computeJwkThumbprint(jwk);
|
|
2442
|
+
keyThumbprint = await crypto$1.computeJwkThumbprint(jwk);
|
|
2387
2443
|
builder.pass("key.resolve", { source: keySource, kid, thumbprint: keyThumbprint });
|
|
2388
2444
|
}
|
|
2389
2445
|
try {
|
|
2390
|
-
const result = await crypto.verify(receiptJws, publicKey);
|
|
2446
|
+
const result = await crypto$1.verify(receiptJws, publicKey);
|
|
2391
2447
|
if (!result.valid) {
|
|
2392
2448
|
builder.fail("jws.signature", "E_VERIFY_SIGNATURE_INVALID", {
|
|
2393
2449
|
error: "Ed25519 signature verification failed"
|
|
@@ -2464,7 +2520,7 @@ async function verifyReceiptCore(options) {
|
|
|
2464
2520
|
builder.addArtifact("issuer_key_thumbprint", keyThumbprint);
|
|
2465
2521
|
}
|
|
2466
2522
|
if (jwksRawBytes) {
|
|
2467
|
-
const jwksDigestHex = await crypto.sha256Hex(jwksRawBytes);
|
|
2523
|
+
const jwksDigestHex = await crypto$1.sha256Hex(jwksRawBytes);
|
|
2468
2524
|
builder.addArtifact("issuer_jwks_digest", createDigest(jwksDigestHex));
|
|
2469
2525
|
}
|
|
2470
2526
|
const report = includeMeta ? builder.addTimestamp().build() : builder.build();
|
|
@@ -2884,7 +2940,7 @@ async function fetchPointerWithDigest(options) {
|
|
|
2884
2940
|
message: jwsValidation.message
|
|
2885
2941
|
};
|
|
2886
2942
|
}
|
|
2887
|
-
const actualDigest = await crypto.sha256Hex(receipt);
|
|
2943
|
+
const actualDigest = await crypto$1.sha256Hex(receipt);
|
|
2888
2944
|
if (actualDigest !== expectedDigest) {
|
|
2889
2945
|
return {
|
|
2890
2946
|
ok: false,
|
|
@@ -2996,35 +3052,35 @@ async function verifyAndFetchPointer(pointerHeader, fetchOptions) {
|
|
|
2996
3052
|
|
|
2997
3053
|
Object.defineProperty(exports, "base64urlDecode", {
|
|
2998
3054
|
enumerable: true,
|
|
2999
|
-
get: function () { return crypto.base64urlDecode; }
|
|
3055
|
+
get: function () { return crypto$1.base64urlDecode; }
|
|
3000
3056
|
});
|
|
3001
3057
|
Object.defineProperty(exports, "base64urlEncode", {
|
|
3002
3058
|
enumerable: true,
|
|
3003
|
-
get: function () { return crypto.base64urlEncode; }
|
|
3059
|
+
get: function () { return crypto$1.base64urlEncode; }
|
|
3004
3060
|
});
|
|
3005
3061
|
Object.defineProperty(exports, "computeJwkThumbprint", {
|
|
3006
3062
|
enumerable: true,
|
|
3007
|
-
get: function () { return crypto.computeJwkThumbprint; }
|
|
3063
|
+
get: function () { return crypto$1.computeJwkThumbprint; }
|
|
3008
3064
|
});
|
|
3009
3065
|
Object.defineProperty(exports, "generateKeypair", {
|
|
3010
3066
|
enumerable: true,
|
|
3011
|
-
get: function () { return crypto.generateKeypair; }
|
|
3067
|
+
get: function () { return crypto$1.generateKeypair; }
|
|
3012
3068
|
});
|
|
3013
3069
|
Object.defineProperty(exports, "jwkToPublicKeyBytes", {
|
|
3014
3070
|
enumerable: true,
|
|
3015
|
-
get: function () { return crypto.jwkToPublicKeyBytes; }
|
|
3071
|
+
get: function () { return crypto$1.jwkToPublicKeyBytes; }
|
|
3016
3072
|
});
|
|
3017
3073
|
Object.defineProperty(exports, "sha256Bytes", {
|
|
3018
3074
|
enumerable: true,
|
|
3019
|
-
get: function () { return crypto.sha256Bytes; }
|
|
3075
|
+
get: function () { return crypto$1.sha256Bytes; }
|
|
3020
3076
|
});
|
|
3021
3077
|
Object.defineProperty(exports, "sha256Hex", {
|
|
3022
3078
|
enumerable: true,
|
|
3023
|
-
get: function () { return crypto.sha256Hex; }
|
|
3079
|
+
get: function () { return crypto$1.sha256Hex; }
|
|
3024
3080
|
});
|
|
3025
3081
|
Object.defineProperty(exports, "verify", {
|
|
3026
3082
|
enumerable: true,
|
|
3027
|
-
get: function () { return crypto.verify; }
|
|
3083
|
+
get: function () { return crypto$1.verify; }
|
|
3028
3084
|
});
|
|
3029
3085
|
exports.CHECK_IDS = CHECK_IDS;
|
|
3030
3086
|
exports.DEFAULT_NETWORK_SECURITY = DEFAULT_NETWORK_SECURITY;
|
|
@@ -3034,11 +3090,15 @@ exports.NON_DETERMINISTIC_ARTIFACT_KEYS = NON_DETERMINISTIC_ARTIFACT_KEYS;
|
|
|
3034
3090
|
exports.VerificationReportBuilder = VerificationReportBuilder;
|
|
3035
3091
|
exports.buildFailureReport = buildFailureReport;
|
|
3036
3092
|
exports.buildSuccessReport = buildSuccessReport;
|
|
3093
|
+
exports.checkDocumentBinding = checkDocumentBinding;
|
|
3037
3094
|
exports.checkPolicyBinding = checkPolicyBinding;
|
|
3038
3095
|
exports.clearJWKSCache = clearJWKSCache;
|
|
3039
3096
|
exports.clearKidThumbprints = clearKidThumbprints;
|
|
3097
|
+
exports.computeDocumentDigest = computeDocumentDigest;
|
|
3098
|
+
exports.computeJsonDocumentDigestJcs = computeJsonDocumentDigestJcs;
|
|
3040
3099
|
exports.computePolicyDigestJcs = computePolicyDigestJcs;
|
|
3041
3100
|
exports.computeReceiptDigest = computeReceiptDigest;
|
|
3101
|
+
exports.computeTextDocumentDigestUtf8 = computeTextDocumentDigestUtf8;
|
|
3042
3102
|
exports.createDefaultPolicy = createDefaultPolicy;
|
|
3043
3103
|
exports.createDigest = createDigest;
|
|
3044
3104
|
exports.createEmptyReport = createEmptyReport;
|