@openeudi/openid4vp 0.5.0 → 0.7.0

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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { X509Certificate, SubjectKeyIdentifierExtension, X509Certificates, BasicConstraintsExtension, KeyUsagesExtension, KeyUsageFlags as KeyUsageFlags$1, AuthorityKeyIdentifierExtension, SubjectAlternativeNameExtension, ExtendedKeyUsageExtension, Name as Name$1 } from '@peculiar/x509';
2
2
  import { v4 } from 'uuid';
3
+ import { SignJWT, importX509, decodeProtectedHeader, jwtVerify, importJWK, compactDecrypt } from 'jose';
3
4
  import { Encoder, Tag } from 'cbor-x';
4
- import { importX509, decodeProtectedHeader, jwtVerify, importJWK } from 'jose';
5
5
  import { splitSdJwt, decodeSdJwt, getClaims } from '@sd-jwt/decode';
6
6
  import { matchQuery, buildSubmission } from '@openeudi/dcql';
7
7
  export { DcqlMatchError, DcqlValidationError } from '@openeudi/dcql';
@@ -19,7 +19,7 @@ var __export = (target, all) => {
19
19
  };
20
20
 
21
21
  // src/errors.ts
22
- var InvalidSignatureError, ExpiredCredentialError, UnsupportedFormatError, MalformedCredentialError, NonceValidationError, HaipValidationError, OpenID4VPError, TrustAnchorNotFoundError, CertificateChainError, RevokedCertificateError, RevocationCheckFailedError, LotlFetchError, LotlSignatureError;
22
+ var InvalidSignatureError, ExpiredCredentialError, UnsupportedFormatError, MalformedCredentialError, NonceValidationError, HaipValidationError, OpenID4VPError, TrustAnchorNotFoundError, CertificateChainError, RevokedCertificateError, RevocationCheckFailedError, LotlFetchError, LotlSignatureError, SignedRequestBuildError, UnsupportedJweError, DecryptionFailedError, MissingDecryptionKeyError, MultipleCredentialsNotSupportedError;
23
23
  var init_errors = __esm({
24
24
  "src/errors.ts"() {
25
25
  InvalidSignatureError = class extends Error {
@@ -115,6 +115,47 @@ var init_errors = __esm({
115
115
  LotlSignatureError = class extends OpenID4VPError {
116
116
  code = "lotl_signature_invalid";
117
117
  };
118
+ SignedRequestBuildError = class extends OpenID4VPError {
119
+ code;
120
+ constructor(code, message) {
121
+ super(message);
122
+ this.code = code;
123
+ }
124
+ };
125
+ UnsupportedJweError = class extends OpenID4VPError {
126
+ code = "unsupported_jwe";
127
+ alg;
128
+ enc;
129
+ constructor(alg, enc) {
130
+ super(`Unsupported JWE algorithms: alg=${alg}, enc=${enc}`);
131
+ this.alg = alg;
132
+ this.enc = enc;
133
+ }
134
+ };
135
+ DecryptionFailedError = class extends OpenID4VPError {
136
+ code = "decryption_failed";
137
+ constructor(message = "Failed to decrypt authorization response", options) {
138
+ super(message, options);
139
+ }
140
+ };
141
+ MissingDecryptionKeyError = class extends OpenID4VPError {
142
+ code = "missing_decryption_key";
143
+ constructor(message = "decryptionKey is required to verify a direct_post.jwt response") {
144
+ super(message);
145
+ }
146
+ };
147
+ MultipleCredentialsNotSupportedError = class extends OpenID4VPError {
148
+ code = "multi_credential_unsupported";
149
+ entryCount;
150
+ presentationCount;
151
+ constructor(entryCount, presentationCount) {
152
+ super(
153
+ `Multi-credential presentations are not yet supported: ${entryCount} queryId entries, ${presentationCount} presentations`
154
+ );
155
+ this.entryCount = entryCount;
156
+ this.presentationCount = presentationCount;
157
+ }
158
+ };
118
159
  }
119
160
  });
120
161
 
@@ -610,6 +651,9 @@ __export(index_es_exports, {
610
651
  Choice: () => Choice,
611
652
  Constructed: () => Constructed,
612
653
  DATE: () => DATE,
654
+ DEFAULT_MAX_CONTENT_LENGTH: () => DEFAULT_MAX_CONTENT_LENGTH,
655
+ DEFAULT_MAX_DEPTH: () => DEFAULT_MAX_DEPTH,
656
+ DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
613
657
  DateTime: () => DateTime,
614
658
  Duration: () => Duration,
615
659
  EndOfContent: () => EndOfContent,
@@ -704,7 +748,7 @@ function HexBlock(BaseClass) {
704
748
  this.isHexOnly = (_b = params.isHexOnly) !== null && _b !== void 0 ? _b : false;
705
749
  this.valueHexView = params.valueHex ? BufferSourceConverter.toUint8Array(params.valueHex) : EMPTY_VIEW;
706
750
  }
707
- fromBER(inputBuffer, inputOffset, inputLength) {
751
+ fromBER(inputBuffer, inputOffset, inputLength, _context) {
708
752
  const view = inputBuffer instanceof ArrayBuffer ? new Uint8Array(inputBuffer) : inputBuffer;
709
753
  if (!checkBufferParams(this, view, inputOffset, inputLength)) {
710
754
  return -1;
@@ -748,6 +792,49 @@ function prepareIndefiniteForm(baseBlock) {
748
792
  }
749
793
  return !!((_a2 = baseBlock.lenBlock) === null || _a2 === void 0 ? void 0 : _a2.isIndefiniteForm);
750
794
  }
795
+ function createFromBerContext(options = {}) {
796
+ var _a2, _b, _c;
797
+ return {
798
+ depth: 0,
799
+ maxDepth: (_a2 = options.maxDepth) !== null && _a2 !== void 0 ? _a2 : DEFAULT_MAX_DEPTH,
800
+ nodesCount: 0,
801
+ maxNodes: (_b = options.maxNodes) !== null && _b !== void 0 ? _b : DEFAULT_MAX_NODES,
802
+ maxContentLength: (_c = options.maxContentLength) !== null && _c !== void 0 ? _c : DEFAULT_MAX_CONTENT_LENGTH
803
+ };
804
+ }
805
+ function createErrorResult(error) {
806
+ const result = new BaseBlock({}, ValueBlock);
807
+ result.error = error;
808
+ return {
809
+ offset: -1,
810
+ result
811
+ };
812
+ }
813
+ function checkNodesLimit(context) {
814
+ context.nodesCount += 1;
815
+ if (context.nodesCount > context.maxNodes) {
816
+ return MAX_NODES_EXCEEDED_ERROR;
817
+ }
818
+ return void 0;
819
+ }
820
+ function checkContentLengthLimit(inputLength, context) {
821
+ if (inputLength > context.maxContentLength) {
822
+ return MAX_CONTENT_LENGTH_EXCEEDED_ERROR;
823
+ }
824
+ return void 0;
825
+ }
826
+ function localFromBERWithChildContext(inputBuffer, inputOffset, inputLength, context) {
827
+ const childDepth = context.depth + 1;
828
+ if (childDepth > context.maxDepth) {
829
+ return createErrorResult(MAX_DEPTH_EXCEEDED_ERROR);
830
+ }
831
+ context.depth = childDepth;
832
+ try {
833
+ return localFromBER(inputBuffer, inputOffset, inputLength, context);
834
+ } finally {
835
+ context.depth -= 1;
836
+ }
837
+ }
751
838
  function localChangeType(inputObject, newType) {
752
839
  if (inputObject instanceof newType) {
753
840
  return inputObject;
@@ -759,7 +846,7 @@ function localChangeType(inputObject, newType) {
759
846
  newObject.valueBeforeDecodeView = inputObject.valueBeforeDecodeView;
760
847
  return newObject;
761
848
  }
762
- function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.length) {
849
+ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.length, context = createFromBerContext()) {
763
850
  const incomingOffset = inputOffset;
764
851
  let returnObject = new BaseBlock({}, ValueBlock);
765
852
  const baseBlock = new LocalBaseBlock();
@@ -778,6 +865,14 @@ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.le
778
865
  result: returnObject
779
866
  };
780
867
  }
868
+ const nodesLimitError = checkNodesLimit(context);
869
+ if (nodesLimitError) {
870
+ returnObject.error = nodesLimitError;
871
+ return {
872
+ offset: -1,
873
+ result: returnObject
874
+ };
875
+ }
781
876
  let resultOffset = returnObject.idBlock.fromBER(inputBuffer, inputOffset, inputLength);
782
877
  if (returnObject.idBlock.warnings.length) {
783
878
  returnObject.warnings.concat(returnObject.idBlock.warnings);
@@ -804,6 +899,15 @@ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.le
804
899
  }
805
900
  inputOffset = resultOffset;
806
901
  inputLength -= returnObject.lenBlock.blockLength;
902
+ const valueLength = returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length;
903
+ const contentLengthError = checkContentLengthLimit(valueLength, context);
904
+ if (contentLengthError) {
905
+ returnObject.error = contentLengthError;
906
+ return {
907
+ offset: -1,
908
+ result: returnObject
909
+ };
910
+ }
807
911
  if (!returnObject.idBlock.isConstructed && returnObject.lenBlock.isIndefiniteForm) {
808
912
  returnObject.error = "Indefinite length form used for primitive encoding form";
809
913
  return {
@@ -942,14 +1046,14 @@ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.le
942
1046
  }
943
1047
  }
944
1048
  returnObject = localChangeType(returnObject, newASN1Type);
945
- resultOffset = returnObject.fromBER(inputBuffer, inputOffset, returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length);
1049
+ resultOffset = returnObject.fromBER(inputBuffer, inputOffset, valueLength, context);
946
1050
  returnObject.valueBeforeDecodeView = inputBuffer.subarray(incomingOffset, incomingOffset + returnObject.blockLength);
947
1051
  return {
948
1052
  offset: resultOffset,
949
1053
  result: returnObject
950
1054
  };
951
1055
  }
952
- function fromBER(inputBuffer) {
1056
+ function fromBER(inputBuffer, options = {}) {
953
1057
  if (!inputBuffer.byteLength) {
954
1058
  const result = new BaseBlock({}, ValueBlock);
955
1059
  result.error = "Input buffer has zero length";
@@ -958,7 +1062,7 @@ function fromBER(inputBuffer) {
958
1062
  result
959
1063
  };
960
1064
  }
961
- return localFromBER(BufferSourceConverter.toUint8Array(inputBuffer).slice(), 0, inputBuffer.byteLength);
1065
+ return localFromBER(BufferSourceConverter.toUint8Array(inputBuffer).slice(), 0, inputBuffer.byteLength, createFromBerContext(options));
962
1066
  }
963
1067
  function checkLen(indefiniteLength, length) {
964
1068
  if (indefiniteLength) {
@@ -1364,7 +1468,7 @@ function verifySchema(inputBuffer, inputSchema) {
1364
1468
  }
1365
1469
  return compareSchema(asn1.result, asn1.result, inputSchema);
1366
1470
  }
1367
- var ViewWriter, powers2, digitsString, NAME, VALUE_HEX_VIEW, IS_HEX_ONLY, ID_BLOCK, TAG_CLASS, TAG_NUMBER, IS_CONSTRUCTED, FROM_BER, TO_BER, LOCAL, EMPTY_STRING, EMPTY_BUFFER, EMPTY_VIEW, END_OF_CONTENT_NAME, OCTET_STRING_NAME, BIT_STRING_NAME, LocalBaseBlock, ValueBlock, LocalIdentificationBlock, LocalLengthBlock, typeStore, BaseBlock, BaseStringBlock, LocalPrimitiveValueBlock, _a$w, Primitive, LocalConstructedValueBlock, _a$v, Constructed, LocalEndOfContentValueBlock, _a$u, EndOfContent, _a$t, Null, LocalBooleanValueBlock, _a$s, Boolean2, LocalOctetStringValueBlock, _a$r, OctetString, LocalBitStringValueBlock, _a$q, BitString, _a$p, LocalIntegerValueBlock, _a$o, Integer, _a$n, Enumerated, LocalSidValueBlock, LocalObjectIdentifierValueBlock, _a$m, ObjectIdentifier, LocalRelativeSidValueBlock, LocalRelativeObjectIdentifierValueBlock, _a$l, RelativeObjectIdentifier, _a$k, Sequence, _a$j, Set2, LocalStringValueBlock, LocalSimpleStringValueBlock, LocalSimpleStringBlock, LocalUtf8StringValueBlock, _a$i, Utf8String, LocalBmpStringValueBlock, _a$h, BmpString, LocalUniversalStringValueBlock, _a$g, UniversalString, _a$f, NumericString, _a$e, PrintableString, _a$d, TeletexString, _a$c, VideotexString, _a$b, IA5String, _a$a, GraphicString, _a$9, VisibleString, _a$8, GeneralString, _a$7, CharacterString, _a$6, UTCTime, _a$5, GeneralizedTime, _a$4, DATE, _a$3, TimeOfDay, _a$2, DateTime, _a$1, Duration, _a, TIME, Any, Choice, Repeated, RawData;
1471
+ var ViewWriter, powers2, digitsString, NAME, VALUE_HEX_VIEW, IS_HEX_ONLY, ID_BLOCK, TAG_CLASS, TAG_NUMBER, IS_CONSTRUCTED, FROM_BER, TO_BER, LOCAL, EMPTY_STRING, EMPTY_BUFFER, EMPTY_VIEW, END_OF_CONTENT_NAME, OCTET_STRING_NAME, BIT_STRING_NAME, LocalBaseBlock, ValueBlock, LocalIdentificationBlock, LocalLengthBlock, typeStore, BaseBlock, BaseStringBlock, LocalPrimitiveValueBlock, _a$w, Primitive, DEFAULT_MAX_DEPTH, DEFAULT_MAX_NODES, DEFAULT_MAX_CONTENT_LENGTH, MAX_DEPTH_EXCEEDED_ERROR, MAX_NODES_EXCEEDED_ERROR, MAX_CONTENT_LENGTH_EXCEEDED_ERROR, LocalConstructedValueBlock, _a$v, Constructed, LocalEndOfContentValueBlock, _a$u, EndOfContent, _a$t, Null, LocalBooleanValueBlock, _a$s, Boolean2, LocalOctetStringValueBlock, _a$r, OctetString, LocalBitStringValueBlock, _a$q, BitString, _a$p, LocalIntegerValueBlock, _a$o, Integer, _a$n, Enumerated, LocalSidValueBlock, LocalObjectIdentifierValueBlock, _a$m, ObjectIdentifier, LocalRelativeSidValueBlock, LocalRelativeObjectIdentifierValueBlock, _a$l, RelativeObjectIdentifier, _a$k, Sequence, _a$j, Set2, LocalStringValueBlock, LocalSimpleStringValueBlock, LocalSimpleStringBlock, LocalUtf8StringValueBlock, _a$i, Utf8String, LocalBmpStringValueBlock, _a$h, BmpString, LocalUniversalStringValueBlock, _a$g, UniversalString, _a$f, NumericString, _a$e, PrintableString, _a$d, TeletexString, _a$c, VideotexString, _a$b, IA5String, _a$a, GraphicString, _a$9, VisibleString, _a$8, GeneralString, _a$7, CharacterString, _a$6, UTCTime, _a$5, GeneralizedTime, _a$4, DATE, _a$3, TimeOfDay, _a$2, DateTime, _a$1, Duration, _a, TIME, Any, Choice, Repeated, RawData;
1368
1472
  var init_index_es2 = __esm({
1369
1473
  "node_modules/asn1js/build/index.es.js"() {
1370
1474
  init_index_es();
@@ -1426,7 +1530,7 @@ var init_index_es2 = __esm({
1426
1530
  };
1427
1531
  LocalBaseBlock.NAME = "baseBlock";
1428
1532
  ValueBlock = class extends LocalBaseBlock {
1429
- fromBER(_inputBuffer, _inputOffset, _inputLength) {
1533
+ fromBER(_inputBuffer, _inputOffset, _inputLength, _context) {
1430
1534
  throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'");
1431
1535
  }
1432
1536
  toBER(_sizeOnly, _writer) {
@@ -1539,31 +1643,21 @@ var init_index_es2 = __esm({
1539
1643
  this.tagNumber = tagNumberMask;
1540
1644
  this.blockLength = 1;
1541
1645
  } else {
1542
- let count = 1;
1543
- let intTagNumberBuffer = this.valueHexView = new Uint8Array(255);
1544
- let tagNumberBufferMaxLength = 255;
1545
- while (intBuffer[count] & 128) {
1546
- intTagNumberBuffer[count - 1] = intBuffer[count] & 127;
1547
- count++;
1548
- if (count >= intBuffer.length) {
1646
+ let count = 0;
1647
+ while (true) {
1648
+ const tagByteIndex = count + 1;
1649
+ if (tagByteIndex >= intBuffer.length) {
1549
1650
  this.error = "End of input reached before message was fully decoded";
1550
1651
  return -1;
1551
1652
  }
1552
- if (count === tagNumberBufferMaxLength) {
1553
- tagNumberBufferMaxLength += 255;
1554
- const tempBufferView2 = new Uint8Array(tagNumberBufferMaxLength);
1555
- for (let i = 0; i < intTagNumberBuffer.length; i++)
1556
- tempBufferView2[i] = intTagNumberBuffer[i];
1557
- intTagNumberBuffer = this.valueHexView = new Uint8Array(tagNumberBufferMaxLength);
1558
- }
1653
+ count++;
1654
+ if ((intBuffer[tagByteIndex] & 128) === 0)
1655
+ break;
1559
1656
  }
1560
1657
  this.blockLength = count + 1;
1561
- intTagNumberBuffer[count - 1] = intBuffer[count] & 127;
1562
- const tempBufferView = new Uint8Array(count);
1658
+ const intTagNumberBuffer = this.valueHexView = new Uint8Array(count);
1563
1659
  for (let i = 0; i < count; i++)
1564
- tempBufferView[i] = intTagNumberBuffer[i];
1565
- intTagNumberBuffer = this.valueHexView = new Uint8Array(count);
1566
- intTagNumberBuffer.set(tempBufferView);
1660
+ intTagNumberBuffer[i] = intBuffer[i + 1] & 127;
1567
1661
  if (this.blockLength <= 9)
1568
1662
  this.tagNumber = utilFromBase(intTagNumberBuffer, 7);
1569
1663
  else {
@@ -1713,8 +1807,8 @@ var init_index_es2 = __esm({
1713
1807
  this.lenBlock = new LocalLengthBlock(parameters);
1714
1808
  this.valueBlock = valueBlockType ? new valueBlockType(parameters) : new ValueBlock(parameters);
1715
1809
  }
1716
- fromBER(inputBuffer, inputOffset, inputLength) {
1717
- const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
1810
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
1811
+ const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length, context);
1718
1812
  if (resultOffset === -1) {
1719
1813
  this.error = this.valueBlock.error;
1720
1814
  return resultOffset;
@@ -1838,14 +1932,21 @@ var init_index_es2 = __esm({
1838
1932
  typeStore.Primitive = _a$w;
1839
1933
  })();
1840
1934
  Primitive.NAME = "PRIMITIVE";
1935
+ DEFAULT_MAX_DEPTH = 100;
1936
+ DEFAULT_MAX_NODES = 1e4;
1937
+ DEFAULT_MAX_CONTENT_LENGTH = 16 * 1024 * 1024;
1938
+ MAX_DEPTH_EXCEEDED_ERROR = "Maximum ASN.1 nesting depth exceeded";
1939
+ MAX_NODES_EXCEEDED_ERROR = "Maximum ASN.1 node count exceeded";
1940
+ MAX_CONTENT_LENGTH_EXCEEDED_ERROR = "Maximum ASN.1 content length exceeded";
1841
1941
  LocalConstructedValueBlock = class extends ValueBlock {
1842
1942
  constructor({ value = [], isIndefiniteForm = false, ...parameters } = {}) {
1843
1943
  super(parameters);
1844
1944
  this.value = value;
1845
1945
  this.isIndefiniteForm = isIndefiniteForm;
1846
1946
  }
1847
- fromBER(inputBuffer, inputOffset, inputLength) {
1947
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
1848
1948
  const view = BufferSourceConverter.toUint8Array(inputBuffer);
1949
+ const parseContext = context !== null && context !== void 0 ? context : createFromBerContext();
1849
1950
  if (!checkBufferParams(this, view, inputOffset, inputLength)) {
1850
1951
  return -1;
1851
1952
  }
@@ -1856,7 +1957,7 @@ var init_index_es2 = __esm({
1856
1957
  }
1857
1958
  let currentOffset = inputOffset;
1858
1959
  while (checkLen(this.isIndefiniteForm, inputLength) > 0) {
1859
- const returnObject = localFromBER(view, currentOffset, inputLength);
1960
+ const returnObject = localFromBERWithChildContext(view, currentOffset, inputLength, parseContext);
1860
1961
  if (returnObject.offset === -1) {
1861
1962
  this.error = returnObject.result.error;
1862
1963
  this.warnings.concat(returnObject.result.warnings);
@@ -1907,9 +2008,9 @@ var init_index_es2 = __esm({
1907
2008
  super(parameters, LocalConstructedValueBlock);
1908
2009
  this.idBlock.isConstructed = true;
1909
2010
  }
1910
- fromBER(inputBuffer, inputOffset, inputLength) {
2011
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
1911
2012
  this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
1912
- const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
2013
+ const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length, context);
1913
2014
  if (resultOffset === -1) {
1914
2015
  this.error = this.valueBlock.error;
1915
2016
  return resultOffset;
@@ -2072,11 +2173,11 @@ ${values.join("\n")}` : `${blockName} :`;
2072
2173
  super(parameters);
2073
2174
  this.isConstructed = isConstructed;
2074
2175
  }
2075
- fromBER(inputBuffer, inputOffset, inputLength) {
2176
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2076
2177
  let resultOffset = 0;
2077
2178
  if (this.isConstructed) {
2078
2179
  this.isHexOnly = false;
2079
- resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength);
2180
+ resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength, context);
2080
2181
  if (resultOffset === -1)
2081
2182
  return resultOffset;
2082
2183
  for (let i = 0; i < this.value.length; i++) {
@@ -2132,7 +2233,7 @@ ${values.join("\n")}` : `${blockName} :`;
2132
2233
  this.idBlock.tagClass = 1;
2133
2234
  this.idBlock.tagNumber = 4;
2134
2235
  }
2135
- fromBER(inputBuffer, inputOffset, inputLength) {
2236
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2136
2237
  this.valueBlock.isConstructed = this.idBlock.isConstructed;
2137
2238
  this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
2138
2239
  if (inputLength === 0) {
@@ -2147,7 +2248,8 @@ ${values.join("\n")}` : `${blockName} :`;
2147
2248
  const buf = view.subarray(inputOffset, inputOffset + inputLength);
2148
2249
  try {
2149
2250
  if (buf.byteLength) {
2150
- const asn = localFromBER(buf, 0, buf.byteLength);
2251
+ const parseContext = context !== null && context !== void 0 ? context : createFromBerContext();
2252
+ const asn = localFromBERWithChildContext(buf, 0, buf.byteLength, parseContext);
2151
2253
  if (asn.offset !== -1 && asn.offset === inputLength) {
2152
2254
  this.valueBlock.value = [asn.result];
2153
2255
  }
@@ -2155,7 +2257,7 @@ ${values.join("\n")}` : `${blockName} :`;
2155
2257
  } catch {
2156
2258
  }
2157
2259
  }
2158
- return super.fromBER(inputBuffer, inputOffset, inputLength);
2260
+ return super.fromBER(inputBuffer, inputOffset, inputLength, context);
2159
2261
  }
2160
2262
  onAsciiEncoding() {
2161
2263
  if (this.valueBlock.isConstructed || this.valueBlock.value && this.valueBlock.value.length) {
@@ -2190,13 +2292,13 @@ ${values.join("\n")}` : `${blockName} :`;
2190
2292
  this.isConstructed = isConstructed;
2191
2293
  this.blockLength = this.valueHexView.byteLength;
2192
2294
  }
2193
- fromBER(inputBuffer, inputOffset, inputLength) {
2295
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2194
2296
  if (!inputLength) {
2195
2297
  return inputOffset;
2196
2298
  }
2197
2299
  let resultOffset = -1;
2198
2300
  if (this.isConstructed) {
2199
- resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength);
2301
+ resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength, context);
2200
2302
  if (resultOffset === -1)
2201
2303
  return resultOffset;
2202
2304
  for (const value of this.value) {
@@ -2236,7 +2338,8 @@ ${values.join("\n")}` : `${blockName} :`;
2236
2338
  const buf = intBuffer.subarray(1);
2237
2339
  try {
2238
2340
  if (buf.byteLength) {
2239
- const asn = localFromBER(buf, 0, buf.byteLength);
2341
+ const parseContext = context !== null && context !== void 0 ? context : createFromBerContext();
2342
+ const asn = localFromBERWithChildContext(buf, 0, buf.byteLength, parseContext);
2240
2343
  if (asn.offset !== -1 && asn.offset === inputLength - 1) {
2241
2344
  this.value = [asn.result];
2242
2345
  }
@@ -2292,10 +2395,10 @@ ${values.join("\n")}` : `${blockName} :`;
2292
2395
  this.idBlock.tagClass = 1;
2293
2396
  this.idBlock.tagNumber = 3;
2294
2397
  }
2295
- fromBER(inputBuffer, inputOffset, inputLength) {
2398
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2296
2399
  this.valueBlock.isConstructed = this.idBlock.isConstructed;
2297
2400
  this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
2298
- return super.fromBER(inputBuffer, inputOffset, inputLength);
2401
+ return super.fromBER(inputBuffer, inputOffset, inputLength, context);
2299
2402
  }
2300
2403
  onAsciiEncoding() {
2301
2404
  if (this.valueBlock.isConstructed || this.valueBlock.value && this.valueBlock.value.length) {
@@ -7962,6 +8065,135 @@ function createAuthorizationRequest(input, query) {
7962
8065
  };
7963
8066
  }
7964
8067
 
8068
+ // src/signed-authorization.ts
8069
+ init_errors();
8070
+ var DEFAULT_SUPPORTED_ENC_VALUES = ["A128GCM", "A256GCM"];
8071
+ var SUPPORTED_SIGNING_ALGS = /* @__PURE__ */ new Set(["ES256", "ES384", "RS256"]);
8072
+ async function createSignedAuthorizationRequest(input, query) {
8073
+ if (input.certificateChain.length === 0) {
8074
+ throw new SignedRequestBuildError(
8075
+ "empty_cert_chain",
8076
+ "certificateChain must contain at least the leaf certificate"
8077
+ );
8078
+ }
8079
+ const signingAlg = input.signingAlgorithm ?? "ES256";
8080
+ if (!SUPPORTED_SIGNING_ALGS.has(signingAlg)) {
8081
+ throw new SignedRequestBuildError(
8082
+ "unsupported_signing_alg",
8083
+ `signingAlgorithm ${signingAlg} is not supported (expected ES256/ES384/RS256)`
8084
+ );
8085
+ }
8086
+ if (!input.vpFormatsSupported || Object.keys(input.vpFormatsSupported).length === 0) {
8087
+ throw new SignedRequestBuildError(
8088
+ "missing_vp_formats",
8089
+ "vpFormatsSupported is required and must be non-empty"
8090
+ );
8091
+ }
8092
+ const leafCert = new X509Certificate(toArrayBuffer(input.certificateChain[0]));
8093
+ const sanDnsNames = extractDnsNames(leafCert);
8094
+ if (!sanDnsNames.includes(input.hostname)) {
8095
+ throw new SignedRequestBuildError(
8096
+ "hostname_cert_mismatch",
8097
+ `leaf cert SAN DNSName values [${sanDnsNames.join(", ")}] do not include hostname "${input.hostname}"`
8098
+ );
8099
+ }
8100
+ const signerPublicSpki = new Uint8Array(
8101
+ await crypto.subtle.exportKey("spki", input.signer.publicKey)
8102
+ );
8103
+ const leafSpki = new Uint8Array(leafCert.publicKey.rawData);
8104
+ if (!bytesEqual(signerPublicSpki, leafSpki)) {
8105
+ throw new SignedRequestBuildError(
8106
+ "signing_key_cert_mismatch",
8107
+ "signer.publicKey SPKI does not match leaf certificate SPKI"
8108
+ );
8109
+ }
8110
+ const responseMode = input.responseMode ?? "direct_post.jwt";
8111
+ if (responseMode === "direct_post.jwt") {
8112
+ if (!input.encryptionKey?.publicJwk) {
8113
+ throw new SignedRequestBuildError(
8114
+ "missing_encryption_jwk",
8115
+ "direct_post.jwt response mode requires encryptionKey.publicJwk"
8116
+ );
8117
+ }
8118
+ if (!input.encryptionKey.publicJwk.alg) {
8119
+ throw new SignedRequestBuildError(
8120
+ "missing_encryption_alg",
8121
+ "encryptionKey.publicJwk must carry alg (OpenID4VP 1.0 \xA78.3)"
8122
+ );
8123
+ }
8124
+ }
8125
+ const state = input.state ?? v4();
8126
+ const clientId = `x509_san_dns:${input.hostname}`;
8127
+ const now = Math.floor(Date.now() / 1e3);
8128
+ const clientMetadata = {
8129
+ vp_formats_supported: input.vpFormatsSupported
8130
+ };
8131
+ if (responseMode === "direct_post.jwt" && input.encryptionKey) {
8132
+ const enc = input.encryptionKey.supportedEncValues;
8133
+ if (enc !== void 0 && enc.length === 0) {
8134
+ throw new SignedRequestBuildError(
8135
+ "empty_supported_enc_values",
8136
+ "encryptionKey.supportedEncValues must not be empty (omit to use defaults [A128GCM, A256GCM])"
8137
+ );
8138
+ }
8139
+ const jwk = { ...input.encryptionKey.publicJwk, use: "enc" };
8140
+ clientMetadata.jwks = { keys: [jwk] };
8141
+ clientMetadata.encrypted_response_enc_values_supported = enc ?? [...DEFAULT_SUPPORTED_ENC_VALUES];
8142
+ }
8143
+ const payload = {
8144
+ iss: clientId,
8145
+ aud: "https://self-issued.me/v2",
8146
+ response_type: "vp_token",
8147
+ response_mode: responseMode,
8148
+ client_id: clientId,
8149
+ response_uri: input.responseUri,
8150
+ nonce: input.nonce,
8151
+ state,
8152
+ dcql_query: query,
8153
+ client_metadata: clientMetadata,
8154
+ iat: now,
8155
+ exp: now + 120
8156
+ };
8157
+ const x5c = input.certificateChain.map((der) => bytesToBase64(der));
8158
+ const jws = await new SignJWT(payload).setProtectedHeader({ typ: "oauth-authz-req+jwt", alg: signingAlg, x5c }).sign(input.signer.privateKey);
8159
+ const uriParams = new URLSearchParams({
8160
+ client_id: clientId,
8161
+ request_uri: input.requestUri
8162
+ });
8163
+ return {
8164
+ uri: `openid4vp://authorize?${uriParams.toString()}`,
8165
+ requestObject: jws,
8166
+ dcqlQuery: query,
8167
+ nonce: input.nonce,
8168
+ state
8169
+ };
8170
+ }
8171
+ function extractDnsNames(cert) {
8172
+ const SAN_OID = "2.5.29.17";
8173
+ const ext = cert.extensions.find((e) => e.type === SAN_OID);
8174
+ if (!ext) return [];
8175
+ const names = ext.names;
8176
+ const items = names?.items ?? [];
8177
+ return items.filter((e) => e.type === "dns").map((e) => e.value);
8178
+ }
8179
+ function bytesEqual(a, b) {
8180
+ if (a.length !== b.length) return false;
8181
+ for (let i = 0; i < a.length; i++) {
8182
+ if (a[i] !== b[i]) return false;
8183
+ }
8184
+ return true;
8185
+ }
8186
+ function bytesToBase64(bytes) {
8187
+ let s = "";
8188
+ for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
8189
+ return btoa(s);
8190
+ }
8191
+ function toArrayBuffer(bytes) {
8192
+ const out = new ArrayBuffer(bytes.byteLength);
8193
+ new Uint8Array(out).set(bytes);
8194
+ return out;
8195
+ }
8196
+
7965
8197
  // src/haip.ts
7966
8198
  init_errors();
7967
8199
  var HAIP_DOCTYPE_NAMESPACES = {
@@ -8188,7 +8420,7 @@ async function computeItemDigest(itemBytes, digestAlgorithm) {
8188
8420
  const buf = await crypto.subtle.digest(digestAlgorithm, new Uint8Array(itemBytes));
8189
8421
  return new Uint8Array(buf);
8190
8422
  }
8191
- function bytesEqual(a, b) {
8423
+ function bytesEqual2(a, b) {
8192
8424
  if (a.length !== b.length) return false;
8193
8425
  for (let i = 0; i < a.length; i++) {
8194
8426
  if (a[i] !== b[i]) return false;
@@ -8234,7 +8466,7 @@ async function verifyAllDigests(nameSpaces, mso) {
8234
8466
  if (!expectedDigest) {
8235
8467
  throw new MalformedCredentialError(`digestID ${digestID} missing from MSO valueDigests[${ns}]`);
8236
8468
  }
8237
- if (!bytesEqual(hash, expectedDigest)) {
8469
+ if (!bytesEqual2(hash, expectedDigest)) {
8238
8470
  throw new MalformedCredentialError(`digest mismatch for ns=${ns} digestID=${digestID}`);
8239
8471
  }
8240
8472
  }
@@ -8353,7 +8585,7 @@ init_errors();
8353
8585
  var decoder4 = new Encoder({ mapsAsObjects: false, useRecords: false });
8354
8586
  var encoder = new Encoder({ mapsAsObjects: false, useRecords: false });
8355
8587
  var DEFAULT_ALLOWED_ALGORITHMS = ["ES256", "ES384", "ES512"];
8356
- function bytesEqual2(a, b) {
8588
+ function bytesEqual3(a, b) {
8357
8589
  if (a.length !== b.length) return false;
8358
8590
  for (let i = 0; i < a.length; i++) {
8359
8591
  if (a[i] !== b[i]) return false;
@@ -8489,8 +8721,8 @@ var MdocParser = class {
8489
8721
  cache: options.cache,
8490
8722
  clockSkewTolerance: options.clockSkewTolerance
8491
8723
  });
8492
- const { X509Certificate: X509Certificate8 } = await import('@peculiar/x509');
8493
- const leaf = new X509Certificate8(issuerCertBytes);
8724
+ const { X509Certificate: X509Certificate9 } = await import('@peculiar/x509');
8725
+ const leaf = new X509Certificate9(issuerCertBytes);
8494
8726
  trustResult = await evaluator.evaluate(leaf);
8495
8727
  } else {
8496
8728
  if (options.trustedCertificates.length === 0) {
@@ -8500,7 +8732,7 @@ var MdocParser = class {
8500
8732
  );
8501
8733
  }
8502
8734
  } else {
8503
- const trusted = options.trustedCertificates.some((t) => bytesEqual2(t, issuerCertBytes));
8735
+ const trusted = options.trustedCertificates.some((t) => bytesEqual3(t, issuerCertBytes));
8504
8736
  if (!trusted) {
8505
8737
  return {
8506
8738
  valid: false,
@@ -8615,7 +8847,7 @@ function base64ToBytes(base64) {
8615
8847
  }
8616
8848
  return bytes;
8617
8849
  }
8618
- function bytesEqual3(a, b) {
8850
+ function bytesEqual4(a, b) {
8619
8851
  if (a.length !== b.length) return false;
8620
8852
  for (let i = 0; i < a.length; i++) {
8621
8853
  if (a[i] !== b[i]) return false;
@@ -8738,8 +8970,8 @@ var SdJwtParser = class {
8738
8970
  cache: options.cache,
8739
8971
  clockSkewTolerance: options.clockSkewTolerance
8740
8972
  });
8741
- const { X509Certificate: X509Certificate8 } = await import('@peculiar/x509');
8742
- const leaf = new X509Certificate8(issuerCertBytes);
8973
+ const { X509Certificate: X509Certificate9 } = await import('@peculiar/x509');
8974
+ const leaf = new X509Certificate9(issuerCertBytes);
8743
8975
  trustResult = await evaluator.evaluate(leaf);
8744
8976
  } else {
8745
8977
  if (options.trustedCertificates.length === 0) {
@@ -8749,7 +8981,7 @@ var SdJwtParser = class {
8749
8981
  );
8750
8982
  }
8751
8983
  } else {
8752
- const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual3(trusted, issuerCertBytes));
8984
+ const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual4(trusted, issuerCertBytes));
8753
8985
  if (!isTrusted) {
8754
8986
  return invalidResult("Issuer certificate is not trusted");
8755
8987
  }
@@ -8846,42 +9078,48 @@ async function parsePresentation(vpToken, options) {
8846
9078
  error: "Unsupported credential format"
8847
9079
  };
8848
9080
  }
8849
- var INTERNAL_TO_DCQL_FORMAT = {
8850
- "sd-jwt-vc": "dc+sd-jwt",
8851
- mdoc: "mso_mdoc"
8852
- };
8853
- function classifyMismatch(query, decoded, hasDetail) {
8854
- if (decoded.format !== query.format) return "format_mismatch";
8855
- if (query.format === "dc+sd-jwt") {
8856
- const allowed = query.meta?.vct_values;
8857
- if (allowed && allowed.length > 0 && !allowed.includes(decoded.vct ?? "")) {
8858
- return "vct_mismatch";
8859
- }
9081
+
9082
+ // src/decrypt-response.ts
9083
+ init_errors();
9084
+ var SUPPORTED_ALG = "ECDH-ES";
9085
+ var SUPPORTED_ENC = /* @__PURE__ */ new Set(["A128GCM", "A256GCM"]);
9086
+ async function decryptAuthorizationResponse(jwe, privateKey) {
9087
+ let header;
9088
+ try {
9089
+ header = decodeProtectedHeader(jwe);
9090
+ } catch (err) {
9091
+ throw new DecryptionFailedError("Malformed JWE header", {
9092
+ cause: err
9093
+ });
8860
9094
  }
8861
- if (query.format === "mso_mdoc") {
8862
- const expected = query.meta?.doctype_value;
8863
- if (expected && expected !== decoded.doctype) return "doctype_mismatch";
9095
+ const alg = typeof header.alg === "string" ? header.alg : "";
9096
+ const enc = typeof header.enc === "string" ? header.enc : "";
9097
+ if (alg !== SUPPORTED_ALG || !SUPPORTED_ENC.has(enc)) {
9098
+ throw new UnsupportedJweError(alg, enc);
8864
9099
  }
8865
- if (query.trusted_authorities && query.trusted_authorities.length > 0) {
8866
- const decodedAuthIds = decoded.trusted_authority_ids ?? [];
8867
- const anyMatch = query.trusted_authorities.some(
8868
- (ta) => ta.values.some((v) => decodedAuthIds.includes(v))
8869
- );
8870
- if (!anyMatch) return "trusted_authority_mismatch";
9100
+ let plaintext;
9101
+ try {
9102
+ const result = await compactDecrypt(jwe, privateKey);
9103
+ plaintext = result.plaintext;
9104
+ } catch (err) {
9105
+ throw new DecryptionFailedError(void 0, { cause: err });
9106
+ }
9107
+ try {
9108
+ const text = new TextDecoder().decode(plaintext);
9109
+ return JSON.parse(text);
9110
+ } catch (err) {
9111
+ throw new DecryptionFailedError("Decrypted plaintext is not valid JSON", {
9112
+ cause: err
9113
+ });
8871
9114
  }
8872
- if (hasDetail) return "missing_claims";
8873
- return "no_credential_found";
8874
- }
8875
- function refineUnmatched(query, decoded, match) {
8876
- if (match.unmatched.length === 0) return match;
8877
- const refined = match.unmatched.map((entry) => {
8878
- const credentialQuery = query.credentials.find((c) => c.id === entry.queryId);
8879
- if (!credentialQuery) return entry;
8880
- const specific = classifyMismatch(credentialQuery, decoded, entry.detail !== void 0);
8881
- return { ...entry, reason: specific };
8882
- });
8883
- return { ...match, unmatched: refined };
8884
9115
  }
9116
+
9117
+ // src/verify.ts
9118
+ init_errors();
9119
+ var INTERNAL_TO_DCQL_FORMAT = {
9120
+ "sd-jwt-vc": "dc+sd-jwt",
9121
+ mdoc: "mso_mdoc"
9122
+ };
8885
9123
  async function verifyPresentation(vpToken, query, options) {
8886
9124
  const parsed = await parsePresentation(vpToken, options);
8887
9125
  const dcqlFormat = INTERNAL_TO_DCQL_FORMAT[parsed.format] ?? parsed.format;
@@ -8900,8 +9138,7 @@ async function verifyPresentation(vpToken, query, options) {
8900
9138
  if (parsed.trust?.trustedAuthorityIds && parsed.trust.trustedAuthorityIds.length > 0) {
8901
9139
  decoded.trusted_authority_ids = [...parsed.trust.trustedAuthorityIds];
8902
9140
  }
8903
- const rawMatch = matchQuery(query, [decoded]);
8904
- const match = refineUnmatched(query, decoded, rawMatch);
9141
+ const match = matchQuery(query, [decoded]);
8905
9142
  const submission = match.satisfied ? buildSubmission(query, match) : null;
8906
9143
  return {
8907
9144
  parsed,
@@ -8910,6 +9147,48 @@ async function verifyPresentation(vpToken, query, options) {
8910
9147
  valid: parsed.valid && match.satisfied
8911
9148
  };
8912
9149
  }
9150
+ function isEncryptedResponse(x) {
9151
+ return typeof x === "object" && x !== null && "response" in x && typeof x.response === "string" && Object.keys(x).length === 1;
9152
+ }
9153
+ function isAuthorizationResponse(x) {
9154
+ return typeof x === "object" && x !== null && "vp_token" in x && typeof x.vp_token === "object" && x.vp_token !== null;
9155
+ }
9156
+ async function verifyAuthorizationResponse(envelope, query, options) {
9157
+ if (isEncryptedResponse(envelope)) {
9158
+ if (!options.decryptionKey) {
9159
+ throw new MissingDecryptionKeyError();
9160
+ }
9161
+ const decrypted = await decryptAuthorizationResponse(
9162
+ envelope.response,
9163
+ options.decryptionKey
9164
+ );
9165
+ return verifyAuthorizationResponse(decrypted, query, options);
9166
+ }
9167
+ if (!isAuthorizationResponse(envelope)) {
9168
+ throw new TypeError("envelope must contain vp_token as a JSON object per OpenID4VP 1.0 \xA78.1");
9169
+ }
9170
+ const vpToken = envelope.vp_token;
9171
+ const queryIds = Object.keys(vpToken);
9172
+ if (queryIds.length === 0) {
9173
+ throw new TypeError("vp_token cannot be empty per OpenID4VP 1.0 \xA78.1");
9174
+ }
9175
+ for (const id of queryIds) {
9176
+ if (!Array.isArray(vpToken[id])) {
9177
+ throw new TypeError(
9178
+ `vp_token entry "${id}" must be an array of presentations per OpenID4VP 1.0 \xA78.1`
9179
+ );
9180
+ }
9181
+ }
9182
+ const presentationCount = queryIds.reduce(
9183
+ (sum, id) => sum + vpToken[id].length,
9184
+ 0
9185
+ );
9186
+ if (queryIds.length > 1 || presentationCount > 1) {
9187
+ throw new MultipleCredentialsNotSupportedError(queryIds.length, presentationCount);
9188
+ }
9189
+ const presentation = vpToken[queryIds[0]][0];
9190
+ return verifyPresentation(presentation, query, options);
9191
+ }
8913
9192
 
8914
9193
  // src/index.ts
8915
9194
  init_errors();
@@ -9521,7 +9800,7 @@ function equalBytes(a, b) {
9521
9800
 
9522
9801
  // src/index.ts
9523
9802
  init_errors();
9524
- var VERSION = "0.5.0";
9803
+ var VERSION = "0.7.0";
9525
9804
  /*! Bundled license information:
9526
9805
 
9527
9806
  pvtsutils/build/index.es.js:
@@ -9591,6 +9870,6 @@ asn1js/build/index.es.js:
9591
9870
  *)
9592
9871
  */
9593
9872
 
9594
- export { CertificateChainError, CompositeTrustStore, ExpiredCredentialError, HAIP_DOCTYPE_NAMESPACES, HaipValidationError, InMemoryCache, InvalidSignatureError, LotlFetchError, LotlSignatureError, LotlTrustStore, MalformedCredentialError, MdocParser, NonceValidationError, OpenID4VPError, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, StaticTrustStore, TrustAnchorNotFoundError, UnsupportedFormatError, VERSION, buildHaipQuery, createAuthorizationRequest, isHaipQuery, parsePresentation, validateHaipQuery, verifyPresentation };
9873
+ export { CertificateChainError, CompositeTrustStore, DecryptionFailedError, ExpiredCredentialError, HAIP_DOCTYPE_NAMESPACES, HaipValidationError, InMemoryCache, InvalidSignatureError, LotlFetchError, LotlSignatureError, LotlTrustStore, MalformedCredentialError, MdocParser, MissingDecryptionKeyError, MultipleCredentialsNotSupportedError, NonceValidationError, OpenID4VPError, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, SignedRequestBuildError, StaticTrustStore, TrustAnchorNotFoundError, UnsupportedFormatError, UnsupportedJweError, VERSION, buildHaipQuery, createAuthorizationRequest, createSignedAuthorizationRequest, decryptAuthorizationResponse, isHaipQuery, parsePresentation, validateHaipQuery, verifyAuthorizationResponse, verifyPresentation };
9595
9874
  //# sourceMappingURL=index.js.map
9596
9875
  //# sourceMappingURL=index.js.map