@peac/mappings-ucp 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,592 @@
1
+ /**
2
+ * @peac/mappings-ucp - RFC 9421 HTTP Message Signature verification
3
+ *
4
+ * Verifies UCP request and webhook signatures using the current UCP signing
5
+ * model: RFC 9421 HTTP Message Signatures (`Signature-Input` / `Signature`)
6
+ * with an RFC 9530 `Content-Digest` computed over the raw body bytes.
7
+ *
8
+ * This is distinct from the legacy `Request-Signature` detached-JWS path in
9
+ * `verify.ts` (`verifyUcpWebhookSignature`). The two schemes do NOT silently
10
+ * fall back to each other: the caller selects which one to verify, and a
11
+ * failure for the selected scheme is surfaced, never quietly downgraded.
12
+ *
13
+ * This verifier covers request-shaped UCP signatures (method / authority / path
14
+ * / headers / body). UCP response signatures use `@status` instead of `@method`
15
+ * and are a separate component model, out of scope here.
16
+ *
17
+ * UCP specifics enforced here (per https://ucp.dev/specification/signatures/):
18
+ * - The algorithm is RESOLVED from the signing key's `crv` (P-256 -> ES256,
19
+ * P-384 -> ES384). UCP does not include a JOSE/JWA `alg` in `Signature-Input`;
20
+ * if a signer includes an `alg` parameter anyway it is treated as advisory and
21
+ * accepted only when it is consistent with the key curve.
22
+ * - `created` is OPTIONAL (UCP handles replay at the business layer through
23
+ * idempotency keys), so the signature is parsed leniently.
24
+ * - The required signed-component set is enforced (see UcpComponentPolicy):
25
+ * `@method`, `@authority`, `@path`; `@query` when the URL has a query;
26
+ * `ucp-agent` when that header is present; `idempotency-key` for
27
+ * state-changing methods; `content-digest` + `content-type` when a body is
28
+ * present.
29
+ * - Signatures are fixed-width raw `r||s` (IEEE P1363): 64 bytes for P-256, 96
30
+ * for P-384. DER is rejected by an explicit length check.
31
+ *
32
+ * Implementation notes:
33
+ * - ECDSA verification uses WebCrypto (`crypto.subtle`), which expects raw
34
+ * `r||s`, so no new external dependency is introduced for ES256/ES384.
35
+ * - RFC 9421 header parsing and signature-base construction are reused from
36
+ * `@peac/http-signatures`. The signature base is built with
37
+ * `preferSerializedParams: true` so the `@signature-params` line is the exact
38
+ * serialized `Signature-Input` value that was signed (RFC 9421 Section 2.5),
39
+ * not a reconstructed one. That package's Ed25519-only `verifySignature` is
40
+ * intentionally not used.
41
+ * - `Content-Digest` is verified over the raw request body bytes with no JSON
42
+ * canonicalization (UCP binds raw bytes). JCS is only relevant to the separate
43
+ * AP2 mandate layer, which is out of scope here.
44
+ */
45
+ import { parseSignature, buildSignatureBase, signatureBaseToBytes, HttpSignatureError, } from '@peac/http-signatures';
46
+ import { ErrorCodes } from './errors.js';
47
+ import { sha256Bytes } from './util.js';
48
+ import { findSigningKey } from './verify.js';
49
+ /**
50
+ * ECDSA parameters resolved from a signing key's curve. ES256 (P-256) is
51
+ * required by UCP; ES384 (P-384) is optional. No other curve is accepted.
52
+ */
53
+ const CURVE_PARAMS = {
54
+ 'P-256': { alg: 'ES256', hash: 'SHA-256', rawSignatureLength: 64 },
55
+ 'P-384': { alg: 'ES384', hash: 'SHA-384', rawSignatureLength: 96 },
56
+ };
57
+ /**
58
+ * RFC 9421 ECDSA algorithm tokens mapped to the curve they imply. Used only for
59
+ * an optional consistency check when a signer redundantly includes `alg`.
60
+ */
61
+ const ALG_TOKEN_CURVE = {
62
+ 'ecdsa-p256-sha256': 'P-256',
63
+ 'ecdsa-p384-sha384': 'P-384',
64
+ };
65
+ const STATE_CHANGING_METHODS = new Set(['POST', 'PUT', 'DELETE', 'PATCH']);
66
+ /**
67
+ * Build a failing result with optional evidence fields.
68
+ */
69
+ function fail(code, message, extra) {
70
+ return { valid: false, error_code: code, error_message: message, ...extra };
71
+ }
72
+ /**
73
+ * Case-insensitive header lookup.
74
+ */
75
+ function getHeader(headers, name) {
76
+ const lower = name.toLowerCase();
77
+ for (const [key, value] of Object.entries(headers)) {
78
+ if (key.toLowerCase() === lower) {
79
+ return value;
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ /**
85
+ * Whether the request URL carries a non-empty query string.
86
+ */
87
+ function hasQueryString(url) {
88
+ try {
89
+ return new URL(url).search !== '';
90
+ }
91
+ catch {
92
+ const i = url.indexOf('?');
93
+ return i !== -1 && i < url.length - 1;
94
+ }
95
+ }
96
+ // Standard base64 alphabet (RFC 4648 Section 4), with optional terminal
97
+ // padding only (not base64url). Used by the RFC 9530 Content-Digest value.
98
+ const SF_BASE64_ALPHABET = /^[A-Za-z0-9+/]*={0,2}$/;
99
+ const SF_BASE64_CANONICAL_PADDED = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
100
+ /**
101
+ * Validate a Structured Field Byte Sequence value (RFC 9651 Section 3.3.5).
102
+ *
103
+ * Uses the RFC 4648 Section 4 base64 alphabet, not base64url. Per RFC 9651
104
+ * parsing, absent padding may be synthesized on decode; characters outside the
105
+ * base64 alphabet remain invalid. Accepts valid unpadded standard base64;
106
+ * rejects base64url (`-`/`_`), misplaced/excess `=`, `length % 4 === 1`,
107
+ * whitespace, and the empty string. Keep separate from JOSE/JWS handling.
108
+ */
109
+ function isValidSfBase64(value) {
110
+ if (value.length === 0)
111
+ return false;
112
+ if (!SF_BASE64_ALPHABET.test(value))
113
+ return false;
114
+ if (value.includes('=')) {
115
+ return SF_BASE64_CANONICAL_PADDED.test(value);
116
+ }
117
+ return value.length % 4 !== 1;
118
+ }
119
+ /**
120
+ * Decode an RFC 4648 Section 4 base64 string to bytes, synthesizing absent
121
+ * padding per RFC 9651 byte-sequence parsing. Returns null on failure. Callers
122
+ * validate the input with {@link isValidSfBase64} first; this does not accept
123
+ * base64url. Keep separate from JOSE/JWS handling.
124
+ */
125
+ function base64ToBytes(b64) {
126
+ try {
127
+ const padded = b64.includes('=')
128
+ ? b64
129
+ : b64.padEnd(b64.length + ((4 - (b64.length % 4)) % 4), '=');
130
+ if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
131
+ return new Uint8Array(Buffer.from(padded, 'base64'));
132
+ }
133
+ const binary = atob(padded);
134
+ const bytes = new Uint8Array(binary.length);
135
+ for (let i = 0; i < binary.length; i++) {
136
+ bytes[i] = binary.charCodeAt(i);
137
+ }
138
+ return bytes;
139
+ }
140
+ catch {
141
+ return null;
142
+ }
143
+ }
144
+ /**
145
+ * Constant-time byte-array equality (defense in depth; digests are not secret).
146
+ */
147
+ function bytesEqual(a, b) {
148
+ if (a.length !== b.length) {
149
+ return false;
150
+ }
151
+ let diff = 0;
152
+ for (let i = 0; i < a.length; i++) {
153
+ diff |= a[i] ^ b[i];
154
+ }
155
+ return diff === 0;
156
+ }
157
+ /**
158
+ * Strictly parse an RFC 9530 / RFC 8941 Content-Digest dictionary. Every member
159
+ * must be exactly `algorithm=:base64:`; a malformed member, a duplicate
160
+ * algorithm, or an empty value rejects the whole header rather than being
161
+ * silently ignored. Base64 byte-sequences contain no commas, so splitting the
162
+ * top-level dictionary on commas is safe.
163
+ */
164
+ function parseContentDigestStrict(headerValue) {
165
+ const digests = new Map();
166
+ const members = headerValue.split(',');
167
+ for (const raw of members) {
168
+ const member = raw.trim();
169
+ if (!member) {
170
+ return {
171
+ ok: false,
172
+ code: ErrorCodes.CONTENT_DIGEST_MALFORMED,
173
+ message: 'Content-Digest contains an empty member',
174
+ };
175
+ }
176
+ // The value is a Structured Field Byte Sequence (RFC 9651 Section 3.3.5):
177
+ // RFC 4648 Section 4 standard base64; absent padding may be synthesized during parsing.
178
+ const match = member.match(/^([A-Za-z0-9-]+)=:([^:]*):$/);
179
+ if (!match || !isValidSfBase64(match[2])) {
180
+ return {
181
+ ok: false,
182
+ code: ErrorCodes.CONTENT_DIGEST_MALFORMED,
183
+ message: `Content-Digest member is malformed: ${member}`,
184
+ };
185
+ }
186
+ const algName = match[1].toLowerCase();
187
+ if (digests.has(algName)) {
188
+ return {
189
+ ok: false,
190
+ code: ErrorCodes.CONTENT_DIGEST_MALFORMED,
191
+ message: `Content-Digest has a duplicate algorithm: ${algName}`,
192
+ };
193
+ }
194
+ digests.set(algName, match[2]);
195
+ }
196
+ if (digests.size === 0) {
197
+ return {
198
+ ok: false,
199
+ code: ErrorCodes.CONTENT_DIGEST_MALFORMED,
200
+ message: 'Content-Digest header is empty',
201
+ };
202
+ }
203
+ return { ok: true, digests };
204
+ }
205
+ /**
206
+ * Verify a Content-Digest header against the raw request body bytes (sha-256).
207
+ */
208
+ function verifyContentDigest(body, headerValue) {
209
+ if (!headerValue || !headerValue.trim()) {
210
+ return {
211
+ ok: false,
212
+ code: ErrorCodes.CONTENT_DIGEST_MISSING,
213
+ message: 'Missing Content-Digest header for a request with a body',
214
+ };
215
+ }
216
+ const parsed = parseContentDigestStrict(headerValue);
217
+ if (!parsed.ok) {
218
+ return parsed;
219
+ }
220
+ const provided = parsed.digests.get('sha-256');
221
+ if (!provided) {
222
+ return {
223
+ ok: false,
224
+ code: ErrorCodes.CONTENT_DIGEST_UNSUPPORTED,
225
+ message: `Content-Digest must include sha-256; got: ${[...parsed.digests.keys()].join(', ')}`,
226
+ };
227
+ }
228
+ const providedBytes = base64ToBytes(provided);
229
+ if (!providedBytes) {
230
+ return {
231
+ ok: false,
232
+ code: ErrorCodes.CONTENT_DIGEST_MALFORMED,
233
+ message: 'Content-Digest sha-256 value is not valid base64',
234
+ };
235
+ }
236
+ if (!bytesEqual(providedBytes, sha256Bytes(body))) {
237
+ return {
238
+ ok: false,
239
+ code: ErrorCodes.CONTENT_DIGEST_MISMATCH,
240
+ message: 'Content-Digest does not match the raw request body',
241
+ };
242
+ }
243
+ return { ok: true };
244
+ }
245
+ /**
246
+ * Components that must be in the signed set for a UCP request (the `ucp-request`
247
+ * policy; `signature-only` skips this). UCP defines no separate webhook set, so
248
+ * webhook deliveries use the same requirements, including idempotency-key.
249
+ */
250
+ function requiredComponents(method, url, headers, hasBody) {
251
+ const required = ['@method', '@authority', '@path'];
252
+ if (hasQueryString(url)) {
253
+ required.push('@query');
254
+ }
255
+ if (getHeader(headers, 'ucp-agent') !== undefined) {
256
+ required.push('ucp-agent');
257
+ }
258
+ if (STATE_CHANGING_METHODS.has(method.toUpperCase())) {
259
+ required.push('idempotency-key');
260
+ }
261
+ if (hasBody) {
262
+ required.push('content-digest', 'content-type');
263
+ }
264
+ return required;
265
+ }
266
+ /** A bare RFC 8941 token / sf-key (sufficient for the UCP-Agent members we accept). */
267
+ const SF_KEY = /^[a-z*][a-z0-9_\-.*]*$/;
268
+ const SF_TOKEN = /^[A-Za-z0-9._-]+$/;
269
+ /**
270
+ * Strictly parse a `UCP-Agent` header. UCP defines it as an RFC 8941 dictionary
271
+ * with a REQUIRED `profile` member whose value is a quoted HTTPS URL, e.g.
272
+ * `UCP-Agent: profile="https://platform.example/.well-known/ucp"`. This is a
273
+ * minimal UCP-scoped parser (not a general structured-field parser): it splits
274
+ * members (respecting quoted strings), requires each member to be `key=value`
275
+ * with a well-formed value (quoted string, token, integer, or boolean), requires
276
+ * exactly one `profile` whose value is a quoted non-empty HTTPS URL, and rejects
277
+ * duplicates, unquoted/empty/non-HTTPS profiles, and malformed members. It never
278
+ * fetches the URL.
279
+ */
280
+ function parseUcpAgentProfile(header) {
281
+ // Split into dictionary members on commas that are not inside a quoted string.
282
+ const members = [];
283
+ let current = '';
284
+ let inString = false;
285
+ for (let i = 0; i < header.length; i++) {
286
+ const ch = header[i];
287
+ if (ch === '"') {
288
+ inString = !inString;
289
+ current += ch;
290
+ }
291
+ else if (ch === ',' && !inString) {
292
+ members.push(current);
293
+ current = '';
294
+ }
295
+ else {
296
+ current += ch;
297
+ }
298
+ }
299
+ members.push(current);
300
+ if (inString) {
301
+ return { ok: false, reason: 'unterminated quoted string' };
302
+ }
303
+ let profileUrl;
304
+ let profileCount = 0;
305
+ for (const raw of members) {
306
+ const member = raw.trim();
307
+ if (member === '') {
308
+ return { ok: false, reason: 'empty dictionary member' };
309
+ }
310
+ const eq = member.indexOf('=');
311
+ if (eq === -1) {
312
+ return { ok: false, reason: `malformed member (expected key=value): ${member}` };
313
+ }
314
+ const key = member.slice(0, eq).trim();
315
+ const value = member.slice(eq + 1).trim();
316
+ if (!SF_KEY.test(key)) {
317
+ return { ok: false, reason: `malformed member key: ${key}` };
318
+ }
319
+ const isQuoted = value.length >= 2 && value.startsWith('"') && value.endsWith('"');
320
+ const valueOk = isQuoted || SF_TOKEN.test(value) || /^-?\d+$/.test(value) || value === '?0' || value === '?1';
321
+ if (!valueOk) {
322
+ return { ok: false, reason: `malformed member value for ${key}` };
323
+ }
324
+ if (key === 'profile') {
325
+ profileCount += 1;
326
+ if (profileCount > 1) {
327
+ return { ok: false, reason: 'duplicate profile member' };
328
+ }
329
+ if (!isQuoted) {
330
+ return { ok: false, reason: 'profile value must be a quoted string' };
331
+ }
332
+ profileUrl = value.slice(1, -1);
333
+ }
334
+ }
335
+ if (profileUrl === undefined) {
336
+ return { ok: false, reason: 'missing profile member' };
337
+ }
338
+ // Validate the profile URL by parsing it, not by prefix matching.
339
+ if (/\s/.test(profileUrl)) {
340
+ return { ok: false, reason: 'profile URL contains whitespace' };
341
+ }
342
+ let parsedUrl;
343
+ try {
344
+ parsedUrl = new URL(profileUrl);
345
+ }
346
+ catch {
347
+ return { ok: false, reason: 'profile is not a valid URL' };
348
+ }
349
+ if (parsedUrl.protocol !== 'https:') {
350
+ return { ok: false, reason: 'profile must be an https URL' };
351
+ }
352
+ if (!parsedUrl.host) {
353
+ return { ok: false, reason: 'profile URL has no host' };
354
+ }
355
+ if (parsedUrl.username !== '' || parsedUrl.password !== '') {
356
+ return { ok: false, reason: 'profile URL must not contain credentials' };
357
+ }
358
+ return { ok: true, profileUrl };
359
+ }
360
+ /**
361
+ * Verify an ECDSA raw (r||s) signature with WebCrypto using a public JWK.
362
+ * Returns false on any import/verify failure (never throws for normal failures).
363
+ */
364
+ async function verifyEcdsaRaw(publicJwk, namedCurve, hash, signature, data) {
365
+ const subtle = globalThis.crypto?.subtle;
366
+ if (!subtle) {
367
+ throw new Error('WebCrypto subtle is unavailable in this runtime');
368
+ }
369
+ try {
370
+ const key = await subtle.importKey('jwk', { kty: 'EC', crv: namedCurve, x: publicJwk.x, y: publicJwk.y }, { name: 'ECDSA', namedCurve }, false, ['verify']);
371
+ return await subtle.verify({ name: 'ECDSA', hash: { name: hash } }, key, signature, data);
372
+ }
373
+ catch {
374
+ // Malformed key material or verification error -> not verifiable.
375
+ return false;
376
+ }
377
+ }
378
+ /**
379
+ * Verify a UCP RFC 9421 HTTP Message Signature.
380
+ *
381
+ * Never falls back to the legacy `Request-Signature` / RFC 7797 path.
382
+ */
383
+ export async function verifyUcpHttpSignature(options) {
384
+ const { signature_input, signature, method, url, headers, body_bytes, profile, label, component_policy = 'ucp-request', expected_profile_url, } = options;
385
+ // 1. Presence checks (explicit; distinct error codes).
386
+ if (!signature_input || !signature_input.trim()) {
387
+ return fail(ErrorCodes.HTTP_SIGNATURE_INPUT_MISSING, 'Missing Signature-Input header');
388
+ }
389
+ if (!signature || !signature.trim()) {
390
+ return fail(ErrorCodes.HTTP_SIGNATURE_MISSING, 'Missing Signature header');
391
+ }
392
+ // 2. Parse RFC 9421 headers. alg and created are optional for UCP; keyid is
393
+ // still required by the parser.
394
+ let parsed;
395
+ try {
396
+ parsed = parseSignature(signature_input, signature, label, {
397
+ requireAlg: false,
398
+ requireCreated: false,
399
+ });
400
+ }
401
+ catch (err) {
402
+ const message = err instanceof HttpSignatureError || err instanceof Error
403
+ ? err.message
404
+ : 'Failed to parse HTTP signature';
405
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, message);
406
+ }
407
+ const params = parsed.params;
408
+ const covered = params.coveredComponents;
409
+ // 2a. Preflight the derived request inputs. RFC 9421 derived components must
410
+ // reflect real values; an empty method/authority/path would bind nothing.
411
+ // UCP is HTTPS-based, so the request URL must be an absolute https URL.
412
+ // The method must be a valid HTTP token with no surrounding or internal
413
+ // whitespace (RFC 7230); `@method` binds it verbatim into the signature base.
414
+ if (!method || method.trim() !== method || !/^[A-Za-z!#$%&'*+.^_`|~-]+$/.test(method)) {
415
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, 'Request method is empty or not a valid token', {
416
+ keyid: params.keyid,
417
+ });
418
+ }
419
+ let requestUrl;
420
+ try {
421
+ requestUrl = new URL(url);
422
+ }
423
+ catch {
424
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, 'Request URL must be an absolute URL', {
425
+ keyid: params.keyid,
426
+ });
427
+ }
428
+ if (requestUrl.protocol !== 'https:') {
429
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, 'Request URL must use https', {
430
+ keyid: params.keyid,
431
+ });
432
+ }
433
+ if (!requestUrl.host) {
434
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, 'Request URL has no authority', {
435
+ keyid: params.keyid,
436
+ });
437
+ }
438
+ if (!requestUrl.pathname.startsWith('/')) {
439
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, 'Request path must begin with /', {
440
+ keyid: params.keyid,
441
+ });
442
+ }
443
+ // 3. Resolve the signing key by keyid -> signing_keys[].kid.
444
+ const key = findSigningKey(profile, params.keyid);
445
+ if (!key) {
446
+ return fail(ErrorCodes.KEY_NOT_FOUND, `Key not found in profile: ${params.keyid}`, {
447
+ keyid: params.keyid,
448
+ covered_components: covered,
449
+ });
450
+ }
451
+ // 4. Key must be a public EC key.
452
+ if (key.kty !== 'EC') {
453
+ return fail(ErrorCodes.KEY_ALGORITHM_MISMATCH, `Expected EC key, got ${key.kty}`, {
454
+ keyid: params.keyid,
455
+ });
456
+ }
457
+ if (!key.x || !key.y) {
458
+ return fail(ErrorCodes.KEY_ALGORITHM_MISMATCH, 'Signing key is missing x/y coordinates', {
459
+ keyid: params.keyid,
460
+ });
461
+ }
462
+ if (key.d !== undefined) {
463
+ return fail(ErrorCodes.KEY_ALGORITHM_MISMATCH, 'Signing key must be a public key (private "d" present)', { keyid: params.keyid });
464
+ }
465
+ // 5. Resolve the algorithm from the key's curve (UCP does not send alg).
466
+ const curveParams = CURVE_PARAMS[key.crv];
467
+ if (!curveParams) {
468
+ return fail(ErrorCodes.KEY_CURVE_MISMATCH, `Unsupported key curve: ${key.crv} (UCP requires P-256, optionally P-384)`, { keyid: params.keyid });
469
+ }
470
+ // 6. If the signer redundantly included alg, it must be consistent with the key.
471
+ if (params.alg !== undefined) {
472
+ const expectedCurve = ALG_TOKEN_CURVE[params.alg.toLowerCase()];
473
+ if (expectedCurve === undefined || expectedCurve !== key.crv) {
474
+ return fail(ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED, `Signature-Input alg "${params.alg}" is not consistent with key curve ${key.crv}`, { keyid: params.keyid, covered_components: covered });
475
+ }
476
+ }
477
+ // 7. Signature must be fixed-width raw (r||s); DER is rejected by length.
478
+ if (parsed.signatureBytes.length !== curveParams.rawSignatureLength) {
479
+ return fail(ErrorCodes.SIGNATURE_MALFORMED, `Expected a ${curveParams.rawSignatureLength}-byte raw (r||s) ${curveParams.alg} signature, ` +
480
+ `got ${parsed.signatureBytes.length} bytes (DER encoding is not accepted)`, { keyid: params.keyid });
481
+ }
482
+ // 8. Enforce the required signed-component policy.
483
+ const hasBody = body_bytes !== undefined && body_bytes.length > 0;
484
+ if (component_policy !== 'signature-only') {
485
+ const required = requiredComponents(method, url, headers, hasBody);
486
+ // 8a. Each required component must be in the signed set.
487
+ const uncovered = required.filter((c) => !covered.includes(c));
488
+ if (uncovered.length > 0) {
489
+ return fail(ErrorCodes.HTTP_SIGNATURE_COMPONENT_MISSING, `Required signed components missing: ${uncovered.join(', ')}`, { keyid: params.keyid, covered_components: covered });
490
+ }
491
+ // 8b. A required header component must also be present and non-empty:
492
+ // buildSignatureBase yields '' for a missing header, so signing the name
493
+ // alone is not equivalent to signing a real value. content-digest is
494
+ // handled in step 9 (so a missing one reports a digest-specific error).
495
+ const emptyHeaders = required.filter((c) => {
496
+ if (c.startsWith('@') || c === 'content-digest') {
497
+ return false;
498
+ }
499
+ const value = getHeader(headers, c);
500
+ return value === undefined || value.trim() === '';
501
+ });
502
+ if (emptyHeaders.length > 0) {
503
+ return fail(ErrorCodes.HTTP_SIGNATURE_COMPONENT_MISSING, `Required signed header(s) absent or empty: ${emptyHeaders.join(', ')}`, { keyid: params.keyid, covered_components: covered });
504
+ }
505
+ }
506
+ // 9. Content-Digest verification over the raw body bytes.
507
+ const digestHeader = getHeader(headers, 'content-digest');
508
+ let contentDigestVerified = false;
509
+ if (hasBody) {
510
+ const skipForSignatureOnly = component_policy === 'signature-only' &&
511
+ !(covered.includes('content-digest') && digestHeader);
512
+ if (!skipForSignatureOnly) {
513
+ const outcome = verifyContentDigest(body_bytes, digestHeader);
514
+ if (!outcome.ok) {
515
+ return fail(outcome.code, outcome.message, {
516
+ keyid: params.keyid,
517
+ covered_components: covered,
518
+ });
519
+ }
520
+ contentDigestVerified = true;
521
+ }
522
+ }
523
+ else if (component_policy !== 'signature-only' &&
524
+ (covered.includes('content-digest') || digestHeader !== undefined)) {
525
+ // A signature covers (or the request carries) a Content-Digest, but no body
526
+ // was supplied to verify it against. Fail closed.
527
+ return fail(ErrorCodes.BODY_REQUIRED, 'content-digest is signed or present but no request body was provided to verify it', { keyid: params.keyid, covered_components: covered });
528
+ }
529
+ // 10. Build the signature base (exact serialized params) and verify ECDSA.
530
+ // buildSignatureBase throws on a duplicate covered component (RFC 9421
531
+ // Section 2.5); surface that as a clean malformed result, never a throw.
532
+ const request = { method, url, headers };
533
+ let baseBytes;
534
+ try {
535
+ const base = buildSignatureBase(request, params, { preferSerializedParams: true });
536
+ baseBytes = signatureBaseToBytes(base);
537
+ }
538
+ catch (err) {
539
+ return fail(ErrorCodes.HTTP_SIGNATURE_MALFORMED, err instanceof Error ? err.message : 'Invalid signature base', { keyid: params.keyid, covered_components: covered });
540
+ }
541
+ let signatureValid;
542
+ try {
543
+ signatureValid = await verifyEcdsaRaw({ crv: key.crv, x: key.x, y: key.y }, key.crv, curveParams.hash, parsed.signatureBytes, baseBytes);
544
+ }
545
+ catch (err) {
546
+ return fail(ErrorCodes.VERIFICATION_FAILED, `Signature verification error: ${err instanceof Error ? err.message : String(err)}`, { keyid: params.keyid, covered_components: covered });
547
+ }
548
+ if (!signatureValid) {
549
+ return fail(ErrorCodes.SIGNATURE_INVALID, 'HTTP Message Signature verification failed', {
550
+ keyid: params.keyid,
551
+ covered_components: covered,
552
+ alg: curveParams.alg,
553
+ });
554
+ }
555
+ // 11. Signer-profile handling. UCP-Agent is a signed identity-binding component.
556
+ // Under the strict request policy it is validated whenever the header is
557
+ // present (not only when a profile is expected); a binding is also enforced
558
+ // when expected_profile_url is supplied. Only a SIGNED ucp-agent is trusted.
559
+ let signerProfileUrl;
560
+ const agentHeader = getHeader(headers, 'ucp-agent');
561
+ const mustValidateAgent = expected_profile_url !== undefined ||
562
+ (component_policy === 'ucp-request' && agentHeader !== undefined);
563
+ if (mustValidateAgent) {
564
+ if (!covered.includes('ucp-agent')) {
565
+ return fail(ErrorCodes.AGENT_MISMATCH, 'UCP-Agent must be a signed component to bind the signer profile', { keyid: params.keyid, covered_components: covered });
566
+ }
567
+ if (!agentHeader || !agentHeader.trim()) {
568
+ return fail(ErrorCodes.AGENT_MISMATCH, 'UCP-Agent header is absent or empty', {
569
+ keyid: params.keyid,
570
+ });
571
+ }
572
+ const parsedAgent = parseUcpAgentProfile(agentHeader);
573
+ if (!parsedAgent.ok) {
574
+ return fail(ErrorCodes.AGENT_MISMATCH, `Invalid UCP-Agent: ${parsedAgent.reason}`, {
575
+ keyid: params.keyid,
576
+ });
577
+ }
578
+ signerProfileUrl = parsedAgent.profileUrl;
579
+ if (expected_profile_url !== undefined && signerProfileUrl !== expected_profile_url) {
580
+ return fail(ErrorCodes.AGENT_MISMATCH, 'UCP-Agent profile does not match the expected profile URL', { keyid: params.keyid });
581
+ }
582
+ }
583
+ return {
584
+ valid: true,
585
+ alg: curveParams.alg,
586
+ keyid: params.keyid,
587
+ covered_components: covered,
588
+ content_digest_verified: contentDigestVerified,
589
+ ...(signerProfileUrl !== undefined ? { signer_profile_url: signerProfileUrl } : {}),
590
+ };
591
+ }
592
+ //# sourceMappingURL=http-signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-signature.js","sourceRoot":"","sources":["../src/http-signature.ts"],"names":[],"mappings":"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"}