@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.cjs CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  var x509 = require('@peculiar/x509');
4
4
  var uuid = require('uuid');
5
- var cborX = require('cbor-x');
6
5
  var jose = require('jose');
6
+ var cborX = require('cbor-x');
7
7
  var decode = require('@sd-jwt/decode');
8
8
  var dcql = require('@openeudi/dcql');
9
9
  var xmldom = require('@xmldom/xmldom');
@@ -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;
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;
44
44
  var init_errors = __esm({
45
45
  "src/errors.ts"() {
46
46
  exports.InvalidSignatureError = class extends Error {
@@ -136,6 +136,47 @@ var init_errors = __esm({
136
136
  exports.LotlSignatureError = class extends exports.OpenID4VPError {
137
137
  code = "lotl_signature_invalid";
138
138
  };
139
+ exports.SignedRequestBuildError = class extends exports.OpenID4VPError {
140
+ code;
141
+ constructor(code, message) {
142
+ super(message);
143
+ this.code = code;
144
+ }
145
+ };
146
+ exports.UnsupportedJweError = class extends exports.OpenID4VPError {
147
+ code = "unsupported_jwe";
148
+ alg;
149
+ enc;
150
+ constructor(alg, enc) {
151
+ super(`Unsupported JWE algorithms: alg=${alg}, enc=${enc}`);
152
+ this.alg = alg;
153
+ this.enc = enc;
154
+ }
155
+ };
156
+ exports.DecryptionFailedError = class extends exports.OpenID4VPError {
157
+ code = "decryption_failed";
158
+ constructor(message = "Failed to decrypt authorization response", options) {
159
+ super(message, options);
160
+ }
161
+ };
162
+ exports.MissingDecryptionKeyError = class extends exports.OpenID4VPError {
163
+ code = "missing_decryption_key";
164
+ constructor(message = "decryptionKey is required to verify a direct_post.jwt response") {
165
+ super(message);
166
+ }
167
+ };
168
+ exports.MultipleCredentialsNotSupportedError = class extends exports.OpenID4VPError {
169
+ code = "multi_credential_unsupported";
170
+ entryCount;
171
+ presentationCount;
172
+ constructor(entryCount, presentationCount) {
173
+ super(
174
+ `Multi-credential presentations are not yet supported: ${entryCount} queryId entries, ${presentationCount} presentations`
175
+ );
176
+ this.entryCount = entryCount;
177
+ this.presentationCount = presentationCount;
178
+ }
179
+ };
139
180
  }
140
181
  });
141
182
 
@@ -631,6 +672,9 @@ __export(index_es_exports, {
631
672
  Choice: () => Choice,
632
673
  Constructed: () => Constructed,
633
674
  DATE: () => DATE,
675
+ DEFAULT_MAX_CONTENT_LENGTH: () => DEFAULT_MAX_CONTENT_LENGTH,
676
+ DEFAULT_MAX_DEPTH: () => DEFAULT_MAX_DEPTH,
677
+ DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
634
678
  DateTime: () => DateTime,
635
679
  Duration: () => Duration,
636
680
  EndOfContent: () => EndOfContent,
@@ -725,7 +769,7 @@ function HexBlock(BaseClass) {
725
769
  this.isHexOnly = (_b = params.isHexOnly) !== null && _b !== void 0 ? _b : false;
726
770
  this.valueHexView = params.valueHex ? BufferSourceConverter.toUint8Array(params.valueHex) : EMPTY_VIEW;
727
771
  }
728
- fromBER(inputBuffer, inputOffset, inputLength) {
772
+ fromBER(inputBuffer, inputOffset, inputLength, _context) {
729
773
  const view = inputBuffer instanceof ArrayBuffer ? new Uint8Array(inputBuffer) : inputBuffer;
730
774
  if (!checkBufferParams(this, view, inputOffset, inputLength)) {
731
775
  return -1;
@@ -769,6 +813,49 @@ function prepareIndefiniteForm(baseBlock) {
769
813
  }
770
814
  return !!((_a2 = baseBlock.lenBlock) === null || _a2 === void 0 ? void 0 : _a2.isIndefiniteForm);
771
815
  }
816
+ function createFromBerContext(options = {}) {
817
+ var _a2, _b, _c;
818
+ return {
819
+ depth: 0,
820
+ maxDepth: (_a2 = options.maxDepth) !== null && _a2 !== void 0 ? _a2 : DEFAULT_MAX_DEPTH,
821
+ nodesCount: 0,
822
+ maxNodes: (_b = options.maxNodes) !== null && _b !== void 0 ? _b : DEFAULT_MAX_NODES,
823
+ maxContentLength: (_c = options.maxContentLength) !== null && _c !== void 0 ? _c : DEFAULT_MAX_CONTENT_LENGTH
824
+ };
825
+ }
826
+ function createErrorResult(error) {
827
+ const result = new BaseBlock({}, ValueBlock);
828
+ result.error = error;
829
+ return {
830
+ offset: -1,
831
+ result
832
+ };
833
+ }
834
+ function checkNodesLimit(context) {
835
+ context.nodesCount += 1;
836
+ if (context.nodesCount > context.maxNodes) {
837
+ return MAX_NODES_EXCEEDED_ERROR;
838
+ }
839
+ return void 0;
840
+ }
841
+ function checkContentLengthLimit(inputLength, context) {
842
+ if (inputLength > context.maxContentLength) {
843
+ return MAX_CONTENT_LENGTH_EXCEEDED_ERROR;
844
+ }
845
+ return void 0;
846
+ }
847
+ function localFromBERWithChildContext(inputBuffer, inputOffset, inputLength, context) {
848
+ const childDepth = context.depth + 1;
849
+ if (childDepth > context.maxDepth) {
850
+ return createErrorResult(MAX_DEPTH_EXCEEDED_ERROR);
851
+ }
852
+ context.depth = childDepth;
853
+ try {
854
+ return localFromBER(inputBuffer, inputOffset, inputLength, context);
855
+ } finally {
856
+ context.depth -= 1;
857
+ }
858
+ }
772
859
  function localChangeType(inputObject, newType) {
773
860
  if (inputObject instanceof newType) {
774
861
  return inputObject;
@@ -780,7 +867,7 @@ function localChangeType(inputObject, newType) {
780
867
  newObject.valueBeforeDecodeView = inputObject.valueBeforeDecodeView;
781
868
  return newObject;
782
869
  }
783
- function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.length) {
870
+ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.length, context = createFromBerContext()) {
784
871
  const incomingOffset = inputOffset;
785
872
  let returnObject = new BaseBlock({}, ValueBlock);
786
873
  const baseBlock = new LocalBaseBlock();
@@ -799,6 +886,14 @@ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.le
799
886
  result: returnObject
800
887
  };
801
888
  }
889
+ const nodesLimitError = checkNodesLimit(context);
890
+ if (nodesLimitError) {
891
+ returnObject.error = nodesLimitError;
892
+ return {
893
+ offset: -1,
894
+ result: returnObject
895
+ };
896
+ }
802
897
  let resultOffset = returnObject.idBlock.fromBER(inputBuffer, inputOffset, inputLength);
803
898
  if (returnObject.idBlock.warnings.length) {
804
899
  returnObject.warnings.concat(returnObject.idBlock.warnings);
@@ -825,6 +920,15 @@ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.le
825
920
  }
826
921
  inputOffset = resultOffset;
827
922
  inputLength -= returnObject.lenBlock.blockLength;
923
+ const valueLength = returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length;
924
+ const contentLengthError = checkContentLengthLimit(valueLength, context);
925
+ if (contentLengthError) {
926
+ returnObject.error = contentLengthError;
927
+ return {
928
+ offset: -1,
929
+ result: returnObject
930
+ };
931
+ }
828
932
  if (!returnObject.idBlock.isConstructed && returnObject.lenBlock.isIndefiniteForm) {
829
933
  returnObject.error = "Indefinite length form used for primitive encoding form";
830
934
  return {
@@ -963,14 +1067,14 @@ function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.le
963
1067
  }
964
1068
  }
965
1069
  returnObject = localChangeType(returnObject, newASN1Type);
966
- resultOffset = returnObject.fromBER(inputBuffer, inputOffset, returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length);
1070
+ resultOffset = returnObject.fromBER(inputBuffer, inputOffset, valueLength, context);
967
1071
  returnObject.valueBeforeDecodeView = inputBuffer.subarray(incomingOffset, incomingOffset + returnObject.blockLength);
968
1072
  return {
969
1073
  offset: resultOffset,
970
1074
  result: returnObject
971
1075
  };
972
1076
  }
973
- function fromBER(inputBuffer) {
1077
+ function fromBER(inputBuffer, options = {}) {
974
1078
  if (!inputBuffer.byteLength) {
975
1079
  const result = new BaseBlock({}, ValueBlock);
976
1080
  result.error = "Input buffer has zero length";
@@ -979,7 +1083,7 @@ function fromBER(inputBuffer) {
979
1083
  result
980
1084
  };
981
1085
  }
982
- return localFromBER(BufferSourceConverter.toUint8Array(inputBuffer).slice(), 0, inputBuffer.byteLength);
1086
+ return localFromBER(BufferSourceConverter.toUint8Array(inputBuffer).slice(), 0, inputBuffer.byteLength, createFromBerContext(options));
983
1087
  }
984
1088
  function checkLen(indefiniteLength, length) {
985
1089
  if (indefiniteLength) {
@@ -1385,7 +1489,7 @@ function verifySchema(inputBuffer, inputSchema) {
1385
1489
  }
1386
1490
  return compareSchema(asn1.result, asn1.result, inputSchema);
1387
1491
  }
1388
- 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;
1492
+ 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;
1389
1493
  var init_index_es2 = __esm({
1390
1494
  "node_modules/asn1js/build/index.es.js"() {
1391
1495
  init_index_es();
@@ -1447,7 +1551,7 @@ var init_index_es2 = __esm({
1447
1551
  };
1448
1552
  LocalBaseBlock.NAME = "baseBlock";
1449
1553
  ValueBlock = class extends LocalBaseBlock {
1450
- fromBER(_inputBuffer, _inputOffset, _inputLength) {
1554
+ fromBER(_inputBuffer, _inputOffset, _inputLength, _context) {
1451
1555
  throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'");
1452
1556
  }
1453
1557
  toBER(_sizeOnly, _writer) {
@@ -1560,31 +1664,21 @@ var init_index_es2 = __esm({
1560
1664
  this.tagNumber = tagNumberMask;
1561
1665
  this.blockLength = 1;
1562
1666
  } else {
1563
- let count = 1;
1564
- let intTagNumberBuffer = this.valueHexView = new Uint8Array(255);
1565
- let tagNumberBufferMaxLength = 255;
1566
- while (intBuffer[count] & 128) {
1567
- intTagNumberBuffer[count - 1] = intBuffer[count] & 127;
1568
- count++;
1569
- if (count >= intBuffer.length) {
1667
+ let count = 0;
1668
+ while (true) {
1669
+ const tagByteIndex = count + 1;
1670
+ if (tagByteIndex >= intBuffer.length) {
1570
1671
  this.error = "End of input reached before message was fully decoded";
1571
1672
  return -1;
1572
1673
  }
1573
- if (count === tagNumberBufferMaxLength) {
1574
- tagNumberBufferMaxLength += 255;
1575
- const tempBufferView2 = new Uint8Array(tagNumberBufferMaxLength);
1576
- for (let i = 0; i < intTagNumberBuffer.length; i++)
1577
- tempBufferView2[i] = intTagNumberBuffer[i];
1578
- intTagNumberBuffer = this.valueHexView = new Uint8Array(tagNumberBufferMaxLength);
1579
- }
1674
+ count++;
1675
+ if ((intBuffer[tagByteIndex] & 128) === 0)
1676
+ break;
1580
1677
  }
1581
1678
  this.blockLength = count + 1;
1582
- intTagNumberBuffer[count - 1] = intBuffer[count] & 127;
1583
- const tempBufferView = new Uint8Array(count);
1679
+ const intTagNumberBuffer = this.valueHexView = new Uint8Array(count);
1584
1680
  for (let i = 0; i < count; i++)
1585
- tempBufferView[i] = intTagNumberBuffer[i];
1586
- intTagNumberBuffer = this.valueHexView = new Uint8Array(count);
1587
- intTagNumberBuffer.set(tempBufferView);
1681
+ intTagNumberBuffer[i] = intBuffer[i + 1] & 127;
1588
1682
  if (this.blockLength <= 9)
1589
1683
  this.tagNumber = utilFromBase(intTagNumberBuffer, 7);
1590
1684
  else {
@@ -1734,8 +1828,8 @@ var init_index_es2 = __esm({
1734
1828
  this.lenBlock = new LocalLengthBlock(parameters);
1735
1829
  this.valueBlock = valueBlockType ? new valueBlockType(parameters) : new ValueBlock(parameters);
1736
1830
  }
1737
- fromBER(inputBuffer, inputOffset, inputLength) {
1738
- const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
1831
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
1832
+ const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length, context);
1739
1833
  if (resultOffset === -1) {
1740
1834
  this.error = this.valueBlock.error;
1741
1835
  return resultOffset;
@@ -1859,14 +1953,21 @@ var init_index_es2 = __esm({
1859
1953
  typeStore.Primitive = _a$w;
1860
1954
  })();
1861
1955
  Primitive.NAME = "PRIMITIVE";
1956
+ DEFAULT_MAX_DEPTH = 100;
1957
+ DEFAULT_MAX_NODES = 1e4;
1958
+ DEFAULT_MAX_CONTENT_LENGTH = 16 * 1024 * 1024;
1959
+ MAX_DEPTH_EXCEEDED_ERROR = "Maximum ASN.1 nesting depth exceeded";
1960
+ MAX_NODES_EXCEEDED_ERROR = "Maximum ASN.1 node count exceeded";
1961
+ MAX_CONTENT_LENGTH_EXCEEDED_ERROR = "Maximum ASN.1 content length exceeded";
1862
1962
  LocalConstructedValueBlock = class extends ValueBlock {
1863
1963
  constructor({ value = [], isIndefiniteForm = false, ...parameters } = {}) {
1864
1964
  super(parameters);
1865
1965
  this.value = value;
1866
1966
  this.isIndefiniteForm = isIndefiniteForm;
1867
1967
  }
1868
- fromBER(inputBuffer, inputOffset, inputLength) {
1968
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
1869
1969
  const view = BufferSourceConverter.toUint8Array(inputBuffer);
1970
+ const parseContext = context !== null && context !== void 0 ? context : createFromBerContext();
1870
1971
  if (!checkBufferParams(this, view, inputOffset, inputLength)) {
1871
1972
  return -1;
1872
1973
  }
@@ -1877,7 +1978,7 @@ var init_index_es2 = __esm({
1877
1978
  }
1878
1979
  let currentOffset = inputOffset;
1879
1980
  while (checkLen(this.isIndefiniteForm, inputLength) > 0) {
1880
- const returnObject = localFromBER(view, currentOffset, inputLength);
1981
+ const returnObject = localFromBERWithChildContext(view, currentOffset, inputLength, parseContext);
1881
1982
  if (returnObject.offset === -1) {
1882
1983
  this.error = returnObject.result.error;
1883
1984
  this.warnings.concat(returnObject.result.warnings);
@@ -1928,9 +2029,9 @@ var init_index_es2 = __esm({
1928
2029
  super(parameters, LocalConstructedValueBlock);
1929
2030
  this.idBlock.isConstructed = true;
1930
2031
  }
1931
- fromBER(inputBuffer, inputOffset, inputLength) {
2032
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
1932
2033
  this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
1933
- const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
2034
+ const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length, context);
1934
2035
  if (resultOffset === -1) {
1935
2036
  this.error = this.valueBlock.error;
1936
2037
  return resultOffset;
@@ -2093,11 +2194,11 @@ ${values.join("\n")}` : `${blockName} :`;
2093
2194
  super(parameters);
2094
2195
  this.isConstructed = isConstructed;
2095
2196
  }
2096
- fromBER(inputBuffer, inputOffset, inputLength) {
2197
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2097
2198
  let resultOffset = 0;
2098
2199
  if (this.isConstructed) {
2099
2200
  this.isHexOnly = false;
2100
- resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength);
2201
+ resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength, context);
2101
2202
  if (resultOffset === -1)
2102
2203
  return resultOffset;
2103
2204
  for (let i = 0; i < this.value.length; i++) {
@@ -2153,7 +2254,7 @@ ${values.join("\n")}` : `${blockName} :`;
2153
2254
  this.idBlock.tagClass = 1;
2154
2255
  this.idBlock.tagNumber = 4;
2155
2256
  }
2156
- fromBER(inputBuffer, inputOffset, inputLength) {
2257
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2157
2258
  this.valueBlock.isConstructed = this.idBlock.isConstructed;
2158
2259
  this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
2159
2260
  if (inputLength === 0) {
@@ -2168,7 +2269,8 @@ ${values.join("\n")}` : `${blockName} :`;
2168
2269
  const buf = view.subarray(inputOffset, inputOffset + inputLength);
2169
2270
  try {
2170
2271
  if (buf.byteLength) {
2171
- const asn = localFromBER(buf, 0, buf.byteLength);
2272
+ const parseContext = context !== null && context !== void 0 ? context : createFromBerContext();
2273
+ const asn = localFromBERWithChildContext(buf, 0, buf.byteLength, parseContext);
2172
2274
  if (asn.offset !== -1 && asn.offset === inputLength) {
2173
2275
  this.valueBlock.value = [asn.result];
2174
2276
  }
@@ -2176,7 +2278,7 @@ ${values.join("\n")}` : `${blockName} :`;
2176
2278
  } catch {
2177
2279
  }
2178
2280
  }
2179
- return super.fromBER(inputBuffer, inputOffset, inputLength);
2281
+ return super.fromBER(inputBuffer, inputOffset, inputLength, context);
2180
2282
  }
2181
2283
  onAsciiEncoding() {
2182
2284
  if (this.valueBlock.isConstructed || this.valueBlock.value && this.valueBlock.value.length) {
@@ -2211,13 +2313,13 @@ ${values.join("\n")}` : `${blockName} :`;
2211
2313
  this.isConstructed = isConstructed;
2212
2314
  this.blockLength = this.valueHexView.byteLength;
2213
2315
  }
2214
- fromBER(inputBuffer, inputOffset, inputLength) {
2316
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2215
2317
  if (!inputLength) {
2216
2318
  return inputOffset;
2217
2319
  }
2218
2320
  let resultOffset = -1;
2219
2321
  if (this.isConstructed) {
2220
- resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength);
2322
+ resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength, context);
2221
2323
  if (resultOffset === -1)
2222
2324
  return resultOffset;
2223
2325
  for (const value of this.value) {
@@ -2257,7 +2359,8 @@ ${values.join("\n")}` : `${blockName} :`;
2257
2359
  const buf = intBuffer.subarray(1);
2258
2360
  try {
2259
2361
  if (buf.byteLength) {
2260
- const asn = localFromBER(buf, 0, buf.byteLength);
2362
+ const parseContext = context !== null && context !== void 0 ? context : createFromBerContext();
2363
+ const asn = localFromBERWithChildContext(buf, 0, buf.byteLength, parseContext);
2261
2364
  if (asn.offset !== -1 && asn.offset === inputLength - 1) {
2262
2365
  this.value = [asn.result];
2263
2366
  }
@@ -2313,10 +2416,10 @@ ${values.join("\n")}` : `${blockName} :`;
2313
2416
  this.idBlock.tagClass = 1;
2314
2417
  this.idBlock.tagNumber = 3;
2315
2418
  }
2316
- fromBER(inputBuffer, inputOffset, inputLength) {
2419
+ fromBER(inputBuffer, inputOffset, inputLength, context) {
2317
2420
  this.valueBlock.isConstructed = this.idBlock.isConstructed;
2318
2421
  this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
2319
- return super.fromBER(inputBuffer, inputOffset, inputLength);
2422
+ return super.fromBER(inputBuffer, inputOffset, inputLength, context);
2320
2423
  }
2321
2424
  onAsciiEncoding() {
2322
2425
  if (this.valueBlock.isConstructed || this.valueBlock.value && this.valueBlock.value.length) {
@@ -7983,6 +8086,135 @@ function createAuthorizationRequest(input, query) {
7983
8086
  };
7984
8087
  }
7985
8088
 
8089
+ // src/signed-authorization.ts
8090
+ init_errors();
8091
+ var DEFAULT_SUPPORTED_ENC_VALUES = ["A128GCM", "A256GCM"];
8092
+ var SUPPORTED_SIGNING_ALGS = /* @__PURE__ */ new Set(["ES256", "ES384", "RS256"]);
8093
+ async function createSignedAuthorizationRequest(input, query) {
8094
+ if (input.certificateChain.length === 0) {
8095
+ throw new exports.SignedRequestBuildError(
8096
+ "empty_cert_chain",
8097
+ "certificateChain must contain at least the leaf certificate"
8098
+ );
8099
+ }
8100
+ const signingAlg = input.signingAlgorithm ?? "ES256";
8101
+ if (!SUPPORTED_SIGNING_ALGS.has(signingAlg)) {
8102
+ throw new exports.SignedRequestBuildError(
8103
+ "unsupported_signing_alg",
8104
+ `signingAlgorithm ${signingAlg} is not supported (expected ES256/ES384/RS256)`
8105
+ );
8106
+ }
8107
+ if (!input.vpFormatsSupported || Object.keys(input.vpFormatsSupported).length === 0) {
8108
+ throw new exports.SignedRequestBuildError(
8109
+ "missing_vp_formats",
8110
+ "vpFormatsSupported is required and must be non-empty"
8111
+ );
8112
+ }
8113
+ const leafCert = new x509.X509Certificate(toArrayBuffer(input.certificateChain[0]));
8114
+ const sanDnsNames = extractDnsNames(leafCert);
8115
+ if (!sanDnsNames.includes(input.hostname)) {
8116
+ throw new exports.SignedRequestBuildError(
8117
+ "hostname_cert_mismatch",
8118
+ `leaf cert SAN DNSName values [${sanDnsNames.join(", ")}] do not include hostname "${input.hostname}"`
8119
+ );
8120
+ }
8121
+ const signerPublicSpki = new Uint8Array(
8122
+ await crypto.subtle.exportKey("spki", input.signer.publicKey)
8123
+ );
8124
+ const leafSpki = new Uint8Array(leafCert.publicKey.rawData);
8125
+ if (!bytesEqual(signerPublicSpki, leafSpki)) {
8126
+ throw new exports.SignedRequestBuildError(
8127
+ "signing_key_cert_mismatch",
8128
+ "signer.publicKey SPKI does not match leaf certificate SPKI"
8129
+ );
8130
+ }
8131
+ const responseMode = input.responseMode ?? "direct_post.jwt";
8132
+ if (responseMode === "direct_post.jwt") {
8133
+ if (!input.encryptionKey?.publicJwk) {
8134
+ throw new exports.SignedRequestBuildError(
8135
+ "missing_encryption_jwk",
8136
+ "direct_post.jwt response mode requires encryptionKey.publicJwk"
8137
+ );
8138
+ }
8139
+ if (!input.encryptionKey.publicJwk.alg) {
8140
+ throw new exports.SignedRequestBuildError(
8141
+ "missing_encryption_alg",
8142
+ "encryptionKey.publicJwk must carry alg (OpenID4VP 1.0 \xA78.3)"
8143
+ );
8144
+ }
8145
+ }
8146
+ const state = input.state ?? uuid.v4();
8147
+ const clientId = `x509_san_dns:${input.hostname}`;
8148
+ const now = Math.floor(Date.now() / 1e3);
8149
+ const clientMetadata = {
8150
+ vp_formats_supported: input.vpFormatsSupported
8151
+ };
8152
+ if (responseMode === "direct_post.jwt" && input.encryptionKey) {
8153
+ const enc = input.encryptionKey.supportedEncValues;
8154
+ if (enc !== void 0 && enc.length === 0) {
8155
+ throw new exports.SignedRequestBuildError(
8156
+ "empty_supported_enc_values",
8157
+ "encryptionKey.supportedEncValues must not be empty (omit to use defaults [A128GCM, A256GCM])"
8158
+ );
8159
+ }
8160
+ const jwk = { ...input.encryptionKey.publicJwk, use: "enc" };
8161
+ clientMetadata.jwks = { keys: [jwk] };
8162
+ clientMetadata.encrypted_response_enc_values_supported = enc ?? [...DEFAULT_SUPPORTED_ENC_VALUES];
8163
+ }
8164
+ const payload = {
8165
+ iss: clientId,
8166
+ aud: "https://self-issued.me/v2",
8167
+ response_type: "vp_token",
8168
+ response_mode: responseMode,
8169
+ client_id: clientId,
8170
+ response_uri: input.responseUri,
8171
+ nonce: input.nonce,
8172
+ state,
8173
+ dcql_query: query,
8174
+ client_metadata: clientMetadata,
8175
+ iat: now,
8176
+ exp: now + 120
8177
+ };
8178
+ const x5c = input.certificateChain.map((der) => bytesToBase64(der));
8179
+ const jws = await new jose.SignJWT(payload).setProtectedHeader({ typ: "oauth-authz-req+jwt", alg: signingAlg, x5c }).sign(input.signer.privateKey);
8180
+ const uriParams = new URLSearchParams({
8181
+ client_id: clientId,
8182
+ request_uri: input.requestUri
8183
+ });
8184
+ return {
8185
+ uri: `openid4vp://authorize?${uriParams.toString()}`,
8186
+ requestObject: jws,
8187
+ dcqlQuery: query,
8188
+ nonce: input.nonce,
8189
+ state
8190
+ };
8191
+ }
8192
+ function extractDnsNames(cert) {
8193
+ const SAN_OID = "2.5.29.17";
8194
+ const ext = cert.extensions.find((e) => e.type === SAN_OID);
8195
+ if (!ext) return [];
8196
+ const names = ext.names;
8197
+ const items = names?.items ?? [];
8198
+ return items.filter((e) => e.type === "dns").map((e) => e.value);
8199
+ }
8200
+ function bytesEqual(a, b) {
8201
+ if (a.length !== b.length) return false;
8202
+ for (let i = 0; i < a.length; i++) {
8203
+ if (a[i] !== b[i]) return false;
8204
+ }
8205
+ return true;
8206
+ }
8207
+ function bytesToBase64(bytes) {
8208
+ let s = "";
8209
+ for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
8210
+ return btoa(s);
8211
+ }
8212
+ function toArrayBuffer(bytes) {
8213
+ const out = new ArrayBuffer(bytes.byteLength);
8214
+ new Uint8Array(out).set(bytes);
8215
+ return out;
8216
+ }
8217
+
7986
8218
  // src/haip.ts
7987
8219
  init_errors();
7988
8220
  var HAIP_DOCTYPE_NAMESPACES = {
@@ -8209,7 +8441,7 @@ async function computeItemDigest(itemBytes, digestAlgorithm) {
8209
8441
  const buf = await crypto.subtle.digest(digestAlgorithm, new Uint8Array(itemBytes));
8210
8442
  return new Uint8Array(buf);
8211
8443
  }
8212
- function bytesEqual(a, b) {
8444
+ function bytesEqual2(a, b) {
8213
8445
  if (a.length !== b.length) return false;
8214
8446
  for (let i = 0; i < a.length; i++) {
8215
8447
  if (a[i] !== b[i]) return false;
@@ -8255,7 +8487,7 @@ async function verifyAllDigests(nameSpaces, mso) {
8255
8487
  if (!expectedDigest) {
8256
8488
  throw new exports.MalformedCredentialError(`digestID ${digestID} missing from MSO valueDigests[${ns}]`);
8257
8489
  }
8258
- if (!bytesEqual(hash, expectedDigest)) {
8490
+ if (!bytesEqual2(hash, expectedDigest)) {
8259
8491
  throw new exports.MalformedCredentialError(`digest mismatch for ns=${ns} digestID=${digestID}`);
8260
8492
  }
8261
8493
  }
@@ -8374,7 +8606,7 @@ init_errors();
8374
8606
  var decoder4 = new cborX.Encoder({ mapsAsObjects: false, useRecords: false });
8375
8607
  var encoder = new cborX.Encoder({ mapsAsObjects: false, useRecords: false });
8376
8608
  var DEFAULT_ALLOWED_ALGORITHMS = ["ES256", "ES384", "ES512"];
8377
- function bytesEqual2(a, b) {
8609
+ function bytesEqual3(a, b) {
8378
8610
  if (a.length !== b.length) return false;
8379
8611
  for (let i = 0; i < a.length; i++) {
8380
8612
  if (a[i] !== b[i]) return false;
@@ -8510,8 +8742,8 @@ var MdocParser = class {
8510
8742
  cache: options.cache,
8511
8743
  clockSkewTolerance: options.clockSkewTolerance
8512
8744
  });
8513
- const { X509Certificate: X509Certificate8 } = await import('@peculiar/x509');
8514
- const leaf = new X509Certificate8(issuerCertBytes);
8745
+ const { X509Certificate: X509Certificate9 } = await import('@peculiar/x509');
8746
+ const leaf = new X509Certificate9(issuerCertBytes);
8515
8747
  trustResult = await evaluator.evaluate(leaf);
8516
8748
  } else {
8517
8749
  if (options.trustedCertificates.length === 0) {
@@ -8521,7 +8753,7 @@ var MdocParser = class {
8521
8753
  );
8522
8754
  }
8523
8755
  } else {
8524
- const trusted = options.trustedCertificates.some((t) => bytesEqual2(t, issuerCertBytes));
8756
+ const trusted = options.trustedCertificates.some((t) => bytesEqual3(t, issuerCertBytes));
8525
8757
  if (!trusted) {
8526
8758
  return {
8527
8759
  valid: false,
@@ -8636,7 +8868,7 @@ function base64ToBytes(base64) {
8636
8868
  }
8637
8869
  return bytes;
8638
8870
  }
8639
- function bytesEqual3(a, b) {
8871
+ function bytesEqual4(a, b) {
8640
8872
  if (a.length !== b.length) return false;
8641
8873
  for (let i = 0; i < a.length; i++) {
8642
8874
  if (a[i] !== b[i]) return false;
@@ -8759,8 +8991,8 @@ var SdJwtParser = class {
8759
8991
  cache: options.cache,
8760
8992
  clockSkewTolerance: options.clockSkewTolerance
8761
8993
  });
8762
- const { X509Certificate: X509Certificate8 } = await import('@peculiar/x509');
8763
- const leaf = new X509Certificate8(issuerCertBytes);
8994
+ const { X509Certificate: X509Certificate9 } = await import('@peculiar/x509');
8995
+ const leaf = new X509Certificate9(issuerCertBytes);
8764
8996
  trustResult = await evaluator.evaluate(leaf);
8765
8997
  } else {
8766
8998
  if (options.trustedCertificates.length === 0) {
@@ -8770,7 +9002,7 @@ var SdJwtParser = class {
8770
9002
  );
8771
9003
  }
8772
9004
  } else {
8773
- const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual3(trusted, issuerCertBytes));
9005
+ const isTrusted = options.trustedCertificates.some((trusted) => bytesEqual4(trusted, issuerCertBytes));
8774
9006
  if (!isTrusted) {
8775
9007
  return invalidResult("Issuer certificate is not trusted");
8776
9008
  }
@@ -8867,42 +9099,48 @@ async function parsePresentation(vpToken, options) {
8867
9099
  error: "Unsupported credential format"
8868
9100
  };
8869
9101
  }
8870
- var INTERNAL_TO_DCQL_FORMAT = {
8871
- "sd-jwt-vc": "dc+sd-jwt",
8872
- mdoc: "mso_mdoc"
8873
- };
8874
- function classifyMismatch(query, decoded, hasDetail) {
8875
- if (decoded.format !== query.format) return "format_mismatch";
8876
- if (query.format === "dc+sd-jwt") {
8877
- const allowed = query.meta?.vct_values;
8878
- if (allowed && allowed.length > 0 && !allowed.includes(decoded.vct ?? "")) {
8879
- return "vct_mismatch";
8880
- }
9102
+
9103
+ // src/decrypt-response.ts
9104
+ init_errors();
9105
+ var SUPPORTED_ALG = "ECDH-ES";
9106
+ var SUPPORTED_ENC = /* @__PURE__ */ new Set(["A128GCM", "A256GCM"]);
9107
+ async function decryptAuthorizationResponse(jwe, privateKey) {
9108
+ let header;
9109
+ try {
9110
+ header = jose.decodeProtectedHeader(jwe);
9111
+ } catch (err) {
9112
+ throw new exports.DecryptionFailedError("Malformed JWE header", {
9113
+ cause: err
9114
+ });
8881
9115
  }
8882
- if (query.format === "mso_mdoc") {
8883
- const expected = query.meta?.doctype_value;
8884
- if (expected && expected !== decoded.doctype) return "doctype_mismatch";
9116
+ const alg = typeof header.alg === "string" ? header.alg : "";
9117
+ const enc = typeof header.enc === "string" ? header.enc : "";
9118
+ if (alg !== SUPPORTED_ALG || !SUPPORTED_ENC.has(enc)) {
9119
+ throw new exports.UnsupportedJweError(alg, enc);
8885
9120
  }
8886
- if (query.trusted_authorities && query.trusted_authorities.length > 0) {
8887
- const decodedAuthIds = decoded.trusted_authority_ids ?? [];
8888
- const anyMatch = query.trusted_authorities.some(
8889
- (ta) => ta.values.some((v) => decodedAuthIds.includes(v))
8890
- );
8891
- if (!anyMatch) return "trusted_authority_mismatch";
9121
+ let plaintext;
9122
+ try {
9123
+ const result = await jose.compactDecrypt(jwe, privateKey);
9124
+ plaintext = result.plaintext;
9125
+ } catch (err) {
9126
+ throw new exports.DecryptionFailedError(void 0, { cause: err });
9127
+ }
9128
+ try {
9129
+ const text = new TextDecoder().decode(plaintext);
9130
+ return JSON.parse(text);
9131
+ } catch (err) {
9132
+ throw new exports.DecryptionFailedError("Decrypted plaintext is not valid JSON", {
9133
+ cause: err
9134
+ });
8892
9135
  }
8893
- if (hasDetail) return "missing_claims";
8894
- return "no_credential_found";
8895
- }
8896
- function refineUnmatched(query, decoded, match) {
8897
- if (match.unmatched.length === 0) return match;
8898
- const refined = match.unmatched.map((entry) => {
8899
- const credentialQuery = query.credentials.find((c) => c.id === entry.queryId);
8900
- if (!credentialQuery) return entry;
8901
- const specific = classifyMismatch(credentialQuery, decoded, entry.detail !== void 0);
8902
- return { ...entry, reason: specific };
8903
- });
8904
- return { ...match, unmatched: refined };
8905
9136
  }
9137
+
9138
+ // src/verify.ts
9139
+ init_errors();
9140
+ var INTERNAL_TO_DCQL_FORMAT = {
9141
+ "sd-jwt-vc": "dc+sd-jwt",
9142
+ mdoc: "mso_mdoc"
9143
+ };
8906
9144
  async function verifyPresentation(vpToken, query, options) {
8907
9145
  const parsed = await parsePresentation(vpToken, options);
8908
9146
  const dcqlFormat = INTERNAL_TO_DCQL_FORMAT[parsed.format] ?? parsed.format;
@@ -8921,8 +9159,7 @@ async function verifyPresentation(vpToken, query, options) {
8921
9159
  if (parsed.trust?.trustedAuthorityIds && parsed.trust.trustedAuthorityIds.length > 0) {
8922
9160
  decoded.trusted_authority_ids = [...parsed.trust.trustedAuthorityIds];
8923
9161
  }
8924
- const rawMatch = dcql.matchQuery(query, [decoded]);
8925
- const match = refineUnmatched(query, decoded, rawMatch);
9162
+ const match = dcql.matchQuery(query, [decoded]);
8926
9163
  const submission = match.satisfied ? dcql.buildSubmission(query, match) : null;
8927
9164
  return {
8928
9165
  parsed,
@@ -8931,6 +9168,48 @@ async function verifyPresentation(vpToken, query, options) {
8931
9168
  valid: parsed.valid && match.satisfied
8932
9169
  };
8933
9170
  }
9171
+ function isEncryptedResponse(x) {
9172
+ return typeof x === "object" && x !== null && "response" in x && typeof x.response === "string" && Object.keys(x).length === 1;
9173
+ }
9174
+ function isAuthorizationResponse(x) {
9175
+ return typeof x === "object" && x !== null && "vp_token" in x && typeof x.vp_token === "object" && x.vp_token !== null;
9176
+ }
9177
+ async function verifyAuthorizationResponse(envelope, query, options) {
9178
+ if (isEncryptedResponse(envelope)) {
9179
+ if (!options.decryptionKey) {
9180
+ throw new exports.MissingDecryptionKeyError();
9181
+ }
9182
+ const decrypted = await decryptAuthorizationResponse(
9183
+ envelope.response,
9184
+ options.decryptionKey
9185
+ );
9186
+ return verifyAuthorizationResponse(decrypted, query, options);
9187
+ }
9188
+ if (!isAuthorizationResponse(envelope)) {
9189
+ throw new TypeError("envelope must contain vp_token as a JSON object per OpenID4VP 1.0 \xA78.1");
9190
+ }
9191
+ const vpToken = envelope.vp_token;
9192
+ const queryIds = Object.keys(vpToken);
9193
+ if (queryIds.length === 0) {
9194
+ throw new TypeError("vp_token cannot be empty per OpenID4VP 1.0 \xA78.1");
9195
+ }
9196
+ for (const id of queryIds) {
9197
+ if (!Array.isArray(vpToken[id])) {
9198
+ throw new TypeError(
9199
+ `vp_token entry "${id}" must be an array of presentations per OpenID4VP 1.0 \xA78.1`
9200
+ );
9201
+ }
9202
+ }
9203
+ const presentationCount = queryIds.reduce(
9204
+ (sum, id) => sum + vpToken[id].length,
9205
+ 0
9206
+ );
9207
+ if (queryIds.length > 1 || presentationCount > 1) {
9208
+ throw new exports.MultipleCredentialsNotSupportedError(queryIds.length, presentationCount);
9209
+ }
9210
+ const presentation = vpToken[queryIds[0]][0];
9211
+ return verifyPresentation(presentation, query, options);
9212
+ }
8934
9213
 
8935
9214
  // src/index.ts
8936
9215
  init_errors();
@@ -9542,7 +9821,7 @@ function equalBytes(a, b) {
9542
9821
 
9543
9822
  // src/index.ts
9544
9823
  init_errors();
9545
- var VERSION = "0.5.0";
9824
+ var VERSION = "0.7.0";
9546
9825
  /*! Bundled license information:
9547
9826
 
9548
9827
  pvtsutils/build/index.es.js:
@@ -9630,9 +9909,12 @@ exports.StaticTrustStore = StaticTrustStore;
9630
9909
  exports.VERSION = VERSION;
9631
9910
  exports.buildHaipQuery = buildHaipQuery;
9632
9911
  exports.createAuthorizationRequest = createAuthorizationRequest;
9912
+ exports.createSignedAuthorizationRequest = createSignedAuthorizationRequest;
9913
+ exports.decryptAuthorizationResponse = decryptAuthorizationResponse;
9633
9914
  exports.isHaipQuery = isHaipQuery;
9634
9915
  exports.parsePresentation = parsePresentation;
9635
9916
  exports.validateHaipQuery = validateHaipQuery;
9917
+ exports.verifyAuthorizationResponse = verifyAuthorizationResponse;
9636
9918
  exports.verifyPresentation = verifyPresentation;
9637
9919
  //# sourceMappingURL=index.cjs.map
9638
9920
  //# sourceMappingURL=index.cjs.map