@peac/protocol 0.10.9 → 0.10.11
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/LICENSE +1 -1
- package/dist/discovery.d.ts.map +1 -1
- package/dist/index.cjs +2694 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +2612 -0
- package/dist/index.mjs.map +1 -0
- package/dist/verification-report.d.ts.map +1 -1
- package/dist/verifier-types.d.ts +42 -2
- package/dist/verifier-types.d.ts.map +1 -1
- package/dist/verify-local.cjs +164 -0
- package/dist/verify-local.cjs.map +1 -0
- package/dist/verify-local.d.ts +15 -0
- package/dist/verify-local.d.ts.map +1 -1
- package/dist/verify-local.mjs +160 -0
- package/dist/verify-local.mjs.map +1 -0
- package/dist/verify.d.ts.map +1 -1
- package/package.json +20 -13
- package/dist/crypto-utils.js +0 -21
- package/dist/crypto-utils.js.map +0 -1
- package/dist/discovery.js +0 -405
- package/dist/discovery.js.map +0 -1
- package/dist/headers.js +0 -110
- package/dist/headers.js.map +0 -1
- package/dist/index.js +0 -44
- package/dist/index.js.map +0 -1
- package/dist/issue.js +0 -198
- package/dist/issue.js.map +0 -1
- package/dist/pointer-fetch.js +0 -305
- package/dist/pointer-fetch.js.map +0 -1
- package/dist/ssrf-safe-fetch.js +0 -671
- package/dist/ssrf-safe-fetch.js.map +0 -1
- package/dist/telemetry.js +0 -43
- package/dist/telemetry.js.map +0 -1
- package/dist/transport-profiles.js +0 -424
- package/dist/transport-profiles.js.map +0 -1
- package/dist/verification-report.js +0 -322
- package/dist/verification-report.js.map +0 -1
- package/dist/verifier-core.js +0 -578
- package/dist/verifier-core.js.map +0 -1
- package/dist/verifier-types.js +0 -161
- package/dist/verifier-types.js.map +0 -1
- package/dist/verify-local.js +0 -230
- package/dist/verify-local.js.map +0 -1
- package/dist/verify.js +0 -213
- package/dist/verify.js.map +0 -1
package/dist/verifier-types.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* PEAC Verifier Types
|
|
4
|
-
*
|
|
5
|
-
* Types for verification policy, trust pinning, and verification reports
|
|
6
|
-
* per VERIFIER-SECURITY-MODEL.md, TRUST-PINNING-POLICY.md, and
|
|
7
|
-
* VERIFICATION-REPORT-FORMAT.md
|
|
8
|
-
*
|
|
9
|
-
* @packageDocumentation
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.NON_DETERMINISTIC_ARTIFACT_KEYS = exports.CHECK_IDS = exports.DEFAULT_NETWORK_SECURITY = exports.DEFAULT_VERIFIER_LIMITS = void 0;
|
|
13
|
-
exports.createDefaultPolicy = createDefaultPolicy;
|
|
14
|
-
exports.createDigest = createDigest;
|
|
15
|
-
exports.createEmptyReport = createEmptyReport;
|
|
16
|
-
exports.ssrfErrorToReasonCode = ssrfErrorToReasonCode;
|
|
17
|
-
exports.reasonCodeToSeverity = reasonCodeToSeverity;
|
|
18
|
-
exports.reasonCodeToErrorCode = reasonCodeToErrorCode;
|
|
19
|
-
const kernel_1 = require("@peac/kernel");
|
|
20
|
-
/**
|
|
21
|
-
* Default verifier limits from VERIFIER-SECURITY-MODEL.md
|
|
22
|
-
*/
|
|
23
|
-
exports.DEFAULT_VERIFIER_LIMITS = {
|
|
24
|
-
max_receipt_bytes: kernel_1.VERIFIER_LIMITS.maxReceiptBytes,
|
|
25
|
-
max_jwks_bytes: kernel_1.VERIFIER_LIMITS.maxJwksBytes,
|
|
26
|
-
max_jwks_keys: kernel_1.VERIFIER_LIMITS.maxJwksKeys,
|
|
27
|
-
max_redirects: kernel_1.VERIFIER_LIMITS.maxRedirects,
|
|
28
|
-
fetch_timeout_ms: kernel_1.VERIFIER_LIMITS.fetchTimeoutMs,
|
|
29
|
-
max_extension_bytes: kernel_1.VERIFIER_LIMITS.maxExtensionBytes,
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Default network security settings from VERIFIER-SECURITY-MODEL.md
|
|
33
|
-
*/
|
|
34
|
-
exports.DEFAULT_NETWORK_SECURITY = {
|
|
35
|
-
https_only: kernel_1.VERIFIER_NETWORK.httpsOnly,
|
|
36
|
-
block_private_ips: kernel_1.VERIFIER_NETWORK.blockPrivateIps,
|
|
37
|
-
allow_redirects: kernel_1.VERIFIER_NETWORK.allowRedirects,
|
|
38
|
-
allow_cross_origin_redirects: true, // Allow for CDN compatibility
|
|
39
|
-
dns_failure_behavior: 'block', // Fail-closed by default
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Create a default verifier policy
|
|
43
|
-
*/
|
|
44
|
-
function createDefaultPolicy(mode) {
|
|
45
|
-
return {
|
|
46
|
-
policy_version: kernel_1.VERIFIER_POLICY_VERSION,
|
|
47
|
-
mode,
|
|
48
|
-
limits: { ...exports.DEFAULT_VERIFIER_LIMITS },
|
|
49
|
-
network: { ...exports.DEFAULT_NETWORK_SECURITY },
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Standard check IDs per VERIFIER-SECURITY-MODEL.md (in order)
|
|
54
|
-
*/
|
|
55
|
-
exports.CHECK_IDS = [
|
|
56
|
-
'jws.parse',
|
|
57
|
-
'limits.receipt_bytes',
|
|
58
|
-
'jws.protected_header',
|
|
59
|
-
'claims.schema_unverified',
|
|
60
|
-
'issuer.trust_policy',
|
|
61
|
-
'issuer.discovery',
|
|
62
|
-
'key.resolve',
|
|
63
|
-
'jws.signature',
|
|
64
|
-
'claims.time_window',
|
|
65
|
-
'extensions.limits',
|
|
66
|
-
'transport.profile_binding',
|
|
67
|
-
];
|
|
68
|
-
/**
|
|
69
|
-
* Keys of artifacts that are non-deterministic (depend on runtime state)
|
|
70
|
-
*/
|
|
71
|
-
exports.NON_DETERMINISTIC_ARTIFACT_KEYS = [
|
|
72
|
-
'issuer_jwks_digest',
|
|
73
|
-
];
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
// Report Builder Utilities
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
/**
|
|
78
|
-
* Create a digest object from a hex string
|
|
79
|
-
*/
|
|
80
|
-
function createDigest(hexValue) {
|
|
81
|
-
return {
|
|
82
|
-
alg: 'sha-256',
|
|
83
|
-
value: hexValue.toLowerCase(),
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Create an empty verification report structure
|
|
88
|
-
*/
|
|
89
|
-
function createEmptyReport(policy) {
|
|
90
|
-
return {
|
|
91
|
-
report_version: kernel_1.VERIFICATION_REPORT_VERSION,
|
|
92
|
-
policy,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Map SSRF fetch error reason to verification reason code
|
|
97
|
-
*/
|
|
98
|
-
function ssrfErrorToReasonCode(ssrfReason, fetchType) {
|
|
99
|
-
const prefix = fetchType === 'key' ? 'key_fetch' : 'pointer_fetch';
|
|
100
|
-
switch (ssrfReason) {
|
|
101
|
-
case 'not_https':
|
|
102
|
-
case 'private_ip':
|
|
103
|
-
case 'loopback':
|
|
104
|
-
case 'link_local':
|
|
105
|
-
case 'cross_origin_redirect':
|
|
106
|
-
case 'dns_failure':
|
|
107
|
-
return `${prefix}_blocked`;
|
|
108
|
-
case 'timeout':
|
|
109
|
-
return `${prefix}_timeout`;
|
|
110
|
-
case 'response_too_large':
|
|
111
|
-
return fetchType === 'pointer' ? 'pointer_fetch_too_large' : 'jwks_too_large';
|
|
112
|
-
case 'jwks_too_many_keys':
|
|
113
|
-
return 'jwks_too_many_keys';
|
|
114
|
-
case 'too_many_redirects':
|
|
115
|
-
case 'scheme_downgrade':
|
|
116
|
-
case 'network_error':
|
|
117
|
-
case 'invalid_url':
|
|
118
|
-
default:
|
|
119
|
-
return `${prefix}_failed`;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Map reason code to severity
|
|
124
|
-
*/
|
|
125
|
-
function reasonCodeToSeverity(reason) {
|
|
126
|
-
if (reason === 'ok')
|
|
127
|
-
return 'info';
|
|
128
|
-
return 'error';
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Map reason code to error code
|
|
132
|
-
*/
|
|
133
|
-
function reasonCodeToErrorCode(reason) {
|
|
134
|
-
const mapping = {
|
|
135
|
-
ok: '',
|
|
136
|
-
receipt_too_large: 'E_VERIFY_RECEIPT_TOO_LARGE',
|
|
137
|
-
malformed_receipt: 'E_VERIFY_MALFORMED_RECEIPT',
|
|
138
|
-
signature_invalid: 'E_VERIFY_SIGNATURE_INVALID',
|
|
139
|
-
issuer_not_allowed: 'E_VERIFY_ISSUER_NOT_ALLOWED',
|
|
140
|
-
key_not_found: 'E_VERIFY_KEY_NOT_FOUND',
|
|
141
|
-
key_fetch_blocked: 'E_VERIFY_KEY_FETCH_BLOCKED',
|
|
142
|
-
key_fetch_failed: 'E_VERIFY_KEY_FETCH_FAILED',
|
|
143
|
-
key_fetch_timeout: 'E_VERIFY_KEY_FETCH_TIMEOUT',
|
|
144
|
-
pointer_fetch_blocked: 'E_VERIFY_POINTER_FETCH_BLOCKED',
|
|
145
|
-
pointer_fetch_failed: 'E_VERIFY_POINTER_FETCH_FAILED',
|
|
146
|
-
pointer_fetch_timeout: 'E_VERIFY_POINTER_FETCH_TIMEOUT',
|
|
147
|
-
pointer_fetch_too_large: 'E_VERIFY_POINTER_FETCH_TOO_LARGE',
|
|
148
|
-
pointer_digest_mismatch: 'E_VERIFY_POINTER_DIGEST_MISMATCH',
|
|
149
|
-
jwks_too_large: 'E_VERIFY_JWKS_TOO_LARGE',
|
|
150
|
-
jwks_too_many_keys: 'E_VERIFY_JWKS_TOO_MANY_KEYS',
|
|
151
|
-
expired: 'E_VERIFY_EXPIRED',
|
|
152
|
-
not_yet_valid: 'E_VERIFY_NOT_YET_VALID',
|
|
153
|
-
audience_mismatch: 'E_VERIFY_AUDIENCE_MISMATCH',
|
|
154
|
-
schema_invalid: 'E_VERIFY_SCHEMA_INVALID',
|
|
155
|
-
policy_violation: 'E_VERIFY_POLICY_VIOLATION',
|
|
156
|
-
extension_too_large: 'E_VERIFY_EXTENSION_TOO_LARGE',
|
|
157
|
-
invalid_transport: 'E_VERIFY_INVALID_TRANSPORT',
|
|
158
|
-
};
|
|
159
|
-
return mapping[reason] || 'E_VERIFY_POLICY_VIOLATION';
|
|
160
|
-
}
|
|
161
|
-
//# sourceMappingURL=verifier-types.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verifier-types.js","sourceRoot":"","sources":["../src/verifier-types.ts"],"names":[],"mappings":"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"}
|
package/dist/verify-local.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Local receipt verification with schema validation
|
|
4
|
-
*
|
|
5
|
-
* Use this for verifying receipts when you have the public key locally,
|
|
6
|
-
* without JWKS discovery.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.verifyLocal = verifyLocal;
|
|
10
|
-
exports.isCommerceResult = isCommerceResult;
|
|
11
|
-
exports.isAttestationResult = isAttestationResult;
|
|
12
|
-
const crypto_1 = require("@peac/crypto");
|
|
13
|
-
const schema_1 = require("@peac/schema");
|
|
14
|
-
/**
|
|
15
|
-
* Structural check for CryptoError
|
|
16
|
-
* More robust than instanceof across module boundaries (ESM/CJS, duplicate packages)
|
|
17
|
-
*/
|
|
18
|
-
function isCryptoError(err) {
|
|
19
|
-
return (err !== null &&
|
|
20
|
-
typeof err === 'object' &&
|
|
21
|
-
'name' in err &&
|
|
22
|
-
err.name === 'CryptoError' &&
|
|
23
|
-
'code' in err &&
|
|
24
|
-
typeof err.code === 'string' &&
|
|
25
|
-
err.code.startsWith('CRYPTO_') &&
|
|
26
|
-
'message' in err &&
|
|
27
|
-
typeof err.message === 'string');
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Crypto error codes that indicate format/validation issues
|
|
31
|
-
* These are CRYPTO_* internal codes from @peac/crypto, mapped to canonical E_* codes
|
|
32
|
-
*/
|
|
33
|
-
const FORMAT_ERROR_CODES = new Set([
|
|
34
|
-
'CRYPTO_INVALID_JWS_FORMAT',
|
|
35
|
-
'CRYPTO_INVALID_TYP',
|
|
36
|
-
'CRYPTO_INVALID_ALG',
|
|
37
|
-
'CRYPTO_INVALID_KEY_LENGTH',
|
|
38
|
-
]);
|
|
39
|
-
/** Max parse issues to include in details (prevents log bloat) */
|
|
40
|
-
const MAX_PARSE_ISSUES = 25;
|
|
41
|
-
/**
|
|
42
|
-
* Sanitize Zod issues into a bounded, stable structure.
|
|
43
|
-
* Avoids exposing raw Zod internals or unbounded arrays in the public API.
|
|
44
|
-
*/
|
|
45
|
-
function sanitizeParseIssues(issues) {
|
|
46
|
-
if (!Array.isArray(issues))
|
|
47
|
-
return undefined;
|
|
48
|
-
return issues.slice(0, MAX_PARSE_ISSUES).map((issue) => ({
|
|
49
|
-
path: Array.isArray(issue?.path) ? issue.path.join('.') : '',
|
|
50
|
-
message: typeof issue?.message === 'string' ? issue.message : String(issue),
|
|
51
|
-
}));
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Verify a PEAC receipt locally with a known public key
|
|
55
|
-
*
|
|
56
|
-
* This function:
|
|
57
|
-
* 1. Verifies the Ed25519 signature and header (typ, alg)
|
|
58
|
-
* 2. Validates the receipt schema with Zod
|
|
59
|
-
* 3. Checks issuer/audience/subject binding (if options provided)
|
|
60
|
-
* 4. Checks time validity (exp/iat with clock skew tolerance)
|
|
61
|
-
*
|
|
62
|
-
* Use this when you have the issuer's public key and don't need JWKS discovery.
|
|
63
|
-
* For JWKS-based verification, use `verifyReceipt()` instead.
|
|
64
|
-
*
|
|
65
|
-
* @param jws - JWS compact serialization
|
|
66
|
-
* @param publicKey - Ed25519 public key (32 bytes)
|
|
67
|
-
* @param options - Optional verification options (issuer, audience, subject, clock skew)
|
|
68
|
-
* @returns Typed verification result
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```typescript
|
|
72
|
-
* const result = await verifyLocal(jws, publicKey, {
|
|
73
|
-
* issuer: 'https://api.example.com',
|
|
74
|
-
* audience: 'https://client.example.com',
|
|
75
|
-
* subjectUri: 'https://api.example.com/inference/v1',
|
|
76
|
-
* });
|
|
77
|
-
* if (result.valid) {
|
|
78
|
-
* console.log('Issuer:', result.claims.iss);
|
|
79
|
-
* console.log('Amount:', result.claims.amt, result.claims.cur);
|
|
80
|
-
* console.log('Key ID:', result.kid);
|
|
81
|
-
* } else {
|
|
82
|
-
* console.error('Verification failed:', result.code, result.message);
|
|
83
|
-
* }
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
|
-
async function verifyLocal(jws, publicKey, options = {}) {
|
|
87
|
-
const { issuer, audience, subjectUri, rid, requireExp = false, maxClockSkew = 300 } = options;
|
|
88
|
-
const now = options.now ?? Math.floor(Date.now() / 1000);
|
|
89
|
-
try {
|
|
90
|
-
// 1. Verify signature and header (typ, alg validated by @peac/crypto)
|
|
91
|
-
const result = await (0, crypto_1.verify)(jws, publicKey);
|
|
92
|
-
if (!result.valid) {
|
|
93
|
-
return {
|
|
94
|
-
valid: false,
|
|
95
|
-
code: 'E_INVALID_SIGNATURE',
|
|
96
|
-
message: 'Ed25519 signature verification failed',
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
// 2. Validate schema (unified parser supports both commerce and attestation)
|
|
100
|
-
const pr = (0, schema_1.parseReceiptClaims)(result.payload);
|
|
101
|
-
if (!pr.ok) {
|
|
102
|
-
return {
|
|
103
|
-
valid: false,
|
|
104
|
-
code: 'E_INVALID_FORMAT',
|
|
105
|
-
message: `Receipt schema validation failed: ${pr.error.message}`,
|
|
106
|
-
details: { parse_code: pr.error.code, issues: sanitizeParseIssues(pr.error.issues) },
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
// Shared binding checks (iss, aud, rid, iat, exp exist on both receipt types)
|
|
110
|
-
// 3. Check issuer binding
|
|
111
|
-
if (issuer !== undefined && pr.claims.iss !== issuer) {
|
|
112
|
-
return {
|
|
113
|
-
valid: false,
|
|
114
|
-
code: 'E_INVALID_ISSUER',
|
|
115
|
-
message: `Issuer mismatch: expected "${issuer}", got "${pr.claims.iss}"`,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
// 4. Check audience binding
|
|
119
|
-
if (audience !== undefined && pr.claims.aud !== audience) {
|
|
120
|
-
return {
|
|
121
|
-
valid: false,
|
|
122
|
-
code: 'E_INVALID_AUDIENCE',
|
|
123
|
-
message: `Audience mismatch: expected "${audience}", got "${pr.claims.aud}"`,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
// 5. Check receipt ID binding
|
|
127
|
-
if (rid !== undefined && pr.claims.rid !== rid) {
|
|
128
|
-
return {
|
|
129
|
-
valid: false,
|
|
130
|
-
code: 'E_INVALID_RECEIPT_ID',
|
|
131
|
-
message: `Receipt ID mismatch: expected "${rid}", got "${pr.claims.rid}"`,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
// 6. Check requireExp
|
|
135
|
-
if (requireExp && pr.claims.exp === undefined) {
|
|
136
|
-
return {
|
|
137
|
-
valid: false,
|
|
138
|
-
code: 'E_MISSING_EXP',
|
|
139
|
-
message: 'Receipt missing required exp claim',
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
// 7. Check not-yet-valid (iat with clock skew)
|
|
143
|
-
if (pr.claims.iat > now + maxClockSkew) {
|
|
144
|
-
return {
|
|
145
|
-
valid: false,
|
|
146
|
-
code: 'E_NOT_YET_VALID',
|
|
147
|
-
message: `Receipt not yet valid: issued at ${new Date(pr.claims.iat * 1000).toISOString()}, now is ${new Date(now * 1000).toISOString()}`,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
// 8. Check expiry (with clock skew tolerance)
|
|
151
|
-
if (pr.claims.exp !== undefined && pr.claims.exp < now - maxClockSkew) {
|
|
152
|
-
return {
|
|
153
|
-
valid: false,
|
|
154
|
-
code: 'E_EXPIRED',
|
|
155
|
-
message: `Receipt expired at ${new Date(pr.claims.exp * 1000).toISOString()}`,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
// 9. Subject binding + typed return (variant-branched, no unsafe casts)
|
|
159
|
-
if (pr.variant === 'commerce') {
|
|
160
|
-
const claims = pr.claims;
|
|
161
|
-
if (subjectUri !== undefined && claims.subject?.uri !== subjectUri) {
|
|
162
|
-
return {
|
|
163
|
-
valid: false,
|
|
164
|
-
code: 'E_INVALID_SUBJECT',
|
|
165
|
-
message: `Subject mismatch: expected "${subjectUri}", got "${claims.subject?.uri ?? 'undefined'}"`,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
return { valid: true, variant: 'commerce', claims, kid: result.header.kid };
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
const claims = pr.claims;
|
|
172
|
-
if (subjectUri !== undefined && claims.sub !== subjectUri) {
|
|
173
|
-
return {
|
|
174
|
-
valid: false,
|
|
175
|
-
code: 'E_INVALID_SUBJECT',
|
|
176
|
-
message: `Subject mismatch: expected "${subjectUri}", got "${claims.sub ?? 'undefined'}"`,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
return { valid: true, variant: 'attestation', claims, kid: result.header.kid };
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
catch (err) {
|
|
183
|
-
// Handle typed CryptoError from @peac/crypto
|
|
184
|
-
// Use structural check instead of instanceof for robustness across ESM/CJS boundaries
|
|
185
|
-
// Map internal CRYPTO_* codes to canonical E_* codes
|
|
186
|
-
if (isCryptoError(err)) {
|
|
187
|
-
if (FORMAT_ERROR_CODES.has(err.code)) {
|
|
188
|
-
return {
|
|
189
|
-
valid: false,
|
|
190
|
-
code: 'E_INVALID_FORMAT',
|
|
191
|
-
message: err.message,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
if (err.code === 'CRYPTO_INVALID_SIGNATURE') {
|
|
195
|
-
return {
|
|
196
|
-
valid: false,
|
|
197
|
-
code: 'E_INVALID_SIGNATURE',
|
|
198
|
-
message: err.message,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// All other errors (JSON parse, unexpected) -> E_INTERNAL
|
|
203
|
-
// No message parsing - code-based mapping only
|
|
204
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
205
|
-
return {
|
|
206
|
-
valid: false,
|
|
207
|
-
code: 'E_INTERNAL',
|
|
208
|
-
message: `Unexpected verification error: ${message}`,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Type guard: narrows a VerifyLocalResult to a commerce success.
|
|
214
|
-
*
|
|
215
|
-
* Use instead of manual `result.valid && result.variant === 'commerce'` checks
|
|
216
|
-
* to get proper claims narrowing to ReceiptClaimsType.
|
|
217
|
-
*/
|
|
218
|
-
function isCommerceResult(r) {
|
|
219
|
-
return r.valid === true && r.variant === 'commerce';
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Type guard: narrows a VerifyLocalResult to an attestation success.
|
|
223
|
-
*
|
|
224
|
-
* Use instead of manual `result.valid && result.variant === 'attestation'` checks
|
|
225
|
-
* to get proper claims narrowing to AttestationReceiptClaims.
|
|
226
|
-
*/
|
|
227
|
-
function isAttestationResult(r) {
|
|
228
|
-
return r.valid === true && r.variant === 'attestation';
|
|
229
|
-
}
|
|
230
|
-
//# sourceMappingURL=verify-local.js.map
|
package/dist/verify-local.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verify-local.js","sourceRoot":"","sources":["../src/verify-local.ts"],"names":[],"mappings":"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"}
|
package/dist/verify.js
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Receipt verification with JWKS fetching and caching
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.verifyReceipt = verifyReceipt;
|
|
7
|
-
const crypto_1 = require("@peac/crypto");
|
|
8
|
-
const schema_1 = require("@peac/schema");
|
|
9
|
-
const telemetry_js_1 = require("./telemetry.js");
|
|
10
|
-
/**
|
|
11
|
-
* In-memory JWKS cache
|
|
12
|
-
* Maps issuer URL to { keys, expiresAt }
|
|
13
|
-
*/
|
|
14
|
-
const jwksCache = new Map();
|
|
15
|
-
/**
|
|
16
|
-
* Cache TTL (5 minutes)
|
|
17
|
-
*/
|
|
18
|
-
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
19
|
-
/**
|
|
20
|
-
* Fetch JWKS from issuer (SSRF-safe)
|
|
21
|
-
*/
|
|
22
|
-
async function fetchJWKS(issuerUrl) {
|
|
23
|
-
// SSRF protection: only allow https://
|
|
24
|
-
if (!issuerUrl.startsWith('https://')) {
|
|
25
|
-
throw new Error('Issuer URL must be https://');
|
|
26
|
-
}
|
|
27
|
-
// Construct JWKS URL from discovery
|
|
28
|
-
const discoveryUrl = `${issuerUrl}/.well-known/peac.txt`;
|
|
29
|
-
try {
|
|
30
|
-
const discoveryResp = await fetch(discoveryUrl, {
|
|
31
|
-
headers: { Accept: 'text/plain' },
|
|
32
|
-
// Timeout after 5 seconds
|
|
33
|
-
signal: AbortSignal.timeout(5000),
|
|
34
|
-
});
|
|
35
|
-
if (!discoveryResp.ok) {
|
|
36
|
-
throw new Error(`Discovery fetch failed: ${discoveryResp.status}`);
|
|
37
|
-
}
|
|
38
|
-
const discoveryText = await discoveryResp.text();
|
|
39
|
-
// Parse YAML-like discovery (simple key: value parsing)
|
|
40
|
-
const jwksLine = discoveryText.split('\n').find((line) => line.startsWith('jwks:'));
|
|
41
|
-
if (!jwksLine) {
|
|
42
|
-
throw new Error('No jwks field in discovery');
|
|
43
|
-
}
|
|
44
|
-
const jwksUrl = jwksLine.replace('jwks:', '').trim();
|
|
45
|
-
// SSRF protection: verify JWKS URL is also https://
|
|
46
|
-
if (!jwksUrl.startsWith('https://')) {
|
|
47
|
-
throw new Error('JWKS URL must be https://');
|
|
48
|
-
}
|
|
49
|
-
// Fetch JWKS
|
|
50
|
-
const jwksResp = await fetch(jwksUrl, {
|
|
51
|
-
headers: { Accept: 'application/json' },
|
|
52
|
-
signal: AbortSignal.timeout(5000),
|
|
53
|
-
});
|
|
54
|
-
if (!jwksResp.ok) {
|
|
55
|
-
throw new Error(`JWKS fetch failed: ${jwksResp.status}`);
|
|
56
|
-
}
|
|
57
|
-
const jwks = (await jwksResp.json());
|
|
58
|
-
return jwks;
|
|
59
|
-
}
|
|
60
|
-
catch (err) {
|
|
61
|
-
throw new Error(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Get JWKS (from cache or fetch)
|
|
66
|
-
*/
|
|
67
|
-
async function getJWKS(issuerUrl) {
|
|
68
|
-
const now = Date.now();
|
|
69
|
-
// Check cache
|
|
70
|
-
const cached = jwksCache.get(issuerUrl);
|
|
71
|
-
if (cached && cached.expiresAt > now) {
|
|
72
|
-
return { jwks: cached.keys, fromCache: true };
|
|
73
|
-
}
|
|
74
|
-
// Fetch fresh JWKS
|
|
75
|
-
const jwks = await fetchJWKS(issuerUrl);
|
|
76
|
-
// Cache it
|
|
77
|
-
jwksCache.set(issuerUrl, {
|
|
78
|
-
keys: jwks,
|
|
79
|
-
expiresAt: now + CACHE_TTL_MS,
|
|
80
|
-
});
|
|
81
|
-
return { jwks, fromCache: false };
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Convert JWK x coordinate to Ed25519 public key
|
|
85
|
-
*/
|
|
86
|
-
function jwkToPublicKey(jwk) {
|
|
87
|
-
if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {
|
|
88
|
-
throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported');
|
|
89
|
-
}
|
|
90
|
-
// Decode base64url x coordinate
|
|
91
|
-
const xBytes = Buffer.from(jwk.x, 'base64url');
|
|
92
|
-
if (xBytes.length !== 32) {
|
|
93
|
-
throw new Error('Ed25519 public key must be 32 bytes');
|
|
94
|
-
}
|
|
95
|
-
return new Uint8Array(xBytes);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Verify a PEAC receipt JWS
|
|
99
|
-
*
|
|
100
|
-
* @param optionsOrJws - Verify options or JWS compact serialization (for backwards compatibility)
|
|
101
|
-
* @returns Verification result or failure
|
|
102
|
-
*/
|
|
103
|
-
async function verifyReceipt(optionsOrJws) {
|
|
104
|
-
// Support both old (string) and new (options) signatures for backwards compatibility
|
|
105
|
-
const receiptJws = typeof optionsOrJws === 'string' ? optionsOrJws : optionsOrJws.receiptJws;
|
|
106
|
-
const inputSnapshot = typeof optionsOrJws === 'string' ? undefined : optionsOrJws.subject_snapshot;
|
|
107
|
-
const telemetry = typeof optionsOrJws === 'string' ? undefined : optionsOrJws.telemetry;
|
|
108
|
-
const startTime = performance.now();
|
|
109
|
-
let jwksFetchTime;
|
|
110
|
-
try {
|
|
111
|
-
// Decode JWS to get issuer
|
|
112
|
-
const { header, payload } = (0, crypto_1.decode)(receiptJws);
|
|
113
|
-
// Validate claims structure
|
|
114
|
-
schema_1.ReceiptClaims.parse(payload);
|
|
115
|
-
// Check expiry
|
|
116
|
-
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
|
|
117
|
-
const durationMs = performance.now() - startTime;
|
|
118
|
-
(0, telemetry_js_1.fireTelemetryHook)(telemetry?.onReceiptVerified, {
|
|
119
|
-
receiptHash: (0, telemetry_js_1.hashReceipt)(receiptJws),
|
|
120
|
-
valid: false,
|
|
121
|
-
reasonCode: 'expired',
|
|
122
|
-
issuer: payload.iss,
|
|
123
|
-
kid: header.kid,
|
|
124
|
-
durationMs,
|
|
125
|
-
});
|
|
126
|
-
return {
|
|
127
|
-
ok: false,
|
|
128
|
-
reason: 'expired',
|
|
129
|
-
details: `Receipt expired at ${new Date(payload.exp * 1000).toISOString()}`,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
// Fetch JWKS
|
|
133
|
-
const jwksFetchStart = performance.now();
|
|
134
|
-
const { jwks, fromCache } = await getJWKS(payload.iss);
|
|
135
|
-
if (!fromCache) {
|
|
136
|
-
jwksFetchTime = performance.now() - jwksFetchStart;
|
|
137
|
-
}
|
|
138
|
-
// Find key by kid
|
|
139
|
-
const jwk = jwks.keys.find((k) => k.kid === header.kid);
|
|
140
|
-
if (!jwk) {
|
|
141
|
-
const durationMs = performance.now() - startTime;
|
|
142
|
-
(0, telemetry_js_1.fireTelemetryHook)(telemetry?.onReceiptVerified, {
|
|
143
|
-
receiptHash: (0, telemetry_js_1.hashReceipt)(receiptJws),
|
|
144
|
-
valid: false,
|
|
145
|
-
reasonCode: 'unknown_key',
|
|
146
|
-
issuer: payload.iss,
|
|
147
|
-
kid: header.kid,
|
|
148
|
-
durationMs,
|
|
149
|
-
});
|
|
150
|
-
return {
|
|
151
|
-
ok: false,
|
|
152
|
-
reason: 'unknown_key',
|
|
153
|
-
details: `No key found with kid=${header.kid}`,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
// Convert JWK to public key
|
|
157
|
-
const publicKey = jwkToPublicKey(jwk);
|
|
158
|
-
// Verify signature
|
|
159
|
-
const result = await (0, crypto_1.verify)(receiptJws, publicKey);
|
|
160
|
-
if (!result.valid) {
|
|
161
|
-
const durationMs = performance.now() - startTime;
|
|
162
|
-
(0, telemetry_js_1.fireTelemetryHook)(telemetry?.onReceiptVerified, {
|
|
163
|
-
receiptHash: (0, telemetry_js_1.hashReceipt)(receiptJws),
|
|
164
|
-
valid: false,
|
|
165
|
-
reasonCode: 'invalid_signature',
|
|
166
|
-
issuer: payload.iss,
|
|
167
|
-
kid: header.kid,
|
|
168
|
-
durationMs,
|
|
169
|
-
});
|
|
170
|
-
return {
|
|
171
|
-
ok: false,
|
|
172
|
-
reason: 'invalid_signature',
|
|
173
|
-
details: 'Ed25519 signature verification failed',
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
// Validate subject_snapshot if provided (v0.9.17+)
|
|
177
|
-
// This validates schema and logs advisory PII warning if applicable
|
|
178
|
-
const validatedSnapshot = (0, schema_1.validateSubjectSnapshot)(inputSnapshot);
|
|
179
|
-
const verifyTime = performance.now() - startTime;
|
|
180
|
-
// Emit success telemetry (fire-and-forget, guarded)
|
|
181
|
-
(0, telemetry_js_1.fireTelemetryHook)(telemetry?.onReceiptVerified, {
|
|
182
|
-
receiptHash: (0, telemetry_js_1.hashReceipt)(receiptJws),
|
|
183
|
-
valid: true,
|
|
184
|
-
issuer: payload.iss,
|
|
185
|
-
kid: header.kid,
|
|
186
|
-
durationMs: verifyTime,
|
|
187
|
-
});
|
|
188
|
-
return {
|
|
189
|
-
ok: true,
|
|
190
|
-
claims: payload,
|
|
191
|
-
...(validatedSnapshot && { subject_snapshot: validatedSnapshot }),
|
|
192
|
-
perf: {
|
|
193
|
-
verify_ms: verifyTime,
|
|
194
|
-
...(jwksFetchTime && { jwks_fetch_ms: jwksFetchTime }),
|
|
195
|
-
},
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
catch (err) {
|
|
199
|
-
const durationMs = performance.now() - startTime;
|
|
200
|
-
(0, telemetry_js_1.fireTelemetryHook)(telemetry?.onReceiptVerified, {
|
|
201
|
-
receiptHash: (0, telemetry_js_1.hashReceipt)(receiptJws),
|
|
202
|
-
valid: false,
|
|
203
|
-
reasonCode: 'verification_error',
|
|
204
|
-
durationMs,
|
|
205
|
-
});
|
|
206
|
-
return {
|
|
207
|
-
ok: false,
|
|
208
|
-
reason: 'verification_error',
|
|
209
|
-
details: err instanceof Error ? err.message : String(err),
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
//# sourceMappingURL=verify.js.map
|
package/dist/verify.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
|