@peac/http-signatures 0.15.0 → 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.
- package/dist/base.d.ts +3 -1
- package/dist/base.d.ts.map +1 -1
- package/dist/index.cjs +115 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +115 -25
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +4 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/types.d.ts +21 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/verify.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/base.d.ts
CHANGED
|
@@ -12,7 +12,9 @@ import { ParsedSignatureParams, SignatureRequest } from './types.js';
|
|
|
12
12
|
* @param params - Parsed signature parameters
|
|
13
13
|
* @returns Signature base string
|
|
14
14
|
*/
|
|
15
|
-
export declare function buildSignatureBase(request: SignatureRequest, params: ParsedSignatureParams
|
|
15
|
+
export declare function buildSignatureBase(request: SignatureRequest, params: ParsedSignatureParams, options?: {
|
|
16
|
+
preferSerializedParams?: boolean;
|
|
17
|
+
}): string;
|
|
16
18
|
/**
|
|
17
19
|
* Convert signature base string to bytes for cryptographic verification.
|
|
18
20
|
*/
|
package/dist/base.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGrE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE;IAAE,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAO,GACjD,MAAM,CA8BR;AAsID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CAGtE"}
|
package/dist/index.cjs
CHANGED
|
@@ -49,30 +49,65 @@ function parseSignatureInput(headerValue) {
|
|
|
49
49
|
const members = splitDictionaryMembers(headerValue);
|
|
50
50
|
for (const member of members) {
|
|
51
51
|
const parsed = parseDictionaryMember(member.trim());
|
|
52
|
-
if (parsed) {
|
|
53
|
-
|
|
52
|
+
if (!parsed) {
|
|
53
|
+
throw new HttpSignatureError(
|
|
54
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
55
|
+
`Malformed Signature-Input member: ${member.trim()}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (results.has(parsed.label)) {
|
|
59
|
+
throw new HttpSignatureError(
|
|
60
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
61
|
+
`Duplicate signature label in Signature-Input: ${parsed.label}`
|
|
62
|
+
);
|
|
54
63
|
}
|
|
64
|
+
results.set(parsed.label, parsed.params);
|
|
55
65
|
}
|
|
56
66
|
return results;
|
|
57
67
|
}
|
|
58
68
|
function parseSignatureHeader(headerValue) {
|
|
59
69
|
const results = /* @__PURE__ */ new Map();
|
|
60
70
|
const members = splitDictionaryMembers(headerValue);
|
|
71
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
61
72
|
for (const member of members) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
const trimmed = member.trim();
|
|
74
|
+
const [label, value] = splitKeyValue(trimmed);
|
|
75
|
+
if (!label || !value) {
|
|
76
|
+
throw new HttpSignatureError(
|
|
77
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
78
|
+
`Malformed Signature member: ${trimmed}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (seenLabels.has(label)) {
|
|
82
|
+
throw new HttpSignatureError(
|
|
83
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
84
|
+
`Duplicate signature label in Signature: ${label}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
seenLabels.add(label);
|
|
88
|
+
const match = value.match(/^:([^:]*):$/);
|
|
89
|
+
if (!match || !isValidSfBase64(match[1])) {
|
|
90
|
+
throw new HttpSignatureError(
|
|
91
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
92
|
+
`Malformed Signature byte sequence for label "${label}"`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
66
95
|
const base64 = match[1];
|
|
96
|
+
let bytes;
|
|
67
97
|
try {
|
|
68
|
-
|
|
69
|
-
results.set(label, { bytes, base64 });
|
|
98
|
+
bytes = base64ToBytes(base64);
|
|
70
99
|
} catch {
|
|
100
|
+
throw new HttpSignatureError(
|
|
101
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
102
|
+
`Invalid base64 in Signature for label "${label}"`
|
|
103
|
+
);
|
|
71
104
|
}
|
|
105
|
+
results.set(label, { bytes, base64 });
|
|
72
106
|
}
|
|
73
107
|
return results;
|
|
74
108
|
}
|
|
75
|
-
function parseSignature(signatureInput, signature, label) {
|
|
109
|
+
function parseSignature(signatureInput, signature, label, options = {}) {
|
|
110
|
+
const { requireAlg = true, requireCreated = true } = options;
|
|
76
111
|
if (!signatureInput) {
|
|
77
112
|
throw new HttpSignatureError(
|
|
78
113
|
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
@@ -114,13 +149,13 @@ function parseSignature(signatureInput, signature, label) {
|
|
|
114
149
|
"Missing required parameter: keyid"
|
|
115
150
|
);
|
|
116
151
|
}
|
|
117
|
-
if (!params.alg) {
|
|
152
|
+
if (requireAlg && !params.alg) {
|
|
118
153
|
throw new HttpSignatureError(
|
|
119
154
|
ErrorCodes.SIGNATURE_PARAM_MISSING,
|
|
120
155
|
"Missing required parameter: alg"
|
|
121
156
|
);
|
|
122
157
|
}
|
|
123
|
-
if (params.created === void 0 || params.created === null) {
|
|
158
|
+
if (requireCreated && (params.created === void 0 || params.created === null)) {
|
|
124
159
|
throw new HttpSignatureError(
|
|
125
160
|
ErrorCodes.SIGNATURE_PARAM_MISSING,
|
|
126
161
|
"Missing required parameter: created"
|
|
@@ -185,10 +220,18 @@ function parseDictionaryMember(member) {
|
|
|
185
220
|
const rawParams = parseParameters(paramsString);
|
|
186
221
|
const params = {
|
|
187
222
|
keyid: String(rawParams.keyid ?? ""),
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
223
|
+
coveredComponents,
|
|
224
|
+
// Preserve the exact serialized inner-list-plus-parameters for this label so
|
|
225
|
+
// callers can build an RFC 9421-faithful @signature-params line. `rest` is
|
|
226
|
+
// the dictionary member value (everything after `label=`), already trimmed.
|
|
227
|
+
signatureParamsValue: rest
|
|
191
228
|
};
|
|
229
|
+
if (rawParams.alg !== void 0) {
|
|
230
|
+
params.alg = String(rawParams.alg);
|
|
231
|
+
}
|
|
232
|
+
if (rawParams.created !== void 0) {
|
|
233
|
+
params.created = Number(rawParams.created);
|
|
234
|
+
}
|
|
192
235
|
if (rawParams.expires !== void 0) {
|
|
193
236
|
params.expires = Number(rawParams.expires);
|
|
194
237
|
}
|
|
@@ -201,20 +244,43 @@ function parseDictionaryMember(member) {
|
|
|
201
244
|
return { label, params };
|
|
202
245
|
}
|
|
203
246
|
function parseInnerList(content) {
|
|
247
|
+
const trimmed = content.trim();
|
|
248
|
+
if (trimmed === "") {
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
204
251
|
const items = [];
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
252
|
+
let pos = 0;
|
|
253
|
+
while (pos < trimmed.length) {
|
|
254
|
+
if (trimmed[pos] === " ") {
|
|
255
|
+
pos++;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (trimmed[pos] !== '"') {
|
|
259
|
+
throw new HttpSignatureError(
|
|
260
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
261
|
+
`Malformed covered-component list near: ${trimmed.slice(pos)}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const end = trimmed.indexOf('"', pos + 1);
|
|
265
|
+
if (end === -1) {
|
|
266
|
+
throw new HttpSignatureError(
|
|
267
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
268
|
+
"Unterminated quoted string in covered-component list"
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
items.push(trimmed.slice(pos + 1, end));
|
|
272
|
+
pos = end + 1;
|
|
209
273
|
}
|
|
210
274
|
return items;
|
|
211
275
|
}
|
|
276
|
+
var ALLOWED_PARAMS = /* @__PURE__ */ new Set(["keyid", "alg", "created", "expires", "nonce", "tag"]);
|
|
212
277
|
function parseParameters(paramsString) {
|
|
213
|
-
const params =
|
|
278
|
+
const params = /* @__PURE__ */ Object.create(null);
|
|
214
279
|
const parts = paramsString.split(";").filter((p) => p.trim());
|
|
215
280
|
for (const part of parts) {
|
|
216
281
|
const [key, value] = splitKeyValue(part.trim());
|
|
217
282
|
if (!key) continue;
|
|
283
|
+
if (!ALLOWED_PARAMS.has(key)) continue;
|
|
218
284
|
if (!value) {
|
|
219
285
|
params[key] = true;
|
|
220
286
|
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
@@ -227,9 +293,18 @@ function parseParameters(paramsString) {
|
|
|
227
293
|
}
|
|
228
294
|
return params;
|
|
229
295
|
}
|
|
296
|
+
var SF_BASE64_ALPHABET = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
297
|
+
var SF_BASE64_CANONICAL_PADDED = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
298
|
+
function isValidSfBase64(value) {
|
|
299
|
+
if (value.length === 0) return false;
|
|
300
|
+
if (!SF_BASE64_ALPHABET.test(value)) return false;
|
|
301
|
+
if (value.includes("=")) {
|
|
302
|
+
return SF_BASE64_CANONICAL_PADDED.test(value);
|
|
303
|
+
}
|
|
304
|
+
return value.length % 4 !== 1;
|
|
305
|
+
}
|
|
230
306
|
function base64ToBytes(base64) {
|
|
231
|
-
const
|
|
232
|
-
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
307
|
+
const padded = base64.includes("=") ? base64 : base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
233
308
|
const binary = atob(padded);
|
|
234
309
|
const bytes = new Uint8Array(binary.length);
|
|
235
310
|
for (let i = 0; i < binary.length; i++) {
|
|
@@ -239,13 +314,21 @@ function base64ToBytes(base64) {
|
|
|
239
314
|
}
|
|
240
315
|
|
|
241
316
|
// src/base.ts
|
|
242
|
-
function buildSignatureBase(request, params) {
|
|
317
|
+
function buildSignatureBase(request, params, options = {}) {
|
|
243
318
|
const lines = [];
|
|
319
|
+
const seenComponents = /* @__PURE__ */ new Set();
|
|
244
320
|
for (const component of params.coveredComponents) {
|
|
321
|
+
if (seenComponents.has(component)) {
|
|
322
|
+
throw new HttpSignatureError(
|
|
323
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
324
|
+
`Duplicate covered component: ${component}`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
seenComponents.add(component);
|
|
245
328
|
const value = getComponentValue(request, component);
|
|
246
329
|
lines.push(`"${component}": ${value}`);
|
|
247
330
|
}
|
|
248
|
-
const paramsLine = buildSignatureParamsLine(params);
|
|
331
|
+
const paramsLine = options.preferSerializedParams && params.signatureParamsValue !== void 0 ? params.signatureParamsValue : buildSignatureParamsLine(params);
|
|
249
332
|
lines.push(`"@signature-params": ${paramsLine}`);
|
|
250
333
|
return lines.join("\n");
|
|
251
334
|
}
|
|
@@ -319,7 +402,9 @@ function getHeaderValue(headers, name) {
|
|
|
319
402
|
function buildSignatureParamsLine(params) {
|
|
320
403
|
const components = params.coveredComponents.map((c) => `"${c}"`).join(" ");
|
|
321
404
|
let line = `(${components})`;
|
|
322
|
-
|
|
405
|
+
if (params.created !== void 0) {
|
|
406
|
+
line += `;created=${params.created}`;
|
|
407
|
+
}
|
|
323
408
|
if (params.expires !== void 0) {
|
|
324
409
|
line += `;expires=${params.expires}`;
|
|
325
410
|
}
|
|
@@ -327,7 +412,9 @@ function buildSignatureParamsLine(params) {
|
|
|
327
412
|
line += `;nonce="${params.nonce}"`;
|
|
328
413
|
}
|
|
329
414
|
line += `;keyid="${params.keyid}"`;
|
|
330
|
-
|
|
415
|
+
if (params.alg !== void 0) {
|
|
416
|
+
line += `;alg="${params.alg}"`;
|
|
417
|
+
}
|
|
331
418
|
if (params.tag !== void 0) {
|
|
332
419
|
line += `;tag="${params.tag}"`;
|
|
333
420
|
}
|
|
@@ -401,6 +488,9 @@ function isExpired(params, now = Math.floor(Date.now() / 1e3)) {
|
|
|
401
488
|
return now > params.expires;
|
|
402
489
|
}
|
|
403
490
|
function isCreatedInFuture(params, now = Math.floor(Date.now() / 1e3), skewSeconds = 60) {
|
|
491
|
+
if (params.created === void 0) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
404
494
|
return params.created > now + skewSeconds;
|
|
405
495
|
}
|
|
406
496
|
async function isEd25519WebCryptoSupported() {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/parser.ts","../src/base.ts","../src/verify.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HTTP Signature error codes per execution pack specification.\n */\n\nexport const ErrorCodes = {\n /** Signature-Input header parse failed */\n SIGNATURE_INPUT_MALFORMED: 'E_SIGNATURE_INPUT_MALFORMED',\n /** No Signature header present */\n SIGNATURE_MISSING: 'E_SIGNATURE_MISSING',\n /** Required param (created/keyid/alg) missing */\n SIGNATURE_PARAM_MISSING: 'E_SIGNATURE_PARAM_MISSING',\n /** Algorithm not ed25519 */\n SIGNATURE_ALGORITHM_UNSUPPORTED: 'E_SIGNATURE_ALGORITHM_UNSUPPORTED',\n /** Signature expired (now > expires) */\n SIGNATURE_EXPIRED: 'E_SIGNATURE_EXPIRED',\n /** Signature from future (created > now + skew) */\n SIGNATURE_FUTURE: 'E_SIGNATURE_FUTURE',\n /** Cryptographic verification failed */\n SIGNATURE_INVALID: 'E_SIGNATURE_INVALID',\n /** Ed25519 WebCrypto not supported */\n WEBCRYPTO_UNAVAILABLE: 'E_WEBCRYPTO_UNAVAILABLE',\n /** Key not found by resolver */\n KEY_NOT_FOUND: 'E_KEY_NOT_FOUND',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n/**\n * HTTP status codes for each error.\n */\nexport const ErrorHttpStatus: Record<ErrorCode, number> = {\n [ErrorCodes.SIGNATURE_INPUT_MALFORMED]: 400,\n [ErrorCodes.SIGNATURE_MISSING]: 401,\n [ErrorCodes.SIGNATURE_PARAM_MISSING]: 400,\n [ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED]: 400,\n [ErrorCodes.SIGNATURE_EXPIRED]: 401,\n [ErrorCodes.SIGNATURE_FUTURE]: 401,\n [ErrorCodes.SIGNATURE_INVALID]: 401,\n [ErrorCodes.WEBCRYPTO_UNAVAILABLE]: 500,\n [ErrorCodes.KEY_NOT_FOUND]: 401,\n};\n\n/**\n * HTTP Signature error with code and HTTP status.\n */\nexport class HttpSignatureError extends Error {\n readonly code: ErrorCode;\n readonly httpStatus: number;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = 'HttpSignatureError';\n this.code = code;\n this.httpStatus = ErrorHttpStatus[code];\n }\n}\n","/**\n * Parser for RFC 9421 Signature-Input and Signature headers.\n *\n * Implements minimal RFC 9651 Structured Fields parsing for\n * Dictionary and Inner List types as needed by HTTP Signatures.\n */\n\nimport { ParsedSignatureParams, ParsedSignature } from './types.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Parse Signature-Input header value into structured parameters.\n *\n * Format: label=(\"component1\" \"component2\");param1=value1;param2=value2\n *\n * @param headerValue - Raw Signature-Input header value\n * @returns Map of label to parsed parameters\n */\nexport function parseSignatureInput(headerValue: string): Map<string, ParsedSignatureParams> {\n const results = new Map<string, ParsedSignatureParams>();\n\n // Split by comma for multiple signatures (outer dictionary members)\n const members = splitDictionaryMembers(headerValue);\n\n for (const member of members) {\n const parsed = parseDictionaryMember(member.trim());\n if (parsed) {\n results.set(parsed.label, parsed.params);\n }\n }\n\n return results;\n}\n\n/**\n * Parse Signature header value into raw signature bytes.\n *\n * Format: label=:base64signature:\n *\n * @param headerValue - Raw Signature header value\n * @returns Map of label to signature bytes\n */\nexport function parseSignatureHeader(\n headerValue: string\n): Map<string, { bytes: Uint8Array; base64: string }> {\n const results = new Map<string, { bytes: Uint8Array; base64: string }>();\n\n const members = splitDictionaryMembers(headerValue);\n\n for (const member of members) {\n const [label, value] = splitKeyValue(member.trim());\n if (!label || !value) continue;\n\n // Extract base64 from :...: byte sequence format\n const match = value.match(/^:([A-Za-z0-9+/=_-]+):$/);\n if (!match) continue;\n\n const base64 = match[1];\n try {\n const bytes = base64ToBytes(base64);\n results.set(label, { bytes, base64 });\n } catch {\n // Skip invalid base64\n }\n }\n\n return results;\n}\n\n/**\n * Parse complete signature from both headers.\n *\n * @param signatureInput - Signature-Input header value\n * @param signature - Signature header value\n * @param label - Optional specific label to parse (defaults to first)\n * @returns Parsed signature or throws HttpSignatureError\n */\nexport function parseSignature(\n signatureInput: string,\n signature: string,\n label?: string\n): ParsedSignature {\n if (!signatureInput) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Missing Signature-Input header'\n );\n }\n\n if (!signature) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_MISSING, 'Missing Signature header');\n }\n\n const inputMap = parseSignatureInput(signatureInput);\n const sigMap = parseSignatureHeader(signature);\n\n if (inputMap.size === 0) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Failed to parse Signature-Input header'\n );\n }\n\n // Use specified label or first available\n const targetLabel = label ?? inputMap.keys().next().value;\n if (!targetLabel) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INPUT_MALFORMED, 'No signature label found');\n }\n\n const params = inputMap.get(targetLabel);\n if (!params) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Signature label \"${targetLabel}\" not found in Signature-Input`\n );\n }\n\n const sigData = sigMap.get(targetLabel);\n if (!sigData) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_MISSING,\n `Signature label \"${targetLabel}\" not found in Signature header`\n );\n }\n\n // Validate required parameters\n if (!params.keyid) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: keyid'\n );\n }\n\n if (!params.alg) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: alg'\n );\n }\n\n if (params.created === undefined || params.created === null) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: created'\n );\n }\n\n return {\n label: targetLabel,\n params,\n signatureBytes: sigData.bytes,\n signatureBase64: sigData.base64,\n };\n}\n\n// --- Internal parsing helpers ---\n\n/**\n * Split dictionary members by comma, respecting inner lists and strings.\n */\nfunction splitDictionaryMembers(value: string): string[] {\n const members: string[] = [];\n let current = '';\n let depth = 0;\n let inString = false;\n let inByteSeq = false;\n\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n\n if (char === '\"' && !inByteSeq) {\n inString = !inString;\n current += char;\n } else if (char === ':' && !inString) {\n inByteSeq = !inByteSeq;\n current += char;\n } else if (char === '(' && !inString && !inByteSeq) {\n depth++;\n current += char;\n } else if (char === ')' && !inString && !inByteSeq) {\n depth--;\n current += char;\n } else if (char === ',' && depth === 0 && !inString && !inByteSeq) {\n if (current.trim()) {\n members.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n members.push(current.trim());\n }\n\n return members;\n}\n\n/**\n * Split a dictionary member into key=value pair.\n */\nfunction splitKeyValue(member: string): [string, string] {\n const eqIndex = member.indexOf('=');\n if (eqIndex === -1) {\n return [member, ''];\n }\n return [member.slice(0, eqIndex), member.slice(eqIndex + 1)];\n}\n\n/**\n * Parse a single dictionary member (label=inner-list;params).\n */\nfunction parseDictionaryMember(\n member: string\n): { label: string; params: ParsedSignatureParams } | null {\n const [label, rest] = splitKeyValue(member);\n if (!label || !rest) return null;\n\n // Parse inner list and parameters\n // Format: (\"component1\" \"component2\");param1=value1;param2=value2\n const innerListMatch = rest.match(/^\\(([^)]*)\\)(.*)$/);\n if (!innerListMatch) return null;\n\n const innerListContent = innerListMatch[1];\n const paramsString = innerListMatch[2];\n\n // Parse covered components from inner list\n const coveredComponents = parseInnerList(innerListContent);\n\n // Parse parameters\n const rawParams = parseParameters(paramsString);\n\n const params: ParsedSignatureParams = {\n keyid: String(rawParams.keyid ?? ''),\n alg: String(rawParams.alg ?? ''),\n created: rawParams.created !== undefined ? Number(rawParams.created) : 0,\n coveredComponents,\n };\n\n if (rawParams.expires !== undefined) {\n params.expires = Number(rawParams.expires);\n }\n\n if (rawParams.nonce !== undefined) {\n params.nonce = String(rawParams.nonce);\n }\n\n if (rawParams.tag !== undefined) {\n params.tag = String(rawParams.tag);\n }\n\n return { label, params };\n}\n\n/**\n * Parse inner list content (space-separated quoted strings).\n */\nfunction parseInnerList(content: string): string[] {\n const items: string[] = [];\n const regex = /\"([^\"]*)\"/g;\n let match;\n while ((match = regex.exec(content)) !== null) {\n items.push(match[1]);\n }\n return items;\n}\n\n/**\n * Parse parameters from ;key=value;key2=value2 format.\n */\nfunction parseParameters(paramsString: string): Record<string, string | number> {\n const params: Record<string, string | number> = {};\n\n // Split by semicolon\n const parts = paramsString.split(';').filter((p) => p.trim());\n\n for (const part of parts) {\n const [key, value] = splitKeyValue(part.trim());\n if (!key) continue;\n\n // Parse value - could be integer, string, or token\n if (!value) {\n params[key] = true as unknown as string;\n } else if (value.startsWith('\"') && value.endsWith('\"')) {\n // Quoted string\n params[key] = value.slice(1, -1);\n } else if (/^-?\\d+$/.test(value)) {\n // Integer\n params[key] = parseInt(value, 10);\n } else {\n // Token or other\n params[key] = value;\n }\n }\n\n return params;\n}\n\n/**\n * Decode base64 (standard or URL-safe) to bytes.\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n // Handle URL-safe base64\n const normalized = base64.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=');\n\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * Signature base construction per RFC 9421 Section 2.5.\n *\n * The signature base is a canonical string constructed from\n * covered components and signature parameters.\n */\n\nimport { ParsedSignatureParams, SignatureRequest } from './types.js';\n\n/**\n * Build signature base string for verification.\n *\n * @param request - Request data\n * @param params - Parsed signature parameters\n * @returns Signature base string\n */\nexport function buildSignatureBase(\n request: SignatureRequest,\n params: ParsedSignatureParams\n): string {\n const lines: string[] = [];\n\n // Add each covered component\n for (const component of params.coveredComponents) {\n const value = getComponentValue(request, component);\n lines.push(`\"${component}\": ${value}`);\n }\n\n // Add signature params line\n const paramsLine = buildSignatureParamsLine(params);\n lines.push(`\"@signature-params\": ${paramsLine}`);\n\n return lines.join('\\n');\n}\n\n/**\n * Get the canonical value for a component identifier.\n */\nfunction getComponentValue(request: SignatureRequest, component: string): string {\n // Derived components start with @\n if (component.startsWith('@')) {\n return getDerivedComponentValue(request, component);\n }\n\n // Otherwise it's a header field\n return getHeaderValue(request.headers, component);\n}\n\n/**\n * Get derived component value.\n */\nfunction getDerivedComponentValue(request: SignatureRequest, component: string): string {\n switch (component) {\n case '@method':\n return request.method.toUpperCase();\n\n case '@target-uri':\n return request.url;\n\n case '@authority': {\n try {\n const url = new URL(request.url);\n return url.host;\n } catch {\n // If URL parsing fails, try to extract from headers\n return getHeaderValue(request.headers, 'host');\n }\n }\n\n case '@scheme': {\n try {\n const url = new URL(request.url);\n return url.protocol.replace(':', '');\n } catch {\n return 'https';\n }\n }\n\n case '@request-target': {\n try {\n const url = new URL(request.url);\n return url.pathname + url.search;\n } catch {\n // If not a full URL, assume it's already a path\n return request.url;\n }\n }\n\n case '@path': {\n try {\n const url = new URL(request.url);\n return url.pathname;\n } catch {\n const pathMatch = request.url.match(/^[^?]*/);\n return pathMatch ? pathMatch[0] : request.url;\n }\n }\n\n case '@query': {\n try {\n const url = new URL(request.url);\n return url.search || '?';\n } catch {\n const queryMatch = request.url.match(/\\?.*/);\n return queryMatch ? queryMatch[0] : '?';\n }\n }\n\n default:\n // Unknown derived component - return empty\n return '';\n }\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeaderValue(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n\n return '';\n}\n\n/**\n * Build the signature-params line value.\n *\n * Format: (\"component1\" \"component2\");created=123;keyid=\"key\";alg=\"ed25519\"\n */\nfunction buildSignatureParamsLine(params: ParsedSignatureParams): string {\n // Build inner list of components\n const components = params.coveredComponents.map((c) => `\"${c}\"`).join(' ');\n let line = `(${components})`;\n\n // Add required parameters in canonical order\n line += `;created=${params.created}`;\n\n if (params.expires !== undefined) {\n line += `;expires=${params.expires}`;\n }\n\n if (params.nonce !== undefined) {\n line += `;nonce=\"${params.nonce}\"`;\n }\n\n line += `;keyid=\"${params.keyid}\"`;\n line += `;alg=\"${params.alg}\"`;\n\n if (params.tag !== undefined) {\n line += `;tag=\"${params.tag}\"`;\n }\n\n return line;\n}\n\n/**\n * Convert signature base string to bytes for cryptographic verification.\n */\nexport function signatureBaseToBytes(signatureBase: string): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(signatureBase);\n}\n","/**\n * HTTP Message Signature verification.\n *\n * Runtime-neutral verification using SignatureVerifier function type.\n * WebCrypto is used internally but NOT exposed in public API.\n */\n\nimport {\n SignatureVerifier,\n SignatureRequest,\n VerifyOptions,\n VerificationResult,\n ParsedSignatureParams,\n} from './types.js';\nimport { parseSignature } from './parser.js';\nimport { buildSignatureBase, signatureBaseToBytes } from './base.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Verify an HTTP Message Signature.\n *\n * @param request - Request data with headers\n * @param options - Verification options including key resolver\n * @returns Verification result\n */\nexport async function verifySignature(\n request: SignatureRequest,\n options: VerifyOptions\n): Promise<VerificationResult> {\n const { keyResolver, now = Math.floor(Date.now() / 1000), clockSkewSeconds = 60 } = options;\n\n try {\n // Get signature headers\n const signatureInput = getHeader(request.headers, 'signature-input');\n const signature = getHeader(request.headers, 'signature');\n\n // Parse signature\n const parsed = parseSignature(signatureInput, signature, options.label);\n\n // Validate algorithm\n if (parsed.params.alg !== 'ed25519') {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED,\n `Unsupported algorithm: ${parsed.params.alg} (only ed25519 is supported)`\n );\n }\n\n // Validate time constraints\n validateTimeConstraints(parsed.params, now, clockSkewSeconds);\n\n // Resolve key\n const verifier = await keyResolver(parsed.params.keyid);\n if (!verifier) {\n throw new HttpSignatureError(\n ErrorCodes.KEY_NOT_FOUND,\n `Key not found: ${parsed.params.keyid}`\n );\n }\n\n // Build signature base\n const signatureBase = buildSignatureBase(request, parsed.params);\n const signatureBaseBytes = signatureBaseToBytes(signatureBase);\n\n // Verify signature\n const valid = await verifier(signatureBaseBytes, parsed.signatureBytes);\n\n if (!valid) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INVALID, 'Signature verification failed');\n }\n\n return {\n valid: true,\n signature: parsed,\n };\n } catch (error) {\n if (error instanceof HttpSignatureError) {\n return {\n valid: false,\n errorCode: error.code,\n errorMessage: error.message,\n };\n }\n throw error;\n }\n}\n\n/**\n * Validate time constraints on signature parameters.\n */\nfunction validateTimeConstraints(\n params: ParsedSignatureParams,\n now: number,\n clockSkewSeconds: number\n): void {\n // Check if signature is from the future (with skew tolerance)\n if (isCreatedInFuture(params, now, clockSkewSeconds)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_FUTURE,\n `Signature created in future: ${params.created} > ${now + clockSkewSeconds}`\n );\n }\n\n // Check if signature is expired\n if (isExpired(params, now)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_EXPIRED,\n `Signature expired: ${params.expires} < ${now}`\n );\n }\n}\n\n/**\n * Check if signature is expired.\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @returns true if signature is expired\n */\nexport function isExpired(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000)\n): boolean {\n if (params.expires === undefined) {\n return false;\n }\n return now > params.expires;\n}\n\n/**\n * Check if signature was created in the future (accounting for clock skew).\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @param skewSeconds - Allowed clock skew in seconds (defaults to 60)\n * @returns true if signature is from the future\n */\nexport function isCreatedInFuture(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000),\n skewSeconds: number = 60\n): boolean {\n return params.created > now + skewSeconds;\n}\n\n/**\n * Check if Ed25519 WebCrypto is supported in current runtime.\n *\n * @returns true if Ed25519 WebCrypto is available\n */\nexport async function isEd25519WebCryptoSupported(): Promise<boolean> {\n try {\n // Try to generate a key pair to test support\n const keyPair = await globalThis.crypto.subtle.generateKey('Ed25519', false, [\n 'sign',\n 'verify',\n ]);\n return keyPair !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create a SignatureVerifier from a WebCrypto CryptoKey.\n *\n * This is a helper for consumers who have CryptoKey objects.\n * The function is runtime-neutral as it accepts unknown and casts internally.\n *\n * @param key - WebCrypto CryptoKey (passed as unknown for runtime neutrality)\n * @returns SignatureVerifier function\n */\nexport function createWebCryptoVerifier(key: unknown): SignatureVerifier {\n return async (data: Uint8Array, signature: Uint8Array): Promise<boolean> => {\n // Create proper ArrayBuffer views to satisfy TypeScript\n const sigBuffer = new Uint8Array(signature).buffer;\n const dataBuffer = new Uint8Array(data).buffer;\n\n // Use type from the actual WebCrypto API to avoid DOM type dependency\n type VerifyKey = Parameters<typeof globalThis.crypto.subtle.verify>[1];\n\n return globalThis.crypto.subtle.verify('Ed25519', key as VerifyKey, sigBuffer, dataBuffer);\n };\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeader(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n return '';\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/parser.ts","../src/base.ts","../src/verify.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HTTP Signature error codes per execution pack specification.\n */\n\nexport const ErrorCodes = {\n /** Signature-Input header parse failed */\n SIGNATURE_INPUT_MALFORMED: 'E_SIGNATURE_INPUT_MALFORMED',\n /** No Signature header present */\n SIGNATURE_MISSING: 'E_SIGNATURE_MISSING',\n /** Required param (created/keyid/alg) missing */\n SIGNATURE_PARAM_MISSING: 'E_SIGNATURE_PARAM_MISSING',\n /** Algorithm not ed25519 */\n SIGNATURE_ALGORITHM_UNSUPPORTED: 'E_SIGNATURE_ALGORITHM_UNSUPPORTED',\n /** Signature expired (now > expires) */\n SIGNATURE_EXPIRED: 'E_SIGNATURE_EXPIRED',\n /** Signature from future (created > now + skew) */\n SIGNATURE_FUTURE: 'E_SIGNATURE_FUTURE',\n /** Cryptographic verification failed */\n SIGNATURE_INVALID: 'E_SIGNATURE_INVALID',\n /** Ed25519 WebCrypto not supported */\n WEBCRYPTO_UNAVAILABLE: 'E_WEBCRYPTO_UNAVAILABLE',\n /** Key not found by resolver */\n KEY_NOT_FOUND: 'E_KEY_NOT_FOUND',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n/**\n * HTTP status codes for each error.\n */\nexport const ErrorHttpStatus: Record<ErrorCode, number> = {\n [ErrorCodes.SIGNATURE_INPUT_MALFORMED]: 400,\n [ErrorCodes.SIGNATURE_MISSING]: 401,\n [ErrorCodes.SIGNATURE_PARAM_MISSING]: 400,\n [ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED]: 400,\n [ErrorCodes.SIGNATURE_EXPIRED]: 401,\n [ErrorCodes.SIGNATURE_FUTURE]: 401,\n [ErrorCodes.SIGNATURE_INVALID]: 401,\n [ErrorCodes.WEBCRYPTO_UNAVAILABLE]: 500,\n [ErrorCodes.KEY_NOT_FOUND]: 401,\n};\n\n/**\n * HTTP Signature error with code and HTTP status.\n */\nexport class HttpSignatureError extends Error {\n readonly code: ErrorCode;\n readonly httpStatus: number;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = 'HttpSignatureError';\n this.code = code;\n this.httpStatus = ErrorHttpStatus[code];\n }\n}\n","/**\n * Parser for RFC 9421 Signature-Input and Signature headers.\n *\n * Implements minimal RFC 9651 Structured Fields parsing for\n * Dictionary and Inner List types as needed by HTTP Signatures.\n */\n\nimport { ParsedSignatureParams, ParsedSignature } from './types.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Parse Signature-Input header value into structured parameters.\n *\n * Format: label=(\"component1\" \"component2\");param1=value1;param2=value2\n *\n * @param headerValue - Raw Signature-Input header value\n * @returns Map of label to parsed parameters\n */\nexport function parseSignatureInput(headerValue: string): Map<string, ParsedSignatureParams> {\n const results = new Map<string, ParsedSignatureParams>();\n\n // Split by comma for multiple signatures (outer dictionary members)\n const members = splitDictionaryMembers(headerValue);\n\n for (const member of members) {\n // Structured-field parsing is not best-effort: a malformed member fails the\n // whole header rather than being skipped.\n const parsed = parseDictionaryMember(member.trim());\n if (!parsed) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed Signature-Input member: ${member.trim()}`\n );\n }\n // RFC 9421: signature labels must be unique. Fail closed on a duplicate\n // rather than silently overwriting an earlier definition.\n if (results.has(parsed.label)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Duplicate signature label in Signature-Input: ${parsed.label}`\n );\n }\n results.set(parsed.label, parsed.params);\n }\n\n return results;\n}\n\n/**\n * Parse Signature header value into raw signature bytes.\n *\n * Format: label=:base64signature:\n *\n * @param headerValue - Raw Signature header value\n * @returns Map of label to signature bytes\n */\nexport function parseSignatureHeader(\n headerValue: string\n): Map<string, { bytes: Uint8Array; base64: string }> {\n const results = new Map<string, { bytes: Uint8Array; base64: string }>();\n\n const members = splitDictionaryMembers(headerValue);\n const seenLabels = new Set<string>();\n\n for (const member of members) {\n const trimmed = member.trim();\n const [label, value] = splitKeyValue(trimmed);\n // Fail closed on a malformed member rather than skipping it.\n if (!label || !value) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed Signature member: ${trimmed}`\n );\n }\n\n // RFC 9421: a label may appear at most once in the Signature header.\n if (seenLabels.has(label)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Duplicate signature label in Signature: ${label}`\n );\n }\n seenLabels.add(label);\n\n // The value MUST be a Structured Field Byte Sequence (:base64:) using\n // RFC 4648 Section 4 standard base64 (RFC 9651 Section 3.3.5), NOT\n // base64url. Capture the inner value, then validate Structured Field base64.\n const match = value.match(/^:([^:]*):$/);\n if (!match || !isValidSfBase64(match[1])) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed Signature byte sequence for label \"${label}\"`\n );\n }\n\n const base64 = match[1];\n let bytes: Uint8Array;\n try {\n bytes = base64ToBytes(base64);\n } catch {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Invalid base64 in Signature for label \"${label}\"`\n );\n }\n results.set(label, { bytes, base64 });\n }\n\n return results;\n}\n\n/**\n * Parse complete signature from both headers.\n *\n * @param signatureInput - Signature-Input header value\n * @param signature - Signature header value\n * @param label - Optional specific label to parse (defaults to first)\n * @returns Parsed signature or throws HttpSignatureError\n */\nexport function parseSignature(\n signatureInput: string,\n signature: string,\n label?: string,\n options: { requireAlg?: boolean; requireCreated?: boolean } = {}\n): ParsedSignature {\n const { requireAlg = true, requireCreated = true } = options;\n\n if (!signatureInput) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Missing Signature-Input header'\n );\n }\n\n if (!signature) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_MISSING, 'Missing Signature header');\n }\n\n const inputMap = parseSignatureInput(signatureInput);\n const sigMap = parseSignatureHeader(signature);\n\n if (inputMap.size === 0) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Failed to parse Signature-Input header'\n );\n }\n\n // Use specified label or first available\n const targetLabel = label ?? inputMap.keys().next().value;\n if (!targetLabel) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INPUT_MALFORMED, 'No signature label found');\n }\n\n const params = inputMap.get(targetLabel);\n if (!params) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Signature label \"${targetLabel}\" not found in Signature-Input`\n );\n }\n\n const sigData = sigMap.get(targetLabel);\n if (!sigData) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_MISSING,\n `Signature label \"${targetLabel}\" not found in Signature header`\n );\n }\n\n // Validate required parameters\n if (!params.keyid) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: keyid'\n );\n }\n\n if (requireAlg && !params.alg) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: alg'\n );\n }\n\n if (requireCreated && (params.created === undefined || params.created === null)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: created'\n );\n }\n\n return {\n label: targetLabel,\n params,\n signatureBytes: sigData.bytes,\n signatureBase64: sigData.base64,\n };\n}\n\n// --- Internal parsing helpers ---\n\n/**\n * Split dictionary members by comma, respecting inner lists and strings.\n */\nfunction splitDictionaryMembers(value: string): string[] {\n const members: string[] = [];\n let current = '';\n let depth = 0;\n let inString = false;\n let inByteSeq = false;\n\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n\n if (char === '\"' && !inByteSeq) {\n inString = !inString;\n current += char;\n } else if (char === ':' && !inString) {\n inByteSeq = !inByteSeq;\n current += char;\n } else if (char === '(' && !inString && !inByteSeq) {\n depth++;\n current += char;\n } else if (char === ')' && !inString && !inByteSeq) {\n depth--;\n current += char;\n } else if (char === ',' && depth === 0 && !inString && !inByteSeq) {\n if (current.trim()) {\n members.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n members.push(current.trim());\n }\n\n return members;\n}\n\n/**\n * Split a dictionary member into key=value pair.\n */\nfunction splitKeyValue(member: string): [string, string] {\n const eqIndex = member.indexOf('=');\n if (eqIndex === -1) {\n return [member, ''];\n }\n return [member.slice(0, eqIndex), member.slice(eqIndex + 1)];\n}\n\n/**\n * Parse a single dictionary member (label=inner-list;params).\n */\nfunction parseDictionaryMember(\n member: string\n): { label: string; params: ParsedSignatureParams } | null {\n const [label, rest] = splitKeyValue(member);\n if (!label || !rest) return null;\n\n // Parse inner list and parameters\n // Format: (\"component1\" \"component2\");param1=value1;param2=value2\n const innerListMatch = rest.match(/^\\(([^)]*)\\)(.*)$/);\n if (!innerListMatch) return null;\n\n const innerListContent = innerListMatch[1];\n const paramsString = innerListMatch[2];\n\n // Parse covered components from inner list\n const coveredComponents = parseInnerList(innerListContent);\n\n // Parse parameters\n const rawParams = parseParameters(paramsString);\n\n const params: ParsedSignatureParams = {\n keyid: String(rawParams.keyid ?? ''),\n coveredComponents,\n // Preserve the exact serialized inner-list-plus-parameters for this label so\n // callers can build an RFC 9421-faithful @signature-params line. `rest` is\n // the dictionary member value (everything after `label=`), already trimmed.\n signatureParamsValue: rest,\n };\n\n // alg and created are optional at the parse layer (RFC 9421). When absent they\n // stay undefined rather than defaulting to '' / 0, so a profile that omits them\n // (e.g. UCP) is represented faithfully and never fabricates a created=0 base.\n if (rawParams.alg !== undefined) {\n params.alg = String(rawParams.alg);\n }\n\n if (rawParams.created !== undefined) {\n params.created = Number(rawParams.created);\n }\n\n if (rawParams.expires !== undefined) {\n params.expires = Number(rawParams.expires);\n }\n\n if (rawParams.nonce !== undefined) {\n params.nonce = String(rawParams.nonce);\n }\n\n if (rawParams.tag !== undefined) {\n params.tag = String(rawParams.tag);\n }\n\n return { label, params };\n}\n\n/**\n * Parse inner list content: space-separated quoted strings (the covered\n * components). Fail closed on anything else (a bare/unquoted token, component\n * parameters, or trailing garbage) rather than silently extracting only the\n * quoted parts.\n */\nfunction parseInnerList(content: string): string[] {\n const trimmed = content.trim();\n if (trimmed === '') {\n return [];\n }\n const items: string[] = [];\n let pos = 0;\n while (pos < trimmed.length) {\n if (trimmed[pos] === ' ') {\n pos++;\n continue;\n }\n if (trimmed[pos] !== '\"') {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed covered-component list near: ${trimmed.slice(pos)}`\n );\n }\n const end = trimmed.indexOf('\"', pos + 1);\n if (end === -1) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Unterminated quoted string in covered-component list'\n );\n }\n items.push(trimmed.slice(pos + 1, end));\n pos = end + 1;\n }\n return items;\n}\n\n/**\n * Parse parameters from ;key=value;key2=value2 format.\n */\n\n// The closed set of RFC 9421 signature parameters consumed by callers. Only these\n// names are stored, so a caller-controlled parameter name from the header is never\n// written to an arbitrary (or prototype-polluting) property. Unknown parameters are\n// ignored, which is already how the consumer treats them.\nconst ALLOWED_PARAMS = new Set(['keyid', 'alg', 'created', 'expires', 'nonce', 'tag']);\n\nfunction parseParameters(paramsString: string): Record<string, string | number> {\n // Null-prototype map, defense in depth alongside the allowlist guard below.\n const params: Record<string, string | number> = Object.create(null);\n\n // Split by semicolon\n const parts = paramsString.split(';').filter((p) => p.trim());\n\n for (const part of parts) {\n const [key, value] = splitKeyValue(part.trim());\n if (!key) continue;\n\n // Only store known RFC 9421 parameters; the write target is constrained to a\n // constant allowlist rather than caller-controlled input.\n if (!ALLOWED_PARAMS.has(key)) continue;\n\n // Parse value - could be integer, string, or token\n if (!value) {\n params[key] = true as unknown as string;\n } else if (value.startsWith('\"') && value.endsWith('\"')) {\n // Quoted string\n params[key] = value.slice(1, -1);\n } else if (/^-?\\d+$/.test(value)) {\n // Integer\n params[key] = parseInt(value, 10);\n } else {\n // Token or other\n params[key] = value;\n }\n }\n\n return params;\n}\n\n// Standard base64 alphabet (RFC 4648 Section 4), with optional terminal\n// padding only. Rejects base64url (`-`/`_`), whitespace, and any `=` that is\n// not part of trailing padding.\nconst SF_BASE64_ALPHABET = /^[A-Za-z0-9+/]*={0,2}$/;\n// Canonical terminal padding when padding is present.\nconst SF_BASE64_CANONICAL_PADDED =\n /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;\n\n/**\n * Validate a Structured Field Byte Sequence value (RFC 9651 Section 3.3.5).\n *\n * Structured Field Byte Sequences use the RFC 4648 Section 4 base64 alphabet,\n * not base64url. Per RFC 9651 parsing, absent padding may be synthesized on\n * decode; characters outside the base64 alphabet remain invalid. So this\n * accepts valid unpadded standard base64, but rejects base64url (`-`/`_`),\n * misplaced/excess `=`, `length % 4 === 1`, whitespace, and the empty string\n * (no behavior widening). Keep this separate from JOSE/JWS base64url handling.\n */\nfunction isValidSfBase64(value: string): boolean {\n if (value.length === 0) return false;\n if (!SF_BASE64_ALPHABET.test(value)) return false;\n if (value.includes('=')) {\n // If padding is present, it must be canonical terminal padding.\n return SF_BASE64_CANONICAL_PADDED.test(value);\n }\n // Absent padding is synthesized on decode; `length % 4 === 1` is unrepairable.\n return value.length % 4 !== 1;\n}\n\n/**\n * Decode an RFC 4648 Section 4 base64 string to bytes, synthesizing absent\n * padding per RFC 9651 byte-sequence parsing. Callers MUST validate the input\n * with {@link isValidSfBase64} first; this does not accept base64url.\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n const padded = base64.includes('=')\n ? base64\n : base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * Signature base construction per RFC 9421 Section 2.5.\n *\n * The signature base is a canonical string constructed from\n * covered components and signature parameters.\n */\n\nimport { ParsedSignatureParams, SignatureRequest } from './types.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Build signature base string for verification.\n *\n * @param request - Request data\n * @param params - Parsed signature parameters\n * @returns Signature base string\n */\nexport function buildSignatureBase(\n request: SignatureRequest,\n params: ParsedSignatureParams,\n options: { preferSerializedParams?: boolean } = {}\n): string {\n const lines: string[] = [];\n\n // Add each covered component. RFC 9421 Section 2.5: if a component identifier\n // has already been added to the signature base, base generation MUST fail.\n const seenComponents = new Set<string>();\n for (const component of params.coveredComponents) {\n if (seenComponents.has(component)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Duplicate covered component: ${component}`\n );\n }\n seenComponents.add(component);\n const value = getComponentValue(request, component);\n lines.push(`\"${component}\": ${value}`);\n }\n\n // Add signature params line. RFC 9421 requires the @signature-params value to\n // be byte-identical to the serialized Signature-Input member that was signed.\n // When the caller opts in and the parser captured that exact serialization,\n // reuse it verbatim; otherwise reconstruct it (the default, and the only option\n // for programmatically built params that have no serialized form).\n const paramsLine =\n options.preferSerializedParams && params.signatureParamsValue !== undefined\n ? params.signatureParamsValue\n : buildSignatureParamsLine(params);\n lines.push(`\"@signature-params\": ${paramsLine}`);\n\n return lines.join('\\n');\n}\n\n/**\n * Get the canonical value for a component identifier.\n */\nfunction getComponentValue(request: SignatureRequest, component: string): string {\n // Derived components start with @\n if (component.startsWith('@')) {\n return getDerivedComponentValue(request, component);\n }\n\n // Otherwise it's a header field\n return getHeaderValue(request.headers, component);\n}\n\n/**\n * Get derived component value.\n */\nfunction getDerivedComponentValue(request: SignatureRequest, component: string): string {\n switch (component) {\n case '@method':\n return request.method.toUpperCase();\n\n case '@target-uri':\n return request.url;\n\n case '@authority': {\n try {\n const url = new URL(request.url);\n return url.host;\n } catch {\n // If URL parsing fails, try to extract from headers\n return getHeaderValue(request.headers, 'host');\n }\n }\n\n case '@scheme': {\n try {\n const url = new URL(request.url);\n return url.protocol.replace(':', '');\n } catch {\n return 'https';\n }\n }\n\n case '@request-target': {\n try {\n const url = new URL(request.url);\n return url.pathname + url.search;\n } catch {\n // If not a full URL, assume it's already a path\n return request.url;\n }\n }\n\n case '@path': {\n try {\n const url = new URL(request.url);\n return url.pathname;\n } catch {\n const pathMatch = request.url.match(/^[^?]*/);\n return pathMatch ? pathMatch[0] : request.url;\n }\n }\n\n case '@query': {\n try {\n const url = new URL(request.url);\n return url.search || '?';\n } catch {\n const queryMatch = request.url.match(/\\?.*/);\n return queryMatch ? queryMatch[0] : '?';\n }\n }\n\n default:\n // Unknown derived component - return empty\n return '';\n }\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeaderValue(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n\n return '';\n}\n\n/**\n * Build the signature-params line value.\n *\n * Format: (\"component1\" \"component2\");created=123;keyid=\"key\";alg=\"ed25519\"\n */\nfunction buildSignatureParamsLine(params: ParsedSignatureParams): string {\n // Build inner list of components\n const components = params.coveredComponents.map((c) => `\"${c}\"`).join(' ');\n let line = `(${components})`;\n\n // Emit parameters in canonical order, skipping optional ones that are absent.\n // For the PEAC/TAP path created and alg are always present, so this is\n // byte-identical to the previous unconditional output.\n if (params.created !== undefined) {\n line += `;created=${params.created}`;\n }\n\n if (params.expires !== undefined) {\n line += `;expires=${params.expires}`;\n }\n\n if (params.nonce !== undefined) {\n line += `;nonce=\"${params.nonce}\"`;\n }\n\n line += `;keyid=\"${params.keyid}\"`;\n\n if (params.alg !== undefined) {\n line += `;alg=\"${params.alg}\"`;\n }\n\n if (params.tag !== undefined) {\n line += `;tag=\"${params.tag}\"`;\n }\n\n return line;\n}\n\n/**\n * Convert signature base string to bytes for cryptographic verification.\n */\nexport function signatureBaseToBytes(signatureBase: string): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(signatureBase);\n}\n","/**\n * HTTP Message Signature verification.\n *\n * Runtime-neutral verification using SignatureVerifier function type.\n * WebCrypto is used internally but NOT exposed in public API.\n */\n\nimport {\n SignatureVerifier,\n SignatureRequest,\n VerifyOptions,\n VerificationResult,\n ParsedSignatureParams,\n} from './types.js';\nimport { parseSignature } from './parser.js';\nimport { buildSignatureBase, signatureBaseToBytes } from './base.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Verify an HTTP Message Signature.\n *\n * @param request - Request data with headers\n * @param options - Verification options including key resolver\n * @returns Verification result\n */\nexport async function verifySignature(\n request: SignatureRequest,\n options: VerifyOptions\n): Promise<VerificationResult> {\n const { keyResolver, now = Math.floor(Date.now() / 1000), clockSkewSeconds = 60 } = options;\n\n try {\n // Get signature headers\n const signatureInput = getHeader(request.headers, 'signature-input');\n const signature = getHeader(request.headers, 'signature');\n\n // Parse signature\n const parsed = parseSignature(signatureInput, signature, options.label);\n\n // Validate algorithm\n if (parsed.params.alg !== 'ed25519') {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED,\n `Unsupported algorithm: ${parsed.params.alg} (only ed25519 is supported)`\n );\n }\n\n // Validate time constraints\n validateTimeConstraints(parsed.params, now, clockSkewSeconds);\n\n // Resolve key\n const verifier = await keyResolver(parsed.params.keyid);\n if (!verifier) {\n throw new HttpSignatureError(\n ErrorCodes.KEY_NOT_FOUND,\n `Key not found: ${parsed.params.keyid}`\n );\n }\n\n // Build signature base\n const signatureBase = buildSignatureBase(request, parsed.params);\n const signatureBaseBytes = signatureBaseToBytes(signatureBase);\n\n // Verify signature\n const valid = await verifier(signatureBaseBytes, parsed.signatureBytes);\n\n if (!valid) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INVALID, 'Signature verification failed');\n }\n\n return {\n valid: true,\n signature: parsed,\n };\n } catch (error) {\n if (error instanceof HttpSignatureError) {\n return {\n valid: false,\n errorCode: error.code,\n errorMessage: error.message,\n };\n }\n throw error;\n }\n}\n\n/**\n * Validate time constraints on signature parameters.\n */\nfunction validateTimeConstraints(\n params: ParsedSignatureParams,\n now: number,\n clockSkewSeconds: number\n): void {\n // Check if signature is from the future (with skew tolerance)\n if (isCreatedInFuture(params, now, clockSkewSeconds)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_FUTURE,\n `Signature created in future: ${params.created} > ${now + clockSkewSeconds}`\n );\n }\n\n // Check if signature is expired\n if (isExpired(params, now)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_EXPIRED,\n `Signature expired: ${params.expires} < ${now}`\n );\n }\n}\n\n/**\n * Check if signature is expired.\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @returns true if signature is expired\n */\nexport function isExpired(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000)\n): boolean {\n if (params.expires === undefined) {\n return false;\n }\n return now > params.expires;\n}\n\n/**\n * Check if signature was created in the future (accounting for clock skew).\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @param skewSeconds - Allowed clock skew in seconds (defaults to 60)\n * @returns true if signature is from the future\n */\nexport function isCreatedInFuture(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000),\n skewSeconds: number = 60\n): boolean {\n // A signature with no `created` parameter cannot be classified as future-dated.\n if (params.created === undefined) {\n return false;\n }\n return params.created > now + skewSeconds;\n}\n\n/**\n * Check if Ed25519 WebCrypto is supported in current runtime.\n *\n * @returns true if Ed25519 WebCrypto is available\n */\nexport async function isEd25519WebCryptoSupported(): Promise<boolean> {\n try {\n // Try to generate a key pair to test support\n const keyPair = await globalThis.crypto.subtle.generateKey('Ed25519', false, [\n 'sign',\n 'verify',\n ]);\n return keyPair !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create a SignatureVerifier from a WebCrypto CryptoKey.\n *\n * This is a helper for consumers who have CryptoKey objects.\n * The function is runtime-neutral as it accepts unknown and casts internally.\n *\n * @param key - WebCrypto CryptoKey (passed as unknown for runtime neutrality)\n * @returns SignatureVerifier function\n */\nexport function createWebCryptoVerifier(key: unknown): SignatureVerifier {\n return async (data: Uint8Array, signature: Uint8Array): Promise<boolean> => {\n // Create proper ArrayBuffer views to satisfy TypeScript\n const sigBuffer = new Uint8Array(signature).buffer;\n const dataBuffer = new Uint8Array(data).buffer;\n\n // Use type from the actual WebCrypto API to avoid DOM type dependency\n type VerifyKey = Parameters<typeof globalThis.crypto.subtle.verify>[1];\n\n return globalThis.crypto.subtle.verify('Ed25519', key as VerifyKey, sigBuffer, dataBuffer);\n };\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeader(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n return '';\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -47,30 +47,65 @@ function parseSignatureInput(headerValue) {
|
|
|
47
47
|
const members = splitDictionaryMembers(headerValue);
|
|
48
48
|
for (const member of members) {
|
|
49
49
|
const parsed = parseDictionaryMember(member.trim());
|
|
50
|
-
if (parsed) {
|
|
51
|
-
|
|
50
|
+
if (!parsed) {
|
|
51
|
+
throw new HttpSignatureError(
|
|
52
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
53
|
+
`Malformed Signature-Input member: ${member.trim()}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (results.has(parsed.label)) {
|
|
57
|
+
throw new HttpSignatureError(
|
|
58
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
59
|
+
`Duplicate signature label in Signature-Input: ${parsed.label}`
|
|
60
|
+
);
|
|
52
61
|
}
|
|
62
|
+
results.set(parsed.label, parsed.params);
|
|
53
63
|
}
|
|
54
64
|
return results;
|
|
55
65
|
}
|
|
56
66
|
function parseSignatureHeader(headerValue) {
|
|
57
67
|
const results = /* @__PURE__ */ new Map();
|
|
58
68
|
const members = splitDictionaryMembers(headerValue);
|
|
69
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
59
70
|
for (const member of members) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
const trimmed = member.trim();
|
|
72
|
+
const [label, value] = splitKeyValue(trimmed);
|
|
73
|
+
if (!label || !value) {
|
|
74
|
+
throw new HttpSignatureError(
|
|
75
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
76
|
+
`Malformed Signature member: ${trimmed}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (seenLabels.has(label)) {
|
|
80
|
+
throw new HttpSignatureError(
|
|
81
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
82
|
+
`Duplicate signature label in Signature: ${label}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
seenLabels.add(label);
|
|
86
|
+
const match = value.match(/^:([^:]*):$/);
|
|
87
|
+
if (!match || !isValidSfBase64(match[1])) {
|
|
88
|
+
throw new HttpSignatureError(
|
|
89
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
90
|
+
`Malformed Signature byte sequence for label "${label}"`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
64
93
|
const base64 = match[1];
|
|
94
|
+
let bytes;
|
|
65
95
|
try {
|
|
66
|
-
|
|
67
|
-
results.set(label, { bytes, base64 });
|
|
96
|
+
bytes = base64ToBytes(base64);
|
|
68
97
|
} catch {
|
|
98
|
+
throw new HttpSignatureError(
|
|
99
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
100
|
+
`Invalid base64 in Signature for label "${label}"`
|
|
101
|
+
);
|
|
69
102
|
}
|
|
103
|
+
results.set(label, { bytes, base64 });
|
|
70
104
|
}
|
|
71
105
|
return results;
|
|
72
106
|
}
|
|
73
|
-
function parseSignature(signatureInput, signature, label) {
|
|
107
|
+
function parseSignature(signatureInput, signature, label, options = {}) {
|
|
108
|
+
const { requireAlg = true, requireCreated = true } = options;
|
|
74
109
|
if (!signatureInput) {
|
|
75
110
|
throw new HttpSignatureError(
|
|
76
111
|
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
@@ -112,13 +147,13 @@ function parseSignature(signatureInput, signature, label) {
|
|
|
112
147
|
"Missing required parameter: keyid"
|
|
113
148
|
);
|
|
114
149
|
}
|
|
115
|
-
if (!params.alg) {
|
|
150
|
+
if (requireAlg && !params.alg) {
|
|
116
151
|
throw new HttpSignatureError(
|
|
117
152
|
ErrorCodes.SIGNATURE_PARAM_MISSING,
|
|
118
153
|
"Missing required parameter: alg"
|
|
119
154
|
);
|
|
120
155
|
}
|
|
121
|
-
if (params.created === void 0 || params.created === null) {
|
|
156
|
+
if (requireCreated && (params.created === void 0 || params.created === null)) {
|
|
122
157
|
throw new HttpSignatureError(
|
|
123
158
|
ErrorCodes.SIGNATURE_PARAM_MISSING,
|
|
124
159
|
"Missing required parameter: created"
|
|
@@ -183,10 +218,18 @@ function parseDictionaryMember(member) {
|
|
|
183
218
|
const rawParams = parseParameters(paramsString);
|
|
184
219
|
const params = {
|
|
185
220
|
keyid: String(rawParams.keyid ?? ""),
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
221
|
+
coveredComponents,
|
|
222
|
+
// Preserve the exact serialized inner-list-plus-parameters for this label so
|
|
223
|
+
// callers can build an RFC 9421-faithful @signature-params line. `rest` is
|
|
224
|
+
// the dictionary member value (everything after `label=`), already trimmed.
|
|
225
|
+
signatureParamsValue: rest
|
|
189
226
|
};
|
|
227
|
+
if (rawParams.alg !== void 0) {
|
|
228
|
+
params.alg = String(rawParams.alg);
|
|
229
|
+
}
|
|
230
|
+
if (rawParams.created !== void 0) {
|
|
231
|
+
params.created = Number(rawParams.created);
|
|
232
|
+
}
|
|
190
233
|
if (rawParams.expires !== void 0) {
|
|
191
234
|
params.expires = Number(rawParams.expires);
|
|
192
235
|
}
|
|
@@ -199,20 +242,43 @@ function parseDictionaryMember(member) {
|
|
|
199
242
|
return { label, params };
|
|
200
243
|
}
|
|
201
244
|
function parseInnerList(content) {
|
|
245
|
+
const trimmed = content.trim();
|
|
246
|
+
if (trimmed === "") {
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
202
249
|
const items = [];
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
250
|
+
let pos = 0;
|
|
251
|
+
while (pos < trimmed.length) {
|
|
252
|
+
if (trimmed[pos] === " ") {
|
|
253
|
+
pos++;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (trimmed[pos] !== '"') {
|
|
257
|
+
throw new HttpSignatureError(
|
|
258
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
259
|
+
`Malformed covered-component list near: ${trimmed.slice(pos)}`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
const end = trimmed.indexOf('"', pos + 1);
|
|
263
|
+
if (end === -1) {
|
|
264
|
+
throw new HttpSignatureError(
|
|
265
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
266
|
+
"Unterminated quoted string in covered-component list"
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
items.push(trimmed.slice(pos + 1, end));
|
|
270
|
+
pos = end + 1;
|
|
207
271
|
}
|
|
208
272
|
return items;
|
|
209
273
|
}
|
|
274
|
+
var ALLOWED_PARAMS = /* @__PURE__ */ new Set(["keyid", "alg", "created", "expires", "nonce", "tag"]);
|
|
210
275
|
function parseParameters(paramsString) {
|
|
211
|
-
const params =
|
|
276
|
+
const params = /* @__PURE__ */ Object.create(null);
|
|
212
277
|
const parts = paramsString.split(";").filter((p) => p.trim());
|
|
213
278
|
for (const part of parts) {
|
|
214
279
|
const [key, value] = splitKeyValue(part.trim());
|
|
215
280
|
if (!key) continue;
|
|
281
|
+
if (!ALLOWED_PARAMS.has(key)) continue;
|
|
216
282
|
if (!value) {
|
|
217
283
|
params[key] = true;
|
|
218
284
|
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
@@ -225,9 +291,18 @@ function parseParameters(paramsString) {
|
|
|
225
291
|
}
|
|
226
292
|
return params;
|
|
227
293
|
}
|
|
294
|
+
var SF_BASE64_ALPHABET = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
295
|
+
var SF_BASE64_CANONICAL_PADDED = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
296
|
+
function isValidSfBase64(value) {
|
|
297
|
+
if (value.length === 0) return false;
|
|
298
|
+
if (!SF_BASE64_ALPHABET.test(value)) return false;
|
|
299
|
+
if (value.includes("=")) {
|
|
300
|
+
return SF_BASE64_CANONICAL_PADDED.test(value);
|
|
301
|
+
}
|
|
302
|
+
return value.length % 4 !== 1;
|
|
303
|
+
}
|
|
228
304
|
function base64ToBytes(base64) {
|
|
229
|
-
const
|
|
230
|
-
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
305
|
+
const padded = base64.includes("=") ? base64 : base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
231
306
|
const binary = atob(padded);
|
|
232
307
|
const bytes = new Uint8Array(binary.length);
|
|
233
308
|
for (let i = 0; i < binary.length; i++) {
|
|
@@ -237,13 +312,21 @@ function base64ToBytes(base64) {
|
|
|
237
312
|
}
|
|
238
313
|
|
|
239
314
|
// src/base.ts
|
|
240
|
-
function buildSignatureBase(request, params) {
|
|
315
|
+
function buildSignatureBase(request, params, options = {}) {
|
|
241
316
|
const lines = [];
|
|
317
|
+
const seenComponents = /* @__PURE__ */ new Set();
|
|
242
318
|
for (const component of params.coveredComponents) {
|
|
319
|
+
if (seenComponents.has(component)) {
|
|
320
|
+
throw new HttpSignatureError(
|
|
321
|
+
ErrorCodes.SIGNATURE_INPUT_MALFORMED,
|
|
322
|
+
`Duplicate covered component: ${component}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
seenComponents.add(component);
|
|
243
326
|
const value = getComponentValue(request, component);
|
|
244
327
|
lines.push(`"${component}": ${value}`);
|
|
245
328
|
}
|
|
246
|
-
const paramsLine = buildSignatureParamsLine(params);
|
|
329
|
+
const paramsLine = options.preferSerializedParams && params.signatureParamsValue !== void 0 ? params.signatureParamsValue : buildSignatureParamsLine(params);
|
|
247
330
|
lines.push(`"@signature-params": ${paramsLine}`);
|
|
248
331
|
return lines.join("\n");
|
|
249
332
|
}
|
|
@@ -317,7 +400,9 @@ function getHeaderValue(headers, name) {
|
|
|
317
400
|
function buildSignatureParamsLine(params) {
|
|
318
401
|
const components = params.coveredComponents.map((c) => `"${c}"`).join(" ");
|
|
319
402
|
let line = `(${components})`;
|
|
320
|
-
|
|
403
|
+
if (params.created !== void 0) {
|
|
404
|
+
line += `;created=${params.created}`;
|
|
405
|
+
}
|
|
321
406
|
if (params.expires !== void 0) {
|
|
322
407
|
line += `;expires=${params.expires}`;
|
|
323
408
|
}
|
|
@@ -325,7 +410,9 @@ function buildSignatureParamsLine(params) {
|
|
|
325
410
|
line += `;nonce="${params.nonce}"`;
|
|
326
411
|
}
|
|
327
412
|
line += `;keyid="${params.keyid}"`;
|
|
328
|
-
|
|
413
|
+
if (params.alg !== void 0) {
|
|
414
|
+
line += `;alg="${params.alg}"`;
|
|
415
|
+
}
|
|
329
416
|
if (params.tag !== void 0) {
|
|
330
417
|
line += `;tag="${params.tag}"`;
|
|
331
418
|
}
|
|
@@ -399,6 +486,9 @@ function isExpired(params, now = Math.floor(Date.now() / 1e3)) {
|
|
|
399
486
|
return now > params.expires;
|
|
400
487
|
}
|
|
401
488
|
function isCreatedInFuture(params, now = Math.floor(Date.now() / 1e3), skewSeconds = 60) {
|
|
489
|
+
if (params.created === void 0) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
402
492
|
return params.created > now + skewSeconds;
|
|
403
493
|
}
|
|
404
494
|
async function isEd25519WebCryptoSupported() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/parser.ts","../src/base.ts","../src/verify.ts"],"names":[],"mappings":";AAIO,IAAM,UAAA,GAAa;AAAA;AAAA,EAExB,yBAAA,EAA2B,6BAAA;AAAA;AAAA,EAE3B,iBAAA,EAAmB,qBAAA;AAAA;AAAA,EAEnB,uBAAA,EAAyB,2BAAA;AAAA;AAAA,EAEzB,+BAAA,EAAiC,mCAAA;AAAA;AAAA,EAEjC,iBAAA,EAAmB,qBAAA;AAAA;AAAA,EAEnB,gBAAA,EAAkB,oBAAA;AAAA;AAAA,EAElB,iBAAA,EAAmB,qBAAA;AAAA;AAAA,EAEnB,qBAAA,EAAuB,yBAAA;AAAA;AAAA,EAEvB,aAAA,EAAe;AACjB;AAOO,IAAM,eAAA,GAA6C;AAAA,EACxD,CAAC,UAAA,CAAW,yBAAyB,GAAG,GAAA;AAAA,EACxC,CAAC,UAAA,CAAW,iBAAiB,GAAG,GAAA;AAAA,EAChC,CAAC,UAAA,CAAW,uBAAuB,GAAG,GAAA;AAAA,EACtC,CAAC,UAAA,CAAW,+BAA+B,GAAG,GAAA;AAAA,EAC9C,CAAC,UAAA,CAAW,iBAAiB,GAAG,GAAA;AAAA,EAChC,CAAC,UAAA,CAAW,gBAAgB,GAAG,GAAA;AAAA,EAC/B,CAAC,UAAA,CAAW,iBAAiB,GAAG,GAAA;AAAA,EAChC,CAAC,UAAA,CAAW,qBAAqB,GAAG,GAAA;AAAA,EACpC,CAAC,UAAA,CAAW,aAAa,GAAG;AAC9B;AAKO,IAAM,kBAAA,GAAN,cAAiC,KAAA,CAAM;AAAA,EACnC,IAAA;AAAA,EACA,UAAA;AAAA,EAET,WAAA,CAAY,MAAiB,OAAA,EAAiB;AAC5C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,gBAAgB,IAAI,CAAA;AAAA,EACxC;AACF;;;ACrCO,SAAS,oBAAoB,WAAA,EAAyD;AAC3F,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAmC;AAGvD,EAAA,MAAM,OAAA,GAAU,uBAAuB,WAAW,CAAA;AAElD,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAM,MAAA,GAAS,qBAAA,CAAsB,MAAA,CAAO,IAAA,EAAM,CAAA;AAClD,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,MAAM,CAAA;AAAA,IACzC;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;AAUO,SAAS,qBACd,WAAA,EACoD;AACpD,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAmD;AAEvE,EAAA,MAAM,OAAA,GAAU,uBAAuB,WAAW,CAAA;AAElD,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAM,CAAC,KAAA,EAAO,KAAK,IAAI,aAAA,CAAc,MAAA,CAAO,MAAM,CAAA;AAClD,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,KAAA,EAAO;AAGtB,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,yBAAyB,CAAA;AACnD,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAM,MAAA,GAAS,MAAM,CAAC,CAAA;AACtB,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,cAAc,MAAM,CAAA;AAClC,MAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,EAAO,EAAE,KAAA,EAAO,QAAQ,CAAA;AAAA,IACtC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;AAUO,SAAS,cAAA,CACd,cAAA,EACA,SAAA,EACA,KAAA,EACiB;AACjB,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,yBAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,IAAI,kBAAA,CAAmB,UAAA,CAAW,iBAAA,EAAmB,0BAA0B,CAAA;AAAA,EACvF;AAEA,EAAA,MAAM,QAAA,GAAW,oBAAoB,cAAc,CAAA;AACnD,EAAA,MAAM,MAAA,GAAS,qBAAqB,SAAS,CAAA;AAE7C,EAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,yBAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AAGA,EAAA,MAAM,cAAc,KAAA,IAAS,QAAA,CAAS,IAAA,EAAK,CAAE,MAAK,CAAE,KAAA;AACpD,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,MAAM,IAAI,kBAAA,CAAmB,UAAA,CAAW,yBAAA,EAA2B,0BAA0B,CAAA;AAAA,EAC/F;AAEA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,WAAW,CAAA;AACvC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,yBAAA;AAAA,MACX,oBAAoB,WAAW,CAAA,8BAAA;AAAA,KACjC;AAAA,EACF;AAEA,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,GAAA,CAAI,WAAW,CAAA;AACtC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,iBAAA;AAAA,MACX,oBAAoB,WAAW,CAAA,+BAAA;AAAA,KACjC;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,OAAO,KAAA,EAAO;AACjB,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,uBAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,OAAO,GAAA,EAAK;AACf,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,uBAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,MAAA,CAAO,OAAA,KAAY,MAAA,IAAa,MAAA,CAAO,YAAY,IAAA,EAAM;AAC3D,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,uBAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,WAAA;AAAA,IACP,MAAA;AAAA,IACA,gBAAgB,OAAA,CAAQ,KAAA;AAAA,IACxB,iBAAiB,OAAA,CAAQ;AAAA,GAC3B;AACF;AAOA,SAAS,uBAAuB,KAAA,EAAyB;AACvD,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,OAAA,GAAU,EAAA;AACd,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAEpB,IAAA,IAAI,IAAA,KAAS,GAAA,IAAO,CAAC,SAAA,EAAW;AAC9B,MAAA,QAAA,GAAW,CAAC,QAAA;AACZ,MAAA,OAAA,IAAW,IAAA;AAAA,IACb,CAAA,MAAA,IAAW,IAAA,KAAS,GAAA,IAAO,CAAC,QAAA,EAAU;AACpC,MAAA,SAAA,GAAY,CAAC,SAAA;AACb,MAAA,OAAA,IAAW,IAAA;AAAA,IACb,WAAW,IAAA,KAAS,GAAA,IAAO,CAAC,QAAA,IAAY,CAAC,SAAA,EAAW;AAClD,MAAA,KAAA,EAAA;AACA,MAAA,OAAA,IAAW,IAAA;AAAA,IACb,WAAW,IAAA,KAAS,GAAA,IAAO,CAAC,QAAA,IAAY,CAAC,SAAA,EAAW;AAClD,MAAA,KAAA,EAAA;AACA,MAAA,OAAA,IAAW,IAAA;AAAA,IACb,CAAA,MAAA,IAAW,SAAS,GAAA,IAAO,KAAA,KAAU,KAAK,CAAC,QAAA,IAAY,CAAC,SAAA,EAAW;AACjE,MAAA,IAAI,OAAA,CAAQ,MAAK,EAAG;AAClB,QAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,CAAA;AAAA,MAC7B;AACA,MAAA,OAAA,GAAU,EAAA;AAAA,IACZ,CAAA,MAAO;AACL,MAAA,OAAA,IAAW,IAAA;AAAA,IACb;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,CAAQ,MAAK,EAAG;AAClB,IAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,CAAA;AAAA,EAC7B;AAEA,EAAA,OAAO,OAAA;AACT;AAKA,SAAS,cAAc,MAAA,EAAkC;AACvD,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA;AAClC,EAAA,IAAI,YAAY,EAAA,EAAI;AAClB,IAAA,OAAO,CAAC,QAAQ,EAAE,CAAA;AAAA,EACpB;AACA,EAAA,OAAO,CAAC,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,OAAO,GAAG,MAAA,CAAO,KAAA,CAAM,OAAA,GAAU,CAAC,CAAC,CAAA;AAC7D;AAKA,SAAS,sBACP,MAAA,EACyD;AACzD,EAAA,MAAM,CAAC,KAAA,EAAO,IAAI,CAAA,GAAI,cAAc,MAAM,CAAA;AAC1C,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,IAAA,EAAM,OAAO,IAAA;AAI5B,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,KAAA,CAAM,mBAAmB,CAAA;AACrD,EAAA,IAAI,CAAC,gBAAgB,OAAO,IAAA;AAE5B,EAAA,MAAM,gBAAA,GAAmB,eAAe,CAAC,CAAA;AACzC,EAAA,MAAM,YAAA,GAAe,eAAe,CAAC,CAAA;AAGrC,EAAA,MAAM,iBAAA,GAAoB,eAAe,gBAAgB,CAAA;AAGzD,EAAA,MAAM,SAAA,GAAY,gBAAgB,YAAY,CAAA;AAE9C,EAAA,MAAM,MAAA,GAAgC;AAAA,IACpC,KAAA,EAAO,MAAA,CAAO,SAAA,CAAU,KAAA,IAAS,EAAE,CAAA;AAAA,IACnC,GAAA,EAAK,MAAA,CAAO,SAAA,CAAU,GAAA,IAAO,EAAE,CAAA;AAAA,IAC/B,SAAS,SAAA,CAAU,OAAA,KAAY,SAAY,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA,GAAI,CAAA;AAAA,IACvE;AAAA,GACF;AAEA,EAAA,IAAI,SAAA,CAAU,YAAY,MAAA,EAAW;AACnC,IAAA,MAAA,CAAO,OAAA,GAAU,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA;AAAA,EAC3C;AAEA,EAAA,IAAI,SAAA,CAAU,UAAU,MAAA,EAAW;AACjC,IAAA,MAAA,CAAO,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA;AAAA,EACvC;AAEA,EAAA,IAAI,SAAA,CAAU,QAAQ,MAAA,EAAW;AAC/B,IAAA,MAAA,CAAO,GAAA,GAAM,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EACnC;AAEA,EAAA,OAAO,EAAE,OAAO,MAAA,EAAO;AACzB;AAKA,SAAS,eAAe,OAAA,EAA2B;AACjD,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,KAAA,GAAQ,YAAA;AACd,EAAA,IAAI,KAAA;AACJ,EAAA,OAAA,CAAQ,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,OAAO,OAAO,IAAA,EAAM;AAC7C,IAAA,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,EACrB;AACA,EAAA,OAAO,KAAA;AACT;AAKA,SAAS,gBAAgB,YAAA,EAAuD;AAC9E,EAAA,MAAM,SAA0C,EAAC;AAGjD,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AAE5D,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,CAAC,GAAA,EAAK,KAAK,IAAI,aAAA,CAAc,IAAA,CAAK,MAAM,CAAA;AAC9C,IAAA,IAAI,CAAC,GAAA,EAAK;AAGV,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA;AAAA,IAChB,CAAA,MAAA,IAAW,MAAM,UAAA,CAAW,GAAG,KAAK,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,EAAG;AAEvD,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,IACjC,CAAA,MAAA,IAAW,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA,EAAG;AAEhC,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,QAAA,CAAS,KAAA,EAAO,EAAE,CAAA;AAAA,IAClC,CAAA,MAAO;AAEL,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,KAAA;AAAA,IAChB;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAKA,SAAS,cAAc,MAAA,EAA4B;AAEjD,EAAA,MAAM,UAAA,GAAa,OAAO,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AAG9D,EAAA,MAAM,MAAA,GAAS,UAAA,CAAW,MAAA,CAAO,UAAA,CAAW,MAAA,GAAA,CAAW,IAAK,UAAA,CAAW,MAAA,GAAS,CAAA,IAAM,CAAA,EAAI,GAAG,CAAA;AAE7F,EAAA,MAAM,MAAA,GAAS,KAAK,MAAM,CAAA;AAC1B,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,MAAA,CAAO,MAAM,CAAA;AAC1C,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACtC,IAAA,KAAA,CAAM,CAAC,CAAA,GAAI,MAAA,CAAO,UAAA,CAAW,CAAC,CAAA;AAAA,EAChC;AACA,EAAA,OAAO,KAAA;AACT;;;AC3SO,SAAS,kBAAA,CACd,SACA,MAAA,EACQ;AACR,EAAA,MAAM,QAAkB,EAAC;AAGzB,EAAA,KAAA,MAAW,SAAA,IAAa,OAAO,iBAAA,EAAmB;AAChD,IAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,OAAA,EAAS,SAAS,CAAA;AAClD,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,CAAA,EAAI,SAAS,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA;AAAA,EACvC;AAGA,EAAA,MAAM,UAAA,GAAa,yBAAyB,MAAM,CAAA;AAClD,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,qBAAA,EAAwB,UAAU,CAAA,CAAE,CAAA;AAE/C,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAKA,SAAS,iBAAA,CAAkB,SAA2B,SAAA,EAA2B;AAE/E,EAAA,IAAI,SAAA,CAAU,UAAA,CAAW,GAAG,CAAA,EAAG;AAC7B,IAAA,OAAO,wBAAA,CAAyB,SAAS,SAAS,CAAA;AAAA,EACpD;AAGA,EAAA,OAAO,cAAA,CAAe,OAAA,CAAQ,OAAA,EAAS,SAAS,CAAA;AAClD;AAKA,SAAS,wBAAA,CAAyB,SAA2B,SAAA,EAA2B;AACtF,EAAA,QAAQ,SAAA;AAAW,IACjB,KAAK,SAAA;AACH,MAAA,OAAO,OAAA,CAAQ,OAAO,WAAA,EAAY;AAAA,IAEpC,KAAK,aAAA;AACH,MAAA,OAAO,OAAA,CAAQ,GAAA;AAAA,IAEjB,KAAK,YAAA,EAAc;AACjB,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,OAAO,GAAA,CAAI,IAAA;AAAA,MACb,CAAA,CAAA,MAAQ;AAEN,QAAA,OAAO,cAAA,CAAe,OAAA,CAAQ,OAAA,EAAS,MAAM,CAAA;AAAA,MAC/C;AAAA,IACF;AAAA,IAEA,KAAK,SAAA,EAAW;AACd,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,OAAO,GAAA,CAAI,QAAA,CAAS,OAAA,CAAQ,GAAA,EAAK,EAAE,CAAA;AAAA,MACrC,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,OAAA;AAAA,MACT;AAAA,IACF;AAAA,IAEA,KAAK,iBAAA,EAAmB;AACtB,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,OAAO,GAAA,CAAI,WAAW,GAAA,CAAI,MAAA;AAAA,MAC5B,CAAA,CAAA,MAAQ;AAEN,QAAA,OAAO,OAAA,CAAQ,GAAA;AAAA,MACjB;AAAA,IACF;AAAA,IAEA,KAAK,OAAA,EAAS;AACZ,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,OAAO,GAAA,CAAI,QAAA;AAAA,MACb,CAAA,CAAA,MAAQ;AACN,QAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA;AAC5C,QAAA,OAAO,SAAA,GAAY,SAAA,CAAU,CAAC,CAAA,GAAI,OAAA,CAAQ,GAAA;AAAA,MAC5C;AAAA,IACF;AAAA,IAEA,KAAK,QAAA,EAAU;AACb,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,OAAO,IAAI,MAAA,IAAU,GAAA;AAAA,MACvB,CAAA,CAAA,MAAQ;AACN,QAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,MAAM,CAAA;AAC3C,QAAA,OAAO,UAAA,GAAa,UAAA,CAAW,CAAC,CAAA,GAAI,GAAA;AAAA,MACtC;AAAA,IACF;AAAA,IAEA;AAEE,MAAA,OAAO,EAAA;AAAA;AAEb;AAKA,SAAS,cAAA,CAAe,SAAiC,IAAA,EAAsB;AAC7E,EAAA,MAAM,SAAA,GAAY,KAAK,WAAA,EAAY;AAEnC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,IAAA,IAAI,GAAA,CAAI,WAAA,EAAY,KAAM,SAAA,EAAW;AACnC,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,EAAA;AACT;AAOA,SAAS,yBAAyB,MAAA,EAAuC;AAEvE,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,iBAAA,CAAkB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACzE,EAAA,IAAI,IAAA,GAAO,IAAI,UAAU,CAAA,CAAA,CAAA;AAGzB,EAAA,IAAA,IAAQ,CAAA,SAAA,EAAY,OAAO,OAAO,CAAA,CAAA;AAElC,EAAA,IAAI,MAAA,CAAO,YAAY,MAAA,EAAW;AAChC,IAAA,IAAA,IAAQ,CAAA,SAAA,EAAY,OAAO,OAAO,CAAA,CAAA;AAAA,EACpC;AAEA,EAAA,IAAI,MAAA,CAAO,UAAU,MAAA,EAAW;AAC9B,IAAA,IAAA,IAAQ,CAAA,QAAA,EAAW,OAAO,KAAK,CAAA,CAAA,CAAA;AAAA,EACjC;AAEA,EAAA,IAAA,IAAQ,CAAA,QAAA,EAAW,OAAO,KAAK,CAAA,CAAA,CAAA;AAC/B,EAAA,IAAA,IAAQ,CAAA,MAAA,EAAS,OAAO,GAAG,CAAA,CAAA,CAAA;AAE3B,EAAA,IAAI,MAAA,CAAO,QAAQ,MAAA,EAAW;AAC5B,IAAA,IAAA,IAAQ,CAAA,MAAA,EAAS,OAAO,GAAG,CAAA,CAAA,CAAA;AAAA,EAC7B;AAEA,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,qBAAqB,aAAA,EAAmC;AACtE,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,OAAO,OAAA,CAAQ,OAAO,aAAa,CAAA;AACrC;;;AC7IA,eAAsB,eAAA,CACpB,SACA,OAAA,EAC6B;AAC7B,EAAA,MAAM,EAAE,WAAA,EAAa,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,EAAI,GAAI,GAAI,CAAA,EAAG,gBAAA,GAAmB,EAAA,EAAG,GAAI,OAAA;AAEpF,EAAA,IAAI;AAEF,IAAA,MAAM,cAAA,GAAiB,SAAA,CAAU,OAAA,CAAQ,OAAA,EAAS,iBAAiB,CAAA;AACnE,IAAA,MAAM,SAAA,GAAY,SAAA,CAAU,OAAA,CAAQ,OAAA,EAAS,WAAW,CAAA;AAGxD,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,cAAA,EAAgB,SAAA,EAAW,QAAQ,KAAK,CAAA;AAGtE,IAAA,IAAI,MAAA,CAAO,MAAA,CAAO,GAAA,KAAQ,SAAA,EAAW;AACnC,MAAA,MAAM,IAAI,kBAAA;AAAA,QACR,UAAA,CAAW,+BAAA;AAAA,QACX,CAAA,uBAAA,EAA0B,MAAA,CAAO,MAAA,CAAO,GAAG,CAAA,4BAAA;AAAA,OAC7C;AAAA,IACF;AAGA,IAAA,uBAAA,CAAwB,MAAA,CAAO,MAAA,EAAQ,GAAA,EAAK,gBAAgB,CAAA;AAG5D,IAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,MAAA,CAAO,OAAO,KAAK,CAAA;AACtD,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,kBAAA;AAAA,QACR,UAAA,CAAW,aAAA;AAAA,QACX,CAAA,eAAA,EAAkB,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AAAA,OACvC;AAAA,IACF;AAGA,IAAA,MAAM,aAAA,GAAgB,kBAAA,CAAmB,OAAA,EAAS,MAAA,CAAO,MAAM,CAAA;AAC/D,IAAA,MAAM,kBAAA,GAAqB,qBAAqB,aAAa,CAAA;AAG7D,IAAA,MAAM,KAAA,GAAQ,MAAM,QAAA,CAAS,kBAAA,EAAoB,OAAO,cAAc,CAAA;AAEtE,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,MAAM,IAAI,kBAAA,CAAmB,UAAA,CAAW,iBAAA,EAAmB,+BAA+B,CAAA;AAAA,IAC5F;AAEA,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,IAAA;AAAA,MACP,SAAA,EAAW;AAAA,KACb;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,kBAAA,EAAoB;AACvC,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,KAAA;AAAA,QACP,WAAW,KAAA,CAAM,IAAA;AAAA,QACjB,cAAc,KAAA,CAAM;AAAA,OACtB;AAAA,IACF;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AAKA,SAAS,uBAAA,CACP,MAAA,EACA,GAAA,EACA,gBAAA,EACM;AAEN,EAAA,IAAI,iBAAA,CAAkB,MAAA,EAAQ,GAAA,EAAK,gBAAgB,CAAA,EAAG;AACpD,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,gBAAA;AAAA,MACX,CAAA,6BAAA,EAAgC,MAAA,CAAO,OAAO,CAAA,GAAA,EAAM,MAAM,gBAAgB,CAAA;AAAA,KAC5E;AAAA,EACF;AAGA,EAAA,IAAI,SAAA,CAAU,MAAA,EAAQ,GAAG,CAAA,EAAG;AAC1B,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,UAAA,CAAW,iBAAA;AAAA,MACX,CAAA,mBAAA,EAAsB,MAAA,CAAO,OAAO,CAAA,GAAA,EAAM,GAAG,CAAA;AAAA,KAC/C;AAAA,EACF;AACF;AASO,SAAS,SAAA,CACd,QACA,GAAA,GAAc,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,EAAI,GAAI,GAAI,CAAA,EACjC;AACT,EAAA,IAAI,MAAA,CAAO,YAAY,MAAA,EAAW;AAChC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAM,MAAA,CAAO,OAAA;AACtB;AAUO,SAAS,iBAAA,CACd,MAAA,EACA,GAAA,GAAc,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,EAAI,GAAI,GAAI,CAAA,EAC1C,WAAA,GAAsB,EAAA,EACb;AACT,EAAA,OAAO,MAAA,CAAO,UAAU,GAAA,GAAM,WAAA;AAChC;AAOA,eAAsB,2BAAA,GAAgD;AACpE,EAAA,IAAI;AAEF,IAAA,MAAM,UAAU,MAAM,UAAA,CAAW,OAAO,MAAA,CAAO,WAAA,CAAY,WAAW,KAAA,EAAO;AAAA,MAC3E,MAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,OAAO,OAAA,KAAY,IAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAWO,SAAS,wBAAwB,GAAA,EAAiC;AACvE,EAAA,OAAO,OAAO,MAAkB,SAAA,KAA4C;AAE1E,IAAA,MAAM,SAAA,GAAY,IAAI,UAAA,CAAW,SAAS,CAAA,CAAE,MAAA;AAC5C,IAAA,MAAM,UAAA,GAAa,IAAI,UAAA,CAAW,IAAI,CAAA,CAAE,MAAA;AAKxC,IAAA,OAAO,WAAW,MAAA,CAAO,MAAA,CAAO,OAAO,SAAA,EAAW,GAAA,EAAkB,WAAW,UAAU,CAAA;AAAA,EAC3F,CAAA;AACF;AAKA,SAAS,SAAA,CAAU,SAAiC,IAAA,EAAsB;AACxE,EAAA,MAAM,SAAA,GAAY,KAAK,WAAA,EAAY;AACnC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,IAAA,IAAI,GAAA,CAAI,WAAA,EAAY,KAAM,SAAA,EAAW;AACnC,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,EAAA;AACT","file":"index.js","sourcesContent":["/**\n * HTTP Signature error codes per execution pack specification.\n */\n\nexport const ErrorCodes = {\n /** Signature-Input header parse failed */\n SIGNATURE_INPUT_MALFORMED: 'E_SIGNATURE_INPUT_MALFORMED',\n /** No Signature header present */\n SIGNATURE_MISSING: 'E_SIGNATURE_MISSING',\n /** Required param (created/keyid/alg) missing */\n SIGNATURE_PARAM_MISSING: 'E_SIGNATURE_PARAM_MISSING',\n /** Algorithm not ed25519 */\n SIGNATURE_ALGORITHM_UNSUPPORTED: 'E_SIGNATURE_ALGORITHM_UNSUPPORTED',\n /** Signature expired (now > expires) */\n SIGNATURE_EXPIRED: 'E_SIGNATURE_EXPIRED',\n /** Signature from future (created > now + skew) */\n SIGNATURE_FUTURE: 'E_SIGNATURE_FUTURE',\n /** Cryptographic verification failed */\n SIGNATURE_INVALID: 'E_SIGNATURE_INVALID',\n /** Ed25519 WebCrypto not supported */\n WEBCRYPTO_UNAVAILABLE: 'E_WEBCRYPTO_UNAVAILABLE',\n /** Key not found by resolver */\n KEY_NOT_FOUND: 'E_KEY_NOT_FOUND',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n/**\n * HTTP status codes for each error.\n */\nexport const ErrorHttpStatus: Record<ErrorCode, number> = {\n [ErrorCodes.SIGNATURE_INPUT_MALFORMED]: 400,\n [ErrorCodes.SIGNATURE_MISSING]: 401,\n [ErrorCodes.SIGNATURE_PARAM_MISSING]: 400,\n [ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED]: 400,\n [ErrorCodes.SIGNATURE_EXPIRED]: 401,\n [ErrorCodes.SIGNATURE_FUTURE]: 401,\n [ErrorCodes.SIGNATURE_INVALID]: 401,\n [ErrorCodes.WEBCRYPTO_UNAVAILABLE]: 500,\n [ErrorCodes.KEY_NOT_FOUND]: 401,\n};\n\n/**\n * HTTP Signature error with code and HTTP status.\n */\nexport class HttpSignatureError extends Error {\n readonly code: ErrorCode;\n readonly httpStatus: number;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = 'HttpSignatureError';\n this.code = code;\n this.httpStatus = ErrorHttpStatus[code];\n }\n}\n","/**\n * Parser for RFC 9421 Signature-Input and Signature headers.\n *\n * Implements minimal RFC 9651 Structured Fields parsing for\n * Dictionary and Inner List types as needed by HTTP Signatures.\n */\n\nimport { ParsedSignatureParams, ParsedSignature } from './types.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Parse Signature-Input header value into structured parameters.\n *\n * Format: label=(\"component1\" \"component2\");param1=value1;param2=value2\n *\n * @param headerValue - Raw Signature-Input header value\n * @returns Map of label to parsed parameters\n */\nexport function parseSignatureInput(headerValue: string): Map<string, ParsedSignatureParams> {\n const results = new Map<string, ParsedSignatureParams>();\n\n // Split by comma for multiple signatures (outer dictionary members)\n const members = splitDictionaryMembers(headerValue);\n\n for (const member of members) {\n const parsed = parseDictionaryMember(member.trim());\n if (parsed) {\n results.set(parsed.label, parsed.params);\n }\n }\n\n return results;\n}\n\n/**\n * Parse Signature header value into raw signature bytes.\n *\n * Format: label=:base64signature:\n *\n * @param headerValue - Raw Signature header value\n * @returns Map of label to signature bytes\n */\nexport function parseSignatureHeader(\n headerValue: string\n): Map<string, { bytes: Uint8Array; base64: string }> {\n const results = new Map<string, { bytes: Uint8Array; base64: string }>();\n\n const members = splitDictionaryMembers(headerValue);\n\n for (const member of members) {\n const [label, value] = splitKeyValue(member.trim());\n if (!label || !value) continue;\n\n // Extract base64 from :...: byte sequence format\n const match = value.match(/^:([A-Za-z0-9+/=_-]+):$/);\n if (!match) continue;\n\n const base64 = match[1];\n try {\n const bytes = base64ToBytes(base64);\n results.set(label, { bytes, base64 });\n } catch {\n // Skip invalid base64\n }\n }\n\n return results;\n}\n\n/**\n * Parse complete signature from both headers.\n *\n * @param signatureInput - Signature-Input header value\n * @param signature - Signature header value\n * @param label - Optional specific label to parse (defaults to first)\n * @returns Parsed signature or throws HttpSignatureError\n */\nexport function parseSignature(\n signatureInput: string,\n signature: string,\n label?: string\n): ParsedSignature {\n if (!signatureInput) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Missing Signature-Input header'\n );\n }\n\n if (!signature) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_MISSING, 'Missing Signature header');\n }\n\n const inputMap = parseSignatureInput(signatureInput);\n const sigMap = parseSignatureHeader(signature);\n\n if (inputMap.size === 0) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Failed to parse Signature-Input header'\n );\n }\n\n // Use specified label or first available\n const targetLabel = label ?? inputMap.keys().next().value;\n if (!targetLabel) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INPUT_MALFORMED, 'No signature label found');\n }\n\n const params = inputMap.get(targetLabel);\n if (!params) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Signature label \"${targetLabel}\" not found in Signature-Input`\n );\n }\n\n const sigData = sigMap.get(targetLabel);\n if (!sigData) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_MISSING,\n `Signature label \"${targetLabel}\" not found in Signature header`\n );\n }\n\n // Validate required parameters\n if (!params.keyid) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: keyid'\n );\n }\n\n if (!params.alg) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: alg'\n );\n }\n\n if (params.created === undefined || params.created === null) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: created'\n );\n }\n\n return {\n label: targetLabel,\n params,\n signatureBytes: sigData.bytes,\n signatureBase64: sigData.base64,\n };\n}\n\n// --- Internal parsing helpers ---\n\n/**\n * Split dictionary members by comma, respecting inner lists and strings.\n */\nfunction splitDictionaryMembers(value: string): string[] {\n const members: string[] = [];\n let current = '';\n let depth = 0;\n let inString = false;\n let inByteSeq = false;\n\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n\n if (char === '\"' && !inByteSeq) {\n inString = !inString;\n current += char;\n } else if (char === ':' && !inString) {\n inByteSeq = !inByteSeq;\n current += char;\n } else if (char === '(' && !inString && !inByteSeq) {\n depth++;\n current += char;\n } else if (char === ')' && !inString && !inByteSeq) {\n depth--;\n current += char;\n } else if (char === ',' && depth === 0 && !inString && !inByteSeq) {\n if (current.trim()) {\n members.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n members.push(current.trim());\n }\n\n return members;\n}\n\n/**\n * Split a dictionary member into key=value pair.\n */\nfunction splitKeyValue(member: string): [string, string] {\n const eqIndex = member.indexOf('=');\n if (eqIndex === -1) {\n return [member, ''];\n }\n return [member.slice(0, eqIndex), member.slice(eqIndex + 1)];\n}\n\n/**\n * Parse a single dictionary member (label=inner-list;params).\n */\nfunction parseDictionaryMember(\n member: string\n): { label: string; params: ParsedSignatureParams } | null {\n const [label, rest] = splitKeyValue(member);\n if (!label || !rest) return null;\n\n // Parse inner list and parameters\n // Format: (\"component1\" \"component2\");param1=value1;param2=value2\n const innerListMatch = rest.match(/^\\(([^)]*)\\)(.*)$/);\n if (!innerListMatch) return null;\n\n const innerListContent = innerListMatch[1];\n const paramsString = innerListMatch[2];\n\n // Parse covered components from inner list\n const coveredComponents = parseInnerList(innerListContent);\n\n // Parse parameters\n const rawParams = parseParameters(paramsString);\n\n const params: ParsedSignatureParams = {\n keyid: String(rawParams.keyid ?? ''),\n alg: String(rawParams.alg ?? ''),\n created: rawParams.created !== undefined ? Number(rawParams.created) : 0,\n coveredComponents,\n };\n\n if (rawParams.expires !== undefined) {\n params.expires = Number(rawParams.expires);\n }\n\n if (rawParams.nonce !== undefined) {\n params.nonce = String(rawParams.nonce);\n }\n\n if (rawParams.tag !== undefined) {\n params.tag = String(rawParams.tag);\n }\n\n return { label, params };\n}\n\n/**\n * Parse inner list content (space-separated quoted strings).\n */\nfunction parseInnerList(content: string): string[] {\n const items: string[] = [];\n const regex = /\"([^\"]*)\"/g;\n let match;\n while ((match = regex.exec(content)) !== null) {\n items.push(match[1]);\n }\n return items;\n}\n\n/**\n * Parse parameters from ;key=value;key2=value2 format.\n */\nfunction parseParameters(paramsString: string): Record<string, string | number> {\n const params: Record<string, string | number> = {};\n\n // Split by semicolon\n const parts = paramsString.split(';').filter((p) => p.trim());\n\n for (const part of parts) {\n const [key, value] = splitKeyValue(part.trim());\n if (!key) continue;\n\n // Parse value - could be integer, string, or token\n if (!value) {\n params[key] = true as unknown as string;\n } else if (value.startsWith('\"') && value.endsWith('\"')) {\n // Quoted string\n params[key] = value.slice(1, -1);\n } else if (/^-?\\d+$/.test(value)) {\n // Integer\n params[key] = parseInt(value, 10);\n } else {\n // Token or other\n params[key] = value;\n }\n }\n\n return params;\n}\n\n/**\n * Decode base64 (standard or URL-safe) to bytes.\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n // Handle URL-safe base64\n const normalized = base64.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=');\n\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * Signature base construction per RFC 9421 Section 2.5.\n *\n * The signature base is a canonical string constructed from\n * covered components and signature parameters.\n */\n\nimport { ParsedSignatureParams, SignatureRequest } from './types.js';\n\n/**\n * Build signature base string for verification.\n *\n * @param request - Request data\n * @param params - Parsed signature parameters\n * @returns Signature base string\n */\nexport function buildSignatureBase(\n request: SignatureRequest,\n params: ParsedSignatureParams\n): string {\n const lines: string[] = [];\n\n // Add each covered component\n for (const component of params.coveredComponents) {\n const value = getComponentValue(request, component);\n lines.push(`\"${component}\": ${value}`);\n }\n\n // Add signature params line\n const paramsLine = buildSignatureParamsLine(params);\n lines.push(`\"@signature-params\": ${paramsLine}`);\n\n return lines.join('\\n');\n}\n\n/**\n * Get the canonical value for a component identifier.\n */\nfunction getComponentValue(request: SignatureRequest, component: string): string {\n // Derived components start with @\n if (component.startsWith('@')) {\n return getDerivedComponentValue(request, component);\n }\n\n // Otherwise it's a header field\n return getHeaderValue(request.headers, component);\n}\n\n/**\n * Get derived component value.\n */\nfunction getDerivedComponentValue(request: SignatureRequest, component: string): string {\n switch (component) {\n case '@method':\n return request.method.toUpperCase();\n\n case '@target-uri':\n return request.url;\n\n case '@authority': {\n try {\n const url = new URL(request.url);\n return url.host;\n } catch {\n // If URL parsing fails, try to extract from headers\n return getHeaderValue(request.headers, 'host');\n }\n }\n\n case '@scheme': {\n try {\n const url = new URL(request.url);\n return url.protocol.replace(':', '');\n } catch {\n return 'https';\n }\n }\n\n case '@request-target': {\n try {\n const url = new URL(request.url);\n return url.pathname + url.search;\n } catch {\n // If not a full URL, assume it's already a path\n return request.url;\n }\n }\n\n case '@path': {\n try {\n const url = new URL(request.url);\n return url.pathname;\n } catch {\n const pathMatch = request.url.match(/^[^?]*/);\n return pathMatch ? pathMatch[0] : request.url;\n }\n }\n\n case '@query': {\n try {\n const url = new URL(request.url);\n return url.search || '?';\n } catch {\n const queryMatch = request.url.match(/\\?.*/);\n return queryMatch ? queryMatch[0] : '?';\n }\n }\n\n default:\n // Unknown derived component - return empty\n return '';\n }\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeaderValue(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n\n return '';\n}\n\n/**\n * Build the signature-params line value.\n *\n * Format: (\"component1\" \"component2\");created=123;keyid=\"key\";alg=\"ed25519\"\n */\nfunction buildSignatureParamsLine(params: ParsedSignatureParams): string {\n // Build inner list of components\n const components = params.coveredComponents.map((c) => `\"${c}\"`).join(' ');\n let line = `(${components})`;\n\n // Add required parameters in canonical order\n line += `;created=${params.created}`;\n\n if (params.expires !== undefined) {\n line += `;expires=${params.expires}`;\n }\n\n if (params.nonce !== undefined) {\n line += `;nonce=\"${params.nonce}\"`;\n }\n\n line += `;keyid=\"${params.keyid}\"`;\n line += `;alg=\"${params.alg}\"`;\n\n if (params.tag !== undefined) {\n line += `;tag=\"${params.tag}\"`;\n }\n\n return line;\n}\n\n/**\n * Convert signature base string to bytes for cryptographic verification.\n */\nexport function signatureBaseToBytes(signatureBase: string): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(signatureBase);\n}\n","/**\n * HTTP Message Signature verification.\n *\n * Runtime-neutral verification using SignatureVerifier function type.\n * WebCrypto is used internally but NOT exposed in public API.\n */\n\nimport {\n SignatureVerifier,\n SignatureRequest,\n VerifyOptions,\n VerificationResult,\n ParsedSignatureParams,\n} from './types.js';\nimport { parseSignature } from './parser.js';\nimport { buildSignatureBase, signatureBaseToBytes } from './base.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Verify an HTTP Message Signature.\n *\n * @param request - Request data with headers\n * @param options - Verification options including key resolver\n * @returns Verification result\n */\nexport async function verifySignature(\n request: SignatureRequest,\n options: VerifyOptions\n): Promise<VerificationResult> {\n const { keyResolver, now = Math.floor(Date.now() / 1000), clockSkewSeconds = 60 } = options;\n\n try {\n // Get signature headers\n const signatureInput = getHeader(request.headers, 'signature-input');\n const signature = getHeader(request.headers, 'signature');\n\n // Parse signature\n const parsed = parseSignature(signatureInput, signature, options.label);\n\n // Validate algorithm\n if (parsed.params.alg !== 'ed25519') {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED,\n `Unsupported algorithm: ${parsed.params.alg} (only ed25519 is supported)`\n );\n }\n\n // Validate time constraints\n validateTimeConstraints(parsed.params, now, clockSkewSeconds);\n\n // Resolve key\n const verifier = await keyResolver(parsed.params.keyid);\n if (!verifier) {\n throw new HttpSignatureError(\n ErrorCodes.KEY_NOT_FOUND,\n `Key not found: ${parsed.params.keyid}`\n );\n }\n\n // Build signature base\n const signatureBase = buildSignatureBase(request, parsed.params);\n const signatureBaseBytes = signatureBaseToBytes(signatureBase);\n\n // Verify signature\n const valid = await verifier(signatureBaseBytes, parsed.signatureBytes);\n\n if (!valid) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INVALID, 'Signature verification failed');\n }\n\n return {\n valid: true,\n signature: parsed,\n };\n } catch (error) {\n if (error instanceof HttpSignatureError) {\n return {\n valid: false,\n errorCode: error.code,\n errorMessage: error.message,\n };\n }\n throw error;\n }\n}\n\n/**\n * Validate time constraints on signature parameters.\n */\nfunction validateTimeConstraints(\n params: ParsedSignatureParams,\n now: number,\n clockSkewSeconds: number\n): void {\n // Check if signature is from the future (with skew tolerance)\n if (isCreatedInFuture(params, now, clockSkewSeconds)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_FUTURE,\n `Signature created in future: ${params.created} > ${now + clockSkewSeconds}`\n );\n }\n\n // Check if signature is expired\n if (isExpired(params, now)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_EXPIRED,\n `Signature expired: ${params.expires} < ${now}`\n );\n }\n}\n\n/**\n * Check if signature is expired.\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @returns true if signature is expired\n */\nexport function isExpired(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000)\n): boolean {\n if (params.expires === undefined) {\n return false;\n }\n return now > params.expires;\n}\n\n/**\n * Check if signature was created in the future (accounting for clock skew).\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @param skewSeconds - Allowed clock skew in seconds (defaults to 60)\n * @returns true if signature is from the future\n */\nexport function isCreatedInFuture(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000),\n skewSeconds: number = 60\n): boolean {\n return params.created > now + skewSeconds;\n}\n\n/**\n * Check if Ed25519 WebCrypto is supported in current runtime.\n *\n * @returns true if Ed25519 WebCrypto is available\n */\nexport async function isEd25519WebCryptoSupported(): Promise<boolean> {\n try {\n // Try to generate a key pair to test support\n const keyPair = await globalThis.crypto.subtle.generateKey('Ed25519', false, [\n 'sign',\n 'verify',\n ]);\n return keyPair !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create a SignatureVerifier from a WebCrypto CryptoKey.\n *\n * This is a helper for consumers who have CryptoKey objects.\n * The function is runtime-neutral as it accepts unknown and casts internally.\n *\n * @param key - WebCrypto CryptoKey (passed as unknown for runtime neutrality)\n * @returns SignatureVerifier function\n */\nexport function createWebCryptoVerifier(key: unknown): SignatureVerifier {\n return async (data: Uint8Array, signature: Uint8Array): Promise<boolean> => {\n // Create proper ArrayBuffer views to satisfy TypeScript\n const sigBuffer = new Uint8Array(signature).buffer;\n const dataBuffer = new Uint8Array(data).buffer;\n\n // Use type from the actual WebCrypto API to avoid DOM type dependency\n type VerifyKey = Parameters<typeof globalThis.crypto.subtle.verify>[1];\n\n return globalThis.crypto.subtle.verify('Ed25519', key as VerifyKey, sigBuffer, dataBuffer);\n };\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeader(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n return '';\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/parser.ts","../src/base.ts","../src/verify.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * HTTP Signature error codes per execution pack specification.\n */\n\nexport const ErrorCodes = {\n /** Signature-Input header parse failed */\n SIGNATURE_INPUT_MALFORMED: 'E_SIGNATURE_INPUT_MALFORMED',\n /** No Signature header present */\n SIGNATURE_MISSING: 'E_SIGNATURE_MISSING',\n /** Required param (created/keyid/alg) missing */\n SIGNATURE_PARAM_MISSING: 'E_SIGNATURE_PARAM_MISSING',\n /** Algorithm not ed25519 */\n SIGNATURE_ALGORITHM_UNSUPPORTED: 'E_SIGNATURE_ALGORITHM_UNSUPPORTED',\n /** Signature expired (now > expires) */\n SIGNATURE_EXPIRED: 'E_SIGNATURE_EXPIRED',\n /** Signature from future (created > now + skew) */\n SIGNATURE_FUTURE: 'E_SIGNATURE_FUTURE',\n /** Cryptographic verification failed */\n SIGNATURE_INVALID: 'E_SIGNATURE_INVALID',\n /** Ed25519 WebCrypto not supported */\n WEBCRYPTO_UNAVAILABLE: 'E_WEBCRYPTO_UNAVAILABLE',\n /** Key not found by resolver */\n KEY_NOT_FOUND: 'E_KEY_NOT_FOUND',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n/**\n * HTTP status codes for each error.\n */\nexport const ErrorHttpStatus: Record<ErrorCode, number> = {\n [ErrorCodes.SIGNATURE_INPUT_MALFORMED]: 400,\n [ErrorCodes.SIGNATURE_MISSING]: 401,\n [ErrorCodes.SIGNATURE_PARAM_MISSING]: 400,\n [ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED]: 400,\n [ErrorCodes.SIGNATURE_EXPIRED]: 401,\n [ErrorCodes.SIGNATURE_FUTURE]: 401,\n [ErrorCodes.SIGNATURE_INVALID]: 401,\n [ErrorCodes.WEBCRYPTO_UNAVAILABLE]: 500,\n [ErrorCodes.KEY_NOT_FOUND]: 401,\n};\n\n/**\n * HTTP Signature error with code and HTTP status.\n */\nexport class HttpSignatureError extends Error {\n readonly code: ErrorCode;\n readonly httpStatus: number;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = 'HttpSignatureError';\n this.code = code;\n this.httpStatus = ErrorHttpStatus[code];\n }\n}\n","/**\n * Parser for RFC 9421 Signature-Input and Signature headers.\n *\n * Implements minimal RFC 9651 Structured Fields parsing for\n * Dictionary and Inner List types as needed by HTTP Signatures.\n */\n\nimport { ParsedSignatureParams, ParsedSignature } from './types.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Parse Signature-Input header value into structured parameters.\n *\n * Format: label=(\"component1\" \"component2\");param1=value1;param2=value2\n *\n * @param headerValue - Raw Signature-Input header value\n * @returns Map of label to parsed parameters\n */\nexport function parseSignatureInput(headerValue: string): Map<string, ParsedSignatureParams> {\n const results = new Map<string, ParsedSignatureParams>();\n\n // Split by comma for multiple signatures (outer dictionary members)\n const members = splitDictionaryMembers(headerValue);\n\n for (const member of members) {\n // Structured-field parsing is not best-effort: a malformed member fails the\n // whole header rather than being skipped.\n const parsed = parseDictionaryMember(member.trim());\n if (!parsed) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed Signature-Input member: ${member.trim()}`\n );\n }\n // RFC 9421: signature labels must be unique. Fail closed on a duplicate\n // rather than silently overwriting an earlier definition.\n if (results.has(parsed.label)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Duplicate signature label in Signature-Input: ${parsed.label}`\n );\n }\n results.set(parsed.label, parsed.params);\n }\n\n return results;\n}\n\n/**\n * Parse Signature header value into raw signature bytes.\n *\n * Format: label=:base64signature:\n *\n * @param headerValue - Raw Signature header value\n * @returns Map of label to signature bytes\n */\nexport function parseSignatureHeader(\n headerValue: string\n): Map<string, { bytes: Uint8Array; base64: string }> {\n const results = new Map<string, { bytes: Uint8Array; base64: string }>();\n\n const members = splitDictionaryMembers(headerValue);\n const seenLabels = new Set<string>();\n\n for (const member of members) {\n const trimmed = member.trim();\n const [label, value] = splitKeyValue(trimmed);\n // Fail closed on a malformed member rather than skipping it.\n if (!label || !value) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed Signature member: ${trimmed}`\n );\n }\n\n // RFC 9421: a label may appear at most once in the Signature header.\n if (seenLabels.has(label)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Duplicate signature label in Signature: ${label}`\n );\n }\n seenLabels.add(label);\n\n // The value MUST be a Structured Field Byte Sequence (:base64:) using\n // RFC 4648 Section 4 standard base64 (RFC 9651 Section 3.3.5), NOT\n // base64url. Capture the inner value, then validate Structured Field base64.\n const match = value.match(/^:([^:]*):$/);\n if (!match || !isValidSfBase64(match[1])) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed Signature byte sequence for label \"${label}\"`\n );\n }\n\n const base64 = match[1];\n let bytes: Uint8Array;\n try {\n bytes = base64ToBytes(base64);\n } catch {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Invalid base64 in Signature for label \"${label}\"`\n );\n }\n results.set(label, { bytes, base64 });\n }\n\n return results;\n}\n\n/**\n * Parse complete signature from both headers.\n *\n * @param signatureInput - Signature-Input header value\n * @param signature - Signature header value\n * @param label - Optional specific label to parse (defaults to first)\n * @returns Parsed signature or throws HttpSignatureError\n */\nexport function parseSignature(\n signatureInput: string,\n signature: string,\n label?: string,\n options: { requireAlg?: boolean; requireCreated?: boolean } = {}\n): ParsedSignature {\n const { requireAlg = true, requireCreated = true } = options;\n\n if (!signatureInput) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Missing Signature-Input header'\n );\n }\n\n if (!signature) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_MISSING, 'Missing Signature header');\n }\n\n const inputMap = parseSignatureInput(signatureInput);\n const sigMap = parseSignatureHeader(signature);\n\n if (inputMap.size === 0) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Failed to parse Signature-Input header'\n );\n }\n\n // Use specified label or first available\n const targetLabel = label ?? inputMap.keys().next().value;\n if (!targetLabel) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INPUT_MALFORMED, 'No signature label found');\n }\n\n const params = inputMap.get(targetLabel);\n if (!params) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Signature label \"${targetLabel}\" not found in Signature-Input`\n );\n }\n\n const sigData = sigMap.get(targetLabel);\n if (!sigData) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_MISSING,\n `Signature label \"${targetLabel}\" not found in Signature header`\n );\n }\n\n // Validate required parameters\n if (!params.keyid) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: keyid'\n );\n }\n\n if (requireAlg && !params.alg) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: alg'\n );\n }\n\n if (requireCreated && (params.created === undefined || params.created === null)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_PARAM_MISSING,\n 'Missing required parameter: created'\n );\n }\n\n return {\n label: targetLabel,\n params,\n signatureBytes: sigData.bytes,\n signatureBase64: sigData.base64,\n };\n}\n\n// --- Internal parsing helpers ---\n\n/**\n * Split dictionary members by comma, respecting inner lists and strings.\n */\nfunction splitDictionaryMembers(value: string): string[] {\n const members: string[] = [];\n let current = '';\n let depth = 0;\n let inString = false;\n let inByteSeq = false;\n\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n\n if (char === '\"' && !inByteSeq) {\n inString = !inString;\n current += char;\n } else if (char === ':' && !inString) {\n inByteSeq = !inByteSeq;\n current += char;\n } else if (char === '(' && !inString && !inByteSeq) {\n depth++;\n current += char;\n } else if (char === ')' && !inString && !inByteSeq) {\n depth--;\n current += char;\n } else if (char === ',' && depth === 0 && !inString && !inByteSeq) {\n if (current.trim()) {\n members.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n members.push(current.trim());\n }\n\n return members;\n}\n\n/**\n * Split a dictionary member into key=value pair.\n */\nfunction splitKeyValue(member: string): [string, string] {\n const eqIndex = member.indexOf('=');\n if (eqIndex === -1) {\n return [member, ''];\n }\n return [member.slice(0, eqIndex), member.slice(eqIndex + 1)];\n}\n\n/**\n * Parse a single dictionary member (label=inner-list;params).\n */\nfunction parseDictionaryMember(\n member: string\n): { label: string; params: ParsedSignatureParams } | null {\n const [label, rest] = splitKeyValue(member);\n if (!label || !rest) return null;\n\n // Parse inner list and parameters\n // Format: (\"component1\" \"component2\");param1=value1;param2=value2\n const innerListMatch = rest.match(/^\\(([^)]*)\\)(.*)$/);\n if (!innerListMatch) return null;\n\n const innerListContent = innerListMatch[1];\n const paramsString = innerListMatch[2];\n\n // Parse covered components from inner list\n const coveredComponents = parseInnerList(innerListContent);\n\n // Parse parameters\n const rawParams = parseParameters(paramsString);\n\n const params: ParsedSignatureParams = {\n keyid: String(rawParams.keyid ?? ''),\n coveredComponents,\n // Preserve the exact serialized inner-list-plus-parameters for this label so\n // callers can build an RFC 9421-faithful @signature-params line. `rest` is\n // the dictionary member value (everything after `label=`), already trimmed.\n signatureParamsValue: rest,\n };\n\n // alg and created are optional at the parse layer (RFC 9421). When absent they\n // stay undefined rather than defaulting to '' / 0, so a profile that omits them\n // (e.g. UCP) is represented faithfully and never fabricates a created=0 base.\n if (rawParams.alg !== undefined) {\n params.alg = String(rawParams.alg);\n }\n\n if (rawParams.created !== undefined) {\n params.created = Number(rawParams.created);\n }\n\n if (rawParams.expires !== undefined) {\n params.expires = Number(rawParams.expires);\n }\n\n if (rawParams.nonce !== undefined) {\n params.nonce = String(rawParams.nonce);\n }\n\n if (rawParams.tag !== undefined) {\n params.tag = String(rawParams.tag);\n }\n\n return { label, params };\n}\n\n/**\n * Parse inner list content: space-separated quoted strings (the covered\n * components). Fail closed on anything else (a bare/unquoted token, component\n * parameters, or trailing garbage) rather than silently extracting only the\n * quoted parts.\n */\nfunction parseInnerList(content: string): string[] {\n const trimmed = content.trim();\n if (trimmed === '') {\n return [];\n }\n const items: string[] = [];\n let pos = 0;\n while (pos < trimmed.length) {\n if (trimmed[pos] === ' ') {\n pos++;\n continue;\n }\n if (trimmed[pos] !== '\"') {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Malformed covered-component list near: ${trimmed.slice(pos)}`\n );\n }\n const end = trimmed.indexOf('\"', pos + 1);\n if (end === -1) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n 'Unterminated quoted string in covered-component list'\n );\n }\n items.push(trimmed.slice(pos + 1, end));\n pos = end + 1;\n }\n return items;\n}\n\n/**\n * Parse parameters from ;key=value;key2=value2 format.\n */\n\n// The closed set of RFC 9421 signature parameters consumed by callers. Only these\n// names are stored, so a caller-controlled parameter name from the header is never\n// written to an arbitrary (or prototype-polluting) property. Unknown parameters are\n// ignored, which is already how the consumer treats them.\nconst ALLOWED_PARAMS = new Set(['keyid', 'alg', 'created', 'expires', 'nonce', 'tag']);\n\nfunction parseParameters(paramsString: string): Record<string, string | number> {\n // Null-prototype map, defense in depth alongside the allowlist guard below.\n const params: Record<string, string | number> = Object.create(null);\n\n // Split by semicolon\n const parts = paramsString.split(';').filter((p) => p.trim());\n\n for (const part of parts) {\n const [key, value] = splitKeyValue(part.trim());\n if (!key) continue;\n\n // Only store known RFC 9421 parameters; the write target is constrained to a\n // constant allowlist rather than caller-controlled input.\n if (!ALLOWED_PARAMS.has(key)) continue;\n\n // Parse value - could be integer, string, or token\n if (!value) {\n params[key] = true as unknown as string;\n } else if (value.startsWith('\"') && value.endsWith('\"')) {\n // Quoted string\n params[key] = value.slice(1, -1);\n } else if (/^-?\\d+$/.test(value)) {\n // Integer\n params[key] = parseInt(value, 10);\n } else {\n // Token or other\n params[key] = value;\n }\n }\n\n return params;\n}\n\n// Standard base64 alphabet (RFC 4648 Section 4), with optional terminal\n// padding only. Rejects base64url (`-`/`_`), whitespace, and any `=` that is\n// not part of trailing padding.\nconst SF_BASE64_ALPHABET = /^[A-Za-z0-9+/]*={0,2}$/;\n// Canonical terminal padding when padding is present.\nconst SF_BASE64_CANONICAL_PADDED =\n /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;\n\n/**\n * Validate a Structured Field Byte Sequence value (RFC 9651 Section 3.3.5).\n *\n * Structured Field Byte Sequences use the RFC 4648 Section 4 base64 alphabet,\n * not base64url. Per RFC 9651 parsing, absent padding may be synthesized on\n * decode; characters outside the base64 alphabet remain invalid. So this\n * accepts valid unpadded standard base64, but rejects base64url (`-`/`_`),\n * misplaced/excess `=`, `length % 4 === 1`, whitespace, and the empty string\n * (no behavior widening). Keep this separate from JOSE/JWS base64url handling.\n */\nfunction isValidSfBase64(value: string): boolean {\n if (value.length === 0) return false;\n if (!SF_BASE64_ALPHABET.test(value)) return false;\n if (value.includes('=')) {\n // If padding is present, it must be canonical terminal padding.\n return SF_BASE64_CANONICAL_PADDED.test(value);\n }\n // Absent padding is synthesized on decode; `length % 4 === 1` is unrepairable.\n return value.length % 4 !== 1;\n}\n\n/**\n * Decode an RFC 4648 Section 4 base64 string to bytes, synthesizing absent\n * padding per RFC 9651 byte-sequence parsing. Callers MUST validate the input\n * with {@link isValidSfBase64} first; this does not accept base64url.\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n const padded = base64.includes('=')\n ? base64\n : base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * Signature base construction per RFC 9421 Section 2.5.\n *\n * The signature base is a canonical string constructed from\n * covered components and signature parameters.\n */\n\nimport { ParsedSignatureParams, SignatureRequest } from './types.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Build signature base string for verification.\n *\n * @param request - Request data\n * @param params - Parsed signature parameters\n * @returns Signature base string\n */\nexport function buildSignatureBase(\n request: SignatureRequest,\n params: ParsedSignatureParams,\n options: { preferSerializedParams?: boolean } = {}\n): string {\n const lines: string[] = [];\n\n // Add each covered component. RFC 9421 Section 2.5: if a component identifier\n // has already been added to the signature base, base generation MUST fail.\n const seenComponents = new Set<string>();\n for (const component of params.coveredComponents) {\n if (seenComponents.has(component)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_INPUT_MALFORMED,\n `Duplicate covered component: ${component}`\n );\n }\n seenComponents.add(component);\n const value = getComponentValue(request, component);\n lines.push(`\"${component}\": ${value}`);\n }\n\n // Add signature params line. RFC 9421 requires the @signature-params value to\n // be byte-identical to the serialized Signature-Input member that was signed.\n // When the caller opts in and the parser captured that exact serialization,\n // reuse it verbatim; otherwise reconstruct it (the default, and the only option\n // for programmatically built params that have no serialized form).\n const paramsLine =\n options.preferSerializedParams && params.signatureParamsValue !== undefined\n ? params.signatureParamsValue\n : buildSignatureParamsLine(params);\n lines.push(`\"@signature-params\": ${paramsLine}`);\n\n return lines.join('\\n');\n}\n\n/**\n * Get the canonical value for a component identifier.\n */\nfunction getComponentValue(request: SignatureRequest, component: string): string {\n // Derived components start with @\n if (component.startsWith('@')) {\n return getDerivedComponentValue(request, component);\n }\n\n // Otherwise it's a header field\n return getHeaderValue(request.headers, component);\n}\n\n/**\n * Get derived component value.\n */\nfunction getDerivedComponentValue(request: SignatureRequest, component: string): string {\n switch (component) {\n case '@method':\n return request.method.toUpperCase();\n\n case '@target-uri':\n return request.url;\n\n case '@authority': {\n try {\n const url = new URL(request.url);\n return url.host;\n } catch {\n // If URL parsing fails, try to extract from headers\n return getHeaderValue(request.headers, 'host');\n }\n }\n\n case '@scheme': {\n try {\n const url = new URL(request.url);\n return url.protocol.replace(':', '');\n } catch {\n return 'https';\n }\n }\n\n case '@request-target': {\n try {\n const url = new URL(request.url);\n return url.pathname + url.search;\n } catch {\n // If not a full URL, assume it's already a path\n return request.url;\n }\n }\n\n case '@path': {\n try {\n const url = new URL(request.url);\n return url.pathname;\n } catch {\n const pathMatch = request.url.match(/^[^?]*/);\n return pathMatch ? pathMatch[0] : request.url;\n }\n }\n\n case '@query': {\n try {\n const url = new URL(request.url);\n return url.search || '?';\n } catch {\n const queryMatch = request.url.match(/\\?.*/);\n return queryMatch ? queryMatch[0] : '?';\n }\n }\n\n default:\n // Unknown derived component - return empty\n return '';\n }\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeaderValue(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n\n return '';\n}\n\n/**\n * Build the signature-params line value.\n *\n * Format: (\"component1\" \"component2\");created=123;keyid=\"key\";alg=\"ed25519\"\n */\nfunction buildSignatureParamsLine(params: ParsedSignatureParams): string {\n // Build inner list of components\n const components = params.coveredComponents.map((c) => `\"${c}\"`).join(' ');\n let line = `(${components})`;\n\n // Emit parameters in canonical order, skipping optional ones that are absent.\n // For the PEAC/TAP path created and alg are always present, so this is\n // byte-identical to the previous unconditional output.\n if (params.created !== undefined) {\n line += `;created=${params.created}`;\n }\n\n if (params.expires !== undefined) {\n line += `;expires=${params.expires}`;\n }\n\n if (params.nonce !== undefined) {\n line += `;nonce=\"${params.nonce}\"`;\n }\n\n line += `;keyid=\"${params.keyid}\"`;\n\n if (params.alg !== undefined) {\n line += `;alg=\"${params.alg}\"`;\n }\n\n if (params.tag !== undefined) {\n line += `;tag=\"${params.tag}\"`;\n }\n\n return line;\n}\n\n/**\n * Convert signature base string to bytes for cryptographic verification.\n */\nexport function signatureBaseToBytes(signatureBase: string): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(signatureBase);\n}\n","/**\n * HTTP Message Signature verification.\n *\n * Runtime-neutral verification using SignatureVerifier function type.\n * WebCrypto is used internally but NOT exposed in public API.\n */\n\nimport {\n SignatureVerifier,\n SignatureRequest,\n VerifyOptions,\n VerificationResult,\n ParsedSignatureParams,\n} from './types.js';\nimport { parseSignature } from './parser.js';\nimport { buildSignatureBase, signatureBaseToBytes } from './base.js';\nimport { ErrorCodes, HttpSignatureError } from './errors.js';\n\n/**\n * Verify an HTTP Message Signature.\n *\n * @param request - Request data with headers\n * @param options - Verification options including key resolver\n * @returns Verification result\n */\nexport async function verifySignature(\n request: SignatureRequest,\n options: VerifyOptions\n): Promise<VerificationResult> {\n const { keyResolver, now = Math.floor(Date.now() / 1000), clockSkewSeconds = 60 } = options;\n\n try {\n // Get signature headers\n const signatureInput = getHeader(request.headers, 'signature-input');\n const signature = getHeader(request.headers, 'signature');\n\n // Parse signature\n const parsed = parseSignature(signatureInput, signature, options.label);\n\n // Validate algorithm\n if (parsed.params.alg !== 'ed25519') {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED,\n `Unsupported algorithm: ${parsed.params.alg} (only ed25519 is supported)`\n );\n }\n\n // Validate time constraints\n validateTimeConstraints(parsed.params, now, clockSkewSeconds);\n\n // Resolve key\n const verifier = await keyResolver(parsed.params.keyid);\n if (!verifier) {\n throw new HttpSignatureError(\n ErrorCodes.KEY_NOT_FOUND,\n `Key not found: ${parsed.params.keyid}`\n );\n }\n\n // Build signature base\n const signatureBase = buildSignatureBase(request, parsed.params);\n const signatureBaseBytes = signatureBaseToBytes(signatureBase);\n\n // Verify signature\n const valid = await verifier(signatureBaseBytes, parsed.signatureBytes);\n\n if (!valid) {\n throw new HttpSignatureError(ErrorCodes.SIGNATURE_INVALID, 'Signature verification failed');\n }\n\n return {\n valid: true,\n signature: parsed,\n };\n } catch (error) {\n if (error instanceof HttpSignatureError) {\n return {\n valid: false,\n errorCode: error.code,\n errorMessage: error.message,\n };\n }\n throw error;\n }\n}\n\n/**\n * Validate time constraints on signature parameters.\n */\nfunction validateTimeConstraints(\n params: ParsedSignatureParams,\n now: number,\n clockSkewSeconds: number\n): void {\n // Check if signature is from the future (with skew tolerance)\n if (isCreatedInFuture(params, now, clockSkewSeconds)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_FUTURE,\n `Signature created in future: ${params.created} > ${now + clockSkewSeconds}`\n );\n }\n\n // Check if signature is expired\n if (isExpired(params, now)) {\n throw new HttpSignatureError(\n ErrorCodes.SIGNATURE_EXPIRED,\n `Signature expired: ${params.expires} < ${now}`\n );\n }\n}\n\n/**\n * Check if signature is expired.\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @returns true if signature is expired\n */\nexport function isExpired(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000)\n): boolean {\n if (params.expires === undefined) {\n return false;\n }\n return now > params.expires;\n}\n\n/**\n * Check if signature was created in the future (accounting for clock skew).\n *\n * @param params - Parsed signature parameters\n * @param now - Current Unix timestamp (defaults to current time)\n * @param skewSeconds - Allowed clock skew in seconds (defaults to 60)\n * @returns true if signature is from the future\n */\nexport function isCreatedInFuture(\n params: ParsedSignatureParams,\n now: number = Math.floor(Date.now() / 1000),\n skewSeconds: number = 60\n): boolean {\n // A signature with no `created` parameter cannot be classified as future-dated.\n if (params.created === undefined) {\n return false;\n }\n return params.created > now + skewSeconds;\n}\n\n/**\n * Check if Ed25519 WebCrypto is supported in current runtime.\n *\n * @returns true if Ed25519 WebCrypto is available\n */\nexport async function isEd25519WebCryptoSupported(): Promise<boolean> {\n try {\n // Try to generate a key pair to test support\n const keyPair = await globalThis.crypto.subtle.generateKey('Ed25519', false, [\n 'sign',\n 'verify',\n ]);\n return keyPair !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create a SignatureVerifier from a WebCrypto CryptoKey.\n *\n * This is a helper for consumers who have CryptoKey objects.\n * The function is runtime-neutral as it accepts unknown and casts internally.\n *\n * @param key - WebCrypto CryptoKey (passed as unknown for runtime neutrality)\n * @returns SignatureVerifier function\n */\nexport function createWebCryptoVerifier(key: unknown): SignatureVerifier {\n return async (data: Uint8Array, signature: Uint8Array): Promise<boolean> => {\n // Create proper ArrayBuffer views to satisfy TypeScript\n const sigBuffer = new Uint8Array(signature).buffer;\n const dataBuffer = new Uint8Array(data).buffer;\n\n // Use type from the actual WebCrypto API to avoid DOM type dependency\n type VerifyKey = Parameters<typeof globalThis.crypto.subtle.verify>[1];\n\n return globalThis.crypto.subtle.verify('Ed25519', key as VerifyKey, sigBuffer, dataBuffer);\n };\n}\n\n/**\n * Get header value by name (case-insensitive).\n */\nfunction getHeader(headers: Record<string, string>, name: string): string {\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n return '';\n}\n"]}
|
package/dist/parser.d.ts
CHANGED
|
@@ -34,5 +34,8 @@ export declare function parseSignatureHeader(headerValue: string): Map<string, {
|
|
|
34
34
|
* @param label - Optional specific label to parse (defaults to first)
|
|
35
35
|
* @returns Parsed signature or throws HttpSignatureError
|
|
36
36
|
*/
|
|
37
|
-
export declare function parseSignature(signatureInput: string, signature: string, label?: string
|
|
37
|
+
export declare function parseSignature(signatureInput: string, signature: string, label?: string, options?: {
|
|
38
|
+
requireAlg?: boolean;
|
|
39
|
+
requireCreated?: boolean;
|
|
40
|
+
}): ParsedSignature;
|
|
38
41
|
//# sourceMappingURL=parser.d.ts.map
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGpE;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGpE;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CA4B3F;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAClB,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAmDpD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/D,eAAe,CA0EjB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -21,10 +21,19 @@ export type KeyResolver = (keyid: string) => Promise<SignatureVerifier | null>;
|
|
|
21
21
|
export interface ParsedSignatureParams {
|
|
22
22
|
/** Key identifier */
|
|
23
23
|
keyid: string;
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Algorithm. Optional per RFC 9421: some profiles omit `alg` from
|
|
26
|
+
* Signature-Input and derive the algorithm from the resolved key (e.g. UCP
|
|
27
|
+
* derives ES256/ES384 from the JWK `crv`). The PEAC/TAP path requires it via
|
|
28
|
+
* `parseSignature(..., { requireAlg: true })`, which is the default.
|
|
29
|
+
*/
|
|
30
|
+
alg?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Unix timestamp when the signature was created. Optional per RFC 9421;
|
|
33
|
+
* some profiles (e.g. UCP) omit it and handle replay at the business layer.
|
|
34
|
+
* Required for the PEAC/TAP path via the default `requireCreated: true`.
|
|
35
|
+
*/
|
|
36
|
+
created?: number;
|
|
28
37
|
/** Unix timestamp when signature expires (optional) */
|
|
29
38
|
expires?: number;
|
|
30
39
|
/** Nonce for replay prevention (optional) */
|
|
@@ -33,6 +42,14 @@ export interface ParsedSignatureParams {
|
|
|
33
42
|
tag?: string;
|
|
34
43
|
/** Covered component identifiers */
|
|
35
44
|
coveredComponents: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Exact serialized `@signature-params` value (the inner list plus parameters)
|
|
47
|
+
* for this label, as received in Signature-Input. RFC 9421 requires the
|
|
48
|
+
* signature base to reuse this exact serialization rather than a reconstructed
|
|
49
|
+
* one; `buildSignatureBase(..., { preferSerializedParams: true })` uses it when
|
|
50
|
+
* present. Populated by the parser; absent for programmatically built params.
|
|
51
|
+
*/
|
|
52
|
+
signatureParamsValue?: string;
|
|
36
53
|
}
|
|
37
54
|
/**
|
|
38
55
|
* Complete parsed signature with raw signature bytes.
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9F;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;AAE/E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9F;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;AAE/E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,gDAAgD;IAChD,cAAc,EAAE,UAAU,CAAC;IAC3B,wCAAwC;IACxC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,8CAA8C;IAC9C,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
package/dist/verify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAKpB;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAwD7B;AA2BD;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,qBAAqB,EAC7B,GAAG,GAAE,MAAsC,GAC1C,OAAO,CAKT;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,qBAAqB,EAC7B,GAAG,GAAE,MAAsC,EAC3C,WAAW,GAAE,MAAW,GACvB,OAAO,
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAKpB;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAwD7B;AA2BD;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,qBAAqB,EAC7B,GAAG,GAAE,MAAsC,GAC1C,OAAO,CAKT;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,qBAAqB,EAC7B,GAAG,GAAE,MAAsC,EAC3C,WAAW,GAAE,MAAW,GACvB,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,OAAO,CAAC,CAWpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAWvE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peac/http-signatures",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "RFC 9421 HTTP Message Signatures parsing and verification",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"typescript": "^5.3.0",
|
|
48
|
-
"vitest": "^4.
|
|
48
|
+
"vitest": "^4.1.0",
|
|
49
49
|
"tsup": "^8.0.0"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|