@peac/protocol 0.12.0 → 0.12.2

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/issue.d.ts CHANGED
@@ -172,7 +172,7 @@ export interface IssueWire02Options {
172
172
  /** Declared purpose string (max 256 chars, optional) */
173
173
  purpose_declared?: string;
174
174
  /**
175
- * Policy binding block (DD-151).
175
+ * Policy binding block.
176
176
  * digest must be 'sha256:<64 lowercase hex>' format (use computePolicyDigestJcs from @peac/protocol).
177
177
  */
178
178
  policy?: PolicyBlock;
@@ -30,7 +30,7 @@ export interface JWKS {
30
30
  keys: JWK[];
31
31
  }
32
32
  /**
33
- * Revoked key entry from issuer configuration (DD-148)
33
+ * Revoked key entry from issuer configuration
34
34
  */
35
35
  export interface RevokedKeyInfo {
36
36
  kid: string;
@@ -46,7 +46,7 @@ export interface JWKSResolveSuccess {
46
46
  fromCache: boolean;
47
47
  /** Raw JWKS bytes for digest computation (only present when not from cache) */
48
48
  rawBytes?: Uint8Array;
49
- /** Revoked keys from issuer configuration (DD-148, v0.11.3+) */
49
+ /** Revoked keys from issuer configuration (v0.11.3+) */
50
50
  revokedKeys?: RevokedKeyInfo[];
51
51
  }
52
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"jwks-resolver.d.ts","sourceRoot":"","sources":["../src/jwks-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,gEAAgE;IAChE,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,KAAK,CAAC;IACV,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClC,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAgHD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAgCD;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAyFD;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAsK5B"}
1
+ {"version":3,"file":"jwks-resolver.d.ts","sourceRoot":"","sources":["../src/jwks-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,wDAAwD;IACxD,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,KAAK,CAAC;IACV,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClC,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAgHD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAgCD;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAyFD;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAsK5B"}
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Policy binding utilities (Layer 3, DD-49, DD-151)
2
+ * Policy binding utilities (Layer 3)
3
3
  *
4
4
  * JCS (RFC 8785) canonicalization + SHA-256 for policy digest computation,
5
5
  * and the 3-state binding check that combines receipt and local digests.
6
6
  *
7
7
  * The JCS + hash computation lives in Layer 3 (@peac/protocol) rather than
8
8
  * Layer 1 (@peac/schema) to avoid introducing crypto dependencies into the
9
- * schema package (DD-141: @peac/schema is validation-only).
9
+ * schema package (@peac/schema is validation-only).
10
10
  *
11
11
  * The pure string comparison (verifyPolicyBinding) lives in Layer 1
12
12
  * (@peac/schema) so that consumers who only need to check pre-computed
@@ -22,7 +22,7 @@ import type { PolicyBindingStatus } from './verifier-types.js';
22
22
  * the PEAC self-describing hash format: 'sha256:<64 lowercase hex>'.
23
23
  *
24
24
  * This is the normative digest format for the policy.digest field in Wire 0.2
25
- * receipts (DD-151). The format is stable and identical across implementations.
25
+ * receipts. The format is stable and identical across implementations.
26
26
  *
27
27
  * Callers MUST pass the same JSON structure that was embedded in the receipt's
28
28
  * policy block when issuing. Key order is irrelevant; JCS normalizes it.
@@ -36,7 +36,7 @@ export declare function computePolicyDigestJcs(policy: JsonValue): Promise<strin
36
36
  /**
37
37
  * Compute the 3-state policy binding result.
38
38
  *
39
- * Three-state semantics (DD-151):
39
+ * Three-state semantics:
40
40
  * - 'unavailable': either digest is absent (receipt has no policy block, or
41
41
  * caller did not provide a local digest). No binding check performed.
42
42
  * - 'verified': both digests present and match exactly.
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Type-to-extension enforcement check (Layer 3)
3
+ *
4
+ * Pure helper: given a receipt kind, type, extensions record, and
5
+ * registry data, determines whether the expected extension group is
6
+ * present, absent, or replaced by a different registered group.
7
+ *
8
+ * This module has no side effects, no I/O, and no strictness logic.
9
+ * Strictness (error vs warning) is decided by the caller (verifyLocal).
10
+ *
11
+ * Decision tree:
12
+ * 1. challenge-kind receipt -> skip
13
+ * 2. unmapped/custom type -> skip
14
+ * 3. expected extension present -> ok
15
+ * 4. expected absent + other registered present -> mismatch
16
+ * 5. expected absent + none present -> missing
17
+ *
18
+ * Unknown third-party extension keys never count as mismatch.
19
+ */
20
+ /** Result of the type-to-extension mapping check */
21
+ export type TypeExtensionCheckResult = {
22
+ status: 'ok';
23
+ } | {
24
+ status: 'skip';
25
+ } | {
26
+ status: 'missing' | 'mismatch';
27
+ expected_extension_group: string;
28
+ present_registered_extension_groups: string[];
29
+ };
30
+ /**
31
+ * Check whether a receipt's extensions match the expected extension
32
+ * group for its registered type.
33
+ *
34
+ * Performance: O(1) lookup for the common ok/skip paths. Only iterates
35
+ * extension keys when the expected group is absent (error path).
36
+ *
37
+ * @param kind - Receipt kind (evidence or challenge)
38
+ * @param type - Receipt type value (e.g., org.peacprotocol/payment)
39
+ * @param extensions - Extensions record from receipt claims (may be undefined)
40
+ * @param typeToExtensionMap - Generated TYPE_TO_EXTENSION_MAP
41
+ * @param registeredExtensionGroupKeys - Generated REGISTERED_EXTENSION_GROUP_KEYS set
42
+ */
43
+ export declare function checkTypeExtensionMapping(kind: string, type: string, extensions: Record<string, unknown> | undefined, typeToExtensionMap: ReadonlyMap<string, string>, registeredExtensionGroupKeys: ReadonlySet<string>): TypeExtensionCheckResult;
44
+ //# sourceMappingURL=type-extension-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-extension-check.d.ts","sourceRoot":"","sources":["../src/type-extension-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,oDAAoD;AACpD,MAAM,MAAM,wBAAwB,GAChC;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAClB;IACE,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,mCAAmC,EAAE,MAAM,EAAE,CAAC;CAC/C,CAAC;AAEN;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC/C,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/C,4BAA4B,EAAE,WAAW,CAAC,MAAM,CAAC,GAChD,wBAAwB,CAkC1B"}
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { VERIFIER_POLICY_VERSION, VERIFICATION_REPORT_VERSION } from '@peac/kernel';
11
11
  /**
12
- * Three-state policy binding status (DD-49)
12
+ * Three-state policy binding status
13
13
  *
14
14
  * - 'verified': Policy digest in receipt matches local policy bytes (Wire 0.2+)
15
15
  * - 'failed': Policy digest mismatch (Wire 0.2+)
@@ -237,7 +237,7 @@ export interface VerificationResult {
237
237
  /** Key ID used for verification (optional) */
238
238
  kid?: string;
239
239
  /**
240
- * Policy binding status (DD-49).
240
+ * Policy binding status.
241
241
  *
242
242
  * Always 'unavailable' for Wire 0.1 receipts.
243
243
  * Wire 0.2+ receipts with `peac.policy.digest` will report 'verified' or 'failed'.
@@ -4,6 +4,35 @@ var crypto = require('@peac/crypto');
4
4
  var kernel = require('@peac/kernel');
5
5
  var schema = require('@peac/schema');
6
6
 
7
+ // src/verify-local.ts
8
+
9
+ // src/type-extension-check.ts
10
+ function checkTypeExtensionMapping(kind, type, extensions, typeToExtensionMap, registeredExtensionGroupKeys) {
11
+ if (kind === "challenge") {
12
+ return { status: "skip" };
13
+ }
14
+ const expectedGroup = typeToExtensionMap.get(type);
15
+ if (expectedGroup === void 0) {
16
+ return { status: "skip" };
17
+ }
18
+ if (extensions !== void 0 && Object.prototype.hasOwnProperty.call(extensions, expectedGroup)) {
19
+ return { status: "ok" };
20
+ }
21
+ const presentRegistered = [];
22
+ if (extensions !== void 0) {
23
+ for (const key of Object.keys(extensions)) {
24
+ if (key !== expectedGroup && registeredExtensionGroupKeys.has(key)) {
25
+ presentRegistered.push(key);
26
+ }
27
+ }
28
+ }
29
+ return {
30
+ status: presentRegistered.length > 0 ? "mismatch" : "missing",
31
+ expected_extension_group: expectedGroup,
32
+ present_registered_extension_groups: presentRegistered
33
+ };
34
+ }
35
+
7
36
  // src/verify-local.ts
8
37
  function isCryptoError(err) {
9
38
  return err !== null && typeof err === "object" && "name" in err && err.name === "CryptoError" && "code" in err && typeof err.code === "string" && err.code.startsWith("CRYPTO_") && "message" in err && typeof err.message === "string";
@@ -131,6 +160,34 @@ async function verifyLocal(jws, publicKey, options = {}) {
131
160
  }
132
161
  }
133
162
  }
163
+ const typeExtCheck = checkTypeExtensionMapping(
164
+ claims.kind,
165
+ claims.type,
166
+ claims.extensions,
167
+ kernel.TYPE_TO_EXTENSION_MAP,
168
+ schema.REGISTERED_EXTENSION_GROUP_KEYS
169
+ );
170
+ if (typeExtCheck.status === "missing" || typeExtCheck.status === "mismatch") {
171
+ const warningCode = typeExtCheck.status === "missing" ? schema.WARNING_EXTENSION_GROUP_MISSING : schema.WARNING_EXTENSION_GROUP_MISMATCH;
172
+ const errorCode = typeExtCheck.status === "missing" ? "E_EXTENSION_GROUP_REQUIRED" : "E_EXTENSION_GROUP_MISMATCH";
173
+ if (strictness === "strict") {
174
+ return {
175
+ valid: false,
176
+ code: errorCode,
177
+ message: `Type "${claims.type}" expects extension group "${typeExtCheck.expected_extension_group}" but it is ${typeExtCheck.status === "mismatch" ? "replaced by a different registered group" : "absent"}`,
178
+ details: {
179
+ type: claims.type,
180
+ expected_extension_group: typeExtCheck.expected_extension_group,
181
+ present_registered_extension_groups: typeExtCheck.present_registered_extension_groups
182
+ }
183
+ };
184
+ }
185
+ accumulatedWarnings.push({
186
+ code: warningCode,
187
+ message: `Type "${claims.type}" expects extension group "${typeExtCheck.expected_extension_group}"`,
188
+ pointer: "/type"
189
+ });
190
+ }
134
191
  if (policyDigest !== void 0 && !kernel.HASH.pattern.test(policyDigest)) {
135
192
  return {
136
193
  valid: false,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/verify-local.ts"],"names":["jwsVerify","WARNING_TYP_MISSING","validateKernelConstraints","parseReceiptClaims","checkOccurredAtSkew","REGISTERED_RECEIPT_TYPES","WARNING_TYPE_UNREGISTERED","REGISTERED_EXTENSION_GROUP_KEYS","isValidExtensionKey","WARNING_UNKNOWN_EXTENSION","HASH","verifyPolicyBinding","sortWarnings"],"mappings":"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","file":"verify-local.cjs","sourcesContent":["/**\n * Local receipt verification with schema validation\n *\n * Use this for verifying receipts when you have the public key locally,\n * without JWKS discovery.\n */\n\nimport { verify as jwsVerify } from '@peac/crypto';\nimport { type VerificationStrictness, type VerificationWarning, HASH } from '@peac/kernel';\nimport {\n parseReceiptClaims,\n validateKernelConstraints,\n type Wire02Claims,\n checkOccurredAtSkew,\n sortWarnings,\n WARNING_TYP_MISSING,\n WARNING_TYPE_UNREGISTERED,\n WARNING_UNKNOWN_EXTENSION,\n REGISTERED_RECEIPT_TYPES,\n REGISTERED_EXTENSION_GROUP_KEYS,\n isValidExtensionKey,\n verifyPolicyBinding,\n} from '@peac/schema';\nimport type { PolicyBindingStatus } from './verifier-types';\n\n/**\n * Structural type for CryptoError\n * Used instead of instanceof for robustness across ESM/CJS boundaries\n */\ninterface CryptoErrorLike {\n name: 'CryptoError';\n code: string;\n message: string;\n}\n\n/**\n * Structural check for CryptoError\n * More robust than instanceof across module boundaries (ESM/CJS, duplicate packages)\n */\nfunction isCryptoError(err: unknown): err is CryptoErrorLike {\n return (\n err !== null &&\n typeof err === 'object' &&\n 'name' in err &&\n err.name === 'CryptoError' &&\n 'code' in err &&\n typeof err.code === 'string' &&\n err.code.startsWith('CRYPTO_') &&\n 'message' in err &&\n typeof err.message === 'string'\n );\n}\n\n/**\n * Canonical error codes for local verification\n *\n * These map to E_* codes in specs/kernel/errors.json.\n * JOSE hardening codes (E_JWS_*) are distinct from generic E_INVALID_FORMAT\n * so callers can distinguish key-injection, compression, and crit attacks from\n * ordinary format errors (v0.12.0-preview.1, DD-156).\n */\nexport type VerifyLocalErrorCode =\n | 'E_INVALID_SIGNATURE'\n | 'E_INVALID_FORMAT'\n | 'E_CONSTRAINT_VIOLATION'\n | 'E_EXPIRED'\n | 'E_NOT_YET_VALID'\n | 'E_INVALID_ISSUER'\n | 'E_INVALID_AUDIENCE'\n | 'E_INVALID_SUBJECT'\n | 'E_INVALID_RECEIPT_ID'\n | 'E_MISSING_EXP'\n | 'E_WIRE_VERSION_MISMATCH'\n | 'E_UNSUPPORTED_WIRE_VERSION'\n | 'E_OCCURRED_AT_FUTURE'\n // JOSE hardening codes (Wire 0.2, v0.12.0-preview.1, DD-156)\n | 'E_JWS_EMBEDDED_KEY'\n | 'E_JWS_CRIT_REJECTED'\n | 'E_JWS_MISSING_KID'\n | 'E_JWS_B64_REJECTED'\n | 'E_JWS_ZIP_REJECTED'\n // Policy binding (Wire 0.2, v0.12.0-preview.1, DD-151)\n | 'E_POLICY_BINDING_FAILED'\n | 'E_INTERNAL';\n\n/**\n * Options for local verification\n */\nexport interface VerifyLocalOptions {\n /**\n * Expected issuer URL\n *\n * If provided, verification fails if receipt.iss does not match.\n */\n issuer?: string;\n\n /**\n * @deprecated Wire 0.2 does not have an `aud` claim. This option is ignored.\n * Retained for source compatibility during migration; will be removed in v1.0.\n */\n audience?: string;\n\n /**\n * Expected subject URI\n *\n * If provided, verification fails if receipt.sub does not match.\n * Binds the receipt to a specific resource/interaction target.\n */\n subjectUri?: string;\n\n /**\n * @deprecated Wire 0.2 does not have a `rid` claim. Use `jti` for receipt identification.\n * This option is ignored. Retained for source compatibility; will be removed in v1.0.\n */\n rid?: string;\n\n /**\n * @deprecated Wire 0.2 receipts do not expire (permanent evidence by design).\n * This option is ignored. Retained for source compatibility; will be removed in v1.0.\n */\n requireExp?: boolean;\n\n /**\n * Current timestamp (Unix seconds)\n *\n * Defaults to Date.now() / 1000. Override for testing.\n */\n now?: number;\n\n /**\n * Maximum clock skew tolerance (seconds)\n *\n * Allows for clock drift between issuer and verifier.\n * Defaults to 300 (5 minutes).\n */\n maxClockSkew?: number;\n\n /**\n * Verification strictness profile (v0.12.0-preview.1, DD-156).\n *\n * - 'strict' (default): missing typ is a hard error before schema validation.\n * - 'interop': missing typ emits a 'typ_missing' warning and routes by payload content.\n *\n * Strictness is EXCLUSIVELY controlled here (@peac/protocol). @peac/crypto has no strictness param.\n */\n strictness?: VerificationStrictness;\n\n /**\n * Pre-computed local policy digest for policy binding (Wire 0.2, v0.12.0-preview.1, DD-151).\n *\n * Must be in 'sha256:<64 lowercase hex>' format, computed via computePolicyDigestJcs()\n * from @peac/protocol. When provided alongside a receipt that contains a policy block\n * (policy.digest), the binding check is performed:\n * - Match: policy_binding = 'verified'\n * - Mismatch: hard fail with E_POLICY_BINDING_FAILED\n * - Either absent: policy_binding = 'unavailable'\n *\n * Always 'unavailable' for Wire 0.1 receipts regardless of this option.\n */\n policyDigest?: string;\n}\n\n/**\n * Result of successful local verification (Wire 0.2 only)\n *\n * Wire 0.1 receipts are no longer accepted by verifyLocal() and return\n * E_UNSUPPORTED_WIRE_VERSION. Re-issue as Wire 0.2 using issue().\n */\nexport interface VerifyLocalSuccess {\n /** Verification succeeded */\n valid: true;\n /** Receipt variant (always 'wire-02') */\n variant: 'wire-02';\n /** Validated Wire 0.2 receipt claims */\n claims: Wire02Claims;\n /** Key ID from JWS header (for logging/indexing) */\n kid: string;\n /** Wire format version */\n wireVersion: '0.2';\n /** Verification warnings from schema parsing and strictness routing */\n warnings: VerificationWarning[];\n /**\n * Policy binding status (DD-49, DD-151).\n *\n * Three-state result:\n * - 'unavailable': either the receipt contains no policy block, or the\n * caller did not pass a policyDigest option to verifyLocal(). No check.\n * - 'verified': both digests present and match exactly.\n * - 'failed': not returned on success; verifyLocal() returns\n * E_POLICY_BINDING_FAILED (valid: false) before reaching this field.\n */\n policy_binding: PolicyBindingStatus;\n}\n\n/**\n * Result of failed local verification\n */\nexport interface VerifyLocalFailure {\n /** Verification failed */\n valid: false;\n\n /** Canonical error code (maps to specs/kernel/errors.json) */\n code: VerifyLocalErrorCode;\n\n /** Human-readable error message */\n message: string;\n\n /** Structured details for debugging (stable error code preserved in `code`) */\n details?: {\n /** Precise parse error code from unified parser (e.g. E_PARSE_COMMERCE_INVALID) */\n parse_code?: string;\n /** Zod validation issues (bounded, stable shape; non-normative, may change) */\n issues?: ReadonlyArray<{ path: string; message: string }>;\n /**\n * Policy digest from the receipt (present when code is E_POLICY_BINDING_FAILED).\n * Both are SHA-256 hashes; safe to log without leaking policy content.\n */\n receipt_policy_digest?: string;\n /** Caller-supplied policy digest (present when code is E_POLICY_BINDING_FAILED). */\n local_policy_digest?: string;\n /** policy.uri hint from the receipt (present when code is E_POLICY_BINDING_FAILED and uri set). */\n policy_uri?: string;\n };\n}\n\n/**\n * Union type for local verification result\n */\nexport type VerifyLocalResult = VerifyLocalSuccess | VerifyLocalFailure;\n\n/**\n * Internal CRYPTO_* codes that map to generic E_INVALID_FORMAT.\n * These are format/encoding errors not security-specific.\n */\nconst FORMAT_ERROR_CODES = new Set([\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'CRYPTO_INVALID_TYP',\n 'CRYPTO_INVALID_ALG',\n 'CRYPTO_INVALID_KEY_LENGTH',\n]);\n\n/**\n * JOSE hardening code mapping: CRYPTO_JWS_* → specific E_JWS_* (v0.12.0-preview.1, DD-156).\n *\n * Each JOSE hazard code maps to its specific public E_JWS_* counterpart rather than\n * collapsing into the generic E_INVALID_FORMAT. This lets callers distinguish embedded-key\n * injection, crit-header abuse, and unencoded-payload attacks from ordinary format errors.\n */\nconst JOSE_CODE_MAP: Record<string, VerifyLocalErrorCode> = {\n CRYPTO_JWS_EMBEDDED_KEY: 'E_JWS_EMBEDDED_KEY',\n CRYPTO_JWS_CRIT_REJECTED: 'E_JWS_CRIT_REJECTED',\n CRYPTO_JWS_MISSING_KID: 'E_JWS_MISSING_KID',\n CRYPTO_JWS_B64_REJECTED: 'E_JWS_B64_REJECTED',\n CRYPTO_JWS_ZIP_REJECTED: 'E_JWS_ZIP_REJECTED',\n};\n\n/** Max parse issues to include in details (prevents log bloat) */\nconst MAX_PARSE_ISSUES = 25;\n\n/**\n * Sanitize Zod issues into a bounded, stable structure.\n * Avoids exposing raw Zod internals or unbounded arrays in the public API.\n */\nfunction sanitizeParseIssues(\n issues: unknown\n): ReadonlyArray<{ path: string; message: string }> | undefined {\n if (!Array.isArray(issues)) return undefined;\n return issues.slice(0, MAX_PARSE_ISSUES).map((issue) => ({\n path: Array.isArray(issue?.path) ? issue.path.join('.') : '',\n message: typeof issue?.message === 'string' ? issue.message : String(issue),\n }));\n}\n\n/**\n * Verify a Wire 0.2 PEAC receipt locally with a known public key.\n *\n * Wire 0.2 only: Wire 0.1 receipts return E_UNSUPPORTED_WIRE_VERSION.\n * Re-issue Wire 0.1 receipts as Wire 0.2 using issueWire02().\n *\n * This function:\n * 1. Verifies the Ed25519 signature and header (typ, alg)\n * 2. Applies strictness routing for missing typ (strict: hard error; interop: warning)\n * 3. Validates the receipt schema with Zod (Wire 0.2 only)\n * 4. Checks issuer/subject binding (if options provided)\n * 5. Checks time validity (iat with clock skew tolerance)\n * 6. Checks occurred_at skew and collects parse warnings\n *\n * @param jws - JWS compact serialization\n * @param publicKey - Ed25519 public key (32 bytes)\n * @param options - Optional verification options (issuer, subject, clock skew, strictness, policyDigest)\n * @returns Typed verification result\n *\n * @example\n * ```typescript\n * const result = await verifyLocal(jws, publicKey, {\n * issuer: 'https://api.example.com',\n * strictness: 'strict',\n * });\n * if (result.valid) {\n * console.log('Kind:', result.claims.kind);\n * console.log('Warnings:', result.warnings);\n * }\n * ```\n */\nexport async function verifyLocal(\n jws: string,\n publicKey: Uint8Array,\n options: VerifyLocalOptions = {}\n): Promise<VerifyLocalResult> {\n const { issuer, subjectUri, maxClockSkew = 300, strictness = 'strict', policyDigest } = options;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n\n try {\n // 1. Verify signature and header (typ, alg validated by @peac/crypto)\n const result = await jwsVerify<unknown>(jws, publicKey);\n\n if (!result.valid) {\n return {\n valid: false,\n code: 'E_INVALID_SIGNATURE',\n message: 'Ed25519 signature verification failed',\n };\n }\n\n // Accumulated warnings for Wire 0.2 path\n const accumulatedWarnings: VerificationWarning[] = [];\n\n // 2. Strictness routing for missing typ (Correction 1, DD-156)\n if (result.header.typ === undefined) {\n if (strictness === 'strict') {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: 'Missing JWS typ header: strict mode requires typ to be present',\n };\n }\n // interop mode: emit warning and continue\n accumulatedWarnings.push({\n code: WARNING_TYP_MISSING,\n message: 'JWS typ header is absent; accepted in interop mode',\n });\n }\n\n // 3. Validate structural kernel constraints (DD-121, fail-closed)\n const constraintResult = validateKernelConstraints(result.payload);\n if (!constraintResult.valid) {\n const v = constraintResult.violations[0];\n return {\n valid: false,\n code: 'E_CONSTRAINT_VIOLATION',\n message: `Kernel constraint violated: ${v.constraint} (actual: ${v.actual}, limit: ${v.limit})`,\n };\n }\n\n // 4. Validate schema (unified parser supports Wire 0.1 and Wire 0.2)\n const pr = parseReceiptClaims(result.payload);\n\n if (!pr.ok) {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: `Receipt schema validation failed: ${pr.error.message}`,\n details: { parse_code: pr.error.code, issues: sanitizeParseIssues(pr.error.issues) },\n };\n }\n\n // 5. Collect parser warnings (Wire 0.2 parser may emit type/extension warnings)\n if (pr.wireVersion === '0.2') {\n accumulatedWarnings.push(...pr.warnings);\n }\n\n // Wire 0.2 path\n if (pr.wireVersion === '0.2') {\n const claims = pr.claims as Wire02Claims;\n\n // Issuer check\n if (issuer !== undefined && claims.iss !== issuer) {\n return {\n valid: false,\n code: 'E_INVALID_ISSUER',\n message: `Issuer mismatch: expected \"${issuer}\", got \"${claims.iss}\"`,\n };\n }\n\n // Subject check\n if (subjectUri !== undefined && claims.sub !== subjectUri) {\n return {\n valid: false,\n code: 'E_INVALID_SUBJECT',\n message: `Subject mismatch: expected \"${subjectUri}\", got \"${claims.sub ?? 'undefined'}\"`,\n };\n }\n\n // iat: not-yet-valid check (with clock skew)\n if (claims.iat > now + maxClockSkew) {\n return {\n valid: false,\n code: 'E_NOT_YET_VALID',\n message: `Receipt not yet valid: issued at ${new Date(claims.iat * 1000).toISOString()}, now is ${new Date(now * 1000).toISOString()}`,\n };\n }\n\n // occurred_at skew check (evidence kind only)\n if (claims.kind === 'evidence') {\n const skewResult = checkOccurredAtSkew(claims.occurred_at, claims.iat, now, maxClockSkew);\n if (skewResult === 'future_error') {\n return {\n valid: false,\n code: 'E_OCCURRED_AT_FUTURE',\n message: `occurred_at is in the future beyond tolerance (${maxClockSkew}s)`,\n };\n }\n if (skewResult !== null) {\n accumulatedWarnings.push(skewResult);\n }\n }\n\n // Emit type_unregistered warning for valid-but-unregistered type values (DD-155)\n if (!REGISTERED_RECEIPT_TYPES.has(claims.type)) {\n accumulatedWarnings.push({\n code: WARNING_TYPE_UNREGISTERED,\n message: 'Receipt type is not in the recommended type registry',\n pointer: '/type',\n });\n }\n\n // Emit unknown_extension_preserved warnings for unrecognized-but-well-formed keys (DD-155)\n // Malformed keys are already hard errors (E_INVALID_EXTENSION_KEY) at schema layer.\n if (claims.extensions !== undefined) {\n for (const key of Object.keys(claims.extensions)) {\n if (!REGISTERED_EXTENSION_GROUP_KEYS.has(key) && isValidExtensionKey(key)) {\n // RFC 6901: '~' -> '~0', '/' -> '~1'\n const escapedKey = key.replace(/~/g, '~0').replace(/\\//g, '~1');\n accumulatedWarnings.push({\n code: WARNING_UNKNOWN_EXTENSION,\n message: 'Unknown extension key preserved without schema validation',\n pointer: `/extensions/${escapedKey}`,\n });\n }\n }\n }\n\n // Validate policyDigest option format (DD-151): must be sha256:<64 lowercase hex> if provided.\n if (policyDigest !== undefined && !HASH.pattern.test(policyDigest)) {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: 'policyDigest option must be in sha256:<64 lowercase hex> format',\n };\n }\n\n // Policy binding check (DD-151): 3-state result.\n // 'unavailable' when either receipt has no policy block or caller omitted policyDigest.\n // 'verified' / 'failed' when both are present; 'failed' is a hard verification error.\n const receiptPolicyDigest = claims.policy?.digest;\n const bindingStatus: PolicyBindingStatus =\n receiptPolicyDigest === undefined || policyDigest === undefined\n ? 'unavailable'\n : verifyPolicyBinding(receiptPolicyDigest, policyDigest);\n if (bindingStatus === 'failed') {\n return {\n valid: false,\n code: 'E_POLICY_BINDING_FAILED',\n message: 'Policy binding check failed: receipt policy digest does not match local policy',\n details: {\n receipt_policy_digest: receiptPolicyDigest,\n local_policy_digest: policyDigest,\n ...(claims.policy?.uri !== undefined && { policy_uri: claims.policy.uri }),\n },\n };\n }\n\n return {\n valid: true,\n variant: 'wire-02',\n claims,\n kid: result.header.kid,\n wireVersion: '0.2',\n warnings: sortWarnings(accumulatedWarnings),\n policy_binding: bindingStatus,\n };\n }\n\n // Wire 0.1 receipts: reject with E_UNSUPPORTED_WIRE_VERSION.\n return {\n valid: false,\n code: 'E_UNSUPPORTED_WIRE_VERSION',\n message: 'Wire 0.1 receipts are not supported. Re-issue as Wire 0.2 using issue().',\n };\n } catch (err) {\n // Handle typed CryptoError from @peac/crypto\n // Use structural check instead of instanceof for robustness across ESM/CJS boundaries\n // Map internal CRYPTO_* codes to canonical E_* codes.\n // JOSE hardening codes get specific E_JWS_* (not generic E_INVALID_FORMAT) so callers\n // can distinguish key-injection attacks from ordinary encoding errors.\n if (isCryptoError(err)) {\n // 1. JOSE hardening: specific E_JWS_* codes (checked first)\n if (Object.prototype.hasOwnProperty.call(JOSE_CODE_MAP, err.code)) {\n return {\n valid: false,\n code: JOSE_CODE_MAP[err.code]!,\n message: err.message,\n };\n }\n // 2. Generic format errors\n if (FORMAT_ERROR_CODES.has(err.code)) {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: err.message,\n };\n }\n if (err.code === 'CRYPTO_INVALID_SIGNATURE') {\n return {\n valid: false,\n code: 'E_INVALID_SIGNATURE',\n message: err.message,\n };\n }\n if (err.code === 'CRYPTO_WIRE_VERSION_MISMATCH') {\n return {\n valid: false,\n code: 'E_WIRE_VERSION_MISMATCH',\n message: err.message,\n };\n }\n }\n\n // Handle JSON parse errors from malformed payloads\n // Use structural check for cross-boundary robustness (consistent with isCryptoError pattern)\n if (\n err !== null &&\n typeof err === 'object' &&\n 'name' in err &&\n (err as { name: unknown }).name === 'SyntaxError'\n ) {\n const syntaxMessage =\n 'message' in err && typeof (err as { message: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Invalid JSON';\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: `Invalid receipt payload: ${syntaxMessage}`,\n };\n }\n\n // All other errors -> E_INTERNAL\n // No message parsing - code-based mapping only\n const message = err instanceof Error ? err.message : String(err);\n return {\n valid: false,\n code: 'E_INTERNAL',\n message: `Unexpected verification error: ${message}`,\n };\n }\n}\n\n/**\n * @deprecated Removed: verifyLocal() is Wire 0.2 only and always returns variant 'wire-02'.\n * This guard always returns false. Remove usage and use isWire02Result() instead.\n */\nexport function isCommerceResult(\n r: VerifyLocalResult\n): r is VerifyLocalSuccess & { variant: 'wire-02' } {\n // Always false: verifyLocal() only returns variant 'wire-02'\n return false;\n}\n\n/**\n * @deprecated Removed: verifyLocal() is Wire 0.2 only and always returns variant 'wire-02'.\n * This guard always returns false. Remove usage and use isWire02Result() instead.\n */\nexport function isAttestationResult(\n r: VerifyLocalResult\n): r is VerifyLocalSuccess & { variant: 'wire-02' } {\n // Always false: verifyLocal() only returns variant 'wire-02'\n return false;\n}\n\n/**\n * Type guard: narrows a VerifyLocalResult to a Wire 0.2 success (v0.12.0-preview.1).\n *\n * Use instead of manual `result.valid && result.variant === 'wire-02'` checks\n * to get proper claims narrowing to Wire02Claims.\n */\nexport function isWire02Result(\n r: VerifyLocalResult\n): r is VerifyLocalSuccess & { variant: 'wire-02' } {\n return r.valid === true && r.variant === 'wire-02';\n}\n"]}
1
+ {"version":3,"sources":["../src/type-extension-check.ts","../src/verify-local.ts"],"names":["jwsVerify","WARNING_TYP_MISSING","validateKernelConstraints","parseReceiptClaims","checkOccurredAtSkew","REGISTERED_RECEIPT_TYPES","WARNING_TYPE_UNREGISTERED","REGISTERED_EXTENSION_GROUP_KEYS","isValidExtensionKey","WARNING_UNKNOWN_EXTENSION","TYPE_TO_EXTENSION_MAP","WARNING_EXTENSION_GROUP_MISSING","WARNING_EXTENSION_GROUP_MISMATCH","HASH","verifyPolicyBinding","sortWarnings"],"mappings":"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","file":"verify-local.cjs","sourcesContent":["/**\n * Type-to-extension enforcement check (Layer 3)\n *\n * Pure helper: given a receipt kind, type, extensions record, and\n * registry data, determines whether the expected extension group is\n * present, absent, or replaced by a different registered group.\n *\n * This module has no side effects, no I/O, and no strictness logic.\n * Strictness (error vs warning) is decided by the caller (verifyLocal).\n *\n * Decision tree:\n * 1. challenge-kind receipt -> skip\n * 2. unmapped/custom type -> skip\n * 3. expected extension present -> ok\n * 4. expected absent + other registered present -> mismatch\n * 5. expected absent + none present -> missing\n *\n * Unknown third-party extension keys never count as mismatch.\n */\n\n/** Result of the type-to-extension mapping check */\nexport type TypeExtensionCheckResult =\n | { status: 'ok' }\n | { status: 'skip' }\n | {\n status: 'missing' | 'mismatch';\n expected_extension_group: string;\n present_registered_extension_groups: string[];\n };\n\n/**\n * Check whether a receipt's extensions match the expected extension\n * group for its registered type.\n *\n * Performance: O(1) lookup for the common ok/skip paths. Only iterates\n * extension keys when the expected group is absent (error path).\n *\n * @param kind - Receipt kind (evidence or challenge)\n * @param type - Receipt type value (e.g., org.peacprotocol/payment)\n * @param extensions - Extensions record from receipt claims (may be undefined)\n * @param typeToExtensionMap - Generated TYPE_TO_EXTENSION_MAP\n * @param registeredExtensionGroupKeys - Generated REGISTERED_EXTENSION_GROUP_KEYS set\n */\nexport function checkTypeExtensionMapping(\n kind: string,\n type: string,\n extensions: Record<string, unknown> | undefined,\n typeToExtensionMap: ReadonlyMap<string, string>,\n registeredExtensionGroupKeys: ReadonlySet<string>\n): TypeExtensionCheckResult {\n // Challenge-kind receipts skip: they indicate requirements, not evidence\n if (kind === 'challenge') {\n return { status: 'skip' };\n }\n\n const expectedGroup = typeToExtensionMap.get(type);\n\n // Unmapped/custom type: no check\n if (expectedGroup === undefined) {\n return { status: 'skip' };\n }\n\n // Expected group present: pass (O(1) check)\n if (extensions !== undefined && Object.prototype.hasOwnProperty.call(extensions, expectedGroup)) {\n return { status: 'ok' };\n }\n\n // Expected group absent: determine missing vs mismatch\n // Only build the present-registered list on this (uncommon) path\n const presentRegistered: string[] = [];\n if (extensions !== undefined) {\n for (const key of Object.keys(extensions)) {\n if (key !== expectedGroup && registeredExtensionGroupKeys.has(key)) {\n presentRegistered.push(key);\n }\n }\n }\n\n return {\n status: presentRegistered.length > 0 ? 'mismatch' : 'missing',\n expected_extension_group: expectedGroup,\n present_registered_extension_groups: presentRegistered,\n };\n}\n","/**\n * Local receipt verification with schema validation\n *\n * Use this for verifying receipts when you have the public key locally,\n * without JWKS discovery.\n */\n\nimport { verify as jwsVerify } from '@peac/crypto';\nimport { type VerificationStrictness, type VerificationWarning, HASH } from '@peac/kernel';\nimport {\n parseReceiptClaims,\n validateKernelConstraints,\n type Wire02Claims,\n checkOccurredAtSkew,\n sortWarnings,\n WARNING_TYP_MISSING,\n WARNING_TYPE_UNREGISTERED,\n WARNING_UNKNOWN_EXTENSION,\n WARNING_EXTENSION_GROUP_MISSING,\n WARNING_EXTENSION_GROUP_MISMATCH,\n REGISTERED_RECEIPT_TYPES,\n REGISTERED_EXTENSION_GROUP_KEYS,\n isValidExtensionKey,\n verifyPolicyBinding,\n} from '@peac/schema';\nimport { TYPE_TO_EXTENSION_MAP } from '@peac/kernel';\nimport { checkTypeExtensionMapping } from './type-extension-check';\nimport type { PolicyBindingStatus } from './verifier-types';\n\n/**\n * Structural type for CryptoError\n * Used instead of instanceof for robustness across ESM/CJS boundaries\n */\ninterface CryptoErrorLike {\n name: 'CryptoError';\n code: string;\n message: string;\n}\n\n/**\n * Structural check for CryptoError\n * More robust than instanceof across module boundaries (ESM/CJS, duplicate packages)\n */\nfunction isCryptoError(err: unknown): err is CryptoErrorLike {\n return (\n err !== null &&\n typeof err === 'object' &&\n 'name' in err &&\n err.name === 'CryptoError' &&\n 'code' in err &&\n typeof err.code === 'string' &&\n err.code.startsWith('CRYPTO_') &&\n 'message' in err &&\n typeof err.message === 'string'\n );\n}\n\n/**\n * Canonical error codes for local verification\n *\n * These map to E_* codes in specs/kernel/errors.json.\n * JOSE hardening codes (E_JWS_*) are distinct from generic E_INVALID_FORMAT\n * so callers can distinguish key-injection, compression, and crit attacks from\n * ordinary format errors (v0.12.0-preview.1).\n */\nexport type VerifyLocalErrorCode =\n | 'E_INVALID_SIGNATURE'\n | 'E_INVALID_FORMAT'\n | 'E_CONSTRAINT_VIOLATION'\n | 'E_EXPIRED'\n | 'E_NOT_YET_VALID'\n | 'E_INVALID_ISSUER'\n | 'E_INVALID_AUDIENCE'\n | 'E_INVALID_SUBJECT'\n | 'E_INVALID_RECEIPT_ID'\n | 'E_MISSING_EXP'\n | 'E_WIRE_VERSION_MISMATCH'\n | 'E_UNSUPPORTED_WIRE_VERSION'\n | 'E_OCCURRED_AT_FUTURE'\n // JOSE hardening codes (Wire 0.2, v0.12.0-preview.1)\n | 'E_JWS_EMBEDDED_KEY'\n | 'E_JWS_CRIT_REJECTED'\n | 'E_JWS_MISSING_KID'\n | 'E_JWS_B64_REJECTED'\n | 'E_JWS_ZIP_REJECTED'\n // Policy binding (Wire 0.2, v0.12.0-preview.1)\n | 'E_POLICY_BINDING_FAILED'\n // Type-to-extension enforcement (Wire 0.2, v0.12.2)\n | 'E_EXTENSION_GROUP_REQUIRED'\n | 'E_EXTENSION_GROUP_MISMATCH'\n | 'E_INTERNAL';\n\n/**\n * Options for local verification\n */\nexport interface VerifyLocalOptions {\n /**\n * Expected issuer URL\n *\n * If provided, verification fails if receipt.iss does not match.\n */\n issuer?: string;\n\n /**\n * @deprecated Wire 0.2 does not have an `aud` claim. This option is ignored.\n * Retained for source compatibility during migration; will be removed in v1.0.\n */\n audience?: string;\n\n /**\n * Expected subject URI\n *\n * If provided, verification fails if receipt.sub does not match.\n * Binds the receipt to a specific resource/interaction target.\n */\n subjectUri?: string;\n\n /**\n * @deprecated Wire 0.2 does not have a `rid` claim. Use `jti` for receipt identification.\n * This option is ignored. Retained for source compatibility; will be removed in v1.0.\n */\n rid?: string;\n\n /**\n * @deprecated Wire 0.2 receipts do not expire (permanent evidence by design).\n * This option is ignored. Retained for source compatibility; will be removed in v1.0.\n */\n requireExp?: boolean;\n\n /**\n * Current timestamp (Unix seconds)\n *\n * Defaults to Date.now() / 1000. Override for testing.\n */\n now?: number;\n\n /**\n * Maximum clock skew tolerance (seconds)\n *\n * Allows for clock drift between issuer and verifier.\n * Defaults to 300 (5 minutes).\n */\n maxClockSkew?: number;\n\n /**\n * Verification strictness profile (v0.12.0-preview.1).\n *\n * - 'strict' (default): missing typ is a hard error before schema validation.\n * - 'interop': missing typ emits a 'typ_missing' warning and routes by payload content.\n *\n * Strictness is EXCLUSIVELY controlled here (@peac/protocol). @peac/crypto has no strictness param.\n */\n strictness?: VerificationStrictness;\n\n /**\n * Pre-computed local policy digest for policy binding (Wire 0.2, v0.12.0-preview.1).\n *\n * Must be in 'sha256:<64 lowercase hex>' format, computed via computePolicyDigestJcs()\n * from @peac/protocol. When provided alongside a receipt that contains a policy block\n * (policy.digest), the binding check is performed:\n * - Match: policy_binding = 'verified'\n * - Mismatch: hard fail with E_POLICY_BINDING_FAILED\n * - Either absent: policy_binding = 'unavailable'\n *\n * Always 'unavailable' for Wire 0.1 receipts regardless of this option.\n */\n policyDigest?: string;\n}\n\n/**\n * Result of successful local verification (Wire 0.2 only)\n *\n * Wire 0.1 receipts are no longer accepted by verifyLocal() and return\n * E_UNSUPPORTED_WIRE_VERSION. Re-issue as Wire 0.2 using issue().\n */\nexport interface VerifyLocalSuccess {\n /** Verification succeeded */\n valid: true;\n /** Receipt variant (always 'wire-02') */\n variant: 'wire-02';\n /** Validated Wire 0.2 receipt claims */\n claims: Wire02Claims;\n /** Key ID from JWS header (for logging/indexing) */\n kid: string;\n /** Wire format version */\n wireVersion: '0.2';\n /** Verification warnings from schema parsing and strictness routing */\n warnings: VerificationWarning[];\n /**\n * Policy binding status.\n *\n * Three-state result:\n * - 'unavailable': either the receipt contains no policy block, or the\n * caller did not pass a policyDigest option to verifyLocal(). No check.\n * - 'verified': both digests present and match exactly.\n * - 'failed': not returned on success; verifyLocal() returns\n * E_POLICY_BINDING_FAILED (valid: false) before reaching this field.\n */\n policy_binding: PolicyBindingStatus;\n}\n\n/**\n * Result of failed local verification\n */\nexport interface VerifyLocalFailure {\n /** Verification failed */\n valid: false;\n\n /** Canonical error code (maps to specs/kernel/errors.json) */\n code: VerifyLocalErrorCode;\n\n /** Human-readable error message */\n message: string;\n\n /** Structured details for debugging (stable error code preserved in `code`) */\n details?: {\n /** Precise parse error code from unified parser (e.g. E_PARSE_COMMERCE_INVALID) */\n parse_code?: string;\n /** Zod validation issues (bounded, stable shape; non-normative, may change) */\n issues?: ReadonlyArray<{ path: string; message: string }>;\n /**\n * Policy digest from the receipt (present when code is E_POLICY_BINDING_FAILED).\n * Both are SHA-256 hashes; safe to log without leaking policy content.\n */\n receipt_policy_digest?: string;\n /** Caller-supplied policy digest (present when code is E_POLICY_BINDING_FAILED). */\n local_policy_digest?: string;\n /** policy.uri hint from the receipt (present when code is E_POLICY_BINDING_FAILED and uri set). */\n policy_uri?: string;\n /** Receipt type value (present when code is E_EXTENSION_GROUP_REQUIRED or E_EXTENSION_GROUP_MISMATCH). */\n type?: string;\n /** Expected extension group key for the receipt type. */\n expected_extension_group?: string;\n /** Registered extension groups actually present in extensions. */\n present_registered_extension_groups?: string[];\n };\n}\n\n/**\n * Union type for local verification result\n */\nexport type VerifyLocalResult = VerifyLocalSuccess | VerifyLocalFailure;\n\n/**\n * Internal CRYPTO_* codes that map to generic E_INVALID_FORMAT.\n * These are format/encoding errors not security-specific.\n */\nconst FORMAT_ERROR_CODES = new Set([\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'CRYPTO_INVALID_TYP',\n 'CRYPTO_INVALID_ALG',\n 'CRYPTO_INVALID_KEY_LENGTH',\n]);\n\n/**\n * JOSE hardening code mapping: CRYPTO_JWS_* → specific E_JWS_* (v0.12.0-preview.1).\n *\n * Each JOSE hazard code maps to its specific public E_JWS_* counterpart rather than\n * collapsing into the generic E_INVALID_FORMAT. This lets callers distinguish embedded-key\n * injection, crit-header abuse, and unencoded-payload attacks from ordinary format errors.\n */\nconst JOSE_CODE_MAP: Record<string, VerifyLocalErrorCode> = {\n CRYPTO_JWS_EMBEDDED_KEY: 'E_JWS_EMBEDDED_KEY',\n CRYPTO_JWS_CRIT_REJECTED: 'E_JWS_CRIT_REJECTED',\n CRYPTO_JWS_MISSING_KID: 'E_JWS_MISSING_KID',\n CRYPTO_JWS_B64_REJECTED: 'E_JWS_B64_REJECTED',\n CRYPTO_JWS_ZIP_REJECTED: 'E_JWS_ZIP_REJECTED',\n};\n\n/** Max parse issues to include in details (prevents log bloat) */\nconst MAX_PARSE_ISSUES = 25;\n\n/**\n * Sanitize Zod issues into a bounded, stable structure.\n * Avoids exposing raw Zod internals or unbounded arrays in the public API.\n */\nfunction sanitizeParseIssues(\n issues: unknown\n): ReadonlyArray<{ path: string; message: string }> | undefined {\n if (!Array.isArray(issues)) return undefined;\n return issues.slice(0, MAX_PARSE_ISSUES).map((issue) => ({\n path: Array.isArray(issue?.path) ? issue.path.join('.') : '',\n message: typeof issue?.message === 'string' ? issue.message : String(issue),\n }));\n}\n\n/**\n * Verify a Wire 0.2 PEAC receipt locally with a known public key.\n *\n * Wire 0.2 only: Wire 0.1 receipts return E_UNSUPPORTED_WIRE_VERSION.\n * Re-issue Wire 0.1 receipts as Wire 0.2 using issueWire02().\n *\n * This function:\n * 1. Verifies the Ed25519 signature and header (typ, alg)\n * 2. Applies strictness routing for missing typ (strict: hard error; interop: warning)\n * 3. Validates the receipt schema with Zod (Wire 0.2 only)\n * 4. Checks issuer/subject binding (if options provided)\n * 5. Checks time validity (iat with clock skew tolerance)\n * 6. Checks occurred_at skew and collects parse warnings\n *\n * @param jws - JWS compact serialization\n * @param publicKey - Ed25519 public key (32 bytes)\n * @param options - Optional verification options (issuer, subject, clock skew, strictness, policyDigest)\n * @returns Typed verification result\n *\n * @example\n * ```typescript\n * const result = await verifyLocal(jws, publicKey, {\n * issuer: 'https://api.example.com',\n * strictness: 'strict',\n * });\n * if (result.valid) {\n * console.log('Kind:', result.claims.kind);\n * console.log('Warnings:', result.warnings);\n * }\n * ```\n */\nexport async function verifyLocal(\n jws: string,\n publicKey: Uint8Array,\n options: VerifyLocalOptions = {}\n): Promise<VerifyLocalResult> {\n const { issuer, subjectUri, maxClockSkew = 300, strictness = 'strict', policyDigest } = options;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n\n try {\n // 1. Verify signature and header (typ, alg validated by @peac/crypto)\n const result = await jwsVerify<unknown>(jws, publicKey);\n\n if (!result.valid) {\n return {\n valid: false,\n code: 'E_INVALID_SIGNATURE',\n message: 'Ed25519 signature verification failed',\n };\n }\n\n // Accumulated warnings for Wire 0.2 path\n const accumulatedWarnings: VerificationWarning[] = [];\n\n // 2. Strictness routing for missing typ (Correction 1)\n if (result.header.typ === undefined) {\n if (strictness === 'strict') {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: 'Missing JWS typ header: strict mode requires typ to be present',\n };\n }\n // interop mode: emit warning and continue\n accumulatedWarnings.push({\n code: WARNING_TYP_MISSING,\n message: 'JWS typ header is absent; accepted in interop mode',\n });\n }\n\n // 3. Validate structural kernel constraints (fail-closed)\n const constraintResult = validateKernelConstraints(result.payload);\n if (!constraintResult.valid) {\n const v = constraintResult.violations[0];\n return {\n valid: false,\n code: 'E_CONSTRAINT_VIOLATION',\n message: `Kernel constraint violated: ${v.constraint} (actual: ${v.actual}, limit: ${v.limit})`,\n };\n }\n\n // 4. Validate schema (unified parser supports Wire 0.1 and Wire 0.2)\n const pr = parseReceiptClaims(result.payload);\n\n if (!pr.ok) {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: `Receipt schema validation failed: ${pr.error.message}`,\n details: { parse_code: pr.error.code, issues: sanitizeParseIssues(pr.error.issues) },\n };\n }\n\n // 5. Collect parser warnings (Wire 0.2 parser may emit type/extension warnings)\n if (pr.wireVersion === '0.2') {\n accumulatedWarnings.push(...pr.warnings);\n }\n\n // Wire 0.2 path\n if (pr.wireVersion === '0.2') {\n const claims = pr.claims as Wire02Claims;\n\n // Issuer check\n if (issuer !== undefined && claims.iss !== issuer) {\n return {\n valid: false,\n code: 'E_INVALID_ISSUER',\n message: `Issuer mismatch: expected \"${issuer}\", got \"${claims.iss}\"`,\n };\n }\n\n // Subject check\n if (subjectUri !== undefined && claims.sub !== subjectUri) {\n return {\n valid: false,\n code: 'E_INVALID_SUBJECT',\n message: `Subject mismatch: expected \"${subjectUri}\", got \"${claims.sub ?? 'undefined'}\"`,\n };\n }\n\n // iat: not-yet-valid check (with clock skew)\n if (claims.iat > now + maxClockSkew) {\n return {\n valid: false,\n code: 'E_NOT_YET_VALID',\n message: `Receipt not yet valid: issued at ${new Date(claims.iat * 1000).toISOString()}, now is ${new Date(now * 1000).toISOString()}`,\n };\n }\n\n // occurred_at skew check (evidence kind only)\n if (claims.kind === 'evidence') {\n const skewResult = checkOccurredAtSkew(claims.occurred_at, claims.iat, now, maxClockSkew);\n if (skewResult === 'future_error') {\n return {\n valid: false,\n code: 'E_OCCURRED_AT_FUTURE',\n message: `occurred_at is in the future beyond tolerance (${maxClockSkew}s)`,\n };\n }\n if (skewResult !== null) {\n accumulatedWarnings.push(skewResult);\n }\n }\n\n // Emit type_unregistered warning for valid-but-unregistered type values\n if (!REGISTERED_RECEIPT_TYPES.has(claims.type)) {\n accumulatedWarnings.push({\n code: WARNING_TYPE_UNREGISTERED,\n message: 'Receipt type is not in the recommended type registry',\n pointer: '/type',\n });\n }\n\n // Emit unknown_extension_preserved warnings for unrecognized-but-well-formed keys\n // Malformed keys are already hard errors (E_INVALID_EXTENSION_KEY) at schema layer.\n if (claims.extensions !== undefined) {\n for (const key of Object.keys(claims.extensions)) {\n if (!REGISTERED_EXTENSION_GROUP_KEYS.has(key) && isValidExtensionKey(key)) {\n // RFC 6901: '~' -> '~0', '/' -> '~1'\n const escapedKey = key.replace(/~/g, '~0').replace(/\\//g, '~1');\n accumulatedWarnings.push({\n code: WARNING_UNKNOWN_EXTENSION,\n message: 'Unknown extension key preserved without schema validation',\n pointer: `/extensions/${escapedKey}`,\n });\n }\n }\n }\n\n // Type-to-extension enforcement: check that the expected extension group is present\n const typeExtCheck = checkTypeExtensionMapping(\n claims.kind,\n claims.type,\n claims.extensions,\n TYPE_TO_EXTENSION_MAP,\n REGISTERED_EXTENSION_GROUP_KEYS\n );\n\n if (typeExtCheck.status === 'missing' || typeExtCheck.status === 'mismatch') {\n const warningCode =\n typeExtCheck.status === 'missing'\n ? WARNING_EXTENSION_GROUP_MISSING\n : WARNING_EXTENSION_GROUP_MISMATCH;\n const errorCode =\n typeExtCheck.status === 'missing'\n ? 'E_EXTENSION_GROUP_REQUIRED'\n : 'E_EXTENSION_GROUP_MISMATCH';\n\n if (strictness === 'strict') {\n return {\n valid: false,\n code: errorCode,\n message: `Type \"${claims.type}\" expects extension group \"${typeExtCheck.expected_extension_group}\" but it is ${typeExtCheck.status === 'mismatch' ? 'replaced by a different registered group' : 'absent'}`,\n details: {\n type: claims.type,\n expected_extension_group: typeExtCheck.expected_extension_group,\n present_registered_extension_groups: typeExtCheck.present_registered_extension_groups,\n },\n };\n }\n\n // Interop mode: emit warning, continue verification\n accumulatedWarnings.push({\n code: warningCode,\n message: `Type \"${claims.type}\" expects extension group \"${typeExtCheck.expected_extension_group}\"`,\n pointer: '/type',\n });\n }\n\n // Validate policyDigest option format: must be sha256:<64 lowercase hex> if provided.\n if (policyDigest !== undefined && !HASH.pattern.test(policyDigest)) {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: 'policyDigest option must be in sha256:<64 lowercase hex> format',\n };\n }\n\n // Policy binding check: 3-state result.\n // 'unavailable' when either receipt has no policy block or caller omitted policyDigest.\n // 'verified' / 'failed' when both are present; 'failed' is a hard verification error.\n const receiptPolicyDigest = claims.policy?.digest;\n const bindingStatus: PolicyBindingStatus =\n receiptPolicyDigest === undefined || policyDigest === undefined\n ? 'unavailable'\n : verifyPolicyBinding(receiptPolicyDigest, policyDigest);\n if (bindingStatus === 'failed') {\n return {\n valid: false,\n code: 'E_POLICY_BINDING_FAILED',\n message: 'Policy binding check failed: receipt policy digest does not match local policy',\n details: {\n receipt_policy_digest: receiptPolicyDigest,\n local_policy_digest: policyDigest,\n ...(claims.policy?.uri !== undefined && { policy_uri: claims.policy.uri }),\n },\n };\n }\n\n return {\n valid: true,\n variant: 'wire-02',\n claims,\n kid: result.header.kid,\n wireVersion: '0.2',\n warnings: sortWarnings(accumulatedWarnings),\n policy_binding: bindingStatus,\n };\n }\n\n // Wire 0.1 receipts: reject with E_UNSUPPORTED_WIRE_VERSION.\n return {\n valid: false,\n code: 'E_UNSUPPORTED_WIRE_VERSION',\n message: 'Wire 0.1 receipts are not supported. Re-issue as Wire 0.2 using issue().',\n };\n } catch (err) {\n // Handle typed CryptoError from @peac/crypto\n // Use structural check instead of instanceof for robustness across ESM/CJS boundaries\n // Map internal CRYPTO_* codes to canonical E_* codes.\n // JOSE hardening codes get specific E_JWS_* (not generic E_INVALID_FORMAT) so callers\n // can distinguish key-injection attacks from ordinary encoding errors.\n if (isCryptoError(err)) {\n // 1. JOSE hardening: specific E_JWS_* codes (checked first)\n if (Object.prototype.hasOwnProperty.call(JOSE_CODE_MAP, err.code)) {\n return {\n valid: false,\n code: JOSE_CODE_MAP[err.code]!,\n message: err.message,\n };\n }\n // 2. Generic format errors\n if (FORMAT_ERROR_CODES.has(err.code)) {\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: err.message,\n };\n }\n if (err.code === 'CRYPTO_INVALID_SIGNATURE') {\n return {\n valid: false,\n code: 'E_INVALID_SIGNATURE',\n message: err.message,\n };\n }\n if (err.code === 'CRYPTO_WIRE_VERSION_MISMATCH') {\n return {\n valid: false,\n code: 'E_WIRE_VERSION_MISMATCH',\n message: err.message,\n };\n }\n }\n\n // Handle JSON parse errors from malformed payloads\n // Use structural check for cross-boundary robustness (consistent with isCryptoError pattern)\n if (\n err !== null &&\n typeof err === 'object' &&\n 'name' in err &&\n (err as { name: unknown }).name === 'SyntaxError'\n ) {\n const syntaxMessage =\n 'message' in err && typeof (err as { message: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Invalid JSON';\n return {\n valid: false,\n code: 'E_INVALID_FORMAT',\n message: `Invalid receipt payload: ${syntaxMessage}`,\n };\n }\n\n // All other errors -> E_INTERNAL\n // No message parsing - code-based mapping only\n const message = err instanceof Error ? err.message : String(err);\n return {\n valid: false,\n code: 'E_INTERNAL',\n message: `Unexpected verification error: ${message}`,\n };\n }\n}\n\n/**\n * @deprecated Removed: verifyLocal() is Wire 0.2 only and always returns variant 'wire-02'.\n * This guard always returns false. Remove usage and use isWire02Result() instead.\n */\nexport function isCommerceResult(\n r: VerifyLocalResult\n): r is VerifyLocalSuccess & { variant: 'wire-02' } {\n // Always false: verifyLocal() only returns variant 'wire-02'\n return false;\n}\n\n/**\n * @deprecated Removed: verifyLocal() is Wire 0.2 only and always returns variant 'wire-02'.\n * This guard always returns false. Remove usage and use isWire02Result() instead.\n */\nexport function isAttestationResult(\n r: VerifyLocalResult\n): r is VerifyLocalSuccess & { variant: 'wire-02' } {\n // Always false: verifyLocal() only returns variant 'wire-02'\n return false;\n}\n\n/**\n * Type guard: narrows a VerifyLocalResult to a Wire 0.2 success (v0.12.0-preview.1).\n *\n * Use instead of manual `result.valid && result.variant === 'wire-02'` checks\n * to get proper claims narrowing to Wire02Claims.\n */\nexport function isWire02Result(\n r: VerifyLocalResult\n): r is VerifyLocalSuccess & { variant: 'wire-02' } {\n return r.valid === true && r.variant === 'wire-02';\n}\n"]}
@@ -13,9 +13,9 @@ import type { PolicyBindingStatus } from './verifier-types';
13
13
  * These map to E_* codes in specs/kernel/errors.json.
14
14
  * JOSE hardening codes (E_JWS_*) are distinct from generic E_INVALID_FORMAT
15
15
  * so callers can distinguish key-injection, compression, and crit attacks from
16
- * ordinary format errors (v0.12.0-preview.1, DD-156).
16
+ * ordinary format errors (v0.12.0-preview.1).
17
17
  */
18
- export type VerifyLocalErrorCode = 'E_INVALID_SIGNATURE' | 'E_INVALID_FORMAT' | 'E_CONSTRAINT_VIOLATION' | 'E_EXPIRED' | 'E_NOT_YET_VALID' | 'E_INVALID_ISSUER' | 'E_INVALID_AUDIENCE' | 'E_INVALID_SUBJECT' | 'E_INVALID_RECEIPT_ID' | 'E_MISSING_EXP' | 'E_WIRE_VERSION_MISMATCH' | 'E_UNSUPPORTED_WIRE_VERSION' | 'E_OCCURRED_AT_FUTURE' | 'E_JWS_EMBEDDED_KEY' | 'E_JWS_CRIT_REJECTED' | 'E_JWS_MISSING_KID' | 'E_JWS_B64_REJECTED' | 'E_JWS_ZIP_REJECTED' | 'E_POLICY_BINDING_FAILED' | 'E_INTERNAL';
18
+ export type VerifyLocalErrorCode = 'E_INVALID_SIGNATURE' | 'E_INVALID_FORMAT' | 'E_CONSTRAINT_VIOLATION' | 'E_EXPIRED' | 'E_NOT_YET_VALID' | 'E_INVALID_ISSUER' | 'E_INVALID_AUDIENCE' | 'E_INVALID_SUBJECT' | 'E_INVALID_RECEIPT_ID' | 'E_MISSING_EXP' | 'E_WIRE_VERSION_MISMATCH' | 'E_UNSUPPORTED_WIRE_VERSION' | 'E_OCCURRED_AT_FUTURE' | 'E_JWS_EMBEDDED_KEY' | 'E_JWS_CRIT_REJECTED' | 'E_JWS_MISSING_KID' | 'E_JWS_B64_REJECTED' | 'E_JWS_ZIP_REJECTED' | 'E_POLICY_BINDING_FAILED' | 'E_EXTENSION_GROUP_REQUIRED' | 'E_EXTENSION_GROUP_MISMATCH' | 'E_INTERNAL';
19
19
  /**
20
20
  * Options for local verification
21
21
  */
@@ -62,7 +62,7 @@ export interface VerifyLocalOptions {
62
62
  */
63
63
  maxClockSkew?: number;
64
64
  /**
65
- * Verification strictness profile (v0.12.0-preview.1, DD-156).
65
+ * Verification strictness profile (v0.12.0-preview.1).
66
66
  *
67
67
  * - 'strict' (default): missing typ is a hard error before schema validation.
68
68
  * - 'interop': missing typ emits a 'typ_missing' warning and routes by payload content.
@@ -71,7 +71,7 @@ export interface VerifyLocalOptions {
71
71
  */
72
72
  strictness?: VerificationStrictness;
73
73
  /**
74
- * Pre-computed local policy digest for policy binding (Wire 0.2, v0.12.0-preview.1, DD-151).
74
+ * Pre-computed local policy digest for policy binding (Wire 0.2, v0.12.0-preview.1).
75
75
  *
76
76
  * Must be in 'sha256:<64 lowercase hex>' format, computed via computePolicyDigestJcs()
77
77
  * from @peac/protocol. When provided alongside a receipt that contains a policy block
@@ -104,7 +104,7 @@ export interface VerifyLocalSuccess {
104
104
  /** Verification warnings from schema parsing and strictness routing */
105
105
  warnings: VerificationWarning[];
106
106
  /**
107
- * Policy binding status (DD-49, DD-151).
107
+ * Policy binding status.
108
108
  *
109
109
  * Three-state result:
110
110
  * - 'unavailable': either the receipt contains no policy block, or the
@@ -143,6 +143,12 @@ export interface VerifyLocalFailure {
143
143
  local_policy_digest?: string;
144
144
  /** policy.uri hint from the receipt (present when code is E_POLICY_BINDING_FAILED and uri set). */
145
145
  policy_uri?: string;
146
+ /** Receipt type value (present when code is E_EXTENSION_GROUP_REQUIRED or E_EXTENSION_GROUP_MISMATCH). */
147
+ type?: string;
148
+ /** Expected extension group key for the receipt type. */
149
+ expected_extension_group?: string;
150
+ /** Registered extension groups actually present in extensions. */
151
+ present_registered_extension_groups?: string[];
146
152
  };
147
153
  }
148
154
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"verify-local.d.ts","sourceRoot":"","sources":["../src/verify-local.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verify-local.d.ts","sourceRoot":"","sources":["../src/verify-local.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,35 @@
1
1
  import { verify } from '@peac/crypto';
2
- import { HASH } from '@peac/kernel';
3
- import { WARNING_TYP_MISSING, validateKernelConstraints, parseReceiptClaims, checkOccurredAtSkew, REGISTERED_RECEIPT_TYPES, WARNING_TYPE_UNREGISTERED, REGISTERED_EXTENSION_GROUP_KEYS, isValidExtensionKey, WARNING_UNKNOWN_EXTENSION, verifyPolicyBinding, sortWarnings } from '@peac/schema';
2
+ import { TYPE_TO_EXTENSION_MAP, HASH } from '@peac/kernel';
3
+ import { WARNING_TYP_MISSING, validateKernelConstraints, parseReceiptClaims, checkOccurredAtSkew, REGISTERED_RECEIPT_TYPES, WARNING_TYPE_UNREGISTERED, REGISTERED_EXTENSION_GROUP_KEYS, isValidExtensionKey, WARNING_UNKNOWN_EXTENSION, WARNING_EXTENSION_GROUP_MISSING, WARNING_EXTENSION_GROUP_MISMATCH, verifyPolicyBinding, sortWarnings } from '@peac/schema';
4
+
5
+ // src/verify-local.ts
6
+
7
+ // src/type-extension-check.ts
8
+ function checkTypeExtensionMapping(kind, type, extensions, typeToExtensionMap, registeredExtensionGroupKeys) {
9
+ if (kind === "challenge") {
10
+ return { status: "skip" };
11
+ }
12
+ const expectedGroup = typeToExtensionMap.get(type);
13
+ if (expectedGroup === void 0) {
14
+ return { status: "skip" };
15
+ }
16
+ if (extensions !== void 0 && Object.prototype.hasOwnProperty.call(extensions, expectedGroup)) {
17
+ return { status: "ok" };
18
+ }
19
+ const presentRegistered = [];
20
+ if (extensions !== void 0) {
21
+ for (const key of Object.keys(extensions)) {
22
+ if (key !== expectedGroup && registeredExtensionGroupKeys.has(key)) {
23
+ presentRegistered.push(key);
24
+ }
25
+ }
26
+ }
27
+ return {
28
+ status: presentRegistered.length > 0 ? "mismatch" : "missing",
29
+ expected_extension_group: expectedGroup,
30
+ present_registered_extension_groups: presentRegistered
31
+ };
32
+ }
4
33
 
5
34
  // src/verify-local.ts
6
35
  function isCryptoError(err) {
@@ -129,6 +158,34 @@ async function verifyLocal(jws, publicKey, options = {}) {
129
158
  }
130
159
  }
131
160
  }
161
+ const typeExtCheck = checkTypeExtensionMapping(
162
+ claims.kind,
163
+ claims.type,
164
+ claims.extensions,
165
+ TYPE_TO_EXTENSION_MAP,
166
+ REGISTERED_EXTENSION_GROUP_KEYS
167
+ );
168
+ if (typeExtCheck.status === "missing" || typeExtCheck.status === "mismatch") {
169
+ const warningCode = typeExtCheck.status === "missing" ? WARNING_EXTENSION_GROUP_MISSING : WARNING_EXTENSION_GROUP_MISMATCH;
170
+ const errorCode = typeExtCheck.status === "missing" ? "E_EXTENSION_GROUP_REQUIRED" : "E_EXTENSION_GROUP_MISMATCH";
171
+ if (strictness === "strict") {
172
+ return {
173
+ valid: false,
174
+ code: errorCode,
175
+ message: `Type "${claims.type}" expects extension group "${typeExtCheck.expected_extension_group}" but it is ${typeExtCheck.status === "mismatch" ? "replaced by a different registered group" : "absent"}`,
176
+ details: {
177
+ type: claims.type,
178
+ expected_extension_group: typeExtCheck.expected_extension_group,
179
+ present_registered_extension_groups: typeExtCheck.present_registered_extension_groups
180
+ }
181
+ };
182
+ }
183
+ accumulatedWarnings.push({
184
+ code: warningCode,
185
+ message: `Type "${claims.type}" expects extension group "${typeExtCheck.expected_extension_group}"`,
186
+ pointer: "/type"
187
+ });
188
+ }
132
189
  if (policyDigest !== void 0 && !HASH.pattern.test(policyDigest)) {
133
190
  return {
134
191
  valid: false,