@peac/crypto 0.11.2 → 0.12.0-preview.1

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/errors.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * These are NOT canonical protocol error codes. They are internal to @peac/crypto.
14
14
  * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).
15
15
  */
16
- export type CryptoErrorCode = 'CRYPTO_INVALID_KEY_LENGTH' | 'CRYPTO_INVALID_SEED_LENGTH' | 'CRYPTO_INVALID_JWS_FORMAT' | 'CRYPTO_INVALID_TYP' | 'CRYPTO_INVALID_ALG' | 'CRYPTO_INVALID_SIGNATURE';
16
+ export type CryptoErrorCode = 'CRYPTO_INVALID_KEY_LENGTH' | 'CRYPTO_INVALID_SEED_LENGTH' | 'CRYPTO_INVALID_JWS_FORMAT' | 'CRYPTO_INVALID_TYP' | 'CRYPTO_INVALID_ALG' | 'CRYPTO_INVALID_SIGNATURE' | 'CRYPTO_WIRE_VERSION_MISMATCH' | 'CRYPTO_JWS_EMBEDDED_KEY' | 'CRYPTO_JWS_CRIT_REJECTED' | 'CRYPTO_JWS_MISSING_KID' | 'CRYPTO_JWS_B64_REJECTED' | 'CRYPTO_JWS_ZIP_REJECTED';
17
17
  /**
18
18
  * Typed error for crypto operations
19
19
  *
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,2BAA2B,GAC3B,4BAA4B,GAC5B,2BAA2B,GAC3B,oBAAoB,GACpB,oBAAoB,GACpB,0BAA0B,CAAC;AAE/B;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBAEnB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM;CAOnD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAQ5D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,2BAA2B,GAC3B,4BAA4B,GAC5B,2BAA2B,GAC3B,oBAAoB,GACpB,oBAAoB,GACpB,0BAA0B,GAE1B,8BAA8B,GAC9B,yBAAyB,GACzB,0BAA0B,GAC1B,wBAAwB,GACxB,yBAAyB,GACzB,yBAAyB,CAAC;AAE9B;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBAEnB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM;CAOnD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAQ5D"}
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var ed = require('@noble/ed25519');
4
- var schema = require('@peac/schema');
4
+ var kernel = require('@peac/kernel');
5
5
 
6
6
  function _interopNamespace(e) {
7
7
  if (e && e.__esModule) return e;
@@ -233,13 +233,84 @@ var sign = ed__namespace.signAsync;
233
233
  var verify = ed__namespace.verifyAsync;
234
234
  var getPublicKey = ed__namespace.getPublicKeyAsync;
235
235
  var randomSecretKey = ed__namespace.utils.randomSecretKey;
236
+ var ACCEPTED_TYP_VALUES = /* @__PURE__ */ new Set([kernel.WIRE_01_JWS_TYP, ...kernel.WIRE_02_JWS_TYP_ACCEPT]);
237
+ function validateWire02Header(header) {
238
+ if (!header.kid || typeof header.kid !== "string" || header.kid.length === 0 || header.kid.length > 256) {
239
+ throw new CryptoError(
240
+ "CRYPTO_JWS_MISSING_KID",
241
+ "kid is missing, empty, or exceeds 256 characters"
242
+ );
243
+ }
244
+ if (header.jwk !== void 0 || header.x5c !== void 0 || header.x5u !== void 0 || header.jku !== void 0) {
245
+ throw new CryptoError(
246
+ "CRYPTO_JWS_EMBEDDED_KEY",
247
+ "embedded key material (jwk/x5c/x5u/jku) is not permitted"
248
+ );
249
+ }
250
+ if (header.crit !== void 0) {
251
+ throw new CryptoError("CRYPTO_JWS_CRIT_REJECTED", "crit header is not permitted");
252
+ }
253
+ if (header.b64 === false) {
254
+ throw new CryptoError(
255
+ "CRYPTO_JWS_B64_REJECTED",
256
+ "b64:false (unencoded payload) is not permitted"
257
+ );
258
+ }
259
+ if (header.zip !== void 0) {
260
+ throw new CryptoError("CRYPTO_JWS_ZIP_REJECTED", "zip header is not permitted");
261
+ }
262
+ }
263
+ function buildHeader(raw) {
264
+ if (raw.alg !== kernel.PEAC_ALG) {
265
+ throw new CryptoError(
266
+ "CRYPTO_INVALID_ALG",
267
+ `Invalid alg: expected ${kernel.PEAC_ALG}, got ${String(raw.alg)}`
268
+ );
269
+ }
270
+ const rawTyp = raw.typ;
271
+ const canonicalTyp = rawTyp === "application/interaction-record+jwt" ? kernel.WIRE_02_JWS_TYP : rawTyp;
272
+ if (canonicalTyp !== void 0 && !ACCEPTED_TYP_VALUES.has(rawTyp)) {
273
+ throw new CryptoError("CRYPTO_INVALID_TYP", `Invalid typ: ${String(rawTyp)}`);
274
+ }
275
+ const kid = typeof raw.kid === "string" ? raw.kid : "";
276
+ if (canonicalTyp === kernel.WIRE_01_JWS_TYP) {
277
+ return { typ: kernel.WIRE_01_JWS_TYP, alg: kernel.PEAC_ALG, kid };
278
+ }
279
+ if (canonicalTyp === kernel.WIRE_02_JWS_TYP) {
280
+ return { typ: kernel.WIRE_02_JWS_TYP, alg: kernel.PEAC_ALG, kid };
281
+ }
282
+ return { typ: void 0, alg: kernel.PEAC_ALG, kid };
283
+ }
236
284
  async function sign2(payload, privateKey, kid) {
237
285
  if (privateKey.length !== 32) {
238
286
  throw new CryptoError("CRYPTO_INVALID_KEY_LENGTH", "Ed25519 private key must be 32 bytes");
239
287
  }
240
288
  const header = {
241
- typ: schema.PEAC_WIRE_TYP,
242
- alg: schema.PEAC_ALG,
289
+ typ: kernel.WIRE_01_JWS_TYP,
290
+ alg: kernel.PEAC_ALG,
291
+ kid
292
+ };
293
+ const headerB64 = base64urlEncodeString(JSON.stringify(header));
294
+ const payloadB64 = base64urlEncodeString(JSON.stringify(payload));
295
+ const signingInput = `${headerB64}.${payloadB64}`;
296
+ const signingInputBytes = new TextEncoder().encode(signingInput);
297
+ const signatureBytes = await sign(signingInputBytes, privateKey);
298
+ const signatureB64 = base64urlEncode(signatureBytes);
299
+ return `${signingInput}.${signatureB64}`;
300
+ }
301
+ async function signWire02(payload, privateKey, kid) {
302
+ if (privateKey.length !== 32) {
303
+ throw new CryptoError("CRYPTO_INVALID_KEY_LENGTH", "Ed25519 private key must be 32 bytes");
304
+ }
305
+ if (!kid || kid.length === 0 || kid.length > 256) {
306
+ throw new CryptoError(
307
+ "CRYPTO_JWS_MISSING_KID",
308
+ "kid is missing, empty, or exceeds 256 characters"
309
+ );
310
+ }
311
+ const header = {
312
+ typ: kernel.WIRE_02_JWS_TYP,
313
+ alg: kernel.PEAC_ALG,
243
314
  kid
244
315
  };
245
316
  const headerB64 = base64urlEncodeString(JSON.stringify(header));
@@ -254,6 +325,12 @@ async function verify2(jws, publicKey) {
254
325
  if (publicKey.length !== 32) {
255
326
  throw new CryptoError("CRYPTO_INVALID_KEY_LENGTH", "Ed25519 public key must be 32 bytes");
256
327
  }
328
+ if (jws.length > kernel.VERIFIER_LIMITS.maxReceiptBytes) {
329
+ throw new CryptoError(
330
+ "CRYPTO_INVALID_JWS_FORMAT",
331
+ `JWS exceeds maximum size of ${kernel.VERIFIER_LIMITS.maxReceiptBytes} bytes`
332
+ );
333
+ }
257
334
  const parts = jws.split(".");
258
335
  if (parts.length !== 3) {
259
336
  throw new CryptoError(
@@ -262,31 +339,42 @@ async function verify2(jws, publicKey) {
262
339
  );
263
340
  }
264
341
  const [headerB64, payloadB64, signatureB64] = parts;
265
- const headerJson = base64urlDecodeString(headerB64);
266
- const header = JSON.parse(headerJson);
267
- if (header.typ !== schema.PEAC_WIRE_TYP) {
268
- throw new CryptoError(
269
- "CRYPTO_INVALID_TYP",
270
- `Invalid typ: expected ${schema.PEAC_WIRE_TYP}, got ${header.typ}`
271
- );
342
+ let rawHeader;
343
+ try {
344
+ rawHeader = JSON.parse(base64urlDecodeString(headerB64));
345
+ } catch {
346
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS header: invalid base64url or JSON");
272
347
  }
273
- if (header.alg !== schema.PEAC_ALG) {
274
- throw new CryptoError(
275
- "CRYPTO_INVALID_ALG",
276
- `Invalid alg: expected ${schema.PEAC_ALG}, got ${header.alg}`
277
- );
348
+ const header = buildHeader(rawHeader);
349
+ if (header.typ === kernel.WIRE_02_JWS_TYP || header.typ === void 0) {
350
+ validateWire02Header(rawHeader);
351
+ }
352
+ let payload;
353
+ try {
354
+ payload = JSON.parse(base64urlDecodeString(payloadB64));
355
+ } catch {
356
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS payload: invalid base64url or JSON");
357
+ }
358
+ if (header.typ !== void 0) {
359
+ const payloadVersion = payload.peac_version;
360
+ if (header.typ === kernel.WIRE_02_JWS_TYP && payloadVersion !== "0.2") {
361
+ throw new CryptoError(
362
+ "CRYPTO_WIRE_VERSION_MISMATCH",
363
+ `typ is ${kernel.WIRE_02_JWS_TYP} but peac_version is ${String(payloadVersion)} (expected '0.2')`
364
+ );
365
+ }
366
+ if (header.typ === kernel.WIRE_01_JWS_TYP && payloadVersion === "0.2") {
367
+ throw new CryptoError(
368
+ "CRYPTO_WIRE_VERSION_MISMATCH",
369
+ `typ is ${kernel.WIRE_01_JWS_TYP} but peac_version is '0.2' (Wire 0.2 must use ${kernel.WIRE_02_JWS_TYP})`
370
+ );
371
+ }
278
372
  }
279
- const payloadJson = base64urlDecodeString(payloadB64);
280
- const payload = JSON.parse(payloadJson);
281
373
  const signatureBytes = base64urlDecode(signatureB64);
282
374
  const signingInput = `${headerB64}.${payloadB64}`;
283
375
  const signingInputBytes = new TextEncoder().encode(signingInput);
284
376
  const valid = await verify(signatureBytes, signingInputBytes, publicKey);
285
- return {
286
- header,
287
- payload,
288
- valid
289
- };
377
+ return { header, payload, valid };
290
378
  }
291
379
  function decode(jws) {
292
380
  const parts = jws.split(".");
@@ -297,10 +385,19 @@ function decode(jws) {
297
385
  );
298
386
  }
299
387
  const [headerB64, payloadB64] = parts;
300
- const headerJson = base64urlDecodeString(headerB64);
301
- const header = JSON.parse(headerJson);
302
- const payloadJson = base64urlDecodeString(payloadB64);
303
- const payload = JSON.parse(payloadJson);
388
+ let rawHeader;
389
+ try {
390
+ rawHeader = JSON.parse(base64urlDecodeString(headerB64));
391
+ } catch {
392
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS header: invalid base64url or JSON");
393
+ }
394
+ const header = buildHeader(rawHeader);
395
+ let payload;
396
+ try {
397
+ payload = JSON.parse(base64urlDecodeString(payloadB64));
398
+ } catch {
399
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS payload: invalid base64url or JSON");
400
+ }
304
401
  return { header, payload };
305
402
  }
306
403
  async function generateKeypair() {
@@ -363,7 +460,9 @@ exports.sha256Base64url = sha256Base64url;
363
460
  exports.sha256Bytes = sha256Bytes;
364
461
  exports.sha256Hex = sha256Hex;
365
462
  exports.sign = sign2;
463
+ exports.signWire02 = signWire02;
366
464
  exports.validateKeypair = validateKeypair;
465
+ exports.validateWire02Header = validateWire02Header;
367
466
  exports.verify = verify2;
368
467
  //# sourceMappingURL=index.cjs.map
369
468
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/base64url.ts","../src/errors.ts","../src/hash.ts","../src/jcs.ts","../src/ed25519.ts","../src/jws.ts"],"names":["ed","sign","PEAC_WIRE_TYP","PEAC_ALG","verify"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base64url encoding/decoding (RFC 4648 §5)\n *\n * Platform-agnostic implementation that works in Node.js, browser, and edge runtimes.\n * No Buffer dependency - uses pure JavaScript for maximum portability.\n *\n * @packageDocumentation\n */\n\n// Standard base64 alphabet\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n// Reverse lookup table (lazily initialized)\nlet base64Lookup: Map<string, number> | null = null;\n\nfunction getBase64Lookup(): Map<string, number> {\n if (!base64Lookup) {\n base64Lookup = new Map();\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n base64Lookup.set(BASE64_ALPHABET[i], i);\n }\n }\n return base64Lookup;\n}\n\n/**\n * Encode bytes to base64url string (no padding)\n *\n * @param bytes - Byte array to encode\n * @returns Base64url encoded string (no padding)\n */\nexport function base64urlEncode(bytes: Uint8Array): string {\n let result = '';\n let i = 0;\n\n while (i < bytes.length) {\n const a = bytes[i++];\n const b = bytes[i++] ?? 0;\n const c = bytes[i++] ?? 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += BASE64_ALPHABET[(triplet >> 18) & 0x3f];\n result += BASE64_ALPHABET[(triplet >> 12) & 0x3f];\n result += i - 2 < bytes.length ? BASE64_ALPHABET[(triplet >> 6) & 0x3f] : '';\n result += i - 1 < bytes.length ? BASE64_ALPHABET[triplet & 0x3f] : '';\n }\n\n // Convert to base64url (no padding)\n return result.replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n\n/**\n * Decode base64url string to bytes\n *\n * @param str - Base64url encoded string\n * @returns Decoded bytes\n */\nexport function base64urlDecode(str: string): Uint8Array {\n // Convert base64url to base64\n let base64 = str.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n while (base64.length % 4 !== 0) {\n base64 += '=';\n }\n\n const lookup = getBase64Lookup();\n const bytes: number[] = [];\n\n for (let i = 0; i < base64.length; i += 4) {\n const a = lookup.get(base64[i]) ?? 0;\n const b = lookup.get(base64[i + 1]) ?? 0;\n const c = lookup.get(base64[i + 2]) ?? 0;\n const d = lookup.get(base64[i + 3]) ?? 0;\n\n bytes.push((a << 2) | (b >> 4));\n if (base64[i + 2] !== '=') {\n bytes.push(((b & 0x0f) << 4) | (c >> 2));\n }\n if (base64[i + 3] !== '=') {\n bytes.push(((c & 0x03) << 6) | d);\n }\n }\n\n return new Uint8Array(bytes);\n}\n\n/**\n * Encode UTF-8 string to base64url\n *\n * @param str - UTF-8 string to encode\n * @returns Base64url encoded string\n */\nexport function base64urlEncodeString(str: string): string {\n return base64urlEncode(new TextEncoder().encode(str));\n}\n\n/**\n * Decode base64url to UTF-8 string\n *\n * @param str - Base64url encoded string\n * @returns Decoded UTF-8 string\n */\nexport function base64urlDecodeString(str: string): string {\n return new TextDecoder().decode(base64urlDecode(str));\n}\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * Cryptographic hash utilities\n *\n * Platform-agnostic SHA-256 implementation that works in Node.js, browser, and edge runtimes.\n * Uses Web Crypto API with Node.js crypto fallback.\n *\n * @packageDocumentation\n */\n\nimport { base64urlDecode, base64urlEncode } from './base64url.js';\n\n/**\n * Compute SHA-256 hash of data and return as lowercase hex string\n *\n * Uses Web Crypto API if available, falls back to Node.js crypto.\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Lowercase hex string (64 characters)\n */\nexport async function sha256Hex(data: Uint8Array | string): Promise<string> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first (works in browser, edge, and Node 20+)\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return hash.digest('hex');\n } catch {\n throw new Error(\n 'No SHA-256 implementation available. Ensure Web Crypto API or Node.js crypto is available.'\n );\n }\n}\n\n/**\n * Compute SHA-256 hash of data and return as Uint8Array (32 bytes)\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Hash as Uint8Array (32 bytes)\n */\nexport async function sha256Bytes(data: Uint8Array | string): Promise<Uint8Array> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n return new Uint8Array(hashBuffer);\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return new Uint8Array(hash.digest());\n } catch {\n throw new Error('No SHA-256 implementation available.');\n }\n}\n\n/**\n * Convert hex string to Uint8Array\n *\n * @param hex - Lowercase hex string\n * @returns Uint8Array\n */\nexport function hexToBytes(hex: string): Uint8Array {\n // Validate: must be even length and contain only hex characters\n if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {\n throw new Error('Invalid hex string');\n }\n if (hex.length === 0) {\n return new Uint8Array([]);\n }\n const matches = hex.match(/.{2}/g);\n if (!matches) {\n throw new Error('Invalid hex string');\n }\n return new Uint8Array(matches.map((byte) => parseInt(byte, 16)));\n}\n\n/**\n * Convert Uint8Array to lowercase hex string\n *\n * @param bytes - Byte array\n * @returns Lowercase hex string\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Compute SHA-256 hash of data and return as base64url string\n *\n * Used by Wire 0.2 DigestRef format. Evidence bundles use sha256Hex() with\n * \"sha256:<hex>\" prefix instead. A conversion bridge between the two formats\n * (hex <-> base64url) is provided by hexToBase64url() and base64urlToHex().\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Base64url-encoded SHA-256 hash (43 characters)\n */\nexport async function sha256Base64url(data: Uint8Array | string): Promise<string> {\n const hashBytes = await sha256Bytes(data);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert a hex-encoded hash to base64url encoding\n *\n * Bridges evidence bundle format (sha256:<hex>) to Wire 0.2 DigestRef format\n * (base64url value). Strips the \"sha256:\" prefix if present.\n *\n * @param hex - Hex string, optionally prefixed with \"sha256:\"\n * @returns Base64url-encoded bytes (43 characters for SHA-256)\n */\nexport function hexToBase64url(hex: string): string {\n const cleanHex = hex.startsWith('sha256:') ? hex.slice(7) : hex;\n return base64urlEncode(hexToBytes(cleanHex));\n}\n\n/**\n * Convert a base64url-encoded hash to hex encoding\n *\n * Bridges Wire 0.2 DigestRef format (base64url) to evidence bundle format\n * (hex). Does NOT add the \"sha256:\" prefix -- caller adds it if needed.\n *\n * @param b64u - Base64url-encoded hash\n * @returns Lowercase hex string (64 characters for SHA-256)\n */\nexport function base64urlToHex(b64u: string): string {\n return bytesToHex(base64urlDecode(b64u));\n}\n\n/**\n * JWK structure for Ed25519 public keys (minimal required fields for thumbprint)\n */\nexport interface JWKThumbprintInput {\n /** Key type - must be \"OKP\" for Ed25519 */\n kty: string;\n /** Curve - must be \"Ed25519\" */\n crv: string;\n /** Public key (base64url) */\n x: string;\n}\n\n/**\n * Compute RFC 7638 JWK Thumbprint (base64url, SHA-256)\n *\n * For Ed25519 keys, the canonical JSON is: {\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"<base64url>\"}\n * Returns base64url-encoded SHA-256 hash (43 characters).\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Base64url-encoded SHA-256 thumbprint (43 characters)\n */\nexport async function computeJwkThumbprint(jwk: JWKThumbprintInput): Promise<string> {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported for thumbprint computation');\n }\n\n // Canonical JSON per RFC 7638 (alphabetically sorted members, no whitespace)\n const canonical = JSON.stringify({\n crv: jwk.crv,\n kty: jwk.kty,\n x: jwk.x,\n });\n\n const hashBytes = await sha256Bytes(canonical);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert JWK to Ed25519 public key bytes (32 bytes)\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Public key as 32-byte Uint8Array\n */\nexport function jwkToPublicKeyBytes(jwk: JWKThumbprintInput): Uint8Array {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported');\n }\n\n const xBytes = base64urlDecode(jwk.x);\n if (xBytes.length !== 32) {\n throw new Error(`Ed25519 public key must be 32 bytes, got ${xBytes.length}`);\n }\n\n return xBytes;\n}\n","/**\n * JSON Canonicalization Scheme (RFC 8785)\n * Deterministic JSON serialization for cryptographic hashing\n *\n * PROTOCOL DECISION: JavaScript `undefined` Handling\n * ===================================================\n *\n * RFC 8785 canonicalizes JSON values, and `undefined` is NOT a JSON value.\n * This implementation adopts \"JS-ergonomic\" semantics that match JSON.stringify:\n *\n * 1. **Object properties with undefined values are OMITTED**\n * - `canonicalize({a: 1, b: undefined})` -> `{\"a\":1}`\n * - Matches: `JSON.stringify({a: 1, b: undefined})` -> `{\"a\":1}`\n *\n * 2. **Array elements that are undefined become null**\n * - `canonicalize([1, undefined, 3])` -> `[1,null,3]`\n * - Matches: `JSON.stringify([1, undefined, 3])` -> `[1,null,3]`\n *\n * 3. **Top-level undefined THROWS**\n * - `canonicalize(undefined)` -> throws Error\n * - Rationale: No valid JSON representation exists\n *\n * CROSS-LANGUAGE INTEROPERABILITY WARNING:\n * =========================================\n * Cross-language producers MUST NOT rely on \"undefined\" semantics. To achieve\n * identical hashes across implementations, explicitly encode `null` in arrays\n * and omit keys in objects. Other language implementations (Go, Rust, Python)\n * will never produce `undefined` since it's JavaScript-specific.\n *\n * Guidelines:\n * - Producers MUST emit explicit `null` or omit keys; do NOT rely on coercion\n * - Verifiers SHOULD sanitize inputs before hashing (remove undefined properties)\n * - The canonical output is identical whether you pass `{a: undefined}` or `{}`\n *\n * This behavior is NORMATIVE for PEAC hashing and MUST NOT change without\n * a wire format version bump.\n */\n\n/**\n * Canonicalize a JSON value according to RFC 8785.\n *\n * @param obj - The value to canonicalize. Must be a valid JSON type (null, boolean,\n * number, string, array, object). Functions and Symbols throw.\n * @returns Canonical JSON string with sorted keys and no whitespace.\n * @throws Error if the value cannot be canonicalized (undefined at top level,\n * non-finite numbers, functions, symbols).\n *\n * @example\n * ```ts\n * canonicalize({ b: 2, a: 1 }) // '{\"a\":1,\"b\":2}'\n * canonicalize([1, null, \"x\"]) // '[1,null,\"x\"]'\n * ```\n */\nexport function canonicalize(obj: unknown): string {\n if (obj === null) {\n return 'null';\n }\n\n if (typeof obj === 'boolean') {\n return obj ? 'true' : 'false';\n }\n\n if (typeof obj === 'number') {\n // RFC 8785 number serialization (no trailing zeros, no exponential for small numbers)\n if (!Number.isFinite(obj)) {\n throw new Error('Cannot canonicalize non-finite number');\n }\n if (Object.is(obj, -0)) {\n return '0';\n }\n // Use JSON.stringify for proper number formatting per RFC 8785\n const str = JSON.stringify(obj);\n // Ensure no exponential notation for integers\n if (Number.isInteger(obj) && str.includes('e')) {\n return obj.toString();\n }\n return str;\n }\n\n if (typeof obj === 'string') {\n return JSON.stringify(obj);\n }\n\n if (Array.isArray(obj)) {\n // PROTOCOL DECISION: undefined in arrays becomes null (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n const elements = obj.map((el) => (el === undefined ? 'null' : canonicalize(el)));\n return `[${elements.join(',')}]`;\n }\n\n if (typeof obj === 'object') {\n // Sort keys lexicographically by UTF-16 code unit (RFC 8785 requirement)\n const keys = Object.keys(obj).sort();\n const pairs: string[] = [];\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n // PROTOCOL DECISION: Skip undefined values (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n if (value === undefined) {\n continue;\n }\n pairs.push(`${JSON.stringify(key)}:${canonicalize(value)}`);\n }\n return `{${pairs.join(',')}}`;\n }\n\n throw new Error(`Cannot canonicalize type: ${typeof obj}`);\n}\n\n/**\n * Canonicalize and encode as UTF-8 bytes\n */\nexport function canonicalizeBytes(obj: unknown): Uint8Array {\n const canonical = canonicalize(obj);\n return new TextEncoder().encode(canonical);\n}\n\n/**\n * Compute JCS+SHA-256 hash of an object\n */\nexport async function jcsHash(obj: unknown): Promise<string> {\n const bytes = canonicalizeBytes(obj);\n const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * JWS compact serialization with Ed25519 (RFC 8032)\n * Implements peac-receipt/0.1 wire format\n */\n\nimport {\n sign as ed25519Sign,\n verify as ed25519Verify,\n getPublicKey,\n randomSecretKey,\n} from './ed25519.js';\nimport { PEAC_WIRE_TYP, PEAC_ALG } from '@peac/schema';\nimport {\n base64urlEncode,\n base64urlDecode,\n base64urlEncodeString,\n base64urlDecodeString,\n} from './base64url';\nimport { CryptoError } from './errors';\n\n/**\n * JWS header for PEAC receipts\n */\nexport interface JWSHeader {\n typ: typeof PEAC_WIRE_TYP;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * Result of JWS verification\n */\nexport interface VerifyResult<T = unknown> {\n header: JWSHeader;\n payload: T;\n valid: boolean;\n}\n\n/**\n * Sign a payload with Ed25519 and return JWS compact serialization\n *\n * @param payload - JSON-serializable payload\n * @param privateKey - Ed25519 private key (32 bytes)\n * @param kid - Key ID (ISO 8601 timestamp)\n * @returns JWS compact serialization (header.payload.signature)\n */\nexport async function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n\n // Create header\n const header: JWSHeader = {\n typ: PEAC_WIRE_TYP,\n alg: PEAC_ALG,\n kid,\n };\n\n // Encode header and payload\n const headerB64 = base64urlEncodeString(JSON.stringify(header));\n const payloadB64 = base64urlEncodeString(JSON.stringify(payload));\n\n // Create signing input\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n // Sign with Ed25519\n const signatureBytes = await ed25519Sign(signingInputBytes, privateKey);\n\n // Encode signature\n const signatureB64 = base64urlEncode(signatureBytes);\n\n // Return JWS compact serialization\n return `${signingInput}.${signatureB64}`;\n}\n\n/**\n * Verify a JWS compact serialization with Ed25519\n *\n * @param jws - JWS compact serialization\n * @param publicKey - Ed25519 public key (32 bytes)\n * @returns Verification result with decoded header and payload\n */\nexport async function verify<T = unknown>(\n jws: string,\n publicKey: Uint8Array\n): Promise<VerifyResult<T>> {\n if (publicKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 public key must be 32 bytes');\n }\n\n // Split JWS\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode header\n const headerJson = base64urlDecodeString(headerB64);\n const header = JSON.parse(headerJson) as JWSHeader;\n\n // Validate header\n if (header.typ !== PEAC_WIRE_TYP) {\n throw new CryptoError(\n 'CRYPTO_INVALID_TYP',\n `Invalid typ: expected ${PEAC_WIRE_TYP}, got ${header.typ}`\n );\n }\n if (header.alg !== PEAC_ALG) {\n throw new CryptoError(\n 'CRYPTO_INVALID_ALG',\n `Invalid alg: expected ${PEAC_ALG}, got ${header.alg}`\n );\n }\n\n // Decode payload\n const payloadJson = base64urlDecodeString(payloadB64);\n const payload = JSON.parse(payloadJson) as T;\n\n // Decode signature\n const signatureBytes = base64urlDecode(signatureB64);\n\n // Verify signature\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n const valid = await ed25519Verify(signatureBytes, signingInputBytes, publicKey);\n\n return {\n header,\n payload,\n valid,\n };\n}\n\n/**\n * Decode JWS without verifying signature (use with caution!)\n *\n * @param jws - JWS compact serialization\n * @returns Decoded header and payload (unverified)\n */\nexport function decode<T = unknown>(jws: string): { header: JWSHeader; payload: T } {\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64] = parts;\n\n const headerJson = base64urlDecodeString(headerB64);\n const header = JSON.parse(headerJson) as JWSHeader;\n\n const payloadJson = base64urlDecodeString(payloadB64);\n const payload = JSON.parse(payloadJson) as T;\n\n return { header, payload };\n}\n\n/**\n * Generate a random Ed25519 keypair\n *\n * @returns Private key (32 bytes) and public key (32 bytes)\n */\nexport async function generateKeypair(): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n const privateKey = randomSecretKey();\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n\n// NOTE: generateKeypairFromSeed has been moved to @peac/crypto/testkit\n// It's intentionally NOT exported from the main module to prevent accidental\n// use in production. Use: import { generateKeypairFromSeed } from '@peac/crypto/testkit'\n\n/**\n * Ed25519 JWK interface for private keys\n */\nexport interface Ed25519PrivateJwk {\n kty: 'OKP';\n crv: 'Ed25519';\n /** Public key (base64url encoded, 32 bytes) */\n x: string;\n /** Private key (base64url encoded, 32 bytes) */\n d: string;\n}\n\n/**\n * Derive the Ed25519 public key from a private key\n *\n * @param privateKey - Ed25519 private key (32 bytes)\n * @returns Public key (32 bytes)\n */\nexport async function derivePublicKey(privateKey: Uint8Array): Promise<Uint8Array> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n return getPublicKey(privateKey);\n}\n\n/**\n * Validate that an Ed25519 JWK has a consistent keypair\n *\n * Derives the public key from the private key (d) and verifies it matches\n * the declared public key (x). This catches configuration errors where\n * the wrong key components are paired.\n *\n * @param jwk - Ed25519 JWK with both public (x) and private (d) components\n * @returns true if the keypair is consistent, false otherwise\n *\n * @example\n * ```typescript\n * const jwk = {\n * kty: 'OKP',\n * crv: 'Ed25519',\n * x: 'base64url-encoded-public-key',\n * d: 'base64url-encoded-private-key',\n * };\n *\n * if (!await validateKeypair(jwk)) {\n * throw new Error('Invalid keypair: d does not derive to x');\n * }\n * ```\n */\nexport async function validateKeypair(jwk: Ed25519PrivateJwk): Promise<boolean> {\n // Validate JWK structure\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n return false;\n }\n\n // Decode the keys\n let privateKeyBytes: Uint8Array;\n let declaredPublicKeyBytes: Uint8Array;\n\n try {\n privateKeyBytes = base64urlDecode(jwk.d);\n declaredPublicKeyBytes = base64urlDecode(jwk.x);\n } catch {\n return false;\n }\n\n // Validate lengths\n if (privateKeyBytes.length !== 32 || declaredPublicKeyBytes.length !== 32) {\n return false;\n }\n\n // Derive the actual public key from the private key\n const derivedPublicKeyBytes = await getPublicKey(privateKeyBytes);\n\n // Compare derived public key with declared public key\n if (derivedPublicKeyBytes.length !== declaredPublicKeyBytes.length) {\n return false;\n }\n\n for (let i = 0; i < derivedPublicKeyBytes.length; i++) {\n if (derivedPublicKeyBytes[i] !== declaredPublicKeyBytes[i]) {\n return false;\n }\n }\n\n return true;\n}\n"]}
1
+ {"version":3,"sources":["../src/base64url.ts","../src/errors.ts","../src/hash.ts","../src/jcs.ts","../src/ed25519.ts","../src/jws.ts"],"names":["ed","WIRE_01_JWS_TYP","WIRE_02_JWS_TYP_ACCEPT","PEAC_ALG","WIRE_02_JWS_TYP","sign","verify","VERIFIER_LIMITS"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base64url encoding/decoding (RFC 4648 §5)\n *\n * Platform-agnostic implementation that works in Node.js, browser, and edge runtimes.\n * No Buffer dependency - uses pure JavaScript for maximum portability.\n *\n * @packageDocumentation\n */\n\n// Standard base64 alphabet\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n// Reverse lookup table (lazily initialized)\nlet base64Lookup: Map<string, number> | null = null;\n\nfunction getBase64Lookup(): Map<string, number> {\n if (!base64Lookup) {\n base64Lookup = new Map();\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n base64Lookup.set(BASE64_ALPHABET[i], i);\n }\n }\n return base64Lookup;\n}\n\n/**\n * Encode bytes to base64url string (no padding)\n *\n * @param bytes - Byte array to encode\n * @returns Base64url encoded string (no padding)\n */\nexport function base64urlEncode(bytes: Uint8Array): string {\n let result = '';\n let i = 0;\n\n while (i < bytes.length) {\n const a = bytes[i++];\n const b = bytes[i++] ?? 0;\n const c = bytes[i++] ?? 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += BASE64_ALPHABET[(triplet >> 18) & 0x3f];\n result += BASE64_ALPHABET[(triplet >> 12) & 0x3f];\n result += i - 2 < bytes.length ? BASE64_ALPHABET[(triplet >> 6) & 0x3f] : '';\n result += i - 1 < bytes.length ? BASE64_ALPHABET[triplet & 0x3f] : '';\n }\n\n // Convert to base64url (no padding)\n return result.replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n\n/**\n * Decode base64url string to bytes\n *\n * @param str - Base64url encoded string\n * @returns Decoded bytes\n */\nexport function base64urlDecode(str: string): Uint8Array {\n // Convert base64url to base64\n let base64 = str.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n while (base64.length % 4 !== 0) {\n base64 += '=';\n }\n\n const lookup = getBase64Lookup();\n const bytes: number[] = [];\n\n for (let i = 0; i < base64.length; i += 4) {\n const a = lookup.get(base64[i]) ?? 0;\n const b = lookup.get(base64[i + 1]) ?? 0;\n const c = lookup.get(base64[i + 2]) ?? 0;\n const d = lookup.get(base64[i + 3]) ?? 0;\n\n bytes.push((a << 2) | (b >> 4));\n if (base64[i + 2] !== '=') {\n bytes.push(((b & 0x0f) << 4) | (c >> 2));\n }\n if (base64[i + 3] !== '=') {\n bytes.push(((c & 0x03) << 6) | d);\n }\n }\n\n return new Uint8Array(bytes);\n}\n\n/**\n * Encode UTF-8 string to base64url\n *\n * @param str - UTF-8 string to encode\n * @returns Base64url encoded string\n */\nexport function base64urlEncodeString(str: string): string {\n return base64urlEncode(new TextEncoder().encode(str));\n}\n\n/**\n * Decode base64url to UTF-8 string\n *\n * @param str - Base64url encoded string\n * @returns Decoded UTF-8 string\n */\nexport function base64urlDecodeString(str: string): string {\n return new TextDecoder().decode(base64urlDecode(str));\n}\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE'\n // Wire 0.2 JOSE hardening (v0.12.0-preview.1, DD-156)\n | 'CRYPTO_WIRE_VERSION_MISMATCH'\n | 'CRYPTO_JWS_EMBEDDED_KEY'\n | 'CRYPTO_JWS_CRIT_REJECTED'\n | 'CRYPTO_JWS_MISSING_KID'\n | 'CRYPTO_JWS_B64_REJECTED'\n | 'CRYPTO_JWS_ZIP_REJECTED';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * Cryptographic hash utilities\n *\n * Platform-agnostic SHA-256 implementation that works in Node.js, browser, and edge runtimes.\n * Uses Web Crypto API with Node.js crypto fallback.\n *\n * @packageDocumentation\n */\n\nimport { base64urlDecode, base64urlEncode } from './base64url.js';\n\n/**\n * Compute SHA-256 hash of data and return as lowercase hex string\n *\n * Uses Web Crypto API if available, falls back to Node.js crypto.\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Lowercase hex string (64 characters)\n */\nexport async function sha256Hex(data: Uint8Array | string): Promise<string> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first (works in browser, edge, and Node 20+)\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return hash.digest('hex');\n } catch {\n throw new Error(\n 'No SHA-256 implementation available. Ensure Web Crypto API or Node.js crypto is available.'\n );\n }\n}\n\n/**\n * Compute SHA-256 hash of data and return as Uint8Array (32 bytes)\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Hash as Uint8Array (32 bytes)\n */\nexport async function sha256Bytes(data: Uint8Array | string): Promise<Uint8Array> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n return new Uint8Array(hashBuffer);\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return new Uint8Array(hash.digest());\n } catch {\n throw new Error('No SHA-256 implementation available.');\n }\n}\n\n/**\n * Convert hex string to Uint8Array\n *\n * @param hex - Lowercase hex string\n * @returns Uint8Array\n */\nexport function hexToBytes(hex: string): Uint8Array {\n // Validate: must be even length and contain only hex characters\n if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {\n throw new Error('Invalid hex string');\n }\n if (hex.length === 0) {\n return new Uint8Array([]);\n }\n const matches = hex.match(/.{2}/g);\n if (!matches) {\n throw new Error('Invalid hex string');\n }\n return new Uint8Array(matches.map((byte) => parseInt(byte, 16)));\n}\n\n/**\n * Convert Uint8Array to lowercase hex string\n *\n * @param bytes - Byte array\n * @returns Lowercase hex string\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Compute SHA-256 hash of data and return as base64url string\n *\n * Used by Wire 0.2 DigestRef format. Evidence bundles use sha256Hex() with\n * \"sha256:<hex>\" prefix instead. A conversion bridge between the two formats\n * (hex <-> base64url) is provided by hexToBase64url() and base64urlToHex().\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Base64url-encoded SHA-256 hash (43 characters)\n */\nexport async function sha256Base64url(data: Uint8Array | string): Promise<string> {\n const hashBytes = await sha256Bytes(data);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert a hex-encoded hash to base64url encoding\n *\n * Bridges evidence bundle format (sha256:<hex>) to Wire 0.2 DigestRef format\n * (base64url value). Strips the \"sha256:\" prefix if present.\n *\n * @param hex - Hex string, optionally prefixed with \"sha256:\"\n * @returns Base64url-encoded bytes (43 characters for SHA-256)\n */\nexport function hexToBase64url(hex: string): string {\n const cleanHex = hex.startsWith('sha256:') ? hex.slice(7) : hex;\n return base64urlEncode(hexToBytes(cleanHex));\n}\n\n/**\n * Convert a base64url-encoded hash to hex encoding\n *\n * Bridges Wire 0.2 DigestRef format (base64url) to evidence bundle format\n * (hex). Does NOT add the \"sha256:\" prefix -- caller adds it if needed.\n *\n * @param b64u - Base64url-encoded hash\n * @returns Lowercase hex string (64 characters for SHA-256)\n */\nexport function base64urlToHex(b64u: string): string {\n return bytesToHex(base64urlDecode(b64u));\n}\n\n/**\n * JWK structure for Ed25519 public keys (minimal required fields for thumbprint)\n */\nexport interface JWKThumbprintInput {\n /** Key type - must be \"OKP\" for Ed25519 */\n kty: string;\n /** Curve - must be \"Ed25519\" */\n crv: string;\n /** Public key (base64url) */\n x: string;\n}\n\n/**\n * Compute RFC 7638 JWK Thumbprint (base64url, SHA-256)\n *\n * For Ed25519 keys, the canonical JSON is: {\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"<base64url>\"}\n * Returns base64url-encoded SHA-256 hash (43 characters).\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Base64url-encoded SHA-256 thumbprint (43 characters)\n */\nexport async function computeJwkThumbprint(jwk: JWKThumbprintInput): Promise<string> {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported for thumbprint computation');\n }\n\n // Canonical JSON per RFC 7638 (alphabetically sorted members, no whitespace)\n const canonical = JSON.stringify({\n crv: jwk.crv,\n kty: jwk.kty,\n x: jwk.x,\n });\n\n const hashBytes = await sha256Bytes(canonical);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert JWK to Ed25519 public key bytes (32 bytes)\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Public key as 32-byte Uint8Array\n */\nexport function jwkToPublicKeyBytes(jwk: JWKThumbprintInput): Uint8Array {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported');\n }\n\n const xBytes = base64urlDecode(jwk.x);\n if (xBytes.length !== 32) {\n throw new Error(`Ed25519 public key must be 32 bytes, got ${xBytes.length}`);\n }\n\n return xBytes;\n}\n","/**\n * JSON Canonicalization Scheme (RFC 8785)\n * Deterministic JSON serialization for cryptographic hashing\n *\n * PROTOCOL DECISION: JavaScript `undefined` Handling\n * ===================================================\n *\n * RFC 8785 canonicalizes JSON values, and `undefined` is NOT a JSON value.\n * This implementation adopts \"JS-ergonomic\" semantics that match JSON.stringify:\n *\n * 1. **Object properties with undefined values are OMITTED**\n * - `canonicalize({a: 1, b: undefined})` -> `{\"a\":1}`\n * - Matches: `JSON.stringify({a: 1, b: undefined})` -> `{\"a\":1}`\n *\n * 2. **Array elements that are undefined become null**\n * - `canonicalize([1, undefined, 3])` -> `[1,null,3]`\n * - Matches: `JSON.stringify([1, undefined, 3])` -> `[1,null,3]`\n *\n * 3. **Top-level undefined THROWS**\n * - `canonicalize(undefined)` -> throws Error\n * - Rationale: No valid JSON representation exists\n *\n * CROSS-LANGUAGE INTEROPERABILITY WARNING:\n * =========================================\n * Cross-language producers MUST NOT rely on \"undefined\" semantics. To achieve\n * identical hashes across implementations, explicitly encode `null` in arrays\n * and omit keys in objects. Other language implementations (Go, Rust, Python)\n * will never produce `undefined` since it's JavaScript-specific.\n *\n * Guidelines:\n * - Producers MUST emit explicit `null` or omit keys; do NOT rely on coercion\n * - Verifiers SHOULD sanitize inputs before hashing (remove undefined properties)\n * - The canonical output is identical whether you pass `{a: undefined}` or `{}`\n *\n * This behavior is NORMATIVE for PEAC hashing and MUST NOT change without\n * a wire format version bump.\n */\n\n/**\n * Canonicalize a JSON value according to RFC 8785.\n *\n * @param obj - The value to canonicalize. Must be a valid JSON type (null, boolean,\n * number, string, array, object). Functions and Symbols throw.\n * @returns Canonical JSON string with sorted keys and no whitespace.\n * @throws Error if the value cannot be canonicalized (undefined at top level,\n * non-finite numbers, functions, symbols).\n *\n * @example\n * ```ts\n * canonicalize({ b: 2, a: 1 }) // '{\"a\":1,\"b\":2}'\n * canonicalize([1, null, \"x\"]) // '[1,null,\"x\"]'\n * ```\n */\nexport function canonicalize(obj: unknown): string {\n if (obj === null) {\n return 'null';\n }\n\n if (typeof obj === 'boolean') {\n return obj ? 'true' : 'false';\n }\n\n if (typeof obj === 'number') {\n // RFC 8785 number serialization (no trailing zeros, no exponential for small numbers)\n if (!Number.isFinite(obj)) {\n throw new Error('Cannot canonicalize non-finite number');\n }\n if (Object.is(obj, -0)) {\n return '0';\n }\n // Use JSON.stringify for proper number formatting per RFC 8785\n const str = JSON.stringify(obj);\n // Ensure no exponential notation for integers\n if (Number.isInteger(obj) && str.includes('e')) {\n return obj.toString();\n }\n return str;\n }\n\n if (typeof obj === 'string') {\n return JSON.stringify(obj);\n }\n\n if (Array.isArray(obj)) {\n // PROTOCOL DECISION: undefined in arrays becomes null (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n const elements = obj.map((el) => (el === undefined ? 'null' : canonicalize(el)));\n return `[${elements.join(',')}]`;\n }\n\n if (typeof obj === 'object') {\n // Sort keys lexicographically by UTF-16 code unit (RFC 8785 requirement)\n const keys = Object.keys(obj).sort();\n const pairs: string[] = [];\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n // PROTOCOL DECISION: Skip undefined values (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n if (value === undefined) {\n continue;\n }\n pairs.push(`${JSON.stringify(key)}:${canonicalize(value)}`);\n }\n return `{${pairs.join(',')}}`;\n }\n\n throw new Error(`Cannot canonicalize type: ${typeof obj}`);\n}\n\n/**\n * Canonicalize and encode as UTF-8 bytes\n */\nexport function canonicalizeBytes(obj: unknown): Uint8Array {\n const canonical = canonicalize(obj);\n return new TextEncoder().encode(canonical);\n}\n\n/**\n * Compute JCS+SHA-256 hash of an object\n */\nexport async function jcsHash(obj: unknown): Promise<string> {\n const bytes = canonicalizeBytes(obj);\n const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * JWS compact serialization with Ed25519 (RFC 8032)\n * Dual-stack: Wire 0.1 (peac-receipt/0.1) and Wire 0.2 (interaction-record+jwt)\n */\n\nimport {\n sign as ed25519Sign,\n verify as ed25519Verify,\n getPublicKey,\n randomSecretKey,\n} from './ed25519.js';\nimport {\n WIRE_01_JWS_TYP,\n WIRE_02_JWS_TYP,\n WIRE_02_JWS_TYP_ACCEPT,\n PEAC_ALG,\n VERIFIER_LIMITS,\n} from '@peac/kernel';\nimport {\n base64urlEncode,\n base64urlDecode,\n base64urlEncodeString,\n base64urlDecodeString,\n} from './base64url';\nimport { CryptoError } from './errors';\n\n// ---------------------------------------------------------------------------\n// JWS header types: 3-variant discriminated union (Correction 1, DD-156)\n// ---------------------------------------------------------------------------\n\n/**\n * JWS header for Wire 0.1 receipts (typ: 'peac-receipt/0.1')\n */\nexport interface Wire01JWSHeader {\n typ: typeof WIRE_01_JWS_TYP;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * JWS header for Wire 0.2 receipts (typ: 'interaction-record+jwt', compact canonical form)\n *\n * The full media type 'application/interaction-record+jwt' is accepted by verify()\n * and decode() but normalized to this compact form before returning.\n */\nexport interface Wire02JWSHeader {\n typ: typeof WIRE_02_JWS_TYP;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * JWS header for tokens with no typ field\n *\n * Crypto layer passes these through without error.\n * The strictness decision (hard error vs. warning) belongs exclusively\n * in @peac/protocol.verifyLocal() (Correction 1, DD-156).\n */\nexport interface UnTypedJWSHeader {\n typ?: undefined;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * Discriminated union of all recognized JWS header variants.\n *\n * Callers narrow by checking `header.typ`:\n * if (header.typ === WIRE_02_JWS_TYP) { ... Wire 0.2 path ... }\n * if (header.typ === WIRE_01_JWS_TYP) { ... Wire 0.1 path ... }\n * if (header.typ === undefined) { ... UnTyped / interop path ... }\n */\nexport type JWSHeader = Wire01JWSHeader | Wire02JWSHeader | UnTypedJWSHeader;\n\n/**\n * Result of JWS verification\n */\nexport interface VerifyResult<T = unknown> {\n header: JWSHeader;\n payload: T;\n valid: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal constants (NOT exported)\n// ---------------------------------------------------------------------------\n\n/** All typ values accepted by verify() / decode(). Includes full media type form. */\nconst ACCEPTED_TYP_VALUES = new Set<string>([WIRE_01_JWS_TYP, ...WIRE_02_JWS_TYP_ACCEPT]);\n\n// ---------------------------------------------------------------------------\n// validateWire02Header (exported, JOSE hardening, Correction 9, DD-156)\n// ---------------------------------------------------------------------------\n\n/**\n * Validate JOSE header fields for Wire 0.2 JOSE hardening requirements.\n *\n * Rejects:\n * - Embedded key material (jwk, x5c, x5u, jku)\n * - crit header (critical extensions)\n * - b64: false (RFC 7797 unencoded payload)\n * - zip header (payload compression)\n * - Missing, empty, or oversized kid (DoS safety: max 256 chars)\n *\n * @param header - Raw parsed JWS header object\n * @throws CryptoError with CRYPTO_JWS_* code on any violation\n */\nexport function validateWire02Header(header: Record<string, unknown>): void {\n // kid: required, non-empty, max 256 chars (Correction 9, DoS safety)\n if (\n !header.kid ||\n typeof header.kid !== 'string' ||\n header.kid.length === 0 ||\n header.kid.length > 256\n ) {\n throw new CryptoError(\n 'CRYPTO_JWS_MISSING_KID',\n 'kid is missing, empty, or exceeds 256 characters'\n );\n }\n\n // Embedded key material: hard reject (prevents key confusion attacks)\n if (\n header.jwk !== undefined ||\n header.x5c !== undefined ||\n header.x5u !== undefined ||\n header.jku !== undefined\n ) {\n throw new CryptoError(\n 'CRYPTO_JWS_EMBEDDED_KEY',\n 'embedded key material (jwk/x5c/x5u/jku) is not permitted'\n );\n }\n\n // crit: hard reject\n if (header.crit !== undefined) {\n throw new CryptoError('CRYPTO_JWS_CRIT_REJECTED', 'crit header is not permitted');\n }\n\n // b64: false: RFC 7797 unencoded payload rejected\n if (header.b64 === false) {\n throw new CryptoError(\n 'CRYPTO_JWS_B64_REJECTED',\n 'b64:false (unencoded payload) is not permitted'\n );\n }\n\n // zip: hard reject\n if (header.zip !== undefined) {\n throw new CryptoError('CRYPTO_JWS_ZIP_REJECTED', 'zip header is not permitted');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helper: build typed JWSHeader from raw parsed object\n// ---------------------------------------------------------------------------\n\n/**\n * Build a typed JWSHeader from a raw parsed header object, applying typ\n * normalization (Correction 2, DD-156): 'application/interaction-record+jwt'\n * is normalized to 'interaction-record+jwt' before returning.\n *\n * @param raw - Raw header object from JSON.parse\n * @returns Typed JWSHeader\n * @throws CryptoError if alg is not PEAC_ALG, or if typ is present and not in ACCEPTED_TYP_VALUES\n */\nfunction buildHeader(raw: Record<string, unknown>): JWSHeader {\n // Validate alg (required)\n if (raw.alg !== PEAC_ALG) {\n throw new CryptoError(\n 'CRYPTO_INVALID_ALG',\n `Invalid alg: expected ${PEAC_ALG}, got ${String(raw.alg)}`\n );\n }\n\n const rawTyp = raw.typ as string | undefined;\n\n // Normalize full media type to compact form (Correction 2)\n const canonicalTyp = rawTyp === 'application/interaction-record+jwt' ? WIRE_02_JWS_TYP : rawTyp;\n\n if (canonicalTyp !== undefined && !ACCEPTED_TYP_VALUES.has(rawTyp!)) {\n throw new CryptoError('CRYPTO_INVALID_TYP', `Invalid typ: ${String(rawTyp)}`);\n }\n\n const kid = typeof raw.kid === 'string' ? raw.kid : '';\n\n if (canonicalTyp === WIRE_01_JWS_TYP) {\n return { typ: WIRE_01_JWS_TYP, alg: PEAC_ALG, kid } satisfies Wire01JWSHeader;\n }\n if (canonicalTyp === WIRE_02_JWS_TYP) {\n return { typ: WIRE_02_JWS_TYP, alg: PEAC_ALG, kid } satisfies Wire02JWSHeader;\n }\n\n // canonicalTyp is undefined: return UnTypedJWSHeader\n return { typ: undefined, alg: PEAC_ALG, kid } satisfies UnTypedJWSHeader;\n}\n\n// ---------------------------------------------------------------------------\n// sign (Wire 0.1)\n// ---------------------------------------------------------------------------\n\n/**\n * Sign a payload with Ed25519 and return JWS compact serialization (Wire 0.1)\n *\n * Always sets typ to WIRE_01_JWS_TYP ('peac-receipt/0.1').\n * For Wire 0.2, use signWire02() instead.\n *\n * @param payload - JSON-serializable payload\n * @param privateKey - Ed25519 private key (32 bytes)\n * @param kid - Key ID (ISO 8601 timestamp)\n * @returns JWS compact serialization (header.payload.signature)\n */\nexport async function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n\n const header: Wire01JWSHeader = {\n typ: WIRE_01_JWS_TYP,\n alg: PEAC_ALG,\n kid,\n };\n\n const headerB64 = base64urlEncodeString(JSON.stringify(header));\n const payloadB64 = base64urlEncodeString(JSON.stringify(payload));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n const signatureBytes = await ed25519Sign(signingInputBytes, privateKey);\n const signatureB64 = base64urlEncode(signatureBytes);\n\n return `${signingInput}.${signatureB64}`;\n}\n\n// ---------------------------------------------------------------------------\n// signWire02 (Wire 0.2)\n// ---------------------------------------------------------------------------\n\n/**\n * Sign a Wire 0.2 payload with Ed25519 and return JWS compact serialization\n *\n * Always sets typ to WIRE_02_JWS_TYP ('interaction-record+jwt').\n * The payload MUST include peac_version: '0.2'.\n *\n * @param payload - JSON-serializable Wire 0.2 claims payload\n * @param privateKey - Ed25519 private key (32 bytes)\n * @param kid - Key ID (max 256 chars per JOSE hardening rules)\n * @returns JWS compact serialization (header.payload.signature)\n */\nexport async function signWire02(\n payload: unknown,\n privateKey: Uint8Array,\n kid: string\n): Promise<string> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n\n // Validate kid length (Correction 9, DoS safety)\n if (!kid || kid.length === 0 || kid.length > 256) {\n throw new CryptoError(\n 'CRYPTO_JWS_MISSING_KID',\n 'kid is missing, empty, or exceeds 256 characters'\n );\n }\n\n // Always set typ: WIRE_02_JWS_TYP: no code path may omit it\n const header: Wire02JWSHeader = {\n typ: WIRE_02_JWS_TYP,\n alg: PEAC_ALG,\n kid,\n };\n\n const headerB64 = base64urlEncodeString(JSON.stringify(header));\n const payloadB64 = base64urlEncodeString(JSON.stringify(payload));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n const signatureBytes = await ed25519Sign(signingInputBytes, privateKey);\n const signatureB64 = base64urlEncode(signatureBytes);\n\n return `${signingInput}.${signatureB64}`;\n}\n\n// ---------------------------------------------------------------------------\n// verify (dual-stack)\n// ---------------------------------------------------------------------------\n\n/**\n * Verify a JWS compact serialization with Ed25519\n *\n * Dual-stack: accepts Wire 0.1 (peac-receipt/0.1) and Wire 0.2\n * (interaction-record+jwt or application/interaction-record+jwt).\n *\n * Typ normalization (Correction 2): 'application/interaction-record+jwt' is\n * normalized to 'interaction-record+jwt' in the returned header.\n *\n * UnTypedJWSHeader (Correction 1): tokens with no typ are returned with\n * typ: undefined without error; @peac/protocol.verifyLocal() applies strictness.\n *\n * Coherence check:\n * - typ === WIRE_02_JWS_TYP but payload.peac_version !== '0.2': CRYPTO_WIRE_VERSION_MISMATCH\n * - typ === WIRE_01_JWS_TYP but payload.peac_version === '0.2': CRYPTO_WIRE_VERSION_MISMATCH\n * - typ absent: no coherence check here; verifyLocal() handles it\n *\n * @param jws - JWS compact serialization\n * @param publicKey - Ed25519 public key (32 bytes)\n * @returns Verification result with typed header and decoded payload\n */\nexport async function verify<T = unknown>(\n jws: string,\n publicKey: Uint8Array\n): Promise<VerifyResult<T>> {\n if (publicKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 public key must be 32 bytes');\n }\n\n // Fast-reject oversized tokens before any parsing (DoS safety).\n // Uses VERIFIER_LIMITS.maxReceiptBytes (256 KB) as the upper bound.\n if (jws.length > VERIFIER_LIMITS.maxReceiptBytes) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n `JWS exceeds maximum size of ${VERIFIER_LIMITS.maxReceiptBytes} bytes`\n );\n }\n\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode and build typed header.\n // Wrap in try/catch to translate SyntaxError / decode failures into stable\n // CryptoError codes; callers depend on CRYPTO_INVALID_JWS_FORMAT for error\n // classification and must never receive a raw SyntaxError at the boundary.\n let rawHeader: Record<string, unknown>;\n try {\n rawHeader = JSON.parse(base64urlDecodeString(headerB64)) as Record<string, unknown>;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS header: invalid base64url or JSON');\n }\n const header = buildHeader(rawHeader);\n\n // Apply JOSE hardening for Wire 0.2 and UnTyped tokens (Correction 1, DD-156).\n // JOSE security invariants (embedded key material, crit, b64:false, zip) MUST be\n // enforced regardless of whether typ is present. Interop mode controls routing only;\n // it does not exempt tokens from key-injection or unencoded-payload attacks.\n // Wire 0.1 tokens are excluded: the legacy format predates these constraints.\n if (header.typ === WIRE_02_JWS_TYP || header.typ === undefined) {\n validateWire02Header(rawHeader);\n }\n\n // Decode payload; same stable-error contract as header decode above.\n let payload: T;\n try {\n payload = JSON.parse(base64urlDecodeString(payloadB64)) as T;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS payload: invalid base64url or JSON');\n }\n\n // Coherence check: wire version consistency (only when typ is present)\n if (header.typ !== undefined) {\n const payloadVersion = (payload as Record<string, unknown>).peac_version;\n if (header.typ === WIRE_02_JWS_TYP && payloadVersion !== '0.2') {\n throw new CryptoError(\n 'CRYPTO_WIRE_VERSION_MISMATCH',\n `typ is ${WIRE_02_JWS_TYP} but peac_version is ${String(payloadVersion)} (expected '0.2')`\n );\n }\n if (header.typ === WIRE_01_JWS_TYP && payloadVersion === '0.2') {\n throw new CryptoError(\n 'CRYPTO_WIRE_VERSION_MISMATCH',\n `typ is ${WIRE_01_JWS_TYP} but peac_version is '0.2' (Wire 0.2 must use ${WIRE_02_JWS_TYP})`\n );\n }\n }\n\n // Verify Ed25519 signature\n const signatureBytes = base64urlDecode(signatureB64);\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n const valid = await ed25519Verify(signatureBytes, signingInputBytes, publicKey);\n\n return { header, payload, valid };\n}\n\n// ---------------------------------------------------------------------------\n// decode (dual-stack, unverified)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode JWS without verifying signature\n *\n * UNSAFE: This function is for debugging and diagnostic inspection only.\n * It does NOT verify the Ed25519 signature and does NOT apply JOSE hardening\n * (embedded-key rejection, b64/zip/crit checks). Tokens returned by decode()\n * must never be trusted for authorization decisions.\n *\n * For secure verification use verify() instead.\n *\n * Applies the same typ normalization and header typing as verify().\n * Unrecognized typ values still throw CryptoError.\n *\n * @param jws - JWS compact serialization\n * @returns Decoded header and payload (unverified, no JOSE hardening applied)\n */\nexport function decode<T = unknown>(jws: string): { header: JWSHeader; payload: T } {\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64] = parts;\n\n let rawHeader: Record<string, unknown>;\n try {\n rawHeader = JSON.parse(base64urlDecodeString(headerB64)) as Record<string, unknown>;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS header: invalid base64url or JSON');\n }\n const header = buildHeader(rawHeader);\n\n let payload: T;\n try {\n payload = JSON.parse(base64urlDecodeString(payloadB64)) as T;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS payload: invalid base64url or JSON');\n }\n\n return { header, payload };\n}\n\n// ---------------------------------------------------------------------------\n// generateKeypair\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a random Ed25519 keypair\n *\n * @returns Private key (32 bytes) and public key (32 bytes)\n */\nexport async function generateKeypair(): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n const privateKey = randomSecretKey();\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n\n// NOTE: generateKeypairFromSeed has been moved to @peac/crypto/testkit\n// It's intentionally NOT exported from the main module to prevent accidental\n// use in production. Use: import { generateKeypairFromSeed } from '@peac/crypto/testkit'\n\n// ---------------------------------------------------------------------------\n// Ed25519 JWK utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Ed25519 JWK interface for private keys\n */\nexport interface Ed25519PrivateJwk {\n kty: 'OKP';\n crv: 'Ed25519';\n /** Public key (base64url encoded, 32 bytes) */\n x: string;\n /** Private key (base64url encoded, 32 bytes) */\n d: string;\n}\n\n/**\n * Derive the Ed25519 public key from a private key\n *\n * @param privateKey - Ed25519 private key (32 bytes)\n * @returns Public key (32 bytes)\n */\nexport async function derivePublicKey(privateKey: Uint8Array): Promise<Uint8Array> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n return getPublicKey(privateKey);\n}\n\n/**\n * Validate that an Ed25519 JWK has a consistent keypair\n *\n * Derives the public key from the private key (d) and verifies it matches\n * the declared public key (x). This catches configuration errors where\n * the wrong key components are paired.\n *\n * @param jwk - Ed25519 JWK with both public (x) and private (d) components\n * @returns true if the keypair is consistent, false otherwise\n *\n * @example\n * ```typescript\n * const jwk = {\n * kty: 'OKP',\n * crv: 'Ed25519',\n * x: 'base64url-encoded-public-key',\n * d: 'base64url-encoded-private-key',\n * };\n *\n * if (!await validateKeypair(jwk)) {\n * throw new Error('Invalid keypair: d does not derive to x');\n * }\n * ```\n */\nexport async function validateKeypair(jwk: Ed25519PrivateJwk): Promise<boolean> {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n return false;\n }\n\n let privateKeyBytes: Uint8Array;\n let declaredPublicKeyBytes: Uint8Array;\n\n try {\n privateKeyBytes = base64urlDecode(jwk.d);\n declaredPublicKeyBytes = base64urlDecode(jwk.x);\n } catch {\n return false;\n }\n\n if (privateKeyBytes.length !== 32 || declaredPublicKeyBytes.length !== 32) {\n return false;\n }\n\n const derivedPublicKeyBytes = await getPublicKey(privateKeyBytes);\n\n if (derivedPublicKeyBytes.length !== declaredPublicKeyBytes.length) {\n return false;\n }\n\n for (let i = 0; i < derivedPublicKeyBytes.length; i++) {\n if (derivedPublicKeyBytes[i] !== declaredPublicKeyBytes[i]) {\n return false;\n }\n }\n\n return true;\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as ed from '@noble/ed25519';
2
- import { PEAC_ALG, PEAC_WIRE_TYP } from '@peac/schema';
2
+ import { WIRE_01_JWS_TYP, WIRE_02_JWS_TYP_ACCEPT, PEAC_ALG, WIRE_02_JWS_TYP, VERIFIER_LIMITS } from '@peac/kernel';
3
3
 
4
4
  // src/base64url.ts
5
5
  var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -211,12 +211,83 @@ var sign = ed.signAsync;
211
211
  var verify = ed.verifyAsync;
212
212
  var getPublicKey = ed.getPublicKeyAsync;
213
213
  var randomSecretKey = ed.utils.randomSecretKey;
214
+ var ACCEPTED_TYP_VALUES = /* @__PURE__ */ new Set([WIRE_01_JWS_TYP, ...WIRE_02_JWS_TYP_ACCEPT]);
215
+ function validateWire02Header(header) {
216
+ if (!header.kid || typeof header.kid !== "string" || header.kid.length === 0 || header.kid.length > 256) {
217
+ throw new CryptoError(
218
+ "CRYPTO_JWS_MISSING_KID",
219
+ "kid is missing, empty, or exceeds 256 characters"
220
+ );
221
+ }
222
+ if (header.jwk !== void 0 || header.x5c !== void 0 || header.x5u !== void 0 || header.jku !== void 0) {
223
+ throw new CryptoError(
224
+ "CRYPTO_JWS_EMBEDDED_KEY",
225
+ "embedded key material (jwk/x5c/x5u/jku) is not permitted"
226
+ );
227
+ }
228
+ if (header.crit !== void 0) {
229
+ throw new CryptoError("CRYPTO_JWS_CRIT_REJECTED", "crit header is not permitted");
230
+ }
231
+ if (header.b64 === false) {
232
+ throw new CryptoError(
233
+ "CRYPTO_JWS_B64_REJECTED",
234
+ "b64:false (unencoded payload) is not permitted"
235
+ );
236
+ }
237
+ if (header.zip !== void 0) {
238
+ throw new CryptoError("CRYPTO_JWS_ZIP_REJECTED", "zip header is not permitted");
239
+ }
240
+ }
241
+ function buildHeader(raw) {
242
+ if (raw.alg !== PEAC_ALG) {
243
+ throw new CryptoError(
244
+ "CRYPTO_INVALID_ALG",
245
+ `Invalid alg: expected ${PEAC_ALG}, got ${String(raw.alg)}`
246
+ );
247
+ }
248
+ const rawTyp = raw.typ;
249
+ const canonicalTyp = rawTyp === "application/interaction-record+jwt" ? WIRE_02_JWS_TYP : rawTyp;
250
+ if (canonicalTyp !== void 0 && !ACCEPTED_TYP_VALUES.has(rawTyp)) {
251
+ throw new CryptoError("CRYPTO_INVALID_TYP", `Invalid typ: ${String(rawTyp)}`);
252
+ }
253
+ const kid = typeof raw.kid === "string" ? raw.kid : "";
254
+ if (canonicalTyp === WIRE_01_JWS_TYP) {
255
+ return { typ: WIRE_01_JWS_TYP, alg: PEAC_ALG, kid };
256
+ }
257
+ if (canonicalTyp === WIRE_02_JWS_TYP) {
258
+ return { typ: WIRE_02_JWS_TYP, alg: PEAC_ALG, kid };
259
+ }
260
+ return { typ: void 0, alg: PEAC_ALG, kid };
261
+ }
214
262
  async function sign2(payload, privateKey, kid) {
215
263
  if (privateKey.length !== 32) {
216
264
  throw new CryptoError("CRYPTO_INVALID_KEY_LENGTH", "Ed25519 private key must be 32 bytes");
217
265
  }
218
266
  const header = {
219
- typ: PEAC_WIRE_TYP,
267
+ typ: WIRE_01_JWS_TYP,
268
+ alg: PEAC_ALG,
269
+ kid
270
+ };
271
+ const headerB64 = base64urlEncodeString(JSON.stringify(header));
272
+ const payloadB64 = base64urlEncodeString(JSON.stringify(payload));
273
+ const signingInput = `${headerB64}.${payloadB64}`;
274
+ const signingInputBytes = new TextEncoder().encode(signingInput);
275
+ const signatureBytes = await sign(signingInputBytes, privateKey);
276
+ const signatureB64 = base64urlEncode(signatureBytes);
277
+ return `${signingInput}.${signatureB64}`;
278
+ }
279
+ async function signWire02(payload, privateKey, kid) {
280
+ if (privateKey.length !== 32) {
281
+ throw new CryptoError("CRYPTO_INVALID_KEY_LENGTH", "Ed25519 private key must be 32 bytes");
282
+ }
283
+ if (!kid || kid.length === 0 || kid.length > 256) {
284
+ throw new CryptoError(
285
+ "CRYPTO_JWS_MISSING_KID",
286
+ "kid is missing, empty, or exceeds 256 characters"
287
+ );
288
+ }
289
+ const header = {
290
+ typ: WIRE_02_JWS_TYP,
220
291
  alg: PEAC_ALG,
221
292
  kid
222
293
  };
@@ -232,6 +303,12 @@ async function verify2(jws, publicKey) {
232
303
  if (publicKey.length !== 32) {
233
304
  throw new CryptoError("CRYPTO_INVALID_KEY_LENGTH", "Ed25519 public key must be 32 bytes");
234
305
  }
306
+ if (jws.length > VERIFIER_LIMITS.maxReceiptBytes) {
307
+ throw new CryptoError(
308
+ "CRYPTO_INVALID_JWS_FORMAT",
309
+ `JWS exceeds maximum size of ${VERIFIER_LIMITS.maxReceiptBytes} bytes`
310
+ );
311
+ }
235
312
  const parts = jws.split(".");
236
313
  if (parts.length !== 3) {
237
314
  throw new CryptoError(
@@ -240,31 +317,42 @@ async function verify2(jws, publicKey) {
240
317
  );
241
318
  }
242
319
  const [headerB64, payloadB64, signatureB64] = parts;
243
- const headerJson = base64urlDecodeString(headerB64);
244
- const header = JSON.parse(headerJson);
245
- if (header.typ !== PEAC_WIRE_TYP) {
246
- throw new CryptoError(
247
- "CRYPTO_INVALID_TYP",
248
- `Invalid typ: expected ${PEAC_WIRE_TYP}, got ${header.typ}`
249
- );
320
+ let rawHeader;
321
+ try {
322
+ rawHeader = JSON.parse(base64urlDecodeString(headerB64));
323
+ } catch {
324
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS header: invalid base64url or JSON");
250
325
  }
251
- if (header.alg !== PEAC_ALG) {
252
- throw new CryptoError(
253
- "CRYPTO_INVALID_ALG",
254
- `Invalid alg: expected ${PEAC_ALG}, got ${header.alg}`
255
- );
326
+ const header = buildHeader(rawHeader);
327
+ if (header.typ === WIRE_02_JWS_TYP || header.typ === void 0) {
328
+ validateWire02Header(rawHeader);
329
+ }
330
+ let payload;
331
+ try {
332
+ payload = JSON.parse(base64urlDecodeString(payloadB64));
333
+ } catch {
334
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS payload: invalid base64url or JSON");
335
+ }
336
+ if (header.typ !== void 0) {
337
+ const payloadVersion = payload.peac_version;
338
+ if (header.typ === WIRE_02_JWS_TYP && payloadVersion !== "0.2") {
339
+ throw new CryptoError(
340
+ "CRYPTO_WIRE_VERSION_MISMATCH",
341
+ `typ is ${WIRE_02_JWS_TYP} but peac_version is ${String(payloadVersion)} (expected '0.2')`
342
+ );
343
+ }
344
+ if (header.typ === WIRE_01_JWS_TYP && payloadVersion === "0.2") {
345
+ throw new CryptoError(
346
+ "CRYPTO_WIRE_VERSION_MISMATCH",
347
+ `typ is ${WIRE_01_JWS_TYP} but peac_version is '0.2' (Wire 0.2 must use ${WIRE_02_JWS_TYP})`
348
+ );
349
+ }
256
350
  }
257
- const payloadJson = base64urlDecodeString(payloadB64);
258
- const payload = JSON.parse(payloadJson);
259
351
  const signatureBytes = base64urlDecode(signatureB64);
260
352
  const signingInput = `${headerB64}.${payloadB64}`;
261
353
  const signingInputBytes = new TextEncoder().encode(signingInput);
262
354
  const valid = await verify(signatureBytes, signingInputBytes, publicKey);
263
- return {
264
- header,
265
- payload,
266
- valid
267
- };
355
+ return { header, payload, valid };
268
356
  }
269
357
  function decode(jws) {
270
358
  const parts = jws.split(".");
@@ -275,10 +363,19 @@ function decode(jws) {
275
363
  );
276
364
  }
277
365
  const [headerB64, payloadB64] = parts;
278
- const headerJson = base64urlDecodeString(headerB64);
279
- const header = JSON.parse(headerJson);
280
- const payloadJson = base64urlDecodeString(payloadB64);
281
- const payload = JSON.parse(payloadJson);
366
+ let rawHeader;
367
+ try {
368
+ rawHeader = JSON.parse(base64urlDecodeString(headerB64));
369
+ } catch {
370
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS header: invalid base64url or JSON");
371
+ }
372
+ const header = buildHeader(rawHeader);
373
+ let payload;
374
+ try {
375
+ payload = JSON.parse(base64urlDecodeString(payloadB64));
376
+ } catch {
377
+ throw new CryptoError("CRYPTO_INVALID_JWS_FORMAT", "JWS payload: invalid base64url or JSON");
378
+ }
282
379
  return { header, payload };
283
380
  }
284
381
  async function generateKeypair() {
@@ -319,6 +416,6 @@ async function validateKeypair(jwk) {
319
416
  return true;
320
417
  }
321
418
 
322
- export { CryptoError, base64urlDecode, base64urlDecodeString, base64urlEncode, base64urlEncodeString, base64urlToHex, bytesToHex, canonicalize, canonicalizeBytes, computeJwkThumbprint, decode, derivePublicKey, generateKeypair, hexToBase64url, hexToBytes, isFormatError, jcsHash, jwkToPublicKeyBytes, sha256Base64url, sha256Bytes, sha256Hex, sign2 as sign, validateKeypair, verify2 as verify };
419
+ export { CryptoError, base64urlDecode, base64urlDecodeString, base64urlEncode, base64urlEncodeString, base64urlToHex, bytesToHex, canonicalize, canonicalizeBytes, computeJwkThumbprint, decode, derivePublicKey, generateKeypair, hexToBase64url, hexToBytes, isFormatError, jcsHash, jwkToPublicKeyBytes, sha256Base64url, sha256Bytes, sha256Hex, sign2 as sign, signWire02, validateKeypair, validateWire02Header, verify2 as verify };
323
420
  //# sourceMappingURL=index.mjs.map
324
421
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/base64url.ts","../src/errors.ts","../src/hash.ts","../src/jcs.ts","../src/ed25519.ts","../src/jws.ts"],"names":["sign","verify"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Base64url encoding/decoding (RFC 4648 §5)\n *\n * Platform-agnostic implementation that works in Node.js, browser, and edge runtimes.\n * No Buffer dependency - uses pure JavaScript for maximum portability.\n *\n * @packageDocumentation\n */\n\n// Standard base64 alphabet\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n// Reverse lookup table (lazily initialized)\nlet base64Lookup: Map<string, number> | null = null;\n\nfunction getBase64Lookup(): Map<string, number> {\n if (!base64Lookup) {\n base64Lookup = new Map();\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n base64Lookup.set(BASE64_ALPHABET[i], i);\n }\n }\n return base64Lookup;\n}\n\n/**\n * Encode bytes to base64url string (no padding)\n *\n * @param bytes - Byte array to encode\n * @returns Base64url encoded string (no padding)\n */\nexport function base64urlEncode(bytes: Uint8Array): string {\n let result = '';\n let i = 0;\n\n while (i < bytes.length) {\n const a = bytes[i++];\n const b = bytes[i++] ?? 0;\n const c = bytes[i++] ?? 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += BASE64_ALPHABET[(triplet >> 18) & 0x3f];\n result += BASE64_ALPHABET[(triplet >> 12) & 0x3f];\n result += i - 2 < bytes.length ? BASE64_ALPHABET[(triplet >> 6) & 0x3f] : '';\n result += i - 1 < bytes.length ? BASE64_ALPHABET[triplet & 0x3f] : '';\n }\n\n // Convert to base64url (no padding)\n return result.replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n\n/**\n * Decode base64url string to bytes\n *\n * @param str - Base64url encoded string\n * @returns Decoded bytes\n */\nexport function base64urlDecode(str: string): Uint8Array {\n // Convert base64url to base64\n let base64 = str.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n while (base64.length % 4 !== 0) {\n base64 += '=';\n }\n\n const lookup = getBase64Lookup();\n const bytes: number[] = [];\n\n for (let i = 0; i < base64.length; i += 4) {\n const a = lookup.get(base64[i]) ?? 0;\n const b = lookup.get(base64[i + 1]) ?? 0;\n const c = lookup.get(base64[i + 2]) ?? 0;\n const d = lookup.get(base64[i + 3]) ?? 0;\n\n bytes.push((a << 2) | (b >> 4));\n if (base64[i + 2] !== '=') {\n bytes.push(((b & 0x0f) << 4) | (c >> 2));\n }\n if (base64[i + 3] !== '=') {\n bytes.push(((c & 0x03) << 6) | d);\n }\n }\n\n return new Uint8Array(bytes);\n}\n\n/**\n * Encode UTF-8 string to base64url\n *\n * @param str - UTF-8 string to encode\n * @returns Base64url encoded string\n */\nexport function base64urlEncodeString(str: string): string {\n return base64urlEncode(new TextEncoder().encode(str));\n}\n\n/**\n * Decode base64url to UTF-8 string\n *\n * @param str - Base64url encoded string\n * @returns Decoded UTF-8 string\n */\nexport function base64urlDecodeString(str: string): string {\n return new TextDecoder().decode(base64urlDecode(str));\n}\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * Cryptographic hash utilities\n *\n * Platform-agnostic SHA-256 implementation that works in Node.js, browser, and edge runtimes.\n * Uses Web Crypto API with Node.js crypto fallback.\n *\n * @packageDocumentation\n */\n\nimport { base64urlDecode, base64urlEncode } from './base64url.js';\n\n/**\n * Compute SHA-256 hash of data and return as lowercase hex string\n *\n * Uses Web Crypto API if available, falls back to Node.js crypto.\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Lowercase hex string (64 characters)\n */\nexport async function sha256Hex(data: Uint8Array | string): Promise<string> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first (works in browser, edge, and Node 20+)\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return hash.digest('hex');\n } catch {\n throw new Error(\n 'No SHA-256 implementation available. Ensure Web Crypto API or Node.js crypto is available.'\n );\n }\n}\n\n/**\n * Compute SHA-256 hash of data and return as Uint8Array (32 bytes)\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Hash as Uint8Array (32 bytes)\n */\nexport async function sha256Bytes(data: Uint8Array | string): Promise<Uint8Array> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n return new Uint8Array(hashBuffer);\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return new Uint8Array(hash.digest());\n } catch {\n throw new Error('No SHA-256 implementation available.');\n }\n}\n\n/**\n * Convert hex string to Uint8Array\n *\n * @param hex - Lowercase hex string\n * @returns Uint8Array\n */\nexport function hexToBytes(hex: string): Uint8Array {\n // Validate: must be even length and contain only hex characters\n if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {\n throw new Error('Invalid hex string');\n }\n if (hex.length === 0) {\n return new Uint8Array([]);\n }\n const matches = hex.match(/.{2}/g);\n if (!matches) {\n throw new Error('Invalid hex string');\n }\n return new Uint8Array(matches.map((byte) => parseInt(byte, 16)));\n}\n\n/**\n * Convert Uint8Array to lowercase hex string\n *\n * @param bytes - Byte array\n * @returns Lowercase hex string\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Compute SHA-256 hash of data and return as base64url string\n *\n * Used by Wire 0.2 DigestRef format. Evidence bundles use sha256Hex() with\n * \"sha256:<hex>\" prefix instead. A conversion bridge between the two formats\n * (hex <-> base64url) is provided by hexToBase64url() and base64urlToHex().\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Base64url-encoded SHA-256 hash (43 characters)\n */\nexport async function sha256Base64url(data: Uint8Array | string): Promise<string> {\n const hashBytes = await sha256Bytes(data);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert a hex-encoded hash to base64url encoding\n *\n * Bridges evidence bundle format (sha256:<hex>) to Wire 0.2 DigestRef format\n * (base64url value). Strips the \"sha256:\" prefix if present.\n *\n * @param hex - Hex string, optionally prefixed with \"sha256:\"\n * @returns Base64url-encoded bytes (43 characters for SHA-256)\n */\nexport function hexToBase64url(hex: string): string {\n const cleanHex = hex.startsWith('sha256:') ? hex.slice(7) : hex;\n return base64urlEncode(hexToBytes(cleanHex));\n}\n\n/**\n * Convert a base64url-encoded hash to hex encoding\n *\n * Bridges Wire 0.2 DigestRef format (base64url) to evidence bundle format\n * (hex). Does NOT add the \"sha256:\" prefix -- caller adds it if needed.\n *\n * @param b64u - Base64url-encoded hash\n * @returns Lowercase hex string (64 characters for SHA-256)\n */\nexport function base64urlToHex(b64u: string): string {\n return bytesToHex(base64urlDecode(b64u));\n}\n\n/**\n * JWK structure for Ed25519 public keys (minimal required fields for thumbprint)\n */\nexport interface JWKThumbprintInput {\n /** Key type - must be \"OKP\" for Ed25519 */\n kty: string;\n /** Curve - must be \"Ed25519\" */\n crv: string;\n /** Public key (base64url) */\n x: string;\n}\n\n/**\n * Compute RFC 7638 JWK Thumbprint (base64url, SHA-256)\n *\n * For Ed25519 keys, the canonical JSON is: {\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"<base64url>\"}\n * Returns base64url-encoded SHA-256 hash (43 characters).\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Base64url-encoded SHA-256 thumbprint (43 characters)\n */\nexport async function computeJwkThumbprint(jwk: JWKThumbprintInput): Promise<string> {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported for thumbprint computation');\n }\n\n // Canonical JSON per RFC 7638 (alphabetically sorted members, no whitespace)\n const canonical = JSON.stringify({\n crv: jwk.crv,\n kty: jwk.kty,\n x: jwk.x,\n });\n\n const hashBytes = await sha256Bytes(canonical);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert JWK to Ed25519 public key bytes (32 bytes)\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Public key as 32-byte Uint8Array\n */\nexport function jwkToPublicKeyBytes(jwk: JWKThumbprintInput): Uint8Array {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported');\n }\n\n const xBytes = base64urlDecode(jwk.x);\n if (xBytes.length !== 32) {\n throw new Error(`Ed25519 public key must be 32 bytes, got ${xBytes.length}`);\n }\n\n return xBytes;\n}\n","/**\n * JSON Canonicalization Scheme (RFC 8785)\n * Deterministic JSON serialization for cryptographic hashing\n *\n * PROTOCOL DECISION: JavaScript `undefined` Handling\n * ===================================================\n *\n * RFC 8785 canonicalizes JSON values, and `undefined` is NOT a JSON value.\n * This implementation adopts \"JS-ergonomic\" semantics that match JSON.stringify:\n *\n * 1. **Object properties with undefined values are OMITTED**\n * - `canonicalize({a: 1, b: undefined})` -> `{\"a\":1}`\n * - Matches: `JSON.stringify({a: 1, b: undefined})` -> `{\"a\":1}`\n *\n * 2. **Array elements that are undefined become null**\n * - `canonicalize([1, undefined, 3])` -> `[1,null,3]`\n * - Matches: `JSON.stringify([1, undefined, 3])` -> `[1,null,3]`\n *\n * 3. **Top-level undefined THROWS**\n * - `canonicalize(undefined)` -> throws Error\n * - Rationale: No valid JSON representation exists\n *\n * CROSS-LANGUAGE INTEROPERABILITY WARNING:\n * =========================================\n * Cross-language producers MUST NOT rely on \"undefined\" semantics. To achieve\n * identical hashes across implementations, explicitly encode `null` in arrays\n * and omit keys in objects. Other language implementations (Go, Rust, Python)\n * will never produce `undefined` since it's JavaScript-specific.\n *\n * Guidelines:\n * - Producers MUST emit explicit `null` or omit keys; do NOT rely on coercion\n * - Verifiers SHOULD sanitize inputs before hashing (remove undefined properties)\n * - The canonical output is identical whether you pass `{a: undefined}` or `{}`\n *\n * This behavior is NORMATIVE for PEAC hashing and MUST NOT change without\n * a wire format version bump.\n */\n\n/**\n * Canonicalize a JSON value according to RFC 8785.\n *\n * @param obj - The value to canonicalize. Must be a valid JSON type (null, boolean,\n * number, string, array, object). Functions and Symbols throw.\n * @returns Canonical JSON string with sorted keys and no whitespace.\n * @throws Error if the value cannot be canonicalized (undefined at top level,\n * non-finite numbers, functions, symbols).\n *\n * @example\n * ```ts\n * canonicalize({ b: 2, a: 1 }) // '{\"a\":1,\"b\":2}'\n * canonicalize([1, null, \"x\"]) // '[1,null,\"x\"]'\n * ```\n */\nexport function canonicalize(obj: unknown): string {\n if (obj === null) {\n return 'null';\n }\n\n if (typeof obj === 'boolean') {\n return obj ? 'true' : 'false';\n }\n\n if (typeof obj === 'number') {\n // RFC 8785 number serialization (no trailing zeros, no exponential for small numbers)\n if (!Number.isFinite(obj)) {\n throw new Error('Cannot canonicalize non-finite number');\n }\n if (Object.is(obj, -0)) {\n return '0';\n }\n // Use JSON.stringify for proper number formatting per RFC 8785\n const str = JSON.stringify(obj);\n // Ensure no exponential notation for integers\n if (Number.isInteger(obj) && str.includes('e')) {\n return obj.toString();\n }\n return str;\n }\n\n if (typeof obj === 'string') {\n return JSON.stringify(obj);\n }\n\n if (Array.isArray(obj)) {\n // PROTOCOL DECISION: undefined in arrays becomes null (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n const elements = obj.map((el) => (el === undefined ? 'null' : canonicalize(el)));\n return `[${elements.join(',')}]`;\n }\n\n if (typeof obj === 'object') {\n // Sort keys lexicographically by UTF-16 code unit (RFC 8785 requirement)\n const keys = Object.keys(obj).sort();\n const pairs: string[] = [];\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n // PROTOCOL DECISION: Skip undefined values (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n if (value === undefined) {\n continue;\n }\n pairs.push(`${JSON.stringify(key)}:${canonicalize(value)}`);\n }\n return `{${pairs.join(',')}}`;\n }\n\n throw new Error(`Cannot canonicalize type: ${typeof obj}`);\n}\n\n/**\n * Canonicalize and encode as UTF-8 bytes\n */\nexport function canonicalizeBytes(obj: unknown): Uint8Array {\n const canonical = canonicalize(obj);\n return new TextEncoder().encode(canonical);\n}\n\n/**\n * Compute JCS+SHA-256 hash of an object\n */\nexport async function jcsHash(obj: unknown): Promise<string> {\n const bytes = canonicalizeBytes(obj);\n const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * JWS compact serialization with Ed25519 (RFC 8032)\n * Implements peac-receipt/0.1 wire format\n */\n\nimport {\n sign as ed25519Sign,\n verify as ed25519Verify,\n getPublicKey,\n randomSecretKey,\n} from './ed25519.js';\nimport { PEAC_WIRE_TYP, PEAC_ALG } from '@peac/schema';\nimport {\n base64urlEncode,\n base64urlDecode,\n base64urlEncodeString,\n base64urlDecodeString,\n} from './base64url';\nimport { CryptoError } from './errors';\n\n/**\n * JWS header for PEAC receipts\n */\nexport interface JWSHeader {\n typ: typeof PEAC_WIRE_TYP;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * Result of JWS verification\n */\nexport interface VerifyResult<T = unknown> {\n header: JWSHeader;\n payload: T;\n valid: boolean;\n}\n\n/**\n * Sign a payload with Ed25519 and return JWS compact serialization\n *\n * @param payload - JSON-serializable payload\n * @param privateKey - Ed25519 private key (32 bytes)\n * @param kid - Key ID (ISO 8601 timestamp)\n * @returns JWS compact serialization (header.payload.signature)\n */\nexport async function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n\n // Create header\n const header: JWSHeader = {\n typ: PEAC_WIRE_TYP,\n alg: PEAC_ALG,\n kid,\n };\n\n // Encode header and payload\n const headerB64 = base64urlEncodeString(JSON.stringify(header));\n const payloadB64 = base64urlEncodeString(JSON.stringify(payload));\n\n // Create signing input\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n // Sign with Ed25519\n const signatureBytes = await ed25519Sign(signingInputBytes, privateKey);\n\n // Encode signature\n const signatureB64 = base64urlEncode(signatureBytes);\n\n // Return JWS compact serialization\n return `${signingInput}.${signatureB64}`;\n}\n\n/**\n * Verify a JWS compact serialization with Ed25519\n *\n * @param jws - JWS compact serialization\n * @param publicKey - Ed25519 public key (32 bytes)\n * @returns Verification result with decoded header and payload\n */\nexport async function verify<T = unknown>(\n jws: string,\n publicKey: Uint8Array\n): Promise<VerifyResult<T>> {\n if (publicKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 public key must be 32 bytes');\n }\n\n // Split JWS\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode header\n const headerJson = base64urlDecodeString(headerB64);\n const header = JSON.parse(headerJson) as JWSHeader;\n\n // Validate header\n if (header.typ !== PEAC_WIRE_TYP) {\n throw new CryptoError(\n 'CRYPTO_INVALID_TYP',\n `Invalid typ: expected ${PEAC_WIRE_TYP}, got ${header.typ}`\n );\n }\n if (header.alg !== PEAC_ALG) {\n throw new CryptoError(\n 'CRYPTO_INVALID_ALG',\n `Invalid alg: expected ${PEAC_ALG}, got ${header.alg}`\n );\n }\n\n // Decode payload\n const payloadJson = base64urlDecodeString(payloadB64);\n const payload = JSON.parse(payloadJson) as T;\n\n // Decode signature\n const signatureBytes = base64urlDecode(signatureB64);\n\n // Verify signature\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n const valid = await ed25519Verify(signatureBytes, signingInputBytes, publicKey);\n\n return {\n header,\n payload,\n valid,\n };\n}\n\n/**\n * Decode JWS without verifying signature (use with caution!)\n *\n * @param jws - JWS compact serialization\n * @returns Decoded header and payload (unverified)\n */\nexport function decode<T = unknown>(jws: string): { header: JWSHeader; payload: T } {\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64] = parts;\n\n const headerJson = base64urlDecodeString(headerB64);\n const header = JSON.parse(headerJson) as JWSHeader;\n\n const payloadJson = base64urlDecodeString(payloadB64);\n const payload = JSON.parse(payloadJson) as T;\n\n return { header, payload };\n}\n\n/**\n * Generate a random Ed25519 keypair\n *\n * @returns Private key (32 bytes) and public key (32 bytes)\n */\nexport async function generateKeypair(): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n const privateKey = randomSecretKey();\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n\n// NOTE: generateKeypairFromSeed has been moved to @peac/crypto/testkit\n// It's intentionally NOT exported from the main module to prevent accidental\n// use in production. Use: import { generateKeypairFromSeed } from '@peac/crypto/testkit'\n\n/**\n * Ed25519 JWK interface for private keys\n */\nexport interface Ed25519PrivateJwk {\n kty: 'OKP';\n crv: 'Ed25519';\n /** Public key (base64url encoded, 32 bytes) */\n x: string;\n /** Private key (base64url encoded, 32 bytes) */\n d: string;\n}\n\n/**\n * Derive the Ed25519 public key from a private key\n *\n * @param privateKey - Ed25519 private key (32 bytes)\n * @returns Public key (32 bytes)\n */\nexport async function derivePublicKey(privateKey: Uint8Array): Promise<Uint8Array> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n return getPublicKey(privateKey);\n}\n\n/**\n * Validate that an Ed25519 JWK has a consistent keypair\n *\n * Derives the public key from the private key (d) and verifies it matches\n * the declared public key (x). This catches configuration errors where\n * the wrong key components are paired.\n *\n * @param jwk - Ed25519 JWK with both public (x) and private (d) components\n * @returns true if the keypair is consistent, false otherwise\n *\n * @example\n * ```typescript\n * const jwk = {\n * kty: 'OKP',\n * crv: 'Ed25519',\n * x: 'base64url-encoded-public-key',\n * d: 'base64url-encoded-private-key',\n * };\n *\n * if (!await validateKeypair(jwk)) {\n * throw new Error('Invalid keypair: d does not derive to x');\n * }\n * ```\n */\nexport async function validateKeypair(jwk: Ed25519PrivateJwk): Promise<boolean> {\n // Validate JWK structure\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n return false;\n }\n\n // Decode the keys\n let privateKeyBytes: Uint8Array;\n let declaredPublicKeyBytes: Uint8Array;\n\n try {\n privateKeyBytes = base64urlDecode(jwk.d);\n declaredPublicKeyBytes = base64urlDecode(jwk.x);\n } catch {\n return false;\n }\n\n // Validate lengths\n if (privateKeyBytes.length !== 32 || declaredPublicKeyBytes.length !== 32) {\n return false;\n }\n\n // Derive the actual public key from the private key\n const derivedPublicKeyBytes = await getPublicKey(privateKeyBytes);\n\n // Compare derived public key with declared public key\n if (derivedPublicKeyBytes.length !== declaredPublicKeyBytes.length) {\n return false;\n }\n\n for (let i = 0; i < derivedPublicKeyBytes.length; i++) {\n if (derivedPublicKeyBytes[i] !== declaredPublicKeyBytes[i]) {\n return false;\n }\n }\n\n return true;\n}\n"]}
1
+ {"version":3,"sources":["../src/base64url.ts","../src/errors.ts","../src/hash.ts","../src/jcs.ts","../src/ed25519.ts","../src/jws.ts"],"names":["sign","verify"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Base64url encoding/decoding (RFC 4648 §5)\n *\n * Platform-agnostic implementation that works in Node.js, browser, and edge runtimes.\n * No Buffer dependency - uses pure JavaScript for maximum portability.\n *\n * @packageDocumentation\n */\n\n// Standard base64 alphabet\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n// Reverse lookup table (lazily initialized)\nlet base64Lookup: Map<string, number> | null = null;\n\nfunction getBase64Lookup(): Map<string, number> {\n if (!base64Lookup) {\n base64Lookup = new Map();\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n base64Lookup.set(BASE64_ALPHABET[i], i);\n }\n }\n return base64Lookup;\n}\n\n/**\n * Encode bytes to base64url string (no padding)\n *\n * @param bytes - Byte array to encode\n * @returns Base64url encoded string (no padding)\n */\nexport function base64urlEncode(bytes: Uint8Array): string {\n let result = '';\n let i = 0;\n\n while (i < bytes.length) {\n const a = bytes[i++];\n const b = bytes[i++] ?? 0;\n const c = bytes[i++] ?? 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += BASE64_ALPHABET[(triplet >> 18) & 0x3f];\n result += BASE64_ALPHABET[(triplet >> 12) & 0x3f];\n result += i - 2 < bytes.length ? BASE64_ALPHABET[(triplet >> 6) & 0x3f] : '';\n result += i - 1 < bytes.length ? BASE64_ALPHABET[triplet & 0x3f] : '';\n }\n\n // Convert to base64url (no padding)\n return result.replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n\n/**\n * Decode base64url string to bytes\n *\n * @param str - Base64url encoded string\n * @returns Decoded bytes\n */\nexport function base64urlDecode(str: string): Uint8Array {\n // Convert base64url to base64\n let base64 = str.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n while (base64.length % 4 !== 0) {\n base64 += '=';\n }\n\n const lookup = getBase64Lookup();\n const bytes: number[] = [];\n\n for (let i = 0; i < base64.length; i += 4) {\n const a = lookup.get(base64[i]) ?? 0;\n const b = lookup.get(base64[i + 1]) ?? 0;\n const c = lookup.get(base64[i + 2]) ?? 0;\n const d = lookup.get(base64[i + 3]) ?? 0;\n\n bytes.push((a << 2) | (b >> 4));\n if (base64[i + 2] !== '=') {\n bytes.push(((b & 0x0f) << 4) | (c >> 2));\n }\n if (base64[i + 3] !== '=') {\n bytes.push(((c & 0x03) << 6) | d);\n }\n }\n\n return new Uint8Array(bytes);\n}\n\n/**\n * Encode UTF-8 string to base64url\n *\n * @param str - UTF-8 string to encode\n * @returns Base64url encoded string\n */\nexport function base64urlEncodeString(str: string): string {\n return base64urlEncode(new TextEncoder().encode(str));\n}\n\n/**\n * Decode base64url to UTF-8 string\n *\n * @param str - Base64url encoded string\n * @returns Decoded UTF-8 string\n */\nexport function base64urlDecodeString(str: string): string {\n return new TextDecoder().decode(base64urlDecode(str));\n}\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE'\n // Wire 0.2 JOSE hardening (v0.12.0-preview.1, DD-156)\n | 'CRYPTO_WIRE_VERSION_MISMATCH'\n | 'CRYPTO_JWS_EMBEDDED_KEY'\n | 'CRYPTO_JWS_CRIT_REJECTED'\n | 'CRYPTO_JWS_MISSING_KID'\n | 'CRYPTO_JWS_B64_REJECTED'\n | 'CRYPTO_JWS_ZIP_REJECTED';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * Cryptographic hash utilities\n *\n * Platform-agnostic SHA-256 implementation that works in Node.js, browser, and edge runtimes.\n * Uses Web Crypto API with Node.js crypto fallback.\n *\n * @packageDocumentation\n */\n\nimport { base64urlDecode, base64urlEncode } from './base64url.js';\n\n/**\n * Compute SHA-256 hash of data and return as lowercase hex string\n *\n * Uses Web Crypto API if available, falls back to Node.js crypto.\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Lowercase hex string (64 characters)\n */\nexport async function sha256Hex(data: Uint8Array | string): Promise<string> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first (works in browser, edge, and Node 20+)\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return hash.digest('hex');\n } catch {\n throw new Error(\n 'No SHA-256 implementation available. Ensure Web Crypto API or Node.js crypto is available.'\n );\n }\n}\n\n/**\n * Compute SHA-256 hash of data and return as Uint8Array (32 bytes)\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Hash as Uint8Array (32 bytes)\n */\nexport async function sha256Bytes(data: Uint8Array | string): Promise<Uint8Array> {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n\n // Try Web Crypto API first\n if (typeof globalThis.crypto?.subtle?.digest === 'function') {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n return new Uint8Array(hashBuffer);\n }\n\n // Fallback to Node.js crypto\n try {\n const { createHash } = await import('crypto');\n const hash = createHash('sha256');\n hash.update(bytes);\n return new Uint8Array(hash.digest());\n } catch {\n throw new Error('No SHA-256 implementation available.');\n }\n}\n\n/**\n * Convert hex string to Uint8Array\n *\n * @param hex - Lowercase hex string\n * @returns Uint8Array\n */\nexport function hexToBytes(hex: string): Uint8Array {\n // Validate: must be even length and contain only hex characters\n if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {\n throw new Error('Invalid hex string');\n }\n if (hex.length === 0) {\n return new Uint8Array([]);\n }\n const matches = hex.match(/.{2}/g);\n if (!matches) {\n throw new Error('Invalid hex string');\n }\n return new Uint8Array(matches.map((byte) => parseInt(byte, 16)));\n}\n\n/**\n * Convert Uint8Array to lowercase hex string\n *\n * @param bytes - Byte array\n * @returns Lowercase hex string\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Compute SHA-256 hash of data and return as base64url string\n *\n * Used by Wire 0.2 DigestRef format. Evidence bundles use sha256Hex() with\n * \"sha256:<hex>\" prefix instead. A conversion bridge between the two formats\n * (hex <-> base64url) is provided by hexToBase64url() and base64urlToHex().\n *\n * @param data - Data to hash (Uint8Array or string)\n * @returns Base64url-encoded SHA-256 hash (43 characters)\n */\nexport async function sha256Base64url(data: Uint8Array | string): Promise<string> {\n const hashBytes = await sha256Bytes(data);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert a hex-encoded hash to base64url encoding\n *\n * Bridges evidence bundle format (sha256:<hex>) to Wire 0.2 DigestRef format\n * (base64url value). Strips the \"sha256:\" prefix if present.\n *\n * @param hex - Hex string, optionally prefixed with \"sha256:\"\n * @returns Base64url-encoded bytes (43 characters for SHA-256)\n */\nexport function hexToBase64url(hex: string): string {\n const cleanHex = hex.startsWith('sha256:') ? hex.slice(7) : hex;\n return base64urlEncode(hexToBytes(cleanHex));\n}\n\n/**\n * Convert a base64url-encoded hash to hex encoding\n *\n * Bridges Wire 0.2 DigestRef format (base64url) to evidence bundle format\n * (hex). Does NOT add the \"sha256:\" prefix -- caller adds it if needed.\n *\n * @param b64u - Base64url-encoded hash\n * @returns Lowercase hex string (64 characters for SHA-256)\n */\nexport function base64urlToHex(b64u: string): string {\n return bytesToHex(base64urlDecode(b64u));\n}\n\n/**\n * JWK structure for Ed25519 public keys (minimal required fields for thumbprint)\n */\nexport interface JWKThumbprintInput {\n /** Key type - must be \"OKP\" for Ed25519 */\n kty: string;\n /** Curve - must be \"Ed25519\" */\n crv: string;\n /** Public key (base64url) */\n x: string;\n}\n\n/**\n * Compute RFC 7638 JWK Thumbprint (base64url, SHA-256)\n *\n * For Ed25519 keys, the canonical JSON is: {\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"<base64url>\"}\n * Returns base64url-encoded SHA-256 hash (43 characters).\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Base64url-encoded SHA-256 thumbprint (43 characters)\n */\nexport async function computeJwkThumbprint(jwk: JWKThumbprintInput): Promise<string> {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported for thumbprint computation');\n }\n\n // Canonical JSON per RFC 7638 (alphabetically sorted members, no whitespace)\n const canonical = JSON.stringify({\n crv: jwk.crv,\n kty: jwk.kty,\n x: jwk.x,\n });\n\n const hashBytes = await sha256Bytes(canonical);\n return base64urlEncode(hashBytes);\n}\n\n/**\n * Convert JWK to Ed25519 public key bytes (32 bytes)\n *\n * @param jwk - JWK with kty, crv, x fields\n * @returns Public key as 32-byte Uint8Array\n */\nexport function jwkToPublicKeyBytes(jwk: JWKThumbprintInput): Uint8Array {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported');\n }\n\n const xBytes = base64urlDecode(jwk.x);\n if (xBytes.length !== 32) {\n throw new Error(`Ed25519 public key must be 32 bytes, got ${xBytes.length}`);\n }\n\n return xBytes;\n}\n","/**\n * JSON Canonicalization Scheme (RFC 8785)\n * Deterministic JSON serialization for cryptographic hashing\n *\n * PROTOCOL DECISION: JavaScript `undefined` Handling\n * ===================================================\n *\n * RFC 8785 canonicalizes JSON values, and `undefined` is NOT a JSON value.\n * This implementation adopts \"JS-ergonomic\" semantics that match JSON.stringify:\n *\n * 1. **Object properties with undefined values are OMITTED**\n * - `canonicalize({a: 1, b: undefined})` -> `{\"a\":1}`\n * - Matches: `JSON.stringify({a: 1, b: undefined})` -> `{\"a\":1}`\n *\n * 2. **Array elements that are undefined become null**\n * - `canonicalize([1, undefined, 3])` -> `[1,null,3]`\n * - Matches: `JSON.stringify([1, undefined, 3])` -> `[1,null,3]`\n *\n * 3. **Top-level undefined THROWS**\n * - `canonicalize(undefined)` -> throws Error\n * - Rationale: No valid JSON representation exists\n *\n * CROSS-LANGUAGE INTEROPERABILITY WARNING:\n * =========================================\n * Cross-language producers MUST NOT rely on \"undefined\" semantics. To achieve\n * identical hashes across implementations, explicitly encode `null` in arrays\n * and omit keys in objects. Other language implementations (Go, Rust, Python)\n * will never produce `undefined` since it's JavaScript-specific.\n *\n * Guidelines:\n * - Producers MUST emit explicit `null` or omit keys; do NOT rely on coercion\n * - Verifiers SHOULD sanitize inputs before hashing (remove undefined properties)\n * - The canonical output is identical whether you pass `{a: undefined}` or `{}`\n *\n * This behavior is NORMATIVE for PEAC hashing and MUST NOT change without\n * a wire format version bump.\n */\n\n/**\n * Canonicalize a JSON value according to RFC 8785.\n *\n * @param obj - The value to canonicalize. Must be a valid JSON type (null, boolean,\n * number, string, array, object). Functions and Symbols throw.\n * @returns Canonical JSON string with sorted keys and no whitespace.\n * @throws Error if the value cannot be canonicalized (undefined at top level,\n * non-finite numbers, functions, symbols).\n *\n * @example\n * ```ts\n * canonicalize({ b: 2, a: 1 }) // '{\"a\":1,\"b\":2}'\n * canonicalize([1, null, \"x\"]) // '[1,null,\"x\"]'\n * ```\n */\nexport function canonicalize(obj: unknown): string {\n if (obj === null) {\n return 'null';\n }\n\n if (typeof obj === 'boolean') {\n return obj ? 'true' : 'false';\n }\n\n if (typeof obj === 'number') {\n // RFC 8785 number serialization (no trailing zeros, no exponential for small numbers)\n if (!Number.isFinite(obj)) {\n throw new Error('Cannot canonicalize non-finite number');\n }\n if (Object.is(obj, -0)) {\n return '0';\n }\n // Use JSON.stringify for proper number formatting per RFC 8785\n const str = JSON.stringify(obj);\n // Ensure no exponential notation for integers\n if (Number.isInteger(obj) && str.includes('e')) {\n return obj.toString();\n }\n return str;\n }\n\n if (typeof obj === 'string') {\n return JSON.stringify(obj);\n }\n\n if (Array.isArray(obj)) {\n // PROTOCOL DECISION: undefined in arrays becomes null (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n const elements = obj.map((el) => (el === undefined ? 'null' : canonicalize(el)));\n return `[${elements.join(',')}]`;\n }\n\n if (typeof obj === 'object') {\n // Sort keys lexicographically by UTF-16 code unit (RFC 8785 requirement)\n const keys = Object.keys(obj).sort();\n const pairs: string[] = [];\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n // PROTOCOL DECISION: Skip undefined values (matches JSON.stringify)\n // See module-level documentation for cross-language interoperability notes.\n if (value === undefined) {\n continue;\n }\n pairs.push(`${JSON.stringify(key)}:${canonicalize(value)}`);\n }\n return `{${pairs.join(',')}}`;\n }\n\n throw new Error(`Cannot canonicalize type: ${typeof obj}`);\n}\n\n/**\n * Canonicalize and encode as UTF-8 bytes\n */\nexport function canonicalizeBytes(obj: unknown): Uint8Array {\n const canonical = canonicalize(obj);\n return new TextEncoder().encode(canonical);\n}\n\n/**\n * Compute JCS+SHA-256 hash of an object\n */\nexport async function jcsHash(obj: unknown): Promise<string> {\n const bytes = canonicalizeBytes(obj);\n const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * JWS compact serialization with Ed25519 (RFC 8032)\n * Dual-stack: Wire 0.1 (peac-receipt/0.1) and Wire 0.2 (interaction-record+jwt)\n */\n\nimport {\n sign as ed25519Sign,\n verify as ed25519Verify,\n getPublicKey,\n randomSecretKey,\n} from './ed25519.js';\nimport {\n WIRE_01_JWS_TYP,\n WIRE_02_JWS_TYP,\n WIRE_02_JWS_TYP_ACCEPT,\n PEAC_ALG,\n VERIFIER_LIMITS,\n} from '@peac/kernel';\nimport {\n base64urlEncode,\n base64urlDecode,\n base64urlEncodeString,\n base64urlDecodeString,\n} from './base64url';\nimport { CryptoError } from './errors';\n\n// ---------------------------------------------------------------------------\n// JWS header types: 3-variant discriminated union (Correction 1, DD-156)\n// ---------------------------------------------------------------------------\n\n/**\n * JWS header for Wire 0.1 receipts (typ: 'peac-receipt/0.1')\n */\nexport interface Wire01JWSHeader {\n typ: typeof WIRE_01_JWS_TYP;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * JWS header for Wire 0.2 receipts (typ: 'interaction-record+jwt', compact canonical form)\n *\n * The full media type 'application/interaction-record+jwt' is accepted by verify()\n * and decode() but normalized to this compact form before returning.\n */\nexport interface Wire02JWSHeader {\n typ: typeof WIRE_02_JWS_TYP;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * JWS header for tokens with no typ field\n *\n * Crypto layer passes these through without error.\n * The strictness decision (hard error vs. warning) belongs exclusively\n * in @peac/protocol.verifyLocal() (Correction 1, DD-156).\n */\nexport interface UnTypedJWSHeader {\n typ?: undefined;\n alg: typeof PEAC_ALG;\n kid: string;\n}\n\n/**\n * Discriminated union of all recognized JWS header variants.\n *\n * Callers narrow by checking `header.typ`:\n * if (header.typ === WIRE_02_JWS_TYP) { ... Wire 0.2 path ... }\n * if (header.typ === WIRE_01_JWS_TYP) { ... Wire 0.1 path ... }\n * if (header.typ === undefined) { ... UnTyped / interop path ... }\n */\nexport type JWSHeader = Wire01JWSHeader | Wire02JWSHeader | UnTypedJWSHeader;\n\n/**\n * Result of JWS verification\n */\nexport interface VerifyResult<T = unknown> {\n header: JWSHeader;\n payload: T;\n valid: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal constants (NOT exported)\n// ---------------------------------------------------------------------------\n\n/** All typ values accepted by verify() / decode(). Includes full media type form. */\nconst ACCEPTED_TYP_VALUES = new Set<string>([WIRE_01_JWS_TYP, ...WIRE_02_JWS_TYP_ACCEPT]);\n\n// ---------------------------------------------------------------------------\n// validateWire02Header (exported, JOSE hardening, Correction 9, DD-156)\n// ---------------------------------------------------------------------------\n\n/**\n * Validate JOSE header fields for Wire 0.2 JOSE hardening requirements.\n *\n * Rejects:\n * - Embedded key material (jwk, x5c, x5u, jku)\n * - crit header (critical extensions)\n * - b64: false (RFC 7797 unencoded payload)\n * - zip header (payload compression)\n * - Missing, empty, or oversized kid (DoS safety: max 256 chars)\n *\n * @param header - Raw parsed JWS header object\n * @throws CryptoError with CRYPTO_JWS_* code on any violation\n */\nexport function validateWire02Header(header: Record<string, unknown>): void {\n // kid: required, non-empty, max 256 chars (Correction 9, DoS safety)\n if (\n !header.kid ||\n typeof header.kid !== 'string' ||\n header.kid.length === 0 ||\n header.kid.length > 256\n ) {\n throw new CryptoError(\n 'CRYPTO_JWS_MISSING_KID',\n 'kid is missing, empty, or exceeds 256 characters'\n );\n }\n\n // Embedded key material: hard reject (prevents key confusion attacks)\n if (\n header.jwk !== undefined ||\n header.x5c !== undefined ||\n header.x5u !== undefined ||\n header.jku !== undefined\n ) {\n throw new CryptoError(\n 'CRYPTO_JWS_EMBEDDED_KEY',\n 'embedded key material (jwk/x5c/x5u/jku) is not permitted'\n );\n }\n\n // crit: hard reject\n if (header.crit !== undefined) {\n throw new CryptoError('CRYPTO_JWS_CRIT_REJECTED', 'crit header is not permitted');\n }\n\n // b64: false: RFC 7797 unencoded payload rejected\n if (header.b64 === false) {\n throw new CryptoError(\n 'CRYPTO_JWS_B64_REJECTED',\n 'b64:false (unencoded payload) is not permitted'\n );\n }\n\n // zip: hard reject\n if (header.zip !== undefined) {\n throw new CryptoError('CRYPTO_JWS_ZIP_REJECTED', 'zip header is not permitted');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helper: build typed JWSHeader from raw parsed object\n// ---------------------------------------------------------------------------\n\n/**\n * Build a typed JWSHeader from a raw parsed header object, applying typ\n * normalization (Correction 2, DD-156): 'application/interaction-record+jwt'\n * is normalized to 'interaction-record+jwt' before returning.\n *\n * @param raw - Raw header object from JSON.parse\n * @returns Typed JWSHeader\n * @throws CryptoError if alg is not PEAC_ALG, or if typ is present and not in ACCEPTED_TYP_VALUES\n */\nfunction buildHeader(raw: Record<string, unknown>): JWSHeader {\n // Validate alg (required)\n if (raw.alg !== PEAC_ALG) {\n throw new CryptoError(\n 'CRYPTO_INVALID_ALG',\n `Invalid alg: expected ${PEAC_ALG}, got ${String(raw.alg)}`\n );\n }\n\n const rawTyp = raw.typ as string | undefined;\n\n // Normalize full media type to compact form (Correction 2)\n const canonicalTyp = rawTyp === 'application/interaction-record+jwt' ? WIRE_02_JWS_TYP : rawTyp;\n\n if (canonicalTyp !== undefined && !ACCEPTED_TYP_VALUES.has(rawTyp!)) {\n throw new CryptoError('CRYPTO_INVALID_TYP', `Invalid typ: ${String(rawTyp)}`);\n }\n\n const kid = typeof raw.kid === 'string' ? raw.kid : '';\n\n if (canonicalTyp === WIRE_01_JWS_TYP) {\n return { typ: WIRE_01_JWS_TYP, alg: PEAC_ALG, kid } satisfies Wire01JWSHeader;\n }\n if (canonicalTyp === WIRE_02_JWS_TYP) {\n return { typ: WIRE_02_JWS_TYP, alg: PEAC_ALG, kid } satisfies Wire02JWSHeader;\n }\n\n // canonicalTyp is undefined: return UnTypedJWSHeader\n return { typ: undefined, alg: PEAC_ALG, kid } satisfies UnTypedJWSHeader;\n}\n\n// ---------------------------------------------------------------------------\n// sign (Wire 0.1)\n// ---------------------------------------------------------------------------\n\n/**\n * Sign a payload with Ed25519 and return JWS compact serialization (Wire 0.1)\n *\n * Always sets typ to WIRE_01_JWS_TYP ('peac-receipt/0.1').\n * For Wire 0.2, use signWire02() instead.\n *\n * @param payload - JSON-serializable payload\n * @param privateKey - Ed25519 private key (32 bytes)\n * @param kid - Key ID (ISO 8601 timestamp)\n * @returns JWS compact serialization (header.payload.signature)\n */\nexport async function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n\n const header: Wire01JWSHeader = {\n typ: WIRE_01_JWS_TYP,\n alg: PEAC_ALG,\n kid,\n };\n\n const headerB64 = base64urlEncodeString(JSON.stringify(header));\n const payloadB64 = base64urlEncodeString(JSON.stringify(payload));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n const signatureBytes = await ed25519Sign(signingInputBytes, privateKey);\n const signatureB64 = base64urlEncode(signatureBytes);\n\n return `${signingInput}.${signatureB64}`;\n}\n\n// ---------------------------------------------------------------------------\n// signWire02 (Wire 0.2)\n// ---------------------------------------------------------------------------\n\n/**\n * Sign a Wire 0.2 payload with Ed25519 and return JWS compact serialization\n *\n * Always sets typ to WIRE_02_JWS_TYP ('interaction-record+jwt').\n * The payload MUST include peac_version: '0.2'.\n *\n * @param payload - JSON-serializable Wire 0.2 claims payload\n * @param privateKey - Ed25519 private key (32 bytes)\n * @param kid - Key ID (max 256 chars per JOSE hardening rules)\n * @returns JWS compact serialization (header.payload.signature)\n */\nexport async function signWire02(\n payload: unknown,\n privateKey: Uint8Array,\n kid: string\n): Promise<string> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n\n // Validate kid length (Correction 9, DoS safety)\n if (!kid || kid.length === 0 || kid.length > 256) {\n throw new CryptoError(\n 'CRYPTO_JWS_MISSING_KID',\n 'kid is missing, empty, or exceeds 256 characters'\n );\n }\n\n // Always set typ: WIRE_02_JWS_TYP: no code path may omit it\n const header: Wire02JWSHeader = {\n typ: WIRE_02_JWS_TYP,\n alg: PEAC_ALG,\n kid,\n };\n\n const headerB64 = base64urlEncodeString(JSON.stringify(header));\n const payloadB64 = base64urlEncodeString(JSON.stringify(payload));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n\n const signatureBytes = await ed25519Sign(signingInputBytes, privateKey);\n const signatureB64 = base64urlEncode(signatureBytes);\n\n return `${signingInput}.${signatureB64}`;\n}\n\n// ---------------------------------------------------------------------------\n// verify (dual-stack)\n// ---------------------------------------------------------------------------\n\n/**\n * Verify a JWS compact serialization with Ed25519\n *\n * Dual-stack: accepts Wire 0.1 (peac-receipt/0.1) and Wire 0.2\n * (interaction-record+jwt or application/interaction-record+jwt).\n *\n * Typ normalization (Correction 2): 'application/interaction-record+jwt' is\n * normalized to 'interaction-record+jwt' in the returned header.\n *\n * UnTypedJWSHeader (Correction 1): tokens with no typ are returned with\n * typ: undefined without error; @peac/protocol.verifyLocal() applies strictness.\n *\n * Coherence check:\n * - typ === WIRE_02_JWS_TYP but payload.peac_version !== '0.2': CRYPTO_WIRE_VERSION_MISMATCH\n * - typ === WIRE_01_JWS_TYP but payload.peac_version === '0.2': CRYPTO_WIRE_VERSION_MISMATCH\n * - typ absent: no coherence check here; verifyLocal() handles it\n *\n * @param jws - JWS compact serialization\n * @param publicKey - Ed25519 public key (32 bytes)\n * @returns Verification result with typed header and decoded payload\n */\nexport async function verify<T = unknown>(\n jws: string,\n publicKey: Uint8Array\n): Promise<VerifyResult<T>> {\n if (publicKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 public key must be 32 bytes');\n }\n\n // Fast-reject oversized tokens before any parsing (DoS safety).\n // Uses VERIFIER_LIMITS.maxReceiptBytes (256 KB) as the upper bound.\n if (jws.length > VERIFIER_LIMITS.maxReceiptBytes) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n `JWS exceeds maximum size of ${VERIFIER_LIMITS.maxReceiptBytes} bytes`\n );\n }\n\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode and build typed header.\n // Wrap in try/catch to translate SyntaxError / decode failures into stable\n // CryptoError codes; callers depend on CRYPTO_INVALID_JWS_FORMAT for error\n // classification and must never receive a raw SyntaxError at the boundary.\n let rawHeader: Record<string, unknown>;\n try {\n rawHeader = JSON.parse(base64urlDecodeString(headerB64)) as Record<string, unknown>;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS header: invalid base64url or JSON');\n }\n const header = buildHeader(rawHeader);\n\n // Apply JOSE hardening for Wire 0.2 and UnTyped tokens (Correction 1, DD-156).\n // JOSE security invariants (embedded key material, crit, b64:false, zip) MUST be\n // enforced regardless of whether typ is present. Interop mode controls routing only;\n // it does not exempt tokens from key-injection or unencoded-payload attacks.\n // Wire 0.1 tokens are excluded: the legacy format predates these constraints.\n if (header.typ === WIRE_02_JWS_TYP || header.typ === undefined) {\n validateWire02Header(rawHeader);\n }\n\n // Decode payload; same stable-error contract as header decode above.\n let payload: T;\n try {\n payload = JSON.parse(base64urlDecodeString(payloadB64)) as T;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS payload: invalid base64url or JSON');\n }\n\n // Coherence check: wire version consistency (only when typ is present)\n if (header.typ !== undefined) {\n const payloadVersion = (payload as Record<string, unknown>).peac_version;\n if (header.typ === WIRE_02_JWS_TYP && payloadVersion !== '0.2') {\n throw new CryptoError(\n 'CRYPTO_WIRE_VERSION_MISMATCH',\n `typ is ${WIRE_02_JWS_TYP} but peac_version is ${String(payloadVersion)} (expected '0.2')`\n );\n }\n if (header.typ === WIRE_01_JWS_TYP && payloadVersion === '0.2') {\n throw new CryptoError(\n 'CRYPTO_WIRE_VERSION_MISMATCH',\n `typ is ${WIRE_01_JWS_TYP} but peac_version is '0.2' (Wire 0.2 must use ${WIRE_02_JWS_TYP})`\n );\n }\n }\n\n // Verify Ed25519 signature\n const signatureBytes = base64urlDecode(signatureB64);\n const signingInput = `${headerB64}.${payloadB64}`;\n const signingInputBytes = new TextEncoder().encode(signingInput);\n const valid = await ed25519Verify(signatureBytes, signingInputBytes, publicKey);\n\n return { header, payload, valid };\n}\n\n// ---------------------------------------------------------------------------\n// decode (dual-stack, unverified)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode JWS without verifying signature\n *\n * UNSAFE: This function is for debugging and diagnostic inspection only.\n * It does NOT verify the Ed25519 signature and does NOT apply JOSE hardening\n * (embedded-key rejection, b64/zip/crit checks). Tokens returned by decode()\n * must never be trusted for authorization decisions.\n *\n * For secure verification use verify() instead.\n *\n * Applies the same typ normalization and header typing as verify().\n * Unrecognized typ values still throw CryptoError.\n *\n * @param jws - JWS compact serialization\n * @returns Decoded header and payload (unverified, no JOSE hardening applied)\n */\nexport function decode<T = unknown>(jws: string): { header: JWSHeader; payload: T } {\n const parts = jws.split('.');\n if (parts.length !== 3) {\n throw new CryptoError(\n 'CRYPTO_INVALID_JWS_FORMAT',\n 'Invalid JWS: must have three dot-separated parts'\n );\n }\n\n const [headerB64, payloadB64] = parts;\n\n let rawHeader: Record<string, unknown>;\n try {\n rawHeader = JSON.parse(base64urlDecodeString(headerB64)) as Record<string, unknown>;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS header: invalid base64url or JSON');\n }\n const header = buildHeader(rawHeader);\n\n let payload: T;\n try {\n payload = JSON.parse(base64urlDecodeString(payloadB64)) as T;\n } catch {\n throw new CryptoError('CRYPTO_INVALID_JWS_FORMAT', 'JWS payload: invalid base64url or JSON');\n }\n\n return { header, payload };\n}\n\n// ---------------------------------------------------------------------------\n// generateKeypair\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a random Ed25519 keypair\n *\n * @returns Private key (32 bytes) and public key (32 bytes)\n */\nexport async function generateKeypair(): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n const privateKey = randomSecretKey();\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n\n// NOTE: generateKeypairFromSeed has been moved to @peac/crypto/testkit\n// It's intentionally NOT exported from the main module to prevent accidental\n// use in production. Use: import { generateKeypairFromSeed } from '@peac/crypto/testkit'\n\n// ---------------------------------------------------------------------------\n// Ed25519 JWK utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Ed25519 JWK interface for private keys\n */\nexport interface Ed25519PrivateJwk {\n kty: 'OKP';\n crv: 'Ed25519';\n /** Public key (base64url encoded, 32 bytes) */\n x: string;\n /** Private key (base64url encoded, 32 bytes) */\n d: string;\n}\n\n/**\n * Derive the Ed25519 public key from a private key\n *\n * @param privateKey - Ed25519 private key (32 bytes)\n * @returns Public key (32 bytes)\n */\nexport async function derivePublicKey(privateKey: Uint8Array): Promise<Uint8Array> {\n if (privateKey.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_KEY_LENGTH', 'Ed25519 private key must be 32 bytes');\n }\n return getPublicKey(privateKey);\n}\n\n/**\n * Validate that an Ed25519 JWK has a consistent keypair\n *\n * Derives the public key from the private key (d) and verifies it matches\n * the declared public key (x). This catches configuration errors where\n * the wrong key components are paired.\n *\n * @param jwk - Ed25519 JWK with both public (x) and private (d) components\n * @returns true if the keypair is consistent, false otherwise\n *\n * @example\n * ```typescript\n * const jwk = {\n * kty: 'OKP',\n * crv: 'Ed25519',\n * x: 'base64url-encoded-public-key',\n * d: 'base64url-encoded-private-key',\n * };\n *\n * if (!await validateKeypair(jwk)) {\n * throw new Error('Invalid keypair: d does not derive to x');\n * }\n * ```\n */\nexport async function validateKeypair(jwk: Ed25519PrivateJwk): Promise<boolean> {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n return false;\n }\n\n let privateKeyBytes: Uint8Array;\n let declaredPublicKeyBytes: Uint8Array;\n\n try {\n privateKeyBytes = base64urlDecode(jwk.d);\n declaredPublicKeyBytes = base64urlDecode(jwk.x);\n } catch {\n return false;\n }\n\n if (privateKeyBytes.length !== 32 || declaredPublicKeyBytes.length !== 32) {\n return false;\n }\n\n const derivedPublicKeyBytes = await getPublicKey(privateKeyBytes);\n\n if (derivedPublicKeyBytes.length !== declaredPublicKeyBytes.length) {\n return false;\n }\n\n for (let i = 0; i < derivedPublicKeyBytes.length; i++) {\n if (derivedPublicKeyBytes[i] !== declaredPublicKeyBytes[i]) {\n return false;\n }\n }\n\n return true;\n}\n"]}
package/dist/jws.d.ts CHANGED
@@ -1,16 +1,48 @@
1
1
  /**
2
2
  * JWS compact serialization with Ed25519 (RFC 8032)
3
- * Implements peac-receipt/0.1 wire format
3
+ * Dual-stack: Wire 0.1 (peac-receipt/0.1) and Wire 0.2 (interaction-record+jwt)
4
4
  */
5
- import { PEAC_WIRE_TYP, PEAC_ALG } from '@peac/schema';
5
+ import { WIRE_01_JWS_TYP, WIRE_02_JWS_TYP, PEAC_ALG } from '@peac/kernel';
6
6
  /**
7
- * JWS header for PEAC receipts
7
+ * JWS header for Wire 0.1 receipts (typ: 'peac-receipt/0.1')
8
8
  */
9
- export interface JWSHeader {
10
- typ: typeof PEAC_WIRE_TYP;
9
+ export interface Wire01JWSHeader {
10
+ typ: typeof WIRE_01_JWS_TYP;
11
11
  alg: typeof PEAC_ALG;
12
12
  kid: string;
13
13
  }
14
+ /**
15
+ * JWS header for Wire 0.2 receipts (typ: 'interaction-record+jwt', compact canonical form)
16
+ *
17
+ * The full media type 'application/interaction-record+jwt' is accepted by verify()
18
+ * and decode() but normalized to this compact form before returning.
19
+ */
20
+ export interface Wire02JWSHeader {
21
+ typ: typeof WIRE_02_JWS_TYP;
22
+ alg: typeof PEAC_ALG;
23
+ kid: string;
24
+ }
25
+ /**
26
+ * JWS header for tokens with no typ field
27
+ *
28
+ * Crypto layer passes these through without error.
29
+ * The strictness decision (hard error vs. warning) belongs exclusively
30
+ * in @peac/protocol.verifyLocal() (Correction 1, DD-156).
31
+ */
32
+ export interface UnTypedJWSHeader {
33
+ typ?: undefined;
34
+ alg: typeof PEAC_ALG;
35
+ kid: string;
36
+ }
37
+ /**
38
+ * Discriminated union of all recognized JWS header variants.
39
+ *
40
+ * Callers narrow by checking `header.typ`:
41
+ * if (header.typ === WIRE_02_JWS_TYP) { ... Wire 0.2 path ... }
42
+ * if (header.typ === WIRE_01_JWS_TYP) { ... Wire 0.1 path ... }
43
+ * if (header.typ === undefined) { ... UnTyped / interop path ... }
44
+ */
45
+ export type JWSHeader = Wire01JWSHeader | Wire02JWSHeader | UnTypedJWSHeader;
14
46
  /**
15
47
  * Result of JWS verification
16
48
  */
@@ -20,7 +52,24 @@ export interface VerifyResult<T = unknown> {
20
52
  valid: boolean;
21
53
  }
22
54
  /**
23
- * Sign a payload with Ed25519 and return JWS compact serialization
55
+ * Validate JOSE header fields for Wire 0.2 JOSE hardening requirements.
56
+ *
57
+ * Rejects:
58
+ * - Embedded key material (jwk, x5c, x5u, jku)
59
+ * - crit header (critical extensions)
60
+ * - b64: false (RFC 7797 unencoded payload)
61
+ * - zip header (payload compression)
62
+ * - Missing, empty, or oversized kid (DoS safety: max 256 chars)
63
+ *
64
+ * @param header - Raw parsed JWS header object
65
+ * @throws CryptoError with CRYPTO_JWS_* code on any violation
66
+ */
67
+ export declare function validateWire02Header(header: Record<string, unknown>): void;
68
+ /**
69
+ * Sign a payload with Ed25519 and return JWS compact serialization (Wire 0.1)
70
+ *
71
+ * Always sets typ to WIRE_01_JWS_TYP ('peac-receipt/0.1').
72
+ * For Wire 0.2, use signWire02() instead.
24
73
  *
25
74
  * @param payload - JSON-serializable payload
26
75
  * @param privateKey - Ed25519 private key (32 bytes)
@@ -28,19 +77,55 @@ export interface VerifyResult<T = unknown> {
28
77
  * @returns JWS compact serialization (header.payload.signature)
29
78
  */
30
79
  export declare function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string>;
80
+ /**
81
+ * Sign a Wire 0.2 payload with Ed25519 and return JWS compact serialization
82
+ *
83
+ * Always sets typ to WIRE_02_JWS_TYP ('interaction-record+jwt').
84
+ * The payload MUST include peac_version: '0.2'.
85
+ *
86
+ * @param payload - JSON-serializable Wire 0.2 claims payload
87
+ * @param privateKey - Ed25519 private key (32 bytes)
88
+ * @param kid - Key ID (max 256 chars per JOSE hardening rules)
89
+ * @returns JWS compact serialization (header.payload.signature)
90
+ */
91
+ export declare function signWire02(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string>;
31
92
  /**
32
93
  * Verify a JWS compact serialization with Ed25519
33
94
  *
95
+ * Dual-stack: accepts Wire 0.1 (peac-receipt/0.1) and Wire 0.2
96
+ * (interaction-record+jwt or application/interaction-record+jwt).
97
+ *
98
+ * Typ normalization (Correction 2): 'application/interaction-record+jwt' is
99
+ * normalized to 'interaction-record+jwt' in the returned header.
100
+ *
101
+ * UnTypedJWSHeader (Correction 1): tokens with no typ are returned with
102
+ * typ: undefined without error; @peac/protocol.verifyLocal() applies strictness.
103
+ *
104
+ * Coherence check:
105
+ * - typ === WIRE_02_JWS_TYP but payload.peac_version !== '0.2': CRYPTO_WIRE_VERSION_MISMATCH
106
+ * - typ === WIRE_01_JWS_TYP but payload.peac_version === '0.2': CRYPTO_WIRE_VERSION_MISMATCH
107
+ * - typ absent: no coherence check here; verifyLocal() handles it
108
+ *
34
109
  * @param jws - JWS compact serialization
35
110
  * @param publicKey - Ed25519 public key (32 bytes)
36
- * @returns Verification result with decoded header and payload
111
+ * @returns Verification result with typed header and decoded payload
37
112
  */
38
113
  export declare function verify<T = unknown>(jws: string, publicKey: Uint8Array): Promise<VerifyResult<T>>;
39
114
  /**
40
- * Decode JWS without verifying signature (use with caution!)
115
+ * Decode JWS without verifying signature
116
+ *
117
+ * UNSAFE: This function is for debugging and diagnostic inspection only.
118
+ * It does NOT verify the Ed25519 signature and does NOT apply JOSE hardening
119
+ * (embedded-key rejection, b64/zip/crit checks). Tokens returned by decode()
120
+ * must never be trusted for authorization decisions.
121
+ *
122
+ * For secure verification use verify() instead.
123
+ *
124
+ * Applies the same typ normalization and header typing as verify().
125
+ * Unrecognized typ values still throw CryptoError.
41
126
  *
42
127
  * @param jws - JWS compact serialization
43
- * @returns Decoded header and payload (unverified)
128
+ * @returns Decoded header and payload (unverified, no JOSE hardening applied)
44
129
  */
45
130
  export declare function decode<T = unknown>(jws: string): {
46
131
  header: JWSHeader;
package/dist/jws.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jws.d.ts","sourceRoot":"","sources":["../src/jws.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AASvD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,OAAO,aAAa,CAAC;IAC1B,GAAG,EAAE,OAAO,QAAQ,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BjG;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,CAAC,GAAG,OAAO,EACtC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAoD1B;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,CAkBlF;AAED;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC,CAKD;AAMD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,KAAK,CAAC;IACX,GAAG,EAAE,SAAS,CAAC;IACf,+CAA+C;IAC/C,CAAC,EAAE,MAAM,CAAC;IACV,gDAAgD;IAChD,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAKjF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAqC9E"}
1
+ {"version":3,"file":"jws.d.ts","sourceRoot":"","sources":["../src/jws.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EACL,eAAe,EACf,eAAe,EAEf,QAAQ,EAET,MAAM,cAAc,CAAC;AAatB;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,OAAO,eAAe,CAAC;IAC5B,GAAG,EAAE,OAAO,QAAQ,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,OAAO,eAAe,CAAC;IAC5B,GAAG,EAAE,OAAO,QAAQ,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,OAAO,QAAQ,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;CAChB;AAaD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA4C1E;AAkDD;;;;;;;;;;GAUG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBjG;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,MAAM,CAAC,CAAC,GAAG,OAAO,EACtC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CA6E1B;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,CA2BlF;AAMD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC,CAKD;AAUD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,KAAK,CAAC;IACX,GAAG,EAAE,SAAS,CAAC;IACf,+CAA+C;IAC/C,CAAC,EAAE,MAAM,CAAC;IACV,gDAAgD;IAChD,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAKjF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAgC9E"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ed25519.ts","../src/errors.ts","../src/testkit.ts"],"names":["ed"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA+BO,IAAM,YAAA,GAAkBA,aAAA,CAAA,iBAAA;AAGGA,aAAA,CAAA,KAAA,CAAM;;;ACJjC,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EAC5B,IAAA;AAAA,EAET,WAAA,CAAY,MAAuB,OAAA,EAAiB;AAClD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAEZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,YAAA,CAAY,SAAS,CAAA;AAAA,EACnD;AACF,CAAA;;;ACHA,eAAsB,wBAAwB,IAAA,EAG3C;AACD,EAAA,IAAI,IAAA,CAAK,WAAW,EAAA,EAAI;AACtB,IAAA,MAAM,IAAI,WAAA,CAAY,4BAAA,EAA8B,+BAA+B,CAAA;AAAA,EACrF;AAIA,EAAA,MAAM,UAAA,GAAa,IAAA;AACnB,EAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,UAAU,CAAA;AAE/C,EAAA,OAAO,EAAE,YAAY,SAAA,EAAU;AACjC","file":"testkit.cjs","sourcesContent":["/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * PEAC Crypto Test Kit\n *\n * This module contains utilities for TEST FIXTURES ONLY.\n * These functions are NOT exported from the main entry point.\n *\n * Import path: @peac/crypto/testkit\n *\n * SECURITY: Never use these in production. Production bundlers can\n * tree-shake this module away since it's a separate export path.\n */\n\nimport { getPublicKey } from './ed25519.js';\nimport { CryptoError } from './errors.js';\n\n/**\n * Generate an Ed25519 keypair from a deterministic seed.\n *\n * WARNING: FOR TEST FIXTURES ONLY. DO NOT USE IN PRODUCTION.\n * Production code should use generateKeypair() which uses cryptographically\n * secure random bytes. Seeded keys are predictable and compromise security.\n *\n * This function is intentionally in a separate module (@peac/crypto/testkit)\n * so that production bundlers can tree-shake it away.\n *\n * @param seed - 32-byte seed (e.g., SHA-256 hash of a known string)\n * @returns Private key (32 bytes) and public key (32 bytes)\n *\n * @example\n * ```ts\n * // Use only in test fixtures for reproducibility\n * import { generateKeypairFromSeed } from '@peac/crypto/testkit';\n *\n * const seed = sha256('peac-test-key-001');\n * const { privateKey, publicKey } = await generateKeypairFromSeed(seed);\n * ```\n */\nexport async function generateKeypairFromSeed(seed: Uint8Array): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n if (seed.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_SEED_LENGTH', 'Ed25519 seed must be 32 bytes');\n }\n\n // In Ed25519, the private key IS the seed (32 bytes)\n // The public key is derived from it\n const privateKey = seed;\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n"]}
1
+ {"version":3,"sources":["../src/ed25519.ts","../src/errors.ts","../src/testkit.ts"],"names":["ed"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA+BO,IAAM,YAAA,GAAkBA,aAAA,CAAA,iBAAA;AAGGA,aAAA,CAAA,KAAA,CAAM;;;ACGjC,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EAC5B,IAAA;AAAA,EAET,WAAA,CAAY,MAAuB,OAAA,EAAiB;AAClD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAEZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,YAAA,CAAY,SAAS,CAAA;AAAA,EACnD;AACF,CAAA;;;ACVA,eAAsB,wBAAwB,IAAA,EAG3C;AACD,EAAA,IAAI,IAAA,CAAK,WAAW,EAAA,EAAI;AACtB,IAAA,MAAM,IAAI,WAAA,CAAY,4BAAA,EAA8B,+BAA+B,CAAA;AAAA,EACrF;AAIA,EAAA,MAAM,UAAA,GAAa,IAAA;AACnB,EAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,UAAU,CAAA;AAE/C,EAAA,OAAO,EAAE,YAAY,SAAA,EAAU;AACjC","file":"testkit.cjs","sourcesContent":["/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE'\n // Wire 0.2 JOSE hardening (v0.12.0-preview.1, DD-156)\n | 'CRYPTO_WIRE_VERSION_MISMATCH'\n | 'CRYPTO_JWS_EMBEDDED_KEY'\n | 'CRYPTO_JWS_CRIT_REJECTED'\n | 'CRYPTO_JWS_MISSING_KID'\n | 'CRYPTO_JWS_B64_REJECTED'\n | 'CRYPTO_JWS_ZIP_REJECTED';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * PEAC Crypto Test Kit\n *\n * This module contains utilities for TEST FIXTURES ONLY.\n * These functions are NOT exported from the main entry point.\n *\n * Import path: @peac/crypto/testkit\n *\n * SECURITY: Never use these in production. Production bundlers can\n * tree-shake this module away since it's a separate export path.\n */\n\nimport { getPublicKey } from './ed25519.js';\nimport { CryptoError } from './errors.js';\n\n/**\n * Generate an Ed25519 keypair from a deterministic seed.\n *\n * WARNING: FOR TEST FIXTURES ONLY. DO NOT USE IN PRODUCTION.\n * Production code should use generateKeypair() which uses cryptographically\n * secure random bytes. Seeded keys are predictable and compromise security.\n *\n * This function is intentionally in a separate module (@peac/crypto/testkit)\n * so that production bundlers can tree-shake it away.\n *\n * @param seed - 32-byte seed (e.g., SHA-256 hash of a known string)\n * @returns Private key (32 bytes) and public key (32 bytes)\n *\n * @example\n * ```ts\n * // Use only in test fixtures for reproducibility\n * import { generateKeypairFromSeed } from '@peac/crypto/testkit';\n *\n * const seed = sha256('peac-test-key-001');\n * const { privateKey, publicKey } = await generateKeypairFromSeed(seed);\n * ```\n */\nexport async function generateKeypairFromSeed(seed: Uint8Array): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n if (seed.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_SEED_LENGTH', 'Ed25519 seed must be 32 bytes');\n }\n\n // In Ed25519, the private key IS the seed (32 bytes)\n // The public key is derived from it\n const privateKey = seed;\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ed25519.ts","../src/errors.ts","../src/testkit.ts"],"names":[],"mappings":";;;AA+BO,IAAM,YAAA,GAAkB,EAAA,CAAA,iBAAA;AAGG,EAAA,CAAA,KAAA,CAAM;;;ACJjC,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EAC5B,IAAA;AAAA,EAET,WAAA,CAAY,MAAuB,OAAA,EAAiB;AAClD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAEZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,YAAA,CAAY,SAAS,CAAA;AAAA,EACnD;AACF,CAAA;;;ACHA,eAAsB,wBAAwB,IAAA,EAG3C;AACD,EAAA,IAAI,IAAA,CAAK,WAAW,EAAA,EAAI;AACtB,IAAA,MAAM,IAAI,WAAA,CAAY,4BAAA,EAA8B,+BAA+B,CAAA;AAAA,EACrF;AAIA,EAAA,MAAM,UAAA,GAAa,IAAA;AACnB,EAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,UAAU,CAAA;AAE/C,EAAA,OAAO,EAAE,YAAY,SAAA,EAAU;AACjC","file":"testkit.mjs","sourcesContent":["/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * PEAC Crypto Test Kit\n *\n * This module contains utilities for TEST FIXTURES ONLY.\n * These functions are NOT exported from the main entry point.\n *\n * Import path: @peac/crypto/testkit\n *\n * SECURITY: Never use these in production. Production bundlers can\n * tree-shake this module away since it's a separate export path.\n */\n\nimport { getPublicKey } from './ed25519.js';\nimport { CryptoError } from './errors.js';\n\n/**\n * Generate an Ed25519 keypair from a deterministic seed.\n *\n * WARNING: FOR TEST FIXTURES ONLY. DO NOT USE IN PRODUCTION.\n * Production code should use generateKeypair() which uses cryptographically\n * secure random bytes. Seeded keys are predictable and compromise security.\n *\n * This function is intentionally in a separate module (@peac/crypto/testkit)\n * so that production bundlers can tree-shake it away.\n *\n * @param seed - 32-byte seed (e.g., SHA-256 hash of a known string)\n * @returns Private key (32 bytes) and public key (32 bytes)\n *\n * @example\n * ```ts\n * // Use only in test fixtures for reproducibility\n * import { generateKeypairFromSeed } from '@peac/crypto/testkit';\n *\n * const seed = sha256('peac-test-key-001');\n * const { privateKey, publicKey } = await generateKeypairFromSeed(seed);\n * ```\n */\nexport async function generateKeypairFromSeed(seed: Uint8Array): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n if (seed.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_SEED_LENGTH', 'Ed25519 seed must be 32 bytes');\n }\n\n // In Ed25519, the private key IS the seed (32 bytes)\n // The public key is derived from it\n const privateKey = seed;\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n"]}
1
+ {"version":3,"sources":["../src/ed25519.ts","../src/errors.ts","../src/testkit.ts"],"names":[],"mappings":";;;AA+BO,IAAM,YAAA,GAAkB,EAAA,CAAA,iBAAA;AAGG,EAAA,CAAA,KAAA,CAAM;;;ACGjC,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EAC5B,IAAA;AAAA,EAET,WAAA,CAAY,MAAuB,OAAA,EAAiB;AAClD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAEZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,YAAA,CAAY,SAAS,CAAA;AAAA,EACnD;AACF,CAAA;;;ACVA,eAAsB,wBAAwB,IAAA,EAG3C;AACD,EAAA,IAAI,IAAA,CAAK,WAAW,EAAA,EAAI;AACtB,IAAA,MAAM,IAAI,WAAA,CAAY,4BAAA,EAA8B,+BAA+B,CAAA;AAAA,EACrF;AAIA,EAAA,MAAM,UAAA,GAAa,IAAA;AACnB,EAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,UAAU,CAAA;AAE/C,EAAA,OAAO,EAAE,YAAY,SAAA,EAAU;AACjC","file":"testkit.mjs","sourcesContent":["/**\n * Internal Ed25519 wrapper -- async-only surface\n *\n * PEAC uses ONLY the async methods from @noble/ed25519.\n * In noble v3, sync methods require explicit hash configuration.\n * Async methods use built-in Web Crypto and need no configuration.\n *\n * This module re-exports only the async surface to prevent accidental\n * sync usage. All other modules in @peac/crypto MUST import from\n * this file, never directly from '@noble/ed25519'.\n *\n * Key material handling:\n * - Private keys are 32-byte Uint8Array (Ed25519 seed)\n * - Public keys are 32-byte Uint8Array (compressed Ed25519 point)\n * - JavaScript cannot guarantee memory zeroization; callers should\n * avoid storing keys longer than necessary and never log key bytes\n * - randomSecretKey() uses crypto.getRandomValues() (CSPRNG)\n * which is available in Node.js >=15 and all modern runtimes\n */\n\n// Namespace import avoids tsup tree-shaking false positive in multi-entry builds\n// where signAsync/verifyAsync appear unused in the testkit entry point.\nimport * as ed from '@noble/ed25519';\n\n/** Sign a message with Ed25519 (async, Web Crypto backed) */\nexport const sign = ed.signAsync;\n\n/** Verify an Ed25519 signature (async, Web Crypto backed) */\nexport const verify = ed.verifyAsync;\n\n/** Derive public key from private key (async, Web Crypto backed) */\nexport const getPublicKey = ed.getPublicKeyAsync;\n\n/** Generate a cryptographically random 32-byte secret key (CSPRNG) */\nexport const randomSecretKey = ed.utils.randomSecretKey;\n","/**\n * Typed errors for @peac/crypto\n *\n * These error codes are INTERNAL to @peac/crypto and should NOT be exposed\n * as protocol-stable API. Higher-level packages (like @peac/protocol) should\n * map these to canonical E_* codes from specs/kernel/errors.json.\n *\n * The CRYPTO_ prefix makes it clear these are package-internal codes.\n */\n\n/**\n * Internal error codes for crypto operations\n *\n * These are NOT canonical protocol error codes. They are internal to @peac/crypto.\n * @peac/protocol maps these to canonical E_* codes (E_INVALID_FORMAT, etc).\n */\nexport type CryptoErrorCode =\n | 'CRYPTO_INVALID_KEY_LENGTH'\n | 'CRYPTO_INVALID_SEED_LENGTH'\n | 'CRYPTO_INVALID_JWS_FORMAT'\n | 'CRYPTO_INVALID_TYP'\n | 'CRYPTO_INVALID_ALG'\n | 'CRYPTO_INVALID_SIGNATURE'\n // Wire 0.2 JOSE hardening (v0.12.0-preview.1, DD-156)\n | 'CRYPTO_WIRE_VERSION_MISMATCH'\n | 'CRYPTO_JWS_EMBEDDED_KEY'\n | 'CRYPTO_JWS_CRIT_REJECTED'\n | 'CRYPTO_JWS_MISSING_KID'\n | 'CRYPTO_JWS_B64_REJECTED'\n | 'CRYPTO_JWS_ZIP_REJECTED';\n\n/**\n * Typed error for crypto operations\n *\n * Use `err.code` to handle errors programmatically without message parsing.\n * The code is a CRYPTO_* internal code, not a canonical E_* protocol code.\n */\nexport class CryptoError extends Error {\n readonly code: CryptoErrorCode;\n\n constructor(code: CryptoErrorCode, message: string) {\n super(message);\n this.name = 'CryptoError';\n this.code = code;\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, CryptoError.prototype);\n }\n}\n\n/**\n * Check if a CryptoError code indicates a format/structure issue\n * (as opposed to a cryptographic verification failure)\n *\n * @internal This is an internal helper, not part of the public API.\n * Use structural checks on CryptoError.code instead of relying on this function.\n */\nexport function isFormatError(code: CryptoErrorCode): boolean {\n return (\n code === 'CRYPTO_INVALID_JWS_FORMAT' ||\n code === 'CRYPTO_INVALID_TYP' ||\n code === 'CRYPTO_INVALID_ALG' ||\n code === 'CRYPTO_INVALID_KEY_LENGTH' ||\n code === 'CRYPTO_INVALID_SEED_LENGTH'\n );\n}\n","/**\n * PEAC Crypto Test Kit\n *\n * This module contains utilities for TEST FIXTURES ONLY.\n * These functions are NOT exported from the main entry point.\n *\n * Import path: @peac/crypto/testkit\n *\n * SECURITY: Never use these in production. Production bundlers can\n * tree-shake this module away since it's a separate export path.\n */\n\nimport { getPublicKey } from './ed25519.js';\nimport { CryptoError } from './errors.js';\n\n/**\n * Generate an Ed25519 keypair from a deterministic seed.\n *\n * WARNING: FOR TEST FIXTURES ONLY. DO NOT USE IN PRODUCTION.\n * Production code should use generateKeypair() which uses cryptographically\n * secure random bytes. Seeded keys are predictable and compromise security.\n *\n * This function is intentionally in a separate module (@peac/crypto/testkit)\n * so that production bundlers can tree-shake it away.\n *\n * @param seed - 32-byte seed (e.g., SHA-256 hash of a known string)\n * @returns Private key (32 bytes) and public key (32 bytes)\n *\n * @example\n * ```ts\n * // Use only in test fixtures for reproducibility\n * import { generateKeypairFromSeed } from '@peac/crypto/testkit';\n *\n * const seed = sha256('peac-test-key-001');\n * const { privateKey, publicKey } = await generateKeypairFromSeed(seed);\n * ```\n */\nexport async function generateKeypairFromSeed(seed: Uint8Array): Promise<{\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n}> {\n if (seed.length !== 32) {\n throw new CryptoError('CRYPTO_INVALID_SEED_LENGTH', 'Ed25519 seed must be 32 bytes');\n }\n\n // In Ed25519, the private key IS the seed (32 bytes)\n // The public key is derived from it\n const privateKey = seed;\n const publicKey = await getPublicKey(privateKey);\n\n return { privateKey, publicKey };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peac/crypto",
3
- "version": "0.11.2",
3
+ "version": "0.12.0-preview.1",
4
4
  "description": "Ed25519 JWS signing and verification for PEAC protocol",
5
5
  "main": "dist/index.cjs",
6
6
  "types": "dist/index.d.ts",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@noble/ed25519": "^3.0.0",
41
- "@peac/schema": "0.11.2"
41
+ "@peac/kernel": "0.12.0-preview.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.19.11",