@openeudi/openid4vp 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -124,6 +124,7 @@ class MyCustomParser implements ICredentialParser {
124
124
  | --------------------- | -------------- | ------------------------------------ |
125
125
  | `trustedCertificates` | `Uint8Array[]` | Issuer certificates to trust |
126
126
  | `nonce` | `string` | Expected nonce for replay protection |
127
+ | `skipTrustCheck` | `boolean?` | Explicit opt-in to skip the trust check. When omitted or `false`, `trustedCertificates` must be non-empty — otherwise parsing throws `MalformedCredentialError`. Use `true` for demo/mock environments. |
127
128
 
128
129
  ### PresentationResult
129
130
 
@@ -157,6 +158,31 @@ try {
157
158
  }
158
159
  ```
159
160
 
161
+ ## Scope and limitations
162
+
163
+ This library implements the **verifier side** of OpenID4VP for SD-JWT VC and mDOC credentials.
164
+
165
+ **What is implemented (v0.2.x):**
166
+
167
+ - SD-JWT VC: full cryptographic verification (issuer JWT signature via x5c, disclosure hashes, key binding JWT signature + sd_hash, nonce check)
168
+ - mDOC / ISO 18013-5 *mso_mdoc* format: CBOR decoding and claim extraction
169
+ - Algorithm allowlist (ES256/384/512 — ECDSA only per EUDI policy)
170
+ - Authorization request builder
171
+ - Certificate trust check via byte-equality against a caller-supplied trusted set
172
+
173
+ **What is NOT yet implemented** (planned for follow-up releases — do not assume compliance in production until present):
174
+
175
+ - mDOC / COSE_Sign1 cryptographic signature verification (design ready, implementation pending)
176
+ - X.509 certificate chain building and validation beyond leaf-byte-equality
177
+ - EU List of Trusted Lists (LOTL) / ETSI TL resolution
178
+ - Certificate revocation (CRL, OCSP)
179
+ - DCQL query / credential matching — see [@openeudi/dcql](https://www.npmjs.com/package/@openeudi/dcql)
180
+ - OpenID4VP HAIP (High Assurance Interoperability Profile) constraint validation
181
+ - OpenID Foundation conformance test suite integration
182
+ - SIOPv2 (Self-Issued OpenID Provider) identity flows
183
+
184
+ EUDI Architecture Reference Framework (ARF) alignment: tracks OpenID4VP 1.0 final. HAIP and ARF 1.4+ profile compliance will be added before a stable 1.0.
185
+
160
186
  ## Related packages
161
187
 
162
188
  - **[@openeudi/core](https://www.npmjs.com/package/@openeudi/core)** -- Framework-agnostic EUDI Wallet verification protocol engine with session management and QR code generation.
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var decode = require('@sd-jwt/decode');
4
+ var jose = require('jose');
4
5
  var cborX = require('cbor-x');
5
6
  var uuid = require('uuid');
6
7
 
@@ -44,6 +45,7 @@ var NonceValidationError = class extends Error {
44
45
  };
45
46
 
46
47
  // src/parsers/sd-jwt.parser.ts
48
+ var DEFAULT_ALLOWED_ALGORITHMS = ["ES256", "ES384", "ES512"];
47
49
  var sha256Hasher = async (data, _alg) => {
48
50
  const encoder = new TextEncoder();
49
51
  const input = typeof data === "string" ? encoder.encode(data) : data;
@@ -68,9 +70,6 @@ function base64ToBytes(base64) {
68
70
  }
69
71
  return bytes;
70
72
  }
71
- function extractCertificateFromX5c(x5cEntry) {
72
- return base64ToBytes(x5cEntry);
73
- }
74
73
  function bytesEqual(a, b) {
75
74
  if (a.length !== b.length) return false;
76
75
  for (let i = 0; i < a.length; i++) {
@@ -78,6 +77,19 @@ function bytesEqual(a, b) {
78
77
  }
79
78
  return true;
80
79
  }
80
+ function derToPem(base64Der) {
81
+ const lines = base64Der.match(/.{1,64}/g) || [];
82
+ return `-----BEGIN CERTIFICATE-----
83
+ ${lines.join("\n")}
84
+ -----END CERTIFICATE-----`;
85
+ }
86
+ function bytesToBase64url(bytes) {
87
+ let binary = "";
88
+ for (const byte of bytes) {
89
+ binary += String.fromCharCode(byte);
90
+ }
91
+ return globalThis.btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
92
+ }
81
93
  function mapToCredentialClaims(raw) {
82
94
  const claims = {};
83
95
  if (typeof raw["age_over_18"] === "boolean") {
@@ -102,24 +114,27 @@ function mapToCredentialClaims(raw) {
102
114
  }
103
115
  return claims;
104
116
  }
117
+ function extractCountryHint(issuer) {
118
+ try {
119
+ const url = new URL(issuer);
120
+ const tld = url.hostname.split(".").pop()?.toUpperCase();
121
+ if (tld && tld.length === 2) {
122
+ return tld;
123
+ }
124
+ } catch {
125
+ }
126
+ return "";
127
+ }
105
128
  var SdJwtParser = class {
106
129
  format = "sd-jwt-vc";
107
- /**
108
- * Returns true when the token looks like an SD-JWT:
109
- * it must be a string containing the `~` disclosure separator.
110
- */
111
130
  canParse(vpToken) {
112
131
  return typeof vpToken === "string" && vpToken.includes("~");
113
132
  }
114
- /**
115
- * Parses and validates an SD-JWT VC token.
116
- *
117
- * @throws {MalformedCredentialError} when the token structure cannot be decoded
118
- */
119
133
  async parse(vpToken, options) {
120
134
  if (typeof vpToken !== "string") {
121
135
  throw new MalformedCredentialError("VP token must be a string");
122
136
  }
137
+ const allowedAlgorithms = options.allowedAlgorithms ?? DEFAULT_ALLOWED_ALGORITHMS;
123
138
  const invalidResult = (error) => ({
124
139
  valid: false,
125
140
  format: this.format,
@@ -134,54 +149,101 @@ var SdJwtParser = class {
134
149
  throw new MalformedCredentialError("Failed to split SD-JWT structure");
135
150
  }
136
151
  let header;
152
+ try {
153
+ header = jose.decodeProtectedHeader(parts.jwt);
154
+ } catch {
155
+ throw new MalformedCredentialError("Failed to decode SD-JWT issuer JWT header");
156
+ }
157
+ const alg = header.alg;
158
+ if (typeof alg !== "string" || !allowedAlgorithms.includes(alg)) {
159
+ return invalidResult(`Unsupported algorithm: ${alg}`);
160
+ }
161
+ const x5cArray = header.x5c;
162
+ if (!Array.isArray(x5cArray) || x5cArray.length === 0 || typeof x5cArray[0] !== "string") {
163
+ throw new MalformedCredentialError("Missing or invalid x5c in JWT header");
164
+ }
165
+ let issuerKey;
166
+ let issuerCertBytes;
167
+ try {
168
+ issuerKey = await jose.importX509(derToPem(x5cArray[0]), alg);
169
+ issuerCertBytes = base64ToBytes(x5cArray[0]);
170
+ } catch (err) {
171
+ if (err instanceof MalformedCredentialError) throw err;
172
+ throw new MalformedCredentialError("Failed to import issuer certificate");
173
+ }
137
174
  let payload;
138
175
  try {
139
- const jwt = decode.decodeJwt(parts.jwt);
140
- header = jwt.header;
141
- payload = jwt.payload;
176
+ const result = await jose.jwtVerify(parts.jwt, issuerKey, { algorithms: allowedAlgorithms });
177
+ payload = result.payload;
142
178
  } catch {
143
- throw new MalformedCredentialError("Failed to decode SD-JWT issuer JWT");
179
+ return invalidResult("Issuer JWT signature verification failed");
144
180
  }
145
- const x5cArray = header["x5c"];
146
- let issuerCertBytes = new Uint8Array();
147
- let issuerInfo = { certificate: new Uint8Array(), country: "" };
148
- if (Array.isArray(x5cArray) && x5cArray.length > 0 && typeof x5cArray[0] === "string") {
149
- try {
150
- issuerCertBytes = extractCertificateFromX5c(x5cArray[0]);
151
- const issuerString = typeof payload["iss"] === "string" ? payload["iss"] : "";
152
- issuerInfo = {
153
- certificate: issuerCertBytes,
154
- country: extractCountryHint(issuerString)
155
- };
156
- } catch {
157
- return invalidResult("Failed to extract issuer certificate from x5c");
181
+ const issuerString = typeof payload["iss"] === "string" ? payload["iss"] : "";
182
+ const issuerInfo = {
183
+ certificate: issuerCertBytes,
184
+ country: extractCountryHint(issuerString)
185
+ };
186
+ if (options.trustedCertificates.length === 0) {
187
+ if (options.skipTrustCheck !== true) {
188
+ throw new MalformedCredentialError(
189
+ "trustedCertificates must not be empty unless skipTrustCheck is true"
190
+ );
158
191
  }
159
- }
160
- if (options.trustedCertificates.length > 0) {
192
+ } else {
161
193
  const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));
162
194
  if (!isTrusted) {
163
195
  return invalidResult("Issuer certificate is not trusted");
164
196
  }
165
- } else if (issuerCertBytes.length === 0) {
166
- return invalidResult("No issuer certificate found and no trusted certificates configured");
167
197
  }
168
- const exp = payload["exp"];
169
- if (typeof exp === "number") {
170
- const nowSeconds = Math.floor(Date.now() / 1e3);
171
- if (exp < nowSeconds) {
172
- return invalidResult("Credential has expired");
198
+ const sdArray = payload["_sd"];
199
+ if (parts.disclosures.length > 0) {
200
+ if (!Array.isArray(sdArray) || sdArray.length === 0) {
201
+ return invalidResult("Disclosures present but no _sd array in issuer JWT");
202
+ }
203
+ for (const disclosure of parts.disclosures) {
204
+ const hashBytes = await sha256Hasher(disclosure);
205
+ const hashB64url = bytesToBase64url(hashBytes);
206
+ if (!sdArray.includes(hashB64url)) {
207
+ return invalidResult("Disclosure hash mismatch");
208
+ }
173
209
  }
174
210
  }
175
211
  if (parts.kbJwt) {
212
+ const cnf = payload["cnf"];
213
+ if (!cnf || typeof cnf !== "object" || !cnf["jwk"]) {
214
+ throw new MalformedCredentialError("Missing cnf.jwk in issuer JWT for key binding");
215
+ }
216
+ let holderKey;
176
217
  try {
177
- const kbDecoded = decode.decodeJwt(parts.kbJwt);
178
- const kbPayload = kbDecoded.payload;
179
- const kbNonce = kbPayload["nonce"];
180
- if (typeof kbNonce === "string" && kbNonce !== options.nonce) {
181
- return invalidResult("Key binding JWT nonce does not match");
218
+ const kbHeader = jose.decodeProtectedHeader(parts.kbJwt);
219
+ const kbAlg = kbHeader.alg;
220
+ if (typeof kbAlg !== "string" || !allowedAlgorithms.includes(kbAlg)) {
221
+ return invalidResult(`Unsupported KB-JWT algorithm: ${kbAlg}`);
182
222
  }
223
+ holderKey = await jose.importJWK(cnf["jwk"], kbAlg);
224
+ } catch (err) {
225
+ if (err instanceof MalformedCredentialError) throw err;
226
+ throw new MalformedCredentialError("Failed to import holder key from cnf.jwk");
227
+ }
228
+ let kbPayload;
229
+ try {
230
+ const kbResult = await jose.jwtVerify(parts.kbJwt, holderKey, {
231
+ algorithms: allowedAlgorithms,
232
+ ...options.audience ? { audience: options.audience } : {}
233
+ });
234
+ kbPayload = kbResult.payload;
183
235
  } catch {
184
- return invalidResult("Failed to decode key binding JWT");
236
+ return invalidResult("Key binding JWT signature verification failed");
237
+ }
238
+ if (kbPayload["nonce"] !== options.nonce) {
239
+ return invalidResult("Key binding JWT nonce does not match");
240
+ }
241
+ const disclosurePart = parts.disclosures.length > 0 ? parts.disclosures.join("~") + "~" : "";
242
+ const sdJwtForHash = parts.jwt + "~" + disclosurePart;
243
+ const sdHashBytes = await sha256Hasher(sdJwtForHash);
244
+ const expectedSdHash = bytesToBase64url(sdHashBytes);
245
+ if (kbPayload["sd_hash"] !== expectedSdHash) {
246
+ return invalidResult("Key binding JWT sd_hash does not match");
185
247
  }
186
248
  }
187
249
  let resolvedClaims;
@@ -193,28 +255,16 @@ var SdJwtParser = class {
193
255
  sha256Hasher
194
256
  );
195
257
  } catch {
196
- resolvedClaims = payload;
258
+ throw new MalformedCredentialError("Failed to decode SD-JWT disclosures");
197
259
  }
198
- const claims = mapToCredentialClaims(resolvedClaims);
199
260
  return {
200
261
  valid: true,
201
262
  format: this.format,
202
- claims,
263
+ claims: mapToCredentialClaims(resolvedClaims),
203
264
  issuer: issuerInfo
204
265
  };
205
266
  }
206
267
  };
207
- function extractCountryHint(issuer) {
208
- try {
209
- const url = new URL(issuer);
210
- const tld = url.hostname.split(".").pop()?.toUpperCase();
211
- if (tld && tld.length === 2) {
212
- return tld;
213
- }
214
- } catch {
215
- }
216
- return "";
217
- }
218
268
  function isBinaryData(value) {
219
269
  return value instanceof Uint8Array;
220
270
  }
@@ -355,13 +405,17 @@ var MdocParser = class {
355
405
  certificate: issuerCertBytes,
356
406
  country: ""
357
407
  };
358
- if (options.trustedCertificates.length > 0) {
408
+ if (options.trustedCertificates.length === 0) {
409
+ if (options.skipTrustCheck !== true) {
410
+ throw new MalformedCredentialError(
411
+ "trustedCertificates must not be empty unless skipTrustCheck is true"
412
+ );
413
+ }
414
+ } else {
359
415
  const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual2(trusted, issuerCertBytes));
360
416
  if (!isTrusted) {
361
417
  return invalidResult("Issuer certificate is not trusted");
362
418
  }
363
- } else if (issuerCertBytes.length === 0) {
364
- return invalidResult("No issuer certificate found and no trusted certificates configured");
365
419
  }
366
420
  const { validUntil } = extractValidityPeriod(issuerAuth);
367
421
  if (validUntil) {
@@ -442,7 +496,7 @@ async function parsePresentation(vpToken, options) {
442
496
  }
443
497
 
444
498
  // src/index.ts
445
- var VERSION = "0.1.0";
499
+ var VERSION = "0.2.1";
446
500
 
447
501
  exports.ExpiredCredentialError = ExpiredCredentialError;
448
502
  exports.InvalidSignatureError = InvalidSignatureError;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/parsers/sd-jwt.parser.ts","../src/parsers/mdoc.parser.ts","../src/authorization.ts","../src/presentation.ts","../src/index.ts"],"names":["splitSdJwt","decodeJwt","decodeSdJwt","getClaims","bytesEqual","decode","mapToCredentialClaims","uuidv4"],"mappings":"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","file":"index.cjs","sourcesContent":["export class InvalidSignatureError extends Error {\n constructor(message = 'Credential signature validation failed') {\n super(message);\n this.name = 'InvalidSignatureError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ExpiredCredentialError extends Error {\n constructor(message = 'Credential has expired') {\n super(message);\n this.name = 'ExpiredCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class UnsupportedFormatError extends Error {\n constructor(format: string) {\n super(`Unsupported credential format: ${format}`);\n this.name = 'UnsupportedFormatError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class MalformedCredentialError extends Error {\n constructor(message = 'Credential structure is malformed') {\n super(message);\n this.name = 'MalformedCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class NonceValidationError extends Error {\n constructor(message = 'Nonce does not match expected value') {\n super(message);\n this.name = 'NonceValidationError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { splitSdJwt, decodeJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Hasher function signature expected by @sd-jwt/decode.\n * Inlined here to avoid a direct dependency on @sd-jwt/types.\n */\ntype SdJwtHasher = (data: string | ArrayBuffer, alg: string) => Promise<Uint8Array>;\n\n/**\n * SHA-256 hasher implementation using the Web Crypto API.\n * Compatible with the @sd-jwt/decode Hasher signature.\n */\nconst sha256Hasher: SdJwtHasher = async (data: string | ArrayBuffer, _alg: string): Promise<Uint8Array> => {\n const encoder = new TextEncoder();\n const input = typeof data === 'string' ? encoder.encode(data) : data;\n const hashBuffer = await crypto.subtle.digest('SHA-256', input);\n return new Uint8Array(hashBuffer);\n};\n\n// Standard base64 alphabet for decoding x5c certificate entries\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/**\n * Decodes a standard base64-encoded string into raw bytes.\n * Does not depend on Node.js Buffer or browser atob.\n */\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n // Strip any padding characters\n const cleaned = base64.replace(/=+$/, '');\n const byteLength = Math.floor((cleaned.length * 3) / 4);\n const buffer = new ArrayBuffer(byteLength);\n const bytes = new Uint8Array(buffer);\n\n let byteIndex = 0;\n for (let i = 0; i < cleaned.length; i += 4) {\n const a = BASE64_CHARS.indexOf(cleaned[i]);\n const b = i + 1 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 1]) : 0;\n const c = i + 2 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 2]) : 0;\n const d = i + 3 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 3]) : 0;\n\n bytes[byteIndex++] = (a << 2) | (b >> 4);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((b & 0x0f) << 4) | (c >> 2);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((c & 0x03) << 6) | d;\n }\n\n return bytes;\n}\n\n/**\n * Extracts the DER-encoded certificate bytes from a base64-encoded X.509 certificate\n * found in the JWT header's x5c field.\n */\nfunction extractCertificateFromX5c(x5cEntry: string): Uint8Array<ArrayBuffer> {\n return base64ToBytes(x5cEntry);\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Maps raw decoded claims to the CredentialClaims structure,\n * picking known age/residency fields and preserving any extra keys.\n */\nfunction mapToCredentialClaims(raw: Record<string, unknown>): CredentialClaims {\n const claims: CredentialClaims = {};\n\n if (typeof raw['age_over_18'] === 'boolean') {\n claims.age_over_18 = raw['age_over_18'];\n }\n if (typeof raw['age_over_21'] === 'boolean') {\n claims.age_over_21 = raw['age_over_21'];\n }\n if (typeof raw['resident_country'] === 'string') {\n claims.resident_country = raw['resident_country'];\n }\n if (typeof raw['nationality'] === 'string') {\n claims.nationality = raw['nationality'];\n }\n if (typeof raw['family_name_birth'] === 'string') {\n claims.family_name_birth = raw['family_name_birth'];\n }\n\n // Preserve any additional claim keys not already mapped\n for (const [key, value] of Object.entries(raw)) {\n if (\n !(key in claims) &&\n !key.startsWith('_sd') &&\n key !== 'iss' &&\n key !== 'exp' &&\n key !== 'iat' &&\n key !== 'cnf' &&\n key !== 'vct'\n ) {\n claims[key] = value;\n }\n }\n\n return claims;\n}\n\n/**\n * SD-JWT VC credential parser.\n *\n * Decodes and validates SD-JWT Verifiable Credentials as specified in\n * the EUDI Wallet ecosystem. Performs:\n * - Structure validation (JWT~disclosure~kb format)\n * - Issuer certificate trust chain verification\n * - Expiry checking\n * - Nonce validation against the key binding JWT\n * - Selective disclosure claim extraction\n */\nexport class SdJwtParser implements ICredentialParser {\n readonly format: CredentialFormat = 'sd-jwt-vc';\n\n /**\n * Returns true when the token looks like an SD-JWT:\n * it must be a string containing the `~` disclosure separator.\n */\n canParse(vpToken: unknown): boolean {\n return typeof vpToken === 'string' && vpToken.includes('~');\n }\n\n /**\n * Parses and validates an SD-JWT VC token.\n *\n * @throws {MalformedCredentialError} when the token structure cannot be decoded\n */\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (typeof vpToken !== 'string') {\n throw new MalformedCredentialError('VP token must be a string');\n }\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Split the SD-JWT into its components\n let parts: ReturnType<typeof splitSdJwt>;\n try {\n parts = splitSdJwt(vpToken);\n } catch {\n throw new MalformedCredentialError('Failed to split SD-JWT structure');\n }\n\n // Step 2: Decode the issuer JWT header and payload\n let header: Record<string, unknown>;\n let payload: Record<string, unknown>;\n try {\n const jwt = decodeJwt(parts.jwt);\n header = jwt.header;\n payload = jwt.payload;\n } catch {\n throw new MalformedCredentialError('Failed to decode SD-JWT issuer JWT');\n }\n\n // Step 3: Extract issuer certificate from x5c header\n const x5cArray = header['x5c'];\n let issuerCertBytes = new Uint8Array();\n let issuerInfo: IssuerInfo = { certificate: new Uint8Array(), country: '' };\n\n if (Array.isArray(x5cArray) && x5cArray.length > 0 && typeof x5cArray[0] === 'string') {\n try {\n issuerCertBytes = extractCertificateFromX5c(x5cArray[0]);\n // Attempt to derive the country from the certificate's issuer string\n // In production, this would parse the X.509 subject, but for now we\n // fall back to payload-level hints.\n const issuerString = typeof payload['iss'] === 'string' ? payload['iss'] : '';\n issuerInfo = {\n certificate: issuerCertBytes,\n country: extractCountryHint(issuerString),\n };\n } catch {\n return invalidResult('Failed to extract issuer certificate from x5c');\n }\n }\n\n // Step 4: Verify trust — certificate must be in the trusted set\n if (options.trustedCertificates.length > 0) {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n } else if (issuerCertBytes.length === 0) {\n return invalidResult('No issuer certificate found and no trusted certificates configured');\n }\n\n // Step 5: Check expiry\n const exp = payload['exp'];\n if (typeof exp === 'number') {\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (exp < nowSeconds) {\n return invalidResult('Credential has expired');\n }\n }\n\n // Step 6: Validate nonce in key binding JWT\n if (parts.kbJwt) {\n try {\n const kbDecoded = decodeJwt(parts.kbJwt);\n const kbPayload = kbDecoded.payload as Record<string, unknown>;\n const kbNonce = kbPayload['nonce'];\n if (typeof kbNonce === 'string' && kbNonce !== options.nonce) {\n return invalidResult('Key binding JWT nonce does not match');\n }\n } catch {\n return invalidResult('Failed to decode key binding JWT');\n }\n }\n\n // Step 7: Decode disclosures and extract claims\n let resolvedClaims: Record<string, unknown>;\n try {\n const decoded = await decodeSdJwt(vpToken, sha256Hasher);\n resolvedClaims = await getClaims<Record<string, unknown>>(\n decoded.jwt.payload,\n decoded.disclosures,\n sha256Hasher\n );\n } catch {\n // If SD-JWT decoding fails (e.g., no _sd_alg), fall back to raw payload\n resolvedClaims = payload;\n }\n\n const claims = mapToCredentialClaims(resolvedClaims);\n\n return {\n valid: true,\n format: this.format,\n claims,\n issuer: issuerInfo,\n };\n }\n}\n\n/**\n * Best-effort extraction of a country code hint from an issuer URL or string.\n * For example, \"https://issuer.de/...\" would yield \"DE\".\n */\nfunction extractCountryHint(issuer: string): string {\n try {\n const url = new URL(issuer);\n const tld = url.hostname.split('.').pop()?.toUpperCase();\n if (tld && tld.length === 2) {\n return tld;\n }\n } catch {\n // Not a valid URL — ignore\n }\n return '';\n}\n","import { decode } from 'cbor-x';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Runtime check for binary data. Returns true for Uint8Array and Node.js Buffer\n * without referencing the Buffer type (which is unavailable in DOM-only tsconfig).\n * Buffer extends Uint8Array, so `instanceof Uint8Array` catches both.\n */\nfunction isBinaryData(value: unknown): value is Uint8Array {\n return value instanceof Uint8Array;\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Represents a single IssuerSignedItem element from an mDOC nameSpace.\n * ISO 18013-5 Section 8.3.2.1.2.\n */\ninterface IssuerSignedItem {\n digestID: number;\n random: Uint8Array | unknown;\n elementIdentifier: string;\n elementValue: unknown;\n}\n\n/**\n * Simplified DeviceResponse structure from ISO 18013-5.\n * Only the fields required for claim extraction and trust validation.\n */\ninterface MdocDocument {\n docType: string;\n issuerSigned: {\n nameSpaces: Record<string, IssuerSignedItem[]>;\n issuerAuth: unknown[];\n };\n}\n\ninterface DeviceResponse {\n version: string;\n documents: MdocDocument[];\n status: number;\n}\n\n/**\n * Extracts the issuer certificate bytes from a COSE_Sign1 issuerAuth structure.\n *\n * The issuerAuth is a COSE_Sign1 array: [protected, unprotected, payload, signature].\n * The protected header (index 0) is CBOR-encoded and may contain the x5chain (label 33)\n * with the issuer certificate. The unprotected header (index 1) may also carry it.\n *\n * If neither header contains a certificate, the raw protected bytes are returned\n * as a fallback for trust matching (test/simplified scenarios).\n */\nfunction extractCertificateFromIssuerAuth(issuerAuth: unknown[]): Uint8Array {\n // Try unprotected header first (index 1) -- it's already decoded as a Map or object\n const unprotected = issuerAuth[1];\n if (unprotected && typeof unprotected === 'object') {\n // x5chain is COSE header label 33\n const certFromUnprotected = extractX5Chain(unprotected);\n if (certFromUnprotected) return certFromUnprotected;\n }\n\n // Try protected header (index 0) -- CBOR-encoded bytes\n const protectedHeader = issuerAuth[0];\n if (isBinaryData(protectedHeader)) {\n try {\n const decoded = decode(protectedHeader) as unknown;\n if (decoded && typeof decoded === 'object') {\n const certFromProtected = extractX5Chain(decoded);\n if (certFromProtected) return certFromProtected;\n }\n } catch {\n // Protected header could not be decoded -- fall through\n }\n // Return raw protected bytes as fallback identifier\n return new Uint8Array(protectedHeader);\n }\n\n return new Uint8Array();\n}\n\n/**\n * Extracts certificate bytes from an x5chain (label 33) in a COSE header.\n * The header can be a plain object or a Map.\n */\nfunction extractX5Chain(header: unknown): Uint8Array | null {\n if (header instanceof Map) {\n const cert = header.get(33) as unknown;\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n } else if (typeof header === 'object' && header !== null) {\n const rec = header as Record<string | number, unknown>;\n const cert = rec[33] ?? rec['x5chain'] ?? rec['33'];\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n }\n return null;\n}\n\n/**\n * Maps raw mDOC IssuerSignedItem elements to the CredentialClaims structure,\n * picking known age/residency fields and preserving any additional identifiers.\n */\nfunction mapToCredentialClaims(items: IssuerSignedItem[]): CredentialClaims {\n const claims: CredentialClaims = {};\n\n for (const item of items) {\n const key = item.elementIdentifier;\n const value = item.elementValue;\n\n switch (key) {\n case 'age_over_18':\n if (typeof value === 'boolean') claims.age_over_18 = value;\n break;\n case 'age_over_21':\n if (typeof value === 'boolean') claims.age_over_21 = value;\n break;\n case 'resident_country':\n if (typeof value === 'string') claims.resident_country = value;\n break;\n case 'nationality':\n if (typeof value === 'string') claims.nationality = value;\n break;\n case 'family_name_birth':\n if (typeof value === 'string') claims.family_name_birth = value;\n break;\n default:\n claims[key] = value;\n break;\n }\n }\n\n return claims;\n}\n\n/**\n * Extracts validity period from the issuerAuth payload.\n *\n * The payload (index 2) of the COSE_Sign1 is CBOR-encoded and should\n * contain a MobileSecurityObject with a validityInfo field carrying\n * `validFrom` and `validUntil` dates.\n */\nfunction extractValidityPeriod(issuerAuth: unknown[]): { validFrom?: Date; validUntil?: Date } {\n const payload = issuerAuth[2];\n if (!payload) return {};\n\n try {\n // payload can be raw CBOR bytes or already decoded\n const decoded = isBinaryData(payload) ? (decode(payload) as unknown) : payload;\n\n if (decoded && typeof decoded === 'object') {\n const obj = decoded as Record<string, unknown>;\n const validityInfo = obj['validityInfo'] as Record<string, unknown> | undefined;\n if (validityInfo) {\n return {\n validFrom:\n validityInfo['validFrom'] instanceof Date\n ? validityInfo['validFrom']\n : typeof validityInfo['validFrom'] === 'string'\n ? new Date(validityInfo['validFrom'])\n : undefined,\n validUntil:\n validityInfo['validUntil'] instanceof Date\n ? validityInfo['validUntil']\n : typeof validityInfo['validUntil'] === 'string'\n ? new Date(validityInfo['validUntil'])\n : undefined,\n };\n }\n }\n } catch {\n // Could not decode payload -- validity check will be skipped\n }\n\n return {};\n}\n\n/**\n * mDOC / mDL credential parser.\n *\n * Decodes and validates mDOC (Mobile Document) credentials as specified in\n * ISO 18013-5 and the EUDI Wallet ecosystem. Processes CBOR-encoded\n * DeviceResponse structures containing:\n * - Document type validation (eu.europa.ec.eudi.pid.1)\n * - Issuer certificate trust chain verification via COSE_Sign1 issuerAuth\n * - Validity period checking\n * - Claim extraction from IssuerSignedItem nameSpace elements\n */\nexport class MdocParser implements ICredentialParser {\n readonly format: CredentialFormat = 'mdoc';\n\n /**\n * Returns true when the token is binary data (Uint8Array or Buffer),\n * indicating a CBOR-encoded mDOC DeviceResponse.\n */\n canParse(vpToken: unknown): boolean {\n return isBinaryData(vpToken);\n }\n\n /**\n * Parses and validates an mDOC DeviceResponse token.\n *\n * @throws {MalformedCredentialError} when the CBOR structure cannot be decoded\n * or does not match the expected DeviceResponse format\n */\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (!isBinaryData(vpToken)) {\n throw new MalformedCredentialError('VP token must be a Uint8Array or Buffer');\n }\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Decode the CBOR DeviceResponse\n let deviceResponse: DeviceResponse;\n try {\n deviceResponse = decode(vpToken) as DeviceResponse;\n } catch {\n throw new MalformedCredentialError('Failed to decode CBOR DeviceResponse');\n }\n\n // Step 2: Validate basic DeviceResponse structure\n if (\n !deviceResponse ||\n typeof deviceResponse !== 'object' ||\n !Array.isArray(deviceResponse.documents) ||\n deviceResponse.documents.length === 0\n ) {\n throw new MalformedCredentialError('Invalid DeviceResponse structure: missing or empty documents array');\n }\n\n const document = deviceResponse.documents[0];\n if (!document.issuerSigned || !document.issuerSigned.nameSpaces || !document.issuerSigned.issuerAuth) {\n throw new MalformedCredentialError('Invalid mDOC document: missing issuerSigned data');\n }\n\n // Step 3: Extract issuer certificate from COSE_Sign1 issuerAuth\n const issuerAuth = document.issuerSigned.issuerAuth;\n if (!Array.isArray(issuerAuth) || issuerAuth.length < 4) {\n throw new MalformedCredentialError('Invalid issuerAuth: expected COSE_Sign1 array with 4 elements');\n }\n\n const issuerCertBytes = extractCertificateFromIssuerAuth(issuerAuth);\n const issuerInfo: IssuerInfo = {\n certificate: issuerCertBytes,\n country: '',\n };\n\n // Step 4: Verify trust -- certificate must be in the trusted set\n if (options.trustedCertificates.length > 0) {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n } else if (issuerCertBytes.length === 0) {\n return invalidResult('No issuer certificate found and no trusted certificates configured');\n }\n\n // Step 5: Check validity period\n const { validUntil } = extractValidityPeriod(issuerAuth);\n if (validUntil) {\n const now = new Date();\n if (validUntil < now) {\n return invalidResult('Credential has expired');\n }\n }\n\n // Step 6: Extract claims from nameSpaces\n // Prefer the EUDI PID namespace, fall back to the first available namespace\n const pidNamespace = 'eu.europa.ec.eudi.pid.1';\n const nameSpaces = document.issuerSigned.nameSpaces;\n const items: IssuerSignedItem[] =\n nameSpaces[pidNamespace] ?? nameSpaces[document.docType] ?? Object.values(nameSpaces)[0] ?? [];\n\n const claims = mapToCredentialClaims(items);\n\n // Try to derive country from resident_country claim\n if (claims.resident_country && typeof claims.resident_country === 'string') {\n issuerInfo.country = claims.resident_country;\n }\n\n return {\n valid: true,\n format: this.format,\n claims,\n issuer: issuerInfo,\n };\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\n\nimport type { AuthorizationRequestInput, AuthorizationRequest } from './types/authorization.js';\n\nexport function createAuthorizationRequest(input: AuthorizationRequestInput): AuthorizationRequest {\n const state = input.state ?? uuidv4();\n\n const presentationDefinition = {\n id: uuidv4(),\n input_descriptors: input.requestedAttributes.map((attr, index) => ({\n id: `descriptor_${index}`,\n format: Object.fromEntries(\n input.acceptedFormats.map((fmt) => {\n if (fmt === 'sd-jwt-vc') return ['vc+sd-jwt', { 'sd-jwt_alg_values': ['ES256'] }];\n return ['mso_mdoc', { alg: ['ES256'] }];\n })\n ),\n constraints: {\n fields: [\n {\n path: [`$.${attr}`, `$['${attr}']`],\n filter: { type: attr.startsWith('age_over') ? 'boolean' : 'string' },\n },\n ],\n },\n })),\n };\n\n const params = new URLSearchParams({\n response_type: 'vp_token',\n response_mode: 'direct_post',\n response_uri: input.responseUri,\n client_id: input.clientId,\n nonce: input.nonce,\n state,\n presentation_definition: JSON.stringify(presentationDefinition),\n });\n\n return {\n uri: `openid4vp://authorize?${params.toString()}`,\n presentationDefinition,\n nonce: input.nonce,\n state,\n };\n}\n","import { MdocParser } from './parsers/mdoc.parser.js';\nimport type { ParseOptions, ICredentialParser } from './parsers/parser.interface.js';\nimport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nimport type { PresentationResult } from './types/presentation.js';\n\nconst parsers: ICredentialParser[] = [new SdJwtParser(), new MdocParser()];\n\nexport async function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n for (const parser of parsers) {\n if (parser.canParse(vpToken)) {\n return parser.parse(vpToken, options);\n }\n }\n\n return {\n valid: false,\n format: 'sd-jwt-vc',\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error: 'Unsupported credential format',\n };\n}\n\nexport type { ParseOptions } from './parsers/parser.interface.js';\n","export const VERSION = '0.1.0';\n\nexport type {\n CredentialFormat,\n CredentialClaims,\n PresentationResult,\n IssuerInfo,\n AuthorizationRequestInput,\n AuthorizationRequest,\n} from './types/index.js';\n\nexport type { ICredentialParser, ParseOptions } from './parsers/parser.interface.js';\n\nexport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nexport { MdocParser } from './parsers/mdoc.parser.js';\n\nexport {\n InvalidSignatureError,\n ExpiredCredentialError,\n UnsupportedFormatError,\n MalformedCredentialError,\n NonceValidationError,\n} from './errors.js';\n\nexport { createAuthorizationRequest } from './authorization.js';\n\nexport { parsePresentation } from './presentation.js';\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/parsers/sd-jwt.parser.ts","../src/parsers/mdoc.parser.ts","../src/authorization.ts","../src/presentation.ts","../src/index.ts"],"names":["splitSdJwt","decodeProtectedHeader","importX509","jwtVerify","importJWK","decodeSdJwt","getClaims","bytesEqual","decode","mapToCredentialClaims","uuidv4"],"mappings":"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","file":"index.cjs","sourcesContent":["export class InvalidSignatureError extends Error {\n constructor(message = 'Credential signature validation failed') {\n super(message);\n this.name = 'InvalidSignatureError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ExpiredCredentialError extends Error {\n constructor(message = 'Credential has expired') {\n super(message);\n this.name = 'ExpiredCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class UnsupportedFormatError extends Error {\n constructor(format: string) {\n super(`Unsupported credential format: ${format}`);\n this.name = 'UnsupportedFormatError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class MalformedCredentialError extends Error {\n constructor(message = 'Credential structure is malformed') {\n super(message);\n this.name = 'MalformedCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class NonceValidationError extends Error {\n constructor(message = 'Nonce does not match expected value') {\n super(message);\n this.name = 'NonceValidationError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { splitSdJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';\nimport { decodeProtectedHeader, jwtVerify, importX509, importJWK } from 'jose';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Hasher function signature expected by @sd-jwt/decode.\n * Inlined here to avoid a direct dependency on @sd-jwt/types.\n */\ntype SdJwtHasher = (data: string | ArrayBuffer, alg: string) => Promise<Uint8Array>;\n\nconst DEFAULT_ALLOWED_ALGORITHMS = ['ES256', 'ES384', 'ES512'];\n\n/**\n * SHA-256 hasher implementation using the Web Crypto API.\n * Compatible with the @sd-jwt/decode Hasher signature.\n */\nconst sha256Hasher: SdJwtHasher = async (data: string | ArrayBuffer, _alg: string): Promise<Uint8Array> => {\n const encoder = new TextEncoder();\n const input = typeof data === 'string' ? encoder.encode(data) : data;\n const hashBuffer = await crypto.subtle.digest('SHA-256', input);\n return new Uint8Array(hashBuffer);\n};\n\n// Standard base64 alphabet for decoding x5c certificate entries\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/**\n * Decodes a standard base64-encoded string into raw bytes.\n * Does not depend on Node.js Buffer or browser atob.\n */\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const cleaned = base64.replace(/=+$/, '');\n const byteLength = Math.floor((cleaned.length * 3) / 4);\n const buffer = new ArrayBuffer(byteLength);\n const bytes = new Uint8Array(buffer);\n\n let byteIndex = 0;\n for (let i = 0; i < cleaned.length; i += 4) {\n const a = BASE64_CHARS.indexOf(cleaned[i]);\n const b = i + 1 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 1]) : 0;\n const c = i + 2 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 2]) : 0;\n const d = i + 3 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 3]) : 0;\n\n bytes[byteIndex++] = (a << 2) | (b >> 4);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((b & 0x0f) << 4) | (c >> 2);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((c & 0x03) << 6) | d;\n }\n\n return bytes;\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Converts a base64-encoded DER certificate to PEM format for jose.importX509.\n */\nfunction derToPem(base64Der: string): string {\n const lines = base64Der.match(/.{1,64}/g) || [];\n return `-----BEGIN CERTIFICATE-----\\n${lines.join('\\n')}\\n-----END CERTIFICATE-----`;\n}\n\n/**\n * Encodes a Uint8Array to base64url (no padding).\n * Used by disclosure hash verification (Task 5) and KB-JWT verification (Task 6).\n */\nfunction bytesToBase64url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return globalThis.btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Maps raw decoded claims to the CredentialClaims structure,\n * picking known age/residency fields and preserving any extra keys.\n */\nfunction mapToCredentialClaims(raw: Record<string, unknown>): CredentialClaims {\n const claims: CredentialClaims = {};\n\n if (typeof raw['age_over_18'] === 'boolean') {\n claims.age_over_18 = raw['age_over_18'];\n }\n if (typeof raw['age_over_21'] === 'boolean') {\n claims.age_over_21 = raw['age_over_21'];\n }\n if (typeof raw['resident_country'] === 'string') {\n claims.resident_country = raw['resident_country'];\n }\n if (typeof raw['nationality'] === 'string') {\n claims.nationality = raw['nationality'];\n }\n if (typeof raw['family_name_birth'] === 'string') {\n claims.family_name_birth = raw['family_name_birth'];\n }\n\n for (const [key, value] of Object.entries(raw)) {\n if (\n !(key in claims) &&\n !key.startsWith('_sd') &&\n key !== 'iss' &&\n key !== 'exp' &&\n key !== 'iat' &&\n key !== 'cnf' &&\n key !== 'vct'\n ) {\n claims[key] = value;\n }\n }\n\n return claims;\n}\n\n/**\n * Best-effort extraction of a country code hint from an issuer URL or string.\n */\nfunction extractCountryHint(issuer: string): string {\n try {\n const url = new URL(issuer);\n const tld = url.hostname.split('.').pop()?.toUpperCase();\n if (tld && tld.length === 2) {\n return tld;\n }\n } catch {\n // Not a valid URL\n }\n return '';\n}\n\n/**\n * SD-JWT VC credential parser.\n *\n * Decodes and cryptographically validates SD-JWT Verifiable Credentials:\n * - JWT signature verification via x5c public key (jose)\n * - Issuer certificate trust chain verification\n * - Disclosure hash integrity verification\n * - Key binding JWT signature and sd_hash verification\n * - Selective disclosure claim extraction\n */\nexport class SdJwtParser implements ICredentialParser {\n readonly format: CredentialFormat = 'sd-jwt-vc';\n\n canParse(vpToken: unknown): boolean {\n return typeof vpToken === 'string' && vpToken.includes('~');\n }\n\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (typeof vpToken !== 'string') {\n throw new MalformedCredentialError('VP token must be a string');\n }\n\n const allowedAlgorithms = options.allowedAlgorithms ?? DEFAULT_ALLOWED_ALGORITHMS;\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Split the SD-JWT into components\n let parts: ReturnType<typeof splitSdJwt>;\n try {\n parts = splitSdJwt(vpToken);\n } catch {\n throw new MalformedCredentialError('Failed to split SD-JWT structure');\n }\n\n // Step 2: Peek at issuer JWT header (need alg and x5c before verification)\n let header: ReturnType<typeof decodeProtectedHeader>;\n try {\n header = decodeProtectedHeader(parts.jwt);\n } catch {\n throw new MalformedCredentialError('Failed to decode SD-JWT issuer JWT header');\n }\n\n // Step 3: Check algorithm against allowlist\n const alg = header.alg;\n if (typeof alg !== 'string' || !allowedAlgorithms.includes(alg)) {\n return invalidResult(`Unsupported algorithm: ${alg}`);\n }\n\n // Step 4: Extract public key from x5c certificate\n const x5cArray = header.x5c;\n if (!Array.isArray(x5cArray) || x5cArray.length === 0 || typeof x5cArray[0] !== 'string') {\n throw new MalformedCredentialError('Missing or invalid x5c in JWT header');\n }\n\n let issuerKey: Awaited<ReturnType<typeof importX509>>;\n let issuerCertBytes: Uint8Array;\n try {\n issuerKey = await importX509(derToPem(x5cArray[0]), alg);\n issuerCertBytes = base64ToBytes(x5cArray[0]);\n } catch (err) {\n if (err instanceof MalformedCredentialError) throw err;\n throw new MalformedCredentialError('Failed to import issuer certificate');\n }\n\n // Step 5: Verify issuer JWT signature (also validates exp, iat via jose)\n let payload: Record<string, unknown>;\n try {\n const result = await jwtVerify(parts.jwt, issuerKey, { algorithms: allowedAlgorithms });\n payload = result.payload as unknown as Record<string, unknown>;\n } catch {\n return invalidResult('Issuer JWT signature verification failed');\n }\n\n // Step 6: Trust check — certificate must be in the trusted set\n // unless the caller explicitly opts out via skipTrustCheck.\n const issuerString = typeof payload['iss'] === 'string' ? payload['iss'] : '';\n const issuerInfo: IssuerInfo = {\n certificate: issuerCertBytes,\n country: extractCountryHint(issuerString),\n };\n\n if (options.trustedCertificates.length === 0) {\n if (options.skipTrustCheck !== true) {\n throw new MalformedCredentialError(\n 'trustedCertificates must not be empty unless skipTrustCheck is true'\n );\n }\n } else {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n }\n\n // Step 7: Verify disclosure hashes against _sd array\n const sdArray = payload['_sd'];\n if (parts.disclosures.length > 0) {\n if (!Array.isArray(sdArray) || sdArray.length === 0) {\n return invalidResult('Disclosures present but no _sd array in issuer JWT');\n }\n for (const disclosure of parts.disclosures) {\n const hashBytes = await sha256Hasher(disclosure, 'sha-256');\n const hashB64url = bytesToBase64url(hashBytes);\n if (!sdArray.includes(hashB64url)) {\n return invalidResult('Disclosure hash mismatch');\n }\n }\n }\n\n // Step 8: Verify key binding JWT (if present)\n if (parts.kbJwt) {\n // Extract holder public key from cnf claim\n const cnf = payload['cnf'] as Record<string, unknown> | undefined;\n if (!cnf || typeof cnf !== 'object' || !cnf['jwk']) {\n throw new MalformedCredentialError('Missing cnf.jwk in issuer JWT for key binding');\n }\n\n let holderKey: Awaited<ReturnType<typeof importJWK>>;\n try {\n const kbHeader = decodeProtectedHeader(parts.kbJwt);\n const kbAlg = kbHeader.alg;\n if (typeof kbAlg !== 'string' || !allowedAlgorithms.includes(kbAlg)) {\n return invalidResult(`Unsupported KB-JWT algorithm: ${kbAlg}`);\n }\n holderKey = await importJWK(cnf['jwk'] as Record<string, unknown>, kbAlg);\n } catch (err) {\n if (err instanceof MalformedCredentialError) throw err;\n throw new MalformedCredentialError('Failed to import holder key from cnf.jwk');\n }\n\n // Verify KB-JWT signature\n let kbPayload: Record<string, unknown>;\n try {\n const kbResult = await jwtVerify(parts.kbJwt, holderKey, {\n algorithms: allowedAlgorithms,\n ...(options.audience ? { audience: options.audience } : {}),\n });\n kbPayload = kbResult.payload as unknown as Record<string, unknown>;\n } catch {\n return invalidResult('Key binding JWT signature verification failed');\n }\n\n // Validate nonce — must be present and match when KB-JWT is used\n if (kbPayload['nonce'] !== options.nonce) {\n return invalidResult('Key binding JWT nonce does not match');\n }\n\n // Verify sd_hash — hash of the SD-JWT content before the KB-JWT\n const disclosurePart = parts.disclosures.length > 0 ? parts.disclosures.join('~') + '~' : '';\n const sdJwtForHash = parts.jwt + '~' + disclosurePart;\n const sdHashBytes = await sha256Hasher(sdJwtForHash, 'sha-256');\n const expectedSdHash = bytesToBase64url(sdHashBytes);\n if (kbPayload['sd_hash'] !== expectedSdHash) {\n return invalidResult('Key binding JWT sd_hash does not match');\n }\n }\n\n // Step 9: Decode disclosures and extract claims\n let resolvedClaims: Record<string, unknown>;\n try {\n const decoded = await decodeSdJwt(vpToken, sha256Hasher);\n resolvedClaims = await getClaims<Record<string, unknown>>(\n decoded.jwt.payload,\n decoded.disclosures,\n sha256Hasher\n );\n } catch {\n throw new MalformedCredentialError('Failed to decode SD-JWT disclosures');\n }\n\n return {\n valid: true,\n format: this.format,\n claims: mapToCredentialClaims(resolvedClaims),\n issuer: issuerInfo,\n };\n }\n}\n","import { decode } from 'cbor-x';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Runtime check for binary data. Returns true for Uint8Array and Node.js Buffer\n * without referencing the Buffer type (which is unavailable in DOM-only tsconfig).\n * Buffer extends Uint8Array, so `instanceof Uint8Array` catches both.\n */\nfunction isBinaryData(value: unknown): value is Uint8Array {\n return value instanceof Uint8Array;\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Represents a single IssuerSignedItem element from an mDOC nameSpace.\n * ISO 18013-5 Section 8.3.2.1.2.\n */\ninterface IssuerSignedItem {\n digestID: number;\n random: Uint8Array | unknown;\n elementIdentifier: string;\n elementValue: unknown;\n}\n\n/**\n * Simplified DeviceResponse structure from ISO 18013-5.\n * Only the fields required for claim extraction and trust validation.\n */\ninterface MdocDocument {\n docType: string;\n issuerSigned: {\n nameSpaces: Record<string, IssuerSignedItem[]>;\n issuerAuth: unknown[];\n };\n}\n\ninterface DeviceResponse {\n version: string;\n documents: MdocDocument[];\n status: number;\n}\n\n/**\n * Extracts the issuer certificate bytes from a COSE_Sign1 issuerAuth structure.\n *\n * The issuerAuth is a COSE_Sign1 array: [protected, unprotected, payload, signature].\n * The protected header (index 0) is CBOR-encoded and may contain the x5chain (label 33)\n * with the issuer certificate. The unprotected header (index 1) may also carry it.\n *\n * If neither header contains a certificate, the raw protected bytes are returned\n * as a fallback for trust matching (test/simplified scenarios).\n */\nfunction extractCertificateFromIssuerAuth(issuerAuth: unknown[]): Uint8Array {\n // Try unprotected header first (index 1) -- it's already decoded as a Map or object\n const unprotected = issuerAuth[1];\n if (unprotected && typeof unprotected === 'object') {\n // x5chain is COSE header label 33\n const certFromUnprotected = extractX5Chain(unprotected);\n if (certFromUnprotected) return certFromUnprotected;\n }\n\n // Try protected header (index 0) -- CBOR-encoded bytes\n const protectedHeader = issuerAuth[0];\n if (isBinaryData(protectedHeader)) {\n try {\n const decoded = decode(protectedHeader) as unknown;\n if (decoded && typeof decoded === 'object') {\n const certFromProtected = extractX5Chain(decoded);\n if (certFromProtected) return certFromProtected;\n }\n } catch {\n // Protected header could not be decoded -- fall through\n }\n // Return raw protected bytes as fallback identifier\n return new Uint8Array(protectedHeader);\n }\n\n return new Uint8Array();\n}\n\n/**\n * Extracts certificate bytes from an x5chain (label 33) in a COSE header.\n * The header can be a plain object or a Map.\n */\nfunction extractX5Chain(header: unknown): Uint8Array | null {\n if (header instanceof Map) {\n const cert = header.get(33) as unknown;\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n } else if (typeof header === 'object' && header !== null) {\n const rec = header as Record<string | number, unknown>;\n const cert = rec[33] ?? rec['x5chain'] ?? rec['33'];\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n }\n return null;\n}\n\n/**\n * Maps raw mDOC IssuerSignedItem elements to the CredentialClaims structure,\n * picking known age/residency fields and preserving any additional identifiers.\n */\nfunction mapToCredentialClaims(items: IssuerSignedItem[]): CredentialClaims {\n const claims: CredentialClaims = {};\n\n for (const item of items) {\n const key = item.elementIdentifier;\n const value = item.elementValue;\n\n switch (key) {\n case 'age_over_18':\n if (typeof value === 'boolean') claims.age_over_18 = value;\n break;\n case 'age_over_21':\n if (typeof value === 'boolean') claims.age_over_21 = value;\n break;\n case 'resident_country':\n if (typeof value === 'string') claims.resident_country = value;\n break;\n case 'nationality':\n if (typeof value === 'string') claims.nationality = value;\n break;\n case 'family_name_birth':\n if (typeof value === 'string') claims.family_name_birth = value;\n break;\n default:\n claims[key] = value;\n break;\n }\n }\n\n return claims;\n}\n\n/**\n * Extracts validity period from the issuerAuth payload.\n *\n * The payload (index 2) of the COSE_Sign1 is CBOR-encoded and should\n * contain a MobileSecurityObject with a validityInfo field carrying\n * `validFrom` and `validUntil` dates.\n */\nfunction extractValidityPeriod(issuerAuth: unknown[]): { validFrom?: Date; validUntil?: Date } {\n const payload = issuerAuth[2];\n if (!payload) return {};\n\n try {\n // payload can be raw CBOR bytes or already decoded\n const decoded = isBinaryData(payload) ? (decode(payload) as unknown) : payload;\n\n if (decoded && typeof decoded === 'object') {\n const obj = decoded as Record<string, unknown>;\n const validityInfo = obj['validityInfo'] as Record<string, unknown> | undefined;\n if (validityInfo) {\n return {\n validFrom:\n validityInfo['validFrom'] instanceof Date\n ? validityInfo['validFrom']\n : typeof validityInfo['validFrom'] === 'string'\n ? new Date(validityInfo['validFrom'])\n : undefined,\n validUntil:\n validityInfo['validUntil'] instanceof Date\n ? validityInfo['validUntil']\n : typeof validityInfo['validUntil'] === 'string'\n ? new Date(validityInfo['validUntil'])\n : undefined,\n };\n }\n }\n } catch {\n // Could not decode payload -- validity check will be skipped\n }\n\n return {};\n}\n\n/**\n * mDOC / mDL credential parser.\n *\n * Decodes and validates mDOC (Mobile Document) credentials as specified in\n * ISO 18013-5 and the EUDI Wallet ecosystem. Processes CBOR-encoded\n * DeviceResponse structures containing:\n * - Document type validation (eu.europa.ec.eudi.pid.1)\n * - Issuer certificate trust chain verification via COSE_Sign1 issuerAuth\n * - Validity period checking\n * - Claim extraction from IssuerSignedItem nameSpace elements\n */\nexport class MdocParser implements ICredentialParser {\n readonly format: CredentialFormat = 'mdoc';\n\n /**\n * Returns true when the token is binary data (Uint8Array or Buffer),\n * indicating a CBOR-encoded mDOC DeviceResponse.\n */\n canParse(vpToken: unknown): boolean {\n return isBinaryData(vpToken);\n }\n\n /**\n * Parses and validates an mDOC DeviceResponse token.\n *\n * @throws {MalformedCredentialError} when the CBOR structure cannot be decoded\n * or does not match the expected DeviceResponse format\n */\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (!isBinaryData(vpToken)) {\n throw new MalformedCredentialError('VP token must be a Uint8Array or Buffer');\n }\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Decode the CBOR DeviceResponse\n let deviceResponse: DeviceResponse;\n try {\n deviceResponse = decode(vpToken) as DeviceResponse;\n } catch {\n throw new MalformedCredentialError('Failed to decode CBOR DeviceResponse');\n }\n\n // Step 2: Validate basic DeviceResponse structure\n if (\n !deviceResponse ||\n typeof deviceResponse !== 'object' ||\n !Array.isArray(deviceResponse.documents) ||\n deviceResponse.documents.length === 0\n ) {\n throw new MalformedCredentialError('Invalid DeviceResponse structure: missing or empty documents array');\n }\n\n const document = deviceResponse.documents[0];\n if (!document.issuerSigned || !document.issuerSigned.nameSpaces || !document.issuerSigned.issuerAuth) {\n throw new MalformedCredentialError('Invalid mDOC document: missing issuerSigned data');\n }\n\n // Step 3: Extract issuer certificate from COSE_Sign1 issuerAuth\n const issuerAuth = document.issuerSigned.issuerAuth;\n if (!Array.isArray(issuerAuth) || issuerAuth.length < 4) {\n throw new MalformedCredentialError('Invalid issuerAuth: expected COSE_Sign1 array with 4 elements');\n }\n\n const issuerCertBytes = extractCertificateFromIssuerAuth(issuerAuth);\n const issuerInfo: IssuerInfo = {\n certificate: issuerCertBytes,\n country: '',\n };\n\n // Step 4: Verify trust -- certificate must be in the trusted set\n // unless the caller explicitly opts out via skipTrustCheck.\n if (options.trustedCertificates.length === 0) {\n if (options.skipTrustCheck !== true) {\n throw new MalformedCredentialError(\n 'trustedCertificates must not be empty unless skipTrustCheck is true'\n );\n }\n } else {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n }\n\n // Step 5: Check validity period\n const { validUntil } = extractValidityPeriod(issuerAuth);\n if (validUntil) {\n const now = new Date();\n if (validUntil < now) {\n return invalidResult('Credential has expired');\n }\n }\n\n // Step 6: Extract claims from nameSpaces\n // Prefer the EUDI PID namespace, fall back to the first available namespace\n const pidNamespace = 'eu.europa.ec.eudi.pid.1';\n const nameSpaces = document.issuerSigned.nameSpaces;\n const items: IssuerSignedItem[] =\n nameSpaces[pidNamespace] ?? nameSpaces[document.docType] ?? Object.values(nameSpaces)[0] ?? [];\n\n const claims = mapToCredentialClaims(items);\n\n // Try to derive country from resident_country claim\n if (claims.resident_country && typeof claims.resident_country === 'string') {\n issuerInfo.country = claims.resident_country;\n }\n\n return {\n valid: true,\n format: this.format,\n claims,\n issuer: issuerInfo,\n };\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\n\nimport type { AuthorizationRequestInput, AuthorizationRequest } from './types/authorization.js';\n\nexport function createAuthorizationRequest(input: AuthorizationRequestInput): AuthorizationRequest {\n const state = input.state ?? uuidv4();\n\n const presentationDefinition = {\n id: uuidv4(),\n input_descriptors: input.requestedAttributes.map((attr, index) => ({\n id: `descriptor_${index}`,\n format: Object.fromEntries(\n input.acceptedFormats.map((fmt) => {\n if (fmt === 'sd-jwt-vc') return ['vc+sd-jwt', { 'sd-jwt_alg_values': ['ES256'] }];\n return ['mso_mdoc', { alg: ['ES256'] }];\n })\n ),\n constraints: {\n fields: [\n {\n path: [`$.${attr}`, `$['${attr}']`],\n filter: { type: attr.startsWith('age_over') ? 'boolean' : 'string' },\n },\n ],\n },\n })),\n };\n\n const params = new URLSearchParams({\n response_type: 'vp_token',\n response_mode: 'direct_post',\n response_uri: input.responseUri,\n client_id: input.clientId,\n nonce: input.nonce,\n state,\n presentation_definition: JSON.stringify(presentationDefinition),\n });\n\n return {\n uri: `openid4vp://authorize?${params.toString()}`,\n presentationDefinition,\n nonce: input.nonce,\n state,\n };\n}\n","import { MdocParser } from './parsers/mdoc.parser.js';\nimport type { ParseOptions, ICredentialParser } from './parsers/parser.interface.js';\nimport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nimport type { PresentationResult } from './types/presentation.js';\n\nconst parsers: ICredentialParser[] = [new SdJwtParser(), new MdocParser()];\n\nexport async function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n for (const parser of parsers) {\n if (parser.canParse(vpToken)) {\n return parser.parse(vpToken, options);\n }\n }\n\n return {\n valid: false,\n format: 'sd-jwt-vc',\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error: 'Unsupported credential format',\n };\n}\n\nexport type { ParseOptions } from './parsers/parser.interface.js';\n","export const VERSION = '0.2.1';\n\nexport type {\n CredentialFormat,\n CredentialClaims,\n PresentationResult,\n IssuerInfo,\n AuthorizationRequestInput,\n AuthorizationRequest,\n} from './types/index.js';\n\nexport type { ICredentialParser, ParseOptions } from './parsers/parser.interface.js';\n\nexport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nexport { MdocParser } from './parsers/mdoc.parser.js';\n\nexport {\n InvalidSignatureError,\n ExpiredCredentialError,\n UnsupportedFormatError,\n MalformedCredentialError,\n NonceValidationError,\n} from './errors.js';\n\nexport { createAuthorizationRequest } from './authorization.js';\n\nexport { parsePresentation } from './presentation.js';\n"]}
package/dist/index.d.cts CHANGED
@@ -40,6 +40,17 @@ interface AuthorizationRequest {
40
40
  interface ParseOptions {
41
41
  trustedCertificates: Uint8Array[];
42
42
  nonce: string;
43
+ /** Expected audience for key binding JWT verification. Optional. */
44
+ audience?: string;
45
+ /** Allowed JWT signature algorithms. Defaults to ['ES256', 'ES384', 'ES512']. */
46
+ allowedAlgorithms?: string[];
47
+ /**
48
+ * Explicit opt-in to skip the trust check. When omitted or `false`,
49
+ * `trustedCertificates` must be non-empty — otherwise parsing throws
50
+ * `MalformedCredentialError`. Set to `true` for demo/mock environments
51
+ * where no trusted issuer set is available.
52
+ */
53
+ skipTrustCheck?: boolean;
43
54
  }
44
55
  interface ICredentialParser {
45
56
  readonly format: CredentialFormat;
@@ -50,26 +61,16 @@ interface ICredentialParser {
50
61
  /**
51
62
  * SD-JWT VC credential parser.
52
63
  *
53
- * Decodes and validates SD-JWT Verifiable Credentials as specified in
54
- * the EUDI Wallet ecosystem. Performs:
55
- * - Structure validation (JWT~disclosure~kb format)
64
+ * Decodes and cryptographically validates SD-JWT Verifiable Credentials:
65
+ * - JWT signature verification via x5c public key (jose)
56
66
  * - Issuer certificate trust chain verification
57
- * - Expiry checking
58
- * - Nonce validation against the key binding JWT
67
+ * - Disclosure hash integrity verification
68
+ * - Key binding JWT signature and sd_hash verification
59
69
  * - Selective disclosure claim extraction
60
70
  */
61
71
  declare class SdJwtParser implements ICredentialParser {
62
72
  readonly format: CredentialFormat;
63
- /**
64
- * Returns true when the token looks like an SD-JWT:
65
- * it must be a string containing the `~` disclosure separator.
66
- */
67
73
  canParse(vpToken: unknown): boolean;
68
- /**
69
- * Parses and validates an SD-JWT VC token.
70
- *
71
- * @throws {MalformedCredentialError} when the token structure cannot be decoded
72
- */
73
74
  parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
74
75
  }
75
76
 
@@ -120,6 +121,6 @@ declare function createAuthorizationRequest(input: AuthorizationRequestInput): A
120
121
 
121
122
  declare function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
122
123
 
123
- declare const VERSION = "0.1.0";
124
+ declare const VERSION = "0.2.1";
124
125
 
125
126
  export { type AuthorizationRequest, type AuthorizationRequestInput, type CredentialClaims, type CredentialFormat, ExpiredCredentialError, type ICredentialParser, InvalidSignatureError, type IssuerInfo, MalformedCredentialError, MdocParser, NonceValidationError, type ParseOptions, type PresentationResult, SdJwtParser, UnsupportedFormatError, VERSION, createAuthorizationRequest, parsePresentation };
package/dist/index.d.ts CHANGED
@@ -40,6 +40,17 @@ interface AuthorizationRequest {
40
40
  interface ParseOptions {
41
41
  trustedCertificates: Uint8Array[];
42
42
  nonce: string;
43
+ /** Expected audience for key binding JWT verification. Optional. */
44
+ audience?: string;
45
+ /** Allowed JWT signature algorithms. Defaults to ['ES256', 'ES384', 'ES512']. */
46
+ allowedAlgorithms?: string[];
47
+ /**
48
+ * Explicit opt-in to skip the trust check. When omitted or `false`,
49
+ * `trustedCertificates` must be non-empty — otherwise parsing throws
50
+ * `MalformedCredentialError`. Set to `true` for demo/mock environments
51
+ * where no trusted issuer set is available.
52
+ */
53
+ skipTrustCheck?: boolean;
43
54
  }
44
55
  interface ICredentialParser {
45
56
  readonly format: CredentialFormat;
@@ -50,26 +61,16 @@ interface ICredentialParser {
50
61
  /**
51
62
  * SD-JWT VC credential parser.
52
63
  *
53
- * Decodes and validates SD-JWT Verifiable Credentials as specified in
54
- * the EUDI Wallet ecosystem. Performs:
55
- * - Structure validation (JWT~disclosure~kb format)
64
+ * Decodes and cryptographically validates SD-JWT Verifiable Credentials:
65
+ * - JWT signature verification via x5c public key (jose)
56
66
  * - Issuer certificate trust chain verification
57
- * - Expiry checking
58
- * - Nonce validation against the key binding JWT
67
+ * - Disclosure hash integrity verification
68
+ * - Key binding JWT signature and sd_hash verification
59
69
  * - Selective disclosure claim extraction
60
70
  */
61
71
  declare class SdJwtParser implements ICredentialParser {
62
72
  readonly format: CredentialFormat;
63
- /**
64
- * Returns true when the token looks like an SD-JWT:
65
- * it must be a string containing the `~` disclosure separator.
66
- */
67
73
  canParse(vpToken: unknown): boolean;
68
- /**
69
- * Parses and validates an SD-JWT VC token.
70
- *
71
- * @throws {MalformedCredentialError} when the token structure cannot be decoded
72
- */
73
74
  parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
74
75
  }
75
76
 
@@ -120,6 +121,6 @@ declare function createAuthorizationRequest(input: AuthorizationRequestInput): A
120
121
 
121
122
  declare function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
122
123
 
123
- declare const VERSION = "0.1.0";
124
+ declare const VERSION = "0.2.1";
124
125
 
125
126
  export { type AuthorizationRequest, type AuthorizationRequestInput, type CredentialClaims, type CredentialFormat, ExpiredCredentialError, type ICredentialParser, InvalidSignatureError, type IssuerInfo, MalformedCredentialError, MdocParser, NonceValidationError, type ParseOptions, type PresentationResult, SdJwtParser, UnsupportedFormatError, VERSION, createAuthorizationRequest, parsePresentation };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { splitSdJwt, decodeJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';
1
+ import { splitSdJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';
2
+ import { decodeProtectedHeader, importX509, jwtVerify, importJWK } from 'jose';
2
3
  import { decode } from 'cbor-x';
3
4
  import { v4 } from 'uuid';
4
5
 
@@ -42,6 +43,7 @@ var NonceValidationError = class extends Error {
42
43
  };
43
44
 
44
45
  // src/parsers/sd-jwt.parser.ts
46
+ var DEFAULT_ALLOWED_ALGORITHMS = ["ES256", "ES384", "ES512"];
45
47
  var sha256Hasher = async (data, _alg) => {
46
48
  const encoder = new TextEncoder();
47
49
  const input = typeof data === "string" ? encoder.encode(data) : data;
@@ -66,9 +68,6 @@ function base64ToBytes(base64) {
66
68
  }
67
69
  return bytes;
68
70
  }
69
- function extractCertificateFromX5c(x5cEntry) {
70
- return base64ToBytes(x5cEntry);
71
- }
72
71
  function bytesEqual(a, b) {
73
72
  if (a.length !== b.length) return false;
74
73
  for (let i = 0; i < a.length; i++) {
@@ -76,6 +75,19 @@ function bytesEqual(a, b) {
76
75
  }
77
76
  return true;
78
77
  }
78
+ function derToPem(base64Der) {
79
+ const lines = base64Der.match(/.{1,64}/g) || [];
80
+ return `-----BEGIN CERTIFICATE-----
81
+ ${lines.join("\n")}
82
+ -----END CERTIFICATE-----`;
83
+ }
84
+ function bytesToBase64url(bytes) {
85
+ let binary = "";
86
+ for (const byte of bytes) {
87
+ binary += String.fromCharCode(byte);
88
+ }
89
+ return globalThis.btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
90
+ }
79
91
  function mapToCredentialClaims(raw) {
80
92
  const claims = {};
81
93
  if (typeof raw["age_over_18"] === "boolean") {
@@ -100,24 +112,27 @@ function mapToCredentialClaims(raw) {
100
112
  }
101
113
  return claims;
102
114
  }
115
+ function extractCountryHint(issuer) {
116
+ try {
117
+ const url = new URL(issuer);
118
+ const tld = url.hostname.split(".").pop()?.toUpperCase();
119
+ if (tld && tld.length === 2) {
120
+ return tld;
121
+ }
122
+ } catch {
123
+ }
124
+ return "";
125
+ }
103
126
  var SdJwtParser = class {
104
127
  format = "sd-jwt-vc";
105
- /**
106
- * Returns true when the token looks like an SD-JWT:
107
- * it must be a string containing the `~` disclosure separator.
108
- */
109
128
  canParse(vpToken) {
110
129
  return typeof vpToken === "string" && vpToken.includes("~");
111
130
  }
112
- /**
113
- * Parses and validates an SD-JWT VC token.
114
- *
115
- * @throws {MalformedCredentialError} when the token structure cannot be decoded
116
- */
117
131
  async parse(vpToken, options) {
118
132
  if (typeof vpToken !== "string") {
119
133
  throw new MalformedCredentialError("VP token must be a string");
120
134
  }
135
+ const allowedAlgorithms = options.allowedAlgorithms ?? DEFAULT_ALLOWED_ALGORITHMS;
121
136
  const invalidResult = (error) => ({
122
137
  valid: false,
123
138
  format: this.format,
@@ -132,54 +147,101 @@ var SdJwtParser = class {
132
147
  throw new MalformedCredentialError("Failed to split SD-JWT structure");
133
148
  }
134
149
  let header;
150
+ try {
151
+ header = decodeProtectedHeader(parts.jwt);
152
+ } catch {
153
+ throw new MalformedCredentialError("Failed to decode SD-JWT issuer JWT header");
154
+ }
155
+ const alg = header.alg;
156
+ if (typeof alg !== "string" || !allowedAlgorithms.includes(alg)) {
157
+ return invalidResult(`Unsupported algorithm: ${alg}`);
158
+ }
159
+ const x5cArray = header.x5c;
160
+ if (!Array.isArray(x5cArray) || x5cArray.length === 0 || typeof x5cArray[0] !== "string") {
161
+ throw new MalformedCredentialError("Missing or invalid x5c in JWT header");
162
+ }
163
+ let issuerKey;
164
+ let issuerCertBytes;
165
+ try {
166
+ issuerKey = await importX509(derToPem(x5cArray[0]), alg);
167
+ issuerCertBytes = base64ToBytes(x5cArray[0]);
168
+ } catch (err) {
169
+ if (err instanceof MalformedCredentialError) throw err;
170
+ throw new MalformedCredentialError("Failed to import issuer certificate");
171
+ }
135
172
  let payload;
136
173
  try {
137
- const jwt = decodeJwt(parts.jwt);
138
- header = jwt.header;
139
- payload = jwt.payload;
174
+ const result = await jwtVerify(parts.jwt, issuerKey, { algorithms: allowedAlgorithms });
175
+ payload = result.payload;
140
176
  } catch {
141
- throw new MalformedCredentialError("Failed to decode SD-JWT issuer JWT");
177
+ return invalidResult("Issuer JWT signature verification failed");
142
178
  }
143
- const x5cArray = header["x5c"];
144
- let issuerCertBytes = new Uint8Array();
145
- let issuerInfo = { certificate: new Uint8Array(), country: "" };
146
- if (Array.isArray(x5cArray) && x5cArray.length > 0 && typeof x5cArray[0] === "string") {
147
- try {
148
- issuerCertBytes = extractCertificateFromX5c(x5cArray[0]);
149
- const issuerString = typeof payload["iss"] === "string" ? payload["iss"] : "";
150
- issuerInfo = {
151
- certificate: issuerCertBytes,
152
- country: extractCountryHint(issuerString)
153
- };
154
- } catch {
155
- return invalidResult("Failed to extract issuer certificate from x5c");
179
+ const issuerString = typeof payload["iss"] === "string" ? payload["iss"] : "";
180
+ const issuerInfo = {
181
+ certificate: issuerCertBytes,
182
+ country: extractCountryHint(issuerString)
183
+ };
184
+ if (options.trustedCertificates.length === 0) {
185
+ if (options.skipTrustCheck !== true) {
186
+ throw new MalformedCredentialError(
187
+ "trustedCertificates must not be empty unless skipTrustCheck is true"
188
+ );
156
189
  }
157
- }
158
- if (options.trustedCertificates.length > 0) {
190
+ } else {
159
191
  const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));
160
192
  if (!isTrusted) {
161
193
  return invalidResult("Issuer certificate is not trusted");
162
194
  }
163
- } else if (issuerCertBytes.length === 0) {
164
- return invalidResult("No issuer certificate found and no trusted certificates configured");
165
195
  }
166
- const exp = payload["exp"];
167
- if (typeof exp === "number") {
168
- const nowSeconds = Math.floor(Date.now() / 1e3);
169
- if (exp < nowSeconds) {
170
- return invalidResult("Credential has expired");
196
+ const sdArray = payload["_sd"];
197
+ if (parts.disclosures.length > 0) {
198
+ if (!Array.isArray(sdArray) || sdArray.length === 0) {
199
+ return invalidResult("Disclosures present but no _sd array in issuer JWT");
200
+ }
201
+ for (const disclosure of parts.disclosures) {
202
+ const hashBytes = await sha256Hasher(disclosure);
203
+ const hashB64url = bytesToBase64url(hashBytes);
204
+ if (!sdArray.includes(hashB64url)) {
205
+ return invalidResult("Disclosure hash mismatch");
206
+ }
171
207
  }
172
208
  }
173
209
  if (parts.kbJwt) {
210
+ const cnf = payload["cnf"];
211
+ if (!cnf || typeof cnf !== "object" || !cnf["jwk"]) {
212
+ throw new MalformedCredentialError("Missing cnf.jwk in issuer JWT for key binding");
213
+ }
214
+ let holderKey;
174
215
  try {
175
- const kbDecoded = decodeJwt(parts.kbJwt);
176
- const kbPayload = kbDecoded.payload;
177
- const kbNonce = kbPayload["nonce"];
178
- if (typeof kbNonce === "string" && kbNonce !== options.nonce) {
179
- return invalidResult("Key binding JWT nonce does not match");
216
+ const kbHeader = decodeProtectedHeader(parts.kbJwt);
217
+ const kbAlg = kbHeader.alg;
218
+ if (typeof kbAlg !== "string" || !allowedAlgorithms.includes(kbAlg)) {
219
+ return invalidResult(`Unsupported KB-JWT algorithm: ${kbAlg}`);
180
220
  }
221
+ holderKey = await importJWK(cnf["jwk"], kbAlg);
222
+ } catch (err) {
223
+ if (err instanceof MalformedCredentialError) throw err;
224
+ throw new MalformedCredentialError("Failed to import holder key from cnf.jwk");
225
+ }
226
+ let kbPayload;
227
+ try {
228
+ const kbResult = await jwtVerify(parts.kbJwt, holderKey, {
229
+ algorithms: allowedAlgorithms,
230
+ ...options.audience ? { audience: options.audience } : {}
231
+ });
232
+ kbPayload = kbResult.payload;
181
233
  } catch {
182
- return invalidResult("Failed to decode key binding JWT");
234
+ return invalidResult("Key binding JWT signature verification failed");
235
+ }
236
+ if (kbPayload["nonce"] !== options.nonce) {
237
+ return invalidResult("Key binding JWT nonce does not match");
238
+ }
239
+ const disclosurePart = parts.disclosures.length > 0 ? parts.disclosures.join("~") + "~" : "";
240
+ const sdJwtForHash = parts.jwt + "~" + disclosurePart;
241
+ const sdHashBytes = await sha256Hasher(sdJwtForHash);
242
+ const expectedSdHash = bytesToBase64url(sdHashBytes);
243
+ if (kbPayload["sd_hash"] !== expectedSdHash) {
244
+ return invalidResult("Key binding JWT sd_hash does not match");
183
245
  }
184
246
  }
185
247
  let resolvedClaims;
@@ -191,28 +253,16 @@ var SdJwtParser = class {
191
253
  sha256Hasher
192
254
  );
193
255
  } catch {
194
- resolvedClaims = payload;
256
+ throw new MalformedCredentialError("Failed to decode SD-JWT disclosures");
195
257
  }
196
- const claims = mapToCredentialClaims(resolvedClaims);
197
258
  return {
198
259
  valid: true,
199
260
  format: this.format,
200
- claims,
261
+ claims: mapToCredentialClaims(resolvedClaims),
201
262
  issuer: issuerInfo
202
263
  };
203
264
  }
204
265
  };
205
- function extractCountryHint(issuer) {
206
- try {
207
- const url = new URL(issuer);
208
- const tld = url.hostname.split(".").pop()?.toUpperCase();
209
- if (tld && tld.length === 2) {
210
- return tld;
211
- }
212
- } catch {
213
- }
214
- return "";
215
- }
216
266
  function isBinaryData(value) {
217
267
  return value instanceof Uint8Array;
218
268
  }
@@ -353,13 +403,17 @@ var MdocParser = class {
353
403
  certificate: issuerCertBytes,
354
404
  country: ""
355
405
  };
356
- if (options.trustedCertificates.length > 0) {
406
+ if (options.trustedCertificates.length === 0) {
407
+ if (options.skipTrustCheck !== true) {
408
+ throw new MalformedCredentialError(
409
+ "trustedCertificates must not be empty unless skipTrustCheck is true"
410
+ );
411
+ }
412
+ } else {
357
413
  const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual2(trusted, issuerCertBytes));
358
414
  if (!isTrusted) {
359
415
  return invalidResult("Issuer certificate is not trusted");
360
416
  }
361
- } else if (issuerCertBytes.length === 0) {
362
- return invalidResult("No issuer certificate found and no trusted certificates configured");
363
417
  }
364
418
  const { validUntil } = extractValidityPeriod(issuerAuth);
365
419
  if (validUntil) {
@@ -440,7 +494,7 @@ async function parsePresentation(vpToken, options) {
440
494
  }
441
495
 
442
496
  // src/index.ts
443
- var VERSION = "0.1.0";
497
+ var VERSION = "0.2.1";
444
498
 
445
499
  export { ExpiredCredentialError, InvalidSignatureError, MalformedCredentialError, MdocParser, NonceValidationError, SdJwtParser, UnsupportedFormatError, VERSION, createAuthorizationRequest, parsePresentation };
446
500
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/parsers/sd-jwt.parser.ts","../src/parsers/mdoc.parser.ts","../src/authorization.ts","../src/presentation.ts","../src/index.ts"],"names":["bytesEqual","mapToCredentialClaims","uuidv4"],"mappings":"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","file":"index.js","sourcesContent":["export class InvalidSignatureError extends Error {\n constructor(message = 'Credential signature validation failed') {\n super(message);\n this.name = 'InvalidSignatureError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ExpiredCredentialError extends Error {\n constructor(message = 'Credential has expired') {\n super(message);\n this.name = 'ExpiredCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class UnsupportedFormatError extends Error {\n constructor(format: string) {\n super(`Unsupported credential format: ${format}`);\n this.name = 'UnsupportedFormatError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class MalformedCredentialError extends Error {\n constructor(message = 'Credential structure is malformed') {\n super(message);\n this.name = 'MalformedCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class NonceValidationError extends Error {\n constructor(message = 'Nonce does not match expected value') {\n super(message);\n this.name = 'NonceValidationError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { splitSdJwt, decodeJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Hasher function signature expected by @sd-jwt/decode.\n * Inlined here to avoid a direct dependency on @sd-jwt/types.\n */\ntype SdJwtHasher = (data: string | ArrayBuffer, alg: string) => Promise<Uint8Array>;\n\n/**\n * SHA-256 hasher implementation using the Web Crypto API.\n * Compatible with the @sd-jwt/decode Hasher signature.\n */\nconst sha256Hasher: SdJwtHasher = async (data: string | ArrayBuffer, _alg: string): Promise<Uint8Array> => {\n const encoder = new TextEncoder();\n const input = typeof data === 'string' ? encoder.encode(data) : data;\n const hashBuffer = await crypto.subtle.digest('SHA-256', input);\n return new Uint8Array(hashBuffer);\n};\n\n// Standard base64 alphabet for decoding x5c certificate entries\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/**\n * Decodes a standard base64-encoded string into raw bytes.\n * Does not depend on Node.js Buffer or browser atob.\n */\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n // Strip any padding characters\n const cleaned = base64.replace(/=+$/, '');\n const byteLength = Math.floor((cleaned.length * 3) / 4);\n const buffer = new ArrayBuffer(byteLength);\n const bytes = new Uint8Array(buffer);\n\n let byteIndex = 0;\n for (let i = 0; i < cleaned.length; i += 4) {\n const a = BASE64_CHARS.indexOf(cleaned[i]);\n const b = i + 1 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 1]) : 0;\n const c = i + 2 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 2]) : 0;\n const d = i + 3 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 3]) : 0;\n\n bytes[byteIndex++] = (a << 2) | (b >> 4);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((b & 0x0f) << 4) | (c >> 2);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((c & 0x03) << 6) | d;\n }\n\n return bytes;\n}\n\n/**\n * Extracts the DER-encoded certificate bytes from a base64-encoded X.509 certificate\n * found in the JWT header's x5c field.\n */\nfunction extractCertificateFromX5c(x5cEntry: string): Uint8Array<ArrayBuffer> {\n return base64ToBytes(x5cEntry);\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Maps raw decoded claims to the CredentialClaims structure,\n * picking known age/residency fields and preserving any extra keys.\n */\nfunction mapToCredentialClaims(raw: Record<string, unknown>): CredentialClaims {\n const claims: CredentialClaims = {};\n\n if (typeof raw['age_over_18'] === 'boolean') {\n claims.age_over_18 = raw['age_over_18'];\n }\n if (typeof raw['age_over_21'] === 'boolean') {\n claims.age_over_21 = raw['age_over_21'];\n }\n if (typeof raw['resident_country'] === 'string') {\n claims.resident_country = raw['resident_country'];\n }\n if (typeof raw['nationality'] === 'string') {\n claims.nationality = raw['nationality'];\n }\n if (typeof raw['family_name_birth'] === 'string') {\n claims.family_name_birth = raw['family_name_birth'];\n }\n\n // Preserve any additional claim keys not already mapped\n for (const [key, value] of Object.entries(raw)) {\n if (\n !(key in claims) &&\n !key.startsWith('_sd') &&\n key !== 'iss' &&\n key !== 'exp' &&\n key !== 'iat' &&\n key !== 'cnf' &&\n key !== 'vct'\n ) {\n claims[key] = value;\n }\n }\n\n return claims;\n}\n\n/**\n * SD-JWT VC credential parser.\n *\n * Decodes and validates SD-JWT Verifiable Credentials as specified in\n * the EUDI Wallet ecosystem. Performs:\n * - Structure validation (JWT~disclosure~kb format)\n * - Issuer certificate trust chain verification\n * - Expiry checking\n * - Nonce validation against the key binding JWT\n * - Selective disclosure claim extraction\n */\nexport class SdJwtParser implements ICredentialParser {\n readonly format: CredentialFormat = 'sd-jwt-vc';\n\n /**\n * Returns true when the token looks like an SD-JWT:\n * it must be a string containing the `~` disclosure separator.\n */\n canParse(vpToken: unknown): boolean {\n return typeof vpToken === 'string' && vpToken.includes('~');\n }\n\n /**\n * Parses and validates an SD-JWT VC token.\n *\n * @throws {MalformedCredentialError} when the token structure cannot be decoded\n */\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (typeof vpToken !== 'string') {\n throw new MalformedCredentialError('VP token must be a string');\n }\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Split the SD-JWT into its components\n let parts: ReturnType<typeof splitSdJwt>;\n try {\n parts = splitSdJwt(vpToken);\n } catch {\n throw new MalformedCredentialError('Failed to split SD-JWT structure');\n }\n\n // Step 2: Decode the issuer JWT header and payload\n let header: Record<string, unknown>;\n let payload: Record<string, unknown>;\n try {\n const jwt = decodeJwt(parts.jwt);\n header = jwt.header;\n payload = jwt.payload;\n } catch {\n throw new MalformedCredentialError('Failed to decode SD-JWT issuer JWT');\n }\n\n // Step 3: Extract issuer certificate from x5c header\n const x5cArray = header['x5c'];\n let issuerCertBytes = new Uint8Array();\n let issuerInfo: IssuerInfo = { certificate: new Uint8Array(), country: '' };\n\n if (Array.isArray(x5cArray) && x5cArray.length > 0 && typeof x5cArray[0] === 'string') {\n try {\n issuerCertBytes = extractCertificateFromX5c(x5cArray[0]);\n // Attempt to derive the country from the certificate's issuer string\n // In production, this would parse the X.509 subject, but for now we\n // fall back to payload-level hints.\n const issuerString = typeof payload['iss'] === 'string' ? payload['iss'] : '';\n issuerInfo = {\n certificate: issuerCertBytes,\n country: extractCountryHint(issuerString),\n };\n } catch {\n return invalidResult('Failed to extract issuer certificate from x5c');\n }\n }\n\n // Step 4: Verify trust — certificate must be in the trusted set\n if (options.trustedCertificates.length > 0) {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n } else if (issuerCertBytes.length === 0) {\n return invalidResult('No issuer certificate found and no trusted certificates configured');\n }\n\n // Step 5: Check expiry\n const exp = payload['exp'];\n if (typeof exp === 'number') {\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (exp < nowSeconds) {\n return invalidResult('Credential has expired');\n }\n }\n\n // Step 6: Validate nonce in key binding JWT\n if (parts.kbJwt) {\n try {\n const kbDecoded = decodeJwt(parts.kbJwt);\n const kbPayload = kbDecoded.payload as Record<string, unknown>;\n const kbNonce = kbPayload['nonce'];\n if (typeof kbNonce === 'string' && kbNonce !== options.nonce) {\n return invalidResult('Key binding JWT nonce does not match');\n }\n } catch {\n return invalidResult('Failed to decode key binding JWT');\n }\n }\n\n // Step 7: Decode disclosures and extract claims\n let resolvedClaims: Record<string, unknown>;\n try {\n const decoded = await decodeSdJwt(vpToken, sha256Hasher);\n resolvedClaims = await getClaims<Record<string, unknown>>(\n decoded.jwt.payload,\n decoded.disclosures,\n sha256Hasher\n );\n } catch {\n // If SD-JWT decoding fails (e.g., no _sd_alg), fall back to raw payload\n resolvedClaims = payload;\n }\n\n const claims = mapToCredentialClaims(resolvedClaims);\n\n return {\n valid: true,\n format: this.format,\n claims,\n issuer: issuerInfo,\n };\n }\n}\n\n/**\n * Best-effort extraction of a country code hint from an issuer URL or string.\n * For example, \"https://issuer.de/...\" would yield \"DE\".\n */\nfunction extractCountryHint(issuer: string): string {\n try {\n const url = new URL(issuer);\n const tld = url.hostname.split('.').pop()?.toUpperCase();\n if (tld && tld.length === 2) {\n return tld;\n }\n } catch {\n // Not a valid URL — ignore\n }\n return '';\n}\n","import { decode } from 'cbor-x';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Runtime check for binary data. Returns true for Uint8Array and Node.js Buffer\n * without referencing the Buffer type (which is unavailable in DOM-only tsconfig).\n * Buffer extends Uint8Array, so `instanceof Uint8Array` catches both.\n */\nfunction isBinaryData(value: unknown): value is Uint8Array {\n return value instanceof Uint8Array;\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Represents a single IssuerSignedItem element from an mDOC nameSpace.\n * ISO 18013-5 Section 8.3.2.1.2.\n */\ninterface IssuerSignedItem {\n digestID: number;\n random: Uint8Array | unknown;\n elementIdentifier: string;\n elementValue: unknown;\n}\n\n/**\n * Simplified DeviceResponse structure from ISO 18013-5.\n * Only the fields required for claim extraction and trust validation.\n */\ninterface MdocDocument {\n docType: string;\n issuerSigned: {\n nameSpaces: Record<string, IssuerSignedItem[]>;\n issuerAuth: unknown[];\n };\n}\n\ninterface DeviceResponse {\n version: string;\n documents: MdocDocument[];\n status: number;\n}\n\n/**\n * Extracts the issuer certificate bytes from a COSE_Sign1 issuerAuth structure.\n *\n * The issuerAuth is a COSE_Sign1 array: [protected, unprotected, payload, signature].\n * The protected header (index 0) is CBOR-encoded and may contain the x5chain (label 33)\n * with the issuer certificate. The unprotected header (index 1) may also carry it.\n *\n * If neither header contains a certificate, the raw protected bytes are returned\n * as a fallback for trust matching (test/simplified scenarios).\n */\nfunction extractCertificateFromIssuerAuth(issuerAuth: unknown[]): Uint8Array {\n // Try unprotected header first (index 1) -- it's already decoded as a Map or object\n const unprotected = issuerAuth[1];\n if (unprotected && typeof unprotected === 'object') {\n // x5chain is COSE header label 33\n const certFromUnprotected = extractX5Chain(unprotected);\n if (certFromUnprotected) return certFromUnprotected;\n }\n\n // Try protected header (index 0) -- CBOR-encoded bytes\n const protectedHeader = issuerAuth[0];\n if (isBinaryData(protectedHeader)) {\n try {\n const decoded = decode(protectedHeader) as unknown;\n if (decoded && typeof decoded === 'object') {\n const certFromProtected = extractX5Chain(decoded);\n if (certFromProtected) return certFromProtected;\n }\n } catch {\n // Protected header could not be decoded -- fall through\n }\n // Return raw protected bytes as fallback identifier\n return new Uint8Array(protectedHeader);\n }\n\n return new Uint8Array();\n}\n\n/**\n * Extracts certificate bytes from an x5chain (label 33) in a COSE header.\n * The header can be a plain object or a Map.\n */\nfunction extractX5Chain(header: unknown): Uint8Array | null {\n if (header instanceof Map) {\n const cert = header.get(33) as unknown;\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n } else if (typeof header === 'object' && header !== null) {\n const rec = header as Record<string | number, unknown>;\n const cert = rec[33] ?? rec['x5chain'] ?? rec['33'];\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n }\n return null;\n}\n\n/**\n * Maps raw mDOC IssuerSignedItem elements to the CredentialClaims structure,\n * picking known age/residency fields and preserving any additional identifiers.\n */\nfunction mapToCredentialClaims(items: IssuerSignedItem[]): CredentialClaims {\n const claims: CredentialClaims = {};\n\n for (const item of items) {\n const key = item.elementIdentifier;\n const value = item.elementValue;\n\n switch (key) {\n case 'age_over_18':\n if (typeof value === 'boolean') claims.age_over_18 = value;\n break;\n case 'age_over_21':\n if (typeof value === 'boolean') claims.age_over_21 = value;\n break;\n case 'resident_country':\n if (typeof value === 'string') claims.resident_country = value;\n break;\n case 'nationality':\n if (typeof value === 'string') claims.nationality = value;\n break;\n case 'family_name_birth':\n if (typeof value === 'string') claims.family_name_birth = value;\n break;\n default:\n claims[key] = value;\n break;\n }\n }\n\n return claims;\n}\n\n/**\n * Extracts validity period from the issuerAuth payload.\n *\n * The payload (index 2) of the COSE_Sign1 is CBOR-encoded and should\n * contain a MobileSecurityObject with a validityInfo field carrying\n * `validFrom` and `validUntil` dates.\n */\nfunction extractValidityPeriod(issuerAuth: unknown[]): { validFrom?: Date; validUntil?: Date } {\n const payload = issuerAuth[2];\n if (!payload) return {};\n\n try {\n // payload can be raw CBOR bytes or already decoded\n const decoded = isBinaryData(payload) ? (decode(payload) as unknown) : payload;\n\n if (decoded && typeof decoded === 'object') {\n const obj = decoded as Record<string, unknown>;\n const validityInfo = obj['validityInfo'] as Record<string, unknown> | undefined;\n if (validityInfo) {\n return {\n validFrom:\n validityInfo['validFrom'] instanceof Date\n ? validityInfo['validFrom']\n : typeof validityInfo['validFrom'] === 'string'\n ? new Date(validityInfo['validFrom'])\n : undefined,\n validUntil:\n validityInfo['validUntil'] instanceof Date\n ? validityInfo['validUntil']\n : typeof validityInfo['validUntil'] === 'string'\n ? new Date(validityInfo['validUntil'])\n : undefined,\n };\n }\n }\n } catch {\n // Could not decode payload -- validity check will be skipped\n }\n\n return {};\n}\n\n/**\n * mDOC / mDL credential parser.\n *\n * Decodes and validates mDOC (Mobile Document) credentials as specified in\n * ISO 18013-5 and the EUDI Wallet ecosystem. Processes CBOR-encoded\n * DeviceResponse structures containing:\n * - Document type validation (eu.europa.ec.eudi.pid.1)\n * - Issuer certificate trust chain verification via COSE_Sign1 issuerAuth\n * - Validity period checking\n * - Claim extraction from IssuerSignedItem nameSpace elements\n */\nexport class MdocParser implements ICredentialParser {\n readonly format: CredentialFormat = 'mdoc';\n\n /**\n * Returns true when the token is binary data (Uint8Array or Buffer),\n * indicating a CBOR-encoded mDOC DeviceResponse.\n */\n canParse(vpToken: unknown): boolean {\n return isBinaryData(vpToken);\n }\n\n /**\n * Parses and validates an mDOC DeviceResponse token.\n *\n * @throws {MalformedCredentialError} when the CBOR structure cannot be decoded\n * or does not match the expected DeviceResponse format\n */\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (!isBinaryData(vpToken)) {\n throw new MalformedCredentialError('VP token must be a Uint8Array or Buffer');\n }\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Decode the CBOR DeviceResponse\n let deviceResponse: DeviceResponse;\n try {\n deviceResponse = decode(vpToken) as DeviceResponse;\n } catch {\n throw new MalformedCredentialError('Failed to decode CBOR DeviceResponse');\n }\n\n // Step 2: Validate basic DeviceResponse structure\n if (\n !deviceResponse ||\n typeof deviceResponse !== 'object' ||\n !Array.isArray(deviceResponse.documents) ||\n deviceResponse.documents.length === 0\n ) {\n throw new MalformedCredentialError('Invalid DeviceResponse structure: missing or empty documents array');\n }\n\n const document = deviceResponse.documents[0];\n if (!document.issuerSigned || !document.issuerSigned.nameSpaces || !document.issuerSigned.issuerAuth) {\n throw new MalformedCredentialError('Invalid mDOC document: missing issuerSigned data');\n }\n\n // Step 3: Extract issuer certificate from COSE_Sign1 issuerAuth\n const issuerAuth = document.issuerSigned.issuerAuth;\n if (!Array.isArray(issuerAuth) || issuerAuth.length < 4) {\n throw new MalformedCredentialError('Invalid issuerAuth: expected COSE_Sign1 array with 4 elements');\n }\n\n const issuerCertBytes = extractCertificateFromIssuerAuth(issuerAuth);\n const issuerInfo: IssuerInfo = {\n certificate: issuerCertBytes,\n country: '',\n };\n\n // Step 4: Verify trust -- certificate must be in the trusted set\n if (options.trustedCertificates.length > 0) {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n } else if (issuerCertBytes.length === 0) {\n return invalidResult('No issuer certificate found and no trusted certificates configured');\n }\n\n // Step 5: Check validity period\n const { validUntil } = extractValidityPeriod(issuerAuth);\n if (validUntil) {\n const now = new Date();\n if (validUntil < now) {\n return invalidResult('Credential has expired');\n }\n }\n\n // Step 6: Extract claims from nameSpaces\n // Prefer the EUDI PID namespace, fall back to the first available namespace\n const pidNamespace = 'eu.europa.ec.eudi.pid.1';\n const nameSpaces = document.issuerSigned.nameSpaces;\n const items: IssuerSignedItem[] =\n nameSpaces[pidNamespace] ?? nameSpaces[document.docType] ?? Object.values(nameSpaces)[0] ?? [];\n\n const claims = mapToCredentialClaims(items);\n\n // Try to derive country from resident_country claim\n if (claims.resident_country && typeof claims.resident_country === 'string') {\n issuerInfo.country = claims.resident_country;\n }\n\n return {\n valid: true,\n format: this.format,\n claims,\n issuer: issuerInfo,\n };\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\n\nimport type { AuthorizationRequestInput, AuthorizationRequest } from './types/authorization.js';\n\nexport function createAuthorizationRequest(input: AuthorizationRequestInput): AuthorizationRequest {\n const state = input.state ?? uuidv4();\n\n const presentationDefinition = {\n id: uuidv4(),\n input_descriptors: input.requestedAttributes.map((attr, index) => ({\n id: `descriptor_${index}`,\n format: Object.fromEntries(\n input.acceptedFormats.map((fmt) => {\n if (fmt === 'sd-jwt-vc') return ['vc+sd-jwt', { 'sd-jwt_alg_values': ['ES256'] }];\n return ['mso_mdoc', { alg: ['ES256'] }];\n })\n ),\n constraints: {\n fields: [\n {\n path: [`$.${attr}`, `$['${attr}']`],\n filter: { type: attr.startsWith('age_over') ? 'boolean' : 'string' },\n },\n ],\n },\n })),\n };\n\n const params = new URLSearchParams({\n response_type: 'vp_token',\n response_mode: 'direct_post',\n response_uri: input.responseUri,\n client_id: input.clientId,\n nonce: input.nonce,\n state,\n presentation_definition: JSON.stringify(presentationDefinition),\n });\n\n return {\n uri: `openid4vp://authorize?${params.toString()}`,\n presentationDefinition,\n nonce: input.nonce,\n state,\n };\n}\n","import { MdocParser } from './parsers/mdoc.parser.js';\nimport type { ParseOptions, ICredentialParser } from './parsers/parser.interface.js';\nimport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nimport type { PresentationResult } from './types/presentation.js';\n\nconst parsers: ICredentialParser[] = [new SdJwtParser(), new MdocParser()];\n\nexport async function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n for (const parser of parsers) {\n if (parser.canParse(vpToken)) {\n return parser.parse(vpToken, options);\n }\n }\n\n return {\n valid: false,\n format: 'sd-jwt-vc',\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error: 'Unsupported credential format',\n };\n}\n\nexport type { ParseOptions } from './parsers/parser.interface.js';\n","export const VERSION = '0.1.0';\n\nexport type {\n CredentialFormat,\n CredentialClaims,\n PresentationResult,\n IssuerInfo,\n AuthorizationRequestInput,\n AuthorizationRequest,\n} from './types/index.js';\n\nexport type { ICredentialParser, ParseOptions } from './parsers/parser.interface.js';\n\nexport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nexport { MdocParser } from './parsers/mdoc.parser.js';\n\nexport {\n InvalidSignatureError,\n ExpiredCredentialError,\n UnsupportedFormatError,\n MalformedCredentialError,\n NonceValidationError,\n} from './errors.js';\n\nexport { createAuthorizationRequest } from './authorization.js';\n\nexport { parsePresentation } from './presentation.js';\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/parsers/sd-jwt.parser.ts","../src/parsers/mdoc.parser.ts","../src/authorization.ts","../src/presentation.ts","../src/index.ts"],"names":["bytesEqual","mapToCredentialClaims","uuidv4"],"mappings":"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","file":"index.js","sourcesContent":["export class InvalidSignatureError extends Error {\n constructor(message = 'Credential signature validation failed') {\n super(message);\n this.name = 'InvalidSignatureError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ExpiredCredentialError extends Error {\n constructor(message = 'Credential has expired') {\n super(message);\n this.name = 'ExpiredCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class UnsupportedFormatError extends Error {\n constructor(format: string) {\n super(`Unsupported credential format: ${format}`);\n this.name = 'UnsupportedFormatError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class MalformedCredentialError extends Error {\n constructor(message = 'Credential structure is malformed') {\n super(message);\n this.name = 'MalformedCredentialError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class NonceValidationError extends Error {\n constructor(message = 'Nonce does not match expected value') {\n super(message);\n this.name = 'NonceValidationError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { splitSdJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';\nimport { decodeProtectedHeader, jwtVerify, importX509, importJWK } from 'jose';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Hasher function signature expected by @sd-jwt/decode.\n * Inlined here to avoid a direct dependency on @sd-jwt/types.\n */\ntype SdJwtHasher = (data: string | ArrayBuffer, alg: string) => Promise<Uint8Array>;\n\nconst DEFAULT_ALLOWED_ALGORITHMS = ['ES256', 'ES384', 'ES512'];\n\n/**\n * SHA-256 hasher implementation using the Web Crypto API.\n * Compatible with the @sd-jwt/decode Hasher signature.\n */\nconst sha256Hasher: SdJwtHasher = async (data: string | ArrayBuffer, _alg: string): Promise<Uint8Array> => {\n const encoder = new TextEncoder();\n const input = typeof data === 'string' ? encoder.encode(data) : data;\n const hashBuffer = await crypto.subtle.digest('SHA-256', input);\n return new Uint8Array(hashBuffer);\n};\n\n// Standard base64 alphabet for decoding x5c certificate entries\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/**\n * Decodes a standard base64-encoded string into raw bytes.\n * Does not depend on Node.js Buffer or browser atob.\n */\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const cleaned = base64.replace(/=+$/, '');\n const byteLength = Math.floor((cleaned.length * 3) / 4);\n const buffer = new ArrayBuffer(byteLength);\n const bytes = new Uint8Array(buffer);\n\n let byteIndex = 0;\n for (let i = 0; i < cleaned.length; i += 4) {\n const a = BASE64_CHARS.indexOf(cleaned[i]);\n const b = i + 1 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 1]) : 0;\n const c = i + 2 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 2]) : 0;\n const d = i + 3 < cleaned.length ? BASE64_CHARS.indexOf(cleaned[i + 3]) : 0;\n\n bytes[byteIndex++] = (a << 2) | (b >> 4);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((b & 0x0f) << 4) | (c >> 2);\n if (byteIndex < byteLength) bytes[byteIndex++] = ((c & 0x03) << 6) | d;\n }\n\n return bytes;\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Converts a base64-encoded DER certificate to PEM format for jose.importX509.\n */\nfunction derToPem(base64Der: string): string {\n const lines = base64Der.match(/.{1,64}/g) || [];\n return `-----BEGIN CERTIFICATE-----\\n${lines.join('\\n')}\\n-----END CERTIFICATE-----`;\n}\n\n/**\n * Encodes a Uint8Array to base64url (no padding).\n * Used by disclosure hash verification (Task 5) and KB-JWT verification (Task 6).\n */\nfunction bytesToBase64url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return globalThis.btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Maps raw decoded claims to the CredentialClaims structure,\n * picking known age/residency fields and preserving any extra keys.\n */\nfunction mapToCredentialClaims(raw: Record<string, unknown>): CredentialClaims {\n const claims: CredentialClaims = {};\n\n if (typeof raw['age_over_18'] === 'boolean') {\n claims.age_over_18 = raw['age_over_18'];\n }\n if (typeof raw['age_over_21'] === 'boolean') {\n claims.age_over_21 = raw['age_over_21'];\n }\n if (typeof raw['resident_country'] === 'string') {\n claims.resident_country = raw['resident_country'];\n }\n if (typeof raw['nationality'] === 'string') {\n claims.nationality = raw['nationality'];\n }\n if (typeof raw['family_name_birth'] === 'string') {\n claims.family_name_birth = raw['family_name_birth'];\n }\n\n for (const [key, value] of Object.entries(raw)) {\n if (\n !(key in claims) &&\n !key.startsWith('_sd') &&\n key !== 'iss' &&\n key !== 'exp' &&\n key !== 'iat' &&\n key !== 'cnf' &&\n key !== 'vct'\n ) {\n claims[key] = value;\n }\n }\n\n return claims;\n}\n\n/**\n * Best-effort extraction of a country code hint from an issuer URL or string.\n */\nfunction extractCountryHint(issuer: string): string {\n try {\n const url = new URL(issuer);\n const tld = url.hostname.split('.').pop()?.toUpperCase();\n if (tld && tld.length === 2) {\n return tld;\n }\n } catch {\n // Not a valid URL\n }\n return '';\n}\n\n/**\n * SD-JWT VC credential parser.\n *\n * Decodes and cryptographically validates SD-JWT Verifiable Credentials:\n * - JWT signature verification via x5c public key (jose)\n * - Issuer certificate trust chain verification\n * - Disclosure hash integrity verification\n * - Key binding JWT signature and sd_hash verification\n * - Selective disclosure claim extraction\n */\nexport class SdJwtParser implements ICredentialParser {\n readonly format: CredentialFormat = 'sd-jwt-vc';\n\n canParse(vpToken: unknown): boolean {\n return typeof vpToken === 'string' && vpToken.includes('~');\n }\n\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (typeof vpToken !== 'string') {\n throw new MalformedCredentialError('VP token must be a string');\n }\n\n const allowedAlgorithms = options.allowedAlgorithms ?? DEFAULT_ALLOWED_ALGORITHMS;\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Split the SD-JWT into components\n let parts: ReturnType<typeof splitSdJwt>;\n try {\n parts = splitSdJwt(vpToken);\n } catch {\n throw new MalformedCredentialError('Failed to split SD-JWT structure');\n }\n\n // Step 2: Peek at issuer JWT header (need alg and x5c before verification)\n let header: ReturnType<typeof decodeProtectedHeader>;\n try {\n header = decodeProtectedHeader(parts.jwt);\n } catch {\n throw new MalformedCredentialError('Failed to decode SD-JWT issuer JWT header');\n }\n\n // Step 3: Check algorithm against allowlist\n const alg = header.alg;\n if (typeof alg !== 'string' || !allowedAlgorithms.includes(alg)) {\n return invalidResult(`Unsupported algorithm: ${alg}`);\n }\n\n // Step 4: Extract public key from x5c certificate\n const x5cArray = header.x5c;\n if (!Array.isArray(x5cArray) || x5cArray.length === 0 || typeof x5cArray[0] !== 'string') {\n throw new MalformedCredentialError('Missing or invalid x5c in JWT header');\n }\n\n let issuerKey: Awaited<ReturnType<typeof importX509>>;\n let issuerCertBytes: Uint8Array;\n try {\n issuerKey = await importX509(derToPem(x5cArray[0]), alg);\n issuerCertBytes = base64ToBytes(x5cArray[0]);\n } catch (err) {\n if (err instanceof MalformedCredentialError) throw err;\n throw new MalformedCredentialError('Failed to import issuer certificate');\n }\n\n // Step 5: Verify issuer JWT signature (also validates exp, iat via jose)\n let payload: Record<string, unknown>;\n try {\n const result = await jwtVerify(parts.jwt, issuerKey, { algorithms: allowedAlgorithms });\n payload = result.payload as unknown as Record<string, unknown>;\n } catch {\n return invalidResult('Issuer JWT signature verification failed');\n }\n\n // Step 6: Trust check — certificate must be in the trusted set\n // unless the caller explicitly opts out via skipTrustCheck.\n const issuerString = typeof payload['iss'] === 'string' ? payload['iss'] : '';\n const issuerInfo: IssuerInfo = {\n certificate: issuerCertBytes,\n country: extractCountryHint(issuerString),\n };\n\n if (options.trustedCertificates.length === 0) {\n if (options.skipTrustCheck !== true) {\n throw new MalformedCredentialError(\n 'trustedCertificates must not be empty unless skipTrustCheck is true'\n );\n }\n } else {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n }\n\n // Step 7: Verify disclosure hashes against _sd array\n const sdArray = payload['_sd'];\n if (parts.disclosures.length > 0) {\n if (!Array.isArray(sdArray) || sdArray.length === 0) {\n return invalidResult('Disclosures present but no _sd array in issuer JWT');\n }\n for (const disclosure of parts.disclosures) {\n const hashBytes = await sha256Hasher(disclosure, 'sha-256');\n const hashB64url = bytesToBase64url(hashBytes);\n if (!sdArray.includes(hashB64url)) {\n return invalidResult('Disclosure hash mismatch');\n }\n }\n }\n\n // Step 8: Verify key binding JWT (if present)\n if (parts.kbJwt) {\n // Extract holder public key from cnf claim\n const cnf = payload['cnf'] as Record<string, unknown> | undefined;\n if (!cnf || typeof cnf !== 'object' || !cnf['jwk']) {\n throw new MalformedCredentialError('Missing cnf.jwk in issuer JWT for key binding');\n }\n\n let holderKey: Awaited<ReturnType<typeof importJWK>>;\n try {\n const kbHeader = decodeProtectedHeader(parts.kbJwt);\n const kbAlg = kbHeader.alg;\n if (typeof kbAlg !== 'string' || !allowedAlgorithms.includes(kbAlg)) {\n return invalidResult(`Unsupported KB-JWT algorithm: ${kbAlg}`);\n }\n holderKey = await importJWK(cnf['jwk'] as Record<string, unknown>, kbAlg);\n } catch (err) {\n if (err instanceof MalformedCredentialError) throw err;\n throw new MalformedCredentialError('Failed to import holder key from cnf.jwk');\n }\n\n // Verify KB-JWT signature\n let kbPayload: Record<string, unknown>;\n try {\n const kbResult = await jwtVerify(parts.kbJwt, holderKey, {\n algorithms: allowedAlgorithms,\n ...(options.audience ? { audience: options.audience } : {}),\n });\n kbPayload = kbResult.payload as unknown as Record<string, unknown>;\n } catch {\n return invalidResult('Key binding JWT signature verification failed');\n }\n\n // Validate nonce — must be present and match when KB-JWT is used\n if (kbPayload['nonce'] !== options.nonce) {\n return invalidResult('Key binding JWT nonce does not match');\n }\n\n // Verify sd_hash — hash of the SD-JWT content before the KB-JWT\n const disclosurePart = parts.disclosures.length > 0 ? parts.disclosures.join('~') + '~' : '';\n const sdJwtForHash = parts.jwt + '~' + disclosurePart;\n const sdHashBytes = await sha256Hasher(sdJwtForHash, 'sha-256');\n const expectedSdHash = bytesToBase64url(sdHashBytes);\n if (kbPayload['sd_hash'] !== expectedSdHash) {\n return invalidResult('Key binding JWT sd_hash does not match');\n }\n }\n\n // Step 9: Decode disclosures and extract claims\n let resolvedClaims: Record<string, unknown>;\n try {\n const decoded = await decodeSdJwt(vpToken, sha256Hasher);\n resolvedClaims = await getClaims<Record<string, unknown>>(\n decoded.jwt.payload,\n decoded.disclosures,\n sha256Hasher\n );\n } catch {\n throw new MalformedCredentialError('Failed to decode SD-JWT disclosures');\n }\n\n return {\n valid: true,\n format: this.format,\n claims: mapToCredentialClaims(resolvedClaims),\n issuer: issuerInfo,\n };\n }\n}\n","import { decode } from 'cbor-x';\n\nimport { MalformedCredentialError } from '../errors.js';\nimport type { IssuerInfo } from '../types/issuer.js';\nimport type { CredentialFormat, CredentialClaims, PresentationResult } from '../types/presentation.js';\n\nimport type { ICredentialParser, ParseOptions } from './parser.interface.js';\n\n/**\n * Runtime check for binary data. Returns true for Uint8Array and Node.js Buffer\n * without referencing the Buffer type (which is unavailable in DOM-only tsconfig).\n * Buffer extends Uint8Array, so `instanceof Uint8Array` catches both.\n */\nfunction isBinaryData(value: unknown): value is Uint8Array {\n return value instanceof Uint8Array;\n}\n\n/**\n * Checks whether two Uint8Array instances contain identical bytes.\n */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Represents a single IssuerSignedItem element from an mDOC nameSpace.\n * ISO 18013-5 Section 8.3.2.1.2.\n */\ninterface IssuerSignedItem {\n digestID: number;\n random: Uint8Array | unknown;\n elementIdentifier: string;\n elementValue: unknown;\n}\n\n/**\n * Simplified DeviceResponse structure from ISO 18013-5.\n * Only the fields required for claim extraction and trust validation.\n */\ninterface MdocDocument {\n docType: string;\n issuerSigned: {\n nameSpaces: Record<string, IssuerSignedItem[]>;\n issuerAuth: unknown[];\n };\n}\n\ninterface DeviceResponse {\n version: string;\n documents: MdocDocument[];\n status: number;\n}\n\n/**\n * Extracts the issuer certificate bytes from a COSE_Sign1 issuerAuth structure.\n *\n * The issuerAuth is a COSE_Sign1 array: [protected, unprotected, payload, signature].\n * The protected header (index 0) is CBOR-encoded and may contain the x5chain (label 33)\n * with the issuer certificate. The unprotected header (index 1) may also carry it.\n *\n * If neither header contains a certificate, the raw protected bytes are returned\n * as a fallback for trust matching (test/simplified scenarios).\n */\nfunction extractCertificateFromIssuerAuth(issuerAuth: unknown[]): Uint8Array {\n // Try unprotected header first (index 1) -- it's already decoded as a Map or object\n const unprotected = issuerAuth[1];\n if (unprotected && typeof unprotected === 'object') {\n // x5chain is COSE header label 33\n const certFromUnprotected = extractX5Chain(unprotected);\n if (certFromUnprotected) return certFromUnprotected;\n }\n\n // Try protected header (index 0) -- CBOR-encoded bytes\n const protectedHeader = issuerAuth[0];\n if (isBinaryData(protectedHeader)) {\n try {\n const decoded = decode(protectedHeader) as unknown;\n if (decoded && typeof decoded === 'object') {\n const certFromProtected = extractX5Chain(decoded);\n if (certFromProtected) return certFromProtected;\n }\n } catch {\n // Protected header could not be decoded -- fall through\n }\n // Return raw protected bytes as fallback identifier\n return new Uint8Array(protectedHeader);\n }\n\n return new Uint8Array();\n}\n\n/**\n * Extracts certificate bytes from an x5chain (label 33) in a COSE header.\n * The header can be a plain object or a Map.\n */\nfunction extractX5Chain(header: unknown): Uint8Array | null {\n if (header instanceof Map) {\n const cert = header.get(33) as unknown;\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n } else if (typeof header === 'object' && header !== null) {\n const rec = header as Record<string | number, unknown>;\n const cert = rec[33] ?? rec['x5chain'] ?? rec['33'];\n if (isBinaryData(cert)) {\n return new Uint8Array(cert);\n }\n }\n return null;\n}\n\n/**\n * Maps raw mDOC IssuerSignedItem elements to the CredentialClaims structure,\n * picking known age/residency fields and preserving any additional identifiers.\n */\nfunction mapToCredentialClaims(items: IssuerSignedItem[]): CredentialClaims {\n const claims: CredentialClaims = {};\n\n for (const item of items) {\n const key = item.elementIdentifier;\n const value = item.elementValue;\n\n switch (key) {\n case 'age_over_18':\n if (typeof value === 'boolean') claims.age_over_18 = value;\n break;\n case 'age_over_21':\n if (typeof value === 'boolean') claims.age_over_21 = value;\n break;\n case 'resident_country':\n if (typeof value === 'string') claims.resident_country = value;\n break;\n case 'nationality':\n if (typeof value === 'string') claims.nationality = value;\n break;\n case 'family_name_birth':\n if (typeof value === 'string') claims.family_name_birth = value;\n break;\n default:\n claims[key] = value;\n break;\n }\n }\n\n return claims;\n}\n\n/**\n * Extracts validity period from the issuerAuth payload.\n *\n * The payload (index 2) of the COSE_Sign1 is CBOR-encoded and should\n * contain a MobileSecurityObject with a validityInfo field carrying\n * `validFrom` and `validUntil` dates.\n */\nfunction extractValidityPeriod(issuerAuth: unknown[]): { validFrom?: Date; validUntil?: Date } {\n const payload = issuerAuth[2];\n if (!payload) return {};\n\n try {\n // payload can be raw CBOR bytes or already decoded\n const decoded = isBinaryData(payload) ? (decode(payload) as unknown) : payload;\n\n if (decoded && typeof decoded === 'object') {\n const obj = decoded as Record<string, unknown>;\n const validityInfo = obj['validityInfo'] as Record<string, unknown> | undefined;\n if (validityInfo) {\n return {\n validFrom:\n validityInfo['validFrom'] instanceof Date\n ? validityInfo['validFrom']\n : typeof validityInfo['validFrom'] === 'string'\n ? new Date(validityInfo['validFrom'])\n : undefined,\n validUntil:\n validityInfo['validUntil'] instanceof Date\n ? validityInfo['validUntil']\n : typeof validityInfo['validUntil'] === 'string'\n ? new Date(validityInfo['validUntil'])\n : undefined,\n };\n }\n }\n } catch {\n // Could not decode payload -- validity check will be skipped\n }\n\n return {};\n}\n\n/**\n * mDOC / mDL credential parser.\n *\n * Decodes and validates mDOC (Mobile Document) credentials as specified in\n * ISO 18013-5 and the EUDI Wallet ecosystem. Processes CBOR-encoded\n * DeviceResponse structures containing:\n * - Document type validation (eu.europa.ec.eudi.pid.1)\n * - Issuer certificate trust chain verification via COSE_Sign1 issuerAuth\n * - Validity period checking\n * - Claim extraction from IssuerSignedItem nameSpace elements\n */\nexport class MdocParser implements ICredentialParser {\n readonly format: CredentialFormat = 'mdoc';\n\n /**\n * Returns true when the token is binary data (Uint8Array or Buffer),\n * indicating a CBOR-encoded mDOC DeviceResponse.\n */\n canParse(vpToken: unknown): boolean {\n return isBinaryData(vpToken);\n }\n\n /**\n * Parses and validates an mDOC DeviceResponse token.\n *\n * @throws {MalformedCredentialError} when the CBOR structure cannot be decoded\n * or does not match the expected DeviceResponse format\n */\n async parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n if (!isBinaryData(vpToken)) {\n throw new MalformedCredentialError('VP token must be a Uint8Array or Buffer');\n }\n\n const invalidResult = (error: string): PresentationResult => ({\n valid: false,\n format: this.format,\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error,\n });\n\n // Step 1: Decode the CBOR DeviceResponse\n let deviceResponse: DeviceResponse;\n try {\n deviceResponse = decode(vpToken) as DeviceResponse;\n } catch {\n throw new MalformedCredentialError('Failed to decode CBOR DeviceResponse');\n }\n\n // Step 2: Validate basic DeviceResponse structure\n if (\n !deviceResponse ||\n typeof deviceResponse !== 'object' ||\n !Array.isArray(deviceResponse.documents) ||\n deviceResponse.documents.length === 0\n ) {\n throw new MalformedCredentialError('Invalid DeviceResponse structure: missing or empty documents array');\n }\n\n const document = deviceResponse.documents[0];\n if (!document.issuerSigned || !document.issuerSigned.nameSpaces || !document.issuerSigned.issuerAuth) {\n throw new MalformedCredentialError('Invalid mDOC document: missing issuerSigned data');\n }\n\n // Step 3: Extract issuer certificate from COSE_Sign1 issuerAuth\n const issuerAuth = document.issuerSigned.issuerAuth;\n if (!Array.isArray(issuerAuth) || issuerAuth.length < 4) {\n throw new MalformedCredentialError('Invalid issuerAuth: expected COSE_Sign1 array with 4 elements');\n }\n\n const issuerCertBytes = extractCertificateFromIssuerAuth(issuerAuth);\n const issuerInfo: IssuerInfo = {\n certificate: issuerCertBytes,\n country: '',\n };\n\n // Step 4: Verify trust -- certificate must be in the trusted set\n // unless the caller explicitly opts out via skipTrustCheck.\n if (options.trustedCertificates.length === 0) {\n if (options.skipTrustCheck !== true) {\n throw new MalformedCredentialError(\n 'trustedCertificates must not be empty unless skipTrustCheck is true'\n );\n }\n } else {\n const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual(trusted, issuerCertBytes));\n if (!isTrusted) {\n return invalidResult('Issuer certificate is not trusted');\n }\n }\n\n // Step 5: Check validity period\n const { validUntil } = extractValidityPeriod(issuerAuth);\n if (validUntil) {\n const now = new Date();\n if (validUntil < now) {\n return invalidResult('Credential has expired');\n }\n }\n\n // Step 6: Extract claims from nameSpaces\n // Prefer the EUDI PID namespace, fall back to the first available namespace\n const pidNamespace = 'eu.europa.ec.eudi.pid.1';\n const nameSpaces = document.issuerSigned.nameSpaces;\n const items: IssuerSignedItem[] =\n nameSpaces[pidNamespace] ?? nameSpaces[document.docType] ?? Object.values(nameSpaces)[0] ?? [];\n\n const claims = mapToCredentialClaims(items);\n\n // Try to derive country from resident_country claim\n if (claims.resident_country && typeof claims.resident_country === 'string') {\n issuerInfo.country = claims.resident_country;\n }\n\n return {\n valid: true,\n format: this.format,\n claims,\n issuer: issuerInfo,\n };\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\n\nimport type { AuthorizationRequestInput, AuthorizationRequest } from './types/authorization.js';\n\nexport function createAuthorizationRequest(input: AuthorizationRequestInput): AuthorizationRequest {\n const state = input.state ?? uuidv4();\n\n const presentationDefinition = {\n id: uuidv4(),\n input_descriptors: input.requestedAttributes.map((attr, index) => ({\n id: `descriptor_${index}`,\n format: Object.fromEntries(\n input.acceptedFormats.map((fmt) => {\n if (fmt === 'sd-jwt-vc') return ['vc+sd-jwt', { 'sd-jwt_alg_values': ['ES256'] }];\n return ['mso_mdoc', { alg: ['ES256'] }];\n })\n ),\n constraints: {\n fields: [\n {\n path: [`$.${attr}`, `$['${attr}']`],\n filter: { type: attr.startsWith('age_over') ? 'boolean' : 'string' },\n },\n ],\n },\n })),\n };\n\n const params = new URLSearchParams({\n response_type: 'vp_token',\n response_mode: 'direct_post',\n response_uri: input.responseUri,\n client_id: input.clientId,\n nonce: input.nonce,\n state,\n presentation_definition: JSON.stringify(presentationDefinition),\n });\n\n return {\n uri: `openid4vp://authorize?${params.toString()}`,\n presentationDefinition,\n nonce: input.nonce,\n state,\n };\n}\n","import { MdocParser } from './parsers/mdoc.parser.js';\nimport type { ParseOptions, ICredentialParser } from './parsers/parser.interface.js';\nimport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nimport type { PresentationResult } from './types/presentation.js';\n\nconst parsers: ICredentialParser[] = [new SdJwtParser(), new MdocParser()];\n\nexport async function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult> {\n for (const parser of parsers) {\n if (parser.canParse(vpToken)) {\n return parser.parse(vpToken, options);\n }\n }\n\n return {\n valid: false,\n format: 'sd-jwt-vc',\n claims: {},\n issuer: { certificate: new Uint8Array(), country: '' },\n error: 'Unsupported credential format',\n };\n}\n\nexport type { ParseOptions } from './parsers/parser.interface.js';\n","export const VERSION = '0.2.1';\n\nexport type {\n CredentialFormat,\n CredentialClaims,\n PresentationResult,\n IssuerInfo,\n AuthorizationRequestInput,\n AuthorizationRequest,\n} from './types/index.js';\n\nexport type { ICredentialParser, ParseOptions } from './parsers/parser.interface.js';\n\nexport { SdJwtParser } from './parsers/sd-jwt.parser.js';\nexport { MdocParser } from './parsers/mdoc.parser.js';\n\nexport {\n InvalidSignatureError,\n ExpiredCredentialError,\n UnsupportedFormatError,\n MalformedCredentialError,\n NonceValidationError,\n} from './errors.js';\n\nexport { createAuthorizationRequest } from './authorization.js';\n\nexport { parsePresentation } from './presentation.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openeudi/openid4vp",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "OpenID4VP credential parsing and validation for EUDI Wallets (SD-JWT VC + mDOC)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -32,18 +32,24 @@
32
32
  "type-check": "tsc --noEmit"
33
33
  },
34
34
  "dependencies": {
35
+ "@sd-jwt/decode": "^0.19.0",
35
36
  "@sphereon/pex": "5.0.0-unstable.28",
36
37
  "@sphereon/ssi-types": "^0.37.1",
37
- "@sd-jwt/decode": "^0.19.0",
38
38
  "cbor-x": "^1.6.0",
39
- "cose-js": "^0.9.0",
39
+ "jose": "^6.1.3",
40
40
  "uuid": "^11.1.0"
41
41
  },
42
42
  "devDependencies": {
43
- "typescript": "^5.8.3",
43
+ "@peculiar/x509": "^2.0.0",
44
+ "reflect-metadata": "^0.2.2",
44
45
  "tsup": "^8.5.1",
46
+ "typescript": "^5.8.3",
45
47
  "vitest": "^4.0.18"
46
48
  },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/openeudi/openid4vp"
52
+ },
47
53
  "keywords": [
48
54
  "openid4vp",
49
55
  "eudi",
@@ -51,19 +57,5 @@
51
57
  "mdoc",
52
58
  "verifiable-credentials",
53
59
  "eidas"
54
- ],
55
- "publishConfig": {
56
- "access": "public",
57
- "provenance": true,
58
- "registry": "https://registry.npmjs.org"
59
- },
60
- "repository": {
61
- "type": "git",
62
- "url": "https://github.com/openeudi/openid4vp"
63
- },
64
- "homepage": "https://github.com/openeudi/openid4vp#readme",
65
- "bugs": {
66
- "url": "https://github.com/openeudi/openid4vp/issues"
67
- },
68
- "author": "OpenEUDI Contributors"
60
+ ]
69
61
  }