@openeudi/openid4vp 0.8.1 → 0.9.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/dist/index.cjs CHANGED
@@ -40,7 +40,7 @@ var __export = (target, all) => {
40
40
  };
41
41
 
42
42
  // src/errors.ts
43
- exports.InvalidSignatureError = void 0; exports.ExpiredCredentialError = void 0; exports.UnsupportedFormatError = void 0; exports.MalformedCredentialError = void 0; exports.NonceValidationError = void 0; exports.HaipValidationError = void 0; exports.OpenID4VPError = void 0; exports.TrustAnchorNotFoundError = void 0; exports.CertificateChainError = void 0; exports.RevokedCertificateError = void 0; exports.RevocationCheckFailedError = void 0; exports.LotlFetchError = void 0; exports.LotlSignatureError = void 0; exports.SignedRequestBuildError = void 0; exports.UnsupportedJweError = void 0; exports.DecryptionFailedError = void 0; exports.MissingDecryptionKeyError = void 0; exports.MultipleCredentialsNotSupportedError = void 0;
43
+ exports.InvalidSignatureError = void 0; exports.ExpiredCredentialError = void 0; exports.UnsupportedFormatError = void 0; exports.MalformedCredentialError = void 0; exports.NonceValidationError = void 0; exports.HaipValidationError = void 0; exports.OpenID4VPError = void 0; exports.TrustAnchorNotFoundError = void 0; exports.CertificateChainError = void 0; exports.RevokedCertificateError = void 0; exports.RevocationCheckFailedError = void 0; exports.LotlFetchError = void 0; exports.LotlSignatureError = void 0; exports.SignedRequestBuildError = void 0; exports.UnsupportedJweError = void 0; exports.DecryptionFailedError = void 0; exports.MissingDecryptionKeyError = void 0; exports.MissingVerifierEncryptionKeyError = void 0; exports.MultipleCredentialsNotSupportedError = void 0;
44
44
  var init_errors = __esm({
45
45
  "src/errors.ts"() {
46
46
  exports.InvalidSignatureError = class extends Error {
@@ -147,10 +147,10 @@ var init_errors = __esm({
147
147
  code = "unsupported_jwe";
148
148
  alg;
149
149
  enc;
150
- constructor(alg, enc) {
151
- super(`Unsupported JWE algorithms: alg=${alg}, enc=${enc}`);
150
+ constructor(alg, enc3) {
151
+ super(`Unsupported JWE algorithms: alg=${alg}, enc=${enc3}`);
152
152
  this.alg = alg;
153
- this.enc = enc;
153
+ this.enc = enc3;
154
154
  }
155
155
  };
156
156
  exports.DecryptionFailedError = class extends exports.OpenID4VPError {
@@ -165,6 +165,12 @@ var init_errors = __esm({
165
165
  super(message);
166
166
  }
167
167
  };
168
+ exports.MissingVerifierEncryptionKeyError = class extends exports.OpenID4VPError {
169
+ code = "missing_verifier_encryption_key";
170
+ constructor(message = "verifierEncryptionJwk is required to build the OID4VP 1.0 OpenID4VPHandover SessionTranscript for an encrypted response") {
171
+ super(message);
172
+ }
173
+ };
168
174
  exports.MultipleCredentialsNotSupportedError = class extends exports.OpenID4VPError {
169
175
  code = "multi_credential_unsupported";
170
176
  entryCount;
@@ -334,9 +340,9 @@ var init_index_es = __esm({
334
340
  static isBase64Url(data) {
335
341
  return typeof data === STRING_TYPE && BASE64URL_REGEX.test(data);
336
342
  }
337
- static ToString(buffer, enc = "utf8") {
343
+ static ToString(buffer, enc3 = "utf8") {
338
344
  const buf = BufferSourceConverter.toUint8Array(buffer);
339
- switch (enc.toLowerCase()) {
345
+ switch (enc3.toLowerCase()) {
340
346
  case "utf8":
341
347
  return this.ToUtf8String(buf);
342
348
  case "binary":
@@ -353,14 +359,14 @@ var init_index_es = __esm({
353
359
  case "utf16be":
354
360
  return Utf16Converter.toString(buf);
355
361
  default:
356
- throw new Error(`Unknown type of encoding '${enc}'`);
362
+ throw new Error(`Unknown type of encoding '${enc3}'`);
357
363
  }
358
364
  }
359
- static FromString(str, enc = "utf8") {
365
+ static FromString(str, enc3 = "utf8") {
360
366
  if (!str) {
361
367
  return new ArrayBuffer(0);
362
368
  }
363
- switch (enc.toLowerCase()) {
369
+ switch (enc3.toLowerCase()) {
364
370
  case "utf8":
365
371
  return this.FromUtf8String(str);
366
372
  case "binary":
@@ -377,7 +383,7 @@ var init_index_es = __esm({
377
383
  case "utf16be":
378
384
  return Utf16Converter.fromString(str);
379
385
  default:
380
- throw new Error(`Unknown type of encoding '${enc}'`);
386
+ throw new Error(`Unknown type of encoding '${enc3}'`);
381
387
  }
382
388
  }
383
389
  static ToBase64(buffer) {
@@ -8191,8 +8197,8 @@ async function createSignedAuthorizationRequest(input, query) {
8191
8197
  vp_formats_supported: input.vpFormatsSupported
8192
8198
  };
8193
8199
  if (responseMode === "direct_post.jwt" && input.encryptionKey) {
8194
- const enc = input.encryptionKey.supportedEncValues;
8195
- if (enc !== void 0 && enc.length === 0) {
8200
+ const enc3 = input.encryptionKey.supportedEncValues;
8201
+ if (enc3 !== void 0 && enc3.length === 0) {
8196
8202
  throw new exports.SignedRequestBuildError(
8197
8203
  "empty_supported_enc_values",
8198
8204
  "encryptionKey.supportedEncValues must not be empty (omit to use defaults [A128GCM, A256GCM])"
@@ -8200,7 +8206,7 @@ async function createSignedAuthorizationRequest(input, query) {
8200
8206
  }
8201
8207
  const jwk = { ...input.encryptionKey.publicJwk, use: "enc" };
8202
8208
  clientMetadata.jwks = { keys: [jwk] };
8203
- const encValues = enc ?? [...DEFAULT_SUPPORTED_ENC_VALUES];
8209
+ const encValues = enc3 ?? [...DEFAULT_SUPPORTED_ENC_VALUES];
8204
8210
  clientMetadata.encrypted_response_enc_values_supported = encValues;
8205
8211
  clientMetadata.authorization_encrypted_response_alg = "ECDH-ES";
8206
8212
  clientMetadata.authorization_encrypted_response_enc = encValues[0];
@@ -8474,6 +8480,131 @@ async function verifyCoseSign1(coseSign1, publicKey, allowedAlgorithms) {
8474
8480
  }
8475
8481
  }
8476
8482
 
8483
+ // src/crypto/device-auth.ts
8484
+ init_errors();
8485
+ var enc = new cborX.Encoder({ mapsAsObjects: false, useRecords: false, tagUint8Array: false });
8486
+ var HEADER_ALG2 = 1;
8487
+ var COSE_ALG_TO_NAME2 = {
8488
+ [-7]: "ES256",
8489
+ [-35]: "ES384",
8490
+ [-36]: "ES512"
8491
+ };
8492
+ var ALG_TO_HASH2 = { ES256: "SHA-256", ES384: "SHA-384", ES512: "SHA-512" };
8493
+ var COSE_KEY_KTY = 1;
8494
+ var COSE_KEY_CRV = -1;
8495
+ var COSE_KEY_X = -2;
8496
+ var COSE_KEY_Y = -3;
8497
+ var COSE_KTY_EC2 = 2;
8498
+ var COSE_CRV_TO_JWK = { 1: "P-256", 2: "P-384", 3: "P-521" };
8499
+ function concatBytes(...chunks) {
8500
+ let len = 0;
8501
+ for (const c of chunks) len += c.length;
8502
+ const out = new Uint8Array(len);
8503
+ let off = 0;
8504
+ for (const c of chunks) {
8505
+ out.set(c, off);
8506
+ off += c.length;
8507
+ }
8508
+ return out;
8509
+ }
8510
+ function bytesToBase64Url(bytes) {
8511
+ let bin = "";
8512
+ for (const b of bytes) bin += String.fromCharCode(b);
8513
+ return globalThis.btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
8514
+ }
8515
+ function encodeTag24(inner) {
8516
+ return concatBytes(new Uint8Array([216, 24]), new Uint8Array(enc.encode(inner)));
8517
+ }
8518
+ function buildDeviceAuthenticationBytes(params) {
8519
+ const deviceAuthentication = concatBytes(
8520
+ new Uint8Array([132]),
8521
+ // array(4)
8522
+ new Uint8Array(enc.encode("DeviceAuthentication")),
8523
+ params.sessionTranscript,
8524
+ new Uint8Array(enc.encode(params.docType)),
8525
+ encodeTag24(params.deviceNameSpacesInner)
8526
+ );
8527
+ return encodeTag24(deviceAuthentication);
8528
+ }
8529
+ function encodeDeviceSigStructure(protectedBytes, deviceAuthBytes) {
8530
+ const sig = ["Signature1", protectedBytes, new Uint8Array(0), deviceAuthBytes];
8531
+ return new Uint8Array(enc.encode(sig));
8532
+ }
8533
+ async function importDeviceKeyFromCose(coseKey) {
8534
+ if (!(coseKey instanceof Map)) {
8535
+ throw new exports.MalformedCredentialError("MSO deviceKeyInfo.deviceKey is not a COSE_Key map");
8536
+ }
8537
+ const kty = coseKey.get(COSE_KEY_KTY);
8538
+ if (kty !== COSE_KTY_EC2) {
8539
+ throw new exports.MalformedCredentialError(`Unsupported COSE key type for device key: ${String(kty)}`);
8540
+ }
8541
+ const crv = coseKey.get(COSE_KEY_CRV);
8542
+ const crvName = typeof crv === "number" ? COSE_CRV_TO_JWK[crv] : void 0;
8543
+ const x = coseKey.get(COSE_KEY_X);
8544
+ const y = coseKey.get(COSE_KEY_Y);
8545
+ if (!crvName || !(x instanceof Uint8Array) || !(y instanceof Uint8Array)) {
8546
+ throw new exports.MalformedCredentialError("Malformed EC2 COSE_Key in MSO deviceKeyInfo");
8547
+ }
8548
+ return crypto.subtle.importKey(
8549
+ "jwk",
8550
+ { kty: "EC", crv: crvName, x: bytesToBase64Url(x), y: bytesToBase64Url(y) },
8551
+ { name: "ECDSA", namedCurve: crvName },
8552
+ false,
8553
+ ["verify"]
8554
+ );
8555
+ }
8556
+ async function verifyDeviceAuth(params) {
8557
+ const { deviceSigned, deviceKeyCose, docType, sessionTranscript, allowedAlgorithms } = params;
8558
+ const deviceAuth = deviceSigned.get("deviceAuth");
8559
+ if (!(deviceAuth instanceof Map)) {
8560
+ throw new exports.MalformedCredentialError("deviceSigned.deviceAuth is missing or malformed");
8561
+ }
8562
+ if (deviceAuth.has("deviceMac") && !deviceAuth.has("deviceSignature")) {
8563
+ throw new exports.MalformedCredentialError("mDOC DeviceMac authentication is not supported");
8564
+ }
8565
+ const deviceSignature = deviceAuth.get("deviceSignature");
8566
+ if (!Array.isArray(deviceSignature) || deviceSignature.length !== 4) {
8567
+ throw new exports.MalformedCredentialError("deviceAuth.deviceSignature must be a COSE_Sign1 array");
8568
+ }
8569
+ const protectedBytes = deviceSignature[0];
8570
+ const signature = deviceSignature[3];
8571
+ if (!(protectedBytes instanceof Uint8Array) || !(signature instanceof Uint8Array)) {
8572
+ throw new exports.MalformedCredentialError("DeviceSignature protected header / signature must be byte strings");
8573
+ }
8574
+ let alg;
8575
+ try {
8576
+ const ph = enc.decode(protectedBytes);
8577
+ const label = ph instanceof Map ? ph.get(HEADER_ALG2) : void 0;
8578
+ alg = typeof label === "number" ? COSE_ALG_TO_NAME2[label] : void 0;
8579
+ } catch {
8580
+ throw new exports.MalformedCredentialError("DeviceSignature protected header is not decodable CBOR");
8581
+ }
8582
+ if (!alg || !allowedAlgorithms.includes(alg)) {
8583
+ throw new exports.MalformedCredentialError(`DeviceSignature uses a disallowed algorithm: ${String(alg)}`);
8584
+ }
8585
+ const deviceNameSpacesInner = extractTag24Inner(deviceSigned.get("nameSpaces"));
8586
+ const deviceAuthBytes = buildDeviceAuthenticationBytes({ sessionTranscript, docType, deviceNameSpacesInner });
8587
+ const sigInput = encodeDeviceSigStructure(protectedBytes, deviceAuthBytes);
8588
+ const deviceKey = await importDeviceKeyFromCose(deviceKeyCose);
8589
+ const ok = await crypto.subtle.verify(
8590
+ { name: "ECDSA", hash: ALG_TO_HASH2[alg] },
8591
+ deviceKey,
8592
+ new Uint8Array(signature),
8593
+ sigInput
8594
+ );
8595
+ if (!ok) {
8596
+ throw new exports.MalformedCredentialError("mDOC device authentication (DeviceSignature) failed to verify");
8597
+ }
8598
+ }
8599
+ function extractTag24Inner(value) {
8600
+ if (value instanceof Uint8Array) return value;
8601
+ if (value !== null && typeof value === "object" && "value" in value && value.value instanceof Uint8Array && // Tag(24,bytes) and EmbeddedCbor both expose .value; both are fine here.
8602
+ ("tag" in value ? value.tag === 24 : true)) {
8603
+ return value.value;
8604
+ }
8605
+ throw new exports.MalformedCredentialError("deviceSigned.nameSpaces must be tag-24 wrapped CBOR");
8606
+ }
8607
+
8477
8608
  // src/crypto/digest.ts
8478
8609
  init_errors();
8479
8610
  var decoder2 = new cborX.Encoder({ mapsAsObjects: false, useRecords: false });
@@ -8606,6 +8737,14 @@ function decodeMso(payload) {
8606
8737
  }
8607
8738
  valueDigests.set(ns, perNs);
8608
8739
  }
8740
+ let deviceKey;
8741
+ const dkiRaw = m.get("deviceKeyInfo");
8742
+ if (dkiRaw instanceof Map) {
8743
+ const dk = dkiRaw.get("deviceKey");
8744
+ if (dk instanceof Map) {
8745
+ deviceKey = dk;
8746
+ }
8747
+ }
8609
8748
  const viRaw = m.get("validityInfo");
8610
8749
  if (!(viRaw instanceof Map)) {
8611
8750
  throw new exports.MalformedCredentialError("MSO.validityInfo must be a Map");
@@ -8619,7 +8758,7 @@ function decodeMso(payload) {
8619
8758
  if (vi.has("expectedUpdate")) {
8620
8759
  validityInfo.expectedUpdate = decodeDateLike(vi.get("expectedUpdate"));
8621
8760
  }
8622
- return { version, digestAlgorithm, valueDigests, docType, validityInfo };
8761
+ return { version, digestAlgorithm, valueDigests, docType, deviceKey, validityInfo };
8623
8762
  }
8624
8763
  function validateMsoValidity(mso, now) {
8625
8764
  const { signed, validFrom, validUntil } = mso.validityInfo;
@@ -8669,15 +8808,54 @@ ${lines.join("\n")}
8669
8808
  function extractCountryHintFromCert(_certDer) {
8670
8809
  return "";
8671
8810
  }
8811
+ function cborByteStringHeader(length) {
8812
+ if (length < 24) return Uint8Array.of(64 | length);
8813
+ if (length < 256) return Uint8Array.of(88, length);
8814
+ if (length < 65536) return Uint8Array.of(89, length >> 8 & 255, length & 255);
8815
+ if (length < 4294967296) {
8816
+ return Uint8Array.of(
8817
+ 90,
8818
+ length >>> 24 & 255,
8819
+ length >>> 16 & 255,
8820
+ length >>> 8 & 255,
8821
+ length & 255
8822
+ );
8823
+ }
8824
+ const hi = Math.floor(length / 4294967296);
8825
+ const lo = length >>> 0;
8826
+ return Uint8Array.of(
8827
+ 91,
8828
+ hi >>> 24 & 255,
8829
+ hi >>> 16 & 255,
8830
+ hi >>> 8 & 255,
8831
+ hi & 255,
8832
+ lo >>> 24 & 255,
8833
+ lo >>> 16 & 255,
8834
+ lo >>> 8 & 255,
8835
+ lo & 255
8836
+ );
8837
+ }
8838
+ function wrapIssuerSignedItemBytes(inner) {
8839
+ const header = cborByteStringHeader(inner.length);
8840
+ const out = new Uint8Array(2 + header.length + inner.length);
8841
+ out[0] = 216;
8842
+ out[1] = 24;
8843
+ out.set(header, 2);
8844
+ out.set(inner, 2 + header.length);
8845
+ return out;
8846
+ }
8672
8847
  function extractRawItemBytes(item) {
8673
8848
  if (item instanceof cborX.Tag && item.tag === 24 && item.value instanceof Uint8Array) {
8674
- return item.value;
8849
+ return wrapIssuerSignedItemBytes(item.value);
8675
8850
  }
8676
8851
  if (item instanceof Uint8Array) {
8677
- return item;
8852
+ if (item.length >= 2 && item[0] === 216 && item[1] === 24) {
8853
+ return item;
8854
+ }
8855
+ return wrapIssuerSignedItemBytes(item);
8678
8856
  }
8679
8857
  if (item !== null && typeof item === "object" && !(item instanceof cborX.Tag) && !(item instanceof Map) && "value" in item && item.value instanceof Uint8Array) {
8680
- return item.value;
8858
+ return wrapIssuerSignedItemBytes(item.value);
8681
8859
  }
8682
8860
  throw new exports.MalformedCredentialError("IssuerSignedItem must be a tag-24 byte string");
8683
8861
  }
@@ -8867,6 +9045,54 @@ var MdocParser = class {
8867
9045
  nameSpaces.set(ns, items.map(extractRawItemBytes));
8868
9046
  }
8869
9047
  await verifyAllDigests(nameSpaces, mso);
9048
+ const deviceSigned = document.get("deviceSigned");
9049
+ if (!(deviceSigned instanceof Map)) {
9050
+ return {
9051
+ valid: false,
9052
+ format: this.format,
9053
+ claims: {},
9054
+ issuer: { certificate: issuerCertBytes, country: "" },
9055
+ error: "Missing deviceSigned \u2014 mDOC device authentication is required"
9056
+ };
9057
+ }
9058
+ if (!(options.mdocSessionTranscript instanceof Uint8Array) || options.mdocSessionTranscript.length === 0) {
9059
+ return {
9060
+ valid: false,
9061
+ format: this.format,
9062
+ claims: {},
9063
+ issuer: { certificate: issuerCertBytes, country: "" },
9064
+ error: "mdocSessionTranscript is required to verify mDOC device authentication"
9065
+ };
9066
+ }
9067
+ if (!mso.deviceKey) {
9068
+ return {
9069
+ valid: false,
9070
+ format: this.format,
9071
+ claims: {},
9072
+ issuer: { certificate: issuerCertBytes, country: "" },
9073
+ error: "MSO is missing deviceKeyInfo.deviceKey; cannot verify device authentication"
9074
+ };
9075
+ }
9076
+ try {
9077
+ await verifyDeviceAuth({
9078
+ deviceSigned,
9079
+ deviceKeyCose: mso.deviceKey,
9080
+ docType: mso.docType,
9081
+ sessionTranscript: options.mdocSessionTranscript,
9082
+ allowedAlgorithms
9083
+ });
9084
+ } catch (err) {
9085
+ if (err instanceof exports.MalformedCredentialError) {
9086
+ return {
9087
+ valid: false,
9088
+ format: this.format,
9089
+ claims: {},
9090
+ issuer: { certificate: issuerCertBytes, country: "" },
9091
+ error: err.message
9092
+ };
9093
+ }
9094
+ throw err;
9095
+ }
8870
9096
  const { flat: claims, namespaced: namespacedClaims } = mapRawToClaims(nameSpaces);
8871
9097
  const issuer = {
8872
9098
  certificate: issuerCertBytes,
@@ -9139,11 +9365,21 @@ var SdJwtParser = class {
9139
9365
  return invalidResult("Disclosure hash mismatch");
9140
9366
  }
9141
9367
  }
9368
+ const cnf = payload["cnf"];
9369
+ const isHolderBound = !!cnf && typeof cnf === "object" && !!cnf["jwk"];
9370
+ const keyBindingRequired = isHolderBound || options.requireKeyBinding === true;
9371
+ if (keyBindingRequired && !parts.kbJwt) {
9372
+ return invalidResult(
9373
+ "Key binding JWT is required for holder-bound credentials but was not presented"
9374
+ );
9375
+ }
9142
9376
  if (parts.kbJwt) {
9143
- const cnf = payload["cnf"];
9144
- if (!cnf || typeof cnf !== "object" || !cnf["jwk"]) {
9377
+ if (!isHolderBound) {
9145
9378
  throw new exports.MalformedCredentialError("Missing cnf.jwk in issuer JWT for key binding");
9146
9379
  }
9380
+ if (typeof options.nonce !== "string" || options.nonce.length === 0) {
9381
+ return invalidResult("A nonce is required to verify the key binding JWT");
9382
+ }
9147
9383
  let holderKey;
9148
9384
  try {
9149
9385
  const kbHeader = jose.decodeProtectedHeader(parts.kbJwt);
@@ -9261,9 +9497,9 @@ async function decryptAuthorizationResponse(jwe, privateKey) {
9261
9497
  });
9262
9498
  }
9263
9499
  const alg = typeof header.alg === "string" ? header.alg : "";
9264
- const enc = typeof header.enc === "string" ? header.enc : "";
9265
- if (alg !== SUPPORTED_ALG || !SUPPORTED_ENC.has(enc)) {
9266
- throw new exports.UnsupportedJweError(alg, enc);
9500
+ const enc3 = typeof header.enc === "string" ? header.enc : "";
9501
+ if (alg !== SUPPORTED_ALG || !SUPPORTED_ENC.has(enc3)) {
9502
+ throw new exports.UnsupportedJweError(alg, enc3);
9267
9503
  }
9268
9504
  let plaintext;
9269
9505
  try {
@@ -9281,6 +9517,32 @@ async function decryptAuthorizationResponse(jwe, privateKey) {
9281
9517
  });
9282
9518
  }
9283
9519
  }
9520
+ var enc2 = new cborX.Encoder({ mapsAsObjects: false, useRecords: false, tagUint8Array: false });
9521
+ async function sha256(bytes) {
9522
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
9523
+ }
9524
+ async function buildOid4vpSessionTranscript(params) {
9525
+ const { clientId, responseUri, nonce, mdocGeneratedNonce } = params;
9526
+ const clientIdHash = await sha256(new Uint8Array(enc2.encode([clientId, mdocGeneratedNonce])));
9527
+ const responseUriHash = await sha256(new Uint8Array(enc2.encode([responseUri, mdocGeneratedNonce])));
9528
+ return new Uint8Array(enc2.encode([null, null, [clientIdHash, responseUriHash, nonce]]));
9529
+ }
9530
+ async function jwkSha256Thumbprint(jwk) {
9531
+ if (jwk.kty !== "EC") {
9532
+ throw new Error(`Unsupported JWK kty for thumbprint: ${jwk.kty}`);
9533
+ }
9534
+ if (!jwk.crv || !jwk.x || !jwk.y) {
9535
+ throw new Error("EC JWK for thumbprint is missing a required member (crv, x, or y)");
9536
+ }
9537
+ const canonical = `{"crv":"${jwk.crv}","kty":"EC","x":"${jwk.x}","y":"${jwk.y}"}`;
9538
+ return sha256(new TextEncoder().encode(canonical));
9539
+ }
9540
+ async function buildOpenID4VPHandoverSessionTranscript(params) {
9541
+ const { clientId, nonce, responseUri, verifierEncryptionJwk } = params;
9542
+ const thumb = verifierEncryptionJwk ? await jwkSha256Thumbprint(verifierEncryptionJwk) : null;
9543
+ const infoHash = await sha256(new Uint8Array(enc2.encode([clientId, nonce, thumb, responseUri])));
9544
+ return new Uint8Array(enc2.encode([null, null, ["OpenID4VPHandover", infoHash]]));
9545
+ }
9284
9546
 
9285
9547
  // src/verify.ts
9286
9548
  init_errors();
@@ -9318,6 +9580,42 @@ async function verifyPresentation(vpToken, query, options) {
9318
9580
  function isEncryptedResponse(x) {
9319
9581
  return typeof x === "object" && x !== null && "response" in x && typeof x.response === "string" && Object.keys(x).length === 1;
9320
9582
  }
9583
+ var BASE64URL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
9584
+ function base64UrlToBytes(input) {
9585
+ const cleaned = input.replace(/=+$/, "");
9586
+ const byteLength = Math.floor(cleaned.length * 3 / 4);
9587
+ const bytes = new Uint8Array(byteLength);
9588
+ let byteIndex = 0;
9589
+ for (let i = 0; i < cleaned.length; i += 4) {
9590
+ const a = BASE64URL_CHARS.indexOf(cleaned[i]);
9591
+ const b = i + 1 < cleaned.length ? BASE64URL_CHARS.indexOf(cleaned[i + 1]) : 0;
9592
+ const c = i + 2 < cleaned.length ? BASE64URL_CHARS.indexOf(cleaned[i + 2]) : 0;
9593
+ const d = i + 3 < cleaned.length ? BASE64URL_CHARS.indexOf(cleaned[i + 3]) : 0;
9594
+ if (a === -1 || b === -1 || c === -1 || d === -1) {
9595
+ throw new TypeError("Invalid base64url character in vp_token/apu");
9596
+ }
9597
+ bytes[byteIndex++] = a << 2 | b >> 4;
9598
+ if (byteIndex < byteLength) bytes[byteIndex++] = (b & 15) << 4 | c >> 2;
9599
+ if (byteIndex < byteLength) bytes[byteIndex++] = (c & 3) << 6 | d;
9600
+ }
9601
+ return bytes;
9602
+ }
9603
+ function extractMdocGeneratedNonce(jwe) {
9604
+ let apu;
9605
+ try {
9606
+ apu = jose.decodeProtectedHeader(jwe).apu;
9607
+ } catch {
9608
+ return void 0;
9609
+ }
9610
+ if (typeof apu !== "string" || apu.length === 0) {
9611
+ return void 0;
9612
+ }
9613
+ try {
9614
+ return new TextDecoder().decode(base64UrlToBytes(apu));
9615
+ } catch {
9616
+ return void 0;
9617
+ }
9618
+ }
9321
9619
  function isAuthorizationResponse(x) {
9322
9620
  return typeof x === "object" && x !== null && "vp_token" in x && typeof x.vp_token === "object" && x.vp_token !== null;
9323
9621
  }
@@ -9326,11 +9624,35 @@ async function verifyAuthorizationResponse(envelope, query, options) {
9326
9624
  if (!options.decryptionKey) {
9327
9625
  throw new exports.MissingDecryptionKeyError();
9328
9626
  }
9627
+ const nextOptions = { ...options };
9628
+ if (options.mdocSessionTranscript === void 0 && options.clientId !== void 0 && options.responseUri !== void 0 && options.nonce !== void 0) {
9629
+ if (options.sessionTranscriptProfile === "openid4vp-1.0") {
9630
+ if (options.verifierEncryptionJwk === void 0) {
9631
+ throw new exports.MissingVerifierEncryptionKeyError();
9632
+ }
9633
+ nextOptions.mdocSessionTranscript = await buildOpenID4VPHandoverSessionTranscript({
9634
+ clientId: options.clientId,
9635
+ nonce: options.nonce,
9636
+ responseUri: options.responseUri,
9637
+ verifierEncryptionJwk: options.verifierEncryptionJwk
9638
+ });
9639
+ } else {
9640
+ const mdocGeneratedNonce = extractMdocGeneratedNonce(envelope.response);
9641
+ if (mdocGeneratedNonce !== void 0) {
9642
+ nextOptions.mdocSessionTranscript = await buildOid4vpSessionTranscript({
9643
+ clientId: options.clientId,
9644
+ responseUri: options.responseUri,
9645
+ nonce: options.nonce,
9646
+ mdocGeneratedNonce
9647
+ });
9648
+ }
9649
+ }
9650
+ }
9329
9651
  const decrypted = await decryptAuthorizationResponse(
9330
9652
  envelope.response,
9331
9653
  options.decryptionKey
9332
9654
  );
9333
- return verifyAuthorizationResponse(decrypted, query, options);
9655
+ return verifyAuthorizationResponse(decrypted, query, nextOptions);
9334
9656
  }
9335
9657
  if (!isAuthorizationResponse(envelope)) {
9336
9658
  throw new TypeError("envelope must contain vp_token as a JSON object per OpenID4VP 1.0 \xA78.1");
@@ -9355,7 +9677,8 @@ async function verifyAuthorizationResponse(envelope, query, options) {
9355
9677
  throw new exports.MultipleCredentialsNotSupportedError(queryIds.length, presentationCount);
9356
9678
  }
9357
9679
  const presentation = vpToken[queryIds[0]][0];
9358
- return verifyPresentation(presentation, query, options);
9680
+ const decodedPresentation = query.credentials[0]?.format === "mso_mdoc" && typeof presentation === "string" ? base64UrlToBytes(presentation) : presentation;
9681
+ return verifyPresentation(decodedPresentation, query, options);
9359
9682
  }
9360
9683
 
9361
9684
  // src/index.ts
@@ -10055,6 +10378,8 @@ exports.SdJwtParser = SdJwtParser;
10055
10378
  exports.StaticTrustStore = StaticTrustStore;
10056
10379
  exports.VERSION = VERSION;
10057
10380
  exports.buildHaipQuery = buildHaipQuery;
10381
+ exports.buildOid4vpSessionTranscript = buildOid4vpSessionTranscript;
10382
+ exports.buildOpenID4VPHandoverSessionTranscript = buildOpenID4VPHandoverSessionTranscript;
10058
10383
  exports.createAuthorizationRequest = createAuthorizationRequest;
10059
10384
  exports.createSignedAuthorizationRequest = createSignedAuthorizationRequest;
10060
10385
  exports.decryptAuthorizationResponse = decryptAuthorizationResponse;