@peac/mappings-ucp 0.9.31
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 +190 -0
- package/README.md +167 -0
- package/dist/bundle.d.ts +102 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +156 -0
- package/dist/bundle.js.map +1 -0
- package/dist/errors.d.ts +51 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +97 -0
- package/dist/errors.js.map +1 -0
- package/dist/evidence.d.ts +69 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +249 -0
- package/dist/evidence.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/mapper.d.ts +48 -0
- package/dist/mapper.d.ts.map +1 -0
- package/dist/mapper.js +165 -0
- package/dist/mapper.js.map +1 -0
- package/dist/types.d.ts +360 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +26 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +103 -0
- package/dist/util.js.map +1 -0
- package/dist/verify.d.ts +40 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +406 -0
- package/dist/verify.js.map +1 -0
- package/package.json +52 -0
package/dist/util.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @peac/mappings-ucp - Shared utilities
|
|
3
|
+
*
|
|
4
|
+
* Crypto and encoding helpers used by both verification and evidence generation.
|
|
5
|
+
* Extracted to prevent architectural coupling between modules.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { ErrorCodes, ucpError } from './errors.js';
|
|
9
|
+
/**
|
|
10
|
+
* SHA-256 hash as hex string.
|
|
11
|
+
*/
|
|
12
|
+
export function sha256Hex(data) {
|
|
13
|
+
return createHash('sha256').update(data).digest('hex');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* SHA-256 hash as Uint8Array.
|
|
17
|
+
*/
|
|
18
|
+
export function sha256Bytes(data) {
|
|
19
|
+
return new Uint8Array(createHash('sha256').update(data).digest());
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convert base64 to base64url by replacing chars.
|
|
23
|
+
*/
|
|
24
|
+
function base64ToBase64url(base64) {
|
|
25
|
+
let result = '';
|
|
26
|
+
for (let i = 0; i < base64.length; i++) {
|
|
27
|
+
const c = base64.charCodeAt(i);
|
|
28
|
+
if (c === 43) {
|
|
29
|
+
// '+' -> '-'
|
|
30
|
+
result += '-';
|
|
31
|
+
}
|
|
32
|
+
else if (c === 47) {
|
|
33
|
+
// '/' -> '_'
|
|
34
|
+
result += '_';
|
|
35
|
+
}
|
|
36
|
+
else if (c === 61) {
|
|
37
|
+
// '=' padding - skip
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
result += base64.charAt(i);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
// Maximum size for base64url encoding (16MB - reasonable limit for webhook payloads)
|
|
47
|
+
const MAX_ENCODE_SIZE = 16 * 1024 * 1024;
|
|
48
|
+
/**
|
|
49
|
+
* Base64url encode without padding.
|
|
50
|
+
* Uses standard base64 + manual conversion for maximum portability.
|
|
51
|
+
* Works with Node.js 12+ (does not require 'base64url' encoding from Node 15.7.0+).
|
|
52
|
+
*/
|
|
53
|
+
export function base64urlEncode(data) {
|
|
54
|
+
// Safety check for unreasonably large input
|
|
55
|
+
if (data.length > MAX_ENCODE_SIZE) {
|
|
56
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `Input too large for base64url encoding: ${data.length} bytes`);
|
|
57
|
+
}
|
|
58
|
+
// Use Buffer in Node.js, then convert base64 to base64url
|
|
59
|
+
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
|
|
60
|
+
const base64 = Buffer.from(data).toString('base64');
|
|
61
|
+
return base64ToBase64url(base64);
|
|
62
|
+
}
|
|
63
|
+
// Fallback for non-Node environments (browsers)
|
|
64
|
+
// Build binary string with bounded iteration
|
|
65
|
+
const len = data.length;
|
|
66
|
+
let binary = '';
|
|
67
|
+
for (let i = 0; i < len; i++) {
|
|
68
|
+
binary += String.fromCharCode(data[i]);
|
|
69
|
+
}
|
|
70
|
+
const base64 = btoa(binary);
|
|
71
|
+
return base64ToBase64url(base64);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* JCS canonicalize (RFC 8785) - synchronous.
|
|
75
|
+
* Produces deterministic JSON with sorted keys and no whitespace.
|
|
76
|
+
*/
|
|
77
|
+
export function jcsCanonicalizeSync(obj) {
|
|
78
|
+
if (obj === null)
|
|
79
|
+
return 'null';
|
|
80
|
+
if (typeof obj === 'boolean')
|
|
81
|
+
return obj ? 'true' : 'false';
|
|
82
|
+
if (typeof obj === 'number') {
|
|
83
|
+
if (!Number.isFinite(obj)) {
|
|
84
|
+
throw ucpError(ErrorCodes.EVIDENCE_SERIALIZATION_FAILED, 'JCS does not support NaN or Infinity');
|
|
85
|
+
}
|
|
86
|
+
// Use ES6 number serialization (matches JSON.stringify for finite numbers)
|
|
87
|
+
return Object.is(obj, -0) ? '0' : String(obj);
|
|
88
|
+
}
|
|
89
|
+
if (typeof obj === 'string')
|
|
90
|
+
return JSON.stringify(obj);
|
|
91
|
+
if (Array.isArray(obj)) {
|
|
92
|
+
return '[' + obj.map(jcsCanonicalizeSync).join(',') + ']';
|
|
93
|
+
}
|
|
94
|
+
if (typeof obj === 'object') {
|
|
95
|
+
const keys = Object.keys(obj).sort();
|
|
96
|
+
const pairs = keys
|
|
97
|
+
.filter((k) => obj[k] !== undefined)
|
|
98
|
+
.map((k) => JSON.stringify(k) + ':' + jcsCanonicalizeSync(obj[k]));
|
|
99
|
+
return '{' + pairs.join(',') + '}';
|
|
100
|
+
}
|
|
101
|
+
throw ucpError(ErrorCodes.EVIDENCE_SERIALIZATION_FAILED, `Cannot canonicalize type: ${typeof obj}`);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @peac/mappings-ucp - Webhook signature verification
|
|
3
|
+
*
|
|
4
|
+
* Verifies UCP webhook signatures using detached JWS (RFC 7797).
|
|
5
|
+
* Supports ES256, ES384, ES512 algorithms with ECDSA keys.
|
|
6
|
+
*
|
|
7
|
+
* Verification strategy (raw-first, JCS fallback):
|
|
8
|
+
* 1. Try verifying against raw request body bytes
|
|
9
|
+
* 2. If that fails AND body is valid JSON, try JCS-canonicalized body
|
|
10
|
+
* 3. Record all attempts for evidence
|
|
11
|
+
*
|
|
12
|
+
* The UCP spec is ambiguous on canonicalization for webhooks (unlike AP2 which
|
|
13
|
+
* explicitly requires JCS). This implementation tries raw bytes first because
|
|
14
|
+
* the spec says "detached JWT over the request body".
|
|
15
|
+
*/
|
|
16
|
+
import type { UcpProfile, UcpSigningKey, ParsedDetachedJws, VerifyUcpWebhookOptions, VerifyUcpWebhookResult } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Parse a detached JWS from the Request-Signature header.
|
|
19
|
+
* Format: <protected>..<signature> (empty payload section)
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseDetachedJws(headerValue: string): ParsedDetachedJws;
|
|
22
|
+
/**
|
|
23
|
+
* Find the signing key by kid in the profile.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findSigningKey(profile: UcpProfile, kid: string): UcpSigningKey | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Verify a UCP webhook signature.
|
|
28
|
+
*
|
|
29
|
+
* Strategy: raw-first, JCS fallback
|
|
30
|
+
* 1. Parse the detached JWS from Request-Signature header
|
|
31
|
+
* 2. Find the signing key in the profile by kid
|
|
32
|
+
* 3. Try verifying against raw body bytes
|
|
33
|
+
* 4. If that fails and body is valid JSON, try JCS-canonicalized body
|
|
34
|
+
* 5. Return result with all verification attempts
|
|
35
|
+
*
|
|
36
|
+
* @param options - Verification options
|
|
37
|
+
* @returns Verification result with profile and key information for evidence capture
|
|
38
|
+
*/
|
|
39
|
+
export declare function verifyUcpWebhookSignature(options: VerifyUcpWebhookOptions): Promise<VerifyUcpWebhookResult>;
|
|
40
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EAEb,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EAGvB,MAAM,YAAY,CAAC;AAmBpB;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAiIvE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE1F;AA4ED;;;;;;;;;;;;GAYG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CAkKjC"}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @peac/mappings-ucp - Webhook signature verification
|
|
3
|
+
*
|
|
4
|
+
* Verifies UCP webhook signatures using detached JWS (RFC 7797).
|
|
5
|
+
* Supports ES256, ES384, ES512 algorithms with ECDSA keys.
|
|
6
|
+
*
|
|
7
|
+
* Verification strategy (raw-first, JCS fallback):
|
|
8
|
+
* 1. Try verifying against raw request body bytes
|
|
9
|
+
* 2. If that fails AND body is valid JSON, try JCS-canonicalized body
|
|
10
|
+
* 3. Record all attempts for evidence
|
|
11
|
+
*
|
|
12
|
+
* The UCP spec is ambiguous on canonicalization for webhooks (unlike AP2 which
|
|
13
|
+
* explicitly requires JCS). This implementation tries raw bytes first because
|
|
14
|
+
* the spec says "detached JWT over the request body".
|
|
15
|
+
*/
|
|
16
|
+
import * as jose from 'jose';
|
|
17
|
+
import { ErrorCodes, ucpError, UcpError } from './errors.js';
|
|
18
|
+
import { jcsCanonicalizeSync, base64urlEncode } from './util.js';
|
|
19
|
+
/**
|
|
20
|
+
* Supported ECDSA algorithms and their curve mappings.
|
|
21
|
+
*/
|
|
22
|
+
const ALG_TO_CURVE = {
|
|
23
|
+
ES256: 'P-256',
|
|
24
|
+
ES384: 'P-384',
|
|
25
|
+
ES512: 'P-521',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Known/understood critical header parameters.
|
|
29
|
+
* Per JOSE rules: if `crit` exists, every entry MUST be understood by the implementation.
|
|
30
|
+
*/
|
|
31
|
+
const UNDERSTOOD_CRIT_PARAMS = new Set(['b64']);
|
|
32
|
+
/**
|
|
33
|
+
* Parse a detached JWS from the Request-Signature header.
|
|
34
|
+
* Format: <protected>..<signature> (empty payload section)
|
|
35
|
+
*/
|
|
36
|
+
export function parseDetachedJws(headerValue) {
|
|
37
|
+
const trimmed = headerValue.trim();
|
|
38
|
+
// Detached JWS format: header..signature (double dot, empty payload)
|
|
39
|
+
const parts = trimmed.split('.');
|
|
40
|
+
if (parts.length !== 3) {
|
|
41
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Invalid detached JWS format, expected 3 parts', {
|
|
42
|
+
parts: parts.length,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const [protectedB64url, payloadPart, signatureB64url] = parts;
|
|
46
|
+
// Payload MUST be empty for detached JWS
|
|
47
|
+
if (payloadPart !== '') {
|
|
48
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Detached JWS must have empty payload section', {
|
|
49
|
+
payloadLength: payloadPart.length,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Decode protected header
|
|
53
|
+
let header;
|
|
54
|
+
try {
|
|
55
|
+
const headerJson = base64urlDecode(protectedB64url);
|
|
56
|
+
header = JSON.parse(new TextDecoder('utf-8').decode(headerJson));
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Failed to decode protected header', {
|
|
60
|
+
error: String(err),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// Validate algorithm
|
|
64
|
+
if (!header.alg || !['ES256', 'ES384', 'ES512'].includes(header.alg)) {
|
|
65
|
+
throw ucpError(ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED, `Unsupported algorithm: ${header.alg}`, {
|
|
66
|
+
alg: header.alg,
|
|
67
|
+
supported: ['ES256', 'ES384', 'ES512'],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Validate kid
|
|
71
|
+
if (!header.kid) {
|
|
72
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Missing kid in protected header');
|
|
73
|
+
}
|
|
74
|
+
// Validate b64 parameter type if present (must be boolean)
|
|
75
|
+
if ('b64' in header && typeof header.b64 !== 'boolean') {
|
|
76
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `b64 header must be a boolean, got ${typeof header.b64}`, { b64_type: typeof header.b64 });
|
|
77
|
+
}
|
|
78
|
+
// Validate crit parameter if present
|
|
79
|
+
if ('crit' in header) {
|
|
80
|
+
// crit must be an array
|
|
81
|
+
if (!Array.isArray(header.crit)) {
|
|
82
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `crit header must be an array, got ${typeof header.crit}`, { crit_type: typeof header.crit });
|
|
83
|
+
}
|
|
84
|
+
// crit must contain only strings
|
|
85
|
+
const nonStrings = header.crit.filter((param) => typeof param !== 'string');
|
|
86
|
+
if (nonStrings.length > 0) {
|
|
87
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'crit array must contain only strings', {
|
|
88
|
+
invalid_types: nonStrings.map((p) => typeof p),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// crit must not contain duplicates
|
|
92
|
+
const seen = new Set();
|
|
93
|
+
const duplicates = [];
|
|
94
|
+
for (const param of header.crit) {
|
|
95
|
+
if (seen.has(param)) {
|
|
96
|
+
duplicates.push(param);
|
|
97
|
+
}
|
|
98
|
+
seen.add(param);
|
|
99
|
+
}
|
|
100
|
+
if (duplicates.length > 0) {
|
|
101
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `crit array contains duplicates: ${duplicates.join(', ')}`, { duplicates });
|
|
102
|
+
}
|
|
103
|
+
// Enforce JOSE crit semantics: reject unknown critical parameters
|
|
104
|
+
const unknownCrit = header.crit.filter((param) => !UNDERSTOOD_CRIT_PARAMS.has(param));
|
|
105
|
+
if (unknownCrit.length > 0) {
|
|
106
|
+
throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `Unknown critical header parameters: ${unknownCrit.join(', ')}`, { unknown_crit: unknownCrit, understood: [...UNDERSTOOD_CRIT_PARAMS] });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Handle b64 parameter
|
|
110
|
+
const isUnencodedPayload = header.b64 === false;
|
|
111
|
+
// If b64=false is used, it MUST be in crit (RFC 7797 requirement)
|
|
112
|
+
if (isUnencodedPayload) {
|
|
113
|
+
if (!header.crit || !header.crit.includes('b64')) {
|
|
114
|
+
throw ucpError(ErrorCodes.SIGNATURE_B64_INVALID, 'When b64=false, "b64" must be in the "crit" array');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
raw_header_value: trimmed,
|
|
119
|
+
header: header,
|
|
120
|
+
protected_b64url: protectedB64url,
|
|
121
|
+
signature_b64url: signatureB64url,
|
|
122
|
+
is_unencoded_payload: isUnencodedPayload,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Find the signing key by kid in the profile.
|
|
127
|
+
*/
|
|
128
|
+
export function findSigningKey(profile, kid) {
|
|
129
|
+
return profile.signing_keys.find((k) => k.kid === kid);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Verify that a key's curve matches the algorithm.
|
|
133
|
+
*/
|
|
134
|
+
function validateKeyForAlgorithm(key, alg) {
|
|
135
|
+
const expectedCurve = ALG_TO_CURVE[alg];
|
|
136
|
+
if (key.kty !== 'EC') {
|
|
137
|
+
throw ucpError(ErrorCodes.KEY_ALGORITHM_MISMATCH, `Expected EC key, got ${key.kty}`, {
|
|
138
|
+
kid: key.kid,
|
|
139
|
+
kty: key.kty,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (key.crv !== expectedCurve) {
|
|
143
|
+
throw ucpError(ErrorCodes.KEY_CURVE_MISMATCH, `Algorithm ${alg} requires curve ${expectedCurve}, got ${key.crv}`, { kid: key.kid, alg, expected: expectedCurve, got: key.crv });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Verify a detached JWS signature against payload bytes.
|
|
148
|
+
*
|
|
149
|
+
* For b64=false (RFC 7797 unencoded payload):
|
|
150
|
+
* - jose.flattenedVerify accepts Uint8Array directly as payload
|
|
151
|
+
* - The signing input is: ASCII(BASE64URL(header)) || '.' || payload_bytes
|
|
152
|
+
*
|
|
153
|
+
* For standard JWS (b64=true or absent):
|
|
154
|
+
* - Payload is base64url-encoded in the signing input
|
|
155
|
+
*
|
|
156
|
+
* @param parsed - Parsed detached JWS
|
|
157
|
+
* @param payloadBytes - The payload bytes to verify against
|
|
158
|
+
* @param key - The EC public key (JWK)
|
|
159
|
+
* @returns true if signature is valid
|
|
160
|
+
*/
|
|
161
|
+
async function verifyDetachedSignature(parsed, payloadBytes, key) {
|
|
162
|
+
// Import the public key
|
|
163
|
+
const publicKey = await jose.importJWK({
|
|
164
|
+
kty: key.kty,
|
|
165
|
+
crv: key.crv,
|
|
166
|
+
x: key.x,
|
|
167
|
+
y: key.y,
|
|
168
|
+
}, parsed.header.alg);
|
|
169
|
+
// Construct the JWS input for verification
|
|
170
|
+
// jose.flattenedVerify handles b64=false correctly when payload is Uint8Array
|
|
171
|
+
const jws = {
|
|
172
|
+
protected: parsed.protected_b64url,
|
|
173
|
+
// For b64=false: pass raw bytes directly (jose handles RFC 7797 semantics)
|
|
174
|
+
// For standard JWS: pass base64url-encoded string
|
|
175
|
+
payload: parsed.is_unencoded_payload ? payloadBytes : base64urlEncode(payloadBytes),
|
|
176
|
+
signature: parsed.signature_b64url,
|
|
177
|
+
};
|
|
178
|
+
try {
|
|
179
|
+
// jose.flattenedVerify handles b64=false correctly when it sees it in the protected header
|
|
180
|
+
await jose.flattenedVerify(jws, publicKey, {
|
|
181
|
+
algorithms: [parsed.header.alg],
|
|
182
|
+
});
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Verify a UCP webhook signature.
|
|
191
|
+
*
|
|
192
|
+
* Strategy: raw-first, JCS fallback
|
|
193
|
+
* 1. Parse the detached JWS from Request-Signature header
|
|
194
|
+
* 2. Find the signing key in the profile by kid
|
|
195
|
+
* 3. Try verifying against raw body bytes
|
|
196
|
+
* 4. If that fails and body is valid JSON, try JCS-canonicalized body
|
|
197
|
+
* 5. Return result with all verification attempts
|
|
198
|
+
*
|
|
199
|
+
* @param options - Verification options
|
|
200
|
+
* @returns Verification result with profile and key information for evidence capture
|
|
201
|
+
*/
|
|
202
|
+
export async function verifyUcpWebhookSignature(options) {
|
|
203
|
+
const { signature_header, body_bytes, profile_url, profile: providedProfile } = options;
|
|
204
|
+
// Track verification attempts
|
|
205
|
+
const attempts = [];
|
|
206
|
+
// Parse the detached JWS
|
|
207
|
+
let parsed;
|
|
208
|
+
try {
|
|
209
|
+
parsed = parseDetachedJws(signature_header);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
if (err instanceof UcpError) {
|
|
213
|
+
return {
|
|
214
|
+
valid: false,
|
|
215
|
+
header: { alg: 'ES256', kid: '' },
|
|
216
|
+
attempts,
|
|
217
|
+
error_code: err.code,
|
|
218
|
+
error_message: err.message,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
// Get the profile (use provided or fetch)
|
|
224
|
+
let profile;
|
|
225
|
+
let profileRaw;
|
|
226
|
+
if (providedProfile) {
|
|
227
|
+
profile = providedProfile;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
try {
|
|
231
|
+
const fetchResult = await fetchUcpProfile(profile_url);
|
|
232
|
+
profile = fetchResult.profile;
|
|
233
|
+
profileRaw = fetchResult.raw;
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
return {
|
|
237
|
+
valid: false,
|
|
238
|
+
header: parsed.header,
|
|
239
|
+
attempts,
|
|
240
|
+
error_code: ErrorCodes.PROFILE_FETCH_FAILED,
|
|
241
|
+
error_message: `Failed to fetch UCP profile: ${err}`,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Find the signing key
|
|
246
|
+
const key = findSigningKey(profile, parsed.header.kid);
|
|
247
|
+
if (!key) {
|
|
248
|
+
return {
|
|
249
|
+
valid: false,
|
|
250
|
+
header: parsed.header,
|
|
251
|
+
profile,
|
|
252
|
+
profile_raw: profileRaw,
|
|
253
|
+
attempts,
|
|
254
|
+
error_code: ErrorCodes.KEY_NOT_FOUND,
|
|
255
|
+
error_message: `Key not found in profile: ${parsed.header.kid}`,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// Validate key matches algorithm
|
|
259
|
+
try {
|
|
260
|
+
validateKeyForAlgorithm(key, parsed.header.alg);
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
if (err instanceof UcpError) {
|
|
264
|
+
return {
|
|
265
|
+
valid: false,
|
|
266
|
+
header: parsed.header,
|
|
267
|
+
key,
|
|
268
|
+
profile,
|
|
269
|
+
profile_raw: profileRaw,
|
|
270
|
+
attempts,
|
|
271
|
+
error_code: err.code,
|
|
272
|
+
error_message: err.message,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
throw err;
|
|
276
|
+
}
|
|
277
|
+
// Attempt 1: Verify against raw body bytes
|
|
278
|
+
try {
|
|
279
|
+
const rawValid = await verifyDetachedSignature(parsed, body_bytes, key);
|
|
280
|
+
attempts.push({
|
|
281
|
+
mode: 'raw',
|
|
282
|
+
success: rawValid,
|
|
283
|
+
...(!rawValid && {
|
|
284
|
+
error_code: ErrorCodes.SIGNATURE_INVALID,
|
|
285
|
+
error_message: 'Signature verification failed',
|
|
286
|
+
}),
|
|
287
|
+
});
|
|
288
|
+
if (rawValid) {
|
|
289
|
+
return {
|
|
290
|
+
valid: true,
|
|
291
|
+
mode_used: 'raw',
|
|
292
|
+
header: parsed.header,
|
|
293
|
+
key,
|
|
294
|
+
profile,
|
|
295
|
+
profile_raw: profileRaw,
|
|
296
|
+
attempts,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
attempts.push({
|
|
302
|
+
mode: 'raw',
|
|
303
|
+
success: false,
|
|
304
|
+
error_code: ErrorCodes.VERIFICATION_FAILED,
|
|
305
|
+
error_message: String(err),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
// Attempt 2: Try JCS-canonicalized body (only if JSON parseable)
|
|
309
|
+
try {
|
|
310
|
+
const bodyText = new TextDecoder('utf-8').decode(body_bytes);
|
|
311
|
+
const parsed_body = JSON.parse(bodyText);
|
|
312
|
+
const canonicalized = jcsCanonicalizeSync(parsed_body);
|
|
313
|
+
const canonicalizedBytes = new TextEncoder().encode(canonicalized);
|
|
314
|
+
const jcsValid = await verifyDetachedSignature(parsed, canonicalizedBytes, key);
|
|
315
|
+
attempts.push({
|
|
316
|
+
mode: 'jcs',
|
|
317
|
+
success: jcsValid,
|
|
318
|
+
...(!jcsValid && {
|
|
319
|
+
error_code: ErrorCodes.SIGNATURE_INVALID,
|
|
320
|
+
error_message: 'JCS signature verification failed',
|
|
321
|
+
}),
|
|
322
|
+
});
|
|
323
|
+
if (jcsValid) {
|
|
324
|
+
return {
|
|
325
|
+
valid: true,
|
|
326
|
+
mode_used: 'jcs',
|
|
327
|
+
header: parsed.header,
|
|
328
|
+
key,
|
|
329
|
+
profile,
|
|
330
|
+
profile_raw: profileRaw,
|
|
331
|
+
attempts,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch (err) {
|
|
336
|
+
// Body is not valid JSON or JCS canonicalization failed
|
|
337
|
+
// This is fine, we just don't try JCS verification
|
|
338
|
+
if (attempts.length === 1) {
|
|
339
|
+
// Only add JCS attempt if we haven't already
|
|
340
|
+
attempts.push({
|
|
341
|
+
mode: 'jcs',
|
|
342
|
+
success: false,
|
|
343
|
+
error_code: ErrorCodes.PAYLOAD_NOT_JSON,
|
|
344
|
+
error_message: 'Body is not valid JSON, cannot try JCS verification',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Both attempts failed
|
|
349
|
+
return {
|
|
350
|
+
valid: false,
|
|
351
|
+
header: parsed.header,
|
|
352
|
+
key,
|
|
353
|
+
profile,
|
|
354
|
+
profile_raw: profileRaw,
|
|
355
|
+
attempts,
|
|
356
|
+
error_code: ErrorCodes.VERIFICATION_FAILED,
|
|
357
|
+
error_message: 'All verification attempts failed',
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Fetch a UCP profile from a URL.
|
|
362
|
+
* Returns both the parsed profile and raw JSON for evidence capture.
|
|
363
|
+
*/
|
|
364
|
+
async function fetchUcpProfile(url) {
|
|
365
|
+
const response = await fetch(url);
|
|
366
|
+
if (!response.ok) {
|
|
367
|
+
throw ucpError(ErrorCodes.PROFILE_FETCH_FAILED, `HTTP ${response.status} from ${url}`);
|
|
368
|
+
}
|
|
369
|
+
// Capture raw response text for evidence (deterministic)
|
|
370
|
+
const raw = await response.text();
|
|
371
|
+
let profile;
|
|
372
|
+
try {
|
|
373
|
+
profile = JSON.parse(raw);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
throw ucpError(ErrorCodes.PROFILE_INVALID, 'Profile response is not valid JSON');
|
|
377
|
+
}
|
|
378
|
+
if (!profile.signing_keys || !Array.isArray(profile.signing_keys)) {
|
|
379
|
+
throw ucpError(ErrorCodes.PROFILE_NO_SIGNING_KEYS, 'Profile has no signing_keys array');
|
|
380
|
+
}
|
|
381
|
+
if (profile.signing_keys.length === 0) {
|
|
382
|
+
throw ucpError(ErrorCodes.PROFILE_NO_SIGNING_KEYS, 'Profile signing_keys array is empty');
|
|
383
|
+
}
|
|
384
|
+
return { profile, raw };
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Base64url decode to Uint8Array.
|
|
388
|
+
* Uses Buffer for Node.js compatibility (avoids atob issues).
|
|
389
|
+
*/
|
|
390
|
+
function base64urlDecode(s) {
|
|
391
|
+
// Use Buffer.from with 'base64url' encoding (Node.js 15.7.0+)
|
|
392
|
+
// Falls back to manual conversion for older environments
|
|
393
|
+
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
|
|
394
|
+
return new Uint8Array(Buffer.from(s, 'base64url'));
|
|
395
|
+
}
|
|
396
|
+
// Fallback for non-Node environments
|
|
397
|
+
const padded = s + '==='.slice(0, (4 - (s.length % 4)) % 4);
|
|
398
|
+
const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
|
|
399
|
+
const binary = atob(base64);
|
|
400
|
+
const bytes = new Uint8Array(binary.length);
|
|
401
|
+
for (let i = 0; i < binary.length; i++) {
|
|
402
|
+
bytes[i] = binary.charCodeAt(i);
|
|
403
|
+
}
|
|
404
|
+
return bytes;
|
|
405
|
+
}
|
|
406
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@peac/mappings-ucp",
|
|
3
|
+
"version": "0.9.31",
|
|
4
|
+
"description": "Google Universal Commerce Protocol (UCP) mapping to PEAC receipts and dispute evidence",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"ucp",
|
|
19
|
+
"google",
|
|
20
|
+
"commerce",
|
|
21
|
+
"webhooks",
|
|
22
|
+
"ap2",
|
|
23
|
+
"peac"
|
|
24
|
+
],
|
|
25
|
+
"author": "PEAC Protocol Contributors",
|
|
26
|
+
"license": "Apache-2.0",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/peacprotocol/peac/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://peacprotocol.org",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/peacprotocol/peac.git",
|
|
34
|
+
"directory": "packages/mappings/ucp"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"jose": "^5.2.0",
|
|
41
|
+
"@peac/kernel": "0.9.31"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"typescript": "^5.3.0",
|
|
45
|
+
"vitest": "^2.0.0"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest"
|
|
51
|
+
}
|
|
52
|
+
}
|